Merge "Add LOCAL_JAR_EXCLUDE_FILES := none to android-support-v13"
am: 69ea07ab69

Change-Id: Id56736dfec41747d36b9bc669fd4fe45bb14a4a4
diff --git a/.gitignore b/.gitignore
index e57740b..abfef7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,5 @@
 *.iml
 **/out
 buildSrc/build
+lifecycle/common/build
+jacoco.exec
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..f433b1a
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2811ea9..ecc61dd 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,5 @@
 [Hook Scripts]
-checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} -c ${REPO_ROOT}/frameworks/support/development/checkstyle/config/support-lib.xml -p development/checkstyle/prebuilt/com.android.support.checkstyle.jar
 
 [Builtin Hooks]
 commit_msg_changeid_field = true
diff --git a/annotations/build.gradle b/annotations/build.gradle
index 4907e2a..6071820 100644
--- a/annotations/build.gradle
+++ b/annotations/build.gradle
@@ -1,14 +1,14 @@
 apply plugin: 'java'
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
 archivesBaseName = 'support-annotations'
 
 sourceSets {
     main.java.srcDir 'src'
 }
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
+compileJava {
+    sourceCompatibility = JavaVersion.VERSION_1_7
+    targetCompatibility = JavaVersion.VERSION_1_7
+}
 
 jar {
     from sourceSets.main.output
diff --git a/annotations/src/android/support/annotation/AnimRes.java b/annotations/src/android/support/annotation/AnimRes.java
index e91fa81..d5cbaca 100644
--- a/annotations/src/android/support/annotation/AnimRes.java
+++ b/annotations/src/android/support/annotation/AnimRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an anim resource reference (e.g. {@code android.R.anim.fade_in}).
diff --git a/annotations/src/android/support/annotation/AnimatorRes.java b/annotations/src/android/support/annotation/AnimatorRes.java
index e969356..03918b3 100644
--- a/annotations/src/android/support/annotation/AnimatorRes.java
+++ b/annotations/src/android/support/annotation/AnimatorRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an animator resource reference (e.g. {@code android.R.animator.fade_in}).
diff --git a/annotations/src/android/support/annotation/AnyRes.java b/annotations/src/android/support/annotation/AnyRes.java
index e831289..2efe0da 100644
--- a/annotations/src/android/support/annotation/AnyRes.java
+++ b/annotations/src/android/support/annotation/AnyRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a resource reference of any type. If the specific type is known, use
diff --git a/annotations/src/android/support/annotation/AnyThread.java b/annotations/src/android/support/annotation/AnyThread.java
index 500d368..4c379d3 100644
--- a/annotations/src/android/support/annotation/AnyThread.java
+++ b/annotations/src/android/support/annotation/AnyThread.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated method can be called from any thread (e.g. it is "thread safe".)
  * If the annotated element is a class, then all methods in the class can be called
diff --git a/annotations/src/android/support/annotation/ArrayRes.java b/annotations/src/android/support/annotation/ArrayRes.java
index 758bce4..d9fd9fa 100644
--- a/annotations/src/android/support/annotation/ArrayRes.java
+++ b/annotations/src/android/support/annotation/ArrayRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an array resource reference (e.g. {@code android.R.array.phoneTypes}).
diff --git a/annotations/src/android/support/annotation/AttrRes.java b/annotations/src/android/support/annotation/AttrRes.java
index 2034be5..db9a76d 100644
--- a/annotations/src/android/support/annotation/AttrRes.java
+++ b/annotations/src/android/support/annotation/AttrRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an attribute reference (e.g. {@code android.R.attr.action}).
diff --git a/annotations/src/android/support/annotation/BinderThread.java b/annotations/src/android/support/annotation/BinderThread.java
index db7dd3a..0b821d5 100644
--- a/annotations/src/android/support/annotation/BinderThread.java
+++ b/annotations/src/android/support/annotation/BinderThread.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated method should only be called on the binder thread.
  * If the annotated element is a class, then all methods in the class should be called
diff --git a/annotations/src/android/support/annotation/BoolRes.java b/annotations/src/android/support/annotation/BoolRes.java
index bef8071..9206ec1 100644
--- a/annotations/src/android/support/annotation/BoolRes.java
+++ b/annotations/src/android/support/annotation/BoolRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a boolean resource reference.
diff --git a/annotations/src/android/support/annotation/CallSuper.java b/annotations/src/android/support/annotation/CallSuper.java
index 9c01cdf..6bf5f55 100644
--- a/annotations/src/android/support/annotation/CallSuper.java
+++ b/annotations/src/android/support/annotation/CallSuper.java
@@ -15,13 +15,13 @@
  */
 package android.support.annotation;
 
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
 /**
  * Denotes that any overriding methods should invoke this method as well.
  * <p>
diff --git a/annotations/src/android/support/annotation/CheckResult.java b/annotations/src/android/support/annotation/CheckResult.java
index 7f40676..cb49eb7 100644
--- a/annotations/src/android/support/annotation/CheckResult.java
+++ b/annotations/src/android/support/annotation/CheckResult.java
@@ -15,13 +15,13 @@
  */
 package android.support.annotation;
 
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
 /**
  * Denotes that the annotated method returns a result that it typically is
  * an error to ignore. This is usually used for methods that have no side effect,
diff --git a/annotations/src/android/support/annotation/ColorInt.java b/annotations/src/android/support/annotation/ColorInt.java
index 4fa46eb..948a5b5 100644
--- a/annotations/src/android/support/annotation/ColorInt.java
+++ b/annotations/src/android/support/annotation/ColorInt.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element represents a packed color
  * int, {@code AARRGGBB}. If applied to an int array, every element
diff --git a/annotations/src/android/support/annotation/ColorLong.java b/annotations/src/android/support/annotation/ColorLong.java
new file mode 100644
index 0000000..bb78138
--- /dev/null
+++ b/annotations/src/android/support/annotation/ColorLong.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.support.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * <p>Denotes that the annotated element represents a packed color
+ * long. If applied to a long array, every element in the array
+ * represents a color long. For more information on how colors
+ * are packed in a long, please refer to the documentation of
+ * the {@link android.graphics.Color} class.</p>
+ *
+ * <p>Example:</p>
+ *
+ * <pre>{@code
+ *  public void setFillColor(@ColorLong long color);
+ * }</pre>
+ *
+ * @see android.graphics.Color
+ */
+@Retention(SOURCE)
+@Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
+public @interface ColorLong {
+}
diff --git a/annotations/src/android/support/annotation/ColorRes.java b/annotations/src/android/support/annotation/ColorRes.java
index d0fd49a..17d1e1d 100644
--- a/annotations/src/android/support/annotation/ColorRes.java
+++ b/annotations/src/android/support/annotation/ColorRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a color resource reference (e.g. {@code android.R.color.black}).
diff --git a/annotations/src/android/support/annotation/DimenRes.java b/annotations/src/android/support/annotation/DimenRes.java
index 08dc4a9..7da7d57 100644
--- a/annotations/src/android/support/annotation/DimenRes.java
+++ b/annotations/src/android/support/annotation/DimenRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a dimension resource reference (e.g. {@code android.R.dimen.app_icon_size}).
diff --git a/annotations/src/android/support/annotation/Dimension.java b/annotations/src/android/support/annotation/Dimension.java
index b8c5590..5c20edf 100644
--- a/annotations/src/android/support/annotation/Dimension.java
+++ b/annotations/src/android/support/annotation/Dimension.java
@@ -16,10 +16,6 @@
 
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@@ -27,6 +23,10 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to represent a dimension.
diff --git a/annotations/src/android/support/annotation/DimensionUnit.java b/annotations/src/android/support/annotation/DimensionUnit.java
index d4d6398..9ad8807 100644
--- a/annotations/src/android/support/annotation/DimensionUnit.java
+++ b/annotations/src/android/support/annotation/DimensionUnit.java
@@ -15,10 +15,10 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import java.lang.annotation.Retention;
+
 /**
  * Typedef for the {@link Dimension#unit} attribute.
  *
diff --git a/annotations/src/android/support/annotation/DrawableRes.java b/annotations/src/android/support/annotation/DrawableRes.java
index f130380..8ecf604 100644
--- a/annotations/src/android/support/annotation/DrawableRes.java
+++ b/annotations/src/android/support/annotation/DrawableRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a drawable resource reference (e.g. {@code android.R.attr.alertDialogIcon}).
diff --git a/annotations/src/android/support/annotation/FloatRange.java b/annotations/src/android/support/annotation/FloatRange.java
index 275b5b6..7e75933 100644
--- a/annotations/src/android/support/annotation/FloatRange.java
+++ b/annotations/src/android/support/annotation/FloatRange.java
@@ -15,9 +15,6 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@@ -25,6 +22,9 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element should be a float or double in the given range
  * <p>
diff --git a/annotations/src/android/support/annotation/FontRes.java b/annotations/src/android/support/annotation/FontRes.java
new file mode 100644
index 0000000..4baa482
--- /dev/null
+++ b/annotations/src/android/support/annotation/FontRes.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package android.support.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that an integer parameter, field or method return value is expected
+ * to be a font resource reference (e.g. {@code R.font.myfont}).
+ */
+@Documented
+@Retention(CLASS)
+@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
+public @interface FontRes {
+}
diff --git a/annotations/src/android/support/annotation/FractionRes.java b/annotations/src/android/support/annotation/FractionRes.java
index 1404866..6854c60 100644
--- a/annotations/src/android/support/annotation/FractionRes.java
+++ b/annotations/src/android/support/annotation/FractionRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a fraction resource reference.
diff --git a/annotations/src/android/support/annotation/GuardedBy.java b/annotations/src/android/support/annotation/GuardedBy.java
new file mode 100644
index 0000000..ee7d77c
--- /dev/null
+++ b/annotations/src/android/support/annotation/GuardedBy.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package android.support.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated method or field can only be accessed when holding the referenced lock.
+ * <p>
+ * Example:
+ * <pre>
+ * final Object objectLock = new Object();
+ *
+ * {@literal @}GuardedBy("objectLock")
+ * volatile Object object;
+ *
+ * Object getObject() {
+ *     synchronized (objectLock) {
+ *         if (object == null) {
+ *             object = new Object();
+ *         }
+ *     }
+ *     return object;
+ * }</pre>
+ */
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.CLASS)
+public @interface GuardedBy {
+    String value();
+}
diff --git a/annotations/src/android/support/annotation/HalfFloat.java b/annotations/src/android/support/annotation/HalfFloat.java
new file mode 100644
index 0000000..d97fe90
--- /dev/null
+++ b/annotations/src/android/support/annotation/HalfFloat.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package android.support.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * <p>Denotes that the annotated element represents a half-precision floating point
+ * value. Such values are stored in short data types and can be manipulated with
+ * the <code>android.util.Half</code> class. If applied to an array of short, every
+ * element in the array represents a half-precision float.</p>
+ *
+ * <p>Example:</p>
+ *
+ * <pre>{@code
+ * public abstract void setPosition(@HalfFloat short x, @HalfFloat short y, @HalfFloat short z);
+ * }</pre>
+ */
+@Retention(SOURCE)
+@Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
+public @interface HalfFloat {
+}
diff --git a/annotations/src/android/support/annotation/IdRes.java b/annotations/src/android/support/annotation/IdRes.java
index 7cb3f98..16fbac3 100644
--- a/annotations/src/android/support/annotation/IdRes.java
+++ b/annotations/src/android/support/annotation/IdRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an id resource reference (e.g. {@code android.R.id.copy}).
diff --git a/annotations/src/android/support/annotation/IntRange.java b/annotations/src/android/support/annotation/IntRange.java
index d489acb..960ffcd 100644
--- a/annotations/src/android/support/annotation/IntRange.java
+++ b/annotations/src/android/support/annotation/IntRange.java
@@ -15,9 +15,6 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@@ -25,6 +22,9 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element should be an int or long in the given range
  * <p>
diff --git a/annotations/src/android/support/annotation/IntegerRes.java b/annotations/src/android/support/annotation/IntegerRes.java
index d965675..a17be6f 100644
--- a/annotations/src/android/support/annotation/IntegerRes.java
+++ b/annotations/src/android/support/annotation/IntegerRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an integer resource reference (e.g. {@code android.R.integer.config_shortAnimTime}).
diff --git a/annotations/src/android/support/annotation/InterpolatorRes.java b/annotations/src/android/support/annotation/InterpolatorRes.java
index 3195c57..ccd8517 100644
--- a/annotations/src/android/support/annotation/InterpolatorRes.java
+++ b/annotations/src/android/support/annotation/InterpolatorRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an interpolator resource reference (e.g. {@code android.R.interpolator.cycle}).
diff --git a/annotations/src/android/support/annotation/Keep.java b/annotations/src/android/support/annotation/Keep.java
index 111971c..52ede05 100644
--- a/annotations/src/android/support/annotation/Keep.java
+++ b/annotations/src/android/support/annotation/Keep.java
@@ -15,9 +15,6 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
@@ -26,6 +23,9 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element should not be removed when
  * the code is minified at build time. This is typically used
diff --git a/annotations/src/android/support/annotation/LayoutRes.java b/annotations/src/android/support/annotation/LayoutRes.java
index 0de4e84..e3413b8 100644
--- a/annotations/src/android/support/annotation/LayoutRes.java
+++ b/annotations/src/android/support/annotation/LayoutRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a layout resource reference (e.g. {@code android.R.layout.list_content}).
diff --git a/annotations/src/android/support/annotation/MainThread.java b/annotations/src/android/support/annotation/MainThread.java
index 1ce8b3c..2f50306 100644
--- a/annotations/src/android/support/annotation/MainThread.java
+++ b/annotations/src/android/support/annotation/MainThread.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated method should only be called on the main thread.
  * If the annotated element is a class, then all methods in the class should be called
@@ -34,9 +34,17 @@
  *  &#64;MainThread
  *  public void deliverResult(D data) { ... }
  * </code></pre>
+ *
+ * <p class="note"><b>Note:</b> Ordinarily, an app's main thread is also the UI
+ * thread. However, However, under special circumstances, an app's main thread
+ * might not be its UI thread; for more information, see
+ * <a href="/studio/write/annotations.html#thread-annotations">Thread
+ * annotations</a>.
+ *
+ * @see android.support.annotation.UiThread
  */
 @Documented
 @Retention(CLASS)
 @Target({METHOD,CONSTRUCTOR,TYPE})
 public @interface MainThread {
-}
\ No newline at end of file
+}
diff --git a/annotations/src/android/support/annotation/MenuRes.java b/annotations/src/android/support/annotation/MenuRes.java
index 0529049..117c0e0 100644
--- a/annotations/src/android/support/annotation/MenuRes.java
+++ b/annotations/src/android/support/annotation/MenuRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a menu resource reference.
diff --git a/annotations/src/android/support/annotation/NonNull.java b/annotations/src/android/support/annotation/NonNull.java
index 708bc6d..600165a 100644
--- a/annotations/src/android/support/annotation/NonNull.java
+++ b/annotations/src/android/support/annotation/NonNull.java
@@ -17,6 +17,7 @@
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PACKAGE;
 import static java.lang.annotation.ElementType.PARAMETER;
@@ -33,6 +34,6 @@
  */
 @Documented
 @Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, ANNOTATION_TYPE, PACKAGE})
+@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
 public @interface NonNull {
 }
diff --git a/annotations/src/android/support/annotation/Nullable.java b/annotations/src/android/support/annotation/Nullable.java
index 5227625..25e5b28 100644
--- a/annotations/src/android/support/annotation/Nullable.java
+++ b/annotations/src/android/support/annotation/Nullable.java
@@ -17,6 +17,7 @@
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PACKAGE;
 import static java.lang.annotation.ElementType.PARAMETER;
@@ -40,6 +41,6 @@
  */
 @Documented
 @Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, ANNOTATION_TYPE, PACKAGE})
+@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
 public @interface Nullable {
 }
diff --git a/annotations/src/android/support/annotation/PluralsRes.java b/annotations/src/android/support/annotation/PluralsRes.java
index 509bc7b..ff20405 100644
--- a/annotations/src/android/support/annotation/PluralsRes.java
+++ b/annotations/src/android/support/annotation/PluralsRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a plurals resource reference.
diff --git a/annotations/src/android/support/annotation/ProductionVisibility.java b/annotations/src/android/support/annotation/ProductionVisibility.java
index 6bd978e..4ae1600 100644
--- a/annotations/src/android/support/annotation/ProductionVisibility.java
+++ b/annotations/src/android/support/annotation/ProductionVisibility.java
@@ -15,10 +15,10 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import java.lang.annotation.Retention;
+
 /**
  * Typedef for the {@link VisibleForTesting#otherwise} attribute.
  *
diff --git a/annotations/src/android/support/annotation/Px.java b/annotations/src/android/support/annotation/Px.java
index e04afa0..b9e8e2b 100644
--- a/annotations/src/android/support/annotation/Px.java
+++ b/annotations/src/android/support/annotation/Px.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to represent a pixel dimension.
diff --git a/annotations/src/android/support/annotation/RawRes.java b/annotations/src/android/support/annotation/RawRes.java
index b1bb47b..b482d0c 100644
--- a/annotations/src/android/support/annotation/RawRes.java
+++ b/annotations/src/android/support/annotation/RawRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a raw resource reference.
diff --git a/annotations/src/android/support/annotation/RequiresApi.java b/annotations/src/android/support/annotation/RequiresApi.java
index ddd3158..bbe386e 100644
--- a/annotations/src/android/support/annotation/RequiresApi.java
+++ b/annotations/src/android/support/annotation/RequiresApi.java
@@ -15,16 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
-
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element should only be called on the given API level
  * or higher.
diff --git a/annotations/src/android/support/annotation/RequiresPermission.java b/annotations/src/android/support/annotation/RequiresPermission.java
index 1ff1964..d1cc9ad 100644
--- a/annotations/src/android/support/annotation/RequiresPermission.java
+++ b/annotations/src/android/support/annotation/RequiresPermission.java
@@ -15,9 +15,6 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
@@ -25,6 +22,9 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element requires (or may require) one or more permissions.
  * <p>
diff --git a/annotations/src/android/support/annotation/Size.java b/annotations/src/android/support/annotation/Size.java
index b1c0308..6ee72dc 100644
--- a/annotations/src/android/support/annotation/Size.java
+++ b/annotations/src/android/support/annotation/Size.java
@@ -15,9 +15,6 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@@ -25,6 +22,9 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element should have a given size or length.
  * Note that "-1" means "unset". Typically used with a parameter or
diff --git a/annotations/src/android/support/annotation/StringDef.java b/annotations/src/android/support/annotation/StringDef.java
index 33f71e9..a8067b7 100644
--- a/annotations/src/android/support/annotation/StringDef.java
+++ b/annotations/src/android/support/annotation/StringDef.java
@@ -15,15 +15,12 @@
  */
 package android.support.annotation;
 
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
 /**
  * Denotes that the annotated String element, represents a logical
  * type and that its value should be one of the explicitly named constants.
diff --git a/annotations/src/android/support/annotation/StringRes.java b/annotations/src/android/support/annotation/StringRes.java
index cf427f6..baf9853 100644
--- a/annotations/src/android/support/annotation/StringRes.java
+++ b/annotations/src/android/support/annotation/StringRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a String resource reference (e.g. {@code android.R.string.ok}).
diff --git a/annotations/src/android/support/annotation/StyleRes.java b/annotations/src/android/support/annotation/StyleRes.java
index ca7a187..11d16c8 100644
--- a/annotations/src/android/support/annotation/StyleRes.java
+++ b/annotations/src/android/support/annotation/StyleRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a style resource reference (e.g. {@code android.R.style.TextAppearance}).
diff --git a/annotations/src/android/support/annotation/StyleableRes.java b/annotations/src/android/support/annotation/StyleableRes.java
index 6e4d97f..48881cc 100644
--- a/annotations/src/android/support/annotation/StyleableRes.java
+++ b/annotations/src/android/support/annotation/StyleableRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a styleable resource reference (e.g. {@code android.R.styleable.TextView_text}).
diff --git a/annotations/src/android/support/annotation/TransitionRes.java b/annotations/src/android/support/annotation/TransitionRes.java
index d1c5208..114a6b0 100644
--- a/annotations/src/android/support/annotation/TransitionRes.java
+++ b/annotations/src/android/support/annotation/TransitionRes.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be a transition resource reference.
diff --git a/annotations/src/android/support/annotation/UiThread.java b/annotations/src/android/support/annotation/UiThread.java
index 98d35f9..0a9a0c1 100644
--- a/annotations/src/android/support/annotation/UiThread.java
+++ b/annotations/src/android/support/annotation/UiThread.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated method or constructor should only be called on the UI thread.
  * If the annotated element is a class, then all methods in the class should be called
@@ -35,9 +35,17 @@
  *
  *  public abstract void setText(@NonNull String text) { ... }
  * </code></pre>
+ *
+ * <p class="note"><b>Note:</b> Ordinarily, an app's UI thread is also the main
+ * thread. However, However, under special circumstances, an app's UI thread
+ * might not be its main thread; for more information, see
+ * <a href="/studio/write/annotations.html#thread-annotations">Thread
+ * annotations</a>.
+ *
+ * @see android.support.annotation.MainThread
  */
 @Documented
 @Retention(CLASS)
 @Target({METHOD,CONSTRUCTOR,TYPE})
 public @interface UiThread {
-}
\ No newline at end of file
+}
diff --git a/annotations/src/android/support/annotation/VisibleForTesting.java b/annotations/src/android/support/annotation/VisibleForTesting.java
index 16d915a..ed685b6 100644
--- a/annotations/src/android/support/annotation/VisibleForTesting.java
+++ b/annotations/src/android/support/annotation/VisibleForTesting.java
@@ -15,10 +15,10 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Retention;
-
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Retention;
+
 /**
  * Denotes that the class, method or field has its visibility relaxed, so that it is more widely
  * visible than otherwise necessary to make code testable.
diff --git a/annotations/src/android/support/annotation/WorkerThread.java b/annotations/src/android/support/annotation/WorkerThread.java
index a5aabd7..237aa66 100644
--- a/annotations/src/android/support/annotation/WorkerThread.java
+++ b/annotations/src/android/support/annotation/WorkerThread.java
@@ -15,15 +15,15 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated method should only be called on a worker thread.
  * If the annotated element is a class, then all methods in the class should be called
diff --git a/annotations/src/android/support/annotation/XmlRes.java b/annotations/src/android/support/annotation/XmlRes.java
index 2b8b9fa..5f06266 100644
--- a/annotations/src/android/support/annotation/XmlRes.java
+++ b/annotations/src/android/support/annotation/XmlRes.java
@@ -15,16 +15,16 @@
  */
 package android.support.annotation;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that an integer parameter, field or method return value is expected
  * to be an XML resource reference.
diff --git a/api/25.4.0.ignore b/api/25.4.0.ignore
new file mode 100644
index 0000000..97d6ff8
--- /dev/null
+++ b/api/25.4.0.ignore
@@ -0,0 +1,9 @@
+3e22d56
+2709ab8
+bcec076
+0eb949f
+a6337a1
+24c3865
+a684458
+7c9f9f7
+18b5692
\ No newline at end of file
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
new file mode 100644
index 0000000..fe0c575
--- /dev/null
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -0,0 +1,13266 @@
+package android.support.animation {
+
+  public abstract class DynamicAnimation<T extends android.support.animation.DynamicAnimation<T>> {
+    method public T addEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public T addUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public void cancel();
+    method public float getMinimumVisibleChange();
+    method public boolean isRunning();
+    method public void removeEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public void removeUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public T setMaxValue(float);
+    method public T setMinValue(float);
+    method public T setMinimumVisibleChange(float);
+    method public T setStartValue(float);
+    method public T setStartVelocity(float);
+    method public void start();
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ALPHA;
+    field public static final float MIN_VISIBLE_CHANGE_ALPHA = 0.00390625f;
+    field public static final float MIN_VISIBLE_CHANGE_PIXELS = 1.0f;
+    field public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 0.1f;
+    field public static final float MIN_VISIBLE_CHANGE_SCALE = 0.002f;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Z;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Z;
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationEndListener {
+    method public abstract void onAnimationEnd(android.support.animation.DynamicAnimation, boolean, float, float);
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationUpdateListener {
+    method public abstract void onAnimationUpdate(android.support.animation.DynamicAnimation, float, float);
+  }
+
+  public static abstract class DynamicAnimation.ViewProperty extends android.support.animation.FloatPropertyCompat {
+  }
+
+  public final class FlingAnimation extends android.support.animation.DynamicAnimation {
+    ctor public FlingAnimation(android.support.animation.FloatValueHolder);
+    ctor public FlingAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    method public float getFriction();
+    method public android.support.animation.FlingAnimation setFriction(float);
+    method public android.support.animation.FlingAnimation setMaxValue(float);
+    method public android.support.animation.FlingAnimation setMinValue(float);
+    method public android.support.animation.FlingAnimation setStartVelocity(float);
+  }
+
+  public abstract class FloatPropertyCompat<T> {
+    ctor public FloatPropertyCompat(java.lang.String);
+    method public static <T> android.support.animation.FloatPropertyCompat<T> createFloatPropertyCompat(android.util.FloatProperty<T>);
+    method public abstract float getValue(T);
+    method public abstract void setValue(T, float);
+  }
+
+  public final class FloatValueHolder {
+    ctor public FloatValueHolder();
+    ctor public FloatValueHolder(float);
+    method public float getValue();
+    method public void setValue(float);
+  }
+
+  public final class SpringAnimation extends android.support.animation.DynamicAnimation {
+    ctor public SpringAnimation(android.support.animation.FloatValueHolder);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>, float);
+    method public void animateToFinalPosition(float);
+    method public boolean canSkipToEnd();
+    method public android.support.animation.SpringForce getSpring();
+    method public android.support.animation.SpringAnimation setSpring(android.support.animation.SpringForce);
+    method public void skipToEnd();
+  }
+
+  public final class SpringForce {
+    ctor public SpringForce();
+    ctor public SpringForce(float);
+    method public float getDampingRatio();
+    method public float getFinalPosition();
+    method public float getStiffness();
+    method public android.support.animation.SpringForce setDampingRatio(float);
+    method public android.support.animation.SpringForce setFinalPosition(float);
+    method public android.support.animation.SpringForce setStiffness(float);
+    field public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
+    field public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
+    field public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
+    field public static final float DAMPING_RATIO_NO_BOUNCY = 1.0f;
+    field public static final float STIFFNESS_HIGH = 10000.0f;
+    field public static final float STIFFNESS_LOW = 200.0f;
+    field public static final float STIFFNESS_MEDIUM = 1500.0f;
+    field public static final float STIFFNESS_VERY_LOW = 50.0f;
+  }
+
+}
+
+package android.support.app.recommendation {
+
+  public final class ContentRecommendation {
+    method public java.lang.String getBackgroundImageUri();
+    method public int getBadgeImageResourceId();
+    method public int getColor();
+    method public android.graphics.Bitmap getContentImage();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
+    method public java.lang.String[] getContentTypes();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getGroup();
+    method public java.lang.String getIdTag();
+    method public java.lang.String getMaturityRating();
+    method public android.app.Notification getNotificationObject(android.content.Context);
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public int getProgressMax();
+    method public int getProgressValue();
+    method public long getRunningTime();
+    method public java.lang.String getSortKey();
+    method public java.lang.String getSourceName();
+    method public int getStatus();
+    method public java.lang.String getText();
+    method public java.lang.String getTitle();
+    method public boolean hasProgressInfo();
+    method public boolean isAutoDismiss();
+    method public void setAutoDismiss(boolean);
+    method public void setGroup(java.lang.String);
+    method public void setProgress(int, int);
+    method public void setSortKey(java.lang.String);
+    method public void setStatus(int);
+    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
+    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
+    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
+    field public static final int CONTENT_STATUS_READY = 0; // 0x0
+    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
+    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
+    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
+    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
+    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
+    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
+    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
+    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
+    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
+    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
+    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
+  }
+
+  public static final class ContentRecommendation.Builder {
+    ctor public ContentRecommendation.Builder();
+    method public android.support.app.recommendation.ContentRecommendation build();
+    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
+  }
+
+  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
+  }
+
+  public static class ContentRecommendation.IntentData {
+    ctor public ContentRecommendation.IntentData();
+  }
+
+  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
+  }
+
+  public final class RecommendationExtender implements android.app.Notification.Extender {
+    ctor public RecommendationExtender();
+    ctor public RecommendationExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String[] getContentTypes();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getMaturityRating();
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public long getRunningTime();
+    method public int getStatus();
+    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
+    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
+  }
+
+}
+
+package android.support.customtabs {
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(java.lang.String, android.os.Bundle);
+    method public void onMessageChannelReady(android.os.Bundle);
+    method public void onNavigationEvent(int, android.os.Bundle);
+    method public void onPostMessage(java.lang.String, android.os.Bundle);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, java.lang.String, android.support.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, java.lang.String);
+    method public android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>, boolean);
+    method public android.support.customtabs.CustomTabsSession newSession(android.support.customtabs.CustomTabsCallback);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context, android.net.Uri);
+    method public static android.content.Intent setAlwaysUseBrowserUI(android.content.Intent);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent);
+    field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final java.lang.String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final java.lang.String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final java.lang.String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final java.lang.String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final java.lang.String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final java.lang.String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
+    method public android.support.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
+    method public deprecated android.support.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, java.lang.String, android.app.PendingIntent) throws java.lang.IllegalStateException;
+    method public android.support.customtabs.CustomTabsIntent build();
+    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setToolbarColor(int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(android.support.customtabs.CustomTabsSessionToken);
+    method protected abstract android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract int postMessage(android.support.customtabs.CustomTabsSessionToken, java.lang.String, android.os.Bundle);
+    method protected abstract boolean requestPostMessageChannel(android.support.customtabs.CustomTabsSessionToken, android.net.Uri);
+    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
+    method protected abstract boolean warmup(long);
+    field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class CustomTabsService.Result implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName, android.support.customtabs.CustomTabsClient);
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+  }
+
+  public final class CustomTabsSession {
+    method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method public int postMessage(java.lang.String, android.os.Bundle);
+    method public boolean requestPostMessageChannel(android.net.Uri);
+    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public deprecated boolean setToolbarItem(int, android.graphics.Bitmap, java.lang.String);
+  }
+
+  public class CustomTabsSessionToken {
+    method public android.support.customtabs.CustomTabsCallback getCallback();
+    method public static android.support.customtabs.CustomTabsSessionToken getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(android.support.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(android.support.customtabs.CustomTabsSessionToken);
+    method public boolean bindSessionToPostMessageService(android.content.Context, java.lang.String);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+    method public final void onServiceDisconnected(android.content.ComponentName);
+    method public final boolean postMessage(java.lang.String, android.os.Bundle);
+    method public void unbindFromContext(android.content.Context);
+  }
+
+}
+
+package android.support.design.widget {
+
+  public class AppBarLayout extends android.widget.LinearLayout {
+    ctor public AppBarLayout(android.content.Context);
+    ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
+    method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method protected android.support.design.widget.AppBarLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.design.widget.AppBarLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.design.widget.AppBarLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public deprecated float getTargetElevation();
+    method public final int getTotalScrollRange();
+    method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public void setExpanded(boolean);
+    method public void setExpanded(boolean, boolean);
+    method public deprecated void setTargetElevation(float);
+  }
+
+  public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
+    ctor public AppBarLayout.Behavior();
+    ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int, int, int, int);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int);
+    method public void setDragCallback(android.support.design.widget.AppBarLayout.Behavior.DragCallback);
+  }
+
+  public static abstract class AppBarLayout.Behavior.DragCallback {
+    ctor public AppBarLayout.Behavior.DragCallback();
+    method public abstract boolean canDrag(android.support.design.widget.AppBarLayout);
+  }
+
+  protected static class AppBarLayout.Behavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+  }
+
+  public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public AppBarLayout.LayoutParams(int, int);
+    ctor public AppBarLayout.LayoutParams(int, int, float);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.widget.LinearLayout.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.support.design.widget.AppBarLayout.LayoutParams);
+    method public int getScrollFlags();
+    method public android.view.animation.Interpolator getScrollInterpolator();
+    method public void setScrollFlags(int);
+    method public void setScrollInterpolator(android.view.animation.Interpolator);
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS = 4; // 0x4
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
+    field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
+    field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
+    field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
+  }
+
+  public static abstract interface AppBarLayout.OnOffsetChangedListener {
+    method public abstract void onOffsetChanged(android.support.design.widget.AppBarLayout, int);
+  }
+
+  public static class AppBarLayout.ScrollingViewBehavior extends android.support.design.widget.HeaderScrollingViewBehavior {
+    ctor public AppBarLayout.ScrollingViewBehavior();
+    ctor public AppBarLayout.ScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
+  }
+
+  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
+    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
+    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public void dismiss();
+    method public android.content.Context getContext();
+    method public int getDuration();
+    method public android.view.View getView();
+    method public boolean isShown();
+    method public boolean isShownOrQueued();
+    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public B setDuration(int);
+    method public void show();
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
+    ctor public BaseTransientBottomBar.BaseCallback();
+    method public void onDismissed(B, int);
+    method public void onShown(B);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
+    method public abstract void animateContentIn(int, int);
+    method public abstract void animateContentOut(int, int);
+  }
+
+  public class BottomNavigationView extends android.widget.FrameLayout {
+    ctor public BottomNavigationView(android.content.Context);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public int getItemBackgroundResource();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public int getMaxItemCount();
+    method public android.view.Menu getMenu();
+    method public int getSelectedItemId();
+    method public void inflateMenu(int);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setOnNavigationItemReselectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemReselectedListener);
+    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
+    method public void setSelectedItemId(int);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemReselectedListener {
+    method public abstract void onNavigationItemReselected(android.view.MenuItem);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public class BottomSheetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public BottomSheetBehavior();
+    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
+    method public static <V extends android.view.View> android.support.design.widget.BottomSheetBehavior<V> from(V);
+    method public final int getPeekHeight();
+    method public boolean getSkipCollapsed();
+    method public final int getState();
+    method public boolean isHideable();
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void setBottomSheetCallback(android.support.design.widget.BottomSheetBehavior.BottomSheetCallback);
+    method public void setHideable(boolean);
+    method public final void setPeekHeight(int);
+    method public void setSkipCollapsed(boolean);
+    method public final void setState(int);
+    field public static final int PEEK_HEIGHT_AUTO = -1; // 0xffffffff
+    field public static final int STATE_COLLAPSED = 4; // 0x4
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_EXPANDED = 3; // 0x3
+    field public static final int STATE_HIDDEN = 5; // 0x5
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class BottomSheetBehavior.BottomSheetCallback {
+    ctor public BottomSheetBehavior.BottomSheetCallback();
+    method public abstract void onSlide(android.view.View, float);
+    method public abstract void onStateChanged(android.view.View, int);
+  }
+
+  protected static class BottomSheetBehavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
+  }
+
+  public class BottomSheetDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public BottomSheetDialog(android.content.Context);
+    ctor public BottomSheetDialog(android.content.Context, int);
+    ctor protected BottomSheetDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+  }
+
+  public class BottomSheetDialogFragment extends android.support.v7.app.AppCompatDialogFragment {
+    ctor public BottomSheetDialogFragment();
+  }
+
+  public class CollapsingToolbarLayout extends android.widget.FrameLayout {
+    ctor public CollapsingToolbarLayout(android.content.Context);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.design.widget.CollapsingToolbarLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.widget.FrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.widget.FrameLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getCollapsedTitleGravity();
+    method public android.graphics.Typeface getCollapsedTitleTypeface();
+    method public android.graphics.drawable.Drawable getContentScrim();
+    method public int getExpandedTitleGravity();
+    method public int getExpandedTitleMarginBottom();
+    method public int getExpandedTitleMarginEnd();
+    method public int getExpandedTitleMarginStart();
+    method public int getExpandedTitleMarginTop();
+    method public android.graphics.Typeface getExpandedTitleTypeface();
+    method public long getScrimAnimationDuration();
+    method public int getScrimVisibleHeightTrigger();
+    method public android.graphics.drawable.Drawable getStatusBarScrim();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isTitleEnabled();
+    method public void setCollapsedTitleGravity(int);
+    method public void setCollapsedTitleTextAppearance(int);
+    method public void setCollapsedTitleTextColor(int);
+    method public void setCollapsedTitleTextColor(android.content.res.ColorStateList);
+    method public void setCollapsedTitleTypeface(android.graphics.Typeface);
+    method public void setContentScrim(android.graphics.drawable.Drawable);
+    method public void setContentScrimColor(int);
+    method public void setContentScrimResource(int);
+    method public void setExpandedTitleColor(int);
+    method public void setExpandedTitleGravity(int);
+    method public void setExpandedTitleMargin(int, int, int, int);
+    method public void setExpandedTitleMarginBottom(int);
+    method public void setExpandedTitleMarginEnd(int);
+    method public void setExpandedTitleMarginStart(int);
+    method public void setExpandedTitleMarginTop(int);
+    method public void setExpandedTitleTextAppearance(int);
+    method public void setExpandedTitleTextColor(android.content.res.ColorStateList);
+    method public void setExpandedTitleTypeface(android.graphics.Typeface);
+    method public void setScrimAnimationDuration(long);
+    method public void setScrimVisibleHeightTrigger(int);
+    method public void setScrimsShown(boolean);
+    method public void setScrimsShown(boolean, boolean);
+    method public void setStatusBarScrim(android.graphics.drawable.Drawable);
+    method public void setStatusBarScrimColor(int);
+    method public void setStatusBarScrimResource(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleEnabled(boolean);
+  }
+
+  public static class CollapsingToolbarLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public CollapsingToolbarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    method public int getCollapseMode();
+    method public float getParallaxMultiplier();
+    method public void setCollapseMode(int);
+    method public void setParallaxMultiplier(float);
+    field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
+    field public static final int COLLAPSE_MODE_PARALLAX = 2; // 0x2
+    field public static final int COLLAPSE_MODE_PIN = 1; // 0x1
+  }
+
+  public class CoordinatorLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingParent2 {
+    ctor public CoordinatorLayout(android.content.Context);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void dispatchDependentViewsChanged(android.view.View);
+    method public boolean doViewsOverlap(android.view.View, android.view.View);
+    method protected android.support.design.widget.CoordinatorLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.design.widget.CoordinatorLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.design.widget.CoordinatorLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public java.util.List<android.view.View> getDependencies(android.view.View);
+    method public java.util.List<android.view.View> getDependents(android.view.View);
+    method public android.graphics.drawable.Drawable getStatusBarBackground();
+    method public boolean isPointInChildBounds(android.view.View, int, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onLayoutChild(android.view.View, int);
+    method public void onMeasureChild(android.view.View, int, int, int, int);
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackgroundColor(int);
+    method public void setStatusBarBackgroundResource(int);
+  }
+
+  public static abstract class CoordinatorLayout.Behavior<V extends android.view.View> {
+    ctor public CoordinatorLayout.Behavior();
+    ctor public CoordinatorLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean blocksInteractionBelow(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect);
+    method public int getScrimColor(android.support.design.widget.CoordinatorLayout, V);
+    method public float getScrimOpacity(android.support.design.widget.CoordinatorLayout, V);
+    method public static java.lang.Object getTag(android.view.View);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.support.design.widget.CoordinatorLayout, V, android.support.v4.view.WindowInsetsCompat);
+    method public void onAttachedToLayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDependentViewRemoved(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDetachedFromLayoutParams();
+    method public boolean onInterceptTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, V, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float);
+    method public deprecated void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[], int);
+    method public deprecated void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int, int);
+    method public deprecated void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int, int);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect, boolean);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
+    method public deprecated boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int, int);
+    method public deprecated void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int);
+    method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public static void setTag(android.view.View, java.lang.Object);
+  }
+
+  public static abstract class CoordinatorLayout.DefaultBehavior implements java.lang.annotation.Annotation {
+  }
+
+  public static class CoordinatorLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public CoordinatorLayout.LayoutParams(int, int);
+    ctor public CoordinatorLayout.LayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAnchorId();
+    method public android.support.design.widget.CoordinatorLayout.Behavior getBehavior();
+    method public void setAnchorId(int);
+    method public void setBehavior(android.support.design.widget.CoordinatorLayout.Behavior);
+    field public int anchorGravity;
+    field public int dodgeInsetEdges;
+    field public int gravity;
+    field public int insetEdge;
+    field public int keyline;
+  }
+
+  protected static class CoordinatorLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public CoordinatorLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+  }
+
+  public class FloatingActionButton extends android.support.design.widget.VisibilityAwareImageButton {
+    ctor public FloatingActionButton(android.content.Context);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+    method public float getCompatElevation();
+    method public android.graphics.drawable.Drawable getContentBackground();
+    method public boolean getContentRect(android.graphics.Rect);
+    method public int getRippleColor();
+    method public int getSize();
+    method public boolean getUseCompatPadding();
+    method public void hide();
+    method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setCompatElevation(float);
+    method public void setRippleColor(int);
+    method public void setSize(int);
+    method public void setUseCompatPadding(boolean);
+    method public void show();
+    method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    field public static final int SIZE_AUTO = -1; // 0xffffffff
+    field public static final int SIZE_MINI = 1; // 0x1
+    field public static final int SIZE_NORMAL = 0; // 0x0
+  }
+
+  public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public FloatingActionButton.Behavior();
+    ctor public FloatingActionButton.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.graphics.Rect);
+    method public boolean isAutoHideEnabled();
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.view.View);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, int);
+    method public void setAutoHideEnabled(boolean);
+  }
+
+  public static abstract class FloatingActionButton.OnVisibilityChangedListener {
+    ctor public FloatingActionButton.OnVisibilityChangedListener();
+    method public void onHidden(android.support.design.widget.FloatingActionButton);
+    method public void onShown(android.support.design.widget.FloatingActionButton);
+  }
+
+   abstract class HeaderBehavior<V extends android.view.View> extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderBehavior();
+    ctor public HeaderBehavior(android.content.Context, android.util.AttributeSet);
+  }
+
+   abstract class HeaderScrollingViewBehavior extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderScrollingViewBehavior();
+    ctor public HeaderScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public final int getOverlayTop();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
+    method public final void setOverlayTop(int);
+  }
+
+  public class NavigationView extends android.widget.FrameLayout {
+    ctor public NavigationView(android.content.Context);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public void addHeaderView(android.view.View);
+    method public int getHeaderCount();
+    method public android.view.View getHeaderView(int);
+    method public android.graphics.drawable.Drawable getItemBackground();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public android.view.Menu getMenu();
+    method public android.view.View inflateHeaderView(int);
+    method public void inflateMenu(int);
+    method public void removeHeaderView(android.view.View);
+    method public void setCheckedItem(int);
+    method public void setItemBackground(android.graphics.drawable.Drawable);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextAppearance(int);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setNavigationItemSelectedListener(android.support.design.widget.NavigationView.OnNavigationItemSelectedListener);
+  }
+
+  public static abstract interface NavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public static class NavigationView.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public NavigationView.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public NavigationView.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.NavigationView.SavedState> CREATOR;
+    field public android.os.Bundle menuState;
+  }
+
+  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
+    method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
+    method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
+    method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
+    method public android.support.design.widget.Snackbar setActionTextColor(int);
+    method public deprecated android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
+    method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
+    method public android.support.design.widget.Snackbar setText(int);
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
+    ctor public Snackbar.Callback();
+    method public void onDismissed(android.support.design.widget.Snackbar, int);
+    method public void onShown(android.support.design.widget.Snackbar);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public SwipeDismissBehavior();
+    method public boolean canSwipeDismissView(android.view.View);
+    method public int getDragState();
+    method public void setDragDismissDistance(float);
+    method public void setEndAlphaSwipeDistance(float);
+    method public void setListener(android.support.design.widget.SwipeDismissBehavior.OnDismissListener);
+    method public void setSensitivity(float);
+    method public void setStartAlphaSwipeDistance(float);
+    method public void setSwipeDirection(int);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_ANY = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_END_TO_START = 1; // 0x1
+    field public static final int SWIPE_DIRECTION_START_TO_END = 0; // 0x0
+  }
+
+  public static abstract interface SwipeDismissBehavior.OnDismissListener {
+    method public abstract void onDismiss(android.view.View);
+    method public abstract void onDragStateChanged(int);
+  }
+
+  public final class TabItem extends android.view.View {
+    ctor public TabItem(android.content.Context);
+    ctor public TabItem(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class TabLayout extends android.widget.HorizontalScrollView {
+    ctor public TabLayout(android.content.Context);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void addTab(android.support.design.widget.TabLayout.Tab);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
+    method public void clearOnTabSelectedListeners();
+    method public android.widget.FrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method public int getSelectedTabPosition();
+    method public android.support.design.widget.TabLayout.Tab getTabAt(int);
+    method public int getTabCount();
+    method public int getTabGravity();
+    method public int getTabMode();
+    method public android.content.res.ColorStateList getTabTextColors();
+    method public android.support.design.widget.TabLayout.Tab newTab();
+    method public void removeAllTabs();
+    method public void removeOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void removeTab(android.support.design.widget.TabLayout.Tab);
+    method public void removeTabAt(int);
+    method public deprecated void setOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void setScrollPosition(int, float, boolean);
+    method public void setSelectedTabIndicatorColor(int);
+    method public void setSelectedTabIndicatorHeight(int);
+    method public void setTabGravity(int);
+    method public void setTabMode(int);
+    method public void setTabTextColors(android.content.res.ColorStateList);
+    method public void setTabTextColors(int, int);
+    method public deprecated void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager, boolean);
+    field public static final int GRAVITY_CENTER = 1; // 0x1
+    field public static final int GRAVITY_FILL = 0; // 0x0
+    field public static final int MODE_FIXED = 1; // 0x1
+    field public static final int MODE_SCROLLABLE = 0; // 0x0
+  }
+
+  public static abstract interface TabLayout.OnTabSelectedListener {
+    method public abstract void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public static final class TabLayout.Tab {
+    method public java.lang.CharSequence getContentDescription();
+    method public android.view.View getCustomView();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public int getPosition();
+    method public java.lang.Object getTag();
+    method public java.lang.CharSequence getText();
+    method public boolean isSelected();
+    method public void select();
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(int);
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(android.view.View);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(int);
+    method public android.support.design.widget.TabLayout.Tab setIcon(android.graphics.drawable.Drawable);
+    method public android.support.design.widget.TabLayout.Tab setIcon(int);
+    method public android.support.design.widget.TabLayout.Tab setTag(java.lang.Object);
+    method public android.support.design.widget.TabLayout.Tab setText(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public class TextInputEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public TextInputEditText(android.content.Context);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class TextInputLayout extends android.widget.LinearLayout {
+    ctor public TextInputLayout(android.content.Context);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCounterMaxLength();
+    method public android.widget.EditText getEditText();
+    method public java.lang.CharSequence getError();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
+    method public android.graphics.Typeface getTypeface();
+    method public boolean isCounterEnabled();
+    method public boolean isErrorEnabled();
+    method public boolean isHintAnimationEnabled();
+    method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void setCounterEnabled(boolean);
+    method public void setCounterMaxLength(int);
+    method public void setError(java.lang.CharSequence);
+    method public void setErrorEnabled(boolean);
+    method public void setErrorTextAppearance(int);
+    method public void setHint(java.lang.CharSequence);
+    method public void setHintAnimationEnabled(boolean);
+    method public void setHintEnabled(boolean);
+    method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTypeface(android.graphics.Typeface);
+  }
+
+   class ViewOffsetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public ViewOffsetBehavior();
+    ctor public ViewOffsetBehavior(android.content.Context, android.util.AttributeSet);
+    method public int getLeftAndRightOffset();
+    method public int getTopAndBottomOffset();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean setLeftAndRightOffset(int);
+    method public boolean setTopAndBottomOffset(int);
+  }
+
+   class VisibilityAwareImageButton extends android.widget.ImageButton {
+    ctor public VisibilityAwareImageButton(android.content.Context);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+}
+
+package android.support.graphics.drawable {
+
+  public abstract interface Animatable2Compat {
+    method public abstract void clearAnimationCallbacks();
+    method public abstract void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public abstract boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+  public static abstract class Animatable2Compat.AnimationCallback {
+    ctor public Animatable2Compat.AnimationCallback();
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
+  }
+
+  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon implements android.support.graphics.drawable.Animatable2Compat {
+    method public void clearAnimationCallbacks();
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public boolean isRunning();
+    method public void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void start();
+    method public void stop();
+    method public boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
+  }
+
+  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
+    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+  }
+
+}
+
+package android.support.media {
+
+  public class ExifInterface {
+    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
+    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
+    method public void flipHorizontally();
+    method public void flipVertically();
+    method public double getAltitude(double);
+    method public java.lang.String getAttribute(java.lang.String);
+    method public double getAttributeDouble(java.lang.String, double);
+    method public int getAttributeInt(java.lang.String, int);
+    method public deprecated boolean getLatLong(float[]);
+    method public double[] getLatLong();
+    method public byte[] getThumbnail();
+    method public android.graphics.Bitmap getThumbnailBitmap();
+    method public byte[] getThumbnailBytes();
+    method public long[] getThumbnailRange();
+    method public boolean hasThumbnail();
+    method public boolean isThumbnailCompressed();
+    method public void resetOrientation();
+    method public void rotate(int);
+    method public void saveAttributes() throws java.io.IOException;
+    method public void setAltitude(double);
+    method public void setAttribute(java.lang.String, java.lang.String);
+    method public void setGpsInfo(android.location.Location);
+    method public void setLatLong(double, double);
+    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
+    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
+    field public static final int ORIENTATION_NORMAL = 1; // 0x1
+    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
+    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
+    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
+    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
+    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
+    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+    field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+    field public static final java.lang.String TAG_COMPRESSION = "Compression";
+    field public static final java.lang.String TAG_CONTRAST = "Contrast";
+    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
+    field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
+    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
+    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
+    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
+    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
+    field public static final java.lang.String TAG_FLASH = "Flash";
+    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
+    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
+    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
+    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
+    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
+    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
+    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
+    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
+    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
+    field public static final java.lang.String TAG_MAKE = "Make";
+    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
+    field public static final java.lang.String TAG_MODEL = "Model";
+    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
+    field public static final java.lang.String TAG_OECF = "OECF";
+    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
+    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
+    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+    field public static final java.lang.String TAG_RW2_ISO = "ISO";
+    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
+    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+    field public static final java.lang.String TAG_SATURATION = "Saturation";
+    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+    field public static final java.lang.String TAG_SOFTWARE = "Software";
+    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
+    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
+    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
+    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
+    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
+  }
+
+}
+
+package android.support.media.tv {
+
+  public final class Channel {
+    method public static android.support.media.tv.Channel fromCursor(android.database.Cursor);
+    method public int getAppLinkColor();
+    method public android.net.Uri getAppLinkIconUri();
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public android.net.Uri getAppLinkPosterArtUri();
+    method public java.lang.String getAppLinkText();
+    method public java.lang.String getDescription();
+    method public java.lang.String getDisplayName();
+    method public java.lang.String getDisplayNumber();
+    method public long getId();
+    method public java.lang.String getInputId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getInternalProviderId();
+    method public java.lang.String getNetworkAffiliation();
+    method public int getOriginalNetworkId();
+    method public java.lang.String getPackageName();
+    method public int getServiceId();
+    method public java.lang.String getServiceType();
+    method public int getTransportStreamId();
+    method public java.lang.String getType();
+    method public java.lang.String getVideoFormat();
+    method public boolean isBrowsable();
+    method public boolean isLocked();
+    method public boolean isSearchable();
+    method public boolean isTransient();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static final class Channel.Builder {
+    ctor public Channel.Builder();
+    ctor public Channel.Builder(android.support.media.tv.Channel);
+    method public android.support.media.tv.Channel build();
+    method public android.support.media.tv.Channel.Builder setAppLinkColor(int);
+    method public android.support.media.tv.Channel.Builder setAppLinkIconUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkPosterArtUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkText(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDescription(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayName(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayNumber(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInputId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setNetworkAffiliation(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setOriginalNetworkId(int);
+    method public android.support.media.tv.Channel.Builder setSearchable(boolean);
+    method public android.support.media.tv.Channel.Builder setServiceId(int);
+    method public android.support.media.tv.Channel.Builder setServiceType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setTransient(boolean);
+    method public android.support.media.tv.Channel.Builder setTransportStreamId(int);
+    method public android.support.media.tv.Channel.Builder setType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
+  }
+
+  public class ChannelLogoUtils {
+    ctor public ChannelLogoUtils();
+    method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
+  }
+
+  public final class PreviewProgram {
+    method public boolean equals(java.lang.Object);
+    method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
+    method public long getChannelId();
+    method public int getWeight();
+    method public android.content.ContentValues toContentValues();
+    method public java.lang.String toString();
+  }
+
+  public static final class PreviewProgram.Builder {
+    ctor public PreviewProgram.Builder();
+    ctor public PreviewProgram.Builder(android.support.media.tv.PreviewProgram);
+    method public android.support.media.tv.PreviewProgram build();
+    method public android.support.media.tv.PreviewProgram.Builder setChannelId(long);
+    method public android.support.media.tv.PreviewProgram.Builder setWeight(int);
+  }
+
+  public final class Program implements java.lang.Comparable {
+    method public int compareTo(android.support.media.tv.Program);
+    method public boolean equals(java.lang.Object);
+    method public static android.support.media.tv.Program fromCursor(android.database.Cursor);
+    method public java.lang.String[] getBroadcastGenres();
+    method public long getChannelId();
+    method public long getEndTimeUtcMillis();
+    method public long getStartTimeUtcMillis();
+    method public int hashCode();
+    method public boolean isRecordingProhibited();
+    method public android.content.ContentValues toContentValues();
+    method public java.lang.String toString();
+  }
+
+  public static class Program.Builder {
+    ctor public Program.Builder();
+    ctor public Program.Builder(android.support.media.tv.Program);
+    method public android.support.media.tv.Program build();
+    method public android.support.media.tv.Program.Builder setBroadcastGenres(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setChannelId(long);
+    method public android.support.media.tv.Program.Builder setEndTimeUtcMillis(long);
+    method public android.support.media.tv.Program.Builder setRecordingProhibited(boolean);
+    method public android.support.media.tv.Program.Builder setStartTimeUtcMillis(long);
+  }
+
+  public final class TvContractCompat {
+    method public static android.net.Uri buildChannelLogoUri(long);
+    method public static android.net.Uri buildChannelLogoUri(android.net.Uri);
+    method public static android.net.Uri buildChannelUri(long);
+    method public static android.net.Uri buildChannelUriForPassthroughInput(java.lang.String);
+    method public static android.net.Uri buildChannelsUriForInput(java.lang.String);
+    method public static java.lang.String buildInputId(android.content.ComponentName);
+    method public static android.net.Uri buildPreviewProgramUri(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramUri(long);
+    method public static android.net.Uri buildProgramsUriForChannel(long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramsUriForChannel(long, long, long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static android.net.Uri buildRecordedProgramUri(long);
+    method public static android.net.Uri buildWatchNextProgramUri(long);
+    method public static boolean isChannelUri(android.net.Uri);
+    method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
+    method public static boolean isChannelUriForTunerInput(android.net.Uri);
+    method public static boolean isProgramUri(android.net.Uri);
+    method public static boolean isRecordedProgramUri(android.net.Uri);
+    method public static void requestChannelBrowsable(android.content.Context, long);
+    field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
+    field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String AUTHORITY = "android.media.tv";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
+    field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
+  }
+
+  public static abstract interface TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
+  }
+
+  public static final class TvContractCompat.Channels implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    method public static java.lang.String getVideoResolution(java.lang.String);
+    field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
+    field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
+    field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
+    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_LOCKED = "locked";
+    field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
+    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+    field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+    field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+    field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+    field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+    field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+    field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+    field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+    field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+    field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+    field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+    field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+    field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+    field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+    field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+    field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+    field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+    field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+    field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+    field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+    field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+    field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+    field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+    field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
+    field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+    field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+    field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+  }
+
+  public static final class TvContractCompat.Channels.Logo {
+    field public static final java.lang.String CONTENT_DIRECTORY = "logo";
+  }
+
+  public static final class TvContractCompat.PreviewPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WEIGHT = "weight";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs.Genres {
+    method public static java.lang.String[] decode(java.lang.String);
+    method public static java.lang.String encode(java.lang.String...);
+    method public static boolean isCanonical(java.lang.String);
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String ARTS = "ARTS";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String ENTERTAINMENT = "ENTERTAINMENT";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String LIFE_STYLE = "LIFE_STYLE";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String MUSIC = "MUSIC";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String PREMIER = "PREMIER";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TECH_SCIENCE = "TECH_SCIENCE";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
+  }
+
+  public static final class TvContractCompat.RecordedPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.WatchNextPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0
+    field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2
+    field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3
+  }
+
+  public final class WatchNextProgram {
+    method public boolean equals(java.lang.Object);
+    method public static android.support.media.tv.WatchNextProgram fromCursor(android.database.Cursor);
+    method public long getLastEngagementTimeUtcMillis();
+    method public int getWatchNextType();
+    method public android.content.ContentValues toContentValues();
+    method public java.lang.String toString();
+  }
+
+  public static final class WatchNextProgram.Builder {
+    ctor public WatchNextProgram.Builder();
+    ctor public WatchNextProgram.Builder(android.support.media.tv.WatchNextProgram);
+    method public android.support.media.tv.WatchNextProgram build();
+    method public android.support.media.tv.WatchNextProgram.Builder setLastEngagementTimeUtcMillis(long);
+    method public android.support.media.tv.WatchNextProgram.Builder setWatchNextType(int);
+  }
+
+}
+
+package android.support.percent {
+
+  public deprecated class PercentFrameLayout extends android.widget.FrameLayout {
+    ctor public PercentFrameLayout(android.content.Context);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.percent.PercentFrameLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.percent.PercentFrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+  }
+
+  public static deprecated class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout.LayoutParams(int, int);
+    ctor public PercentFrameLayout.LayoutParams(int, int, int);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentLayoutHelper {
+    ctor public PercentLayoutHelper(android.view.ViewGroup);
+    method public void adjustChildren(int, int);
+    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+    method public boolean handleMeasuredStateTooSmall();
+    method public void restoreOriginalParams();
+  }
+
+  public static deprecated class PercentLayoutHelper.PercentLayoutInfo {
+    ctor public PercentLayoutHelper.PercentLayoutInfo();
+    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+    method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void fillMarginLayoutParams(android.view.View, android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public float aspectRatio;
+    field public float bottomMarginPercent;
+    field public float endMarginPercent;
+    field public float heightPercent;
+    field public float leftMarginPercent;
+    field public float rightMarginPercent;
+    field public float startMarginPercent;
+    field public float topMarginPercent;
+    field public float widthPercent;
+  }
+
+  public static abstract deprecated interface PercentLayoutHelper.PercentLayoutParams {
+    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentRelativeLayout extends android.widget.RelativeLayout {
+    ctor public PercentRelativeLayout(android.content.Context);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.percent.PercentRelativeLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.percent.PercentRelativeLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+  }
+
+  public static deprecated class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout.LayoutParams(int, int);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+}
+
+package android.support.text.emoji {
+
+  public class EmojiCompat {
+    method public static android.support.text.emoji.EmojiCompat get();
+    method public java.lang.String getAssetSignature();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, int, int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence, int);
+    method public static android.support.text.emoji.EmojiCompat init(android.support.text.emoji.EmojiCompat.Config);
+    method public java.lang.CharSequence process(java.lang.CharSequence);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int, int);
+    method public void registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final java.lang.String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public static abstract class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataRepoLoader);
+    method protected final android.support.text.emoji.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public android.support.text.emoji.EmojiCompat.Config registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config setReplaceAll(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+  }
+
+  public static abstract class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(java.lang.Throwable);
+    method public void onInitialized();
+  }
+
+  public static abstract interface EmojiCompat.MetadataRepoLoader {
+    method public abstract void load(android.support.text.emoji.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public static abstract class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(java.lang.Throwable);
+    method public abstract void onLoaded(android.support.text.emoji.MetadataRepo);
+  }
+
+  public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, java.lang.CharSequence, int, int, android.graphics.Paint.FontMetricsInt);
+  }
+
+  public class FontRequestEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, android.support.v4.provider.FontRequest);
+    method public android.support.text.emoji.FontRequestEmojiCompatConfig setHandler(android.os.Handler);
+    method public android.support.text.emoji.FontRequestEmojiCompatConfig setRetryPolicy(android.support.text.emoji.FontRequestEmojiCompatConfig.RetryPolicy);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends android.support.text.emoji.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public static abstract class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  public final class MetadataRepo {
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.content.res.AssetManager, java.lang.String) throws java.io.IOException;
+  }
+
+}
+
+package android.support.text.emoji.bundled {
+
+  public class BundledEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
+package android.support.text.emoji.widget {
+
+  public class EmojiAppCompatButton extends android.support.v7.widget.AppCompatButton {
+    ctor public EmojiAppCompatButton(android.content.Context);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiAppCompatEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public EmojiAppCompatEditText(android.content.Context);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
+  }
+
+  public class EmojiAppCompatTextView extends android.support.v7.widget.AppCompatTextView {
+    ctor public EmojiAppCompatTextView(android.content.Context);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
+  }
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
+    method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter[] getFilters(android.text.InputFilter[]);
+    method public void setAllCaps(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod wrapTransformationMethod(android.text.method.TransformationMethod);
+  }
+
+}
+
+package android.support.transition {
+
+  public class ArcMotion extends android.support.transition.PathMotion {
+    ctor public ArcMotion();
+    ctor public ArcMotion(android.content.Context, android.util.AttributeSet);
+    method public float getMaximumAngle();
+    method public float getMinimumHorizontalAngle();
+    method public float getMinimumVerticalAngle();
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public void setMaximumAngle(float);
+    method public void setMinimumHorizontalAngle(float);
+    method public void setMinimumVerticalAngle(float);
+  }
+
+  public class AutoTransition extends android.support.transition.TransitionSet {
+    ctor public AutoTransition();
+    ctor public AutoTransition(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class ChangeBounds extends android.support.transition.Transition {
+    ctor public ChangeBounds();
+    ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getResizeClip();
+    method public void setResizeClip(boolean);
+  }
+
+  public class ChangeClipBounds extends android.support.transition.Transition {
+    ctor public ChangeClipBounds();
+    ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeImageTransform extends android.support.transition.Transition {
+    ctor public ChangeImageTransform();
+    ctor public ChangeImageTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeScroll extends android.support.transition.Transition {
+    ctor public ChangeScroll();
+    ctor public ChangeScroll(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeTransform extends android.support.transition.Transition {
+    ctor public ChangeTransform();
+    ctor public ChangeTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getReparent();
+    method public boolean getReparentWithOverlay();
+    method public void setReparent(boolean);
+    method public void setReparentWithOverlay(boolean);
+  }
+
+  public class CircularPropagation extends android.support.transition.VisibilityPropagation {
+    ctor public CircularPropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+  }
+
+  public class Explode extends android.support.transition.Visibility {
+    ctor public Explode();
+    ctor public Explode(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class Fade extends android.support.transition.Visibility {
+    ctor public Fade(int);
+    ctor public Fade();
+    ctor public Fade(android.content.Context, android.util.AttributeSet);
+    field public static final int IN = 1; // 0x1
+    field public static final int OUT = 2; // 0x2
+  }
+
+  public abstract class PathMotion {
+    ctor public PathMotion();
+    ctor public PathMotion(android.content.Context, android.util.AttributeSet);
+    method public abstract android.graphics.Path getPath(float, float, float, float);
+  }
+
+  public class PatternPathMotion extends android.support.transition.PathMotion {
+    ctor public PatternPathMotion();
+    ctor public PatternPathMotion(android.content.Context, android.util.AttributeSet);
+    ctor public PatternPathMotion(android.graphics.Path);
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public android.graphics.Path getPatternPath();
+    method public void setPatternPath(android.graphics.Path);
+  }
+
+  public class Scene {
+    ctor public Scene(android.view.ViewGroup);
+    ctor public Scene(android.view.ViewGroup, android.view.View);
+    method public void enter();
+    method public void exit();
+    method public static android.support.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
+    method public android.view.ViewGroup getSceneRoot();
+    method public void setEnterAction(java.lang.Runnable);
+    method public void setExitAction(java.lang.Runnable);
+  }
+
+  public class SidePropagation extends android.support.transition.VisibilityPropagation {
+    ctor public SidePropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+    method public void setSide(int);
+  }
+
+  public class Slide extends android.support.transition.Visibility {
+    ctor public Slide();
+    ctor public Slide(int);
+    ctor public Slide(android.content.Context, android.util.AttributeSet);
+    method public int getSlideEdge();
+    method public void setSlideEdge(int);
+  }
+
+  public abstract class Transition {
+    ctor public Transition();
+    ctor public Transition(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition addTarget(android.view.View);
+    method public android.support.transition.Transition addTarget(int);
+    method public android.support.transition.Transition addTarget(java.lang.String);
+    method public android.support.transition.Transition addTarget(java.lang.Class);
+    method public abstract void captureEndValues(android.support.transition.TransitionValues);
+    method public abstract void captureStartValues(android.support.transition.TransitionValues);
+    method public android.support.transition.Transition clone();
+    method public android.animation.Animator createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition excludeChildren(android.view.View, boolean);
+    method public android.support.transition.Transition excludeChildren(int, boolean);
+    method public android.support.transition.Transition excludeChildren(java.lang.Class, boolean);
+    method public android.support.transition.Transition excludeTarget(android.view.View, boolean);
+    method public android.support.transition.Transition excludeTarget(int, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
+    method public long getDuration();
+    method public android.graphics.Rect getEpicenter();
+    method public android.support.transition.Transition.EpicenterCallback getEpicenterCallback();
+    method public android.animation.TimeInterpolator getInterpolator();
+    method public java.lang.String getName();
+    method public android.support.transition.PathMotion getPathMotion();
+    method public android.support.transition.TransitionPropagation getPropagation();
+    method public long getStartDelay();
+    method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.String> getTargetNames();
+    method public java.util.List<java.lang.Class> getTargetTypes();
+    method public java.util.List<android.view.View> getTargets();
+    method public java.lang.String[] getTransitionProperties();
+    method public android.support.transition.TransitionValues getTransitionValues(android.view.View, boolean);
+    method public boolean isTransitionRequired(android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition removeTarget(android.view.View);
+    method public android.support.transition.Transition removeTarget(int);
+    method public android.support.transition.Transition removeTarget(java.lang.String);
+    method public android.support.transition.Transition removeTarget(java.lang.Class);
+    method public android.support.transition.Transition setDuration(long);
+    method public void setEpicenterCallback(android.support.transition.Transition.EpicenterCallback);
+    method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
+    method public void setMatchOrder(int...);
+    method public void setPathMotion(android.support.transition.PathMotion);
+    method public void setPropagation(android.support.transition.TransitionPropagation);
+    method public android.support.transition.Transition setStartDelay(long);
+    field public static final int MATCH_ID = 3; // 0x3
+    field public static final int MATCH_INSTANCE = 1; // 0x1
+    field public static final int MATCH_ITEM_ID = 4; // 0x4
+    field public static final int MATCH_NAME = 2; // 0x2
+  }
+
+  public static abstract class Transition.EpicenterCallback {
+    ctor public Transition.EpicenterCallback();
+    method public abstract android.graphics.Rect onGetEpicenter(android.support.transition.Transition);
+  }
+
+  public static abstract interface Transition.TransitionListener {
+    method public abstract void onTransitionCancel(android.support.transition.Transition);
+    method public abstract void onTransitionEnd(android.support.transition.Transition);
+    method public abstract void onTransitionPause(android.support.transition.Transition);
+    method public abstract void onTransitionResume(android.support.transition.Transition);
+    method public abstract void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionInflater {
+    method public static android.support.transition.TransitionInflater from(android.content.Context);
+    method public android.support.transition.Transition inflateTransition(int);
+    method public android.support.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+  }
+
+  public class TransitionListenerAdapter implements android.support.transition.Transition.TransitionListener {
+    ctor public TransitionListenerAdapter();
+    method public void onTransitionCancel(android.support.transition.Transition);
+    method public void onTransitionEnd(android.support.transition.Transition);
+    method public void onTransitionPause(android.support.transition.Transition);
+    method public void onTransitionResume(android.support.transition.Transition);
+    method public void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionManager {
+    ctor public TransitionManager();
+    method public static void beginDelayedTransition(android.view.ViewGroup);
+    method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
+    method public static void endTransitions(android.view.ViewGroup);
+    method public static void go(android.support.transition.Scene);
+    method public static void go(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Scene, android.support.transition.Transition);
+    method public void transitionTo(android.support.transition.Scene);
+  }
+
+  public abstract class TransitionPropagation {
+    ctor public TransitionPropagation();
+    method public abstract void captureValues(android.support.transition.TransitionValues);
+    method public abstract java.lang.String[] getPropagationProperties();
+    method public abstract long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+  }
+
+  public class TransitionSet extends android.support.transition.Transition {
+    ctor public TransitionSet();
+    ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.TransitionSet addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.TransitionSet addTarget(android.view.View);
+    method public android.support.transition.TransitionSet addTarget(int);
+    method public android.support.transition.TransitionSet addTarget(java.lang.String);
+    method public android.support.transition.TransitionSet addTarget(java.lang.Class);
+    method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getOrdering();
+    method public android.support.transition.Transition getTransitionAt(int);
+    method public int getTransitionCount();
+    method public android.support.transition.TransitionSet removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.TransitionSet removeTarget(int);
+    method public android.support.transition.TransitionSet removeTarget(android.view.View);
+    method public android.support.transition.TransitionSet removeTarget(java.lang.Class);
+    method public android.support.transition.TransitionSet removeTarget(java.lang.String);
+    method public android.support.transition.TransitionSet removeTransition(android.support.transition.Transition);
+    method public android.support.transition.TransitionSet setDuration(long);
+    method public android.support.transition.TransitionSet setInterpolator(android.animation.TimeInterpolator);
+    method public android.support.transition.TransitionSet setOrdering(int);
+    method public android.support.transition.TransitionSet setStartDelay(long);
+    field public static final int ORDERING_SEQUENTIAL = 1; // 0x1
+    field public static final int ORDERING_TOGETHER = 0; // 0x0
+  }
+
+  public class TransitionValues {
+    ctor public TransitionValues();
+    field public final java.util.Map<java.lang.String, java.lang.Object> values;
+    field public android.view.View view;
+  }
+
+  public abstract class Visibility extends android.support.transition.Transition {
+    ctor public Visibility();
+    ctor public Visibility(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getMode();
+    method public boolean isVisible(android.support.transition.TransitionValues);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setMode(int);
+    field public static final int MODE_IN = 1; // 0x1
+    field public static final int MODE_OUT = 2; // 0x2
+  }
+
+  public abstract class VisibilityPropagation extends android.support.transition.TransitionPropagation {
+    ctor public VisibilityPropagation();
+    method public void captureValues(android.support.transition.TransitionValues);
+    method public java.lang.String[] getPropagationProperties();
+    method public int getViewVisibility(android.support.transition.TransitionValues);
+    method public int getViewX(android.support.transition.TransitionValues);
+    method public int getViewY(android.support.transition.TransitionValues);
+  }
+
+}
+
+package android.support.v13.app {
+
+  public class ActivityCompat extends android.support.v4.app.ActivityCompat {
+    ctor protected ActivityCompat();
+    method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+  }
+
+  public class FragmentCompat {
+    ctor public FragmentCompat();
+    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
+    method public static deprecated void setMenuVisibility(android.app.Fragment, boolean);
+    method public static void setUserVisibleHint(android.app.Fragment, boolean);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
+  }
+
+  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public deprecated void setup();
+    method public void setup(android.content.Context, android.app.FragmentManager);
+    method public void setup(android.content.Context, android.app.FragmentManager, int);
+  }
+
+}
+
+package android.support.v13.view {
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, android.support.v13.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static abstract interface DragStartHelper.OnDragStartListener {
+    method public abstract boolean onDragStart(android.view.View, android.support.v13.view.DragStartHelper);
+  }
+
+  public deprecated class ViewCompat extends android.support.v4.view.ViewCompat {
+  }
+
+}
+
+package android.support.v13.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor public EditorInfoCompat();
+    method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
+  }
+
+  public static abstract interface InputConnectionCompat.OnCommitContentListener {
+    method public abstract boolean onCommitContent(android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public java.lang.Object unwrap();
+    method public static android.support.v13.view.inputmethod.InputContentInfoCompat wrap(java.lang.Object);
+  }
+
+}
+
+package android.support.v14.preference {
+
+  public class EditTextPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public EditTextPreferenceDialogFragment();
+    method public static android.support.v14.preference.EditTextPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public ListPreferenceDialogFragment();
+    method public static android.support.v14.preference.ListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public MultiSelectListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence[] getEntryValues();
+    method protected boolean[] getSelectedItems();
+    method public java.util.Set<java.lang.String> getValues();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValues(java.util.Set<java.lang.String>);
+  }
+
+  public class MultiSelectListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public MultiSelectListPreferenceDialogFragment();
+    method public static android.support.v14.preference.MultiSelectListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragment extends android.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragment();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public class SwitchPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreference(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+}
+
+package android.support.v17.leanback.app {
+
+  public final class BackgroundManager {
+    method public void attach(android.view.Window);
+    method public void attachToView(android.view.View);
+    method public void clearDrawable();
+    method public final int getColor();
+    method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
+    method public deprecated android.graphics.drawable.Drawable getDimLayer();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
+    method public boolean isAttached();
+    method public boolean isAutoReleaseOnStop();
+    method public void release();
+    method public void setAutoReleaseOnStop(boolean);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColor(int);
+    method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setThemeDrawableResourceId(int);
+  }
+
+  public class BaseFragment extends android.support.v17.leanback.app.BrandedFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+   abstract class BaseRowFragment extends android.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+   abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public class BaseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+  public class BrandedFragment extends android.app.Fragment {
+    ctor public BrandedFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrandedSupportFragment extends android.support.v4.app.Fragment {
+    ctor public BrandedSupportFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrowseFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public BrowseFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
+    method public int getHeadersState();
+    method public android.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersFragment onCreateHeadersFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseFragment.BrowseTransitionListener {
+    ctor public BrowseFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseFragment.FragmentFactory<T extends android.app.Fragment> {
+    ctor public BrowseFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseFragment.FragmentFactory {
+    ctor public BrowseFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseFragment.MainFragmentAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseFragment.MainFragmentAdapterRegistry();
+    method public android.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseFragment.FragmentFactory);
+  }
+
+  public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class BrowseSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public BrowseSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public int getHeadersState();
+    method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
+    method public android.support.v4.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersSupportFragment onCreateHeadersSupportFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseSupportFragment.BrowseTransitionListener {
+    ctor public BrowseSupportFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseSupportFragment.FragmentFactory<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseSupportFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseSupportFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory {
+    ctor public BrowseSupportFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsSupportFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseSupportFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseSupportFragment.MainFragmentAdapterRegistry();
+    method public android.support.v4.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class DetailsFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public DetailsFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsFragmentBackgroundController {
+    ctor public DetailsFragmentBackgroundController(android.support.v17.leanback.app.DetailsFragment);
+    method public boolean canNavigateToVideoFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.app.Fragment findOrCreateVideoFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.app.Fragment onCreateVideoFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class DetailsSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public DetailsSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsSupportFragmentBackgroundController {
+    ctor public DetailsSupportFragmentBackgroundController(android.support.v17.leanback.app.DetailsSupportFragment);
+    method public boolean canNavigateToVideoSupportFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class ErrorFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public ErrorFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class ErrorSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public ErrorSupportFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class GuidedStepFragment extends android.app.Fragment {
+    ctor public GuidedStepFragment();
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class GuidedStepSupportFragment extends android.support.v4.app.Fragment {
+    ctor public GuidedStepSupportFragment();
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepSupportFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment {
+    ctor public HeadersFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment {
+    ctor public HeadersSupportFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+    method protected void pausePlayback();
+    method protected void skipToNext();
+    method protected void skipToPrevious();
+    method protected void startPlayback(int);
+  }
+
+  public abstract class OnboardingFragment extends android.app.Fragment {
+    ctor public OnboardingFragment();
+    method public final int getArrowBackgroundColor();
+    method public final int getArrowColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final java.lang.CharSequence getStartButtonText();
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setArrowColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setStartButtonText(java.lang.CharSequence);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract class OnboardingSupportFragment extends android.support.v4.app.Fragment {
+    ctor public OnboardingSupportFragment();
+    method public final int getArrowBackgroundColor();
+    method public final int getArrowColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final java.lang.CharSequence getStartButtonText();
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setArrowColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setStartButtonText(java.lang.CharSequence);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
+    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
+    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
+    method protected deprecated void pausePlayback();
+    method public final void play(int);
+    method public final void previous();
+    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method protected deprecated void skipToNext();
+    method protected deprecated void skipToPrevious();
+    method protected deprecated void startPlayback(int);
+  }
+
+  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
+    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  }
+
+  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public class PlaybackFragment extends android.app.Fragment {
+    ctor public PlaybackFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
+    method public void fadeOut();
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
+    ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlayFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlayFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+    ctor public PlaybackOverlaySupportFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
+    ctor public PlaybackSupportFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
+    method public void fadeOut();
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public final class ProgressBarManager {
+    ctor public ProgressBarManager();
+    method public void disableProgressBar();
+    method public void enableProgressBar();
+    method public long getInitialDelay();
+    method public void hide();
+    method public void setInitialDelay(long);
+    method public void setProgressBarView(android.view.View);
+    method public void setRootView(android.view.ViewGroup);
+    method public void show();
+  }
+
+  public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment implements android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter {
+    ctor public RowsFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public static class RowsFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter {
+    ctor public RowsFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment implements android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsSupportFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsSupportFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter {
+    ctor public RowsSupportFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public static class RowsSupportFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter {
+    ctor public RowsSupportFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public class SearchFragment extends android.app.Fragment {
+    ctor public SearchFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SearchSupportFragment extends android.support.v4.app.Fragment {
+    ctor public SearchSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchSupportFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class VerticalGridFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public VerticalGridFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public VerticalGridSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
+    ctor public VideoFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
+    ctor public VideoSupportFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.database {
+
+  public abstract class CursorMapper {
+    ctor public CursorMapper();
+    method protected abstract java.lang.Object bind(android.database.Cursor);
+    method protected abstract void bindColumns(android.database.Cursor);
+    method public java.lang.Object convert(android.database.Cursor);
+  }
+
+}
+
+package android.support.v17.leanback.graphics {
+
+  public class BoundsRule {
+    ctor public BoundsRule();
+    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
+    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule top;
+  }
+
+  public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
+    method public int getAbsoluteValue();
+    method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
+    method public void setAbsoluteValue(int);
+    method public void setFraction(float);
+  }
+
+  public final class ColorFilterCache {
+    method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
+    method public android.graphics.ColorFilter getFilterForLevel(float);
+  }
+
+  public final class ColorFilterDimmer {
+    method public void applyFilterToView(android.view.View);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context);
+    method public android.graphics.ColorFilter getColorFilter();
+    method public android.graphics.Paint getPaint();
+    method public void setActiveLevel(float);
+  }
+
+  public final class ColorOverlayDimmer {
+    method public int applyToColor(int);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context);
+    method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean);
+    method public int getAlpha();
+    method public float getAlphaFloat();
+    method public android.graphics.Paint getPaint();
+    method public boolean needsDraw();
+    method public void setActiveLevel(float);
+  }
+
+  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public CompositeDrawable();
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
+    method public int getChildCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void removeChild(int);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+  }
+
+  public static final class CompositeDrawable.ChildDrawable {
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
+    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void recomputeBounds();
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
+  }
+
+  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
+    ctor public FitWidthBitmapDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.Bitmap getBitmap();
+    method public int getOpacity();
+    method public android.graphics.Rect getSource();
+    method public int getVerticalOffset();
+    method public void setAlpha(int);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setSource(android.graphics.Rect);
+    method public void setVerticalOffset(int);
+    field public static final android.util.Property<android.support.v17.leanback.graphics.FitWidthBitmapDrawable, java.lang.Integer> PROPERTY_VERTICAL_OFFSET;
+  }
+
+}
+
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public class MediaPlayerAdapter extends android.support.v17.leanback.media.PlayerAdapter {
+    ctor public MediaPlayerAdapter(android.content.Context);
+    method protected boolean onError(int, int);
+    method protected boolean onInfo(int, int);
+    method protected void onSeekComplete();
+    method public void pause();
+    method public void play();
+    method public void release();
+    method public void reset();
+    method public boolean setDataSource(android.net.Uri);
+  }
+
+  public class PlaybackBannerControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackBaseControlGlue {
+    ctor public PlaybackBannerControlGlue(android.content.Context, int[], T);
+    ctor public PlaybackBannerControlGlue(android.content.Context, int[], int[], T);
+    method public int[] getFastForwardSpeeds();
+    method public int[] getRewindSpeeds();
+    method public long getSupportedActions();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackBaseControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackBaseControlGlue(android.content.Context, T);
+    method public android.graphics.drawable.Drawable getArt();
+    method public final long getBufferedPosition();
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public long getCurrentPosition();
+    method public final long getDuration();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public final T getPlayerAdapter();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public final boolean isPlaying();
+    method public final boolean isPrepared();
+    method protected static void notifyItemChanged(android.support.v17.leanback.widget.ArrayObjectAdapter, java.lang.Object);
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method protected abstract android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onPlayCompleted();
+    method protected void onPlayStateChanged();
+    method protected void onPreparedStateChanged();
+    method public final void seekTo(long);
+    method public void setArt(android.graphics.drawable.Drawable);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public deprecated android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public deprecated void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public void addPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method protected java.util.List<android.support.v17.leanback.media.PlaybackGlue.PlayerCallback> getPlayerCallbacks();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public deprecated boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public void removePlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public deprecated void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public deprecated void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.media.PlaybackGlueHost.PlayerCallback getPlayerCallback();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public void notifyPlaybackRowChanged();
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void showControlsOverlay(boolean);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostDestroy();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public static class PlaybackGlueHost.PlayerCallback {
+    ctor public PlaybackGlueHost.PlayerCallback();
+    method public void onBufferingStateChanged(boolean);
+    method public void onError(int, java.lang.CharSequence);
+    method public void onVideoSizeChanged(int, int);
+  }
+
+  public class PlaybackTransportControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackBaseControlGlue {
+    ctor public PlaybackTransportControlGlue(android.content.Context, T);
+    method public final android.support.v17.leanback.widget.PlaybackSeekDataProvider getSeekProvider();
+    method public final boolean isSeekEnabled();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method public final void setSeekEnabled(boolean);
+    method public final void setSeekProvider(android.support.v17.leanback.widget.PlaybackSeekDataProvider);
+  }
+
+  public abstract class PlayerAdapter {
+    ctor public PlayerAdapter();
+    method public long getBufferedPosition();
+    method public final android.support.v17.leanback.media.PlayerAdapter.Callback getCallback();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void onDetachedFromHost();
+    method public abstract void pause();
+    method public abstract void play();
+    method public void seekTo(long);
+    method public final void setCallback(android.support.v17.leanback.media.PlayerAdapter.Callback);
+    method public void setProgressUpdatingEnabled(boolean);
+  }
+
+  public static class PlayerAdapter.Callback {
+    ctor public PlayerAdapter.Callback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onBufferingStateChanged(android.support.v17.leanback.media.PlayerAdapter, boolean);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onDurationChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onError(android.support.v17.leanback.media.PlayerAdapter, int, java.lang.String);
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onVideoSizeChanged(android.support.v17.leanback.media.PlayerAdapter, int, int);
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.system {
+
+  public class Settings {
+    method public boolean getBoolean(java.lang.String);
+    method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context);
+    method public void setBoolean(java.lang.String, boolean);
+    field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
+  }
+
+}
+
+package android.support.v17.leanback.widget {
+
+  public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public AbstractDetailsDescriptionPresenter();
+    method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getBody();
+    method public android.widget.TextView getSubtitle();
+    method public android.widget.TextView getTitle();
+  }
+
+  public abstract class AbstractMediaItemPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaItemPresenter();
+    ctor public AbstractMediaItemPresenter(int);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter getActionPresenter();
+    method protected int getMediaPlayState(java.lang.Object);
+    method public int getThemeId();
+    method public boolean hasMediaRowSeparator();
+    method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
+    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setBackgroundColor(int);
+    method public void setHasMediaRowSeparator(boolean);
+    method public void setThemeId(int);
+    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
+    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
+  }
+
+  public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaItemPresenter.ViewHolder(android.view.View);
+    method public android.view.ViewGroup getMediaItemActionsContainer();
+    method public android.view.View getMediaItemDetailsView();
+    method public android.widget.TextView getMediaItemDurationView();
+    method public android.widget.TextView getMediaItemNameView();
+    method public android.widget.TextView getMediaItemNumberView();
+    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
+    method public android.view.View getMediaItemPausedView();
+    method public android.view.View getMediaItemPlayingView();
+    method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
+    method public android.view.View getMediaItemRowSeparator();
+    method public android.view.View getSelectorView();
+    method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
+    method public void notifyDetailsChanged();
+    method public void notifyPlayStateChanged();
+    method public void onBindRowActions();
+    method public void setSelectedMediaItemNumberView(int);
+  }
+
+  public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaListHeaderPresenter(android.content.Context, int);
+    ctor public AbstractMediaListHeaderPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected abstract void onBindMediaListHeaderViewHolder(android.support.v17.leanback.widget.AbstractMediaListHeaderPresenter.ViewHolder, java.lang.Object);
+    method public void setBackgroundColor(int);
+  }
+
+  public static class AbstractMediaListHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaListHeaderPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getHeaderView();
+  }
+
+  public class Action {
+    ctor public Action(long);
+    ctor public Action(long, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable);
+    method public final void addKeyCode(int);
+    method public final android.graphics.drawable.Drawable getIcon();
+    method public final long getId();
+    method public final java.lang.CharSequence getLabel1();
+    method public final java.lang.CharSequence getLabel2();
+    method public final void removeKeyCode(int);
+    method public final boolean respondsToKeyCode(int);
+    method public final void setIcon(android.graphics.drawable.Drawable);
+    method public final void setId(long);
+    method public final void setLabel1(java.lang.CharSequence);
+    method public final void setLabel2(java.lang.CharSequence);
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+  }
+
+  public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ArrayObjectAdapter();
+    method public void add(java.lang.Object);
+    method public void add(int, java.lang.Object);
+    method public void addAll(int, java.util.Collection);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public boolean remove(java.lang.Object);
+    method public int removeItems(int, int);
+    method public void replace(int, java.lang.Object);
+    method public int size();
+    method public <E> java.util.List<E> unmodifiableList();
+  }
+
+  public class BaseCardView extends android.widget.FrameLayout {
+    ctor public BaseCardView(android.content.Context);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.v17.leanback.widget.BaseCardView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v17.leanback.widget.BaseCardView.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v17.leanback.widget.BaseCardView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getCardType();
+    method public deprecated int getExtraVisibility();
+    method public int getInfoVisibility();
+    method public boolean isSelectedAnimationDelayed();
+    method public void setCardType(int);
+    method public deprecated void setExtraVisibility(int);
+    method public void setInfoVisibility(int);
+    method public void setSelectedAnimationDelayed(boolean);
+    field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
+    field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0
+    field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1
+    field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3
+    field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0
+  }
+
+  public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView.LayoutParams(int, int);
+    ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams);
+    field public static final int VIEW_TYPE_EXTRA = 2; // 0x2
+    field public static final int VIEW_TYPE_INFO = 1; // 0x1
+    field public static final int VIEW_TYPE_MAIN = 0; // 0x0
+    field public int viewType;
+  }
+
+  public abstract class BaseGridView extends android.support.v7.widget.RecyclerView {
+    method public void addOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void animateIn();
+    method public void animateOut();
+    method public int getChildDrawingOrder(int, int);
+    method public deprecated int getHorizontalMargin();
+    method public int getHorizontalSpacing();
+    method public int getInitialPrefetchItemCount();
+    method public int getItemAlignmentOffset();
+    method public float getItemAlignmentOffsetPercent();
+    method public int getItemAlignmentViewId();
+    method public android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener getOnUnhandledKeyListener();
+    method public final int getSaveChildrenLimitNumber();
+    method public final int getSaveChildrenPolicy();
+    method public int getSelectedPosition();
+    method public deprecated int getVerticalMargin();
+    method public int getVerticalSpacing();
+    method public void getViewSelectedOffsets(android.view.View, int[]);
+    method public int getWindowAlignment();
+    method public int getWindowAlignmentOffset();
+    method public float getWindowAlignmentOffsetPercent();
+    method public boolean hasPreviousViewInSameRow(int);
+    method public boolean isChildLayoutAnimated();
+    method public boolean isFocusDrawingOrderEnabled();
+    method public final boolean isFocusSearchDisabled();
+    method public boolean isItemAlignmentOffsetWithPadding();
+    method public boolean isScrollEnabled();
+    method public boolean isWindowAlignmentPreferKeyLineOverHighEdge();
+    method public boolean isWindowAlignmentPreferKeyLineOverLowEdge();
+    method public boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+    method public void removeOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setAnimateChildLayout(boolean);
+    method public void setChildrenVisibility(int);
+    method public void setFocusDrawingOrderEnabled(boolean);
+    method public final void setFocusSearchDisabled(boolean);
+    method public void setGravity(int);
+    method public void setHasOverlappingRendering(boolean);
+    method public deprecated void setHorizontalMargin(int);
+    method public void setHorizontalSpacing(int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setItemAlignmentOffset(int);
+    method public void setItemAlignmentOffsetPercent(float);
+    method public void setItemAlignmentOffsetWithPadding(boolean);
+    method public void setItemAlignmentViewId(int);
+    method public deprecated void setItemMargin(int);
+    method public void setItemSpacing(int);
+    method public void setLayoutEnabled(boolean);
+    method public void setOnChildLaidOutListener(android.support.v17.leanback.widget.OnChildLaidOutListener);
+    method public void setOnChildSelectedListener(android.support.v17.leanback.widget.OnChildSelectedListener);
+    method public void setOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setOnKeyInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnKeyInterceptListener);
+    method public void setOnMotionInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnMotionInterceptListener);
+    method public void setOnTouchInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnTouchInterceptListener);
+    method public void setOnUnhandledKeyListener(android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener);
+    method public void setPruneChild(boolean);
+    method public final void setSaveChildrenLimitNumber(int);
+    method public final void setSaveChildrenPolicy(int);
+    method public void setScrollEnabled(boolean);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, int);
+    method public void setSelectedPosition(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public void setSelectedPositionSmooth(int);
+    method public void setSelectedPositionSmooth(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public deprecated void setVerticalMargin(int);
+    method public void setVerticalSpacing(int);
+    method public void setWindowAlignment(int);
+    method public void setWindowAlignmentOffset(int);
+    method public void setWindowAlignmentOffsetPercent(float);
+    method public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean);
+    method public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+    field public static final int SAVE_ALL_CHILD = 3; // 0x3
+    field public static final int SAVE_LIMITED_CHILD = 2; // 0x2
+    field public static final int SAVE_NO_CHILD = 0; // 0x0
+    field public static final int SAVE_ON_SCREEN_CHILD = 1; // 0x1
+    field public static final int WINDOW_ALIGN_BOTH_EDGE = 3; // 0x3
+    field public static final int WINDOW_ALIGN_HIGH_EDGE = 2; // 0x2
+    field public static final int WINDOW_ALIGN_LOW_EDGE = 1; // 0x1
+    field public static final int WINDOW_ALIGN_NO_EDGE = 0; // 0x0
+    field public static final float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static abstract interface BaseGridView.OnKeyInterceptListener {
+    method public abstract boolean onInterceptKeyEvent(android.view.KeyEvent);
+  }
+
+  public static abstract interface BaseGridView.OnMotionInterceptListener {
+    method public abstract boolean onInterceptMotionEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnTouchInterceptListener {
+    method public abstract boolean onInterceptTouchEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnUnhandledKeyListener {
+    method public abstract boolean onUnhandledKey(android.view.KeyEvent);
+  }
+
+  public abstract interface BaseOnItemViewClickedListener<T> {
+    method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public abstract interface BaseOnItemViewSelectedListener<T> {
+    method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public class BrowseFrameLayout extends android.widget.FrameLayout {
+    ctor public BrowseFrameLayout(android.content.Context);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
+    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
+    method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnChildFocusListener {
+    method public abstract void onRequestChildFocus(android.view.View, android.view.View);
+    method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnFocusSearchListener {
+    method public abstract android.view.View onFocusSearch(android.view.View, int);
+  }
+
+  public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ClassPresenterSelector();
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenterSelector(java.lang.Class<?>, android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ControlButtonPresenterSelector();
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter();
+    method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter();
+  }
+
+  public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public CursorObjectAdapter();
+    method public void changeCursor(android.database.Cursor);
+    method public void close();
+    method public java.lang.Object get(int);
+    method public final android.database.Cursor getCursor();
+    method public final android.support.v17.leanback.database.CursorMapper getMapper();
+    method protected final void invalidateCache(int);
+    method protected final void invalidateCache(int, int);
+    method public boolean isClosed();
+    method protected void onCursorChanged();
+    method protected void onMapperChanged();
+    method public final void setMapper(android.support.v17.leanback.database.CursorMapper);
+    method public int size();
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+  }
+
+  public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DetailsOverviewLogoPresenter();
+    method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+  }
+
+  public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter getParentPresenter();
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder();
+    method public boolean isSizeFromDrawableIntrinsic();
+    method public void setSizeFromDrawableIntrinsic(boolean);
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter;
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
+  }
+
+  public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row {
+    ctor public DetailsOverviewRow(java.lang.Object);
+    method public final deprecated void addAction(android.support.v17.leanback.widget.Action);
+    method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public boolean isImageScaleUpAllowed();
+    method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action);
+    method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setImageScaleUpAllowed(boolean);
+    method public final void setItem(java.lang.Object);
+  }
+
+  public static class DetailsOverviewRow.Listener {
+    ctor public DetailsOverviewRow.Listener();
+    method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+  }
+
+  public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public boolean isStyleLarge();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setStyleLarge(boolean);
+  }
+
+  public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder;
+  }
+
+  public class DetailsParallax extends android.support.v17.leanback.widget.RecyclerViewParallax {
+    ctor public DetailsParallax();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowBottom();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowTop();
+  }
+
+  public class DividerPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DividerPresenter();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public class DividerRow extends android.support.v17.leanback.widget.Row {
+    ctor public DividerRow();
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract interface FacetProvider {
+    method public abstract java.lang.Object getFacet(java.lang.Class<?>);
+  }
+
+  public abstract interface FacetProviderAdapter {
+    method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+  }
+
+  public abstract interface FocusHighlight {
+    field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3
+    field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2
+    field public static final int ZOOM_FACTOR_NONE = 0; // 0x0
+    field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1
+    field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4
+  }
+
+  public class FocusHighlightHelper {
+    ctor public FocusHighlightHelper();
+    method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean);
+    method public static deprecated void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView);
+    method public static deprecated void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView, boolean);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, boolean);
+  }
+
+  public abstract interface FragmentAnimationProvider {
+    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public final int getActionsBackgroundColor();
+    method public final int getAlignmentMode();
+    method public final int getBackgroundColor();
+    method public final int getInitialState();
+    method protected int getLayoutResourceId();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public final boolean isParticipatingEntranceTransition();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+    method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    method public final void setActionsBackgroundColor(int);
+    method public final void setAlignmentMode(int);
+    method public final void setBackgroundColor(int);
+    method public final void setInitialState(int);
+    method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setParticipatingEntranceTransition(boolean);
+    method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1
+    field public static final int ALIGN_MODE_START = 0; // 0x0
+    field public static final int STATE_FULL = 1; // 0x1
+    field public static final int STATE_HALF = 0; // 0x0
+    field public static final int STATE_SMALL = 2; // 0x2
+    field protected int mInitialState;
+  }
+
+  public static abstract class FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.Listener();
+    method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener();
+    method public final android.view.ViewGroup getActionsRow();
+    method public final android.view.ViewGroup getDetailsDescriptionFrame();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder();
+    method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder();
+    method public final android.view.ViewGroup getOverviewView();
+    method public final int getState();
+    field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener;
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener();
+  }
+
+  public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewSharedElementHelper();
+    method public boolean getAutoStartSharedElementTransition();
+    method public void setAutoStartSharedElementTransition(boolean);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public void startPostponedEnterTransition();
+  }
+
+  public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidanceStylist();
+    method public android.widget.TextView getBreadcrumbView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance);
+    method public void onDestroyView();
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideLayoutId();
+  }
+
+  public static class GuidanceStylist.Guidance {
+    ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable);
+    method public java.lang.String getBreadcrumb();
+    method public java.lang.String getDescription();
+    method public android.graphics.drawable.Drawable getIconDrawable();
+    method public java.lang.String getTitle();
+  }
+
+  public class GuidedAction extends android.support.v17.leanback.widget.Action {
+    ctor protected GuidedAction();
+    method public int getCheckSetId();
+    method public java.lang.CharSequence getDescription();
+    method public int getDescriptionEditInputType();
+    method public int getDescriptionInputType();
+    method public java.lang.CharSequence getEditDescription();
+    method public int getEditInputType();
+    method public java.lang.CharSequence getEditTitle();
+    method public int getInputType();
+    method public android.content.Intent getIntent();
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getSubActions();
+    method public java.lang.CharSequence getTitle();
+    method public boolean hasEditableActivatorView();
+    method public boolean hasMultilineDescription();
+    method public boolean hasNext();
+    method public boolean hasSubActions();
+    method public boolean hasTextEditable();
+    method public boolean infoOnly();
+    method public final boolean isAutoSaveRestoreEnabled();
+    method public boolean isChecked();
+    method public boolean isDescriptionEditable();
+    method public boolean isEditTitleUsed();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public void onRestoreInstanceState(android.os.Bundle, java.lang.String);
+    method public void onSaveInstanceState(android.os.Bundle, java.lang.String);
+    method public void setChecked(boolean);
+    method public void setDescription(java.lang.CharSequence);
+    method public void setEditDescription(java.lang.CharSequence);
+    method public void setEditTitle(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setSubActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setTitle(java.lang.CharSequence);
+    field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
+    field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
+    field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
+    field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL
+    field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL
+    field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
+    field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
+    field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
+    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
+    field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
+    field public static final int NO_CHECK_SET = 0; // 0x0
+  }
+
+  public static class GuidedAction.Builder extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public deprecated GuidedAction.Builder();
+    ctor public GuidedAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedAction build();
+  }
+
+  public static abstract class GuidedAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedAction.BuilderBase> {
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction);
+    method public B autoSaveRestoreEnabled(boolean);
+    method public B checkSetId(int);
+    method public B checked(boolean);
+    method public B clickAction(long);
+    method public B description(java.lang.CharSequence);
+    method public B description(int);
+    method public B descriptionEditInputType(int);
+    method public B descriptionEditable(boolean);
+    method public B descriptionInputType(int);
+    method public B editDescription(java.lang.CharSequence);
+    method public B editDescription(int);
+    method public B editInputType(int);
+    method public B editTitle(java.lang.CharSequence);
+    method public B editTitle(int);
+    method public B editable(boolean);
+    method public B enabled(boolean);
+    method public B focusable(boolean);
+    method public android.content.Context getContext();
+    method public B hasEditableActivatorView(boolean);
+    method public B hasNext(boolean);
+    method public B icon(android.graphics.drawable.Drawable);
+    method public B icon(int);
+    method public deprecated B iconResourceId(int, android.content.Context);
+    method public B id(long);
+    method public B infoOnly(boolean);
+    method public B inputType(int);
+    method public B intent(android.content.Intent);
+    method public B multilineDescription(boolean);
+    method public B subActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public B title(java.lang.CharSequence);
+    method public B title(int);
+  }
+
+  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
+    ctor public GuidedActionEditText(android.content.Context);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidedActionsStylist();
+    method public void collapseAction(boolean);
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
+    method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
+    method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
+    method public final boolean isBackKeyToCollapseActivatorView();
+    method public final boolean isBackKeyToCollapseSubActions();
+    method public boolean isButtonActions();
+    method public boolean isExpandTransitionSupported();
+    method public boolean isExpanded();
+    method public boolean isInExpandTransition();
+    method public boolean isSubActionsExpanded();
+    method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void onBindActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDestroyView();
+    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideItemLayoutId();
+    method public int onProvideItemLayoutId(int);
+    method public int onProvideLayoutId();
+    method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void setAsButtonActions();
+    method public final void setBackKeyToCollapseActivatorView(boolean);
+    method public final void setBackKeyToCollapseSubActions(boolean);
+    method public deprecated void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
+    field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
+  }
+
+  public static class GuidedActionsStylist.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View);
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View, boolean);
+    method public android.support.v17.leanback.widget.GuidedAction getAction();
+    method public android.widget.ImageView getCheckmarkView();
+    method public android.widget.ImageView getChevronView();
+    method public android.view.View getContentView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.EditText getEditableDescriptionView();
+    method public android.widget.EditText getEditableTitleView();
+    method public android.view.View getEditingView();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public boolean isInEditing();
+    method public boolean isInEditingActivatorView();
+    method public boolean isInEditingDescription();
+    method public boolean isInEditingText();
+    method public boolean isInEditingTitle();
+    method public boolean isSubAction();
+  }
+
+  public class GuidedDatePickerAction extends android.support.v17.leanback.widget.GuidedAction {
+    ctor public GuidedDatePickerAction();
+    method public long getDate();
+    method public java.lang.String getDatePickerFormat();
+    method public long getMaxDate();
+    method public long getMinDate();
+    method public void setDate(long);
+  }
+
+  public static final class GuidedDatePickerAction.Builder extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase {
+    ctor public GuidedDatePickerAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedDatePickerAction build();
+  }
+
+  public static abstract class GuidedDatePickerAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase> extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public GuidedDatePickerAction.BuilderBase(android.content.Context);
+    method protected final void applyDatePickerValues(android.support.v17.leanback.widget.GuidedDatePickerAction);
+    method public B date(long);
+    method public B datePickerFormat(java.lang.String);
+    method public B maxDate(long);
+    method public B minDate(long);
+  }
+
+  public class HeaderItem {
+    ctor public HeaderItem(long, java.lang.String);
+    ctor public HeaderItem(java.lang.String);
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getDescription();
+    method public final long getId();
+    method public final java.lang.String getName();
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setDescription(java.lang.CharSequence);
+  }
+
+  public class HorizontalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public HorizontalGridView(android.content.Context);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int);
+    method public final boolean getFadingLeftEdge();
+    method public final int getFadingLeftEdgeLength();
+    method public final int getFadingLeftEdgeOffset();
+    method public final boolean getFadingRightEdge();
+    method public final int getFadingRightEdgeLength();
+    method public final int getFadingRightEdgeOffset();
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public final void setFadingLeftEdge(boolean);
+    method public final void setFadingLeftEdgeLength(int);
+    method public final void setFadingLeftEdgeOffset(int);
+    method public final void setFadingRightEdge(boolean);
+    method public final void setFadingRightEdgeLength(int);
+    method public final void setFadingRightEdgeOffset(int);
+    method public void setNumRows(int);
+    method public void setRowHeight(int);
+  }
+
+  public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher {
+    ctor public HorizontalHoverCardSwitcher();
+    method protected void insertView(android.view.View);
+    method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object);
+  }
+
+  public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
+    ctor public deprecated ImageCardView(android.content.Context, int);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
+    ctor public ImageCardView(android.content.Context);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
+    method public android.graphics.drawable.Drawable getBadgeImage();
+    method public java.lang.CharSequence getContentText();
+    method public android.graphics.drawable.Drawable getInfoAreaBackground();
+    method public android.graphics.drawable.Drawable getMainImage();
+    method public final android.widget.ImageView getMainImageView();
+    method public java.lang.CharSequence getTitleText();
+    method public void setBadgeImage(android.graphics.drawable.Drawable);
+    method public void setContentText(java.lang.CharSequence);
+    method public void setInfoAreaBackground(android.graphics.drawable.Drawable);
+    method public void setInfoAreaBackgroundColor(int);
+    method public void setMainImage(android.graphics.drawable.Drawable);
+    method public void setMainImage(android.graphics.drawable.Drawable, boolean);
+    method public void setMainImageAdjustViewBounds(boolean);
+    method public void setMainImageDimensions(int, int);
+    method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
+    method public void setTitleText(java.lang.CharSequence);
+    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
+    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
+    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
+    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
+    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
+  }
+
+  public abstract interface ImeKeyMonitor {
+    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public static abstract interface ImeKeyMonitor.ImeKeyListener {
+    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
+  }
+
+  public final class ItemAlignmentFacet {
+    ctor public ItemAlignmentFacet();
+    method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs();
+    method public boolean isMultiAlignment();
+    method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static class ItemAlignmentFacet.ItemAlignmentDef {
+    ctor public ItemAlignmentFacet.ItemAlignmentDef();
+    method public final int getItemAlignmentFocusViewId();
+    method public final int getItemAlignmentOffset();
+    method public final float getItemAlignmentOffsetPercent();
+    method public final int getItemAlignmentViewId();
+    method public boolean isAlignedToTextViewBaseLine();
+    method public final boolean isItemAlignmentOffsetWithPadding();
+    method public final void setAlignedToTextViewBaseline(boolean);
+    method public final void setItemAlignmentFocusViewId(int);
+    method public final void setItemAlignmentOffset(int);
+    method public final void setItemAlignmentOffsetPercent(float);
+    method public final void setItemAlignmentOffsetWithPadding(boolean);
+    method public final void setItemAlignmentViewId(int);
+  }
+
+  public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter {
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ItemBridgeAdapter();
+    method public void clear();
+    method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+    method public int getItemCount();
+    method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper();
+    method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper();
+    method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener);
+    method public void setPresenter(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>);
+    method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper);
+  }
+
+  public static class ItemBridgeAdapter.AdapterListener {
+    ctor public ItemBridgeAdapter.AdapterListener();
+    method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+  }
+
+  public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    method public final java.lang.Object getExtraObject();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.Presenter getPresenter();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder();
+    method public void setExtraObject(java.lang.Object);
+  }
+
+  public static abstract class ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapter.Wrapper();
+    method public abstract android.view.View createWrapper(android.view.View);
+    method public abstract void wrap(android.view.View, android.view.View);
+  }
+
+  public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper);
+    method public android.view.View createWrapper(android.view.View);
+    method public void wrap(android.view.View, android.view.View);
+  }
+
+  public class ListRow extends android.support.v17.leanback.widget.Row {
+    ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public java.lang.CharSequence getContentDescription();
+    method public void setContentDescription(java.lang.CharSequence);
+  }
+
+  public final class ListRowHoverCardView extends android.widget.LinearLayout {
+    ctor public ListRowHoverCardView(android.content.Context);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int);
+    method public final java.lang.CharSequence getDescription();
+    method public final java.lang.CharSequence getTitle();
+    method public final void setDescription(java.lang.CharSequence);
+    method public final void setTitle(java.lang.CharSequence);
+  }
+
+  public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public ListRowPresenter();
+    ctor public ListRowPresenter(int);
+    ctor public ListRowPresenter(int, boolean);
+    method protected void applySelectLevelToChild(android.support.v17.leanback.widget.ListRowPresenter.ViewHolder, android.view.View);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public int getExpandedRowHeight();
+    method public final int getFocusZoomFactor();
+    method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector();
+    method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter);
+    method public int getRowHeight();
+    method public final boolean getShadowEnabled();
+    method public final deprecated int getZoomFactor();
+    method public final boolean isFocusDimmerUsed();
+    method public final boolean isKeepChildForeground();
+    method public boolean isUsingDefaultListSelectEffect();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void setExpandedRowHeight(int);
+    method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumRows(int);
+    method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int);
+    method public void setRowHeight(int);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class ListRowPresenter.SelectItemViewHolderTask extends android.support.v17.leanback.widget.Presenter.ViewHolderTask {
+    ctor public ListRowPresenter.SelectItemViewHolderTask(int);
+    method public int getItemPosition();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolderTask getItemTask();
+    method public boolean isSmoothScroll();
+    method public void setItemPosition(int);
+    method public void setItemTask(android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSmoothScroll(boolean);
+  }
+
+  public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter);
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.HorizontalGridView getGridView();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getItemViewHolder(int);
+    method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter();
+    method public int getSelectedPosition();
+  }
+
+  public final class ListRowView extends android.widget.LinearLayout {
+    ctor public ListRowView(android.content.Context);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
+  }
+
+  public abstract interface MultiActionsProvider {
+    method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
+  }
+
+  public static class MultiActionsProvider.MultiAction {
+    ctor public MultiActionsProvider.MultiAction(long);
+    method public android.graphics.drawable.Drawable getCurrentDrawable();
+    method public android.graphics.drawable.Drawable[] getDrawables();
+    method public long getId();
+    method public int getIndex();
+    method public void incrementIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+  }
+
+  public abstract class ObjectAdapter {
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ObjectAdapter();
+    method public abstract java.lang.Object get(int);
+    method public long getId(int);
+    method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public final boolean hasStableIds();
+    method public boolean isImmediateNotifySupported();
+    method protected final void notifyChanged();
+    method public final void notifyItemRangeChanged(int, int);
+    method protected final void notifyItemRangeInserted(int, int);
+    method protected final void notifyItemRangeRemoved(int, int);
+    method protected void onHasStableIdsChanged();
+    method protected void onPresenterSelectorChanged();
+    method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    method public final void setHasStableIds(boolean);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public abstract int size();
+    method public final void unregisterAllObservers();
+    method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    field public static final int NO_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class ObjectAdapter.DataObserver {
+    ctor public ObjectAdapter.DataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public abstract interface OnActionClickedListener {
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+  }
+
+  public abstract interface OnChildLaidOutListener {
+    method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract deprecated interface OnChildSelectedListener {
+    method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract class OnChildViewHolderSelectedListener {
+    ctor public OnChildViewHolderSelectedListener();
+    method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+  }
+
+  public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
+  }
+
+  public abstract interface OnItemViewSelectedListener implements android.support.v17.leanback.widget.BaseOnItemViewSelectedListener {
+  }
+
+  public class PageRow extends android.support.v17.leanback.widget.Row {
+    ctor public PageRow(android.support.v17.leanback.widget.HeaderItem);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract class Parallax<PropertyT extends android.util.Property> {
+    ctor public Parallax();
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
+    method public abstract PropertyT createProperty(java.lang.String, int);
+    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
+    method public final java.util.List<PropertyT> getProperties();
+    method public void removeAllEffects();
+    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public void updateValues();
+  }
+
+  public static class Parallax.FloatProperty extends android.util.Property {
+    ctor public Parallax.FloatProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
+    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
+    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
+  }
+
+  public static class Parallax.IntProperty extends android.util.Property {
+    ctor public Parallax.IntProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
+    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
+    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
+  }
+
+  public static class Parallax.PropertyMarkerValue<PropertyT> {
+    ctor public Parallax.PropertyMarkerValue(PropertyT);
+    method public PropertyT getProperty();
+  }
+
+  public abstract class ParallaxEffect {
+    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
+    method public final void performMapping(android.support.v17.leanback.widget.Parallax);
+    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
+  }
+
+  public abstract class ParallaxTarget {
+    ctor public ParallaxTarget();
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
+  }
+
+  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
+  }
+
+  public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
+    ctor public PlaybackControlsRow(java.lang.Object);
+    ctor public PlaybackControlsRow();
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
+    method public long getBufferedPosition();
+    method public deprecated int getBufferedProgress();
+    method public deprecated long getBufferedProgressLong();
+    method public long getCurrentPosition();
+    method public deprecated int getCurrentTime();
+    method public deprecated long getCurrentTimeLong();
+    method public long getDuration();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
+    method public deprecated int getTotalTime();
+    method public deprecated long getTotalTimeLong();
+    method public void setBufferedPosition(long);
+    method public deprecated void setBufferedProgress(int);
+    method public deprecated void setBufferedProgressLong(long);
+    method public void setCurrentPosition(long);
+    method public deprecated void setCurrentTime(int);
+    method public deprecated void setCurrentTimeLong(long);
+    method public void setDuration(long);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setOnPlaybackProgressChangedListener(android.support.v17.leanback.widget.PlaybackControlsRow.OnPlaybackProgressCallback);
+    method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public deprecated void setTotalTime(int);
+    method public deprecated void setTotalTimeLong(long);
+  }
+
+  public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context);
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context);
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context);
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MoreActions(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MultiAction(int);
+    method public int getActionCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getIndex();
+    method public java.lang.String getLabel(int);
+    method public java.lang.String getSecondaryLabel(int);
+    method public void nextIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+    method public void setLabels(java.lang.String[]);
+    method public void setSecondaryLabels(java.lang.String[]);
+  }
+
+  public static class PlaybackControlsRow.OnPlaybackProgressCallback {
+    ctor public PlaybackControlsRow.OnPlaybackProgressCallback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onDurationChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+  }
+
+  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
+    field public static final int INDEX_PAUSE = 1; // 0x1
+    field public static final int INDEX_PLAY = 0; // 0x0
+    field public static deprecated int PAUSE;
+    field public static deprecated int PLAY;
+  }
+
+  public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int);
+    field public static deprecated int ALL;
+    field public static final int INDEX_ALL = 1; // 0x1
+    field public static final int INDEX_NONE = 0; // 0x0
+    field public static final int INDEX_ONE = 2; // 0x2
+    field public static deprecated int NONE;
+    field public static deprecated int ONE;
+  }
+
+  public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context);
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context);
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipNextAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int);
+    field public static final int INDEX_OUTLINE = 1; // 0x1
+    field public static final int INDEX_SOLID = 0; // 0x0
+    field public static deprecated int OUTLINE;
+    field public static deprecated int SOLID;
+  }
+
+  public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
+  }
+
+  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public PlaybackControlsRowPresenter();
+    method public boolean areSecondaryActionsHidden();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+    method public void setSecondaryActionsHidden(boolean);
+    method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean);
+    method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
+  }
+
+  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
+  }
+
+  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public PlaybackRowPresenter();
+    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+  }
+
+  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
+  }
+
+  public class PlaybackSeekDataProvider {
+    ctor public PlaybackSeekDataProvider();
+    method public long[] getSeekPositions();
+    method public void getThumbnail(int, android.support.v17.leanback.widget.PlaybackSeekDataProvider.ResultCallback);
+    method public void reset();
+  }
+
+  public static class PlaybackSeekDataProvider.ResultCallback {
+    ctor public PlaybackSeekDataProvider.ResultCallback();
+    method public void onThumbnailLoaded(android.graphics.Bitmap, int);
+  }
+
+  public abstract interface PlaybackSeekUi {
+    method public abstract void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public static class PlaybackSeekUi.Client {
+    ctor public PlaybackSeekUi.Client();
+    method public android.support.v17.leanback.widget.PlaybackSeekDataProvider getPlaybackSeekDataProvider();
+    method public boolean isSeekEnabled();
+    method public void onSeekFinished(boolean);
+    method public void onSeekPositionChanged(long);
+    method public void onSeekStarted();
+  }
+
+  public class PlaybackTransportRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackTransportRowPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public float getDefaultSeekIncrement();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method protected void onProgressBarClicked(android.support.v17.leanback.widget.PlaybackTransportRowPresenter.ViewHolder);
+    method public void setDefaultSeekIncrement(float);
+    method public void setDescriptionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+  }
+
+  public class PlaybackTransportRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackTransportRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    method public final android.widget.TextView getCurrentPositionView();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDescriptionViewHolder();
+    method public final android.widget.TextView getDurationView();
+    method protected void onSetCurrentPositionLabel(long);
+    method protected void onSetDurationLabel(long);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter();
+    method protected static void cancelAnimationsRecursive(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener);
+  }
+
+  public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter.ViewHolder(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    field public final android.view.View view;
+  }
+
+  public static abstract class Presenter.ViewHolderTask {
+    ctor public Presenter.ViewHolderTask();
+    method public void run(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public abstract class PresenterSelector {
+    ctor public PresenterSelector();
+    method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter[] getPresenters();
+  }
+
+  public abstract class PresenterSwitcher {
+    ctor public PresenterSwitcher();
+    method public void clear();
+    method public final android.view.ViewGroup getParentViewGroup();
+    method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector);
+    method protected abstract void insertView(android.view.View);
+    method protected void onViewSelected(android.view.View);
+    method public void select(java.lang.Object);
+    method protected void showView(android.view.View, boolean);
+    method public void unselect();
+  }
+
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
+    ctor public RecyclerViewParallax();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
+    method public float getMaxValue();
+    method public android.support.v7.widget.RecyclerView getRecyclerView();
+    method public void setRecyclerView(android.support.v7.widget.RecyclerView);
+  }
+
+  public static final class RecyclerViewParallax.ChildPositionProperty extends android.support.v17.leanback.widget.Parallax.IntProperty {
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty adapterPosition(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty fraction(float);
+    method public int getAdapterPosition();
+    method public float getFraction();
+    method public int getOffset();
+    method public int getViewId();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty offset(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty viewId(int);
+  }
+
+  public class Row {
+    ctor public Row(long, android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row(android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row();
+    method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem();
+    method public final long getId();
+    method public boolean isRenderedAsRowView();
+    method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem);
+    method public final void setId(long);
+  }
+
+  public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowHeaderPresenter();
+    method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint);
+    method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public boolean isNullItemVisibilityGone();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setNullItemVisibilityGone(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float);
+  }
+
+  public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowHeaderPresenter.ViewHolder(android.view.View);
+    method public final float getSelectLevel();
+  }
+
+  public final class RowHeaderView extends android.widget.TextView {
+    ctor public RowHeaderView(android.content.Context);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowPresenter();
+    method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter();
+    method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final boolean getSelectEffectEnabled();
+    method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final int getSyncActivatePolicy();
+    method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected boolean isClippingChildren();
+    method public boolean isUsingDefaultSelectEffect();
+    method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter);
+    method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setSelectEffectEnabled(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float);
+    method public final void setSyncActivatePolicy(int);
+    field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3
+    field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2
+  }
+
+  public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowPresenter.ViewHolder(android.view.View);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.view.View.OnKeyListener getOnKeyListener();
+    method public final android.support.v17.leanback.widget.Row getRow();
+    method public final java.lang.Object getRowObject();
+    method public final float getSelectLevel();
+    method public java.lang.Object getSelectedItem();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
+    method public final boolean isExpanded();
+    method public final boolean isSelected();
+    method public final void setActivated(boolean);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setOnKeyListener(android.view.View.OnKeyListener);
+    method public final void syncActivatedStatus(android.view.View);
+    field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer;
+  }
+
+  public class SearchBar extends android.widget.RelativeLayout {
+    ctor public SearchBar(android.content.Context);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet, int);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.String getTitle();
+    method public boolean isRecognizing();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
+    method public void setSearchQuery(java.lang.String);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setSpeechRecognizer(android.speech.SpeechRecognizer);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+    method public void stopRecognition();
+  }
+
+  public static abstract interface SearchBar.SearchBarListener {
+    method public abstract void onKeyboardDismiss(java.lang.String);
+    method public abstract void onSearchQueryChange(java.lang.String);
+    method public abstract void onSearchQuerySubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchBar.SearchBarPermissionListener {
+    method public abstract void requestAudioPermission();
+  }
+
+  public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView {
+    ctor public SearchEditText(android.content.Context);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener);
+  }
+
+  public static abstract interface SearchEditText.OnKeyboardDismissListener {
+    method public abstract void onKeyboardDismiss();
+  }
+
+  public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableOrbColorAnimation(boolean);
+    method public int getOrbColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors();
+    method public android.graphics.drawable.Drawable getOrbIcon();
+    method public void onClick(android.view.View);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener);
+    method public void setOrbColor(int);
+    method public deprecated void setOrbColor(int, int);
+    method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
+  }
+
+  public static class SearchOrbView.Colors {
+    ctor public SearchOrbView.Colors(int);
+    ctor public SearchOrbView.Colors(int, int);
+    ctor public SearchOrbView.Colors(int, int, int);
+    method public static int getBrightColor(int);
+    field public int brightColor;
+    field public int color;
+    field public int iconColor;
+  }
+
+  public class SectionRow extends android.support.v17.leanback.widget.Row {
+    ctor public SectionRow(android.support.v17.leanback.widget.HeaderItem);
+    ctor public SectionRow(long, java.lang.String);
+    ctor public SectionRow(java.lang.String);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public class ShadowOverlayContainer extends android.widget.FrameLayout {
+    ctor public ShadowOverlayContainer(android.content.Context);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
+    method public int getShadowType();
+    method public android.view.View getWrappedView();
+    method public deprecated void initialize(boolean, boolean);
+    method public deprecated void initialize(boolean, boolean, boolean);
+    method public static void prepareParentForShadow(android.view.ViewGroup);
+    method public void setOverlayColor(int);
+    method public void setShadowFocusLevel(float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsShadow();
+    method public void useDynamicShadow();
+    method public void useDynamicShadow(float, float);
+    method public void useStaticShadow();
+    method public void wrap(android.view.View);
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public final class ShadowOverlayHelper {
+    method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context);
+    method public int getShadowType();
+    method public boolean needsOverlay();
+    method public boolean needsRoundedCorner();
+    method public boolean needsWrapper();
+    method public void onViewCreated(android.view.View);
+    method public void prepareParentForShadow(android.view.ViewGroup);
+    method public static void setNoneWrapperOverlayColor(android.view.View, int);
+    method public static void setNoneWrapperShadowFocusLevel(android.view.View, float);
+    method public void setOverlayColor(android.view.View, int);
+    method public void setShadowFocusLevel(android.view.View, float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsForeground();
+    method public static boolean supportsRoundedCorner();
+    method public static boolean supportsShadow();
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public static final class ShadowOverlayHelper.Builder {
+    ctor public ShadowOverlayHelper.Builder();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean);
+  }
+
+  public static final class ShadowOverlayHelper.Options {
+    ctor public ShadowOverlayHelper.Options();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float);
+    method public final float getDynamicShadowFocusedZ();
+    method public final float getDynamicShadowUnfocusedZ();
+    method public final int getRoundedCornerRadius();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int);
+    field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT;
+  }
+
+  public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public SparseArrayObjectAdapter();
+    method public void clear(int);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public int indexOf(int);
+    method public java.lang.Object lookup(int);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public void set(int, java.lang.Object);
+    method public int size();
+  }
+
+  public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView {
+    ctor public SpeechOrbView(android.content.Context);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSoundLevel(int);
+    method public void showListening();
+    method public void showNotListening();
+  }
+
+  public abstract interface SpeechRecognitionCallback {
+    method public abstract void recognizeSpeech();
+  }
+
+   class StreamingTextView extends android.widget.EditText {
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet);
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int);
+    method public static boolean isLayoutRtl(android.view.View);
+    method public void reset();
+    method public void setFinalRecognizedText(java.lang.CharSequence);
+    method public void updateRecognizedText(java.lang.String, java.lang.String);
+    method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>);
+  }
+
+  public class TitleHelper {
+    ctor public TitleHelper(android.view.ViewGroup, android.view.View);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public android.view.ViewGroup getSceneRoot();
+    method public android.view.View getTitleView();
+    method public void showTitle(boolean);
+  }
+
+  public class TitleView extends android.widget.FrameLayout implements android.support.v17.leanback.widget.TitleViewAdapter.Provider {
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableAnimation(boolean);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+  }
+
+  public abstract class TitleViewAdapter {
+    ctor public TitleViewAdapter();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public abstract android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public void setAnimationEnabled(boolean);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+    field public static final int BRANDING_VIEW_VISIBLE = 2; // 0x2
+    field public static final int FULL_VIEW_VISIBLE = 6; // 0x6
+    field public static final int SEARCH_VIEW_VISIBLE = 4; // 0x4
+  }
+
+  public static abstract interface TitleViewAdapter.Provider {
+    method public abstract android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+  }
+
+  public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public VerticalGridPresenter();
+    ctor public VerticalGridPresenter(int);
+    ctor public VerticalGridPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public final int getFocusZoomFactor();
+    method public final boolean getKeepChildForeground();
+    method public int getNumberOfColumns();
+    method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public final boolean getShadowEnabled();
+    method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder);
+    method public final boolean isFocusDimmerUsed();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumberOfColumns(int);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView);
+    method public android.support.v17.leanback.widget.VerticalGridView getGridView();
+  }
+
+  public class VerticalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public VerticalGridView(android.content.Context);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int);
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public void setColumnWidth(int);
+    method public void setNumColumns(int);
+  }
+
+  public abstract interface ViewHolderTask {
+    method public abstract void run(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+}
+
+package android.support.v17.leanback.widget.picker {
+
+  public class Picker extends android.widget.FrameLayout {
+    ctor public Picker(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public float getActivatedVisibleItemCount();
+    method public android.support.v17.leanback.widget.picker.PickerColumn getColumnAt(int);
+    method public int getColumnsCount();
+    method protected int getPickerItemHeightPixels();
+    method public final int getPickerItemLayoutId();
+    method public final int getPickerItemTextViewId();
+    method public int getSelectedColumn();
+    method public final java.lang.CharSequence getSeparator();
+    method public float getVisibleItemCount();
+    method public void onColumnValueChanged(int, int);
+    method public void removeOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public void setActivatedVisibleItemCount(float);
+    method public void setColumnAt(int, android.support.v17.leanback.widget.picker.PickerColumn);
+    method public void setColumnValue(int, int, boolean);
+    method public void setColumns(java.util.List<android.support.v17.leanback.widget.picker.PickerColumn>);
+    method public final void setPickerItemTextViewId(int);
+    method public void setSelectedColumn(int);
+    method public final void setSeparator(java.lang.CharSequence);
+    method public void setVisibleItemCount(float);
+  }
+
+  public static abstract interface Picker.PickerValueListener {
+    method public abstract void onValueChanged(android.support.v17.leanback.widget.picker.Picker, int);
+  }
+
+  public class PickerColumn {
+    ctor public PickerColumn();
+    method public int getCount();
+    method public int getCurrentValue();
+    method public java.lang.CharSequence getLabelFor(int);
+    method public java.lang.String getLabelFormat();
+    method public int getMaxValue();
+    method public int getMinValue();
+    method public java.lang.CharSequence[] getStaticLabels();
+    method public void setCurrentValue(int);
+    method public void setLabelFormat(java.lang.String);
+    method public void setMaxValue(int);
+    method public void setMinValue(int);
+    method public void setStaticLabels(java.lang.CharSequence[]);
+  }
+
+  public class TimePicker extends android.support.v17.leanback.widget.picker.Picker {
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet);
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24Hour();
+    method public boolean isPm();
+    method public void setHour(int);
+    method public void setIs24Hour(boolean);
+    method public void setMinute(int);
+  }
+
+}
+
+package android.support.v17.preference {
+
+  public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment {
+    ctor public BaseLeanbackPreferenceFragment();
+  }
+
+  public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment {
+    ctor public LeanbackListPreferenceDialogFragment();
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String);
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String);
+    method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter();
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener);
+    method public android.view.ViewGroup getContainer();
+    method public android.widget.TextView getTitleView();
+    method public android.widget.Checkable getWidgetView();
+    method public void onClick(android.view.View);
+  }
+
+  public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackPreferenceDialogFragment extends android.app.Fragment {
+    ctor public LeanbackPreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    field public static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
+    ctor public LeanbackPreferenceFragment();
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
+    ctor public LeanbackSettingsFragment();
+    method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+    method public abstract void onPreferenceStartInitialScreen();
+    method public void startImmersiveFragment(android.app.Fragment);
+    method public void startPreferenceFragment(android.app.Fragment);
+  }
+
+}
+
+package android.support.v4.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static java.lang.String capabilityToString(int);
+    method public static java.lang.String feedbackTypeToString(int);
+    method public static java.lang.String flagToString(int);
+    method public static deprecated boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final deprecated int DEFAULT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package android.support.v4.app {
+
+  public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int);
+    method public boolean isDrawerIndicatorEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void syncState();
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class ActivityCompat extends android.support.v4.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri getReferrer(android.app.Activity);
+    method public static boolean invalidateOptionsMenu(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void requestPermissions(android.app.Activity, java.lang.String[], int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect getLaunchBounds();
+    method public static android.support.v4.app.ActivityOptionsCompat makeBasic();
+    method public static android.support.v4.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...);
+    method public static android.support.v4.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public android.support.v4.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect);
+    method public android.os.Bundle toBundle();
+    method public void update(android.support.v4.app.ActivityOptionsCompat);
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  public class AppLaunchChecker {
+    ctor public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
+    method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
+    method public static java.lang.String permissionToOp(java.lang.String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String);
+    method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder);
+  }
+
+  public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method public android.app.Dialog getDialog();
+    method public boolean getShowsDialog();
+    method public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method public android.app.Dialog onCreateDialog(android.os.Bundle);
+    method public void onDismiss(android.content.DialogInterface);
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, int);
+    method public void show(android.support.v4.app.FragmentManager, java.lang.String);
+    method public int show(android.support.v4.app.FragmentTransaction, java.lang.String);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+    ctor public Fragment();
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final boolean equals(java.lang.Object);
+    method public final android.support.v4.app.FragmentActivity getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle getArguments();
+    method public final android.support.v4.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context getContext();
+    method public java.lang.Object getEnterTransition();
+    method public java.lang.Object getExitTransition();
+    method public final android.support.v4.app.FragmentManager getFragmentManager();
+    method public final java.lang.Object getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method public android.support.v4.app.LoaderManager getLoaderManager();
+    method public final android.support.v4.app.Fragment getParentFragment();
+    method public java.lang.Object getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method public final boolean getRetainInstance();
+    method public java.lang.Object getReturnTransition();
+    method public java.lang.Object getSharedElementEnterTransition();
+    method public java.lang.Object getSharedElementReturnTransition();
+    method public final java.lang.String getString(int);
+    method public final java.lang.String getString(int, java.lang.Object...);
+    method public final java.lang.String getTag();
+    method public final android.support.v4.app.Fragment getTargetFragment();
+    method public final int getTargetRequestCode();
+    method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
+    method public android.view.View getView();
+    method public final int hashCode();
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String);
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method public void onActivityCreated(android.os.Bundle);
+    method public void onActivityResult(int, int, android.content.Intent);
+    method public void onAttach(android.content.Context);
+    method public deprecated void onAttach(android.app.Activity);
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public boolean onContextItemSelected(android.view.MenuItem);
+    method public void onCreate(android.os.Bundle);
+    method public android.view.animation.Animation onCreateAnimation(int, boolean, int);
+    method public android.animation.Animator onCreateAnimator(int, boolean, int);
+    method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
+    method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDestroy();
+    method public void onDestroyOptionsMenu();
+    method public void onDestroyView();
+    method public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
+    method public void onHiddenChanged(boolean);
+    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
+    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
+    method public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void onOptionsMenuClosed(android.view.Menu);
+    method public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
+    method public void onResume();
+    method public void onSaveInstanceState(android.os.Bundle);
+    method public void onStart();
+    method public void onStop();
+    method public void onViewCreated(android.view.View, android.os.Bundle);
+    method public void onViewStateRestored(android.os.Bundle);
+    method public void postponeEnterTransition();
+    method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle);
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setEnterTransition(java.lang.Object);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitTransition(java.lang.Object);
+    method public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(java.lang.Object);
+    method public void setRetainInstance(boolean);
+    method public void setReturnTransition(java.lang.Object);
+    method public void setSharedElementEnterTransition(java.lang.Object);
+    method public void setSharedElementReturnTransition(java.lang.Object);
+    method public void setTargetFragment(android.support.v4.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle);
+    method public void startActivityForResult(android.content.Intent, int);
+    method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR;
+  }
+
+  public class FragmentActivity extends android.app.Activity implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback {
+    ctor public FragmentActivity();
+    method public java.lang.Object getLastCustomNonConfigurationInstance();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onMultiWindowModeChanged(boolean);
+    method public void onPictureInPictureModeChanged(boolean);
+    method protected void onResumeFragments();
+    method public java.lang.Object onRetainCustomNonConfigurationInstance();
+    method public final java.lang.Object onRetainNonConfigurationInstance();
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method public deprecated void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method public android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public abstract android.view.View onFindViewById(int);
+    method public abstract boolean onHasView();
+  }
+
+  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 void dispatchActivityCreated();
+    method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method public void dispatchLowMemory();
+    method public void dispatchMultiWindowModeChanged(boolean);
+    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method public void dispatchPictureInPictureModeChanged(boolean);
+    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method public void doLoaderDestroy();
+    method public void doLoaderRetain();
+    method public void doLoaderStart();
+    method public void doLoaderStop(boolean);
+    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public boolean execPendingActions();
+    method public android.support.v4.app.Fragment findFragmentByWho(java.lang.String);
+    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
+    method public int getActiveFragmentsCount();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public void reportLoaderStart();
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig);
+    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
+    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
+    method public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.support.v4.app.Fragment> retainNonConfig();
+    method public android.os.Parcelable saveAllState();
+  }
+
+  public abstract class FragmentHostCallback<E> extends android.support.v4.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.view.View onFindViewById(int);
+    method public abstract E onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int);
+    method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(java.lang.String);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager {
+    ctor public FragmentManager();
+    method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.FragmentTransaction beginTransaction();
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract boolean executePendingTransactions();
+    method public abstract android.support.v4.app.Fragment findFragmentById(int);
+    method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String);
+    method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public abstract int getBackStackEntryCount();
+    method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String);
+    method public abstract java.util.List<android.support.v4.app.Fragment> getFragments();
+    method public abstract android.support.v4.app.Fragment getPrimaryNavigationFragment();
+    method public abstract boolean isDestroyed();
+    method public abstract boolean isStateSaved();
+    method public abstract void popBackStack();
+    method public abstract void popBackStack(java.lang.String, int);
+    method public abstract void popBackStack(int, int);
+    method public abstract boolean popBackStackImmediate();
+    method public abstract boolean popBackStackImmediate(java.lang.String, int);
+    method public abstract boolean popBackStackImmediate(int, int);
+    method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static abstract interface FragmentManager.BackStackEntry {
+    method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
+    method public abstract int getBreadCrumbShortTitleRes();
+    method public abstract java.lang.CharSequence getBreadCrumbTitle();
+    method public abstract int getBreadCrumbTitleRes();
+    method public abstract int getId();
+    method public abstract java.lang.String getName();
+  }
+
+  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentPreCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+  }
+
+  public static abstract interface FragmentManager.OnBackStackChangedListener {
+    method public abstract void onBackStackChanged();
+  }
+
+  public class FragmentManagerNonConfig {
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public deprecated void setup();
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor public FragmentTransaction();
+    method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
+    method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack();
+    method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
+    method public abstract boolean isAddToBackStackAllowed();
+    method public abstract boolean isEmpty();
+    method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction runOnCommit(java.lang.Runnable);
+    method public abstract deprecated android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setPrimaryNavigationFragment(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction setReorderingAllowed(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setTransition(int);
+    method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int);
+    method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray[] getMetrics();
+    method public android.util.SparseIntArray[] remove(android.app.Activity);
+    method public android.util.SparseIntArray[] reset();
+    method public android.util.SparseIntArray[] stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public abstract class JobIntentService extends android.app.Service {
+    ctor public JobIntentService();
+    method public static void enqueueWork(android.content.Context, java.lang.Class, int, android.content.Intent);
+    method public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method public boolean isStopped();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract void onHandleWork(android.content.Intent);
+    method public boolean onStopCurrentWork();
+    method public void setInterruptIfStopped(boolean);
+  }
+
+  public class ListFragment extends android.support.v4.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public void setEmptyText(java.lang.CharSequence);
+    method public void setListAdapter(android.widget.ListAdapter);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+  public abstract class LoaderManager {
+    ctor public LoaderManager();
+    method public abstract void destroyLoader(int);
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract <D> android.support.v4.content.Loader<D> getLoader(int);
+    method public boolean hasRunningLoaders();
+    method public abstract <D> android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+    method public abstract <D> android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+  }
+
+  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+    method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle);
+    method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D);
+    method public abstract void onLoaderReset(android.support.v4.content.Loader<D>);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static java.lang.String getParentActivityName(android.app.Activity);
+    method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static java.lang.String getCategory(android.app.Notification);
+    method public static java.lang.String getChannelId(android.app.Notification);
+    method public static android.os.Bundle getExtras(android.app.Notification);
+    method public static java.lang.String getGroup(android.app.Notification);
+    method public static int getGroupAlertBehavior(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static java.lang.String getShortcutId(android.app.Notification);
+    method public static java.lang.String getSortKey(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+    field public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+    field public static final java.lang.String EXTRA_PICTURE = "android.picture";
+    field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
+    field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
+    field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
+    field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
+    field public static final java.lang.String EXTRA_TEXT = "android.text";
+    field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_TITLE = "android.title";
+    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.app.PendingIntent getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public android.support.v4.app.RemoteInput[] getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public android.support.v4.app.RemoteInput[] getRemoteInputs();
+    method public java.lang.CharSequence getTitle();
+    field public android.app.PendingIntent actionIntent;
+    field public int icon;
+    field public java.lang.CharSequence title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
+    ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput);
+    method public android.support.v4.app.NotificationCompat.Action build();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+  }
+
+  public static abstract interface NotificationCompat.Action.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+    method public java.lang.CharSequence getCancelLabel();
+    method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method public java.lang.CharSequence getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated NotificationCompat.Builder(android.content.Context);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String);
+    method public android.app.Notification build();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method public deprecated android.app.Notification getNotification();
+    method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setChannelId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setColor(int);
+    method public android.support.v4.app.NotificationCompat.Builder setColorized(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setDefaults(int);
+    method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setNumber(int);
+    method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPriority(int);
+    method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.support.v4.app.NotificationCompat.Builder setShortcutId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int);
+    method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style);
+    method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]);
+    method public android.support.v4.app.NotificationCompat.Builder setVisibility(int);
+    method public android.support.v4.app.NotificationCompat.Builder setWhen(long);
+    field public java.util.ArrayList<java.lang.String> mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.support.v4.app.NotificationCompat.CarExtender setColor(int);
+    method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation);
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.support.v4.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static abstract interface NotificationCompat.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
+    method public void addCompatExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(android.support.v4.app.NotificationCompat.MessagingStyle.Message);
+    method public static android.support.v4.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.support.v4.app.NotificationCompat.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+  }
+
+  public static abstract class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification build();
+    method public void setBuilder(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearActions();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearPages();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public java.lang.String getDismissalId();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintAmbientBigPicture();
+    method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintHideIcon();
+    method public int getHintScreenTimeout();
+    method public boolean getHintShowBackgroundOnly();
+    method public java.util.List<android.app.Notification> getPages();
+    method public boolean getStartScrollBottom();
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBridgeTag(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDismissalId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field public static final int SIZE_LARGE = 4; // 0x4
+    field public static final int SIZE_MEDIUM = 3; // 0x3
+    field public static final int SIZE_SMALL = 2; // 0x2
+    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(java.lang.String, int, java.lang.String);
+    method public abstract void cancelAll(java.lang.String);
+    method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(java.lang.String, int);
+    method public void cancelAll();
+    method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public void notify(int, android.app.Notification);
+    method public void notify(java.lang.String, int, android.app.Notification);
+    field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public final class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput {
+    method public static void addDataResultToIntent(android.support.v4.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
+    method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
+    method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getResultKey();
+    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
+    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(java.lang.String);
+    method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
+    method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+    method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+  }
+
+   deprecated class RemoteInputCompatBase {
+  }
+
+  public static abstract class RemoteInputCompatBase.RemoteInput {
+    ctor public RemoteInputCompatBase.RemoteInput();
+    method protected abstract boolean getAllowFreeFormInput();
+    method protected abstract java.util.Set<java.lang.String> getAllowedDataTypes();
+    method protected abstract java.lang.CharSequence[] getChoices();
+    method protected abstract android.os.Bundle getExtras();
+    method protected abstract java.lang.CharSequence getLabel();
+    method protected abstract java.lang.String getResultKey();
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName getCallingActivity(android.app.Activity);
+    method public static java.lang.String getCallingPackage(android.app.Activity);
+    field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName getCallingActivity();
+    method public android.graphics.drawable.Drawable getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable getCallingApplicationIcon();
+    method public java.lang.CharSequence getCallingApplicationLabel();
+    method public java.lang.String getCallingPackage();
+    method public java.lang.String[] getEmailBcc();
+    method public java.lang.String[] getEmailCc();
+    method public java.lang.String[] getEmailTo();
+    method public java.lang.String getHtmlText();
+    method public android.net.Uri getStream();
+    method public android.net.Uri getStream(int);
+    method public int getStreamCount();
+    method public java.lang.String getSubject();
+    method public java.lang.CharSequence getText();
+    method public java.lang.String getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
+    method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable);
+    method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
+    method public void onRejectSharedElements(java.util.List<android.view.View>);
+    method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String>, java.util.List<android.view.View>, android.support.v4.app.SharedElementCallback.OnSharedElementsReadyListener);
+  }
+
+  public static abstract interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public abstract void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable {
+    method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public static android.support.v4.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent editIntentAt(int);
+    method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context);
+    method public deprecated android.content.Intent getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent[] getIntents();
+    method public android.app.PendingIntent getPendingIntent(int, int);
+    method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle);
+    method public deprecated java.util.Iterator<android.content.Intent> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle);
+  }
+
+  public static abstract interface TaskStackBuilder.SupportParentable {
+    method public abstract android.content.Intent getSupportParentActivityIntent();
+  }
+
+}
+
+package android.support.v4.content {
+
+  public abstract class AsyncTaskLoader<D> extends android.support.v4.content.Loader {
+    ctor public AsyncTaskLoader(android.content.Context);
+    method public void cancelLoadInBackground();
+    method public boolean isLoadInBackgroundCanceled();
+    method public abstract D loadInBackground();
+    method public void onCanceled(D);
+    method protected D onLoadInBackground();
+    method public void setUpdateThrottle(long);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    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 java.io.File getDataDir(android.content.Context);
+    method public static final 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[] getObbDirs(android.content.Context);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class CursorLoader extends android.support.v4.content.AsyncTaskLoader {
+    ctor public CursorLoader(android.content.Context);
+    ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public void deliverResult(android.database.Cursor);
+    method public java.lang.String[] getProjection();
+    method public java.lang.String getSelection();
+    method public java.lang.String[] getSelectionArgs();
+    method public java.lang.String getSortOrder();
+    method public android.net.Uri getUri();
+    method public android.database.Cursor loadInBackground();
+    method public void onCanceled(android.database.Cursor);
+    method public void setProjection(java.lang.String[]);
+    method public void setSelection(java.lang.String);
+    method public void setSelectionArgs(java.lang.String[]);
+    method public void setSortOrder(java.lang.String);
+    method public void setUri(android.net.Uri);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File);
+    method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
+  public final class IntentCompat {
+    method public static deprecated android.content.Intent makeMainActivity(android.content.ComponentName);
+    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
+    method public static deprecated android.content.Intent makeRestartActivityTask(android.content.ComponentName);
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final java.lang.String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final deprecated int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
+    field public static final deprecated int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
+  }
+
+  public class Loader<D> {
+    ctor public Loader(android.content.Context);
+    method public void abandon();
+    method public boolean cancelLoad();
+    method public void commitContentChanged();
+    method public java.lang.String dataToString(D);
+    method public void deliverCancellation();
+    method public void deliverResult(D);
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void forceLoad();
+    method public android.content.Context getContext();
+    method public int getId();
+    method public boolean isAbandoned();
+    method public boolean isReset();
+    method public boolean isStarted();
+    method protected void onAbandon();
+    method protected boolean onCancelLoad();
+    method public void onContentChanged();
+    method protected void onForceLoad();
+    method protected void onReset();
+    method protected void onStartLoading();
+    method protected void onStopLoading();
+    method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+    method public void reset();
+    method public void rollbackContentChanged();
+    method public final void startLoading();
+    method public void stopLoading();
+    method public boolean takeContentChanged();
+    method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+  }
+
+  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+    ctor public Loader.ForceLoadContentObserver();
+  }
+
+  public static abstract interface Loader.OnLoadCanceledListener<D> {
+    method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>);
+  }
+
+  public static abstract interface Loader.OnLoadCompleteListener<D> {
+    method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D);
+  }
+
+  public final class LocalBroadcastManager {
+    method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context);
+    method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+    method public boolean sendBroadcast(android.content.Intent);
+    method public void sendBroadcastSync(android.content.Intent);
+    method public void unregisterReceiver(android.content.BroadcastReceiver);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(java.lang.String, java.lang.String);
+    method public static java.lang.String matches(java.lang.String, java.lang.String[]);
+    method public static java.lang.String matches(java.lang.String[], java.lang.String);
+    method public static java.lang.String[] matchesMany(java.lang.String[], java.lang.String);
+  }
+
+  public final deprecated class ParallelExecutorCompat {
+    method public static deprecated java.util.concurrent.Executor getParallelExecutor();
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String);
+    method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String);
+    method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String);
+    method public static int checkSelfPermission(android.content.Context, java.lang.String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  public final class SharedPreferencesCompat {
+  }
+
+  public static final class SharedPreferencesCompat.EditorCompat {
+    method public void apply(android.content.SharedPreferences.Editor);
+    method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance();
+  }
+
+  public abstract deprecated class WakefulBroadcastReceiver extends android.content.BroadcastReceiver {
+    ctor public WakefulBroadcastReceiver();
+    method public static boolean completeWakefulIntent(android.content.Intent);
+    method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent);
+  }
+
+}
+
+package android.support.v4.content.pm {
+
+  public final class ActivityInfoCompat {
+    field public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName getActivity();
+    method public java.lang.CharSequence getDisabledMessage();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent[] getIntents();
+    method public java.lang.CharSequence getLongLabel();
+    method public java.lang.CharSequence getShortLabel();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, java.lang.String);
+    method public android.support.v4.content.pm.ShortcutInfoCompat build();
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(java.lang.CharSequence);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.graphics.Bitmap);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(int);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.support.v4.graphics.drawable.IconCompat);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent[]);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setLongLabel(java.lang.CharSequence);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setShortLabel(java.lang.CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean requestPinShortcut(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat, android.content.IntentSender);
+  }
+
+}
+
+package android.support.v4.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+    method public static deprecated int getScreenHeightDp(android.content.res.Resources);
+    method public static deprecated int getScreenWidthDp(android.content.res.Resources);
+    method public static deprecated int getSmallestScreenWidthDp(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.Typeface getFont(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
+  }
+
+}
+
+package android.support.v4.database {
+
+  public final class DatabaseUtilsCompat {
+    method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]);
+    method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public final class ColorUtils {
+    method public static int HSLToColor(float[]);
+    method public static int LABToColor(double, double, double);
+    method public static void LABToXYZ(double, double, double, double[]);
+    method public static void RGBToHSL(int, int, int, float[]);
+    method public static void RGBToLAB(int, int, int, double[]);
+    method public static void RGBToXYZ(int, int, int, double[]);
+    method public static int XYZToColor(double, double, double);
+    method public static void XYZToLAB(double, double, double, double[]);
+    method public static int blendARGB(int, int, float);
+    method public static void blendHSL(float[], float[], float, float[]);
+    method public static void blendLAB(double[], double[], double, double[]);
+    method public static double calculateContrast(int, int);
+    method public static double calculateLuminance(int);
+    method public static int calculateMinimumAlpha(int, int, float);
+    method public static void colorToHSL(int, float[]);
+    method public static void colorToLAB(int, double[]);
+    method public static void colorToXYZ(int, double[]);
+    method public static int compositeColors(int, int);
+    method public static double distanceEuclidean(double[], double[]);
+    method public static int setAlphaComponent(int, int);
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  public class IconCompat {
+    method public static android.support.v4.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(java.lang.String);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithData(byte[], int, int);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithResource(android.content.Context, int);
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package android.support.v4.hardware.display {
+
+  public abstract class DisplayManagerCompat {
+    method public abstract android.view.Display getDisplay(int);
+    method public abstract android.view.Display[] getDisplays();
+    method public abstract android.view.Display[] getDisplays(java.lang.String);
+    method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package android.support.v4.hardware.fingerprint {
+
+  public final class FingerprintManagerCompat {
+    method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler);
+    method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
+  }
+
+  public static abstract class FingerprintManagerCompat.AuthenticationCallback {
+    ctor public FingerprintManagerCompat.AuthenticationCallback();
+    method public void onAuthenticationError(int, java.lang.CharSequence);
+    method public void onAuthenticationFailed();
+    method public void onAuthenticationHelp(int, java.lang.CharSequence);
+    method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult);
+  }
+
+  public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject);
+    method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject();
+  }
+
+  public static class FingerprintManagerCompat.CryptoObject {
+    ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method public javax.crypto.Cipher getCipher();
+    method public javax.crypto.Mac getMac();
+    method public java.security.Signature getSignature();
+  }
+
+}
+
+package android.support.v4.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+  }
+
+}
+
+package android.support.v4.media {
+
+  public class AudioAttributesCompat {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method public int getUsage();
+    method public int getVolumeControlStream();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.media.AudioAttributesCompat wrap(java.lang.Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(android.support.v4.media.AudioAttributesCompat);
+    method public android.support.v4.media.AudioAttributesCompat build();
+    method public android.support.v4.media.AudioAttributesCompat.Builder setContentType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setFlags(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setLegacyStreamType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setUsage(int);
+  }
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle getExtras();
+    method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method public java.lang.String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.CustomActionCallback);
+    method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(java.lang.String);
+    method public void unsubscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final java.lang.String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+    field public static final java.lang.String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE = "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+    field public static final java.lang.String EXTRA_DOWNLOAD_PROGRESS = "android.media.browse.extra.DOWNLOAD_PROGRESS";
+    field public static final java.lang.String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+    field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public static abstract class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onProgressUpdate(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onResult(java.lang.String, android.os.Bundle, android.os.Bundle);
+  }
+
+  public static abstract class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(java.lang.String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem fromMediaItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem> fromMediaItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public java.lang.String getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static abstract class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+  }
+
+  public static abstract class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>, android.os.Bundle);
+    method public void onError(java.lang.String);
+    method public void onError(java.lang.String, android.os.Bundle);
+  }
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public void notifyChildrenChanged(java.lang.String);
+    method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<android.os.Bundle>);
+    method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>, android.os.Bundle);
+    method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field public static final deprecated java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle);
+    method public void sendProgressUpdate(android.os.Bundle);
+    method public void sendResult(T);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object);
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public android.net.Uri getIconUri();
+    method public java.lang.Object getMediaDescription();
+    method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR;
+    field public static final java.lang.String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+    field public static final java.lang.String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(java.lang.String);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object);
+    method public android.graphics.Bitmap getBitmap(java.lang.String);
+    method public android.os.Bundle getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getLong(java.lang.String);
+    method public java.lang.Object getMediaMetadata();
+    method public android.support.v4.media.RatingCompat getRating(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR;
+    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat);
+    method public android.support.v4.media.MediaMetadataCompat build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object);
+    method public float getPercentRating();
+    method public java.lang.Object getRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat newStarRating(int, float);
+    method public static android.support.v4.media.RatingCompat newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public java.lang.Object getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static abstract class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat);
+  }
+
+}
+
+package android.support.v4.media.app {
+
+  public class NotificationCompat {
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v4.media.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
+    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
+    method public android.os.Bundle getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat getMediaController(android.app.Activity);
+    method public java.lang.Object getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat getMetadata();
+    method public java.lang.String getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
+    method public java.lang.CharSequence getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent getSessionActivity();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public int getShuffleMode();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public deprecated boolean isShuffleModeEnabled();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void removeQueueItemAt(int);
+    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onRepeatModeChanged(int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public deprecated void onShuffleModeChanged(boolean);
+    method public void onShuffleModeChanged(int);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public static abstract class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void playFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void playFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
+    method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRating(android.support.v4.media.RatingCompat, android.os.Bundle);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleMode(int);
+    method public abstract deprecated void setShuffleModeEnabled(boolean);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+    field public static final java.lang.String EXTRA_LEGACY_STREAM_TYPE = "android.media.session.extra.LEGACY_STREAM_TYPE";
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public static android.support.v4.media.session.MediaSessionCompat fromMediaSession(android.content.Context, java.lang.Object);
+    method public android.support.v4.media.session.MediaControllerCompat getController();
+    method public java.lang.Object getMediaSession();
+    method public java.lang.Object getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void setQueueTitle(java.lang.CharSequence);
+    method public void setRatingType(int);
+    method public void setRepeatMode(int);
+    method public void setSessionActivity(android.app.PendingIntent);
+    method public void setShuffleMode(int);
+    method public deprecated void setShuffleModeEnabled(boolean);
+    field public static final java.lang.String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final java.lang.String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+    field public static final java.lang.String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final java.lang.String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final java.lang.String ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final java.lang.String ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int MEDIA_ATTRIBUTE_ALBUM = 1; // 0x1
+    field public static final int MEDIA_ATTRIBUTE_ARTIST = 0; // 0x0
+    field public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2; // 0x2
+  }
+
+  public static abstract class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void onCustomAction(java.lang.String, android.os.Bundle);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRating(android.support.v4.media.RatingCompat, android.os.Bundle);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleMode(int);
+    method public deprecated void onSetShuffleModeEnabled(boolean);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static abstract interface MediaSessionCompat.OnActiveChangeListener {
+    method public abstract void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem fromQueueItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> fromQueueItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getQueueId();
+    method public java.lang.Object getQueueItem();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object);
+    method public java.lang.Object getToken();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object);
+    method public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions();
+    method public int getErrorCode();
+    method public java.lang.CharSequence getErrorMessage();
+    method public android.os.Bundle getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public java.lang.Object getPlaybackState();
+    method public long getPosition();
+    method public int getState();
+    method public static int toKeyCode(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction);
+    method public android.support.v4.media.session.PlaybackStateCompat build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long);
+    method public deprecated android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(int, java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object);
+    method public java.lang.String getAction();
+    method public java.lang.Object getCustomAction();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle);
+  }
+
+}
+
+package android.support.v4.net {
+
+  public final class ConnectivityManagerCompat {
+    method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class TrafficStatsCompat {
+    method public static deprecated void clearThreadStatsTag();
+    method public static deprecated int getThreadStatsTag();
+    method public static deprecated void incrementOperationCount(int);
+    method public static deprecated void incrementOperationCount(int, int);
+    method public static deprecated void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void tagSocket(java.net.Socket) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void untagSocket(java.net.Socket) throws java.net.SocketException;
+  }
+
+}
+
+package android.support.v4.os {
+
+  public final deprecated class AsyncTaskCompat {
+    method public static deprecated <Params, Progress, Result> android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
+  }
+
+  public class BuildCompat {
+    method public static deprecated boolean isAtLeastN();
+    method public static deprecated boolean isAtLeastNMR1();
+    method public static deprecated boolean isAtLeastO();
+    method public static boolean isAtLeastOMR1();
+    method public static boolean isAtLeastP();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public java.lang.Object getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener);
+    method public void throwIfCanceled();
+  }
+
+  public static abstract interface CancellationSignal.OnCancelListener {
+    method public abstract void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static android.support.v4.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static java.lang.String getStorageState(java.io.File);
+    field public static final java.lang.String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class LocaleListCompat {
+    method public static android.support.v4.os.LocaleListCompat create(java.util.Locale...);
+    method public static android.support.v4.os.LocaleListCompat forLanguageTags(java.lang.String);
+    method public java.util.Locale get(int);
+    method public static android.support.v4.os.LocaleListCompat getAdjustedDefault();
+    method public static android.support.v4.os.LocaleListCompat getDefault();
+    method public static android.support.v4.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale getFirstMatch(java.lang.String[]);
+    method public int indexOf(java.util.Locale);
+    method public boolean isEmpty();
+    method public int size();
+    method public java.lang.String toLanguageTags();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.os.LocaleListCompat wrap(java.lang.Object);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(java.lang.String);
+  }
+
+  public final deprecated class ParcelableCompat {
+    method public static deprecated <T> android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>);
+  }
+
+  public abstract deprecated interface ParcelableCompatCreatorCallbacks<T> {
+    method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader);
+    method public abstract T[] newArray(int);
+  }
+
+  public final class TraceCompat {
+    method public static void beginSection(java.lang.String);
+    method public static void endSection();
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package android.support.v4.print {
+
+  public final class PrintHelper {
+    ctor public PrintHelper(android.content.Context);
+    method public int getColorMode();
+    method public int getOrientation();
+    method public int getScaleMode();
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap);
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback);
+    method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException;
+    method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException;
+    method public void setColorMode(int);
+    method public void setOrientation(int);
+    method public void setScaleMode(int);
+    method public static boolean systemSupportsPrint();
+    field public static final int COLOR_MODE_COLOR = 2; // 0x2
+    field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
+    field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
+    field public static final int ORIENTATION_PORTRAIT = 2; // 0x2
+    field public static final int SCALE_MODE_FILL = 2; // 0x2
+    field public static final int SCALE_MODE_FIT = 1; // 0x1
+  }
+
+  public static abstract interface PrintHelper.OnPrintFinishCallback {
+    method public abstract void onFinish();
+  }
+
+}
+
+package android.support.v4.provider {
+
+  public abstract class DocumentFile {
+    method public abstract boolean canRead();
+    method public abstract boolean canWrite();
+    method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String);
+    method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String);
+    method public abstract boolean delete();
+    method public abstract boolean exists();
+    method public android.support.v4.provider.DocumentFile findFile(java.lang.String);
+    method public static android.support.v4.provider.DocumentFile fromFile(java.io.File);
+    method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri);
+    method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri);
+    method public abstract java.lang.String getName();
+    method public android.support.v4.provider.DocumentFile getParentFile();
+    method public abstract java.lang.String getType();
+    method public abstract android.net.Uri getUri();
+    method public abstract boolean isDirectory();
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public abstract boolean isFile();
+    method public abstract boolean isVirtual();
+    method public abstract long lastModified();
+    method public abstract long length();
+    method public abstract android.support.v4.provider.DocumentFile[] listFiles();
+    method public abstract boolean renameTo(java.lang.String);
+  }
+
+  public final class FontRequest {
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, int);
+    method public java.util.List<java.util.List<byte[]>> getCertificates();
+    method public int getCertificatesArrayResId();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getProviderPackage();
+    method public java.lang.String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontsContractCompat.FontInfo[]);
+    method public static android.support.v4.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, android.support.v4.provider.FontRequest, android.support.v4.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns {
+    ctor public FontsContractCompat.Columns();
+    field public static final java.lang.String FILE_ID = "file_id";
+    field public static final java.lang.String ITALIC = "font_italic";
+    field public static final java.lang.String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final java.lang.String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public android.support.v4.provider.FontsContractCompat.FontInfo[] getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package android.support.v4.text {
+
+  public final class BidiFormatter {
+    method public static android.support.v4.text.BidiFormatter getInstance();
+    method public static android.support.v4.text.BidiFormatter getInstance(boolean);
+    method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale);
+    method public boolean getStereoReset();
+    method public boolean isRtl(java.lang.String);
+    method public boolean isRtl(java.lang.CharSequence);
+    method public boolean isRtlContext();
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.String unicodeWrap(java.lang.String, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale);
+    method public android.support.v4.text.BidiFormatter build();
+    method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat);
+    method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean);
+  }
+
+  public final class ICUCompat {
+    method public static java.lang.String maximizeAndGetScript(java.util.Locale);
+  }
+
+  public abstract interface TextDirectionHeuristicCompat {
+    method public abstract boolean isRtl(char[], int, int);
+    method public abstract boolean isRtl(java.lang.CharSequence, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale);
+    method public static java.lang.String htmlEncode(java.lang.String);
+    field public static final deprecated java.util.Locale ROOT;
+  }
+
+}
+
+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);
+  }
+
+}
+
+package android.support.v4.util {
+
+  public class ArrayMap<K, V> extends android.support.v4.util.SimpleArrayMap implements java.util.Map {
+    ctor public ArrayMap();
+    ctor public ArrayMap(int);
+    ctor public ArrayMap(android.support.v4.util.SimpleArrayMap);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+    method public java.util.Set<K> keySet();
+    method public void putAll(java.util.Map<? extends K, ? extends V>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public java.util.Collection<V> values();
+  }
+
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    ctor public ArraySet();
+    ctor public ArraySet(int);
+    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
+    method public boolean add(E);
+    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(java.lang.Object);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public void ensureCapacity(int);
+    method public int indexOf(java.lang.Object);
+    method public boolean isEmpty();
+    method public java.util.Iterator<E> iterator();
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public E removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public int size();
+    method public java.lang.Object[] toArray();
+    method public <T> T[] toArray(T[]);
+    method public E valueAt(int);
+  }
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream);
+    method public void finishWrite(java.io.FileOutputStream);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public final class CircularArray<E> {
+    ctor public CircularArray();
+    ctor public CircularArray(int);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public void clear();
+    method public E get(int);
+    method public E getFirst();
+    method public E getLast();
+    method public boolean isEmpty();
+    method public E popFirst();
+    method public E popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public final class CircularIntArray {
+    ctor public CircularIntArray();
+    ctor public CircularIntArray(int);
+    method public void addFirst(int);
+    method public void addLast(int);
+    method public void clear();
+    method public int get(int);
+    method public int getFirst();
+    method public int getLast();
+    method public boolean isEmpty();
+    method public int popFirst();
+    method public int popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public class LongSparseArray<E> {
+    ctor public LongSparseArray();
+    ctor public LongSparseArray(int);
+    method public void append(long, E);
+    method public void clear();
+    method public android.support.v4.util.LongSparseArray<E> clone();
+    method public void delete(long);
+    method public E get(long);
+    method public E get(long, E);
+    method public int indexOfKey(long);
+    method public int indexOfValue(E);
+    method public long keyAt(int);
+    method public void put(long, E);
+    method public void remove(long);
+    method public void removeAt(int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+  public class LruCache<K, V> {
+    ctor public LruCache(int);
+    method protected V create(K);
+    method public final synchronized int createCount();
+    method protected void entryRemoved(boolean, K, V, V);
+    method public final void evictAll();
+    method public final synchronized int evictionCount();
+    method public final V get(K);
+    method public final synchronized int hitCount();
+    method public final synchronized int maxSize();
+    method public final synchronized int missCount();
+    method public final V put(K, V);
+    method public final synchronized int putCount();
+    method public final V remove(K);
+    method public void resize(int);
+    method public final synchronized int size();
+    method protected int sizeOf(K, V);
+    method public final synchronized java.util.Map<K, V> snapshot();
+    method public final synchronized java.lang.String toString();
+    method public void trimToSize(int);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(java.lang.Object, java.lang.Object);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F, S);
+    method public static <A, B> android.support.v4.util.Pair<A, B> create(A, B);
+    field public final F first;
+    field public final S second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static abstract interface Pools.Pool<T> {
+    method public abstract T acquire();
+    method public abstract boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements android.support.v4.util.Pools.Pool {
+    ctor public Pools.SimplePool(int);
+    method public T acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends android.support.v4.util.Pools.SimplePool {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public class SimpleArrayMap<K, V> {
+    ctor public SimpleArrayMap();
+    ctor public SimpleArrayMap(int);
+    ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap<K, V>);
+    method public void clear();
+    method public boolean containsKey(java.lang.Object);
+    method public boolean containsValue(java.lang.Object);
+    method public void ensureCapacity(int);
+    method public V get(java.lang.Object);
+    method public int indexOfKey(java.lang.Object);
+    method public boolean isEmpty();
+    method public K keyAt(int);
+    method public V put(K, V);
+    method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>);
+    method public V remove(java.lang.Object);
+    method public V removeAt(int);
+    method public V setValueAt(int, V);
+    method public int size();
+    method public V valueAt(int);
+  }
+
+  public class SparseArrayCompat<E> {
+    ctor public SparseArrayCompat();
+    ctor public SparseArrayCompat(int);
+    method public void append(int, E);
+    method public void clear();
+    method public android.support.v4.util.SparseArrayCompat<E> clone();
+    method public void delete(int);
+    method public E get(int);
+    method public E get(int, E);
+    method public int indexOfKey(int);
+    method public int indexOfValue(E);
+    method public int keyAt(int);
+    method public void put(int, E);
+    method public void remove(int);
+    method public void removeAt(int);
+    method public void removeAtRange(int, int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+}
+
+package android.support.v4.view {
+
+  public abstract class AbsSavedState implements android.os.Parcelable {
+    ctor protected AbsSavedState(android.os.Parcelable);
+    ctor protected AbsSavedState(android.os.Parcel);
+    ctor protected AbsSavedState(android.os.Parcel, java.lang.ClassLoader);
+    method public int describeContents();
+    method public final android.os.Parcelable getSuperState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.AbsSavedState> CREATOR;
+    field public static final android.support.v4.view.AbsSavedState EMPTY_STATE;
+  }
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener);
+  }
+
+  public static abstract interface ActionProvider.VisibilityListener {
+    method public abstract void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class AsyncLayoutInflater {
+    ctor public AsyncLayoutInflater(android.content.Context);
+    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+  }
+
+  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final deprecated class KeyEventCompat {
+    method public static deprecated boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object);
+    method public static deprecated java.lang.Object getKeyDispatcherState(android.view.View);
+    method public static deprecated boolean hasModifiers(android.view.KeyEvent, int);
+    method public static deprecated boolean hasNoModifiers(android.view.KeyEvent);
+    method public static deprecated boolean isCtrlPressed(android.view.KeyEvent);
+    method public static deprecated boolean isTracking(android.view.KeyEvent);
+    method public static deprecated boolean metaStateHasModifiers(int, int);
+    method public static deprecated boolean metaStateHasNoModifiers(int);
+    method public static deprecated int normalizeMetaState(int);
+    method public static deprecated void startTracking(android.view.KeyEvent);
+  }
+
+  public final class LayoutInflaterCompat {
+    method public static deprecated android.support.v4.view.LayoutInflaterFactory getFactory(android.view.LayoutInflater);
+    method public static deprecated void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  public abstract deprecated interface LayoutInflaterFactory {
+    method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+  }
+
+  public final class MenuItemCompat {
+    method public static deprecated boolean collapseActionView(android.view.MenuItem);
+    method public static deprecated boolean expandActionView(android.view.MenuItem);
+    method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
+    method public static deprecated android.view.View getActionView(android.view.MenuItem);
+    method public static int getAlphabeticModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getContentDescription(android.view.MenuItem);
+    method public static android.content.res.ColorStateList getIconTintList(android.view.MenuItem);
+    method public static android.graphics.PorterDuff.Mode getIconTintMode(android.view.MenuItem);
+    method public static int getNumericModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getTooltipText(android.view.MenuItem);
+    method public static deprecated boolean isActionViewExpanded(android.view.MenuItem);
+    method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+    method public static void setContentDescription(android.view.MenuItem, java.lang.CharSequence);
+    method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList);
+    method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
+    method public static deprecated android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+    method public static void setTooltipText(android.view.MenuItem, java.lang.CharSequence);
+    field public static final deprecated int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final deprecated int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final deprecated int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final deprecated int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final deprecated int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  public static abstract deprecated interface MenuItemCompat.OnActionExpandListener {
+    method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem);
+    method public abstract boolean onMenuItemActionExpand(android.view.MenuItem);
+  }
+
+  public final class MotionEventCompat {
+    method public static deprecated int findPointerIndex(android.view.MotionEvent, int);
+    method public static deprecated int getActionIndex(android.view.MotionEvent);
+    method public static deprecated int getActionMasked(android.view.MotionEvent);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int, int);
+    method public static deprecated int getButtonState(android.view.MotionEvent);
+    method public static deprecated int getPointerCount(android.view.MotionEvent);
+    method public static deprecated int getPointerId(android.view.MotionEvent, int);
+    method public static deprecated int getSource(android.view.MotionEvent);
+    method public static deprecated float getX(android.view.MotionEvent, int);
+    method public static deprecated float getY(android.view.MotionEvent, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field public static final deprecated int ACTION_HOVER_ENTER = 9; // 0x9
+    field public static final deprecated int ACTION_HOVER_EXIT = 10; // 0xa
+    field public static final deprecated int ACTION_HOVER_MOVE = 7; // 0x7
+    field public static final deprecated int ACTION_MASK = 255; // 0xff
+    field public static final deprecated int ACTION_POINTER_DOWN = 5; // 0x5
+    field public static final deprecated int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field public static final deprecated int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field public static final deprecated int ACTION_POINTER_UP = 6; // 0x6
+    field public static final deprecated int ACTION_SCROLL = 8; // 0x8
+    field public static final deprecated int AXIS_BRAKE = 23; // 0x17
+    field public static final deprecated int AXIS_DISTANCE = 24; // 0x18
+    field public static final deprecated int AXIS_GAS = 22; // 0x16
+    field public static final deprecated int AXIS_GENERIC_1 = 32; // 0x20
+    field public static final deprecated int AXIS_GENERIC_10 = 41; // 0x29
+    field public static final deprecated int AXIS_GENERIC_11 = 42; // 0x2a
+    field public static final deprecated int AXIS_GENERIC_12 = 43; // 0x2b
+    field public static final deprecated int AXIS_GENERIC_13 = 44; // 0x2c
+    field public static final deprecated int AXIS_GENERIC_14 = 45; // 0x2d
+    field public static final deprecated int AXIS_GENERIC_15 = 46; // 0x2e
+    field public static final deprecated int AXIS_GENERIC_16 = 47; // 0x2f
+    field public static final deprecated int AXIS_GENERIC_2 = 33; // 0x21
+    field public static final deprecated int AXIS_GENERIC_3 = 34; // 0x22
+    field public static final deprecated int AXIS_GENERIC_4 = 35; // 0x23
+    field public static final deprecated int AXIS_GENERIC_5 = 36; // 0x24
+    field public static final deprecated int AXIS_GENERIC_6 = 37; // 0x25
+    field public static final deprecated int AXIS_GENERIC_7 = 38; // 0x26
+    field public static final deprecated int AXIS_GENERIC_8 = 39; // 0x27
+    field public static final deprecated int AXIS_GENERIC_9 = 40; // 0x28
+    field public static final deprecated int AXIS_HAT_X = 15; // 0xf
+    field public static final deprecated int AXIS_HAT_Y = 16; // 0x10
+    field public static final deprecated int AXIS_HSCROLL = 10; // 0xa
+    field public static final deprecated int AXIS_LTRIGGER = 17; // 0x11
+    field public static final deprecated int AXIS_ORIENTATION = 8; // 0x8
+    field public static final deprecated int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field public static final deprecated int AXIS_RTRIGGER = 18; // 0x12
+    field public static final deprecated int AXIS_RUDDER = 20; // 0x14
+    field public static final deprecated int AXIS_RX = 12; // 0xc
+    field public static final deprecated int AXIS_RY = 13; // 0xd
+    field public static final deprecated int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field public static final deprecated int AXIS_SIZE = 3; // 0x3
+    field public static final deprecated int AXIS_THROTTLE = 19; // 0x13
+    field public static final deprecated int AXIS_TILT = 25; // 0x19
+    field public static final deprecated int AXIS_TOOL_MAJOR = 6; // 0x6
+    field public static final deprecated int AXIS_TOOL_MINOR = 7; // 0x7
+    field public static final deprecated int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field public static final deprecated int AXIS_TOUCH_MINOR = 5; // 0x5
+    field public static final deprecated int AXIS_VSCROLL = 9; // 0x9
+    field public static final deprecated int AXIS_WHEEL = 21; // 0x15
+    field public static final deprecated int AXIS_X = 0; // 0x0
+    field public static final deprecated int AXIS_Y = 1; // 0x1
+    field public static final deprecated int AXIS_Z = 11; // 0xb
+    field public static final deprecated int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public abstract interface NestedScrollingChild {
+    method public abstract boolean dispatchNestedFling(float, float, boolean);
+    method public abstract boolean dispatchNestedPreFling(float, float);
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public abstract boolean hasNestedScrollingParent();
+    method public abstract boolean isNestedScrollingEnabled();
+    method public abstract void setNestedScrollingEnabled(boolean);
+    method public abstract boolean startNestedScroll(int);
+    method public abstract void stopNestedScroll();
+  }
+
+  public abstract interface NestedScrollingChild2 implements android.support.v4.view.NestedScrollingChild {
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public abstract boolean hasNestedScrollingParent(int);
+    method public abstract boolean startNestedScroll(int, int);
+    method public abstract void stopNestedScroll(int);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(int);
+  }
+
+  public abstract interface NestedScrollingParent {
+    method public abstract int getNestedScrollAxes();
+    method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public abstract void onStopNestedScroll(android.view.View);
+  }
+
+  public abstract interface NestedScrollingParent2 implements android.support.v4.view.NestedScrollingParent {
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public abstract void onStopNestedScroll(android.view.View, int);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public abstract interface OnApplyWindowInsetsListener {
+    method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+  }
+
+  public abstract class PagerAdapter {
+    ctor public PagerAdapter();
+    method public void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void destroyItem(android.view.View, int, java.lang.Object);
+    method public void finishUpdate(android.view.ViewGroup);
+    method public deprecated void finishUpdate(android.view.View);
+    method public abstract int getCount();
+    method public int getItemPosition(java.lang.Object);
+    method public java.lang.CharSequence getPageTitle(int);
+    method public float getPageWidth(int);
+    method public java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated java.lang.Object instantiateItem(android.view.View, int);
+    method public abstract boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public void notifyDataSetChanged();
+    method public void registerDataSetObserver(android.database.DataSetObserver);
+    method public void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public android.os.Parcelable saveState();
+    method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object);
+    method public void startUpdate(android.view.ViewGroup);
+    method public deprecated void startUpdate(android.view.View);
+    method public void unregisterDataSetObserver(android.database.DataSetObserver);
+    field public static final int POSITION_NONE = -2; // 0xfffffffe
+    field public static final int POSITION_UNCHANGED = -1; // 0xffffffff
+  }
+
+  public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip {
+    ctor public PagerTabStrip(android.content.Context);
+    ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet);
+    method public boolean getDrawFullUnderline();
+    method public int getTabIndicatorColor();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setDrawFullUnderline(boolean);
+    method public void setTabIndicatorColor(int);
+    method public void setTabIndicatorColorResource(int);
+  }
+
+  public class PagerTitleStrip extends android.view.ViewGroup {
+    ctor public PagerTitleStrip(android.content.Context);
+    ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet);
+    method public int getTextSpacing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setGravity(int);
+    method public void setNonPrimaryAlpha(float);
+    method public void setTextColor(int);
+    method public void setTextSize(int, float);
+    method public void setTextSpacing(int);
+  }
+
+  public final class PointerIconCompat {
+    method public static android.support.v4.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method public static android.support.v4.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static android.support.v4.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static deprecated boolean isQuickScaleEnabled(java.lang.Object);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+    method public static deprecated void setQuickScaleEnabled(java.lang.Object, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+  }
+
+  public abstract interface ScrollingView {
+    method public abstract int computeHorizontalScrollExtent();
+    method public abstract int computeHorizontalScrollOffset();
+    method public abstract int computeHorizontalScrollRange();
+    method public abstract int computeVerticalScrollExtent();
+    method public abstract int computeVerticalScrollOffset();
+    method public abstract int computeVerticalScrollRange();
+  }
+
+  public abstract interface TintableBackgroundView {
+    method public abstract android.content.res.ColorStateList getSupportBackgroundTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode();
+    method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final deprecated class VelocityTrackerCompat {
+    method public static deprecated float getXVelocity(android.view.VelocityTracker, int);
+    method public static deprecated float getYVelocity(android.view.VelocityTracker, int);
+  }
+
+  public class ViewCompat {
+    ctor protected ViewCompat();
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View>, int);
+    method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method public static deprecated boolean canScrollHorizontally(android.view.View, int);
+    method public static deprecated boolean canScrollVertically(android.view.View, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method public static deprecated int combineMeasuredStates(int, int);
+    method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[], int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[], int);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public static deprecated float getAlpha(android.view.View);
+    method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect getClipBounds(android.view.View);
+    method public static android.view.Display getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method public static deprecated int getLayerType(android.view.View);
+    method public static int getLayoutDirection(android.view.View);
+    method public static deprecated android.graphics.Matrix getMatrix(android.view.View);
+    method public static deprecated int getMeasuredHeightAndState(android.view.View);
+    method public static deprecated int getMeasuredState(android.view.View);
+    method public static deprecated int getMeasuredWidthAndState(android.view.View);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static deprecated int getOverScrollMode(android.view.View);
+    method public static int getPaddingEnd(android.view.View);
+    method public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent getParentForAccessibility(android.view.View);
+    method public static deprecated float getPivotX(android.view.View);
+    method public static deprecated float getPivotY(android.view.View);
+    method public static deprecated float getRotation(android.view.View);
+    method public static deprecated float getRotationX(android.view.View);
+    method public static deprecated float getRotationY(android.view.View);
+    method public static deprecated float getScaleX(android.view.View);
+    method public static deprecated float getScaleY(android.view.View);
+    method public static int getScrollIndicators(android.view.View);
+    method public static java.lang.String getTransitionName(android.view.View);
+    method public static deprecated float getTranslationX(android.view.View);
+    method public static deprecated float getTranslationY(android.view.View);
+    method public static float getTranslationZ(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method public static deprecated float getX(android.view.View);
+    method public static deprecated float getY(android.view.View);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method public static deprecated boolean isOpaque(android.view.View);
+    method public static boolean isPaddingRelative(android.view.View);
+    method public static deprecated void jumpDrawablesToCurrentState(android.view.View);
+    method public static android.view.View keyboardNavigationClusterSearch(android.view.View, android.view.View, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static deprecated void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public static deprecated void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, java.lang.Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long);
+    method public static void requestApplyInsets(android.view.View);
+    method public static deprecated int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method public static deprecated void setActivated(android.view.View, boolean);
+    method public static deprecated void setAlpha(android.view.View, float);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode);
+    method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect);
+    method public static void setElevation(android.view.View, float);
+    method public static deprecated void setFitsSystemWindows(android.view.View, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint);
+    method public static deprecated void setLayerType(android.view.View, int, android.graphics.Paint);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener);
+    method public static deprecated void setOverScrollMode(android.view.View, int);
+    method public static void setPaddingRelative(android.view.View, int, int, int, int);
+    method public static deprecated void setPivotX(android.view.View, float);
+    method public static deprecated void setPivotY(android.view.View, float);
+    method public static void setPointerIcon(android.view.View, android.support.v4.view.PointerIconCompat);
+    method public static deprecated void setRotation(android.view.View, float);
+    method public static deprecated void setRotationX(android.view.View, float);
+    method public static deprecated void setRotationY(android.view.View, float);
+    method public static deprecated void setSaveFromParentEnabled(android.view.View, boolean);
+    method public static deprecated void setScaleX(android.view.View, float);
+    method public static deprecated void setScaleY(android.view.View, float);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+    method public static void setTransitionName(android.view.View, java.lang.String);
+    method public static deprecated void setTranslationX(android.view.View, float);
+    method public static deprecated void setTranslationY(android.view.View, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static deprecated void setX(android.view.View, float);
+    method public static deprecated void setY(android.view.View, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static boolean startNestedScroll(android.view.View, int, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final deprecated int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field public static final deprecated int LAYER_TYPE_NONE = 0; // 0x0
+    field public static final deprecated int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field public static final deprecated int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field public static final deprecated int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field public static final deprecated int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field public static final deprecated int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final deprecated int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field public static final deprecated int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field public static final deprecated int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  public final deprecated class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method public static deprecated boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method public static deprecated void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public class ViewPager extends android.view.ViewGroup {
+    ctor public ViewPager(android.content.Context);
+    ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+    method public void addOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public boolean arrowScroll(int);
+    method public boolean beginFakeDrag();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public void clearOnPageChangeListeners();
+    method public void endFakeDrag();
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fakeDragBy(float);
+    method public android.support.v4.view.PagerAdapter getAdapter();
+    method public int getCurrentItem();
+    method public int getOffscreenPageLimit();
+    method public int getPageMargin();
+    method public boolean isFakeDragging();
+    method protected void onLayout(boolean, int, int, int, int);
+    method protected void onPageScrolled(int, float, int);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void removeOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setAdapter(android.support.v4.view.PagerAdapter);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(int);
+    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setPageMargin(int);
+    method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
+    method public void setPageMarginDrawable(int);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer, int);
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewPager.DecorView implements java.lang.annotation.Annotation {
+  }
+
+  public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams {
+    ctor public ViewPager.LayoutParams();
+    ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public int gravity;
+    field public boolean isDecor;
+  }
+
+  public static abstract interface ViewPager.OnAdapterChangeListener {
+    method public abstract void onAdapterChanged(android.support.v4.view.ViewPager, android.support.v4.view.PagerAdapter, android.support.v4.view.PagerAdapter);
+  }
+
+  public static abstract interface ViewPager.OnPageChangeListener {
+    method public abstract void onPageScrollStateChanged(int);
+    method public abstract void onPageScrolled(int, float, int);
+    method public abstract void onPageSelected(int);
+  }
+
+  public static abstract interface ViewPager.PageTransformer {
+    method public abstract void transformPage(android.view.View, float);
+  }
+
+  public static class ViewPager.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public ViewPager.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR;
+  }
+
+  public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public ViewPager.SimpleOnPageChangeListener();
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[], int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View, int);
+    method public static deprecated boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public long getStartDelay();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener);
+    method public void start();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat x(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat y(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat z(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public abstract interface ViewPropertyAnimatorListener {
+    method public abstract void onAnimationCancel(android.view.View);
+    method public abstract void onAnimationEnd(android.view.View);
+    method public abstract void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public abstract interface ViewPropertyAnimatorUpdateListener {
+    method public abstract void onAnimationUpdate(android.view.View);
+  }
+
+  public final class WindowCompat {
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(android.support.v4.view.WindowInsetsCompat);
+    method public android.support.v4.view.WindowInsetsCompat consumeStableInsets();
+    method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public int getStableInsetBottom();
+    method public int getStableInsetLeft();
+    method public int getStableInsetRight();
+    method public int getStableInsetTop();
+    method public int getSystemWindowInsetBottom();
+    method public int getSystemWindowInsetLeft();
+    method public int getSystemWindowInsetRight();
+    method public int getSystemWindowInsetTop();
+    method public boolean hasInsets();
+    method public boolean hasStableInsets();
+    method public boolean hasSystemWindowInsets();
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+  }
+
+}
+
+package android.support.v4.view.accessibility {
+
+  public final class AccessibilityEventCompat {
+    method public static deprecated void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent);
+    method public int getAction(android.view.accessibility.AccessibilityEvent);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int);
+    method public static deprecated int getRecordCount(android.view.accessibility.AccessibilityEvent);
+    method public void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+    method public void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field public static final deprecated int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field public static final deprecated int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field public static final deprecated int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final deprecated int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field public static final deprecated int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method public static deprecated boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  public static abstract deprecated interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method public abstract deprecated void onAccessibilityStateChanged(boolean);
+  }
+
+  public static abstract deprecated class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static abstract interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public abstract void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor public deprecated AccessibilityNodeInfoCompat(java.lang.Object);
+    method public void addAction(int);
+    method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public void addChild(android.view.View);
+    method public void addChild(android.view.View, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList();
+    method public int getActions();
+    method public void getBoundsInParent(android.graphics.Rect);
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getDrawingOrder();
+    method public java.lang.CharSequence getError();
+    method public android.os.Bundle getExtras();
+    method public deprecated java.lang.Object getInfo();
+    method public int getInputType();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public java.lang.CharSequence getPackageName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo();
+    method public java.lang.CharSequence getRoleDescription();
+    method public java.lang.CharSequence getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore();
+    method public java.lang.String getViewIdResourceName();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isVisibleToUser();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public boolean removeChild(android.view.View);
+    method public boolean removeChild(android.view.View, int);
+    method public void setAccessibilityFocused(boolean);
+    method public void setBoundsInParent(android.graphics.Rect);
+    method public void setBoundsInScreen(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(java.lang.Object);
+    method public void setCollectionItemInfo(java.lang.Object);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(java.lang.CharSequence);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View);
+    method public void setLabelFor(android.view.View, int);
+    method public void setLabeledBy(android.view.View);
+    method public void setLabeledBy(android.view.View, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setParent(android.view.View, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat);
+    method public void setRoleDescription(java.lang.CharSequence);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setSource(android.view.View, int);
+    method public void setText(java.lang.CharSequence);
+    method public void setTextSelection(int, int);
+    method public void setTraversalAfter(android.view.View);
+    method public void setTraversalAfter(android.view.View, int);
+    method public void setTraversalBefore(android.view.View);
+    method public void setTraversalBefore(android.view.View, int);
+    method public void setViewIdResourceName(java.lang.String);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo unwrap();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence);
+    method public int getId();
+    method public java.lang.CharSequence getLabel();
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CONTEXT_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_DOWN;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_LEFT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_RIGHT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_TO_POSITION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_UP;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_PROGRESS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_ON_SCREEN;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public boolean isHeading();
+    method public boolean isSelected();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(java.lang.Object);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public java.lang.Object getProvider();
+    method public boolean performAction(int, int, android.os.Bundle);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor public deprecated AccessibilityRecordCompat(java.lang.Object);
+    method public deprecated boolean equals(java.lang.Object);
+    method public deprecated int getAddedCount();
+    method public deprecated java.lang.CharSequence getBeforeText();
+    method public deprecated java.lang.CharSequence getClassName();
+    method public deprecated java.lang.CharSequence getContentDescription();
+    method public deprecated int getCurrentItemIndex();
+    method public deprecated int getFromIndex();
+    method public deprecated java.lang.Object getImpl();
+    method public deprecated int getItemCount();
+    method public deprecated int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+    method public deprecated int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+    method public deprecated android.os.Parcelable getParcelableData();
+    method public deprecated int getRemovedCount();
+    method public deprecated int getScrollX();
+    method public deprecated int getScrollY();
+    method public deprecated android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource();
+    method public deprecated java.util.List<java.lang.CharSequence> getText();
+    method public deprecated int getToIndex();
+    method public deprecated int getWindowId();
+    method public deprecated int hashCode();
+    method public deprecated boolean isChecked();
+    method public deprecated boolean isEnabled();
+    method public deprecated boolean isFullScreen();
+    method public deprecated boolean isPassword();
+    method public deprecated boolean isScrollable();
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain();
+    method public deprecated void recycle();
+    method public deprecated void setAddedCount(int);
+    method public deprecated void setBeforeText(java.lang.CharSequence);
+    method public deprecated void setChecked(boolean);
+    method public deprecated void setClassName(java.lang.CharSequence);
+    method public deprecated void setContentDescription(java.lang.CharSequence);
+    method public deprecated void setCurrentItemIndex(int);
+    method public deprecated void setEnabled(boolean);
+    method public deprecated void setFromIndex(int);
+    method public deprecated void setFullScreen(boolean);
+    method public deprecated void setItemCount(int);
+    method public deprecated void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setParcelableData(android.os.Parcelable);
+    method public deprecated void setPassword(boolean);
+    method public deprecated void setRemovedCount(int);
+    method public deprecated void setScrollX(int);
+    method public deprecated void setScrollY(int);
+    method public deprecated void setScrollable(boolean);
+    method public deprecated void setSource(android.view.View);
+    method public deprecated void setSource(android.view.View, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View, int);
+    method public deprecated void setToIndex(int);
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot();
+    method public java.lang.CharSequence getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package android.support.v4.view.animation {
+
+  public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutLinearInInterpolator();
+  }
+
+  public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutSlowInInterpolator();
+  }
+
+  public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public LinearOutSlowInInterpolator();
+  }
+
+   abstract class LookupTableInterpolator implements android.view.animation.Interpolator {
+    ctor public LookupTableInterpolator(float[]);
+    method public float getInterpolation(float);
+  }
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package android.support.v4.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+    method public abstract void scrollTargetBy(int, int);
+    method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int);
+    method public android.support.v4.widget.AutoScrollHelper setEdgeType(int);
+    method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public class CircularProgressDrawable extends android.graphics.drawable.Drawable {
+    ctor public CircularProgressDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public boolean getArrowEnabled();
+    method public float getArrowHeight();
+    method public float getArrowScale();
+    method public float getArrowWidth();
+    method public int getBackgroundColor();
+    method public float getCenterRadius();
+    method public int[] getColorSchemeColors();
+    method public float getEndTrim();
+    method public int getOpacity();
+    method public float getProgressRotation();
+    method public float getStartTrim();
+    method public android.graphics.Paint.Cap getStrokeCap();
+    method public float getStrokeWidth();
+    method public boolean isRunning();
+    method public void setAlpha(int);
+    method public void setArrowDimensions(float, float);
+    method public void setArrowEnabled(boolean);
+    method public void setArrowScale(float);
+    method public void setBackgroundColor(int);
+    method public void setCenterRadius(float);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setColorSchemeColors(int...);
+    method public void setProgressRotation(float);
+    method public void setStartEndTrim(float, float);
+    method public void setStrokeCap(android.graphics.Paint.Cap);
+    method public void setStrokeWidth(float);
+    method public void setStyle(int);
+    method public void start();
+    method public void stop();
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode);
+  }
+
+  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 void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public abstract class CursorAdapter extends android.widget.BaseAdapter {
+    ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, int);
+    method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursor(android.database.Cursor);
+    method public java.lang.CharSequence convertToString(android.database.Cursor);
+    method public int getCount();
+    method public android.database.Cursor getCursor();
+    method public android.widget.Filter getFilter();
+    method public android.widget.FilterQueryProvider getFilterQueryProvider();
+    method public java.lang.Object getItem(int);
+    method public long getItemId(int);
+    method public android.view.View getView(int, android.view.View, android.view.ViewGroup);
+    method protected deprecated void init(android.content.Context, android.database.Cursor, boolean);
+    method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method protected void onContentChanged();
+    method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence);
+    method public void setFilterQueryProvider(android.widget.FilterQueryProvider);
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+    field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1
+    field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2
+  }
+
+  public class DrawerLayout extends android.view.ViewGroup {
+    ctor public DrawerLayout(android.content.Context);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void closeDrawer(android.view.View);
+    method public void closeDrawer(android.view.View, boolean);
+    method public void closeDrawer(int);
+    method public void closeDrawer(int, boolean);
+    method public void closeDrawers();
+    method public float getDrawerElevation();
+    method public int getDrawerLockMode(int);
+    method public int getDrawerLockMode(android.view.View);
+    method public java.lang.CharSequence getDrawerTitle(int);
+    method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable();
+    method public boolean isDrawerOpen(android.view.View);
+    method public boolean isDrawerOpen(int);
+    method public boolean isDrawerVisible(android.view.View);
+    method public boolean isDrawerVisible(int);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void openDrawer(android.view.View);
+    method public void openDrawer(android.view.View, boolean);
+    method public void openDrawer(int);
+    method public void openDrawer(int, boolean);
+    method public void removeDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerElevation(float);
+    method public deprecated void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerLockMode(int);
+    method public void setDrawerLockMode(int, int);
+    method public void setDrawerLockMode(int, android.view.View);
+    method public void setDrawerShadow(android.graphics.drawable.Drawable, int);
+    method public void setDrawerShadow(int, int);
+    method public void setDrawerTitle(int, java.lang.CharSequence);
+    method public void setScrimColor(int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackground(int);
+    method public void setStatusBarBackgroundColor(int);
+    field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1
+    field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2
+    field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3
+    field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract interface DrawerLayout.DrawerListener {
+    method public abstract void onDrawerClosed(android.view.View);
+    method public abstract void onDrawerOpened(android.view.View);
+    method public abstract void onDrawerSlide(android.view.View, float);
+    method public abstract void onDrawerStateChanged(int);
+  }
+
+  public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout.LayoutParams(int, int);
+    ctor public DrawerLayout.LayoutParams(int, int, int);
+    ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public int gravity;
+  }
+
+  protected static class DrawerLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public DrawerLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public DrawerLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR;
+  }
+
+  public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public DrawerLayout.SimpleDrawerListener();
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+  }
+
+  public final class EdgeEffectCompat {
+    ctor public deprecated EdgeEffectCompat(android.content.Context);
+    method public deprecated boolean draw(android.graphics.Canvas);
+    method public deprecated void finish();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean onAbsorb(int);
+    method public deprecated boolean onPull(float);
+    method public deprecated boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method public deprecated boolean onRelease();
+    method public deprecated void setSize(int, int);
+  }
+
+  public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public ExploreByTouchHelper(android.view.View);
+    method public final boolean clearKeyboardFocusForVirtualView(int);
+    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public final int getAccessibilityFocusedVirtualViewId();
+    method public deprecated int getFocusedVirtualView();
+    method public final int getKeyboardFocusedVirtualViewId();
+    method protected abstract int getVirtualViewAt(float, float);
+    method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
+    method public final void invalidateRoot();
+    method public final void invalidateVirtualView(int);
+    method public final void invalidateVirtualView(int, int);
+    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
+    method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
+    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+    method public final boolean requestKeyboardFocusForVirtualView(int);
+    method public final boolean sendEventForVirtualView(int, int);
+    field public static final int HOST_ID = -1; // 0xffffffff
+    field public static final int INVALID_ID = -2147483648; // 0x80000000
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode);
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static deprecated android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View);
+    method public static android.view.View.OnTouchListener createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild2 android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int);
+    method public boolean arrowScroll(int);
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollTo(int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static abstract interface NestedScrollView.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter {
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor);
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean);
+    ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int);
+    method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public void setDropDownViewResource(int);
+    method public void setViewResource(int);
+  }
+
+  public final deprecated class ScrollerCompat {
+    method public deprecated void abortAnimation();
+    method public deprecated boolean computeScrollOffset();
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context);
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator);
+    method public deprecated void fling(int, int, int, int, int, int, int, int);
+    method public deprecated void fling(int, int, int, int, int, int, int, int, int, int);
+    method public deprecated float getCurrVelocity();
+    method public deprecated int getCurrX();
+    method public deprecated int getCurrY();
+    method public deprecated int getFinalX();
+    method public deprecated int getFinalY();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean isOverScrolled();
+    method public deprecated void notifyHorizontalEdgeReached(int, int, int);
+    method public deprecated void notifyVerticalEdgeReached(int, int, int);
+    method public deprecated boolean springBack(int, int, int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int, int);
+  }
+
+  public final deprecated class SearchViewCompat {
+    method public static deprecated java.lang.CharSequence getQuery(android.view.View);
+    method public static deprecated boolean isIconified(android.view.View);
+    method public static deprecated boolean isQueryRefinementEnabled(android.view.View);
+    method public static deprecated boolean isSubmitButtonEnabled(android.view.View);
+    method public static deprecated android.view.View newSearchView(android.content.Context);
+    method public static deprecated void setIconified(android.view.View, boolean);
+    method public static deprecated void setImeOptions(android.view.View, int);
+    method public static deprecated void setInputType(android.view.View, int);
+    method public static deprecated void setMaxWidth(android.view.View, int);
+    method public static deprecated void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListener);
+    method public static deprecated void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListener);
+    method public static deprecated void setQuery(android.view.View, java.lang.CharSequence, boolean);
+    method public static deprecated void setQueryHint(android.view.View, java.lang.CharSequence);
+    method public static deprecated void setQueryRefinementEnabled(android.view.View, boolean);
+    method public static deprecated void setSearchableInfo(android.view.View, android.content.ComponentName);
+    method public static deprecated void setSubmitButtonEnabled(android.view.View, boolean);
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnCloseListenerCompat implements android.support.v4.widget.SearchViewCompat.OnCloseListener {
+    ctor public SearchViewCompat.OnCloseListenerCompat();
+    method public boolean onClose();
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnQueryTextListenerCompat implements android.support.v4.widget.SearchViewCompat.OnQueryTextListener {
+    ctor public SearchViewCompat.OnQueryTextListenerCompat();
+    method public boolean onQueryTextChange(java.lang.String);
+    method public boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter {
+    ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]);
+    ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int);
+    method public void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]);
+    method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter();
+    method public int getStringConversionColumn();
+    method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder();
+    method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter);
+    method public void setStringConversionColumn(int);
+    method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder);
+    method public void setViewImage(android.widget.ImageView, java.lang.String);
+    method public void setViewText(android.widget.TextView, java.lang.String);
+  }
+
+  public static abstract interface SimpleCursorAdapter.CursorToStringConverter {
+    method public abstract java.lang.CharSequence convertToString(android.database.Cursor);
+  }
+
+  public static abstract interface SimpleCursorAdapter.ViewBinder {
+    method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int);
+  }
+
+  public class SlidingPaneLayout extends android.view.ViewGroup {
+    ctor public SlidingPaneLayout(android.content.Context);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public deprecated boolean canSlide();
+    method public boolean closePane();
+    method public int getCoveredFadeColor();
+    method public int getParallaxDistance();
+    method public int getSliderFadeColor();
+    method public boolean isOpen();
+    method public boolean isSlideable();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public boolean openPane();
+    method public void setCoveredFadeColor(int);
+    method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener);
+    method public void setParallaxDistance(int);
+    method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableLeft(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableRight(android.graphics.drawable.Drawable);
+    method public deprecated void setShadowResource(int);
+    method public void setShadowResourceLeft(int);
+    method public void setShadowResourceRight(int);
+    method public void setSliderFadeColor(int);
+    method public deprecated void smoothSlideClosed();
+    method public deprecated void smoothSlideOpen();
+  }
+
+  public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public SlidingPaneLayout.LayoutParams();
+    ctor public SlidingPaneLayout.LayoutParams(int, int);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public float weight;
+  }
+
+  public static abstract interface SlidingPaneLayout.PanelSlideListener {
+    method public abstract void onPanelClosed(android.view.View);
+    method public abstract void onPanelOpened(android.view.View);
+    method public abstract void onPanelSlide(android.view.View, float);
+  }
+
+  public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener {
+    ctor public SlidingPaneLayout.SimplePanelSlideListener();
+    method public void onPanelClosed(android.view.View);
+    method public void onPanelOpened(android.view.View);
+    method public void onPanelSlide(android.view.View, float);
+  }
+
+  public class Space extends android.view.View {
+    ctor public Space(android.content.Context, android.util.AttributeSet, int);
+    ctor public Space(android.content.Context, android.util.AttributeSet);
+    ctor public Space(android.content.Context);
+  }
+
+  public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent {
+    ctor public SwipeRefreshLayout(android.content.Context);
+    ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet);
+    method public boolean canChildScrollUp();
+    method public int getProgressCircleDiameter();
+    method public int getProgressViewEndOffset();
+    method public int getProgressViewStartOffset();
+    method public boolean isRefreshing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public deprecated void setColorScheme(int...);
+    method public void setColorSchemeColors(int...);
+    method public void setColorSchemeResources(int...);
+    method public void setDistanceToTriggerSync(int);
+    method public void setOnChildScrollUpCallback(android.support.v4.widget.SwipeRefreshLayout.OnChildScrollUpCallback);
+    method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener);
+    method public deprecated void setProgressBackgroundColor(int);
+    method public void setProgressBackgroundColorSchemeColor(int);
+    method public void setProgressBackgroundColorSchemeResource(int);
+    method public void setProgressViewEndTarget(boolean, int);
+    method public void setProgressViewOffset(boolean, int, int);
+    method public void setRefreshing(boolean);
+    method public void setSize(int);
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+    field protected int mFrom;
+    field protected int mOriginalOffsetTop;
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnChildScrollUpCallback {
+    method public abstract boolean canChildScrollUp(android.support.v4.widget.SwipeRefreshLayout, android.view.View);
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnRefreshListener {
+    method public abstract void onRefresh();
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
+    method public static void setTextAppearance(android.widget.TextView, int);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public abstract interface TintableCompoundButton {
+    method public abstract android.content.res.ColorStateList getSupportButtonTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode();
+    method public abstract void setSupportButtonTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public class ViewDragHelper {
+    method public void abort();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int, int);
+    method public void cancel();
+    method public void captureChildView(android.view.View, int);
+    method public boolean checkTouchSlop(int);
+    method public boolean checkTouchSlop(int, int);
+    method public boolean continueSettling(boolean);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback);
+    method public android.view.View findTopChildUnder(int, int);
+    method public void flingCapturedView(int, int, int, int);
+    method public int getActivePointerId();
+    method public android.view.View getCapturedView();
+    method public int getEdgeSize();
+    method public float getMinVelocity();
+    method public int getTouchSlop();
+    method public int getViewDragState();
+    method public boolean isCapturedViewUnder(int, int);
+    method public boolean isEdgeTouched(int);
+    method public boolean isEdgeTouched(int, int);
+    method public boolean isPointerDown(int);
+    method public boolean isViewUnder(android.view.View, int, int);
+    method public void processTouchEvent(android.view.MotionEvent);
+    method public void setEdgeTrackingEnabled(int);
+    method public void setMinVelocity(float);
+    method public boolean settleCapturedViewAt(int, int);
+    method public boolean shouldInterceptTouchEvent(android.view.MotionEvent);
+    method public boolean smoothSlideViewTo(android.view.View, int, int);
+    field public static final int DIRECTION_ALL = 3; // 0x3
+    field public static final int DIRECTION_HORIZONTAL = 1; // 0x1
+    field public static final int DIRECTION_VERTICAL = 2; // 0x2
+    field public static final int EDGE_ALL = 15; // 0xf
+    field public static final int EDGE_BOTTOM = 8; // 0x8
+    field public static final int EDGE_LEFT = 1; // 0x1
+    field public static final int EDGE_RIGHT = 2; // 0x2
+    field public static final int EDGE_TOP = 4; // 0x4
+    field public static final int INVALID_POINTER = -1; // 0xffffffff
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewDragHelper.Callback {
+    ctor public ViewDragHelper.Callback();
+    method public int clampViewPositionHorizontal(android.view.View, int, int);
+    method public int clampViewPositionVertical(android.view.View, int, int);
+    method public int getOrderedChildIndex(int);
+    method public int getViewHorizontalDragRange(android.view.View);
+    method public int getViewVerticalDragRange(android.view.View);
+    method public void onEdgeDragStarted(int, int);
+    method public boolean onEdgeLock(int);
+    method public void onEdgeTouched(int, int);
+    method public void onViewCaptured(android.view.View, int);
+    method public void onViewDragStateChanged(int);
+    method public void onViewPositionChanged(android.view.View, int, int, int, int);
+    method public void onViewReleased(android.view.View, float, float);
+    method public abstract boolean tryCaptureView(android.view.View, int);
+  }
+
+}
+
+package android.support.v7.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, boolean);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int, boolean);
+    method public abstract android.view.View getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method public abstract deprecated int getNavigationItemCount();
+    method public abstract deprecated int getNavigationMode();
+    method public abstract deprecated int getSelectedNavigationIndex();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getSelectedTab();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getTabAt(int);
+    method public abstract deprecated int getTabCount();
+    method public android.content.Context getThemedContext();
+    method public abstract java.lang.CharSequence getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab newTab();
+    method public abstract deprecated void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void removeTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void removeTabAt(int);
+    method public abstract deprecated void selectTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setCustomView(android.view.View, android.support.v7.app.ActionBar.LayoutParams);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(java.lang.CharSequence);
+    method public void setHomeActionContentDescription(int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.support.v7.app.ActionBar.OnNavigationListener);
+    method public abstract void setLogo(int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setNavigationMode(int);
+    method public abstract deprecated void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
+    field public int gravity;
+  }
+
+  public static abstract interface ActionBar.OnMenuVisibilityListener {
+    method public abstract void onMenuVisibilityChanged(boolean);
+  }
+
+  public static abstract deprecated interface ActionBar.OnNavigationListener {
+    method public abstract boolean onNavigationItemSelected(int, long);
+  }
+
+  public static abstract deprecated class ActionBar.Tab {
+    ctor public ActionBar.Tab();
+    method public abstract java.lang.CharSequence getContentDescription();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.graphics.drawable.Drawable getIcon();
+    method public abstract int getPosition();
+    method public abstract java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract void select();
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(android.view.View);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(android.graphics.drawable.Drawable);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setTabListener(android.support.v7.app.ActionBar.TabListener);
+    method public abstract android.support.v7.app.ActionBar.Tab setTag(java.lang.Object);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static abstract deprecated interface ActionBar.TabListener {
+    method public abstract void onTabReselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabSelected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+  }
+
+  public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
+    method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
+    method public void syncState();
+  }
+
+  public static abstract interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.content.Context getActionBarThemedContext();
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract boolean isNavigationVisible();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends android.support.v7.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.widget.Button getButton(int);
+    method public android.widget.ListView getListView();
+    method public void setButton(int, java.lang.CharSequence, android.os.Message);
+    method public void setButton(int, java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public void setCustomTitle(android.view.View);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIconAttribute(int);
+    method public void setMessage(java.lang.CharSequence);
+    method public void setView(android.view.View);
+    method public void setView(android.view.View, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, int);
+    method public android.support.v7.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public android.support.v7.app.AlertDialog.Builder setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setCancelable(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setCursor(android.database.Cursor, android.content.DialogInterface.OnClickListener, java.lang.String);
+    method public android.support.v7.app.AlertDialog.Builder setCustomTitle(android.view.View);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(int);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.support.v7.app.AlertDialog.Builder setIconAttribute(int);
+    method public deprecated android.support.v7.app.AlertDialog.Builder setInverseBackgroundForced(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setItems(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setItems(java.lang.CharSequence[], android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(int);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(int, boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(java.lang.CharSequence[], boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(android.database.Cursor, java.lang.String, java.lang.String, android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(int, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.database.Cursor, int, java.lang.String, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(java.lang.CharSequence[], int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.widget.ListAdapter, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(int);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setView(int);
+    method public android.support.v7.app.AlertDialog.Builder setView(android.view.View);
+    method public android.support.v7.app.AlertDialog show();
+  }
+
+  public class AppCompatActivity extends android.support.v4.app.FragmentActivity implements android.support.v7.app.ActionBarDrawerToggle.DelegateProvider android.support.v7.app.AppCompatCallback android.support.v4.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public android.content.Intent getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method public void onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public deprecated void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public deprecated void setSupportProgress(int);
+    method public deprecated void setSupportProgressBarIndeterminate(boolean);
+    method public deprecated void setSupportProgressBarIndeterminateVisibility(boolean);
+    method public deprecated void setSupportProgressBarVisibility(boolean);
+    method public android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void supportInvalidateOptionsMenu();
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public abstract interface AppCompatCallback {
+    method public abstract void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public abstract void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public abstract android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract boolean applyDayNight();
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
+    method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T findViewById(int);
+    method public static int getDefaultNightMode();
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration);
+    method public abstract void onCreate(android.os.Bundle);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View);
+    method public abstract void setContentView(int);
+    method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context);
+    ctor public AppCompatDialog(android.content.Context, int);
+    ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class MediaRouteActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.app.MediaRouteButton getMediaRouteButton();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public android.support.v7.app.MediaRouteButton onCreateMediaRouteButton();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<android.support.v7.media.MediaRouter.RouteInfo>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends android.support.v7.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSession();
+    method public android.support.v7.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View onCreateMediaControlView(android.os.Bundle);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static android.support.v7.app.MediaRouteDialogFactory getDefault();
+    method public android.support.v7.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends android.support.v4.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public android.support.v7.media.MediaRouter getMediaRouter();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.media.MediaRouter.Callback onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public deprecated class NotificationCompat extends android.support.v4.app.NotificationCompat {
+    ctor public deprecated NotificationCompat();
+    method public static deprecated android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+  }
+
+  public static deprecated class NotificationCompat.Builder extends android.support.v4.app.NotificationCompat.Builder {
+    ctor public deprecated NotificationCompat.Builder(android.content.Context);
+  }
+
+  public static deprecated class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle {
+    ctor public deprecated NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static deprecated class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v4.media.app.NotificationCompat.DecoratedMediaCustomViewStyle {
+    ctor public deprecated NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static deprecated class NotificationCompat.MediaStyle extends android.support.v4.media.app.NotificationCompat.MediaStyle {
+    ctor public deprecated NotificationCompat.MediaStyle();
+    ctor public deprecated NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v7.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+  }
+
+}
+
+package android.support.v7.graphics {
+
+  public final class Palette {
+    method public static android.support.v7.graphics.Palette.Builder from(android.graphics.Bitmap);
+    method public static android.support.v7.graphics.Palette from(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap, int);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, int, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public int getColorForTarget(android.support.v7.graphics.Target, int);
+    method public int getDarkMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
+    method public int getDarkVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
+    method public int getDominantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDominantSwatch();
+    method public int getLightMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
+    method public int getLightVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
+    method public int getMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
+    method public android.support.v7.graphics.Palette.Swatch getSwatchForTarget(android.support.v7.graphics.Target);
+    method public java.util.List<android.support.v7.graphics.Palette.Swatch> getSwatches();
+    method public java.util.List<android.support.v7.graphics.Target> getTargets();
+    method public int getVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
+  }
+
+  public static final class Palette.Builder {
+    ctor public Palette.Builder(android.graphics.Bitmap);
+    ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
+    method public android.support.v7.graphics.Palette.Builder addTarget(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Palette.Builder clearFilters();
+    method public android.support.v7.graphics.Palette.Builder clearRegion();
+    method public android.support.v7.graphics.Palette.Builder clearTargets();
+    method public android.support.v7.graphics.Palette generate();
+    method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
+    method public android.support.v7.graphics.Palette.Builder resizeBitmapArea(int);
+    method public deprecated android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
+    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
+  }
+
+  public static abstract interface Palette.Filter {
+    method public abstract boolean isAllowed(int, float[]);
+  }
+
+  public static abstract interface Palette.PaletteAsyncListener {
+    method public abstract void onGenerated(android.support.v7.graphics.Palette);
+  }
+
+  public static final class Palette.Swatch {
+    ctor public Palette.Swatch(int, int);
+    method public int getBodyTextColor();
+    method public float[] getHsl();
+    method public int getPopulation();
+    method public int getRgb();
+    method public int getTitleTextColor();
+  }
+
+  public final class Target {
+    method public float getLightnessWeight();
+    method public float getMaximumLightness();
+    method public float getMaximumSaturation();
+    method public float getMinimumLightness();
+    method public float getMinimumSaturation();
+    method public float getPopulationWeight();
+    method public float getSaturationWeight();
+    method public float getTargetLightness();
+    method public float getTargetSaturation();
+    method public boolean isExclusive();
+    field public static final android.support.v7.graphics.Target DARK_MUTED;
+    field public static final android.support.v7.graphics.Target DARK_VIBRANT;
+    field public static final android.support.v7.graphics.Target LIGHT_MUTED;
+    field public static final android.support.v7.graphics.Target LIGHT_VIBRANT;
+    field public static final android.support.v7.graphics.Target MUTED;
+    field public static final android.support.v7.graphics.Target VIBRANT;
+  }
+
+  public static final class Target.Builder {
+    ctor public Target.Builder();
+    ctor public Target.Builder(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Target build();
+    method public android.support.v7.graphics.Target.Builder setExclusive(boolean);
+    method public android.support.v7.graphics.Target.Builder setLightnessWeight(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setPopulationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setSaturationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setTargetLightness(float);
+    method public android.support.v7.graphics.Target.Builder setTargetSaturation(float);
+  }
+
+}
+
+package android.support.v7.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package android.support.v7.media {
+
+  public final class MediaControlIntent {
+    field public static final java.lang.String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final java.lang.String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final java.lang.String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final java.lang.String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final java.lang.String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final java.lang.String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final java.lang.String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final java.lang.String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final java.lang.String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final java.lang.String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final java.lang.String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final java.lang.String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final java.lang.String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final java.lang.String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final java.lang.String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final java.lang.String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final java.lang.String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final java.lang.String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final java.lang.String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final java.lang.String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final java.lang.String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final java.lang.String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final java.lang.String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final java.lang.String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final java.lang.String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final java.lang.String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final java.lang.String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final java.lang.String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaItemStatus fromBundle(android.os.Bundle);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final java.lang.String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final java.lang.String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(int);
+    ctor public MediaItemStatus.Builder(android.support.v7.media.MediaItemStatus);
+    method public android.support.v7.media.MediaItemStatus build();
+    method public android.support.v7.media.MediaItemStatus.Builder setContentDuration(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setContentPosition(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public android.support.v7.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static android.support.v7.media.MediaRouteDescriptor fromBundle(android.os.Bundle);
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public deprecated boolean isConnecting();
+    method public boolean isEnabled();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(java.lang.String, java.lang.String);
+    ctor public MediaRouteDescriptor.Builder(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter>);
+    method public android.support.v7.media.MediaRouteDescriptor build();
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method public deprecated android.support.v7.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDescription(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setId(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setName(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(android.support.v7.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteDiscoveryRequest fromBundle(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final android.support.v7.media.MediaRouteProviderDescriptor getDescriptor();
+    method public final android.support.v7.media.MediaRouteDiscoveryRequest getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final android.support.v7.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public android.support.v7.media.MediaRouteProvider.RouteController onCreateRouteController(java.lang.String);
+    method public void onDiscoveryRequestChanged(android.support.v7.media.MediaRouteDiscoveryRequest);
+    method public final void setCallback(android.support.v7.media.MediaRouteProvider.Callback);
+    method public final void setDescriptor(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public final void setDiscoveryRequest(android.support.v7.media.MediaRouteDiscoveryRequest);
+  }
+
+  public static abstract class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(android.support.v7.media.MediaRouteProvider, android.support.v7.media.MediaRouteProviderDescriptor);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+  }
+
+  public static abstract class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteProviderDescriptor fromBundle(android.os.Bundle);
+    method public java.util.List<android.support.v7.media.MediaRouteDescriptor> getRoutes();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoute(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<android.support.v7.media.MediaRouteDescriptor>);
+    method public android.support.v7.media.MediaRouteProviderDescriptor build();
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public android.support.v7.media.MediaRouteProvider getMediaRouteProvider();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.support.v7.media.MediaRouteProvider onCreateMediaRouteProvider();
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(android.support.v7.media.MediaRouteSelector);
+    method public static android.support.v7.media.MediaRouteSelector fromBundle(android.os.Bundle);
+    method public java.util.List<java.lang.String> getControlCategories();
+    method public boolean hasControlCategory(java.lang.String);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter>);
+    field public static final android.support.v7.media.MediaRouteSelector EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String>);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategory(java.lang.String);
+    method public android.support.v7.media.MediaRouteSelector.Builder addSelector(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback);
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
+    method public void addProvider(android.support.v7.media.MediaRouteProvider);
+    method public void addRemoteControlClient(java.lang.Object);
+    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
+    method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
+    method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
+    method public java.util.List<android.support.v7.media.MediaRouter.ProviderInfo> getProviders();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+    method public android.support.v7.media.MediaRouter.RouteInfo getSelectedRoute();
+    method public boolean isRouteAvailable(android.support.v7.media.MediaRouteSelector, int);
+    method public void removeCallback(android.support.v7.media.MediaRouter.Callback);
+    method public void removeProvider(android.support.v7.media.MediaRouteProvider);
+    method public void removeRemoteControlClient(java.lang.Object);
+    method public void selectRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void setMediaSession(java.lang.Object);
+    method public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat);
+    method public void unselect(int);
+    method public android.support.v7.media.MediaRouter.RouteInfo updateSelectedRoute(android.support.v7.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+  }
+
+  public static abstract class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onResult(android.os.Bundle);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+    method public android.support.v7.media.MediaRouteProvider getProviderInstance();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
+    method public android.support.v7.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public boolean isBluetooth();
+    method public boolean isConnecting();
+    method public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method public boolean isSelected();
+    method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
+    method public void requestSetVolume(int);
+    method public void requestUpdateVolume(int);
+    method public void select();
+    method public void sendControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public boolean supportsControlAction(java.lang.String, java.lang.String);
+    method public boolean supportsControlCategory(java.lang.String);
+    method public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaSessionStatus fromBundle(android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(int);
+    ctor public MediaSessionStatus.Builder(android.support.v7.media.MediaSessionStatus);
+    method public android.support.v7.media.MediaSessionStatus build();
+    method public android.support.v7.media.MediaSessionStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public android.support.v7.media.MediaSessionStatus.Builder setSessionState(int);
+    method public android.support.v7.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void enqueue(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public java.lang.String getSessionId();
+    method public void getSessionStatus(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void getStatus(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void play(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void release();
+    method public void remove(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void resume(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void seek(java.lang.String, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void sendMessage(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void setOnMessageReceivedListener(android.support.v7.media.RemotePlaybackClient.OnMessageReceivedListener);
+    method public void setSessionId(java.lang.String);
+    method public void setStatusCallback(android.support.v7.media.RemotePlaybackClient.StatusCallback);
+    method public void startSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void stop(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+  }
+
+  public static abstract class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(java.lang.String, int, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.ItemActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+  }
+
+  public static abstract interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public abstract void onMessageReceived(java.lang.String, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.SessionActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+  public static abstract class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+    method public void onSessionChanged(java.lang.String);
+    method public void onSessionStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+}
+
+package android.support.v7.preference {
+
+  public class CheckBoxPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
+    ctor public CheckBoxPreference(android.content.Context);
+  }
+
+  public abstract class DialogPreference extends android.support.v7.preference.Preference {
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DialogPreference(android.content.Context);
+    method public android.graphics.drawable.Drawable getDialogIcon();
+    method public int getDialogLayoutResource();
+    method public java.lang.CharSequence getDialogMessage();
+    method public java.lang.CharSequence getDialogTitle();
+    method public java.lang.CharSequence getNegativeButtonText();
+    method public java.lang.CharSequence getPositiveButtonText();
+    method public void setDialogIcon(android.graphics.drawable.Drawable);
+    method public void setDialogIcon(int);
+    method public void setDialogLayoutResource(int);
+    method public void setDialogMessage(java.lang.CharSequence);
+    method public void setDialogMessage(int);
+    method public void setDialogTitle(java.lang.CharSequence);
+    method public void setDialogTitle(int);
+    method public void setNegativeButtonText(java.lang.CharSequence);
+    method public void setNegativeButtonText(int);
+    method public void setPositiveButtonText(java.lang.CharSequence);
+    method public void setPositiveButtonText(int);
+  }
+
+  public static abstract interface DialogPreference.TargetFragment {
+    method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+  }
+
+  public class DropDownPreference extends android.support.v7.preference.ListPreference {
+    ctor public DropDownPreference(android.content.Context);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
+    method protected android.widget.ArrayAdapter createAdapter();
+  }
+
+  public class EditTextPreference extends android.support.v7.preference.DialogPreference {
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
+    ctor public EditTextPreference(android.content.Context);
+    method public java.lang.String getText();
+    method public void setText(java.lang.String);
+  }
+
+  public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public EditTextPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.EditTextPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public ListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence getEntry();
+    method public java.lang.CharSequence[] getEntryValues();
+    method public java.lang.String getValue();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValue(java.lang.String);
+    method public void setValueIndex(int);
+  }
+
+  public class ListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public ListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.ListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public MultiSelectListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class Preference implements java.lang.Comparable {
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet);
+    ctor public Preference(android.content.Context);
+    method public boolean callChangeListener(java.lang.Object);
+    method public int compareTo(android.support.v7.preference.Preference);
+    method protected android.support.v7.preference.Preference findPreferenceInHierarchy(java.lang.String);
+    method public android.content.Context getContext();
+    method public java.lang.String getDependency();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getFragment();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.String getKey();
+    method public final int getLayoutResource();
+    method public android.support.v7.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
+    method public android.support.v7.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
+    method public int getOrder();
+    method public android.support.v7.preference.PreferenceGroup getParent();
+    method protected boolean getPersistedBoolean(boolean);
+    method protected float getPersistedFloat(float);
+    method protected int getPersistedInt(int);
+    method protected long getPersistedLong(long);
+    method protected java.lang.String getPersistedString(java.lang.String);
+    method public java.util.Set<java.lang.String> getPersistedStringSet(java.util.Set<java.lang.String>);
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public boolean getShouldDisableView();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public final int getWidgetLayoutResource();
+    method public boolean hasKey();
+    method public boolean isEnabled();
+    method public boolean isIconSpaceReserved();
+    method public boolean isPersistent();
+    method public boolean isSelectable();
+    method public boolean isSingleLineTitle();
+    method public final boolean isVisible();
+    method protected void notifyChanged();
+    method public void notifyDependencyChange(boolean);
+    method protected void notifyHierarchyChanged();
+    method public void onAttached();
+    method protected void onAttachedToHierarchy(android.support.v7.preference.PreferenceManager);
+    method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
+    method protected void onClick();
+    method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
+    method public void onDetached();
+    method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onParentChanged(android.support.v7.preference.Preference, boolean);
+    method protected void onPrepareForRemoval();
+    method protected void onRestoreInstanceState(android.os.Parcelable);
+    method protected android.os.Parcelable onSaveInstanceState();
+    method protected void onSetInitialValue(boolean, java.lang.Object);
+    method public android.os.Bundle peekExtras();
+    method protected boolean persistBoolean(boolean);
+    method protected boolean persistFloat(float);
+    method protected boolean persistInt(int);
+    method protected boolean persistLong(long);
+    method protected boolean persistString(java.lang.String);
+    method public boolean persistStringSet(java.util.Set<java.lang.String>);
+    method public void restoreHierarchyState(android.os.Bundle);
+    method public void saveHierarchyState(android.os.Bundle);
+    method public void setDefaultValue(java.lang.Object);
+    method public void setDependency(java.lang.String);
+    method public void setEnabled(boolean);
+    method public void setFragment(java.lang.String);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIcon(int);
+    method public void setIconSpaceReserved(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setKey(java.lang.String);
+    method public void setLayoutResource(int);
+    method public void setOnPreferenceChangeListener(android.support.v7.preference.Preference.OnPreferenceChangeListener);
+    method public void setOnPreferenceClickListener(android.support.v7.preference.Preference.OnPreferenceClickListener);
+    method public void setOrder(int);
+    method public void setPersistent(boolean);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public void setSelectable(boolean);
+    method public void setShouldDisableView(boolean);
+    method public void setSingleLineTitle(boolean);
+    method public void setSummary(java.lang.CharSequence);
+    method public void setSummary(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitle(int);
+    method public void setViewId(int);
+    method public final void setVisible(boolean);
+    method public void setWidgetLayoutResource(int);
+    method public boolean shouldDisableDependents();
+    method protected boolean shouldPersist();
+    field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
+  }
+
+  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+    ctor public Preference.BaseSavedState(android.os.Parcel);
+    ctor public Preference.BaseSavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.preference.Preference.BaseSavedState> CREATOR;
+  }
+
+  public static abstract interface Preference.OnPreferenceChangeListener {
+    method public abstract boolean onPreferenceChange(android.support.v7.preference.Preference, java.lang.Object);
+  }
+
+  public static abstract interface Preference.OnPreferenceClickListener {
+    method public abstract boolean onPreferenceClick(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceCategory extends android.support.v7.preference.PreferenceGroup {
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
+    ctor public PreferenceCategory(android.content.Context);
+  }
+
+  public abstract class PreferenceDataStore {
+    ctor public PreferenceDataStore();
+    method public boolean getBoolean(java.lang.String, boolean);
+    method public float getFloat(java.lang.String, float);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public java.util.Set<java.lang.String> getStringSet(java.lang.String, java.util.Set<java.lang.String>);
+    method public void putBoolean(java.lang.String, boolean);
+    method public void putFloat(java.lang.String, float);
+    method public void putInt(java.lang.String, int);
+    method public void putLong(java.lang.String, long);
+    method public void putString(java.lang.String, java.lang.String);
+    method public void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
+  }
+
+  public abstract class PreferenceDialogFragmentCompat extends android.support.v4.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragmentCompat();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragmentCompat extends android.support.v4.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragmentCompat();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public abstract class PreferenceGroup extends android.support.v7.preference.Preference {
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
+    method public void addItemFromInflater(android.support.v7.preference.Preference);
+    method public boolean addPreference(android.support.v7.preference.Preference);
+    method protected void dispatchRestoreInstanceState(android.os.Bundle);
+    method protected void dispatchSaveInstanceState(android.os.Bundle);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.support.v7.preference.Preference getPreference(int);
+    method public int getPreferenceCount();
+    method protected boolean isOnSameScreenAsChildren();
+    method public boolean isOrderingAsAdded();
+    method protected boolean onPrepareAddPreference(android.support.v7.preference.Preference);
+    method public void removeAll();
+    method public boolean removePreference(android.support.v7.preference.Preference);
+    method public void setOrderingAsAdded(boolean);
+  }
+
+  public static abstract interface PreferenceGroup.PreferencePositionCallback {
+    method public abstract int getPreferenceAdapterPosition(java.lang.String);
+    method public abstract int getPreferenceAdapterPosition(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceManager {
+    method public android.support.v7.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.content.Context getContext();
+    method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
+    method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
+    method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
+    method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
+    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public int getSharedPreferencesMode();
+    method public java.lang.String getSharedPreferencesName();
+    method public boolean isStorageDefault();
+    method public boolean isStorageDeviceProtected();
+    method public static void setDefaultValues(android.content.Context, int, boolean);
+    method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
+    method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
+    method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
+    method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
+    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
+    method public void setSharedPreferencesMode(int);
+    method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceProtected();
+    method public void showDialog(android.support.v7.preference.Preference);
+    field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
+  }
+
+  public static abstract interface PreferenceManager.OnDisplayPreferenceDialogListener {
+    method public abstract void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceManager.OnNavigateToScreenListener {
+    method public abstract void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+  }
+
+  public static abstract interface PreferenceManager.OnPreferenceTreeClickListener {
+    method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+  }
+
+  public static abstract class PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.PreferenceComparisonCallback();
+    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
+    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
+    method public void setShouldUseGeneratedIds(boolean);
+    method public boolean shouldUseGeneratedIds();
+  }
+
+  public class PreferenceViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
+    method public android.view.View findViewById(int);
+    method public boolean isDividerAllowedAbove();
+    method public boolean isDividerAllowedBelow();
+    method public void setDividerAllowedAbove(boolean);
+    method public void setDividerAllowedBelow(boolean);
+  }
+
+  public class SeekBarPreference extends android.support.v7.preference.Preference {
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SeekBarPreference(android.content.Context);
+    method public int getMax();
+    method public int getMin();
+    method public final int getSeekBarIncrement();
+    method public int getValue();
+    method public boolean isAdjustable();
+    method public void setAdjustable(boolean);
+    method public final void setMax(int);
+    method public void setMin(int);
+    method public final void setSeekBarIncrement(int);
+    method public void setValue(int);
+  }
+
+  public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreferenceCompat(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+  public abstract class TwoStatePreference extends android.support.v7.preference.Preference {
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
+    ctor public TwoStatePreference(android.content.Context);
+    method public boolean getDisableDependentsState();
+    method public java.lang.CharSequence getSummaryOff();
+    method public java.lang.CharSequence getSummaryOn();
+    method public boolean isChecked();
+    method public void setChecked(boolean);
+    method public void setDisableDependentsState(boolean);
+    method public void setSummaryOff(java.lang.CharSequence);
+    method public void setSummaryOff(int);
+    method public void setSummaryOn(java.lang.CharSequence);
+    method public void setSummaryOn(int);
+    method protected void syncSummaryView(android.support.v7.preference.PreferenceViewHolder);
+    field protected boolean mChecked;
+  }
+
+}
+
+package android.support.v7.util {
+
+  public class AsyncListUtil<T> {
+    ctor public AsyncListUtil(java.lang.Class<T>, int, android.support.v7.util.AsyncListUtil.DataCallback<T>, android.support.v7.util.AsyncListUtil.ViewCallback);
+    method public T getItem(int);
+    method public int getItemCount();
+    method public void onRangeChanged();
+    method public void refresh();
+  }
+
+  public static abstract class AsyncListUtil.DataCallback<T> {
+    ctor public AsyncListUtil.DataCallback();
+    method public abstract void fillData(T[], int, int);
+    method public int getMaxCachedTiles();
+    method public void recycleData(T[], int);
+    method public abstract int refreshData();
+  }
+
+  public static abstract class AsyncListUtil.ViewCallback {
+    ctor public AsyncListUtil.ViewCallback();
+    method public void extendRangeInto(int[], int[], int);
+    method public abstract void getItemRangeInto(int[]);
+    method public abstract void onDataRefresh();
+    method public abstract void onItemLoaded(int);
+    field public static final int HINT_SCROLL_ASC = 2; // 0x2
+    field public static final int HINT_SCROLL_DESC = 1; // 0x1
+    field public static final int HINT_SCROLL_NONE = 0; // 0x0
+  }
+
+  public class BatchingListUpdateCallback implements android.support.v7.util.ListUpdateCallback {
+    ctor public BatchingListUpdateCallback(android.support.v7.util.ListUpdateCallback);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int, java.lang.Object);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public class DiffUtil {
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback);
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback, boolean);
+  }
+
+  public static abstract class DiffUtil.Callback {
+    ctor public DiffUtil.Callback();
+    method public abstract boolean areContentsTheSame(int, int);
+    method public abstract boolean areItemsTheSame(int, int);
+    method public java.lang.Object getChangePayload(int, int);
+    method public abstract int getNewListSize();
+    method public abstract int getOldListSize();
+  }
+
+  public static class DiffUtil.DiffResult {
+    method public void dispatchUpdatesTo(android.support.v7.widget.RecyclerView.Adapter);
+    method public void dispatchUpdatesTo(android.support.v7.util.ListUpdateCallback);
+  }
+
+  public abstract interface ListUpdateCallback {
+    method public abstract void onChanged(int, int, java.lang.Object);
+    method public abstract void onInserted(int, int);
+    method public abstract void onMoved(int, int);
+    method public abstract void onRemoved(int, int);
+  }
+
+  public class SortedList<T> {
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>);
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>, int);
+    method public int add(T);
+    method public void addAll(T[], boolean);
+    method public void addAll(T...);
+    method public void addAll(java.util.Collection<T>);
+    method public void beginBatchedUpdates();
+    method public void clear();
+    method public void endBatchedUpdates();
+    method public T get(int) throws java.lang.IndexOutOfBoundsException;
+    method public int indexOf(T);
+    method public void recalculatePositionOfItemAt(int);
+    method public boolean remove(T);
+    method public T removeItemAt(int);
+    method public int size();
+    method public void updateItemAt(int, T);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class SortedList.BatchedCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedList.BatchedCallback(android.support.v7.util.SortedList.Callback<T2>);
+    method public boolean areContentsTheSame(T2, T2);
+    method public boolean areItemsTheSame(T2, T2);
+    method public int compare(T2, T2);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public static abstract class SortedList.Callback<T2> implements java.util.Comparator android.support.v7.util.ListUpdateCallback {
+    ctor public SortedList.Callback();
+    method public abstract boolean areContentsTheSame(T2, T2);
+    method public abstract boolean areItemsTheSame(T2, T2);
+    method public abstract int compare(T2, T2);
+    method public abstract void onChanged(int, int);
+    method public void onChanged(int, int, java.lang.Object);
+  }
+
+}
+
+package android.support.v7.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.view.Menu getMenu();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public void setTag(java.lang.Object);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static abstract interface ActionMode.Callback {
+    method public abstract boolean onActionItemClicked(android.support.v7.view.ActionMode, android.view.MenuItem);
+    method public abstract boolean onCreateActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+    method public abstract void onDestroyActionMode(android.support.v7.view.ActionMode);
+    method public abstract boolean onPrepareActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+  }
+
+  public abstract interface CollapsibleActionView {
+    method public abstract void onActionViewCollapsed();
+    method public abstract void onActionViewExpanded();
+  }
+
+}
+
+package android.support.v7.widget {
+
+  public class ActionMenuView extends android.support.v7.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
+    method protected android.support.v7.widget.ActionMenuView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.ActionMenuView.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.ActionMenuView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.view.Menu getMenu();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(android.support.v7.widget.ActionMenuView.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends android.support.v7.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(android.support.v7.widget.ActionMenuView.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static abstract interface ActionMenuView.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setSupportAllCaps(boolean);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class CardView extends android.widget.FrameLayout {
+    ctor public CardView(android.content.Context);
+    ctor public CardView(android.content.Context, android.util.AttributeSet);
+    ctor public CardView(android.content.Context, android.util.AttributeSet, int);
+    method public android.content.res.ColorStateList getCardBackgroundColor();
+    method public float getCardElevation();
+    method public int getContentPaddingBottom();
+    method public int getContentPaddingLeft();
+    method public int getContentPaddingRight();
+    method public int getContentPaddingTop();
+    method public float getMaxCardElevation();
+    method public boolean getPreventCornerOverlap();
+    method public float getRadius();
+    method public boolean getUseCompatPadding();
+    method public void setCardBackgroundColor(int);
+    method public void setCardBackgroundColor(android.content.res.ColorStateList);
+    method public void setCardElevation(float);
+    method public void setContentPadding(int, int, int, int);
+    method public void setMaxCardElevation(float);
+    method public void setPreventCornerOverlap(boolean);
+    method public void setRadius(float);
+    method public void setUseCompatPadding(boolean);
+  }
+
+  public class DefaultItemAnimator extends android.support.v7.widget.SimpleItemAnimator {
+    ctor public DefaultItemAnimator();
+    method public boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimations();
+    method public boolean isRunning();
+    method public void runPendingAnimations();
+  }
+
+  public class DividerItemDecoration extends android.support.v7.widget.RecyclerView.ItemDecoration {
+    ctor public DividerItemDecoration(android.content.Context, int);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setOrientation(int);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayout(android.content.Context);
+    method protected android.support.v7.widget.GridLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.GridLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.GridLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final android.support.v7.widget.GridLayout.Alignment BASELINE;
+    field public static final android.support.v7.widget.GridLayout.Alignment BOTTOM;
+    field public static final android.support.v7.widget.GridLayout.Alignment CENTER;
+    field public static final android.support.v7.widget.GridLayout.Alignment END;
+    field public static final android.support.v7.widget.GridLayout.Alignment FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final android.support.v7.widget.GridLayout.Alignment LEFT;
+    field public static final android.support.v7.widget.GridLayout.Alignment RIGHT;
+    field public static final android.support.v7.widget.GridLayout.Alignment START;
+    field public static final android.support.v7.widget.GridLayout.Alignment TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.Spec, android.support.v7.widget.GridLayout.Spec);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    method public void setGravity(int);
+    field public android.support.v7.widget.GridLayout.Spec columnSpec;
+    field public android.support.v7.widget.GridLayout.Spec rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public android.support.v7.widget.GridLayout.Alignment getAbsoluteAlignment(boolean);
+  }
+
+  public class GridLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public GridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public GridLayoutManager(android.content.Context, int);
+    ctor public GridLayoutManager(android.content.Context, int, int, boolean);
+    method public int getSpanCount();
+    method public android.support.v7.widget.GridLayoutManager.SpanSizeLookup getSpanSizeLookup();
+    method public void setSpanCount(int);
+    method public void setSpanSizeLookup(android.support.v7.widget.GridLayoutManager.SpanSizeLookup);
+    field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
+  }
+
+  public static final class GridLayoutManager.DefaultSpanSizeLookup extends android.support.v7.widget.GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.DefaultSpanSizeLookup();
+    method public int getSpanSize(int);
+  }
+
+  public static class GridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public GridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayoutManager.LayoutParams(int, int);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getSpanIndex();
+    method public int getSpanSize();
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.SpanSizeLookup();
+    method public int getSpanGroupIndex(int, int);
+    method public int getSpanIndex(int, int);
+    method public abstract int getSpanSize(int);
+    method public void invalidateSpanIndexCache();
+    method public boolean isSpanIndexCacheEnabled();
+    method public void setSpanIndexCacheEnabled(boolean);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.v7.widget.LinearLayoutCompat.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.LinearLayoutCompat.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.LinearLayoutCompat.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.support.v7.widget.LinearLayoutCompat.LayoutParams);
+    field public int gravity;
+    field public float weight;
+  }
+
+  public class LinearLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.helper.ItemTouchHelper.ViewDropHandler android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public LinearLayoutManager(android.content.Context);
+    ctor public LinearLayoutManager(android.content.Context, int, boolean);
+    ctor public LinearLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int findFirstCompletelyVisibleItemPosition();
+    method public int findFirstVisibleItemPosition();
+    method public int findLastCompletelyVisibleItemPosition();
+    method public int findLastVisibleItemPosition();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
+    method public int getInitialPrefetchItemCount();
+    method public int getOrientation();
+    method public boolean getRecycleChildrenOnDetach();
+    method public boolean getReverseLayout();
+    method public boolean getStackFromEnd();
+    method protected boolean isLayoutRTL();
+    method public boolean isSmoothScrollbarEnabled();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setOrientation(int);
+    method public void setRecycleChildrenOnDetach(boolean);
+    method public void setReverseLayout(boolean);
+    method public void setSmoothScrollbarEnabled(boolean);
+    method public void setStackFromEnd(boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  protected static class LinearLayoutManager.LayoutChunkResult {
+    ctor protected LinearLayoutManager.LayoutChunkResult();
+    field public int mConsumed;
+    field public boolean mFinished;
+    field public boolean mFocusable;
+    field public boolean mIgnoreConsumed;
+  }
+
+  public class LinearSmoothScroller extends android.support.v7.widget.RecyclerView.SmoothScroller {
+    ctor public LinearSmoothScroller(android.content.Context);
+    method public int calculateDtToFit(int, int, int, int, int);
+    method public int calculateDxToMakeVisible(android.view.View, int);
+    method public int calculateDyToMakeVisible(android.view.View, int);
+    method protected float calculateSpeedPerPixel(android.util.DisplayMetrics);
+    method protected int calculateTimeForDeceleration(int);
+    method protected int calculateTimeForScrolling(int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method protected int getHorizontalSnapPreference();
+    method protected int getVerticalSnapPreference();
+    method protected void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void onStart();
+    method protected void onStop();
+    method protected void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void updateActionForInterimTarget(android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    field public static final int SNAP_TO_ANY = 0; // 0x0
+    field public static final int SNAP_TO_END = 1; // 0x1
+    field public static final int SNAP_TO_START = -1; // 0xffffffff
+    field protected final android.view.animation.DecelerateInterpolator mDecelerateInterpolator;
+    field protected int mInterimTargetDx;
+    field protected int mInterimTargetDy;
+    field protected final android.view.animation.LinearInterpolator mLinearInterpolator;
+    field protected android.graphics.PointF mTargetVector;
+  }
+
+  public class LinearSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public LinearSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int, int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener createDragToOpenListener(android.view.View);
+    method public void dismiss();
+    method public android.view.View getAnchorView();
+    method public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable getBackground();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView getListView();
+    method public int getPromptPosition();
+    method public java.lang.Object getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter);
+    method public void setAnchorView(android.view.View);
+    method public void setAnimationStyle(int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public abstract class OrientationHelper {
+    method public static android.support.v7.widget.OrientationHelper createHorizontalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public static android.support.v7.widget.OrientationHelper createOrientationHelper(android.support.v7.widget.RecyclerView.LayoutManager, int);
+    method public static android.support.v7.widget.OrientationHelper createVerticalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int getDecoratedEnd(android.view.View);
+    method public abstract int getDecoratedMeasurement(android.view.View);
+    method public abstract int getDecoratedMeasurementInOther(android.view.View);
+    method public abstract int getDecoratedStart(android.view.View);
+    method public abstract int getEnd();
+    method public abstract int getEndAfterPadding();
+    method public abstract int getEndPadding();
+    method public abstract int getMode();
+    method public abstract int getModeInOther();
+    method public abstract int getStartAfterPadding();
+    method public abstract int getTotalSpace();
+    method public int getTotalSpaceChange();
+    method public abstract int getTransformedEndWithDecoration(android.view.View);
+    method public abstract int getTransformedStartWithDecoration(android.view.View);
+    method public abstract void offsetChild(android.view.View, int);
+    method public abstract void offsetChildren(int);
+    method public void onLayoutComplete();
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+    field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
+  }
+
+  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public PagerSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, int, int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(android.support.v7.widget.PopupMenu.OnDismissListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.PopupMenu.OnMenuItemClickListener);
+    method public void show();
+  }
+
+  public static abstract interface PopupMenu.OnDismissListener {
+    method public abstract void onDismiss(android.support.v7.widget.PopupMenu);
+  }
+
+  public static abstract interface PopupMenu.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class RecyclerView extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild2 android.support.v4.view.ScrollingView {
+    ctor public RecyclerView(android.content.Context);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void addOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void addOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void addOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void clearOnChildAttachStateChangeListeners();
+    method public void clearOnScrollListeners();
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
+    method public android.view.View findChildViewUnder(float, float);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
+    method public deprecated android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForPosition(int);
+    method public boolean fling(int, int);
+    method public android.support.v7.widget.RecyclerView.Adapter getAdapter();
+    method public int getChildAdapterPosition(android.view.View);
+    method public long getChildItemId(android.view.View);
+    method public int getChildLayoutPosition(android.view.View);
+    method public deprecated int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder getChildViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate();
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator getItemAnimator();
+    method public android.support.v7.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getMaxFlingVelocity();
+    method public int getMinFlingVelocity();
+    method public android.support.v7.widget.RecyclerView.OnFlingListener getOnFlingListener();
+    method public boolean getPreserveFocusAfterLayout();
+    method public android.support.v7.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
+    method public int getScrollState();
+    method public boolean hasFixedSize();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean hasPendingAdapterUpdates();
+    method public void invalidateItemDecorations();
+    method public boolean isAnimating();
+    method public boolean isComputingLayout();
+    method public boolean isLayoutFrozen();
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onChildAttachedToWindow(android.view.View);
+    method public void onChildDetachedFromWindow(android.view.View);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onScrollStateChanged(int);
+    method public void onScrolled(int, int);
+    method public void removeItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void removeOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void removeOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void removeOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void scrollToPosition(int);
+    method public void setAccessibilityDelegateCompat(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+    method public void setAdapter(android.support.v7.widget.RecyclerView.Adapter);
+    method public void setChildDrawingOrderCallback(android.support.v7.widget.RecyclerView.ChildDrawingOrderCallback);
+    method public void setHasFixedSize(boolean);
+    method public void setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator);
+    method public void setItemViewCacheSize(int);
+    method public void setLayoutFrozen(boolean);
+    method public void setLayoutManager(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public void setOnFlingListener(android.support.v7.widget.RecyclerView.OnFlingListener);
+    method public deprecated void setOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void setPreserveFocusAfterLayout(boolean);
+    method public void setRecycledViewPool(android.support.v7.widget.RecyclerView.RecycledViewPool);
+    method public void setRecyclerListener(android.support.v7.widget.RecyclerView.RecyclerListener);
+    method public void setScrollingTouchSlop(int);
+    method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
+    method public void smoothScrollBy(int, int);
+    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
+    method public void smoothScrollToPosition(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+    method public void stopScroll();
+    method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_TYPE = -1; // 0xffffffff
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+    field public static final int NO_POSITION = -1; // 0xffffffff
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+    field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
+    field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder> {
+    ctor public RecyclerView.Adapter();
+    method public final void bindViewHolder(VH, int);
+    method public final VH createViewHolder(android.view.ViewGroup, int);
+    method public abstract int getItemCount();
+    method public long getItemId(int);
+    method public int getItemViewType(int);
+    method public final boolean hasObservers();
+    method public final boolean hasStableIds();
+    method public final void notifyDataSetChanged();
+    method public final void notifyItemChanged(int);
+    method public final void notifyItemChanged(int, java.lang.Object);
+    method public final void notifyItemInserted(int);
+    method public final void notifyItemMoved(int, int);
+    method public final void notifyItemRangeChanged(int, int);
+    method public final void notifyItemRangeChanged(int, int, java.lang.Object);
+    method public final void notifyItemRangeInserted(int, int);
+    method public final void notifyItemRangeRemoved(int, int);
+    method public final void notifyItemRemoved(int);
+    method public void onAttachedToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public abstract void onBindViewHolder(VH, int);
+    method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object>);
+    method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDetachedFromRecyclerView(android.support.v7.widget.RecyclerView);
+    method public boolean onFailedToRecycleView(VH);
+    method public void onViewAttachedToWindow(VH);
+    method public void onViewDetachedFromWindow(VH);
+    method public void onViewRecycled(VH);
+    method public void registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+    method public void setHasStableIds(boolean);
+    method public void unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+  }
+
+  public static abstract class RecyclerView.AdapterDataObserver {
+    ctor public RecyclerView.AdapterDataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeChanged(int, int, java.lang.Object);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeMoved(int, int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public static abstract interface RecyclerView.ChildDrawingOrderCallback {
+    method public abstract int onGetChildDrawingOrder(int, int);
+  }
+
+  public static abstract class RecyclerView.ItemAnimator {
+    ctor public RecyclerView.ItemAnimator();
+    method public abstract boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object>);
+    method public final void dispatchAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationsFinished();
+    method public abstract void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract void endAnimations();
+    method public long getAddDuration();
+    method public long getChangeDuration();
+    method public long getMoveDuration();
+    method public long getRemoveDuration();
+    method public abstract boolean isRunning();
+    method public final boolean isRunning(android.support.v7.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
+    method public void onAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder, int, java.util.List<java.lang.Object>);
+    method public abstract void runPendingAnimations();
+    method public void setAddDuration(long);
+    method public void setChangeDuration(long);
+    method public void setMoveDuration(long);
+    method public void setRemoveDuration(long);
+    field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
+    field public static final int FLAG_CHANGED = 2; // 0x2
+    field public static final int FLAG_INVALIDATED = 4; // 0x4
+    field public static final int FLAG_MOVED = 2048; // 0x800
+    field public static final int FLAG_REMOVED = 8; // 0x8
+  }
+
+  public static abstract class RecyclerView.ItemAnimator.AdapterChanges implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
+    method public abstract void onAnimationsFinished();
+  }
+
+  public static class RecyclerView.ItemAnimator.ItemHolderInfo {
+    ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public int bottom;
+    field public int changeFlags;
+    field public int left;
+    field public int right;
+    field public int top;
+  }
+
+  public static abstract class RecyclerView.ItemDecoration {
+    ctor public RecyclerView.ItemDecoration();
+    method public deprecated void getItemOffsets(android.graphics.Rect, int, android.support.v7.widget.RecyclerView);
+    method public void getItemOffsets(android.graphics.Rect, android.view.View, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+    method public void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+  }
+
+  public static abstract class RecyclerView.LayoutManager {
+    ctor public RecyclerView.LayoutManager();
+    method public void addDisappearingView(android.view.View);
+    method public void addDisappearingView(android.view.View, int);
+    method public void addView(android.view.View);
+    method public void addView(android.view.View, int);
+    method public void assertInLayoutOrScroll(java.lang.String);
+    method public void assertNotInLayoutOrScroll(java.lang.String);
+    method public void attachView(android.view.View, int, android.support.v7.widget.RecyclerView.LayoutParams);
+    method public void attachView(android.view.View, int);
+    method public void attachView(android.view.View);
+    method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
+    method public boolean canScrollHorizontally();
+    method public boolean canScrollVertically();
+    method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public static int chooseSize(int, int, int);
+    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public void detachAndScrapAttachedViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachView(android.view.View);
+    method public void detachViewAt(int);
+    method public void endAnimation(android.view.View);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.view.View findViewByPosition(int);
+    method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.content.Context, android.util.AttributeSet);
+    method public int getBaseline();
+    method public int getBottomDecorationHeight(android.view.View);
+    method public android.view.View getChildAt(int);
+    method public int getChildCount();
+    method public static deprecated int getChildMeasureSpec(int, int, int, boolean);
+    method public static int getChildMeasureSpec(int, int, int, int, boolean);
+    method public boolean getClipToPadding();
+    method public int getColumnCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getDecoratedBottom(android.view.View);
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public int getDecoratedLeft(android.view.View);
+    method public int getDecoratedMeasuredHeight(android.view.View);
+    method public int getDecoratedMeasuredWidth(android.view.View);
+    method public int getDecoratedRight(android.view.View);
+    method public int getDecoratedTop(android.view.View);
+    method public android.view.View getFocusedChild();
+    method public int getHeight();
+    method public int getHeightMode();
+    method public int getItemCount();
+    method public int getItemViewType(android.view.View);
+    method public int getLayoutDirection();
+    method public int getLeftDecorationWidth(android.view.View);
+    method public int getMinimumHeight();
+    method public int getMinimumWidth();
+    method public int getPaddingBottom();
+    method public int getPaddingEnd();
+    method public int getPaddingLeft();
+    method public int getPaddingRight();
+    method public int getPaddingStart();
+    method public int getPaddingTop();
+    method public int getPosition(android.view.View);
+    method public static android.support.v7.widget.RecyclerView.LayoutManager.Properties getProperties(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getRightDecorationWidth(android.view.View);
+    method public int getRowCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getSelectionModeForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getTopDecorationHeight(android.view.View);
+    method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
+    method public int getWidth();
+    method public int getWidthMode();
+    method public boolean hasFocus();
+    method public void ignoreView(android.view.View);
+    method public boolean isAttachedToWindow();
+    method public boolean isAutoMeasureEnabled();
+    method public boolean isFocused();
+    method public final boolean isItemPrefetchEnabled();
+    method public boolean isLayoutHierarchical(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public boolean isMeasurementCacheEnabled();
+    method public boolean isSmoothScrolling();
+    method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
+    method public void layoutDecorated(android.view.View, int, int, int, int);
+    method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
+    method public void measureChild(android.view.View, int, int);
+    method public void measureChildWithMargins(android.view.View, int, int);
+    method public void moveView(int, int);
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onAdapterChanged(android.support.v7.widget.RecyclerView.Adapter, android.support.v7.widget.RecyclerView.Adapter);
+    method public boolean onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int);
+    method public void onAttachedToWindow(android.support.v7.widget.RecyclerView);
+    method public deprecated void onDetachedFromWindow(android.support.v7.widget.RecyclerView);
+    method public void onDetachedFromWindow(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.Recycler);
+    method public android.view.View onFocusSearchFailed(android.view.View, int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityEvent(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onInitializeAccessibilityNodeInfoForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public android.view.View onInterceptFocusSearch(android.view.View, int);
+    method public void onItemsAdded(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsChanged(android.support.v7.widget.RecyclerView);
+    method public void onItemsMoved(android.support.v7.widget.RecyclerView, int, int, int);
+    method public void onItemsRemoved(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int, java.lang.Object);
+    method public void onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onLayoutCompleted(android.support.v7.widget.RecyclerView.State);
+    method public void onMeasure(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, int);
+    method public deprecated boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.view.View, android.view.View);
+    method public boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, android.view.View, android.view.View);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void onScrollStateChanged(int);
+    method public boolean performAccessibilityAction(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, android.os.Bundle);
+    method public boolean performAccessibilityActionForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, int, android.os.Bundle);
+    method public void postOnAnimation(java.lang.Runnable);
+    method public void removeAllViews();
+    method public void removeAndRecycleAllViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public boolean removeCallbacks(java.lang.Runnable);
+    method public void removeDetachedView(android.view.View);
+    method public void removeView(android.view.View);
+    method public void removeViewAt(int);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
+    method public void requestLayout();
+    method public void requestSimpleAnimationsInNextLayout();
+    method public int scrollHorizontallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void scrollToPosition(int);
+    method public int scrollVerticallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void setAutoMeasureEnabled(boolean);
+    method public final void setItemPrefetchEnabled(boolean);
+    method public void setMeasuredDimension(android.graphics.Rect, int, int);
+    method public void setMeasuredDimension(int, int);
+    method public void setMeasurementCacheEnabled(boolean);
+    method public void smoothScrollToPosition(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, int);
+    method public void startSmoothScroll(android.support.v7.widget.RecyclerView.SmoothScroller);
+    method public void stopIgnoringView(android.view.View);
+    method public boolean supportsPredictiveItemAnimations();
+  }
+
+  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+    method public abstract void addPosition(int, int);
+  }
+
+  public static class RecyclerView.LayoutManager.Properties {
+    ctor public RecyclerView.LayoutManager.Properties();
+    field public int orientation;
+    field public boolean reverseLayout;
+    field public int spanCount;
+    field public boolean stackFromEnd;
+  }
+
+  public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public RecyclerView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView.LayoutParams(int, int);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public RecyclerView.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getViewAdapterPosition();
+    method public int getViewLayoutPosition();
+    method public deprecated int getViewPosition();
+    method public boolean isItemChanged();
+    method public boolean isItemRemoved();
+    method public boolean isViewInvalid();
+    method public boolean viewNeedsUpdate();
+  }
+
+  public static abstract interface RecyclerView.OnChildAttachStateChangeListener {
+    method public abstract void onChildViewAttachedToWindow(android.view.View);
+    method public abstract void onChildViewDetachedFromWindow(android.view.View);
+  }
+
+  public static abstract class RecyclerView.OnFlingListener {
+    ctor public RecyclerView.OnFlingListener();
+    method public abstract boolean onFling(int, int);
+  }
+
+  public static abstract interface RecyclerView.OnItemTouchListener {
+    method public abstract boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public abstract void onRequestDisallowInterceptTouchEvent(boolean);
+    method public abstract void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.OnScrollListener {
+    ctor public RecyclerView.OnScrollListener();
+    method public void onScrollStateChanged(android.support.v7.widget.RecyclerView, int);
+    method public void onScrolled(android.support.v7.widget.RecyclerView, int, int);
+  }
+
+  public static class RecyclerView.RecycledViewPool {
+    ctor public RecyclerView.RecycledViewPool();
+    method public void clear();
+    method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
+    method public int getRecycledViewCount(int);
+    method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setMaxRecycledViews(int, int);
+  }
+
+  public final class RecyclerView.Recycler {
+    ctor public RecyclerView.Recycler();
+    method public void bindViewToPosition(android.view.View, int);
+    method public void clear();
+    method public int convertPreLayoutPositionToPostLayout(int);
+    method public java.util.List<android.support.v7.widget.RecyclerView.ViewHolder> getScrapList();
+    method public android.view.View getViewForPosition(int);
+    method public void recycleView(android.view.View);
+    method public void setViewCacheSize(int);
+  }
+
+  public static abstract interface RecyclerView.RecyclerListener {
+    method public abstract void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+  public static class RecyclerView.SimpleOnItemTouchListener implements android.support.v7.widget.RecyclerView.OnItemTouchListener {
+    ctor public RecyclerView.SimpleOnItemTouchListener();
+    method public boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public void onRequestDisallowInterceptTouchEvent(boolean);
+    method public void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.SmoothScroller {
+    ctor public RecyclerView.SmoothScroller();
+    method public android.view.View findViewByPosition(int);
+    method public int getChildCount();
+    method public int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getTargetPosition();
+    method public deprecated void instantScrollToPosition(int);
+    method public boolean isPendingInitialRun();
+    method public boolean isRunning();
+    method protected void normalize(android.graphics.PointF);
+    method protected void onChildAttachedToWindow(android.view.View);
+    method protected abstract void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected abstract void onStart();
+    method protected abstract void onStop();
+    method protected abstract void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method public void setTargetPosition(int);
+    method protected final void stop();
+  }
+
+  public static class RecyclerView.SmoothScroller.Action {
+    ctor public RecyclerView.SmoothScroller.Action(int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int, android.view.animation.Interpolator);
+    method public int getDuration();
+    method public int getDx();
+    method public int getDy();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public void jumpTo(int);
+    method public void setDuration(int);
+    method public void setDx(int);
+    method public void setDy(int);
+    method public void setInterpolator(android.view.animation.Interpolator);
+    method public void update(int, int, int, android.view.animation.Interpolator);
+    field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+  }
+
+  public static abstract interface RecyclerView.SmoothScroller.ScrollVectorProvider {
+    method public abstract android.graphics.PointF computeScrollVectorForPosition(int);
+  }
+
+  public static class RecyclerView.State {
+    ctor public RecyclerView.State();
+    method public boolean didStructureChange();
+    method public <T> T get(int);
+    method public int getItemCount();
+    method public int getRemainingScrollHorizontal();
+    method public int getRemainingScrollVertical();
+    method public int getTargetScrollPosition();
+    method public boolean hasTargetScrollPosition();
+    method public boolean isMeasuring();
+    method public boolean isPreLayout();
+    method public void put(int, java.lang.Object);
+    method public void remove(int);
+    method public boolean willRunPredictiveAnimations();
+    method public boolean willRunSimpleAnimations();
+  }
+
+  public static abstract class RecyclerView.ViewCacheExtension {
+    ctor public RecyclerView.ViewCacheExtension();
+    method public abstract android.view.View getViewForPositionAndType(android.support.v7.widget.RecyclerView.Recycler, int, int);
+  }
+
+  public static abstract class RecyclerView.ViewHolder {
+    ctor public RecyclerView.ViewHolder(android.view.View);
+    method public final int getAdapterPosition();
+    method public final long getItemId();
+    method public final int getItemViewType();
+    method public final int getLayoutPosition();
+    method public final int getOldPosition();
+    method public final deprecated int getPosition();
+    method public final boolean isRecyclable();
+    method public final void setIsRecyclable(boolean);
+    field public final android.view.View itemView;
+  }
+
+  public class RecyclerViewAccessibilityDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate(android.support.v7.widget.RecyclerView);
+    method public android.support.v4.view.AccessibilityDelegateCompat getItemDelegate();
+  }
+
+  public static class RecyclerViewAccessibilityDelegate.ItemDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate.ItemDelegate(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+  }
+
+  public class SearchView extends android.support.v7.widget.LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public java.lang.CharSequence getQuery();
+    method public java.lang.CharSequence getQueryHint();
+    method public android.support.v4.widget.CursorAdapter getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(android.support.v7.widget.SearchView.OnCloseListener);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener);
+    method public void setOnQueryTextListener(android.support.v7.widget.SearchView.OnQueryTextListener);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener);
+    method public void setOnSuggestionListener(android.support.v7.widget.SearchView.OnSuggestionListener);
+    method public void setQuery(java.lang.CharSequence, boolean);
+    method public void setQueryHint(java.lang.CharSequence);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(android.support.v4.widget.CursorAdapter);
+  }
+
+  public static abstract interface SearchView.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract interface SearchView.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchView.OnSuggestionListener {
+    method public abstract boolean onSuggestionClick(int);
+    method public abstract boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context);
+    method public android.view.View onCreateActionView();
+    method public void setOnShareTargetSelectedListener(android.support.v7.widget.ShareActionProvider.OnShareTargetSelectedListener);
+    method public void setShareHistoryFileName(java.lang.String);
+    method public void setShareIntent(android.content.Intent);
+    field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static abstract interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public abstract boolean onShareTargetSelected(android.support.v7.widget.ShareActionProvider, android.content.Intent);
+  }
+
+  public abstract class SimpleItemAnimator extends android.support.v7.widget.RecyclerView.ItemAnimator {
+    ctor public SimpleItemAnimator();
+    method public abstract boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean getSupportsChangeAnimations();
+    method public void onAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setSupportsChangeAnimations(boolean);
+  }
+
+  public abstract class SnapHelper extends android.support.v7.widget.RecyclerView.OnFlingListener {
+    ctor public SnapHelper();
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
+    method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public int[] calculateScrollDistance(int, int);
+    method protected android.support.v7.widget.RecyclerView.SmoothScroller createScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method protected deprecated android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+    method public boolean onFling(int, int);
+  }
+
+  public class StaggeredGridLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public StaggeredGridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public StaggeredGridLayoutManager(int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int[] findFirstCompletelyVisibleItemPositions(int[]);
+    method public int[] findFirstVisibleItemPositions(int[]);
+    method public int[] findLastCompletelyVisibleItemPositions(int[]);
+    method public int[] findLastVisibleItemPositions(int[]);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public int getGapStrategy();
+    method public int getOrientation();
+    method public boolean getReverseLayout();
+    method public int getSpanCount();
+    method public void invalidateSpanAssignments();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setGapStrategy(int);
+    method public void setOrientation(int);
+    method public void setReverseLayout(boolean);
+    method public void setSpanCount(int);
+    field public static final deprecated int GAP_HANDLING_LAZY = 1; // 0x1
+    field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
+    field public static final int GAP_HANDLING_NONE = 0; // 0x0
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class StaggeredGridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public final int getSpanIndex();
+    method public boolean isFullSpan();
+    method public void setFullSpan(boolean);
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public java.lang.CharSequence getTextOff();
+    method public java.lang.CharSequence getTextOn();
+    method public android.graphics.drawable.Drawable getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context, int);
+    method public void setSwitchTypeface(android.graphics.Typeface, int);
+    method public void setSwitchTypeface(android.graphics.Typeface);
+    method public void setTextOff(java.lang.CharSequence);
+    method public void setTextOn(java.lang.CharSequence);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public abstract interface ThemedSpinnerAdapter implements android.widget.SpinnerAdapter {
+    method public abstract android.content.res.Resources.Theme getDropDownViewTheme();
+    method public abstract void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method protected android.support.v7.widget.Toolbar.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.Toolbar.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.Toolbar.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable getLogo();
+    method public java.lang.CharSequence getLogoDescription();
+    method public android.view.Menu getMenu();
+    method public java.lang.CharSequence getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable getNavigationIcon();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable);
+    method public void setLogoDescription(int);
+    method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationIcon(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.Toolbar.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public void setSubtitle(int);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setSubtitleTextAppearance(android.content.Context, int);
+    method public void setSubtitleTextColor(int);
+    method public void setTitle(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context, int);
+    method public void setTitleTextColor(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends android.support.v7.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(android.support.v7.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
+  }
+
+  public static abstract interface Toolbar.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public static class Toolbar.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel);
+    ctor public Toolbar.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public Toolbar.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.widget.Toolbar.SavedState> CREATOR;
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+  }
+
+}
+
+package android.support.v7.widget.helper {
+
+  public class ItemTouchHelper extends android.support.v7.widget.RecyclerView.ItemDecoration implements android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener {
+    ctor public ItemTouchHelper(android.support.v7.widget.helper.ItemTouchHelper.Callback);
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public void onChildViewAttachedToWindow(android.view.View);
+    method public void onChildViewDetachedFromWindow(android.view.View);
+    method public void startDrag(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void startSwipe(android.support.v7.widget.RecyclerView.ViewHolder);
+    field public static final int ACTION_STATE_DRAG = 2; // 0x2
+    field public static final int ACTION_STATE_IDLE = 0; // 0x0
+    field public static final int ACTION_STATE_SWIPE = 1; // 0x1
+    field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
+    field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
+    field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
+    field public static final int DOWN = 2; // 0x2
+    field public static final int END = 32; // 0x20
+    field public static final int LEFT = 4; // 0x4
+    field public static final int RIGHT = 8; // 0x8
+    field public static final int START = 16; // 0x10
+    field public static final int UP = 1; // 0x1
+  }
+
+  public static abstract class ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.Callback();
+    method public boolean canDropOver(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ViewHolder chooseDropTarget(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<android.support.v7.widget.RecyclerView.ViewHolder>, int, int);
+    method public void clearView(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int convertToAbsoluteDirection(int, int);
+    method public static int convertToRelativeDirection(int, int);
+    method public long getAnimationDuration(android.support.v7.widget.RecyclerView, int, float, float);
+    method public int getBoundingBoxMargin();
+    method public static android.support.v7.widget.helper.ItemTouchUIUtil getDefaultUIUtil();
+    method public float getMoveThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeEscapeVelocity(float);
+    method public float getSwipeThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeVelocityThreshold(float);
+    method public int interpolateOutOfBoundsScroll(android.support.v7.widget.RecyclerView, int, int, int, long);
+    method public boolean isItemViewSwipeEnabled();
+    method public boolean isLongPressDragEnabled();
+    method public static int makeFlag(int, int);
+    method public static int makeMovementFlags(int, int);
+    method public void onChildDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public void onChildDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public abstract boolean onMove(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoved(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int);
+    method public void onSelectedChanged(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method public abstract void onSwiped(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
+    field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
+  }
+
+  public static abstract class ItemTouchHelper.SimpleCallback extends android.support.v7.widget.helper.ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.SimpleCallback(int, int);
+    method public int getDragDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getSwipeDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setDefaultDragDirs(int);
+    method public void setDefaultSwipeDirs(int);
+  }
+
+  public static abstract interface ItemTouchHelper.ViewDropHandler {
+    method public abstract void prepareForDrop(android.view.View, android.view.View, int, int);
+  }
+
+  public abstract interface ItemTouchUIUtil {
+    method public abstract void clearView(android.view.View);
+    method public abstract void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onSelected(android.view.View);
+  }
+
+}
+
+package android.support.v7.widget.util {
+
+  public abstract class SortedListAdapterCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedListAdapterCallback(android.support.v7.widget.RecyclerView.Adapter);
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+}
+
+package android.support.wear.widget {
+
+  public class BoxInsetLayout extends android.view.ViewGroup {
+    ctor public BoxInsetLayout(android.content.Context);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.wear.widget.BoxInsetLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected void onLayout(boolean, int, int, int, int);
+  }
+
+  public static class BoxInsetLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BoxInsetLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout.LayoutParams(int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.support.wear.widget.BoxInsetLayout.LayoutParams);
+    field public static final int BOX_ALL = 15; // 0xf
+    field public static final int BOX_BOTTOM = 8; // 0x8
+    field public static final int BOX_LEFT = 1; // 0x1
+    field public static final int BOX_NONE = 0; // 0x0
+    field public static final int BOX_RIGHT = 4; // 0x4
+    field public static final int BOX_TOP = 2; // 0x2
+    field public int boxedEdges;
+  }
+
+  public class CircularProgressLayout extends android.widget.FrameLayout {
+    ctor public CircularProgressLayout(android.content.Context);
+    ctor public CircularProgressLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CircularProgressLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public CircularProgressLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getBackgroundColor();
+    method public int[] getColorSchemeColors();
+    method public android.support.wear.widget.CircularProgressLayout.OnTimerFinishedListener getOnTimerFinishedListener();
+    method public android.support.v4.widget.CircularProgressDrawable getProgressDrawable();
+    method public float getStartingRotation();
+    method public float getStrokeWidth();
+    method public long getTotalTime();
+    method public boolean isIndeterminate();
+    method public boolean isTimerRunning();
+    method public void setColorSchemeColors(int...);
+    method public void setIndeterminate(boolean);
+    method public void setOnTimerFinishedListener(android.support.wear.widget.CircularProgressLayout.OnTimerFinishedListener);
+    method public void setStartingRotation(float);
+    method public void setStrokeWidth(float);
+    method public void setTotalTime(long);
+    method public void startTimer();
+    method public void stopTimer();
+  }
+
+  public static abstract interface CircularProgressLayout.OnTimerFinishedListener {
+    method public abstract void onTimerFinished(android.support.wear.widget.CircularProgressLayout);
+  }
+
+  public class CurvingLayoutCallback extends android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback {
+    ctor public CurvingLayoutCallback(android.content.Context);
+    method public void adjustAnchorOffsetXY(android.view.View, float[]);
+    method public void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class RoundedDrawable extends android.graphics.drawable.Drawable {
+    ctor public RoundedDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public int getBackgroundColor();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public int getOpacity();
+    method public int getRadius();
+    method public boolean isClipEnabled();
+    method public void setAlpha(int);
+    method public void setBackgroundColor(int);
+    method public void setClipEnabled(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setRadius(int);
+  }
+
+  public class SwipeDismissFrameLayout extends android.widget.FrameLayout {
+    ctor public SwipeDismissFrameLayout(android.content.Context);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+    method public void removeCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+  }
+
+  public static abstract class SwipeDismissFrameLayout.Callback {
+    ctor public SwipeDismissFrameLayout.Callback();
+    method public void onDismissed(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeCanceled(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeStarted(android.support.wear.widget.SwipeDismissFrameLayout);
+  }
+
+  public class WearableLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public WearableLinearLayoutManager(android.content.Context, android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+    ctor public WearableLinearLayoutManager(android.content.Context);
+    method public android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback getLayoutCallback();
+    method public void setLayoutCallback(android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+  }
+
+  public static abstract class WearableLinearLayoutManager.LayoutCallback {
+    ctor public WearableLinearLayoutManager.LayoutCallback();
+    method public abstract void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class WearableRecyclerView extends android.support.v7.widget.RecyclerView {
+    ctor public WearableRecyclerView(android.content.Context);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public float getBezelFraction();
+    method public float getScrollDegreesPerScreen();
+    method public boolean isCircularScrollingGestureEnabled();
+    method public boolean isEdgeItemsCenteringEnabled();
+    method public void setBezelFraction(float);
+    method public void setCircularScrollingGestureEnabled(boolean);
+    method public void setEdgeItemsCenteringEnabled(boolean);
+    method public void setScrollDegreesPerScreen(float);
+  }
+
+}
+
+package android.support.wear.widget.drawer {
+
+  public class WearableActionDrawerView extends android.support.wear.widget.drawer.WearableDrawerView {
+    ctor public WearableActionDrawerView(android.content.Context);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.view.Menu getMenu();
+    method public void setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public class WearableDrawerController {
+    method public void closeDrawer();
+    method public void openDrawer();
+    method public void peekDrawer();
+  }
+
+  public class WearableDrawerLayout extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingParent android.view.View.OnLayoutChangeListener {
+    ctor public WearableDrawerLayout(android.content.Context);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void onFlingComplete(android.view.View);
+    method public void onLayoutChange(android.view.View, int, int, int, int, int, int, int, int);
+    method public void setDrawerStateCallback(android.support.wear.widget.drawer.WearableDrawerLayout.DrawerStateCallback);
+  }
+
+  public static class WearableDrawerLayout.DrawerStateCallback {
+    ctor public WearableDrawerLayout.DrawerStateCallback();
+    method public void onDrawerClosed(android.support.wear.widget.drawer.WearableDrawerLayout, android.support.wear.widget.drawer.WearableDrawerView);
+    method public void onDrawerOpened(android.support.wear.widget.drawer.WearableDrawerLayout, android.support.wear.widget.drawer.WearableDrawerView);
+    method public void onDrawerStateChanged(android.support.wear.widget.drawer.WearableDrawerLayout, int);
+  }
+
+  public class WearableDrawerView extends android.widget.FrameLayout {
+    ctor public WearableDrawerView(android.content.Context);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.support.wear.widget.drawer.WearableDrawerController getController();
+    method public android.view.View getDrawerContent();
+    method public int getDrawerState();
+    method public boolean isAutoPeekEnabled();
+    method public boolean isClosed();
+    method public boolean isLocked();
+    method public boolean isLockedWhenClosed();
+    method public boolean isOpenOnlyAtTopEnabled();
+    method public boolean isOpened();
+    method public boolean isPeekOnScrollDownEnabled();
+    method public boolean isPeeking();
+    method public void onDrawerClosed();
+    method public void onDrawerOpened();
+    method public void onDrawerStateChanged(int);
+    method public void onPeekContainerClicked(android.view.View);
+    method public void setDrawerContent(android.view.View);
+    method public void setIsAutoPeekEnabled(boolean);
+    method public void setIsLocked(boolean);
+    method public void setLockedWhenClosed(boolean);
+    method public void setOpenOnlyAtTopEnabled(boolean);
+    method public void setPeekContent(android.view.View);
+    method public void setPeekOnScrollDownEnabled(boolean);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public class WearableNavigationDrawerView extends android.support.wear.widget.drawer.WearableDrawerView {
+    ctor public WearableNavigationDrawerView(android.content.Context);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addOnItemSelectedListener(android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener);
+    method public int getNavigationStyle();
+    method public void removeOnItemSelectedListener(android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener);
+    method public void setAdapter(android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter);
+    method public void setCurrentItem(int, boolean);
+    field public static final int MULTI_PAGE = 1; // 0x1
+    field public static final int SINGLE_PAGE = 0; // 0x0
+  }
+
+  public static abstract interface WearableNavigationDrawerView.OnItemSelectedListener {
+    method public abstract void onItemSelected(int);
+  }
+
+  public static abstract class WearableNavigationDrawerView.WearableNavigationDrawerAdapter {
+    ctor public WearableNavigationDrawerView.WearableNavigationDrawerAdapter();
+    method public abstract int getCount();
+    method public abstract android.graphics.drawable.Drawable getItemDrawable(int);
+    method public abstract java.lang.CharSequence getItemText(int);
+    method public void notifyDataSetChanged();
+  }
+
+}
+
diff --git a/api/26.0.0-alpha1.txt b/api/26.0.0-alpha1.txt
new file mode 100644
index 0000000..7300fa5
--- /dev/null
+++ b/api/26.0.0-alpha1.txt
@@ -0,0 +1,11757 @@
+package android.support.app.recommendation {
+
+  public final class ContentRecommendation {
+    method public java.lang.String getBackgroundImageUri();
+    method public int getBadgeImageResourceId();
+    method public int getColor();
+    method public android.graphics.Bitmap getContentImage();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
+    method public java.lang.String[] getContentTypes();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getGroup();
+    method public java.lang.String getIdTag();
+    method public java.lang.String getMaturityRating();
+    method public android.app.Notification getNotificationObject(android.content.Context);
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public int getProgressMax();
+    method public int getProgressValue();
+    method public long getRunningTime();
+    method public java.lang.String getSortKey();
+    method public java.lang.String getSourceName();
+    method public int getStatus();
+    method public java.lang.String getText();
+    method public java.lang.String getTitle();
+    method public boolean hasProgressInfo();
+    method public boolean isAutoDismiss();
+    method public void setAutoDismiss(boolean);
+    method public void setGroup(java.lang.String);
+    method public void setProgress(int, int);
+    method public void setSortKey(java.lang.String);
+    method public void setStatus(int);
+    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
+    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
+    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
+    field public static final int CONTENT_STATUS_READY = 0; // 0x0
+    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
+    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
+    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
+    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
+    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
+    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
+    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
+    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
+    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
+    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
+    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
+  }
+
+  public static final class ContentRecommendation.Builder {
+    ctor public ContentRecommendation.Builder();
+    method public android.support.app.recommendation.ContentRecommendation build();
+    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
+  }
+
+  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
+  }
+
+  public static class ContentRecommendation.IntentData {
+    ctor public ContentRecommendation.IntentData();
+  }
+
+  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
+  }
+
+  public final class RecommendationExtender implements android.app.Notification.Extender {
+    ctor public RecommendationExtender();
+    ctor public RecommendationExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String[] getContentTypes();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getMaturityRating();
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public long getRunningTime();
+    method public int getStatus();
+    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
+    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
+  }
+
+}
+
+package android.support.customtabs {
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(java.lang.String, android.os.Bundle);
+    method public void onMessageChannelReady(android.os.Bundle);
+    method public void onNavigationEvent(int, android.os.Bundle);
+    method public void onPostMessage(java.lang.String, android.os.Bundle);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, java.lang.String, android.support.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, java.lang.String);
+    method public android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>, boolean);
+    method public android.support.customtabs.CustomTabsSession newSession(android.support.customtabs.CustomTabsCallback);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context, android.net.Uri);
+    method public static android.content.Intent setAlwaysUseBrowserUI(android.content.Intent);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent);
+    field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final java.lang.String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final java.lang.String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final java.lang.String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final java.lang.String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final java.lang.String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final java.lang.String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
+    method public android.support.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
+    method public deprecated android.support.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, java.lang.String, android.app.PendingIntent) throws java.lang.IllegalStateException;
+    method public android.support.customtabs.CustomTabsIntent build();
+    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setToolbarColor(int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(android.support.customtabs.CustomTabsSessionToken);
+    method protected abstract android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract int postMessage(android.support.customtabs.CustomTabsSessionToken, java.lang.String, android.os.Bundle);
+    method protected abstract boolean requestPostMessageChannel(android.support.customtabs.CustomTabsSessionToken, android.net.Uri);
+    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
+    method protected abstract boolean warmup(long);
+    field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class CustomTabsService.Result implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName, android.support.customtabs.CustomTabsClient);
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+  }
+
+  public final class CustomTabsSession {
+    method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method public int postMessage(java.lang.String, android.os.Bundle);
+    method public boolean requestPostMessageChannel(android.net.Uri);
+    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public deprecated boolean setToolbarItem(int, android.graphics.Bitmap, java.lang.String);
+  }
+
+  public class CustomTabsSessionToken {
+    method public android.support.customtabs.CustomTabsCallback getCallback();
+    method public static android.support.customtabs.CustomTabsSessionToken getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(android.support.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(android.support.customtabs.CustomTabsSessionToken);
+    method public boolean bindSessionToPostMessageService(android.content.Context, java.lang.String);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+    method public final void onServiceDisconnected(android.content.ComponentName);
+    method public final boolean postMessage(java.lang.String, android.os.Bundle);
+    method public void unbindFromContext(android.content.Context);
+  }
+
+}
+
+package android.support.design.widget {
+
+  public class AppBarLayout extends android.widget.LinearLayout {
+    ctor public AppBarLayout(android.content.Context);
+    ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
+    method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public deprecated float getTargetElevation();
+    method public final int getTotalScrollRange();
+    method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public void setExpanded(boolean);
+    method public void setExpanded(boolean, boolean);
+    method public deprecated void setTargetElevation(float);
+  }
+
+  public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
+    ctor public AppBarLayout.Behavior();
+    ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, float, float, boolean);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View);
+    method public void setDragCallback(android.support.design.widget.AppBarLayout.Behavior.DragCallback);
+  }
+
+  public static abstract class AppBarLayout.Behavior.DragCallback {
+    ctor public AppBarLayout.Behavior.DragCallback();
+    method public abstract boolean canDrag(android.support.design.widget.AppBarLayout);
+  }
+
+  protected static class AppBarLayout.Behavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+  }
+
+  public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public AppBarLayout.LayoutParams(int, int);
+    ctor public AppBarLayout.LayoutParams(int, int, float);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.widget.LinearLayout.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.support.design.widget.AppBarLayout.LayoutParams);
+    method public int getScrollFlags();
+    method public android.view.animation.Interpolator getScrollInterpolator();
+    method public void setScrollFlags(int);
+    method public void setScrollInterpolator(android.view.animation.Interpolator);
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS = 4; // 0x4
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
+    field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
+    field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
+    field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
+  }
+
+  public static abstract interface AppBarLayout.OnOffsetChangedListener {
+    method public abstract void onOffsetChanged(android.support.design.widget.AppBarLayout, int);
+  }
+
+  public static class AppBarLayout.ScrollingViewBehavior extends android.support.design.widget.HeaderScrollingViewBehavior {
+    ctor public AppBarLayout.ScrollingViewBehavior();
+    ctor public AppBarLayout.ScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
+  }
+
+  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
+    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
+    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public void dismiss();
+    method public android.content.Context getContext();
+    method public int getDuration();
+    method public android.view.View getView();
+    method public boolean isShown();
+    method public boolean isShownOrQueued();
+    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public B setDuration(int);
+    method public void show();
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
+    ctor public BaseTransientBottomBar.BaseCallback();
+    method public void onDismissed(B, int);
+    method public void onShown(B);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
+    method public abstract void animateContentIn(int, int);
+    method public abstract void animateContentOut(int, int);
+  }
+
+  public class BottomNavigationView extends android.widget.FrameLayout {
+    ctor public BottomNavigationView(android.content.Context);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public int getItemBackgroundResource();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public int getMaxItemCount();
+    method public android.view.Menu getMenu();
+    method public int getSelectedItemId();
+    method public void inflateMenu(int);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setOnNavigationItemReselectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemReselectedListener);
+    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
+    method public void setSelectedItemId(int);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemReselectedListener {
+    method public abstract void onNavigationItemReselected(android.view.MenuItem);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public class BottomSheetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public BottomSheetBehavior();
+    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
+    method public static <V extends android.view.View> android.support.design.widget.BottomSheetBehavior<V> from(V);
+    method public final int getPeekHeight();
+    method public boolean getSkipCollapsed();
+    method public final int getState();
+    method public boolean isHideable();
+    method public void setBottomSheetCallback(android.support.design.widget.BottomSheetBehavior.BottomSheetCallback);
+    method public void setHideable(boolean);
+    method public final void setPeekHeight(int);
+    method public void setSkipCollapsed(boolean);
+    method public final void setState(int);
+    field public static final int PEEK_HEIGHT_AUTO = -1; // 0xffffffff
+    field public static final int STATE_COLLAPSED = 4; // 0x4
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_EXPANDED = 3; // 0x3
+    field public static final int STATE_HIDDEN = 5; // 0x5
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class BottomSheetBehavior.BottomSheetCallback {
+    ctor public BottomSheetBehavior.BottomSheetCallback();
+    method public abstract void onSlide(android.view.View, float);
+    method public abstract void onStateChanged(android.view.View, int);
+  }
+
+  protected static class BottomSheetBehavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
+  }
+
+  public class BottomSheetDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public BottomSheetDialog(android.content.Context);
+    ctor public BottomSheetDialog(android.content.Context, int);
+    ctor protected BottomSheetDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+  }
+
+  public class BottomSheetDialogFragment extends android.support.v7.app.AppCompatDialogFragment {
+    ctor public BottomSheetDialogFragment();
+  }
+
+  public class CollapsingToolbarLayout extends android.widget.FrameLayout {
+    ctor public CollapsingToolbarLayout(android.content.Context);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCollapsedTitleGravity();
+    method public android.graphics.Typeface getCollapsedTitleTypeface();
+    method public android.graphics.drawable.Drawable getContentScrim();
+    method public int getExpandedTitleGravity();
+    method public int getExpandedTitleMarginBottom();
+    method public int getExpandedTitleMarginEnd();
+    method public int getExpandedTitleMarginStart();
+    method public int getExpandedTitleMarginTop();
+    method public android.graphics.Typeface getExpandedTitleTypeface();
+    method public long getScrimAnimationDuration();
+    method public int getScrimVisibleHeightTrigger();
+    method public android.graphics.drawable.Drawable getStatusBarScrim();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isTitleEnabled();
+    method public void setCollapsedTitleGravity(int);
+    method public void setCollapsedTitleTextAppearance(int);
+    method public void setCollapsedTitleTextColor(int);
+    method public void setCollapsedTitleTextColor(android.content.res.ColorStateList);
+    method public void setCollapsedTitleTypeface(android.graphics.Typeface);
+    method public void setContentScrim(android.graphics.drawable.Drawable);
+    method public void setContentScrimColor(int);
+    method public void setContentScrimResource(int);
+    method public void setExpandedTitleColor(int);
+    method public void setExpandedTitleGravity(int);
+    method public void setExpandedTitleMargin(int, int, int, int);
+    method public void setExpandedTitleMarginBottom(int);
+    method public void setExpandedTitleMarginEnd(int);
+    method public void setExpandedTitleMarginStart(int);
+    method public void setExpandedTitleMarginTop(int);
+    method public void setExpandedTitleTextAppearance(int);
+    method public void setExpandedTitleTextColor(android.content.res.ColorStateList);
+    method public void setExpandedTitleTypeface(android.graphics.Typeface);
+    method public void setScrimAnimationDuration(long);
+    method public void setScrimVisibleHeightTrigger(int);
+    method public void setScrimsShown(boolean);
+    method public void setScrimsShown(boolean, boolean);
+    method public void setStatusBarScrim(android.graphics.drawable.Drawable);
+    method public void setStatusBarScrimColor(int);
+    method public void setStatusBarScrimResource(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleEnabled(boolean);
+  }
+
+  public static class CollapsingToolbarLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public CollapsingToolbarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    method public int getCollapseMode();
+    method public float getParallaxMultiplier();
+    method public void setCollapseMode(int);
+    method public void setParallaxMultiplier(float);
+    field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
+    field public static final int COLLAPSE_MODE_PARALLAX = 2; // 0x2
+    field public static final int COLLAPSE_MODE_PIN = 1; // 0x1
+  }
+
+  public class CoordinatorLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingParent {
+    ctor public CoordinatorLayout(android.content.Context);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void dispatchDependentViewsChanged(android.view.View);
+    method public boolean doViewsOverlap(android.view.View, android.view.View);
+    method public java.util.List<android.view.View> getDependencies(android.view.View);
+    method public java.util.List<android.view.View> getDependents(android.view.View);
+    method public android.graphics.drawable.Drawable getStatusBarBackground();
+    method public boolean isPointInChildBounds(android.view.View, int, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onLayoutChild(android.view.View, int);
+    method public void onMeasureChild(android.view.View, int, int, int, int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackgroundColor(int);
+    method public void setStatusBarBackgroundResource(int);
+  }
+
+  public static abstract class CoordinatorLayout.Behavior<V extends android.view.View> {
+    ctor public CoordinatorLayout.Behavior();
+    ctor public CoordinatorLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean blocksInteractionBelow(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect);
+    method public int getScrimColor(android.support.design.widget.CoordinatorLayout, V);
+    method public float getScrimOpacity(android.support.design.widget.CoordinatorLayout, V);
+    method public static java.lang.Object getTag(android.view.View);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.support.design.widget.CoordinatorLayout, V, android.support.v4.view.WindowInsetsCompat);
+    method public void onAttachedToLayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDependentViewRemoved(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDetachedFromLayoutParams();
+    method public boolean onInterceptTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, V, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect, boolean);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public static void setTag(android.view.View, java.lang.Object);
+  }
+
+  public static abstract class CoordinatorLayout.DefaultBehavior implements java.lang.annotation.Annotation {
+  }
+
+  public static class CoordinatorLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public CoordinatorLayout.LayoutParams(int, int);
+    ctor public CoordinatorLayout.LayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAnchorId();
+    method public android.support.design.widget.CoordinatorLayout.Behavior getBehavior();
+    method public void setAnchorId(int);
+    method public void setBehavior(android.support.design.widget.CoordinatorLayout.Behavior);
+    field public int anchorGravity;
+    field public int dodgeInsetEdges;
+    field public int gravity;
+    field public int insetEdge;
+    field public int keyline;
+  }
+
+  protected static class CoordinatorLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public CoordinatorLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+  }
+
+  public class FloatingActionButton extends android.support.design.widget.VisibilityAwareImageButton {
+    ctor public FloatingActionButton(android.content.Context);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+    method public float getCompatElevation();
+    method public android.graphics.drawable.Drawable getContentBackground();
+    method public boolean getContentRect(android.graphics.Rect);
+    method public int getRippleColor();
+    method public int getSize();
+    method public boolean getUseCompatPadding();
+    method public void hide();
+    method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    method public void setCompatElevation(float);
+    method public void setRippleColor(int);
+    method public void setSize(int);
+    method public void setUseCompatPadding(boolean);
+    method public void show();
+    method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    field public static final int SIZE_AUTO = -1; // 0xffffffff
+    field public static final int SIZE_MINI = 1; // 0x1
+    field public static final int SIZE_NORMAL = 0; // 0x0
+  }
+
+  public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public FloatingActionButton.Behavior();
+    ctor public FloatingActionButton.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.graphics.Rect);
+    method public boolean isAutoHideEnabled();
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.view.View);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, int);
+    method public void setAutoHideEnabled(boolean);
+  }
+
+  public static abstract class FloatingActionButton.OnVisibilityChangedListener {
+    ctor public FloatingActionButton.OnVisibilityChangedListener();
+    method public void onHidden(android.support.design.widget.FloatingActionButton);
+    method public void onShown(android.support.design.widget.FloatingActionButton);
+  }
+
+   abstract class HeaderBehavior<V extends android.view.View> extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderBehavior();
+    ctor public HeaderBehavior(android.content.Context, android.util.AttributeSet);
+  }
+
+   abstract class HeaderScrollingViewBehavior extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderScrollingViewBehavior();
+    ctor public HeaderScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public final int getOverlayTop();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
+    method public final void setOverlayTop(int);
+  }
+
+  public class NavigationView extends android.widget.FrameLayout {
+    ctor public NavigationView(android.content.Context);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public void addHeaderView(android.view.View);
+    method public int getHeaderCount();
+    method public android.view.View getHeaderView(int);
+    method public android.graphics.drawable.Drawable getItemBackground();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public android.view.Menu getMenu();
+    method public android.view.View inflateHeaderView(int);
+    method public void inflateMenu(int);
+    method public void removeHeaderView(android.view.View);
+    method public void setCheckedItem(int);
+    method public void setItemBackground(android.graphics.drawable.Drawable);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextAppearance(int);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setNavigationItemSelectedListener(android.support.design.widget.NavigationView.OnNavigationItemSelectedListener);
+  }
+
+  public static abstract interface NavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public static class NavigationView.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public NavigationView.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public NavigationView.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.NavigationView.SavedState> CREATOR;
+    field public android.os.Bundle menuState;
+  }
+
+  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
+    method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
+    method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
+    method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
+    method public android.support.design.widget.Snackbar setActionTextColor(int);
+    method public deprecated android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
+    method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
+    method public android.support.design.widget.Snackbar setText(int);
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
+    ctor public Snackbar.Callback();
+    method public void onDismissed(android.support.design.widget.Snackbar, int);
+    method public void onShown(android.support.design.widget.Snackbar);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public SwipeDismissBehavior();
+    method public boolean canSwipeDismissView(android.view.View);
+    method public int getDragState();
+    method public void setDragDismissDistance(float);
+    method public void setEndAlphaSwipeDistance(float);
+    method public void setListener(android.support.design.widget.SwipeDismissBehavior.OnDismissListener);
+    method public void setSensitivity(float);
+    method public void setStartAlphaSwipeDistance(float);
+    method public void setSwipeDirection(int);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_ANY = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_END_TO_START = 1; // 0x1
+    field public static final int SWIPE_DIRECTION_START_TO_END = 0; // 0x0
+  }
+
+  public static abstract interface SwipeDismissBehavior.OnDismissListener {
+    method public abstract void onDismiss(android.view.View);
+    method public abstract void onDragStateChanged(int);
+  }
+
+  public final class TabItem extends android.view.View {
+    ctor public TabItem(android.content.Context);
+    ctor public TabItem(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class TabLayout extends android.widget.HorizontalScrollView {
+    ctor public TabLayout(android.content.Context);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void addTab(android.support.design.widget.TabLayout.Tab);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
+    method public void clearOnTabSelectedListeners();
+    method public int getSelectedTabPosition();
+    method public android.support.design.widget.TabLayout.Tab getTabAt(int);
+    method public int getTabCount();
+    method public int getTabGravity();
+    method public int getTabMode();
+    method public android.content.res.ColorStateList getTabTextColors();
+    method public android.support.design.widget.TabLayout.Tab newTab();
+    method public void removeAllTabs();
+    method public void removeOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void removeTab(android.support.design.widget.TabLayout.Tab);
+    method public void removeTabAt(int);
+    method public deprecated void setOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void setScrollPosition(int, float, boolean);
+    method public void setSelectedTabIndicatorColor(int);
+    method public void setSelectedTabIndicatorHeight(int);
+    method public void setTabGravity(int);
+    method public void setTabMode(int);
+    method public void setTabTextColors(android.content.res.ColorStateList);
+    method public void setTabTextColors(int, int);
+    method public deprecated void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager, boolean);
+    field public static final int GRAVITY_CENTER = 1; // 0x1
+    field public static final int GRAVITY_FILL = 0; // 0x0
+    field public static final int MODE_FIXED = 1; // 0x1
+    field public static final int MODE_SCROLLABLE = 0; // 0x0
+  }
+
+  public static abstract interface TabLayout.OnTabSelectedListener {
+    method public abstract void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public static final class TabLayout.Tab {
+    method public java.lang.CharSequence getContentDescription();
+    method public android.view.View getCustomView();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public int getPosition();
+    method public java.lang.Object getTag();
+    method public java.lang.CharSequence getText();
+    method public boolean isSelected();
+    method public void select();
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(int);
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(android.view.View);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(int);
+    method public android.support.design.widget.TabLayout.Tab setIcon(android.graphics.drawable.Drawable);
+    method public android.support.design.widget.TabLayout.Tab setIcon(int);
+    method public android.support.design.widget.TabLayout.Tab setTag(java.lang.Object);
+    method public android.support.design.widget.TabLayout.Tab setText(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public class TextInputEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public TextInputEditText(android.content.Context);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class TextInputLayout extends android.widget.LinearLayout {
+    ctor public TextInputLayout(android.content.Context);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCounterMaxLength();
+    method public android.widget.EditText getEditText();
+    method public java.lang.CharSequence getError();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
+    method public android.graphics.Typeface getTypeface();
+    method public boolean isCounterEnabled();
+    method public boolean isErrorEnabled();
+    method public boolean isHintAnimationEnabled();
+    method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void setCounterEnabled(boolean);
+    method public void setCounterMaxLength(int);
+    method public void setError(java.lang.CharSequence);
+    method public void setErrorEnabled(boolean);
+    method public void setErrorTextAppearance(int);
+    method public void setHint(java.lang.CharSequence);
+    method public void setHintAnimationEnabled(boolean);
+    method public void setHintEnabled(boolean);
+    method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTypeface(android.graphics.Typeface);
+  }
+
+   class ViewOffsetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public ViewOffsetBehavior();
+    ctor public ViewOffsetBehavior(android.content.Context, android.util.AttributeSet);
+    method public int getLeftAndRightOffset();
+    method public int getTopAndBottomOffset();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean setLeftAndRightOffset(int);
+    method public boolean setTopAndBottomOffset(int);
+  }
+
+   class VisibilityAwareImageButton extends android.widget.ImageButton {
+    ctor public VisibilityAwareImageButton(android.content.Context);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+}
+
+package android.support.graphics.drawable {
+
+  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public boolean isRunning();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void start();
+    method public void stop();
+  }
+
+   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
+  }
+
+  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
+    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+  }
+
+}
+
+package android.support.media {
+
+  public class ExifInterface {
+    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
+    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
+    method public double getAltitude(double);
+    method public java.lang.String getAttribute(java.lang.String);
+    method public double getAttributeDouble(java.lang.String, double);
+    method public int getAttributeInt(java.lang.String, int);
+    method public deprecated boolean getLatLong(float[]);
+    method public double[] getLatLong();
+    method public byte[] getThumbnail();
+    method public android.graphics.Bitmap getThumbnailBitmap();
+    method public byte[] getThumbnailBytes();
+    method public long[] getThumbnailRange();
+    method public boolean hasThumbnail();
+    method public boolean isThumbnailCompressed();
+    method public void saveAttributes() throws java.io.IOException;
+    method public void setAttribute(java.lang.String, java.lang.String);
+    method public void setLatLong(double, double);
+    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
+    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
+    field public static final int ORIENTATION_NORMAL = 1; // 0x1
+    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
+    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
+    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
+    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
+    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
+    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+    field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+    field public static final java.lang.String TAG_COMPRESSION = "Compression";
+    field public static final java.lang.String TAG_CONTRAST = "Contrast";
+    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
+    field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
+    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
+    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
+    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
+    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
+    field public static final java.lang.String TAG_FLASH = "Flash";
+    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
+    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
+    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
+    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
+    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
+    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
+    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
+    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
+    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
+    field public static final java.lang.String TAG_MAKE = "Make";
+    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
+    field public static final java.lang.String TAG_MODEL = "Model";
+    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
+    field public static final java.lang.String TAG_OECF = "OECF";
+    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
+    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
+    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+    field public static final java.lang.String TAG_RW2_ISO = "ISO";
+    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
+    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+    field public static final java.lang.String TAG_SATURATION = "Saturation";
+    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+    field public static final java.lang.String TAG_SOFTWARE = "Software";
+    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
+    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
+    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
+    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
+    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
+  }
+
+}
+
+package android.support.media.instantvideo.preload {
+
+  public class InstantVideoPreloadManager {
+    method public void clearCache();
+    method public static synchronized android.support.media.instantvideo.preload.InstantVideoPreloadManager getInstance(android.content.Context);
+    method public void preload(android.net.Uri);
+    method public void setMaxCacheSize(int);
+    method public void setMaxPreloadVideoCount(int);
+  }
+
+}
+
+package android.support.media.instantvideo.widget {
+
+  public class InstantVideoView extends android.widget.FrameLayout {
+    ctor public InstantVideoView(android.content.Context);
+    ctor public InstantVideoView(android.content.Context, android.util.AttributeSet);
+    ctor public InstantVideoView(android.content.Context, android.util.AttributeSet, int);
+    method public int getCurrentPosition();
+    method public void seekTo(int);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setVideoUri(android.net.Uri);
+    method public void start();
+    method public void stop();
+  }
+
+}
+
+package android.support.media.tv {
+
+  public final class Channel {
+    method public static android.support.media.tv.Channel fromCursor(android.database.Cursor);
+    method public int getAppLinkColor();
+    method public android.net.Uri getAppLinkIconUri();
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public android.net.Uri getAppLinkPosterArtUri();
+    method public java.lang.String getAppLinkText();
+    method public java.lang.String getChannelLogo();
+    method public java.lang.String getDescription();
+    method public java.lang.String getDisplayName();
+    method public java.lang.String getDisplayNumber();
+    method public long getId();
+    method public java.lang.String getInputId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getNetworkAffiliation();
+    method public int getOriginalNetworkId();
+    method public java.lang.String getPackageName();
+    method public int getServiceId();
+    method public java.lang.String getServiceType();
+    method public int getTransportStreamId();
+    method public java.lang.String getType();
+    method public java.lang.String getVideoFormat();
+    method public boolean isSearchable();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static final class Channel.Builder {
+    ctor public Channel.Builder();
+    ctor public Channel.Builder(android.support.media.tv.Channel);
+    method public android.support.media.tv.Channel build();
+    method public android.support.media.tv.Channel.Builder setAppLinkColor(int);
+    method public android.support.media.tv.Channel.Builder setAppLinkIconUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkPosterArtUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkText(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setChannelLogo(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDescription(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayName(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayNumber(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInputId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.Channel.Builder setNetworkAffiliation(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setOriginalNetworkId(int);
+    method public android.support.media.tv.Channel.Builder setPackageName(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setSearchable(boolean);
+    method public android.support.media.tv.Channel.Builder setServiceId(int);
+    method public android.support.media.tv.Channel.Builder setServiceType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setTransportStreamId(int);
+    method public android.support.media.tv.Channel.Builder setType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
+  }
+
+  public final class Program implements java.lang.Comparable {
+    method public int compareTo(android.support.media.tv.Program);
+    method public static android.support.media.tv.Program fromCursor(android.database.Cursor);
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public java.lang.String[] getAudioLanguages();
+    method public java.lang.String getAuthor();
+    method public java.lang.String getAvailability();
+    method public java.lang.String[] getBroadcastGenres();
+    method public java.lang.String[] getCanonicalGenres();
+    method public long getChannelId();
+    method public android.media.tv.TvContentRating[] getContentRatings();
+    method public java.lang.String getDescription();
+    method public int getDurationMillis();
+    method public long getEndTimeUtcMillis();
+    method public java.lang.String getEpisodeNumber();
+    method public java.lang.String getEpisodeTitle();
+    method public long getId();
+    method public int getInteractionCount();
+    method public java.lang.String getInteractionType();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getInternalProviderId();
+    method public int getItemCount();
+    method public int getLastPlaybackPositionMillis();
+    method public android.net.Uri getLogoUri();
+    method public java.lang.String getLongDescription();
+    method public java.lang.String getOfferPrice();
+    method public java.lang.String getPosterArtAspectRatio();
+    method public android.net.Uri getPosterArtUri();
+    method public android.net.Uri getPreviewVideoUri();
+    method public java.lang.String getReleaseDate();
+    method public java.lang.String getReviewRating();
+    method public java.lang.String getReviewRatingStyle();
+    method public java.lang.String getSeasonNumber();
+    method public java.lang.String getSeasonTitle();
+    method public long getStartTimeUtcMillis();
+    method public java.lang.String getStartingPrice();
+    method public java.lang.String getThumbnailAspectRatio();
+    method public android.net.Uri getThumbnailUri();
+    method public java.lang.String getTitle();
+    method public java.lang.String getType();
+    method public int getVideoHeight();
+    method public int getVideoWidth();
+    method public java.lang.String getWatchNextType();
+    method public int getWeight();
+    method public boolean isLive();
+    method public boolean isRecordingProhibited();
+    method public boolean isSearchable();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static final class Program.Builder {
+    ctor public Program.Builder();
+    ctor public Program.Builder(android.support.media.tv.Program);
+    method public android.support.media.tv.Program build();
+    method public android.support.media.tv.Program.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.Program.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.Program.Builder setAudioLanguages(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setAuthor(java.lang.String);
+    method public android.support.media.tv.Program.Builder setAvailability(java.lang.String);
+    method public android.support.media.tv.Program.Builder setBroadcastGenres(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setCanonicalGenres(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setChannelId(long);
+    method public android.support.media.tv.Program.Builder setContentRatings(android.media.tv.TvContentRating[]);
+    method public android.support.media.tv.Program.Builder setDescription(java.lang.String);
+    method public android.support.media.tv.Program.Builder setDurationMillis(int);
+    method public android.support.media.tv.Program.Builder setEndTimeUtcMillis(long);
+    method public android.support.media.tv.Program.Builder setEpisodeNumber(int);
+    method public android.support.media.tv.Program.Builder setEpisodeNumber(java.lang.String, int);
+    method public android.support.media.tv.Program.Builder setEpisodeTitle(java.lang.String);
+    method public android.support.media.tv.Program.Builder setId(long);
+    method public android.support.media.tv.Program.Builder setInteractionCount(int);
+    method public android.support.media.tv.Program.Builder setInteractionType(java.lang.String);
+    method public android.support.media.tv.Program.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.Program.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.Program.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.Program.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.Program.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.Program.Builder setInternalProviderId(java.lang.String);
+    method public android.support.media.tv.Program.Builder setItemCount(int);
+    method public android.support.media.tv.Program.Builder setLastPlaybackPositionMillis(int);
+    method public android.support.media.tv.Program.Builder setLive(boolean);
+    method public android.support.media.tv.Program.Builder setLogoUri(android.net.Uri);
+    method public android.support.media.tv.Program.Builder setLongDescription(java.lang.String);
+    method public android.support.media.tv.Program.Builder setOfferPrice(java.lang.String);
+    method public android.support.media.tv.Program.Builder setPosterArtAspectRatio(java.lang.String);
+    method public android.support.media.tv.Program.Builder setPosterArtUri(android.net.Uri);
+    method public android.support.media.tv.Program.Builder setPreviewVideoUri(android.net.Uri);
+    method public android.support.media.tv.Program.Builder setRecordingProhibited(boolean);
+    method public android.support.media.tv.Program.Builder setReleaseDate(java.lang.String);
+    method public android.support.media.tv.Program.Builder setReleaseDate(java.util.Date);
+    method public android.support.media.tv.Program.Builder setReviewRating(java.lang.String);
+    method public android.support.media.tv.Program.Builder setReviewRatingStyle(java.lang.String);
+    method public android.support.media.tv.Program.Builder setSearchable(boolean);
+    method public android.support.media.tv.Program.Builder setSeasonNumber(int);
+    method public android.support.media.tv.Program.Builder setSeasonNumber(java.lang.String, int);
+    method public android.support.media.tv.Program.Builder setSeasonTitle(java.lang.String);
+    method public android.support.media.tv.Program.Builder setStartTimeUtcMillis(long);
+    method public android.support.media.tv.Program.Builder setStartingPrice(java.lang.String);
+    method public android.support.media.tv.Program.Builder setThumbnailAspectRatio(java.lang.String);
+    method public android.support.media.tv.Program.Builder setThumbnailUri(android.net.Uri);
+    method public android.support.media.tv.Program.Builder setTitle(java.lang.String);
+    method public android.support.media.tv.Program.Builder setType(java.lang.String);
+    method public android.support.media.tv.Program.Builder setVideoHeight(int);
+    method public android.support.media.tv.Program.Builder setVideoWidth(int);
+    method public android.support.media.tv.Program.Builder setWatchNextType(java.lang.String);
+    method public android.support.media.tv.Program.Builder setWeight(int);
+  }
+
+  public final class TvContractCompat {
+    method public static android.net.Uri buildChannelLogoUri(long);
+    method public static android.net.Uri buildChannelLogoUri(android.net.Uri);
+    method public static android.net.Uri buildChannelUri(long);
+    method public static android.net.Uri buildChannelUriForPassthroughInput(java.lang.String);
+    method public static android.net.Uri buildChannelsUriForInput(java.lang.String);
+    method public static java.lang.String buildInputId(android.content.ComponentName);
+    method public static android.net.Uri buildProgramUri(long);
+    method public static android.net.Uri buildProgramsUriForChannel(long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramsUriForChannel(long, long, long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static android.net.Uri buildRecordedProgramUri(long);
+    method public static boolean isChannelUri(android.net.Uri);
+    method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
+    method public static boolean isChannelUriForTunerInput(android.net.Uri);
+    method public static boolean isProgramUri(android.net.Uri);
+    field public static final java.lang.String AUTHORITY = "android.media.tv";
+  }
+
+  public static abstract interface TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
+  }
+
+  public static final class TvContractCompat.Channels implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    method public static java.lang.String getVideoResolution(java.lang.String);
+    field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
+    field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text";
+    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
+    field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
+    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
+    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+    field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+    field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+    field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+    field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+    field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+    field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+    field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+    field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+    field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+    field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+    field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+    field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+    field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+    field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+    field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+    field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+    field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+    field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+    field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+    field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+    field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+    field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+    field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
+    field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+    field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+    field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+  }
+
+  public static final class TvContractCompat.Channels.Logo {
+    field public static final java.lang.String CONTENT_DIRECTORY = "logo";
+  }
+
+  public static final class TvContractCompat.Programs implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9";
+    field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1";
+    field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3";
+    field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2";
+    field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE";
+    field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION";
+    field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT";
+    field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+    field public static final java.lang.String COLUMN_WEIGHT = "weight";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS";
+    field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS";
+    field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES";
+    field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS";
+    field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
+    field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN";
+    field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM";
+    field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST";
+    field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL";
+    field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP";
+    field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT";
+    field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE";
+    field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST";
+    field public static final java.lang.String TYPE_STATION = "TYPE_STATION";
+    field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK";
+    field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE";
+    field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON";
+    field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES";
+    field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE";
+    field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW";
+    field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT";
+  }
+
+  public static final class TvContractCompat.Programs.Genres {
+    method public static java.lang.String[] decode(java.lang.String);
+    method public static java.lang.String encode(java.lang.String...);
+    method public static boolean isCanonical(java.lang.String);
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String ARTS = "ARTS";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String ENTERTAINMENT = "ENTERTAINMENT";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String LIFE_STYLE = "LIFE_STYLE";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String MUSIC = "MUSIC";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String PREMIER = "PREMIER";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TECH_SCIENCE = "TECH_SCIENCE";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
+  }
+
+  public static final class TvContractCompat.RecordedPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
+}
+
+package android.support.percent {
+
+  public class PercentFrameLayout extends android.widget.FrameLayout {
+    ctor public PercentFrameLayout(android.content.Context);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout.LayoutParams(int, int);
+    ctor public PercentFrameLayout.LayoutParams(int, int, int);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public class PercentLayoutHelper {
+    ctor public PercentLayoutHelper(android.view.ViewGroup);
+    method public void adjustChildren(int, int);
+    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+    method public boolean handleMeasuredStateTooSmall();
+    method public void restoreOriginalParams();
+  }
+
+  public static class PercentLayoutHelper.PercentLayoutInfo {
+    ctor public PercentLayoutHelper.PercentLayoutInfo();
+    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+    method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void fillMarginLayoutParams(android.view.View, android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public float aspectRatio;
+    field public float bottomMarginPercent;
+    field public float endMarginPercent;
+    field public float heightPercent;
+    field public float leftMarginPercent;
+    field public float rightMarginPercent;
+    field public float startMarginPercent;
+    field public float topMarginPercent;
+    field public float widthPercent;
+  }
+
+  public static abstract interface PercentLayoutHelper.PercentLayoutParams {
+    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public class PercentRelativeLayout extends android.widget.RelativeLayout {
+    ctor public PercentRelativeLayout(android.content.Context);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout.LayoutParams(int, int);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+}
+
+package android.support.transition {
+
+  public class AutoTransition extends android.support.transition.TransitionSet {
+    ctor public AutoTransition();
+  }
+
+  public class ChangeBounds extends android.support.transition.Transition {
+    ctor public ChangeBounds();
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public void setResizeClip(boolean);
+  }
+
+  public class Fade extends android.support.transition.Visibility {
+    ctor public Fade(int);
+    ctor public Fade();
+    field public static final int IN = 1; // 0x1
+    field public static final int OUT = 2; // 0x2
+  }
+
+  public class Scene {
+    ctor public Scene(android.view.ViewGroup);
+    ctor public Scene(android.view.ViewGroup, android.view.View);
+    method public void enter();
+    method public void exit();
+    method public static android.support.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
+    method public android.view.ViewGroup getSceneRoot();
+    method public void setEnterAction(java.lang.Runnable);
+    method public void setExitAction(java.lang.Runnable);
+  }
+
+  public abstract class Transition {
+    ctor public Transition();
+    method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition addTarget(android.view.View);
+    method public android.support.transition.Transition addTarget(int);
+    method public android.support.transition.Transition addTarget(java.lang.String);
+    method public android.support.transition.Transition addTarget(java.lang.Class);
+    method public abstract void captureEndValues(android.support.transition.TransitionValues);
+    method public abstract void captureStartValues(android.support.transition.TransitionValues);
+    method public android.support.transition.Transition clone();
+    method public android.animation.Animator createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition excludeChildren(android.view.View, boolean);
+    method public android.support.transition.Transition excludeChildren(int, boolean);
+    method public android.support.transition.Transition excludeChildren(java.lang.Class, boolean);
+    method public android.support.transition.Transition excludeTarget(android.view.View, boolean);
+    method public android.support.transition.Transition excludeTarget(int, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
+    method public long getDuration();
+    method public android.animation.TimeInterpolator getInterpolator();
+    method public java.lang.String getName();
+    method public long getStartDelay();
+    method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.String> getTargetNames();
+    method public java.util.List<java.lang.Class> getTargetTypes();
+    method public java.util.List<android.view.View> getTargets();
+    method public java.lang.String[] getTransitionProperties();
+    method public android.support.transition.TransitionValues getTransitionValues(android.view.View, boolean);
+    method public android.support.transition.Transition removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition removeTarget(android.view.View);
+    method public android.support.transition.Transition removeTarget(int);
+    method public android.support.transition.Transition removeTarget(java.lang.String);
+    method public android.support.transition.Transition removeTarget(java.lang.Class);
+    method public android.support.transition.Transition setDuration(long);
+    method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
+    method public void setMatchOrder(int...);
+    method public android.support.transition.Transition setStartDelay(long);
+    field public static final int MATCH_ID = 3; // 0x3
+    field public static final int MATCH_INSTANCE = 1; // 0x1
+    field public static final int MATCH_ITEM_ID = 4; // 0x4
+    field public static final int MATCH_NAME = 2; // 0x2
+  }
+
+  public static abstract interface Transition.TransitionListener {
+    method public abstract void onTransitionCancel(android.support.transition.Transition);
+    method public abstract void onTransitionEnd(android.support.transition.Transition);
+    method public abstract void onTransitionPause(android.support.transition.Transition);
+    method public abstract void onTransitionResume(android.support.transition.Transition);
+    method public abstract void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionManager {
+    ctor public TransitionManager();
+    method public static void beginDelayedTransition(android.view.ViewGroup);
+    method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
+    method public static void go(android.support.transition.Scene);
+    method public static void go(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Scene, android.support.transition.Transition);
+    method public void transitionTo(android.support.transition.Scene);
+  }
+
+  public class TransitionSet extends android.support.transition.Transition {
+    ctor public TransitionSet();
+    method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getOrdering();
+    method public android.support.transition.Transition getTransitionAt(int);
+    method public int getTransitionCount();
+    method public android.support.transition.TransitionSet removeTransition(android.support.transition.Transition);
+    method public android.support.transition.TransitionSet setOrdering(int);
+    field public static final int ORDERING_SEQUENTIAL = 1; // 0x1
+    field public static final int ORDERING_TOGETHER = 0; // 0x0
+  }
+
+  public class TransitionValues {
+    ctor public TransitionValues();
+    field public final java.util.Map<java.lang.String, java.lang.Object> values;
+    field public android.view.View view;
+  }
+
+  public abstract class Visibility extends android.support.transition.Transition {
+    ctor public Visibility();
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean isVisible(android.support.transition.TransitionValues);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+  }
+
+}
+
+package android.support.v13.app {
+
+  public class ActivityCompat extends android.support.v4.app.ActivityCompat {
+    ctor protected ActivityCompat();
+    method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+  }
+
+  public class FragmentCompat {
+    ctor public FragmentCompat();
+    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
+    method public static void setMenuVisibility(android.app.Fragment, boolean);
+    method public static void setUserVisibleHint(android.app.Fragment, boolean);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
+  }
+
+  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public void setup(android.content.Context, android.app.FragmentManager);
+    method public void setup(android.content.Context, android.app.FragmentManager, int);
+  }
+
+}
+
+package android.support.v13.view {
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, android.support.v13.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static abstract interface DragStartHelper.OnDragStartListener {
+    method public abstract boolean onDragStart(android.view.View, android.support.v13.view.DragStartHelper);
+  }
+
+  public class ViewCompat extends android.support.v4.view.ViewCompat {
+    method public static void cancelDragAndDrop(android.view.View);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+  }
+
+}
+
+package android.support.v13.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor public EditorInfoCompat();
+    method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
+  }
+
+  public static abstract interface InputConnectionCompat.OnCommitContentListener {
+    method public abstract boolean onCommitContent(android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public java.lang.Object unwrap();
+    method public static android.support.v13.view.inputmethod.InputContentInfoCompat wrap(java.lang.Object);
+  }
+
+}
+
+package android.support.v14.preference {
+
+  public class EditTextPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public EditTextPreferenceDialogFragment();
+    method public static android.support.v14.preference.EditTextPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public ListPreferenceDialogFragment();
+    method public static android.support.v14.preference.ListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public MultiSelectListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence[] getEntryValues();
+    method protected boolean[] getSelectedItems();
+    method public java.util.Set<java.lang.String> getValues();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValues(java.util.Set<java.lang.String>);
+  }
+
+  public class MultiSelectListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public MultiSelectListPreferenceDialogFragment();
+    method public static android.support.v14.preference.MultiSelectListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragment extends android.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragment();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public class SwitchPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreference(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+}
+
+package android.support.v17.leanback.app {
+
+  public final class BackgroundManager {
+    method public void attach(android.view.Window);
+    method public void attachToView(android.view.View);
+    method public void clearDrawable();
+    method public final int getColor();
+    method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
+    method public deprecated android.graphics.drawable.Drawable getDimLayer();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
+    method public boolean isAttached();
+    method public boolean isAutoReleaseOnStop();
+    method public void release();
+    method public void setAutoReleaseOnStop(boolean);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColor(int);
+    method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setThemeDrawableResourceId(int);
+  }
+
+   abstract class BaseRowFragment extends android.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+   abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public class BrandedFragment extends android.app.Fragment {
+    ctor public BrandedFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrandedSupportFragment extends android.support.v4.app.Fragment {
+    ctor public BrandedSupportFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrowseFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public BrowseFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method protected java.lang.Object createEntranceTransition();
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
+    method public int getHeadersState();
+    method public android.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersFragment onCreateHeadersFragment();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseFragment.BrowseTransitionListener {
+    ctor public BrowseFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseFragment.FragmentFactory<T extends android.app.Fragment> {
+    ctor public BrowseFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseFragment.FragmentFactory {
+    ctor public BrowseFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseFragment.MainFragmentAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseFragment.MainFragmentAdapterRegistry();
+    method public android.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseFragment.FragmentFactory);
+  }
+
+  public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class BrowseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public BrowseSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method protected java.lang.Object createEntranceTransition();
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public int getHeadersState();
+    method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
+    method public android.support.v4.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersSupportFragment onCreateHeadersSupportFragment();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseSupportFragment.BrowseTransitionListener {
+    ctor public BrowseSupportFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseSupportFragment.FragmentFactory<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseSupportFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseSupportFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory {
+    ctor public BrowseSupportFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsSupportFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseSupportFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseSupportFragment.MainFragmentAdapterRegistry();
+    method public android.support.v4.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class DetailsFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public DetailsFragment();
+    method protected java.lang.Object createEntranceTransition();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsFragmentBackgroundController {
+    ctor public DetailsFragmentBackgroundController(android.support.v17.leanback.app.DetailsFragment);
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.app.Fragment findOrCreateVideoFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.app.Fragment onCreateVideoFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+  }
+
+  public class DetailsSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public DetailsSupportFragment();
+    method protected java.lang.Object createEntranceTransition();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsSupportFragmentBackgroundController {
+    ctor public DetailsSupportFragmentBackgroundController(android.support.v17.leanback.app.DetailsSupportFragment);
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+  }
+
+  public class ErrorFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public ErrorFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class ErrorSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public ErrorSupportFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class GuidedStepFragment extends android.app.Fragment {
+    ctor public GuidedStepFragment();
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class GuidedStepSupportFragment extends android.support.v4.app.Fragment {
+    ctor public GuidedStepSupportFragment();
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepSupportFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment {
+    ctor public HeadersFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment {
+    ctor public HeadersSupportFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public abstract class OnboardingFragment extends android.app.Fragment {
+    ctor public OnboardingFragment();
+    method protected final int getCurrentPageIndex();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+  }
+
+  public abstract class OnboardingSupportFragment extends android.support.v4.app.Fragment {
+    ctor public OnboardingSupportFragment();
+    method protected final int getCurrentPageIndex();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+  }
+
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
+    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
+    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
+    method protected deprecated void pausePlayback();
+    method public final void play(int);
+    method public final void previous();
+    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method protected deprecated void skipToNext();
+    method protected deprecated void skipToPrevious();
+    method protected deprecated void startPlayback(int);
+  }
+
+  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
+    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  }
+
+  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public class PlaybackFragment extends android.app.Fragment {
+    ctor public PlaybackFragment();
+    method public void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
+    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
+  }
+
+  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
+    ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlayFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlayFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+    ctor public PlaybackOverlaySupportFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
+    ctor public PlaybackSupportFragment();
+    method public void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
+    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
+  }
+
+  public final class ProgressBarManager {
+    ctor public ProgressBarManager();
+    method public void disableProgressBar();
+    method public void enableProgressBar();
+    method public long getInitialDelay();
+    method public void hide();
+    method public void setInitialDelay(long);
+    method public void setProgressBarView(android.view.View);
+    method public void setRootView(android.view.ViewGroup);
+    method public void show();
+  }
+
+  public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment implements android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter {
+    ctor public RowsFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public static class RowsFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter {
+    ctor public RowsFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment implements android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsSupportFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsSupportFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter {
+    ctor public RowsSupportFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public static class RowsSupportFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter {
+    ctor public RowsSupportFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public class SearchFragment extends android.app.Fragment {
+    ctor public SearchFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SearchSupportFragment extends android.support.v4.app.Fragment {
+    ctor public SearchSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchSupportFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public VerticalGridFragment();
+    method protected java.lang.Object createEntranceTransition();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public VerticalGridSupportFragment();
+    method protected java.lang.Object createEntranceTransition();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
+    ctor public VideoFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
+    ctor public VideoSupportFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.database {
+
+  public abstract class CursorMapper {
+    ctor public CursorMapper();
+    method protected abstract java.lang.Object bind(android.database.Cursor);
+    method protected abstract void bindColumns(android.database.Cursor);
+    method public java.lang.Object convert(android.database.Cursor);
+  }
+
+}
+
+package android.support.v17.leanback.graphics {
+
+  public class BoundsRule {
+    ctor public BoundsRule();
+    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
+    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule top;
+  }
+
+  public static final class BoundsRule.ValueRule {
+    method public int getAbsoluteValue();
+    method public float getFraction();
+    method public void setAbsoluteValue(int);
+    method public void setFraction(float);
+  }
+
+  public final class ColorFilterCache {
+    method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
+    method public android.graphics.ColorFilter getFilterForLevel(float);
+  }
+
+  public final class ColorFilterDimmer {
+    method public void applyFilterToView(android.view.View);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context);
+    method public android.graphics.ColorFilter getColorFilter();
+    method public android.graphics.Paint getPaint();
+    method public void setActiveLevel(float);
+  }
+
+  public final class ColorOverlayDimmer {
+    method public int applyToColor(int);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context);
+    method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean);
+    method public int getAlpha();
+    method public float getAlphaFloat();
+    method public android.graphics.Paint getPaint();
+    method public boolean needsDraw();
+    method public void setActiveLevel(float);
+  }
+
+  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public CompositeDrawable();
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
+    method public int getChildCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void removeChild(int);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+  }
+
+  public static final class CompositeDrawable.ChildDrawable {
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
+    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void recomputeBounds();
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
+  }
+
+  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
+    ctor public FitWidthBitmapDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.Bitmap getBitmap();
+    method public int getOpacity();
+    method public android.graphics.Rect getSource();
+    method public int getVerticalOffset();
+    method public void setAlpha(int);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setSource(android.graphics.Rect);
+    method public void setVerticalOffset(int);
+    field public static final android.util.Property<android.support.v17.leanback.graphics.FitWidthBitmapDrawable, java.lang.Integer> PROPERTY_VERTICAL_OFFSET;
+  }
+
+}
+
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method public boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public abstract void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public void fadeOut();
+    method public void notifyPlaybackRowChanged();
+    method public void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostDestroy();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.system {
+
+  public class Settings {
+    method public boolean getBoolean(java.lang.String);
+    method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context);
+    method public void setBoolean(java.lang.String, boolean);
+    field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
+  }
+
+}
+
+package android.support.v17.leanback.widget {
+
+  public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public AbstractDetailsDescriptionPresenter();
+    method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getBody();
+    method public android.widget.TextView getSubtitle();
+    method public android.widget.TextView getTitle();
+  }
+
+  public abstract class AbstractMediaItemPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaItemPresenter();
+    ctor public AbstractMediaItemPresenter(int);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter getActionPresenter();
+    method protected int getMediaPlayState(java.lang.Object);
+    method public int getThemeId();
+    method public boolean hasMediaRowSeparator();
+    method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
+    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setBackgroundColor(int);
+    method public void setHasMediaRowSeparator(boolean);
+    method public void setThemeId(int);
+    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
+    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
+  }
+
+  public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaItemPresenter.ViewHolder(android.view.View);
+    method public android.view.ViewGroup getMediaItemActionsContainer();
+    method public android.view.View getMediaItemDetailsView();
+    method public android.widget.TextView getMediaItemDurationView();
+    method public android.widget.TextView getMediaItemNameView();
+    method public android.widget.TextView getMediaItemNumberView();
+    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
+    method public android.view.View getMediaItemPausedView();
+    method public android.view.View getMediaItemPlayingView();
+    method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
+    method public android.view.View getMediaItemRowSeparator();
+    method public android.view.View getSelectorView();
+    method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
+    method public void notifyDetailsChanged();
+    method public void notifyPlayStateChanged();
+    method public void onBindRowActions();
+    method public void setSelectedMediaItemNumberView(int);
+  }
+
+  public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaListHeaderPresenter(android.content.Context, int);
+    ctor public AbstractMediaListHeaderPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected abstract void onBindMediaListHeaderViewHolder(android.support.v17.leanback.widget.AbstractMediaListHeaderPresenter.ViewHolder, java.lang.Object);
+    method public void setBackgroundColor(int);
+  }
+
+  public static class AbstractMediaListHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaListHeaderPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getHeaderView();
+  }
+
+  public class Action {
+    ctor public Action(long);
+    ctor public Action(long, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable);
+    method public final void addKeyCode(int);
+    method public final android.graphics.drawable.Drawable getIcon();
+    method public final long getId();
+    method public final java.lang.CharSequence getLabel1();
+    method public final java.lang.CharSequence getLabel2();
+    method public final void removeKeyCode(int);
+    method public final boolean respondsToKeyCode(int);
+    method public final void setIcon(android.graphics.drawable.Drawable);
+    method public final void setId(long);
+    method public final void setLabel1(java.lang.CharSequence);
+    method public final void setLabel2(java.lang.CharSequence);
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+  }
+
+  public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ArrayObjectAdapter();
+    method public void add(java.lang.Object);
+    method public void add(int, java.lang.Object);
+    method public void addAll(int, java.util.Collection);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public boolean remove(java.lang.Object);
+    method public int removeItems(int, int);
+    method public void replace(int, java.lang.Object);
+    method public int size();
+    method public <E> java.util.List<E> unmodifiableList();
+  }
+
+  public class BaseCardView extends android.widget.FrameLayout {
+    ctor public BaseCardView(android.content.Context);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
+    method public int getCardType();
+    method public deprecated int getExtraVisibility();
+    method public int getInfoVisibility();
+    method public boolean isSelectedAnimationDelayed();
+    method public void setCardType(int);
+    method public deprecated void setExtraVisibility(int);
+    method public void setInfoVisibility(int);
+    method public void setSelectedAnimationDelayed(boolean);
+    field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
+    field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0
+    field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1
+    field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3
+    field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0
+  }
+
+  public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView.LayoutParams(int, int);
+    ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams);
+    field public static final int VIEW_TYPE_EXTRA = 2; // 0x2
+    field public static final int VIEW_TYPE_INFO = 1; // 0x1
+    field public static final int VIEW_TYPE_MAIN = 0; // 0x0
+    field public int viewType;
+  }
+
+  public abstract interface BaseOnItemViewClickedListener<T> {
+    method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public abstract interface BaseOnItemViewSelectedListener<T> {
+    method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public class BrowseFrameLayout extends android.widget.FrameLayout {
+    ctor public BrowseFrameLayout(android.content.Context);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
+    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
+    method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnChildFocusListener {
+    method public abstract void onRequestChildFocus(android.view.View, android.view.View);
+    method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnFocusSearchListener {
+    method public abstract android.view.View onFocusSearch(android.view.View, int);
+  }
+
+  public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ClassPresenterSelector();
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenterSelector(java.lang.Class<?>, android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ControlButtonPresenterSelector();
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter();
+    method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter();
+  }
+
+  public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public CursorObjectAdapter();
+    method public void changeCursor(android.database.Cursor);
+    method public void close();
+    method public java.lang.Object get(int);
+    method public final android.database.Cursor getCursor();
+    method public final android.support.v17.leanback.database.CursorMapper getMapper();
+    method protected final void invalidateCache(int);
+    method protected final void invalidateCache(int, int);
+    method public boolean isClosed();
+    method protected void onCursorChanged();
+    method protected void onMapperChanged();
+    method public final void setMapper(android.support.v17.leanback.database.CursorMapper);
+    method public int size();
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+  }
+
+  public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DetailsOverviewLogoPresenter();
+    method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+  }
+
+  public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter getParentPresenter();
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder();
+    method public boolean isSizeFromDrawableIntrinsic();
+    method public void setSizeFromDrawableIntrinsic(boolean);
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter;
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
+  }
+
+  public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row {
+    ctor public DetailsOverviewRow(java.lang.Object);
+    method public final deprecated void addAction(android.support.v17.leanback.widget.Action);
+    method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public boolean isImageScaleUpAllowed();
+    method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action);
+    method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setImageScaleUpAllowed(boolean);
+    method public final void setItem(java.lang.Object);
+  }
+
+  public static class DetailsOverviewRow.Listener {
+    ctor public DetailsOverviewRow.Listener();
+    method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+  }
+
+  public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public boolean isStyleLarge();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setStyleLarge(boolean);
+  }
+
+  public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder;
+  }
+
+  public class DetailsParallax extends android.support.v17.leanback.widget.RecyclerViewParallax {
+    ctor public DetailsParallax();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowBottom();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowTop();
+  }
+
+  public class DividerPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DividerPresenter();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public class DividerRow extends android.support.v17.leanback.widget.Row {
+    ctor public DividerRow();
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract interface FacetProvider {
+    method public abstract java.lang.Object getFacet(java.lang.Class<?>);
+  }
+
+  public abstract interface FacetProviderAdapter {
+    method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+  }
+
+  public abstract interface FocusHighlight {
+    field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3
+    field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2
+    field public static final int ZOOM_FACTOR_NONE = 0; // 0x0
+    field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1
+    field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4
+  }
+
+  public class FocusHighlightHelper {
+    ctor public FocusHighlightHelper();
+    method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView, boolean);
+  }
+
+  public abstract interface FragmentAnimationProvider {
+    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public final int getActionsBackgroundColor();
+    method public final int getAlignmentMode();
+    method public final int getBackgroundColor();
+    method public final int getInitialState();
+    method protected int getLayoutResourceId();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public final boolean isParticipatingEntranceTransition();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+    method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    method public final void setActionsBackgroundColor(int);
+    method public final void setAlignmentMode(int);
+    method public final void setBackgroundColor(int);
+    method public final void setInitialState(int);
+    method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setParticipatingEntranceTransition(boolean);
+    method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1
+    field public static final int ALIGN_MODE_START = 0; // 0x0
+    field public static final int STATE_FULL = 1; // 0x1
+    field public static final int STATE_HALF = 0; // 0x0
+    field public static final int STATE_SMALL = 2; // 0x2
+    field protected int mInitialState;
+  }
+
+  public static abstract class FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.Listener();
+    method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener();
+    method public final android.view.ViewGroup getActionsRow();
+    method public final android.view.ViewGroup getDetailsDescriptionFrame();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder();
+    method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder();
+    method public final android.view.ViewGroup getOverviewView();
+    method public final int getState();
+    field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener;
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener();
+  }
+
+  public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewSharedElementHelper();
+    method public boolean getAutoStartSharedElementTransition();
+    method public void setAutoStartSharedElementTransition(boolean);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public void startPostponedEnterTransition();
+  }
+
+  public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidanceStylist();
+    method public android.widget.TextView getBreadcrumbView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance);
+    method public void onDestroyView();
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideLayoutId();
+  }
+
+  public static class GuidanceStylist.Guidance {
+    ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable);
+    method public java.lang.String getBreadcrumb();
+    method public java.lang.String getDescription();
+    method public android.graphics.drawable.Drawable getIconDrawable();
+    method public java.lang.String getTitle();
+  }
+
+  public class GuidedAction extends android.support.v17.leanback.widget.Action {
+    ctor protected GuidedAction();
+    method public int getCheckSetId();
+    method public java.lang.CharSequence getDescription();
+    method public int getDescriptionEditInputType();
+    method public int getDescriptionInputType();
+    method public java.lang.CharSequence getEditDescription();
+    method public int getEditInputType();
+    method public java.lang.CharSequence getEditTitle();
+    method public int getInputType();
+    method public android.content.Intent getIntent();
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getSubActions();
+    method public java.lang.CharSequence getTitle();
+    method public boolean hasEditableActivatorView();
+    method public boolean hasMultilineDescription();
+    method public boolean hasNext();
+    method public boolean hasSubActions();
+    method public boolean hasTextEditable();
+    method public boolean infoOnly();
+    method public final boolean isAutoSaveRestoreEnabled();
+    method public boolean isChecked();
+    method public boolean isDescriptionEditable();
+    method public boolean isEditTitleUsed();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public void onRestoreInstanceState(android.os.Bundle, java.lang.String);
+    method public void onSaveInstanceState(android.os.Bundle, java.lang.String);
+    method public void setChecked(boolean);
+    method public void setDescription(java.lang.CharSequence);
+    method public void setEditDescription(java.lang.CharSequence);
+    method public void setEditTitle(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setSubActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setTitle(java.lang.CharSequence);
+    field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
+    field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
+    field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
+    field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL
+    field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL
+    field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
+    field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
+    field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
+    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
+    field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
+    field public static final int NO_CHECK_SET = 0; // 0x0
+  }
+
+  public static class GuidedAction.Builder extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public deprecated GuidedAction.Builder();
+    ctor public GuidedAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedAction build();
+  }
+
+  public static abstract class GuidedAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedAction.BuilderBase> {
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction);
+    method public B autoSaveRestoreEnabled(boolean);
+    method public B checkSetId(int);
+    method public B checked(boolean);
+    method public B clickAction(long);
+    method public B description(java.lang.CharSequence);
+    method public B description(int);
+    method public B descriptionEditInputType(int);
+    method public B descriptionEditable(boolean);
+    method public B descriptionInputType(int);
+    method public B editDescription(java.lang.CharSequence);
+    method public B editDescription(int);
+    method public B editInputType(int);
+    method public B editTitle(java.lang.CharSequence);
+    method public B editTitle(int);
+    method public B editable(boolean);
+    method public B enabled(boolean);
+    method public B focusable(boolean);
+    method public android.content.Context getContext();
+    method public B hasEditableActivatorView(boolean);
+    method public B hasNext(boolean);
+    method public B icon(android.graphics.drawable.Drawable);
+    method public B icon(int);
+    method public deprecated B iconResourceId(int, android.content.Context);
+    method public B id(long);
+    method public B infoOnly(boolean);
+    method public B inputType(int);
+    method public B intent(android.content.Intent);
+    method public B multilineDescription(boolean);
+    method public B subActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public B title(java.lang.CharSequence);
+    method public B title(int);
+  }
+
+  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
+    ctor public GuidedActionEditText(android.content.Context);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidedActionsStylist();
+    method public void collapseAction(boolean);
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
+    method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
+    method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
+    method public final boolean isBackKeyToCollapseActivatorView();
+    method public final boolean isBackKeyToCollapseSubActions();
+    method public boolean isButtonActions();
+    method public boolean isExpandTransitionSupported();
+    method public boolean isExpanded();
+    method public boolean isInExpandTransition();
+    method public boolean isSubActionsExpanded();
+    method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void onBindActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDestroyView();
+    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideItemLayoutId();
+    method public int onProvideItemLayoutId(int);
+    method public int onProvideLayoutId();
+    method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void setAsButtonActions();
+    method public final void setBackKeyToCollapseActivatorView(boolean);
+    method public final void setBackKeyToCollapseSubActions(boolean);
+    method public deprecated void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
+    field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
+  }
+
+  public static class GuidedActionsStylist.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View);
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View, boolean);
+    method public android.support.v17.leanback.widget.GuidedAction getAction();
+    method public android.widget.ImageView getCheckmarkView();
+    method public android.widget.ImageView getChevronView();
+    method public android.view.View getContentView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.EditText getEditableDescriptionView();
+    method public android.widget.EditText getEditableTitleView();
+    method public android.view.View getEditingView();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public boolean isInEditing();
+    method public boolean isInEditingActivatorView();
+    method public boolean isInEditingDescription();
+    method public boolean isInEditingText();
+    method public boolean isInEditingTitle();
+    method public boolean isSubAction();
+  }
+
+  public class GuidedDatePickerAction extends android.support.v17.leanback.widget.GuidedAction {
+    ctor public GuidedDatePickerAction();
+    method public long getDate();
+    method public java.lang.String getDatePickerFormat();
+    method public long getMaxDate();
+    method public long getMinDate();
+    method public void setDate(long);
+  }
+
+  public static final class GuidedDatePickerAction.Builder extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase {
+    ctor public GuidedDatePickerAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedDatePickerAction build();
+  }
+
+  public static abstract class GuidedDatePickerAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase> extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public GuidedDatePickerAction.BuilderBase(android.content.Context);
+    method protected final void applyDatePickerValues(android.support.v17.leanback.widget.GuidedDatePickerAction);
+    method public B date(long);
+    method public B datePickerFormat(java.lang.String);
+    method public B maxDate(long);
+    method public B minDate(long);
+  }
+
+  public class HeaderItem {
+    ctor public HeaderItem(long, java.lang.String);
+    ctor public HeaderItem(java.lang.String);
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getDescription();
+    method public final long getId();
+    method public final java.lang.String getName();
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setDescription(java.lang.CharSequence);
+  }
+
+  public class HorizontalGridView extends android.support.v7.widget.RecyclerView {
+    ctor public HorizontalGridView(android.content.Context);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int);
+    method public final boolean getFadingLeftEdge();
+    method public final int getFadingLeftEdgeLength();
+    method public final int getFadingLeftEdgeOffset();
+    method public final boolean getFadingRightEdge();
+    method public final int getFadingRightEdgeLength();
+    method public final int getFadingRightEdgeOffset();
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public final void setFadingLeftEdge(boolean);
+    method public final void setFadingLeftEdgeLength(int);
+    method public final void setFadingLeftEdgeOffset(int);
+    method public final void setFadingRightEdge(boolean);
+    method public final void setFadingRightEdgeLength(int);
+    method public final void setFadingRightEdgeOffset(int);
+    method public void setNumRows(int);
+    method public void setRowHeight(int);
+  }
+
+  public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher {
+    ctor public HorizontalHoverCardSwitcher();
+    method protected void insertView(android.view.View);
+    method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object);
+  }
+
+  public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
+    ctor public deprecated ImageCardView(android.content.Context, int);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
+    ctor public ImageCardView(android.content.Context);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
+    method public android.graphics.drawable.Drawable getBadgeImage();
+    method public java.lang.CharSequence getContentText();
+    method public android.graphics.drawable.Drawable getInfoAreaBackground();
+    method public android.graphics.drawable.Drawable getMainImage();
+    method public final android.widget.ImageView getMainImageView();
+    method public java.lang.CharSequence getTitleText();
+    method public void setBadgeImage(android.graphics.drawable.Drawable);
+    method public void setContentText(java.lang.CharSequence);
+    method public void setInfoAreaBackground(android.graphics.drawable.Drawable);
+    method public void setInfoAreaBackgroundColor(int);
+    method public void setMainImage(android.graphics.drawable.Drawable);
+    method public void setMainImage(android.graphics.drawable.Drawable, boolean);
+    method public void setMainImageAdjustViewBounds(boolean);
+    method public void setMainImageDimensions(int, int);
+    method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
+    method public void setTitleText(java.lang.CharSequence);
+    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
+    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
+    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
+    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
+    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
+  }
+
+  public abstract interface ImeKeyMonitor {
+    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public static abstract interface ImeKeyMonitor.ImeKeyListener {
+    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
+  }
+
+  public final class ItemAlignmentFacet {
+    ctor public ItemAlignmentFacet();
+    method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs();
+    method public boolean isMultiAlignment();
+    method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static class ItemAlignmentFacet.ItemAlignmentDef {
+    ctor public ItemAlignmentFacet.ItemAlignmentDef();
+    method public final int getItemAlignmentFocusViewId();
+    method public final int getItemAlignmentOffset();
+    method public final float getItemAlignmentOffsetPercent();
+    method public final int getItemAlignmentViewId();
+    method public boolean isAlignedToTextViewBaseLine();
+    method public final boolean isItemAlignmentOffsetWithPadding();
+    method public final void setAlignedToTextViewBaseline(boolean);
+    method public final void setItemAlignmentFocusViewId(int);
+    method public final void setItemAlignmentOffset(int);
+    method public final void setItemAlignmentOffsetPercent(float);
+    method public final void setItemAlignmentOffsetWithPadding(boolean);
+    method public final void setItemAlignmentViewId(int);
+  }
+
+  public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter {
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ItemBridgeAdapter();
+    method public void clear();
+    method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+    method public int getItemCount();
+    method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper();
+    method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper();
+    method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener);
+    method public void setPresenter(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>);
+    method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper);
+  }
+
+  public static class ItemBridgeAdapter.AdapterListener {
+    ctor public ItemBridgeAdapter.AdapterListener();
+    method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+  }
+
+  public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    method public final java.lang.Object getExtraObject();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.Presenter getPresenter();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder();
+    method public void setExtraObject(java.lang.Object);
+  }
+
+  public static abstract class ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapter.Wrapper();
+    method public abstract android.view.View createWrapper(android.view.View);
+    method public abstract void wrap(android.view.View, android.view.View);
+  }
+
+  public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper);
+    method public android.view.View createWrapper(android.view.View);
+    method public void wrap(android.view.View, android.view.View);
+  }
+
+  public class ListRow extends android.support.v17.leanback.widget.Row {
+    ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public java.lang.CharSequence getContentDescription();
+    method public void setContentDescription(java.lang.CharSequence);
+  }
+
+  public final class ListRowHoverCardView extends android.widget.LinearLayout {
+    ctor public ListRowHoverCardView(android.content.Context);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int);
+    method public final java.lang.CharSequence getDescription();
+    method public final java.lang.CharSequence getTitle();
+    method public final void setDescription(java.lang.CharSequence);
+    method public final void setTitle(java.lang.CharSequence);
+  }
+
+  public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public ListRowPresenter();
+    ctor public ListRowPresenter(int);
+    ctor public ListRowPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public int getExpandedRowHeight();
+    method public final int getFocusZoomFactor();
+    method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector();
+    method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter);
+    method public int getRowHeight();
+    method public final boolean getShadowEnabled();
+    method public final deprecated int getZoomFactor();
+    method public final boolean isFocusDimmerUsed();
+    method public final boolean isKeepChildForeground();
+    method public boolean isUsingDefaultListSelectEffect();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void setExpandedRowHeight(int);
+    method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumRows(int);
+    method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int);
+    method public void setRowHeight(int);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class ListRowPresenter.SelectItemViewHolderTask extends android.support.v17.leanback.widget.Presenter.ViewHolderTask {
+    ctor public ListRowPresenter.SelectItemViewHolderTask(int);
+    method public int getItemPosition();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolderTask getItemTask();
+    method public boolean isSmoothScroll();
+    method public void setItemPosition(int);
+    method public void setItemTask(android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSmoothScroll(boolean);
+  }
+
+  public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter);
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.HorizontalGridView getGridView();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getItemViewHolder(int);
+    method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter();
+    method public int getSelectedPosition();
+  }
+
+  public final class ListRowView extends android.widget.LinearLayout {
+    ctor public ListRowView(android.content.Context);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
+  }
+
+  public abstract interface MultiActionsProvider {
+    method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
+  }
+
+  public static class MultiActionsProvider.MultiAction {
+    ctor public MultiActionsProvider.MultiAction(long);
+    method public android.graphics.drawable.Drawable getCurrentDrawable();
+    method public android.graphics.drawable.Drawable[] getDrawables();
+    method public long getId();
+    method public int getIndex();
+    method public void incrementIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+  }
+
+  public abstract class ObjectAdapter {
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ObjectAdapter();
+    method public abstract java.lang.Object get(int);
+    method public long getId(int);
+    method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public final boolean hasStableIds();
+    method public boolean isImmediateNotifySupported();
+    method protected final void notifyChanged();
+    method public final void notifyItemRangeChanged(int, int);
+    method protected final void notifyItemRangeInserted(int, int);
+    method protected final void notifyItemRangeRemoved(int, int);
+    method protected void onHasStableIdsChanged();
+    method protected void onPresenterSelectorChanged();
+    method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    method public final void setHasStableIds(boolean);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public abstract int size();
+    method public final void unregisterAllObservers();
+    method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    field public static final int NO_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class ObjectAdapter.DataObserver {
+    ctor public ObjectAdapter.DataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public abstract interface OnActionClickedListener {
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+  }
+
+  public abstract interface OnChildLaidOutListener {
+    method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract deprecated interface OnChildSelectedListener {
+    method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract class OnChildViewHolderSelectedListener {
+    ctor public OnChildViewHolderSelectedListener();
+    method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+  }
+
+  public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
+  }
+
+  public abstract interface OnItemViewSelectedListener implements android.support.v17.leanback.widget.BaseOnItemViewSelectedListener {
+  }
+
+  public class PageRow extends android.support.v17.leanback.widget.Row {
+    ctor public PageRow(android.support.v17.leanback.widget.HeaderItem);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract class Parallax<PropertyT extends android.util.Property> {
+    ctor public Parallax();
+    method public void addEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue...);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue...);
+    method public abstract PropertyT addProperty(java.lang.String);
+    method public abstract PropertyT createProperty(java.lang.String, int);
+    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public final java.util.List<PropertyT> getProperties();
+    method public void removeAllEffects();
+    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public void updateValues();
+    method public abstract void verifyProperties() throws java.lang.IllegalStateException;
+  }
+
+  public static abstract class Parallax.FloatParallax<FloatPropertyT extends android.support.v17.leanback.widget.Parallax.FloatProperty> extends android.support.v17.leanback.widget.Parallax {
+    ctor public Parallax.FloatParallax();
+    method public final FloatPropertyT addProperty(java.lang.String);
+    method public abstract float getMaxValue();
+    method public final float getPropertyValue(int);
+    method public final void setPropertyValue(int, float);
+    method public final void verifyProperties() throws java.lang.IllegalStateException;
+  }
+
+  public static class Parallax.FloatProperty extends android.util.Property {
+    ctor public Parallax.FloatProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atFraction(float);
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax.FloatParallax);
+    method public final int getIndex();
+    method public final void set(android.support.v17.leanback.widget.Parallax.FloatParallax, java.lang.Float);
+    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
+    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
+  }
+
+  public static class Parallax.FloatPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
+    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float);
+    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float, float);
+    method public final float getMarkerValue(android.support.v17.leanback.widget.Parallax.FloatParallax);
+  }
+
+  public static abstract class Parallax.IntParallax<IntPropertyT extends android.support.v17.leanback.widget.Parallax.IntProperty> extends android.support.v17.leanback.widget.Parallax {
+    ctor public Parallax.IntParallax();
+    method public final IntPropertyT addProperty(java.lang.String);
+    method public abstract int getMaxValue();
+    method public final int getPropertyValue(int);
+    method public final void setPropertyValue(int, int);
+    method public final void verifyProperties() throws java.lang.IllegalStateException;
+  }
+
+  public static class Parallax.IntProperty extends android.util.Property {
+    ctor public Parallax.IntProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atFraction(float);
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax.IntParallax);
+    method public final int getIndex();
+    method public final void set(android.support.v17.leanback.widget.Parallax.IntParallax, java.lang.Integer);
+    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
+    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
+  }
+
+  public static class Parallax.IntPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
+    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int);
+    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int, float);
+    method public final int getMarkerValue(android.support.v17.leanback.widget.Parallax.IntParallax);
+  }
+
+  public static class Parallax.PropertyMarkerValue<PropertyT> {
+    ctor public Parallax.PropertyMarkerValue(PropertyT);
+    method public PropertyT getProperty();
+  }
+
+  public abstract class ParallaxEffect<ParallaxEffectT extends android.support.v17.leanback.widget.ParallaxEffect, PropertyMarkerValueT extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> {
+    ctor public ParallaxEffect();
+    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method protected abstract float calculateFraction(android.support.v17.leanback.widget.Parallax);
+    method public final java.util.List<PropertyMarkerValueT> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
+    method public final void performMapping(android.support.v17.leanback.widget.Parallax);
+    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final void setPropertyRanges(PropertyMarkerValueT...);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
+  }
+
+  public static final class ParallaxEffect.FloatEffect extends android.support.v17.leanback.widget.ParallaxEffect {
+    ctor public ParallaxEffect.FloatEffect();
+    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
+  }
+
+  public static final class ParallaxEffect.IntEffect extends android.support.v17.leanback.widget.ParallaxEffect {
+    ctor public ParallaxEffect.IntEffect();
+    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
+  }
+
+  public abstract class ParallaxTarget {
+    ctor public ParallaxTarget();
+    method public abstract float getFraction();
+    method public abstract void update(float);
+  }
+
+  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
+    method public float getFraction();
+    method public void update(float);
+  }
+
+  public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
+    ctor public PlaybackControlsRow(java.lang.Object);
+    ctor public PlaybackControlsRow();
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
+    method public int getBufferedProgress();
+    method public long getBufferedProgressLong();
+    method public int getCurrentTime();
+    method public long getCurrentTimeLong();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
+    method public int getTotalTime();
+    method public long getTotalTimeLong();
+    method public void setBufferedProgress(int);
+    method public void setBufferedProgressLong(long);
+    method public void setCurrentTime(int);
+    method public void setCurrentTimeLong(long);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setTotalTime(int);
+    method public void setTotalTimeLong(long);
+  }
+
+  public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context);
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int);
+    field public static int OFF;
+    field public static int ON;
+  }
+
+  public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context);
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context);
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int);
+    field public static int OFF;
+    field public static int ON;
+  }
+
+  public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MoreActions(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MultiAction(int);
+    method public int getActionCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getIndex();
+    method public java.lang.String getLabel(int);
+    method public java.lang.String getSecondaryLabel(int);
+    method public void nextIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+    method public void setLabels(java.lang.String[]);
+    method public void setSecondaryLabels(java.lang.String[]);
+  }
+
+  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
+    field public static int PAUSE;
+    field public static int PLAY;
+  }
+
+  public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int);
+    field public static int ALL;
+    field public static int NONE;
+    field public static int ONE;
+  }
+
+  public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context);
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context);
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int);
+    field public static int OFF;
+    field public static int ON;
+  }
+
+  public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipNextAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int);
+    field public static int OUTLINE;
+    field public static int SOLID;
+  }
+
+  public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
+  }
+
+  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public PlaybackControlsRowPresenter();
+    method public boolean areSecondaryActionsHidden();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+    method public void setSecondaryActionsHidden(boolean);
+    method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean);
+    method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
+  }
+
+  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
+  }
+
+  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public PlaybackRowPresenter();
+    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+  }
+
+  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
+  }
+
+  public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter();
+    method protected static void cancelAnimationsRecursive(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener);
+  }
+
+  public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter.ViewHolder(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    field public final android.view.View view;
+  }
+
+  public static abstract class Presenter.ViewHolderTask {
+    ctor public Presenter.ViewHolderTask();
+    method public void run(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public abstract class PresenterSelector {
+    ctor public PresenterSelector();
+    method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter[] getPresenters();
+  }
+
+  public abstract class PresenterSwitcher {
+    ctor public PresenterSwitcher();
+    method public void clear();
+    method public final android.view.ViewGroup getParentViewGroup();
+    method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector);
+    method protected abstract void insertView(android.view.View);
+    method protected void onViewSelected(android.view.View);
+    method public void select(java.lang.Object);
+    method protected void showView(android.view.View, boolean);
+    method public void unselect();
+  }
+
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax.IntParallax {
+    ctor public RecyclerViewParallax();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
+    method public int getMaxValue();
+    method public android.support.v7.widget.RecyclerView getRecyclerView();
+    method public void setRecyclerView(android.support.v7.widget.RecyclerView);
+  }
+
+  public static final class RecyclerViewParallax.ChildPositionProperty extends android.support.v17.leanback.widget.Parallax.IntProperty {
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty adapterPosition(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty fraction(float);
+    method public int getAdapterPosition();
+    method public float getFraction();
+    method public int getOffset();
+    method public int getViewId();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty offset(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty viewId(int);
+  }
+
+  public class Row {
+    ctor public Row(long, android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row(android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row();
+    method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem();
+    method public final long getId();
+    method public boolean isRenderedAsRowView();
+    method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem);
+    method public final void setId(long);
+  }
+
+  public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowHeaderPresenter();
+    method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint);
+    method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public boolean isNullItemVisibilityGone();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setNullItemVisibilityGone(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float);
+  }
+
+  public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowHeaderPresenter.ViewHolder(android.view.View);
+    method public final float getSelectLevel();
+  }
+
+  public final class RowHeaderView extends android.widget.TextView {
+    ctor public RowHeaderView(android.content.Context);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowPresenter();
+    method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter();
+    method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final boolean getSelectEffectEnabled();
+    method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final int getSyncActivatePolicy();
+    method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected boolean isClippingChildren();
+    method public boolean isUsingDefaultSelectEffect();
+    method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter);
+    method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setSelectEffectEnabled(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float);
+    method public final void setSyncActivatePolicy(int);
+    field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3
+    field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2
+  }
+
+  public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowPresenter.ViewHolder(android.view.View);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.view.View.OnKeyListener getOnKeyListener();
+    method public final android.support.v17.leanback.widget.Row getRow();
+    method public final java.lang.Object getRowObject();
+    method public final float getSelectLevel();
+    method public java.lang.Object getSelectedItem();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
+    method public final boolean isExpanded();
+    method public final boolean isSelected();
+    method public final void setActivated(boolean);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setOnKeyListener(android.view.View.OnKeyListener);
+    method public final void syncActivatedStatus(android.view.View);
+    field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer;
+  }
+
+  public class SearchBar extends android.widget.RelativeLayout {
+    ctor public SearchBar(android.content.Context);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet, int);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.String getTitle();
+    method public boolean isRecognizing();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
+    method public void setSearchQuery(java.lang.String);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setSpeechRecognizer(android.speech.SpeechRecognizer);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+    method public void stopRecognition();
+  }
+
+  public static abstract interface SearchBar.SearchBarListener {
+    method public abstract void onKeyboardDismiss(java.lang.String);
+    method public abstract void onSearchQueryChange(java.lang.String);
+    method public abstract void onSearchQuerySubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchBar.SearchBarPermissionListener {
+    method public abstract void requestAudioPermission();
+  }
+
+  public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView {
+    ctor public SearchEditText(android.content.Context);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener);
+  }
+
+  public static abstract interface SearchEditText.OnKeyboardDismissListener {
+    method public abstract void onKeyboardDismiss();
+  }
+
+  public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableOrbColorAnimation(boolean);
+    method public int getOrbColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors();
+    method public android.graphics.drawable.Drawable getOrbIcon();
+    method public void onClick(android.view.View);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener);
+    method public void setOrbColor(int);
+    method public deprecated void setOrbColor(int, int);
+    method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
+  }
+
+  public static class SearchOrbView.Colors {
+    ctor public SearchOrbView.Colors(int);
+    ctor public SearchOrbView.Colors(int, int);
+    ctor public SearchOrbView.Colors(int, int, int);
+    method public static int getBrightColor(int);
+    field public int brightColor;
+    field public int color;
+    field public int iconColor;
+  }
+
+  public class SectionRow extends android.support.v17.leanback.widget.Row {
+    ctor public SectionRow(android.support.v17.leanback.widget.HeaderItem);
+    ctor public SectionRow(long, java.lang.String);
+    ctor public SectionRow(java.lang.String);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public class ShadowOverlayContainer extends android.widget.FrameLayout {
+    ctor public ShadowOverlayContainer(android.content.Context);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
+    method public int getShadowType();
+    method public android.view.View getWrappedView();
+    method public deprecated void initialize(boolean, boolean);
+    method public deprecated void initialize(boolean, boolean, boolean);
+    method public static void prepareParentForShadow(android.view.ViewGroup);
+    method public void setOverlayColor(int);
+    method public void setShadowFocusLevel(float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsShadow();
+    method public void useDynamicShadow();
+    method public void useDynamicShadow(float, float);
+    method public void useStaticShadow();
+    method public void wrap(android.view.View);
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public final class ShadowOverlayHelper {
+    method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context);
+    method public int getShadowType();
+    method public boolean needsOverlay();
+    method public boolean needsRoundedCorner();
+    method public boolean needsWrapper();
+    method public void onViewCreated(android.view.View);
+    method public void prepareParentForShadow(android.view.ViewGroup);
+    method public static void setNoneWrapperOverlayColor(android.view.View, int);
+    method public static void setNoneWrapperShadowFocusLevel(android.view.View, float);
+    method public void setOverlayColor(android.view.View, int);
+    method public void setShadowFocusLevel(android.view.View, float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsForeground();
+    method public static boolean supportsRoundedCorner();
+    method public static boolean supportsShadow();
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public static final class ShadowOverlayHelper.Builder {
+    ctor public ShadowOverlayHelper.Builder();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean);
+  }
+
+  public static final class ShadowOverlayHelper.Options {
+    ctor public ShadowOverlayHelper.Options();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float);
+    method public final float getDynamicShadowFocusedZ();
+    method public final float getDynamicShadowUnfocusedZ();
+    method public final int getRoundedCornerRadius();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int);
+    field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT;
+  }
+
+  public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public SparseArrayObjectAdapter();
+    method public void clear(int);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public int indexOf(int);
+    method public java.lang.Object lookup(int);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public void set(int, java.lang.Object);
+    method public int size();
+  }
+
+  public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView {
+    ctor public SpeechOrbView(android.content.Context);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSoundLevel(int);
+    method public void showListening();
+    method public void showNotListening();
+  }
+
+  public abstract interface SpeechRecognitionCallback {
+    method public abstract void recognizeSpeech();
+  }
+
+   class StreamingTextView extends android.widget.EditText {
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet);
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int);
+    method public static boolean isLayoutRtl(android.view.View);
+    method public void reset();
+    method public void setFinalRecognizedText(java.lang.CharSequence);
+    method public void updateRecognizedText(java.lang.String, java.lang.String);
+    method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>);
+  }
+
+  public class TitleHelper {
+    ctor public TitleHelper(android.view.ViewGroup, android.view.View);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public android.view.ViewGroup getSceneRoot();
+    method public android.view.View getTitleView();
+    method public void showTitle(boolean);
+  }
+
+  public class TitleView extends android.widget.FrameLayout implements android.support.v17.leanback.widget.TitleViewAdapter.Provider {
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableAnimation(boolean);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+  }
+
+  public abstract class TitleViewAdapter {
+    ctor public TitleViewAdapter();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public abstract android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public void setAnimationEnabled(boolean);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+    field public static final int BRANDING_VIEW_VISIBLE = 2; // 0x2
+    field public static final int FULL_VIEW_VISIBLE = 6; // 0x6
+    field public static final int SEARCH_VIEW_VISIBLE = 4; // 0x4
+  }
+
+  public static abstract interface TitleViewAdapter.Provider {
+    method public abstract android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+  }
+
+  public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public VerticalGridPresenter();
+    ctor public VerticalGridPresenter(int);
+    ctor public VerticalGridPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public final int getFocusZoomFactor();
+    method public final boolean getKeepChildForeground();
+    method public int getNumberOfColumns();
+    method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public final boolean getShadowEnabled();
+    method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder);
+    method public final boolean isFocusDimmerUsed();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumberOfColumns(int);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView);
+    method public android.support.v17.leanback.widget.VerticalGridView getGridView();
+  }
+
+  public class VerticalGridView extends android.support.v7.widget.RecyclerView {
+    ctor public VerticalGridView(android.content.Context);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int);
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public void setColumnWidth(int);
+    method public void setNumColumns(int);
+  }
+
+  public abstract interface ViewHolderTask {
+    method public abstract void run(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+}
+
+package android.support.v17.leanback.widget.picker {
+
+  public class Picker extends android.widget.FrameLayout {
+    ctor public Picker(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public float getActivatedVisibleItemCount();
+    method public android.support.v17.leanback.widget.picker.PickerColumn getColumnAt(int);
+    method public int getColumnsCount();
+    method protected int getPickerItemHeightPixels();
+    method public final int getPickerItemLayoutId();
+    method public final int getPickerItemTextViewId();
+    method public int getSelectedColumn();
+    method public final java.lang.CharSequence getSeparator();
+    method public float getVisibleItemCount();
+    method public void onColumnValueChanged(int, int);
+    method public void removeOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public void setActivatedVisibleItemCount(float);
+    method public void setColumnAt(int, android.support.v17.leanback.widget.picker.PickerColumn);
+    method public void setColumnValue(int, int, boolean);
+    method public void setColumns(java.util.List<android.support.v17.leanback.widget.picker.PickerColumn>);
+    method public final void setPickerItemTextViewId(int);
+    method public void setSelectedColumn(int);
+    method public final void setSeparator(java.lang.CharSequence);
+    method public void setVisibleItemCount(float);
+  }
+
+  public static abstract interface Picker.PickerValueListener {
+    method public abstract void onValueChanged(android.support.v17.leanback.widget.picker.Picker, int);
+  }
+
+  public class PickerColumn {
+    ctor public PickerColumn();
+    method public int getCount();
+    method public int getCurrentValue();
+    method public java.lang.CharSequence getLabelFor(int);
+    method public java.lang.String getLabelFormat();
+    method public int getMaxValue();
+    method public int getMinValue();
+    method public java.lang.CharSequence[] getStaticLabels();
+    method public void setCurrentValue(int);
+    method public void setLabelFormat(java.lang.String);
+    method public void setMaxValue(int);
+    method public void setMinValue(int);
+    method public void setStaticLabels(java.lang.CharSequence[]);
+  }
+
+  public class TimePicker extends android.support.v17.leanback.widget.picker.Picker {
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet);
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24Hour();
+    method public boolean isPm();
+    method public void setHour(int);
+    method public void setIs24Hour(boolean);
+    method public void setMinute(int);
+  }
+
+}
+
+package android.support.v17.preference {
+
+  public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment {
+    ctor public BaseLeanbackPreferenceFragment();
+  }
+
+  public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment {
+    ctor public LeanbackListPreferenceDialogFragment();
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String);
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String);
+    method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter();
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener);
+    method public android.view.ViewGroup getContainer();
+    method public android.widget.TextView getTitleView();
+    method public android.widget.Checkable getWidgetView();
+    method public void onClick(android.view.View);
+  }
+
+  public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackPreferenceDialogFragment extends android.app.Fragment {
+    ctor public LeanbackPreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    field public static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
+    ctor public LeanbackPreferenceFragment();
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
+    ctor public LeanbackSettingsFragment();
+    method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+    method public abstract void onPreferenceStartInitialScreen();
+    method public void startImmersiveFragment(android.app.Fragment);
+    method public void startPreferenceFragment(android.app.Fragment);
+  }
+
+}
+
+package android.support.v4.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static java.lang.String capabilityToString(int);
+    method public static java.lang.String feedbackTypeToString(int);
+    method public static java.lang.String flagToString(int);
+    method public static boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package android.support.v4.app {
+
+  public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int);
+    method public boolean isDrawerIndicatorEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void syncState();
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class ActivityCompat extends android.support.v4.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri getReferrer(android.app.Activity);
+    method public static boolean invalidateOptionsMenu(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void requestPermissions(android.app.Activity, java.lang.String[], int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect getLaunchBounds();
+    method public static android.support.v4.app.ActivityOptionsCompat makeBasic();
+    method public static android.support.v4.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...);
+    method public static android.support.v4.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public android.support.v4.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect);
+    method public android.os.Bundle toBundle();
+    method public void update(android.support.v4.app.ActivityOptionsCompat);
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  public class AppLaunchChecker {
+    ctor public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
+    method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
+    method public static java.lang.String permissionToOp(java.lang.String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String);
+    method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder);
+  }
+
+  public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method public android.app.Dialog getDialog();
+    method public boolean getShowsDialog();
+    method public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method public android.app.Dialog onCreateDialog(android.os.Bundle);
+    method public void onDismiss(android.content.DialogInterface);
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, int);
+    method public void show(android.support.v4.app.FragmentManager, java.lang.String);
+    method public int show(android.support.v4.app.FragmentTransaction, java.lang.String);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+    ctor public Fragment();
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final boolean equals(java.lang.Object);
+    method public final android.support.v4.app.FragmentActivity getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle getArguments();
+    method public final android.support.v4.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context getContext();
+    method public java.lang.Object getEnterTransition();
+    method public java.lang.Object getExitTransition();
+    method public final android.support.v4.app.FragmentManager getFragmentManager();
+    method public final java.lang.Object getHost();
+    method public final int getId();
+    method public android.support.v4.app.LoaderManager getLoaderManager();
+    method public final android.support.v4.app.Fragment getParentFragment();
+    method public java.lang.Object getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method public final boolean getRetainInstance();
+    method public java.lang.Object getReturnTransition();
+    method public java.lang.Object getSharedElementEnterTransition();
+    method public java.lang.Object getSharedElementReturnTransition();
+    method public final java.lang.String getString(int);
+    method public final java.lang.String getString(int, java.lang.Object...);
+    method public final java.lang.String getTag();
+    method public final android.support.v4.app.Fragment getTargetFragment();
+    method public final int getTargetRequestCode();
+    method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
+    method public android.view.View getView();
+    method public final int hashCode();
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String);
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method public void onActivityCreated(android.os.Bundle);
+    method public void onActivityResult(int, int, android.content.Intent);
+    method public void onAttach(android.content.Context);
+    method public deprecated void onAttach(android.app.Activity);
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public boolean onContextItemSelected(android.view.MenuItem);
+    method public void onCreate(android.os.Bundle);
+    method public android.view.animation.Animation onCreateAnimation(int, boolean, int);
+    method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
+    method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDestroy();
+    method public void onDestroyOptionsMenu();
+    method public void onDestroyView();
+    method public void onDetach();
+    method public void onHiddenChanged(boolean);
+    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
+    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
+    method public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void onOptionsMenuClosed(android.view.Menu);
+    method public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
+    method public void onResume();
+    method public void onSaveInstanceState(android.os.Bundle);
+    method public void onStart();
+    method public void onStop();
+    method public void onViewCreated(android.view.View, android.os.Bundle);
+    method public void onViewStateRestored(android.os.Bundle);
+    method public void postponeEnterTransition();
+    method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle);
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setEnterTransition(java.lang.Object);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitTransition(java.lang.Object);
+    method public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(java.lang.Object);
+    method public void setRetainInstance(boolean);
+    method public void setReturnTransition(java.lang.Object);
+    method public void setSharedElementEnterTransition(java.lang.Object);
+    method public void setSharedElementReturnTransition(java.lang.Object);
+    method public void setTargetFragment(android.support.v4.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle);
+    method public void startActivityForResult(android.content.Intent, int);
+    method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR;
+  }
+
+  public class FragmentActivity extends android.app.Activity implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback {
+    ctor public FragmentActivity();
+    method public java.lang.Object getLastCustomNonConfigurationInstance();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method protected void onResumeFragments();
+    method public java.lang.Object onRetainCustomNonConfigurationInstance();
+    method public final java.lang.Object onRetainNonConfigurationInstance();
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method public deprecated void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method public android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public abstract android.view.View onFindViewById(int);
+    method public abstract boolean onHasView();
+  }
+
+  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 void dispatchActivityCreated();
+    method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method public void dispatchLowMemory();
+    method public void dispatchMultiWindowModeChanged(boolean);
+    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method public void dispatchPictureInPictureModeChanged(boolean);
+    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method public void doLoaderDestroy();
+    method public void doLoaderRetain();
+    method public void doLoaderStart();
+    method public void doLoaderStop(boolean);
+    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public boolean execPendingActions();
+    method public android.support.v4.app.Fragment findFragmentByWho(java.lang.String);
+    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
+    method public int getActiveFragmentsCount();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public void reportLoaderStart();
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig);
+    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
+    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
+    method public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.support.v4.app.Fragment> retainNonConfig();
+    method public android.os.Parcelable saveAllState();
+  }
+
+  public abstract class FragmentHostCallback<E> extends android.support.v4.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.view.View onFindViewById(int);
+    method public abstract E onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int);
+    method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(java.lang.String);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager {
+    ctor public FragmentManager();
+    method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.FragmentTransaction beginTransaction();
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract boolean executePendingTransactions();
+    method public abstract android.support.v4.app.Fragment findFragmentById(int);
+    method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String);
+    method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public abstract int getBackStackEntryCount();
+    method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String);
+    method public abstract android.support.v4.app.Fragment getPrimaryNavigationFragment();
+    method public abstract boolean isDestroyed();
+    method public abstract void popBackStack();
+    method public abstract void popBackStack(java.lang.String, int);
+    method public abstract void popBackStack(int, int);
+    method public abstract boolean popBackStackImmediate();
+    method public abstract boolean popBackStackImmediate(java.lang.String, int);
+    method public abstract boolean popBackStackImmediate(int, int);
+    method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static abstract interface FragmentManager.BackStackEntry {
+    method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
+    method public abstract int getBreadCrumbShortTitleRes();
+    method public abstract java.lang.CharSequence getBreadCrumbTitle();
+    method public abstract int getBreadCrumbTitleRes();
+    method public abstract int getId();
+    method public abstract java.lang.String getName();
+  }
+
+  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+  }
+
+  public static abstract interface FragmentManager.OnBackStackChangedListener {
+    method public abstract void onBackStackChanged();
+  }
+
+  public class FragmentManagerNonConfig {
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor public FragmentTransaction();
+    method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
+    method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack();
+    method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
+    method public abstract boolean isAddToBackStackAllowed();
+    method public abstract boolean isEmpty();
+    method public abstract android.support.v4.app.FragmentTransaction postOnCommit(java.lang.Runnable);
+    method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setPrimaryNavigationFragment(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction setTransition(int);
+    method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int);
+    method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class ListFragment extends android.support.v4.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public void setEmptyText(java.lang.CharSequence);
+    method public void setListAdapter(android.widget.ListAdapter);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+  public abstract class LoaderManager {
+    ctor public LoaderManager();
+    method public abstract void destroyLoader(int);
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract <D> android.support.v4.content.Loader<D> getLoader(int);
+    method public boolean hasRunningLoaders();
+    method public abstract <D> android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+    method public abstract <D> android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+  }
+
+  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+    method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle);
+    method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D);
+    method public abstract void onLoaderReset(android.support.v4.content.Loader<D>);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static java.lang.String getParentActivityName(android.app.Activity);
+    method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static java.lang.String getCategory(android.app.Notification);
+    method public static java.lang.String getChannel(android.app.Notification);
+    method public static android.os.Bundle getExtras(android.app.Notification);
+    method public static java.lang.String getGroup(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static java.lang.String getSortKey(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+    field public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+    field public static final java.lang.String EXTRA_PICTURE = "android.picture";
+    field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
+    field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
+    field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
+    field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
+    field public static final java.lang.String EXTRA_TEXT = "android.text";
+    field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_TITLE = "android.title";
+    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.app.PendingIntent getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public android.support.v4.app.RemoteInput[] getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public android.support.v4.app.RemoteInput[] getRemoteInputs();
+    method public java.lang.CharSequence getTitle();
+    field public android.app.PendingIntent actionIntent;
+    field public int icon;
+    field public java.lang.CharSequence title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
+    ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput);
+    method public android.support.v4.app.NotificationCompat.Action build();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+  }
+
+  public static abstract interface NotificationCompat.Action.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+    method public java.lang.CharSequence getCancelLabel();
+    method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method public java.lang.CharSequence getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String);
+    method public android.app.Notification build();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method public deprecated android.app.Notification getNotification();
+    method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setChannel(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setColor(int);
+    method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setDefaults(int);
+    method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setNumber(int);
+    method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPriority(int);
+    method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int);
+    method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style);
+    method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]);
+    method public android.support.v4.app.NotificationCompat.Builder setVisibility(int);
+    method public android.support.v4.app.NotificationCompat.Builder setWhen(long);
+    field public java.util.ArrayList<java.lang.String> mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.support.v4.app.NotificationCompat.CarExtender setColor(int);
+    method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation);
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.support.v4.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput);
+  }
+
+  public static abstract interface NotificationCompat.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
+    method public void addCompatExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(android.support.v4.app.NotificationCompat.MessagingStyle.Message);
+    method public static android.support.v4.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.support.v4.app.NotificationCompat.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+  }
+
+  public static abstract class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification build();
+    method public void setBuilder(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearActions();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearPages();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public java.lang.String getDismissalId();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintAmbientBigPicture();
+    method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintHideIcon();
+    method public int getHintScreenTimeout();
+    method public boolean getHintShowBackgroundOnly();
+    method public java.util.List<android.app.Notification> getPages();
+    method public boolean getStartScrollBottom();
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBridgeTag(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDismissalId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field public static final int SIZE_LARGE = 4; // 0x4
+    field public static final int SIZE_MEDIUM = 3; // 0x3
+    field public static final int SIZE_SMALL = 2; // 0x2
+    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(java.lang.String, int, java.lang.String);
+    method public abstract void cancelAll(java.lang.String);
+    method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(java.lang.String, int);
+    method public void cancelAll();
+    method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public void notify(int, android.app.Notification);
+    method public void notify(java.lang.String, int, android.app.Notification);
+    field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public final class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput {
+    method public static void addDataResultToIntent(android.support.v4.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
+    method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
+    method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getResultKey();
+    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
+    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(java.lang.String);
+    method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
+    method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+    method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+  }
+
+   class RemoteInputCompatBase {
+  }
+
+  public static abstract class RemoteInputCompatBase.RemoteInput {
+    ctor public RemoteInputCompatBase.RemoteInput();
+    method protected abstract boolean getAllowFreeFormInput();
+    method protected abstract java.util.Set<java.lang.String> getAllowedDataTypes();
+    method protected abstract java.lang.CharSequence[] getChoices();
+    method protected abstract android.os.Bundle getExtras();
+    method protected abstract java.lang.CharSequence getLabel();
+    method protected abstract java.lang.String getResultKey();
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName getCallingActivity(android.app.Activity);
+    method public static java.lang.String getCallingPackage(android.app.Activity);
+    field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName getCallingActivity();
+    method public android.graphics.drawable.Drawable getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable getCallingApplicationIcon();
+    method public java.lang.CharSequence getCallingApplicationLabel();
+    method public java.lang.String getCallingPackage();
+    method public java.lang.String[] getEmailBcc();
+    method public java.lang.String[] getEmailCc();
+    method public java.lang.String[] getEmailTo();
+    method public java.lang.String getHtmlText();
+    method public android.net.Uri getStream();
+    method public android.net.Uri getStream(int);
+    method public int getStreamCount();
+    method public java.lang.String getSubject();
+    method public java.lang.CharSequence getText();
+    method public java.lang.String getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
+    method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable);
+    method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
+    method public void onRejectSharedElements(java.util.List<android.view.View>);
+    method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String>, java.util.List<android.view.View>, android.support.v4.app.SharedElementCallback.OnSharedElementsReadyListener);
+  }
+
+  public static abstract interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public abstract void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable {
+    method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public static android.support.v4.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent editIntentAt(int);
+    method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context);
+    method public deprecated android.content.Intent getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent[] getIntents();
+    method public android.app.PendingIntent getPendingIntent(int, int);
+    method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle);
+    method public deprecated java.util.Iterator<android.content.Intent> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle);
+  }
+
+  public static abstract interface TaskStackBuilder.SupportParentable {
+    method public abstract android.content.Intent getSupportParentActivityIntent();
+  }
+
+}
+
+package android.support.v4.content {
+
+  public abstract class AsyncTaskLoader<D> extends android.support.v4.content.Loader {
+    ctor public AsyncTaskLoader(android.content.Context);
+    method public void cancelLoadInBackground();
+    method public boolean isLoadInBackgroundCanceled();
+    method public abstract D loadInBackground();
+    method public void onCanceled(D);
+    method protected D onLoadInBackground();
+    method public void setUpdateThrottle(long);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    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 java.io.File getDataDir(android.content.Context);
+    method public static final 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[] getObbDirs(android.content.Context);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
+  }
+
+  public class CursorLoader extends android.support.v4.content.AsyncTaskLoader {
+    ctor public CursorLoader(android.content.Context);
+    ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public void deliverResult(android.database.Cursor);
+    method public java.lang.String[] getProjection();
+    method public java.lang.String getSelection();
+    method public java.lang.String[] getSelectionArgs();
+    method public java.lang.String getSortOrder();
+    method public android.net.Uri getUri();
+    method public android.database.Cursor loadInBackground();
+    method public void onCanceled(android.database.Cursor);
+    method public void setProjection(java.lang.String[]);
+    method public void setSelection(java.lang.String);
+    method public void setSelectionArgs(java.lang.String[]);
+    method public void setSortOrder(java.lang.String);
+    method public void setUri(android.net.Uri);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File);
+    method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
+  public final class IntentCompat {
+    method public static deprecated android.content.Intent makeMainActivity(android.content.ComponentName);
+    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
+    method public static deprecated android.content.Intent makeRestartActivityTask(android.content.ComponentName);
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final deprecated int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
+    field public static final deprecated int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
+  }
+
+  public class Loader<D> {
+    ctor public Loader(android.content.Context);
+    method public void abandon();
+    method public boolean cancelLoad();
+    method public void commitContentChanged();
+    method public java.lang.String dataToString(D);
+    method public void deliverCancellation();
+    method public void deliverResult(D);
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void forceLoad();
+    method public android.content.Context getContext();
+    method public int getId();
+    method public boolean isAbandoned();
+    method public boolean isReset();
+    method public boolean isStarted();
+    method protected void onAbandon();
+    method protected boolean onCancelLoad();
+    method public void onContentChanged();
+    method protected void onForceLoad();
+    method protected void onReset();
+    method protected void onStartLoading();
+    method protected void onStopLoading();
+    method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+    method public void reset();
+    method public void rollbackContentChanged();
+    method public final void startLoading();
+    method public void stopLoading();
+    method public boolean takeContentChanged();
+    method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+  }
+
+  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+    ctor public Loader.ForceLoadContentObserver();
+  }
+
+  public static abstract interface Loader.OnLoadCanceledListener<D> {
+    method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>);
+  }
+
+  public static abstract interface Loader.OnLoadCompleteListener<D> {
+    method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D);
+  }
+
+  public final class LocalBroadcastManager {
+    method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context);
+    method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+    method public boolean sendBroadcast(android.content.Intent);
+    method public void sendBroadcastSync(android.content.Intent);
+    method public void unregisterReceiver(android.content.BroadcastReceiver);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(java.lang.String, java.lang.String);
+    method public static java.lang.String matches(java.lang.String, java.lang.String[]);
+    method public static java.lang.String matches(java.lang.String[], java.lang.String);
+    method public static java.lang.String[] matchesMany(java.lang.String[], java.lang.String);
+  }
+
+  public final deprecated class ParallelExecutorCompat {
+    method public static deprecated java.util.concurrent.Executor getParallelExecutor();
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String);
+    method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String);
+    method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String);
+    method public static int checkSelfPermission(android.content.Context, java.lang.String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  public static abstract class PermissionChecker.PermissionResult implements java.lang.annotation.Annotation {
+  }
+
+  public final class SharedPreferencesCompat {
+  }
+
+  public static final class SharedPreferencesCompat.EditorCompat {
+    method public void apply(android.content.SharedPreferences.Editor);
+    method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance();
+  }
+
+  public abstract class WakefulBroadcastReceiver extends android.content.BroadcastReceiver {
+    ctor public WakefulBroadcastReceiver();
+    method public static boolean completeWakefulIntent(android.content.Intent);
+    method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent);
+  }
+
+}
+
+package android.support.v4.content.pm {
+
+  public final class ActivityInfoCompat {
+    field public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public class ShortcutInfoCompat {
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, java.lang.String);
+    method public android.support.v4.content.pm.ShortcutInfoCompat build();
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(java.lang.CharSequence);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.graphics.Bitmap);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(int);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent[]);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setLongLabel(java.lang.CharSequence);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setShortLabel(java.lang.CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    ctor public ShortcutManagerCompat();
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean requestPinShortcut(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat, android.content.IntentSender);
+  }
+
+}
+
+package android.support.v4.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+    method public static deprecated int getScreenHeightDp(android.content.res.Resources);
+    method public static deprecated int getScreenWidthDp(android.content.res.Resources);
+    method public static deprecated int getSmallestScreenWidthDp(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+  }
+
+}
+
+package android.support.v4.database {
+
+  public final class DatabaseUtilsCompat {
+    method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]);
+    method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public final class ColorUtils {
+    method public static int HSLToColor(float[]);
+    method public static int LABToColor(double, double, double);
+    method public static void LABToXYZ(double, double, double, double[]);
+    method public static void RGBToHSL(int, int, int, float[]);
+    method public static void RGBToLAB(int, int, int, double[]);
+    method public static void RGBToXYZ(int, int, int, double[]);
+    method public static int XYZToColor(double, double, double);
+    method public static void XYZToLAB(double, double, double, double[]);
+    method public static int blendARGB(int, int, float);
+    method public static void blendHSL(float[], float[], float, float[]);
+    method public static void blendLAB(double[], double[], double, double[]);
+    method public static double calculateContrast(int, int);
+    method public static double calculateLuminance(int);
+    method public static int calculateMinimumAlpha(int, int, float);
+    method public static void colorToHSL(int, float[]);
+    method public static void colorToLAB(int, double[]);
+    method public static void colorToXYZ(int, double[]);
+    method public static int compositeColors(int, int);
+    method public static double distanceEuclidean(double[], double[]);
+    method public static int setAlphaComponent(int, int);
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setCornerRadius(float);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package android.support.v4.hardware.display {
+
+  public abstract class DisplayManagerCompat {
+    method public abstract android.view.Display getDisplay(int);
+    method public abstract android.view.Display[] getDisplays();
+    method public abstract android.view.Display[] getDisplays(java.lang.String);
+    method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package android.support.v4.hardware.fingerprint {
+
+  public final class FingerprintManagerCompat {
+    method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler);
+    method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
+  }
+
+  public static abstract class FingerprintManagerCompat.AuthenticationCallback {
+    ctor public FingerprintManagerCompat.AuthenticationCallback();
+    method public void onAuthenticationError(int, java.lang.CharSequence);
+    method public void onAuthenticationFailed();
+    method public void onAuthenticationHelp(int, java.lang.CharSequence);
+    method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult);
+  }
+
+  public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject);
+    method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject();
+  }
+
+  public static class FingerprintManagerCompat.CryptoObject {
+    ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method public javax.crypto.Cipher getCipher();
+    method public javax.crypto.Mac getMac();
+    method public java.security.Signature getSignature();
+  }
+
+}
+
+package android.support.v4.math {
+
+  public class MathUtils {
+    ctor public MathUtils();
+    method public static float clamp(float, int, int);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+  }
+
+}
+
+package android.support.v4.media {
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle getExtras();
+    method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method public java.lang.String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(java.lang.String);
+    method public void unsubscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public static abstract class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(java.lang.String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem fromMediaItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem> fromMediaItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public java.lang.String getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static abstract class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>, android.os.Bundle);
+    method public void onError(java.lang.String);
+    method public void onError(java.lang.String, android.os.Bundle);
+  }
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public void notifyChildrenChanged(java.lang.String);
+    method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>, android.os.Bundle);
+    method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendResult(T);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object);
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public android.net.Uri getIconUri();
+    method public java.lang.Object getMediaDescription();
+    method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR;
+    field public static final java.lang.String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(java.lang.String);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object);
+    method public android.graphics.Bitmap getBitmap(java.lang.String);
+    method public android.os.Bundle getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getLong(java.lang.String);
+    method public java.lang.Object getMediaMetadata();
+    method public android.support.v4.media.RatingCompat getRating(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR;
+    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat);
+    method public android.support.v4.media.MediaMetadataCompat build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object);
+    method public float getPercentRating();
+    method public java.lang.Object getRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat newStarRating(int, float);
+    method public static android.support.v4.media.RatingCompat newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+  public abstract class TransportController {
+    ctor public TransportController();
+    method public abstract int getBufferPercentage();
+    method public abstract long getCurrentPosition();
+    method public abstract long getDuration();
+    method public abstract int getTransportControlFlags();
+    method public abstract boolean isPlaying();
+    method public abstract void pausePlaying();
+    method public abstract void registerStateListener(android.support.v4.media.TransportStateListener);
+    method public abstract void seekTo(long);
+    method public abstract void startPlaying();
+    method public abstract void stopPlaying();
+    method public abstract void unregisterStateListener(android.support.v4.media.TransportStateListener);
+  }
+
+  public class TransportMediator extends android.support.v4.media.TransportController {
+    ctor public TransportMediator(android.app.Activity, android.support.v4.media.TransportPerformer);
+    ctor public TransportMediator(android.view.View, android.support.v4.media.TransportPerformer);
+    method public void destroy();
+    method public boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public int getBufferPercentage();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public java.lang.Object getRemoteControlClient();
+    method public int getTransportControlFlags();
+    method public boolean isPlaying();
+    method public void pausePlaying();
+    method public void refreshState();
+    method public void registerStateListener(android.support.v4.media.TransportStateListener);
+    method public void seekTo(long);
+    method public void startPlaying();
+    method public void stopPlaying();
+    method public void unregisterStateListener(android.support.v4.media.TransportStateListener);
+    field public static final int FLAG_KEY_MEDIA_FAST_FORWARD = 64; // 0x40
+    field public static final int FLAG_KEY_MEDIA_NEXT = 128; // 0x80
+    field public static final int FLAG_KEY_MEDIA_PAUSE = 16; // 0x10
+    field public static final int FLAG_KEY_MEDIA_PLAY = 4; // 0x4
+    field public static final int FLAG_KEY_MEDIA_PLAY_PAUSE = 8; // 0x8
+    field public static final int FLAG_KEY_MEDIA_PREVIOUS = 1; // 0x1
+    field public static final int FLAG_KEY_MEDIA_REWIND = 2; // 0x2
+    field public static final int FLAG_KEY_MEDIA_STOP = 32; // 0x20
+    field public static final int KEYCODE_MEDIA_PAUSE = 127; // 0x7f
+    field public static final int KEYCODE_MEDIA_PLAY = 126; // 0x7e
+    field public static final int KEYCODE_MEDIA_RECORD = 130; // 0x82
+  }
+
+  public abstract class TransportPerformer {
+    ctor public TransportPerformer();
+    method public void onAudioFocusChange(int);
+    method public int onGetBufferPercentage();
+    method public abstract long onGetCurrentPosition();
+    method public abstract long onGetDuration();
+    method public int onGetTransportControlFlags();
+    method public abstract boolean onIsPlaying();
+    method public boolean onMediaButtonDown(int, android.view.KeyEvent);
+    method public boolean onMediaButtonUp(int, android.view.KeyEvent);
+    method public abstract void onPause();
+    method public abstract void onSeekTo(long);
+    method public abstract void onStart();
+    method public abstract void onStop();
+  }
+
+  public class TransportStateListener {
+    ctor public TransportStateListener();
+    method public void onPlayingChanged(android.support.v4.media.TransportController);
+    method public void onTransportControlsChanged(android.support.v4.media.TransportController);
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public java.lang.Object getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static abstract class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat);
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
+    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
+    method public android.os.Bundle getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat getMediaController(android.app.Activity);
+    method public java.lang.Object getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat getMetadata();
+    method public java.lang.String getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
+    method public java.lang.CharSequence getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent getSessionActivity();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
+    method public boolean isShuffleModeEnabled();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
+    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo);
+    method public void onExtrasChanged(android.os.Bundle);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onRepeatModeChanged(int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public void onShuffleModeChanged(boolean);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public static abstract class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void playFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void playFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
+    method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
+    method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleModeEnabled(boolean);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public static android.support.v4.media.session.MediaSessionCompat fromMediaSession(android.content.Context, java.lang.Object);
+    method public android.support.v4.media.session.MediaControllerCompat getController();
+    method public java.lang.Object getMediaSession();
+    method public java.lang.Object getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isActive();
+    method public static deprecated android.support.v4.media.session.MediaSessionCompat obtain(android.content.Context, java.lang.Object);
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler);
+    method public void setExtras(android.os.Bundle);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void setQueueTitle(java.lang.CharSequence);
+    method public void setRatingType(int);
+    method public void setRepeatMode(int);
+    method public void setSessionActivity(android.app.PendingIntent);
+    method public void setShuffleModeEnabled(boolean);
+    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+  }
+
+  public static abstract class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void onCustomAction(java.lang.String, android.os.Bundle);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleModeEnabled(boolean);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static abstract interface MediaSessionCompat.OnActiveChangeListener {
+    method public abstract void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem fromQueueItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> fromQueueItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getQueueId();
+    method public java.lang.Object getQueueItem();
+    method public static deprecated android.support.v4.media.session.MediaSessionCompat.QueueItem obtain(java.lang.Object);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object);
+    method public java.lang.Object getToken();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object);
+    method public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions();
+    method public int getErrorCode();
+    method public java.lang.CharSequence getErrorMessage();
+    method public android.os.Bundle getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public java.lang.Object getPlaybackState();
+    method public long getPosition();
+    method public int getState();
+    method public static int toKeyCode(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction);
+    method public android.support.v4.media.session.PlaybackStateCompat build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long);
+    method public deprecated android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(int, java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object);
+    method public java.lang.String getAction();
+    method public java.lang.Object getCustomAction();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle);
+  }
+
+}
+
+package android.support.v4.net {
+
+  public final class ConnectivityManagerCompat {
+    method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class TrafficStatsCompat {
+    method public static deprecated void clearThreadStatsTag();
+    method public static deprecated int getThreadStatsTag();
+    method public static deprecated void incrementOperationCount(int);
+    method public static deprecated void incrementOperationCount(int, int);
+    method public static deprecated void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void tagSocket(java.net.Socket) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void untagSocket(java.net.Socket) throws java.net.SocketException;
+  }
+
+}
+
+package android.support.v4.os {
+
+  public final deprecated class AsyncTaskCompat {
+    method public static deprecated <Params, Progress, Result> android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
+  }
+
+  public class BuildCompat {
+    method public static boolean isAtLeastN();
+    method public static boolean isAtLeastNMR1();
+    method public static boolean isAtLeastO();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public java.lang.Object getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener);
+    method public void throwIfCanceled();
+  }
+
+  public static abstract interface CancellationSignal.OnCancelListener {
+    method public abstract void onCancel();
+  }
+
+  public final class EnvironmentCompat {
+    method public static java.lang.String getStorageState(java.io.File);
+    field public static final java.lang.String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(java.lang.String);
+  }
+
+  public final class ParcelableCompat {
+    method public static <T> android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>);
+  }
+
+  public abstract interface ParcelableCompatCreatorCallbacks<T> {
+    method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader);
+    method public abstract T[] newArray(int);
+  }
+
+  public final class TraceCompat {
+    method public static void beginSection(java.lang.String);
+    method public static void endSection();
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package android.support.v4.print {
+
+  public final class PrintHelper {
+    ctor public PrintHelper(android.content.Context);
+    method public int getColorMode();
+    method public int getOrientation();
+    method public int getScaleMode();
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap);
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback);
+    method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException;
+    method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException;
+    method public void setColorMode(int);
+    method public void setOrientation(int);
+    method public void setScaleMode(int);
+    method public static boolean systemSupportsPrint();
+    field public static final int COLOR_MODE_COLOR = 2; // 0x2
+    field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
+    field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
+    field public static final int ORIENTATION_PORTRAIT = 2; // 0x2
+    field public static final int SCALE_MODE_FILL = 2; // 0x2
+    field public static final int SCALE_MODE_FIT = 1; // 0x1
+  }
+
+  public static abstract interface PrintHelper.OnPrintFinishCallback {
+    method public abstract void onFinish();
+  }
+
+}
+
+package android.support.v4.provider {
+
+  public abstract class DocumentFile {
+    method public abstract boolean canRead();
+    method public abstract boolean canWrite();
+    method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String);
+    method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String);
+    method public abstract boolean delete();
+    method public abstract boolean exists();
+    method public android.support.v4.provider.DocumentFile findFile(java.lang.String);
+    method public static android.support.v4.provider.DocumentFile fromFile(java.io.File);
+    method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri);
+    method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri);
+    method public abstract java.lang.String getName();
+    method public android.support.v4.provider.DocumentFile getParentFile();
+    method public abstract java.lang.String getType();
+    method public abstract android.net.Uri getUri();
+    method public abstract boolean isDirectory();
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public abstract boolean isFile();
+    method public abstract boolean isVirtual();
+    method public abstract long lastModified();
+    method public abstract long length();
+    method public abstract android.support.v4.provider.DocumentFile[] listFiles();
+    method public abstract boolean renameTo(java.lang.String);
+  }
+
+}
+
+package android.support.v4.text {
+
+  public final class BidiFormatter {
+    method public static android.support.v4.text.BidiFormatter getInstance();
+    method public static android.support.v4.text.BidiFormatter getInstance(boolean);
+    method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale);
+    method public boolean getStereoReset();
+    method public boolean isRtl(java.lang.String);
+    method public boolean isRtl(java.lang.CharSequence);
+    method public boolean isRtlContext();
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.String unicodeWrap(java.lang.String, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale);
+    method public android.support.v4.text.BidiFormatter build();
+    method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat);
+    method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean);
+  }
+
+  public final class ICUCompat {
+    method public static java.lang.String maximizeAndGetScript(java.util.Locale);
+  }
+
+  public abstract interface TextDirectionHeuristicCompat {
+    method public abstract boolean isRtl(char[], int, int);
+    method public abstract boolean isRtl(java.lang.CharSequence, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale);
+    method public static java.lang.String htmlEncode(java.lang.String);
+    field public static final java.util.Locale ROOT;
+  }
+
+}
+
+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);
+  }
+
+  public static abstract class LinkifyCompat.LinkifyMask implements java.lang.annotation.Annotation {
+  }
+
+}
+
+package android.support.v4.util {
+
+  public class ArrayMap<K, V> extends android.support.v4.util.SimpleArrayMap implements java.util.Map {
+    ctor public ArrayMap();
+    ctor public ArrayMap(int);
+    ctor public ArrayMap(android.support.v4.util.SimpleArrayMap);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+    method public java.util.Set<K> keySet();
+    method public void putAll(java.util.Map<? extends K, ? extends V>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public java.util.Collection<V> values();
+  }
+
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    ctor public ArraySet();
+    ctor public ArraySet(int);
+    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
+    method public boolean add(E);
+    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(java.lang.Object);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public void ensureCapacity(int);
+    method public int indexOf(java.lang.Object);
+    method public boolean isEmpty();
+    method public java.util.Iterator<E> iterator();
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public E removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public int size();
+    method public java.lang.Object[] toArray();
+    method public <T> T[] toArray(T[]);
+    method public E valueAt(int);
+  }
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream);
+    method public void finishWrite(java.io.FileOutputStream);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public final class CircularArray<E> {
+    ctor public CircularArray();
+    ctor public CircularArray(int);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public void clear();
+    method public E get(int);
+    method public E getFirst();
+    method public E getLast();
+    method public boolean isEmpty();
+    method public E popFirst();
+    method public E popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public final class CircularIntArray {
+    ctor public CircularIntArray();
+    ctor public CircularIntArray(int);
+    method public void addFirst(int);
+    method public void addLast(int);
+    method public void clear();
+    method public int get(int);
+    method public int getFirst();
+    method public int getLast();
+    method public boolean isEmpty();
+    method public int popFirst();
+    method public int popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public class LongSparseArray<E> {
+    ctor public LongSparseArray();
+    ctor public LongSparseArray(int);
+    method public void append(long, E);
+    method public void clear();
+    method public android.support.v4.util.LongSparseArray<E> clone();
+    method public void delete(long);
+    method public E get(long);
+    method public E get(long, E);
+    method public int indexOfKey(long);
+    method public int indexOfValue(E);
+    method public long keyAt(int);
+    method public void put(long, E);
+    method public void remove(long);
+    method public void removeAt(int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+  public class LruCache<K, V> {
+    ctor public LruCache(int);
+    method protected V create(K);
+    method public final synchronized int createCount();
+    method protected void entryRemoved(boolean, K, V, V);
+    method public final void evictAll();
+    method public final synchronized int evictionCount();
+    method public final V get(K);
+    method public final synchronized int hitCount();
+    method public final synchronized int maxSize();
+    method public final synchronized int missCount();
+    method public final V put(K, V);
+    method public final synchronized int putCount();
+    method public final V remove(K);
+    method public void resize(int);
+    method public final synchronized int size();
+    method protected int sizeOf(K, V);
+    method public final synchronized java.util.Map<K, V> snapshot();
+    method public final synchronized java.lang.String toString();
+    method public void trimToSize(int);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F, S);
+    method public static <A, B> android.support.v4.util.Pair<A, B> create(A, B);
+    field public final F first;
+    field public final S second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static abstract interface Pools.Pool<T> {
+    method public abstract T acquire();
+    method public abstract boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements android.support.v4.util.Pools.Pool {
+    ctor public Pools.SimplePool(int);
+    method public T acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends android.support.v4.util.Pools.SimplePool {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public class SimpleArrayMap<K, V> {
+    ctor public SimpleArrayMap();
+    ctor public SimpleArrayMap(int);
+    ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap);
+    method public void clear();
+    method public boolean containsKey(java.lang.Object);
+    method public boolean containsValue(java.lang.Object);
+    method public void ensureCapacity(int);
+    method public V get(java.lang.Object);
+    method public int indexOfKey(java.lang.Object);
+    method public boolean isEmpty();
+    method public K keyAt(int);
+    method public V put(K, V);
+    method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>);
+    method public V remove(java.lang.Object);
+    method public V removeAt(int);
+    method public V setValueAt(int, V);
+    method public int size();
+    method public V valueAt(int);
+  }
+
+  public class SparseArrayCompat<E> {
+    ctor public SparseArrayCompat();
+    ctor public SparseArrayCompat(int);
+    method public void append(int, E);
+    method public void clear();
+    method public android.support.v4.util.SparseArrayCompat<E> clone();
+    method public void delete(int);
+    method public E get(int);
+    method public E get(int, E);
+    method public int indexOfKey(int);
+    method public int indexOfValue(E);
+    method public int keyAt(int);
+    method public void put(int, E);
+    method public void remove(int);
+    method public void removeAt(int);
+    method public void removeAtRange(int, int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+}
+
+package android.support.v4.view {
+
+  public abstract class AbsSavedState implements android.os.Parcelable {
+    ctor protected AbsSavedState(android.os.Parcelable);
+    ctor protected AbsSavedState(android.os.Parcel);
+    ctor protected AbsSavedState(android.os.Parcel, java.lang.ClassLoader);
+    method public int describeContents();
+    method public final android.os.Parcelable getSuperState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.AbsSavedState> CREATOR;
+    field public static final android.support.v4.view.AbsSavedState EMPTY_STATE;
+  }
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener);
+  }
+
+  public static abstract interface ActionProvider.VisibilityListener {
+    method public abstract void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class AsyncLayoutInflater {
+    ctor public AsyncLayoutInflater(android.content.Context);
+    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+  }
+
+  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final deprecated class KeyEventCompat {
+    method public static deprecated boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object);
+    method public static deprecated java.lang.Object getKeyDispatcherState(android.view.View);
+    method public static deprecated boolean hasModifiers(android.view.KeyEvent, int);
+    method public static deprecated boolean hasNoModifiers(android.view.KeyEvent);
+    method public static deprecated boolean isCtrlPressed(android.view.KeyEvent);
+    method public static deprecated boolean isTracking(android.view.KeyEvent);
+    method public static deprecated boolean metaStateHasModifiers(int, int);
+    method public static deprecated boolean metaStateHasNoModifiers(int);
+    method public static deprecated int normalizeMetaState(int);
+    method public static deprecated void startTracking(android.view.KeyEvent);
+  }
+
+  public final class LayoutInflaterCompat {
+    method public static deprecated android.support.v4.view.LayoutInflaterFactory getFactory(android.view.LayoutInflater);
+    method public static deprecated void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  public abstract deprecated interface LayoutInflaterFactory {
+    method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+  }
+
+  public final class MenuItemCompat {
+    method public static boolean collapseActionView(android.view.MenuItem);
+    method public static boolean expandActionView(android.view.MenuItem);
+    method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
+    method public static android.view.View getActionView(android.view.MenuItem);
+    method public static java.lang.CharSequence getContentDescription(android.view.MenuItem);
+    method public static java.lang.CharSequence getTooltipText(android.view.MenuItem);
+    method public static boolean isActionViewExpanded(android.view.MenuItem);
+    method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
+    method public static android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
+    method public static android.view.MenuItem setActionView(android.view.MenuItem, int);
+    method public static void setContentDescription(android.view.MenuItem, java.lang.CharSequence);
+    method public static android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
+    method public static void setShowAsAction(android.view.MenuItem, int);
+    method public static void setTooltipText(android.view.MenuItem, java.lang.CharSequence);
+    field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  public static abstract interface MenuItemCompat.OnActionExpandListener {
+    method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem);
+    method public abstract boolean onMenuItemActionExpand(android.view.MenuItem);
+  }
+
+  public final class MotionEventCompat {
+    method public static deprecated int findPointerIndex(android.view.MotionEvent, int);
+    method public static deprecated int getActionIndex(android.view.MotionEvent);
+    method public static deprecated int getActionMasked(android.view.MotionEvent);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int, int);
+    method public static deprecated int getButtonState(android.view.MotionEvent);
+    method public static deprecated int getPointerCount(android.view.MotionEvent);
+    method public static deprecated int getPointerId(android.view.MotionEvent, int);
+    method public static deprecated int getSource(android.view.MotionEvent);
+    method public static deprecated float getX(android.view.MotionEvent, int);
+    method public static deprecated float getY(android.view.MotionEvent, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field public static final deprecated int ACTION_HOVER_ENTER = 9; // 0x9
+    field public static final deprecated int ACTION_HOVER_EXIT = 10; // 0xa
+    field public static final deprecated int ACTION_HOVER_MOVE = 7; // 0x7
+    field public static final deprecated int ACTION_MASK = 255; // 0xff
+    field public static final deprecated int ACTION_POINTER_DOWN = 5; // 0x5
+    field public static final deprecated int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field public static final deprecated int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field public static final deprecated int ACTION_POINTER_UP = 6; // 0x6
+    field public static final deprecated int ACTION_SCROLL = 8; // 0x8
+    field public static final deprecated int AXIS_BRAKE = 23; // 0x17
+    field public static final deprecated int AXIS_DISTANCE = 24; // 0x18
+    field public static final deprecated int AXIS_GAS = 22; // 0x16
+    field public static final deprecated int AXIS_GENERIC_1 = 32; // 0x20
+    field public static final deprecated int AXIS_GENERIC_10 = 41; // 0x29
+    field public static final deprecated int AXIS_GENERIC_11 = 42; // 0x2a
+    field public static final deprecated int AXIS_GENERIC_12 = 43; // 0x2b
+    field public static final deprecated int AXIS_GENERIC_13 = 44; // 0x2c
+    field public static final deprecated int AXIS_GENERIC_14 = 45; // 0x2d
+    field public static final deprecated int AXIS_GENERIC_15 = 46; // 0x2e
+    field public static final deprecated int AXIS_GENERIC_16 = 47; // 0x2f
+    field public static final deprecated int AXIS_GENERIC_2 = 33; // 0x21
+    field public static final deprecated int AXIS_GENERIC_3 = 34; // 0x22
+    field public static final deprecated int AXIS_GENERIC_4 = 35; // 0x23
+    field public static final deprecated int AXIS_GENERIC_5 = 36; // 0x24
+    field public static final deprecated int AXIS_GENERIC_6 = 37; // 0x25
+    field public static final deprecated int AXIS_GENERIC_7 = 38; // 0x26
+    field public static final deprecated int AXIS_GENERIC_8 = 39; // 0x27
+    field public static final deprecated int AXIS_GENERIC_9 = 40; // 0x28
+    field public static final deprecated int AXIS_HAT_X = 15; // 0xf
+    field public static final deprecated int AXIS_HAT_Y = 16; // 0x10
+    field public static final deprecated int AXIS_HSCROLL = 10; // 0xa
+    field public static final deprecated int AXIS_LTRIGGER = 17; // 0x11
+    field public static final deprecated int AXIS_ORIENTATION = 8; // 0x8
+    field public static final deprecated int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field public static final deprecated int AXIS_RTRIGGER = 18; // 0x12
+    field public static final deprecated int AXIS_RUDDER = 20; // 0x14
+    field public static final deprecated int AXIS_RX = 12; // 0xc
+    field public static final deprecated int AXIS_RY = 13; // 0xd
+    field public static final deprecated int AXIS_RZ = 14; // 0xe
+    field public static final deprecated int AXIS_SIZE = 3; // 0x3
+    field public static final deprecated int AXIS_THROTTLE = 19; // 0x13
+    field public static final deprecated int AXIS_TILT = 25; // 0x19
+    field public static final deprecated int AXIS_TOOL_MAJOR = 6; // 0x6
+    field public static final deprecated int AXIS_TOOL_MINOR = 7; // 0x7
+    field public static final deprecated int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field public static final deprecated int AXIS_TOUCH_MINOR = 5; // 0x5
+    field public static final deprecated int AXIS_VSCROLL = 9; // 0x9
+    field public static final deprecated int AXIS_WHEEL = 21; // 0x15
+    field public static final deprecated int AXIS_X = 0; // 0x0
+    field public static final deprecated int AXIS_Y = 1; // 0x1
+    field public static final deprecated int AXIS_Z = 11; // 0xb
+    field public static final deprecated int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public abstract interface NestedScrollingChild {
+    method public abstract boolean dispatchNestedFling(float, float, boolean);
+    method public abstract boolean dispatchNestedPreFling(float, float);
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public abstract boolean hasNestedScrollingParent();
+    method public abstract boolean isNestedScrollingEnabled();
+    method public abstract void setNestedScrollingEnabled(boolean);
+    method public abstract boolean startNestedScroll(int);
+    method public abstract void stopNestedScroll();
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public void stopNestedScroll();
+  }
+
+  public abstract interface NestedScrollingParent {
+    method public abstract int getNestedScrollAxes();
+    method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public abstract void onStopNestedScroll(android.view.View);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public abstract interface OnApplyWindowInsetsListener {
+    method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+  }
+
+  public abstract class PagerAdapter {
+    ctor public PagerAdapter();
+    method public void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void destroyItem(android.view.View, int, java.lang.Object);
+    method public void finishUpdate(android.view.ViewGroup);
+    method public deprecated void finishUpdate(android.view.View);
+    method public abstract int getCount();
+    method public int getItemPosition(java.lang.Object);
+    method public java.lang.CharSequence getPageTitle(int);
+    method public float getPageWidth(int);
+    method public java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated java.lang.Object instantiateItem(android.view.View, int);
+    method public abstract boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public void notifyDataSetChanged();
+    method public void registerDataSetObserver(android.database.DataSetObserver);
+    method public void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public android.os.Parcelable saveState();
+    method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object);
+    method public void startUpdate(android.view.ViewGroup);
+    method public deprecated void startUpdate(android.view.View);
+    method public void unregisterDataSetObserver(android.database.DataSetObserver);
+    field public static final int POSITION_NONE = -2; // 0xfffffffe
+    field public static final int POSITION_UNCHANGED = -1; // 0xffffffff
+  }
+
+  public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip {
+    ctor public PagerTabStrip(android.content.Context);
+    ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet);
+    method public boolean getDrawFullUnderline();
+    method public int getTabIndicatorColor();
+    method public void setDrawFullUnderline(boolean);
+    method public void setTabIndicatorColor(int);
+    method public void setTabIndicatorColorResource(int);
+  }
+
+  public class PagerTitleStrip extends android.view.ViewGroup {
+    ctor public PagerTitleStrip(android.content.Context);
+    ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet);
+    method public int getTextSpacing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setGravity(int);
+    method public void setNonPrimaryAlpha(float);
+    method public void setTextColor(int);
+    method public void setTextSize(int, float);
+    method public void setTextSpacing(int);
+  }
+
+  public final class PointerIconCompat {
+    method public static android.support.v4.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method public static android.support.v4.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static android.support.v4.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static boolean isQuickScaleEnabled(java.lang.Object);
+    method public static void setQuickScaleEnabled(java.lang.Object, boolean);
+  }
+
+  public abstract interface ScrollingView {
+    method public abstract int computeHorizontalScrollExtent();
+    method public abstract int computeHorizontalScrollOffset();
+    method public abstract int computeHorizontalScrollRange();
+    method public abstract int computeVerticalScrollExtent();
+    method public abstract int computeVerticalScrollOffset();
+    method public abstract int computeVerticalScrollRange();
+  }
+
+  public abstract interface TintableBackgroundView {
+    method public abstract android.content.res.ColorStateList getSupportBackgroundTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode();
+    method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final deprecated class VelocityTrackerCompat {
+    method public static deprecated float getXVelocity(android.view.VelocityTracker, int);
+    method public static deprecated float getYVelocity(android.view.VelocityTracker, int);
+  }
+
+  public class ViewCompat {
+    ctor protected ViewCompat();
+    method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method public static boolean canScrollHorizontally(android.view.View, int);
+    method public static boolean canScrollVertically(android.view.View, int);
+    method public static deprecated int combineMeasuredStates(int, int);
+    method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public static deprecated float getAlpha(android.view.View);
+    method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect getClipBounds(android.view.View);
+    method public static android.view.Display getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method public static deprecated int getLayerType(android.view.View);
+    method public static int getLayoutDirection(android.view.View);
+    method public static deprecated android.graphics.Matrix getMatrix(android.view.View);
+    method public static deprecated int getMeasuredHeightAndState(android.view.View);
+    method public static deprecated int getMeasuredState(android.view.View);
+    method public static deprecated int getMeasuredWidthAndState(android.view.View);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static deprecated int getOverScrollMode(android.view.View);
+    method public static int getPaddingEnd(android.view.View);
+    method public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent getParentForAccessibility(android.view.View);
+    method public static deprecated float getPivotX(android.view.View);
+    method public static deprecated float getPivotY(android.view.View);
+    method public static deprecated float getRotation(android.view.View);
+    method public static deprecated float getRotationX(android.view.View);
+    method public static deprecated float getRotationY(android.view.View);
+    method public static deprecated float getScaleX(android.view.View);
+    method public static deprecated float getScaleY(android.view.View);
+    method public static int getScrollIndicators(android.view.View);
+    method public static java.lang.String getTransitionName(android.view.View);
+    method public static deprecated float getTranslationX(android.view.View);
+    method public static deprecated float getTranslationY(android.view.View);
+    method public static float getTranslationZ(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method public static deprecated float getX(android.view.View);
+    method public static deprecated float getY(android.view.View);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method public static deprecated boolean isOpaque(android.view.View);
+    method public static boolean isPaddingRelative(android.view.View);
+    method public static deprecated void jumpDrawablesToCurrentState(android.view.View);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public static void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, java.lang.Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long);
+    method public static void requestApplyInsets(android.view.View);
+    method public static deprecated int resolveSizeAndState(int, int, int);
+    method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method public static deprecated void setActivated(android.view.View, boolean);
+    method public static deprecated void setAlpha(android.view.View, float);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode);
+    method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect);
+    method public static void setElevation(android.view.View, float);
+    method public static void setFitsSystemWindows(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setLabelFor(android.view.View, int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint);
+    method public static deprecated void setLayerType(android.view.View, int, android.graphics.Paint);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener);
+    method public static deprecated void setOverScrollMode(android.view.View, int);
+    method public static void setPaddingRelative(android.view.View, int, int, int, int);
+    method public static deprecated void setPivotX(android.view.View, float);
+    method public static deprecated void setPivotY(android.view.View, float);
+    method public static void setPointerIcon(android.view.View, android.support.v4.view.PointerIconCompat);
+    method public static deprecated void setRotation(android.view.View, float);
+    method public static deprecated void setRotationX(android.view.View, float);
+    method public static deprecated void setRotationY(android.view.View, float);
+    method public static deprecated void setSaveFromParentEnabled(android.view.View, boolean);
+    method public static deprecated void setScaleX(android.view.View, float);
+    method public static deprecated void setScaleY(android.view.View, float);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+    method public static void setTransitionName(android.view.View, java.lang.String);
+    method public static deprecated void setTranslationX(android.view.View, float);
+    method public static deprecated void setTranslationY(android.view.View, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static deprecated void setX(android.view.View, float);
+    method public static deprecated void setY(android.view.View, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static void stopNestedScroll(android.view.View);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field public static final int LAYER_TYPE_NONE = 0; // 0x0
+    field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field public static final deprecated int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field public static final deprecated int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field public static final deprecated int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field public static final deprecated int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final deprecated int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field public static final deprecated int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field public static final deprecated int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+  }
+
+  public final class ViewConfigurationCompat {
+    method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
+    method public static boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method public static void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public class ViewPager extends android.view.ViewGroup {
+    ctor public ViewPager(android.content.Context);
+    ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+    method public void addOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public boolean arrowScroll(int);
+    method public boolean beginFakeDrag();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public void clearOnPageChangeListeners();
+    method public void endFakeDrag();
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fakeDragBy(float);
+    method public android.support.v4.view.PagerAdapter getAdapter();
+    method public int getCurrentItem();
+    method public int getOffscreenPageLimit();
+    method public int getPageMargin();
+    method public boolean isFakeDragging();
+    method protected void onLayout(boolean, int, int, int, int);
+    method protected void onPageScrolled(int, float, int);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void removeOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setAdapter(android.support.v4.view.PagerAdapter);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(int);
+    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setPageMargin(int);
+    method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
+    method public void setPageMarginDrawable(int);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer, int);
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewPager.DecorView implements java.lang.annotation.Annotation {
+  }
+
+  public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams {
+    ctor public ViewPager.LayoutParams();
+    ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public int gravity;
+    field public boolean isDecor;
+  }
+
+  public static abstract interface ViewPager.OnAdapterChangeListener {
+    method public abstract void onAdapterChanged(android.support.v4.view.ViewPager, android.support.v4.view.PagerAdapter, android.support.v4.view.PagerAdapter);
+  }
+
+  public static abstract interface ViewPager.OnPageChangeListener {
+    method public abstract void onPageScrollStateChanged(int);
+    method public abstract void onPageScrolled(int, float, int);
+    method public abstract void onPageSelected(int);
+  }
+
+  public static abstract interface ViewPager.PageTransformer {
+    method public abstract void transformPage(android.view.View, float);
+  }
+
+  public static class ViewPager.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public ViewPager.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR;
+  }
+
+  public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public ViewPager.SimpleOnPageChangeListener();
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public long getStartDelay();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener);
+    method public void start();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat x(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat y(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat z(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public abstract interface ViewPropertyAnimatorListener {
+    method public abstract void onAnimationCancel(android.view.View);
+    method public abstract void onAnimationEnd(android.view.View);
+    method public abstract void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public abstract interface ViewPropertyAnimatorUpdateListener {
+    method public abstract void onAnimationUpdate(android.view.View);
+  }
+
+  public final class WindowCompat {
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(android.support.v4.view.WindowInsetsCompat);
+    method public android.support.v4.view.WindowInsetsCompat consumeStableInsets();
+    method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public int getStableInsetBottom();
+    method public int getStableInsetLeft();
+    method public int getStableInsetRight();
+    method public int getStableInsetTop();
+    method public int getSystemWindowInsetBottom();
+    method public int getSystemWindowInsetLeft();
+    method public int getSystemWindowInsetRight();
+    method public int getSystemWindowInsetTop();
+    method public boolean hasInsets();
+    method public boolean hasStableInsets();
+    method public boolean hasSystemWindowInsets();
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+  }
+
+}
+
+package android.support.v4.view.accessibility {
+
+  public final class AccessibilityEventCompat {
+    method public static void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent);
+    method public int getAction(android.view.accessibility.AccessibilityEvent);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int);
+    method public static int getRecordCount(android.view.accessibility.AccessibilityEvent);
+    method public void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+    method public void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int);
+    method public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager);
+    method public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager);
+    method public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  public static abstract interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method public abstract void onAccessibilityStateChanged(boolean);
+  }
+
+  public static abstract deprecated class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static abstract interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public abstract void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor public AccessibilityNodeInfoCompat(java.lang.Object);
+    method public void addAction(int);
+    method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public void addChild(android.view.View);
+    method public void addChild(android.view.View, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList();
+    method public int getActions();
+    method public void getBoundsInParent(android.graphics.Rect);
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getDrawingOrder();
+    method public java.lang.CharSequence getError();
+    method public android.os.Bundle getExtras();
+    method public java.lang.Object getInfo();
+    method public int getInputType();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public java.lang.CharSequence getPackageName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo();
+    method public java.lang.CharSequence getRoleDescription();
+    method public java.lang.CharSequence getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore();
+    method public java.lang.String getViewIdResourceName();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isVisibleToUser();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public boolean removeChild(android.view.View);
+    method public boolean removeChild(android.view.View, int);
+    method public void setAccessibilityFocused(boolean);
+    method public void setBoundsInParent(android.graphics.Rect);
+    method public void setBoundsInScreen(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(java.lang.Object);
+    method public void setCollectionItemInfo(java.lang.Object);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(java.lang.CharSequence);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View);
+    method public void setLabelFor(android.view.View, int);
+    method public void setLabeledBy(android.view.View);
+    method public void setLabeledBy(android.view.View, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setParent(android.view.View, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat);
+    method public void setRoleDescription(java.lang.CharSequence);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setSource(android.view.View, int);
+    method public void setText(java.lang.CharSequence);
+    method public void setTextSelection(int, int);
+    method public void setTraversalAfter(android.view.View);
+    method public void setTraversalAfter(android.view.View, int);
+    method public void setTraversalBefore(android.view.View);
+    method public void setTraversalBefore(android.view.View, int);
+    method public void setViewIdResourceName(java.lang.String);
+    method public void setVisibleToUser(boolean);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence);
+    method public int getId();
+    method public java.lang.CharSequence getLabel();
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CONTEXT_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_DOWN;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_LEFT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_RIGHT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_TO_POSITION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_UP;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_PROGRESS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_ON_SCREEN;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public boolean isHeading();
+    method public boolean isSelected();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(java.lang.Object);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public java.lang.Object getProvider();
+    method public boolean performAction(int, int, android.os.Bundle);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor public deprecated AccessibilityRecordCompat(java.lang.Object);
+    method public int getAddedCount();
+    method public java.lang.CharSequence getBeforeText();
+    method public java.lang.CharSequence getClassName();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getCurrentItemIndex();
+    method public int getFromIndex();
+    method public deprecated java.lang.Object getImpl();
+    method public int getItemCount();
+    method public int getMaxScrollX();
+    method public int getMaxScrollY();
+    method public android.os.Parcelable getParcelableData();
+    method public int getRemovedCount();
+    method public int getScrollX();
+    method public int getScrollY();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource();
+    method public java.util.List<java.lang.CharSequence> getText();
+    method public int getToIndex();
+    method public int getWindowId();
+    method public boolean isChecked();
+    method public boolean isEnabled();
+    method public boolean isFullScreen();
+    method public boolean isPassword();
+    method public boolean isScrollable();
+    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat obtain();
+    method public void recycle();
+    method public void setAddedCount(int);
+    method public void setBeforeText(java.lang.CharSequence);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setCurrentItemIndex(int);
+    method public void setEnabled(boolean);
+    method public void setFromIndex(int);
+    method public void setFullScreen(boolean);
+    method public void setItemCount(int);
+    method public void setMaxScrollX(int);
+    method public void setMaxScrollY(int);
+    method public void setParcelableData(android.os.Parcelable);
+    method public void setPassword(boolean);
+    method public void setRemovedCount(int);
+    method public void setScrollX(int);
+    method public void setScrollY(int);
+    method public void setScrollable(boolean);
+    method public void setSource(android.view.View);
+    method public void setSource(android.view.View, int);
+    method public void setToIndex(int);
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot();
+    method public java.lang.CharSequence getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package android.support.v4.view.animation {
+
+  public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutLinearInInterpolator();
+  }
+
+  public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutSlowInInterpolator();
+  }
+
+  public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public LinearOutSlowInInterpolator();
+  }
+
+   abstract class LookupTableInterpolator implements android.view.animation.Interpolator {
+    ctor public LookupTableInterpolator(float[]);
+    method public float getInterpolation(float);
+  }
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package android.support.v4.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+    method public abstract void scrollTargetBy(int, int);
+    method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int);
+    method public android.support.v4.widget.AutoScrollHelper setEdgeType(int);
+    method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode);
+  }
+
+  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 void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public abstract class CursorAdapter extends android.widget.BaseAdapter {
+    ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, int);
+    method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursor(android.database.Cursor);
+    method public java.lang.CharSequence convertToString(android.database.Cursor);
+    method public int getCount();
+    method public android.database.Cursor getCursor();
+    method public android.widget.Filter getFilter();
+    method public android.widget.FilterQueryProvider getFilterQueryProvider();
+    method public java.lang.Object getItem(int);
+    method public long getItemId(int);
+    method public android.view.View getView(int, android.view.View, android.view.ViewGroup);
+    method protected deprecated void init(android.content.Context, android.database.Cursor, boolean);
+    method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method protected void onContentChanged();
+    method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence);
+    method public void setFilterQueryProvider(android.widget.FilterQueryProvider);
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+    field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1
+    field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2
+  }
+
+  public class DrawerLayout extends android.view.ViewGroup {
+    ctor public DrawerLayout(android.content.Context);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void closeDrawer(android.view.View);
+    method public void closeDrawer(android.view.View, boolean);
+    method public void closeDrawer(int);
+    method public void closeDrawer(int, boolean);
+    method public void closeDrawers();
+    method public float getDrawerElevation();
+    method public int getDrawerLockMode(int);
+    method public int getDrawerLockMode(android.view.View);
+    method public java.lang.CharSequence getDrawerTitle(int);
+    method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable();
+    method public boolean isDrawerOpen(android.view.View);
+    method public boolean isDrawerOpen(int);
+    method public boolean isDrawerVisible(android.view.View);
+    method public boolean isDrawerVisible(int);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void openDrawer(android.view.View);
+    method public void openDrawer(android.view.View, boolean);
+    method public void openDrawer(int);
+    method public void openDrawer(int, boolean);
+    method public void removeDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerElevation(float);
+    method public deprecated void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerLockMode(int);
+    method public void setDrawerLockMode(int, int);
+    method public void setDrawerLockMode(int, android.view.View);
+    method public void setDrawerShadow(android.graphics.drawable.Drawable, int);
+    method public void setDrawerShadow(int, int);
+    method public void setDrawerTitle(int, java.lang.CharSequence);
+    method public void setScrimColor(int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackground(int);
+    method public void setStatusBarBackgroundColor(int);
+    field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1
+    field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2
+    field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3
+    field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract interface DrawerLayout.DrawerListener {
+    method public abstract void onDrawerClosed(android.view.View);
+    method public abstract void onDrawerOpened(android.view.View);
+    method public abstract void onDrawerSlide(android.view.View, float);
+    method public abstract void onDrawerStateChanged(int);
+  }
+
+  public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout.LayoutParams(int, int);
+    ctor public DrawerLayout.LayoutParams(int, int, int);
+    ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public int gravity;
+  }
+
+  protected static class DrawerLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public DrawerLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public DrawerLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR;
+  }
+
+  public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public DrawerLayout.SimpleDrawerListener();
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+  }
+
+  public final class EdgeEffectCompat {
+    ctor public EdgeEffectCompat(android.content.Context);
+    method public boolean draw(android.graphics.Canvas);
+    method public void finish();
+    method public boolean isFinished();
+    method public boolean onAbsorb(int);
+    method public deprecated boolean onPull(float);
+    method public boolean onPull(float, float);
+    method public boolean onRelease();
+    method public void setSize(int, int);
+  }
+
+  public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public ExploreByTouchHelper(android.view.View);
+    method public final boolean clearKeyboardFocusForVirtualView(int);
+    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public final int getAccessibilityFocusedVirtualViewId();
+    method public deprecated int getFocusedVirtualView();
+    method public final int getKeyboardFocusedVirtualViewId();
+    method protected abstract int getVirtualViewAt(float, float);
+    method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
+    method public final void invalidateRoot();
+    method public final void invalidateVirtualView(int);
+    method public final void invalidateVirtualView(int, int);
+    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
+    method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
+    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+    method public final boolean requestKeyboardFocusForVirtualView(int);
+    method public final boolean sendEventForVirtualView(int, int);
+    field public static final int HOST_ID = -1; // 0xffffffff
+    field public static final int INVALID_ID = -2147483648; // 0x80000000
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int);
+    method public boolean arrowScroll(int);
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollTo(int, int);
+  }
+
+  public static abstract interface NestedScrollView.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter {
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor);
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean);
+    ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int);
+    method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public void setDropDownViewResource(int);
+    method public void setViewResource(int);
+  }
+
+  public final class ScrollerCompat {
+    method public void abortAnimation();
+    method public boolean computeScrollOffset();
+    method public static android.support.v4.widget.ScrollerCompat create(android.content.Context);
+    method public static android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator);
+    method public void fling(int, int, int, int, int, int, int, int);
+    method public void fling(int, int, int, int, int, int, int, int, int, int);
+    method public float getCurrVelocity();
+    method public int getCurrX();
+    method public int getCurrY();
+    method public int getFinalX();
+    method public int getFinalY();
+    method public boolean isFinished();
+    method public boolean isOverScrolled();
+    method public void notifyHorizontalEdgeReached(int, int, int);
+    method public void notifyVerticalEdgeReached(int, int, int);
+    method public boolean springBack(int, int, int, int, int, int);
+    method public void startScroll(int, int, int, int);
+    method public void startScroll(int, int, int, int, int);
+  }
+
+  public final deprecated class SearchViewCompat {
+    method public static deprecated java.lang.CharSequence getQuery(android.view.View);
+    method public static deprecated boolean isIconified(android.view.View);
+    method public static deprecated boolean isQueryRefinementEnabled(android.view.View);
+    method public static deprecated boolean isSubmitButtonEnabled(android.view.View);
+    method public static deprecated android.view.View newSearchView(android.content.Context);
+    method public static deprecated void setIconified(android.view.View, boolean);
+    method public static deprecated void setImeOptions(android.view.View, int);
+    method public static deprecated void setInputType(android.view.View, int);
+    method public static deprecated void setMaxWidth(android.view.View, int);
+    method public static deprecated void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListener);
+    method public static deprecated void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListener);
+    method public static deprecated void setQuery(android.view.View, java.lang.CharSequence, boolean);
+    method public static deprecated void setQueryHint(android.view.View, java.lang.CharSequence);
+    method public static deprecated void setQueryRefinementEnabled(android.view.View, boolean);
+    method public static deprecated void setSearchableInfo(android.view.View, android.content.ComponentName);
+    method public static deprecated void setSubmitButtonEnabled(android.view.View, boolean);
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnCloseListenerCompat implements android.support.v4.widget.SearchViewCompat.OnCloseListener {
+    ctor public SearchViewCompat.OnCloseListenerCompat();
+    method public boolean onClose();
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnQueryTextListenerCompat implements android.support.v4.widget.SearchViewCompat.OnQueryTextListener {
+    ctor public SearchViewCompat.OnQueryTextListenerCompat();
+    method public boolean onQueryTextChange(java.lang.String);
+    method public boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter {
+    ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]);
+    ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int);
+    method public void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]);
+    method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter();
+    method public int getStringConversionColumn();
+    method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder();
+    method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter);
+    method public void setStringConversionColumn(int);
+    method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder);
+    method public void setViewImage(android.widget.ImageView, java.lang.String);
+    method public void setViewText(android.widget.TextView, java.lang.String);
+  }
+
+  public static abstract interface SimpleCursorAdapter.CursorToStringConverter {
+    method public abstract java.lang.CharSequence convertToString(android.database.Cursor);
+  }
+
+  public static abstract interface SimpleCursorAdapter.ViewBinder {
+    method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int);
+  }
+
+  public class SlidingPaneLayout extends android.view.ViewGroup {
+    ctor public SlidingPaneLayout(android.content.Context);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public deprecated boolean canSlide();
+    method public boolean closePane();
+    method public int getCoveredFadeColor();
+    method public int getParallaxDistance();
+    method public int getSliderFadeColor();
+    method public boolean isOpen();
+    method public boolean isSlideable();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public boolean openPane();
+    method public void setCoveredFadeColor(int);
+    method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener);
+    method public void setParallaxDistance(int);
+    method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableLeft(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableRight(android.graphics.drawable.Drawable);
+    method public deprecated void setShadowResource(int);
+    method public void setShadowResourceLeft(int);
+    method public void setShadowResourceRight(int);
+    method public void setSliderFadeColor(int);
+    method public deprecated void smoothSlideClosed();
+    method public deprecated void smoothSlideOpen();
+  }
+
+  public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public SlidingPaneLayout.LayoutParams();
+    ctor public SlidingPaneLayout.LayoutParams(int, int);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public float weight;
+  }
+
+  public static abstract interface SlidingPaneLayout.PanelSlideListener {
+    method public abstract void onPanelClosed(android.view.View);
+    method public abstract void onPanelOpened(android.view.View);
+    method public abstract void onPanelSlide(android.view.View, float);
+  }
+
+  public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener {
+    ctor public SlidingPaneLayout.SimplePanelSlideListener();
+    method public void onPanelClosed(android.view.View);
+    method public void onPanelOpened(android.view.View);
+    method public void onPanelSlide(android.view.View, float);
+  }
+
+  public class Space extends android.view.View {
+    ctor public Space(android.content.Context, android.util.AttributeSet, int);
+    ctor public Space(android.content.Context, android.util.AttributeSet);
+    ctor public Space(android.content.Context);
+  }
+
+  public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent {
+    ctor public SwipeRefreshLayout(android.content.Context);
+    ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet);
+    method public boolean canChildScrollUp();
+    method public int getProgressCircleDiameter();
+    method public int getProgressViewEndOffset();
+    method public int getProgressViewStartOffset();
+    method public boolean isRefreshing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public deprecated void setColorScheme(int...);
+    method public void setColorSchemeColors(int...);
+    method public void setColorSchemeResources(int...);
+    method public void setDistanceToTriggerSync(int);
+    method public void setOnChildScrollUpCallback(android.support.v4.widget.SwipeRefreshLayout.OnChildScrollUpCallback);
+    method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener);
+    method public deprecated void setProgressBackgroundColor(int);
+    method public void setProgressBackgroundColorSchemeColor(int);
+    method public void setProgressBackgroundColorSchemeResource(int);
+    method public void setProgressViewEndTarget(boolean, int);
+    method public void setProgressViewOffset(boolean, int, int);
+    method public void setRefreshing(boolean);
+    method public void setSize(int);
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+    field protected int mFrom;
+    field protected int mOriginalOffsetTop;
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnChildScrollUpCallback {
+    method public abstract boolean canChildScrollUp(android.support.v4.widget.SwipeRefreshLayout, android.view.View);
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnRefreshListener {
+    method public abstract void onRefresh();
+  }
+
+  public final class TextViewCompat {
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
+    method public static void setTextAppearance(android.widget.TextView, int);
+  }
+
+  public abstract interface TintableCompoundButton {
+    method public abstract android.content.res.ColorStateList getSupportButtonTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode();
+    method public abstract void setSupportButtonTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public class ViewDragHelper {
+    method public void abort();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int, int);
+    method public void cancel();
+    method public void captureChildView(android.view.View, int);
+    method public boolean checkTouchSlop(int);
+    method public boolean checkTouchSlop(int, int);
+    method public boolean continueSettling(boolean);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback);
+    method public android.view.View findTopChildUnder(int, int);
+    method public void flingCapturedView(int, int, int, int);
+    method public int getActivePointerId();
+    method public android.view.View getCapturedView();
+    method public int getEdgeSize();
+    method public float getMinVelocity();
+    method public int getTouchSlop();
+    method public int getViewDragState();
+    method public boolean isCapturedViewUnder(int, int);
+    method public boolean isEdgeTouched(int);
+    method public boolean isEdgeTouched(int, int);
+    method public boolean isPointerDown(int);
+    method public boolean isViewUnder(android.view.View, int, int);
+    method public void processTouchEvent(android.view.MotionEvent);
+    method public void setEdgeTrackingEnabled(int);
+    method public void setMinVelocity(float);
+    method public boolean settleCapturedViewAt(int, int);
+    method public boolean shouldInterceptTouchEvent(android.view.MotionEvent);
+    method public boolean smoothSlideViewTo(android.view.View, int, int);
+    field public static final int DIRECTION_ALL = 3; // 0x3
+    field public static final int DIRECTION_HORIZONTAL = 1; // 0x1
+    field public static final int DIRECTION_VERTICAL = 2; // 0x2
+    field public static final int EDGE_ALL = 15; // 0xf
+    field public static final int EDGE_BOTTOM = 8; // 0x8
+    field public static final int EDGE_LEFT = 1; // 0x1
+    field public static final int EDGE_RIGHT = 2; // 0x2
+    field public static final int EDGE_TOP = 4; // 0x4
+    field public static final int INVALID_POINTER = -1; // 0xffffffff
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewDragHelper.Callback {
+    ctor public ViewDragHelper.Callback();
+    method public int clampViewPositionHorizontal(android.view.View, int, int);
+    method public int clampViewPositionVertical(android.view.View, int, int);
+    method public int getOrderedChildIndex(int);
+    method public int getViewHorizontalDragRange(android.view.View);
+    method public int getViewVerticalDragRange(android.view.View);
+    method public void onEdgeDragStarted(int, int);
+    method public boolean onEdgeLock(int);
+    method public void onEdgeTouched(int, int);
+    method public void onViewCaptured(android.view.View, int);
+    method public void onViewDragStateChanged(int);
+    method public void onViewPositionChanged(android.view.View, int, int, int, int);
+    method public void onViewReleased(android.view.View, float, float);
+    method public abstract boolean tryCaptureView(android.view.View, int);
+  }
+
+}
+
+package android.support.v7.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, boolean);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int, boolean);
+    method public abstract android.view.View getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method public abstract deprecated int getNavigationItemCount();
+    method public abstract deprecated int getNavigationMode();
+    method public abstract deprecated int getSelectedNavigationIndex();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getSelectedTab();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getTabAt(int);
+    method public abstract deprecated int getTabCount();
+    method public android.content.Context getThemedContext();
+    method public abstract java.lang.CharSequence getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab newTab();
+    method public abstract deprecated void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void removeTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void removeTabAt(int);
+    method public abstract deprecated void selectTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setCustomView(android.view.View, android.support.v7.app.ActionBar.LayoutParams);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(java.lang.CharSequence);
+    method public void setHomeActionContentDescription(int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.support.v7.app.ActionBar.OnNavigationListener);
+    method public abstract void setLogo(int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setNavigationMode(int);
+    method public abstract deprecated void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
+    field public int gravity;
+  }
+
+  public static abstract interface ActionBar.OnMenuVisibilityListener {
+    method public abstract void onMenuVisibilityChanged(boolean);
+  }
+
+  public static abstract deprecated interface ActionBar.OnNavigationListener {
+    method public abstract boolean onNavigationItemSelected(int, long);
+  }
+
+  public static abstract deprecated class ActionBar.Tab {
+    ctor public ActionBar.Tab();
+    method public abstract java.lang.CharSequence getContentDescription();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.graphics.drawable.Drawable getIcon();
+    method public abstract int getPosition();
+    method public abstract java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract void select();
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(android.view.View);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(android.graphics.drawable.Drawable);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setTabListener(android.support.v7.app.ActionBar.TabListener);
+    method public abstract android.support.v7.app.ActionBar.Tab setTag(java.lang.Object);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static abstract deprecated interface ActionBar.TabListener {
+    method public abstract void onTabReselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabSelected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+  }
+
+  public deprecated class ActionBarActivity extends android.support.v7.app.AppCompatActivity {
+    ctor public ActionBarActivity();
+  }
+
+  public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
+    method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
+    method public void syncState();
+  }
+
+  public static abstract interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.content.Context getActionBarThemedContext();
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract boolean isNavigationVisible();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends android.support.v7.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.widget.Button getButton(int);
+    method public android.widget.ListView getListView();
+    method public void setButton(int, java.lang.CharSequence, android.os.Message);
+    method public void setButton(int, java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public void setCustomTitle(android.view.View);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIconAttribute(int);
+    method public void setMessage(java.lang.CharSequence);
+    method public void setView(android.view.View);
+    method public void setView(android.view.View, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, int);
+    method public android.support.v7.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public android.support.v7.app.AlertDialog.Builder setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setCancelable(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setCursor(android.database.Cursor, android.content.DialogInterface.OnClickListener, java.lang.String);
+    method public android.support.v7.app.AlertDialog.Builder setCustomTitle(android.view.View);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(int);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.support.v7.app.AlertDialog.Builder setIconAttribute(int);
+    method public deprecated android.support.v7.app.AlertDialog.Builder setInverseBackgroundForced(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setItems(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setItems(java.lang.CharSequence[], android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(int);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(int, boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(java.lang.CharSequence[], boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(android.database.Cursor, java.lang.String, java.lang.String, android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(int, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.database.Cursor, int, java.lang.String, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(java.lang.CharSequence[], int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.widget.ListAdapter, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(int);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setView(int);
+    method public android.support.v7.app.AlertDialog.Builder setView(android.view.View);
+    method public android.support.v7.app.AlertDialog show();
+  }
+
+  public class AppCompatActivity extends android.support.v4.app.FragmentActivity implements android.support.v7.app.ActionBarDrawerToggle.DelegateProvider android.support.v7.app.AppCompatCallback android.support.v4.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public android.content.Intent getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method public void onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public deprecated void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public deprecated void setSupportProgress(int);
+    method public deprecated void setSupportProgressBarIndeterminate(boolean);
+    method public deprecated void setSupportProgressBarIndeterminateVisibility(boolean);
+    method public deprecated void setSupportProgressBarVisibility(boolean);
+    method public android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public abstract interface AppCompatCallback {
+    method public abstract void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public abstract void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public abstract android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract boolean applyDayNight();
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
+    method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public abstract android.view.View findViewById(int);
+    method public static int getDefaultNightMode();
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration);
+    method public abstract void onCreate(android.os.Bundle);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View);
+    method public abstract void setContentView(int);
+    method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context);
+    ctor public AppCompatDialog(android.content.Context, int);
+    ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class MediaRouteActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.app.MediaRouteButton getMediaRouteButton();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public android.support.v7.app.MediaRouteButton onCreateMediaRouteButton();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<android.support.v7.media.MediaRouter.RouteInfo>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends android.support.v7.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSession();
+    method public android.support.v7.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View onCreateMediaControlView(android.os.Bundle);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static android.support.v7.app.MediaRouteDialogFactory getDefault();
+    method public android.support.v7.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends android.support.v4.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public android.support.v7.media.MediaRouter getMediaRouter();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.media.MediaRouter.Callback onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class NotificationCompat extends android.support.v4.app.NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+  }
+
+  public static class NotificationCompat.Builder extends android.support.v4.app.NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v7.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v7.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+  }
+
+}
+
+package android.support.v7.graphics {
+
+  public final class Palette {
+    method public static android.support.v7.graphics.Palette.Builder from(android.graphics.Bitmap);
+    method public static android.support.v7.graphics.Palette from(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap, int);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, int, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public int getColorForTarget(android.support.v7.graphics.Target, int);
+    method public int getDarkMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
+    method public int getDarkVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
+    method public int getDominantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDominantSwatch();
+    method public int getLightMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
+    method public int getLightVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
+    method public int getMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
+    method public android.support.v7.graphics.Palette.Swatch getSwatchForTarget(android.support.v7.graphics.Target);
+    method public java.util.List<android.support.v7.graphics.Palette.Swatch> getSwatches();
+    method public java.util.List<android.support.v7.graphics.Target> getTargets();
+    method public int getVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
+  }
+
+  public static final class Palette.Builder {
+    ctor public Palette.Builder(android.graphics.Bitmap);
+    ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
+    method public android.support.v7.graphics.Palette.Builder addTarget(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Palette.Builder clearFilters();
+    method public android.support.v7.graphics.Palette.Builder clearRegion();
+    method public android.support.v7.graphics.Palette.Builder clearTargets();
+    method public android.support.v7.graphics.Palette generate();
+    method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
+    method public android.support.v7.graphics.Palette.Builder resizeBitmapArea(int);
+    method public deprecated android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
+    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
+  }
+
+  public static abstract interface Palette.Filter {
+    method public abstract boolean isAllowed(int, float[]);
+  }
+
+  public static abstract interface Palette.PaletteAsyncListener {
+    method public abstract void onGenerated(android.support.v7.graphics.Palette);
+  }
+
+  public static final class Palette.Swatch {
+    ctor public Palette.Swatch(int, int);
+    method public int getBodyTextColor();
+    method public float[] getHsl();
+    method public int getPopulation();
+    method public int getRgb();
+    method public int getTitleTextColor();
+  }
+
+  public final class Target {
+    method public float getLightnessWeight();
+    method public float getMaximumLightness();
+    method public float getMaximumSaturation();
+    method public float getMinimumLightness();
+    method public float getMinimumSaturation();
+    method public float getPopulationWeight();
+    method public float getSaturationWeight();
+    method public float getTargetLightness();
+    method public float getTargetSaturation();
+    method public boolean isExclusive();
+    field public static final android.support.v7.graphics.Target DARK_MUTED;
+    field public static final android.support.v7.graphics.Target DARK_VIBRANT;
+    field public static final android.support.v7.graphics.Target LIGHT_MUTED;
+    field public static final android.support.v7.graphics.Target LIGHT_VIBRANT;
+    field public static final android.support.v7.graphics.Target MUTED;
+    field public static final android.support.v7.graphics.Target VIBRANT;
+  }
+
+  public static final class Target.Builder {
+    ctor public Target.Builder();
+    ctor public Target.Builder(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Target build();
+    method public android.support.v7.graphics.Target.Builder setExclusive(boolean);
+    method public android.support.v7.graphics.Target.Builder setLightnessWeight(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setPopulationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setSaturationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setTargetLightness(float);
+    method public android.support.v7.graphics.Target.Builder setTargetSaturation(float);
+  }
+
+}
+
+package android.support.v7.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package android.support.v7.media {
+
+  public final class MediaControlIntent {
+    field public static final java.lang.String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final java.lang.String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final java.lang.String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final java.lang.String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final java.lang.String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final java.lang.String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final java.lang.String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final java.lang.String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final java.lang.String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final java.lang.String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final java.lang.String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final java.lang.String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final java.lang.String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final java.lang.String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final java.lang.String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final java.lang.String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final java.lang.String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final java.lang.String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final java.lang.String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final java.lang.String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final java.lang.String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final java.lang.String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final java.lang.String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final java.lang.String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final java.lang.String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final java.lang.String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final java.lang.String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final java.lang.String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaItemStatus fromBundle(android.os.Bundle);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final java.lang.String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final java.lang.String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(int);
+    ctor public MediaItemStatus.Builder(android.support.v7.media.MediaItemStatus);
+    method public android.support.v7.media.MediaItemStatus build();
+    method public android.support.v7.media.MediaItemStatus.Builder setContentDuration(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setContentPosition(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public android.support.v7.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static android.support.v7.media.MediaRouteDescriptor fromBundle(android.os.Bundle);
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public deprecated boolean isConnecting();
+    method public boolean isEnabled();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(java.lang.String, java.lang.String);
+    ctor public MediaRouteDescriptor.Builder(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter>);
+    method public android.support.v7.media.MediaRouteDescriptor build();
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method public deprecated android.support.v7.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDescription(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setId(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setName(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(android.support.v7.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteDiscoveryRequest fromBundle(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final android.support.v7.media.MediaRouteProviderDescriptor getDescriptor();
+    method public final android.support.v7.media.MediaRouteDiscoveryRequest getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final android.support.v7.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public android.support.v7.media.MediaRouteProvider.RouteController onCreateRouteController(java.lang.String);
+    method public void onDiscoveryRequestChanged(android.support.v7.media.MediaRouteDiscoveryRequest);
+    method public final void setCallback(android.support.v7.media.MediaRouteProvider.Callback);
+    method public final void setDescriptor(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public final void setDiscoveryRequest(android.support.v7.media.MediaRouteDiscoveryRequest);
+  }
+
+  public static abstract class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(android.support.v7.media.MediaRouteProvider, android.support.v7.media.MediaRouteProviderDescriptor);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+  }
+
+  public static abstract class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteProviderDescriptor fromBundle(android.os.Bundle);
+    method public java.util.List<android.support.v7.media.MediaRouteDescriptor> getRoutes();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoute(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<android.support.v7.media.MediaRouteDescriptor>);
+    method public android.support.v7.media.MediaRouteProviderDescriptor build();
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public android.support.v7.media.MediaRouteProvider getMediaRouteProvider();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.support.v7.media.MediaRouteProvider onCreateMediaRouteProvider();
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(android.support.v7.media.MediaRouteSelector);
+    method public static android.support.v7.media.MediaRouteSelector fromBundle(android.os.Bundle);
+    method public java.util.List<java.lang.String> getControlCategories();
+    method public boolean hasControlCategory(java.lang.String);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter>);
+    field public static final android.support.v7.media.MediaRouteSelector EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String>);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategory(java.lang.String);
+    method public android.support.v7.media.MediaRouteSelector.Builder addSelector(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback);
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
+    method public void addProvider(android.support.v7.media.MediaRouteProvider);
+    method public void addRemoteControlClient(java.lang.Object);
+    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
+    method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
+    method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
+    method public java.util.List<android.support.v7.media.MediaRouter.ProviderInfo> getProviders();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+    method public android.support.v7.media.MediaRouter.RouteInfo getSelectedRoute();
+    method public boolean isRouteAvailable(android.support.v7.media.MediaRouteSelector, int);
+    method public void removeCallback(android.support.v7.media.MediaRouter.Callback);
+    method public void removeProvider(android.support.v7.media.MediaRouteProvider);
+    method public void removeRemoteControlClient(java.lang.Object);
+    method public void selectRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void setMediaSession(java.lang.Object);
+    method public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat);
+    method public void unselect(int);
+    method public android.support.v7.media.MediaRouter.RouteInfo updateSelectedRoute(android.support.v7.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+  }
+
+  public static abstract class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onResult(android.os.Bundle);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+    method public android.support.v7.media.MediaRouteProvider getProviderInstance();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
+    method public android.support.v7.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public boolean isBluetooth();
+    method public boolean isConnecting();
+    method public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method public boolean isSelected();
+    method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
+    method public void requestSetVolume(int);
+    method public void requestUpdateVolume(int);
+    method public void select();
+    method public void sendControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public boolean supportsControlAction(java.lang.String, java.lang.String);
+    method public boolean supportsControlCategory(java.lang.String);
+    method public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaSessionStatus fromBundle(android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(int);
+    ctor public MediaSessionStatus.Builder(android.support.v7.media.MediaSessionStatus);
+    method public android.support.v7.media.MediaSessionStatus build();
+    method public android.support.v7.media.MediaSessionStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public android.support.v7.media.MediaSessionStatus.Builder setSessionState(int);
+    method public android.support.v7.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void enqueue(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public java.lang.String getSessionId();
+    method public void getSessionStatus(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void getStatus(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void play(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void release();
+    method public void remove(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void resume(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void seek(java.lang.String, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void sendMessage(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void setOnMessageReceivedListener(android.support.v7.media.RemotePlaybackClient.OnMessageReceivedListener);
+    method public void setSessionId(java.lang.String);
+    method public void setStatusCallback(android.support.v7.media.RemotePlaybackClient.StatusCallback);
+    method public void startSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void stop(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+  }
+
+  public static abstract class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(java.lang.String, int, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.ItemActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+  }
+
+  public static abstract interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public abstract void onMessageReceived(java.lang.String, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.SessionActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+  public static abstract class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+    method public void onSessionChanged(java.lang.String);
+    method public void onSessionStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+}
+
+package android.support.v7.preference {
+
+  public class CheckBoxPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
+    ctor public CheckBoxPreference(android.content.Context);
+  }
+
+  public abstract class DialogPreference extends android.support.v7.preference.Preference {
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DialogPreference(android.content.Context);
+    method public android.graphics.drawable.Drawable getDialogIcon();
+    method public int getDialogLayoutResource();
+    method public java.lang.CharSequence getDialogMessage();
+    method public java.lang.CharSequence getDialogTitle();
+    method public java.lang.CharSequence getNegativeButtonText();
+    method public java.lang.CharSequence getPositiveButtonText();
+    method public void setDialogIcon(android.graphics.drawable.Drawable);
+    method public void setDialogIcon(int);
+    method public void setDialogLayoutResource(int);
+    method public void setDialogMessage(java.lang.CharSequence);
+    method public void setDialogMessage(int);
+    method public void setDialogTitle(java.lang.CharSequence);
+    method public void setDialogTitle(int);
+    method public void setNegativeButtonText(java.lang.CharSequence);
+    method public void setNegativeButtonText(int);
+    method public void setPositiveButtonText(java.lang.CharSequence);
+    method public void setPositiveButtonText(int);
+  }
+
+  public static abstract interface DialogPreference.TargetFragment {
+    method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+  }
+
+  public class DropDownPreference extends android.support.v7.preference.ListPreference {
+    ctor public DropDownPreference(android.content.Context);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
+    method protected android.widget.ArrayAdapter createAdapter();
+  }
+
+  public class EditTextPreference extends android.support.v7.preference.DialogPreference {
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
+    ctor public EditTextPreference(android.content.Context);
+    method public java.lang.String getText();
+    method public void setText(java.lang.String);
+  }
+
+  public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public EditTextPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.EditTextPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public ListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence getEntry();
+    method public java.lang.CharSequence[] getEntryValues();
+    method public java.lang.String getValue();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValue(java.lang.String);
+    method public void setValueIndex(int);
+  }
+
+  public class ListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public ListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.ListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public MultiSelectListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class Preference implements java.lang.Comparable {
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet);
+    ctor public Preference(android.content.Context);
+    method public boolean callChangeListener(java.lang.Object);
+    method public int compareTo(android.support.v7.preference.Preference);
+    method protected android.support.v7.preference.Preference findPreferenceInHierarchy(java.lang.String);
+    method public android.content.Context getContext();
+    method public java.lang.String getDependency();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getFragment();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.String getKey();
+    method public final int getLayoutResource();
+    method public android.support.v7.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
+    method public android.support.v7.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
+    method public int getOrder();
+    method protected boolean getPersistedBoolean(boolean);
+    method protected float getPersistedFloat(float);
+    method protected int getPersistedInt(int);
+    method protected long getPersistedLong(long);
+    method protected java.lang.String getPersistedString(java.lang.String);
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public boolean getShouldDisableView();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public final int getWidgetLayoutResource();
+    method public boolean hasKey();
+    method public boolean isEnabled();
+    method public boolean isPersistent();
+    method public boolean isSelectable();
+    method public final boolean isVisible();
+    method protected void notifyChanged();
+    method public void notifyDependencyChange(boolean);
+    method protected void notifyHierarchyChanged();
+    method public void onAttached();
+    method protected void onAttachedToHierarchy(android.support.v7.preference.PreferenceManager);
+    method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
+    method protected void onClick();
+    method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
+    method public void onDetached();
+    method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onParentChanged(android.support.v7.preference.Preference, boolean);
+    method protected void onPrepareForRemoval();
+    method protected void onRestoreInstanceState(android.os.Parcelable);
+    method protected android.os.Parcelable onSaveInstanceState();
+    method protected void onSetInitialValue(boolean, java.lang.Object);
+    method public android.os.Bundle peekExtras();
+    method protected boolean persistBoolean(boolean);
+    method protected boolean persistFloat(float);
+    method protected boolean persistInt(int);
+    method protected boolean persistLong(long);
+    method protected boolean persistString(java.lang.String);
+    method public void restoreHierarchyState(android.os.Bundle);
+    method public void saveHierarchyState(android.os.Bundle);
+    method public void setDefaultValue(java.lang.Object);
+    method public void setDependency(java.lang.String);
+    method public void setEnabled(boolean);
+    method public void setFragment(java.lang.String);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIcon(int);
+    method public void setIntent(android.content.Intent);
+    method public void setKey(java.lang.String);
+    method public void setLayoutResource(int);
+    method public void setOnPreferenceChangeListener(android.support.v7.preference.Preference.OnPreferenceChangeListener);
+    method public void setOnPreferenceClickListener(android.support.v7.preference.Preference.OnPreferenceClickListener);
+    method public void setOrder(int);
+    method public void setPersistent(boolean);
+    method public void setSelectable(boolean);
+    method public void setShouldDisableView(boolean);
+    method public void setSummary(java.lang.CharSequence);
+    method public void setSummary(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitle(int);
+    method public void setViewId(int);
+    method public final void setVisible(boolean);
+    method public void setWidgetLayoutResource(int);
+    method public boolean shouldDisableDependents();
+    method protected boolean shouldPersist();
+    field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
+  }
+
+  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+    ctor public Preference.BaseSavedState(android.os.Parcel);
+    ctor public Preference.BaseSavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.preference.Preference.BaseSavedState> CREATOR;
+  }
+
+  public static abstract interface Preference.OnPreferenceChangeListener {
+    method public abstract boolean onPreferenceChange(android.support.v7.preference.Preference, java.lang.Object);
+  }
+
+  public static abstract interface Preference.OnPreferenceClickListener {
+    method public abstract boolean onPreferenceClick(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceCategory extends android.support.v7.preference.PreferenceGroup {
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
+    ctor public PreferenceCategory(android.content.Context);
+  }
+
+  public abstract class PreferenceDialogFragmentCompat extends android.support.v4.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragmentCompat();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragmentCompat extends android.support.v4.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragmentCompat();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public abstract class PreferenceGroup extends android.support.v7.preference.Preference {
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
+    method public void addItemFromInflater(android.support.v7.preference.Preference);
+    method public boolean addPreference(android.support.v7.preference.Preference);
+    method protected void dispatchRestoreInstanceState(android.os.Bundle);
+    method protected void dispatchSaveInstanceState(android.os.Bundle);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.support.v7.preference.Preference getPreference(int);
+    method public int getPreferenceCount();
+    method protected boolean isOnSameScreenAsChildren();
+    method public boolean isOrderingAsAdded();
+    method protected boolean onPrepareAddPreference(android.support.v7.preference.Preference);
+    method public void removeAll();
+    method public boolean removePreference(android.support.v7.preference.Preference);
+    method public void setOrderingAsAdded(boolean);
+  }
+
+  public static abstract interface PreferenceGroup.PreferencePositionCallback {
+    method public abstract int getPreferenceAdapterPosition(java.lang.String);
+    method public abstract int getPreferenceAdapterPosition(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceManager {
+    method public android.support.v7.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.content.Context getContext();
+    method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
+    method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
+    method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
+    method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
+    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public int getSharedPreferencesMode();
+    method public java.lang.String getSharedPreferencesName();
+    method public boolean isStorageDefault();
+    method public boolean isStorageDeviceProtected();
+    method public static void setDefaultValues(android.content.Context, int, boolean);
+    method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
+    method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
+    method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
+    method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
+    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
+    method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
+    method public void setSharedPreferencesMode(int);
+    method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceProtected();
+    method public void showDialog(android.support.v7.preference.Preference);
+    field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
+  }
+
+  public static abstract interface PreferenceManager.OnDisplayPreferenceDialogListener {
+    method public abstract void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceManager.OnNavigateToScreenListener {
+    method public abstract void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+  }
+
+  public static abstract interface PreferenceManager.OnPreferenceTreeClickListener {
+    method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+  }
+
+  public static abstract class PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.PreferenceComparisonCallback();
+    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
+    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
+    method public void setShouldUseGeneratedIds(boolean);
+    method public boolean shouldUseGeneratedIds();
+  }
+
+  public class PreferenceViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
+    method public android.view.View findViewById(int);
+    method public boolean isDividerAllowedAbove();
+    method public boolean isDividerAllowedBelow();
+    method public void setDividerAllowedAbove(boolean);
+    method public void setDividerAllowedBelow(boolean);
+  }
+
+  public class SeekBarPreference extends android.support.v7.preference.Preference {
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SeekBarPreference(android.content.Context);
+    method public int getMax();
+    method public int getMin();
+    method public final int getSeekBarIncrement();
+    method public int getValue();
+    method public boolean isAdjustable();
+    method public void setAdjustable(boolean);
+    method public final void setMax(int);
+    method public void setMin(int);
+    method public final void setSeekBarIncrement(int);
+    method public void setValue(int);
+  }
+
+  public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreferenceCompat(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+  public abstract class TwoStatePreference extends android.support.v7.preference.Preference {
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
+    ctor public TwoStatePreference(android.content.Context);
+    method public boolean getDisableDependentsState();
+    method public java.lang.CharSequence getSummaryOff();
+    method public java.lang.CharSequence getSummaryOn();
+    method public boolean isChecked();
+    method public void setChecked(boolean);
+    method public void setDisableDependentsState(boolean);
+    method public void setSummaryOff(java.lang.CharSequence);
+    method public void setSummaryOff(int);
+    method public void setSummaryOn(java.lang.CharSequence);
+    method public void setSummaryOn(int);
+    method protected void syncSummaryView(android.support.v7.preference.PreferenceViewHolder);
+    field protected boolean mChecked;
+  }
+
+}
+
+package android.support.v7.util {
+
+  public class AsyncListUtil<T> {
+    ctor public AsyncListUtil(java.lang.Class<T>, int, android.support.v7.util.AsyncListUtil.DataCallback<T>, android.support.v7.util.AsyncListUtil.ViewCallback);
+    method public T getItem(int);
+    method public int getItemCount();
+    method public void onRangeChanged();
+    method public void refresh();
+  }
+
+  public static abstract class AsyncListUtil.DataCallback<T> {
+    ctor public AsyncListUtil.DataCallback();
+    method public abstract void fillData(T[], int, int);
+    method public int getMaxCachedTiles();
+    method public void recycleData(T[], int);
+    method public abstract int refreshData();
+  }
+
+  public static abstract class AsyncListUtil.ViewCallback {
+    ctor public AsyncListUtil.ViewCallback();
+    method public void extendRangeInto(int[], int[], int);
+    method public abstract void getItemRangeInto(int[]);
+    method public abstract void onDataRefresh();
+    method public abstract void onItemLoaded(int);
+    field public static final int HINT_SCROLL_ASC = 2; // 0x2
+    field public static final int HINT_SCROLL_DESC = 1; // 0x1
+    field public static final int HINT_SCROLL_NONE = 0; // 0x0
+  }
+
+  public class BatchingListUpdateCallback implements android.support.v7.util.ListUpdateCallback {
+    ctor public BatchingListUpdateCallback(android.support.v7.util.ListUpdateCallback);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int, java.lang.Object);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public class DiffUtil {
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback);
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback, boolean);
+  }
+
+  public static abstract class DiffUtil.Callback {
+    ctor public DiffUtil.Callback();
+    method public abstract boolean areContentsTheSame(int, int);
+    method public abstract boolean areItemsTheSame(int, int);
+    method public java.lang.Object getChangePayload(int, int);
+    method public abstract int getNewListSize();
+    method public abstract int getOldListSize();
+  }
+
+  public static class DiffUtil.DiffResult {
+    method public void dispatchUpdatesTo(android.support.v7.widget.RecyclerView.Adapter);
+    method public void dispatchUpdatesTo(android.support.v7.util.ListUpdateCallback);
+  }
+
+  public abstract interface ListUpdateCallback {
+    method public abstract void onChanged(int, int, java.lang.Object);
+    method public abstract void onInserted(int, int);
+    method public abstract void onMoved(int, int);
+    method public abstract void onRemoved(int, int);
+  }
+
+  public class SortedList<T> {
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>);
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>, int);
+    method public int add(T);
+    method public void addAll(T[], boolean);
+    method public void addAll(T...);
+    method public void addAll(java.util.Collection<T>);
+    method public void beginBatchedUpdates();
+    method public void clear();
+    method public void endBatchedUpdates();
+    method public T get(int) throws java.lang.IndexOutOfBoundsException;
+    method public int indexOf(T);
+    method public void recalculatePositionOfItemAt(int);
+    method public boolean remove(T);
+    method public T removeItemAt(int);
+    method public int size();
+    method public void updateItemAt(int, T);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class SortedList.BatchedCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedList.BatchedCallback(android.support.v7.util.SortedList.Callback<T2>);
+    method public boolean areContentsTheSame(T2, T2);
+    method public boolean areItemsTheSame(T2, T2);
+    method public int compare(T2, T2);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public static abstract class SortedList.Callback<T2> implements java.util.Comparator android.support.v7.util.ListUpdateCallback {
+    ctor public SortedList.Callback();
+    method public abstract boolean areContentsTheSame(T2, T2);
+    method public abstract boolean areItemsTheSame(T2, T2);
+    method public abstract int compare(T2, T2);
+    method public abstract void onChanged(int, int);
+    method public void onChanged(int, int, java.lang.Object);
+  }
+
+}
+
+package android.support.v7.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.view.Menu getMenu();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public void setTag(java.lang.Object);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static abstract interface ActionMode.Callback {
+    method public abstract boolean onActionItemClicked(android.support.v7.view.ActionMode, android.view.MenuItem);
+    method public abstract boolean onCreateActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+    method public abstract void onDestroyActionMode(android.support.v7.view.ActionMode);
+    method public abstract boolean onPrepareActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+  }
+
+  public abstract interface CollapsibleActionView {
+    method public abstract void onActionViewCollapsed();
+    method public abstract void onActionViewExpanded();
+  }
+
+}
+
+package android.support.v7.widget {
+
+  public class ActionMenuView extends android.support.v7.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
+    method public android.view.Menu getMenu();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(android.support.v7.widget.ActionMenuView.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends android.support.v7.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(android.support.v7.widget.ActionMenuView.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static abstract interface ActionMenuView.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setSupportAllCaps(boolean);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class CardView extends android.widget.FrameLayout {
+    ctor public CardView(android.content.Context);
+    ctor public CardView(android.content.Context, android.util.AttributeSet);
+    ctor public CardView(android.content.Context, android.util.AttributeSet, int);
+    method public android.content.res.ColorStateList getCardBackgroundColor();
+    method public float getCardElevation();
+    method public int getContentPaddingBottom();
+    method public int getContentPaddingLeft();
+    method public int getContentPaddingRight();
+    method public int getContentPaddingTop();
+    method public float getMaxCardElevation();
+    method public boolean getPreventCornerOverlap();
+    method public float getRadius();
+    method public boolean getUseCompatPadding();
+    method public void setCardBackgroundColor(int);
+    method public void setCardBackgroundColor(android.content.res.ColorStateList);
+    method public void setCardElevation(float);
+    method public void setContentPadding(int, int, int, int);
+    method public void setMaxCardElevation(float);
+    method public void setPreventCornerOverlap(boolean);
+    method public void setRadius(float);
+    method public void setUseCompatPadding(boolean);
+  }
+
+  public class DefaultItemAnimator extends android.support.v7.widget.SimpleItemAnimator {
+    ctor public DefaultItemAnimator();
+    method public boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimations();
+    method public boolean isRunning();
+    method public void runPendingAnimations();
+  }
+
+  public class DividerItemDecoration extends android.support.v7.widget.RecyclerView.ItemDecoration {
+    ctor public DividerItemDecoration(android.content.Context, int);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setOrientation(int);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayout(android.content.Context);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final android.support.v7.widget.GridLayout.Alignment BASELINE;
+    field public static final android.support.v7.widget.GridLayout.Alignment BOTTOM;
+    field public static final android.support.v7.widget.GridLayout.Alignment CENTER;
+    field public static final android.support.v7.widget.GridLayout.Alignment END;
+    field public static final android.support.v7.widget.GridLayout.Alignment FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final android.support.v7.widget.GridLayout.Alignment LEFT;
+    field public static final android.support.v7.widget.GridLayout.Alignment RIGHT;
+    field public static final android.support.v7.widget.GridLayout.Alignment START;
+    field public static final android.support.v7.widget.GridLayout.Alignment TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.Spec, android.support.v7.widget.GridLayout.Spec);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    method public void setGravity(int);
+    field public android.support.v7.widget.GridLayout.Spec columnSpec;
+    field public android.support.v7.widget.GridLayout.Spec rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public android.support.v7.widget.GridLayout.Alignment getAbsoluteAlignment(boolean);
+  }
+
+  public class GridLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public GridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public GridLayoutManager(android.content.Context, int);
+    ctor public GridLayoutManager(android.content.Context, int, int, boolean);
+    method public int getSpanCount();
+    method public android.support.v7.widget.GridLayoutManager.SpanSizeLookup getSpanSizeLookup();
+    method public void setSpanCount(int);
+    method public void setSpanSizeLookup(android.support.v7.widget.GridLayoutManager.SpanSizeLookup);
+    field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
+  }
+
+  public static final class GridLayoutManager.DefaultSpanSizeLookup extends android.support.v7.widget.GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.DefaultSpanSizeLookup();
+    method public int getSpanSize(int);
+  }
+
+  public static class GridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public GridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayoutManager.LayoutParams(int, int);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getSpanIndex();
+    method public int getSpanSize();
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.SpanSizeLookup();
+    method public int getSpanGroupIndex(int, int);
+    method public int getSpanIndex(int, int);
+    method public abstract int getSpanSize(int);
+    method public void invalidateSpanIndexCache();
+    method public boolean isSpanIndexCacheEnabled();
+    method public void setSpanIndexCacheEnabled(boolean);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet, int);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.support.v7.widget.LinearLayoutCompat.LayoutParams);
+    field public int gravity;
+    field public float weight;
+  }
+
+  public class LinearLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.helper.ItemTouchHelper.ViewDropHandler android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public LinearLayoutManager(android.content.Context);
+    ctor public LinearLayoutManager(android.content.Context, int, boolean);
+    ctor public LinearLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int findFirstCompletelyVisibleItemPosition();
+    method public int findFirstVisibleItemPosition();
+    method public int findLastCompletelyVisibleItemPosition();
+    method public int findLastVisibleItemPosition();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
+    method public int getInitialItemPrefetchCount();
+    method public int getOrientation();
+    method public boolean getRecycleChildrenOnDetach();
+    method public boolean getReverseLayout();
+    method public boolean getStackFromEnd();
+    method protected boolean isLayoutRTL();
+    method public boolean isSmoothScrollbarEnabled();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setOrientation(int);
+    method public void setRecycleChildrenOnDetach(boolean);
+    method public void setReverseLayout(boolean);
+    method public void setSmoothScrollbarEnabled(boolean);
+    method public void setStackFromEnd(boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  protected static class LinearLayoutManager.LayoutChunkResult {
+    ctor protected LinearLayoutManager.LayoutChunkResult();
+    field public int mConsumed;
+    field public boolean mFinished;
+    field public boolean mFocusable;
+    field public boolean mIgnoreConsumed;
+  }
+
+  public class LinearSmoothScroller extends android.support.v7.widget.RecyclerView.SmoothScroller {
+    ctor public LinearSmoothScroller(android.content.Context);
+    method public int calculateDtToFit(int, int, int, int, int);
+    method public int calculateDxToMakeVisible(android.view.View, int);
+    method public int calculateDyToMakeVisible(android.view.View, int);
+    method protected float calculateSpeedPerPixel(android.util.DisplayMetrics);
+    method protected int calculateTimeForDeceleration(int);
+    method protected int calculateTimeForScrolling(int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method protected int getHorizontalSnapPreference();
+    method protected int getVerticalSnapPreference();
+    method protected void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void onStart();
+    method protected void onStop();
+    method protected void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void updateActionForInterimTarget(android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    field public static final int SNAP_TO_ANY = 0; // 0x0
+    field public static final int SNAP_TO_END = 1; // 0x1
+    field public static final int SNAP_TO_START = -1; // 0xffffffff
+    field protected final android.view.animation.DecelerateInterpolator mDecelerateInterpolator;
+    field protected int mInterimTargetDx;
+    field protected int mInterimTargetDy;
+    field protected final android.view.animation.LinearInterpolator mLinearInterpolator;
+    field protected android.graphics.PointF mTargetVector;
+  }
+
+  public class LinearSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public LinearSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int, int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener createDragToOpenListener(android.view.View);
+    method public void dismiss();
+    method public android.view.View getAnchorView();
+    method public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable getBackground();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView getListView();
+    method public int getPromptPosition();
+    method public java.lang.Object getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter);
+    method public void setAnchorView(android.view.View);
+    method public void setAnimationStyle(int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public abstract class OrientationHelper {
+    method public static android.support.v7.widget.OrientationHelper createHorizontalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public static android.support.v7.widget.OrientationHelper createOrientationHelper(android.support.v7.widget.RecyclerView.LayoutManager, int);
+    method public static android.support.v7.widget.OrientationHelper createVerticalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int getDecoratedEnd(android.view.View);
+    method public abstract int getDecoratedMeasurement(android.view.View);
+    method public abstract int getDecoratedMeasurementInOther(android.view.View);
+    method public abstract int getDecoratedStart(android.view.View);
+    method public abstract int getEnd();
+    method public abstract int getEndAfterPadding();
+    method public abstract int getEndPadding();
+    method public abstract int getMode();
+    method public abstract int getModeInOther();
+    method public abstract int getStartAfterPadding();
+    method public abstract int getTotalSpace();
+    method public int getTotalSpaceChange();
+    method public abstract int getTransformedEndWithDecoration(android.view.View);
+    method public abstract int getTransformedStartWithDecoration(android.view.View);
+    method public abstract void offsetChild(android.view.View, int);
+    method public abstract void offsetChildren(int);
+    method public void onLayoutComplete();
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+    field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
+  }
+
+  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public PagerSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, int, int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(android.support.v7.widget.PopupMenu.OnDismissListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.PopupMenu.OnMenuItemClickListener);
+    method public void show();
+  }
+
+  public static abstract interface PopupMenu.OnDismissListener {
+    method public abstract void onDismiss(android.support.v7.widget.PopupMenu);
+  }
+
+  public static abstract interface PopupMenu.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class RecyclerView extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.ScrollingView {
+    ctor public RecyclerView(android.content.Context);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void addOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void addOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void addOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void clearOnChildAttachStateChangeListeners();
+    method public void clearOnScrollListeners();
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+    method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
+    method public android.view.View findChildViewUnder(float, float);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
+    method public deprecated android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForPosition(int);
+    method public boolean fling(int, int);
+    method public android.support.v7.widget.RecyclerView.Adapter getAdapter();
+    method public int getChildAdapterPosition(android.view.View);
+    method public long getChildItemId(android.view.View);
+    method public int getChildLayoutPosition(android.view.View);
+    method public deprecated int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder getChildViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate();
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator getItemAnimator();
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getMaxFlingVelocity();
+    method public int getMinFlingVelocity();
+    method public android.support.v7.widget.RecyclerView.OnFlingListener getOnFlingListener();
+    method public boolean getPreserveFocusAfterLayout();
+    method public android.support.v7.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
+    method public int getScrollState();
+    method public boolean hasFixedSize();
+    method public boolean hasPendingAdapterUpdates();
+    method public void invalidateItemDecorations();
+    method public boolean isAnimating();
+    method public boolean isComputingLayout();
+    method public boolean isLayoutFrozen();
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onChildAttachedToWindow(android.view.View);
+    method public void onChildDetachedFromWindow(android.view.View);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onScrollStateChanged(int);
+    method public void onScrolled(int, int);
+    method public void removeItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void removeOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void removeOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void removeOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void scrollToPosition(int);
+    method public void setAccessibilityDelegateCompat(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+    method public void setAdapter(android.support.v7.widget.RecyclerView.Adapter);
+    method public void setChildDrawingOrderCallback(android.support.v7.widget.RecyclerView.ChildDrawingOrderCallback);
+    method public void setHasFixedSize(boolean);
+    method public void setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator);
+    method public void setItemViewCacheSize(int);
+    method public void setLayoutFrozen(boolean);
+    method public void setLayoutManager(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public void setOnFlingListener(android.support.v7.widget.RecyclerView.OnFlingListener);
+    method public deprecated void setOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void setPreserveFocusAfterLayout(boolean);
+    method public void setRecycledViewPool(android.support.v7.widget.RecyclerView.RecycledViewPool);
+    method public void setRecyclerListener(android.support.v7.widget.RecyclerView.RecyclerListener);
+    method public void setScrollingTouchSlop(int);
+    method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
+    method public void smoothScrollBy(int, int);
+    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
+    method public void smoothScrollToPosition(int);
+    method public void stopScroll();
+    method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_TYPE = -1; // 0xffffffff
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+    field public static final int NO_POSITION = -1; // 0xffffffff
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+    field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
+    field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder> {
+    ctor public RecyclerView.Adapter();
+    method public final void bindViewHolder(VH, int);
+    method public final VH createViewHolder(android.view.ViewGroup, int);
+    method public abstract int getItemCount();
+    method public long getItemId(int);
+    method public int getItemViewType(int);
+    method public final boolean hasObservers();
+    method public final boolean hasStableIds();
+    method public final void notifyDataSetChanged();
+    method public final void notifyItemChanged(int);
+    method public final void notifyItemChanged(int, java.lang.Object);
+    method public final void notifyItemInserted(int);
+    method public final void notifyItemMoved(int, int);
+    method public final void notifyItemRangeChanged(int, int);
+    method public final void notifyItemRangeChanged(int, int, java.lang.Object);
+    method public final void notifyItemRangeInserted(int, int);
+    method public final void notifyItemRangeRemoved(int, int);
+    method public final void notifyItemRemoved(int);
+    method public void onAttachedToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public abstract void onBindViewHolder(VH, int);
+    method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object>);
+    method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDetachedFromRecyclerView(android.support.v7.widget.RecyclerView);
+    method public boolean onFailedToRecycleView(VH);
+    method public void onViewAttachedToWindow(VH);
+    method public void onViewDetachedFromWindow(VH);
+    method public void onViewRecycled(VH);
+    method public void registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+    method public void setHasStableIds(boolean);
+    method public void unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+  }
+
+  public static abstract class RecyclerView.AdapterDataObserver {
+    ctor public RecyclerView.AdapterDataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeChanged(int, int, java.lang.Object);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeMoved(int, int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public static abstract interface RecyclerView.ChildDrawingOrderCallback {
+    method public abstract int onGetChildDrawingOrder(int, int);
+  }
+
+  public static abstract class RecyclerView.ItemAnimator {
+    ctor public RecyclerView.ItemAnimator();
+    method public abstract boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object>);
+    method public final void dispatchAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationsFinished();
+    method public abstract void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract void endAnimations();
+    method public long getAddDuration();
+    method public long getChangeDuration();
+    method public long getMoveDuration();
+    method public long getRemoveDuration();
+    method public abstract boolean isRunning();
+    method public final boolean isRunning(android.support.v7.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
+    method public void onAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder, int, java.util.List<java.lang.Object>);
+    method public abstract void runPendingAnimations();
+    method public void setAddDuration(long);
+    method public void setChangeDuration(long);
+    method public void setMoveDuration(long);
+    method public void setRemoveDuration(long);
+    field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
+    field public static final int FLAG_CHANGED = 2; // 0x2
+    field public static final int FLAG_INVALIDATED = 4; // 0x4
+    field public static final int FLAG_MOVED = 2048; // 0x800
+    field public static final int FLAG_REMOVED = 8; // 0x8
+  }
+
+  public static abstract class RecyclerView.ItemAnimator.AdapterChanges implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
+    method public abstract void onAnimationsFinished();
+  }
+
+  public static class RecyclerView.ItemAnimator.ItemHolderInfo {
+    ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public int bottom;
+    field public int changeFlags;
+    field public int left;
+    field public int right;
+    field public int top;
+  }
+
+  public static abstract class RecyclerView.ItemDecoration {
+    ctor public RecyclerView.ItemDecoration();
+    method public deprecated void getItemOffsets(android.graphics.Rect, int, android.support.v7.widget.RecyclerView);
+    method public void getItemOffsets(android.graphics.Rect, android.view.View, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+    method public void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+  }
+
+  public static abstract class RecyclerView.LayoutManager {
+    ctor public RecyclerView.LayoutManager();
+    method public void addDisappearingView(android.view.View);
+    method public void addDisappearingView(android.view.View, int);
+    method public void addView(android.view.View);
+    method public void addView(android.view.View, int);
+    method public void assertInLayoutOrScroll(java.lang.String);
+    method public void assertNotInLayoutOrScroll(java.lang.String);
+    method public void attachView(android.view.View, int, android.support.v7.widget.RecyclerView.LayoutParams);
+    method public void attachView(android.view.View, int);
+    method public void attachView(android.view.View);
+    method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
+    method public boolean canScrollHorizontally();
+    method public boolean canScrollVertically();
+    method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public static int chooseSize(int, int, int);
+    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public void detachAndScrapAttachedViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachView(android.view.View);
+    method public void detachViewAt(int);
+    method public void endAnimation(android.view.View);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.view.View findViewByPosition(int);
+    method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.content.Context, android.util.AttributeSet);
+    method public int getBaseline();
+    method public int getBottomDecorationHeight(android.view.View);
+    method public android.view.View getChildAt(int);
+    method public int getChildCount();
+    method public static deprecated int getChildMeasureSpec(int, int, int, boolean);
+    method public static int getChildMeasureSpec(int, int, int, int, boolean);
+    method public boolean getClipToPadding();
+    method public int getColumnCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getDecoratedBottom(android.view.View);
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public int getDecoratedLeft(android.view.View);
+    method public int getDecoratedMeasuredHeight(android.view.View);
+    method public int getDecoratedMeasuredWidth(android.view.View);
+    method public int getDecoratedRight(android.view.View);
+    method public int getDecoratedTop(android.view.View);
+    method public android.view.View getFocusedChild();
+    method public int getHeight();
+    method public int getHeightMode();
+    method public int getItemCount();
+    method public int getItemViewType(android.view.View);
+    method public int getLayoutDirection();
+    method public int getLeftDecorationWidth(android.view.View);
+    method public int getMinimumHeight();
+    method public int getMinimumWidth();
+    method public int getPaddingBottom();
+    method public int getPaddingEnd();
+    method public int getPaddingLeft();
+    method public int getPaddingRight();
+    method public int getPaddingStart();
+    method public int getPaddingTop();
+    method public int getPosition(android.view.View);
+    method public static android.support.v7.widget.RecyclerView.LayoutManager.Properties getProperties(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getRightDecorationWidth(android.view.View);
+    method public int getRowCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getSelectionModeForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getTopDecorationHeight(android.view.View);
+    method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
+    method public int getWidth();
+    method public int getWidthMode();
+    method public boolean hasFocus();
+    method public void ignoreView(android.view.View);
+    method public boolean isAttachedToWindow();
+    method public boolean isAutoMeasureEnabled();
+    method public boolean isFocused();
+    method public final boolean isItemPrefetchEnabled();
+    method public boolean isLayoutHierarchical(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public boolean isMeasurementCacheEnabled();
+    method public boolean isSmoothScrolling();
+    method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
+    method public void layoutDecorated(android.view.View, int, int, int, int);
+    method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
+    method public void measureChild(android.view.View, int, int);
+    method public void measureChildWithMargins(android.view.View, int, int);
+    method public void moveView(int, int);
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onAdapterChanged(android.support.v7.widget.RecyclerView.Adapter, android.support.v7.widget.RecyclerView.Adapter);
+    method public boolean onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int);
+    method public void onAttachedToWindow(android.support.v7.widget.RecyclerView);
+    method public deprecated void onDetachedFromWindow(android.support.v7.widget.RecyclerView);
+    method public void onDetachedFromWindow(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.Recycler);
+    method public android.view.View onFocusSearchFailed(android.view.View, int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityEvent(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onInitializeAccessibilityNodeInfoForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public android.view.View onInterceptFocusSearch(android.view.View, int);
+    method public void onItemsAdded(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsChanged(android.support.v7.widget.RecyclerView);
+    method public void onItemsMoved(android.support.v7.widget.RecyclerView, int, int, int);
+    method public void onItemsRemoved(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int, java.lang.Object);
+    method public void onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onLayoutCompleted(android.support.v7.widget.RecyclerView.State);
+    method public void onMeasure(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, int);
+    method public deprecated boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.view.View, android.view.View);
+    method public boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, android.view.View, android.view.View);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void onScrollStateChanged(int);
+    method public boolean performAccessibilityAction(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, android.os.Bundle);
+    method public boolean performAccessibilityActionForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, int, android.os.Bundle);
+    method public void postOnAnimation(java.lang.Runnable);
+    method public void removeAllViews();
+    method public void removeAndRecycleAllViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public boolean removeCallbacks(java.lang.Runnable);
+    method public void removeDetachedView(android.view.View);
+    method public void removeView(android.view.View);
+    method public void removeViewAt(int);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
+    method public void requestLayout();
+    method public void requestSimpleAnimationsInNextLayout();
+    method public int scrollHorizontallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void scrollToPosition(int);
+    method public int scrollVerticallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void setAutoMeasureEnabled(boolean);
+    method public final void setItemPrefetchEnabled(boolean);
+    method public void setMeasuredDimension(android.graphics.Rect, int, int);
+    method public void setMeasuredDimension(int, int);
+    method public void setMeasurementCacheEnabled(boolean);
+    method public void smoothScrollToPosition(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, int);
+    method public void startSmoothScroll(android.support.v7.widget.RecyclerView.SmoothScroller);
+    method public void stopIgnoringView(android.view.View);
+    method public boolean supportsPredictiveItemAnimations();
+  }
+
+  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+    method public abstract void addPosition(int, int);
+  }
+
+  public static class RecyclerView.LayoutManager.Properties {
+    ctor public RecyclerView.LayoutManager.Properties();
+    field public int orientation;
+    field public boolean reverseLayout;
+    field public int spanCount;
+    field public boolean stackFromEnd;
+  }
+
+  public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public RecyclerView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView.LayoutParams(int, int);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public RecyclerView.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getViewAdapterPosition();
+    method public int getViewLayoutPosition();
+    method public deprecated int getViewPosition();
+    method public boolean isItemChanged();
+    method public boolean isItemRemoved();
+    method public boolean isViewInvalid();
+    method public boolean viewNeedsUpdate();
+  }
+
+  public static abstract interface RecyclerView.OnChildAttachStateChangeListener {
+    method public abstract void onChildViewAttachedToWindow(android.view.View);
+    method public abstract void onChildViewDetachedFromWindow(android.view.View);
+  }
+
+  public static abstract class RecyclerView.OnFlingListener {
+    ctor public RecyclerView.OnFlingListener();
+    method public abstract boolean onFling(int, int);
+  }
+
+  public static abstract interface RecyclerView.OnItemTouchListener {
+    method public abstract boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public abstract void onRequestDisallowInterceptTouchEvent(boolean);
+    method public abstract void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.OnScrollListener {
+    ctor public RecyclerView.OnScrollListener();
+    method public void onScrollStateChanged(android.support.v7.widget.RecyclerView, int);
+    method public void onScrolled(android.support.v7.widget.RecyclerView, int, int);
+  }
+
+  public static class RecyclerView.RecycledViewPool {
+    ctor public RecyclerView.RecycledViewPool();
+    method public void clear();
+    method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
+    method public int getRecycledViewCount(int);
+    method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setMaxRecycledViews(int, int);
+  }
+
+  public final class RecyclerView.Recycler {
+    ctor public RecyclerView.Recycler();
+    method public void bindViewToPosition(android.view.View, int);
+    method public void clear();
+    method public int convertPreLayoutPositionToPostLayout(int);
+    method public java.util.List<android.support.v7.widget.RecyclerView.ViewHolder> getScrapList();
+    method public android.view.View getViewForPosition(int);
+    method public void recycleView(android.view.View);
+    method public void setViewCacheSize(int);
+  }
+
+  public static abstract interface RecyclerView.RecyclerListener {
+    method public abstract void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+  public static class RecyclerView.SimpleOnItemTouchListener implements android.support.v7.widget.RecyclerView.OnItemTouchListener {
+    ctor public RecyclerView.SimpleOnItemTouchListener();
+    method public boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public void onRequestDisallowInterceptTouchEvent(boolean);
+    method public void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.SmoothScroller {
+    ctor public RecyclerView.SmoothScroller();
+    method public android.view.View findViewByPosition(int);
+    method public int getChildCount();
+    method public int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getTargetPosition();
+    method public deprecated void instantScrollToPosition(int);
+    method public boolean isPendingInitialRun();
+    method public boolean isRunning();
+    method protected void normalize(android.graphics.PointF);
+    method protected void onChildAttachedToWindow(android.view.View);
+    method protected abstract void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected abstract void onStart();
+    method protected abstract void onStop();
+    method protected abstract void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method public void setTargetPosition(int);
+    method protected final void stop();
+  }
+
+  public static class RecyclerView.SmoothScroller.Action {
+    ctor public RecyclerView.SmoothScroller.Action(int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int, android.view.animation.Interpolator);
+    method public int getDuration();
+    method public int getDx();
+    method public int getDy();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public void jumpTo(int);
+    method public void setDuration(int);
+    method public void setDx(int);
+    method public void setDy(int);
+    method public void setInterpolator(android.view.animation.Interpolator);
+    method public void update(int, int, int, android.view.animation.Interpolator);
+    field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+  }
+
+  public static abstract interface RecyclerView.SmoothScroller.ScrollVectorProvider {
+    method public abstract android.graphics.PointF computeScrollVectorForPosition(int);
+  }
+
+  public static class RecyclerView.State {
+    ctor public RecyclerView.State();
+    method public boolean didStructureChange();
+    method public <T> T get(int);
+    method public int getItemCount();
+    method public int getTargetScrollPosition();
+    method public boolean hasTargetScrollPosition();
+    method public boolean isMeasuring();
+    method public boolean isPreLayout();
+    method public void put(int, java.lang.Object);
+    method public void remove(int);
+    method public boolean willRunPredictiveAnimations();
+    method public boolean willRunSimpleAnimations();
+  }
+
+  public static abstract class RecyclerView.ViewCacheExtension {
+    ctor public RecyclerView.ViewCacheExtension();
+    method public abstract android.view.View getViewForPositionAndType(android.support.v7.widget.RecyclerView.Recycler, int, int);
+  }
+
+  public static abstract class RecyclerView.ViewHolder {
+    ctor public RecyclerView.ViewHolder(android.view.View);
+    method public final int getAdapterPosition();
+    method public final long getItemId();
+    method public final int getItemViewType();
+    method public final int getLayoutPosition();
+    method public final int getOldPosition();
+    method public final deprecated int getPosition();
+    method public final boolean isRecyclable();
+    method public final void setIsRecyclable(boolean);
+    field public final android.view.View itemView;
+  }
+
+  public class RecyclerViewAccessibilityDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate(android.support.v7.widget.RecyclerView);
+    method public android.support.v4.view.AccessibilityDelegateCompat getItemDelegate();
+  }
+
+  public static class RecyclerViewAccessibilityDelegate.ItemDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate.ItemDelegate(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+  }
+
+  public class SearchView extends android.support.v7.widget.LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public java.lang.CharSequence getQuery();
+    method public java.lang.CharSequence getQueryHint();
+    method public android.support.v4.widget.CursorAdapter getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(android.support.v7.widget.SearchView.OnCloseListener);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener);
+    method public void setOnQueryTextListener(android.support.v7.widget.SearchView.OnQueryTextListener);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener);
+    method public void setOnSuggestionListener(android.support.v7.widget.SearchView.OnSuggestionListener);
+    method public void setQuery(java.lang.CharSequence, boolean);
+    method public void setQueryHint(java.lang.CharSequence);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(android.support.v4.widget.CursorAdapter);
+  }
+
+  public static abstract interface SearchView.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract interface SearchView.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchView.OnSuggestionListener {
+    method public abstract boolean onSuggestionClick(int);
+    method public abstract boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context);
+    method public android.view.View onCreateActionView();
+    method public void setOnShareTargetSelectedListener(android.support.v7.widget.ShareActionProvider.OnShareTargetSelectedListener);
+    method public void setShareHistoryFileName(java.lang.String);
+    method public void setShareIntent(android.content.Intent);
+    field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static abstract interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public abstract boolean onShareTargetSelected(android.support.v7.widget.ShareActionProvider, android.content.Intent);
+  }
+
+  public abstract class SimpleItemAnimator extends android.support.v7.widget.RecyclerView.ItemAnimator {
+    ctor public SimpleItemAnimator();
+    method public abstract boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean getSupportsChangeAnimations();
+    method public void onAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setSupportsChangeAnimations(boolean);
+  }
+
+  public abstract class SnapHelper extends android.support.v7.widget.RecyclerView.OnFlingListener {
+    ctor public SnapHelper();
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
+    method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public int[] calculateScrollDistance(int, int);
+    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+    method public boolean onFling(int, int);
+  }
+
+  public class StaggeredGridLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public StaggeredGridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public StaggeredGridLayoutManager(int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int[] findFirstCompletelyVisibleItemPositions(int[]);
+    method public int[] findFirstVisibleItemPositions(int[]);
+    method public int[] findLastCompletelyVisibleItemPositions(int[]);
+    method public int[] findLastVisibleItemPositions(int[]);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public int getGapStrategy();
+    method public int getOrientation();
+    method public boolean getReverseLayout();
+    method public int getSpanCount();
+    method public void invalidateSpanAssignments();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setGapStrategy(int);
+    method public void setOrientation(int);
+    method public void setReverseLayout(boolean);
+    method public void setSpanCount(int);
+    field public static final deprecated int GAP_HANDLING_LAZY = 1; // 0x1
+    field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
+    field public static final int GAP_HANDLING_NONE = 0; // 0x0
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class StaggeredGridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public final int getSpanIndex();
+    method public boolean isFullSpan();
+    method public void setFullSpan(boolean);
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public java.lang.CharSequence getTextOff();
+    method public java.lang.CharSequence getTextOn();
+    method public android.graphics.drawable.Drawable getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context, int);
+    method public void setSwitchTypeface(android.graphics.Typeface, int);
+    method public void setSwitchTypeface(android.graphics.Typeface);
+    method public void setTextOff(java.lang.CharSequence);
+    method public void setTextOn(java.lang.CharSequence);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public abstract interface ThemedSpinnerAdapter implements android.widget.SpinnerAdapter {
+    method public abstract android.content.res.Resources.Theme getDropDownViewTheme();
+    method public abstract void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable getLogo();
+    method public java.lang.CharSequence getLogoDescription();
+    method public android.view.Menu getMenu();
+    method public java.lang.CharSequence getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable getNavigationIcon();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable);
+    method public void setLogoDescription(int);
+    method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationIcon(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.Toolbar.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public void setSubtitle(int);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setSubtitleTextAppearance(android.content.Context, int);
+    method public void setSubtitleTextColor(int);
+    method public void setTitle(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context, int);
+    method public void setTitleTextColor(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends android.support.v7.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(android.support.v7.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
+  }
+
+  public static abstract interface Toolbar.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public static class Toolbar.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel);
+    ctor public Toolbar.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public Toolbar.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.widget.Toolbar.SavedState> CREATOR;
+  }
+
+}
+
+package android.support.v7.widget.helper {
+
+  public class ItemTouchHelper extends android.support.v7.widget.RecyclerView.ItemDecoration implements android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener {
+    ctor public ItemTouchHelper(android.support.v7.widget.helper.ItemTouchHelper.Callback);
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public void onChildViewAttachedToWindow(android.view.View);
+    method public void onChildViewDetachedFromWindow(android.view.View);
+    method public void startDrag(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void startSwipe(android.support.v7.widget.RecyclerView.ViewHolder);
+    field public static final int ACTION_STATE_DRAG = 2; // 0x2
+    field public static final int ACTION_STATE_IDLE = 0; // 0x0
+    field public static final int ACTION_STATE_SWIPE = 1; // 0x1
+    field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
+    field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
+    field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
+    field public static final int DOWN = 2; // 0x2
+    field public static final int END = 32; // 0x20
+    field public static final int LEFT = 4; // 0x4
+    field public static final int RIGHT = 8; // 0x8
+    field public static final int START = 16; // 0x10
+    field public static final int UP = 1; // 0x1
+  }
+
+  public static abstract class ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.Callback();
+    method public boolean canDropOver(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ViewHolder chooseDropTarget(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<android.support.v7.widget.RecyclerView.ViewHolder>, int, int);
+    method public void clearView(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int convertToAbsoluteDirection(int, int);
+    method public static int convertToRelativeDirection(int, int);
+    method public long getAnimationDuration(android.support.v7.widget.RecyclerView, int, float, float);
+    method public int getBoundingBoxMargin();
+    method public static android.support.v7.widget.helper.ItemTouchUIUtil getDefaultUIUtil();
+    method public float getMoveThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeEscapeVelocity(float);
+    method public float getSwipeThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeVelocityThreshold(float);
+    method public int interpolateOutOfBoundsScroll(android.support.v7.widget.RecyclerView, int, int, int, long);
+    method public boolean isItemViewSwipeEnabled();
+    method public boolean isLongPressDragEnabled();
+    method public static int makeFlag(int, int);
+    method public static int makeMovementFlags(int, int);
+    method public void onChildDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public void onChildDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public abstract boolean onMove(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoved(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int);
+    method public void onSelectedChanged(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method public abstract void onSwiped(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
+    field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
+  }
+
+  public static abstract class ItemTouchHelper.SimpleCallback extends android.support.v7.widget.helper.ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.SimpleCallback(int, int);
+    method public int getDragDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getSwipeDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setDefaultDragDirs(int);
+    method public void setDefaultSwipeDirs(int);
+  }
+
+  public static abstract interface ItemTouchHelper.ViewDropHandler {
+    method public abstract void prepareForDrop(android.view.View, android.view.View, int, int);
+  }
+
+  public abstract interface ItemTouchUIUtil {
+    method public abstract void clearView(android.view.View);
+    method public abstract void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onSelected(android.view.View);
+  }
+
+}
+
+package android.support.v7.widget.util {
+
+  public abstract class SortedListAdapterCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedListAdapterCallback(android.support.v7.widget.RecyclerView.Adapter);
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+}
+
+package android.support.wearable.view {
+
+  public class BoxInsetLayout extends android.view.ViewGroup {
+    ctor public BoxInsetLayout(android.content.Context);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected void onLayout(boolean, int, int, int, int);
+  }
+
+  public static class BoxInsetLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BoxInsetLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout.LayoutParams(int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.support.wearable.view.BoxInsetLayout.LayoutParams);
+    field public static final int BOX_ALL = 15; // 0xf
+    field public static final int BOX_BOTTOM = 8; // 0x8
+    field public static final int BOX_LEFT = 1; // 0x1
+    field public static final int BOX_NONE = 0; // 0x0
+    field public static final int BOX_RIGHT = 4; // 0x4
+    field public static final int BOX_TOP = 2; // 0x2
+    field public int boxedEdges;
+  }
+
+}
+
diff --git a/api/26.0.0-beta1.txt b/api/26.0.0-beta1.txt
new file mode 100644
index 0000000..4af97e8
--- /dev/null
+++ b/api/26.0.0-beta1.txt
@@ -0,0 +1,12850 @@
+package android.support.animation {
+
+  public abstract class DynamicAnimation<T extends android.support.animation.DynamicAnimation<T>> {
+    method public T addEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public T addUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public void cancel();
+    method public float getMinimumVisibleChange();
+    method public boolean isRunning();
+    method public void removeEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public void removeUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public T setMaxValue(float);
+    method public T setMinValue(float);
+    method public T setMinimumVisibleChange(float);
+    method public T setStartValue(float);
+    method public T setStartVelocity(float);
+    method public void start();
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ALPHA;
+    field public static final float MIN_VISIBLE_CHANGE_ALPHA = 0.00390625f;
+    field public static final float MIN_VISIBLE_CHANGE_PIXELS = 1.0f;
+    field public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 0.1f;
+    field public static final float MIN_VISIBLE_CHANGE_SCALE = 0.002f;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Z;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Z;
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationEndListener {
+    method public abstract void onAnimationEnd(android.support.animation.DynamicAnimation, boolean, float, float);
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationUpdateListener {
+    method public abstract void onAnimationUpdate(android.support.animation.DynamicAnimation, float, float);
+  }
+
+  public static abstract class DynamicAnimation.ViewProperty extends android.support.animation.FloatPropertyCompat {
+  }
+
+  public final class FlingAnimation extends android.support.animation.DynamicAnimation {
+    ctor public FlingAnimation(android.support.animation.FloatValueHolder);
+    ctor public FlingAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    method public float getFriction();
+    method public android.support.animation.FlingAnimation setFriction(float);
+  }
+
+  public abstract class FloatPropertyCompat<T> {
+    ctor public FloatPropertyCompat(java.lang.String);
+    method public static <T> android.support.animation.FloatPropertyCompat<T> createFloatPropertyCompat(android.util.FloatProperty<T>);
+    method public abstract float getValue(T);
+    method public abstract void setValue(T, float);
+  }
+
+  public final class FloatValueHolder {
+    ctor public FloatValueHolder();
+    ctor public FloatValueHolder(float);
+    method public float getValue();
+    method public void setValue(float);
+  }
+
+  public final class SpringAnimation extends android.support.animation.DynamicAnimation {
+    ctor public SpringAnimation(android.support.animation.FloatValueHolder);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>, float);
+    method public void animateToFinalPosition(float);
+    method public boolean canSkipToEnd();
+    method public android.support.animation.SpringForce getSpring();
+    method public android.support.animation.SpringAnimation setSpring(android.support.animation.SpringForce);
+    method public void skipToEnd();
+  }
+
+  public final class SpringForce {
+    ctor public SpringForce();
+    ctor public SpringForce(float);
+    method public float getDampingRatio();
+    method public float getFinalPosition();
+    method public float getStiffness();
+    method public android.support.animation.SpringForce setDampingRatio(float);
+    method public android.support.animation.SpringForce setFinalPosition(float);
+    method public android.support.animation.SpringForce setStiffness(float);
+    field public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
+    field public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
+    field public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
+    field public static final float DAMPING_RATIO_NO_BOUNCY = 1.0f;
+    field public static final float STIFFNESS_HIGH = 10000.0f;
+    field public static final float STIFFNESS_LOW = 200.0f;
+    field public static final float STIFFNESS_MEDIUM = 1500.0f;
+    field public static final float STIFFNESS_VERY_LOW = 50.0f;
+  }
+
+}
+
+package android.support.app.recommendation {
+
+  public final class ContentRecommendation {
+    method public java.lang.String getBackgroundImageUri();
+    method public int getBadgeImageResourceId();
+    method public int getColor();
+    method public android.graphics.Bitmap getContentImage();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
+    method public java.lang.String[] getContentTypes();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getGroup();
+    method public java.lang.String getIdTag();
+    method public java.lang.String getMaturityRating();
+    method public android.app.Notification getNotificationObject(android.content.Context);
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public int getProgressMax();
+    method public int getProgressValue();
+    method public long getRunningTime();
+    method public java.lang.String getSortKey();
+    method public java.lang.String getSourceName();
+    method public int getStatus();
+    method public java.lang.String getText();
+    method public java.lang.String getTitle();
+    method public boolean hasProgressInfo();
+    method public boolean isAutoDismiss();
+    method public void setAutoDismiss(boolean);
+    method public void setGroup(java.lang.String);
+    method public void setProgress(int, int);
+    method public void setSortKey(java.lang.String);
+    method public void setStatus(int);
+    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
+    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
+    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
+    field public static final int CONTENT_STATUS_READY = 0; // 0x0
+    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
+    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
+    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
+    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
+    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
+    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
+    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
+    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
+    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
+    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
+    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
+  }
+
+  public static final class ContentRecommendation.Builder {
+    ctor public ContentRecommendation.Builder();
+    method public android.support.app.recommendation.ContentRecommendation build();
+    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
+  }
+
+  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
+  }
+
+  public static class ContentRecommendation.IntentData {
+    ctor public ContentRecommendation.IntentData();
+  }
+
+  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
+  }
+
+  public final class RecommendationExtender implements android.app.Notification.Extender {
+    ctor public RecommendationExtender();
+    ctor public RecommendationExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String[] getContentTypes();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getMaturityRating();
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public long getRunningTime();
+    method public int getStatus();
+    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
+    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
+  }
+
+}
+
+package android.support.customtabs {
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(java.lang.String, android.os.Bundle);
+    method public void onMessageChannelReady(android.os.Bundle);
+    method public void onNavigationEvent(int, android.os.Bundle);
+    method public void onPostMessage(java.lang.String, android.os.Bundle);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, java.lang.String, android.support.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, java.lang.String);
+    method public android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>, boolean);
+    method public android.support.customtabs.CustomTabsSession newSession(android.support.customtabs.CustomTabsCallback);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context, android.net.Uri);
+    method public static android.content.Intent setAlwaysUseBrowserUI(android.content.Intent);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent);
+    field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final java.lang.String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final java.lang.String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final java.lang.String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final java.lang.String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final java.lang.String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final java.lang.String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
+    method public android.support.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
+    method public deprecated android.support.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, java.lang.String, android.app.PendingIntent) throws java.lang.IllegalStateException;
+    method public android.support.customtabs.CustomTabsIntent build();
+    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setToolbarColor(int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(android.support.customtabs.CustomTabsSessionToken);
+    method protected abstract android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract int postMessage(android.support.customtabs.CustomTabsSessionToken, java.lang.String, android.os.Bundle);
+    method protected abstract boolean requestPostMessageChannel(android.support.customtabs.CustomTabsSessionToken, android.net.Uri);
+    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
+    method protected abstract boolean warmup(long);
+    field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class CustomTabsService.Result implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName, android.support.customtabs.CustomTabsClient);
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+  }
+
+  public final class CustomTabsSession {
+    method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method public int postMessage(java.lang.String, android.os.Bundle);
+    method public boolean requestPostMessageChannel(android.net.Uri);
+    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public deprecated boolean setToolbarItem(int, android.graphics.Bitmap, java.lang.String);
+  }
+
+  public class CustomTabsSessionToken {
+    method public android.support.customtabs.CustomTabsCallback getCallback();
+    method public static android.support.customtabs.CustomTabsSessionToken getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(android.support.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(android.support.customtabs.CustomTabsSessionToken);
+    method public boolean bindSessionToPostMessageService(android.content.Context, java.lang.String);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+    method public final void onServiceDisconnected(android.content.ComponentName);
+    method public final boolean postMessage(java.lang.String, android.os.Bundle);
+    method public void unbindFromContext(android.content.Context);
+  }
+
+}
+
+package android.support.design.widget {
+
+  public class AppBarLayout extends android.widget.LinearLayout {
+    ctor public AppBarLayout(android.content.Context);
+    ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
+    method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public deprecated float getTargetElevation();
+    method public final int getTotalScrollRange();
+    method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public void setExpanded(boolean);
+    method public void setExpanded(boolean, boolean);
+    method public deprecated void setTargetElevation(float);
+  }
+
+  public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
+    ctor public AppBarLayout.Behavior();
+    ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, float, float, boolean);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View);
+    method public void setDragCallback(android.support.design.widget.AppBarLayout.Behavior.DragCallback);
+  }
+
+  public static abstract class AppBarLayout.Behavior.DragCallback {
+    ctor public AppBarLayout.Behavior.DragCallback();
+    method public abstract boolean canDrag(android.support.design.widget.AppBarLayout);
+  }
+
+  protected static class AppBarLayout.Behavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+  }
+
+  public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public AppBarLayout.LayoutParams(int, int);
+    ctor public AppBarLayout.LayoutParams(int, int, float);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.widget.LinearLayout.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.support.design.widget.AppBarLayout.LayoutParams);
+    method public int getScrollFlags();
+    method public android.view.animation.Interpolator getScrollInterpolator();
+    method public void setScrollFlags(int);
+    method public void setScrollInterpolator(android.view.animation.Interpolator);
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS = 4; // 0x4
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
+    field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
+    field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
+    field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
+  }
+
+  public static abstract interface AppBarLayout.OnOffsetChangedListener {
+    method public abstract void onOffsetChanged(android.support.design.widget.AppBarLayout, int);
+  }
+
+  public static class AppBarLayout.ScrollingViewBehavior extends android.support.design.widget.HeaderScrollingViewBehavior {
+    ctor public AppBarLayout.ScrollingViewBehavior();
+    ctor public AppBarLayout.ScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
+  }
+
+  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
+    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
+    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public void dismiss();
+    method public android.content.Context getContext();
+    method public int getDuration();
+    method public android.view.View getView();
+    method public boolean isShown();
+    method public boolean isShownOrQueued();
+    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public B setDuration(int);
+    method public void show();
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
+    ctor public BaseTransientBottomBar.BaseCallback();
+    method public void onDismissed(B, int);
+    method public void onShown(B);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
+    method public abstract void animateContentIn(int, int);
+    method public abstract void animateContentOut(int, int);
+  }
+
+  public class BottomNavigationView extends android.widget.FrameLayout {
+    ctor public BottomNavigationView(android.content.Context);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public int getItemBackgroundResource();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public int getMaxItemCount();
+    method public android.view.Menu getMenu();
+    method public int getSelectedItemId();
+    method public void inflateMenu(int);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setOnNavigationItemReselectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemReselectedListener);
+    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
+    method public void setSelectedItemId(int);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemReselectedListener {
+    method public abstract void onNavigationItemReselected(android.view.MenuItem);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public class BottomSheetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public BottomSheetBehavior();
+    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
+    method public static <V extends android.view.View> android.support.design.widget.BottomSheetBehavior<V> from(V);
+    method public final int getPeekHeight();
+    method public boolean getSkipCollapsed();
+    method public final int getState();
+    method public boolean isHideable();
+    method public void setBottomSheetCallback(android.support.design.widget.BottomSheetBehavior.BottomSheetCallback);
+    method public void setHideable(boolean);
+    method public final void setPeekHeight(int);
+    method public void setSkipCollapsed(boolean);
+    method public final void setState(int);
+    field public static final int PEEK_HEIGHT_AUTO = -1; // 0xffffffff
+    field public static final int STATE_COLLAPSED = 4; // 0x4
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_EXPANDED = 3; // 0x3
+    field public static final int STATE_HIDDEN = 5; // 0x5
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class BottomSheetBehavior.BottomSheetCallback {
+    ctor public BottomSheetBehavior.BottomSheetCallback();
+    method public abstract void onSlide(android.view.View, float);
+    method public abstract void onStateChanged(android.view.View, int);
+  }
+
+  protected static class BottomSheetBehavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
+  }
+
+  public class BottomSheetDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public BottomSheetDialog(android.content.Context);
+    ctor public BottomSheetDialog(android.content.Context, int);
+    ctor protected BottomSheetDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+  }
+
+  public class BottomSheetDialogFragment extends android.support.v7.app.AppCompatDialogFragment {
+    ctor public BottomSheetDialogFragment();
+  }
+
+  public class CollapsingToolbarLayout extends android.widget.FrameLayout {
+    ctor public CollapsingToolbarLayout(android.content.Context);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCollapsedTitleGravity();
+    method public android.graphics.Typeface getCollapsedTitleTypeface();
+    method public android.graphics.drawable.Drawable getContentScrim();
+    method public int getExpandedTitleGravity();
+    method public int getExpandedTitleMarginBottom();
+    method public int getExpandedTitleMarginEnd();
+    method public int getExpandedTitleMarginStart();
+    method public int getExpandedTitleMarginTop();
+    method public android.graphics.Typeface getExpandedTitleTypeface();
+    method public long getScrimAnimationDuration();
+    method public int getScrimVisibleHeightTrigger();
+    method public android.graphics.drawable.Drawable getStatusBarScrim();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isTitleEnabled();
+    method public void setCollapsedTitleGravity(int);
+    method public void setCollapsedTitleTextAppearance(int);
+    method public void setCollapsedTitleTextColor(int);
+    method public void setCollapsedTitleTextColor(android.content.res.ColorStateList);
+    method public void setCollapsedTitleTypeface(android.graphics.Typeface);
+    method public void setContentScrim(android.graphics.drawable.Drawable);
+    method public void setContentScrimColor(int);
+    method public void setContentScrimResource(int);
+    method public void setExpandedTitleColor(int);
+    method public void setExpandedTitleGravity(int);
+    method public void setExpandedTitleMargin(int, int, int, int);
+    method public void setExpandedTitleMarginBottom(int);
+    method public void setExpandedTitleMarginEnd(int);
+    method public void setExpandedTitleMarginStart(int);
+    method public void setExpandedTitleMarginTop(int);
+    method public void setExpandedTitleTextAppearance(int);
+    method public void setExpandedTitleTextColor(android.content.res.ColorStateList);
+    method public void setExpandedTitleTypeface(android.graphics.Typeface);
+    method public void setScrimAnimationDuration(long);
+    method public void setScrimVisibleHeightTrigger(int);
+    method public void setScrimsShown(boolean);
+    method public void setScrimsShown(boolean, boolean);
+    method public void setStatusBarScrim(android.graphics.drawable.Drawable);
+    method public void setStatusBarScrimColor(int);
+    method public void setStatusBarScrimResource(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleEnabled(boolean);
+  }
+
+  public static class CollapsingToolbarLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public CollapsingToolbarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    method public int getCollapseMode();
+    method public float getParallaxMultiplier();
+    method public void setCollapseMode(int);
+    method public void setParallaxMultiplier(float);
+    field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
+    field public static final int COLLAPSE_MODE_PARALLAX = 2; // 0x2
+    field public static final int COLLAPSE_MODE_PIN = 1; // 0x1
+  }
+
+  public class CoordinatorLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingParent {
+    ctor public CoordinatorLayout(android.content.Context);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void dispatchDependentViewsChanged(android.view.View);
+    method public boolean doViewsOverlap(android.view.View, android.view.View);
+    method public java.util.List<android.view.View> getDependencies(android.view.View);
+    method public java.util.List<android.view.View> getDependents(android.view.View);
+    method public android.graphics.drawable.Drawable getStatusBarBackground();
+    method public boolean isPointInChildBounds(android.view.View, int, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onLayoutChild(android.view.View, int);
+    method public void onMeasureChild(android.view.View, int, int, int, int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackgroundColor(int);
+    method public void setStatusBarBackgroundResource(int);
+  }
+
+  public static abstract class CoordinatorLayout.Behavior<V extends android.view.View> {
+    ctor public CoordinatorLayout.Behavior();
+    ctor public CoordinatorLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean blocksInteractionBelow(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect);
+    method public int getScrimColor(android.support.design.widget.CoordinatorLayout, V);
+    method public float getScrimOpacity(android.support.design.widget.CoordinatorLayout, V);
+    method public static java.lang.Object getTag(android.view.View);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.support.design.widget.CoordinatorLayout, V, android.support.v4.view.WindowInsetsCompat);
+    method public void onAttachedToLayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDependentViewRemoved(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDetachedFromLayoutParams();
+    method public boolean onInterceptTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, V, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect, boolean);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public static void setTag(android.view.View, java.lang.Object);
+  }
+
+  public static abstract class CoordinatorLayout.DefaultBehavior implements java.lang.annotation.Annotation {
+  }
+
+  public static class CoordinatorLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public CoordinatorLayout.LayoutParams(int, int);
+    ctor public CoordinatorLayout.LayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAnchorId();
+    method public android.support.design.widget.CoordinatorLayout.Behavior getBehavior();
+    method public void setAnchorId(int);
+    method public void setBehavior(android.support.design.widget.CoordinatorLayout.Behavior);
+    field public int anchorGravity;
+    field public int dodgeInsetEdges;
+    field public int gravity;
+    field public int insetEdge;
+    field public int keyline;
+  }
+
+  protected static class CoordinatorLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public CoordinatorLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+  }
+
+  public class FloatingActionButton extends android.support.design.widget.VisibilityAwareImageButton {
+    ctor public FloatingActionButton(android.content.Context);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+    method public float getCompatElevation();
+    method public android.graphics.drawable.Drawable getContentBackground();
+    method public boolean getContentRect(android.graphics.Rect);
+    method public int getRippleColor();
+    method public int getSize();
+    method public boolean getUseCompatPadding();
+    method public void hide();
+    method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    method public void setCompatElevation(float);
+    method public void setRippleColor(int);
+    method public void setSize(int);
+    method public void setUseCompatPadding(boolean);
+    method public void show();
+    method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    field public static final int SIZE_AUTO = -1; // 0xffffffff
+    field public static final int SIZE_MINI = 1; // 0x1
+    field public static final int SIZE_NORMAL = 0; // 0x0
+  }
+
+  public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public FloatingActionButton.Behavior();
+    ctor public FloatingActionButton.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.graphics.Rect);
+    method public boolean isAutoHideEnabled();
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.view.View);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, int);
+    method public void setAutoHideEnabled(boolean);
+  }
+
+  public static abstract class FloatingActionButton.OnVisibilityChangedListener {
+    ctor public FloatingActionButton.OnVisibilityChangedListener();
+    method public void onHidden(android.support.design.widget.FloatingActionButton);
+    method public void onShown(android.support.design.widget.FloatingActionButton);
+  }
+
+   abstract class HeaderBehavior<V extends android.view.View> extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderBehavior();
+    ctor public HeaderBehavior(android.content.Context, android.util.AttributeSet);
+  }
+
+   abstract class HeaderScrollingViewBehavior extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderScrollingViewBehavior();
+    ctor public HeaderScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public final int getOverlayTop();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
+    method public final void setOverlayTop(int);
+  }
+
+  public class NavigationView extends android.widget.FrameLayout {
+    ctor public NavigationView(android.content.Context);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public void addHeaderView(android.view.View);
+    method public int getHeaderCount();
+    method public android.view.View getHeaderView(int);
+    method public android.graphics.drawable.Drawable getItemBackground();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public android.view.Menu getMenu();
+    method public android.view.View inflateHeaderView(int);
+    method public void inflateMenu(int);
+    method public void removeHeaderView(android.view.View);
+    method public void setCheckedItem(int);
+    method public void setItemBackground(android.graphics.drawable.Drawable);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextAppearance(int);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setNavigationItemSelectedListener(android.support.design.widget.NavigationView.OnNavigationItemSelectedListener);
+  }
+
+  public static abstract interface NavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public static class NavigationView.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public NavigationView.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public NavigationView.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.NavigationView.SavedState> CREATOR;
+    field public android.os.Bundle menuState;
+  }
+
+  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
+    method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
+    method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
+    method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
+    method public android.support.design.widget.Snackbar setActionTextColor(int);
+    method public deprecated android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
+    method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
+    method public android.support.design.widget.Snackbar setText(int);
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
+    ctor public Snackbar.Callback();
+    method public void onDismissed(android.support.design.widget.Snackbar, int);
+    method public void onShown(android.support.design.widget.Snackbar);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public SwipeDismissBehavior();
+    method public boolean canSwipeDismissView(android.view.View);
+    method public int getDragState();
+    method public void setDragDismissDistance(float);
+    method public void setEndAlphaSwipeDistance(float);
+    method public void setListener(android.support.design.widget.SwipeDismissBehavior.OnDismissListener);
+    method public void setSensitivity(float);
+    method public void setStartAlphaSwipeDistance(float);
+    method public void setSwipeDirection(int);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_ANY = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_END_TO_START = 1; // 0x1
+    field public static final int SWIPE_DIRECTION_START_TO_END = 0; // 0x0
+  }
+
+  public static abstract interface SwipeDismissBehavior.OnDismissListener {
+    method public abstract void onDismiss(android.view.View);
+    method public abstract void onDragStateChanged(int);
+  }
+
+  public final class TabItem extends android.view.View {
+    ctor public TabItem(android.content.Context);
+    ctor public TabItem(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class TabLayout extends android.widget.HorizontalScrollView {
+    ctor public TabLayout(android.content.Context);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void addTab(android.support.design.widget.TabLayout.Tab);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
+    method public void clearOnTabSelectedListeners();
+    method public int getSelectedTabPosition();
+    method public android.support.design.widget.TabLayout.Tab getTabAt(int);
+    method public int getTabCount();
+    method public int getTabGravity();
+    method public int getTabMode();
+    method public android.content.res.ColorStateList getTabTextColors();
+    method public android.support.design.widget.TabLayout.Tab newTab();
+    method public void removeAllTabs();
+    method public void removeOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void removeTab(android.support.design.widget.TabLayout.Tab);
+    method public void removeTabAt(int);
+    method public deprecated void setOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void setScrollPosition(int, float, boolean);
+    method public void setSelectedTabIndicatorColor(int);
+    method public void setSelectedTabIndicatorHeight(int);
+    method public void setTabGravity(int);
+    method public void setTabMode(int);
+    method public void setTabTextColors(android.content.res.ColorStateList);
+    method public void setTabTextColors(int, int);
+    method public deprecated void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager, boolean);
+    field public static final int GRAVITY_CENTER = 1; // 0x1
+    field public static final int GRAVITY_FILL = 0; // 0x0
+    field public static final int MODE_FIXED = 1; // 0x1
+    field public static final int MODE_SCROLLABLE = 0; // 0x0
+  }
+
+  public static abstract interface TabLayout.OnTabSelectedListener {
+    method public abstract void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public static final class TabLayout.Tab {
+    method public java.lang.CharSequence getContentDescription();
+    method public android.view.View getCustomView();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public int getPosition();
+    method public java.lang.Object getTag();
+    method public java.lang.CharSequence getText();
+    method public boolean isSelected();
+    method public void select();
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(int);
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(android.view.View);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(int);
+    method public android.support.design.widget.TabLayout.Tab setIcon(android.graphics.drawable.Drawable);
+    method public android.support.design.widget.TabLayout.Tab setIcon(int);
+    method public android.support.design.widget.TabLayout.Tab setTag(java.lang.Object);
+    method public android.support.design.widget.TabLayout.Tab setText(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public class TextInputEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public TextInputEditText(android.content.Context);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class TextInputLayout extends android.widget.LinearLayout {
+    ctor public TextInputLayout(android.content.Context);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCounterMaxLength();
+    method public android.widget.EditText getEditText();
+    method public java.lang.CharSequence getError();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
+    method public android.graphics.Typeface getTypeface();
+    method public boolean isCounterEnabled();
+    method public boolean isErrorEnabled();
+    method public boolean isHintAnimationEnabled();
+    method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void setCounterEnabled(boolean);
+    method public void setCounterMaxLength(int);
+    method public void setError(java.lang.CharSequence);
+    method public void setErrorEnabled(boolean);
+    method public void setErrorTextAppearance(int);
+    method public void setHint(java.lang.CharSequence);
+    method public void setHintAnimationEnabled(boolean);
+    method public void setHintEnabled(boolean);
+    method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTypeface(android.graphics.Typeface);
+  }
+
+   class ViewOffsetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public ViewOffsetBehavior();
+    ctor public ViewOffsetBehavior(android.content.Context, android.util.AttributeSet);
+    method public int getLeftAndRightOffset();
+    method public int getTopAndBottomOffset();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean setLeftAndRightOffset(int);
+    method public boolean setTopAndBottomOffset(int);
+  }
+
+   class VisibilityAwareImageButton extends android.widget.ImageButton {
+    ctor public VisibilityAwareImageButton(android.content.Context);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+}
+
+package android.support.graphics.drawable {
+
+  public abstract interface Animatable2Compat {
+    method public abstract void clearAnimationCallbacks();
+    method public abstract void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public abstract boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+  public static abstract class Animatable2Compat.AnimationCallback {
+    ctor public Animatable2Compat.AnimationCallback();
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
+  }
+
+  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon implements android.support.graphics.drawable.Animatable2Compat {
+    method public void clearAnimationCallbacks();
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public boolean isRunning();
+    method public void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void start();
+    method public void stop();
+    method public boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
+  }
+
+  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
+    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+  }
+
+}
+
+package android.support.media {
+
+  public class ExifInterface {
+    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
+    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
+    method public double getAltitude(double);
+    method public java.lang.String getAttribute(java.lang.String);
+    method public double getAttributeDouble(java.lang.String, double);
+    method public int getAttributeInt(java.lang.String, int);
+    method public deprecated boolean getLatLong(float[]);
+    method public double[] getLatLong();
+    method public byte[] getThumbnail();
+    method public android.graphics.Bitmap getThumbnailBitmap();
+    method public byte[] getThumbnailBytes();
+    method public long[] getThumbnailRange();
+    method public boolean hasThumbnail();
+    method public boolean isThumbnailCompressed();
+    method public void saveAttributes() throws java.io.IOException;
+    method public void setAttribute(java.lang.String, java.lang.String);
+    method public void setLatLong(double, double);
+    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
+    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
+    field public static final int ORIENTATION_NORMAL = 1; // 0x1
+    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
+    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
+    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
+    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
+    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
+    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+    field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+    field public static final java.lang.String TAG_COMPRESSION = "Compression";
+    field public static final java.lang.String TAG_CONTRAST = "Contrast";
+    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
+    field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
+    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
+    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
+    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
+    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
+    field public static final java.lang.String TAG_FLASH = "Flash";
+    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
+    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
+    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
+    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
+    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
+    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
+    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
+    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
+    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
+    field public static final java.lang.String TAG_MAKE = "Make";
+    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
+    field public static final java.lang.String TAG_MODEL = "Model";
+    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
+    field public static final java.lang.String TAG_OECF = "OECF";
+    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
+    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
+    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+    field public static final java.lang.String TAG_RW2_ISO = "ISO";
+    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
+    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+    field public static final java.lang.String TAG_SATURATION = "Saturation";
+    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+    field public static final java.lang.String TAG_SOFTWARE = "Software";
+    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
+    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
+    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
+    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
+    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
+  }
+
+}
+
+package android.support.media.tv {
+
+  public abstract class BasePreviewProgram extends android.support.media.tv.BaseProgram {
+    method public java.lang.String getAuthor();
+    method public int getAvailability();
+    method public java.lang.String getContentId();
+    method public int getDurationMillis();
+    method public android.content.Intent getIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getIntentUri();
+    method public long getInteractionCount();
+    method public int getInteractionType();
+    method public java.lang.String getInternalProviderId();
+    method public int getItemCount();
+    method public int getLastPlaybackPositionMillis();
+    method public android.net.Uri getLogoUri();
+    method public java.lang.String getOfferPrice();
+    method public int getPosterArtAspectRatio();
+    method public android.net.Uri getPreviewVideoUri();
+    method public java.lang.String getReleaseDate();
+    method public java.lang.String getStartingPrice();
+    method public int getThumbnailAspectRatio();
+    method public int getType();
+    method public boolean isBrowsable();
+    method public boolean isLive();
+    method public boolean isTransient();
+  }
+
+  public static abstract class BasePreviewProgram.Builder<T extends android.support.media.tv.BasePreviewProgram.Builder> extends android.support.media.tv.BaseProgram.Builder {
+    ctor public BasePreviewProgram.Builder();
+    ctor public BasePreviewProgram.Builder(android.support.media.tv.BasePreviewProgram);
+    method public T setAuthor(java.lang.String);
+    method public T setAvailability(int);
+    method public T setContentId(java.lang.String);
+    method public T setDurationMillis(int);
+    method public T setIntent(android.content.Intent);
+    method public T setIntentUri(android.net.Uri);
+    method public T setInteractionCount(long);
+    method public T setInteractionType(int);
+    method public T setInternalProviderId(java.lang.String);
+    method public T setItemCount(int);
+    method public T setLastPlaybackPositionMillis(int);
+    method public T setLive(boolean);
+    method public T setLogoUri(android.net.Uri);
+    method public T setOfferPrice(java.lang.String);
+    method public T setPosterArtAspectRatio(int);
+    method public T setPreviewVideoUri(android.net.Uri);
+    method public T setReleaseDate(java.lang.String);
+    method public T setReleaseDate(java.util.Date);
+    method public T setStartingPrice(java.lang.String);
+    method public T setThumbnailAspectRatio(int);
+    method public T setTransient(boolean);
+    method public T setType(int);
+  }
+
+  public abstract class BaseProgram {
+    method public java.lang.String[] getAudioLanguages();
+    method public java.lang.String[] getCanonicalGenres();
+    method public android.media.tv.TvContentRating[] getContentRatings();
+    method public java.lang.String getDescription();
+    method public java.lang.String getEpisodeNumber();
+    method public java.lang.String getEpisodeTitle();
+    method public long getId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getLongDescription();
+    method public android.net.Uri getPosterArtUri();
+    method public java.lang.String getReviewRating();
+    method public int getReviewRatingStyle();
+    method public java.lang.String getSeasonNumber();
+    method public java.lang.String getSeasonTitle();
+    method public android.net.Uri getThumbnailUri();
+    method public java.lang.String getTitle();
+    method public int getVideoHeight();
+    method public int getVideoWidth();
+    method public boolean isSearchable();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static abstract class BaseProgram.Builder<T extends android.support.media.tv.BaseProgram.Builder> {
+    ctor public BaseProgram.Builder();
+    ctor public BaseProgram.Builder(android.support.media.tv.BaseProgram);
+    method public T setAudioLanguages(java.lang.String[]);
+    method public T setCanonicalGenres(java.lang.String[]);
+    method public T setContentRatings(android.media.tv.TvContentRating[]);
+    method public T setDescription(java.lang.String);
+    method public T setEpisodeNumber(int);
+    method public T setEpisodeNumber(java.lang.String, int);
+    method public T setEpisodeTitle(java.lang.String);
+    method public T setId(long);
+    method public T setInternalProviderData(byte[]);
+    method public T setInternalProviderFlag1(long);
+    method public T setInternalProviderFlag2(long);
+    method public T setInternalProviderFlag3(long);
+    method public T setInternalProviderFlag4(long);
+    method public T setLongDescription(java.lang.String);
+    method public T setPosterArtUri(android.net.Uri);
+    method public T setReviewRating(java.lang.String);
+    method public T setReviewRatingStyle(int);
+    method public T setSearchable(boolean);
+    method public T setSeasonNumber(int);
+    method public T setSeasonNumber(java.lang.String, int);
+    method public T setSeasonTitle(java.lang.String);
+    method public T setThumbnailUri(android.net.Uri);
+    method public T setTitle(java.lang.String);
+    method public T setVideoHeight(int);
+    method public T setVideoWidth(int);
+  }
+
+  public final class Channel {
+    method public static android.support.media.tv.Channel fromCursor(android.database.Cursor);
+    method public int getAppLinkColor();
+    method public android.net.Uri getAppLinkIconUri();
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public android.net.Uri getAppLinkPosterArtUri();
+    method public java.lang.String getAppLinkText();
+    method public java.lang.String getDescription();
+    method public java.lang.String getDisplayName();
+    method public java.lang.String getDisplayNumber();
+    method public long getId();
+    method public java.lang.String getInputId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getInternalProviderId();
+    method public java.lang.String getNetworkAffiliation();
+    method public int getOriginalNetworkId();
+    method public java.lang.String getPackageName();
+    method public int getServiceId();
+    method public java.lang.String getServiceType();
+    method public int getTransportStreamId();
+    method public java.lang.String getType();
+    method public java.lang.String getVideoFormat();
+    method public boolean isBrowsable();
+    method public boolean isLocked();
+    method public boolean isSearchable();
+    method public boolean isTransient();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static final class Channel.Builder {
+    ctor public Channel.Builder();
+    ctor public Channel.Builder(android.support.media.tv.Channel);
+    method public android.support.media.tv.Channel build();
+    method public android.support.media.tv.Channel.Builder setAppLinkColor(int);
+    method public android.support.media.tv.Channel.Builder setAppLinkIconUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkPosterArtUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkText(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDescription(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayName(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayNumber(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInputId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setNetworkAffiliation(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setOriginalNetworkId(int);
+    method public android.support.media.tv.Channel.Builder setSearchable(boolean);
+    method public android.support.media.tv.Channel.Builder setServiceId(int);
+    method public android.support.media.tv.Channel.Builder setServiceType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setTransient(boolean);
+    method public android.support.media.tv.Channel.Builder setTransportStreamId(int);
+    method public android.support.media.tv.Channel.Builder setType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
+  }
+
+  public class ChannelLogoUtils {
+    ctor public ChannelLogoUtils();
+    method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
+  }
+
+  public final class PreviewProgram extends android.support.media.tv.BasePreviewProgram {
+    method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
+    method public long getChannelId();
+    method public int getWeight();
+  }
+
+  public static final class PreviewProgram.Builder extends android.support.media.tv.BasePreviewProgram.Builder {
+    ctor public PreviewProgram.Builder();
+    ctor public PreviewProgram.Builder(android.support.media.tv.PreviewProgram);
+    method public android.support.media.tv.PreviewProgram build();
+    method public android.support.media.tv.PreviewProgram.Builder setChannelId(long);
+    method public android.support.media.tv.PreviewProgram.Builder setWeight(int);
+  }
+
+  public final class Program extends android.support.media.tv.BaseProgram implements java.lang.Comparable {
+    method public int compareTo(android.support.media.tv.Program);
+    method public static android.support.media.tv.Program fromCursor(android.database.Cursor);
+    method public java.lang.String[] getBroadcastGenres();
+    method public long getChannelId();
+    method public long getEndTimeUtcMillis();
+    method public long getStartTimeUtcMillis();
+    method public boolean isRecordingProhibited();
+  }
+
+  public static class Program.Builder extends android.support.media.tv.BaseProgram.Builder {
+    ctor public Program.Builder();
+    ctor public Program.Builder(android.support.media.tv.Program);
+    method public android.support.media.tv.Program build();
+    method public android.support.media.tv.Program.Builder setBroadcastGenres(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setChannelId(long);
+    method public android.support.media.tv.Program.Builder setEndTimeUtcMillis(long);
+    method public android.support.media.tv.Program.Builder setRecordingProhibited(boolean);
+    method public android.support.media.tv.Program.Builder setStartTimeUtcMillis(long);
+  }
+
+  public final class TvContractCompat {
+    method public static android.net.Uri buildChannelLogoUri(long);
+    method public static android.net.Uri buildChannelLogoUri(android.net.Uri);
+    method public static android.net.Uri buildChannelUri(long);
+    method public static android.net.Uri buildChannelUriForPassthroughInput(java.lang.String);
+    method public static android.net.Uri buildChannelsUriForInput(java.lang.String);
+    method public static java.lang.String buildInputId(android.content.ComponentName);
+    method public static android.net.Uri buildPreviewProgramUri(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramUri(long);
+    method public static android.net.Uri buildProgramsUriForChannel(long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramsUriForChannel(long, long, long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static android.net.Uri buildRecordedProgramUri(long);
+    method public static android.net.Uri buildWatchNextProgramUri(long);
+    method public static boolean isChannelUri(android.net.Uri);
+    method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
+    method public static boolean isChannelUriForTunerInput(android.net.Uri);
+    method public static boolean isProgramUri(android.net.Uri);
+    method public static void requestChannelBrowsable(android.content.Context, long);
+    field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
+    field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String AUTHORITY = "android.media.tv";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
+    field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
+  }
+
+  public static abstract interface TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
+  }
+
+  public static final class TvContractCompat.Channels implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    method public static java.lang.String getVideoResolution(java.lang.String);
+    field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
+    field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
+    field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
+    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_LOCKED = "locked";
+    field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
+    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+    field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+    field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+    field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+    field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+    field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+    field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+    field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+    field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+    field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+    field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+    field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+    field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+    field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+    field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+    field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+    field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+    field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+    field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+    field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+    field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+    field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+    field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+    field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
+    field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+    field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+    field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+  }
+
+  public static final class TvContractCompat.Channels.Logo {
+    field public static final java.lang.String CONTENT_DIRECTORY = "logo";
+  }
+
+  public static final class TvContractCompat.PreviewPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WEIGHT = "weight";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs.Genres {
+    method public static java.lang.String[] decode(java.lang.String);
+    method public static java.lang.String encode(java.lang.String...);
+    method public static boolean isCanonical(java.lang.String);
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String ARTS = "ARTS";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String ENTERTAINMENT = "ENTERTAINMENT";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String LIFE_STYLE = "LIFE_STYLE";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String MUSIC = "MUSIC";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String PREMIER = "PREMIER";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TECH_SCIENCE = "TECH_SCIENCE";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
+  }
+
+  public static final class TvContractCompat.RecordedPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.WatchNextPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    ctor public TvContractCompat.WatchNextPrograms();
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0
+    field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2
+    field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3
+  }
+
+  public final class WatchNextProgram extends android.support.media.tv.BasePreviewProgram {
+    method public static android.support.media.tv.WatchNextProgram fromCursor(android.database.Cursor);
+    method public long getLastEngagementTimeUtcMillis();
+    method public int getWatchNextType();
+  }
+
+  public static final class WatchNextProgram.Builder extends android.support.media.tv.BasePreviewProgram.Builder {
+    ctor public WatchNextProgram.Builder();
+    ctor public WatchNextProgram.Builder(android.support.media.tv.WatchNextProgram);
+    method public android.support.media.tv.WatchNextProgram build();
+    method public android.support.media.tv.WatchNextProgram.Builder setLastEngagementTimeUtcMillis(long);
+    method public android.support.media.tv.WatchNextProgram.Builder setWatchNextType(int);
+  }
+
+}
+
+package android.support.percent {
+
+  public deprecated class PercentFrameLayout extends android.widget.FrameLayout {
+    ctor public PercentFrameLayout(android.content.Context);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static deprecated class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout.LayoutParams(int, int);
+    ctor public PercentFrameLayout.LayoutParams(int, int, int);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentLayoutHelper {
+    ctor public PercentLayoutHelper(android.view.ViewGroup);
+    method public void adjustChildren(int, int);
+    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+    method public boolean handleMeasuredStateTooSmall();
+    method public void restoreOriginalParams();
+  }
+
+  public static deprecated class PercentLayoutHelper.PercentLayoutInfo {
+    ctor public PercentLayoutHelper.PercentLayoutInfo();
+    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+    method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void fillMarginLayoutParams(android.view.View, android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public float aspectRatio;
+    field public float bottomMarginPercent;
+    field public float endMarginPercent;
+    field public float heightPercent;
+    field public float leftMarginPercent;
+    field public float rightMarginPercent;
+    field public float startMarginPercent;
+    field public float topMarginPercent;
+    field public float widthPercent;
+  }
+
+  public static abstract deprecated interface PercentLayoutHelper.PercentLayoutParams {
+    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentRelativeLayout extends android.widget.RelativeLayout {
+    ctor public PercentRelativeLayout(android.content.Context);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static deprecated class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout.LayoutParams(int, int);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+}
+
+package android.support.text.emoji {
+
+  public class EmojiCompat {
+    method public static android.support.text.emoji.EmojiCompat get();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, int, int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence, int);
+    method public static android.support.text.emoji.EmojiCompat init(android.support.text.emoji.EmojiCompat.Config);
+    method public java.lang.CharSequence process(java.lang.CharSequence);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int);
+    method public void registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final java.lang.String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_FAILURE = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCESS = 1; // 0x1
+  }
+
+  public static abstract class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataLoader);
+    method public android.support.text.emoji.EmojiCompat.Config registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config setReplaceAll(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+  }
+
+  public static abstract class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(java.lang.Throwable);
+    method public void onInitialized();
+  }
+
+  public static abstract class EmojiCompat.LoaderCallback {
+    ctor public EmojiCompat.LoaderCallback();
+    method public abstract void onFailed(java.lang.Throwable);
+    method public abstract void onLoaded(android.support.text.emoji.MetadataRepo);
+  }
+
+  public static abstract interface EmojiCompat.MetadataLoader {
+    method public abstract void load(android.support.text.emoji.EmojiCompat.LoaderCallback);
+  }
+
+  public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, java.lang.CharSequence, int, int, android.graphics.Paint.FontMetricsInt);
+  }
+
+  public class FontRequestEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, android.support.v4.provider.FontRequest);
+  }
+
+  public final class MetadataRepo {
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.content.res.AssetManager, java.lang.String) throws java.io.IOException;
+  }
+
+}
+
+package android.support.text.emoji.bundled {
+
+  public class BundledEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
+package android.support.text.emoji.widget {
+
+  public class EmojiAppCompatButton extends android.support.v7.widget.AppCompatButton {
+    ctor public EmojiAppCompatButton(android.content.Context);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiAppCompatEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public EmojiAppCompatEditText(android.content.Context);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiAppCompatTextView extends android.support.v7.widget.AppCompatTextView {
+    ctor public EmojiAppCompatTextView(android.content.Context);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter[] getFilters(android.text.InputFilter[]);
+    method public void setAllCaps(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod wrapTransformationMethod(android.text.method.TransformationMethod);
+  }
+
+}
+
+package android.support.transition {
+
+  public class ArcMotion extends android.support.transition.PathMotion {
+    ctor public ArcMotion();
+    ctor public ArcMotion(android.content.Context, android.util.AttributeSet);
+    method public float getMaximumAngle();
+    method public float getMinimumHorizontalAngle();
+    method public float getMinimumVerticalAngle();
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public void setMaximumAngle(float);
+    method public void setMinimumHorizontalAngle(float);
+    method public void setMinimumVerticalAngle(float);
+  }
+
+  public class AutoTransition extends android.support.transition.TransitionSet {
+    ctor public AutoTransition();
+    ctor public AutoTransition(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class ChangeBounds extends android.support.transition.Transition {
+    ctor public ChangeBounds();
+    ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public void setResizeClip(boolean);
+  }
+
+  public class ChangeClipBounds extends android.support.transition.Transition {
+    ctor public ChangeClipBounds();
+    ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeImageTransform extends android.support.transition.Transition {
+    ctor public ChangeImageTransform();
+    ctor public ChangeImageTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeTransform extends android.support.transition.Transition {
+    ctor public ChangeTransform();
+    ctor public ChangeTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getReparent();
+    method public boolean getReparentWithOverlay();
+    method public void setReparent(boolean);
+    method public void setReparentWithOverlay(boolean);
+  }
+
+  public class CircularPropagation extends android.support.transition.VisibilityPropagation {
+    ctor public CircularPropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+  }
+
+  public class Explode extends android.support.transition.Visibility {
+    ctor public Explode();
+    ctor public Explode(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class Fade extends android.support.transition.Visibility {
+    ctor public Fade(int);
+    ctor public Fade();
+    ctor public Fade(android.content.Context, android.util.AttributeSet);
+    field public static final int IN = 1; // 0x1
+    field public static final int OUT = 2; // 0x2
+  }
+
+  public abstract class PathMotion {
+    ctor public PathMotion();
+    ctor public PathMotion(android.content.Context, android.util.AttributeSet);
+    method public abstract android.graphics.Path getPath(float, float, float, float);
+  }
+
+  public class PatternPathMotion extends android.support.transition.PathMotion {
+    ctor public PatternPathMotion();
+    ctor public PatternPathMotion(android.content.Context, android.util.AttributeSet);
+    ctor public PatternPathMotion(android.graphics.Path);
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public android.graphics.Path getPatternPath();
+    method public void setPatternPath(android.graphics.Path);
+  }
+
+  public class Scene {
+    ctor public Scene(android.view.ViewGroup);
+    ctor public Scene(android.view.ViewGroup, android.view.View);
+    method public void enter();
+    method public void exit();
+    method public static android.support.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
+    method public android.view.ViewGroup getSceneRoot();
+    method public void setEnterAction(java.lang.Runnable);
+    method public void setExitAction(java.lang.Runnable);
+  }
+
+  public class SidePropagation extends android.support.transition.VisibilityPropagation {
+    ctor public SidePropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+    method public void setSide(int);
+  }
+
+  public class Slide extends android.support.transition.Visibility {
+    ctor public Slide();
+    ctor public Slide(int);
+    ctor public Slide(android.content.Context, android.util.AttributeSet);
+    method public int getSlideEdge();
+    method public void setSlideEdge(int);
+  }
+
+  public abstract class Transition {
+    ctor public Transition();
+    ctor public Transition(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition addTarget(android.view.View);
+    method public android.support.transition.Transition addTarget(int);
+    method public android.support.transition.Transition addTarget(java.lang.String);
+    method public android.support.transition.Transition addTarget(java.lang.Class);
+    method public abstract void captureEndValues(android.support.transition.TransitionValues);
+    method public abstract void captureStartValues(android.support.transition.TransitionValues);
+    method public android.support.transition.Transition clone();
+    method public android.animation.Animator createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition excludeChildren(android.view.View, boolean);
+    method public android.support.transition.Transition excludeChildren(int, boolean);
+    method public android.support.transition.Transition excludeChildren(java.lang.Class, boolean);
+    method public android.support.transition.Transition excludeTarget(android.view.View, boolean);
+    method public android.support.transition.Transition excludeTarget(int, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
+    method public long getDuration();
+    method public android.graphics.Rect getEpicenter();
+    method public android.support.transition.Transition.EpicenterCallback getEpicenterCallback();
+    method public android.animation.TimeInterpolator getInterpolator();
+    method public java.lang.String getName();
+    method public android.support.transition.PathMotion getPathMotion();
+    method public android.support.transition.TransitionPropagation getPropagation();
+    method public long getStartDelay();
+    method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.String> getTargetNames();
+    method public java.util.List<java.lang.Class> getTargetTypes();
+    method public java.util.List<android.view.View> getTargets();
+    method public java.lang.String[] getTransitionProperties();
+    method public android.support.transition.TransitionValues getTransitionValues(android.view.View, boolean);
+    method public android.support.transition.Transition removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition removeTarget(android.view.View);
+    method public android.support.transition.Transition removeTarget(int);
+    method public android.support.transition.Transition removeTarget(java.lang.String);
+    method public android.support.transition.Transition removeTarget(java.lang.Class);
+    method public android.support.transition.Transition setDuration(long);
+    method public void setEpicenterCallback(android.support.transition.Transition.EpicenterCallback);
+    method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
+    method public void setMatchOrder(int...);
+    method public void setPathMotion(android.support.transition.PathMotion);
+    method public void setPropagation(android.support.transition.TransitionPropagation);
+    method public android.support.transition.Transition setStartDelay(long);
+    field public static final int MATCH_ID = 3; // 0x3
+    field public static final int MATCH_INSTANCE = 1; // 0x1
+    field public static final int MATCH_ITEM_ID = 4; // 0x4
+    field public static final int MATCH_NAME = 2; // 0x2
+  }
+
+  public static abstract class Transition.EpicenterCallback {
+    ctor public Transition.EpicenterCallback();
+    method public abstract android.graphics.Rect onGetEpicenter(android.support.transition.Transition);
+  }
+
+  public static abstract interface Transition.TransitionListener {
+    method public abstract void onTransitionCancel(android.support.transition.Transition);
+    method public abstract void onTransitionEnd(android.support.transition.Transition);
+    method public abstract void onTransitionPause(android.support.transition.Transition);
+    method public abstract void onTransitionResume(android.support.transition.Transition);
+    method public abstract void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionInflater {
+    method public static android.support.transition.TransitionInflater from(android.content.Context);
+    method public android.support.transition.Transition inflateTransition(int);
+    method public android.support.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+  }
+
+  public class TransitionManager {
+    ctor public TransitionManager();
+    method public static void beginDelayedTransition(android.view.ViewGroup);
+    method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
+    method public static void go(android.support.transition.Scene);
+    method public static void go(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Scene, android.support.transition.Transition);
+    method public void transitionTo(android.support.transition.Scene);
+  }
+
+  public abstract class TransitionPropagation {
+    ctor public TransitionPropagation();
+    method public abstract void captureValues(android.support.transition.TransitionValues);
+    method public abstract java.lang.String[] getPropagationProperties();
+    method public abstract long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+  }
+
+  public class TransitionSet extends android.support.transition.Transition {
+    ctor public TransitionSet();
+    ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getOrdering();
+    method public android.support.transition.Transition getTransitionAt(int);
+    method public int getTransitionCount();
+    method public android.support.transition.TransitionSet removeTransition(android.support.transition.Transition);
+    method public android.support.transition.TransitionSet setOrdering(int);
+    field public static final int ORDERING_SEQUENTIAL = 1; // 0x1
+    field public static final int ORDERING_TOGETHER = 0; // 0x0
+  }
+
+  public class TransitionValues {
+    ctor public TransitionValues();
+    field public final java.util.Map<java.lang.String, java.lang.Object> values;
+    field public android.view.View view;
+  }
+
+  public abstract class Visibility extends android.support.transition.Transition {
+    ctor public Visibility();
+    ctor public Visibility(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getMode();
+    method public boolean isVisible(android.support.transition.TransitionValues);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setMode(int);
+    field public static final int MODE_IN = 1; // 0x1
+    field public static final int MODE_OUT = 2; // 0x2
+  }
+
+  public abstract class VisibilityPropagation extends android.support.transition.TransitionPropagation {
+    ctor public VisibilityPropagation();
+    method public void captureValues(android.support.transition.TransitionValues);
+    method public java.lang.String[] getPropagationProperties();
+    method public int getViewVisibility(android.support.transition.TransitionValues);
+    method public int getViewX(android.support.transition.TransitionValues);
+    method public int getViewY(android.support.transition.TransitionValues);
+  }
+
+}
+
+package android.support.v13.app {
+
+  public class ActivityCompat extends android.support.v4.app.ActivityCompat {
+    ctor protected ActivityCompat();
+    method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+  }
+
+  public class FragmentCompat {
+    ctor public FragmentCompat();
+    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
+    method public static deprecated void setMenuVisibility(android.app.Fragment, boolean);
+    method public static void setUserVisibleHint(android.app.Fragment, boolean);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
+  }
+
+  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public void setup(android.content.Context, android.app.FragmentManager);
+    method public void setup(android.content.Context, android.app.FragmentManager, int);
+  }
+
+}
+
+package android.support.v13.view {
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, android.support.v13.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static abstract interface DragStartHelper.OnDragStartListener {
+    method public abstract boolean onDragStart(android.view.View, android.support.v13.view.DragStartHelper);
+  }
+
+  public deprecated class ViewCompat extends android.support.v4.view.ViewCompat {
+  }
+
+}
+
+package android.support.v13.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor public EditorInfoCompat();
+    method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
+  }
+
+  public static abstract interface InputConnectionCompat.OnCommitContentListener {
+    method public abstract boolean onCommitContent(android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public java.lang.Object unwrap();
+    method public static android.support.v13.view.inputmethod.InputContentInfoCompat wrap(java.lang.Object);
+  }
+
+}
+
+package android.support.v14.preference {
+
+  public class EditTextPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public EditTextPreferenceDialogFragment();
+    method public static android.support.v14.preference.EditTextPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public ListPreferenceDialogFragment();
+    method public static android.support.v14.preference.ListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public MultiSelectListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence[] getEntryValues();
+    method protected boolean[] getSelectedItems();
+    method public java.util.Set<java.lang.String> getValues();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValues(java.util.Set<java.lang.String>);
+  }
+
+  public class MultiSelectListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public MultiSelectListPreferenceDialogFragment();
+    method public static android.support.v14.preference.MultiSelectListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragment extends android.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragment();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public class SwitchPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreference(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+}
+
+package android.support.v17.leanback.app {
+
+  public final class BackgroundManager {
+    method public void attach(android.view.Window);
+    method public void attachToView(android.view.View);
+    method public void clearDrawable();
+    method public final int getColor();
+    method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
+    method public deprecated android.graphics.drawable.Drawable getDimLayer();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
+    method public boolean isAttached();
+    method public boolean isAutoReleaseOnStop();
+    method public void release();
+    method public void setAutoReleaseOnStop(boolean);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColor(int);
+    method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setThemeDrawableResourceId(int);
+  }
+
+  public class BaseFragment extends android.support.v17.leanback.app.BrandedFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+   abstract class BaseRowFragment extends android.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+   abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public class BaseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+  public class BrandedFragment extends android.app.Fragment {
+    ctor public BrandedFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrandedSupportFragment extends android.support.v4.app.Fragment {
+    ctor public BrandedSupportFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrowseFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public BrowseFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
+    method public int getHeadersState();
+    method public android.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersFragment onCreateHeadersFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseFragment.BrowseTransitionListener {
+    ctor public BrowseFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseFragment.FragmentFactory<T extends android.app.Fragment> {
+    ctor public BrowseFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseFragment.FragmentFactory {
+    ctor public BrowseFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseFragment.MainFragmentAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseFragment.MainFragmentAdapterRegistry();
+    method public android.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseFragment.FragmentFactory);
+  }
+
+  public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class BrowseSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public BrowseSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public int getHeadersState();
+    method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
+    method public android.support.v4.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersSupportFragment onCreateHeadersSupportFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseSupportFragment.BrowseTransitionListener {
+    ctor public BrowseSupportFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseSupportFragment.FragmentFactory<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseSupportFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseSupportFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory {
+    ctor public BrowseSupportFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsSupportFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseSupportFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseSupportFragment.MainFragmentAdapterRegistry();
+    method public android.support.v4.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class DetailsFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public DetailsFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsFragmentBackgroundController {
+    ctor public DetailsFragmentBackgroundController(android.support.v17.leanback.app.DetailsFragment);
+    method public boolean canNavigateToVideoFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.app.Fragment findOrCreateVideoFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.app.Fragment onCreateVideoFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class DetailsSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public DetailsSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsSupportFragmentBackgroundController {
+    ctor public DetailsSupportFragmentBackgroundController(android.support.v17.leanback.app.DetailsSupportFragment);
+    method public boolean canNavigateToVideoSupportFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class ErrorFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public ErrorFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class ErrorSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public ErrorSupportFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class GuidedStepFragment extends android.app.Fragment {
+    ctor public GuidedStepFragment();
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class GuidedStepSupportFragment extends android.support.v4.app.Fragment {
+    ctor public GuidedStepSupportFragment();
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepSupportFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment {
+    ctor public HeadersFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment {
+    ctor public HeadersSupportFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public abstract class OnboardingFragment extends android.app.Fragment {
+    ctor public OnboardingFragment();
+    method public final int getArrowBackgroundColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract class OnboardingSupportFragment extends android.support.v4.app.Fragment {
+    ctor public OnboardingSupportFragment();
+    method public final int getArrowBackgroundColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
+    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
+    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
+    method protected deprecated void pausePlayback();
+    method public final void play(int);
+    method public final void previous();
+    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method protected deprecated void skipToNext();
+    method protected deprecated void skipToPrevious();
+    method protected deprecated void startPlayback(int);
+  }
+
+  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
+    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  }
+
+  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public class PlaybackFragment extends android.app.Fragment {
+    ctor public PlaybackFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
+    ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlayFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlayFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+    ctor public PlaybackOverlaySupportFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
+    ctor public PlaybackSupportFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public final class ProgressBarManager {
+    ctor public ProgressBarManager();
+    method public void disableProgressBar();
+    method public void enableProgressBar();
+    method public long getInitialDelay();
+    method public void hide();
+    method public void setInitialDelay(long);
+    method public void setProgressBarView(android.view.View);
+    method public void setRootView(android.view.ViewGroup);
+    method public void show();
+  }
+
+  public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment implements android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter {
+    ctor public RowsFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public static class RowsFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter {
+    ctor public RowsFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment implements android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsSupportFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsSupportFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter {
+    ctor public RowsSupportFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public static class RowsSupportFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter {
+    ctor public RowsSupportFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public class SearchFragment extends android.app.Fragment {
+    ctor public SearchFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SearchSupportFragment extends android.support.v4.app.Fragment {
+    ctor public SearchSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchSupportFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class VerticalGridFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public VerticalGridFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public VerticalGridSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
+    ctor public VideoFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
+    ctor public VideoSupportFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.database {
+
+  public abstract class CursorMapper {
+    ctor public CursorMapper();
+    method protected abstract java.lang.Object bind(android.database.Cursor);
+    method protected abstract void bindColumns(android.database.Cursor);
+    method public java.lang.Object convert(android.database.Cursor);
+  }
+
+}
+
+package android.support.v17.leanback.graphics {
+
+  public class BoundsRule {
+    ctor public BoundsRule();
+    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
+    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule top;
+  }
+
+  public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
+    method public int getAbsoluteValue();
+    method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
+    method public void setAbsoluteValue(int);
+    method public void setFraction(float);
+  }
+
+  public final class ColorFilterCache {
+    method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
+    method public android.graphics.ColorFilter getFilterForLevel(float);
+  }
+
+  public final class ColorFilterDimmer {
+    method public void applyFilterToView(android.view.View);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context);
+    method public android.graphics.ColorFilter getColorFilter();
+    method public android.graphics.Paint getPaint();
+    method public void setActiveLevel(float);
+  }
+
+  public final class ColorOverlayDimmer {
+    method public int applyToColor(int);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context);
+    method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean);
+    method public int getAlpha();
+    method public float getAlphaFloat();
+    method public android.graphics.Paint getPaint();
+    method public boolean needsDraw();
+    method public void setActiveLevel(float);
+  }
+
+  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public CompositeDrawable();
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
+    method public int getChildCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void removeChild(int);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+  }
+
+  public static final class CompositeDrawable.ChildDrawable {
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
+    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void recomputeBounds();
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
+  }
+
+  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
+    ctor public FitWidthBitmapDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.Bitmap getBitmap();
+    method public int getOpacity();
+    method public android.graphics.Rect getSource();
+    method public int getVerticalOffset();
+    method public void setAlpha(int);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setSource(android.graphics.Rect);
+    method public void setVerticalOffset(int);
+    field public static final android.util.Property<android.support.v17.leanback.graphics.FitWidthBitmapDrawable, java.lang.Integer> PROPERTY_VERTICAL_OFFSET;
+  }
+
+}
+
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public class MediaPlayerAdapter extends android.support.v17.leanback.media.PlayerAdapter {
+    ctor public MediaPlayerAdapter(android.content.Context);
+    method protected boolean onError(int, int);
+    method protected boolean onInfo(int, int);
+    method protected void onSeekComplete();
+    method public void pause();
+    method public void play();
+    method public void release();
+    method public void reset();
+    method public boolean setDataSource(android.net.Uri);
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public deprecated android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public deprecated void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public void addPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method protected java.util.List<android.support.v17.leanback.media.PlaybackGlue.PlayerCallback> getPlayerCallbacks();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public deprecated boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public void removePlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public deprecated void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public deprecated void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.media.PlaybackGlueHost.PlayerCallback getPlayerCallback();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public void notifyPlaybackRowChanged();
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void showControlsOverlay(boolean);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostDestroy();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public static class PlaybackGlueHost.PlayerCallback {
+    ctor public PlaybackGlueHost.PlayerCallback();
+    method public void onBufferingStateChanged(boolean);
+    method public void onError(int, java.lang.CharSequence);
+    method public void onVideoSizeChanged(int, int);
+  }
+
+  public class PlaybackTransportControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackTransportControlGlue(android.content.Context, T);
+    method public android.graphics.drawable.Drawable getArt();
+    method public final long getBufferedPosition();
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public final long getCurrentPosition();
+    method public final long getDuration();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public final T getPlayerAdapter();
+    method public final android.support.v17.leanback.widget.PlaybackSeekDataProvider getSeekProvider();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public final boolean isPlaying();
+    method public final boolean isPrepared();
+    method public final boolean isSeekEnabled();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onPlayCompleted();
+    method protected void onPlayStateChanged();
+    method protected void onPreparedStateChanged();
+    method public final void play();
+    method public final void seekTo(long);
+    method public void setArt(android.graphics.drawable.Drawable);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public final void setSeekEnabled(boolean);
+    method public final void setSeekProvider(android.support.v17.leanback.widget.PlaybackSeekDataProvider);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class PlayerAdapter {
+    ctor public PlayerAdapter();
+    method public long getBufferedPosition();
+    method public final android.support.v17.leanback.media.PlayerAdapter.Callback getCallback();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void onDetachedFromHost();
+    method public abstract void pause();
+    method public abstract void play();
+    method public void seekTo(long);
+    method public final void setCallback(android.support.v17.leanback.media.PlayerAdapter.Callback);
+    method public void setProgressUpdatingEnabled(boolean);
+  }
+
+  public static class PlayerAdapter.Callback {
+    ctor public PlayerAdapter.Callback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onBufferingStateChanged(android.support.v17.leanback.media.PlayerAdapter, boolean);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onDurationChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onError(android.support.v17.leanback.media.PlayerAdapter, int, java.lang.String);
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onVideoSizeChanged(android.support.v17.leanback.media.PlayerAdapter, int, int);
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.system {
+
+  public class Settings {
+    method public boolean getBoolean(java.lang.String);
+    method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context);
+    method public void setBoolean(java.lang.String, boolean);
+    field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
+  }
+
+}
+
+package android.support.v17.leanback.widget {
+
+  public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public AbstractDetailsDescriptionPresenter();
+    method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getBody();
+    method public android.widget.TextView getSubtitle();
+    method public android.widget.TextView getTitle();
+  }
+
+  public abstract class AbstractMediaItemPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaItemPresenter();
+    ctor public AbstractMediaItemPresenter(int);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter getActionPresenter();
+    method protected int getMediaPlayState(java.lang.Object);
+    method public int getThemeId();
+    method public boolean hasMediaRowSeparator();
+    method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
+    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setBackgroundColor(int);
+    method public void setHasMediaRowSeparator(boolean);
+    method public void setThemeId(int);
+    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
+    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
+  }
+
+  public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaItemPresenter.ViewHolder(android.view.View);
+    method public android.view.ViewGroup getMediaItemActionsContainer();
+    method public android.view.View getMediaItemDetailsView();
+    method public android.widget.TextView getMediaItemDurationView();
+    method public android.widget.TextView getMediaItemNameView();
+    method public android.widget.TextView getMediaItemNumberView();
+    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
+    method public android.view.View getMediaItemPausedView();
+    method public android.view.View getMediaItemPlayingView();
+    method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
+    method public android.view.View getMediaItemRowSeparator();
+    method public android.view.View getSelectorView();
+    method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
+    method public void notifyDetailsChanged();
+    method public void notifyPlayStateChanged();
+    method public void onBindRowActions();
+    method public void setSelectedMediaItemNumberView(int);
+  }
+
+  public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaListHeaderPresenter(android.content.Context, int);
+    ctor public AbstractMediaListHeaderPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected abstract void onBindMediaListHeaderViewHolder(android.support.v17.leanback.widget.AbstractMediaListHeaderPresenter.ViewHolder, java.lang.Object);
+    method public void setBackgroundColor(int);
+  }
+
+  public static class AbstractMediaListHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaListHeaderPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getHeaderView();
+  }
+
+  public class Action {
+    ctor public Action(long);
+    ctor public Action(long, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable);
+    method public final void addKeyCode(int);
+    method public final android.graphics.drawable.Drawable getIcon();
+    method public final long getId();
+    method public final java.lang.CharSequence getLabel1();
+    method public final java.lang.CharSequence getLabel2();
+    method public final void removeKeyCode(int);
+    method public final boolean respondsToKeyCode(int);
+    method public final void setIcon(android.graphics.drawable.Drawable);
+    method public final void setId(long);
+    method public final void setLabel1(java.lang.CharSequence);
+    method public final void setLabel2(java.lang.CharSequence);
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+  }
+
+  public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ArrayObjectAdapter();
+    method public void add(java.lang.Object);
+    method public void add(int, java.lang.Object);
+    method public void addAll(int, java.util.Collection);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public boolean remove(java.lang.Object);
+    method public int removeItems(int, int);
+    method public void replace(int, java.lang.Object);
+    method public int size();
+    method public <E> java.util.List<E> unmodifiableList();
+  }
+
+  public class BaseCardView extends android.widget.FrameLayout {
+    ctor public BaseCardView(android.content.Context);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
+    method public int getCardType();
+    method public deprecated int getExtraVisibility();
+    method public int getInfoVisibility();
+    method public boolean isSelectedAnimationDelayed();
+    method public void setCardType(int);
+    method public deprecated void setExtraVisibility(int);
+    method public void setInfoVisibility(int);
+    method public void setSelectedAnimationDelayed(boolean);
+    field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
+    field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0
+    field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1
+    field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3
+    field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0
+  }
+
+  public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView.LayoutParams(int, int);
+    ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams);
+    field public static final int VIEW_TYPE_EXTRA = 2; // 0x2
+    field public static final int VIEW_TYPE_INFO = 1; // 0x1
+    field public static final int VIEW_TYPE_MAIN = 0; // 0x0
+    field public int viewType;
+  }
+
+  public abstract class BaseGridView extends android.support.v7.widget.RecyclerView {
+    method public void addOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void animateIn();
+    method public void animateOut();
+    method public int getChildDrawingOrder(int, int);
+    method public deprecated int getHorizontalMargin();
+    method public int getHorizontalSpacing();
+    method public int getInitialPrefetchItemCount();
+    method public int getItemAlignmentOffset();
+    method public float getItemAlignmentOffsetPercent();
+    method public int getItemAlignmentViewId();
+    method public android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener getOnUnhandledKeyListener();
+    method public final int getSaveChildrenLimitNumber();
+    method public final int getSaveChildrenPolicy();
+    method public int getSelectedPosition();
+    method public deprecated int getVerticalMargin();
+    method public int getVerticalSpacing();
+    method public void getViewSelectedOffsets(android.view.View, int[]);
+    method public int getWindowAlignment();
+    method public int getWindowAlignmentOffset();
+    method public float getWindowAlignmentOffsetPercent();
+    method public boolean hasPreviousViewInSameRow(int);
+    method public boolean isChildLayoutAnimated();
+    method public boolean isFocusDrawingOrderEnabled();
+    method public final boolean isFocusSearchDisabled();
+    method public boolean isItemAlignmentOffsetWithPadding();
+    method public boolean isScrollEnabled();
+    method public boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+    method public void removeOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setAnimateChildLayout(boolean);
+    method public void setChildrenVisibility(int);
+    method public void setFocusDrawingOrderEnabled(boolean);
+    method public final void setFocusSearchDisabled(boolean);
+    method public void setGravity(int);
+    method public void setHasOverlappingRendering(boolean);
+    method public deprecated void setHorizontalMargin(int);
+    method public void setHorizontalSpacing(int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setItemAlignmentOffset(int);
+    method public void setItemAlignmentOffsetPercent(float);
+    method public void setItemAlignmentOffsetWithPadding(boolean);
+    method public void setItemAlignmentViewId(int);
+    method public deprecated void setItemMargin(int);
+    method public void setItemSpacing(int);
+    method public void setLayoutEnabled(boolean);
+    method public void setOnChildLaidOutListener(android.support.v17.leanback.widget.OnChildLaidOutListener);
+    method public void setOnChildSelectedListener(android.support.v17.leanback.widget.OnChildSelectedListener);
+    method public void setOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setOnKeyInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnKeyInterceptListener);
+    method public void setOnMotionInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnMotionInterceptListener);
+    method public void setOnTouchInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnTouchInterceptListener);
+    method public void setOnUnhandledKeyListener(android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener);
+    method public void setPruneChild(boolean);
+    method public final void setSaveChildrenLimitNumber(int);
+    method public final void setSaveChildrenPolicy(int);
+    method public void setScrollEnabled(boolean);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, int);
+    method public void setSelectedPosition(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public void setSelectedPositionSmooth(int);
+    method public void setSelectedPositionSmooth(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public deprecated void setVerticalMargin(int);
+    method public void setVerticalSpacing(int);
+    method public void setWindowAlignment(int);
+    method public void setWindowAlignmentOffset(int);
+    method public void setWindowAlignmentOffsetPercent(float);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+    field public static final int SAVE_ALL_CHILD = 3; // 0x3
+    field public static final int SAVE_LIMITED_CHILD = 2; // 0x2
+    field public static final int SAVE_NO_CHILD = 0; // 0x0
+    field public static final int SAVE_ON_SCREEN_CHILD = 1; // 0x1
+    field public static final int WINDOW_ALIGN_BOTH_EDGE = 3; // 0x3
+    field public static final int WINDOW_ALIGN_HIGH_EDGE = 2; // 0x2
+    field public static final int WINDOW_ALIGN_LOW_EDGE = 1; // 0x1
+    field public static final int WINDOW_ALIGN_NO_EDGE = 0; // 0x0
+    field public static final float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static abstract interface BaseGridView.OnKeyInterceptListener {
+    method public abstract boolean onInterceptKeyEvent(android.view.KeyEvent);
+  }
+
+  public static abstract interface BaseGridView.OnMotionInterceptListener {
+    method public abstract boolean onInterceptMotionEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnTouchInterceptListener {
+    method public abstract boolean onInterceptTouchEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnUnhandledKeyListener {
+    method public abstract boolean onUnhandledKey(android.view.KeyEvent);
+  }
+
+  public abstract interface BaseOnItemViewClickedListener<T> {
+    method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public abstract interface BaseOnItemViewSelectedListener<T> {
+    method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public class BrowseFrameLayout extends android.widget.FrameLayout {
+    ctor public BrowseFrameLayout(android.content.Context);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
+    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
+    method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnChildFocusListener {
+    method public abstract void onRequestChildFocus(android.view.View, android.view.View);
+    method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnFocusSearchListener {
+    method public abstract android.view.View onFocusSearch(android.view.View, int);
+  }
+
+  public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ClassPresenterSelector();
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenterSelector(java.lang.Class<?>, android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ControlButtonPresenterSelector();
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter();
+    method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter();
+  }
+
+  public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public CursorObjectAdapter();
+    method public void changeCursor(android.database.Cursor);
+    method public void close();
+    method public java.lang.Object get(int);
+    method public final android.database.Cursor getCursor();
+    method public final android.support.v17.leanback.database.CursorMapper getMapper();
+    method protected final void invalidateCache(int);
+    method protected final void invalidateCache(int, int);
+    method public boolean isClosed();
+    method protected void onCursorChanged();
+    method protected void onMapperChanged();
+    method public final void setMapper(android.support.v17.leanback.database.CursorMapper);
+    method public int size();
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+  }
+
+  public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DetailsOverviewLogoPresenter();
+    method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+  }
+
+  public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter getParentPresenter();
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder();
+    method public boolean isSizeFromDrawableIntrinsic();
+    method public void setSizeFromDrawableIntrinsic(boolean);
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter;
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
+  }
+
+  public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row {
+    ctor public DetailsOverviewRow(java.lang.Object);
+    method public final deprecated void addAction(android.support.v17.leanback.widget.Action);
+    method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public boolean isImageScaleUpAllowed();
+    method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action);
+    method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setImageScaleUpAllowed(boolean);
+    method public final void setItem(java.lang.Object);
+  }
+
+  public static class DetailsOverviewRow.Listener {
+    ctor public DetailsOverviewRow.Listener();
+    method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+  }
+
+  public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public boolean isStyleLarge();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setStyleLarge(boolean);
+  }
+
+  public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder;
+  }
+
+  public class DetailsParallax extends android.support.v17.leanback.widget.RecyclerViewParallax {
+    ctor public DetailsParallax();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowBottom();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowTop();
+  }
+
+  public class DividerPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DividerPresenter();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public class DividerRow extends android.support.v17.leanback.widget.Row {
+    ctor public DividerRow();
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract interface FacetProvider {
+    method public abstract java.lang.Object getFacet(java.lang.Class<?>);
+  }
+
+  public abstract interface FacetProviderAdapter {
+    method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+  }
+
+  public abstract interface FocusHighlight {
+    field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3
+    field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2
+    field public static final int ZOOM_FACTOR_NONE = 0; // 0x0
+    field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1
+    field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4
+  }
+
+  public class FocusHighlightHelper {
+    ctor public FocusHighlightHelper();
+    method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView, boolean);
+  }
+
+  public abstract interface FragmentAnimationProvider {
+    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public final int getActionsBackgroundColor();
+    method public final int getAlignmentMode();
+    method public final int getBackgroundColor();
+    method public final int getInitialState();
+    method protected int getLayoutResourceId();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public final boolean isParticipatingEntranceTransition();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+    method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    method public final void setActionsBackgroundColor(int);
+    method public final void setAlignmentMode(int);
+    method public final void setBackgroundColor(int);
+    method public final void setInitialState(int);
+    method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setParticipatingEntranceTransition(boolean);
+    method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1
+    field public static final int ALIGN_MODE_START = 0; // 0x0
+    field public static final int STATE_FULL = 1; // 0x1
+    field public static final int STATE_HALF = 0; // 0x0
+    field public static final int STATE_SMALL = 2; // 0x2
+    field protected int mInitialState;
+  }
+
+  public static abstract class FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.Listener();
+    method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener();
+    method public final android.view.ViewGroup getActionsRow();
+    method public final android.view.ViewGroup getDetailsDescriptionFrame();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder();
+    method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder();
+    method public final android.view.ViewGroup getOverviewView();
+    method public final int getState();
+    field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener;
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener();
+  }
+
+  public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewSharedElementHelper();
+    method public boolean getAutoStartSharedElementTransition();
+    method public void setAutoStartSharedElementTransition(boolean);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public void startPostponedEnterTransition();
+  }
+
+  public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidanceStylist();
+    method public android.widget.TextView getBreadcrumbView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance);
+    method public void onDestroyView();
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideLayoutId();
+  }
+
+  public static class GuidanceStylist.Guidance {
+    ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable);
+    method public java.lang.String getBreadcrumb();
+    method public java.lang.String getDescription();
+    method public android.graphics.drawable.Drawable getIconDrawable();
+    method public java.lang.String getTitle();
+  }
+
+  public class GuidedAction extends android.support.v17.leanback.widget.Action {
+    ctor protected GuidedAction();
+    method public int getCheckSetId();
+    method public java.lang.CharSequence getDescription();
+    method public int getDescriptionEditInputType();
+    method public int getDescriptionInputType();
+    method public java.lang.CharSequence getEditDescription();
+    method public int getEditInputType();
+    method public java.lang.CharSequence getEditTitle();
+    method public int getInputType();
+    method public android.content.Intent getIntent();
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getSubActions();
+    method public java.lang.CharSequence getTitle();
+    method public boolean hasEditableActivatorView();
+    method public boolean hasMultilineDescription();
+    method public boolean hasNext();
+    method public boolean hasSubActions();
+    method public boolean hasTextEditable();
+    method public boolean infoOnly();
+    method public final boolean isAutoSaveRestoreEnabled();
+    method public boolean isChecked();
+    method public boolean isDescriptionEditable();
+    method public boolean isEditTitleUsed();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public void onRestoreInstanceState(android.os.Bundle, java.lang.String);
+    method public void onSaveInstanceState(android.os.Bundle, java.lang.String);
+    method public void setChecked(boolean);
+    method public void setDescription(java.lang.CharSequence);
+    method public void setEditDescription(java.lang.CharSequence);
+    method public void setEditTitle(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setSubActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setTitle(java.lang.CharSequence);
+    field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
+    field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
+    field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
+    field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL
+    field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL
+    field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
+    field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
+    field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
+    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
+    field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
+    field public static final int NO_CHECK_SET = 0; // 0x0
+  }
+
+  public static class GuidedAction.Builder extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public deprecated GuidedAction.Builder();
+    ctor public GuidedAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedAction build();
+  }
+
+  public static abstract class GuidedAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedAction.BuilderBase> {
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction);
+    method public B autoSaveRestoreEnabled(boolean);
+    method public B checkSetId(int);
+    method public B checked(boolean);
+    method public B clickAction(long);
+    method public B description(java.lang.CharSequence);
+    method public B description(int);
+    method public B descriptionEditInputType(int);
+    method public B descriptionEditable(boolean);
+    method public B descriptionInputType(int);
+    method public B editDescription(java.lang.CharSequence);
+    method public B editDescription(int);
+    method public B editInputType(int);
+    method public B editTitle(java.lang.CharSequence);
+    method public B editTitle(int);
+    method public B editable(boolean);
+    method public B enabled(boolean);
+    method public B focusable(boolean);
+    method public android.content.Context getContext();
+    method public B hasEditableActivatorView(boolean);
+    method public B hasNext(boolean);
+    method public B icon(android.graphics.drawable.Drawable);
+    method public B icon(int);
+    method public deprecated B iconResourceId(int, android.content.Context);
+    method public B id(long);
+    method public B infoOnly(boolean);
+    method public B inputType(int);
+    method public B intent(android.content.Intent);
+    method public B multilineDescription(boolean);
+    method public B subActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public B title(java.lang.CharSequence);
+    method public B title(int);
+  }
+
+  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
+    ctor public GuidedActionEditText(android.content.Context);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidedActionsStylist();
+    method public void collapseAction(boolean);
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
+    method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
+    method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
+    method public final boolean isBackKeyToCollapseActivatorView();
+    method public final boolean isBackKeyToCollapseSubActions();
+    method public boolean isButtonActions();
+    method public boolean isExpandTransitionSupported();
+    method public boolean isExpanded();
+    method public boolean isInExpandTransition();
+    method public boolean isSubActionsExpanded();
+    method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void onBindActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDestroyView();
+    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideItemLayoutId();
+    method public int onProvideItemLayoutId(int);
+    method public int onProvideLayoutId();
+    method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void setAsButtonActions();
+    method public final void setBackKeyToCollapseActivatorView(boolean);
+    method public final void setBackKeyToCollapseSubActions(boolean);
+    method public deprecated void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
+    field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
+  }
+
+  public static class GuidedActionsStylist.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View);
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View, boolean);
+    method public android.support.v17.leanback.widget.GuidedAction getAction();
+    method public android.widget.ImageView getCheckmarkView();
+    method public android.widget.ImageView getChevronView();
+    method public android.view.View getContentView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.EditText getEditableDescriptionView();
+    method public android.widget.EditText getEditableTitleView();
+    method public android.view.View getEditingView();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public boolean isInEditing();
+    method public boolean isInEditingActivatorView();
+    method public boolean isInEditingDescription();
+    method public boolean isInEditingText();
+    method public boolean isInEditingTitle();
+    method public boolean isSubAction();
+  }
+
+  public class GuidedDatePickerAction extends android.support.v17.leanback.widget.GuidedAction {
+    ctor public GuidedDatePickerAction();
+    method public long getDate();
+    method public java.lang.String getDatePickerFormat();
+    method public long getMaxDate();
+    method public long getMinDate();
+    method public void setDate(long);
+  }
+
+  public static final class GuidedDatePickerAction.Builder extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase {
+    ctor public GuidedDatePickerAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedDatePickerAction build();
+  }
+
+  public static abstract class GuidedDatePickerAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase> extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public GuidedDatePickerAction.BuilderBase(android.content.Context);
+    method protected final void applyDatePickerValues(android.support.v17.leanback.widget.GuidedDatePickerAction);
+    method public B date(long);
+    method public B datePickerFormat(java.lang.String);
+    method public B maxDate(long);
+    method public B minDate(long);
+  }
+
+  public class HeaderItem {
+    ctor public HeaderItem(long, java.lang.String);
+    ctor public HeaderItem(java.lang.String);
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getDescription();
+    method public final long getId();
+    method public final java.lang.String getName();
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setDescription(java.lang.CharSequence);
+  }
+
+  public class HorizontalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public HorizontalGridView(android.content.Context);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int);
+    method public final boolean getFadingLeftEdge();
+    method public final int getFadingLeftEdgeLength();
+    method public final int getFadingLeftEdgeOffset();
+    method public final boolean getFadingRightEdge();
+    method public final int getFadingRightEdgeLength();
+    method public final int getFadingRightEdgeOffset();
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public final void setFadingLeftEdge(boolean);
+    method public final void setFadingLeftEdgeLength(int);
+    method public final void setFadingLeftEdgeOffset(int);
+    method public final void setFadingRightEdge(boolean);
+    method public final void setFadingRightEdgeLength(int);
+    method public final void setFadingRightEdgeOffset(int);
+    method public void setNumRows(int);
+    method public void setRowHeight(int);
+  }
+
+  public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher {
+    ctor public HorizontalHoverCardSwitcher();
+    method protected void insertView(android.view.View);
+    method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object);
+  }
+
+  public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
+    ctor public deprecated ImageCardView(android.content.Context, int);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
+    ctor public ImageCardView(android.content.Context);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
+    method public android.graphics.drawable.Drawable getBadgeImage();
+    method public java.lang.CharSequence getContentText();
+    method public android.graphics.drawable.Drawable getInfoAreaBackground();
+    method public android.graphics.drawable.Drawable getMainImage();
+    method public final android.widget.ImageView getMainImageView();
+    method public java.lang.CharSequence getTitleText();
+    method public void setBadgeImage(android.graphics.drawable.Drawable);
+    method public void setContentText(java.lang.CharSequence);
+    method public void setInfoAreaBackground(android.graphics.drawable.Drawable);
+    method public void setInfoAreaBackgroundColor(int);
+    method public void setMainImage(android.graphics.drawable.Drawable);
+    method public void setMainImage(android.graphics.drawable.Drawable, boolean);
+    method public void setMainImageAdjustViewBounds(boolean);
+    method public void setMainImageDimensions(int, int);
+    method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
+    method public void setTitleText(java.lang.CharSequence);
+    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
+    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
+    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
+    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
+    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
+  }
+
+  public abstract interface ImeKeyMonitor {
+    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public static abstract interface ImeKeyMonitor.ImeKeyListener {
+    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
+  }
+
+  public final class ItemAlignmentFacet {
+    ctor public ItemAlignmentFacet();
+    method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs();
+    method public boolean isMultiAlignment();
+    method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static class ItemAlignmentFacet.ItemAlignmentDef {
+    ctor public ItemAlignmentFacet.ItemAlignmentDef();
+    method public final int getItemAlignmentFocusViewId();
+    method public final int getItemAlignmentOffset();
+    method public final float getItemAlignmentOffsetPercent();
+    method public final int getItemAlignmentViewId();
+    method public boolean isAlignedToTextViewBaseLine();
+    method public final boolean isItemAlignmentOffsetWithPadding();
+    method public final void setAlignedToTextViewBaseline(boolean);
+    method public final void setItemAlignmentFocusViewId(int);
+    method public final void setItemAlignmentOffset(int);
+    method public final void setItemAlignmentOffsetPercent(float);
+    method public final void setItemAlignmentOffsetWithPadding(boolean);
+    method public final void setItemAlignmentViewId(int);
+  }
+
+  public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter {
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ItemBridgeAdapter();
+    method public void clear();
+    method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+    method public int getItemCount();
+    method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper();
+    method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper();
+    method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener);
+    method public void setPresenter(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>);
+    method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper);
+  }
+
+  public static class ItemBridgeAdapter.AdapterListener {
+    ctor public ItemBridgeAdapter.AdapterListener();
+    method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+  }
+
+  public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    method public final java.lang.Object getExtraObject();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.Presenter getPresenter();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder();
+    method public void setExtraObject(java.lang.Object);
+  }
+
+  public static abstract class ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapter.Wrapper();
+    method public abstract android.view.View createWrapper(android.view.View);
+    method public abstract void wrap(android.view.View, android.view.View);
+  }
+
+  public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper);
+    method public android.view.View createWrapper(android.view.View);
+    method public void wrap(android.view.View, android.view.View);
+  }
+
+  public class ListRow extends android.support.v17.leanback.widget.Row {
+    ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public java.lang.CharSequence getContentDescription();
+    method public void setContentDescription(java.lang.CharSequence);
+  }
+
+  public final class ListRowHoverCardView extends android.widget.LinearLayout {
+    ctor public ListRowHoverCardView(android.content.Context);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int);
+    method public final java.lang.CharSequence getDescription();
+    method public final java.lang.CharSequence getTitle();
+    method public final void setDescription(java.lang.CharSequence);
+    method public final void setTitle(java.lang.CharSequence);
+  }
+
+  public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public ListRowPresenter();
+    ctor public ListRowPresenter(int);
+    ctor public ListRowPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public int getExpandedRowHeight();
+    method public final int getFocusZoomFactor();
+    method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector();
+    method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter);
+    method public int getRowHeight();
+    method public final boolean getShadowEnabled();
+    method public final deprecated int getZoomFactor();
+    method public final boolean isFocusDimmerUsed();
+    method public final boolean isKeepChildForeground();
+    method public boolean isUsingDefaultListSelectEffect();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void setExpandedRowHeight(int);
+    method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumRows(int);
+    method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int);
+    method public void setRowHeight(int);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class ListRowPresenter.SelectItemViewHolderTask extends android.support.v17.leanback.widget.Presenter.ViewHolderTask {
+    ctor public ListRowPresenter.SelectItemViewHolderTask(int);
+    method public int getItemPosition();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolderTask getItemTask();
+    method public boolean isSmoothScroll();
+    method public void setItemPosition(int);
+    method public void setItemTask(android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSmoothScroll(boolean);
+  }
+
+  public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter);
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.HorizontalGridView getGridView();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getItemViewHolder(int);
+    method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter();
+    method public int getSelectedPosition();
+  }
+
+  public final class ListRowView extends android.widget.LinearLayout {
+    ctor public ListRowView(android.content.Context);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
+  }
+
+  public abstract interface MultiActionsProvider {
+    method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
+  }
+
+  public static class MultiActionsProvider.MultiAction {
+    ctor public MultiActionsProvider.MultiAction(long);
+    method public android.graphics.drawable.Drawable getCurrentDrawable();
+    method public android.graphics.drawable.Drawable[] getDrawables();
+    method public long getId();
+    method public int getIndex();
+    method public void incrementIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+  }
+
+  public abstract class ObjectAdapter {
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ObjectAdapter();
+    method public abstract java.lang.Object get(int);
+    method public long getId(int);
+    method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public final boolean hasStableIds();
+    method public boolean isImmediateNotifySupported();
+    method protected final void notifyChanged();
+    method public final void notifyItemRangeChanged(int, int);
+    method protected final void notifyItemRangeInserted(int, int);
+    method protected final void notifyItemRangeRemoved(int, int);
+    method protected void onHasStableIdsChanged();
+    method protected void onPresenterSelectorChanged();
+    method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    method public final void setHasStableIds(boolean);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public abstract int size();
+    method public final void unregisterAllObservers();
+    method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    field public static final int NO_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class ObjectAdapter.DataObserver {
+    ctor public ObjectAdapter.DataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public abstract interface OnActionClickedListener {
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+  }
+
+  public abstract interface OnChildLaidOutListener {
+    method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract deprecated interface OnChildSelectedListener {
+    method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract class OnChildViewHolderSelectedListener {
+    ctor public OnChildViewHolderSelectedListener();
+    method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+  }
+
+  public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
+  }
+
+  public abstract interface OnItemViewSelectedListener implements android.support.v17.leanback.widget.BaseOnItemViewSelectedListener {
+  }
+
+  public class PageRow extends android.support.v17.leanback.widget.Row {
+    ctor public PageRow(android.support.v17.leanback.widget.HeaderItem);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract class Parallax<PropertyT extends android.util.Property> {
+    ctor public Parallax();
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
+    method public abstract PropertyT createProperty(java.lang.String, int);
+    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
+    method public final java.util.List<PropertyT> getProperties();
+    method public void removeAllEffects();
+    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public void updateValues();
+  }
+
+  public static class Parallax.FloatProperty extends android.util.Property {
+    ctor public Parallax.FloatProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
+    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
+    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
+  }
+
+  public static class Parallax.IntProperty extends android.util.Property {
+    ctor public Parallax.IntProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
+    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
+    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
+  }
+
+  public static class Parallax.PropertyMarkerValue<PropertyT> {
+    ctor public Parallax.PropertyMarkerValue(PropertyT);
+    method public PropertyT getProperty();
+  }
+
+  public abstract class ParallaxEffect {
+    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
+    method public final void performMapping(android.support.v17.leanback.widget.Parallax);
+    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
+  }
+
+  public abstract class ParallaxTarget {
+    ctor public ParallaxTarget();
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
+  }
+
+  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
+  }
+
+  public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
+    ctor public PlaybackControlsRow(java.lang.Object);
+    ctor public PlaybackControlsRow();
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
+    method public long getBufferedPosition();
+    method public deprecated int getBufferedProgress();
+    method public deprecated long getBufferedProgressLong();
+    method public long getCurrentPosition();
+    method public deprecated int getCurrentTime();
+    method public deprecated long getCurrentTimeLong();
+    method public long getDuration();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
+    method public deprecated int getTotalTime();
+    method public deprecated long getTotalTimeLong();
+    method public void setBufferedPosition(long);
+    method public deprecated void setBufferedProgress(int);
+    method public deprecated void setBufferedProgressLong(long);
+    method public void setCurrentPosition(long);
+    method public deprecated void setCurrentTime(int);
+    method public deprecated void setCurrentTimeLong(long);
+    method public void setDuration(long);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setOnPlaybackProgressChangedListener(android.support.v17.leanback.widget.PlaybackControlsRow.OnPlaybackProgressCallback);
+    method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public deprecated void setTotalTime(int);
+    method public deprecated void setTotalTimeLong(long);
+  }
+
+  public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context);
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int);
+    field public static int OFF;
+    field public static int ON;
+  }
+
+  public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context);
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context);
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int);
+    field public static int OFF;
+    field public static int ON;
+  }
+
+  public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MoreActions(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MultiAction(int);
+    method public int getActionCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getIndex();
+    method public java.lang.String getLabel(int);
+    method public java.lang.String getSecondaryLabel(int);
+    method public void nextIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+    method public void setLabels(java.lang.String[]);
+    method public void setSecondaryLabels(java.lang.String[]);
+  }
+
+  public static class PlaybackControlsRow.OnPlaybackProgressCallback {
+    ctor public PlaybackControlsRow.OnPlaybackProgressCallback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onDurationChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+  }
+
+  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
+    field public static int PAUSE;
+    field public static int PLAY;
+  }
+
+  public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int);
+    field public static int ALL;
+    field public static int NONE;
+    field public static int ONE;
+  }
+
+  public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context);
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context);
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int);
+    field public static int OFF;
+    field public static int ON;
+  }
+
+  public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipNextAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int);
+    field public static int OUTLINE;
+    field public static int SOLID;
+  }
+
+  public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
+  }
+
+  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public PlaybackControlsRowPresenter();
+    method public boolean areSecondaryActionsHidden();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+    method public void setSecondaryActionsHidden(boolean);
+    method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean);
+    method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
+  }
+
+  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
+  }
+
+  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public PlaybackRowPresenter();
+    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+  }
+
+  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
+  }
+
+  public class PlaybackSeekDataProvider {
+    ctor public PlaybackSeekDataProvider();
+    method public long[] getSeekPositions();
+    method public void getThumbnail(int, android.support.v17.leanback.widget.PlaybackSeekDataProvider.ResultCallback);
+    method public void reset();
+  }
+
+  public static class PlaybackSeekDataProvider.ResultCallback {
+    ctor public PlaybackSeekDataProvider.ResultCallback();
+    method public void onThumbnailLoaded(android.graphics.Bitmap, int);
+  }
+
+  public abstract interface PlaybackSeekUi {
+    method public abstract void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public static class PlaybackSeekUi.Client {
+    ctor public PlaybackSeekUi.Client();
+    method public android.support.v17.leanback.widget.PlaybackSeekDataProvider getPlaybackSeekDataProvider();
+    method public boolean isSeekEnabled();
+    method public void onSeekFinished(boolean);
+    method public void onSeekPositionChanged(long);
+    method public void onSeekStarted();
+  }
+
+  public class PlaybackTransportRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackTransportRowPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public float getDefaultSeekIncrement();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method protected void onProgressBarClicked(android.support.v17.leanback.widget.PlaybackTransportRowPresenter.ViewHolder);
+    method public void setDefaultSeekIncrement(float);
+    method public void setDescriptionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+  }
+
+  public class PlaybackTransportRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackTransportRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    method public final android.widget.TextView getCurrentPositionView();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDescriptionViewHolder();
+    method public final android.widget.TextView getDurationView();
+    method protected void onSetCurrentPositionLabel(long);
+    method protected void onSetDurationLabel(long);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter();
+    method protected static void cancelAnimationsRecursive(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener);
+  }
+
+  public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter.ViewHolder(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    field public final android.view.View view;
+  }
+
+  public static abstract class Presenter.ViewHolderTask {
+    ctor public Presenter.ViewHolderTask();
+    method public void run(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public abstract class PresenterSelector {
+    ctor public PresenterSelector();
+    method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter[] getPresenters();
+  }
+
+  public abstract class PresenterSwitcher {
+    ctor public PresenterSwitcher();
+    method public void clear();
+    method public final android.view.ViewGroup getParentViewGroup();
+    method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector);
+    method protected abstract void insertView(android.view.View);
+    method protected void onViewSelected(android.view.View);
+    method public void select(java.lang.Object);
+    method protected void showView(android.view.View, boolean);
+    method public void unselect();
+  }
+
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
+    ctor public RecyclerViewParallax();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
+    method public float getMaxValue();
+    method public android.support.v7.widget.RecyclerView getRecyclerView();
+    method public void setRecyclerView(android.support.v7.widget.RecyclerView);
+  }
+
+  public static final class RecyclerViewParallax.ChildPositionProperty extends android.support.v17.leanback.widget.Parallax.IntProperty {
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty adapterPosition(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty fraction(float);
+    method public int getAdapterPosition();
+    method public float getFraction();
+    method public int getOffset();
+    method public int getViewId();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty offset(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty viewId(int);
+  }
+
+  public class Row {
+    ctor public Row(long, android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row(android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row();
+    method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem();
+    method public final long getId();
+    method public boolean isRenderedAsRowView();
+    method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem);
+    method public final void setId(long);
+  }
+
+  public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowHeaderPresenter();
+    method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint);
+    method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public boolean isNullItemVisibilityGone();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setNullItemVisibilityGone(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float);
+  }
+
+  public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowHeaderPresenter.ViewHolder(android.view.View);
+    method public final float getSelectLevel();
+  }
+
+  public final class RowHeaderView extends android.widget.TextView {
+    ctor public RowHeaderView(android.content.Context);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowPresenter();
+    method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter();
+    method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final boolean getSelectEffectEnabled();
+    method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final int getSyncActivatePolicy();
+    method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected boolean isClippingChildren();
+    method public boolean isUsingDefaultSelectEffect();
+    method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter);
+    method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setSelectEffectEnabled(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float);
+    method public final void setSyncActivatePolicy(int);
+    field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3
+    field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2
+  }
+
+  public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowPresenter.ViewHolder(android.view.View);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.view.View.OnKeyListener getOnKeyListener();
+    method public final android.support.v17.leanback.widget.Row getRow();
+    method public final java.lang.Object getRowObject();
+    method public final float getSelectLevel();
+    method public java.lang.Object getSelectedItem();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
+    method public final boolean isExpanded();
+    method public final boolean isSelected();
+    method public final void setActivated(boolean);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setOnKeyListener(android.view.View.OnKeyListener);
+    method public final void syncActivatedStatus(android.view.View);
+    field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer;
+  }
+
+  public class SearchBar extends android.widget.RelativeLayout {
+    ctor public SearchBar(android.content.Context);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet, int);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.String getTitle();
+    method public boolean isRecognizing();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
+    method public void setSearchQuery(java.lang.String);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setSpeechRecognizer(android.speech.SpeechRecognizer);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+    method public void stopRecognition();
+  }
+
+  public static abstract interface SearchBar.SearchBarListener {
+    method public abstract void onKeyboardDismiss(java.lang.String);
+    method public abstract void onSearchQueryChange(java.lang.String);
+    method public abstract void onSearchQuerySubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchBar.SearchBarPermissionListener {
+    method public abstract void requestAudioPermission();
+  }
+
+  public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView {
+    ctor public SearchEditText(android.content.Context);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener);
+  }
+
+  public static abstract interface SearchEditText.OnKeyboardDismissListener {
+    method public abstract void onKeyboardDismiss();
+  }
+
+  public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableOrbColorAnimation(boolean);
+    method public int getOrbColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors();
+    method public android.graphics.drawable.Drawable getOrbIcon();
+    method public void onClick(android.view.View);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener);
+    method public void setOrbColor(int);
+    method public deprecated void setOrbColor(int, int);
+    method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
+  }
+
+  public static class SearchOrbView.Colors {
+    ctor public SearchOrbView.Colors(int);
+    ctor public SearchOrbView.Colors(int, int);
+    ctor public SearchOrbView.Colors(int, int, int);
+    method public static int getBrightColor(int);
+    field public int brightColor;
+    field public int color;
+    field public int iconColor;
+  }
+
+  public class SectionRow extends android.support.v17.leanback.widget.Row {
+    ctor public SectionRow(android.support.v17.leanback.widget.HeaderItem);
+    ctor public SectionRow(long, java.lang.String);
+    ctor public SectionRow(java.lang.String);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public class ShadowOverlayContainer extends android.widget.FrameLayout {
+    ctor public ShadowOverlayContainer(android.content.Context);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
+    method public int getShadowType();
+    method public android.view.View getWrappedView();
+    method public deprecated void initialize(boolean, boolean);
+    method public deprecated void initialize(boolean, boolean, boolean);
+    method public static void prepareParentForShadow(android.view.ViewGroup);
+    method public void setOverlayColor(int);
+    method public void setShadowFocusLevel(float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsShadow();
+    method public void useDynamicShadow();
+    method public void useDynamicShadow(float, float);
+    method public void useStaticShadow();
+    method public void wrap(android.view.View);
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public final class ShadowOverlayHelper {
+    method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context);
+    method public int getShadowType();
+    method public boolean needsOverlay();
+    method public boolean needsRoundedCorner();
+    method public boolean needsWrapper();
+    method public void onViewCreated(android.view.View);
+    method public void prepareParentForShadow(android.view.ViewGroup);
+    method public static void setNoneWrapperOverlayColor(android.view.View, int);
+    method public static void setNoneWrapperShadowFocusLevel(android.view.View, float);
+    method public void setOverlayColor(android.view.View, int);
+    method public void setShadowFocusLevel(android.view.View, float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsForeground();
+    method public static boolean supportsRoundedCorner();
+    method public static boolean supportsShadow();
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public static final class ShadowOverlayHelper.Builder {
+    ctor public ShadowOverlayHelper.Builder();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean);
+  }
+
+  public static final class ShadowOverlayHelper.Options {
+    ctor public ShadowOverlayHelper.Options();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float);
+    method public final float getDynamicShadowFocusedZ();
+    method public final float getDynamicShadowUnfocusedZ();
+    method public final int getRoundedCornerRadius();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int);
+    field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT;
+  }
+
+  public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public SparseArrayObjectAdapter();
+    method public void clear(int);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public int indexOf(int);
+    method public java.lang.Object lookup(int);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public void set(int, java.lang.Object);
+    method public int size();
+  }
+
+  public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView {
+    ctor public SpeechOrbView(android.content.Context);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSoundLevel(int);
+    method public void showListening();
+    method public void showNotListening();
+  }
+
+  public abstract interface SpeechRecognitionCallback {
+    method public abstract void recognizeSpeech();
+  }
+
+   class StreamingTextView extends android.widget.EditText {
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet);
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int);
+    method public static boolean isLayoutRtl(android.view.View);
+    method public void reset();
+    method public void setFinalRecognizedText(java.lang.CharSequence);
+    method public void updateRecognizedText(java.lang.String, java.lang.String);
+    method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>);
+  }
+
+  public class TitleHelper {
+    ctor public TitleHelper(android.view.ViewGroup, android.view.View);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public android.view.ViewGroup getSceneRoot();
+    method public android.view.View getTitleView();
+    method public void showTitle(boolean);
+  }
+
+  public class TitleView extends android.widget.FrameLayout implements android.support.v17.leanback.widget.TitleViewAdapter.Provider {
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableAnimation(boolean);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+  }
+
+  public abstract class TitleViewAdapter {
+    ctor public TitleViewAdapter();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public abstract android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public void setAnimationEnabled(boolean);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+    field public static final int BRANDING_VIEW_VISIBLE = 2; // 0x2
+    field public static final int FULL_VIEW_VISIBLE = 6; // 0x6
+    field public static final int SEARCH_VIEW_VISIBLE = 4; // 0x4
+  }
+
+  public static abstract interface TitleViewAdapter.Provider {
+    method public abstract android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+  }
+
+  public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public VerticalGridPresenter();
+    ctor public VerticalGridPresenter(int);
+    ctor public VerticalGridPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public final int getFocusZoomFactor();
+    method public final boolean getKeepChildForeground();
+    method public int getNumberOfColumns();
+    method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public final boolean getShadowEnabled();
+    method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder);
+    method public final boolean isFocusDimmerUsed();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumberOfColumns(int);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView);
+    method public android.support.v17.leanback.widget.VerticalGridView getGridView();
+  }
+
+  public class VerticalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public VerticalGridView(android.content.Context);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int);
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public void setColumnWidth(int);
+    method public void setNumColumns(int);
+  }
+
+  public abstract interface ViewHolderTask {
+    method public abstract void run(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+}
+
+package android.support.v17.leanback.widget.picker {
+
+  public class Picker extends android.widget.FrameLayout {
+    ctor public Picker(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public float getActivatedVisibleItemCount();
+    method public android.support.v17.leanback.widget.picker.PickerColumn getColumnAt(int);
+    method public int getColumnsCount();
+    method protected int getPickerItemHeightPixels();
+    method public final int getPickerItemLayoutId();
+    method public final int getPickerItemTextViewId();
+    method public int getSelectedColumn();
+    method public final java.lang.CharSequence getSeparator();
+    method public float getVisibleItemCount();
+    method public void onColumnValueChanged(int, int);
+    method public void removeOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public void setActivatedVisibleItemCount(float);
+    method public void setColumnAt(int, android.support.v17.leanback.widget.picker.PickerColumn);
+    method public void setColumnValue(int, int, boolean);
+    method public void setColumns(java.util.List<android.support.v17.leanback.widget.picker.PickerColumn>);
+    method public final void setPickerItemTextViewId(int);
+    method public void setSelectedColumn(int);
+    method public final void setSeparator(java.lang.CharSequence);
+    method public void setVisibleItemCount(float);
+  }
+
+  public static abstract interface Picker.PickerValueListener {
+    method public abstract void onValueChanged(android.support.v17.leanback.widget.picker.Picker, int);
+  }
+
+  public class PickerColumn {
+    ctor public PickerColumn();
+    method public int getCount();
+    method public int getCurrentValue();
+    method public java.lang.CharSequence getLabelFor(int);
+    method public java.lang.String getLabelFormat();
+    method public int getMaxValue();
+    method public int getMinValue();
+    method public java.lang.CharSequence[] getStaticLabels();
+    method public void setCurrentValue(int);
+    method public void setLabelFormat(java.lang.String);
+    method public void setMaxValue(int);
+    method public void setMinValue(int);
+    method public void setStaticLabels(java.lang.CharSequence[]);
+  }
+
+  public class TimePicker extends android.support.v17.leanback.widget.picker.Picker {
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet);
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24Hour();
+    method public boolean isPm();
+    method public void setHour(int);
+    method public void setIs24Hour(boolean);
+    method public void setMinute(int);
+  }
+
+}
+
+package android.support.v17.preference {
+
+  public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment {
+    ctor public BaseLeanbackPreferenceFragment();
+  }
+
+  public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment {
+    ctor public LeanbackListPreferenceDialogFragment();
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String);
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String);
+    method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter();
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener);
+    method public android.view.ViewGroup getContainer();
+    method public android.widget.TextView getTitleView();
+    method public android.widget.Checkable getWidgetView();
+    method public void onClick(android.view.View);
+  }
+
+  public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackPreferenceDialogFragment extends android.app.Fragment {
+    ctor public LeanbackPreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    field public static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
+    ctor public LeanbackPreferenceFragment();
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
+    ctor public LeanbackSettingsFragment();
+    method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+    method public abstract void onPreferenceStartInitialScreen();
+    method public void startImmersiveFragment(android.app.Fragment);
+    method public void startPreferenceFragment(android.app.Fragment);
+  }
+
+}
+
+package android.support.v4.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static java.lang.String capabilityToString(int);
+    method public static java.lang.String feedbackTypeToString(int);
+    method public static java.lang.String flagToString(int);
+    method public static deprecated boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final deprecated int DEFAULT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package android.support.v4.app {
+
+  public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int);
+    method public boolean isDrawerIndicatorEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void syncState();
+  }
+
+  public static abstract interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class ActivityCompat extends android.support.v4.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri getReferrer(android.app.Activity);
+    method public static boolean invalidateOptionsMenu(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void requestPermissions(android.app.Activity, java.lang.String[], int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect getLaunchBounds();
+    method public static android.support.v4.app.ActivityOptionsCompat makeBasic();
+    method public static android.support.v4.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...);
+    method public static android.support.v4.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public android.support.v4.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect);
+    method public android.os.Bundle toBundle();
+    method public void update(android.support.v4.app.ActivityOptionsCompat);
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  public class AppLaunchChecker {
+    ctor public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
+    method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
+    method public static java.lang.String permissionToOp(java.lang.String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String);
+    method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder);
+  }
+
+  public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method public android.app.Dialog getDialog();
+    method public boolean getShowsDialog();
+    method public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method public android.app.Dialog onCreateDialog(android.os.Bundle);
+    method public void onDismiss(android.content.DialogInterface);
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, int);
+    method public void show(android.support.v4.app.FragmentManager, java.lang.String);
+    method public int show(android.support.v4.app.FragmentTransaction, java.lang.String);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+    ctor public Fragment();
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final boolean equals(java.lang.Object);
+    method public final android.support.v4.app.FragmentActivity getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle getArguments();
+    method public final android.support.v4.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context getContext();
+    method public java.lang.Object getEnterTransition();
+    method public java.lang.Object getExitTransition();
+    method public final android.support.v4.app.FragmentManager getFragmentManager();
+    method public final java.lang.Object getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method public android.support.v4.app.LoaderManager getLoaderManager();
+    method public final android.support.v4.app.Fragment getParentFragment();
+    method public java.lang.Object getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method public final boolean getRetainInstance();
+    method public java.lang.Object getReturnTransition();
+    method public java.lang.Object getSharedElementEnterTransition();
+    method public java.lang.Object getSharedElementReturnTransition();
+    method public final java.lang.String getString(int);
+    method public final java.lang.String getString(int, java.lang.Object...);
+    method public final java.lang.String getTag();
+    method public final android.support.v4.app.Fragment getTargetFragment();
+    method public final int getTargetRequestCode();
+    method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
+    method public android.view.View getView();
+    method public final int hashCode();
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String);
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method public void onActivityCreated(android.os.Bundle);
+    method public void onActivityResult(int, int, android.content.Intent);
+    method public void onAttach(android.content.Context);
+    method public deprecated void onAttach(android.app.Activity);
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public boolean onContextItemSelected(android.view.MenuItem);
+    method public void onCreate(android.os.Bundle);
+    method public android.view.animation.Animation onCreateAnimation(int, boolean, int);
+    method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
+    method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDestroy();
+    method public void onDestroyOptionsMenu();
+    method public void onDestroyView();
+    method public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
+    method public void onHiddenChanged(boolean);
+    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
+    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
+    method public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void onOptionsMenuClosed(android.view.Menu);
+    method public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
+    method public void onResume();
+    method public void onSaveInstanceState(android.os.Bundle);
+    method public void onStart();
+    method public void onStop();
+    method public void onViewCreated(android.view.View, android.os.Bundle);
+    method public void onViewStateRestored(android.os.Bundle);
+    method public void postponeEnterTransition();
+    method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle);
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setEnterTransition(java.lang.Object);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitTransition(java.lang.Object);
+    method public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(java.lang.Object);
+    method public void setRetainInstance(boolean);
+    method public void setReturnTransition(java.lang.Object);
+    method public void setSharedElementEnterTransition(java.lang.Object);
+    method public void setSharedElementReturnTransition(java.lang.Object);
+    method public void setTargetFragment(android.support.v4.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle);
+    method public void startActivityForResult(android.content.Intent, int);
+    method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR;
+  }
+
+  public class FragmentActivity extends android.app.Activity implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback {
+    ctor public FragmentActivity();
+    method public java.lang.Object getLastCustomNonConfigurationInstance();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method protected void onResumeFragments();
+    method public java.lang.Object onRetainCustomNonConfigurationInstance();
+    method public final java.lang.Object onRetainNonConfigurationInstance();
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method public deprecated void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method public android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public abstract android.view.View onFindViewById(int);
+    method public abstract boolean onHasView();
+  }
+
+  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 void dispatchActivityCreated();
+    method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method public void dispatchLowMemory();
+    method public void dispatchMultiWindowModeChanged(boolean);
+    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method public void dispatchPictureInPictureModeChanged(boolean);
+    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method public void doLoaderDestroy();
+    method public void doLoaderRetain();
+    method public void doLoaderStart();
+    method public void doLoaderStop(boolean);
+    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public boolean execPendingActions();
+    method public android.support.v4.app.Fragment findFragmentByWho(java.lang.String);
+    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
+    method public int getActiveFragmentsCount();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public void reportLoaderStart();
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig);
+    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
+    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
+    method public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.support.v4.app.Fragment> retainNonConfig();
+    method public android.os.Parcelable saveAllState();
+  }
+
+  public abstract class FragmentHostCallback<E> extends android.support.v4.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.view.View onFindViewById(int);
+    method public abstract E onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int);
+    method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(java.lang.String);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager {
+    ctor public FragmentManager();
+    method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.FragmentTransaction beginTransaction();
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract boolean executePendingTransactions();
+    method public abstract android.support.v4.app.Fragment findFragmentById(int);
+    method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String);
+    method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public abstract int getBackStackEntryCount();
+    method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String);
+    method public abstract java.util.List<android.support.v4.app.Fragment> getFragments();
+    method public abstract android.support.v4.app.Fragment getPrimaryNavigationFragment();
+    method public abstract boolean isDestroyed();
+    method public abstract boolean isStateSaved();
+    method public abstract void popBackStack();
+    method public abstract void popBackStack(java.lang.String, int);
+    method public abstract void popBackStack(int, int);
+    method public abstract boolean popBackStackImmediate();
+    method public abstract boolean popBackStackImmediate(java.lang.String, int);
+    method public abstract boolean popBackStackImmediate(int, int);
+    method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static abstract interface FragmentManager.BackStackEntry {
+    method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
+    method public abstract int getBreadCrumbShortTitleRes();
+    method public abstract java.lang.CharSequence getBreadCrumbTitle();
+    method public abstract int getBreadCrumbTitleRes();
+    method public abstract int getId();
+    method public abstract java.lang.String getName();
+  }
+
+  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+  }
+
+  public static abstract interface FragmentManager.OnBackStackChangedListener {
+    method public abstract void onBackStackChanged();
+  }
+
+  public class FragmentManagerNonConfig {
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor public FragmentTransaction();
+    method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
+    method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack();
+    method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
+    method public abstract boolean isAddToBackStackAllowed();
+    method public abstract boolean isEmpty();
+    method public abstract android.support.v4.app.FragmentTransaction postOnCommit(java.lang.Runnable);
+    method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setPrimaryNavigationFragment(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction setTransition(int);
+    method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int);
+    method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray[] getMetrics();
+    method public android.util.SparseIntArray[] remove(android.app.Activity);
+    method public android.util.SparseIntArray[] reset();
+    method public android.util.SparseIntArray[] stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public class ListFragment extends android.support.v4.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public void setEmptyText(java.lang.CharSequence);
+    method public void setListAdapter(android.widget.ListAdapter);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+  public abstract class LoaderManager {
+    ctor public LoaderManager();
+    method public abstract void destroyLoader(int);
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract <D> android.support.v4.content.Loader<D> getLoader(int);
+    method public boolean hasRunningLoaders();
+    method public abstract <D> android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+    method public abstract <D> android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+  }
+
+  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+    method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle);
+    method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D);
+    method public abstract void onLoaderReset(android.support.v4.content.Loader<D>);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static java.lang.String getParentActivityName(android.app.Activity);
+    method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static java.lang.String getCategory(android.app.Notification);
+    method public static java.lang.String getChannel(android.app.Notification);
+    method public static android.os.Bundle getExtras(android.app.Notification);
+    method public static java.lang.String getGroup(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static java.lang.String getShortcutId(android.app.Notification);
+    method public static java.lang.String getSortKey(android.app.Notification);
+    method public static long getTimeout(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+    field public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+    field public static final java.lang.String EXTRA_PICTURE = "android.picture";
+    field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
+    field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
+    field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
+    field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
+    field public static final java.lang.String EXTRA_TEXT = "android.text";
+    field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_TITLE = "android.title";
+    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.app.PendingIntent getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public android.support.v4.app.RemoteInput[] getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public android.support.v4.app.RemoteInput[] getRemoteInputs();
+    method public java.lang.CharSequence getTitle();
+    field public android.app.PendingIntent actionIntent;
+    field public int icon;
+    field public java.lang.CharSequence title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
+    ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput);
+    method public android.support.v4.app.NotificationCompat.Action build();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+  }
+
+  public static abstract interface NotificationCompat.Action.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+    method public java.lang.CharSequence getCancelLabel();
+    method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method public java.lang.CharSequence getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated NotificationCompat.Builder(android.content.Context);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String);
+    method public android.app.Notification build();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method public deprecated android.app.Notification getNotification();
+    method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setChannel(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setColor(int);
+    method public android.support.v4.app.NotificationCompat.Builder setColorized(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setDefaults(int);
+    method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setNumber(int);
+    method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPriority(int);
+    method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.support.v4.app.NotificationCompat.Builder setShortcutId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int);
+    method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style);
+    method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setTimeout(long);
+    method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]);
+    method public android.support.v4.app.NotificationCompat.Builder setVisibility(int);
+    method public android.support.v4.app.NotificationCompat.Builder setWhen(long);
+    field public java.util.ArrayList<java.lang.String> mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.support.v4.app.NotificationCompat.CarExtender setColor(int);
+    method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation);
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.support.v4.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput);
+  }
+
+  public static abstract interface NotificationCompat.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
+    method public void addCompatExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(android.support.v4.app.NotificationCompat.MessagingStyle.Message);
+    method public static android.support.v4.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.support.v4.app.NotificationCompat.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+  }
+
+  public static abstract class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification build();
+    method public void setBuilder(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearActions();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearPages();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public java.lang.String getDismissalId();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintAmbientBigPicture();
+    method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintHideIcon();
+    method public int getHintScreenTimeout();
+    method public boolean getHintShowBackgroundOnly();
+    method public java.util.List<android.app.Notification> getPages();
+    method public boolean getStartScrollBottom();
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBridgeTag(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDismissalId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field public static final int SIZE_LARGE = 4; // 0x4
+    field public static final int SIZE_MEDIUM = 3; // 0x3
+    field public static final int SIZE_SMALL = 2; // 0x2
+    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(java.lang.String, int, java.lang.String);
+    method public abstract void cancelAll(java.lang.String);
+    method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(java.lang.String, int);
+    method public void cancelAll();
+    method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public void notify(int, android.app.Notification);
+    method public void notify(java.lang.String, int, android.app.Notification);
+    field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public final class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput {
+    method public static void addDataResultToIntent(android.support.v4.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
+    method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
+    method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getResultKey();
+    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
+    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(java.lang.String);
+    method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
+    method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+    method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+  }
+
+   deprecated class RemoteInputCompatBase {
+  }
+
+  public static abstract class RemoteInputCompatBase.RemoteInput {
+    ctor public RemoteInputCompatBase.RemoteInput();
+    method protected abstract boolean getAllowFreeFormInput();
+    method protected abstract java.util.Set<java.lang.String> getAllowedDataTypes();
+    method protected abstract java.lang.CharSequence[] getChoices();
+    method protected abstract android.os.Bundle getExtras();
+    method protected abstract java.lang.CharSequence getLabel();
+    method protected abstract java.lang.String getResultKey();
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName getCallingActivity(android.app.Activity);
+    method public static java.lang.String getCallingPackage(android.app.Activity);
+    field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName getCallingActivity();
+    method public android.graphics.drawable.Drawable getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable getCallingApplicationIcon();
+    method public java.lang.CharSequence getCallingApplicationLabel();
+    method public java.lang.String getCallingPackage();
+    method public java.lang.String[] getEmailBcc();
+    method public java.lang.String[] getEmailCc();
+    method public java.lang.String[] getEmailTo();
+    method public java.lang.String getHtmlText();
+    method public android.net.Uri getStream();
+    method public android.net.Uri getStream(int);
+    method public int getStreamCount();
+    method public java.lang.String getSubject();
+    method public java.lang.CharSequence getText();
+    method public java.lang.String getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
+    method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable);
+    method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
+    method public void onRejectSharedElements(java.util.List<android.view.View>);
+    method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String>, java.util.List<android.view.View>, android.support.v4.app.SharedElementCallback.OnSharedElementsReadyListener);
+  }
+
+  public static abstract interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public abstract void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable {
+    method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public static android.support.v4.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent editIntentAt(int);
+    method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context);
+    method public deprecated android.content.Intent getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent[] getIntents();
+    method public android.app.PendingIntent getPendingIntent(int, int);
+    method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle);
+    method public deprecated java.util.Iterator<android.content.Intent> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle);
+  }
+
+  public static abstract interface TaskStackBuilder.SupportParentable {
+    method public abstract android.content.Intent getSupportParentActivityIntent();
+  }
+
+}
+
+package android.support.v4.content {
+
+  public abstract class AsyncTaskLoader<D> extends android.support.v4.content.Loader {
+    ctor public AsyncTaskLoader(android.content.Context);
+    method public void cancelLoadInBackground();
+    method public boolean isLoadInBackgroundCanceled();
+    method public abstract D loadInBackground();
+    method public void onCanceled(D);
+    method protected D onLoadInBackground();
+    method public void setUpdateThrottle(long);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    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 java.io.File getDataDir(android.content.Context);
+    method public static final 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[] getObbDirs(android.content.Context);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class CursorLoader extends android.support.v4.content.AsyncTaskLoader {
+    ctor public CursorLoader(android.content.Context);
+    ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public void deliverResult(android.database.Cursor);
+    method public java.lang.String[] getProjection();
+    method public java.lang.String getSelection();
+    method public java.lang.String[] getSelectionArgs();
+    method public java.lang.String getSortOrder();
+    method public android.net.Uri getUri();
+    method public android.database.Cursor loadInBackground();
+    method public void onCanceled(android.database.Cursor);
+    method public void setProjection(java.lang.String[]);
+    method public void setSelection(java.lang.String);
+    method public void setSelectionArgs(java.lang.String[]);
+    method public void setSortOrder(java.lang.String);
+    method public void setUri(android.net.Uri);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File);
+    method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
+  public final class IntentCompat {
+    method public static deprecated android.content.Intent makeMainActivity(android.content.ComponentName);
+    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
+    method public static deprecated android.content.Intent makeRestartActivityTask(android.content.ComponentName);
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final java.lang.String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final deprecated int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
+    field public static final deprecated int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
+  }
+
+  public class Loader<D> {
+    ctor public Loader(android.content.Context);
+    method public void abandon();
+    method public boolean cancelLoad();
+    method public void commitContentChanged();
+    method public java.lang.String dataToString(D);
+    method public void deliverCancellation();
+    method public void deliverResult(D);
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void forceLoad();
+    method public android.content.Context getContext();
+    method public int getId();
+    method public boolean isAbandoned();
+    method public boolean isReset();
+    method public boolean isStarted();
+    method protected void onAbandon();
+    method protected boolean onCancelLoad();
+    method public void onContentChanged();
+    method protected void onForceLoad();
+    method protected void onReset();
+    method protected void onStartLoading();
+    method protected void onStopLoading();
+    method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+    method public void reset();
+    method public void rollbackContentChanged();
+    method public final void startLoading();
+    method public void stopLoading();
+    method public boolean takeContentChanged();
+    method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+  }
+
+  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+    ctor public Loader.ForceLoadContentObserver();
+  }
+
+  public static abstract interface Loader.OnLoadCanceledListener<D> {
+    method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>);
+  }
+
+  public static abstract interface Loader.OnLoadCompleteListener<D> {
+    method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D);
+  }
+
+  public final class LocalBroadcastManager {
+    method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context);
+    method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+    method public boolean sendBroadcast(android.content.Intent);
+    method public void sendBroadcastSync(android.content.Intent);
+    method public void unregisterReceiver(android.content.BroadcastReceiver);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(java.lang.String, java.lang.String);
+    method public static java.lang.String matches(java.lang.String, java.lang.String[]);
+    method public static java.lang.String matches(java.lang.String[], java.lang.String);
+    method public static java.lang.String[] matchesMany(java.lang.String[], java.lang.String);
+  }
+
+  public final deprecated class ParallelExecutorCompat {
+    method public static deprecated java.util.concurrent.Executor getParallelExecutor();
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String);
+    method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String);
+    method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String);
+    method public static int checkSelfPermission(android.content.Context, java.lang.String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  public final class SharedPreferencesCompat {
+  }
+
+  public static final class SharedPreferencesCompat.EditorCompat {
+    method public void apply(android.content.SharedPreferences.Editor);
+    method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance();
+  }
+
+  public abstract deprecated class WakefulBroadcastReceiver extends android.content.BroadcastReceiver {
+    ctor public WakefulBroadcastReceiver();
+    method public static boolean completeWakefulIntent(android.content.Intent);
+    method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent);
+  }
+
+}
+
+package android.support.v4.content.pm {
+
+  public final class ActivityInfoCompat {
+    field public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName getActivity();
+    method public java.lang.CharSequence getDisabledMessage();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent[] getIntents();
+    method public java.lang.CharSequence getLongLabel();
+    method public java.lang.CharSequence getShortLabel();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, java.lang.String);
+    method public android.support.v4.content.pm.ShortcutInfoCompat build();
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(java.lang.CharSequence);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.graphics.Bitmap);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(int);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.support.v4.graphics.drawable.IconCompat);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent[]);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setLongLabel(java.lang.CharSequence);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setShortLabel(java.lang.CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean requestPinShortcut(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat, android.content.IntentSender);
+  }
+
+}
+
+package android.support.v4.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+    method public static deprecated int getScreenHeightDp(android.content.res.Resources);
+    method public static deprecated int getScreenWidthDp(android.content.res.Resources);
+    method public static deprecated int getSmallestScreenWidthDp(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.Typeface getFont(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
+  }
+
+}
+
+package android.support.v4.database {
+
+  public final class DatabaseUtilsCompat {
+    method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]);
+    method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public final class ColorUtils {
+    method public static int HSLToColor(float[]);
+    method public static int LABToColor(double, double, double);
+    method public static void LABToXYZ(double, double, double, double[]);
+    method public static void RGBToHSL(int, int, int, float[]);
+    method public static void RGBToLAB(int, int, int, double[]);
+    method public static void RGBToXYZ(int, int, int, double[]);
+    method public static int XYZToColor(double, double, double);
+    method public static void XYZToLAB(double, double, double, double[]);
+    method public static int blendARGB(int, int, float);
+    method public static void blendHSL(float[], float[], float, float[]);
+    method public static void blendLAB(double[], double[], double, double[]);
+    method public static double calculateContrast(int, int);
+    method public static double calculateLuminance(int);
+    method public static int calculateMinimumAlpha(int, int, float);
+    method public static void colorToHSL(int, float[]);
+    method public static void colorToLAB(int, double[]);
+    method public static void colorToXYZ(int, double[]);
+    method public static int compositeColors(int, int);
+    method public static double distanceEuclidean(double[], double[]);
+    method public static int setAlphaComponent(int, int);
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  public class IconCompat {
+    method public static android.support.v4.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(java.lang.String);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithData(byte[], int, int);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithResource(android.content.Context, int);
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setCornerRadius(float);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package android.support.v4.hardware.display {
+
+  public abstract class DisplayManagerCompat {
+    method public abstract android.view.Display getDisplay(int);
+    method public abstract android.view.Display[] getDisplays();
+    method public abstract android.view.Display[] getDisplays(java.lang.String);
+    method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package android.support.v4.hardware.fingerprint {
+
+  public final class FingerprintManagerCompat {
+    method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler);
+    method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
+  }
+
+  public static abstract class FingerprintManagerCompat.AuthenticationCallback {
+    ctor public FingerprintManagerCompat.AuthenticationCallback();
+    method public void onAuthenticationError(int, java.lang.CharSequence);
+    method public void onAuthenticationFailed();
+    method public void onAuthenticationHelp(int, java.lang.CharSequence);
+    method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult);
+  }
+
+  public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject);
+    method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject();
+  }
+
+  public static class FingerprintManagerCompat.CryptoObject {
+    ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method public javax.crypto.Cipher getCipher();
+    method public javax.crypto.Mac getMac();
+    method public java.security.Signature getSignature();
+  }
+
+}
+
+package android.support.v4.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+  }
+
+}
+
+package android.support.v4.media {
+
+  public class AudioAttributesCompat {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method public int getUsage();
+    method public int getVolumeControlStream();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.media.AudioAttributesCompat wrap(java.lang.Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(android.support.v4.media.AudioAttributesCompat);
+    method public android.support.v4.media.AudioAttributesCompat build();
+    method public android.support.v4.media.AudioAttributesCompat.Builder setContentType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setFlags(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setLegacyStreamType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setUsage(int);
+  }
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle getExtras();
+    method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method public java.lang.String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.CustomActionCallback);
+    method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(java.lang.String);
+    method public void unsubscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public static abstract class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onProgressUpdate(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onResult(java.lang.String, android.os.Bundle, android.os.Bundle);
+  }
+
+  public static abstract class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(java.lang.String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem fromMediaItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem> fromMediaItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public java.lang.String getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static abstract class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+  }
+
+  public static abstract class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>, android.os.Bundle);
+    method public void onError(java.lang.String);
+    method public void onError(java.lang.String, android.os.Bundle);
+  }
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public void notifyChildrenChanged(java.lang.String);
+    method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<android.os.Bundle>);
+    method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>, android.os.Bundle);
+    method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field public static final deprecated java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle);
+    method public void sendProgressUpdate(android.os.Bundle);
+    method public void sendResult(T);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object);
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public android.net.Uri getIconUri();
+    method public java.lang.Object getMediaDescription();
+    method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR;
+    field public static final java.lang.String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(java.lang.String);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object);
+    method public android.graphics.Bitmap getBitmap(java.lang.String);
+    method public android.os.Bundle getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getLong(java.lang.String);
+    method public java.lang.Object getMediaMetadata();
+    method public android.support.v4.media.RatingCompat getRating(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR;
+    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat);
+    method public android.support.v4.media.MediaMetadataCompat build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object);
+    method public float getPercentRating();
+    method public java.lang.Object getRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat newStarRating(int, float);
+    method public static android.support.v4.media.RatingCompat newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public java.lang.Object getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static abstract class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat);
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
+    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
+    method public android.os.Bundle getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat getMediaController(android.app.Activity);
+    method public java.lang.Object getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat getMetadata();
+    method public java.lang.String getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
+    method public java.lang.CharSequence getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent getSessionActivity();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public boolean isShuffleModeEnabled();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void removeQueueItemAt(int);
+    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onRepeatModeChanged(int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public void onShuffleModeChanged(boolean);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public static abstract class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void playFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void playFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
+    method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleModeEnabled(boolean);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public static android.support.v4.media.session.MediaSessionCompat fromMediaSession(android.content.Context, java.lang.Object);
+    method public android.support.v4.media.session.MediaControllerCompat getController();
+    method public java.lang.Object getMediaSession();
+    method public java.lang.Object getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void setQueueTitle(java.lang.CharSequence);
+    method public void setRatingType(int);
+    method public void setRepeatMode(int);
+    method public void setSessionActivity(android.app.PendingIntent);
+    method public void setShuffleModeEnabled(boolean);
+    field public static final java.lang.String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final java.lang.String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+  }
+
+  public static abstract class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void onCustomAction(java.lang.String, android.os.Bundle);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleModeEnabled(boolean);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static abstract interface MediaSessionCompat.OnActiveChangeListener {
+    method public abstract void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem fromQueueItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> fromQueueItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getQueueId();
+    method public java.lang.Object getQueueItem();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object);
+    method public java.lang.Object getToken();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object);
+    method public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions();
+    method public int getErrorCode();
+    method public java.lang.CharSequence getErrorMessage();
+    method public android.os.Bundle getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public java.lang.Object getPlaybackState();
+    method public long getPosition();
+    method public int getState();
+    method public static int toKeyCode(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction);
+    method public android.support.v4.media.session.PlaybackStateCompat build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long);
+    method public deprecated android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(int, java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object);
+    method public java.lang.String getAction();
+    method public java.lang.Object getCustomAction();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle);
+  }
+
+}
+
+package android.support.v4.net {
+
+  public final class ConnectivityManagerCompat {
+    method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class TrafficStatsCompat {
+    method public static deprecated void clearThreadStatsTag();
+    method public static deprecated int getThreadStatsTag();
+    method public static deprecated void incrementOperationCount(int);
+    method public static deprecated void incrementOperationCount(int, int);
+    method public static deprecated void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void tagSocket(java.net.Socket) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void untagSocket(java.net.Socket) throws java.net.SocketException;
+  }
+
+}
+
+package android.support.v4.os {
+
+  public final deprecated class AsyncTaskCompat {
+    method public static deprecated <Params, Progress, Result> android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
+  }
+
+  public class BuildCompat {
+    method public static deprecated boolean isAtLeastN();
+    method public static deprecated boolean isAtLeastNMR1();
+    method public static boolean isAtLeastO();
+    method public static boolean isAtLeastOMR1();
+    method public static boolean isAtLeastP();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public java.lang.Object getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener);
+    method public void throwIfCanceled();
+  }
+
+  public static abstract interface CancellationSignal.OnCancelListener {
+    method public abstract void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static android.support.v4.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static java.lang.String getStorageState(java.io.File);
+    field public static final java.lang.String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class LocaleListCompat {
+    method public static android.support.v4.os.LocaleListCompat create(java.util.Locale...);
+    method public static android.support.v4.os.LocaleListCompat forLanguageTags(java.lang.String);
+    method public java.util.Locale get(int);
+    method public static android.support.v4.os.LocaleListCompat getAdjustedDefault();
+    method public static android.support.v4.os.LocaleListCompat getDefault();
+    method public static android.support.v4.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale getFirstMatch(java.lang.String[]);
+    method public int indexOf(java.util.Locale);
+    method public boolean isEmpty();
+    method public int size();
+    method public java.lang.String toLanguageTags();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.os.LocaleListCompat wrap(java.lang.Object);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(java.lang.String);
+  }
+
+  public final deprecated class ParcelableCompat {
+    method public static deprecated <T> android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>);
+  }
+
+  public abstract deprecated interface ParcelableCompatCreatorCallbacks<T> {
+    method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader);
+    method public abstract T[] newArray(int);
+  }
+
+  public final class TraceCompat {
+    method public static void beginSection(java.lang.String);
+    method public static void endSection();
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package android.support.v4.print {
+
+  public final class PrintHelper {
+    ctor public PrintHelper(android.content.Context);
+    method public int getColorMode();
+    method public int getOrientation();
+    method public int getScaleMode();
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap);
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback);
+    method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException;
+    method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException;
+    method public void setColorMode(int);
+    method public void setOrientation(int);
+    method public void setScaleMode(int);
+    method public static boolean systemSupportsPrint();
+    field public static final int COLOR_MODE_COLOR = 2; // 0x2
+    field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
+    field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
+    field public static final int ORIENTATION_PORTRAIT = 2; // 0x2
+    field public static final int SCALE_MODE_FILL = 2; // 0x2
+    field public static final int SCALE_MODE_FIT = 1; // 0x1
+  }
+
+  public static abstract interface PrintHelper.OnPrintFinishCallback {
+    method public abstract void onFinish();
+  }
+
+}
+
+package android.support.v4.provider {
+
+  public abstract class DocumentFile {
+    method public abstract boolean canRead();
+    method public abstract boolean canWrite();
+    method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String);
+    method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String);
+    method public abstract boolean delete();
+    method public abstract boolean exists();
+    method public android.support.v4.provider.DocumentFile findFile(java.lang.String);
+    method public static android.support.v4.provider.DocumentFile fromFile(java.io.File);
+    method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri);
+    method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri);
+    method public abstract java.lang.String getName();
+    method public android.support.v4.provider.DocumentFile getParentFile();
+    method public abstract java.lang.String getType();
+    method public abstract android.net.Uri getUri();
+    method public abstract boolean isDirectory();
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public abstract boolean isFile();
+    method public abstract boolean isVirtual();
+    method public abstract long lastModified();
+    method public abstract long length();
+    method public abstract android.support.v4.provider.DocumentFile[] listFiles();
+    method public abstract boolean renameTo(java.lang.String);
+  }
+
+  public final class FontRequest {
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, int);
+    method public java.util.List<java.util.List<byte[]>> getCertificates();
+    method public int getCertificatesArrayResId();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getProviderPackage();
+    method public java.lang.String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontsContractCompat.FontInfo[]);
+    method public static android.support.v4.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, android.support.v4.provider.FontRequest, android.support.v4.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns {
+    ctor public FontsContractCompat.Columns();
+    field public static final java.lang.String FILE_ID = "file_id";
+    field public static final java.lang.String ITALIC = "font_italic";
+    field public static final java.lang.String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final java.lang.String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public android.support.v4.provider.FontsContractCompat.FontInfo[] getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package android.support.v4.text {
+
+  public final class BidiFormatter {
+    method public static android.support.v4.text.BidiFormatter getInstance();
+    method public static android.support.v4.text.BidiFormatter getInstance(boolean);
+    method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale);
+    method public boolean getStereoReset();
+    method public boolean isRtl(java.lang.String);
+    method public boolean isRtl(java.lang.CharSequence);
+    method public boolean isRtlContext();
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.String unicodeWrap(java.lang.String, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale);
+    method public android.support.v4.text.BidiFormatter build();
+    method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat);
+    method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean);
+  }
+
+  public final class ICUCompat {
+    method public static java.lang.String maximizeAndGetScript(java.util.Locale);
+  }
+
+  public abstract interface TextDirectionHeuristicCompat {
+    method public abstract boolean isRtl(char[], int, int);
+    method public abstract boolean isRtl(java.lang.CharSequence, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale);
+    method public static java.lang.String htmlEncode(java.lang.String);
+    field public static final deprecated java.util.Locale ROOT;
+  }
+
+}
+
+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);
+  }
+
+}
+
+package android.support.v4.util {
+
+  public class ArrayMap<K, V> extends android.support.v4.util.SimpleArrayMap implements java.util.Map {
+    ctor public ArrayMap();
+    ctor public ArrayMap(int);
+    ctor public ArrayMap(android.support.v4.util.SimpleArrayMap);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+    method public java.util.Set<K> keySet();
+    method public void putAll(java.util.Map<? extends K, ? extends V>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public java.util.Collection<V> values();
+  }
+
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    ctor public ArraySet();
+    ctor public ArraySet(int);
+    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
+    method public boolean add(E);
+    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(java.lang.Object);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public void ensureCapacity(int);
+    method public int indexOf(java.lang.Object);
+    method public boolean isEmpty();
+    method public java.util.Iterator<E> iterator();
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public E removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public int size();
+    method public java.lang.Object[] toArray();
+    method public <T> T[] toArray(T[]);
+    method public E valueAt(int);
+  }
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream);
+    method public void finishWrite(java.io.FileOutputStream);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public final class CircularArray<E> {
+    ctor public CircularArray();
+    ctor public CircularArray(int);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public void clear();
+    method public E get(int);
+    method public E getFirst();
+    method public E getLast();
+    method public boolean isEmpty();
+    method public E popFirst();
+    method public E popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public final class CircularIntArray {
+    ctor public CircularIntArray();
+    ctor public CircularIntArray(int);
+    method public void addFirst(int);
+    method public void addLast(int);
+    method public void clear();
+    method public int get(int);
+    method public int getFirst();
+    method public int getLast();
+    method public boolean isEmpty();
+    method public int popFirst();
+    method public int popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public class LongSparseArray<E> {
+    ctor public LongSparseArray();
+    ctor public LongSparseArray(int);
+    method public void append(long, E);
+    method public void clear();
+    method public android.support.v4.util.LongSparseArray<E> clone();
+    method public void delete(long);
+    method public E get(long);
+    method public E get(long, E);
+    method public int indexOfKey(long);
+    method public int indexOfValue(E);
+    method public long keyAt(int);
+    method public void put(long, E);
+    method public void remove(long);
+    method public void removeAt(int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+  public class LruCache<K, V> {
+    ctor public LruCache(int);
+    method protected V create(K);
+    method public final synchronized int createCount();
+    method protected void entryRemoved(boolean, K, V, V);
+    method public final void evictAll();
+    method public final synchronized int evictionCount();
+    method public final V get(K);
+    method public final synchronized int hitCount();
+    method public final synchronized int maxSize();
+    method public final synchronized int missCount();
+    method public final V put(K, V);
+    method public final synchronized int putCount();
+    method public final V remove(K);
+    method public void resize(int);
+    method public final synchronized int size();
+    method protected int sizeOf(K, V);
+    method public final synchronized java.util.Map<K, V> snapshot();
+    method public final synchronized java.lang.String toString();
+    method public void trimToSize(int);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F, S);
+    method public static <A, B> android.support.v4.util.Pair<A, B> create(A, B);
+    field public final F first;
+    field public final S second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static abstract interface Pools.Pool<T> {
+    method public abstract T acquire();
+    method public abstract boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements android.support.v4.util.Pools.Pool {
+    ctor public Pools.SimplePool(int);
+    method public T acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends android.support.v4.util.Pools.SimplePool {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public class SimpleArrayMap<K, V> {
+    ctor public SimpleArrayMap();
+    ctor public SimpleArrayMap(int);
+    ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap<K, V>);
+    method public void clear();
+    method public boolean containsKey(java.lang.Object);
+    method public boolean containsValue(java.lang.Object);
+    method public void ensureCapacity(int);
+    method public V get(java.lang.Object);
+    method public int indexOfKey(java.lang.Object);
+    method public boolean isEmpty();
+    method public K keyAt(int);
+    method public V put(K, V);
+    method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>);
+    method public V remove(java.lang.Object);
+    method public V removeAt(int);
+    method public V setValueAt(int, V);
+    method public int size();
+    method public V valueAt(int);
+  }
+
+  public class SparseArrayCompat<E> {
+    ctor public SparseArrayCompat();
+    ctor public SparseArrayCompat(int);
+    method public void append(int, E);
+    method public void clear();
+    method public android.support.v4.util.SparseArrayCompat<E> clone();
+    method public void delete(int);
+    method public E get(int);
+    method public E get(int, E);
+    method public int indexOfKey(int);
+    method public int indexOfValue(E);
+    method public int keyAt(int);
+    method public void put(int, E);
+    method public void remove(int);
+    method public void removeAt(int);
+    method public void removeAtRange(int, int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+}
+
+package android.support.v4.view {
+
+  public abstract class AbsSavedState implements android.os.Parcelable {
+    ctor protected AbsSavedState(android.os.Parcelable);
+    ctor protected AbsSavedState(android.os.Parcel);
+    ctor protected AbsSavedState(android.os.Parcel, java.lang.ClassLoader);
+    method public int describeContents();
+    method public final android.os.Parcelable getSuperState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.AbsSavedState> CREATOR;
+    field public static final android.support.v4.view.AbsSavedState EMPTY_STATE;
+  }
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener);
+  }
+
+  public static abstract interface ActionProvider.VisibilityListener {
+    method public abstract void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class AsyncLayoutInflater {
+    ctor public AsyncLayoutInflater(android.content.Context);
+    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+  }
+
+  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final deprecated class KeyEventCompat {
+    method public static deprecated boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object);
+    method public static deprecated java.lang.Object getKeyDispatcherState(android.view.View);
+    method public static deprecated boolean hasModifiers(android.view.KeyEvent, int);
+    method public static deprecated boolean hasNoModifiers(android.view.KeyEvent);
+    method public static deprecated boolean isCtrlPressed(android.view.KeyEvent);
+    method public static deprecated boolean isTracking(android.view.KeyEvent);
+    method public static deprecated boolean metaStateHasModifiers(int, int);
+    method public static deprecated boolean metaStateHasNoModifiers(int);
+    method public static deprecated int normalizeMetaState(int);
+    method public static deprecated void startTracking(android.view.KeyEvent);
+  }
+
+  public final class LayoutInflaterCompat {
+    method public static deprecated android.support.v4.view.LayoutInflaterFactory getFactory(android.view.LayoutInflater);
+    method public static deprecated void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  public abstract deprecated interface LayoutInflaterFactory {
+    method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+  }
+
+  public final class MenuItemCompat {
+    method public static deprecated boolean collapseActionView(android.view.MenuItem);
+    method public static deprecated boolean expandActionView(android.view.MenuItem);
+    method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
+    method public static deprecated android.view.View getActionView(android.view.MenuItem);
+    method public static int getAlphabeticModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getContentDescription(android.view.MenuItem);
+    method public static android.content.res.ColorStateList getIconTintList(android.view.MenuItem);
+    method public static android.graphics.PorterDuff.Mode getIconTintMode(android.view.MenuItem);
+    method public static int getNumericModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getTooltipText(android.view.MenuItem);
+    method public static deprecated boolean isActionViewExpanded(android.view.MenuItem);
+    method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+    method public static void setContentDescription(android.view.MenuItem, java.lang.CharSequence);
+    method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList);
+    method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
+    method public static deprecated android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+    method public static void setTooltipText(android.view.MenuItem, java.lang.CharSequence);
+    field public static final deprecated int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final deprecated int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final deprecated int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final deprecated int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final deprecated int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  public static abstract deprecated interface MenuItemCompat.OnActionExpandListener {
+    method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem);
+    method public abstract boolean onMenuItemActionExpand(android.view.MenuItem);
+  }
+
+  public final class MotionEventCompat {
+    method public static deprecated int findPointerIndex(android.view.MotionEvent, int);
+    method public static deprecated int getActionIndex(android.view.MotionEvent);
+    method public static deprecated int getActionMasked(android.view.MotionEvent);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int, int);
+    method public static deprecated int getButtonState(android.view.MotionEvent);
+    method public static deprecated int getPointerCount(android.view.MotionEvent);
+    method public static deprecated int getPointerId(android.view.MotionEvent, int);
+    method public static deprecated int getSource(android.view.MotionEvent);
+    method public static deprecated float getX(android.view.MotionEvent, int);
+    method public static deprecated float getY(android.view.MotionEvent, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field public static final deprecated int ACTION_HOVER_ENTER = 9; // 0x9
+    field public static final deprecated int ACTION_HOVER_EXIT = 10; // 0xa
+    field public static final deprecated int ACTION_HOVER_MOVE = 7; // 0x7
+    field public static final deprecated int ACTION_MASK = 255; // 0xff
+    field public static final deprecated int ACTION_POINTER_DOWN = 5; // 0x5
+    field public static final deprecated int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field public static final deprecated int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field public static final deprecated int ACTION_POINTER_UP = 6; // 0x6
+    field public static final deprecated int ACTION_SCROLL = 8; // 0x8
+    field public static final deprecated int AXIS_BRAKE = 23; // 0x17
+    field public static final deprecated int AXIS_DISTANCE = 24; // 0x18
+    field public static final deprecated int AXIS_GAS = 22; // 0x16
+    field public static final deprecated int AXIS_GENERIC_1 = 32; // 0x20
+    field public static final deprecated int AXIS_GENERIC_10 = 41; // 0x29
+    field public static final deprecated int AXIS_GENERIC_11 = 42; // 0x2a
+    field public static final deprecated int AXIS_GENERIC_12 = 43; // 0x2b
+    field public static final deprecated int AXIS_GENERIC_13 = 44; // 0x2c
+    field public static final deprecated int AXIS_GENERIC_14 = 45; // 0x2d
+    field public static final deprecated int AXIS_GENERIC_15 = 46; // 0x2e
+    field public static final deprecated int AXIS_GENERIC_16 = 47; // 0x2f
+    field public static final deprecated int AXIS_GENERIC_2 = 33; // 0x21
+    field public static final deprecated int AXIS_GENERIC_3 = 34; // 0x22
+    field public static final deprecated int AXIS_GENERIC_4 = 35; // 0x23
+    field public static final deprecated int AXIS_GENERIC_5 = 36; // 0x24
+    field public static final deprecated int AXIS_GENERIC_6 = 37; // 0x25
+    field public static final deprecated int AXIS_GENERIC_7 = 38; // 0x26
+    field public static final deprecated int AXIS_GENERIC_8 = 39; // 0x27
+    field public static final deprecated int AXIS_GENERIC_9 = 40; // 0x28
+    field public static final deprecated int AXIS_HAT_X = 15; // 0xf
+    field public static final deprecated int AXIS_HAT_Y = 16; // 0x10
+    field public static final deprecated int AXIS_HSCROLL = 10; // 0xa
+    field public static final deprecated int AXIS_LTRIGGER = 17; // 0x11
+    field public static final deprecated int AXIS_ORIENTATION = 8; // 0x8
+    field public static final deprecated int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field public static final deprecated int AXIS_RTRIGGER = 18; // 0x12
+    field public static final deprecated int AXIS_RUDDER = 20; // 0x14
+    field public static final deprecated int AXIS_RX = 12; // 0xc
+    field public static final deprecated int AXIS_RY = 13; // 0xd
+    field public static final deprecated int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field public static final deprecated int AXIS_SIZE = 3; // 0x3
+    field public static final deprecated int AXIS_THROTTLE = 19; // 0x13
+    field public static final deprecated int AXIS_TILT = 25; // 0x19
+    field public static final deprecated int AXIS_TOOL_MAJOR = 6; // 0x6
+    field public static final deprecated int AXIS_TOOL_MINOR = 7; // 0x7
+    field public static final deprecated int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field public static final deprecated int AXIS_TOUCH_MINOR = 5; // 0x5
+    field public static final deprecated int AXIS_VSCROLL = 9; // 0x9
+    field public static final deprecated int AXIS_WHEEL = 21; // 0x15
+    field public static final deprecated int AXIS_X = 0; // 0x0
+    field public static final deprecated int AXIS_Y = 1; // 0x1
+    field public static final deprecated int AXIS_Z = 11; // 0xb
+    field public static final deprecated int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public abstract interface NestedScrollingChild {
+    method public abstract boolean dispatchNestedFling(float, float, boolean);
+    method public abstract boolean dispatchNestedPreFling(float, float);
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public abstract boolean hasNestedScrollingParent();
+    method public abstract boolean isNestedScrollingEnabled();
+    method public abstract void setNestedScrollingEnabled(boolean);
+    method public abstract boolean startNestedScroll(int);
+    method public abstract void stopNestedScroll();
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public void stopNestedScroll();
+  }
+
+  public abstract interface NestedScrollingParent {
+    method public abstract int getNestedScrollAxes();
+    method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public abstract void onStopNestedScroll(android.view.View);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public abstract interface OnApplyWindowInsetsListener {
+    method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+  }
+
+  public abstract class PagerAdapter {
+    ctor public PagerAdapter();
+    method public void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void destroyItem(android.view.View, int, java.lang.Object);
+    method public void finishUpdate(android.view.ViewGroup);
+    method public deprecated void finishUpdate(android.view.View);
+    method public abstract int getCount();
+    method public int getItemPosition(java.lang.Object);
+    method public java.lang.CharSequence getPageTitle(int);
+    method public float getPageWidth(int);
+    method public java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated java.lang.Object instantiateItem(android.view.View, int);
+    method public abstract boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public void notifyDataSetChanged();
+    method public void registerDataSetObserver(android.database.DataSetObserver);
+    method public void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public android.os.Parcelable saveState();
+    method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object);
+    method public void startUpdate(android.view.ViewGroup);
+    method public deprecated void startUpdate(android.view.View);
+    method public void unregisterDataSetObserver(android.database.DataSetObserver);
+    field public static final int POSITION_NONE = -2; // 0xfffffffe
+    field public static final int POSITION_UNCHANGED = -1; // 0xffffffff
+  }
+
+  public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip {
+    ctor public PagerTabStrip(android.content.Context);
+    ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet);
+    method public boolean getDrawFullUnderline();
+    method public int getTabIndicatorColor();
+    method public void setDrawFullUnderline(boolean);
+    method public void setTabIndicatorColor(int);
+    method public void setTabIndicatorColorResource(int);
+  }
+
+  public class PagerTitleStrip extends android.view.ViewGroup {
+    ctor public PagerTitleStrip(android.content.Context);
+    ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet);
+    method public int getTextSpacing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setGravity(int);
+    method public void setNonPrimaryAlpha(float);
+    method public void setTextColor(int);
+    method public void setTextSize(int, float);
+    method public void setTextSpacing(int);
+  }
+
+  public final class PointerIconCompat {
+    method public static android.support.v4.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method public static android.support.v4.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static android.support.v4.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static deprecated boolean isQuickScaleEnabled(java.lang.Object);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+    method public static deprecated void setQuickScaleEnabled(java.lang.Object, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+  }
+
+  public abstract interface ScrollingView {
+    method public abstract int computeHorizontalScrollExtent();
+    method public abstract int computeHorizontalScrollOffset();
+    method public abstract int computeHorizontalScrollRange();
+    method public abstract int computeVerticalScrollExtent();
+    method public abstract int computeVerticalScrollOffset();
+    method public abstract int computeVerticalScrollRange();
+  }
+
+  public abstract interface TintableBackgroundView {
+    method public abstract android.content.res.ColorStateList getSupportBackgroundTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode();
+    method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final deprecated class VelocityTrackerCompat {
+    method public static deprecated float getXVelocity(android.view.VelocityTracker, int);
+    method public static deprecated float getYVelocity(android.view.VelocityTracker, int);
+  }
+
+  public class ViewCompat {
+    ctor protected ViewCompat();
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View>, int);
+    method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method public static deprecated boolean canScrollHorizontally(android.view.View, int);
+    method public static deprecated boolean canScrollVertically(android.view.View, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method public static deprecated int combineMeasuredStates(int, int);
+    method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public static deprecated float getAlpha(android.view.View);
+    method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect getClipBounds(android.view.View);
+    method public static android.view.Display getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method public static deprecated int getLayerType(android.view.View);
+    method public static int getLayoutDirection(android.view.View);
+    method public static deprecated android.graphics.Matrix getMatrix(android.view.View);
+    method public static deprecated int getMeasuredHeightAndState(android.view.View);
+    method public static deprecated int getMeasuredState(android.view.View);
+    method public static deprecated int getMeasuredWidthAndState(android.view.View);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static deprecated int getOverScrollMode(android.view.View);
+    method public static int getPaddingEnd(android.view.View);
+    method public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent getParentForAccessibility(android.view.View);
+    method public static deprecated float getPivotX(android.view.View);
+    method public static deprecated float getPivotY(android.view.View);
+    method public static deprecated float getRotation(android.view.View);
+    method public static deprecated float getRotationX(android.view.View);
+    method public static deprecated float getRotationY(android.view.View);
+    method public static deprecated float getScaleX(android.view.View);
+    method public static deprecated float getScaleY(android.view.View);
+    method public static int getScrollIndicators(android.view.View);
+    method public static java.lang.String getTransitionName(android.view.View);
+    method public static deprecated float getTranslationX(android.view.View);
+    method public static deprecated float getTranslationY(android.view.View);
+    method public static float getTranslationZ(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method public static deprecated float getX(android.view.View);
+    method public static deprecated float getY(android.view.View);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method public static deprecated boolean isOpaque(android.view.View);
+    method public static boolean isPaddingRelative(android.view.View);
+    method public static deprecated void jumpDrawablesToCurrentState(android.view.View);
+    method public static android.view.View keyboardNavigationClusterSearch(android.view.View, android.view.View, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static deprecated void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public static deprecated void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, java.lang.Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long);
+    method public static void requestApplyInsets(android.view.View);
+    method public static deprecated int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method public static deprecated void setActivated(android.view.View, boolean);
+    method public static deprecated void setAlpha(android.view.View, float);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode);
+    method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect);
+    method public static void setElevation(android.view.View, float);
+    method public static deprecated void setFitsSystemWindows(android.view.View, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint);
+    method public static deprecated void setLayerType(android.view.View, int, android.graphics.Paint);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener);
+    method public static deprecated void setOverScrollMode(android.view.View, int);
+    method public static void setPaddingRelative(android.view.View, int, int, int, int);
+    method public static deprecated void setPivotX(android.view.View, float);
+    method public static deprecated void setPivotY(android.view.View, float);
+    method public static void setPointerIcon(android.view.View, android.support.v4.view.PointerIconCompat);
+    method public static deprecated void setRotation(android.view.View, float);
+    method public static deprecated void setRotationX(android.view.View, float);
+    method public static deprecated void setRotationY(android.view.View, float);
+    method public static deprecated void setSaveFromParentEnabled(android.view.View, boolean);
+    method public static deprecated void setScaleX(android.view.View, float);
+    method public static deprecated void setScaleY(android.view.View, float);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+    method public static void setTransitionName(android.view.View, java.lang.String);
+    method public static deprecated void setTranslationX(android.view.View, float);
+    method public static deprecated void setTranslationY(android.view.View, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static deprecated void setX(android.view.View, float);
+    method public static deprecated void setY(android.view.View, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final deprecated int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field public static final deprecated int LAYER_TYPE_NONE = 0; // 0x0
+    field public static final deprecated int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field public static final deprecated int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field public static final deprecated int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field public static final deprecated int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field public static final deprecated int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final deprecated int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field public static final deprecated int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field public static final deprecated int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+  }
+
+  public final deprecated class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method public static deprecated boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method public static deprecated void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public class ViewPager extends android.view.ViewGroup {
+    ctor public ViewPager(android.content.Context);
+    ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+    method public void addOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public boolean arrowScroll(int);
+    method public boolean beginFakeDrag();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public void clearOnPageChangeListeners();
+    method public void endFakeDrag();
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fakeDragBy(float);
+    method public android.support.v4.view.PagerAdapter getAdapter();
+    method public int getCurrentItem();
+    method public int getOffscreenPageLimit();
+    method public int getPageMargin();
+    method public boolean isFakeDragging();
+    method protected void onLayout(boolean, int, int, int, int);
+    method protected void onPageScrolled(int, float, int);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void removeOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setAdapter(android.support.v4.view.PagerAdapter);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(int);
+    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setPageMargin(int);
+    method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
+    method public void setPageMarginDrawable(int);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer, int);
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewPager.DecorView implements java.lang.annotation.Annotation {
+  }
+
+  public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams {
+    ctor public ViewPager.LayoutParams();
+    ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public int gravity;
+    field public boolean isDecor;
+  }
+
+  public static abstract interface ViewPager.OnAdapterChangeListener {
+    method public abstract void onAdapterChanged(android.support.v4.view.ViewPager, android.support.v4.view.PagerAdapter, android.support.v4.view.PagerAdapter);
+  }
+
+  public static abstract interface ViewPager.OnPageChangeListener {
+    method public abstract void onPageScrollStateChanged(int);
+    method public abstract void onPageScrolled(int, float, int);
+    method public abstract void onPageSelected(int);
+  }
+
+  public static abstract interface ViewPager.PageTransformer {
+    method public abstract void transformPage(android.view.View, float);
+  }
+
+  public static class ViewPager.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public ViewPager.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR;
+  }
+
+  public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public ViewPager.SimpleOnPageChangeListener();
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static deprecated boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public long getStartDelay();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener);
+    method public void start();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat x(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat y(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat z(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public abstract interface ViewPropertyAnimatorListener {
+    method public abstract void onAnimationCancel(android.view.View);
+    method public abstract void onAnimationEnd(android.view.View);
+    method public abstract void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public abstract interface ViewPropertyAnimatorUpdateListener {
+    method public abstract void onAnimationUpdate(android.view.View);
+  }
+
+  public final class WindowCompat {
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(android.support.v4.view.WindowInsetsCompat);
+    method public android.support.v4.view.WindowInsetsCompat consumeStableInsets();
+    method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public int getStableInsetBottom();
+    method public int getStableInsetLeft();
+    method public int getStableInsetRight();
+    method public int getStableInsetTop();
+    method public int getSystemWindowInsetBottom();
+    method public int getSystemWindowInsetLeft();
+    method public int getSystemWindowInsetRight();
+    method public int getSystemWindowInsetTop();
+    method public boolean hasInsets();
+    method public boolean hasStableInsets();
+    method public boolean hasSystemWindowInsets();
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+  }
+
+}
+
+package android.support.v4.view.accessibility {
+
+  public final class AccessibilityEventCompat {
+    method public static deprecated void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent);
+    method public int getAction(android.view.accessibility.AccessibilityEvent);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int);
+    method public static deprecated int getRecordCount(android.view.accessibility.AccessibilityEvent);
+    method public void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+    method public void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field public static final deprecated int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field public static final deprecated int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field public static final deprecated int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final deprecated int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field public static final deprecated int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method public static deprecated boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  public static abstract deprecated interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method public abstract deprecated void onAccessibilityStateChanged(boolean);
+  }
+
+  public static abstract deprecated class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static abstract interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public abstract void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor public deprecated AccessibilityNodeInfoCompat(java.lang.Object);
+    method public void addAction(int);
+    method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public void addChild(android.view.View);
+    method public void addChild(android.view.View, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList();
+    method public int getActions();
+    method public void getBoundsInParent(android.graphics.Rect);
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getDrawingOrder();
+    method public java.lang.CharSequence getError();
+    method public android.os.Bundle getExtras();
+    method public deprecated java.lang.Object getInfo();
+    method public int getInputType();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public java.lang.CharSequence getPackageName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo();
+    method public java.lang.CharSequence getRoleDescription();
+    method public java.lang.CharSequence getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore();
+    method public java.lang.String getViewIdResourceName();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isVisibleToUser();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public boolean removeChild(android.view.View);
+    method public boolean removeChild(android.view.View, int);
+    method public void setAccessibilityFocused(boolean);
+    method public void setBoundsInParent(android.graphics.Rect);
+    method public void setBoundsInScreen(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(java.lang.Object);
+    method public void setCollectionItemInfo(java.lang.Object);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(java.lang.CharSequence);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View);
+    method public void setLabelFor(android.view.View, int);
+    method public void setLabeledBy(android.view.View);
+    method public void setLabeledBy(android.view.View, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setParent(android.view.View, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat);
+    method public void setRoleDescription(java.lang.CharSequence);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setSource(android.view.View, int);
+    method public void setText(java.lang.CharSequence);
+    method public void setTextSelection(int, int);
+    method public void setTraversalAfter(android.view.View);
+    method public void setTraversalAfter(android.view.View, int);
+    method public void setTraversalBefore(android.view.View);
+    method public void setTraversalBefore(android.view.View, int);
+    method public void setViewIdResourceName(java.lang.String);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo unwrap();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence);
+    method public int getId();
+    method public java.lang.CharSequence getLabel();
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CONTEXT_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_DOWN;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_LEFT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_RIGHT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_TO_POSITION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_UP;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_PROGRESS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_ON_SCREEN;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public boolean isHeading();
+    method public boolean isSelected();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(java.lang.Object);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public java.lang.Object getProvider();
+    method public boolean performAction(int, int, android.os.Bundle);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor public deprecated AccessibilityRecordCompat(java.lang.Object);
+    method public deprecated int getAddedCount();
+    method public deprecated java.lang.CharSequence getBeforeText();
+    method public deprecated java.lang.CharSequence getClassName();
+    method public deprecated java.lang.CharSequence getContentDescription();
+    method public deprecated int getCurrentItemIndex();
+    method public deprecated int getFromIndex();
+    method public deprecated java.lang.Object getImpl();
+    method public deprecated int getItemCount();
+    method public deprecated int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+    method public deprecated int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+    method public deprecated android.os.Parcelable getParcelableData();
+    method public deprecated int getRemovedCount();
+    method public deprecated int getScrollX();
+    method public deprecated int getScrollY();
+    method public deprecated android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource();
+    method public deprecated java.util.List<java.lang.CharSequence> getText();
+    method public deprecated int getToIndex();
+    method public deprecated int getWindowId();
+    method public deprecated boolean isChecked();
+    method public deprecated boolean isEnabled();
+    method public deprecated boolean isFullScreen();
+    method public deprecated boolean isPassword();
+    method public deprecated boolean isScrollable();
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain();
+    method public deprecated void recycle();
+    method public deprecated void setAddedCount(int);
+    method public deprecated void setBeforeText(java.lang.CharSequence);
+    method public deprecated void setChecked(boolean);
+    method public deprecated void setClassName(java.lang.CharSequence);
+    method public deprecated void setContentDescription(java.lang.CharSequence);
+    method public deprecated void setCurrentItemIndex(int);
+    method public deprecated void setEnabled(boolean);
+    method public deprecated void setFromIndex(int);
+    method public deprecated void setFullScreen(boolean);
+    method public deprecated void setItemCount(int);
+    method public deprecated void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setParcelableData(android.os.Parcelable);
+    method public deprecated void setPassword(boolean);
+    method public deprecated void setRemovedCount(int);
+    method public deprecated void setScrollX(int);
+    method public deprecated void setScrollY(int);
+    method public deprecated void setScrollable(boolean);
+    method public deprecated void setSource(android.view.View);
+    method public deprecated void setSource(android.view.View, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View, int);
+    method public deprecated void setToIndex(int);
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot();
+    method public java.lang.CharSequence getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package android.support.v4.view.animation {
+
+  public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutLinearInInterpolator();
+  }
+
+  public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutSlowInInterpolator();
+  }
+
+  public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public LinearOutSlowInInterpolator();
+  }
+
+   abstract class LookupTableInterpolator implements android.view.animation.Interpolator {
+    ctor public LookupTableInterpolator(float[]);
+    method public float getInterpolation(float);
+  }
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package android.support.v4.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+    method public abstract void scrollTargetBy(int, int);
+    method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int);
+    method public android.support.v4.widget.AutoScrollHelper setEdgeType(int);
+    method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode);
+  }
+
+  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 void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public abstract class CursorAdapter extends android.widget.BaseAdapter {
+    ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, int);
+    method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursor(android.database.Cursor);
+    method public java.lang.CharSequence convertToString(android.database.Cursor);
+    method public int getCount();
+    method public android.database.Cursor getCursor();
+    method public android.widget.Filter getFilter();
+    method public android.widget.FilterQueryProvider getFilterQueryProvider();
+    method public java.lang.Object getItem(int);
+    method public long getItemId(int);
+    method public android.view.View getView(int, android.view.View, android.view.ViewGroup);
+    method protected deprecated void init(android.content.Context, android.database.Cursor, boolean);
+    method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method protected void onContentChanged();
+    method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence);
+    method public void setFilterQueryProvider(android.widget.FilterQueryProvider);
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+    field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1
+    field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2
+  }
+
+  public class DrawerLayout extends android.view.ViewGroup {
+    ctor public DrawerLayout(android.content.Context);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void closeDrawer(android.view.View);
+    method public void closeDrawer(android.view.View, boolean);
+    method public void closeDrawer(int);
+    method public void closeDrawer(int, boolean);
+    method public void closeDrawers();
+    method public float getDrawerElevation();
+    method public int getDrawerLockMode(int);
+    method public int getDrawerLockMode(android.view.View);
+    method public java.lang.CharSequence getDrawerTitle(int);
+    method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable();
+    method public boolean isDrawerOpen(android.view.View);
+    method public boolean isDrawerOpen(int);
+    method public boolean isDrawerVisible(android.view.View);
+    method public boolean isDrawerVisible(int);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void openDrawer(android.view.View);
+    method public void openDrawer(android.view.View, boolean);
+    method public void openDrawer(int);
+    method public void openDrawer(int, boolean);
+    method public void removeDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerElevation(float);
+    method public deprecated void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerLockMode(int);
+    method public void setDrawerLockMode(int, int);
+    method public void setDrawerLockMode(int, android.view.View);
+    method public void setDrawerShadow(android.graphics.drawable.Drawable, int);
+    method public void setDrawerShadow(int, int);
+    method public void setDrawerTitle(int, java.lang.CharSequence);
+    method public void setScrimColor(int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackground(int);
+    method public void setStatusBarBackgroundColor(int);
+    field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1
+    field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2
+    field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3
+    field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract interface DrawerLayout.DrawerListener {
+    method public abstract void onDrawerClosed(android.view.View);
+    method public abstract void onDrawerOpened(android.view.View);
+    method public abstract void onDrawerSlide(android.view.View, float);
+    method public abstract void onDrawerStateChanged(int);
+  }
+
+  public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout.LayoutParams(int, int);
+    ctor public DrawerLayout.LayoutParams(int, int, int);
+    ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public int gravity;
+  }
+
+  protected static class DrawerLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public DrawerLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public DrawerLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR;
+  }
+
+  public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public DrawerLayout.SimpleDrawerListener();
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+  }
+
+  public final class EdgeEffectCompat {
+    ctor public deprecated EdgeEffectCompat(android.content.Context);
+    method public deprecated boolean draw(android.graphics.Canvas);
+    method public deprecated void finish();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean onAbsorb(int);
+    method public deprecated boolean onPull(float);
+    method public deprecated boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method public deprecated boolean onRelease();
+    method public deprecated void setSize(int, int);
+  }
+
+  public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public ExploreByTouchHelper(android.view.View);
+    method public final boolean clearKeyboardFocusForVirtualView(int);
+    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public final int getAccessibilityFocusedVirtualViewId();
+    method public deprecated int getFocusedVirtualView();
+    method public final int getKeyboardFocusedVirtualViewId();
+    method protected abstract int getVirtualViewAt(float, float);
+    method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
+    method public final void invalidateRoot();
+    method public final void invalidateVirtualView(int);
+    method public final void invalidateVirtualView(int, int);
+    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
+    method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
+    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+    method public final boolean requestKeyboardFocusForVirtualView(int);
+    method public final boolean sendEventForVirtualView(int, int);
+    field public static final int HOST_ID = -1; // 0xffffffff
+    field public static final int INVALID_ID = -2147483648; // 0x80000000
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode);
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static deprecated android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View);
+    method public static android.view.View.OnTouchListener createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int);
+    method public boolean arrowScroll(int);
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollTo(int, int);
+  }
+
+  public static abstract interface NestedScrollView.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter {
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor);
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean);
+    ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int);
+    method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public void setDropDownViewResource(int);
+    method public void setViewResource(int);
+  }
+
+  public final deprecated class ScrollerCompat {
+    method public deprecated void abortAnimation();
+    method public deprecated boolean computeScrollOffset();
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context);
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator);
+    method public deprecated void fling(int, int, int, int, int, int, int, int);
+    method public deprecated void fling(int, int, int, int, int, int, int, int, int, int);
+    method public deprecated float getCurrVelocity();
+    method public deprecated int getCurrX();
+    method public deprecated int getCurrY();
+    method public deprecated int getFinalX();
+    method public deprecated int getFinalY();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean isOverScrolled();
+    method public deprecated void notifyHorizontalEdgeReached(int, int, int);
+    method public deprecated void notifyVerticalEdgeReached(int, int, int);
+    method public deprecated boolean springBack(int, int, int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int, int);
+  }
+
+  public final deprecated class SearchViewCompat {
+    method public static deprecated java.lang.CharSequence getQuery(android.view.View);
+    method public static deprecated boolean isIconified(android.view.View);
+    method public static deprecated boolean isQueryRefinementEnabled(android.view.View);
+    method public static deprecated boolean isSubmitButtonEnabled(android.view.View);
+    method public static deprecated android.view.View newSearchView(android.content.Context);
+    method public static deprecated void setIconified(android.view.View, boolean);
+    method public static deprecated void setImeOptions(android.view.View, int);
+    method public static deprecated void setInputType(android.view.View, int);
+    method public static deprecated void setMaxWidth(android.view.View, int);
+    method public static deprecated void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListener);
+    method public static deprecated void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListener);
+    method public static deprecated void setQuery(android.view.View, java.lang.CharSequence, boolean);
+    method public static deprecated void setQueryHint(android.view.View, java.lang.CharSequence);
+    method public static deprecated void setQueryRefinementEnabled(android.view.View, boolean);
+    method public static deprecated void setSearchableInfo(android.view.View, android.content.ComponentName);
+    method public static deprecated void setSubmitButtonEnabled(android.view.View, boolean);
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnCloseListenerCompat implements android.support.v4.widget.SearchViewCompat.OnCloseListener {
+    ctor public SearchViewCompat.OnCloseListenerCompat();
+    method public boolean onClose();
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnQueryTextListenerCompat implements android.support.v4.widget.SearchViewCompat.OnQueryTextListener {
+    ctor public SearchViewCompat.OnQueryTextListenerCompat();
+    method public boolean onQueryTextChange(java.lang.String);
+    method public boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter {
+    ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]);
+    ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int);
+    method public void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]);
+    method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter();
+    method public int getStringConversionColumn();
+    method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder();
+    method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter);
+    method public void setStringConversionColumn(int);
+    method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder);
+    method public void setViewImage(android.widget.ImageView, java.lang.String);
+    method public void setViewText(android.widget.TextView, java.lang.String);
+  }
+
+  public static abstract interface SimpleCursorAdapter.CursorToStringConverter {
+    method public abstract java.lang.CharSequence convertToString(android.database.Cursor);
+  }
+
+  public static abstract interface SimpleCursorAdapter.ViewBinder {
+    method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int);
+  }
+
+  public class SlidingPaneLayout extends android.view.ViewGroup {
+    ctor public SlidingPaneLayout(android.content.Context);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public deprecated boolean canSlide();
+    method public boolean closePane();
+    method public int getCoveredFadeColor();
+    method public int getParallaxDistance();
+    method public int getSliderFadeColor();
+    method public boolean isOpen();
+    method public boolean isSlideable();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public boolean openPane();
+    method public void setCoveredFadeColor(int);
+    method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener);
+    method public void setParallaxDistance(int);
+    method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableLeft(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableRight(android.graphics.drawable.Drawable);
+    method public deprecated void setShadowResource(int);
+    method public void setShadowResourceLeft(int);
+    method public void setShadowResourceRight(int);
+    method public void setSliderFadeColor(int);
+    method public deprecated void smoothSlideClosed();
+    method public deprecated void smoothSlideOpen();
+  }
+
+  public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public SlidingPaneLayout.LayoutParams();
+    ctor public SlidingPaneLayout.LayoutParams(int, int);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public float weight;
+  }
+
+  public static abstract interface SlidingPaneLayout.PanelSlideListener {
+    method public abstract void onPanelClosed(android.view.View);
+    method public abstract void onPanelOpened(android.view.View);
+    method public abstract void onPanelSlide(android.view.View, float);
+  }
+
+  public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener {
+    ctor public SlidingPaneLayout.SimplePanelSlideListener();
+    method public void onPanelClosed(android.view.View);
+    method public void onPanelOpened(android.view.View);
+    method public void onPanelSlide(android.view.View, float);
+  }
+
+  public class Space extends android.view.View {
+    ctor public Space(android.content.Context, android.util.AttributeSet, int);
+    ctor public Space(android.content.Context, android.util.AttributeSet);
+    ctor public Space(android.content.Context);
+  }
+
+  public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent {
+    ctor public SwipeRefreshLayout(android.content.Context);
+    ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet);
+    method public boolean canChildScrollUp();
+    method public int getProgressCircleDiameter();
+    method public int getProgressViewEndOffset();
+    method public int getProgressViewStartOffset();
+    method public boolean isRefreshing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public deprecated void setColorScheme(int...);
+    method public void setColorSchemeColors(int...);
+    method public void setColorSchemeResources(int...);
+    method public void setDistanceToTriggerSync(int);
+    method public void setOnChildScrollUpCallback(android.support.v4.widget.SwipeRefreshLayout.OnChildScrollUpCallback);
+    method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener);
+    method public deprecated void setProgressBackgroundColor(int);
+    method public void setProgressBackgroundColorSchemeColor(int);
+    method public void setProgressBackgroundColorSchemeResource(int);
+    method public void setProgressViewEndTarget(boolean, int);
+    method public void setProgressViewOffset(boolean, int, int);
+    method public void setRefreshing(boolean);
+    method public void setSize(int);
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+    field protected int mFrom;
+    field protected int mOriginalOffsetTop;
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnChildScrollUpCallback {
+    method public abstract boolean canChildScrollUp(android.support.v4.widget.SwipeRefreshLayout, android.view.View);
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnRefreshListener {
+    method public abstract void onRefresh();
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
+    method public static void setTextAppearance(android.widget.TextView, int);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public abstract interface TintableCompoundButton {
+    method public abstract android.content.res.ColorStateList getSupportButtonTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode();
+    method public abstract void setSupportButtonTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public class ViewDragHelper {
+    method public void abort();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int, int);
+    method public void cancel();
+    method public void captureChildView(android.view.View, int);
+    method public boolean checkTouchSlop(int);
+    method public boolean checkTouchSlop(int, int);
+    method public boolean continueSettling(boolean);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback);
+    method public android.view.View findTopChildUnder(int, int);
+    method public void flingCapturedView(int, int, int, int);
+    method public int getActivePointerId();
+    method public android.view.View getCapturedView();
+    method public int getEdgeSize();
+    method public float getMinVelocity();
+    method public int getTouchSlop();
+    method public int getViewDragState();
+    method public boolean isCapturedViewUnder(int, int);
+    method public boolean isEdgeTouched(int);
+    method public boolean isEdgeTouched(int, int);
+    method public boolean isPointerDown(int);
+    method public boolean isViewUnder(android.view.View, int, int);
+    method public void processTouchEvent(android.view.MotionEvent);
+    method public void setEdgeTrackingEnabled(int);
+    method public void setMinVelocity(float);
+    method public boolean settleCapturedViewAt(int, int);
+    method public boolean shouldInterceptTouchEvent(android.view.MotionEvent);
+    method public boolean smoothSlideViewTo(android.view.View, int, int);
+    field public static final int DIRECTION_ALL = 3; // 0x3
+    field public static final int DIRECTION_HORIZONTAL = 1; // 0x1
+    field public static final int DIRECTION_VERTICAL = 2; // 0x2
+    field public static final int EDGE_ALL = 15; // 0xf
+    field public static final int EDGE_BOTTOM = 8; // 0x8
+    field public static final int EDGE_LEFT = 1; // 0x1
+    field public static final int EDGE_RIGHT = 2; // 0x2
+    field public static final int EDGE_TOP = 4; // 0x4
+    field public static final int INVALID_POINTER = -1; // 0xffffffff
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewDragHelper.Callback {
+    ctor public ViewDragHelper.Callback();
+    method public int clampViewPositionHorizontal(android.view.View, int, int);
+    method public int clampViewPositionVertical(android.view.View, int, int);
+    method public int getOrderedChildIndex(int);
+    method public int getViewHorizontalDragRange(android.view.View);
+    method public int getViewVerticalDragRange(android.view.View);
+    method public void onEdgeDragStarted(int, int);
+    method public boolean onEdgeLock(int);
+    method public void onEdgeTouched(int, int);
+    method public void onViewCaptured(android.view.View, int);
+    method public void onViewDragStateChanged(int);
+    method public void onViewPositionChanged(android.view.View, int, int, int, int);
+    method public void onViewReleased(android.view.View, float, float);
+    method public abstract boolean tryCaptureView(android.view.View, int);
+  }
+
+}
+
+package android.support.v7.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, boolean);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int, boolean);
+    method public abstract android.view.View getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method public abstract deprecated int getNavigationItemCount();
+    method public abstract deprecated int getNavigationMode();
+    method public abstract deprecated int getSelectedNavigationIndex();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getSelectedTab();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getTabAt(int);
+    method public abstract deprecated int getTabCount();
+    method public android.content.Context getThemedContext();
+    method public abstract java.lang.CharSequence getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab newTab();
+    method public abstract deprecated void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void removeTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void removeTabAt(int);
+    method public abstract deprecated void selectTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setCustomView(android.view.View, android.support.v7.app.ActionBar.LayoutParams);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(java.lang.CharSequence);
+    method public void setHomeActionContentDescription(int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.support.v7.app.ActionBar.OnNavigationListener);
+    method public abstract void setLogo(int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setNavigationMode(int);
+    method public abstract deprecated void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
+    field public int gravity;
+  }
+
+  public static abstract interface ActionBar.OnMenuVisibilityListener {
+    method public abstract void onMenuVisibilityChanged(boolean);
+  }
+
+  public static abstract deprecated interface ActionBar.OnNavigationListener {
+    method public abstract boolean onNavigationItemSelected(int, long);
+  }
+
+  public static abstract deprecated class ActionBar.Tab {
+    ctor public ActionBar.Tab();
+    method public abstract java.lang.CharSequence getContentDescription();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.graphics.drawable.Drawable getIcon();
+    method public abstract int getPosition();
+    method public abstract java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract void select();
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(android.view.View);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(android.graphics.drawable.Drawable);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setTabListener(android.support.v7.app.ActionBar.TabListener);
+    method public abstract android.support.v7.app.ActionBar.Tab setTag(java.lang.Object);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static abstract deprecated interface ActionBar.TabListener {
+    method public abstract void onTabReselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabSelected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+  }
+
+  public deprecated class ActionBarActivity extends android.support.v7.app.AppCompatActivity {
+    ctor public ActionBarActivity();
+  }
+
+  public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
+    method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
+    method public void syncState();
+  }
+
+  public static abstract interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.content.Context getActionBarThemedContext();
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract boolean isNavigationVisible();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends android.support.v7.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.widget.Button getButton(int);
+    method public android.widget.ListView getListView();
+    method public void setButton(int, java.lang.CharSequence, android.os.Message);
+    method public void setButton(int, java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public void setCustomTitle(android.view.View);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIconAttribute(int);
+    method public void setMessage(java.lang.CharSequence);
+    method public void setView(android.view.View);
+    method public void setView(android.view.View, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, int);
+    method public android.support.v7.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public android.support.v7.app.AlertDialog.Builder setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setCancelable(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setCursor(android.database.Cursor, android.content.DialogInterface.OnClickListener, java.lang.String);
+    method public android.support.v7.app.AlertDialog.Builder setCustomTitle(android.view.View);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(int);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.support.v7.app.AlertDialog.Builder setIconAttribute(int);
+    method public deprecated android.support.v7.app.AlertDialog.Builder setInverseBackgroundForced(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setItems(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setItems(java.lang.CharSequence[], android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(int);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(int, boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(java.lang.CharSequence[], boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(android.database.Cursor, java.lang.String, java.lang.String, android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(int, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.database.Cursor, int, java.lang.String, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(java.lang.CharSequence[], int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.widget.ListAdapter, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(int);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setView(int);
+    method public android.support.v7.app.AlertDialog.Builder setView(android.view.View);
+    method public android.support.v7.app.AlertDialog show();
+  }
+
+  public class AppCompatActivity extends android.support.v4.app.FragmentActivity implements android.support.v7.app.ActionBarDrawerToggle.DelegateProvider android.support.v7.app.AppCompatCallback android.support.v4.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public android.content.Intent getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method public void onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public deprecated void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public deprecated void setSupportProgress(int);
+    method public deprecated void setSupportProgressBarIndeterminate(boolean);
+    method public deprecated void setSupportProgressBarIndeterminateVisibility(boolean);
+    method public deprecated void setSupportProgressBarVisibility(boolean);
+    method public android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public abstract interface AppCompatCallback {
+    method public abstract void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public abstract void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public abstract android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract boolean applyDayNight();
+    method public abstract boolean checkActionBarFocusKey(android.view.KeyEvent);
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
+    method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T findViewById(int);
+    method public static int getDefaultNightMode();
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration);
+    method public abstract void onCreate(android.os.Bundle);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View);
+    method public abstract void setContentView(int);
+    method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context);
+    ctor public AppCompatDialog(android.content.Context, int);
+    ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class MediaRouteActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.app.MediaRouteButton getMediaRouteButton();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public android.support.v7.app.MediaRouteButton onCreateMediaRouteButton();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<android.support.v7.media.MediaRouter.RouteInfo>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends android.support.v7.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSession();
+    method public android.support.v7.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View onCreateMediaControlView(android.os.Bundle);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static android.support.v7.app.MediaRouteDialogFactory getDefault();
+    method public android.support.v7.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends android.support.v4.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public android.support.v7.media.MediaRouter getMediaRouter();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.media.MediaRouter.Callback onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class NotificationCompat extends android.support.v4.app.NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+  }
+
+  public static class NotificationCompat.Builder extends android.support.v4.app.NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v7.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v7.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+  }
+
+}
+
+package android.support.v7.graphics {
+
+  public final class Palette {
+    method public static android.support.v7.graphics.Palette.Builder from(android.graphics.Bitmap);
+    method public static android.support.v7.graphics.Palette from(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap, int);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, int, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public int getColorForTarget(android.support.v7.graphics.Target, int);
+    method public int getDarkMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
+    method public int getDarkVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
+    method public int getDominantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDominantSwatch();
+    method public int getLightMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
+    method public int getLightVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
+    method public int getMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
+    method public android.support.v7.graphics.Palette.Swatch getSwatchForTarget(android.support.v7.graphics.Target);
+    method public java.util.List<android.support.v7.graphics.Palette.Swatch> getSwatches();
+    method public java.util.List<android.support.v7.graphics.Target> getTargets();
+    method public int getVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
+  }
+
+  public static final class Palette.Builder {
+    ctor public Palette.Builder(android.graphics.Bitmap);
+    ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
+    method public android.support.v7.graphics.Palette.Builder addTarget(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Palette.Builder clearFilters();
+    method public android.support.v7.graphics.Palette.Builder clearRegion();
+    method public android.support.v7.graphics.Palette.Builder clearTargets();
+    method public android.support.v7.graphics.Palette generate();
+    method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
+    method public android.support.v7.graphics.Palette.Builder resizeBitmapArea(int);
+    method public deprecated android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
+    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
+  }
+
+  public static abstract interface Palette.Filter {
+    method public abstract boolean isAllowed(int, float[]);
+  }
+
+  public static abstract interface Palette.PaletteAsyncListener {
+    method public abstract void onGenerated(android.support.v7.graphics.Palette);
+  }
+
+  public static final class Palette.Swatch {
+    ctor public Palette.Swatch(int, int);
+    method public int getBodyTextColor();
+    method public float[] getHsl();
+    method public int getPopulation();
+    method public int getRgb();
+    method public int getTitleTextColor();
+  }
+
+  public final class Target {
+    method public float getLightnessWeight();
+    method public float getMaximumLightness();
+    method public float getMaximumSaturation();
+    method public float getMinimumLightness();
+    method public float getMinimumSaturation();
+    method public float getPopulationWeight();
+    method public float getSaturationWeight();
+    method public float getTargetLightness();
+    method public float getTargetSaturation();
+    method public boolean isExclusive();
+    field public static final android.support.v7.graphics.Target DARK_MUTED;
+    field public static final android.support.v7.graphics.Target DARK_VIBRANT;
+    field public static final android.support.v7.graphics.Target LIGHT_MUTED;
+    field public static final android.support.v7.graphics.Target LIGHT_VIBRANT;
+    field public static final android.support.v7.graphics.Target MUTED;
+    field public static final android.support.v7.graphics.Target VIBRANT;
+  }
+
+  public static final class Target.Builder {
+    ctor public Target.Builder();
+    ctor public Target.Builder(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Target build();
+    method public android.support.v7.graphics.Target.Builder setExclusive(boolean);
+    method public android.support.v7.graphics.Target.Builder setLightnessWeight(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setPopulationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setSaturationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setTargetLightness(float);
+    method public android.support.v7.graphics.Target.Builder setTargetSaturation(float);
+  }
+
+}
+
+package android.support.v7.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package android.support.v7.media {
+
+  public final class MediaControlIntent {
+    field public static final java.lang.String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final java.lang.String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final java.lang.String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final java.lang.String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final java.lang.String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final java.lang.String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final java.lang.String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final java.lang.String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final java.lang.String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final java.lang.String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final java.lang.String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final java.lang.String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final java.lang.String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final java.lang.String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final java.lang.String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final java.lang.String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final java.lang.String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final java.lang.String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final java.lang.String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final java.lang.String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final java.lang.String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final java.lang.String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final java.lang.String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final java.lang.String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final java.lang.String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final java.lang.String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final java.lang.String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final java.lang.String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaItemStatus fromBundle(android.os.Bundle);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final java.lang.String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final java.lang.String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(int);
+    ctor public MediaItemStatus.Builder(android.support.v7.media.MediaItemStatus);
+    method public android.support.v7.media.MediaItemStatus build();
+    method public android.support.v7.media.MediaItemStatus.Builder setContentDuration(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setContentPosition(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public android.support.v7.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static android.support.v7.media.MediaRouteDescriptor fromBundle(android.os.Bundle);
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public deprecated boolean isConnecting();
+    method public boolean isEnabled();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(java.lang.String, java.lang.String);
+    ctor public MediaRouteDescriptor.Builder(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter>);
+    method public android.support.v7.media.MediaRouteDescriptor build();
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method public deprecated android.support.v7.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDescription(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setId(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setName(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(android.support.v7.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteDiscoveryRequest fromBundle(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final android.support.v7.media.MediaRouteProviderDescriptor getDescriptor();
+    method public final android.support.v7.media.MediaRouteDiscoveryRequest getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final android.support.v7.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public android.support.v7.media.MediaRouteProvider.RouteController onCreateRouteController(java.lang.String);
+    method public void onDiscoveryRequestChanged(android.support.v7.media.MediaRouteDiscoveryRequest);
+    method public final void setCallback(android.support.v7.media.MediaRouteProvider.Callback);
+    method public final void setDescriptor(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public final void setDiscoveryRequest(android.support.v7.media.MediaRouteDiscoveryRequest);
+  }
+
+  public static abstract class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(android.support.v7.media.MediaRouteProvider, android.support.v7.media.MediaRouteProviderDescriptor);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+  }
+
+  public static abstract class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteProviderDescriptor fromBundle(android.os.Bundle);
+    method public java.util.List<android.support.v7.media.MediaRouteDescriptor> getRoutes();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoute(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<android.support.v7.media.MediaRouteDescriptor>);
+    method public android.support.v7.media.MediaRouteProviderDescriptor build();
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public android.support.v7.media.MediaRouteProvider getMediaRouteProvider();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.support.v7.media.MediaRouteProvider onCreateMediaRouteProvider();
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(android.support.v7.media.MediaRouteSelector);
+    method public static android.support.v7.media.MediaRouteSelector fromBundle(android.os.Bundle);
+    method public java.util.List<java.lang.String> getControlCategories();
+    method public boolean hasControlCategory(java.lang.String);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter>);
+    field public static final android.support.v7.media.MediaRouteSelector EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String>);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategory(java.lang.String);
+    method public android.support.v7.media.MediaRouteSelector.Builder addSelector(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback);
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
+    method public void addProvider(android.support.v7.media.MediaRouteProvider);
+    method public void addRemoteControlClient(java.lang.Object);
+    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
+    method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
+    method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
+    method public java.util.List<android.support.v7.media.MediaRouter.ProviderInfo> getProviders();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+    method public android.support.v7.media.MediaRouter.RouteInfo getSelectedRoute();
+    method public boolean isRouteAvailable(android.support.v7.media.MediaRouteSelector, int);
+    method public void removeCallback(android.support.v7.media.MediaRouter.Callback);
+    method public void removeProvider(android.support.v7.media.MediaRouteProvider);
+    method public void removeRemoteControlClient(java.lang.Object);
+    method public void selectRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void setMediaSession(java.lang.Object);
+    method public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat);
+    method public void unselect(int);
+    method public android.support.v7.media.MediaRouter.RouteInfo updateSelectedRoute(android.support.v7.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+  }
+
+  public static abstract class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onResult(android.os.Bundle);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+    method public android.support.v7.media.MediaRouteProvider getProviderInstance();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
+    method public android.support.v7.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public boolean isBluetooth();
+    method public boolean isConnecting();
+    method public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method public boolean isSelected();
+    method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
+    method public void requestSetVolume(int);
+    method public void requestUpdateVolume(int);
+    method public void select();
+    method public void sendControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public boolean supportsControlAction(java.lang.String, java.lang.String);
+    method public boolean supportsControlCategory(java.lang.String);
+    method public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaSessionStatus fromBundle(android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(int);
+    ctor public MediaSessionStatus.Builder(android.support.v7.media.MediaSessionStatus);
+    method public android.support.v7.media.MediaSessionStatus build();
+    method public android.support.v7.media.MediaSessionStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public android.support.v7.media.MediaSessionStatus.Builder setSessionState(int);
+    method public android.support.v7.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void enqueue(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public java.lang.String getSessionId();
+    method public void getSessionStatus(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void getStatus(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void play(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void release();
+    method public void remove(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void resume(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void seek(java.lang.String, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void sendMessage(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void setOnMessageReceivedListener(android.support.v7.media.RemotePlaybackClient.OnMessageReceivedListener);
+    method public void setSessionId(java.lang.String);
+    method public void setStatusCallback(android.support.v7.media.RemotePlaybackClient.StatusCallback);
+    method public void startSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void stop(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+  }
+
+  public static abstract class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(java.lang.String, int, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.ItemActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+  }
+
+  public static abstract interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public abstract void onMessageReceived(java.lang.String, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.SessionActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+  public static abstract class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+    method public void onSessionChanged(java.lang.String);
+    method public void onSessionStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+}
+
+package android.support.v7.preference {
+
+  public class CheckBoxPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
+    ctor public CheckBoxPreference(android.content.Context);
+  }
+
+  public abstract class DialogPreference extends android.support.v7.preference.Preference {
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DialogPreference(android.content.Context);
+    method public android.graphics.drawable.Drawable getDialogIcon();
+    method public int getDialogLayoutResource();
+    method public java.lang.CharSequence getDialogMessage();
+    method public java.lang.CharSequence getDialogTitle();
+    method public java.lang.CharSequence getNegativeButtonText();
+    method public java.lang.CharSequence getPositiveButtonText();
+    method public void setDialogIcon(android.graphics.drawable.Drawable);
+    method public void setDialogIcon(int);
+    method public void setDialogLayoutResource(int);
+    method public void setDialogMessage(java.lang.CharSequence);
+    method public void setDialogMessage(int);
+    method public void setDialogTitle(java.lang.CharSequence);
+    method public void setDialogTitle(int);
+    method public void setNegativeButtonText(java.lang.CharSequence);
+    method public void setNegativeButtonText(int);
+    method public void setPositiveButtonText(java.lang.CharSequence);
+    method public void setPositiveButtonText(int);
+  }
+
+  public static abstract interface DialogPreference.TargetFragment {
+    method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+  }
+
+  public class DropDownPreference extends android.support.v7.preference.ListPreference {
+    ctor public DropDownPreference(android.content.Context);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
+    method protected android.widget.ArrayAdapter createAdapter();
+  }
+
+  public class EditTextPreference extends android.support.v7.preference.DialogPreference {
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
+    ctor public EditTextPreference(android.content.Context);
+    method public java.lang.String getText();
+    method public void setText(java.lang.String);
+  }
+
+  public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public EditTextPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.EditTextPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public ListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence getEntry();
+    method public java.lang.CharSequence[] getEntryValues();
+    method public java.lang.String getValue();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValue(java.lang.String);
+    method public void setValueIndex(int);
+  }
+
+  public class ListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public ListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.ListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public MultiSelectListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class Preference implements java.lang.Comparable {
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet);
+    ctor public Preference(android.content.Context);
+    method public boolean callChangeListener(java.lang.Object);
+    method public int compareTo(android.support.v7.preference.Preference);
+    method protected android.support.v7.preference.Preference findPreferenceInHierarchy(java.lang.String);
+    method public android.content.Context getContext();
+    method public java.lang.String getDependency();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getFragment();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.String getKey();
+    method public final int getLayoutResource();
+    method public android.support.v7.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
+    method public android.support.v7.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
+    method public int getOrder();
+    method public android.support.v7.preference.PreferenceGroup getParent();
+    method protected boolean getPersistedBoolean(boolean);
+    method protected float getPersistedFloat(float);
+    method protected int getPersistedInt(int);
+    method protected long getPersistedLong(long);
+    method protected java.lang.String getPersistedString(java.lang.String);
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public boolean getShouldDisableView();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public final int getWidgetLayoutResource();
+    method public boolean hasKey();
+    method public boolean isEnabled();
+    method public boolean isIconSpaceReserved();
+    method public boolean isPersistent();
+    method public boolean isSelectable();
+    method public boolean isSingleLineTitle();
+    method public final boolean isVisible();
+    method protected void notifyChanged();
+    method public void notifyDependencyChange(boolean);
+    method protected void notifyHierarchyChanged();
+    method public void onAttached();
+    method protected void onAttachedToHierarchy(android.support.v7.preference.PreferenceManager);
+    method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
+    method protected void onClick();
+    method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
+    method public void onDetached();
+    method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onParentChanged(android.support.v7.preference.Preference, boolean);
+    method protected void onPrepareForRemoval();
+    method protected void onRestoreInstanceState(android.os.Parcelable);
+    method protected android.os.Parcelable onSaveInstanceState();
+    method protected void onSetInitialValue(boolean, java.lang.Object);
+    method public android.os.Bundle peekExtras();
+    method protected boolean persistBoolean(boolean);
+    method protected boolean persistFloat(float);
+    method protected boolean persistInt(int);
+    method protected boolean persistLong(long);
+    method protected boolean persistString(java.lang.String);
+    method public void restoreHierarchyState(android.os.Bundle);
+    method public void saveHierarchyState(android.os.Bundle);
+    method public void setDefaultValue(java.lang.Object);
+    method public void setDependency(java.lang.String);
+    method public void setEnabled(boolean);
+    method public void setFragment(java.lang.String);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIcon(int);
+    method public void setIconSpaceReserved(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setKey(java.lang.String);
+    method public void setLayoutResource(int);
+    method public void setOnPreferenceChangeListener(android.support.v7.preference.Preference.OnPreferenceChangeListener);
+    method public void setOnPreferenceClickListener(android.support.v7.preference.Preference.OnPreferenceClickListener);
+    method public void setOrder(int);
+    method public void setPersistent(boolean);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public void setSelectable(boolean);
+    method public void setShouldDisableView(boolean);
+    method public void setSingleLineTitle(boolean);
+    method public void setSummary(java.lang.CharSequence);
+    method public void setSummary(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitle(int);
+    method public void setViewId(int);
+    method public final void setVisible(boolean);
+    method public void setWidgetLayoutResource(int);
+    method public boolean shouldDisableDependents();
+    method protected boolean shouldPersist();
+    field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
+  }
+
+  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+    ctor public Preference.BaseSavedState(android.os.Parcel);
+    ctor public Preference.BaseSavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.preference.Preference.BaseSavedState> CREATOR;
+  }
+
+  public static abstract interface Preference.OnPreferenceChangeListener {
+    method public abstract boolean onPreferenceChange(android.support.v7.preference.Preference, java.lang.Object);
+  }
+
+  public static abstract interface Preference.OnPreferenceClickListener {
+    method public abstract boolean onPreferenceClick(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceCategory extends android.support.v7.preference.PreferenceGroup {
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
+    ctor public PreferenceCategory(android.content.Context);
+  }
+
+  public abstract class PreferenceDataStore {
+    ctor public PreferenceDataStore();
+    method public boolean getBoolean(java.lang.String, boolean);
+    method public float getFloat(java.lang.String, float);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public void putBoolean(java.lang.String, boolean);
+    method public void putFloat(java.lang.String, float);
+    method public void putInt(java.lang.String, int);
+    method public void putLong(java.lang.String, long);
+    method public void putString(java.lang.String, java.lang.String);
+  }
+
+  public abstract class PreferenceDialogFragmentCompat extends android.support.v4.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragmentCompat();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragmentCompat extends android.support.v4.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragmentCompat();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public abstract class PreferenceGroup extends android.support.v7.preference.Preference {
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
+    method public void addItemFromInflater(android.support.v7.preference.Preference);
+    method public boolean addPreference(android.support.v7.preference.Preference);
+    method protected void dispatchRestoreInstanceState(android.os.Bundle);
+    method protected void dispatchSaveInstanceState(android.os.Bundle);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.support.v7.preference.Preference getPreference(int);
+    method public int getPreferenceCount();
+    method protected boolean isOnSameScreenAsChildren();
+    method public boolean isOrderingAsAdded();
+    method protected boolean onPrepareAddPreference(android.support.v7.preference.Preference);
+    method public void removeAll();
+    method public boolean removePreference(android.support.v7.preference.Preference);
+    method public void setOrderingAsAdded(boolean);
+  }
+
+  public static abstract interface PreferenceGroup.PreferencePositionCallback {
+    method public abstract int getPreferenceAdapterPosition(java.lang.String);
+    method public abstract int getPreferenceAdapterPosition(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceManager {
+    method public android.support.v7.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.content.Context getContext();
+    method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
+    method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
+    method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
+    method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
+    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public int getSharedPreferencesMode();
+    method public java.lang.String getSharedPreferencesName();
+    method public boolean isStorageDefault();
+    method public boolean isStorageDeviceProtected();
+    method public static void setDefaultValues(android.content.Context, int, boolean);
+    method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
+    method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
+    method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
+    method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
+    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
+    method public void setSharedPreferencesMode(int);
+    method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceProtected();
+    method public void showDialog(android.support.v7.preference.Preference);
+    field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
+  }
+
+  public static abstract interface PreferenceManager.OnDisplayPreferenceDialogListener {
+    method public abstract void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceManager.OnNavigateToScreenListener {
+    method public abstract void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+  }
+
+  public static abstract interface PreferenceManager.OnPreferenceTreeClickListener {
+    method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+  }
+
+  public static abstract class PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.PreferenceComparisonCallback();
+    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
+    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
+    method public void setShouldUseGeneratedIds(boolean);
+    method public boolean shouldUseGeneratedIds();
+  }
+
+  public class PreferenceViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
+    method public android.view.View findViewById(int);
+    method public boolean isDividerAllowedAbove();
+    method public boolean isDividerAllowedBelow();
+    method public void setDividerAllowedAbove(boolean);
+    method public void setDividerAllowedBelow(boolean);
+  }
+
+  public class SeekBarPreference extends android.support.v7.preference.Preference {
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SeekBarPreference(android.content.Context);
+    method public int getMax();
+    method public int getMin();
+    method public final int getSeekBarIncrement();
+    method public int getValue();
+    method public boolean isAdjustable();
+    method public void setAdjustable(boolean);
+    method public final void setMax(int);
+    method public void setMin(int);
+    method public final void setSeekBarIncrement(int);
+    method public void setValue(int);
+  }
+
+  public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreferenceCompat(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+  public abstract class TwoStatePreference extends android.support.v7.preference.Preference {
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
+    ctor public TwoStatePreference(android.content.Context);
+    method public boolean getDisableDependentsState();
+    method public java.lang.CharSequence getSummaryOff();
+    method public java.lang.CharSequence getSummaryOn();
+    method public boolean isChecked();
+    method public void setChecked(boolean);
+    method public void setDisableDependentsState(boolean);
+    method public void setSummaryOff(java.lang.CharSequence);
+    method public void setSummaryOff(int);
+    method public void setSummaryOn(java.lang.CharSequence);
+    method public void setSummaryOn(int);
+    method protected void syncSummaryView(android.support.v7.preference.PreferenceViewHolder);
+    field protected boolean mChecked;
+  }
+
+}
+
+package android.support.v7.util {
+
+  public class AsyncListUtil<T> {
+    ctor public AsyncListUtil(java.lang.Class<T>, int, android.support.v7.util.AsyncListUtil.DataCallback<T>, android.support.v7.util.AsyncListUtil.ViewCallback);
+    method public T getItem(int);
+    method public int getItemCount();
+    method public void onRangeChanged();
+    method public void refresh();
+  }
+
+  public static abstract class AsyncListUtil.DataCallback<T> {
+    ctor public AsyncListUtil.DataCallback();
+    method public abstract void fillData(T[], int, int);
+    method public int getMaxCachedTiles();
+    method public void recycleData(T[], int);
+    method public abstract int refreshData();
+  }
+
+  public static abstract class AsyncListUtil.ViewCallback {
+    ctor public AsyncListUtil.ViewCallback();
+    method public void extendRangeInto(int[], int[], int);
+    method public abstract void getItemRangeInto(int[]);
+    method public abstract void onDataRefresh();
+    method public abstract void onItemLoaded(int);
+    field public static final int HINT_SCROLL_ASC = 2; // 0x2
+    field public static final int HINT_SCROLL_DESC = 1; // 0x1
+    field public static final int HINT_SCROLL_NONE = 0; // 0x0
+  }
+
+  public class BatchingListUpdateCallback implements android.support.v7.util.ListUpdateCallback {
+    ctor public BatchingListUpdateCallback(android.support.v7.util.ListUpdateCallback);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int, java.lang.Object);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public class DiffUtil {
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback);
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback, boolean);
+  }
+
+  public static abstract class DiffUtil.Callback {
+    ctor public DiffUtil.Callback();
+    method public abstract boolean areContentsTheSame(int, int);
+    method public abstract boolean areItemsTheSame(int, int);
+    method public java.lang.Object getChangePayload(int, int);
+    method public abstract int getNewListSize();
+    method public abstract int getOldListSize();
+  }
+
+  public static class DiffUtil.DiffResult {
+    method public void dispatchUpdatesTo(android.support.v7.widget.RecyclerView.Adapter);
+    method public void dispatchUpdatesTo(android.support.v7.util.ListUpdateCallback);
+  }
+
+  public abstract interface ListUpdateCallback {
+    method public abstract void onChanged(int, int, java.lang.Object);
+    method public abstract void onInserted(int, int);
+    method public abstract void onMoved(int, int);
+    method public abstract void onRemoved(int, int);
+  }
+
+  public class SortedList<T> {
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>);
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>, int);
+    method public int add(T);
+    method public void addAll(T[], boolean);
+    method public void addAll(T...);
+    method public void addAll(java.util.Collection<T>);
+    method public void beginBatchedUpdates();
+    method public void clear();
+    method public void endBatchedUpdates();
+    method public T get(int) throws java.lang.IndexOutOfBoundsException;
+    method public int indexOf(T);
+    method public void recalculatePositionOfItemAt(int);
+    method public boolean remove(T);
+    method public T removeItemAt(int);
+    method public int size();
+    method public void updateItemAt(int, T);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class SortedList.BatchedCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedList.BatchedCallback(android.support.v7.util.SortedList.Callback<T2>);
+    method public boolean areContentsTheSame(T2, T2);
+    method public boolean areItemsTheSame(T2, T2);
+    method public int compare(T2, T2);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public static abstract class SortedList.Callback<T2> implements java.util.Comparator android.support.v7.util.ListUpdateCallback {
+    ctor public SortedList.Callback();
+    method public abstract boolean areContentsTheSame(T2, T2);
+    method public abstract boolean areItemsTheSame(T2, T2);
+    method public abstract int compare(T2, T2);
+    method public abstract void onChanged(int, int);
+    method public void onChanged(int, int, java.lang.Object);
+  }
+
+}
+
+package android.support.v7.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.view.Menu getMenu();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public void setTag(java.lang.Object);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static abstract interface ActionMode.Callback {
+    method public abstract boolean onActionItemClicked(android.support.v7.view.ActionMode, android.view.MenuItem);
+    method public abstract boolean onCreateActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+    method public abstract void onDestroyActionMode(android.support.v7.view.ActionMode);
+    method public abstract boolean onPrepareActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+  }
+
+  public abstract interface CollapsibleActionView {
+    method public abstract void onActionViewCollapsed();
+    method public abstract void onActionViewExpanded();
+  }
+
+}
+
+package android.support.v7.widget {
+
+  public class ActionMenuView extends android.support.v7.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
+    method public android.view.Menu getMenu();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(android.support.v7.widget.ActionMenuView.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends android.support.v7.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(android.support.v7.widget.ActionMenuView.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static abstract interface ActionMenuView.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setSupportAllCaps(boolean);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class CardView extends android.widget.FrameLayout {
+    ctor public CardView(android.content.Context);
+    ctor public CardView(android.content.Context, android.util.AttributeSet);
+    ctor public CardView(android.content.Context, android.util.AttributeSet, int);
+    method public android.content.res.ColorStateList getCardBackgroundColor();
+    method public float getCardElevation();
+    method public int getContentPaddingBottom();
+    method public int getContentPaddingLeft();
+    method public int getContentPaddingRight();
+    method public int getContentPaddingTop();
+    method public float getMaxCardElevation();
+    method public boolean getPreventCornerOverlap();
+    method public float getRadius();
+    method public boolean getUseCompatPadding();
+    method public void setCardBackgroundColor(int);
+    method public void setCardBackgroundColor(android.content.res.ColorStateList);
+    method public void setCardElevation(float);
+    method public void setContentPadding(int, int, int, int);
+    method public void setMaxCardElevation(float);
+    method public void setPreventCornerOverlap(boolean);
+    method public void setRadius(float);
+    method public void setUseCompatPadding(boolean);
+  }
+
+  public class DefaultItemAnimator extends android.support.v7.widget.SimpleItemAnimator {
+    ctor public DefaultItemAnimator();
+    method public boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimations();
+    method public boolean isRunning();
+    method public void runPendingAnimations();
+  }
+
+  public class DividerItemDecoration extends android.support.v7.widget.RecyclerView.ItemDecoration {
+    ctor public DividerItemDecoration(android.content.Context, int);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setOrientation(int);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayout(android.content.Context);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final android.support.v7.widget.GridLayout.Alignment BASELINE;
+    field public static final android.support.v7.widget.GridLayout.Alignment BOTTOM;
+    field public static final android.support.v7.widget.GridLayout.Alignment CENTER;
+    field public static final android.support.v7.widget.GridLayout.Alignment END;
+    field public static final android.support.v7.widget.GridLayout.Alignment FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final android.support.v7.widget.GridLayout.Alignment LEFT;
+    field public static final android.support.v7.widget.GridLayout.Alignment RIGHT;
+    field public static final android.support.v7.widget.GridLayout.Alignment START;
+    field public static final android.support.v7.widget.GridLayout.Alignment TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.Spec, android.support.v7.widget.GridLayout.Spec);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    method public void setGravity(int);
+    field public android.support.v7.widget.GridLayout.Spec columnSpec;
+    field public android.support.v7.widget.GridLayout.Spec rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public android.support.v7.widget.GridLayout.Alignment getAbsoluteAlignment(boolean);
+  }
+
+  public class GridLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public GridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public GridLayoutManager(android.content.Context, int);
+    ctor public GridLayoutManager(android.content.Context, int, int, boolean);
+    method public int getSpanCount();
+    method public android.support.v7.widget.GridLayoutManager.SpanSizeLookup getSpanSizeLookup();
+    method public void setSpanCount(int);
+    method public void setSpanSizeLookup(android.support.v7.widget.GridLayoutManager.SpanSizeLookup);
+    field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
+  }
+
+  public static final class GridLayoutManager.DefaultSpanSizeLookup extends android.support.v7.widget.GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.DefaultSpanSizeLookup();
+    method public int getSpanSize(int);
+  }
+
+  public static class GridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public GridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayoutManager.LayoutParams(int, int);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getSpanIndex();
+    method public int getSpanSize();
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.SpanSizeLookup();
+    method public int getSpanGroupIndex(int, int);
+    method public int getSpanIndex(int, int);
+    method public abstract int getSpanSize(int);
+    method public void invalidateSpanIndexCache();
+    method public boolean isSpanIndexCacheEnabled();
+    method public void setSpanIndexCacheEnabled(boolean);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet, int);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.support.v7.widget.LinearLayoutCompat.LayoutParams);
+    field public int gravity;
+    field public float weight;
+  }
+
+  public class LinearLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.helper.ItemTouchHelper.ViewDropHandler android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public LinearLayoutManager(android.content.Context);
+    ctor public LinearLayoutManager(android.content.Context, int, boolean);
+    ctor public LinearLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int findFirstCompletelyVisibleItemPosition();
+    method public int findFirstVisibleItemPosition();
+    method public int findLastCompletelyVisibleItemPosition();
+    method public int findLastVisibleItemPosition();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
+    method public int getInitialPrefetchItemCount();
+    method public int getOrientation();
+    method public boolean getRecycleChildrenOnDetach();
+    method public boolean getReverseLayout();
+    method public boolean getStackFromEnd();
+    method protected boolean isLayoutRTL();
+    method public boolean isSmoothScrollbarEnabled();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setOrientation(int);
+    method public void setRecycleChildrenOnDetach(boolean);
+    method public void setReverseLayout(boolean);
+    method public void setSmoothScrollbarEnabled(boolean);
+    method public void setStackFromEnd(boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  protected static class LinearLayoutManager.LayoutChunkResult {
+    ctor protected LinearLayoutManager.LayoutChunkResult();
+    field public int mConsumed;
+    field public boolean mFinished;
+    field public boolean mFocusable;
+    field public boolean mIgnoreConsumed;
+  }
+
+  public class LinearSmoothScroller extends android.support.v7.widget.RecyclerView.SmoothScroller {
+    ctor public LinearSmoothScroller(android.content.Context);
+    method public int calculateDtToFit(int, int, int, int, int);
+    method public int calculateDxToMakeVisible(android.view.View, int);
+    method public int calculateDyToMakeVisible(android.view.View, int);
+    method protected float calculateSpeedPerPixel(android.util.DisplayMetrics);
+    method protected int calculateTimeForDeceleration(int);
+    method protected int calculateTimeForScrolling(int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method protected int getHorizontalSnapPreference();
+    method protected int getVerticalSnapPreference();
+    method protected void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void onStart();
+    method protected void onStop();
+    method protected void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void updateActionForInterimTarget(android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    field public static final int SNAP_TO_ANY = 0; // 0x0
+    field public static final int SNAP_TO_END = 1; // 0x1
+    field public static final int SNAP_TO_START = -1; // 0xffffffff
+    field protected final android.view.animation.DecelerateInterpolator mDecelerateInterpolator;
+    field protected int mInterimTargetDx;
+    field protected int mInterimTargetDy;
+    field protected final android.view.animation.LinearInterpolator mLinearInterpolator;
+    field protected android.graphics.PointF mTargetVector;
+  }
+
+  public class LinearSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public LinearSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int, int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener createDragToOpenListener(android.view.View);
+    method public void dismiss();
+    method public android.view.View getAnchorView();
+    method public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable getBackground();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView getListView();
+    method public int getPromptPosition();
+    method public java.lang.Object getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter);
+    method public void setAnchorView(android.view.View);
+    method public void setAnimationStyle(int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public abstract class OrientationHelper {
+    method public static android.support.v7.widget.OrientationHelper createHorizontalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public static android.support.v7.widget.OrientationHelper createOrientationHelper(android.support.v7.widget.RecyclerView.LayoutManager, int);
+    method public static android.support.v7.widget.OrientationHelper createVerticalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int getDecoratedEnd(android.view.View);
+    method public abstract int getDecoratedMeasurement(android.view.View);
+    method public abstract int getDecoratedMeasurementInOther(android.view.View);
+    method public abstract int getDecoratedStart(android.view.View);
+    method public abstract int getEnd();
+    method public abstract int getEndAfterPadding();
+    method public abstract int getEndPadding();
+    method public abstract int getMode();
+    method public abstract int getModeInOther();
+    method public abstract int getStartAfterPadding();
+    method public abstract int getTotalSpace();
+    method public int getTotalSpaceChange();
+    method public abstract int getTransformedEndWithDecoration(android.view.View);
+    method public abstract int getTransformedStartWithDecoration(android.view.View);
+    method public abstract void offsetChild(android.view.View, int);
+    method public abstract void offsetChildren(int);
+    method public void onLayoutComplete();
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+    field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
+  }
+
+  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public PagerSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, int, int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(android.support.v7.widget.PopupMenu.OnDismissListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.PopupMenu.OnMenuItemClickListener);
+    method public void show();
+  }
+
+  public static abstract interface PopupMenu.OnDismissListener {
+    method public abstract void onDismiss(android.support.v7.widget.PopupMenu);
+  }
+
+  public static abstract interface PopupMenu.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class RecyclerView extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.ScrollingView {
+    ctor public RecyclerView(android.content.Context);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void addOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void addOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void addOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void clearOnChildAttachStateChangeListeners();
+    method public void clearOnScrollListeners();
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+    method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
+    method public android.view.View findChildViewUnder(float, float);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
+    method public deprecated android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForPosition(int);
+    method public boolean fling(int, int);
+    method public android.support.v7.widget.RecyclerView.Adapter getAdapter();
+    method public int getChildAdapterPosition(android.view.View);
+    method public long getChildItemId(android.view.View);
+    method public int getChildLayoutPosition(android.view.View);
+    method public deprecated int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder getChildViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate();
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator getItemAnimator();
+    method public android.support.v7.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getMaxFlingVelocity();
+    method public int getMinFlingVelocity();
+    method public android.support.v7.widget.RecyclerView.OnFlingListener getOnFlingListener();
+    method public boolean getPreserveFocusAfterLayout();
+    method public android.support.v7.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
+    method public int getScrollState();
+    method public boolean hasFixedSize();
+    method public boolean hasPendingAdapterUpdates();
+    method public void invalidateItemDecorations();
+    method public boolean isAnimating();
+    method public boolean isComputingLayout();
+    method public boolean isLayoutFrozen();
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onChildAttachedToWindow(android.view.View);
+    method public void onChildDetachedFromWindow(android.view.View);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onScrollStateChanged(int);
+    method public void onScrolled(int, int);
+    method public void removeItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void removeOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void removeOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void removeOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void scrollToPosition(int);
+    method public void setAccessibilityDelegateCompat(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+    method public void setAdapter(android.support.v7.widget.RecyclerView.Adapter);
+    method public void setChildDrawingOrderCallback(android.support.v7.widget.RecyclerView.ChildDrawingOrderCallback);
+    method public void setHasFixedSize(boolean);
+    method public void setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator);
+    method public void setItemViewCacheSize(int);
+    method public void setLayoutFrozen(boolean);
+    method public void setLayoutManager(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public void setOnFlingListener(android.support.v7.widget.RecyclerView.OnFlingListener);
+    method public deprecated void setOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void setPreserveFocusAfterLayout(boolean);
+    method public void setRecycledViewPool(android.support.v7.widget.RecyclerView.RecycledViewPool);
+    method public void setRecyclerListener(android.support.v7.widget.RecyclerView.RecyclerListener);
+    method public void setScrollingTouchSlop(int);
+    method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
+    method public void smoothScrollBy(int, int);
+    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
+    method public void smoothScrollToPosition(int);
+    method public void stopScroll();
+    method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_TYPE = -1; // 0xffffffff
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+    field public static final int NO_POSITION = -1; // 0xffffffff
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+    field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
+    field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder> {
+    ctor public RecyclerView.Adapter();
+    method public final void bindViewHolder(VH, int);
+    method public final VH createViewHolder(android.view.ViewGroup, int);
+    method public abstract int getItemCount();
+    method public long getItemId(int);
+    method public int getItemViewType(int);
+    method public final boolean hasObservers();
+    method public final boolean hasStableIds();
+    method public final void notifyDataSetChanged();
+    method public final void notifyItemChanged(int);
+    method public final void notifyItemChanged(int, java.lang.Object);
+    method public final void notifyItemInserted(int);
+    method public final void notifyItemMoved(int, int);
+    method public final void notifyItemRangeChanged(int, int);
+    method public final void notifyItemRangeChanged(int, int, java.lang.Object);
+    method public final void notifyItemRangeInserted(int, int);
+    method public final void notifyItemRangeRemoved(int, int);
+    method public final void notifyItemRemoved(int);
+    method public void onAttachedToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public abstract void onBindViewHolder(VH, int);
+    method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object>);
+    method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDetachedFromRecyclerView(android.support.v7.widget.RecyclerView);
+    method public boolean onFailedToRecycleView(VH);
+    method public void onViewAttachedToWindow(VH);
+    method public void onViewDetachedFromWindow(VH);
+    method public void onViewRecycled(VH);
+    method public void registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+    method public void setHasStableIds(boolean);
+    method public void unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+  }
+
+  public static abstract class RecyclerView.AdapterDataObserver {
+    ctor public RecyclerView.AdapterDataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeChanged(int, int, java.lang.Object);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeMoved(int, int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public static abstract interface RecyclerView.ChildDrawingOrderCallback {
+    method public abstract int onGetChildDrawingOrder(int, int);
+  }
+
+  public static abstract class RecyclerView.ItemAnimator {
+    ctor public RecyclerView.ItemAnimator();
+    method public abstract boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object>);
+    method public final void dispatchAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationsFinished();
+    method public abstract void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract void endAnimations();
+    method public long getAddDuration();
+    method public long getChangeDuration();
+    method public long getMoveDuration();
+    method public long getRemoveDuration();
+    method public abstract boolean isRunning();
+    method public final boolean isRunning(android.support.v7.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
+    method public void onAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder, int, java.util.List<java.lang.Object>);
+    method public abstract void runPendingAnimations();
+    method public void setAddDuration(long);
+    method public void setChangeDuration(long);
+    method public void setMoveDuration(long);
+    method public void setRemoveDuration(long);
+    field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
+    field public static final int FLAG_CHANGED = 2; // 0x2
+    field public static final int FLAG_INVALIDATED = 4; // 0x4
+    field public static final int FLAG_MOVED = 2048; // 0x800
+    field public static final int FLAG_REMOVED = 8; // 0x8
+  }
+
+  public static abstract class RecyclerView.ItemAnimator.AdapterChanges implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
+    method public abstract void onAnimationsFinished();
+  }
+
+  public static class RecyclerView.ItemAnimator.ItemHolderInfo {
+    ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public int bottom;
+    field public int changeFlags;
+    field public int left;
+    field public int right;
+    field public int top;
+  }
+
+  public static abstract class RecyclerView.ItemDecoration {
+    ctor public RecyclerView.ItemDecoration();
+    method public deprecated void getItemOffsets(android.graphics.Rect, int, android.support.v7.widget.RecyclerView);
+    method public void getItemOffsets(android.graphics.Rect, android.view.View, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+    method public void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+  }
+
+  public static abstract class RecyclerView.LayoutManager {
+    ctor public RecyclerView.LayoutManager();
+    method public void addDisappearingView(android.view.View);
+    method public void addDisappearingView(android.view.View, int);
+    method public void addView(android.view.View);
+    method public void addView(android.view.View, int);
+    method public void assertInLayoutOrScroll(java.lang.String);
+    method public void assertNotInLayoutOrScroll(java.lang.String);
+    method public void attachView(android.view.View, int, android.support.v7.widget.RecyclerView.LayoutParams);
+    method public void attachView(android.view.View, int);
+    method public void attachView(android.view.View);
+    method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
+    method public boolean canScrollHorizontally();
+    method public boolean canScrollVertically();
+    method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public static int chooseSize(int, int, int);
+    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public void detachAndScrapAttachedViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachView(android.view.View);
+    method public void detachViewAt(int);
+    method public void endAnimation(android.view.View);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.view.View findViewByPosition(int);
+    method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.content.Context, android.util.AttributeSet);
+    method public int getBaseline();
+    method public int getBottomDecorationHeight(android.view.View);
+    method public android.view.View getChildAt(int);
+    method public int getChildCount();
+    method public static deprecated int getChildMeasureSpec(int, int, int, boolean);
+    method public static int getChildMeasureSpec(int, int, int, int, boolean);
+    method public boolean getClipToPadding();
+    method public int getColumnCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getDecoratedBottom(android.view.View);
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public int getDecoratedLeft(android.view.View);
+    method public int getDecoratedMeasuredHeight(android.view.View);
+    method public int getDecoratedMeasuredWidth(android.view.View);
+    method public int getDecoratedRight(android.view.View);
+    method public int getDecoratedTop(android.view.View);
+    method public android.view.View getFocusedChild();
+    method public int getHeight();
+    method public int getHeightMode();
+    method public int getItemCount();
+    method public int getItemViewType(android.view.View);
+    method public int getLayoutDirection();
+    method public int getLeftDecorationWidth(android.view.View);
+    method public int getMinimumHeight();
+    method public int getMinimumWidth();
+    method public int getPaddingBottom();
+    method public int getPaddingEnd();
+    method public int getPaddingLeft();
+    method public int getPaddingRight();
+    method public int getPaddingStart();
+    method public int getPaddingTop();
+    method public int getPosition(android.view.View);
+    method public static android.support.v7.widget.RecyclerView.LayoutManager.Properties getProperties(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getRightDecorationWidth(android.view.View);
+    method public int getRowCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getSelectionModeForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getTopDecorationHeight(android.view.View);
+    method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
+    method public int getWidth();
+    method public int getWidthMode();
+    method public boolean hasFocus();
+    method public void ignoreView(android.view.View);
+    method public boolean isAttachedToWindow();
+    method public boolean isAutoMeasureEnabled();
+    method public boolean isFocused();
+    method public final boolean isItemPrefetchEnabled();
+    method public boolean isLayoutHierarchical(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public boolean isMeasurementCacheEnabled();
+    method public boolean isSmoothScrolling();
+    method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
+    method public void layoutDecorated(android.view.View, int, int, int, int);
+    method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
+    method public void measureChild(android.view.View, int, int);
+    method public void measureChildWithMargins(android.view.View, int, int);
+    method public void moveView(int, int);
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onAdapterChanged(android.support.v7.widget.RecyclerView.Adapter, android.support.v7.widget.RecyclerView.Adapter);
+    method public boolean onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int);
+    method public void onAttachedToWindow(android.support.v7.widget.RecyclerView);
+    method public deprecated void onDetachedFromWindow(android.support.v7.widget.RecyclerView);
+    method public void onDetachedFromWindow(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.Recycler);
+    method public android.view.View onFocusSearchFailed(android.view.View, int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityEvent(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onInitializeAccessibilityNodeInfoForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public android.view.View onInterceptFocusSearch(android.view.View, int);
+    method public void onItemsAdded(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsChanged(android.support.v7.widget.RecyclerView);
+    method public void onItemsMoved(android.support.v7.widget.RecyclerView, int, int, int);
+    method public void onItemsRemoved(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int, java.lang.Object);
+    method public void onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onLayoutCompleted(android.support.v7.widget.RecyclerView.State);
+    method public void onMeasure(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, int);
+    method public deprecated boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.view.View, android.view.View);
+    method public boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, android.view.View, android.view.View);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void onScrollStateChanged(int);
+    method public boolean performAccessibilityAction(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, android.os.Bundle);
+    method public boolean performAccessibilityActionForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, int, android.os.Bundle);
+    method public void postOnAnimation(java.lang.Runnable);
+    method public void removeAllViews();
+    method public void removeAndRecycleAllViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public boolean removeCallbacks(java.lang.Runnable);
+    method public void removeDetachedView(android.view.View);
+    method public void removeView(android.view.View);
+    method public void removeViewAt(int);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
+    method public void requestLayout();
+    method public void requestSimpleAnimationsInNextLayout();
+    method public int scrollHorizontallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void scrollToPosition(int);
+    method public int scrollVerticallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void setAutoMeasureEnabled(boolean);
+    method public final void setItemPrefetchEnabled(boolean);
+    method public void setMeasuredDimension(android.graphics.Rect, int, int);
+    method public void setMeasuredDimension(int, int);
+    method public void setMeasurementCacheEnabled(boolean);
+    method public void smoothScrollToPosition(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, int);
+    method public void startSmoothScroll(android.support.v7.widget.RecyclerView.SmoothScroller);
+    method public void stopIgnoringView(android.view.View);
+    method public boolean supportsPredictiveItemAnimations();
+  }
+
+  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+    method public abstract void addPosition(int, int);
+  }
+
+  public static class RecyclerView.LayoutManager.Properties {
+    ctor public RecyclerView.LayoutManager.Properties();
+    field public int orientation;
+    field public boolean reverseLayout;
+    field public int spanCount;
+    field public boolean stackFromEnd;
+  }
+
+  public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public RecyclerView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView.LayoutParams(int, int);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public RecyclerView.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getViewAdapterPosition();
+    method public int getViewLayoutPosition();
+    method public deprecated int getViewPosition();
+    method public boolean isItemChanged();
+    method public boolean isItemRemoved();
+    method public boolean isViewInvalid();
+    method public boolean viewNeedsUpdate();
+  }
+
+  public static abstract interface RecyclerView.OnChildAttachStateChangeListener {
+    method public abstract void onChildViewAttachedToWindow(android.view.View);
+    method public abstract void onChildViewDetachedFromWindow(android.view.View);
+  }
+
+  public static abstract class RecyclerView.OnFlingListener {
+    ctor public RecyclerView.OnFlingListener();
+    method public abstract boolean onFling(int, int);
+  }
+
+  public static abstract interface RecyclerView.OnItemTouchListener {
+    method public abstract boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public abstract void onRequestDisallowInterceptTouchEvent(boolean);
+    method public abstract void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.OnScrollListener {
+    ctor public RecyclerView.OnScrollListener();
+    method public void onScrollStateChanged(android.support.v7.widget.RecyclerView, int);
+    method public void onScrolled(android.support.v7.widget.RecyclerView, int, int);
+  }
+
+  public static class RecyclerView.RecycledViewPool {
+    ctor public RecyclerView.RecycledViewPool();
+    method public void clear();
+    method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
+    method public int getRecycledViewCount(int);
+    method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setMaxRecycledViews(int, int);
+  }
+
+  public final class RecyclerView.Recycler {
+    ctor public RecyclerView.Recycler();
+    method public void bindViewToPosition(android.view.View, int);
+    method public void clear();
+    method public int convertPreLayoutPositionToPostLayout(int);
+    method public java.util.List<android.support.v7.widget.RecyclerView.ViewHolder> getScrapList();
+    method public android.view.View getViewForPosition(int);
+    method public void recycleView(android.view.View);
+    method public void setViewCacheSize(int);
+  }
+
+  public static abstract interface RecyclerView.RecyclerListener {
+    method public abstract void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+  public static class RecyclerView.SimpleOnItemTouchListener implements android.support.v7.widget.RecyclerView.OnItemTouchListener {
+    ctor public RecyclerView.SimpleOnItemTouchListener();
+    method public boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public void onRequestDisallowInterceptTouchEvent(boolean);
+    method public void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.SmoothScroller {
+    ctor public RecyclerView.SmoothScroller();
+    method public android.view.View findViewByPosition(int);
+    method public int getChildCount();
+    method public int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getTargetPosition();
+    method public deprecated void instantScrollToPosition(int);
+    method public boolean isPendingInitialRun();
+    method public boolean isRunning();
+    method protected void normalize(android.graphics.PointF);
+    method protected void onChildAttachedToWindow(android.view.View);
+    method protected abstract void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected abstract void onStart();
+    method protected abstract void onStop();
+    method protected abstract void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method public void setTargetPosition(int);
+    method protected final void stop();
+  }
+
+  public static class RecyclerView.SmoothScroller.Action {
+    ctor public RecyclerView.SmoothScroller.Action(int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int, android.view.animation.Interpolator);
+    method public int getDuration();
+    method public int getDx();
+    method public int getDy();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public void jumpTo(int);
+    method public void setDuration(int);
+    method public void setDx(int);
+    method public void setDy(int);
+    method public void setInterpolator(android.view.animation.Interpolator);
+    method public void update(int, int, int, android.view.animation.Interpolator);
+    field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+  }
+
+  public static abstract interface RecyclerView.SmoothScroller.ScrollVectorProvider {
+    method public abstract android.graphics.PointF computeScrollVectorForPosition(int);
+  }
+
+  public static class RecyclerView.State {
+    ctor public RecyclerView.State();
+    method public boolean didStructureChange();
+    method public <T> T get(int);
+    method public int getItemCount();
+    method public int getTargetScrollPosition();
+    method public boolean hasTargetScrollPosition();
+    method public boolean isMeasuring();
+    method public boolean isPreLayout();
+    method public void put(int, java.lang.Object);
+    method public void remove(int);
+    method public boolean willRunPredictiveAnimations();
+    method public boolean willRunSimpleAnimations();
+  }
+
+  public static abstract class RecyclerView.ViewCacheExtension {
+    ctor public RecyclerView.ViewCacheExtension();
+    method public abstract android.view.View getViewForPositionAndType(android.support.v7.widget.RecyclerView.Recycler, int, int);
+  }
+
+  public static abstract class RecyclerView.ViewHolder {
+    ctor public RecyclerView.ViewHolder(android.view.View);
+    method public final int getAdapterPosition();
+    method public final long getItemId();
+    method public final int getItemViewType();
+    method public final int getLayoutPosition();
+    method public final int getOldPosition();
+    method public final deprecated int getPosition();
+    method public final boolean isRecyclable();
+    method public final void setIsRecyclable(boolean);
+    field public final android.view.View itemView;
+  }
+
+  public class RecyclerViewAccessibilityDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate(android.support.v7.widget.RecyclerView);
+    method public android.support.v4.view.AccessibilityDelegateCompat getItemDelegate();
+  }
+
+  public static class RecyclerViewAccessibilityDelegate.ItemDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate.ItemDelegate(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+  }
+
+  public class SearchView extends android.support.v7.widget.LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public java.lang.CharSequence getQuery();
+    method public java.lang.CharSequence getQueryHint();
+    method public android.support.v4.widget.CursorAdapter getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(android.support.v7.widget.SearchView.OnCloseListener);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener);
+    method public void setOnQueryTextListener(android.support.v7.widget.SearchView.OnQueryTextListener);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener);
+    method public void setOnSuggestionListener(android.support.v7.widget.SearchView.OnSuggestionListener);
+    method public void setQuery(java.lang.CharSequence, boolean);
+    method public void setQueryHint(java.lang.CharSequence);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(android.support.v4.widget.CursorAdapter);
+  }
+
+  public static abstract interface SearchView.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract interface SearchView.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchView.OnSuggestionListener {
+    method public abstract boolean onSuggestionClick(int);
+    method public abstract boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context);
+    method public android.view.View onCreateActionView();
+    method public void setOnShareTargetSelectedListener(android.support.v7.widget.ShareActionProvider.OnShareTargetSelectedListener);
+    method public void setShareHistoryFileName(java.lang.String);
+    method public void setShareIntent(android.content.Intent);
+    field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static abstract interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public abstract boolean onShareTargetSelected(android.support.v7.widget.ShareActionProvider, android.content.Intent);
+  }
+
+  public abstract class SimpleItemAnimator extends android.support.v7.widget.RecyclerView.ItemAnimator {
+    ctor public SimpleItemAnimator();
+    method public abstract boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean getSupportsChangeAnimations();
+    method public void onAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setSupportsChangeAnimations(boolean);
+  }
+
+  public abstract class SnapHelper extends android.support.v7.widget.RecyclerView.OnFlingListener {
+    ctor public SnapHelper();
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
+    method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public int[] calculateScrollDistance(int, int);
+    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+    method public boolean onFling(int, int);
+  }
+
+  public class StaggeredGridLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public StaggeredGridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public StaggeredGridLayoutManager(int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int[] findFirstCompletelyVisibleItemPositions(int[]);
+    method public int[] findFirstVisibleItemPositions(int[]);
+    method public int[] findLastCompletelyVisibleItemPositions(int[]);
+    method public int[] findLastVisibleItemPositions(int[]);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public int getGapStrategy();
+    method public int getOrientation();
+    method public boolean getReverseLayout();
+    method public int getSpanCount();
+    method public void invalidateSpanAssignments();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setGapStrategy(int);
+    method public void setOrientation(int);
+    method public void setReverseLayout(boolean);
+    method public void setSpanCount(int);
+    field public static final deprecated int GAP_HANDLING_LAZY = 1; // 0x1
+    field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
+    field public static final int GAP_HANDLING_NONE = 0; // 0x0
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class StaggeredGridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public final int getSpanIndex();
+    method public boolean isFullSpan();
+    method public void setFullSpan(boolean);
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public java.lang.CharSequence getTextOff();
+    method public java.lang.CharSequence getTextOn();
+    method public android.graphics.drawable.Drawable getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context, int);
+    method public void setSwitchTypeface(android.graphics.Typeface, int);
+    method public void setSwitchTypeface(android.graphics.Typeface);
+    method public void setTextOff(java.lang.CharSequence);
+    method public void setTextOn(java.lang.CharSequence);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public abstract interface ThemedSpinnerAdapter implements android.widget.SpinnerAdapter {
+    method public abstract android.content.res.Resources.Theme getDropDownViewTheme();
+    method public abstract void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable getLogo();
+    method public java.lang.CharSequence getLogoDescription();
+    method public android.view.Menu getMenu();
+    method public java.lang.CharSequence getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable getNavigationIcon();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable);
+    method public void setLogoDescription(int);
+    method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationIcon(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.Toolbar.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public void setSubtitle(int);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setSubtitleTextAppearance(android.content.Context, int);
+    method public void setSubtitleTextColor(int);
+    method public void setTitle(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context, int);
+    method public void setTitleTextColor(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends android.support.v7.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(android.support.v7.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
+  }
+
+  public static abstract interface Toolbar.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public static class Toolbar.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel);
+    ctor public Toolbar.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public Toolbar.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.widget.Toolbar.SavedState> CREATOR;
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+  }
+
+}
+
+package android.support.v7.widget.helper {
+
+  public class ItemTouchHelper extends android.support.v7.widget.RecyclerView.ItemDecoration implements android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener {
+    ctor public ItemTouchHelper(android.support.v7.widget.helper.ItemTouchHelper.Callback);
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public void onChildViewAttachedToWindow(android.view.View);
+    method public void onChildViewDetachedFromWindow(android.view.View);
+    method public void startDrag(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void startSwipe(android.support.v7.widget.RecyclerView.ViewHolder);
+    field public static final int ACTION_STATE_DRAG = 2; // 0x2
+    field public static final int ACTION_STATE_IDLE = 0; // 0x0
+    field public static final int ACTION_STATE_SWIPE = 1; // 0x1
+    field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
+    field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
+    field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
+    field public static final int DOWN = 2; // 0x2
+    field public static final int END = 32; // 0x20
+    field public static final int LEFT = 4; // 0x4
+    field public static final int RIGHT = 8; // 0x8
+    field public static final int START = 16; // 0x10
+    field public static final int UP = 1; // 0x1
+  }
+
+  public static abstract class ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.Callback();
+    method public boolean canDropOver(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ViewHolder chooseDropTarget(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<android.support.v7.widget.RecyclerView.ViewHolder>, int, int);
+    method public void clearView(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int convertToAbsoluteDirection(int, int);
+    method public static int convertToRelativeDirection(int, int);
+    method public long getAnimationDuration(android.support.v7.widget.RecyclerView, int, float, float);
+    method public int getBoundingBoxMargin();
+    method public static android.support.v7.widget.helper.ItemTouchUIUtil getDefaultUIUtil();
+    method public float getMoveThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeEscapeVelocity(float);
+    method public float getSwipeThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeVelocityThreshold(float);
+    method public int interpolateOutOfBoundsScroll(android.support.v7.widget.RecyclerView, int, int, int, long);
+    method public boolean isItemViewSwipeEnabled();
+    method public boolean isLongPressDragEnabled();
+    method public static int makeFlag(int, int);
+    method public static int makeMovementFlags(int, int);
+    method public void onChildDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public void onChildDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public abstract boolean onMove(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoved(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int);
+    method public void onSelectedChanged(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method public abstract void onSwiped(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
+    field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
+  }
+
+  public static abstract class ItemTouchHelper.SimpleCallback extends android.support.v7.widget.helper.ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.SimpleCallback(int, int);
+    method public int getDragDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getSwipeDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setDefaultDragDirs(int);
+    method public void setDefaultSwipeDirs(int);
+  }
+
+  public static abstract interface ItemTouchHelper.ViewDropHandler {
+    method public abstract void prepareForDrop(android.view.View, android.view.View, int, int);
+  }
+
+  public abstract interface ItemTouchUIUtil {
+    method public abstract void clearView(android.view.View);
+    method public abstract void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onSelected(android.view.View);
+  }
+
+}
+
+package android.support.v7.widget.util {
+
+  public abstract class SortedListAdapterCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedListAdapterCallback(android.support.v7.widget.RecyclerView.Adapter);
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+}
+
+package android.support.wear.widget {
+
+  public class BoxInsetLayout extends android.view.ViewGroup {
+    ctor public BoxInsetLayout(android.content.Context);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected void onLayout(boolean, int, int, int, int);
+  }
+
+  public static class BoxInsetLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BoxInsetLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout.LayoutParams(int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.support.wear.widget.BoxInsetLayout.LayoutParams);
+    field public static final int BOX_ALL = 15; // 0xf
+    field public static final int BOX_BOTTOM = 8; // 0x8
+    field public static final int BOX_LEFT = 1; // 0x1
+    field public static final int BOX_NONE = 0; // 0x0
+    field public static final int BOX_RIGHT = 4; // 0x4
+    field public static final int BOX_TOP = 2; // 0x2
+    field public int boxedEdges;
+  }
+
+  public class CurvingLayoutCallback extends android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback {
+    ctor public CurvingLayoutCallback(android.content.Context);
+    method public void adjustAnchorOffsetXY(android.view.View, float[]);
+    method public void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class SwipeDismissFrameLayout extends android.widget.FrameLayout {
+    ctor public SwipeDismissFrameLayout(android.content.Context);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+    method public void removeCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+  }
+
+  public static abstract class SwipeDismissFrameLayout.Callback {
+    ctor public SwipeDismissFrameLayout.Callback();
+    method public void onDismissed(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeCanceled(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeStarted(android.support.wear.widget.SwipeDismissFrameLayout);
+  }
+
+  public class WearableLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public WearableLinearLayoutManager(android.content.Context, android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+    ctor public WearableLinearLayoutManager(android.content.Context);
+    method public android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback getLayoutCallback();
+    method public void setLayoutCallback(android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+  }
+
+  public static abstract class WearableLinearLayoutManager.LayoutCallback {
+    ctor public WearableLinearLayoutManager.LayoutCallback();
+    method public abstract void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class WearableRecyclerView extends android.support.v7.widget.RecyclerView {
+    ctor public WearableRecyclerView(android.content.Context);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public float getBezelFraction();
+    method public float getScrollDegreesPerScreen();
+    method public boolean isCircularScrollingGestureEnabled();
+    method public boolean isEdgeItemsCenteringEnabled();
+    method public void setBezelFraction(float);
+    method public void setCircularScrollingGestureEnabled(boolean);
+    method public void setEdgeItemsCenteringEnabled(boolean);
+    method public void setScrollDegreesPerScreen(float);
+  }
+
+}
+
diff --git a/api/26.0.0-beta2.txt b/api/26.0.0-beta2.txt
new file mode 100644
index 0000000..5d7496b
--- /dev/null
+++ b/api/26.0.0-beta2.txt
@@ -0,0 +1,13140 @@
+package android.support.animation {
+
+  public abstract class DynamicAnimation<T extends android.support.animation.DynamicAnimation<T>> {
+    method public T addEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public T addUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public void cancel();
+    method public float getMinimumVisibleChange();
+    method public boolean isRunning();
+    method public void removeEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public void removeUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public T setMaxValue(float);
+    method public T setMinValue(float);
+    method public T setMinimumVisibleChange(float);
+    method public T setStartValue(float);
+    method public T setStartVelocity(float);
+    method public void start();
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ALPHA;
+    field public static final float MIN_VISIBLE_CHANGE_ALPHA = 0.00390625f;
+    field public static final float MIN_VISIBLE_CHANGE_PIXELS = 1.0f;
+    field public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 0.1f;
+    field public static final float MIN_VISIBLE_CHANGE_SCALE = 0.002f;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Z;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Z;
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationEndListener {
+    method public abstract void onAnimationEnd(android.support.animation.DynamicAnimation, boolean, float, float);
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationUpdateListener {
+    method public abstract void onAnimationUpdate(android.support.animation.DynamicAnimation, float, float);
+  }
+
+  public static abstract class DynamicAnimation.ViewProperty extends android.support.animation.FloatPropertyCompat {
+  }
+
+  public final class FlingAnimation extends android.support.animation.DynamicAnimation {
+    ctor public FlingAnimation(android.support.animation.FloatValueHolder);
+    ctor public FlingAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    method public float getFriction();
+    method public android.support.animation.FlingAnimation setFriction(float);
+  }
+
+  public abstract class FloatPropertyCompat<T> {
+    ctor public FloatPropertyCompat(java.lang.String);
+    method public static <T> android.support.animation.FloatPropertyCompat<T> createFloatPropertyCompat(android.util.FloatProperty<T>);
+    method public abstract float getValue(T);
+    method public abstract void setValue(T, float);
+  }
+
+  public final class FloatValueHolder {
+    ctor public FloatValueHolder();
+    ctor public FloatValueHolder(float);
+    method public float getValue();
+    method public void setValue(float);
+  }
+
+  public final class SpringAnimation extends android.support.animation.DynamicAnimation {
+    ctor public SpringAnimation(android.support.animation.FloatValueHolder);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>, float);
+    method public void animateToFinalPosition(float);
+    method public boolean canSkipToEnd();
+    method public android.support.animation.SpringForce getSpring();
+    method public android.support.animation.SpringAnimation setSpring(android.support.animation.SpringForce);
+    method public void skipToEnd();
+  }
+
+  public final class SpringForce {
+    ctor public SpringForce();
+    ctor public SpringForce(float);
+    method public float getDampingRatio();
+    method public float getFinalPosition();
+    method public float getStiffness();
+    method public android.support.animation.SpringForce setDampingRatio(float);
+    method public android.support.animation.SpringForce setFinalPosition(float);
+    method public android.support.animation.SpringForce setStiffness(float);
+    field public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
+    field public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
+    field public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
+    field public static final float DAMPING_RATIO_NO_BOUNCY = 1.0f;
+    field public static final float STIFFNESS_HIGH = 10000.0f;
+    field public static final float STIFFNESS_LOW = 200.0f;
+    field public static final float STIFFNESS_MEDIUM = 1500.0f;
+    field public static final float STIFFNESS_VERY_LOW = 50.0f;
+  }
+
+}
+
+package android.support.app.recommendation {
+
+  public final class ContentRecommendation {
+    method public java.lang.String getBackgroundImageUri();
+    method public int getBadgeImageResourceId();
+    method public int getColor();
+    method public android.graphics.Bitmap getContentImage();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
+    method public java.lang.String[] getContentTypes();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getGroup();
+    method public java.lang.String getIdTag();
+    method public java.lang.String getMaturityRating();
+    method public android.app.Notification getNotificationObject(android.content.Context);
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public int getProgressMax();
+    method public int getProgressValue();
+    method public long getRunningTime();
+    method public java.lang.String getSortKey();
+    method public java.lang.String getSourceName();
+    method public int getStatus();
+    method public java.lang.String getText();
+    method public java.lang.String getTitle();
+    method public boolean hasProgressInfo();
+    method public boolean isAutoDismiss();
+    method public void setAutoDismiss(boolean);
+    method public void setGroup(java.lang.String);
+    method public void setProgress(int, int);
+    method public void setSortKey(java.lang.String);
+    method public void setStatus(int);
+    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
+    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
+    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
+    field public static final int CONTENT_STATUS_READY = 0; // 0x0
+    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
+    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
+    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
+    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
+    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
+    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
+    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
+    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
+    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
+    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
+    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
+  }
+
+  public static final class ContentRecommendation.Builder {
+    ctor public ContentRecommendation.Builder();
+    method public android.support.app.recommendation.ContentRecommendation build();
+    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
+  }
+
+  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
+  }
+
+  public static class ContentRecommendation.IntentData {
+    ctor public ContentRecommendation.IntentData();
+  }
+
+  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
+  }
+
+  public final class RecommendationExtender implements android.app.Notification.Extender {
+    ctor public RecommendationExtender();
+    ctor public RecommendationExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String[] getContentTypes();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getMaturityRating();
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public long getRunningTime();
+    method public int getStatus();
+    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
+    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
+  }
+
+}
+
+package android.support.customtabs {
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(java.lang.String, android.os.Bundle);
+    method public void onMessageChannelReady(android.os.Bundle);
+    method public void onNavigationEvent(int, android.os.Bundle);
+    method public void onPostMessage(java.lang.String, android.os.Bundle);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, java.lang.String, android.support.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, java.lang.String);
+    method public android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>, boolean);
+    method public android.support.customtabs.CustomTabsSession newSession(android.support.customtabs.CustomTabsCallback);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context, android.net.Uri);
+    method public static android.content.Intent setAlwaysUseBrowserUI(android.content.Intent);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent);
+    field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final java.lang.String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final java.lang.String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final java.lang.String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final java.lang.String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final java.lang.String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final java.lang.String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
+    method public android.support.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
+    method public deprecated android.support.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, java.lang.String, android.app.PendingIntent) throws java.lang.IllegalStateException;
+    method public android.support.customtabs.CustomTabsIntent build();
+    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setToolbarColor(int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(android.support.customtabs.CustomTabsSessionToken);
+    method protected abstract android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract int postMessage(android.support.customtabs.CustomTabsSessionToken, java.lang.String, android.os.Bundle);
+    method protected abstract boolean requestPostMessageChannel(android.support.customtabs.CustomTabsSessionToken, android.net.Uri);
+    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
+    method protected abstract boolean warmup(long);
+    field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class CustomTabsService.Result implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName, android.support.customtabs.CustomTabsClient);
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+  }
+
+  public final class CustomTabsSession {
+    method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method public int postMessage(java.lang.String, android.os.Bundle);
+    method public boolean requestPostMessageChannel(android.net.Uri);
+    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public deprecated boolean setToolbarItem(int, android.graphics.Bitmap, java.lang.String);
+  }
+
+  public class CustomTabsSessionToken {
+    method public android.support.customtabs.CustomTabsCallback getCallback();
+    method public static android.support.customtabs.CustomTabsSessionToken getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(android.support.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(android.support.customtabs.CustomTabsSessionToken);
+    method public boolean bindSessionToPostMessageService(android.content.Context, java.lang.String);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+    method public final void onServiceDisconnected(android.content.ComponentName);
+    method public final boolean postMessage(java.lang.String, android.os.Bundle);
+    method public void unbindFromContext(android.content.Context);
+  }
+
+}
+
+package android.support.design.widget {
+
+  public class AppBarLayout extends android.widget.LinearLayout {
+    ctor public AppBarLayout(android.content.Context);
+    ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
+    method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public deprecated float getTargetElevation();
+    method public final int getTotalScrollRange();
+    method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public void setExpanded(boolean);
+    method public void setExpanded(boolean, boolean);
+    method public deprecated void setTargetElevation(float);
+  }
+
+  public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
+    ctor public AppBarLayout.Behavior();
+    ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int, int, int, int);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int);
+    method public void setDragCallback(android.support.design.widget.AppBarLayout.Behavior.DragCallback);
+  }
+
+  public static abstract class AppBarLayout.Behavior.DragCallback {
+    ctor public AppBarLayout.Behavior.DragCallback();
+    method public abstract boolean canDrag(android.support.design.widget.AppBarLayout);
+  }
+
+  protected static class AppBarLayout.Behavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+  }
+
+  public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public AppBarLayout.LayoutParams(int, int);
+    ctor public AppBarLayout.LayoutParams(int, int, float);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.widget.LinearLayout.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.support.design.widget.AppBarLayout.LayoutParams);
+    method public int getScrollFlags();
+    method public android.view.animation.Interpolator getScrollInterpolator();
+    method public void setScrollFlags(int);
+    method public void setScrollInterpolator(android.view.animation.Interpolator);
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS = 4; // 0x4
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
+    field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
+    field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
+    field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
+  }
+
+  public static abstract interface AppBarLayout.OnOffsetChangedListener {
+    method public abstract void onOffsetChanged(android.support.design.widget.AppBarLayout, int);
+  }
+
+  public static class AppBarLayout.ScrollingViewBehavior extends android.support.design.widget.HeaderScrollingViewBehavior {
+    ctor public AppBarLayout.ScrollingViewBehavior();
+    ctor public AppBarLayout.ScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
+  }
+
+  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
+    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
+    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public void dismiss();
+    method public android.content.Context getContext();
+    method public int getDuration();
+    method public android.view.View getView();
+    method public boolean isShown();
+    method public boolean isShownOrQueued();
+    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public B setDuration(int);
+    method public void show();
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
+    ctor public BaseTransientBottomBar.BaseCallback();
+    method public void onDismissed(B, int);
+    method public void onShown(B);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
+    method public abstract void animateContentIn(int, int);
+    method public abstract void animateContentOut(int, int);
+  }
+
+  public class BottomNavigationView extends android.widget.FrameLayout {
+    ctor public BottomNavigationView(android.content.Context);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public int getItemBackgroundResource();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public int getMaxItemCount();
+    method public android.view.Menu getMenu();
+    method public int getSelectedItemId();
+    method public void inflateMenu(int);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setOnNavigationItemReselectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemReselectedListener);
+    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
+    method public void setSelectedItemId(int);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemReselectedListener {
+    method public abstract void onNavigationItemReselected(android.view.MenuItem);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public class BottomSheetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public BottomSheetBehavior();
+    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
+    method public static <V extends android.view.View> android.support.design.widget.BottomSheetBehavior<V> from(V);
+    method public final int getPeekHeight();
+    method public boolean getSkipCollapsed();
+    method public final int getState();
+    method public boolean isHideable();
+    method public void setBottomSheetCallback(android.support.design.widget.BottomSheetBehavior.BottomSheetCallback);
+    method public void setHideable(boolean);
+    method public final void setPeekHeight(int);
+    method public void setSkipCollapsed(boolean);
+    method public final void setState(int);
+    field public static final int PEEK_HEIGHT_AUTO = -1; // 0xffffffff
+    field public static final int STATE_COLLAPSED = 4; // 0x4
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_EXPANDED = 3; // 0x3
+    field public static final int STATE_HIDDEN = 5; // 0x5
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class BottomSheetBehavior.BottomSheetCallback {
+    ctor public BottomSheetBehavior.BottomSheetCallback();
+    method public abstract void onSlide(android.view.View, float);
+    method public abstract void onStateChanged(android.view.View, int);
+  }
+
+  protected static class BottomSheetBehavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
+  }
+
+  public class BottomSheetDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public BottomSheetDialog(android.content.Context);
+    ctor public BottomSheetDialog(android.content.Context, int);
+    ctor protected BottomSheetDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+  }
+
+  public class BottomSheetDialogFragment extends android.support.v7.app.AppCompatDialogFragment {
+    ctor public BottomSheetDialogFragment();
+  }
+
+  public class CollapsingToolbarLayout extends android.widget.FrameLayout {
+    ctor public CollapsingToolbarLayout(android.content.Context);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCollapsedTitleGravity();
+    method public android.graphics.Typeface getCollapsedTitleTypeface();
+    method public android.graphics.drawable.Drawable getContentScrim();
+    method public int getExpandedTitleGravity();
+    method public int getExpandedTitleMarginBottom();
+    method public int getExpandedTitleMarginEnd();
+    method public int getExpandedTitleMarginStart();
+    method public int getExpandedTitleMarginTop();
+    method public android.graphics.Typeface getExpandedTitleTypeface();
+    method public long getScrimAnimationDuration();
+    method public int getScrimVisibleHeightTrigger();
+    method public android.graphics.drawable.Drawable getStatusBarScrim();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isTitleEnabled();
+    method public void setCollapsedTitleGravity(int);
+    method public void setCollapsedTitleTextAppearance(int);
+    method public void setCollapsedTitleTextColor(int);
+    method public void setCollapsedTitleTextColor(android.content.res.ColorStateList);
+    method public void setCollapsedTitleTypeface(android.graphics.Typeface);
+    method public void setContentScrim(android.graphics.drawable.Drawable);
+    method public void setContentScrimColor(int);
+    method public void setContentScrimResource(int);
+    method public void setExpandedTitleColor(int);
+    method public void setExpandedTitleGravity(int);
+    method public void setExpandedTitleMargin(int, int, int, int);
+    method public void setExpandedTitleMarginBottom(int);
+    method public void setExpandedTitleMarginEnd(int);
+    method public void setExpandedTitleMarginStart(int);
+    method public void setExpandedTitleMarginTop(int);
+    method public void setExpandedTitleTextAppearance(int);
+    method public void setExpandedTitleTextColor(android.content.res.ColorStateList);
+    method public void setExpandedTitleTypeface(android.graphics.Typeface);
+    method public void setScrimAnimationDuration(long);
+    method public void setScrimVisibleHeightTrigger(int);
+    method public void setScrimsShown(boolean);
+    method public void setScrimsShown(boolean, boolean);
+    method public void setStatusBarScrim(android.graphics.drawable.Drawable);
+    method public void setStatusBarScrimColor(int);
+    method public void setStatusBarScrimResource(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleEnabled(boolean);
+  }
+
+  public static class CollapsingToolbarLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public CollapsingToolbarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    method public int getCollapseMode();
+    method public float getParallaxMultiplier();
+    method public void setCollapseMode(int);
+    method public void setParallaxMultiplier(float);
+    field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
+    field public static final int COLLAPSE_MODE_PARALLAX = 2; // 0x2
+    field public static final int COLLAPSE_MODE_PIN = 1; // 0x1
+  }
+
+  public class CoordinatorLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingParent2 {
+    ctor public CoordinatorLayout(android.content.Context);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void dispatchDependentViewsChanged(android.view.View);
+    method public boolean doViewsOverlap(android.view.View, android.view.View);
+    method public java.util.List<android.view.View> getDependencies(android.view.View);
+    method public java.util.List<android.view.View> getDependents(android.view.View);
+    method public android.graphics.drawable.Drawable getStatusBarBackground();
+    method public boolean isPointInChildBounds(android.view.View, int, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onLayoutChild(android.view.View, int);
+    method public void onMeasureChild(android.view.View, int, int, int, int);
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackgroundColor(int);
+    method public void setStatusBarBackgroundResource(int);
+  }
+
+  public static abstract class CoordinatorLayout.Behavior<V extends android.view.View> {
+    ctor public CoordinatorLayout.Behavior();
+    ctor public CoordinatorLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean blocksInteractionBelow(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect);
+    method public int getScrimColor(android.support.design.widget.CoordinatorLayout, V);
+    method public float getScrimOpacity(android.support.design.widget.CoordinatorLayout, V);
+    method public static java.lang.Object getTag(android.view.View);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.support.design.widget.CoordinatorLayout, V, android.support.v4.view.WindowInsetsCompat);
+    method public void onAttachedToLayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDependentViewRemoved(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDetachedFromLayoutParams();
+    method public boolean onInterceptTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, V, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float);
+    method public deprecated void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[], int);
+    method public deprecated void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int, int);
+    method public deprecated void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int, int);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect, boolean);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
+    method public deprecated boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int, int);
+    method public deprecated void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int);
+    method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public static void setTag(android.view.View, java.lang.Object);
+  }
+
+  public static abstract class CoordinatorLayout.DefaultBehavior implements java.lang.annotation.Annotation {
+  }
+
+  public static class CoordinatorLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public CoordinatorLayout.LayoutParams(int, int);
+    ctor public CoordinatorLayout.LayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAnchorId();
+    method public android.support.design.widget.CoordinatorLayout.Behavior getBehavior();
+    method public void setAnchorId(int);
+    method public void setBehavior(android.support.design.widget.CoordinatorLayout.Behavior);
+    field public int anchorGravity;
+    field public int dodgeInsetEdges;
+    field public int gravity;
+    field public int insetEdge;
+    field public int keyline;
+  }
+
+  protected static class CoordinatorLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public CoordinatorLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+  }
+
+  public class FloatingActionButton extends android.support.design.widget.VisibilityAwareImageButton {
+    ctor public FloatingActionButton(android.content.Context);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+    method public float getCompatElevation();
+    method public android.graphics.drawable.Drawable getContentBackground();
+    method public boolean getContentRect(android.graphics.Rect);
+    method public int getRippleColor();
+    method public int getSize();
+    method public boolean getUseCompatPadding();
+    method public void hide();
+    method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    method public void setCompatElevation(float);
+    method public void setRippleColor(int);
+    method public void setSize(int);
+    method public void setUseCompatPadding(boolean);
+    method public void show();
+    method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    field public static final int SIZE_AUTO = -1; // 0xffffffff
+    field public static final int SIZE_MINI = 1; // 0x1
+    field public static final int SIZE_NORMAL = 0; // 0x0
+  }
+
+  public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public FloatingActionButton.Behavior();
+    ctor public FloatingActionButton.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.graphics.Rect);
+    method public boolean isAutoHideEnabled();
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.view.View);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, int);
+    method public void setAutoHideEnabled(boolean);
+  }
+
+  public static abstract class FloatingActionButton.OnVisibilityChangedListener {
+    ctor public FloatingActionButton.OnVisibilityChangedListener();
+    method public void onHidden(android.support.design.widget.FloatingActionButton);
+    method public void onShown(android.support.design.widget.FloatingActionButton);
+  }
+
+   abstract class HeaderBehavior<V extends android.view.View> extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderBehavior();
+    ctor public HeaderBehavior(android.content.Context, android.util.AttributeSet);
+  }
+
+   abstract class HeaderScrollingViewBehavior extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderScrollingViewBehavior();
+    ctor public HeaderScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public final int getOverlayTop();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
+    method public final void setOverlayTop(int);
+  }
+
+  public class NavigationView extends android.widget.FrameLayout {
+    ctor public NavigationView(android.content.Context);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public void addHeaderView(android.view.View);
+    method public int getHeaderCount();
+    method public android.view.View getHeaderView(int);
+    method public android.graphics.drawable.Drawable getItemBackground();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public android.view.Menu getMenu();
+    method public android.view.View inflateHeaderView(int);
+    method public void inflateMenu(int);
+    method public void removeHeaderView(android.view.View);
+    method public void setCheckedItem(int);
+    method public void setItemBackground(android.graphics.drawable.Drawable);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextAppearance(int);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setNavigationItemSelectedListener(android.support.design.widget.NavigationView.OnNavigationItemSelectedListener);
+  }
+
+  public static abstract interface NavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public static class NavigationView.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public NavigationView.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public NavigationView.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.NavigationView.SavedState> CREATOR;
+    field public android.os.Bundle menuState;
+  }
+
+  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
+    method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
+    method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
+    method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
+    method public android.support.design.widget.Snackbar setActionTextColor(int);
+    method public deprecated android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
+    method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
+    method public android.support.design.widget.Snackbar setText(int);
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
+    ctor public Snackbar.Callback();
+    method public void onDismissed(android.support.design.widget.Snackbar, int);
+    method public void onShown(android.support.design.widget.Snackbar);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public SwipeDismissBehavior();
+    method public boolean canSwipeDismissView(android.view.View);
+    method public int getDragState();
+    method public void setDragDismissDistance(float);
+    method public void setEndAlphaSwipeDistance(float);
+    method public void setListener(android.support.design.widget.SwipeDismissBehavior.OnDismissListener);
+    method public void setSensitivity(float);
+    method public void setStartAlphaSwipeDistance(float);
+    method public void setSwipeDirection(int);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_ANY = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_END_TO_START = 1; // 0x1
+    field public static final int SWIPE_DIRECTION_START_TO_END = 0; // 0x0
+  }
+
+  public static abstract interface SwipeDismissBehavior.OnDismissListener {
+    method public abstract void onDismiss(android.view.View);
+    method public abstract void onDragStateChanged(int);
+  }
+
+  public final class TabItem extends android.view.View {
+    ctor public TabItem(android.content.Context);
+    ctor public TabItem(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class TabLayout extends android.widget.HorizontalScrollView {
+    ctor public TabLayout(android.content.Context);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void addTab(android.support.design.widget.TabLayout.Tab);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
+    method public void clearOnTabSelectedListeners();
+    method public int getSelectedTabPosition();
+    method public android.support.design.widget.TabLayout.Tab getTabAt(int);
+    method public int getTabCount();
+    method public int getTabGravity();
+    method public int getTabMode();
+    method public android.content.res.ColorStateList getTabTextColors();
+    method public android.support.design.widget.TabLayout.Tab newTab();
+    method public void removeAllTabs();
+    method public void removeOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void removeTab(android.support.design.widget.TabLayout.Tab);
+    method public void removeTabAt(int);
+    method public deprecated void setOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void setScrollPosition(int, float, boolean);
+    method public void setSelectedTabIndicatorColor(int);
+    method public void setSelectedTabIndicatorHeight(int);
+    method public void setTabGravity(int);
+    method public void setTabMode(int);
+    method public void setTabTextColors(android.content.res.ColorStateList);
+    method public void setTabTextColors(int, int);
+    method public deprecated void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager, boolean);
+    field public static final int GRAVITY_CENTER = 1; // 0x1
+    field public static final int GRAVITY_FILL = 0; // 0x0
+    field public static final int MODE_FIXED = 1; // 0x1
+    field public static final int MODE_SCROLLABLE = 0; // 0x0
+  }
+
+  public static abstract interface TabLayout.OnTabSelectedListener {
+    method public abstract void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public static final class TabLayout.Tab {
+    method public java.lang.CharSequence getContentDescription();
+    method public android.view.View getCustomView();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public int getPosition();
+    method public java.lang.Object getTag();
+    method public java.lang.CharSequence getText();
+    method public boolean isSelected();
+    method public void select();
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(int);
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(android.view.View);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(int);
+    method public android.support.design.widget.TabLayout.Tab setIcon(android.graphics.drawable.Drawable);
+    method public android.support.design.widget.TabLayout.Tab setIcon(int);
+    method public android.support.design.widget.TabLayout.Tab setTag(java.lang.Object);
+    method public android.support.design.widget.TabLayout.Tab setText(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public class TextInputEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public TextInputEditText(android.content.Context);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class TextInputLayout extends android.widget.LinearLayout {
+    ctor public TextInputLayout(android.content.Context);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCounterMaxLength();
+    method public android.widget.EditText getEditText();
+    method public java.lang.CharSequence getError();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
+    method public android.graphics.Typeface getTypeface();
+    method public boolean isCounterEnabled();
+    method public boolean isErrorEnabled();
+    method public boolean isHintAnimationEnabled();
+    method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void setCounterEnabled(boolean);
+    method public void setCounterMaxLength(int);
+    method public void setError(java.lang.CharSequence);
+    method public void setErrorEnabled(boolean);
+    method public void setErrorTextAppearance(int);
+    method public void setHint(java.lang.CharSequence);
+    method public void setHintAnimationEnabled(boolean);
+    method public void setHintEnabled(boolean);
+    method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTypeface(android.graphics.Typeface);
+  }
+
+   class ViewOffsetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public ViewOffsetBehavior();
+    ctor public ViewOffsetBehavior(android.content.Context, android.util.AttributeSet);
+    method public int getLeftAndRightOffset();
+    method public int getTopAndBottomOffset();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean setLeftAndRightOffset(int);
+    method public boolean setTopAndBottomOffset(int);
+  }
+
+   class VisibilityAwareImageButton extends android.widget.ImageButton {
+    ctor public VisibilityAwareImageButton(android.content.Context);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+}
+
+package android.support.graphics.drawable {
+
+  public abstract interface Animatable2Compat {
+    method public abstract void clearAnimationCallbacks();
+    method public abstract void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public abstract boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+  public static abstract class Animatable2Compat.AnimationCallback {
+    ctor public Animatable2Compat.AnimationCallback();
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
+  }
+
+  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon implements android.support.graphics.drawable.Animatable2Compat {
+    method public void clearAnimationCallbacks();
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public boolean isRunning();
+    method public void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void start();
+    method public void stop();
+    method public boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
+  }
+
+  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
+    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+  }
+
+}
+
+package android.support.media {
+
+  public class ExifInterface {
+    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
+    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
+    method public double getAltitude(double);
+    method public java.lang.String getAttribute(java.lang.String);
+    method public double getAttributeDouble(java.lang.String, double);
+    method public int getAttributeInt(java.lang.String, int);
+    method public deprecated boolean getLatLong(float[]);
+    method public double[] getLatLong();
+    method public byte[] getThumbnail();
+    method public android.graphics.Bitmap getThumbnailBitmap();
+    method public byte[] getThumbnailBytes();
+    method public long[] getThumbnailRange();
+    method public boolean hasThumbnail();
+    method public boolean isThumbnailCompressed();
+    method public void saveAttributes() throws java.io.IOException;
+    method public void setAttribute(java.lang.String, java.lang.String);
+    method public void setLatLong(double, double);
+    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
+    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
+    field public static final int ORIENTATION_NORMAL = 1; // 0x1
+    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
+    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
+    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
+    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
+    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
+    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+    field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+    field public static final java.lang.String TAG_COMPRESSION = "Compression";
+    field public static final java.lang.String TAG_CONTRAST = "Contrast";
+    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
+    field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
+    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
+    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
+    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
+    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
+    field public static final java.lang.String TAG_FLASH = "Flash";
+    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
+    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
+    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
+    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
+    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
+    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
+    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
+    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
+    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
+    field public static final java.lang.String TAG_MAKE = "Make";
+    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
+    field public static final java.lang.String TAG_MODEL = "Model";
+    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
+    field public static final java.lang.String TAG_OECF = "OECF";
+    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
+    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
+    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+    field public static final java.lang.String TAG_RW2_ISO = "ISO";
+    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
+    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+    field public static final java.lang.String TAG_SATURATION = "Saturation";
+    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+    field public static final java.lang.String TAG_SOFTWARE = "Software";
+    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
+    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
+    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
+    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
+    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
+  }
+
+}
+
+package android.support.media.tv {
+
+  public abstract class BasePreviewProgram extends android.support.media.tv.BaseProgram {
+    method public java.lang.String getAuthor();
+    method public int getAvailability();
+    method public java.lang.String getContentId();
+    method public int getDurationMillis();
+    method public android.content.Intent getIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getIntentUri();
+    method public long getInteractionCount();
+    method public int getInteractionType();
+    method public java.lang.String getInternalProviderId();
+    method public int getItemCount();
+    method public int getLastPlaybackPositionMillis();
+    method public android.net.Uri getLogoUri();
+    method public java.lang.String getOfferPrice();
+    method public int getPosterArtAspectRatio();
+    method public android.net.Uri getPreviewVideoUri();
+    method public java.lang.String getReleaseDate();
+    method public java.lang.String getStartingPrice();
+    method public int getThumbnailAspectRatio();
+    method public int getType();
+    method public boolean isBrowsable();
+    method public boolean isLive();
+    method public boolean isTransient();
+  }
+
+  public static abstract class BasePreviewProgram.Builder<T extends android.support.media.tv.BasePreviewProgram.Builder> extends android.support.media.tv.BaseProgram.Builder {
+    ctor public BasePreviewProgram.Builder();
+    ctor public BasePreviewProgram.Builder(android.support.media.tv.BasePreviewProgram);
+    method public T setAuthor(java.lang.String);
+    method public T setAvailability(int);
+    method public T setContentId(java.lang.String);
+    method public T setDurationMillis(int);
+    method public T setIntent(android.content.Intent);
+    method public T setIntentUri(android.net.Uri);
+    method public T setInteractionCount(long);
+    method public T setInteractionType(int);
+    method public T setInternalProviderId(java.lang.String);
+    method public T setItemCount(int);
+    method public T setLastPlaybackPositionMillis(int);
+    method public T setLive(boolean);
+    method public T setLogoUri(android.net.Uri);
+    method public T setOfferPrice(java.lang.String);
+    method public T setPosterArtAspectRatio(int);
+    method public T setPreviewVideoUri(android.net.Uri);
+    method public T setReleaseDate(java.lang.String);
+    method public T setReleaseDate(java.util.Date);
+    method public T setStartingPrice(java.lang.String);
+    method public T setThumbnailAspectRatio(int);
+    method public T setTransient(boolean);
+    method public T setType(int);
+  }
+
+  public abstract class BaseProgram {
+    method public java.lang.String[] getAudioLanguages();
+    method public java.lang.String[] getCanonicalGenres();
+    method public android.media.tv.TvContentRating[] getContentRatings();
+    method public java.lang.String getDescription();
+    method public java.lang.String getEpisodeNumber();
+    method public java.lang.String getEpisodeTitle();
+    method public long getId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getLongDescription();
+    method public android.net.Uri getPosterArtUri();
+    method public java.lang.String getReviewRating();
+    method public int getReviewRatingStyle();
+    method public java.lang.String getSeasonNumber();
+    method public java.lang.String getSeasonTitle();
+    method public android.net.Uri getThumbnailUri();
+    method public java.lang.String getTitle();
+    method public int getVideoHeight();
+    method public int getVideoWidth();
+    method public boolean isSearchable();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static abstract class BaseProgram.Builder<T extends android.support.media.tv.BaseProgram.Builder> {
+    ctor public BaseProgram.Builder();
+    ctor public BaseProgram.Builder(android.support.media.tv.BaseProgram);
+    method public T setAudioLanguages(java.lang.String[]);
+    method public T setCanonicalGenres(java.lang.String[]);
+    method public T setContentRatings(android.media.tv.TvContentRating[]);
+    method public T setDescription(java.lang.String);
+    method public T setEpisodeNumber(int);
+    method public T setEpisodeNumber(java.lang.String, int);
+    method public T setEpisodeTitle(java.lang.String);
+    method public T setId(long);
+    method public T setInternalProviderData(byte[]);
+    method public T setInternalProviderFlag1(long);
+    method public T setInternalProviderFlag2(long);
+    method public T setInternalProviderFlag3(long);
+    method public T setInternalProviderFlag4(long);
+    method public T setLongDescription(java.lang.String);
+    method public T setPosterArtUri(android.net.Uri);
+    method public T setReviewRating(java.lang.String);
+    method public T setReviewRatingStyle(int);
+    method public T setSearchable(boolean);
+    method public T setSeasonNumber(int);
+    method public T setSeasonNumber(java.lang.String, int);
+    method public T setSeasonTitle(java.lang.String);
+    method public T setThumbnailUri(android.net.Uri);
+    method public T setTitle(java.lang.String);
+    method public T setVideoHeight(int);
+    method public T setVideoWidth(int);
+  }
+
+  public final class Channel {
+    method public static android.support.media.tv.Channel fromCursor(android.database.Cursor);
+    method public int getAppLinkColor();
+    method public android.net.Uri getAppLinkIconUri();
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public android.net.Uri getAppLinkPosterArtUri();
+    method public java.lang.String getAppLinkText();
+    method public java.lang.String getDescription();
+    method public java.lang.String getDisplayName();
+    method public java.lang.String getDisplayNumber();
+    method public long getId();
+    method public java.lang.String getInputId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getInternalProviderId();
+    method public java.lang.String getNetworkAffiliation();
+    method public int getOriginalNetworkId();
+    method public java.lang.String getPackageName();
+    method public int getServiceId();
+    method public java.lang.String getServiceType();
+    method public int getTransportStreamId();
+    method public java.lang.String getType();
+    method public java.lang.String getVideoFormat();
+    method public boolean isBrowsable();
+    method public boolean isLocked();
+    method public boolean isSearchable();
+    method public boolean isTransient();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static final class Channel.Builder {
+    ctor public Channel.Builder();
+    ctor public Channel.Builder(android.support.media.tv.Channel);
+    method public android.support.media.tv.Channel build();
+    method public android.support.media.tv.Channel.Builder setAppLinkColor(int);
+    method public android.support.media.tv.Channel.Builder setAppLinkIconUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkPosterArtUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkText(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDescription(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayName(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayNumber(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInputId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setNetworkAffiliation(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setOriginalNetworkId(int);
+    method public android.support.media.tv.Channel.Builder setSearchable(boolean);
+    method public android.support.media.tv.Channel.Builder setServiceId(int);
+    method public android.support.media.tv.Channel.Builder setServiceType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setTransient(boolean);
+    method public android.support.media.tv.Channel.Builder setTransportStreamId(int);
+    method public android.support.media.tv.Channel.Builder setType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
+  }
+
+  public class ChannelLogoUtils {
+    ctor public ChannelLogoUtils();
+    method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
+  }
+
+  public final class PreviewProgram extends android.support.media.tv.BasePreviewProgram {
+    method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
+    method public long getChannelId();
+    method public int getWeight();
+  }
+
+  public static final class PreviewProgram.Builder extends android.support.media.tv.BasePreviewProgram.Builder {
+    ctor public PreviewProgram.Builder();
+    ctor public PreviewProgram.Builder(android.support.media.tv.PreviewProgram);
+    method public android.support.media.tv.PreviewProgram build();
+    method public android.support.media.tv.PreviewProgram.Builder setChannelId(long);
+    method public android.support.media.tv.PreviewProgram.Builder setWeight(int);
+  }
+
+  public final class Program extends android.support.media.tv.BaseProgram implements java.lang.Comparable {
+    method public int compareTo(android.support.media.tv.Program);
+    method public static android.support.media.tv.Program fromCursor(android.database.Cursor);
+    method public java.lang.String[] getBroadcastGenres();
+    method public long getChannelId();
+    method public long getEndTimeUtcMillis();
+    method public long getStartTimeUtcMillis();
+    method public boolean isRecordingProhibited();
+  }
+
+  public static class Program.Builder extends android.support.media.tv.BaseProgram.Builder {
+    ctor public Program.Builder();
+    ctor public Program.Builder(android.support.media.tv.Program);
+    method public android.support.media.tv.Program build();
+    method public android.support.media.tv.Program.Builder setBroadcastGenres(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setChannelId(long);
+    method public android.support.media.tv.Program.Builder setEndTimeUtcMillis(long);
+    method public android.support.media.tv.Program.Builder setRecordingProhibited(boolean);
+    method public android.support.media.tv.Program.Builder setStartTimeUtcMillis(long);
+  }
+
+  public final class TvContractCompat {
+    method public static android.net.Uri buildChannelLogoUri(long);
+    method public static android.net.Uri buildChannelLogoUri(android.net.Uri);
+    method public static android.net.Uri buildChannelUri(long);
+    method public static android.net.Uri buildChannelUriForPassthroughInput(java.lang.String);
+    method public static android.net.Uri buildChannelsUriForInput(java.lang.String);
+    method public static java.lang.String buildInputId(android.content.ComponentName);
+    method public static android.net.Uri buildPreviewProgramUri(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramUri(long);
+    method public static android.net.Uri buildProgramsUriForChannel(long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramsUriForChannel(long, long, long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static android.net.Uri buildRecordedProgramUri(long);
+    method public static android.net.Uri buildWatchNextProgramUri(long);
+    method public static boolean isChannelUri(android.net.Uri);
+    method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
+    method public static boolean isChannelUriForTunerInput(android.net.Uri);
+    method public static boolean isProgramUri(android.net.Uri);
+    method public static void requestChannelBrowsable(android.content.Context, long);
+    field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
+    field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String AUTHORITY = "android.media.tv";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
+    field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
+  }
+
+  public static abstract interface TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
+  }
+
+  public static final class TvContractCompat.Channels implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    method public static java.lang.String getVideoResolution(java.lang.String);
+    field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
+    field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
+    field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
+    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_LOCKED = "locked";
+    field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
+    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+    field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+    field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+    field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+    field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+    field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+    field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+    field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+    field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+    field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+    field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+    field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+    field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+    field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+    field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+    field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+    field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+    field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+    field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+    field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+    field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+    field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+    field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+    field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
+    field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+    field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+    field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+  }
+
+  public static final class TvContractCompat.Channels.Logo {
+    field public static final java.lang.String CONTENT_DIRECTORY = "logo";
+  }
+
+  public static final class TvContractCompat.PreviewPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WEIGHT = "weight";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs.Genres {
+    method public static java.lang.String[] decode(java.lang.String);
+    method public static java.lang.String encode(java.lang.String...);
+    method public static boolean isCanonical(java.lang.String);
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String ARTS = "ARTS";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String ENTERTAINMENT = "ENTERTAINMENT";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String LIFE_STYLE = "LIFE_STYLE";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String MUSIC = "MUSIC";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String PREMIER = "PREMIER";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TECH_SCIENCE = "TECH_SCIENCE";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
+  }
+
+  public static final class TvContractCompat.RecordedPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.WatchNextPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0
+    field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2
+    field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3
+  }
+
+  public final class WatchNextProgram extends android.support.media.tv.BasePreviewProgram {
+    method public static android.support.media.tv.WatchNextProgram fromCursor(android.database.Cursor);
+    method public long getLastEngagementTimeUtcMillis();
+    method public int getWatchNextType();
+  }
+
+  public static final class WatchNextProgram.Builder extends android.support.media.tv.BasePreviewProgram.Builder {
+    ctor public WatchNextProgram.Builder();
+    ctor public WatchNextProgram.Builder(android.support.media.tv.WatchNextProgram);
+    method public android.support.media.tv.WatchNextProgram build();
+    method public android.support.media.tv.WatchNextProgram.Builder setLastEngagementTimeUtcMillis(long);
+    method public android.support.media.tv.WatchNextProgram.Builder setWatchNextType(int);
+  }
+
+}
+
+package android.support.percent {
+
+  public deprecated class PercentFrameLayout extends android.widget.FrameLayout {
+    ctor public PercentFrameLayout(android.content.Context);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static deprecated class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout.LayoutParams(int, int);
+    ctor public PercentFrameLayout.LayoutParams(int, int, int);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentLayoutHelper {
+    ctor public PercentLayoutHelper(android.view.ViewGroup);
+    method public void adjustChildren(int, int);
+    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+    method public boolean handleMeasuredStateTooSmall();
+    method public void restoreOriginalParams();
+  }
+
+  public static deprecated class PercentLayoutHelper.PercentLayoutInfo {
+    ctor public PercentLayoutHelper.PercentLayoutInfo();
+    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+    method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void fillMarginLayoutParams(android.view.View, android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public float aspectRatio;
+    field public float bottomMarginPercent;
+    field public float endMarginPercent;
+    field public float heightPercent;
+    field public float leftMarginPercent;
+    field public float rightMarginPercent;
+    field public float startMarginPercent;
+    field public float topMarginPercent;
+    field public float widthPercent;
+  }
+
+  public static abstract deprecated interface PercentLayoutHelper.PercentLayoutParams {
+    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentRelativeLayout extends android.widget.RelativeLayout {
+    ctor public PercentRelativeLayout(android.content.Context);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static deprecated class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout.LayoutParams(int, int);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+}
+
+package android.support.text.emoji {
+
+  public class EmojiCompat {
+    method public static android.support.text.emoji.EmojiCompat get();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, int, int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence, int);
+    method public static android.support.text.emoji.EmojiCompat init(android.support.text.emoji.EmojiCompat.Config);
+    method public java.lang.CharSequence process(java.lang.CharSequence);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int, int);
+    method public void registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final java.lang.String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_FAILURE = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCESS = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public static abstract class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataLoader);
+    method public android.support.text.emoji.EmojiCompat.Config registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config setReplaceAll(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+  }
+
+  public static abstract class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(java.lang.Throwable);
+    method public void onInitialized();
+  }
+
+  public static abstract class EmojiCompat.LoaderCallback {
+    ctor public EmojiCompat.LoaderCallback();
+    method public abstract void onFailed(java.lang.Throwable);
+    method public abstract void onLoaded(android.support.text.emoji.MetadataRepo);
+  }
+
+  public static abstract interface EmojiCompat.MetadataLoader {
+    method public abstract void load(android.support.text.emoji.EmojiCompat.LoaderCallback);
+  }
+
+  public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, java.lang.CharSequence, int, int, android.graphics.Paint.FontMetricsInt);
+  }
+
+  public class FontRequestEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, android.support.v4.provider.FontRequest);
+  }
+
+  public final class MetadataRepo {
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.content.res.AssetManager, java.lang.String) throws java.io.IOException;
+  }
+
+}
+
+package android.support.text.emoji.bundled {
+
+  public class BundledEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
+package android.support.text.emoji.widget {
+
+  public class EmojiAppCompatButton extends android.support.v7.widget.AppCompatButton {
+    ctor public EmojiAppCompatButton(android.content.Context);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiAppCompatEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public EmojiAppCompatEditText(android.content.Context);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
+  }
+
+  public class EmojiAppCompatTextView extends android.support.v7.widget.AppCompatTextView {
+    ctor public EmojiAppCompatTextView(android.content.Context);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
+  }
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
+    method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter[] getFilters(android.text.InputFilter[]);
+    method public void setAllCaps(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod wrapTransformationMethod(android.text.method.TransformationMethod);
+  }
+
+}
+
+package android.support.transition {
+
+  public class ArcMotion extends android.support.transition.PathMotion {
+    ctor public ArcMotion();
+    ctor public ArcMotion(android.content.Context, android.util.AttributeSet);
+    method public float getMaximumAngle();
+    method public float getMinimumHorizontalAngle();
+    method public float getMinimumVerticalAngle();
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public void setMaximumAngle(float);
+    method public void setMinimumHorizontalAngle(float);
+    method public void setMinimumVerticalAngle(float);
+  }
+
+  public class AutoTransition extends android.support.transition.TransitionSet {
+    ctor public AutoTransition();
+    ctor public AutoTransition(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class ChangeBounds extends android.support.transition.Transition {
+    ctor public ChangeBounds();
+    ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getResizeClip();
+    method public void setResizeClip(boolean);
+  }
+
+  public class ChangeClipBounds extends android.support.transition.Transition {
+    ctor public ChangeClipBounds();
+    ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeImageTransform extends android.support.transition.Transition {
+    ctor public ChangeImageTransform();
+    ctor public ChangeImageTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeScroll extends android.support.transition.Transition {
+    ctor public ChangeScroll();
+    ctor public ChangeScroll(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeTransform extends android.support.transition.Transition {
+    ctor public ChangeTransform();
+    ctor public ChangeTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getReparent();
+    method public boolean getReparentWithOverlay();
+    method public void setReparent(boolean);
+    method public void setReparentWithOverlay(boolean);
+  }
+
+  public class CircularPropagation extends android.support.transition.VisibilityPropagation {
+    ctor public CircularPropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+  }
+
+  public class Explode extends android.support.transition.Visibility {
+    ctor public Explode();
+    ctor public Explode(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class Fade extends android.support.transition.Visibility {
+    ctor public Fade(int);
+    ctor public Fade();
+    ctor public Fade(android.content.Context, android.util.AttributeSet);
+    field public static final int IN = 1; // 0x1
+    field public static final int OUT = 2; // 0x2
+  }
+
+  public abstract class PathMotion {
+    ctor public PathMotion();
+    ctor public PathMotion(android.content.Context, android.util.AttributeSet);
+    method public abstract android.graphics.Path getPath(float, float, float, float);
+  }
+
+  public class PatternPathMotion extends android.support.transition.PathMotion {
+    ctor public PatternPathMotion();
+    ctor public PatternPathMotion(android.content.Context, android.util.AttributeSet);
+    ctor public PatternPathMotion(android.graphics.Path);
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public android.graphics.Path getPatternPath();
+    method public void setPatternPath(android.graphics.Path);
+  }
+
+  public class Scene {
+    ctor public Scene(android.view.ViewGroup);
+    ctor public Scene(android.view.ViewGroup, android.view.View);
+    method public void enter();
+    method public void exit();
+    method public static android.support.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
+    method public android.view.ViewGroup getSceneRoot();
+    method public void setEnterAction(java.lang.Runnable);
+    method public void setExitAction(java.lang.Runnable);
+  }
+
+  public class SidePropagation extends android.support.transition.VisibilityPropagation {
+    ctor public SidePropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+    method public void setSide(int);
+  }
+
+  public class Slide extends android.support.transition.Visibility {
+    ctor public Slide();
+    ctor public Slide(int);
+    ctor public Slide(android.content.Context, android.util.AttributeSet);
+    method public int getSlideEdge();
+    method public void setSlideEdge(int);
+  }
+
+  public abstract class Transition {
+    ctor public Transition();
+    ctor public Transition(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition addTarget(android.view.View);
+    method public android.support.transition.Transition addTarget(int);
+    method public android.support.transition.Transition addTarget(java.lang.String);
+    method public android.support.transition.Transition addTarget(java.lang.Class);
+    method public abstract void captureEndValues(android.support.transition.TransitionValues);
+    method public abstract void captureStartValues(android.support.transition.TransitionValues);
+    method public android.support.transition.Transition clone();
+    method public android.animation.Animator createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition excludeChildren(android.view.View, boolean);
+    method public android.support.transition.Transition excludeChildren(int, boolean);
+    method public android.support.transition.Transition excludeChildren(java.lang.Class, boolean);
+    method public android.support.transition.Transition excludeTarget(android.view.View, boolean);
+    method public android.support.transition.Transition excludeTarget(int, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
+    method public long getDuration();
+    method public android.graphics.Rect getEpicenter();
+    method public android.support.transition.Transition.EpicenterCallback getEpicenterCallback();
+    method public android.animation.TimeInterpolator getInterpolator();
+    method public java.lang.String getName();
+    method public android.support.transition.PathMotion getPathMotion();
+    method public android.support.transition.TransitionPropagation getPropagation();
+    method public long getStartDelay();
+    method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.String> getTargetNames();
+    method public java.util.List<java.lang.Class> getTargetTypes();
+    method public java.util.List<android.view.View> getTargets();
+    method public java.lang.String[] getTransitionProperties();
+    method public android.support.transition.TransitionValues getTransitionValues(android.view.View, boolean);
+    method public boolean isTransitionRequired(android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition removeTarget(android.view.View);
+    method public android.support.transition.Transition removeTarget(int);
+    method public android.support.transition.Transition removeTarget(java.lang.String);
+    method public android.support.transition.Transition removeTarget(java.lang.Class);
+    method public android.support.transition.Transition setDuration(long);
+    method public void setEpicenterCallback(android.support.transition.Transition.EpicenterCallback);
+    method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
+    method public void setMatchOrder(int...);
+    method public void setPathMotion(android.support.transition.PathMotion);
+    method public void setPropagation(android.support.transition.TransitionPropagation);
+    method public android.support.transition.Transition setStartDelay(long);
+    field public static final int MATCH_ID = 3; // 0x3
+    field public static final int MATCH_INSTANCE = 1; // 0x1
+    field public static final int MATCH_ITEM_ID = 4; // 0x4
+    field public static final int MATCH_NAME = 2; // 0x2
+  }
+
+  public static abstract class Transition.EpicenterCallback {
+    ctor public Transition.EpicenterCallback();
+    method public abstract android.graphics.Rect onGetEpicenter(android.support.transition.Transition);
+  }
+
+  public static abstract interface Transition.TransitionListener {
+    method public abstract void onTransitionCancel(android.support.transition.Transition);
+    method public abstract void onTransitionEnd(android.support.transition.Transition);
+    method public abstract void onTransitionPause(android.support.transition.Transition);
+    method public abstract void onTransitionResume(android.support.transition.Transition);
+    method public abstract void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionInflater {
+    method public static android.support.transition.TransitionInflater from(android.content.Context);
+    method public android.support.transition.Transition inflateTransition(int);
+    method public android.support.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+  }
+
+  public class TransitionManager {
+    ctor public TransitionManager();
+    method public static void beginDelayedTransition(android.view.ViewGroup);
+    method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
+    method public static void endTransitions(android.view.ViewGroup);
+    method public static void go(android.support.transition.Scene);
+    method public static void go(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Scene, android.support.transition.Transition);
+    method public void transitionTo(android.support.transition.Scene);
+  }
+
+  public abstract class TransitionPropagation {
+    ctor public TransitionPropagation();
+    method public abstract void captureValues(android.support.transition.TransitionValues);
+    method public abstract java.lang.String[] getPropagationProperties();
+    method public abstract long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+  }
+
+  public class TransitionSet extends android.support.transition.Transition {
+    ctor public TransitionSet();
+    ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getOrdering();
+    method public android.support.transition.Transition getTransitionAt(int);
+    method public int getTransitionCount();
+    method public android.support.transition.TransitionSet removeTransition(android.support.transition.Transition);
+    method public android.support.transition.TransitionSet setOrdering(int);
+    field public static final int ORDERING_SEQUENTIAL = 1; // 0x1
+    field public static final int ORDERING_TOGETHER = 0; // 0x0
+  }
+
+  public class TransitionValues {
+    ctor public TransitionValues();
+    field public final java.util.Map<java.lang.String, java.lang.Object> values;
+    field public android.view.View view;
+  }
+
+  public abstract class Visibility extends android.support.transition.Transition {
+    ctor public Visibility();
+    ctor public Visibility(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getMode();
+    method public boolean isVisible(android.support.transition.TransitionValues);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setMode(int);
+    field public static final int MODE_IN = 1; // 0x1
+    field public static final int MODE_OUT = 2; // 0x2
+  }
+
+  public abstract class VisibilityPropagation extends android.support.transition.TransitionPropagation {
+    ctor public VisibilityPropagation();
+    method public void captureValues(android.support.transition.TransitionValues);
+    method public java.lang.String[] getPropagationProperties();
+    method public int getViewVisibility(android.support.transition.TransitionValues);
+    method public int getViewX(android.support.transition.TransitionValues);
+    method public int getViewY(android.support.transition.TransitionValues);
+  }
+
+}
+
+package android.support.v13.app {
+
+  public class ActivityCompat extends android.support.v4.app.ActivityCompat {
+    ctor protected ActivityCompat();
+    method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+  }
+
+  public class FragmentCompat {
+    ctor public FragmentCompat();
+    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
+    method public static deprecated void setMenuVisibility(android.app.Fragment, boolean);
+    method public static void setUserVisibleHint(android.app.Fragment, boolean);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
+  }
+
+  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public void setup(android.content.Context, android.app.FragmentManager);
+    method public void setup(android.content.Context, android.app.FragmentManager, int);
+  }
+
+}
+
+package android.support.v13.view {
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, android.support.v13.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static abstract interface DragStartHelper.OnDragStartListener {
+    method public abstract boolean onDragStart(android.view.View, android.support.v13.view.DragStartHelper);
+  }
+
+  public deprecated class ViewCompat extends android.support.v4.view.ViewCompat {
+  }
+
+}
+
+package android.support.v13.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor public EditorInfoCompat();
+    method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
+  }
+
+  public static abstract interface InputConnectionCompat.OnCommitContentListener {
+    method public abstract boolean onCommitContent(android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public java.lang.Object unwrap();
+    method public static android.support.v13.view.inputmethod.InputContentInfoCompat wrap(java.lang.Object);
+  }
+
+}
+
+package android.support.v14.preference {
+
+  public class EditTextPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public EditTextPreferenceDialogFragment();
+    method public static android.support.v14.preference.EditTextPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public ListPreferenceDialogFragment();
+    method public static android.support.v14.preference.ListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public MultiSelectListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence[] getEntryValues();
+    method protected boolean[] getSelectedItems();
+    method public java.util.Set<java.lang.String> getValues();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValues(java.util.Set<java.lang.String>);
+  }
+
+  public class MultiSelectListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public MultiSelectListPreferenceDialogFragment();
+    method public static android.support.v14.preference.MultiSelectListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragment extends android.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragment();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public class SwitchPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreference(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+}
+
+package android.support.v17.leanback.app {
+
+  public final class BackgroundManager {
+    method public void attach(android.view.Window);
+    method public void attachToView(android.view.View);
+    method public void clearDrawable();
+    method public final int getColor();
+    method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
+    method public deprecated android.graphics.drawable.Drawable getDimLayer();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
+    method public boolean isAttached();
+    method public boolean isAutoReleaseOnStop();
+    method public void release();
+    method public void setAutoReleaseOnStop(boolean);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColor(int);
+    method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setThemeDrawableResourceId(int);
+  }
+
+  public class BaseFragment extends android.support.v17.leanback.app.BrandedFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+   abstract class BaseRowFragment extends android.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+   abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public class BaseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+  public class BrandedFragment extends android.app.Fragment {
+    ctor public BrandedFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrandedSupportFragment extends android.support.v4.app.Fragment {
+    ctor public BrandedSupportFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrowseFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public BrowseFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
+    method public int getHeadersState();
+    method public android.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersFragment onCreateHeadersFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseFragment.BrowseTransitionListener {
+    ctor public BrowseFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseFragment.FragmentFactory<T extends android.app.Fragment> {
+    ctor public BrowseFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseFragment.FragmentFactory {
+    ctor public BrowseFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseFragment.MainFragmentAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseFragment.MainFragmentAdapterRegistry();
+    method public android.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseFragment.FragmentFactory);
+  }
+
+  public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class BrowseSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public BrowseSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public int getHeadersState();
+    method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
+    method public android.support.v4.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersSupportFragment onCreateHeadersSupportFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseSupportFragment.BrowseTransitionListener {
+    ctor public BrowseSupportFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseSupportFragment.FragmentFactory<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseSupportFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseSupportFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory {
+    ctor public BrowseSupportFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsSupportFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseSupportFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseSupportFragment.MainFragmentAdapterRegistry();
+    method public android.support.v4.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class DetailsFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public DetailsFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsFragmentBackgroundController {
+    ctor public DetailsFragmentBackgroundController(android.support.v17.leanback.app.DetailsFragment);
+    method public boolean canNavigateToVideoFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.app.Fragment findOrCreateVideoFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.app.Fragment onCreateVideoFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class DetailsSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public DetailsSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsSupportFragmentBackgroundController {
+    ctor public DetailsSupportFragmentBackgroundController(android.support.v17.leanback.app.DetailsSupportFragment);
+    method public boolean canNavigateToVideoSupportFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class ErrorFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public ErrorFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class ErrorSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public ErrorSupportFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class GuidedStepFragment extends android.app.Fragment {
+    ctor public GuidedStepFragment();
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class GuidedStepSupportFragment extends android.support.v4.app.Fragment {
+    ctor public GuidedStepSupportFragment();
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepSupportFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment {
+    ctor public HeadersFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment {
+    ctor public HeadersSupportFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public abstract class OnboardingFragment extends android.app.Fragment {
+    ctor public OnboardingFragment();
+    method public final int getArrowBackgroundColor();
+    method public final int getArrowColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final java.lang.CharSequence getStartButtonText();
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setArrowColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setStartButtonText(java.lang.CharSequence);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract class OnboardingSupportFragment extends android.support.v4.app.Fragment {
+    ctor public OnboardingSupportFragment();
+    method public final int getArrowBackgroundColor();
+    method public final int getArrowColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final java.lang.CharSequence getStartButtonText();
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setArrowColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setStartButtonText(java.lang.CharSequence);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
+    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
+    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
+    method protected deprecated void pausePlayback();
+    method public final void play(int);
+    method public final void previous();
+    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method protected deprecated void skipToNext();
+    method protected deprecated void skipToPrevious();
+    method protected deprecated void startPlayback(int);
+  }
+
+  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
+    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  }
+
+  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public class PlaybackFragment extends android.app.Fragment {
+    ctor public PlaybackFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
+    ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlayFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlayFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+    ctor public PlaybackOverlaySupportFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
+    ctor public PlaybackSupportFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public final class ProgressBarManager {
+    ctor public ProgressBarManager();
+    method public void disableProgressBar();
+    method public void enableProgressBar();
+    method public long getInitialDelay();
+    method public void hide();
+    method public void setInitialDelay(long);
+    method public void setProgressBarView(android.view.View);
+    method public void setRootView(android.view.ViewGroup);
+    method public void show();
+  }
+
+  public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment implements android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter {
+    ctor public RowsFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public static class RowsFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter {
+    ctor public RowsFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment implements android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsSupportFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsSupportFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter {
+    ctor public RowsSupportFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public static class RowsSupportFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter {
+    ctor public RowsSupportFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public class SearchFragment extends android.app.Fragment {
+    ctor public SearchFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SearchSupportFragment extends android.support.v4.app.Fragment {
+    ctor public SearchSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchSupportFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class VerticalGridFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public VerticalGridFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public VerticalGridSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
+    ctor public VideoFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
+    ctor public VideoSupportFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.database {
+
+  public abstract class CursorMapper {
+    ctor public CursorMapper();
+    method protected abstract java.lang.Object bind(android.database.Cursor);
+    method protected abstract void bindColumns(android.database.Cursor);
+    method public java.lang.Object convert(android.database.Cursor);
+  }
+
+}
+
+package android.support.v17.leanback.graphics {
+
+  public class BoundsRule {
+    ctor public BoundsRule();
+    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
+    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule top;
+  }
+
+  public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
+    method public int getAbsoluteValue();
+    method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
+    method public void setAbsoluteValue(int);
+    method public void setFraction(float);
+  }
+
+  public final class ColorFilterCache {
+    method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
+    method public android.graphics.ColorFilter getFilterForLevel(float);
+  }
+
+  public final class ColorFilterDimmer {
+    method public void applyFilterToView(android.view.View);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context);
+    method public android.graphics.ColorFilter getColorFilter();
+    method public android.graphics.Paint getPaint();
+    method public void setActiveLevel(float);
+  }
+
+  public final class ColorOverlayDimmer {
+    method public int applyToColor(int);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context);
+    method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean);
+    method public int getAlpha();
+    method public float getAlphaFloat();
+    method public android.graphics.Paint getPaint();
+    method public boolean needsDraw();
+    method public void setActiveLevel(float);
+  }
+
+  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public CompositeDrawable();
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
+    method public int getChildCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void removeChild(int);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+  }
+
+  public static final class CompositeDrawable.ChildDrawable {
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
+    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void recomputeBounds();
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
+  }
+
+  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
+    ctor public FitWidthBitmapDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.Bitmap getBitmap();
+    method public int getOpacity();
+    method public android.graphics.Rect getSource();
+    method public int getVerticalOffset();
+    method public void setAlpha(int);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setSource(android.graphics.Rect);
+    method public void setVerticalOffset(int);
+    field public static final android.util.Property<android.support.v17.leanback.graphics.FitWidthBitmapDrawable, java.lang.Integer> PROPERTY_VERTICAL_OFFSET;
+  }
+
+}
+
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public class MediaPlayerAdapter extends android.support.v17.leanback.media.PlayerAdapter {
+    ctor public MediaPlayerAdapter(android.content.Context);
+    method protected boolean onError(int, int);
+    method protected boolean onInfo(int, int);
+    method protected void onSeekComplete();
+    method public void pause();
+    method public void play();
+    method public void release();
+    method public void reset();
+    method public boolean setDataSource(android.net.Uri);
+  }
+
+  public class PlaybackBannerControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackBaseControlGlue {
+    ctor public PlaybackBannerControlGlue(android.content.Context, int[], T);
+    ctor public PlaybackBannerControlGlue(android.content.Context, int[], int[], T);
+    method public int[] getFastForwardSpeeds();
+    method public int[] getRewindSpeeds();
+    method public long getSupportedActions();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackBaseControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackBaseControlGlue(android.content.Context, T);
+    method public android.graphics.drawable.Drawable getArt();
+    method public final long getBufferedPosition();
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public long getCurrentPosition();
+    method public final long getDuration();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public final T getPlayerAdapter();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public final boolean isPlaying();
+    method public final boolean isPrepared();
+    method protected static void notifyItemChanged(android.support.v17.leanback.widget.ArrayObjectAdapter, java.lang.Object);
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method protected abstract android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onPlayCompleted();
+    method protected void onPlayStateChanged();
+    method protected void onPreparedStateChanged();
+    method public final void seekTo(long);
+    method public void setArt(android.graphics.drawable.Drawable);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public deprecated android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public deprecated void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public void addPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method protected java.util.List<android.support.v17.leanback.media.PlaybackGlue.PlayerCallback> getPlayerCallbacks();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public deprecated boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public void removePlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public deprecated void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public deprecated void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.media.PlaybackGlueHost.PlayerCallback getPlayerCallback();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public void notifyPlaybackRowChanged();
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void showControlsOverlay(boolean);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostDestroy();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public static class PlaybackGlueHost.PlayerCallback {
+    ctor public PlaybackGlueHost.PlayerCallback();
+    method public void onBufferingStateChanged(boolean);
+    method public void onError(int, java.lang.CharSequence);
+    method public void onVideoSizeChanged(int, int);
+  }
+
+  public class PlaybackTransportControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackBaseControlGlue {
+    ctor public PlaybackTransportControlGlue(android.content.Context, T);
+    method public final android.support.v17.leanback.widget.PlaybackSeekDataProvider getSeekProvider();
+    method public final boolean isSeekEnabled();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method public final void setSeekEnabled(boolean);
+    method public final void setSeekProvider(android.support.v17.leanback.widget.PlaybackSeekDataProvider);
+  }
+
+  public abstract class PlayerAdapter {
+    ctor public PlayerAdapter();
+    method public long getBufferedPosition();
+    method public final android.support.v17.leanback.media.PlayerAdapter.Callback getCallback();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void onDetachedFromHost();
+    method public abstract void pause();
+    method public abstract void play();
+    method public void seekTo(long);
+    method public final void setCallback(android.support.v17.leanback.media.PlayerAdapter.Callback);
+    method public void setProgressUpdatingEnabled(boolean);
+  }
+
+  public static class PlayerAdapter.Callback {
+    ctor public PlayerAdapter.Callback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onBufferingStateChanged(android.support.v17.leanback.media.PlayerAdapter, boolean);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onDurationChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onError(android.support.v17.leanback.media.PlayerAdapter, int, java.lang.String);
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onVideoSizeChanged(android.support.v17.leanback.media.PlayerAdapter, int, int);
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.system {
+
+  public class Settings {
+    method public boolean getBoolean(java.lang.String);
+    method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context);
+    method public void setBoolean(java.lang.String, boolean);
+    field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
+  }
+
+}
+
+package android.support.v17.leanback.widget {
+
+  public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public AbstractDetailsDescriptionPresenter();
+    method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getBody();
+    method public android.widget.TextView getSubtitle();
+    method public android.widget.TextView getTitle();
+  }
+
+  public abstract class AbstractMediaItemPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaItemPresenter();
+    ctor public AbstractMediaItemPresenter(int);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter getActionPresenter();
+    method protected int getMediaPlayState(java.lang.Object);
+    method public int getThemeId();
+    method public boolean hasMediaRowSeparator();
+    method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
+    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setBackgroundColor(int);
+    method public void setHasMediaRowSeparator(boolean);
+    method public void setThemeId(int);
+    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
+    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
+  }
+
+  public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaItemPresenter.ViewHolder(android.view.View);
+    method public android.view.ViewGroup getMediaItemActionsContainer();
+    method public android.view.View getMediaItemDetailsView();
+    method public android.widget.TextView getMediaItemDurationView();
+    method public android.widget.TextView getMediaItemNameView();
+    method public android.widget.TextView getMediaItemNumberView();
+    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
+    method public android.view.View getMediaItemPausedView();
+    method public android.view.View getMediaItemPlayingView();
+    method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
+    method public android.view.View getMediaItemRowSeparator();
+    method public android.view.View getSelectorView();
+    method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
+    method public void notifyDetailsChanged();
+    method public void notifyPlayStateChanged();
+    method public void onBindRowActions();
+    method public void setSelectedMediaItemNumberView(int);
+  }
+
+  public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaListHeaderPresenter(android.content.Context, int);
+    ctor public AbstractMediaListHeaderPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected abstract void onBindMediaListHeaderViewHolder(android.support.v17.leanback.widget.AbstractMediaListHeaderPresenter.ViewHolder, java.lang.Object);
+    method public void setBackgroundColor(int);
+  }
+
+  public static class AbstractMediaListHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaListHeaderPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getHeaderView();
+  }
+
+  public class Action {
+    ctor public Action(long);
+    ctor public Action(long, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable);
+    method public final void addKeyCode(int);
+    method public final android.graphics.drawable.Drawable getIcon();
+    method public final long getId();
+    method public final java.lang.CharSequence getLabel1();
+    method public final java.lang.CharSequence getLabel2();
+    method public final void removeKeyCode(int);
+    method public final boolean respondsToKeyCode(int);
+    method public final void setIcon(android.graphics.drawable.Drawable);
+    method public final void setId(long);
+    method public final void setLabel1(java.lang.CharSequence);
+    method public final void setLabel2(java.lang.CharSequence);
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+  }
+
+  public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ArrayObjectAdapter();
+    method public void add(java.lang.Object);
+    method public void add(int, java.lang.Object);
+    method public void addAll(int, java.util.Collection);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public boolean remove(java.lang.Object);
+    method public int removeItems(int, int);
+    method public void replace(int, java.lang.Object);
+    method public int size();
+    method public <E> java.util.List<E> unmodifiableList();
+  }
+
+  public class BaseCardView extends android.widget.FrameLayout {
+    ctor public BaseCardView(android.content.Context);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
+    method public int getCardType();
+    method public deprecated int getExtraVisibility();
+    method public int getInfoVisibility();
+    method public boolean isSelectedAnimationDelayed();
+    method public void setCardType(int);
+    method public deprecated void setExtraVisibility(int);
+    method public void setInfoVisibility(int);
+    method public void setSelectedAnimationDelayed(boolean);
+    field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
+    field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0
+    field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1
+    field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3
+    field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0
+  }
+
+  public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView.LayoutParams(int, int);
+    ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams);
+    field public static final int VIEW_TYPE_EXTRA = 2; // 0x2
+    field public static final int VIEW_TYPE_INFO = 1; // 0x1
+    field public static final int VIEW_TYPE_MAIN = 0; // 0x0
+    field public int viewType;
+  }
+
+  public abstract class BaseGridView extends android.support.v7.widget.RecyclerView {
+    method public void addOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void animateIn();
+    method public void animateOut();
+    method public int getChildDrawingOrder(int, int);
+    method public deprecated int getHorizontalMargin();
+    method public int getHorizontalSpacing();
+    method public int getInitialPrefetchItemCount();
+    method public int getItemAlignmentOffset();
+    method public float getItemAlignmentOffsetPercent();
+    method public int getItemAlignmentViewId();
+    method public android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener getOnUnhandledKeyListener();
+    method public final int getSaveChildrenLimitNumber();
+    method public final int getSaveChildrenPolicy();
+    method public int getSelectedPosition();
+    method public deprecated int getVerticalMargin();
+    method public int getVerticalSpacing();
+    method public void getViewSelectedOffsets(android.view.View, int[]);
+    method public int getWindowAlignment();
+    method public int getWindowAlignmentOffset();
+    method public float getWindowAlignmentOffsetPercent();
+    method public boolean hasPreviousViewInSameRow(int);
+    method public boolean isChildLayoutAnimated();
+    method public boolean isFocusDrawingOrderEnabled();
+    method public final boolean isFocusSearchDisabled();
+    method public boolean isItemAlignmentOffsetWithPadding();
+    method public boolean isScrollEnabled();
+    method public boolean isWindowAlignmentPreferKeyLineOverHighEdge();
+    method public boolean isWindowAlignmentPreferKeyLineOverLowEdge();
+    method public boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+    method public void removeOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setAnimateChildLayout(boolean);
+    method public void setChildrenVisibility(int);
+    method public void setFocusDrawingOrderEnabled(boolean);
+    method public final void setFocusSearchDisabled(boolean);
+    method public void setGravity(int);
+    method public void setHasOverlappingRendering(boolean);
+    method public deprecated void setHorizontalMargin(int);
+    method public void setHorizontalSpacing(int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setItemAlignmentOffset(int);
+    method public void setItemAlignmentOffsetPercent(float);
+    method public void setItemAlignmentOffsetWithPadding(boolean);
+    method public void setItemAlignmentViewId(int);
+    method public deprecated void setItemMargin(int);
+    method public void setItemSpacing(int);
+    method public void setLayoutEnabled(boolean);
+    method public void setOnChildLaidOutListener(android.support.v17.leanback.widget.OnChildLaidOutListener);
+    method public void setOnChildSelectedListener(android.support.v17.leanback.widget.OnChildSelectedListener);
+    method public void setOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setOnKeyInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnKeyInterceptListener);
+    method public void setOnMotionInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnMotionInterceptListener);
+    method public void setOnTouchInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnTouchInterceptListener);
+    method public void setOnUnhandledKeyListener(android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener);
+    method public void setPruneChild(boolean);
+    method public final void setSaveChildrenLimitNumber(int);
+    method public final void setSaveChildrenPolicy(int);
+    method public void setScrollEnabled(boolean);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, int);
+    method public void setSelectedPosition(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public void setSelectedPositionSmooth(int);
+    method public void setSelectedPositionSmooth(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public deprecated void setVerticalMargin(int);
+    method public void setVerticalSpacing(int);
+    method public void setWindowAlignment(int);
+    method public void setWindowAlignmentOffset(int);
+    method public void setWindowAlignmentOffsetPercent(float);
+    method public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean);
+    method public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+    field public static final int SAVE_ALL_CHILD = 3; // 0x3
+    field public static final int SAVE_LIMITED_CHILD = 2; // 0x2
+    field public static final int SAVE_NO_CHILD = 0; // 0x0
+    field public static final int SAVE_ON_SCREEN_CHILD = 1; // 0x1
+    field public static final int WINDOW_ALIGN_BOTH_EDGE = 3; // 0x3
+    field public static final int WINDOW_ALIGN_HIGH_EDGE = 2; // 0x2
+    field public static final int WINDOW_ALIGN_LOW_EDGE = 1; // 0x1
+    field public static final int WINDOW_ALIGN_NO_EDGE = 0; // 0x0
+    field public static final float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static abstract interface BaseGridView.OnKeyInterceptListener {
+    method public abstract boolean onInterceptKeyEvent(android.view.KeyEvent);
+  }
+
+  public static abstract interface BaseGridView.OnMotionInterceptListener {
+    method public abstract boolean onInterceptMotionEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnTouchInterceptListener {
+    method public abstract boolean onInterceptTouchEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnUnhandledKeyListener {
+    method public abstract boolean onUnhandledKey(android.view.KeyEvent);
+  }
+
+  public abstract interface BaseOnItemViewClickedListener<T> {
+    method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public abstract interface BaseOnItemViewSelectedListener<T> {
+    method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public class BrowseFrameLayout extends android.widget.FrameLayout {
+    ctor public BrowseFrameLayout(android.content.Context);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
+    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
+    method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnChildFocusListener {
+    method public abstract void onRequestChildFocus(android.view.View, android.view.View);
+    method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnFocusSearchListener {
+    method public abstract android.view.View onFocusSearch(android.view.View, int);
+  }
+
+  public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ClassPresenterSelector();
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenterSelector(java.lang.Class<?>, android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ControlButtonPresenterSelector();
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter();
+    method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter();
+  }
+
+  public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public CursorObjectAdapter();
+    method public void changeCursor(android.database.Cursor);
+    method public void close();
+    method public java.lang.Object get(int);
+    method public final android.database.Cursor getCursor();
+    method public final android.support.v17.leanback.database.CursorMapper getMapper();
+    method protected final void invalidateCache(int);
+    method protected final void invalidateCache(int, int);
+    method public boolean isClosed();
+    method protected void onCursorChanged();
+    method protected void onMapperChanged();
+    method public final void setMapper(android.support.v17.leanback.database.CursorMapper);
+    method public int size();
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+  }
+
+  public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DetailsOverviewLogoPresenter();
+    method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+  }
+
+  public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter getParentPresenter();
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder();
+    method public boolean isSizeFromDrawableIntrinsic();
+    method public void setSizeFromDrawableIntrinsic(boolean);
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter;
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
+  }
+
+  public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row {
+    ctor public DetailsOverviewRow(java.lang.Object);
+    method public final deprecated void addAction(android.support.v17.leanback.widget.Action);
+    method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public boolean isImageScaleUpAllowed();
+    method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action);
+    method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setImageScaleUpAllowed(boolean);
+    method public final void setItem(java.lang.Object);
+  }
+
+  public static class DetailsOverviewRow.Listener {
+    ctor public DetailsOverviewRow.Listener();
+    method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+  }
+
+  public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public boolean isStyleLarge();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setStyleLarge(boolean);
+  }
+
+  public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder;
+  }
+
+  public class DetailsParallax extends android.support.v17.leanback.widget.RecyclerViewParallax {
+    ctor public DetailsParallax();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowBottom();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowTop();
+  }
+
+  public class DividerPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DividerPresenter();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public class DividerRow extends android.support.v17.leanback.widget.Row {
+    ctor public DividerRow();
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract interface FacetProvider {
+    method public abstract java.lang.Object getFacet(java.lang.Class<?>);
+  }
+
+  public abstract interface FacetProviderAdapter {
+    method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+  }
+
+  public abstract interface FocusHighlight {
+    field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3
+    field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2
+    field public static final int ZOOM_FACTOR_NONE = 0; // 0x0
+    field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1
+    field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4
+  }
+
+  public class FocusHighlightHelper {
+    ctor public FocusHighlightHelper();
+    method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean);
+    method public static deprecated void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView);
+    method public static deprecated void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView, boolean);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, boolean);
+  }
+
+  public abstract interface FragmentAnimationProvider {
+    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public final int getActionsBackgroundColor();
+    method public final int getAlignmentMode();
+    method public final int getBackgroundColor();
+    method public final int getInitialState();
+    method protected int getLayoutResourceId();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public final boolean isParticipatingEntranceTransition();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+    method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    method public final void setActionsBackgroundColor(int);
+    method public final void setAlignmentMode(int);
+    method public final void setBackgroundColor(int);
+    method public final void setInitialState(int);
+    method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setParticipatingEntranceTransition(boolean);
+    method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1
+    field public static final int ALIGN_MODE_START = 0; // 0x0
+    field public static final int STATE_FULL = 1; // 0x1
+    field public static final int STATE_HALF = 0; // 0x0
+    field public static final int STATE_SMALL = 2; // 0x2
+    field protected int mInitialState;
+  }
+
+  public static abstract class FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.Listener();
+    method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener();
+    method public final android.view.ViewGroup getActionsRow();
+    method public final android.view.ViewGroup getDetailsDescriptionFrame();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder();
+    method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder();
+    method public final android.view.ViewGroup getOverviewView();
+    method public final int getState();
+    field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener;
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener();
+  }
+
+  public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewSharedElementHelper();
+    method public boolean getAutoStartSharedElementTransition();
+    method public void setAutoStartSharedElementTransition(boolean);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public void startPostponedEnterTransition();
+  }
+
+  public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidanceStylist();
+    method public android.widget.TextView getBreadcrumbView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance);
+    method public void onDestroyView();
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideLayoutId();
+  }
+
+  public static class GuidanceStylist.Guidance {
+    ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable);
+    method public java.lang.String getBreadcrumb();
+    method public java.lang.String getDescription();
+    method public android.graphics.drawable.Drawable getIconDrawable();
+    method public java.lang.String getTitle();
+  }
+
+  public class GuidedAction extends android.support.v17.leanback.widget.Action {
+    ctor protected GuidedAction();
+    method public int getCheckSetId();
+    method public java.lang.CharSequence getDescription();
+    method public int getDescriptionEditInputType();
+    method public int getDescriptionInputType();
+    method public java.lang.CharSequence getEditDescription();
+    method public int getEditInputType();
+    method public java.lang.CharSequence getEditTitle();
+    method public int getInputType();
+    method public android.content.Intent getIntent();
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getSubActions();
+    method public java.lang.CharSequence getTitle();
+    method public boolean hasEditableActivatorView();
+    method public boolean hasMultilineDescription();
+    method public boolean hasNext();
+    method public boolean hasSubActions();
+    method public boolean hasTextEditable();
+    method public boolean infoOnly();
+    method public final boolean isAutoSaveRestoreEnabled();
+    method public boolean isChecked();
+    method public boolean isDescriptionEditable();
+    method public boolean isEditTitleUsed();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public void onRestoreInstanceState(android.os.Bundle, java.lang.String);
+    method public void onSaveInstanceState(android.os.Bundle, java.lang.String);
+    method public void setChecked(boolean);
+    method public void setDescription(java.lang.CharSequence);
+    method public void setEditDescription(java.lang.CharSequence);
+    method public void setEditTitle(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setSubActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setTitle(java.lang.CharSequence);
+    field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
+    field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
+    field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
+    field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL
+    field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL
+    field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
+    field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
+    field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
+    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
+    field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
+    field public static final int NO_CHECK_SET = 0; // 0x0
+  }
+
+  public static class GuidedAction.Builder extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public deprecated GuidedAction.Builder();
+    ctor public GuidedAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedAction build();
+  }
+
+  public static abstract class GuidedAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedAction.BuilderBase> {
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction);
+    method public B autoSaveRestoreEnabled(boolean);
+    method public B checkSetId(int);
+    method public B checked(boolean);
+    method public B clickAction(long);
+    method public B description(java.lang.CharSequence);
+    method public B description(int);
+    method public B descriptionEditInputType(int);
+    method public B descriptionEditable(boolean);
+    method public B descriptionInputType(int);
+    method public B editDescription(java.lang.CharSequence);
+    method public B editDescription(int);
+    method public B editInputType(int);
+    method public B editTitle(java.lang.CharSequence);
+    method public B editTitle(int);
+    method public B editable(boolean);
+    method public B enabled(boolean);
+    method public B focusable(boolean);
+    method public android.content.Context getContext();
+    method public B hasEditableActivatorView(boolean);
+    method public B hasNext(boolean);
+    method public B icon(android.graphics.drawable.Drawable);
+    method public B icon(int);
+    method public deprecated B iconResourceId(int, android.content.Context);
+    method public B id(long);
+    method public B infoOnly(boolean);
+    method public B inputType(int);
+    method public B intent(android.content.Intent);
+    method public B multilineDescription(boolean);
+    method public B subActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public B title(java.lang.CharSequence);
+    method public B title(int);
+  }
+
+  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
+    ctor public GuidedActionEditText(android.content.Context);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidedActionsStylist();
+    method public void collapseAction(boolean);
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
+    method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
+    method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
+    method public final boolean isBackKeyToCollapseActivatorView();
+    method public final boolean isBackKeyToCollapseSubActions();
+    method public boolean isButtonActions();
+    method public boolean isExpandTransitionSupported();
+    method public boolean isExpanded();
+    method public boolean isInExpandTransition();
+    method public boolean isSubActionsExpanded();
+    method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void onBindActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDestroyView();
+    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideItemLayoutId();
+    method public int onProvideItemLayoutId(int);
+    method public int onProvideLayoutId();
+    method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void setAsButtonActions();
+    method public final void setBackKeyToCollapseActivatorView(boolean);
+    method public final void setBackKeyToCollapseSubActions(boolean);
+    method public deprecated void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
+    field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
+  }
+
+  public static class GuidedActionsStylist.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View);
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View, boolean);
+    method public android.support.v17.leanback.widget.GuidedAction getAction();
+    method public android.widget.ImageView getCheckmarkView();
+    method public android.widget.ImageView getChevronView();
+    method public android.view.View getContentView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.EditText getEditableDescriptionView();
+    method public android.widget.EditText getEditableTitleView();
+    method public android.view.View getEditingView();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public boolean isInEditing();
+    method public boolean isInEditingActivatorView();
+    method public boolean isInEditingDescription();
+    method public boolean isInEditingText();
+    method public boolean isInEditingTitle();
+    method public boolean isSubAction();
+  }
+
+  public class GuidedDatePickerAction extends android.support.v17.leanback.widget.GuidedAction {
+    ctor public GuidedDatePickerAction();
+    method public long getDate();
+    method public java.lang.String getDatePickerFormat();
+    method public long getMaxDate();
+    method public long getMinDate();
+    method public void setDate(long);
+  }
+
+  public static final class GuidedDatePickerAction.Builder extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase {
+    ctor public GuidedDatePickerAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedDatePickerAction build();
+  }
+
+  public static abstract class GuidedDatePickerAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase> extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public GuidedDatePickerAction.BuilderBase(android.content.Context);
+    method protected final void applyDatePickerValues(android.support.v17.leanback.widget.GuidedDatePickerAction);
+    method public B date(long);
+    method public B datePickerFormat(java.lang.String);
+    method public B maxDate(long);
+    method public B minDate(long);
+  }
+
+  public class HeaderItem {
+    ctor public HeaderItem(long, java.lang.String);
+    ctor public HeaderItem(java.lang.String);
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getDescription();
+    method public final long getId();
+    method public final java.lang.String getName();
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setDescription(java.lang.CharSequence);
+  }
+
+  public class HorizontalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public HorizontalGridView(android.content.Context);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int);
+    method public final boolean getFadingLeftEdge();
+    method public final int getFadingLeftEdgeLength();
+    method public final int getFadingLeftEdgeOffset();
+    method public final boolean getFadingRightEdge();
+    method public final int getFadingRightEdgeLength();
+    method public final int getFadingRightEdgeOffset();
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public final void setFadingLeftEdge(boolean);
+    method public final void setFadingLeftEdgeLength(int);
+    method public final void setFadingLeftEdgeOffset(int);
+    method public final void setFadingRightEdge(boolean);
+    method public final void setFadingRightEdgeLength(int);
+    method public final void setFadingRightEdgeOffset(int);
+    method public void setNumRows(int);
+    method public void setRowHeight(int);
+  }
+
+  public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher {
+    ctor public HorizontalHoverCardSwitcher();
+    method protected void insertView(android.view.View);
+    method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object);
+  }
+
+  public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
+    ctor public deprecated ImageCardView(android.content.Context, int);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
+    ctor public ImageCardView(android.content.Context);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
+    method public android.graphics.drawable.Drawable getBadgeImage();
+    method public java.lang.CharSequence getContentText();
+    method public android.graphics.drawable.Drawable getInfoAreaBackground();
+    method public android.graphics.drawable.Drawable getMainImage();
+    method public final android.widget.ImageView getMainImageView();
+    method public java.lang.CharSequence getTitleText();
+    method public void setBadgeImage(android.graphics.drawable.Drawable);
+    method public void setContentText(java.lang.CharSequence);
+    method public void setInfoAreaBackground(android.graphics.drawable.Drawable);
+    method public void setInfoAreaBackgroundColor(int);
+    method public void setMainImage(android.graphics.drawable.Drawable);
+    method public void setMainImage(android.graphics.drawable.Drawable, boolean);
+    method public void setMainImageAdjustViewBounds(boolean);
+    method public void setMainImageDimensions(int, int);
+    method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
+    method public void setTitleText(java.lang.CharSequence);
+    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
+    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
+    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
+    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
+    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
+  }
+
+  public abstract interface ImeKeyMonitor {
+    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public static abstract interface ImeKeyMonitor.ImeKeyListener {
+    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
+  }
+
+  public final class ItemAlignmentFacet {
+    ctor public ItemAlignmentFacet();
+    method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs();
+    method public boolean isMultiAlignment();
+    method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static class ItemAlignmentFacet.ItemAlignmentDef {
+    ctor public ItemAlignmentFacet.ItemAlignmentDef();
+    method public final int getItemAlignmentFocusViewId();
+    method public final int getItemAlignmentOffset();
+    method public final float getItemAlignmentOffsetPercent();
+    method public final int getItemAlignmentViewId();
+    method public boolean isAlignedToTextViewBaseLine();
+    method public final boolean isItemAlignmentOffsetWithPadding();
+    method public final void setAlignedToTextViewBaseline(boolean);
+    method public final void setItemAlignmentFocusViewId(int);
+    method public final void setItemAlignmentOffset(int);
+    method public final void setItemAlignmentOffsetPercent(float);
+    method public final void setItemAlignmentOffsetWithPadding(boolean);
+    method public final void setItemAlignmentViewId(int);
+  }
+
+  public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter {
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ItemBridgeAdapter();
+    method public void clear();
+    method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+    method public int getItemCount();
+    method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper();
+    method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper();
+    method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener);
+    method public void setPresenter(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>);
+    method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper);
+  }
+
+  public static class ItemBridgeAdapter.AdapterListener {
+    ctor public ItemBridgeAdapter.AdapterListener();
+    method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+  }
+
+  public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    method public final java.lang.Object getExtraObject();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.Presenter getPresenter();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder();
+    method public void setExtraObject(java.lang.Object);
+  }
+
+  public static abstract class ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapter.Wrapper();
+    method public abstract android.view.View createWrapper(android.view.View);
+    method public abstract void wrap(android.view.View, android.view.View);
+  }
+
+  public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper);
+    method public android.view.View createWrapper(android.view.View);
+    method public void wrap(android.view.View, android.view.View);
+  }
+
+  public class ListRow extends android.support.v17.leanback.widget.Row {
+    ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public java.lang.CharSequence getContentDescription();
+    method public void setContentDescription(java.lang.CharSequence);
+  }
+
+  public final class ListRowHoverCardView extends android.widget.LinearLayout {
+    ctor public ListRowHoverCardView(android.content.Context);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int);
+    method public final java.lang.CharSequence getDescription();
+    method public final java.lang.CharSequence getTitle();
+    method public final void setDescription(java.lang.CharSequence);
+    method public final void setTitle(java.lang.CharSequence);
+  }
+
+  public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public ListRowPresenter();
+    ctor public ListRowPresenter(int);
+    ctor public ListRowPresenter(int, boolean);
+    method protected void applySelectLevelToChild(android.support.v17.leanback.widget.ListRowPresenter.ViewHolder, android.view.View);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public int getExpandedRowHeight();
+    method public final int getFocusZoomFactor();
+    method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector();
+    method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter);
+    method public int getRowHeight();
+    method public final boolean getShadowEnabled();
+    method public final deprecated int getZoomFactor();
+    method public final boolean isFocusDimmerUsed();
+    method public final boolean isKeepChildForeground();
+    method public boolean isUsingDefaultListSelectEffect();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void setExpandedRowHeight(int);
+    method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumRows(int);
+    method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int);
+    method public void setRowHeight(int);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class ListRowPresenter.SelectItemViewHolderTask extends android.support.v17.leanback.widget.Presenter.ViewHolderTask {
+    ctor public ListRowPresenter.SelectItemViewHolderTask(int);
+    method public int getItemPosition();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolderTask getItemTask();
+    method public boolean isSmoothScroll();
+    method public void setItemPosition(int);
+    method public void setItemTask(android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSmoothScroll(boolean);
+  }
+
+  public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter);
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.HorizontalGridView getGridView();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getItemViewHolder(int);
+    method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter();
+    method public int getSelectedPosition();
+  }
+
+  public final class ListRowView extends android.widget.LinearLayout {
+    ctor public ListRowView(android.content.Context);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
+  }
+
+  public abstract interface MultiActionsProvider {
+    method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
+  }
+
+  public static class MultiActionsProvider.MultiAction {
+    ctor public MultiActionsProvider.MultiAction(long);
+    method public android.graphics.drawable.Drawable getCurrentDrawable();
+    method public android.graphics.drawable.Drawable[] getDrawables();
+    method public long getId();
+    method public int getIndex();
+    method public void incrementIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+  }
+
+  public abstract class ObjectAdapter {
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ObjectAdapter();
+    method public abstract java.lang.Object get(int);
+    method public long getId(int);
+    method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public final boolean hasStableIds();
+    method public boolean isImmediateNotifySupported();
+    method protected final void notifyChanged();
+    method public final void notifyItemRangeChanged(int, int);
+    method protected final void notifyItemRangeInserted(int, int);
+    method protected final void notifyItemRangeRemoved(int, int);
+    method protected void onHasStableIdsChanged();
+    method protected void onPresenterSelectorChanged();
+    method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    method public final void setHasStableIds(boolean);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public abstract int size();
+    method public final void unregisterAllObservers();
+    method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    field public static final int NO_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class ObjectAdapter.DataObserver {
+    ctor public ObjectAdapter.DataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public abstract interface OnActionClickedListener {
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+  }
+
+  public abstract interface OnChildLaidOutListener {
+    method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract deprecated interface OnChildSelectedListener {
+    method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract class OnChildViewHolderSelectedListener {
+    ctor public OnChildViewHolderSelectedListener();
+    method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+  }
+
+  public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
+  }
+
+  public abstract interface OnItemViewSelectedListener implements android.support.v17.leanback.widget.BaseOnItemViewSelectedListener {
+  }
+
+  public class PageRow extends android.support.v17.leanback.widget.Row {
+    ctor public PageRow(android.support.v17.leanback.widget.HeaderItem);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract class Parallax<PropertyT extends android.util.Property> {
+    ctor public Parallax();
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
+    method public abstract PropertyT createProperty(java.lang.String, int);
+    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
+    method public final java.util.List<PropertyT> getProperties();
+    method public void removeAllEffects();
+    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public void updateValues();
+  }
+
+  public static class Parallax.FloatProperty extends android.util.Property {
+    ctor public Parallax.FloatProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
+    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
+    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
+  }
+
+  public static class Parallax.IntProperty extends android.util.Property {
+    ctor public Parallax.IntProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
+    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
+    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
+  }
+
+  public static class Parallax.PropertyMarkerValue<PropertyT> {
+    ctor public Parallax.PropertyMarkerValue(PropertyT);
+    method public PropertyT getProperty();
+  }
+
+  public abstract class ParallaxEffect {
+    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
+    method public final void performMapping(android.support.v17.leanback.widget.Parallax);
+    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
+  }
+
+  public abstract class ParallaxTarget {
+    ctor public ParallaxTarget();
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
+  }
+
+  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
+  }
+
+  public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
+    ctor public PlaybackControlsRow(java.lang.Object);
+    ctor public PlaybackControlsRow();
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
+    method public long getBufferedPosition();
+    method public deprecated int getBufferedProgress();
+    method public deprecated long getBufferedProgressLong();
+    method public long getCurrentPosition();
+    method public deprecated int getCurrentTime();
+    method public deprecated long getCurrentTimeLong();
+    method public long getDuration();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
+    method public deprecated int getTotalTime();
+    method public deprecated long getTotalTimeLong();
+    method public void setBufferedPosition(long);
+    method public deprecated void setBufferedProgress(int);
+    method public deprecated void setBufferedProgressLong(long);
+    method public void setCurrentPosition(long);
+    method public deprecated void setCurrentTime(int);
+    method public deprecated void setCurrentTimeLong(long);
+    method public void setDuration(long);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setOnPlaybackProgressChangedListener(android.support.v17.leanback.widget.PlaybackControlsRow.OnPlaybackProgressCallback);
+    method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public deprecated void setTotalTime(int);
+    method public deprecated void setTotalTimeLong(long);
+  }
+
+  public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context);
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context);
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context);
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MoreActions(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MultiAction(int);
+    method public int getActionCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getIndex();
+    method public java.lang.String getLabel(int);
+    method public java.lang.String getSecondaryLabel(int);
+    method public void nextIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+    method public void setLabels(java.lang.String[]);
+    method public void setSecondaryLabels(java.lang.String[]);
+  }
+
+  public static class PlaybackControlsRow.OnPlaybackProgressCallback {
+    ctor public PlaybackControlsRow.OnPlaybackProgressCallback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onDurationChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+  }
+
+  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
+    field public static final int INDEX_PAUSE = 1; // 0x1
+    field public static final int INDEX_PLAY = 0; // 0x0
+    field public static deprecated int PAUSE;
+    field public static deprecated int PLAY;
+  }
+
+  public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int);
+    field public static deprecated int ALL;
+    field public static final int INDEX_ALL = 1; // 0x1
+    field public static final int INDEX_NONE = 0; // 0x0
+    field public static final int INDEX_ONE = 2; // 0x2
+    field public static deprecated int NONE;
+    field public static deprecated int ONE;
+  }
+
+  public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context);
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context);
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipNextAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int);
+    field public static final int INDEX_OUTLINE = 1; // 0x1
+    field public static final int INDEX_SOLID = 0; // 0x0
+    field public static deprecated int OUTLINE;
+    field public static deprecated int SOLID;
+  }
+
+  public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
+  }
+
+  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public PlaybackControlsRowPresenter();
+    method public boolean areSecondaryActionsHidden();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+    method public void setSecondaryActionsHidden(boolean);
+    method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean);
+    method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
+  }
+
+  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
+  }
+
+  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public PlaybackRowPresenter();
+    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+  }
+
+  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
+  }
+
+  public class PlaybackSeekDataProvider {
+    ctor public PlaybackSeekDataProvider();
+    method public long[] getSeekPositions();
+    method public void getThumbnail(int, android.support.v17.leanback.widget.PlaybackSeekDataProvider.ResultCallback);
+    method public void reset();
+  }
+
+  public static class PlaybackSeekDataProvider.ResultCallback {
+    ctor public PlaybackSeekDataProvider.ResultCallback();
+    method public void onThumbnailLoaded(android.graphics.Bitmap, int);
+  }
+
+  public abstract interface PlaybackSeekUi {
+    method public abstract void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public static class PlaybackSeekUi.Client {
+    ctor public PlaybackSeekUi.Client();
+    method public android.support.v17.leanback.widget.PlaybackSeekDataProvider getPlaybackSeekDataProvider();
+    method public boolean isSeekEnabled();
+    method public void onSeekFinished(boolean);
+    method public void onSeekPositionChanged(long);
+    method public void onSeekStarted();
+  }
+
+  public class PlaybackTransportRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackTransportRowPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public float getDefaultSeekIncrement();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method protected void onProgressBarClicked(android.support.v17.leanback.widget.PlaybackTransportRowPresenter.ViewHolder);
+    method public void setDefaultSeekIncrement(float);
+    method public void setDescriptionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+  }
+
+  public class PlaybackTransportRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackTransportRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    method public final android.widget.TextView getCurrentPositionView();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDescriptionViewHolder();
+    method public final android.widget.TextView getDurationView();
+    method protected void onSetCurrentPositionLabel(long);
+    method protected void onSetDurationLabel(long);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter();
+    method protected static void cancelAnimationsRecursive(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener);
+  }
+
+  public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter.ViewHolder(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    field public final android.view.View view;
+  }
+
+  public static abstract class Presenter.ViewHolderTask {
+    ctor public Presenter.ViewHolderTask();
+    method public void run(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public abstract class PresenterSelector {
+    ctor public PresenterSelector();
+    method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter[] getPresenters();
+  }
+
+  public abstract class PresenterSwitcher {
+    ctor public PresenterSwitcher();
+    method public void clear();
+    method public final android.view.ViewGroup getParentViewGroup();
+    method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector);
+    method protected abstract void insertView(android.view.View);
+    method protected void onViewSelected(android.view.View);
+    method public void select(java.lang.Object);
+    method protected void showView(android.view.View, boolean);
+    method public void unselect();
+  }
+
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
+    ctor public RecyclerViewParallax();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
+    method public float getMaxValue();
+    method public android.support.v7.widget.RecyclerView getRecyclerView();
+    method public void setRecyclerView(android.support.v7.widget.RecyclerView);
+  }
+
+  public static final class RecyclerViewParallax.ChildPositionProperty extends android.support.v17.leanback.widget.Parallax.IntProperty {
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty adapterPosition(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty fraction(float);
+    method public int getAdapterPosition();
+    method public float getFraction();
+    method public int getOffset();
+    method public int getViewId();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty offset(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty viewId(int);
+  }
+
+  public class Row {
+    ctor public Row(long, android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row(android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row();
+    method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem();
+    method public final long getId();
+    method public boolean isRenderedAsRowView();
+    method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem);
+    method public final void setId(long);
+  }
+
+  public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowHeaderPresenter();
+    method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint);
+    method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public boolean isNullItemVisibilityGone();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setNullItemVisibilityGone(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float);
+  }
+
+  public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowHeaderPresenter.ViewHolder(android.view.View);
+    method public final float getSelectLevel();
+  }
+
+  public final class RowHeaderView extends android.widget.TextView {
+    ctor public RowHeaderView(android.content.Context);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowPresenter();
+    method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter();
+    method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final boolean getSelectEffectEnabled();
+    method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final int getSyncActivatePolicy();
+    method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected boolean isClippingChildren();
+    method public boolean isUsingDefaultSelectEffect();
+    method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter);
+    method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setSelectEffectEnabled(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float);
+    method public final void setSyncActivatePolicy(int);
+    field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3
+    field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2
+  }
+
+  public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowPresenter.ViewHolder(android.view.View);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.view.View.OnKeyListener getOnKeyListener();
+    method public final android.support.v17.leanback.widget.Row getRow();
+    method public final java.lang.Object getRowObject();
+    method public final float getSelectLevel();
+    method public java.lang.Object getSelectedItem();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
+    method public final boolean isExpanded();
+    method public final boolean isSelected();
+    method public final void setActivated(boolean);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setOnKeyListener(android.view.View.OnKeyListener);
+    method public final void syncActivatedStatus(android.view.View);
+    field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer;
+  }
+
+  public class SearchBar extends android.widget.RelativeLayout {
+    ctor public SearchBar(android.content.Context);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet, int);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.String getTitle();
+    method public boolean isRecognizing();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
+    method public void setSearchQuery(java.lang.String);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setSpeechRecognizer(android.speech.SpeechRecognizer);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+    method public void stopRecognition();
+  }
+
+  public static abstract interface SearchBar.SearchBarListener {
+    method public abstract void onKeyboardDismiss(java.lang.String);
+    method public abstract void onSearchQueryChange(java.lang.String);
+    method public abstract void onSearchQuerySubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchBar.SearchBarPermissionListener {
+    method public abstract void requestAudioPermission();
+  }
+
+  public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView {
+    ctor public SearchEditText(android.content.Context);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener);
+  }
+
+  public static abstract interface SearchEditText.OnKeyboardDismissListener {
+    method public abstract void onKeyboardDismiss();
+  }
+
+  public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableOrbColorAnimation(boolean);
+    method public int getOrbColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors();
+    method public android.graphics.drawable.Drawable getOrbIcon();
+    method public void onClick(android.view.View);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener);
+    method public void setOrbColor(int);
+    method public deprecated void setOrbColor(int, int);
+    method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
+  }
+
+  public static class SearchOrbView.Colors {
+    ctor public SearchOrbView.Colors(int);
+    ctor public SearchOrbView.Colors(int, int);
+    ctor public SearchOrbView.Colors(int, int, int);
+    method public static int getBrightColor(int);
+    field public int brightColor;
+    field public int color;
+    field public int iconColor;
+  }
+
+  public class SectionRow extends android.support.v17.leanback.widget.Row {
+    ctor public SectionRow(android.support.v17.leanback.widget.HeaderItem);
+    ctor public SectionRow(long, java.lang.String);
+    ctor public SectionRow(java.lang.String);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public class ShadowOverlayContainer extends android.widget.FrameLayout {
+    ctor public ShadowOverlayContainer(android.content.Context);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
+    method public int getShadowType();
+    method public android.view.View getWrappedView();
+    method public deprecated void initialize(boolean, boolean);
+    method public deprecated void initialize(boolean, boolean, boolean);
+    method public static void prepareParentForShadow(android.view.ViewGroup);
+    method public void setOverlayColor(int);
+    method public void setShadowFocusLevel(float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsShadow();
+    method public void useDynamicShadow();
+    method public void useDynamicShadow(float, float);
+    method public void useStaticShadow();
+    method public void wrap(android.view.View);
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public final class ShadowOverlayHelper {
+    method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context);
+    method public int getShadowType();
+    method public boolean needsOverlay();
+    method public boolean needsRoundedCorner();
+    method public boolean needsWrapper();
+    method public void onViewCreated(android.view.View);
+    method public void prepareParentForShadow(android.view.ViewGroup);
+    method public static void setNoneWrapperOverlayColor(android.view.View, int);
+    method public static void setNoneWrapperShadowFocusLevel(android.view.View, float);
+    method public void setOverlayColor(android.view.View, int);
+    method public void setShadowFocusLevel(android.view.View, float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsForeground();
+    method public static boolean supportsRoundedCorner();
+    method public static boolean supportsShadow();
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public static final class ShadowOverlayHelper.Builder {
+    ctor public ShadowOverlayHelper.Builder();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean);
+  }
+
+  public static final class ShadowOverlayHelper.Options {
+    ctor public ShadowOverlayHelper.Options();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float);
+    method public final float getDynamicShadowFocusedZ();
+    method public final float getDynamicShadowUnfocusedZ();
+    method public final int getRoundedCornerRadius();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int);
+    field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT;
+  }
+
+  public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public SparseArrayObjectAdapter();
+    method public void clear(int);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public int indexOf(int);
+    method public java.lang.Object lookup(int);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public void set(int, java.lang.Object);
+    method public int size();
+  }
+
+  public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView {
+    ctor public SpeechOrbView(android.content.Context);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSoundLevel(int);
+    method public void showListening();
+    method public void showNotListening();
+  }
+
+  public abstract interface SpeechRecognitionCallback {
+    method public abstract void recognizeSpeech();
+  }
+
+   class StreamingTextView extends android.widget.EditText {
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet);
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int);
+    method public static boolean isLayoutRtl(android.view.View);
+    method public void reset();
+    method public void setFinalRecognizedText(java.lang.CharSequence);
+    method public void updateRecognizedText(java.lang.String, java.lang.String);
+    method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>);
+  }
+
+  public class TitleHelper {
+    ctor public TitleHelper(android.view.ViewGroup, android.view.View);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public android.view.ViewGroup getSceneRoot();
+    method public android.view.View getTitleView();
+    method public void showTitle(boolean);
+  }
+
+  public class TitleView extends android.widget.FrameLayout implements android.support.v17.leanback.widget.TitleViewAdapter.Provider {
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableAnimation(boolean);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+  }
+
+  public abstract class TitleViewAdapter {
+    ctor public TitleViewAdapter();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public abstract android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public void setAnimationEnabled(boolean);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+    field public static final int BRANDING_VIEW_VISIBLE = 2; // 0x2
+    field public static final int FULL_VIEW_VISIBLE = 6; // 0x6
+    field public static final int SEARCH_VIEW_VISIBLE = 4; // 0x4
+  }
+
+  public static abstract interface TitleViewAdapter.Provider {
+    method public abstract android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+  }
+
+  public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public VerticalGridPresenter();
+    ctor public VerticalGridPresenter(int);
+    ctor public VerticalGridPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public final int getFocusZoomFactor();
+    method public final boolean getKeepChildForeground();
+    method public int getNumberOfColumns();
+    method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public final boolean getShadowEnabled();
+    method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder);
+    method public final boolean isFocusDimmerUsed();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumberOfColumns(int);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView);
+    method public android.support.v17.leanback.widget.VerticalGridView getGridView();
+  }
+
+  public class VerticalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public VerticalGridView(android.content.Context);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int);
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public void setColumnWidth(int);
+    method public void setNumColumns(int);
+  }
+
+  public abstract interface ViewHolderTask {
+    method public abstract void run(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+}
+
+package android.support.v17.leanback.widget.picker {
+
+  public class Picker extends android.widget.FrameLayout {
+    ctor public Picker(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public float getActivatedVisibleItemCount();
+    method public android.support.v17.leanback.widget.picker.PickerColumn getColumnAt(int);
+    method public int getColumnsCount();
+    method protected int getPickerItemHeightPixels();
+    method public final int getPickerItemLayoutId();
+    method public final int getPickerItemTextViewId();
+    method public int getSelectedColumn();
+    method public final java.lang.CharSequence getSeparator();
+    method public float getVisibleItemCount();
+    method public void onColumnValueChanged(int, int);
+    method public void removeOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public void setActivatedVisibleItemCount(float);
+    method public void setColumnAt(int, android.support.v17.leanback.widget.picker.PickerColumn);
+    method public void setColumnValue(int, int, boolean);
+    method public void setColumns(java.util.List<android.support.v17.leanback.widget.picker.PickerColumn>);
+    method public final void setPickerItemTextViewId(int);
+    method public void setSelectedColumn(int);
+    method public final void setSeparator(java.lang.CharSequence);
+    method public void setVisibleItemCount(float);
+  }
+
+  public static abstract interface Picker.PickerValueListener {
+    method public abstract void onValueChanged(android.support.v17.leanback.widget.picker.Picker, int);
+  }
+
+  public class PickerColumn {
+    ctor public PickerColumn();
+    method public int getCount();
+    method public int getCurrentValue();
+    method public java.lang.CharSequence getLabelFor(int);
+    method public java.lang.String getLabelFormat();
+    method public int getMaxValue();
+    method public int getMinValue();
+    method public java.lang.CharSequence[] getStaticLabels();
+    method public void setCurrentValue(int);
+    method public void setLabelFormat(java.lang.String);
+    method public void setMaxValue(int);
+    method public void setMinValue(int);
+    method public void setStaticLabels(java.lang.CharSequence[]);
+  }
+
+  public class TimePicker extends android.support.v17.leanback.widget.picker.Picker {
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet);
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24Hour();
+    method public boolean isPm();
+    method public void setHour(int);
+    method public void setIs24Hour(boolean);
+    method public void setMinute(int);
+  }
+
+}
+
+package android.support.v17.preference {
+
+  public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment {
+    ctor public BaseLeanbackPreferenceFragment();
+  }
+
+  public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment {
+    ctor public LeanbackListPreferenceDialogFragment();
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String);
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String);
+    method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter();
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener);
+    method public android.view.ViewGroup getContainer();
+    method public android.widget.TextView getTitleView();
+    method public android.widget.Checkable getWidgetView();
+    method public void onClick(android.view.View);
+  }
+
+  public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackPreferenceDialogFragment extends android.app.Fragment {
+    ctor public LeanbackPreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    field public static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
+    ctor public LeanbackPreferenceFragment();
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
+    ctor public LeanbackSettingsFragment();
+    method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+    method public abstract void onPreferenceStartInitialScreen();
+    method public void startImmersiveFragment(android.app.Fragment);
+    method public void startPreferenceFragment(android.app.Fragment);
+  }
+
+}
+
+package android.support.v4.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static java.lang.String capabilityToString(int);
+    method public static java.lang.String feedbackTypeToString(int);
+    method public static java.lang.String flagToString(int);
+    method public static deprecated boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final deprecated int DEFAULT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package android.support.v4.app {
+
+  public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int);
+    method public boolean isDrawerIndicatorEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void syncState();
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class ActivityCompat extends android.support.v4.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri getReferrer(android.app.Activity);
+    method public static boolean invalidateOptionsMenu(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void requestPermissions(android.app.Activity, java.lang.String[], int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect getLaunchBounds();
+    method public static android.support.v4.app.ActivityOptionsCompat makeBasic();
+    method public static android.support.v4.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...);
+    method public static android.support.v4.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public android.support.v4.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect);
+    method public android.os.Bundle toBundle();
+    method public void update(android.support.v4.app.ActivityOptionsCompat);
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  public class AppLaunchChecker {
+    ctor public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
+    method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
+    method public static java.lang.String permissionToOp(java.lang.String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String);
+    method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder);
+  }
+
+  public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method public android.app.Dialog getDialog();
+    method public boolean getShowsDialog();
+    method public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method public android.app.Dialog onCreateDialog(android.os.Bundle);
+    method public void onDismiss(android.content.DialogInterface);
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, int);
+    method public void show(android.support.v4.app.FragmentManager, java.lang.String);
+    method public int show(android.support.v4.app.FragmentTransaction, java.lang.String);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+    ctor public Fragment();
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final boolean equals(java.lang.Object);
+    method public final android.support.v4.app.FragmentActivity getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle getArguments();
+    method public final android.support.v4.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context getContext();
+    method public java.lang.Object getEnterTransition();
+    method public java.lang.Object getExitTransition();
+    method public final android.support.v4.app.FragmentManager getFragmentManager();
+    method public final java.lang.Object getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method public android.support.v4.app.LoaderManager getLoaderManager();
+    method public final android.support.v4.app.Fragment getParentFragment();
+    method public java.lang.Object getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method public final boolean getRetainInstance();
+    method public java.lang.Object getReturnTransition();
+    method public java.lang.Object getSharedElementEnterTransition();
+    method public java.lang.Object getSharedElementReturnTransition();
+    method public final java.lang.String getString(int);
+    method public final java.lang.String getString(int, java.lang.Object...);
+    method public final java.lang.String getTag();
+    method public final android.support.v4.app.Fragment getTargetFragment();
+    method public final int getTargetRequestCode();
+    method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
+    method public android.view.View getView();
+    method public final int hashCode();
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String);
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method public void onActivityCreated(android.os.Bundle);
+    method public void onActivityResult(int, int, android.content.Intent);
+    method public void onAttach(android.content.Context);
+    method public deprecated void onAttach(android.app.Activity);
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public boolean onContextItemSelected(android.view.MenuItem);
+    method public void onCreate(android.os.Bundle);
+    method public android.view.animation.Animation onCreateAnimation(int, boolean, int);
+    method public android.animation.Animator onCreateAnimator(int, boolean, int);
+    method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
+    method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDestroy();
+    method public void onDestroyOptionsMenu();
+    method public void onDestroyView();
+    method public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
+    method public void onHiddenChanged(boolean);
+    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
+    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
+    method public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void onOptionsMenuClosed(android.view.Menu);
+    method public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
+    method public void onResume();
+    method public void onSaveInstanceState(android.os.Bundle);
+    method public void onStart();
+    method public void onStop();
+    method public void onViewCreated(android.view.View, android.os.Bundle);
+    method public void onViewStateRestored(android.os.Bundle);
+    method public void postponeEnterTransition();
+    method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle);
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setEnterTransition(java.lang.Object);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitTransition(java.lang.Object);
+    method public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(java.lang.Object);
+    method public void setRetainInstance(boolean);
+    method public void setReturnTransition(java.lang.Object);
+    method public void setSharedElementEnterTransition(java.lang.Object);
+    method public void setSharedElementReturnTransition(java.lang.Object);
+    method public void setTargetFragment(android.support.v4.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle);
+    method public void startActivityForResult(android.content.Intent, int);
+    method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR;
+  }
+
+  public class FragmentActivity extends android.app.Activity implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback {
+    ctor public FragmentActivity();
+    method public java.lang.Object getLastCustomNonConfigurationInstance();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method protected void onResumeFragments();
+    method public java.lang.Object onRetainCustomNonConfigurationInstance();
+    method public final java.lang.Object onRetainNonConfigurationInstance();
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method public deprecated void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method public android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public abstract android.view.View onFindViewById(int);
+    method public abstract boolean onHasView();
+  }
+
+  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 void dispatchActivityCreated();
+    method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method public void dispatchLowMemory();
+    method public void dispatchMultiWindowModeChanged(boolean);
+    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method public void dispatchPictureInPictureModeChanged(boolean);
+    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method public void doLoaderDestroy();
+    method public void doLoaderRetain();
+    method public void doLoaderStart();
+    method public void doLoaderStop(boolean);
+    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public boolean execPendingActions();
+    method public android.support.v4.app.Fragment findFragmentByWho(java.lang.String);
+    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
+    method public int getActiveFragmentsCount();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public void reportLoaderStart();
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig);
+    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
+    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
+    method public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.support.v4.app.Fragment> retainNonConfig();
+    method public android.os.Parcelable saveAllState();
+  }
+
+  public abstract class FragmentHostCallback<E> extends android.support.v4.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.view.View onFindViewById(int);
+    method public abstract E onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int);
+    method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(java.lang.String);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager {
+    ctor public FragmentManager();
+    method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.FragmentTransaction beginTransaction();
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract boolean executePendingTransactions();
+    method public abstract android.support.v4.app.Fragment findFragmentById(int);
+    method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String);
+    method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public abstract int getBackStackEntryCount();
+    method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String);
+    method public abstract java.util.List<android.support.v4.app.Fragment> getFragments();
+    method public abstract android.support.v4.app.Fragment getPrimaryNavigationFragment();
+    method public abstract boolean isDestroyed();
+    method public abstract boolean isStateSaved();
+    method public abstract void popBackStack();
+    method public abstract void popBackStack(java.lang.String, int);
+    method public abstract void popBackStack(int, int);
+    method public abstract boolean popBackStackImmediate();
+    method public abstract boolean popBackStackImmediate(java.lang.String, int);
+    method public abstract boolean popBackStackImmediate(int, int);
+    method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static abstract interface FragmentManager.BackStackEntry {
+    method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
+    method public abstract int getBreadCrumbShortTitleRes();
+    method public abstract java.lang.CharSequence getBreadCrumbTitle();
+    method public abstract int getBreadCrumbTitleRes();
+    method public abstract int getId();
+    method public abstract java.lang.String getName();
+  }
+
+  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentPreCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+  }
+
+  public static abstract interface FragmentManager.OnBackStackChangedListener {
+    method public abstract void onBackStackChanged();
+  }
+
+  public class FragmentManagerNonConfig {
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor public FragmentTransaction();
+    method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
+    method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack();
+    method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
+    method public abstract boolean isAddToBackStackAllowed();
+    method public abstract boolean isEmpty();
+    method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction runOnCommit(java.lang.Runnable);
+    method public abstract deprecated android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setPrimaryNavigationFragment(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction setReorderingAllowed(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setTransition(int);
+    method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int);
+    method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray[] getMetrics();
+    method public android.util.SparseIntArray[] remove(android.app.Activity);
+    method public android.util.SparseIntArray[] reset();
+    method public android.util.SparseIntArray[] stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public abstract class JobIntentService extends android.app.Service {
+    ctor public JobIntentService();
+    method public static void enqueueWork(android.content.Context, java.lang.Class, int, android.content.Intent);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract void onHandleWork(android.content.Intent);
+    method public boolean onStopCurrentWork();
+  }
+
+  public class ListFragment extends android.support.v4.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public void setEmptyText(java.lang.CharSequence);
+    method public void setListAdapter(android.widget.ListAdapter);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+  public abstract class LoaderManager {
+    ctor public LoaderManager();
+    method public abstract void destroyLoader(int);
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract <D> android.support.v4.content.Loader<D> getLoader(int);
+    method public boolean hasRunningLoaders();
+    method public abstract <D> android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+    method public abstract <D> android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+  }
+
+  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+    method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle);
+    method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D);
+    method public abstract void onLoaderReset(android.support.v4.content.Loader<D>);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static java.lang.String getParentActivityName(android.app.Activity);
+    method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static java.lang.String getCategory(android.app.Notification);
+    method public static deprecated java.lang.String getChannel(android.app.Notification);
+    method public static java.lang.String getChannelId(android.app.Notification);
+    method public static android.os.Bundle getExtras(android.app.Notification);
+    method public static java.lang.String getGroup(android.app.Notification);
+    method public static int getGroupAlertBehavior(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static java.lang.String getShortcutId(android.app.Notification);
+    method public static java.lang.String getSortKey(android.app.Notification);
+    method public static deprecated long getTimeout(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+    field public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+    field public static final java.lang.String EXTRA_PICTURE = "android.picture";
+    field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
+    field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
+    field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
+    field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
+    field public static final java.lang.String EXTRA_TEXT = "android.text";
+    field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_TITLE = "android.title";
+    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.app.PendingIntent getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public android.support.v4.app.RemoteInput[] getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public android.support.v4.app.RemoteInput[] getRemoteInputs();
+    method public java.lang.CharSequence getTitle();
+    field public android.app.PendingIntent actionIntent;
+    field public int icon;
+    field public java.lang.CharSequence title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
+    ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput);
+    method public android.support.v4.app.NotificationCompat.Action build();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+  }
+
+  public static abstract interface NotificationCompat.Action.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+    method public java.lang.CharSequence getCancelLabel();
+    method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method public java.lang.CharSequence getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated NotificationCompat.Builder(android.content.Context);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String);
+    method public android.app.Notification build();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method public deprecated android.app.Notification getNotification();
+    method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String);
+    method public deprecated android.support.v4.app.NotificationCompat.Builder setChannel(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setChannelId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setColor(int);
+    method public android.support.v4.app.NotificationCompat.Builder setColorized(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setDefaults(int);
+    method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setNumber(int);
+    method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPriority(int);
+    method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.support.v4.app.NotificationCompat.Builder setShortcutId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int);
+    method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style);
+    method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public deprecated android.support.v4.app.NotificationCompat.Builder setTimeout(long);
+    method public android.support.v4.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]);
+    method public android.support.v4.app.NotificationCompat.Builder setVisibility(int);
+    method public android.support.v4.app.NotificationCompat.Builder setWhen(long);
+    field public java.util.ArrayList<java.lang.String> mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.support.v4.app.NotificationCompat.CarExtender setColor(int);
+    method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation);
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.support.v4.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput);
+  }
+
+  public static abstract interface NotificationCompat.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
+    method public void addCompatExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(android.support.v4.app.NotificationCompat.MessagingStyle.Message);
+    method public static android.support.v4.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.support.v4.app.NotificationCompat.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+  }
+
+  public static abstract class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification build();
+    method public void setBuilder(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearActions();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearPages();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public java.lang.String getDismissalId();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintAmbientBigPicture();
+    method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintHideIcon();
+    method public int getHintScreenTimeout();
+    method public boolean getHintShowBackgroundOnly();
+    method public java.util.List<android.app.Notification> getPages();
+    method public boolean getStartScrollBottom();
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBridgeTag(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDismissalId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field public static final int SIZE_LARGE = 4; // 0x4
+    field public static final int SIZE_MEDIUM = 3; // 0x3
+    field public static final int SIZE_SMALL = 2; // 0x2
+    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(java.lang.String, int, java.lang.String);
+    method public abstract void cancelAll(java.lang.String);
+    method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(java.lang.String, int);
+    method public void cancelAll();
+    method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public void notify(int, android.app.Notification);
+    method public void notify(java.lang.String, int, android.app.Notification);
+    field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public final class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput {
+    method public static void addDataResultToIntent(android.support.v4.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
+    method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
+    method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getResultKey();
+    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
+    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(java.lang.String);
+    method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
+    method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+    method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+  }
+
+   deprecated class RemoteInputCompatBase {
+  }
+
+  public static abstract class RemoteInputCompatBase.RemoteInput {
+    ctor public RemoteInputCompatBase.RemoteInput();
+    method protected abstract boolean getAllowFreeFormInput();
+    method protected abstract java.util.Set<java.lang.String> getAllowedDataTypes();
+    method protected abstract java.lang.CharSequence[] getChoices();
+    method protected abstract android.os.Bundle getExtras();
+    method protected abstract java.lang.CharSequence getLabel();
+    method protected abstract java.lang.String getResultKey();
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName getCallingActivity(android.app.Activity);
+    method public static java.lang.String getCallingPackage(android.app.Activity);
+    field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName getCallingActivity();
+    method public android.graphics.drawable.Drawable getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable getCallingApplicationIcon();
+    method public java.lang.CharSequence getCallingApplicationLabel();
+    method public java.lang.String getCallingPackage();
+    method public java.lang.String[] getEmailBcc();
+    method public java.lang.String[] getEmailCc();
+    method public java.lang.String[] getEmailTo();
+    method public java.lang.String getHtmlText();
+    method public android.net.Uri getStream();
+    method public android.net.Uri getStream(int);
+    method public int getStreamCount();
+    method public java.lang.String getSubject();
+    method public java.lang.CharSequence getText();
+    method public java.lang.String getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
+    method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable);
+    method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
+    method public void onRejectSharedElements(java.util.List<android.view.View>);
+    method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String>, java.util.List<android.view.View>, android.support.v4.app.SharedElementCallback.OnSharedElementsReadyListener);
+  }
+
+  public static abstract interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public abstract void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable {
+    method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public static android.support.v4.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent editIntentAt(int);
+    method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context);
+    method public deprecated android.content.Intent getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent[] getIntents();
+    method public android.app.PendingIntent getPendingIntent(int, int);
+    method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle);
+    method public deprecated java.util.Iterator<android.content.Intent> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle);
+  }
+
+  public static abstract interface TaskStackBuilder.SupportParentable {
+    method public abstract android.content.Intent getSupportParentActivityIntent();
+  }
+
+}
+
+package android.support.v4.content {
+
+  public abstract class AsyncTaskLoader<D> extends android.support.v4.content.Loader {
+    ctor public AsyncTaskLoader(android.content.Context);
+    method public void cancelLoadInBackground();
+    method public boolean isLoadInBackgroundCanceled();
+    method public abstract D loadInBackground();
+    method public void onCanceled(D);
+    method protected D onLoadInBackground();
+    method public void setUpdateThrottle(long);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    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 java.io.File getDataDir(android.content.Context);
+    method public static final 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[] getObbDirs(android.content.Context);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class CursorLoader extends android.support.v4.content.AsyncTaskLoader {
+    ctor public CursorLoader(android.content.Context);
+    ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public void deliverResult(android.database.Cursor);
+    method public java.lang.String[] getProjection();
+    method public java.lang.String getSelection();
+    method public java.lang.String[] getSelectionArgs();
+    method public java.lang.String getSortOrder();
+    method public android.net.Uri getUri();
+    method public android.database.Cursor loadInBackground();
+    method public void onCanceled(android.database.Cursor);
+    method public void setProjection(java.lang.String[]);
+    method public void setSelection(java.lang.String);
+    method public void setSelectionArgs(java.lang.String[]);
+    method public void setSortOrder(java.lang.String);
+    method public void setUri(android.net.Uri);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File);
+    method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
+  public final class IntentCompat {
+    method public static deprecated android.content.Intent makeMainActivity(android.content.ComponentName);
+    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
+    method public static deprecated android.content.Intent makeRestartActivityTask(android.content.ComponentName);
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final java.lang.String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final deprecated int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
+    field public static final deprecated int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
+  }
+
+  public class Loader<D> {
+    ctor public Loader(android.content.Context);
+    method public void abandon();
+    method public boolean cancelLoad();
+    method public void commitContentChanged();
+    method public java.lang.String dataToString(D);
+    method public void deliverCancellation();
+    method public void deliverResult(D);
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void forceLoad();
+    method public android.content.Context getContext();
+    method public int getId();
+    method public boolean isAbandoned();
+    method public boolean isReset();
+    method public boolean isStarted();
+    method protected void onAbandon();
+    method protected boolean onCancelLoad();
+    method public void onContentChanged();
+    method protected void onForceLoad();
+    method protected void onReset();
+    method protected void onStartLoading();
+    method protected void onStopLoading();
+    method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+    method public void reset();
+    method public void rollbackContentChanged();
+    method public final void startLoading();
+    method public void stopLoading();
+    method public boolean takeContentChanged();
+    method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+  }
+
+  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+    ctor public Loader.ForceLoadContentObserver();
+  }
+
+  public static abstract interface Loader.OnLoadCanceledListener<D> {
+    method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>);
+  }
+
+  public static abstract interface Loader.OnLoadCompleteListener<D> {
+    method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D);
+  }
+
+  public final class LocalBroadcastManager {
+    method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context);
+    method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+    method public boolean sendBroadcast(android.content.Intent);
+    method public void sendBroadcastSync(android.content.Intent);
+    method public void unregisterReceiver(android.content.BroadcastReceiver);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(java.lang.String, java.lang.String);
+    method public static java.lang.String matches(java.lang.String, java.lang.String[]);
+    method public static java.lang.String matches(java.lang.String[], java.lang.String);
+    method public static java.lang.String[] matchesMany(java.lang.String[], java.lang.String);
+  }
+
+  public final deprecated class ParallelExecutorCompat {
+    method public static deprecated java.util.concurrent.Executor getParallelExecutor();
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String);
+    method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String);
+    method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String);
+    method public static int checkSelfPermission(android.content.Context, java.lang.String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  public final class SharedPreferencesCompat {
+  }
+
+  public static final class SharedPreferencesCompat.EditorCompat {
+    method public void apply(android.content.SharedPreferences.Editor);
+    method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance();
+  }
+
+  public abstract deprecated class WakefulBroadcastReceiver extends android.content.BroadcastReceiver {
+    ctor public WakefulBroadcastReceiver();
+    method public static boolean completeWakefulIntent(android.content.Intent);
+    method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent);
+  }
+
+}
+
+package android.support.v4.content.pm {
+
+  public final class ActivityInfoCompat {
+    field public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName getActivity();
+    method public java.lang.CharSequence getDisabledMessage();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent[] getIntents();
+    method public java.lang.CharSequence getLongLabel();
+    method public java.lang.CharSequence getShortLabel();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, java.lang.String);
+    method public android.support.v4.content.pm.ShortcutInfoCompat build();
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(java.lang.CharSequence);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.graphics.Bitmap);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(int);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.support.v4.graphics.drawable.IconCompat);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent[]);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setLongLabel(java.lang.CharSequence);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setShortLabel(java.lang.CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean requestPinShortcut(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat, android.content.IntentSender);
+  }
+
+}
+
+package android.support.v4.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+    method public static deprecated int getScreenHeightDp(android.content.res.Resources);
+    method public static deprecated int getScreenWidthDp(android.content.res.Resources);
+    method public static deprecated int getSmallestScreenWidthDp(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.Typeface getFont(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
+  }
+
+}
+
+package android.support.v4.database {
+
+  public final class DatabaseUtilsCompat {
+    method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]);
+    method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public final class ColorUtils {
+    method public static int HSLToColor(float[]);
+    method public static int LABToColor(double, double, double);
+    method public static void LABToXYZ(double, double, double, double[]);
+    method public static void RGBToHSL(int, int, int, float[]);
+    method public static void RGBToLAB(int, int, int, double[]);
+    method public static void RGBToXYZ(int, int, int, double[]);
+    method public static int XYZToColor(double, double, double);
+    method public static void XYZToLAB(double, double, double, double[]);
+    method public static int blendARGB(int, int, float);
+    method public static void blendHSL(float[], float[], float, float[]);
+    method public static void blendLAB(double[], double[], double, double[]);
+    method public static double calculateContrast(int, int);
+    method public static double calculateLuminance(int);
+    method public static int calculateMinimumAlpha(int, int, float);
+    method public static void colorToHSL(int, float[]);
+    method public static void colorToLAB(int, double[]);
+    method public static void colorToXYZ(int, double[]);
+    method public static int compositeColors(int, int);
+    method public static double distanceEuclidean(double[], double[]);
+    method public static int setAlphaComponent(int, int);
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  public class IconCompat {
+    method public static android.support.v4.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(java.lang.String);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithData(byte[], int, int);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithResource(android.content.Context, int);
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setCornerRadius(float);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package android.support.v4.hardware.display {
+
+  public abstract class DisplayManagerCompat {
+    method public abstract android.view.Display getDisplay(int);
+    method public abstract android.view.Display[] getDisplays();
+    method public abstract android.view.Display[] getDisplays(java.lang.String);
+    method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package android.support.v4.hardware.fingerprint {
+
+  public final class FingerprintManagerCompat {
+    method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler);
+    method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
+  }
+
+  public static abstract class FingerprintManagerCompat.AuthenticationCallback {
+    ctor public FingerprintManagerCompat.AuthenticationCallback();
+    method public void onAuthenticationError(int, java.lang.CharSequence);
+    method public void onAuthenticationFailed();
+    method public void onAuthenticationHelp(int, java.lang.CharSequence);
+    method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult);
+  }
+
+  public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject);
+    method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject();
+  }
+
+  public static class FingerprintManagerCompat.CryptoObject {
+    ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method public javax.crypto.Cipher getCipher();
+    method public javax.crypto.Mac getMac();
+    method public java.security.Signature getSignature();
+  }
+
+}
+
+package android.support.v4.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+  }
+
+}
+
+package android.support.v4.media {
+
+  public class AudioAttributesCompat {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method public int getUsage();
+    method public int getVolumeControlStream();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.media.AudioAttributesCompat wrap(java.lang.Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(android.support.v4.media.AudioAttributesCompat);
+    method public android.support.v4.media.AudioAttributesCompat build();
+    method public android.support.v4.media.AudioAttributesCompat.Builder setContentType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setFlags(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setLegacyStreamType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setUsage(int);
+  }
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle getExtras();
+    method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method public java.lang.String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.CustomActionCallback);
+    method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(java.lang.String);
+    method public void unsubscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final java.lang.String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+    field public static final java.lang.String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE = "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+    field public static final java.lang.String EXTRA_DOWNLOAD_PROGRESS = "android.media.browse.extra.DOWNLOAD_PROGRESS";
+    field public static final java.lang.String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+    field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public static abstract class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onProgressUpdate(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onResult(java.lang.String, android.os.Bundle, android.os.Bundle);
+  }
+
+  public static abstract class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(java.lang.String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem fromMediaItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem> fromMediaItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public java.lang.String getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static abstract class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+  }
+
+  public static abstract class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>, android.os.Bundle);
+    method public void onError(java.lang.String);
+    method public void onError(java.lang.String, android.os.Bundle);
+  }
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public void notifyChildrenChanged(java.lang.String);
+    method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<android.os.Bundle>);
+    method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>, android.os.Bundle);
+    method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field public static final deprecated java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle);
+    method public void sendProgressUpdate(android.os.Bundle);
+    method public void sendResult(T);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object);
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public android.net.Uri getIconUri();
+    method public java.lang.Object getMediaDescription();
+    method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR;
+    field public static final java.lang.String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+    field public static final java.lang.String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(java.lang.String);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object);
+    method public android.graphics.Bitmap getBitmap(java.lang.String);
+    method public android.os.Bundle getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getLong(java.lang.String);
+    method public java.lang.Object getMediaMetadata();
+    method public android.support.v4.media.RatingCompat getRating(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR;
+    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat);
+    method public android.support.v4.media.MediaMetadataCompat build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object);
+    method public float getPercentRating();
+    method public java.lang.Object getRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat newStarRating(int, float);
+    method public static android.support.v4.media.RatingCompat newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public java.lang.Object getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static abstract class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat);
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
+    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
+    method public android.os.Bundle getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat getMediaController(android.app.Activity);
+    method public java.lang.Object getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat getMetadata();
+    method public java.lang.String getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
+    method public java.lang.CharSequence getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent getSessionActivity();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public int getShuffleMode();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public deprecated boolean isShuffleModeEnabled();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void removeQueueItemAt(int);
+    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onRepeatModeChanged(int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public deprecated void onShuffleModeChanged(boolean);
+    method public void onShuffleModeChanged(int);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public static abstract class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void playFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void playFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
+    method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleMode(int);
+    method public abstract deprecated void setShuffleModeEnabled(boolean);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public static android.support.v4.media.session.MediaSessionCompat fromMediaSession(android.content.Context, java.lang.Object);
+    method public android.support.v4.media.session.MediaControllerCompat getController();
+    method public java.lang.Object getMediaSession();
+    method public java.lang.Object getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void setQueueTitle(java.lang.CharSequence);
+    method public void setRatingType(int);
+    method public void setRepeatMode(int);
+    method public void setSessionActivity(android.app.PendingIntent);
+    method public void setShuffleMode(int);
+    method public deprecated void setShuffleModeEnabled(boolean);
+    field public static final java.lang.String ACTION_ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.action.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final java.lang.String ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.action.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    field public static final java.lang.String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final java.lang.String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+    field public static final java.lang.String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final java.lang.String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int MEDIA_ATTRIBUTE_ALBUM = 1; // 0x1
+    field public static final int MEDIA_ATTRIBUTE_ARTIST = 0; // 0x0
+    field public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2; // 0x2
+  }
+
+  public static abstract class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void onCustomAction(java.lang.String, android.os.Bundle);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleMode(int);
+    method public deprecated void onSetShuffleModeEnabled(boolean);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static abstract interface MediaSessionCompat.OnActiveChangeListener {
+    method public abstract void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem fromQueueItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> fromQueueItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getQueueId();
+    method public java.lang.Object getQueueItem();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object);
+    method public java.lang.Object getToken();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object);
+    method public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions();
+    method public int getErrorCode();
+    method public java.lang.CharSequence getErrorMessage();
+    method public android.os.Bundle getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public java.lang.Object getPlaybackState();
+    method public long getPosition();
+    method public int getState();
+    method public static int toKeyCode(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction);
+    method public android.support.v4.media.session.PlaybackStateCompat build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long);
+    method public deprecated android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(int, java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object);
+    method public java.lang.String getAction();
+    method public java.lang.Object getCustomAction();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle);
+  }
+
+}
+
+package android.support.v4.net {
+
+  public final class ConnectivityManagerCompat {
+    method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class TrafficStatsCompat {
+    method public static deprecated void clearThreadStatsTag();
+    method public static deprecated int getThreadStatsTag();
+    method public static deprecated void incrementOperationCount(int);
+    method public static deprecated void incrementOperationCount(int, int);
+    method public static deprecated void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void tagSocket(java.net.Socket) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void untagSocket(java.net.Socket) throws java.net.SocketException;
+  }
+
+}
+
+package android.support.v4.os {
+
+  public final deprecated class AsyncTaskCompat {
+    method public static deprecated <Params, Progress, Result> android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
+  }
+
+  public class BuildCompat {
+    method public static deprecated boolean isAtLeastN();
+    method public static deprecated boolean isAtLeastNMR1();
+    method public static deprecated boolean isAtLeastO();
+    method public static boolean isAtLeastOMR1();
+    method public static boolean isAtLeastP();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public java.lang.Object getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener);
+    method public void throwIfCanceled();
+  }
+
+  public static abstract interface CancellationSignal.OnCancelListener {
+    method public abstract void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static android.support.v4.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static java.lang.String getStorageState(java.io.File);
+    field public static final java.lang.String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class LocaleListCompat {
+    method public static android.support.v4.os.LocaleListCompat create(java.util.Locale...);
+    method public static android.support.v4.os.LocaleListCompat forLanguageTags(java.lang.String);
+    method public java.util.Locale get(int);
+    method public static android.support.v4.os.LocaleListCompat getAdjustedDefault();
+    method public static android.support.v4.os.LocaleListCompat getDefault();
+    method public static android.support.v4.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale getFirstMatch(java.lang.String[]);
+    method public int indexOf(java.util.Locale);
+    method public boolean isEmpty();
+    method public int size();
+    method public java.lang.String toLanguageTags();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.os.LocaleListCompat wrap(java.lang.Object);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(java.lang.String);
+  }
+
+  public final deprecated class ParcelableCompat {
+    method public static deprecated <T> android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>);
+  }
+
+  public abstract deprecated interface ParcelableCompatCreatorCallbacks<T> {
+    method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader);
+    method public abstract T[] newArray(int);
+  }
+
+  public final class TraceCompat {
+    method public static void beginSection(java.lang.String);
+    method public static void endSection();
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package android.support.v4.print {
+
+  public final class PrintHelper {
+    ctor public PrintHelper(android.content.Context);
+    method public int getColorMode();
+    method public int getOrientation();
+    method public int getScaleMode();
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap);
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback);
+    method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException;
+    method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException;
+    method public void setColorMode(int);
+    method public void setOrientation(int);
+    method public void setScaleMode(int);
+    method public static boolean systemSupportsPrint();
+    field public static final int COLOR_MODE_COLOR = 2; // 0x2
+    field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
+    field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
+    field public static final int ORIENTATION_PORTRAIT = 2; // 0x2
+    field public static final int SCALE_MODE_FILL = 2; // 0x2
+    field public static final int SCALE_MODE_FIT = 1; // 0x1
+  }
+
+  public static abstract interface PrintHelper.OnPrintFinishCallback {
+    method public abstract void onFinish();
+  }
+
+}
+
+package android.support.v4.provider {
+
+  public abstract class DocumentFile {
+    method public abstract boolean canRead();
+    method public abstract boolean canWrite();
+    method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String);
+    method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String);
+    method public abstract boolean delete();
+    method public abstract boolean exists();
+    method public android.support.v4.provider.DocumentFile findFile(java.lang.String);
+    method public static android.support.v4.provider.DocumentFile fromFile(java.io.File);
+    method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri);
+    method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri);
+    method public abstract java.lang.String getName();
+    method public android.support.v4.provider.DocumentFile getParentFile();
+    method public abstract java.lang.String getType();
+    method public abstract android.net.Uri getUri();
+    method public abstract boolean isDirectory();
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public abstract boolean isFile();
+    method public abstract boolean isVirtual();
+    method public abstract long lastModified();
+    method public abstract long length();
+    method public abstract android.support.v4.provider.DocumentFile[] listFiles();
+    method public abstract boolean renameTo(java.lang.String);
+  }
+
+  public final class FontRequest {
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, int);
+    method public java.util.List<java.util.List<byte[]>> getCertificates();
+    method public int getCertificatesArrayResId();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getProviderPackage();
+    method public java.lang.String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontsContractCompat.FontInfo[]);
+    method public static android.support.v4.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, android.support.v4.provider.FontRequest, android.support.v4.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns {
+    ctor public FontsContractCompat.Columns();
+    field public static final java.lang.String FILE_ID = "file_id";
+    field public static final java.lang.String ITALIC = "font_italic";
+    field public static final java.lang.String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final java.lang.String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public android.support.v4.provider.FontsContractCompat.FontInfo[] getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package android.support.v4.text {
+
+  public final class BidiFormatter {
+    method public static android.support.v4.text.BidiFormatter getInstance();
+    method public static android.support.v4.text.BidiFormatter getInstance(boolean);
+    method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale);
+    method public boolean getStereoReset();
+    method public boolean isRtl(java.lang.String);
+    method public boolean isRtl(java.lang.CharSequence);
+    method public boolean isRtlContext();
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.String unicodeWrap(java.lang.String, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale);
+    method public android.support.v4.text.BidiFormatter build();
+    method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat);
+    method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean);
+  }
+
+  public final class ICUCompat {
+    method public static java.lang.String maximizeAndGetScript(java.util.Locale);
+  }
+
+  public abstract interface TextDirectionHeuristicCompat {
+    method public abstract boolean isRtl(char[], int, int);
+    method public abstract boolean isRtl(java.lang.CharSequence, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale);
+    method public static java.lang.String htmlEncode(java.lang.String);
+    field public static final deprecated java.util.Locale ROOT;
+  }
+
+}
+
+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);
+  }
+
+}
+
+package android.support.v4.util {
+
+  public class ArrayMap<K, V> extends android.support.v4.util.SimpleArrayMap implements java.util.Map {
+    ctor public ArrayMap();
+    ctor public ArrayMap(int);
+    ctor public ArrayMap(android.support.v4.util.SimpleArrayMap);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+    method public java.util.Set<K> keySet();
+    method public void putAll(java.util.Map<? extends K, ? extends V>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public java.util.Collection<V> values();
+  }
+
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    ctor public ArraySet();
+    ctor public ArraySet(int);
+    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
+    method public boolean add(E);
+    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(java.lang.Object);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public void ensureCapacity(int);
+    method public int indexOf(java.lang.Object);
+    method public boolean isEmpty();
+    method public java.util.Iterator<E> iterator();
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public E removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public int size();
+    method public java.lang.Object[] toArray();
+    method public <T> T[] toArray(T[]);
+    method public E valueAt(int);
+  }
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream);
+    method public void finishWrite(java.io.FileOutputStream);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public final class CircularArray<E> {
+    ctor public CircularArray();
+    ctor public CircularArray(int);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public void clear();
+    method public E get(int);
+    method public E getFirst();
+    method public E getLast();
+    method public boolean isEmpty();
+    method public E popFirst();
+    method public E popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public final class CircularIntArray {
+    ctor public CircularIntArray();
+    ctor public CircularIntArray(int);
+    method public void addFirst(int);
+    method public void addLast(int);
+    method public void clear();
+    method public int get(int);
+    method public int getFirst();
+    method public int getLast();
+    method public boolean isEmpty();
+    method public int popFirst();
+    method public int popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public class LongSparseArray<E> {
+    ctor public LongSparseArray();
+    ctor public LongSparseArray(int);
+    method public void append(long, E);
+    method public void clear();
+    method public android.support.v4.util.LongSparseArray<E> clone();
+    method public void delete(long);
+    method public E get(long);
+    method public E get(long, E);
+    method public int indexOfKey(long);
+    method public int indexOfValue(E);
+    method public long keyAt(int);
+    method public void put(long, E);
+    method public void remove(long);
+    method public void removeAt(int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+  public class LruCache<K, V> {
+    ctor public LruCache(int);
+    method protected V create(K);
+    method public final synchronized int createCount();
+    method protected void entryRemoved(boolean, K, V, V);
+    method public final void evictAll();
+    method public final synchronized int evictionCount();
+    method public final V get(K);
+    method public final synchronized int hitCount();
+    method public final synchronized int maxSize();
+    method public final synchronized int missCount();
+    method public final V put(K, V);
+    method public final synchronized int putCount();
+    method public final V remove(K);
+    method public void resize(int);
+    method public final synchronized int size();
+    method protected int sizeOf(K, V);
+    method public final synchronized java.util.Map<K, V> snapshot();
+    method public final synchronized java.lang.String toString();
+    method public void trimToSize(int);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F, S);
+    method public static <A, B> android.support.v4.util.Pair<A, B> create(A, B);
+    field public final F first;
+    field public final S second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static abstract interface Pools.Pool<T> {
+    method public abstract T acquire();
+    method public abstract boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements android.support.v4.util.Pools.Pool {
+    ctor public Pools.SimplePool(int);
+    method public T acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends android.support.v4.util.Pools.SimplePool {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public class SimpleArrayMap<K, V> {
+    ctor public SimpleArrayMap();
+    ctor public SimpleArrayMap(int);
+    ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap<K, V>);
+    method public void clear();
+    method public boolean containsKey(java.lang.Object);
+    method public boolean containsValue(java.lang.Object);
+    method public void ensureCapacity(int);
+    method public V get(java.lang.Object);
+    method public int indexOfKey(java.lang.Object);
+    method public boolean isEmpty();
+    method public K keyAt(int);
+    method public V put(K, V);
+    method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>);
+    method public V remove(java.lang.Object);
+    method public V removeAt(int);
+    method public V setValueAt(int, V);
+    method public int size();
+    method public V valueAt(int);
+  }
+
+  public class SparseArrayCompat<E> {
+    ctor public SparseArrayCompat();
+    ctor public SparseArrayCompat(int);
+    method public void append(int, E);
+    method public void clear();
+    method public android.support.v4.util.SparseArrayCompat<E> clone();
+    method public void delete(int);
+    method public E get(int);
+    method public E get(int, E);
+    method public int indexOfKey(int);
+    method public int indexOfValue(E);
+    method public int keyAt(int);
+    method public void put(int, E);
+    method public void remove(int);
+    method public void removeAt(int);
+    method public void removeAtRange(int, int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+}
+
+package android.support.v4.utils {
+
+  public class ObjectUtils {
+    method public static boolean objectEquals(java.lang.Object, java.lang.Object);
+  }
+
+}
+
+package android.support.v4.view {
+
+  public abstract class AbsSavedState implements android.os.Parcelable {
+    ctor protected AbsSavedState(android.os.Parcelable);
+    ctor protected AbsSavedState(android.os.Parcel);
+    ctor protected AbsSavedState(android.os.Parcel, java.lang.ClassLoader);
+    method public int describeContents();
+    method public final android.os.Parcelable getSuperState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.AbsSavedState> CREATOR;
+    field public static final android.support.v4.view.AbsSavedState EMPTY_STATE;
+  }
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener);
+  }
+
+  public static abstract interface ActionProvider.VisibilityListener {
+    method public abstract void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class AsyncLayoutInflater {
+    ctor public AsyncLayoutInflater(android.content.Context);
+    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+  }
+
+  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final deprecated class KeyEventCompat {
+    method public static deprecated boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object);
+    method public static deprecated java.lang.Object getKeyDispatcherState(android.view.View);
+    method public static deprecated boolean hasModifiers(android.view.KeyEvent, int);
+    method public static deprecated boolean hasNoModifiers(android.view.KeyEvent);
+    method public static deprecated boolean isCtrlPressed(android.view.KeyEvent);
+    method public static deprecated boolean isTracking(android.view.KeyEvent);
+    method public static deprecated boolean metaStateHasModifiers(int, int);
+    method public static deprecated boolean metaStateHasNoModifiers(int);
+    method public static deprecated int normalizeMetaState(int);
+    method public static deprecated void startTracking(android.view.KeyEvent);
+  }
+
+  public final class LayoutInflaterCompat {
+    method public static deprecated android.support.v4.view.LayoutInflaterFactory getFactory(android.view.LayoutInflater);
+    method public static deprecated void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  public abstract deprecated interface LayoutInflaterFactory {
+    method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+  }
+
+  public final class MenuItemCompat {
+    method public static deprecated boolean collapseActionView(android.view.MenuItem);
+    method public static deprecated boolean expandActionView(android.view.MenuItem);
+    method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
+    method public static deprecated android.view.View getActionView(android.view.MenuItem);
+    method public static int getAlphabeticModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getContentDescription(android.view.MenuItem);
+    method public static android.content.res.ColorStateList getIconTintList(android.view.MenuItem);
+    method public static android.graphics.PorterDuff.Mode getIconTintMode(android.view.MenuItem);
+    method public static int getNumericModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getTooltipText(android.view.MenuItem);
+    method public static deprecated boolean isActionViewExpanded(android.view.MenuItem);
+    method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+    method public static void setContentDescription(android.view.MenuItem, java.lang.CharSequence);
+    method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList);
+    method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
+    method public static deprecated android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+    method public static void setTooltipText(android.view.MenuItem, java.lang.CharSequence);
+    field public static final deprecated int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final deprecated int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final deprecated int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final deprecated int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final deprecated int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  public static abstract deprecated interface MenuItemCompat.OnActionExpandListener {
+    method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem);
+    method public abstract boolean onMenuItemActionExpand(android.view.MenuItem);
+  }
+
+  public final class MotionEventCompat {
+    method public static deprecated int findPointerIndex(android.view.MotionEvent, int);
+    method public static deprecated int getActionIndex(android.view.MotionEvent);
+    method public static deprecated int getActionMasked(android.view.MotionEvent);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int, int);
+    method public static deprecated int getButtonState(android.view.MotionEvent);
+    method public static deprecated int getPointerCount(android.view.MotionEvent);
+    method public static deprecated int getPointerId(android.view.MotionEvent, int);
+    method public static deprecated int getSource(android.view.MotionEvent);
+    method public static deprecated float getX(android.view.MotionEvent, int);
+    method public static deprecated float getY(android.view.MotionEvent, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field public static final deprecated int ACTION_HOVER_ENTER = 9; // 0x9
+    field public static final deprecated int ACTION_HOVER_EXIT = 10; // 0xa
+    field public static final deprecated int ACTION_HOVER_MOVE = 7; // 0x7
+    field public static final deprecated int ACTION_MASK = 255; // 0xff
+    field public static final deprecated int ACTION_POINTER_DOWN = 5; // 0x5
+    field public static final deprecated int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field public static final deprecated int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field public static final deprecated int ACTION_POINTER_UP = 6; // 0x6
+    field public static final deprecated int ACTION_SCROLL = 8; // 0x8
+    field public static final deprecated int AXIS_BRAKE = 23; // 0x17
+    field public static final deprecated int AXIS_DISTANCE = 24; // 0x18
+    field public static final deprecated int AXIS_GAS = 22; // 0x16
+    field public static final deprecated int AXIS_GENERIC_1 = 32; // 0x20
+    field public static final deprecated int AXIS_GENERIC_10 = 41; // 0x29
+    field public static final deprecated int AXIS_GENERIC_11 = 42; // 0x2a
+    field public static final deprecated int AXIS_GENERIC_12 = 43; // 0x2b
+    field public static final deprecated int AXIS_GENERIC_13 = 44; // 0x2c
+    field public static final deprecated int AXIS_GENERIC_14 = 45; // 0x2d
+    field public static final deprecated int AXIS_GENERIC_15 = 46; // 0x2e
+    field public static final deprecated int AXIS_GENERIC_16 = 47; // 0x2f
+    field public static final deprecated int AXIS_GENERIC_2 = 33; // 0x21
+    field public static final deprecated int AXIS_GENERIC_3 = 34; // 0x22
+    field public static final deprecated int AXIS_GENERIC_4 = 35; // 0x23
+    field public static final deprecated int AXIS_GENERIC_5 = 36; // 0x24
+    field public static final deprecated int AXIS_GENERIC_6 = 37; // 0x25
+    field public static final deprecated int AXIS_GENERIC_7 = 38; // 0x26
+    field public static final deprecated int AXIS_GENERIC_8 = 39; // 0x27
+    field public static final deprecated int AXIS_GENERIC_9 = 40; // 0x28
+    field public static final deprecated int AXIS_HAT_X = 15; // 0xf
+    field public static final deprecated int AXIS_HAT_Y = 16; // 0x10
+    field public static final deprecated int AXIS_HSCROLL = 10; // 0xa
+    field public static final deprecated int AXIS_LTRIGGER = 17; // 0x11
+    field public static final deprecated int AXIS_ORIENTATION = 8; // 0x8
+    field public static final deprecated int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field public static final deprecated int AXIS_RTRIGGER = 18; // 0x12
+    field public static final deprecated int AXIS_RUDDER = 20; // 0x14
+    field public static final deprecated int AXIS_RX = 12; // 0xc
+    field public static final deprecated int AXIS_RY = 13; // 0xd
+    field public static final deprecated int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field public static final deprecated int AXIS_SIZE = 3; // 0x3
+    field public static final deprecated int AXIS_THROTTLE = 19; // 0x13
+    field public static final deprecated int AXIS_TILT = 25; // 0x19
+    field public static final deprecated int AXIS_TOOL_MAJOR = 6; // 0x6
+    field public static final deprecated int AXIS_TOOL_MINOR = 7; // 0x7
+    field public static final deprecated int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field public static final deprecated int AXIS_TOUCH_MINOR = 5; // 0x5
+    field public static final deprecated int AXIS_VSCROLL = 9; // 0x9
+    field public static final deprecated int AXIS_WHEEL = 21; // 0x15
+    field public static final deprecated int AXIS_X = 0; // 0x0
+    field public static final deprecated int AXIS_Y = 1; // 0x1
+    field public static final deprecated int AXIS_Z = 11; // 0xb
+    field public static final deprecated int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public abstract interface NestedScrollingChild {
+    method public abstract boolean dispatchNestedFling(float, float, boolean);
+    method public abstract boolean dispatchNestedPreFling(float, float);
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public abstract boolean hasNestedScrollingParent();
+    method public abstract boolean isNestedScrollingEnabled();
+    method public abstract void setNestedScrollingEnabled(boolean);
+    method public abstract boolean startNestedScroll(int);
+    method public abstract void stopNestedScroll();
+  }
+
+  public abstract interface NestedScrollingChild2 implements android.support.v4.view.NestedScrollingChild {
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public abstract boolean hasNestedScrollingParent(int);
+    method public abstract boolean startNestedScroll(int, int);
+    method public abstract void stopNestedScroll(int);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(int);
+  }
+
+  public abstract interface NestedScrollingParent {
+    method public abstract int getNestedScrollAxes();
+    method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public abstract void onStopNestedScroll(android.view.View);
+  }
+
+  public abstract interface NestedScrollingParent2 implements android.support.v4.view.NestedScrollingParent {
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public abstract void onStopNestedScroll(android.view.View, int);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public abstract interface OnApplyWindowInsetsListener {
+    method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+  }
+
+  public abstract class PagerAdapter {
+    ctor public PagerAdapter();
+    method public void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void destroyItem(android.view.View, int, java.lang.Object);
+    method public void finishUpdate(android.view.ViewGroup);
+    method public deprecated void finishUpdate(android.view.View);
+    method public abstract int getCount();
+    method public int getItemPosition(java.lang.Object);
+    method public java.lang.CharSequence getPageTitle(int);
+    method public float getPageWidth(int);
+    method public java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated java.lang.Object instantiateItem(android.view.View, int);
+    method public abstract boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public void notifyDataSetChanged();
+    method public void registerDataSetObserver(android.database.DataSetObserver);
+    method public void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public android.os.Parcelable saveState();
+    method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object);
+    method public void startUpdate(android.view.ViewGroup);
+    method public deprecated void startUpdate(android.view.View);
+    method public void unregisterDataSetObserver(android.database.DataSetObserver);
+    field public static final int POSITION_NONE = -2; // 0xfffffffe
+    field public static final int POSITION_UNCHANGED = -1; // 0xffffffff
+  }
+
+  public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip {
+    ctor public PagerTabStrip(android.content.Context);
+    ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet);
+    method public boolean getDrawFullUnderline();
+    method public int getTabIndicatorColor();
+    method public void setDrawFullUnderline(boolean);
+    method public void setTabIndicatorColor(int);
+    method public void setTabIndicatorColorResource(int);
+  }
+
+  public class PagerTitleStrip extends android.view.ViewGroup {
+    ctor public PagerTitleStrip(android.content.Context);
+    ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet);
+    method public int getTextSpacing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setGravity(int);
+    method public void setNonPrimaryAlpha(float);
+    method public void setTextColor(int);
+    method public void setTextSize(int, float);
+    method public void setTextSpacing(int);
+  }
+
+  public final class PointerIconCompat {
+    method public static android.support.v4.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method public static android.support.v4.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static android.support.v4.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static deprecated boolean isQuickScaleEnabled(java.lang.Object);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+    method public static deprecated void setQuickScaleEnabled(java.lang.Object, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+  }
+
+  public abstract interface ScrollingView {
+    method public abstract int computeHorizontalScrollExtent();
+    method public abstract int computeHorizontalScrollOffset();
+    method public abstract int computeHorizontalScrollRange();
+    method public abstract int computeVerticalScrollExtent();
+    method public abstract int computeVerticalScrollOffset();
+    method public abstract int computeVerticalScrollRange();
+  }
+
+  public abstract interface TintableBackgroundView {
+    method public abstract android.content.res.ColorStateList getSupportBackgroundTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode();
+    method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final deprecated class VelocityTrackerCompat {
+    method public static deprecated float getXVelocity(android.view.VelocityTracker, int);
+    method public static deprecated float getYVelocity(android.view.VelocityTracker, int);
+  }
+
+  public class ViewCompat {
+    ctor protected ViewCompat();
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View>, int);
+    method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method public static deprecated boolean canScrollHorizontally(android.view.View, int);
+    method public static deprecated boolean canScrollVertically(android.view.View, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method public static deprecated int combineMeasuredStates(int, int);
+    method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[], int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[], int);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public static deprecated float getAlpha(android.view.View);
+    method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect getClipBounds(android.view.View);
+    method public static android.view.Display getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method public static deprecated int getLayerType(android.view.View);
+    method public static int getLayoutDirection(android.view.View);
+    method public static deprecated android.graphics.Matrix getMatrix(android.view.View);
+    method public static deprecated int getMeasuredHeightAndState(android.view.View);
+    method public static deprecated int getMeasuredState(android.view.View);
+    method public static deprecated int getMeasuredWidthAndState(android.view.View);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static deprecated int getOverScrollMode(android.view.View);
+    method public static int getPaddingEnd(android.view.View);
+    method public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent getParentForAccessibility(android.view.View);
+    method public static deprecated float getPivotX(android.view.View);
+    method public static deprecated float getPivotY(android.view.View);
+    method public static deprecated float getRotation(android.view.View);
+    method public static deprecated float getRotationX(android.view.View);
+    method public static deprecated float getRotationY(android.view.View);
+    method public static deprecated float getScaleX(android.view.View);
+    method public static deprecated float getScaleY(android.view.View);
+    method public static int getScrollIndicators(android.view.View);
+    method public static java.lang.String getTransitionName(android.view.View);
+    method public static deprecated float getTranslationX(android.view.View);
+    method public static deprecated float getTranslationY(android.view.View);
+    method public static float getTranslationZ(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method public static deprecated float getX(android.view.View);
+    method public static deprecated float getY(android.view.View);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method public static deprecated boolean isOpaque(android.view.View);
+    method public static boolean isPaddingRelative(android.view.View);
+    method public static deprecated void jumpDrawablesToCurrentState(android.view.View);
+    method public static android.view.View keyboardNavigationClusterSearch(android.view.View, android.view.View, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static deprecated void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public static deprecated void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, java.lang.Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long);
+    method public static void requestApplyInsets(android.view.View);
+    method public static deprecated int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method public static deprecated void setActivated(android.view.View, boolean);
+    method public static deprecated void setAlpha(android.view.View, float);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode);
+    method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect);
+    method public static void setElevation(android.view.View, float);
+    method public static deprecated void setFitsSystemWindows(android.view.View, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint);
+    method public static deprecated void setLayerType(android.view.View, int, android.graphics.Paint);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener);
+    method public static deprecated void setOverScrollMode(android.view.View, int);
+    method public static void setPaddingRelative(android.view.View, int, int, int, int);
+    method public static deprecated void setPivotX(android.view.View, float);
+    method public static deprecated void setPivotY(android.view.View, float);
+    method public static void setPointerIcon(android.view.View, android.support.v4.view.PointerIconCompat);
+    method public static deprecated void setRotation(android.view.View, float);
+    method public static deprecated void setRotationX(android.view.View, float);
+    method public static deprecated void setRotationY(android.view.View, float);
+    method public static deprecated void setSaveFromParentEnabled(android.view.View, boolean);
+    method public static deprecated void setScaleX(android.view.View, float);
+    method public static deprecated void setScaleY(android.view.View, float);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+    method public static void setTransitionName(android.view.View, java.lang.String);
+    method public static deprecated void setTranslationX(android.view.View, float);
+    method public static deprecated void setTranslationY(android.view.View, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static deprecated void setX(android.view.View, float);
+    method public static deprecated void setY(android.view.View, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static boolean startNestedScroll(android.view.View, int, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final deprecated int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field public static final deprecated int LAYER_TYPE_NONE = 0; // 0x0
+    field public static final deprecated int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field public static final deprecated int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field public static final deprecated int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field public static final deprecated int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field public static final deprecated int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final deprecated int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field public static final deprecated int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field public static final deprecated int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  public final deprecated class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method public static deprecated boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method public static deprecated void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public class ViewPager extends android.view.ViewGroup {
+    ctor public ViewPager(android.content.Context);
+    ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+    method public void addOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public boolean arrowScroll(int);
+    method public boolean beginFakeDrag();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public void clearOnPageChangeListeners();
+    method public void endFakeDrag();
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fakeDragBy(float);
+    method public android.support.v4.view.PagerAdapter getAdapter();
+    method public int getCurrentItem();
+    method public int getOffscreenPageLimit();
+    method public int getPageMargin();
+    method public boolean isFakeDragging();
+    method protected void onLayout(boolean, int, int, int, int);
+    method protected void onPageScrolled(int, float, int);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void removeOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setAdapter(android.support.v4.view.PagerAdapter);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(int);
+    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setPageMargin(int);
+    method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
+    method public void setPageMarginDrawable(int);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer, int);
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewPager.DecorView implements java.lang.annotation.Annotation {
+  }
+
+  public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams {
+    ctor public ViewPager.LayoutParams();
+    ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public int gravity;
+    field public boolean isDecor;
+  }
+
+  public static abstract interface ViewPager.OnAdapterChangeListener {
+    method public abstract void onAdapterChanged(android.support.v4.view.ViewPager, android.support.v4.view.PagerAdapter, android.support.v4.view.PagerAdapter);
+  }
+
+  public static abstract interface ViewPager.OnPageChangeListener {
+    method public abstract void onPageScrollStateChanged(int);
+    method public abstract void onPageScrolled(int, float, int);
+    method public abstract void onPageSelected(int);
+  }
+
+  public static abstract interface ViewPager.PageTransformer {
+    method public abstract void transformPage(android.view.View, float);
+  }
+
+  public static class ViewPager.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public ViewPager.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR;
+  }
+
+  public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public ViewPager.SimpleOnPageChangeListener();
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[], int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View, int);
+    method public static deprecated boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public long getStartDelay();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener);
+    method public void start();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat x(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat y(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat z(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public abstract interface ViewPropertyAnimatorListener {
+    method public abstract void onAnimationCancel(android.view.View);
+    method public abstract void onAnimationEnd(android.view.View);
+    method public abstract void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public abstract interface ViewPropertyAnimatorUpdateListener {
+    method public abstract void onAnimationUpdate(android.view.View);
+  }
+
+  public final class WindowCompat {
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(android.support.v4.view.WindowInsetsCompat);
+    method public android.support.v4.view.WindowInsetsCompat consumeStableInsets();
+    method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public int getStableInsetBottom();
+    method public int getStableInsetLeft();
+    method public int getStableInsetRight();
+    method public int getStableInsetTop();
+    method public int getSystemWindowInsetBottom();
+    method public int getSystemWindowInsetLeft();
+    method public int getSystemWindowInsetRight();
+    method public int getSystemWindowInsetTop();
+    method public boolean hasInsets();
+    method public boolean hasStableInsets();
+    method public boolean hasSystemWindowInsets();
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+  }
+
+}
+
+package android.support.v4.view.accessibility {
+
+  public final class AccessibilityEventCompat {
+    method public static deprecated void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent);
+    method public int getAction(android.view.accessibility.AccessibilityEvent);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int);
+    method public static deprecated int getRecordCount(android.view.accessibility.AccessibilityEvent);
+    method public void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+    method public void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field public static final deprecated int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field public static final deprecated int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field public static final deprecated int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final deprecated int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field public static final deprecated int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method public static deprecated boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  public static abstract deprecated interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method public abstract deprecated void onAccessibilityStateChanged(boolean);
+  }
+
+  public static abstract deprecated class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static abstract interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public abstract void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor public deprecated AccessibilityNodeInfoCompat(java.lang.Object);
+    method public void addAction(int);
+    method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public void addChild(android.view.View);
+    method public void addChild(android.view.View, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList();
+    method public int getActions();
+    method public void getBoundsInParent(android.graphics.Rect);
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getDrawingOrder();
+    method public java.lang.CharSequence getError();
+    method public android.os.Bundle getExtras();
+    method public deprecated java.lang.Object getInfo();
+    method public int getInputType();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public java.lang.CharSequence getPackageName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo();
+    method public java.lang.CharSequence getRoleDescription();
+    method public java.lang.CharSequence getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore();
+    method public java.lang.String getViewIdResourceName();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isVisibleToUser();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public boolean removeChild(android.view.View);
+    method public boolean removeChild(android.view.View, int);
+    method public void setAccessibilityFocused(boolean);
+    method public void setBoundsInParent(android.graphics.Rect);
+    method public void setBoundsInScreen(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(java.lang.Object);
+    method public void setCollectionItemInfo(java.lang.Object);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(java.lang.CharSequence);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View);
+    method public void setLabelFor(android.view.View, int);
+    method public void setLabeledBy(android.view.View);
+    method public void setLabeledBy(android.view.View, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setParent(android.view.View, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat);
+    method public void setRoleDescription(java.lang.CharSequence);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setSource(android.view.View, int);
+    method public void setText(java.lang.CharSequence);
+    method public void setTextSelection(int, int);
+    method public void setTraversalAfter(android.view.View);
+    method public void setTraversalAfter(android.view.View, int);
+    method public void setTraversalBefore(android.view.View);
+    method public void setTraversalBefore(android.view.View, int);
+    method public void setViewIdResourceName(java.lang.String);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo unwrap();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence);
+    method public int getId();
+    method public java.lang.CharSequence getLabel();
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CONTEXT_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_DOWN;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_LEFT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_RIGHT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_TO_POSITION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_UP;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_PROGRESS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_ON_SCREEN;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public boolean isHeading();
+    method public boolean isSelected();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(java.lang.Object);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public java.lang.Object getProvider();
+    method public boolean performAction(int, int, android.os.Bundle);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor public deprecated AccessibilityRecordCompat(java.lang.Object);
+    method public deprecated int getAddedCount();
+    method public deprecated java.lang.CharSequence getBeforeText();
+    method public deprecated java.lang.CharSequence getClassName();
+    method public deprecated java.lang.CharSequence getContentDescription();
+    method public deprecated int getCurrentItemIndex();
+    method public deprecated int getFromIndex();
+    method public deprecated java.lang.Object getImpl();
+    method public deprecated int getItemCount();
+    method public deprecated int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+    method public deprecated int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+    method public deprecated android.os.Parcelable getParcelableData();
+    method public deprecated int getRemovedCount();
+    method public deprecated int getScrollX();
+    method public deprecated int getScrollY();
+    method public deprecated android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource();
+    method public deprecated java.util.List<java.lang.CharSequence> getText();
+    method public deprecated int getToIndex();
+    method public deprecated int getWindowId();
+    method public deprecated boolean isChecked();
+    method public deprecated boolean isEnabled();
+    method public deprecated boolean isFullScreen();
+    method public deprecated boolean isPassword();
+    method public deprecated boolean isScrollable();
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain();
+    method public deprecated void recycle();
+    method public deprecated void setAddedCount(int);
+    method public deprecated void setBeforeText(java.lang.CharSequence);
+    method public deprecated void setChecked(boolean);
+    method public deprecated void setClassName(java.lang.CharSequence);
+    method public deprecated void setContentDescription(java.lang.CharSequence);
+    method public deprecated void setCurrentItemIndex(int);
+    method public deprecated void setEnabled(boolean);
+    method public deprecated void setFromIndex(int);
+    method public deprecated void setFullScreen(boolean);
+    method public deprecated void setItemCount(int);
+    method public deprecated void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setParcelableData(android.os.Parcelable);
+    method public deprecated void setPassword(boolean);
+    method public deprecated void setRemovedCount(int);
+    method public deprecated void setScrollX(int);
+    method public deprecated void setScrollY(int);
+    method public deprecated void setScrollable(boolean);
+    method public deprecated void setSource(android.view.View);
+    method public deprecated void setSource(android.view.View, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View, int);
+    method public deprecated void setToIndex(int);
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot();
+    method public java.lang.CharSequence getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package android.support.v4.view.animation {
+
+  public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutLinearInInterpolator();
+  }
+
+  public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutSlowInInterpolator();
+  }
+
+  public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public LinearOutSlowInInterpolator();
+  }
+
+   abstract class LookupTableInterpolator implements android.view.animation.Interpolator {
+    ctor public LookupTableInterpolator(float[]);
+    method public float getInterpolation(float);
+  }
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package android.support.v4.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+    method public abstract void scrollTargetBy(int, int);
+    method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int);
+    method public android.support.v4.widget.AutoScrollHelper setEdgeType(int);
+    method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode);
+  }
+
+  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 void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public abstract class CursorAdapter extends android.widget.BaseAdapter {
+    ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, int);
+    method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursor(android.database.Cursor);
+    method public java.lang.CharSequence convertToString(android.database.Cursor);
+    method public int getCount();
+    method public android.database.Cursor getCursor();
+    method public android.widget.Filter getFilter();
+    method public android.widget.FilterQueryProvider getFilterQueryProvider();
+    method public java.lang.Object getItem(int);
+    method public long getItemId(int);
+    method public android.view.View getView(int, android.view.View, android.view.ViewGroup);
+    method protected deprecated void init(android.content.Context, android.database.Cursor, boolean);
+    method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method protected void onContentChanged();
+    method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence);
+    method public void setFilterQueryProvider(android.widget.FilterQueryProvider);
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+    field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1
+    field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2
+  }
+
+  public class DrawerLayout extends android.view.ViewGroup {
+    ctor public DrawerLayout(android.content.Context);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void closeDrawer(android.view.View);
+    method public void closeDrawer(android.view.View, boolean);
+    method public void closeDrawer(int);
+    method public void closeDrawer(int, boolean);
+    method public void closeDrawers();
+    method public float getDrawerElevation();
+    method public int getDrawerLockMode(int);
+    method public int getDrawerLockMode(android.view.View);
+    method public java.lang.CharSequence getDrawerTitle(int);
+    method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable();
+    method public boolean isDrawerOpen(android.view.View);
+    method public boolean isDrawerOpen(int);
+    method public boolean isDrawerVisible(android.view.View);
+    method public boolean isDrawerVisible(int);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void openDrawer(android.view.View);
+    method public void openDrawer(android.view.View, boolean);
+    method public void openDrawer(int);
+    method public void openDrawer(int, boolean);
+    method public void removeDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerElevation(float);
+    method public deprecated void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerLockMode(int);
+    method public void setDrawerLockMode(int, int);
+    method public void setDrawerLockMode(int, android.view.View);
+    method public void setDrawerShadow(android.graphics.drawable.Drawable, int);
+    method public void setDrawerShadow(int, int);
+    method public void setDrawerTitle(int, java.lang.CharSequence);
+    method public void setScrimColor(int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackground(int);
+    method public void setStatusBarBackgroundColor(int);
+    field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1
+    field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2
+    field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3
+    field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract interface DrawerLayout.DrawerListener {
+    method public abstract void onDrawerClosed(android.view.View);
+    method public abstract void onDrawerOpened(android.view.View);
+    method public abstract void onDrawerSlide(android.view.View, float);
+    method public abstract void onDrawerStateChanged(int);
+  }
+
+  public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout.LayoutParams(int, int);
+    ctor public DrawerLayout.LayoutParams(int, int, int);
+    ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public int gravity;
+  }
+
+  protected static class DrawerLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public DrawerLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public DrawerLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR;
+  }
+
+  public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public DrawerLayout.SimpleDrawerListener();
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+  }
+
+  public final class EdgeEffectCompat {
+    ctor public deprecated EdgeEffectCompat(android.content.Context);
+    method public deprecated boolean draw(android.graphics.Canvas);
+    method public deprecated void finish();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean onAbsorb(int);
+    method public deprecated boolean onPull(float);
+    method public deprecated boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method public deprecated boolean onRelease();
+    method public deprecated void setSize(int, int);
+  }
+
+  public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public ExploreByTouchHelper(android.view.View);
+    method public final boolean clearKeyboardFocusForVirtualView(int);
+    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public final int getAccessibilityFocusedVirtualViewId();
+    method public deprecated int getFocusedVirtualView();
+    method public final int getKeyboardFocusedVirtualViewId();
+    method protected abstract int getVirtualViewAt(float, float);
+    method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
+    method public final void invalidateRoot();
+    method public final void invalidateVirtualView(int);
+    method public final void invalidateVirtualView(int, int);
+    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
+    method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
+    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+    method public final boolean requestKeyboardFocusForVirtualView(int);
+    method public final boolean sendEventForVirtualView(int, int);
+    field public static final int HOST_ID = -1; // 0xffffffff
+    field public static final int INVALID_ID = -2147483648; // 0x80000000
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode);
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static deprecated android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View);
+    method public static android.view.View.OnTouchListener createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild2 android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int);
+    method public boolean arrowScroll(int);
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollTo(int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static abstract interface NestedScrollView.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter {
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor);
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean);
+    ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int);
+    method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public void setDropDownViewResource(int);
+    method public void setViewResource(int);
+  }
+
+  public final deprecated class ScrollerCompat {
+    method public deprecated void abortAnimation();
+    method public deprecated boolean computeScrollOffset();
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context);
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator);
+    method public deprecated void fling(int, int, int, int, int, int, int, int);
+    method public deprecated void fling(int, int, int, int, int, int, int, int, int, int);
+    method public deprecated float getCurrVelocity();
+    method public deprecated int getCurrX();
+    method public deprecated int getCurrY();
+    method public deprecated int getFinalX();
+    method public deprecated int getFinalY();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean isOverScrolled();
+    method public deprecated void notifyHorizontalEdgeReached(int, int, int);
+    method public deprecated void notifyVerticalEdgeReached(int, int, int);
+    method public deprecated boolean springBack(int, int, int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int, int);
+  }
+
+  public final deprecated class SearchViewCompat {
+    method public static deprecated java.lang.CharSequence getQuery(android.view.View);
+    method public static deprecated boolean isIconified(android.view.View);
+    method public static deprecated boolean isQueryRefinementEnabled(android.view.View);
+    method public static deprecated boolean isSubmitButtonEnabled(android.view.View);
+    method public static deprecated android.view.View newSearchView(android.content.Context);
+    method public static deprecated void setIconified(android.view.View, boolean);
+    method public static deprecated void setImeOptions(android.view.View, int);
+    method public static deprecated void setInputType(android.view.View, int);
+    method public static deprecated void setMaxWidth(android.view.View, int);
+    method public static deprecated void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListener);
+    method public static deprecated void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListener);
+    method public static deprecated void setQuery(android.view.View, java.lang.CharSequence, boolean);
+    method public static deprecated void setQueryHint(android.view.View, java.lang.CharSequence);
+    method public static deprecated void setQueryRefinementEnabled(android.view.View, boolean);
+    method public static deprecated void setSearchableInfo(android.view.View, android.content.ComponentName);
+    method public static deprecated void setSubmitButtonEnabled(android.view.View, boolean);
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnCloseListenerCompat implements android.support.v4.widget.SearchViewCompat.OnCloseListener {
+    ctor public SearchViewCompat.OnCloseListenerCompat();
+    method public boolean onClose();
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnQueryTextListenerCompat implements android.support.v4.widget.SearchViewCompat.OnQueryTextListener {
+    ctor public SearchViewCompat.OnQueryTextListenerCompat();
+    method public boolean onQueryTextChange(java.lang.String);
+    method public boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter {
+    ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]);
+    ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int);
+    method public void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]);
+    method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter();
+    method public int getStringConversionColumn();
+    method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder();
+    method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter);
+    method public void setStringConversionColumn(int);
+    method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder);
+    method public void setViewImage(android.widget.ImageView, java.lang.String);
+    method public void setViewText(android.widget.TextView, java.lang.String);
+  }
+
+  public static abstract interface SimpleCursorAdapter.CursorToStringConverter {
+    method public abstract java.lang.CharSequence convertToString(android.database.Cursor);
+  }
+
+  public static abstract interface SimpleCursorAdapter.ViewBinder {
+    method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int);
+  }
+
+  public class SlidingPaneLayout extends android.view.ViewGroup {
+    ctor public SlidingPaneLayout(android.content.Context);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public deprecated boolean canSlide();
+    method public boolean closePane();
+    method public int getCoveredFadeColor();
+    method public int getParallaxDistance();
+    method public int getSliderFadeColor();
+    method public boolean isOpen();
+    method public boolean isSlideable();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public boolean openPane();
+    method public void setCoveredFadeColor(int);
+    method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener);
+    method public void setParallaxDistance(int);
+    method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableLeft(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableRight(android.graphics.drawable.Drawable);
+    method public deprecated void setShadowResource(int);
+    method public void setShadowResourceLeft(int);
+    method public void setShadowResourceRight(int);
+    method public void setSliderFadeColor(int);
+    method public deprecated void smoothSlideClosed();
+    method public deprecated void smoothSlideOpen();
+  }
+
+  public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public SlidingPaneLayout.LayoutParams();
+    ctor public SlidingPaneLayout.LayoutParams(int, int);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public float weight;
+  }
+
+  public static abstract interface SlidingPaneLayout.PanelSlideListener {
+    method public abstract void onPanelClosed(android.view.View);
+    method public abstract void onPanelOpened(android.view.View);
+    method public abstract void onPanelSlide(android.view.View, float);
+  }
+
+  public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener {
+    ctor public SlidingPaneLayout.SimplePanelSlideListener();
+    method public void onPanelClosed(android.view.View);
+    method public void onPanelOpened(android.view.View);
+    method public void onPanelSlide(android.view.View, float);
+  }
+
+  public class Space extends android.view.View {
+    ctor public Space(android.content.Context, android.util.AttributeSet, int);
+    ctor public Space(android.content.Context, android.util.AttributeSet);
+    ctor public Space(android.content.Context);
+  }
+
+  public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent {
+    ctor public SwipeRefreshLayout(android.content.Context);
+    ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet);
+    method public boolean canChildScrollUp();
+    method public int getProgressCircleDiameter();
+    method public int getProgressViewEndOffset();
+    method public int getProgressViewStartOffset();
+    method public boolean isRefreshing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public deprecated void setColorScheme(int...);
+    method public void setColorSchemeColors(int...);
+    method public void setColorSchemeResources(int...);
+    method public void setDistanceToTriggerSync(int);
+    method public void setOnChildScrollUpCallback(android.support.v4.widget.SwipeRefreshLayout.OnChildScrollUpCallback);
+    method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener);
+    method public deprecated void setProgressBackgroundColor(int);
+    method public void setProgressBackgroundColorSchemeColor(int);
+    method public void setProgressBackgroundColorSchemeResource(int);
+    method public void setProgressViewEndTarget(boolean, int);
+    method public void setProgressViewOffset(boolean, int, int);
+    method public void setRefreshing(boolean);
+    method public void setSize(int);
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+    field protected int mFrom;
+    field protected int mOriginalOffsetTop;
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnChildScrollUpCallback {
+    method public abstract boolean canChildScrollUp(android.support.v4.widget.SwipeRefreshLayout, android.view.View);
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnRefreshListener {
+    method public abstract void onRefresh();
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
+    method public static void setTextAppearance(android.widget.TextView, int);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public abstract interface TintableCompoundButton {
+    method public abstract android.content.res.ColorStateList getSupportButtonTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode();
+    method public abstract void setSupportButtonTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public class ViewDragHelper {
+    method public void abort();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int, int);
+    method public void cancel();
+    method public void captureChildView(android.view.View, int);
+    method public boolean checkTouchSlop(int);
+    method public boolean checkTouchSlop(int, int);
+    method public boolean continueSettling(boolean);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback);
+    method public android.view.View findTopChildUnder(int, int);
+    method public void flingCapturedView(int, int, int, int);
+    method public int getActivePointerId();
+    method public android.view.View getCapturedView();
+    method public int getEdgeSize();
+    method public float getMinVelocity();
+    method public int getTouchSlop();
+    method public int getViewDragState();
+    method public boolean isCapturedViewUnder(int, int);
+    method public boolean isEdgeTouched(int);
+    method public boolean isEdgeTouched(int, int);
+    method public boolean isPointerDown(int);
+    method public boolean isViewUnder(android.view.View, int, int);
+    method public void processTouchEvent(android.view.MotionEvent);
+    method public void setEdgeTrackingEnabled(int);
+    method public void setMinVelocity(float);
+    method public boolean settleCapturedViewAt(int, int);
+    method public boolean shouldInterceptTouchEvent(android.view.MotionEvent);
+    method public boolean smoothSlideViewTo(android.view.View, int, int);
+    field public static final int DIRECTION_ALL = 3; // 0x3
+    field public static final int DIRECTION_HORIZONTAL = 1; // 0x1
+    field public static final int DIRECTION_VERTICAL = 2; // 0x2
+    field public static final int EDGE_ALL = 15; // 0xf
+    field public static final int EDGE_BOTTOM = 8; // 0x8
+    field public static final int EDGE_LEFT = 1; // 0x1
+    field public static final int EDGE_RIGHT = 2; // 0x2
+    field public static final int EDGE_TOP = 4; // 0x4
+    field public static final int INVALID_POINTER = -1; // 0xffffffff
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewDragHelper.Callback {
+    ctor public ViewDragHelper.Callback();
+    method public int clampViewPositionHorizontal(android.view.View, int, int);
+    method public int clampViewPositionVertical(android.view.View, int, int);
+    method public int getOrderedChildIndex(int);
+    method public int getViewHorizontalDragRange(android.view.View);
+    method public int getViewVerticalDragRange(android.view.View);
+    method public void onEdgeDragStarted(int, int);
+    method public boolean onEdgeLock(int);
+    method public void onEdgeTouched(int, int);
+    method public void onViewCaptured(android.view.View, int);
+    method public void onViewDragStateChanged(int);
+    method public void onViewPositionChanged(android.view.View, int, int, int, int);
+    method public void onViewReleased(android.view.View, float, float);
+    method public abstract boolean tryCaptureView(android.view.View, int);
+  }
+
+}
+
+package android.support.v7.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, boolean);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int, boolean);
+    method public abstract android.view.View getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method public abstract deprecated int getNavigationItemCount();
+    method public abstract deprecated int getNavigationMode();
+    method public abstract deprecated int getSelectedNavigationIndex();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getSelectedTab();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getTabAt(int);
+    method public abstract deprecated int getTabCount();
+    method public android.content.Context getThemedContext();
+    method public abstract java.lang.CharSequence getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab newTab();
+    method public abstract deprecated void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void removeTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void removeTabAt(int);
+    method public abstract deprecated void selectTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setCustomView(android.view.View, android.support.v7.app.ActionBar.LayoutParams);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(java.lang.CharSequence);
+    method public void setHomeActionContentDescription(int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.support.v7.app.ActionBar.OnNavigationListener);
+    method public abstract void setLogo(int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setNavigationMode(int);
+    method public abstract deprecated void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
+    field public int gravity;
+  }
+
+  public static abstract interface ActionBar.OnMenuVisibilityListener {
+    method public abstract void onMenuVisibilityChanged(boolean);
+  }
+
+  public static abstract deprecated interface ActionBar.OnNavigationListener {
+    method public abstract boolean onNavigationItemSelected(int, long);
+  }
+
+  public static abstract deprecated class ActionBar.Tab {
+    ctor public ActionBar.Tab();
+    method public abstract java.lang.CharSequence getContentDescription();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.graphics.drawable.Drawable getIcon();
+    method public abstract int getPosition();
+    method public abstract java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract void select();
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(android.view.View);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(android.graphics.drawable.Drawable);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setTabListener(android.support.v7.app.ActionBar.TabListener);
+    method public abstract android.support.v7.app.ActionBar.Tab setTag(java.lang.Object);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static abstract deprecated interface ActionBar.TabListener {
+    method public abstract void onTabReselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabSelected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+  }
+
+  public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
+    method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
+    method public void syncState();
+  }
+
+  public static abstract interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.content.Context getActionBarThemedContext();
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract boolean isNavigationVisible();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends android.support.v7.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.widget.Button getButton(int);
+    method public android.widget.ListView getListView();
+    method public void setButton(int, java.lang.CharSequence, android.os.Message);
+    method public void setButton(int, java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public void setCustomTitle(android.view.View);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIconAttribute(int);
+    method public void setMessage(java.lang.CharSequence);
+    method public void setView(android.view.View);
+    method public void setView(android.view.View, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, int);
+    method public android.support.v7.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public android.support.v7.app.AlertDialog.Builder setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setCancelable(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setCursor(android.database.Cursor, android.content.DialogInterface.OnClickListener, java.lang.String);
+    method public android.support.v7.app.AlertDialog.Builder setCustomTitle(android.view.View);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(int);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.support.v7.app.AlertDialog.Builder setIconAttribute(int);
+    method public deprecated android.support.v7.app.AlertDialog.Builder setInverseBackgroundForced(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setItems(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setItems(java.lang.CharSequence[], android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(int);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(int, boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(java.lang.CharSequence[], boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(android.database.Cursor, java.lang.String, java.lang.String, android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(int, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.database.Cursor, int, java.lang.String, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(java.lang.CharSequence[], int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.widget.ListAdapter, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(int);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setView(int);
+    method public android.support.v7.app.AlertDialog.Builder setView(android.view.View);
+    method public android.support.v7.app.AlertDialog show();
+  }
+
+  public class AppCompatActivity extends android.support.v4.app.FragmentActivity implements android.support.v7.app.ActionBarDrawerToggle.DelegateProvider android.support.v7.app.AppCompatCallback android.support.v4.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public android.content.Intent getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method public void onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public deprecated void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public deprecated void setSupportProgress(int);
+    method public deprecated void setSupportProgressBarIndeterminate(boolean);
+    method public deprecated void setSupportProgressBarIndeterminateVisibility(boolean);
+    method public deprecated void setSupportProgressBarVisibility(boolean);
+    method public android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public abstract interface AppCompatCallback {
+    method public abstract void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public abstract void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public abstract android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract boolean applyDayNight();
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
+    method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T findViewById(int);
+    method public static int getDefaultNightMode();
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration);
+    method public abstract void onCreate(android.os.Bundle);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View);
+    method public abstract void setContentView(int);
+    method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context);
+    ctor public AppCompatDialog(android.content.Context, int);
+    ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class MediaRouteActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.app.MediaRouteButton getMediaRouteButton();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public android.support.v7.app.MediaRouteButton onCreateMediaRouteButton();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<android.support.v7.media.MediaRouter.RouteInfo>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends android.support.v7.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSession();
+    method public android.support.v7.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View onCreateMediaControlView(android.os.Bundle);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static android.support.v7.app.MediaRouteDialogFactory getDefault();
+    method public android.support.v7.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends android.support.v4.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public android.support.v7.media.MediaRouter getMediaRouter();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.media.MediaRouter.Callback onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class NotificationCompat extends android.support.v4.app.NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+  }
+
+  public static class NotificationCompat.Builder extends android.support.v4.app.NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v7.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public android.support.v7.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v7.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+  }
+
+}
+
+package android.support.v7.graphics {
+
+  public final class Palette {
+    method public static android.support.v7.graphics.Palette.Builder from(android.graphics.Bitmap);
+    method public static android.support.v7.graphics.Palette from(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap, int);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, int, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public int getColorForTarget(android.support.v7.graphics.Target, int);
+    method public int getDarkMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
+    method public int getDarkVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
+    method public int getDominantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDominantSwatch();
+    method public int getLightMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
+    method public int getLightVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
+    method public int getMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
+    method public android.support.v7.graphics.Palette.Swatch getSwatchForTarget(android.support.v7.graphics.Target);
+    method public java.util.List<android.support.v7.graphics.Palette.Swatch> getSwatches();
+    method public java.util.List<android.support.v7.graphics.Target> getTargets();
+    method public int getVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
+  }
+
+  public static final class Palette.Builder {
+    ctor public Palette.Builder(android.graphics.Bitmap);
+    ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
+    method public android.support.v7.graphics.Palette.Builder addTarget(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Palette.Builder clearFilters();
+    method public android.support.v7.graphics.Palette.Builder clearRegion();
+    method public android.support.v7.graphics.Palette.Builder clearTargets();
+    method public android.support.v7.graphics.Palette generate();
+    method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
+    method public android.support.v7.graphics.Palette.Builder resizeBitmapArea(int);
+    method public deprecated android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
+    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
+  }
+
+  public static abstract interface Palette.Filter {
+    method public abstract boolean isAllowed(int, float[]);
+  }
+
+  public static abstract interface Palette.PaletteAsyncListener {
+    method public abstract void onGenerated(android.support.v7.graphics.Palette);
+  }
+
+  public static final class Palette.Swatch {
+    ctor public Palette.Swatch(int, int);
+    method public int getBodyTextColor();
+    method public float[] getHsl();
+    method public int getPopulation();
+    method public int getRgb();
+    method public int getTitleTextColor();
+  }
+
+  public final class Target {
+    method public float getLightnessWeight();
+    method public float getMaximumLightness();
+    method public float getMaximumSaturation();
+    method public float getMinimumLightness();
+    method public float getMinimumSaturation();
+    method public float getPopulationWeight();
+    method public float getSaturationWeight();
+    method public float getTargetLightness();
+    method public float getTargetSaturation();
+    method public boolean isExclusive();
+    field public static final android.support.v7.graphics.Target DARK_MUTED;
+    field public static final android.support.v7.graphics.Target DARK_VIBRANT;
+    field public static final android.support.v7.graphics.Target LIGHT_MUTED;
+    field public static final android.support.v7.graphics.Target LIGHT_VIBRANT;
+    field public static final android.support.v7.graphics.Target MUTED;
+    field public static final android.support.v7.graphics.Target VIBRANT;
+  }
+
+  public static final class Target.Builder {
+    ctor public Target.Builder();
+    ctor public Target.Builder(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Target build();
+    method public android.support.v7.graphics.Target.Builder setExclusive(boolean);
+    method public android.support.v7.graphics.Target.Builder setLightnessWeight(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setPopulationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setSaturationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setTargetLightness(float);
+    method public android.support.v7.graphics.Target.Builder setTargetSaturation(float);
+  }
+
+}
+
+package android.support.v7.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package android.support.v7.media {
+
+  public final class MediaControlIntent {
+    field public static final java.lang.String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final java.lang.String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final java.lang.String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final java.lang.String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final java.lang.String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final java.lang.String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final java.lang.String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final java.lang.String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final java.lang.String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final java.lang.String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final java.lang.String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final java.lang.String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final java.lang.String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final java.lang.String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final java.lang.String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final java.lang.String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final java.lang.String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final java.lang.String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final java.lang.String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final java.lang.String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final java.lang.String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final java.lang.String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final java.lang.String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final java.lang.String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final java.lang.String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final java.lang.String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final java.lang.String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final java.lang.String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaItemStatus fromBundle(android.os.Bundle);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final java.lang.String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final java.lang.String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(int);
+    ctor public MediaItemStatus.Builder(android.support.v7.media.MediaItemStatus);
+    method public android.support.v7.media.MediaItemStatus build();
+    method public android.support.v7.media.MediaItemStatus.Builder setContentDuration(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setContentPosition(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public android.support.v7.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static android.support.v7.media.MediaRouteDescriptor fromBundle(android.os.Bundle);
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public deprecated boolean isConnecting();
+    method public boolean isEnabled();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(java.lang.String, java.lang.String);
+    ctor public MediaRouteDescriptor.Builder(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter>);
+    method public android.support.v7.media.MediaRouteDescriptor build();
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method public deprecated android.support.v7.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDescription(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setId(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setName(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(android.support.v7.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteDiscoveryRequest fromBundle(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final android.support.v7.media.MediaRouteProviderDescriptor getDescriptor();
+    method public final android.support.v7.media.MediaRouteDiscoveryRequest getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final android.support.v7.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public android.support.v7.media.MediaRouteProvider.RouteController onCreateRouteController(java.lang.String);
+    method public void onDiscoveryRequestChanged(android.support.v7.media.MediaRouteDiscoveryRequest);
+    method public final void setCallback(android.support.v7.media.MediaRouteProvider.Callback);
+    method public final void setDescriptor(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public final void setDiscoveryRequest(android.support.v7.media.MediaRouteDiscoveryRequest);
+  }
+
+  public static abstract class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(android.support.v7.media.MediaRouteProvider, android.support.v7.media.MediaRouteProviderDescriptor);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+  }
+
+  public static abstract class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteProviderDescriptor fromBundle(android.os.Bundle);
+    method public java.util.List<android.support.v7.media.MediaRouteDescriptor> getRoutes();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoute(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<android.support.v7.media.MediaRouteDescriptor>);
+    method public android.support.v7.media.MediaRouteProviderDescriptor build();
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public android.support.v7.media.MediaRouteProvider getMediaRouteProvider();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.support.v7.media.MediaRouteProvider onCreateMediaRouteProvider();
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(android.support.v7.media.MediaRouteSelector);
+    method public static android.support.v7.media.MediaRouteSelector fromBundle(android.os.Bundle);
+    method public java.util.List<java.lang.String> getControlCategories();
+    method public boolean hasControlCategory(java.lang.String);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter>);
+    field public static final android.support.v7.media.MediaRouteSelector EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String>);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategory(java.lang.String);
+    method public android.support.v7.media.MediaRouteSelector.Builder addSelector(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback);
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
+    method public void addProvider(android.support.v7.media.MediaRouteProvider);
+    method public void addRemoteControlClient(java.lang.Object);
+    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
+    method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
+    method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
+    method public java.util.List<android.support.v7.media.MediaRouter.ProviderInfo> getProviders();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+    method public android.support.v7.media.MediaRouter.RouteInfo getSelectedRoute();
+    method public boolean isRouteAvailable(android.support.v7.media.MediaRouteSelector, int);
+    method public void removeCallback(android.support.v7.media.MediaRouter.Callback);
+    method public void removeProvider(android.support.v7.media.MediaRouteProvider);
+    method public void removeRemoteControlClient(java.lang.Object);
+    method public void selectRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void setMediaSession(java.lang.Object);
+    method public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat);
+    method public void unselect(int);
+    method public android.support.v7.media.MediaRouter.RouteInfo updateSelectedRoute(android.support.v7.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+  }
+
+  public static abstract class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onResult(android.os.Bundle);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+    method public android.support.v7.media.MediaRouteProvider getProviderInstance();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
+    method public android.support.v7.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public boolean isBluetooth();
+    method public boolean isConnecting();
+    method public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method public boolean isSelected();
+    method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
+    method public void requestSetVolume(int);
+    method public void requestUpdateVolume(int);
+    method public void select();
+    method public void sendControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public boolean supportsControlAction(java.lang.String, java.lang.String);
+    method public boolean supportsControlCategory(java.lang.String);
+    method public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaSessionStatus fromBundle(android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(int);
+    ctor public MediaSessionStatus.Builder(android.support.v7.media.MediaSessionStatus);
+    method public android.support.v7.media.MediaSessionStatus build();
+    method public android.support.v7.media.MediaSessionStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public android.support.v7.media.MediaSessionStatus.Builder setSessionState(int);
+    method public android.support.v7.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void enqueue(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public java.lang.String getSessionId();
+    method public void getSessionStatus(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void getStatus(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void play(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void release();
+    method public void remove(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void resume(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void seek(java.lang.String, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void sendMessage(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void setOnMessageReceivedListener(android.support.v7.media.RemotePlaybackClient.OnMessageReceivedListener);
+    method public void setSessionId(java.lang.String);
+    method public void setStatusCallback(android.support.v7.media.RemotePlaybackClient.StatusCallback);
+    method public void startSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void stop(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+  }
+
+  public static abstract class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(java.lang.String, int, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.ItemActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+  }
+
+  public static abstract interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public abstract void onMessageReceived(java.lang.String, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.SessionActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+  public static abstract class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+    method public void onSessionChanged(java.lang.String);
+    method public void onSessionStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+}
+
+package android.support.v7.preference {
+
+  public class CheckBoxPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
+    ctor public CheckBoxPreference(android.content.Context);
+  }
+
+  public abstract class DialogPreference extends android.support.v7.preference.Preference {
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DialogPreference(android.content.Context);
+    method public android.graphics.drawable.Drawable getDialogIcon();
+    method public int getDialogLayoutResource();
+    method public java.lang.CharSequence getDialogMessage();
+    method public java.lang.CharSequence getDialogTitle();
+    method public java.lang.CharSequence getNegativeButtonText();
+    method public java.lang.CharSequence getPositiveButtonText();
+    method public void setDialogIcon(android.graphics.drawable.Drawable);
+    method public void setDialogIcon(int);
+    method public void setDialogLayoutResource(int);
+    method public void setDialogMessage(java.lang.CharSequence);
+    method public void setDialogMessage(int);
+    method public void setDialogTitle(java.lang.CharSequence);
+    method public void setDialogTitle(int);
+    method public void setNegativeButtonText(java.lang.CharSequence);
+    method public void setNegativeButtonText(int);
+    method public void setPositiveButtonText(java.lang.CharSequence);
+    method public void setPositiveButtonText(int);
+  }
+
+  public static abstract interface DialogPreference.TargetFragment {
+    method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+  }
+
+  public class DropDownPreference extends android.support.v7.preference.ListPreference {
+    ctor public DropDownPreference(android.content.Context);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
+    method protected android.widget.ArrayAdapter createAdapter();
+  }
+
+  public class EditTextPreference extends android.support.v7.preference.DialogPreference {
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
+    ctor public EditTextPreference(android.content.Context);
+    method public java.lang.String getText();
+    method public void setText(java.lang.String);
+  }
+
+  public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public EditTextPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.EditTextPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public ListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence getEntry();
+    method public java.lang.CharSequence[] getEntryValues();
+    method public java.lang.String getValue();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValue(java.lang.String);
+    method public void setValueIndex(int);
+  }
+
+  public class ListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public ListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.ListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public MultiSelectListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class Preference implements java.lang.Comparable {
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet);
+    ctor public Preference(android.content.Context);
+    method public boolean callChangeListener(java.lang.Object);
+    method public int compareTo(android.support.v7.preference.Preference);
+    method protected android.support.v7.preference.Preference findPreferenceInHierarchy(java.lang.String);
+    method public android.content.Context getContext();
+    method public java.lang.String getDependency();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getFragment();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.String getKey();
+    method public final int getLayoutResource();
+    method public android.support.v7.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
+    method public android.support.v7.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
+    method public int getOrder();
+    method public android.support.v7.preference.PreferenceGroup getParent();
+    method protected boolean getPersistedBoolean(boolean);
+    method protected float getPersistedFloat(float);
+    method protected int getPersistedInt(int);
+    method protected long getPersistedLong(long);
+    method protected java.lang.String getPersistedString(java.lang.String);
+    method public java.util.Set<java.lang.String> getPersistedStringSet(java.util.Set<java.lang.String>);
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public boolean getShouldDisableView();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public final int getWidgetLayoutResource();
+    method public boolean hasKey();
+    method public boolean isEnabled();
+    method public boolean isIconSpaceReserved();
+    method public boolean isPersistent();
+    method public boolean isSelectable();
+    method public boolean isSingleLineTitle();
+    method public final boolean isVisible();
+    method protected void notifyChanged();
+    method public void notifyDependencyChange(boolean);
+    method protected void notifyHierarchyChanged();
+    method public void onAttached();
+    method protected void onAttachedToHierarchy(android.support.v7.preference.PreferenceManager);
+    method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
+    method protected void onClick();
+    method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
+    method public void onDetached();
+    method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onParentChanged(android.support.v7.preference.Preference, boolean);
+    method protected void onPrepareForRemoval();
+    method protected void onRestoreInstanceState(android.os.Parcelable);
+    method protected android.os.Parcelable onSaveInstanceState();
+    method protected void onSetInitialValue(boolean, java.lang.Object);
+    method public android.os.Bundle peekExtras();
+    method protected boolean persistBoolean(boolean);
+    method protected boolean persistFloat(float);
+    method protected boolean persistInt(int);
+    method protected boolean persistLong(long);
+    method protected boolean persistString(java.lang.String);
+    method public boolean persistStringSet(java.util.Set<java.lang.String>);
+    method public void restoreHierarchyState(android.os.Bundle);
+    method public void saveHierarchyState(android.os.Bundle);
+    method public void setDefaultValue(java.lang.Object);
+    method public void setDependency(java.lang.String);
+    method public void setEnabled(boolean);
+    method public void setFragment(java.lang.String);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIcon(int);
+    method public void setIconSpaceReserved(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setKey(java.lang.String);
+    method public void setLayoutResource(int);
+    method public void setOnPreferenceChangeListener(android.support.v7.preference.Preference.OnPreferenceChangeListener);
+    method public void setOnPreferenceClickListener(android.support.v7.preference.Preference.OnPreferenceClickListener);
+    method public void setOrder(int);
+    method public void setPersistent(boolean);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public void setSelectable(boolean);
+    method public void setShouldDisableView(boolean);
+    method public void setSingleLineTitle(boolean);
+    method public void setSummary(java.lang.CharSequence);
+    method public void setSummary(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitle(int);
+    method public void setViewId(int);
+    method public final void setVisible(boolean);
+    method public void setWidgetLayoutResource(int);
+    method public boolean shouldDisableDependents();
+    method protected boolean shouldPersist();
+    field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
+  }
+
+  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+    ctor public Preference.BaseSavedState(android.os.Parcel);
+    ctor public Preference.BaseSavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.preference.Preference.BaseSavedState> CREATOR;
+  }
+
+  public static abstract interface Preference.OnPreferenceChangeListener {
+    method public abstract boolean onPreferenceChange(android.support.v7.preference.Preference, java.lang.Object);
+  }
+
+  public static abstract interface Preference.OnPreferenceClickListener {
+    method public abstract boolean onPreferenceClick(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceCategory extends android.support.v7.preference.PreferenceGroup {
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
+    ctor public PreferenceCategory(android.content.Context);
+  }
+
+  public abstract class PreferenceDataStore {
+    ctor public PreferenceDataStore();
+    method public boolean getBoolean(java.lang.String, boolean);
+    method public float getFloat(java.lang.String, float);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public java.util.Set<java.lang.String> getStringSet(java.lang.String, java.util.Set<java.lang.String>);
+    method public void putBoolean(java.lang.String, boolean);
+    method public void putFloat(java.lang.String, float);
+    method public void putInt(java.lang.String, int);
+    method public void putLong(java.lang.String, long);
+    method public void putString(java.lang.String, java.lang.String);
+    method public void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
+  }
+
+  public abstract class PreferenceDialogFragmentCompat extends android.support.v4.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragmentCompat();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragmentCompat extends android.support.v4.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragmentCompat();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public abstract class PreferenceGroup extends android.support.v7.preference.Preference {
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
+    method public void addItemFromInflater(android.support.v7.preference.Preference);
+    method public boolean addPreference(android.support.v7.preference.Preference);
+    method protected void dispatchRestoreInstanceState(android.os.Bundle);
+    method protected void dispatchSaveInstanceState(android.os.Bundle);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.support.v7.preference.Preference getPreference(int);
+    method public int getPreferenceCount();
+    method protected boolean isOnSameScreenAsChildren();
+    method public boolean isOrderingAsAdded();
+    method protected boolean onPrepareAddPreference(android.support.v7.preference.Preference);
+    method public void removeAll();
+    method public boolean removePreference(android.support.v7.preference.Preference);
+    method public void setOrderingAsAdded(boolean);
+  }
+
+  public static abstract interface PreferenceGroup.PreferencePositionCallback {
+    method public abstract int getPreferenceAdapterPosition(java.lang.String);
+    method public abstract int getPreferenceAdapterPosition(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceManager {
+    method public android.support.v7.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.content.Context getContext();
+    method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
+    method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
+    method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
+    method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
+    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public int getSharedPreferencesMode();
+    method public java.lang.String getSharedPreferencesName();
+    method public boolean isStorageDefault();
+    method public boolean isStorageDeviceProtected();
+    method public static void setDefaultValues(android.content.Context, int, boolean);
+    method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
+    method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
+    method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
+    method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
+    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
+    method public void setSharedPreferencesMode(int);
+    method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceProtected();
+    method public void showDialog(android.support.v7.preference.Preference);
+    field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
+  }
+
+  public static abstract interface PreferenceManager.OnDisplayPreferenceDialogListener {
+    method public abstract void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceManager.OnNavigateToScreenListener {
+    method public abstract void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+  }
+
+  public static abstract interface PreferenceManager.OnPreferenceTreeClickListener {
+    method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+  }
+
+  public static abstract class PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.PreferenceComparisonCallback();
+    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
+    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
+    method public void setShouldUseGeneratedIds(boolean);
+    method public boolean shouldUseGeneratedIds();
+  }
+
+  public class PreferenceViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
+    method public android.view.View findViewById(int);
+    method public boolean isDividerAllowedAbove();
+    method public boolean isDividerAllowedBelow();
+    method public void setDividerAllowedAbove(boolean);
+    method public void setDividerAllowedBelow(boolean);
+  }
+
+  public class SeekBarPreference extends android.support.v7.preference.Preference {
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SeekBarPreference(android.content.Context);
+    method public int getMax();
+    method public int getMin();
+    method public final int getSeekBarIncrement();
+    method public int getValue();
+    method public boolean isAdjustable();
+    method public void setAdjustable(boolean);
+    method public final void setMax(int);
+    method public void setMin(int);
+    method public final void setSeekBarIncrement(int);
+    method public void setValue(int);
+  }
+
+  public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreferenceCompat(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+  public abstract class TwoStatePreference extends android.support.v7.preference.Preference {
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
+    ctor public TwoStatePreference(android.content.Context);
+    method public boolean getDisableDependentsState();
+    method public java.lang.CharSequence getSummaryOff();
+    method public java.lang.CharSequence getSummaryOn();
+    method public boolean isChecked();
+    method public void setChecked(boolean);
+    method public void setDisableDependentsState(boolean);
+    method public void setSummaryOff(java.lang.CharSequence);
+    method public void setSummaryOff(int);
+    method public void setSummaryOn(java.lang.CharSequence);
+    method public void setSummaryOn(int);
+    method protected void syncSummaryView(android.support.v7.preference.PreferenceViewHolder);
+    field protected boolean mChecked;
+  }
+
+}
+
+package android.support.v7.util {
+
+  public class AsyncListUtil<T> {
+    ctor public AsyncListUtil(java.lang.Class<T>, int, android.support.v7.util.AsyncListUtil.DataCallback<T>, android.support.v7.util.AsyncListUtil.ViewCallback);
+    method public T getItem(int);
+    method public int getItemCount();
+    method public void onRangeChanged();
+    method public void refresh();
+  }
+
+  public static abstract class AsyncListUtil.DataCallback<T> {
+    ctor public AsyncListUtil.DataCallback();
+    method public abstract void fillData(T[], int, int);
+    method public int getMaxCachedTiles();
+    method public void recycleData(T[], int);
+    method public abstract int refreshData();
+  }
+
+  public static abstract class AsyncListUtil.ViewCallback {
+    ctor public AsyncListUtil.ViewCallback();
+    method public void extendRangeInto(int[], int[], int);
+    method public abstract void getItemRangeInto(int[]);
+    method public abstract void onDataRefresh();
+    method public abstract void onItemLoaded(int);
+    field public static final int HINT_SCROLL_ASC = 2; // 0x2
+    field public static final int HINT_SCROLL_DESC = 1; // 0x1
+    field public static final int HINT_SCROLL_NONE = 0; // 0x0
+  }
+
+  public class BatchingListUpdateCallback implements android.support.v7.util.ListUpdateCallback {
+    ctor public BatchingListUpdateCallback(android.support.v7.util.ListUpdateCallback);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int, java.lang.Object);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public class DiffUtil {
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback);
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback, boolean);
+  }
+
+  public static abstract class DiffUtil.Callback {
+    ctor public DiffUtil.Callback();
+    method public abstract boolean areContentsTheSame(int, int);
+    method public abstract boolean areItemsTheSame(int, int);
+    method public java.lang.Object getChangePayload(int, int);
+    method public abstract int getNewListSize();
+    method public abstract int getOldListSize();
+  }
+
+  public static class DiffUtil.DiffResult {
+    method public void dispatchUpdatesTo(android.support.v7.widget.RecyclerView.Adapter);
+    method public void dispatchUpdatesTo(android.support.v7.util.ListUpdateCallback);
+  }
+
+  public abstract interface ListUpdateCallback {
+    method public abstract void onChanged(int, int, java.lang.Object);
+    method public abstract void onInserted(int, int);
+    method public abstract void onMoved(int, int);
+    method public abstract void onRemoved(int, int);
+  }
+
+  public class SortedList<T> {
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>);
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>, int);
+    method public int add(T);
+    method public void addAll(T[], boolean);
+    method public void addAll(T...);
+    method public void addAll(java.util.Collection<T>);
+    method public void beginBatchedUpdates();
+    method public void clear();
+    method public void endBatchedUpdates();
+    method public T get(int) throws java.lang.IndexOutOfBoundsException;
+    method public int indexOf(T);
+    method public void recalculatePositionOfItemAt(int);
+    method public boolean remove(T);
+    method public T removeItemAt(int);
+    method public int size();
+    method public void updateItemAt(int, T);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class SortedList.BatchedCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedList.BatchedCallback(android.support.v7.util.SortedList.Callback<T2>);
+    method public boolean areContentsTheSame(T2, T2);
+    method public boolean areItemsTheSame(T2, T2);
+    method public int compare(T2, T2);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public static abstract class SortedList.Callback<T2> implements java.util.Comparator android.support.v7.util.ListUpdateCallback {
+    ctor public SortedList.Callback();
+    method public abstract boolean areContentsTheSame(T2, T2);
+    method public abstract boolean areItemsTheSame(T2, T2);
+    method public abstract int compare(T2, T2);
+    method public abstract void onChanged(int, int);
+    method public void onChanged(int, int, java.lang.Object);
+  }
+
+}
+
+package android.support.v7.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.view.Menu getMenu();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public void setTag(java.lang.Object);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static abstract interface ActionMode.Callback {
+    method public abstract boolean onActionItemClicked(android.support.v7.view.ActionMode, android.view.MenuItem);
+    method public abstract boolean onCreateActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+    method public abstract void onDestroyActionMode(android.support.v7.view.ActionMode);
+    method public abstract boolean onPrepareActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+  }
+
+  public abstract interface CollapsibleActionView {
+    method public abstract void onActionViewCollapsed();
+    method public abstract void onActionViewExpanded();
+  }
+
+}
+
+package android.support.v7.widget {
+
+  public class ActionMenuView extends android.support.v7.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
+    method public android.view.Menu getMenu();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(android.support.v7.widget.ActionMenuView.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends android.support.v7.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(android.support.v7.widget.ActionMenuView.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static abstract interface ActionMenuView.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setSupportAllCaps(boolean);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class CardView extends android.widget.FrameLayout {
+    ctor public CardView(android.content.Context);
+    ctor public CardView(android.content.Context, android.util.AttributeSet);
+    ctor public CardView(android.content.Context, android.util.AttributeSet, int);
+    method public android.content.res.ColorStateList getCardBackgroundColor();
+    method public float getCardElevation();
+    method public int getContentPaddingBottom();
+    method public int getContentPaddingLeft();
+    method public int getContentPaddingRight();
+    method public int getContentPaddingTop();
+    method public float getMaxCardElevation();
+    method public boolean getPreventCornerOverlap();
+    method public float getRadius();
+    method public boolean getUseCompatPadding();
+    method public void setCardBackgroundColor(int);
+    method public void setCardBackgroundColor(android.content.res.ColorStateList);
+    method public void setCardElevation(float);
+    method public void setContentPadding(int, int, int, int);
+    method public void setMaxCardElevation(float);
+    method public void setPreventCornerOverlap(boolean);
+    method public void setRadius(float);
+    method public void setUseCompatPadding(boolean);
+  }
+
+  public class DefaultItemAnimator extends android.support.v7.widget.SimpleItemAnimator {
+    ctor public DefaultItemAnimator();
+    method public boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimations();
+    method public boolean isRunning();
+    method public void runPendingAnimations();
+  }
+
+  public class DividerItemDecoration extends android.support.v7.widget.RecyclerView.ItemDecoration {
+    ctor public DividerItemDecoration(android.content.Context, int);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setOrientation(int);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayout(android.content.Context);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final android.support.v7.widget.GridLayout.Alignment BASELINE;
+    field public static final android.support.v7.widget.GridLayout.Alignment BOTTOM;
+    field public static final android.support.v7.widget.GridLayout.Alignment CENTER;
+    field public static final android.support.v7.widget.GridLayout.Alignment END;
+    field public static final android.support.v7.widget.GridLayout.Alignment FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final android.support.v7.widget.GridLayout.Alignment LEFT;
+    field public static final android.support.v7.widget.GridLayout.Alignment RIGHT;
+    field public static final android.support.v7.widget.GridLayout.Alignment START;
+    field public static final android.support.v7.widget.GridLayout.Alignment TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.Spec, android.support.v7.widget.GridLayout.Spec);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    method public void setGravity(int);
+    field public android.support.v7.widget.GridLayout.Spec columnSpec;
+    field public android.support.v7.widget.GridLayout.Spec rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public android.support.v7.widget.GridLayout.Alignment getAbsoluteAlignment(boolean);
+  }
+
+  public class GridLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public GridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public GridLayoutManager(android.content.Context, int);
+    ctor public GridLayoutManager(android.content.Context, int, int, boolean);
+    method public int getSpanCount();
+    method public android.support.v7.widget.GridLayoutManager.SpanSizeLookup getSpanSizeLookup();
+    method public void setSpanCount(int);
+    method public void setSpanSizeLookup(android.support.v7.widget.GridLayoutManager.SpanSizeLookup);
+    field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
+  }
+
+  public static final class GridLayoutManager.DefaultSpanSizeLookup extends android.support.v7.widget.GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.DefaultSpanSizeLookup();
+    method public int getSpanSize(int);
+  }
+
+  public static class GridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public GridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayoutManager.LayoutParams(int, int);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getSpanIndex();
+    method public int getSpanSize();
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.SpanSizeLookup();
+    method public int getSpanGroupIndex(int, int);
+    method public int getSpanIndex(int, int);
+    method public abstract int getSpanSize(int);
+    method public void invalidateSpanIndexCache();
+    method public boolean isSpanIndexCacheEnabled();
+    method public void setSpanIndexCacheEnabled(boolean);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet, int);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.support.v7.widget.LinearLayoutCompat.LayoutParams);
+    field public int gravity;
+    field public float weight;
+  }
+
+  public class LinearLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.helper.ItemTouchHelper.ViewDropHandler android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public LinearLayoutManager(android.content.Context);
+    ctor public LinearLayoutManager(android.content.Context, int, boolean);
+    ctor public LinearLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int findFirstCompletelyVisibleItemPosition();
+    method public int findFirstVisibleItemPosition();
+    method public int findLastCompletelyVisibleItemPosition();
+    method public int findLastVisibleItemPosition();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
+    method public int getInitialPrefetchItemCount();
+    method public int getOrientation();
+    method public boolean getRecycleChildrenOnDetach();
+    method public boolean getReverseLayout();
+    method public boolean getStackFromEnd();
+    method protected boolean isLayoutRTL();
+    method public boolean isSmoothScrollbarEnabled();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setOrientation(int);
+    method public void setRecycleChildrenOnDetach(boolean);
+    method public void setReverseLayout(boolean);
+    method public void setSmoothScrollbarEnabled(boolean);
+    method public void setStackFromEnd(boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  protected static class LinearLayoutManager.LayoutChunkResult {
+    ctor protected LinearLayoutManager.LayoutChunkResult();
+    field public int mConsumed;
+    field public boolean mFinished;
+    field public boolean mFocusable;
+    field public boolean mIgnoreConsumed;
+  }
+
+  public class LinearSmoothScroller extends android.support.v7.widget.RecyclerView.SmoothScroller {
+    ctor public LinearSmoothScroller(android.content.Context);
+    method public int calculateDtToFit(int, int, int, int, int);
+    method public int calculateDxToMakeVisible(android.view.View, int);
+    method public int calculateDyToMakeVisible(android.view.View, int);
+    method protected float calculateSpeedPerPixel(android.util.DisplayMetrics);
+    method protected int calculateTimeForDeceleration(int);
+    method protected int calculateTimeForScrolling(int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method protected int getHorizontalSnapPreference();
+    method protected int getVerticalSnapPreference();
+    method protected void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void onStart();
+    method protected void onStop();
+    method protected void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void updateActionForInterimTarget(android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    field public static final int SNAP_TO_ANY = 0; // 0x0
+    field public static final int SNAP_TO_END = 1; // 0x1
+    field public static final int SNAP_TO_START = -1; // 0xffffffff
+    field protected final android.view.animation.DecelerateInterpolator mDecelerateInterpolator;
+    field protected int mInterimTargetDx;
+    field protected int mInterimTargetDy;
+    field protected final android.view.animation.LinearInterpolator mLinearInterpolator;
+    field protected android.graphics.PointF mTargetVector;
+  }
+
+  public class LinearSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public LinearSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int, int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener createDragToOpenListener(android.view.View);
+    method public void dismiss();
+    method public android.view.View getAnchorView();
+    method public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable getBackground();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView getListView();
+    method public int getPromptPosition();
+    method public java.lang.Object getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter);
+    method public void setAnchorView(android.view.View);
+    method public void setAnimationStyle(int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public abstract class OrientationHelper {
+    method public static android.support.v7.widget.OrientationHelper createHorizontalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public static android.support.v7.widget.OrientationHelper createOrientationHelper(android.support.v7.widget.RecyclerView.LayoutManager, int);
+    method public static android.support.v7.widget.OrientationHelper createVerticalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int getDecoratedEnd(android.view.View);
+    method public abstract int getDecoratedMeasurement(android.view.View);
+    method public abstract int getDecoratedMeasurementInOther(android.view.View);
+    method public abstract int getDecoratedStart(android.view.View);
+    method public abstract int getEnd();
+    method public abstract int getEndAfterPadding();
+    method public abstract int getEndPadding();
+    method public abstract int getMode();
+    method public abstract int getModeInOther();
+    method public abstract int getStartAfterPadding();
+    method public abstract int getTotalSpace();
+    method public int getTotalSpaceChange();
+    method public abstract int getTransformedEndWithDecoration(android.view.View);
+    method public abstract int getTransformedStartWithDecoration(android.view.View);
+    method public abstract void offsetChild(android.view.View, int);
+    method public abstract void offsetChildren(int);
+    method public void onLayoutComplete();
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+    field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
+  }
+
+  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public PagerSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, int, int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(android.support.v7.widget.PopupMenu.OnDismissListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.PopupMenu.OnMenuItemClickListener);
+    method public void show();
+  }
+
+  public static abstract interface PopupMenu.OnDismissListener {
+    method public abstract void onDismiss(android.support.v7.widget.PopupMenu);
+  }
+
+  public static abstract interface PopupMenu.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class RecyclerView extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild2 android.support.v4.view.ScrollingView {
+    ctor public RecyclerView(android.content.Context);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void addOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void addOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void addOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void clearOnChildAttachStateChangeListeners();
+    method public void clearOnScrollListeners();
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
+    method public android.view.View findChildViewUnder(float, float);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
+    method public deprecated android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForPosition(int);
+    method public boolean fling(int, int);
+    method public android.support.v7.widget.RecyclerView.Adapter getAdapter();
+    method public int getChildAdapterPosition(android.view.View);
+    method public long getChildItemId(android.view.View);
+    method public int getChildLayoutPosition(android.view.View);
+    method public deprecated int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder getChildViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate();
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator getItemAnimator();
+    method public android.support.v7.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getMaxFlingVelocity();
+    method public int getMinFlingVelocity();
+    method public android.support.v7.widget.RecyclerView.OnFlingListener getOnFlingListener();
+    method public boolean getPreserveFocusAfterLayout();
+    method public android.support.v7.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
+    method public int getScrollState();
+    method public boolean hasFixedSize();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean hasPendingAdapterUpdates();
+    method public void invalidateItemDecorations();
+    method public boolean isAnimating();
+    method public boolean isComputingLayout();
+    method public boolean isLayoutFrozen();
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onChildAttachedToWindow(android.view.View);
+    method public void onChildDetachedFromWindow(android.view.View);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onScrollStateChanged(int);
+    method public void onScrolled(int, int);
+    method public void removeItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void removeOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void removeOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void removeOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void scrollToPosition(int);
+    method public void setAccessibilityDelegateCompat(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+    method public void setAdapter(android.support.v7.widget.RecyclerView.Adapter);
+    method public void setChildDrawingOrderCallback(android.support.v7.widget.RecyclerView.ChildDrawingOrderCallback);
+    method public void setHasFixedSize(boolean);
+    method public void setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator);
+    method public void setItemViewCacheSize(int);
+    method public void setLayoutFrozen(boolean);
+    method public void setLayoutManager(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public void setOnFlingListener(android.support.v7.widget.RecyclerView.OnFlingListener);
+    method public deprecated void setOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void setPreserveFocusAfterLayout(boolean);
+    method public void setRecycledViewPool(android.support.v7.widget.RecyclerView.RecycledViewPool);
+    method public void setRecyclerListener(android.support.v7.widget.RecyclerView.RecyclerListener);
+    method public void setScrollingTouchSlop(int);
+    method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
+    method public void smoothScrollBy(int, int);
+    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
+    method public void smoothScrollToPosition(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+    method public void stopScroll();
+    method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_TYPE = -1; // 0xffffffff
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+    field public static final int NO_POSITION = -1; // 0xffffffff
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+    field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
+    field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder> {
+    ctor public RecyclerView.Adapter();
+    method public final void bindViewHolder(VH, int);
+    method public final VH createViewHolder(android.view.ViewGroup, int);
+    method public abstract int getItemCount();
+    method public long getItemId(int);
+    method public int getItemViewType(int);
+    method public final boolean hasObservers();
+    method public final boolean hasStableIds();
+    method public final void notifyDataSetChanged();
+    method public final void notifyItemChanged(int);
+    method public final void notifyItemChanged(int, java.lang.Object);
+    method public final void notifyItemInserted(int);
+    method public final void notifyItemMoved(int, int);
+    method public final void notifyItemRangeChanged(int, int);
+    method public final void notifyItemRangeChanged(int, int, java.lang.Object);
+    method public final void notifyItemRangeInserted(int, int);
+    method public final void notifyItemRangeRemoved(int, int);
+    method public final void notifyItemRemoved(int);
+    method public void onAttachedToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public abstract void onBindViewHolder(VH, int);
+    method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object>);
+    method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDetachedFromRecyclerView(android.support.v7.widget.RecyclerView);
+    method public boolean onFailedToRecycleView(VH);
+    method public void onViewAttachedToWindow(VH);
+    method public void onViewDetachedFromWindow(VH);
+    method public void onViewRecycled(VH);
+    method public void registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+    method public void setHasStableIds(boolean);
+    method public void unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+  }
+
+  public static abstract class RecyclerView.AdapterDataObserver {
+    ctor public RecyclerView.AdapterDataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeChanged(int, int, java.lang.Object);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeMoved(int, int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public static abstract interface RecyclerView.ChildDrawingOrderCallback {
+    method public abstract int onGetChildDrawingOrder(int, int);
+  }
+
+  public static abstract class RecyclerView.ItemAnimator {
+    ctor public RecyclerView.ItemAnimator();
+    method public abstract boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object>);
+    method public final void dispatchAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationsFinished();
+    method public abstract void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract void endAnimations();
+    method public long getAddDuration();
+    method public long getChangeDuration();
+    method public long getMoveDuration();
+    method public long getRemoveDuration();
+    method public abstract boolean isRunning();
+    method public final boolean isRunning(android.support.v7.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
+    method public void onAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder, int, java.util.List<java.lang.Object>);
+    method public abstract void runPendingAnimations();
+    method public void setAddDuration(long);
+    method public void setChangeDuration(long);
+    method public void setMoveDuration(long);
+    method public void setRemoveDuration(long);
+    field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
+    field public static final int FLAG_CHANGED = 2; // 0x2
+    field public static final int FLAG_INVALIDATED = 4; // 0x4
+    field public static final int FLAG_MOVED = 2048; // 0x800
+    field public static final int FLAG_REMOVED = 8; // 0x8
+  }
+
+  public static abstract class RecyclerView.ItemAnimator.AdapterChanges implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
+    method public abstract void onAnimationsFinished();
+  }
+
+  public static class RecyclerView.ItemAnimator.ItemHolderInfo {
+    ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public int bottom;
+    field public int changeFlags;
+    field public int left;
+    field public int right;
+    field public int top;
+  }
+
+  public static abstract class RecyclerView.ItemDecoration {
+    ctor public RecyclerView.ItemDecoration();
+    method public deprecated void getItemOffsets(android.graphics.Rect, int, android.support.v7.widget.RecyclerView);
+    method public void getItemOffsets(android.graphics.Rect, android.view.View, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+    method public void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+  }
+
+  public static abstract class RecyclerView.LayoutManager {
+    ctor public RecyclerView.LayoutManager();
+    method public void addDisappearingView(android.view.View);
+    method public void addDisappearingView(android.view.View, int);
+    method public void addView(android.view.View);
+    method public void addView(android.view.View, int);
+    method public void assertInLayoutOrScroll(java.lang.String);
+    method public void assertNotInLayoutOrScroll(java.lang.String);
+    method public void attachView(android.view.View, int, android.support.v7.widget.RecyclerView.LayoutParams);
+    method public void attachView(android.view.View, int);
+    method public void attachView(android.view.View);
+    method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
+    method public boolean canScrollHorizontally();
+    method public boolean canScrollVertically();
+    method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public static int chooseSize(int, int, int);
+    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public void detachAndScrapAttachedViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachView(android.view.View);
+    method public void detachViewAt(int);
+    method public void endAnimation(android.view.View);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.view.View findViewByPosition(int);
+    method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.content.Context, android.util.AttributeSet);
+    method public int getBaseline();
+    method public int getBottomDecorationHeight(android.view.View);
+    method public android.view.View getChildAt(int);
+    method public int getChildCount();
+    method public static deprecated int getChildMeasureSpec(int, int, int, boolean);
+    method public static int getChildMeasureSpec(int, int, int, int, boolean);
+    method public boolean getClipToPadding();
+    method public int getColumnCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getDecoratedBottom(android.view.View);
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public int getDecoratedLeft(android.view.View);
+    method public int getDecoratedMeasuredHeight(android.view.View);
+    method public int getDecoratedMeasuredWidth(android.view.View);
+    method public int getDecoratedRight(android.view.View);
+    method public int getDecoratedTop(android.view.View);
+    method public android.view.View getFocusedChild();
+    method public int getHeight();
+    method public int getHeightMode();
+    method public int getItemCount();
+    method public int getItemViewType(android.view.View);
+    method public int getLayoutDirection();
+    method public int getLeftDecorationWidth(android.view.View);
+    method public int getMinimumHeight();
+    method public int getMinimumWidth();
+    method public int getPaddingBottom();
+    method public int getPaddingEnd();
+    method public int getPaddingLeft();
+    method public int getPaddingRight();
+    method public int getPaddingStart();
+    method public int getPaddingTop();
+    method public int getPosition(android.view.View);
+    method public static android.support.v7.widget.RecyclerView.LayoutManager.Properties getProperties(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getRightDecorationWidth(android.view.View);
+    method public int getRowCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getSelectionModeForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getTopDecorationHeight(android.view.View);
+    method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
+    method public int getWidth();
+    method public int getWidthMode();
+    method public boolean hasFocus();
+    method public void ignoreView(android.view.View);
+    method public boolean isAttachedToWindow();
+    method public boolean isAutoMeasureEnabled();
+    method public boolean isFocused();
+    method public final boolean isItemPrefetchEnabled();
+    method public boolean isLayoutHierarchical(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public boolean isMeasurementCacheEnabled();
+    method public boolean isSmoothScrolling();
+    method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
+    method public void layoutDecorated(android.view.View, int, int, int, int);
+    method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
+    method public void measureChild(android.view.View, int, int);
+    method public void measureChildWithMargins(android.view.View, int, int);
+    method public void moveView(int, int);
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onAdapterChanged(android.support.v7.widget.RecyclerView.Adapter, android.support.v7.widget.RecyclerView.Adapter);
+    method public boolean onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int);
+    method public void onAttachedToWindow(android.support.v7.widget.RecyclerView);
+    method public deprecated void onDetachedFromWindow(android.support.v7.widget.RecyclerView);
+    method public void onDetachedFromWindow(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.Recycler);
+    method public android.view.View onFocusSearchFailed(android.view.View, int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityEvent(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onInitializeAccessibilityNodeInfoForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public android.view.View onInterceptFocusSearch(android.view.View, int);
+    method public void onItemsAdded(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsChanged(android.support.v7.widget.RecyclerView);
+    method public void onItemsMoved(android.support.v7.widget.RecyclerView, int, int, int);
+    method public void onItemsRemoved(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int, java.lang.Object);
+    method public void onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onLayoutCompleted(android.support.v7.widget.RecyclerView.State);
+    method public void onMeasure(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, int);
+    method public deprecated boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.view.View, android.view.View);
+    method public boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, android.view.View, android.view.View);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void onScrollStateChanged(int);
+    method public boolean performAccessibilityAction(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, android.os.Bundle);
+    method public boolean performAccessibilityActionForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, int, android.os.Bundle);
+    method public void postOnAnimation(java.lang.Runnable);
+    method public void removeAllViews();
+    method public void removeAndRecycleAllViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public boolean removeCallbacks(java.lang.Runnable);
+    method public void removeDetachedView(android.view.View);
+    method public void removeView(android.view.View);
+    method public void removeViewAt(int);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
+    method public void requestLayout();
+    method public void requestSimpleAnimationsInNextLayout();
+    method public int scrollHorizontallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void scrollToPosition(int);
+    method public int scrollVerticallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void setAutoMeasureEnabled(boolean);
+    method public final void setItemPrefetchEnabled(boolean);
+    method public void setMeasuredDimension(android.graphics.Rect, int, int);
+    method public void setMeasuredDimension(int, int);
+    method public void setMeasurementCacheEnabled(boolean);
+    method public void smoothScrollToPosition(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, int);
+    method public void startSmoothScroll(android.support.v7.widget.RecyclerView.SmoothScroller);
+    method public void stopIgnoringView(android.view.View);
+    method public boolean supportsPredictiveItemAnimations();
+  }
+
+  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+    method public abstract void addPosition(int, int);
+  }
+
+  public static class RecyclerView.LayoutManager.Properties {
+    ctor public RecyclerView.LayoutManager.Properties();
+    field public int orientation;
+    field public boolean reverseLayout;
+    field public int spanCount;
+    field public boolean stackFromEnd;
+  }
+
+  public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public RecyclerView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView.LayoutParams(int, int);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public RecyclerView.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getViewAdapterPosition();
+    method public int getViewLayoutPosition();
+    method public deprecated int getViewPosition();
+    method public boolean isItemChanged();
+    method public boolean isItemRemoved();
+    method public boolean isViewInvalid();
+    method public boolean viewNeedsUpdate();
+  }
+
+  public static abstract interface RecyclerView.OnChildAttachStateChangeListener {
+    method public abstract void onChildViewAttachedToWindow(android.view.View);
+    method public abstract void onChildViewDetachedFromWindow(android.view.View);
+  }
+
+  public static abstract class RecyclerView.OnFlingListener {
+    ctor public RecyclerView.OnFlingListener();
+    method public abstract boolean onFling(int, int);
+  }
+
+  public static abstract interface RecyclerView.OnItemTouchListener {
+    method public abstract boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public abstract void onRequestDisallowInterceptTouchEvent(boolean);
+    method public abstract void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.OnScrollListener {
+    ctor public RecyclerView.OnScrollListener();
+    method public void onScrollStateChanged(android.support.v7.widget.RecyclerView, int);
+    method public void onScrolled(android.support.v7.widget.RecyclerView, int, int);
+  }
+
+  public static class RecyclerView.RecycledViewPool {
+    ctor public RecyclerView.RecycledViewPool();
+    method public void clear();
+    method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
+    method public int getRecycledViewCount(int);
+    method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setMaxRecycledViews(int, int);
+  }
+
+  public final class RecyclerView.Recycler {
+    ctor public RecyclerView.Recycler();
+    method public void bindViewToPosition(android.view.View, int);
+    method public void clear();
+    method public int convertPreLayoutPositionToPostLayout(int);
+    method public java.util.List<android.support.v7.widget.RecyclerView.ViewHolder> getScrapList();
+    method public android.view.View getViewForPosition(int);
+    method public void recycleView(android.view.View);
+    method public void setViewCacheSize(int);
+  }
+
+  public static abstract interface RecyclerView.RecyclerListener {
+    method public abstract void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+  public static class RecyclerView.SimpleOnItemTouchListener implements android.support.v7.widget.RecyclerView.OnItemTouchListener {
+    ctor public RecyclerView.SimpleOnItemTouchListener();
+    method public boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public void onRequestDisallowInterceptTouchEvent(boolean);
+    method public void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.SmoothScroller {
+    ctor public RecyclerView.SmoothScroller();
+    method public android.view.View findViewByPosition(int);
+    method public int getChildCount();
+    method public int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getTargetPosition();
+    method public deprecated void instantScrollToPosition(int);
+    method public boolean isPendingInitialRun();
+    method public boolean isRunning();
+    method protected void normalize(android.graphics.PointF);
+    method protected void onChildAttachedToWindow(android.view.View);
+    method protected abstract void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected abstract void onStart();
+    method protected abstract void onStop();
+    method protected abstract void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method public void setTargetPosition(int);
+    method protected final void stop();
+  }
+
+  public static class RecyclerView.SmoothScroller.Action {
+    ctor public RecyclerView.SmoothScroller.Action(int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int, android.view.animation.Interpolator);
+    method public int getDuration();
+    method public int getDx();
+    method public int getDy();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public void jumpTo(int);
+    method public void setDuration(int);
+    method public void setDx(int);
+    method public void setDy(int);
+    method public void setInterpolator(android.view.animation.Interpolator);
+    method public void update(int, int, int, android.view.animation.Interpolator);
+    field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+  }
+
+  public static abstract interface RecyclerView.SmoothScroller.ScrollVectorProvider {
+    method public abstract android.graphics.PointF computeScrollVectorForPosition(int);
+  }
+
+  public static class RecyclerView.State {
+    ctor public RecyclerView.State();
+    method public boolean didStructureChange();
+    method public <T> T get(int);
+    method public int getItemCount();
+    method public int getRemainingScrollHorizontal();
+    method public int getRemainingScrollVertical();
+    method public int getTargetScrollPosition();
+    method public boolean hasTargetScrollPosition();
+    method public boolean isMeasuring();
+    method public boolean isPreLayout();
+    method public void put(int, java.lang.Object);
+    method public void remove(int);
+    method public boolean willRunPredictiveAnimations();
+    method public boolean willRunSimpleAnimations();
+  }
+
+  public static abstract class RecyclerView.ViewCacheExtension {
+    ctor public RecyclerView.ViewCacheExtension();
+    method public abstract android.view.View getViewForPositionAndType(android.support.v7.widget.RecyclerView.Recycler, int, int);
+  }
+
+  public static abstract class RecyclerView.ViewHolder {
+    ctor public RecyclerView.ViewHolder(android.view.View);
+    method public final int getAdapterPosition();
+    method public final long getItemId();
+    method public final int getItemViewType();
+    method public final int getLayoutPosition();
+    method public final int getOldPosition();
+    method public final deprecated int getPosition();
+    method public final boolean isRecyclable();
+    method public final void setIsRecyclable(boolean);
+    field public final android.view.View itemView;
+  }
+
+  public class RecyclerViewAccessibilityDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate(android.support.v7.widget.RecyclerView);
+    method public android.support.v4.view.AccessibilityDelegateCompat getItemDelegate();
+  }
+
+  public static class RecyclerViewAccessibilityDelegate.ItemDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate.ItemDelegate(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+  }
+
+  public class SearchView extends android.support.v7.widget.LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public java.lang.CharSequence getQuery();
+    method public java.lang.CharSequence getQueryHint();
+    method public android.support.v4.widget.CursorAdapter getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(android.support.v7.widget.SearchView.OnCloseListener);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener);
+    method public void setOnQueryTextListener(android.support.v7.widget.SearchView.OnQueryTextListener);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener);
+    method public void setOnSuggestionListener(android.support.v7.widget.SearchView.OnSuggestionListener);
+    method public void setQuery(java.lang.CharSequence, boolean);
+    method public void setQueryHint(java.lang.CharSequence);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(android.support.v4.widget.CursorAdapter);
+  }
+
+  public static abstract interface SearchView.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract interface SearchView.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchView.OnSuggestionListener {
+    method public abstract boolean onSuggestionClick(int);
+    method public abstract boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context);
+    method public android.view.View onCreateActionView();
+    method public void setOnShareTargetSelectedListener(android.support.v7.widget.ShareActionProvider.OnShareTargetSelectedListener);
+    method public void setShareHistoryFileName(java.lang.String);
+    method public void setShareIntent(android.content.Intent);
+    field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static abstract interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public abstract boolean onShareTargetSelected(android.support.v7.widget.ShareActionProvider, android.content.Intent);
+  }
+
+  public abstract class SimpleItemAnimator extends android.support.v7.widget.RecyclerView.ItemAnimator {
+    ctor public SimpleItemAnimator();
+    method public abstract boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean getSupportsChangeAnimations();
+    method public void onAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setSupportsChangeAnimations(boolean);
+  }
+
+  public abstract class SnapHelper extends android.support.v7.widget.RecyclerView.OnFlingListener {
+    ctor public SnapHelper();
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
+    method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public int[] calculateScrollDistance(int, int);
+    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+    method public boolean onFling(int, int);
+  }
+
+  public class StaggeredGridLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public StaggeredGridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public StaggeredGridLayoutManager(int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int[] findFirstCompletelyVisibleItemPositions(int[]);
+    method public int[] findFirstVisibleItemPositions(int[]);
+    method public int[] findLastCompletelyVisibleItemPositions(int[]);
+    method public int[] findLastVisibleItemPositions(int[]);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public int getGapStrategy();
+    method public int getOrientation();
+    method public boolean getReverseLayout();
+    method public int getSpanCount();
+    method public void invalidateSpanAssignments();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setGapStrategy(int);
+    method public void setOrientation(int);
+    method public void setReverseLayout(boolean);
+    method public void setSpanCount(int);
+    field public static final deprecated int GAP_HANDLING_LAZY = 1; // 0x1
+    field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
+    field public static final int GAP_HANDLING_NONE = 0; // 0x0
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class StaggeredGridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public final int getSpanIndex();
+    method public boolean isFullSpan();
+    method public void setFullSpan(boolean);
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public java.lang.CharSequence getTextOff();
+    method public java.lang.CharSequence getTextOn();
+    method public android.graphics.drawable.Drawable getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context, int);
+    method public void setSwitchTypeface(android.graphics.Typeface, int);
+    method public void setSwitchTypeface(android.graphics.Typeface);
+    method public void setTextOff(java.lang.CharSequence);
+    method public void setTextOn(java.lang.CharSequence);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public abstract interface ThemedSpinnerAdapter implements android.widget.SpinnerAdapter {
+    method public abstract android.content.res.Resources.Theme getDropDownViewTheme();
+    method public abstract void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable getLogo();
+    method public java.lang.CharSequence getLogoDescription();
+    method public android.view.Menu getMenu();
+    method public java.lang.CharSequence getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable getNavigationIcon();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable);
+    method public void setLogoDescription(int);
+    method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationIcon(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.Toolbar.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public void setSubtitle(int);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setSubtitleTextAppearance(android.content.Context, int);
+    method public void setSubtitleTextColor(int);
+    method public void setTitle(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context, int);
+    method public void setTitleTextColor(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends android.support.v7.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(android.support.v7.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
+  }
+
+  public static abstract interface Toolbar.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public static class Toolbar.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel);
+    ctor public Toolbar.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public Toolbar.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.widget.Toolbar.SavedState> CREATOR;
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+  }
+
+}
+
+package android.support.v7.widget.helper {
+
+  public class ItemTouchHelper extends android.support.v7.widget.RecyclerView.ItemDecoration implements android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener {
+    ctor public ItemTouchHelper(android.support.v7.widget.helper.ItemTouchHelper.Callback);
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public void onChildViewAttachedToWindow(android.view.View);
+    method public void onChildViewDetachedFromWindow(android.view.View);
+    method public void startDrag(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void startSwipe(android.support.v7.widget.RecyclerView.ViewHolder);
+    field public static final int ACTION_STATE_DRAG = 2; // 0x2
+    field public static final int ACTION_STATE_IDLE = 0; // 0x0
+    field public static final int ACTION_STATE_SWIPE = 1; // 0x1
+    field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
+    field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
+    field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
+    field public static final int DOWN = 2; // 0x2
+    field public static final int END = 32; // 0x20
+    field public static final int LEFT = 4; // 0x4
+    field public static final int RIGHT = 8; // 0x8
+    field public static final int START = 16; // 0x10
+    field public static final int UP = 1; // 0x1
+  }
+
+  public static abstract class ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.Callback();
+    method public boolean canDropOver(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ViewHolder chooseDropTarget(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<android.support.v7.widget.RecyclerView.ViewHolder>, int, int);
+    method public void clearView(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int convertToAbsoluteDirection(int, int);
+    method public static int convertToRelativeDirection(int, int);
+    method public long getAnimationDuration(android.support.v7.widget.RecyclerView, int, float, float);
+    method public int getBoundingBoxMargin();
+    method public static android.support.v7.widget.helper.ItemTouchUIUtil getDefaultUIUtil();
+    method public float getMoveThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeEscapeVelocity(float);
+    method public float getSwipeThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeVelocityThreshold(float);
+    method public int interpolateOutOfBoundsScroll(android.support.v7.widget.RecyclerView, int, int, int, long);
+    method public boolean isItemViewSwipeEnabled();
+    method public boolean isLongPressDragEnabled();
+    method public static int makeFlag(int, int);
+    method public static int makeMovementFlags(int, int);
+    method public void onChildDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public void onChildDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public abstract boolean onMove(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoved(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int);
+    method public void onSelectedChanged(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method public abstract void onSwiped(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
+    field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
+  }
+
+  public static abstract class ItemTouchHelper.SimpleCallback extends android.support.v7.widget.helper.ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.SimpleCallback(int, int);
+    method public int getDragDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getSwipeDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setDefaultDragDirs(int);
+    method public void setDefaultSwipeDirs(int);
+  }
+
+  public static abstract interface ItemTouchHelper.ViewDropHandler {
+    method public abstract void prepareForDrop(android.view.View, android.view.View, int, int);
+  }
+
+  public abstract interface ItemTouchUIUtil {
+    method public abstract void clearView(android.view.View);
+    method public abstract void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onSelected(android.view.View);
+  }
+
+}
+
+package android.support.v7.widget.util {
+
+  public abstract class SortedListAdapterCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedListAdapterCallback(android.support.v7.widget.RecyclerView.Adapter);
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+}
+
+package android.support.wear.widget {
+
+  public class BoxInsetLayout extends android.view.ViewGroup {
+    ctor public BoxInsetLayout(android.content.Context);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected void onLayout(boolean, int, int, int, int);
+  }
+
+  public static class BoxInsetLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BoxInsetLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout.LayoutParams(int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.support.wear.widget.BoxInsetLayout.LayoutParams);
+    field public static final int BOX_ALL = 15; // 0xf
+    field public static final int BOX_BOTTOM = 8; // 0x8
+    field public static final int BOX_LEFT = 1; // 0x1
+    field public static final int BOX_NONE = 0; // 0x0
+    field public static final int BOX_RIGHT = 4; // 0x4
+    field public static final int BOX_TOP = 2; // 0x2
+    field public int boxedEdges;
+  }
+
+  public class CurvingLayoutCallback extends android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback {
+    ctor public CurvingLayoutCallback(android.content.Context);
+    method public void adjustAnchorOffsetXY(android.view.View, float[]);
+    method public void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class SwipeDismissFrameLayout extends android.widget.FrameLayout {
+    ctor public SwipeDismissFrameLayout(android.content.Context);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+    method public void removeCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+  }
+
+  public static abstract class SwipeDismissFrameLayout.Callback {
+    ctor public SwipeDismissFrameLayout.Callback();
+    method public void onDismissed(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeCanceled(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeStarted(android.support.wear.widget.SwipeDismissFrameLayout);
+  }
+
+  public class WearableLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public WearableLinearLayoutManager(android.content.Context, android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+    ctor public WearableLinearLayoutManager(android.content.Context);
+    method public android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback getLayoutCallback();
+    method public void setLayoutCallback(android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+  }
+
+  public static abstract class WearableLinearLayoutManager.LayoutCallback {
+    ctor public WearableLinearLayoutManager.LayoutCallback();
+    method public abstract void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class WearableRecyclerView extends android.support.v7.widget.RecyclerView {
+    ctor public WearableRecyclerView(android.content.Context);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public float getBezelFraction();
+    method public float getScrollDegreesPerScreen();
+    method public boolean isCircularScrollingGestureEnabled();
+    method public boolean isEdgeItemsCenteringEnabled();
+    method public void setBezelFraction(float);
+    method public void setCircularScrollingGestureEnabled(boolean);
+    method public void setEdgeItemsCenteringEnabled(boolean);
+    method public void setScrollDegreesPerScreen(float);
+  }
+
+}
+
+package android.support.wear.widget.drawer {
+
+  public class WearableActionDrawerView extends android.support.wear.widget.drawer.WearableDrawerView {
+    ctor public WearableActionDrawerView(android.content.Context);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.view.Menu getMenu();
+    method public void setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public class WearableDrawerController {
+    method public void closeDrawer();
+    method public void openDrawer();
+    method public void peekDrawer();
+  }
+
+  public class WearableDrawerLayout extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingParent android.view.View.OnLayoutChangeListener {
+    ctor public WearableDrawerLayout(android.content.Context);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void onFlingComplete(android.view.View);
+    method public void onLayoutChange(android.view.View, int, int, int, int, int, int, int, int);
+    method public void setDrawerStateCallback(android.support.wear.widget.drawer.WearableDrawerLayout.DrawerStateCallback);
+  }
+
+  public static class WearableDrawerLayout.DrawerStateCallback {
+    ctor public WearableDrawerLayout.DrawerStateCallback();
+    method public void onDrawerClosed(android.support.wear.widget.drawer.WearableDrawerLayout, android.support.wear.widget.drawer.WearableDrawerView);
+    method public void onDrawerOpened(android.support.wear.widget.drawer.WearableDrawerLayout, android.support.wear.widget.drawer.WearableDrawerView);
+    method public void onDrawerStateChanged(android.support.wear.widget.drawer.WearableDrawerLayout, int);
+  }
+
+  public class WearableDrawerView extends android.widget.FrameLayout {
+    ctor public WearableDrawerView(android.content.Context);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.support.wear.widget.drawer.WearableDrawerController getController();
+    method public android.view.View getDrawerContent();
+    method public int getDrawerState();
+    method public boolean isAutoPeekEnabled();
+    method public boolean isClosed();
+    method public boolean isLocked();
+    method public boolean isLockedWhenClosed();
+    method public boolean isOpenOnlyAtTopEnabled();
+    method public boolean isOpened();
+    method public boolean isPeekOnScrollDownEnabled();
+    method public boolean isPeeking();
+    method public void onDrawerClosed();
+    method public void onDrawerOpened();
+    method public void onDrawerStateChanged(int);
+    method public void onPeekContainerClicked(android.view.View);
+    method public void setDrawerContent(android.view.View);
+    method public void setIsAutoPeekEnabled(boolean);
+    method public void setIsLocked(boolean);
+    method public void setLockedWhenClosed(boolean);
+    method public void setOpenOnlyAtTopEnabled(boolean);
+    method public void setPeekContent(android.view.View);
+    method public void setPeekOnScrollDownEnabled(boolean);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public class WearableNavigationDrawerView extends android.support.wear.widget.drawer.WearableDrawerView {
+    ctor public WearableNavigationDrawerView(android.content.Context);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addOnItemSelectedListener(android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener);
+    method public int getNavigationStyle();
+    method public void removeOnItemSelectedListener(android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener);
+    method public void setAdapter(android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter);
+    method public void setCurrentItem(int, boolean);
+    field public static final int MULTI_PAGE = 1; // 0x1
+    field public static final int SINGLE_PAGE = 0; // 0x0
+  }
+
+  public static abstract interface WearableNavigationDrawerView.OnItemSelectedListener {
+    method public abstract void onItemSelected(int);
+  }
+
+  public static abstract class WearableNavigationDrawerView.WearableNavigationDrawerAdapter {
+    ctor public WearableNavigationDrawerView.WearableNavigationDrawerAdapter();
+    method public abstract int getCount();
+    method public abstract android.graphics.drawable.Drawable getItemDrawable(int);
+    method public abstract java.lang.CharSequence getItemText(int);
+    method public void notifyDataSetChanged();
+  }
+
+}
+
diff --git a/api/26.0.0.txt b/api/26.0.0.txt
new file mode 100644
index 0000000..fe0c575
--- /dev/null
+++ b/api/26.0.0.txt
@@ -0,0 +1,13266 @@
+package android.support.animation {
+
+  public abstract class DynamicAnimation<T extends android.support.animation.DynamicAnimation<T>> {
+    method public T addEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public T addUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public void cancel();
+    method public float getMinimumVisibleChange();
+    method public boolean isRunning();
+    method public void removeEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public void removeUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public T setMaxValue(float);
+    method public T setMinValue(float);
+    method public T setMinimumVisibleChange(float);
+    method public T setStartValue(float);
+    method public T setStartVelocity(float);
+    method public void start();
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ALPHA;
+    field public static final float MIN_VISIBLE_CHANGE_ALPHA = 0.00390625f;
+    field public static final float MIN_VISIBLE_CHANGE_PIXELS = 1.0f;
+    field public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 0.1f;
+    field public static final float MIN_VISIBLE_CHANGE_SCALE = 0.002f;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Z;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Z;
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationEndListener {
+    method public abstract void onAnimationEnd(android.support.animation.DynamicAnimation, boolean, float, float);
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationUpdateListener {
+    method public abstract void onAnimationUpdate(android.support.animation.DynamicAnimation, float, float);
+  }
+
+  public static abstract class DynamicAnimation.ViewProperty extends android.support.animation.FloatPropertyCompat {
+  }
+
+  public final class FlingAnimation extends android.support.animation.DynamicAnimation {
+    ctor public FlingAnimation(android.support.animation.FloatValueHolder);
+    ctor public FlingAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    method public float getFriction();
+    method public android.support.animation.FlingAnimation setFriction(float);
+    method public android.support.animation.FlingAnimation setMaxValue(float);
+    method public android.support.animation.FlingAnimation setMinValue(float);
+    method public android.support.animation.FlingAnimation setStartVelocity(float);
+  }
+
+  public abstract class FloatPropertyCompat<T> {
+    ctor public FloatPropertyCompat(java.lang.String);
+    method public static <T> android.support.animation.FloatPropertyCompat<T> createFloatPropertyCompat(android.util.FloatProperty<T>);
+    method public abstract float getValue(T);
+    method public abstract void setValue(T, float);
+  }
+
+  public final class FloatValueHolder {
+    ctor public FloatValueHolder();
+    ctor public FloatValueHolder(float);
+    method public float getValue();
+    method public void setValue(float);
+  }
+
+  public final class SpringAnimation extends android.support.animation.DynamicAnimation {
+    ctor public SpringAnimation(android.support.animation.FloatValueHolder);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>);
+    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>, float);
+    method public void animateToFinalPosition(float);
+    method public boolean canSkipToEnd();
+    method public android.support.animation.SpringForce getSpring();
+    method public android.support.animation.SpringAnimation setSpring(android.support.animation.SpringForce);
+    method public void skipToEnd();
+  }
+
+  public final class SpringForce {
+    ctor public SpringForce();
+    ctor public SpringForce(float);
+    method public float getDampingRatio();
+    method public float getFinalPosition();
+    method public float getStiffness();
+    method public android.support.animation.SpringForce setDampingRatio(float);
+    method public android.support.animation.SpringForce setFinalPosition(float);
+    method public android.support.animation.SpringForce setStiffness(float);
+    field public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
+    field public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
+    field public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
+    field public static final float DAMPING_RATIO_NO_BOUNCY = 1.0f;
+    field public static final float STIFFNESS_HIGH = 10000.0f;
+    field public static final float STIFFNESS_LOW = 200.0f;
+    field public static final float STIFFNESS_MEDIUM = 1500.0f;
+    field public static final float STIFFNESS_VERY_LOW = 50.0f;
+  }
+
+}
+
+package android.support.app.recommendation {
+
+  public final class ContentRecommendation {
+    method public java.lang.String getBackgroundImageUri();
+    method public int getBadgeImageResourceId();
+    method public int getColor();
+    method public android.graphics.Bitmap getContentImage();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
+    method public java.lang.String[] getContentTypes();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getGroup();
+    method public java.lang.String getIdTag();
+    method public java.lang.String getMaturityRating();
+    method public android.app.Notification getNotificationObject(android.content.Context);
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public int getProgressMax();
+    method public int getProgressValue();
+    method public long getRunningTime();
+    method public java.lang.String getSortKey();
+    method public java.lang.String getSourceName();
+    method public int getStatus();
+    method public java.lang.String getText();
+    method public java.lang.String getTitle();
+    method public boolean hasProgressInfo();
+    method public boolean isAutoDismiss();
+    method public void setAutoDismiss(boolean);
+    method public void setGroup(java.lang.String);
+    method public void setProgress(int, int);
+    method public void setSortKey(java.lang.String);
+    method public void setStatus(int);
+    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
+    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
+    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
+    field public static final int CONTENT_STATUS_READY = 0; // 0x0
+    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
+    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
+    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
+    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
+    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
+    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
+    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
+    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
+    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
+    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
+    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
+  }
+
+  public static final class ContentRecommendation.Builder {
+    ctor public ContentRecommendation.Builder();
+    method public android.support.app.recommendation.ContentRecommendation build();
+    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
+  }
+
+  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
+  }
+
+  public static class ContentRecommendation.IntentData {
+    ctor public ContentRecommendation.IntentData();
+  }
+
+  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
+  }
+
+  public final class RecommendationExtender implements android.app.Notification.Extender {
+    ctor public RecommendationExtender();
+    ctor public RecommendationExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String[] getContentTypes();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getMaturityRating();
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public long getRunningTime();
+    method public int getStatus();
+    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
+    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
+  }
+
+}
+
+package android.support.customtabs {
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(java.lang.String, android.os.Bundle);
+    method public void onMessageChannelReady(android.os.Bundle);
+    method public void onNavigationEvent(int, android.os.Bundle);
+    method public void onPostMessage(java.lang.String, android.os.Bundle);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, java.lang.String, android.support.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, java.lang.String);
+    method public android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>);
+    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>, boolean);
+    method public android.support.customtabs.CustomTabsSession newSession(android.support.customtabs.CustomTabsCallback);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context, android.net.Uri);
+    method public static android.content.Intent setAlwaysUseBrowserUI(android.content.Intent);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent);
+    field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final java.lang.String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final java.lang.String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final java.lang.String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final java.lang.String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final java.lang.String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final java.lang.String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final java.lang.String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final java.lang.String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
+    method public android.support.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
+    method public deprecated android.support.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, java.lang.String, android.app.PendingIntent) throws java.lang.IllegalStateException;
+    method public android.support.customtabs.CustomTabsIntent build();
+    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public android.support.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, int, int);
+    method public android.support.customtabs.CustomTabsIntent.Builder setToolbarColor(int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(android.support.customtabs.CustomTabsSessionToken);
+    method protected abstract android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract int postMessage(android.support.customtabs.CustomTabsSessionToken, java.lang.String, android.os.Bundle);
+    method protected abstract boolean requestPostMessageChannel(android.support.customtabs.CustomTabsSessionToken, android.net.Uri);
+    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
+    method protected abstract boolean warmup(long);
+    field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class CustomTabsService.Result implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName, android.support.customtabs.CustomTabsClient);
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+  }
+
+  public final class CustomTabsSession {
+    method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
+    method public int postMessage(java.lang.String, android.os.Bundle);
+    method public boolean requestPostMessageChannel(android.net.Uri);
+    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
+    method public deprecated boolean setToolbarItem(int, android.graphics.Bitmap, java.lang.String);
+  }
+
+  public class CustomTabsSessionToken {
+    method public android.support.customtabs.CustomTabsCallback getCallback();
+    method public static android.support.customtabs.CustomTabsSessionToken getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(android.support.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(android.support.customtabs.CustomTabsSessionToken);
+    method public boolean bindSessionToPostMessageService(android.content.Context, java.lang.String);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
+    method public final void onServiceDisconnected(android.content.ComponentName);
+    method public final boolean postMessage(java.lang.String, android.os.Bundle);
+    method public void unbindFromContext(android.content.Context);
+  }
+
+}
+
+package android.support.design.widget {
+
+  public class AppBarLayout extends android.widget.LinearLayout {
+    ctor public AppBarLayout(android.content.Context);
+    ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
+    method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method protected android.support.design.widget.AppBarLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.design.widget.AppBarLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.design.widget.AppBarLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public deprecated float getTargetElevation();
+    method public final int getTotalScrollRange();
+    method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public void setExpanded(boolean);
+    method public void setExpanded(boolean, boolean);
+    method public deprecated void setTargetElevation(float);
+  }
+
+  public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
+    ctor public AppBarLayout.Behavior();
+    ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int, int, int, int);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int);
+    method public void setDragCallback(android.support.design.widget.AppBarLayout.Behavior.DragCallback);
+  }
+
+  public static abstract class AppBarLayout.Behavior.DragCallback {
+    ctor public AppBarLayout.Behavior.DragCallback();
+    method public abstract boolean canDrag(android.support.design.widget.AppBarLayout);
+  }
+
+  protected static class AppBarLayout.Behavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+  }
+
+  public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public AppBarLayout.LayoutParams(int, int);
+    ctor public AppBarLayout.LayoutParams(int, int, float);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.widget.LinearLayout.LayoutParams);
+    ctor public AppBarLayout.LayoutParams(android.support.design.widget.AppBarLayout.LayoutParams);
+    method public int getScrollFlags();
+    method public android.view.animation.Interpolator getScrollInterpolator();
+    method public void setScrollFlags(int);
+    method public void setScrollInterpolator(android.view.animation.Interpolator);
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS = 4; // 0x4
+    field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
+    field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
+    field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
+    field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
+  }
+
+  public static abstract interface AppBarLayout.OnOffsetChangedListener {
+    method public abstract void onOffsetChanged(android.support.design.widget.AppBarLayout, int);
+  }
+
+  public static class AppBarLayout.ScrollingViewBehavior extends android.support.design.widget.HeaderScrollingViewBehavior {
+    ctor public AppBarLayout.ScrollingViewBehavior();
+    ctor public AppBarLayout.ScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
+  }
+
+  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
+    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
+    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public void dismiss();
+    method public android.content.Context getContext();
+    method public int getDuration();
+    method public android.view.View getView();
+    method public boolean isShown();
+    method public boolean isShownOrQueued();
+    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
+    method public B setDuration(int);
+    method public void show();
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
+    ctor public BaseTransientBottomBar.BaseCallback();
+    method public void onDismissed(B, int);
+    method public void onShown(B);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
+    method public abstract void animateContentIn(int, int);
+    method public abstract void animateContentOut(int, int);
+  }
+
+  public class BottomNavigationView extends android.widget.FrameLayout {
+    ctor public BottomNavigationView(android.content.Context);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public int getItemBackgroundResource();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public int getMaxItemCount();
+    method public android.view.Menu getMenu();
+    method public int getSelectedItemId();
+    method public void inflateMenu(int);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setOnNavigationItemReselectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemReselectedListener);
+    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
+    method public void setSelectedItemId(int);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemReselectedListener {
+    method public abstract void onNavigationItemReselected(android.view.MenuItem);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public class BottomSheetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public BottomSheetBehavior();
+    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
+    method public static <V extends android.view.View> android.support.design.widget.BottomSheetBehavior<V> from(V);
+    method public final int getPeekHeight();
+    method public boolean getSkipCollapsed();
+    method public final int getState();
+    method public boolean isHideable();
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void setBottomSheetCallback(android.support.design.widget.BottomSheetBehavior.BottomSheetCallback);
+    method public void setHideable(boolean);
+    method public final void setPeekHeight(int);
+    method public void setSkipCollapsed(boolean);
+    method public final void setState(int);
+    field public static final int PEEK_HEIGHT_AUTO = -1; // 0xffffffff
+    field public static final int STATE_COLLAPSED = 4; // 0x4
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_EXPANDED = 3; // 0x3
+    field public static final int STATE_HIDDEN = 5; // 0x5
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class BottomSheetBehavior.BottomSheetCallback {
+    ctor public BottomSheetBehavior.BottomSheetCallback();
+    method public abstract void onSlide(android.view.View, float);
+    method public abstract void onStateChanged(android.view.View, int);
+  }
+
+  protected static class BottomSheetBehavior.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
+  }
+
+  public class BottomSheetDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public BottomSheetDialog(android.content.Context);
+    ctor public BottomSheetDialog(android.content.Context, int);
+    ctor protected BottomSheetDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+  }
+
+  public class BottomSheetDialogFragment extends android.support.v7.app.AppCompatDialogFragment {
+    ctor public BottomSheetDialogFragment();
+  }
+
+  public class CollapsingToolbarLayout extends android.widget.FrameLayout {
+    ctor public CollapsingToolbarLayout(android.content.Context);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.design.widget.CollapsingToolbarLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.widget.FrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.widget.FrameLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getCollapsedTitleGravity();
+    method public android.graphics.Typeface getCollapsedTitleTypeface();
+    method public android.graphics.drawable.Drawable getContentScrim();
+    method public int getExpandedTitleGravity();
+    method public int getExpandedTitleMarginBottom();
+    method public int getExpandedTitleMarginEnd();
+    method public int getExpandedTitleMarginStart();
+    method public int getExpandedTitleMarginTop();
+    method public android.graphics.Typeface getExpandedTitleTypeface();
+    method public long getScrimAnimationDuration();
+    method public int getScrimVisibleHeightTrigger();
+    method public android.graphics.drawable.Drawable getStatusBarScrim();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isTitleEnabled();
+    method public void setCollapsedTitleGravity(int);
+    method public void setCollapsedTitleTextAppearance(int);
+    method public void setCollapsedTitleTextColor(int);
+    method public void setCollapsedTitleTextColor(android.content.res.ColorStateList);
+    method public void setCollapsedTitleTypeface(android.graphics.Typeface);
+    method public void setContentScrim(android.graphics.drawable.Drawable);
+    method public void setContentScrimColor(int);
+    method public void setContentScrimResource(int);
+    method public void setExpandedTitleColor(int);
+    method public void setExpandedTitleGravity(int);
+    method public void setExpandedTitleMargin(int, int, int, int);
+    method public void setExpandedTitleMarginBottom(int);
+    method public void setExpandedTitleMarginEnd(int);
+    method public void setExpandedTitleMarginStart(int);
+    method public void setExpandedTitleMarginTop(int);
+    method public void setExpandedTitleTextAppearance(int);
+    method public void setExpandedTitleTextColor(android.content.res.ColorStateList);
+    method public void setExpandedTitleTypeface(android.graphics.Typeface);
+    method public void setScrimAnimationDuration(long);
+    method public void setScrimVisibleHeightTrigger(int);
+    method public void setScrimsShown(boolean);
+    method public void setScrimsShown(boolean, boolean);
+    method public void setStatusBarScrim(android.graphics.drawable.Drawable);
+    method public void setStatusBarScrimColor(int);
+    method public void setStatusBarScrimResource(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleEnabled(boolean);
+  }
+
+  public static class CollapsingToolbarLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public CollapsingToolbarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(int, int, int);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    method public int getCollapseMode();
+    method public float getParallaxMultiplier();
+    method public void setCollapseMode(int);
+    method public void setParallaxMultiplier(float);
+    field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
+    field public static final int COLLAPSE_MODE_PARALLAX = 2; // 0x2
+    field public static final int COLLAPSE_MODE_PIN = 1; // 0x1
+  }
+
+  public class CoordinatorLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingParent2 {
+    ctor public CoordinatorLayout(android.content.Context);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void dispatchDependentViewsChanged(android.view.View);
+    method public boolean doViewsOverlap(android.view.View, android.view.View);
+    method protected android.support.design.widget.CoordinatorLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.design.widget.CoordinatorLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.design.widget.CoordinatorLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public java.util.List<android.view.View> getDependencies(android.view.View);
+    method public java.util.List<android.view.View> getDependents(android.view.View);
+    method public android.graphics.drawable.Drawable getStatusBarBackground();
+    method public boolean isPointInChildBounds(android.view.View, int, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onLayoutChild(android.view.View, int);
+    method public void onMeasureChild(android.view.View, int, int, int, int);
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackgroundColor(int);
+    method public void setStatusBarBackgroundResource(int);
+  }
+
+  public static abstract class CoordinatorLayout.Behavior<V extends android.view.View> {
+    ctor public CoordinatorLayout.Behavior();
+    ctor public CoordinatorLayout.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean blocksInteractionBelow(android.support.design.widget.CoordinatorLayout, V);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect);
+    method public int getScrimColor(android.support.design.widget.CoordinatorLayout, V);
+    method public float getScrimOpacity(android.support.design.widget.CoordinatorLayout, V);
+    method public static java.lang.Object getTag(android.view.View);
+    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.support.design.widget.CoordinatorLayout, V, android.support.v4.view.WindowInsetsCompat);
+    method public void onAttachedToLayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDependentViewRemoved(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onDetachedFromLayoutParams();
+    method public boolean onInterceptTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, V, int, int, int, int);
+    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float);
+    method public deprecated void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
+    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[], int);
+    method public deprecated void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
+    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int, int);
+    method public deprecated void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int, int);
+    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect, boolean);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
+    method public deprecated boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int, int);
+    method public deprecated void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
+    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int);
+    method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
+    method public static void setTag(android.view.View, java.lang.Object);
+  }
+
+  public static abstract class CoordinatorLayout.DefaultBehavior implements java.lang.annotation.Annotation {
+  }
+
+  public static class CoordinatorLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public CoordinatorLayout.LayoutParams(int, int);
+    ctor public CoordinatorLayout.LayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAnchorId();
+    method public android.support.design.widget.CoordinatorLayout.Behavior getBehavior();
+    method public void setAnchorId(int);
+    method public void setBehavior(android.support.design.widget.CoordinatorLayout.Behavior);
+    field public int anchorGravity;
+    field public int dodgeInsetEdges;
+    field public int gravity;
+    field public int insetEdge;
+    field public int keyline;
+  }
+
+  protected static class CoordinatorLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public CoordinatorLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+  }
+
+  public class FloatingActionButton extends android.support.design.widget.VisibilityAwareImageButton {
+    ctor public FloatingActionButton(android.content.Context);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
+    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+    method public float getCompatElevation();
+    method public android.graphics.drawable.Drawable getContentBackground();
+    method public boolean getContentRect(android.graphics.Rect);
+    method public int getRippleColor();
+    method public int getSize();
+    method public boolean getUseCompatPadding();
+    method public void hide();
+    method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setCompatElevation(float);
+    method public void setRippleColor(int);
+    method public void setSize(int);
+    method public void setUseCompatPadding(boolean);
+    method public void show();
+    method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+    field public static final int SIZE_AUTO = -1; // 0xffffffff
+    field public static final int SIZE_MINI = 1; // 0x1
+    field public static final int SIZE_NORMAL = 0; // 0x0
+  }
+
+  public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public FloatingActionButton.Behavior();
+    ctor public FloatingActionButton.Behavior(android.content.Context, android.util.AttributeSet);
+    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.graphics.Rect);
+    method public boolean isAutoHideEnabled();
+    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.view.View);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, int);
+    method public void setAutoHideEnabled(boolean);
+  }
+
+  public static abstract class FloatingActionButton.OnVisibilityChangedListener {
+    ctor public FloatingActionButton.OnVisibilityChangedListener();
+    method public void onHidden(android.support.design.widget.FloatingActionButton);
+    method public void onShown(android.support.design.widget.FloatingActionButton);
+  }
+
+   abstract class HeaderBehavior<V extends android.view.View> extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderBehavior();
+    ctor public HeaderBehavior(android.content.Context, android.util.AttributeSet);
+  }
+
+   abstract class HeaderScrollingViewBehavior extends android.support.design.widget.ViewOffsetBehavior {
+    ctor public HeaderScrollingViewBehavior();
+    ctor public HeaderScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
+    method public final int getOverlayTop();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
+    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
+    method public final void setOverlayTop(int);
+  }
+
+  public class NavigationView extends android.widget.FrameLayout {
+    ctor public NavigationView(android.content.Context);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public NavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public void addHeaderView(android.view.View);
+    method public int getHeaderCount();
+    method public android.view.View getHeaderView(int);
+    method public android.graphics.drawable.Drawable getItemBackground();
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public android.view.Menu getMenu();
+    method public android.view.View inflateHeaderView(int);
+    method public void inflateMenu(int);
+    method public void removeHeaderView(android.view.View);
+    method public void setCheckedItem(int);
+    method public void setItemBackground(android.graphics.drawable.Drawable);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextAppearance(int);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setNavigationItemSelectedListener(android.support.design.widget.NavigationView.OnNavigationItemSelectedListener);
+  }
+
+  public static abstract interface NavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
+  public static class NavigationView.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public NavigationView.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public NavigationView.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.NavigationView.SavedState> CREATOR;
+    field public android.os.Bundle menuState;
+  }
+
+  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
+    method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
+    method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
+    method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
+    method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
+    method public android.support.design.widget.Snackbar setActionTextColor(int);
+    method public deprecated android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
+    method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
+    method public android.support.design.widget.Snackbar setText(int);
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
+    ctor public Snackbar.Callback();
+    method public void onDismissed(android.support.design.widget.Snackbar, int);
+    method public void onShown(android.support.design.widget.Snackbar);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public SwipeDismissBehavior();
+    method public boolean canSwipeDismissView(android.view.View);
+    method public int getDragState();
+    method public void setDragDismissDistance(float);
+    method public void setEndAlphaSwipeDistance(float);
+    method public void setListener(android.support.design.widget.SwipeDismissBehavior.OnDismissListener);
+    method public void setSensitivity(float);
+    method public void setStartAlphaSwipeDistance(float);
+    method public void setSwipeDirection(int);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_ANY = 2; // 0x2
+    field public static final int SWIPE_DIRECTION_END_TO_START = 1; // 0x1
+    field public static final int SWIPE_DIRECTION_START_TO_END = 0; // 0x0
+  }
+
+  public static abstract interface SwipeDismissBehavior.OnDismissListener {
+    method public abstract void onDismiss(android.view.View);
+    method public abstract void onDragStateChanged(int);
+  }
+
+  public final class TabItem extends android.view.View {
+    ctor public TabItem(android.content.Context);
+    ctor public TabItem(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class TabLayout extends android.widget.HorizontalScrollView {
+    ctor public TabLayout(android.content.Context);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TabLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void addTab(android.support.design.widget.TabLayout.Tab);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
+    method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
+    method public void clearOnTabSelectedListeners();
+    method public android.widget.FrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method public int getSelectedTabPosition();
+    method public android.support.design.widget.TabLayout.Tab getTabAt(int);
+    method public int getTabCount();
+    method public int getTabGravity();
+    method public int getTabMode();
+    method public android.content.res.ColorStateList getTabTextColors();
+    method public android.support.design.widget.TabLayout.Tab newTab();
+    method public void removeAllTabs();
+    method public void removeOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void removeTab(android.support.design.widget.TabLayout.Tab);
+    method public void removeTabAt(int);
+    method public deprecated void setOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
+    method public void setScrollPosition(int, float, boolean);
+    method public void setSelectedTabIndicatorColor(int);
+    method public void setSelectedTabIndicatorHeight(int);
+    method public void setTabGravity(int);
+    method public void setTabMode(int);
+    method public void setTabTextColors(android.content.res.ColorStateList);
+    method public void setTabTextColors(int, int);
+    method public deprecated void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager, boolean);
+    field public static final int GRAVITY_CENTER = 1; // 0x1
+    field public static final int GRAVITY_FILL = 0; // 0x0
+    field public static final int MODE_FIXED = 1; // 0x1
+    field public static final int MODE_SCROLLABLE = 0; // 0x0
+  }
+
+  public static abstract interface TabLayout.OnTabSelectedListener {
+    method public abstract void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public abstract void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public static final class TabLayout.Tab {
+    method public java.lang.CharSequence getContentDescription();
+    method public android.view.View getCustomView();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public int getPosition();
+    method public java.lang.Object getTag();
+    method public java.lang.CharSequence getText();
+    method public boolean isSelected();
+    method public void select();
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(int);
+    method public android.support.design.widget.TabLayout.Tab setContentDescription(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(android.view.View);
+    method public android.support.design.widget.TabLayout.Tab setCustomView(int);
+    method public android.support.design.widget.TabLayout.Tab setIcon(android.graphics.drawable.Drawable);
+    method public android.support.design.widget.TabLayout.Tab setIcon(int);
+    method public android.support.design.widget.TabLayout.Tab setTag(java.lang.Object);
+    method public android.support.design.widget.TabLayout.Tab setText(java.lang.CharSequence);
+    method public android.support.design.widget.TabLayout.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
+  public class TextInputEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public TextInputEditText(android.content.Context);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class TextInputLayout extends android.widget.LinearLayout {
+    ctor public TextInputLayout(android.content.Context);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
+    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
+    method public int getCounterMaxLength();
+    method public android.widget.EditText getEditText();
+    method public java.lang.CharSequence getError();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
+    method public android.graphics.Typeface getTypeface();
+    method public boolean isCounterEnabled();
+    method public boolean isErrorEnabled();
+    method public boolean isHintAnimationEnabled();
+    method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void setCounterEnabled(boolean);
+    method public void setCounterMaxLength(int);
+    method public void setError(java.lang.CharSequence);
+    method public void setErrorEnabled(boolean);
+    method public void setErrorTextAppearance(int);
+    method public void setHint(java.lang.CharSequence);
+    method public void setHintAnimationEnabled(boolean);
+    method public void setHintEnabled(boolean);
+    method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTypeface(android.graphics.Typeface);
+  }
+
+   class ViewOffsetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public ViewOffsetBehavior();
+    ctor public ViewOffsetBehavior(android.content.Context, android.util.AttributeSet);
+    method public int getLeftAndRightOffset();
+    method public int getTopAndBottomOffset();
+    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, V, int);
+    method public boolean setLeftAndRightOffset(int);
+    method public boolean setTopAndBottomOffset(int);
+  }
+
+   class VisibilityAwareImageButton extends android.widget.ImageButton {
+    ctor public VisibilityAwareImageButton(android.content.Context);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+}
+
+package android.support.graphics.drawable {
+
+  public abstract interface Animatable2Compat {
+    method public abstract void clearAnimationCallbacks();
+    method public abstract void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public abstract boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+  public static abstract class Animatable2Compat.AnimationCallback {
+    ctor public Animatable2Compat.AnimationCallback();
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
+  }
+
+  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon implements android.support.graphics.drawable.Animatable2Compat {
+    method public void clearAnimationCallbacks();
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
+    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public boolean isRunning();
+    method public void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void start();
+    method public void stop();
+    method public boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
+  }
+
+  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
+    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+  }
+
+}
+
+package android.support.media {
+
+  public class ExifInterface {
+    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
+    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
+    method public void flipHorizontally();
+    method public void flipVertically();
+    method public double getAltitude(double);
+    method public java.lang.String getAttribute(java.lang.String);
+    method public double getAttributeDouble(java.lang.String, double);
+    method public int getAttributeInt(java.lang.String, int);
+    method public deprecated boolean getLatLong(float[]);
+    method public double[] getLatLong();
+    method public byte[] getThumbnail();
+    method public android.graphics.Bitmap getThumbnailBitmap();
+    method public byte[] getThumbnailBytes();
+    method public long[] getThumbnailRange();
+    method public boolean hasThumbnail();
+    method public boolean isThumbnailCompressed();
+    method public void resetOrientation();
+    method public void rotate(int);
+    method public void saveAttributes() throws java.io.IOException;
+    method public void setAltitude(double);
+    method public void setAttribute(java.lang.String, java.lang.String);
+    method public void setGpsInfo(android.location.Location);
+    method public void setLatLong(double, double);
+    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
+    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
+    field public static final int ORIENTATION_NORMAL = 1; // 0x1
+    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
+    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
+    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
+    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
+    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
+    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+    field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+    field public static final java.lang.String TAG_COMPRESSION = "Compression";
+    field public static final java.lang.String TAG_CONTRAST = "Contrast";
+    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
+    field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
+    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
+    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
+    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
+    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
+    field public static final java.lang.String TAG_FLASH = "Flash";
+    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
+    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
+    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
+    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
+    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
+    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
+    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
+    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
+    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
+    field public static final java.lang.String TAG_MAKE = "Make";
+    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
+    field public static final java.lang.String TAG_MODEL = "Model";
+    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
+    field public static final java.lang.String TAG_OECF = "OECF";
+    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
+    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
+    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+    field public static final java.lang.String TAG_RW2_ISO = "ISO";
+    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
+    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+    field public static final java.lang.String TAG_SATURATION = "Saturation";
+    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+    field public static final java.lang.String TAG_SOFTWARE = "Software";
+    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
+    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
+    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
+    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
+    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
+  }
+
+}
+
+package android.support.media.tv {
+
+  public final class Channel {
+    method public static android.support.media.tv.Channel fromCursor(android.database.Cursor);
+    method public int getAppLinkColor();
+    method public android.net.Uri getAppLinkIconUri();
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public android.net.Uri getAppLinkPosterArtUri();
+    method public java.lang.String getAppLinkText();
+    method public java.lang.String getDescription();
+    method public java.lang.String getDisplayName();
+    method public java.lang.String getDisplayNumber();
+    method public long getId();
+    method public java.lang.String getInputId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getInternalProviderId();
+    method public java.lang.String getNetworkAffiliation();
+    method public int getOriginalNetworkId();
+    method public java.lang.String getPackageName();
+    method public int getServiceId();
+    method public java.lang.String getServiceType();
+    method public int getTransportStreamId();
+    method public java.lang.String getType();
+    method public java.lang.String getVideoFormat();
+    method public boolean isBrowsable();
+    method public boolean isLocked();
+    method public boolean isSearchable();
+    method public boolean isTransient();
+    method public android.content.ContentValues toContentValues();
+  }
+
+  public static final class Channel.Builder {
+    ctor public Channel.Builder();
+    ctor public Channel.Builder(android.support.media.tv.Channel);
+    method public android.support.media.tv.Channel build();
+    method public android.support.media.tv.Channel.Builder setAppLinkColor(int);
+    method public android.support.media.tv.Channel.Builder setAppLinkIconUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.Channel.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkPosterArtUri(android.net.Uri);
+    method public android.support.media.tv.Channel.Builder setAppLinkText(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDescription(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayName(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setDisplayNumber(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInputId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.Channel.Builder setInternalProviderData(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.Channel.Builder setInternalProviderId(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setNetworkAffiliation(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setOriginalNetworkId(int);
+    method public android.support.media.tv.Channel.Builder setSearchable(boolean);
+    method public android.support.media.tv.Channel.Builder setServiceId(int);
+    method public android.support.media.tv.Channel.Builder setServiceType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setTransient(boolean);
+    method public android.support.media.tv.Channel.Builder setTransportStreamId(int);
+    method public android.support.media.tv.Channel.Builder setType(java.lang.String);
+    method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
+  }
+
+  public class ChannelLogoUtils {
+    ctor public ChannelLogoUtils();
+    method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
+    method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
+  }
+
+  public final class PreviewProgram {
+    method public boolean equals(java.lang.Object);
+    method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
+    method public long getChannelId();
+    method public int getWeight();
+    method public android.content.ContentValues toContentValues();
+    method public java.lang.String toString();
+  }
+
+  public static final class PreviewProgram.Builder {
+    ctor public PreviewProgram.Builder();
+    ctor public PreviewProgram.Builder(android.support.media.tv.PreviewProgram);
+    method public android.support.media.tv.PreviewProgram build();
+    method public android.support.media.tv.PreviewProgram.Builder setChannelId(long);
+    method public android.support.media.tv.PreviewProgram.Builder setWeight(int);
+  }
+
+  public final class Program implements java.lang.Comparable {
+    method public int compareTo(android.support.media.tv.Program);
+    method public boolean equals(java.lang.Object);
+    method public static android.support.media.tv.Program fromCursor(android.database.Cursor);
+    method public java.lang.String[] getBroadcastGenres();
+    method public long getChannelId();
+    method public long getEndTimeUtcMillis();
+    method public long getStartTimeUtcMillis();
+    method public int hashCode();
+    method public boolean isRecordingProhibited();
+    method public android.content.ContentValues toContentValues();
+    method public java.lang.String toString();
+  }
+
+  public static class Program.Builder {
+    ctor public Program.Builder();
+    ctor public Program.Builder(android.support.media.tv.Program);
+    method public android.support.media.tv.Program build();
+    method public android.support.media.tv.Program.Builder setBroadcastGenres(java.lang.String[]);
+    method public android.support.media.tv.Program.Builder setChannelId(long);
+    method public android.support.media.tv.Program.Builder setEndTimeUtcMillis(long);
+    method public android.support.media.tv.Program.Builder setRecordingProhibited(boolean);
+    method public android.support.media.tv.Program.Builder setStartTimeUtcMillis(long);
+  }
+
+  public final class TvContractCompat {
+    method public static android.net.Uri buildChannelLogoUri(long);
+    method public static android.net.Uri buildChannelLogoUri(android.net.Uri);
+    method public static android.net.Uri buildChannelUri(long);
+    method public static android.net.Uri buildChannelUriForPassthroughInput(java.lang.String);
+    method public static android.net.Uri buildChannelsUriForInput(java.lang.String);
+    method public static java.lang.String buildInputId(android.content.ComponentName);
+    method public static android.net.Uri buildPreviewProgramUri(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(long);
+    method public static android.net.Uri buildPreviewProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramUri(long);
+    method public static android.net.Uri buildProgramsUriForChannel(long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+    method public static android.net.Uri buildProgramsUriForChannel(long, long, long);
+    method public static android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static android.net.Uri buildRecordedProgramUri(long);
+    method public static android.net.Uri buildWatchNextProgramUri(long);
+    method public static boolean isChannelUri(android.net.Uri);
+    method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
+    method public static boolean isChannelUriForTunerInput(android.net.Uri);
+    method public static boolean isProgramUri(android.net.Uri);
+    method public static boolean isRecordedProgramUri(android.net.Uri);
+    method public static void requestChannelBrowsable(android.content.Context, long);
+    field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
+    field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
+    field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
+    field public static final java.lang.String AUTHORITY = "android.media.tv";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
+    field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
+  }
+
+  public static abstract interface TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
+  }
+
+  public static final class TvContractCompat.Channels implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    method public static java.lang.String getVideoResolution(java.lang.String);
+    field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
+    field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
+    field public static final java.lang.String COLUMN_APP_LINK_TEXT = "app_link_text";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
+    field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
+    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_LOCKED = "locked";
+    field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
+    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+    field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+    field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+    field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+    field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+    field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+    field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+    field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+    field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+    field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+    field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+    field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+    field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+    field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+    field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+    field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+    field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+    field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+    field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+    field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+    field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+    field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+    field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+    field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
+    field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+    field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+    field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+  }
+
+  public static final class TvContractCompat.Channels.Logo {
+    field public static final java.lang.String CONTENT_DIRECTORY = "logo";
+  }
+
+  public static final class TvContractCompat.PreviewPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WEIGHT = "weight";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.Programs.Genres {
+    method public static java.lang.String[] decode(java.lang.String);
+    method public static java.lang.String encode(java.lang.String...);
+    method public static boolean isCanonical(java.lang.String);
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String ARTS = "ARTS";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String ENTERTAINMENT = "ENTERTAINMENT";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String LIFE_STYLE = "LIFE_STYLE";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String MUSIC = "MUSIC";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String PREMIER = "PREMIER";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TECH_SCIENCE = "TECH_SCIENCE";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
+  }
+
+  public static final class TvContractCompat.RecordedPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+  }
+
+  public static final class TvContractCompat.WatchNextPrograms implements android.support.media.tv.TvContractCompat.BaseTvColumns {
+    field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
+    field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
+    field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
+    field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
+    field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
+    field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+    field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
+    field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
+    field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
+    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CONTENT_ID = "content_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis";
+    field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+    field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+    field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis";
+    field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
+    field public static final java.lang.String COLUMN_LIVE = "live";
+    field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+    field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+    field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
+    field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
+    field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
+    field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
+    field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
+    field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
+    field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
+    field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+    field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+    field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+    field public static final int TYPE_ALBUM = 8; // 0x8
+    field public static final int TYPE_ARTIST = 9; // 0x9
+    field public static final int TYPE_CHANNEL = 6; // 0x6
+    field public static final int TYPE_CLIP = 4; // 0x4
+    field public static final int TYPE_EVENT = 5; // 0x5
+    field public static final int TYPE_MOVIE = 0; // 0x0
+    field public static final int TYPE_PLAYLIST = 10; // 0xa
+    field public static final int TYPE_STATION = 11; // 0xb
+    field public static final int TYPE_TRACK = 7; // 0x7
+    field public static final int TYPE_TV_EPISODE = 3; // 0x3
+    field public static final int TYPE_TV_SEASON = 2; // 0x2
+    field public static final int TYPE_TV_SERIES = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0
+    field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2
+    field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1
+    field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3
+  }
+
+  public final class WatchNextProgram {
+    method public boolean equals(java.lang.Object);
+    method public static android.support.media.tv.WatchNextProgram fromCursor(android.database.Cursor);
+    method public long getLastEngagementTimeUtcMillis();
+    method public int getWatchNextType();
+    method public android.content.ContentValues toContentValues();
+    method public java.lang.String toString();
+  }
+
+  public static final class WatchNextProgram.Builder {
+    ctor public WatchNextProgram.Builder();
+    ctor public WatchNextProgram.Builder(android.support.media.tv.WatchNextProgram);
+    method public android.support.media.tv.WatchNextProgram build();
+    method public android.support.media.tv.WatchNextProgram.Builder setLastEngagementTimeUtcMillis(long);
+    method public android.support.media.tv.WatchNextProgram.Builder setWatchNextType(int);
+  }
+
+}
+
+package android.support.percent {
+
+  public deprecated class PercentFrameLayout extends android.widget.FrameLayout {
+    ctor public PercentFrameLayout(android.content.Context);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.percent.PercentFrameLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.percent.PercentFrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+  }
+
+  public static deprecated class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout.LayoutParams(int, int);
+    ctor public PercentFrameLayout.LayoutParams(int, int, int);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentLayoutHelper {
+    ctor public PercentLayoutHelper(android.view.ViewGroup);
+    method public void adjustChildren(int, int);
+    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+    method public boolean handleMeasuredStateTooSmall();
+    method public void restoreOriginalParams();
+  }
+
+  public static deprecated class PercentLayoutHelper.PercentLayoutInfo {
+    ctor public PercentLayoutHelper.PercentLayoutInfo();
+    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+    method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void fillMarginLayoutParams(android.view.View, android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public float aspectRatio;
+    field public float bottomMarginPercent;
+    field public float endMarginPercent;
+    field public float heightPercent;
+    field public float leftMarginPercent;
+    field public float rightMarginPercent;
+    field public float startMarginPercent;
+    field public float topMarginPercent;
+    field public float widthPercent;
+  }
+
+  public static abstract deprecated interface PercentLayoutHelper.PercentLayoutParams {
+    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public deprecated class PercentRelativeLayout extends android.widget.RelativeLayout {
+    ctor public PercentRelativeLayout(android.content.Context);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.percent.PercentRelativeLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.percent.PercentRelativeLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+  }
+
+  public static deprecated class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout.LayoutParams(int, int);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+}
+
+package android.support.text.emoji {
+
+  public class EmojiCompat {
+    method public static android.support.text.emoji.EmojiCompat get();
+    method public java.lang.String getAssetSignature();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, int, int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence);
+    method public boolean hasEmojiGlyph(java.lang.CharSequence, int);
+    method public static android.support.text.emoji.EmojiCompat init(android.support.text.emoji.EmojiCompat.Config);
+    method public java.lang.CharSequence process(java.lang.CharSequence);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int);
+    method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int, int);
+    method public void registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final java.lang.String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public static abstract class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataRepoLoader);
+    method protected final android.support.text.emoji.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public android.support.text.emoji.EmojiCompat.Config registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
+    method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config setReplaceAll(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
+  }
+
+  public static abstract class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(java.lang.Throwable);
+    method public void onInitialized();
+  }
+
+  public static abstract interface EmojiCompat.MetadataRepoLoader {
+    method public abstract void load(android.support.text.emoji.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public static abstract class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(java.lang.Throwable);
+    method public abstract void onLoaded(android.support.text.emoji.MetadataRepo);
+  }
+
+  public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, java.lang.CharSequence, int, int, android.graphics.Paint.FontMetricsInt);
+  }
+
+  public class FontRequestEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, android.support.v4.provider.FontRequest);
+    method public android.support.text.emoji.FontRequestEmojiCompatConfig setHandler(android.os.Handler);
+    method public android.support.text.emoji.FontRequestEmojiCompatConfig setRetryPolicy(android.support.text.emoji.FontRequestEmojiCompatConfig.RetryPolicy);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends android.support.text.emoji.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public static abstract class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  public final class MetadataRepo {
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static android.support.text.emoji.MetadataRepo create(android.content.res.AssetManager, java.lang.String) throws java.io.IOException;
+  }
+
+}
+
+package android.support.text.emoji.bundled {
+
+  public class BundledEmojiCompatConfig extends android.support.text.emoji.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
+package android.support.text.emoji.widget {
+
+  public class EmojiAppCompatButton extends android.support.v7.widget.AppCompatButton {
+    ctor public EmojiAppCompatButton(android.content.Context);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiAppCompatEditText extends android.support.v7.widget.AppCompatEditText {
+    ctor public EmojiAppCompatEditText(android.content.Context);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
+  }
+
+  public class EmojiAppCompatTextView extends android.support.v7.widget.AppCompatTextView {
+    ctor public EmojiAppCompatTextView(android.content.Context);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiAppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
+  }
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
+    method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet, int, int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter[] getFilters(android.text.InputFilter[]);
+    method public void setAllCaps(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod wrapTransformationMethod(android.text.method.TransformationMethod);
+  }
+
+}
+
+package android.support.transition {
+
+  public class ArcMotion extends android.support.transition.PathMotion {
+    ctor public ArcMotion();
+    ctor public ArcMotion(android.content.Context, android.util.AttributeSet);
+    method public float getMaximumAngle();
+    method public float getMinimumHorizontalAngle();
+    method public float getMinimumVerticalAngle();
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public void setMaximumAngle(float);
+    method public void setMinimumHorizontalAngle(float);
+    method public void setMinimumVerticalAngle(float);
+  }
+
+  public class AutoTransition extends android.support.transition.TransitionSet {
+    ctor public AutoTransition();
+    ctor public AutoTransition(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class ChangeBounds extends android.support.transition.Transition {
+    ctor public ChangeBounds();
+    ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getResizeClip();
+    method public void setResizeClip(boolean);
+  }
+
+  public class ChangeClipBounds extends android.support.transition.Transition {
+    ctor public ChangeClipBounds();
+    ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeImageTransform extends android.support.transition.Transition {
+    ctor public ChangeImageTransform();
+    ctor public ChangeImageTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeScroll extends android.support.transition.Transition {
+    ctor public ChangeScroll();
+    ctor public ChangeScroll(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+  }
+
+  public class ChangeTransform extends android.support.transition.Transition {
+    ctor public ChangeTransform();
+    ctor public ChangeTransform(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public boolean getReparent();
+    method public boolean getReparentWithOverlay();
+    method public void setReparent(boolean);
+    method public void setReparentWithOverlay(boolean);
+  }
+
+  public class CircularPropagation extends android.support.transition.VisibilityPropagation {
+    ctor public CircularPropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+  }
+
+  public class Explode extends android.support.transition.Visibility {
+    ctor public Explode();
+    ctor public Explode(android.content.Context, android.util.AttributeSet);
+  }
+
+  public class Fade extends android.support.transition.Visibility {
+    ctor public Fade(int);
+    ctor public Fade();
+    ctor public Fade(android.content.Context, android.util.AttributeSet);
+    field public static final int IN = 1; // 0x1
+    field public static final int OUT = 2; // 0x2
+  }
+
+  public abstract class PathMotion {
+    ctor public PathMotion();
+    ctor public PathMotion(android.content.Context, android.util.AttributeSet);
+    method public abstract android.graphics.Path getPath(float, float, float, float);
+  }
+
+  public class PatternPathMotion extends android.support.transition.PathMotion {
+    ctor public PatternPathMotion();
+    ctor public PatternPathMotion(android.content.Context, android.util.AttributeSet);
+    ctor public PatternPathMotion(android.graphics.Path);
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public android.graphics.Path getPatternPath();
+    method public void setPatternPath(android.graphics.Path);
+  }
+
+  public class Scene {
+    ctor public Scene(android.view.ViewGroup);
+    ctor public Scene(android.view.ViewGroup, android.view.View);
+    method public void enter();
+    method public void exit();
+    method public static android.support.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
+    method public android.view.ViewGroup getSceneRoot();
+    method public void setEnterAction(java.lang.Runnable);
+    method public void setExitAction(java.lang.Runnable);
+  }
+
+  public class SidePropagation extends android.support.transition.VisibilityPropagation {
+    ctor public SidePropagation();
+    method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setPropagationSpeed(float);
+    method public void setSide(int);
+  }
+
+  public class Slide extends android.support.transition.Visibility {
+    ctor public Slide();
+    ctor public Slide(int);
+    ctor public Slide(android.content.Context, android.util.AttributeSet);
+    method public int getSlideEdge();
+    method public void setSlideEdge(int);
+  }
+
+  public abstract class Transition {
+    ctor public Transition();
+    ctor public Transition(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition addTarget(android.view.View);
+    method public android.support.transition.Transition addTarget(int);
+    method public android.support.transition.Transition addTarget(java.lang.String);
+    method public android.support.transition.Transition addTarget(java.lang.Class);
+    method public abstract void captureEndValues(android.support.transition.TransitionValues);
+    method public abstract void captureStartValues(android.support.transition.TransitionValues);
+    method public android.support.transition.Transition clone();
+    method public android.animation.Animator createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition excludeChildren(android.view.View, boolean);
+    method public android.support.transition.Transition excludeChildren(int, boolean);
+    method public android.support.transition.Transition excludeChildren(java.lang.Class, boolean);
+    method public android.support.transition.Transition excludeTarget(android.view.View, boolean);
+    method public android.support.transition.Transition excludeTarget(int, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
+    method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
+    method public long getDuration();
+    method public android.graphics.Rect getEpicenter();
+    method public android.support.transition.Transition.EpicenterCallback getEpicenterCallback();
+    method public android.animation.TimeInterpolator getInterpolator();
+    method public java.lang.String getName();
+    method public android.support.transition.PathMotion getPathMotion();
+    method public android.support.transition.TransitionPropagation getPropagation();
+    method public long getStartDelay();
+    method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.String> getTargetNames();
+    method public java.util.List<java.lang.Class> getTargetTypes();
+    method public java.util.List<android.view.View> getTargets();
+    method public java.lang.String[] getTransitionProperties();
+    method public android.support.transition.TransitionValues getTransitionValues(android.view.View, boolean);
+    method public boolean isTransitionRequired(android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.support.transition.Transition removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.Transition removeTarget(android.view.View);
+    method public android.support.transition.Transition removeTarget(int);
+    method public android.support.transition.Transition removeTarget(java.lang.String);
+    method public android.support.transition.Transition removeTarget(java.lang.Class);
+    method public android.support.transition.Transition setDuration(long);
+    method public void setEpicenterCallback(android.support.transition.Transition.EpicenterCallback);
+    method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
+    method public void setMatchOrder(int...);
+    method public void setPathMotion(android.support.transition.PathMotion);
+    method public void setPropagation(android.support.transition.TransitionPropagation);
+    method public android.support.transition.Transition setStartDelay(long);
+    field public static final int MATCH_ID = 3; // 0x3
+    field public static final int MATCH_INSTANCE = 1; // 0x1
+    field public static final int MATCH_ITEM_ID = 4; // 0x4
+    field public static final int MATCH_NAME = 2; // 0x2
+  }
+
+  public static abstract class Transition.EpicenterCallback {
+    ctor public Transition.EpicenterCallback();
+    method public abstract android.graphics.Rect onGetEpicenter(android.support.transition.Transition);
+  }
+
+  public static abstract interface Transition.TransitionListener {
+    method public abstract void onTransitionCancel(android.support.transition.Transition);
+    method public abstract void onTransitionEnd(android.support.transition.Transition);
+    method public abstract void onTransitionPause(android.support.transition.Transition);
+    method public abstract void onTransitionResume(android.support.transition.Transition);
+    method public abstract void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionInflater {
+    method public static android.support.transition.TransitionInflater from(android.content.Context);
+    method public android.support.transition.Transition inflateTransition(int);
+    method public android.support.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+  }
+
+  public class TransitionListenerAdapter implements android.support.transition.Transition.TransitionListener {
+    ctor public TransitionListenerAdapter();
+    method public void onTransitionCancel(android.support.transition.Transition);
+    method public void onTransitionEnd(android.support.transition.Transition);
+    method public void onTransitionPause(android.support.transition.Transition);
+    method public void onTransitionResume(android.support.transition.Transition);
+    method public void onTransitionStart(android.support.transition.Transition);
+  }
+
+  public class TransitionManager {
+    ctor public TransitionManager();
+    method public static void beginDelayedTransition(android.view.ViewGroup);
+    method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
+    method public static void endTransitions(android.view.ViewGroup);
+    method public static void go(android.support.transition.Scene);
+    method public static void go(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
+    method public void setTransition(android.support.transition.Scene, android.support.transition.Scene, android.support.transition.Transition);
+    method public void transitionTo(android.support.transition.Scene);
+  }
+
+  public abstract class TransitionPropagation {
+    ctor public TransitionPropagation();
+    method public abstract void captureValues(android.support.transition.TransitionValues);
+    method public abstract java.lang.String[] getPropagationProperties();
+    method public abstract long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+  }
+
+  public class TransitionSet extends android.support.transition.Transition {
+    ctor public TransitionSet();
+    ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
+    method public android.support.transition.TransitionSet addListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.TransitionSet addTarget(android.view.View);
+    method public android.support.transition.TransitionSet addTarget(int);
+    method public android.support.transition.TransitionSet addTarget(java.lang.String);
+    method public android.support.transition.TransitionSet addTarget(java.lang.Class);
+    method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getOrdering();
+    method public android.support.transition.Transition getTransitionAt(int);
+    method public int getTransitionCount();
+    method public android.support.transition.TransitionSet removeListener(android.support.transition.Transition.TransitionListener);
+    method public android.support.transition.TransitionSet removeTarget(int);
+    method public android.support.transition.TransitionSet removeTarget(android.view.View);
+    method public android.support.transition.TransitionSet removeTarget(java.lang.Class);
+    method public android.support.transition.TransitionSet removeTarget(java.lang.String);
+    method public android.support.transition.TransitionSet removeTransition(android.support.transition.Transition);
+    method public android.support.transition.TransitionSet setDuration(long);
+    method public android.support.transition.TransitionSet setInterpolator(android.animation.TimeInterpolator);
+    method public android.support.transition.TransitionSet setOrdering(int);
+    method public android.support.transition.TransitionSet setStartDelay(long);
+    field public static final int ORDERING_SEQUENTIAL = 1; // 0x1
+    field public static final int ORDERING_TOGETHER = 0; // 0x0
+  }
+
+  public class TransitionValues {
+    ctor public TransitionValues();
+    field public final java.util.Map<java.lang.String, java.lang.Object> values;
+    field public android.view.View view;
+  }
+
+  public abstract class Visibility extends android.support.transition.Transition {
+    ctor public Visibility();
+    ctor public Visibility(android.content.Context, android.util.AttributeSet);
+    method public void captureEndValues(android.support.transition.TransitionValues);
+    method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getMode();
+    method public boolean isVisible(android.support.transition.TransitionValues);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setMode(int);
+    field public static final int MODE_IN = 1; // 0x1
+    field public static final int MODE_OUT = 2; // 0x2
+  }
+
+  public abstract class VisibilityPropagation extends android.support.transition.TransitionPropagation {
+    ctor public VisibilityPropagation();
+    method public void captureValues(android.support.transition.TransitionValues);
+    method public java.lang.String[] getPropagationProperties();
+    method public int getViewVisibility(android.support.transition.TransitionValues);
+    method public int getViewX(android.support.transition.TransitionValues);
+    method public int getViewY(android.support.transition.TransitionValues);
+  }
+
+}
+
+package android.support.v13.app {
+
+  public class ActivityCompat extends android.support.v4.app.ActivityCompat {
+    ctor protected ActivityCompat();
+    method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+  }
+
+  public class FragmentCompat {
+    ctor public FragmentCompat();
+    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
+    method public static deprecated void setMenuVisibility(android.app.Fragment, boolean);
+    method public static void setUserVisibleHint(android.app.Fragment, boolean);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
+  }
+
+  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
+    method public abstract android.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public deprecated void setup();
+    method public void setup(android.content.Context, android.app.FragmentManager);
+    method public void setup(android.content.Context, android.app.FragmentManager, int);
+  }
+
+}
+
+package android.support.v13.view {
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, android.support.v13.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static abstract interface DragStartHelper.OnDragStartListener {
+    method public abstract boolean onDragStart(android.view.View, android.support.v13.view.DragStartHelper);
+  }
+
+  public deprecated class ViewCompat extends android.support.v4.view.ViewCompat {
+  }
+
+}
+
+package android.support.v13.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor public EditorInfoCompat();
+    method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
+  }
+
+  public static abstract interface InputConnectionCompat.OnCommitContentListener {
+    method public abstract boolean onCommitContent(android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public java.lang.Object unwrap();
+    method public static android.support.v13.view.inputmethod.InputContentInfoCompat wrap(java.lang.Object);
+  }
+
+}
+
+package android.support.v14.preference {
+
+  public class EditTextPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public EditTextPreferenceDialogFragment();
+    method public static android.support.v14.preference.EditTextPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public ListPreferenceDialogFragment();
+    method public static android.support.v14.preference.ListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public MultiSelectListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence[] getEntryValues();
+    method protected boolean[] getSelectedItems();
+    method public java.util.Set<java.lang.String> getValues();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValues(java.util.Set<java.lang.String>);
+  }
+
+  public class MultiSelectListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
+    ctor public MultiSelectListPreferenceDialogFragment();
+    method public static android.support.v14.preference.MultiSelectListPreferenceDialogFragment newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragment extends android.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragment();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragment.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public class SwitchPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreference(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+}
+
+package android.support.v17.leanback.app {
+
+  public final class BackgroundManager {
+    method public void attach(android.view.Window);
+    method public void attachToView(android.view.View);
+    method public void clearDrawable();
+    method public final int getColor();
+    method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
+    method public deprecated android.graphics.drawable.Drawable getDimLayer();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
+    method public boolean isAttached();
+    method public boolean isAutoReleaseOnStop();
+    method public void release();
+    method public void setAutoReleaseOnStop(boolean);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColor(int);
+    method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setThemeDrawableResourceId(int);
+  }
+
+  public class BaseFragment extends android.support.v17.leanback.app.BrandedFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+   abstract class BaseRowFragment extends android.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+   abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment {
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAlignment(int);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public class BaseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method protected void onEntranceTransitionEnd();
+    method protected void onEntranceTransitionPrepare();
+    method protected void onEntranceTransitionStart();
+    method public void prepareEntranceTransition();
+    method protected void runEntranceTransition(java.lang.Object);
+    method public void startEntranceTransition();
+  }
+
+  public class BrandedFragment extends android.app.Fragment {
+    ctor public BrandedFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrandedSupportFragment extends android.support.v4.app.Fragment {
+    ctor public BrandedSupportFragment();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public int getSearchAffordanceColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public java.lang.CharSequence getTitle();
+    method public android.view.View getTitleView();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public final boolean isShowingTitle();
+    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColor(int);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleView(android.view.View);
+    method public void showTitle(boolean);
+    method public void showTitle(int);
+  }
+
+  public class BrowseFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public BrowseFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
+    method public int getHeadersState();
+    method public android.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersFragment onCreateHeadersFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseFragment.BrowseTransitionListener {
+    ctor public BrowseFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseFragment.FragmentFactory<T extends android.app.Fragment> {
+    ctor public BrowseFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseFragment.FragmentFactory {
+    ctor public BrowseFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseFragment.MainFragmentAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseFragment.MainFragmentAdapterRegistry();
+    method public android.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseFragment.FragmentFactory);
+  }
+
+  public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
+    ctor public BrowseFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class BrowseSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public BrowseSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
+    method public void enableMainFragmentScaling(boolean);
+    method public deprecated void enableRowScaling(boolean);
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBrandColor();
+    method public int getHeadersState();
+    method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
+    method public android.support.v4.app.Fragment getMainFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public int getSelectedPosition();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
+    method public final boolean isHeadersTransitionOnBackEnabled();
+    method public boolean isInHeadersTransition();
+    method public boolean isShowingHeaders();
+    method public android.support.v17.leanback.app.HeadersSupportFragment onCreateHeadersSupportFragment();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBrandColor(int);
+    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener);
+    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setHeadersState(int);
+    method public final void setHeadersTransitionOnBackEnabled(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void startHeadersTransition(boolean);
+    field public static final int HEADERS_DISABLED = 3; // 0x3
+    field public static final int HEADERS_ENABLED = 1; // 0x1
+    field public static final int HEADERS_HIDDEN = 2; // 0x2
+  }
+
+  public static class BrowseSupportFragment.BrowseTransitionListener {
+    ctor public BrowseSupportFragment.BrowseTransitionListener();
+    method public void onHeadersTransitionStart(boolean);
+    method public void onHeadersTransitionStop(boolean);
+  }
+
+  public static abstract class BrowseSupportFragment.FragmentFactory<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.FragmentFactory();
+    method public abstract T createFragment(java.lang.Object);
+  }
+
+  public static abstract interface BrowseSupportFragment.FragmentHost {
+    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
+    method public abstract void showTitleView(boolean);
+  }
+
+  public static class BrowseSupportFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory {
+    ctor public BrowseSupportFragment.ListRowFragmentFactory();
+    method public android.support.v17.leanback.app.RowsSupportFragment createFragment(java.lang.Object);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentAdapter(T);
+    method public final T getFragment();
+    method public final android.support.v17.leanback.app.BrowseSupportFragment.FragmentHost getFragmentHost();
+    method public boolean isScalingEnabled();
+    method public boolean isScrolling();
+    method public void onTransitionEnd();
+    method public boolean onTransitionPrepare();
+    method public void onTransitionStart();
+    method public void setAlignment(int);
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setScalingEnabled(boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+  }
+
+  public static final class BrowseSupportFragment.MainFragmentAdapterRegistry {
+    ctor public BrowseSupportFragment.MainFragmentAdapterRegistry();
+    method public android.support.v4.app.Fragment createFragment(java.lang.Object);
+    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory);
+  }
+
+  public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
+    ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public final T getFragment();
+    method public int getSelectedPosition();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSelectedPosition(int, boolean);
+  }
+
+  public static abstract interface BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+  }
+
+  public class DetailsFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public DetailsFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsFragmentBackgroundController {
+    ctor public DetailsFragmentBackgroundController(android.support.v17.leanback.app.DetailsFragment);
+    method public boolean canNavigateToVideoFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.app.Fragment findOrCreateVideoFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.app.Fragment onCreateVideoFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class DetailsSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public DetailsSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
+    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
+  }
+
+  public class DetailsSupportFragmentBackgroundController {
+    ctor public DetailsSupportFragmentBackgroundController(android.support.v17.leanback.app.DetailsSupportFragment);
+    method public boolean canNavigateToVideoSupportFragment();
+    method public void enableParallax();
+    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
+    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
+    method public final android.graphics.drawable.Drawable getBottomDrawable();
+    method public final android.graphics.Bitmap getCoverBitmap();
+    method public final android.graphics.drawable.Drawable getCoverDrawable();
+    method public final int getParallaxDrawableMaxOffset();
+    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
+    method public final int getSolidColor();
+    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
+    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
+    method public final void setCoverBitmap(android.graphics.Bitmap);
+    method public final void setParallaxDrawableMaxOffset(int);
+    method public final void setSolidColor(int);
+    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
+    method public final void switchToRows();
+    method public final void switchToVideo();
+  }
+
+  public class ErrorFragment extends android.support.v17.leanback.app.BrandedFragment {
+    ctor public ErrorFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class ErrorSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
+    ctor public ErrorSupportFragment();
+    method public android.graphics.drawable.Drawable getBackgroundDrawable();
+    method public android.view.View.OnClickListener getButtonClickListener();
+    method public java.lang.String getButtonText();
+    method public android.graphics.drawable.Drawable getImageDrawable();
+    method public java.lang.CharSequence getMessage();
+    method public boolean isBackgroundTranslucent();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonClickListener(android.view.View.OnClickListener);
+    method public void setButtonText(java.lang.String);
+    method public void setDefaultBackground(boolean);
+    method public void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setMessage(java.lang.CharSequence);
+  }
+
+  public class GuidedStepFragment extends android.app.Fragment {
+    ctor public GuidedStepFragment();
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
+    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class GuidedStepSupportFragment extends android.support.v4.app.Fragment {
+    ctor public GuidedStepSupportFragment();
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public void collapseAction(boolean);
+    method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
+    method public int findActionPositionById(long);
+    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
+    method public int findButtonActionPositionById(long);
+    method public void finishGuidedStepSupportFragments();
+    method public android.view.View getActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
+    method public android.view.View getButtonActionItemView(int);
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
+    method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager);
+    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
+    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
+    method public int getSelectedActionPosition();
+    method public int getSelectedButtonActionPosition();
+    method public int getUiStyle();
+    method public boolean isExpanded();
+    method public boolean isFocusOutEndAllowed();
+    method public boolean isFocusOutStartAllowed();
+    method public boolean isSubActionsExpanded();
+    method public void notifyActionChanged(int);
+    method public void notifyButtonActionChanged(int);
+    method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment);
+    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
+    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
+    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
+    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
+    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
+    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
+    method protected void onProvideFragmentTransitions();
+    method public int onProvideTheme();
+    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int);
+    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setSelectedActionPosition(int);
+    method public void setSelectedButtonActionPosition(int);
+    method public void setUiStyle(int);
+    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
+    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
+    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
+    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
+    field public static final int UI_STYLE_REPLACE = 0; // 0x0
+  }
+
+  public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment {
+    ctor public HeadersFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment {
+    ctor public HeadersSupportFragment();
+    method public boolean isScrolling();
+    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener);
+    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderClickedListener {
+    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener {
+    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
+  }
+
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+    method protected void pausePlayback();
+    method protected void skipToNext();
+    method protected void skipToPrevious();
+    method protected void startPlayback(int);
+  }
+
+  public abstract class OnboardingFragment extends android.app.Fragment {
+    ctor public OnboardingFragment();
+    method public final int getArrowBackgroundColor();
+    method public final int getArrowColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final java.lang.CharSequence getStartButtonText();
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setArrowColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setStartButtonText(java.lang.CharSequence);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract class OnboardingSupportFragment extends android.support.v4.app.Fragment {
+    ctor public OnboardingSupportFragment();
+    method public final int getArrowBackgroundColor();
+    method public final int getArrowColor();
+    method protected final int getCurrentPageIndex();
+    method public final int getDescriptionViewTextColor();
+    method public final int getDotBackgroundColor();
+    method public final int getIconResourceId();
+    method public final int getLogoResourceId();
+    method protected abstract int getPageCount();
+    method protected abstract java.lang.CharSequence getPageDescription(int);
+    method protected abstract java.lang.CharSequence getPageTitle(int);
+    method public final java.lang.CharSequence getStartButtonText();
+    method public final int getTitleViewTextColor();
+    method protected final boolean isLogoAnimationFinished();
+    method protected void moveToNextPage();
+    method protected void moveToPreviousPage();
+    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateDescriptionAnimator();
+    method protected android.animation.Animator onCreateEnterAnimation();
+    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
+    method protected android.animation.Animator onCreateLogoAnimation();
+    method protected android.animation.Animator onCreateTitleAnimator();
+    method protected void onFinishFragment();
+    method protected void onLogoAnimationFinished();
+    method protected void onPageChanged(int, int);
+    method public int onProvideTheme();
+    method public void setArrowBackgroundColor(int);
+    method public void setArrowColor(int);
+    method public void setDescriptionViewTextColor(int);
+    method public void setDotBackgroundColor(int);
+    method public final void setIconResouceId(int);
+    method public final void setLogoResourceId(int);
+    method public void setStartButtonText(java.lang.CharSequence);
+    method public void setTitleViewTextColor(int);
+    method protected final void startEnterAnimation(boolean);
+  }
+
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
+    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
+    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
+    method protected deprecated void pausePlayback();
+    method public final void play(int);
+    method public final void previous();
+    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method protected deprecated void skipToNext();
+    method protected deprecated void skipToPrevious();
+    method protected deprecated void startPlayback(int);
+  }
+
+  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
+    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  }
+
+  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
+    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public class PlaybackFragment extends android.app.Fragment {
+    ctor public PlaybackFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
+    method public void fadeOut();
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
+    ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlayFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlayFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+    ctor public PlaybackOverlaySupportFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
+  }
+
+  public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
+    ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
+    ctor public PlaybackSupportFragment();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public android.support.v17.leanback.app.ProgressBarManager getProgressBarManager();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public deprecated boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method protected void onBufferingStateChanged(boolean);
+    method protected void onError(int, java.lang.CharSequence);
+    method protected void onVideoSizeChanged(int, int);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void showControlsOverlay(boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
+    method public void fadeOut();
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public final class ProgressBarManager {
+    ctor public ProgressBarManager();
+    method public void disableProgressBar();
+    method public void enableProgressBar();
+    method public long getInitialDelay();
+    method public void hide();
+    method public void setInitialDelay(long);
+    method public void setProgressBarView(android.view.View);
+    method public void setRootView(android.view.ViewGroup);
+    method public void show();
+  }
+
+  public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment implements android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter {
+    ctor public RowsFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public static class RowsFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter {
+    ctor public RowsFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsFragment);
+  }
+
+  public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment implements android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapterProvider {
+    ctor public RowsSupportFragment();
+    method public deprecated void enableRowScaling(boolean);
+    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
+    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
+    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
+    method public boolean isScrolling();
+    method public void setEntranceTransitionState(boolean);
+    method public void setExpand(boolean);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+  }
+
+  public static class RowsSupportFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter {
+    ctor public RowsSupportFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public static class RowsSupportFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter {
+    ctor public RowsSupportFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsSupportFragment);
+  }
+
+  public class SearchFragment extends android.app.Fragment {
+    ctor public SearchFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SearchSupportFragment extends android.support.v4.app.Fragment {
+    ctor public SearchSupportFragment();
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
+    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.content.Intent getRecognizerIntent();
+    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
+    method public java.lang.String getTitle();
+    method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchQuery(java.lang.String, boolean);
+    method public void setSearchQuery(android.content.Intent, boolean);
+    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+  }
+
+  public static abstract interface SearchSupportFragment.SearchResultProvider {
+    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class VerticalGridFragment extends android.support.v17.leanback.app.BaseFragment {
+    ctor public VerticalGridFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BaseSupportFragment {
+    ctor public VerticalGridSupportFragment();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
+    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSelectedPosition(int);
+  }
+
+  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
+    ctor public VideoFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
+    ctor public VideoSupportFragment();
+    method public android.view.SurfaceView getSurfaceView();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
+    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.database {
+
+  public abstract class CursorMapper {
+    ctor public CursorMapper();
+    method protected abstract java.lang.Object bind(android.database.Cursor);
+    method protected abstract void bindColumns(android.database.Cursor);
+    method public java.lang.Object convert(android.database.Cursor);
+  }
+
+}
+
+package android.support.v17.leanback.graphics {
+
+  public class BoundsRule {
+    ctor public BoundsRule();
+    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
+    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule top;
+  }
+
+  public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
+    method public int getAbsoluteValue();
+    method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
+    method public void setAbsoluteValue(int);
+    method public void setFraction(float);
+  }
+
+  public final class ColorFilterCache {
+    method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
+    method public android.graphics.ColorFilter getFilterForLevel(float);
+  }
+
+  public final class ColorFilterDimmer {
+    method public void applyFilterToView(android.view.View);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float);
+    method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context);
+    method public android.graphics.ColorFilter getColorFilter();
+    method public android.graphics.Paint getPaint();
+    method public void setActiveLevel(float);
+  }
+
+  public final class ColorOverlayDimmer {
+    method public int applyToColor(int);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float);
+    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context);
+    method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean);
+    method public int getAlpha();
+    method public float getAlphaFloat();
+    method public android.graphics.Paint getPaint();
+    method public boolean needsDraw();
+    method public void setActiveLevel(float);
+  }
+
+  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public CompositeDrawable();
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
+    method public int getChildCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void removeChild(int);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+  }
+
+  public static final class CompositeDrawable.ChildDrawable {
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
+    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void recomputeBounds();
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
+  }
+
+  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
+    ctor public FitWidthBitmapDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.Bitmap getBitmap();
+    method public int getOpacity();
+    method public android.graphics.Rect getSource();
+    method public int getVerticalOffset();
+    method public void setAlpha(int);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setSource(android.graphics.Rect);
+    method public void setVerticalOffset(int);
+    field public static final android.util.Property<android.support.v17.leanback.graphics.FitWidthBitmapDrawable, java.lang.Integer> PROPERTY_VERTICAL_OFFSET;
+  }
+
+}
+
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public class MediaPlayerAdapter extends android.support.v17.leanback.media.PlayerAdapter {
+    ctor public MediaPlayerAdapter(android.content.Context);
+    method protected boolean onError(int, int);
+    method protected boolean onInfo(int, int);
+    method protected void onSeekComplete();
+    method public void pause();
+    method public void play();
+    method public void release();
+    method public void reset();
+    method public boolean setDataSource(android.net.Uri);
+  }
+
+  public class PlaybackBannerControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackBaseControlGlue {
+    ctor public PlaybackBannerControlGlue(android.content.Context, int[], T);
+    ctor public PlaybackBannerControlGlue(android.content.Context, int[], int[], T);
+    method public int[] getFastForwardSpeeds();
+    method public int[] getRewindSpeeds();
+    method public long getSupportedActions();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackBaseControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackBaseControlGlue(android.content.Context, T);
+    method public android.graphics.drawable.Drawable getArt();
+    method public final long getBufferedPosition();
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public long getCurrentPosition();
+    method public final long getDuration();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public final T getPlayerAdapter();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public final boolean isPlaying();
+    method public final boolean isPrepared();
+    method protected static void notifyItemChanged(android.support.v17.leanback.widget.ArrayObjectAdapter, java.lang.Object);
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method protected abstract android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onPlayCompleted();
+    method protected void onPlayStateChanged();
+    method protected void onPreparedStateChanged();
+    method public final void seekTo(long);
+    method public void setArt(android.graphics.drawable.Drawable);
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public deprecated android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public deprecated void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public void addPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method protected java.util.List<android.support.v17.leanback.media.PlaybackGlue.PlayerCallback> getPlayerCallbacks();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public deprecated boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public void removePlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public deprecated void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlaybackGlue);
+    method public deprecated void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public deprecated void fadeOut();
+    method public android.support.v17.leanback.media.PlaybackGlueHost.PlayerCallback getPlayerCallback();
+    method public void hideControlsOverlay(boolean);
+    method public boolean isControlsOverlayAutoHideEnabled();
+    method public boolean isControlsOverlayVisible();
+    method public void notifyPlaybackRowChanged();
+    method public void setControlsOverlayAutoHideEnabled(boolean);
+    method public deprecated void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void showControlsOverlay(boolean);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostDestroy();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public static class PlaybackGlueHost.PlayerCallback {
+    ctor public PlaybackGlueHost.PlayerCallback();
+    method public void onBufferingStateChanged(boolean);
+    method public void onError(int, java.lang.CharSequence);
+    method public void onVideoSizeChanged(int, int);
+  }
+
+  public class PlaybackTransportControlGlue<T extends android.support.v17.leanback.media.PlayerAdapter> extends android.support.v17.leanback.media.PlaybackBaseControlGlue {
+    ctor public PlaybackTransportControlGlue(android.content.Context, T);
+    method public final android.support.v17.leanback.widget.PlaybackSeekDataProvider getSeekProvider();
+    method public final boolean isSeekEnabled();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected android.support.v17.leanback.widget.PlaybackRowPresenter onCreateRowPresenter();
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method public final void setSeekEnabled(boolean);
+    method public final void setSeekProvider(android.support.v17.leanback.widget.PlaybackSeekDataProvider);
+  }
+
+  public abstract class PlayerAdapter {
+    ctor public PlayerAdapter();
+    method public long getBufferedPosition();
+    method public final android.support.v17.leanback.media.PlayerAdapter.Callback getCallback();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public boolean isPlaying();
+    method public boolean isPrepared();
+    method public void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void onDetachedFromHost();
+    method public abstract void pause();
+    method public abstract void play();
+    method public void seekTo(long);
+    method public final void setCallback(android.support.v17.leanback.media.PlayerAdapter.Callback);
+    method public void setProgressUpdatingEnabled(boolean);
+  }
+
+  public static class PlayerAdapter.Callback {
+    ctor public PlayerAdapter.Callback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onBufferingStateChanged(android.support.v17.leanback.media.PlayerAdapter, boolean);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onDurationChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onError(android.support.v17.leanback.media.PlayerAdapter, int, java.lang.String);
+    method public void onPlayCompleted(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPlayStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onPreparedStateChanged(android.support.v17.leanback.media.PlayerAdapter);
+    method public void onVideoSizeChanged(android.support.v17.leanback.media.PlayerAdapter, int, int);
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
+package android.support.v17.leanback.system {
+
+  public class Settings {
+    method public boolean getBoolean(java.lang.String);
+    method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context);
+    method public void setBoolean(java.lang.String, boolean);
+    field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
+  }
+
+}
+
+package android.support.v17.leanback.widget {
+
+  public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public AbstractDetailsDescriptionPresenter();
+    method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getBody();
+    method public android.widget.TextView getSubtitle();
+    method public android.widget.TextView getTitle();
+  }
+
+  public abstract class AbstractMediaItemPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaItemPresenter();
+    ctor public AbstractMediaItemPresenter(int);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter getActionPresenter();
+    method protected int getMediaPlayState(java.lang.Object);
+    method public int getThemeId();
+    method public boolean hasMediaRowSeparator();
+    method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
+    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setBackgroundColor(int);
+    method public void setHasMediaRowSeparator(boolean);
+    method public void setThemeId(int);
+    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
+    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
+  }
+
+  public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaItemPresenter.ViewHolder(android.view.View);
+    method public android.view.ViewGroup getMediaItemActionsContainer();
+    method public android.view.View getMediaItemDetailsView();
+    method public android.widget.TextView getMediaItemDurationView();
+    method public android.widget.TextView getMediaItemNameView();
+    method public android.widget.TextView getMediaItemNumberView();
+    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
+    method public android.view.View getMediaItemPausedView();
+    method public android.view.View getMediaItemPlayingView();
+    method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
+    method public android.view.View getMediaItemRowSeparator();
+    method public android.view.View getSelectorView();
+    method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
+    method public void notifyDetailsChanged();
+    method public void notifyPlayStateChanged();
+    method public void onBindRowActions();
+    method public void setSelectedMediaItemNumberView(int);
+  }
+
+  public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public AbstractMediaListHeaderPresenter(android.content.Context, int);
+    ctor public AbstractMediaListHeaderPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected abstract void onBindMediaListHeaderViewHolder(android.support.v17.leanback.widget.AbstractMediaListHeaderPresenter.ViewHolder, java.lang.Object);
+    method public void setBackgroundColor(int);
+  }
+
+  public static class AbstractMediaListHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public AbstractMediaListHeaderPresenter.ViewHolder(android.view.View);
+    method public android.widget.TextView getHeaderView();
+  }
+
+  public class Action {
+    ctor public Action(long);
+    ctor public Action(long, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence);
+    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable);
+    method public final void addKeyCode(int);
+    method public final android.graphics.drawable.Drawable getIcon();
+    method public final long getId();
+    method public final java.lang.CharSequence getLabel1();
+    method public final java.lang.CharSequence getLabel2();
+    method public final void removeKeyCode(int);
+    method public final boolean respondsToKeyCode(int);
+    method public final void setIcon(android.graphics.drawable.Drawable);
+    method public final void setId(long);
+    method public final void setLabel1(java.lang.CharSequence);
+    method public final void setLabel2(java.lang.CharSequence);
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+  }
+
+  public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ArrayObjectAdapter();
+    method public void add(java.lang.Object);
+    method public void add(int, java.lang.Object);
+    method public void addAll(int, java.util.Collection);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public boolean remove(java.lang.Object);
+    method public int removeItems(int, int);
+    method public void replace(int, java.lang.Object);
+    method public int size();
+    method public <E> java.util.List<E> unmodifiableList();
+  }
+
+  public class BaseCardView extends android.widget.FrameLayout {
+    ctor public BaseCardView(android.content.Context);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.v17.leanback.widget.BaseCardView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v17.leanback.widget.BaseCardView.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v17.leanback.widget.BaseCardView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getCardType();
+    method public deprecated int getExtraVisibility();
+    method public int getInfoVisibility();
+    method public boolean isSelectedAnimationDelayed();
+    method public void setCardType(int);
+    method public deprecated void setExtraVisibility(int);
+    method public void setInfoVisibility(int);
+    method public void setSelectedAnimationDelayed(boolean);
+    field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
+    field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0
+    field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1
+    field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2
+    field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3
+    field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0
+  }
+
+  public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BaseCardView.LayoutParams(int, int);
+    ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams);
+    field public static final int VIEW_TYPE_EXTRA = 2; // 0x2
+    field public static final int VIEW_TYPE_INFO = 1; // 0x1
+    field public static final int VIEW_TYPE_MAIN = 0; // 0x0
+    field public int viewType;
+  }
+
+  public abstract class BaseGridView extends android.support.v7.widget.RecyclerView {
+    method public void addOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void animateIn();
+    method public void animateOut();
+    method public int getChildDrawingOrder(int, int);
+    method public deprecated int getHorizontalMargin();
+    method public int getHorizontalSpacing();
+    method public int getInitialPrefetchItemCount();
+    method public int getItemAlignmentOffset();
+    method public float getItemAlignmentOffsetPercent();
+    method public int getItemAlignmentViewId();
+    method public android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener getOnUnhandledKeyListener();
+    method public final int getSaveChildrenLimitNumber();
+    method public final int getSaveChildrenPolicy();
+    method public int getSelectedPosition();
+    method public deprecated int getVerticalMargin();
+    method public int getVerticalSpacing();
+    method public void getViewSelectedOffsets(android.view.View, int[]);
+    method public int getWindowAlignment();
+    method public int getWindowAlignmentOffset();
+    method public float getWindowAlignmentOffsetPercent();
+    method public boolean hasPreviousViewInSameRow(int);
+    method public boolean isChildLayoutAnimated();
+    method public boolean isFocusDrawingOrderEnabled();
+    method public final boolean isFocusSearchDisabled();
+    method public boolean isItemAlignmentOffsetWithPadding();
+    method public boolean isScrollEnabled();
+    method public boolean isWindowAlignmentPreferKeyLineOverHighEdge();
+    method public boolean isWindowAlignmentPreferKeyLineOverLowEdge();
+    method public boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+    method public void removeOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setAnimateChildLayout(boolean);
+    method public void setChildrenVisibility(int);
+    method public void setFocusDrawingOrderEnabled(boolean);
+    method public final void setFocusSearchDisabled(boolean);
+    method public void setGravity(int);
+    method public void setHasOverlappingRendering(boolean);
+    method public deprecated void setHorizontalMargin(int);
+    method public void setHorizontalSpacing(int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setItemAlignmentOffset(int);
+    method public void setItemAlignmentOffsetPercent(float);
+    method public void setItemAlignmentOffsetWithPadding(boolean);
+    method public void setItemAlignmentViewId(int);
+    method public deprecated void setItemMargin(int);
+    method public void setItemSpacing(int);
+    method public void setLayoutEnabled(boolean);
+    method public void setOnChildLaidOutListener(android.support.v17.leanback.widget.OnChildLaidOutListener);
+    method public void setOnChildSelectedListener(android.support.v17.leanback.widget.OnChildSelectedListener);
+    method public void setOnChildViewHolderSelectedListener(android.support.v17.leanback.widget.OnChildViewHolderSelectedListener);
+    method public void setOnKeyInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnKeyInterceptListener);
+    method public void setOnMotionInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnMotionInterceptListener);
+    method public void setOnTouchInterceptListener(android.support.v17.leanback.widget.BaseGridView.OnTouchInterceptListener);
+    method public void setOnUnhandledKeyListener(android.support.v17.leanback.widget.BaseGridView.OnUnhandledKeyListener);
+    method public void setPruneChild(boolean);
+    method public final void setSaveChildrenLimitNumber(int);
+    method public final void setSaveChildrenPolicy(int);
+    method public void setScrollEnabled(boolean);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, int);
+    method public void setSelectedPosition(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public void setSelectedPositionSmooth(int);
+    method public void setSelectedPositionSmooth(int, android.support.v17.leanback.widget.ViewHolderTask);
+    method public deprecated void setVerticalMargin(int);
+    method public void setVerticalSpacing(int);
+    method public void setWindowAlignment(int);
+    method public void setWindowAlignmentOffset(int);
+    method public void setWindowAlignmentOffsetPercent(float);
+    method public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean);
+    method public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+    field public static final int SAVE_ALL_CHILD = 3; // 0x3
+    field public static final int SAVE_LIMITED_CHILD = 2; // 0x2
+    field public static final int SAVE_NO_CHILD = 0; // 0x0
+    field public static final int SAVE_ON_SCREEN_CHILD = 1; // 0x1
+    field public static final int WINDOW_ALIGN_BOTH_EDGE = 3; // 0x3
+    field public static final int WINDOW_ALIGN_HIGH_EDGE = 2; // 0x2
+    field public static final int WINDOW_ALIGN_LOW_EDGE = 1; // 0x1
+    field public static final int WINDOW_ALIGN_NO_EDGE = 0; // 0x0
+    field public static final float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static abstract interface BaseGridView.OnKeyInterceptListener {
+    method public abstract boolean onInterceptKeyEvent(android.view.KeyEvent);
+  }
+
+  public static abstract interface BaseGridView.OnMotionInterceptListener {
+    method public abstract boolean onInterceptMotionEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnTouchInterceptListener {
+    method public abstract boolean onInterceptTouchEvent(android.view.MotionEvent);
+  }
+
+  public static abstract interface BaseGridView.OnUnhandledKeyListener {
+    method public abstract boolean onUnhandledKey(android.view.KeyEvent);
+  }
+
+  public abstract interface BaseOnItemViewClickedListener<T> {
+    method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public abstract interface BaseOnItemViewSelectedListener<T> {
+    method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
+  }
+
+  public class BrowseFrameLayout extends android.widget.FrameLayout {
+    ctor public BrowseFrameLayout(android.content.Context);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
+    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
+    method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnChildFocusListener {
+    method public abstract void onRequestChildFocus(android.view.View, android.view.View);
+    method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect);
+  }
+
+  public static abstract interface BrowseFrameLayout.OnFocusSearchListener {
+    method public abstract android.view.View onFocusSearch(android.view.View, int);
+  }
+
+  public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ClassPresenterSelector();
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenterSelector(java.lang.Class<?>, android.support.v17.leanback.widget.PresenterSelector);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public ControlButtonPresenterSelector();
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter();
+    method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter();
+  }
+
+  public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public CursorObjectAdapter();
+    method public void changeCursor(android.database.Cursor);
+    method public void close();
+    method public java.lang.Object get(int);
+    method public final android.database.Cursor getCursor();
+    method public final android.support.v17.leanback.database.CursorMapper getMapper();
+    method protected final void invalidateCache(int);
+    method protected final void invalidateCache(int, int);
+    method public boolean isClosed();
+    method protected void onCursorChanged();
+    method protected void onMapperChanged();
+    method public final void setMapper(android.support.v17.leanback.database.CursorMapper);
+    method public int size();
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+  }
+
+  public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DetailsOverviewLogoPresenter();
+    method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
+  }
+
+  public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter getParentPresenter();
+    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder();
+    method public boolean isSizeFromDrawableIntrinsic();
+    method public void setSizeFromDrawableIntrinsic(boolean);
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter;
+    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
+  }
+
+  public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row {
+    ctor public DetailsOverviewRow(java.lang.Object);
+    method public final deprecated void addAction(android.support.v17.leanback.widget.Action);
+    method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public boolean isImageScaleUpAllowed();
+    method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action);
+    method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setImageScaleUpAllowed(boolean);
+    method public final void setItem(java.lang.Object);
+  }
+
+  public static class DetailsOverviewRow.Listener {
+    ctor public DetailsOverviewRow.Listener();
+    method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+    method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
+  }
+
+  public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public boolean isStyleLarge();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setStyleLarge(boolean);
+  }
+
+  public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder;
+  }
+
+  public class DetailsParallax extends android.support.v17.leanback.widget.RecyclerViewParallax {
+    ctor public DetailsParallax();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowBottom();
+    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowTop();
+  }
+
+  public class DividerPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public DividerPresenter();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public class DividerRow extends android.support.v17.leanback.widget.Row {
+    ctor public DividerRow();
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract interface FacetProvider {
+    method public abstract java.lang.Object getFacet(java.lang.Class<?>);
+  }
+
+  public abstract interface FacetProviderAdapter {
+    method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+  }
+
+  public abstract interface FocusHighlight {
+    field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3
+    field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2
+    field public static final int ZOOM_FACTOR_NONE = 0; // 0x0
+    field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1
+    field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4
+  }
+
+  public class FocusHighlightHelper {
+    ctor public FocusHighlightHelper();
+    method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean);
+    method public static deprecated void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView);
+    method public static deprecated void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView, boolean);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter);
+    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, boolean);
+  }
+
+  public abstract interface FragmentAnimationProvider {
+    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public final int getActionsBackgroundColor();
+    method public final int getAlignmentMode();
+    method public final int getBackgroundColor();
+    method public final int getInitialState();
+    method protected int getLayoutResourceId();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public final boolean isParticipatingEntranceTransition();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+    method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
+    method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    method public final void setActionsBackgroundColor(int);
+    method public final void setAlignmentMode(int);
+    method public final void setBackgroundColor(int);
+    method public final void setInitialState(int);
+    method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public final void setParticipatingEntranceTransition(boolean);
+    method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
+    field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1
+    field public static final int ALIGN_MODE_START = 0; // 0x0
+    field public static final int STATE_FULL = 1; // 0x1
+    field public static final int STATE_HALF = 0; // 0x0
+    field public static final int STATE_SMALL = 2; // 0x2
+    field protected int mInitialState;
+  }
+
+  public static abstract class FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.Listener();
+    method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
+    method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener();
+    method public final android.view.ViewGroup getActionsRow();
+    method public final android.view.ViewGroup getDetailsDescriptionFrame();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder();
+    method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder();
+    method public final android.view.ViewGroup getOverviewView();
+    method public final int getState();
+    field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener;
+  }
+
+  public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener {
+    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener();
+  }
+
+  public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener {
+    ctor public FullWidthDetailsOverviewSharedElementHelper();
+    method public boolean getAutoStartSharedElementTransition();
+    method public void setAutoStartSharedElementTransition(boolean);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
+    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
+    method public void startPostponedEnterTransition();
+  }
+
+  public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidanceStylist();
+    method public android.widget.TextView getBreadcrumbView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance);
+    method public void onDestroyView();
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideLayoutId();
+  }
+
+  public static class GuidanceStylist.Guidance {
+    ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable);
+    method public java.lang.String getBreadcrumb();
+    method public java.lang.String getDescription();
+    method public android.graphics.drawable.Drawable getIconDrawable();
+    method public java.lang.String getTitle();
+  }
+
+  public class GuidedAction extends android.support.v17.leanback.widget.Action {
+    ctor protected GuidedAction();
+    method public int getCheckSetId();
+    method public java.lang.CharSequence getDescription();
+    method public int getDescriptionEditInputType();
+    method public int getDescriptionInputType();
+    method public java.lang.CharSequence getEditDescription();
+    method public int getEditInputType();
+    method public java.lang.CharSequence getEditTitle();
+    method public int getInputType();
+    method public android.content.Intent getIntent();
+    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getSubActions();
+    method public java.lang.CharSequence getTitle();
+    method public boolean hasEditableActivatorView();
+    method public boolean hasMultilineDescription();
+    method public boolean hasNext();
+    method public boolean hasSubActions();
+    method public boolean hasTextEditable();
+    method public boolean infoOnly();
+    method public final boolean isAutoSaveRestoreEnabled();
+    method public boolean isChecked();
+    method public boolean isDescriptionEditable();
+    method public boolean isEditTitleUsed();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public void onRestoreInstanceState(android.os.Bundle, java.lang.String);
+    method public void onSaveInstanceState(android.os.Bundle, java.lang.String);
+    method public void setChecked(boolean);
+    method public void setDescription(java.lang.CharSequence);
+    method public void setEditDescription(java.lang.CharSequence);
+    method public void setEditTitle(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setSubActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public void setTitle(java.lang.CharSequence);
+    field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
+    field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
+    field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
+    field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL
+    field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL
+    field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
+    field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
+    field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
+    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
+    field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
+    field public static final int NO_CHECK_SET = 0; // 0x0
+  }
+
+  public static class GuidedAction.Builder extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public deprecated GuidedAction.Builder();
+    ctor public GuidedAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedAction build();
+  }
+
+  public static abstract class GuidedAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedAction.BuilderBase> {
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction);
+    method public B autoSaveRestoreEnabled(boolean);
+    method public B checkSetId(int);
+    method public B checked(boolean);
+    method public B clickAction(long);
+    method public B description(java.lang.CharSequence);
+    method public B description(int);
+    method public B descriptionEditInputType(int);
+    method public B descriptionEditable(boolean);
+    method public B descriptionInputType(int);
+    method public B editDescription(java.lang.CharSequence);
+    method public B editDescription(int);
+    method public B editInputType(int);
+    method public B editTitle(java.lang.CharSequence);
+    method public B editTitle(int);
+    method public B editable(boolean);
+    method public B enabled(boolean);
+    method public B focusable(boolean);
+    method public android.content.Context getContext();
+    method public B hasEditableActivatorView(boolean);
+    method public B hasNext(boolean);
+    method public B icon(android.graphics.drawable.Drawable);
+    method public B icon(int);
+    method public deprecated B iconResourceId(int, android.content.Context);
+    method public B id(long);
+    method public B infoOnly(boolean);
+    method public B inputType(int);
+    method public B intent(android.content.Intent);
+    method public B multilineDescription(boolean);
+    method public B subActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
+    method public B title(java.lang.CharSequence);
+    method public B title(int);
+  }
+
+  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
+    ctor public GuidedActionEditText(android.content.Context);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
+    ctor public GuidedActionsStylist();
+    method public void collapseAction(boolean);
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
+    method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
+    method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
+    method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
+    method public final boolean isBackKeyToCollapseActivatorView();
+    method public final boolean isBackKeyToCollapseSubActions();
+    method public boolean isButtonActions();
+    method public boolean isExpandTransitionSupported();
+    method public boolean isExpanded();
+    method public boolean isInExpandTransition();
+    method public boolean isSubActionsExpanded();
+    method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
+    method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void onBindActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDestroyView();
+    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
+    method public int onProvideItemLayoutId();
+    method public int onProvideItemLayoutId(int);
+    method public int onProvideLayoutId();
+    method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void openInEditMode(android.support.v17.leanback.widget.GuidedAction);
+    method public void setAsButtonActions();
+    method public final void setBackKeyToCollapseActivatorView(boolean);
+    method public final void setBackKeyToCollapseSubActions(boolean);
+    method public deprecated void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
+    field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
+  }
+
+  public static class GuidedActionsStylist.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View);
+    ctor public GuidedActionsStylist.ViewHolder(android.view.View, boolean);
+    method public android.support.v17.leanback.widget.GuidedAction getAction();
+    method public android.widget.ImageView getCheckmarkView();
+    method public android.widget.ImageView getChevronView();
+    method public android.view.View getContentView();
+    method public android.widget.TextView getDescriptionView();
+    method public android.widget.EditText getEditableDescriptionView();
+    method public android.widget.EditText getEditableTitleView();
+    method public android.view.View getEditingView();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public android.widget.ImageView getIconView();
+    method public android.widget.TextView getTitleView();
+    method public boolean isInEditing();
+    method public boolean isInEditingActivatorView();
+    method public boolean isInEditingDescription();
+    method public boolean isInEditingText();
+    method public boolean isInEditingTitle();
+    method public boolean isSubAction();
+  }
+
+  public class GuidedDatePickerAction extends android.support.v17.leanback.widget.GuidedAction {
+    ctor public GuidedDatePickerAction();
+    method public long getDate();
+    method public java.lang.String getDatePickerFormat();
+    method public long getMaxDate();
+    method public long getMinDate();
+    method public void setDate(long);
+  }
+
+  public static final class GuidedDatePickerAction.Builder extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase {
+    ctor public GuidedDatePickerAction.Builder(android.content.Context);
+    method public android.support.v17.leanback.widget.GuidedDatePickerAction build();
+  }
+
+  public static abstract class GuidedDatePickerAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase> extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
+    ctor public GuidedDatePickerAction.BuilderBase(android.content.Context);
+    method protected final void applyDatePickerValues(android.support.v17.leanback.widget.GuidedDatePickerAction);
+    method public B date(long);
+    method public B datePickerFormat(java.lang.String);
+    method public B maxDate(long);
+    method public B minDate(long);
+  }
+
+  public class HeaderItem {
+    ctor public HeaderItem(long, java.lang.String);
+    ctor public HeaderItem(java.lang.String);
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getDescription();
+    method public final long getId();
+    method public final java.lang.String getName();
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setDescription(java.lang.CharSequence);
+  }
+
+  public class HorizontalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public HorizontalGridView(android.content.Context);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int);
+    method public final boolean getFadingLeftEdge();
+    method public final int getFadingLeftEdgeLength();
+    method public final int getFadingLeftEdgeOffset();
+    method public final boolean getFadingRightEdge();
+    method public final int getFadingRightEdgeLength();
+    method public final int getFadingRightEdgeOffset();
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public final void setFadingLeftEdge(boolean);
+    method public final void setFadingLeftEdgeLength(int);
+    method public final void setFadingLeftEdgeOffset(int);
+    method public final void setFadingRightEdge(boolean);
+    method public final void setFadingRightEdgeLength(int);
+    method public final void setFadingRightEdgeOffset(int);
+    method public void setNumRows(int);
+    method public void setRowHeight(int);
+  }
+
+  public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher {
+    ctor public HorizontalHoverCardSwitcher();
+    method protected void insertView(android.view.View);
+    method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object);
+  }
+
+  public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
+    ctor public deprecated ImageCardView(android.content.Context, int);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
+    ctor public ImageCardView(android.content.Context);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
+    method public android.graphics.drawable.Drawable getBadgeImage();
+    method public java.lang.CharSequence getContentText();
+    method public android.graphics.drawable.Drawable getInfoAreaBackground();
+    method public android.graphics.drawable.Drawable getMainImage();
+    method public final android.widget.ImageView getMainImageView();
+    method public java.lang.CharSequence getTitleText();
+    method public void setBadgeImage(android.graphics.drawable.Drawable);
+    method public void setContentText(java.lang.CharSequence);
+    method public void setInfoAreaBackground(android.graphics.drawable.Drawable);
+    method public void setInfoAreaBackgroundColor(int);
+    method public void setMainImage(android.graphics.drawable.Drawable);
+    method public void setMainImage(android.graphics.drawable.Drawable, boolean);
+    method public void setMainImageAdjustViewBounds(boolean);
+    method public void setMainImageDimensions(int, int);
+    method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
+    method public void setTitleText(java.lang.CharSequence);
+    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
+    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
+    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
+    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
+    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
+  }
+
+  public abstract interface ImeKeyMonitor {
+    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public static abstract interface ImeKeyMonitor.ImeKeyListener {
+    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
+  }
+
+  public final class ItemAlignmentFacet {
+    ctor public ItemAlignmentFacet();
+    method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs();
+    method public boolean isMultiAlignment();
+    method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]);
+    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
+  }
+
+  public static class ItemAlignmentFacet.ItemAlignmentDef {
+    ctor public ItemAlignmentFacet.ItemAlignmentDef();
+    method public final int getItemAlignmentFocusViewId();
+    method public final int getItemAlignmentOffset();
+    method public final float getItemAlignmentOffsetPercent();
+    method public final int getItemAlignmentViewId();
+    method public boolean isAlignedToTextViewBaseLine();
+    method public final boolean isItemAlignmentOffsetWithPadding();
+    method public final void setAlignedToTextViewBaseline(boolean);
+    method public final void setItemAlignmentFocusViewId(int);
+    method public final void setItemAlignmentOffset(int);
+    method public final void setItemAlignmentOffsetPercent(float);
+    method public final void setItemAlignmentOffsetWithPadding(boolean);
+    method public final void setItemAlignmentViewId(int);
+  }
+
+  public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter {
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ItemBridgeAdapter();
+    method public void clear();
+    method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
+    method public int getItemCount();
+    method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper();
+    method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper();
+    method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener);
+    method public void setPresenter(android.support.v17.leanback.widget.PresenterSelector);
+    method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>);
+    method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper);
+  }
+
+  public static class ItemBridgeAdapter.AdapterListener {
+    ctor public ItemBridgeAdapter.AdapterListener();
+    method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
+    method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
+  }
+
+  public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    method public final java.lang.Object getExtraObject();
+    method public java.lang.Object getFacet(java.lang.Class<?>);
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.Presenter getPresenter();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder();
+    method public void setExtraObject(java.lang.Object);
+  }
+
+  public static abstract class ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapter.Wrapper();
+    method public abstract android.view.View createWrapper(android.view.View);
+    method public abstract void wrap(android.view.View, android.view.View);
+  }
+
+  public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper {
+    ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper);
+    method public android.view.View createWrapper(android.view.View);
+    method public void wrap(android.view.View, android.view.View);
+  }
+
+  public class ListRow extends android.support.v17.leanback.widget.Row {
+    ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
+    ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public java.lang.CharSequence getContentDescription();
+    method public void setContentDescription(java.lang.CharSequence);
+  }
+
+  public final class ListRowHoverCardView extends android.widget.LinearLayout {
+    ctor public ListRowHoverCardView(android.content.Context);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int);
+    method public final java.lang.CharSequence getDescription();
+    method public final java.lang.CharSequence getTitle();
+    method public final void setDescription(java.lang.CharSequence);
+    method public final void setTitle(java.lang.CharSequence);
+  }
+
+  public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public ListRowPresenter();
+    ctor public ListRowPresenter(int);
+    ctor public ListRowPresenter(int, boolean);
+    method protected void applySelectLevelToChild(android.support.v17.leanback.widget.ListRowPresenter.ViewHolder, android.view.View);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public int getExpandedRowHeight();
+    method public final int getFocusZoomFactor();
+    method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector();
+    method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter);
+    method public int getRowHeight();
+    method public final boolean getShadowEnabled();
+    method public final deprecated int getZoomFactor();
+    method public final boolean isFocusDimmerUsed();
+    method public final boolean isKeepChildForeground();
+    method public boolean isUsingDefaultListSelectEffect();
+    method public final boolean isUsingDefaultSelectEffect();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void setExpandedRowHeight(int);
+    method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumRows(int);
+    method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int);
+    method public void setRowHeight(int);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class ListRowPresenter.SelectItemViewHolderTask extends android.support.v17.leanback.widget.Presenter.ViewHolderTask {
+    ctor public ListRowPresenter.SelectItemViewHolderTask(int);
+    method public int getItemPosition();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolderTask getItemTask();
+    method public boolean isSmoothScroll();
+    method public void setItemPosition(int);
+    method public void setItemTask(android.support.v17.leanback.widget.Presenter.ViewHolderTask);
+    method public void setSmoothScroll(boolean);
+  }
+
+  public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter);
+    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
+    method public final android.support.v17.leanback.widget.HorizontalGridView getGridView();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getItemViewHolder(int);
+    method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter();
+    method public int getSelectedPosition();
+  }
+
+  public final class ListRowView extends android.widget.LinearLayout {
+    ctor public ListRowView(android.content.Context);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet);
+    ctor public ListRowView(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
+  }
+
+  public abstract interface MultiActionsProvider {
+    method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
+  }
+
+  public static class MultiActionsProvider.MultiAction {
+    ctor public MultiActionsProvider.MultiAction(long);
+    method public android.graphics.drawable.Drawable getCurrentDrawable();
+    method public android.graphics.drawable.Drawable[] getDrawables();
+    method public long getId();
+    method public int getIndex();
+    method public void incrementIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+  }
+
+  public abstract class ObjectAdapter {
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public ObjectAdapter();
+    method public abstract java.lang.Object get(int);
+    method public long getId(int);
+    method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
+    method public final boolean hasStableIds();
+    method public boolean isImmediateNotifySupported();
+    method protected final void notifyChanged();
+    method public final void notifyItemRangeChanged(int, int);
+    method protected final void notifyItemRangeInserted(int, int);
+    method protected final void notifyItemRangeRemoved(int, int);
+    method protected void onHasStableIdsChanged();
+    method protected void onPresenterSelectorChanged();
+    method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    method public final void setHasStableIds(boolean);
+    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
+    method public abstract int size();
+    method public final void unregisterAllObservers();
+    method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
+    field public static final int NO_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class ObjectAdapter.DataObserver {
+    ctor public ObjectAdapter.DataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public abstract interface OnActionClickedListener {
+    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
+  }
+
+  public abstract interface OnChildLaidOutListener {
+    method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract deprecated interface OnChildSelectedListener {
+    method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long);
+  }
+
+  public abstract class OnChildViewHolderSelectedListener {
+    ctor public OnChildViewHolderSelectedListener();
+    method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+  }
+
+  public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
+  }
+
+  public abstract interface OnItemViewSelectedListener implements android.support.v17.leanback.widget.BaseOnItemViewSelectedListener {
+  }
+
+  public class PageRow extends android.support.v17.leanback.widget.Row {
+    ctor public PageRow(android.support.v17.leanback.widget.HeaderItem);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public abstract class Parallax<PropertyT extends android.util.Property> {
+    ctor public Parallax();
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
+    method public abstract PropertyT createProperty(java.lang.String, int);
+    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
+    method public final java.util.List<PropertyT> getProperties();
+    method public void removeAllEffects();
+    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public void updateValues();
+  }
+
+  public static class Parallax.FloatProperty extends android.util.Property {
+    ctor public Parallax.FloatProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
+    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
+    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
+  }
+
+  public static class Parallax.IntProperty extends android.util.Property {
+    ctor public Parallax.IntProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
+    method public final int getIndex();
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
+    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
+    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
+  }
+
+  public static class Parallax.PropertyMarkerValue<PropertyT> {
+    ctor public Parallax.PropertyMarkerValue(PropertyT);
+    method public PropertyT getProperty();
+  }
+
+  public abstract class ParallaxEffect {
+    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
+    method public final void performMapping(android.support.v17.leanback.widget.Parallax);
+    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
+  }
+
+  public abstract class ParallaxTarget {
+    ctor public ParallaxTarget();
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
+  }
+
+  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
+  }
+
+  public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
+    ctor public PlaybackControlsRow(java.lang.Object);
+    ctor public PlaybackControlsRow();
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
+    method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
+    method public long getBufferedPosition();
+    method public deprecated int getBufferedProgress();
+    method public deprecated long getBufferedProgressLong();
+    method public long getCurrentPosition();
+    method public deprecated int getCurrentTime();
+    method public deprecated long getCurrentTimeLong();
+    method public long getDuration();
+    method public final android.graphics.drawable.Drawable getImageDrawable();
+    method public final java.lang.Object getItem();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
+    method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
+    method public deprecated int getTotalTime();
+    method public deprecated long getTotalTimeLong();
+    method public void setBufferedPosition(long);
+    method public deprecated void setBufferedProgress(int);
+    method public deprecated void setBufferedProgressLong(long);
+    method public void setCurrentPosition(long);
+    method public deprecated void setCurrentTime(int);
+    method public deprecated void setCurrentTimeLong(long);
+    method public void setDuration(long);
+    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
+    method public final void setImageDrawable(android.graphics.drawable.Drawable);
+    method public void setOnPlaybackProgressChangedListener(android.support.v17.leanback.widget.PlaybackControlsRow.OnPlaybackProgressCallback);
+    method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public deprecated void setTotalTime(int);
+    method public deprecated void setTotalTimeLong(long);
+  }
+
+  public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context);
+    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context);
+    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context);
+    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MoreActions(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.MultiAction(int);
+    method public int getActionCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getIndex();
+    method public java.lang.String getLabel(int);
+    method public java.lang.String getSecondaryLabel(int);
+    method public void nextIndex();
+    method public void setDrawables(android.graphics.drawable.Drawable[]);
+    method public void setIndex(int);
+    method public void setLabels(java.lang.String[]);
+    method public void setSecondaryLabels(java.lang.String[]);
+  }
+
+  public static class PlaybackControlsRow.OnPlaybackProgressCallback {
+    ctor public PlaybackControlsRow.OnPlaybackProgressCallback();
+    method public void onBufferedPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onCurrentPositionChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+    method public void onDurationChanged(android.support.v17.leanback.widget.PlaybackControlsRow, long);
+  }
+
+  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
+    field public static final int INDEX_PAUSE = 1; // 0x1
+    field public static final int INDEX_PLAY = 0; // 0x0
+    field public static deprecated int PAUSE;
+    field public static deprecated int PLAY;
+  }
+
+  public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int);
+    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int);
+    field public static deprecated int ALL;
+    field public static final int INDEX_ALL = 1; // 0x1
+    field public static final int INDEX_NONE = 0; // 0x0
+    field public static final int INDEX_ONE = 2; // 0x2
+    field public static deprecated int NONE;
+    field public static deprecated int ONE;
+  }
+
+  public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context);
+    ctor public PlaybackControlsRow.RewindAction(android.content.Context, int);
+  }
+
+  public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context);
+    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int);
+    field public static final int INDEX_OFF = 0; // 0x0
+    field public static final int INDEX_ON = 1; // 0x1
+    field public static deprecated int OFF;
+    field public static deprecated int ON;
+  }
+
+  public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipNextAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context);
+  }
+
+  public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
+    ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int);
+    field public static final int INDEX_OUTLINE = 1; // 0x1
+    field public static final int INDEX_SOLID = 0; // 0x0
+    field public static deprecated int OUTLINE;
+    field public static deprecated int SOLID;
+  }
+
+  public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context);
+  }
+
+  public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
+    ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
+  }
+
+  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
+    ctor public PlaybackControlsRowPresenter();
+    method public boolean areSecondaryActionsHidden();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public int getBackgroundColor();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method public void setBackgroundColor(int);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+    method public void setSecondaryActionsHidden(boolean);
+    method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean);
+    method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
+  }
+
+  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
+    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
+  }
+
+  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public PlaybackRowPresenter();
+    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+  }
+
+  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
+  }
+
+  public class PlaybackSeekDataProvider {
+    ctor public PlaybackSeekDataProvider();
+    method public long[] getSeekPositions();
+    method public void getThumbnail(int, android.support.v17.leanback.widget.PlaybackSeekDataProvider.ResultCallback);
+    method public void reset();
+  }
+
+  public static class PlaybackSeekDataProvider.ResultCallback {
+    ctor public PlaybackSeekDataProvider.ResultCallback();
+    method public void onThumbnailLoaded(android.graphics.Bitmap, int);
+  }
+
+  public abstract interface PlaybackSeekUi {
+    method public abstract void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public static class PlaybackSeekUi.Client {
+    ctor public PlaybackSeekUi.Client();
+    method public android.support.v17.leanback.widget.PlaybackSeekDataProvider getPlaybackSeekDataProvider();
+    method public boolean isSeekEnabled();
+    method public void onSeekFinished(boolean);
+    method public void onSeekPositionChanged(long);
+    method public void onSeekStarted();
+  }
+
+  public class PlaybackTransportRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
+    ctor public PlaybackTransportRowPresenter();
+    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method public float getDefaultSeekIncrement();
+    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
+    method public int getProgressColor();
+    method protected void onProgressBarClicked(android.support.v17.leanback.widget.PlaybackTransportRowPresenter.ViewHolder);
+    method public void setDefaultSeekIncrement(float);
+    method public void setDescriptionPresenter(android.support.v17.leanback.widget.Presenter);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setProgressColor(int);
+  }
+
+  public class PlaybackTransportRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder implements android.support.v17.leanback.widget.PlaybackSeekUi {
+    ctor public PlaybackTransportRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
+    method public final android.widget.TextView getCurrentPositionView();
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDescriptionViewHolder();
+    method public final android.widget.TextView getDurationView();
+    method protected void onSetCurrentPositionLabel(long);
+    method protected void onSetDurationLabel(long);
+    method public void setPlaybackSeekUiClient(android.support.v17.leanback.widget.PlaybackSeekUi.Client);
+  }
+
+  public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter();
+    method protected static void cancelAnimationsRecursive(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener);
+  }
+
+  public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
+    ctor public Presenter.ViewHolder(android.view.View);
+    method public final java.lang.Object getFacet(java.lang.Class<?>);
+    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
+    field public final android.view.View view;
+  }
+
+  public static abstract class Presenter.ViewHolderTask {
+    ctor public Presenter.ViewHolderTask();
+    method public void run(android.support.v17.leanback.widget.Presenter.ViewHolder);
+  }
+
+  public abstract class PresenterSelector {
+    ctor public PresenterSelector();
+    method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter[] getPresenters();
+  }
+
+  public abstract class PresenterSwitcher {
+    ctor public PresenterSwitcher();
+    method public void clear();
+    method public final android.view.ViewGroup getParentViewGroup();
+    method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector);
+    method protected abstract void insertView(android.view.View);
+    method protected void onViewSelected(android.view.View);
+    method public void select(java.lang.Object);
+    method protected void showView(android.view.View, boolean);
+    method public void unselect();
+  }
+
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
+    ctor public RecyclerViewParallax();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
+    method public float getMaxValue();
+    method public android.support.v7.widget.RecyclerView getRecyclerView();
+    method public void setRecyclerView(android.support.v7.widget.RecyclerView);
+  }
+
+  public static final class RecyclerViewParallax.ChildPositionProperty extends android.support.v17.leanback.widget.Parallax.IntProperty {
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty adapterPosition(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty fraction(float);
+    method public int getAdapterPosition();
+    method public float getFraction();
+    method public int getOffset();
+    method public int getViewId();
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty offset(int);
+    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty viewId(int);
+  }
+
+  public class Row {
+    ctor public Row(long, android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row(android.support.v17.leanback.widget.HeaderItem);
+    ctor public Row();
+    method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem();
+    method public final long getId();
+    method public boolean isRenderedAsRowView();
+    method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem);
+    method public final void setId(long);
+  }
+
+  public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowHeaderPresenter();
+    method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint);
+    method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public boolean isNullItemVisibilityGone();
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setNullItemVisibilityGone(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float);
+  }
+
+  public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowHeaderPresenter.ViewHolder(android.view.View);
+    method public final float getSelectLevel();
+  }
+
+  public final class RowHeaderView extends android.widget.TextView {
+    ctor public RowHeaderView(android.content.Context);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet);
+    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public RowPresenter();
+    method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
+    method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter();
+    method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final boolean getSelectEffectEnabled();
+    method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final int getSyncActivatePolicy();
+    method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected boolean isClippingChildren();
+    method public boolean isUsingDefaultSelectEffect();
+    method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object);
+    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+    method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
+    method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter);
+    method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
+    method public final void setSelectEffectEnabled(boolean);
+    method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float);
+    method public final void setSyncActivatePolicy(int);
+    field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1
+    field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3
+    field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2
+  }
+
+  public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public RowPresenter.ViewHolder(android.view.View);
+    method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public android.view.View.OnKeyListener getOnKeyListener();
+    method public final android.support.v17.leanback.widget.Row getRow();
+    method public final java.lang.Object getRowObject();
+    method public final float getSelectLevel();
+    method public java.lang.Object getSelectedItem();
+    method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
+    method public final boolean isExpanded();
+    method public final boolean isSelected();
+    method public final void setActivated(boolean);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
+    method public void setOnKeyListener(android.view.View.OnKeyListener);
+    method public final void syncActivatedStatus(android.view.View);
+    field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer;
+  }
+
+  public class SearchBar extends android.widget.RelativeLayout {
+    ctor public SearchBar(android.content.Context);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet);
+    ctor public SearchBar(android.content.Context, android.util.AttributeSet, int);
+    method public void displayCompletions(java.util.List<java.lang.String>);
+    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public java.lang.CharSequence getHint();
+    method public java.lang.String getTitle();
+    method public boolean isRecognizing();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
+    method public void setSearchQuery(java.lang.String);
+    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
+    method public void setSpeechRecognizer(android.speech.SpeechRecognizer);
+    method public void setTitle(java.lang.String);
+    method public void startRecognition();
+    method public void stopRecognition();
+  }
+
+  public static abstract interface SearchBar.SearchBarListener {
+    method public abstract void onKeyboardDismiss(java.lang.String);
+    method public abstract void onSearchQueryChange(java.lang.String);
+    method public abstract void onSearchQuerySubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchBar.SearchBarPermissionListener {
+    method public abstract void requestAudioPermission();
+  }
+
+  public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView {
+    ctor public SearchEditText(android.content.Context);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet);
+    ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener);
+  }
+
+  public static abstract interface SearchEditText.OnKeyboardDismissListener {
+    method public abstract void onKeyboardDismiss();
+  }
+
+  public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableOrbColorAnimation(boolean);
+    method public int getOrbColor();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors();
+    method public android.graphics.drawable.Drawable getOrbIcon();
+    method public void onClick(android.view.View);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener);
+    method public void setOrbColor(int);
+    method public deprecated void setOrbColor(int, int);
+    method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
+  }
+
+  public static class SearchOrbView.Colors {
+    ctor public SearchOrbView.Colors(int);
+    ctor public SearchOrbView.Colors(int, int);
+    ctor public SearchOrbView.Colors(int, int, int);
+    method public static int getBrightColor(int);
+    field public int brightColor;
+    field public int color;
+    field public int iconColor;
+  }
+
+  public class SectionRow extends android.support.v17.leanback.widget.Row {
+    ctor public SectionRow(android.support.v17.leanback.widget.HeaderItem);
+    ctor public SectionRow(long, java.lang.String);
+    ctor public SectionRow(java.lang.String);
+    method public final boolean isRenderedAsRowView();
+  }
+
+  public class ShadowOverlayContainer extends android.widget.FrameLayout {
+    ctor public ShadowOverlayContainer(android.content.Context);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
+    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
+    method public int getShadowType();
+    method public android.view.View getWrappedView();
+    method public deprecated void initialize(boolean, boolean);
+    method public deprecated void initialize(boolean, boolean, boolean);
+    method public static void prepareParentForShadow(android.view.ViewGroup);
+    method public void setOverlayColor(int);
+    method public void setShadowFocusLevel(float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsShadow();
+    method public void useDynamicShadow();
+    method public void useDynamicShadow(float, float);
+    method public void useStaticShadow();
+    method public void wrap(android.view.View);
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public final class ShadowOverlayHelper {
+    method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context);
+    method public int getShadowType();
+    method public boolean needsOverlay();
+    method public boolean needsRoundedCorner();
+    method public boolean needsWrapper();
+    method public void onViewCreated(android.view.View);
+    method public void prepareParentForShadow(android.view.ViewGroup);
+    method public static void setNoneWrapperOverlayColor(android.view.View, int);
+    method public static void setNoneWrapperShadowFocusLevel(android.view.View, float);
+    method public void setOverlayColor(android.view.View, int);
+    method public void setShadowFocusLevel(android.view.View, float);
+    method public static boolean supportsDynamicShadow();
+    method public static boolean supportsForeground();
+    method public static boolean supportsRoundedCorner();
+    method public static boolean supportsShadow();
+    field public static final int SHADOW_DYNAMIC = 3; // 0x3
+    field public static final int SHADOW_NONE = 1; // 0x1
+    field public static final int SHADOW_STATIC = 2; // 0x2
+  }
+
+  public static final class ShadowOverlayHelper.Builder {
+    ctor public ShadowOverlayHelper.Builder();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options);
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean);
+  }
+
+  public static final class ShadowOverlayHelper.Options {
+    ctor public ShadowOverlayHelper.Options();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float);
+    method public final float getDynamicShadowFocusedZ();
+    method public final float getDynamicShadowUnfocusedZ();
+    method public final int getRoundedCornerRadius();
+    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int);
+    field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT;
+  }
+
+  public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
+    ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter);
+    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
+  }
+
+  public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
+    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
+    ctor public SparseArrayObjectAdapter();
+    method public void clear(int);
+    method public void clear();
+    method public java.lang.Object get(int);
+    method public int indexOf(java.lang.Object);
+    method public int indexOf(int);
+    method public java.lang.Object lookup(int);
+    method public void notifyArrayItemRangeChanged(int, int);
+    method public void set(int, java.lang.Object);
+    method public int size();
+  }
+
+  public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView {
+    ctor public SpeechOrbView(android.content.Context);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
+    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSoundLevel(int);
+    method public void showListening();
+    method public void showNotListening();
+  }
+
+  public abstract interface SpeechRecognitionCallback {
+    method public abstract void recognizeSpeech();
+  }
+
+   class StreamingTextView extends android.widget.EditText {
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet);
+    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int);
+    method public static boolean isLayoutRtl(android.view.View);
+    method public void reset();
+    method public void setFinalRecognizedText(java.lang.CharSequence);
+    method public void updateRecognizedText(java.lang.String, java.lang.String);
+    method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>);
+  }
+
+  public class TitleHelper {
+    ctor public TitleHelper(android.view.ViewGroup, android.view.View);
+    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
+    method public android.view.ViewGroup getSceneRoot();
+    method public android.view.View getTitleView();
+    method public void showTitle(boolean);
+  }
+
+  public class TitleView extends android.widget.FrameLayout implements android.support.v17.leanback.widget.TitleViewAdapter.Provider {
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet, int);
+    method public void enableAnimation(boolean);
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+  }
+
+  public abstract class TitleViewAdapter {
+    ctor public TitleViewAdapter();
+    method public android.graphics.drawable.Drawable getBadgeDrawable();
+    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
+    method public abstract android.view.View getSearchAffordanceView();
+    method public java.lang.CharSequence getTitle();
+    method public void setAnimationEnabled(boolean);
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(java.lang.CharSequence);
+    method public void updateComponentsVisibility(int);
+    field public static final int BRANDING_VIEW_VISIBLE = 2; // 0x2
+    field public static final int FULL_VIEW_VISIBLE = 6; // 0x6
+    field public static final int SEARCH_VIEW_VISIBLE = 4; // 0x4
+  }
+
+  public static abstract interface TitleViewAdapter.Provider {
+    method public abstract android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+  }
+
+  public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter {
+    ctor public VerticalGridPresenter();
+    ctor public VerticalGridPresenter(int);
+    ctor public VerticalGridPresenter(int, boolean);
+    method public final boolean areChildRoundedCornersEnabled();
+    method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup);
+    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
+    method public final void enableChildRoundedCorners(boolean);
+    method public final int getFocusZoomFactor();
+    method public final boolean getKeepChildForeground();
+    method public int getNumberOfColumns();
+    method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
+    method public final boolean getShadowEnabled();
+    method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder);
+    method public final boolean isFocusDimmerUsed();
+    method public boolean isUsingDefaultShadow();
+    method public boolean isUsingZOrder(android.content.Context);
+    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
+    method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
+    method public final void setKeepChildForeground(boolean);
+    method public void setNumberOfColumns(int);
+    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
+    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public final void setShadowEnabled(boolean);
+  }
+
+  public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
+    ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView);
+    method public android.support.v17.leanback.widget.VerticalGridView getGridView();
+  }
+
+  public class VerticalGridView extends android.support.v17.leanback.widget.BaseGridView {
+    ctor public VerticalGridView(android.content.Context);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet);
+    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int);
+    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
+    method public void setColumnWidth(int);
+    method public void setNumColumns(int);
+  }
+
+  public abstract interface ViewHolderTask {
+    method public abstract void run(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+}
+
+package android.support.v17.leanback.widget.picker {
+
+  public class Picker extends android.widget.FrameLayout {
+    ctor public Picker(android.content.Context, android.util.AttributeSet, int);
+    method public void addOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public float getActivatedVisibleItemCount();
+    method public android.support.v17.leanback.widget.picker.PickerColumn getColumnAt(int);
+    method public int getColumnsCount();
+    method protected int getPickerItemHeightPixels();
+    method public final int getPickerItemLayoutId();
+    method public final int getPickerItemTextViewId();
+    method public int getSelectedColumn();
+    method public final java.lang.CharSequence getSeparator();
+    method public float getVisibleItemCount();
+    method public void onColumnValueChanged(int, int);
+    method public void removeOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
+    method public void setActivatedVisibleItemCount(float);
+    method public void setColumnAt(int, android.support.v17.leanback.widget.picker.PickerColumn);
+    method public void setColumnValue(int, int, boolean);
+    method public void setColumns(java.util.List<android.support.v17.leanback.widget.picker.PickerColumn>);
+    method public final void setPickerItemTextViewId(int);
+    method public void setSelectedColumn(int);
+    method public final void setSeparator(java.lang.CharSequence);
+    method public void setVisibleItemCount(float);
+  }
+
+  public static abstract interface Picker.PickerValueListener {
+    method public abstract void onValueChanged(android.support.v17.leanback.widget.picker.Picker, int);
+  }
+
+  public class PickerColumn {
+    ctor public PickerColumn();
+    method public int getCount();
+    method public int getCurrentValue();
+    method public java.lang.CharSequence getLabelFor(int);
+    method public java.lang.String getLabelFormat();
+    method public int getMaxValue();
+    method public int getMinValue();
+    method public java.lang.CharSequence[] getStaticLabels();
+    method public void setCurrentValue(int);
+    method public void setLabelFormat(java.lang.String);
+    method public void setMaxValue(int);
+    method public void setMinValue(int);
+    method public void setStaticLabels(java.lang.CharSequence[]);
+  }
+
+  public class TimePicker extends android.support.v17.leanback.widget.picker.Picker {
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet);
+    ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24Hour();
+    method public boolean isPm();
+    method public void setHour(int);
+    method public void setIs24Hour(boolean);
+    method public void setMinute(int);
+  }
+
+}
+
+package android.support.v17.preference {
+
+  public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment {
+    ctor public BaseLeanbackPreferenceFragment();
+  }
+
+  public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment {
+    ctor public LeanbackListPreferenceDialogFragment();
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String);
+    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String);
+    method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter();
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence);
+    method public int getItemCount();
+    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
+    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener {
+    ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener);
+    method public android.view.ViewGroup getContainer();
+    method public android.widget.TextView getTitleView();
+    method public android.widget.Checkable getWidgetView();
+    method public void onClick(android.view.View);
+  }
+
+  public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
+    method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
+  }
+
+  public class LeanbackPreferenceDialogFragment extends android.app.Fragment {
+    ctor public LeanbackPreferenceDialogFragment();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    field public static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
+    ctor public LeanbackPreferenceFragment();
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
+    ctor public LeanbackSettingsFragment();
+    method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
+    method public abstract void onPreferenceStartInitialScreen();
+    method public void startImmersiveFragment(android.app.Fragment);
+    method public void startPreferenceFragment(android.app.Fragment);
+  }
+
+}
+
+package android.support.v4.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static java.lang.String capabilityToString(int);
+    method public static java.lang.String feedbackTypeToString(int);
+    method public static java.lang.String flagToString(int);
+    method public static deprecated boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static deprecated java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static java.lang.String loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final deprecated int DEFAULT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package android.support.v4.app {
+
+  public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int);
+    method public boolean isDrawerIndicatorEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void syncState();
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract deprecated interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class ActivityCompat extends android.support.v4.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri getReferrer(android.app.Activity);
+    method public static boolean invalidateOptionsMenu(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void requestPermissions(android.app.Activity, java.lang.String[], int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect getLaunchBounds();
+    method public static android.support.v4.app.ActivityOptionsCompat makeBasic();
+    method public static android.support.v4.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
+    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...);
+    method public static android.support.v4.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public android.support.v4.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect);
+    method public android.os.Bundle toBundle();
+    method public void update(android.support.v4.app.ActivityOptionsCompat);
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  public class AppLaunchChecker {
+    ctor public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
+    method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
+    method public static java.lang.String permissionToOp(java.lang.String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String);
+    method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder);
+  }
+
+  public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+    ctor public DialogFragment();
+    method public void dismiss();
+    method public void dismissAllowingStateLoss();
+    method public android.app.Dialog getDialog();
+    method public boolean getShowsDialog();
+    method public int getTheme();
+    method public boolean isCancelable();
+    method public void onCancel(android.content.DialogInterface);
+    method public android.app.Dialog onCreateDialog(android.os.Bundle);
+    method public void onDismiss(android.content.DialogInterface);
+    method public void setCancelable(boolean);
+    method public void setShowsDialog(boolean);
+    method public void setStyle(int, int);
+    method public void show(android.support.v4.app.FragmentManager, java.lang.String);
+    method public int show(android.support.v4.app.FragmentTransaction, java.lang.String);
+    field public static final int STYLE_NORMAL = 0; // 0x0
+    field public static final int STYLE_NO_FRAME = 2; // 0x2
+    field public static final int STYLE_NO_INPUT = 3; // 0x3
+    field public static final int STYLE_NO_TITLE = 1; // 0x1
+  }
+
+  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+    ctor public Fragment();
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final boolean equals(java.lang.Object);
+    method public final android.support.v4.app.FragmentActivity getActivity();
+    method public boolean getAllowEnterTransitionOverlap();
+    method public boolean getAllowReturnTransitionOverlap();
+    method public final android.os.Bundle getArguments();
+    method public final android.support.v4.app.FragmentManager getChildFragmentManager();
+    method public android.content.Context getContext();
+    method public java.lang.Object getEnterTransition();
+    method public java.lang.Object getExitTransition();
+    method public final android.support.v4.app.FragmentManager getFragmentManager();
+    method public final java.lang.Object getHost();
+    method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
+    method public android.support.v4.app.LoaderManager getLoaderManager();
+    method public final android.support.v4.app.Fragment getParentFragment();
+    method public java.lang.Object getReenterTransition();
+    method public final android.content.res.Resources getResources();
+    method public final boolean getRetainInstance();
+    method public java.lang.Object getReturnTransition();
+    method public java.lang.Object getSharedElementEnterTransition();
+    method public java.lang.Object getSharedElementReturnTransition();
+    method public final java.lang.String getString(int);
+    method public final java.lang.String getString(int, java.lang.Object...);
+    method public final java.lang.String getTag();
+    method public final android.support.v4.app.Fragment getTargetFragment();
+    method public final int getTargetRequestCode();
+    method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
+    method public android.view.View getView();
+    method public final int hashCode();
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String);
+    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public final boolean isAdded();
+    method public final boolean isDetached();
+    method public final boolean isHidden();
+    method public final boolean isInLayout();
+    method public final boolean isRemoving();
+    method public final boolean isResumed();
+    method public final boolean isStateSaved();
+    method public final boolean isVisible();
+    method public void onActivityCreated(android.os.Bundle);
+    method public void onActivityResult(int, int, android.content.Intent);
+    method public void onAttach(android.content.Context);
+    method public deprecated void onAttach(android.app.Activity);
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public boolean onContextItemSelected(android.view.MenuItem);
+    method public void onCreate(android.os.Bundle);
+    method public android.view.animation.Animation onCreateAnimation(int, boolean, int);
+    method public android.animation.Animator onCreateAnimator(int, boolean, int);
+    method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
+    method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDestroy();
+    method public void onDestroyOptionsMenu();
+    method public void onDestroyView();
+    method public void onDetach();
+    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
+    method public void onHiddenChanged(boolean);
+    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
+    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
+    method public void onLowMemory();
+    method public void onMultiWindowModeChanged(boolean);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void onOptionsMenuClosed(android.view.Menu);
+    method public void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
+    method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
+    method public void onResume();
+    method public void onSaveInstanceState(android.os.Bundle);
+    method public void onStart();
+    method public void onStop();
+    method public void onViewCreated(android.view.View, android.os.Bundle);
+    method public void onViewStateRestored(android.os.Bundle);
+    method public void postponeEnterTransition();
+    method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
+    method public void setAllowEnterTransitionOverlap(boolean);
+    method public void setAllowReturnTransitionOverlap(boolean);
+    method public void setArguments(android.os.Bundle);
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setEnterTransition(java.lang.Object);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitTransition(java.lang.Object);
+    method public void setHasOptionsMenu(boolean);
+    method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState);
+    method public void setMenuVisibility(boolean);
+    method public void setReenterTransition(java.lang.Object);
+    method public void setRetainInstance(boolean);
+    method public void setReturnTransition(java.lang.Object);
+    method public void setSharedElementEnterTransition(java.lang.Object);
+    method public void setSharedElementReturnTransition(java.lang.Object);
+    method public void setTargetFragment(android.support.v4.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
+    method public void startActivity(android.content.Intent);
+    method public void startActivity(android.content.Intent, android.os.Bundle);
+    method public void startActivityForResult(android.content.Intent, int);
+    method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
+    method public void unregisterForContextMenu(android.view.View);
+  }
+
+  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+    ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
+  }
+
+  public static class Fragment.SavedState implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR;
+  }
+
+  public class FragmentActivity extends android.app.Activity implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback {
+    ctor public FragmentActivity();
+    method public java.lang.Object getLastCustomNonConfigurationInstance();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void onAttachFragment(android.support.v4.app.Fragment);
+    method public void onMultiWindowModeChanged(boolean);
+    method public void onPictureInPictureModeChanged(boolean);
+    method protected void onResumeFragments();
+    method public java.lang.Object onRetainCustomNonConfigurationInstance();
+    method public final java.lang.Object onRetainNonConfigurationInstance();
+    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void supportFinishAfterTransition();
+    method public deprecated void supportInvalidateOptionsMenu();
+    method public void supportPostponeEnterTransition();
+    method public void supportStartPostponedEnterTransition();
+    method public final void validateRequestPermissionsRequestCode(int);
+  }
+
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method public android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
+    method public abstract android.view.View onFindViewById(int);
+    method public abstract boolean onHasView();
+  }
+
+  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 void dispatchActivityCreated();
+    method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method public void dispatchLowMemory();
+    method public void dispatchMultiWindowModeChanged(boolean);
+    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method public void dispatchPictureInPictureModeChanged(boolean);
+    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method public void doLoaderDestroy();
+    method public void doLoaderRetain();
+    method public void doLoaderStart();
+    method public void doLoaderStop(boolean);
+    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public boolean execPendingActions();
+    method public android.support.v4.app.Fragment findFragmentByWho(java.lang.String);
+    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
+    method public int getActiveFragmentsCount();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public void reportLoaderStart();
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig);
+    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
+    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
+    method public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.support.v4.app.Fragment> retainNonConfig();
+    method public android.os.Parcelable saveAllState();
+  }
+
+  public abstract class FragmentHostCallback<E> extends android.support.v4.app.FragmentContainer {
+    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.view.View onFindViewById(int);
+    method public abstract E onGetHost();
+    method public android.view.LayoutInflater onGetLayoutInflater();
+    method public int onGetWindowAnimations();
+    method public boolean onHasView();
+    method public boolean onHasWindowAnimations();
+    method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int);
+    method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment);
+    method public boolean onShouldShowRequestPermissionRationale(java.lang.String);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void onSupportInvalidateOptionsMenu();
+  }
+
+  public abstract class FragmentManager {
+    ctor public FragmentManager();
+    method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.FragmentTransaction beginTransaction();
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract boolean executePendingTransactions();
+    method public abstract android.support.v4.app.Fragment findFragmentById(int);
+    method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String);
+    method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+    method public abstract int getBackStackEntryCount();
+    method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String);
+    method public abstract java.util.List<android.support.v4.app.Fragment> getFragments();
+    method public abstract android.support.v4.app.Fragment getPrimaryNavigationFragment();
+    method public abstract boolean isDestroyed();
+    method public abstract boolean isStateSaved();
+    method public abstract void popBackStack();
+    method public abstract void popBackStack(java.lang.String, int);
+    method public abstract void popBackStack(int, int);
+    method public abstract boolean popBackStackImmediate();
+    method public abstract boolean popBackStackImmediate(java.lang.String, int);
+    method public abstract boolean popBackStackImmediate(int, int);
+    method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+    method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
+    method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
+    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+  }
+
+  public static abstract interface FragmentManager.BackStackEntry {
+    method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
+    method public abstract int getBreadCrumbShortTitleRes();
+    method public abstract java.lang.CharSequence getBreadCrumbTitle();
+    method public abstract int getBreadCrumbTitleRes();
+    method public abstract int getId();
+    method public abstract java.lang.String getName();
+  }
+
+  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentPreCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+  }
+
+  public static abstract interface FragmentManager.OnBackStackChangedListener {
+    method public abstract void onBackStackChanged();
+  }
+
+  public class FragmentManagerNonConfig {
+  }
+
+  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public long getItemId(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager);
+    method public abstract android.support.v4.app.Fragment getItem(int);
+    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  }
+
+  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public FragmentTabHost(android.content.Context);
+    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method public void onTabChanged(java.lang.String);
+    method public deprecated void setup();
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager);
+    method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int);
+  }
+
+  public abstract class FragmentTransaction {
+    ctor public FragmentTransaction();
+    method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment);
+    method public abstract int commit();
+    method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
+    method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack();
+    method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
+    method public abstract boolean isAddToBackStackAllowed();
+    method public abstract boolean isEmpty();
+    method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction runOnCommit(java.lang.Runnable);
+    method public abstract deprecated android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
+    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int);
+    method public abstract android.support.v4.app.FragmentTransaction setPrimaryNavigationFragment(android.support.v4.app.Fragment);
+    method public abstract android.support.v4.app.FragmentTransaction setReorderingAllowed(boolean);
+    method public abstract android.support.v4.app.FragmentTransaction setTransition(int);
+    method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int);
+    method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment);
+    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+    field public static final int TRANSIT_NONE = 0; // 0x0
+    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray[] getMetrics();
+    method public android.util.SparseIntArray[] remove(android.app.Activity);
+    method public android.util.SparseIntArray[] reset();
+    method public android.util.SparseIntArray[] stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public abstract class JobIntentService extends android.app.Service {
+    ctor public JobIntentService();
+    method public static void enqueueWork(android.content.Context, java.lang.Class, int, android.content.Intent);
+    method public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method public boolean isStopped();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method protected abstract void onHandleWork(android.content.Intent);
+    method public boolean onStopCurrentWork();
+    method public void setInterruptIfStopped(boolean);
+  }
+
+  public class ListFragment extends android.support.v4.app.Fragment {
+    ctor public ListFragment();
+    method public android.widget.ListAdapter getListAdapter();
+    method public android.widget.ListView getListView();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+    method public void setEmptyText(java.lang.CharSequence);
+    method public void setListAdapter(android.widget.ListAdapter);
+    method public void setListShown(boolean);
+    method public void setListShownNoAnimation(boolean);
+    method public void setSelection(int);
+  }
+
+  public abstract class LoaderManager {
+    ctor public LoaderManager();
+    method public abstract void destroyLoader(int);
+    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public static void enableDebugLogging(boolean);
+    method public abstract <D> android.support.v4.content.Loader<D> getLoader(int);
+    method public boolean hasRunningLoaders();
+    method public abstract <D> android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+    method public abstract <D> android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
+  }
+
+  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+    method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle);
+    method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D);
+    method public abstract void onLoaderReset(android.support.v4.content.Loader<D>);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static java.lang.String getParentActivityName(android.app.Activity);
+    method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationCompat {
+    ctor public NotificationCompat();
+    method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static java.lang.String getCategory(android.app.Notification);
+    method public static java.lang.String getChannelId(android.app.Notification);
+    method public static android.os.Bundle getExtras(android.app.Notification);
+    method public static java.lang.String getGroup(android.app.Notification);
+    method public static int getGroupAlertBehavior(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static java.lang.String getShortcutId(android.app.Notification);
+    method public static java.lang.String getSortKey(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+    field public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+    field public static final java.lang.String EXTRA_PICTURE = "android.picture";
+    field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
+    field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
+    field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
+    field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
+    field public static final java.lang.String EXTRA_TEXT = "android.text";
+    field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_TITLE = "android.title";
+    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.app.PendingIntent getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public android.support.v4.app.RemoteInput[] getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public android.support.v4.app.RemoteInput[] getRemoteInputs();
+    method public java.lang.CharSequence getTitle();
+    field public android.app.PendingIntent actionIntent;
+    field public int icon;
+    field public java.lang.CharSequence title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
+    ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput);
+    method public android.support.v4.app.NotificationCompat.Action build();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+  }
+
+  public static abstract interface NotificationCompat.Action.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
+    method public java.lang.CharSequence getCancelLabel();
+    method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method public java.lang.CharSequence getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor public NotificationCompat.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated NotificationCompat.Builder(android.content.Context);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String);
+    method public android.app.Notification build();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method public deprecated android.app.Notification getNotification();
+    method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setChannelId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setColor(int);
+    method public android.support.v4.app.NotificationCompat.Builder setColorized(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setDefaults(int);
+    method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+    method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setNumber(int);
+    method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPriority(int);
+    method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.support.v4.app.NotificationCompat.Builder setShortcutId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int);
+    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri);
+    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int);
+    method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style);
+    method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.support.v4.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]);
+    method public android.support.v4.app.NotificationCompat.Builder setVisibility(int);
+    method public android.support.v4.app.NotificationCompat.Builder setWhen(long);
+    field public java.util.ArrayList<java.lang.String> mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.support.v4.app.NotificationCompat.CarExtender setColor(int);
+    method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation);
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.support.v4.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
+  public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static abstract interface NotificationCompat.Extender {
+    method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
+    method public void addCompatExtras(android.os.Bundle);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(android.support.v4.app.NotificationCompat.MessagingStyle.Message);
+    method public static android.support.v4.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.support.v4.app.NotificationCompat.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.support.v4.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+  }
+
+  public static abstract class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification build();
+    method public void setBuilder(android.support.v4.app.NotificationCompat.Builder);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearActions();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clearPages();
+    method public android.support.v4.app.NotificationCompat.WearableExtender clone();
+    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
+    method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public java.lang.String getDismissalId();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintAmbientBigPicture();
+    method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintHideIcon();
+    method public int getHintScreenTimeout();
+    method public boolean getHintShowBackgroundOnly();
+    method public java.util.List<android.app.Notification> getPages();
+    method public boolean getStartScrollBottom();
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setBridgeTag(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDismissalId(java.lang.String);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field public static final int SIZE_LARGE = 4; // 0x4
+    field public static final int SIZE_MEDIUM = 3; // 0x3
+    field public static final int SIZE_SMALL = 2; // 0x2
+    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(java.lang.String, int, java.lang.String);
+    method public abstract void cancelAll(java.lang.String);
+    method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(java.lang.String, int);
+    method public void cancelAll();
+    method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public void notify(int, android.app.Notification);
+    method public void notify(java.lang.String, int, android.app.Notification);
+    field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public final class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput {
+    method public static void addDataResultToIntent(android.support.v4.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
+    method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
+    method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getResultKey();
+    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
+    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(java.lang.String);
+    method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public android.support.v4.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public android.support.v4.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
+    method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+    method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+  }
+
+   deprecated class RemoteInputCompatBase {
+  }
+
+  public static abstract class RemoteInputCompatBase.RemoteInput {
+    ctor public RemoteInputCompatBase.RemoteInput();
+    method protected abstract boolean getAllowFreeFormInput();
+    method protected abstract java.util.Set<java.lang.String> getAllowedDataTypes();
+    method protected abstract java.lang.CharSequence[] getChoices();
+    method protected abstract android.os.Bundle getExtras();
+    method protected abstract java.lang.CharSequence getLabel();
+    method protected abstract java.lang.String getResultKey();
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName getCallingActivity(android.app.Activity);
+    method public static java.lang.String getCallingPackage(android.app.Activity);
+    field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence);
+    method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName getCallingActivity();
+    method public android.graphics.drawable.Drawable getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable getCallingApplicationIcon();
+    method public java.lang.CharSequence getCallingApplicationLabel();
+    method public java.lang.String getCallingPackage();
+    method public java.lang.String[] getEmailBcc();
+    method public java.lang.String[] getEmailCc();
+    method public java.lang.String[] getEmailTo();
+    method public java.lang.String getHtmlText();
+    method public android.net.Uri getStream();
+    method public android.net.Uri getStream(int);
+    method public int getStreamCount();
+    method public java.lang.String getSubject();
+    method public java.lang.CharSequence getText();
+    method public java.lang.String getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
+    method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable);
+    method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
+    method public void onRejectSharedElements(java.util.List<android.view.View>);
+    method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String>, java.util.List<android.view.View>, android.support.v4.app.SharedElementCallback.OnSharedElementsReadyListener);
+  }
+
+  public static abstract interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public abstract void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable {
+    method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
+    method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public static android.support.v4.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent editIntentAt(int);
+    method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context);
+    method public deprecated android.content.Intent getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent[] getIntents();
+    method public android.app.PendingIntent getPendingIntent(int, int);
+    method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle);
+    method public deprecated java.util.Iterator<android.content.Intent> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle);
+  }
+
+  public static abstract interface TaskStackBuilder.SupportParentable {
+    method public abstract android.content.Intent getSupportParentActivityIntent();
+  }
+
+}
+
+package android.support.v4.content {
+
+  public abstract class AsyncTaskLoader<D> extends android.support.v4.content.Loader {
+    ctor public AsyncTaskLoader(android.content.Context);
+    method public void cancelLoadInBackground();
+    method public boolean isLoadInBackgroundCanceled();
+    method public abstract D loadInBackground();
+    method public void onCanceled(D);
+    method protected D onLoadInBackground();
+    method public void setUpdateThrottle(long);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    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 java.io.File getDataDir(android.content.Context);
+    method public static final 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[] getObbDirs(android.content.Context);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class CursorLoader extends android.support.v4.content.AsyncTaskLoader {
+    ctor public CursorLoader(android.content.Context);
+    ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public void deliverResult(android.database.Cursor);
+    method public java.lang.String[] getProjection();
+    method public java.lang.String getSelection();
+    method public java.lang.String[] getSelectionArgs();
+    method public java.lang.String getSortOrder();
+    method public android.net.Uri getUri();
+    method public android.database.Cursor loadInBackground();
+    method public void onCanceled(android.database.Cursor);
+    method public void setProjection(java.lang.String[]);
+    method public void setSelection(java.lang.String);
+    method public void setSelectionArgs(java.lang.String[]);
+    method public void setSortOrder(java.lang.String);
+    method public void setUri(android.net.Uri);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File);
+    method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
+  public final class IntentCompat {
+    method public static deprecated android.content.Intent makeMainActivity(android.content.ComponentName);
+    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
+    method public static deprecated android.content.Intent makeRestartActivityTask(android.content.ComponentName);
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
+    field public static final deprecated java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
+    field public static final deprecated java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final java.lang.String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final deprecated int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
+    field public static final deprecated int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
+  }
+
+  public class Loader<D> {
+    ctor public Loader(android.content.Context);
+    method public void abandon();
+    method public boolean cancelLoad();
+    method public void commitContentChanged();
+    method public java.lang.String dataToString(D);
+    method public void deliverCancellation();
+    method public void deliverResult(D);
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void forceLoad();
+    method public android.content.Context getContext();
+    method public int getId();
+    method public boolean isAbandoned();
+    method public boolean isReset();
+    method public boolean isStarted();
+    method protected void onAbandon();
+    method protected boolean onCancelLoad();
+    method public void onContentChanged();
+    method protected void onForceLoad();
+    method protected void onReset();
+    method protected void onStartLoading();
+    method protected void onStopLoading();
+    method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+    method public void reset();
+    method public void rollbackContentChanged();
+    method public final void startLoading();
+    method public void stopLoading();
+    method public boolean takeContentChanged();
+    method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>);
+    method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
+  }
+
+  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+    ctor public Loader.ForceLoadContentObserver();
+  }
+
+  public static abstract interface Loader.OnLoadCanceledListener<D> {
+    method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>);
+  }
+
+  public static abstract interface Loader.OnLoadCompleteListener<D> {
+    method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D);
+  }
+
+  public final class LocalBroadcastManager {
+    method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context);
+    method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+    method public boolean sendBroadcast(android.content.Intent);
+    method public void sendBroadcastSync(android.content.Intent);
+    method public void unregisterReceiver(android.content.BroadcastReceiver);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(java.lang.String, java.lang.String);
+    method public static java.lang.String matches(java.lang.String, java.lang.String[]);
+    method public static java.lang.String matches(java.lang.String[], java.lang.String);
+    method public static java.lang.String[] matchesMany(java.lang.String[], java.lang.String);
+  }
+
+  public final deprecated class ParallelExecutorCompat {
+    method public static deprecated java.util.concurrent.Executor getParallelExecutor();
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String);
+    method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String);
+    method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String);
+    method public static int checkSelfPermission(android.content.Context, java.lang.String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  public final class SharedPreferencesCompat {
+  }
+
+  public static final class SharedPreferencesCompat.EditorCompat {
+    method public void apply(android.content.SharedPreferences.Editor);
+    method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance();
+  }
+
+  public abstract deprecated class WakefulBroadcastReceiver extends android.content.BroadcastReceiver {
+    ctor public WakefulBroadcastReceiver();
+    method public static boolean completeWakefulIntent(android.content.Intent);
+    method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent);
+  }
+
+}
+
+package android.support.v4.content.pm {
+
+  public final class ActivityInfoCompat {
+    field public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName getActivity();
+    method public java.lang.CharSequence getDisabledMessage();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent[] getIntents();
+    method public java.lang.CharSequence getLongLabel();
+    method public java.lang.CharSequence getShortLabel();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, java.lang.String);
+    method public android.support.v4.content.pm.ShortcutInfoCompat build();
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(java.lang.CharSequence);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.graphics.Bitmap);
+    method public deprecated android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(int);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIcon(android.support.v4.graphics.drawable.IconCompat);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent[]);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setLongLabel(java.lang.CharSequence);
+    method public android.support.v4.content.pm.ShortcutInfoCompat.Builder setShortLabel(java.lang.CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean requestPinShortcut(android.content.Context, android.support.v4.content.pm.ShortcutInfoCompat, android.content.IntentSender);
+  }
+
+}
+
+package android.support.v4.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+    method public static deprecated int getScreenHeightDp(android.content.res.Resources);
+    method public static deprecated int getScreenWidthDp(android.content.res.Resources);
+    method public static deprecated int getSmallestScreenWidthDp(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.Typeface getFont(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
+  }
+
+}
+
+package android.support.v4.database {
+
+  public final class DatabaseUtilsCompat {
+    method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]);
+    method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public final class ColorUtils {
+    method public static int HSLToColor(float[]);
+    method public static int LABToColor(double, double, double);
+    method public static void LABToXYZ(double, double, double, double[]);
+    method public static void RGBToHSL(int, int, int, float[]);
+    method public static void RGBToLAB(int, int, int, double[]);
+    method public static void RGBToXYZ(int, int, int, double[]);
+    method public static int XYZToColor(double, double, double);
+    method public static void XYZToLAB(double, double, double, double[]);
+    method public static int blendARGB(int, int, float);
+    method public static void blendHSL(float[], float[], float, float[]);
+    method public static void blendLAB(double[], double[], double, double[]);
+    method public static double calculateContrast(int, int);
+    method public static double calculateLuminance(int);
+    method public static int calculateMinimumAlpha(int, int, float);
+    method public static void colorToHSL(int, float[]);
+    method public static void colorToLAB(int, double[]);
+    method public static void colorToXYZ(int, double[]);
+    method public static int compositeColors(int, int);
+    method public static double distanceEuclidean(double[], double[]);
+    method public static int setAlphaComponent(int, int);
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, java.lang.String);
+  }
+
+}
+
+package android.support.v4.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  public class IconCompat {
+    method public static android.support.v4.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(java.lang.String);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithData(byte[], int, int);
+    method public static android.support.v4.graphics.drawable.IconCompat createWithResource(android.content.Context, int);
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String);
+    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package android.support.v4.hardware.display {
+
+  public abstract class DisplayManagerCompat {
+    method public abstract android.view.Display getDisplay(int);
+    method public abstract android.view.Display[] getDisplays();
+    method public abstract android.view.Display[] getDisplays(java.lang.String);
+    method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package android.support.v4.hardware.fingerprint {
+
+  public final class FingerprintManagerCompat {
+    method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler);
+    method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
+  }
+
+  public static abstract class FingerprintManagerCompat.AuthenticationCallback {
+    ctor public FingerprintManagerCompat.AuthenticationCallback();
+    method public void onAuthenticationError(int, java.lang.CharSequence);
+    method public void onAuthenticationFailed();
+    method public void onAuthenticationHelp(int, java.lang.CharSequence);
+    method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult);
+  }
+
+  public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject);
+    method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject();
+  }
+
+  public static class FingerprintManagerCompat.CryptoObject {
+    ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method public javax.crypto.Cipher getCipher();
+    method public javax.crypto.Mac getMac();
+    method public java.security.Signature getSignature();
+  }
+
+}
+
+package android.support.v4.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+  }
+
+}
+
+package android.support.v4.media {
+
+  public class AudioAttributesCompat {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method public int getUsage();
+    method public int getVolumeControlStream();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.media.AudioAttributesCompat wrap(java.lang.Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(android.support.v4.media.AudioAttributesCompat);
+    method public android.support.v4.media.AudioAttributesCompat build();
+    method public android.support.v4.media.AudioAttributesCompat.Builder setContentType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setFlags(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setLegacyStreamType(int);
+    method public android.support.v4.media.AudioAttributesCompat.Builder setUsage(int);
+  }
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle getExtras();
+    method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method public java.lang.String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.CustomActionCallback);
+    method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(java.lang.String);
+    method public void unsubscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final java.lang.String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+    field public static final java.lang.String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE = "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+    field public static final java.lang.String EXTRA_DOWNLOAD_PROGRESS = "android.media.browse.extra.DOWNLOAD_PROGRESS";
+    field public static final java.lang.String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+    field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public static abstract class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onProgressUpdate(java.lang.String, android.os.Bundle, android.os.Bundle);
+    method public void onResult(java.lang.String, android.os.Bundle, android.os.Bundle);
+  }
+
+  public static abstract class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(java.lang.String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem fromMediaItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem> fromMediaItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public java.lang.String getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static abstract class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+  }
+
+  public static abstract class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>, android.os.Bundle);
+    method public void onError(java.lang.String);
+    method public void onError(java.lang.String, android.os.Bundle);
+  }
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public void notifyChildrenChanged(java.lang.String);
+    method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void onCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<android.os.Bundle>);
+    method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>, android.os.Bundle);
+    method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field public static final deprecated java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle);
+    method public void sendProgressUpdate(android.os.Bundle);
+    method public void sendResult(T);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object);
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public android.net.Uri getIconUri();
+    method public java.lang.Object getMediaDescription();
+    method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR;
+    field public static final java.lang.String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+    field public static final java.lang.String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(java.lang.String);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object);
+    method public android.graphics.Bitmap getBitmap(java.lang.String);
+    method public android.os.Bundle getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getLong(java.lang.String);
+    method public java.lang.Object getMediaMetadata();
+    method public android.support.v4.media.RatingCompat getRating(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR;
+    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat);
+    method public android.support.v4.media.MediaMetadataCompat build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String);
+    method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object);
+    method public float getPercentRating();
+    method public java.lang.Object getRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat newStarRating(int, float);
+    method public static android.support.v4.media.RatingCompat newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public java.lang.Object getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static abstract class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat);
+  }
+
+}
+
+package android.support.v4.media.app {
+
+  public class NotificationCompat {
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v4.media.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public android.support.v4.media.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
+    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
+    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
+    method public android.os.Bundle getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat getMediaController(android.app.Activity);
+    method public java.lang.Object getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat getMetadata();
+    method public java.lang.String getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
+    method public java.lang.CharSequence getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent getSessionActivity();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public int getShuffleMode();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public deprecated boolean isShuffleModeEnabled();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void removeQueueItemAt(int);
+    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onRepeatModeChanged(int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public deprecated void onShuffleModeChanged(boolean);
+    method public void onShuffleModeChanged(int);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public static abstract class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void playFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void playFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromSearch(java.lang.String, android.os.Bundle);
+    method public abstract void prepareFromUri(android.net.Uri, android.os.Bundle);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
+    method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRating(android.support.v4.media.RatingCompat, android.os.Bundle);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleMode(int);
+    method public abstract deprecated void setShuffleModeEnabled(boolean);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+    field public static final java.lang.String EXTRA_LEGACY_STREAM_TYPE = "android.media.session.extra.LEGACY_STREAM_TYPE";
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public static android.support.v4.media.session.MediaSessionCompat fromMediaSession(android.content.Context, java.lang.Object);
+    method public android.support.v4.media.session.MediaControllerCompat getController();
+    method public java.lang.Object getMediaSession();
+    method public java.lang.Object getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
+    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
+    method public void setQueueTitle(java.lang.CharSequence);
+    method public void setRatingType(int);
+    method public void setRepeatMode(int);
+    method public void setSessionActivity(android.app.PendingIntent);
+    method public void setShuffleMode(int);
+    method public deprecated void setShuffleModeEnabled(boolean);
+    field public static final java.lang.String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final java.lang.String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+    field public static final java.lang.String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final java.lang.String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final java.lang.String ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final java.lang.String ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int MEDIA_ATTRIBUTE_ALBUM = 1; // 0x1
+    field public static final int MEDIA_ATTRIBUTE_ARTIST = 0; // 0x0
+    field public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2; // 0x2
+  }
+
+  public static abstract class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void onCustomAction(java.lang.String, android.os.Bundle);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public deprecated void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRating(android.support.v4.media.RatingCompat, android.os.Bundle);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleMode(int);
+    method public deprecated void onSetShuffleModeEnabled(boolean);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static abstract interface MediaSessionCompat.OnActiveChangeListener {
+    method public abstract void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem fromQueueItem(java.lang.Object);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> fromQueueItemList(java.util.List<?>);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public long getQueueId();
+    method public java.lang.Object getQueueItem();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object);
+    method public java.lang.Object getToken();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object);
+    method public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions();
+    method public int getErrorCode();
+    method public java.lang.CharSequence getErrorMessage();
+    method public android.os.Bundle getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public java.lang.Object getPlaybackState();
+    method public long getPosition();
+    method public int getState();
+    method public static int toKeyCode(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction);
+    method public android.support.v4.media.session.PlaybackStateCompat build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long);
+    method public deprecated android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(int, java.lang.CharSequence);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object);
+    method public java.lang.String getAction();
+    method public java.lang.Object getCustomAction();
+    method public android.os.Bundle getExtras();
+    method public int getIcon();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle);
+  }
+
+}
+
+package android.support.v4.net {
+
+  public final class ConnectivityManagerCompat {
+    method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class TrafficStatsCompat {
+    method public static deprecated void clearThreadStatsTag();
+    method public static deprecated int getThreadStatsTag();
+    method public static deprecated void incrementOperationCount(int);
+    method public static deprecated void incrementOperationCount(int, int);
+    method public static deprecated void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void tagSocket(java.net.Socket) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static deprecated void untagSocket(java.net.Socket) throws java.net.SocketException;
+  }
+
+}
+
+package android.support.v4.os {
+
+  public final deprecated class AsyncTaskCompat {
+    method public static deprecated <Params, Progress, Result> android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
+  }
+
+  public class BuildCompat {
+    method public static deprecated boolean isAtLeastN();
+    method public static deprecated boolean isAtLeastNMR1();
+    method public static deprecated boolean isAtLeastO();
+    method public static boolean isAtLeastOMR1();
+    method public static boolean isAtLeastP();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public java.lang.Object getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener);
+    method public void throwIfCanceled();
+  }
+
+  public static abstract interface CancellationSignal.OnCancelListener {
+    method public abstract void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static android.support.v4.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static java.lang.String getStorageState(java.io.File);
+    field public static final java.lang.String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class LocaleListCompat {
+    method public static android.support.v4.os.LocaleListCompat create(java.util.Locale...);
+    method public static android.support.v4.os.LocaleListCompat forLanguageTags(java.lang.String);
+    method public java.util.Locale get(int);
+    method public static android.support.v4.os.LocaleListCompat getAdjustedDefault();
+    method public static android.support.v4.os.LocaleListCompat getDefault();
+    method public static android.support.v4.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale getFirstMatch(java.lang.String[]);
+    method public int indexOf(java.util.Locale);
+    method public boolean isEmpty();
+    method public int size();
+    method public java.lang.String toLanguageTags();
+    method public java.lang.Object unwrap();
+    method public static android.support.v4.os.LocaleListCompat wrap(java.lang.Object);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(java.lang.String);
+  }
+
+  public final deprecated class ParcelableCompat {
+    method public static deprecated <T> android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>);
+  }
+
+  public abstract deprecated interface ParcelableCompatCreatorCallbacks<T> {
+    method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader);
+    method public abstract T[] newArray(int);
+  }
+
+  public final class TraceCompat {
+    method public static void beginSection(java.lang.String);
+    method public static void endSection();
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package android.support.v4.print {
+
+  public final class PrintHelper {
+    ctor public PrintHelper(android.content.Context);
+    method public int getColorMode();
+    method public int getOrientation();
+    method public int getScaleMode();
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap);
+    method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback);
+    method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException;
+    method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException;
+    method public void setColorMode(int);
+    method public void setOrientation(int);
+    method public void setScaleMode(int);
+    method public static boolean systemSupportsPrint();
+    field public static final int COLOR_MODE_COLOR = 2; // 0x2
+    field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
+    field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
+    field public static final int ORIENTATION_PORTRAIT = 2; // 0x2
+    field public static final int SCALE_MODE_FILL = 2; // 0x2
+    field public static final int SCALE_MODE_FIT = 1; // 0x1
+  }
+
+  public static abstract interface PrintHelper.OnPrintFinishCallback {
+    method public abstract void onFinish();
+  }
+
+}
+
+package android.support.v4.provider {
+
+  public abstract class DocumentFile {
+    method public abstract boolean canRead();
+    method public abstract boolean canWrite();
+    method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String);
+    method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String);
+    method public abstract boolean delete();
+    method public abstract boolean exists();
+    method public android.support.v4.provider.DocumentFile findFile(java.lang.String);
+    method public static android.support.v4.provider.DocumentFile fromFile(java.io.File);
+    method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri);
+    method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri);
+    method public abstract java.lang.String getName();
+    method public android.support.v4.provider.DocumentFile getParentFile();
+    method public abstract java.lang.String getType();
+    method public abstract android.net.Uri getUri();
+    method public abstract boolean isDirectory();
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public abstract boolean isFile();
+    method public abstract boolean isVirtual();
+    method public abstract long lastModified();
+    method public abstract long length();
+    method public abstract android.support.v4.provider.DocumentFile[] listFiles();
+    method public abstract boolean renameTo(java.lang.String);
+  }
+
+  public final class FontRequest {
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
+    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, int);
+    method public java.util.List<java.util.List<byte[]>> getCertificates();
+    method public int getCertificatesArrayResId();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getProviderPackage();
+    method public java.lang.String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontsContractCompat.FontInfo[]);
+    method public static android.support.v4.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.support.v4.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, android.support.v4.provider.FontRequest, android.support.v4.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns {
+    ctor public FontsContractCompat.Columns();
+    field public static final java.lang.String FILE_ID = "file_id";
+    field public static final java.lang.String ITALIC = "font_italic";
+    field public static final java.lang.String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final java.lang.String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public android.support.v4.provider.FontsContractCompat.FontInfo[] getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package android.support.v4.text {
+
+  public final class BidiFormatter {
+    method public static android.support.v4.text.BidiFormatter getInstance();
+    method public static android.support.v4.text.BidiFormatter getInstance(boolean);
+    method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale);
+    method public boolean getStereoReset();
+    method public boolean isRtl(java.lang.String);
+    method public boolean isRtl(java.lang.CharSequence);
+    method public boolean isRtlContext();
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat);
+    method public java.lang.String unicodeWrap(java.lang.String, boolean);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, boolean);
+    method public java.lang.String unicodeWrap(java.lang.String);
+    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale);
+    method public android.support.v4.text.BidiFormatter build();
+    method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat);
+    method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean);
+  }
+
+  public final class ICUCompat {
+    method public static java.lang.String maximizeAndGetScript(java.util.Locale);
+  }
+
+  public abstract interface TextDirectionHeuristicCompat {
+    method public abstract boolean isRtl(char[], int, int);
+    method public abstract boolean isRtl(java.lang.CharSequence, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR;
+    field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale);
+    method public static java.lang.String htmlEncode(java.lang.String);
+    field public static final deprecated java.util.Locale ROOT;
+  }
+
+}
+
+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);
+  }
+
+}
+
+package android.support.v4.util {
+
+  public class ArrayMap<K, V> extends android.support.v4.util.SimpleArrayMap implements java.util.Map {
+    ctor public ArrayMap();
+    ctor public ArrayMap(int);
+    ctor public ArrayMap(android.support.v4.util.SimpleArrayMap);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+    method public java.util.Set<K> keySet();
+    method public void putAll(java.util.Map<? extends K, ? extends V>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public java.util.Collection<V> values();
+  }
+
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    ctor public ArraySet();
+    ctor public ArraySet(int);
+    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
+    method public boolean add(E);
+    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(java.lang.Object);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public void ensureCapacity(int);
+    method public int indexOf(java.lang.Object);
+    method public boolean isEmpty();
+    method public java.util.Iterator<E> iterator();
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public E removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public int size();
+    method public java.lang.Object[] toArray();
+    method public <T> T[] toArray(T[]);
+    method public E valueAt(int);
+  }
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream);
+    method public void finishWrite(java.io.FileOutputStream);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public final class CircularArray<E> {
+    ctor public CircularArray();
+    ctor public CircularArray(int);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public void clear();
+    method public E get(int);
+    method public E getFirst();
+    method public E getLast();
+    method public boolean isEmpty();
+    method public E popFirst();
+    method public E popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public final class CircularIntArray {
+    ctor public CircularIntArray();
+    ctor public CircularIntArray(int);
+    method public void addFirst(int);
+    method public void addLast(int);
+    method public void clear();
+    method public int get(int);
+    method public int getFirst();
+    method public int getLast();
+    method public boolean isEmpty();
+    method public int popFirst();
+    method public int popLast();
+    method public void removeFromEnd(int);
+    method public void removeFromStart(int);
+    method public int size();
+  }
+
+  public class LongSparseArray<E> {
+    ctor public LongSparseArray();
+    ctor public LongSparseArray(int);
+    method public void append(long, E);
+    method public void clear();
+    method public android.support.v4.util.LongSparseArray<E> clone();
+    method public void delete(long);
+    method public E get(long);
+    method public E get(long, E);
+    method public int indexOfKey(long);
+    method public int indexOfValue(E);
+    method public long keyAt(int);
+    method public void put(long, E);
+    method public void remove(long);
+    method public void removeAt(int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+  public class LruCache<K, V> {
+    ctor public LruCache(int);
+    method protected V create(K);
+    method public final synchronized int createCount();
+    method protected void entryRemoved(boolean, K, V, V);
+    method public final void evictAll();
+    method public final synchronized int evictionCount();
+    method public final V get(K);
+    method public final synchronized int hitCount();
+    method public final synchronized int maxSize();
+    method public final synchronized int missCount();
+    method public final V put(K, V);
+    method public final synchronized int putCount();
+    method public final V remove(K);
+    method public void resize(int);
+    method public final synchronized int size();
+    method protected int sizeOf(K, V);
+    method public final synchronized java.util.Map<K, V> snapshot();
+    method public final synchronized java.lang.String toString();
+    method public void trimToSize(int);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(java.lang.Object, java.lang.Object);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F, S);
+    method public static <A, B> android.support.v4.util.Pair<A, B> create(A, B);
+    field public final F first;
+    field public final S second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static abstract interface Pools.Pool<T> {
+    method public abstract T acquire();
+    method public abstract boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements android.support.v4.util.Pools.Pool {
+    ctor public Pools.SimplePool(int);
+    method public T acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends android.support.v4.util.Pools.SimplePool {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public class SimpleArrayMap<K, V> {
+    ctor public SimpleArrayMap();
+    ctor public SimpleArrayMap(int);
+    ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap<K, V>);
+    method public void clear();
+    method public boolean containsKey(java.lang.Object);
+    method public boolean containsValue(java.lang.Object);
+    method public void ensureCapacity(int);
+    method public V get(java.lang.Object);
+    method public int indexOfKey(java.lang.Object);
+    method public boolean isEmpty();
+    method public K keyAt(int);
+    method public V put(K, V);
+    method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>);
+    method public V remove(java.lang.Object);
+    method public V removeAt(int);
+    method public V setValueAt(int, V);
+    method public int size();
+    method public V valueAt(int);
+  }
+
+  public class SparseArrayCompat<E> {
+    ctor public SparseArrayCompat();
+    ctor public SparseArrayCompat(int);
+    method public void append(int, E);
+    method public void clear();
+    method public android.support.v4.util.SparseArrayCompat<E> clone();
+    method public void delete(int);
+    method public E get(int);
+    method public E get(int, E);
+    method public int indexOfKey(int);
+    method public int indexOfValue(E);
+    method public int keyAt(int);
+    method public void put(int, E);
+    method public void remove(int);
+    method public void removeAt(int);
+    method public void removeAtRange(int, int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
+}
+
+package android.support.v4.view {
+
+  public abstract class AbsSavedState implements android.os.Parcelable {
+    ctor protected AbsSavedState(android.os.Parcelable);
+    ctor protected AbsSavedState(android.os.Parcel);
+    ctor protected AbsSavedState(android.os.Parcel, java.lang.ClassLoader);
+    method public int describeContents();
+    method public final android.os.Parcelable getSuperState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.AbsSavedState> CREATOR;
+    field public static final android.support.v4.view.AbsSavedState EMPTY_STATE;
+  }
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener);
+  }
+
+  public static abstract interface ActionProvider.VisibilityListener {
+    method public abstract void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class AsyncLayoutInflater {
+    ctor public AsyncLayoutInflater(android.content.Context);
+    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+  }
+
+  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final deprecated class KeyEventCompat {
+    method public static deprecated boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object);
+    method public static deprecated java.lang.Object getKeyDispatcherState(android.view.View);
+    method public static deprecated boolean hasModifiers(android.view.KeyEvent, int);
+    method public static deprecated boolean hasNoModifiers(android.view.KeyEvent);
+    method public static deprecated boolean isCtrlPressed(android.view.KeyEvent);
+    method public static deprecated boolean isTracking(android.view.KeyEvent);
+    method public static deprecated boolean metaStateHasModifiers(int, int);
+    method public static deprecated boolean metaStateHasNoModifiers(int);
+    method public static deprecated int normalizeMetaState(int);
+    method public static deprecated void startTracking(android.view.KeyEvent);
+  }
+
+  public final class LayoutInflaterCompat {
+    method public static deprecated android.support.v4.view.LayoutInflaterFactory getFactory(android.view.LayoutInflater);
+    method public static deprecated void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  public abstract deprecated interface LayoutInflaterFactory {
+    method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+  }
+
+  public final class MenuItemCompat {
+    method public static deprecated boolean collapseActionView(android.view.MenuItem);
+    method public static deprecated boolean expandActionView(android.view.MenuItem);
+    method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
+    method public static deprecated android.view.View getActionView(android.view.MenuItem);
+    method public static int getAlphabeticModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getContentDescription(android.view.MenuItem);
+    method public static android.content.res.ColorStateList getIconTintList(android.view.MenuItem);
+    method public static android.graphics.PorterDuff.Mode getIconTintMode(android.view.MenuItem);
+    method public static int getNumericModifiers(android.view.MenuItem);
+    method public static java.lang.CharSequence getTooltipText(android.view.MenuItem);
+    method public static deprecated boolean isActionViewExpanded(android.view.MenuItem);
+    method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
+    method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+    method public static void setContentDescription(android.view.MenuItem, java.lang.CharSequence);
+    method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList);
+    method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
+    method public static deprecated android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
+    method public static void setTooltipText(android.view.MenuItem, java.lang.CharSequence);
+    field public static final deprecated int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final deprecated int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final deprecated int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final deprecated int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final deprecated int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  public static abstract deprecated interface MenuItemCompat.OnActionExpandListener {
+    method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem);
+    method public abstract boolean onMenuItemActionExpand(android.view.MenuItem);
+  }
+
+  public final class MotionEventCompat {
+    method public static deprecated int findPointerIndex(android.view.MotionEvent, int);
+    method public static deprecated int getActionIndex(android.view.MotionEvent);
+    method public static deprecated int getActionMasked(android.view.MotionEvent);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int);
+    method public static deprecated float getAxisValue(android.view.MotionEvent, int, int);
+    method public static deprecated int getButtonState(android.view.MotionEvent);
+    method public static deprecated int getPointerCount(android.view.MotionEvent);
+    method public static deprecated int getPointerId(android.view.MotionEvent, int);
+    method public static deprecated int getSource(android.view.MotionEvent);
+    method public static deprecated float getX(android.view.MotionEvent, int);
+    method public static deprecated float getY(android.view.MotionEvent, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field public static final deprecated int ACTION_HOVER_ENTER = 9; // 0x9
+    field public static final deprecated int ACTION_HOVER_EXIT = 10; // 0xa
+    field public static final deprecated int ACTION_HOVER_MOVE = 7; // 0x7
+    field public static final deprecated int ACTION_MASK = 255; // 0xff
+    field public static final deprecated int ACTION_POINTER_DOWN = 5; // 0x5
+    field public static final deprecated int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field public static final deprecated int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field public static final deprecated int ACTION_POINTER_UP = 6; // 0x6
+    field public static final deprecated int ACTION_SCROLL = 8; // 0x8
+    field public static final deprecated int AXIS_BRAKE = 23; // 0x17
+    field public static final deprecated int AXIS_DISTANCE = 24; // 0x18
+    field public static final deprecated int AXIS_GAS = 22; // 0x16
+    field public static final deprecated int AXIS_GENERIC_1 = 32; // 0x20
+    field public static final deprecated int AXIS_GENERIC_10 = 41; // 0x29
+    field public static final deprecated int AXIS_GENERIC_11 = 42; // 0x2a
+    field public static final deprecated int AXIS_GENERIC_12 = 43; // 0x2b
+    field public static final deprecated int AXIS_GENERIC_13 = 44; // 0x2c
+    field public static final deprecated int AXIS_GENERIC_14 = 45; // 0x2d
+    field public static final deprecated int AXIS_GENERIC_15 = 46; // 0x2e
+    field public static final deprecated int AXIS_GENERIC_16 = 47; // 0x2f
+    field public static final deprecated int AXIS_GENERIC_2 = 33; // 0x21
+    field public static final deprecated int AXIS_GENERIC_3 = 34; // 0x22
+    field public static final deprecated int AXIS_GENERIC_4 = 35; // 0x23
+    field public static final deprecated int AXIS_GENERIC_5 = 36; // 0x24
+    field public static final deprecated int AXIS_GENERIC_6 = 37; // 0x25
+    field public static final deprecated int AXIS_GENERIC_7 = 38; // 0x26
+    field public static final deprecated int AXIS_GENERIC_8 = 39; // 0x27
+    field public static final deprecated int AXIS_GENERIC_9 = 40; // 0x28
+    field public static final deprecated int AXIS_HAT_X = 15; // 0xf
+    field public static final deprecated int AXIS_HAT_Y = 16; // 0x10
+    field public static final deprecated int AXIS_HSCROLL = 10; // 0xa
+    field public static final deprecated int AXIS_LTRIGGER = 17; // 0x11
+    field public static final deprecated int AXIS_ORIENTATION = 8; // 0x8
+    field public static final deprecated int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field public static final deprecated int AXIS_RTRIGGER = 18; // 0x12
+    field public static final deprecated int AXIS_RUDDER = 20; // 0x14
+    field public static final deprecated int AXIS_RX = 12; // 0xc
+    field public static final deprecated int AXIS_RY = 13; // 0xd
+    field public static final deprecated int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field public static final deprecated int AXIS_SIZE = 3; // 0x3
+    field public static final deprecated int AXIS_THROTTLE = 19; // 0x13
+    field public static final deprecated int AXIS_TILT = 25; // 0x19
+    field public static final deprecated int AXIS_TOOL_MAJOR = 6; // 0x6
+    field public static final deprecated int AXIS_TOOL_MINOR = 7; // 0x7
+    field public static final deprecated int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field public static final deprecated int AXIS_TOUCH_MINOR = 5; // 0x5
+    field public static final deprecated int AXIS_VSCROLL = 9; // 0x9
+    field public static final deprecated int AXIS_WHEEL = 21; // 0x15
+    field public static final deprecated int AXIS_X = 0; // 0x0
+    field public static final deprecated int AXIS_Y = 1; // 0x1
+    field public static final deprecated int AXIS_Z = 11; // 0xb
+    field public static final deprecated int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public abstract interface NestedScrollingChild {
+    method public abstract boolean dispatchNestedFling(float, float, boolean);
+    method public abstract boolean dispatchNestedPreFling(float, float);
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public abstract boolean hasNestedScrollingParent();
+    method public abstract boolean isNestedScrollingEnabled();
+    method public abstract void setNestedScrollingEnabled(boolean);
+    method public abstract boolean startNestedScroll(int);
+    method public abstract void stopNestedScroll();
+  }
+
+  public abstract interface NestedScrollingChild2 implements android.support.v4.view.NestedScrollingChild {
+    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public abstract boolean hasNestedScrollingParent(int);
+    method public abstract boolean startNestedScroll(int, int);
+    method public abstract void stopNestedScroll(int);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(int);
+  }
+
+  public abstract interface NestedScrollingParent {
+    method public abstract int getNestedScrollAxes();
+    method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public abstract void onStopNestedScroll(android.view.View);
+  }
+
+  public abstract interface NestedScrollingParent2 implements android.support.v4.view.NestedScrollingParent {
+    method public abstract void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public abstract void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public abstract void onStopNestedScroll(android.view.View, int);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public abstract interface OnApplyWindowInsetsListener {
+    method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+  }
+
+  public abstract class PagerAdapter {
+    ctor public PagerAdapter();
+    method public void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void destroyItem(android.view.View, int, java.lang.Object);
+    method public void finishUpdate(android.view.ViewGroup);
+    method public deprecated void finishUpdate(android.view.View);
+    method public abstract int getCount();
+    method public int getItemPosition(java.lang.Object);
+    method public java.lang.CharSequence getPageTitle(int);
+    method public float getPageWidth(int);
+    method public java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated java.lang.Object instantiateItem(android.view.View, int);
+    method public abstract boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public void notifyDataSetChanged();
+    method public void registerDataSetObserver(android.database.DataSetObserver);
+    method public void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public android.os.Parcelable saveState();
+    method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object);
+    method public void startUpdate(android.view.ViewGroup);
+    method public deprecated void startUpdate(android.view.View);
+    method public void unregisterDataSetObserver(android.database.DataSetObserver);
+    field public static final int POSITION_NONE = -2; // 0xfffffffe
+    field public static final int POSITION_UNCHANGED = -1; // 0xffffffff
+  }
+
+  public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip {
+    ctor public PagerTabStrip(android.content.Context);
+    ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet);
+    method public boolean getDrawFullUnderline();
+    method public int getTabIndicatorColor();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setDrawFullUnderline(boolean);
+    method public void setTabIndicatorColor(int);
+    method public void setTabIndicatorColorResource(int);
+  }
+
+  public class PagerTitleStrip extends android.view.ViewGroup {
+    ctor public PagerTitleStrip(android.content.Context);
+    ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet);
+    method public int getTextSpacing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setGravity(int);
+    method public void setNonPrimaryAlpha(float);
+    method public void setTextColor(int);
+    method public void setTextSize(int, float);
+    method public void setTextSpacing(int);
+  }
+
+  public final class PointerIconCompat {
+    method public static android.support.v4.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method public static android.support.v4.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static android.support.v4.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static deprecated boolean isQuickScaleEnabled(java.lang.Object);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+    method public static deprecated void setQuickScaleEnabled(java.lang.Object, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+  }
+
+  public abstract interface ScrollingView {
+    method public abstract int computeHorizontalScrollExtent();
+    method public abstract int computeHorizontalScrollOffset();
+    method public abstract int computeHorizontalScrollRange();
+    method public abstract int computeVerticalScrollExtent();
+    method public abstract int computeVerticalScrollOffset();
+    method public abstract int computeVerticalScrollRange();
+  }
+
+  public abstract interface TintableBackgroundView {
+    method public abstract android.content.res.ColorStateList getSupportBackgroundTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode();
+    method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final deprecated class VelocityTrackerCompat {
+    method public static deprecated float getXVelocity(android.view.VelocityTracker, int);
+    method public static deprecated float getYVelocity(android.view.VelocityTracker, int);
+  }
+
+  public class ViewCompat {
+    ctor protected ViewCompat();
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View>, int);
+    method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method public static deprecated boolean canScrollHorizontally(android.view.View, int);
+    method public static deprecated boolean canScrollVertically(android.view.View, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method public static deprecated int combineMeasuredStates(int, int);
+    method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[], int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[], int);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
+    method public static deprecated float getAlpha(android.view.View);
+    method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect getClipBounds(android.view.View);
+    method public static android.view.Display getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method public static deprecated int getLayerType(android.view.View);
+    method public static int getLayoutDirection(android.view.View);
+    method public static deprecated android.graphics.Matrix getMatrix(android.view.View);
+    method public static deprecated int getMeasuredHeightAndState(android.view.View);
+    method public static deprecated int getMeasuredState(android.view.View);
+    method public static deprecated int getMeasuredWidthAndState(android.view.View);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static deprecated int getOverScrollMode(android.view.View);
+    method public static int getPaddingEnd(android.view.View);
+    method public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent getParentForAccessibility(android.view.View);
+    method public static deprecated float getPivotX(android.view.View);
+    method public static deprecated float getPivotY(android.view.View);
+    method public static deprecated float getRotation(android.view.View);
+    method public static deprecated float getRotationX(android.view.View);
+    method public static deprecated float getRotationY(android.view.View);
+    method public static deprecated float getScaleX(android.view.View);
+    method public static deprecated float getScaleY(android.view.View);
+    method public static int getScrollIndicators(android.view.View);
+    method public static java.lang.String getTransitionName(android.view.View);
+    method public static deprecated float getTranslationX(android.view.View);
+    method public static deprecated float getTranslationY(android.view.View);
+    method public static float getTranslationZ(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method public static deprecated float getX(android.view.View);
+    method public static deprecated float getY(android.view.View);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method public static deprecated boolean isOpaque(android.view.View);
+    method public static boolean isPaddingRelative(android.view.View);
+    method public static deprecated void jumpDrawablesToCurrentState(android.view.View);
+    method public static android.view.View keyboardNavigationClusterSearch(android.view.View, android.view.View, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
+    method public static deprecated void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public static deprecated void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, java.lang.Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long);
+    method public static void requestApplyInsets(android.view.View);
+    method public static deprecated int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method public static deprecated void setActivated(android.view.View, boolean);
+    method public static deprecated void setAlpha(android.view.View, float);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode);
+    method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect);
+    method public static void setElevation(android.view.View, float);
+    method public static deprecated void setFitsSystemWindows(android.view.View, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint);
+    method public static deprecated void setLayerType(android.view.View, int, android.graphics.Paint);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener);
+    method public static deprecated void setOverScrollMode(android.view.View, int);
+    method public static void setPaddingRelative(android.view.View, int, int, int, int);
+    method public static deprecated void setPivotX(android.view.View, float);
+    method public static deprecated void setPivotY(android.view.View, float);
+    method public static void setPointerIcon(android.view.View, android.support.v4.view.PointerIconCompat);
+    method public static deprecated void setRotation(android.view.View, float);
+    method public static deprecated void setRotationX(android.view.View, float);
+    method public static deprecated void setRotationY(android.view.View, float);
+    method public static deprecated void setSaveFromParentEnabled(android.view.View, boolean);
+    method public static deprecated void setScaleX(android.view.View, float);
+    method public static deprecated void setScaleY(android.view.View, float);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+    method public static void setTransitionName(android.view.View, java.lang.String);
+    method public static deprecated void setTranslationX(android.view.View, float);
+    method public static deprecated void setTranslationY(android.view.View, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static deprecated void setX(android.view.View, float);
+    method public static deprecated void setY(android.view.View, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static boolean startNestedScroll(android.view.View, int, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final deprecated int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field public static final deprecated int LAYER_TYPE_NONE = 0; // 0x0
+    field public static final deprecated int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field public static final deprecated int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field public static final deprecated int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field public static final deprecated int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field public static final deprecated int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final deprecated int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field public static final deprecated int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field public static final deprecated int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  public final deprecated class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static deprecated boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method public static deprecated boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method public static deprecated void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public class ViewPager extends android.view.ViewGroup {
+    ctor public ViewPager(android.content.Context);
+    ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+    method public void addOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public boolean arrowScroll(int);
+    method public boolean beginFakeDrag();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public void clearOnPageChangeListeners();
+    method public void endFakeDrag();
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fakeDragBy(float);
+    method public android.support.v4.view.PagerAdapter getAdapter();
+    method public int getCurrentItem();
+    method public int getOffscreenPageLimit();
+    method public int getPageMargin();
+    method public boolean isFakeDragging();
+    method protected void onLayout(boolean, int, int, int, int);
+    method protected void onPageScrolled(int, float, int);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void removeOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
+    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setAdapter(android.support.v4.view.PagerAdapter);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(int);
+    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public void setPageMargin(int);
+    method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
+    method public void setPageMarginDrawable(int);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer);
+    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer, int);
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewPager.DecorView implements java.lang.annotation.Annotation {
+  }
+
+  public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams {
+    ctor public ViewPager.LayoutParams();
+    ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public int gravity;
+    field public boolean isDecor;
+  }
+
+  public static abstract interface ViewPager.OnAdapterChangeListener {
+    method public abstract void onAdapterChanged(android.support.v4.view.ViewPager, android.support.v4.view.PagerAdapter, android.support.v4.view.PagerAdapter);
+  }
+
+  public static abstract interface ViewPager.OnPageChangeListener {
+    method public abstract void onPageScrollStateChanged(int);
+    method public abstract void onPageScrolled(int, float, int);
+    method public abstract void onPageSelected(int);
+  }
+
+  public static abstract interface ViewPager.PageTransformer {
+    method public abstract void transformPage(android.view.View, float);
+  }
+
+  public static class ViewPager.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public ViewPager.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR;
+  }
+
+  public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public ViewPager.SimpleOnPageChangeListener();
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[], int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View, int);
+    method public static deprecated boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public long getStartDelay();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener);
+    method public void start();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer();
+    method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat x(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat y(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat z(float);
+    method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public abstract interface ViewPropertyAnimatorListener {
+    method public abstract void onAnimationCancel(android.view.View);
+    method public abstract void onAnimationEnd(android.view.View);
+    method public abstract void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public abstract interface ViewPropertyAnimatorUpdateListener {
+    method public abstract void onAnimationUpdate(android.view.View);
+  }
+
+  public final class WindowCompat {
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(android.support.v4.view.WindowInsetsCompat);
+    method public android.support.v4.view.WindowInsetsCompat consumeStableInsets();
+    method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public int getStableInsetBottom();
+    method public int getStableInsetLeft();
+    method public int getStableInsetRight();
+    method public int getStableInsetTop();
+    method public int getSystemWindowInsetBottom();
+    method public int getSystemWindowInsetLeft();
+    method public int getSystemWindowInsetRight();
+    method public int getSystemWindowInsetTop();
+    method public boolean hasInsets();
+    method public boolean hasStableInsets();
+    method public boolean hasSystemWindowInsets();
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+  }
+
+}
+
+package android.support.v4.view.accessibility {
+
+  public final class AccessibilityEventCompat {
+    method public static deprecated void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent);
+    method public int getAction(android.view.accessibility.AccessibilityEvent);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int);
+    method public static deprecated int getRecordCount(android.view.accessibility.AccessibilityEvent);
+    method public void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+    method public void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field public static final deprecated int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field public static final deprecated int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field public static final deprecated int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field public static final deprecated int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final deprecated int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field public static final deprecated int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method public static deprecated boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int);
+    method public static deprecated java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager);
+    method public static deprecated boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  public static abstract deprecated interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method public abstract deprecated void onAccessibilityStateChanged(boolean);
+  }
+
+  public static abstract deprecated class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static abstract interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public abstract void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor public deprecated AccessibilityNodeInfoCompat(java.lang.Object);
+    method public void addAction(int);
+    method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public void addChild(android.view.View);
+    method public void addChild(android.view.View, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList();
+    method public int getActions();
+    method public void getBoundsInParent(android.graphics.Rect);
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo();
+    method public java.lang.CharSequence getContentDescription();
+    method public int getDrawingOrder();
+    method public java.lang.CharSequence getError();
+    method public android.os.Bundle getExtras();
+    method public deprecated java.lang.Object getInfo();
+    method public int getInputType();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public java.lang.CharSequence getPackageName();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo();
+    method public java.lang.CharSequence getRoleDescription();
+    method public java.lang.CharSequence getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore();
+    method public java.lang.String getViewIdResourceName();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isVisibleToUser();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
+    method public boolean removeChild(android.view.View);
+    method public boolean removeChild(android.view.View, int);
+    method public void setAccessibilityFocused(boolean);
+    method public void setBoundsInParent(android.graphics.Rect);
+    method public void setBoundsInScreen(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(java.lang.Object);
+    method public void setCollectionItemInfo(java.lang.Object);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(java.lang.CharSequence);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View);
+    method public void setLabelFor(android.view.View, int);
+    method public void setLabeledBy(android.view.View);
+    method public void setLabeledBy(android.view.View, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setParent(android.view.View, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat);
+    method public void setRoleDescription(java.lang.CharSequence);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setSource(android.view.View, int);
+    method public void setText(java.lang.CharSequence);
+    method public void setTextSelection(int, int);
+    method public void setTraversalAfter(android.view.View);
+    method public void setTraversalAfter(android.view.View, int);
+    method public void setTraversalBefore(android.view.View);
+    method public void setTraversalBefore(android.view.View, int);
+    method public void setViewIdResourceName(java.lang.String);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo unwrap();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence);
+    method public int getId();
+    method public java.lang.CharSequence getLabel();
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CONTEXT_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_DOWN;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_LEFT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_RIGHT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_TO_POSITION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_UP;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_PROGRESS;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT;
+    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_ON_SCREEN;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public boolean isHeading();
+    method public boolean isSelected();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean);
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(java.lang.Object);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int);
+    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int);
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
+    method public java.lang.Object getProvider();
+    method public boolean performAction(int, int, android.os.Bundle);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor public deprecated AccessibilityRecordCompat(java.lang.Object);
+    method public deprecated boolean equals(java.lang.Object);
+    method public deprecated int getAddedCount();
+    method public deprecated java.lang.CharSequence getBeforeText();
+    method public deprecated java.lang.CharSequence getClassName();
+    method public deprecated java.lang.CharSequence getContentDescription();
+    method public deprecated int getCurrentItemIndex();
+    method public deprecated int getFromIndex();
+    method public deprecated java.lang.Object getImpl();
+    method public deprecated int getItemCount();
+    method public deprecated int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+    method public deprecated int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+    method public deprecated android.os.Parcelable getParcelableData();
+    method public deprecated int getRemovedCount();
+    method public deprecated int getScrollX();
+    method public deprecated int getScrollY();
+    method public deprecated android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource();
+    method public deprecated java.util.List<java.lang.CharSequence> getText();
+    method public deprecated int getToIndex();
+    method public deprecated int getWindowId();
+    method public deprecated int hashCode();
+    method public deprecated boolean isChecked();
+    method public deprecated boolean isEnabled();
+    method public deprecated boolean isFullScreen();
+    method public deprecated boolean isPassword();
+    method public deprecated boolean isScrollable();
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat);
+    method public static deprecated android.support.v4.view.accessibility.AccessibilityRecordCompat obtain();
+    method public deprecated void recycle();
+    method public deprecated void setAddedCount(int);
+    method public deprecated void setBeforeText(java.lang.CharSequence);
+    method public deprecated void setChecked(boolean);
+    method public deprecated void setClassName(java.lang.CharSequence);
+    method public deprecated void setContentDescription(java.lang.CharSequence);
+    method public deprecated void setCurrentItemIndex(int);
+    method public deprecated void setEnabled(boolean);
+    method public deprecated void setFromIndex(int);
+    method public deprecated void setFullScreen(boolean);
+    method public deprecated void setItemCount(int);
+    method public deprecated void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+    method public deprecated void setParcelableData(android.os.Parcelable);
+    method public deprecated void setPassword(boolean);
+    method public deprecated void setRemovedCount(int);
+    method public deprecated void setScrollX(int);
+    method public deprecated void setScrollY(int);
+    method public deprecated void setScrollable(boolean);
+    method public deprecated void setSource(android.view.View);
+    method public deprecated void setSource(android.view.View, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View, int);
+    method public deprecated void setToIndex(int);
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent();
+    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot();
+    method public java.lang.CharSequence getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain();
+    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package android.support.v4.view.animation {
+
+  public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutLinearInInterpolator();
+  }
+
+  public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public FastOutSlowInInterpolator();
+  }
+
+  public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
+    ctor public LinearOutSlowInInterpolator();
+  }
+
+   abstract class LookupTableInterpolator implements android.view.animation.Interpolator {
+    ctor public LookupTableInterpolator(float[]);
+    method public float getInterpolation(float);
+  }
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package android.support.v4.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+    method public abstract void scrollTargetBy(int, int);
+    method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int);
+    method public android.support.v4.widget.AutoScrollHelper setEdgeType(int);
+    method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public class CircularProgressDrawable extends android.graphics.drawable.Drawable {
+    ctor public CircularProgressDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public boolean getArrowEnabled();
+    method public float getArrowHeight();
+    method public float getArrowScale();
+    method public float getArrowWidth();
+    method public int getBackgroundColor();
+    method public float getCenterRadius();
+    method public int[] getColorSchemeColors();
+    method public float getEndTrim();
+    method public int getOpacity();
+    method public float getProgressRotation();
+    method public float getStartTrim();
+    method public android.graphics.Paint.Cap getStrokeCap();
+    method public float getStrokeWidth();
+    method public boolean isRunning();
+    method public void setAlpha(int);
+    method public void setArrowDimensions(float, float);
+    method public void setArrowEnabled(boolean);
+    method public void setArrowScale(float);
+    method public void setBackgroundColor(int);
+    method public void setCenterRadius(float);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setColorSchemeColors(int...);
+    method public void setProgressRotation(float);
+    method public void setStartEndTrim(float, float);
+    method public void setStrokeCap(android.graphics.Paint.Cap);
+    method public void setStrokeWidth(float);
+    method public void setStyle(int);
+    method public void start();
+    method public void stop();
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode);
+  }
+
+  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 void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public abstract class CursorAdapter extends android.widget.BaseAdapter {
+    ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean);
+    ctor public CursorAdapter(android.content.Context, android.database.Cursor, int);
+    method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursor(android.database.Cursor);
+    method public java.lang.CharSequence convertToString(android.database.Cursor);
+    method public int getCount();
+    method public android.database.Cursor getCursor();
+    method public android.widget.Filter getFilter();
+    method public android.widget.FilterQueryProvider getFilterQueryProvider();
+    method public java.lang.Object getItem(int);
+    method public long getItemId(int);
+    method public android.view.View getView(int, android.view.View, android.view.ViewGroup);
+    method protected deprecated void init(android.content.Context, android.database.Cursor, boolean);
+    method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method protected void onContentChanged();
+    method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence);
+    method public void setFilterQueryProvider(android.widget.FilterQueryProvider);
+    method public android.database.Cursor swapCursor(android.database.Cursor);
+    field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1
+    field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2
+  }
+
+  public class DrawerLayout extends android.view.ViewGroup {
+    ctor public DrawerLayout(android.content.Context);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    method public void addDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void closeDrawer(android.view.View);
+    method public void closeDrawer(android.view.View, boolean);
+    method public void closeDrawer(int);
+    method public void closeDrawer(int, boolean);
+    method public void closeDrawers();
+    method public float getDrawerElevation();
+    method public int getDrawerLockMode(int);
+    method public int getDrawerLockMode(android.view.View);
+    method public java.lang.CharSequence getDrawerTitle(int);
+    method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable();
+    method public boolean isDrawerOpen(android.view.View);
+    method public boolean isDrawerOpen(int);
+    method public boolean isDrawerVisible(android.view.View);
+    method public boolean isDrawerVisible(int);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void openDrawer(android.view.View);
+    method public void openDrawer(android.view.View, boolean);
+    method public void openDrawer(int);
+    method public void openDrawer(int, boolean);
+    method public void removeDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerElevation(float);
+    method public deprecated void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
+    method public void setDrawerLockMode(int);
+    method public void setDrawerLockMode(int, int);
+    method public void setDrawerLockMode(int, android.view.View);
+    method public void setDrawerShadow(android.graphics.drawable.Drawable, int);
+    method public void setDrawerShadow(int, int);
+    method public void setDrawerTitle(int, java.lang.CharSequence);
+    method public void setScrimColor(int);
+    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
+    method public void setStatusBarBackground(int);
+    method public void setStatusBarBackgroundColor(int);
+    field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1
+    field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2
+    field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3
+    field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract interface DrawerLayout.DrawerListener {
+    method public abstract void onDrawerClosed(android.view.View);
+    method public abstract void onDrawerOpened(android.view.View);
+    method public abstract void onDrawerSlide(android.view.View, float);
+    method public abstract void onDrawerStateChanged(int);
+  }
+
+  public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public DrawerLayout.LayoutParams(int, int);
+    ctor public DrawerLayout.LayoutParams(int, int, int);
+    ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public int gravity;
+  }
+
+  protected static class DrawerLayout.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public DrawerLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public DrawerLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR;
+  }
+
+  public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public DrawerLayout.SimpleDrawerListener();
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+  }
+
+  public final class EdgeEffectCompat {
+    ctor public deprecated EdgeEffectCompat(android.content.Context);
+    method public deprecated boolean draw(android.graphics.Canvas);
+    method public deprecated void finish();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean onAbsorb(int);
+    method public deprecated boolean onPull(float);
+    method public deprecated boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method public deprecated boolean onRelease();
+    method public deprecated void setSize(int, int);
+  }
+
+  public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public ExploreByTouchHelper(android.view.View);
+    method public final boolean clearKeyboardFocusForVirtualView(int);
+    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public final int getAccessibilityFocusedVirtualViewId();
+    method public deprecated int getFocusedVirtualView();
+    method public final int getKeyboardFocusedVirtualViewId();
+    method protected abstract int getVirtualViewAt(float, float);
+    method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
+    method public final void invalidateRoot();
+    method public final void invalidateVirtualView(int);
+    method public final void invalidateVirtualView(int, int);
+    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
+    method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
+    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+    method public final boolean requestKeyboardFocusForVirtualView(int);
+    method public final boolean sendEventForVirtualView(int, int);
+    field public static final int HOST_ID = -1; // 0xffffffff
+    field public static final int INVALID_ID = -2147483648; // 0x80000000
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode);
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static deprecated android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View);
+    method public static android.view.View.OnTouchListener createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild2 android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int);
+    method public boolean arrowScroll(int);
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect);
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollTo(int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static abstract interface NestedScrollView.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter {
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor);
+    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean);
+    ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int);
+    method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
+    method public void setDropDownViewResource(int);
+    method public void setViewResource(int);
+  }
+
+  public final deprecated class ScrollerCompat {
+    method public deprecated void abortAnimation();
+    method public deprecated boolean computeScrollOffset();
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context);
+    method public static deprecated android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator);
+    method public deprecated void fling(int, int, int, int, int, int, int, int);
+    method public deprecated void fling(int, int, int, int, int, int, int, int, int, int);
+    method public deprecated float getCurrVelocity();
+    method public deprecated int getCurrX();
+    method public deprecated int getCurrY();
+    method public deprecated int getFinalX();
+    method public deprecated int getFinalY();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean isOverScrolled();
+    method public deprecated void notifyHorizontalEdgeReached(int, int, int);
+    method public deprecated void notifyVerticalEdgeReached(int, int, int);
+    method public deprecated boolean springBack(int, int, int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int);
+    method public deprecated void startScroll(int, int, int, int, int);
+  }
+
+  public final deprecated class SearchViewCompat {
+    method public static deprecated java.lang.CharSequence getQuery(android.view.View);
+    method public static deprecated boolean isIconified(android.view.View);
+    method public static deprecated boolean isQueryRefinementEnabled(android.view.View);
+    method public static deprecated boolean isSubmitButtonEnabled(android.view.View);
+    method public static deprecated android.view.View newSearchView(android.content.Context);
+    method public static deprecated void setIconified(android.view.View, boolean);
+    method public static deprecated void setImeOptions(android.view.View, int);
+    method public static deprecated void setInputType(android.view.View, int);
+    method public static deprecated void setMaxWidth(android.view.View, int);
+    method public static deprecated void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListener);
+    method public static deprecated void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListener);
+    method public static deprecated void setQuery(android.view.View, java.lang.CharSequence, boolean);
+    method public static deprecated void setQueryHint(android.view.View, java.lang.CharSequence);
+    method public static deprecated void setQueryRefinementEnabled(android.view.View, boolean);
+    method public static deprecated void setSearchableInfo(android.view.View, android.content.ComponentName);
+    method public static deprecated void setSubmitButtonEnabled(android.view.View, boolean);
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnCloseListenerCompat implements android.support.v4.widget.SearchViewCompat.OnCloseListener {
+    ctor public SearchViewCompat.OnCloseListenerCompat();
+    method public boolean onClose();
+  }
+
+  public static abstract deprecated interface SearchViewCompat.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract deprecated class SearchViewCompat.OnQueryTextListenerCompat implements android.support.v4.widget.SearchViewCompat.OnQueryTextListener {
+    ctor public SearchViewCompat.OnQueryTextListenerCompat();
+    method public boolean onQueryTextChange(java.lang.String);
+    method public boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter {
+    ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]);
+    ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int);
+    method public void bindView(android.view.View, android.content.Context, android.database.Cursor);
+    method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]);
+    method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter();
+    method public int getStringConversionColumn();
+    method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder();
+    method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter);
+    method public void setStringConversionColumn(int);
+    method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder);
+    method public void setViewImage(android.widget.ImageView, java.lang.String);
+    method public void setViewText(android.widget.TextView, java.lang.String);
+  }
+
+  public static abstract interface SimpleCursorAdapter.CursorToStringConverter {
+    method public abstract java.lang.CharSequence convertToString(android.database.Cursor);
+  }
+
+  public static abstract interface SimpleCursorAdapter.ViewBinder {
+    method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int);
+  }
+
+  public class SlidingPaneLayout extends android.view.ViewGroup {
+    ctor public SlidingPaneLayout(android.content.Context);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int);
+    method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public deprecated boolean canSlide();
+    method public boolean closePane();
+    method public int getCoveredFadeColor();
+    method public int getParallaxDistance();
+    method public int getSliderFadeColor();
+    method public boolean isOpen();
+    method public boolean isSlideable();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public boolean openPane();
+    method public void setCoveredFadeColor(int);
+    method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener);
+    method public void setParallaxDistance(int);
+    method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableLeft(android.graphics.drawable.Drawable);
+    method public void setShadowDrawableRight(android.graphics.drawable.Drawable);
+    method public deprecated void setShadowResource(int);
+    method public void setShadowResourceLeft(int);
+    method public void setShadowResourceRight(int);
+    method public void setSliderFadeColor(int);
+    method public deprecated void smoothSlideClosed();
+    method public deprecated void smoothSlideOpen();
+  }
+
+  public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public SlidingPaneLayout.LayoutParams();
+    ctor public SlidingPaneLayout.LayoutParams(int, int);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams);
+    ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    field public float weight;
+  }
+
+  public static abstract interface SlidingPaneLayout.PanelSlideListener {
+    method public abstract void onPanelClosed(android.view.View);
+    method public abstract void onPanelOpened(android.view.View);
+    method public abstract void onPanelSlide(android.view.View, float);
+  }
+
+  public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener {
+    ctor public SlidingPaneLayout.SimplePanelSlideListener();
+    method public void onPanelClosed(android.view.View);
+    method public void onPanelOpened(android.view.View);
+    method public void onPanelSlide(android.view.View, float);
+  }
+
+  public class Space extends android.view.View {
+    ctor public Space(android.content.Context, android.util.AttributeSet, int);
+    ctor public Space(android.content.Context, android.util.AttributeSet);
+    ctor public Space(android.content.Context);
+  }
+
+  public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent {
+    ctor public SwipeRefreshLayout(android.content.Context);
+    ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet);
+    method public boolean canChildScrollUp();
+    method public int getProgressCircleDiameter();
+    method public int getProgressViewEndOffset();
+    method public int getProgressViewStartOffset();
+    method public boolean isRefreshing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public deprecated void setColorScheme(int...);
+    method public void setColorSchemeColors(int...);
+    method public void setColorSchemeResources(int...);
+    method public void setDistanceToTriggerSync(int);
+    method public void setOnChildScrollUpCallback(android.support.v4.widget.SwipeRefreshLayout.OnChildScrollUpCallback);
+    method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener);
+    method public deprecated void setProgressBackgroundColor(int);
+    method public void setProgressBackgroundColorSchemeColor(int);
+    method public void setProgressBackgroundColorSchemeResource(int);
+    method public void setProgressViewEndTarget(boolean, int);
+    method public void setProgressViewOffset(boolean, int, int);
+    method public void setRefreshing(boolean);
+    method public void setSize(int);
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+    field protected int mFrom;
+    field protected int mOriginalOffsetTop;
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnChildScrollUpCallback {
+    method public abstract boolean canChildScrollUp(android.support.v4.widget.SwipeRefreshLayout, android.view.View);
+  }
+
+  public static abstract interface SwipeRefreshLayout.OnRefreshListener {
+    method public abstract void onRefresh();
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
+    method public static void setTextAppearance(android.widget.TextView, int);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public abstract interface TintableCompoundButton {
+    method public abstract android.content.res.ColorStateList getSupportButtonTintList();
+    method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode();
+    method public abstract void setSupportButtonTintList(android.content.res.ColorStateList);
+    method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public class ViewDragHelper {
+    method public void abort();
+    method protected boolean canScroll(android.view.View, boolean, int, int, int, int);
+    method public void cancel();
+    method public void captureChildView(android.view.View, int);
+    method public boolean checkTouchSlop(int);
+    method public boolean checkTouchSlop(int, int);
+    method public boolean continueSettling(boolean);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback);
+    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback);
+    method public android.view.View findTopChildUnder(int, int);
+    method public void flingCapturedView(int, int, int, int);
+    method public int getActivePointerId();
+    method public android.view.View getCapturedView();
+    method public int getEdgeSize();
+    method public float getMinVelocity();
+    method public int getTouchSlop();
+    method public int getViewDragState();
+    method public boolean isCapturedViewUnder(int, int);
+    method public boolean isEdgeTouched(int);
+    method public boolean isEdgeTouched(int, int);
+    method public boolean isPointerDown(int);
+    method public boolean isViewUnder(android.view.View, int, int);
+    method public void processTouchEvent(android.view.MotionEvent);
+    method public void setEdgeTrackingEnabled(int);
+    method public void setMinVelocity(float);
+    method public boolean settleCapturedViewAt(int, int);
+    method public boolean shouldInterceptTouchEvent(android.view.MotionEvent);
+    method public boolean smoothSlideViewTo(android.view.View, int, int);
+    field public static final int DIRECTION_ALL = 3; // 0x3
+    field public static final int DIRECTION_HORIZONTAL = 1; // 0x1
+    field public static final int DIRECTION_VERTICAL = 2; // 0x2
+    field public static final int EDGE_ALL = 15; // 0xf
+    field public static final int EDGE_BOTTOM = 8; // 0x8
+    field public static final int EDGE_LEFT = 1; // 0x1
+    field public static final int EDGE_RIGHT = 2; // 0x2
+    field public static final int EDGE_TOP = 4; // 0x4
+    field public static final int INVALID_POINTER = -1; // 0xffffffff
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public static abstract class ViewDragHelper.Callback {
+    ctor public ViewDragHelper.Callback();
+    method public int clampViewPositionHorizontal(android.view.View, int, int);
+    method public int clampViewPositionVertical(android.view.View, int, int);
+    method public int getOrderedChildIndex(int);
+    method public int getViewHorizontalDragRange(android.view.View);
+    method public int getViewVerticalDragRange(android.view.View);
+    method public void onEdgeDragStarted(int, int);
+    method public boolean onEdgeLock(int);
+    method public void onEdgeTouched(int, int);
+    method public void onViewCaptured(android.view.View, int);
+    method public void onViewDragStateChanged(int);
+    method public void onViewPositionChanged(android.view.View, int, int, int, int);
+    method public void onViewReleased(android.view.View, float, float);
+    method public abstract boolean tryCaptureView(android.view.View, int);
+  }
+
+}
+
+package android.support.v7.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, boolean);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int);
+    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int, boolean);
+    method public abstract android.view.View getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method public abstract deprecated int getNavigationItemCount();
+    method public abstract deprecated int getNavigationMode();
+    method public abstract deprecated int getSelectedNavigationIndex();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getSelectedTab();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab getTabAt(int);
+    method public abstract deprecated int getTabCount();
+    method public android.content.Context getThemedContext();
+    method public abstract java.lang.CharSequence getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method public abstract deprecated android.support.v7.app.ActionBar.Tab newTab();
+    method public abstract deprecated void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
+    method public abstract deprecated void removeTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract deprecated void removeTabAt(int);
+    method public abstract deprecated void selectTab(android.support.v7.app.ActionBar.Tab);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setCustomView(android.view.View, android.support.v7.app.ActionBar.LayoutParams);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(java.lang.CharSequence);
+    method public void setHomeActionContentDescription(int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.support.v7.app.ActionBar.OnNavigationListener);
+    method public abstract void setLogo(int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable);
+    method public abstract deprecated void setNavigationMode(int);
+    method public abstract deprecated void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1
+    field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
+    field public int gravity;
+  }
+
+  public static abstract interface ActionBar.OnMenuVisibilityListener {
+    method public abstract void onMenuVisibilityChanged(boolean);
+  }
+
+  public static abstract deprecated interface ActionBar.OnNavigationListener {
+    method public abstract boolean onNavigationItemSelected(int, long);
+  }
+
+  public static abstract deprecated class ActionBar.Tab {
+    ctor public ActionBar.Tab();
+    method public abstract java.lang.CharSequence getContentDescription();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.graphics.drawable.Drawable getIcon();
+    method public abstract int getPosition();
+    method public abstract java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract void select();
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(android.view.View);
+    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(android.graphics.drawable.Drawable);
+    method public abstract android.support.v7.app.ActionBar.Tab setIcon(int);
+    method public abstract android.support.v7.app.ActionBar.Tab setTabListener(android.support.v7.app.ActionBar.TabListener);
+    method public abstract android.support.v7.app.ActionBar.Tab setTag(java.lang.Object);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(java.lang.CharSequence);
+    method public abstract android.support.v7.app.ActionBar.Tab setText(int);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static abstract deprecated interface ActionBar.TabListener {
+    method public abstract void onTabReselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabSelected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+    method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
+  }
+
+  public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
+    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
+    method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDrawerClosed(android.view.View);
+    method public void onDrawerOpened(android.view.View);
+    method public void onDrawerSlide(android.view.View, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
+    method public void syncState();
+  }
+
+  public static abstract interface ActionBarDrawerToggle.Delegate {
+    method public abstract android.content.Context getActionBarThemedContext();
+    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
+    method public abstract boolean isNavigationVisible();
+    method public abstract void setActionBarDescription(int);
+    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
+  }
+
+  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends android.support.v7.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.widget.Button getButton(int);
+    method public android.widget.ListView getListView();
+    method public void setButton(int, java.lang.CharSequence, android.os.Message);
+    method public void setButton(int, java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public void setCustomTitle(android.view.View);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIconAttribute(int);
+    method public void setMessage(java.lang.CharSequence);
+    method public void setView(android.view.View);
+    method public void setView(android.view.View, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, int);
+    method public android.support.v7.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public android.support.v7.app.AlertDialog.Builder setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setCancelable(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setCursor(android.database.Cursor, android.content.DialogInterface.OnClickListener, java.lang.String);
+    method public android.support.v7.app.AlertDialog.Builder setCustomTitle(android.view.View);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(int);
+    method public android.support.v7.app.AlertDialog.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.support.v7.app.AlertDialog.Builder setIconAttribute(int);
+    method public deprecated android.support.v7.app.AlertDialog.Builder setInverseBackgroundForced(boolean);
+    method public android.support.v7.app.AlertDialog.Builder setItems(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setItems(java.lang.CharSequence[], android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(int);
+    method public android.support.v7.app.AlertDialog.Builder setMessage(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(int, boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(java.lang.CharSequence[], boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(android.database.Cursor, java.lang.String, java.lang.String, android.content.DialogInterface.OnMultiChoiceClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public android.support.v7.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(int, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.database.Cursor, int, java.lang.String, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(java.lang.CharSequence[], int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.widget.ListAdapter, int, android.content.DialogInterface.OnClickListener);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(int);
+    method public android.support.v7.app.AlertDialog.Builder setTitle(java.lang.CharSequence);
+    method public android.support.v7.app.AlertDialog.Builder setView(int);
+    method public android.support.v7.app.AlertDialog.Builder setView(android.view.View);
+    method public android.support.v7.app.AlertDialog show();
+  }
+
+  public class AppCompatActivity extends android.support.v4.app.FragmentActivity implements android.support.v7.app.ActionBarDrawerToggle.DelegateProvider android.support.v7.app.AppCompatCallback android.support.v4.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public android.content.Intent getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method public void onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public deprecated void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public deprecated void setSupportProgress(int);
+    method public deprecated void setSupportProgressBarIndeterminate(boolean);
+    method public deprecated void setSupportProgressBarIndeterminateVisibility(boolean);
+    method public deprecated void setSupportProgressBarVisibility(boolean);
+    method public android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public void supportInvalidateOptionsMenu();
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public abstract interface AppCompatCallback {
+    method public abstract void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public abstract void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public abstract android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract boolean applyDayNight();
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
+    method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
+    method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T findViewById(int);
+    method public static int getDefaultNightMode();
+    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration);
+    method public abstract void onCreate(android.os.Bundle);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View);
+    method public abstract void setContentView(int);
+    method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context);
+    ctor public AppCompatDialog(android.content.Context, int);
+    ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
+    method public android.support.v7.app.AppCompatDelegate getDelegate();
+    method public android.support.v7.app.ActionBar getSupportActionBar();
+    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
+    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
+    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class MediaRouteActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.app.MediaRouteButton getMediaRouteButton();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public android.support.v7.app.MediaRouteButton onCreateMediaRouteButton();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends android.support.v7.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<android.support.v7.media.MediaRouter.RouteInfo>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle);
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends android.support.v7.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSession();
+    method public android.support.v7.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View onCreateMediaControlView(android.os.Bundle);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends android.support.v4.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static android.support.v7.app.MediaRouteDialogFactory getDefault();
+    method public android.support.v7.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public android.support.v7.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends android.support.v4.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public android.support.v7.media.MediaRouter getMediaRouter();
+    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
+    method public android.support.v7.media.MediaRouter.Callback onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
+  }
+
+  public deprecated class NotificationCompat extends android.support.v4.app.NotificationCompat {
+    ctor public deprecated NotificationCompat();
+    method public static deprecated android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
+  }
+
+  public static deprecated class NotificationCompat.Builder extends android.support.v4.app.NotificationCompat.Builder {
+    ctor public deprecated NotificationCompat.Builder(android.content.Context);
+  }
+
+  public static deprecated class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle {
+    ctor public deprecated NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static deprecated class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v4.media.app.NotificationCompat.DecoratedMediaCustomViewStyle {
+    ctor public deprecated NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static deprecated class NotificationCompat.MediaStyle extends android.support.v4.media.app.NotificationCompat.MediaStyle {
+    ctor public deprecated NotificationCompat.MediaStyle();
+    ctor public deprecated NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
+    method public deprecated android.support.v7.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
+  }
+
+}
+
+package android.support.v7.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+  }
+
+}
+
+package android.support.v7.graphics {
+
+  public final class Palette {
+    method public static android.support.v7.graphics.Palette.Builder from(android.graphics.Bitmap);
+    method public static android.support.v7.graphics.Palette from(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap);
+    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap, int);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, int, android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public int getColorForTarget(android.support.v7.graphics.Target, int);
+    method public int getDarkMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
+    method public int getDarkVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
+    method public int getDominantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getDominantSwatch();
+    method public int getLightMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
+    method public int getLightVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
+    method public int getMutedColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
+    method public android.support.v7.graphics.Palette.Swatch getSwatchForTarget(android.support.v7.graphics.Target);
+    method public java.util.List<android.support.v7.graphics.Palette.Swatch> getSwatches();
+    method public java.util.List<android.support.v7.graphics.Target> getTargets();
+    method public int getVibrantColor(int);
+    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
+  }
+
+  public static final class Palette.Builder {
+    ctor public Palette.Builder(android.graphics.Bitmap);
+    ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
+    method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
+    method public android.support.v7.graphics.Palette.Builder addTarget(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Palette.Builder clearFilters();
+    method public android.support.v7.graphics.Palette.Builder clearRegion();
+    method public android.support.v7.graphics.Palette.Builder clearTargets();
+    method public android.support.v7.graphics.Palette generate();
+    method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
+    method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
+    method public android.support.v7.graphics.Palette.Builder resizeBitmapArea(int);
+    method public deprecated android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
+    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
+  }
+
+  public static abstract interface Palette.Filter {
+    method public abstract boolean isAllowed(int, float[]);
+  }
+
+  public static abstract interface Palette.PaletteAsyncListener {
+    method public abstract void onGenerated(android.support.v7.graphics.Palette);
+  }
+
+  public static final class Palette.Swatch {
+    ctor public Palette.Swatch(int, int);
+    method public int getBodyTextColor();
+    method public float[] getHsl();
+    method public int getPopulation();
+    method public int getRgb();
+    method public int getTitleTextColor();
+  }
+
+  public final class Target {
+    method public float getLightnessWeight();
+    method public float getMaximumLightness();
+    method public float getMaximumSaturation();
+    method public float getMinimumLightness();
+    method public float getMinimumSaturation();
+    method public float getPopulationWeight();
+    method public float getSaturationWeight();
+    method public float getTargetLightness();
+    method public float getTargetSaturation();
+    method public boolean isExclusive();
+    field public static final android.support.v7.graphics.Target DARK_MUTED;
+    field public static final android.support.v7.graphics.Target DARK_VIBRANT;
+    field public static final android.support.v7.graphics.Target LIGHT_MUTED;
+    field public static final android.support.v7.graphics.Target LIGHT_VIBRANT;
+    field public static final android.support.v7.graphics.Target MUTED;
+    field public static final android.support.v7.graphics.Target VIBRANT;
+  }
+
+  public static final class Target.Builder {
+    ctor public Target.Builder();
+    ctor public Target.Builder(android.support.v7.graphics.Target);
+    method public android.support.v7.graphics.Target build();
+    method public android.support.v7.graphics.Target.Builder setExclusive(boolean);
+    method public android.support.v7.graphics.Target.Builder setLightnessWeight(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMaximumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumLightness(float);
+    method public android.support.v7.graphics.Target.Builder setMinimumSaturation(float);
+    method public android.support.v7.graphics.Target.Builder setPopulationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setSaturationWeight(float);
+    method public android.support.v7.graphics.Target.Builder setTargetLightness(float);
+    method public android.support.v7.graphics.Target.Builder setTargetSaturation(float);
+  }
+
+}
+
+package android.support.v7.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package android.support.v7.media {
+
+  public final class MediaControlIntent {
+    field public static final java.lang.String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final java.lang.String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final java.lang.String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final java.lang.String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final java.lang.String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final java.lang.String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final java.lang.String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final java.lang.String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final java.lang.String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final java.lang.String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final java.lang.String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final java.lang.String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final java.lang.String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final java.lang.String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final java.lang.String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final java.lang.String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final java.lang.String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final java.lang.String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final java.lang.String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final java.lang.String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final java.lang.String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final java.lang.String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final java.lang.String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final java.lang.String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final java.lang.String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final java.lang.String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final java.lang.String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final java.lang.String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaItemStatus fromBundle(android.os.Bundle);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final java.lang.String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final java.lang.String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(int);
+    ctor public MediaItemStatus.Builder(android.support.v7.media.MediaItemStatus);
+    method public android.support.v7.media.MediaItemStatus build();
+    method public android.support.v7.media.MediaItemStatus.Builder setContentDuration(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setContentPosition(long);
+    method public android.support.v7.media.MediaItemStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public android.support.v7.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static android.support.v7.media.MediaRouteDescriptor fromBundle(android.os.Bundle);
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public deprecated boolean isConnecting();
+    method public boolean isEnabled();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(java.lang.String, java.lang.String);
+    ctor public MediaRouteDescriptor.Builder(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter>);
+    method public android.support.v7.media.MediaRouteDescriptor build();
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method public deprecated android.support.v7.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDescription(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setId(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setName(java.lang.String);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(android.support.v7.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteDiscoveryRequest fromBundle(android.os.Bundle);
+    method public android.support.v7.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final android.support.v7.media.MediaRouteProviderDescriptor getDescriptor();
+    method public final android.support.v7.media.MediaRouteDiscoveryRequest getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final android.support.v7.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public android.support.v7.media.MediaRouteProvider.RouteController onCreateRouteController(java.lang.String);
+    method public void onDiscoveryRequestChanged(android.support.v7.media.MediaRouteDiscoveryRequest);
+    method public final void setCallback(android.support.v7.media.MediaRouteProvider.Callback);
+    method public final void setDescriptor(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public final void setDiscoveryRequest(android.support.v7.media.MediaRouteDiscoveryRequest);
+  }
+
+  public static abstract class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(android.support.v7.media.MediaRouteProvider, android.support.v7.media.MediaRouteProviderDescriptor);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+  }
+
+  public static abstract class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaRouteProviderDescriptor fromBundle(android.os.Bundle);
+    method public java.util.List<android.support.v7.media.MediaRouteDescriptor> getRoutes();
+    method public boolean isValid();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(android.support.v7.media.MediaRouteProviderDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoute(android.support.v7.media.MediaRouteDescriptor);
+    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<android.support.v7.media.MediaRouteDescriptor>);
+    method public android.support.v7.media.MediaRouteProviderDescriptor build();
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public android.support.v7.media.MediaRouteProvider getMediaRouteProvider();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.support.v7.media.MediaRouteProvider onCreateMediaRouteProvider();
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(android.support.v7.media.MediaRouteSelector);
+    method public static android.support.v7.media.MediaRouteSelector fromBundle(android.os.Bundle);
+    method public java.util.List<java.lang.String> getControlCategories();
+    method public boolean hasControlCategory(java.lang.String);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter>);
+    field public static final android.support.v7.media.MediaRouteSelector EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String>);
+    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategory(java.lang.String);
+    method public android.support.v7.media.MediaRouteSelector.Builder addSelector(android.support.v7.media.MediaRouteSelector);
+    method public android.support.v7.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback);
+    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
+    method public void addProvider(android.support.v7.media.MediaRouteProvider);
+    method public void addRemoteControlClient(java.lang.Object);
+    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
+    method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
+    method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
+    method public java.util.List<android.support.v7.media.MediaRouter.ProviderInfo> getProviders();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+    method public android.support.v7.media.MediaRouter.RouteInfo getSelectedRoute();
+    method public boolean isRouteAvailable(android.support.v7.media.MediaRouteSelector, int);
+    method public void removeCallback(android.support.v7.media.MediaRouter.Callback);
+    method public void removeProvider(android.support.v7.media.MediaRouteProvider);
+    method public void removeRemoteControlClient(java.lang.Object);
+    method public void selectRoute(android.support.v7.media.MediaRouter.RouteInfo);
+    method public void setMediaSession(java.lang.Object);
+    method public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat);
+    method public void unselect(int);
+    method public android.support.v7.media.MediaRouter.RouteInfo updateSelectedRoute(android.support.v7.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+  }
+
+  public static abstract class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onResult(android.os.Bundle);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getPackageName();
+    method public android.support.v7.media.MediaRouteProvider getProviderInstance();
+    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter> getControlFilters();
+    method public java.lang.String getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getId();
+    method public java.lang.String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
+    method public android.support.v7.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public boolean isBluetooth();
+    method public boolean isConnecting();
+    method public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method public boolean isSelected();
+    method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
+    method public void requestSetVolume(int);
+    method public void requestUpdateVolume(int);
+    method public void select();
+    method public void sendControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
+    method public boolean supportsControlAction(java.lang.String, java.lang.String);
+    method public boolean supportsControlCategory(java.lang.String);
+    method public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static android.support.v7.media.MediaSessionStatus fromBundle(android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(int);
+    ctor public MediaSessionStatus.Builder(android.support.v7.media.MediaSessionStatus);
+    method public android.support.v7.media.MediaSessionStatus build();
+    method public android.support.v7.media.MediaSessionStatus.Builder setExtras(android.os.Bundle);
+    method public android.support.v7.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public android.support.v7.media.MediaSessionStatus.Builder setSessionState(int);
+    method public android.support.v7.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void enqueue(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public java.lang.String getSessionId();
+    method public void getSessionStatus(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void getStatus(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void play(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void release();
+    method public void remove(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void resume(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void seek(java.lang.String, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
+    method public void sendMessage(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void setOnMessageReceivedListener(android.support.v7.media.RemotePlaybackClient.OnMessageReceivedListener);
+    method public void setSessionId(java.lang.String);
+    method public void setStatusCallback(android.support.v7.media.RemotePlaybackClient.StatusCallback);
+    method public void startSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+    method public void stop(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
+  }
+
+  public static abstract class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(java.lang.String, int, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.ItemActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+  }
+
+  public static abstract interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public abstract void onMessageReceived(java.lang.String, android.os.Bundle);
+  }
+
+  public static abstract class RemotePlaybackClient.SessionActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+  public static abstract class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
+    method public void onSessionChanged(java.lang.String);
+    method public void onSessionStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
+  }
+
+}
+
+package android.support.v7.preference {
+
+  public class CheckBoxPreference extends android.support.v7.preference.TwoStatePreference {
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
+    ctor public CheckBoxPreference(android.content.Context);
+  }
+
+  public abstract class DialogPreference extends android.support.v7.preference.Preference {
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DialogPreference(android.content.Context);
+    method public android.graphics.drawable.Drawable getDialogIcon();
+    method public int getDialogLayoutResource();
+    method public java.lang.CharSequence getDialogMessage();
+    method public java.lang.CharSequence getDialogTitle();
+    method public java.lang.CharSequence getNegativeButtonText();
+    method public java.lang.CharSequence getPositiveButtonText();
+    method public void setDialogIcon(android.graphics.drawable.Drawable);
+    method public void setDialogIcon(int);
+    method public void setDialogLayoutResource(int);
+    method public void setDialogMessage(java.lang.CharSequence);
+    method public void setDialogMessage(int);
+    method public void setDialogTitle(java.lang.CharSequence);
+    method public void setDialogTitle(int);
+    method public void setNegativeButtonText(java.lang.CharSequence);
+    method public void setNegativeButtonText(int);
+    method public void setPositiveButtonText(java.lang.CharSequence);
+    method public void setPositiveButtonText(int);
+  }
+
+  public static abstract interface DialogPreference.TargetFragment {
+    method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+  }
+
+  public class DropDownPreference extends android.support.v7.preference.ListPreference {
+    ctor public DropDownPreference(android.content.Context);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
+    method protected android.widget.ArrayAdapter createAdapter();
+  }
+
+  public class EditTextPreference extends android.support.v7.preference.DialogPreference {
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
+    ctor public EditTextPreference(android.content.Context);
+    method public java.lang.String getText();
+    method public void setText(java.lang.String);
+  }
+
+  public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public EditTextPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.EditTextPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class ListPreference extends android.support.v7.preference.DialogPreference {
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPreference(android.content.Context, android.util.AttributeSet);
+    ctor public ListPreference(android.content.Context);
+    method public int findIndexOfValue(java.lang.String);
+    method public java.lang.CharSequence[] getEntries();
+    method public java.lang.CharSequence getEntry();
+    method public java.lang.CharSequence[] getEntryValues();
+    method public java.lang.String getValue();
+    method public void setEntries(java.lang.CharSequence[]);
+    method public void setEntries(int);
+    method public void setEntryValues(java.lang.CharSequence[]);
+    method public void setEntryValues(int);
+    method public void setValue(java.lang.String);
+    method public void setValueIndex(int);
+  }
+
+  public class ListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public ListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.ListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public MultiSelectListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
+  public class Preference implements java.lang.Comparable {
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet, int);
+    ctor public Preference(android.content.Context, android.util.AttributeSet);
+    ctor public Preference(android.content.Context);
+    method public boolean callChangeListener(java.lang.Object);
+    method public int compareTo(android.support.v7.preference.Preference);
+    method protected android.support.v7.preference.Preference findPreferenceInHierarchy(java.lang.String);
+    method public android.content.Context getContext();
+    method public java.lang.String getDependency();
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getFragment();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.String getKey();
+    method public final int getLayoutResource();
+    method public android.support.v7.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
+    method public android.support.v7.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
+    method public int getOrder();
+    method public android.support.v7.preference.PreferenceGroup getParent();
+    method protected boolean getPersistedBoolean(boolean);
+    method protected float getPersistedFloat(float);
+    method protected int getPersistedInt(int);
+    method protected long getPersistedLong(long);
+    method protected java.lang.String getPersistedString(java.lang.String);
+    method public java.util.Set<java.lang.String> getPersistedStringSet(java.util.Set<java.lang.String>);
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public boolean getShouldDisableView();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public final int getWidgetLayoutResource();
+    method public boolean hasKey();
+    method public boolean isEnabled();
+    method public boolean isIconSpaceReserved();
+    method public boolean isPersistent();
+    method public boolean isSelectable();
+    method public boolean isSingleLineTitle();
+    method public final boolean isVisible();
+    method protected void notifyChanged();
+    method public void notifyDependencyChange(boolean);
+    method protected void notifyHierarchyChanged();
+    method public void onAttached();
+    method protected void onAttachedToHierarchy(android.support.v7.preference.PreferenceManager);
+    method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
+    method protected void onClick();
+    method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
+    method public void onDetached();
+    method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onParentChanged(android.support.v7.preference.Preference, boolean);
+    method protected void onPrepareForRemoval();
+    method protected void onRestoreInstanceState(android.os.Parcelable);
+    method protected android.os.Parcelable onSaveInstanceState();
+    method protected void onSetInitialValue(boolean, java.lang.Object);
+    method public android.os.Bundle peekExtras();
+    method protected boolean persistBoolean(boolean);
+    method protected boolean persistFloat(float);
+    method protected boolean persistInt(int);
+    method protected boolean persistLong(long);
+    method protected boolean persistString(java.lang.String);
+    method public boolean persistStringSet(java.util.Set<java.lang.String>);
+    method public void restoreHierarchyState(android.os.Bundle);
+    method public void saveHierarchyState(android.os.Bundle);
+    method public void setDefaultValue(java.lang.Object);
+    method public void setDependency(java.lang.String);
+    method public void setEnabled(boolean);
+    method public void setFragment(java.lang.String);
+    method public void setIcon(android.graphics.drawable.Drawable);
+    method public void setIcon(int);
+    method public void setIconSpaceReserved(boolean);
+    method public void setIntent(android.content.Intent);
+    method public void setKey(java.lang.String);
+    method public void setLayoutResource(int);
+    method public void setOnPreferenceChangeListener(android.support.v7.preference.Preference.OnPreferenceChangeListener);
+    method public void setOnPreferenceClickListener(android.support.v7.preference.Preference.OnPreferenceClickListener);
+    method public void setOrder(int);
+    method public void setPersistent(boolean);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public void setSelectable(boolean);
+    method public void setShouldDisableView(boolean);
+    method public void setSingleLineTitle(boolean);
+    method public void setSummary(java.lang.CharSequence);
+    method public void setSummary(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitle(int);
+    method public void setViewId(int);
+    method public final void setVisible(boolean);
+    method public void setWidgetLayoutResource(int);
+    method public boolean shouldDisableDependents();
+    method protected boolean shouldPersist();
+    field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
+  }
+
+  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+    ctor public Preference.BaseSavedState(android.os.Parcel);
+    ctor public Preference.BaseSavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.preference.Preference.BaseSavedState> CREATOR;
+  }
+
+  public static abstract interface Preference.OnPreferenceChangeListener {
+    method public abstract boolean onPreferenceChange(android.support.v7.preference.Preference, java.lang.Object);
+  }
+
+  public static abstract interface Preference.OnPreferenceClickListener {
+    method public abstract boolean onPreferenceClick(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceCategory extends android.support.v7.preference.PreferenceGroup {
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
+    ctor public PreferenceCategory(android.content.Context);
+  }
+
+  public abstract class PreferenceDataStore {
+    ctor public PreferenceDataStore();
+    method public boolean getBoolean(java.lang.String, boolean);
+    method public float getFloat(java.lang.String, float);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public java.util.Set<java.lang.String> getStringSet(java.lang.String, java.util.Set<java.lang.String>);
+    method public void putBoolean(java.lang.String, boolean);
+    method public void putFloat(java.lang.String, float);
+    method public void putInt(java.lang.String, int);
+    method public void putLong(java.lang.String, long);
+    method public void putString(java.lang.String, java.lang.String);
+    method public void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
+  }
+
+  public abstract class PreferenceDialogFragmentCompat extends android.support.v4.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
+    ctor public PreferenceDialogFragmentCompat();
+    method public android.support.v7.preference.DialogPreference getPreference();
+    method protected void onBindDialogView(android.view.View);
+    method public void onClick(android.content.DialogInterface, int);
+    method protected android.view.View onCreateDialogView(android.content.Context);
+    method public abstract void onDialogClosed(boolean);
+    method protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder);
+    field protected static final java.lang.String ARG_KEY = "key";
+  }
+
+  public abstract class PreferenceFragmentCompat extends android.support.v4.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
+    ctor public PreferenceFragmentCompat();
+    method public void addPreferencesFromResource(int);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public final android.support.v7.widget.RecyclerView getListView();
+    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
+    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
+    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
+    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+    method public void scrollToPreference(java.lang.String);
+    method public void scrollToPreference(android.support.v7.preference.Preference);
+    method public void setDivider(android.graphics.drawable.Drawable);
+    method public void setDividerHeight(int);
+    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
+    method public void setPreferencesFromResource(int, java.lang.String);
+    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
+    method public abstract boolean onPreferenceDisplayDialog(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+    method public abstract boolean onPreferenceStartFragment(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
+    method public abstract boolean onPreferenceStartScreen(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.PreferenceScreen);
+  }
+
+  public abstract class PreferenceGroup extends android.support.v7.preference.Preference {
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
+    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
+    method public void addItemFromInflater(android.support.v7.preference.Preference);
+    method public boolean addPreference(android.support.v7.preference.Preference);
+    method protected void dispatchRestoreInstanceState(android.os.Bundle);
+    method protected void dispatchSaveInstanceState(android.os.Bundle);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.support.v7.preference.Preference getPreference(int);
+    method public int getPreferenceCount();
+    method protected boolean isOnSameScreenAsChildren();
+    method public boolean isOrderingAsAdded();
+    method protected boolean onPrepareAddPreference(android.support.v7.preference.Preference);
+    method public void removeAll();
+    method public boolean removePreference(android.support.v7.preference.Preference);
+    method public void setOrderingAsAdded(boolean);
+  }
+
+  public static abstract interface PreferenceGroup.PreferencePositionCallback {
+    method public abstract int getPreferenceAdapterPosition(java.lang.String);
+    method public abstract int getPreferenceAdapterPosition(android.support.v7.preference.Preference);
+  }
+
+  public class PreferenceManager {
+    method public android.support.v7.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
+    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
+    method public android.content.Context getContext();
+    method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
+    method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
+    method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
+    method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
+    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
+    method public android.support.v7.preference.PreferenceDataStore getPreferenceDataStore();
+    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
+    method public android.content.SharedPreferences getSharedPreferences();
+    method public int getSharedPreferencesMode();
+    method public java.lang.String getSharedPreferencesName();
+    method public boolean isStorageDefault();
+    method public boolean isStorageDeviceProtected();
+    method public static void setDefaultValues(android.content.Context, int, boolean);
+    method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
+    method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
+    method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
+    method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
+    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
+    method public void setPreferenceDataStore(android.support.v7.preference.PreferenceDataStore);
+    method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
+    method public void setSharedPreferencesMode(int);
+    method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceProtected();
+    method public void showDialog(android.support.v7.preference.Preference);
+    field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
+  }
+
+  public static abstract interface PreferenceManager.OnDisplayPreferenceDialogListener {
+    method public abstract void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
+  }
+
+  public static abstract interface PreferenceManager.OnNavigateToScreenListener {
+    method public abstract void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
+  }
+
+  public static abstract interface PreferenceManager.OnPreferenceTreeClickListener {
+    method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
+  }
+
+  public static abstract class PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.PreferenceComparisonCallback();
+    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
+    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
+    method public void setShouldUseGeneratedIds(boolean);
+    method public boolean shouldUseGeneratedIds();
+  }
+
+  public class PreferenceViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
+    method public android.view.View findViewById(int);
+    method public boolean isDividerAllowedAbove();
+    method public boolean isDividerAllowedBelow();
+    method public void setDividerAllowedAbove(boolean);
+    method public void setDividerAllowedBelow(boolean);
+  }
+
+  public class SeekBarPreference extends android.support.v7.preference.Preference {
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SeekBarPreference(android.content.Context);
+    method public int getMax();
+    method public int getMin();
+    method public final int getSeekBarIncrement();
+    method public int getValue();
+    method public boolean isAdjustable();
+    method public void setAdjustable(boolean);
+    method public final void setMax(int);
+    method public void setMin(int);
+    method public final void setSeekBarIncrement(int);
+    method public void setValue(int);
+  }
+
+  public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreferenceCompat(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+  public abstract class TwoStatePreference extends android.support.v7.preference.Preference {
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
+    ctor public TwoStatePreference(android.content.Context);
+    method public boolean getDisableDependentsState();
+    method public java.lang.CharSequence getSummaryOff();
+    method public java.lang.CharSequence getSummaryOn();
+    method public boolean isChecked();
+    method public void setChecked(boolean);
+    method public void setDisableDependentsState(boolean);
+    method public void setSummaryOff(java.lang.CharSequence);
+    method public void setSummaryOff(int);
+    method public void setSummaryOn(java.lang.CharSequence);
+    method public void setSummaryOn(int);
+    method protected void syncSummaryView(android.support.v7.preference.PreferenceViewHolder);
+    field protected boolean mChecked;
+  }
+
+}
+
+package android.support.v7.util {
+
+  public class AsyncListUtil<T> {
+    ctor public AsyncListUtil(java.lang.Class<T>, int, android.support.v7.util.AsyncListUtil.DataCallback<T>, android.support.v7.util.AsyncListUtil.ViewCallback);
+    method public T getItem(int);
+    method public int getItemCount();
+    method public void onRangeChanged();
+    method public void refresh();
+  }
+
+  public static abstract class AsyncListUtil.DataCallback<T> {
+    ctor public AsyncListUtil.DataCallback();
+    method public abstract void fillData(T[], int, int);
+    method public int getMaxCachedTiles();
+    method public void recycleData(T[], int);
+    method public abstract int refreshData();
+  }
+
+  public static abstract class AsyncListUtil.ViewCallback {
+    ctor public AsyncListUtil.ViewCallback();
+    method public void extendRangeInto(int[], int[], int);
+    method public abstract void getItemRangeInto(int[]);
+    method public abstract void onDataRefresh();
+    method public abstract void onItemLoaded(int);
+    field public static final int HINT_SCROLL_ASC = 2; // 0x2
+    field public static final int HINT_SCROLL_DESC = 1; // 0x1
+    field public static final int HINT_SCROLL_NONE = 0; // 0x0
+  }
+
+  public class BatchingListUpdateCallback implements android.support.v7.util.ListUpdateCallback {
+    ctor public BatchingListUpdateCallback(android.support.v7.util.ListUpdateCallback);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int, java.lang.Object);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public class DiffUtil {
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback);
+    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback, boolean);
+  }
+
+  public static abstract class DiffUtil.Callback {
+    ctor public DiffUtil.Callback();
+    method public abstract boolean areContentsTheSame(int, int);
+    method public abstract boolean areItemsTheSame(int, int);
+    method public java.lang.Object getChangePayload(int, int);
+    method public abstract int getNewListSize();
+    method public abstract int getOldListSize();
+  }
+
+  public static class DiffUtil.DiffResult {
+    method public void dispatchUpdatesTo(android.support.v7.widget.RecyclerView.Adapter);
+    method public void dispatchUpdatesTo(android.support.v7.util.ListUpdateCallback);
+  }
+
+  public abstract interface ListUpdateCallback {
+    method public abstract void onChanged(int, int, java.lang.Object);
+    method public abstract void onInserted(int, int);
+    method public abstract void onMoved(int, int);
+    method public abstract void onRemoved(int, int);
+  }
+
+  public class SortedList<T> {
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>);
+    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>, int);
+    method public int add(T);
+    method public void addAll(T[], boolean);
+    method public void addAll(T...);
+    method public void addAll(java.util.Collection<T>);
+    method public void beginBatchedUpdates();
+    method public void clear();
+    method public void endBatchedUpdates();
+    method public T get(int) throws java.lang.IndexOutOfBoundsException;
+    method public int indexOf(T);
+    method public void recalculatePositionOfItemAt(int);
+    method public boolean remove(T);
+    method public T removeItemAt(int);
+    method public int size();
+    method public void updateItemAt(int, T);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  public static class SortedList.BatchedCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedList.BatchedCallback(android.support.v7.util.SortedList.Callback<T2>);
+    method public boolean areContentsTheSame(T2, T2);
+    method public boolean areItemsTheSame(T2, T2);
+    method public int compare(T2, T2);
+    method public void dispatchLastEvent();
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+  public static abstract class SortedList.Callback<T2> implements java.util.Comparator android.support.v7.util.ListUpdateCallback {
+    ctor public SortedList.Callback();
+    method public abstract boolean areContentsTheSame(T2, T2);
+    method public abstract boolean areItemsTheSame(T2, T2);
+    method public abstract int compare(T2, T2);
+    method public abstract void onChanged(int, int);
+    method public void onChanged(int, int, java.lang.Object);
+  }
+
+}
+
+package android.support.v7.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View getCustomView();
+    method public abstract android.view.Menu getMenu();
+    method public abstract android.view.MenuInflater getMenuInflater();
+    method public abstract java.lang.CharSequence getSubtitle();
+    method public java.lang.Object getTag();
+    method public abstract java.lang.CharSequence getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View);
+    method public abstract void setSubtitle(java.lang.CharSequence);
+    method public abstract void setSubtitle(int);
+    method public void setTag(java.lang.Object);
+    method public abstract void setTitle(java.lang.CharSequence);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static abstract interface ActionMode.Callback {
+    method public abstract boolean onActionItemClicked(android.support.v7.view.ActionMode, android.view.MenuItem);
+    method public abstract boolean onCreateActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+    method public abstract void onDestroyActionMode(android.support.v7.view.ActionMode);
+    method public abstract boolean onPrepareActionMode(android.support.v7.view.ActionMode, android.view.Menu);
+  }
+
+  public abstract interface CollapsibleActionView {
+    method public abstract void onActionViewCollapsed();
+    method public abstract void onActionViewExpanded();
+  }
+
+}
+
+package android.support.v7.widget {
+
+  public class ActionMenuView extends android.support.v7.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
+    method protected android.support.v7.widget.ActionMenuView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.ActionMenuView.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.ActionMenuView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.view.Menu getMenu();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(android.support.v7.widget.ActionMenuView.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends android.support.v7.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(android.support.v7.widget.ActionMenuView.LayoutParams);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static abstract interface ActionMenuView.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setSupportAllCaps(boolean);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements android.support.v4.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements android.support.v4.view.TintableBackgroundView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet, int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setTextAppearance(android.content.Context, int);
+  }
+
+  public class CardView extends android.widget.FrameLayout {
+    ctor public CardView(android.content.Context);
+    ctor public CardView(android.content.Context, android.util.AttributeSet);
+    ctor public CardView(android.content.Context, android.util.AttributeSet, int);
+    method public android.content.res.ColorStateList getCardBackgroundColor();
+    method public float getCardElevation();
+    method public int getContentPaddingBottom();
+    method public int getContentPaddingLeft();
+    method public int getContentPaddingRight();
+    method public int getContentPaddingTop();
+    method public float getMaxCardElevation();
+    method public boolean getPreventCornerOverlap();
+    method public float getRadius();
+    method public boolean getUseCompatPadding();
+    method public void setCardBackgroundColor(int);
+    method public void setCardBackgroundColor(android.content.res.ColorStateList);
+    method public void setCardElevation(float);
+    method public void setContentPadding(int, int, int, int);
+    method public void setMaxCardElevation(float);
+    method public void setPreventCornerOverlap(boolean);
+    method public void setRadius(float);
+    method public void setUseCompatPadding(boolean);
+  }
+
+  public class DefaultItemAnimator extends android.support.v7.widget.SimpleItemAnimator {
+    ctor public DefaultItemAnimator();
+    method public boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void endAnimations();
+    method public boolean isRunning();
+    method public void runPendingAnimations();
+  }
+
+  public class DividerItemDecoration extends android.support.v7.widget.RecyclerView.ItemDecoration {
+    ctor public DividerItemDecoration(android.content.Context, int);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setOrientation(int);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public class GridLayout extends android.view.ViewGroup {
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public GridLayout(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayout(android.content.Context);
+    method protected android.support.v7.widget.GridLayout.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.GridLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.GridLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getAlignmentMode();
+    method public int getColumnCount();
+    method public int getOrientation();
+    method public android.util.Printer getPrinter();
+    method public int getRowCount();
+    method public boolean getUseDefaultMargins();
+    method public boolean isColumnOrderPreserved();
+    method public boolean isRowOrderPreserved();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setAlignmentMode(int);
+    method public void setColumnCount(int);
+    method public void setColumnOrderPreserved(boolean);
+    method public void setOrientation(int);
+    method public void setPrinter(android.util.Printer);
+    method public void setRowCount(int);
+    method public void setRowOrderPreserved(boolean);
+    method public void setUseDefaultMargins(boolean);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, float);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int, int);
+    method public static android.support.v7.widget.GridLayout.Spec spec(int);
+    field public static final int ALIGN_BOUNDS = 0; // 0x0
+    field public static final int ALIGN_MARGINS = 1; // 0x1
+    field public static final android.support.v7.widget.GridLayout.Alignment BASELINE;
+    field public static final android.support.v7.widget.GridLayout.Alignment BOTTOM;
+    field public static final android.support.v7.widget.GridLayout.Alignment CENTER;
+    field public static final android.support.v7.widget.GridLayout.Alignment END;
+    field public static final android.support.v7.widget.GridLayout.Alignment FILL;
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final android.support.v7.widget.GridLayout.Alignment LEFT;
+    field public static final android.support.v7.widget.GridLayout.Alignment RIGHT;
+    field public static final android.support.v7.widget.GridLayout.Alignment START;
+    field public static final android.support.v7.widget.GridLayout.Alignment TOP;
+    field public static final int UNDEFINED = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class GridLayout.Alignment {
+  }
+
+  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.Spec, android.support.v7.widget.GridLayout.Spec);
+    ctor public GridLayout.LayoutParams();
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.LayoutParams);
+    ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    method public void setGravity(int);
+    field public android.support.v7.widget.GridLayout.Spec columnSpec;
+    field public android.support.v7.widget.GridLayout.Spec rowSpec;
+  }
+
+  public static class GridLayout.Spec {
+    method public android.support.v7.widget.GridLayout.Alignment getAbsoluteAlignment(boolean);
+  }
+
+  public class GridLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public GridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public GridLayoutManager(android.content.Context, int);
+    ctor public GridLayoutManager(android.content.Context, int, int, boolean);
+    method public int getSpanCount();
+    method public android.support.v7.widget.GridLayoutManager.SpanSizeLookup getSpanSizeLookup();
+    method public void setSpanCount(int);
+    method public void setSpanSizeLookup(android.support.v7.widget.GridLayoutManager.SpanSizeLookup);
+    field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
+  }
+
+  public static final class GridLayoutManager.DefaultSpanSizeLookup extends android.support.v7.widget.GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.DefaultSpanSizeLookup();
+    method public int getSpanSize(int);
+  }
+
+  public static class GridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public GridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayoutManager.LayoutParams(int, int);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public GridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getSpanIndex();
+    method public int getSpanSize();
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public static abstract class GridLayoutManager.SpanSizeLookup {
+    ctor public GridLayoutManager.SpanSizeLookup();
+    method public int getSpanGroupIndex(int, int);
+    method public int getSpanIndex(int, int);
+    method public abstract int getSpanSize(int);
+    method public void invalidateSpanIndexCache();
+    method public boolean isSpanIndexCacheEnabled();
+    method public void setSpanIndexCacheEnabled(boolean);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet, int);
+    method protected android.support.v7.widget.LinearLayoutCompat.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.LinearLayoutCompat.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.LinearLayoutCompat.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public LinearLayoutCompat.LayoutParams(android.support.v7.widget.LinearLayoutCompat.LayoutParams);
+    field public int gravity;
+    field public float weight;
+  }
+
+  public class LinearLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.helper.ItemTouchHelper.ViewDropHandler android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public LinearLayoutManager(android.content.Context);
+    ctor public LinearLayoutManager(android.content.Context, int, boolean);
+    ctor public LinearLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int findFirstCompletelyVisibleItemPosition();
+    method public int findFirstVisibleItemPosition();
+    method public int findLastCompletelyVisibleItemPosition();
+    method public int findLastVisibleItemPosition();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
+    method public int getInitialPrefetchItemCount();
+    method public int getOrientation();
+    method public boolean getRecycleChildrenOnDetach();
+    method public boolean getReverseLayout();
+    method public boolean getStackFromEnd();
+    method protected boolean isLayoutRTL();
+    method public boolean isSmoothScrollbarEnabled();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setInitialPrefetchItemCount(int);
+    method public void setOrientation(int);
+    method public void setRecycleChildrenOnDetach(boolean);
+    method public void setReverseLayout(boolean);
+    method public void setSmoothScrollbarEnabled(boolean);
+    method public void setStackFromEnd(boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  protected static class LinearLayoutManager.LayoutChunkResult {
+    ctor protected LinearLayoutManager.LayoutChunkResult();
+    field public int mConsumed;
+    field public boolean mFinished;
+    field public boolean mFocusable;
+    field public boolean mIgnoreConsumed;
+  }
+
+  public class LinearSmoothScroller extends android.support.v7.widget.RecyclerView.SmoothScroller {
+    ctor public LinearSmoothScroller(android.content.Context);
+    method public int calculateDtToFit(int, int, int, int, int);
+    method public int calculateDxToMakeVisible(android.view.View, int);
+    method public int calculateDyToMakeVisible(android.view.View, int);
+    method protected float calculateSpeedPerPixel(android.util.DisplayMetrics);
+    method protected int calculateTimeForDeceleration(int);
+    method protected int calculateTimeForScrolling(int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method protected int getHorizontalSnapPreference();
+    method protected int getVerticalSnapPreference();
+    method protected void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void onStart();
+    method protected void onStop();
+    method protected void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected void updateActionForInterimTarget(android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    field public static final int SNAP_TO_ANY = 0; // 0x0
+    field public static final int SNAP_TO_END = 1; // 0x1
+    field public static final int SNAP_TO_START = -1; // 0xffffffff
+    field protected final android.view.animation.DecelerateInterpolator mDecelerateInterpolator;
+    field protected int mInterimTargetDx;
+    field protected int mInterimTargetDy;
+    field protected final android.view.animation.LinearInterpolator mLinearInterpolator;
+    field protected android.graphics.PointF mTargetVector;
+  }
+
+  public class LinearSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public LinearSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int, int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener createDragToOpenListener(android.view.View);
+    method public void dismiss();
+    method public android.view.View getAnchorView();
+    method public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable getBackground();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView getListView();
+    method public int getPromptPosition();
+    method public java.lang.Object getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter);
+    method public void setAnchorView(android.view.View);
+    method public void setAnimationStyle(int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public abstract class OrientationHelper {
+    method public static android.support.v7.widget.OrientationHelper createHorizontalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public static android.support.v7.widget.OrientationHelper createOrientationHelper(android.support.v7.widget.RecyclerView.LayoutManager, int);
+    method public static android.support.v7.widget.OrientationHelper createVerticalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int getDecoratedEnd(android.view.View);
+    method public abstract int getDecoratedMeasurement(android.view.View);
+    method public abstract int getDecoratedMeasurementInOther(android.view.View);
+    method public abstract int getDecoratedStart(android.view.View);
+    method public abstract int getEnd();
+    method public abstract int getEndAfterPadding();
+    method public abstract int getEndPadding();
+    method public abstract int getMode();
+    method public abstract int getModeInOther();
+    method public abstract int getStartAfterPadding();
+    method public abstract int getTotalSpace();
+    method public int getTotalSpaceChange();
+    method public abstract int getTransformedEndWithDecoration(android.view.View);
+    method public abstract int getTransformedStartWithDecoration(android.view.View);
+    method public abstract void offsetChild(android.view.View, int);
+    method public abstract void offsetChildren(int);
+    method public void onLayoutComplete();
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+    field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
+  }
+
+  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public PagerSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, int, int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(android.support.v7.widget.PopupMenu.OnDismissListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.PopupMenu.OnMenuItemClickListener);
+    method public void show();
+  }
+
+  public static abstract interface PopupMenu.OnDismissListener {
+    method public abstract void onDismiss(android.support.v7.widget.PopupMenu);
+  }
+
+  public static abstract interface PopupMenu.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public class RecyclerView extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild2 android.support.v4.view.ScrollingView {
+    ctor public RecyclerView(android.content.Context);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView(android.content.Context, android.util.AttributeSet, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration, int);
+    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void addOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void addOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void addOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void clearOnChildAttachStateChangeListeners();
+    method public void clearOnScrollListeners();
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[], int[], int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[], int);
+    method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
+    method public android.view.View findChildViewUnder(float, float);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
+    method public deprecated android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForPosition(int);
+    method public boolean fling(int, int);
+    method public android.support.v7.widget.RecyclerView.Adapter getAdapter();
+    method public int getChildAdapterPosition(android.view.View);
+    method public long getChildItemId(android.view.View);
+    method public int getChildLayoutPosition(android.view.View);
+    method public deprecated int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder getChildViewHolder(android.view.View);
+    method public android.support.v7.widget.RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate();
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator getItemAnimator();
+    method public android.support.v7.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getMaxFlingVelocity();
+    method public int getMinFlingVelocity();
+    method public android.support.v7.widget.RecyclerView.OnFlingListener getOnFlingListener();
+    method public boolean getPreserveFocusAfterLayout();
+    method public android.support.v7.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
+    method public int getScrollState();
+    method public boolean hasFixedSize();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean hasPendingAdapterUpdates();
+    method public void invalidateItemDecorations();
+    method public boolean isAnimating();
+    method public boolean isComputingLayout();
+    method public boolean isLayoutFrozen();
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onChildAttachedToWindow(android.view.View);
+    method public void onChildDetachedFromWindow(android.view.View);
+    method public void onDraw(android.graphics.Canvas);
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void onScrollStateChanged(int);
+    method public void onScrolled(int, int);
+    method public void removeItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
+    method public void removeOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
+    method public void removeOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
+    method public void removeOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void scrollToPosition(int);
+    method public void setAccessibilityDelegateCompat(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+    method public void setAdapter(android.support.v7.widget.RecyclerView.Adapter);
+    method public void setChildDrawingOrderCallback(android.support.v7.widget.RecyclerView.ChildDrawingOrderCallback);
+    method public void setHasFixedSize(boolean);
+    method public void setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator);
+    method public void setItemViewCacheSize(int);
+    method public void setLayoutFrozen(boolean);
+    method public void setLayoutManager(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public void setOnFlingListener(android.support.v7.widget.RecyclerView.OnFlingListener);
+    method public deprecated void setOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
+    method public void setPreserveFocusAfterLayout(boolean);
+    method public void setRecycledViewPool(android.support.v7.widget.RecyclerView.RecycledViewPool);
+    method public void setRecyclerListener(android.support.v7.widget.RecyclerView.RecyclerListener);
+    method public void setScrollingTouchSlop(int);
+    method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
+    method public void smoothScrollBy(int, int);
+    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
+    method public void smoothScrollToPosition(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+    method public void stopScroll();
+    method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int INVALID_TYPE = -1; // 0xffffffff
+    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+    field public static final int NO_POSITION = -1; // 0xffffffff
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+    field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
+    field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static abstract class RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder> {
+    ctor public RecyclerView.Adapter();
+    method public final void bindViewHolder(VH, int);
+    method public final VH createViewHolder(android.view.ViewGroup, int);
+    method public abstract int getItemCount();
+    method public long getItemId(int);
+    method public int getItemViewType(int);
+    method public final boolean hasObservers();
+    method public final boolean hasStableIds();
+    method public final void notifyDataSetChanged();
+    method public final void notifyItemChanged(int);
+    method public final void notifyItemChanged(int, java.lang.Object);
+    method public final void notifyItemInserted(int);
+    method public final void notifyItemMoved(int, int);
+    method public final void notifyItemRangeChanged(int, int);
+    method public final void notifyItemRangeChanged(int, int, java.lang.Object);
+    method public final void notifyItemRangeInserted(int, int);
+    method public final void notifyItemRangeRemoved(int, int);
+    method public final void notifyItemRemoved(int);
+    method public void onAttachedToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public abstract void onBindViewHolder(VH, int);
+    method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object>);
+    method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
+    method public void onDetachedFromRecyclerView(android.support.v7.widget.RecyclerView);
+    method public boolean onFailedToRecycleView(VH);
+    method public void onViewAttachedToWindow(VH);
+    method public void onViewDetachedFromWindow(VH);
+    method public void onViewRecycled(VH);
+    method public void registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+    method public void setHasStableIds(boolean);
+    method public void unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
+  }
+
+  public static abstract class RecyclerView.AdapterDataObserver {
+    ctor public RecyclerView.AdapterDataObserver();
+    method public void onChanged();
+    method public void onItemRangeChanged(int, int);
+    method public void onItemRangeChanged(int, int, java.lang.Object);
+    method public void onItemRangeInserted(int, int);
+    method public void onItemRangeMoved(int, int, int);
+    method public void onItemRangeRemoved(int, int);
+  }
+
+  public static abstract interface RecyclerView.ChildDrawingOrderCallback {
+    method public abstract int onGetChildDrawingOrder(int, int);
+  }
+
+  public static abstract class RecyclerView.ItemAnimator {
+    ctor public RecyclerView.ItemAnimator();
+    method public abstract boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object>);
+    method public final void dispatchAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAnimationsFinished();
+    method public abstract void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract void endAnimations();
+    method public long getAddDuration();
+    method public long getChangeDuration();
+    method public long getMoveDuration();
+    method public long getRemoveDuration();
+    method public abstract boolean isRunning();
+    method public final boolean isRunning(android.support.v7.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
+    method public void onAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder, int, java.util.List<java.lang.Object>);
+    method public abstract void runPendingAnimations();
+    method public void setAddDuration(long);
+    method public void setChangeDuration(long);
+    method public void setMoveDuration(long);
+    method public void setRemoveDuration(long);
+    field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
+    field public static final int FLAG_CHANGED = 2; // 0x2
+    field public static final int FLAG_INVALIDATED = 4; // 0x4
+    field public static final int FLAG_MOVED = 2048; // 0x800
+    field public static final int FLAG_REMOVED = 8; // 0x8
+  }
+
+  public static abstract class RecyclerView.ItemAnimator.AdapterChanges implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
+    method public abstract void onAnimationsFinished();
+  }
+
+  public static class RecyclerView.ItemAnimator.ItemHolderInfo {
+    ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public int bottom;
+    field public int changeFlags;
+    field public int left;
+    field public int right;
+    field public int top;
+  }
+
+  public static abstract class RecyclerView.ItemDecoration {
+    ctor public RecyclerView.ItemDecoration();
+    method public deprecated void getItemOffsets(android.graphics.Rect, int, android.support.v7.widget.RecyclerView);
+    method public void getItemOffsets(android.graphics.Rect, android.view.View, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+    method public void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
+    method public deprecated void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
+  }
+
+  public static abstract class RecyclerView.LayoutManager {
+    ctor public RecyclerView.LayoutManager();
+    method public void addDisappearingView(android.view.View);
+    method public void addDisappearingView(android.view.View, int);
+    method public void addView(android.view.View);
+    method public void addView(android.view.View, int);
+    method public void assertInLayoutOrScroll(java.lang.String);
+    method public void assertNotInLayoutOrScroll(java.lang.String);
+    method public void attachView(android.view.View, int, android.support.v7.widget.RecyclerView.LayoutParams);
+    method public void attachView(android.view.View, int);
+    method public void attachView(android.view.View);
+    method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
+    method public boolean canScrollHorizontally();
+    method public boolean canScrollVertically();
+    method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public static int chooseSize(int, int, int);
+    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollExtent(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollOffset(android.support.v7.widget.RecyclerView.State);
+    method public int computeVerticalScrollRange(android.support.v7.widget.RecyclerView.State);
+    method public void detachAndScrapAttachedViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachAndScrapViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public void detachView(android.view.View);
+    method public void detachViewAt(int);
+    method public void endAnimation(android.view.View);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.view.View findViewByPosition(int);
+    method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.content.Context, android.util.AttributeSet);
+    method public int getBaseline();
+    method public int getBottomDecorationHeight(android.view.View);
+    method public android.view.View getChildAt(int);
+    method public int getChildCount();
+    method public static deprecated int getChildMeasureSpec(int, int, int, boolean);
+    method public static int getChildMeasureSpec(int, int, int, int, boolean);
+    method public boolean getClipToPadding();
+    method public int getColumnCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getDecoratedBottom(android.view.View);
+    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+    method public int getDecoratedLeft(android.view.View);
+    method public int getDecoratedMeasuredHeight(android.view.View);
+    method public int getDecoratedMeasuredWidth(android.view.View);
+    method public int getDecoratedRight(android.view.View);
+    method public int getDecoratedTop(android.view.View);
+    method public android.view.View getFocusedChild();
+    method public int getHeight();
+    method public int getHeightMode();
+    method public int getItemCount();
+    method public int getItemViewType(android.view.View);
+    method public int getLayoutDirection();
+    method public int getLeftDecorationWidth(android.view.View);
+    method public int getMinimumHeight();
+    method public int getMinimumWidth();
+    method public int getPaddingBottom();
+    method public int getPaddingEnd();
+    method public int getPaddingLeft();
+    method public int getPaddingRight();
+    method public int getPaddingStart();
+    method public int getPaddingTop();
+    method public int getPosition(android.view.View);
+    method public static android.support.v7.widget.RecyclerView.LayoutManager.Properties getProperties(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getRightDecorationWidth(android.view.View);
+    method public int getRowCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getSelectionModeForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public int getTopDecorationHeight(android.view.View);
+    method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
+    method public int getWidth();
+    method public int getWidthMode();
+    method public boolean hasFocus();
+    method public void ignoreView(android.view.View);
+    method public boolean isAttachedToWindow();
+    method public boolean isAutoMeasureEnabled();
+    method public boolean isFocused();
+    method public final boolean isItemPrefetchEnabled();
+    method public boolean isLayoutHierarchical(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public boolean isMeasurementCacheEnabled();
+    method public boolean isSmoothScrolling();
+    method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
+    method public void layoutDecorated(android.view.View, int, int, int, int);
+    method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
+    method public void measureChild(android.view.View, int, int);
+    method public void measureChildWithMargins(android.view.View, int, int);
+    method public void moveView(int, int);
+    method public void offsetChildrenHorizontal(int);
+    method public void offsetChildrenVertical(int);
+    method public void onAdapterChanged(android.support.v7.widget.RecyclerView.Adapter, android.support.v7.widget.RecyclerView.Adapter);
+    method public boolean onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int);
+    method public void onAttachedToWindow(android.support.v7.widget.RecyclerView);
+    method public deprecated void onDetachedFromWindow(android.support.v7.widget.RecyclerView);
+    method public void onDetachedFromWindow(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.Recycler);
+    method public android.view.View onFocusSearchFailed(android.view.View, int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityEvent(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onInitializeAccessibilityNodeInfoForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method public android.view.View onInterceptFocusSearch(android.view.View, int);
+    method public void onItemsAdded(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsChanged(android.support.v7.widget.RecyclerView);
+    method public void onItemsMoved(android.support.v7.widget.RecyclerView, int, int, int);
+    method public void onItemsRemoved(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int);
+    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int, java.lang.Object);
+    method public void onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void onLayoutCompleted(android.support.v7.widget.RecyclerView.State);
+    method public void onMeasure(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, int);
+    method public deprecated boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.view.View, android.view.View);
+    method public boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, android.view.View, android.view.View);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public void onScrollStateChanged(int);
+    method public boolean performAccessibilityAction(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, android.os.Bundle);
+    method public boolean performAccessibilityActionForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, int, android.os.Bundle);
+    method public void postOnAnimation(java.lang.Runnable);
+    method public void removeAllViews();
+    method public void removeAndRecycleAllViews(android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
+    method public void removeAndRecycleViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
+    method public boolean removeCallbacks(java.lang.Runnable);
+    method public void removeDetachedView(android.view.View);
+    method public void removeView(android.view.View);
+    method public void removeViewAt(int);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
+    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
+    method public void requestLayout();
+    method public void requestSimpleAnimationsInNextLayout();
+    method public int scrollHorizontallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void scrollToPosition(int);
+    method public int scrollVerticallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
+    method public void setAutoMeasureEnabled(boolean);
+    method public final void setItemPrefetchEnabled(boolean);
+    method public void setMeasuredDimension(android.graphics.Rect, int, int);
+    method public void setMeasuredDimension(int, int);
+    method public void setMeasurementCacheEnabled(boolean);
+    method public void smoothScrollToPosition(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, int);
+    method public void startSmoothScroll(android.support.v7.widget.RecyclerView.SmoothScroller);
+    method public void stopIgnoringView(android.view.View);
+    method public boolean supportsPredictiveItemAnimations();
+  }
+
+  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+    method public abstract void addPosition(int, int);
+  }
+
+  public static class RecyclerView.LayoutManager.Properties {
+    ctor public RecyclerView.LayoutManager.Properties();
+    field public int orientation;
+    field public boolean reverseLayout;
+    field public int spanCount;
+    field public boolean stackFromEnd;
+  }
+
+  public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public RecyclerView.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public RecyclerView.LayoutParams(int, int);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public RecyclerView.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public int getViewAdapterPosition();
+    method public int getViewLayoutPosition();
+    method public deprecated int getViewPosition();
+    method public boolean isItemChanged();
+    method public boolean isItemRemoved();
+    method public boolean isViewInvalid();
+    method public boolean viewNeedsUpdate();
+  }
+
+  public static abstract interface RecyclerView.OnChildAttachStateChangeListener {
+    method public abstract void onChildViewAttachedToWindow(android.view.View);
+    method public abstract void onChildViewDetachedFromWindow(android.view.View);
+  }
+
+  public static abstract class RecyclerView.OnFlingListener {
+    ctor public RecyclerView.OnFlingListener();
+    method public abstract boolean onFling(int, int);
+  }
+
+  public static abstract interface RecyclerView.OnItemTouchListener {
+    method public abstract boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public abstract void onRequestDisallowInterceptTouchEvent(boolean);
+    method public abstract void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.OnScrollListener {
+    ctor public RecyclerView.OnScrollListener();
+    method public void onScrollStateChanged(android.support.v7.widget.RecyclerView, int);
+    method public void onScrolled(android.support.v7.widget.RecyclerView, int, int);
+  }
+
+  public static class RecyclerView.RecycledViewPool {
+    ctor public RecyclerView.RecycledViewPool();
+    method public void clear();
+    method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
+    method public int getRecycledViewCount(int);
+    method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setMaxRecycledViews(int, int);
+  }
+
+  public final class RecyclerView.Recycler {
+    ctor public RecyclerView.Recycler();
+    method public void bindViewToPosition(android.view.View, int);
+    method public void clear();
+    method public int convertPreLayoutPositionToPostLayout(int);
+    method public java.util.List<android.support.v7.widget.RecyclerView.ViewHolder> getScrapList();
+    method public android.view.View getViewForPosition(int);
+    method public void recycleView(android.view.View);
+    method public void setViewCacheSize(int);
+  }
+
+  public static abstract interface RecyclerView.RecyclerListener {
+    method public abstract void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
+  }
+
+  public static class RecyclerView.SimpleOnItemTouchListener implements android.support.v7.widget.RecyclerView.OnItemTouchListener {
+    ctor public RecyclerView.SimpleOnItemTouchListener();
+    method public boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+    method public void onRequestDisallowInterceptTouchEvent(boolean);
+    method public void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
+  }
+
+  public static abstract class RecyclerView.SmoothScroller {
+    ctor public RecyclerView.SmoothScroller();
+    method public android.view.View findViewByPosition(int);
+    method public int getChildCount();
+    method public int getChildPosition(android.view.View);
+    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
+    method public int getTargetPosition();
+    method public deprecated void instantScrollToPosition(int);
+    method public boolean isPendingInitialRun();
+    method public boolean isRunning();
+    method protected void normalize(android.graphics.PointF);
+    method protected void onChildAttachedToWindow(android.view.View);
+    method protected abstract void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method protected abstract void onStart();
+    method protected abstract void onStop();
+    method protected abstract void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
+    method public void setTargetPosition(int);
+    method protected final void stop();
+  }
+
+  public static class RecyclerView.SmoothScroller.Action {
+    ctor public RecyclerView.SmoothScroller.Action(int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int);
+    ctor public RecyclerView.SmoothScroller.Action(int, int, int, android.view.animation.Interpolator);
+    method public int getDuration();
+    method public int getDx();
+    method public int getDy();
+    method public android.view.animation.Interpolator getInterpolator();
+    method public void jumpTo(int);
+    method public void setDuration(int);
+    method public void setDx(int);
+    method public void setDy(int);
+    method public void setInterpolator(android.view.animation.Interpolator);
+    method public void update(int, int, int, android.view.animation.Interpolator);
+    field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+  }
+
+  public static abstract interface RecyclerView.SmoothScroller.ScrollVectorProvider {
+    method public abstract android.graphics.PointF computeScrollVectorForPosition(int);
+  }
+
+  public static class RecyclerView.State {
+    ctor public RecyclerView.State();
+    method public boolean didStructureChange();
+    method public <T> T get(int);
+    method public int getItemCount();
+    method public int getRemainingScrollHorizontal();
+    method public int getRemainingScrollVertical();
+    method public int getTargetScrollPosition();
+    method public boolean hasTargetScrollPosition();
+    method public boolean isMeasuring();
+    method public boolean isPreLayout();
+    method public void put(int, java.lang.Object);
+    method public void remove(int);
+    method public boolean willRunPredictiveAnimations();
+    method public boolean willRunSimpleAnimations();
+  }
+
+  public static abstract class RecyclerView.ViewCacheExtension {
+    ctor public RecyclerView.ViewCacheExtension();
+    method public abstract android.view.View getViewForPositionAndType(android.support.v7.widget.RecyclerView.Recycler, int, int);
+  }
+
+  public static abstract class RecyclerView.ViewHolder {
+    ctor public RecyclerView.ViewHolder(android.view.View);
+    method public final int getAdapterPosition();
+    method public final long getItemId();
+    method public final int getItemViewType();
+    method public final int getLayoutPosition();
+    method public final int getOldPosition();
+    method public final deprecated int getPosition();
+    method public final boolean isRecyclable();
+    method public final void setIsRecyclable(boolean);
+    field public final android.view.View itemView;
+  }
+
+  public class RecyclerViewAccessibilityDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate(android.support.v7.widget.RecyclerView);
+    method public android.support.v4.view.AccessibilityDelegateCompat getItemDelegate();
+  }
+
+  public static class RecyclerViewAccessibilityDelegate.ItemDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
+    ctor public RecyclerViewAccessibilityDelegate.ItemDelegate(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
+  }
+
+  public class SearchView extends android.support.v7.widget.LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public java.lang.CharSequence getQuery();
+    method public java.lang.CharSequence getQueryHint();
+    method public android.support.v4.widget.CursorAdapter getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(android.support.v7.widget.SearchView.OnCloseListener);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener);
+    method public void setOnQueryTextListener(android.support.v7.widget.SearchView.OnQueryTextListener);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener);
+    method public void setOnSuggestionListener(android.support.v7.widget.SearchView.OnSuggestionListener);
+    method public void setQuery(java.lang.CharSequence, boolean);
+    method public void setQueryHint(java.lang.CharSequence);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(android.support.v4.widget.CursorAdapter);
+  }
+
+  public static abstract interface SearchView.OnCloseListener {
+    method public abstract boolean onClose();
+  }
+
+  public static abstract interface SearchView.OnQueryTextListener {
+    method public abstract boolean onQueryTextChange(java.lang.String);
+    method public abstract boolean onQueryTextSubmit(java.lang.String);
+  }
+
+  public static abstract interface SearchView.OnSuggestionListener {
+    method public abstract boolean onSuggestionClick(int);
+    method public abstract boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends android.support.v4.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context);
+    method public android.view.View onCreateActionView();
+    method public void setOnShareTargetSelectedListener(android.support.v7.widget.ShareActionProvider.OnShareTargetSelectedListener);
+    method public void setShareHistoryFileName(java.lang.String);
+    method public void setShareIntent(android.content.Intent);
+    field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static abstract interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public abstract boolean onShareTargetSelected(android.support.v7.widget.ShareActionProvider, android.content.Intent);
+  }
+
+  public abstract class SimpleItemAnimator extends android.support.v7.widget.RecyclerView.ItemAnimator {
+    ctor public SimpleItemAnimator();
+    method public abstract boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
+    method public boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+    method public abstract boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public final void dispatchMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public final void dispatchRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public boolean getSupportsChangeAnimations();
+    method public void onAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
+    method public void onMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setSupportsChangeAnimations(boolean);
+  }
+
+  public abstract class SnapHelper extends android.support.v7.widget.RecyclerView.OnFlingListener {
+    ctor public SnapHelper();
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
+    method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public int[] calculateScrollDistance(int, int);
+    method protected android.support.v7.widget.RecyclerView.SmoothScroller createScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method protected deprecated android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+    method public boolean onFling(int, int);
+  }
+
+  public class StaggeredGridLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+    ctor public StaggeredGridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public StaggeredGridLayoutManager(int, int);
+    method public android.graphics.PointF computeScrollVectorForPosition(int);
+    method public int[] findFirstCompletelyVisibleItemPositions(int[]);
+    method public int[] findFirstVisibleItemPositions(int[]);
+    method public int[] findLastCompletelyVisibleItemPositions(int[]);
+    method public int[] findLastVisibleItemPositions(int[]);
+    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
+    method public int getGapStrategy();
+    method public int getOrientation();
+    method public boolean getReverseLayout();
+    method public int getSpanCount();
+    method public void invalidateSpanAssignments();
+    method public void scrollToPositionWithOffset(int, int);
+    method public void setGapStrategy(int);
+    method public void setOrientation(int);
+    method public void setReverseLayout(boolean);
+    method public void setSpanCount(int);
+    field public static final deprecated int GAP_HANDLING_LAZY = 1; // 0x1
+    field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
+    field public static final int GAP_HANDLING_NONE = 0; // 0x0
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class StaggeredGridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public StaggeredGridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
+    method public final int getSpanIndex();
+    method public boolean isFullSpan();
+    method public void setFullSpan(boolean);
+    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public java.lang.CharSequence getTextOff();
+    method public java.lang.CharSequence getTextOn();
+    method public android.graphics.drawable.Drawable getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context, int);
+    method public void setSwitchTypeface(android.graphics.Typeface, int);
+    method public void setSwitchTypeface(android.graphics.Typeface);
+    method public void setTextOff(java.lang.CharSequence);
+    method public void setTextOn(java.lang.CharSequence);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
+  }
+
+  public abstract interface ThemedSpinnerAdapter implements android.widget.SpinnerAdapter {
+    method public abstract android.content.res.Resources.Theme getDropDownViewTheme();
+    method public abstract void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method protected android.support.v7.widget.Toolbar.LayoutParams generateDefaultLayoutParams();
+    method public android.support.v7.widget.Toolbar.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected android.support.v7.widget.Toolbar.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable getLogo();
+    method public java.lang.CharSequence getLogoDescription();
+    method public android.view.Menu getMenu();
+    method public java.lang.CharSequence getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable getNavigationIcon();
+    method public android.graphics.drawable.Drawable getOverflowIcon();
+    method public int getPopupTheme();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
+    method protected void onLayout(boolean, int, int, int, int);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable);
+    method public void setLogoDescription(int);
+    method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationIcon(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener);
+    method public void setOnMenuItemClickListener(android.support.v7.widget.Toolbar.OnMenuItemClickListener);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable);
+    method public void setPopupTheme(int);
+    method public void setSubtitle(int);
+    method public void setSubtitle(java.lang.CharSequence);
+    method public void setSubtitleTextAppearance(android.content.Context, int);
+    method public void setSubtitleTextColor(int);
+    method public void setTitle(int);
+    method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context, int);
+    method public void setTitleTextColor(int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends android.support.v7.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(android.support.v7.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
+  }
+
+  public static abstract interface Toolbar.OnMenuItemClickListener {
+    method public abstract boolean onMenuItemClick(android.view.MenuItem);
+  }
+
+  public static class Toolbar.SavedState extends android.support.v4.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel);
+    ctor public Toolbar.SavedState(android.os.Parcel, java.lang.ClassLoader);
+    ctor public Toolbar.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.v7.widget.Toolbar.SavedState> CREATOR;
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, java.lang.CharSequence);
+  }
+
+}
+
+package android.support.v7.widget.helper {
+
+  public class ItemTouchHelper extends android.support.v7.widget.RecyclerView.ItemDecoration implements android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener {
+    ctor public ItemTouchHelper(android.support.v7.widget.helper.ItemTouchHelper.Callback);
+    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView);
+    method public void onChildViewAttachedToWindow(android.view.View);
+    method public void onChildViewDetachedFromWindow(android.view.View);
+    method public void startDrag(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void startSwipe(android.support.v7.widget.RecyclerView.ViewHolder);
+    field public static final int ACTION_STATE_DRAG = 2; // 0x2
+    field public static final int ACTION_STATE_IDLE = 0; // 0x0
+    field public static final int ACTION_STATE_SWIPE = 1; // 0x1
+    field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
+    field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
+    field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
+    field public static final int DOWN = 2; // 0x2
+    field public static final int END = 32; // 0x20
+    field public static final int LEFT = 4; // 0x4
+    field public static final int RIGHT = 8; // 0x8
+    field public static final int START = 16; // 0x10
+    field public static final int UP = 1; // 0x1
+  }
+
+  public static abstract class ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.Callback();
+    method public boolean canDropOver(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public android.support.v7.widget.RecyclerView.ViewHolder chooseDropTarget(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<android.support.v7.widget.RecyclerView.ViewHolder>, int, int);
+    method public void clearView(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int convertToAbsoluteDirection(int, int);
+    method public static int convertToRelativeDirection(int, int);
+    method public long getAnimationDuration(android.support.v7.widget.RecyclerView, int, float, float);
+    method public int getBoundingBoxMargin();
+    method public static android.support.v7.widget.helper.ItemTouchUIUtil getDefaultUIUtil();
+    method public float getMoveThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public abstract int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeEscapeVelocity(float);
+    method public float getSwipeThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
+    method public float getSwipeVelocityThreshold(float);
+    method public int interpolateOutOfBoundsScroll(android.support.v7.widget.RecyclerView, int, int, int, long);
+    method public boolean isItemViewSwipeEnabled();
+    method public boolean isLongPressDragEnabled();
+    method public static int makeFlag(int, int);
+    method public static int makeMovementFlags(int, int);
+    method public void onChildDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public void onChildDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+    method public abstract boolean onMove(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void onMoved(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int);
+    method public void onSelectedChanged(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    method public abstract void onSwiped(android.support.v7.widget.RecyclerView.ViewHolder, int);
+    field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
+    field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
+  }
+
+  public static abstract class ItemTouchHelper.SimpleCallback extends android.support.v7.widget.helper.ItemTouchHelper.Callback {
+    ctor public ItemTouchHelper.SimpleCallback(int, int);
+    method public int getDragDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public int getSwipeDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
+    method public void setDefaultDragDirs(int);
+    method public void setDefaultSwipeDirs(int);
+  }
+
+  public static abstract interface ItemTouchHelper.ViewDropHandler {
+    method public abstract void prepareForDrop(android.view.View, android.view.View, int, int);
+  }
+
+  public abstract interface ItemTouchUIUtil {
+    method public abstract void clearView(android.view.View);
+    method public abstract void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
+    method public abstract void onSelected(android.view.View);
+  }
+
+}
+
+package android.support.v7.widget.util {
+
+  public abstract class SortedListAdapterCallback<T2> extends android.support.v7.util.SortedList.Callback {
+    ctor public SortedListAdapterCallback(android.support.v7.widget.RecyclerView.Adapter);
+    method public void onChanged(int, int);
+    method public void onInserted(int, int);
+    method public void onMoved(int, int);
+    method public void onRemoved(int, int);
+  }
+
+}
+
+package android.support.wear.widget {
+
+  public class BoxInsetLayout extends android.view.ViewGroup {
+    ctor public BoxInsetLayout(android.content.Context);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout(android.content.Context, android.util.AttributeSet, int);
+    method public android.support.wear.widget.BoxInsetLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
+    method protected void onLayout(boolean, int, int, int, int);
+  }
+
+  public static class BoxInsetLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
+    ctor public BoxInsetLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public BoxInsetLayout.LayoutParams(int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(int, int, int, int);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public BoxInsetLayout.LayoutParams(android.support.wear.widget.BoxInsetLayout.LayoutParams);
+    field public static final int BOX_ALL = 15; // 0xf
+    field public static final int BOX_BOTTOM = 8; // 0x8
+    field public static final int BOX_LEFT = 1; // 0x1
+    field public static final int BOX_NONE = 0; // 0x0
+    field public static final int BOX_RIGHT = 4; // 0x4
+    field public static final int BOX_TOP = 2; // 0x2
+    field public int boxedEdges;
+  }
+
+  public class CircularProgressLayout extends android.widget.FrameLayout {
+    ctor public CircularProgressLayout(android.content.Context);
+    ctor public CircularProgressLayout(android.content.Context, android.util.AttributeSet);
+    ctor public CircularProgressLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public CircularProgressLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getBackgroundColor();
+    method public int[] getColorSchemeColors();
+    method public android.support.wear.widget.CircularProgressLayout.OnTimerFinishedListener getOnTimerFinishedListener();
+    method public android.support.v4.widget.CircularProgressDrawable getProgressDrawable();
+    method public float getStartingRotation();
+    method public float getStrokeWidth();
+    method public long getTotalTime();
+    method public boolean isIndeterminate();
+    method public boolean isTimerRunning();
+    method public void setColorSchemeColors(int...);
+    method public void setIndeterminate(boolean);
+    method public void setOnTimerFinishedListener(android.support.wear.widget.CircularProgressLayout.OnTimerFinishedListener);
+    method public void setStartingRotation(float);
+    method public void setStrokeWidth(float);
+    method public void setTotalTime(long);
+    method public void startTimer();
+    method public void stopTimer();
+  }
+
+  public static abstract interface CircularProgressLayout.OnTimerFinishedListener {
+    method public abstract void onTimerFinished(android.support.wear.widget.CircularProgressLayout);
+  }
+
+  public class CurvingLayoutCallback extends android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback {
+    ctor public CurvingLayoutCallback(android.content.Context);
+    method public void adjustAnchorOffsetXY(android.view.View, float[]);
+    method public void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class RoundedDrawable extends android.graphics.drawable.Drawable {
+    ctor public RoundedDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public int getBackgroundColor();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public int getOpacity();
+    method public int getRadius();
+    method public boolean isClipEnabled();
+    method public void setAlpha(int);
+    method public void setBackgroundColor(int);
+    method public void setClipEnabled(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setDrawable(android.graphics.drawable.Drawable);
+    method public void setRadius(int);
+  }
+
+  public class SwipeDismissFrameLayout extends android.widget.FrameLayout {
+    ctor public SwipeDismissFrameLayout(android.content.Context);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwipeDismissFrameLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+    method public void removeCallback(android.support.wear.widget.SwipeDismissFrameLayout.Callback);
+  }
+
+  public static abstract class SwipeDismissFrameLayout.Callback {
+    ctor public SwipeDismissFrameLayout.Callback();
+    method public void onDismissed(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeCanceled(android.support.wear.widget.SwipeDismissFrameLayout);
+    method public void onSwipeStarted(android.support.wear.widget.SwipeDismissFrameLayout);
+  }
+
+  public class WearableLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+    ctor public WearableLinearLayoutManager(android.content.Context, android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+    ctor public WearableLinearLayoutManager(android.content.Context);
+    method public android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback getLayoutCallback();
+    method public void setLayoutCallback(android.support.wear.widget.WearableLinearLayoutManager.LayoutCallback);
+  }
+
+  public static abstract class WearableLinearLayoutManager.LayoutCallback {
+    ctor public WearableLinearLayoutManager.LayoutCallback();
+    method public abstract void onLayoutFinished(android.view.View, android.support.v7.widget.RecyclerView);
+  }
+
+  public class WearableRecyclerView extends android.support.v7.widget.RecyclerView {
+    ctor public WearableRecyclerView(android.content.Context);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableRecyclerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public float getBezelFraction();
+    method public float getScrollDegreesPerScreen();
+    method public boolean isCircularScrollingGestureEnabled();
+    method public boolean isEdgeItemsCenteringEnabled();
+    method public void setBezelFraction(float);
+    method public void setCircularScrollingGestureEnabled(boolean);
+    method public void setEdgeItemsCenteringEnabled(boolean);
+    method public void setScrollDegreesPerScreen(float);
+  }
+
+}
+
+package android.support.wear.widget.drawer {
+
+  public class WearableActionDrawerView extends android.support.wear.widget.drawer.WearableDrawerView {
+    ctor public WearableActionDrawerView(android.content.Context);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableActionDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.view.Menu getMenu();
+    method public void setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener);
+    method public void setTitle(java.lang.CharSequence);
+  }
+
+  public class WearableDrawerController {
+    method public void closeDrawer();
+    method public void openDrawer();
+    method public void peekDrawer();
+  }
+
+  public class WearableDrawerLayout extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingParent android.view.View.OnLayoutChangeListener {
+    ctor public WearableDrawerLayout(android.content.Context);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableDrawerLayout(android.content.Context, android.util.AttributeSet, int, int);
+    method public void onFlingComplete(android.view.View);
+    method public void onLayoutChange(android.view.View, int, int, int, int, int, int, int, int);
+    method public void setDrawerStateCallback(android.support.wear.widget.drawer.WearableDrawerLayout.DrawerStateCallback);
+  }
+
+  public static class WearableDrawerLayout.DrawerStateCallback {
+    ctor public WearableDrawerLayout.DrawerStateCallback();
+    method public void onDrawerClosed(android.support.wear.widget.drawer.WearableDrawerLayout, android.support.wear.widget.drawer.WearableDrawerView);
+    method public void onDrawerOpened(android.support.wear.widget.drawer.WearableDrawerLayout, android.support.wear.widget.drawer.WearableDrawerView);
+    method public void onDrawerStateChanged(android.support.wear.widget.drawer.WearableDrawerLayout, int);
+  }
+
+  public class WearableDrawerView extends android.widget.FrameLayout {
+    ctor public WearableDrawerView(android.content.Context);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.support.wear.widget.drawer.WearableDrawerController getController();
+    method public android.view.View getDrawerContent();
+    method public int getDrawerState();
+    method public boolean isAutoPeekEnabled();
+    method public boolean isClosed();
+    method public boolean isLocked();
+    method public boolean isLockedWhenClosed();
+    method public boolean isOpenOnlyAtTopEnabled();
+    method public boolean isOpened();
+    method public boolean isPeekOnScrollDownEnabled();
+    method public boolean isPeeking();
+    method public void onDrawerClosed();
+    method public void onDrawerOpened();
+    method public void onDrawerStateChanged(int);
+    method public void onPeekContainerClicked(android.view.View);
+    method public void setDrawerContent(android.view.View);
+    method public void setIsAutoPeekEnabled(boolean);
+    method public void setIsLocked(boolean);
+    method public void setLockedWhenClosed(boolean);
+    method public void setOpenOnlyAtTopEnabled(boolean);
+    method public void setPeekContent(android.view.View);
+    method public void setPeekOnScrollDownEnabled(boolean);
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  public class WearableNavigationDrawerView extends android.support.wear.widget.drawer.WearableDrawerView {
+    ctor public WearableNavigationDrawerView(android.content.Context);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet, int);
+    ctor public WearableNavigationDrawerView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addOnItemSelectedListener(android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener);
+    method public int getNavigationStyle();
+    method public void removeOnItemSelectedListener(android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener);
+    method public void setAdapter(android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter);
+    method public void setCurrentItem(int, boolean);
+    field public static final int MULTI_PAGE = 1; // 0x1
+    field public static final int SINGLE_PAGE = 0; // 0x0
+  }
+
+  public static abstract interface WearableNavigationDrawerView.OnItemSelectedListener {
+    method public abstract void onItemSelected(int);
+  }
+
+  public static abstract class WearableNavigationDrawerView.WearableNavigationDrawerAdapter {
+    ctor public WearableNavigationDrawerView.WearableNavigationDrawerAdapter();
+    method public abstract int getCount();
+    method public abstract android.graphics.drawable.Drawable getItemDrawable(int);
+    method public abstract java.lang.CharSequence getItemText(int);
+    method public void notifyDataSetChanged();
+  }
+
+}
+
diff --git a/api/current.txt b/api/current.txt
deleted file mode 100644
index 30642f1..0000000
--- a/api/current.txt
+++ /dev/null
@@ -1,11332 +0,0 @@
-package android.support.animation {
-
-  public abstract class DynamicAnimation<T extends android.support.animation.DynamicAnimation<T>> {
-    method public T addEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
-    method public T addUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
-    method public void cancel();
-    method public float getMinimumVisibleChange();
-    method public boolean isRunning();
-    method public void removeEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
-    method public void removeUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
-    method public T setMaxValue(float);
-    method public T setMinValue(float);
-    method public T setMinimumVisibleChange(float);
-    method public T setStartValue(float);
-    method public T setStartVelocity(float);
-    method public void start();
-    field public static final android.support.animation.DynamicAnimation.ViewProperty ALPHA;
-    field public static final float MIN_VISIBLE_CHANGE_ALPHA = 0.00390625f;
-    field public static final float MIN_VISIBLE_CHANGE_PIXELS = 1.0f;
-    field public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 0.1f;
-    field public static final float MIN_VISIBLE_CHANGE_SCALE = 0.002f;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_X;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_Y;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_X;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_Y;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_X;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_Y;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_X;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Y;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Z;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty X;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty Y;
-    field public static final android.support.animation.DynamicAnimation.ViewProperty Z;
-  }
-
-  public static abstract interface DynamicAnimation.OnAnimationEndListener {
-    method public abstract void onAnimationEnd(android.support.animation.DynamicAnimation, boolean, float, float);
-  }
-
-  public static abstract interface DynamicAnimation.OnAnimationUpdateListener {
-    method public abstract void onAnimationUpdate(android.support.animation.DynamicAnimation, float, float);
-  }
-
-  public static abstract class DynamicAnimation.ViewProperty extends android.support.animation.FloatPropertyCompat {
-  }
-
-  public final class FlingAnimation extends android.support.animation.DynamicAnimation {
-    ctor public FlingAnimation(android.support.animation.FloatValueHolder);
-    ctor public FlingAnimation(K, android.support.animation.FloatPropertyCompat<K>);
-    method public float getFriction();
-    method public android.support.animation.FlingAnimation setFriction(float);
-  }
-
-  public abstract class FloatPropertyCompat<T> {
-    ctor public FloatPropertyCompat(java.lang.String);
-    method public static <T> android.support.animation.FloatPropertyCompat<T> createFloatPropertyCompat(android.util.FloatProperty<T>);
-    method public abstract float getValue(T);
-    method public abstract void setValue(T, float);
-  }
-
-  public final class FloatValueHolder {
-    ctor public FloatValueHolder();
-    ctor public FloatValueHolder(float);
-    method public float getValue();
-    method public void setValue(float);
-  }
-
-  public final class SpringAnimation extends android.support.animation.DynamicAnimation {
-    ctor public SpringAnimation(android.support.animation.FloatValueHolder);
-    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>);
-    ctor public SpringAnimation(K, android.support.animation.FloatPropertyCompat<K>, float);
-    ctor public deprecated SpringAnimation(android.view.View, android.support.animation.DynamicAnimation.ViewProperty);
-    ctor public deprecated SpringAnimation(android.view.View, android.support.animation.DynamicAnimation.ViewProperty, float);
-    method public void animateToFinalPosition(float);
-    method public boolean canSkipToEnd();
-    method public android.support.animation.SpringForce getSpring();
-    method public android.support.animation.SpringAnimation setSpring(android.support.animation.SpringForce);
-    method public void skipToEnd();
-  }
-
-  public final class SpringForce {
-    ctor public SpringForce();
-    ctor public SpringForce(float);
-    method public float getDampingRatio();
-    method public float getFinalPosition();
-    method public float getStiffness();
-    method public android.support.animation.SpringForce setDampingRatio(float);
-    method public android.support.animation.SpringForce setFinalPosition(float);
-    method public android.support.animation.SpringForce setStiffness(float);
-    field public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
-    field public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
-    field public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
-    field public static final float DAMPING_RATIO_NO_BOUNCY = 1.0f;
-    field public static final float STIFFNESS_HIGH = 10000.0f;
-    field public static final float STIFFNESS_LOW = 200.0f;
-    field public static final float STIFFNESS_MEDIUM = 1500.0f;
-    field public static final float STIFFNESS_VERY_LOW = 50.0f;
-  }
-
-}
-
-package android.support.app.recommendation {
-
-  public final class ContentRecommendation {
-    method public java.lang.String getBackgroundImageUri();
-    method public int getBadgeImageResourceId();
-    method public int getColor();
-    method public android.graphics.Bitmap getContentImage();
-    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
-    method public java.lang.String[] getContentTypes();
-    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
-    method public java.lang.String[] getGenres();
-    method public java.lang.String getGroup();
-    method public java.lang.String getIdTag();
-    method public java.lang.String getMaturityRating();
-    method public android.app.Notification getNotificationObject(android.content.Context);
-    method public java.lang.String getPricingType();
-    method public java.lang.String getPricingValue();
-    method public java.lang.String getPrimaryContentType();
-    method public int getProgressMax();
-    method public int getProgressValue();
-    method public long getRunningTime();
-    method public java.lang.String getSortKey();
-    method public java.lang.String getSourceName();
-    method public int getStatus();
-    method public java.lang.String getText();
-    method public java.lang.String getTitle();
-    method public boolean hasProgressInfo();
-    method public boolean isAutoDismiss();
-    method public void setAutoDismiss(boolean);
-    method public void setGroup(java.lang.String);
-    method public void setProgress(int, int);
-    method public void setSortKey(java.lang.String);
-    method public void setStatus(int);
-    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
-    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
-    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
-    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
-    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
-    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
-    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
-    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
-    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
-    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
-    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
-    field public static final int CONTENT_STATUS_READY = 0; // 0x0
-    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
-    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
-    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
-    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
-    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
-    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
-    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
-    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
-    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
-    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
-    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
-    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
-    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
-    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
-    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
-    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
-    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
-    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
-    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
-  }
-
-  public static final class ContentRecommendation.Builder {
-    ctor public ContentRecommendation.Builder();
-    method public android.support.app.recommendation.ContentRecommendation build();
-    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
-    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
-  }
-
-  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
-  }
-
-  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
-  }
-
-  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
-  }
-
-  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
-  }
-
-  public static class ContentRecommendation.IntentData {
-    ctor public ContentRecommendation.IntentData();
-  }
-
-  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
-  }
-
-  public final class RecommendationExtender implements android.app.Notification.Extender {
-    ctor public RecommendationExtender();
-    ctor public RecommendationExtender(android.app.Notification);
-    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
-    method public java.lang.String[] getContentTypes();
-    method public java.lang.String[] getGenres();
-    method public java.lang.String getMaturityRating();
-    method public java.lang.String getPricingType();
-    method public java.lang.String getPricingValue();
-    method public java.lang.String getPrimaryContentType();
-    method public long getRunningTime();
-    method public int getStatus();
-    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
-    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
-    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
-    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
-    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
-    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
-  }
-
-}
-
-package android.support.customtabs {
-
-  public class CustomTabsCallback {
-    ctor public CustomTabsCallback();
-    method public void extraCallback(java.lang.String, android.os.Bundle);
-    method public void onMessageChannelReady(android.os.Bundle);
-    method public void onNavigationEvent(int, android.os.Bundle);
-    method public void onPostMessage(java.lang.String, android.os.Bundle);
-    field public static final int NAVIGATION_ABORTED = 4; // 0x4
-    field public static final int NAVIGATION_FAILED = 3; // 0x3
-    field public static final int NAVIGATION_FINISHED = 2; // 0x2
-    field public static final int NAVIGATION_STARTED = 1; // 0x1
-    field public static final int TAB_HIDDEN = 6; // 0x6
-    field public static final int TAB_SHOWN = 5; // 0x5
-  }
-
-  public class CustomTabsClient {
-    method public static boolean bindCustomTabsService(android.content.Context, java.lang.String, android.support.customtabs.CustomTabsServiceConnection);
-    method public static boolean connectAndInitialize(android.content.Context, java.lang.String);
-    method public android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
-    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>);
-    method public static java.lang.String getPackageName(android.content.Context, java.util.List<java.lang.String>, boolean);
-    method public android.support.customtabs.CustomTabsSession newSession(android.support.customtabs.CustomTabsCallback);
-    method public boolean warmup(long);
-  }
-
-  public final class CustomTabsIntent {
-    method public static int getMaxToolbarItems();
-    method public void launchUrl(android.content.Context, android.net.Uri);
-    method public static android.content.Intent setAlwaysUseBrowserUI(android.content.Intent);
-    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent);
-    field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
-    field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
-    field public static final java.lang.String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
-    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
-    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
-    field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
-    field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
-    field public static final java.lang.String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
-    field public static final java.lang.String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
-    field public static final java.lang.String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
-    field public static final java.lang.String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
-    field public static final java.lang.String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
-    field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
-    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
-    field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
-    field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
-    field public static final java.lang.String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
-    field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
-    field public static final java.lang.String KEY_ICON = "android.support.customtabs.customaction.ICON";
-    field public static final java.lang.String KEY_ID = "android.support.customtabs.customaction.ID";
-    field public static final java.lang.String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
-    field public static final java.lang.String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
-    field public static final int NO_TITLE = 0; // 0x0
-    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
-    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
-    field public final android.content.Intent intent;
-    field public final android.os.Bundle startAnimationBundle;
-  }
-
-  public static final class CustomTabsIntent.Builder {
-    ctor public CustomTabsIntent.Builder();
-    ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
-    method public android.support.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
-    method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
-    method public deprecated android.support.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, java.lang.String, android.app.PendingIntent) throws java.lang.IllegalStateException;
-    method public android.support.customtabs.CustomTabsIntent build();
-    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
-    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
-    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
-    method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
-    method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
-    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
-    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
-    method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
-    method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
-    method public android.support.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, int, int);
-    method public android.support.customtabs.CustomTabsIntent.Builder setToolbarColor(int);
-  }
-
-  public abstract class CustomTabsService extends android.app.Service {
-    ctor public CustomTabsService();
-    method protected boolean cleanUpSession(android.support.customtabs.CustomTabsSessionToken);
-    method protected abstract android.os.Bundle extraCommand(java.lang.String, android.os.Bundle);
-    method protected abstract boolean mayLaunchUrl(android.support.customtabs.CustomTabsSessionToken, android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
-    method protected abstract boolean newSession(android.support.customtabs.CustomTabsSessionToken);
-    method public android.os.IBinder onBind(android.content.Intent);
-    method protected abstract int postMessage(android.support.customtabs.CustomTabsSessionToken, java.lang.String, android.os.Bundle);
-    method protected abstract boolean requestPostMessageChannel(android.support.customtabs.CustomTabsSessionToken, android.net.Uri);
-    method protected abstract boolean updateVisuals(android.support.customtabs.CustomTabsSessionToken, android.os.Bundle);
-    method protected abstract boolean warmup(long);
-    field public static final java.lang.String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
-    field public static final java.lang.String KEY_URL = "android.support.customtabs.otherurls.URL";
-    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
-    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
-    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
-    field public static final int RESULT_SUCCESS = 0; // 0x0
-  }
-
-  public static abstract class CustomTabsService.Result implements java.lang.annotation.Annotation {
-  }
-
-  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
-    ctor public CustomTabsServiceConnection();
-    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName, android.support.customtabs.CustomTabsClient);
-    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
-  }
-
-  public final class CustomTabsSession {
-    method public boolean mayLaunchUrl(android.net.Uri, android.os.Bundle, java.util.List<android.os.Bundle>);
-    method public int postMessage(java.lang.String, android.os.Bundle);
-    method public boolean requestPostMessageChannel(android.net.Uri);
-    method public boolean setActionButton(android.graphics.Bitmap, java.lang.String);
-    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
-    method public deprecated boolean setToolbarItem(int, android.graphics.Bitmap, java.lang.String);
-  }
-
-  public class CustomTabsSessionToken {
-    method public android.support.customtabs.CustomTabsCallback getCallback();
-    method public static android.support.customtabs.CustomTabsSessionToken getSessionTokenFromIntent(android.content.Intent);
-    method public boolean isAssociatedWith(android.support.customtabs.CustomTabsSession);
-  }
-
-  public class PostMessageService extends android.app.Service {
-    ctor public PostMessageService();
-    method public android.os.IBinder onBind(android.content.Intent);
-  }
-
-  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
-    ctor public PostMessageServiceConnection(android.support.customtabs.CustomTabsSessionToken);
-    method public boolean bindSessionToPostMessageService(android.content.Context, java.lang.String);
-    method public final boolean notifyMessageChannelReady(android.os.Bundle);
-    method public void onPostMessageServiceConnected();
-    method public void onPostMessageServiceDisconnected();
-    method public final void onServiceConnected(android.content.ComponentName, android.os.IBinder);
-    method public final void onServiceDisconnected(android.content.ComponentName);
-    method public final boolean postMessage(java.lang.String, android.os.Bundle);
-    method public void unbindFromContext(android.content.Context);
-  }
-
-}
-
-package android.support.design.widget {
-
-  public class AppBarLayout extends android.widget.LinearLayout {
-    ctor public AppBarLayout(android.content.Context);
-    ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
-    method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
-    method public deprecated float getTargetElevation();
-    method public final int getTotalScrollRange();
-    method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
-    method public void setExpanded(boolean);
-    method public void setExpanded(boolean, boolean);
-    method public deprecated void setTargetElevation(float);
-  }
-
-  public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
-    ctor public AppBarLayout.Behavior();
-    ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
-    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
-    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int, int, int, int);
-    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, float, float, boolean);
-    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[]);
-    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int);
-    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
-    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
-    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int);
-    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View);
-    method public void setDragCallback(android.support.design.widget.AppBarLayout.Behavior.DragCallback);
-  }
-
-  public static abstract class AppBarLayout.Behavior.DragCallback {
-    ctor public AppBarLayout.Behavior.DragCallback();
-    method public abstract boolean canDrag(android.support.design.widget.AppBarLayout);
-  }
-
-  protected static class AppBarLayout.Behavior.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
-    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
-  }
-
-  public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
-    ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public AppBarLayout.LayoutParams(int, int);
-    ctor public AppBarLayout.LayoutParams(int, int, float);
-    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public AppBarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public AppBarLayout.LayoutParams(android.widget.LinearLayout.LayoutParams);
-    ctor public AppBarLayout.LayoutParams(android.support.design.widget.AppBarLayout.LayoutParams);
-    method public int getScrollFlags();
-    method public android.view.animation.Interpolator getScrollInterpolator();
-    method public void setScrollFlags(int);
-    method public void setScrollInterpolator(android.view.animation.Interpolator);
-    field public static final int SCROLL_FLAG_ENTER_ALWAYS = 4; // 0x4
-    field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
-    field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
-    field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
-    field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
-  }
-
-  public static abstract interface AppBarLayout.OnOffsetChangedListener {
-    method public abstract void onOffsetChanged(android.support.design.widget.AppBarLayout, int);
-  }
-
-  public static class AppBarLayout.ScrollingViewBehavior extends android.support.design.widget.HeaderScrollingViewBehavior {
-    ctor public AppBarLayout.ScrollingViewBehavior();
-    ctor public AppBarLayout.ScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
-    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
-    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
-    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
-  }
-
-  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
-    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
-    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
-    method public void dismiss();
-    method public android.content.Context getContext();
-    method public int getDuration();
-    method public android.view.View getView();
-    method public boolean isShown();
-    method public boolean isShownOrQueued();
-    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback<B>);
-    method public B setDuration(int);
-    method public void show();
-    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
-    field public static final int LENGTH_LONG = 0; // 0x0
-    field public static final int LENGTH_SHORT = -1; // 0xffffffff
-  }
-
-  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
-    ctor public BaseTransientBottomBar.BaseCallback();
-    method public void onDismissed(B, int);
-    method public void onShown(B);
-    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
-    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
-    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
-    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
-    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
-  }
-
-  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
-    method public abstract void animateContentIn(int, int);
-    method public abstract void animateContentOut(int, int);
-  }
-
-  public class BottomNavigationView extends android.widget.FrameLayout {
-    ctor public BottomNavigationView(android.content.Context);
-    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
-    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
-    method public int getItemBackgroundResource();
-    method public android.content.res.ColorStateList getItemIconTintList();
-    method public android.content.res.ColorStateList getItemTextColor();
-    method public int getMaxItemCount();
-    method public android.view.Menu getMenu();
-    method public int getSelectedItemId();
-    method public void inflateMenu(int);
-    method public void setItemBackgroundResource(int);
-    method public void setItemIconTintList(android.content.res.ColorStateList);
-    method public void setItemTextColor(android.content.res.ColorStateList);
-    method public void setOnNavigationItemReselectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemReselectedListener);
-    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
-    method public void setSelectedItemId(int);
-  }
-
-  public static abstract interface BottomNavigationView.OnNavigationItemReselectedListener {
-    method public abstract void onNavigationItemReselected(android.view.MenuItem);
-  }
-
-  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
-    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
-  }
-
-  public class BottomSheetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
-    ctor public BottomSheetBehavior();
-    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
-    method public static <V extends android.view.View> android.support.design.widget.BottomSheetBehavior<V> from(V);
-    method public final int getPeekHeight();
-    method public boolean getSkipCollapsed();
-    method public final int getState();
-    method public boolean isHideable();
-    method public void setBottomSheetCallback(android.support.design.widget.BottomSheetBehavior.BottomSheetCallback);
-    method public void setHideable(boolean);
-    method public final void setPeekHeight(int);
-    method public void setSkipCollapsed(boolean);
-    method public final void setState(int);
-    field public static final int PEEK_HEIGHT_AUTO = -1; // 0xffffffff
-    field public static final int STATE_COLLAPSED = 4; // 0x4
-    field public static final int STATE_DRAGGING = 1; // 0x1
-    field public static final int STATE_EXPANDED = 3; // 0x3
-    field public static final int STATE_HIDDEN = 5; // 0x5
-    field public static final int STATE_SETTLING = 2; // 0x2
-  }
-
-  public static abstract class BottomSheetBehavior.BottomSheetCallback {
-    ctor public BottomSheetBehavior.BottomSheetCallback();
-    method public abstract void onSlide(android.view.View, float);
-    method public abstract void onStateChanged(android.view.View, int);
-  }
-
-  protected static class BottomSheetBehavior.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
-    ctor public BottomSheetBehavior.SavedState(android.os.Parcel, java.lang.ClassLoader);
-    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
-    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
-  }
-
-  public class BottomSheetDialog extends android.support.v7.app.AppCompatDialog {
-    ctor public BottomSheetDialog(android.content.Context);
-    ctor public BottomSheetDialog(android.content.Context, int);
-    ctor protected BottomSheetDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
-  }
-
-  public class BottomSheetDialogFragment extends android.support.v7.app.AppCompatDialogFragment {
-    ctor public BottomSheetDialogFragment();
-  }
-
-  public class CollapsingToolbarLayout extends android.widget.FrameLayout {
-    ctor public CollapsingToolbarLayout(android.content.Context);
-    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
-    ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
-    method public int getCollapsedTitleGravity();
-    method public android.graphics.Typeface getCollapsedTitleTypeface();
-    method public android.graphics.drawable.Drawable getContentScrim();
-    method public int getExpandedTitleGravity();
-    method public int getExpandedTitleMarginBottom();
-    method public int getExpandedTitleMarginEnd();
-    method public int getExpandedTitleMarginStart();
-    method public int getExpandedTitleMarginTop();
-    method public android.graphics.Typeface getExpandedTitleTypeface();
-    method public long getScrimAnimationDuration();
-    method public int getScrimVisibleHeightTrigger();
-    method public android.graphics.drawable.Drawable getStatusBarScrim();
-    method public java.lang.CharSequence getTitle();
-    method public boolean isTitleEnabled();
-    method public void setCollapsedTitleGravity(int);
-    method public void setCollapsedTitleTextAppearance(int);
-    method public void setCollapsedTitleTextColor(int);
-    method public void setCollapsedTitleTextColor(android.content.res.ColorStateList);
-    method public void setCollapsedTitleTypeface(android.graphics.Typeface);
-    method public void setContentScrim(android.graphics.drawable.Drawable);
-    method public void setContentScrimColor(int);
-    method public void setContentScrimResource(int);
-    method public void setExpandedTitleColor(int);
-    method public void setExpandedTitleGravity(int);
-    method public void setExpandedTitleMargin(int, int, int, int);
-    method public void setExpandedTitleMarginBottom(int);
-    method public void setExpandedTitleMarginEnd(int);
-    method public void setExpandedTitleMarginStart(int);
-    method public void setExpandedTitleMarginTop(int);
-    method public void setExpandedTitleTextAppearance(int);
-    method public void setExpandedTitleTextColor(android.content.res.ColorStateList);
-    method public void setExpandedTitleTypeface(android.graphics.Typeface);
-    method public void setScrimAnimationDuration(long);
-    method public void setScrimVisibleHeightTrigger(int);
-    method public void setScrimsShown(boolean);
-    method public void setScrimsShown(boolean, boolean);
-    method public void setStatusBarScrim(android.graphics.drawable.Drawable);
-    method public void setStatusBarScrimColor(int);
-    method public void setStatusBarScrimResource(int);
-    method public void setTitle(java.lang.CharSequence);
-    method public void setTitleEnabled(boolean);
-  }
-
-  public static class CollapsingToolbarLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams {
-    ctor public CollapsingToolbarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public CollapsingToolbarLayout.LayoutParams(int, int);
-    ctor public CollapsingToolbarLayout.LayoutParams(int, int, int);
-    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
-    method public int getCollapseMode();
-    method public float getParallaxMultiplier();
-    method public void setCollapseMode(int);
-    method public void setParallaxMultiplier(float);
-    field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
-    field public static final int COLLAPSE_MODE_PARALLAX = 2; // 0x2
-    field public static final int COLLAPSE_MODE_PIN = 1; // 0x1
-  }
-
-  public class CoordinatorLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingParent {
-    ctor public CoordinatorLayout(android.content.Context);
-    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet);
-    ctor public CoordinatorLayout(android.content.Context, android.util.AttributeSet, int);
-    method public void dispatchDependentViewsChanged(android.view.View);
-    method public boolean doViewsOverlap(android.view.View, android.view.View);
-    method public java.util.List<android.view.View> getDependencies(android.view.View);
-    method public java.util.List<android.view.View> getDependents(android.view.View);
-    method public android.graphics.drawable.Drawable getStatusBarBackground();
-    method public boolean isPointInChildBounds(android.view.View, int, int);
-    method public void onAttachedToWindow();
-    method public void onDetachedFromWindow();
-    method public void onDraw(android.graphics.Canvas);
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void onLayoutChild(android.view.View, int);
-    method public void onMeasureChild(android.view.View, int, int, int, int);
-    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
-    method public void setStatusBarBackgroundColor(int);
-    method public void setStatusBarBackgroundResource(int);
-  }
-
-  public static abstract class CoordinatorLayout.Behavior<V extends android.view.View> {
-    ctor public CoordinatorLayout.Behavior();
-    ctor public CoordinatorLayout.Behavior(android.content.Context, android.util.AttributeSet);
-    method public boolean blocksInteractionBelow(android.support.design.widget.CoordinatorLayout, V);
-    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect);
-    method public int getScrimColor(android.support.design.widget.CoordinatorLayout, V);
-    method public float getScrimOpacity(android.support.design.widget.CoordinatorLayout, V);
-    method public static java.lang.Object getTag(android.view.View);
-    method public deprecated boolean isDirty(android.support.design.widget.CoordinatorLayout, V);
-    method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, V, android.view.View);
-    method public android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.support.design.widget.CoordinatorLayout, V, android.support.v4.view.WindowInsetsCompat);
-    method public void onAttachedToLayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
-    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, V, android.view.View);
-    method public void onDependentViewRemoved(android.support.design.widget.CoordinatorLayout, V, android.view.View);
-    method public void onDetachedFromLayoutParams();
-    method public boolean onInterceptTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
-    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, V, int);
-    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, V, int, int, int, int);
-    method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float, boolean);
-    method public boolean onNestedPreFling(android.support.design.widget.CoordinatorLayout, V, android.view.View, float, float);
-    method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
-    method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
-    method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
-    method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, V, android.graphics.Rect, boolean);
-    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
-    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
-    method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
-    method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
-    method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
-    method public static void setTag(android.view.View, java.lang.Object);
-  }
-
-  public static abstract class CoordinatorLayout.DefaultBehavior implements java.lang.annotation.Annotation {
-  }
-
-  public static class CoordinatorLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public CoordinatorLayout.LayoutParams(int, int);
-    ctor public CoordinatorLayout.LayoutParams(android.support.design.widget.CoordinatorLayout.LayoutParams);
-    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public CoordinatorLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    method public int getAnchorId();
-    method public android.support.design.widget.CoordinatorLayout.Behavior getBehavior();
-    method public void setAnchorId(int);
-    method public void setBehavior(android.support.design.widget.CoordinatorLayout.Behavior);
-    field public int anchorGravity;
-    field public int dodgeInsetEdges;
-    field public int gravity;
-    field public int insetEdge;
-    field public int keyline;
-  }
-
-  protected static class CoordinatorLayout.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public CoordinatorLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
-    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
-  }
-
-  public class FloatingActionButton extends android.support.design.widget.VisibilityAwareImageButton {
-    ctor public FloatingActionButton(android.content.Context);
-    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
-    ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
-    method public float getCompatElevation();
-    method public android.graphics.drawable.Drawable getContentBackground();
-    method public boolean getContentRect(android.graphics.Rect);
-    method public int getRippleColor();
-    method public int getSize();
-    method public boolean getUseCompatPadding();
-    method public void hide();
-    method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
-    method public void setCompatElevation(float);
-    method public void setRippleColor(int);
-    method public void setSize(int);
-    method public void setUseCompatPadding(boolean);
-    method public void show();
-    method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
-    field public static final int SIZE_AUTO = -1; // 0xffffffff
-    field public static final int SIZE_MINI = 1; // 0x1
-    field public static final int SIZE_NORMAL = 0; // 0x0
-  }
-
-  public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
-    ctor public FloatingActionButton.Behavior();
-    ctor public FloatingActionButton.Behavior(android.content.Context, android.util.AttributeSet);
-    method public boolean getInsetDodgeRect(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.graphics.Rect);
-    method public boolean isAutoHideEnabled();
-    method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, android.view.View);
-    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.FloatingActionButton, int);
-    method public void setAutoHideEnabled(boolean);
-  }
-
-  public static abstract class FloatingActionButton.OnVisibilityChangedListener {
-    ctor public FloatingActionButton.OnVisibilityChangedListener();
-    method public void onHidden(android.support.design.widget.FloatingActionButton);
-    method public void onShown(android.support.design.widget.FloatingActionButton);
-  }
-
-   abstract class HeaderBehavior<V extends android.view.View> extends android.support.design.widget.ViewOffsetBehavior {
-    ctor public HeaderBehavior();
-    ctor public HeaderBehavior(android.content.Context, android.util.AttributeSet);
-  }
-
-   abstract class HeaderScrollingViewBehavior extends android.support.design.widget.ViewOffsetBehavior {
-    ctor public HeaderScrollingViewBehavior();
-    ctor public HeaderScrollingViewBehavior(android.content.Context, android.util.AttributeSet);
-    method public final int getOverlayTop();
-    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
-    method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
-    method public final void setOverlayTop(int);
-  }
-
-  public class NavigationView extends android.widget.FrameLayout {
-    ctor public NavigationView(android.content.Context);
-    ctor public NavigationView(android.content.Context, android.util.AttributeSet);
-    ctor public NavigationView(android.content.Context, android.util.AttributeSet, int);
-    method public void addHeaderView(android.view.View);
-    method public int getHeaderCount();
-    method public android.view.View getHeaderView(int);
-    method public android.graphics.drawable.Drawable getItemBackground();
-    method public android.content.res.ColorStateList getItemIconTintList();
-    method public android.content.res.ColorStateList getItemTextColor();
-    method public android.view.Menu getMenu();
-    method public android.view.View inflateHeaderView(int);
-    method public void inflateMenu(int);
-    method public void removeHeaderView(android.view.View);
-    method public void setCheckedItem(int);
-    method public void setItemBackground(android.graphics.drawable.Drawable);
-    method public void setItemBackgroundResource(int);
-    method public void setItemIconTintList(android.content.res.ColorStateList);
-    method public void setItemTextAppearance(int);
-    method public void setItemTextColor(android.content.res.ColorStateList);
-    method public void setNavigationItemSelectedListener(android.support.design.widget.NavigationView.OnNavigationItemSelectedListener);
-  }
-
-  public static abstract interface NavigationView.OnNavigationItemSelectedListener {
-    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
-  }
-
-  public static class NavigationView.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public NavigationView.SavedState(android.os.Parcel, java.lang.ClassLoader);
-    ctor public NavigationView.SavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.design.widget.NavigationView.SavedState> CREATOR;
-    field public android.os.Bundle menuState;
-  }
-
-  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
-    method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
-    method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
-    method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
-    method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
-    method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
-    method public android.support.design.widget.Snackbar setActionTextColor(int);
-    method public deprecated android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
-    method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
-    method public android.support.design.widget.Snackbar setText(int);
-    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
-    field public static final int LENGTH_LONG = 0; // 0x0
-    field public static final int LENGTH_SHORT = -1; // 0xffffffff
-  }
-
-  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
-    ctor public Snackbar.Callback();
-    method public void onDismissed(android.support.design.widget.Snackbar, int);
-    method public void onShown(android.support.design.widget.Snackbar);
-    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
-    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
-    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
-    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
-    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
-  }
-
-  public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
-    ctor public SwipeDismissBehavior();
-    method public boolean canSwipeDismissView(android.view.View);
-    method public int getDragState();
-    method public void setDragDismissDistance(float);
-    method public void setEndAlphaSwipeDistance(float);
-    method public void setListener(android.support.design.widget.SwipeDismissBehavior.OnDismissListener);
-    method public void setSensitivity(float);
-    method public void setStartAlphaSwipeDistance(float);
-    method public void setSwipeDirection(int);
-    field public static final int STATE_DRAGGING = 1; // 0x1
-    field public static final int STATE_IDLE = 0; // 0x0
-    field public static final int STATE_SETTLING = 2; // 0x2
-    field public static final int SWIPE_DIRECTION_ANY = 2; // 0x2
-    field public static final int SWIPE_DIRECTION_END_TO_START = 1; // 0x1
-    field public static final int SWIPE_DIRECTION_START_TO_END = 0; // 0x0
-  }
-
-  public static abstract interface SwipeDismissBehavior.OnDismissListener {
-    method public abstract void onDismiss(android.view.View);
-    method public abstract void onDragStateChanged(int);
-  }
-
-  public final class TabItem extends android.view.View {
-    ctor public TabItem(android.content.Context);
-    ctor public TabItem(android.content.Context, android.util.AttributeSet);
-  }
-
-  public class TabLayout extends android.widget.HorizontalScrollView {
-    ctor public TabLayout(android.content.Context);
-    ctor public TabLayout(android.content.Context, android.util.AttributeSet);
-    ctor public TabLayout(android.content.Context, android.util.AttributeSet, int);
-    method public void addOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
-    method public void addTab(android.support.design.widget.TabLayout.Tab);
-    method public void addTab(android.support.design.widget.TabLayout.Tab, int);
-    method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
-    method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
-    method public void clearOnTabSelectedListeners();
-    method public int getSelectedTabPosition();
-    method public android.support.design.widget.TabLayout.Tab getTabAt(int);
-    method public int getTabCount();
-    method public int getTabGravity();
-    method public int getTabMode();
-    method public android.content.res.ColorStateList getTabTextColors();
-    method public android.support.design.widget.TabLayout.Tab newTab();
-    method public void removeAllTabs();
-    method public void removeOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
-    method public void removeTab(android.support.design.widget.TabLayout.Tab);
-    method public void removeTabAt(int);
-    method public deprecated void setOnTabSelectedListener(android.support.design.widget.TabLayout.OnTabSelectedListener);
-    method public void setScrollPosition(int, float, boolean);
-    method public void setSelectedTabIndicatorColor(int);
-    method public void setSelectedTabIndicatorHeight(int);
-    method public void setTabGravity(int);
-    method public void setTabMode(int);
-    method public void setTabTextColors(android.content.res.ColorStateList);
-    method public void setTabTextColors(int, int);
-    method public deprecated void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
-    method public void setupWithViewPager(android.support.v4.view.ViewPager);
-    method public void setupWithViewPager(android.support.v4.view.ViewPager, boolean);
-    field public static final int GRAVITY_CENTER = 1; // 0x1
-    field public static final int GRAVITY_FILL = 0; // 0x0
-    field public static final int MODE_FIXED = 1; // 0x1
-    field public static final int MODE_SCROLLABLE = 0; // 0x0
-  }
-
-  public static abstract interface TabLayout.OnTabSelectedListener {
-    method public abstract void onTabReselected(android.support.design.widget.TabLayout.Tab);
-    method public abstract void onTabSelected(android.support.design.widget.TabLayout.Tab);
-    method public abstract void onTabUnselected(android.support.design.widget.TabLayout.Tab);
-  }
-
-  public static final class TabLayout.Tab {
-    method public java.lang.CharSequence getContentDescription();
-    method public android.view.View getCustomView();
-    method public android.graphics.drawable.Drawable getIcon();
-    method public int getPosition();
-    method public java.lang.Object getTag();
-    method public java.lang.CharSequence getText();
-    method public boolean isSelected();
-    method public void select();
-    method public android.support.design.widget.TabLayout.Tab setContentDescription(int);
-    method public android.support.design.widget.TabLayout.Tab setContentDescription(java.lang.CharSequence);
-    method public android.support.design.widget.TabLayout.Tab setCustomView(android.view.View);
-    method public android.support.design.widget.TabLayout.Tab setCustomView(int);
-    method public android.support.design.widget.TabLayout.Tab setIcon(android.graphics.drawable.Drawable);
-    method public android.support.design.widget.TabLayout.Tab setIcon(int);
-    method public android.support.design.widget.TabLayout.Tab setTag(java.lang.Object);
-    method public android.support.design.widget.TabLayout.Tab setText(java.lang.CharSequence);
-    method public android.support.design.widget.TabLayout.Tab setText(int);
-    field public static final int INVALID_POSITION = -1; // 0xffffffff
-  }
-
-  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
-    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
-    method public void onPageScrollStateChanged(int);
-    method public void onPageScrolled(int, float, int);
-    method public void onPageSelected(int);
-  }
-
-  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
-    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
-    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
-    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
-    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
-  }
-
-  public class TextInputEditText extends android.support.v7.widget.AppCompatEditText {
-    ctor public TextInputEditText(android.content.Context);
-    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet);
-    ctor public TextInputEditText(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class TextInputLayout extends android.widget.LinearLayout {
-    ctor public TextInputLayout(android.content.Context);
-    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
-    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
-    method public int getCounterMaxLength();
-    method public android.widget.EditText getEditText();
-    method public java.lang.CharSequence getError();
-    method public java.lang.CharSequence getHint();
-    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
-    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
-    method public android.graphics.Typeface getTypeface();
-    method public boolean isCounterEnabled();
-    method public boolean isErrorEnabled();
-    method public boolean isHintAnimationEnabled();
-    method public boolean isHintEnabled();
-    method public boolean isPasswordVisibilityToggleEnabled();
-    method public android.os.Parcelable onSaveInstanceState();
-    method public void setCounterEnabled(boolean);
-    method public void setCounterMaxLength(int);
-    method public void setError(java.lang.CharSequence);
-    method public void setErrorEnabled(boolean);
-    method public void setErrorTextAppearance(int);
-    method public void setHint(java.lang.CharSequence);
-    method public void setHintAnimationEnabled(boolean);
-    method public void setHintEnabled(boolean);
-    method public void setHintTextAppearance(int);
-    method public void setPasswordVisibilityToggleContentDescription(int);
-    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
-    method public void setPasswordVisibilityToggleDrawable(int);
-    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
-    method public void setPasswordVisibilityToggleEnabled(boolean);
-    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
-    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
-    method public void setTypeface(android.graphics.Typeface);
-  }
-
-   class ViewOffsetBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
-    ctor public ViewOffsetBehavior();
-    ctor public ViewOffsetBehavior(android.content.Context, android.util.AttributeSet);
-    method public int getLeftAndRightOffset();
-    method public int getTopAndBottomOffset();
-    method protected void layoutChild(android.support.design.widget.CoordinatorLayout, V, int);
-    method public boolean setLeftAndRightOffset(int);
-    method public boolean setTopAndBottomOffset(int);
-  }
-
-   class VisibilityAwareImageButton extends android.widget.ImageButton {
-    ctor public VisibilityAwareImageButton(android.content.Context);
-    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet);
-    ctor public VisibilityAwareImageButton(android.content.Context, android.util.AttributeSet, int);
-  }
-
-}
-
-package android.support.graphics.drawable {
-
-  public abstract interface Animatable2Compat {
-    method public abstract void clearAnimationCallbacks();
-    method public abstract void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public abstract boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
-  }
-
-  public static abstract class Animatable2Compat.AnimationCallback {
-    ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable);
-    method public void onAnimationStart(android.graphics.drawable.Drawable);
-  }
-
-  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon implements android.support.graphics.drawable.Animatable2Compat {
-    method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable);
-    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
-    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas);
-    method public int getOpacity();
-    method public boolean isRunning();
-    method public void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void start();
-    method public void stop();
-    method public boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
-  }
-
-   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
-  }
-
-  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
-    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
-    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas);
-    method public int getOpacity();
-    method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter);
-  }
-
-}
-
-package android.support.media {
-
-  public class ExifInterface {
-    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
-    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
-    method public double getAltitude(double);
-    method public java.lang.String getAttribute(java.lang.String);
-    method public double getAttributeDouble(java.lang.String, double);
-    method public int getAttributeInt(java.lang.String, int);
-    method public deprecated boolean getLatLong(float[]);
-    method public double[] getLatLong();
-    method public byte[] getThumbnail();
-    method public android.graphics.Bitmap getThumbnailBitmap();
-    method public byte[] getThumbnailBytes();
-    method public long[] getThumbnailRange();
-    method public boolean hasThumbnail();
-    method public boolean isThumbnailCompressed();
-    method public void saveAttributes() throws java.io.IOException;
-    method public void setAttribute(java.lang.String, java.lang.String);
-    method public void setLatLong(double, double);
-    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
-    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
-    field public static final int ORIENTATION_NORMAL = 1; // 0x1
-    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
-    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
-    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
-    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
-    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
-    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
-    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
-    field public static final java.lang.String TAG_ARTIST = "Artist";
-    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
-    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
-    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
-    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
-    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
-    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
-    field public static final java.lang.String TAG_COMPRESSION = "Compression";
-    field public static final java.lang.String TAG_CONTRAST = "Contrast";
-    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
-    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
-    field public static final java.lang.String TAG_DATETIME = "DateTime";
-    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
-    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
-    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
-    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
-    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
-    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
-    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
-    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
-    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
-    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
-    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
-    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
-    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
-    field public static final java.lang.String TAG_FLASH = "Flash";
-    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
-    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
-    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
-    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
-    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
-    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
-    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
-    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
-    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
-    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
-    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
-    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
-    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
-    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
-    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
-    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
-    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
-    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
-    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
-    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
-    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
-    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
-    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
-    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
-    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
-    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
-    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
-    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
-    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
-    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
-    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
-    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
-    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
-    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
-    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
-    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
-    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
-    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
-    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
-    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
-    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
-    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
-    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
-    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
-    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
-    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
-    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
-    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
-    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
-    field public static final java.lang.String TAG_MAKE = "Make";
-    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
-    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
-    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
-    field public static final java.lang.String TAG_MODEL = "Model";
-    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
-    field public static final java.lang.String TAG_OECF = "OECF";
-    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
-    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
-    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
-    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
-    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
-    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
-    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
-    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
-    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
-    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
-    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
-    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
-    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
-    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
-    field public static final java.lang.String TAG_RW2_ISO = "ISO";
-    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
-    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
-    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
-    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
-    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
-    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
-    field public static final java.lang.String TAG_SATURATION = "Saturation";
-    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
-    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
-    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
-    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
-    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
-    field public static final java.lang.String TAG_SOFTWARE = "Software";
-    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
-    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
-    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
-    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
-    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
-    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
-    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
-    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
-    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
-    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
-    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
-    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
-    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
-    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
-    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
-    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
-    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
-    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
-    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
-    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
-    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
-    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
-    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
-    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
-    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
-  }
-
-}
-
-package android.support.percent {
-
-  public class PercentFrameLayout extends android.widget.FrameLayout {
-    ctor public PercentFrameLayout(android.content.Context);
-    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
-    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public static class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
-    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public PercentFrameLayout.LayoutParams(int, int);
-    ctor public PercentFrameLayout.LayoutParams(int, int, int);
-    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
-    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
-    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
-  }
-
-  public class PercentLayoutHelper {
-    ctor public PercentLayoutHelper(android.view.ViewGroup);
-    method public void adjustChildren(int, int);
-    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
-    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
-    method public boolean handleMeasuredStateTooSmall();
-    method public void restoreOriginalParams();
-  }
-
-  public static class PercentLayoutHelper.PercentLayoutInfo {
-    ctor public PercentLayoutHelper.PercentLayoutInfo();
-    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
-    method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
-    method public void fillMarginLayoutParams(android.view.View, android.view.ViewGroup.MarginLayoutParams, int, int);
-    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
-    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    field public float aspectRatio;
-    field public float bottomMarginPercent;
-    field public float endMarginPercent;
-    field public float heightPercent;
-    field public float leftMarginPercent;
-    field public float rightMarginPercent;
-    field public float startMarginPercent;
-    field public float topMarginPercent;
-    field public float widthPercent;
-  }
-
-  public static abstract interface PercentLayoutHelper.PercentLayoutParams {
-    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
-  }
-
-  public class PercentRelativeLayout extends android.widget.RelativeLayout {
-    ctor public PercentRelativeLayout(android.content.Context);
-    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
-    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public static class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
-    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public PercentRelativeLayout.LayoutParams(int, int);
-    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
-  }
-
-}
-
-package android.support.transition {
-
-  public class AutoTransition extends android.support.transition.TransitionSet {
-    ctor public AutoTransition();
-  }
-
-  public class ChangeBounds extends android.support.transition.Transition {
-    ctor public ChangeBounds();
-    method public void captureEndValues(android.support.transition.TransitionValues);
-    method public void captureStartValues(android.support.transition.TransitionValues);
-    method public void setResizeClip(boolean);
-  }
-
-  public class Fade extends android.support.transition.Visibility {
-    ctor public Fade(int);
-    ctor public Fade();
-    field public static final int IN = 1; // 0x1
-    field public static final int OUT = 2; // 0x2
-  }
-
-  public class Scene {
-    ctor public Scene(android.view.ViewGroup);
-    ctor public Scene(android.view.ViewGroup, android.view.View);
-    method public void enter();
-    method public void exit();
-    method public static android.support.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
-    method public android.view.ViewGroup getSceneRoot();
-    method public void setEnterAction(java.lang.Runnable);
-    method public void setExitAction(java.lang.Runnable);
-  }
-
-  public abstract class Transition {
-    ctor public Transition();
-    method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
-    method public android.support.transition.Transition addTarget(android.view.View);
-    method public android.support.transition.Transition addTarget(int);
-    method public abstract void captureEndValues(android.support.transition.TransitionValues);
-    method public abstract void captureStartValues(android.support.transition.TransitionValues);
-    method public android.animation.Animator createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
-    method public android.support.transition.Transition excludeChildren(android.view.View, boolean);
-    method public android.support.transition.Transition excludeChildren(int, boolean);
-    method public android.support.transition.Transition excludeChildren(java.lang.Class, boolean);
-    method public android.support.transition.Transition excludeTarget(android.view.View, boolean);
-    method public android.support.transition.Transition excludeTarget(int, boolean);
-    method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
-    method public long getDuration();
-    method public android.animation.TimeInterpolator getInterpolator();
-    method public java.lang.String getName();
-    method public long getStartDelay();
-    method public java.util.List<java.lang.Integer> getTargetIds();
-    method public java.util.List<android.view.View> getTargets();
-    method public java.lang.String[] getTransitionProperties();
-    method public android.support.transition.TransitionValues getTransitionValues(android.view.View, boolean);
-    method public android.support.transition.Transition removeListener(android.support.transition.Transition.TransitionListener);
-    method public android.support.transition.Transition removeTarget(android.view.View);
-    method public android.support.transition.Transition removeTarget(int);
-    method public android.support.transition.Transition setDuration(long);
-    method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
-    method public android.support.transition.Transition setStartDelay(long);
-  }
-
-  public static abstract interface Transition.TransitionListener {
-    method public abstract void onTransitionCancel(android.support.transition.Transition);
-    method public abstract void onTransitionEnd(android.support.transition.Transition);
-    method public abstract void onTransitionPause(android.support.transition.Transition);
-    method public abstract void onTransitionResume(android.support.transition.Transition);
-    method public abstract void onTransitionStart(android.support.transition.Transition);
-  }
-
-  public class TransitionManager {
-    ctor public TransitionManager();
-    method public static void beginDelayedTransition(android.view.ViewGroup);
-    method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
-    method public static void go(android.support.transition.Scene);
-    method public static void go(android.support.transition.Scene, android.support.transition.Transition);
-    method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
-    method public void setTransition(android.support.transition.Scene, android.support.transition.Scene, android.support.transition.Transition);
-    method public void transitionTo(android.support.transition.Scene);
-  }
-
-  public class TransitionSet extends android.support.transition.Transition {
-    ctor public TransitionSet();
-    method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
-    method public void captureEndValues(android.support.transition.TransitionValues);
-    method public void captureStartValues(android.support.transition.TransitionValues);
-    method public int getOrdering();
-    method public android.support.transition.TransitionSet removeTransition(android.support.transition.Transition);
-    method public android.support.transition.TransitionSet setOrdering(int);
-    field public static final int ORDERING_SEQUENTIAL = 1; // 0x1
-    field public static final int ORDERING_TOGETHER = 0; // 0x0
-  }
-
-  public class TransitionValues {
-    ctor public TransitionValues();
-    field public final java.util.Map<java.lang.String, java.lang.Object> values;
-    field public android.view.View view;
-  }
-
-  public abstract class Visibility extends android.support.transition.Transition {
-    ctor public Visibility();
-    method public void captureEndValues(android.support.transition.TransitionValues);
-    method public void captureStartValues(android.support.transition.TransitionValues);
-    method public boolean isVisible(android.support.transition.TransitionValues);
-    method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
-    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
-  }
-
-}
-
-package android.support.v13.app {
-
-  public class ActivityCompat extends android.support.v4.app.ActivityCompat {
-    ctor protected ActivityCompat();
-    method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
-  }
-
-  public class FragmentCompat {
-    ctor public FragmentCompat();
-    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
-    method public static void setMenuVisibility(android.app.Fragment, boolean);
-    method public static void setUserVisibleHint(android.app.Fragment, boolean);
-    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
-  }
-
-  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
-    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
-  }
-
-  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
-    ctor public FragmentPagerAdapter(android.app.FragmentManager);
-    method public abstract android.app.Fragment getItem(int);
-    method public long getItemId(int);
-    method public boolean isViewFromObject(android.view.View, java.lang.Object);
-  }
-
-  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
-    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
-    method public abstract android.app.Fragment getItem(int);
-    method public boolean isViewFromObject(android.view.View, java.lang.Object);
-  }
-
-  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
-    ctor public FragmentTabHost(android.content.Context);
-    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
-    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
-    method public void onTabChanged(java.lang.String);
-    method public void setup(android.content.Context, android.app.FragmentManager);
-    method public void setup(android.content.Context, android.app.FragmentManager, int);
-  }
-
-}
-
-package android.support.v13.view {
-
-  public final class DragAndDropPermissionsCompat {
-    method public void release();
-  }
-
-  public class DragStartHelper {
-    ctor public DragStartHelper(android.view.View, android.support.v13.view.DragStartHelper.OnDragStartListener);
-    method public void attach();
-    method public void detach();
-    method public void getTouchPosition(android.graphics.Point);
-    method public boolean onLongClick(android.view.View);
-    method public boolean onTouch(android.view.View, android.view.MotionEvent);
-  }
-
-  public static abstract interface DragStartHelper.OnDragStartListener {
-    method public abstract boolean onDragStart(android.view.View, android.support.v13.view.DragStartHelper);
-  }
-
-  public class ViewCompat extends android.support.v4.view.ViewCompat {
-    method public static void cancelDragAndDrop(android.view.View);
-    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
-    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
-  }
-
-}
-
-package android.support.v13.view.inputmethod {
-
-  public final class EditorInfoCompat {
-    ctor public EditorInfoCompat();
-    method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
-    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
-  }
-
-  public final class InputConnectionCompat {
-    ctor public InputConnectionCompat();
-    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
-    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, android.support.v13.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
-    field public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
-  }
-
-  public static abstract interface InputConnectionCompat.OnCommitContentListener {
-    method public abstract boolean onCommitContent(android.support.v13.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
-  }
-
-  public final class InputContentInfoCompat {
-    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri);
-    method public android.net.Uri getContentUri();
-    method public android.content.ClipDescription getDescription();
-    method public android.net.Uri getLinkUri();
-    method public void releasePermission();
-    method public void requestPermission();
-    method public java.lang.Object unwrap();
-    method public static android.support.v13.view.inputmethod.InputContentInfoCompat wrap(java.lang.Object);
-  }
-
-}
-
-package android.support.v14.preference {
-
-  public class EditTextPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
-    ctor public EditTextPreferenceDialogFragment();
-    method public static android.support.v14.preference.EditTextPreferenceDialogFragment newInstance(java.lang.String);
-    method public void onDialogClosed(boolean);
-  }
-
-  public class ListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
-    ctor public ListPreferenceDialogFragment();
-    method public static android.support.v14.preference.ListPreferenceDialogFragment newInstance(java.lang.String);
-    method public void onDialogClosed(boolean);
-  }
-
-  public class MultiSelectListPreference extends android.support.v7.preference.DialogPreference {
-    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
-    ctor public MultiSelectListPreference(android.content.Context);
-    method public int findIndexOfValue(java.lang.String);
-    method public java.lang.CharSequence[] getEntries();
-    method public java.lang.CharSequence[] getEntryValues();
-    method protected boolean[] getSelectedItems();
-    method public java.util.Set<java.lang.String> getValues();
-    method public void setEntries(java.lang.CharSequence[]);
-    method public void setEntries(int);
-    method public void setEntryValues(java.lang.CharSequence[]);
-    method public void setEntryValues(int);
-    method public void setValues(java.util.Set<java.lang.String>);
-  }
-
-  public class MultiSelectListPreferenceDialogFragment extends android.support.v14.preference.PreferenceDialogFragment {
-    ctor public MultiSelectListPreferenceDialogFragment();
-    method public static android.support.v14.preference.MultiSelectListPreferenceDialogFragment newInstance(java.lang.String);
-    method public void onDialogClosed(boolean);
-  }
-
-  public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
-    ctor public PreferenceDialogFragment();
-    method public android.support.v7.preference.DialogPreference getPreference();
-    method protected void onBindDialogView(android.view.View);
-    method public void onClick(android.content.DialogInterface, int);
-    method protected android.view.View onCreateDialogView(android.content.Context);
-    method public abstract void onDialogClosed(boolean);
-    method protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder);
-    field protected static final java.lang.String ARG_KEY = "key";
-  }
-
-  public abstract class PreferenceFragment extends android.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
-    ctor public PreferenceFragment();
-    method public void addPreferencesFromResource(int);
-    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
-    method public final android.support.v7.widget.RecyclerView getListView();
-    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
-    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
-    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
-    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
-    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
-    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
-    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
-    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
-    method public void scrollToPreference(java.lang.String);
-    method public void scrollToPreference(android.support.v7.preference.Preference);
-    method public void setDivider(android.graphics.drawable.Drawable);
-    method public void setDividerHeight(int);
-    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
-    method public void setPreferencesFromResource(int, java.lang.String);
-    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
-  }
-
-  public static abstract interface PreferenceFragment.OnPreferenceDisplayDialogCallback {
-    method public abstract boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
-  }
-
-  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
-    method public abstract boolean onPreferenceStartFragment(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
-  }
-
-  public static abstract interface PreferenceFragment.OnPreferenceStartScreenCallback {
-    method public abstract boolean onPreferenceStartScreen(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.PreferenceScreen);
-  }
-
-  public class SwitchPreference extends android.support.v7.preference.TwoStatePreference {
-    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
-    ctor public SwitchPreference(android.content.Context);
-    method public java.lang.CharSequence getSwitchTextOff();
-    method public java.lang.CharSequence getSwitchTextOn();
-    method public void setSwitchTextOff(java.lang.CharSequence);
-    method public void setSwitchTextOff(int);
-    method public void setSwitchTextOn(java.lang.CharSequence);
-    method public void setSwitchTextOn(int);
-  }
-
-}
-
-package android.support.v17.leanback.app {
-
-  public final class BackgroundManager {
-    method public void attach(android.view.Window);
-    method public void attachToView(android.view.View);
-    method public void clearDrawable();
-    method public final int getColor();
-    method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
-    method public deprecated android.graphics.drawable.Drawable getDimLayer();
-    method public android.graphics.drawable.Drawable getDrawable();
-    method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
-    method public boolean isAttached();
-    method public boolean isAutoReleaseOnStop();
-    method public void release();
-    method public void setAutoReleaseOnStop(boolean);
-    method public void setBitmap(android.graphics.Bitmap);
-    method public void setColor(int);
-    method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
-    method public void setDrawable(android.graphics.drawable.Drawable);
-    method public void setThemeDrawableResourceId(int);
-  }
-
-   abstract class BaseRowFragment extends android.app.Fragment {
-    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
-    method public int getSelectedPosition();
-    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
-    method public void onTransitionEnd();
-    method public boolean onTransitionPrepare();
-    method public void onTransitionStart();
-    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setAlignment(int);
-    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-  }
-
-   abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment {
-    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
-    method public int getSelectedPosition();
-    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
-    method public void onTransitionEnd();
-    method public boolean onTransitionPrepare();
-    method public void onTransitionStart();
-    method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setAlignment(int);
-    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-  }
-
-  public class BrandedFragment extends android.app.Fragment {
-    ctor public BrandedFragment();
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public int getSearchAffordanceColor();
-    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
-    method public java.lang.CharSequence getTitle();
-    method public android.view.View getTitleView();
-    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
-    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public final boolean isShowingTitle();
-    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
-    method public void setSearchAffordanceColor(int);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setTitle(java.lang.CharSequence);
-    method public void setTitleView(android.view.View);
-    method public void showTitle(boolean);
-    method public void showTitle(int);
-  }
-
-  public class BrandedSupportFragment extends android.support.v4.app.Fragment {
-    ctor public BrandedSupportFragment();
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public int getSearchAffordanceColor();
-    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
-    method public java.lang.CharSequence getTitle();
-    method public android.view.View getTitleView();
-    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
-    method public void installTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public final boolean isShowingTitle();
-    method public android.view.View onInflateTitleView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
-    method public void setSearchAffordanceColor(int);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setTitle(java.lang.CharSequence);
-    method public void setTitleView(android.view.View);
-    method public void showTitle(boolean);
-    method public void showTitle(int);
-  }
-
-  public class BrowseFragment extends android.support.v17.leanback.app.BrandedFragment {
-    ctor public BrowseFragment();
-    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
-    method protected java.lang.Object createEntranceTransition();
-    method public void enableMainFragmentScaling(boolean);
-    method public deprecated void enableRowScaling(boolean);
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public int getBrandColor();
-    method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
-    method public int getHeadersState();
-    method public android.app.Fragment getMainFragment();
-    method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
-    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
-    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
-    method public int getSelectedPosition();
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
-    method public final boolean isHeadersTransitionOnBackEnabled();
-    method public boolean isInHeadersTransition();
-    method public boolean isShowingHeaders();
-    method public android.support.v17.leanback.app.HeadersFragment onCreateHeadersFragment();
-    method protected void onEntranceTransitionEnd();
-    method protected void onEntranceTransitionPrepare();
-    method protected void onEntranceTransitionStart();
-    method protected void runEntranceTransition(java.lang.Object);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setBrandColor(int);
-    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener);
-    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
-    method public void setHeadersState(int);
-    method public final void setHeadersTransitionOnBackEnabled(boolean);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-    method public void startHeadersTransition(boolean);
-    field public static final int HEADERS_DISABLED = 3; // 0x3
-    field public static final int HEADERS_ENABLED = 1; // 0x1
-    field public static final int HEADERS_HIDDEN = 2; // 0x2
-  }
-
-  public static class BrowseFragment.BrowseTransitionListener {
-    ctor public BrowseFragment.BrowseTransitionListener();
-    method public void onHeadersTransitionStart(boolean);
-    method public void onHeadersTransitionStop(boolean);
-  }
-
-  public static abstract class BrowseFragment.FragmentFactory<T extends android.app.Fragment> {
-    ctor public BrowseFragment.FragmentFactory();
-    method public abstract T createFragment(java.lang.Object);
-  }
-
-  public static abstract interface BrowseFragment.FragmentHost {
-    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
-    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter);
-    method public abstract void showTitleView(boolean);
-  }
-
-  public static class BrowseFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseFragment.FragmentFactory {
-    ctor public BrowseFragment.ListRowFragmentFactory();
-    method public android.support.v17.leanback.app.RowsFragment createFragment(java.lang.Object);
-  }
-
-  public static class BrowseFragment.MainFragmentAdapter<T extends android.app.Fragment> {
-    ctor public BrowseFragment.MainFragmentAdapter(T);
-    method public final T getFragment();
-    method public final android.support.v17.leanback.app.BrowseFragment.FragmentHost getFragmentHost();
-    method public boolean isScalingEnabled();
-    method public boolean isScrolling();
-    method public void onTransitionEnd();
-    method public boolean onTransitionPrepare();
-    method public void onTransitionStart();
-    method public void setAlignment(int);
-    method public void setEntranceTransitionState(boolean);
-    method public void setExpand(boolean);
-    method public void setScalingEnabled(boolean);
-  }
-
-  public static abstract interface BrowseFragment.MainFragmentAdapterProvider {
-    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
-  }
-
-  public static final class BrowseFragment.MainFragmentAdapterRegistry {
-    ctor public BrowseFragment.MainFragmentAdapterRegistry();
-    method public android.app.Fragment createFragment(java.lang.Object);
-    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseFragment.FragmentFactory);
-  }
-
-  public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
-    ctor public BrowseFragment.MainFragmentRowsAdapter(T);
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
-    method public final T getFragment();
-    method public int getSelectedPosition();
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-    method public void setSelectedPosition(int, boolean);
-  }
-
-  public static abstract interface BrowseFragment.MainFragmentRowsAdapterProvider {
-    method public abstract android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
-  }
-
-  public class BrowseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
-    ctor public BrowseSupportFragment();
-    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int);
-    method protected java.lang.Object createEntranceTransition();
-    method public void enableMainFragmentScaling(boolean);
-    method public deprecated void enableRowScaling(boolean);
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public int getBrandColor();
-    method public int getHeadersState();
-    method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
-    method public android.support.v4.app.Fragment getMainFragment();
-    method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
-    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
-    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
-    method public int getSelectedPosition();
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
-    method public final boolean isHeadersTransitionOnBackEnabled();
-    method public boolean isInHeadersTransition();
-    method public boolean isShowingHeaders();
-    method public android.support.v17.leanback.app.HeadersSupportFragment onCreateHeadersSupportFragment();
-    method protected void onEntranceTransitionEnd();
-    method protected void onEntranceTransitionPrepare();
-    method protected void onEntranceTransitionStart();
-    method protected void runEntranceTransition(java.lang.Object);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setBrandColor(int);
-    method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener);
-    method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
-    method public void setHeadersState(int);
-    method public final void setHeadersTransitionOnBackEnabled(boolean);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-    method public void startHeadersTransition(boolean);
-    field public static final int HEADERS_DISABLED = 3; // 0x3
-    field public static final int HEADERS_ENABLED = 1; // 0x1
-    field public static final int HEADERS_HIDDEN = 2; // 0x2
-  }
-
-  public static class BrowseSupportFragment.BrowseTransitionListener {
-    ctor public BrowseSupportFragment.BrowseTransitionListener();
-    method public void onHeadersTransitionStart(boolean);
-    method public void onHeadersTransitionStop(boolean);
-  }
-
-  public static abstract class BrowseSupportFragment.FragmentFactory<T extends android.support.v4.app.Fragment> {
-    ctor public BrowseSupportFragment.FragmentFactory();
-    method public abstract T createFragment(java.lang.Object);
-  }
-
-  public static abstract interface BrowseSupportFragment.FragmentHost {
-    method public abstract void notifyDataReady(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
-    method public abstract void notifyViewCreated(android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter);
-    method public abstract void showTitleView(boolean);
-  }
-
-  public static class BrowseSupportFragment.ListRowFragmentFactory extends android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory {
-    ctor public BrowseSupportFragment.ListRowFragmentFactory();
-    method public android.support.v17.leanback.app.RowsSupportFragment createFragment(java.lang.Object);
-  }
-
-  public static class BrowseSupportFragment.MainFragmentAdapter<T extends android.support.v4.app.Fragment> {
-    ctor public BrowseSupportFragment.MainFragmentAdapter(T);
-    method public final T getFragment();
-    method public final android.support.v17.leanback.app.BrowseSupportFragment.FragmentHost getFragmentHost();
-    method public boolean isScalingEnabled();
-    method public boolean isScrolling();
-    method public void onTransitionEnd();
-    method public boolean onTransitionPrepare();
-    method public void onTransitionStart();
-    method public void setAlignment(int);
-    method public void setEntranceTransitionState(boolean);
-    method public void setExpand(boolean);
-    method public void setScalingEnabled(boolean);
-  }
-
-  public static abstract interface BrowseSupportFragment.MainFragmentAdapterProvider {
-    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
-  }
-
-  public static final class BrowseSupportFragment.MainFragmentAdapterRegistry {
-    ctor public BrowseSupportFragment.MainFragmentAdapterRegistry();
-    method public android.support.v4.app.Fragment createFragment(java.lang.Object);
-    method public void registerFragment(java.lang.Class, android.support.v17.leanback.app.BrowseSupportFragment.FragmentFactory);
-  }
-
-  public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
-    ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
-    method public final T getFragment();
-    method public int getSelectedPosition();
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-    method public void setSelectedPosition(int, boolean);
-  }
-
-  public static abstract interface BrowseSupportFragment.MainFragmentRowsAdapterProvider {
-    method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
-  }
-
-  public class DetailsFragment extends android.support.v17.leanback.app.BrandedFragment {
-    ctor public DetailsFragment();
-    method protected java.lang.Object createEntranceTransition();
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
-    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
-    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
-    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method protected void onEntranceTransitionEnd();
-    method protected void onEntranceTransitionPrepare();
-    method protected void onEntranceTransitionStart();
-    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
-    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
-    method protected void runEntranceTransition(java.lang.Object);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
-    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
-  }
-
-  public class DetailsFragmentBackgroundController {
-    ctor public DetailsFragmentBackgroundController(android.support.v17.leanback.app.DetailsFragment);
-    method public boolean canNavigateToVideoFragment();
-    method public void enableParallax();
-    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
-    method public final android.app.Fragment findOrCreateVideoFragment();
-    method public final android.graphics.drawable.Drawable getBottomDrawable();
-    method public final android.graphics.Bitmap getCoverBitmap();
-    method public final android.graphics.drawable.Drawable getCoverDrawable();
-    method public final int getParallaxDrawableMaxOffset();
-    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
-    method public final int getSolidColor();
-    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
-    method public android.app.Fragment onCreateVideoFragment();
-    method public final void setCoverBitmap(android.graphics.Bitmap);
-    method public final void setParallaxDrawableMaxOffset(int);
-    method public final void setSolidColor(int);
-    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
-    method public final void switchToRows();
-    method public final void switchToVideo();
-  }
-
-  public class DetailsSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
-    ctor public DetailsSupportFragment();
-    method protected java.lang.Object createEntranceTransition();
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
-    method public android.support.v17.leanback.widget.DetailsParallax getParallax();
-    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
-    method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method protected void onEntranceTransitionEnd();
-    method protected void onEntranceTransitionPrepare();
-    method protected void onEntranceTransitionStart();
-    method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int);
-    method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int);
-    method protected void runEntranceTransition(java.lang.Object);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-    method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
-    method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
-  }
-
-  public class DetailsSupportFragmentBackgroundController {
-    ctor public DetailsSupportFragmentBackgroundController(android.support.v17.leanback.app.DetailsSupportFragment);
-    method public boolean canNavigateToVideoSupportFragment();
-    method public void enableParallax();
-    method public void enableParallax(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.support.v17.leanback.widget.ParallaxTarget.PropertyValuesHolderTarget);
-    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
-    method public final android.graphics.drawable.Drawable getBottomDrawable();
-    method public final android.graphics.Bitmap getCoverBitmap();
-    method public final android.graphics.drawable.Drawable getCoverDrawable();
-    method public final int getParallaxDrawableMaxOffset();
-    method public final android.support.v17.leanback.media.PlaybackGlue getPlaybackGlue();
-    method public final int getSolidColor();
-    method public android.support.v17.leanback.media.PlaybackGlueHost onCreateGlueHost();
-    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
-    method public final void setCoverBitmap(android.graphics.Bitmap);
-    method public final void setParallaxDrawableMaxOffset(int);
-    method public final void setSolidColor(int);
-    method public void setupVideoPlayback(android.support.v17.leanback.media.PlaybackGlue);
-    method public final void switchToRows();
-    method public final void switchToVideo();
-  }
-
-  public class ErrorFragment extends android.support.v17.leanback.app.BrandedFragment {
-    ctor public ErrorFragment();
-    method public android.graphics.drawable.Drawable getBackgroundDrawable();
-    method public android.view.View.OnClickListener getButtonClickListener();
-    method public java.lang.String getButtonText();
-    method public android.graphics.drawable.Drawable getImageDrawable();
-    method public java.lang.CharSequence getMessage();
-    method public boolean isBackgroundTranslucent();
-    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
-    method public void setButtonClickListener(android.view.View.OnClickListener);
-    method public void setButtonText(java.lang.String);
-    method public void setDefaultBackground(boolean);
-    method public void setImageDrawable(android.graphics.drawable.Drawable);
-    method public void setMessage(java.lang.CharSequence);
-  }
-
-  public class ErrorSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
-    ctor public ErrorSupportFragment();
-    method public android.graphics.drawable.Drawable getBackgroundDrawable();
-    method public android.view.View.OnClickListener getButtonClickListener();
-    method public java.lang.String getButtonText();
-    method public android.graphics.drawable.Drawable getImageDrawable();
-    method public java.lang.CharSequence getMessage();
-    method public boolean isBackgroundTranslucent();
-    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
-    method public void setButtonClickListener(android.view.View.OnClickListener);
-    method public void setButtonText(java.lang.String);
-    method public void setDefaultBackground(boolean);
-    method public void setImageDrawable(android.graphics.drawable.Drawable);
-    method public void setMessage(java.lang.CharSequence);
-  }
-
-  public class GuidedStepFragment extends android.app.Fragment {
-    ctor public GuidedStepFragment();
-    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
-    method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
-    method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
-    method public void collapseAction(boolean);
-    method public void collapseSubActions();
-    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
-    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
-    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
-    method public int findActionPositionById(long);
-    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
-    method public int findButtonActionPositionById(long);
-    method public void finishGuidedStepFragments();
-    method public android.view.View getActionItemView(int);
-    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
-    method public android.view.View getButtonActionItemView(int);
-    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
-    method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager);
-    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
-    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
-    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
-    method public int getSelectedActionPosition();
-    method public int getSelectedButtonActionPosition();
-    method public int getUiStyle();
-    method public boolean isExpanded();
-    method public boolean isFocusOutEndAllowed();
-    method public boolean isFocusOutStartAllowed();
-    method public boolean isSubActionsExpanded();
-    method public void notifyActionChanged(int);
-    method public void notifyButtonActionChanged(int);
-    method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment);
-    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
-    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
-    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
-    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
-    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
-    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
-    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
-    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
-    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
-    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
-    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
-    method protected void onProvideFragmentTransitions();
-    method public int onProvideTheme();
-    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
-    method public void popBackStackToGuidedStepFragment(java.lang.Class, int);
-    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
-    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
-    method public void setSelectedActionPosition(int);
-    method public void setSelectedButtonActionPosition(int);
-    method public void setUiStyle(int);
-    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
-    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
-    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
-    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
-    field public static final int UI_STYLE_REPLACE = 0; // 0x0
-  }
-
-  public class GuidedStepSupportFragment extends android.support.v4.app.Fragment {
-    ctor public GuidedStepSupportFragment();
-    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
-    method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
-    method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
-    method public void collapseAction(boolean);
-    method public void collapseSubActions();
-    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
-    method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
-    method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
-    method public int findActionPositionById(long);
-    method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long);
-    method public int findButtonActionPositionById(long);
-    method public void finishGuidedStepSupportFragments();
-    method public android.view.View getActionItemView(int);
-    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions();
-    method public android.view.View getButtonActionItemView(int);
-    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions();
-    method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager);
-    method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist();
-    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist();
-    method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist();
-    method public int getSelectedActionPosition();
-    method public int getSelectedButtonActionPosition();
-    method public int getUiStyle();
-    method public boolean isExpanded();
-    method public boolean isFocusOutEndAllowed();
-    method public boolean isFocusOutStartAllowed();
-    method public boolean isSubActionsExpanded();
-    method public void notifyActionChanged(int);
-    method public void notifyButtonActionChanged(int);
-    method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment);
-    method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
-    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist();
-    method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle);
-    method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist();
-    method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
-    method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
-    method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
-    method public void onGuidedActionEditCanceled(android.support.v17.leanback.widget.GuidedAction);
-    method public deprecated void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
-    method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction);
-    method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
-    method protected void onProvideFragmentTransitions();
-    method public int onProvideTheme();
-    method public boolean onSubGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
-    method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int);
-    method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
-    method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
-    method public void setSelectedActionPosition(int);
-    method public void setSelectedButtonActionPosition(int);
-    method public void setUiStyle(int);
-    field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle";
-    field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2
-    field public static final deprecated int UI_STYLE_DEFAULT = 0; // 0x0
-    field public static final int UI_STYLE_ENTRANCE = 1; // 0x1
-    field public static final int UI_STYLE_REPLACE = 0; // 0x0
-  }
-
-  public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment {
-    ctor public HeadersFragment();
-    method public boolean isScrolling();
-    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener);
-    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener);
-  }
-
-  public static abstract interface HeadersFragment.OnHeaderClickedListener {
-    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
-  }
-
-  public static abstract interface HeadersFragment.OnHeaderViewSelectedListener {
-    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
-  }
-
-  public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment {
-    ctor public HeadersSupportFragment();
-    method public boolean isScrolling();
-    method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener);
-    method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener);
-  }
-
-  public static abstract interface HeadersSupportFragment.OnHeaderClickedListener {
-    method public abstract void onHeaderClicked(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
-  }
-
-  public static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener {
-    method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
-  }
-
-  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
-    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
-    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
-    method public void detach();
-    method public int getCurrentPosition();
-    method public int getCurrentSpeedId();
-    method public android.graphics.drawable.Drawable getMediaArt();
-    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
-    method public int getMediaDuration();
-    method public java.lang.CharSequence getMediaSubtitle();
-    method public java.lang.CharSequence getMediaTitle();
-    method public long getSupportedActions();
-    method public boolean hasValidMedia();
-    method public boolean isMediaPlaying();
-  }
-
-  public abstract class OnboardingFragment extends android.app.Fragment {
-    ctor public OnboardingFragment();
-    method protected final int getCurrentPageIndex();
-    method public final int getLogoResourceId();
-    method protected abstract int getPageCount();
-    method protected abstract java.lang.CharSequence getPageDescription(int);
-    method protected abstract java.lang.CharSequence getPageTitle(int);
-    method protected final boolean isLogoAnimationFinished();
-    method protected void moveToNextPage();
-    method protected void moveToPreviousPage();
-    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
-    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
-    method protected android.animation.Animator onCreateEnterAnimation();
-    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
-    method protected android.animation.Animator onCreateLogoAnimation();
-    method protected void onFinishFragment();
-    method protected void onLogoAnimationFinished();
-    method protected void onPageChanged(int, int);
-    method public int onProvideTheme();
-    method public final void setLogoResourceId(int);
-  }
-
-  public abstract class OnboardingSupportFragment extends android.support.v4.app.Fragment {
-    ctor public OnboardingSupportFragment();
-    method protected final int getCurrentPageIndex();
-    method public final int getLogoResourceId();
-    method protected abstract int getPageCount();
-    method protected abstract java.lang.CharSequence getPageDescription(int);
-    method protected abstract java.lang.CharSequence getPageTitle(int);
-    method protected final boolean isLogoAnimationFinished();
-    method protected void moveToNextPage();
-    method protected void moveToPreviousPage();
-    method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
-    method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
-    method protected android.animation.Animator onCreateEnterAnimation();
-    method protected abstract android.view.View onCreateForegroundView(android.view.LayoutInflater, android.view.ViewGroup);
-    method protected android.animation.Animator onCreateLogoAnimation();
-    method protected void onFinishFragment();
-    method protected void onLogoAnimationFinished();
-    method protected void onPageChanged(int, int);
-    method public int onProvideTheme();
-    method public final void setLogoResourceId(int);
-  }
-
-  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
-    ctor public PlaybackControlGlue(android.content.Context, int[]);
-    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
-    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
-    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
-    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
-    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public final void next();
-    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method public final void pause();
-    method protected deprecated void pausePlayback();
-    method public final void play(int);
-    method public final void previous();
-    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method protected deprecated void skipToNext();
-    method protected deprecated void skipToPrevious();
-    method protected deprecated void startPlayback(int);
-  }
-
-  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
-    method public abstract boolean handleInputEvent(android.view.InputEvent);
-  }
-
-  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
-    ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
-    ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
-    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
-    ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
-    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
-    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
-    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
-    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
-    field public static final int ACTION_REWIND = 32; // 0x20
-    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
-    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
-    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
-    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
-    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
-    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
-    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
-    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
-    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
-    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
-  }
-
-  public class PlaybackFragment extends android.app.Fragment {
-    ctor public PlaybackFragment();
-    method public void fadeOut();
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public int getBackgroundType();
-    method public boolean isFadingEnabled();
-    method public void notifyPlaybackRowChanged();
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setBackgroundType(int);
-    method public void setFadingEnabled(boolean);
-    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
-    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
-    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-    method public void tickle();
-    field public static final int BG_DARK = 1; // 0x1
-    field public static final int BG_LIGHT = 2; // 0x2
-    field public static final int BG_NONE = 0; // 0x0
-  }
-
-  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
-    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
-  }
-
-  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
-    ctor public PlaybackOverlayFragment();
-    method public void fadeOut();
-    method public int getBackgroundType();
-    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
-    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
-    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
-    method public boolean isFadingEnabled();
-    method public void setBackgroundType(int);
-    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
-    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
-    method public void setFadingEnabled(boolean);
-    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
-    method public void tickle();
-    field public static final int BG_DARK = 1; // 0x1
-    field public static final int BG_LIGHT = 2; // 0x2
-    field public static final int BG_NONE = 0; // 0x0
-  }
-
-  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
-  }
-
-  public static class PlaybackOverlayFragment.OnFadeCompleteListener {
-    ctor public PlaybackOverlayFragment.OnFadeCompleteListener();
-    method public void onFadeInComplete();
-    method public void onFadeOutComplete();
-  }
-
-  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
-    ctor public PlaybackOverlaySupportFragment();
-    method public void fadeOut();
-    method public int getBackgroundType();
-    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
-    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
-    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
-    method public boolean isFadingEnabled();
-    method public void setBackgroundType(int);
-    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
-    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
-    method public void setFadingEnabled(boolean);
-    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
-    method public void tickle();
-    field public static final int BG_DARK = 1; // 0x1
-    field public static final int BG_LIGHT = 2; // 0x2
-    field public static final int BG_NONE = 0; // 0x0
-  }
-
-  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
-  }
-
-  public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
-    ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener();
-    method public void onFadeInComplete();
-    method public void onFadeOutComplete();
-  }
-
-  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
-    ctor public PlaybackSupportFragment();
-    method public void fadeOut();
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public int getBackgroundType();
-    method public boolean isFadingEnabled();
-    method public void notifyPlaybackRowChanged();
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setBackgroundType(int);
-    method public void setFadingEnabled(boolean);
-    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
-    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
-    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
-    method public void setSelectedPosition(int);
-    method public void setSelectedPosition(int, boolean);
-    method public void tickle();
-    field public static final int BG_DARK = 1; // 0x1
-    field public static final int BG_LIGHT = 2; // 0x2
-    field public static final int BG_NONE = 0; // 0x0
-  }
-
-  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
-    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
-  }
-
-  public final class ProgressBarManager {
-    ctor public ProgressBarManager();
-    method public void disableProgressBar();
-    method public void enableProgressBar();
-    method public long getInitialDelay();
-    method public void hide();
-    method public void setInitialDelay(long);
-    method public void setProgressBarView(android.view.View);
-    method public void setRootView(android.view.ViewGroup);
-    method public void show();
-  }
-
-  public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment implements android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapterProvider {
-    ctor public RowsFragment();
-    method public deprecated void enableRowScaling(boolean);
-    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
-    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
-    method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
-    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
-    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
-    method public boolean isScrolling();
-    method public void setEntranceTransitionState(boolean);
-    method public void setExpand(boolean);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-  }
-
-  public static class RowsFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter {
-    ctor public RowsFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsFragment);
-  }
-
-  public static class RowsFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter {
-    ctor public RowsFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsFragment);
-  }
-
-  public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment implements android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterProvider android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapterProvider {
-    ctor public RowsSupportFragment();
-    method public deprecated void enableRowScaling(boolean);
-    method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
-    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
-    method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
-    method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
-    method public android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
-    method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(int);
-    method public boolean isScrolling();
-    method public void setEntranceTransitionState(boolean);
-    method public void setExpand(boolean);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public void setSelectedPosition(int, boolean, android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-  }
-
-  public static class RowsSupportFragment.MainFragmentAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter {
-    ctor public RowsSupportFragment.MainFragmentAdapter(android.support.v17.leanback.app.RowsSupportFragment);
-  }
-
-  public static class RowsSupportFragment.MainFragmentRowsAdapter extends android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter {
-    ctor public RowsSupportFragment.MainFragmentRowsAdapter(android.support.v17.leanback.app.RowsSupportFragment);
-  }
-
-  public class SearchFragment extends android.app.Fragment {
-    ctor public SearchFragment();
-    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
-    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
-    method public void displayCompletions(java.util.List<java.lang.String>);
-    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public android.content.Intent getRecognizerIntent();
-    method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
-    method public java.lang.String getTitle();
-    method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String);
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSearchQuery(java.lang.String, boolean);
-    method public void setSearchQuery(android.content.Intent, boolean);
-    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
-    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
-    method public void setTitle(java.lang.String);
-    method public void startRecognition();
-  }
-
-  public static abstract interface SearchFragment.SearchResultProvider {
-    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
-    method public abstract boolean onQueryTextChange(java.lang.String);
-    method public abstract boolean onQueryTextSubmit(java.lang.String);
-  }
-
-  public class SearchSupportFragment extends android.support.v4.app.Fragment {
-    ctor public SearchSupportFragment();
-    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String);
-    method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String);
-    method public void displayCompletions(java.util.List<java.lang.String>);
-    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public android.content.Intent getRecognizerIntent();
-    method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
-    method public java.lang.String getTitle();
-    method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String);
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSearchQuery(java.lang.String, boolean);
-    method public void setSearchQuery(android.content.Intent, boolean);
-    method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
-    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
-    method public void setTitle(java.lang.String);
-    method public void startRecognition();
-  }
-
-  public static abstract interface SearchSupportFragment.SearchResultProvider {
-    method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter();
-    method public abstract boolean onQueryTextChange(java.lang.String);
-    method public abstract boolean onQueryTextSubmit(java.lang.String);
-  }
-
-  public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment {
-    ctor public VerticalGridFragment();
-    method protected java.lang.Object createEntranceTransition();
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
-    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method protected void runEntranceTransition(java.lang.Object);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSelectedPosition(int);
-  }
-
-  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
-    ctor public VerticalGridSupportFragment();
-    method protected java.lang.Object createEntranceTransition();
-    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
-    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method protected void runEntranceTransition(java.lang.Object);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter);
-    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public void setSelectedPosition(int);
-  }
-
-  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
-    ctor public VideoFragment();
-    method public android.view.SurfaceView getSurfaceView();
-    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
-  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
-    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
-    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
-  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
-    ctor public VideoSupportFragment();
-    method public android.view.SurfaceView getSurfaceView();
-    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
-  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost implements android.support.v17.leanback.media.SurfaceHolderGlueHost {
-    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
-    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
-}
-
-package android.support.v17.leanback.database {
-
-  public abstract class CursorMapper {
-    ctor public CursorMapper();
-    method protected abstract java.lang.Object bind(android.database.Cursor);
-    method protected abstract void bindColumns(android.database.Cursor);
-    method public java.lang.Object convert(android.database.Cursor);
-  }
-
-}
-
-package android.support.v17.leanback.graphics {
-
-  public class BoundsRule {
-    ctor public BoundsRule();
-    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
-    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
-    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
-    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
-    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
-    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule top;
-  }
-
-  public static final class BoundsRule.ValueRule {
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
-    method public int getAbsoluteValue();
-    method public float getFraction();
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
-    method public void setAbsoluteValue(int);
-    method public void setFraction(float);
-  }
-
-  public final class ColorFilterCache {
-    method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
-    method public android.graphics.ColorFilter getFilterForLevel(float);
-  }
-
-  public final class ColorFilterDimmer {
-    method public void applyFilterToView(android.view.View);
-    method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float);
-    method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context);
-    method public android.graphics.ColorFilter getColorFilter();
-    method public android.graphics.Paint getPaint();
-    method public void setActiveLevel(float);
-  }
-
-  public final class ColorOverlayDimmer {
-    method public int applyToColor(int);
-    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float);
-    method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context);
-    method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean);
-    method public int getAlpha();
-    method public float getAlphaFloat();
-    method public android.graphics.Paint getPaint();
-    method public boolean needsDraw();
-    method public void setActiveLevel(float);
-  }
-
-  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
-    ctor public CompositeDrawable();
-    method public void addChildDrawable(android.graphics.drawable.Drawable);
-    method public void draw(android.graphics.Canvas);
-    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
-    method public int getChildCount();
-    method public android.graphics.drawable.Drawable getDrawable(int);
-    method public int getOpacity();
-    method public void invalidateDrawable(android.graphics.drawable.Drawable);
-    method public void removeChild(int);
-    method public void removeDrawable(android.graphics.drawable.Drawable);
-    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
-    method public void setAlpha(int);
-    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
-  }
-
-  public static final class CompositeDrawable.ChildDrawable {
-    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
-    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
-    method public android.graphics.drawable.Drawable getDrawable();
-    method public void recomputeBounds();
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
-    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
-  }
-
-  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
-    ctor public FitWidthBitmapDrawable();
-    method public void draw(android.graphics.Canvas);
-    method public android.graphics.Bitmap getBitmap();
-    method public int getOpacity();
-    method public android.graphics.Rect getSource();
-    method public int getVerticalOffset();
-    method public void setAlpha(int);
-    method public void setBitmap(android.graphics.Bitmap);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void setSource(android.graphics.Rect);
-    method public void setVerticalOffset(int);
-    field public static final android.util.Property<android.support.v17.leanback.graphics.FitWidthBitmapDrawable, java.lang.Integer> PROPERTY_VERTICAL_OFFSET;
-  }
-
-}
-
-package android.support.v17.leanback.media {
-
-  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
-    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
-    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
-    method public void detach();
-    method public int getCurrentPosition();
-    method public int getCurrentSpeedId();
-    method public android.graphics.drawable.Drawable getMediaArt();
-    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
-    method public int getMediaDuration();
-    method public java.lang.CharSequence getMediaSubtitle();
-    method public java.lang.CharSequence getMediaTitle();
-    method public long getSupportedActions();
-    method public boolean hasValidMedia();
-    method public boolean isMediaPlaying();
-  }
-
-  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
-    ctor public PlaybackControlGlue(android.content.Context, int[]);
-    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
-    method public void enableProgressUpdating(boolean);
-    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
-    method public deprecated android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
-    method public abstract int getCurrentPosition();
-    method public abstract int getCurrentSpeedId();
-    method public int[] getFastForwardSpeeds();
-    method public abstract android.graphics.drawable.Drawable getMediaArt();
-    method public abstract int getMediaDuration();
-    method public abstract java.lang.CharSequence getMediaSubtitle();
-    method public abstract java.lang.CharSequence getMediaTitle();
-    method public android.support.v17.leanback.widget.PlaybackRowPresenter getPlaybackRowPresenter();
-    method public int[] getRewindSpeeds();
-    method public abstract long getSupportedActions();
-    method public int getUpdatePeriod();
-    method public abstract boolean hasValidMedia();
-    method public boolean isFadingEnabled();
-    method public abstract boolean isMediaPlaying();
-    method public void onActionClicked(android.support.v17.leanback.widget.Action);
-    method protected void onCreateControlsRowAndPresenter();
-    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
-    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
-    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
-    method protected void onMetadataChanged();
-    method protected void onStateChanged();
-    method public void play(int);
-    method public final void play();
-    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method public deprecated void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
-    method public void setFadingEnabled(boolean);
-    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
-    method public void updateProgress();
-    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
-    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
-    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
-    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
-    field public static final int ACTION_REWIND = 32; // 0x20
-    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
-    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
-    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
-    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
-    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
-    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
-    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
-    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
-    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
-    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
-  }
-
-  public abstract class PlaybackGlue {
-    ctor public PlaybackGlue(android.content.Context);
-    method public android.content.Context getContext();
-    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
-    method public boolean isReadyForPlayback();
-    method public void next();
-    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
-    method protected void onDetachedFromHost();
-    method protected void onHostPause();
-    method protected void onHostResume();
-    method protected void onHostStart();
-    method protected void onHostStop();
-    method public void pause();
-    method public void play();
-    method public void previous();
-    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
-    method public void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
-  }
-
-  public static abstract class PlaybackGlue.PlayerCallback {
-    ctor public PlaybackGlue.PlayerCallback();
-    method public abstract void onReadyForPlayback();
-  }
-
-  public abstract class PlaybackGlueHost {
-    ctor public PlaybackGlueHost();
-    method public void fadeOut();
-    method public void notifyPlaybackRowChanged();
-    method public void setFadingEnabled(boolean);
-    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
-    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
-    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
-    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
-    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
-  }
-
-  public static abstract class PlaybackGlueHost.HostCallback {
-    ctor public PlaybackGlueHost.HostCallback();
-    method public void onHostDestroy();
-    method public void onHostPause();
-    method public void onHostResume();
-    method public void onHostStart();
-    method public void onHostStop();
-  }
-
-  public abstract interface SurfaceHolderGlueHost {
-    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
-}
-
-package android.support.v17.leanback.system {
-
-  public class Settings {
-    method public boolean getBoolean(java.lang.String);
-    method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context);
-    method public void setBoolean(java.lang.String, boolean);
-    field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
-  }
-
-}
-
-package android.support.v17.leanback.widget {
-
-  public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter {
-    ctor public AbstractDetailsDescriptionPresenter();
-    method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object);
-    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-  }
-
-  public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
-    ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View);
-    method public android.widget.TextView getBody();
-    method public android.widget.TextView getSubtitle();
-    method public android.widget.TextView getTitle();
-  }
-
-  public abstract class AbstractMediaItemPresenter extends android.support.v17.leanback.widget.RowPresenter {
-    ctor public AbstractMediaItemPresenter();
-    ctor public AbstractMediaItemPresenter(int);
-    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method public android.support.v17.leanback.widget.Presenter getActionPresenter();
-    method protected int getMediaPlayState(java.lang.Object);
-    method public int getThemeId();
-    method public boolean hasMediaRowSeparator();
-    method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
-    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
-    method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
-    method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
-    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
-    method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
-    method public void setBackgroundColor(int);
-    method public void setHasMediaRowSeparator(boolean);
-    method public void setThemeId(int);
-    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
-    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
-    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
-  }
-
-  public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
-    ctor public AbstractMediaItemPresenter.ViewHolder(android.view.View);
-    method public android.view.ViewGroup getMediaItemActionsContainer();
-    method public android.view.View getMediaItemDetailsView();
-    method public android.widget.TextView getMediaItemDurationView();
-    method public android.widget.TextView getMediaItemNameView();
-    method public android.widget.TextView getMediaItemNumberView();
-    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
-    method public android.view.View getMediaItemPausedView();
-    method public android.view.View getMediaItemPlayingView();
-    method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
-    method public android.view.View getMediaItemRowSeparator();
-    method public android.view.View getSelectorView();
-    method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
-    method public void notifyDetailsChanged();
-    method public void notifyPlayStateChanged();
-    method public void onBindRowActions();
-    method public void setSelectedMediaItemNumberView(int);
-  }
-
-  public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
-    ctor public AbstractMediaListHeaderPresenter(android.content.Context, int);
-    ctor public AbstractMediaListHeaderPresenter();
-    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method protected abstract void onBindMediaListHeaderViewHolder(android.support.v17.leanback.widget.AbstractMediaListHeaderPresenter.ViewHolder, java.lang.Object);
-    method public void setBackgroundColor(int);
-  }
-
-  public static class AbstractMediaListHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
-    ctor public AbstractMediaListHeaderPresenter.ViewHolder(android.view.View);
-    method public android.widget.TextView getHeaderView();
-  }
-
-  public class Action {
-    ctor public Action(long);
-    ctor public Action(long, java.lang.CharSequence);
-    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence);
-    ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable);
-    method public final void addKeyCode(int);
-    method public final android.graphics.drawable.Drawable getIcon();
-    method public final long getId();
-    method public final java.lang.CharSequence getLabel1();
-    method public final java.lang.CharSequence getLabel2();
-    method public final void removeKeyCode(int);
-    method public final boolean respondsToKeyCode(int);
-    method public final void setIcon(android.graphics.drawable.Drawable);
-    method public final void setId(long);
-    method public final void setLabel1(java.lang.CharSequence);
-    method public final void setLabel2(java.lang.CharSequence);
-    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
-  }
-
-  public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
-    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
-    ctor public ArrayObjectAdapter();
-    method public void add(java.lang.Object);
-    method public void add(int, java.lang.Object);
-    method public void addAll(int, java.util.Collection);
-    method public void clear();
-    method public java.lang.Object get(int);
-    method public int indexOf(java.lang.Object);
-    method public void notifyArrayItemRangeChanged(int, int);
-    method public boolean remove(java.lang.Object);
-    method public int removeItems(int, int);
-    method public void replace(int, java.lang.Object);
-    method public int size();
-    method public <E> java.util.List<E> unmodifiableList();
-  }
-
-  public class BaseCardView extends android.widget.FrameLayout {
-    ctor public BaseCardView(android.content.Context);
-    ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
-    ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
-    method public int getCardType();
-    method public deprecated int getExtraVisibility();
-    method public int getInfoVisibility();
-    method public boolean isSelectedAnimationDelayed();
-    method public void setCardType(int);
-    method public deprecated void setExtraVisibility(int);
-    method public void setInfoVisibility(int);
-    method public void setSelectedAnimationDelayed(boolean);
-    field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
-    field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0
-    field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2
-    field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1
-    field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2
-    field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3
-    field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0
-  }
-
-  public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams {
-    ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public BaseCardView.LayoutParams(int, int);
-    ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams);
-    field public static final int VIEW_TYPE_EXTRA = 2; // 0x2
-    field public static final int VIEW_TYPE_INFO = 1; // 0x1
-    field public static final int VIEW_TYPE_MAIN = 0; // 0x0
-    field public int viewType;
-  }
-
-  public abstract interface BaseOnItemViewClickedListener<T> {
-    method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
-  }
-
-  public abstract interface BaseOnItemViewSelectedListener<T> {
-    method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, T);
-  }
-
-  public class BrowseFrameLayout extends android.widget.FrameLayout {
-    ctor public BrowseFrameLayout(android.content.Context);
-    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet);
-    ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int);
-    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
-    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
-    method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
-    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
-    method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
-  }
-
-  public static abstract interface BrowseFrameLayout.OnChildFocusListener {
-    method public abstract void onRequestChildFocus(android.view.View, android.view.View);
-    method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect);
-  }
-
-  public static abstract interface BrowseFrameLayout.OnFocusSearchListener {
-    method public abstract android.view.View onFocusSearch(android.view.View, int);
-  }
-
-  public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
-    ctor public ClassPresenterSelector();
-    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter);
-    method public android.support.v17.leanback.widget.ClassPresenterSelector addClassPresenterSelector(java.lang.Class<?>, android.support.v17.leanback.widget.PresenterSelector);
-    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
-  }
-
-  public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
-    ctor public ControlButtonPresenterSelector();
-    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
-    method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter();
-    method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter();
-  }
-
-  public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
-    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter);
-    ctor public CursorObjectAdapter();
-    method public void changeCursor(android.database.Cursor);
-    method public void close();
-    method public java.lang.Object get(int);
-    method public final android.database.Cursor getCursor();
-    method public final android.support.v17.leanback.database.CursorMapper getMapper();
-    method protected final void invalidateCache(int);
-    method protected final void invalidateCache(int, int);
-    method public boolean isClosed();
-    method protected void onCursorChanged();
-    method protected void onMapperChanged();
-    method public final void setMapper(android.support.v17.leanback.database.CursorMapper);
-    method public int size();
-    method public android.database.Cursor swapCursor(android.database.Cursor);
-  }
-
-  public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter {
-    ctor public DetailsOverviewLogoPresenter();
-    method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow);
-    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public android.view.View onCreateView(android.view.ViewGroup);
-    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter);
-  }
-
-  public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
-    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
-    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter getParentPresenter();
-    method public android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder();
-    method public boolean isSizeFromDrawableIntrinsic();
-    method public void setSizeFromDrawableIntrinsic(boolean);
-    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter;
-    field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
-  }
-
-  public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row {
-    ctor public DetailsOverviewRow(java.lang.Object);
-    method public final deprecated void addAction(android.support.v17.leanback.widget.Action);
-    method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action);
-    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
-    method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions();
-    method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter();
-    method public final android.graphics.drawable.Drawable getImageDrawable();
-    method public final java.lang.Object getItem();
-    method public boolean isImageScaleUpAllowed();
-    method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action);
-    method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
-    method public final void setImageDrawable(android.graphics.drawable.Drawable);
-    method public void setImageScaleUpAllowed(boolean);
-    method public final void setItem(java.lang.Object);
-  }
-
-  public static class DetailsOverviewRow.Listener {
-    ctor public DetailsOverviewRow.Listener();
-    method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
-    method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
-    method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow);
-  }
-
-  public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
-    ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
-    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method public int getBackgroundColor();
-    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
-    method public boolean isStyleLarge();
-    method public final boolean isUsingDefaultSelectEffect();
-    method public void setBackgroundColor(int);
-    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
-    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
-    method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
-    method public void setStyleLarge(boolean);
-  }
-
-  public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
-    ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter);
-    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder;
-  }
-
-  public class DetailsParallax extends android.support.v17.leanback.widget.RecyclerViewParallax {
-    ctor public DetailsParallax();
-    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowBottom();
-    method public android.support.v17.leanback.widget.Parallax.IntProperty getOverviewRowTop();
-  }
-
-  public class DividerPresenter extends android.support.v17.leanback.widget.Presenter {
-    ctor public DividerPresenter();
-    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-  }
-
-  public class DividerRow extends android.support.v17.leanback.widget.Row {
-    ctor public DividerRow();
-    method public final boolean isRenderedAsRowView();
-  }
-
-  public abstract interface FacetProvider {
-    method public abstract java.lang.Object getFacet(java.lang.Class<?>);
-  }
-
-  public abstract interface FacetProviderAdapter {
-    method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
-  }
-
-  public abstract interface FocusHighlight {
-    field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3
-    field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2
-    field public static final int ZOOM_FACTOR_NONE = 0; // 0x0
-    field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1
-    field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4
-  }
-
-  public class FocusHighlightHelper {
-    ctor public FocusHighlightHelper();
-    method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean);
-    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView);
-    method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView, boolean);
-  }
-
-  public abstract interface FragmentAnimationProvider {
-    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
-    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
-  }
-
-  public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
-    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter);
-    ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
-    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method public final int getActionsBackgroundColor();
-    method public final int getAlignmentMode();
-    method public final int getBackgroundColor();
-    method public final int getInitialState();
-    method protected int getLayoutResourceId();
-    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
-    method public final boolean isParticipatingEntranceTransition();
-    method public final boolean isUsingDefaultSelectEffect();
-    method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
-    method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
-    method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean);
-    method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
-    method public final void setActionsBackgroundColor(int);
-    method public final void setAlignmentMode(int);
-    method public final void setBackgroundColor(int);
-    method public final void setInitialState(int);
-    method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener);
-    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
-    method public final void setParticipatingEntranceTransition(boolean);
-    method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int);
-    field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1
-    field public static final int ALIGN_MODE_START = 0; // 0x0
-    field public static final int STATE_FULL = 1; // 0x1
-    field public static final int STATE_HALF = 0; // 0x0
-    field public static final int STATE_SMALL = 2; // 0x2
-    field protected int mInitialState;
-  }
-
-  public static abstract class FullWidthDetailsOverviewRowPresenter.Listener {
-    ctor public FullWidthDetailsOverviewRowPresenter.Listener();
-    method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder);
-  }
-
-  public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
-    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter);
-    method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener();
-    method public final android.view.ViewGroup getActionsRow();
-    method public final android.view.ViewGroup getDetailsDescriptionFrame();
-    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder();
-    method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder();
-    method public final android.view.ViewGroup getOverviewView();
-    method public final int getState();
-    field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener;
-  }
-
-  public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener {
-    ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener();
-  }
-
-  public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener {
-    ctor public FullWidthDetailsOverviewSharedElementHelper();
-    method public boolean getAutoStartSharedElementTransition();
-    method public void setAutoStartSharedElementTransition(boolean);
-    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String);
-    method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long);
-    method public void startPostponedEnterTransition();
-  }
-
-  public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
-    ctor public GuidanceStylist();
-    method public android.widget.TextView getBreadcrumbView();
-    method public android.widget.TextView getDescriptionView();
-    method public android.widget.ImageView getIconView();
-    method public android.widget.TextView getTitleView();
-    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance);
-    method public void onDestroyView();
-    method public void onImeAppearing(java.util.List<android.animation.Animator>);
-    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
-    method public int onProvideLayoutId();
-  }
-
-  public static class GuidanceStylist.Guidance {
-    ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable);
-    method public java.lang.String getBreadcrumb();
-    method public java.lang.String getDescription();
-    method public android.graphics.drawable.Drawable getIconDrawable();
-    method public java.lang.String getTitle();
-  }
-
-  public class GuidedAction extends android.support.v17.leanback.widget.Action {
-    ctor protected GuidedAction();
-    method public int getCheckSetId();
-    method public java.lang.CharSequence getDescription();
-    method public int getDescriptionEditInputType();
-    method public int getDescriptionInputType();
-    method public java.lang.CharSequence getEditDescription();
-    method public int getEditInputType();
-    method public java.lang.CharSequence getEditTitle();
-    method public int getInputType();
-    method public android.content.Intent getIntent();
-    method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getSubActions();
-    method public java.lang.CharSequence getTitle();
-    method public boolean hasEditableActivatorView();
-    method public boolean hasMultilineDescription();
-    method public boolean hasNext();
-    method public boolean hasSubActions();
-    method public boolean hasTextEditable();
-    method public boolean infoOnly();
-    method public final boolean isAutoSaveRestoreEnabled();
-    method public boolean isChecked();
-    method public boolean isDescriptionEditable();
-    method public boolean isEditTitleUsed();
-    method public boolean isEditable();
-    method public boolean isEnabled();
-    method public boolean isFocusable();
-    method public void onRestoreInstanceState(android.os.Bundle, java.lang.String);
-    method public void onSaveInstanceState(android.os.Bundle, java.lang.String);
-    method public void setChecked(boolean);
-    method public void setDescription(java.lang.CharSequence);
-    method public void setEditDescription(java.lang.CharSequence);
-    method public void setEditTitle(java.lang.CharSequence);
-    method public void setEnabled(boolean);
-    method public void setFocusable(boolean);
-    method public void setIntent(android.content.Intent);
-    method public void setSubActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
-    method public void setTitle(java.lang.CharSequence);
-    field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
-    field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
-    field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
-    field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL
-    field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL
-    field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
-    field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
-    field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
-    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
-    field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
-    field public static final int NO_CHECK_SET = 0; // 0x0
-  }
-
-  public static class GuidedAction.Builder extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
-    ctor public deprecated GuidedAction.Builder();
-    ctor public GuidedAction.Builder(android.content.Context);
-    method public android.support.v17.leanback.widget.GuidedAction build();
-  }
-
-  public static abstract class GuidedAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedAction.BuilderBase> {
-    ctor public GuidedAction.BuilderBase(android.content.Context);
-    method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction);
-    method public B autoSaveRestoreEnabled(boolean);
-    method public B checkSetId(int);
-    method public B checked(boolean);
-    method public B clickAction(long);
-    method public B description(java.lang.CharSequence);
-    method public B description(int);
-    method public B descriptionEditInputType(int);
-    method public B descriptionEditable(boolean);
-    method public B descriptionInputType(int);
-    method public B editDescription(java.lang.CharSequence);
-    method public B editDescription(int);
-    method public B editInputType(int);
-    method public B editTitle(java.lang.CharSequence);
-    method public B editTitle(int);
-    method public B editable(boolean);
-    method public B enabled(boolean);
-    method public B focusable(boolean);
-    method public android.content.Context getContext();
-    method public B hasEditableActivatorView(boolean);
-    method public B hasNext(boolean);
-    method public B icon(android.graphics.drawable.Drawable);
-    method public B icon(int);
-    method public deprecated B iconResourceId(int, android.content.Context);
-    method public B id(long);
-    method public B infoOnly(boolean);
-    method public B inputType(int);
-    method public B intent(android.content.Intent);
-    method public B multilineDescription(boolean);
-    method public B subActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
-    method public B title(java.lang.CharSequence);
-    method public B title(int);
-  }
-
-  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
-    ctor public GuidedActionEditText(android.content.Context);
-    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
-    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
-    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
-  }
-
-  public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
-    ctor public GuidedActionsStylist();
-    method public void collapseAction(boolean);
-    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
-    method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
-    method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
-    method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
-    method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
-    method public final boolean isBackKeyToCollapseActivatorView();
-    method public final boolean isBackKeyToCollapseSubActions();
-    method public boolean isButtonActions();
-    method public boolean isExpandTransitionSupported();
-    method public boolean isExpanded();
-    method public boolean isInExpandTransition();
-    method public boolean isSubActionsExpanded();
-    method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
-    method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
-    method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
-    method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
-    method public void onBindActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
-    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
-    method public void onDestroyView();
-    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
-    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
-    method public void onImeAppearing(java.util.List<android.animation.Animator>);
-    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
-    method public int onProvideItemLayoutId();
-    method public int onProvideItemLayoutId(int);
-    method public int onProvideLayoutId();
-    method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
-    method public void setAsButtonActions();
-    method public final void setBackKeyToCollapseActivatorView(boolean);
-    method public final void setBackKeyToCollapseSubActions(boolean);
-    method public deprecated void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
-    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
-    method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
-    field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
-    field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
-  }
-
-  public static class GuidedActionsStylist.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
-    ctor public GuidedActionsStylist.ViewHolder(android.view.View);
-    ctor public GuidedActionsStylist.ViewHolder(android.view.View, boolean);
-    method public android.support.v17.leanback.widget.GuidedAction getAction();
-    method public android.widget.ImageView getCheckmarkView();
-    method public android.widget.ImageView getChevronView();
-    method public android.view.View getContentView();
-    method public android.widget.TextView getDescriptionView();
-    method public android.widget.EditText getEditableDescriptionView();
-    method public android.widget.EditText getEditableTitleView();
-    method public android.view.View getEditingView();
-    method public java.lang.Object getFacet(java.lang.Class<?>);
-    method public android.widget.ImageView getIconView();
-    method public android.widget.TextView getTitleView();
-    method public boolean isInEditing();
-    method public boolean isInEditingActivatorView();
-    method public boolean isInEditingDescription();
-    method public boolean isInEditingText();
-    method public boolean isInEditingTitle();
-    method public boolean isSubAction();
-  }
-
-  public class GuidedDatePickerAction extends android.support.v17.leanback.widget.GuidedAction {
-    ctor public GuidedDatePickerAction();
-    method public long getDate();
-    method public java.lang.String getDatePickerFormat();
-    method public long getMaxDate();
-    method public long getMinDate();
-    method public void setDate(long);
-  }
-
-  public static final class GuidedDatePickerAction.Builder extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase {
-    ctor public GuidedDatePickerAction.Builder(android.content.Context);
-    method public android.support.v17.leanback.widget.GuidedDatePickerAction build();
-  }
-
-  public static abstract class GuidedDatePickerAction.BuilderBase<B extends android.support.v17.leanback.widget.GuidedDatePickerAction.BuilderBase> extends android.support.v17.leanback.widget.GuidedAction.BuilderBase {
-    ctor public GuidedDatePickerAction.BuilderBase(android.content.Context);
-    method protected final void applyDatePickerValues(android.support.v17.leanback.widget.GuidedDatePickerAction);
-    method public B date(long);
-    method public B datePickerFormat(java.lang.String);
-    method public B maxDate(long);
-    method public B minDate(long);
-  }
-
-  public class HeaderItem {
-    ctor public HeaderItem(long, java.lang.String);
-    ctor public HeaderItem(java.lang.String);
-    method public java.lang.CharSequence getContentDescription();
-    method public java.lang.CharSequence getDescription();
-    method public final long getId();
-    method public final java.lang.String getName();
-    method public void setContentDescription(java.lang.CharSequence);
-    method public void setDescription(java.lang.CharSequence);
-  }
-
-  public class HorizontalGridView extends android.support.v7.widget.RecyclerView {
-    ctor public HorizontalGridView(android.content.Context);
-    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet);
-    ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int);
-    method public final boolean getFadingLeftEdge();
-    method public final int getFadingLeftEdgeLength();
-    method public final int getFadingLeftEdgeOffset();
-    method public final boolean getFadingRightEdge();
-    method public final int getFadingRightEdgeLength();
-    method public final int getFadingRightEdgeOffset();
-    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
-    method public final void setFadingLeftEdge(boolean);
-    method public final void setFadingLeftEdgeLength(int);
-    method public final void setFadingLeftEdgeOffset(int);
-    method public final void setFadingRightEdge(boolean);
-    method public final void setFadingRightEdgeLength(int);
-    method public final void setFadingRightEdgeOffset(int);
-    method public void setNumRows(int);
-    method public void setRowHeight(int);
-  }
-
-  public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher {
-    ctor public HorizontalHoverCardSwitcher();
-    method protected void insertView(android.view.View);
-    method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object);
-  }
-
-  public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
-    ctor public deprecated ImageCardView(android.content.Context, int);
-    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
-    ctor public ImageCardView(android.content.Context);
-    ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
-    method public android.graphics.drawable.Drawable getBadgeImage();
-    method public java.lang.CharSequence getContentText();
-    method public android.graphics.drawable.Drawable getInfoAreaBackground();
-    method public android.graphics.drawable.Drawable getMainImage();
-    method public final android.widget.ImageView getMainImageView();
-    method public java.lang.CharSequence getTitleText();
-    method public void setBadgeImage(android.graphics.drawable.Drawable);
-    method public void setContentText(java.lang.CharSequence);
-    method public void setInfoAreaBackground(android.graphics.drawable.Drawable);
-    method public void setInfoAreaBackgroundColor(int);
-    method public void setMainImage(android.graphics.drawable.Drawable);
-    method public void setMainImage(android.graphics.drawable.Drawable, boolean);
-    method public void setMainImageAdjustViewBounds(boolean);
-    method public void setMainImageDimensions(int, int);
-    method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
-    method public void setTitleText(java.lang.CharSequence);
-    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
-    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
-    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
-    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
-    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
-  }
-
-  public abstract interface ImeKeyMonitor {
-    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
-  }
-
-  public static abstract interface ImeKeyMonitor.ImeKeyListener {
-    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
-  }
-
-  public final class ItemAlignmentFacet {
-    ctor public ItemAlignmentFacet();
-    method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs();
-    method public boolean isMultiAlignment();
-    method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]);
-    field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f;
-  }
-
-  public static class ItemAlignmentFacet.ItemAlignmentDef {
-    ctor public ItemAlignmentFacet.ItemAlignmentDef();
-    method public final int getItemAlignmentFocusViewId();
-    method public final int getItemAlignmentOffset();
-    method public final float getItemAlignmentOffsetPercent();
-    method public final int getItemAlignmentViewId();
-    method public boolean isAlignedToTextViewBaseLine();
-    method public final boolean isItemAlignmentOffsetWithPadding();
-    method public final void setAlignedToTextViewBaseline(boolean);
-    method public final void setItemAlignmentFocusViewId(int);
-    method public final void setItemAlignmentOffset(int);
-    method public final void setItemAlignmentOffsetPercent(float);
-    method public final void setItemAlignmentOffsetWithPadding(boolean);
-    method public final void setItemAlignmentViewId(int);
-  }
-
-  public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter {
-    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector);
-    ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    ctor public ItemBridgeAdapter();
-    method public void clear();
-    method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int);
-    method public int getItemCount();
-    method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper();
-    method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper();
-    method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
-    method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int);
-    method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
-    method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener);
-    method public void setPresenter(android.support.v17.leanback.widget.PresenterSelector);
-    method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>);
-    method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper);
-  }
-
-  public static class ItemBridgeAdapter.AdapterListener {
-    ctor public ItemBridgeAdapter.AdapterListener();
-    method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int);
-    method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-    method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder);
-  }
-
-  public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
-    method public final java.lang.Object getExtraObject();
-    method public java.lang.Object getFacet(java.lang.Class<?>);
-    method public final java.lang.Object getItem();
-    method public final android.support.v17.leanback.widget.Presenter getPresenter();
-    method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder();
-    method public void setExtraObject(java.lang.Object);
-  }
-
-  public static abstract class ItemBridgeAdapter.Wrapper {
-    ctor public ItemBridgeAdapter.Wrapper();
-    method public abstract android.view.View createWrapper(android.view.View);
-    method public abstract void wrap(android.view.View, android.view.View);
-  }
-
-  public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper {
-    ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper);
-    method public android.view.View createWrapper(android.view.View);
-    method public void wrap(android.view.View, android.view.View);
-  }
-
-  public class ListRow extends android.support.v17.leanback.widget.Row {
-    ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
-    ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter);
-    ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter);
-    method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
-    method public java.lang.CharSequence getContentDescription();
-    method public void setContentDescription(java.lang.CharSequence);
-  }
-
-  public final class ListRowHoverCardView extends android.widget.LinearLayout {
-    ctor public ListRowHoverCardView(android.content.Context);
-    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet);
-    ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int);
-    method public final java.lang.CharSequence getDescription();
-    method public final java.lang.CharSequence getTitle();
-    method public final void setDescription(java.lang.CharSequence);
-    method public final void setTitle(java.lang.CharSequence);
-  }
-
-  public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
-    ctor public ListRowPresenter();
-    ctor public ListRowPresenter(int);
-    ctor public ListRowPresenter(int, boolean);
-    method public final boolean areChildRoundedCornersEnabled();
-    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
-    method public final void enableChildRoundedCorners(boolean);
-    method public int getExpandedRowHeight();
-    method public final int getFocusZoomFactor();
-    method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector();
-    method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter);
-    method public int getRowHeight();
-    method public final boolean getShadowEnabled();
-    method public final deprecated int getZoomFactor();
-    method public final boolean isFocusDimmerUsed();
-    method public final boolean isKeepChildForeground();
-    method public boolean isUsingDefaultListSelectEffect();
-    method public final boolean isUsingDefaultSelectEffect();
-    method public boolean isUsingDefaultShadow();
-    method public boolean isUsingZOrder(android.content.Context);
-    method public void setExpandedRowHeight(int);
-    method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
-    method public final void setKeepChildForeground(boolean);
-    method public void setNumRows(int);
-    method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int);
-    method public void setRowHeight(int);
-    method public final void setShadowEnabled(boolean);
-  }
-
-  public static class ListRowPresenter.SelectItemViewHolderTask extends android.support.v17.leanback.widget.Presenter.ViewHolderTask {
-    ctor public ListRowPresenter.SelectItemViewHolderTask(int);
-    method public int getItemPosition();
-    method public android.support.v17.leanback.widget.Presenter.ViewHolderTask getItemTask();
-    method public boolean isSmoothScroll();
-    method public void setItemPosition(int);
-    method public void setItemTask(android.support.v17.leanback.widget.Presenter.ViewHolderTask);
-    method public void setSmoothScroll(boolean);
-  }
-
-  public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
-    ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter);
-    method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter();
-    method public final android.support.v17.leanback.widget.HorizontalGridView getGridView();
-    method public android.support.v17.leanback.widget.Presenter.ViewHolder getItemViewHolder(int);
-    method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter();
-    method public int getSelectedPosition();
-  }
-
-  public final class ListRowView extends android.widget.LinearLayout {
-    ctor public ListRowView(android.content.Context);
-    ctor public ListRowView(android.content.Context, android.util.AttributeSet);
-    ctor public ListRowView(android.content.Context, android.util.AttributeSet, int);
-    method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
-  }
-
-  public abstract interface MultiActionsProvider {
-    method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
-  }
-
-  public static class MultiActionsProvider.MultiAction {
-    ctor public MultiActionsProvider.MultiAction(long);
-    method public android.graphics.drawable.Drawable getCurrentDrawable();
-    method public android.graphics.drawable.Drawable[] getDrawables();
-    method public long getId();
-    method public int getIndex();
-    method public void incrementIndex();
-    method public void setDrawables(android.graphics.drawable.Drawable[]);
-    method public void setIndex(int);
-  }
-
-  public abstract class ObjectAdapter {
-    ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter);
-    ctor public ObjectAdapter();
-    method public abstract java.lang.Object get(int);
-    method public long getId(int);
-    method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
-    method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
-    method public final boolean hasStableIds();
-    method public boolean isImmediateNotifySupported();
-    method protected final void notifyChanged();
-    method public final void notifyItemRangeChanged(int, int);
-    method protected final void notifyItemRangeInserted(int, int);
-    method protected final void notifyItemRangeRemoved(int, int);
-    method protected void onHasStableIdsChanged();
-    method protected void onPresenterSelectorChanged();
-    method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
-    method public final void setHasStableIds(boolean);
-    method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector);
-    method public abstract int size();
-    method public final void unregisterAllObservers();
-    method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver);
-    field public static final int NO_ID = -1; // 0xffffffff
-  }
-
-  public static abstract class ObjectAdapter.DataObserver {
-    ctor public ObjectAdapter.DataObserver();
-    method public void onChanged();
-    method public void onItemRangeChanged(int, int);
-    method public void onItemRangeInserted(int, int);
-    method public void onItemRangeRemoved(int, int);
-  }
-
-  public abstract interface OnActionClickedListener {
-    method public abstract void onActionClicked(android.support.v17.leanback.widget.Action);
-  }
-
-  public abstract interface OnChildLaidOutListener {
-    method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long);
-  }
-
-  public abstract deprecated interface OnChildSelectedListener {
-    method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long);
-  }
-
-  public abstract class OnChildViewHolderSelectedListener {
-    ctor public OnChildViewHolderSelectedListener();
-    method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
-    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
-  }
-
-  public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
-  }
-
-  public abstract interface OnItemViewSelectedListener implements android.support.v17.leanback.widget.BaseOnItemViewSelectedListener {
-  }
-
-  public class PageRow extends android.support.v17.leanback.widget.Row {
-    ctor public PageRow(android.support.v17.leanback.widget.HeaderItem);
-    method public final boolean isRenderedAsRowView();
-  }
-
-  public abstract class Parallax<PropertyT extends android.util.Property> {
-    ctor public Parallax();
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
-    method public final PropertyT addProperty(java.lang.String);
-    method public abstract PropertyT createProperty(java.lang.String, int);
-    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
-    method public abstract float getMaxValue();
-    method public final java.util.List<PropertyT> getProperties();
-    method public void removeAllEffects();
-    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
-    method public void updateValues();
-  }
-
-  public static class Parallax.FloatProperty extends android.util.Property {
-    ctor public Parallax.FloatProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
-    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
-    method public final int getIndex();
-    method public final float getValue(android.support.v17.leanback.widget.Parallax);
-    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
-    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
-    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
-    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
-  }
-
-  public static class Parallax.IntProperty extends android.util.Property {
-    ctor public Parallax.IntProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
-    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
-    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
-    method public final int getIndex();
-    method public final int getValue(android.support.v17.leanback.widget.Parallax);
-    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
-    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
-    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
-    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
-  }
-
-  public static class Parallax.PropertyMarkerValue<PropertyT> {
-    ctor public Parallax.PropertyMarkerValue(PropertyT);
-    method public PropertyT getProperty();
-  }
-
-  public abstract class ParallaxEffect {
-    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
-    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
-    method public final void performMapping(android.support.v17.leanback.widget.Parallax);
-    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
-    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
-    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
-    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
-  }
-
-  public abstract class ParallaxTarget {
-    ctor public ParallaxTarget();
-    method public void directUpdate(java.lang.Number);
-    method public boolean isDirectMapping();
-    method public void update(float);
-  }
-
-  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
-    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
-  }
-
-  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
-    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
-  }
-
-  public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
-    ctor public PlaybackControlsRow(java.lang.Object);
-    ctor public PlaybackControlsRow();
-    method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
-    method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
-    method public int getBufferedProgress();
-    method public long getBufferedProgressLong();
-    method public int getCurrentTime();
-    method public long getCurrentTimeLong();
-    method public final android.graphics.drawable.Drawable getImageDrawable();
-    method public final java.lang.Object getItem();
-    method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
-    method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
-    method public int getTotalTime();
-    method public long getTotalTimeLong();
-    method public void setBufferedProgress(int);
-    method public void setBufferedProgressLong(long);
-    method public void setCurrentTime(int);
-    method public void setCurrentTimeLong(long);
-    method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
-    method public final void setImageDrawable(android.graphics.drawable.Drawable);
-    method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
-    method public void setTotalTime(int);
-    method public void setTotalTimeLong(long);
-  }
-
-  public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context);
-    ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int);
-    field public static int OFF;
-    field public static int ON;
-  }
-
-  public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context);
-    ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int);
-  }
-
-  public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context);
-    ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int);
-    field public static int OFF;
-    field public static int ON;
-  }
-
-  public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action {
-    ctor public PlaybackControlsRow.MoreActions(android.content.Context);
-  }
-
-  public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action {
-    ctor public PlaybackControlsRow.MultiAction(int);
-    method public int getActionCount();
-    method public android.graphics.drawable.Drawable getDrawable(int);
-    method public int getIndex();
-    method public java.lang.String getLabel(int);
-    method public java.lang.String getSecondaryLabel(int);
-    method public void nextIndex();
-    method public void setDrawables(android.graphics.drawable.Drawable[]);
-    method public void setIndex(int);
-    method public void setLabels(java.lang.String[]);
-    method public void setSecondaryLabels(java.lang.String[]);
-  }
-
-  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
-    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
-  }
-
-  public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
-    field public static int PAUSE;
-    field public static int PLAY;
-  }
-
-  public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.RepeatAction(android.content.Context);
-    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int);
-    ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int);
-    field public static int ALL;
-    field public static int NONE;
-    field public static int ONE;
-  }
-
-  public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.RewindAction(android.content.Context);
-    ctor public PlaybackControlsRow.RewindAction(android.content.Context, int);
-  }
-
-  public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context);
-    ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int);
-    field public static int OFF;
-    field public static int ON;
-  }
-
-  public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action {
-    ctor public PlaybackControlsRow.SkipNextAction(android.content.Context);
-  }
-
-  public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action {
-    ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context);
-  }
-
-  public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
-    ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int);
-    field public static int OUTLINE;
-    field public static int SOLID;
-  }
-
-  public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
-    ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context);
-  }
-
-  public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction {
-    ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
-  }
-
-  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
-    ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
-    ctor public PlaybackControlsRowPresenter();
-    method public boolean areSecondaryActionsHidden();
-    method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method public int getBackgroundColor();
-    method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener();
-    method public int getProgressColor();
-    method public void setBackgroundColor(int);
-    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
-    method public void setProgressColor(int);
-    method public void setSecondaryActionsHidden(boolean);
-    method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean);
-    method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
-  }
-
-  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
-    field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
-  }
-
-  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
-    ctor public PlaybackRowPresenter();
-    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
-  }
-
-  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
-    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
-  }
-
-  public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
-    ctor public Presenter();
-    method protected static void cancelAnimationsRecursive(android.view.View);
-    method public final java.lang.Object getFacet(java.lang.Class<?>);
-    method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
-    method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener);
-  }
-
-  public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider {
-    ctor public Presenter.ViewHolder(android.view.View);
-    method public final java.lang.Object getFacet(java.lang.Class<?>);
-    method public final void setFacet(java.lang.Class<?>, java.lang.Object);
-    field public final android.view.View view;
-  }
-
-  public static abstract class Presenter.ViewHolderTask {
-    ctor public Presenter.ViewHolderTask();
-    method public void run(android.support.v17.leanback.widget.Presenter.ViewHolder);
-  }
-
-  public abstract class PresenterSelector {
-    ctor public PresenterSelector();
-    method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
-    method public android.support.v17.leanback.widget.Presenter[] getPresenters();
-  }
-
-  public abstract class PresenterSwitcher {
-    ctor public PresenterSwitcher();
-    method public void clear();
-    method public final android.view.ViewGroup getParentViewGroup();
-    method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector);
-    method protected abstract void insertView(android.view.View);
-    method protected void onViewSelected(android.view.View);
-    method public void select(java.lang.Object);
-    method protected void showView(android.view.View, boolean);
-    method public void unselect();
-  }
-
-  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
-    ctor public RecyclerViewParallax();
-    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
-    method public float getMaxValue();
-    method public android.support.v7.widget.RecyclerView getRecyclerView();
-    method public void setRecyclerView(android.support.v7.widget.RecyclerView);
-  }
-
-  public static final class RecyclerViewParallax.ChildPositionProperty extends android.support.v17.leanback.widget.Parallax.IntProperty {
-    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty adapterPosition(int);
-    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty fraction(float);
-    method public int getAdapterPosition();
-    method public float getFraction();
-    method public int getOffset();
-    method public int getViewId();
-    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty offset(int);
-    method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty viewId(int);
-  }
-
-  public class Row {
-    ctor public Row(long, android.support.v17.leanback.widget.HeaderItem);
-    ctor public Row(android.support.v17.leanback.widget.HeaderItem);
-    ctor public Row();
-    method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem();
-    method public final long getId();
-    method public boolean isRenderedAsRowView();
-    method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem);
-    method public final void setId(long);
-  }
-
-  public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter {
-    ctor public RowHeaderPresenter();
-    method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint);
-    method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
-    method public boolean isNullItemVisibilityGone();
-    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder);
-    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public void setNullItemVisibilityGone(boolean);
-    method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float);
-  }
-
-  public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
-    ctor public RowHeaderPresenter.ViewHolder(android.view.View);
-    method public final float getSelectLevel();
-  }
-
-  public final class RowHeaderView extends android.widget.TextView {
-    ctor public RowHeaderView(android.content.Context);
-    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet);
-    ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter {
-    ctor public RowPresenter();
-    method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
-    method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
-    method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
-    method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter();
-    method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public final boolean getSelectEffectEnabled();
-    method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public final int getSyncActivatePolicy();
-    method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
-    method protected boolean isClippingChildren();
-    method public boolean isUsingDefaultSelectEffect();
-    method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object);
-    method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
-    method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
-    method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
-    method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
-    method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
-    method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
-    method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean);
-    method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter);
-    method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
-    method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean);
-    method public final void setSelectEffectEnabled(boolean);
-    method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float);
-    method public final void setSyncActivatePolicy(int);
-    field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0
-    field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1
-    field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3
-    field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2
-  }
-
-  public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
-    ctor public RowPresenter.ViewHolder(android.view.View);
-    method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder();
-    method public final android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
-    method public final android.support.v17.leanback.widget.BaseOnItemViewSelectedListener getOnItemViewSelectedListener();
-    method public android.view.View.OnKeyListener getOnKeyListener();
-    method public final android.support.v17.leanback.widget.Row getRow();
-    method public final java.lang.Object getRowObject();
-    method public final float getSelectLevel();
-    method public java.lang.Object getSelectedItem();
-    method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
-    method public final boolean isExpanded();
-    method public final boolean isSelected();
-    method public final void setActivated(boolean);
-    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
-    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.BaseOnItemViewSelectedListener);
-    method public void setOnKeyListener(android.view.View.OnKeyListener);
-    method public final void syncActivatedStatus(android.view.View);
-    field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer;
-  }
-
-  public class SearchBar extends android.widget.RelativeLayout {
-    ctor public SearchBar(android.content.Context);
-    ctor public SearchBar(android.content.Context, android.util.AttributeSet);
-    ctor public SearchBar(android.content.Context, android.util.AttributeSet, int);
-    method public void displayCompletions(java.util.List<java.lang.String>);
-    method public void displayCompletions(android.view.inputmethod.CompletionInfo[]);
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public java.lang.CharSequence getHint();
-    method public java.lang.String getTitle();
-    method public boolean isRecognizing();
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
-    method public void setSearchQuery(java.lang.String);
-    method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
-    method public void setSpeechRecognizer(android.speech.SpeechRecognizer);
-    method public void setTitle(java.lang.String);
-    method public void startRecognition();
-    method public void stopRecognition();
-  }
-
-  public static abstract interface SearchBar.SearchBarListener {
-    method public abstract void onKeyboardDismiss(java.lang.String);
-    method public abstract void onSearchQueryChange(java.lang.String);
-    method public abstract void onSearchQuerySubmit(java.lang.String);
-  }
-
-  public static abstract interface SearchBar.SearchBarPermissionListener {
-    method public abstract void requestAudioPermission();
-  }
-
-  public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView {
-    ctor public SearchEditText(android.content.Context);
-    ctor public SearchEditText(android.content.Context, android.util.AttributeSet);
-    ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int);
-    method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener);
-  }
-
-  public static abstract interface SearchEditText.OnKeyboardDismissListener {
-    method public abstract void onKeyboardDismiss();
-  }
-
-  public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
-    ctor public SearchOrbView(android.content.Context);
-    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet);
-    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int);
-    method public void enableOrbColorAnimation(boolean);
-    method public int getOrbColor();
-    method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors();
-    method public android.graphics.drawable.Drawable getOrbIcon();
-    method public void onClick(android.view.View);
-    method public void setOnOrbClickedListener(android.view.View.OnClickListener);
-    method public void setOrbColor(int);
-    method public deprecated void setOrbColor(int, int);
-    method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setOrbIcon(android.graphics.drawable.Drawable);
-  }
-
-  public static class SearchOrbView.Colors {
-    ctor public SearchOrbView.Colors(int);
-    ctor public SearchOrbView.Colors(int, int);
-    ctor public SearchOrbView.Colors(int, int, int);
-    method public static int getBrightColor(int);
-    field public int brightColor;
-    field public int color;
-    field public int iconColor;
-  }
-
-  public class SectionRow extends android.support.v17.leanback.widget.Row {
-    ctor public SectionRow(android.support.v17.leanback.widget.HeaderItem);
-    ctor public SectionRow(long, java.lang.String);
-    ctor public SectionRow(java.lang.String);
-    method public final boolean isRenderedAsRowView();
-  }
-
-  public class ShadowOverlayContainer extends android.widget.FrameLayout {
-    ctor public ShadowOverlayContainer(android.content.Context);
-    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
-    ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
-    method public int getShadowType();
-    method public android.view.View getWrappedView();
-    method public deprecated void initialize(boolean, boolean);
-    method public deprecated void initialize(boolean, boolean, boolean);
-    method public static void prepareParentForShadow(android.view.ViewGroup);
-    method public void setOverlayColor(int);
-    method public void setShadowFocusLevel(float);
-    method public static boolean supportsDynamicShadow();
-    method public static boolean supportsShadow();
-    method public void useDynamicShadow();
-    method public void useDynamicShadow(float, float);
-    method public void useStaticShadow();
-    method public void wrap(android.view.View);
-    field public static final int SHADOW_DYNAMIC = 3; // 0x3
-    field public static final int SHADOW_NONE = 1; // 0x1
-    field public static final int SHADOW_STATIC = 2; // 0x2
-  }
-
-  public final class ShadowOverlayHelper {
-    method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context);
-    method public int getShadowType();
-    method public boolean needsOverlay();
-    method public boolean needsRoundedCorner();
-    method public boolean needsWrapper();
-    method public void onViewCreated(android.view.View);
-    method public void prepareParentForShadow(android.view.ViewGroup);
-    method public static void setNoneWrapperOverlayColor(android.view.View, int);
-    method public static void setNoneWrapperShadowFocusLevel(android.view.View, float);
-    method public void setOverlayColor(android.view.View, int);
-    method public void setShadowFocusLevel(android.view.View, float);
-    method public static boolean supportsDynamicShadow();
-    method public static boolean supportsForeground();
-    method public static boolean supportsRoundedCorner();
-    method public static boolean supportsShadow();
-    field public static final int SHADOW_DYNAMIC = 3; // 0x3
-    field public static final int SHADOW_NONE = 1; // 0x1
-    field public static final int SHADOW_STATIC = 2; // 0x2
-  }
-
-  public static final class ShadowOverlayHelper.Builder {
-    ctor public ShadowOverlayHelper.Builder();
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context);
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean);
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean);
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean);
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean);
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options);
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean);
-  }
-
-  public static final class ShadowOverlayHelper.Options {
-    ctor public ShadowOverlayHelper.Options();
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float);
-    method public final float getDynamicShadowFocusedZ();
-    method public final float getDynamicShadowUnfocusedZ();
-    method public final int getRoundedCornerRadius();
-    method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int);
-    field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT;
-  }
-
-  public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector {
-    ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter);
-    method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object);
-  }
-
-  public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter {
-    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter);
-    ctor public SparseArrayObjectAdapter();
-    method public void clear(int);
-    method public void clear();
-    method public java.lang.Object get(int);
-    method public int indexOf(java.lang.Object);
-    method public int indexOf(int);
-    method public java.lang.Object lookup(int);
-    method public void notifyArrayItemRangeChanged(int, int);
-    method public void set(int, java.lang.Object);
-    method public int size();
-  }
-
-  public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView {
-    ctor public SpeechOrbView(android.content.Context);
-    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
-    ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
-    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setSoundLevel(int);
-    method public void showListening();
-    method public void showNotListening();
-  }
-
-  public abstract interface SpeechRecognitionCallback {
-    method public abstract void recognizeSpeech();
-  }
-
-   class StreamingTextView extends android.widget.EditText {
-    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet);
-    ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int);
-    method public static boolean isLayoutRtl(android.view.View);
-    method public void reset();
-    method public void setFinalRecognizedText(java.lang.CharSequence);
-    method public void updateRecognizedText(java.lang.String, java.lang.String);
-    method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>);
-  }
-
-  public class TitleHelper {
-    ctor public TitleHelper(android.view.ViewGroup, android.view.View);
-    method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
-    method public android.view.ViewGroup getSceneRoot();
-    method public android.view.View getTitleView();
-    method public void showTitle(boolean);
-  }
-
-  public class TitleView extends android.widget.FrameLayout implements android.support.v17.leanback.widget.TitleViewAdapter.Provider {
-    ctor public TitleView(android.content.Context);
-    ctor public TitleView(android.content.Context, android.util.AttributeSet);
-    ctor public TitleView(android.content.Context, android.util.AttributeSet, int);
-    method public void enableAnimation(boolean);
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
-    method public android.view.View getSearchAffordanceView();
-    method public java.lang.CharSequence getTitle();
-    method public android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setTitle(java.lang.CharSequence);
-    method public void updateComponentsVisibility(int);
-  }
-
-  public abstract class TitleViewAdapter {
-    ctor public TitleViewAdapter();
-    method public android.graphics.drawable.Drawable getBadgeDrawable();
-    method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors();
-    method public abstract android.view.View getSearchAffordanceView();
-    method public java.lang.CharSequence getTitle();
-    method public void setAnimationEnabled(boolean);
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener);
-    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
-    method public void setTitle(java.lang.CharSequence);
-    method public void updateComponentsVisibility(int);
-    field public static final int BRANDING_VIEW_VISIBLE = 2; // 0x2
-    field public static final int FULL_VIEW_VISIBLE = 6; // 0x6
-    field public static final int SEARCH_VIEW_VISIBLE = 4; // 0x4
-  }
-
-  public static abstract interface TitleViewAdapter.Provider {
-    method public abstract android.support.v17.leanback.widget.TitleViewAdapter getTitleViewAdapter();
-  }
-
-  public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter {
-    ctor public VerticalGridPresenter();
-    ctor public VerticalGridPresenter(int);
-    ctor public VerticalGridPresenter(int, boolean);
-    method public final boolean areChildRoundedCornersEnabled();
-    method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup);
-    method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions();
-    method public final void enableChildRoundedCorners(boolean);
-    method public final int getFocusZoomFactor();
-    method public final boolean getKeepChildForeground();
-    method public int getNumberOfColumns();
-    method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
-    method public final boolean getShadowEnabled();
-    method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder);
-    method public final boolean isFocusDimmerUsed();
-    method public boolean isUsingDefaultShadow();
-    method public boolean isUsingZOrder(android.content.Context);
-    method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
-    method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
-    method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
-    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
-    method public final void setKeepChildForeground(boolean);
-    method public void setNumberOfColumns(int);
-    method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
-    method public final void setShadowEnabled(boolean);
-  }
-
-  public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder {
-    ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView);
-    method public android.support.v17.leanback.widget.VerticalGridView getGridView();
-  }
-
-  public class VerticalGridView extends android.support.v7.widget.RecyclerView {
-    ctor public VerticalGridView(android.content.Context);
-    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet);
-    ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int);
-    method protected void initAttributes(android.content.Context, android.util.AttributeSet);
-    method public void setColumnWidth(int);
-    method public void setNumColumns(int);
-  }
-
-  public abstract interface ViewHolderTask {
-    method public abstract void run(android.support.v7.widget.RecyclerView.ViewHolder);
-  }
-
-}
-
-package android.support.v17.leanback.widget.picker {
-
-  public class Picker extends android.widget.FrameLayout {
-    ctor public Picker(android.content.Context, android.util.AttributeSet, int);
-    method public void addOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
-    method public float getActivatedVisibleItemCount();
-    method public android.support.v17.leanback.widget.picker.PickerColumn getColumnAt(int);
-    method public int getColumnsCount();
-    method protected int getPickerItemHeightPixels();
-    method public final int getPickerItemLayoutId();
-    method public final int getPickerItemTextViewId();
-    method public int getSelectedColumn();
-    method public final java.lang.CharSequence getSeparator();
-    method public float getVisibleItemCount();
-    method public void onColumnValueChanged(int, int);
-    method public void removeOnValueChangedListener(android.support.v17.leanback.widget.picker.Picker.PickerValueListener);
-    method public void setActivatedVisibleItemCount(float);
-    method public void setColumnAt(int, android.support.v17.leanback.widget.picker.PickerColumn);
-    method public void setColumnValue(int, int, boolean);
-    method public void setColumns(java.util.List<android.support.v17.leanback.widget.picker.PickerColumn>);
-    method public final void setPickerItemTextViewId(int);
-    method public void setSelectedColumn(int);
-    method public final void setSeparator(java.lang.CharSequence);
-    method public void setVisibleItemCount(float);
-  }
-
-  public static abstract interface Picker.PickerValueListener {
-    method public abstract void onValueChanged(android.support.v17.leanback.widget.picker.Picker, int);
-  }
-
-  public class PickerColumn {
-    ctor public PickerColumn();
-    method public int getCount();
-    method public int getCurrentValue();
-    method public java.lang.CharSequence getLabelFor(int);
-    method public java.lang.String getLabelFormat();
-    method public int getMaxValue();
-    method public int getMinValue();
-    method public java.lang.CharSequence[] getStaticLabels();
-    method public void setCurrentValue(int);
-    method public void setLabelFormat(java.lang.String);
-    method public void setMaxValue(int);
-    method public void setMinValue(int);
-    method public void setStaticLabels(java.lang.CharSequence[]);
-  }
-
-  public class TimePicker extends android.support.v17.leanback.widget.picker.Picker {
-    ctor public TimePicker(android.content.Context, android.util.AttributeSet);
-    ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
-    method public int getHour();
-    method public int getMinute();
-    method public boolean is24Hour();
-    method public boolean isPm();
-    method public void setHour(int);
-    method public void setIs24Hour(boolean);
-    method public void setMinute(int);
-  }
-
-}
-
-package android.support.v17.preference {
-
-  public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment {
-    ctor public BaseLeanbackPreferenceFragment();
-  }
-
-  public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment {
-    ctor public LeanbackListPreferenceDialogFragment();
-    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String);
-    method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String);
-    method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter();
-  }
-
-  public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
-    ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>);
-    method public int getItemCount();
-    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
-    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
-    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
-  }
-
-  public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
-    ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence);
-    method public int getItemCount();
-    method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int);
-    method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
-    method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
-  }
-
-  public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener {
-    ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener);
-    method public android.view.ViewGroup getContainer();
-    method public android.widget.TextView getTitleView();
-    method public android.widget.Checkable getWidgetView();
-    method public void onClick(android.view.View);
-  }
-
-  public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener {
-    method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder);
-  }
-
-  public class LeanbackPreferenceDialogFragment extends android.app.Fragment {
-    ctor public LeanbackPreferenceDialogFragment();
-    method public android.support.v7.preference.DialogPreference getPreference();
-    field public static final java.lang.String ARG_KEY = "key";
-  }
-
-  public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
-    ctor public LeanbackPreferenceFragment();
-    method public void setTitle(java.lang.CharSequence);
-  }
-
-  public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
-    ctor public LeanbackSettingsFragment();
-    method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference);
-    method public abstract void onPreferenceStartInitialScreen();
-    method public void startImmersiveFragment(android.app.Fragment);
-    method public void startPreferenceFragment(android.app.Fragment);
-  }
-
-}
-
-package android.support.v4.accessibilityservice {
-
-  public final class AccessibilityServiceInfoCompat {
-    method public static java.lang.String capabilityToString(int);
-    method public static java.lang.String feedbackTypeToString(int);
-    method public static java.lang.String flagToString(int);
-    method public static boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo);
-    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
-    method public static deprecated java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo);
-    method public static java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo);
-    method public static android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo);
-    method public static java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo);
-    method public static java.lang.String loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
-    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
-    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
-    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
-    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
-    field public static final int DEFAULT = 1; // 0x1
-    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
-    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
-    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
-    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
-    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
-    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
-    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
-  }
-
-}
-
-package android.support.v4.app {
-
-  public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
-    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int);
-    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int);
-    method public boolean isDrawerIndicatorEnabled();
-    method public void onConfigurationChanged(android.content.res.Configuration);
-    method public void onDrawerClosed(android.view.View);
-    method public void onDrawerOpened(android.view.View);
-    method public void onDrawerSlide(android.view.View, float);
-    method public void onDrawerStateChanged(int);
-    method public boolean onOptionsItemSelected(android.view.MenuItem);
-    method public void setDrawerIndicatorEnabled(boolean);
-    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
-    method public void setHomeAsUpIndicator(int);
-    method public void syncState();
-  }
-
-  public static abstract deprecated interface ActionBarDrawerToggle.Delegate {
-    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
-    method public abstract void setActionBarDescription(int);
-    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
-  }
-
-  public static abstract deprecated interface ActionBarDrawerToggle.DelegateProvider {
-    method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
-  }
-
-  public class ActivityCompat extends android.support.v4.content.ContextCompat {
-    ctor protected ActivityCompat();
-    method public static void finishAffinity(android.app.Activity);
-    method public static void finishAfterTransition(android.app.Activity);
-    method public static android.net.Uri getReferrer(android.app.Activity);
-    method public static boolean invalidateOptionsMenu(android.app.Activity);
-    method public static void postponeEnterTransition(android.app.Activity);
-    method public static void requestPermissions(android.app.Activity, java.lang.String[], int);
-    method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
-    method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback);
-    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String);
-    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle);
-    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
-    method public static void startPostponedEnterTransition(android.app.Activity);
-  }
-
-  public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback {
-    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
-  }
-
-  public final class ActivityManagerCompat {
-    method public static boolean isLowRamDevice(android.app.ActivityManager);
-  }
-
-  public class ActivityOptionsCompat {
-    ctor protected ActivityOptionsCompat();
-    method public android.graphics.Rect getLaunchBounds();
-    method public static android.support.v4.app.ActivityOptionsCompat makeBasic();
-    method public static android.support.v4.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
-    method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
-    method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
-    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
-    method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...);
-    method public static android.support.v4.app.ActivityOptionsCompat makeTaskLaunchBehind();
-    method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
-    method public void requestUsageTimeReport(android.app.PendingIntent);
-    method public android.support.v4.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect);
-    method public android.os.Bundle toBundle();
-    method public void update(android.support.v4.app.ActivityOptionsCompat);
-    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
-    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
-  }
-
-  public class AppLaunchChecker {
-    ctor public AppLaunchChecker();
-    method public static boolean hasStartedFromLauncher(android.content.Context);
-    method public static void onActivityCreate(android.app.Activity);
-  }
-
-  public final class AppOpsManagerCompat {
-    method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
-    method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
-    method public static java.lang.String permissionToOp(java.lang.String);
-    field public static final int MODE_ALLOWED = 0; // 0x0
-    field public static final int MODE_DEFAULT = 3; // 0x3
-    field public static final int MODE_IGNORED = 1; // 0x1
-  }
-
-  public final class BundleCompat {
-    method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String);
-    method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder);
-  }
-
-  public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
-    ctor public DialogFragment();
-    method public void dismiss();
-    method public void dismissAllowingStateLoss();
-    method public android.app.Dialog getDialog();
-    method public boolean getShowsDialog();
-    method public int getTheme();
-    method public boolean isCancelable();
-    method public void onCancel(android.content.DialogInterface);
-    method public android.app.Dialog onCreateDialog(android.os.Bundle);
-    method public void onDismiss(android.content.DialogInterface);
-    method public void setCancelable(boolean);
-    method public void setShowsDialog(boolean);
-    method public void setStyle(int, int);
-    method public void show(android.support.v4.app.FragmentManager, java.lang.String);
-    method public int show(android.support.v4.app.FragmentTransaction, java.lang.String);
-    field public static final int STYLE_NORMAL = 0; // 0x0
-    field public static final int STYLE_NO_FRAME = 2; // 0x2
-    field public static final int STYLE_NO_INPUT = 3; // 0x3
-    field public static final int STYLE_NO_TITLE = 1; // 0x1
-  }
-
-  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
-    ctor public Fragment();
-    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public final boolean equals(java.lang.Object);
-    method public final android.support.v4.app.FragmentActivity getActivity();
-    method public boolean getAllowEnterTransitionOverlap();
-    method public boolean getAllowReturnTransitionOverlap();
-    method public final android.os.Bundle getArguments();
-    method public final android.support.v4.app.FragmentManager getChildFragmentManager();
-    method public android.content.Context getContext();
-    method public java.lang.Object getEnterTransition();
-    method public java.lang.Object getExitTransition();
-    method public final android.support.v4.app.FragmentManager getFragmentManager();
-    method public final java.lang.Object getHost();
-    method public final int getId();
-    method public final android.view.LayoutInflater getLayoutInflater();
-    method public android.support.v4.app.LoaderManager getLoaderManager();
-    method public final android.support.v4.app.Fragment getParentFragment();
-    method public java.lang.Object getReenterTransition();
-    method public final android.content.res.Resources getResources();
-    method public final boolean getRetainInstance();
-    method public java.lang.Object getReturnTransition();
-    method public java.lang.Object getSharedElementEnterTransition();
-    method public java.lang.Object getSharedElementReturnTransition();
-    method public final java.lang.String getString(int);
-    method public final java.lang.String getString(int, java.lang.Object...);
-    method public final java.lang.String getTag();
-    method public final android.support.v4.app.Fragment getTargetFragment();
-    method public final int getTargetRequestCode();
-    method public final java.lang.CharSequence getText(int);
-    method public boolean getUserVisibleHint();
-    method public android.view.View getView();
-    method public final int hashCode();
-    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String);
-    method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
-    method public final boolean isAdded();
-    method public final boolean isDetached();
-    method public final boolean isHidden();
-    method public final boolean isInLayout();
-    method public final boolean isRemoving();
-    method public final boolean isResumed();
-    method public final boolean isVisible();
-    method public void onActivityCreated(android.os.Bundle);
-    method public void onActivityResult(int, int, android.content.Intent);
-    method public void onAttach(android.content.Context);
-    method public deprecated void onAttach(android.app.Activity);
-    method public void onAttachFragment(android.support.v4.app.Fragment);
-    method public void onConfigurationChanged(android.content.res.Configuration);
-    method public boolean onContextItemSelected(android.view.MenuItem);
-    method public void onCreate(android.os.Bundle);
-    method public android.view.animation.Animation onCreateAnimation(int, boolean, int);
-    method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
-    method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
-    method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void onDestroy();
-    method public void onDestroyOptionsMenu();
-    method public void onDestroyView();
-    method public void onDetach();
-    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
-    method public void onHiddenChanged(boolean);
-    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
-    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
-    method public void onLowMemory();
-    method public void onMultiWindowModeChanged(boolean);
-    method public boolean onOptionsItemSelected(android.view.MenuItem);
-    method public void onOptionsMenuClosed(android.view.Menu);
-    method public void onPause();
-    method public void onPictureInPictureModeChanged(boolean);
-    method public void onPrepareOptionsMenu(android.view.Menu);
-    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
-    method public void onResume();
-    method public void onSaveInstanceState(android.os.Bundle);
-    method public void onStart();
-    method public void onStop();
-    method public void onViewCreated(android.view.View, android.os.Bundle);
-    method public void onViewStateRestored(android.os.Bundle);
-    method public void postponeEnterTransition();
-    method public void registerForContextMenu(android.view.View);
-    method public final void requestPermissions(java.lang.String[], int);
-    method public void setAllowEnterTransitionOverlap(boolean);
-    method public void setAllowReturnTransitionOverlap(boolean);
-    method public void setArguments(android.os.Bundle);
-    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
-    method public void setEnterTransition(java.lang.Object);
-    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
-    method public void setExitTransition(java.lang.Object);
-    method public void setHasOptionsMenu(boolean);
-    method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState);
-    method public void setMenuVisibility(boolean);
-    method public void setReenterTransition(java.lang.Object);
-    method public void setRetainInstance(boolean);
-    method public void setReturnTransition(java.lang.Object);
-    method public void setSharedElementEnterTransition(java.lang.Object);
-    method public void setSharedElementReturnTransition(java.lang.Object);
-    method public void setTargetFragment(android.support.v4.app.Fragment, int);
-    method public void setUserVisibleHint(boolean);
-    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
-    method public void startActivity(android.content.Intent);
-    method public void startActivity(android.content.Intent, android.os.Bundle);
-    method public void startActivityForResult(android.content.Intent, int);
-    method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
-    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
-    method public void startPostponedEnterTransition();
-    method public void unregisterForContextMenu(android.view.View);
-  }
-
-  public static class Fragment.InstantiationException extends java.lang.RuntimeException {
-    ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
-  }
-
-  public static class Fragment.SavedState implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR;
-  }
-
-  public class FragmentActivity extends android.app.Activity implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback {
-    ctor public FragmentActivity();
-    method public java.lang.Object getLastCustomNonConfigurationInstance();
-    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
-    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
-    method public final deprecated android.support.v4.media.session.MediaControllerCompat getSupportMediaController();
-    method public void onAttachFragment(android.support.v4.app.Fragment);
-    method protected void onResumeFragments();
-    method public java.lang.Object onRetainCustomNonConfigurationInstance();
-    method public final java.lang.Object onRetainNonConfigurationInstance();
-    method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
-    method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
-    method public final deprecated void setSupportMediaController(android.support.v4.media.session.MediaControllerCompat);
-    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
-    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
-    method public void startIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
-    method public void supportFinishAfterTransition();
-    method public void supportInvalidateOptionsMenu();
-    method public void supportPostponeEnterTransition();
-    method public void supportStartPostponedEnterTransition();
-    method public final void validateRequestPermissionsRequestCode(int);
-  }
-
-  public abstract class FragmentContainer {
-    ctor public FragmentContainer();
-    method public abstract android.view.View onFindViewById(int);
-    method public abstract boolean onHasView();
-  }
-
-  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 void dispatchActivityCreated();
-    method public void dispatchConfigurationChanged(android.content.res.Configuration);
-    method public boolean dispatchContextItemSelected(android.view.MenuItem);
-    method public void dispatchCreate();
-    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
-    method public void dispatchDestroy();
-    method public void dispatchDestroyView();
-    method public void dispatchLowMemory();
-    method public void dispatchMultiWindowModeChanged(boolean);
-    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
-    method public void dispatchOptionsMenuClosed(android.view.Menu);
-    method public void dispatchPause();
-    method public void dispatchPictureInPictureModeChanged(boolean);
-    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
-    method public void dispatchReallyStop();
-    method public void dispatchResume();
-    method public void dispatchStart();
-    method public void dispatchStop();
-    method public void doLoaderDestroy();
-    method public void doLoaderRetain();
-    method public void doLoaderStart();
-    method public void doLoaderStop(boolean);
-    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public boolean execPendingActions();
-    method public android.support.v4.app.Fragment findFragmentByWho(java.lang.String);
-    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
-    method public int getActiveFragmentsCount();
-    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
-    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
-    method public void noteStateNotSaved();
-    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
-    method public void reportLoaderStart();
-    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>);
-    method public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig);
-    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
-    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
-    method public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig();
-    method public deprecated java.util.List<android.support.v4.app.Fragment> retainNonConfig();
-    method public android.os.Parcelable saveAllState();
-  }
-
-  public abstract class FragmentHostCallback<E> extends android.support.v4.app.FragmentContainer {
-    ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
-    method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public android.view.View onFindViewById(int);
-    method public abstract E onGetHost();
-    method public android.view.LayoutInflater onGetLayoutInflater();
-    method public int onGetWindowAnimations();
-    method public boolean onHasView();
-    method public boolean onHasWindowAnimations();
-    method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int);
-    method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment);
-    method public boolean onShouldShowRequestPermissionRationale(java.lang.String);
-    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
-    method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int, android.os.Bundle);
-    method public void onStartIntentSenderFromFragment(android.support.v4.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
-    method public void onSupportInvalidateOptionsMenu();
-  }
-
-  public abstract class FragmentManager {
-    ctor public FragmentManager();
-    method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
-    method public abstract android.support.v4.app.FragmentTransaction beginTransaction();
-    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public static void enableDebugLogging(boolean);
-    method public abstract boolean executePendingTransactions();
-    method public abstract android.support.v4.app.Fragment findFragmentById(int);
-    method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String);
-    method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
-    method public abstract int getBackStackEntryCount();
-    method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String);
-    method public abstract java.util.List<android.support.v4.app.Fragment> getFragments();
-    method public abstract boolean isDestroyed();
-    method public abstract void popBackStack();
-    method public abstract void popBackStack(java.lang.String, int);
-    method public abstract void popBackStack(int, int);
-    method public abstract boolean popBackStackImmediate();
-    method public abstract boolean popBackStackImmediate(java.lang.String, int);
-    method public abstract boolean popBackStackImmediate(int, int);
-    method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
-    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
-    method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
-    method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
-    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
-    field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
-  }
-
-  public static abstract interface FragmentManager.BackStackEntry {
-    method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
-    method public abstract int getBreadCrumbShortTitleRes();
-    method public abstract java.lang.CharSequence getBreadCrumbTitle();
-    method public abstract int getBreadCrumbTitleRes();
-    method public abstract int getId();
-    method public abstract java.lang.String getName();
-  }
-
-  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
-    ctor public FragmentManager.FragmentLifecycleCallbacks();
-    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
-    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
-    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
-    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
-    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
-    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
-    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
-  }
-
-  public static abstract interface FragmentManager.OnBackStackChangedListener {
-    method public abstract void onBackStackChanged();
-  }
-
-  public class FragmentManagerNonConfig {
-  }
-
-  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
-    ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager);
-    method public abstract android.support.v4.app.Fragment getItem(int);
-    method public long getItemId(int);
-    method public boolean isViewFromObject(android.view.View, java.lang.Object);
-  }
-
-  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
-    ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager);
-    method public abstract android.support.v4.app.Fragment getItem(int);
-    method public boolean isViewFromObject(android.view.View, java.lang.Object);
-  }
-
-  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
-    ctor public FragmentTabHost(android.content.Context);
-    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
-    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
-    method public void onTabChanged(java.lang.String);
-    method public void setup(android.content.Context, android.support.v4.app.FragmentManager);
-    method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int);
-  }
-
-  public abstract class FragmentTransaction {
-    ctor public FragmentTransaction();
-    method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String);
-    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment);
-    method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String);
-    method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String);
-    method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String);
-    method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment);
-    method public abstract int commit();
-    method public abstract int commitAllowingStateLoss();
-    method public abstract void commitNow();
-    method public abstract void commitNowAllowingStateLoss();
-    method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment);
-    method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack();
-    method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
-    method public abstract boolean isAddToBackStackAllowed();
-    method public abstract boolean isEmpty();
-    method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
-    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
-    method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
-    method public abstract android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
-    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
-    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
-    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
-    method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
-    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int);
-    method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int);
-    method public abstract android.support.v4.app.FragmentTransaction setTransition(int);
-    method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int);
-    method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment);
-    field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
-    field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
-    field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
-    field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
-    field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
-    field public static final int TRANSIT_NONE = 0; // 0x0
-    field public static final int TRANSIT_UNSET = -1; // 0xffffffff
-  }
-
-  public class ListFragment extends android.support.v4.app.Fragment {
-    ctor public ListFragment();
-    method public android.widget.ListAdapter getListAdapter();
-    method public android.widget.ListView getListView();
-    method public long getSelectedItemId();
-    method public int getSelectedItemPosition();
-    method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
-    method public void setEmptyText(java.lang.CharSequence);
-    method public void setListAdapter(android.widget.ListAdapter);
-    method public void setListShown(boolean);
-    method public void setListShownNoAnimation(boolean);
-    method public void setSelection(int);
-  }
-
-  public abstract class LoaderManager {
-    ctor public LoaderManager();
-    method public abstract void destroyLoader(int);
-    method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public static void enableDebugLogging(boolean);
-    method public abstract <D> android.support.v4.content.Loader<D> getLoader(int);
-    method public boolean hasRunningLoaders();
-    method public abstract <D> android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
-    method public abstract <D> android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>);
-  }
-
-  public static abstract interface LoaderManager.LoaderCallbacks<D> {
-    method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle);
-    method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D);
-    method public abstract void onLoaderReset(android.support.v4.content.Loader<D>);
-  }
-
-  public final class NavUtils {
-    method public static android.content.Intent getParentActivityIntent(android.app.Activity);
-    method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public static java.lang.String getParentActivityName(android.app.Activity);
-    method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public static void navigateUpFromSameTask(android.app.Activity);
-    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
-    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
-    field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
-  }
-
-  public class NotificationCompat {
-    ctor public NotificationCompat();
-    method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int);
-    method public static int getActionCount(android.app.Notification);
-    method public static java.lang.String getCategory(android.app.Notification);
-    method public static android.os.Bundle getExtras(android.app.Notification);
-    method public static java.lang.String getGroup(android.app.Notification);
-    method public static boolean getLocalOnly(android.app.Notification);
-    method public static java.lang.String getSortKey(android.app.Notification);
-    method public static boolean isGroupSummary(android.app.Notification);
-    field public static final java.lang.String CATEGORY_ALARM = "alarm";
-    field public static final java.lang.String CATEGORY_CALL = "call";
-    field public static final java.lang.String CATEGORY_EMAIL = "email";
-    field public static final java.lang.String CATEGORY_ERROR = "err";
-    field public static final java.lang.String CATEGORY_EVENT = "event";
-    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
-    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
-    field public static final java.lang.String CATEGORY_PROMO = "promo";
-    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
-    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
-    field public static final java.lang.String CATEGORY_SERVICE = "service";
-    field public static final java.lang.String CATEGORY_SOCIAL = "social";
-    field public static final java.lang.String CATEGORY_STATUS = "status";
-    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
-    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
-    field public static final int COLOR_DEFAULT = 0; // 0x0
-    field public static final int DEFAULT_ALL = -1; // 0xffffffff
-    field public static final int DEFAULT_LIGHTS = 4; // 0x4
-    field public static final int DEFAULT_SOUND = 1; // 0x1
-    field public static final int DEFAULT_VIBRATE = 2; // 0x2
-    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
-    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
-    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
-    field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
-    field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
-    field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
-    field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
-    field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
-    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
-    field public static final java.lang.String EXTRA_PEOPLE = "android.people";
-    field public static final java.lang.String EXTRA_PICTURE = "android.picture";
-    field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
-    field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
-    field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
-    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
-    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
-    field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
-    field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
-    field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
-    field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText";
-    field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText";
-    field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
-    field public static final java.lang.String EXTRA_TEXT = "android.text";
-    field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
-    field public static final java.lang.String EXTRA_TITLE = "android.title";
-    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
-    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
-    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
-    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
-    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
-    field public static final int FLAG_INSISTENT = 4; // 0x4
-    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
-    field public static final int FLAG_NO_CLEAR = 32; // 0x20
-    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
-    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
-    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
-    field public static final int PRIORITY_DEFAULT = 0; // 0x0
-    field public static final int PRIORITY_HIGH = 1; // 0x1
-    field public static final int PRIORITY_LOW = -1; // 0xffffffff
-    field public static final int PRIORITY_MAX = 2; // 0x2
-    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
-    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
-    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
-    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
-    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
-  }
-
-  public static class NotificationCompat.Action {
-    ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent);
-    method public android.app.PendingIntent getActionIntent();
-    method public boolean getAllowGeneratedReplies();
-    method public android.os.Bundle getExtras();
-    method public int getIcon();
-    method public android.support.v4.app.RemoteInput[] getRemoteInputs();
-    method public java.lang.CharSequence getTitle();
-    field public android.app.PendingIntent actionIntent;
-    field public int icon;
-    field public java.lang.CharSequence title;
-  }
-
-  public static final class NotificationCompat.Action.Builder {
-    ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
-    ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action);
-    method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle);
-    method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput);
-    method public android.support.v4.app.NotificationCompat.Action build();
-    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender);
-    method public android.os.Bundle getExtras();
-    method public android.support.v4.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
-  }
-
-  public static abstract interface NotificationCompat.Action.Extender {
-    method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
-  }
-
-  public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender {
-    ctor public NotificationCompat.Action.WearableExtender();
-    ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action);
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone();
-    method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
-    method public java.lang.CharSequence getCancelLabel();
-    method public java.lang.CharSequence getConfirmLabel();
-    method public boolean getHintDisplayActionInline();
-    method public boolean getHintLaunchesActivity();
-    method public java.lang.CharSequence getInProgressLabel();
-    method public boolean isAvailableOffline();
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
-    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
-  }
-
-  public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style {
-    ctor public NotificationCompat.BigPictureStyle();
-    ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder);
-    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
-    method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap);
-    method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence);
-  }
-
-  public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style {
-    ctor public NotificationCompat.BigTextStyle();
-    ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder);
-    method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence);
-  }
-
-  public static class NotificationCompat.Builder {
-    ctor public NotificationCompat.Builder(android.content.Context);
-    method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
-    method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action);
-    method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle);
-    method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String);
-    method public android.app.Notification build();
-    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender);
-    method public android.os.Bundle getExtras();
-    method public deprecated android.app.Notification getNotification();
-    method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.Builder setColor(int);
-    method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews);
-    method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent);
-    method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews);
-    method public android.support.v4.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews);
-    method public android.support.v4.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
-    method public android.support.v4.app.NotificationCompat.Builder setDefaults(int);
-    method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent);
-    method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle);
-    method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap);
-    method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int);
-    method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setNumber(int);
-    method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setPriority(int);
-    method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification);
-    method public android.support.v4.app.NotificationCompat.Builder setRemoteInputHistory(java.lang.CharSequence[]);
-    method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int);
-    method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int);
-    method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri);
-    method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int);
-    method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style);
-    method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
-    method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean);
-    method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]);
-    method public android.support.v4.app.NotificationCompat.Builder setVisibility(int);
-    method public android.support.v4.app.NotificationCompat.Builder setWhen(long);
-    field public java.util.ArrayList<java.lang.String> mPeople;
-  }
-
-  public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender {
-    ctor public NotificationCompat.CarExtender();
-    ctor public NotificationCompat.CarExtender(android.app.Notification);
-    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
-    method public int getColor();
-    method public android.graphics.Bitmap getLargeIcon();
-    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation();
-    method public android.support.v4.app.NotificationCompat.CarExtender setColor(int);
-    method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap);
-    method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation);
-  }
-
-  public static class NotificationCompat.CarExtender.UnreadConversation {
-    method public long getLatestTimestamp();
-    method public java.lang.String[] getMessages();
-    method public java.lang.String getParticipant();
-    method public java.lang.String[] getParticipants();
-    method public android.app.PendingIntent getReadPendingIntent();
-    method public android.support.v4.app.RemoteInput getRemoteInput();
-    method public android.app.PendingIntent getReplyPendingIntent();
-  }
-
-  public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
-    ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build();
-    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
-    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent);
-    method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput);
-  }
-
-  public static abstract interface NotificationCompat.Extender {
-    method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
-  }
-
-  public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style {
-    ctor public NotificationCompat.InboxStyle();
-    ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder);
-    method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence);
-  }
-
-  public static class NotificationCompat.MessagingStyle extends android.support.v4.app.NotificationCompat.Style {
-    ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
-    method public void addCompatExtras(android.os.Bundle);
-    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
-    method public android.support.v4.app.NotificationCompat.MessagingStyle addMessage(android.support.v4.app.NotificationCompat.MessagingStyle.Message);
-    method public static android.support.v4.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
-    method public java.lang.CharSequence getConversationTitle();
-    method public java.util.List<android.support.v4.app.NotificationCompat.MessagingStyle.Message> getMessages();
-    method public java.lang.CharSequence getUserDisplayName();
-    method public android.support.v4.app.NotificationCompat.MessagingStyle setConversationTitle(java.lang.CharSequence);
-    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
-  }
-
-  public static final class NotificationCompat.MessagingStyle.Message {
-    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
-    method public java.lang.String getDataMimeType();
-    method public android.net.Uri getDataUri();
-    method public java.lang.CharSequence getSender();
-    method public java.lang.CharSequence getText();
-    method public long getTimestamp();
-    method public android.support.v4.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
-  }
-
-  public static abstract class NotificationCompat.Style {
-    ctor public NotificationCompat.Style();
-    method public android.app.Notification build();
-    method public void setBuilder(android.support.v4.app.NotificationCompat.Builder);
-  }
-
-  public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender {
-    ctor public NotificationCompat.WearableExtender();
-    ctor public NotificationCompat.WearableExtender(android.app.Notification);
-    method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action);
-    method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>);
-    method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
-    method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>);
-    method public android.support.v4.app.NotificationCompat.WearableExtender clearActions();
-    method public android.support.v4.app.NotificationCompat.WearableExtender clearPages();
-    method public android.support.v4.app.NotificationCompat.WearableExtender clone();
-    method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder);
-    method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions();
-    method public android.graphics.Bitmap getBackground();
-    method public java.lang.String getBridgeTag();
-    method public int getContentAction();
-    method public int getContentIcon();
-    method public int getContentIconGravity();
-    method public boolean getContentIntentAvailableOffline();
-    method public int getCustomContentHeight();
-    method public int getCustomSizePreset();
-    method public java.lang.String getDismissalId();
-    method public android.app.PendingIntent getDisplayIntent();
-    method public int getGravity();
-    method public boolean getHintAmbientBigPicture();
-    method public boolean getHintAvoidBackgroundClipping();
-    method public boolean getHintContentIntentLaunchesActivity();
-    method public boolean getHintHideIcon();
-    method public int getHintScreenTimeout();
-    method public boolean getHintShowBackgroundOnly();
-    method public java.util.List<android.app.Notification> getPages();
-    method public boolean getStartScrollBottom();
-    method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setBridgeTag(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setDismissalId(java.lang.String);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
-    method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
-    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
-    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
-    field public static final int SIZE_DEFAULT = 0; // 0x0
-    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
-    field public static final int SIZE_LARGE = 4; // 0x4
-    field public static final int SIZE_MEDIUM = 3; // 0x3
-    field public static final int SIZE_SMALL = 2; // 0x2
-    field public static final int SIZE_XSMALL = 1; // 0x1
-    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
-  }
-
-  public final class NotificationCompatExtras {
-    field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
-    field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey";
-    field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
-    field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly";
-    field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
-    field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey";
-  }
-
-  public abstract class NotificationCompatSideChannelService extends android.app.Service {
-    ctor public NotificationCompatSideChannelService();
-    method public abstract void cancel(java.lang.String, int, java.lang.String);
-    method public abstract void cancelAll(java.lang.String);
-    method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification);
-    method public android.os.IBinder onBind(android.content.Intent);
-  }
-
-  public final class NotificationManagerCompat {
-    method public boolean areNotificationsEnabled();
-    method public void cancel(int);
-    method public void cancel(java.lang.String, int);
-    method public void cancelAll();
-    method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context);
-    method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context);
-    method public int getImportance();
-    method public void notify(int, android.app.Notification);
-    method public void notify(java.lang.String, int, android.app.Notification);
-    field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
-    field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
-    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
-    field public static final int IMPORTANCE_HIGH = 4; // 0x4
-    field public static final int IMPORTANCE_LOW = 2; // 0x2
-    field public static final int IMPORTANCE_MAX = 5; // 0x5
-    field public static final int IMPORTANCE_MIN = 1; // 0x1
-    field public static final int IMPORTANCE_NONE = 0; // 0x0
-    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
-  }
-
-  public final class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput {
-    method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle);
-    method public boolean getAllowFreeFormInput();
-    method public java.lang.CharSequence[] getChoices();
-    method public android.os.Bundle getExtras();
-    method public java.lang.CharSequence getLabel();
-    method public java.lang.String getResultKey();
-    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
-    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
-    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
-  }
-
-  public static final class RemoteInput.Builder {
-    ctor public RemoteInput.Builder(java.lang.String);
-    method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle);
-    method public android.support.v4.app.RemoteInput build();
-    method public android.os.Bundle getExtras();
-    method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
-    method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
-    method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
-  }
-
-   class RemoteInputCompatBase {
-  }
-
-  public static abstract class RemoteInputCompatBase.RemoteInput {
-    ctor public RemoteInputCompatBase.RemoteInput();
-    method protected abstract boolean getAllowFreeFormInput();
-    method protected abstract java.lang.CharSequence[] getChoices();
-    method protected abstract android.os.Bundle getExtras();
-    method protected abstract java.lang.CharSequence getLabel();
-    method protected abstract java.lang.String getResultKey();
-  }
-
-  public final class ServiceCompat {
-    method public static void stopForeground(android.app.Service, int);
-    field public static final int START_STICKY = 1; // 0x1
-    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
-    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
-  }
-
-  public final class ShareCompat {
-    method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder);
-    method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder);
-    method public static android.content.ComponentName getCallingActivity(android.app.Activity);
-    method public static java.lang.String getCallingPackage(android.app.Activity);
-    field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
-    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
-  }
-
-  public static class ShareCompat.IntentBuilder {
-    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String);
-    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]);
-    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String);
-    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]);
-    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String);
-    method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]);
-    method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
-    method public android.content.Intent createChooserIntent();
-    method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity);
-    method public android.content.Intent getIntent();
-    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence);
-    method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String);
-    method public void startChooser();
-  }
-
-  public static class ShareCompat.IntentReader {
-    method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity);
-    method public android.content.ComponentName getCallingActivity();
-    method public android.graphics.drawable.Drawable getCallingActivityIcon();
-    method public android.graphics.drawable.Drawable getCallingApplicationIcon();
-    method public java.lang.CharSequence getCallingApplicationLabel();
-    method public java.lang.String getCallingPackage();
-    method public java.lang.String[] getEmailBcc();
-    method public java.lang.String[] getEmailCc();
-    method public java.lang.String[] getEmailTo();
-    method public java.lang.String getHtmlText();
-    method public android.net.Uri getStream();
-    method public android.net.Uri getStream(int);
-    method public int getStreamCount();
-    method public java.lang.String getSubject();
-    method public java.lang.CharSequence getText();
-    method public java.lang.String getType();
-    method public boolean isMultipleShare();
-    method public boolean isShareIntent();
-    method public boolean isSingleShare();
-  }
-
-  public abstract class SharedElementCallback {
-    ctor public SharedElementCallback();
-    method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
-    method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable);
-    method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
-    method public void onRejectSharedElements(java.util.List<android.view.View>);
-    method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
-    method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
-    method public void onSharedElementsArrived(java.util.List<java.lang.String>, java.util.List<android.view.View>, android.support.v4.app.SharedElementCallback.OnSharedElementsReadyListener);
-  }
-
-  public static abstract interface SharedElementCallback.OnSharedElementsReadyListener {
-    method public abstract void onSharedElementsReady();
-  }
-
-  public final class TaskStackBuilder implements java.lang.Iterable {
-    method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent);
-    method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
-    method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity);
-    method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
-    method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName);
-    method public static android.support.v4.app.TaskStackBuilder create(android.content.Context);
-    method public android.content.Intent editIntentAt(int);
-    method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context);
-    method public deprecated android.content.Intent getIntent(int);
-    method public int getIntentCount();
-    method public android.content.Intent[] getIntents();
-    method public android.app.PendingIntent getPendingIntent(int, int);
-    method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle);
-    method public deprecated java.util.Iterator<android.content.Intent> iterator();
-    method public void startActivities();
-    method public void startActivities(android.os.Bundle);
-  }
-
-  public static abstract interface TaskStackBuilder.SupportParentable {
-    method public abstract android.content.Intent getSupportParentActivityIntent();
-  }
-
-}
-
-package android.support.v4.content {
-
-  public abstract class AsyncTaskLoader<D> extends android.support.v4.content.Loader {
-    ctor public AsyncTaskLoader(android.content.Context);
-    method public void cancelLoadInBackground();
-    method public boolean isLoadInBackgroundCanceled();
-    method public abstract D loadInBackground();
-    method public void onCanceled(D);
-    method protected D onLoadInBackground();
-    method public void setUpdateThrottle(long);
-  }
-
-  public final class ContentResolverCompat {
-    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal);
-  }
-
-  public class ContextCompat {
-    ctor protected ContextCompat();
-    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 java.io.File getDataDir(android.content.Context);
-    method public static final 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[] getObbDirs(android.content.Context);
-    method public static boolean isDeviceProtectedStorage(android.content.Context);
-    method public static boolean startActivities(android.content.Context, android.content.Intent[]);
-    method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle);
-    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
-  }
-
-  public class CursorLoader extends android.support.v4.content.AsyncTaskLoader {
-    ctor public CursorLoader(android.content.Context);
-    ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public void deliverResult(android.database.Cursor);
-    method public java.lang.String[] getProjection();
-    method public java.lang.String getSelection();
-    method public java.lang.String[] getSelectionArgs();
-    method public java.lang.String getSortOrder();
-    method public android.net.Uri getUri();
-    method public android.database.Cursor loadInBackground();
-    method public void onCanceled(android.database.Cursor);
-    method public void setProjection(java.lang.String[]);
-    method public void setSelection(java.lang.String);
-    method public void setSelectionArgs(java.lang.String[]);
-    method public void setSortOrder(java.lang.String);
-    method public void setUri(android.net.Uri);
-  }
-
-  public class FileProvider extends android.content.ContentProvider {
-    ctor public FileProvider();
-    method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
-    method public java.lang.String getType(android.net.Uri);
-    method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File);
-    method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
-    method public boolean onCreate();
-    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
-  }
-
-  public final class IntentCompat {
-    method public static android.content.Intent makeMainActivity(android.content.ComponentName);
-    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
-    method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName);
-    field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
-    field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
-    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
-    field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
-    field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
-    field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
-    field public static final java.lang.String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
-    field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
-    field public static final int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
-  }
-
-  public class Loader<D> {
-    ctor public Loader(android.content.Context);
-    method public void abandon();
-    method public boolean cancelLoad();
-    method public void commitContentChanged();
-    method public java.lang.String dataToString(D);
-    method public void deliverCancellation();
-    method public void deliverResult(D);
-    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public void forceLoad();
-    method public android.content.Context getContext();
-    method public int getId();
-    method public boolean isAbandoned();
-    method public boolean isReset();
-    method public boolean isStarted();
-    method protected void onAbandon();
-    method protected boolean onCancelLoad();
-    method public void onContentChanged();
-    method protected void onForceLoad();
-    method protected void onReset();
-    method protected void onStartLoading();
-    method protected void onStopLoading();
-    method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>);
-    method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
-    method public void reset();
-    method public void rollbackContentChanged();
-    method public final void startLoading();
-    method public void stopLoading();
-    method public boolean takeContentChanged();
-    method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>);
-    method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>);
-  }
-
-  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
-    ctor public Loader.ForceLoadContentObserver();
-  }
-
-  public static abstract interface Loader.OnLoadCanceledListener<D> {
-    method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>);
-  }
-
-  public static abstract interface Loader.OnLoadCompleteListener<D> {
-    method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D);
-  }
-
-  public final class LocalBroadcastManager {
-    method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context);
-    method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
-    method public boolean sendBroadcast(android.content.Intent);
-    method public void sendBroadcastSync(android.content.Intent);
-    method public void unregisterReceiver(android.content.BroadcastReceiver);
-  }
-
-  public final class ParallelExecutorCompat {
-    method public static java.util.concurrent.Executor getParallelExecutor();
-  }
-
-  public final class PermissionChecker {
-    method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String);
-    method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String);
-    method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String);
-    method public static int checkSelfPermission(android.content.Context, java.lang.String);
-    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
-    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
-    field public static final int PERMISSION_GRANTED = 0; // 0x0
-  }
-
-  public static abstract class PermissionChecker.PermissionResult implements java.lang.annotation.Annotation {
-  }
-
-  public final class SharedPreferencesCompat {
-  }
-
-  public static final class SharedPreferencesCompat.EditorCompat {
-    method public void apply(android.content.SharedPreferences.Editor);
-    method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance();
-  }
-
-  public abstract class WakefulBroadcastReceiver extends android.content.BroadcastReceiver {
-    ctor public WakefulBroadcastReceiver();
-    method public static boolean completeWakefulIntent(android.content.Intent);
-    method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent);
-  }
-
-}
-
-package android.support.v4.content.pm {
-
-  public final class ActivityInfoCompat {
-    field public static final int CONFIG_UI_MODE = 512; // 0x200
-  }
-
-}
-
-package android.support.v4.content.res {
-
-  public final class ConfigurationHelper {
-    method public static int getDensityDpi(android.content.res.Resources);
-    method public static int getScreenHeightDp(android.content.res.Resources);
-    method public static int getScreenWidthDp(android.content.res.Resources);
-    method public static int getSmallestScreenWidthDp(android.content.res.Resources);
-  }
-
-  public final class ResourcesCompat {
-    method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
-    method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
-    method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
-    method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
-  }
-
-}
-
-package android.support.v4.database {
-
-  public final class DatabaseUtilsCompat {
-    method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]);
-    method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String);
-  }
-
-}
-
-package android.support.v4.graphics {
-
-  public final class BitmapCompat {
-    method public static int getAllocationByteCount(android.graphics.Bitmap);
-    method public static boolean hasMipMap(android.graphics.Bitmap);
-    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
-  }
-
-  public final class ColorUtils {
-    method public static int HSLToColor(float[]);
-    method public static int LABToColor(double, double, double);
-    method public static void LABToXYZ(double, double, double, double[]);
-    method public static void RGBToHSL(int, int, int, float[]);
-    method public static void RGBToLAB(int, int, int, double[]);
-    method public static void RGBToXYZ(int, int, int, double[]);
-    method public static int XYZToColor(double, double, double);
-    method public static void XYZToLAB(double, double, double, double[]);
-    method public static int blendARGB(int, int, float);
-    method public static void blendHSL(float[], float[], float, float[]);
-    method public static void blendLAB(double[], double[], double, double[]);
-    method public static double calculateContrast(int, int);
-    method public static double calculateLuminance(int);
-    method public static int calculateMinimumAlpha(int, int, float);
-    method public static void colorToHSL(int, float[]);
-    method public static void colorToLAB(int, double[]);
-    method public static void colorToXYZ(int, double[]);
-    method public static int compositeColors(int, int);
-    method public static double distanceEuclidean(double[], double[]);
-    method public static int setAlphaComponent(int, int);
-  }
-
-  public final class PaintCompat {
-    method public static boolean hasGlyph(android.graphics.Paint, java.lang.String);
-  }
-
-}
-
-package android.support.v4.graphics.drawable {
-
-  public final class DrawableCompat {
-    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
-    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
-    method public static void clearColorFilter(android.graphics.drawable.Drawable);
-    method public static int getAlpha(android.graphics.drawable.Drawable);
-    method public static android.graphics.ColorFilter getColorFilter(android.graphics.drawable.Drawable);
-    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
-    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
-    method public static void jumpToCurrentState(android.graphics.drawable.Drawable);
-    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
-    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
-    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
-    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
-    method public static void setTint(android.graphics.drawable.Drawable, int);
-    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList);
-    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
-    method public static <T extends android.graphics.drawable.Drawable> T unwrap(android.graphics.drawable.Drawable);
-    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
-  }
-
-  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
-    method public void draw(android.graphics.Canvas);
-    method public final android.graphics.Bitmap getBitmap();
-    method public float getCornerRadius();
-    method public int getGravity();
-    method public int getOpacity();
-    method public final android.graphics.Paint getPaint();
-    method public boolean hasAntiAlias();
-    method public boolean hasMipMap();
-    method public boolean isCircular();
-    method public void setAlpha(int);
-    method public void setAntiAlias(boolean);
-    method public void setCircular(boolean);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void setCornerRadius(float);
-    method public void setGravity(int);
-    method public void setMipMap(boolean);
-    method public void setTargetDensity(android.graphics.Canvas);
-    method public void setTargetDensity(android.util.DisplayMetrics);
-    method public void setTargetDensity(int);
-  }
-
-  public final class RoundedBitmapDrawableFactory {
-    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap);
-    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String);
-    method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
-  }
-
-}
-
-package android.support.v4.hardware.display {
-
-  public abstract class DisplayManagerCompat {
-    method public abstract android.view.Display getDisplay(int);
-    method public abstract android.view.Display[] getDisplays();
-    method public abstract android.view.Display[] getDisplays(java.lang.String);
-    method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
-    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
-  }
-
-}
-
-package android.support.v4.hardware.fingerprint {
-
-  public final class FingerprintManagerCompat {
-    method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler);
-    method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
-    method public boolean hasEnrolledFingerprints();
-    method public boolean isHardwareDetected();
-  }
-
-  public static abstract class FingerprintManagerCompat.AuthenticationCallback {
-    ctor public FingerprintManagerCompat.AuthenticationCallback();
-    method public void onAuthenticationError(int, java.lang.CharSequence);
-    method public void onAuthenticationFailed();
-    method public void onAuthenticationHelp(int, java.lang.CharSequence);
-    method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult);
-  }
-
-  public static final class FingerprintManagerCompat.AuthenticationResult {
-    ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject);
-    method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject();
-  }
-
-  public static class FingerprintManagerCompat.CryptoObject {
-    ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature);
-    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
-    ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
-    method public javax.crypto.Cipher getCipher();
-    method public javax.crypto.Mac getMac();
-    method public java.security.Signature getSignature();
-  }
-
-}
-
-package android.support.v4.media {
-
-  public final class MediaBrowserCompat {
-    ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle);
-    method public void connect();
-    method public void disconnect();
-    method public android.os.Bundle getExtras();
-    method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
-    method public java.lang.String getRoot();
-    method public android.content.ComponentName getServiceComponent();
-    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
-    method public boolean isConnected();
-    method public void search(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SearchCallback);
-    method public void sendCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.CustomActionCallback);
-    method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
-    method public void subscribe(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
-    method public void unsubscribe(java.lang.String);
-    method public void unsubscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
-    field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
-    field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
-  }
-
-  public static class MediaBrowserCompat.ConnectionCallback {
-    ctor public MediaBrowserCompat.ConnectionCallback();
-    method public void onConnected();
-    method public void onConnectionFailed();
-    method public void onConnectionSuspended();
-  }
-
-  public static abstract class MediaBrowserCompat.CustomActionCallback {
-    ctor public MediaBrowserCompat.CustomActionCallback();
-    method public void onError(java.lang.String, android.os.Bundle, android.os.Bundle);
-    method public void onProgressUpdate(java.lang.String, android.os.Bundle, android.os.Bundle);
-    method public void onResult(java.lang.String, android.os.Bundle, android.os.Bundle);
-  }
-
-  public static abstract class MediaBrowserCompat.ItemCallback {
-    ctor public MediaBrowserCompat.ItemCallback();
-    method public void onError(java.lang.String);
-    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem);
-  }
-
-  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
-    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
-    method public int describeContents();
-    method public static android.support.v4.media.MediaBrowserCompat.MediaItem fromMediaItem(java.lang.Object);
-    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem> fromMediaItemList(java.util.List<?>);
-    method public android.support.v4.media.MediaDescriptionCompat getDescription();
-    method public int getFlags();
-    method public java.lang.String getMediaId();
-    method public boolean isBrowsable();
-    method public boolean isPlayable();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR;
-    field public static final int FLAG_BROWSABLE = 1; // 0x1
-    field public static final int FLAG_PLAYABLE = 2; // 0x2
-  }
-
-  public static abstract class MediaBrowserCompat.SearchCallback {
-    ctor public MediaBrowserCompat.SearchCallback();
-    method public void onError(java.lang.String, android.os.Bundle);
-    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
-  }
-
-  public static abstract class MediaBrowserCompat.SubscriptionCallback {
-    ctor public MediaBrowserCompat.SubscriptionCallback();
-    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>);
-    method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>, android.os.Bundle);
-    method public void onError(java.lang.String);
-    method public void onError(java.lang.String, android.os.Bundle);
-  }
-
-  public abstract class MediaBrowserServiceCompat extends android.app.Service {
-    ctor public MediaBrowserServiceCompat();
-    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public final android.os.Bundle getBrowserRootHints();
-    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
-    method public void notifyChildrenChanged(java.lang.String);
-    method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public void onCustomAction(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<android.os.Bundle>);
-    method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
-    method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
-    method public void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>, android.os.Bundle);
-    method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>);
-    method public void onSearch(java.lang.String, android.os.Bundle, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>);
-    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
-  }
-
-  public static final class MediaBrowserServiceCompat.BrowserRoot {
-    ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle);
-    method public android.os.Bundle getExtras();
-    method public java.lang.String getRootId();
-    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
-    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
-    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final deprecated java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
-  }
-
-  public static class MediaBrowserServiceCompat.Result<T> {
-    method public void detach();
-    method public void sendError(android.os.Bundle);
-    method public void sendProgressUpdate(android.os.Bundle);
-    method public void sendResult(T);
-  }
-
-  public final class MediaDescriptionCompat implements android.os.Parcelable {
-    method public int describeContents();
-    method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object);
-    method public java.lang.CharSequence getDescription();
-    method public android.os.Bundle getExtras();
-    method public android.graphics.Bitmap getIconBitmap();
-    method public android.net.Uri getIconUri();
-    method public java.lang.Object getMediaDescription();
-    method public java.lang.String getMediaId();
-    method public android.net.Uri getMediaUri();
-    method public java.lang.CharSequence getSubtitle();
-    method public java.lang.CharSequence getTitle();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
-    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
-    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
-    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
-    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
-    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
-    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR;
-    field public static final java.lang.String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
-  }
-
-  public static final class MediaDescriptionCompat.Builder {
-    ctor public MediaDescriptionCompat.Builder();
-    method public android.support.v4.media.MediaDescriptionCompat build();
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence);
-    method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence);
-  }
-
-  public final class MediaMetadataCompat implements android.os.Parcelable {
-    method public boolean containsKey(java.lang.String);
-    method public int describeContents();
-    method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object);
-    method public android.graphics.Bitmap getBitmap(java.lang.String);
-    method public android.os.Bundle getBundle();
-    method public android.support.v4.media.MediaDescriptionCompat getDescription();
-    method public long getLong(java.lang.String);
-    method public java.lang.Object getMediaMetadata();
-    method public android.support.v4.media.RatingCompat getRating(java.lang.String);
-    method public java.lang.String getString(java.lang.String);
-    method public java.lang.CharSequence getText(java.lang.String);
-    method public java.util.Set<java.lang.String> keySet();
-    method public int size();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR;
-    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
-    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
-    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
-    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
-    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
-    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
-    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
-    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
-    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
-    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
-    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
-    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
-    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
-    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
-    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
-    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
-    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
-    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
-    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
-    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
-    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
-    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
-    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
-  }
-
-  public static final class MediaMetadataCompat.Builder {
-    ctor public MediaMetadataCompat.Builder();
-    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat);
-    method public android.support.v4.media.MediaMetadataCompat build();
-    method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
-    method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long);
-    method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat);
-    method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String);
-    method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence);
-  }
-
-  public final class RatingCompat implements android.os.Parcelable {
-    method public int describeContents();
-    method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object);
-    method public float getPercentRating();
-    method public java.lang.Object getRating();
-    method public int getRatingStyle();
-    method public float getStarRating();
-    method public boolean hasHeart();
-    method public boolean isRated();
-    method public boolean isThumbUp();
-    method public static android.support.v4.media.RatingCompat newHeartRating(boolean);
-    method public static android.support.v4.media.RatingCompat newPercentageRating(float);
-    method public static android.support.v4.media.RatingCompat newStarRating(int, float);
-    method public static android.support.v4.media.RatingCompat newThumbRating(boolean);
-    method public static android.support.v4.media.RatingCompat newUnratedRating(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR;
-    field public static final int RATING_3_STARS = 3; // 0x3
-    field public static final int RATING_4_STARS = 4; // 0x4
-    field public static final int RATING_5_STARS = 5; // 0x5
-    field public static final int RATING_HEART = 1; // 0x1
-    field public static final int RATING_NONE = 0; // 0x0
-    field public static final int RATING_PERCENTAGE = 6; // 0x6
-    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
-  }
-
-  public abstract deprecated class TransportController {
-    ctor public deprecated TransportController();
-    method public abstract deprecated int getBufferPercentage();
-    method public abstract deprecated long getCurrentPosition();
-    method public abstract deprecated long getDuration();
-    method public abstract deprecated int getTransportControlFlags();
-    method public abstract deprecated boolean isPlaying();
-    method public abstract deprecated void pausePlaying();
-    method public abstract deprecated void registerStateListener(android.support.v4.media.TransportStateListener);
-    method public abstract deprecated void seekTo(long);
-    method public abstract deprecated void startPlaying();
-    method public abstract deprecated void stopPlaying();
-    method public abstract deprecated void unregisterStateListener(android.support.v4.media.TransportStateListener);
-  }
-
-  public deprecated class TransportMediator extends android.support.v4.media.TransportController {
-    ctor public deprecated TransportMediator(android.app.Activity, android.support.v4.media.TransportPerformer);
-    ctor public deprecated TransportMediator(android.view.View, android.support.v4.media.TransportPerformer);
-    method public deprecated void destroy();
-    method public deprecated boolean dispatchKeyEvent(android.view.KeyEvent);
-    method public deprecated int getBufferPercentage();
-    method public deprecated long getCurrentPosition();
-    method public deprecated long getDuration();
-    method public deprecated java.lang.Object getRemoteControlClient();
-    method public deprecated int getTransportControlFlags();
-    method public deprecated boolean isPlaying();
-    method public deprecated void pausePlaying();
-    method public deprecated void refreshState();
-    method public deprecated void registerStateListener(android.support.v4.media.TransportStateListener);
-    method public deprecated void seekTo(long);
-    method public deprecated void startPlaying();
-    method public deprecated void stopPlaying();
-    method public deprecated void unregisterStateListener(android.support.v4.media.TransportStateListener);
-    field public static final deprecated int FLAG_KEY_MEDIA_FAST_FORWARD = 64; // 0x40
-    field public static final deprecated int FLAG_KEY_MEDIA_NEXT = 128; // 0x80
-    field public static final deprecated int FLAG_KEY_MEDIA_PAUSE = 16; // 0x10
-    field public static final deprecated int FLAG_KEY_MEDIA_PLAY = 4; // 0x4
-    field public static final deprecated int FLAG_KEY_MEDIA_PLAY_PAUSE = 8; // 0x8
-    field public static final deprecated int FLAG_KEY_MEDIA_PREVIOUS = 1; // 0x1
-    field public static final deprecated int FLAG_KEY_MEDIA_REWIND = 2; // 0x2
-    field public static final deprecated int FLAG_KEY_MEDIA_STOP = 32; // 0x20
-    field public static final deprecated int KEYCODE_MEDIA_PAUSE = 127; // 0x7f
-    field public static final deprecated int KEYCODE_MEDIA_PLAY = 126; // 0x7e
-    field public static final deprecated int KEYCODE_MEDIA_RECORD = 130; // 0x82
-  }
-
-  public abstract deprecated class TransportPerformer {
-    ctor public deprecated TransportPerformer();
-    method public deprecated void onAudioFocusChange(int);
-    method public deprecated int onGetBufferPercentage();
-    method public abstract deprecated long onGetCurrentPosition();
-    method public abstract deprecated long onGetDuration();
-    method public deprecated int onGetTransportControlFlags();
-    method public abstract deprecated boolean onIsPlaying();
-    method public deprecated boolean onMediaButtonDown(int, android.view.KeyEvent);
-    method public deprecated boolean onMediaButtonUp(int, android.view.KeyEvent);
-    method public abstract deprecated void onPause();
-    method public abstract deprecated void onSeekTo(long);
-    method public abstract deprecated void onStart();
-    method public abstract deprecated void onStop();
-  }
-
-  public deprecated class TransportStateListener {
-    ctor public deprecated TransportStateListener();
-    method public deprecated void onPlayingChanged(android.support.v4.media.TransportController);
-    method public deprecated void onTransportControlsChanged(android.support.v4.media.TransportController);
-  }
-
-  public abstract class VolumeProviderCompat {
-    ctor public VolumeProviderCompat(int, int, int);
-    method public final int getCurrentVolume();
-    method public final int getMaxVolume();
-    method public final int getVolumeControl();
-    method public java.lang.Object getVolumeProvider();
-    method public void onAdjustVolume(int);
-    method public void onSetVolumeTo(int);
-    method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback);
-    method public final void setCurrentVolume(int);
-    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
-    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
-    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
-  }
-
-  public static abstract class VolumeProviderCompat.Callback {
-    ctor public VolumeProviderCompat.Callback();
-    method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat);
-  }
-
-}
-
-package android.support.v4.media.session {
-
-  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
-    ctor public MediaButtonReceiver();
-    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, long);
-    method public static android.app.PendingIntent buildMediaButtonPendingIntent(android.content.Context, android.content.ComponentName, long);
-    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
-    method public void onReceive(android.content.Context, android.content.Intent);
-  }
-
-  public final class MediaControllerCompat {
-    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
-    ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
-    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat);
-    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
-    method public void adjustVolume(int, int);
-    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
-    method public android.os.Bundle getExtras();
-    method public long getFlags();
-    method public static android.support.v4.media.session.MediaControllerCompat getMediaController(android.app.Activity);
-    method public java.lang.Object getMediaController();
-    method public android.support.v4.media.MediaMetadataCompat getMetadata();
-    method public java.lang.String getPackageName();
-    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo();
-    method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState();
-    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
-    method public java.lang.CharSequence getQueueTitle();
-    method public int getRatingType();
-    method public int getRepeatMode();
-    method public android.app.PendingIntent getSessionActivity();
-    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
-    method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
-    method public boolean isCaptioningEnabled();
-    method public boolean isShuffleModeEnabled();
-    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
-    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
-    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat);
-    method public void removeQueueItemAt(int);
-    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
-    method public void setVolumeTo(int, int);
-    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
-  }
-
-  public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
-    ctor public MediaControllerCompat.Callback();
-    method public void binderDied();
-    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo);
-    method public void onCaptioningEnabledChanged(boolean);
-    method public void onExtrasChanged(android.os.Bundle);
-    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat);
-    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
-    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
-    method public void onQueueTitleChanged(java.lang.CharSequence);
-    method public void onRepeatModeChanged(int);
-    method public void onSessionDestroyed();
-    method public void onSessionEvent(java.lang.String, android.os.Bundle);
-    method public void onShuffleModeChanged(boolean);
-  }
-
-  public static final class MediaControllerCompat.PlaybackInfo {
-    method public int getAudioStream();
-    method public int getCurrentVolume();
-    method public int getMaxVolume();
-    method public int getPlaybackType();
-    method public int getVolumeControl();
-    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
-    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
-  }
-
-  public static abstract class MediaControllerCompat.TransportControls {
-    method public abstract void fastForward();
-    method public abstract void pause();
-    method public abstract void play();
-    method public abstract void playFromMediaId(java.lang.String, android.os.Bundle);
-    method public abstract void playFromSearch(java.lang.String, android.os.Bundle);
-    method public abstract void playFromUri(android.net.Uri, android.os.Bundle);
-    method public abstract void prepare();
-    method public abstract void prepareFromMediaId(java.lang.String, android.os.Bundle);
-    method public abstract void prepareFromSearch(java.lang.String, android.os.Bundle);
-    method public abstract void prepareFromUri(android.net.Uri, android.os.Bundle);
-    method public abstract void rewind();
-    method public abstract void seekTo(long);
-    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
-    method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
-    method public abstract void setCaptioningEnabled(boolean);
-    method public abstract void setRating(android.support.v4.media.RatingCompat);
-    method public abstract void setRepeatMode(int);
-    method public abstract void setShuffleModeEnabled(boolean);
-    method public abstract void skipToNext();
-    method public abstract void skipToPrevious();
-    method public abstract void skipToQueueItem(long);
-    method public abstract void stop();
-  }
-
-  public class MediaSessionCompat {
-    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
-    ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
-    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
-    method public static android.support.v4.media.session.MediaSessionCompat fromMediaSession(android.content.Context, java.lang.Object);
-    method public android.support.v4.media.session.MediaControllerCompat getController();
-    method public java.lang.Object getMediaSession();
-    method public java.lang.Object getRemoteControlClient();
-    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
-    method public boolean isActive();
-    method public static deprecated android.support.v4.media.session.MediaSessionCompat obtain(android.content.Context, java.lang.Object);
-    method public void release();
-    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
-    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
-    method public void setActive(boolean);
-    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback);
-    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler);
-    method public void setCaptioningEnabled(boolean);
-    method public void setExtras(android.os.Bundle);
-    method public void setFlags(int);
-    method public void setMediaButtonReceiver(android.app.PendingIntent);
-    method public void setMetadata(android.support.v4.media.MediaMetadataCompat);
-    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat);
-    method public void setPlaybackToLocal(int);
-    method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat);
-    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
-    method public void setQueueTitle(java.lang.CharSequence);
-    method public void setRatingType(int);
-    method public void setRepeatMode(int);
-    method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleModeEnabled(boolean);
-    field public static final java.lang.String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
-    field public static final java.lang.String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
-    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
-    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
-    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
-  }
-
-  public static abstract class MediaSessionCompat.Callback {
-    ctor public MediaSessionCompat.Callback();
-    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat);
-    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
-    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    method public void onCustomAction(java.lang.String, android.os.Bundle);
-    method public void onFastForward();
-    method public boolean onMediaButtonEvent(android.content.Intent);
-    method public void onPause();
-    method public void onPlay();
-    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
-    method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
-    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
-    method public void onPrepare();
-    method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
-    method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
-    method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
-    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat);
-    method public void onRemoveQueueItemAt(int);
-    method public void onRewind();
-    method public void onSeekTo(long);
-    method public void onSetCaptioningEnabled(boolean);
-    method public void onSetRating(android.support.v4.media.RatingCompat);
-    method public void onSetRepeatMode(int);
-    method public void onSetShuffleModeEnabled(boolean);
-    method public void onSkipToNext();
-    method public void onSkipToPrevious();
-    method public void onSkipToQueueItem(long);
-    method public void onStop();
-  }
-
-  public static abstract interface MediaSessionCompat.OnActiveChangeListener {
-    method public abstract void onActiveChanged();
-  }
-
-  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
-    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long);
-    method public int describeContents();
-    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem fromQueueItem(java.lang.Object);
-    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> fromQueueItemList(java.util.List<?>);
-    method public android.support.v4.media.MediaDescriptionCompat getDescription();
-    method public long getQueueId();
-    method public java.lang.Object getQueueItem();
-    method public static deprecated android.support.v4.media.session.MediaSessionCompat.QueueItem obtain(java.lang.Object);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
-    field public static final int UNKNOWN_ID = -1; // 0xffffffff
-  }
-
-  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
-    method public int describeContents();
-    method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object);
-    method public java.lang.Object getToken();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR;
-  }
-
-  public class ParcelableVolumeInfo implements android.os.Parcelable {
-    ctor public ParcelableVolumeInfo(int, int, int, int, int);
-    ctor public ParcelableVolumeInfo(android.os.Parcel);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR;
-    field public int audioStream;
-    field public int controlType;
-    field public int currentVolume;
-    field public int maxVolume;
-    field public int volumeType;
-  }
-
-  public final class PlaybackStateCompat implements android.os.Parcelable {
-    method public int describeContents();
-    method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object);
-    method public long getActions();
-    method public long getActiveQueueItemId();
-    method public long getBufferedPosition();
-    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions();
-    method public int getErrorCode();
-    method public java.lang.CharSequence getErrorMessage();
-    method public android.os.Bundle getExtras();
-    method public long getLastPositionUpdateTime();
-    method public float getPlaybackSpeed();
-    method public java.lang.Object getPlaybackState();
-    method public long getPosition();
-    method public int getState();
-    method public static int toKeyCode(long);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
-    field public static final long ACTION_PAUSE = 2L; // 0x2L
-    field public static final long ACTION_PLAY = 4L; // 0x4L
-    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
-    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
-    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
-    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
-    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
-    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
-    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
-    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
-    field public static final long ACTION_REWIND = 8L; // 0x8L
-    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
-    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
-    field public static final long ACTION_SET_RATING = 128L; // 0x80L
-    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
-    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
-    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
-    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
-    field public static final long ACTION_STOP = 1L; // 0x1L
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
-    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
-    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
-    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
-    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
-    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
-    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
-    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
-    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
-    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
-    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
-    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
-    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
-    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
-    field public static final int REPEAT_MODE_ALL = 2; // 0x2
-    field public static final int REPEAT_MODE_NONE = 0; // 0x0
-    field public static final int REPEAT_MODE_ONE = 1; // 0x1
-    field public static final int STATE_BUFFERING = 6; // 0x6
-    field public static final int STATE_CONNECTING = 8; // 0x8
-    field public static final int STATE_ERROR = 7; // 0x7
-    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
-    field public static final int STATE_NONE = 0; // 0x0
-    field public static final int STATE_PAUSED = 2; // 0x2
-    field public static final int STATE_PLAYING = 3; // 0x3
-    field public static final int STATE_REWINDING = 5; // 0x5
-    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
-    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
-    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
-    field public static final int STATE_STOPPED = 1; // 0x1
-  }
-
-  public static final class PlaybackStateCompat.Builder {
-    ctor public PlaybackStateCompat.Builder();
-    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction);
-    method public android.support.v4.media.session.PlaybackStateCompat build();
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long);
-    method public deprecated android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(int, java.lang.CharSequence);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float);
-    method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long);
-  }
-
-  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
-    method public int describeContents();
-    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object);
-    method public java.lang.String getAction();
-    method public java.lang.Object getCustomAction();
-    method public android.os.Bundle getExtras();
-    method public int getIcon();
-    method public java.lang.CharSequence getName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR;
-  }
-
-  public static final class PlaybackStateCompat.CustomAction.Builder {
-    ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
-    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build();
-    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle);
-  }
-
-}
-
-package android.support.v4.net {
-
-  public final class ConnectivityManagerCompat {
-    method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
-    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
-    method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
-    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
-    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
-    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
-  }
-
-  public final class TrafficStatsCompat {
-    method public static void clearThreadStatsTag();
-    method public static int getThreadStatsTag();
-    method public static void incrementOperationCount(int);
-    method public static void incrementOperationCount(int, int);
-    method public static void setThreadStatsTag(int);
-    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
-    method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
-    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
-    method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
-  }
-
-}
-
-package android.support.v4.os {
-
-  public final class AsyncTaskCompat {
-    method public static <Params, Progress, Result> android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
-  }
-
-  public class BuildCompat {
-    method public static boolean isAtLeastN();
-    method public static boolean isAtLeastNMR1();
-    method public static boolean isAtLeastO();
-  }
-
-  public final class CancellationSignal {
-    ctor public CancellationSignal();
-    method public void cancel();
-    method public java.lang.Object getCancellationSignalObject();
-    method public boolean isCanceled();
-    method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener);
-    method public void throwIfCanceled();
-  }
-
-  public static abstract interface CancellationSignal.OnCancelListener {
-    method public abstract void onCancel();
-  }
-
-  public final class EnvironmentCompat {
-    method public static java.lang.String getStorageState(java.io.File);
-    field public static final java.lang.String MEDIA_UNKNOWN = "unknown";
-  }
-
-  public class OperationCanceledException extends java.lang.RuntimeException {
-    ctor public OperationCanceledException();
-    ctor public OperationCanceledException(java.lang.String);
-  }
-
-  public final class ParcelableCompat {
-    method public static <T> android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>);
-  }
-
-  public abstract interface ParcelableCompatCreatorCallbacks<T> {
-    method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader);
-    method public abstract T[] newArray(int);
-  }
-
-  public final class TraceCompat {
-    method public static void beginSection(java.lang.String);
-    method public static void endSection();
-  }
-
-  public class UserManagerCompat {
-    method public static boolean isUserUnlocked(android.content.Context);
-  }
-
-}
-
-package android.support.v4.print {
-
-  public final class PrintHelper {
-    ctor public PrintHelper(android.content.Context);
-    method public int getColorMode();
-    method public int getOrientation();
-    method public int getScaleMode();
-    method public void printBitmap(java.lang.String, android.graphics.Bitmap);
-    method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback);
-    method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException;
-    method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException;
-    method public void setColorMode(int);
-    method public void setOrientation(int);
-    method public void setScaleMode(int);
-    method public static boolean systemSupportsPrint();
-    field public static final int COLOR_MODE_COLOR = 2; // 0x2
-    field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
-    field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
-    field public static final int ORIENTATION_PORTRAIT = 2; // 0x2
-    field public static final int SCALE_MODE_FILL = 2; // 0x2
-    field public static final int SCALE_MODE_FIT = 1; // 0x1
-  }
-
-  public static abstract interface PrintHelper.OnPrintFinishCallback {
-    method public abstract void onFinish();
-  }
-
-}
-
-package android.support.v4.provider {
-
-  public abstract class DocumentFile {
-    method public abstract boolean canRead();
-    method public abstract boolean canWrite();
-    method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String);
-    method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String);
-    method public abstract boolean delete();
-    method public abstract boolean exists();
-    method public android.support.v4.provider.DocumentFile findFile(java.lang.String);
-    method public static android.support.v4.provider.DocumentFile fromFile(java.io.File);
-    method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri);
-    method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri);
-    method public abstract java.lang.String getName();
-    method public android.support.v4.provider.DocumentFile getParentFile();
-    method public abstract java.lang.String getType();
-    method public abstract android.net.Uri getUri();
-    method public abstract boolean isDirectory();
-    method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
-    method public abstract boolean isFile();
-    method public abstract boolean isVirtual();
-    method public abstract long lastModified();
-    method public abstract long length();
-    method public abstract android.support.v4.provider.DocumentFile[] listFiles();
-    method public abstract boolean renameTo(java.lang.String);
-  }
-
-}
-
-package android.support.v4.text {
-
-  public final class BidiFormatter {
-    method public static android.support.v4.text.BidiFormatter getInstance();
-    method public static android.support.v4.text.BidiFormatter getInstance(boolean);
-    method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale);
-    method public boolean getStereoReset();
-    method public boolean isRtl(java.lang.String);
-    method public boolean isRtl(java.lang.CharSequence);
-    method public boolean isRtlContext();
-    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
-    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat, boolean);
-    method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat);
-    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, android.support.v4.text.TextDirectionHeuristicCompat);
-    method public java.lang.String unicodeWrap(java.lang.String, boolean);
-    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence, boolean);
-    method public java.lang.String unicodeWrap(java.lang.String);
-    method public java.lang.CharSequence unicodeWrap(java.lang.CharSequence);
-  }
-
-  public static final class BidiFormatter.Builder {
-    ctor public BidiFormatter.Builder();
-    ctor public BidiFormatter.Builder(boolean);
-    ctor public BidiFormatter.Builder(java.util.Locale);
-    method public android.support.v4.text.BidiFormatter build();
-    method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat);
-    method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean);
-  }
-
-  public final class ICUCompat {
-    method public static java.lang.String maximizeAndGetScript(java.util.Locale);
-  }
-
-  public abstract interface TextDirectionHeuristicCompat {
-    method public abstract boolean isRtl(char[], int, int);
-    method public abstract boolean isRtl(java.lang.CharSequence, int, int);
-  }
-
-  public final class TextDirectionHeuristicsCompat {
-    field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR;
-    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR;
-    field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL;
-    field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE;
-    field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR;
-    field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL;
-  }
-
-  public final class TextUtilsCompat {
-    method public static int getLayoutDirectionFromLocale(java.util.Locale);
-    method public static java.lang.String htmlEncode(java.lang.String);
-    field public static final java.util.Locale ROOT;
-  }
-
-}
-
-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);
-  }
-
-  public static abstract class LinkifyCompat.LinkifyMask implements java.lang.annotation.Annotation {
-  }
-
-}
-
-package android.support.v4.util {
-
-  public class ArrayMap<K, V> extends android.support.v4.util.SimpleArrayMap implements java.util.Map {
-    ctor public ArrayMap();
-    ctor public ArrayMap(int);
-    ctor public ArrayMap(android.support.v4.util.SimpleArrayMap);
-    method public boolean containsAll(java.util.Collection<?>);
-    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
-    method public java.util.Set<K> keySet();
-    method public void putAll(java.util.Map<? extends K, ? extends V>);
-    method public boolean removeAll(java.util.Collection<?>);
-    method public boolean retainAll(java.util.Collection<?>);
-    method public java.util.Collection<V> values();
-  }
-
-  public final class ArraySet<E> implements java.util.Collection java.util.Set {
-    ctor public ArraySet();
-    ctor public ArraySet(int);
-    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
-    method public boolean add(E);
-    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
-    method public boolean addAll(java.util.Collection<? extends E>);
-    method public void clear();
-    method public boolean contains(java.lang.Object);
-    method public boolean containsAll(java.util.Collection<?>);
-    method public void ensureCapacity(int);
-    method public int indexOf(java.lang.Object);
-    method public boolean isEmpty();
-    method public java.util.Iterator<E> iterator();
-    method public boolean remove(java.lang.Object);
-    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
-    method public boolean removeAll(java.util.Collection<?>);
-    method public E removeAt(int);
-    method public boolean retainAll(java.util.Collection<?>);
-    method public int size();
-    method public java.lang.Object[] toArray();
-    method public <T> T[] toArray(T[]);
-    method public E valueAt(int);
-  }
-
-  public class AtomicFile {
-    ctor public AtomicFile(java.io.File);
-    method public void delete();
-    method public void failWrite(java.io.FileOutputStream);
-    method public void finishWrite(java.io.FileOutputStream);
-    method public java.io.File getBaseFile();
-    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
-    method public byte[] readFully() throws java.io.IOException;
-    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
-  }
-
-  public final class CircularArray<E> {
-    ctor public CircularArray();
-    ctor public CircularArray(int);
-    method public void addFirst(E);
-    method public void addLast(E);
-    method public void clear();
-    method public E get(int);
-    method public E getFirst();
-    method public E getLast();
-    method public boolean isEmpty();
-    method public E popFirst();
-    method public E popLast();
-    method public void removeFromEnd(int);
-    method public void removeFromStart(int);
-    method public int size();
-  }
-
-  public final class CircularIntArray {
-    ctor public CircularIntArray();
-    ctor public CircularIntArray(int);
-    method public void addFirst(int);
-    method public void addLast(int);
-    method public void clear();
-    method public int get(int);
-    method public int getFirst();
-    method public int getLast();
-    method public boolean isEmpty();
-    method public int popFirst();
-    method public int popLast();
-    method public void removeFromEnd(int);
-    method public void removeFromStart(int);
-    method public int size();
-  }
-
-  public class LongSparseArray<E> {
-    ctor public LongSparseArray();
-    ctor public LongSparseArray(int);
-    method public void append(long, E);
-    method public void clear();
-    method public android.support.v4.util.LongSparseArray<E> clone();
-    method public void delete(long);
-    method public E get(long);
-    method public E get(long, E);
-    method public int indexOfKey(long);
-    method public int indexOfValue(E);
-    method public long keyAt(int);
-    method public void put(long, E);
-    method public void remove(long);
-    method public void removeAt(int);
-    method public void setValueAt(int, E);
-    method public int size();
-    method public E valueAt(int);
-  }
-
-  public class LruCache<K, V> {
-    ctor public LruCache(int);
-    method protected V create(K);
-    method public final synchronized int createCount();
-    method protected void entryRemoved(boolean, K, V, V);
-    method public final void evictAll();
-    method public final synchronized int evictionCount();
-    method public final V get(K);
-    method public final synchronized int hitCount();
-    method public final synchronized int maxSize();
-    method public final synchronized int missCount();
-    method public final V put(K, V);
-    method public final synchronized int putCount();
-    method public final V remove(K);
-    method public void resize(int);
-    method public final synchronized int size();
-    method protected int sizeOf(K, V);
-    method public final synchronized java.util.Map<K, V> snapshot();
-    method public final synchronized java.lang.String toString();
-    method public void trimToSize(int);
-  }
-
-  public class Pair<F, S> {
-    ctor public Pair(F, S);
-    method public static <A, B> android.support.v4.util.Pair<A, B> create(A, B);
-    field public final F first;
-    field public final S second;
-  }
-
-  public final class PatternsCompat {
-    field public static final java.util.regex.Pattern DOMAIN_NAME;
-    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
-    field public static final java.util.regex.Pattern IP_ADDRESS;
-    field public static final java.util.regex.Pattern WEB_URL;
-  }
-
-  public final class Pools {
-  }
-
-  public static abstract interface Pools.Pool<T> {
-    method public abstract T acquire();
-    method public abstract boolean release(T);
-  }
-
-  public static class Pools.SimplePool<T> implements android.support.v4.util.Pools.Pool {
-    ctor public Pools.SimplePool(int);
-    method public T acquire();
-    method public boolean release(T);
-  }
-
-  public static class Pools.SynchronizedPool<T> extends android.support.v4.util.Pools.SimplePool {
-    ctor public Pools.SynchronizedPool(int);
-  }
-
-  public class SimpleArrayMap<K, V> {
-    ctor public SimpleArrayMap();
-    ctor public SimpleArrayMap(int);
-    ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap);
-    method public void clear();
-    method public boolean containsKey(java.lang.Object);
-    method public boolean containsValue(java.lang.Object);
-    method public void ensureCapacity(int);
-    method public V get(java.lang.Object);
-    method public int indexOfKey(java.lang.Object);
-    method public boolean isEmpty();
-    method public K keyAt(int);
-    method public V put(K, V);
-    method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>);
-    method public V remove(java.lang.Object);
-    method public V removeAt(int);
-    method public V setValueAt(int, V);
-    method public int size();
-    method public V valueAt(int);
-  }
-
-  public class SparseArrayCompat<E> {
-    ctor public SparseArrayCompat();
-    ctor public SparseArrayCompat(int);
-    method public void append(int, E);
-    method public void clear();
-    method public android.support.v4.util.SparseArrayCompat<E> clone();
-    method public void delete(int);
-    method public E get(int);
-    method public E get(int, E);
-    method public int indexOfKey(int);
-    method public int indexOfValue(E);
-    method public int keyAt(int);
-    method public void put(int, E);
-    method public void remove(int);
-    method public void removeAt(int);
-    method public void removeAtRange(int, int);
-    method public void setValueAt(int, E);
-    method public int size();
-    method public E valueAt(int);
-  }
-
-}
-
-package android.support.v4.view {
-
-  public abstract class AbsSavedState implements android.os.Parcelable {
-    ctor protected AbsSavedState(android.os.Parcelable);
-    ctor protected AbsSavedState(android.os.Parcel);
-    ctor protected AbsSavedState(android.os.Parcel, java.lang.ClassLoader);
-    method public int describeContents();
-    method public final android.os.Parcelable getSuperState();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.support.v4.view.AbsSavedState> CREATOR;
-    field public static final android.support.v4.view.AbsSavedState EMPTY_STATE;
-  }
-
-  public class AccessibilityDelegateCompat {
-    ctor public AccessibilityDelegateCompat();
-    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
-    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
-    method public void sendAccessibilityEvent(android.view.View, int);
-    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
-  }
-
-  public abstract class ActionProvider {
-    ctor public ActionProvider(android.content.Context);
-    method public android.content.Context getContext();
-    method public boolean hasSubMenu();
-    method public boolean isVisible();
-    method public abstract android.view.View onCreateActionView();
-    method public android.view.View onCreateActionView(android.view.MenuItem);
-    method public boolean onPerformDefaultAction();
-    method public void onPrepareSubMenu(android.view.SubMenu);
-    method public boolean overridesItemVisibility();
-    method public void refreshVisibility();
-    method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener);
-  }
-
-  public static abstract interface ActionProvider.VisibilityListener {
-    method public abstract void onActionProviderVisibilityChanged(boolean);
-  }
-
-  public final class AsyncLayoutInflater {
-    ctor public AsyncLayoutInflater(android.content.Context);
-    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
-  }
-
-  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
-    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
-  }
-
-  public final class GestureDetectorCompat {
-    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
-    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
-    method public boolean isLongpressEnabled();
-    method public boolean onTouchEvent(android.view.MotionEvent);
-    method public void setIsLongpressEnabled(boolean);
-    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
-  }
-
-  public final class GravityCompat {
-    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
-    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
-    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
-    method public static int getAbsoluteGravity(int, int);
-    field public static final int END = 8388613; // 0x800005
-    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
-    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
-    field public static final int START = 8388611; // 0x800003
-  }
-
-  public final class InputDeviceCompat {
-    field public static final int SOURCE_ANY = -256; // 0xffffff00
-    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
-    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
-    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
-    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
-    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
-    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
-    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
-    field public static final int SOURCE_DPAD = 513; // 0x201
-    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
-    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
-    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
-    field public static final int SOURCE_KEYBOARD = 257; // 0x101
-    field public static final int SOURCE_MOUSE = 8194; // 0x2002
-    field public static final int SOURCE_STYLUS = 16386; // 0x4002
-    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
-    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
-    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
-    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
-    field public static final int SOURCE_UNKNOWN = 0; // 0x0
-  }
-
-  public final class KeyEventCompat {
-    method public static deprecated boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object);
-    method public static deprecated java.lang.Object getKeyDispatcherState(android.view.View);
-    method public static boolean hasModifiers(android.view.KeyEvent, int);
-    method public static boolean hasNoModifiers(android.view.KeyEvent);
-    method public static boolean isCtrlPressed(android.view.KeyEvent);
-    method public static deprecated boolean isTracking(android.view.KeyEvent);
-    method public static boolean metaStateHasModifiers(int, int);
-    method public static boolean metaStateHasNoModifiers(int);
-    method public static int normalizeMetaState(int);
-    method public static deprecated void startTracking(android.view.KeyEvent);
-  }
-
-  public final class LayoutInflaterCompat {
-    method public static android.support.v4.view.LayoutInflaterFactory getFactory(android.view.LayoutInflater);
-    method public static void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory);
-  }
-
-  public abstract interface LayoutInflaterFactory {
-    method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
-  }
-
-  public final class MarginLayoutParamsCompat {
-    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
-    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
-    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
-    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
-    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
-    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
-    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
-    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
-  }
-
-  public final class MenuCompat {
-    method public static deprecated void setShowAsAction(android.view.MenuItem, int);
-  }
-
-  public final class MenuItemCompat {
-    method public static boolean collapseActionView(android.view.MenuItem);
-    method public static boolean expandActionView(android.view.MenuItem);
-    method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
-    method public static android.view.View getActionView(android.view.MenuItem);
-    method public static boolean isActionViewExpanded(android.view.MenuItem);
-    method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
-    method public static android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
-    method public static android.view.MenuItem setActionView(android.view.MenuItem, int);
-    method public static android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
-    method public static void setShowAsAction(android.view.MenuItem, int);
-    field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
-    field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
-    field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
-    field public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
-    field public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
-  }
-
-  public static abstract interface MenuItemCompat.OnActionExpandListener {
-    method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem);
-    method public abstract boolean onMenuItemActionExpand(android.view.MenuItem);
-  }
-
-  public final class MotionEventCompat {
-    method public static deprecated int findPointerIndex(android.view.MotionEvent, int);
-    method public static int getActionIndex(android.view.MotionEvent);
-    method public static int getActionMasked(android.view.MotionEvent);
-    method public static float getAxisValue(android.view.MotionEvent, int);
-    method public static float getAxisValue(android.view.MotionEvent, int, int);
-    method public static int getButtonState(android.view.MotionEvent);
-    method public static deprecated int getPointerCount(android.view.MotionEvent);
-    method public static deprecated int getPointerId(android.view.MotionEvent, int);
-    method public static deprecated int getSource(android.view.MotionEvent);
-    method public static deprecated float getX(android.view.MotionEvent, int);
-    method public static deprecated float getY(android.view.MotionEvent, int);
-    method public static boolean isFromSource(android.view.MotionEvent, int);
-    field public static final int ACTION_HOVER_ENTER = 9; // 0x9
-    field public static final int ACTION_HOVER_EXIT = 10; // 0xa
-    field public static final int ACTION_HOVER_MOVE = 7; // 0x7
-    field public static final int ACTION_MASK = 255; // 0xff
-    field public static final int ACTION_POINTER_DOWN = 5; // 0x5
-    field public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
-    field public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
-    field public static final int ACTION_POINTER_UP = 6; // 0x6
-    field public static final int ACTION_SCROLL = 8; // 0x8
-    field public static final int AXIS_BRAKE = 23; // 0x17
-    field public static final int AXIS_DISTANCE = 24; // 0x18
-    field public static final int AXIS_GAS = 22; // 0x16
-    field public static final int AXIS_GENERIC_1 = 32; // 0x20
-    field public static final int AXIS_GENERIC_10 = 41; // 0x29
-    field public static final int AXIS_GENERIC_11 = 42; // 0x2a
-    field public static final int AXIS_GENERIC_12 = 43; // 0x2b
-    field public static final int AXIS_GENERIC_13 = 44; // 0x2c
-    field public static final int AXIS_GENERIC_14 = 45; // 0x2d
-    field public static final int AXIS_GENERIC_15 = 46; // 0x2e
-    field public static final int AXIS_GENERIC_16 = 47; // 0x2f
-    field public static final int AXIS_GENERIC_2 = 33; // 0x21
-    field public static final int AXIS_GENERIC_3 = 34; // 0x22
-    field public static final int AXIS_GENERIC_4 = 35; // 0x23
-    field public static final int AXIS_GENERIC_5 = 36; // 0x24
-    field public static final int AXIS_GENERIC_6 = 37; // 0x25
-    field public static final int AXIS_GENERIC_7 = 38; // 0x26
-    field public static final int AXIS_GENERIC_8 = 39; // 0x27
-    field public static final int AXIS_GENERIC_9 = 40; // 0x28
-    field public static final int AXIS_HAT_X = 15; // 0xf
-    field public static final int AXIS_HAT_Y = 16; // 0x10
-    field public static final int AXIS_HSCROLL = 10; // 0xa
-    field public static final int AXIS_LTRIGGER = 17; // 0x11
-    field public static final int AXIS_ORIENTATION = 8; // 0x8
-    field public static final int AXIS_PRESSURE = 2; // 0x2
-    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
-    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
-    field public static final int AXIS_RTRIGGER = 18; // 0x12
-    field public static final int AXIS_RUDDER = 20; // 0x14
-    field public static final int AXIS_RX = 12; // 0xc
-    field public static final int AXIS_RY = 13; // 0xd
-    field public static final int AXIS_RZ = 14; // 0xe
-    field public static final int AXIS_SIZE = 3; // 0x3
-    field public static final int AXIS_THROTTLE = 19; // 0x13
-    field public static final int AXIS_TILT = 25; // 0x19
-    field public static final int AXIS_TOOL_MAJOR = 6; // 0x6
-    field public static final int AXIS_TOOL_MINOR = 7; // 0x7
-    field public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
-    field public static final int AXIS_TOUCH_MINOR = 5; // 0x5
-    field public static final int AXIS_VSCROLL = 9; // 0x9
-    field public static final int AXIS_WHEEL = 21; // 0x15
-    field public static final int AXIS_X = 0; // 0x0
-    field public static final int AXIS_Y = 1; // 0x1
-    field public static final int AXIS_Z = 11; // 0xb
-    field public static final int BUTTON_PRIMARY = 1; // 0x1
-  }
-
-  public abstract interface NestedScrollingChild {
-    method public abstract boolean dispatchNestedFling(float, float, boolean);
-    method public abstract boolean dispatchNestedPreFling(float, float);
-    method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]);
-    method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]);
-    method public abstract boolean hasNestedScrollingParent();
-    method public abstract boolean isNestedScrollingEnabled();
-    method public abstract void setNestedScrollingEnabled(boolean);
-    method public abstract boolean startNestedScroll(int);
-    method public abstract void stopNestedScroll();
-  }
-
-  public class NestedScrollingChildHelper {
-    ctor public NestedScrollingChildHelper(android.view.View);
-    method public boolean dispatchNestedFling(float, float, boolean);
-    method public boolean dispatchNestedPreFling(float, float);
-    method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
-    method public boolean dispatchNestedScroll(int, int, int, int, int[]);
-    method public boolean hasNestedScrollingParent();
-    method public boolean isNestedScrollingEnabled();
-    method public void onDetachedFromWindow();
-    method public void onStopNestedScroll(android.view.View);
-    method public void setNestedScrollingEnabled(boolean);
-    method public boolean startNestedScroll(int);
-    method public void stopNestedScroll();
-  }
-
-  public abstract interface NestedScrollingParent {
-    method public abstract int getNestedScrollAxes();
-    method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
-    method public abstract boolean onNestedPreFling(android.view.View, float, float);
-    method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
-    method public abstract void onNestedScroll(android.view.View, int, int, int, int);
-    method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
-    method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int);
-    method public abstract void onStopNestedScroll(android.view.View);
-  }
-
-  public class NestedScrollingParentHelper {
-    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
-    method public int getNestedScrollAxes();
-    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
-    method public void onStopNestedScroll(android.view.View);
-  }
-
-  public abstract interface OnApplyWindowInsetsListener {
-    method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
-  }
-
-  public abstract class PagerAdapter {
-    ctor public PagerAdapter();
-    method public void destroyItem(android.view.ViewGroup, int, java.lang.Object);
-    method public deprecated void destroyItem(android.view.View, int, java.lang.Object);
-    method public void finishUpdate(android.view.ViewGroup);
-    method public deprecated void finishUpdate(android.view.View);
-    method public abstract int getCount();
-    method public int getItemPosition(java.lang.Object);
-    method public java.lang.CharSequence getPageTitle(int);
-    method public float getPageWidth(int);
-    method public java.lang.Object instantiateItem(android.view.ViewGroup, int);
-    method public deprecated java.lang.Object instantiateItem(android.view.View, int);
-    method public abstract boolean isViewFromObject(android.view.View, java.lang.Object);
-    method public void notifyDataSetChanged();
-    method public void registerDataSetObserver(android.database.DataSetObserver);
-    method public void restoreState(android.os.Parcelable, java.lang.ClassLoader);
-    method public android.os.Parcelable saveState();
-    method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
-    method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object);
-    method public void startUpdate(android.view.ViewGroup);
-    method public deprecated void startUpdate(android.view.View);
-    method public void unregisterDataSetObserver(android.database.DataSetObserver);
-    field public static final int POSITION_NONE = -2; // 0xfffffffe
-    field public static final int POSITION_UNCHANGED = -1; // 0xffffffff
-  }
-
-  public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip {
-    ctor public PagerTabStrip(android.content.Context);
-    ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet);
-    method public boolean getDrawFullUnderline();
-    method public int getTabIndicatorColor();
-    method public void setDrawFullUnderline(boolean);
-    method public void setTabIndicatorColor(int);
-    method public void setTabIndicatorColorResource(int);
-  }
-
-  public class PagerTitleStrip extends android.view.ViewGroup {
-    ctor public PagerTitleStrip(android.content.Context);
-    ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet);
-    method public int getTextSpacing();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setGravity(int);
-    method public void setNonPrimaryAlpha(float);
-    method public void setTextColor(int);
-    method public void setTextSize(int, float);
-    method public void setTextSpacing(int);
-  }
-
-  public final class PointerIconCompat {
-    method public static android.support.v4.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
-    method public static android.support.v4.view.PointerIconCompat getSystemIcon(android.content.Context, int);
-    method public static android.support.v4.view.PointerIconCompat load(android.content.res.Resources, int);
-    field public static final int TYPE_ALIAS = 1010; // 0x3f2
-    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
-    field public static final int TYPE_ARROW = 1000; // 0x3e8
-    field public static final int TYPE_CELL = 1006; // 0x3ee
-    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
-    field public static final int TYPE_COPY = 1011; // 0x3f3
-    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
-    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
-    field public static final int TYPE_GRAB = 1020; // 0x3fc
-    field public static final int TYPE_GRABBING = 1021; // 0x3fd
-    field public static final int TYPE_HAND = 1002; // 0x3ea
-    field public static final int TYPE_HELP = 1003; // 0x3eb
-    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
-    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
-    field public static final int TYPE_NULL = 0; // 0x0
-    field public static final int TYPE_TEXT = 1008; // 0x3f0
-    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
-    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
-    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
-    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
-    field public static final int TYPE_WAIT = 1004; // 0x3ec
-    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
-    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
-  }
-
-  public final class ScaleGestureDetectorCompat {
-    method public static boolean isQuickScaleEnabled(java.lang.Object);
-    method public static void setQuickScaleEnabled(java.lang.Object, boolean);
-  }
-
-  public abstract interface ScrollingView {
-    method public abstract int computeHorizontalScrollExtent();
-    method public abstract int computeHorizontalScrollOffset();
-    method public abstract int computeHorizontalScrollRange();
-    method public abstract int computeVerticalScrollExtent();
-    method public abstract int computeVerticalScrollOffset();
-    method public abstract int computeVerticalScrollRange();
-  }
-
-  public abstract interface TintableBackgroundView {
-    method public abstract android.content.res.ColorStateList getSupportBackgroundTintList();
-    method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode();
-    method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList);
-    method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode);
-  }
-
-  public final class VelocityTrackerCompat {
-    method public static float getXVelocity(android.view.VelocityTracker, int);
-    method public static float getYVelocity(android.view.VelocityTracker, int);
-  }
-
-  public class ViewCompat {
-    ctor protected ViewCompat();
-    method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View);
-    method public static boolean canScrollHorizontally(android.view.View, int);
-    method public static boolean canScrollVertically(android.view.View, int);
-    method public static int combineMeasuredStates(int, int);
-    method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
-    method public static void dispatchFinishTemporaryDetach(android.view.View);
-    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
-    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
-    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]);
-    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]);
-    method public static void dispatchStartTemporaryDetach(android.view.View);
-    method public static int getAccessibilityLiveRegion(android.view.View);
-    method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View);
-    method public static float getAlpha(android.view.View);
-    method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View);
-    method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View);
-    method public static android.graphics.Rect getClipBounds(android.view.View);
-    method public static android.view.Display getDisplay(android.view.View);
-    method public static float getElevation(android.view.View);
-    method public static boolean getFitsSystemWindows(android.view.View);
-    method public static int getImportantForAccessibility(android.view.View);
-    method public static int getLabelFor(android.view.View);
-    method public static int getLayerType(android.view.View);
-    method public static int getLayoutDirection(android.view.View);
-    method public static android.graphics.Matrix getMatrix(android.view.View);
-    method public static int getMeasuredHeightAndState(android.view.View);
-    method public static int getMeasuredState(android.view.View);
-    method public static int getMeasuredWidthAndState(android.view.View);
-    method public static int getMinimumHeight(android.view.View);
-    method public static int getMinimumWidth(android.view.View);
-    method public static deprecated int getOverScrollMode(android.view.View);
-    method public static int getPaddingEnd(android.view.View);
-    method public static int getPaddingStart(android.view.View);
-    method public static android.view.ViewParent getParentForAccessibility(android.view.View);
-    method public static float getPivotX(android.view.View);
-    method public static float getPivotY(android.view.View);
-    method public static float getRotation(android.view.View);
-    method public static float getRotationX(android.view.View);
-    method public static float getRotationY(android.view.View);
-    method public static float getScaleX(android.view.View);
-    method public static float getScaleY(android.view.View);
-    method public static int getScrollIndicators(android.view.View);
-    method public static java.lang.String getTransitionName(android.view.View);
-    method public static float getTranslationX(android.view.View);
-    method public static float getTranslationY(android.view.View);
-    method public static float getTranslationZ(android.view.View);
-    method public static int getWindowSystemUiVisibility(android.view.View);
-    method public static float getX(android.view.View);
-    method public static float getY(android.view.View);
-    method public static float getZ(android.view.View);
-    method public static boolean hasAccessibilityDelegate(android.view.View);
-    method public static boolean hasNestedScrollingParent(android.view.View);
-    method public static boolean hasOnClickListeners(android.view.View);
-    method public static boolean hasOverlappingRendering(android.view.View);
-    method public static boolean hasTransientState(android.view.View);
-    method public static boolean isAttachedToWindow(android.view.View);
-    method public static boolean isImportantForAccessibility(android.view.View);
-    method public static boolean isInLayout(android.view.View);
-    method public static boolean isLaidOut(android.view.View);
-    method public static boolean isLayoutDirectionResolved(android.view.View);
-    method public static boolean isNestedScrollingEnabled(android.view.View);
-    method public static deprecated boolean isOpaque(android.view.View);
-    method public static boolean isPaddingRelative(android.view.View);
-    method public static void jumpDrawablesToCurrentState(android.view.View);
-    method public static void offsetLeftAndRight(android.view.View, int);
-    method public static void offsetTopAndBottom(android.view.View, int);
-    method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat);
-    method public static void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public static void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
-    method public static void postInvalidateOnAnimation(android.view.View);
-    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
-    method public static void postOnAnimation(android.view.View, java.lang.Runnable);
-    method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long);
-    method public static void requestApplyInsets(android.view.View);
-    method public static int resolveSizeAndState(int, int, int);
-    method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat);
-    method public static void setAccessibilityLiveRegion(android.view.View, int);
-    method public static void setActivated(android.view.View, boolean);
-    method public static void setAlpha(android.view.View, float);
-    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable);
-    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList);
-    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode);
-    method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean);
-    method public static void setClipBounds(android.view.View, android.graphics.Rect);
-    method public static void setElevation(android.view.View, float);
-    method public static void setFitsSystemWindows(android.view.View, boolean);
-    method public static void setHasTransientState(android.view.View, boolean);
-    method public static void setImportantForAccessibility(android.view.View, int);
-    method public static void setLabelFor(android.view.View, int);
-    method public static void setLayerPaint(android.view.View, android.graphics.Paint);
-    method public static void setLayerType(android.view.View, int, android.graphics.Paint);
-    method public static void setLayoutDirection(android.view.View, int);
-    method public static void setNestedScrollingEnabled(android.view.View, boolean);
-    method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener);
-    method public static deprecated void setOverScrollMode(android.view.View, int);
-    method public static void setPaddingRelative(android.view.View, int, int, int, int);
-    method public static void setPivotX(android.view.View, float);
-    method public static void setPivotY(android.view.View, float);
-    method public static void setPointerIcon(android.view.View, android.support.v4.view.PointerIconCompat);
-    method public static void setRotation(android.view.View, float);
-    method public static void setRotationX(android.view.View, float);
-    method public static void setRotationY(android.view.View, float);
-    method public static void setSaveFromParentEnabled(android.view.View, boolean);
-    method public static void setScaleX(android.view.View, float);
-    method public static void setScaleY(android.view.View, float);
-    method public static void setScrollIndicators(android.view.View, int);
-    method public static void setScrollIndicators(android.view.View, int, int);
-    method public static void setTransitionName(android.view.View, java.lang.String);
-    method public static void setTranslationX(android.view.View, float);
-    method public static void setTranslationY(android.view.View, float);
-    method public static void setTranslationZ(android.view.View, float);
-    method public static void setX(android.view.View, float);
-    method public static void setY(android.view.View, float);
-    method public static void setZ(android.view.View, float);
-    method public static boolean startNestedScroll(android.view.View, int);
-    method public static void stopNestedScroll(android.view.View);
-    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
-    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
-    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
-    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
-    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
-    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
-    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
-    field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
-    field public static final int LAYER_TYPE_NONE = 0; // 0x0
-    field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
-    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
-    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
-    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
-    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
-    field public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
-    field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
-    field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
-    field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
-    field public static final deprecated int OVER_SCROLL_ALWAYS = 0; // 0x0
-    field public static final deprecated int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
-    field public static final deprecated int OVER_SCROLL_NEVER = 2; // 0x2
-    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
-    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
-    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
-    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
-    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
-    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
-    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
-    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
-    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
-  }
-
-  public final class ViewConfigurationCompat {
-    method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
-    method public static boolean hasPermanentMenuKey(android.view.ViewConfiguration);
-  }
-
-  public final class ViewGroupCompat {
-    method public static int getLayoutMode(android.view.ViewGroup);
-    method public static int getNestedScrollAxes(android.view.ViewGroup);
-    method public static boolean isTransitionGroup(android.view.ViewGroup);
-    method public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
-    method public static void setLayoutMode(android.view.ViewGroup, int);
-    method public static void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
-    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
-    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
-    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
-  }
-
-  public class ViewPager extends android.view.ViewGroup {
-    ctor public ViewPager(android.content.Context);
-    ctor public ViewPager(android.content.Context, android.util.AttributeSet);
-    method public void addOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
-    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
-    method public boolean arrowScroll(int);
-    method public boolean beginFakeDrag();
-    method protected boolean canScroll(android.view.View, boolean, int, int, int);
-    method public void clearOnPageChangeListeners();
-    method public void endFakeDrag();
-    method public boolean executeKeyEvent(android.view.KeyEvent);
-    method public void fakeDragBy(float);
-    method public android.support.v4.view.PagerAdapter getAdapter();
-    method public int getCurrentItem();
-    method public int getOffscreenPageLimit();
-    method public int getPageMargin();
-    method public boolean isFakeDragging();
-    method protected void onLayout(boolean, int, int, int, int);
-    method protected void onPageScrolled(int, float, int);
-    method public void onRestoreInstanceState(android.os.Parcelable);
-    method public android.os.Parcelable onSaveInstanceState();
-    method public void removeOnAdapterChangeListener(android.support.v4.view.ViewPager.OnAdapterChangeListener);
-    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
-    method public void setAdapter(android.support.v4.view.PagerAdapter);
-    method public void setCurrentItem(int);
-    method public void setCurrentItem(int, boolean);
-    method public void setOffscreenPageLimit(int);
-    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
-    method public void setPageMargin(int);
-    method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
-    method public void setPageMarginDrawable(int);
-    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer);
-    method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer, int);
-    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
-    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
-    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
-  }
-
-  public static abstract class ViewPager.DecorView implements java.lang.annotation.Annotation {
-  }
-
-  public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams {
-    ctor public ViewPager.LayoutParams();
-    ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet);
-    field public int gravity;
-    field public boolean isDecor;
-  }
-
-  public static abstract interface ViewPager.OnAdapterChangeListener {
-    method public abstract void onAdapterChanged(android.support.v4.view.ViewPager, android.support.v4.view.PagerAdapter, android.support.v4.view.PagerAdapter);
-  }
-
-  public static abstract interface ViewPager.OnPageChangeListener {
-    method public abstract void onPageScrollStateChanged(int);
-    method public abstract void onPageScrolled(int, float, int);
-    method public abstract void onPageSelected(int);
-  }
-
-  public static abstract interface ViewPager.PageTransformer {
-    method public abstract void transformPage(android.view.View, float);
-  }
-
-  public static class ViewPager.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public ViewPager.SavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR;
-  }
-
-  public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
-    ctor public ViewPager.SimpleOnPageChangeListener();
-    method public void onPageScrollStateChanged(int);
-    method public void onPageScrolled(int, float, int);
-    method public void onPageSelected(int);
-  }
-
-  public final class ViewParentCompat {
-    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
-    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
-    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
-    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
-    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
-    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
-    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
-    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
-    method public static boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
-  }
-
-  public final class ViewPropertyAnimatorCompat {
-    method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float);
-    method public void cancel();
-    method public long getDuration();
-    method public android.view.animation.Interpolator getInterpolator();
-    method public long getStartDelay();
-    method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener);
-    method public void start();
-    method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer();
-    method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat x(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat y(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat z(float);
-    method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float);
-  }
-
-  public abstract interface ViewPropertyAnimatorListener {
-    method public abstract void onAnimationCancel(android.view.View);
-    method public abstract void onAnimationEnd(android.view.View);
-    method public abstract void onAnimationStart(android.view.View);
-  }
-
-  public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener {
-    ctor public ViewPropertyAnimatorListenerAdapter();
-    method public void onAnimationCancel(android.view.View);
-    method public void onAnimationEnd(android.view.View);
-    method public void onAnimationStart(android.view.View);
-  }
-
-  public abstract interface ViewPropertyAnimatorUpdateListener {
-    method public abstract void onAnimationUpdate(android.view.View);
-  }
-
-  public final class WindowCompat {
-    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
-    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
-    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
-  }
-
-  public class WindowInsetsCompat {
-    ctor public WindowInsetsCompat(android.support.v4.view.WindowInsetsCompat);
-    method public android.support.v4.view.WindowInsetsCompat consumeStableInsets();
-    method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets();
-    method public int getStableInsetBottom();
-    method public int getStableInsetLeft();
-    method public int getStableInsetRight();
-    method public int getStableInsetTop();
-    method public int getSystemWindowInsetBottom();
-    method public int getSystemWindowInsetLeft();
-    method public int getSystemWindowInsetRight();
-    method public int getSystemWindowInsetTop();
-    method public boolean hasInsets();
-    method public boolean hasStableInsets();
-    method public boolean hasSystemWindowInsets();
-    method public boolean isConsumed();
-    method public boolean isRound();
-    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
-    method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
-  }
-
-}
-
-package android.support.v4.view.accessibility {
-
-  public final class AccessibilityEventCompat {
-    method public static void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat);
-    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent);
-    method public int getAction(android.view.accessibility.AccessibilityEvent);
-    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
-    method public int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
-    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int);
-    method public static int getRecordCount(android.view.accessibility.AccessibilityEvent);
-    method public void setAction(android.view.accessibility.AccessibilityEvent, int);
-    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
-    method public void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
-    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
-    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
-    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
-    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
-    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
-    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
-    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
-    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
-    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
-    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
-    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
-    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
-    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
-    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
-    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
-    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
-    field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
-    field public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
-    field public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
-    field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
-    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
-    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
-    field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
-  }
-
-  public final class AccessibilityManagerCompat {
-    method public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
-    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
-    method public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int);
-    method public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager);
-    method public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager);
-    method public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener);
-    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
-  }
-
-  public static abstract interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
-    method public abstract void onAccessibilityStateChanged(boolean);
-  }
-
-  public static abstract deprecated class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
-    ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
-  }
-
-  public static abstract interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
-    method public abstract void onTouchExplorationStateChanged(boolean);
-  }
-
-  public class AccessibilityNodeInfoCompat {
-    ctor public AccessibilityNodeInfoCompat(java.lang.Object);
-    method public void addAction(int);
-    method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
-    method public void addChild(android.view.View);
-    method public void addChild(android.view.View, int);
-    method public boolean canOpenPopup();
-    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String);
-    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String);
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int);
-    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList();
-    method public int getActions();
-    method public void getBoundsInParent(android.graphics.Rect);
-    method public void getBoundsInScreen(android.graphics.Rect);
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int);
-    method public int getChildCount();
-    method public java.lang.CharSequence getClassName();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo();
-    method public java.lang.CharSequence getContentDescription();
-    method public int getDrawingOrder();
-    method public java.lang.CharSequence getError();
-    method public android.os.Bundle getExtras();
-    method public java.lang.Object getInfo();
-    method public int getInputType();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy();
-    method public int getLiveRegion();
-    method public int getMaxTextLength();
-    method public int getMovementGranularities();
-    method public java.lang.CharSequence getPackageName();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo();
-    method public java.lang.CharSequence getRoleDescription();
-    method public java.lang.CharSequence getText();
-    method public int getTextSelectionEnd();
-    method public int getTextSelectionStart();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore();
-    method public java.lang.String getViewIdResourceName();
-    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow();
-    method public int getWindowId();
-    method public boolean isAccessibilityFocused();
-    method public boolean isCheckable();
-    method public boolean isChecked();
-    method public boolean isClickable();
-    method public boolean isContentInvalid();
-    method public boolean isContextClickable();
-    method public boolean isDismissable();
-    method public boolean isEditable();
-    method public boolean isEnabled();
-    method public boolean isFocusable();
-    method public boolean isFocused();
-    method public boolean isImportantForAccessibility();
-    method public boolean isLongClickable();
-    method public boolean isMultiLine();
-    method public boolean isPassword();
-    method public boolean isScrollable();
-    method public boolean isSelected();
-    method public boolean isVisibleToUser();
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View);
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int);
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain();
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public boolean performAction(int);
-    method public boolean performAction(int, android.os.Bundle);
-    method public void recycle();
-    method public boolean refresh();
-    method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat);
-    method public boolean removeChild(android.view.View);
-    method public boolean removeChild(android.view.View, int);
-    method public void setAccessibilityFocused(boolean);
-    method public void setBoundsInParent(android.graphics.Rect);
-    method public void setBoundsInScreen(android.graphics.Rect);
-    method public void setCanOpenPopup(boolean);
-    method public void setCheckable(boolean);
-    method public void setChecked(boolean);
-    method public void setClassName(java.lang.CharSequence);
-    method public void setClickable(boolean);
-    method public void setCollectionInfo(java.lang.Object);
-    method public void setCollectionItemInfo(java.lang.Object);
-    method public void setContentDescription(java.lang.CharSequence);
-    method public void setContentInvalid(boolean);
-    method public void setContextClickable(boolean);
-    method public void setDismissable(boolean);
-    method public void setDrawingOrder(int);
-    method public void setEditable(boolean);
-    method public void setEnabled(boolean);
-    method public void setError(java.lang.CharSequence);
-    method public void setFocusable(boolean);
-    method public void setFocused(boolean);
-    method public void setImportantForAccessibility(boolean);
-    method public void setInputType(int);
-    method public void setLabelFor(android.view.View);
-    method public void setLabelFor(android.view.View, int);
-    method public void setLabeledBy(android.view.View);
-    method public void setLabeledBy(android.view.View, int);
-    method public void setLiveRegion(int);
-    method public void setLongClickable(boolean);
-    method public void setMaxTextLength(int);
-    method public void setMovementGranularities(int);
-    method public void setMultiLine(boolean);
-    method public void setPackageName(java.lang.CharSequence);
-    method public void setParent(android.view.View);
-    method public void setParent(android.view.View, int);
-    method public void setPassword(boolean);
-    method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat);
-    method public void setRoleDescription(java.lang.CharSequence);
-    method public void setScrollable(boolean);
-    method public void setSelected(boolean);
-    method public void setSource(android.view.View);
-    method public void setSource(android.view.View, int);
-    method public void setText(java.lang.CharSequence);
-    method public void setTextSelection(int, int);
-    method public void setTraversalAfter(android.view.View);
-    method public void setTraversalAfter(android.view.View, int);
-    method public void setTraversalBefore(android.view.View);
-    method public void setTraversalBefore(android.view.View, int);
-    method public void setViewIdResourceName(java.lang.String);
-    method public void setVisibleToUser(boolean);
-    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
-    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
-    field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
-    field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
-    field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
-    field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
-    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
-    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
-    field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
-    field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
-    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
-    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
-    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
-    field public static final int ACTION_CLICK = 16; // 0x10
-    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
-    field public static final int ACTION_COPY = 16384; // 0x4000
-    field public static final int ACTION_CUT = 65536; // 0x10000
-    field public static final int ACTION_DISMISS = 1048576; // 0x100000
-    field public static final int ACTION_EXPAND = 262144; // 0x40000
-    field public static final int ACTION_FOCUS = 1; // 0x1
-    field public static final int ACTION_LONG_CLICK = 32; // 0x20
-    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
-    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
-    field public static final int ACTION_PASTE = 32768; // 0x8000
-    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
-    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
-    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
-    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
-    field public static final int ACTION_SELECT = 4; // 0x4
-    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
-    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
-    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
-    field public static final int FOCUS_INPUT = 1; // 0x1
-    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
-    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
-    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
-    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
-    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
-  }
-
-  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
-    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence);
-    method public int getId();
-    method public java.lang.CharSequence getLabel();
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CONTEXT_CLICK;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_DOWN;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_LEFT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_RIGHT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_TO_POSITION;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_UP;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_PROGRESS;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT;
-    field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_ON_SCREEN;
-  }
-
-  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
-    method public int getColumnCount();
-    method public int getRowCount();
-    method public int getSelectionMode();
-    method public boolean isHierarchical();
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int);
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean);
-    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
-    field public static final int SELECTION_MODE_NONE = 0; // 0x0
-    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
-  }
-
-  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
-    method public int getColumnIndex();
-    method public int getColumnSpan();
-    method public int getRowIndex();
-    method public int getRowSpan();
-    method public boolean isHeading();
-    method public boolean isSelected();
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean);
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean);
-  }
-
-  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
-    method public float getCurrent();
-    method public float getMax();
-    method public float getMin();
-    method public int getType();
-    method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat obtain(int, float, float, float);
-    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
-    field public static final int RANGE_TYPE_INT = 0; // 0x0
-    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
-  }
-
-  public class AccessibilityNodeProviderCompat {
-    ctor public AccessibilityNodeProviderCompat();
-    ctor public AccessibilityNodeProviderCompat(java.lang.Object);
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int);
-    method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int);
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int);
-    method public java.lang.Object getProvider();
-    method public boolean performAction(int, int, android.os.Bundle);
-    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
-  }
-
-  public class AccessibilityRecordCompat {
-    ctor public deprecated AccessibilityRecordCompat(java.lang.Object);
-    method public int getAddedCount();
-    method public java.lang.CharSequence getBeforeText();
-    method public java.lang.CharSequence getClassName();
-    method public java.lang.CharSequence getContentDescription();
-    method public int getCurrentItemIndex();
-    method public int getFromIndex();
-    method public deprecated java.lang.Object getImpl();
-    method public int getItemCount();
-    method public int getMaxScrollX();
-    method public int getMaxScrollY();
-    method public android.os.Parcelable getParcelableData();
-    method public int getRemovedCount();
-    method public int getScrollX();
-    method public int getScrollY();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource();
-    method public java.util.List<java.lang.CharSequence> getText();
-    method public int getToIndex();
-    method public int getWindowId();
-    method public boolean isChecked();
-    method public boolean isEnabled();
-    method public boolean isFullScreen();
-    method public boolean isPassword();
-    method public boolean isScrollable();
-    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat);
-    method public static android.support.v4.view.accessibility.AccessibilityRecordCompat obtain();
-    method public void recycle();
-    method public void setAddedCount(int);
-    method public void setBeforeText(java.lang.CharSequence);
-    method public void setChecked(boolean);
-    method public void setClassName(java.lang.CharSequence);
-    method public void setContentDescription(java.lang.CharSequence);
-    method public void setCurrentItemIndex(int);
-    method public void setEnabled(boolean);
-    method public void setFromIndex(int);
-    method public void setFullScreen(boolean);
-    method public void setItemCount(int);
-    method public void setMaxScrollX(int);
-    method public void setMaxScrollY(int);
-    method public void setParcelableData(android.os.Parcelable);
-    method public void setPassword(boolean);
-    method public void setRemovedCount(int);
-    method public void setScrollX(int);
-    method public void setScrollY(int);
-    method public void setScrollable(boolean);
-    method public void setSource(android.view.View);
-    method public void setSource(android.view.View, int);
-    method public void setToIndex(int);
-  }
-
-  public class AccessibilityWindowInfoCompat {
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getAnchor();
-    method public void getBoundsInScreen(android.graphics.Rect);
-    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int);
-    method public int getChildCount();
-    method public int getId();
-    method public int getLayer();
-    method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent();
-    method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot();
-    method public java.lang.CharSequence getTitle();
-    method public int getType();
-    method public boolean isAccessibilityFocused();
-    method public boolean isActive();
-    method public boolean isFocused();
-    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain();
-    method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat);
-    method public void recycle();
-    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
-    field public static final int TYPE_APPLICATION = 1; // 0x1
-    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
-    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
-    field public static final int TYPE_SYSTEM = 3; // 0x3
-  }
-
-}
-
-package android.support.v4.view.animation {
-
-  public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
-    ctor public FastOutLinearInInterpolator();
-  }
-
-  public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
-    ctor public FastOutSlowInInterpolator();
-  }
-
-  public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator {
-    ctor public LinearOutSlowInInterpolator();
-  }
-
-   abstract class LookupTableInterpolator implements android.view.animation.Interpolator {
-    ctor public LookupTableInterpolator(float[]);
-    method public float getInterpolation(float);
-  }
-
-  public final class PathInterpolatorCompat {
-    method public static android.view.animation.Interpolator create(android.graphics.Path);
-    method public static android.view.animation.Interpolator create(float, float);
-    method public static android.view.animation.Interpolator create(float, float, float, float);
-  }
-
-}
-
-package android.support.v4.widget {
-
-  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
-    ctor public AutoScrollHelper(android.view.View);
-    method public abstract boolean canTargetScrollHorizontally(int);
-    method public abstract boolean canTargetScrollVertically(int);
-    method public boolean isEnabled();
-    method public boolean isExclusive();
-    method public boolean onTouch(android.view.View, android.view.MotionEvent);
-    method public abstract void scrollTargetBy(int, int);
-    method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int);
-    method public android.support.v4.widget.AutoScrollHelper setEdgeType(int);
-    method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean);
-    method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean);
-    method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float);
-    method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float);
-    method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float);
-    method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int);
-    method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int);
-    method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float);
-    method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float);
-    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
-    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
-    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
-    field public static final float NO_MAX = 3.4028235E38f;
-    field public static final float NO_MIN = 0.0f;
-    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
-  }
-
-  public final class CompoundButtonCompat {
-    method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
-    method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
-    method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton);
-    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList);
-    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode);
-  }
-
-  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 void onAttachedToWindow();
-    method public void onDetachedFromWindow();
-    method public void show();
-  }
-
-  public abstract class CursorAdapter extends android.widget.BaseAdapter {
-    ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor);
-    ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean);
-    ctor public CursorAdapter(android.content.Context, android.database.Cursor, int);
-    method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor);
-    method public void changeCursor(android.database.Cursor);
-    method public java.lang.CharSequence convertToString(android.database.Cursor);
-    method public int getCount();
-    method public android.database.Cursor getCursor();
-    method public android.widget.Filter getFilter();
-    method public android.widget.FilterQueryProvider getFilterQueryProvider();
-    method public java.lang.Object getItem(int);
-    method public long getItemId(int);
-    method public android.view.View getView(int, android.view.View, android.view.ViewGroup);
-    method protected deprecated void init(android.content.Context, android.database.Cursor, boolean);
-    method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
-    method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
-    method protected void onContentChanged();
-    method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence);
-    method public void setFilterQueryProvider(android.widget.FilterQueryProvider);
-    method public android.database.Cursor swapCursor(android.database.Cursor);
-    field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1
-    field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2
-  }
-
-  public class DrawerLayout extends android.view.ViewGroup {
-    ctor public DrawerLayout(android.content.Context);
-    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet);
-    ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int);
-    method public void addDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
-    method public void closeDrawer(android.view.View);
-    method public void closeDrawer(android.view.View, boolean);
-    method public void closeDrawer(int);
-    method public void closeDrawer(int, boolean);
-    method public void closeDrawers();
-    method public float getDrawerElevation();
-    method public int getDrawerLockMode(int);
-    method public int getDrawerLockMode(android.view.View);
-    method public java.lang.CharSequence getDrawerTitle(int);
-    method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable();
-    method public boolean isDrawerOpen(android.view.View);
-    method public boolean isDrawerOpen(int);
-    method public boolean isDrawerVisible(android.view.View);
-    method public boolean isDrawerVisible(int);
-    method public void onDraw(android.graphics.Canvas);
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void openDrawer(android.view.View);
-    method public void openDrawer(android.view.View, boolean);
-    method public void openDrawer(int);
-    method public void openDrawer(int, boolean);
-    method public void removeDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
-    method public void setDrawerElevation(float);
-    method public deprecated void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener);
-    method public void setDrawerLockMode(int);
-    method public void setDrawerLockMode(int, int);
-    method public void setDrawerLockMode(int, android.view.View);
-    method public void setDrawerShadow(android.graphics.drawable.Drawable, int);
-    method public void setDrawerShadow(int, int);
-    method public void setDrawerTitle(int, java.lang.CharSequence);
-    method public void setScrimColor(int);
-    method public void setStatusBarBackground(android.graphics.drawable.Drawable);
-    method public void setStatusBarBackground(int);
-    method public void setStatusBarBackgroundColor(int);
-    field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1
-    field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2
-    field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3
-    field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0
-    field public static final int STATE_DRAGGING = 1; // 0x1
-    field public static final int STATE_IDLE = 0; // 0x0
-    field public static final int STATE_SETTLING = 2; // 0x2
-  }
-
-  public static abstract interface DrawerLayout.DrawerListener {
-    method public abstract void onDrawerClosed(android.view.View);
-    method public abstract void onDrawerOpened(android.view.View);
-    method public abstract void onDrawerSlide(android.view.View, float);
-    method public abstract void onDrawerStateChanged(int);
-  }
-
-  public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public DrawerLayout.LayoutParams(int, int);
-    ctor public DrawerLayout.LayoutParams(int, int, int);
-    ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams);
-    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    field public int gravity;
-  }
-
-  protected static class DrawerLayout.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public DrawerLayout.SavedState(android.os.Parcel, java.lang.ClassLoader);
-    ctor public DrawerLayout.SavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR;
-  }
-
-  public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
-    ctor public DrawerLayout.SimpleDrawerListener();
-    method public void onDrawerClosed(android.view.View);
-    method public void onDrawerOpened(android.view.View);
-    method public void onDrawerSlide(android.view.View, float);
-    method public void onDrawerStateChanged(int);
-  }
-
-  public final class EdgeEffectCompat {
-    ctor public EdgeEffectCompat(android.content.Context);
-    method public boolean draw(android.graphics.Canvas);
-    method public void finish();
-    method public boolean isFinished();
-    method public boolean onAbsorb(int);
-    method public deprecated boolean onPull(float);
-    method public boolean onPull(float, float);
-    method public boolean onRelease();
-    method public void setSize(int, int);
-  }
-
-  public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
-    ctor public ExploreByTouchHelper(android.view.View);
-    method public final boolean clearKeyboardFocusForVirtualView(int);
-    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
-    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
-    method public final int getAccessibilityFocusedVirtualViewId();
-    method public deprecated int getFocusedVirtualView();
-    method public final int getKeyboardFocusedVirtualViewId();
-    method protected abstract int getVirtualViewAt(float, float);
-    method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
-    method public final void invalidateRoot();
-    method public final void invalidateVirtualView(int);
-    method public final void invalidateVirtualView(int, int);
-    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
-    method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
-    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
-    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
-    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
-    method public final boolean requestKeyboardFocusForVirtualView(int);
-    method public final boolean sendEventForVirtualView(int, int);
-    field public static final int HOST_ID = -1; // 0xffffffff
-    field public static final int INVALID_ID = -2147483648; // 0x80000000
-  }
-
-  public class ImageViewCompat {
-    method public static android.content.res.ColorStateList getImageTintList(android.widget.ImageView);
-    method public static android.graphics.PorterDuff.Mode getImageTintMode(android.widget.ImageView);
-    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList);
-    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode);
-  }
-
-  public final class ListPopupWindowCompat {
-    method public static android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View);
-  }
-
-  public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper {
-    ctor public ListViewAutoScrollHelper(android.widget.ListView);
-    method public boolean canTargetScrollHorizontally(int);
-    method public boolean canTargetScrollVertically(int);
-    method public void scrollTargetBy(int, int);
-  }
-
-  public final class ListViewCompat {
-    method public static void scrollListBy(android.widget.ListView, int);
-  }
-
-  public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView {
-    ctor public NestedScrollView(android.content.Context);
-    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet);
-    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int);
-    method public boolean arrowScroll(int);
-    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect);
-    method public boolean executeKeyEvent(android.view.KeyEvent);
-    method public void fling(int);
-    method public boolean fullScroll(int);
-    method public int getMaxScrollAmount();
-    method public boolean isFillViewport();
-    method public boolean isSmoothScrollingEnabled();
-    method public void onAttachedToWindow();
-    method public boolean pageScroll(int);
-    method public void setFillViewport(boolean);
-    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
-    method public void setSmoothScrollingEnabled(boolean);
-    method public final void smoothScrollBy(int, int);
-    method public final void smoothScrollTo(int, int);
-  }
-
-  public static abstract interface NestedScrollView.OnScrollChangeListener {
-    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
-  }
-
-  public final class PopupMenuCompat {
-    method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
-  }
-
-  public final class PopupWindowCompat {
-    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
-    method public static int getWindowLayoutType(android.widget.PopupWindow);
-    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
-    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
-    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
-  }
-
-  public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter {
-    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor);
-    ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean);
-    ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int);
-    method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup);
-    method public void setDropDownViewResource(int);
-    method public void setViewResource(int);
-  }
-
-  public final class ScrollerCompat {
-    method public void abortAnimation();
-    method public boolean computeScrollOffset();
-    method public static android.support.v4.widget.ScrollerCompat create(android.content.Context);
-    method public static android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator);
-    method public void fling(int, int, int, int, int, int, int, int);
-    method public void fling(int, int, int, int, int, int, int, int, int, int);
-    method public float getCurrVelocity();
-    method public int getCurrX();
-    method public int getCurrY();
-    method public int getFinalX();
-    method public int getFinalY();
-    method public boolean isFinished();
-    method public boolean isOverScrolled();
-    method public void notifyHorizontalEdgeReached(int, int, int);
-    method public void notifyVerticalEdgeReached(int, int, int);
-    method public boolean springBack(int, int, int, int, int, int);
-    method public void startScroll(int, int, int, int);
-    method public void startScroll(int, int, int, int, int);
-  }
-
-  public final class SearchViewCompat {
-    method public static java.lang.CharSequence getQuery(android.view.View);
-    method public static boolean isIconified(android.view.View);
-    method public static boolean isQueryRefinementEnabled(android.view.View);
-    method public static boolean isSubmitButtonEnabled(android.view.View);
-    method public static android.view.View newSearchView(android.content.Context);
-    method public static void setIconified(android.view.View, boolean);
-    method public static void setImeOptions(android.view.View, int);
-    method public static void setInputType(android.view.View, int);
-    method public static void setMaxWidth(android.view.View, int);
-    method public static void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListener);
-    method public static void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListener);
-    method public static void setQuery(android.view.View, java.lang.CharSequence, boolean);
-    method public static void setQueryHint(android.view.View, java.lang.CharSequence);
-    method public static void setQueryRefinementEnabled(android.view.View, boolean);
-    method public static void setSearchableInfo(android.view.View, android.content.ComponentName);
-    method public static void setSubmitButtonEnabled(android.view.View, boolean);
-  }
-
-  public static abstract interface SearchViewCompat.OnCloseListener {
-    method public abstract boolean onClose();
-  }
-
-  public static abstract deprecated class SearchViewCompat.OnCloseListenerCompat implements android.support.v4.widget.SearchViewCompat.OnCloseListener {
-    ctor public SearchViewCompat.OnCloseListenerCompat();
-    method public boolean onClose();
-  }
-
-  public static abstract interface SearchViewCompat.OnQueryTextListener {
-    method public abstract boolean onQueryTextChange(java.lang.String);
-    method public abstract boolean onQueryTextSubmit(java.lang.String);
-  }
-
-  public static abstract deprecated class SearchViewCompat.OnQueryTextListenerCompat implements android.support.v4.widget.SearchViewCompat.OnQueryTextListener {
-    ctor public SearchViewCompat.OnQueryTextListenerCompat();
-    method public boolean onQueryTextChange(java.lang.String);
-    method public boolean onQueryTextSubmit(java.lang.String);
-  }
-
-  public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter {
-    ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]);
-    ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int);
-    method public void bindView(android.view.View, android.content.Context, android.database.Cursor);
-    method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]);
-    method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter();
-    method public int getStringConversionColumn();
-    method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder();
-    method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter);
-    method public void setStringConversionColumn(int);
-    method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder);
-    method public void setViewImage(android.widget.ImageView, java.lang.String);
-    method public void setViewText(android.widget.TextView, java.lang.String);
-  }
-
-  public static abstract interface SimpleCursorAdapter.CursorToStringConverter {
-    method public abstract java.lang.CharSequence convertToString(android.database.Cursor);
-  }
-
-  public static abstract interface SimpleCursorAdapter.ViewBinder {
-    method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int);
-  }
-
-  public class SlidingPaneLayout extends android.view.ViewGroup {
-    ctor public SlidingPaneLayout(android.content.Context);
-    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet);
-    ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int);
-    method protected boolean canScroll(android.view.View, boolean, int, int, int);
-    method public deprecated boolean canSlide();
-    method public boolean closePane();
-    method public int getCoveredFadeColor();
-    method public int getParallaxDistance();
-    method public int getSliderFadeColor();
-    method public boolean isOpen();
-    method public boolean isSlideable();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public boolean openPane();
-    method public void setCoveredFadeColor(int);
-    method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener);
-    method public void setParallaxDistance(int);
-    method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable);
-    method public void setShadowDrawableLeft(android.graphics.drawable.Drawable);
-    method public void setShadowDrawableRight(android.graphics.drawable.Drawable);
-    method public deprecated void setShadowResource(int);
-    method public void setShadowResourceLeft(int);
-    method public void setShadowResourceRight(int);
-    method public void setSliderFadeColor(int);
-    method public deprecated void smoothSlideClosed();
-    method public deprecated void smoothSlideOpen();
-  }
-
-  public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public SlidingPaneLayout.LayoutParams();
-    ctor public SlidingPaneLayout.LayoutParams(int, int);
-    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams);
-    ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    field public float weight;
-  }
-
-  public static abstract interface SlidingPaneLayout.PanelSlideListener {
-    method public abstract void onPanelClosed(android.view.View);
-    method public abstract void onPanelOpened(android.view.View);
-    method public abstract void onPanelSlide(android.view.View, float);
-  }
-
-  public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener {
-    ctor public SlidingPaneLayout.SimplePanelSlideListener();
-    method public void onPanelClosed(android.view.View);
-    method public void onPanelOpened(android.view.View);
-    method public void onPanelSlide(android.view.View, float);
-  }
-
-  public class Space extends android.view.View {
-    ctor public Space(android.content.Context, android.util.AttributeSet, int);
-    ctor public Space(android.content.Context, android.util.AttributeSet);
-    ctor public Space(android.content.Context);
-  }
-
-  public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent {
-    ctor public SwipeRefreshLayout(android.content.Context);
-    ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet);
-    method public boolean canChildScrollUp();
-    method public int getProgressCircleDiameter();
-    method public int getProgressViewEndOffset();
-    method public int getProgressViewStartOffset();
-    method public boolean isRefreshing();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void onMeasure(int, int);
-    method public deprecated void setColorScheme(int...);
-    method public void setColorSchemeColors(int...);
-    method public void setColorSchemeResources(int...);
-    method public void setDistanceToTriggerSync(int);
-    method public void setOnChildScrollUpCallback(android.support.v4.widget.SwipeRefreshLayout.OnChildScrollUpCallback);
-    method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener);
-    method public deprecated void setProgressBackgroundColor(int);
-    method public void setProgressBackgroundColorSchemeColor(int);
-    method public void setProgressBackgroundColorSchemeResource(int);
-    method public void setProgressViewEndTarget(boolean, int);
-    method public void setProgressViewOffset(boolean, int, int);
-    method public void setRefreshing(boolean);
-    method public void setSize(int);
-    field public static final int DEFAULT = 1; // 0x1
-    field public static final int LARGE = 0; // 0x0
-    field protected int mFrom;
-    field protected int mOriginalOffsetTop;
-  }
-
-  public static abstract interface SwipeRefreshLayout.OnChildScrollUpCallback {
-    method public abstract boolean canChildScrollUp(android.support.v4.widget.SwipeRefreshLayout, android.view.View);
-  }
-
-  public static abstract interface SwipeRefreshLayout.OnRefreshListener {
-    method public abstract void onRefresh();
-  }
-
-  public final class TextViewCompat {
-    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
-    method public static int getMaxLines(android.widget.TextView);
-    method public static int getMinLines(android.widget.TextView);
-    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
-    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
-    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
-    method public static void setTextAppearance(android.widget.TextView, int);
-  }
-
-  public abstract interface TintableCompoundButton {
-    method public abstract android.content.res.ColorStateList getSupportButtonTintList();
-    method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode();
-    method public abstract void setSupportButtonTintList(android.content.res.ColorStateList);
-    method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode);
-  }
-
-  public class ViewDragHelper {
-    method public void abort();
-    method protected boolean canScroll(android.view.View, boolean, int, int, int, int);
-    method public void cancel();
-    method public void captureChildView(android.view.View, int);
-    method public boolean checkTouchSlop(int);
-    method public boolean checkTouchSlop(int, int);
-    method public boolean continueSettling(boolean);
-    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback);
-    method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback);
-    method public android.view.View findTopChildUnder(int, int);
-    method public void flingCapturedView(int, int, int, int);
-    method public int getActivePointerId();
-    method public android.view.View getCapturedView();
-    method public int getEdgeSize();
-    method public float getMinVelocity();
-    method public int getTouchSlop();
-    method public int getViewDragState();
-    method public boolean isCapturedViewUnder(int, int);
-    method public boolean isEdgeTouched(int);
-    method public boolean isEdgeTouched(int, int);
-    method public boolean isPointerDown(int);
-    method public boolean isViewUnder(android.view.View, int, int);
-    method public void processTouchEvent(android.view.MotionEvent);
-    method public void setEdgeTrackingEnabled(int);
-    method public void setMinVelocity(float);
-    method public boolean settleCapturedViewAt(int, int);
-    method public boolean shouldInterceptTouchEvent(android.view.MotionEvent);
-    method public boolean smoothSlideViewTo(android.view.View, int, int);
-    field public static final int DIRECTION_ALL = 3; // 0x3
-    field public static final int DIRECTION_HORIZONTAL = 1; // 0x1
-    field public static final int DIRECTION_VERTICAL = 2; // 0x2
-    field public static final int EDGE_ALL = 15; // 0xf
-    field public static final int EDGE_BOTTOM = 8; // 0x8
-    field public static final int EDGE_LEFT = 1; // 0x1
-    field public static final int EDGE_RIGHT = 2; // 0x2
-    field public static final int EDGE_TOP = 4; // 0x4
-    field public static final int INVALID_POINTER = -1; // 0xffffffff
-    field public static final int STATE_DRAGGING = 1; // 0x1
-    field public static final int STATE_IDLE = 0; // 0x0
-    field public static final int STATE_SETTLING = 2; // 0x2
-  }
-
-  public static abstract class ViewDragHelper.Callback {
-    ctor public ViewDragHelper.Callback();
-    method public int clampViewPositionHorizontal(android.view.View, int, int);
-    method public int clampViewPositionVertical(android.view.View, int, int);
-    method public int getOrderedChildIndex(int);
-    method public int getViewHorizontalDragRange(android.view.View);
-    method public int getViewVerticalDragRange(android.view.View);
-    method public void onEdgeDragStarted(int, int);
-    method public boolean onEdgeLock(int);
-    method public void onEdgeTouched(int, int);
-    method public void onViewCaptured(android.view.View, int);
-    method public void onViewDragStateChanged(int);
-    method public void onViewPositionChanged(android.view.View, int, int, int, int);
-    method public void onViewReleased(android.view.View, float, float);
-    method public abstract boolean tryCaptureView(android.view.View, int);
-  }
-
-}
-
-package android.support.v7.app {
-
-  public abstract class ActionBar {
-    ctor public ActionBar();
-    method public abstract void addOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
-    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab);
-    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, boolean);
-    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int);
-    method public abstract deprecated void addTab(android.support.v7.app.ActionBar.Tab, int, boolean);
-    method public abstract android.view.View getCustomView();
-    method public abstract int getDisplayOptions();
-    method public float getElevation();
-    method public abstract int getHeight();
-    method public int getHideOffset();
-    method public abstract deprecated int getNavigationItemCount();
-    method public abstract deprecated int getNavigationMode();
-    method public abstract deprecated int getSelectedNavigationIndex();
-    method public abstract deprecated android.support.v7.app.ActionBar.Tab getSelectedTab();
-    method public abstract java.lang.CharSequence getSubtitle();
-    method public abstract deprecated android.support.v7.app.ActionBar.Tab getTabAt(int);
-    method public abstract deprecated int getTabCount();
-    method public android.content.Context getThemedContext();
-    method public abstract java.lang.CharSequence getTitle();
-    method public abstract void hide();
-    method public boolean isHideOnContentScrollEnabled();
-    method public abstract boolean isShowing();
-    method public abstract deprecated android.support.v7.app.ActionBar.Tab newTab();
-    method public abstract deprecated void removeAllTabs();
-    method public abstract void removeOnMenuVisibilityListener(android.support.v7.app.ActionBar.OnMenuVisibilityListener);
-    method public abstract deprecated void removeTab(android.support.v7.app.ActionBar.Tab);
-    method public abstract deprecated void removeTabAt(int);
-    method public abstract deprecated void selectTab(android.support.v7.app.ActionBar.Tab);
-    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
-    method public abstract void setCustomView(android.view.View);
-    method public abstract void setCustomView(android.view.View, android.support.v7.app.ActionBar.LayoutParams);
-    method public abstract void setCustomView(int);
-    method public abstract void setDisplayHomeAsUpEnabled(boolean);
-    method public abstract void setDisplayOptions(int);
-    method public abstract void setDisplayOptions(int, int);
-    method public abstract void setDisplayShowCustomEnabled(boolean);
-    method public abstract void setDisplayShowHomeEnabled(boolean);
-    method public abstract void setDisplayShowTitleEnabled(boolean);
-    method public abstract void setDisplayUseLogoEnabled(boolean);
-    method public void setElevation(float);
-    method public void setHideOffset(int);
-    method public void setHideOnContentScrollEnabled(boolean);
-    method public void setHomeActionContentDescription(java.lang.CharSequence);
-    method public void setHomeActionContentDescription(int);
-    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
-    method public void setHomeAsUpIndicator(int);
-    method public void setHomeButtonEnabled(boolean);
-    method public abstract void setIcon(int);
-    method public abstract void setIcon(android.graphics.drawable.Drawable);
-    method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.support.v7.app.ActionBar.OnNavigationListener);
-    method public abstract void setLogo(int);
-    method public abstract void setLogo(android.graphics.drawable.Drawable);
-    method public abstract deprecated void setNavigationMode(int);
-    method public abstract deprecated void setSelectedNavigationItem(int);
-    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
-    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
-    method public abstract void setSubtitle(java.lang.CharSequence);
-    method public abstract void setSubtitle(int);
-    method public abstract void setTitle(java.lang.CharSequence);
-    method public abstract void setTitle(int);
-    method public abstract void show();
-    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
-    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
-    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
-    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
-    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
-    field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1
-    field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0
-    field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2
-  }
-
-  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public ActionBar.LayoutParams(int, int);
-    ctor public ActionBar.LayoutParams(int, int, int);
-    ctor public ActionBar.LayoutParams(int);
-    ctor public ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
-    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
-    field public int gravity;
-  }
-
-  public static abstract interface ActionBar.OnMenuVisibilityListener {
-    method public abstract void onMenuVisibilityChanged(boolean);
-  }
-
-  public static abstract deprecated interface ActionBar.OnNavigationListener {
-    method public abstract boolean onNavigationItemSelected(int, long);
-  }
-
-  public static abstract deprecated class ActionBar.Tab {
-    ctor public ActionBar.Tab();
-    method public abstract java.lang.CharSequence getContentDescription();
-    method public abstract android.view.View getCustomView();
-    method public abstract android.graphics.drawable.Drawable getIcon();
-    method public abstract int getPosition();
-    method public abstract java.lang.Object getTag();
-    method public abstract java.lang.CharSequence getText();
-    method public abstract void select();
-    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(int);
-    method public abstract android.support.v7.app.ActionBar.Tab setContentDescription(java.lang.CharSequence);
-    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(android.view.View);
-    method public abstract android.support.v7.app.ActionBar.Tab setCustomView(int);
-    method public abstract android.support.v7.app.ActionBar.Tab setIcon(android.graphics.drawable.Drawable);
-    method public abstract android.support.v7.app.ActionBar.Tab setIcon(int);
-    method public abstract android.support.v7.app.ActionBar.Tab setTabListener(android.support.v7.app.ActionBar.TabListener);
-    method public abstract android.support.v7.app.ActionBar.Tab setTag(java.lang.Object);
-    method public abstract android.support.v7.app.ActionBar.Tab setText(java.lang.CharSequence);
-    method public abstract android.support.v7.app.ActionBar.Tab setText(int);
-    field public static final int INVALID_POSITION = -1; // 0xffffffff
-  }
-
-  public static abstract deprecated interface ActionBar.TabListener {
-    method public abstract void onTabReselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
-    method public abstract void onTabSelected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
-    method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
-  }
-
-  public deprecated class ActionBarActivity extends android.support.v7.app.AppCompatActivity {
-    ctor public ActionBarActivity();
-  }
-
-  public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
-    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
-    ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
-    method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
-    method public android.view.View.OnClickListener getToolbarNavigationClickListener();
-    method public boolean isDrawerIndicatorEnabled();
-    method public boolean isDrawerSlideAnimationEnabled();
-    method public void onConfigurationChanged(android.content.res.Configuration);
-    method public void onDrawerClosed(android.view.View);
-    method public void onDrawerOpened(android.view.View);
-    method public void onDrawerSlide(android.view.View, float);
-    method public void onDrawerStateChanged(int);
-    method public boolean onOptionsItemSelected(android.view.MenuItem);
-    method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
-    method public void setDrawerIndicatorEnabled(boolean);
-    method public void setDrawerSlideAnimationEnabled(boolean);
-    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
-    method public void setHomeAsUpIndicator(int);
-    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
-    method public void syncState();
-  }
-
-  public static abstract interface ActionBarDrawerToggle.Delegate {
-    method public abstract android.content.Context getActionBarThemedContext();
-    method public abstract android.graphics.drawable.Drawable getThemeUpIndicator();
-    method public abstract boolean isNavigationVisible();
-    method public abstract void setActionBarDescription(int);
-    method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int);
-  }
-
-  public static abstract interface ActionBarDrawerToggle.DelegateProvider {
-    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
-  }
-
-  public class AlertDialog extends android.support.v7.app.AppCompatDialog implements android.content.DialogInterface {
-    ctor protected AlertDialog(android.content.Context);
-    ctor protected AlertDialog(android.content.Context, int);
-    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
-    method public android.widget.Button getButton(int);
-    method public android.widget.ListView getListView();
-    method public void setButton(int, java.lang.CharSequence, android.os.Message);
-    method public void setButton(int, java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
-    method public void setCustomTitle(android.view.View);
-    method public void setIcon(int);
-    method public void setIcon(android.graphics.drawable.Drawable);
-    method public void setIconAttribute(int);
-    method public void setMessage(java.lang.CharSequence);
-    method public void setView(android.view.View);
-    method public void setView(android.view.View, int, int, int, int);
-  }
-
-  public static class AlertDialog.Builder {
-    ctor public AlertDialog.Builder(android.content.Context);
-    ctor public AlertDialog.Builder(android.content.Context, int);
-    method public android.support.v7.app.AlertDialog create();
-    method public android.content.Context getContext();
-    method public android.support.v7.app.AlertDialog.Builder setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setCancelable(boolean);
-    method public android.support.v7.app.AlertDialog.Builder setCursor(android.database.Cursor, android.content.DialogInterface.OnClickListener, java.lang.String);
-    method public android.support.v7.app.AlertDialog.Builder setCustomTitle(android.view.View);
-    method public android.support.v7.app.AlertDialog.Builder setIcon(int);
-    method public android.support.v7.app.AlertDialog.Builder setIcon(android.graphics.drawable.Drawable);
-    method public android.support.v7.app.AlertDialog.Builder setIconAttribute(int);
-    method public deprecated android.support.v7.app.AlertDialog.Builder setInverseBackgroundForced(boolean);
-    method public android.support.v7.app.AlertDialog.Builder setItems(int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setItems(java.lang.CharSequence[], android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setMessage(int);
-    method public android.support.v7.app.AlertDialog.Builder setMessage(java.lang.CharSequence);
-    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(int, boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(java.lang.CharSequence[], boolean[], android.content.DialogInterface.OnMultiChoiceClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setMultiChoiceItems(android.database.Cursor, java.lang.String, java.lang.String, android.content.DialogInterface.OnMultiChoiceClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
-    method public android.support.v7.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
-    method public android.support.v7.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
-    method public android.support.v7.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
-    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(int, int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.database.Cursor, int, java.lang.String, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(java.lang.CharSequence[], int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setSingleChoiceItems(android.widget.ListAdapter, int, android.content.DialogInterface.OnClickListener);
-    method public android.support.v7.app.AlertDialog.Builder setTitle(int);
-    method public android.support.v7.app.AlertDialog.Builder setTitle(java.lang.CharSequence);
-    method public android.support.v7.app.AlertDialog.Builder setView(int);
-    method public android.support.v7.app.AlertDialog.Builder setView(android.view.View);
-    method public android.support.v7.app.AlertDialog show();
-  }
-
-  public class AppCompatActivity extends android.support.v4.app.FragmentActivity implements android.support.v7.app.ActionBarDrawerToggle.DelegateProvider android.support.v7.app.AppCompatCallback android.support.v4.app.TaskStackBuilder.SupportParentable {
-    ctor public AppCompatActivity();
-    method public android.support.v7.app.AppCompatDelegate getDelegate();
-    method public android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
-    method public android.support.v7.app.ActionBar getSupportActionBar();
-    method public android.content.Intent getSupportParentActivityIntent();
-    method public void onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
-    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
-    method public void onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder);
-    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
-    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
-    method public deprecated void onSupportContentChanged();
-    method public boolean onSupportNavigateUp();
-    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
-    method public void setSupportActionBar(android.support.v7.widget.Toolbar);
-    method public deprecated void setSupportProgress(int);
-    method public deprecated void setSupportProgressBarIndeterminate(boolean);
-    method public deprecated void setSupportProgressBarIndeterminateVisibility(boolean);
-    method public deprecated void setSupportProgressBarVisibility(boolean);
-    method public android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
-    method public void supportNavigateUpTo(android.content.Intent);
-    method public boolean supportRequestWindowFeature(int);
-    method public boolean supportShouldUpRecreateTask(android.content.Intent);
-  }
-
-  public abstract interface AppCompatCallback {
-    method public abstract void onSupportActionModeFinished(android.support.v7.view.ActionMode);
-    method public abstract void onSupportActionModeStarted(android.support.v7.view.ActionMode);
-    method public abstract android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
-  }
-
-  public abstract class AppCompatDelegate {
-    method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
-    method public abstract boolean applyDayNight();
-    method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
-    method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
-    method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
-    method public abstract android.view.View findViewById(int);
-    method public static int getDefaultNightMode();
-    method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
-    method public abstract android.view.MenuInflater getMenuInflater();
-    method public abstract android.support.v7.app.ActionBar getSupportActionBar();
-    method public abstract boolean hasWindowFeature(int);
-    method public abstract void installViewFactory();
-    method public abstract void invalidateOptionsMenu();
-    method public static boolean isCompatVectorFromResourcesEnabled();
-    method public abstract boolean isHandleNativeActionModesEnabled();
-    method public abstract void onConfigurationChanged(android.content.res.Configuration);
-    method public abstract void onCreate(android.os.Bundle);
-    method public abstract void onDestroy();
-    method public abstract void onPostCreate(android.os.Bundle);
-    method public abstract void onPostResume();
-    method public abstract void onSaveInstanceState(android.os.Bundle);
-    method public abstract void onStart();
-    method public abstract void onStop();
-    method public abstract boolean requestWindowFeature(int);
-    method public static void setCompatVectorFromResourcesEnabled(boolean);
-    method public abstract void setContentView(android.view.View);
-    method public abstract void setContentView(int);
-    method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
-    method public static void setDefaultNightMode(int);
-    method public abstract void setHandleNativeActionModesEnabled(boolean);
-    method public abstract void setLocalNightMode(int);
-    method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
-    method public abstract void setTitle(java.lang.CharSequence);
-    method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
-    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
-    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
-    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
-    field public static final int MODE_NIGHT_AUTO = 0; // 0x0
-    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
-    field public static final int MODE_NIGHT_NO = 1; // 0x1
-    field public static final int MODE_NIGHT_YES = 2; // 0x2
-  }
-
-  public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
-    ctor public AppCompatDialog(android.content.Context);
-    ctor public AppCompatDialog(android.content.Context, int);
-    ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener);
-    method public android.support.v7.app.AppCompatDelegate getDelegate();
-    method public android.support.v7.app.ActionBar getSupportActionBar();
-    method public void onSupportActionModeFinished(android.support.v7.view.ActionMode);
-    method public void onSupportActionModeStarted(android.support.v7.view.ActionMode);
-    method public android.support.v7.view.ActionMode onWindowStartingSupportActionMode(android.support.v7.view.ActionMode.Callback);
-    method public boolean supportRequestWindowFeature(int);
-  }
-
-  public class AppCompatDialogFragment extends android.support.v4.app.DialogFragment {
-    ctor public AppCompatDialogFragment();
-  }
-
-  public class MediaRouteActionProvider extends android.support.v4.view.ActionProvider {
-    ctor public MediaRouteActionProvider(android.content.Context);
-    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
-    method public android.support.v7.app.MediaRouteButton getMediaRouteButton();
-    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
-    method public android.view.View onCreateActionView();
-    method public android.support.v7.app.MediaRouteButton onCreateMediaRouteButton();
-    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
-    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
-  }
-
-  public class MediaRouteButton extends android.view.View {
-    ctor public MediaRouteButton(android.content.Context);
-    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
-    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
-    method public android.support.v7.app.MediaRouteDialogFactory getDialogFactory();
-    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
-    method public void onAttachedToWindow();
-    method public void onDetachedFromWindow();
-    method public void setDialogFactory(android.support.v7.app.MediaRouteDialogFactory);
-    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable);
-    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
-    method public boolean showDialog();
-  }
-
-  public class MediaRouteChooserDialog extends android.support.v7.app.AppCompatDialog {
-    ctor public MediaRouteChooserDialog(android.content.Context);
-    ctor public MediaRouteChooserDialog(android.content.Context, int);
-    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
-    method public boolean onFilterRoute(android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onFilterRoutes(java.util.List<android.support.v7.media.MediaRouter.RouteInfo>);
-    method public void refreshRoutes();
-    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
-  }
-
-  public class MediaRouteChooserDialogFragment extends android.support.v4.app.DialogFragment {
-    ctor public MediaRouteChooserDialogFragment();
-    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
-    method public android.support.v7.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle);
-    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
-  }
-
-  public class MediaRouteControllerDialog extends android.support.v7.app.AlertDialog {
-    ctor public MediaRouteControllerDialog(android.content.Context);
-    ctor public MediaRouteControllerDialog(android.content.Context, int);
-    method public android.view.View getMediaControlView();
-    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSession();
-    method public android.support.v7.media.MediaRouter.RouteInfo getRoute();
-    method public boolean isVolumeControlEnabled();
-    method public android.view.View onCreateMediaControlView(android.os.Bundle);
-    method public void setVolumeControlEnabled(boolean);
-  }
-
-  public class MediaRouteControllerDialogFragment extends android.support.v4.app.DialogFragment {
-    ctor public MediaRouteControllerDialogFragment();
-    method public android.support.v7.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle);
-  }
-
-  public class MediaRouteDialogFactory {
-    ctor public MediaRouteDialogFactory();
-    method public static android.support.v7.app.MediaRouteDialogFactory getDefault();
-    method public android.support.v7.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
-    method public android.support.v7.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
-  }
-
-  public class MediaRouteDiscoveryFragment extends android.support.v4.app.Fragment {
-    ctor public MediaRouteDiscoveryFragment();
-    method public android.support.v7.media.MediaRouter getMediaRouter();
-    method public android.support.v7.media.MediaRouteSelector getRouteSelector();
-    method public android.support.v7.media.MediaRouter.Callback onCreateCallback();
-    method public int onPrepareCallbackFlags();
-    method public void setRouteSelector(android.support.v7.media.MediaRouteSelector);
-  }
-
-  public class NotificationCompat extends android.support.v4.app.NotificationCompat {
-    ctor public NotificationCompat();
-    method public static android.support.v4.media.session.MediaSessionCompat.Token getMediaSession(android.app.Notification);
-  }
-
-  public static class NotificationCompat.Builder extends android.support.v4.app.NotificationCompat.Builder {
-    ctor public NotificationCompat.Builder(android.content.Context);
-  }
-
-  public static class NotificationCompat.DecoratedCustomViewStyle extends android.support.v4.app.NotificationCompat.Style {
-    ctor public NotificationCompat.DecoratedCustomViewStyle();
-  }
-
-  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends android.support.v7.app.NotificationCompat.MediaStyle {
-    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
-  }
-
-  public static class NotificationCompat.MediaStyle extends android.support.v4.app.NotificationCompat.Style {
-    ctor public NotificationCompat.MediaStyle();
-    ctor public NotificationCompat.MediaStyle(android.support.v4.app.NotificationCompat.Builder);
-    method public android.support.v7.app.NotificationCompat.MediaStyle setCancelButtonIntent(android.app.PendingIntent);
-    method public android.support.v7.app.NotificationCompat.MediaStyle setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token);
-    method public android.support.v7.app.NotificationCompat.MediaStyle setShowActionsInCompactView(int...);
-    method public android.support.v7.app.NotificationCompat.MediaStyle setShowCancelButton(boolean);
-  }
-
-}
-
-package android.support.v7.content.res {
-
-  public final class AppCompatResources {
-    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
-    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
-  }
-
-}
-
-package android.support.v7.graphics {
-
-  public final class Palette {
-    method public static android.support.v7.graphics.Palette.Builder from(android.graphics.Bitmap);
-    method public static android.support.v7.graphics.Palette from(java.util.List<android.support.v7.graphics.Palette.Swatch>);
-    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap);
-    method public static deprecated android.support.v7.graphics.Palette generate(android.graphics.Bitmap, int);
-    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, android.support.v7.graphics.Palette.PaletteAsyncListener);
-    method public static deprecated android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generateAsync(android.graphics.Bitmap, int, android.support.v7.graphics.Palette.PaletteAsyncListener);
-    method public int getColorForTarget(android.support.v7.graphics.Target, int);
-    method public int getDarkMutedColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
-    method public int getDarkVibrantColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
-    method public int getDominantColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getDominantSwatch();
-    method public int getLightMutedColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
-    method public int getLightVibrantColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
-    method public int getMutedColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getSwatchForTarget(android.support.v7.graphics.Target);
-    method public java.util.List<android.support.v7.graphics.Palette.Swatch> getSwatches();
-    method public java.util.List<android.support.v7.graphics.Target> getTargets();
-    method public int getVibrantColor(int);
-    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
-  }
-
-  public static final class Palette.Builder {
-    ctor public Palette.Builder(android.graphics.Bitmap);
-    ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
-    method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
-    method public android.support.v7.graphics.Palette.Builder addTarget(android.support.v7.graphics.Target);
-    method public android.support.v7.graphics.Palette.Builder clearFilters();
-    method public android.support.v7.graphics.Palette.Builder clearRegion();
-    method public android.support.v7.graphics.Palette.Builder clearTargets();
-    method public android.support.v7.graphics.Palette generate();
-    method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
-    method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
-    method public android.support.v7.graphics.Palette.Builder resizeBitmapArea(int);
-    method public deprecated android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
-    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
-  }
-
-  public static abstract interface Palette.Filter {
-    method public abstract boolean isAllowed(int, float[]);
-  }
-
-  public static abstract interface Palette.PaletteAsyncListener {
-    method public abstract void onGenerated(android.support.v7.graphics.Palette);
-  }
-
-  public static final class Palette.Swatch {
-    ctor public Palette.Swatch(int, int);
-    method public int getBodyTextColor();
-    method public float[] getHsl();
-    method public int getPopulation();
-    method public int getRgb();
-    method public int getTitleTextColor();
-  }
-
-  public final class Target {
-    method public float getLightnessWeight();
-    method public float getMaximumLightness();
-    method public float getMaximumSaturation();
-    method public float getMinimumLightness();
-    method public float getMinimumSaturation();
-    method public float getPopulationWeight();
-    method public float getSaturationWeight();
-    method public float getTargetLightness();
-    method public float getTargetSaturation();
-    method public boolean isExclusive();
-    field public static final android.support.v7.graphics.Target DARK_MUTED;
-    field public static final android.support.v7.graphics.Target DARK_VIBRANT;
-    field public static final android.support.v7.graphics.Target LIGHT_MUTED;
-    field public static final android.support.v7.graphics.Target LIGHT_VIBRANT;
-    field public static final android.support.v7.graphics.Target MUTED;
-    field public static final android.support.v7.graphics.Target VIBRANT;
-  }
-
-  public static final class Target.Builder {
-    ctor public Target.Builder();
-    ctor public Target.Builder(android.support.v7.graphics.Target);
-    method public android.support.v7.graphics.Target build();
-    method public android.support.v7.graphics.Target.Builder setExclusive(boolean);
-    method public android.support.v7.graphics.Target.Builder setLightnessWeight(float);
-    method public android.support.v7.graphics.Target.Builder setMaximumLightness(float);
-    method public android.support.v7.graphics.Target.Builder setMaximumSaturation(float);
-    method public android.support.v7.graphics.Target.Builder setMinimumLightness(float);
-    method public android.support.v7.graphics.Target.Builder setMinimumSaturation(float);
-    method public android.support.v7.graphics.Target.Builder setPopulationWeight(float);
-    method public android.support.v7.graphics.Target.Builder setSaturationWeight(float);
-    method public android.support.v7.graphics.Target.Builder setTargetLightness(float);
-    method public android.support.v7.graphics.Target.Builder setTargetSaturation(float);
-  }
-
-}
-
-package android.support.v7.graphics.drawable {
-
-  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
-    ctor public DrawerArrowDrawable(android.content.Context);
-    method public void draw(android.graphics.Canvas);
-    method public float getArrowHeadLength();
-    method public float getArrowShaftLength();
-    method public float getBarLength();
-    method public float getBarThickness();
-    method public int getColor();
-    method public int getDirection();
-    method public float getGapSize();
-    method public int getOpacity();
-    method public final android.graphics.Paint getPaint();
-    method public float getProgress();
-    method public boolean isSpinEnabled();
-    method public void setAlpha(int);
-    method public void setArrowHeadLength(float);
-    method public void setArrowShaftLength(float);
-    method public void setBarLength(float);
-    method public void setBarThickness(float);
-    method public void setColor(int);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void setDirection(int);
-    method public void setGapSize(float);
-    method public void setProgress(float);
-    method public void setSpinEnabled(boolean);
-    method public void setVerticalMirror(boolean);
-    field public static final int ARROW_DIRECTION_END = 3; // 0x3
-    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
-    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
-    field public static final int ARROW_DIRECTION_START = 2; // 0x2
-  }
-
-}
-
-package android.support.v7.media {
-
-  public final class MediaControlIntent {
-    field public static final java.lang.String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
-    field public static final java.lang.String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
-    field public static final java.lang.String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
-    field public static final java.lang.String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
-    field public static final java.lang.String ACTION_PAUSE = "android.media.intent.action.PAUSE";
-    field public static final java.lang.String ACTION_PLAY = "android.media.intent.action.PLAY";
-    field public static final java.lang.String ACTION_REMOVE = "android.media.intent.action.REMOVE";
-    field public static final java.lang.String ACTION_RESUME = "android.media.intent.action.RESUME";
-    field public static final java.lang.String ACTION_SEEK = "android.media.intent.action.SEEK";
-    field public static final java.lang.String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
-    field public static final java.lang.String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
-    field public static final java.lang.String ACTION_STOP = "android.media.intent.action.STOP";
-    field public static final java.lang.String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
-    field public static final java.lang.String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
-    field public static final java.lang.String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
-    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
-    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
-    field public static final int ERROR_UNKNOWN = 0; // 0x0
-    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
-    field public static final java.lang.String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
-    field public static final java.lang.String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
-    field public static final java.lang.String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
-    field public static final java.lang.String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
-    field public static final java.lang.String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
-    field public static final java.lang.String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
-    field public static final java.lang.String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
-    field public static final java.lang.String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
-    field public static final java.lang.String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
-    field public static final java.lang.String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
-    field public static final java.lang.String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
-    field public static final java.lang.String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
-  }
-
-  public final class MediaItemMetadata {
-    field public static final java.lang.String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-    field public static final java.lang.String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
-    field public static final java.lang.String KEY_ARTIST = "android.media.metadata.ARTIST";
-    field public static final java.lang.String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
-    field public static final java.lang.String KEY_AUTHOR = "android.media.metadata.AUTHOR";
-    field public static final java.lang.String KEY_COMPOSER = "android.media.metadata.COMPOSER";
-    field public static final java.lang.String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-    field public static final java.lang.String KEY_DURATION = "android.media.metadata.DURATION";
-    field public static final java.lang.String KEY_TITLE = "android.media.metadata.TITLE";
-    field public static final java.lang.String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-    field public static final java.lang.String KEY_YEAR = "android.media.metadata.YEAR";
-  }
-
-  public final class MediaItemStatus {
-    method public android.os.Bundle asBundle();
-    method public static android.support.v7.media.MediaItemStatus fromBundle(android.os.Bundle);
-    method public long getContentDuration();
-    method public long getContentPosition();
-    method public android.os.Bundle getExtras();
-    method public int getPlaybackState();
-    method public long getTimestamp();
-    field public static final java.lang.String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
-    field public static final java.lang.String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
-    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
-    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
-    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
-    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
-    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
-    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
-    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
-    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
-  }
-
-  public static final class MediaItemStatus.Builder {
-    ctor public MediaItemStatus.Builder(int);
-    ctor public MediaItemStatus.Builder(android.support.v7.media.MediaItemStatus);
-    method public android.support.v7.media.MediaItemStatus build();
-    method public android.support.v7.media.MediaItemStatus.Builder setContentDuration(long);
-    method public android.support.v7.media.MediaItemStatus.Builder setContentPosition(long);
-    method public android.support.v7.media.MediaItemStatus.Builder setExtras(android.os.Bundle);
-    method public android.support.v7.media.MediaItemStatus.Builder setPlaybackState(int);
-    method public android.support.v7.media.MediaItemStatus.Builder setTimestamp(long);
-  }
-
-  public final class MediaRouteDescriptor {
-    method public android.os.Bundle asBundle();
-    method public boolean canDisconnectAndKeepPlaying();
-    method public static android.support.v7.media.MediaRouteDescriptor fromBundle(android.os.Bundle);
-    method public int getConnectionState();
-    method public java.util.List<android.content.IntentFilter> getControlFilters();
-    method public java.lang.String getDescription();
-    method public int getDeviceType();
-    method public android.os.Bundle getExtras();
-    method public android.net.Uri getIconUri();
-    method public java.lang.String getId();
-    method public java.lang.String getName();
-    method public int getPlaybackStream();
-    method public int getPlaybackType();
-    method public int getPresentationDisplayId();
-    method public android.content.IntentSender getSettingsActivity();
-    method public int getVolume();
-    method public int getVolumeHandling();
-    method public int getVolumeMax();
-    method public deprecated boolean isConnecting();
-    method public boolean isEnabled();
-    method public boolean isValid();
-  }
-
-  public static final class MediaRouteDescriptor.Builder {
-    ctor public MediaRouteDescriptor.Builder(java.lang.String, java.lang.String);
-    ctor public MediaRouteDescriptor.Builder(android.support.v7.media.MediaRouteDescriptor);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter>);
-    method public android.support.v7.media.MediaRouteDescriptor build();
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
-    method public deprecated android.support.v7.media.MediaRouteDescriptor.Builder setConnecting(boolean);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setConnectionState(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setDescription(java.lang.String);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setDeviceType(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setEnabled(boolean);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setId(java.lang.String);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setName(java.lang.String);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setPlaybackType(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolume(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
-    method public android.support.v7.media.MediaRouteDescriptor.Builder setVolumeMax(int);
-  }
-
-  public final class MediaRouteDiscoveryRequest {
-    ctor public MediaRouteDiscoveryRequest(android.support.v7.media.MediaRouteSelector, boolean);
-    method public android.os.Bundle asBundle();
-    method public static android.support.v7.media.MediaRouteDiscoveryRequest fromBundle(android.os.Bundle);
-    method public android.support.v7.media.MediaRouteSelector getSelector();
-    method public boolean isActiveScan();
-    method public boolean isValid();
-  }
-
-  public abstract class MediaRouteProvider {
-    ctor public MediaRouteProvider(android.content.Context);
-    method public final android.content.Context getContext();
-    method public final android.support.v7.media.MediaRouteProviderDescriptor getDescriptor();
-    method public final android.support.v7.media.MediaRouteDiscoveryRequest getDiscoveryRequest();
-    method public final android.os.Handler getHandler();
-    method public final android.support.v7.media.MediaRouteProvider.ProviderMetadata getMetadata();
-    method public android.support.v7.media.MediaRouteProvider.RouteController onCreateRouteController(java.lang.String);
-    method public void onDiscoveryRequestChanged(android.support.v7.media.MediaRouteDiscoveryRequest);
-    method public final void setCallback(android.support.v7.media.MediaRouteProvider.Callback);
-    method public final void setDescriptor(android.support.v7.media.MediaRouteProviderDescriptor);
-    method public final void setDiscoveryRequest(android.support.v7.media.MediaRouteDiscoveryRequest);
-  }
-
-  public static abstract class MediaRouteProvider.Callback {
-    ctor public MediaRouteProvider.Callback();
-    method public void onDescriptorChanged(android.support.v7.media.MediaRouteProvider, android.support.v7.media.MediaRouteProviderDescriptor);
-  }
-
-  public static final class MediaRouteProvider.ProviderMetadata {
-    method public android.content.ComponentName getComponentName();
-    method public java.lang.String getPackageName();
-  }
-
-  public static abstract class MediaRouteProvider.RouteController {
-    ctor public MediaRouteProvider.RouteController();
-    method public boolean onControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
-    method public void onRelease();
-    method public void onSelect();
-    method public void onSetVolume(int);
-    method public void onUnselect();
-    method public void onUnselect(int);
-    method public void onUpdateVolume(int);
-  }
-
-  public final class MediaRouteProviderDescriptor {
-    method public android.os.Bundle asBundle();
-    method public static android.support.v7.media.MediaRouteProviderDescriptor fromBundle(android.os.Bundle);
-    method public java.util.List<android.support.v7.media.MediaRouteDescriptor> getRoutes();
-    method public boolean isValid();
-  }
-
-  public static final class MediaRouteProviderDescriptor.Builder {
-    ctor public MediaRouteProviderDescriptor.Builder();
-    ctor public MediaRouteProviderDescriptor.Builder(android.support.v7.media.MediaRouteProviderDescriptor);
-    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoute(android.support.v7.media.MediaRouteDescriptor);
-    method public android.support.v7.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<android.support.v7.media.MediaRouteDescriptor>);
-    method public android.support.v7.media.MediaRouteProviderDescriptor build();
-  }
-
-  public abstract class MediaRouteProviderService extends android.app.Service {
-    ctor public MediaRouteProviderService();
-    method public android.support.v7.media.MediaRouteProvider getMediaRouteProvider();
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public abstract android.support.v7.media.MediaRouteProvider onCreateMediaRouteProvider();
-    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
-  }
-
-  public final class MediaRouteSelector {
-    method public android.os.Bundle asBundle();
-    method public boolean contains(android.support.v7.media.MediaRouteSelector);
-    method public static android.support.v7.media.MediaRouteSelector fromBundle(android.os.Bundle);
-    method public java.util.List<java.lang.String> getControlCategories();
-    method public boolean hasControlCategory(java.lang.String);
-    method public boolean isEmpty();
-    method public boolean isValid();
-    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter>);
-    field public static final android.support.v7.media.MediaRouteSelector EMPTY;
-  }
-
-  public static final class MediaRouteSelector.Builder {
-    ctor public MediaRouteSelector.Builder();
-    ctor public MediaRouteSelector.Builder(android.support.v7.media.MediaRouteSelector);
-    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String>);
-    method public android.support.v7.media.MediaRouteSelector.Builder addControlCategory(java.lang.String);
-    method public android.support.v7.media.MediaRouteSelector.Builder addSelector(android.support.v7.media.MediaRouteSelector);
-    method public android.support.v7.media.MediaRouteSelector build();
-  }
-
-  public final class MediaRouter {
-    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback);
-    method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
-    method public void addProvider(android.support.v7.media.MediaRouteProvider);
-    method public void addRemoteControlClient(java.lang.Object);
-    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
-    method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
-    method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
-    method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
-    method public java.util.List<android.support.v7.media.MediaRouter.ProviderInfo> getProviders();
-    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
-    method public android.support.v7.media.MediaRouter.RouteInfo getSelectedRoute();
-    method public boolean isRouteAvailable(android.support.v7.media.MediaRouteSelector, int);
-    method public void removeCallback(android.support.v7.media.MediaRouter.Callback);
-    method public void removeProvider(android.support.v7.media.MediaRouteProvider);
-    method public void removeRemoteControlClient(java.lang.Object);
-    method public void selectRoute(android.support.v7.media.MediaRouter.RouteInfo);
-    method public void setMediaSession(java.lang.Object);
-    method public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat);
-    method public void unselect(int);
-    method public android.support.v7.media.MediaRouter.RouteInfo updateSelectedRoute(android.support.v7.media.MediaRouteSelector);
-    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
-    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
-    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
-    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
-    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
-    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
-    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
-    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
-    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
-    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
-  }
-
-  public static abstract class MediaRouter.Callback {
-    ctor public MediaRouter.Callback();
-    method public void onProviderAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
-    method public void onProviderChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
-    method public void onProviderRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.ProviderInfo);
-    method public void onRouteAdded(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onRouteChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onRoutePresentationDisplayChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
-    method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
-  }
-
-  public static abstract class MediaRouter.ControlRequestCallback {
-    ctor public MediaRouter.ControlRequestCallback();
-    method public void onError(java.lang.String, android.os.Bundle);
-    method public void onResult(android.os.Bundle);
-  }
-
-  public static final class MediaRouter.ProviderInfo {
-    method public android.content.ComponentName getComponentName();
-    method public java.lang.String getPackageName();
-    method public android.support.v7.media.MediaRouteProvider getProviderInstance();
-    method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
-  }
-
-  public static class MediaRouter.RouteInfo {
-    method public boolean canDisconnect();
-    method public int getConnectionState();
-    method public java.util.List<android.content.IntentFilter> getControlFilters();
-    method public java.lang.String getDescription();
-    method public int getDeviceType();
-    method public android.os.Bundle getExtras();
-    method public android.net.Uri getIconUri();
-    method public java.lang.String getId();
-    method public java.lang.String getName();
-    method public int getPlaybackStream();
-    method public int getPlaybackType();
-    method public android.view.Display getPresentationDisplay();
-    method public android.support.v7.media.MediaRouter.ProviderInfo getProvider();
-    method public android.content.IntentSender getSettingsIntent();
-    method public int getVolume();
-    method public int getVolumeHandling();
-    method public int getVolumeMax();
-    method public boolean isBluetooth();
-    method public boolean isConnecting();
-    method public boolean isDefault();
-    method public boolean isDeviceSpeaker();
-    method public boolean isEnabled();
-    method public boolean isSelected();
-    method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
-    method public void requestSetVolume(int);
-    method public void requestUpdateVolume(int);
-    method public void select();
-    method public void sendControlRequest(android.content.Intent, android.support.v7.media.MediaRouter.ControlRequestCallback);
-    method public boolean supportsControlAction(java.lang.String, java.lang.String);
-    method public boolean supportsControlCategory(java.lang.String);
-    method public boolean supportsControlRequest(android.content.Intent);
-    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
-    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
-    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
-    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
-    field public static final int DEVICE_TYPE_TV = 1; // 0x1
-    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
-    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
-    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
-    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
-  }
-
-  public final class MediaSessionStatus {
-    method public android.os.Bundle asBundle();
-    method public static android.support.v7.media.MediaSessionStatus fromBundle(android.os.Bundle);
-    method public android.os.Bundle getExtras();
-    method public int getSessionState();
-    method public long getTimestamp();
-    method public boolean isQueuePaused();
-    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
-    field public static final int SESSION_STATE_ENDED = 1; // 0x1
-    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
-  }
-
-  public static final class MediaSessionStatus.Builder {
-    ctor public MediaSessionStatus.Builder(int);
-    ctor public MediaSessionStatus.Builder(android.support.v7.media.MediaSessionStatus);
-    method public android.support.v7.media.MediaSessionStatus build();
-    method public android.support.v7.media.MediaSessionStatus.Builder setExtras(android.os.Bundle);
-    method public android.support.v7.media.MediaSessionStatus.Builder setQueuePaused(boolean);
-    method public android.support.v7.media.MediaSessionStatus.Builder setSessionState(int);
-    method public android.support.v7.media.MediaSessionStatus.Builder setTimestamp(long);
-  }
-
-  public class RemotePlaybackClient {
-    ctor public RemotePlaybackClient(android.content.Context, android.support.v7.media.MediaRouter.RouteInfo);
-    method public void endSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-    method public void enqueue(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
-    method public java.lang.String getSessionId();
-    method public void getSessionStatus(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-    method public void getStatus(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
-    method public boolean hasSession();
-    method public boolean isMessagingSupported();
-    method public boolean isQueuingSupported();
-    method public boolean isRemotePlaybackSupported();
-    method public boolean isSessionManagementSupported();
-    method public void pause(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-    method public void play(android.net.Uri, java.lang.String, android.os.Bundle, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
-    method public void release();
-    method public void remove(java.lang.String, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
-    method public void resume(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-    method public void seek(java.lang.String, long, android.os.Bundle, android.support.v7.media.RemotePlaybackClient.ItemActionCallback);
-    method public void sendMessage(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-    method public void setOnMessageReceivedListener(android.support.v7.media.RemotePlaybackClient.OnMessageReceivedListener);
-    method public void setSessionId(java.lang.String);
-    method public void setStatusCallback(android.support.v7.media.RemotePlaybackClient.StatusCallback);
-    method public void startSession(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-    method public void stop(android.os.Bundle, android.support.v7.media.RemotePlaybackClient.SessionActionCallback);
-  }
-
-  public static abstract class RemotePlaybackClient.ActionCallback {
-    ctor public RemotePlaybackClient.ActionCallback();
-    method public void onError(java.lang.String, int, android.os.Bundle);
-  }
-
-  public static abstract class RemotePlaybackClient.ItemActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
-    ctor public RemotePlaybackClient.ItemActionCallback();
-    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
-  }
-
-  public static abstract interface RemotePlaybackClient.OnMessageReceivedListener {
-    method public abstract void onMessageReceived(java.lang.String, android.os.Bundle);
-  }
-
-  public static abstract class RemotePlaybackClient.SessionActionCallback extends android.support.v7.media.RemotePlaybackClient.ActionCallback {
-    ctor public RemotePlaybackClient.SessionActionCallback();
-    method public void onResult(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
-  }
-
-  public static abstract class RemotePlaybackClient.StatusCallback {
-    ctor public RemotePlaybackClient.StatusCallback();
-    method public void onItemStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus, java.lang.String, android.support.v7.media.MediaItemStatus);
-    method public void onSessionChanged(java.lang.String);
-    method public void onSessionStatusChanged(android.os.Bundle, java.lang.String, android.support.v7.media.MediaSessionStatus);
-  }
-
-}
-
-package android.support.v7.preference {
-
-  public class CheckBoxPreference extends android.support.v7.preference.TwoStatePreference {
-    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
-    ctor public CheckBoxPreference(android.content.Context);
-  }
-
-  public abstract class DialogPreference extends android.support.v7.preference.Preference {
-    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
-    ctor public DialogPreference(android.content.Context);
-    method public android.graphics.drawable.Drawable getDialogIcon();
-    method public int getDialogLayoutResource();
-    method public java.lang.CharSequence getDialogMessage();
-    method public java.lang.CharSequence getDialogTitle();
-    method public java.lang.CharSequence getNegativeButtonText();
-    method public java.lang.CharSequence getPositiveButtonText();
-    method public void setDialogIcon(android.graphics.drawable.Drawable);
-    method public void setDialogIcon(int);
-    method public void setDialogLayoutResource(int);
-    method public void setDialogMessage(java.lang.CharSequence);
-    method public void setDialogMessage(int);
-    method public void setDialogTitle(java.lang.CharSequence);
-    method public void setDialogTitle(int);
-    method public void setNegativeButtonText(java.lang.CharSequence);
-    method public void setNegativeButtonText(int);
-    method public void setPositiveButtonText(java.lang.CharSequence);
-    method public void setPositiveButtonText(int);
-  }
-
-  public static abstract interface DialogPreference.TargetFragment {
-    method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
-  }
-
-  public class DropDownPreference extends android.support.v7.preference.ListPreference {
-    ctor public DropDownPreference(android.content.Context);
-    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
-    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
-    method protected android.widget.ArrayAdapter createAdapter();
-  }
-
-  public class EditTextPreference extends android.support.v7.preference.DialogPreference {
-    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
-    ctor public EditTextPreference(android.content.Context);
-    method public java.lang.String getText();
-    method public void setText(java.lang.String);
-  }
-
-  public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
-    ctor public EditTextPreferenceDialogFragmentCompat();
-    method public static android.support.v7.preference.EditTextPreferenceDialogFragmentCompat newInstance(java.lang.String);
-    method public void onDialogClosed(boolean);
-  }
-
-  public class ListPreference extends android.support.v7.preference.DialogPreference {
-    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public ListPreference(android.content.Context, android.util.AttributeSet);
-    ctor public ListPreference(android.content.Context);
-    method public int findIndexOfValue(java.lang.String);
-    method public java.lang.CharSequence[] getEntries();
-    method public java.lang.CharSequence getEntry();
-    method public java.lang.CharSequence[] getEntryValues();
-    method public java.lang.String getValue();
-    method public void setEntries(java.lang.CharSequence[]);
-    method public void setEntries(int);
-    method public void setEntryValues(java.lang.CharSequence[]);
-    method public void setEntryValues(int);
-    method public void setValue(java.lang.String);
-    method public void setValueIndex(int);
-  }
-
-  public class ListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
-    ctor public ListPreferenceDialogFragmentCompat();
-    method public static android.support.v7.preference.ListPreferenceDialogFragmentCompat newInstance(java.lang.String);
-    method public void onDialogClosed(boolean);
-  }
-
-  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
-    ctor public MultiSelectListPreferenceDialogFragmentCompat();
-    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
-    method public void onDialogClosed(boolean);
-  }
-
-  public class Preference implements java.lang.Comparable {
-    ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public Preference(android.content.Context, android.util.AttributeSet, int);
-    ctor public Preference(android.content.Context, android.util.AttributeSet);
-    ctor public Preference(android.content.Context);
-    method public boolean callChangeListener(java.lang.Object);
-    method public int compareTo(android.support.v7.preference.Preference);
-    method protected android.support.v7.preference.Preference findPreferenceInHierarchy(java.lang.String);
-    method public android.content.Context getContext();
-    method public java.lang.String getDependency();
-    method public android.os.Bundle getExtras();
-    method public java.lang.String getFragment();
-    method public android.graphics.drawable.Drawable getIcon();
-    method public android.content.Intent getIntent();
-    method public java.lang.String getKey();
-    method public final int getLayoutResource();
-    method public android.support.v7.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
-    method public android.support.v7.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
-    method public int getOrder();
-    method protected boolean getPersistedBoolean(boolean);
-    method protected float getPersistedFloat(float);
-    method protected int getPersistedInt(int);
-    method protected long getPersistedLong(long);
-    method protected java.lang.String getPersistedString(java.lang.String);
-    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
-    method public android.content.SharedPreferences getSharedPreferences();
-    method public boolean getShouldDisableView();
-    method public java.lang.CharSequence getSummary();
-    method public java.lang.CharSequence getTitle();
-    method public final int getWidgetLayoutResource();
-    method public boolean hasKey();
-    method public boolean isEnabled();
-    method public boolean isPersistent();
-    method public boolean isSelectable();
-    method public final boolean isVisible();
-    method protected void notifyChanged();
-    method public void notifyDependencyChange(boolean);
-    method protected void notifyHierarchyChanged();
-    method public void onAttached();
-    method protected void onAttachedToHierarchy(android.support.v7.preference.PreferenceManager);
-    method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
-    method protected void onClick();
-    method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
-    method public void onDetached();
-    method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
-    method public void onInitializeAccessibilityNodeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public void onParentChanged(android.support.v7.preference.Preference, boolean);
-    method protected void onPrepareForRemoval();
-    method protected void onRestoreInstanceState(android.os.Parcelable);
-    method protected android.os.Parcelable onSaveInstanceState();
-    method protected void onSetInitialValue(boolean, java.lang.Object);
-    method public android.os.Bundle peekExtras();
-    method protected boolean persistBoolean(boolean);
-    method protected boolean persistFloat(float);
-    method protected boolean persistInt(int);
-    method protected boolean persistLong(long);
-    method protected boolean persistString(java.lang.String);
-    method public void restoreHierarchyState(android.os.Bundle);
-    method public void saveHierarchyState(android.os.Bundle);
-    method public void setDefaultValue(java.lang.Object);
-    method public void setDependency(java.lang.String);
-    method public void setEnabled(boolean);
-    method public void setFragment(java.lang.String);
-    method public void setIcon(android.graphics.drawable.Drawable);
-    method public void setIcon(int);
-    method public void setIntent(android.content.Intent);
-    method public void setKey(java.lang.String);
-    method public void setLayoutResource(int);
-    method public void setOnPreferenceChangeListener(android.support.v7.preference.Preference.OnPreferenceChangeListener);
-    method public void setOnPreferenceClickListener(android.support.v7.preference.Preference.OnPreferenceClickListener);
-    method public void setOrder(int);
-    method public void setPersistent(boolean);
-    method public void setSelectable(boolean);
-    method public void setShouldDisableView(boolean);
-    method public void setSummary(java.lang.CharSequence);
-    method public void setSummary(int);
-    method public void setTitle(java.lang.CharSequence);
-    method public void setTitle(int);
-    method public void setViewId(int);
-    method public final void setVisible(boolean);
-    method public void setWidgetLayoutResource(int);
-    method public boolean shouldDisableDependents();
-    method protected boolean shouldPersist();
-    field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
-  }
-
-  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
-    ctor public Preference.BaseSavedState(android.os.Parcel);
-    ctor public Preference.BaseSavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.v7.preference.Preference.BaseSavedState> CREATOR;
-  }
-
-  public static abstract interface Preference.OnPreferenceChangeListener {
-    method public abstract boolean onPreferenceChange(android.support.v7.preference.Preference, java.lang.Object);
-  }
-
-  public static abstract interface Preference.OnPreferenceClickListener {
-    method public abstract boolean onPreferenceClick(android.support.v7.preference.Preference);
-  }
-
-  public class PreferenceCategory extends android.support.v7.preference.PreferenceGroup {
-    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
-    ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
-    ctor public PreferenceCategory(android.content.Context);
-  }
-
-  public abstract class PreferenceDialogFragmentCompat extends android.support.v4.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
-    ctor public PreferenceDialogFragmentCompat();
-    method public android.support.v7.preference.DialogPreference getPreference();
-    method protected void onBindDialogView(android.view.View);
-    method public void onClick(android.content.DialogInterface, int);
-    method protected android.view.View onCreateDialogView(android.content.Context);
-    method public abstract void onDialogClosed(boolean);
-    method protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder);
-    field protected static final java.lang.String ARG_KEY = "key";
-  }
-
-  public abstract class PreferenceFragmentCompat extends android.support.v4.app.Fragment implements android.support.v7.preference.DialogPreference.TargetFragment android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener {
-    ctor public PreferenceFragmentCompat();
-    method public void addPreferencesFromResource(int);
-    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
-    method public final android.support.v7.widget.RecyclerView getListView();
-    method public android.support.v7.preference.PreferenceManager getPreferenceManager();
-    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
-    method protected android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(android.support.v7.preference.PreferenceScreen);
-    method public android.support.v7.widget.RecyclerView.LayoutManager onCreateLayoutManager();
-    method public abstract void onCreatePreferences(android.os.Bundle, java.lang.String);
-    method public android.support.v7.widget.RecyclerView onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
-    method public void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
-    method public void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
-    method public boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
-    method public void scrollToPreference(java.lang.String);
-    method public void scrollToPreference(android.support.v7.preference.Preference);
-    method public void setDivider(android.graphics.drawable.Drawable);
-    method public void setDividerHeight(int);
-    method public void setPreferenceScreen(android.support.v7.preference.PreferenceScreen);
-    method public void setPreferencesFromResource(int, java.lang.String);
-    field public static final java.lang.String ARG_PREFERENCE_ROOT = "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
-  }
-
-  public static abstract interface PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
-    method public abstract boolean onPreferenceDisplayDialog(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
-  }
-
-  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
-    method public abstract boolean onPreferenceStartFragment(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.Preference);
-  }
-
-  public static abstract interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
-    method public abstract boolean onPreferenceStartScreen(android.support.v7.preference.PreferenceFragmentCompat, android.support.v7.preference.PreferenceScreen);
-  }
-
-  public abstract class PreferenceGroup extends android.support.v7.preference.Preference {
-    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
-    ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
-    method public void addItemFromInflater(android.support.v7.preference.Preference);
-    method public boolean addPreference(android.support.v7.preference.Preference);
-    method protected void dispatchRestoreInstanceState(android.os.Bundle);
-    method protected void dispatchSaveInstanceState(android.os.Bundle);
-    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
-    method public android.support.v7.preference.Preference getPreference(int);
-    method public int getPreferenceCount();
-    method protected boolean isOnSameScreenAsChildren();
-    method public boolean isOrderingAsAdded();
-    method protected boolean onPrepareAddPreference(android.support.v7.preference.Preference);
-    method public void removeAll();
-    method public boolean removePreference(android.support.v7.preference.Preference);
-    method public void setOrderingAsAdded(boolean);
-  }
-
-  public static abstract interface PreferenceGroup.PreferencePositionCallback {
-    method public abstract int getPreferenceAdapterPosition(java.lang.String);
-    method public abstract int getPreferenceAdapterPosition(android.support.v7.preference.Preference);
-  }
-
-  public class PreferenceManager {
-    method public android.support.v7.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
-    method public android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
-    method public android.content.Context getContext();
-    method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
-    method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
-    method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
-    method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
-    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
-    method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
-    method public android.content.SharedPreferences getSharedPreferences();
-    method public int getSharedPreferencesMode();
-    method public java.lang.String getSharedPreferencesName();
-    method public boolean isStorageDefault();
-    method public boolean isStorageDeviceProtected();
-    method public static void setDefaultValues(android.content.Context, int, boolean);
-    method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
-    method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
-    method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
-    method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
-    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
-    method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
-    method public void setSharedPreferencesMode(int);
-    method public void setSharedPreferencesName(java.lang.String);
-    method public void setStorageDefault();
-    method public void setStorageDeviceProtected();
-    method public void showDialog(android.support.v7.preference.Preference);
-    field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
-  }
-
-  public static abstract interface PreferenceManager.OnDisplayPreferenceDialogListener {
-    method public abstract void onDisplayPreferenceDialog(android.support.v7.preference.Preference);
-  }
-
-  public static abstract interface PreferenceManager.OnNavigateToScreenListener {
-    method public abstract void onNavigateToScreen(android.support.v7.preference.PreferenceScreen);
-  }
-
-  public static abstract interface PreferenceManager.OnPreferenceTreeClickListener {
-    method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
-  }
-
-  public static abstract class PreferenceManager.PreferenceComparisonCallback {
-    ctor public PreferenceManager.PreferenceComparisonCallback();
-    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
-    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
-  }
-
-  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
-    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
-    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
-    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
-  }
-
-  public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
-    method public void setShouldUseGeneratedIds(boolean);
-    method public boolean shouldUseGeneratedIds();
-  }
-
-  public class PreferenceViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
-    method public android.view.View findViewById(int);
-    method public boolean isDividerAllowedAbove();
-    method public boolean isDividerAllowedBelow();
-    method public void setDividerAllowedAbove(boolean);
-    method public void setDividerAllowedBelow(boolean);
-  }
-
-  public class SeekBarPreference extends android.support.v7.preference.Preference {
-    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
-    ctor public SeekBarPreference(android.content.Context);
-    method public int getMax();
-    method public int getMin();
-    method public final int getSeekBarIncrement();
-    method public int getValue();
-    method public boolean isAdjustable();
-    method public void setAdjustable(boolean);
-    method public final void setMax(int);
-    method public void setMin(int);
-    method public final void setSeekBarIncrement(int);
-    method public void setValue(int);
-  }
-
-  public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
-    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
-    ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet);
-    ctor public SwitchPreferenceCompat(android.content.Context);
-    method public java.lang.CharSequence getSwitchTextOff();
-    method public java.lang.CharSequence getSwitchTextOn();
-    method public void setSwitchTextOff(java.lang.CharSequence);
-    method public void setSwitchTextOff(int);
-    method public void setSwitchTextOn(java.lang.CharSequence);
-    method public void setSwitchTextOn(int);
-  }
-
-  public abstract class TwoStatePreference extends android.support.v7.preference.Preference {
-    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
-    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
-    ctor public TwoStatePreference(android.content.Context);
-    method public boolean getDisableDependentsState();
-    method public java.lang.CharSequence getSummaryOff();
-    method public java.lang.CharSequence getSummaryOn();
-    method public boolean isChecked();
-    method public void setChecked(boolean);
-    method public void setDisableDependentsState(boolean);
-    method public void setSummaryOff(java.lang.CharSequence);
-    method public void setSummaryOff(int);
-    method public void setSummaryOn(java.lang.CharSequence);
-    method public void setSummaryOn(int);
-    method protected void syncSummaryView(android.support.v7.preference.PreferenceViewHolder);
-    field protected boolean mChecked;
-  }
-
-}
-
-package android.support.v7.util {
-
-  public class AsyncListUtil<T> {
-    ctor public AsyncListUtil(java.lang.Class<T>, int, android.support.v7.util.AsyncListUtil.DataCallback<T>, android.support.v7.util.AsyncListUtil.ViewCallback);
-    method public T getItem(int);
-    method public int getItemCount();
-    method public void onRangeChanged();
-    method public void refresh();
-  }
-
-  public static abstract class AsyncListUtil.DataCallback<T> {
-    ctor public AsyncListUtil.DataCallback();
-    method public abstract void fillData(T[], int, int);
-    method public int getMaxCachedTiles();
-    method public void recycleData(T[], int);
-    method public abstract int refreshData();
-  }
-
-  public static abstract class AsyncListUtil.ViewCallback {
-    ctor public AsyncListUtil.ViewCallback();
-    method public void extendRangeInto(int[], int[], int);
-    method public abstract void getItemRangeInto(int[]);
-    method public abstract void onDataRefresh();
-    method public abstract void onItemLoaded(int);
-    field public static final int HINT_SCROLL_ASC = 2; // 0x2
-    field public static final int HINT_SCROLL_DESC = 1; // 0x1
-    field public static final int HINT_SCROLL_NONE = 0; // 0x0
-  }
-
-  public class BatchingListUpdateCallback implements android.support.v7.util.ListUpdateCallback {
-    ctor public BatchingListUpdateCallback(android.support.v7.util.ListUpdateCallback);
-    method public void dispatchLastEvent();
-    method public void onChanged(int, int, java.lang.Object);
-    method public void onInserted(int, int);
-    method public void onMoved(int, int);
-    method public void onRemoved(int, int);
-  }
-
-  public class DiffUtil {
-    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback);
-    method public static android.support.v7.util.DiffUtil.DiffResult calculateDiff(android.support.v7.util.DiffUtil.Callback, boolean);
-  }
-
-  public static abstract class DiffUtil.Callback {
-    ctor public DiffUtil.Callback();
-    method public abstract boolean areContentsTheSame(int, int);
-    method public abstract boolean areItemsTheSame(int, int);
-    method public java.lang.Object getChangePayload(int, int);
-    method public abstract int getNewListSize();
-    method public abstract int getOldListSize();
-  }
-
-  public static class DiffUtil.DiffResult {
-    method public void dispatchUpdatesTo(android.support.v7.widget.RecyclerView.Adapter);
-    method public void dispatchUpdatesTo(android.support.v7.util.ListUpdateCallback);
-  }
-
-  public abstract interface ListUpdateCallback {
-    method public abstract void onChanged(int, int, java.lang.Object);
-    method public abstract void onInserted(int, int);
-    method public abstract void onMoved(int, int);
-    method public abstract void onRemoved(int, int);
-  }
-
-  public class SortedList<T> {
-    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>);
-    ctor public SortedList(java.lang.Class<T>, android.support.v7.util.SortedList.Callback<T>, int);
-    method public int add(T);
-    method public void addAll(T[], boolean);
-    method public void addAll(T...);
-    method public void addAll(java.util.Collection<T>);
-    method public void beginBatchedUpdates();
-    method public void clear();
-    method public void endBatchedUpdates();
-    method public T get(int) throws java.lang.IndexOutOfBoundsException;
-    method public int indexOf(T);
-    method public void recalculatePositionOfItemAt(int);
-    method public boolean remove(T);
-    method public T removeItemAt(int);
-    method public int size();
-    method public void updateItemAt(int, T);
-    field public static final int INVALID_POSITION = -1; // 0xffffffff
-  }
-
-  public static class SortedList.BatchedCallback<T2> extends android.support.v7.util.SortedList.Callback {
-    ctor public SortedList.BatchedCallback(android.support.v7.util.SortedList.Callback<T2>);
-    method public boolean areContentsTheSame(T2, T2);
-    method public boolean areItemsTheSame(T2, T2);
-    method public int compare(T2, T2);
-    method public void dispatchLastEvent();
-    method public void onChanged(int, int);
-    method public void onInserted(int, int);
-    method public void onMoved(int, int);
-    method public void onRemoved(int, int);
-  }
-
-  public static abstract class SortedList.Callback<T2> implements java.util.Comparator android.support.v7.util.ListUpdateCallback {
-    ctor public SortedList.Callback();
-    method public abstract boolean areContentsTheSame(T2, T2);
-    method public abstract boolean areItemsTheSame(T2, T2);
-    method public abstract int compare(T2, T2);
-    method public abstract void onChanged(int, int);
-    method public void onChanged(int, int, java.lang.Object);
-  }
-
-}
-
-package android.support.v7.view {
-
-  public abstract class ActionMode {
-    ctor public ActionMode();
-    method public abstract void finish();
-    method public abstract android.view.View getCustomView();
-    method public abstract android.view.Menu getMenu();
-    method public abstract android.view.MenuInflater getMenuInflater();
-    method public abstract java.lang.CharSequence getSubtitle();
-    method public java.lang.Object getTag();
-    method public abstract java.lang.CharSequence getTitle();
-    method public boolean getTitleOptionalHint();
-    method public abstract void invalidate();
-    method public boolean isTitleOptional();
-    method public abstract void setCustomView(android.view.View);
-    method public abstract void setSubtitle(java.lang.CharSequence);
-    method public abstract void setSubtitle(int);
-    method public void setTag(java.lang.Object);
-    method public abstract void setTitle(java.lang.CharSequence);
-    method public abstract void setTitle(int);
-    method public void setTitleOptionalHint(boolean);
-  }
-
-  public static abstract interface ActionMode.Callback {
-    method public abstract boolean onActionItemClicked(android.support.v7.view.ActionMode, android.view.MenuItem);
-    method public abstract boolean onCreateActionMode(android.support.v7.view.ActionMode, android.view.Menu);
-    method public abstract void onDestroyActionMode(android.support.v7.view.ActionMode);
-    method public abstract boolean onPrepareActionMode(android.support.v7.view.ActionMode, android.view.Menu);
-  }
-
-  public abstract interface CollapsibleActionView {
-    method public abstract void onActionViewCollapsed();
-    method public abstract void onActionViewExpanded();
-  }
-
-}
-
-package android.support.v7.widget {
-
-  public class ActionMenuView extends android.support.v7.widget.LinearLayoutCompat {
-    ctor public ActionMenuView(android.content.Context);
-    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
-    method public void dismissPopupMenus();
-    method public android.view.Menu getMenu();
-    method public android.graphics.drawable.Drawable getOverflowIcon();
-    method public int getPopupTheme();
-    method public boolean hideOverflowMenu();
-    method public boolean isOverflowMenuShowing();
-    method public void onConfigurationChanged(android.content.res.Configuration);
-    method public void onDetachedFromWindow();
-    method public void setOnMenuItemClickListener(android.support.v7.widget.ActionMenuView.OnMenuItemClickListener);
-    method public void setOverflowIcon(android.graphics.drawable.Drawable);
-    method public void setPopupTheme(int);
-    method public boolean showOverflowMenu();
-  }
-
-  public static class ActionMenuView.LayoutParams extends android.support.v7.widget.LinearLayoutCompat.LayoutParams {
-    ctor public ActionMenuView.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public ActionMenuView.LayoutParams(android.support.v7.widget.ActionMenuView.LayoutParams);
-    ctor public ActionMenuView.LayoutParams(int, int);
-    field public int cellsUsed;
-    field public boolean expandable;
-    field public int extraPixels;
-    field public boolean isOverflowButton;
-    field public boolean preventEdgeOffset;
-  }
-
-  public static abstract interface ActionMenuView.OnMenuItemClickListener {
-    method public abstract boolean onMenuItemClick(android.view.MenuItem);
-  }
-
-  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatAutoCompleteTextView(android.content.Context);
-    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatButton extends android.widget.Button implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatButton(android.content.Context);
-    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet, int);
-    method public void setSupportAllCaps(boolean);
-  }
-
-  public class AppCompatCheckBox extends android.widget.CheckBox implements android.support.v4.widget.TintableCompoundButton {
-    ctor public AppCompatCheckBox(android.content.Context);
-    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
-    ctor public AppCompatCheckedTextView(android.content.Context);
-    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatEditText extends android.widget.EditText implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatEditText(android.content.Context);
-    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatImageButton extends android.widget.ImageButton implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatImageButton(android.content.Context);
-    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatImageView extends android.widget.ImageView implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatImageView(android.content.Context);
-    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatRadioButton extends android.widget.RadioButton implements android.support.v4.widget.TintableCompoundButton {
-    ctor public AppCompatRadioButton(android.content.Context);
-    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatRadioButton(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatRatingBar extends android.widget.RatingBar {
-    ctor public AppCompatRatingBar(android.content.Context);
-    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatSeekBar extends android.widget.SeekBar {
-    ctor public AppCompatSeekBar(android.content.Context);
-    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class AppCompatSpinner extends android.widget.Spinner implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatSpinner(android.content.Context);
-    ctor public AppCompatSpinner(android.content.Context, int);
-    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int);
-    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
-  }
-
-  public class AppCompatTextView extends android.widget.TextView implements android.support.v4.view.TintableBackgroundView {
-    ctor public AppCompatTextView(android.content.Context);
-    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet);
-    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet, int);
-  }
-
-  public class CardView extends android.widget.FrameLayout {
-    ctor public CardView(android.content.Context);
-    ctor public CardView(android.content.Context, android.util.AttributeSet);
-    ctor public CardView(android.content.Context, android.util.AttributeSet, int);
-    method public android.content.res.ColorStateList getCardBackgroundColor();
-    method public float getCardElevation();
-    method public int getContentPaddingBottom();
-    method public int getContentPaddingLeft();
-    method public int getContentPaddingRight();
-    method public int getContentPaddingTop();
-    method public float getMaxCardElevation();
-    method public boolean getPreventCornerOverlap();
-    method public float getRadius();
-    method public boolean getUseCompatPadding();
-    method public void setCardBackgroundColor(int);
-    method public void setCardBackgroundColor(android.content.res.ColorStateList);
-    method public void setCardElevation(float);
-    method public void setContentPadding(int, int, int, int);
-    method public void setMaxCardElevation(float);
-    method public void setPreventCornerOverlap(boolean);
-    method public void setRadius(float);
-    method public void setUseCompatPadding(boolean);
-  }
-
-  public class DefaultItemAnimator extends android.support.v7.widget.SimpleItemAnimator {
-    ctor public DefaultItemAnimator();
-    method public boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
-    method public boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
-    method public boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void endAnimations();
-    method public boolean isRunning();
-    method public void runPendingAnimations();
-  }
-
-  public class DividerItemDecoration extends android.support.v7.widget.RecyclerView.ItemDecoration {
-    ctor public DividerItemDecoration(android.content.Context, int);
-    method public void setDrawable(android.graphics.drawable.Drawable);
-    method public void setOrientation(int);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public class GridLayout extends android.view.ViewGroup {
-    ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
-    ctor public GridLayout(android.content.Context, android.util.AttributeSet);
-    ctor public GridLayout(android.content.Context);
-    method public int getAlignmentMode();
-    method public int getColumnCount();
-    method public int getOrientation();
-    method public android.util.Printer getPrinter();
-    method public int getRowCount();
-    method public boolean getUseDefaultMargins();
-    method public boolean isColumnOrderPreserved();
-    method public boolean isRowOrderPreserved();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setAlignmentMode(int);
-    method public void setColumnCount(int);
-    method public void setColumnOrderPreserved(boolean);
-    method public void setOrientation(int);
-    method public void setPrinter(android.util.Printer);
-    method public void setRowCount(int);
-    method public void setRowOrderPreserved(boolean);
-    method public void setUseDefaultMargins(boolean);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment, float);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment, float);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, float);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, float);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, int, android.support.v7.widget.GridLayout.Alignment);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, android.support.v7.widget.GridLayout.Alignment);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int, int);
-    method public static android.support.v7.widget.GridLayout.Spec spec(int);
-    field public static final int ALIGN_BOUNDS = 0; // 0x0
-    field public static final int ALIGN_MARGINS = 1; // 0x1
-    field public static final android.support.v7.widget.GridLayout.Alignment BASELINE;
-    field public static final android.support.v7.widget.GridLayout.Alignment BOTTOM;
-    field public static final android.support.v7.widget.GridLayout.Alignment CENTER;
-    field public static final android.support.v7.widget.GridLayout.Alignment END;
-    field public static final android.support.v7.widget.GridLayout.Alignment FILL;
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final android.support.v7.widget.GridLayout.Alignment LEFT;
-    field public static final android.support.v7.widget.GridLayout.Alignment RIGHT;
-    field public static final android.support.v7.widget.GridLayout.Alignment START;
-    field public static final android.support.v7.widget.GridLayout.Alignment TOP;
-    field public static final int UNDEFINED = -2147483648; // 0x80000000
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public static abstract class GridLayout.Alignment {
-  }
-
-  public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.Spec, android.support.v7.widget.GridLayout.Spec);
-    ctor public GridLayout.LayoutParams();
-    ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public GridLayout.LayoutParams(android.support.v7.widget.GridLayout.LayoutParams);
-    ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
-    method public void setGravity(int);
-    field public android.support.v7.widget.GridLayout.Spec columnSpec;
-    field public android.support.v7.widget.GridLayout.Spec rowSpec;
-  }
-
-  public static class GridLayout.Spec {
-    method public android.support.v7.widget.GridLayout.Alignment getAbsoluteAlignment(boolean);
-  }
-
-  public class GridLayoutManager extends android.support.v7.widget.LinearLayoutManager {
-    ctor public GridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public GridLayoutManager(android.content.Context, int);
-    ctor public GridLayoutManager(android.content.Context, int, int, boolean);
-    method public int getSpanCount();
-    method public android.support.v7.widget.GridLayoutManager.SpanSizeLookup getSpanSizeLookup();
-    method public void setSpanCount(int);
-    method public void setSpanSizeLookup(android.support.v7.widget.GridLayoutManager.SpanSizeLookup);
-    field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
-  }
-
-  public static final class GridLayoutManager.DefaultSpanSizeLookup extends android.support.v7.widget.GridLayoutManager.SpanSizeLookup {
-    ctor public GridLayoutManager.DefaultSpanSizeLookup();
-    method public int getSpanSize(int);
-  }
-
-  public static class GridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
-    ctor public GridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public GridLayoutManager.LayoutParams(int, int);
-    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public GridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
-    method public int getSpanIndex();
-    method public int getSpanSize();
-    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
-  }
-
-  public static abstract class GridLayoutManager.SpanSizeLookup {
-    ctor public GridLayoutManager.SpanSizeLookup();
-    method public int getSpanGroupIndex(int, int);
-    method public int getSpanIndex(int, int);
-    method public abstract int getSpanSize(int);
-    method public void invalidateSpanIndexCache();
-    method public boolean isSpanIndexCacheEnabled();
-    method public void setSpanIndexCacheEnabled(boolean);
-  }
-
-  public class LinearLayoutCompat extends android.view.ViewGroup {
-    ctor public LinearLayoutCompat(android.content.Context);
-    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet);
-    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet, int);
-    method public int getBaselineAlignedChildIndex();
-    method public android.graphics.drawable.Drawable getDividerDrawable();
-    method public int getDividerPadding();
-    method public int getGravity();
-    method public int getOrientation();
-    method public int getShowDividers();
-    method public float getWeightSum();
-    method public boolean isBaselineAligned();
-    method public boolean isMeasureWithLargestChildEnabled();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setBaselineAligned(boolean);
-    method public void setBaselineAlignedChildIndex(int);
-    method public void setDividerDrawable(android.graphics.drawable.Drawable);
-    method public void setDividerPadding(int);
-    method public void setGravity(int);
-    method public void setHorizontalGravity(int);
-    method public void setMeasureWithLargestChildEnabled(boolean);
-    method public void setOrientation(int);
-    method public void setShowDividers(int);
-    method public void setVerticalGravity(int);
-    method public void setWeightSum(float);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
-    field public static final int SHOW_DIVIDER_END = 4; // 0x4
-    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
-    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public static class LinearLayoutCompat.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public LinearLayoutCompat.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public LinearLayoutCompat.LayoutParams(int, int);
-    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
-    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public LinearLayoutCompat.LayoutParams(android.support.v7.widget.LinearLayoutCompat.LayoutParams);
-    field public int gravity;
-    field public float weight;
-  }
-
-  public class LinearLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.helper.ItemTouchHelper.ViewDropHandler android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
-    ctor public LinearLayoutManager(android.content.Context);
-    ctor public LinearLayoutManager(android.content.Context, int, boolean);
-    ctor public LinearLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
-    method public android.graphics.PointF computeScrollVectorForPosition(int);
-    method public int findFirstCompletelyVisibleItemPosition();
-    method public int findFirstVisibleItemPosition();
-    method public int findLastCompletelyVisibleItemPosition();
-    method public int findLastVisibleItemPosition();
-    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
-    method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
-    method public deprecated int getInitialItemPrefetchCount();
-    method public int getInitialPrefetchItemCount();
-    method public int getOrientation();
-    method public boolean getRecycleChildrenOnDetach();
-    method public boolean getReverseLayout();
-    method public boolean getStackFromEnd();
-    method protected boolean isLayoutRTL();
-    method public boolean isSmoothScrollbarEnabled();
-    method public void scrollToPositionWithOffset(int, int);
-    method public void setInitialPrefetchItemCount(int);
-    method public void setOrientation(int);
-    method public void setRecycleChildrenOnDetach(boolean);
-    method public void setReverseLayout(boolean);
-    method public void setSmoothScrollbarEnabled(boolean);
-    method public void setStackFromEnd(boolean);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  protected static class LinearLayoutManager.LayoutChunkResult {
-    ctor protected LinearLayoutManager.LayoutChunkResult();
-    field public int mConsumed;
-    field public boolean mFinished;
-    field public boolean mFocusable;
-    field public boolean mIgnoreConsumed;
-  }
-
-  public class LinearSmoothScroller extends android.support.v7.widget.RecyclerView.SmoothScroller {
-    ctor public LinearSmoothScroller(android.content.Context);
-    method public int calculateDtToFit(int, int, int, int, int);
-    method public int calculateDxToMakeVisible(android.view.View, int);
-    method public int calculateDyToMakeVisible(android.view.View, int);
-    method protected float calculateSpeedPerPixel(android.util.DisplayMetrics);
-    method protected int calculateTimeForDeceleration(int);
-    method protected int calculateTimeForScrolling(int);
-    method public android.graphics.PointF computeScrollVectorForPosition(int);
-    method protected int getHorizontalSnapPreference();
-    method protected int getVerticalSnapPreference();
-    method protected void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
-    method protected void onStart();
-    method protected void onStop();
-    method protected void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
-    method protected void updateActionForInterimTarget(android.support.v7.widget.RecyclerView.SmoothScroller.Action);
-    field public static final int SNAP_TO_ANY = 0; // 0x0
-    field public static final int SNAP_TO_END = 1; // 0x1
-    field public static final int SNAP_TO_START = -1; // 0xffffffff
-    field protected final android.view.animation.DecelerateInterpolator mDecelerateInterpolator;
-    field protected int mInterimTargetDx;
-    field protected int mInterimTargetDy;
-    field protected final android.view.animation.LinearInterpolator mLinearInterpolator;
-    field protected android.graphics.PointF mTargetVector;
-  }
-
-  public class LinearSnapHelper extends android.support.v7.widget.SnapHelper {
-    ctor public LinearSnapHelper();
-    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
-    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
-  }
-
-  public class ListPopupWindow {
-    ctor public ListPopupWindow(android.content.Context);
-    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet);
-    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int);
-    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearListSelection();
-    method public android.view.View.OnTouchListener createDragToOpenListener(android.view.View);
-    method public void dismiss();
-    method public android.view.View getAnchorView();
-    method public int getAnimationStyle();
-    method public android.graphics.drawable.Drawable getBackground();
-    method public int getHeight();
-    method public int getHorizontalOffset();
-    method public int getInputMethodMode();
-    method public android.widget.ListView getListView();
-    method public int getPromptPosition();
-    method public java.lang.Object getSelectedItem();
-    method public long getSelectedItemId();
-    method public int getSelectedItemPosition();
-    method public android.view.View getSelectedView();
-    method public int getSoftInputMode();
-    method public int getVerticalOffset();
-    method public int getWidth();
-    method public boolean isInputMethodNotNeeded();
-    method public boolean isModal();
-    method public boolean isShowing();
-    method public boolean onKeyDown(int, android.view.KeyEvent);
-    method public boolean onKeyPreIme(int, android.view.KeyEvent);
-    method public boolean onKeyUp(int, android.view.KeyEvent);
-    method public boolean performItemClick(int);
-    method public void postShow();
-    method public void setAdapter(android.widget.ListAdapter);
-    method public void setAnchorView(android.view.View);
-    method public void setAnimationStyle(int);
-    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
-    method public void setContentWidth(int);
-    method public void setDropDownGravity(int);
-    method public void setHeight(int);
-    method public void setHorizontalOffset(int);
-    method public void setInputMethodMode(int);
-    method public void setListSelector(android.graphics.drawable.Drawable);
-    method public void setModal(boolean);
-    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
-    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
-    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
-    method public void setPromptPosition(int);
-    method public void setPromptView(android.view.View);
-    method public void setSelection(int);
-    method public void setSoftInputMode(int);
-    method public void setVerticalOffset(int);
-    method public void setWidth(int);
-    method public void setWindowLayoutType(int);
-    method public void show();
-    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
-    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
-    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
-    field public static final int MATCH_PARENT = -1; // 0xffffffff
-    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
-    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public abstract class OrientationHelper {
-    method public static android.support.v7.widget.OrientationHelper createHorizontalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public static android.support.v7.widget.OrientationHelper createOrientationHelper(android.support.v7.widget.RecyclerView.LayoutManager, int);
-    method public static android.support.v7.widget.OrientationHelper createVerticalHelper(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public abstract int getDecoratedEnd(android.view.View);
-    method public abstract int getDecoratedMeasurement(android.view.View);
-    method public abstract int getDecoratedMeasurementInOther(android.view.View);
-    method public abstract int getDecoratedStart(android.view.View);
-    method public abstract int getEnd();
-    method public abstract int getEndAfterPadding();
-    method public abstract int getEndPadding();
-    method public abstract int getMode();
-    method public abstract int getModeInOther();
-    method public abstract int getStartAfterPadding();
-    method public abstract int getTotalSpace();
-    method public int getTotalSpaceChange();
-    method public abstract int getTransformedEndWithDecoration(android.view.View);
-    method public abstract int getTransformedStartWithDecoration(android.view.View);
-    method public abstract void offsetChild(android.view.View, int);
-    method public abstract void offsetChildren(int);
-    method public void onLayoutComplete();
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-    field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
-  }
-
-  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
-    ctor public PagerSnapHelper();
-    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
-    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
-  }
-
-  public class PopupMenu {
-    ctor public PopupMenu(android.content.Context, android.view.View);
-    ctor public PopupMenu(android.content.Context, android.view.View, int);
-    ctor public PopupMenu(android.content.Context, android.view.View, int, int, int);
-    method public void dismiss();
-    method public android.view.View.OnTouchListener getDragToOpenListener();
-    method public int getGravity();
-    method public android.view.Menu getMenu();
-    method public android.view.MenuInflater getMenuInflater();
-    method public void inflate(int);
-    method public void setGravity(int);
-    method public void setOnDismissListener(android.support.v7.widget.PopupMenu.OnDismissListener);
-    method public void setOnMenuItemClickListener(android.support.v7.widget.PopupMenu.OnMenuItemClickListener);
-    method public void show();
-  }
-
-  public static abstract interface PopupMenu.OnDismissListener {
-    method public abstract void onDismiss(android.support.v7.widget.PopupMenu);
-  }
-
-  public static abstract interface PopupMenu.OnMenuItemClickListener {
-    method public abstract boolean onMenuItemClick(android.view.MenuItem);
-  }
-
-  public class RecyclerView extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.ScrollingView {
-    ctor public RecyclerView(android.content.Context);
-    ctor public RecyclerView(android.content.Context, android.util.AttributeSet);
-    ctor public RecyclerView(android.content.Context, android.util.AttributeSet, int);
-    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration, int);
-    method public void addItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
-    method public void addOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
-    method public void addOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
-    method public void addOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
-    method public void clearOnChildAttachStateChangeListeners();
-    method public void clearOnScrollListeners();
-    method public int computeHorizontalScrollExtent();
-    method public int computeHorizontalScrollOffset();
-    method public int computeHorizontalScrollRange();
-    method public int computeVerticalScrollExtent();
-    method public int computeVerticalScrollOffset();
-    method public int computeVerticalScrollRange();
-    method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
-    method public android.view.View findChildViewUnder(float, float);
-    method public android.view.View findContainingItemView(android.view.View);
-    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
-    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
-    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
-    method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
-    method public deprecated android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForPosition(int);
-    method public boolean fling(int, int);
-    method public android.support.v7.widget.RecyclerView.Adapter getAdapter();
-    method public int getChildAdapterPosition(android.view.View);
-    method public long getChildItemId(android.view.View);
-    method public int getChildLayoutPosition(android.view.View);
-    method public deprecated int getChildPosition(android.view.View);
-    method public android.support.v7.widget.RecyclerView.ViewHolder getChildViewHolder(android.view.View);
-    method public android.support.v7.widget.RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate();
-    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
-    method public android.support.v7.widget.RecyclerView.ItemAnimator getItemAnimator();
-    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
-    method public int getMaxFlingVelocity();
-    method public int getMinFlingVelocity();
-    method public android.support.v7.widget.RecyclerView.OnFlingListener getOnFlingListener();
-    method public boolean getPreserveFocusAfterLayout();
-    method public android.support.v7.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
-    method public int getScrollState();
-    method public boolean hasFixedSize();
-    method public boolean hasPendingAdapterUpdates();
-    method public void invalidateItemDecorations();
-    method public boolean isAnimating();
-    method public boolean isComputingLayout();
-    method public boolean isLayoutFrozen();
-    method public void offsetChildrenHorizontal(int);
-    method public void offsetChildrenVertical(int);
-    method public void onChildAttachedToWindow(android.view.View);
-    method public void onChildDetachedFromWindow(android.view.View);
-    method public void onDraw(android.graphics.Canvas);
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void onScrollStateChanged(int);
-    method public void onScrolled(int, int);
-    method public void removeItemDecoration(android.support.v7.widget.RecyclerView.ItemDecoration);
-    method public void removeOnChildAttachStateChangeListener(android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener);
-    method public void removeOnItemTouchListener(android.support.v7.widget.RecyclerView.OnItemTouchListener);
-    method public void removeOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
-    method public void scrollToPosition(int);
-    method public void setAccessibilityDelegateCompat(android.support.v7.widget.RecyclerViewAccessibilityDelegate);
-    method public void setAdapter(android.support.v7.widget.RecyclerView.Adapter);
-    method public void setChildDrawingOrderCallback(android.support.v7.widget.RecyclerView.ChildDrawingOrderCallback);
-    method public void setHasFixedSize(boolean);
-    method public void setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator);
-    method public void setItemViewCacheSize(int);
-    method public void setLayoutFrozen(boolean);
-    method public void setLayoutManager(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public void setOnFlingListener(android.support.v7.widget.RecyclerView.OnFlingListener);
-    method public deprecated void setOnScrollListener(android.support.v7.widget.RecyclerView.OnScrollListener);
-    method public void setPreserveFocusAfterLayout(boolean);
-    method public void setRecycledViewPool(android.support.v7.widget.RecyclerView.RecycledViewPool);
-    method public void setRecyclerListener(android.support.v7.widget.RecyclerView.RecyclerListener);
-    method public void setScrollingTouchSlop(int);
-    method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
-    method public void smoothScrollBy(int, int);
-    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
-    method public void smoothScrollToPosition(int);
-    method public void stopScroll();
-    method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int INVALID_TYPE = -1; // 0xffffffff
-    field public static final long NO_ID = -1L; // 0xffffffffffffffffL
-    field public static final int NO_POSITION = -1; // 0xffffffff
-    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
-    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
-    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
-    field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
-    field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public static abstract class RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder> {
-    ctor public RecyclerView.Adapter();
-    method public final void bindViewHolder(VH, int);
-    method public final VH createViewHolder(android.view.ViewGroup, int);
-    method public abstract int getItemCount();
-    method public long getItemId(int);
-    method public int getItemViewType(int);
-    method public final boolean hasObservers();
-    method public final boolean hasStableIds();
-    method public final void notifyDataSetChanged();
-    method public final void notifyItemChanged(int);
-    method public final void notifyItemChanged(int, java.lang.Object);
-    method public final void notifyItemInserted(int);
-    method public final void notifyItemMoved(int, int);
-    method public final void notifyItemRangeChanged(int, int);
-    method public final void notifyItemRangeChanged(int, int, java.lang.Object);
-    method public final void notifyItemRangeInserted(int, int);
-    method public final void notifyItemRangeRemoved(int, int);
-    method public final void notifyItemRemoved(int);
-    method public void onAttachedToRecyclerView(android.support.v7.widget.RecyclerView);
-    method public abstract void onBindViewHolder(VH, int);
-    method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object>);
-    method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
-    method public void onDetachedFromRecyclerView(android.support.v7.widget.RecyclerView);
-    method public boolean onFailedToRecycleView(VH);
-    method public void onViewAttachedToWindow(VH);
-    method public void onViewDetachedFromWindow(VH);
-    method public void onViewRecycled(VH);
-    method public void registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
-    method public void setHasStableIds(boolean);
-    method public void unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver);
-  }
-
-  public static abstract class RecyclerView.AdapterDataObserver {
-    ctor public RecyclerView.AdapterDataObserver();
-    method public void onChanged();
-    method public void onItemRangeChanged(int, int);
-    method public void onItemRangeChanged(int, int, java.lang.Object);
-    method public void onItemRangeInserted(int, int);
-    method public void onItemRangeMoved(int, int, int);
-    method public void onItemRangeRemoved(int, int);
-  }
-
-  public static abstract interface RecyclerView.ChildDrawingOrderCallback {
-    method public abstract int onGetChildDrawingOrder(int, int);
-  }
-
-  public static abstract class RecyclerView.ItemAnimator {
-    ctor public RecyclerView.ItemAnimator();
-    method public abstract boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public abstract boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public abstract boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public boolean canReuseUpdatedViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object>);
-    method public final void dispatchAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchAnimationsFinished();
-    method public abstract void endAnimation(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public abstract void endAnimations();
-    method public long getAddDuration();
-    method public long getChangeDuration();
-    method public long getMoveDuration();
-    method public long getRemoveDuration();
-    method public abstract boolean isRunning();
-    method public final boolean isRunning(android.support.v7.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener);
-    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
-    method public void onAnimationFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onAnimationStarted(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.ViewHolder, int, java.util.List<java.lang.Object>);
-    method public abstract void runPendingAnimations();
-    method public void setAddDuration(long);
-    method public void setChangeDuration(long);
-    method public void setMoveDuration(long);
-    method public void setRemoveDuration(long);
-    field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
-    field public static final int FLAG_CHANGED = 2; // 0x2
-    field public static final int FLAG_INVALIDATED = 4; // 0x4
-    field public static final int FLAG_MOVED = 2048; // 0x800
-    field public static final int FLAG_REMOVED = 8; // 0x8
-  }
-
-  public static abstract class RecyclerView.ItemAnimator.AdapterChanges implements java.lang.annotation.Annotation {
-  }
-
-  public static abstract interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
-    method public abstract void onAnimationsFinished();
-  }
-
-  public static class RecyclerView.ItemAnimator.ItemHolderInfo {
-    ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
-    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(android.support.v7.widget.RecyclerView.ViewHolder, int);
-    field public int bottom;
-    field public int changeFlags;
-    field public int left;
-    field public int right;
-    field public int top;
-  }
-
-  public static abstract class RecyclerView.ItemDecoration {
-    ctor public RecyclerView.ItemDecoration();
-    method public deprecated void getItemOffsets(android.graphics.Rect, int, android.support.v7.widget.RecyclerView);
-    method public void getItemOffsets(android.graphics.Rect, android.view.View, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
-    method public void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
-    method public deprecated void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
-    method public void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State);
-    method public deprecated void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView);
-  }
-
-  public static abstract class RecyclerView.LayoutManager {
-    ctor public RecyclerView.LayoutManager();
-    method public void addDisappearingView(android.view.View);
-    method public void addDisappearingView(android.view.View, int);
-    method public void addView(android.view.View);
-    method public void addView(android.view.View, int);
-    method public void assertInLayoutOrScroll(java.lang.String);
-    method public void assertNotInLayoutOrScroll(java.lang.String);
-    method public void attachView(android.view.View, int, android.support.v7.widget.RecyclerView.LayoutParams);
-    method public void attachView(android.view.View, int);
-    method public void attachView(android.view.View);
-    method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
-    method public boolean canScrollHorizontally();
-    method public boolean canScrollVertically();
-    method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
-    method public static int chooseSize(int, int, int);
-    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
-    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
-    method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
-    method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
-    method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
-    method public int computeVerticalScrollExtent(android.support.v7.widget.RecyclerView.State);
-    method public int computeVerticalScrollOffset(android.support.v7.widget.RecyclerView.State);
-    method public int computeVerticalScrollRange(android.support.v7.widget.RecyclerView.State);
-    method public void detachAndScrapAttachedViews(android.support.v7.widget.RecyclerView.Recycler);
-    method public void detachAndScrapView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
-    method public void detachAndScrapViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
-    method public void detachView(android.view.View);
-    method public void detachViewAt(int);
-    method public void endAnimation(android.view.View);
-    method public android.view.View findContainingItemView(android.view.View);
-    method public android.view.View findViewByPosition(int);
-    method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
-    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
-    method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.content.Context, android.util.AttributeSet);
-    method public int getBaseline();
-    method public int getBottomDecorationHeight(android.view.View);
-    method public android.view.View getChildAt(int);
-    method public int getChildCount();
-    method public static deprecated int getChildMeasureSpec(int, int, int, boolean);
-    method public static int getChildMeasureSpec(int, int, int, int, boolean);
-    method public boolean getClipToPadding();
-    method public int getColumnCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public int getDecoratedBottom(android.view.View);
-    method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
-    method public int getDecoratedLeft(android.view.View);
-    method public int getDecoratedMeasuredHeight(android.view.View);
-    method public int getDecoratedMeasuredWidth(android.view.View);
-    method public int getDecoratedRight(android.view.View);
-    method public int getDecoratedTop(android.view.View);
-    method public android.view.View getFocusedChild();
-    method public int getHeight();
-    method public int getHeightMode();
-    method public int getItemCount();
-    method public int getItemViewType(android.view.View);
-    method public int getLayoutDirection();
-    method public int getLeftDecorationWidth(android.view.View);
-    method public int getMinimumHeight();
-    method public int getMinimumWidth();
-    method public int getPaddingBottom();
-    method public int getPaddingEnd();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
-    method public int getPaddingStart();
-    method public int getPaddingTop();
-    method public int getPosition(android.view.View);
-    method public static android.support.v7.widget.RecyclerView.LayoutManager.Properties getProperties(android.content.Context, android.util.AttributeSet, int, int);
-    method public int getRightDecorationWidth(android.view.View);
-    method public int getRowCountForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public int getSelectionModeForAccessibility(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public int getTopDecorationHeight(android.view.View);
-    method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
-    method public int getWidth();
-    method public int getWidthMode();
-    method public boolean hasFocus();
-    method public void ignoreView(android.view.View);
-    method public boolean isAttachedToWindow();
-    method public boolean isAutoMeasureEnabled();
-    method public boolean isFocused();
-    method public final boolean isItemPrefetchEnabled();
-    method public boolean isLayoutHierarchical(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public boolean isMeasurementCacheEnabled();
-    method public boolean isSmoothScrolling();
-    method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
-    method public void layoutDecorated(android.view.View, int, int, int, int);
-    method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
-    method public void measureChild(android.view.View, int, int);
-    method public void measureChildWithMargins(android.view.View, int, int);
-    method public void moveView(int, int);
-    method public void offsetChildrenHorizontal(int);
-    method public void offsetChildrenVertical(int);
-    method public void onAdapterChanged(android.support.v7.widget.RecyclerView.Adapter, android.support.v7.widget.RecyclerView.Adapter);
-    method public boolean onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int);
-    method public void onAttachedToWindow(android.support.v7.widget.RecyclerView);
-    method public deprecated void onDetachedFromWindow(android.support.v7.widget.RecyclerView);
-    method public void onDetachedFromWindow(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.Recycler);
-    method public android.view.View onFocusSearchFailed(android.view.View, int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onInitializeAccessibilityEvent(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
-    method public void onInitializeAccessibilityNodeInfo(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public void onInitializeAccessibilityNodeInfoForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public android.view.View onInterceptFocusSearch(android.view.View, int);
-    method public void onItemsAdded(android.support.v7.widget.RecyclerView, int, int);
-    method public void onItemsChanged(android.support.v7.widget.RecyclerView);
-    method public void onItemsMoved(android.support.v7.widget.RecyclerView, int, int, int);
-    method public void onItemsRemoved(android.support.v7.widget.RecyclerView, int, int);
-    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int);
-    method public void onItemsUpdated(android.support.v7.widget.RecyclerView, int, int, java.lang.Object);
-    method public void onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public void onLayoutCompleted(android.support.v7.widget.RecyclerView.State);
-    method public void onMeasure(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, int);
-    method public deprecated boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.view.View, android.view.View);
-    method public boolean onRequestChildFocus(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, android.view.View, android.view.View);
-    method public void onRestoreInstanceState(android.os.Parcelable);
-    method public android.os.Parcelable onSaveInstanceState();
-    method public void onScrollStateChanged(int);
-    method public boolean performAccessibilityAction(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, int, android.os.Bundle);
-    method public boolean performAccessibilityActionForItem(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State, android.view.View, int, android.os.Bundle);
-    method public void postOnAnimation(java.lang.Runnable);
-    method public void removeAllViews();
-    method public void removeAndRecycleAllViews(android.support.v7.widget.RecyclerView.Recycler);
-    method public void removeAndRecycleView(android.view.View, android.support.v7.widget.RecyclerView.Recycler);
-    method public void removeAndRecycleViewAt(int, android.support.v7.widget.RecyclerView.Recycler);
-    method public boolean removeCallbacks(java.lang.Runnable);
-    method public void removeDetachedView(android.view.View);
-    method public void removeView(android.view.View);
-    method public void removeViewAt(int);
-    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
-    method public boolean requestChildRectangleOnScreen(android.support.v7.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
-    method public void requestLayout();
-    method public void requestSimpleAnimationsInNextLayout();
-    method public int scrollHorizontallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public void scrollToPosition(int);
-    method public int scrollVerticallyBy(int, android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State);
-    method public void setAutoMeasureEnabled(boolean);
-    method public final void setItemPrefetchEnabled(boolean);
-    method public void setMeasuredDimension(android.graphics.Rect, int, int);
-    method public void setMeasuredDimension(int, int);
-    method public void setMeasurementCacheEnabled(boolean);
-    method public void smoothScrollToPosition(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.State, int);
-    method public void startSmoothScroll(android.support.v7.widget.RecyclerView.SmoothScroller);
-    method public void stopIgnoringView(android.view.View);
-    method public boolean supportsPredictiveItemAnimations();
-  }
-
-  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
-    method public abstract void addPosition(int, int);
-  }
-
-  public static class RecyclerView.LayoutManager.Properties {
-    ctor public RecyclerView.LayoutManager.Properties();
-    field public int orientation;
-    field public boolean reverseLayout;
-    field public int spanCount;
-    field public boolean stackFromEnd;
-  }
-
-  public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public RecyclerView.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public RecyclerView.LayoutParams(int, int);
-    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public RecyclerView.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
-    method public int getViewAdapterPosition();
-    method public int getViewLayoutPosition();
-    method public deprecated int getViewPosition();
-    method public boolean isItemChanged();
-    method public boolean isItemRemoved();
-    method public boolean isViewInvalid();
-    method public boolean viewNeedsUpdate();
-  }
-
-  public static abstract interface RecyclerView.OnChildAttachStateChangeListener {
-    method public abstract void onChildViewAttachedToWindow(android.view.View);
-    method public abstract void onChildViewDetachedFromWindow(android.view.View);
-  }
-
-  public static abstract class RecyclerView.OnFlingListener {
-    ctor public RecyclerView.OnFlingListener();
-    method public abstract boolean onFling(int, int);
-  }
-
-  public static abstract interface RecyclerView.OnItemTouchListener {
-    method public abstract boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
-    method public abstract void onRequestDisallowInterceptTouchEvent(boolean);
-    method public abstract void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
-  }
-
-  public static abstract class RecyclerView.OnScrollListener {
-    ctor public RecyclerView.OnScrollListener();
-    method public void onScrollStateChanged(android.support.v7.widget.RecyclerView, int);
-    method public void onScrolled(android.support.v7.widget.RecyclerView, int, int);
-  }
-
-  public static class RecyclerView.RecycledViewPool {
-    ctor public RecyclerView.RecycledViewPool();
-    method public void clear();
-    method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
-    method public int getRecycledViewCount(int);
-    method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void setMaxRecycledViews(int, int);
-  }
-
-  public final class RecyclerView.Recycler {
-    ctor public RecyclerView.Recycler();
-    method public void bindViewToPosition(android.view.View, int);
-    method public void clear();
-    method public int convertPreLayoutPositionToPostLayout(int);
-    method public java.util.List<android.support.v7.widget.RecyclerView.ViewHolder> getScrapList();
-    method public android.view.View getViewForPosition(int);
-    method public void recycleView(android.view.View);
-    method public void setViewCacheSize(int);
-  }
-
-  public static abstract interface RecyclerView.RecyclerListener {
-    method public abstract void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder);
-  }
-
-  public static class RecyclerView.SimpleOnItemTouchListener implements android.support.v7.widget.RecyclerView.OnItemTouchListener {
-    ctor public RecyclerView.SimpleOnItemTouchListener();
-    method public boolean onInterceptTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
-    method public void onRequestDisallowInterceptTouchEvent(boolean);
-    method public void onTouchEvent(android.support.v7.widget.RecyclerView, android.view.MotionEvent);
-  }
-
-  public static abstract class RecyclerView.SmoothScroller {
-    ctor public RecyclerView.SmoothScroller();
-    method public android.view.View findViewByPosition(int);
-    method public int getChildCount();
-    method public int getChildPosition(android.view.View);
-    method public android.support.v7.widget.RecyclerView.LayoutManager getLayoutManager();
-    method public int getTargetPosition();
-    method public deprecated void instantScrollToPosition(int);
-    method public boolean isPendingInitialRun();
-    method public boolean isRunning();
-    method protected void normalize(android.graphics.PointF);
-    method protected void onChildAttachedToWindow(android.view.View);
-    method protected abstract void onSeekTargetStep(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
-    method protected abstract void onStart();
-    method protected abstract void onStop();
-    method protected abstract void onTargetFound(android.view.View, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.SmoothScroller.Action);
-    method public void setTargetPosition(int);
-    method protected final void stop();
-  }
-
-  public static class RecyclerView.SmoothScroller.Action {
-    ctor public RecyclerView.SmoothScroller.Action(int, int);
-    ctor public RecyclerView.SmoothScroller.Action(int, int, int);
-    ctor public RecyclerView.SmoothScroller.Action(int, int, int, android.view.animation.Interpolator);
-    method public int getDuration();
-    method public int getDx();
-    method public int getDy();
-    method public android.view.animation.Interpolator getInterpolator();
-    method public void jumpTo(int);
-    method public void setDuration(int);
-    method public void setDx(int);
-    method public void setDy(int);
-    method public void setInterpolator(android.view.animation.Interpolator);
-    method public void update(int, int, int, android.view.animation.Interpolator);
-    field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
-  }
-
-  public static abstract interface RecyclerView.SmoothScroller.ScrollVectorProvider {
-    method public abstract android.graphics.PointF computeScrollVectorForPosition(int);
-  }
-
-  public static class RecyclerView.State {
-    ctor public RecyclerView.State();
-    method public boolean didStructureChange();
-    method public <T> T get(int);
-    method public int getItemCount();
-    method public int getTargetScrollPosition();
-    method public boolean hasTargetScrollPosition();
-    method public boolean isMeasuring();
-    method public boolean isPreLayout();
-    method public void put(int, java.lang.Object);
-    method public void remove(int);
-    method public boolean willRunPredictiveAnimations();
-    method public boolean willRunSimpleAnimations();
-  }
-
-  public static abstract class RecyclerView.ViewCacheExtension {
-    ctor public RecyclerView.ViewCacheExtension();
-    method public abstract android.view.View getViewForPositionAndType(android.support.v7.widget.RecyclerView.Recycler, int, int);
-  }
-
-  public static abstract class RecyclerView.ViewHolder {
-    ctor public RecyclerView.ViewHolder(android.view.View);
-    method public final int getAdapterPosition();
-    method public final long getItemId();
-    method public final int getItemViewType();
-    method public final int getLayoutPosition();
-    method public final int getOldPosition();
-    method public final deprecated int getPosition();
-    method public final boolean isRecyclable();
-    method public final void setIsRecyclable(boolean);
-    field public final android.view.View itemView;
-  }
-
-  public class RecyclerViewAccessibilityDelegate extends android.support.v4.view.AccessibilityDelegateCompat {
-    ctor public RecyclerViewAccessibilityDelegate(android.support.v7.widget.RecyclerView);
-    method public android.support.v4.view.AccessibilityDelegateCompat getItemDelegate();
-  }
-
-  public class SearchView extends android.support.v7.widget.LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView {
-    ctor public SearchView(android.content.Context);
-    ctor public SearchView(android.content.Context, android.util.AttributeSet);
-    ctor public SearchView(android.content.Context, android.util.AttributeSet, int);
-    method public int getImeOptions();
-    method public int getInputType();
-    method public int getMaxWidth();
-    method public java.lang.CharSequence getQuery();
-    method public java.lang.CharSequence getQueryHint();
-    method public android.support.v4.widget.CursorAdapter getSuggestionsAdapter();
-    method public boolean isIconfiedByDefault();
-    method public boolean isIconified();
-    method public boolean isQueryRefinementEnabled();
-    method public boolean isSubmitButtonEnabled();
-    method public void onActionViewCollapsed();
-    method public void onActionViewExpanded();
-    method public void setIconified(boolean);
-    method public void setIconifiedByDefault(boolean);
-    method public void setImeOptions(int);
-    method public void setInputType(int);
-    method public void setMaxWidth(int);
-    method public void setOnCloseListener(android.support.v7.widget.SearchView.OnCloseListener);
-    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener);
-    method public void setOnQueryTextListener(android.support.v7.widget.SearchView.OnQueryTextListener);
-    method public void setOnSearchClickListener(android.view.View.OnClickListener);
-    method public void setOnSuggestionListener(android.support.v7.widget.SearchView.OnSuggestionListener);
-    method public void setQuery(java.lang.CharSequence, boolean);
-    method public void setQueryHint(java.lang.CharSequence);
-    method public void setQueryRefinementEnabled(boolean);
-    method public void setSearchableInfo(android.app.SearchableInfo);
-    method public void setSubmitButtonEnabled(boolean);
-    method public void setSuggestionsAdapter(android.support.v4.widget.CursorAdapter);
-  }
-
-  public static abstract interface SearchView.OnCloseListener {
-    method public abstract boolean onClose();
-  }
-
-  public static abstract interface SearchView.OnQueryTextListener {
-    method public abstract boolean onQueryTextChange(java.lang.String);
-    method public abstract boolean onQueryTextSubmit(java.lang.String);
-  }
-
-  public static abstract interface SearchView.OnSuggestionListener {
-    method public abstract boolean onSuggestionClick(int);
-    method public abstract boolean onSuggestionSelect(int);
-  }
-
-  public class ShareActionProvider extends android.support.v4.view.ActionProvider {
-    ctor public ShareActionProvider(android.content.Context);
-    method public android.view.View onCreateActionView();
-    method public void setOnShareTargetSelectedListener(android.support.v7.widget.ShareActionProvider.OnShareTargetSelectedListener);
-    method public void setShareHistoryFileName(java.lang.String);
-    method public void setShareIntent(android.content.Intent);
-    field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
-  }
-
-  public static abstract interface ShareActionProvider.OnShareTargetSelectedListener {
-    method public abstract boolean onShareTargetSelected(android.support.v7.widget.ShareActionProvider, android.content.Intent);
-  }
-
-  public abstract class SimpleItemAnimator extends android.support.v7.widget.RecyclerView.ItemAnimator {
-    ctor public SimpleItemAnimator();
-    method public abstract boolean animateAdd(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public boolean animateAppearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public abstract boolean animateChange(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
-    method public boolean animateDisappearance(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public abstract boolean animateMove(android.support.v7.widget.RecyclerView.ViewHolder, int, int, int, int);
-    method public boolean animatePersistence(android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo, android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
-    method public abstract boolean animateRemove(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
-    method public final void dispatchChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
-    method public final void dispatchMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public final void dispatchRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public boolean getSupportsChangeAnimations();
-    method public void onAddFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onAddStarting(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onChangeFinished(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
-    method public void onChangeStarting(android.support.v7.widget.RecyclerView.ViewHolder, boolean);
-    method public void onMoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onMoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onRemoveFinished(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onRemoveStarting(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void setSupportsChangeAnimations(boolean);
-  }
-
-  public abstract class SnapHelper extends android.support.v7.widget.RecyclerView.OnFlingListener {
-    ctor public SnapHelper();
-    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
-    method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
-    method public int[] calculateScrollDistance(int, int);
-    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
-    method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
-    method public boolean onFling(int, int);
-  }
-
-  public class StaggeredGridLayoutManager extends android.support.v7.widget.RecyclerView.LayoutManager implements android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
-    ctor public StaggeredGridLayoutManager(android.content.Context, android.util.AttributeSet, int, int);
-    ctor public StaggeredGridLayoutManager(int, int);
-    method public android.graphics.PointF computeScrollVectorForPosition(int);
-    method public int[] findFirstCompletelyVisibleItemPositions(int[]);
-    method public int[] findFirstVisibleItemPositions(int[]);
-    method public int[] findLastCompletelyVisibleItemPositions(int[]);
-    method public int[] findLastVisibleItemPositions(int[]);
-    method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
-    method public int getGapStrategy();
-    method public int getOrientation();
-    method public boolean getReverseLayout();
-    method public int getSpanCount();
-    method public void invalidateSpanAssignments();
-    method public void scrollToPositionWithOffset(int, int);
-    method public void setGapStrategy(int);
-    method public void setOrientation(int);
-    method public void setReverseLayout(boolean);
-    method public void setSpanCount(int);
-    field public static final deprecated int GAP_HANDLING_LAZY = 1; // 0x1
-    field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
-    field public static final int GAP_HANDLING_NONE = 0; // 0x0
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public static class StaggeredGridLayoutManager.LayoutParams extends android.support.v7.widget.RecyclerView.LayoutParams {
-    ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
-    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public StaggeredGridLayoutManager.LayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
-    method public final int getSpanIndex();
-    method public boolean isFullSpan();
-    method public void setFullSpan(boolean);
-    field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
-  }
-
-  public class SwitchCompat extends android.widget.CompoundButton {
-    ctor public SwitchCompat(android.content.Context);
-    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet);
-    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet, int);
-    method public boolean getShowText();
-    method public boolean getSplitTrack();
-    method public int getSwitchMinWidth();
-    method public int getSwitchPadding();
-    method public java.lang.CharSequence getTextOff();
-    method public java.lang.CharSequence getTextOn();
-    method public android.graphics.drawable.Drawable getThumbDrawable();
-    method public int getThumbTextPadding();
-    method public android.content.res.ColorStateList getThumbTintList();
-    method public android.graphics.PorterDuff.Mode getThumbTintMode();
-    method public android.graphics.drawable.Drawable getTrackDrawable();
-    method public android.content.res.ColorStateList getTrackTintList();
-    method public android.graphics.PorterDuff.Mode getTrackTintMode();
-    method public void onMeasure(int, int);
-    method public void setShowText(boolean);
-    method public void setSplitTrack(boolean);
-    method public void setSwitchMinWidth(int);
-    method public void setSwitchPadding(int);
-    method public void setSwitchTextAppearance(android.content.Context, int);
-    method public void setSwitchTypeface(android.graphics.Typeface, int);
-    method public void setSwitchTypeface(android.graphics.Typeface);
-    method public void setTextOff(java.lang.CharSequence);
-    method public void setTextOn(java.lang.CharSequence);
-    method public void setThumbDrawable(android.graphics.drawable.Drawable);
-    method public void setThumbResource(int);
-    method public void setThumbTextPadding(int);
-    method public void setThumbTintList(android.content.res.ColorStateList);
-    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
-    method public void setTrackDrawable(android.graphics.drawable.Drawable);
-    method public void setTrackResource(int);
-    method public void setTrackTintList(android.content.res.ColorStateList);
-    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
-  }
-
-  public abstract interface ThemedSpinnerAdapter implements android.widget.SpinnerAdapter {
-    method public abstract android.content.res.Resources.Theme getDropDownViewTheme();
-    method public abstract void setDropDownViewTheme(android.content.res.Resources.Theme);
-  }
-
-  public static final class ThemedSpinnerAdapter.Helper {
-    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
-    method public android.view.LayoutInflater getDropDownViewInflater();
-    method public android.content.res.Resources.Theme getDropDownViewTheme();
-    method public void setDropDownViewTheme(android.content.res.Resources.Theme);
-  }
-
-  public class Toolbar extends android.view.ViewGroup {
-    ctor public Toolbar(android.content.Context);
-    ctor public Toolbar(android.content.Context, android.util.AttributeSet);
-    ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
-    method public void collapseActionView();
-    method public void dismissPopupMenus();
-    method public int getContentInsetEnd();
-    method public int getContentInsetEndWithActions();
-    method public int getContentInsetLeft();
-    method public int getContentInsetRight();
-    method public int getContentInsetStart();
-    method public int getContentInsetStartWithNavigation();
-    method public int getCurrentContentInsetEnd();
-    method public int getCurrentContentInsetLeft();
-    method public int getCurrentContentInsetRight();
-    method public int getCurrentContentInsetStart();
-    method public android.graphics.drawable.Drawable getLogo();
-    method public java.lang.CharSequence getLogoDescription();
-    method public android.view.Menu getMenu();
-    method public java.lang.CharSequence getNavigationContentDescription();
-    method public android.graphics.drawable.Drawable getNavigationIcon();
-    method public android.graphics.drawable.Drawable getOverflowIcon();
-    method public int getPopupTheme();
-    method public java.lang.CharSequence getSubtitle();
-    method public java.lang.CharSequence getTitle();
-    method public int getTitleMarginBottom();
-    method public int getTitleMarginEnd();
-    method public int getTitleMarginStart();
-    method public int getTitleMarginTop();
-    method public boolean hasExpandedActionView();
-    method public boolean hideOverflowMenu();
-    method public void inflateMenu(int);
-    method public boolean isOverflowMenuShowing();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setContentInsetEndWithActions(int);
-    method public void setContentInsetStartWithNavigation(int);
-    method public void setContentInsetsAbsolute(int, int);
-    method public void setContentInsetsRelative(int, int);
-    method public void setLogo(int);
-    method public void setLogo(android.graphics.drawable.Drawable);
-    method public void setLogoDescription(int);
-    method public void setLogoDescription(java.lang.CharSequence);
-    method public void setNavigationContentDescription(int);
-    method public void setNavigationContentDescription(java.lang.CharSequence);
-    method public void setNavigationIcon(int);
-    method public void setNavigationIcon(android.graphics.drawable.Drawable);
-    method public void setNavigationOnClickListener(android.view.View.OnClickListener);
-    method public void setOnMenuItemClickListener(android.support.v7.widget.Toolbar.OnMenuItemClickListener);
-    method public void setOverflowIcon(android.graphics.drawable.Drawable);
-    method public void setPopupTheme(int);
-    method public void setSubtitle(int);
-    method public void setSubtitle(java.lang.CharSequence);
-    method public void setSubtitleTextAppearance(android.content.Context, int);
-    method public void setSubtitleTextColor(int);
-    method public void setTitle(int);
-    method public void setTitle(java.lang.CharSequence);
-    method public void setTitleMargin(int, int, int, int);
-    method public void setTitleMarginBottom(int);
-    method public void setTitleMarginEnd(int);
-    method public void setTitleMarginStart(int);
-    method public void setTitleMarginTop(int);
-    method public void setTitleTextAppearance(android.content.Context, int);
-    method public void setTitleTextColor(int);
-    method public boolean showOverflowMenu();
-  }
-
-  public static class Toolbar.LayoutParams extends android.support.v7.app.ActionBar.LayoutParams {
-    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
-    ctor public Toolbar.LayoutParams(int, int);
-    ctor public Toolbar.LayoutParams(int, int, int);
-    ctor public Toolbar.LayoutParams(int);
-    ctor public Toolbar.LayoutParams(android.support.v7.widget.Toolbar.LayoutParams);
-    ctor public Toolbar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams);
-    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
-    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
-  }
-
-  public static abstract interface Toolbar.OnMenuItemClickListener {
-    method public abstract boolean onMenuItemClick(android.view.MenuItem);
-  }
-
-  public static class Toolbar.SavedState extends android.support.v4.view.AbsSavedState {
-    ctor public Toolbar.SavedState(android.os.Parcel);
-    ctor public Toolbar.SavedState(android.os.Parcel, java.lang.ClassLoader);
-    ctor public Toolbar.SavedState(android.os.Parcelable);
-    field public static final android.os.Parcelable.Creator<android.support.v7.widget.Toolbar.SavedState> CREATOR;
-  }
-
-}
-
-package android.support.v7.widget.helper {
-
-  public class ItemTouchHelper extends android.support.v7.widget.RecyclerView.ItemDecoration implements android.support.v7.widget.RecyclerView.OnChildAttachStateChangeListener {
-    ctor public ItemTouchHelper(android.support.v7.widget.helper.ItemTouchHelper.Callback);
-    method public void attachToRecyclerView(android.support.v7.widget.RecyclerView);
-    method public void onChildViewAttachedToWindow(android.view.View);
-    method public void onChildViewDetachedFromWindow(android.view.View);
-    method public void startDrag(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void startSwipe(android.support.v7.widget.RecyclerView.ViewHolder);
-    field public static final int ACTION_STATE_DRAG = 2; // 0x2
-    field public static final int ACTION_STATE_IDLE = 0; // 0x0
-    field public static final int ACTION_STATE_SWIPE = 1; // 0x1
-    field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
-    field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
-    field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
-    field public static final int DOWN = 2; // 0x2
-    field public static final int END = 32; // 0x20
-    field public static final int LEFT = 4; // 0x4
-    field public static final int RIGHT = 8; // 0x8
-    field public static final int START = 16; // 0x10
-    field public static final int UP = 1; // 0x1
-  }
-
-  public static abstract class ItemTouchHelper.Callback {
-    ctor public ItemTouchHelper.Callback();
-    method public boolean canDropOver(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public android.support.v7.widget.RecyclerView.ViewHolder chooseDropTarget(android.support.v7.widget.RecyclerView.ViewHolder, java.util.List<android.support.v7.widget.RecyclerView.ViewHolder>, int, int);
-    method public void clearView(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public int convertToAbsoluteDirection(int, int);
-    method public static int convertToRelativeDirection(int, int);
-    method public long getAnimationDuration(android.support.v7.widget.RecyclerView, int, float, float);
-    method public int getBoundingBoxMargin();
-    method public static android.support.v7.widget.helper.ItemTouchUIUtil getDefaultUIUtil();
-    method public float getMoveThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public abstract int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public float getSwipeEscapeVelocity(float);
-    method public float getSwipeThreshold(android.support.v7.widget.RecyclerView.ViewHolder);
-    method public float getSwipeVelocityThreshold(float);
-    method public int interpolateOutOfBoundsScroll(android.support.v7.widget.RecyclerView, int, int, int, long);
-    method public boolean isItemViewSwipeEnabled();
-    method public boolean isLongPressDragEnabled();
-    method public static int makeFlag(int, int);
-    method public static int makeMovementFlags(int, int);
-    method public void onChildDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
-    method public void onChildDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, float, float, int, boolean);
-    method public abstract boolean onMove(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void onMoved(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, android.support.v7.widget.RecyclerView.ViewHolder, int, int, int);
-    method public void onSelectedChanged(android.support.v7.widget.RecyclerView.ViewHolder, int);
-    method public abstract void onSwiped(android.support.v7.widget.RecyclerView.ViewHolder, int);
-    field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
-    field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
-  }
-
-  public static abstract class ItemTouchHelper.SimpleCallback extends android.support.v7.widget.helper.ItemTouchHelper.Callback {
-    ctor public ItemTouchHelper.SimpleCallback(int, int);
-    method public int getDragDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public int getMovementFlags(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public int getSwipeDirs(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder);
-    method public void setDefaultDragDirs(int);
-    method public void setDefaultSwipeDirs(int);
-  }
-
-  public static abstract interface ItemTouchHelper.ViewDropHandler {
-    method public abstract void prepareForDrop(android.view.View, android.view.View, int, int);
-  }
-
-  public abstract interface ItemTouchUIUtil {
-    method public abstract void clearView(android.view.View);
-    method public abstract void onDraw(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
-    method public abstract void onDrawOver(android.graphics.Canvas, android.support.v7.widget.RecyclerView, android.view.View, float, float, int, boolean);
-    method public abstract void onSelected(android.view.View);
-  }
-
-}
-
-package android.support.v7.widget.util {
-
-  public abstract class SortedListAdapterCallback<T2> extends android.support.v7.util.SortedList.Callback {
-    ctor public SortedListAdapterCallback(android.support.v7.widget.RecyclerView.Adapter);
-    method public void onChanged(int, int);
-    method public void onInserted(int, int);
-    method public void onMoved(int, int);
-    method public void onRemoved(int, int);
-  }
-
-}
-
diff --git a/app-toolkit/.gitignore b/app-toolkit/.gitignore
new file mode 100644
index 0000000..be4e6f1
--- /dev/null
+++ b/app-toolkit/.gitignore
@@ -0,0 +1,4 @@
+local.properties
+maven-repo/
+build/
+*.DS_Store
diff --git a/app-toolkit/README.md b/app-toolkit/README.md
new file mode 100644
index 0000000..e36944f
--- /dev/null
+++ b/app-toolkit/README.md
@@ -0,0 +1,3 @@
+This is a wrapper project for flatfoot projects.
+You can use either individual projects or this one.
+Build server uses this project to build flatfoot.
\ No newline at end of file
diff --git a/app-toolkit/build.gradle b/app-toolkit/build.gradle
new file mode 100644
index 0000000..6a9d5a0
--- /dev/null
+++ b/app-toolkit/build.gradle
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+buildscript {
+    ext.supportRootFolder = new File(project.projectDir, "../")
+    apply from: 'buildSrc/repos.gradle'
+    apply from: 'init.gradle'
+    repos.addMavenRepositories(repositories)
+    dependencies {
+        classpath libs.jacoco
+        classpath libs.gradle
+        classpath libs.kotlin.gradle_plugin
+        if (enablePublicRepos) {
+            classpath libs.localize_maven
+        }
+    }
+}
diff --git a/app-toolkit/buildSrc b/app-toolkit/buildSrc
new file mode 120000
index 0000000..053a423
--- /dev/null
+++ b/app-toolkit/buildSrc
@@ -0,0 +1 @@
+../buildSrc
\ No newline at end of file
diff --git a/app-toolkit/common/build.gradle b/app-toolkit/common/build.gradle
new file mode 100644
index 0000000..2f54c2a
--- /dev/null
+++ b/app-toolkit/common/build.gradle
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'java'
+apply plugin: 'maven'
+
+sourceCompatibility = 1.7
+
+dependencies {
+    compile libs.support.annotations
+
+    testCompile libs.junit
+    testCompile libs.mockito_core
+}
+
+archivesBaseName = "common"
+
+createAndroidCheckstyle(project)
diff --git a/app-toolkit/common/src/main/java/android/arch/core/internal/FastSafeIterableMap.java b/app-toolkit/common/src/main/java/android/arch/core/internal/FastSafeIterableMap.java
new file mode 100644
index 0000000..dbd4d5f
--- /dev/null
+++ b/app-toolkit/common/src/main/java/android/arch/core/internal/FastSafeIterableMap.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.internal;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Poor's man LinkedHashMap, which supports modifications during iterations.
+ * Takes more memory that {@link SafeIterableMap}
+ * It is NOT thread safe.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class FastSafeIterableMap<K, V> extends SafeIterableMap<K, V> {
+
+    private HashMap<K, Entry<K, V>> mHashMap = new HashMap<>();
+
+    @Override
+    protected Entry<K, V> get(K k) {
+        return mHashMap.get(k);
+    }
+
+    @Override
+    public V putIfAbsent(@NonNull K key, @NonNull V v) {
+        Entry<K, V> current = get(key);
+        if (current != null) {
+            return current.mValue;
+        }
+        mHashMap.put(key, put(key, v));
+        return null;
+    }
+
+    @Override
+    public V remove(@NonNull K key) {
+        V removed = super.remove(key);
+        mHashMap.remove(key);
+        return removed;
+    }
+
+    /**
+     * Returns {@code true} if this map contains a mapping for the specified
+     * key.
+     */
+    public boolean contains(K key) {
+        return mHashMap.containsKey(key);
+    }
+
+    /**
+     * Return an entry added to prior to an entry associated with the given key.
+     *
+     * @param k the key
+     */
+    public Map.Entry<K, V> ceil(K k) {
+        if (contains(k)) {
+            return mHashMap.get(k).mPrevious;
+        }
+        return null;
+    }
+}
diff --git a/app-toolkit/common/src/main/java/android/arch/core/internal/SafeIterableMap.java b/app-toolkit/common/src/main/java/android/arch/core/internal/SafeIterableMap.java
new file mode 100644
index 0000000..16a7607
--- /dev/null
+++ b/app-toolkit/common/src/main/java/android/arch/core/internal/SafeIterableMap.java
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.internal;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * LinkedList, which pretends to be a map and supports modifications during iterations.
+ * It is NOT thread safe.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SafeIterableMap<K, V> implements Iterable<Map.Entry<K, V>> {
+
+    private Entry<K, V> mStart;
+    private Entry<K, V> mEnd;
+    // using WeakHashMap over List<WeakReference>, so we don't have to manually remove
+    // WeakReferences that have null in them.
+    private WeakHashMap<SupportRemove<K, V>, Boolean> mIterators = new WeakHashMap<>();
+    private int mSize = 0;
+
+    protected Entry<K, V> get(K k) {
+        Entry<K, V> currentNode = mStart;
+        while (currentNode != null) {
+            if (currentNode.mKey.equals(k)) {
+                break;
+            }
+            currentNode = currentNode.mNext;
+        }
+        return currentNode;
+    }
+
+    /**
+     * If the specified key is not already associated
+     * with a value, associates it with the given value.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param v   value to be associated with the specified key
+     * @return the previous value associated with the specified key,
+     * or {@code null} if there was no mapping for the key
+     */
+    public V putIfAbsent(@NonNull K key, @NonNull V v) {
+        Entry<K, V> entry = get(key);
+        if (entry != null) {
+            return entry.mValue;
+        }
+        put(key, v);
+        return null;
+    }
+
+    protected Entry<K, V> put(@NonNull K key, @NonNull V v) {
+        Entry<K, V> newEntry = new Entry<>(key, v);
+        mSize++;
+        if (mEnd == null) {
+            mStart = newEntry;
+            mEnd = mStart;
+            return newEntry;
+        }
+
+        mEnd.mNext = newEntry;
+        newEntry.mPrevious = mEnd;
+        mEnd = newEntry;
+        return newEntry;
+
+    }
+
+    /**
+     * Removes the mapping for a key from this map if it is present.
+     *
+     * @param key key whose mapping is to be removed from the map
+     * @return the previous value associated with the specified key,
+     * or {@code null} if there was no mapping for the key
+     */
+    public V remove(@NonNull K key) {
+        Entry<K, V> toRemove = get(key);
+        if (toRemove == null) {
+            return null;
+        }
+        mSize--;
+        if (!mIterators.isEmpty()) {
+            for (SupportRemove<K, V> iter : mIterators.keySet()) {
+                iter.supportRemove(toRemove);
+            }
+        }
+
+        if (toRemove.mPrevious != null) {
+            toRemove.mPrevious.mNext = toRemove.mNext;
+        } else {
+            mStart = toRemove.mNext;
+        }
+
+        if (toRemove.mNext != null) {
+            toRemove.mNext.mPrevious = toRemove.mPrevious;
+        } else {
+            mEnd = toRemove.mPrevious;
+        }
+
+        toRemove.mNext = null;
+        toRemove.mPrevious = null;
+        return toRemove.mValue;
+    }
+
+    /**
+     * @return the number of elements in this map
+     */
+    public int size() {
+        return mSize;
+    }
+
+    /**
+     * @return an ascending iterator, which doesn't include new elements added during an
+     * iteration.
+     */
+    @NonNull
+    @Override
+    public Iterator<Map.Entry<K, V>> iterator() {
+        ListIterator<K, V> iterator = new AscendingIterator<>(mStart, mEnd);
+        mIterators.put(iterator, false);
+        return iterator;
+    }
+
+    /**
+     * @return an descending iterator, which doesn't include new elements added during an
+     * iteration.
+     */
+    public Iterator<Map.Entry<K, V>> descendingIterator() {
+        DescendingIterator<K, V> iterator = new DescendingIterator<>(mEnd, mStart);
+        mIterators.put(iterator, false);
+        return iterator;
+    }
+
+    /**
+     * return an iterator with additions.
+     */
+    public IteratorWithAdditions iteratorWithAdditions() {
+        @SuppressWarnings("unchecked")
+        IteratorWithAdditions iterator = new IteratorWithAdditions();
+        mIterators.put(iterator, false);
+        return iterator;
+    }
+
+    /**
+     * @return eldest added entry or null
+     */
+    public Map.Entry<K, V> eldest() {
+        return mStart;
+    }
+
+    /**
+     * @return newest added entry or null
+     */
+    public Map.Entry<K, V> newest() {
+        return mEnd;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof SafeIterableMap)) {
+            return false;
+        }
+        SafeIterableMap map = (SafeIterableMap) obj;
+        if (this.size() != map.size()) {
+            return false;
+        }
+        Iterator<Map.Entry<K, V>> iterator1 = iterator();
+        Iterator iterator2 = map.iterator();
+        while (iterator1.hasNext() && iterator2.hasNext()) {
+            Map.Entry<K, V> next1 = iterator1.next();
+            Object next2 = iterator2.next();
+            if ((next1 == null && next2 != null)
+                    || (next1 != null && !next1.equals(next2))) {
+                return false;
+            }
+        }
+        return !iterator1.hasNext() && !iterator2.hasNext();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("[");
+        Iterator<Map.Entry<K, V>> iterator = iterator();
+        while (iterator.hasNext()) {
+            builder.append(iterator.next().toString());
+            if (iterator.hasNext()) {
+                builder.append(", ");
+            }
+        }
+        builder.append("]");
+        return builder.toString();
+    }
+
+    private abstract static class ListIterator<K, V> implements Iterator<Map.Entry<K, V>>,
+            SupportRemove<K, V> {
+        Entry<K, V> mExpectedEnd;
+        Entry<K, V> mNext;
+
+        ListIterator(Entry<K, V> start, Entry<K, V> expectedEnd) {
+            this.mExpectedEnd = expectedEnd;
+            this.mNext = start;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return mNext != null;
+        }
+
+        @Override
+        public void supportRemove(@NonNull Entry<K, V> entry) {
+            if (mExpectedEnd == entry && entry == mNext) {
+                mNext = null;
+                mExpectedEnd = null;
+            }
+
+            if (mExpectedEnd == entry) {
+                mExpectedEnd = backward(mExpectedEnd);
+            }
+
+            if (mNext == entry) {
+                mNext = nextNode();
+            }
+        }
+
+        private Entry<K, V> nextNode() {
+            if (mNext == mExpectedEnd || mExpectedEnd == null) {
+                return null;
+            }
+            return forward(mNext);
+        }
+
+        @Override
+        public Map.Entry<K, V> next() {
+            Map.Entry<K, V> result = mNext;
+            mNext = nextNode();
+            return result;
+        }
+
+        abstract Entry<K, V> forward(Entry<K, V> entry);
+
+        abstract Entry<K, V> backward(Entry<K, V> entry);
+    }
+
+    static class AscendingIterator<K, V> extends ListIterator<K, V> {
+        AscendingIterator(Entry<K, V> start, Entry<K, V> expectedEnd) {
+            super(start, expectedEnd);
+        }
+
+        @Override
+        Entry<K, V> forward(Entry<K, V> entry) {
+            return entry.mNext;
+        }
+
+        @Override
+        Entry<K, V> backward(Entry<K, V> entry) {
+            return entry.mPrevious;
+        }
+    }
+
+    private static class DescendingIterator<K, V> extends ListIterator<K, V> {
+
+        DescendingIterator(Entry<K, V> start, Entry<K, V> expectedEnd) {
+            super(start, expectedEnd);
+        }
+
+        @Override
+        Entry<K, V> forward(Entry<K, V> entry) {
+            return entry.mPrevious;
+        }
+
+        @Override
+        Entry<K, V> backward(Entry<K, V> entry) {
+            return entry.mNext;
+        }
+    }
+
+    private class IteratorWithAdditions implements Iterator<Map.Entry<K, V>>, SupportRemove<K, V> {
+        private Entry<K, V> mCurrent;
+        private boolean mFirstStep = true;
+
+        @Override
+        public void supportRemove(@NonNull Entry<K, V> entry) {
+            if (entry == mCurrent) {
+                mCurrent = mCurrent.mPrevious;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (mFirstStep) {
+                return mStart != null;
+            }
+            return mCurrent != null && mCurrent.mNext != null;
+        }
+
+        @Override
+        public Map.Entry<K, V> next() {
+            if (mFirstStep) {
+                mFirstStep = false;
+                mCurrent = mStart;
+            } else {
+                mCurrent = mCurrent != null ? mCurrent.mNext : null;
+            }
+            return mCurrent;
+        }
+    }
+
+    interface SupportRemove<K, V> {
+        void supportRemove(@NonNull Entry<K, V> entry);
+    }
+
+    static class Entry<K, V> implements Map.Entry<K, V> {
+        @NonNull
+        final K mKey;
+        @NonNull
+        final V mValue;
+        Entry<K, V> mNext;
+        Entry<K, V> mPrevious;
+
+        Entry(@NonNull K key, @NonNull V value) {
+            mKey = key;
+            this.mValue = value;
+        }
+
+        @NonNull
+        @Override
+        public K getKey() {
+            return mKey;
+        }
+
+        @NonNull
+        @Override
+        public V getValue() {
+            return mValue;
+        }
+
+        @Override
+        public V setValue(V value) {
+            throw new UnsupportedOperationException("An entry modification is not supported");
+        }
+
+        @Override
+        public String toString() {
+            return mKey + "=" + mValue;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (!(obj instanceof Entry)) {
+                return false;
+            }
+            Entry entry = (Entry) obj;
+            return mKey.equals(entry.mKey) && mValue.equals(entry.mValue);
+        }
+    }
+}
diff --git a/app-toolkit/common/src/main/java/android/arch/core/util/Function.java b/app-toolkit/common/src/main/java/android/arch/core/util/Function.java
new file mode 100644
index 0000000..25e7a6b
--- /dev/null
+++ b/app-toolkit/common/src/main/java/android/arch/core/util/Function.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.util;
+
+/**
+ * Represents a function.
+ *
+ * @param <I> the type of the input to the function
+ * @param <O> the type of the output of the function
+ */
+public interface Function<I, O> {
+    /**
+     * Applies this function to the given input.
+     *
+     * @param input the input
+     * @return the function result.
+     */
+    O apply(I input);
+}
diff --git a/app-toolkit/common/src/test/java/android/arch/core/internal/FastSafeIterableMapTest.java b/app-toolkit/common/src/test/java/android/arch/core/internal/FastSafeIterableMapTest.java
new file mode 100644
index 0000000..41b1497
--- /dev/null
+++ b/app-toolkit/common/src/test/java/android/arch/core/internal/FastSafeIterableMapTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.internal;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FastSafeIterableMapTest {
+    @Test
+    public void testCeil() {
+        FastSafeIterableMap<Integer, Boolean> map = new FastSafeIterableMap<>();
+        assertThat(map.ceil(1), nullValue());
+        map.putIfAbsent(1, false);
+        assertThat(map.ceil(1), nullValue());
+        map.putIfAbsent(2, false);
+        assertThat(map.ceil(2).getKey(), is(1));
+        map.remove(1);
+        assertThat(map.ceil(2), nullValue());
+    }
+
+    @Test
+    public void testPut() {
+        FastSafeIterableMap<Integer, Integer> map = new FastSafeIterableMap<>();
+        map.putIfAbsent(10, 20);
+        map.putIfAbsent(20, 40);
+        map.putIfAbsent(30, 60);
+        assertThat(map.putIfAbsent(5, 10), is((Integer) null));
+        assertThat(map.putIfAbsent(10, 30), is(20));
+    }
+
+    @Test
+    public void testContains() {
+        FastSafeIterableMap<Integer, Integer> map = new FastSafeIterableMap<>();
+        map.putIfAbsent(10, 20);
+        map.putIfAbsent(20, 40);
+        map.putIfAbsent(30, 60);
+        assertThat(map.contains(10), is(true));
+        assertThat(map.contains(11), is(false));
+        assertThat(new FastSafeIterableMap<Integer, Integer>().contains(0), is(false));
+    }
+
+
+    @Test
+    public void testRemove() {
+        FastSafeIterableMap<Integer, Integer> map = new FastSafeIterableMap<>();
+        map.putIfAbsent(10, 20);
+        map.putIfAbsent(20, 40);
+        assertThat(map.contains(10), is(true));
+        assertThat(map.contains(20), is(true));
+        assertThat(map.remove(10), is(20));
+        assertThat(map.contains(10), is(false));
+        assertThat(map.putIfAbsent(10, 30), nullValue());
+        assertThat(map.putIfAbsent(10, 40), is(30));
+    }
+}
diff --git a/app-toolkit/common/src/test/java/android/arch/core/internal/SafeIterableMapTest.java b/app-toolkit/common/src/test/java/android/arch/core/internal/SafeIterableMapTest.java
new file mode 100644
index 0000000..4b25e34
--- /dev/null
+++ b/app-toolkit/common/src/test/java/android/arch/core/internal/SafeIterableMapTest.java
@@ -0,0 +1,475 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.internal;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+@RunWith(JUnit4.class)
+public class SafeIterableMapTest {
+
+    @Test
+    public void testToString() {
+        SafeIterableMap<Integer, String> map = from(1, 2, 3, 4).to("a", "b", "c", "d");
+        assertThat(map.toString(), is("[1=a, 2=b, 3=c, 4=d]"));
+    }
+
+    @Test
+    public void testEmptyToString() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        assertThat(map.toString(), is("[]"));
+    }
+
+    @Test
+    public void testOneElementToString() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1);
+        assertThat(map.toString(), is("[1=true]"));
+    }
+
+
+    @Test
+    public void testEquality1() {
+        SafeIterableMap<Integer, Integer> map1 = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        SafeIterableMap<Integer, Integer> map2 = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        assertThat(map1.equals(map2), is(true));
+    }
+
+    @Test
+    public void testEquality2() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        //noinspection ObjectEqualsNull
+        assertThat(map.equals(null), is(false));
+    }
+
+    @Test
+    public void testEquality3() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        //noinspection EqualsBetweenInconvertibleTypes
+        assertThat(map.equals(new ArrayList<>()), is(false));
+    }
+
+    @Test
+    public void testEquality4() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        assertThat(map.equals(new SafeIterableMap<Integer, Boolean>()), is(false));
+    }
+
+    @Test
+    public void testEquality5() {
+        SafeIterableMap<Integer, Boolean> map1 = mapOf(1, 2, 3, 4);
+        SafeIterableMap<Integer, Boolean> map2 = mapOf(1);
+        assertThat(map1.equals(map2), is(false));
+    }
+
+    @Test
+    public void testEquality6() {
+        SafeIterableMap<Integer, Boolean> map1 = mapOf(1, 2, 3, 4);
+        SafeIterableMap<Integer, Boolean> map2 = mapOf(1, 2, 3, 5);
+        assertThat(map1.equals(map2), is(false));
+    }
+
+    @Test
+    public void testEquality7() {
+        SafeIterableMap<Integer, Integer> map1 = from(1, 2, 3, 4).to(1, 2, 3, 4);
+        SafeIterableMap<Integer, Integer> map2 = from(1, 2, 3, 4).to(1, 2, 3, 5);
+        assertThat(map1.equals(map2), is(false));
+    }
+
+
+    @Test
+    public void testEquality8() {
+        SafeIterableMap<Integer, Boolean> map1 = mapOf();
+        SafeIterableMap<Integer, Boolean> map2 = mapOf();
+        assertThat(map1.equals(map2), is(true));
+    }
+
+    @Test
+    public void testEqualityRespectsOrder() {
+        SafeIterableMap<Integer, Boolean> map1 = mapOf(1, 2, 3, 4);
+        SafeIterableMap<Integer, Boolean> map2 = mapOf(1, 3, 2, 4);
+        assertThat(map1.equals(map2), is(false));
+    }
+
+    @Test
+    public void testPut() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        assertThat(map.putIfAbsent(5, 10), is((Integer) null));
+        assertThat(map, is(from(1, 2, 3, 4, 5).to(10, 20, 30, 40, 10)));
+    }
+
+    @Test
+    public void testAddExisted() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2, 3, 4).to(10, 20, 261, 40);
+        assertThat(map.putIfAbsent(3, 239), is(261));
+        assertThat(map, is(from(1, 2, 3, 4).to(10, 20, 261, 40)));
+    }
+
+    @Test
+    public void testRemoveLast() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        assertThat(map.remove(4), is(40));
+        assertThat(map, is(from(1, 2, 3).to(10, 20, 30)));
+    }
+
+    @Test
+    public void testRemoveFirst() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        assertThat(map.remove(1), is(true));
+        assertThat(map, is(mapOf(2, 3, 4)));
+    }
+
+    @Test
+    public void testRemoveMiddle() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        assertThat(map.remove(2), is(20));
+        assertThat(map.remove(3), is(30));
+        assertThat(map, is(from(1, 4).to(10, 40)));
+    }
+
+    @Test
+    public void testRemoveNotExisted() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        assertThat(map.remove(5), is((Boolean) null));
+        assertThat(map, is(mapOf(1, 2, 3, 4)));
+    }
+
+    @Test
+    public void testRemoveSole() {
+        SafeIterableMap<Integer, Integer> map = from(1).to(261);
+        assertThat(map.remove(1), is(261));
+        assertThat(map, is(new SafeIterableMap<Integer, Integer>()));
+    }
+
+    @Test
+    public void testRemoveDuringIteration1() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        int index = 0;
+        int[] expected = new int[]{1, 4};
+        for (Entry<Integer, Integer> i : map) {
+            assertThat(i.getKey(), is(expected[index++]));
+            if (index == 1) {
+                assertThat(map.remove(2), is(20));
+                assertThat(map.remove(3), is(30));
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveDuringIteration2() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2).to(10, 20);
+        Iterator<Entry<Integer, Integer>> iter = map.iterator();
+        assertThat(map.remove(2), is(20));
+        assertThat(map.remove(1), is(10));
+        assertThat(iter.hasNext(), is(false));
+    }
+
+    @Test
+    public void testRemoveDuringIteration3() {
+        SafeIterableMap<Integer, Integer> map = from(1, 2, 3, 4).to(10, 20, 30, 40);
+        int index = 0;
+        Iterator<Entry<Integer, Integer>> iter = map.iterator();
+        assertThat(map.remove(1), is(10));
+        assertThat(map.remove(2), is(20));
+        int[] expected = new int[]{3, 4};
+        while (iter.hasNext()) {
+            assertThat(iter.next().getKey(), is(expected[index++]));
+        }
+    }
+
+    @Test
+    public void testAdditionDuringIteration() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{1, 2, 3, 4};
+        int index = 0;
+        for (Entry<Integer, Boolean> entry : map) {
+            assertThat(entry.getKey(), is(expected[index++]));
+            if (index == 1) {
+                map.putIfAbsent(5, true);
+            }
+        }
+    }
+
+    @Test
+    public void testReAdditionDuringIteration() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{1, 2, 4};
+        int index = 0;
+        for (Entry<Integer, Boolean> entry : map) {
+            assertThat(entry.getKey(), is(expected[index++]));
+            if (index == 1) {
+                map.remove(3);
+                map.putIfAbsent(3, true);
+            }
+        }
+    }
+
+    @Test
+    public void testSize() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        assertThat(map.size(), is(4));
+        map.putIfAbsent(5, true);
+        map.putIfAbsent(6, true);
+        assertThat(map.size(), is(6));
+        map.remove(5);
+        map.remove(5);
+        assertThat(map.size(), is(5));
+        map.remove(1);
+        map.remove(2);
+        map.remove(4);
+        map.remove(3);
+        map.remove(6);
+        assertThat(map.size(), is(0));
+        map.putIfAbsent(4, true);
+        assertThat(map.size(), is(1));
+        assertThat(mapOf().size(), is(0));
+    }
+
+    @Test
+    public void testIteratorWithAdditions1() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{1, 2, 3, 5};
+        int index = 0;
+        Iterator<Entry<Integer, Boolean>> iterator = map.iteratorWithAdditions();
+        while (iterator.hasNext()) {
+            Entry<Integer, Boolean> entry = iterator.next();
+            assertThat(entry.getKey(), is(expected[index++]));
+            if (index == 3) {
+                map.remove(4);
+                map.putIfAbsent(5, true);
+            }
+        }
+    }
+
+    @Test
+    public void testIteratorWithAdditions2() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1);
+        int[] expected = new int[]{1, 2, 3};
+        int index = 0;
+        Iterator<Entry<Integer, Boolean>> iterator = map.iteratorWithAdditions();
+        while (iterator.hasNext()) {
+            Entry<Integer, Boolean> entry = iterator.next();
+            assertThat(entry.getKey(), is(expected[index++]));
+            if (index == 1) {
+                map.putIfAbsent(2, true);
+                map.putIfAbsent(3, true);
+            }
+        }
+        assertThat(index, is(3));
+    }
+
+
+    @Test
+    public void testIteratorWithAdditions3() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3);
+        int[] expected = new int[]{1};
+        int index = 0;
+        Iterator<Entry<Integer, Boolean>> iterator = map.iteratorWithAdditions();
+        while (iterator.hasNext()) {
+            Entry<Integer, Boolean> entry = iterator.next();
+            assertThat(entry.getKey(), is(expected[index++]));
+            map.remove(2);
+            map.remove(3);
+        }
+        assertThat(index, is(1));
+    }
+
+    @Test
+    public void testIteratorWithAdditions4() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        int[] expected = new int[]{1, 2, 3};
+        int index = 0;
+        Iterator<Entry<Integer, Boolean>> iterator = map.iteratorWithAdditions();
+        map.putIfAbsent(1, true);
+        while (iterator.hasNext()) {
+            Entry<Integer, Boolean> entry = iterator.next();
+            assertThat(entry.getKey(), is(expected[index++]));
+            if (index == 1) {
+                map.putIfAbsent(2, false);
+            }
+            if (index == 2) {
+                map.putIfAbsent(3, false);
+            }
+        }
+        assertThat(index, is(3));
+    }
+
+    @Test
+    public void testDescendingIteration() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{4, 3, 2, 1};
+        int index = 0;
+        for (Iterator<Entry<Integer, Boolean>> iter = map.descendingIterator(); iter.hasNext(); ) {
+            assertThat(iter.next().getKey(), is(expected[index++]));
+        }
+        assertThat(index, is(4));
+    }
+
+    @Test
+    public void testDescendingIterationRemove1() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{4, 3, 2};
+        int index = 0;
+        for (Iterator<Entry<Integer, Boolean>> iter = map.descendingIterator(); iter.hasNext(); ) {
+            if (index == 1) {
+                map.remove(1);
+            }
+            assertThat(iter.next().getKey(), is(expected[index++]));
+        }
+        assertThat(index, is(3));
+        assertThat(map.size(), is(3));
+    }
+
+    @Test
+    public void testDescendingIterationRemove2() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{3, 2, 1};
+        int index = 0;
+        for (Iterator<Entry<Integer, Boolean>> iter = map.descendingIterator(); iter.hasNext(); ) {
+            if (index == 0) {
+                map.remove(4);
+            }
+            assertThat(iter.next().getKey(), is(expected[index++]));
+        }
+        assertThat(index, is(3));
+        assertThat(map.size(), is(3));
+    }
+
+    @Test
+    public void testDescendingIterationRemove3() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{4, 1};
+        int index = 0;
+        for (Iterator<Entry<Integer, Boolean>> iter = map.descendingIterator(); iter.hasNext(); ) {
+            if (index == 1) {
+                map.remove(3);
+                map.remove(2);
+            }
+            assertThat(iter.next().getKey(), is(expected[index++]));
+        }
+        assertThat(index, is(2));
+        assertThat(map.size(), is(2));
+    }
+
+    @Test
+    public void testDescendingIterationAddition() {
+        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2, 3, 4);
+        int[] expected = new int[]{4, 3, 2, 1};
+        int index = 0;
+        for (Iterator<Entry<Integer, Boolean>> iter = map.descendingIterator(); iter.hasNext(); ) {
+            if (index == 0) {
+                map.putIfAbsent(5, false);
+            }
+            assertThat(iter.next().getKey(), is(expected[index++]));
+        }
+        assertThat(index, is(4));
+        assertThat(map.size(), is(5));
+    }
+
+    @Test
+    public void testDescendingIteratorEmpty() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        Iterator<Entry<Integer, Boolean>> iterator = map.descendingIterator();
+        assertThat(iterator.hasNext(), is(false));
+    }
+
+    @Test
+    public void testIteratorEmpty() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        Iterator<Entry<Integer, Boolean>> iterator = map.iterator();
+        assertThat(iterator.hasNext(), is(false));
+    }
+
+    @Test
+    public void testIteratorWithAdditionEmpty() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        Iterator<Entry<Integer, Boolean>> iterator = map.iteratorWithAdditions();
+        assertThat(iterator.hasNext(), is(false));
+    }
+
+    @Test
+    public void testEldest() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        assertThat(map.eldest(), nullValue());
+        map.putIfAbsent(1, false);
+        assertThat(map.eldest().getKey(), is(1));
+        map.putIfAbsent(2, false);
+        assertThat(map.eldest().getKey(), is(1));
+        map.remove(1);
+        assertThat(map.eldest().getKey(), is(2));
+        map.remove(2);
+        assertThat(map.eldest(), nullValue());
+    }
+
+    @Test
+    public void testNewest() {
+        SafeIterableMap<Integer, Boolean> map = mapOf();
+        assertThat(map.newest(), nullValue());
+        map.putIfAbsent(1, false);
+        assertThat(map.newest().getKey(), is(1));
+        map.putIfAbsent(2, false);
+        assertThat(map.newest().getKey(), is(2));
+        map.remove(2);
+        assertThat(map.eldest().getKey(), is(1));
+        map.remove(1);
+        assertThat(map.newest(), nullValue());
+    }
+
+
+    // for most operations we don't care about values, so we create map from key to true
+    @SafeVarargs
+    private static <K> SafeIterableMap<K, Boolean> mapOf(K... keys) {
+        SafeIterableMap<K, Boolean> map = new SafeIterableMap<>();
+        for (K key : keys) {
+            map.putIfAbsent(key, true);
+        }
+        return map;
+    }
+
+    @SafeVarargs
+    private static <K> MapBuilder<K> from(K... keys) {
+        return new MapBuilder<>(keys);
+    }
+
+    private static class MapBuilder<K> {
+        final K[] mKeys;
+
+        MapBuilder(K[] keys) {
+            this.mKeys = keys;
+        }
+
+        @SafeVarargs
+        public final <V> SafeIterableMap<K, V> to(V... values) {
+            assertThat("Failed to build Map", mKeys.length, is(values.length));
+            SafeIterableMap<K, V> map = new SafeIterableMap<>();
+            for (int i = 0; i < mKeys.length; i++) {
+                map.putIfAbsent(mKeys[i], values[i]);
+            }
+            return map;
+        }
+    }
+}
+
+
diff --git a/app-toolkit/core-testing/build.gradle b/app-toolkit/core-testing/build.gradle
new file mode 100644
index 0000000..c757533
--- /dev/null
+++ b/app-toolkit/core-testing/build.gradle
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile project(":arch:runtime")
+    compile libs.support.annotations
+    compile libs.support.core_utils
+    compile(libs.junit) {
+        exclude module: 'hamcrest-core'
+    }
+    compile libs.mockito_core
+
+    testCompile libs.junit
+    testCompile libs.support.annotations
+
+    androidTestCompile libs.junit
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+}
+
+archivesBaseName = "core-testing"
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    project.tasks.create(name: "jar${suffix}", type: Jar) {
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/app-toolkit/core-testing/src/androidTest/java/android/arch/core/executor/testing/CountingTaskExecutorRuleTest.java b/app-toolkit/core-testing/src/androidTest/java/android/arch/core/executor/testing/CountingTaskExecutorRuleTest.java
new file mode 100644
index 0000000..ad36b9b
--- /dev/null
+++ b/app-toolkit/core-testing/src/androidTest/java/android/arch/core/executor/testing/CountingTaskExecutorRuleTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor.testing;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class CountingTaskExecutorRuleTest {
+    private final Semaphore mOnIdleCount = new Semaphore(0);
+
+    @Rule
+    public CountingTaskExecutorRule mRule = new CountingTaskExecutorRule() {
+        @Override
+        protected void onIdle() {
+            super.onIdle();
+            mOnIdleCount.release(1);
+        }
+    };
+
+    @Test
+    public void initialIdle() {
+        assertThat(mRule.isIdle(), is(true));
+    }
+
+    @Test
+    public void busyIO() throws InterruptedException {
+        LatchRunnable task = runOnIO();
+        singleTaskTest(task);
+    }
+
+    @Test
+    public void busyMain() throws InterruptedException {
+        LatchRunnable task = runOnMain();
+        singleTaskTest(task);
+    }
+
+    @Test
+    public void multipleTasks() throws InterruptedException {
+        List<LatchRunnable> latches = new ArrayList<>(10);
+        for (int i = 0; i < 5; i++) {
+            latches.add(runOnIO());
+            latches.add(runOnMain());
+        }
+        assertNotIdle();
+        for (int i = 0; i < 9; i++) {
+            latches.get(i).start();
+        }
+        for (int i = 0; i < 9; i++) {
+            latches.get(i).await();
+        }
+        assertNotIdle();
+
+        LatchRunnable another = runOnIO();
+        latches.get(9).startAndFinish();
+        assertNotIdle();
+
+        another.startAndFinish();
+        assertBecomeIdle();
+
+        LatchRunnable oneMore = runOnMain();
+
+        assertNotIdle();
+
+        oneMore.startAndFinish();
+        assertBecomeIdle();
+    }
+
+    private void assertNotIdle() throws InterruptedException {
+        assertThat(mOnIdleCount.tryAcquire(300, TimeUnit.MILLISECONDS), is(false));
+        assertThat(mRule.isIdle(), is(false));
+    }
+
+    private void assertBecomeIdle() throws InterruptedException {
+        assertThat(mOnIdleCount.tryAcquire(1, TimeUnit.SECONDS), is(true));
+        assertThat(mRule.isIdle(), is(true));
+    }
+
+    private void singleTaskTest(LatchRunnable task)
+            throws InterruptedException {
+        assertNotIdle();
+        task.startAndFinish();
+        assertBecomeIdle();
+    }
+
+    private LatchRunnable runOnIO() {
+        LatchRunnable latchRunnable = new LatchRunnable();
+        AppToolkitTaskExecutor.getInstance().executeOnDiskIO(latchRunnable);
+        return latchRunnable;
+    }
+
+    private LatchRunnable runOnMain() {
+        LatchRunnable latchRunnable = new LatchRunnable();
+        AppToolkitTaskExecutor.getInstance().executeOnMainThread(latchRunnable);
+        return latchRunnable;
+    }
+
+    @Test
+    public void drainFailure() throws InterruptedException {
+        runOnIO();
+        try {
+            mRule.drainTasks(300, TimeUnit.MILLISECONDS);
+            throw new AssertionError("drain should fail");
+        } catch (TimeoutException ignored) {
+        }
+    }
+
+    @Test
+    public void drainSuccess() throws TimeoutException, InterruptedException {
+        final LatchRunnable task = runOnIO();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(300);
+                } catch (InterruptedException ignored) {
+                }
+                task.start();
+            }
+        }).start();
+        mRule.drainTasks(1, TimeUnit.SECONDS);
+    }
+
+    private static class LatchRunnable implements Runnable {
+        private final CountDownLatch mStart = new CountDownLatch(1);
+        private final CountDownLatch mEnd = new CountDownLatch(1);
+
+        @Override
+        public void run() {
+            try {
+                mStart.await(10, TimeUnit.SECONDS);
+                mEnd.countDown();
+            } catch (InterruptedException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        void await() throws InterruptedException {
+            mEnd.await(10, TimeUnit.SECONDS);
+        }
+
+        void start() {
+            mStart.countDown();
+        }
+
+        private void startAndFinish() throws InterruptedException {
+            start();
+            await();
+        }
+    }
+}
diff --git a/app-toolkit/core-testing/src/main/AndroidManifest.xml b/app-toolkit/core-testing/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..83e0d83
--- /dev/null
+++ b/app-toolkit/core-testing/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest package="android.arch.core.testing">
+</manifest>
diff --git a/app-toolkit/core-testing/src/main/java/android/arch/core/executor/JunitTaskExecutorRule.java b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/JunitTaskExecutorRule.java
new file mode 100644
index 0000000..cd4f8f5
--- /dev/null
+++ b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/JunitTaskExecutorRule.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor;
+
+import android.support.annotation.RestrictTo;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.MultipleFailureException;
+import org.junit.runners.model.Statement;
+import org.mockito.Mockito;
+
+import java.util.List;
+
+/**
+ * A JUnit rule that swaps the task executor with a more controllable one.
+ * Once we have the TaskExecutor API, we should consider making this public (via some test package).
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class JunitTaskExecutorRule implements TestRule {
+    private final TaskExecutorWithFakeMainThread mTaskExecutor;
+
+    public JunitTaskExecutorRule(int ioThreadCount, boolean spyOnExecutor) {
+        if (spyOnExecutor) {
+            mTaskExecutor = Mockito.spy(new TaskExecutorWithFakeMainThread(ioThreadCount));
+        } else {
+            mTaskExecutor = new TaskExecutorWithFakeMainThread(ioThreadCount);
+        }
+
+    }
+
+    private void beforeStart() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(mTaskExecutor);
+    }
+
+    private void afterFinished() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    public TaskExecutor getTaskExecutor() {
+        return mTaskExecutor;
+    }
+
+    /**
+     * Awaits while all currently posted tasks will be finished
+     *
+     * @param seconds timeout in seconds
+     */
+    public void drainTasks(int seconds) throws InterruptedException {
+        mTaskExecutor.drainTasks(seconds);
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                beforeStart();
+                try {
+                    base.evaluate();
+                    finishExecutors();
+                } catch (Throwable t) {
+                    throw new RuntimeException(t);
+                } finally {
+                    afterFinished();
+                }
+            }
+        };
+    }
+
+    private void finishExecutors() throws InterruptedException, MultipleFailureException {
+        mTaskExecutor.shutdown(10);
+        final List<Throwable> errors = mTaskExecutor.getErrors();
+        if (!errors.isEmpty()) {
+            throw new MultipleFailureException(errors);
+        }
+    }
+}
diff --git a/app-toolkit/core-testing/src/main/java/android/arch/core/executor/TaskExecutorWithFakeMainThread.java b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/TaskExecutorWithFakeMainThread.java
new file mode 100644
index 0000000..af0aca4
--- /dev/null
+++ b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/TaskExecutorWithFakeMainThread.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A TaskExecutor that has a real thread for main thread operations and can wait for execution etc.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class TaskExecutorWithFakeMainThread extends TaskExecutor {
+    private List<Throwable> mCaughtExceptions = Collections.synchronizedList(new ArrayList
+            <Throwable>());
+
+    private ExecutorService mIOService;
+
+    private Thread mMainThread;
+    private final int mIOThreadCount;
+
+    private ExecutorService mMainThreadService =
+            Executors.newSingleThreadExecutor(new ThreadFactory() {
+                @Override
+                public Thread newThread(@NonNull final Runnable r) {
+                    mMainThread = new LoggingThread(r);
+                    return mMainThread;
+                }
+            });
+
+    public TaskExecutorWithFakeMainThread(int ioThreadCount) {
+        mIOThreadCount = ioThreadCount;
+        mIOService = Executors.newFixedThreadPool(ioThreadCount, new ThreadFactory() {
+            @Override
+            public Thread newThread(@NonNull Runnable r) {
+                return new LoggingThread(r);
+            }
+        });
+    }
+
+    @Override
+    public void executeOnDiskIO(Runnable runnable) {
+        mIOService.execute(runnable);
+    }
+
+    @Override
+    public void postToMainThread(Runnable runnable) {
+        // Tasks in SingleThreadExecutor are guaranteed to execute sequentially,
+        // and no more than one task will be active at any given time.
+        // So if we call this method from the main thread, new task will be scheduled,
+        // which is equivalent to post.
+        mMainThreadService.execute(runnable);
+    }
+
+    @Override
+    public boolean isMainThread() {
+        return Thread.currentThread() == mMainThread;
+    }
+
+    List<Throwable> getErrors() {
+        return mCaughtExceptions;
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    void shutdown(int timeoutInSeconds) throws InterruptedException {
+        mMainThreadService.shutdown();
+        mIOService.shutdown();
+        mMainThreadService.awaitTermination(timeoutInSeconds, TimeUnit.SECONDS);
+        mIOService.awaitTermination(timeoutInSeconds, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Drains tasks at the given time limit
+     * @param seconds Number of seconds to wait
+     * @throws InterruptedException
+     */
+    public void drainTasks(int seconds) throws InterruptedException {
+        if (isMainThread()) {
+            throw new IllegalStateException();
+        }
+        final CountDownLatch enterLatch = new CountDownLatch(mIOThreadCount);
+        final CountDownLatch exitLatch = new CountDownLatch(1);
+        for (int i = 0; i < mIOThreadCount; i++) {
+            executeOnDiskIO(new Runnable() {
+                @Override
+                public void run() {
+                    enterLatch.countDown();
+                    try {
+                        exitLatch.await();
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+        }
+
+        final CountDownLatch mainLatch = new CountDownLatch(1);
+        postToMainThread(new Runnable() {
+            @Override
+            public void run() {
+                mainLatch.countDown();
+            }
+        });
+        if (!enterLatch.await(seconds, TimeUnit.SECONDS)) {
+            throw new AssertionError("Could not drain IO tasks in " + seconds
+                    + " seconds");
+        }
+        exitLatch.countDown();
+        if (!mainLatch.await(seconds, TimeUnit.SECONDS)) {
+            throw new AssertionError("Could not drain UI tasks in " + seconds
+                    + " seconds");
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    class LoggingThread extends Thread {
+        LoggingThread(final Runnable target) {
+            super(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        target.run();
+                    } catch (Throwable t) {
+                        mCaughtExceptions.add(t);
+                    }
+                }
+            });
+        }
+    }
+}
diff --git a/app-toolkit/core-testing/src/main/java/android/arch/core/executor/testing/CountingTaskExecutorRule.java b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/testing/CountingTaskExecutorRule.java
new file mode 100644
index 0000000..ad930aa
--- /dev/null
+++ b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/testing/CountingTaskExecutorRule.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor.testing;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.DefaultTaskExecutor;
+import android.os.SystemClock;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A JUnit Test Rule that swaps the background executor used by the Architecture Components with a
+ * different one which counts the tasks as they are start and finish.
+ * <p>
+ * You can use this rule for your host side tests that use Architecture Components.
+ */
+public class CountingTaskExecutorRule extends TestWatcher {
+    private final Object mCountLock = new Object();
+    private int mTaskCount = 0;
+
+    @Override
+    protected void starting(Description description) {
+        super.starting(description);
+        AppToolkitTaskExecutor.getInstance().setDelegate(new DefaultTaskExecutor() {
+            @Override
+            public void executeOnDiskIO(Runnable runnable) {
+                super.executeOnDiskIO(new CountingRunnable(runnable));
+            }
+
+            @Override
+            public void postToMainThread(Runnable runnable) {
+                super.postToMainThread(new CountingRunnable(runnable));
+            }
+        });
+    }
+
+    @Override
+    protected void finished(Description description) {
+        super.finished(description);
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    private void increment() {
+        synchronized (mCountLock) {
+            mTaskCount++;
+        }
+    }
+
+    private void decrement() {
+        synchronized (mCountLock) {
+            mTaskCount--;
+            if (mTaskCount == 0) {
+                onIdle();
+                mCountLock.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Called when the number of awaiting tasks reaches to 0.
+     *
+     * @see #isIdle()
+     */
+    protected void onIdle() {
+
+    }
+
+    /**
+     * Returns false if there are tasks waiting to be executed, true otherwise.
+     *
+     * @return False if there are tasks waiting to be executed, true otherwise.
+     *
+     * @see #onIdle()
+     */
+    public boolean isIdle() {
+        synchronized (mCountLock) {
+            return mTaskCount == 0;
+        }
+    }
+
+    /**
+     * Waits until all active tasks are finished.
+     *
+     * @param time The duration to wait
+     * @param timeUnit The time unit for the {@code time} parameter
+     *
+     * @throws InterruptedException If thread is interrupted while waiting
+     * @throws TimeoutException If tasks cannot be drained at the given time
+     */
+    public void drainTasks(int time, TimeUnit timeUnit)
+            throws InterruptedException, TimeoutException {
+        long end = SystemClock.uptimeMillis() + timeUnit.toMillis(time);
+        synchronized (mCountLock) {
+            while (mTaskCount != 0) {
+                long now = SystemClock.uptimeMillis();
+                long remaining = end - now;
+                if (remaining > 0) {
+                    mCountLock.wait(remaining);
+                } else {
+                    throw new TimeoutException("could not drain tasks");
+                }
+            }
+        }
+    }
+
+    class CountingRunnable implements Runnable {
+        final Runnable mWrapped;
+
+        CountingRunnable(Runnable wrapped) {
+            mWrapped = wrapped;
+            increment();
+        }
+
+        @Override
+        public void run() {
+            try {
+                mWrapped.run();
+            } finally {
+                decrement();
+            }
+        }
+    }
+}
diff --git a/app-toolkit/core-testing/src/main/java/android/arch/core/executor/testing/InstantTaskExecutorRule.java b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/testing/InstantTaskExecutorRule.java
new file mode 100644
index 0000000..07dcf1f
--- /dev/null
+++ b/app-toolkit/core-testing/src/main/java/android/arch/core/executor/testing/InstantTaskExecutorRule.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor.testing;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.TaskExecutor;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/**
+ * A JUnit Test Rule that swaps the background executor used by the Architecture Components with a
+ * different one which executes each task synchronously.
+ * <p>
+ * You can use this rule for your host side tests that use Architecture Components.
+ */
+public class InstantTaskExecutorRule extends TestWatcher {
+    @Override
+    protected void starting(Description description) {
+        super.starting(description);
+        AppToolkitTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+            @Override
+            public void executeOnDiskIO(Runnable runnable) {
+                runnable.run();
+            }
+
+            @Override
+            public void postToMainThread(Runnable runnable) {
+                runnable.run();
+            }
+
+            @Override
+            public boolean isMainThread() {
+                return true;
+            }
+        });
+    }
+
+    @Override
+    protected void finished(Description description) {
+        super.finished(description);
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+}
diff --git a/app-toolkit/core-testing/src/test/java/android/arch/core/executor/testing/InstantTaskExecutorRuleTest.java b/app-toolkit/core-testing/src/test/java/android/arch/core/executor/testing/InstantTaskExecutorRuleTest.java
new file mode 100644
index 0000000..4345fd1
--- /dev/null
+++ b/app-toolkit/core-testing/src/test/java/android/arch/core/executor/testing/InstantTaskExecutorRuleTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor.testing;
+
+import static org.junit.Assert.assertTrue;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(JUnit4.class)
+public class InstantTaskExecutorRuleTest {
+    @Rule
+    public InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
+
+    @Test
+    public void executeOnMain() throws ExecutionException, InterruptedException, TimeoutException {
+        final Thread current = Thread.currentThread();
+        FutureTask<Void> check = new FutureTask<>(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertTrue(Thread.currentThread() == current);
+                return null;
+            }
+        });
+        AppToolkitTaskExecutor.getInstance().executeOnMainThread(check);
+        check.get(1, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void executeOnIO() throws ExecutionException, InterruptedException, TimeoutException {
+        final Thread current = Thread.currentThread();
+        FutureTask<Void> check = new FutureTask<>(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertTrue(Thread.currentThread() == current);
+                return null;
+            }
+        });
+        AppToolkitTaskExecutor.getInstance().executeOnDiskIO(check);
+        check.get(1, TimeUnit.SECONDS);
+    }
+}
diff --git a/app-toolkit/dependencies.gradle b/app-toolkit/dependencies.gradle
new file mode 100644
index 0000000..bec804a
--- /dev/null
+++ b/app-toolkit/dependencies.gradle
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+def ffLibs
+if (hasProperty("libs")) {
+    ffLibs = libs
+} else {
+    apply from: "${ext.supportRootFolder}/buildSrc/dependencies.gradle"
+    ffLibs = libs
+}
+def ffVersions = [:]
+ffVersions.kotlin = "1.1.3"
+ffVersions.auto_common = "0.6"
+ffVersions.javapoet = "1.8.0"
+ffVersions.compile_testing = "0.11"
+ffVersions.localize_maven = "1.2"
+ffVersions.support_lib = "26.0.0"
+ffVersions.intellij_annotations = "12.0"
+ffVersions.rxjava2 = "2.0.6"
+ffVersions.reactivestreams = "1.0.0"
+// this Xerial version is newer than we want but we need it to fix
+// https://github.com/xerial/sqlite-jdbc/issues/97
+ffVersions.xerial = "3.16.1"
+ffVersions.antlr = "4.5.3"
+ffVersions.commons_codec = "1.10"
+ffVersions.gson = "2.8.0"
+ffVersions.guava = "21.0"
+
+ffLibs.kotlin = [
+        stdlib : "org.jetbrains.kotlin:kotlin-stdlib:$ffVersions.kotlin",
+        gradle_plugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$ffVersions.kotlin"
+]
+ffLibs.auto_common = "com.google.auto:auto-common:$ffVersions.auto_common"
+ffLibs.apache = [
+    commons : [
+            codec : "commons-codec:commons-codec:$ffVersions.commons_codec"
+    ]
+]
+
+def getSupportLib(String name, String version, String artifactName = null) {
+    def sourceProject = findProject(name)
+    if (sourceProject != null) {
+        return sourceProject
+    }
+    if (artifactName == null) {
+        artifactName = name
+    }
+    return "com.android.support$artifactName:$version"
+}
+ffLibs.support = [
+        annotations : getSupportLib(":support-annotations", ffVersions.support_lib),
+        core_utils : getSupportLib(':support-core-utils', ffVersions.support_lib),
+        fragments : getSupportLib(':support-fragment', ffVersions.support_lib),
+        app_compat : getSupportLib(':support-appcompat-v7', ffVersions.support_lib, ":appcompat-v7"),
+        design : getSupportLib(':support-design', ffVersions.support_lib, ":design"),
+        recyclerview : getSupportLib(':support-recyclerview-v7', ffVersions.support_lib, ":recyclerview-v7")
+]
+
+ffLibs.localize_maven = "com.android.databinding:localizemaven:$ffVersions.localize_maven"
+ffLibs.javapoet = "com.squareup:javapoet:$ffVersions.javapoet"
+ffLibs.antlr = "org.antlr:antlr4:$ffVersions.antlr"
+ffLibs.xerial = "org.xerial:sqlite-jdbc:$ffVersions.xerial"
+ffLibs.google_compile_testing = "com.google.testing.compile:compile-testing:$ffVersions.compile_testing"
+ffLibs.ij_annotations = "com.intellij:annotations:$ffVersions.intellij_annotations"
+ffLibs.reactive_streams = "org.reactivestreams:reactive-streams:$ffVersions.reactivestreams"
+ffLibs.rx_java = "io.reactivex.rxjava2:rxjava:$ffVersions.rxjava2"
+ffLibs.gson = "com.google.code.gson:gson:$ffVersions.gson"
+ffLibs.guava= "com.google.guava:guava:$ffVersions.guava"
+
+ext.tools = [:]
+ext.tools.current_sdk = gradle.ext.currentSdk
+ext.tools.build_tools_version = rootProject.ext.buildToolsVersion
+ext.flatfoot = [:]
+ext.flatfoot.release_version = "1.0.0-alpha8"
+ext.flatfoot.min_sdk = 14
diff --git a/app-toolkit/gradle.properties b/app-toolkit/gradle.properties
new file mode 100644
index 0000000..3e1a1a6
--- /dev/null
+++ b/app-toolkit/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx3000M
+org.gradle.daemon=true
+org.gradle.configureondemand=false
+org.gradle.parallel=false
\ No newline at end of file
diff --git a/app-toolkit/gradle/wrapper/gradle-wrapper.jar b/app-toolkit/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..d6e2637
--- /dev/null
+++ b/app-toolkit/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/app-toolkit/gradle/wrapper/gradle-wrapper.properties b/app-toolkit/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1b987ef
--- /dev/null
+++ b/app-toolkit/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Aug 16 10:43:36 PDT 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=../../../../../tools/external/gradle/gradle-4.1-milestone-1-bin.zip
diff --git a/app-toolkit/gradlew b/app-toolkit/gradlew
new file mode 100755
index 0000000..4ef3a87
--- /dev/null
+++ b/app-toolkit/gradlew
@@ -0,0 +1,171 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+for s in "${@}" ; do
+    s=\"$s\"
+    APP_ARGS=$APP_ARGS" "$s
+done
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- "$DEFAULT_JVM_OPTS" "$JAVA_OPTS" "$GRADLE_OPTS" "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/app-toolkit/gradlew.bat b/app-toolkit/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/app-toolkit/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windows variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
new file mode 100644
index 0000000..70e518a
--- /dev/null
+++ b/app-toolkit/init.gradle
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+import org.gradle.internal.os.OperatingSystem
+
+def root = ext.supportRootFolder
+ext.usePrebuilts = "true" // for doclava
+ext.inAppToolkitProject = rootProject.name == "app-toolkit"
+
+if (ext.inAppToolkitProject) {
+    apply from: "${ext.supportRootFolder}/buildSrc/init.gradle"
+    init.loadDefaultVersions()
+    init.setSdkInLocalPropertiesFile()
+}
+
+def checkoutRoot = "${root}/../.."
+ext.checkoutRoot = checkoutRoot
+ext.prebuiltsRoot = "$checkoutRoot/prebuilts"
+ext.prebuiltsRootUri = "file://${prebuiltsRoot}"
+
+final String platform = OperatingSystem.current().isMacOsX() ? 'darwin' : 'linux'
+final String fullSdkPath = new File("${checkoutRoot}/prebuilts/fullsdk-${platform}").getCanonicalPath()
+System.setProperty('android.home', fullSdkPath)
+File props = file("local.properties")
+props.write "sdk.dir=${fullSdkPath}"
+
+def buildDir
+def distDir
+def supportLibBuildDir
+
+if (ext.runningInBuildServer) {
+    supportLibBuildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build').getCanonicalFile();
+    buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/app-toolkit/build').getCanonicalFile()
+    distDir = new File(System.env.DIST_DIR).getCanonicalFile()
+} else {
+    supportLibBuildDir = file("${checkoutRoot}/out/host/gradle/frameworks/support/build")
+    buildDir = file("${checkoutRoot}/out/host/gradle/frameworks/app-toolkit/build")
+    distDir = file("${checkoutRoot}/out/dist")
+}
+
+def localMavenRepo = "file://${new File(buildDir, "flatfoot_repo").absolutePath}"
+ext.testApkDistOut = distDir
+ext.testResultsDistDir = new File(distDir, "host-test-reports")
+ext.localMavenRepo = localMavenRepo
+file(localMavenRepo).delete()
+file(localMavenRepo).mkdirs()
+
+ext.repoNames = ["$prebuiltsRootUri/maven_repo",
+                 "$prebuiltsRootUri/gradle-plugin",
+                 "$prebuiltsRootUri/tools/common/m2/repository",
+                 "$prebuiltsRootUri/tools/common/m2/internal",
+                 "$prebuiltsRootUri/tools/common/offline-m2",
+                 "$prebuiltsRootUri/maven_repo/android",
+                 "file://$fullSdkPath/extras/android/m2repository",
+                 "file://${new File(supportLibBuildDir, "support_repo").absolutePath}"]
+
+apply from: "${ext.supportRootFolder}/app-toolkit/dependencies.gradle"
+ext.enablePublicRepos = System.getenv("ALLOW_PUBLIC_REPOS")
+
+// repository creation task
+def buildServerAnchorTask = rootProject.tasks.create(name : "runBuildServerCompilationTasks",
+    description: "Anchor task for everything we want to run in build server.")
+
+if (ext.inAppToolkitProject) {
+    // always build offline docs for flatfoot specific builds.
+    ext.docs.dac = [
+            libraryroot: "android/arch",
+            dataname: "ARCH_DATA"
+    ]
+    repos.addMavenRepositories(repositories)
+    init.setupRepoOutAndBuildNumber()
+    init.configureSubProjects()
+    init.setupRelease()
+    init.enableDoclavaAndJDiff(this)
+    rootProject.tasks["generateDocs"].exclude '**/R.java'
+}
+
+
+// flatfoot docs
+def zipFlatfootDocsTask = rootProject.tasks.create(name : "createFlatfootDocsArchive", type : Zip) {
+    from rootProject.docsDir
+    destinationDir distDir
+    baseName = String.format("flatfoot-docs-%s", rootProject.ext.flatfoot.release_version)
+}
+
+buildServerAnchorTask.dependsOn zipFlatfootDocsTask
+zipFlatfootDocsTask.dependsOn rootProject.tasks["generateDocs"]
+
+// Disable API checks for now.
+checkApiRelease.enabled = false
+checkApi.enabled = false
+generateApi.enabled = false
+
+buildServerAnchorTask.dependsOn createArchive
+
+rootProject.ext.flatfootProjectGroups = [
+        "room" : "android.arch.persistence.room",
+        "lifecycle" : "android.arch.lifecycle",
+        "arch" : "android.arch.core",
+        "paging" : "android.arch.paging",
+        "navigation" : "android.arch.navigation"]
+
+subprojects {
+    repos.addMavenRepositories(project.repositories)
+    if (project.name == 'doclava' || project.name == 'jdiff') {
+        project.tasks.whenTaskAdded { task ->
+            if (task instanceof org.gradle.api.tasks.testing.Test) {
+                task.enabled = false
+            }
+        }
+        return
+    }
+
+    def mavenGroup = project.getPath().split(":")[1]
+    def finalGroup = rootProject.flatfootProjectGroups[mavenGroup]
+
+    if (finalGroup == null) {
+        return
+    }
+    project.group = finalGroup
+    project.version = flatfoot.release_version
+
+    if (project.getPath().contains("integration-tests")) {
+        // disable upload tasks
+        project.tasks.whenTaskAdded { task ->
+            if (task instanceof Upload || task.name == "generateSourceProps") {
+                task.enabled = false
+            }
+        }
+    } else {
+        // add license to pom
+        project.tasks.whenTaskAdded { task ->
+            if (task instanceof Upload) {
+                task.repositories {
+                    mavenDeployer {
+                        repository(url: project.uri(project.rootProject.supportRepoOut))
+                        pom.project {
+                            url "https://developer.android.com/topic/libraries/architecture/index.html"
+                            inceptionYear 2017
+                            licenses {
+                                license {
+                                    name 'The Apache Software License, Version 2.0'
+                                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                                    distribution 'repo'
+                                }
+                            }
+                            scm {
+                                url "http://source.android.com"
+                                connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
+                            }
+                            developers {
+                                developer {
+                                    name 'The Android Open Source Project'
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    project.plugins.whenPluginAdded { plugin ->
+        def isAndroidLibrary = "com.android.build.gradle.LibraryPlugin"
+                .equals(plugin.class.name)
+        def isJavaLibrary = "org.gradle.api.plugins.JavaPlugin".equals(plugin.class.name)
+        if (isAndroidLibrary) {
+            // it is an android lib, enable sources.
+            def sourcesTask = project.tasks.create(name: "sourcesJar", type : Jar) {
+                classifier = 'sources'
+                from android.sourceSets.main.getJava().getSrcDirs()
+            }
+            project.artifacts {
+                archives sourcesTask
+            }
+        } else if(isJavaLibrary && project.name == "common") {
+            // it is a shared lib, enable sources.
+            def sourcesTask = project.tasks.create(name: "sourcesJar", type : Jar) {
+                classifier = 'sources'
+                from sourceSets.main.allSource
+            }
+            project.artifacts {
+                archives sourcesTask
+            }
+        }
+    }
+
+    project.tasks.whenTaskAdded { task ->
+        if (task.name.startsWith("assembleAndroidTest")) {
+            buildServerAnchorTask.dependsOn task
+        }
+        if (task.name.startsWith("assembleDebug")) {
+            buildServerAnchorTask.dependsOn task
+        }
+    }
+
+    if (enablePublicRepos) {
+        project.afterEvaluate {
+            apply plugin: 'com.android.databinding.localizemaven'
+            project.localizeMaven {
+                localRepoDir = file("$prebuiltsRoot/tools/common/m2/repository")
+                otherRepoDirs = repoNames
+                licenseInformation = [
+                        [
+                                libraries: ["org.jetbrains:annotations",
+                                            "org.jetbrains.kotlin:kotlin-compiler-runner"],
+                                licenses : ["http://www.apache.org/licenses/LICENSE-2.0.txt"]
+                        ],
+                        [
+                                libraries: ["com.google.code.gson:gson"],
+                                licenses : ["http://www.apache.org/licenses/LICENSE-2.0.txt"]
+                        ]
+
+                ]
+            }
+        }
+    }
+}
+
+def createKotlinCheckstyle(Project project) {
+    def fs = files();
+    if (project.sourceSets.hasProperty('main')) {
+        fs += files(project.sourceSets.main.allJava.srcDirs.collect { fileTree(it) })
+    }
+    if (project.sourceSets.hasProperty('test')) {
+        fs += files(project.sourceSets.test.allJava.srcDirs.collect { fileTree(it) })
+    }
+    if (project.hasProperty('android')) {
+        fs += files(project.android.sourceSets.main.java.getSrcDirs().collect {fileTree(it)})
+        fs += files(project.android.sourceSets.test.java.getSrcDirs().collect {fileTree(it)})
+        fs += files(project.android.sourceSets.androidTest.java.getSrcDirs().collect {fileTree(it)})
+    }
+    fs = fs.filter{file -> file.name.endsWith(".kt")}
+    def kotlinCheckstyle = createCheckstyleTask(project, 'checkstyleKotlin',
+            "${project.rootProject.ext.supportRootFolder}/app-toolkit/kotlin-checkstyle.xml",
+            fs.files)
+
+    project.tasks.findByName("check").dependsOn(kotlinCheckstyle)
+    // poor man's line length check
+    def lineCheck = project.tasks.create(name : "lineLengthCheck") {
+        fs.each { sourceDir ->
+                  fileTree(dir : sourceDir, include : "**/*.kt").each{ file ->
+                      file.readLines().eachWithIndex { line, index ->
+                          if (line.size() > 100) {
+                              project.logger.error("line too long: file: $file" +
+                                      " index:$index line: $line")
+                          }
+                      }
+                  }
+        }
+    }
+    kotlinCheckstyle.dependsOn(lineCheck)
+}
+
+def createAndroidCheckstyle(Project project) {
+    def fs = files()
+    if (project.hasProperty('android')) {
+        fs += files(project.android.sourceSets.main.java.getSrcDirs().collect {fileTree(it)})
+    }
+    if (project.sourceSets.hasProperty('main')) {
+        fs += files(project.sourceSets.main.allJava)
+    }
+    fs = fs.filter{file -> file.name.endsWith(".java")}
+
+    def checkStyle = createCheckstyleTask(project, 'checkstyleAndroid',
+            "${project.rootProject.ext.checkoutRoot}/prebuilts/checkstyle/android-style.xml",
+            fs.files)
+    project.tasks.findByName("check").dependsOn(checkStyle)
+}
+
+def createCheckstyleTask(project, taskName, configFile, inputFiles) {
+    def arguments = ['-c', configFile]
+    arguments.addAll(inputFiles)
+    def checkStyle = project.tasks.create(name : taskName, type: JavaExec) {
+        inputs.files(inputFiles).skipWhenEmpty()
+        main = "com.puppycrawl.tools.checkstyle.Main"
+        args = arguments
+        classpath = files(file("${project.rootProject.ext.checkoutRoot}/prebuilts/checkstyle/checkstyle.jar").path)
+    }
+    return checkStyle;
+}
+
+ext.createKotlinCheckstyle = this.&createKotlinCheckstyle
+ext.createAndroidCheckstyle = this.&createAndroidCheckstyle
diff --git a/app-toolkit/kotlin-checkstyle.xml b/app-toolkit/kotlin-checkstyle.xml
new file mode 100644
index 0000000..5cdad0a
--- /dev/null
+++ b/app-toolkit/kotlin-checkstyle.xml
@@ -0,0 +1,51 @@
+<?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.
+  -->
+
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+<!-- this is a very limited set of checkstyle rules mainly because TreeWalker rules are not
+supported for kt files yet.-->
+<module name="Checker">
+    <property name="severity" value="warning"/>
+    <property name="charset" value="UTF-8"/>
+    <module name="SuppressionCommentFilter">
+        <property name="offCommentFormat" value="CHECKSTYLE:OFF IndentationCheck"/>
+        <property name="onCommentFormat" value="CHECKSTYLE:ON IndentationCheck"/>
+        <property name="checkFormat" value="IndentationCheck"/>
+    </module>
+    <module name="SuppressionCommentFilter">
+        <property name="offCommentFormat" value="CHECKSTYLE:OFF Generated code"/>
+        <property name="onCommentFormat" value="CHECKSTYLE:ON Generated code"/>
+    </module>
+    <module name="FileTabCharacter">
+        <property name="severity" value="error"/>
+    </module>
+    <module name="NewlineAtEndOfFile">
+        <property name="severity" value="error"/>
+    </module>
+    <module name="RegexpSingleline">
+        <property name="severity" value="error"/>
+        <property name="format" value="[ \t]+$"/>
+        <property name="message" value="Trailing whitespace"/>
+    </module>
+    <module name="RegexpHeader">
+        <property name="severity" value="error"/>
+        <message key="header.mismatch"
+                 value="Android Copyright header seems to be incorrect. Expected ''{0}'' on this line."/>
+        <property name="header" value="^/\*\n \* Copyright \([Cc]\) [0-9]{4} The Android Open Source Project\n \*\n \* Licensed under the Apache License, Version 2\.0 \(the \&quot;License\&quot;\);\n \* you may not use this file except in compliance with the License.\n \* You may obtain a copy of the License at\n \*\n \*      http:\/\/www\.apache\.org\/licenses\/LICENSE-2\.0\n \*\n \* Unless required by applicable law or agreed to in writing, software\n \* distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n \* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\.\n \* See the License for the specific language governing permissions and\n \* limitations under the License\.\n \*\/" />
+    </module>
+</module>
diff --git a/app-toolkit/localize.sh b/app-toolkit/localize.sh
new file mode 100755
index 0000000..3b7cb99
--- /dev/null
+++ b/app-toolkit/localize.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+ALLOW_PUBLIC_REPOS=true ./gradlew localizeDependencies :room:compiler:buildLicenseNotice :lifecycle:compiler:buildLicenseNotice
\ No newline at end of file
diff --git a/app-toolkit/runtime/build.gradle b/app-toolkit/runtime/build.gradle
new file mode 100644
index 0000000..5e2fe46
--- /dev/null
+++ b/app-toolkit/runtime/build.gradle
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile libs.support.annotations
+    compile project(":arch:common")
+    testCompile libs.junit
+    testCompile libs.mockito_core
+    testCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+}
+
+archivesBaseName = "runtime"
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/app-toolkit/runtime/src/main/AndroidManifest.xml b/app-toolkit/runtime/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3f40068
--- /dev/null
+++ b/app-toolkit/runtime/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.core">
+</manifest>
diff --git a/app-toolkit/runtime/src/main/java/android/arch/core/executor/AppToolkitTaskExecutor.java b/app-toolkit/runtime/src/main/java/android/arch/core/executor/AppToolkitTaskExecutor.java
new file mode 100644
index 0000000..7337f74
--- /dev/null
+++ b/app-toolkit/runtime/src/main/java/android/arch/core/executor/AppToolkitTaskExecutor.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A static class that serves as a central point to execute common tasks.
+ * <p>
+ *
+ * @hide This API is not final.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class AppToolkitTaskExecutor extends TaskExecutor {
+    private static volatile AppToolkitTaskExecutor sInstance;
+
+    @NonNull
+    private TaskExecutor mDelegate;
+
+    @NonNull
+    private TaskExecutor mDefaultTaskExecutor;
+
+    @NonNull
+    private static final Executor sMainThreadExecutor = new Executor() {
+        @Override
+        public void execute(Runnable command) {
+            getInstance().postToMainThread(command);
+        }
+    };
+
+    @NonNull
+    private static final Executor sIOThreadExecutor = new Executor() {
+        @Override
+        public void execute(Runnable command) {
+            getInstance().executeOnDiskIO(command);
+        }
+    };
+
+    private AppToolkitTaskExecutor() {
+        mDefaultTaskExecutor = new DefaultTaskExecutor();
+        mDelegate = mDefaultTaskExecutor;
+    }
+
+    /**
+     * Returns an instance of the task executor.
+     *
+     * @return The singleton AppToolkitTaskExecutor.
+     */
+    public static AppToolkitTaskExecutor getInstance() {
+        if (sInstance != null) {
+            return sInstance;
+        }
+        synchronized (AppToolkitTaskExecutor.class) {
+            if (sInstance == null) {
+                sInstance = new AppToolkitTaskExecutor();
+            }
+        }
+        return sInstance;
+    }
+
+    /**
+     * Sets a delegate to handle task execution requests.
+     * <p>
+     * If you have a common executor, you can set it as the delegate and App Toolkit components will
+     * use your executors. You may also want to use this for your tests.
+     * <p>
+     * Calling this method with {@code null} sets it to the default TaskExecutor.
+     *
+     * @param taskExecutor The task executor to handle task requests.
+     */
+    public void setDelegate(@Nullable TaskExecutor taskExecutor) {
+        mDelegate = taskExecutor == null ? mDefaultTaskExecutor : taskExecutor;
+    }
+
+    @Override
+    public void executeOnDiskIO(Runnable runnable) {
+        mDelegate.executeOnDiskIO(runnable);
+    }
+
+    @Override
+    public void postToMainThread(Runnable runnable) {
+        mDelegate.postToMainThread(runnable);
+    }
+
+    @NonNull
+    public static Executor getMainThreadExecutor() {
+        return sMainThreadExecutor;
+    }
+
+    @NonNull
+    public static Executor getIOThreadExecutor() {
+        return sIOThreadExecutor;
+    }
+
+    @Override
+    public boolean isMainThread() {
+        return mDelegate.isMainThread();
+    }
+}
diff --git a/app-toolkit/runtime/src/main/java/android/arch/core/executor/DefaultTaskExecutor.java b/app-toolkit/runtime/src/main/java/android/arch/core/executor/DefaultTaskExecutor.java
new file mode 100644
index 0000000..dbb1054
--- /dev/null
+++ b/app-toolkit/runtime/src/main/java/android/arch/core/executor/DefaultTaskExecutor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class DefaultTaskExecutor extends TaskExecutor {
+    private final Object mLock = new Object();
+    private ExecutorService mDiskIO = Executors.newFixedThreadPool(2);
+
+    @Nullable
+    private volatile Handler mMainHandler;
+
+    @Override
+    public void executeOnDiskIO(Runnable runnable) {
+        mDiskIO.execute(runnable);
+    }
+
+    @Override
+    public void postToMainThread(Runnable runnable) {
+        if (mMainHandler == null) {
+            synchronized (mLock) {
+                if (mMainHandler == null) {
+                    mMainHandler = new Handler(Looper.getMainLooper());
+                }
+            }
+        }
+        //noinspection ConstantConditions
+        mMainHandler.post(runnable);
+    }
+
+    @Override
+    public boolean isMainThread() {
+        return Looper.getMainLooper().getThread() == Thread.currentThread();
+    }
+}
diff --git a/app-toolkit/runtime/src/main/java/android/arch/core/executor/TaskExecutor.java b/app-toolkit/runtime/src/main/java/android/arch/core/executor/TaskExecutor.java
new file mode 100644
index 0000000..055b476
--- /dev/null
+++ b/app-toolkit/runtime/src/main/java/android/arch/core/executor/TaskExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.arch.core.executor;
+
+import android.support.annotation.RestrictTo;
+
+/**
+ * A task executor that can divide tasks into logical groups.
+ * <p>
+ * It holds a collection a executors for each group of task.
+ * <p>
+ * TODO: Don't use this from outside, we don't know what the API will look like yet.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class TaskExecutor {
+    /**
+     * Executes the given task in the disk IO thread pool.
+     *
+     * @param runnable The runnable to run in the disk IO thread pool.
+     */
+    public abstract void executeOnDiskIO(Runnable runnable);
+
+    /**
+     * Posts the given task to the main thread.
+     *
+     * @param runnable The runnable to run on the main thread.
+     */
+    public abstract void postToMainThread(Runnable runnable);
+
+    /**
+     * Executes the given task on the main thread.
+     * <p>
+     * If the current thread is a main thread, immediately runs the given runnable.
+     *
+     * @param runnable The runnable to run on the main thread.
+     */
+    public void executeOnMainThread(Runnable runnable) {
+        if (isMainThread()) {
+            runnable.run();
+        } else {
+            postToMainThread(runnable);
+        }
+    }
+
+    /**
+     * Returns true if the current thread is the main thread, false otherwise.
+     *
+     * @return true if we are on the main thread, false otherwise.
+     */
+    public abstract boolean isMainThread();
+}
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
new file mode 100644
index 0000000..0977dc6
--- /dev/null
+++ b/app-toolkit/settings.gradle
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+// If you change this file, you should also change the settings gradle inside
+// the sub project.
+
+def inAppToolkitProject = rootProject.name == "app-toolkit"
+def supportRoot
+if (inAppToolkitProject) {
+    supportRoot = new File(rootProject.projectDir, "..").getCanonicalFile()
+} else {
+    supportRoot = rootProject.projectDir
+}
+
+println "support root:${supportRoot}"
+
+include ':arch:runtime'
+project(':arch:runtime').projectDir = new File(supportRoot, "app-toolkit/runtime")
+
+include ':arch:common'
+project(':arch:common').projectDir = new File(supportRoot, "app-toolkit/common")
+
+include ':paging:common'
+project(':paging:common').projectDir = new File(supportRoot, "paging/common")
+
+include ":paging:runtime"
+project(':paging:runtime').projectDir = new File(supportRoot, "paging/runtime")
+
+include ':paging:integration-tests:testapp'
+project(':paging:integration-tests:testapp').projectDir = new File(supportRoot, "paging/integration-tests/testapp")
+
+include ':arch:core-testing'
+project(':arch:core-testing').projectDir = new File(supportRoot, "app-toolkit/core-testing")
+
+include ':lifecycle:extensions'
+project(':lifecycle:extensions').projectDir = new File(supportRoot, "lifecycle/extensions")
+
+include ':lifecycle:reactivestreams'
+project(':lifecycle:reactivestreams').projectDir = new File(supportRoot, "lifecycle/reactivestreams")
+
+include ':lifecycle:runtime'
+project(':lifecycle:runtime').projectDir = new File(supportRoot, "lifecycle/runtime")
+
+include ':lifecycle:common'
+project(':lifecycle:common').projectDir = new File(supportRoot, "lifecycle/common")
+
+include ':lifecycle:compiler'
+project(':lifecycle:compiler').projectDir = new File(supportRoot, "lifecycle/compiler")
+
+include ':lifecycle:integration-tests:testapp'
+project(':lifecycle:integration-tests:testapp').projectDir = new File(supportRoot, "lifecycle/integration-tests/testapp")
+
+include ':room:common'
+project(':room:common').projectDir = new File(supportRoot, "room/common")
+
+include ':room:runtime'
+project(':room:runtime').projectDir = new File(supportRoot, "room/runtime")
+
+include ':room:compiler'
+project(':room:compiler').projectDir = new File(supportRoot, "room/compiler")
+
+include ':room:migration'
+project(':room:migration').projectDir = new File(supportRoot, "room/migration")
+
+include ':room:db'
+project(':room:db').projectDir = new File(supportRoot, "room/db")
+
+include ":room:db-impl"
+project(':room:db-impl').projectDir = new File(supportRoot, "room/db-impl")
+
+include ":room:testing"
+project(':room:testing').projectDir = new File(supportRoot, "room/testing")
+
+include ":room:rxjava2"
+project(':room:rxjava2').projectDir = new File(supportRoot, "room/rxjava2")
+
+include ':room:integration-tests:testapp'
+project(':room:integration-tests:testapp').projectDir = new File(supportRoot, "room/integration-tests/testapp")
+
+include ':room:integration-tests:kotlintestapp'
+project(':room:integration-tests:kotlintestapp').projectDir = new File(supportRoot, "room/integration-tests/kotlintestapp")
+
+/////////////////////////////
+//
+// SupportLib
+//
+/////////////////////////////
+if (inAppToolkitProject && System.getenv("USE_SUPPORT_LIB_SOURCE")) {
+    apply from: "${supportRoot.absolutePath}/app-toolkit/settings_support_lib.gradle"
+}
+
+/////////////////////////////
+//
+// External
+//
+/////////////////////////////
+if (inAppToolkitProject) {
+    File externalRoot = new File(supportRoot, '../../external')
+
+    include ':doclava'
+    project(':doclava').projectDir = new File(externalRoot, 'doclava')
+
+    include ':jdiff'
+    project(':jdiff').projectDir = new File(externalRoot, 'jdiff')
+}
diff --git a/app-toolkit/settings_support_lib.gradle b/app-toolkit/settings_support_lib.gradle
new file mode 100644
index 0000000..c6f56f1
--- /dev/null
+++ b/app-toolkit/settings_support_lib.gradle
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+// settings file to include support lib sources.
+def supportRootDir = new File("../.")
+
+include ':support-core-utils'
+project(':support-core-utils').projectDir = new File(supportRootDir, 'core-utils')
+
+include ':support-fragment'
+project(':support-fragment').projectDir = new File(supportRootDir, 'fragment')
+
+include ':support-compat'
+project(':support-compat').projectDir = new File(supportRootDir, 'compat')
+
+include ':support-annotations'
+project(':support-annotations').projectDir = new File(supportRootDir, 'annotations')
+
+include ':support-core-ui'
+project(':support-core-ui').projectDir = new File(supportRootDir, 'core-ui')
+
+include ':support-appcompat-v7'
+project(':support-appcompat-v7').projectDir = new File(rootDir, 'v7/appcompat')
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index ddb70c5..360ec02 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,490 +1,46 @@
-import android.support.checkapi.CheckApiTask
-import android.support.checkapi.UpdateApiTask
-import android.support.doclava.DoclavaMultilineJavadocOptionFileOption
-import android.support.doclava.DoclavaTask
-
-import com.android.build.gradle.internal.coverage.JacocoReportTask
-import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
-import org.gradle.internal.os.OperatingSystem
-
-import com.google.common.base.Charsets
-import com.google.common.hash.HashCode
-import com.google.common.hash.HashFunction
-import com.google.common.hash.Hashing
-import com.google.common.io.Files
-
-import groovy.io.FileType
+/*
+ * 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.
+ */
 
 buildscript {
-    repositories {
-        maven { url '../../prebuilts/gradle-plugin' }
-        maven { url '../../prebuilts/tools/common/m2/repository' }
-        maven { url '../../prebuilts/tools/common/m2/internal' }
-        maven { url "../../prebuilts/maven_repo/android" }
-    }
+    ext.supportRootFolder = project.projectDir
+    apply from: 'buildSrc/repos.gradle'
+
+    apply from: 'buildSrc/init.gradle'
+    init.loadDefaultVersions()
+    init.setSdkInLocalPropertiesFile()
+    repos.addMavenRepositories(repositories)
+
     dependencies {
-        // Keep gradle plugin version in sync with ub_supportlib-master manifest.
-        classpath 'com.android.tools.build:gradle:2.3.0'
+        classpath libs.gradle
+        classpath libs.jacoco
     }
 }
 
-repositories {
-    maven { url '../../prebuilts/tools/common/m2/repository' }
-}
+repos.addMavenRepositories(repositories)
 
-configurations {
-    doclava
-}
+init.setupRepoOutAndBuildNumber()
 
-dependencies {
-    doclava project(':doclava')
-}
+init.configureSubProjects()
 
-ext.supportVersion = '25.4.0'
-ext.extraVersion = 49
-ext.supportRepoOut = ''
-ext.buildNumber = Integer.toString(ext.extraVersion)
+init.configureBuildOnServer()
 
-ext.buildToolsVersion = '25.0.0'
-ext.testRunnerVersion = '0.6-alpha'
-ext.espressoVersion = '2.3-alpha'
+init.setupRelease()
 
-// Enforce the use of prebuilt dependencies in all sub-projects. This is
-// required for the doclava dependency.
-ext.usePrebuilts = "true"
+init.enableDoclavaAndJDiff(this)
 
-// Prevent the Android Gradle plug-in from automatically downloading SDK dependencies.
-ext['android.builder.sdkDownload'] = false
+///// FLATFOOT START
 
-final String platform = OperatingSystem.current().isMacOsX() ? 'darwin' : 'linux'
-System.setProperty('android.dir', "${rootDir}/../../")
-final String fullSdkPath = "${rootDir}/../../prebuilts/fullsdk-${platform}"
-if (file(fullSdkPath).exists()) {
-    gradle.ext.currentSdk = 25
-    project.ext.androidJar = files("${fullSdkPath}/platforms/android-${gradle.ext.currentSdk}/android.jar")
-    System.setProperty('android.home', "${rootDir}/../../prebuilts/fullsdk-${platform}")
-    File props = file("local.properties")
-    props.write "sdk.dir=${fullSdkPath}"
-} else {
-    gradle.ext.currentSdk = 'current'
-    project.ext.androidJar = files("${project.rootDir}/../../prebuilts/sdk/current/android.jar")
-    File props = file("local.properties")
-    props.write "android.dir=../../"
-}
-
-/*
- * With the build server you are given two env variables.
- * The OUT_DIR is a temporary directory you can use to put things during the build.
- * The DIST_DIR is where you want to save things from the build.
- *
- * The build server will copy the contents of DIST_DIR to somewhere and make it available.
- */
-if (System.env.DIST_DIR != null && System.env.OUT_DIR != null) {
-    buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build').getCanonicalFile()
-    project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()
-
-    // the build server does not pass the build number so we infer it from the last folder of the dist path.
-    ext.buildNumber = project.ext.distDir.getName()
-} else {
-    buildDir = file("${project.rootDir}/../../out/host/gradle/frameworks/support/build")
-    project.ext.distDir = file("${project.rootDir}/../../out/dist")
-}
-
-subprojects {
-    // Change buildDir first so that all plugins pick up the new value.
-    project.buildDir = project.file("$project.parent.buildDir/../$project.name/build")
-}
-
-ext.docsDir = new File(buildDir, 'javadoc')
-ext.supportRepoOut = new File(buildDir, 'support_repo')
-ext.testApkDistOut = ext.distDir
-
-// Main task called by the build server.
-task(createArchive) << {
-}
-
-// upload anchor for subprojects to upload their artifacts
-// to the local repo.
-task(mainUpload) << {
-}
-
-// repository creation task
-task createRepository(type: Zip, dependsOn: mainUpload) {
-    from project.ext.supportRepoOut
-    destinationDir project.ext.distDir
-    into 'm2repository'
-    baseName = String.format("sdk-repo-linux-m2repository-%s", project.ext.buildNumber)
-}
-createArchive.dependsOn createRepository
-
-// prepare repository with older versions
-task unzipRepo(type: Copy) {
-    from "${project.rootDir}/../../prebuilts/maven_repo/android"
-    into project.ext.supportRepoOut
-}
-
-unzipRepo.doFirst {
-    project.ext.supportRepoOut.deleteDir()
-    project.ext.supportRepoOut.mkdirs()
-}
-
-// anchor for prepare repo. This is post unzip + sourceProp.
-task(prepareRepo) << {
-}
-
-// lint every library
-task(lint) << {
-}
-
-task(createXml) << {
-    def repoArchive = createRepository.archivePath
-    def repoArchiveName = createRepository.archiveName
-    def size = repoArchive.length()
-    def sha1 = getSha1(repoArchive)
-
-    def xml =
-"<sdk:sdk-addon xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:sdk=\"http://schemas.android.com/sdk/android/addon/6\">\n\
-  <sdk:extra>\n\
-    <sdk:revision>\n\
-      <sdk:major>${project.ext.extraVersion}</sdk:major>\n\
-    </sdk:revision>\n\
-    <sdk:vendor-display>Android</sdk:vendor-display>\n\
-    <sdk:vendor-id>android</sdk:vendor-id>\n\
-    <sdk:name-display>Local Maven repository for Support Libraries</sdk:name-display>\n\
-    <sdk:path>m2repository</sdk:path>\n\
-    <sdk:archives>\n\
-      <sdk:archive>\n\
-       <sdk:size>${size}</sdk:size>\n\
-       <sdk:checksum type=\"sha1\">${sha1}</sdk:checksum>\n\
-       <sdk:url>${repoArchiveName}</sdk:url>\n\
-      </sdk:archive>\n\
-    </sdk:archives>\n\
-  </sdk:extra>\n\
-</sdk:sdk-addon>"
-
-    Files.write(xml, new File(project.ext.distDir, 'repo-extras.xml'), Charsets.UTF_8)
-}
-createArchive.dependsOn createXml
-createXml.dependsOn createRepository
-
-task(createSourceProp) << {
-    def sourceProp =
-"Extra.VendorDisplay=Android\n\
-Extra.Path=m2repository\n\
-Archive.Arch=ANY\n\
-Extra.NameDisplay=Android Support Repository\n\
-Archive.Os=ANY\n\
-Pkg.Desc=Local Maven repository for Support Libraries\n\
-Pkg.Revision=${project.ext.extraVersion}.0.0\n\
-Extra.VendorId=android"
-
-    Files.write(sourceProp, new File(project.ext.supportRepoOut, 'source.properties'), Charsets.UTF_8)
-}
-createSourceProp.dependsOn unzipRepo
-prepareRepo.dependsOn createSourceProp
-
-import java.nio.charset.Charset
-
-/**
- * Generates SHA1 hash for the specified file's absolute path.
- *
- * @param inputFile file to hash
- * @return SHA1 hash
- */
-String getSha1(File inputFile) {
-    HashFunction hashFunction = Hashing.sha1()
-    HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charset.forName("UTF-8"))
-    return hashCode.toString()
-}
-
-void registerForDocsTask(Task task, Project subProject, releaseVariant) {
-    task.dependsOn releaseVariant.javaCompile
-    task.source {
-        def buildConfig = fileTree(releaseVariant.getGenerateBuildConfig().sourceOutputDir)
-        return releaseVariant.javaCompile.source.minus(buildConfig) +
-            fileTree(releaseVariant.aidlCompile.sourceOutputDir) +
-            fileTree(releaseVariant.outputs[0].processResources.sourceOutputDir)
-    }
-    task.classpath += files(releaseVariant.javaCompile.classpath) +
-            files(releaseVariant.javaCompile.destinationDir)
-}
-
-// Generates online docs.
-task generateDocs(type: DoclavaTask, dependsOn: configurations.doclava) {
-    group = JavaBasePlugin.DOCUMENTATION_GROUP
-    description = 'Generates d.android.com style documentation.'
-
-    docletpath = configurations.doclava.resolve()
-    destinationDir = new File(project.docsDir, "online")
-
-    // Base classpath is Android SDK, sub-projects add their own.
-    classpath = project.ext.androidJar
-
-    def hdfOption = new DoclavaMultilineJavadocOptionFileOption('hdf')
-    hdfOption.add(
-            ['android.whichdoc', 'online'],
-            ['android.hasSamples', 'true']);
-
-    // Default hidden errors + hidden superclass (111) and
-    // deprecation mismatch (113) to match framework docs.
-    final def hidden = [105, 107, 111, 112, 113, 115, 116, 121]
-
-    doclavaErrors = (101..122) - hidden
-    doclavaWarnings = []
-    doclavaHidden += hidden
-
-    options {
-        addStringOption "templatedir",
-                "${project.rootDir}/../../build/tools/droiddoc/templates-sdk"
-        addStringOption "stubpackages", "android.support.*"
-        addStringOption "samplesdir", "${project.rootDir}/samples"
-        addOption hdfOption
-    }
-
-    exclude '**/BuildConfig.java'
-}
-
-// Generates API files.
-task generateApi(type: DoclavaTask, dependsOn: configurations.doclava) {
-    docletpath = configurations.doclava.resolve()
-    destinationDir = project.docsDir
-
-    // Base classpath is Android SDK, sub-projects add their own.
-    classpath = project.ext.androidJar
-
-    apiFile = new File(project.docsDir, 'release/current.txt')
-    removedApiFile = new File(project.docsDir, 'release/removed.txt')
-    generateDocs = false
-
-    options {
-        addStringOption "templatedir",
-                "${project.rootDir}/../../build/tools/droiddoc/templates-sdk"
-        addStringOption "stubpackages", "android.support.*"
-    }
-    exclude '**/BuildConfig.java'
-    exclude '**/R.java'
-}
-
-// Copies generated API files to current version.
-task updateApi(type: UpdateApiTask, dependsOn: generateApi) {
-    group JavaBasePlugin.VERIFICATION_GROUP
-    description 'Invoke Doclava\'s ApiCheck tool to update current.txt based on current changes.'
-
-    newApiFile = new File(project.docsDir, 'release/current.txt')
-    oldApiFile = new File(project.rootDir, 'api/current.txt')
-    newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
-    oldRemovedApiFile = new File(project.rootDir, 'api/removed.txt')
-}
-
-// Checks generated API files against current version.
-task checkApi(type: CheckApiTask, dependsOn: generateApi) {
-    doclavaClasspath = generateApi.docletpath
-
-    checkApiTaskPath = name
-    updateApiTaskPath = updateApi.name
-
-    // Check that the API we're building hasn't changed from the development
-    // version. These typed of changes require an explicit API file update.
-    checkApiErrors = (2..30)-[22]
-    checkApiWarnings = []
-    checkApiHidden = [22]
-
-    newApiFile = new File(project.docsDir, 'release/current.txt')
-    oldApiFile = new File(project.rootDir, 'api/current.txt')
-    newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
-    oldRemovedApiFile = new File(project.rootDir, 'api/removed.txt')
-}
-createArchive.dependsOn checkApi
-
-// Checks generated API files against current version.
-task checkApiStable(type: CheckApiTask, dependsOn: generateApi) {
-    doclavaClasspath = generateApi.docletpath
-
-    checkApiTaskPath = name
-    updateApiTaskPath = updateApi.name
-
-    // Check that the API we're building hasn't broken the last-released
-    // library version. These types of changes are forbidden.
-    checkApiErrors = (7..18)
-    checkApiWarnings = [23, 24]
-    checkApiHidden = (2..6) + (19..22) + (25..30)
-
-    def lastReleasedApiFile = null
-    def apiDir = new File(project.rootDir, 'api')
-    apiDir.eachFileMatch FileType.FILES, ~/(\d+\.){3}txt/, { File apiFile ->
-        if (lastReleasedApiFile == null || apiFile.name > lastReleasedApiFile.name) {
-            lastReleasedApiFile = apiFile;
-        }
-    }
-
-    newApiFile = new File(project.docsDir, 'release/current.txt')
-    oldApiFile = lastReleasedApiFile
-    newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
-    oldRemovedApiFile = new File(project.rootDir, 'api/removed.txt')
-}
-checkApi.dependsOn checkApiStable
-
-subprojects {
-    // Only modify Android projects.
-    if (project.name.equals('doclava')) return;
-
-    // Current SDK is set in studioCompat.gradle.
-    project.ext.currentSdk = gradle.ext.currentSdk
-    apply plugin: 'maven'
-
-    version = rootProject.ext.supportVersion
-    group = 'com.android.support'
-
-    repositories {
-        maven { url "${project.parent.projectDir}/../../prebuilts/tools/common/m2/repository" }
-        maven { url "${project.parent.projectDir}/../../prebuilts/tools/common/m2/internal" }
-        maven { url "${project.parent.projectDir}/../../prebuilts/maven_repo/android" }
-    }
-
-    project.plugins.whenPluginAdded { plugin ->
-        def isAndroidLibrary = "com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)
-        def isAndroidApp = "com.android.build.gradle.AppPlugin".equals(plugin.class.name)
-        def isJavaLibrary = "org.gradle.api.plugins.JavaPlugin".equals(plugin.class.name)
-
-        if (isAndroidLibrary || isAndroidApp) {
-            project.android.buildToolsVersion = rootProject.buildToolsVersion
-
-            // Enable code coverage for debug builds only if we are not running inside the IDE,
-            // since enabling coverage reports breaks the method parameter resolution in the IDE
-            // debugger.
-            project.android.buildTypes.debug.testCoverageEnabled = !hasProperty('android.injected.invoked.from.ide')
-
-            // Enforce NewApi lint check as fatal.
-            project.android.lintOptions.check 'NewApi'
-            project.android.lintOptions.fatal 'NewApi'
-            project.parent.lint.dependsOn project.lint
-        }
-
-        if (isAndroidLibrary || isJavaLibrary) {
-            // Add library to the aggregate dependency report.
-            task allDeps(type: DependencyReportTask) {}
-
-            project.afterEvaluate {
-                Upload uploadTask = (Upload) project.tasks.uploadArchives;
-
-                uploadTask.repositories.mavenDeployer {
-                    // Disable unique names for SNAPSHOTS so they can be updated in place.
-                    setUniqueVersion(false)
-                }
-
-                uploadTask.doLast {
-                    // Remove any invalid maven-metadata.xml files that may have been
-                    // created for SNAPSHOT versions that are *not* uniquely versioned.
-                    repositories.mavenDeployer.pom*.each { pom ->
-                        if (pom.version.endsWith('-SNAPSHOT')) {
-                            final File artifactDir = new File(
-                                    rootProject.ext.supportRepoOut,
-                                    pom.groupId.replace('.', '/')
-                                            + '/' + pom.artifactId
-                                            + '/' + pom.version)
-                            delete fileTree(dir: artifactDir,
-                                    include: 'maven-metadata.xml*')
-                        }
-                    }
-                }
-
-                uploadTask.repositories.mavenDeployer.pom*.whenConfigured { pom ->
-                    pom.dependencies.findAll { dep ->
-                        dep.groupId == 'com.android.support' &&
-                                dep.artifactId != 'support-annotations'
-                    }*.type = 'aar'
-                }
-
-                task createSeparateZip(type: Zip)  {
-                    into archivesBaseName
-                    destinationDir project.parent.ext.distDir
-                    baseName = project.group
-                    version = project.parent.ext.buildNumber
-                }
-                project.parent.createArchive.dependsOn createSeparateZip
-
-                // Before the upload, make sure the repo is ready.
-                uploadTask.dependsOn rootProject.tasks.prepareRepo
-
-                // Make the mainupload depend on this one.
-                mainUpload.dependsOn uploadTask
-            }
-        }
-    }
-
-    project.afterEvaluate {
-        // The archivesBaseName isn't available initially, so set it now
-        def createZipTask = project.tasks.findByName("createSeparateZip")
-        if (createZipTask != null) {
-            createZipTask.appendix = archivesBaseName
-            createZipTask.from versionDir()
-        }
-
-        // Copy instrumentation test APK into the dist dir
-        def assembleTestTask = project.tasks.findByPath('assembleAndroidTest')
-        if (assembleTestTask != null) {
-            assembleTestTask.doLast {
-                // If the project actually has some instrumentation tests, copy its APK
-                if (!project.android.sourceSets.androidTest.java.sourceFiles.isEmpty()) {
-                    def pkgTask = project.tasks.findByPath('packageDebugAndroidTest')
-                    copy {
-                        from(pkgTask.outputFile)
-                        into(rootProject.ext.testApkDistOut)
-                    }
-                }
-            }
-        }
-    }
-
-    project.afterEvaluate { p ->
-        // remove dependency on the test so that we still get coverage even if some tests fail
-        p.tasks.findAll { it instanceof JacocoReportTask}.each { task ->
-            def toBeRemoved = new ArrayList()
-            def dependencyList = task.taskDependencies.values
-            dependencyList.each { dep ->
-                if (dep instanceof String) {
-                    def t = tasks.findByName(dep)
-                    if (t instanceof DeviceProviderInstrumentTestTask) {
-                        toBeRemoved.add(dep)
-                        task.mustRunAfter(t)
-                    }
-                }
-            }
-            toBeRemoved.each { dep ->
-                dependencyList.remove(dep)
-            }
-        }
-    }
-
-    project.afterEvaluate { p ->
-        if (p.hasProperty('android')
-                && p.android.hasProperty('libraryVariants')
-                && !(p.android.hasProperty('noDocs') && p.android.noDocs)) {
-            p.android.libraryVariants.all { v ->
-                if (v.name == 'release') {
-                    registerForDocsTask(rootProject.generateDocs, p, v)
-                    registerForDocsTask(rootProject.generateApi, p, v)
-                }
-            }
-        }
-    }
-
-    // Update the version meta-data in each Manifest
-    project.afterEvaluate { p ->
-        if (p.hasProperty('android')) {
-            p.android.defaultConfig.manifestPlaceholders =
-                    ["support-version": rootProject.ext.supportVersion]
-        }
-    }
-}
-
-project.gradle.buildFinished { buildResult ->
-    if (buildResult.getFailure() != null) {
-        println()
-        println 'Build failed. Possible causes include:'
-        println '    1) Bad codes'
-        println '    2) Out of date prebuilts in prebuilts/sdk'
-        println '    3) Need to update the compileSdkVersion in a library\'s build.gradle'
-        println()
-    }
-}
+///// FLATFOOT END
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 0000000..d63de24
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'groovy'
+apply plugin: 'java'
+
+apply from: "dependencies.gradle"
+
+ext.supportRootFolder = project.projectDir.getParentFile()
+apply from: 'repos.gradle'
+
+repos.addMavenRepositories(repositories)
+
+dependencies {
+    compile libs.gradle
+    compile libs.jacoco
+    compile libs.error_prone
+    compile libs.jarjar_gradle
+}
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
new file mode 100644
index 0000000..8a7d69a
--- /dev/null
+++ b/buildSrc/dependencies.gradle
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+// Add ext.libs for library versions
+def libs = [:]
+
+// Testing dependencies
+libs.mockito_core = 'org.mockito:mockito-core:2.7.6'
+libs.dexmaker_mockito = 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'
+libs.junit = 'junit:junit:4.12'
+libs.test_runner = 'com.android.support.test:runner:0.6-alpha'
+libs.espresso_core = 'com.android.support.test.espresso:espresso-core:2.3-alpha'
+libs.espresso_contrib = 'com.android.support.test.espresso:espresso-contrib:2.3-alpha'
+libs.jacoco = 'org.jacoco:org.jacoco.core:0.7.8'
+libs.test_rules = 'com.android.support.test:rules:0.6-alpha'
+
+def androidPluginVersionOverride = System.getenv("GRADLE_PLUGIN_VERSION")
+
+if (androidPluginVersionOverride != null) {
+    libs.gradle = 'com.android.tools.build:gradle:' + androidPluginVersionOverride
+} else {
+    // Keep gradle plugin version in sync with ub_supportlib-master manifest.
+    libs.gradle = 'com.android.tools.build:gradle:3.0.0-alpha7'
+}
+
+// Other dependencies
+libs.xml_parser_apis = 'xerces:xmlParserAPIs:2.6.2'
+libs.xerces_impl = 'xerces:xercesImpl:2.6.2'
+libs.error_prone = 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.10'
+
+// jarjar plugin
+libs.jarjar_gradle = 'org.anarres.jarjar:jarjar-gradle:1.0.0'
+
+rootProject.ext['libs'] = libs
diff --git a/buildSrc/diff_and_docs.gradle b/buildSrc/diff_and_docs.gradle
new file mode 100644
index 0000000..1f012d1
--- /dev/null
+++ b/buildSrc/diff_and_docs.gradle
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+
+import android.support.checkapi.ApiXmlConversionTask
+import android.support.checkapi.CheckApiTask
+import android.support.checkapi.UpdateApiTask
+import android.support.doclava.DoclavaMultilineJavadocOptionFileOption
+import android.support.doclava.DoclavaTask
+import android.support.jdiff.JDiffTask
+
+import org.gradle.api.InvalidUserDataException
+
+import groovy.io.FileType
+
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+// Set up platform API files for federation.
+if (project.androidApiTxt != null) {
+    task generateSdkApi(type: Copy) {
+        description = 'Copies the API files for the current SDK.'
+
+        // Export the API files so this looks like a DoclavaTask.
+        ext.apiFile = new File(project.docsDir, 'release/sdk_current.txt')
+        ext.removedApiFile = new File(project.docsDir, 'release/sdk_removed.txt')
+
+        from project.androidApiTxt.absolutePath
+        into apiFile.parent
+        rename { apiFile.name }
+
+        // Register the fake removed file as an output.
+        outputs.file removedApiFile
+
+        doLast {
+            removedApiFile.createNewFile()
+        }
+    }
+} else {
+    task generateSdkApi(type: DoclavaTask, dependsOn: [configurations.doclava]) {
+        description = 'Generates API files for the current SDK.'
+
+        docletpath = configurations.doclava.resolve()
+        destinationDir = project.docsDir
+
+        classpath = project.androidJar
+        source zipTree(project.androidSrcJar)
+
+        apiFile = new File(project.docsDir, 'release/sdk_current.txt')
+        removedApiFile = new File(project.docsDir, 'release/sdk_removed.txt')
+        generateDocs = false
+
+        options {
+            addStringOption "stubpackages", "android.*"
+        }
+    }
+}
+
+// Generates online docs.
+task generateDocs(type: DoclavaTask, dependsOn: [configurations.doclava, generateSdkApi]) {
+    def offlineDocs = project.docs.offline
+    group = JavaBasePlugin.DOCUMENTATION_GROUP
+    description = 'Generates d.android.com-style documentation. To generate offline docs use ' +
+            '\'-PofflineDocs=true\' parameter.'
+
+    docletpath = configurations.doclava.resolve()
+    destinationDir = new File(project.docsDir, offlineDocs ? "offline" : "online")
+
+    // Base classpath is Android SDK, sub-projects add their own.
+    classpath = project.ext.androidJar
+
+    def hdfOption = new DoclavaMultilineJavadocOptionFileOption('hdf')
+    hdfOption.add(
+            ['android.whichdoc', 'online'],
+            ['android.hasSamples', 'true'],
+            ['dac', 'true'])
+
+    def federateOption = new DoclavaMultilineJavadocOptionFileOption('federate')
+    federateOption.add(['Android', 'https://developer.android.com'])
+
+    def federationapiOption = new DoclavaMultilineJavadocOptionFileOption('federationapi')
+    federationapiOption.add(['Android', generateSdkApi.apiFile.absolutePath])
+
+    // Track API change history.
+    def apiFilePattern = /(\d+\.\d+\.\d).txt/
+    def sinceOption = new DoclavaMultilineJavadocOptionFileOption('since')
+    File apiDir = new File(supportRootFolder, 'api')
+    apiDir.eachFileMatch FileType.FILES, ~apiFilePattern, { File apiFile ->
+        def apiLevel = (apiFile.name =~ apiFilePattern)[0][1]
+        sinceOption.add([apiFile.absolutePath, apiLevel])
+    }
+
+    // Default hidden errors + hidden superclass (111) and
+    // deprecation mismatch (113) to match framework docs.
+    final def hidden = [105, 106, 107, 111, 112, 113, 115, 116, 121]
+
+    doclavaErrors = (101..122) - hidden
+    doclavaWarnings = []
+    doclavaHidden += hidden
+
+    options {
+        addStringOption "templatedir",
+                "${supportRootFolder}/../../external/doclava/res/assets/templates-sdk"
+        addStringOption "stubpackages", "android.support.*"
+        addStringOption "samplesdir", "${supportRootFolder}/samples"
+        addOption federateOption
+        addOption federationapiOption
+        addOption hdfOption
+        addOption sinceOption
+
+        // Specific to reference docs.
+        if (!offlineDocs) {
+            addStringOption "toroot", "/"
+            addBooleanOption "devsite", true
+            addStringOption "dac_libraryroot", project.docs.dac.libraryroot
+            addStringOption "dac_dataname", project.docs.dac.dataname
+        }
+    }
+
+    exclude '**/BuildConfig.java'
+}
+
+// Generates a distribution artifact for online docs.
+task distDocs(type: Zip, dependsOn: generateDocs) {
+    group = JavaBasePlugin.DOCUMENTATION_GROUP
+    description = 'Generates distribution artifact for d.android.com-style documentation.'
+
+    from generateDocs.destinationDir
+    destinationDir project.distDir
+    baseName = "android-support-docs"
+    version = project.buildNumber
+
+    doLast {
+        logger.lifecycle("'Wrote API reference to ${archivePath}")
+    }
+}
+
+def MSG_HIDE_API =
+        "If you are adding APIs that should be excluded from the public API surface,\n" +
+        "consider using package or private visibility. If the API must have public\n" +
+        "visibility, you may exclude it from public API by using the @hide javadoc\n" +
+        "annotation paired with the @RestrictTo(LIBRARY_GROUP) code annotation."
+
+// Check that the API we're building hasn't broken compatibility with the
+// previously released version. These types of changes are forbidden.
+def CHECK_API_CONFIG_RELEASE = [
+    onFailMessage:
+            "Compatibility with previously released public APIs has been broken. Please\n" +
+            "verify your change with Support API Council and provide error output,\n" +
+            "including the error messages and associated SHAs.\n" +
+            "\n" +
+            "If you are removing APIs, they must be deprecated first before being removed\n" +
+            "in a subsequent release.\n" +
+            "\n" + MSG_HIDE_API,
+    errors: (7..18),
+    warnings: [],
+    hidden: (2..6) + (19..30)
+]
+
+// Check that the API we're building hasn't changed from the development
+// version. These types of changes require an explicit API file update.
+def CHECK_API_CONFIG_DEVELOP = [
+    onFailMessage:
+            "Public API definition has changed. Please run ./gradlew updateApi to confirm\n" +
+            "these changes are intentional by updating the public API definition.\n" +
+            "\n" + MSG_HIDE_API,
+    errors: (2..30)-[22],
+    warnings: [],
+    hidden: [22]
+]
+
+// This is a patch or finalized release. Check that the API we're building
+// hasn't changed from the current.
+def CHECK_API_CONFIG_PATCH = [
+    onFailMessage:
+            "Public API definition may not change in finalized or patch releases.\n" +
+            "\n" + MSG_HIDE_API,
+    errors: (2..30)-[22],
+    warnings: [],
+    hidden: [22]
+]
+
+CheckApiTask createCheckApiTask(String taskName, def checkApiConfig, File oldApi, File newApi,
+                                File whitelist = null) {
+    return tasks.create(name: taskName, type: CheckApiTask.class) {
+        doclavaClasspath = generateApi.docletpath
+
+        onFailMessage = checkApiConfig.onFailMessage
+        checkApiErrors = checkApiConfig.errors
+        checkApiWarnings = checkApiConfig.warnings
+        checkApiHidden = checkApiConfig.hidden
+
+        newApiFile = newApi
+        oldApiFile = oldApi
+        newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
+        oldRemovedApiFile = new File(supportRootFolder, 'api/removed.txt')
+
+        whitelistErrorsFile = whitelist
+
+        doFirst {
+            logger.lifecycle "Verifying ${newApi.name} against ${oldApi.name}..."
+        }
+    }
+}
+
+// Generates API files.
+task generateApi(type: DoclavaTask, dependsOn: configurations.doclava) {
+    docletpath = configurations.doclava.resolve()
+    destinationDir = project.docsDir
+
+    // Base classpath is Android SDK, sub-projects add their own.
+    classpath = project.ext.androidJar
+
+    apiFile = new File(project.docsDir, 'release/current.txt')
+    removedApiFile = new File(project.docsDir, 'release/removed.txt')
+    generateDocs = false
+
+    options {
+        addStringOption "templatedir",
+                "${supportRootFolder}/../../external/doclava/res/assets/templates-sdk"
+        addStringOption "stubpackages", "android.support.*"
+    }
+    exclude '**/BuildConfig.java'
+    exclude '**/R.java'
+}
+
+/**
+ * Returns the most recent API, optionally restricting to APIs before
+ * <code>beforeApi</code>.
+ *
+ * @param refApi the reference API version, ex. 25.0.0-SNAPSHOT
+ * @return the most recently released API file
+ */
+File getApiFile(String refApi = supportVersion, boolean previous = false, boolean release = false) {
+    def refMatcher = refApi =~ /^(\d+)\.(\d+)\.(\d+)(-.+)?$/
+    def refMajor = refMatcher[0][1] as int
+    def refMinor = refMatcher[0][2] as int
+    def refPatch = refMatcher[0][3] as int
+    def refExtra = refMatcher[0][4]
+
+    File apiDir = new File(ext.supportRootFolder, 'api')
+
+    if (!previous) {
+        // If this is a patch or release version, ignore the extra.
+        return new File(apiDir, "$refMajor.$refMinor.0"
+                + (!refExtra || refPatch || release ? "" : refExtra) + ".txt")
+    }
+
+    File lastFile = null
+    def lastMajor
+    def lastMinor
+
+    // Only look at released versions and snapshots thereof, ex. X.Y.0.txt or X.Y.0-SNAPSHOT.txt.
+    apiDir.eachFileMatch FileType.FILES, ~/(\d+)\.(\d+)\.0(-SNAPSHOT)?\.txt/, { File file ->
+        def matcher = file.name =~ /(\d+)\.(\d+)\.0(-SNAPSHOT)?\.txt/
+        def major = matcher[0][1] as int
+        def minor = matcher[0][2] as int
+
+        if (lastFile == null || major > lastMajor || (major == lastMajor && minor > lastMinor)) {
+            if (refMajor > major || (refMajor == major && refMinor > minor)) {
+                lastFile = file
+                lastMajor = major
+                lastMinor = minor
+            }
+        }
+    }
+
+    return lastFile
+}
+
+String stripExtension(String fileName) {
+    return fileName[0..fileName.lastIndexOf('.')-1]
+}
+
+// Make sure the API surface has not broken since the last release.
+def isPatchVersion = supportVersion ==~ /\d+\.\d+.[1-9]\d*(-.+)?/
+def isSnapshotVersion = supportVersion ==~ /\d+\.\d+.\d+-SNAPSHOT/
+def previousApiFile = getApiFile(project.supportVersion, !isPatchVersion)
+def whitelistFile = new File(
+        previousApiFile.parentFile, stripExtension(previousApiFile.name) + ".ignore")
+def checkApiRelease = createCheckApiTask("checkApiRelease", CHECK_API_CONFIG_RELEASE,
+        previousApiFile, generateApi.apiFile, whitelistFile).dependsOn(generateApi)
+
+// Allow a comma-delimited list of whitelisted errors.
+if (project.hasProperty("ignore")) {
+    checkApiRelease.whitelistErrors = ignore.split(',')
+}
+
+// Check whether the development API surface has changed.
+def verifyConfig = isPatchVersion != 0 ? CHECK_API_CONFIG_DEVELOP : CHECK_API_CONFIG_PATCH;
+def checkApi = createCheckApiTask("checkApi", verifyConfig, getApiFile(), generateApi.apiFile)
+        .dependsOn(generateApi, checkApiRelease)
+
+checkApi.group JavaBasePlugin.VERIFICATION_GROUP
+checkApi.description 'Verify the API surface.'
+
+rootProject.createArchive.dependsOn checkApi
+
+task verifyUpdateApiAllowed() {
+    // This could be moved to doFirst inside updateApi, but using it as a
+    // dependency with no inputs forces it to run even when updateApi is a
+    // no-op.
+    doLast {
+        if (isPatchVersion) {
+            throw new GradleException("Public APIs may not be modified in patch releases.")
+        } else if (isSnapshotVersion && getApiFile(supportVersion, false, true).exists()) {
+            throw new GradleException("Inconsistent version. Public API file already exists.")
+        } else if (!isSnapshotVersion && getApiFile().exists() && !project.hasProperty("force")) {
+            throw new GradleException("Public APIs may not be modified in finalized releases.")
+        }
+    }
+}
+
+task updateApi(type: UpdateApiTask, dependsOn: [checkApiRelease, verifyUpdateApiAllowed]) {
+    group JavaBasePlugin.VERIFICATION_GROUP
+    description 'Updates the candidate API file to incorporate valid changes.'
+    newApiFile = checkApiRelease.newApiFile
+    oldApiFile = getApiFile()
+    newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
+    oldRemovedApiFile = new File(supportRootFolder, 'api/removed.txt')
+    whitelistErrors = checkApiRelease.whitelistErrors
+    whitelistErrorsFile = checkApiRelease.whitelistErrorsFile
+}
+
+/**
+ * Converts the <code>toApi</code>.txt file (or current.txt if not explicitly
+ * defined using -PtoAPi=<file>) to XML format for use by JDiff.
+ */
+task newApiXml(type: ApiXmlConversionTask, dependsOn: configurations.doclava) {
+    classpath configurations.doclava.resolve()
+
+    if (project.hasProperty("toApi")) {
+        // Use an explicit API file.
+        inputApiFile = new File(rootProject.ext.supportRootFolder, "api/${toApi}.txt")
+    } else {
+        // Use the current API file (e.g. current.txt).
+        inputApiFile = generateApi.apiFile
+        dependsOn generateApi
+    }
+
+    int lastDot = inputApiFile.name.lastIndexOf('.')
+    outputApiXmlFile = new File(project.docsDir,
+            "release/" + inputApiFile.name.substring(0, lastDot) + ".xml")
+}
+
+/**
+ * Converts the <code>fromApi</code>.txt file (or the most recently released
+ * X.Y.Z.txt if not explicitly defined using -PfromAPi=<file>) to XML format
+ * for use by JDiff.
+ */
+task oldApiXml(type: ApiXmlConversionTask, dependsOn: configurations.doclava) {
+    classpath configurations.doclava.resolve()
+
+    if (project.hasProperty("fromApi")) {
+        // Use an explicit API file.
+        inputApiFile = new File(rootProject.ext.supportRootFolder, "api/${fromApi}.txt")
+    } else if (project.hasProperty("toApi") && toApi.matches(~/(\d+\.){2}\d+/)) {
+        // If toApi matches released API (X.Y.Z) format, use the most recently
+        // released API file prior to toApi.
+        inputApiFile = getApiFile(toApi, true)
+    } else {
+        // Use the most recently released API file.
+        inputApiFile = getApiFile();
+    }
+
+    int lastDot = inputApiFile.name.lastIndexOf('.')
+    outputApiXmlFile = new File(project.docsDir,
+            "release/" + inputApiFile.name.substring(0, lastDot) + ".xml")
+}
+
+/**
+ * Generates API diffs.
+ * <p>
+ * By default, diffs are generated for the delta between current.txt and the
+ * next most recent X.Y.Z.txt API file. Behavior may be changed by specifying
+ * one or both of -PtoApi and -PfromApi.
+ * <p>
+ * If both fromApi and toApi are specified, diffs will be generated for
+ * fromApi -> toApi. For example, 25.0.0 -> 26.0.0 diffs could be generated by
+ * using:
+ * <br><code>
+ *   ./gradlew generateDiffs -PfromApi=25.0.0 -PtoApi=26.0.0
+ * </code>
+ * <p>
+ * If only toApi is specified, it MUST be specified as X.Y.Z and diffs will be
+ * generated for (release before toApi) -> toApi. For example, 24.2.0 -> 25.0.0
+ * diffs could be generated by using:
+ * <br><code>
+ *   ./gradlew generateDiffs -PtoApi=25.0.0
+ * </code>
+ * <p>
+ * If only fromApi is specified, diffs will be generated for fromApi -> current.
+ * For example, lastApiReview -> current diffs could be generated by using:
+ * <br><code>
+ *   ./gradlew generateDiffs -PfromApi=lastApiReview
+ * </code>
+ * <p>
+ */
+task generateDiffs(type: JDiffTask, dependsOn: [configurations.jdiff, configurations.doclava,
+                                                oldApiXml, newApiXml, generateDocs]) {
+    // Base classpath is Android SDK, sub-projects add their own.
+    classpath = project.ext.androidJar
+
+    // JDiff properties.
+    oldApiXmlFile = oldApiXml.outputApiXmlFile
+    newApiXmlFile = newApiXml.outputApiXmlFile
+    newJavadocPrefix = "../../../../reference/"
+
+    String newApi = newApiXmlFile.name
+    int lastDot = newApi.lastIndexOf('.')
+    newApi = newApi.substring(0, lastDot)
+
+    // Javadoc properties.
+    docletpath = configurations.jdiff.resolve()
+    destinationDir = new File(project.docsDir, "online/sdk/support_api_diff/$newApi")
+    title = "Support&nbsp;Library&nbsp;API&nbsp;Differences&nbsp;Report"
+
+    exclude '**/BuildConfig.java'
+    exclude '**/R.java'
+}
+
+// configuration file for setting up api diffs and api docs
+void registerForDocsTask(Task task, Project subProject, releaseVariant) {
+    task.dependsOn releaseVariant.javaCompile
+    task.source {
+        def buildConfig = fileTree(releaseVariant.getGenerateBuildConfig().sourceOutputDir)
+        return releaseVariant.javaCompile.source.minus(buildConfig) +
+                fileTree(releaseVariant.aidlCompile.sourceOutputDir) +
+                fileTree(releaseVariant.outputs[0].processResources.sourceOutputDir)
+    }
+
+    task.classpath += releaseVariant.getCompileClasspath(null) +
+            files(releaseVariant.javaCompile.destinationDir)
+}
+
+// configuration file for setting up api diffs and api docs
+void registerJavaProjectForDocsTask(Task task, Project subProject, javaCompileTask) {
+    task.dependsOn javaCompileTask
+    task.source javaCompileTask.source
+    task.classpath += files(javaCompileTask.classpath) +
+            files(javaCompileTask.destinationDir)
+}
+
+subprojects { subProject ->
+    subProject.afterEvaluate { p ->
+        if (!p.hasProperty("noDocs") || !p.noDocs) {
+            if (p.hasProperty('android') && p.android.hasProperty('libraryVariants')) {
+                p.android.libraryVariants.all { v ->
+                    if (v.name == 'release') {
+                        registerForDocsTask(rootProject.generateDocs, p, v)
+                        registerForDocsTask(rootProject.generateApi, p, v)
+                        registerForDocsTask(rootProject.generateDiffs, p, v)
+                    }
+                }
+            } else if (p.hasProperty("compileJava")) {
+                registerJavaProjectForDocsTask(rootProject.generateDocs, p, p.compileJava)
+            }
+        }
+    }
+}
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
new file mode 100644
index 0000000..a483337
--- /dev/null
+++ b/buildSrc/init.gradle
@@ -0,0 +1,357 @@
+/*
+ * 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.
+ */
+
+import com.android.build.gradle.internal.coverage.JacocoPlugin
+import com.android.build.gradle.internal.coverage.JacocoReportTask
+import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
+import org.gradle.api.logging.configuration.ShowStacktrace
+
+def supportRoot = ext.supportRootFolder
+if (supportRoot == null) {
+    throw new RuntimeException("variable supportRootFolder is not set. you must set it before" +
+            " including this script")
+}
+def init = new Properties()
+ext.init = init
+ext.init.debugKeystore = file("${supportRoot}/development/keystore/debug.keystore")
+
+ext.runningInBuildServer = System.env.DIST_DIR != null && System.env.OUT_DIR != null
+
+apply from: "${supportRoot}/buildSrc/dependencies.gradle"
+ext.docs = [:]
+ext.docs.offline = rootProject.getProperties().containsKey("offlineDocs")
+ext.docs.dac = [
+        libraryroot: "android/support",
+        dataname: "SUPPORT_DATA"
+]
+
+def loadDefaultVersions() {
+    apply from: "${supportRootFolder}/buildSrc/versions.gradle"
+}
+
+def enableDoclavaAndJDiff(p) {
+    p.configurations {
+        doclava
+        jdiff
+    }
+
+    p.dependencies {
+        doclava project(':doclava')
+        jdiff project(':jdiff')
+        jdiff libs.xml_parser_apis
+        jdiff libs.xerces_impl
+    }
+    apply from: "${ext.supportRootFolder}/buildSrc/diff_and_docs.gradle"
+}
+
+def getFullSdkPath() {
+    final String osName = System.getProperty("os.name").toLowerCase();
+    final boolean isMacOsX =
+            osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx");
+    final String platform = isMacOsX ? 'darwin' : 'linux'
+    return "${repos.prebuiltsRoot}/fullsdk-${platform}"
+}
+
+def setSdkInLocalPropertiesFile() {
+    ext.buildToolsVersion = '26.0.0'
+    final String fullSdkPath = getFullSdkPath();
+    if (file(fullSdkPath).exists()) {
+        gradle.ext.currentSdk = 26
+        project.ext.androidJar =
+                files("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android.jar")
+        project.ext.androidSrcJar =
+                file("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android-stubs-src.jar")
+        project.ext.androidApiTxt = null
+        File props = file("local.properties")
+        props.write "sdk.dir=${fullSdkPath}"
+        ext.usingFullSdk = true
+    } else {
+        gradle.ext.currentSdk = 'current'
+        project.ext.androidJar = files("${repos.prebuiltsRoot}/sdk/current/android.jar")
+        project.ext.androidSrcJar = null
+        project.ext.androidApiTxt = file("${repos.prebuiltsRoot}/sdk/api/26.txt")
+        System.setProperty('android.dir', "${supportRootFolder}/../../")
+        File props = file("local.properties")
+        props.write "android.dir=../../"
+        ext.usingFullSdk = false
+    }
+}
+
+def setupRepoOutAndBuildNumber() {
+    ext.supportRepoOut = ''
+    ext.buildNumber = Integer.toString(ext.extraVersion)
+
+    /*
+     * With the build server you are given two env variables.
+     * The OUT_DIR is a temporary directory you can use to put things during the build.
+     * The DIST_DIR is where you want to save things from the build.
+     *
+     * The build server will copy the contents of DIST_DIR to somewhere and make it available.
+     */
+    if (ext.runningInBuildServer) {
+        buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build')
+                .getCanonicalFile()
+        project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()
+
+        // the build server does not pass the build number so we infer it from the last folder of
+        // the dist path.
+        ext.buildNumber = project.ext.distDir.getName()
+
+        // the build server should always print out full stack traces for any failures.
+        gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
+    } else {
+        buildDir = file("${ext.supportRootFolder}/../../out/host/gradle/frameworks/support/build")
+        project.ext.distDir = new File("${ext.supportRootFolder}/../../out/dist")
+    }
+    subprojects {
+        // Change buildDir first so that all plugins pick up the new value.
+        project.buildDir = new File("$project.parent.buildDir/../$project.name/build")
+    }
+    ext.supportRepoOut = new File(buildDir, 'support_repo')
+    ext.testApkDistOut = ext.distDir
+    ext.testResultsDistDir = new File(distDir, "host-test-reports")
+    ext.docsDir = new File(buildDir, 'javadoc')
+}
+
+def configureBuildOnServer() {
+    def buildOnServerTask = rootProject.tasks.create("buildOnServer")
+    rootProject.tasks.whenTaskAdded { task ->
+        if ("createArchive".equals(task.name)) {
+            buildOnServerTask.dependsOn task
+        }
+    }
+
+    subprojects {
+        project.tasks.whenTaskAdded { task ->
+            if ("assembleErrorProne".equals(task.name) || "assembleAndroidTest".equals(task.name)) {
+                buildOnServerTask.dependsOn task
+            }
+        }
+    }
+}
+
+def configureSubProjects() {
+    // lint every library
+    def lintTask = project.tasks.create("lint")
+    subprojects {
+        repos.addMavenRepositories(repositories)
+
+        // Only modify Android projects.
+        if (project.name.equals('doclava')
+                || project.name.equals('jdiff')
+                || project.name.equals('support-testutils')
+                || project.name.equals('noto-emoji-compat')) {
+            // disable tests and return
+            project.tasks.whenTaskAdded { task ->
+                if (task instanceof org.gradle.api.tasks.testing.Test) {
+                    task.enabled = false
+                }
+            }
+            return
+        }
+
+        // Current SDK is set in studioCompat.gradle.
+        project.ext.currentSdk = gradle.ext.currentSdk
+        apply plugin: 'maven'
+
+        version = rootProject.ext.supportVersion
+        group = 'com.android.support'
+
+        project.plugins.whenPluginAdded { plugin ->
+            def isAndroidLibrary = "com.android.build.gradle.LibraryPlugin"
+                    .equals(plugin.class.name)
+            def isAndroidApp = "com.android.build.gradle.AppPlugin".equals(plugin.class.name)
+            def isJavaLibrary = "org.gradle.api.plugins.JavaPlugin".equals(plugin.class.name)
+
+            if (isAndroidLibrary || isAndroidApp) {
+                project.android.buildToolsVersion = rootProject.buildToolsVersion
+
+                // Enable code coverage for debug builds only if we are not running inside the IDE,
+                // since enabling coverage reports breaks the method parameter resolution in the IDE
+                // debugger.
+                project.android.buildTypes.debug.testCoverageEnabled =
+                        !project.hasProperty('android.injected.invoked.from.ide')
+
+                // Copy the class files in a jar to be later used to generate code coverage report
+                project.android.testVariants.all { v ->
+                    // check if the variant has any source files
+                    // and test coverage is enabled
+                    if (v.buildType.testCoverageEnabled
+                            && v.sourceSets.any { !it.java.sourceFiles.isEmpty() }) {
+                        def jarifyTask = project.tasks.create(
+                                name: "package${v.name.capitalize()}ClassFilesForCoverageReport",
+                                type: Jar) {
+                            from v.testedVariant.javaCompile.destinationDir
+                            exclude "**/R.class"
+                            exclude "**/R\$*.class"
+                            exclude "**/BuildConfig.class"
+                            destinationDir file(project.distDir)
+                            archiveName "${project.archivesBaseName}-${v.baseName}-allclasses.jar"
+                        }
+
+                        def collectJacocoAntPackages = project.tasks.create(
+                                name: "collectJacocoAntPackages",
+                                type: Jar) {
+                            inputs.files project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
+                            from {
+                                project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
+                                        .resolvedConfiguration
+                                        .resolvedArtifacts.collect{ zipTree(it.getFile()) }} {
+                                    // exclude all the signatures the jar might have
+                                    exclude "META-INF/*.SF"
+                                    exclude "META-INF/*.DSA"
+                                    exclude "META-INF/*.RSA"
+                                }
+                            destinationDir file(project.distDir)
+                            archiveName "jacocoant.jar"
+                        }
+
+                        jarifyTask.dependsOn v.getJavaCompiler()
+                        v.assemble.dependsOn jarifyTask , collectJacocoAntPackages
+                    }
+                }
+
+                // Enforce NewApi lint check as fatal.
+                project.android.lintOptions.fatal 'NewApi'
+                lintTask.dependsOn project.lint
+            }
+
+            if (isAndroidLibrary || isJavaLibrary) {
+                // Add library to the aggregate dependency report.
+                task allDeps(type: DependencyReportTask) {}
+
+                project.afterEvaluate {
+                    Upload uploadTask = (Upload) project.tasks.uploadArchives;
+                    uploadTask.repositories.mavenDeployer {
+                        // Disable unique names for SNAPSHOTS so they can be updated in place.
+                        setUniqueVersion(false)
+                    }
+                    uploadTask.doLast {
+                        // Remove any invalid maven-metadata.xml files that may have been
+                        // created for SNAPSHOT versions that are *not* uniquely versioned.
+                        repositories.mavenDeployer.pom*.each { pom ->
+                            if (pom.version.endsWith('-SNAPSHOT')) {
+                                final File artifactDir = new File(
+                                        rootProject.ext.supportRepoOut,
+                                        pom.groupId.replace('.', '/')
+                                                + '/' + pom.artifactId
+                                                + '/' + pom.version)
+                                delete fileTree(dir: artifactDir,
+                                        include: 'maven-metadata.xml*')
+                            }
+                        }
+                    }
+
+                    // Before the upload, make sure the repo is ready.
+                    uploadTask.dependsOn rootProject.tasks.prepareRepo
+
+                    // Make the mainupload depend on this one.
+                    mainUpload.dependsOn uploadTask
+                }
+
+            }
+        }
+
+        // Copy instrumentation test APKs and app APKs into the dist dir
+        // For test apks, they are uploaded only if we have java test sources.
+        // For regular app apks, they are uploaded only if they have java sources.
+        project.tasks.whenTaskAdded { task ->
+            if (task.name.startsWith("packageDebug")) {
+                def testApk = task.name.contains("AndroidTest")
+                task.doLast {
+                    def source = testApk ? project.android.sourceSets.androidTest
+                            : project.android.sourceSets.main
+                    if (task.hasProperty("outputDirectory") && !source.java.sourceFiles.isEmpty()) {
+                        copy {
+                            from(task.outputDirectory)
+                            include '*.apk'
+                            into(rootProject.ext.testApkDistOut)
+                            rename { String fileName ->
+                                // multiple modules may have the same name so prefix the name with
+                                // the module's path to ensure it is unique.
+                                // e.g. palette-v7-debug-androidTest.apk becomes
+                                // support-palette-v7_palette-v7-debug-androidTest.apk
+                                "${project.getPath().replace(':', '-').substring(1)}_${fileName}"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // copy host side test results to DIST
+        project.tasks.whenTaskAdded { task ->
+            if (task instanceof org.gradle.api.tasks.testing.Test) {
+                def junitReport = task.reports.junitXml
+                if (junitReport.enabled) {
+                    def zipTask = project.tasks.create(name : "zipResultsOf${task.name.capitalize()}", type : Zip) {
+                        destinationDir(testResultsDistDir)
+                        // first one is always :, drop it.
+                        archiveName("${project.getPath().split(":").join("_").substring(1)}.zip")
+                    }
+                    if (project.rootProject.ext.runningInBuildServer) {
+                        task.ignoreFailures = true
+                    }
+                    task.finalizedBy zipTask
+                    task.doFirst {
+                        zipTask.from(junitReport.destination)
+                    }
+                }
+            }
+        }
+
+        project.afterEvaluate {
+            // The archivesBaseName isn't available initially, so set it now
+            def createZipTask = project.tasks.findByName("createSeparateZip")
+            if (createZipTask != null) {
+                createZipTask.appendix = archivesBaseName
+                createZipTask.from versionDir()
+            }
+        }
+
+        project.afterEvaluate { p ->
+            // remove dependency on the test so that we still get coverage even if some tests fail
+            p.tasks.findAll { it instanceof JacocoReportTask }.each { task ->
+                def toBeRemoved = new ArrayList()
+                def dependencyList = task.taskDependencies.values
+                dependencyList.each { dep ->
+                    if (dep instanceof String) {
+                        def t = tasks.findByName(dep)
+                        if (t instanceof DeviceProviderInstrumentTestTask) {
+                            toBeRemoved.add(dep)
+                            task.mustRunAfter(t)
+                        }
+                    }
+                }
+                toBeRemoved.each { dep ->
+                    dependencyList.remove(dep)
+                }
+            }
+        }
+    }
+}
+
+def setupRelease() {
+    apply from: "${ext.supportRootFolder}/buildSrc/release.gradle"
+}
+
+ext.init.enableDoclavaAndJDiff = this.&enableDoclavaAndJDiff
+ext.init.setSdkInLocalPropertiesFile = this.&setSdkInLocalPropertiesFile
+ext.init.setupRepoOutAndBuildNumber = this.&setupRepoOutAndBuildNumber
+ext.init.setupRelease = this.&setupRelease
+ext.init.loadDefaultVersions = this.&loadDefaultVersions
+ext.init.configureSubProjects = this.&configureSubProjects
+ext.init.configureBuildOnServer = this.&configureBuildOnServer
diff --git a/buildSrc/release.gradle b/buildSrc/release.gradle
new file mode 100644
index 0000000..5126d47
--- /dev/null
+++ b/buildSrc/release.gradle
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+import com.google.common.base.Charsets
+import com.google.common.io.Files
+import com.google.common.hash.HashCode
+import com.google.common.hash.HashFunction
+import com.google.common.hash.Hashing
+import java.nio.charset.Charset
+
+// Main task called by the build server.
+task(createArchive)
+
+// upload anchor for subprojects to upload their artifacts
+// to the local repo.
+task(mainUpload)
+
+rootProject.ext.repoWithHistoryOut = new File(buildDir, 'support_repo_with_history')
+
+// repository creation task
+task createRepository(type: Zip, dependsOn: mainUpload) {
+    from rootProject.ext.supportRepoOut
+    from "${repos.prebuiltsRoot}/maven_repo/android"
+    // if there are duplicates, pick the first one.
+    duplicatesStrategy "EXCLUDE"
+    destinationDir project.ext.distDir
+    into 'm2repository'
+    baseName = String.format("sdk-repo-linux-m2repository-%s", project.ext.buildNumber)
+}
+
+task createTopOfTreeRepository(type : Zip) {
+    description "Creates a maven repository that includes just the libraries compiled in this" +
+            " project, without any history from prebuilts."
+    from rootProject.ext.supportRepoOut
+    destinationDir rootProject.ext.distDir
+    into 'm2repository'
+    baseName = String.format("top-of-tree-m2repository-%s", project.ext.buildNumber)
+    dependsOn mainUpload
+}
+
+createArchive.dependsOn createRepository
+createRepository.dependsOn createTopOfTreeRepository
+
+// anchor for prepare repo. This is post unzip + sourceProp.
+task nukeRepoOut() {
+    description "This task clears the repo folder to ensure that we run a fresh build every" +
+            " time we create arhives. Otherwise, snapshots will accumulate in the builds folder."
+    doFirst {
+        rootProject.ext.supportRepoOut.deleteDir()
+        rootProject.ext.supportRepoOut.mkdirs()
+    }
+}
+
+task(prepareRepo)
+
+task(createXml).doLast({
+    def repoArchive = createRepository.archivePath
+    def repoArchiveName = createRepository.archiveName
+    def size = repoArchive.length()
+    def sha1 = getSha1(repoArchive)
+
+    def xml =
+            "<sdk:sdk-addon xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:sdk=\"http://schemas.android.com/sdk/android/addon/6\">\n\
+  <sdk:extra>\n\
+    <sdk:revision>\n\
+      <sdk:major>${project.ext.extraVersion}</sdk:major>\n\
+    </sdk:revision>\n\
+    <sdk:vendor-display>Android</sdk:vendor-display>\n\
+    <sdk:vendor-id>android</sdk:vendor-id>\n\
+    <sdk:name-display>Local Maven repository for Support Libraries</sdk:name-display>\n\
+    <sdk:path>m2repository</sdk:path>\n\
+    <sdk:archives>\n\
+      <sdk:archive>\n\
+       <sdk:size>${size}</sdk:size>\n\
+       <sdk:checksum type=\"sha1\">${sha1}</sdk:checksum>\n\
+       <sdk:url>${repoArchiveName}</sdk:url>\n\
+      </sdk:archive>\n\
+    </sdk:archives>\n\
+  </sdk:extra>\n\
+</sdk:sdk-addon>"
+
+    Files.write(xml, new File(project.ext.distDir, 'repo-extras.xml'), Charsets.UTF_8)
+})
+createArchive.dependsOn createXml
+createXml.dependsOn createRepository
+
+task(createSourceProp).doLast({
+    def sourceProp =
+            "Extra.VendorDisplay=Android\n\
+Extra.Path=m2repository\n\
+Archive.Arch=ANY\n\
+Extra.NameDisplay=Android Support Repository\n\
+Archive.Os=ANY\n\
+Pkg.Desc=Local Maven repository for Support Libraries\n\
+Pkg.Revision=${project.ext.extraVersion}.0.0\n\
+Extra.VendorId=android"
+
+    Files.write(sourceProp, new File(project.ext.supportRepoOut, 'source.properties'), Charsets.UTF_8)
+})
+createSourceProp.dependsOn nukeRepoOut
+prepareRepo.dependsOn createSourceProp
+
+/**
+ * Generates SHA1 hash for the specified file's absolute path.
+ *
+ * @param inputFile file to hash
+ * @return SHA1 hash
+ */
+String getSha1(File inputFile) {
+    HashFunction hashFunction = Hashing.sha1()
+    HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charset.forName("UTF-8"))
+    return hashCode.toString()
+}
diff --git a/buildSrc/repos.gradle b/buildSrc/repos.gradle
new file mode 100644
index 0000000..caedf3a
--- /dev/null
+++ b/buildSrc/repos.gradle
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+
+def supportRoot = ext.supportRootFolder
+if (supportRoot == null) {
+    throw new RuntimeException("variable supportRootFolder is not set. you must set it before" +
+            " including this script")
+}
+
+def checkoutRoot = "${supportRoot}/../.."
+ext.repos = new Properties()
+ext.repos.prebuiltsRoot = "${checkoutRoot}/prebuilts"
+ext.repos.prebuiltsRootUri = "file://${repos.prebuiltsRoot}"
+
+ext.repoNames = ["${repos.prebuiltsRoot}/gradle-plugin",
+                 "${repos.prebuiltsRoot}/tools/common/m2/repository",
+                 "${repos.prebuiltsRoot}/tools/common/m2/internal",
+                 "${repos.prebuiltsRoot}/maven_repo/android"]
+
+/**
+ * Adds maven repositories to the given repository handler.
+ */
+def addMavenRepositories(RepositoryHandler handler) {
+    repoNames.each { repo ->
+        handler.maven {
+            url repo
+        }
+    }
+    if (System.getenv("ALLOW_PUBLIC_REPOS") != null) {
+        handler.mavenCentral()
+        handler.jcenter()
+        handler.google()
+    }
+    def androidPluginRepoOverride = System.getenv("GRADLE_PLUGIN_REPO")
+    if (androidPluginRepoOverride != null) {
+        handler.maven {
+            url androidPluginRepoOverride
+        }
+    }
+}
+
+ext.repos.addMavenRepositories = this.&addMavenRepositories
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy b/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy
new file mode 100644
index 0000000..f9a2581
--- /dev/null
+++ b/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.support;
+
+import org.gradle.api.Project
+
+/**
+ * Extension for {@link SupportLibraryPlugin}.
+ */
+class SupportLibraryExtension {
+    Project project
+    String name;
+    String description;
+    String inceptionYear;
+    Collection<License> licenses = [];
+
+    SupportLibraryExtension(Project project) {
+        this.project = project
+    }
+
+    License license(Closure closure) {
+        def license = project.configure(new License(), closure)
+        licenses.add(license)
+        return license
+    }
+
+    class License {
+        String name;
+        String url;
+
+        void url(String p) {
+            url = p
+        }
+
+        void name(String p) {
+            name = p
+        }
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
new file mode 100644
index 0000000..1d07f51
--- /dev/null
+++ b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+package android.support
+
+import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.api.LibraryVariant
+import com.android.builder.core.BuilderConstants
+import com.google.common.collect.ImmutableMap
+import net.ltgt.gradle.errorprone.ErrorProneBasePlugin
+import net.ltgt.gradle.errorprone.ErrorProneToolChain
+import org.gradle.api.Action
+import org.gradle.api.JavaVersion
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.artifacts.maven.MavenDeployer
+import org.gradle.api.tasks.Upload
+import org.gradle.api.tasks.bundling.Jar
+
+/**
+ * Support library specific com.android.library plugin that sets common configurations needed for
+ * support library modules.
+ */
+class SupportLibraryPlugin implements Plugin<Project> {
+    private static final String INSTRUMENTATION_RUNNER =
+            "android.support.test.runner.AndroidJUnitRunner";
+
+    @Override
+    public void apply(Project project) {
+        SupportLibraryExtension supportLibraryExtension =
+                project.extensions.create("supportLibrary", SupportLibraryExtension, project);
+
+        project.apply(ImmutableMap.of("plugin", "com.android.library"));
+        project.apply(ImmutableMap.of("plugin", ErrorProneBasePlugin.class));
+
+        LibraryExtension library = project.extensions.findByType(LibraryExtension.class);
+
+        library.compileSdkVersion project.currentSdk
+
+        library.defaultConfig {
+            // Update the version meta-data in each Manifest.
+            addManifestPlaceholders(["support-version": project.rootProject.supportVersion,
+                                     "target-sdk-version": project.currentSdk])
+
+            // Set test related options.
+            testInstrumentationRunner INSTRUMENTATION_RUNNER
+        }
+
+        library.signingConfigs {
+            debug {
+                // Use a local debug keystore to avoid build server issues.
+                storeFile project.rootProject.init.debugKeystore
+            }
+        }
+
+        library.sourceSets {
+            main {
+                // We use a non-standard manifest path.
+                manifest.srcFile 'AndroidManifest.xml'
+            }
+
+            androidTest {
+                // We use a non-standard test directory structure.
+                root 'tests'
+                java.srcDir 'tests/src'
+                res.srcDir 'tests/res'
+                manifest.srcFile 'tests/AndroidManifest.xml'
+            }
+        }
+
+        // Always lint check NewApi as fatal.
+        library.lintOptions {
+            abortOnError true
+            ignoreWarnings true
+
+            // Write output directly to the console (and nowhere else).
+            textOutput 'stderr'
+            textReport true
+            htmlReport false
+            //xmlReport false
+
+            // Format output for convenience.
+            explainIssues true
+            noLines false
+            quiet true
+
+            // Always fail on NewApi.
+            error 'NewApi'
+
+            // TODO(aurimas): figure out the issue with missing translation check
+            disable 'MissingTranslation', 'ExtraTranslation'
+
+            // Set baseline file for all legacy lint warnings.
+            baseline new File(project.projectDir, "/lint-baseline.xml")
+        }
+
+        // Java 8 is only fully supported on API 24+ and not all Java 8 features are binary
+        // compatible with API < 24, so use Java 7 for both source AND target.
+        library.compileOptions {
+            sourceCompatibility JavaVersion.VERSION_1_7
+            targetCompatibility JavaVersion.VERSION_1_7
+        }
+
+        // Create sources jar for release builds
+        library.getLibraryVariants().all(new Action<LibraryVariant>() {
+            @Override
+            public void execute(LibraryVariant libraryVariant) {
+                if (!libraryVariant.getBuildType().getName().equals(BuilderConstants.RELEASE)) {
+                    return; // Skip non-release builds.
+                }
+
+                Jar sourceJar = project.getTasks().create("sourceJarRelease", Jar.class);
+                sourceJar.setClassifier("sources");
+                sourceJar.from(library.getSourceSets().findByName("main").getJava().getSrcDirs());
+                project.getArtifacts().add("archives", sourceJar);
+            }
+        });
+
+        // Set uploadArchives options.
+        Upload uploadTask = (Upload) project.getTasks().getByName("uploadArchives");
+        project.afterEvaluate {
+            uploadTask.repositories {
+                mavenDeployer {
+                    repository(url: project.uri(project.rootProject.supportRepoOut))
+                }
+            };
+            uploadTask.getRepositories().withType(MavenDeployer.class, new Action<MavenDeployer>() {
+                @Override
+                public void execute(MavenDeployer mavenDeployer) {
+                    mavenDeployer.getPom().project {
+                        name supportLibraryExtension.getName()
+                        description supportLibraryExtension.getDescription()
+                        url 'http://developer.android.com/tools/extras/support-library.html'
+                        inceptionYear supportLibraryExtension.getInceptionYear()
+
+                        licenses {
+                            license {
+                                name 'The Apache Software License, Version 2.0'
+                                url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                                distribution 'repo'
+                            }
+
+                            supportLibraryExtension.getLicenses().each {
+                                SupportLibraryExtension.License supportLicense ->
+                                    license {
+                                        name supportLicense.name
+                                        url supportLicense.url
+                                        distribution 'repo'
+                                    }
+                            }
+                        }
+
+                        scm {
+                            url "http://source.android.com"
+                            connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
+                        }
+                        developers {
+                            developer {
+                                name 'The Android Open Source Project'
+                            }
+                        }
+                    }
+                }
+            });
+
+            if (project.rootProject.usingFullSdk) {
+                // Library projects don't run lint by default, so set up dependency.
+                uploadTask.dependsOn project.getTasks().getByName("lintRelease")
+            }
+        }
+
+        final ErrorProneToolChain toolChain = ErrorProneToolChain.create(project);
+        library.getBuildTypes().create("errorProne")
+        library.getLibraryVariants().all(new Action<LibraryVariant>() {
+            @Override
+            void execute(LibraryVariant libraryVariant) {
+                if (libraryVariant.getBuildType().getName().equals("errorProne")) {
+                    libraryVariant.getJavaCompile().setToolChain(toolChain);
+
+                    libraryVariant.getJavaCompile().options.compilerArgs += [
+                            '-XDcompilePolicy=simple', // Workaround for b/36098770
+
+                            // Enforce the following checks.
+                            '-Xep:MissingOverride:ERROR',
+                            '-Xep:NarrowingCompoundAssignment:ERROR',
+                            '-Xep:ClassNewInstance:ERROR',
+                            '-Xep:ClassCanBeStatic:ERROR',
+                            '-Xep:SynchronizeOnNonFinalField:ERROR'
+                    ]
+                }
+            }
+        })
+    }
+}
diff --git a/buildSrc/src/main/groovy/android/support/checkapi/ApiXmlConversionTask.groovy b/buildSrc/src/main/groovy/android/support/checkapi/ApiXmlConversionTask.groovy
new file mode 100644
index 0000000..bd4bda7
--- /dev/null
+++ b/buildSrc/src/main/groovy/android/support/checkapi/ApiXmlConversionTask.groovy
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.support.checkapi
+
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.JavaExec
+import org.gradle.api.tasks.OutputFile
+
+public class ApiXmlConversionTask extends JavaExec {
+
+    @InputFile
+    File inputApiFile
+
+    @OutputFile
+    File outputApiXmlFile
+
+    public ApiXmlConversionTask() {
+        maxHeapSize = "1024m"
+
+        // Despite this tool living in ApiCheck, its purpose more fits with doclava's "purposes",
+        // generation of api files in this case. Thus, I am putting this in the doclava package.
+        setMain('com.google.doclava.apicheck.ApiCheck')
+    }
+
+    /**
+     * "Configures" this ApiXmlConversionTask with parameters that might not be at their final
+     * values until this task is run.
+     */
+    private configureApiXmlConversionTask() {
+        setArgs([
+                '-convert2xml',
+                getInputApiFile().absolutePath,
+                getOutputApiXmlFile().absolutePath
+        ])
+    }
+
+    @Override
+    public void exec() {
+        configureApiXmlConversionTask()
+        super.exec()
+    }
+}
diff --git a/buildSrc/src/main/groovy/android/support/checkapi/CheckApiTask.groovy b/buildSrc/src/main/groovy/android/support/checkapi/CheckApiTask.groovy
index c245f77..2aa9d21 100644
--- a/buildSrc/src/main/groovy/android/support/checkapi/CheckApiTask.groovy
+++ b/buildSrc/src/main/groovy/android/support/checkapi/CheckApiTask.groovy
@@ -1,142 +1,89 @@
-package android.support.checkapi;
+/*
+ * 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.
+ */
+
+package android.support.checkapi
 
 import org.gradle.api.DefaultTask
-import org.gradle.api.Nullable
 import org.gradle.api.GradleException
 import org.gradle.api.InvalidUserDataException
+import org.gradle.api.Nullable
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.ParallelizableTask
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFile
-import org.gradle.process.ExecResult
+
+import java.security.MessageDigest
 
 /**
- * A task to invoke Doclava's ApiCheck tool.
+ * Task used to verify changes between two API files.
  * <p>
- * By default, any API changes will be flagged as errors (strict mode). This
- * can be loosened to merely backwards compatibility checks using
- * {@link #configureAsBackwardsCompatCheck()}.
+ * This task may be configured to ignore, warn, or fail with a message for a specific set of
+ * Doclava-defined error codes. See {@link com.google.doclava.Errors} for a complete list of
+ * supported error codes.
+ * <p>
+ * Specific failures may be ignored by specifying a list of SHAs in {@link #whitelistErrors}. Each
+ * SHA is unique to a specific API change and is logged to the error output on failure.
  */
-@ParallelizableTask
 public class CheckApiTask extends DefaultTask {
+    /** Character that resets console output color. */
+    private static final String ANSI_RESET = "\u001B[0m";
 
-    // see external/doclava/src/com/google/doclava/Errors.java for error code meanings.
+    /** Character that sets console output color to red. */
+    private static final String ANSI_RED = "\u001B[31m";
 
-    // 2-6 are all the error represented added APIs.
-    private static final def API_ADDITIONS = (2..6)
+    /** Character that sets console output color to yellow. */
+    private static final String ANSI_YELLOW = "\u001B[33m";
 
-    // Everything past the addition errors except for:
-    // 15: CHANGED_VOLATILE
-    // 17: CHANGED_VALUE
-    // 22: CHANGED_NATIVE
-    // 27: REMOVED_FINAL
-    private static final def API_CHANGES_AND_REMOVALS = (7..27) - [15, 17, 22, 27]
-
-    // ApiCheck error types which will cause the build to fail
-    // Basically, error on everything but
-    // 15: CHANGED_VOLATILE
-    // 17: CHANGED_VALUE
-    // 22: CHANGED_NATIVE
-    // 27: REMOVED_FINAL
-    // But include the "catch all"
-    // 1: PARSE_ERROR
-    public static final def DEFAULT_CHECK_API_ERRORS = Collections.unmodifiableSet(
-            ([1] + API_ADDITIONS + API_CHANGES_AND_REMOVALS) as Set
-    )
-
-    // Ones we want to emit warnings for.
-    // 15: CHANGED_VOLATILE
-    // 17: CHANGED_VALUE
-    // 27: REMOVED_FINAL
-    public static final def DEFAULT_CHECK_API_WARNINGS = Collections.unmodifiableSet(
-            [15, 17, 27] as Set
-    )
-
-    // Ones to just to just ignore as they usually aren't useful.
-    // 22: CHANGED_NATIVE
-    public static final def DEFAULT_CHECK_API_HIDDEN = Collections.singleton(22)
-
-    // ApiCheck error types for backwards compatiblity API checks which will cause the build to fail.
-    // Allow additions, but not removals or changes, except for deprecation changes.
-    // 24: CHANGED_DEPRECATED
-    // But include the "catch all"
-    // 1: PARSE_ERROR
-    public static final def DEFAULT_CHECK_API_BACKWARDS_COMPAT_ERRORS = Collections.unmodifiableSet(
-            ([1] + API_CHANGES_AND_REMOVALS - [24]) as Set
-    )
-
-    // Same as the normal warnings, but with deprecation added as a warning type.
-    public static final def DEFAULT_CHECK_API_BACKWARDS_COMPAT_WARNINGS = Collections.unmodifiableSet(
-            (DEFAULT_CHECK_API_WARNINGS + [24]) as Set
-    )
-
-    // Same as the normal hidden ones + all API addition errors.
-    public static final def DEFAULT_CHECK_API_BACKWARDS_COMPAT_HIDDEN = Collections.unmodifiableSet(
-            (DEFAULT_CHECK_API_HIDDEN + API_ADDITIONS) as Set
-    )
-
-
-    // Error messages shamelessly ripped from AOSP's check-api error message.
-    // For these templates, the parameters are:
-    // See #getOnFailMessage()
-    // 1: oldApiFile.name
-    // 2: oldRemovedApiFile.name
-    // 3: updateApiTaskPath
-    // 4: checkApiTaskPath
-    private static final String DEFAULT_ERROR_MESSAGE_WITH_UPDATE_TASK =
-            '''******************************
-You have tried to change the API from what has been previously approved.
-
-To make these errors go away, you have two choices:
-   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
-      errors above.
-
-   2) You can update %1$s and %2$s
-      by executing the following command:
-          ./gradlew %3$s
-
-      To submit the revised %1$s and %2$s
-      to the main repository, you will need approval.
-
-   You can re-run just the API checks using the command:
-       ./gradlew %4$s
-******************************'''
-
-    private static final String DEFAULT_ERROR_MESSAGE_WITHOUT_UPDATE_TASK =
-            '''******************************
-    You have tried to change the API from what has been previously approved.
-
-    To make these errors go away you can add "@hide" javadoc comments to the methods, etc. listed
-    in the errors above.
-
-    You can re-run just the API checks using the command:
-        ./gradlew %4$s
-******************************'''
-
-    private static final String DEFAULT_ERROR_MESSAGE_FOR_BACKWARDS_COMPAT =
-            '''******************************
-    You have tried to change the API from what has been previously released in
-    an SDK.  Please fix the errors listed above.
-
-    You can re-run just the API checks using the command:
-        ./gradlew %4$s
-******************************'''
-
+    /** API file that represents the existing API surface. */
     @InputFile
     File oldApiFile
+
+    /** API file that represents the existing API surface's removals. */
     @InputFile
     File oldRemovedApiFile
 
+    /** API file that represents the candidate API surface. */
     @InputFile
     File newApiFile
+
+    /** API file that represents the candidate API surface's removals. */
     @InputFile
     File newRemovedApiFile
 
+    /** Optional file containing a newline-delimited list of error SHAs to ignore. */
+    @Nullable
+    File whitelistErrorsFile
+
+    @Optional
+    @Nullable
+    @InputFile
+    File getWhiteListErrorsFileInput() {
+        // Gradle requires non-null InputFiles to exist -- even with Optional -- so work around that
+        // by returning null for this field if the file doesn't exist.
+        if (whitelistErrorsFile && whitelistErrorsFile.exists()) {
+            return whitelistErrorsFile;
+        }
+        return null;
+    }
+
     /**
-     * If non-null, the list of packages to ignore any API checks on.<br>
+     * Optional list of packages to ignore.
+     * <p>
      * Packages names will be matched exactly; sub-packages are not automatically recognized.
      */
     @Optional
@@ -145,8 +92,9 @@
     Collection ignoredPackages = null
 
     /**
-     * If non-null, the list of classes to ignore any API checks on.<br>
-     * Class names will be matched exactly by their full qualified names; inner classes are not
+     * Optional list of classes to ignore.
+     * <p>
+     * Class names will be matched exactly by their fully-qualified names; inner classes are not
      * automatically recognized.
      */
     @Optional
@@ -154,6 +102,15 @@
     @Input
     Collection ignoredClasses = null
 
+    /**
+     * Optional set of error SHAs to ignore.
+     * <p>
+     * Each error SHA is unique to a specific API change.
+     */
+    @Optional
+    @Input
+    Set whitelistErrors = []
+
     @InputFiles
     Collection<File> doclavaClasspath
 
@@ -173,28 +130,33 @@
         mOutputFile = outputFile
     }
 
+    /**
+     * List of Doclava error codes to treat as errors.
+     * <p>
+     * See {@link com.google.doclava.Errors} for a complete list of error codes.
+     */
     @Input
-    Collection checkApiErrors = DEFAULT_CHECK_API_ERRORS
+    Collection checkApiErrors
 
+    /**
+     * List of Doclava error codes to treat as warnings.
+     * <p>
+     * See {@link com.google.doclava.Errors} for a complete list of error codes.
+     */
     @Input
-    Collection checkApiWarnings = DEFAULT_CHECK_API_WARNINGS
+    Collection checkApiWarnings
 
+    /**
+     * List of Doclava error codes to ignore.
+     * <p>
+     * See {@link com.google.doclava.Errors} for a complete list of error codes.
+     */
     @Input
-    Collection checkApiHidden = DEFAULT_CHECK_API_HIDDEN
+    Collection checkApiHidden
 
-    // The following are optional. They are only used for constructing the failure message.
-    @Nullable
-    @Optional
-    String checkApiTaskPath;
-    @Nullable
-    @Optional
-    String updateApiTaskPath;
-
-    private String checkApiTaskPathToPrint() {
-        return getCheckApiTaskPath() ?: this.path
-    }
-
-    private def mOnFailMessage
+    /** Message to display on API check failure. */
+    @Input
+    String onFailMessage
 
     public CheckApiTask() {
         group = 'Verification'
@@ -202,7 +164,8 @@
     }
 
     private Set<File> collectAndVerifyInputs() {
-        Set<File> apiFiles = [getOldApiFile(), getNewApiFile(), getOldRemovedApiFile(), getNewRemovedApiFile()] as Set
+        Set<File> apiFiles = [getOldApiFile(), getNewApiFile(), getOldRemovedApiFile(),
+                              getNewRemovedApiFile()] as Set
         if (apiFiles.size() != 4) {
             throw new InvalidUserDataException("""Conflicting input files:
     oldApiFile: ${getOldApiFile()}
@@ -214,70 +177,6 @@
         return apiFiles;
     }
 
-    /**
-     * Returns the preprocessed failure message.<br>
-     * This string will be passed to {@link String#format(String, Object[])} as the format
-     * string with the given parameters to get the failure message.<br>
-     * The arguments used are:<br>
-     * 1 (String): oldApiFile.name<br>
-     * 2 (String): oldRemovedApiFile.name<br>
-     * 3 (String): updateApiTaskPath<br>
-     * 4 (String): checkApiTaskPath<br>
-     * The format string need not use all, or even any, of these arguments.
-     */
-    public String getOnFailMessage() {
-        return (mOnFailMessage == null ?
-                (getUpdateApiTaskPath() == null ?
-                        DEFAULT_ERROR_MESSAGE_WITHOUT_UPDATE_TASK :
-                        DEFAULT_ERROR_MESSAGE_WITH_UPDATE_TASK
-                ) : mOnFailMessage.toString())
-    }
-
-    /**
-     * Returns the failure error message after all the arguments have been processed through
-     * {@link String#format(String, Object[])}. This String is what will be used as the
-     * error mesage upon failure.<br>
-     * See {@link #getOnFailMessage()} for how the arguments are evaluated.
-     */
-    public String getOnFailMessageFormatted() {
-        return String.format(getOnFailMessage(),
-                getOldApiFile().name,
-                getOldRemovedApiFile().name,
-                getUpdateApiTaskPath(),
-                getCheckApiTaskPath())
-    }
-
-    /**
-     * Sets the preprocessed failure message.<br>
-     * The given string will be passed to {@link String#format(String, Object[])} as the format
-     * string with the given parameters to get the final failure message.<br>
-     * The arguments used are:<br>
-     * 1 (String): oldApiFileName<br>
-     * 2 (String): oldRemovedApiFileName<br>
-     * 3 (String): updateApiTaskPath<br>
-     * 4 (String): checkApiTaskPath<br>
-     * The format string need not use all, or even any, of these arguments.
-     */
-    public void setOnFailMessage(Object onFailMessage) {
-        mOnFailMessage = onFailMessage
-    }
-
-    /**
-     * Configures this CheckApiTask with reasonable defaults for backwards compatibility checks,
-     * which are a bit looser than the normal defaults of erroring on any changes.<br>
-     * In particular, this will cause the api check to allow additions of new APIs, though removals
-     * and changes of existing APIs will still be marked as errors.<p>
-     *
-     * Please note that this will set several properties of this task, overwriting any values they
-     * may already be set to. This method is meant to be called first thing when configuring this CheckApiTask.
-     */
-    public void configureAsBackwardsCompatCheck() {
-        checkApiErrors = DEFAULT_CHECK_API_BACKWARDS_COMPAT_ERRORS
-        checkApiWarnings = DEFAULT_CHECK_API_BACKWARDS_COMPAT_WARNINGS
-        checkApiHidden = DEFAULT_CHECK_API_BACKWARDS_COMPAT_HIDDEN
-        mOnFailMessage = DEFAULT_ERROR_MESSAGE_FOR_BACKWARDS_COMPAT
-    }
-
     public void setCheckApiErrors(Collection errors) {
         // Make it serializable.
         checkApiErrors = errors as int[]
@@ -295,13 +194,12 @@
 
     @TaskAction
     public void exec() {
-        // TODO(csyoung) Option to run this within the build JVM rather than always fork?
         final def apiFiles = collectAndVerifyInputs()
-        // TODO(csyoung) Right now, it is difficult to get the exit code of an ExecTask (including
-        // JavaExec), and it is also difficult to have a custom error message on failure. But it is
-        // easy to get the exit code with Project#javaexec.
+
+        OutputStream errStream = new ByteArrayOutputStream()
+
         // If either of those gets tweaked, then this should be refactored to extend JavaExec.
-        ExecResult result = project.javaexec {
+        project.javaexec {
             // Put Doclava on the classpath so we can get the ApiCheck class.
             classpath(getDoclavaClasspath())
             main = 'com.google.doclava.apicheck.ApiCheck'
@@ -327,12 +225,45 @@
 
             args(apiFiles.collect( { it.absolutePath } ))
 
+            // Redirect error output so that we can whitelist specific errors.
+            errorOutput = errStream
+
             // We will be handling failures ourselves with a custom message.
             ignoreExitValue = true
         }
 
-        if (result.exitValue != 0) {
-            throw new GradleException(getOnFailMessageFormatted())
+        // Load the whitelist file, if present.
+        if (whitelistErrorsFile && whitelistErrorsFile.exists()) {
+            whitelistErrors += whitelistErrorsFile.readLines()
+        }
+
+        // Parse the error output.
+        def unparsedErrors = []
+        def ignoredErrors = []
+        def parsedErrors = []
+        errStream.toString().split("\n").each {
+            if (it) {
+                def matcher = it =~ ~/^(.+):(.+): (\w+) (\d+): (.+)$/
+                if (!matcher) {
+                    unparsedErrors += [it]
+                } else if (matcher[0][3] == "error") {
+                    def hash = getShortHash(matcher[0][5]);
+                    def error = matcher[0][1..-1] + [hash]
+                    if (hash in whitelistErrors) {
+                        ignoredErrors += [error]
+                    } else {
+                        parsedErrors += [error]
+                    }
+                }
+            }
+        }
+
+        unparsedErrors.each { error -> logger.error "$ANSI_RED$error$ANSI_RESET" }
+        parsedErrors.each { logger.error "$ANSI_RED${it[5]}$ANSI_RESET ${it[4]}"}
+        ignoredErrors.each { logger.warn "$ANSI_YELLOW${it[5]}$ANSI_RESET ${it[4]}"}
+
+        if (unparsedErrors || parsedErrors) {
+            throw new GradleException(onFailMessage)
         }
 
         // Just create a dummy file upon completion. Without any outputs, Gradle will run this task
@@ -341,4 +272,11 @@
         outputFile.parentFile.mkdirs()
         outputFile.createNewFile()
     }
+
+    def getShortHash(src) {
+        return MessageDigest.getInstance("SHA-1")
+                .digest(src.toString().bytes)
+                .encodeHex()
+                .toString()[-7..-1]
+    }
 }
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/android/support/checkapi/UpdateApiTask.groovy b/buildSrc/src/main/groovy/android/support/checkapi/UpdateApiTask.groovy
index 1170b0b..944154e 100644
--- a/buildSrc/src/main/groovy/android/support/checkapi/UpdateApiTask.groovy
+++ b/buildSrc/src/main/groovy/android/support/checkapi/UpdateApiTask.groovy
@@ -1,7 +1,26 @@
+/*
+ * 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.
+ */
+
 package android.support.checkapi;
 
 import org.gradle.api.DefaultTask
+import org.gradle.api.Nullable
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.WorkResult
@@ -12,11 +31,20 @@
     @InputFile
     File newRemovedApiFile
 
+    @Input
+    @Optional
+    Set whitelistErrors = []
+
     @OutputFile
     File oldApiFile
     @OutputFile
     File oldRemovedApiFile
 
+    @OutputFile
+    @Optional
+    @Nullable
+    File whitelistErrorsFile
+
     private WorkResult copyFromToFile(File src, File dest) {
         return project.copy {
             from src
@@ -29,6 +57,15 @@
     public void doUpdate() {
         copyFromToFile(getNewApiFile(), getOldApiFile())
         copyFromToFile(getNewRemovedApiFile(), getOldRemovedApiFile())
-        project.logger.warn("Updated ${getOldApiFile().name} and ${getOldRemovedApiFile().name} API files.")
+
+        if (whitelistErrorsFile && !whitelistErrors.empty) {
+            if (whitelistErrorsFile.exists()) {
+                whitelistErrors.removeAll(whitelistErrorsFile.readLines())
+            }
+            whitelistErrors.each { whitelistErrorsFile << "$it\n" }
+            logger.lifecycle "Whitelisted ${whitelistErrors.size()} error(s)..."
+        }
+
+        logger.lifecycle "Wrote public API definition to ${oldApiFile.name}"
     }
 }
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java b/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java
index 4984e9f..db3f318 100644
--- a/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java
+++ b/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java
@@ -16,39 +16,34 @@
 
 package android.support.doclava;
 
-import org.gradle.external.javadoc.JavadocOptionFileOption;
+import org.gradle.external.javadoc.internal.AbstractJavadocOptionFileOption;
 import org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Iterator;
 
 /**
  * This class is used to hold complex argument(s) to doclava
  */
-public class DoclavaJavadocOptionFileOption implements JavadocOptionFileOption<Iterable<String>> {
-    private final String option;
-    private Iterable<String> args;
+public class DoclavaJavadocOptionFileOption extends
+        AbstractJavadocOptionFileOption<Iterable<String>> {
 
     public DoclavaJavadocOptionFileOption(String option) {
-        this.option = option;
+        super(option, null);
     }
 
-    public Iterable<String> getValue() {
-        return args;
+    public DoclavaJavadocOptionFileOption(String option, Iterable<String> value) {
+        super(option, value);
     }
 
-    public void setValue(Iterable<String> args) {
-        this.args = args;
-    }
-
-    public String getOption() {
-        return option;
-    }
-
+    @Override
     public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
         writerContext.writeOptionHeader(getOption());
+
+        final Iterable<String> args = getValue();
         if (args != null) {
-            Iterator<String> iter = args.iterator();
+            final Iterator<String> iter = args.iterator();
             while (true) {
                 writerContext.writeValue(iter.next());
                 if (!iter.hasNext()) {
@@ -57,6 +52,24 @@
                 writerContext.write(" ");
             }
         }
+
         writerContext.newLine();
     }
-}
\ No newline at end of file
+
+    /**
+     * @return a deep copy of the option
+     */
+    public DoclavaJavadocOptionFileOption duplicate() {
+        final Iterable<String> value = getValue();
+        final ArrayList<String> valueCopy;
+        if (value != null) {
+            valueCopy = new ArrayList<>();
+            for (String item : value) {
+                valueCopy.add(item);
+            }
+        } else {
+            valueCopy = null;
+        }
+        return new DoclavaJavadocOptionFileOption(getOption(), valueCopy);
+    }
+}
diff --git a/buildSrc/src/main/groovy/android/support/doclava/DoclavaMultilineJavadocOptionFileOption.java b/buildSrc/src/main/groovy/android/support/doclava/DoclavaMultilineJavadocOptionFileOption.java
index 64b2fe7..d4b1668 100644
--- a/buildSrc/src/main/groovy/android/support/doclava/DoclavaMultilineJavadocOptionFileOption.java
+++ b/buildSrc/src/main/groovy/android/support/doclava/DoclavaMultilineJavadocOptionFileOption.java
@@ -16,55 +16,56 @@
 
 package android.support.doclava;
 
-import org.gradle.external.javadoc.JavadocOptionFileOption;
+import org.gradle.external.javadoc.internal.AbstractJavadocOptionFileOption;
 import org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
 /**
  * This class is used to hold complex argument(s) to doclava
  */
-public class DoclavaMultilineJavadocOptionFileOption implements JavadocOptionFileOption<List<List<String>>> {
-    private final String option;
-    private List<List<String>> args;
+public class DoclavaMultilineJavadocOptionFileOption extends
+        AbstractJavadocOptionFileOption<List<List<String>>> {
 
     public DoclavaMultilineJavadocOptionFileOption(String option) {
-        this.option = option;
+        super(option, null);
     }
 
-    public List<List<String>> getValue() {
-        return args;
+    public DoclavaMultilineJavadocOptionFileOption(String option, List<List<String>> value) {
+        super(option, value);
     }
 
+    @Override
     public void setValue(List<List<String>> value) {
-        if (this.args == null) {
-            this.args = new ArrayList<List<String>>(value.size());
+        final List<List<String>> args = getValue();
+        if (args == null) {
+            super.setValue(new ArrayList<List<String>>(value));
+        } else {
+            args.addAll(value);
         }
-        this.args.addAll(value);
     }
 
     public void add(List<String>... moreArgs) {
-        if (this.args == null) {
-            this.args = new ArrayList<List<String>>(moreArgs.length);
-        }
-        for (List<String> arg : moreArgs) {
-            this.args.add(arg);
+        final List<List<String>> args = getValue();
+        if (args == null) {
+            super.setValue(new ArrayList<List<String>>(Arrays.asList(moreArgs)));
+        } else {
+            args.addAll(Arrays.asList(moreArgs));
         }
     }
 
-    public String getOption() {
-        return option;
-    }
-
+    @Override
     public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        final List<List<String>> args = getValue();
         if (args != null && !args.isEmpty()) {
             for (List<String> arg : args) {
                 writerContext.writeOptionHeader(getOption());
                 if (!arg.isEmpty()) {
-                    Iterator<String> iter = arg.iterator();
+                    final Iterator<String> iter = arg.iterator();
                     while (true) {
                         writerContext.writeValue(iter.next());
                         if (!iter.hasNext()) {
@@ -77,4 +78,16 @@
             }
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * @return a deep copy of the option
+     */
+    public DoclavaMultilineJavadocOptionFileOption duplicate() {
+        final List<List<String>> value = getValue();
+        final ArrayList<List<String>> valueCopy = new ArrayList<>(value.size());
+        for (List<String> item : value) {
+            valueCopy.add(new ArrayList<>(item));
+        }
+        return new DoclavaMultilineJavadocOptionFileOption(getOption(), valueCopy);
+    }
+}
diff --git a/buildSrc/src/main/groovy/android/support/doclava/DoclavaTask.groovy b/buildSrc/src/main/groovy/android/support/doclava/DoclavaTask.groovy
index 53c0797..b82ccc8 100644
--- a/buildSrc/src/main/groovy/android/support/doclava/DoclavaTask.groovy
+++ b/buildSrc/src/main/groovy/android/support/doclava/DoclavaTask.groovy
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
 package android.support.doclava;
 
 import org.gradle.api.InvalidUserDataException
@@ -8,9 +24,7 @@
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.ParallelizableTask
 
-@ParallelizableTask
 public class DoclavaTask extends Javadoc {
 
     // external/doclava/src/com/google/doclava/Errors.java
@@ -208,6 +222,8 @@
                 options.addStringOption('stubpackages', stubPackages.join(':'))
             }
         }
+        // Always treat this as an Android docs task.
+        options.addOption(new DoclavaJavadocOptionFileOption('android'))
     }
 
     @Override
diff --git a/buildSrc/src/main/java/android/support/jdiff/JDiffTask.java b/buildSrc/src/main/java/android/support/jdiff/JDiffTask.java
new file mode 100644
index 0000000..f89a375
--- /dev/null
+++ b/buildSrc/src/main/java/android/support/jdiff/JDiffTask.java
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+package android.support.jdiff;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.javadoc.Javadoc;
+import org.gradle.external.javadoc.CoreJavadocOptions;
+import org.gradle.external.javadoc.MinimalJavadocOptions;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * JDiff task to compare API changes.
+ */
+public class JDiffTask extends Javadoc {
+
+    private Collection<File> mDocletpath;
+
+    private File mOldApiXmlFile;
+
+    private File mNewApiXmlFile;
+
+    /**
+     * Relative path to the Javadoc corresponding to the old API, relative to
+     * "${destinationDir}/changes". Should end with the directory separator (usually '/').
+     */
+    private String mOldJavadocPrefix;
+
+    /**
+     * Relative path to the Javadoc corresponding to the new API, relative to
+     * "${destinationDir}/changes". Should end with the directory separator (usually '/').
+     */
+    private String mNewJavadocPrefix;
+
+    // HTML diff files will be placed in destinationDir, which is defined by the superclass.
+
+    private boolean mStats = true;
+
+    public JDiffTask() {
+        setFailOnError(true);
+        getOptions().setDoclet("jdiff.JDiff");
+        getOptions().setEncoding("UTF-8");
+        setMaxMemory("1280m");
+    }
+
+    public void setOldApiXmlFile(File file) {
+        mOldApiXmlFile = file;
+    }
+
+    @InputFile
+    public File getOldApiXmlFile() {
+        return mOldApiXmlFile;
+    }
+
+    public void setNewApiXmlFile(File file) {
+        mNewApiXmlFile = file;
+    }
+
+    @InputFile
+    public File getNewApiXmlFile() {
+        return mNewApiXmlFile;
+    }
+
+    @Optional
+    public void setOldJavadocPrefix(String prefix) {
+        mOldJavadocPrefix = prefix;
+    }
+
+    @Optional
+    @Input
+    public String getOldJavadocPrefix() {
+        return mOldJavadocPrefix;
+    }
+
+    public void setNewJavadocPrefix(String prefix) {
+        mNewJavadocPrefix = prefix;
+    }
+
+    @Input
+    public String getNewJavadocPrefix() {
+        return mNewJavadocPrefix;
+    }
+
+    public void setStats(boolean enabled) {
+        mStats = enabled;
+    }
+
+    @Input
+    public boolean getStats() {
+        return mStats;
+    }
+
+    /**
+     * Sets the doclet path which has the {@code com.gogole.doclava.Doclava} class.
+     * <p>
+     * This option will override any doclet path set in this instance's
+     * {@link #getOptions() JavadocOptions}.
+     *
+     * @see MinimalJavadocOptions#setDocletpath(java.util.List)
+     */
+    public void setDocletpath(Collection<File> docletpath) {
+        mDocletpath = docletpath;
+
+        // Go ahead and keep the mDocletpath in our JavadocOptions object in sync.
+        getOptions().setDocletpath(new ArrayList<>(docletpath));
+    }
+
+    @InputFiles
+    public Collection<File> getDocletpath() {
+        return mDocletpath;
+    }
+
+    /**
+     * "Configures" this JDiffTask with parameters that might not be at their final values
+     * until this task is run.
+     */
+    private void configureJDiffTask() {
+        CoreJavadocOptions options = (CoreJavadocOptions) getOptions();
+
+        options.setDocletpath(new ArrayList<>(mDocletpath));
+
+        if (getStats()) {
+            options.addStringOption("stats");
+        }
+
+        File oldApiXmlFile = getOldApiXmlFile();
+        File newApiXmlFile = getNewApiXmlFile();
+
+        File oldApiXmlFileDir = oldApiXmlFile.getParentFile();
+        File newApiXmlFileDir = newApiXmlFile.getParentFile();
+
+        if (oldApiXmlFileDir.exists()) {
+            options.addStringOption("oldapidir", oldApiXmlFileDir.getAbsolutePath());
+        }
+        // For whatever reason, jdiff appends .xml to the file name on its own.
+        // Strip the .xml off the end of the file name
+        options.addStringOption("oldapi",
+                oldApiXmlFile.getName().substring(0, oldApiXmlFile.getName().length() - 4));
+        if (newApiXmlFileDir.exists()) {
+            options.addStringOption("newapidir", newApiXmlFileDir.getAbsolutePath());
+        }
+        options.addStringOption("newapi",
+                newApiXmlFile.getName().substring(0, newApiXmlFile.getName().length() - 4));
+
+        String oldJavadocPrefix = getOldJavadocPrefix();
+        String newJavadocPrefix = getNewJavadocPrefix();
+
+        if (oldJavadocPrefix != null) {
+            options.addStringOption("javadocold", oldJavadocPrefix);
+        }
+        if (newJavadocPrefix != null) {
+            options.addStringOption("javadocnew", newJavadocPrefix);
+        }
+    }
+
+    @Override
+    public void generate() {
+        configureJDiffTask();
+        super.generate();
+    }
+}
diff --git a/buildSrc/versions.gradle b/buildSrc/versions.gradle
new file mode 100644
index 0000000..02a1e20
--- /dev/null
+++ b/buildSrc/versions.gradle
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+// Prevent the Android Gradle plug-in from automatically downloading SDK dependencies.
+ext['android.builder.sdkDownload'] = false
+
+// Version code of the support library components.
+ext.supportVersion = "26.0.0"
+
+// This number gets incremented for each public release.
+ext.extraVersion = 46
diff --git a/compat/Android.mk b/compat/Android.mk
index 97916ae..9eec5f2 100644
--- a/compat/Android.mk
+++ b/compat/Android.mk
@@ -27,25 +27,17 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
 LOCAL_SRC_FILES := \
-    $(call all-java-files-under,gingerbread) \
-    $(call all-java-files-under,honeycomb) \
-    $(call all-java-files-under,honeycomb_mr1) \
-    $(call all-java-files-under,honeycomb_mr2) \
     $(call all-java-files-under,ics) \
-    $(call all-java-files-under,ics-mr1) \
     $(call all-java-files-under,jellybean) \
-    $(call all-java-files-under,jellybean-mr1) \
-    $(call all-java-files-under,jellybean-mr2) \
     $(call all-java-files-under,kitkat) \
     $(call all-java-files-under,api20) \
     $(call all-java-files-under,api21) \
-    $(call all-java-files-under,api22) \
     $(call all-java-files-under,api23) \
     $(call all-java-files-under,api24) \
+    $(call all-java-files-under,api26) \
     $(call all-java-files-under,java) \
     $(call all-Iaidl-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/compat/AndroidManifest-make.xml b/compat/AndroidManifest-make.xml
deleted file mode 100644
index b2bd5bb..0000000
--- a/compat/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.compat">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.compat"/>
-    <application />
-</manifest>
diff --git a/compat/AndroidManifest.xml b/compat/AndroidManifest.xml
index 54d7767..09383ee 100644
--- a/compat/AndroidManifest.xml
+++ b/compat/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.compat">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.compat"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.compat"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/compat/api20/android/support/v4/app/NotificationCompatApi20.java b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
index 57c272e..f646b8f 100644
--- a/compat/api20/android/support/v4/app/NotificationCompatApi20.java
+++ b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
@@ -16,7 +16,13 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
+import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
+import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
+import static android.support.v4.app.NotificationCompat.FLAG_GROUP_SUMMARY;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
+
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -30,7 +36,6 @@
 import java.util.ArrayList;
 
 @RequiresApi(20)
-@TargetApi(20)
 class NotificationCompatApi20 {
     public static class Builder implements NotificationBuilderWithBuilderAccessor,
             NotificationBuilderWithActions {
@@ -38,6 +43,7 @@
         private Bundle mExtras;
         private RemoteViews mContentView;
         private RemoteViews mBigContentView;
+        private int mGroupAlertBehavior;
 
         public Builder(Context context, Notification n,
                 CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
@@ -46,7 +52,8 @@
                 int progressMax, int progress, boolean progressIndeterminate, boolean showWhen,
                 boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
                 ArrayList<String> people, Bundle extras, String groupKey, boolean groupSummary,
-                String sortKey, RemoteViews contentView, RemoteViews bigContentView) {
+                String sortKey, RemoteViews contentView, RemoteViews bigContentView,
+                int groupAlertBehavior) {
             b = new Notification.Builder(context)
                 .setWhen(n.when)
                 .setShowWhen(showWhen)
@@ -87,6 +94,7 @@
             }
             mContentView = contentView;
             mBigContentView = bigContentView;
+            mGroupAlertBehavior = groupAlertBehavior;
         }
 
         @Override
@@ -109,8 +117,31 @@
             if (mBigContentView != null) {
                 notification.bigContentView = mBigContentView;
             }
+
+            if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
+                // if is summary and only children should alert
+                if (notification.getGroup() != null
+                        && (notification.flags & FLAG_GROUP_SUMMARY) != 0
+                        && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
+                    removeSoundAndVibration(notification);
+                }
+                // if is group child and only summary should alert
+                if (notification.getGroup() != null
+                        && (notification.flags & FLAG_GROUP_SUMMARY) == 0
+                        && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
+                    removeSoundAndVibration(notification);
+                }
+            }
+
             return notification;
         }
+
+        private void removeSoundAndVibration(Notification notification) {
+            notification.sound = null;
+            notification.vibrate = null;
+            notification.defaults &= ~DEFAULT_SOUND;
+            notification.defaults &= ~DEFAULT_VIBRATE;
+        }
     }
 
     public static void addAction(Notification.Builder b, NotificationCompatBase.Action action) {
@@ -148,7 +179,7 @@
         boolean allowGeneratedReplies = action.getExtras().getBoolean(
                 NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES);
         return actionFactory.build(action.icon, action.title, action.actionIntent,
-                action.getExtras(), remoteInputs, allowGeneratedReplies);
+                action.getExtras(), remoteInputs, null, allowGeneratedReplies);
     }
 
     private static Notification.Action getActionFromActionCompat(
@@ -211,20 +242,4 @@
         }
         return parcelables;
     }
-
-    public static boolean getLocalOnly(Notification notif) {
-        return (notif.flags & Notification.FLAG_LOCAL_ONLY) != 0;
-    }
-
-    public static String getGroup(Notification notif) {
-        return notif.getGroup();
-    }
-
-    public static boolean isGroupSummary(Notification notif) {
-        return (notif.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
-    }
-
-    public static String getSortKey(Notification notif) {
-        return notif.getSortKey();
-    }
 }
diff --git a/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java b/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
index 2949cfd..19c693c 100644
--- a/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
+++ b/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
@@ -17,14 +17,22 @@
 package android.support.v4.app;
 
 import android.app.RemoteInput;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
+
+import java.util.HashMap;
+import java.util.Map;
 
 @RequiresApi(20)
-@TargetApi(20)
 class RemoteInputCompatApi20 {
+    /** Extra added to a clip data intent object to hold the data results bundle. */
+    private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
+            "android.remoteinput.dataTypeResultsData";
+
     static RemoteInputCompatBase.RemoteInput[] toCompat(RemoteInput[] srcArray,
             RemoteInputCompatBase.RemoteInput.Factory factory) {
         if (srcArray == null) {
@@ -34,7 +42,7 @@
         for (int i = 0; i < srcArray.length; i++) {
             RemoteInput src = srcArray[i];
             result[i] = factory.build(src.getResultKey(), src.getLabel(), src.getChoices(),
-                    src.getAllowFreeFormInput(), src.getExtras());
+                    src.getAllowFreeFormInput(), src.getExtras(), null);
         }
         return result;
     }
@@ -60,8 +68,100 @@
         return RemoteInput.getResultsFromIntent(intent);
     }
 
+    static Map<String, Uri> getDataResultsFromIntent(Intent intent, String remoteInputResultKey) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            return null;
+        }
+        Map<String, Uri> results = new HashMap<>();
+        Bundle extras = clipDataIntent.getExtras();
+        for (String key : extras.keySet()) {
+            if (key.startsWith(EXTRA_DATA_TYPE_RESULTS_DATA)) {
+                String mimeType = key.substring(EXTRA_DATA_TYPE_RESULTS_DATA.length());
+                if (mimeType == null || mimeType.isEmpty()) {
+                    continue;
+                }
+                Bundle bundle = clipDataIntent.getBundleExtra(key);
+                String uriStr = bundle.getString(remoteInputResultKey);
+                if (uriStr == null || uriStr.isEmpty()) {
+                    continue;
+                }
+                results.put(mimeType, Uri.parse(uriStr));
+            }
+        }
+        return results.isEmpty() ? null : results;
+    }
+
     static void addResultsToIntent(RemoteInputCompatBase.RemoteInput[] remoteInputs,
             Intent intent, Bundle results) {
-        RemoteInput.addResultsToIntent(fromCompat(remoteInputs), intent, results);
+        // Implementations of RemoteInput#addResultsToIntent prior to SDK 26 don't actually add
+        // results, they wipe out old results and insert the new one. Work around that by preserving
+        // old results.
+        Bundle existingTextResults = getResultsFromIntent(intent);
+        if (existingTextResults == null) {
+            existingTextResults = results;
+        } else {
+            existingTextResults.putAll(results);
+        }
+        for (RemoteInputCompatBase.RemoteInput input : remoteInputs) {
+            // Data results are also wiped out. So grab them and add them back in.
+            Map<String, Uri> existingDataResults =
+                    getDataResultsFromIntent(intent, input.getResultKey());
+            RemoteInputCompatBase.RemoteInput[] arr = new RemoteInputCompatBase.RemoteInput[1];
+            arr[0] = input;
+            RemoteInput.addResultsToIntent(fromCompat(arr), intent, existingTextResults);
+            if (existingDataResults != null) {
+                addDataResultToIntent(input, intent, existingDataResults);
+            }
+        }
+    }
+
+    /**
+     * Same as {@link #addResultsToIntent} but for setting data results.
+     * @param remoteInput The remote input for which results are being provided
+     * @param intent The intent to add remote input results to. The {@link ClipData}
+     *               field of the intent will be modified to contain the results.
+     * @param results A map of mime type to the Uri result for that mime type.
+     */
+    public static void addDataResultToIntent(RemoteInputCompatBase.RemoteInput remoteInput,
+            Intent intent, Map<String, Uri> results) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        for (Map.Entry<String, Uri> entry : results.entrySet()) {
+            String mimeType = entry.getKey();
+            Uri uri = entry.getValue();
+            if (mimeType == null) {
+                continue;
+            }
+            Bundle resultsBundle =
+                    clipDataIntent.getBundleExtra(getExtraResultsKeyForData(mimeType));
+            if (resultsBundle == null) {
+                resultsBundle = new Bundle();
+            }
+            resultsBundle.putString(remoteInput.getResultKey(), uri.toString());
+            clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle);
+        }
+        intent.setClipData(ClipData.newIntent(RemoteInput.RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    private static String getExtraResultsKeyForData(String mimeType) {
+        return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
+    }
+
+    private static Intent getClipDataIntentFromIntent(Intent intent) {
+        ClipData clipData = intent.getClipData();
+        if (clipData == null) {
+            return null;
+        }
+        ClipDescription clipDescription = clipData.getDescription();
+        if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+            return null;
+        }
+        if (!clipDescription.getLabel().equals(RemoteInput.RESULTS_CLIP_LABEL)) {
+            return null;
+        }
+        return clipData.getItemAt(0).getIntent();
     }
 }
diff --git a/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java b/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
deleted file mode 100644
index 617920c..0000000
--- a/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.WindowInsets;
-
-@RequiresApi(20)
-@TargetApi(20)
-class WindowInsetsCompatApi20 {
-    public static Object consumeSystemWindowInsets(Object insets) {
-        return ((WindowInsets) insets).consumeSystemWindowInsets();
-    }
-
-    public static int getSystemWindowInsetBottom(Object insets) {
-        return ((WindowInsets) insets).getSystemWindowInsetBottom();
-    }
-
-    public static int getSystemWindowInsetLeft(Object insets) {
-        return ((WindowInsets) insets).getSystemWindowInsetLeft();
-    }
-
-    public static int getSystemWindowInsetRight(Object insets) {
-        return ((WindowInsets) insets).getSystemWindowInsetRight();
-    }
-
-    public static int getSystemWindowInsetTop(Object insets) {
-        return ((WindowInsets) insets).getSystemWindowInsetTop();
-    }
-
-    public static boolean hasInsets(Object insets) {
-        return ((WindowInsets) insets).hasInsets();
-    }
-
-    public static boolean hasSystemWindowInsets(Object insets) {
-        return ((WindowInsets) insets).hasSystemWindowInsets();
-    }
-
-    public static boolean isRound(Object insets) {
-        return ((WindowInsets) insets).isRound();
-    }
-
-    public static Object replaceSystemWindowInsets(Object insets, int left, int top, int right,
-            int bottom) {
-        return ((WindowInsets) insets).replaceSystemWindowInsets(left, top, right, bottom);
-    }
-
-    public static Object getSourceWindowInsets(Object src) {
-        return new WindowInsets((WindowInsets) src);
-    }
-}
diff --git a/compat/api21/android/support/v4/app/ActivityCompatApi21.java b/compat/api21/android/support/v4/app/ActivityCompatApi21.java
deleted file mode 100644
index ddc95fd..0000000
--- a/compat/api21/android/support/v4/app/ActivityCompatApi21.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.SharedElementCallback;
-import android.content.Context;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-import java.util.List;
-import java.util.Map;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ActivityCompatApi21 {
-
-    public static void finishAfterTransition(Activity activity) {
-        activity.finishAfterTransition();
-    }
-
-    public static void setEnterSharedElementCallback(Activity activity,
-            SharedElementCallback21 callback) {
-        activity.setEnterSharedElementCallback(createCallback(callback));
-    }
-
-    public static void setExitSharedElementCallback(Activity activity,
-            SharedElementCallback21 callback) {
-        activity.setExitSharedElementCallback(createCallback(callback));
-    }
-
-    public static void postponeEnterTransition(Activity activity) {
-        activity.postponeEnterTransition();
-    }
-
-    public static void startPostponedEnterTransition(Activity activity) {
-        activity.startPostponedEnterTransition();
-    }
-
-    public abstract static class SharedElementCallback21 {
-        public abstract void onSharedElementStart(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots);
-
-        public abstract void onSharedElementEnd(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots);
-
-        public abstract void onRejectSharedElements(List<View> rejectedSharedElements);
-
-        public abstract void onMapSharedElements(List<String> names,
-                Map<String, View> sharedElements);
-        public abstract Parcelable onCaptureSharedElementSnapshot(View sharedElement,
-                Matrix viewToGlobalMatrix, RectF screenBounds);
-        public abstract View onCreateSnapshotView(Context context, Parcelable snapshot);
-    }
-
-    private static SharedElementCallback createCallback(SharedElementCallback21 callback) {
-        SharedElementCallback newListener = null;
-        if (callback != null) {
-            newListener = new SharedElementCallbackImpl(callback);
-        }
-        return newListener;
-    }
-
-    private static class SharedElementCallbackImpl extends SharedElementCallback {
-        private SharedElementCallback21 mCallback;
-
-        public SharedElementCallbackImpl(SharedElementCallback21 callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onSharedElementStart(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots) {
-            mCallback.onSharedElementStart(sharedElementNames, sharedElements,
-                    sharedElementSnapshots);
-        }
-
-        @Override
-        public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements,
-                List<View> sharedElementSnapshots) {
-            mCallback.onSharedElementEnd(sharedElementNames, sharedElements,
-                    sharedElementSnapshots);
-        }
-
-        @Override
-        public void onRejectSharedElements(List<View> rejectedSharedElements) {
-            mCallback.onRejectSharedElements(rejectedSharedElements);
-        }
-
-        @Override
-        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
-            mCallback.onMapSharedElements(names, sharedElements);
-        }
-
-        @Override
-        public Parcelable onCaptureSharedElementSnapshot(View sharedElement,
-                Matrix viewToGlobalMatrix,
-                RectF screenBounds) {
-            return mCallback.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix,
-                            screenBounds);
-        }
-
-        @Override
-        public View onCreateSnapshotView(Context context, Parcelable snapshot) {
-            return mCallback.onCreateSnapshotView(context, snapshot);
-        }
-    }
-}
diff --git a/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java b/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
deleted file mode 100644
index 16287d2..0000000
--- a/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.util.Pair;
-import android.view.View;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ActivityOptionsCompat21 {
-
-    private final ActivityOptions mActivityOptions;
-
-    public static ActivityOptionsCompat21 makeCustomAnimation(Context context,
-            int enterResId, int exitResId) {
-        return new ActivityOptionsCompat21(
-            ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
-    }
-
-    public static ActivityOptionsCompat21 makeScaleUpAnimation(View source,
-            int startX, int startY, int startWidth, int startHeight) {
-        return new ActivityOptionsCompat21(
-            ActivityOptions.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight));
-    }
-
-    public static ActivityOptionsCompat21 makeThumbnailScaleUpAnimation(View source,
-            Bitmap thumbnail, int startX, int startY) {
-        return new ActivityOptionsCompat21(
-            ActivityOptions.makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY));
-    }
-
-    public static ActivityOptionsCompat21 makeSceneTransitionAnimation(Activity activity,
-            View sharedElement, String sharedElementName) {
-        return new ActivityOptionsCompat21(
-                ActivityOptions.makeSceneTransitionAnimation(activity, sharedElement,
-                        sharedElementName));
-    }
-
-    public static ActivityOptionsCompat21 makeSceneTransitionAnimation(Activity activity,
-            View[] sharedElements, String[] sharedElementNames) {
-        Pair[] pairs = null;
-        if (sharedElements != null) {
-            pairs = new Pair[sharedElements.length];
-            for (int i = 0; i < pairs.length; i++) {
-                pairs[i] = Pair.create(sharedElements[i], sharedElementNames[i]);
-            }
-        }
-        return new ActivityOptionsCompat21(
-                ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
-    }
-
-    public static ActivityOptionsCompat21 makeTaskLaunchBehind() {
-        return new ActivityOptionsCompat21(
-                ActivityOptions.makeTaskLaunchBehind());
-    }
-
-    private ActivityOptionsCompat21(ActivityOptions activityOptions) {
-        mActivityOptions = activityOptions;
-    }
-
-    public Bundle toBundle() {
-        return mActivityOptions.toBundle();
-    }
-
-    public void update(ActivityOptionsCompat21 otherOptions) {
-        mActivityOptions.update(otherOptions.mActivityOptions);
-    }
-}
diff --git a/compat/api21/android/support/v4/app/NotificationCompatApi21.java b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
index feeb044..d56d87e 100644
--- a/compat/api21/android/support/v4/app/NotificationCompatApi21.java
+++ b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
@@ -16,6 +16,13 @@
 
 package android.support.v4.app;
 
+import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
+import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
+import static android.support.v4.app.NotificationCompat.FLAG_GROUP_SUMMARY;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
+
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -23,30 +30,12 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 
 @RequiresApi(21)
-@TargetApi(21)
 class NotificationCompatApi21 {
-
-    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
-    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
-    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
-    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
-    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
-    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
-    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
-    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
-    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
-    public static final String CATEGORY_TRANSPORT = Notification.CATEGORY_TRANSPORT;
-    public static final String CATEGORY_SYSTEM = Notification.CATEGORY_SYSTEM;
-    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
-    public static final String CATEGORY_RECOMMENDATION = Notification.CATEGORY_RECOMMENDATION;
-    public static final String CATEGORY_STATUS = Notification.CATEGORY_STATUS;
-
     private static final String KEY_AUTHOR = "author";
     private static final String KEY_TEXT = "text";
     private static final String KEY_MESSAGES = "messages";
@@ -63,6 +52,7 @@
         private RemoteViews mContentView;
         private RemoteViews mBigContentView;
         private RemoteViews mHeadsUpContentView;
+        private int mGroupAlertBehavior;
 
         public Builder(Context context, Notification n,
                 CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
@@ -73,7 +63,7 @@
                 String category, ArrayList<String> people, Bundle extras, int color,
                 int visibility, Notification publicVersion, String groupKey, boolean groupSummary,
                 String sortKey, RemoteViews contentView, RemoteViews bigContentView,
-                RemoteViews headsUpContentView) {
+                RemoteViews headsUpContentView, int groupAlertBehavior) {
             b = new Notification.Builder(context)
                     .setWhen(n.when)
                     .setShowWhen(showWhen)
@@ -118,6 +108,7 @@
             mContentView = contentView;
             mBigContentView = bigContentView;
             mHeadsUpContentView = headsUpContentView;
+            mGroupAlertBehavior = groupAlertBehavior;
         }
 
         @Override
@@ -143,12 +134,30 @@
             if (mHeadsUpContentView != null) {
                 notification.headsUpContentView = mHeadsUpContentView;
             }
+
+            if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
+                // if is summary and only children should alert
+                if (notification.getGroup() != null
+                        && (notification.flags & FLAG_GROUP_SUMMARY) != 0
+                        && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
+                    removeSoundAndVibration(notification);
+                }
+                // if is group child and only summary should alert
+                if (notification.getGroup() != null
+                        && (notification.flags & FLAG_GROUP_SUMMARY) == 0
+                        && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
+                    removeSoundAndVibration(notification);
+                }
+            }
             return notification;
         }
-    }
 
-    public static String getCategory(Notification notif) {
-        return notif.category;
+        private void removeSoundAndVibration(Notification notification) {
+            notification.sound = null;
+            notification.vibrate = null;
+            notification.defaults &= ~DEFAULT_SOUND;
+            notification.defaults &= ~DEFAULT_VIBRATE;
+        }
     }
 
     static Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
@@ -244,6 +253,7 @@
                 remoteInput.getLabel(),
                 remoteInput.getChoices(),
                 remoteInput.getAllowFreeFormInput(),
-                remoteInput.getExtras());
+                remoteInput.getExtras(),
+                null /* allowedDataTypes */);
     }
 }
diff --git a/compat/api21/android/support/v4/content/ContextCompatApi21.java b/compat/api21/android/support/v4/content/ContextCompatApi21.java
deleted file mode 100644
index 97a0b37..0000000
--- a/compat/api21/android/support/v4/content/ContextCompatApi21.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.io.File;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ContextCompatApi21 {
-    public static Drawable getDrawable(Context context, int id) {
-        return context.getDrawable(id);
-    }
-
-    public static File getNoBackupFilesDir(Context context) {
-        return context.getNoBackupFilesDir();
-    }
-
-    public static File getCodeCacheDir(Context context) {
-        return context.getCodeCacheDir();
-    }
-}
diff --git a/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java b/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
deleted file mode 100644
index f08dbe1..0000000
--- a/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content.res;
-
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ResourcesCompatApi21 {
-    public static Drawable getDrawable(Resources res, int id, Theme theme)
-            throws NotFoundException {
-        return res.getDrawable(id, theme);
-    }
-
-    public static Drawable getDrawableForDensity(Resources res, int id, int density, Theme theme)
-            throws NotFoundException {
-        return res.getDrawableForDensity(id, density, theme);
-    }
-}
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java b/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
deleted file mode 100644
index a5e8650..0000000
--- a/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
-import android.graphics.drawable.InsetDrawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.AttributeSet;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * Implementation of drawable compatibility that can call L APIs.
- */
-
-@RequiresApi(21)
-@TargetApi(21)
-class DrawableCompatLollipop {
-
-    public static void setHotspot(Drawable drawable, float x, float y) {
-        drawable.setHotspot(x, y);
-    }
-
-    public static void setHotspotBounds(Drawable drawable, int left, int top,
-            int right, int bottom) {
-        drawable.setHotspotBounds( left, top, right, bottom);
-    }
-
-    public static void setTint(Drawable drawable, int tint) {
-        drawable.setTint(tint);
-    }
-
-    public static void setTintList(Drawable drawable, ColorStateList tint) {
-        drawable.setTintList(tint);
-    }
-
-    public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
-        drawable.setTintMode(tintMode);
-    }
-
-    public static Drawable wrapForTinting(final Drawable drawable) {
-        if (!(drawable instanceof TintAwareDrawable)) {
-            return new DrawableWrapperLollipop(drawable);
-        }
-        return drawable;
-    }
-
-    public static void applyTheme(Drawable drawable, Resources.Theme t) {
-        drawable.applyTheme(t);
-    }
-
-    public static boolean canApplyTheme(Drawable drawable) {
-        return drawable.canApplyTheme();
-    }
-
-    public static ColorFilter getColorFilter(Drawable drawable) {
-        return drawable.getColorFilter();
-    }
-
-    public static void clearColorFilter(Drawable drawable) {
-        drawable.clearColorFilter();
-
-        // API 21 + 22 have an issue where clearing a color filter on a DrawableContainer
-        // will not propagate to all of its children. To workaround this we unwrap the drawable
-        // to find any DrawableContainers, and then unwrap those to clear the filter on its
-        // children manually
-        if (drawable instanceof InsetDrawable) {
-            clearColorFilter(((InsetDrawable) drawable).getDrawable());
-        } else if (drawable instanceof DrawableWrapper) {
-            clearColorFilter(((DrawableWrapper) drawable).getWrappedDrawable());
-        } else if (drawable instanceof DrawableContainer) {
-            final DrawableContainer container = (DrawableContainer) drawable;
-            final DrawableContainer.DrawableContainerState state =
-                    (DrawableContainer.DrawableContainerState) container.getConstantState();
-            if (state != null) {
-                Drawable child;
-                for (int i = 0, count = state.getChildCount(); i < count; i++) {
-                    child = state.getChild(i);
-                    if (child != null) {
-                        clearColorFilter(child);
-                    }
-                }
-            }
-        }
-    }
-
-    public static void inflate(Drawable drawable, Resources res, XmlPullParser parser,
-                               AttributeSet attrs, Resources.Theme t)
-            throws IOException, XmlPullParserException {
-        drawable.inflate(res, parser, attrs, t);
-    }
-}
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperApi21.java b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperApi21.java
new file mode 100644
index 0000000..5195cc9
--- /dev/null
+++ b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperApi21.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics.drawable;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+@RequiresApi(21)
+class DrawableWrapperApi21 extends DrawableWrapperApi19 {
+    private static final String TAG = "DrawableWrapperApi21";
+    private static Method sIsProjectedDrawableMethod;
+
+    DrawableWrapperApi21(Drawable drawable) {
+        super(drawable);
+        findAndCacheIsProjectedDrawableMethod();
+    }
+
+    DrawableWrapperApi21(DrawableWrapperState state, Resources resources) {
+        super(state, resources);
+        findAndCacheIsProjectedDrawableMethod();
+    }
+
+    @Override
+    public void setHotspot(float x, float y) {
+        mDrawable.setHotspot(x, y);
+    }
+
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        mDrawable.setHotspotBounds(left, top, right, bottom);
+    }
+
+    @Override
+    public void getOutline(Outline outline) {
+        mDrawable.getOutline(outline);
+    }
+
+    @Override
+    public Rect getDirtyBounds() {
+        return mDrawable.getDirtyBounds();
+    }
+
+    @Override
+    public void setTintList(ColorStateList tint) {
+        if (isCompatTintEnabled()) {
+            super.setTintList(tint);
+        } else {
+            mDrawable.setTintList(tint);
+        }
+    }
+
+    @Override
+    public void setTint(int tintColor) {
+        if (isCompatTintEnabled()) {
+            super.setTint(tintColor);
+        } else {
+            mDrawable.setTint(tintColor);
+        }
+    }
+
+    @Override
+    public void setTintMode(PorterDuff.Mode tintMode) {
+        if (isCompatTintEnabled()) {
+            super.setTintMode(tintMode);
+        } else {
+            mDrawable.setTintMode(tintMode);
+        }
+    }
+
+    @Override
+    public boolean setState(int[] stateSet) {
+        if (super.setState(stateSet)) {
+            // Manually invalidate because the framework doesn't currently force an invalidation
+            // on a state change
+            invalidateSelf();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean isCompatTintEnabled() {
+        if (Build.VERSION.SDK_INT == 21) {
+            final Drawable drawable = mDrawable;
+            return drawable instanceof GradientDrawable
+                    || drawable instanceof DrawableContainer
+                    || drawable instanceof InsetDrawable
+                    || drawable instanceof RippleDrawable;
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public boolean isProjected() {
+        if (mDrawable != null && sIsProjectedDrawableMethod != null) {
+            try {
+                return (Boolean) sIsProjectedDrawableMethod.invoke(mDrawable);
+            } catch (Exception ex) {
+                Log.w(TAG, "Error calling Drawable#isProjected() method", ex);
+            }
+        }
+
+        return false;
+    }
+
+    @NonNull
+    @Override
+    DrawableWrapperState mutateConstantState() {
+        return new DrawableWrapperStateLollipop(mState, null);
+    }
+
+    private static class DrawableWrapperStateLollipop extends DrawableWrapperState {
+        DrawableWrapperStateLollipop(@Nullable DrawableWrapperState orig,
+                @Nullable Resources res) {
+            super(orig, res);
+        }
+
+        @Override
+        public Drawable newDrawable(@Nullable Resources res) {
+            return new DrawableWrapperApi21(this, res);
+        }
+    }
+
+    private void findAndCacheIsProjectedDrawableMethod() {
+        if (sIsProjectedDrawableMethod == null) {
+            try {
+                sIsProjectedDrawableMethod = Drawable.class.getDeclaredMethod("isProjected");
+            } catch (Exception ex) {
+                Log.w(TAG, "Failed to retrieve Drawable#isProjected() method", ex);
+            }
+        }
+    }
+}
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
deleted file mode 100644
index 9458f7b..0000000
--- a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(21)
-@TargetApi(21)
-class DrawableWrapperLollipop extends DrawableWrapperKitKat {
-
-    DrawableWrapperLollipop(Drawable drawable) {
-        super(drawable);
-    }
-
-    DrawableWrapperLollipop(DrawableWrapperState state, Resources resources) {
-        super(state, resources);
-    }
-
-    @Override
-    public void setHotspot(float x, float y) {
-        mDrawable.setHotspot(x, y);
-    }
-
-    @Override
-    public void setHotspotBounds(int left, int top, int right, int bottom) {
-        mDrawable.setHotspotBounds(left, top, right, bottom);
-    }
-
-    @Override
-    public void getOutline(Outline outline) {
-        mDrawable.getOutline(outline);
-    }
-
-    @Override
-    public Rect getDirtyBounds() {
-        return mDrawable.getDirtyBounds();
-    }
-
-    @Override
-    public void setTintList(ColorStateList tint) {
-        if (isCompatTintEnabled()) {
-            super.setTintList(tint);
-        } else {
-            mDrawable.setTintList(tint);
-        }
-    }
-
-    @Override
-    public void setTint(int tintColor) {
-        if (isCompatTintEnabled()) {
-            super.setTint(tintColor);
-        } else {
-            mDrawable.setTint(tintColor);
-        }
-    }
-
-    @Override
-    public void setTintMode(PorterDuff.Mode tintMode) {
-        if (isCompatTintEnabled()) {
-            super.setTintMode(tintMode);
-        } else {
-            mDrawable.setTintMode(tintMode);
-        }
-    }
-
-    @Override
-    public boolean setState(int[] stateSet) {
-        if (super.setState(stateSet)) {
-            // Manually invalidate because the framework doesn't currently force an invalidation
-            // on a state change
-            invalidateSelf();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    protected boolean isCompatTintEnabled() {
-        if (Build.VERSION.SDK_INT == 21) {
-            final Drawable drawable = mDrawable;
-            return drawable instanceof GradientDrawable || drawable instanceof DrawableContainer
-                    || drawable instanceof InsetDrawable;
-        }
-        return false;
-    }
-
-    @NonNull
-    @Override
-    DrawableWrapperState mutateConstantState() {
-        return new DrawableWrapperStateLollipop(mState, null);
-    }
-
-    private static class DrawableWrapperStateLollipop extends DrawableWrapperState {
-        DrawableWrapperStateLollipop(@Nullable DrawableWrapperState orig,
-                @Nullable Resources res) {
-            super(orig, res);
-        }
-
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new DrawableWrapperLollipop(this, res);
-        }
-    }
-}
diff --git a/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java b/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
deleted file mode 100644
index 7fae8a8..0000000
--- a/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.LayoutInflater;
-
-@RequiresApi(21)
-@TargetApi(21)
-class LayoutInflaterCompatLollipop {
-    static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
-        inflater.setFactory2(factory != null
-                ? new LayoutInflaterCompatHC.FactoryWrapperHC(factory) : null);
-    }
-}
diff --git a/compat/api21/android/support/v4/view/ViewCompatLollipop.java b/compat/api21/android/support/v4/view/ViewCompatLollipop.java
deleted file mode 100644
index 1f226fc..0000000
--- a/compat/api21/android/support/v4/view/ViewCompatLollipop.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.annotation.TargetApi;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.WindowInsets;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ViewCompatLollipop {
-
-    public interface OnApplyWindowInsetsListenerBridge {
-        Object onApplyWindowInsets(View v, Object insets);
-    }
-
-    private static ThreadLocal<Rect> sThreadLocalRect;
-
-    public static void setTransitionName(View view, String transitionName) {
-        view.setTransitionName(transitionName);
-    }
-
-    public static String getTransitionName(View view) {
-        return view.getTransitionName();
-    }
-
-    public static void requestApplyInsets(View view) {
-        view.requestApplyInsets();
-    }
-
-    public static void setElevation(View view, float elevation) {
-        view.setElevation(elevation);
-    }
-
-    public static float getElevation(View view) {
-        return view.getElevation();
-    }
-
-    public static void setTranslationZ(View view, float translationZ) {
-        view.setTranslationZ(translationZ);
-    }
-
-    public static float getTranslationZ(View view) {
-        return view.getTranslationZ();
-    }
-
-    public static void setOnApplyWindowInsetsListener(
-            View view, final OnApplyWindowInsetsListenerBridge bridge) {
-        if (bridge == null) {
-            view.setOnApplyWindowInsetsListener(null);
-        } else {
-            view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
-                @Override
-                public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
-                    return (WindowInsets) bridge.onApplyWindowInsets(view, insets);
-                }
-            });
-        }
-    }
-
-    public static boolean isImportantForAccessibility(View view) {
-        return view.isImportantForAccessibility();
-    }
-
-    static ColorStateList getBackgroundTintList(View view) {
-        return view.getBackgroundTintList();
-    }
-
-    static void setBackgroundTintList(View view, ColorStateList tintList) {
-        view.setBackgroundTintList(tintList);
-
-        if (Build.VERSION.SDK_INT == 21) {
-            // Work around a bug in L that did not update the state of the background
-            // after applying the tint
-            Drawable background = view.getBackground();
-            boolean hasTint = (view.getBackgroundTintList() != null)
-                    && (view.getBackgroundTintMode() != null);
-            if ((background != null) && hasTint) {
-                if (background.isStateful()) {
-                    background.setState(view.getDrawableState());
-                }
-                view.setBackground(background);
-            }
-        }
-    }
-
-    static PorterDuff.Mode getBackgroundTintMode(View view) {
-        return view.getBackgroundTintMode();
-    }
-
-    static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
-        view.setBackgroundTintMode(mode);
-
-        if (Build.VERSION.SDK_INT == 21) {
-            // Work around a bug in L that did not update the state of the background
-            // after applying the tint
-            Drawable background = view.getBackground();
-            boolean hasTint = (view.getBackgroundTintList() != null)
-                    && (view.getBackgroundTintMode() != null);
-            if ((background != null) && hasTint) {
-                if (background.isStateful()) {
-                    background.setState(view.getDrawableState());
-                }
-                view.setBackground(background);
-            }
-        }
-    }
-
-    public static Object onApplyWindowInsets(View v, Object insets) {
-        WindowInsets unwrapped = (WindowInsets) insets;
-        WindowInsets result = v.onApplyWindowInsets(unwrapped);
-        if (result != unwrapped) {
-            insets = new WindowInsets(result);
-        }
-        return insets;
-    }
-
-    public static Object dispatchApplyWindowInsets(View v, Object insets) {
-        WindowInsets unwrapped = (WindowInsets) insets;
-        WindowInsets result = v.dispatchApplyWindowInsets(unwrapped);
-        if (result != unwrapped) {
-            insets = new WindowInsets(result);
-        }
-        return insets;
-    }
-
-    public static void setNestedScrollingEnabled(View view, boolean enabled) {
-        view.setNestedScrollingEnabled(enabled);
-    }
-
-    public static boolean isNestedScrollingEnabled(View view) {
-        return view.isNestedScrollingEnabled();
-    }
-
-    public static boolean startNestedScroll(View view, int axes) {
-        return view.startNestedScroll(axes);
-    }
-
-    public static void stopNestedScroll(View view) {
-        view.stopNestedScroll();
-    }
-
-    public static boolean hasNestedScrollingParent(View view) {
-        return view.hasNestedScrollingParent();
-    }
-
-    public static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
-            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
-        return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
-                offsetInWindow);
-    }
-
-    public static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
-            int[] offsetInWindow) {
-        return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
-    }
-
-    public static boolean dispatchNestedFling(View view, float velocityX, float velocityY,
-            boolean consumed) {
-        return view.dispatchNestedFling(velocityX, velocityY, consumed);
-    }
-
-    public static boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
-        return view.dispatchNestedPreFling(velocityX, velocityY);
-    }
-
-    public static float getZ(View view) {
-        return view.getZ();
-    }
-
-    public static void setZ(View view, float z) {
-        view.setZ(z);
-    }
-
-    static void offsetTopAndBottom(final View view, final int offset) {
-        final Rect parentRect = getEmptyTempRect();
-        boolean needInvalidateWorkaround = false;
-
-        final ViewParent parent = view.getParent();
-        if (parent instanceof View) {
-            final View p = (View) parent;
-            parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
-            // If the view currently does not currently intersect the parent (and is therefore
-            // not displayed) we may need need to invalidate
-            needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
-                    view.getRight(), view.getBottom());
-        }
-
-        // Now offset, invoking the API 11+ implementation (which contains its own workarounds)
-        ViewCompatHC.offsetTopAndBottom(view, offset);
-
-        // The view has now been offset, so let's intersect the Rect and invalidate where
-        // the View is now displayed
-        if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
-                view.getRight(), view.getBottom())) {
-            ((View) parent).invalidate(parentRect);
-        }
-    }
-
-    static void offsetLeftAndRight(final View view, final int offset) {
-        final Rect parentRect = getEmptyTempRect();
-        boolean needInvalidateWorkaround = false;
-
-        final ViewParent parent = view.getParent();
-        if (parent instanceof View) {
-            final View p = (View) parent;
-            parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
-            // If the view currently does not currently intersect the parent (and is therefore
-            // not displayed) we may need need to invalidate
-            needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
-                    view.getRight(), view.getBottom());
-        }
-
-        // Now offset, invoking the API 11+ implementation (which contains its own workarounds)
-        ViewCompatHC.offsetLeftAndRight(view, offset);
-
-        // The view has now been offset, so let's intersect the Rect and invalidate where
-        // the View is now displayed
-        if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
-                view.getRight(), view.getBottom())) {
-            ((View) parent).invalidate(parentRect);
-        }
-    }
-
-    private static Rect getEmptyTempRect() {
-        if (sThreadLocalRect == null) {
-            sThreadLocalRect = new ThreadLocal<>();
-        }
-        Rect rect = sThreadLocalRect.get();
-        if (rect == null) {
-            rect = new Rect();
-            sThreadLocalRect.set(rect);
-        }
-        rect.setEmpty();
-        return rect;
-    }
-}
diff --git a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java b/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
deleted file mode 100644
index 03430e6..0000000
--- a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ViewGroup;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ViewGroupCompatLollipop {
-
-    public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-        group.setTransitionGroup(isTransitionGroup);
-    }
-
-    public static boolean isTransitionGroup(ViewGroup group) {
-        return group.isTransitionGroup();
-    }
-
-    public static int getNestedScrollAxes(ViewGroup group) {
-        return group.getNestedScrollAxes();
-    }
-}
diff --git a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java b/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
deleted file mode 100644
index 1e65a09..0000000
--- a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewParent;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ViewParentCompatLollipop {
-    private static final String TAG = "ViewParentCompat";
-
-    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
-            int nestedScrollAxes) {
-        try {
-            return parent.onStartNestedScroll(child, target, nestedScrollAxes);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onStartNestedScroll", e);
-            return false;
-        }
-    }
-
-    public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
-            int nestedScrollAxes) {
-        try {
-            parent.onNestedScrollAccepted(child, target, nestedScrollAxes);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedScrollAccepted", e);
-        }
-    }
-
-    public static void onStopNestedScroll(ViewParent parent, View target) {
-        try {
-            parent.onStopNestedScroll(target);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onStopNestedScroll", e);
-        }
-    }
-
-    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
-            int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
-        try {
-            parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedScroll", e);
-        }
-    }
-
-    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
-            int[] consumed) {
-        try {
-            parent.onNestedPreScroll(target, dx, dy, consumed);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedPreScroll", e);
-        }
-    }
-
-    public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
-            float velocityY, boolean consumed) {
-        try {
-            return parent.onNestedFling(target, velocityX, velocityY, consumed);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedFling", e);
-            return false;
-        }
-    }
-
-    public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
-            float velocityY) {
-        try {
-            return parent.onNestedPreFling(target, velocityX, velocityY);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedPreFling", e);
-            return false;
-        }
-    }
-}
diff --git a/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java b/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
deleted file mode 100644
index 2b979a9..0000000
--- a/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-@RequiresApi(21)
-@TargetApi(21)
-class ViewPropertyAnimatorCompatLollipop {
-
-    public static void translationZ(View view, float value) {
-        view.animate().translationZ(value);
-    }
-
-    public static void translationZBy(View view, float value) {
-        view.animate().translationZBy(value);
-    }
-
-    public static void z(View view, float value) {
-        view.animate().z(value);
-    }
-
-    public static void zBy(View view, float value) {
-        view.animate().zBy(value);
-    }
-
-}
diff --git a/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java b/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
deleted file mode 100644
index 5bbb802..0000000
--- a/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.WindowInsets;
-
-@RequiresApi(21)
-@TargetApi(21)
-class WindowInsetsCompatApi21 {
-    public static Object consumeStableInsets(Object insets) {
-        return ((WindowInsets) insets).consumeStableInsets();
-    }
-
-    public static int getStableInsetBottom(Object insets) {
-        return ((WindowInsets) insets).getStableInsetBottom();
-    }
-
-    public static int getStableInsetLeft(Object insets) {
-        return ((WindowInsets) insets).getStableInsetLeft();
-    }
-
-    public static int getStableInsetRight(Object insets) {
-        return ((WindowInsets) insets).getStableInsetRight();
-    }
-
-    public static int getStableInsetTop(Object insets) {
-        return ((WindowInsets) insets).getStableInsetTop();
-    }
-
-    public static boolean hasStableInsets(Object insets) {
-        return ((WindowInsets) insets).hasStableInsets();
-    }
-
-    public static boolean isConsumed(Object insets) {
-        return ((WindowInsets) insets).isConsumed();
-    }
-
-    public static Object replaceSystemWindowInsets(Object insets, Rect systemWindowInsets) {
-        return ((WindowInsets) insets).replaceSystemWindowInsets(systemWindowInsets);
-    }
-}
diff --git a/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java b/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
deleted file mode 100644
index e24b873..0000000
--- a/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import java.util.List;
-
-/**
- * Api21-specific AccessibilityNodeInfo API implementation.
- */
-
-@RequiresApi(21)
-@TargetApi(21)
-class AccessibilityNodeInfoCompatApi21 {
-    static List<Object> getActionList(Object info) {
-        Object result = ((AccessibilityNodeInfo) info).getActionList();
-        return (List<Object>) result;
-    }
-
-    static void addAction(Object info, Object action) {
-        ((AccessibilityNodeInfo) info).addAction((AccessibilityAction) action);
-    }
-
-    public static boolean removeAction(Object info, Object action) {
-        return ((AccessibilityNodeInfo) info).removeAction((AccessibilityAction) action);
-    }
-
-    public static Object obtainCollectionInfo(int rowCount, int columnCount,
-            boolean hierarchical, int selectionMode) {
-        return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical,
-                selectionMode);
-    }
-
-    public static Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
-            int columnSpan, boolean heading, boolean selected) {
-        return AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
-                columnSpan, heading, selected);
-    }
-
-    public static CharSequence getError(Object info) {
-        return ((AccessibilityNodeInfo) info).getError();
-    }
-
-    public static void setError(Object info, CharSequence error) {
-        ((AccessibilityNodeInfo) info).setError(error);
-    }
-
-    public static void setMaxTextLength(Object info, int max) {
-        ((AccessibilityNodeInfo) info).setMaxTextLength(max);
-    }
-
-    public static int getMaxTextLength(Object info) {
-        return ((AccessibilityNodeInfo) info).getMaxTextLength();
-    }
-
-    public static Object getWindow(Object info) {
-        return ((AccessibilityNodeInfo) info).getWindow();
-    }
-
-    public static boolean removeChild(Object info, View child) {
-        return ((AccessibilityNodeInfo) info).removeChild(child);
-    }
-
-    public static boolean removeChild(Object info, View root, int virtualDescendantId) {
-        return ((AccessibilityNodeInfo) info).removeChild(root, virtualDescendantId);
-    }
-
-    static class CollectionInfo {
-        public static int getSelectionMode(Object info) {
-            return ((AccessibilityNodeInfo.CollectionInfo) info).getSelectionMode();
-        }
-    }
-
-    static class CollectionItemInfo {
-        public static boolean isSelected(Object info) {
-            return ((AccessibilityNodeInfo.CollectionItemInfo) info).isSelected();
-        }
-    }
-
-    static Object newAccessibilityAction(int actionId, CharSequence label) {
-        return new AccessibilityAction(actionId, label);
-    }
-
-    static int getAccessibilityActionId(Object action) {
-        return ((AccessibilityNodeInfo.AccessibilityAction) action).getId();
-    }
-
-    static CharSequence getAccessibilityActionLabel(Object action) {
-        return ((AccessibilityNodeInfo.AccessibilityAction) action).getLabel();
-    }
-}
diff --git a/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java b/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
deleted file mode 100644
index 23fd7ca..0000000
--- a/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityWindowInfo;
-
-/**
- * Api21-specific AccessibilityWindowInfo API implementation.
- */
-
-@RequiresApi(21)
-@TargetApi(21)
-class AccessibilityWindowInfoCompatApi21 {
-
-    public static Object obtain() {
-        return AccessibilityWindowInfo.obtain();
-    }
-
-    public static Object obtain(Object info) {
-        return AccessibilityWindowInfo.obtain((AccessibilityWindowInfo) info);
-
-    }
-
-    public static int getType(Object info) {
-        return ((AccessibilityWindowInfo) info).getType();
-    }
-
-    public static int getLayer(Object info) {
-        return ((AccessibilityWindowInfo) info).getLayer();
-    }
-
-    public static Object getRoot(Object info) {
-        return ((AccessibilityWindowInfo) info).getRoot();
-    }
-
-    public static Object getParent(Object info) {
-        return ((AccessibilityWindowInfo) info).getParent();
-    }
-
-    public static int getId(Object info) {
-        return ((AccessibilityWindowInfo) info).getId();
-    }
-
-    public static void getBoundsInScreen(Object info, Rect outBounds) {
-        ((AccessibilityWindowInfo) info).getBoundsInScreen(outBounds);
-    }
-
-    public static boolean isActive(Object info) {
-        return ((AccessibilityWindowInfo) info).isActive();
-    }
-
-    public static boolean isFocused(Object info) {
-        return ((AccessibilityWindowInfo) info).isFocused();
-    }
-
-    public static boolean isAccessibilityFocused(Object info) {
-        return ((AccessibilityWindowInfo) info).isAccessibilityFocused();
-    }
-
-    public static int getChildCount(Object info) {
-        return ((AccessibilityWindowInfo) info).getChildCount();
-    }
-
-    public static Object getChild(Object info, int index) {
-        return ((AccessibilityWindowInfo) info).getChild(index);
-    }
-
-    public static void recycle(Object info) {
-        ((AccessibilityWindowInfo) info).recycle();
-    }
-}
diff --git a/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java b/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
deleted file mode 100644
index 835e4e0..0000000
--- a/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.animation;
-
-import android.graphics.Path;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * API 21+ implementation for path interpolator compatibility.
- */
-
-@RequiresApi(21)
-@TargetApi(21)
-class PathInterpolatorCompatApi21  {
-
-    private PathInterpolatorCompatApi21() {
-        // prevent instantiation
-    }
-
-    public static Interpolator create(Path path) {
-        return new PathInterpolator(path);
-    }
-
-    public static Interpolator create(float controlX, float controlY) {
-        return new PathInterpolator(controlX, controlY);
-    }
-
-    public static Interpolator create(float controlX1, float controlY1,
-            float controlX2, float controlY2) {
-        return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
-    }
-}
diff --git a/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java b/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
deleted file mode 100644
index 42aa89a..0000000
--- a/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.CompoundButton;
-
-@RequiresApi(21)
-@TargetApi(21)
-class CompoundButtonCompatLollipop {
-
-    static void setButtonTintList(CompoundButton button, ColorStateList tint) {
-        button.setButtonTintList(tint);
-    }
-
-    static ColorStateList getButtonTintList(CompoundButton button) {
-        return button.getButtonTintList();
-    }
-
-    static void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
-        button.setButtonTintMode(tintMode);
-    }
-
-    static PorterDuff.Mode getButtonTintMode(CompoundButton button) {
-        return button.getButtonTintMode();
-    }
-}
diff --git a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java b/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
deleted file mode 100644
index f12bc23..0000000
--- a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.EdgeEffect;
-
-@RequiresApi(21)
-@TargetApi(21)
-class EdgeEffectCompatLollipop {
-    public static boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-        ((EdgeEffect) edgeEffect).onPull(deltaDistance, displacement);
-        return true;
-    }
-}
diff --git a/compat/api21/android/support/v4/widget/ImageViewCompatLollipop.java b/compat/api21/android/support/v4/widget/ImageViewCompatLollipop.java
deleted file mode 100644
index c5279d0..0000000
--- a/compat/api21/android/support/v4/widget/ImageViewCompatLollipop.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.widget.ImageView;
-
-@RequiresApi(21)
-class ImageViewCompatLollipop {
-    static ColorStateList getImageTintList(ImageView view) {
-        return view.getImageTintList();
-    }
-
-    static void setImageTintList(ImageView view, ColorStateList tintList) {
-        view.setImageTintList(tintList);
-
-        if (Build.VERSION.SDK_INT == 21) {
-            // Work around a bug in L that did not update the state of the image source
-            // after applying the tint
-            Drawable imageViewDrawable = view.getDrawable();
-            boolean hasTint = (view.getImageTintList() != null)
-                    && (view.getImageTintMode() != null);
-            if ((imageViewDrawable != null) && hasTint) {
-                if (imageViewDrawable.isStateful()) {
-                    imageViewDrawable.setState(view.getDrawableState());
-                }
-                view.setImageDrawable(imageViewDrawable);
-            }
-        }
-    }
-
-    static PorterDuff.Mode getImageTintMode(ImageView view) {
-        return view.getImageTintMode();
-    }
-
-    static void setImageTintMode(ImageView view, PorterDuff.Mode mode) {
-        view.setImageTintMode(mode);
-
-        if (Build.VERSION.SDK_INT == 21) {
-            // Work around a bug in L that did not update the state of the image source
-            // after applying the tint
-            Drawable imageViewDrawable = view.getDrawable();
-            boolean hasTint = (view.getImageTintList() != null)
-                    && (view.getImageTintMode() != null);
-            if ((imageViewDrawable != null) && hasTint) {
-                if (imageViewDrawable.isStateful()) {
-                    imageViewDrawable.setState(view.getDrawableState());
-                }
-                view.setImageDrawable(imageViewDrawable);
-            }
-        }
-    }
-}
diff --git a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java b/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
deleted file mode 100644
index 393efa6..0000000
--- a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Log;
-import android.widget.PopupWindow;
-
-import java.lang.reflect.Field;
-
-@RequiresApi(21)
-@TargetApi(21)
-class PopupWindowCompatApi21 {
-
-    private static final String TAG = "PopupWindowCompatApi21";
-
-    private static Field sOverlapAnchorField;
-
-    static {
-        try {
-            sOverlapAnchorField = PopupWindow.class.getDeclaredField("mOverlapAnchor");
-            sOverlapAnchorField.setAccessible(true);
-        } catch (NoSuchFieldException e) {
-            Log.i(TAG, "Could not fetch mOverlapAnchor field from PopupWindow", e);
-        }
-    }
-
-    static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-        if (sOverlapAnchorField != null) {
-            try {
-                sOverlapAnchorField.set(popupWindow, overlapAnchor);
-            } catch (IllegalAccessException e) {
-                Log.i(TAG, "Could not set overlap anchor field in PopupWindow", e);
-            }
-        }
-    }
-
-    static boolean getOverlapAnchor(PopupWindow popupWindow) {
-        if (sOverlapAnchorField != null) {
-            try {
-                return (Boolean) sOverlapAnchorField.get(popupWindow);
-            } catch (IllegalAccessException e) {
-                Log.i(TAG, "Could not get overlap anchor field in PopupWindow", e);
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/compat/api22/android/support/v4/app/ActivityCompatApi22.java b/compat/api22/android/support/v4/app/ActivityCompatApi22.java
deleted file mode 100644
index 1efef64..0000000
--- a/compat/api22/android/support/v4/app/ActivityCompatApi22.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.Activity;
-import android.net.Uri;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(22)
-@TargetApi(22)
-class ActivityCompatApi22 {
-    public static Uri getReferrer(Activity activity) {
-        return activity.getReferrer();
-    }
-}
diff --git a/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java b/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
deleted file mode 100644
index dd482d4..0000000
--- a/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-/**
- * Api22-specific AccessibilityNodeInfo API implementation.
- */
-
-@RequiresApi(22)
-@TargetApi(22)
-class AccessibilityNodeInfoCompatApi22 {
-
-    public static Object getTraversalBefore(Object info) {
-        return ((AccessibilityNodeInfo) info).getTraversalBefore();
-    }
-
-    public static void setTraversalBefore(Object info, View view) {
-        ((AccessibilityNodeInfo) info).setTraversalBefore(view);
-    }
-
-    public static void setTraversalBefore(Object info, View root, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).setTraversalBefore(root, virtualDescendantId);
-    }
-
-    public static Object getTraversalAfter(Object info) {
-        return ((AccessibilityNodeInfo) info).getTraversalAfter();
-    }
-
-    public static void setTraversalAfter(Object info, View view) {
-        ((AccessibilityNodeInfo) info).setTraversalAfter(view);
-    }
-
-    public static void setTraversalAfter(Object info, View root, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).setTraversalAfter(root, virtualDescendantId);
-    }
-}
diff --git a/compat/api23/android/support/v4/app/ActivityCompatApi23.java b/compat/api23/android/support/v4/app/ActivityCompatApi23.java
deleted file mode 100644
index 9012f56..0000000
--- a/compat/api23/android/support/v4/app/ActivityCompatApi23.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.Activity;
-import android.app.SharedElementCallback;
-import android.content.Context;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-import java.util.List;
-import java.util.Map;
-
-@RequiresApi(23)
-@TargetApi(23)
-class ActivityCompatApi23 {
-    public interface OnSharedElementsReadyListenerBridge {
-        void onSharedElementsReady();
-    }
-
-    public interface RequestPermissionsRequestCodeValidator {
-        void validateRequestPermissionsRequestCode(int requestCode);
-    }
-
-    public static void requestPermissions(Activity activity, String[] permissions,
-            int requestCode) {
-        if (activity instanceof RequestPermissionsRequestCodeValidator) {
-            ((RequestPermissionsRequestCodeValidator) activity)
-                    .validateRequestPermissionsRequestCode(requestCode);
-        }
-        activity.requestPermissions(permissions, requestCode);
-    }
-
-    public static boolean shouldShowRequestPermissionRationale(Activity activity,
-            String permission) {
-        return activity.shouldShowRequestPermissionRationale(permission);
-    }
-
-    public static void setEnterSharedElementCallback(Activity activity,
-            SharedElementCallback23 callback) {
-        activity.setEnterSharedElementCallback(createCallback(callback));
-    }
-
-    public static void setExitSharedElementCallback(Activity activity,
-            SharedElementCallback23 callback) {
-        activity.setExitSharedElementCallback(createCallback(callback));
-    }
-
-    private static SharedElementCallback createCallback(SharedElementCallback23 callback) {
-        SharedElementCallback newListener = null;
-        if (callback != null) {
-            newListener = new SharedElementCallbackImpl(callback);
-        }
-        return newListener;
-    }
-
-    public abstract static class SharedElementCallback23
-            extends ActivityCompatApi21.SharedElementCallback21 {
-        public abstract void onSharedElementsArrived(List<String> sharedElementNames,
-                List<View> sharedElements, OnSharedElementsReadyListenerBridge listener);
-    }
-
-    private static class SharedElementCallbackImpl extends SharedElementCallback {
-        private SharedElementCallback23 mCallback;
-
-        public SharedElementCallbackImpl(SharedElementCallback23 callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onSharedElementStart(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots) {
-            mCallback.onSharedElementStart(sharedElementNames, sharedElements,
-                    sharedElementSnapshots);
-        }
-
-        @Override
-        public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements,
-                List<View> sharedElementSnapshots) {
-            mCallback.onSharedElementEnd(sharedElementNames, sharedElements,
-                    sharedElementSnapshots);
-        }
-
-        @Override
-        public void onRejectSharedElements(List<View> rejectedSharedElements) {
-            mCallback.onRejectSharedElements(rejectedSharedElements);
-        }
-
-        @Override
-        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
-            mCallback.onMapSharedElements(names, sharedElements);
-        }
-
-        @Override
-        public Parcelable onCaptureSharedElementSnapshot(View sharedElement,
-                Matrix viewToGlobalMatrix, RectF screenBounds) {
-            return mCallback.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix,
-                            screenBounds);
-        }
-
-        @Override
-        public View onCreateSnapshotView(Context context, Parcelable snapshot) {
-            return mCallback.onCreateSnapshotView(context, snapshot);
-        }
-
-        @Override
-        public void onSharedElementsArrived(List<String> sharedElementNames,
-                List<View> sharedElements, final OnSharedElementsReadyListener listener) {
-            mCallback.onSharedElementsArrived(sharedElementNames, sharedElements,
-                    new OnSharedElementsReadyListenerBridge() {
-                        @Override
-                        public void onSharedElementsReady() {
-                            listener.onSharedElementsReady();
-                        }
-                    });
-        }
-    }
-}
diff --git a/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java b/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
deleted file mode 100644
index 81be941..0000000
--- a/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Pair;
-import android.view.View;
-
-@RequiresApi(23)
-@TargetApi(23)
-class ActivityOptionsCompat23 {
-
-    private final ActivityOptions mActivityOptions;
-
-    public static ActivityOptionsCompat23 makeCustomAnimation(Context context,
-            int enterResId, int exitResId) {
-        return new ActivityOptionsCompat23(
-            ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
-    }
-
-    public static ActivityOptionsCompat23 makeScaleUpAnimation(View source,
-            int startX, int startY, int startWidth, int startHeight) {
-        return new ActivityOptionsCompat23(
-            ActivityOptions.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight));
-    }
-
-    public static ActivityOptionsCompat23 makeThumbnailScaleUpAnimation(View source,
-            Bitmap thumbnail, int startX, int startY) {
-        return new ActivityOptionsCompat23(
-            ActivityOptions.makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY));
-    }
-
-    public static ActivityOptionsCompat23 makeSceneTransitionAnimation(Activity activity,
-            View sharedElement, String sharedElementName) {
-        return new ActivityOptionsCompat23(
-                ActivityOptions.makeSceneTransitionAnimation(activity, sharedElement,
-                        sharedElementName));
-    }
-
-    public static ActivityOptionsCompat23 makeSceneTransitionAnimation(Activity activity,
-            View[] sharedElements, String[] sharedElementNames) {
-        Pair[] pairs = null;
-        if (sharedElements != null) {
-            pairs = new Pair[sharedElements.length];
-            for (int i = 0; i < pairs.length; i++) {
-                pairs[i] = Pair.create(sharedElements[i], sharedElementNames[i]);
-            }
-        }
-        return new ActivityOptionsCompat23(
-                ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
-    }
-
-    public static ActivityOptionsCompat23 makeClipRevealAnimation(View source,
-            int startX, int startY, int width, int height) {
-        return new ActivityOptionsCompat23(
-            ActivityOptions.makeClipRevealAnimation(source, startX, startY, width, height));
-    }
-
-    public static ActivityOptionsCompat23 makeTaskLaunchBehind() {
-        return new ActivityOptionsCompat23(
-                ActivityOptions.makeTaskLaunchBehind());
-    }
-
-    public static ActivityOptionsCompat23 makeBasic() {
-        return new ActivityOptionsCompat23(ActivityOptions.makeBasic());
-    }
-
-    private ActivityOptionsCompat23(ActivityOptions activityOptions) {
-        mActivityOptions = activityOptions;
-    }
-
-    public Bundle toBundle() {
-        return mActivityOptions.toBundle();
-    }
-
-    public void update(ActivityOptionsCompat23 otherOptions) {
-        mActivityOptions.update(otherOptions.mActivityOptions);
-    }
-
-    public void requestUsageTimeReport(PendingIntent receiver) {
-        mActivityOptions.requestUsageTimeReport(receiver);
-    }
-}
diff --git a/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java b/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
deleted file mode 100644
index 853fd5d..0000000
--- a/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * AppOpsManager implementations for API 23.
- */
-
-@RequiresApi(23)
-@TargetApi(23)
-class AppOpsManagerCompat23 {
-    public static String permissionToOp(String permission) {
-        return AppOpsManager.permissionToOp(permission);
-    }
-
-    public static int noteOp(Context context, String op, int uid, String packageName) {
-        AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-        return appOpsManager.noteOp(op, uid, packageName);
-    }
-
-    public static int noteProxyOp(Context context, String op, String proxiedPackageName) {
-        AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-        return appOpsManager.noteProxyOp(op, proxiedPackageName);
-    }
-}
diff --git a/compat/api23/android/support/v4/app/NotificationCompatApi23.java b/compat/api23/android/support/v4/app/NotificationCompatApi23.java
deleted file mode 100644
index 5262ef3..0000000
--- a/compat/api23/android/support/v4/app/NotificationCompatApi23.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.Notification;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(23)
-@TargetApi(23)
-class NotificationCompatApi23 {
-
-    public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
-}
diff --git a/compat/api23/android/support/v4/content/ContextCompatApi23.java b/compat/api23/android/support/v4/content/ContextCompatApi23.java
deleted file mode 100644
index c22f5b6..0000000
--- a/compat/api23/android/support/v4/content/ContextCompatApi23.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(23)
-@TargetApi(23)
-class ContextCompatApi23 {
-    public static ColorStateList getColorStateList(Context context, int id) {
-        return context.getColorStateList(id);
-    }
-
-    public static int getColor(Context context, int id) {
-        return context.getColor(id);
-    }
-}
diff --git a/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java b/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
deleted file mode 100644
index eade1ef..0000000
--- a/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content.res;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(23)
-@TargetApi(23)
-class ResourcesCompatApi23 {
-    public static int getColor(Resources res, int id, Theme theme) throws NotFoundException {
-        return res.getColor(id, theme);
-    }
-
-    public static ColorStateList getColorStateList(Resources res, int id, Theme theme)
-            throws NotFoundException {
-        return res.getColorStateList(id, theme);
-    }
-}
diff --git a/compat/api23/android/support/v4/graphics/PaintCompatApi23.java b/compat/api23/android/support/v4/graphics/PaintCompatApi23.java
deleted file mode 100644
index c51f175..0000000
--- a/compat/api23/android/support/v4/graphics/PaintCompatApi23.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics;
-
-import android.graphics.Paint;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(23)
-class PaintCompatApi23 {
-    static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
-        return paint.hasGlyph(string);
-    }
-}
diff --git a/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java b/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
deleted file mode 100644
index e454d41..0000000
--- a/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of drawable compatibility that can call M APIs.
- */
-
-@RequiresApi(23)
-@TargetApi(23)
-class DrawableCompatApi23 {
-    public static boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
-        return drawable.setLayoutDirection(layoutDirection);
-    }
-
-    public static int getLayoutDirection(Drawable drawable) {
-        return drawable.getLayoutDirection();
-    }
-}
diff --git a/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java b/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
index e4e2aa5..2e23b85 100644
--- a/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
+++ b/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
@@ -18,7 +18,7 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
@@ -36,7 +36,6 @@
  * @hide
  */
 @RequiresApi(23)
-@TargetApi(23)
 @RestrictTo(LIBRARY_GROUP)
 public final class FingerprintManagerCompatApi23 {
 
@@ -48,17 +47,26 @@
         }
     }
 
-    public static boolean hasEnrolledFingerprints(Context context) {
+    // We expect developers to add android.permission.USE_FINGERPRINT to their manifest if they
+    // use this API.
+    @SuppressLint("MissingPermission")
+    static boolean hasEnrolledFingerprints(Context context) {
         final FingerprintManager fp = getFingerprintManagerOrNull(context);
         return (fp != null) && fp.hasEnrolledFingerprints();
     }
 
-    public static boolean isHardwareDetected(Context context) {
+    // We expect developers to add android.permission.USE_FINGERPRINT to their manifest if they
+    // use this API.
+    @SuppressLint("MissingPermission")
+    static boolean isHardwareDetected(Context context) {
         final FingerprintManager fp = getFingerprintManagerOrNull(context);
         return (fp != null) && fp.isHardwareDetected();
     }
 
-    public static void authenticate(Context context, CryptoObject crypto, int flags, Object cancel,
+    // We expect developers to add android.permission.USE_FINGERPRINT to their manifest if they
+    // use this API.
+    @SuppressLint("MissingPermission")
+    static void authenticate(Context context, CryptoObject crypto, int flags, Object cancel,
             AuthenticationCallback callback, Handler handler) {
         final FingerprintManager fp = getFingerprintManagerOrNull(context);
         if (fp != null) {
diff --git a/compat/api23/android/support/v4/text/ICUCompatApi21.java b/compat/api23/android/support/v4/text/ICUCompatApi21.java
new file mode 100644
index 0000000..086a7cd
--- /dev/null
+++ b/compat/api23/android/support/v4/text/ICUCompatApi21.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.text;
+
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+@RequiresApi(21)
+class ICUCompatApi21 {
+
+    private static final String TAG = "ICUCompatApi21";
+
+    private static Method sAddLikelySubtagsMethod;
+
+    static {
+        try {
+            // This class should always exist on API-21 since it's CTS tested.
+            final Class<?> clazz = Class.forName("libcore.icu.ICU");
+            sAddLikelySubtagsMethod = clazz.getMethod("addLikelySubtags",
+                    new Class[]{ Locale.class });
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public static String maximizeAndGetScript(Locale locale) {
+        try {
+            final Object[] args = new Object[] { locale };
+            return ((Locale) sAddLikelySubtagsMethod.invoke(null, args)).getScript();
+        } catch (InvocationTargetException e) {
+            Log.w(TAG, e);
+        } catch (IllegalAccessException e) {
+            Log.w(TAG, e);
+        }
+
+        return locale.getScript();
+    }
+}
diff --git a/compat/api23/android/support/v4/text/ICUCompatApi23.java b/compat/api23/android/support/v4/text/ICUCompatApi23.java
deleted file mode 100644
index 182c6f3..0000000
--- a/compat/api23/android/support/v4/text/ICUCompatApi23.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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
- */
-
-package android.support.v4.text;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Locale;
-
-@RequiresApi(23)
-@TargetApi(23)
-class ICUCompatApi23 {
-
-    private static final String TAG = "ICUCompatIcs";
-
-    private static Method sAddLikelySubtagsMethod;
-
-    static {
-        try {
-            // This class should always exist on API-23 since it's CTS tested.
-            final Class<?> clazz = Class.forName("libcore.icu.ICU");
-            sAddLikelySubtagsMethod = clazz.getMethod("addLikelySubtags",
-                    new Class[]{ Locale.class });
-        } catch (Exception e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-
-    public static String maximizeAndGetScript(Locale locale) {
-        try {
-            final Object[] args = new Object[] { locale };
-            return ((Locale) sAddLikelySubtagsMethod.invoke(null, args)).getScript();
-        } catch (InvocationTargetException e) {
-            Log.w(TAG, e);
-        } catch (IllegalAccessException e) {
-            Log.w(TAG, e);
-        }
-
-        return locale.getScript();
-    }
-}
diff --git a/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java b/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
deleted file mode 100644
index 30645ec..0000000
--- a/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-@RequiresApi(23)
-@TargetApi(23)
-class ViewCompatMarshmallow {
-    public static void setScrollIndicators(View view, int indicators) {
-        view.setScrollIndicators(indicators);
-    }
-
-    public static void setScrollIndicators(View view, int indicators, int mask) {
-        view.setScrollIndicators(indicators, mask);
-    }
-
-    public static int getScrollIndicators(View view) {
-        return view.getScrollIndicators();
-    }
-
-    static void offsetTopAndBottom(View view, int offset) {
-        view.offsetTopAndBottom(offset);
-    }
-
-    static void offsetLeftAndRight(View view, int offset) {
-        view.offsetLeftAndRight(offset);
-    }
-}
diff --git a/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java b/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
deleted file mode 100644
index 457cd39..0000000
--- a/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-@RequiresApi(23)
-@TargetApi(23)
-class AccessibilityNodeInfoCompatApi23 {
-    public static Object getActionScrollToPosition() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION;
-    }
-
-    public static boolean isContextClickable(Object info) {
-        return ((AccessibilityNodeInfo) info).isContextClickable();
-    }
-
-    public static void setContextClickable(Object info, boolean contextClickable) {
-        ((AccessibilityNodeInfo) info).setContextClickable(contextClickable);
-    }
-
-    public static Object getActionShowOnScreen() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_ON_SCREEN;
-    }
-
-    public static Object getActionScrollUp() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP;
-    }
-
-    public static Object getActionScrollDown() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN;
-    }
-
-    public static Object getActionScrollLeft() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT;
-    }
-
-    public static Object getActionScrollRight() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT;
-    }
-
-    public static Object getActionContextClick() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK;
-    }
-}
diff --git a/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java b/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
deleted file mode 100644
index 6dddbdb..0000000
--- a/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.CompoundButton;
-
-@RequiresApi(23)
-@TargetApi(23)
-class CompoundButtonCompatApi23 {
-
-    static Drawable getButtonDrawable(CompoundButton button) {
-        return button.getButtonDrawable();
-    }
-}
diff --git a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java b/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
deleted file mode 100644
index 1483e41..0000000
--- a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.PopupWindow;
-
-@RequiresApi(23)
-@TargetApi(23)
-class PopupWindowCompatApi23 {
-
-    static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-        popupWindow.setOverlapAnchor(overlapAnchor);
-    }
-
-    static boolean getOverlapAnchor(PopupWindow popupWindow) {
-        return popupWindow.getOverlapAnchor();
-    }
-
-    static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
-        popupWindow.setWindowLayoutType(layoutType);
-    }
-
-    static int getWindowLayoutType(PopupWindow popupWindow) {
-        return popupWindow.getWindowLayoutType();
-    }
-
-}
diff --git a/compat/api23/android/support/v4/widget/TextViewCompatApi23.java b/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
deleted file mode 100644
index f31242b..0000000
--- a/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.support.annotation.StyleRes;
-import android.widget.TextView;
-
-@RequiresApi(23)
-@TargetApi(23)
-class TextViewCompatApi23 {
-    public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
-        textView.setTextAppearance(resId);
-    }
-}
diff --git a/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java b/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
deleted file mode 100644
index d33d8a2..0000000
--- a/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.util.Pair;
-import android.view.View;
-
-@RequiresApi(24)
-@TargetApi(24)
-class ActivityOptionsCompat24 {
-
-    public static ActivityOptionsCompat24 makeCustomAnimation(Context context,
-            int enterResId, int exitResId) {
-        return new ActivityOptionsCompat24(
-            ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
-    }
-
-    public static ActivityOptionsCompat24 makeScaleUpAnimation(View source,
-            int startX, int startY, int startWidth, int startHeight) {
-        return new ActivityOptionsCompat24(
-            ActivityOptions.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight));
-    }
-
-    public static ActivityOptionsCompat24 makeThumbnailScaleUpAnimation(View source,
-            Bitmap thumbnail, int startX, int startY) {
-        return new ActivityOptionsCompat24(
-            ActivityOptions.makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY));
-    }
-
-    public static ActivityOptionsCompat24 makeSceneTransitionAnimation(Activity activity,
-            View sharedElement, String sharedElementName) {
-        return new ActivityOptionsCompat24(
-                ActivityOptions.makeSceneTransitionAnimation(activity, sharedElement,
-                        sharedElementName));
-    }
-
-    public static ActivityOptionsCompat24 makeSceneTransitionAnimation(Activity activity,
-            View[] sharedElements, String[] sharedElementNames) {
-        Pair[] pairs = null;
-        if (sharedElements != null) {
-            pairs = new Pair[sharedElements.length];
-            for (int i = 0; i < pairs.length; i++) {
-                pairs[i] = Pair.create(sharedElements[i], sharedElementNames[i]);
-            }
-        }
-        return new ActivityOptionsCompat24(
-                ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
-    }
-
-    public static ActivityOptionsCompat24 makeClipRevealAnimation(View source,
-            int startX, int startY, int width, int height) {
-        return new ActivityOptionsCompat24(
-            ActivityOptions.makeClipRevealAnimation(source, startX, startY, width, height));
-    }
-
-    public static ActivityOptionsCompat24 makeTaskLaunchBehind() {
-        return new ActivityOptionsCompat24(
-                ActivityOptions.makeTaskLaunchBehind());
-    }
-
-    public static ActivityOptionsCompat24 makeBasic() {
-        return new ActivityOptionsCompat24(ActivityOptions.makeBasic());
-    }
-
-    private final ActivityOptions mActivityOptions;
-
-    private ActivityOptionsCompat24(ActivityOptions activityOptions) {
-        mActivityOptions = activityOptions;
-    }
-
-    public ActivityOptionsCompat24 setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
-        return new ActivityOptionsCompat24(mActivityOptions.setLaunchBounds(screenSpacePixelRect));
-    }
-
-    public Rect getLaunchBounds() {
-        return mActivityOptions.getLaunchBounds();
-    }
-
-    public Bundle toBundle() {
-        return mActivityOptions.toBundle();
-    }
-
-    public void update(ActivityOptionsCompat24 otherOptions) {
-        mActivityOptions.update(otherOptions.mActivityOptions);
-    }
-
-    public void requestUsageTimeReport(PendingIntent receiver) {
-        mActivityOptions.requestUsageTimeReport(receiver);
-    }
-}
diff --git a/compat/api24/android/support/v4/app/NotificationCompatApi24.java b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
index 6a29d89..67ea5dc 100644
--- a/compat/api24/android/support/v4/app/NotificationCompatApi24.java
+++ b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
@@ -16,6 +16,13 @@
 
 package android.support.v4.app;
 
+import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
+import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
+import static android.support.v4.app.NotificationCompat.FLAG_GROUP_SUMMARY;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
+
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -23,35 +30,20 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 import java.util.List;
 
 @RequiresApi(24)
-@TargetApi(24)
 class NotificationCompatApi24 {
 
-    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
-    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
-    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
-    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
-    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
-    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
-    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
-    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
-    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
-    public static final String CATEGORY_TRANSPORT = Notification.CATEGORY_TRANSPORT;
-    public static final String CATEGORY_SYSTEM = Notification.CATEGORY_SYSTEM;
-    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
-    public static final String CATEGORY_RECOMMENDATION = Notification.CATEGORY_RECOMMENDATION;
-    public static final String CATEGORY_STATUS = Notification.CATEGORY_STATUS;
-
     public static class Builder implements NotificationBuilderWithBuilderAccessor,
             NotificationBuilderWithActions {
         private Notification.Builder b;
+        private int mGroupAlertBehavior;
 
         public Builder(Context context, Notification n,
                 CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
@@ -62,7 +54,8 @@
                 String category, ArrayList<String> people, Bundle extras, int color,
                 int visibility, Notification publicVersion, String groupKey, boolean groupSummary,
                 String sortKey, CharSequence[] remoteInputHistory, RemoteViews contentView,
-                RemoteViews bigContentView, RemoteViews headsUpContentView) {
+                RemoteViews bigContentView, RemoteViews headsUpContentView,
+                int groupAlertBehavior) {
             b = new Notification.Builder(context)
                     .setWhen(n.when)
                     .setShowWhen(showWhen)
@@ -111,29 +104,13 @@
             for (String person: people) {
                 b.addPerson(person);
             }
+
+            mGroupAlertBehavior = groupAlertBehavior;
         }
 
         @Override
         public void addAction(NotificationCompatBase.Action action) {
-            Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
-                    action.getIcon(), action.getTitle(), action.getActionIntent());
-            if (action.getRemoteInputs() != null) {
-                for (RemoteInput remoteInput : RemoteInputCompatApi20.fromCompat(
-                        action.getRemoteInputs())) {
-                    actionBuilder.addRemoteInput(remoteInput);
-                }
-            }
-            Bundle actionExtras;
-            if (action.getExtras() != null) {
-                actionExtras = new Bundle(action.getExtras());
-            } else {
-                actionExtras = new Bundle();
-            }
-            actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
-                    action.getAllowGeneratedReplies());
-            actionBuilder.addExtras(actionExtras);
-            actionBuilder.setAllowGeneratedReplies(action.getAllowGeneratedReplies());
-            b.addAction(actionBuilder.build());
+            NotificationCompatApi24.addAction(b, action);
         }
 
         @Override
@@ -143,7 +120,31 @@
 
         @Override
         public Notification build() {
-            return b.build();
+            Notification notification =  b.build();
+
+            if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
+                // if is summary and only children should alert
+                if (notification.getGroup() != null
+                        && (notification.flags & FLAG_GROUP_SUMMARY) != 0
+                        && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
+                    removeSoundAndVibration(notification);
+                }
+                // if is group child and only summary should alert
+                if (notification.getGroup() != null
+                        && (notification.flags & FLAG_GROUP_SUMMARY) == 0
+                        && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
+                    removeSoundAndVibration(notification);
+                }
+            }
+
+            return notification;
+        }
+
+        private void removeSoundAndVibration(Notification notification) {
+            notification.sound = null;
+            notification.vibrate = null;
+            notification.defaults &= ~DEFAULT_SOUND;
+            notification.defaults &= ~DEFAULT_VIBRATE;
         }
     }
 
@@ -163,4 +164,107 @@
         }
         style.setBuilder(b.getBuilder());
     }
+
+    public static void addAction(Notification.Builder b, NotificationCompatBase.Action action) {
+        Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
+                action.getIcon(), action.getTitle(), action.getActionIntent());
+        if (action.getRemoteInputs() != null) {
+            for (RemoteInput remoteInput : RemoteInputCompatApi20.fromCompat(
+                    action.getRemoteInputs())) {
+                actionBuilder.addRemoteInput(remoteInput);
+            }
+        }
+        Bundle actionExtras;
+        if (action.getExtras() != null) {
+            actionExtras = new Bundle(action.getExtras());
+        } else {
+            actionExtras = new Bundle();
+        }
+        actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
+                action.getAllowGeneratedReplies());
+        actionBuilder.setAllowGeneratedReplies(action.getAllowGeneratedReplies());
+        actionBuilder.addExtras(actionExtras);
+        b.addAction(actionBuilder.build());
+    }
+
+    public static NotificationCompatBase.Action getAction(Notification notif,
+            int actionIndex, NotificationCompatBase.Action.Factory actionFactory,
+            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
+        return getActionCompatFromAction(notif.actions[actionIndex], actionFactory,
+                remoteInputFactory);
+    }
+
+    private static NotificationCompatBase.Action getActionCompatFromAction(
+            Notification.Action action, NotificationCompatBase.Action.Factory actionFactory,
+            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
+        RemoteInputCompatBase.RemoteInput[] remoteInputs = RemoteInputCompatApi20.toCompat(
+                action.getRemoteInputs(), remoteInputFactory);
+        boolean allowGeneratedReplies = action.getExtras().getBoolean(
+                NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES)
+                || action.getAllowGeneratedReplies();
+        return actionFactory.build(action.icon, action.title, action.actionIntent,
+                action.getExtras(), remoteInputs, null, allowGeneratedReplies);
+    }
+
+    private static Notification.Action getActionFromActionCompat(
+            NotificationCompatBase.Action actionCompat) {
+        Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
+                actionCompat.getIcon(), actionCompat.getTitle(), actionCompat.getActionIntent());
+        Bundle actionExtras;
+        if (actionCompat.getExtras() != null) {
+            actionExtras = new Bundle(actionCompat.getExtras());
+        } else {
+            actionExtras = new Bundle();
+        }
+        actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
+                actionCompat.getAllowGeneratedReplies());
+        actionBuilder.setAllowGeneratedReplies(actionCompat.getAllowGeneratedReplies());
+        actionBuilder.addExtras(actionExtras);
+        RemoteInputCompatBase.RemoteInput[] remoteInputCompats = actionCompat.getRemoteInputs();
+        if (remoteInputCompats != null) {
+            RemoteInput[] remoteInputs = RemoteInputCompatApi20.fromCompat(remoteInputCompats);
+            for (RemoteInput remoteInput : remoteInputs) {
+                actionBuilder.addRemoteInput(remoteInput);
+            }
+        }
+        return actionBuilder.build();
+    }
+
+    /**
+     * Get a list of notification compat actions by parsing actions stored within a list of
+     * parcelables using the {@link Bundle#getParcelableArrayList} function in the same
+     * manner that framework code would do so. In API20, Using Action parcelable directly
+     * is correct.
+     */
+    public static NotificationCompatBase.Action[] getActionsFromParcelableArrayList(
+            ArrayList<Parcelable> parcelables,
+            NotificationCompatBase.Action.Factory actionFactory,
+            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
+        if (parcelables == null) {
+            return null;
+        }
+        NotificationCompatBase.Action[] actions = actionFactory.newArray(parcelables.size());
+        for (int i = 0; i < actions.length; i++) {
+            Notification.Action action = (Notification.Action) parcelables.get(i);
+            actions[i] = getActionCompatFromAction(action, actionFactory, remoteInputFactory);
+        }
+        return actions;
+    }
+
+    /**
+     * Get an array list of parcelables, suitable for {@link Bundle#putParcelableArrayList},
+     * that matches what framework code would do to store an actions list in this way. In API20,
+     * action parcelables were directly placed as entries in the array list.
+     */
+    public static ArrayList<Parcelable> getParcelableArrayListForActions(
+            NotificationCompatBase.Action[] actions) {
+        if (actions == null) {
+            return null;
+        }
+        ArrayList<Parcelable> parcelables = new ArrayList<Parcelable>(actions.length);
+        for (NotificationCompatBase.Action action : actions) {
+            parcelables.add(getActionFromActionCompat(action));
+        }
+        return parcelables;
+    }
 }
diff --git a/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java b/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
deleted file mode 100644
index 468592f..0000000
--- a/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.app;
-
-import android.app.NotificationManager;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(24)
-@TargetApi(24)
-class NotificationManagerCompatApi24 {
-    public static boolean areNotificationsEnabled(NotificationManager notificationManager) {
-        return notificationManager.areNotificationsEnabled();
-    }
-
-    public static int getImportance(NotificationManager notificationManager) {
-        return notificationManager.getImportance();
-    }
-}
diff --git a/compat/api24/android/support/v4/app/ServiceCompatApi24.java b/compat/api24/android/support/v4/app/ServiceCompatApi24.java
deleted file mode 100644
index 29b6112..0000000
--- a/compat/api24/android/support/v4/app/ServiceCompatApi24.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.app;
-
-import android.app.Service;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(24)
-@TargetApi(24)
-class ServiceCompatApi24 {
-    public static void stopForeground(Service service, int flags) {
-        service.stopForeground(flags);
-    }
-}
diff --git a/compat/api24/android/support/v4/content/ContextCompatApi24.java b/compat/api24/android/support/v4/content/ContextCompatApi24.java
deleted file mode 100644
index a65f21b..0000000
--- a/compat/api24/android/support/v4/content/ContextCompatApi24.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.io.File;
-
-@RequiresApi(24)
-@TargetApi(24)
-class ContextCompatApi24 {
-    public static File getDataDir(Context context) {
-        return context.getDataDir();
-    }
-
-    public static Context createDeviceProtectedStorageContext(Context context) {
-        return context.createDeviceProtectedStorageContext();
-    }
-
-    public static boolean isDeviceProtectedStorage(Context context) {
-        return context.isDeviceProtectedStorage();
-    }
-}
diff --git a/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java b/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
deleted file mode 100644
index b6e86cb..0000000
--- a/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.net;
-
-import android.annotation.TargetApi;
-import android.net.ConnectivityManager;
-import android.support.annotation.RequiresApi;
-
-/**
- * Implementation of ConnectivityManagerCompat that can use API 24 APIs.
- */
-@RequiresApi(24)
-@TargetApi(24)
-class ConnectivityManagerCompatApi24 {
-    public static int getRestrictBackgroundStatus(ConnectivityManager cm) {
-        return cm.getRestrictBackgroundStatus();
-    }
-}
diff --git a/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java b/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
deleted file mode 100644
index 834c6ad..0000000
--- a/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.net;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
-import android.net.TrafficStats;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-
-import java.net.DatagramSocket;
-import java.net.SocketException;
-
-/** @hide */
-@RequiresApi(24)
-@TargetApi(24)
-@RestrictTo(LIBRARY_GROUP)
-public class TrafficStatsCompatApi24 {
-    public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
-        TrafficStats.tagDatagramSocket(socket);
-    }
-
-    public static void untagDatagramSocket(DatagramSocket socket) throws SocketException {
-        TrafficStats.untagDatagramSocket(socket);
-    }
-}
diff --git a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
deleted file mode 100644
index ab6f91d..0000000
--- a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.os;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.UserManager;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-
-/** @hide */
-@RequiresApi(24)
-@TargetApi(24)
-@RestrictTo(LIBRARY_GROUP)
-public class UserManagerCompatApi24 {
-    public static boolean isUserUnlocked(Context context) {
-        return context.getSystemService(UserManager.class).isUserUnlocked();
-    }
-}
diff --git a/compat/api24/android/support/v4/view/PointerIconCompatApi24.java b/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
deleted file mode 100644
index 424af92..0000000
--- a/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.PointerIcon;
-
-@RequiresApi(24)
-@TargetApi(24)
-class PointerIconCompatApi24 {
-    public static Object getSystemIcon(Context context, int style) {
-        return PointerIcon.getSystemIcon(context, style);
-    }
-
-    public static Object create(Bitmap bitmap, float hotSpotX, float hotSpotY) {
-        return PointerIcon.create(bitmap, hotSpotX, hotSpotY);
-    }
-
-    public static Object load(Resources resources, int resourceId) {
-        return PointerIcon.load(resources, resourceId);
-    }
-}
diff --git a/compat/api24/android/support/v4/view/ViewCompatApi24.java b/compat/api24/android/support/v4/view/ViewCompatApi24.java
deleted file mode 100644
index 71366a8..0000000
--- a/compat/api24/android/support/v4/view/ViewCompatApi24.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.PointerIcon;
-import android.view.View;
-
-@RequiresApi(24)
-@TargetApi(24)
-class ViewCompatApi24 {
-    public static void setPointerIcon(View view, Object pointerIcon) {
-        view.setPointerIcon((PointerIcon)pointerIcon);
-    }
-}
diff --git a/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java b/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
deleted file mode 100644
index 5e64091..0000000
--- a/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-/**
- * Api24-specific AccessibilityNodeInfo API implementation.
- */
-
-@RequiresApi(24)
-@TargetApi(24)
-class AccessibilityNodeInfoCompatApi24 {
-    public static Object getActionSetProgress() {
-        return AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS;
-    }
-
-    public static int getDrawingOrder(Object info) {
-        return ((AccessibilityNodeInfo) info).getDrawingOrder();
-    }
-
-    public static void setDrawingOrder(Object info, int drawingOrderInParent) {
-        ((AccessibilityNodeInfo) info).setDrawingOrder(drawingOrderInParent);
-    }
-
-    public static boolean isImportantForAccessibility(Object info) {
-        return ((AccessibilityNodeInfo) info).isImportantForAccessibility();
-    }
-
-    public static void setImportantForAccessibility(Object info,
-            boolean importantForAccessibility) {
-        ((AccessibilityNodeInfo) info).setImportantForAccessibility(importantForAccessibility);
-    }
-}
diff --git a/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java b/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
deleted file mode 100644
index c8aa21a..0000000
--- a/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityWindowInfo;
-
-/**
- * Api24-specific AccessibilityWindowInfo API implementation.
- */
-
-@RequiresApi(24)
-@TargetApi(24)
-class AccessibilityWindowInfoCompatApi24 {
-    public static CharSequence getTitle(Object info) {
-        return ((AccessibilityWindowInfo) info).getTitle();
-    }
-
-    public static Object getAnchor(Object info) {
-        return ((AccessibilityWindowInfo) info).getAnchor();
-    }
-}
diff --git a/compat/api26/android/support/v4/app/NotificationCompatApi26.java b/compat/api26/android/support/v4/app/NotificationCompatApi26.java
new file mode 100644
index 0000000..ad696a8
--- /dev/null
+++ b/compat/api26/android/support/v4/app/NotificationCompatApi26.java
@@ -0,0 +1,121 @@
+/**
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.widget.RemoteViews;
+
+import java.util.ArrayList;
+
+@RequiresApi(26)
+class NotificationCompatApi26 {
+    public static class Builder implements NotificationBuilderWithBuilderAccessor,
+            NotificationBuilderWithActions {
+
+        private Notification.Builder mB;
+
+        Builder(Context context, Notification n,
+                CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
+                RemoteViews tickerView, int number,
+                PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
+                int progressMax, int progress, boolean progressIndeterminate, boolean showWhen,
+                boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
+                String category, ArrayList<String> people, Bundle extras, int color,
+                int visibility, Notification publicVersion, String groupKey, boolean groupSummary,
+                String sortKey, CharSequence[] remoteInputHistory, RemoteViews contentView,
+                RemoteViews bigContentView, RemoteViews headsUpContentView,
+                String channelId, int badgeIcon, String shortcutId, long timeoutMs,
+                boolean colorized, boolean colorizedSet, int groupAlertBehavior) {
+            mB = new Notification.Builder(context, channelId)
+                    .setWhen(n.when)
+                    .setShowWhen(showWhen)
+                    .setSmallIcon(n.icon, n.iconLevel)
+                    .setContent(n.contentView)
+                    .setTicker(n.tickerText, tickerView)
+                    .setSound(n.sound, n.audioStreamType)
+                    .setVibrate(n.vibrate)
+                    .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
+                    .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+                    .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
+                    .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
+                    .setDefaults(n.defaults)
+                    .setContentTitle(contentTitle)
+                    .setContentText(contentText)
+                    .setSubText(subText)
+                    .setContentInfo(contentInfo)
+                    .setContentIntent(contentIntent)
+                    .setDeleteIntent(n.deleteIntent)
+                    .setFullScreenIntent(fullScreenIntent,
+                            (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
+                    .setLargeIcon(largeIcon)
+                    .setNumber(number)
+                    .setUsesChronometer(useChronometer)
+                    .setPriority(priority)
+                    .setProgress(progressMax, progress, progressIndeterminate)
+                    .setLocalOnly(localOnly)
+                    .setExtras(extras)
+                    .setGroup(groupKey)
+                    .setGroupSummary(groupSummary)
+                    .setSortKey(sortKey)
+                    .setCategory(category)
+                    .setColor(color)
+                    .setVisibility(visibility)
+                    .setPublicVersion(publicVersion)
+                    .setRemoteInputHistory(remoteInputHistory)
+                    .setChannelId(channelId)
+                    .setBadgeIconType(badgeIcon)
+                    .setShortcutId(shortcutId)
+                    .setTimeoutAfter(timeoutMs)
+                    .setGroupAlertBehavior(groupAlertBehavior);
+            if (colorizedSet) {
+                mB.setColorized(colorized);
+            }
+            if (contentView != null) {
+                mB.setCustomContentView(contentView);
+            }
+            if (bigContentView != null) {
+                mB.setCustomBigContentView(bigContentView);
+            }
+            if (headsUpContentView != null) {
+                mB.setCustomHeadsUpContentView(headsUpContentView);
+            }
+            for (String person : people) {
+                mB.addPerson(person);
+            }
+        }
+
+        @Override
+        public void addAction(NotificationCompatBase.Action action) {
+            NotificationCompatApi24.addAction(mB, action);
+        }
+
+        @Override
+        public Notification.Builder getBuilder() {
+            return mB;
+        }
+
+        @Override
+        public Notification build() {
+            return mB.build();
+        }
+    }
+}
diff --git a/compat/build.gradle b/compat/build.gradle
index e87db0e..5f9e079 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -1,111 +1,48 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-compat'
 
 dependencies {
-    compile project(':support-annotations')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+    androidTestImplementation project(':support-testutils')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
-                'gingerbread',
-                'honeycomb',
-                'honeycomb_mr1',
-                'honeycomb_mr2',
                 'ics',
-                'ics-mr1',
                 'jellybean',
-                'jellybean-mr1',
-                'jellybean-mr2',
                 'kitkat',
                 'api20',
                 'api21',
-                'api22',
                 'api23',
                 'api24',
+                'api26',
                 'java'
         ]
         main.aidl.srcDirs = ['java']
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
+        main.res.srcDirs 'res', 'res-public'
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+    aaptOptions {
+        noCompress 'ttf'
     }
-        compileSdkVersion project.ext.currentSdk
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-        exclude('android/content/pm/**')
-        exclude('android/service/media/**')
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android Support Library compat'
+    inceptionYear '2015'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library compat'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/compat/gingerbread/android/support/v4/animation/AnimatorListenerCompat.java b/compat/gingerbread/android/support/v4/animation/AnimatorListenerCompat.java
deleted file mode 100644
index a2c043f..0000000
--- a/compat/gingerbread/android/support/v4/animation/AnimatorListenerCompat.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * <p>An animation listener that receives notifications from an animation.
- * Notifications indicate animation related events, such as the end or the
- * repetition of the animation.</p>
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface AnimatorListenerCompat {
-
-    /**
-     * <p>Notifies the start of the animation.</p>
-     *
-     * @param animation The started animation.
-     */
-    void onAnimationStart(ValueAnimatorCompat animation);
-
-    /**
-     * <p>Notifies the end of the animation. This callback is not invoked
-     * for animations with repeat count set to INFINITE.</p>
-     *
-     * @param animation The animation which reached its end.
-     */
-    void onAnimationEnd(ValueAnimatorCompat animation);
-
-    /**
-     * <p>Notifies the cancellation of the animation. This callback is not invoked
-     * for animations with repeat count set to INFINITE.</p>
-     *
-     * @param animation The animation which was canceled.
-     */
-    void onAnimationCancel(ValueAnimatorCompat animation);
-
-    /**
-     * <p>Notifies the repetition of the animation.</p>
-     *
-     * @param animation The animation which was repeated.
-     */
-    void onAnimationRepeat(ValueAnimatorCompat animation);
-}
diff --git a/compat/gingerbread/android/support/v4/animation/AnimatorProvider.java b/compat/gingerbread/android/support/v4/animation/AnimatorProvider.java
deleted file mode 100644
index 51d4bb0..0000000
--- a/compat/gingerbread/android/support/v4/animation/AnimatorProvider.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import android.view.View;
-
-/**
- * A simple interface to do things in animation pulse.
- * <p>
- * Before Honeycomb, it uses a simple Handler to mimic animation callback.
- * <p>
- * This is only a minimal implementation which is why this class is hidden.
- */
-interface AnimatorProvider {
-
-    /**
-     * Provides a simple ValueAnimator w/o any start or end values. It provides the same
-     * Animator callback interface.
-     */
-    ValueAnimatorCompat emptyValueAnimator();
-
-    void clearInterpolator(View view);
-}
diff --git a/compat/gingerbread/android/support/v4/animation/AnimatorUpdateListenerCompat.java b/compat/gingerbread/android/support/v4/animation/AnimatorUpdateListenerCompat.java
deleted file mode 100644
index 2cf3fbd..0000000
--- a/compat/gingerbread/android/support/v4/animation/AnimatorUpdateListenerCompat.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Implementors of this interface can add themselves as update listeners
- * to a <code>ValueAnimator</code> instance to receive callbacks on every animation
- * frame, after the current frame's values have been calculated for that
- * <code>ValueAnimator</code>.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface AnimatorUpdateListenerCompat {
-
-    /**
-     * <p>Notifies the occurrence of another frame of the animation.</p>
-     *
-     * @param animation The animation which was repeated.
-     */
-    void onAnimationUpdate(ValueAnimatorCompat animation);
-
-}
\ No newline at end of file
diff --git a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java b/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
deleted file mode 100644
index 83ba12a..0000000
--- a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Provides similar functionality to Animators on platforms prior to Honeycomb.
- * <p>
- * This is not a fully implemented API which is why it is not public.
- */
-
-@RequiresApi(9)
-@TargetApi(9)
-class GingerbreadAnimatorCompatProvider implements AnimatorProvider {
-
-    @Override
-    public ValueAnimatorCompat emptyValueAnimator() {
-        return new GingerbreadFloatValueAnimator();
-    }
-
-    private static class GingerbreadFloatValueAnimator implements ValueAnimatorCompat {
-
-        List<AnimatorListenerCompat> mListeners = new ArrayList<AnimatorListenerCompat>();
-        List<AnimatorUpdateListenerCompat> mUpdateListeners
-                = new ArrayList<AnimatorUpdateListenerCompat>();
-        View mTarget;
-        private long mStartTime;
-        private long mDuration = 200;
-        private float mFraction = 0f;
-
-        private boolean mStarted = false;
-        private boolean mEnded = false;
-
-        public GingerbreadFloatValueAnimator() {
-        }
-
-        private Runnable mLoopRunnable = new Runnable() {
-            @Override
-            public void run() {
-                long dt = getTime() - mStartTime;
-                float fraction = dt * 1f / mDuration;
-                if (fraction > 1f || mTarget.getParent() == null) {
-                    fraction = 1f;
-                }
-                mFraction = fraction;
-                notifyUpdateListeners();
-                if (mFraction >= 1f) {
-                    dispatchEnd();
-                } else {
-                    mTarget.postDelayed(mLoopRunnable, 16);
-                }
-            }
-        };
-
-        private void notifyUpdateListeners() {
-            for (int i = mUpdateListeners.size() - 1; i >= 0; i--) {
-                mUpdateListeners.get(i).onAnimationUpdate(this);
-            }
-        }
-
-        @Override
-        public void setTarget(View view) {
-            mTarget = view;
-        }
-
-        @Override
-        public void addListener(AnimatorListenerCompat listener) {
-            mListeners.add(listener);
-        }
-
-        @Override
-        public void setDuration(long duration) {
-            if (!mStarted) {
-                mDuration = duration;
-            }
-        }
-
-        @Override
-        public void start() {
-            if (mStarted) {
-                return;
-            }
-            mStarted = true;
-            dispatchStart();
-            mFraction = 0f;
-            mStartTime = getTime();
-            mTarget.postDelayed(mLoopRunnable, 16);
-        }
-
-        private long getTime() {
-            return mTarget.getDrawingTime();
-        }
-
-        private void dispatchStart() {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onAnimationStart(this);
-            }
-        }
-
-        private void dispatchEnd() {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onAnimationEnd(this);
-            }
-        }
-
-        private void dispatchCancel() {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onAnimationCancel(this);
-            }
-        }
-
-        @Override
-        public void cancel() {
-            if (mEnded) {
-                return;
-            }
-            mEnded = true;
-            if (mStarted) {
-                dispatchCancel();
-            }
-            dispatchEnd();
-        }
-
-        @Override
-        public void addUpdateListener(AnimatorUpdateListenerCompat animatorUpdateListener) {
-            mUpdateListeners.add(animatorUpdateListener);
-        }
-
-        @Override
-        public float getAnimatedFraction() {
-            return mFraction;
-        }
-    }
-
-    @Override
-    public void clearInterpolator(View view) {
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/animation/ValueAnimatorCompat.java b/compat/gingerbread/android/support/v4/animation/ValueAnimatorCompat.java
deleted file mode 100644
index b064030..0000000
--- a/compat/gingerbread/android/support/v4/animation/ValueAnimatorCompat.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.View;
-
-/**
- * Compatibility implementation for {@code android.animation.ValueAnimator}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface ValueAnimatorCompat {
-
-    public void setTarget(View view);
-
-    public void addListener(AnimatorListenerCompat listener);
-
-    public void setDuration(long duration);
-
-    public void start();
-
-    public void cancel();
-
-    void addUpdateListener(AnimatorUpdateListenerCompat animatorUpdateListener);
-
-    public float getAnimatedFraction();
-}
diff --git a/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java b/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
deleted file mode 100644
index f7656be..0000000
--- a/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.os.Bundle;
-import android.os.IBinder;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-@RequiresApi(9)
-@TargetApi(9)
-class BundleCompatGingerbread {
-    private static final String TAG = "BundleCompatGingerbread";
-
-    private static Method sGetIBinderMethod;
-    private static boolean sGetIBinderMethodFetched;
-
-    private static Method sPutIBinderMethod;
-    private static boolean sPutIBinderMethodFetched;
-
-    public static IBinder getBinder(Bundle bundle, String key) {
-        if (!sGetIBinderMethodFetched) {
-            try {
-                sGetIBinderMethod = Bundle.class.getMethod("getIBinder", String.class);
-                sGetIBinderMethod.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                Log.i(TAG, "Failed to retrieve getIBinder method", e);
-            }
-            sGetIBinderMethodFetched = true;
-        }
-
-        if (sGetIBinderMethod != null) {
-            try {
-                return (IBinder) sGetIBinderMethod.invoke(bundle, key);
-            } catch (InvocationTargetException | IllegalAccessException
-                    | IllegalArgumentException e) {
-                Log.i(TAG, "Failed to invoke getIBinder via reflection", e);
-                sGetIBinderMethod = null;
-            }
-        }
-        return null;
-    }
-
-    public static void putBinder(Bundle bundle, String key, IBinder binder) {
-        if (!sPutIBinderMethodFetched) {
-            try {
-                sPutIBinderMethod =
-                        Bundle.class.getMethod("putIBinder", String.class, IBinder.class);
-                sPutIBinderMethod.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                Log.i(TAG, "Failed to retrieve putIBinder method", e);
-            }
-            sPutIBinderMethodFetched = true;
-        }
-
-        if (sPutIBinderMethod != null) {
-            try {
-                sPutIBinderMethod.invoke(bundle, key, binder);
-            } catch (InvocationTargetException | IllegalAccessException
-                    | IllegalArgumentException e) {
-                Log.i(TAG, "Failed to invoke putIBinder via reflection", e);
-                sPutIBinderMethod = null;
-            }
-        }
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java b/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
deleted file mode 100644
index 33447cc..0000000
--- a/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-@RequiresApi(9)
-@TargetApi(9)
-public class NotificationCompatBase {
-    private static Method sSetLatestEventInfo;
-
-    public static abstract class Action {
-        public abstract int getIcon();
-        public abstract CharSequence getTitle();
-        public abstract PendingIntent getActionIntent();
-        public abstract Bundle getExtras();
-        public abstract RemoteInputCompatBase.RemoteInput[] getRemoteInputs();
-        public abstract boolean getAllowGeneratedReplies();
-
-        public interface Factory {
-            Action build(int icon, CharSequence title, PendingIntent actionIntent,
-                    Bundle extras, RemoteInputCompatBase.RemoteInput[] remoteInputs,
-                    boolean allowGeneratedReplies);
-            public Action[] newArray(int length);
-        }
-    }
-
-    public static abstract class UnreadConversation {
-        abstract String[] getParticipants();
-        abstract String getParticipant();
-        abstract String[] getMessages();
-        abstract RemoteInputCompatBase.RemoteInput getRemoteInput();
-        abstract PendingIntent getReplyPendingIntent();
-        abstract PendingIntent getReadPendingIntent();
-        abstract long getLatestTimestamp();
-
-        public interface Factory {
-            UnreadConversation build(String[] messages,
-                    RemoteInputCompatBase.RemoteInput remoteInput,
-                    PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
-                    String[] participants, long latestTimestamp);
-        }
-    }
-
-    public static Notification add(Notification notification, Context context,
-            CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent,
-            PendingIntent fullScreenIntent) {
-        if (sSetLatestEventInfo == null) {
-            try {
-                sSetLatestEventInfo = Notification.class.getMethod("setLatestEventInfo",
-                        Context.class, CharSequence.class, CharSequence.class, PendingIntent.class);
-            } catch (NoSuchMethodException e) {
-                // This method was @removed, so it must exist on later
-                // versions even if it's not in public API.
-                throw new RuntimeException(e);
-            }
-        }
-
-        try {
-            sSetLatestEventInfo.invoke(notification, context,
-                    contentTitle, contentText, contentIntent);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            // This method was @removed, so it must be invokable on later
-            // versions even if it's not in public API.
-            throw new RuntimeException(e);
-        }
-
-        notification.fullScreenIntent = fullScreenIntent;
-        return notification;
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java b/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
deleted file mode 100644
index 85117dd..0000000
--- a/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(9)
-@TargetApi(9)
-class RemoteInputCompatBase {
-
-    public static abstract class RemoteInput {
-        protected abstract String getResultKey();
-        protected abstract CharSequence getLabel();
-        protected abstract CharSequence[] getChoices();
-        protected abstract boolean getAllowFreeFormInput();
-        protected abstract Bundle getExtras();
-
-        public interface Factory {
-            public RemoteInput build(String resultKey, CharSequence label,
-                    CharSequence[] choices, boolean allowFreeFormInput, Bundle extras);
-            public RemoteInput[] newArray(int length);
-        }
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java b/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
deleted file mode 100644
index 6667431..0000000
--- a/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content.res;
-
-import android.content.res.Resources;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.DisplayMetrics;
-
-@RequiresApi(9)
-@TargetApi(9)
-class ConfigurationHelperGingerbread {
-
-    static int getScreenHeightDp(@NonNull Resources resources) {
-        final DisplayMetrics metrics = resources.getDisplayMetrics();
-        return (int) (metrics.heightPixels / metrics.density);
-    }
-
-    static int getScreenWidthDp(@NonNull Resources resources) {
-        final DisplayMetrics metrics = resources.getDisplayMetrics();
-        return (int) (metrics.widthPixels / metrics.density);
-    }
-
-    static int getSmallestScreenWidthDp(@NonNull Resources resources) {
-        // Not perfect, but close enough
-        return Math.min(getScreenWidthDp(resources), getScreenHeightDp(resources));
-    }
-
-    static int getDensityDpi(@NonNull Resources resources) {
-        return resources.getDisplayMetrics().densityDpi;
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/graphics/PaintCompatGingerbread.java b/compat/gingerbread/android/support/v4/graphics/PaintCompatGingerbread.java
deleted file mode 100644
index 0d1076f..0000000
--- a/compat/gingerbread/android/support/v4/graphics/PaintCompatGingerbread.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics;
-
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.v4.util.Pair;
-
-@RequiresApi(9)
-class PaintCompatGingerbread {
-    // U+DFFFD which is very end of unassigned plane.
-    private static final String TOFU_STRING = "\uDB3F\uDFFD";
-
-    private static final ThreadLocal<Pair<Rect, Rect>> sRectThreadLocal = new ThreadLocal<>();
-
-    static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
-        final int length = string.length();
-
-        if (length == 1 && Character.isWhitespace(string.charAt(0))) {
-            // measureText + getTextBounds skips whitespace so we need to special case it here
-            return true;
-        }
-
-        final float missingGlyphWidth = paint.measureText(TOFU_STRING);
-        final float width = paint.measureText(string);
-
-        if (width == 0f) {
-            // If the string width is 0, it can't be rendered
-            return false;
-        }
-
-        if (string.codePointCount(0, string.length()) > 1) {
-            // Heuristic to detect fallback glyphs for ligatures like flags and ZWJ sequences
-            // Return false if string is rendered too widely
-            if (width > 2 * missingGlyphWidth) {
-                return false;
-            }
-
-            // Heuristic to detect fallback glyphs for ligatures like flags and ZWJ sequences (2).
-            // If width is greater than or equal to the sum of width of each code point, it is very
-            // likely that the system is using fallback fonts to draw {@code string} in two or more
-            // glyphs instead of a single ligature glyph. (hasGlyph returns false in this case.)
-            // False detections are possible (the ligature glyph may happen to have the same width
-            // as the sum width), but there are no good way to avoid them.
-            // NOTE: This heuristic does not work with proportional glyphs.
-            // NOTE: This heuristic does not work when a ZWJ sequence is partially combined.
-            // E.g. If system has a glyph for "A ZWJ B" and not for "A ZWJ B ZWJ C", this heuristic
-            // returns true for "A ZWJ B ZWJ C".
-            float sumWidth = 0;
-            int i = 0;
-            while (i < length) {
-                int charCount = Character.charCount(string.codePointAt(i));
-                sumWidth += paint.measureText(string, i, i + charCount);
-                i += charCount;
-            }
-            if (width >= sumWidth) {
-                return false;
-            }
-        }
-
-        if (width != missingGlyphWidth) {
-            // If the widths are different then its not tofu
-            return true;
-        }
-
-        // If the widths are the same, lets check the bounds. The chance of them being
-        // different chars with the same bounds is extremely small
-        final Pair<Rect, Rect> rects = obtainEmptyRects();
-        paint.getTextBounds(TOFU_STRING, 0, TOFU_STRING.length(), rects.first);
-        paint.getTextBounds(string, 0, length, rects.second);
-        return !rects.first.equals(rects.second);
-    }
-
-    private static Pair<Rect, Rect> obtainEmptyRects() {
-        Pair<Rect, Rect> rects = sRectThreadLocal.get();
-        if (rects == null) {
-            rects = new Pair(new Rect(), new Rect());
-            sRectThreadLocal.set(rects);
-        } else {
-            rects.first.setEmpty();
-            rects.second.setEmpty();
-        }
-        return rects;
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
deleted file mode 100644
index 8e5cd9f..0000000
--- a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.AttributeSet;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * Base implementation of drawable compatibility.
- */
-
-@RequiresApi(9)
-@TargetApi(9)
-class DrawableCompatBase {
-
-    public static void setTint(Drawable drawable, int tint) {
-        if (drawable instanceof TintAwareDrawable) {
-            ((TintAwareDrawable) drawable).setTint(tint);
-        }
-    }
-
-    public static void setTintList(Drawable drawable, ColorStateList tint) {
-        if (drawable instanceof TintAwareDrawable) {
-            ((TintAwareDrawable) drawable).setTintList(tint);
-        }
-    }
-
-    public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
-        if (drawable instanceof TintAwareDrawable) {
-            ((TintAwareDrawable) drawable).setTintMode(tintMode);
-        }
-    }
-
-    public static Drawable wrapForTinting(Drawable drawable) {
-        if (!(drawable instanceof TintAwareDrawable)) {
-            return new DrawableWrapperGingerbread(drawable);
-        }
-        return drawable;
-    }
-
-    public static void inflate(Drawable drawable, Resources res, XmlPullParser parser,
-                               AttributeSet attrs, Resources.Theme t)
-            throws IOException, XmlPullParserException {
-        drawable.inflate(res, parser, attrs);
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
deleted file mode 100644
index 4b6613e..0000000
--- a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Drawable which delegates all calls to its wrapped {@link Drawable}.
- * <p/>
- * Also allows backward compatible tinting via a color or {@link ColorStateList}.
- * This functionality is accessed via static methods in {@code DrawableCompat}.
- */
-
-@RequiresApi(9)
-@TargetApi(9)
-class DrawableWrapperGingerbread extends Drawable
-        implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
-
-    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
-
-    private int mCurrentColor;
-    private PorterDuff.Mode mCurrentMode;
-    private boolean mColorFilterSet;
-
-    DrawableWrapperState mState;
-    private boolean mMutated;
-
-    Drawable mDrawable;
-
-    DrawableWrapperGingerbread(@NonNull DrawableWrapperState state, @Nullable Resources res) {
-        mState = state;
-        updateLocalState(res);
-    }
-
-    /**
-     * Creates a new wrapper around the specified drawable.
-     *
-     * @param dr the drawable to wrap
-     */
-    DrawableWrapperGingerbread(@Nullable Drawable dr) {
-        mState = mutateConstantState();
-        // Now set the drawable...
-        setWrappedDrawable(dr);
-    }
-
-    /**
-     * Initializes local dynamic properties from state. This should be called
-     * after significant state changes, e.g. from the One True Constructor and
-     * after inflating or applying a theme.
-     */
-    private void updateLocalState(@Nullable Resources res) {
-        if (mState != null && mState.mDrawableState != null) {
-            final Drawable dr = newDrawableFromState(mState.mDrawableState, res);
-            setWrappedDrawable(dr);
-        }
-    }
-
-    /**
-     * Allows us to call ConstantState.newDrawable(*) is a API safe way
-     */
-    protected Drawable newDrawableFromState(@NonNull Drawable.ConstantState state,
-            @Nullable Resources res) {
-        return state.newDrawable(res);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        mDrawable.draw(canvas);
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        if (mDrawable != null) {
-            mDrawable.setBounds(bounds);
-        }
-    }
-
-    @Override
-    public void setChangingConfigurations(int configs) {
-        mDrawable.setChangingConfigurations(configs);
-    }
-
-    @Override
-    public int getChangingConfigurations() {
-        return super.getChangingConfigurations()
-                | (mState != null ? mState.getChangingConfigurations() : 0)
-                | mDrawable.getChangingConfigurations();
-    }
-
-    @Override
-    public void setDither(boolean dither) {
-        mDrawable.setDither(dither);
-    }
-
-    @Override
-    public void setFilterBitmap(boolean filter) {
-        mDrawable.setFilterBitmap(filter);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mDrawable.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        mDrawable.setColorFilter(cf);
-    }
-
-    @Override
-    public boolean isStateful() {
-        final ColorStateList tintList = (isCompatTintEnabled() && mState != null)
-                ? mState.mTint
-                : null;
-        return (tintList != null && tintList.isStateful()) || mDrawable.isStateful();
-    }
-
-    @Override
-    public boolean setState(final int[] stateSet) {
-        boolean handled = mDrawable.setState(stateSet);
-        handled = updateTint(stateSet) || handled;
-        return handled;
-    }
-
-    @Override
-    public int[] getState() {
-        return mDrawable.getState();
-    }
-
-    @Override
-    public Drawable getCurrent() {
-        return mDrawable.getCurrent();
-    }
-
-    @Override
-    public boolean setVisible(boolean visible, boolean restart) {
-        return super.setVisible(visible, restart) || mDrawable.setVisible(visible, restart);
-    }
-
-    @Override
-    public int getOpacity() {
-        return mDrawable.getOpacity();
-    }
-
-    @Override
-    public Region getTransparentRegion() {
-        return mDrawable.getTransparentRegion();
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mDrawable.getIntrinsicWidth();
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mDrawable.getIntrinsicHeight();
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        return mDrawable.getMinimumWidth();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        return mDrawable.getMinimumHeight();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        return mDrawable.getPadding(padding);
-    }
-
-    @Override
-    @Nullable
-    public ConstantState getConstantState() {
-        if (mState != null && mState.canConstantState()) {
-            mState.mChangingConfigurations = getChangingConfigurations();
-            return mState;
-        }
-        return null;
-    }
-
-    @Override
-    public Drawable mutate() {
-        if (!mMutated && super.mutate() == this) {
-            mState = mutateConstantState();
-            if (mDrawable != null) {
-                mDrawable.mutate();
-            }
-            if (mState != null) {
-                mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null;
-            }
-            mMutated = true;
-        }
-        return this;
-    }
-
-    /**
-     * Mutates the constant state and returns the new state.
-     * <p>
-     * This method should never call the super implementation; it should always
-     * mutate and return its own constant state.
-     *
-     * @return the new state
-     */
-    @NonNull
-    DrawableWrapperState mutateConstantState() {
-        return new DrawableWrapperStateBase(mState, null);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void invalidateDrawable(Drawable who) {
-        invalidateSelf();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void scheduleDrawable(Drawable who, Runnable what, long when) {
-        scheduleSelf(what, when);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void unscheduleDrawable(Drawable who, Runnable what) {
-        unscheduleSelf(what);
-    }
-
-    @Override
-    protected boolean onLevelChange(int level) {
-        return mDrawable.setLevel(level);
-    }
-
-    @Override
-    public void setTint(int tint) {
-        setTintList(ColorStateList.valueOf(tint));
-    }
-
-    @Override
-    public void setTintList(ColorStateList tint) {
-        mState.mTint = tint;
-        updateTint(getState());
-    }
-
-    @Override
-    public void setTintMode(PorterDuff.Mode tintMode) {
-        mState.mTintMode = tintMode;
-        updateTint(getState());
-    }
-
-    private boolean updateTint(int[] state) {
-        if (!isCompatTintEnabled()) {
-            // If compat tinting is not enabled, fail fast
-            return false;
-        }
-
-        final ColorStateList tintList = mState.mTint;
-        final PorterDuff.Mode tintMode = mState.mTintMode;
-
-        if (tintList != null && tintMode != null) {
-            final int color = tintList.getColorForState(state, tintList.getDefaultColor());
-            if (!mColorFilterSet || color != mCurrentColor || tintMode != mCurrentMode) {
-                setColorFilter(color, tintMode);
-                mCurrentColor = color;
-                mCurrentMode = tintMode;
-                mColorFilterSet = true;
-                return true;
-            }
-        } else {
-            mColorFilterSet = false;
-            clearColorFilter();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the wrapped {@link Drawable}
-     */
-    public final Drawable getWrappedDrawable() {
-        return mDrawable;
-    }
-
-    /**
-     * Sets the current wrapped {@link Drawable}
-     */
-    public final void setWrappedDrawable(Drawable dr) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-        }
-
-        mDrawable = dr;
-
-        if (dr != null) {
-            dr.setCallback(this);
-            // Only call setters for data that's stored in the base Drawable.
-            setVisible(dr.isVisible(), true);
-            setState(dr.getState());
-            setLevel(dr.getLevel());
-            setBounds(dr.getBounds());
-            if (mState != null) {
-                mState.mDrawableState = dr.getConstantState();
-            }
-        }
-
-        invalidateSelf();
-    }
-
-    protected boolean isCompatTintEnabled() {
-        // It's enabled by default on Gingerbread
-        return true;
-    }
-
-    protected static abstract class DrawableWrapperState extends Drawable.ConstantState {
-        int mChangingConfigurations;
-        Drawable.ConstantState mDrawableState;
-
-        ColorStateList mTint = null;
-        PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
-
-        DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
-            if (orig != null) {
-                mChangingConfigurations = orig.mChangingConfigurations;
-                mDrawableState = orig.mDrawableState;
-                mTint = orig.mTint;
-                mTintMode = orig.mTintMode;
-            }
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            return newDrawable(null);
-        }
-
-        public abstract Drawable newDrawable(@Nullable Resources res);
-
-        @Override
-        public int getChangingConfigurations() {
-            return mChangingConfigurations
-                    | (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0);
-        }
-
-        boolean canConstantState() {
-            return mDrawableState != null;
-        }
-    }
-
-    private static class DrawableWrapperStateBase extends DrawableWrapperState {
-        DrawableWrapperStateBase(
-                @Nullable DrawableWrapperState orig, @Nullable Resources res) {
-            super(orig, res);
-        }
-
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new DrawableWrapperGingerbread(this, res);
-        }
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/os/BuildCompat.java b/compat/gingerbread/android/support/v4/os/BuildCompat.java
deleted file mode 100644
index b8e9d9b..0000000
--- a/compat/gingerbread/android/support/v4/os/BuildCompat.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.os;
-
-import android.os.Build.VERSION;
-
-/**
- * BuildCompat contains additional platform version checking methods for
- * testing compatibility with new features.
- */
-public class BuildCompat {
-    private BuildCompat() {
-    }
-    /* Boilerplate for isAtLeast${PLATFORM}:
-     * public static boolean isAtLeast*() {
-     *     return !"REL".equals(VERSION.CODENAME)
-     *             && ("${PLATFORM}".equals(VERSION.CODENAME) || VERSION.CODENAME.startsWith("${PLATFORM}MR"));
-     * }
-     */
-
-    /**
-     * Check if the device is running on the Android N release or newer.
-     *
-     * @return {@code true} if N APIs are available for use
-     */
-    public static boolean isAtLeastN() {
-        return VERSION.SDK_INT >= 24;
-    }
-
-    /**
-     * Check if the device is running on the Android N MR1 release or newer.
-     *
-     * @return {@code true} if N MR1 APIs are available for use
-     */
-    public static boolean isAtLeastNMR1() {
-        return VERSION.SDK_INT >= 25;
-    }
-
-    /**
-     * Check if the device is running on the Android O release or newer.
-     *
-     * @return {@code true} if O APIs are available for use
-     */
-    public static boolean isAtLeastO() {
-        return !"REL".equals(VERSION.CODENAME)
-                && ("O".equals(VERSION.CODENAME) || VERSION.CODENAME.startsWith("OMR"));
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java b/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
deleted file mode 100644
index 5d97d04..0000000
--- a/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-
-@RequiresApi(9)
-@TargetApi(9)
-class LayoutInflaterCompatBase {
-
-    static class FactoryWrapper implements LayoutInflater.Factory {
-
-        final LayoutInflaterFactory mDelegateFactory;
-
-        FactoryWrapper(LayoutInflaterFactory delegateFactory) {
-            mDelegateFactory = delegateFactory;
-        }
-
-        @Override
-        public View onCreateView(String name, Context context, AttributeSet attrs) {
-            return mDelegateFactory.onCreateView(null, name, context, attrs);
-        }
-
-        public String toString() {
-            return getClass().getName() + "{" + mDelegateFactory + "}";
-        }
-    }
-
-    static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
-        inflater.setFactory(factory != null ? new FactoryWrapper(factory) : null);
-    }
-
-    static LayoutInflaterFactory getFactory(LayoutInflater inflater) {
-        LayoutInflater.Factory factory = inflater.getFactory();
-        if (factory instanceof FactoryWrapper) {
-            return ((FactoryWrapper) factory).mDelegateFactory;
-        }
-        return null;
-    }
-
-}
diff --git a/compat/gingerbread/android/support/v4/view/LayoutInflaterFactory.java b/compat/gingerbread/android/support/v4/view/LayoutInflaterFactory.java
deleted file mode 100644
index 02b3d63..0000000
--- a/compat/gingerbread/android/support/v4/view/LayoutInflaterFactory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Used with {@code LayoutInflaterCompat.setFactory()}. Offers the same API as
- * {@code LayoutInflater.Factory2}.
- */
-public interface LayoutInflaterFactory {
-
-    /**
-     * Hook you can supply that is called when inflating from a LayoutInflater.
-     * You can use this to customize the tag names available in your XML
-     * layout files.
-     *
-     * @param parent The parent that the created view will be placed
-     * in; <em>note that this may be null</em>.
-     * @param name Tag name to be inflated.
-     * @param context The context the view is being created in.
-     * @param attrs Inflation attributes as specified in XML file.
-     *
-     * @return View Newly created view. Return null for the default
-     *         behavior.
-     */
-    public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
-
-}
diff --git a/compat/gingerbread/android/support/v4/view/ViewCompatBase.java b/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
deleted file mode 100644
index 7c81f5e..0000000
--- a/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.support.annotation.RequiresApi;
-import android.view.Display;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.WindowManager;
-
-import java.lang.reflect.Field;
-
-@RequiresApi(9)
-@TargetApi(9)
-class ViewCompatBase {
-
-    private static final String TAG = "ViewCompatBase";
-
-    private static Field sMinWidthField;
-    private static boolean sMinWidthFieldFetched;
-    private static Field sMinHeightField;
-    private static boolean sMinHeightFieldFetched;
-
-    static ColorStateList getBackgroundTintList(View view) {
-        return (view instanceof TintableBackgroundView)
-                ? ((TintableBackgroundView) view).getSupportBackgroundTintList()
-                : null;
-    }
-
-    static void setBackgroundTintList(View view, ColorStateList tintList) {
-        if (view instanceof TintableBackgroundView) {
-            ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList);
-        }
-    }
-
-    static PorterDuff.Mode getBackgroundTintMode(View view) {
-        return (view instanceof TintableBackgroundView)
-                ? ((TintableBackgroundView) view).getSupportBackgroundTintMode()
-                : null;
-    }
-
-    static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
-        if (view instanceof TintableBackgroundView) {
-            ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode);
-        }
-    }
-
-    static boolean isLaidOut(View view) {
-        return view.getWidth() > 0 && view.getHeight() > 0;
-    }
-
-    static int getMinimumWidth(View view) {
-        if (!sMinWidthFieldFetched) {
-            try {
-                sMinWidthField = View.class.getDeclaredField("mMinWidth");
-                sMinWidthField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                // Couldn't find the field. Abort!
-            }
-            sMinWidthFieldFetched = true;
-        }
-
-        if (sMinWidthField != null) {
-            try {
-                return (int) sMinWidthField.get(view);
-            } catch (Exception e) {
-                // Field get failed. Oh well...
-            }
-        }
-
-        // We failed, return 0
-        return 0;
-    }
-
-    static int getMinimumHeight(View view) {
-        if (!sMinHeightFieldFetched) {
-            try {
-                sMinHeightField = View.class.getDeclaredField("mMinHeight");
-                sMinHeightField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                // Couldn't find the field. Abort!
-            }
-            sMinHeightFieldFetched = true;
-        }
-
-        if (sMinHeightField != null) {
-            try {
-                return (int) sMinHeightField.get(view);
-            } catch (Exception e) {
-                // Field get failed. Oh well...
-            }
-        }
-
-        // We failed, return 0
-        return 0;
-    }
-
-    static boolean isAttachedToWindow(View view) {
-        return view.getWindowToken() != null;
-    }
-
-    static void offsetTopAndBottom(View view, int offset) {
-        final int currentTop = view.getTop();
-        view.offsetTopAndBottom(offset);
-
-        if (offset != 0) {
-            // We need to manually invalidate pre-honeycomb
-            final ViewParent parent = view.getParent();
-            if (parent instanceof View) {
-                final int absOffset = Math.abs(offset);
-                ((View) parent).invalidate(
-                        view.getLeft(),
-                        currentTop - absOffset,
-                        view.getRight(),
-                        currentTop + view.getHeight() + absOffset);
-            } else {
-                view.invalidate();
-            }
-        }
-    }
-
-    static void offsetLeftAndRight(View view, int offset) {
-        final int currentLeft = view.getLeft();
-        view.offsetLeftAndRight(offset);
-
-        if (offset != 0) {
-            // We need to manually invalidate pre-honeycomb
-            final ViewParent parent = view.getParent();
-            if (parent instanceof View) {
-                final int absOffset = Math.abs(offset);
-                ((View) parent).invalidate(
-                        currentLeft - absOffset,
-                        view.getTop(),
-                        currentLeft + view.getWidth() + absOffset,
-                        view.getBottom());
-            } else {
-                view.invalidate();
-            }
-        }
-    }
-
-    static Display getDisplay(View view) {
-        if (isAttachedToWindow(view)) {
-            final WindowManager wm = (WindowManager) view.getContext().getSystemService(
-                    Context.WINDOW_SERVICE);
-            return wm.getDefaultDisplay();
-        }
-        return null;
-    }
-
-}
diff --git a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
deleted file mode 100644
index 5f3e253..0000000
--- a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.animation;
-
-import android.graphics.Path;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.animation.Interpolator;
-
-/**
- * Base implementation for path interpolator compatibility.
- */
-
-@RequiresApi(9)
-@TargetApi(9)
-class PathInterpolatorCompatBase  {
-
-    private PathInterpolatorCompatBase() {
-        // prevent instantiation
-    }
-
-    public static Interpolator create(Path path) {
-        return new PathInterpolatorGingerbread(path);
-    }
-
-    public static Interpolator create(float controlX, float controlY) {
-        return new PathInterpolatorGingerbread(controlX, controlY);
-    }
-
-    public static Interpolator create(float controlX1, float controlY1,
-            float controlX2, float controlY2) {
-        return new PathInterpolatorGingerbread(controlX1, controlY1, controlX2, controlY2);
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
deleted file mode 100644
index 4c96b97..0000000
--- a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.animation;
-
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.animation.Interpolator;
-
-/**
- * A path interpolator implementation compatible with API 9+.
- */
-
-@RequiresApi(9)
-@TargetApi(9)
-class PathInterpolatorGingerbread implements Interpolator {
-
-    /**
-     * Governs the accuracy of the approximation of the {@link Path}.
-     */
-    private static final float PRECISION = 0.002f;
-
-    private final float[] mX;
-    private final float[] mY;
-
-    public PathInterpolatorGingerbread(Path path) {
-        final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
-
-        final float pathLength = pathMeasure.getLength();
-        final int numPoints = (int) (pathLength / PRECISION) + 1;
-
-        mX = new float[numPoints];
-        mY = new float[numPoints];
-
-        final float[] position = new float[2];
-        for (int i = 0; i < numPoints; ++i) {
-            final float distance = (i * pathLength) / (numPoints - 1);
-            pathMeasure.getPosTan(distance, position, null /* tangent */);
-
-            mX[i] = position[0];
-            mY[i] = position[1];
-        }
-    }
-
-    public PathInterpolatorGingerbread(float controlX, float controlY) {
-        this(createQuad(controlX, controlY));
-    }
-
-    public PathInterpolatorGingerbread(float controlX1, float controlY1,
-            float controlX2, float controlY2) {
-        this(createCubic(controlX1, controlY1, controlX2, controlY2));
-    }
-
-    @Override
-    public float getInterpolation(float t) {
-        if (t <= 0.0f) {
-            return 0.0f;
-        } else if (t >= 1.0f) {
-            return 1.0f;
-        }
-
-        // Do a binary search for the correct x to interpolate between.
-        int startIndex = 0;
-        int endIndex = mX.length - 1;
-        while (endIndex - startIndex > 1) {
-            int midIndex = (startIndex + endIndex) / 2;
-            if (t < mX[midIndex]) {
-                endIndex = midIndex;
-            } else {
-                startIndex = midIndex;
-            }
-        }
-
-        final float xRange = mX[endIndex] - mX[startIndex];
-        if (xRange == 0) {
-            return mY[startIndex];
-        }
-
-        final float tInRange = t - mX[startIndex];
-        final float fraction = tInRange / xRange;
-
-        final float startY = mY[startIndex];
-        final float endY = mY[endIndex];
-
-        return startY + (fraction * (endY - startY));
-    }
-
-    private static Path createQuad(float controlX, float controlY) {
-        final Path path = new Path();
-        path.moveTo(0.0f, 0.0f);
-        path.quadTo(controlX, controlY, 1.0f, 1.0f);
-        return path;
-    }
-
-    private static Path createCubic(float controlX1, float controlY1,
-            float controlX2, float controlY2) {
-        final Path path = new Path();
-        path.moveTo(0.0f, 0.0f);
-        path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
-        return path;
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java b/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
deleted file mode 100644
index 0fe01f0..0000000
--- a/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Log;
-import android.widget.CompoundButton;
-
-import java.lang.reflect.Field;
-
-@RequiresApi(9)
-@TargetApi(9)
-class CompoundButtonCompatGingerbread {
-
-    private static final String TAG = "CompoundButtonCompatGingerbread";
-
-    private static Field sButtonDrawableField;
-    private static boolean sButtonDrawableFieldFetched;
-
-    static void setButtonTintList(CompoundButton button, ColorStateList tint) {
-        if (button instanceof TintableCompoundButton) {
-            ((TintableCompoundButton) button).setSupportButtonTintList(tint);
-        }
-    }
-
-    static ColorStateList getButtonTintList(CompoundButton button) {
-        if (button instanceof TintableCompoundButton) {
-             return((TintableCompoundButton) button).getSupportButtonTintList();
-        }
-        return null;
-    }
-
-    static void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
-        if (button instanceof TintableCompoundButton) {
-            ((TintableCompoundButton) button).setSupportButtonTintMode(tintMode);
-        }
-    }
-
-    static PorterDuff.Mode getButtonTintMode(CompoundButton button) {
-        if (button instanceof TintableCompoundButton) {
-            return ((TintableCompoundButton) button).getSupportButtonTintMode();
-        }
-        return null;
-    }
-
-    static Drawable getButtonDrawable(CompoundButton button) {
-        if (!sButtonDrawableFieldFetched) {
-            try {
-                sButtonDrawableField = CompoundButton.class.getDeclaredField("mButtonDrawable");
-                sButtonDrawableField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.i(TAG, "Failed to retrieve mButtonDrawable field", e);
-            }
-            sButtonDrawableFieldFetched = true;
-        }
-
-        if (sButtonDrawableField != null) {
-            try {
-                return (Drawable) sButtonDrawableField.get(button);
-            } catch (IllegalAccessException e) {
-                Log.i(TAG, "Failed to get button drawable via reflection", e);
-                sButtonDrawableField = null;
-            }
-        }
-        return null;
-    }
-
-}
diff --git a/compat/gingerbread/android/support/v4/widget/ImageViewCompatBase.java b/compat/gingerbread/android/support/v4/widget/ImageViewCompatBase.java
deleted file mode 100644
index 1963340..0000000
--- a/compat/gingerbread/android/support/v4/widget/ImageViewCompatBase.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.widget.ImageView;
-
-class ImageViewCompatBase {
-    static ColorStateList getImageTintList(ImageView view) {
-        return (view instanceof TintableImageSourceView)
-                ? ((TintableImageSourceView) view).getSupportImageTintList()
-                : null;
-    }
-
-    static void setImageTintList(ImageView view, ColorStateList tintList) {
-        if (view instanceof TintableImageSourceView) {
-            ((TintableImageSourceView) view).setSupportImageTintList(tintList);
-        }
-    }
-
-    static PorterDuff.Mode getImageTintMode(ImageView view) {
-        return (view instanceof TintableImageSourceView)
-                ? ((TintableImageSourceView) view).getSupportImageTintMode()
-                : null;
-    }
-
-    static void setImageTintMode(ImageView view, PorterDuff.Mode mode) {
-        if (view instanceof TintableImageSourceView) {
-            ((TintableImageSourceView) view).setSupportImageTintMode(mode);
-        }
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java b/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
deleted file mode 100644
index 79edf2c..0000000
--- a/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.widget.ListView;
-
-@RequiresApi(9)
-@TargetApi(9)
-class ListViewCompatGingerbread {
-    static void scrollListBy(final ListView listView, int y) {
-        final int firstPosition = listView.getFirstVisiblePosition();
-        if (firstPosition == ListView.INVALID_POSITION) {
-            return;
-        }
-
-        final View firstView = listView.getChildAt(0);
-        if (firstView == null) {
-            return;
-        }
-
-        final int newTop = firstView.getTop() - y;
-        listView.setSelectionFromTop(firstPosition, newTop);
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/widget/TintableCompoundButton.java b/compat/gingerbread/android/support/v4/widget/TintableCompoundButton.java
deleted file mode 100644
index 6bcfa30..0000000
--- a/compat/gingerbread/android/support/v4/widget/TintableCompoundButton.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-
-/**
- * Interface which allows a {@link android.widget.CompoundButton} to receive tinting
- * calls from {@code CompoundButtonCompat} when running on API v20 devices or lower.
- */
-public interface TintableCompoundButton {
-
-    /**
-     * Applies a tint to the button drawable. Does not modify the current tint
-     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
-     * <p>
-     * Subsequent calls to
-     * {@link android.widget.CompoundButton#setButtonDrawable(Drawable) setButtonDrawable(Drawable)}
-     * should automatically mutate the drawable and apply the specified tint and tint mode.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     */
-    public void setSupportButtonTintList(@Nullable ColorStateList tint);
-
-    /**
-     * Returns the tint applied to the button drawable
-     *
-     * @see #setSupportButtonTintList(ColorStateList)
-     */
-    @Nullable
-    public ColorStateList getSupportButtonTintList();
-
-    /**
-     * Specifies the blending mode which should be used to apply the tint specified by
-     * {@link #setSupportButtonTintList(ColorStateList)} to the button drawable. The
-     * default mode is {@link PorterDuff.Mode#SRC_IN}.
-     *
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @see #getSupportButtonTintMode()
-     * @see DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)
-     */
-    public void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode);
-
-    /**
-     * Returns the blending mode used to apply the tint to the button drawable
-     *
-     * @see #setSupportButtonTintMode(PorterDuff.Mode)
-     */
-    @Nullable
-    public PorterDuff.Mode getSupportButtonTintMode();
-}
diff --git a/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java b/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
deleted file mode 100644
index e5f3bbf..0000000
--- a/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2011 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.app;
-
-import android.app.Activity;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Implementation of activity compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class ActivityCompatHoneycomb {
-    static void invalidateOptionsMenu(Activity activity) {
-        activity.invalidateOptionsMenu();
-    }
-
-    static void dump(Activity activity, String prefix, FileDescriptor fd,
-            PrintWriter writer, String[] args) {
-        activity.dump(prefix, fd, writer, args);
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java b/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
deleted file mode 100644
index c75e6a2..0000000
--- a/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-
-/**
- * Interface implemented by notification compat builders that support
- * an accessor for {@link Notification.Builder}. {@link Notification.Builder}
- * was introduced in HoneyComb.
- *
- * @hide
- */
-@RequiresApi(11)
-@TargetApi(11)
-@RestrictTo(LIBRARY_GROUP)
-public interface NotificationBuilderWithBuilderAccessor {
-    public Notification.Builder getBuilder();
-    public Notification build();
-}
diff --git a/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java b/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
deleted file mode 100644
index 44ef21a..0000000
--- a/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.RemoteViews;
-
-@RequiresApi(11)
-@TargetApi(11)
-class NotificationCompatHoneycomb {
-    static Notification add(Context context, Notification n,
-            CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
-            RemoteViews tickerView, int number,
-            PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon) {
-        Notification.Builder b = new Notification.Builder(context)
-                .setWhen(n.when)
-                .setSmallIcon(n.icon, n.iconLevel)
-                .setContent(n.contentView)
-                .setTicker(n.tickerText, tickerView)
-                .setSound(n.sound, n.audioStreamType)
-                .setVibrate(n.vibrate)
-                .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
-                .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
-                .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
-                .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
-                .setDefaults(n.defaults)
-                .setContentTitle(contentTitle)
-                .setContentText(contentText)
-                .setContentInfo(contentInfo)
-                .setContentIntent(contentIntent)
-                .setDeleteIntent(n.deleteIntent)
-                .setFullScreenIntent(fullScreenIntent,
-                        (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
-                .setLargeIcon(largeIcon)
-                .setNumber(number);
-
-        return b.getNotification();
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
deleted file mode 100644
index 6b804ad..0000000
--- a/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2012 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.content;
-
-import android.content.Context;
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.io.File;
-
-/**
- * Implementation of context compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class ContextCompatHoneycomb {
-
-    static void startActivities(Context context, Intent[] intents) {
-        context.startActivities(intents);
-    }
-
-    public static File getObbDir(Context context) {
-        return context.getObbDir();
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
deleted file mode 100644
index 0dcd0d7..0000000
--- a/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content;
-
-import android.os.AsyncTask;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.util.concurrent.Executor;
-
-/**
- * Implementation of parallel executor compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class ExecutorCompatHoneycomb {
-    public static Executor getParallelExecutor() {
-        return AsyncTask.THREAD_POOL_EXECUTOR;
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
deleted file mode 100644
index 81ada48..0000000
--- a/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2012 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.content;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(11)
-@TargetApi(11)
-class IntentCompatHoneycomb {
-    public static Intent makeMainActivity(ComponentName mainActivity) {
-        return Intent.makeMainActivity(mainActivity);
-    }
-
-    public static Intent makeRestartActivityTask(ComponentName mainActivity) {
-        return Intent.makeRestartActivityTask(mainActivity);
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
deleted file mode 100644
index e19f8a8..0000000
--- a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2013 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.graphics.drawable;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of drawable compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class DrawableCompatHoneycomb {
-
-    public static void jumpToCurrentState(Drawable drawable) {
-        drawable.jumpToCurrentState();
-    }
-
-    public static Drawable wrapForTinting(Drawable drawable) {
-        if (!(drawable instanceof TintAwareDrawable)) {
-            return new DrawableWrapperHoneycomb(drawable);
-        }
-        return drawable;
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
deleted file mode 100644
index 1bd6355..0000000
--- a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(11)
-@TargetApi(11)
-class DrawableWrapperHoneycomb extends DrawableWrapperGingerbread {
-
-    DrawableWrapperHoneycomb(Drawable drawable) {
-        super(drawable);
-    }
-
-    DrawableWrapperHoneycomb(DrawableWrapperState state, Resources resources) {
-        super(state, resources);
-    }
-
-    @Override
-    public void jumpToCurrentState() {
-        mDrawable.jumpToCurrentState();
-    }
-
-    @NonNull
-    @Override
-    DrawableWrapperState mutateConstantState() {
-        return new DrawableWrapperStateHoneycomb(mState, null);
-    }
-
-    private static class DrawableWrapperStateHoneycomb extends DrawableWrapperState {
-        DrawableWrapperStateHoneycomb(@Nullable DrawableWrapperState orig,
-                @Nullable Resources res) {
-            super(orig, res);
-        }
-
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new DrawableWrapperHoneycomb(this, res);
-        }
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java b/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
deleted file mode 100644
index 1b3836e..0000000
--- a/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.os;
-
-import android.os.AsyncTask;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of AsyncTask compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class AsyncTaskCompatHoneycomb {
-
-    static <Params, Progress, Result> void executeParallel(
-            AsyncTask<Params, Progress, Result> task,
-            Params... params) {
-        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
-    }
-
-}
diff --git a/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
deleted file mode 100644
index 80425d8..0000000
--- a/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.KeyEvent;
-
-/**
- * Implementation of key event compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class KeyEventCompatHoneycomb {
-    public static int normalizeMetaState(int metaState) {
-        return KeyEvent.normalizeMetaState(metaState);
-    }
-    
-    public static boolean metaStateHasModifiers(int metaState, int modifiers) {
-        return KeyEvent.metaStateHasModifiers(metaState, modifiers);
-    }
-
-    public static boolean metaStateHasNoModifiers(int metaState) {
-        return KeyEvent.metaStateHasNoModifiers(metaState);
-    }
-
-    public static boolean isCtrlPressed(KeyEvent event) {
-        return event.isCtrlPressed();
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java b/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
deleted file mode 100644
index 7eea934..0000000
--- a/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import java.lang.reflect.Field;
-
-@RequiresApi(11)
-@TargetApi(11)
-class LayoutInflaterCompatHC {
-    private static final String TAG = "LayoutInflaterCompatHC";
-
-    private static Field sLayoutInflaterFactory2Field;
-    private static boolean sCheckedField;
-
-    static class FactoryWrapperHC extends LayoutInflaterCompatBase.FactoryWrapper
-            implements LayoutInflater.Factory2 {
-
-        FactoryWrapperHC(LayoutInflaterFactory delegateFactory) {
-            super(delegateFactory);
-        }
-
-        @Override
-        public View onCreateView(View parent, String name, Context context,
-                AttributeSet attributeSet) {
-            return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
-        }
-    }
-
-    static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
-        final LayoutInflater.Factory2 factory2 = factory != null
-                ? new FactoryWrapperHC(factory) : null;
-        inflater.setFactory2(factory2);
-
-        final LayoutInflater.Factory f = inflater.getFactory();
-        if (f instanceof LayoutInflater.Factory2) {
-            // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
-            // We will now try and force set the merged factory to mFactory2
-            forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
-        } else {
-            // Else, we will force set the original wrapped Factory2
-            forceSetFactory2(inflater, factory2);
-        }
-    }
-
-    /**
-     * For APIs >= 11 && < 21, there was a framework bug that prevented a LayoutInflater's
-     * Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater
-     * that already had a Factory2 registered. We work around that bug here. If we can't we
-     * log an error.
-     */
-    static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
-        if (!sCheckedField) {
-            try {
-                sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2");
-                sLayoutInflaterFactory2Field.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class "
-                        + LayoutInflater.class.getName()
-                        + "; inflation may have unexpected results.", e);
-            }
-            sCheckedField = true;
-        }
-        if (sLayoutInflaterFactory2Field != null) {
-            try {
-                sLayoutInflaterFactory2Field.set(inflater, factory);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater "
-                        + inflater + "; inflation may have unexpected results.", e);
-            }
-        }
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
deleted file mode 100644
index 0b267d2..0000000
--- a/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.MenuItem;
-import android.view.View;
-
-/**
- * Implementation of menu compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class MenuItemCompatHoneycomb {
-    public static void setShowAsAction(MenuItem item, int actionEnum) {
-        item.setShowAsAction(actionEnum);
-    }
-
-    public static MenuItem setActionView(MenuItem item, View view) {
-        return item.setActionView(view);
-    }
-
-    public static MenuItem setActionView(MenuItem item, int resId) {
-        return item.setActionView(resId);
-    }
-
-    public static View getActionView(MenuItem item) {
-        return item.getActionView();
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
deleted file mode 100644
index 189dc03..0000000
--- a/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.VelocityTracker;
-
-/**
- * Implementation of velocity tracker compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class VelocityTrackerCompatHoneycomb {
-    public static float getXVelocity(VelocityTracker tracker, int pointerId) {
-        return tracker.getXVelocity(pointerId);
-    }
-    public static float getYVelocity(VelocityTracker tracker, int pointerId) {
-        return tracker.getYVelocity(pointerId);
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/view/ViewCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
deleted file mode 100644
index 607175b..0000000
--- a/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2012 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.view;
-
-import android.animation.ValueAnimator;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.ViewParent;
-
-@RequiresApi(11)
-@TargetApi(11)
-class ViewCompatHC {
-    static long getFrameTime() {
-        return ValueAnimator.getFrameDelay();
-    }
-
-    public static float getAlpha(View view) {
-        return view.getAlpha();
-    }
-
-    public static void setLayerType(View view, int layerType, Paint paint) {
-        view.setLayerType(layerType, paint);
-    }
-
-    public static int getLayerType(View view) {
-        return view.getLayerType();
-    }
-
-    public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
-        return View.resolveSizeAndState(size, measureSpec, childMeasuredState);
-    }
-
-    public static int getMeasuredWidthAndState(View view) {
-        return view.getMeasuredWidthAndState();
-    }
-
-    public static int getMeasuredHeightAndState(View view) {
-        return view.getMeasuredHeightAndState();
-    }
-
-    public static int getMeasuredState(View view) {
-        return view.getMeasuredState();
-    }
-
-    public static float getTranslationX(View view) {
-        return view.getTranslationX();
-    }
-
-    public static float getTranslationY(View view) {
-        return view.getTranslationY();
-    }
-
-    public static float getX(View view) {
-        return view.getX();
-    }
-
-    public static float getY(View view) {
-        return view.getY();
-    }
-
-    public static float getRotation(View view) {
-        return view.getRotation();
-    }
-
-    public static float getRotationX(View view) {
-        return view.getRotationX();
-    }
-
-    public static float getRotationY(View view) {
-        return view.getRotationY();
-    }
-
-    public static float getScaleX(View view) {
-        return view.getScaleX();
-    }
-
-    public static float getScaleY(View view) {
-        return view.getScaleY();
-    }
-
-    public static void setTranslationX(View view, float value) {
-        view.setTranslationX(value);
-    }
-
-    public static void setTranslationY(View view, float value) {
-        view.setTranslationY(value);
-    }
-
-    public static Matrix getMatrix(View view) {
-        return view.getMatrix();
-    }
-
-    public static void setAlpha(View view, float value) {
-        view.setAlpha(value);
-    }
-
-    public static void setX(View view, float value) {
-        view.setX(value);
-    }
-
-    public static void setY(View view, float value) {
-        view.setY(value);
-    }
-
-    public static void setRotation(View view, float value) {
-        view.setRotation(value);
-    }
-
-    public static void setRotationX(View view, float value) {
-        view.setRotationX(value);
-    }
-
-    public static void setRotationY(View view, float value) {
-        view.setRotationY(value);
-    }
-
-    public static void setScaleX(View view, float value) {
-        view.setScaleX(value);
-    }
-
-    public static void setScaleY(View view, float value) {
-        view.setScaleY(value);
-    }
-
-    public static void setPivotX(View view, float value) {
-        view.setPivotX(value);
-    }
-
-    public static void setPivotY(View view, float value) {
-        view.setPivotY(value);
-    }
-
-    public static float getPivotX(View view) {
-        return view.getPivotX();
-    }
-
-    public static float getPivotY(View view) {
-        return view.getPivotY();
-    }
-
-    public static void jumpDrawablesToCurrentState(View view) {
-        view.jumpDrawablesToCurrentState();
-    }
-
-    public static void setSaveFromParentEnabled(View view, boolean enabled) {
-        view.setSaveFromParentEnabled(enabled);
-    }
-
-    public static void setActivated(View view, boolean activated) {
-        view.setActivated(activated);
-    }
-
-    public static int combineMeasuredStates(int curState, int newState) {
-        return View.combineMeasuredStates(curState, newState);
-    }
-
-    static void offsetTopAndBottom(View view, int offset) {
-        view.offsetTopAndBottom(offset);
-        if (view.getVisibility() == View.VISIBLE) {
-            tickleInvalidationFlag(view);
-
-            ViewParent parent = view.getParent();
-            if (parent instanceof View) {
-                tickleInvalidationFlag((View) parent);
-            }
-        }
-    }
-
-    static void offsetLeftAndRight(View view, int offset) {
-        view.offsetLeftAndRight(offset);
-        if (view.getVisibility() == View.VISIBLE) {
-            tickleInvalidationFlag(view);
-
-            ViewParent parent = view.getParent();
-            if (parent instanceof View) {
-                tickleInvalidationFlag((View) parent);
-            }
-        }
-    }
-
-    private static void tickleInvalidationFlag(View view) {
-        final float y = view.getTranslationY();
-        view.setTranslationY(y + 1);
-        view.setTranslationY(y);
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
deleted file mode 100644
index 3b31adf..0000000
--- a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ViewGroup;
-
-@RequiresApi(11)
-@TargetApi(11)
-class ViewGroupCompatHC {
-    private ViewGroupCompatHC() {
-    }
-
-    public static void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-        group.setMotionEventSplittingEnabled(split);
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
deleted file mode 100644
index 01867d8..0000000
--- a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2011 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.widget;
-
-import android.app.SearchManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.widget.SearchView;
-import android.widget.SearchView.OnCloseListener;
-import android.widget.SearchView.OnQueryTextListener;
-
-/**
- * Implementation of SearchView compatibility that can call Honeycomb APIs.
- */
-
-@RequiresApi(11)
-@TargetApi(11)
-class SearchViewCompatHoneycomb {
-
-    public static void checkIfLegalArg(View searchView) {
-        if (searchView == null) {
-            throw new IllegalArgumentException("searchView must be non-null");
-        }
-        if (!(searchView instanceof SearchView)) {
-            throw new IllegalArgumentException("searchView must be an instance of" +
-                    "android.widget.SearchView");
-        }
-    }
-
-    interface OnQueryTextListenerCompatBridge {
-        boolean onQueryTextSubmit(String query);
-        boolean onQueryTextChange(String newText);
-    }
-
-    interface OnCloseListenerCompatBridge {
-        boolean onClose();
-    }
-
-    public static View newSearchView(Context context) {
-        return new SearchView(context);
-    }
-
-    public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
-        SearchView sv = ((SearchView) searchView);
-        SearchManager searchManager = (SearchManager)
-                sv.getContext().getSystemService(Context.SEARCH_SERVICE);
-        sv.setSearchableInfo(searchManager.getSearchableInfo(searchableComponent));
-    }
-
-    public static Object newOnQueryTextListener(final OnQueryTextListenerCompatBridge listener) {
-        return new OnQueryTextListener() {
-            @Override
-            public boolean onQueryTextSubmit(String query) {
-                return listener.onQueryTextSubmit(query);
-            }
-
-            @Override
-            public boolean onQueryTextChange(String newText) {
-                return listener.onQueryTextChange(newText);
-            }
-        };
-    }
-
-    public static void setOnQueryTextListener(View searchView, Object listener) {
-        ((SearchView) searchView).setOnQueryTextListener((OnQueryTextListener) listener);
-    }
-
-    public static Object newOnCloseListener(final OnCloseListenerCompatBridge listener) {
-        return new OnCloseListener() {
-            @Override
-            public boolean onClose() {
-                return listener.onClose();
-            }
-        };
-    }
-
-    public static void setOnCloseListener(View searchView, Object listener) {
-        ((SearchView) searchView).setOnCloseListener((OnCloseListener) listener);
-    }
-
-    public static CharSequence getQuery(View searchView) {
-        return ((SearchView) searchView).getQuery();
-    }
-
-    public static void setQuery(View searchView, CharSequence query, boolean submit) {
-        ((SearchView) searchView).setQuery(query, submit);
-    }
-
-    public static void setQueryHint(View searchView, CharSequence hint) {
-        ((SearchView) searchView).setQueryHint(hint);
-    }
-
-    public static void setIconified(View searchView, boolean iconify) {
-        ((SearchView) searchView).setIconified(iconify);
-    }
-
-    public static boolean isIconified(View searchView) {
-        return ((SearchView) searchView).isIconified();
-    }
-
-    public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
-        ((SearchView) searchView).setSubmitButtonEnabled(enabled);
-    }
-
-    public static boolean isSubmitButtonEnabled(View searchView) {
-        return ((SearchView) searchView).isSubmitButtonEnabled();
-    }
-
-    public static void setQueryRefinementEnabled(View searchView, boolean enable) {
-        ((SearchView) searchView).setQueryRefinementEnabled(enable);
-    }
-
-    public static boolean isQueryRefinementEnabled(View searchView) {
-        return ((SearchView) searchView).isQueryRefinementEnabled();
-    }
-
-    public static void setMaxWidth(View searchView, int maxpixels) {
-        ((SearchView) searchView).setMaxWidth(maxpixels);
-    }
-}
diff --git a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java b/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
deleted file mode 100644
index 9aaae0b..0000000
--- a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-/**
- * Uses framework Animators to provide ValueAnimatorCompat interface.
- * <p/>
- * This is not a fully implemented API which is why it is not public.
- */
-
-@RequiresApi(12)
-@TargetApi(12)
-class HoneycombMr1AnimatorCompatProvider implements AnimatorProvider {
-
-    private TimeInterpolator mDefaultInterpolator;
-
-    @Override
-    public ValueAnimatorCompat emptyValueAnimator() {
-        return new HoneycombValueAnimatorCompat(ValueAnimator.ofFloat(0f, 1f));
-    }
-
-    static class HoneycombValueAnimatorCompat implements ValueAnimatorCompat {
-
-        final Animator mWrapped;
-
-        public HoneycombValueAnimatorCompat(Animator wrapped) {
-            mWrapped = wrapped;
-        }
-
-        @Override
-        public void setTarget(View view) {
-            mWrapped.setTarget(view);
-        }
-
-        @Override
-        public void addListener(AnimatorListenerCompat listener) {
-            mWrapped.addListener(new AnimatorListenerCompatWrapper(listener, this));
-        }
-
-        @Override
-        public void setDuration(long duration) {
-            mWrapped.setDuration(duration);
-        }
-
-        @Override
-        public void start() {
-            mWrapped.start();
-        }
-
-        @Override
-        public void cancel() {
-            mWrapped.cancel();
-        }
-
-        @Override
-        public void addUpdateListener(final AnimatorUpdateListenerCompat animatorUpdateListener) {
-            if (mWrapped instanceof ValueAnimator) {
-                ((ValueAnimator) mWrapped).addUpdateListener(
-                        new ValueAnimator.AnimatorUpdateListener() {
-                            @Override
-                            public void onAnimationUpdate(ValueAnimator animation) {
-                                animatorUpdateListener
-                                        .onAnimationUpdate(HoneycombValueAnimatorCompat.this);
-                            }
-                        });
-            }
-        }
-
-        @Override
-        public float getAnimatedFraction() {
-            return ((ValueAnimator) mWrapped).getAnimatedFraction();
-        }
-    }
-
-    static class AnimatorListenerCompatWrapper implements Animator.AnimatorListener {
-
-        final AnimatorListenerCompat mWrapped;
-
-        final ValueAnimatorCompat mValueAnimatorCompat;
-
-        public AnimatorListenerCompatWrapper(
-                AnimatorListenerCompat wrapped, ValueAnimatorCompat valueAnimatorCompat) {
-            mWrapped = wrapped;
-            mValueAnimatorCompat = valueAnimatorCompat;
-        }
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            mWrapped.onAnimationStart(mValueAnimatorCompat);
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mWrapped.onAnimationEnd(mValueAnimatorCompat);
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mWrapped.onAnimationCancel(mValueAnimatorCompat);
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-            mWrapped.onAnimationRepeat(mValueAnimatorCompat);
-        }
-    }
-
-    @Override
-    public void clearInterpolator(View view) {
-        if (mDefaultInterpolator == null) {
-            mDefaultInterpolator = new ValueAnimator().getInterpolator();
-        }
-        view.animate().setInterpolator(mDefaultInterpolator);
-    }
-}
diff --git a/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java b/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
deleted file mode 100644
index 4266460..0000000
--- a/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.graphics;
-
-import android.graphics.Bitmap;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of BitmapCompat that can use Honeycomb MR1 APIs.
- */
-
-@RequiresApi(12)
-@TargetApi(12)
-class BitmapCompatHoneycombMr1 {
-
-    static int getAllocationByteCount(Bitmap bitmap) {
-        return bitmap.getByteCount();
-    }
-
-}
diff --git a/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java b/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
deleted file mode 100644
index f14e77d..0000000
--- a/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.MotionEvent;
-
-/**
- * Motion event compatibility class for API 12+.
- */
-
-@RequiresApi(12)
-@TargetApi(12)
-class MotionEventCompatHoneycombMr1 {
-    static float getAxisValue(MotionEvent event, int axis) {
-        return event.getAxisValue(axis);
-    }
-
-    static float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
-        return event.getAxisValue(axis, pointerIndex);
-    }
-}
diff --git a/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java b/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
deleted file mode 100644
index aa3aaef..0000000
--- a/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content.res;
-
-import android.content.res.Resources;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(13)
-@TargetApi(13)
-class ConfigurationHelperHoneycombMr2 {
-
-    static int getScreenHeightDp(@NonNull Resources resources) {
-        return resources.getConfiguration().screenHeightDp;
-    }
-
-    static int getScreenWidthDp(@NonNull Resources resources) {
-        return resources.getConfiguration().screenWidthDp;
-    }
-
-    static int getSmallestScreenWidthDp(@NonNull Resources resources) {
-        return resources.getConfiguration().smallestScreenWidthDp;
-    }
-}
diff --git a/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java b/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
deleted file mode 100644
index a631941..0000000
--- a/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
-
-/**
- * Implementation of ConnectivityManagerCompat that can use Honeycomb MR2 APIs.
- */
-
-@RequiresApi(13)
-@TargetApi(13)
-class ConnectivityManagerCompatHoneycombMR2 {
-    public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
-        final NetworkInfo info = cm.getActiveNetworkInfo();
-        if (info == null) {
-            // err on side of caution
-            return true;
-        }
-
-        final int type = info.getType();
-        switch (type) {
-            case TYPE_MOBILE:
-            case TYPE_MOBILE_DUN:
-            case TYPE_MOBILE_HIPRI:
-            case TYPE_MOBILE_MMS:
-            case TYPE_MOBILE_SUPL:
-            case TYPE_WIMAX:
-                return true;
-            case TYPE_WIFI:
-            case TYPE_BLUETOOTH:
-            case TYPE_ETHERNET:
-                return false;
-            default:
-                // err on side of caution
-                return true;
-        }
-    }
-}
diff --git a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatCreatorCallbacks.java b/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
deleted file mode 100644
index a577d6f..0000000
--- a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 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.os;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Callbacks a {@link Parcelable} creator should implement.
- */
-public interface ParcelableCompatCreatorCallbacks<T> {
-
-    /**
-     * Create a new instance of the Parcelable class, instantiating it
-     * from the given Parcel whose data had previously been written by
-     * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
-     * using the given ClassLoader.
-     *
-     * @param in The Parcel to read the object's data from.
-     * @param loader The ClassLoader that this object is being created in.
-     * @return Returns a new instance of the Parcelable class.
-     */
-    public T createFromParcel(Parcel in, ClassLoader loader);
-
-    /**
-     * Create a new array of the Parcelable class.
-     *
-     * @param size Size of the array.
-     * @return Returns an array of the Parcelable class, with every entry
-     *         initialized to null.
-     */
-    public T[] newArray(int size);
-}
diff --git a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java b/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
deleted file mode 100644
index fe754c4..0000000
--- a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2011 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.os;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(13)
-@TargetApi(13)
-class ParcelableCompatCreatorHoneycombMR2Stub {
-    static <T> Parcelable.Creator<T> instantiate(ParcelableCompatCreatorCallbacks<T> callbacks) {
-        return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);
-    }
-}
-
-@RequiresApi(13)
-@TargetApi(13)
-class ParcelableCompatCreatorHoneycombMR2<T> implements Parcelable.ClassLoaderCreator<T> {
-    private final ParcelableCompatCreatorCallbacks<T> mCallbacks;
-
-    public ParcelableCompatCreatorHoneycombMR2(ParcelableCompatCreatorCallbacks<T> callbacks) {
-        mCallbacks = callbacks;
-    }
-
-    public T createFromParcel(Parcel in) {
-        return mCallbacks.createFromParcel(in, null);
-    }
-
-    public T createFromParcel(Parcel in, ClassLoader loader) {
-        return mCallbacks.createFromParcel(in, loader);
-    }
-
-    public T[] newArray(int size) {
-        return mCallbacks.newArray(size);
-    }
-}
diff --git a/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
deleted file mode 100644
index be17cd6..0000000
--- a/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 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.content;
-
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(15)
-@TargetApi(15)
-class IntentCompatIcsMr1 {
-
-    public static Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) {
-        return Intent.makeMainSelectorActivity(selectorAction, selectorCategory);
-    }
-
-}
diff --git a/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
deleted file mode 100644
index be229b5..0000000
--- a/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content.res;
-
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(15)
-@TargetApi(15)
-class ResourcesCompatIcsMr1 {
-    public static Drawable getDrawableForDensity(Resources res, int id, int density)
-            throws NotFoundException {
-        return res.getDrawableForDensity(id, density);
-    }
-}
diff --git a/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java b/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
deleted file mode 100644
index 3cf4e5e..0000000
--- a/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-/**
- * Helper for accessing newer features in View introduced in ICS Mr1.
- */
-
-@RequiresApi(15)
-@TargetApi(15)
-class ViewCompatICSMr1 {
-    public static boolean hasOnClickListeners(View v) {
-        return v.hasOnClickListeners();
-    }
-}
diff --git a/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
deleted file mode 100644
index f249bdd..0000000
--- a/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 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.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityRecord;
-
-/**
- * ICS MR1 specific AccessibilityRecord API implementation.
- */
-
-@RequiresApi(15)
-@TargetApi(15)
-class AccessibilityRecordCompatIcsMr1 {
-
-    public static int getMaxScrollX(Object record) {
-        return ((AccessibilityRecord) record).getMaxScrollX();
-    }
-
-    public static int getMaxScrollY(Object record) {
-        return ((AccessibilityRecord) record).getMaxScrollY();
-    }
-    public static void setMaxScrollX(Object record, int maxScrollX) {
-        ((AccessibilityRecord) record).setMaxScrollX(maxScrollX);
-    }
-
-    public static void setMaxScrollY(Object record, int maxScrollY) {
-        ((AccessibilityRecord) record).setMaxScrollY(maxScrollY);
-    }
-}
diff --git a/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java b/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
deleted file mode 100644
index 21e797d..0000000
--- a/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2011 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.accessibilityservice;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.pm.ResolveInfo;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * ICS implementation of the new APIs in AccessibilityServiceInfo.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class AccessibilityServiceInfoCompatIcs {
-
-    public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
-        return info.getCanRetrieveWindowContent();
-    }
-
-    public static String getDescription(AccessibilityServiceInfo info) {
-        return info.getDescription();
-    }
-
-    public static String getId(AccessibilityServiceInfo info) {
-        return info.getId();
-    }
-
-    public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
-        return info.getResolveInfo();
-    }
-
-    public static String getSettingsActivityName(AccessibilityServiceInfo info) {
-        return info.getSettingsActivityName();
-    }
-}
diff --git a/compat/ics/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java b/compat/ics/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
new file mode 100644
index 0000000..253af12
--- /dev/null
+++ b/compat/ics/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.Notification;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Interface implemented by notification compat builders that support
+ * an accessor for {@link Notification.Builder}. {@link Notification.Builder}
+ * was introduced in HoneyComb.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public interface NotificationBuilderWithBuilderAccessor {
+    Notification.Builder getBuilder();
+    Notification build();
+}
diff --git a/compat/ics/android/support/v4/app/NotificationCompatBase.java b/compat/ics/android/support/v4/app/NotificationCompatBase.java
new file mode 100644
index 0000000..7067dee
--- /dev/null
+++ b/compat/ics/android/support/v4/app/NotificationCompatBase.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.PendingIntent;
+import android.os.Bundle;
+import android.support.annotation.RestrictTo;
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class NotificationCompatBase {
+    public abstract static class Action {
+        public abstract int getIcon();
+        public abstract CharSequence getTitle();
+        public abstract PendingIntent getActionIntent();
+        public abstract Bundle getExtras();
+        public abstract RemoteInputCompatBase.RemoteInput[] getRemoteInputs();
+        /** Returns RemoteInputs that ONLY accept data results, not text. */
+        public abstract RemoteInputCompatBase.RemoteInput[] getDataOnlyRemoteInputs();
+        public abstract boolean getAllowGeneratedReplies();
+
+        public interface Factory {
+            Action build(int icon, CharSequence title, PendingIntent actionIntent,
+                    Bundle extras, RemoteInputCompatBase.RemoteInput[] remoteInputs,
+                    RemoteInputCompatBase.RemoteInput[] dataOnlyRemoteInputs,
+                    boolean allowGeneratedReplies);
+            Action[] newArray(int length);
+        }
+    }
+
+    public abstract static class UnreadConversation {
+        abstract String[] getParticipants();
+        abstract String getParticipant();
+        abstract String[] getMessages();
+        abstract RemoteInputCompatBase.RemoteInput getRemoteInput();
+        abstract PendingIntent getReplyPendingIntent();
+        abstract PendingIntent getReadPendingIntent();
+        abstract long getLatestTimestamp();
+
+        public interface Factory {
+            UnreadConversation build(String[] messages,
+                    RemoteInputCompatBase.RemoteInput remoteInput,
+                    PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
+                    String[] participants, long latestTimestamp);
+        }
+    }
+}
diff --git a/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java b/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
deleted file mode 100644
index d2e0e44..0000000
--- a/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.RemoteViews;
-
-@RequiresApi(14)
-@TargetApi(14)
-class NotificationCompatIceCreamSandwich {
-
-    public static class Builder implements NotificationBuilderWithBuilderAccessor {
-
-        private Notification.Builder b;
-
-        public Builder(Context context, Notification n, CharSequence contentTitle,
-                CharSequence contentText, CharSequence contentInfo, RemoteViews tickerView,
-                int number, PendingIntent contentIntent, PendingIntent fullScreenIntent,
-                Bitmap largeIcon, int progressMax, int progress, boolean progressIndeterminate) {
-            b = new Notification.Builder(context)
-                    .setWhen(n.when)
-                    .setSmallIcon(n.icon, n.iconLevel)
-                    .setContent(n.contentView)
-                    .setTicker(n.tickerText, tickerView)
-                    .setSound(n.sound, n.audioStreamType)
-                    .setVibrate(n.vibrate)
-                    .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
-                    .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
-                    .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
-                    .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
-                    .setDefaults(n.defaults)
-                    .setContentTitle(contentTitle)
-                    .setContentText(contentText)
-                    .setContentInfo(contentInfo)
-                    .setContentIntent(contentIntent)
-                    .setDeleteIntent(n.deleteIntent)
-                    .setFullScreenIntent(fullScreenIntent,
-                            (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
-                    .setLargeIcon(largeIcon)
-                    .setNumber(number)
-                    .setProgress(progressMax, progress, progressIndeterminate);
-        }
-
-        @Override
-        public Notification.Builder getBuilder() {
-            return b;
-        }
-
-        @Override
-        public Notification build() {
-            return b.getNotification();
-        }
-    }
-}
diff --git a/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java b/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
deleted file mode 100644
index 4fcf2b1..0000000
--- a/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.Service;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(14)
-@TargetApi(14)
-class NotificationManagerCompatIceCreamSandwich {
-    static final int SIDE_CHANNEL_BIND_FLAGS = Service.BIND_AUTO_CREATE
-            | Service.BIND_WAIVE_PRIORITY;
-}
diff --git a/compat/ics/android/support/v4/app/RemoteInputCompatBase.java b/compat/ics/android/support/v4/app/RemoteInputCompatBase.java
new file mode 100644
index 0000000..09c3429
--- /dev/null
+++ b/compat/ics/android/support/v4/app/RemoteInputCompatBase.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @deprecated This class was not meant to be made public.
+ */
+@Deprecated
+class RemoteInputCompatBase {
+
+    public abstract static class RemoteInput {
+        protected abstract String getResultKey();
+        protected abstract CharSequence getLabel();
+        protected abstract CharSequence[] getChoices();
+        protected abstract boolean getAllowFreeFormInput();
+        protected abstract Bundle getExtras();
+        protected abstract Set<String> getAllowedDataTypes();
+
+        public interface Factory {
+            RemoteInput build(String resultKey, CharSequence label,
+                    CharSequence[] choices, boolean allowFreeFormInput, Bundle extras,
+                    Set<String> allowedDataTypes);
+            RemoteInput[] newArray(int length);
+        }
+    }
+}
diff --git a/compat/ics/android/support/v4/app/ShareCompatICS.java b/compat/ics/android/support/v4/app/ShareCompatICS.java
deleted file mode 100644
index a6d1e92..0000000
--- a/compat/ics/android/support/v4/app/ShareCompatICS.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 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.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ActionProvider;
-import android.view.MenuItem;
-import android.widget.ShareActionProvider;
-
-@RequiresApi(14)
-@TargetApi(14)
-class ShareCompatICS {
-    private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
-
-    public static void configureMenuItem(MenuItem item, Activity callingActivity, Intent intent) {
-        ActionProvider itemProvider = item.getActionProvider();
-        ShareActionProvider provider = null;
-        if (!(itemProvider instanceof ShareActionProvider)) {
-            provider = new ShareActionProvider(callingActivity);
-        } else {
-            provider = (ShareActionProvider) itemProvider;
-        }
-        provider.setShareHistoryFileName(HISTORY_FILENAME_PREFIX +
-                callingActivity.getClass().getName());
-        provider.setShareIntent(intent);
-        item.setActionProvider(provider);
-    }
-}
diff --git a/compat/ics/android/support/v4/graphics/PaintCompatApi14.java b/compat/ics/android/support/v4/graphics/PaintCompatApi14.java
new file mode 100644
index 0000000..7a7de7c
--- /dev/null
+++ b/compat/ics/android/support/v4/graphics/PaintCompatApi14.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.v4.util.Pair;
+
+class PaintCompatApi14 {
+    // U+DFFFD which is very end of unassigned plane.
+    private static final String TOFU_STRING = "\uDB3F\uDFFD";
+    private static final String EM_STRING = "m";
+
+    private static final ThreadLocal<Pair<Rect, Rect>> sRectThreadLocal = new ThreadLocal<>();
+
+    static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
+        final int length = string.length();
+
+        if (length == 1 && Character.isWhitespace(string.charAt(0))) {
+            // measureText + getTextBounds skips whitespace so we need to special case it here
+            return true;
+        }
+
+        final float missingGlyphWidth = paint.measureText(TOFU_STRING);
+        final float emGlyphWidth = paint.measureText(EM_STRING);
+
+        final float width = paint.measureText(string);
+
+        if (width == 0f) {
+            // If the string width is 0, it can't be rendered
+            return false;
+        }
+
+        if (string.codePointCount(0, string.length()) > 1) {
+            // Heuristic to detect fallback glyphs for ligatures like flags and ZWJ sequences
+            // Return false if string is rendered too widely
+            if (width > 2 * emGlyphWidth) {
+                return false;
+            }
+
+            // Heuristic to detect fallback glyphs for ligatures like flags and ZWJ sequences (2).
+            // If width is greater than or equal to the sum of width of each code point, it is very
+            // likely that the system is using fallback fonts to draw {@code string} in two or more
+            // glyphs instead of a single ligature glyph. (hasGlyph returns false in this case.)
+            // False detections are possible (the ligature glyph may happen to have the same width
+            // as the sum width), but there are no good way to avoid them.
+            // NOTE: This heuristic does not work with proportional glyphs.
+            // NOTE: This heuristic does not work when a ZWJ sequence is partially combined.
+            // E.g. If system has a glyph for "A ZWJ B" and not for "A ZWJ B ZWJ C", this heuristic
+            // returns true for "A ZWJ B ZWJ C".
+            float sumWidth = 0;
+            int i = 0;
+            while (i < length) {
+                int charCount = Character.charCount(string.codePointAt(i));
+                sumWidth += paint.measureText(string, i, i + charCount);
+                i += charCount;
+            }
+            if (width >= sumWidth) {
+                return false;
+            }
+        }
+
+        if (width != missingGlyphWidth) {
+            // If the widths are different then its not tofu
+            return true;
+        }
+
+        // If the widths are the same, lets check the bounds. The chance of them being
+        // different chars with the same bounds is extremely small
+        final Pair<Rect, Rect> rects = obtainEmptyRects();
+        paint.getTextBounds(TOFU_STRING, 0, TOFU_STRING.length(), rects.first);
+        paint.getTextBounds(string, 0, length, rects.second);
+        return !rects.first.equals(rects.second);
+    }
+
+    private static Pair<Rect, Rect> obtainEmptyRects() {
+        Pair<Rect, Rect> rects = sRectThreadLocal.get();
+        if (rects == null) {
+            rects = new Pair<>(new Rect(), new Rect());
+            sRectThreadLocal.set(rects);
+        } else {
+            rects.first.setEmpty();
+            rects.second.setEmpty();
+        }
+        return rects;
+    }
+}
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapper.java b/compat/ics/android/support/v4/graphics/drawable/DrawableWrapper.java
similarity index 100%
rename from compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapper.java
rename to compat/ics/android/support/v4/graphics/drawable/DrawableWrapper.java
diff --git a/compat/ics/android/support/v4/graphics/drawable/DrawableWrapperApi14.java b/compat/ics/android/support/v4/graphics/drawable/DrawableWrapperApi14.java
new file mode 100644
index 0000000..5b1bbc7
--- /dev/null
+++ b/compat/ics/android/support/v4/graphics/drawable/DrawableWrapperApi14.java
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics.drawable;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+
+/**
+ * Drawable which delegates all calls to its wrapped {@link Drawable}.
+ * <p/>
+ * Also allows backward compatible tinting via a color or {@link ColorStateList}.
+ * This functionality is accessed via static methods in {@code DrawableCompat}.
+ */
+
+@RequiresApi(14)
+class DrawableWrapperApi14 extends Drawable
+        implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
+
+    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
+
+    private int mCurrentColor;
+    private PorterDuff.Mode mCurrentMode;
+    private boolean mColorFilterSet;
+
+    DrawableWrapperState mState;
+    private boolean mMutated;
+
+    Drawable mDrawable;
+
+    DrawableWrapperApi14(@NonNull DrawableWrapperState state, @Nullable Resources res) {
+        mState = state;
+        updateLocalState(res);
+    }
+
+    /**
+     * Creates a new wrapper around the specified drawable.
+     *
+     * @param dr the drawable to wrap
+     */
+    DrawableWrapperApi14(@Nullable Drawable dr) {
+        mState = mutateConstantState();
+        // Now set the drawable...
+        setWrappedDrawable(dr);
+    }
+
+    /**
+     * Initializes local dynamic properties from state. This should be called
+     * after significant state changes, e.g. from the One True Constructor and
+     * after inflating or applying a theme.
+     */
+    private void updateLocalState(@Nullable Resources res) {
+        if (mState != null && mState.mDrawableState != null) {
+            final Drawable dr = newDrawableFromState(mState.mDrawableState, res);
+            setWrappedDrawable(dr);
+        }
+    }
+
+    /**
+     * Allows us to call ConstantState.newDrawable(*) is a API safe way
+     */
+    protected Drawable newDrawableFromState(@NonNull Drawable.ConstantState state,
+            @Nullable Resources res) {
+        return state.newDrawable(res);
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        mDrawable.jumpToCurrentState();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        mDrawable.draw(canvas);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (mDrawable != null) {
+            mDrawable.setBounds(bounds);
+        }
+    }
+
+    @Override
+    public void setChangingConfigurations(int configs) {
+        mDrawable.setChangingConfigurations(configs);
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return super.getChangingConfigurations()
+                | (mState != null ? mState.getChangingConfigurations() : 0)
+                | mDrawable.getChangingConfigurations();
+    }
+
+    @Override
+    public void setDither(boolean dither) {
+        mDrawable.setDither(dither);
+    }
+
+    @Override
+    public void setFilterBitmap(boolean filter) {
+        mDrawable.setFilterBitmap(filter);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mDrawable.setAlpha(alpha);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mDrawable.setColorFilter(cf);
+    }
+
+    @Override
+    public boolean isStateful() {
+        final ColorStateList tintList = (isCompatTintEnabled() && mState != null)
+                ? mState.mTint
+                : null;
+        return (tintList != null && tintList.isStateful()) || mDrawable.isStateful();
+    }
+
+    @Override
+    public boolean setState(final int[] stateSet) {
+        boolean handled = mDrawable.setState(stateSet);
+        handled = updateTint(stateSet) || handled;
+        return handled;
+    }
+
+    @Override
+    public int[] getState() {
+        return mDrawable.getState();
+    }
+
+    @Override
+    public Drawable getCurrent() {
+        return mDrawable.getCurrent();
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        return super.setVisible(visible, restart) || mDrawable.setVisible(visible, restart);
+    }
+
+    @Override
+    public int getOpacity() {
+        return mDrawable.getOpacity();
+    }
+
+    @Override
+    public Region getTransparentRegion() {
+        return mDrawable.getTransparentRegion();
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mDrawable.getIntrinsicWidth();
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mDrawable.getIntrinsicHeight();
+    }
+
+    @Override
+    public int getMinimumWidth() {
+        return mDrawable.getMinimumWidth();
+    }
+
+    @Override
+    public int getMinimumHeight() {
+        return mDrawable.getMinimumHeight();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        return mDrawable.getPadding(padding);
+    }
+
+    @Override
+    @Nullable
+    public ConstantState getConstantState() {
+        if (mState != null && mState.canConstantState()) {
+            mState.mChangingConfigurations = getChangingConfigurations();
+            return mState;
+        }
+        return null;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mState = mutateConstantState();
+            if (mDrawable != null) {
+                mDrawable.mutate();
+            }
+            if (mState != null) {
+                mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null;
+            }
+            mMutated = true;
+        }
+        return this;
+    }
+
+    /**
+     * Mutates the constant state and returns the new state.
+     * <p>
+     * This method should never call the super implementation; it should always
+     * mutate and return its own constant state.
+     *
+     * @return the new state
+     */
+    @NonNull
+    DrawableWrapperState mutateConstantState() {
+        return new DrawableWrapperStateBase(mState, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invalidateDrawable(Drawable who) {
+        invalidateSelf();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void unscheduleDrawable(Drawable who, Runnable what) {
+        unscheduleSelf(what);
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        return mDrawable.setLevel(level);
+    }
+
+    @Override
+    public void setTint(int tint) {
+        setTintList(ColorStateList.valueOf(tint));
+    }
+
+    @Override
+    public void setTintList(ColorStateList tint) {
+        mState.mTint = tint;
+        updateTint(getState());
+    }
+
+    @Override
+    public void setTintMode(PorterDuff.Mode tintMode) {
+        mState.mTintMode = tintMode;
+        updateTint(getState());
+    }
+
+    private boolean updateTint(int[] state) {
+        if (!isCompatTintEnabled()) {
+            // If compat tinting is not enabled, fail fast
+            return false;
+        }
+
+        final ColorStateList tintList = mState.mTint;
+        final PorterDuff.Mode tintMode = mState.mTintMode;
+
+        if (tintList != null && tintMode != null) {
+            final int color = tintList.getColorForState(state, tintList.getDefaultColor());
+            if (!mColorFilterSet || color != mCurrentColor || tintMode != mCurrentMode) {
+                setColorFilter(color, tintMode);
+                mCurrentColor = color;
+                mCurrentMode = tintMode;
+                mColorFilterSet = true;
+                return true;
+            }
+        } else {
+            mColorFilterSet = false;
+            clearColorFilter();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the wrapped {@link Drawable}
+     */
+    @Override
+    public final Drawable getWrappedDrawable() {
+        return mDrawable;
+    }
+
+    /**
+     * Sets the current wrapped {@link Drawable}
+     */
+    @Override
+    public final void setWrappedDrawable(Drawable dr) {
+        if (mDrawable != null) {
+            mDrawable.setCallback(null);
+        }
+
+        mDrawable = dr;
+
+        if (dr != null) {
+            dr.setCallback(this);
+            // Only call setters for data that's stored in the base Drawable.
+            setVisible(dr.isVisible(), true);
+            setState(dr.getState());
+            setLevel(dr.getLevel());
+            setBounds(dr.getBounds());
+            if (mState != null) {
+                mState.mDrawableState = dr.getConstantState();
+            }
+        }
+
+        invalidateSelf();
+    }
+
+    protected boolean isCompatTintEnabled() {
+        // It's enabled by default on Gingerbread
+        return true;
+    }
+
+    protected abstract static class DrawableWrapperState extends Drawable.ConstantState {
+        int mChangingConfigurations;
+        Drawable.ConstantState mDrawableState;
+
+        ColorStateList mTint = null;
+        PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
+
+        DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
+            if (orig != null) {
+                mChangingConfigurations = orig.mChangingConfigurations;
+                mDrawableState = orig.mDrawableState;
+                mTint = orig.mTint;
+                mTintMode = orig.mTintMode;
+            }
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return newDrawable(null);
+        }
+
+        @Override
+        public abstract Drawable newDrawable(@Nullable Resources res);
+
+        @Override
+        public int getChangingConfigurations() {
+            return mChangingConfigurations
+                    | (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0);
+        }
+
+        boolean canConstantState() {
+            return mDrawableState != null;
+        }
+    }
+
+    private static class DrawableWrapperStateBase extends DrawableWrapperState {
+        DrawableWrapperStateBase(
+                @Nullable DrawableWrapperState orig, @Nullable Resources res) {
+            super(orig, res);
+        }
+
+        @Override
+        public Drawable newDrawable(@Nullable Resources res) {
+            return new DrawableWrapperApi14(this, res);
+        }
+    }
+}
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/TintAwareDrawable.java b/compat/ics/android/support/v4/graphics/drawable/TintAwareDrawable.java
similarity index 100%
rename from compat/gingerbread/android/support/v4/graphics/drawable/TintAwareDrawable.java
rename to compat/ics/android/support/v4/graphics/drawable/TintAwareDrawable.java
diff --git a/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java b/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
deleted file mode 100644
index 724c34e..0000000
--- a/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import android.net.TrafficStats;
-import android.os.ParcelFileDescriptor;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.net.DatagramSocket;
-import java.net.Socket;
-import java.net.SocketException;
-
-/**
- * Implementation of TrafficStatsCompat that can call ICS APIs.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class TrafficStatsCompatIcs {
-    public static void clearThreadStatsTag() {
-        TrafficStats.clearThreadStatsTag();
-    }
-
-    public static int getThreadStatsTag() {
-        return TrafficStats.getThreadStatsTag();
-    }
-
-    public static void incrementOperationCount(int operationCount) {
-        TrafficStats.incrementOperationCount(operationCount);
-    }
-
-    public static void incrementOperationCount(int tag, int operationCount) {
-        TrafficStats.incrementOperationCount(tag, operationCount);
-    }
-
-    public static void setThreadStatsTag(int tag) {
-        TrafficStats.setThreadStatsTag(tag);
-    }
-
-    public static void tagSocket(Socket socket) throws SocketException {
-        TrafficStats.tagSocket(socket);
-    }
-
-    public static void untagSocket(Socket socket) throws SocketException {
-        TrafficStats.untagSocket(socket);
-    }
-
-    public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
-        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
-        TrafficStats.tagSocket(new DatagramSocketWrapper(socket, pfd.getFileDescriptor()));
-        // The developer is still using the FD, so we need to detach it to
-        // prevent the PFD finalizer from closing it in their face. We had to
-        // wait until after the tagging call above, since detaching clears out
-        // the getFileDescriptor() result which tagging depends on.
-        pfd.detachFd();
-    }
-
-    public static void untagDatagramSocket(DatagramSocket socket) throws SocketException {
-        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
-        TrafficStats.untagSocket(new DatagramSocketWrapper(socket, pfd.getFileDescriptor()));
-        // The developer is still using the FD, so we need to detach it to
-        // prevent the PFD finalizer from closing it in their face. We had to
-        // wait until after the tagging call above, since detaching clears out
-        // the getFileDescriptor() result which tagging depends on.
-        pfd.detachFd();
-    }
-}
diff --git a/compat/ics/android/support/v4/text/ICUCompatIcs.java b/compat/ics/android/support/v4/text/ICUCompatIcs.java
index 4baafd6..cd773b5 100644
--- a/compat/ics/android/support/v4/text/ICUCompatIcs.java
+++ b/compat/ics/android/support/v4/text/ICUCompatIcs.java
@@ -17,7 +17,6 @@
 package android.support.v4.text;
 
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.util.Log;
 
 import java.lang.reflect.InvocationTargetException;
@@ -25,7 +24,6 @@
 import java.util.Locale;
 
 @RequiresApi(14)
-@TargetApi(14)
 class ICUCompatIcs {
 
     private static final String TAG = "ICUCompatIcs";
diff --git a/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java b/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
deleted file mode 100644
index fee33d7..0000000
--- a/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-/**
- * ICS specific AccessibilityDelegate API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class AccessibilityDelegateCompatIcs {
-
-    public interface AccessibilityDelegateBridge {
-        public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event);
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event);
-        public void onInitializeAccessibilityNodeInfo(View host, Object info);
-        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event);
-        public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-            AccessibilityEvent event);
-        public void sendAccessibilityEvent(View host, int eventType);
-        public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event);
-    }
-
-    public static Object newAccessibilityDelegateDefaultImpl() {
-        return new AccessibilityDelegate();
-    }
-
-    public static Object newAccessibilityDelegateBridge(final AccessibilityDelegateBridge bridge) {
-        return new AccessibilityDelegate() {
-            @Override
-            public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-                return bridge.dispatchPopulateAccessibilityEvent(host, event);
-            }
-
-            @Override
-            public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-                bridge.onInitializeAccessibilityEvent(host, event);
-            }
-
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                bridge.onInitializeAccessibilityNodeInfo(host, info);
-            }
-
-            @Override
-            public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-                bridge.onPopulateAccessibilityEvent(host, event);
-            }
-
-            @Override
-            public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-                    AccessibilityEvent event) {
-                return bridge.onRequestSendAccessibilityEvent(host, child, event);
-            }
-
-            @Override
-            public void sendAccessibilityEvent(View host, int eventType) {
-                bridge.sendAccessibilityEvent(host, eventType);
-            }
-
-            @Override
-            public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-                bridge.sendAccessibilityEventUnchecked(host, event);
-            }
-        };
-    }
-
-    public static boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
-            AccessibilityEvent event) {
-        return ((AccessibilityDelegate) delegate).dispatchPopulateAccessibilityEvent(host, event);
-    }
-
-    public static void onInitializeAccessibilityEvent(Object delegate, View host,
-            AccessibilityEvent event) {
-        ((AccessibilityDelegate) delegate).onInitializeAccessibilityEvent(host, event);
-    }
-
-    public static void onInitializeAccessibilityNodeInfo(Object delegate, View host, Object info) {
-        ((AccessibilityDelegate) delegate).onInitializeAccessibilityNodeInfo(host,
-                (AccessibilityNodeInfo) info);
-    }
-
-    public static void onPopulateAccessibilityEvent(Object delegate, View host,
-            AccessibilityEvent event) {
-        ((AccessibilityDelegate) delegate).onPopulateAccessibilityEvent(host, event);
-    }
-
-    public static boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host,
-            View child, AccessibilityEvent event) {
-        return ((AccessibilityDelegate) delegate).onRequestSendAccessibilityEvent(host, child,
-                event);
-    }
-
-    public static void sendAccessibilityEvent(Object delegate, View host, int eventType) {
-        ((AccessibilityDelegate) delegate).sendAccessibilityEvent(host, eventType);
-    }
-
-    public static void sendAccessibilityEventUnchecked(Object delegate, View host,
-            AccessibilityEvent event) {
-        ((AccessibilityDelegate) delegate).sendAccessibilityEventUnchecked(host, event);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/MenuItemCompatIcs.java b/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
deleted file mode 100644
index 4dbea9a..0000000
--- a/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.MenuItem;
-
-@RequiresApi(14)
-@TargetApi(14)
-class MenuItemCompatIcs {
-    public static boolean expandActionView(MenuItem item) {
-        return item.expandActionView();
-    }
-
-    public static boolean collapseActionView(MenuItem item) {
-        return item.collapseActionView();
-    }
-
-    public static boolean isActionViewExpanded(MenuItem item) {
-        return item.isActionViewExpanded();
-    }
-
-    public static MenuItem setOnActionExpandListener(MenuItem item,
-            SupportActionExpandProxy listener) {
-        return item.setOnActionExpandListener(new OnActionExpandListenerWrapper(listener));
-    }
-
-    /**
-     * Work around the support lib's build dependency chain. The actual API-lib
-     * depends on -ics, but -ics doesn't depend on the API-lib so it doesn't know
-     * that MenuItemCompat.OnActionExpandListener exists.
-     */
-    interface SupportActionExpandProxy {
-        boolean onMenuItemActionExpand(MenuItem item);
-        boolean onMenuItemActionCollapse(MenuItem item);
-    }
-
-    // support => framework
-    static class OnActionExpandListenerWrapper implements MenuItem.OnActionExpandListener {
-        private SupportActionExpandProxy mWrapped;
-
-        public OnActionExpandListenerWrapper(SupportActionExpandProxy wrapped) {
-            mWrapped = wrapped;
-        }
-
-        @Override
-        public boolean onMenuItemActionExpand(MenuItem item) {
-            return mWrapped.onMenuItemActionExpand(item);
-        }
-
-        @Override
-        public boolean onMenuItemActionCollapse(MenuItem item) {
-            return mWrapped.onMenuItemActionCollapse(item);
-        }
-    }
-}
diff --git a/compat/ics/android/support/v4/view/MotionEventCompatICS.java b/compat/ics/android/support/v4/view/MotionEventCompatICS.java
deleted file mode 100644
index e8f9d49..0000000
--- a/compat/ics/android/support/v4/view/MotionEventCompatICS.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.MotionEvent;
-
-@RequiresApi(14)
-@TargetApi(14)
-class MotionEventCompatICS {
-    public static int getButtonState(MotionEvent event) {
-        return event.getButtonState();
-    }
-}
diff --git a/compat/ics/android/support/v4/view/ViewCompatICS.java b/compat/ics/android/support/v4/view/ViewCompatICS.java
deleted file mode 100644
index 338b009..0000000
--- a/compat/ics/android/support/v4/view/ViewCompatICS.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-/**
- * Helper for accessing newer features in View introduced in ICS.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewCompatICS {
-
-    public static boolean canScrollHorizontally(View v, int direction) {
-        return v.canScrollHorizontally(direction);
-    }
-
-    public static boolean canScrollVertically(View v, int direction) {
-        return v.canScrollVertically(direction);
-    }
-
-    public static void setAccessibilityDelegate(View v, @Nullable Object delegate) {
-        v.setAccessibilityDelegate((AccessibilityDelegate) delegate);
-    }
-
-    public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
-        v.onPopulateAccessibilityEvent(event);
-    }
-
-    public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
-        v.onInitializeAccessibilityEvent(event);
-    }
-
-    public static void onInitializeAccessibilityNodeInfo(View v, Object info) {
-        v.onInitializeAccessibilityNodeInfo((AccessibilityNodeInfo) info);
-    }
-
-    public static void setFitsSystemWindows(View view, boolean fitSystemWindows) {
-        view.setFitsSystemWindows(fitSystemWindows);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java b/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
deleted file mode 100644
index 19a7174..0000000
--- a/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ViewConfiguration;
-
-/**
- * Implementation of menu compatibility that can call ICS APIs.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewConfigurationCompatICS {
-    static boolean hasPermanentMenuKey(ViewConfiguration config) {
-        return config.hasPermanentMenuKey();
-    }
-}
diff --git a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java b/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
deleted file mode 100644
index bb03e7d..0000000
--- a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * ICS specific ViewGroup API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewGroupCompatIcs {
-    public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
-            AccessibilityEvent event) {
-        return group.onRequestSendAccessibilityEvent(child, event);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/ViewParentCompatICS.java b/compat/ics/android/support/v4/view/ViewParentCompatICS.java
deleted file mode 100644
index 693aa40..0000000
--- a/compat/ics/android/support/v4/view/ViewParentCompatICS.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * ICS-specific ViewParent API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewParentCompatICS {
-    public static boolean requestSendAccessibilityEvent(
-            ViewParent parent, View child, AccessibilityEvent event) {
-        return parent.requestSendAccessibilityEvent(child, event);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java b/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
deleted file mode 100644
index 9cc5583..0000000
--- a/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.view;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewPropertyAnimatorCompatICS {
-
-    public static void setDuration(View view, long value) {
-        view.animate().setDuration(value);
-    }
-
-    public static void alpha(View view, float value) {
-        view.animate().alpha(value);
-    }
-
-    public static void translationX(View view, float value) {
-        view.animate().translationX(value);
-    }
-
-    public static void translationY(View view, float value) {
-        view.animate().translationY(value);
-    }
-
-    public static long getDuration(View view) {
-        return view.animate().getDuration();
-    }
-
-    public static void setInterpolator(View view, Interpolator value) {
-        view.animate().setInterpolator(value);
-    }
-
-    public static void setStartDelay(View view, long value) {
-        view.animate().setStartDelay(value);
-    }
-
-    public static long getStartDelay(View view) {
-        return view.animate().getStartDelay();
-    }
-
-    public static void alphaBy(View view, float value) {
-        view.animate().alphaBy(value);
-    }
-
-    public static void rotation(View view, float value) {
-        view.animate().rotation(value);
-    }
-
-    public static void rotationBy(View view, float value) {
-        view.animate().rotationBy(value);
-    }
-
-    public static void rotationX(View view, float value) {
-        view.animate().rotationX(value);
-    }
-
-    public static void rotationXBy(View view, float value) {
-        view.animate().rotationXBy(value);
-    }
-
-    public static void rotationY(View view, float value) {
-        view.animate().rotationY(value);
-    }
-
-    public static void rotationYBy(View view, float value) {
-        view.animate().rotationYBy(value);
-    }
-
-    public static void scaleX(View view, float value) {
-        view.animate().scaleX(value);
-    }
-
-    public static void scaleXBy(View view, float value) {
-        view.animate().scaleXBy(value);
-    }
-
-    public static void scaleY(View view, float value) {
-        view.animate().scaleY(value);
-    }
-
-    public static void scaleYBy(View view, float value) {
-        view.animate().scaleYBy(value);
-    }
-
-    public static void cancel(View view) {
-        view.animate().cancel();
-    }
-
-    public static void x(View view, float value) {
-        view.animate().x(value);
-    }
-
-    public static void xBy(View view, float value) {
-        view.animate().xBy(value);
-    }
-
-    public static void y(View view, float value) {
-        view.animate().y(value);
-    }
-
-    public static void yBy(View view, float value) {
-        view.animate().yBy(value);
-    }
-
-    public static void translationXBy(View view, float value) {
-        view.animate().translationXBy(value);
-    }
-
-    public static void translationYBy(View view, float value) {
-        view.animate().translationYBy(value);
-    }
-
-    public static void start(View view) {
-        view.animate().start();
-    }
-
-    public static void setListener(final View view,
-            final ViewPropertyAnimatorListener listener) {
-        if (listener != null) {
-            view.animate().setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    listener.onAnimationCancel(view);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    listener.onAnimationEnd(view);
-                }
-
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    listener.onAnimationStart(view);
-                }
-            });
-        } else {
-            view.animate().setListener(null);
-        }
-    }
-}
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
deleted file mode 100644
index 0d5196f..0000000
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 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.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityRecord;
-
-/**
- * ICS specific AccessibilityEvent API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class AccessibilityEventCompatIcs {
-
-    public static int getRecordCount(AccessibilityEvent event) {
-        return event.getRecordCount();
-    }
-
-    public static void appendRecord(AccessibilityEvent event, Object record) {
-        event.appendRecord((AccessibilityRecord) record);
-    }
-
-    public static Object getRecord(AccessibilityEvent event, int index) {
-        return event.getRecord(index);
-    }
-
-    public static void setScrollable(AccessibilityEvent event, boolean scrollable) {
-        event.setScrollable(scrollable);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
deleted file mode 100644
index 4af6aa3..0000000
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2011 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.view.accessibility;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
-
-import java.util.List;
-
-/**
- * ICS specific AccessibilityManager API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class AccessibilityManagerCompatIcs {
-
-    public static class AccessibilityStateChangeListenerWrapper
-            implements AccessibilityStateChangeListener {
-        Object mListener;
-        AccessibilityStateChangeListenerBridge mListenerBridge;
-
-        public AccessibilityStateChangeListenerWrapper(Object listener,
-                AccessibilityStateChangeListenerBridge listenerBridge) {
-            mListener = listener;
-            mListenerBridge = listenerBridge;
-        }
-
-        @Override
-        public int hashCode() {
-            return mListener == null ? 0 : mListener.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            AccessibilityStateChangeListenerWrapper other =
-                    (AccessibilityStateChangeListenerWrapper) o;
-            return mListener == null ? other.mListener == null : mListener.equals(other.mListener);
-        }
-
-        @Override
-        public void onAccessibilityStateChanged(boolean enabled) {
-            mListenerBridge.onAccessibilityStateChanged(enabled);
-        }
-    }
-
-    interface AccessibilityStateChangeListenerBridge {
-        void onAccessibilityStateChanged(boolean enabled);
-    }
-
-    public static boolean addAccessibilityStateChangeListener(AccessibilityManager manager,
-            AccessibilityStateChangeListenerWrapper listener) {
-        return manager.addAccessibilityStateChangeListener(listener);
-    }
-
-    public static boolean removeAccessibilityStateChangeListener(AccessibilityManager manager,
-            AccessibilityStateChangeListenerWrapper listener) {
-        return manager.removeAccessibilityStateChangeListener(listener);
-    }
-
-    public static List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
-            AccessibilityManager manager,int feedbackTypeFlags) {
-        return manager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
-    }
-
-    public static List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
-            AccessibilityManager manager) {
-        return manager.getInstalledAccessibilityServiceList();
-    }
-
-    public static boolean isTouchExplorationEnabled(AccessibilityManager manager) {
-        return manager.isTouchExplorationEnabled();
-    }
-}
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
deleted file mode 100644
index 51faa89..0000000
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2011 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.view.accessibility;
-
-import android.annotation.TargetApi;
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import java.util.List;
-
-/**
- * ICS specific AccessibilityNodeInfo API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class AccessibilityNodeInfoCompatIcs {
-    public static Object obtain() {
-        return AccessibilityNodeInfo.obtain();
-    }
-
-    public static Object obtain(View source) {
-        return AccessibilityNodeInfo.obtain(source);
-    }
-
-    public static Object obtain(Object info) {
-        return AccessibilityNodeInfo.obtain((AccessibilityNodeInfo) info);
-    }
-
-    public static void addAction(Object info, int action) {
-        ((AccessibilityNodeInfo) info).addAction(action);
-    }
-
-    public static void addChild(Object info, View child) {
-        ((AccessibilityNodeInfo) info).addChild(child);
-    }
-
-    @SuppressWarnings("unchecked")
-    public static List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
-        Object result = ((AccessibilityNodeInfo) info).findAccessibilityNodeInfosByText(text);
-        return (List<Object>) result;
-    }
-
-    public static int getActions(Object info) {
-        return ((AccessibilityNodeInfo) info).getActions();
-    }
-
-    public static void getBoundsInParent(Object info, Rect outBounds) {
-        ((AccessibilityNodeInfo) info).getBoundsInParent(outBounds);
-    }
-
-    public static void getBoundsInScreen(Object info, Rect outBounds) {
-        ((AccessibilityNodeInfo) info).getBoundsInScreen(outBounds);
-    }
-
-    public static Object getChild(Object info, int index) {
-        return ((AccessibilityNodeInfo) info).getChild(index);
-    }
-
-    public static int getChildCount(Object info) {
-        return ((AccessibilityNodeInfo) info).getChildCount();
-    }
-
-    public static CharSequence getClassName(Object info) {
-        return ((AccessibilityNodeInfo) info).getClassName();
-    }
-
-    public static CharSequence getContentDescription(Object info) {
-        return ((AccessibilityNodeInfo) info).getContentDescription();
-    }
-
-    public static CharSequence getPackageName(Object info) {
-        return ((AccessibilityNodeInfo) info).getPackageName();
-    }
-
-    public static Object getParent(Object info) {
-        return ((AccessibilityNodeInfo) info).getParent();
-    }
-
-    public static CharSequence getText(Object info) {
-        return ((AccessibilityNodeInfo) info).getText();
-    }
-
-    public static int getWindowId(Object info) {
-        return ((AccessibilityNodeInfo) info).getWindowId();
-    }
-
-    public static boolean isCheckable(Object info) {
-        return ((AccessibilityNodeInfo) info).isCheckable();
-    }
-
-    public static boolean isChecked(Object info) {
-        return ((AccessibilityNodeInfo) info).isChecked();
-    }
-
-    public static boolean isClickable(Object info) {
-        return ((AccessibilityNodeInfo) info).isClickable();
-    }
-
-    public static boolean isEnabled(Object info) {
-        return ((AccessibilityNodeInfo) info).isEnabled();
-    }
-
-    public static boolean isFocusable(Object info) {
-        return ((AccessibilityNodeInfo) info).isFocusable();
-    }
-
-    public static boolean isFocused(Object info) {
-        return ((AccessibilityNodeInfo) info).isFocused();
-    }
-
-    public static boolean isLongClickable(Object info) {
-        return ((AccessibilityNodeInfo) info).isLongClickable();
-    }
-
-    public static boolean isPassword(Object info) {
-        return ((AccessibilityNodeInfo) info).isPassword();
-    }
-
-    public static boolean isScrollable(Object info) {
-        return ((AccessibilityNodeInfo) info).isScrollable();
-    }
-
-    public static boolean isSelected(Object info) {
-        return ((AccessibilityNodeInfo) info).isSelected();
-    }
-
-    public static boolean performAction(Object info, int action) {
-        return ((AccessibilityNodeInfo) info).performAction(action);
-    }
-
-    public static void setBoundsInParent(Object info, Rect bounds) {
-        ((AccessibilityNodeInfo) info).setBoundsInParent(bounds);
-    }
-
-    public static void setBoundsInScreen(Object info, Rect bounds) {
-        ((AccessibilityNodeInfo) info).setBoundsInScreen(bounds);
-    }
-
-    public static void setCheckable(Object info, boolean checkable) {
-        ((AccessibilityNodeInfo) info).setCheckable(checkable);
-    }
-
-    public static void setChecked(Object info, boolean checked) {
-        ((AccessibilityNodeInfo) info).setChecked(checked);
-    }
-
-    public static void setClassName(Object info, CharSequence className) {
-        ((AccessibilityNodeInfo) info).setClassName(className);
-    }
-
-    public static void setClickable(Object info, boolean clickable) {
-        ((AccessibilityNodeInfo) info).setClickable(clickable);
-    }
-
-    public static void setContentDescription(Object info, CharSequence contentDescription) {
-        ((AccessibilityNodeInfo) info).setContentDescription(contentDescription);
-    }
-
-    public static void setEnabled(Object info, boolean enabled) {
-        ((AccessibilityNodeInfo) info).setEnabled(enabled);
-    }
-
-    public static void setFocusable(Object info, boolean focusable) {
-        ((AccessibilityNodeInfo) info).setFocusable(focusable);
-    }
-
-    public static void setFocused(Object info, boolean focused) {
-        ((AccessibilityNodeInfo) info).setFocused(focused);
-    }
-
-    public static void setLongClickable(Object info, boolean longClickable) {
-        ((AccessibilityNodeInfo) info).setLongClickable(longClickable);
-    }
-
-    public static void setPackageName(Object info, CharSequence packageName) {
-        ((AccessibilityNodeInfo) info).setPackageName(packageName);
-    }
-
-    public static void setParent(Object info, View parent) {
-        ((AccessibilityNodeInfo) info).setParent(parent);
-    }
-
-    public static void setPassword(Object info, boolean password) {
-        ((AccessibilityNodeInfo) info).setPassword(password);
-    }
-
-    public static void setScrollable(Object info, boolean scrollable) {
-        ((AccessibilityNodeInfo) info).setScrollable(scrollable);
-    }
-
-    public static void setSelected(Object info, boolean selected) {
-        ((AccessibilityNodeInfo) info).setSelected(selected);
-    }
-
-    public static void setSource(Object info, View source) {
-        ((AccessibilityNodeInfo) info).setSource(source);
-    }
-
-    public static void setText(Object info, CharSequence text) {
-        ((AccessibilityNodeInfo) info).setText(text);
-    }
-
-    public static void recycle(Object info) {
-        ((AccessibilityNodeInfo) info).recycle();
-    }
-
-}
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
deleted file mode 100644
index f6e078f..0000000
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2011 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.view.accessibility;
-
-import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityRecord;
-
-import java.util.List;
-
-/**
- * ICS specific AccessibilityRecord API implementation.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class AccessibilityRecordCompatIcs {
-
-    public static Object obtain() {
-        return AccessibilityRecord.obtain();
-    }
-
-    public static Object obtain(Object record) {
-        return AccessibilityRecord.obtain((AccessibilityRecord) record);
-    }
-
-    public static int getAddedCount(Object record) {
-        return ((AccessibilityRecord) record).getAddedCount();
-    }
-
-    public static CharSequence getBeforeText(Object record) {
-        return ((AccessibilityRecord) record).getBeforeText();
-    }
-
-    public static CharSequence getClassName(Object record) {
-        return ((AccessibilityRecord) record).getClassName();
-    }
-
-    public static CharSequence getContentDescription(Object record) {
-        return ((AccessibilityRecord) record).getContentDescription();
-    }
-
-    public static int getCurrentItemIndex(Object record) {
-        return ((AccessibilityRecord) record).getCurrentItemIndex();
-    }
-
-    public static int getFromIndex(Object record) {
-        return ((AccessibilityRecord) record).getFromIndex();
-    }
-
-    public static int getItemCount(Object record) {
-        return ((AccessibilityRecord) record).getItemCount();
-    }
-
-    public static Parcelable getParcelableData(Object record) {
-        return ((AccessibilityRecord) record).getParcelableData();
-    }
-
-    public static int getRemovedCount(Object record) {
-        return ((AccessibilityRecord) record).getRemovedCount();
-    }
-
-    public static int getScrollX(Object record) {
-        return ((AccessibilityRecord) record).getScrollX();
-    }
-
-    public static int getScrollY(Object record) {
-        return ((AccessibilityRecord) record).getScrollY();
-    }
-
-    public static Object getSource(Object record) {
-        return ((AccessibilityRecord) record).getSource();
-    }
-
-    public static List<CharSequence> getText(Object record) {
-        return ((AccessibilityRecord) record).getText();
-    }
-
-    public static int getToIndex(Object record) {
-        return ((AccessibilityRecord) record).getToIndex();
-    }
-
-    public static int getWindowId(Object record) {
-        return ((AccessibilityRecord) record).getWindowId();
-    }
-
-    public static boolean isChecked(Object record) {
-        return ((AccessibilityRecord) record).isChecked();
-    }
-
-    public static boolean isEnabled(Object record) {
-        return ((AccessibilityRecord) record).isEnabled();
-    }
-
-    public static boolean isFullScreen(Object record) {
-        return ((AccessibilityRecord) record).isFullScreen();
-    }
-
-    public static boolean isPassword(Object record) {
-        return ((AccessibilityRecord) record).isPassword();
-    }
-
-    public static boolean isScrollable(Object record) {
-        return ((AccessibilityRecord) record).isScrollable();
-    }
-
-    public static void recycle(Object record) {
-        ((AccessibilityRecord) record).recycle();
-    }
-
-    public static void setAddedCount(Object record, int addedCount) {
-        ((AccessibilityRecord) record).setAddedCount(addedCount);
-    }
-
-    public static void setBeforeText(Object record, CharSequence beforeText) {
-        ((AccessibilityRecord) record).setBeforeText(beforeText);
-    }
-
-    public static void setChecked(Object record, boolean isChecked) {
-        ((AccessibilityRecord) record).setChecked(isChecked);
-    }
-
-    public static void setClassName(Object record, CharSequence className) {
-        ((AccessibilityRecord) record).setClassName(className);
-    }
-
-    public static void setContentDescription(Object record, CharSequence contentDescription) {
-        ((AccessibilityRecord) record).setContentDescription(contentDescription);
-    }
-
-    public static void setCurrentItemIndex(Object record, int currentItemIndex) {
-        ((AccessibilityRecord) record).setCurrentItemIndex(currentItemIndex);
-    }
-
-    public static void setEnabled(Object record, boolean isEnabled) {
-        ((AccessibilityRecord) record).setEnabled(isEnabled);
-    }
-
-    public static void setFromIndex(Object record, int fromIndex) {
-        ((AccessibilityRecord) record).setFromIndex(fromIndex);
-    }
-
-    public static void setFullScreen(Object record, boolean isFullScreen) {
-        ((AccessibilityRecord) record).setFullScreen(isFullScreen);
-    }
-
-    public static void setItemCount(Object record, int itemCount) {
-        ((AccessibilityRecord) record).setItemCount(itemCount);
-    }
-
-    public static void setParcelableData(Object record, Parcelable parcelableData) {
-        ((AccessibilityRecord) record).setParcelableData(parcelableData);
-    }
-
-    public static void setPassword(Object record, boolean isPassword) {
-        ((AccessibilityRecord) record).setPassword(isPassword);
-    }
-
-    public static void setRemovedCount(Object record, int removedCount) {
-        ((AccessibilityRecord) record).setRemovedCount(removedCount);
-    }
-
-    public static void setScrollX(Object record, int scrollX) {
-        ((AccessibilityRecord) record).setScrollX(scrollX);
-    }
-
-    public static void setScrollY(Object record, int scrollY) {
-        ((AccessibilityRecord) record).setScrollY(scrollY);
-    }
-
-    public static void setScrollable(Object record, boolean scrollable) {
-        ((AccessibilityRecord) record).setScrollable(scrollable);
-    }
-
-    public static void setSource(Object record, View source) {
-        ((AccessibilityRecord) record).setSource(source);
-    }
-
-    public static void setToIndex(Object record, int toIndex) {
-        ((AccessibilityRecord) record).setToIndex(toIndex);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/animation/PathInterpolatorApi14.java b/compat/ics/android/support/v4/view/animation/PathInterpolatorApi14.java
new file mode 100644
index 0000000..4416a79
--- /dev/null
+++ b/compat/ics/android/support/v4/view/animation/PathInterpolatorApi14.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.view.animation;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.view.animation.Interpolator;
+
+/**
+ * A path interpolator implementation compatible with API 14+.
+ */
+class PathInterpolatorApi14 implements Interpolator {
+
+    /**
+     * Governs the accuracy of the approximation of the {@link Path}.
+     */
+    private static final float PRECISION = 0.002f;
+
+    private final float[] mX;
+    private final float[] mY;
+
+    PathInterpolatorApi14(Path path) {
+        final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
+
+        final float pathLength = pathMeasure.getLength();
+        final int numPoints = (int) (pathLength / PRECISION) + 1;
+
+        mX = new float[numPoints];
+        mY = new float[numPoints];
+
+        final float[] position = new float[2];
+        for (int i = 0; i < numPoints; ++i) {
+            final float distance = (i * pathLength) / (numPoints - 1);
+            pathMeasure.getPosTan(distance, position, null /* tangent */);
+
+            mX[i] = position[0];
+            mY[i] = position[1];
+        }
+    }
+
+    PathInterpolatorApi14(float controlX, float controlY) {
+        this(createQuad(controlX, controlY));
+    }
+
+    PathInterpolatorApi14(float controlX1, float controlY1,
+            float controlX2, float controlY2) {
+        this(createCubic(controlX1, controlY1, controlX2, controlY2));
+    }
+
+    @Override
+    public float getInterpolation(float t) {
+        if (t <= 0.0f) {
+            return 0.0f;
+        } else if (t >= 1.0f) {
+            return 1.0f;
+        }
+
+        // Do a binary search for the correct x to interpolate between.
+        int startIndex = 0;
+        int endIndex = mX.length - 1;
+        while (endIndex - startIndex > 1) {
+            int midIndex = (startIndex + endIndex) / 2;
+            if (t < mX[midIndex]) {
+                endIndex = midIndex;
+            } else {
+                startIndex = midIndex;
+            }
+        }
+
+        final float xRange = mX[endIndex] - mX[startIndex];
+        if (xRange == 0) {
+            return mY[startIndex];
+        }
+
+        final float tInRange = t - mX[startIndex];
+        final float fraction = tInRange / xRange;
+
+        final float startY = mY[startIndex];
+        final float endY = mY[endIndex];
+
+        return startY + (fraction * (endY - startY));
+    }
+
+    private static Path createQuad(float controlX, float controlY) {
+        final Path path = new Path();
+        path.moveTo(0.0f, 0.0f);
+        path.quadTo(controlX, controlY, 1.0f, 1.0f);
+        return path;
+    }
+
+    private static Path createCubic(float controlX1, float controlY1,
+            float controlX2, float controlY2) {
+        final Path path = new Path();
+        path.moveTo(0.0f, 0.0f);
+        path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
+        return path;
+    }
+}
diff --git a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java b/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
deleted file mode 100644
index 1f75b4a..0000000
--- a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2011 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.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.EdgeEffect;
-
-/**
- * Stub implementation that contains a real EdgeEffect on ICS.
- * <p/>
- * This class is an implementation detail for EdgeEffectCompat
- * and should not be used directly.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class EdgeEffectCompatIcs {
-    public static Object newEdgeEffect(Context context) {
-        return new EdgeEffect(context);
-    }
-
-    public static void setSize(Object edgeEffect, int width, int height) {
-        ((EdgeEffect) edgeEffect).setSize(width, height);
-    }
-
-    public static boolean isFinished(Object edgeEffect) {
-        return ((EdgeEffect) edgeEffect).isFinished();
-    }
-
-    public static void finish(Object edgeEffect) {
-        ((EdgeEffect) edgeEffect).finish();
-    }
-
-    public static boolean onPull(Object edgeEffect, float deltaDistance) {
-        ((EdgeEffect) edgeEffect).onPull(deltaDistance);
-        return true;
-    }
-
-    public static boolean onRelease(Object edgeEffect) {
-        EdgeEffect eff = (EdgeEffect) edgeEffect;
-        eff.onRelease();
-        return eff.isFinished();
-    }
-
-    public static boolean onAbsorb(Object edgeEffect, int velocity) {
-        ((EdgeEffect) edgeEffect).onAbsorb(velocity);
-        return true;
-    }
-
-    public static boolean draw(Object edgeEffect, Canvas canvas) {
-        return ((EdgeEffect) edgeEffect).draw(canvas);
-    }
-}
\ No newline at end of file
diff --git a/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java b/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
deleted file mode 100644
index be7a07e..0000000
--- a/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2012 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.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.OverScroller;
-
-/**
- * ICS API access for ScrollerCompat
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class ScrollerCompatIcs {
-    public static float getCurrVelocity(Object scroller) {
-        return ((OverScroller) scroller).getCurrVelocity();
-    }
-}
diff --git a/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java b/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
deleted file mode 100644
index 3938081..0000000
--- a/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 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.widget;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.widget.SearchView;
-
-/**
- * Implementation of SearchView compatibility that can call ICS APIs.
- */
-
-@RequiresApi(14)
-@TargetApi(14)
-class SearchViewCompatIcs {
-
-    public static class MySearchView extends SearchView {
-        public MySearchView(Context context) {
-            super(context);
-        }
-
-        // The normal SearchView doesn't clear its search text when
-        // collapsed, so we will do this for it.
-        @Override
-        public void onActionViewCollapsed() {
-            setQuery("", false);
-            super.onActionViewCollapsed();
-        }
-    }
-
-    public static View newSearchView(Context context) {
-        return new MySearchView(context);
-    }
-
-    public static void setImeOptions(View searchView, int imeOptions) {
-        ((SearchView) searchView).setImeOptions(imeOptions);
-    }
-
-    public static void setInputType(View searchView, int inputType) {
-        ((SearchView) searchView).setInputType(inputType);
-    }
-}
diff --git a/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java b/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
index 9655197..3c17fda 100644
--- a/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
+++ b/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
@@ -21,128 +21,57 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
 /**
- * Helper for accessing features in {@link android.accessibilityservice.AccessibilityService}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.accessibilityservice.AccessibilityService}.
  */
 public final class AccessibilityServiceInfoCompat {
 
-    static interface AccessibilityServiceInfoVersionImpl {
-        public String getId(AccessibilityServiceInfo info);
-        public ResolveInfo getResolveInfo(AccessibilityServiceInfo info);
-        public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info);
-        public String getDescription(AccessibilityServiceInfo info);
-        public String getSettingsActivityName(AccessibilityServiceInfo info);
-        public int getCapabilities(AccessibilityServiceInfo info);
-        public String loadDescription(AccessibilityServiceInfo info, PackageManager pm);
-    }
-
-    static class AccessibilityServiceInfoStubImpl implements AccessibilityServiceInfoVersionImpl {
-
-        @Override
-        public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
-            return false;
-        }
-
-        @Override
-        public String getDescription(AccessibilityServiceInfo info) {
-            return null;
-        }
-
-        @Override
-        public String getId(AccessibilityServiceInfo info) {
-            return null;
-        }
-
-        @Override
-        public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
-            return null;
-        }
-
-        @Override
-        public String getSettingsActivityName(AccessibilityServiceInfo info) {
-            return null;
-        }
-
-        @Override
-        public int getCapabilities(AccessibilityServiceInfo info) {
-            return 0;
-        }
-
-        @Override
-        public String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
-            return null;
-        }
-    }
-
-    static class AccessibilityServiceInfoIcsImpl extends AccessibilityServiceInfoStubImpl {
-
-        @Override
-        public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
-            return AccessibilityServiceInfoCompatIcs.getCanRetrieveWindowContent(info);
-        }
-
-        @Override
-        public String getDescription(AccessibilityServiceInfo info) {
-            return AccessibilityServiceInfoCompatIcs.getDescription(info);
-        }
-
-        @Override
-        public String getId(AccessibilityServiceInfo info) {
-            return AccessibilityServiceInfoCompatIcs.getId(info);
-        }
-
-        @Override
-        public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
-            return AccessibilityServiceInfoCompatIcs.getResolveInfo(info);
-        }
-
-        @Override
-        public String getSettingsActivityName(AccessibilityServiceInfo info) {
-            return AccessibilityServiceInfoCompatIcs.getSettingsActivityName(info);
-        }
-
-        @Override
+    static class AccessibilityServiceInfoBaseImpl {
         public int getCapabilities(AccessibilityServiceInfo info) {
             if (getCanRetrieveWindowContent(info)) {
                 return CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
             }
             return 0;
         }
-    }
 
-    static class AccessibilityServiceInfoJellyBeanImpl extends AccessibilityServiceInfoIcsImpl {
-        @Override
         public String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
-            return AccessibilityServiceInfoCompatJellyBean.loadDescription(info, pm);
+            return null;
         }
     }
 
-    static class AccessibilityServiceInfoJellyBeanMr2Impl
-            extends AccessibilityServiceInfoJellyBeanImpl {
+    @RequiresApi(16)
+    static class AccessibilityServiceInfoApi16Impl extends AccessibilityServiceInfoBaseImpl {
+        @Override
+        public String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
+            return info.loadDescription(pm);
+        }
+    }
+
+    @RequiresApi(18)
+    static class AccessibilityServiceInfoApi18Impl
+            extends AccessibilityServiceInfoApi16Impl {
         @Override
         public int getCapabilities(AccessibilityServiceInfo info) {
-            return AccessibilityServiceInfoCompatJellyBeanMr2.getCapabilities(info);
+            return info.getCapabilities();
         }
     }
 
     static {
         if (Build.VERSION.SDK_INT >= 18) { // JellyBean MR2
-            IMPL = new AccessibilityServiceInfoJellyBeanMr2Impl();
+            IMPL = new AccessibilityServiceInfoApi18Impl();
         } else if (Build.VERSION.SDK_INT >= 16) { // JB
-            IMPL = new AccessibilityServiceInfoJellyBeanImpl();
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new AccessibilityServiceInfoIcsImpl();
+            IMPL = new AccessibilityServiceInfoApi16Impl();
         } else {
-            IMPL = new AccessibilityServiceInfoStubImpl();
+            IMPL = new AccessibilityServiceInfoBaseImpl();
         }
     }
 
     // Capabilities
 
-    private static final AccessibilityServiceInfoVersionImpl IMPL;
+    private static final AccessibilityServiceInfoBaseImpl IMPL;
 
     /**
      * Capability: This accessibility service can retrieve the active window content.
@@ -181,7 +110,7 @@
      * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
      * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
      * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
-     * @see FEEDBACK_BRAILLE
+     * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
      */
     public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
 
@@ -191,8 +120,11 @@
      * If an {@link AccessibilityService} is the default for a given type.
      * Default service is invoked only if no package specific one exists. In case of
      * more than one package specific service only the earlier registered is notified.
+     *
+     * @deprecated Use {@link AccessibilityServiceInfo#DEFAULT} directly.
      */
-    public static final int DEFAULT = 0x0000001;
+    @Deprecated
+    public static final int DEFAULT = AccessibilityServiceInfo.DEFAULT;
 
     /**
      * If this flag is set the system will regard views that are not important
@@ -310,9 +242,12 @@
      *
      * @param info The service info of interest
      * @return The id.
+     *
+     * @deprecated Use {@link AccessibilityServiceInfo#getId()} directly.
      */
+    @Deprecated
     public static String getId(AccessibilityServiceInfo info) {
-        return IMPL.getId(info);
+        return info.getId();
     }
 
     /**
@@ -323,9 +258,12 @@
      *
      * @param info The service info of interest
      * @return The info.
+     *
+     * @deprecated Use {@link AccessibilityServiceInfo#getResolveInfo()} directly.
      */
+    @Deprecated
     public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
-        return IMPL.getResolveInfo(info);
+        return info.getResolveInfo();
     }
 
     /**
@@ -337,9 +275,12 @@
      *
      * @param info The service info of interest
      * @return The settings activity name.
+     *
+     * @deprecated Use {@link AccessibilityServiceInfo#getSettingsActivityName()} directly.
      */
+    @Deprecated
     public static String getSettingsActivityName(AccessibilityServiceInfo info) {
-        return IMPL.getSettingsActivityName(info);
+        return info.getSettingsActivityName();
     }
 
     /**
@@ -351,9 +292,12 @@
      *
      * @param info The service info of interest
      * @return True window content can be retrieved.
+     *
+     * @deprecated Use {@link AccessibilityServiceInfo#getCanRetrieveWindowContent()} directly.
      */
+    @Deprecated
     public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
-        return IMPL.getCanRetrieveWindowContent(info);
+        return info.getCanRetrieveWindowContent();
     }
 
     /**
@@ -368,8 +312,9 @@
      *
      * @deprecated Use {@link #loadDescription(AccessibilityServiceInfo, PackageManager)}.
      */
+    @Deprecated
     public static String getDescription(AccessibilityServiceInfo info) {
-        return IMPL.getDescription(info);
+        return info.getDescription();
     }
 
     /**
@@ -437,7 +382,7 @@
      */
     public static String flagToString(int flag) {
         switch (flag) {
-            case DEFAULT:
+            case AccessibilityServiceInfo.DEFAULT:
                 return "DEFAULT";
             case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
                 return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
diff --git a/compat/java/android/support/v4/animation/AnimatorCompatHelper.java b/compat/java/android/support/v4/animation/AnimatorCompatHelper.java
deleted file mode 100644
index 46b5b15..0000000
--- a/compat/java/android/support/v4/animation/AnimatorCompatHelper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.view.View;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class AnimatorCompatHelper {
-
-    private final static AnimatorProvider IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 12) {
-            IMPL = new HoneycombMr1AnimatorCompatProvider();
-        } else {
-            IMPL = new GingerbreadAnimatorCompatProvider();
-        }
-    }
-
-    public static ValueAnimatorCompat emptyValueAnimator() {
-        return IMPL.emptyValueAnimator();
-    }
-
-    private AnimatorCompatHelper() {}
-
-    public static void clearInterpolator(View view) {
-        IMPL.clearInterpolator(view);
-    }
-}
diff --git a/compat/java/android/support/v4/app/ActivityCompat.java b/compat/java/android/support/v4/app/ActivityCompat.java
index 3888be9..f260508 100644
--- a/compat/java/android/support/v4/app/ActivityCompat.java
+++ b/compat/java/android/support/v4/app/ActivityCompat.java
@@ -32,6 +32,8 @@
 import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
 import android.support.v4.content.ContextCompat;
 import android.view.View;
 
@@ -39,8 +41,7 @@
 import java.util.Map;
 
 /**
- * Helper for accessing features in {@link android.app.Activity}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.Activity}.
  */
 public class ActivityCompat extends ContextCompat {
 
@@ -73,8 +74,16 @@
     }
 
     /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public interface RequestPermissionsRequestCodeValidator {
+        void validateRequestPermissionsRequestCode(int requestCode);
+    }
+
+    /**
      * This class should not be instantiated, but the constructor must be
-     * visible for the class to be extended (ex. in support-v13).
+     * visible for the class to be extended (as in support-v13).
      */
     protected ActivityCompat() {
         // Not publicly instantiable, but may be extended.
@@ -113,11 +122,8 @@
      * @return true if this operation was supported and it completed; false if it was not available.
      */
     public static boolean invalidateOptionsMenu(Activity activity) {
-        if (Build.VERSION.SDK_INT >= 11) {
-            ActivityCompatHoneycomb.invalidateOptionsMenu(activity);
-            return true;
-        }
-        return false;
+        activity.invalidateOptionsMenu();
+        return true;
     }
 
     /**
@@ -143,7 +149,7 @@
     public static void startActivityForResult(Activity activity, Intent intent, int requestCode,
             @Nullable Bundle options) {
         if (Build.VERSION.SDK_INT >= 16) {
-            ActivityCompatJB.startActivityForResult(activity, intent, requestCode, options);
+            activity.startActivityForResult(intent, requestCode, options);
         } else {
             activity.startActivityForResult(intent, requestCode);
         }
@@ -179,8 +185,8 @@
             int requestCode, Intent fillInIntent, int flagsMask, int flagsValues,
             int extraFlags, @Nullable Bundle options) throws IntentSender.SendIntentException {
         if (Build.VERSION.SDK_INT >= 16) {
-            ActivityCompatJB.startIntentSenderForResult(activity, intent, requestCode, fillInIntent,
-                    flagsMask, flagsValues, extraFlags, options);
+            activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
+                    flagsValues, extraFlags, options);
         } else {
             activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
                     flagsValues, extraFlags);
@@ -196,7 +202,7 @@
      */
     public static void finishAffinity(Activity activity) {
         if (Build.VERSION.SDK_INT >= 16) {
-            ActivityCompatJB.finishAffinity(activity);
+            activity.finishAffinity();
         } else {
             activity.finish();
         }
@@ -213,7 +219,7 @@
      */
     public static void finishAfterTransition(Activity activity) {
         if (Build.VERSION.SDK_INT >= 21) {
-            ActivityCompatApi21.finishAfterTransition(activity);
+            activity.finishAfterTransition();
         } else {
             activity.finish();
         }
@@ -238,7 +244,7 @@
     @Nullable
     public static Uri getReferrer(Activity activity) {
         if (Build.VERSION.SDK_INT >= 22) {
-            return ActivityCompatApi22.getReferrer(activity);
+            return activity.getReferrer();
         }
         Intent intent = activity.getIntent();
         Uri referrer = intent.getParcelableExtra("android.intent.extra.REFERRER");
@@ -263,9 +269,15 @@
     public static void setEnterSharedElementCallback(Activity activity,
             SharedElementCallback callback) {
         if (Build.VERSION.SDK_INT >= 23) {
-            ActivityCompatApi23.setEnterSharedElementCallback(activity, createCallback23(callback));
+            android.app.SharedElementCallback frameworkCallback = callback != null
+                    ? new SharedElementCallback23Impl(callback)
+                    : null;
+            activity.setEnterSharedElementCallback(frameworkCallback);
         } else if (Build.VERSION.SDK_INT >= 21) {
-            ActivityCompatApi21.setEnterSharedElementCallback(activity, createCallback(callback));
+            android.app.SharedElementCallback frameworkCallback = callback != null
+                    ? new SharedElementCallback21Impl(callback)
+                    : null;
+            activity.setEnterSharedElementCallback(frameworkCallback);
         }
     }
 
@@ -281,21 +293,27 @@
     public static void setExitSharedElementCallback(Activity activity,
             SharedElementCallback callback) {
         if (Build.VERSION.SDK_INT >= 23) {
-            ActivityCompatApi23.setExitSharedElementCallback(activity, createCallback23(callback));
+            android.app.SharedElementCallback frameworkCallback = callback != null
+                    ? new SharedElementCallback23Impl(callback)
+                    : null;
+            activity.setExitSharedElementCallback(frameworkCallback);
         } else if (Build.VERSION.SDK_INT >= 21) {
-            ActivityCompatApi21.setExitSharedElementCallback(activity, createCallback(callback));
+            android.app.SharedElementCallback frameworkCallback = callback != null
+                    ? new SharedElementCallback21Impl(callback)
+                    : null;
+            activity.setExitSharedElementCallback(frameworkCallback);
         }
     }
 
     public static void postponeEnterTransition(Activity activity) {
         if (Build.VERSION.SDK_INT >= 21) {
-            ActivityCompatApi21.postponeEnterTransition(activity);
+            activity.postponeEnterTransition();
         }
     }
 
     public static void startPostponedEnterTransition(Activity activity) {
         if (Build.VERSION.SDK_INT >= 21) {
-            ActivityCompatApi21.startPostponedEnterTransition(activity);
+            activity.startPostponedEnterTransition();
         }
     }
 
@@ -369,7 +387,11 @@
     public static void requestPermissions(final @NonNull Activity activity,
             final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
         if (Build.VERSION.SDK_INT >= 23) {
-            ActivityCompatApi23.requestPermissions(activity, permissions, requestCode);
+            if (activity instanceof RequestPermissionsRequestCodeValidator) {
+                ((RequestPermissionsRequestCodeValidator) activity)
+                        .validateRequestPermissionsRequestCode(requestCode);
+            }
+            activity.requestPermissions(permissions, requestCode);
         } else if (activity instanceof OnRequestPermissionsResultCallback) {
             Handler handler = new Handler(Looper.getMainLooper());
             handler.post(new Runnable() {
@@ -416,33 +438,15 @@
     public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
             @NonNull String permission) {
         if (Build.VERSION.SDK_INT >= 23) {
-            return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
+            return activity.shouldShowRequestPermissionRationale(permission);
         }
         return false;
     }
 
-    private static ActivityCompatApi21.SharedElementCallback21 createCallback(
-            SharedElementCallback callback) {
-        ActivityCompatApi21.SharedElementCallback21 newCallback = null;
-        if (callback != null) {
-            newCallback = new ActivityCompat.SharedElementCallback21Impl(callback);
-        }
-        return newCallback;
-    }
+    @RequiresApi(21)
+    private static class SharedElementCallback21Impl extends android.app.SharedElementCallback {
 
-    private static ActivityCompatApi23.SharedElementCallback23 createCallback23(
-            SharedElementCallback callback) {
-        ActivityCompatApi23.SharedElementCallback23 newCallback = null;
-        if (callback != null) {
-            newCallback = new ActivityCompat.SharedElementCallback23Impl(callback);
-        }
-        return newCallback;
-    }
-
-    private static class SharedElementCallback21Impl
-            extends ActivityCompatApi21.SharedElementCallback21 {
-
-        private SharedElementCallback mCallback;
+        protected SharedElementCallback mCallback;
 
         public SharedElementCallback21Impl(SharedElementCallback callback) {
             mCallback = callback;
@@ -485,55 +489,15 @@
         }
     }
 
-    private static class SharedElementCallback23Impl
-            extends ActivityCompatApi23.SharedElementCallback23 {
-
-        private SharedElementCallback mCallback;
-
+    @RequiresApi(23)
+    private static class SharedElementCallback23Impl extends SharedElementCallback21Impl {
         public SharedElementCallback23Impl(SharedElementCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onSharedElementStart(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots) {
-            mCallback.onSharedElementStart(sharedElementNames, sharedElements,
-                    sharedElementSnapshots);
-        }
-
-        @Override
-        public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements,
-                List<View> sharedElementSnapshots) {
-            mCallback.onSharedElementEnd(sharedElementNames, sharedElements,
-                    sharedElementSnapshots);
-        }
-
-        @Override
-        public void onRejectSharedElements(List<View> rejectedSharedElements) {
-            mCallback.onRejectSharedElements(rejectedSharedElements);
-        }
-
-        @Override
-        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
-            mCallback.onMapSharedElements(names, sharedElements);
-        }
-
-        @Override
-        public Parcelable onCaptureSharedElementSnapshot(View sharedElement,
-                Matrix viewToGlobalMatrix, RectF screenBounds) {
-            return mCallback.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix,
-                    screenBounds);
-        }
-
-        @Override
-        public View onCreateSnapshotView(Context context, Parcelable snapshot) {
-            return mCallback.onCreateSnapshotView(context, snapshot);
+            super(callback);
         }
 
         @Override
         public void onSharedElementsArrived(List<String> sharedElementNames,
-                List<View> sharedElements,
-                final ActivityCompatApi23.OnSharedElementsReadyListenerBridge listener) {
+                List<View> sharedElements, final OnSharedElementsReadyListener listener) {
             mCallback.onSharedElementsArrived(sharedElementNames, sharedElements,
                     new SharedElementCallback.OnSharedElementsReadyListener() {
                         @Override
diff --git a/compat/java/android/support/v4/app/ActivityManagerCompat.java b/compat/java/android/support/v4/app/ActivityManagerCompat.java
index 53d8703..e8aaab6 100644
--- a/compat/java/android/support/v4/app/ActivityManagerCompat.java
+++ b/compat/java/android/support/v4/app/ActivityManagerCompat.java
@@ -21,8 +21,8 @@
 import android.support.annotation.NonNull;
 
 /**
- * Helper for accessing features in {@link android.app.ActivityManager}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.ActivityManager} in a backwards compatible
+ * fashion.
  */
 public final class ActivityManagerCompat {
 
@@ -35,9 +35,9 @@
      * This is mostly intended to be used by apps to determine whether they should turn
      * off certain features that require more RAM.
      */
-    public static boolean isLowRamDevice(@NonNull ActivityManager am) {
+    public static boolean isLowRamDevice(@NonNull ActivityManager activityManager) {
         if (Build.VERSION.SDK_INT >= 19) {
-            return ActivityManagerCompatKitKat.isLowRamDevice(am);
+            return activityManager.isLowRamDevice();
         }
         return false;
     }
diff --git a/compat/java/android/support/v4/app/ActivityOptionsCompat.java b/compat/java/android/support/v4/app/ActivityOptionsCompat.java
index 57ca1a4..7b5916f 100644
--- a/compat/java/android/support/v4/app/ActivityOptionsCompat.java
+++ b/compat/java/android/support/v4/app/ActivityOptionsCompat.java
@@ -16,8 +16,8 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -30,8 +30,8 @@
 import android.view.View;
 
 /**
- * Helper for accessing features in {@link android.app.ActivityOptions}
- * introduced in API level 16 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.ActivityOptions} in a backwards compatible
+ * fashion.
  */
 public class ActivityOptionsCompat {
     /**
@@ -62,18 +62,8 @@
      */
     public static ActivityOptionsCompat makeCustomAnimation(Context context,
             int enterResId, int exitResId) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsImpl24(
-                ActivityOptionsCompat24.makeCustomAnimation(context, enterResId, exitResId));
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsImpl23(
-                ActivityOptionsCompat23.makeCustomAnimation(context, enterResId, exitResId));
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            return new ActivityOptionsImpl21(
-                ActivityOptionsCompat21.makeCustomAnimation(context, enterResId, exitResId));
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return new ActivityOptionsImplJB(
-                ActivityOptionsCompatJB.makeCustomAnimation(context, enterResId, exitResId));
+        if (Build.VERSION.SDK_INT >= 16) {
+            return createImpl(ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
         }
         return new ActivityOptionsCompat();
     }
@@ -100,22 +90,9 @@
      */
     public static ActivityOptionsCompat makeScaleUpAnimation(View source,
             int startX, int startY, int startWidth, int startHeight) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsImpl24(
-                ActivityOptionsCompat24.makeScaleUpAnimation(source, startX, startY,
-                        startWidth, startHeight));
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsImpl23(
-                ActivityOptionsCompat23.makeScaleUpAnimation(source, startX, startY,
-                        startWidth, startHeight));
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            return new ActivityOptionsImpl21(
-                ActivityOptionsCompat21.makeScaleUpAnimation(source, startX, startY,
-                        startWidth, startHeight));
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return new ActivityOptionsImplJB(
-                ActivityOptionsCompatJB.makeScaleUpAnimation(source, startX, startY,
-                        startWidth, startHeight));
+        if (Build.VERSION.SDK_INT >= 16) {
+            return createImpl(ActivityOptions.makeScaleUpAnimation(
+                    source, startX, startY, startWidth, startHeight));
         }
         return new ActivityOptionsCompat();
     }
@@ -136,14 +113,9 @@
      */
     public static ActivityOptionsCompat makeClipRevealAnimation(View source,
             int startX, int startY, int width, int height) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsImpl24(
-                ActivityOptionsCompat24.makeClipRevealAnimation(source, startX, startY,
-                        width, height));
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsImpl23(
-                ActivityOptionsCompat23.makeClipRevealAnimation(source, startX, startY,
-                        width, height));
+        if (Build.VERSION.SDK_INT >= 23) {
+            return createImpl(ActivityOptions.makeClipRevealAnimation(
+                    source, startX, startY, width, height));
         }
         return new ActivityOptionsCompat();
     }
@@ -169,22 +141,9 @@
      */
     public static ActivityOptionsCompat makeThumbnailScaleUpAnimation(View source,
             Bitmap thumbnail, int startX, int startY) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsImpl24(
-                ActivityOptionsCompat24.makeThumbnailScaleUpAnimation(source, thumbnail,
-                        startX, startY));
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsImpl23(
-                ActivityOptionsCompat23.makeThumbnailScaleUpAnimation(source, thumbnail,
-                        startX, startY));
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            return new ActivityOptionsImpl21(
-                ActivityOptionsCompat21.makeThumbnailScaleUpAnimation(source, thumbnail,
-                        startX, startY));
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return new ActivityOptionsImplJB(
-                ActivityOptionsCompatJB.makeThumbnailScaleUpAnimation(source, thumbnail,
-                        startX, startY));
+        if (Build.VERSION.SDK_INT >= 16) {
+            return createImpl(ActivityOptions.makeThumbnailScaleUpAnimation(
+                    source, thumbnail, startX, startY));
         }
         return new ActivityOptionsCompat();
     }
@@ -209,18 +168,9 @@
      */
     public static ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity,
             View sharedElement, String sharedElementName) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl24(
-                    ActivityOptionsCompat24.makeSceneTransitionAnimation(activity,
-                            sharedElement, sharedElementName));
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl23(
-                    ActivityOptionsCompat23.makeSceneTransitionAnimation(activity,
-                            sharedElement, sharedElementName));
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl21(
-                    ActivityOptionsCompat21.makeSceneTransitionAnimation(activity,
-                            sharedElement, sharedElementName));
+        if (Build.VERSION.SDK_INT >= 21) {
+            return createImpl(ActivityOptions.makeSceneTransitionAnimation(
+                    activity, sharedElement, sharedElementName));
         }
         return new ActivityOptionsCompat();
     }
@@ -242,29 +192,19 @@
      * @return Returns a new ActivityOptions object that you can use to
      *         supply these options as the options Bundle when starting an activity.
      */
+    @SuppressWarnings("unchecked")
     public static ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity,
             Pair<View, String>... sharedElements) {
         if (Build.VERSION.SDK_INT >= 21) {
-            View[] views = null;
-            String[] names = null;
+            android.util.Pair<View, String>[] pairs = null;
             if (sharedElements != null) {
-                views = new View[sharedElements.length];
-                names = new String[sharedElements.length];
+                pairs = new android.util.Pair[sharedElements.length];
                 for (int i = 0; i < sharedElements.length; i++) {
-                    views[i] = sharedElements[i].first;
-                    names[i] = sharedElements[i].second;
+                    pairs[i] = android.util.Pair.create(
+                            sharedElements[i].first, sharedElements[i].second);
                 }
             }
-            if (Build.VERSION.SDK_INT >= 24) {
-                return new ActivityOptionsCompat.ActivityOptionsImpl24(
-                        ActivityOptionsCompat24.makeSceneTransitionAnimation(activity, views, names));
-            } else if (Build.VERSION.SDK_INT >= 23) {
-                return new ActivityOptionsCompat.ActivityOptionsImpl23(
-                        ActivityOptionsCompat23.makeSceneTransitionAnimation(activity, views, names));
-            } else {
-                return new ActivityOptionsCompat.ActivityOptionsImpl21(
-                        ActivityOptionsCompat21.makeSceneTransitionAnimation(activity, views, names));
-            }
+            return createImpl(ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
         }
         return new ActivityOptionsCompat();
     }
@@ -280,15 +220,8 @@
      * <code>singleInstance</code> or <code>singleTask</code>.
      */
     public static ActivityOptionsCompat makeTaskLaunchBehind() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl24(
-                    ActivityOptionsCompat24.makeTaskLaunchBehind());
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl23(
-                    ActivityOptionsCompat23.makeTaskLaunchBehind());
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl21(
-                    ActivityOptionsCompat21.makeTaskLaunchBehind());
+        if (Build.VERSION.SDK_INT >= 21) {
+            return createImpl(ActivityOptions.makeTaskLaunchBehind());
         }
         return new ActivityOptionsCompat();
     }
@@ -298,128 +231,73 @@
      * Other options can still be set.
      */
     public static ActivityOptionsCompat makeBasic() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl24(
-                    ActivityOptionsCompat24.makeBasic());
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new ActivityOptionsCompat.ActivityOptionsImpl23(
-                    ActivityOptionsCompat23.makeBasic());
+        if (Build.VERSION.SDK_INT >= 23) {
+            return createImpl(ActivityOptions.makeBasic());
         }
         return new ActivityOptionsCompat();
     }
 
     @RequiresApi(16)
-    @TargetApi(16)
-    private static class ActivityOptionsImplJB extends ActivityOptionsCompat {
-        private final ActivityOptionsCompatJB mImpl;
-
-        ActivityOptionsImplJB(ActivityOptionsCompatJB impl) {
-            mImpl = impl;
-        }
-
-        @Override
-        public Bundle toBundle() {
-            return mImpl.toBundle();
-        }
-
-        @Override
-        public void update(ActivityOptionsCompat otherOptions) {
-            if (otherOptions instanceof ActivityOptionsImplJB) {
-                ActivityOptionsImplJB otherImpl = (ActivityOptionsImplJB)otherOptions;
-                mImpl.update(otherImpl.mImpl);
-            }
+    private static ActivityOptionsCompat createImpl(ActivityOptions options) {
+        if (Build.VERSION.SDK_INT >= 24) {
+            return new ActivityOptionsCompatApi24Impl(options);
+        } else if (Build.VERSION.SDK_INT >= 23) {
+            return new ActivityOptionsCompatApi23Impl(options);
+        } else {
+            return new ActivityOptionsCompatApi16Impl(options);
         }
     }
 
-    @RequiresApi(21)
-    @TargetApi(21)
-    private static class ActivityOptionsImpl21 extends ActivityOptionsCompat {
-        private final ActivityOptionsCompat21 mImpl;
+    @RequiresApi(16)
+    private static class ActivityOptionsCompatApi16Impl extends ActivityOptionsCompat {
+        protected final ActivityOptions mActivityOptions;
 
-        ActivityOptionsImpl21(ActivityOptionsCompat21 impl) {
-            mImpl = impl;
+        ActivityOptionsCompatApi16Impl(ActivityOptions activityOptions) {
+            mActivityOptions = activityOptions;
         }
 
         @Override
         public Bundle toBundle() {
-            return mImpl.toBundle();
+            return mActivityOptions.toBundle();
         }
 
         @Override
         public void update(ActivityOptionsCompat otherOptions) {
-            if (otherOptions instanceof ActivityOptionsCompat.ActivityOptionsImpl21) {
-                ActivityOptionsCompat.ActivityOptionsImpl21
-                        otherImpl = (ActivityOptionsCompat.ActivityOptionsImpl21)otherOptions;
-                mImpl.update(otherImpl.mImpl);
+            if (otherOptions instanceof ActivityOptionsCompatApi16Impl) {
+                ActivityOptionsCompatApi16Impl otherImpl =
+                        (ActivityOptionsCompatApi16Impl) otherOptions;
+                mActivityOptions.update(otherImpl.mActivityOptions);
             }
         }
     }
 
     @RequiresApi(23)
-    @TargetApi(23)
-    private static class ActivityOptionsImpl23 extends ActivityOptionsCompat {
-        private final ActivityOptionsCompat23 mImpl;
-
-        ActivityOptionsImpl23(ActivityOptionsCompat23 impl) {
-            mImpl = impl;
-        }
-
-        @Override
-        public Bundle toBundle() {
-            return mImpl.toBundle();
-        }
-
-        @Override
-        public void update(ActivityOptionsCompat otherOptions) {
-            if (otherOptions instanceof ActivityOptionsCompat.ActivityOptionsImpl23) {
-                ActivityOptionsCompat.ActivityOptionsImpl23
-                        otherImpl = (ActivityOptionsCompat.ActivityOptionsImpl23)otherOptions;
-                mImpl.update(otherImpl.mImpl);
-            }
+    private static class ActivityOptionsCompatApi23Impl extends ActivityOptionsCompatApi16Impl {
+        ActivityOptionsCompatApi23Impl(ActivityOptions activityOptions) {
+            super(activityOptions);
         }
 
         @Override
         public void requestUsageTimeReport(PendingIntent receiver) {
-            mImpl.requestUsageTimeReport(receiver);
+            mActivityOptions.requestUsageTimeReport(receiver);
         }
     }
 
     @RequiresApi(24)
-    @TargetApi(24)
-    private static class ActivityOptionsImpl24 extends ActivityOptionsCompat {
-        private final ActivityOptionsCompat24 mImpl;
-
-        ActivityOptionsImpl24(ActivityOptionsCompat24 impl) {
-            mImpl = impl;
-        }
-
-        @Override
-        public Bundle toBundle() {
-            return mImpl.toBundle();
-        }
-
-        @Override
-        public void update(ActivityOptionsCompat otherOptions) {
-            if (otherOptions instanceof ActivityOptionsCompat.ActivityOptionsImpl24) {
-                ActivityOptionsCompat.ActivityOptionsImpl24
-                        otherImpl = (ActivityOptionsCompat.ActivityOptionsImpl24)otherOptions;
-                mImpl.update(otherImpl.mImpl);
-            }
+    private static class ActivityOptionsCompatApi24Impl extends ActivityOptionsCompatApi23Impl {
+        ActivityOptionsCompatApi24Impl(ActivityOptions activityOptions) {
+            super(activityOptions);
         }
 
         @Override
         public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
-            return new ActivityOptionsImpl24(mImpl.setLaunchBounds(screenSpacePixelRect));
+            return new ActivityOptionsCompatApi24Impl(
+                    mActivityOptions.setLaunchBounds(screenSpacePixelRect));
         }
 
         @Override
         public Rect getLaunchBounds() {
-            return mImpl.getLaunchBounds();
-        }
-
-        @Override
-        public void requestUsageTimeReport(PendingIntent receiver) {
-            mImpl.requestUsageTimeReport(receiver);
+            return mActivityOptions.getLaunchBounds();
         }
     }
 
@@ -492,7 +370,7 @@
      * the launcher and recents stops time tracking of the session); it is the act of
      * going somewhere else that completes the tracking.</p>
      *
-     * @param receiver A broadcast receiver that willl receive the report.
+     * @param receiver A broadcast receiver that will receive the report.
      */
     public void requestUsageTimeReport(PendingIntent receiver) {
         // Do nothing.
diff --git a/compat/java/android/support/v4/app/AlarmManagerCompat.java b/compat/java/android/support/v4/app/AlarmManagerCompat.java
new file mode 100644
index 0000000..5a4582b
--- /dev/null
+++ b/compat/java/android/support/v4/app/AlarmManagerCompat.java
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.os.Build;
+
+/**
+ * Compatibility library for {@link AlarmManager} with fallbacks for older platforms.
+ */
+public final class AlarmManagerCompat {
+    /**
+     * Schedule an alarm that represents an alarm clock.
+     *
+     * The system may choose to display information about this alarm to the user.
+     *
+     * <p>
+     * This method is like {@link #setExact}, but implies
+     * {@link AlarmManager#RTC_WAKEUP}.
+     *
+     * @param alarmManager AlarmManager instance used to set the alarm
+     * @param triggerTime time at which the underlying alarm is triggered in wall time
+     *                    milliseconds since the epoch
+     * @param showIntent an intent that can be used to show or edit details of
+     *                    the alarm clock.
+     * @param operation Action to perform when the alarm goes off;
+     *        typically comes from {@link PendingIntent#getBroadcast
+     *        IntentSender.getBroadcast()}.
+     *
+     * @see AlarmManager#set
+     * @see AlarmManager#setRepeating
+     * @see AlarmManager#setWindow
+     * @see #setExact
+     * @see AlarmManager#cancel
+     * @see AlarmManager#getNextAlarmClock()
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     */
+    public static void setAlarmClock(AlarmManager alarmManager, long triggerTime,
+            PendingIntent showIntent, PendingIntent operation) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(triggerTime, showIntent),
+                    operation);
+        } else {
+            AlarmManagerCompat.setExact(alarmManager, AlarmManager.RTC_WAKEUP, triggerTime,
+                    operation);
+        }
+    }
+
+    /**
+     * Like {@link AlarmManager#set(int, long, PendingIntent)}, but this alarm will be allowed to
+     * execute even when the system is in low-power idle modes.  This type of alarm must <b>only</b>
+     * be used for situations where it is actually required that the alarm go off while in
+     * idle -- a reasonable example would be for a calendar notification that should make a
+     * sound so the user is aware of it.  When the alarm is dispatched, the app will also be
+     * added to the system's temporary whitelist for approximately 10 seconds to allow that
+     * application to acquire further wake locks in which to complete its work.</p>
+     *
+     * <p>These alarms can significantly impact the power use
+     * of the device when idle (and thus cause significant battery blame to the app scheduling
+     * them), so they should be used with care.  To reduce abuse, there are restrictions on how
+     * frequently these alarms will go off for a particular application.
+     * Under normal system operation, it will not dispatch these
+     * alarms more than about every minute (at which point every such pending alarm is
+     * dispatched); when in low-power idle modes this duration may be significantly longer,
+     * such as 15 minutes.</p>
+     *
+     * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen
+     * out of order with any other alarms, even those from the same app.  This will clearly happen
+     * when the device is idle (since this alarm can go off while idle, when any other alarms
+     * from the app will be held until later), but may also happen even when not idle.</p>
+     *
+     * <p>Regardless of the app's target SDK version, this call always allows batching of the
+     * alarm.</p>
+     *
+     * @param alarmManager AlarmManager instance used to set the alarm
+     * @param type One of {@link AlarmManager#ELAPSED_REALTIME},
+     *        {@link AlarmManager#ELAPSED_REALTIME_WAKEUP},
+     *        {@link AlarmManager#RTC}, or {@link AlarmManager#RTC_WAKEUP}.
+     * @param triggerAtMillis time in milliseconds that the alarm should go
+     * off, using the appropriate clock (depending on the alarm type).
+     * @param operation Action to perform when the alarm goes off;
+     * typically comes from {@link PendingIntent#getBroadcast
+     * IntentSender.getBroadcast()}.
+     *
+     * @see AlarmManager#set(int, long, PendingIntent)
+     * @see #setExactAndAllowWhileIdle
+     * @see AlarmManager#cancel
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     * @see AlarmManager#ELAPSED_REALTIME
+     * @see AlarmManager#ELAPSED_REALTIME_WAKEUP
+     * @see AlarmManager#RTC
+     * @see AlarmManager#RTC_WAKEUP
+     */
+    public static void setAndAllowWhileIdle(AlarmManager alarmManager, int type,
+            long triggerAtMillis, PendingIntent operation) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            alarmManager.setAndAllowWhileIdle(type, triggerAtMillis, operation);
+        } else {
+            alarmManager.set(type, triggerAtMillis, operation);
+        }
+    }
+
+    /**
+     * Schedule an alarm to be delivered precisely at the stated time.
+     *
+     * <p>
+     * This method is like {@link AlarmManager#set(int, long, PendingIntent)}, but does not permit
+     * the OS to adjust the delivery time.  The alarm will be delivered as nearly as
+     * possible to the requested trigger time.
+     *
+     * <p>
+     * <b>Note:</b> only alarms for which there is a strong demand for exact-time
+     * delivery (such as an alarm clock ringing at the requested time) should be
+     * scheduled as exact.  Applications are strongly discouraged from using exact
+     * alarms unnecessarily as they reduce the OS's ability to minimize battery use.
+     *
+     * @param alarmManager AlarmManager instance used to set the alarm
+     * @param type One of {@link AlarmManager#ELAPSED_REALTIME},
+     *        {@link AlarmManager#ELAPSED_REALTIME_WAKEUP},
+     *        {@link AlarmManager#RTC}, or {@link AlarmManager#RTC_WAKEUP}.
+     * @param triggerAtMillis time in milliseconds that the alarm should go
+     *        off, using the appropriate clock (depending on the alarm type).
+     * @param operation Action to perform when the alarm goes off;
+     *        typically comes from {@link PendingIntent#getBroadcast
+     *        IntentSender.getBroadcast()}.
+     *
+     * @see AlarmManager#set
+     * @see AlarmManager#setRepeating
+     * @see AlarmManager#setWindow
+     * @see AlarmManager#cancel
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     * @see AlarmManager#ELAPSED_REALTIME
+     * @see AlarmManager#ELAPSED_REALTIME_WAKEUP
+     * @see AlarmManager#RTC
+     * @see AlarmManager#RTC_WAKEUP
+     */
+    public static void setExact(AlarmManager alarmManager, int type, long triggerAtMillis,
+            PendingIntent operation) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            alarmManager.setExact(type, triggerAtMillis, operation);
+        } else {
+            alarmManager.set(type, triggerAtMillis, operation);
+        }
+    }
+
+    /**
+     * Like {@link #setExact}, but this alarm will be allowed to execute
+     * even when the system is in low-power idle modes.  If you don't need exact scheduling of
+     * the alarm but still need to execute while idle, consider using
+     * {@link #setAndAllowWhileIdle}.  This type of alarm must <b>only</b>
+     * be used for situations where it is actually required that the alarm go off while in
+     * idle -- a reasonable example would be for a calendar notification that should make a
+     * sound so the user is aware of it.  When the alarm is dispatched, the app will also be
+     * added to the system's temporary whitelist for approximately 10 seconds to allow that
+     * application to acquire further wake locks in which to complete its work.</p>
+     *
+     * <p>These alarms can significantly impact the power use
+     * of the device when idle (and thus cause significant battery blame to the app scheduling
+     * them), so they should be used with care.  To reduce abuse, there are restrictions on how
+     * frequently these alarms will go off for a particular application.
+     * Under normal system operation, it will not dispatch these
+     * alarms more than about every minute (at which point every such pending alarm is
+     * dispatched); when in low-power idle modes this duration may be significantly longer,
+     * such as 15 minutes.</p>
+     *
+     * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen
+     * out of order with any other alarms, even those from the same app.  This will clearly happen
+     * when the device is idle (since this alarm can go off while idle, when any other alarms
+     * from the app will be held until later), but may also happen even when not idle.
+     * Note that the OS will allow itself more flexibility for scheduling these alarms than
+     * regular exact alarms, since the application has opted into this behavior.  When the
+     * device is idle it may take even more liberties with scheduling in order to optimize
+     * for battery life.</p>
+     *
+     * @param alarmManager AlarmManager instance used to set the alarm
+     * @param type One of {@link AlarmManager#ELAPSED_REALTIME},
+     *        {@link AlarmManager#ELAPSED_REALTIME_WAKEUP},
+     *        {@link AlarmManager#RTC}, or {@link AlarmManager#RTC_WAKEUP}.
+     * @param triggerAtMillis time in milliseconds that the alarm should go
+     *        off, using the appropriate clock (depending on the alarm type).
+     * @param operation Action to perform when the alarm goes off;
+     *        typically comes from {@link PendingIntent#getBroadcast
+     *        IntentSender.getBroadcast()}.
+     *
+     * @see AlarmManager#set
+     * @see AlarmManager#setRepeating
+     * @see AlarmManager#setWindow
+     * @see AlarmManager#cancel
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     * @see AlarmManager#ELAPSED_REALTIME
+     * @see AlarmManager#ELAPSED_REALTIME_WAKEUP
+     * @see AlarmManager#RTC
+     * @see AlarmManager#RTC_WAKEUP
+     */
+    public static void setExactAndAllowWhileIdle(AlarmManager alarmManager, int type,
+            long triggerAtMillis, PendingIntent operation) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            alarmManager.setExactAndAllowWhileIdle(type, triggerAtMillis, operation);
+        } else {
+            AlarmManagerCompat.setExact(alarmManager, type, triggerAtMillis, operation);
+        }
+    }
+
+    private AlarmManagerCompat() {
+    }
+}
diff --git a/compat/java/android/support/v4/app/AppOpsManagerCompat.java b/compat/java/android/support/v4/app/AppOpsManagerCompat.java
index 06da861..ce2d2c6 100644
--- a/compat/java/android/support/v4/app/AppOpsManagerCompat.java
+++ b/compat/java/android/support/v4/app/AppOpsManagerCompat.java
@@ -16,13 +16,14 @@
 
 package android.support.v4.app;
 
+import static android.os.Build.VERSION.SDK_INT;
+
+import android.app.AppOpsManager;
 import android.content.Context;
-import android.os.Build;
 import android.support.annotation.NonNull;
 
 /**
- * Helper for accessing features in android.app.AppOpsManager
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.AppOpsManager}.
  */
 public final class AppOpsManagerCompat {
 
@@ -47,52 +48,6 @@
      */
     public static final int MODE_DEFAULT = 3;
 
-    private static class AppOpsManagerImpl {
-        AppOpsManagerImpl() {
-        }
-
-        public String permissionToOp(String permission) {
-            return null;
-        }
-
-        public int noteOp(Context context, String op, int uid, String packageName) {
-            return MODE_IGNORED;
-        }
-
-        public int noteProxyOp(Context context, String op, String proxiedPackageName) {
-            return MODE_IGNORED;
-        }
-    }
-
-    private static class AppOpsManager23 extends AppOpsManagerImpl {
-        AppOpsManager23() {
-        }
-
-        @Override
-        public String permissionToOp(String permission) {
-            return AppOpsManagerCompat23.permissionToOp(permission);
-        }
-
-        @Override
-        public int noteOp(Context context, String op, int uid, String packageName) {
-            return AppOpsManagerCompat23.noteOp(context, op, uid, packageName);
-        }
-
-        @Override
-        public int noteProxyOp(Context context, String op, String proxiedPackageName) {
-            return AppOpsManagerCompat23.noteProxyOp(context, op, proxiedPackageName);
-        }
-    }
-
-    private static final AppOpsManagerImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 23) {
-            IMPL = new AppOpsManager23();
-        } else {
-            IMPL = new AppOpsManagerImpl();
-        }
-    }
-
     private AppOpsManagerCompat() {}
 
     /**
@@ -102,7 +57,11 @@
      * @return The app op associated with the permission or null.
      */
     public static String permissionToOp(@NonNull String permission) {
-        return IMPL.permissionToOp(permission);
+        if (SDK_INT >= 23) {
+            return AppOpsManager.permissionToOp(permission);
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -122,7 +81,12 @@
      */
     public static int noteOp(@NonNull Context context, @NonNull String op, int uid,
             @NonNull String packageName) {
-        return IMPL.noteOp(context, op, uid, packageName);
+        if (SDK_INT >= 23) {
+            AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+            return appOpsManager.noteOp(op, uid, packageName);
+        } else {
+            return MODE_IGNORED;
+        }
     }
 
     /**
@@ -143,6 +107,11 @@
      */
     public static int noteProxyOp(@NonNull Context context, @NonNull String op,
             @NonNull String proxiedPackageName) {
-        return IMPL.noteProxyOp(context, op, proxiedPackageName);
+        if (SDK_INT >= 23) {
+            AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+            return appOpsManager.noteProxyOp(op, proxiedPackageName);
+        } else {
+            return MODE_IGNORED;
+        }
     }
 }
diff --git a/compat/java/android/support/v4/app/BundleCompat.java b/compat/java/android/support/v4/app/BundleCompat.java
index 17e28d4..e5fc302 100644
--- a/compat/java/android/support/v4/app/BundleCompat.java
+++ b/compat/java/android/support/v4/app/BundleCompat.java
@@ -19,13 +19,72 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 
 /**
- * Helper for accessing features in {@link Bundle}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link Bundle}.
  */
 public final class BundleCompat {
 
+    static class BundleCompatBaseImpl {
+        private static final String TAG = "BundleCompatBaseImpl";
+
+        private static Method sGetIBinderMethod;
+        private static boolean sGetIBinderMethodFetched;
+
+        private static Method sPutIBinderMethod;
+        private static boolean sPutIBinderMethodFetched;
+
+        public static IBinder getBinder(Bundle bundle, String key) {
+            if (!sGetIBinderMethodFetched) {
+                try {
+                    sGetIBinderMethod = Bundle.class.getMethod("getIBinder", String.class);
+                    sGetIBinderMethod.setAccessible(true);
+                } catch (NoSuchMethodException e) {
+                    Log.i(TAG, "Failed to retrieve getIBinder method", e);
+                }
+                sGetIBinderMethodFetched = true;
+            }
+
+            if (sGetIBinderMethod != null) {
+                try {
+                    return (IBinder) sGetIBinderMethod.invoke(bundle, key);
+                } catch (InvocationTargetException | IllegalAccessException
+                        | IllegalArgumentException e) {
+                    Log.i(TAG, "Failed to invoke getIBinder via reflection", e);
+                    sGetIBinderMethod = null;
+                }
+            }
+            return null;
+        }
+
+        public static void putBinder(Bundle bundle, String key, IBinder binder) {
+            if (!sPutIBinderMethodFetched) {
+                try {
+                    sPutIBinderMethod =
+                            Bundle.class.getMethod("putIBinder", String.class, IBinder.class);
+                    sPutIBinderMethod.setAccessible(true);
+                } catch (NoSuchMethodException e) {
+                    Log.i(TAG, "Failed to retrieve putIBinder method", e);
+                }
+                sPutIBinderMethodFetched = true;
+            }
+
+            if (sPutIBinderMethod != null) {
+                try {
+                    sPutIBinderMethod.invoke(bundle, key, binder);
+                } catch (InvocationTargetException | IllegalAccessException
+                        | IllegalArgumentException e) {
+                    Log.i(TAG, "Failed to invoke putIBinder via reflection", e);
+                    sPutIBinderMethod = null;
+                }
+            }
+        }
+    }
+
     private BundleCompat() {}
 
     /**
@@ -37,9 +96,9 @@
      */
     public static IBinder getBinder(Bundle bundle, String key) {
         if (Build.VERSION.SDK_INT >= 18) {
-            return BundleCompatJellybeanMR2.getBinder(bundle, key);
+            return bundle.getBinder(key);
         } else {
-            return BundleCompatGingerbread.getBinder(bundle, key);
+            return BundleCompatBaseImpl.getBinder(bundle, key);
         }
     }
 
@@ -52,9 +111,9 @@
      */
     public static void putBinder(Bundle bundle, String key, IBinder binder) {
         if (Build.VERSION.SDK_INT >= 18) {
-            BundleCompatJellybeanMR2.putBinder(bundle, key, binder);
+            bundle.putBinder(key, binder);
         } else {
-            BundleCompatGingerbread.putBinder(bundle, key, binder);
+            BundleCompatBaseImpl.putBinder(bundle, key, binder);
         }
     }
 }
diff --git a/compat/java/android/support/v4/app/JobIntentService.java b/compat/java/android/support/v4/app/JobIntentService.java
new file mode 100644
index 0000000..4c83eb9
--- /dev/null
+++ b/compat/java/android/support/v4/app/JobIntentService.java
@@ -0,0 +1,638 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.app.Service;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobServiceEngine;
+import android.app.job.JobWorkItem;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Helper for processing work that has been enqueued for a job/service.  When running on
+ * {@link android.os.Build.VERSION_CODES#O Android O} or later, the work will be dispatched
+ * as a job via {@link android.app.job.JobScheduler#enqueue JobScheduler.enqueue}.  When running
+ * on older versions of the platform, it will use
+ * {@link android.content.Context#startService Context.startService}.
+ *
+ * <p>You must publish your subclass in your manifest for the system to interact with.  This
+ * should be published as a {@link android.app.job.JobService}, as described for that class,
+ * since on O and later platforms it will be executed that way.</p>
+ *
+ * <p>Use {@link #enqueueWork(Context, Class, int, Intent)} to enqueue new work to be
+ * dispatched to and handled by your service.  It will be executed in
+ * {@link #onHandleWork(Intent)}.</p>
+ *
+ * <p>You do not need to use {@link android.support.v4.content.WakefulBroadcastReceiver}
+ * when using this class.  When running on {@link android.os.Build.VERSION_CODES#O Android O},
+ * the JobScheduler will take care of wake locks for you (holding a wake lock from the time
+ * you enqueue work until the job has been dispatched and while it is running).  When running
+ * on previous versions of the platform, this wake lock handling is emulated in the class here
+ * by directly calling the PowerManager; this means the application must request the
+ * {@link android.Manifest.permission#WAKE_LOCK} permission.</p>
+ *
+ * <p>There are a few important differences in behavior when running on
+ * {@link android.os.Build.VERSION_CODES#O Android O} or later as a Job vs. pre-O:</p>
+ *
+ * <ul>
+ *     <li><p>When running as a pre-O service, the act of enqueueing work will generally start
+ *     the service immediately, regardless of whether the device is dozing or in other
+ *     conditions.  When running as a Job, it will be subject to standard JobScheduler
+ *     policies for a Job with a {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)}
+ *     of 0: the job will not run while the device is dozing, it may get delayed more than
+ *     a service if the device is under strong memory pressure with lots of demand to run
+ *     jobs.</p></li>
+ *     <li><p>When running as a pre-O service, the normal service execution semantics apply:
+ *     the service can run indefinitely, though the longer it runs the more likely the system
+ *     will be to outright kill its process, and under memory pressure one should expect
+ *     the process to be killed even of recently started services.  When running as a Job,
+ *     the typical {@link android.app.job.JobService} execution time limit will apply, after
+ *     which the job will be stopped (cleanly, not by killing the process) and rescheduled
+ *     to continue its execution later.  Job are generally not killed when the system is
+ *     under memory pressure, since the number of concurrent jobs is adjusted based on the
+ *     memory state of the device.</p></li>
+ * </ul>
+ *
+ * <p>Here is an example implementation of this class:</p>
+ *
+ * {@sample frameworks/support/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentService.java
+ *      complete}
+ */
+public abstract class JobIntentService extends Service {
+    static final String TAG = "JobIntentService";
+
+    static final boolean DEBUG = false;
+
+    CompatJobEngine mJobImpl;
+    WorkEnqueuer mCompatWorkEnqueuer;
+    CommandProcessor mCurProcessor;
+    boolean mInterruptIfStopped = false;
+    boolean mStopped = false;
+
+    final ArrayList<CompatWorkItem> mCompatQueue;
+
+    static final Object sLock = new Object();
+    static final HashMap<ComponentName, WorkEnqueuer> sClassWorkEnqueuer = new HashMap<>();
+
+    /**
+     * Base class for the target service we can deliver work to and the implementation of
+     * how to deliver that work.
+     */
+    abstract static class WorkEnqueuer {
+        final ComponentName mComponentName;
+
+        boolean mHasJobId;
+        int mJobId;
+
+        WorkEnqueuer(Context context, ComponentName cn) {
+            mComponentName = cn;
+        }
+
+        void ensureJobId(int jobId) {
+            if (!mHasJobId) {
+                mHasJobId = true;
+                mJobId = jobId;
+            } else if (mJobId != jobId) {
+                throw new IllegalArgumentException("Given job ID " + jobId
+                        + " is different than previous " + mJobId);
+            }
+        }
+
+        abstract void enqueueWork(Intent work);
+
+        public void serviceCreated() {
+        }
+
+        public void serviceStartReceived() {
+        }
+
+        public void serviceDestroyed() {
+        }
+    }
+
+    /**
+     * Get rid of lint warnings about API levels.
+     */
+    interface CompatJobEngine {
+        IBinder compatGetBinder();
+        GenericWorkItem dequeueWork();
+    }
+
+    /**
+     * An implementation of WorkEnqueuer that works for pre-O (raw Service-based).
+     */
+    static final class CompatWorkEnqueuer extends WorkEnqueuer {
+        private final Context mContext;
+        private final PowerManager.WakeLock mLaunchWakeLock;
+        private final PowerManager.WakeLock mRunWakeLock;
+        boolean mLaunchingService;
+        boolean mServiceRunning;
+
+        CompatWorkEnqueuer(Context context, ComponentName cn) {
+            super(context, cn);
+            mContext = context.getApplicationContext();
+            // Make wake locks.  We need two, because the launch wake lock wants to have
+            // a timeout, and the system does not do the right thing if you mix timeout and
+            // non timeout (or even changing the timeout duration) in one wake lock.
+            PowerManager pm = ((PowerManager) context.getSystemService(Context.POWER_SERVICE));
+            mLaunchWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    cn.getClassName() + ":launch");
+            mLaunchWakeLock.setReferenceCounted(false);
+            mRunWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    cn.getClassName() + ":run");
+            mRunWakeLock.setReferenceCounted(false);
+        }
+
+        @Override
+        void enqueueWork(Intent work) {
+            Intent intent = new Intent(work);
+            intent.setComponent(mComponentName);
+            if (DEBUG) Log.d(TAG, "Starting service for work: " + work);
+            if (mContext.startService(intent) != null) {
+                synchronized (this) {
+                    if (!mLaunchingService) {
+                        mLaunchingService = true;
+                        if (!mServiceRunning) {
+                            // If the service is not already holding the wake lock for
+                            // itself, acquire it now to keep the system running until
+                            // we get this work dispatched.  We use a timeout here to
+                            // protect against whatever problem may cause is to not get
+                            // the work.
+                            mLaunchWakeLock.acquire(60 * 1000);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void serviceCreated() {
+            synchronized (this) {
+                // We hold the wake lock as long as the service is running.
+                if (!mServiceRunning) {
+                    mServiceRunning = true;
+                    mRunWakeLock.acquire();
+                    mLaunchWakeLock.release();
+                }
+            }
+        }
+
+        @Override
+        public void serviceStartReceived() {
+            synchronized (this) {
+                // Once we have started processing work, we can count whatever last
+                // enqueueWork() that happened as handled.
+                mLaunchingService = false;
+            }
+        }
+
+        @Override
+        public void serviceDestroyed() {
+            synchronized (this) {
+                // If we are transitioning back to a wakelock with a timeout, do the same
+                // as if we had enqueued work without the service running.
+                if (mLaunchingService) {
+                    mLaunchWakeLock.acquire(60 * 1000);
+                }
+                mServiceRunning = false;
+                mRunWakeLock.release();
+            }
+        }
+    }
+
+    /**
+     * Implementation of a JobServiceEngine for interaction with JobIntentService.
+     */
+    @RequiresApi(26)
+    static final class JobServiceEngineImpl extends JobServiceEngine
+            implements JobIntentService.CompatJobEngine {
+        static final String TAG = "JobServiceEngineImpl";
+
+        static final boolean DEBUG = false;
+
+        final JobIntentService mService;
+        final Object mLock = new Object();
+        JobParameters mParams;
+
+        final class WrapperWorkItem implements JobIntentService.GenericWorkItem {
+            final JobWorkItem mJobWork;
+
+            WrapperWorkItem(JobWorkItem jobWork) {
+                mJobWork = jobWork;
+            }
+
+            @Override
+            public Intent getIntent() {
+                return mJobWork.getIntent();
+            }
+
+            @Override
+            public void complete() {
+                synchronized (mLock) {
+                    if (mParams != null) {
+                        mParams.completeWork(mJobWork);
+                    }
+                }
+            }
+        }
+
+        JobServiceEngineImpl(JobIntentService service) {
+            super(service);
+            mService = service;
+        }
+
+        @Override
+        public IBinder compatGetBinder() {
+            return getBinder();
+        }
+
+        @Override
+        public boolean onStartJob(JobParameters params) {
+            if (DEBUG) Log.d(TAG, "onStartJob: " + params);
+            mParams = params;
+            // We can now start dequeuing work!
+            mService.ensureProcessorRunningLocked();
+            return true;
+        }
+
+        @Override
+        public boolean onStopJob(JobParameters params) {
+            if (DEBUG) Log.d(TAG, "onStartJob: " + params);
+            boolean result = mService.doStopCurrentWork();
+            synchronized (mLock) {
+                // Once we return, the job is stopped, so its JobParameters are no
+                // longer valid and we should not be doing anything with them.
+                mParams = null;
+            }
+            return result;
+        }
+
+        /**
+         * Dequeue some work.
+         */
+        @Override
+        public JobIntentService.GenericWorkItem dequeueWork() {
+            JobWorkItem work;
+            synchronized (mLock) {
+                if (mParams == null) {
+                    return null;
+                }
+                work = mParams.dequeueWork();
+            }
+            if (work != null) {
+                work.getIntent().setExtrasClassLoader(mService.getClassLoader());
+                return new WrapperWorkItem(work);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    @RequiresApi(26)
+    static final class JobWorkEnqueuer extends JobIntentService.WorkEnqueuer {
+        private final JobInfo mJobInfo;
+        private final JobScheduler mJobScheduler;
+
+        JobWorkEnqueuer(Context context, ComponentName cn, int jobId) {
+            super(context, cn);
+            ensureJobId(jobId);
+            JobInfo.Builder b = new JobInfo.Builder(jobId, mComponentName);
+            mJobInfo = b.setOverrideDeadline(0).build();
+            mJobScheduler = (JobScheduler) context.getApplicationContext().getSystemService(
+                    Context.JOB_SCHEDULER_SERVICE);
+        }
+
+        @Override
+        void enqueueWork(Intent work) {
+            if (DEBUG) Log.d(TAG, "Enqueueing work: " + work);
+            mJobScheduler.enqueue(mJobInfo, new JobWorkItem(work));
+        }
+    }
+
+    /**
+     * Abstract definition of an item of work that is being dispatched.
+     */
+    interface GenericWorkItem {
+        Intent getIntent();
+        void complete();
+    }
+
+    /**
+     * An implementation of GenericWorkItem that dispatches work for pre-O platforms: intents
+     * received through a raw service's onStartCommand.
+     */
+    final class CompatWorkItem implements GenericWorkItem {
+        final Intent mIntent;
+        final int mStartId;
+
+        CompatWorkItem(Intent intent, int startId) {
+            mIntent = intent;
+            mStartId = startId;
+        }
+
+        @Override
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        @Override
+        public void complete() {
+            if (DEBUG) Log.d(TAG, "Stopping self: #" + mStartId);
+            stopSelf(mStartId);
+        }
+    }
+
+    /**
+     * This is a task to dequeue and process work in the background.
+     */
+    final class CommandProcessor extends AsyncTask<Void, Void, Void> {
+        @Override
+        protected Void doInBackground(Void... params) {
+            GenericWorkItem work;
+
+            if (DEBUG) Log.d(TAG, "Starting to dequeue work...");
+
+            while ((work = dequeueWork()) != null) {
+                if (DEBUG) Log.d(TAG, "Processing next work: " + work);
+                onHandleWork(work.getIntent());
+                if (DEBUG) Log.d(TAG, "Completing work: " + work);
+                work.complete();
+            }
+
+            if (DEBUG) Log.d(TAG, "Done processing work!");
+
+            return null;
+        }
+
+        @Override
+        protected void onCancelled(Void aVoid) {
+            processorFinished();
+        }
+
+        @Override
+        protected void onPostExecute(Void aVoid) {
+            processorFinished();
+        }
+    }
+
+    /**
+     * Default empty constructor.
+     */
+    public JobIntentService() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            mCompatQueue = null;
+        } else {
+            mCompatQueue = new ArrayList<>();
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (DEBUG) Log.d(TAG, "CREATING: " + this);
+        if (Build.VERSION.SDK_INT >= 26) {
+            mJobImpl = new JobServiceEngineImpl(this);
+            mCompatWorkEnqueuer = null;
+        } else {
+            mJobImpl = null;
+            ComponentName cn = new ComponentName(this, this.getClass());
+            mCompatWorkEnqueuer = getWorkEnqueuer(this, cn, false, 0);
+            mCompatWorkEnqueuer.serviceCreated();
+        }
+    }
+
+    /**
+     * Processes start commands when running as a pre-O service, enqueueing them to be
+     * later dispatched in {@link #onHandleWork(Intent)}.
+     */
+    @Override
+    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
+        if (mCompatQueue != null) {
+            mCompatWorkEnqueuer.serviceStartReceived();
+            if (DEBUG) Log.d(TAG, "Received compat start command #" + startId + ": " + intent);
+            synchronized (mCompatQueue) {
+                mCompatQueue.add(new CompatWorkItem(intent != null ? intent : new Intent(),
+                        startId));
+                ensureProcessorRunningLocked();
+            }
+            return START_REDELIVER_INTENT;
+        } else {
+            if (DEBUG) Log.d(TAG, "Ignoring start command: " + intent);
+            return START_NOT_STICKY;
+        }
+    }
+
+    /**
+     * Returns the IBinder for the {@link android.app.job.JobServiceEngine} when
+     * running as a JobService on O and later platforms.
+     */
+    @Override
+    public IBinder onBind(@NonNull Intent intent) {
+        if (mJobImpl != null) {
+            IBinder engine = mJobImpl.compatGetBinder();
+            if (DEBUG) Log.d(TAG, "Returning engine: " + engine);
+            return engine;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mCompatWorkEnqueuer != null) {
+            mCompatWorkEnqueuer.serviceDestroyed();
+        }
+    }
+
+    /**
+     * Call this to enqueue work for your subclass of {@link JobIntentService}.  This will
+     * either directly start the service (when running on pre-O platforms) or enqueue work
+     * for it as a job (when running on O and later).  In either case, a wake lock will be
+     * held for you to ensure you continue running.  The work you enqueue will ultimately
+     * appear at {@link #onHandleWork(Intent)}.
+     *
+     * @param context Context this is being called from.
+     * @param cls The concrete class the work should be dispatched to (this is the class that
+     * is published in your manifest).
+     * @param jobId A unique job ID for scheduling; must be the same value for all work
+     * enqueued for the same class.
+     * @param work The Intent of work to enqueue.
+     */
+    public static void enqueueWork(@NonNull Context context, @NonNull Class cls, int jobId,
+            @NonNull Intent work) {
+        enqueueWork(context, new ComponentName(context, cls), jobId, work);
+    }
+
+    /**
+     * Like {@link #enqueueWork(Context, Class, int, Intent)}, but supplies a ComponentName
+     * for the service to interact with instead of its class.
+     *
+     * @param context Context this is being called from.
+     * @param component The published ComponentName of the class this work should be
+     * dispatched to.
+     * @param jobId A unique job ID for scheduling; must be the same value for all work
+     * enqueued for the same class.
+     * @param work The Intent of work to enqueue.
+     */
+    public static void enqueueWork(@NonNull Context context, @NonNull ComponentName component,
+            int jobId, @NonNull Intent work) {
+        if (work == null) {
+            throw new IllegalArgumentException("work must not be null");
+        }
+        synchronized (sLock) {
+            WorkEnqueuer we = getWorkEnqueuer(context, component, true, jobId);
+            we.ensureJobId(jobId);
+            we.enqueueWork(work);
+        }
+    }
+
+    static WorkEnqueuer getWorkEnqueuer(Context context, ComponentName cn, boolean hasJobId,
+            int jobId) {
+        WorkEnqueuer we = sClassWorkEnqueuer.get(cn);
+        if (we == null) {
+            if (Build.VERSION.SDK_INT >= 26) {
+                if (!hasJobId) {
+                    throw new IllegalArgumentException("Can't be here without a job id");
+                }
+                we = new JobWorkEnqueuer(context, cn, jobId);
+            } else {
+                we = new CompatWorkEnqueuer(context, cn);
+            }
+            sClassWorkEnqueuer.put(cn, we);
+        }
+        return we;
+    }
+
+    /**
+     * Called serially for each work dispatched to and processed by the service.  This
+     * method is called on a background thread, so you can do long blocking operations
+     * here.  Upon returning, that work will be considered complete and either the next
+     * pending work dispatched here or the overall service destroyed now that it has
+     * nothing else to do.
+     *
+     * <p>Be aware that when running as a job, you are limited by the maximum job execution
+     * time and any single or total sequential items of work that exceeds that limit will
+     * cause the service to be stopped while in progress and later restarted with the
+     * last unfinished work.  (There is currently no limit on execution duration when
+     * running as a pre-O plain Service.)</p>
+     *
+     * @param intent The intent describing the work to now be processed.
+     */
+    protected abstract void onHandleWork(@NonNull Intent intent);
+
+    /**
+     * Control whether code executing in {@link #onHandleWork(Intent)} will be interrupted
+     * if the job is stopped.  By default this is false.  If called and set to true, any
+     * time {@link #onStopCurrentWork()} is called, the class will first call
+     * {@link AsyncTask#cancel(boolean) AsyncTask.cancel(true)} to interrupt the running
+     * task.
+     *
+     * @param interruptIfStopped Set to true to allow the system to interrupt actively
+     * running work.
+     */
+    public void setInterruptIfStopped(boolean interruptIfStopped) {
+        mInterruptIfStopped = interruptIfStopped;
+    }
+
+    /**
+     * Returns true if {@link #onStopCurrentWork()} has been called.  You can use this,
+     * while executing your work, to see if it should be stopped.
+     */
+    public boolean isStopped() {
+        return mStopped;
+    }
+
+    /**
+     * This will be called if the JobScheduler has decided to stop this job.  The job for
+     * this service does not have any constraints specified, so this will only generally happen
+     * if the service exceeds the job's maximum execution time.
+     *
+     * @return True to indicate to the JobManager whether you'd like to reschedule this work,
+     * false to drop this and all following work. Regardless of the value returned, your service
+     * must stop executing or the system will ultimately kill it.  The default implementation
+     * returns true, and that is most likely what you want to return as well (so no work gets
+     * lost).
+     */
+    public boolean onStopCurrentWork() {
+        return true;
+    }
+
+    boolean doStopCurrentWork() {
+        if (mCurProcessor != null) {
+            mCurProcessor.cancel(mInterruptIfStopped);
+        }
+        mStopped = true;
+        return onStopCurrentWork();
+    }
+
+    void ensureProcessorRunningLocked() {
+        if (mCurProcessor == null) {
+            mCurProcessor = new CommandProcessor();
+            if (DEBUG) Log.d(TAG, "Starting processor: " + mCurProcessor);
+            mCurProcessor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }
+    }
+
+    void processorFinished() {
+        if (mCompatQueue != null) {
+            synchronized (mCompatQueue) {
+                mCurProcessor = null;
+                // The async task has finished, but we may have gotten more work scheduled in the
+                // meantime.  If so, we need to restart the new processor to execute it.  If there
+                // is no more work at this point, either the service is in the process of being
+                // destroyed (because we called stopSelf on the last intent started for it), or
+                // someone has already called startService with a new Intent that will be
+                // arriving shortly.  In either case, we want to just leave the service
+                // waiting -- either to get destroyed, or get a new onStartCommand() callback
+                // which will then kick off a new processor.
+                if (mCompatQueue != null && mCompatQueue.size() > 0) {
+                    ensureProcessorRunningLocked();
+                }
+            }
+        }
+    }
+
+    GenericWorkItem dequeueWork() {
+        if (mJobImpl != null) {
+            return mJobImpl.dequeueWork();
+        } else {
+            synchronized (mCompatQueue) {
+                if (mCompatQueue.size() > 0) {
+                    return mCompatQueue.remove(0);
+                } else {
+                    return null;
+                }
+            }
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/app/NotificationCompat.java b/compat/java/android/support/v4/app/NotificationCompat.java
index 5d08191..8d370ab 100644
--- a/compat/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/java/android/support/v4/app/NotificationCompat.java
@@ -17,37 +17,55 @@
 package android.support.v4.app;
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.app.Activity;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.support.annotation.ColorInt;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
+import android.support.compat.R;
+import android.support.v4.text.BidiFormatter;
 import android.support.v4.view.GravityCompat;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
+import android.util.TypedValue;
 import android.view.Gravity;
+import android.view.View;
 import android.widget.RemoteViews;
 
 import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * Helper for accessing features in {@link android.app.Notification}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.Notification}.
  */
 public class NotificationCompat {
 
@@ -385,6 +403,32 @@
     public static final String EXTRA_MESSAGES = "android.messages";
 
     /**
+     * Keys into the {@link #getExtras} Bundle: the audio contents of this notification.
+     *
+     * This is for use when rendering the notification on an audio-focused interface;
+     * the audio contents are a complete sound sample that contains the contents/body of the
+     * notification. This may be used in substitute of a Text-to-Speech reading of the
+     * notification. For example if the notification represents a voice message this should point
+     * to the audio of that message.
+     *
+     * The data stored under this key should be a String representation of a Uri that contains the
+     * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
+     *
+     * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
+     * has a field for holding data URI. That field can be used for audio.
+     * See {@code Message#setData}.
+     *
+     * Example usage:
+     * <pre>
+     * {@code
+     * NotificationCompat.Builder myBuilder = (build your Notification as normal);
+     * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
+     * }
+     * </pre>
+     */
+    public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+
+    /**
      * Value of {@link Notification#color} equal to 0 (also known as
      * {@link android.graphics.Color#TRANSPARENT Color.TRANSPARENT}),
      * telling the system not to decorate this notification with any special color but instead use
@@ -422,67 +466,67 @@
     /**
      * Notification category: incoming call (voice or video) or similar synchronous communication request.
      */
-    public static final String CATEGORY_CALL = NotificationCompatApi21.CATEGORY_CALL;
+    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
 
     /**
      * Notification category: incoming direct message (SMS, instant message, etc.).
      */
-    public static final String CATEGORY_MESSAGE = NotificationCompatApi21.CATEGORY_MESSAGE;
+    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
 
     /**
      * Notification category: asynchronous bulk message (email).
      */
-    public static final String CATEGORY_EMAIL = NotificationCompatApi21.CATEGORY_EMAIL;
+    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
 
     /**
      * Notification category: calendar event.
      */
-    public static final String CATEGORY_EVENT = NotificationCompatApi21.CATEGORY_EVENT;
+    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
 
     /**
      * Notification category: promotion or advertisement.
      */
-    public static final String CATEGORY_PROMO = NotificationCompatApi21.CATEGORY_PROMO;
+    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
 
     /**
      * Notification category: alarm or timer.
      */
-    public static final String CATEGORY_ALARM = NotificationCompatApi21.CATEGORY_ALARM;
+    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
 
     /**
      * Notification category: progress of a long-running background operation.
      */
-    public static final String CATEGORY_PROGRESS = NotificationCompatApi21.CATEGORY_PROGRESS;
+    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
 
     /**
      * Notification category: social network or sharing update.
      */
-    public static final String CATEGORY_SOCIAL = NotificationCompatApi21.CATEGORY_SOCIAL;
+    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
 
     /**
      * Notification category: error in background operation or authentication status.
      */
-    public static final String CATEGORY_ERROR = NotificationCompatApi21.CATEGORY_ERROR;
+    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
 
     /**
      * Notification category: media transport control for playback.
      */
-    public static final String CATEGORY_TRANSPORT = NotificationCompatApi21.CATEGORY_TRANSPORT;
+    public static final String CATEGORY_TRANSPORT = Notification.CATEGORY_TRANSPORT;
 
     /**
      * Notification category: system or device status update.  Reserved for system use.
      */
-    public static final String CATEGORY_SYSTEM = NotificationCompatApi21.CATEGORY_SYSTEM;
+    public static final String CATEGORY_SYSTEM = Notification.CATEGORY_SYSTEM;
 
     /**
      * Notification category: indication of running background service.
      */
-    public static final String CATEGORY_SERVICE = NotificationCompatApi21.CATEGORY_SERVICE;
+    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
 
     /**
      * Notification category: user-scheduled reminder.
      */
-    public static final String CATEGORY_REMINDER = NotificationCompatApi23.CATEGORY_REMINDER;
+    public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
 
     /**
      * Notification category: a specific, timely recommendation for a single thing.
@@ -490,27 +534,72 @@
      * want to read next.
      */
     public static final String CATEGORY_RECOMMENDATION =
-            NotificationCompatApi21.CATEGORY_RECOMMENDATION;
+            Notification.CATEGORY_RECOMMENDATION;
 
     /**
      * Notification category: ongoing information about device or contextual status.
      */
-    public static final String CATEGORY_STATUS = NotificationCompatApi21.CATEGORY_STATUS;
+    public static final String CATEGORY_STATUS = Notification.CATEGORY_STATUS;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({BADGE_ICON_NONE, BADGE_ICON_SMALL, BADGE_ICON_LARGE})
+    public @interface BadgeIconType {}
+    /**
+     * If this notification is being shown as a badge, always show as a number.
+     */
+    public static final int BADGE_ICON_NONE = Notification.BADGE_ICON_NONE;
+
+    /**
+     * If this notification is being shown as a badge, use the icon provided to
+     * {@link Builder#setSmallIcon(int)} to represent this notification.
+     */
+    public static final int BADGE_ICON_SMALL = Notification.BADGE_ICON_SMALL;
+
+    /**
+     * If this notification is being shown as a badge, use the icon provided to
+     * {@link Builder#setLargeIcon(Bitmap) to represent this notification.
+     */
+    public static final int BADGE_ICON_LARGE = Notification.BADGE_ICON_LARGE;
+
+    /**
+     * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
+     * group with sound or vibration ought to make sound or vibrate (respectively), so this
+     * notification will not be muted when it is in a group.
+     */
+    public static final int GROUP_ALERT_ALL = 0;
+
+    /**
+     * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
+     * notification in a group should be silenced (no sound or vibration) even if they would
+     * otherwise make sound or vibrate. Use this constant to mute this notification if this
+     * notification is a group child.
+     *
+     * <p> For example, you might want to use this constant if you post a number of children
+     * notifications at once (say, after a periodic sync), and only need to notify the user
+     * audibly once.
+     */
+    public static final int GROUP_ALERT_SUMMARY = 1;
+
+    /**
+     * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
+     * notification in a group should be silenced (no sound or vibration) even if they would
+     * otherwise make sound or vibrate. Use this constant
+     * to mute this notification if this notification is a group summary.
+     *
+     * <p>For example, you might want to use this constant if only the children notifications
+     * in your group have content and the summary is only used to visually group notifications.
+     */
+    public static final int GROUP_ALERT_CHILDREN = 2;
 
     static final NotificationCompatImpl IMPL;
 
     interface NotificationCompatImpl {
-        public Notification build(Builder b, BuilderExtender extender);
-        public Bundle getExtras(Notification n);
-        public int getActionCount(Notification n);
-        public Action getAction(Notification n, int actionIndex);
-        public Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables);
-        public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions);
-        public String getCategory(Notification n);
-        public boolean getLocalOnly(Notification n);
-        public String getGroup(Notification n);
-        public boolean isGroupSummary(Notification n);
-        public String getSortKey(Notification n);
+        Notification build(Builder b, BuilderExtender extender);
+        Action getAction(Notification n, int actionIndex);
+        Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables);
+        ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions);
         Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc);
         NotificationCompatBase.UnreadConversation getUnreadConversationFromBundle(
                 Bundle b, NotificationCompatBase.UnreadConversation.Factory factory,
@@ -518,45 +607,92 @@
     }
 
     /**
-     * Interface for appcompat to extend v4 builder with media style.
+     * Interface for appcompat to extend compat builder with media style.
      *
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
     protected static class BuilderExtender {
         public Notification build(Builder b, NotificationBuilderWithBuilderAccessor builder) {
+            RemoteViews styleContentView = b.mStyle != null
+                    ? b.mStyle.makeContentView(builder)
+                    : null;
             Notification n = builder.build();
-            if (b.mContentView != null) {
+            if (styleContentView != null) {
+                n.contentView = styleContentView;
+            } else if (b.mContentView != null) {
                 n.contentView = b.mContentView;
             }
+            if (Build.VERSION.SDK_INT >= 16 && b.mStyle != null) {
+                RemoteViews styleBigContentView = b.mStyle.makeBigContentView(builder);
+                if (styleBigContentView != null) {
+                    n.bigContentView = styleBigContentView;
+                }
+            }
+            if (Build.VERSION.SDK_INT >= 21 && b.mStyle != null) {
+                RemoteViews styleHeadsUpContentView = b.mStyle.makeHeadsUpContentView(builder);
+                if (styleHeadsUpContentView != null) {
+                    n.headsUpContentView = styleHeadsUpContentView;
+                }
+            }
             return n;
         }
     }
 
-    static class NotificationCompatImplBase implements NotificationCompatImpl {
+    static class NotificationCompatBaseImpl implements NotificationCompatImpl {
+
+        public static class BuilderBase implements NotificationBuilderWithBuilderAccessor {
+
+            private Notification.Builder mBuilder;
+
+            BuilderBase(Context context, Notification n, CharSequence contentTitle,
+                    CharSequence contentText, CharSequence contentInfo, RemoteViews tickerView,
+                    int number, PendingIntent contentIntent, PendingIntent fullScreenIntent,
+                    Bitmap largeIcon, int progressMax, int progress,
+                    boolean progressIndeterminate) {
+                mBuilder = new Notification.Builder(context)
+                        .setWhen(n.when)
+                        .setSmallIcon(n.icon, n.iconLevel)
+                        .setContent(n.contentView)
+                        .setTicker(n.tickerText, tickerView)
+                        .setSound(n.sound, n.audioStreamType)
+                        .setVibrate(n.vibrate)
+                        .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
+                        .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+                        .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
+                        .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
+                        .setDefaults(n.defaults)
+                        .setContentTitle(contentTitle)
+                        .setContentText(contentText)
+                        .setContentInfo(contentInfo)
+                        .setContentIntent(contentIntent)
+                        .setDeleteIntent(n.deleteIntent)
+                        .setFullScreenIntent(fullScreenIntent,
+                                (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
+                        .setLargeIcon(largeIcon)
+                        .setNumber(number)
+                        .setProgress(progressMax, progress, progressIndeterminate);
+            }
+
+            @Override
+            public Notification.Builder getBuilder() {
+                return mBuilder;
+            }
+
+            @Override
+            public Notification build() {
+                return mBuilder.getNotification();
+            }
+        }
+
         @Override
         public Notification build(Builder b, BuilderExtender extender) {
-            Notification result = b.mNotification;
-            result = NotificationCompatBase.add(result, b.mContext,
-                    b.resolveTitle(), b.resolveText(), b.mContentIntent, b.mFullScreenIntent);
-            // translate high priority requests into legacy flag
-            if (b.mPriority > PRIORITY_DEFAULT) {
-                result.flags |= FLAG_HIGH_PRIORITY;
-            }
-            if (b.mContentView != null) {
-                result.contentView = b.mContentView;
-            }
-            return result;
-        }
-
-        @Override
-        public Bundle getExtras(Notification n) {
-            return null;
-        }
-
-        @Override
-        public int getActionCount(Notification n) {
-            return 0;
+            BuilderBase builder =
+                    new BuilderBase(b.mContext, b.mNotification,
+                            b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
+                            b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
+                            b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
+            return extender.build(b, builder);
         }
 
         @Override
@@ -565,8 +701,7 @@
         }
 
         @Override
-        public Action[] getActionsFromParcelableArrayList(
-                ArrayList<Parcelable> parcelables) {
+        public Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables) {
             return null;
         }
 
@@ -576,31 +711,6 @@
         }
 
         @Override
-        public String getCategory(Notification n) {
-            return null;
-        }
-
-        @Override
-        public boolean getLocalOnly(Notification n) {
-            return false;
-        }
-
-        @Override
-        public String getGroup(Notification n) {
-            return null;
-        }
-
-        @Override
-        public boolean isGroupSummary(Notification n) {
-            return false;
-        }
-
-        @Override
-        public String getSortKey(Notification n) {
-            return null;
-        }
-
-        @Override
         public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
             return null;
         }
@@ -613,42 +723,20 @@
         }
     }
 
-    static class NotificationCompatImplHoneycomb extends NotificationCompatImplBase {
-        @Override
-        public Notification build(Builder b, BuilderExtender extender) {
-            Notification notification = NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
-                    b.resolveTitle(), b.resolveText(), b.mContentInfo, b.mTickerView,
-                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
-            if (b.mContentView != null) {
-                notification.contentView = b.mContentView;
-            }
-            return notification;
-        }
-    }
-
-    static class NotificationCompatImplIceCreamSandwich extends NotificationCompatImplBase {
-        @Override
-        public Notification build(Builder b, BuilderExtender extender) {
-            NotificationCompatIceCreamSandwich.Builder builder =
-                    new NotificationCompatIceCreamSandwich.Builder(b.mContext, b.mNotification,
-                            b.resolveTitle(), b.resolveText(), b.mContentInfo, b.mTickerView,
-                            b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
-                            b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
-            return extender.build(b, builder);
-        }
-    }
-
-    static class NotificationCompatImplJellybean extends NotificationCompatImplBase {
+    @RequiresApi(16)
+    static class NotificationCompatApi16Impl extends NotificationCompatBaseImpl {
         @Override
         public Notification build(Builder b, BuilderExtender extender) {
             NotificationCompatJellybean.Builder builder = new NotificationCompatJellybean.Builder(
-                    b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo,
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
                     b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
                     b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
                     b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mExtras,
                     b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView);
             addActionsToBuilder(builder, b.mActions);
-            addStyleToBuilderJellybean(builder, b.mStyle);
+            if (b.mStyle != null) {
+                b.mStyle.apply(builder);
+            }
             Notification notification = extender.build(b, builder);
             if (b.mStyle != null) {
                 Bundle extras = getExtras(notification);
@@ -660,16 +748,6 @@
         }
 
         @Override
-        public Bundle getExtras(Notification n) {
-            return NotificationCompatJellybean.getExtras(n);
-        }
-
-        @Override
-        public int getActionCount(Notification n) {
-            return NotificationCompatJellybean.getActionCount(n);
-        }
-
-        @Override
         public Action getAction(Notification n, int actionIndex) {
             return (Action) NotificationCompatJellybean.getAction(n, actionIndex, Action.FACTORY,
                     RemoteInput.FACTORY);
@@ -687,91 +765,48 @@
                 Action[] actions) {
             return NotificationCompatJellybean.getParcelableArrayListForActions(actions);
         }
-
-        @Override
-        public boolean getLocalOnly(Notification n) {
-            return NotificationCompatJellybean.getLocalOnly(n);
-        }
-
-        @Override
-        public String getGroup(Notification n) {
-            return NotificationCompatJellybean.getGroup(n);
-        }
-
-        @Override
-        public boolean isGroupSummary(Notification n) {
-            return NotificationCompatJellybean.isGroupSummary(n);
-        }
-
-        @Override
-        public String getSortKey(Notification n) {
-            return NotificationCompatJellybean.getSortKey(n);
-        }
     }
 
-    static class NotificationCompatImplKitKat extends NotificationCompatImplJellybean {
+    @RequiresApi(19)
+    static class NotificationCompatApi19Impl extends NotificationCompatApi16Impl {
         @Override
         public Notification build(Builder b, BuilderExtender extender) {
             NotificationCompatKitKat.Builder builder = new NotificationCompatKitKat.Builder(
-                    b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo,
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
                     b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
                     b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
                     b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly,
                     b.mPeople, b.mExtras, b.mGroupKey, b.mGroupSummary, b.mSortKey,
                     b.mContentView, b.mBigContentView);
             addActionsToBuilder(builder, b.mActions);
-            addStyleToBuilderJellybean(builder, b.mStyle);
+            if (b.mStyle != null) {
+                b.mStyle.apply(builder);
+            }
             return extender.build(b, builder);
         }
 
         @Override
-        public Bundle getExtras(Notification n) {
-            return NotificationCompatKitKat.getExtras(n);
-        }
-
-        @Override
-        public int getActionCount(Notification n) {
-            return NotificationCompatKitKat.getActionCount(n);
-        }
-
-        @Override
         public Action getAction(Notification n, int actionIndex) {
             return (Action) NotificationCompatKitKat.getAction(n, actionIndex, Action.FACTORY,
                     RemoteInput.FACTORY);
         }
-
-        @Override
-        public boolean getLocalOnly(Notification n) {
-            return NotificationCompatKitKat.getLocalOnly(n);
-        }
-
-        @Override
-        public String getGroup(Notification n) {
-            return NotificationCompatKitKat.getGroup(n);
-        }
-
-        @Override
-        public boolean isGroupSummary(Notification n) {
-            return NotificationCompatKitKat.isGroupSummary(n);
-        }
-
-        @Override
-        public String getSortKey(Notification n) {
-            return NotificationCompatKitKat.getSortKey(n);
-        }
     }
 
-    static class NotificationCompatImplApi20 extends NotificationCompatImplKitKat {
+    @RequiresApi(20)
+    static class NotificationCompatApi20Impl extends NotificationCompatApi19Impl {
         @Override
         public Notification build(Builder b, BuilderExtender extender) {
             NotificationCompatApi20.Builder builder = new NotificationCompatApi20.Builder(
-                    b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo,
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
                     b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
                     b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
                     b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mPeople, b.mExtras,
-                    b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView);
+                    b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView,
+                    b.mGroupAlertBehavior);
             addActionsToBuilder(builder, b.mActions);
-            addStyleToBuilderJellybean(builder, b.mStyle);
+            if (b.mStyle != null) {
+                b.mStyle.apply(builder);
+            }
             Notification notification = extender.build(b, builder);
             if (b.mStyle != null) {
                 b.mStyle.addCompatExtras(getExtras(notification));
@@ -797,41 +832,24 @@
                 Action[] actions) {
             return NotificationCompatApi20.getParcelableArrayListForActions(actions);
         }
-
-        @Override
-        public boolean getLocalOnly(Notification n) {
-            return NotificationCompatApi20.getLocalOnly(n);
-        }
-
-        @Override
-        public String getGroup(Notification n) {
-            return NotificationCompatApi20.getGroup(n);
-        }
-
-        @Override
-        public boolean isGroupSummary(Notification n) {
-            return NotificationCompatApi20.isGroupSummary(n);
-        }
-
-        @Override
-        public String getSortKey(Notification n) {
-            return NotificationCompatApi20.getSortKey(n);
-        }
     }
 
-    static class NotificationCompatImplApi21 extends NotificationCompatImplApi20 {
+    @RequiresApi(21)
+    static class NotificationCompatApi21Impl extends NotificationCompatApi20Impl {
         @Override
         public Notification build(Builder b, BuilderExtender extender) {
             NotificationCompatApi21.Builder builder = new NotificationCompatApi21.Builder(
-                    b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo,
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
                     b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
                     b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
                     b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
                     b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
                     b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView,
-                    b.mHeadsUpContentView);
+                    b.mHeadsUpContentView, b.mGroupAlertBehavior);
             addActionsToBuilder(builder, b.mActions);
-            addStyleToBuilderJellybean(builder, b.mStyle);
+            if (b.mStyle != null) {
+                b.mStyle.apply(builder);
+            }
             Notification notification = extender.build(b, builder);
             if (b.mStyle != null) {
                 b.mStyle.addCompatExtras(getExtras(notification));
@@ -840,11 +858,6 @@
         }
 
         @Override
-        public String getCategory(Notification notif) {
-            return NotificationCompatApi21.getCategory(notif);
-        }
-
-        @Override
         public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
             return NotificationCompatApi21.getBundleForUnreadConversation(uc);
         }
@@ -858,7 +871,8 @@
         }
     }
 
-    static class NotificationCompatImplApi24 extends NotificationCompatImplApi21 {
+    @RequiresApi(24)
+    static class NotificationCompatApi24Impl extends NotificationCompatApi21Impl {
         @Override
         public Notification build(Builder b,
                 BuilderExtender extender) {
@@ -869,9 +883,57 @@
                     b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
                     b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
                     b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mRemoteInputHistory, b.mContentView,
-                    b.mBigContentView, b.mHeadsUpContentView);
+                    b.mBigContentView, b.mHeadsUpContentView, b.mGroupAlertBehavior);
             addActionsToBuilder(builder, b.mActions);
-            addStyleToBuilderApi24(builder, b.mStyle);
+            if (b.mStyle != null) {
+                b.mStyle.apply(builder);
+            }
+            Notification notification = extender.build(b, builder);
+            if (b.mStyle != null) {
+                b.mStyle.addCompatExtras(getExtras(notification));
+            }
+            return notification;
+        }
+
+        @Override
+        public Action getAction(Notification n, int actionIndex) {
+            return (Action) NotificationCompatApi24.getAction(n, actionIndex, Action.FACTORY,
+                    RemoteInput.FACTORY);
+        }
+
+        @Override
+        public Action[] getActionsFromParcelableArrayList(
+                ArrayList<Parcelable> parcelables) {
+            return (Action[]) NotificationCompatApi24.getActionsFromParcelableArrayList(
+                    parcelables, Action.FACTORY, RemoteInput.FACTORY);
+        }
+
+        @Override
+        public ArrayList<Parcelable> getParcelableArrayListForActions(
+                Action[] actions) {
+            return NotificationCompatApi24.getParcelableArrayListForActions(actions);
+        }
+    }
+
+    @RequiresApi(26)
+    static class NotificationCompatApi26Impl extends NotificationCompatApi24Impl {
+        @Override
+        public Notification build(Builder b,
+                                  BuilderExtender extender) {
+            NotificationCompatApi26.Builder builder = new NotificationCompatApi26.Builder(
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
+                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
+                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
+                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
+                    b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
+                    b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mRemoteInputHistory, b.mContentView,
+                    b.mBigContentView, b.mHeadsUpContentView, b.mChannelId, b.mBadgeIcon,
+                    b.mShortcutId, b.mTimeout, b.mColorized, b.mColorizedSet,
+                    b.mGroupAlertBehavior);
+            addActionsToBuilder(builder, b.mActions);
+            if (b.mStyle != null) {
+                b.mStyle.apply(builder);
+            }
             Notification notification = extender.build(b, builder);
             if (b.mStyle != null) {
                 b.mStyle.addCompatExtras(getExtras(notification));
@@ -887,80 +949,21 @@
         }
     }
 
-    static void addStyleToBuilderJellybean(NotificationBuilderWithBuilderAccessor builder,
-            Style style) {
-        if (style != null) {
-            if (style instanceof BigTextStyle) {
-                BigTextStyle bigTextStyle = (BigTextStyle) style;
-                NotificationCompatJellybean.addBigTextStyle(builder,
-                        bigTextStyle.mBigContentTitle,
-                        bigTextStyle.mSummaryTextSet,
-                        bigTextStyle.mSummaryText,
-                        bigTextStyle.mBigText);
-            } else if (style instanceof InboxStyle) {
-                InboxStyle inboxStyle = (InboxStyle) style;
-                NotificationCompatJellybean.addInboxStyle(builder,
-                        inboxStyle.mBigContentTitle,
-                        inboxStyle.mSummaryTextSet,
-                        inboxStyle.mSummaryText,
-                        inboxStyle.mTexts);
-            } else if (style instanceof BigPictureStyle) {
-                BigPictureStyle bigPictureStyle = (BigPictureStyle) style;
-                NotificationCompatJellybean.addBigPictureStyle(builder,
-                        bigPictureStyle.mBigContentTitle,
-                        bigPictureStyle.mSummaryTextSet,
-                        bigPictureStyle.mSummaryText,
-                        bigPictureStyle.mPicture,
-                        bigPictureStyle.mBigLargeIcon,
-                        bigPictureStyle.mBigLargeIconSet);
-            }
-        }
-    }
-
-    static void addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder,
-            Style style) {
-        if (style != null) {
-            if (style instanceof MessagingStyle) {
-                MessagingStyle messagingStyle = (MessagingStyle) style;
-                List<CharSequence> texts = new ArrayList<>();
-                List<Long> timestamps = new ArrayList<>();
-                List<CharSequence> senders = new ArrayList<>();
-                List<String> dataMimeTypes = new ArrayList<>();
-                List<Uri> dataUris = new ArrayList<>();
-
-                for (MessagingStyle.Message message : messagingStyle.mMessages) {
-                    texts.add(message.getText());
-                    timestamps.add(message.getTimestamp());
-                    senders.add(message.getSender());
-                    dataMimeTypes.add(message.getDataMimeType());
-                    dataUris.add(message.getDataUri());
-                }
-                NotificationCompatApi24.addMessagingStyle(builder, messagingStyle.mUserDisplayName,
-                        messagingStyle.mConversationTitle, texts, timestamps, senders,
-                        dataMimeTypes, dataUris);
-            } else {
-                addStyleToBuilderJellybean(builder, style);
-            }
-        }
-    }
-
     static {
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new NotificationCompatImplApi24();
+        if (Build.VERSION.SDK_INT >= 26) {
+            IMPL = new NotificationCompatApi26Impl();
+        } else if (Build.VERSION.SDK_INT >= 24) {
+            IMPL = new NotificationCompatApi24Impl();
         } else if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new NotificationCompatImplApi21();
+            IMPL = new NotificationCompatApi21Impl();
         } else if (Build.VERSION.SDK_INT >= 20) {
-            IMPL = new NotificationCompatImplApi20();
+            IMPL = new NotificationCompatApi20Impl();
         } else if (Build.VERSION.SDK_INT >= 19) {
-            IMPL = new NotificationCompatImplKitKat();
+            IMPL = new NotificationCompatApi19Impl();
         } else if (Build.VERSION.SDK_INT >= 16) {
-            IMPL = new NotificationCompatImplJellybean();
-        } else if (Build.VERSION.SDK_INT >= 14) {
-            IMPL = new NotificationCompatImplIceCreamSandwich();
-        } else if (Build.VERSION.SDK_INT >= 11) {
-            IMPL = new NotificationCompatImplHoneycomb();
+            IMPL = new NotificationCompatApi16Impl();
         } else {
-            IMPL = new NotificationCompatImplBase();
+            IMPL = new NotificationCompatBaseImpl();
         }
     }
 
@@ -1042,6 +1045,8 @@
         @RestrictTo(LIBRARY_GROUP)
         public ArrayList<Action> mActions = new ArrayList<Action>();
         boolean mLocalOnly = false;
+        boolean mColorized;
+        boolean mColorizedSet;
         String mCategory;
         Bundle mExtras;
         int mColor = COLOR_DEFAULT;
@@ -1050,6 +1055,11 @@
         RemoteViews mContentView;
         RemoteViews mBigContentView;
         RemoteViews mHeadsUpContentView;
+        String mChannelId;
+        int mBadgeIcon = BADGE_ICON_NONE;
+        String mShortcutId;
+        long mTimeout;
+        private int mGroupAlertBehavior = GROUP_ALERT_ALL;
 
         /** @hide */
         @RestrictTo(LIBRARY_GROUP)
@@ -1066,9 +1076,12 @@
          * @param context A {@link Context} that will be used to construct the
          *      RemoteViews. The Context will not be held past the lifetime of this
          *      Builder object.
+         * @param channelId The constructed Notification will be posted on this
+         *      NotificationChannel.
          */
-        public Builder(Context context) {
+        public Builder(@NonNull Context context, @NonNull String channelId) {
             mContext = context;
+            mChannelId = channelId;
 
             // Set defaults to match the defaults of a Notification
             mNotification.when = System.currentTimeMillis();
@@ -1078,6 +1091,16 @@
         }
 
         /**
+         * @deprecated use
+         * {@link NotificationCompat.Builder#NotificationCompat.Builder(Context, String)} instead.
+         * All posted Notifications must specify a NotificationChannel Id.
+         */
+        @Deprecated
+        public Builder(Context context) {
+            this(context, null);
+        }
+
+        /**
          * Set the time that the event occurred.  Notifications in the panel are
          * sorted by this time.
          */
@@ -1116,7 +1139,7 @@
          * may return different sizes.  See the UX guidelines for more information on how to
          * design these icons.
          *
-         * @param icon A resource ID in the application's package of the drawble to use.
+         * @param icon A resource ID in the application's package of the drawable to use.
          */
         public Builder setSmallIcon(int icon) {
             mNotification.icon = icon;
@@ -1128,7 +1151,7 @@
          * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
          * LevelListDrawable}.
          *
-         * @param icon A resource ID in the application's package of the drawble to use.
+         * @param icon A resource ID in the application's package of the drawable to use.
          * @param level The level to use for the icon.
          *
          * @see android.graphics.drawable.LevelListDrawable
@@ -1381,6 +1404,30 @@
         }
 
         /**
+         * Set whether this notification should be colorized. When set, the color set with
+         * {@link #setColor(int)} will be used as the background color of this notification.
+         * <p>
+         * This should only be used for high priority ongoing tasks like navigation, an ongoing
+         * call, or other similarly high-priority events for the user.
+         * <p>
+         * For most styles, the coloring will only be applied if the notification is for a
+         * foreground service notification.
+         * <p>
+         * However, for MediaStyle and DecoratedMediaCustomViewStyle notifications
+         * that have a media session attached there is no such requirement.
+         * <p>
+         * Calling this method on any version prior to {@link android.os.Build.VERSION_CODES#O} will
+         * not have an effect on the notification and it won't be colorized.
+         *
+         * @see #setColor(int)
+         */
+        public Builder setColorized(boolean colorize) {
+            mColorized = colorize;
+            mColorizedSet = true;
+            return this;
+        }
+
+        /**
          * Set this flag if you would only like the sound, vibrate
          * and ticker to be played if the notification is not already showing.
          */
@@ -1728,6 +1775,68 @@
         }
 
         /**
+         * Specifies the channel the notification should be delivered on.
+         *
+         * No-op on versions prior to {@link android.os.Build.VERSION_CODES#O} .
+         */
+        public Builder setChannelId(@NonNull String channelId) {
+            mChannelId = channelId;
+            return this;
+        }
+
+        /**
+         * Specifies the time at which this notification should be canceled, if it is not already
+         * canceled.
+         */
+        public Builder setTimeoutAfter(long durationMs) {
+            mTimeout = durationMs;
+            return this;
+        }
+
+        /**
+         * If this notification is duplicative of a Launcher shortcut, sets the
+         * {@link android.support.v4.content.pm.ShortcutInfoCompat#getId() id} of the shortcut, in
+         * case the Launcher wants to hide the shortcut.
+         *
+         * <p><strong>Note:</strong>This field will be ignored by Launchers that don't support
+         * badging or {@link android.support.v4.content.pm.ShortcutManagerCompat shortcuts}.
+         *
+         * @param shortcutId the {@link android.support.v4.content.pm.ShortcutInfoCompat#getId() id}
+         *                   of the shortcut this notification supersedes
+         */
+        public Builder setShortcutId(String shortcutId) {
+            mShortcutId = shortcutId;
+            return this;
+        }
+
+        /**
+         * Sets which icon to display as a badge for this notification.
+         *
+         * <p>Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
+         * {@link #BADGE_ICON_LARGE}.
+         *
+         * <p><strong>Note:</strong> This value might be ignored, for launchers that don't support
+         * badge icons.
+         */
+        public Builder setBadgeIconType(@BadgeIconType int icon) {
+            mBadgeIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the group alert behavior for this notification. Use this method to mute this
+         * notification if alerts for this notification's group should be handled by a different
+         * notification. This is only applicable for notifications that belong to a
+         * {@link #setGroup(String) group}.
+         *
+         * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
+         */
+        public Builder setGroupAlertBehavior(int groupAlertBehavior) {
+            mGroupAlertBehavior = groupAlertBehavior;
+            return this;
+        }
+
+        /**
          * Apply an extender to this notification builder. Extenders may be used to add
          * metadata or change options on this builder.
          */
@@ -1821,27 +1930,6 @@
         public int getColor() {
             return mColor;
         }
-
-
-        /**
-         * @return the text of the notification
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        protected CharSequence resolveText() {
-            return mContentText;
-        }
-
-        /**
-         * @return the title of the notification
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        protected CharSequence resolveTitle() {
-            return mContentTitle;
-        }
     }
 
     /**
@@ -1852,7 +1940,11 @@
      * effect.
      */
     public static abstract class Style {
-        Builder mBuilder;
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        protected Builder mBuilder;
         CharSequence mBigContentTitle;
         CharSequence mSummaryText;
         boolean mSummaryTextSet = false;
@@ -1879,6 +1971,38 @@
          */
         @RestrictTo(LIBRARY_GROUP)
         // TODO: implement for all styles
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
+            return null;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
+            return null;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {
+            return null;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        // TODO: implement for all styles
         public void addCompatExtras(Bundle extras) {
         }
 
@@ -1889,6 +2013,233 @@
         // TODO: implement for all styles
         protected void restoreFromCompatExtras(Bundle extras) {
         }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public RemoteViews applyStandardTemplate(boolean showSmallIcon,
+                int resId, boolean fitIn1U) {
+            Resources res = mBuilder.mContext.getResources();
+            RemoteViews contentView = new RemoteViews(mBuilder.mContext.getPackageName(), resId);
+            boolean showLine3 = false;
+            boolean showLine2 = false;
+
+            boolean minPriority = mBuilder.getPriority() < NotificationCompat.PRIORITY_LOW;
+            if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 21) {
+                // lets color the backgrounds
+                if (minPriority) {
+                    contentView.setInt(R.id.notification_background,
+                            "setBackgroundResource", R.drawable.notification_bg_low);
+                    contentView.setInt(R.id.icon,
+                            "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
+                } else {
+                    contentView.setInt(R.id.notification_background,
+                            "setBackgroundResource", R.drawable.notification_bg);
+                    contentView.setInt(R.id.icon,
+                            "setBackgroundResource", R.drawable.notification_template_icon_bg);
+                }
+            }
+
+            if (mBuilder.mLargeIcon != null) {
+                // On versions before Jellybean, the large icon was shown by SystemUI, so we need
+                // to hide it here.
+                if (Build.VERSION.SDK_INT >= 16) {
+                    contentView.setViewVisibility(R.id.icon, View.VISIBLE);
+                    contentView.setImageViewBitmap(R.id.icon, mBuilder.mLargeIcon);
+                } else {
+                    contentView.setViewVisibility(R.id.icon, View.GONE);
+                }
+                if (showSmallIcon && mBuilder.mNotification.icon != 0) {
+                    int backgroundSize = res.getDimensionPixelSize(
+                            R.dimen.notification_right_icon_size);
+                    int iconSize = backgroundSize - res.getDimensionPixelSize(
+                            R.dimen.notification_small_icon_background_padding) * 2;
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        Bitmap smallBit = createIconWithBackground(
+                                mBuilder.mNotification.icon,
+                                backgroundSize,
+                                iconSize,
+                                mBuilder.getColor());
+                        contentView.setImageViewBitmap(R.id.right_icon, smallBit);
+                    } else {
+                        contentView.setImageViewBitmap(R.id.right_icon, createColoredBitmap(
+                                mBuilder.mNotification.icon, Color.WHITE));
+                    }
+                    contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
+                }
+            } else if (showSmallIcon && mBuilder.mNotification.icon != 0) { // small icon at left
+                contentView.setViewVisibility(R.id.icon, View.VISIBLE);
+                if (Build.VERSION.SDK_INT >= 21) {
+                    int backgroundSize = res.getDimensionPixelSize(
+                            R.dimen.notification_large_icon_width)
+                            - res.getDimensionPixelSize(R.dimen.notification_big_circle_margin);
+                    int iconSize = res.getDimensionPixelSize(
+                            R.dimen.notification_small_icon_size_as_large);
+                    Bitmap smallBit = createIconWithBackground(
+                            mBuilder.mNotification.icon,
+                            backgroundSize,
+                            iconSize,
+                            mBuilder.getColor());
+                    contentView.setImageViewBitmap(R.id.icon, smallBit);
+                } else {
+                    contentView.setImageViewBitmap(R.id.icon, createColoredBitmap(
+                            mBuilder.mNotification.icon, Color.WHITE));
+                }
+            }
+            if (mBuilder.mContentTitle != null) {
+                contentView.setTextViewText(R.id.title, mBuilder.mContentTitle);
+            }
+            if (mBuilder.mContentText != null) {
+                contentView.setTextViewText(R.id.text, mBuilder.mContentText);
+                showLine3 = true;
+            }
+            // If there is a large icon we have a right side
+            boolean hasRightSide = !(Build.VERSION.SDK_INT >= 21) && mBuilder.mLargeIcon != null;
+            if (mBuilder.mContentInfo != null) {
+                contentView.setTextViewText(R.id.info, mBuilder.mContentInfo);
+                contentView.setViewVisibility(R.id.info, View.VISIBLE);
+                showLine3 = true;
+                hasRightSide = true;
+            } else if (mBuilder.mNumber > 0) {
+                final int tooBig = res.getInteger(
+                        R.integer.status_bar_notification_info_maxnum);
+                if (mBuilder.mNumber > tooBig) {
+                    contentView.setTextViewText(R.id.info, ((Resources) res).getString(
+                            R.string.status_bar_notification_info_overflow));
+                } else {
+                    NumberFormat f = NumberFormat.getIntegerInstance();
+                    contentView.setTextViewText(R.id.info, f.format(mBuilder.mNumber));
+                }
+                contentView.setViewVisibility(R.id.info, View.VISIBLE);
+                showLine3 = true;
+                hasRightSide = true;
+            } else {
+                contentView.setViewVisibility(R.id.info, View.GONE);
+            }
+
+            // Need to show three lines? Only allow on Jellybean+
+            if (mBuilder.mSubText != null && Build.VERSION.SDK_INT >= 16) {
+                contentView.setTextViewText(R.id.text, mBuilder.mSubText);
+                if (mBuilder.mContentText != null) {
+                    contentView.setTextViewText(R.id.text2, mBuilder.mContentText);
+                    contentView.setViewVisibility(R.id.text2, View.VISIBLE);
+                    showLine2 = true;
+                } else {
+                    contentView.setViewVisibility(R.id.text2, View.GONE);
+                }
+            }
+
+            // RemoteViews.setViewPadding and RemoteViews.setTextViewTextSize is not available on
+            // ICS-
+            if (showLine2 && Build.VERSION.SDK_INT >= 16) {
+                if (fitIn1U) {
+                    // need to shrink all the type to make sure everything fits
+                    final float subTextSize = res.getDimensionPixelSize(
+                            R.dimen.notification_subtext_size);
+                    contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX,
+                            subTextSize);
+                }
+                // vertical centering
+                contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
+            }
+
+            if (mBuilder.getWhenIfShowing() != 0) {
+                if (mBuilder.mUseChronometer && Build.VERSION.SDK_INT >= 16) {
+                    contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
+                    contentView.setLong(R.id.chronometer, "setBase",
+                            mBuilder.getWhenIfShowing()
+                                    + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
+                    contentView.setBoolean(R.id.chronometer, "setStarted", true);
+                } else {
+                    contentView.setViewVisibility(R.id.time, View.VISIBLE);
+                    contentView.setLong(R.id.time, "setTime", mBuilder.getWhenIfShowing());
+                }
+                hasRightSide = true;
+            }
+            contentView.setViewVisibility(R.id.right_side, hasRightSide ? View.VISIBLE : View.GONE);
+            contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
+            return contentView;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public Bitmap createColoredBitmap(int iconId, int color) {
+            return createColoredBitmap(iconId, color, 0);
+        }
+
+        private Bitmap createColoredBitmap(int iconId, int color, int size) {
+            Drawable drawable = mBuilder.mContext.getResources().getDrawable(iconId);
+            int width = size == 0 ? drawable.getIntrinsicWidth() : size;
+            int height = size == 0 ? drawable.getIntrinsicHeight() : size;
+            Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            drawable.setBounds(0, 0, width, height);
+            if (color != 0) {
+                drawable.mutate().setColorFilter(
+                        new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
+            }
+            Canvas canvas = new Canvas(resultBitmap);
+            drawable.draw(canvas);
+            return resultBitmap;
+        }
+
+        private Bitmap createIconWithBackground(int iconId, int size,
+                int iconSize, int color) {
+            Bitmap coloredBitmap = createColoredBitmap(R.drawable.notification_icon_background,
+                    color == NotificationCompat.COLOR_DEFAULT ? 0 : color, size);
+            Canvas canvas = new Canvas(coloredBitmap);
+            Drawable icon = mBuilder.mContext.getResources().getDrawable(iconId).mutate();
+            icon.setFilterBitmap(true);
+            int inset = (size - iconSize) / 2;
+            icon.setBounds(inset, inset, iconSize + inset, iconSize + inset);
+            icon.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP));
+            icon.draw(canvas);
+            return coloredBitmap;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public void buildIntoRemoteViews(RemoteViews outerView,
+                RemoteViews innerView) {
+            // this needs to be done fore the other calls, since otherwise we might hide the wrong
+            // things if our ids collide.
+            hideNormalContent(outerView);
+            outerView.removeAllViews(R.id.notification_main_column);
+            outerView.addView(R.id.notification_main_column, innerView.clone());
+            outerView.setViewVisibility(R.id.notification_main_column, View.VISIBLE);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                // Adjust padding depending on font size.
+                outerView.setViewPadding(R.id.notification_main_column_container,
+                        0, calculateTopPadding(), 0, 0);
+            }
+        }
+
+        private void hideNormalContent(RemoteViews outerView) {
+            outerView.setViewVisibility(R.id.title, View.GONE);
+            outerView.setViewVisibility(R.id.text2, View.GONE);
+            outerView.setViewVisibility(R.id.text, View.GONE);
+        }
+
+        private int calculateTopPadding() {
+            Resources resources = mBuilder.mContext.getResources();
+            int padding = resources.getDimensionPixelSize(R.dimen.notification_top_pad);
+            int largePadding = resources.getDimensionPixelSize(
+                    R.dimen.notification_top_pad_large_text);
+            float fontScale = resources.getConfiguration().fontScale;
+            float largeFactor = (constrain(fontScale, 1.0f, 1.3f) - 1f) / (1.3f - 1f);
+
+            // Linearly interpolate the padding between large and normal with the font scale ranging
+            // from 1f to LARGE_TEXT_SCALE
+            return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
+        }
+
+        private static float constrain(float amount, float low, float high) {
+            return amount < low ? low : (amount > high ? high : amount);
+        }
     }
 
     /**
@@ -1899,7 +2250,7 @@
      * <br>
      * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
      * <pre class="prettyprint">
-     * Notification notif = new Notification.Builder(mContext)
+     * Notification notification = new Notification.Builder(mContext)
      *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
      *     .setContentText(subject)
      *     .setSmallIcon(R.drawable.new_post)
@@ -1912,9 +2263,9 @@
      * @see Notification#bigContentView
      */
     public static class BigPictureStyle extends Style {
-        Bitmap mPicture;
-        Bitmap mBigLargeIcon;
-        boolean mBigLargeIconSet;
+        private Bitmap mPicture;
+        private Bitmap mBigLargeIcon;
+        private boolean mBigLargeIconSet;
 
         public BigPictureStyle() {
         }
@@ -1957,6 +2308,23 @@
             mBigLargeIconSet = true;
             return this;
         }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 16) {
+                NotificationCompatJellybean.addBigPictureStyle(builder,
+                        mBigContentTitle,
+                        mSummaryTextSet,
+                        mSummaryText,
+                        mPicture,
+                        mBigLargeIcon,
+                        mBigLargeIconSet);
+            }
+        }
     }
 
     /**
@@ -1968,7 +2336,7 @@
      * <br>
      * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
      * <pre class="prettyprint">
-     * Notification notif = new Notification.Builder(mContext)
+     * Notification notification = new Notification.Builder(mContext)
      *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
      *     .setContentText(subject)
      *     .setSmallIcon(R.drawable.new_mail)
@@ -1981,7 +2349,7 @@
      * @see Notification#bigContentView
      */
     public static class BigTextStyle extends Style {
-        CharSequence mBigText;
+        private CharSequence mBigText;
 
         public BigTextStyle() {
         }
@@ -2016,6 +2384,21 @@
             mBigText = Builder.limitCharSequenceLength(cs);
             return this;
         }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 16) {
+                NotificationCompatJellybean.addBigTextStyle(builder,
+                        mBigContentTitle,
+                        mSummaryTextSet,
+                        mSummaryText,
+                        mBigText);
+            }
+        }
     }
 
     /**
@@ -2039,8 +2422,8 @@
      * so:
      * <pre class="prettyprint">
      *
-     * Notification noti = new Notification.Builder()
-     *     .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
+     * Notification notification = new Notification.Builder()
+     *     .setContentTitle(&quot;2 new messages with &quot; + sender.toString())
      *     .setContentText(subject)
      *     .setSmallIcon(R.drawable.new_message)
      *     .setLargeIcon(aBitmap)
@@ -2085,7 +2468,7 @@
         /**
          * Sets the title to be displayed on this conversation. This should only be used for
          * group messaging and left unset for one-on-one conversations.
-         * @param conversationTitle
+         * @param conversationTitle Title displayed for this conversation.
          * @return this object for method chaining.
          */
         public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
@@ -2105,7 +2488,7 @@
          * Adds a message for display by this notification. Convenience call for a simple
          * {@link Message} in {@link #addMessage(Message)}
          * @param text A {@link CharSequence} to be displayed as the message content
-         * @param timestamp Time at which the message arrived
+         * @param timestamp Time at which the message arrived in ms since Unix epoch
          * @param sender A {@link CharSequence} to be used for displaying the name of the
          * sender. Should be <code>null</code> for messages by the current user, in which case
          * the platform will insert {@link #getUserDisplayName()}.
@@ -2151,9 +2534,10 @@
          * application using {@link NotificationCompat}, regardless of the API level of the system.
          * Returns {@code null} if there is no {@link MessagingStyle} set.
          */
-        public static MessagingStyle extractMessagingStyleFromNotification(Notification notif) {
+        public static MessagingStyle extractMessagingStyleFromNotification(
+                Notification notification) {
             MessagingStyle style;
-            Bundle extras = IMPL.getExtras(notif);
+            Bundle extras = NotificationCompat.getExtras(notification);
             if (extras != null && !extras.containsKey(EXTRA_SELF_DISPLAY_NAME)) {
                 style = null;
             } else {
@@ -2167,6 +2551,121 @@
             return style;
         }
 
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                List<CharSequence> texts = new ArrayList<>();
+                List<Long> timestamps = new ArrayList<>();
+                List<CharSequence> senders = new ArrayList<>();
+                List<String> dataMimeTypes = new ArrayList<>();
+                List<Uri> dataUris = new ArrayList<>();
+
+                for (MessagingStyle.Message message : mMessages) {
+                    texts.add(message.getText());
+                    timestamps.add(message.getTimestamp());
+                    senders.add(message.getSender());
+                    dataMimeTypes.add(message.getDataMimeType());
+                    dataUris.add(message.getDataUri());
+                }
+                NotificationCompatApi24.addMessagingStyle(builder, mUserDisplayName,
+                        mConversationTitle, texts, timestamps, senders,
+                        dataMimeTypes, dataUris);
+            } else {
+                MessagingStyle.Message latestIncomingMessage = findLatestIncomingMessage();
+                // Set the title
+                if (mConversationTitle != null) {
+                    builder.getBuilder().setContentTitle(mConversationTitle);
+                } else if (latestIncomingMessage != null) {
+                    builder.getBuilder().setContentTitle(latestIncomingMessage.getSender());
+                }
+                // Set the text
+                if (latestIncomingMessage != null) {
+                    builder.getBuilder().setContentText(mConversationTitle != null
+                            ? makeMessageLine(latestIncomingMessage)
+                            : latestIncomingMessage.getText());
+                }
+                // Build a fallback BigTextStyle for API 16-23 devices
+                if (Build.VERSION.SDK_INT >= 16) {
+                    SpannableStringBuilder completeMessage = new SpannableStringBuilder();
+                    boolean showNames = mConversationTitle != null
+                            || hasMessagesWithoutSender();
+                    for (int i = mMessages.size() - 1; i >= 0; i--) {
+                        MessagingStyle.Message message = mMessages.get(i);
+                        CharSequence line;
+                        line = showNames ? makeMessageLine(message) : message.getText();
+                        if (i != mMessages.size() - 1) {
+                            completeMessage.insert(0, "\n");
+                        }
+                        completeMessage.insert(0, line);
+                    }
+                    NotificationCompatJellybean.addBigTextStyle(builder,
+                            null,
+                            false,
+                            null,
+                            completeMessage);
+                }
+            }
+        }
+
+        @Nullable
+        private MessagingStyle.Message findLatestIncomingMessage() {
+            for (int i = mMessages.size() - 1; i >= 0; i--) {
+                MessagingStyle.Message message = mMessages.get(i);
+                // Incoming messages have a non-empty sender.
+                if (!TextUtils.isEmpty(message.getSender())) {
+                    return message;
+                }
+            }
+            if (!mMessages.isEmpty()) {
+                // No incoming messages, fall back to outgoing message
+                return mMessages.get(mMessages.size() - 1);
+            }
+            return null;
+        }
+
+        private boolean hasMessagesWithoutSender() {
+            for (int i = mMessages.size() - 1; i >= 0; i--) {
+                MessagingStyle.Message message = mMessages.get(i);
+                if (message.getSender() == null) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private CharSequence makeMessageLine(MessagingStyle.Message message) {
+            BidiFormatter bidi = BidiFormatter.getInstance();
+            SpannableStringBuilder sb = new SpannableStringBuilder();
+            final boolean afterLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+            int color = afterLollipop ? Color.BLACK : Color.WHITE;
+            CharSequence replyName = message.getSender();
+            if (TextUtils.isEmpty(message.getSender())) {
+                replyName = mUserDisplayName == null
+                        ? "" : mUserDisplayName;
+                color = afterLollipop && mBuilder.getColor() != NotificationCompat.COLOR_DEFAULT
+                        ? mBuilder.getColor()
+                        : color;
+            }
+            CharSequence senderText = bidi.unicodeWrap(replyName);
+            sb.append(senderText);
+            sb.setSpan(makeFontColorSpan(color),
+                    sb.length() - senderText.length(),
+                    sb.length(),
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* flags */);
+            CharSequence text = message.getText() == null ? "" : message.getText();
+            sb.append("  ").append(bidi.unicodeWrap(text));
+            return sb;
+        }
+
+        @NonNull
+        private TextAppearanceSpan makeFontColorSpan(int color) {
+            return new TextAppearanceSpan(null, 0, 0, ColorStateList.valueOf(color), null);
+        }
+
         @Override
         public void addCompatExtras(Bundle extras) {
             super.addCompatExtras(extras);
@@ -2203,18 +2702,20 @@
             static final String KEY_SENDER = "sender";
             static final String KEY_DATA_MIME_TYPE = "type";
             static final String KEY_DATA_URI= "uri";
+            static final String KEY_EXTRAS_BUNDLE = "extras";
 
             private final CharSequence mText;
             private final long mTimestamp;
             private final CharSequence mSender;
 
+            private Bundle mExtras = new Bundle();
             private String mDataMimeType;
             private Uri mDataUri;
 
             /**
              * Constructor
              * @param text A {@link CharSequence} to be displayed as the message content
-             * @param timestamp Time at which the message arrived
+             * @param timestamp Time at which the message arrived in ms since Unix epoch
              * @param sender A {@link CharSequence} to be used for displaying the name of the
              * sender. Should be <code>null</code> for messages by the current user, in which case
              * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
@@ -2269,13 +2770,20 @@
             }
 
             /**
-             * Get the time at which this message arrived
+             * Get the time at which this message arrived in ms since Unix epoch
              */
             public long getTimestamp() {
                 return mTimestamp;
             }
 
             /**
+             * Get the extras Bundle for this message.
+             */
+            public Bundle getExtras() {
+                return mExtras;
+            }
+
+            /**
              * Get the text used to display the contact's name in the messaging experience
              */
             public CharSequence getSender() {
@@ -2312,6 +2820,9 @@
                 if (mDataUri != null) {
                     bundle.putParcelable(KEY_DATA_URI, mDataUri);
                 }
+                if (mExtras != null) {
+                    bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
+                }
                 return bundle;
             }
 
@@ -2346,10 +2857,12 @@
                                 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
                         if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
                                 bundle.containsKey(KEY_DATA_URI)) {
-
                             message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
                                     (Uri) bundle.getParcelable(KEY_DATA_URI));
                         }
+                        if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
+                            message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
+                        }
                         return message;
                     }
                 } catch (ClassCastException e) {
@@ -2368,7 +2881,7 @@
      * <br>
      * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
      * <pre class="prettyprint">
-     * Notification noti = new Notification.Builder()
+     * Notification notification = new Notification.Builder()
      *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
      *     .setContentText(subject)
      *     .setSmallIcon(R.drawable.new_mail)
@@ -2384,7 +2897,7 @@
      * @see Notification#bigContentView
      */
     public static class InboxStyle extends Style {
-        ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+        private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
 
         public InboxStyle() {
         }
@@ -2418,6 +2931,165 @@
             mTexts.add(Builder.limitCharSequenceLength(cs));
             return this;
         }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 16) {
+                NotificationCompatJellybean.addInboxStyle(builder,
+                        mBigContentTitle,
+                        mSummaryTextSet,
+                        mSummaryText,
+                        mTexts);
+            }
+        }
+    }
+
+    /**
+     * Notification style for custom views that are decorated by the system.
+     *
+     * <p>Instead of providing a notification that is completely custom, a developer can set this
+     * style and still obtain system decorations like the notification header with the expand
+     * affordance and actions.
+     *
+     * <p>Use {@link NotificationCompat.Builder#setCustomContentView(RemoteViews)},
+     * {@link NotificationCompat.Builder#setCustomBigContentView(RemoteViews)} and
+     * {@link NotificationCompat.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
+     * corresponding custom views to display.
+     *
+     * <p>To use this style with your Notification, feed it to
+     * {@link NotificationCompat.Builder#setStyle(Style)} like so:
+     * <pre class="prettyprint">
+     * Notification noti = new NotificationCompat.Builder()
+     *     .setSmallIcon(R.drawable.ic_stat_player)
+     *     .setLargeIcon(albumArtBitmap))
+     *     .setCustomContentView(contentView)
+     *     .setStyle(<b>new NotificationCompat.DecoratedCustomViewStyle()</b>)
+     *     .build();
+     * </pre>
+     *
+     * <p>If you are using this style, consider using the corresponding styles like
+     * {@link android.support.compat.R.style#TextAppearance_Compat_Notification} or
+     * {@link android.support.compat.R.style#TextAppearance_Compat_Notification_Title} in
+     * your custom views in order to get the correct styling on each platform version.
+     */
+    public static class DecoratedCustomViewStyle extends Style {
+
+        private static final int MAX_ACTION_BUTTONS = 3;
+
+        public DecoratedCustomViewStyle() {
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                builder.getBuilder().setStyle(new Notification.DecoratedCustomViewStyle());
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                // No custom content view required
+                return null;
+            }
+            if (mBuilder.getContentView() == null) {
+                // No special content view
+                return null;
+            }
+            return createRemoteViews(mBuilder.getContentView(), false);
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                // No custom big content view required
+                return null;
+            }
+            RemoteViews bigContentView = mBuilder.getBigContentView();
+            RemoteViews innerView = bigContentView != null
+                    ? bigContentView
+                    : mBuilder.getContentView();
+            if (innerView == null) {
+                // No expandable notification
+                return null;
+            }
+            return createRemoteViews(innerView, true);
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                // No custom heads up content view required
+                return null;
+            }
+            RemoteViews headsUp = mBuilder.getHeadsUpContentView();
+            RemoteViews innerView = headsUp != null ? headsUp : mBuilder.getContentView();
+            if (headsUp == null) {
+                // No expandable notification
+                return null;
+            }
+            return createRemoteViews(innerView, true);
+        }
+
+        private RemoteViews createRemoteViews(RemoteViews innerView, boolean showActions) {
+            RemoteViews remoteViews = applyStandardTemplate(true /* showSmallIcon */,
+                    R.layout.notification_template_custom_big, false /* fitIn1U */);
+            remoteViews.removeAllViews(R.id.actions);
+            boolean actionsVisible = false;
+            if (showActions && mBuilder.mActions != null) {
+                int numActions = Math.min(mBuilder.mActions.size(), MAX_ACTION_BUTTONS);
+                if (numActions > 0) {
+                    actionsVisible = true;
+                    for (int i = 0; i < numActions; i++) {
+                        final RemoteViews button = generateActionButton(mBuilder.mActions.get(i));
+                        remoteViews.addView(R.id.actions, button);
+                    }
+                }
+            }
+            int actionVisibility = actionsVisible ? View.VISIBLE : View.GONE;
+            remoteViews.setViewVisibility(R.id.actions, actionVisibility);
+            remoteViews.setViewVisibility(R.id.action_divider, actionVisibility);
+            buildIntoRemoteViews(remoteViews, innerView);
+            return remoteViews;
+        }
+
+        private RemoteViews generateActionButton(NotificationCompat.Action action) {
+            final boolean tombstone = (action.actionIntent == null);
+            RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
+                    tombstone ? R.layout.notification_action_tombstone
+                            : R.layout.notification_action);
+            button.setImageViewBitmap(R.id.action_image,
+                    createColoredBitmap(action.getIcon(), mBuilder.mContext.getResources()
+                            .getColor(R.color.notification_action_color_filter)));
+            button.setTextViewText(R.id.action_text, action.title);
+            if (!tombstone) {
+                button.setOnClickPendingIntent(R.id.action_container, action.actionIntent);
+            }
+            if (Build.VERSION.SDK_INT >= 15) {
+                button.setContentDescription(R.id.action_container, action.title);
+            }
+            return button;
+        }
     }
 
     /**
@@ -2432,6 +3104,19 @@
     public static class Action extends NotificationCompatBase.Action {
         final Bundle mExtras;
         private final RemoteInput[] mRemoteInputs;
+
+        /**
+         * Holds {@link RemoteInput}s that only accept data, meaning
+         * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
+         * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
+         * empty. These {@link RemoteInput}s will be ignored by devices that do not
+         * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
+         *
+         * You can test if a RemoteInput matches these constraints using
+         * {@link RemoteInput#isDataOnly}.
+         */
+        private final RemoteInput[] mDataOnlyRemoteInputs;
+
         private boolean mAllowGeneratedReplies;
 
         /**
@@ -2449,16 +3134,18 @@
         public PendingIntent actionIntent;
 
         public Action(int icon, CharSequence title, PendingIntent intent) {
-            this(icon, title, intent, new Bundle(), null, true);
+            this(icon, title, intent, new Bundle(), null, null, true);
         }
 
         Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
-                RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
+                RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
+                boolean allowGeneratedReplies) {
             this.icon = icon;
             this.title = NotificationCompat.Builder.limitCharSequenceLength(title);
             this.actionIntent = intent;
             this.mExtras = extras != null ? extras : new Bundle();
             this.mRemoteInputs = remoteInputs;
+            this.mDataOnlyRemoteInputs = dataOnlyRemoteInputs;
             this.mAllowGeneratedReplies = allowGeneratedReplies;
         }
 
@@ -2496,7 +3183,8 @@
 
         /**
          * Get the list of inputs to be collected from the user when this action is sent.
-         * May return null if no remote inputs were added.
+         * May return null if no remote inputs were added. Only returns inputs which accept
+         * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
          */
         @Override
         public RemoteInput[] getRemoteInputs() {
@@ -2504,6 +3192,21 @@
         }
 
         /**
+         * Get the list of inputs to be collected from the user that ONLY accept data when this
+         * action is sent. These remote inputs are guaranteed to return true on a call to
+         * {@link RemoteInput#isDataOnly}.
+         *
+         * <p>May return null if no data-only remote inputs were added.
+         *
+         * <p>This method exists so that legacy RemoteInput collectors that pre-date the addition
+         * of non-textual RemoteInputs do not access these remote inputs.
+         */
+        @Override
+        public RemoteInput[] getDataOnlyRemoteInputs() {
+            return mDataOnlyRemoteInputs;
+        }
+
+        /**
          * Builder class for {@link Action} objects.
          */
         public static final class Builder {
@@ -2612,10 +3315,23 @@
              * @return the built action
              */
             public Action build() {
-                RemoteInput[] remoteInputs = mRemoteInputs != null
-                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
-                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
-                        mAllowGeneratedReplies);
+                List<RemoteInput> dataOnlyInputs = new ArrayList<>();
+                List<RemoteInput> textInputs = new ArrayList<>();
+                if (mRemoteInputs != null) {
+                    for (RemoteInput input : mRemoteInputs) {
+                        if (input.isDataOnly()) {
+                            dataOnlyInputs.add(input);
+                        } else {
+                            textInputs.add(input);
+                        }
+                    }
+                }
+                RemoteInput[] dataOnlyInputsArr = dataOnlyInputs.isEmpty()
+                        ? null : dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
+                RemoteInput[] textInputsArr = textInputs.isEmpty()
+                        ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
+                return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
+                        dataOnlyInputsArr, mAllowGeneratedReplies);
             }
         }
 
@@ -2630,7 +3346,7 @@
              * @param builder the builder to be modified.
              * @return the build object for chaining.
              */
-            public Builder extend(Builder builder);
+            Builder extend(Builder builder);
         }
 
         /**
@@ -2883,9 +3599,11 @@
             public NotificationCompatBase.Action build(int icon, CharSequence title,
                     PendingIntent actionIntent, Bundle extras,
                     RemoteInputCompatBase.RemoteInput[] remoteInputs,
+                    RemoteInputCompatBase.RemoteInput[] dataOnlyRemoteInputs,
                     boolean allowGeneratedReplies) {
                 return new Action(icon, title, actionIntent, extras,
-                        (RemoteInput[]) remoteInputs, allowGeneratedReplies);
+                        (RemoteInput[]) remoteInputs, (RemoteInput[]) dataOnlyRemoteInputs,
+                        allowGeneratedReplies);
             }
 
             @Override
@@ -2906,7 +3624,7 @@
          * @param builder the builder to be modified.
          * @return the build object for chaining.
          */
-        public Builder extend(Builder builder);
+        Builder extend(Builder builder);
     }
 
     /**
@@ -2930,14 +3648,14 @@
      * </ol>
      *
      * <pre class="prettyprint">
-     * Notification notif = new NotificationCompat.Builder(mContext)
+     * Notification notification = new NotificationCompat.Builder(mContext)
      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
      *         .setContentText(subject)
      *         .setSmallIcon(R.drawable.new_mail)
      *         .extend(new NotificationCompat.WearableExtender()
      *                 .setContentIcon(R.drawable.new_mail))
      *         .build();
-     * NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+     * NotificationManagerCompat.from(mContext).notify(0, notification);</pre>
      *
      * <p>Wearable extensions can be accessed on an existing notification by using the
      * {@code WearableExtender(Notification)} constructor,
@@ -3072,8 +3790,8 @@
         public WearableExtender() {
         }
 
-        public WearableExtender(Notification notif) {
-            Bundle extras = getExtras(notif);
+        public WearableExtender(Notification notification) {
+            Bundle extras = getExtras(notification);
             Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
                     : null;
             if (wearableBundle != null) {
@@ -3247,7 +3965,7 @@
          * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
          * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
          *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-         * Notification notif = new NotificationCompat.Builder(context)
+         * Notification notification = new NotificationCompat.Builder(context)
          *         .extend(new NotificationCompat.WearableExtender()
          *                 .setDisplayIntent(displayPendingIntent)
          *                 .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM))
@@ -3774,15 +4492,15 @@
         /**
          * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
          *
-         * @param notif The notification from which to copy options.
+         * @param notification The notification from which to copy options.
          */
-        public CarExtender(Notification notif) {
+        public CarExtender(Notification notification) {
             if (Build.VERSION.SDK_INT < 21) {
                 return;
             }
 
-            Bundle carBundle = getExtras(notif)==null ?
-                    null : getExtras(notif).getBundle(EXTRA_CAR_EXTENDER);
+            Bundle carBundle = getExtras(notification) == null
+                    ? null : getExtras(notification).getBundle(EXTRA_CAR_EXTENDER);
             if (carBundle != null) {
                 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
                 mColor = carBundle.getInt(EXTRA_COLOR, NotificationCompat.COLOR_DEFAULT);
@@ -4101,35 +4819,51 @@
      * compatible manner. Extras field was supported from JellyBean (Api level 16)
      * forwards. This function will return null on older api levels.
      */
-    public static Bundle getExtras(Notification notif) {
-        return IMPL.getExtras(notif);
+    public static Bundle getExtras(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            return notification.extras;
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            return NotificationCompatJellybean.getExtras(notification);
+        } else {
+            return null;
+        }
     }
 
     /**
      * Get the number of actions in this notification in a backwards compatible
      * manner. Actions were supported from JellyBean (Api level 16) forwards.
      */
-    public static int getActionCount(Notification notif) {
-        return IMPL.getActionCount(notif);
+    public static int getActionCount(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            return notification.actions != null ? notification.actions.length : 0;
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            return NotificationCompatJellybean.getActionCount(notification);
+        } else {
+            return 0;
+        }
     }
 
     /**
      * Get an action on this notification in a backwards compatible
      * manner. Actions were supported from JellyBean (Api level 16) forwards.
-     * @param notif The notification to inspect.
+     * @param notification The notification to inspect.
      * @param actionIndex The index of the action to retrieve.
      */
-    public static Action getAction(Notification notif, int actionIndex) {
-        return IMPL.getAction(notif, actionIndex);
+    public static Action getAction(Notification notification, int actionIndex) {
+        return IMPL.getAction(notification, actionIndex);
     }
 
     /**
      * Get the category of this notification in a backwards compatible
      * manner.
-     * @param notif The notification to inspect.
+     * @param notification The notification to inspect.
      */
-    public static String getCategory(Notification notif) {
-        return IMPL.getCategory(notif);
+    public static String getCategory(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            return notification.category;
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -4138,16 +4872,34 @@
      * <p>Some notifications can be bridged to other devices for remote display.
      * If this hint is set, it is recommend that this notification not be bridged.
      */
-    public static boolean getLocalOnly(Notification notif) {
-        return IMPL.getLocalOnly(notif);
+    public static boolean getLocalOnly(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 20) {
+            return (notification.flags & Notification.FLAG_LOCAL_ONLY) != 0;
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            return notification.extras.getBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY);
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            return NotificationCompatJellybean.getExtras(notification).getBoolean(
+                    NotificationCompatExtras.EXTRA_LOCAL_ONLY);
+        } else {
+            return false;
+        }
     }
 
     /**
      * Get the key used to group this notification into a cluster or stack
      * with other notifications on devices which support such rendering.
      */
-    public static String getGroup(Notification notif) {
-        return IMPL.getGroup(notif);
+    public static String getGroup(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 20) {
+            return notification.getGroup();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            return notification.extras.getString(NotificationCompatExtras.EXTRA_GROUP_KEY);
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            return NotificationCompatJellybean.getExtras(notification).getString(
+                    NotificationCompatExtras.EXTRA_GROUP_KEY);
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -4156,8 +4908,17 @@
      * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
      * @return Whether this notification is a group summary.
      */
-    public static boolean isGroupSummary(Notification notif) {
-        return IMPL.isGroupSummary(notif);
+    public static boolean isGroupSummary(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 20) {
+            return (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            return notification.extras.getBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY);
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            return NotificationCompatJellybean.getExtras(notification).getBoolean(
+                    NotificationCompatExtras.EXTRA_GROUP_SUMMARY);
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -4172,7 +4933,77 @@
      *
      * @see String#compareTo(String)
      */
-    public static String getSortKey(Notification notif) {
-        return IMPL.getSortKey(notif);
+    public static String getSortKey(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 20) {
+            return notification.getSortKey();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            return notification.extras.getString(NotificationCompatExtras.EXTRA_SORT_KEY);
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            return NotificationCompatJellybean.getExtras(notification).getString(
+                    NotificationCompatExtras.EXTRA_SORT_KEY);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @return the ID of the channel this notification posts to.
+     */
+    public static String getChannelId(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return notification.getChannelId();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the time at which this notification should be canceled by the system, if it's not
+     * canceled already.
+     */
+    public static long getTimeoutAfter(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return notification.getTimeoutAfter();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns what icon should be shown for this notification if it is being displayed in a
+     * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
+     * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
+     */
+    public static int getBadgeIconType(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return notification.getBadgeIconType();
+        } else {
+            return BADGE_ICON_NONE;
+        }
+    }
+
+    /**
+     * Returns the {@link android.support.v4.content.pm.ShortcutInfoCompat#getId() id} that this
+     * notification supersedes, if any.
+     */
+    public static String getShortcutId(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return notification.getShortcutId();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns which type of notifications in a group are responsible for audibly alerting the
+     * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
+     * {@link #GROUP_ALERT_SUMMARY}.
+     */
+    public static int getGroupAlertBehavior(Notification notification) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return notification.getGroupAlertBehavior();
+        } else {
+            return GROUP_ALERT_ALL;
+        }
     }
 }
diff --git a/compat/java/android/support/v4/app/NotificationCompatExtras.java b/compat/java/android/support/v4/app/NotificationCompatExtras.java
index bf9ba0f..33bdd06 100644
--- a/compat/java/android/support/v4/app/NotificationCompatExtras.java
+++ b/compat/java/android/support/v4/app/NotificationCompatExtras.java
@@ -25,37 +25,35 @@
      * the {@link android.app.Notification#FLAG_LOCAL_ONLY} field before it was available.
      * If possible, use {@link NotificationCompat#getLocalOnly} to access this field.
      */
-    public static final String EXTRA_LOCAL_ONLY = NotificationCompatJellybean.EXTRA_LOCAL_ONLY;
+    public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value set
      * by {@link android.app.Notification.Builder#setGroup} before it was available.
      * If possible, use {@link NotificationCompat#getGroup} to access this value.
      */
-    public static final String EXTRA_GROUP_KEY = NotificationCompatJellybean.EXTRA_GROUP_KEY;
+    public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value set
      * by {@link android.app.Notification.Builder#setGroupSummary} before it was available.
      * If possible, use {@link NotificationCompat#isGroupSummary} to access this value.
      */
-    public static final String EXTRA_GROUP_SUMMARY =
-            NotificationCompatJellybean.EXTRA_GROUP_SUMMARY;
+    public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value set
      * by {@link android.app.Notification.Builder#setSortKey} before it was available.
      * If possible, use {@link NotificationCompat#getSortKey} to access this value.
      */
-    public static final String EXTRA_SORT_KEY = NotificationCompatJellybean.EXTRA_SORT_KEY;
+    public static final String EXTRA_SORT_KEY = "android.support.sortKey";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value of
      * the {@link android.app.Notification.Action#extras} field before it was available.
      * If possible, use {@link NotificationCompat#getAction} to access this field.
      */
-    public static final String EXTRA_ACTION_EXTRAS =
-            NotificationCompatJellybean.EXTRA_ACTION_EXTRAS;
+    public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value of
@@ -63,8 +61,7 @@
      * was available.
      * If possible, use {@link NotificationCompat.Action#getRemoteInputs} to access this field.
      */
-    public static final String EXTRA_REMOTE_INPUTS =
-            NotificationCompatJellybean.EXTRA_REMOTE_INPUTS;
+    public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
 
     private NotificationCompatExtras() {}
 }
diff --git a/compat/java/android/support/v4/app/NotificationManagerCompat.java b/compat/java/android/support/v4/app/NotificationManagerCompat.java
index f2010a3..93775ec 100644
--- a/compat/java/android/support/v4/app/NotificationManagerCompat.java
+++ b/compat/java/android/support/v4/app/NotificationManagerCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.app;
 
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.Service;
@@ -23,7 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
 import android.os.Bundle;
@@ -34,9 +35,12 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Settings;
-import android.support.v4.os.BuildCompat;
+import android.support.annotation.GuardedBy;
 import android.util.Log;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -54,13 +58,14 @@
  */
 public final class NotificationManagerCompat {
     private static final String TAG = "NotifManCompat";
+    private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
+    private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
 
     /**
      * Notification extras key: if set to true, the posted notification should use
      * the side channel for delivery instead of using notification manager.
      */
-    public static final String EXTRA_USE_SIDE_CHANNEL =
-            NotificationCompatJellybean.EXTRA_USE_SIDE_CHANNEL;
+    public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
 
     /**
      * Intent action to register for on a service to receive side channel
@@ -83,20 +88,19 @@
     /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
     private static final String SETTING_ENABLED_NOTIFICATION_LISTENERS =
             "enabled_notification_listeners";
-    static final int SIDE_CHANNEL_BIND_FLAGS;
 
     /** Cache of enabled notification listener components */
     private static final Object sEnabledNotificationListenersLock = new Object();
-    /** Guarded by {@link #sEnabledNotificationListenersLock} */
+    @GuardedBy("sEnabledNotificationListenersLock")
     private static String sEnabledNotificationListeners;
-    /** Guarded by {@link #sEnabledNotificationListenersLock} */
+    @GuardedBy("sEnabledNotificationListenersLock")
     private static Set<String> sEnabledNotificationListenerPackages = new HashSet<String>();
 
     private final Context mContext;
     private final NotificationManager mNotificationManager;
     /** Lock for mutable static fields */
     private static final Object sLock = new Object();
-    /** Guarded by {@link #sLock} */
+    @GuardedBy("sLock")
     private static SideChannelManager sSideChannelManager;
 
     /**
@@ -150,93 +154,6 @@
                 Context.NOTIFICATION_SERVICE);
     }
 
-    private static final Impl IMPL;
-
-    interface Impl {
-        void cancelNotification(NotificationManager notificationManager, String tag, int id);
-
-        void postNotification(NotificationManager notificationManager, String tag, int id,
-                Notification notification);
-
-        int getSideChannelBindFlags();
-
-        boolean areNotificationsEnabled(Context context, NotificationManager notificationManager);
-
-        int getImportance(NotificationManager notificationManager);
-    }
-
-    static class ImplBase implements Impl {
-
-        @Override
-        public void cancelNotification(NotificationManager notificationManager, String tag,
-                int id) {
-            notificationManager.cancel(tag, id);
-        }
-
-        @Override
-        public void postNotification(NotificationManager notificationManager, String tag, int id,
-                Notification notification) {
-            notificationManager.notify(tag, id, notification);
-        }
-
-        @Override
-        public int getSideChannelBindFlags() {
-            return Service.BIND_AUTO_CREATE;
-        }
-
-        @Override
-        public boolean areNotificationsEnabled(Context context,
-                NotificationManager notificationManager) {
-            return true;
-        }
-
-        @Override
-        public int getImportance(NotificationManager notificationManager) {
-            return IMPORTANCE_UNSPECIFIED;
-        }
-    }
-
-    static class ImplIceCreamSandwich extends ImplBase {
-        @Override
-        public int getSideChannelBindFlags() {
-            return NotificationManagerCompatIceCreamSandwich.SIDE_CHANNEL_BIND_FLAGS;
-        }
-    }
-
-    static class ImplKitKat extends ImplIceCreamSandwich {
-        @Override
-        public boolean areNotificationsEnabled(Context context,
-                NotificationManager notificationManager) {
-            return NotificationManagerCompatKitKat.areNotificationsEnabled(context);
-        }
-    }
-
-    static class ImplApi24 extends ImplKitKat {
-        @Override
-        public boolean areNotificationsEnabled(Context context,
-                NotificationManager notificationManager) {
-            return NotificationManagerCompatApi24.areNotificationsEnabled(notificationManager);
-        }
-
-        @Override
-        public int getImportance(NotificationManager notificationManager) {
-            return NotificationManagerCompatApi24.getImportance(notificationManager);
-        }
-    }
-
-    static {
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new ImplApi24();
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            IMPL = new ImplKitKat();
-        }  else if (Build.VERSION.SDK_INT >= 14) {
-            IMPL = new ImplIceCreamSandwich();
-        } else {
-            IMPL = new ImplBase();
-        }
-        SIDE_CHANNEL_BIND_FLAGS = IMPL.getSideChannelBindFlags();
-    }
-
     /**
      * Cancel a previously shown notification.
      * @param id the ID of the notification
@@ -251,7 +168,7 @@
      * @param id the ID of the notification
      */
     public void cancel(String tag, int id) {
-        IMPL.cancelNotification(mNotificationManager, tag, id);
+        mNotificationManager.cancel(tag, id);
         if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {
             pushSideChannelQueue(new CancelTask(mContext.getPackageName(), id, tag));
         }
@@ -285,9 +202,9 @@
             pushSideChannelQueue(new NotifyTask(mContext.getPackageName(), id, tag, notification));
             // Cancel this notification in notification manager if it just transitioned to being
             // side channelled.
-            IMPL.cancelNotification(mNotificationManager, tag, id);
+            mNotificationManager.cancel(tag, id);
         } else {
-            IMPL.postNotification(mNotificationManager, tag, id, notification);
+            mNotificationManager.notify(tag, id, notification);
         }
     }
 
@@ -295,7 +212,29 @@
      * Returns whether notifications from the calling package are not blocked.
      */
     public boolean areNotificationsEnabled() {
-        return IMPL.areNotificationsEnabled(mContext, mNotificationManager);
+        if (Build.VERSION.SDK_INT >= 24) {
+            return mNotificationManager.areNotificationsEnabled();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            AppOpsManager appOps =
+                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+            ApplicationInfo appInfo = mContext.getApplicationInfo();
+            String pkg = mContext.getApplicationContext().getPackageName();
+            int uid = appInfo.uid;
+            try {
+                Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
+                Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE,
+                        Integer.TYPE, String.class);
+                Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
+                int value = (int) opPostNotificationValue.get(Integer.class);
+                return ((int) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg)
+                        == AppOpsManager.MODE_ALLOWED);
+            } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException
+                    | InvocationTargetException | IllegalAccessException | RuntimeException e) {
+                return true;
+            }
+        } else {
+            return true;
+        }
     }
 
     /**
@@ -304,7 +243,11 @@
      * @return An importance level, such as {@link #IMPORTANCE_DEFAULT}.
      */
     public int getImportance() {
-        return IMPL.getImportance(mNotificationManager);
+        if (Build.VERSION.SDK_INT >= 24) {
+            return mNotificationManager.getImportance();
+        } else {
+            return IMPORTANCE_UNSPECIFIED;
+        }
     }
 
     /**
@@ -363,8 +306,6 @@
         private static final int MSG_SERVICE_DISCONNECTED = 2;
         private static final int MSG_RETRY_LISTENER_QUEUE = 3;
 
-        private static final String KEY_BINDER = "binder";
-
         private final Context mContext;
         private final HandlerThread mHandlerThread;
         private final Handler mHandler;
@@ -468,7 +409,7 @@
             }
             mCachedEnabledPackages = enabledPackages;
             List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServices(
-                    new Intent().setAction(ACTION_BIND_SIDE_CHANNEL), PackageManager.GET_SERVICES);
+                    new Intent().setAction(ACTION_BIND_SIDE_CHANNEL), 0);
             Set<ComponentName> enabledComponents = new HashSet<ComponentName>();
             for (ResolveInfo resolveInfo : resolveInfos) {
                 if (!enabledPackages.contains(resolveInfo.serviceInfo.packageName)) {
@@ -516,7 +457,8 @@
                 return true;
             }
             Intent intent = new Intent(ACTION_BIND_SIDE_CHANNEL).setComponent(record.componentName);
-            record.bound = mContext.bindService(intent, this, SIDE_CHANNEL_BIND_FLAGS);
+            record.bound = mContext.bindService(intent, this, Service.BIND_AUTO_CREATE
+                    | Service.BIND_WAIVE_PRIORITY);
             if (record.bound) {
                 record.retryCount = 0;
             } else {
@@ -628,7 +570,7 @@
         final ComponentName componentName;
         final IBinder iBinder;
 
-        public ServiceConnectedEvent(ComponentName componentName,
+        ServiceConnectedEvent(ComponentName componentName,
                 final IBinder iBinder) {
             this.componentName = componentName;
             this.iBinder = iBinder;
@@ -636,7 +578,7 @@
     }
 
     private interface Task {
-        public void send(INotificationSideChannel service) throws RemoteException;
+        void send(INotificationSideChannel service) throws RemoteException;
     }
 
     private static class NotifyTask implements Task {
@@ -645,7 +587,7 @@
         final String tag;
         final Notification notif;
 
-        public NotifyTask(String packageName, int id, String tag, Notification notif) {
+        NotifyTask(String packageName, int id, String tag, Notification notif) {
             this.packageName = packageName;
             this.id = id;
             this.tag = tag;
@@ -657,6 +599,7 @@
             service.notify(packageName, id, tag, notif);
         }
 
+        @Override
         public String toString() {
             StringBuilder sb = new StringBuilder("NotifyTask[");
             sb.append("packageName:").append(packageName);
@@ -673,14 +616,14 @@
         final String tag;
         final boolean all;
 
-        public CancelTask(String packageName) {
+        CancelTask(String packageName) {
             this.packageName = packageName;
             this.id = 0;
             this.tag = null;
             this.all = true;
         }
 
-        public CancelTask(String packageName, int id, String tag) {
+        CancelTask(String packageName, int id, String tag) {
             this.packageName = packageName;
             this.id = id;
             this.tag = tag;
@@ -696,6 +639,7 @@
             }
         }
 
+        @Override
         public String toString() {
             StringBuilder sb = new StringBuilder("CancelTask[");
             sb.append("packageName:").append(packageName);
diff --git a/compat/java/android/support/v4/app/RemoteInput.java b/compat/java/android/support/v4/app/RemoteInput.java
index 1f69520..48f7744 100644
--- a/compat/java/android/support/v4/app/RemoteInput.java
+++ b/compat/java/android/support/v4/app/RemoteInput.java
@@ -19,37 +19,48 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.util.Log;
 
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 /**
- * Helper for using the {@link android.app.RemoteInput} API
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for using the {@link android.app.RemoteInput}.
  */
 public final class RemoteInput extends RemoteInputCompatBase.RemoteInput {
     private static final String TAG = "RemoteInput";
 
     /** Label used to denote the clip data type used for remote input transport */
-    public static final String RESULTS_CLIP_LABEL = RemoteInputCompatJellybean.RESULTS_CLIP_LABEL;
+    public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
 
-    /** Extra added to a clip data intent object to hold the results bundle. */
-    public static final String EXTRA_RESULTS_DATA = RemoteInputCompatJellybean.EXTRA_RESULTS_DATA;
+    /** Extra added to a clip data intent object to hold the text results bundle. */
+    public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+
+    /** Extra added to a clip data intent object to hold the data results bundle. */
+    private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
+            "android.remoteinput.dataTypeResultsData";
 
     private final String mResultKey;
     private final CharSequence mLabel;
     private final CharSequence[] mChoices;
-    private final boolean mAllowFreeFormInput;
+    private final boolean mAllowFreeFormTextInput;
     private final Bundle mExtras;
+    private final Set<String> mAllowedDataTypes;
 
     RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
-            boolean allowFreeFormInput, Bundle extras) {
+            boolean allowFreeFormTextInput, Bundle extras, Set<String> allowedDataTypes) {
         this.mResultKey = resultKey;
         this.mLabel = label;
         this.mChoices = choices;
-        this.mAllowFreeFormInput = allowFreeFormInput;
+        this.mAllowFreeFormTextInput = allowFreeFormTextInput;
         this.mExtras = extras;
+        this.mAllowedDataTypes = allowedDataTypes;
     }
 
     /**
@@ -77,6 +88,23 @@
         return mChoices;
     }
 
+    @Override
+    public Set<String> getAllowedDataTypes() {
+        return mAllowedDataTypes;
+    }
+
+    /**
+     * Returns true if the input only accepts data, meaning {@link #getAllowFreeFormInput}
+     * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes is
+     * non-null and not empty.
+     */
+    public boolean isDataOnly() {
+        return !getAllowFreeFormInput()
+                && (getChoices() == null || getChoices().length == 0)
+                && getAllowedDataTypes() != null
+                && !getAllowedDataTypes().isEmpty();
+    }
+
     /**
      * Get whether or not users can provide an arbitrary value for
      * input. If you set this to {@code false}, users must select one of the
@@ -85,7 +113,7 @@
      */
     @Override
     public boolean getAllowFreeFormInput() {
-        return mAllowFreeFormInput;
+        return mAllowFreeFormTextInput;
     }
 
     /**
@@ -103,8 +131,9 @@
         private final String mResultKey;
         private CharSequence mLabel;
         private CharSequence[] mChoices;
-        private boolean mAllowFreeFormInput = true;
+        private boolean mAllowFreeFormTextInput = true;
         private Bundle mExtras = new Bundle();
+        private final Set<String> mAllowedDataTypes = new HashSet<>();
 
         /**
          * Create a builder object for {@link android.support.v4.app.RemoteInput} objects.
@@ -142,14 +171,34 @@
         /**
          * Specifies whether the user can provide arbitrary values.
          *
-         * @param allowFreeFormInput The default is {@code true}.
-         *         If you specify {@code false}, you must provide a non-null
-         *         and non-empty array to {@link #setChoices} or an
+         * @param mimeType A mime type that results are allowed to come in.
+         *         Be aware that text results (see {@link #setAllowFreeFormInput}
+         *         are allowed by default. If you do not want text results you will have to
+         *         pass false to {@code setAllowFreeFormInput}.
+         * @param doAllow Whether the mime type should be allowed or not.
+         * @return this object for method chaining
+         */
+        public Builder setAllowDataType(String mimeType, boolean doAllow) {
+            if (doAllow) {
+                mAllowedDataTypes.add(mimeType);
+            } else {
+                mAllowedDataTypes.remove(mimeType);
+            }
+            return this;
+        }
+
+        /**
+         * Specifies whether the user can provide arbitrary text values.
+         *
+         * @param allowFreeFormTextInput The default is {@code true}.
+         *         If you specify {@code false}, you must either provide a non-null
+         *         and non-empty array to {@link #setChoices}, or enable a data result
+         *         in {@code setAllowDataType}. Otherwise an
          *         {@link IllegalArgumentException} is thrown.
          * @return this object for method chaining
          */
-        public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
-            mAllowFreeFormInput = allowFreeFormInput;
+        public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
+            mAllowFreeFormTextInput = allowFreeFormTextInput;
             return this;
         }
 
@@ -181,14 +230,42 @@
          * {@link android.support.v4.app.RemoteInput} object.
          */
         public RemoteInput build() {
-            return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
+            return new RemoteInput(
+                    mResultKey,
+                    mLabel,
+                    mChoices,
+                    mAllowFreeFormTextInput,
+                    mExtras,
+                    mAllowedDataTypes);
         }
     }
 
     /**
-     * Get the remote input results bundle from an intent. The returned Bundle will
+     * Similar as {@link #getResultsFromIntent} but retrieves data results for a
+     * specific RemoteInput result. To retrieve a value use:
+     * <pre>
+     * {@code
+     * Map<String, Uri> results =
+     *     RemoteInput.getDataResultsFromIntent(intent, REMOTE_INPUT_KEY);
+     * if (results != null) {
+     *   Uri data = results.get(MIME_TYPE_OF_INTEREST);
+     * }
+     * }
+     * </pre>
+     * @param intent The intent object that fired in response to an action or content intent
+     *               which also had one or more remote input requested.
+     * @param remoteInputResultKey The result key for the RemoteInput you want results for.
+     */
+    public static Map<String, Uri> getDataResultsFromIntent(
+            Intent intent, String remoteInputResultKey) {
+        return IMPL.getDataResultsFromIntent(intent, remoteInputResultKey);
+    }
+
+    /**
+     * Get the remote input text results bundle from an intent. The returned Bundle will
      * contain a key/value for every result key populated by remote input collector.
-     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value.
+     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value. For data results
+     * use {@link #getDataResultsFromIntent}.
      * @param intent The intent object that fired in response to an action or content intent
      *               which also had one or more remote input requested.
      */
@@ -212,12 +289,28 @@
         IMPL.addResultsToIntent(remoteInputs, intent, results);
     }
 
+    /**
+     * Same as {@link #addResultsToIntent} but for setting data results.
+     * @param remoteInput The remote input for which results are being provided
+     * @param intent The intent to add remote input results to. The
+     *               {@link android.content.ClipData} field of the intent will be
+     *               modified to contain the results.
+     * @param results A map of mime type to the Uri result for that mime type.
+     */
+    public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+            Map<String, Uri> results) {
+        IMPL.addDataResultToIntent(remoteInput, intent, results);
+    }
+
     private static final Impl IMPL;
 
     interface Impl {
         Bundle getResultsFromIntent(Intent intent);
+        Map<String, Uri> getDataResultsFromIntent(Intent intent, String remoteInputResultKey);
         void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
                 Bundle results);
+        void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+                Map<String, Uri> results);
     }
 
     static class ImplBase implements Impl {
@@ -228,11 +321,25 @@
         }
 
         @Override
+        public Map<String, Uri> getDataResultsFromIntent(
+                Intent intent, String remoteInputResultKey) {
+            Log.w(TAG, "RemoteInput is only supported from API Level 16");
+            return null;
+        }
+
+        @Override
         public void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, Bundle results) {
             Log.w(TAG, "RemoteInput is only supported from API Level 16");
         }
+
+        @Override
+        public void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+                Map<String, Uri> results) {
+            Log.w(TAG, "RemoteInput is only supported from API Level 16");
+        }
     }
 
+    @RequiresApi(16)
     static class ImplJellybean implements Impl {
         @Override
         public Bundle getResultsFromIntent(Intent intent) {
@@ -240,11 +347,25 @@
         }
 
         @Override
+        public Map<String, Uri> getDataResultsFromIntent(
+                Intent intent, String remoteInputResultKey) {
+            return RemoteInputCompatJellybean.getDataResultsFromIntent(
+                    intent, remoteInputResultKey);
+        }
+
+        @Override
         public void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, Bundle results) {
             RemoteInputCompatJellybean.addResultsToIntent(remoteInputs, intent, results);
         }
+
+        @Override
+        public void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+                Map<String, Uri> results) {
+            RemoteInputCompatJellybean.addDataResultToIntent(remoteInput, intent, results);
+        }
     }
 
+    @RequiresApi(20)
     static class ImplApi20 implements Impl {
         @Override
         public Bundle getResultsFromIntent(Intent intent) {
@@ -252,9 +373,21 @@
         }
 
         @Override
+        public Map<String, Uri> getDataResultsFromIntent(
+                Intent intent, String remoteInputResultKey) {
+            return RemoteInputCompatApi20.getDataResultsFromIntent(intent, remoteInputResultKey);
+        }
+
+        @Override
         public void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, Bundle results) {
             RemoteInputCompatApi20.addResultsToIntent(remoteInputs, intent, results);
         }
+
+        @Override
+        public void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+                Map<String, Uri> results) {
+            RemoteInputCompatApi20.addDataResultToIntent(remoteInput, intent, results);
+        }
     }
 
     static {
@@ -273,8 +406,9 @@
         @Override
         public RemoteInput build(String resultKey,
                 CharSequence label, CharSequence[] choices, boolean allowFreeFormInput,
-                Bundle extras) {
-            return new RemoteInput(resultKey, label, choices, allowFreeFormInput, extras);
+                Bundle extras, Set<String> allowedDataTypes) {
+            return new RemoteInput(
+                    resultKey, label, choices, allowFreeFormInput, extras, allowedDataTypes);
         }
 
         @Override
diff --git a/compat/java/android/support/v4/app/ServiceCompat.java b/compat/java/android/support/v4/app/ServiceCompat.java
index 3fc6ae8..1676ee8 100644
--- a/compat/java/android/support/v4/app/ServiceCompat.java
+++ b/compat/java/android/support/v4/app/ServiceCompat.java
@@ -20,16 +20,15 @@
 
 import android.app.Notification;
 import android.app.Service;
+import android.os.Build;
 import android.support.annotation.IntDef;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Helper for accessing features in {@link android.app.Service}
- * introduced after API level 9 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.Service}.
  */
 public final class ServiceCompat {
 
@@ -86,31 +85,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StopForegroundFlags {}
 
-    interface ServiceCompatImpl {
-        void stopForeground(Service service, @ServiceCompat.StopForegroundFlags int flags);
-    }
-
-    static class BaseServiceCompatImpl implements ServiceCompat.ServiceCompatImpl {
-        public void stopForeground(Service service, @ServiceCompat.StopForegroundFlags int flags) {
-            service.stopForeground((flags & ServiceCompat.STOP_FOREGROUND_REMOVE) != 0);
-        }
-    }
-
-    static class Api24ServiceCompatImpl implements ServiceCompat.ServiceCompatImpl {
-        public void stopForeground(Service service, @ServiceCompat.StopForegroundFlags int flags) {
-            ServiceCompatApi24.stopForeground(service, flags);
-        }
-    }
-
-    static final ServiceCompatImpl IMPL;
-    static {
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new Api24ServiceCompatImpl();
-        } else {
-            IMPL = new BaseServiceCompatImpl();
-        }
-    }
-
     /**
      * Remove the passed service from foreground state, allowing it to be killed if
      * more memory is needed.
@@ -118,8 +92,11 @@
      * {@link #STOP_FOREGROUND_DETACH}.
      * @see Service#startForeground(int, Notification)
      */
-    public static void stopForeground(Service service,
-            @ServiceCompat.StopForegroundFlags int flags) {
-        IMPL.stopForeground(service, flags);
+    public static void stopForeground(Service service, @StopForegroundFlags int flags) {
+        if (Build.VERSION.SDK_INT >= 24) {
+            service.stopForeground(flags);
+        } else {
+            service.stopForeground((flags & ServiceCompat.STOP_FOREGROUND_REMOVE) != 0);
+        }
     }
 }
diff --git a/compat/java/android/support/v4/app/ShareCompat.java b/compat/java/android/support/v4/app/ShareCompat.java
index 2c82c96..b0c6e62 100644
--- a/compat/java/android/support/v4/app/ShareCompat.java
+++ b/compat/java/android/support/v4/app/ShareCompat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.app;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -23,15 +25,15 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Build;
 import android.support.annotation.StringRes;
 import android.support.v4.content.IntentCompat;
-import android.support.v4.view.MenuItemCompat;
 import android.text.Html;
 import android.text.Spanned;
 import android.util.Log;
+import android.view.ActionProvider;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.widget.ShareActionProvider;
 
 import java.util.ArrayList;
 
@@ -75,92 +77,7 @@
     public static final String EXTRA_CALLING_ACTIVITY =
             "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
 
-    /**
-     * Compatibility shims for sharing operations
-     */
-    interface ShareCompatImpl {
-        void configureMenuItem(MenuItem item, IntentBuilder shareIntent);
-        String escapeHtml(CharSequence text);
-    }
-
-    static class ShareCompatImplBase implements ShareCompatImpl {
-        @Override
-        public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
-            item.setIntent(shareIntent.createChooserIntent());
-        }
-
-        @Override
-        public String escapeHtml(CharSequence text) {
-            StringBuilder out = new StringBuilder();
-            withinStyle(out, text, 0, text.length());
-            return out.toString();
-        }
-
-        private static void withinStyle(StringBuilder out, CharSequence text,
-                int start, int end) {
-            for (int i = start; i < end; i++) {
-                char c = text.charAt(i);
-
-                if (c == '<') {
-                    out.append("&lt;");
-                } else if (c == '>') {
-                    out.append("&gt;");
-                } else if (c == '&') {
-                    out.append("&amp;");
-                } else if (c > 0x7E || c < ' ') {
-                    out.append("&#" + ((int) c) + ";");
-                } else if (c == ' ') {
-                    while (i + 1 < end && text.charAt(i + 1) == ' ') {
-                        out.append("&nbsp;");
-                        i++;
-                    }
-
-                    out.append(' ');
-                } else {
-                    out.append(c);
-                }
-            }
-        }
-    }
-
-    static class ShareCompatImplICS extends ShareCompatImplBase {
-        @Override
-        public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
-            ShareCompatICS.configureMenuItem(item, shareIntent.getActivity(),
-                    shareIntent.getIntent());
-            if (shouldAddChooserIntent(item)) {
-                item.setIntent(shareIntent.createChooserIntent());
-            }
-        }
-
-        boolean shouldAddChooserIntent(MenuItem item) {
-            return !item.hasSubMenu();
-        }
-    }
-
-    static class ShareCompatImplJB extends ShareCompatImplICS {
-        @Override
-        public String escapeHtml(CharSequence html) {
-            return ShareCompatJB.escapeHtml(html);
-        }
-
-        @Override
-        boolean shouldAddChooserIntent(MenuItem item) {
-            return false;
-        }
-    }
-
-    static ShareCompatImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 16) {
-            IMPL = new ShareCompatImplJB();
-        } else if (Build.VERSION.SDK_INT >= 14) {
-            IMPL = new ShareCompatImplICS();
-        } else {
-            IMPL = new ShareCompatImplBase();
-        }
-    }
+    private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
 
     private ShareCompat() {}
 
@@ -207,23 +124,18 @@
     /**
      * Configure a {@link MenuItem} to act as a sharing action.
      *
-     * <p>If the app is running on API level 14 or higher (Android 4.0/Ice Cream Sandwich)
-     * this method will configure a ShareActionProvider to provide a more robust UI
+     * <p>This method will configure a ShareActionProvider to provide a more robust UI
      * for selecting the target of the share. History will be tracked for each calling
      * activity in a file named with the prefix ".sharecompat_" in the application's
      * private data directory. If the application wishes to set this MenuItem to show
-     * as an action in the Action Bar it should use
-     * {@link MenuItemCompat#setShowAsAction(MenuItem, int)} to request that behavior
-     * in addition to calling this method.</p>
-     *
-     * <p>If the app is running on an older platform version this method will configure
-     * a standard activity chooser dialog for the menu item.</p>
+     * as an action in the Action Bar it should use {@link MenuItem#setShowAsAction(int)} to request
+     * that behavior in addition to calling this method.</p>
      *
      * <p>During the calling activity's lifecycle, if data within the share intent must
      * change the app should change that state in one of several ways:</p>
      * <ul>
-     * <li>Call {@link ActivityCompat#invalidateOptionsMenu(Activity)}. If the app is running
-     * on API level 11 or above and uses the Action Bar its menu will be recreated and rebuilt.
+     * <li>Call {@link ActivityCompat#invalidateOptionsMenu(Activity)}. If the app uses the
+     * Action Bar its menu will be recreated and rebuilt.
      * If not, the activity will receive a call to {@link Activity#onPrepareOptionsMenu(Menu)}
      * the next time the user presses the menu key to open the options menu panel. The activity
      * can then call configureMenuItem again with a new or altered IntentBuilder to reconfigure
@@ -236,7 +148,23 @@
      * @param shareIntent IntentBuilder with data about the content to share
      */
     public static void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
-        IMPL.configureMenuItem(item, shareIntent);
+        ActionProvider itemProvider = item.getActionProvider();
+        ShareActionProvider provider;
+        if (!(itemProvider instanceof ShareActionProvider)) {
+            provider = new ShareActionProvider(shareIntent.getActivity());
+        } else {
+            provider = (ShareActionProvider) itemProvider;
+        }
+        provider.setShareHistoryFileName(HISTORY_FILENAME_PREFIX
+                + shareIntent.getActivity().getClass().getName());
+        provider.setShareIntent(shareIntent.getIntent());
+        item.setActionProvider(provider);
+
+        if (SDK_INT < 16) {
+            if (!item.hasSubMenu()) {
+                item.setIntent(shareIntent.createChooserIntent());
+            }
+        }
     }
 
     /**
@@ -250,8 +178,8 @@
     public static void configureMenuItem(Menu menu, int menuItemId, IntentBuilder shareIntent) {
         MenuItem item = menu.findItem(menuItemId);
         if (item == null) {
-            throw new IllegalArgumentException("Could not find menu item with id " + menuItemId +
-                    " in the supplied menu");
+            throw new IllegalArgumentException("Could not find menu item with id " + menuItemId
+                    + " in the supplied menu");
         }
         configureMenuItem(item, shareIntent);
     }
@@ -759,12 +687,44 @@
                 if (text instanceof Spanned) {
                     result = Html.toHtml((Spanned) text);
                 } else if (text != null) {
-                    result = IMPL.escapeHtml(text);
+                    if (SDK_INT >= 16) {
+                        result = Html.escapeHtml(text);
+                    } else {
+                        StringBuilder out = new StringBuilder();
+                        withinStyle(out, text, 0, text.length());
+                        result = out.toString();
+                    }
                 }
             }
             return result;
         }
 
+        private static void withinStyle(StringBuilder out, CharSequence text,
+                int start, int end) {
+            for (int i = start; i < end; i++) {
+                char c = text.charAt(i);
+
+                if (c == '<') {
+                    out.append("&lt;");
+                } else if (c == '>') {
+                    out.append("&gt;");
+                } else if (c == '&') {
+                    out.append("&amp;");
+                } else if (c > 0x7E || c < ' ') {
+                    out.append("&#" + ((int) c) + ";");
+                } else if (c == ' ') {
+                    while (i + 1 < end && text.charAt(i + 1) == ' ') {
+                        out.append("&nbsp;");
+                        i++;
+                    }
+
+                    out.append(' ');
+                } else {
+                    out.append(c);
+                }
+            }
+        }
+
         /**
          * Get a URI referring to a data stream shared with the target activity.
          *
@@ -799,8 +759,8 @@
             if (index == 0) {
                 return mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
             }
-            throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount() +
-                    " index requested: " + index);
+            throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount()
+                    + " index requested: " + index);
         }
 
         /**
diff --git a/compat/java/android/support/v4/app/SharedElementCallback.java b/compat/java/android/support/v4/app/SharedElementCallback.java
index 5c64762..c218d86 100644
--- a/compat/java/android/support/v4/app/SharedElementCallback.java
+++ b/compat/java/android/support/v4/app/SharedElementCallback.java
@@ -190,9 +190,9 @@
         int bitmapHeight = Math.round(screenBounds.height());
         Bitmap bitmap = null;
         if (bitmapWidth > 0 && bitmapHeight > 0) {
-            float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
-            bitmapWidth *= scale;
-            bitmapHeight *= scale;
+            float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
+            bitmapWidth = (int) (bitmapWidth * scale);
+            bitmapHeight = (int) (bitmapHeight * scale);
             if (mTempMatrix == null) {
                 mTempMatrix = new Matrix();
             }
diff --git a/compat/java/android/support/v4/content/ContentResolverCompat.java b/compat/java/android/support/v4/content/ContentResolverCompat.java
index 7efe79e..1076738 100644
--- a/compat/java/android/support/v4/content/ContentResolverCompat.java
+++ b/compat/java/android/support/v4/content/ContentResolverCompat.java
@@ -16,71 +16,19 @@
 
 package android.support.v4.content;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.content.ContentResolver;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.Build;
 import android.support.v4.os.CancellationSignal;
 import android.support.v4.os.OperationCanceledException;
 
 /**
- * Helper for accessing features in {@link android.content.ContentResolver}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.content.ContentResolver} in a backwards
+ * compatible fashion.
  */
 public final class ContentResolverCompat {
-    interface ContentResolverCompatImpl {
-        Cursor query(ContentResolver resolver,
-                Uri uri, String[] projection, String selection, String[] selectionArgs,
-                String sortOrder, CancellationSignal cancellationSignal);
-    }
-
-    static class ContentResolverCompatImplBase implements ContentResolverCompatImpl {
-        @Override
-        public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
-                String selection, String[] selectionArgs, String sortOrder,
-                CancellationSignal cancellationSignal) {
-            // Note that the cancellation signal cannot cancel the query in progress
-            // prior to Jellybean so we cancel it preemptively here if needed.
-            if (cancellationSignal != null) {
-                cancellationSignal.throwIfCanceled();
-            }
-            return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
-        }
-    }
-
-    static class ContentResolverCompatImplJB extends ContentResolverCompatImplBase {
-        @Override
-        public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
-                String selection, String[] selectionArgs, String sortOrder,
-                CancellationSignal cancellationSignal) {
-            try {
-                return ContentResolverCompatJellybean.query(resolver,
-                        uri, projection, selection, selectionArgs, sortOrder,
-                        cancellationSignal != null ?
-                                cancellationSignal.getCancellationSignalObject() : null);
-            } catch (Exception e) {
-                if (ContentResolverCompatJellybean.isFrameworkOperationCanceledException(e)) {
-                    // query() can throw a framework OperationCanceledException if it has been
-                    // canceled. We catch that and throw the support version instead.
-                    throw new OperationCanceledException();
-                } else {
-                    // If it's not a framework OperationCanceledException, re-throw the exception
-                    throw e;
-                }
-            }
-        }
-    }
-
-    private static final ContentResolverCompatImpl IMPL;
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 16) {
-            IMPL = new ContentResolverCompatImplJB();
-        } else {
-            IMPL = new ContentResolverCompatImplBase();
-        }
-    }
-
     private ContentResolverCompat() {
         /* Hide constructor */
     }
@@ -122,7 +70,32 @@
     public static Cursor query(ContentResolver resolver,
             Uri uri, String[] projection, String selection, String[] selectionArgs,
             String sortOrder, CancellationSignal cancellationSignal) {
-        return IMPL.query(resolver, uri, projection, selection, selectionArgs,
-                sortOrder, cancellationSignal);
+        if (SDK_INT >= 16) {
+            try {
+                final android.os.CancellationSignal cancellationSignalObj =
+                        (android.os.CancellationSignal)
+                                (cancellationSignal != null
+                                        ? cancellationSignal.getCancellationSignalObject()
+                                        : null);
+                return resolver.query(uri, projection, selection, selectionArgs, sortOrder,
+                        cancellationSignalObj);
+            } catch (Exception e) {
+                if (e instanceof android.os.OperationCanceledException) {
+                    // query() can throw a framework OperationCanceledException if it has been
+                    // canceled. We catch that and throw the support version instead.
+                    throw new OperationCanceledException();
+                } else {
+                    // If it's not a framework OperationCanceledException, re-throw the exception
+                    throw e;
+                }
+            }
+        } else {
+            // Note that the cancellation signal cannot cancel the query in progress
+            // prior to Jellybean so we cancel it preemptively here if needed.
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+            }
+            return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+        }
     }
 }
diff --git a/compat/java/android/support/v4/content/ContextCompat.java b/compat/java/android/support/v4/content/ContextCompat.java
index d51d6cb..fdbe32f 100644
--- a/compat/java/android/support/v4/content/ContextCompat.java
+++ b/compat/java/android/support/v4/content/ContextCompat.java
@@ -23,7 +23,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Process;
 import android.support.annotation.ColorInt;
 import android.support.annotation.ColorRes;
@@ -31,7 +30,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.ActivityOptionsCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.os.EnvironmentCompat;
 import android.util.Log;
 import android.util.TypedValue;
@@ -39,15 +37,11 @@
 import java.io.File;
 
 /**
- * Helper for accessing features in {@link android.content.Context}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.content.Context}.
  */
 public class ContextCompat {
     private static final String TAG = "ContextCompat";
 
-    private static final String DIR_ANDROID = "Android";
-    private static final String DIR_OBB = "obb";
-
     private static final Object sLock = new Object();
 
     private static TypedValue sTempValue;
@@ -116,17 +110,13 @@
      * See {@link android.content.Context#startActivity(Intent, android.os.Bundle)}
      * @return true if the underlying API was available and the call was successful, false otherwise
      */
-    public static boolean startActivities(Context context, Intent[] intents,
-            Bundle options) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 16) {
-            ContextCompatJellybean.startActivities(context, intents, options);
-            return true;
-        } else if (version >= 11) {
-            ContextCompatHoneycomb.startActivities(context, intents);
-            return true;
+    public static boolean startActivities(Context context, Intent[] intents, Bundle options) {
+        if (Build.VERSION.SDK_INT >= 16) {
+            context.startActivities(intents, options);
+        } else {
+            context.startActivities(intents);
         }
-        return false;
+        return true;
     }
 
     /**
@@ -148,7 +138,7 @@
      */
     public static void startActivity(Context context, Intent intent, @Nullable Bundle options) {
         if (Build.VERSION.SDK_INT >= 16) {
-            ContextCompatJellybean.startActivity(context, intent, options);
+            context.startActivity(intent, options);
         } else {
             context.startActivity(intent);
         }
@@ -170,8 +160,8 @@
      * @see ApplicationInfo#dataDir
      */
     public static File getDataDir(Context context) {
-        if (BuildCompat.isAtLeastN()) {
-            return ContextCompatApi24.getDataDir(context);
+        if (Build.VERSION.SDK_INT >= 24) {
+            return context.getDataDir();
         } else {
             final String dataDir = context.getApplicationInfo().dataDir;
             return dataDir != null ? new File(dataDir) : null;
@@ -222,18 +212,10 @@
      * @see EnvironmentCompat#getStorageState(File)
      */
     public static File[] getObbDirs(Context context) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            return ContextCompatKitKat.getObbDirs(context);
+        if (Build.VERSION.SDK_INT >= 19) {
+            return context.getObbDirs();
         } else {
-            final File single;
-            if (version >= 11) {
-                single = ContextCompatHoneycomb.getObbDir(context);
-            } else {
-                single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_OBB,
-                        context.getPackageName());
-            }
-            return new File[] { single };
+            return new File[] { context.getObbDir() };
         }
     }
 
@@ -282,9 +264,8 @@
      * @see EnvironmentCompat#getStorageState(File)
      */
     public static File[] getExternalFilesDirs(Context context, String type) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            return ContextCompatKitKat.getExternalFilesDirs(context, type);
+        if (Build.VERSION.SDK_INT >= 19) {
+            return context.getExternalFilesDirs(type);
         } else {
             return new File[] { context.getExternalFilesDir(type) };
         }
@@ -335,9 +316,8 @@
      * @see EnvironmentCompat#getStorageState(File)
      */
     public static File[] getExternalCacheDirs(Context context) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            return ContextCompatKitKat.getExternalCacheDirs(context);
+        if (Build.VERSION.SDK_INT >= 19) {
+            return context.getExternalCacheDirs();
         } else {
             return new File[] { context.getExternalCacheDir() };
         }
@@ -367,10 +347,9 @@
      * @return Drawable An object that can be used to draw this resource.
      */
     public static final Drawable getDrawable(Context context, @DrawableRes int id) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            return ContextCompatApi21.getDrawable(context, id);
-        } else if (version >= 16) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            return context.getDrawable(id);
+        } else if (Build.VERSION.SDK_INT >= 16) {
             return context.getResources().getDrawable(id);
         } else {
             // Prior to JELLY_BEAN, Resources.getDrawable() would not correctly
@@ -404,9 +383,8 @@
      *         does not exist.
      */
     public static final ColorStateList getColorStateList(Context context, @ColorRes int id) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 23) {
-            return ContextCompatApi23.getColorStateList(context, id);
+        if (Build.VERSION.SDK_INT >= 23) {
+            return context.getColorStateList(id);
         } else {
             return context.getResources().getColorStateList(id);
         }
@@ -427,9 +405,8 @@
      */
     @ColorInt
     public static final int getColor(Context context, @ColorRes int id) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 23) {
-            return ContextCompatApi23.getColor(context, id);
+        if (Build.VERSION.SDK_INT >= 23) {
+            return context.getColor(id);
         } else {
             return context.getResources().getColor(id);
         }
@@ -468,9 +445,8 @@
      * @see android.content.Context#getFilesDir()
      */
     public static final File getNoBackupFilesDir(Context context) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            return ContextCompatApi21.getNoBackupFilesDir(context);
+        if (Build.VERSION.SDK_INT >= 21) {
+            return context.getNoBackupFilesDir();
         } else {
             ApplicationInfo appInfo = context.getApplicationInfo();
             return createFilesDir(new File(appInfo.dataDir, "no_backup"));
@@ -493,9 +469,8 @@
      * @return The path of the directory holding application code cache files.
      */
     public static File getCodeCacheDir(Context context) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            return ContextCompatApi21.getCodeCacheDir(context);
+        if (Build.VERSION.SDK_INT >= 21) {
+            return context.getCodeCacheDir();
         } else {
             ApplicationInfo appInfo = context.getApplicationInfo();
             return createFilesDir(new File(appInfo.dataDir, "code_cache"));
@@ -542,14 +517,14 @@
      * Resources for the same configuration) may be so the Context itself can be
      * fairly lightweight.
      * <p>
-     * Prior to {@link BuildCompat#isAtLeastN()} this method returns
+     * Prior to API 24 this method returns
      * {@code null}, since device-protected storage is not available.
      *
      * @see ContextCompat#isDeviceProtectedStorage(Context)
      */
     public static Context createDeviceProtectedStorageContext(Context context) {
-        if (BuildCompat.isAtLeastN()) {
-            return ContextCompatApi24.createDeviceProtectedStorageContext(context);
+        if (Build.VERSION.SDK_INT >= 24) {
+            return context.createDeviceProtectedStorageContext();
         } else {
             return null;
         }
@@ -562,10 +537,29 @@
      * @see ContextCompat#createDeviceProtectedStorageContext(Context)
      */
     public static boolean isDeviceProtectedStorage(Context context) {
-        if (BuildCompat.isAtLeastN()) {
-            return ContextCompatApi24.isDeviceProtectedStorage(context);
+        if (Build.VERSION.SDK_INT >= 24) {
+            return context.isDeviceProtectedStorage();
         } else {
             return false;
         }
     }
+
+    /**
+     * startForegroundService() was introduced in O, just call startService
+     * for before O.
+     *
+     * @param context Context to start Service from.
+     * @param intent The description of the Service to start.
+     *
+     * @see Context#startForegeroundService()
+     * @see Context#startService()
+     */
+    public static void startForegroundService(Context context, Intent intent) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            context.startForegroundService(intent);
+        } else {
+            // Pre-O behavior.
+            context.startService(intent);
+        }
+    }
 }
diff --git a/compat/java/android/support/v4/content/IntentCompat.java b/compat/java/android/support/v4/content/IntentCompat.java
index 2ea0a10..cf6b012 100644
--- a/compat/java/android/support/v4/content/IntentCompat.java
+++ b/compat/java/android/support/v4/content/IntentCompat.java
@@ -20,29 +20,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 
 /**
- * Helper for accessing features in {@link android.content.Intent}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.content.Intent}.
  */
 public final class IntentCompat {
 
-    interface IntentCompatImpl {
-        Intent makeMainActivity(ComponentName componentName);
-        Intent makeMainSelectorActivity(String selectorAction, String selectorCategory);
-        Intent makeRestartActivityTask(ComponentName mainActivity);
-    }
-
-    static class IntentCompatImplBase implements IntentCompatImpl {
-        @Override
-        public Intent makeMainActivity(ComponentName componentName) {
-            Intent intent = new Intent(Intent.ACTION_MAIN);
-            intent.setComponent(componentName);
-            intent.addCategory(Intent.CATEGORY_LAUNCHER);
-            return intent;
-        }
-
-        @Override
+    static class IntentCompatBaseImpl {
         public Intent makeMainSelectorActivity(String selectorAction,
                 String selectorCategory) {
             // Before api 15 you couldn't set a selector intent.
@@ -52,43 +37,22 @@
             intent.addCategory(selectorCategory);
             return intent;
         }
-
-        @Override
-        public Intent makeRestartActivityTask(ComponentName mainActivity) {
-            Intent intent = makeMainActivity(mainActivity);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
-            return intent;
-        }
     }
 
-    static class IntentCompatImplHC extends IntentCompatImplBase {
-        @Override
-        public Intent makeMainActivity(ComponentName componentName) {
-            return IntentCompatHoneycomb.makeMainActivity(componentName);
-        }
-        @Override
-        public Intent makeRestartActivityTask(ComponentName componentName) {
-            return IntentCompatHoneycomb.makeRestartActivityTask(componentName);
-        }
-    }
-
-    static class IntentCompatImplIcsMr1 extends IntentCompatImplHC {
+    @RequiresApi(15)
+    static class IntentCompatApi15Impl extends IntentCompatBaseImpl {
         @Override
         public Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) {
-            return IntentCompatIcsMr1.makeMainSelectorActivity(selectorAction, selectorCategory);
+            return Intent.makeMainSelectorActivity(selectorAction, selectorCategory);
         }
     }
 
-    private static final IntentCompatImpl IMPL;
+    private static final IntentCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 15) {
-            IMPL = new IntentCompatImplIcsMr1();
-        } else if (version >= 11) {
-            IMPL = new IntentCompatImplHC();
+        if (Build.VERSION.SDK_INT >= 15) {
+            IMPL = new IntentCompatApi15Impl();
         } else {
-            IMPL = new IntentCompatImplBase();
+            IMPL = new IntentCompatBaseImpl();
         }
     }
 
@@ -118,7 +82,10 @@
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
+     *
+     * @deprecated Use {@link Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE} directly.
      */
+    @Deprecated
     public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE =
         "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
 
@@ -141,7 +108,10 @@
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
+     *
+     * @deprecated Use {@link Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE} directly.
      */
+    @Deprecated
     public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE =
         "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
 
@@ -150,7 +120,10 @@
      * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
      * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
      * and contains a string array of all of the components that have changed.
+     *
+     * @deprecated Use {@link Intent#EXTRA_CHANGED_PACKAGE_LIST} directly.
      */
+    @Deprecated
     public static final String EXTRA_CHANGED_PACKAGE_LIST =
             "android.intent.extra.changed_package_list";
 
@@ -160,7 +133,10 @@
      * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
      * and contains an integer array of uids of all of the components
      * that have changed.
+     *
+     * @deprecated Use {@link Intent#EXTRA_CHANGED_UID_LIST} directly.
      */
+    @Deprecated
     public static final String EXTRA_CHANGED_UID_LIST =
             "android.intent.extra.changed_uid_list";
 
@@ -193,7 +169,10 @@
      * will always return the user to home even if that was not the last activity they
      * saw. This can only be used in conjunction with
      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}.
+     *
+     * @deprecated Use {@link Intent#FLAG_ACTIVITY_TASK_ON_HOME} directly.
      */
+    @Deprecated
     public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0x00004000;
 
     /**
@@ -205,7 +184,10 @@
      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}.
      *
      * <p>This flag will only be obeyed on devices supporting API 11 or higher.</p>
+     *
+     * @deprecated Use {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} directly.
      */
+    @Deprecated
     public static final int FLAG_ACTIVITY_CLEAR_TASK = 0x00008000;
 
     /**
@@ -229,12 +211,14 @@
      *
      * @see Intent#setClass
      * @see Intent#setComponent
+     *
+     * @deprecated Use {@link Intent#makeMainActivity(ComponentName)} directly.
      */
+    @Deprecated
     public static Intent makeMainActivity(ComponentName mainActivity) {
-        return IMPL.makeMainActivity(mainActivity);
+        return Intent.makeMainActivity(mainActivity);
     }
 
-
     /**
      * Make an Intent for the main activity of an application, without
      * specifying a specific activity to run but giving a selector to find
@@ -264,7 +248,7 @@
      * Make an Intent that can be used to re-launch an application's task
      * in its base state.  This is like {@link #makeMainActivity(ComponentName)},
      * but also sets the flags {@link Intent#FLAG_ACTIVITY_NEW_TASK} and
-     * {@link IntentCompat#FLAG_ACTIVITY_CLEAR_TASK}.
+     * {@link Intent#FLAG_ACTIVITY_CLEAR_TASK}.
      *
      * @param mainActivity The activity component that is the root of the
      * task; this is the activity that has been published in the application's
@@ -272,8 +256,11 @@
      *
      * @return Returns a newly created Intent that can be used to relaunch the
      * activity's task in its root state.
+     *
+     * @deprecated Use {@link Intent#makeRestartActivityTask(ComponentName)} directly.
      */
+    @Deprecated
     public static Intent makeRestartActivityTask(ComponentName mainActivity) {
-        return IMPL.makeRestartActivityTask(mainActivity);
+        return Intent.makeRestartActivityTask(mainActivity);
     }
 }
diff --git a/compat/java/android/support/v4/content/ModernAsyncTask.java b/compat/java/android/support/v4/content/ModernAsyncTask.java
index 306d334..8e2991a 100644
--- a/compat/java/android/support/v4/content/ModernAsyncTask.java
+++ b/compat/java/android/support/v4/content/ModernAsyncTask.java
@@ -47,7 +47,7 @@
  *
  * <p>Note that for now this is not publicly available because it is not a
  * complete implementation, only sufficient for the needs of
- * {@link AsyncTaskLoader}.
+ * {@link android.content.AsyncTaskLoader}.
  */
 abstract class ModernAsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
@@ -178,7 +178,7 @@
 
     Result postResult(Result result) {
         Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
-                new AsyncTaskResult<Result>(this, result));
+                new AsyncTaskResult<>(this, result));
         message.sendToTarget();
         return result;
     }
@@ -436,6 +436,8 @@
                     throw new IllegalStateException("Cannot execute task:"
                             + " the task has already been executed "
                             + "(a task can be executed only once)");
+                default:
+                    throw new IllegalStateException("We should never reach this state");
             }
         }
 
diff --git a/compat/java/android/support/v4/content/ParallelExecutorCompat.java b/compat/java/android/support/v4/content/ParallelExecutorCompat.java
index 20ef3d5..4dc4168 100644
--- a/compat/java/android/support/v4/content/ParallelExecutorCompat.java
+++ b/compat/java/android/support/v4/content/ParallelExecutorCompat.java
@@ -16,22 +16,24 @@
 
 package android.support.v4.content;
 
-import android.os.Build;
+import android.os.AsyncTask;
 
 import java.util.concurrent.Executor;
 
 /**
- * Helper for accessing a shared parallel Executor instance
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing a shared parallel Executor instance.
+ *
+ * @deprecated Use {@link AsyncTask} directly.
  */
+@Deprecated
 public final class ParallelExecutorCompat {
+
+    /**
+     * @deprecated Use {@link AsyncTask#THREAD_POOL_EXECUTOR} directly.
+     */
+    @Deprecated
     public static Executor getParallelExecutor() {
-        if (Build.VERSION.SDK_INT >= 11) {
-            // From API 11 onwards, return AsyncTask.THREAD_POOL_EXECUTOR
-            return ExecutorCompatHoneycomb.getParallelExecutor();
-        } else {
-            return ModernAsyncTask.THREAD_POOL_EXECUTOR;
-        }
+        return AsyncTask.THREAD_POOL_EXECUTOR;
     }
 
     private ParallelExecutorCompat() {}
diff --git a/compat/java/android/support/v4/content/pm/ActivityInfoCompat.java b/compat/java/android/support/v4/content/pm/ActivityInfoCompat.java
index 4014404..61bed07 100644
--- a/compat/java/android/support/v4/content/pm/ActivityInfoCompat.java
+++ b/compat/java/android/support/v4/content/pm/ActivityInfoCompat.java
@@ -17,8 +17,7 @@
 package android.support.v4.content.pm;
 
 /**
- * Helper for accessing features in {@link android.content.pm.ActivityInfo}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.content.pm.ActivityInfo}.
  */
 public final class ActivityInfoCompat {
 
diff --git a/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java b/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java
new file mode 100644
index 0000000..eb892c6
--- /dev/null
+++ b/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java
@@ -0,0 +1,293 @@
+/**
+ * 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.
+ */
+package android.support.v4.content.pm;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.v4.graphics.drawable.IconCompat;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+
+/**
+ * Helper for accessing features in {@link android.content.pm.ShortcutInfo}.
+ */
+public class ShortcutInfoCompat {
+
+    private Context mContext;
+    private String mId;
+
+    private Intent[] mIntents;
+    private ComponentName mActivity;
+
+    private CharSequence mLabel;
+    private CharSequence mLongLabel;
+    private CharSequence mDisabledMessage;
+
+    private IconCompat mIcon;
+
+    private ShortcutInfoCompat() { }
+
+    @RequiresApi(26)
+    ShortcutInfo toShortcutInfo() {
+        ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mContext, mId)
+                .setShortLabel(mLabel)
+                .setIntents(mIntents);
+        if (mIcon != null) {
+            builder.setIcon(mIcon.toIcon());
+        }
+        if (!TextUtils.isEmpty(mLongLabel)) {
+            builder.setLongLabel(mLongLabel);
+        }
+        if (!TextUtils.isEmpty(mDisabledMessage)) {
+            builder.setDisabledMessage(mDisabledMessage);
+        }
+        if (mActivity != null) {
+            builder.setActivity(mActivity);
+        }
+        return builder.build();
+    }
+
+    Intent addToIntent(Intent outIntent) {
+        outIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, mIntents[mIntents.length - 1])
+                .putExtra(Intent.EXTRA_SHORTCUT_NAME, mLabel.toString());
+        if (mIcon != null) {
+            mIcon.addToShortcutIntent(outIntent);
+        }
+        return outIntent;
+    }
+
+    /**
+     * Returns the ID of a shortcut.
+     *
+     * <p>Shortcut IDs are unique within each publisher app and must be stable across
+     * devices so that shortcuts will still be valid when restored on a different device.
+     * See {@link android.content.pm.ShortcutManager} for details.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Return the target activity.
+     *
+     * <p>This has nothing to do with the activity that this shortcut will launch.
+     * Launcher apps should show the launcher icon for the returned activity alongside
+     * this shortcut.
+     *
+     * @see Builder#setActivity(ComponentName)
+     */
+    @Nullable
+    public ComponentName getActivity() {
+        return mActivity;
+    }
+
+    /**
+     * Return the short description of a shortcut.
+     *
+     * @see Builder#setShortLabel(CharSequence)
+     */
+    @NonNull
+    public CharSequence getShortLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Return the long description of a shortcut.
+     *
+     * @see Builder#setLongLabel(CharSequence)
+     */
+    @Nullable
+    public CharSequence getLongLabel() {
+        return mLongLabel;
+    }
+
+    /**
+     * Return the message that should be shown when the user attempts to start a shortcut
+     * that is disabled.
+     *
+     * @see Builder#setDisabledMessage(CharSequence)
+     */
+    @Nullable
+    public CharSequence getDisabledMessage() {
+        return mDisabledMessage;
+    }
+
+    /**
+     * Returns the intent that is executed when the user selects this shortcut.
+     * If setIntents() was used, then return the last intent in the array.
+     *
+     * @see Builder#setIntent(Intent)
+     */
+    @NonNull
+    public Intent getIntent() {
+        return mIntents[mIntents.length - 1];
+    }
+
+    /**
+     * Return the intent set with {@link Builder#setIntents(Intent[])}.
+     *
+     * @see Builder#setIntents(Intent[])
+     */
+    @NonNull
+    public Intent[] getIntents() {
+        return Arrays.copyOf(mIntents, mIntents.length);
+    }
+
+    /**
+     * Builder class for {@link ShortcutInfoCompat} objects.
+     */
+    public static class Builder {
+
+        private final ShortcutInfoCompat mInfo;
+
+        public Builder(@NonNull Context context, @NonNull String id) {
+            mInfo = new ShortcutInfoCompat();
+            mInfo.mContext = context;
+            mInfo.mId = id;
+        }
+
+        /**
+         * Sets the short title of a shortcut.
+         *
+         * <p>This is a mandatory field when publishing a new shortcut.
+         *
+         * <p>This field is intended to be a concise description of a shortcut.
+         *
+         * <p>The recommended maximum length is 10 characters.
+         */
+        @NonNull
+        public Builder setShortLabel(@NonNull CharSequence shortLabel) {
+            mInfo.mLabel = shortLabel;
+            return this;
+        }
+
+        /**
+         * Sets the text of a shortcut.
+         *
+         * <p>This field is intended to be more descriptive than the shortcut title. The launcher
+         * shows this instead of the short title when it has enough space.
+         *
+         * <p>The recommend maximum length is 25 characters.
+         */
+        @NonNull
+        public Builder setLongLabel(@NonNull CharSequence longLabel) {
+            mInfo.mLongLabel = longLabel;
+            return this;
+        }
+
+        /**
+         * Sets the message that should be shown when the user attempts to start a shortcut that
+         * is disabled.
+         *
+         * @see ShortcutInfo#getDisabledMessage()
+         */
+        @NonNull
+        public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
+            mInfo.mDisabledMessage = disabledMessage;
+            return this;
+        }
+
+        /**
+         * Sets the intent of a shortcut.  Alternatively, {@link #setIntents(Intent[])} can be used
+         * to launch an activity with other activities in the back stack.
+         *
+         * <p>This is a mandatory field when publishing a new shortcut.
+         *
+         * <p>The given {@code intent} can contain extras, but these extras must contain values
+         * of primitive types in order for the system to persist these values.
+         */
+        @NonNull
+        public Builder setIntent(@NonNull Intent intent) {
+            return setIntents(new Intent[]{intent});
+        }
+
+        /**
+         * Sets multiple intents instead of a single intent, in order to launch an activity with
+         * other activities in back stack.  Use {@link android.app.TaskStackBuilder} to build
+         * intents. The last element in the list represents the only intent that doesn't place
+         * an activity on the back stack.
+         */
+        @NonNull
+        public Builder setIntents(@NonNull Intent[] intents) {
+            mInfo.mIntents = intents;
+            return this;
+        }
+
+        /**
+         * Sets an icon of a shortcut.
+         * @deprecated use {@link #setIcon(IconCompat)} instead
+         */
+        @NonNull
+        public Builder setIcon(@NonNull Bitmap icon) {
+            return setIcon(IconCompat.createWithBitmap(icon));
+        }
+
+        /**
+         * Sets an icon of a shortcut.
+         * @deprecated use {@link #setIcon(IconCompat)} instead
+         */
+        @NonNull
+        public Builder setIcon(@DrawableRes int icon) {
+            return setIcon(IconCompat.createWithResource(mInfo.mContext, icon));
+        }
+
+        /**
+         * Sets an icon of a shortcut.
+         */
+        @NonNull
+        public Builder setIcon(IconCompat icon) {
+            mInfo.mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the target activity. A shortcut will be shown along with this activity's icon
+         * on the launcher.
+         *
+         * @see ShortcutInfo#getActivity()
+         * @see android.content.pm.ShortcutInfo.Builder#setActivity(ComponentName)
+         */
+        @NonNull
+        public Builder setActivity(@NonNull ComponentName activity) {
+            mInfo.mActivity = activity;
+            return this;
+        }
+
+        /**
+         * Creates a {@link ShortcutInfoCompat} instance.
+         */
+        @NonNull
+        public ShortcutInfoCompat build() {
+            // Verify the arguments
+            if (TextUtils.isEmpty(mInfo.mLabel)) {
+                throw new IllegalArgumentException("Shortcut much have a non-empty label");
+            }
+            if (mInfo.mIntents == null || mInfo.mIntents.length == 0) {
+                throw new IllegalArgumentException("Shortcut much have an intent");
+            }
+            return mInfo;
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java b/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
new file mode 100644
index 0000000..189e51d
--- /dev/null
+++ b/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
@@ -0,0 +1,143 @@
+/**
+ * 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.
+ */
+
+package android.support.v4.content.pm;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutManager;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+
+/**
+ * Helper for accessing features in {@link android.content.pm.ShortcutManager}.
+ */
+public class ShortcutManagerCompat {
+
+    @VisibleForTesting static final String ACTION_INSTALL_SHORTCUT =
+            "com.android.launcher.action.INSTALL_SHORTCUT";
+    @VisibleForTesting static final String INSTALL_SHORTCUT_PERMISSION =
+            "com.android.launcher.permission.INSTALL_SHORTCUT";
+
+    private ShortcutManagerCompat() {
+        /* Hide constructor */
+    }
+
+    /**
+     * @return {@code true} if the launcher supports {@link #requestPinShortcut},
+     * {@code false} otherwise
+     */
+    public static boolean isRequestPinShortcutSupported(@NonNull Context context) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();
+        }
+
+        if (ContextCompat.checkSelfPermission(context, INSTALL_SHORTCUT_PERMISSION)
+                != PackageManager.PERMISSION_GRANTED) {
+            return false;
+        }
+        for (ResolveInfo info : context.getPackageManager().queryBroadcastReceivers(
+                new Intent(ACTION_INSTALL_SHORTCUT), 0)) {
+            String permission = info.activityInfo.permission;
+            if (TextUtils.isEmpty(permission) || INSTALL_SHORTCUT_PERMISSION.equals(permission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Request to create a pinned shortcut.
+     * <p>On API <= 25 it creates a legacy shortcut with the provided icon, label and intent. For
+     * newer APIs it will create a {@link android.content.pm.ShortcutInfo} object which can be
+     * updated by the app.
+     *
+     * <p>Use {@link android.app.PendingIntent#getIntentSender()} to create a {@link IntentSender}.
+     *
+     * @param shortcut new shortcut to pin
+     * @param callback if not null, this intent will be sent when the shortcut is pinned
+     *
+     * @return {@code true} if the launcher supports this feature
+     *
+     * @see #isRequestPinShortcutSupported
+     * @see IntentSender
+     * @see android.app.PendingIntent#getIntentSender()
+     */
+    public static boolean requestPinShortcut(@NonNull final Context context,
+            @NonNull ShortcutInfoCompat shortcut, @Nullable final IntentSender callback) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return context.getSystemService(ShortcutManager.class).requestPinShortcut(
+                    shortcut.toShortcutInfo(), callback);
+        }
+
+        if (!isRequestPinShortcutSupported(context)) {
+            return false;
+        }
+        Intent intent = shortcut.addToIntent(new Intent(ACTION_INSTALL_SHORTCUT));
+
+        // If the callback is null, just send the broadcast
+        if (callback == null) {
+            context.sendBroadcast(intent);
+            return true;
+        }
+
+        // Otherwise send the callback when the intent has successfully been dispatched.
+        context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                try {
+                    callback.sendIntent(context, 0, null, null, null);
+                } catch (IntentSender.SendIntentException e) {
+                    // Ignore
+                }
+            }
+        }, null, Activity.RESULT_OK, null, null);
+        return true;
+    }
+
+    /**
+     * Returns an Intent which can be used by the launcher to pin shortcut.
+     * <p>This should be used by an Activity to set result in response to
+     * {@link Intent#ACTION_CREATE_SHORTCUT}.
+     *
+     * @param shortcut new shortcut to pin
+     * @return the intent that should be set as the result for the calling activity
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     */
+    @NonNull
+    public static Intent createShortcutResultIntent(@NonNull Context context,
+            @NonNull ShortcutInfoCompat shortcut) {
+        Intent result = null;
+        if (Build.VERSION.SDK_INT >= 26) {
+            result = context.getSystemService(ShortcutManager.class)
+                    .createShortcutResultIntent(shortcut.toShortcutInfo());
+        }
+        if (result == null) {
+            result = new Intent();
+        }
+        return shortcut.addToIntent(result);
+    }
+}
diff --git a/compat/java/android/support/v4/content/res/ConfigurationHelper.java b/compat/java/android/support/v4/content/res/ConfigurationHelper.java
index b821bd6..a337671 100644
--- a/compat/java/android/support/v4/content/res/ConfigurationHelper.java
+++ b/compat/java/android/support/v4/content/res/ConfigurationHelper.java
@@ -16,91 +16,18 @@
 
 package android.support.v4.content.res;
 
+import static android.os.Build.VERSION.SDK_INT;
+
+import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.os.Build;
 import android.support.annotation.NonNull;
 
 /**
- * Helper class which allows access to properties of {@link android.content.res.Configuration} in
+ * Helper class which allows access to properties of {@link Configuration} in
  * a backward compatible fashion.
  */
 public final class ConfigurationHelper {
-
-    private static final ConfigurationHelperImpl IMPL;
-
-    static {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk >= 17) {
-            IMPL = new JellybeanMr1Impl();
-        } else if (sdk >= 13) {
-            IMPL = new HoneycombMr2Impl();
-        } else {
-            IMPL = new GingerbreadImpl();
-        }
-    }
-
-    private ConfigurationHelper() {}
-
-    private interface ConfigurationHelperImpl {
-        int getScreenHeightDp(@NonNull Resources resources);
-        int getScreenWidthDp(@NonNull Resources resources);
-        int getSmallestScreenWidthDp(@NonNull Resources resources);
-        int getDensityDpi(@NonNull Resources resources);
-    }
-
-    private static class GingerbreadImpl implements ConfigurationHelperImpl {
-        GingerbreadImpl() {
-        }
-
-        @Override
-        public int getScreenHeightDp(@NonNull Resources resources) {
-            return ConfigurationHelperGingerbread.getScreenHeightDp(resources);
-        }
-
-        @Override
-        public int getScreenWidthDp(@NonNull Resources resources) {
-            return ConfigurationHelperGingerbread.getScreenWidthDp(resources);
-        }
-
-        @Override
-        public int getSmallestScreenWidthDp(@NonNull Resources resources) {
-            return ConfigurationHelperGingerbread.getSmallestScreenWidthDp(resources);
-        }
-
-        @Override
-        public int getDensityDpi(@NonNull Resources resources) {
-            return ConfigurationHelperGingerbread.getDensityDpi(resources);
-        }
-    }
-
-    private static class HoneycombMr2Impl extends GingerbreadImpl {
-        HoneycombMr2Impl() {
-        }
-
-        @Override
-        public int getScreenHeightDp(@NonNull Resources resources) {
-            return ConfigurationHelperHoneycombMr2.getScreenHeightDp(resources);
-        }
-
-        @Override
-        public int getScreenWidthDp(@NonNull Resources resources) {
-            return ConfigurationHelperHoneycombMr2.getScreenWidthDp(resources);
-        }
-
-        @Override
-        public int getSmallestScreenWidthDp(@NonNull Resources resources) {
-            return ConfigurationHelperHoneycombMr2.getSmallestScreenWidthDp(resources);
-        }
-    }
-
-    private static class JellybeanMr1Impl extends HoneycombMr2Impl {
-        JellybeanMr1Impl() {
-        }
-
-        @Override
-        public int getDensityDpi(@NonNull Resources resources) {
-            return ConfigurationHelperJellybeanMr1.getDensityDpi(resources);
-        }
+    private ConfigurationHelper() {
     }
 
     /**
@@ -108,9 +35,12 @@
      *
      * <p>Uses {@code Configuration.screenHeightDp} when available, otherwise an approximation
      * is computed and returned.</p>
+     *
+     * @deprecated Use {@link Configuration#screenHeightDp} directly.
      */
+    @Deprecated
     public static int getScreenHeightDp(@NonNull Resources resources) {
-        return IMPL.getScreenHeightDp(resources);
+        return resources.getConfiguration().screenHeightDp;
     }
 
     /**
@@ -118,9 +48,12 @@
      *
      * <p>Uses {@code Configuration.screenWidthDp} when available, otherwise an approximation
      * is computed and returned.</p>
+     *
+     * @deprecated Use {@link Configuration#screenWidthDp} directly.
      */
+    @Deprecated
     public static int getScreenWidthDp(@NonNull Resources resources) {
-        return IMPL.getScreenWidthDp(resources);
+        return resources.getConfiguration().screenWidthDp;
     }
 
     /**
@@ -128,9 +61,12 @@
      *
      * <p>Uses {@code Configuration.smallestScreenWidthDp} when available, otherwise an
      * approximation is computed and returned.</p>
+     *
+     * @deprecated Use {@link Configuration#smallestScreenWidthDp} directly.
      */
+    @Deprecated
     public static int getSmallestScreenWidthDp(@NonNull Resources resources) {
-        return IMPL.getSmallestScreenWidthDp(resources);
+        return resources.getConfiguration().smallestScreenWidthDp;
     }
 
     /**
@@ -140,6 +76,10 @@
      * is computed and returned.</p>
      */
     public static int getDensityDpi(@NonNull Resources resources) {
-        return IMPL.getDensityDpi(resources);
+        if (SDK_INT >= 17) {
+            return resources.getConfiguration().densityDpi;
+        } else {
+            return resources.getDisplayMetrics().densityDpi;
+        }
     }
 }
diff --git a/compat/java/android/support/v4/content/res/FontResourcesParserCompat.java b/compat/java/android/support/v4/content/res/FontResourcesParserCompat.java
new file mode 100644
index 0000000..7fe86ad
--- /dev/null
+++ b/compat/java/android/support/v4/content/res/FontResourcesParserCompat.java
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.content.res;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.compat.R;
+import android.support.v4.provider.FontRequest;
+import android.util.AttributeSet;
+import android.util.Base64;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parser for xml type font resources.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class FontResourcesParserCompat {
+    private static final int NORMAL_WEIGHT = 400;
+    private static final int ITALIC = 1;
+
+    @IntDef({FETCH_STRATEGY_BLOCKING, FETCH_STRATEGY_ASYNC})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FetchStrategy {}
+
+    public static final int FETCH_STRATEGY_BLOCKING = 0;
+    public static final int FETCH_STRATEGY_ASYNC = 1;
+
+    // A special timeout value for infinite blocking.
+    public static final int INFINITE_TIMEOUT_VALUE = -1;
+
+    private static final int DEFAULT_TIMEOUT_MILLIS = 500;
+
+    /**
+     * A class that represents a single entry of font-family in an xml file.
+     */
+    public interface FamilyResourceEntry {}
+
+    /**
+     * A class that represents a font provider based font-family element in an xml file.
+     */
+    public static final class ProviderResourceEntry implements FamilyResourceEntry {
+        private final @NonNull FontRequest mRequest;
+        private final int mTimeoutMs;
+        private final @FetchStrategy int mStrategy;
+
+        public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
+                int timeoutMs) {
+            mRequest = request;
+            mStrategy = strategy;
+            mTimeoutMs = timeoutMs;
+        }
+
+        public @NonNull FontRequest getRequest() {
+            return mRequest;
+        }
+
+        public @FetchStrategy int getFetchStrategy() {
+            return mStrategy;
+        }
+
+        public int getTimeout() {
+            return mTimeoutMs;
+        }
+    }
+
+    /**
+     * A class that represents a font element in an xml file which points to a file in resources.
+     */
+    public static final class FontFileResourceEntry {
+        private final @NonNull String mFileName;
+        private int mWeight;
+        private boolean mItalic;
+        private int mResourceId;
+
+        public FontFileResourceEntry(@NonNull String fileName, int weight, boolean italic,
+                int resourceId) {
+            mFileName = fileName;
+            mWeight = weight;
+            mItalic = italic;
+            mResourceId = resourceId;
+        }
+
+        public @NonNull String getFileName() {
+            return mFileName;
+        }
+
+        public int getWeight() {
+            return mWeight;
+        }
+
+        public boolean isItalic() {
+            return mItalic;
+        }
+
+        public int getResourceId() {
+            return mResourceId;
+        }
+    }
+
+    /**
+     * A class that represents a file based font-family element in an xml font file.
+     */
+    public static final class FontFamilyFilesResourceEntry implements FamilyResourceEntry {
+        private final @NonNull FontFileResourceEntry[] mEntries;
+
+        public FontFamilyFilesResourceEntry(@NonNull FontFileResourceEntry[] entries) {
+            mEntries = entries;
+        }
+
+        public @NonNull FontFileResourceEntry[] getEntries() {
+            return mEntries;
+        }
+    }
+
+    /**
+     * Parse an XML font resource. The result type will depend on the contents of the xml.
+     */
+    public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Empty loop.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+        return readFamilies(parser, resources);
+    }
+
+    private static @Nullable FamilyResourceEntry readFamilies(XmlPullParser parser,
+            Resources resources) throws XmlPullParserException, IOException {
+        parser.require(XmlPullParser.START_TAG, null, "font-family");
+        String tag = parser.getName();
+        if (tag.equals("font-family")) {
+            return readFamily(parser, resources);
+        } else {
+            skip(parser);
+            return null;
+        }
+    }
+
+    private static @Nullable FamilyResourceEntry readFamily(XmlPullParser parser,
+            Resources resources) throws XmlPullParserException, IOException {
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
+        String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
+        String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
+        String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
+        int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0);
+        int strategy = array.getInteger(R.styleable.FontFamily_fontProviderFetchStrategy,
+                FETCH_STRATEGY_ASYNC);
+        int timeoutMs = array.getInteger(R.styleable.FontFamily_fontProviderFetchTimeout,
+                DEFAULT_TIMEOUT_MILLIS);
+        array.recycle();
+        if (authority != null && providerPackage != null && query != null) {
+            while (parser.next() != XmlPullParser.END_TAG) {
+                skip(parser);
+            }
+            List<List<byte[]>> certs = readCerts(resources, certsId);
+            return new ProviderResourceEntry(
+                    new FontRequest(authority, providerPackage, query, certs), strategy, timeoutMs);
+        }
+        List<FontFileResourceEntry> fonts = new ArrayList<>();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+            String tag = parser.getName();
+            if (tag.equals("font")) {
+                fonts.add(readFont(parser, resources));
+            } else {
+                skip(parser);
+            }
+        }
+        if (fonts.isEmpty()) {
+            return null;
+        }
+        return new FontFamilyFilesResourceEntry(fonts.toArray(
+                new FontFileResourceEntry[fonts.size()]));
+    }
+
+    /**
+     * Creates the necessary cert structure given a resources array. This method is capable of
+     * loading one string array as well as an array of string arrays.
+     */
+    public static List<List<byte[]>> readCerts(Resources resources, @ArrayRes int certsId) {
+        List<List<byte[]>> certs = null;
+        if (certsId != 0) {
+            TypedArray typedArray = resources.obtainTypedArray(certsId);
+            if (typedArray.length() > 0) {
+                certs = new ArrayList<>();
+                boolean isArrayOfArrays = typedArray.getResourceId(0, 0) != 0;
+                if (isArrayOfArrays) {
+                    for (int i = 0; i < typedArray.length(); i++) {
+                        int certId = typedArray.getResourceId(i, 0);
+                        String[] certsArray = resources.getStringArray(certId);
+                        List<byte[]> certsList = toByteArrayList(certsArray);
+                        certs.add(certsList);
+                    }
+                } else {
+                    String[] certsArray = resources.getStringArray(certsId);
+                    List<byte[]> certsList = toByteArrayList(certsArray);
+                    certs.add(certsList);
+                }
+            }
+            typedArray.recycle();
+        }
+        return certs != null ?  certs : Collections.<List<byte[]>>emptyList();
+    }
+
+    private static List<byte[]> toByteArrayList(String[] stringArray) {
+        List<byte[]> result = new ArrayList<>();
+        for (String item : stringArray) {
+            result.add(Base64.decode(item, Base64.DEFAULT));
+        }
+        return result;
+    }
+
+    private static FontFileResourceEntry readFont(XmlPullParser parser, Resources resources)
+            throws XmlPullParserException, IOException {
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont);
+        int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight, NORMAL_WEIGHT);
+        boolean isItalic = ITALIC == array.getInt(R.styleable.FontFamilyFont_fontStyle, 0);
+        int resourceId = array.getResourceId(R.styleable.FontFamilyFont_font, 0);
+        String filename = array.getString(R.styleable.FontFamilyFont_font);
+        array.recycle();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            skip(parser);
+        }
+        return new FontFileResourceEntry(filename, weight, isItalic, resourceId);
+    }
+
+    private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int depth = 1;
+        while (depth > 0) {
+            switch (parser.next()) {
+                case XmlPullParser.START_TAG:
+                    depth++;
+                    break;
+                case XmlPullParser.END_TAG:
+                    depth--;
+                    break;
+            }
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/content/res/ResourcesCompat.java b/compat/java/android/support/v4/content/res/ResourcesCompat.java
index 0f4b800..43d78d0 100644
--- a/compat/java/android/support/v4/content/res/ResourcesCompat.java
+++ b/compat/java/android/support/v4/content/res/ResourcesCompat.java
@@ -16,24 +16,40 @@
 
 package android.support.v4.content.res;
 
+import static android.os.Build.VERSION.SDK_INT;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
+import android.content.res.XmlResourceParser;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.annotation.ColorRes;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.FontRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry;
+import android.support.v4.graphics.TypefaceCompat;
+import android.util.Log;
+import android.util.TypedValue;
+import android.widget.TextView;
 
-import static android.os.Build.VERSION.SDK_INT;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 
 /**
- * Helper for accessing features in {@link android.content.res.Resources}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.content.res.Resources}.
  */
 public final class ResourcesCompat {
+    private static final String TAG = "ResourcesCompat";
+
     /**
      * Return a drawable object associated with a particular resource ID and
      * styled for the specified theme. Various types of objects will be
@@ -57,7 +73,7 @@
     public static Drawable getDrawable(@NonNull Resources res, @DrawableRes int id,
             @Nullable Theme theme) throws NotFoundException {
         if (SDK_INT >= 21) {
-            return ResourcesCompatApi21.getDrawable(res, id, theme);
+            return res.getDrawable(id, theme);
         } else {
             return res.getDrawable(id);
         }
@@ -90,9 +106,9 @@
     public static Drawable getDrawableForDensity(@NonNull Resources res, @DrawableRes int id,
             int density, @Nullable Theme theme) throws NotFoundException {
         if (SDK_INT >= 21) {
-            return ResourcesCompatApi21.getDrawableForDensity(res, id, density, theme);
+            return res.getDrawableForDensity(id, density, theme);
         } else if (SDK_INT >= 15) {
-            return ResourcesCompatIcsMr1.getDrawableForDensity(res, id, density);
+            return res.getDrawableForDensity(id, density);
         } else {
             return res.getDrawable(id);
         }
@@ -120,7 +136,7 @@
     public static int getColor(@NonNull Resources res, @ColorRes int id, @Nullable Theme theme)
             throws NotFoundException {
         if (SDK_INT >= 23) {
-            return ResourcesCompatApi23.getColor(res, id, theme);
+            return res.getColor(id, theme);
         } else {
             return res.getColor(id);
         }
@@ -150,11 +166,97 @@
     public static ColorStateList getColorStateList(@NonNull Resources res, @ColorRes int id,
             @Nullable Theme theme) throws NotFoundException {
         if (SDK_INT >= 23) {
-            return ResourcesCompatApi23.getColorStateList(res, id, theme);
+            return res.getColorStateList(id, theme);
         } else {
             return res.getColorStateList(id);
         }
     }
 
+    /**
+     * Returns a font Typeface associated with a particular resource ID.
+     * <p>
+     * Prior to API level 23, font resources with more than one font in a family will only load the
+     * first font in that family.
+     *
+     * @param context A context to retrieve the Resources from.
+     * @param id The desired resource identifier of a {@link Typeface},
+     *           as generated by the aapt tool. This integer encodes the
+     *           package, type, and resource entry. The value 0 is an invalid
+     *           identifier.
+     * @return A font Typeface object.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     */
+    @Nullable
+    public static Typeface getFont(@NonNull Context context, @FontRes int id)
+            throws NotFoundException {
+        if (context.isRestricted()) {
+            return null;
+        }
+        return loadFont(context, id, new TypedValue(), Typeface.NORMAL, null);
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,
+            int style, @Nullable TextView targetView) throws NotFoundException {
+        if (context.isRestricted()) {
+            return null;
+        }
+        return loadFont(context, id, value, style, targetView);
+    }
+
+    private static Typeface loadFont(@NonNull Context context, int id, TypedValue value,
+            int style, @Nullable TextView targetView) {
+        final Resources resources = context.getResources();
+        resources.getValue(id, value, true);
+        Typeface typeface = loadFont(context, resources, value, id, style, targetView);
+        if (typeface != null) {
+            return typeface;
+        }
+        throw new NotFoundException("Font resource ID #0x"
+                + Integer.toHexString(id));
+    }
+
+    private static Typeface loadFont(
+            @NonNull Context context, Resources wrapper, TypedValue value, int id, int style,
+            @Nullable TextView targetView) {
+        if (value.string == null) {
+            throw new NotFoundException("Resource \"" + wrapper.getResourceName(id) + "\" ("
+                    + Integer.toHexString(id) + ") is not a Font: " + value);
+        }
+
+        final String file = value.string.toString();
+        if (!file.startsWith("res/")) {
+            // Early exit if the specified string is unlikely to the resource path.
+            return null;
+        }
+
+        Typeface cached = TypefaceCompat.findFromCache(wrapper, id, style);
+        if (cached != null) {
+            return cached;
+        }
+
+        try {
+            if (file.toLowerCase().endsWith(".xml")) {
+                final XmlResourceParser rp = wrapper.getXml(id);
+                final FamilyResourceEntry familyEntry =
+                        FontResourcesParserCompat.parse(rp, wrapper);
+                if (familyEntry == null) {
+                    Log.e(TAG, "Failed to find font-family tag");
+                    return null;
+                }
+                return TypefaceCompat.createFromResourcesFamilyXml(
+                        context, familyEntry, wrapper, id, style, targetView);
+            }
+            return TypefaceCompat.createFromResourcesFontFile(context, wrapper, id, file, style);
+        } catch (XmlPullParserException e) {
+            Log.e(TAG, "Failed to parse xml resource " + file, e);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to read xml resource " + file, e);
+        }
+        return null;
+    }
+
     private ResourcesCompat() {}
 }
diff --git a/compat/java/android/support/v4/content/res/TypedArrayUtils.java b/compat/java/android/support/v4/content/res/TypedArrayUtils.java
new file mode 100644
index 0000000..e4d6b29
--- /dev/null
+++ b/compat/java/android/support/v4/content/res/TypedArrayUtils.java
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+package android.support.v4.content.res;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.AnyRes;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.StyleableRes;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Compat methods for accessing TypedArray values.
+ *
+ * All the getNamed*() functions added the attribute name match, to take care of potential ID
+ * collision between the private attributes in older OS version (OEM) and the attributes existed in
+ * the newer OS version.
+ * For example, if an private attribute named "abcdefg" in Kitkat has the
+ * same id value as "android:pathData" in Lollipop, we need to match the attribute's namefirst.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class TypedArrayUtils {
+
+    private static final String NAMESPACE = "http://schemas.android.com/apk/res/android";
+
+    /**
+     * @return Whether the current node ofthe  {@link XmlPullParser} has an attribute with the
+     * specified {@code attrName}.
+     */
+    public static boolean hasAttribute(@NonNull XmlPullParser parser, @NonNull String attrName) {
+        return parser.getAttributeValue(NAMESPACE, attrName) != null;
+    }
+
+    /**
+     * Retrieves a float attribute value. In addition to the styleable resource ID, we also make
+     * sure that the attribute name matches.
+     *
+     * @return a float value in the {@link TypedArray} with the specified {@code resId}, or
+     * {@code defaultValue} if it does not exist.
+     */
+    public static float getNamedFloat(@NonNull TypedArray a, @NonNull XmlPullParser parser,
+            @NonNull String attrName, @StyleableRes int resId, float defaultValue) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return defaultValue;
+        } else {
+            return a.getFloat(resId, defaultValue);
+        }
+    }
+
+    /**
+     * Retrieves a boolean attribute value. In addition to the styleable resource ID, we also make
+     * sure that the attribute name matches.
+     *
+     * @return a boolean value in the {@link TypedArray} with the specified {@code resId}, or
+     * {@code defaultValue} if it does not exist.
+     */
+    public static boolean getNamedBoolean(@NonNull TypedArray a, @NonNull XmlPullParser parser,
+            String attrName, @StyleableRes int resId, boolean defaultValue) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return defaultValue;
+        } else {
+            return a.getBoolean(resId, defaultValue);
+        }
+    }
+
+    /**
+     * Retrieves an int attribute value. In addition to the styleable resource ID, we also make
+     * sure that the attribute name matches.
+     *
+     * @return an int value in the {@link TypedArray} with the specified {@code resId}, or
+     * {@code defaultValue} if it does not exist.
+     */
+    public static int getNamedInt(@NonNull TypedArray a, @NonNull XmlPullParser parser,
+            String attrName, @StyleableRes int resId, int defaultValue) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return defaultValue;
+        } else {
+            return a.getInt(resId, defaultValue);
+        }
+    }
+
+    /**
+     * Retrieves a color attribute value. In addition to the styleable resource ID, we also make
+     * sure that the attribute name matches.
+     *
+     * @return a color value in the {@link TypedArray} with the specified {@code resId}, or
+     * {@code defaultValue} if it does not exist.
+     */
+    @ColorInt
+    public static int getNamedColor(@NonNull TypedArray a, @NonNull XmlPullParser parser,
+            String attrName, @StyleableRes int resId, @ColorInt int defaultValue) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return defaultValue;
+        } else {
+            return a.getColor(resId, defaultValue);
+        }
+    }
+
+    /**
+     * Retrieves a resource ID attribute value. In addition to the styleable resource ID, we also
+     * make sure that the attribute name matches.
+     *
+     * @return a resource ID value in the {@link TypedArray} with the specified {@code resId}, or
+     * {@code defaultValue} if it does not exist.
+     */
+    @AnyRes
+    public static int getNamedResourceId(@NonNull TypedArray a, @NonNull XmlPullParser parser,
+            String attrName, @StyleableRes int resId, @AnyRes int defaultValue) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return defaultValue;
+        } else {
+            return a.getResourceId(resId, defaultValue);
+        }
+    }
+
+    /**
+     * Retrieves a string attribute value. In addition to the styleable resource ID, we also
+     * make sure that the attribute name matches.
+     *
+     * @return a string value in the {@link TypedArray} with the specified {@code resId}, or
+     * null if it does not exist.
+     */
+    public static String getNamedString(@NonNull TypedArray a, @NonNull XmlPullParser parser,
+            String attrName, @StyleableRes int resId) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return null;
+        } else {
+            return a.getString(resId);
+        }
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>
+     * and return a temporary object holding its data.  This object is only
+     * valid until the next call on to {@link TypedArray}.
+     */
+    public static TypedValue peekNamedValue(TypedArray a, XmlPullParser parser, String attrName,
+            int resId) {
+        final boolean hasAttr = hasAttribute(parser, attrName);
+        if (!hasAttr) {
+            return null;
+        } else {
+            return a.peekValue(resId);
+        }
+    }
+
+    /**
+     * Obtains styled attributes from the theme, if available, or unstyled
+     * resources if the theme is null.
+     */
+    public static TypedArray obtainAttributes(
+            Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
+        if (theme == null) {
+            return res.obtainAttributes(set, attrs);
+        }
+        return theme.obtainStyledAttributes(set, attrs, 0, 0);
+    }
+
+    /**
+     * @return a boolean value of {@code index}. If it does not exist, a boolean value of
+     * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
+     */
+    public static boolean getBoolean(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex, boolean defaultValue) {
+        boolean val = a.getBoolean(fallbackIndex, defaultValue);
+        return a.getBoolean(index, val);
+    }
+
+    /**
+     * @return a drawable value of {@code index}. If it does not exist, a drawable value of
+     * {@code fallbackIndex}. If it still does not exist, {@code null}.
+     */
+    public static Drawable getDrawable(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex) {
+        Drawable val = a.getDrawable(index);
+        if (val == null) {
+            val = a.getDrawable(fallbackIndex);
+        }
+        return val;
+    }
+
+    /**
+     * @return an int value of {@code index}. If it does not exist, an int value of
+     * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
+     */
+    public static int getInt(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex, int defaultValue) {
+        int val = a.getInt(fallbackIndex, defaultValue);
+        return a.getInt(index, val);
+    }
+
+    /**
+     * @return a resource ID value of {@code index}. If it does not exist, a resource ID value of
+     * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
+     */
+    @AnyRes
+    public static int getResourceId(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex, @AnyRes int defaultValue) {
+        int val = a.getResourceId(fallbackIndex, defaultValue);
+        return a.getResourceId(index, val);
+    }
+
+    /**
+     * @return a string value of {@code index}. If it does not exist, a string value of
+     * {@code fallbackIndex}. If it still does not exist, {@code null}.
+     */
+    public static String getString(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex) {
+        String val = a.getString(index);
+        if (val == null) {
+            val = a.getString(fallbackIndex);
+        }
+        return val;
+    }
+
+    /**
+     * Retrieves a text attribute value with the specified fallback ID.
+     *
+     * @return a text value of {@code index}. If it does not exist, a text value of
+     * {@code fallbackIndex}. If it still does not exist, {@code null}.
+     */
+    public static CharSequence getText(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex) {
+        CharSequence val = a.getText(index);
+        if (val == null) {
+            val = a.getText(fallbackIndex);
+        }
+        return val;
+    }
+
+    /**
+     * Retrieves a string array attribute value with the specified fallback ID.
+     *
+     * @return a string array value of {@code index}. If it does not exist, a string array value
+     * of {@code fallbackIndex}. If it still does not exist, {@code null}.
+     */
+    public static CharSequence[] getTextArray(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex) {
+        CharSequence[] val = a.getTextArray(index);
+        if (val == null) {
+            val = a.getTextArray(fallbackIndex);
+        }
+        return val;
+    }
+
+    /**
+     * @return The resource ID value in the {@code context} specified by {@code attr}. If it does
+     * not exist, {@code fallbackAttr}.
+     */
+    public static int getAttr(Context context, int attr, int fallbackAttr) {
+        TypedValue value = new TypedValue();
+        context.getTheme().resolveAttribute(attr, value, true);
+        if (value.resourceId != 0) {
+            return attr;
+        }
+        return fallbackAttr;
+    }
+}
diff --git a/compat/java/android/support/v4/database/DatabaseUtilsCompat.java b/compat/java/android/support/v4/database/DatabaseUtilsCompat.java
index dd53ced..f3d3077 100644
--- a/compat/java/android/support/v4/database/DatabaseUtilsCompat.java
+++ b/compat/java/android/support/v4/database/DatabaseUtilsCompat.java
@@ -19,8 +19,7 @@
 import android.text.TextUtils;
 
 /**
- * Helper for accessing features in {@link android.database.DatabaseUtils}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.database.DatabaseUtils}.
  */
 public final class DatabaseUtilsCompat {
 
diff --git a/compat/java/android/support/v4/graphics/BitmapCompat.java b/compat/java/android/support/v4/graphics/BitmapCompat.java
index 599b737..20caf80 100644
--- a/compat/java/android/support/v4/graphics/BitmapCompat.java
+++ b/compat/java/android/support/v4/graphics/BitmapCompat.java
@@ -16,77 +16,58 @@
 package android.support.v4.graphics;
 
 import android.graphics.Bitmap;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 
 /**
- * Helper for accessing features in {@link android.graphics.Bitmap}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.graphics.Bitmap}.
  */
 public final class BitmapCompat {
-    /**
-     * Interface for the full API.
-     */
-    interface BitmapImpl {
-        public boolean hasMipMap(Bitmap bitmap);
-        public void setHasMipMap(Bitmap bitmap, boolean hasMipMap);
-        public int getAllocationByteCount(Bitmap bitmap);
-    }
-
-    static class BaseBitmapImpl implements BitmapImpl {
-        @Override
+    static class BitmapCompatBaseImpl {
         public boolean hasMipMap(Bitmap bitmap) {
             return false;
         }
 
-        @Override
         public void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
         }
 
-        @Override
         public int getAllocationByteCount(Bitmap bitmap) {
-            return bitmap.getRowBytes() * bitmap.getHeight();
+            return bitmap.getByteCount();
         }
     }
 
-    static class HcMr1BitmapCompatImpl extends BaseBitmapImpl {
-        @Override
-        public int getAllocationByteCount(Bitmap bitmap) {
-            return BitmapCompatHoneycombMr1.getAllocationByteCount(bitmap);
-        }
-    }
-
-    static class JbMr2BitmapCompatImpl extends HcMr1BitmapCompatImpl {
+    @RequiresApi(18)
+    static class BitmapCompatApi18Impl extends BitmapCompatBaseImpl {
         @Override
         public boolean hasMipMap(Bitmap bitmap){
-            return BitmapCompatJellybeanMR2.hasMipMap(bitmap);
+            return bitmap.hasMipMap();
         }
 
         @Override
         public void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
-            BitmapCompatJellybeanMR2.setHasMipMap(bitmap, hasMipMap);
+            bitmap.setHasMipMap(hasMipMap);
         }
     }
 
-    static class KitKatBitmapCompatImpl extends JbMr2BitmapCompatImpl {
+    @RequiresApi(19)
+    static class BitmapCompatApi19Impl extends BitmapCompatApi18Impl {
         @Override
         public int getAllocationByteCount(Bitmap bitmap) {
-            return BitmapCompatKitKat.getAllocationByteCount(bitmap);
+            return bitmap.getAllocationByteCount();
         }
     }
 
     /**
      * Select the correct implementation to use for the current platform.
      */
-    static final BitmapImpl IMPL;
+    static final BitmapCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            IMPL = new KitKatBitmapCompatImpl();
-        } else if (version >= 18) {
-            IMPL = new JbMr2BitmapCompatImpl();
-        } else if (version >= 12) {
-            IMPL = new HcMr1BitmapCompatImpl();
+        if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new BitmapCompatApi19Impl();
+        } else if (Build.VERSION.SDK_INT >= 18) {
+            IMPL = new BitmapCompatApi18Impl();
         } else {
-            IMPL = new BaseBitmapImpl();
+            IMPL = new BitmapCompatBaseImpl();
         }
     }
 
diff --git a/compat/java/android/support/v4/graphics/PaintCompat.java b/compat/java/android/support/v4/graphics/PaintCompat.java
index 66599f7..95eee0c 100644
--- a/compat/java/android/support/v4/graphics/PaintCompat.java
+++ b/compat/java/android/support/v4/graphics/PaintCompat.java
@@ -21,7 +21,7 @@
 import android.support.annotation.NonNull;
 
 /**
- * Helper for accessing features in {@link Paint} in a backwards compatible fashion.
+ * Helper for accessing features in {@link Paint}.
  */
 public final class PaintCompat {
 
@@ -35,9 +35,9 @@
      */
     public static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
         if (Build.VERSION.SDK_INT >= 23) {
-            return PaintCompatApi23.hasGlyph(paint, string);
+            return paint.hasGlyph(string);
         }
-        return PaintCompatGingerbread.hasGlyph(paint, string);
+        return PaintCompatApi14.hasGlyph(paint, string);
     }
 
     private PaintCompat() {}
diff --git a/compat/java/android/support/v4/graphics/PathParser.java b/compat/java/android/support/v4/graphics/PathParser.java
new file mode 100644
index 0000000..3ee85cc
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/PathParser.java
@@ -0,0 +1,760 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Path;
+import android.support.annotation.RestrictTo;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * This class is a duplicate from the PathParser.java of frameworks/base, with slight
+ * update on incompatible API like copyOfRange().
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class PathParser {
+    private static final String LOGTAG = "PathParser";
+
+    // Copy from Arrays.copyOfRange() which is only available from API level 9.
+
+    /**
+     * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
+     * end (exclusive). The original order of elements is preserved.
+     * If {@code end} is greater than {@code original.length}, the result is padded
+     * with the value {@code 0.0f}.
+     *
+     * @param original the original array
+     * @param start    the start index, inclusive
+     * @param end      the end index, exclusive
+     * @return the new array
+     * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
+     * @throws IllegalArgumentException       if {@code start > end}
+     * @throws NullPointerException           if {@code original == null}
+     */
+    static float[] copyOfRange(float[] original, int start, int end) {
+        if (start > end) {
+            throw new IllegalArgumentException();
+        }
+        int originalLength = original.length;
+        if (start < 0 || start > originalLength) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        int resultLength = end - start;
+        int copyLength = Math.min(resultLength, originalLength - start);
+        float[] result = new float[resultLength];
+        System.arraycopy(original, start, result, 0, copyLength);
+        return result;
+    }
+
+    /**
+     * @param pathData The string representing a path, the same as "d" string in svg file.
+     * @return the generated Path object.
+     */
+    public static Path createPathFromPathData(String pathData) {
+        Path path = new Path();
+        PathDataNode[] nodes = createNodesFromPathData(pathData);
+        if (nodes != null) {
+            try {
+                PathDataNode.nodesToPath(nodes, path);
+            } catch (RuntimeException e) {
+                throw new RuntimeException("Error in parsing " + pathData, e);
+            }
+            return path;
+        }
+        return null;
+    }
+
+    /**
+     * @param pathData The string representing a path, the same as "d" string in svg file.
+     * @return an array of the PathDataNode.
+     */
+    public static PathDataNode[] createNodesFromPathData(String pathData) {
+        if (pathData == null) {
+            return null;
+        }
+        int start = 0;
+        int end = 1;
+
+        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
+        while (end < pathData.length()) {
+            end = nextStart(pathData, end);
+            String s = pathData.substring(start, end).trim();
+            if (s.length() > 0) {
+                float[] val = getFloats(s);
+                addNode(list, s.charAt(0), val);
+            }
+
+            start = end;
+            end++;
+        }
+        if ((end - start) == 1 && start < pathData.length()) {
+            addNode(list, pathData.charAt(start), new float[0]);
+        }
+        return list.toArray(new PathDataNode[list.size()]);
+    }
+
+    /**
+     * @param source The array of PathDataNode to be duplicated.
+     * @return a deep copy of the <code>source</code>.
+     */
+    public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
+        if (source == null) {
+            return null;
+        }
+        PathDataNode[] copy = new PathParser.PathDataNode[source.length];
+        for (int i = 0; i < source.length; i++) {
+            copy[i] = new PathDataNode(source[i]);
+        }
+        return copy;
+    }
+
+    /**
+     * @param nodesFrom The source path represented in an array of PathDataNode
+     * @param nodesTo   The target path represented in an array of PathDataNode
+     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
+     */
+    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
+        if (nodesFrom == null || nodesTo == null) {
+            return false;
+        }
+
+        if (nodesFrom.length != nodesTo.length) {
+            return false;
+        }
+
+        for (int i = 0; i < nodesFrom.length; i++) {
+            if (nodesFrom[i].mType != nodesTo[i].mType
+                    || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Update the target's data to match the source.
+     * Before calling this, make sure canMorph(target, source) is true.
+     *
+     * @param target The target path represented in an array of PathDataNode
+     * @param source The source path represented in an array of PathDataNode
+     */
+    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
+        for (int i = 0; i < source.length; i++) {
+            target[i].mType = source[i].mType;
+            for (int j = 0; j < source[i].mParams.length; j++) {
+                target[i].mParams[j] = source[i].mParams[j];
+            }
+        }
+    }
+
+    private static int nextStart(String s, int end) {
+        char c;
+
+        while (end < s.length()) {
+            c = s.charAt(end);
+            // Note that 'e' or 'E' are not valid path commands, but could be
+            // used for floating point numbers' scientific notation.
+            // Therefore, when searching for next command, we should ignore 'e'
+            // and 'E'.
+            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
+                    && c != 'e' && c != 'E') {
+                return end;
+            }
+            end++;
+        }
+        return end;
+    }
+
+    private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
+        list.add(new PathDataNode(cmd, val));
+    }
+
+    private static class ExtractFloatResult {
+        // We need to return the position of the next separator and whether the
+        // next float starts with a '-' or a '.'.
+        int mEndPosition;
+        boolean mEndWithNegOrDot;
+
+        ExtractFloatResult() {
+        }
+    }
+
+    /**
+     * Parse the floats in the string.
+     * This is an optimized version of parseFloat(s.split(",|\\s"));
+     *
+     * @param s the string containing a command and list of floats
+     * @return array of floats
+     */
+    private static float[] getFloats(String s) {
+        if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
+            return new float[0];
+        }
+        try {
+            float[] results = new float[s.length()];
+            int count = 0;
+            int startPosition = 1;
+            int endPosition = 0;
+
+            ExtractFloatResult result = new ExtractFloatResult();
+            int totalLength = s.length();
+
+            // The startPosition should always be the first character of the
+            // current number, and endPosition is the character after the current
+            // number.
+            while (startPosition < totalLength) {
+                extract(s, startPosition, result);
+                endPosition = result.mEndPosition;
+
+                if (startPosition < endPosition) {
+                    results[count++] = Float.parseFloat(
+                            s.substring(startPosition, endPosition));
+                }
+
+                if (result.mEndWithNegOrDot) {
+                    // Keep the '-' or '.' sign with next number.
+                    startPosition = endPosition;
+                } else {
+                    startPosition = endPosition + 1;
+                }
+            }
+            return copyOfRange(results, 0, count);
+        } catch (NumberFormatException e) {
+            throw new RuntimeException("error in parsing \"" + s + "\"", e);
+        }
+    }
+
+    /**
+     * Calculate the position of the next comma or space or negative sign
+     *
+     * @param s      the string to search
+     * @param start  the position to start searching
+     * @param result the result of the extraction, including the position of the
+     *               the starting position of next number, whether it is ending with a '-'.
+     */
+    private static void extract(String s, int start, ExtractFloatResult result) {
+        // Now looking for ' ', ',', '.' or '-' from the start.
+        int currentIndex = start;
+        boolean foundSeparator = false;
+        result.mEndWithNegOrDot = false;
+        boolean secondDot = false;
+        boolean isExponential = false;
+        for (; currentIndex < s.length(); currentIndex++) {
+            boolean isPrevExponential = isExponential;
+            isExponential = false;
+            char currentChar = s.charAt(currentIndex);
+            switch (currentChar) {
+                case ' ':
+                case ',':
+                    foundSeparator = true;
+                    break;
+                case '-':
+                    // The negative sign following a 'e' or 'E' is not a separator.
+                    if (currentIndex != start && !isPrevExponential) {
+                        foundSeparator = true;
+                        result.mEndWithNegOrDot = true;
+                    }
+                    break;
+                case '.':
+                    if (!secondDot) {
+                        secondDot = true;
+                    } else {
+                        // This is the second dot, and it is considered as a separator.
+                        foundSeparator = true;
+                        result.mEndWithNegOrDot = true;
+                    }
+                    break;
+                case 'e':
+                case 'E':
+                    isExponential = true;
+                    break;
+            }
+            if (foundSeparator) {
+                break;
+            }
+        }
+        // When there is nothing found, then we put the end position to the end
+        // of the string.
+        result.mEndPosition = currentIndex;
+    }
+
+    /**
+     * Each PathDataNode represents one command in the "d" attribute of the svg
+     * file.
+     * An array of PathDataNode can represent the whole "d" attribute.
+     */
+    public static class PathDataNode {
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public char mType;
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public float[] mParams;
+
+        PathDataNode(char type, float[] params) {
+            this.mType = type;
+            this.mParams = params;
+        }
+
+        PathDataNode(PathDataNode n) {
+            mType = n.mType;
+            mParams = copyOfRange(n.mParams, 0, n.mParams.length);
+        }
+
+        /**
+         * Convert an array of PathDataNode to Path.
+         *
+         * @param node The source array of PathDataNode.
+         * @param path The target Path object.
+         */
+        public static void nodesToPath(PathDataNode[] node, Path path) {
+            float[] current = new float[6];
+            char previousCommand = 'm';
+            for (int i = 0; i < node.length; i++) {
+                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
+                previousCommand = node[i].mType;
+            }
+        }
+
+        /**
+         * The current PathDataNode will be interpolated between the
+         * <code>nodeFrom</code> and <code>nodeTo</code> according to the
+         * <code>fraction</code>.
+         *
+         * @param nodeFrom The start value as a PathDataNode.
+         * @param nodeTo   The end value as a PathDataNode
+         * @param fraction The fraction to interpolate.
+         */
+        public void interpolatePathDataNode(PathDataNode nodeFrom,
+                PathDataNode nodeTo, float fraction) {
+            for (int i = 0; i < nodeFrom.mParams.length; i++) {
+                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
+                        + nodeTo.mParams[i] * fraction;
+            }
+        }
+
+        private static void addCommand(Path path, float[] current,
+                char previousCmd, char cmd, float[] val) {
+
+            int incr = 2;
+            float currentX = current[0];
+            float currentY = current[1];
+            float ctrlPointX = current[2];
+            float ctrlPointY = current[3];
+            float currentSegmentStartX = current[4];
+            float currentSegmentStartY = current[5];
+            float reflectiveCtrlPointX;
+            float reflectiveCtrlPointY;
+
+            switch (cmd) {
+                case 'z':
+                case 'Z':
+                    path.close();
+                    // Path is closed here, but we need to move the pen to the
+                    // closed position. So we cache the segment's starting position,
+                    // and restore it here.
+                    currentX = currentSegmentStartX;
+                    currentY = currentSegmentStartY;
+                    ctrlPointX = currentSegmentStartX;
+                    ctrlPointY = currentSegmentStartY;
+                    path.moveTo(currentX, currentY);
+                    break;
+                case 'm':
+                case 'M':
+                case 'l':
+                case 'L':
+                case 't':
+                case 'T':
+                    incr = 2;
+                    break;
+                case 'h':
+                case 'H':
+                case 'v':
+                case 'V':
+                    incr = 1;
+                    break;
+                case 'c':
+                case 'C':
+                    incr = 6;
+                    break;
+                case 's':
+                case 'S':
+                case 'q':
+                case 'Q':
+                    incr = 4;
+                    break;
+                case 'a':
+                case 'A':
+                    incr = 7;
+                    break;
+            }
+
+            for (int k = 0; k < val.length; k += incr) {
+                switch (cmd) {
+                    case 'm': // moveto - Start a new sub-path (relative)
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        if (k > 0) {
+                            // According to the spec, if a moveto is followed by multiple
+                            // pairs of coordinates, the subsequent pairs are treated as
+                            // implicit lineto commands.
+                            path.rLineTo(val[k + 0], val[k + 1]);
+                        } else {
+                            path.rMoveTo(val[k + 0], val[k + 1]);
+                            currentSegmentStartX = currentX;
+                            currentSegmentStartY = currentY;
+                        }
+                        break;
+                    case 'M': // moveto - Start a new sub-path
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        if (k > 0) {
+                            // According to the spec, if a moveto is followed by multiple
+                            // pairs of coordinates, the subsequent pairs are treated as
+                            // implicit lineto commands.
+                            path.lineTo(val[k + 0], val[k + 1]);
+                        } else {
+                            path.moveTo(val[k + 0], val[k + 1]);
+                            currentSegmentStartX = currentX;
+                            currentSegmentStartY = currentY;
+                        }
+                        break;
+                    case 'l': // lineto - Draw a line from the current point (relative)
+                        path.rLineTo(val[k + 0], val[k + 1]);
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'L': // lineto - Draw a line from the current point
+                        path.lineTo(val[k + 0], val[k + 1]);
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
+                        path.rLineTo(val[k + 0], 0);
+                        currentX += val[k + 0];
+                        break;
+                    case 'H': // horizontal lineto - Draws a horizontal line
+                        path.lineTo(val[k + 0], currentY);
+                        currentX = val[k + 0];
+                        break;
+                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
+                        path.rLineTo(0, val[k + 0]);
+                        currentY += val[k + 0];
+                        break;
+                    case 'V': // vertical lineto - Draws a vertical line from the current point
+                        path.lineTo(currentX, val[k + 0]);
+                        currentY = val[k + 0];
+                        break;
+                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
+                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                                val[k + 4], val[k + 5]);
+
+                        ctrlPointX = currentX + val[k + 2];
+                        ctrlPointY = currentY + val[k + 3];
+                        currentX += val[k + 4];
+                        currentY += val[k + 5];
+
+                        break;
+                    case 'C': // curveto - Draws a cubic Bézier curve
+                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                                val[k + 4], val[k + 5]);
+                        currentX = val[k + 4];
+                        currentY = val[k + 5];
+                        ctrlPointX = val[k + 2];
+                        ctrlPointY = val[k + 3];
+                        break;
+                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
+                        reflectiveCtrlPointX = 0;
+                        reflectiveCtrlPointY = 0;
+                        if (previousCmd == 'c' || previousCmd == 's'
+                                || previousCmd == 'C' || previousCmd == 'S') {
+                            reflectiveCtrlPointX = currentX - ctrlPointX;
+                            reflectiveCtrlPointY = currentY - ctrlPointY;
+                        }
+                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1],
+                                val[k + 2], val[k + 3]);
+
+                        ctrlPointX = currentX + val[k + 0];
+                        ctrlPointY = currentY + val[k + 1];
+                        currentX += val[k + 2];
+                        currentY += val[k + 3];
+                        break;
+                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
+                        reflectiveCtrlPointX = currentX;
+                        reflectiveCtrlPointY = currentY;
+                        if (previousCmd == 'c' || previousCmd == 's'
+                                || previousCmd == 'C' || previousCmd == 'S') {
+                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+                        }
+                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = val[k + 0];
+                        ctrlPointY = val[k + 1];
+                        currentX = val[k + 2];
+                        currentY = val[k + 3];
+                        break;
+                    case 'q': // Draws a quadratic Bézier (relative)
+                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = currentX + val[k + 0];
+                        ctrlPointY = currentY + val[k + 1];
+                        currentX += val[k + 2];
+                        currentY += val[k + 3];
+                        break;
+                    case 'Q': // Draws a quadratic Bézier
+                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = val[k + 0];
+                        ctrlPointY = val[k + 1];
+                        currentX = val[k + 2];
+                        currentY = val[k + 3];
+                        break;
+                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
+                        reflectiveCtrlPointX = 0;
+                        reflectiveCtrlPointY = 0;
+                        if (previousCmd == 'q' || previousCmd == 't'
+                                || previousCmd == 'Q' || previousCmd == 'T') {
+                            reflectiveCtrlPointX = currentX - ctrlPointX;
+                            reflectiveCtrlPointY = currentY - ctrlPointY;
+                        }
+                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1]);
+                        ctrlPointX = currentX + reflectiveCtrlPointX;
+                        ctrlPointY = currentY + reflectiveCtrlPointY;
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
+                        reflectiveCtrlPointX = currentX;
+                        reflectiveCtrlPointY = currentY;
+                        if (previousCmd == 'q' || previousCmd == 't'
+                                || previousCmd == 'Q' || previousCmd == 'T') {
+                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+                        }
+                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1]);
+                        ctrlPointX = reflectiveCtrlPointX;
+                        ctrlPointY = reflectiveCtrlPointY;
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'a': // Draws an elliptical arc
+                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+                        drawArc(path,
+                                currentX,
+                                currentY,
+                                val[k + 5] + currentX,
+                                val[k + 6] + currentY,
+                                val[k + 0],
+                                val[k + 1],
+                                val[k + 2],
+                                val[k + 3] != 0,
+                                val[k + 4] != 0);
+                        currentX += val[k + 5];
+                        currentY += val[k + 6];
+                        ctrlPointX = currentX;
+                        ctrlPointY = currentY;
+                        break;
+                    case 'A': // Draws an elliptical arc
+                        drawArc(path,
+                                currentX,
+                                currentY,
+                                val[k + 5],
+                                val[k + 6],
+                                val[k + 0],
+                                val[k + 1],
+                                val[k + 2],
+                                val[k + 3] != 0,
+                                val[k + 4] != 0);
+                        currentX = val[k + 5];
+                        currentY = val[k + 6];
+                        ctrlPointX = currentX;
+                        ctrlPointY = currentY;
+                        break;
+                }
+                previousCmd = cmd;
+            }
+            current[0] = currentX;
+            current[1] = currentY;
+            current[2] = ctrlPointX;
+            current[3] = ctrlPointY;
+            current[4] = currentSegmentStartX;
+            current[5] = currentSegmentStartY;
+        }
+
+        private static void drawArc(Path p,
+                float x0,
+                float y0,
+                float x1,
+                float y1,
+                float a,
+                float b,
+                float theta,
+                boolean isMoreThanHalf,
+                boolean isPositiveArc) {
+
+            /* Convert rotation angle from degrees to radians */
+            double thetaD = Math.toRadians(theta);
+            /* Pre-compute rotation matrix entries */
+            double cosTheta = Math.cos(thetaD);
+            double sinTheta = Math.sin(thetaD);
+            /* Transform (x0, y0) and (x1, y1) into unit space */
+            /* using (inverse) rotation, followed by (inverse) scale */
+            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+
+            /* Compute differences and averages */
+            double dx = x0p - x1p;
+            double dy = y0p - y1p;
+            double xm = (x0p + x1p) / 2;
+            double ym = (y0p + y1p) / 2;
+            /* Solve for intersecting unit circles */
+            double dsq = dx * dx + dy * dy;
+            if (dsq == 0.0) {
+                Log.w(LOGTAG, " Points are coincident");
+                return; /* Points are coincident */
+            }
+            double disc = 1.0 / dsq - 1.0 / 4.0;
+            if (disc < 0.0) {
+                Log.w(LOGTAG, "Points are too far apart " + dsq);
+                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
+                drawArc(p, x0, y0, x1, y1, a * adjust,
+                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
+                return; /* Points are too far apart */
+            }
+            double s = Math.sqrt(disc);
+            double sdx = s * dx;
+            double sdy = s * dy;
+            double cx;
+            double cy;
+            if (isMoreThanHalf == isPositiveArc) {
+                cx = xm - sdy;
+                cy = ym + sdx;
+            } else {
+                cx = xm + sdy;
+                cy = ym - sdx;
+            }
+
+            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
+
+            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
+
+            double sweep = (eta1 - eta0);
+            if (isPositiveArc != (sweep >= 0)) {
+                if (sweep > 0) {
+                    sweep -= 2 * Math.PI;
+                } else {
+                    sweep += 2 * Math.PI;
+                }
+            }
+
+            cx *= a;
+            cy *= b;
+            double tcx = cx;
+            cx = cx * cosTheta - cy * sinTheta;
+            cy = tcx * sinTheta + cy * cosTheta;
+
+            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+        }
+
+        /**
+         * Converts an arc to cubic Bezier segments and records them in p.
+         *
+         * @param p     The target for the cubic Bezier segments
+         * @param cx    The x coordinate center of the ellipse
+         * @param cy    The y coordinate center of the ellipse
+         * @param a     The radius of the ellipse in the horizontal direction
+         * @param b     The radius of the ellipse in the vertical direction
+         * @param e1x   E(eta1) x coordinate of the starting point of the arc
+         * @param e1y   E(eta2) y coordinate of the starting point of the arc
+         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+         * @param start The start angle of the arc on the ellipse
+         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+         */
+        private static void arcToBezier(Path p,
+                double cx,
+                double cy,
+                double a,
+                double b,
+                double e1x,
+                double e1y,
+                double theta,
+                double start,
+                double sweep) {
+            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+            // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+            // Maximum of 45 degrees per cubic Bezier segment
+            int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
+
+            double eta1 = start;
+            double cosTheta = Math.cos(theta);
+            double sinTheta = Math.sin(theta);
+            double cosEta1 = Math.cos(eta1);
+            double sinEta1 = Math.sin(eta1);
+            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+            double anglePerSegment = sweep / numSegments;
+            for (int i = 0; i < numSegments; i++) {
+                double eta2 = eta1 + anglePerSegment;
+                double sinEta2 = Math.sin(eta2);
+                double cosEta2 = Math.cos(eta2);
+                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
+                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
+                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
+                double alpha =
+                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+                double q1x = e1x + alpha * ep1x;
+                double q1y = e1y + alpha * ep1y;
+                double q2x = e2x - alpha * ep2x;
+                double q2y = e2y - alpha * ep2y;
+
+                // Adding this no-op call to workaround a proguard related issue.
+                p.rLineTo(0, 0);
+
+                p.cubicTo((float) q1x,
+                        (float) q1y,
+                        (float) q2x,
+                        (float) q2y,
+                        (float) e2x,
+                        (float) e2y);
+                eta1 = eta2;
+                e1x = e2x;
+                e1y = e2y;
+                ep1x = ep2x;
+                ep1y = ep2y;
+            }
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompat.java b/compat/java/android/support/v4/graphics/TypefaceCompat.java
new file mode 100644
index 0000000..6d114b6
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompat.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry;
+import android.support.v4.provider.FontsContractCompat;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.support.v4.util.LruCache;
+import android.widget.TextView;
+
+/**
+ * Helper for accessing features in {@link Typeface}.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class TypefaceCompat {
+    private static final String TAG = "TypefaceCompat";
+
+    private static final TypefaceCompatImpl sTypefaceCompatImpl;
+    static {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            sTypefaceCompatImpl = new TypefaceCompatApi26Impl();
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+                && TypefaceCompatApi24Impl.isUsable()) {
+            sTypefaceCompatImpl = new TypefaceCompatApi24Impl();
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            sTypefaceCompatImpl = new TypefaceCompatApi21Impl();
+        } else {
+            sTypefaceCompatImpl = new TypefaceCompatBaseImpl();
+        }
+    }
+
+    /**
+     * Cache for Typeface objects dynamically loaded from assets.
+     */
+    private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
+
+    interface TypefaceCompatImpl {
+        // Create Typeface from XML which root node is "font-family"
+        Typeface createFromFontFamilyFilesResourceEntry(
+                Context context, FontFamilyFilesResourceEntry entry, Resources resources,
+                int style);
+
+        Typeface createFromFontInfo(Context context,
+                @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts,
+                int style);
+
+        Typeface createFromResourcesFontFile(
+                Context context, Resources resources, int id, String path, int style);
+    }
+
+    private TypefaceCompat() {}
+
+    /**
+     * Find from internal cache.
+     *
+     * @return null if not found.
+     */
+    public static Typeface findFromCache(Resources resources, int id, int style) {
+        return sTypefaceCache.get(createResourceUid(resources, id, style));
+    }
+
+    /**
+     * Create a unique id for a given Resource and id.
+     *
+     * @param resources Resources instance
+     * @param id a resource id
+     * @param style style to be used for this resource, -1 if not available.
+     * @return Unique id for a given resource and id.
+     */
+    private static String createResourceUid(final Resources resources, int id, int style) {
+        return resources.getResourcePackageName(id) + "-" + id + "-" + style;
+    }
+
+    /**
+     * Create Typeface from XML resource which root node is font-family.
+     *
+     * @return null if failed to create.
+     */
+    public static Typeface createFromResourcesFamilyXml(
+            Context context, FamilyResourceEntry entry, Resources resources, int id, int style,
+            @Nullable TextView targetView) {
+        Typeface typeface;
+        if (entry instanceof ProviderResourceEntry) {
+            ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
+            typeface = FontsContractCompat.getFontSync(context,
+                    providerEntry.getRequest(), targetView, providerEntry.getFetchStrategy(),
+                    providerEntry.getTimeout(), style);
+        } else {
+            typeface = sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
+                    context, (FontFamilyFilesResourceEntry) entry, resources, style);
+        }
+        if (typeface != null) {
+            sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
+        }
+        return typeface;
+    }
+
+    /**
+     * Used by Resources to load a font resource of type font file.
+     */
+    @Nullable
+    public static Typeface createFromResourcesFontFile(
+            Context context, Resources resources, int id, String path, int style) {
+        Typeface typeface = sTypefaceCompatImpl.createFromResourcesFontFile(
+                context, resources, id, path, style);
+        if (typeface != null) {
+            sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
+        }
+        return typeface;
+    }
+
+    /**
+     * Create a Typeface from a given FontInfo list and a map that matches them to ByteBuffers.
+     */
+    public static Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
+        return sTypefaceCompatImpl.createFromFontInfo(context, cancellationSignal, fonts, style);
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatApi21Impl.java b/compat/java/android/support/v4/graphics/TypefaceCompatApi21Impl.java
new file mode 100644
index 0000000..a742004
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatApi21Impl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+
+/**
+ * Implementation of the Typeface compat methods for API 21 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(21)
+class TypefaceCompatApi21Impl extends TypefaceCompatBaseImpl {
+    private static final String TAG = "TypefaceCompatApi21Impl";
+
+    private File getFile(ParcelFileDescriptor fd) {
+        try {
+            final String path = Os.readlink("/proc/self/fd/" + fd.getFd());
+            // Check if the symbolic link points the regular file.
+            if (OsConstants.S_ISREG(Os.stat(path).st_mode)) {
+                return new File(path);
+            } else {
+                return null;
+            }
+        } catch (ErrnoException e) {
+            return null;  // Mostly permission error.
+        }
+    }
+
+    @Override
+    public Typeface createFromFontInfo(Context context, CancellationSignal cancellationSignal,
+            @NonNull FontInfo[] fonts, int style) {
+        if (fonts.length < 1) {
+            return null;
+        }
+        final FontInfo bestFont = findBestInfo(fonts, style);
+        final ContentResolver resolver = context.getContentResolver();
+        try (ParcelFileDescriptor pfd =
+                     resolver.openFileDescriptor(bestFont.getUri(), "r", cancellationSignal)) {
+            final File file = getFile(pfd);
+            if (file == null || !file.canRead()) {
+                // Unable to use the real file for creating Typeface. Fallback to copying
+                // implementation.
+                try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
+                    return super.createFromInputStream(context, fis);
+                }
+            }
+            return Typeface.createFromFile(file);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatApi24Impl.java b/compat/java/android/support/v4/graphics/TypefaceCompatApi24Impl.java
new file mode 100644
index 0000000..c64e0c3
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatApi24Impl.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.support.v4.util.SimpleArrayMap;
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+
+/**
+ * Implementation of the Typeface compat methods for API 24 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(24)
+class TypefaceCompatApi24Impl extends TypefaceCompatBaseImpl {
+    private static final String TAG = "TypefaceCompatApi24Impl";
+
+    private static final String FONT_FAMILY_CLASS = "android.graphics.FontFamily";
+    private static final String ADD_FONT_WEIGHT_STYLE_METHOD = "addFontWeightStyle";
+    private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
+            "createFromFamiliesWithDefault";
+    private static final Class sFontFamily;
+    private static final Constructor sFontFamilyCtor;
+    private static final Method sAddFontWeightStyle;
+    private static final Method sCreateFromFamiliesWithDefault;
+
+    static {
+        Class fontFamilyClass;
+        Constructor fontFamilyCtor;
+        Method addFontMethod;
+        Method createFromFamiliesWithDefaultMethod;
+        try {
+            fontFamilyClass = Class.forName(FONT_FAMILY_CLASS);
+            fontFamilyCtor = fontFamilyClass.getConstructor();
+            addFontMethod = fontFamilyClass.getMethod(ADD_FONT_WEIGHT_STYLE_METHOD,
+                    ByteBuffer.class, Integer.TYPE, List.class, Integer.TYPE, Boolean.TYPE);
+            Object familyArray = Array.newInstance(fontFamilyClass, 1);
+            createFromFamiliesWithDefaultMethod =
+                    Typeface.class.getMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+                          familyArray.getClass());
+        } catch (ClassNotFoundException | NoSuchMethodException e) {
+            Log.e(TAG, e.getClass().getName(), e);
+            fontFamilyClass = null;
+            fontFamilyCtor = null;
+            addFontMethod = null;
+            createFromFamiliesWithDefaultMethod = null;
+        }
+        sFontFamilyCtor = fontFamilyCtor;
+        sFontFamily = fontFamilyClass;
+        sAddFontWeightStyle = addFontMethod;
+        sCreateFromFamiliesWithDefault = createFromFamiliesWithDefaultMethod;
+    }
+
+    /**
+     * Returns true if API24 implementation is usable.
+     */
+    public static boolean isUsable() {
+        if (sAddFontWeightStyle == null) {
+            Log.w(TAG, "Unable to collect necessary private methods."
+                    + "Fallback to legacy implementation.");
+        }
+        return sAddFontWeightStyle != null;
+    }
+
+    private static Object newFamily() {
+        try {
+            return sFontFamilyCtor.newInstance();
+        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static boolean addFontWeightStyle(Object family, ByteBuffer buffer, int ttcIndex,
+            int weight, boolean style) {
+        try {
+            final Boolean result = (Boolean) sAddFontWeightStyle.invoke(
+                    family, buffer, ttcIndex, null /* variation axis */, weight, style);
+            return result.booleanValue();
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Typeface createFromFamiliesWithDefault(Object family) {
+        try {
+            Object familyArray = Array.newInstance(sFontFamily, 1);
+            Array.set(familyArray, 0, family);
+            return (Typeface) sCreateFromFamiliesWithDefault.invoke(
+                    null /* static method */, familyArray);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
+        Object family = newFamily();
+        SimpleArrayMap<Uri, ByteBuffer> bufferCache = new SimpleArrayMap<>();
+
+        for (final FontInfo font : fonts) {
+            final Uri uri = font.getUri();
+            ByteBuffer buffer = bufferCache.get(uri);
+            if (buffer == null) {
+                buffer = TypefaceCompatUtil.mmap(context, cancellationSignal, uri);
+                bufferCache.put(uri, buffer);
+            }
+            if (!addFontWeightStyle(family, buffer, font.getTtcIndex(), font.getWeight(),
+                    font.isItalic())) {
+                return null;
+            }
+        }
+        return createFromFamiliesWithDefault(family);
+    }
+
+    @Override
+    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
+            FontFamilyFilesResourceEntry entry, Resources resources, int style) {
+        Object family = newFamily();
+        for (final FontFileResourceEntry e : entry.getEntries()) {
+            final ByteBuffer buffer =
+                    TypefaceCompatUtil.copyToDirectBuffer(context, resources, e.getResourceId());
+            // TODO: support ttc index.
+            if (!addFontWeightStyle(family, buffer, 0, e.getWeight(), e.isItalic())) {
+                return null;
+            }
+        }
+        return createFromFamiliesWithDefault(family);
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java b/compat/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
new file mode 100644
index 0000000..b217c00
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
@@ -0,0 +1,301 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.graphics.fonts.FontVariationAxis;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.FontResourcesParserCompat;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
+import android.support.v4.provider.FontsContractCompat;
+import android.util.Log;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * Implementation of the Typeface compat methods for API 26 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(26)
+public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
+    private static final String TAG = "TypefaceCompatApi26Impl";
+
+    private static final String FONT_FAMILY_CLASS = "android.graphics.FontFamily";
+    private static final String ADD_FONT_FROM_ASSET_MANAGER_METHOD = "addFontFromAssetManager";
+    private static final String ADD_FONT_FROM_BUFFER_METHOD = "addFontFromBuffer";
+    private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
+            "createFromFamiliesWithDefault";
+    private static final String FREEZE_METHOD = "freeze";
+    private static final String ABORT_CREATION_METHOD = "abortCreation";
+    private static final Class sFontFamily;
+    private static final Constructor sFontFamilyCtor;
+    private static final Method sAddFontFromAssetManager;
+    private static final Method sAddFontFromBuffer;
+    private static final Method sFreeze;
+    private static final Method sAbortCreation;
+    private static final Method sCreateFromFamiliesWithDefault;
+    private static final int RESOLVE_BY_FONT_TABLE = -1;
+
+    static {
+        Class fontFamilyClass;
+        Constructor fontFamilyCtor;
+        Method addFontMethod;
+        Method addFromBufferMethod;
+        Method freezeMethod;
+        Method abortCreationMethod;
+        Method createFromFamiliesWithDefaultMethod;
+        try {
+            fontFamilyClass = Class.forName(FONT_FAMILY_CLASS);
+            fontFamilyCtor = fontFamilyClass.getConstructor();
+            addFontMethod = fontFamilyClass.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
+                    AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
+                    Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
+            addFromBufferMethod = fontFamilyClass.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
+                    ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
+                    Integer.TYPE);
+            freezeMethod = fontFamilyClass.getMethod(FREEZE_METHOD);
+            abortCreationMethod = fontFamilyClass.getMethod(ABORT_CREATION_METHOD);
+            Object familyArray = Array.newInstance(fontFamilyClass, 1);
+            createFromFamiliesWithDefaultMethod =
+                    Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+                            familyArray.getClass(), Integer.TYPE, Integer.TYPE);
+            createFromFamiliesWithDefaultMethod.setAccessible(true);
+        } catch (ClassNotFoundException | NoSuchMethodException e) {
+            Log.e(TAG, "Unable to collect necessary methods for class " + e.getClass().getName(),
+                    e);
+            fontFamilyClass = null;
+            fontFamilyCtor = null;
+            addFontMethod = null;
+            addFromBufferMethod = null;
+            freezeMethod = null;
+            abortCreationMethod = null;
+            createFromFamiliesWithDefaultMethod = null;
+        }
+        sFontFamilyCtor = fontFamilyCtor;
+        sFontFamily = fontFamilyClass;
+        sAddFontFromAssetManager = addFontMethod;
+        sAddFontFromBuffer = addFromBufferMethod;
+        sFreeze = freezeMethod;
+        sAbortCreation = abortCreationMethod;
+        sCreateFromFamiliesWithDefault = createFromFamiliesWithDefaultMethod;
+    }
+
+    /**
+     * Returns true if API26 implementation is usable.
+     */
+    private static boolean isFontFamilyPrivateAPIAvailable() {
+        if (sAddFontFromAssetManager == null) {
+            Log.w(TAG, "Unable to collect necessary private methods."
+                    + "Fallback to legacy implementation.");
+        }
+        return sAddFontFromAssetManager != null;
+    }
+
+    /**
+     * Create a new FontFamily instance
+     */
+    private static Object newFamily() {
+        try {
+            return sFontFamilyCtor.newInstance();
+        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Call FontFamily#addFontFromAssetManager(AssetManager mgr, String path, int cookie,
+     *      boolean isAsset, int ttcIndex, int weight, int isItalic, FontVariationAxis[] axes)
+     */
+    private static boolean addFontFromAssetManager(Context context, Object family, String fileName,
+            int ttcIndex, int weight, int style) {
+        try {
+            final Boolean result = (Boolean) sAddFontFromAssetManager.invoke(family,
+                    context.getAssets(), fileName, 0 /* cookie */, false /* isAsset */, ttcIndex,
+                    weight, style, null /* axes */);
+            return result.booleanValue();
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Call FontFamily#addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
+     *      int weight, int italic)
+     */
+    private static boolean addFontFromBuffer(Object family, ByteBuffer buffer,
+            int ttcIndex, int weight, int style) {
+        try {
+            final Boolean result = (Boolean) sAddFontFromBuffer.invoke(family,
+                    buffer, ttcIndex, null /* axes */, weight, style);
+            return result.booleanValue();
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Call static method Typeface#createFromFamiliesWithDefault(
+     *      FontFamily[] families, int weight, int italic)
+     */
+    private static Typeface createFromFamiliesWithDefault(Object family) {
+        try {
+            Object familyArray = Array.newInstance(sFontFamily, 1);
+            Array.set(familyArray, 0, family);
+            return (Typeface) sCreateFromFamiliesWithDefault.invoke(null /* static method */,
+                    familyArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Call FontFamily#freeze()
+     */
+    private static boolean freeze(Object family) {
+        try {
+            Boolean result = (Boolean) sFreeze.invoke(family);
+            return result.booleanValue();
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Call FontFamily#abortCreation()
+     */
+    private static boolean abortCreation(Object family) {
+        try {
+            Boolean result = (Boolean) sAbortCreation.invoke(family);
+            return result.booleanValue();
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
+            FontResourcesParserCompat.FontFamilyFilesResourceEntry entry, Resources resources,
+            int style) {
+        if (!isFontFamilyPrivateAPIAvailable()) {
+            return super.createFromFontFamilyFilesResourceEntry(context, entry, resources, style);
+        }
+        Object fontFamily = newFamily();
+        for (final FontFileResourceEntry fontFile : entry.getEntries()) {
+            // TODO: Add ttc and variation font support. (b/37853920)
+            if (!addFontFromAssetManager(context, fontFamily, fontFile.getFileName(),
+                    0 /* ttcIndex */, fontFile.getWeight(), fontFile.isItalic() ? 1 : 0)) {
+                abortCreation(fontFamily);
+                return null;
+            }
+        }
+        if (!freeze(fontFamily)) {
+            return null;
+        }
+        return createFromFamiliesWithDefault(fontFamily);
+    }
+
+    @Override
+    public Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull FontsContractCompat.FontInfo[] fonts, int style) {
+        if (fonts.length < 1) {
+            return null;
+        }
+        if (!isFontFamilyPrivateAPIAvailable()) {
+            // Even if the private API is not avaiable, don't use API 21 implemenation and use
+            // public API to create Typeface from file descriptor.
+            final FontsContractCompat.FontInfo bestFont = findBestInfo(fonts, style);
+            final ContentResolver resolver = context.getContentResolver();
+            try (ParcelFileDescriptor pfd =
+                    resolver.openFileDescriptor(bestFont.getUri(), "r", cancellationSignal)) {
+                return new Typeface.Builder(pfd.getFileDescriptor())
+                        .setWeight(bestFont.getWeight())
+                        .setItalic(bestFont.isItalic())
+                        .build();
+            } catch (IOException e) {
+                return null;
+            }
+        }
+        Map<Uri, ByteBuffer> uriBuffer = FontsContractCompat.prepareFontData(
+                context, fonts, cancellationSignal);
+        final Object fontFamily = newFamily();
+        boolean atLeastOneFont = false;
+        for (FontsContractCompat.FontInfo font : fonts) {
+            final ByteBuffer fontBuffer = uriBuffer.get(font.getUri());
+            if (fontBuffer == null) {
+                continue;  // skip
+            }
+            final boolean success = addFontFromBuffer(fontFamily, fontBuffer,
+                    font.getTtcIndex(), font.getWeight(), font.isItalic() ? 1 : 0);
+            if (!success) {
+                abortCreation(fontFamily);
+                return null;
+            }
+            atLeastOneFont = true;
+        }
+        if (!atLeastOneFont) {
+            abortCreation(fontFamily);
+            return null;
+        }
+        if (!freeze(fontFamily)) {
+            return null;
+        }
+        return createFromFamiliesWithDefault(fontFamily);
+    }
+
+    /**
+     * Used by Resources to load a font resource of type font file.
+     */
+    @Nullable
+    @Override
+    public Typeface createFromResourcesFontFile(
+            Context context, Resources resources, int id, String path, int style) {
+        if (!isFontFamilyPrivateAPIAvailable()) {
+            super.createFromResourcesFontFile(context, resources, id, path, style);
+        }
+        Object fontFamily = newFamily();
+        if (!addFontFromAssetManager(context, fontFamily, path,
+                0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
+                RESOLVE_BY_FONT_TABLE /* italic */)) {
+            abortCreation(fontFamily);
+            return null;
+        }
+        if (!freeze(fontFamily)) {
+            return null;
+        }
+        return createFromFamiliesWithDefault(fontFamily);
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java b/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
new file mode 100644
index 0000000..8cfbd5d
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.os.CancellationSignal;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Implementation of the Typeface compat methods for API 14 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(14)
+class TypefaceCompatBaseImpl implements TypefaceCompat.TypefaceCompatImpl {
+    private static final String TAG = "TypefaceCompatBaseImpl";
+    private static final String CACHE_FILE_PREFIX = "cached_font_";
+
+    private interface StyleExtractor<T> {
+        int getWeight(T t);
+        boolean isItalic(T t);
+    }
+
+    private static <T> T findBestFont(T[] fonts, int style, StyleExtractor<T> extractor) {
+        final int targetWeight = (style & Typeface.BOLD) == 0 ? 400 : 700;
+        final boolean isTargetItalic = (style & Typeface.ITALIC) != 0;
+
+        T best = null;
+        int bestScore = Integer.MAX_VALUE;  // smaller is better
+
+        for (final T font : fonts) {
+            final int score = (Math.abs(extractor.getWeight(font) - targetWeight) * 2)
+                    + (extractor.isItalic(font) == isTargetItalic ? 0 : 1);
+
+            if (best == null || bestScore > score) {
+                best = font;
+                bestScore = score;
+            }
+        }
+        return best;
+    }
+
+    protected FontInfo findBestInfo(FontInfo[] fonts, int style) {
+        return findBestFont(fonts, style, new StyleExtractor<FontInfo>() {
+            @Override
+            public int getWeight(FontInfo info) {
+                return info.getWeight();
+            }
+
+            @Override
+            public boolean isItalic(FontInfo info) {
+                return info.isItalic();
+            }
+        });
+    }
+
+    // Caller must close the stream.
+    protected Typeface createFromInputStream(Context context, InputStream is) {
+        final File tmpFile = TypefaceCompatUtil.getTempFile(context);
+        if (tmpFile == null) {
+            return null;
+        }
+        try {
+            if (!TypefaceCompatUtil.copyToFile(tmpFile, is)) {
+                return null;
+            }
+            return Typeface.createFromFile(tmpFile.getPath());
+        } catch (RuntimeException e) {
+            // This was thrown from Typeface.createFromFile when a Typeface could not be loaded,
+            // such as due to an invalid ttf or unreadable file. We don't want to throw that
+            // exception anymore.
+            return null;
+        } finally {
+            tmpFile.delete();
+        }
+    }
+
+    @Override
+    public Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
+        // When we load from file, we can only load one font so just take the first one.
+        if (fonts.length < 1) {
+            return null;
+        }
+        FontInfo font = findBestInfo(fonts, style);
+        InputStream is = null;
+        try {
+            is = context.getContentResolver().openInputStream(font.getUri());
+            return createFromInputStream(context, is);
+        } catch (IOException e) {
+            return null;
+        } finally {
+            TypefaceCompatUtil.closeQuietly(is);
+        }
+    }
+
+    private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry, int style) {
+        return findBestFont(entry.getEntries(), style, new StyleExtractor<FontFileResourceEntry>() {
+            @Override
+            public int getWeight(FontFileResourceEntry entry) {
+                return entry.getWeight();
+            }
+
+            @Override
+            public boolean isItalic(FontFileResourceEntry entry) {
+                return entry.isItalic();
+            }
+        });
+    }
+
+    @Nullable
+    @Override
+    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
+            FontFamilyFilesResourceEntry entry, Resources resources, int style) {
+        FontFileResourceEntry best = findBestEntry(entry, style);
+        if (best == null) {
+            return null;
+        }
+        return TypefaceCompat.createFromResourcesFontFile(
+                context, resources, best.getResourceId(), best.getFileName(), style);
+    }
+
+    /**
+     * Used by Resources to load a font resource of type font file.
+     */
+    @Nullable
+    @Override
+    public Typeface createFromResourcesFontFile(
+            Context context, Resources resources, int id, String path, int style) {
+        final File tmpFile = TypefaceCompatUtil.getTempFile(context);
+        if (tmpFile == null) {
+            return null;
+        }
+        try {
+            if (!TypefaceCompatUtil.copyToFile(tmpFile, resources, id)) {
+                return null;
+            }
+            return Typeface.createFromFile(tmpFile.getPath());
+        } catch (RuntimeException e) {
+            // This was thrown from Typeface.createFromFile when a Typeface could not be loaded.
+            // such as due to an invalid ttf or unreadable file. We don't want to throw that
+            // exception anymore.
+            return null;
+        } finally {
+            tmpFile.delete();
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatUtil.java b/compat/java/android/support/v4/graphics/TypefaceCompatUtil.java
new file mode 100644
index 0000000..b7d4707
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatUtil.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Utility methods for TypefaceCompat.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class TypefaceCompatUtil {
+    private static final String TAG = "TypefaceCompatUtil";
+
+    private TypefaceCompatUtil() {}  // Do not instantiate.
+
+    private static final String CACHE_FILE_PREFIX = ".font";
+
+    /**
+     * Creates a temp file.
+     *
+     * Returns null if failed to create temp file.
+     */
+    public static File getTempFile(Context context) {
+        final String prefix = CACHE_FILE_PREFIX + Process.myPid() + "-" + Process.myTid() + "-";
+        for (int i = 0; i < 100; ++i) {
+            final File file = new File(context.getCacheDir(), prefix + i);
+            try {
+                if (file.createNewFile()) {
+                    return file;
+                }
+            } catch (IOException e) {
+                // ignore. Try next file.
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Copy the file contents to the direct byte buffer.
+     */
+    @RequiresApi(19)
+    private static ByteBuffer mmap(File file) {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            FileChannel channel = fis.getChannel();
+            final long size = channel.size();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Copy the file contents to the direct byte buffer.
+     */
+    @RequiresApi(19)
+    public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) {
+        final ContentResolver resolver = context.getContentResolver();
+        try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r", cancellationSignal);
+                FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
+            FileChannel channel = fis.getChannel();
+            final long size = channel.size();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Copy the resource contents to the direct byte buffer.
+     */
+    @RequiresApi(19)
+    public static ByteBuffer copyToDirectBuffer(Context context, Resources res, int id) {
+        File tmpFile = getTempFile(context);
+        if (tmpFile == null) {
+            return null;
+        }
+        try {
+            if (!copyToFile(tmpFile, res, id)) {
+                return null;
+            }
+            return mmap(tmpFile);
+        } finally {
+            tmpFile.delete();
+        }
+    }
+
+    /**
+     * Copy the input stream contents to file.
+     */
+    public static boolean copyToFile(File file, InputStream is) {
+        FileOutputStream os = null;
+        try {
+            os = new FileOutputStream(file, false);
+            byte[] buffer = new byte[1024];
+            int readLen;
+            while ((readLen = is.read(buffer)) != -1) {
+                os.write(buffer, 0, readLen);
+            }
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
+            return false;
+        } finally {
+            closeQuietly(os);
+        }
+    }
+
+    /**
+     * Copy the resource contents to file.
+     */
+    public static boolean copyToFile(File file, Resources res, int id) {
+        InputStream is = null;
+        try {
+            is = res.openRawResource(id);
+            return copyToFile(file, is);
+        } finally {
+            closeQuietly(is);
+        }
+    }
+
+    public static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java b/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java
index bd3424c..79fb9c6 100644
--- a/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java
+++ b/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java
@@ -21,260 +21,298 @@
 import android.graphics.ColorFilter;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
+import android.util.Log;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
 
 /**
- * Helper for accessing features in {@link android.graphics.drawable.Drawable}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.graphics.drawable.Drawable}.
  */
 public final class DrawableCompat {
     /**
-     * Interface for the full API.
+     * Interface implementation that doesn't use anything about platform-specific APIs.
      */
-    interface DrawableImpl {
-        void jumpToCurrentState(Drawable drawable);
-        void setAutoMirrored(Drawable drawable, boolean mirrored);
-        boolean isAutoMirrored(Drawable drawable);
-        void setHotspot(Drawable drawable, float x, float y);
-        void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom);
-        void setTint(Drawable drawable, int tint);
-        void setTintList(Drawable drawable, ColorStateList tint);
-        void setTintMode(Drawable drawable, PorterDuff.Mode tintMode);
-        Drawable wrap(Drawable drawable);
-        boolean setLayoutDirection(Drawable drawable, int layoutDirection);
-        int getLayoutDirection(Drawable drawable);
-        int getAlpha(Drawable drawable);
-        void applyTheme(Drawable drawable, Resources.Theme t);
-        boolean canApplyTheme(Drawable drawable);
-        ColorFilter getColorFilter(Drawable drawable);
-        void clearColorFilter(Drawable drawable);
-        void inflate(Drawable drawable, Resources res, XmlPullParser parser, AttributeSet attrs,
-                     Resources.Theme t) throws IOException, XmlPullParserException;
-    }
-
-    /**
-     * Interface implementation that doesn't use anything about v4 APIs.
-     */
-    static class BaseDrawableImpl implements DrawableImpl {
-        @Override
+    static class DrawableCompatBaseImpl {
         public void jumpToCurrentState(Drawable drawable) {
+            drawable.jumpToCurrentState();
         }
 
-        @Override
         public void setAutoMirrored(Drawable drawable, boolean mirrored) {
         }
 
-        @Override
         public boolean isAutoMirrored(Drawable drawable) {
             return false;
         }
 
-        @Override
         public void setHotspot(Drawable drawable, float x, float y) {
         }
 
-        @Override
         public void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) {
         }
 
-        @Override
         public void setTint(Drawable drawable, int tint) {
-            DrawableCompatBase.setTint(drawable, tint);
+            if (drawable instanceof TintAwareDrawable) {
+                ((TintAwareDrawable) drawable).setTint(tint);
+            }
         }
 
-        @Override
         public void setTintList(Drawable drawable, ColorStateList tint) {
-            DrawableCompatBase.setTintList(drawable, tint);
+            if (drawable instanceof TintAwareDrawable) {
+                ((TintAwareDrawable) drawable).setTintList(tint);
+            }
         }
 
-        @Override
         public void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
-            DrawableCompatBase.setTintMode(drawable, tintMode);
+            if (drawable instanceof TintAwareDrawable) {
+                ((TintAwareDrawable) drawable).setTintMode(tintMode);
+            }
         }
 
-        @Override
         public Drawable wrap(Drawable drawable) {
-            return DrawableCompatBase.wrapForTinting(drawable);
+            if (!(drawable instanceof TintAwareDrawable)) {
+                return new DrawableWrapperApi14(drawable);
+            }
+            return drawable;
         }
 
-        @Override
         public boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
             // No op for API < 23
             return false;
         }
 
-        @Override
         public int getLayoutDirection(Drawable drawable) {
             return ViewCompat.LAYOUT_DIRECTION_LTR;
         }
 
-        @Override
         public int getAlpha(Drawable drawable) {
             return 0;
         }
 
-        @Override
         public void applyTheme(Drawable drawable, Resources.Theme t) {
         }
 
-        @Override
         public boolean canApplyTheme(Drawable drawable) {
             return false;
         }
 
-        @Override
         public ColorFilter getColorFilter(Drawable drawable) {
             return null;
         }
 
-        @Override
         public void clearColorFilter(Drawable drawable) {
             drawable.clearColorFilter();
         }
 
-        @Override
         public void inflate(Drawable drawable, Resources res, XmlPullParser parser,
                             AttributeSet attrs, Resources.Theme t)
                 throws IOException, XmlPullParserException {
-            DrawableCompatBase.inflate(drawable, res, parser, attrs, t);
+            drawable.inflate(res, parser, attrs);
         }
     }
 
-    /**
-     * Interface implementation for devices with at least v11 APIs.
-     */
-    static class HoneycombDrawableImpl extends BaseDrawableImpl {
-        @Override
-        public void jumpToCurrentState(Drawable drawable) {
-            DrawableCompatHoneycomb.jumpToCurrentState(drawable);
-        }
+    @RequiresApi(17)
+    static class DrawableCompatApi17Impl extends DrawableCompatBaseImpl {
+        private static final String TAG = "DrawableCompatApi17";
 
-        @Override
-        public Drawable wrap(Drawable drawable) {
-            return DrawableCompatHoneycomb.wrapForTinting(drawable);
-        }
-    }
+        private static Method sSetLayoutDirectionMethod;
+        private static boolean sSetLayoutDirectionMethodFetched;
 
-    static class JellybeanMr1DrawableImpl extends HoneycombDrawableImpl {
+        private static Method sGetLayoutDirectionMethod;
+        private static boolean sGetLayoutDirectionMethodFetched;
+
         @Override
         public boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
-            return DrawableCompatJellybeanMr1.setLayoutDirection(drawable, layoutDirection);
+            if (!sSetLayoutDirectionMethodFetched) {
+                try {
+                    sSetLayoutDirectionMethod =
+                            Drawable.class.getDeclaredMethod("setLayoutDirection", int.class);
+                    sSetLayoutDirectionMethod.setAccessible(true);
+                } catch (NoSuchMethodException e) {
+                    Log.i(TAG, "Failed to retrieve setLayoutDirection(int) method", e);
+                }
+                sSetLayoutDirectionMethodFetched = true;
+            }
+
+            if (sSetLayoutDirectionMethod != null) {
+                try {
+                    sSetLayoutDirectionMethod.invoke(drawable, layoutDirection);
+                    return true;
+                } catch (Exception e) {
+                    Log.i(TAG, "Failed to invoke setLayoutDirection(int) via reflection", e);
+                    sSetLayoutDirectionMethod = null;
+                }
+            }
+            return false;
         }
 
         @Override
         public int getLayoutDirection(Drawable drawable) {
-            final int dir = DrawableCompatJellybeanMr1.getLayoutDirection(drawable);
-            return dir >= 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR;
+            if (!sGetLayoutDirectionMethodFetched) {
+                try {
+                    sGetLayoutDirectionMethod = Drawable.class.getDeclaredMethod("getLayoutDirection");
+                    sGetLayoutDirectionMethod.setAccessible(true);
+                } catch (NoSuchMethodException e) {
+                    Log.i(TAG, "Failed to retrieve getLayoutDirection() method", e);
+                }
+                sGetLayoutDirectionMethodFetched = true;
+            }
+
+            if (sGetLayoutDirectionMethod != null) {
+                try {
+                    return (int) sGetLayoutDirectionMethod.invoke(drawable);
+                } catch (Exception e) {
+                    Log.i(TAG, "Failed to invoke getLayoutDirection() via reflection", e);
+                    sGetLayoutDirectionMethod = null;
+                }
+            }
+            return ViewCompat.LAYOUT_DIRECTION_LTR;
         }
     }
 
     /**
      * Interface implementation for devices with at least KitKat APIs.
      */
-    static class KitKatDrawableImpl extends JellybeanMr1DrawableImpl {
+    @RequiresApi(19)
+    static class DrawableCompatApi19Impl extends DrawableCompatApi17Impl {
         @Override
         public void setAutoMirrored(Drawable drawable, boolean mirrored) {
-            DrawableCompatKitKat.setAutoMirrored(drawable, mirrored);
+            drawable.setAutoMirrored(mirrored);
         }
 
         @Override
         public boolean isAutoMirrored(Drawable drawable) {
-            return DrawableCompatKitKat.isAutoMirrored(drawable);
+            return drawable.isAutoMirrored();
         }
 
         @Override
         public Drawable wrap(Drawable drawable) {
-            return DrawableCompatKitKat.wrapForTinting(drawable);
+            if (!(drawable instanceof TintAwareDrawable)) {
+                return new DrawableWrapperApi19(drawable);
+            }
+            return drawable;
         }
 
         @Override
         public int getAlpha(Drawable drawable) {
-            return DrawableCompatKitKat.getAlpha(drawable);
+            return drawable.getAlpha();
         }
     }
 
     /**
      * Interface implementation for devices with at least L APIs.
      */
-    static class LollipopDrawableImpl extends KitKatDrawableImpl {
+    @RequiresApi(21)
+    static class DrawableCompatApi21Impl extends DrawableCompatApi19Impl {
         @Override
         public void setHotspot(Drawable drawable, float x, float y) {
-            DrawableCompatLollipop.setHotspot(drawable, x, y);
+            drawable.setHotspot(x, y);
         }
 
         @Override
         public void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) {
-            DrawableCompatLollipop.setHotspotBounds(drawable, left, top, right, bottom);
+            drawable.setHotspotBounds(left, top, right, bottom);
         }
 
         @Override
         public void setTint(Drawable drawable, int tint) {
-            DrawableCompatLollipop.setTint(drawable, tint);
+            drawable.setTint(tint);
         }
 
         @Override
         public void setTintList(Drawable drawable, ColorStateList tint) {
-            DrawableCompatLollipop.setTintList(drawable, tint);
+            drawable.setTintList(tint);
         }
 
         @Override
         public void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
-            DrawableCompatLollipop.setTintMode(drawable, tintMode);
+            drawable.setTintMode(tintMode);
         }
 
         @Override
         public Drawable wrap(Drawable drawable) {
-            return DrawableCompatLollipop.wrapForTinting(drawable);
+            if (!(drawable instanceof TintAwareDrawable)) {
+                return new DrawableWrapperApi21(drawable);
+            }
+            return drawable;
         }
 
         @Override
         public void applyTheme(Drawable drawable, Resources.Theme t) {
-            DrawableCompatLollipop.applyTheme(drawable, t);
+            drawable.applyTheme(t);
         }
 
         @Override
         public boolean canApplyTheme(Drawable drawable) {
-            return DrawableCompatLollipop.canApplyTheme(drawable);
+            return drawable.canApplyTheme();
         }
 
         @Override
         public ColorFilter getColorFilter(Drawable drawable) {
-            return DrawableCompatLollipop.getColorFilter(drawable);
+            return drawable.getColorFilter();
         }
 
         @Override
         public void clearColorFilter(Drawable drawable) {
-            DrawableCompatLollipop.clearColorFilter(drawable);
+            drawable.clearColorFilter();
+
+            // API 21 + 22 have an issue where clearing a color filter on a DrawableContainer
+            // will not propagate to all of its children. To workaround this we unwrap the drawable
+            // to find any DrawableContainers, and then unwrap those to clear the filter on its
+            // children manually
+            if (drawable instanceof InsetDrawable) {
+                clearColorFilter(((InsetDrawable) drawable).getDrawable());
+            } else if (drawable instanceof DrawableWrapper) {
+                clearColorFilter(((DrawableWrapper) drawable).getWrappedDrawable());
+            } else if (drawable instanceof DrawableContainer) {
+                final DrawableContainer container = (DrawableContainer) drawable;
+                final DrawableContainer.DrawableContainerState state =
+                        (DrawableContainer.DrawableContainerState) container.getConstantState();
+                if (state != null) {
+                    Drawable child;
+                    for (int i = 0, count = state.getChildCount(); i < count; i++) {
+                        child = state.getChild(i);
+                        if (child != null) {
+                            clearColorFilter(child);
+                        }
+                    }
+                }
+            }
         }
 
         @Override
         public void inflate(Drawable drawable, Resources res, XmlPullParser parser,
                             AttributeSet attrs, Resources.Theme t)
                 throws IOException, XmlPullParserException {
-            DrawableCompatLollipop.inflate(drawable, res, parser, attrs, t);
+            drawable.inflate(res, parser, attrs, t);
         }
     }
 
     /**
      * Interface implementation for devices with at least M APIs.
      */
-    static class MDrawableImpl extends LollipopDrawableImpl {
+    @RequiresApi(23)
+    static class DrawableCompatApi23Impl extends DrawableCompatApi21Impl {
         @Override
         public boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
-            return DrawableCompatApi23.setLayoutDirection(drawable, layoutDirection);
+            return drawable.setLayoutDirection(layoutDirection);
         }
 
         @Override
         public int getLayoutDirection(Drawable drawable) {
-            return DrawableCompatApi23.getLayoutDirection(drawable);
+            return drawable.getLayoutDirection();
         }
 
         @Override
@@ -293,21 +331,18 @@
     /**
      * Select the correct implementation to use for the current platform.
      */
-    static final DrawableImpl IMPL;
+    static final DrawableCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 23) {
-            IMPL = new MDrawableImpl();
-        } else if (version >= 21) {
-            IMPL = new LollipopDrawableImpl();
-        } else if (version >= 19) {
-            IMPL = new KitKatDrawableImpl();
-        } else if (version >= 17) {
-            IMPL = new JellybeanMr1DrawableImpl();
-        } else if (version >= 11) {
-            IMPL = new HoneycombDrawableImpl();
+        if (Build.VERSION.SDK_INT >= 23) {
+            IMPL = new DrawableCompatApi23Impl();
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new DrawableCompatApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new DrawableCompatApi19Impl();
+        } else if (Build.VERSION.SDK_INT >= 17) {
+            IMPL = new DrawableCompatApi17Impl();
         } else {
-            IMPL = new BaseDrawableImpl();
+            IMPL = new DrawableCompatBaseImpl();
         }
     }
 
@@ -508,6 +543,7 @@
      *
      * @see #wrap(Drawable)
      */
+    @SuppressWarnings("TypeParameterUnusedInFormals")
     public static <T extends Drawable> T unwrap(@NonNull Drawable drawable) {
         if (drawable instanceof DrawableWrapper) {
             return (T) ((DrawableWrapper) drawable).getWrappedDrawable();
diff --git a/compat/java/android/support/v4/graphics/drawable/IconCompat.java b/compat/java/android/support/v4/graphics/drawable/IconCompat.java
new file mode 100644
index 0000000..d6ef5de
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/drawable/IconCompat.java
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics.drawable;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+
+/**
+ * Helper for accessing features in {@link android.graphics.drawable.Icon}.
+ */
+public class IconCompat {
+
+    // Ratio of expected size to actual icon size
+    private static final float ADAPTIVE_ICON_INSET_FACTOR = 1 / 4f;
+    private static final float DEFAULT_VIEW_PORT_SCALE = 1 / (1 + 2 * ADAPTIVE_ICON_INSET_FACTOR);
+    private static final float ICON_DIAMETER_FACTOR = 176f / 192;
+    private static final float BLUR_FACTOR = 0.5f / 48;
+    private static final float KEY_SHADOW_OFFSET_FACTOR = 1f / 48;
+
+    private static final int KEY_SHADOW_ALPHA = 61;
+    private static final int AMBIENT_SHADOW_ALPHA = 30;
+
+    private static final int TYPE_BITMAP   = 1;
+    private static final int TYPE_RESOURCE = 2;
+    private static final int TYPE_DATA     = 3;
+    private static final int TYPE_URI      = 4;
+    private static final int TYPE_ADAPTIVE_BITMAP = 5;
+
+    private final int mType;
+
+    // To avoid adding unnecessary overhead, we have a few basic objects that get repurposed
+    // based on the value of mType.
+
+    // TYPE_BITMAP: Bitmap
+    // TYPE_ADAPTIVE_BITMAP: Bitmap
+    // TYPE_RESOURCE: Context
+    // TYPE_URI: String
+    // TYPE_DATA: DataBytes
+    private Object          mObj1;
+
+    // TYPE_RESOURCE: resId
+    // TYPE_DATA: data offset
+    private int             mInt1;
+
+    // TYPE_DATA: data length
+    private int             mInt2;
+
+    /**
+     * Create an Icon pointing to a drawable resource.
+     * @param context The context for the application whose resources should be used to resolve the
+     *                given resource ID.
+     * @param resId ID of the drawable resource
+     * @see android.graphics.drawable.Icon#createWithResource(Context, int)
+     */
+    public static IconCompat createWithResource(Context context, @DrawableRes int resId) {
+        if (context == null) {
+            throw new IllegalArgumentException("Context must not be null.");
+        }
+        final IconCompat rep = new IconCompat(TYPE_RESOURCE);
+        rep.mInt1 = resId;
+        rep.mObj1 = context;
+        return rep;
+    }
+
+    /**
+     * Create an Icon pointing to a bitmap in memory.
+     * @param bits A valid {@link android.graphics.Bitmap} object
+     * @see android.graphics.drawable.Icon#createWithBitmap(Bitmap)
+     */
+    public static IconCompat createWithBitmap(Bitmap bits) {
+        if (bits == null) {
+            throw new IllegalArgumentException("Bitmap must not be null.");
+        }
+        final IconCompat rep = new IconCompat(TYPE_BITMAP);
+        rep.mObj1 = bits;
+        return rep;
+    }
+
+    /**
+     * Create an Icon pointing to a bitmap in memory that follows the icon design guideline defined
+     * by {@link android.graphics.drawable.AdaptiveIconDrawable}.
+     * @param bits A valid {@link android.graphics.Bitmap} object
+     * @see android.graphics.drawable.Icon#createWithAdaptiveBitmap(Bitmap)
+     */
+    public static IconCompat createWithAdaptiveBitmap(Bitmap bits) {
+        if (bits == null) {
+            throw new IllegalArgumentException("Bitmap must not be null.");
+        }
+        final IconCompat rep = new IconCompat(TYPE_ADAPTIVE_BITMAP);
+        rep.mObj1 = bits;
+        return rep;
+    }
+
+    /**
+     * Create an Icon pointing to a compressed bitmap stored in a byte array.
+     * @param data Byte array storing compressed bitmap data of a type that
+     *             {@link android.graphics.BitmapFactory}
+     *             can decode (see {@link android.graphics.Bitmap.CompressFormat}).
+     * @param offset Offset into <code>data</code> at which the bitmap data starts
+     * @param length Length of the bitmap data
+     * @see android.graphics.drawable.Icon#createWithData(byte[], int, int)
+     */
+    public static IconCompat createWithData(byte[] data, int offset, int length) {
+        if (data == null) {
+            throw new IllegalArgumentException("Data must not be null.");
+        }
+        final IconCompat rep = new IconCompat(TYPE_DATA);
+        rep.mObj1 = data;
+        rep.mInt1 = offset;
+        rep.mInt2 = length;
+        return rep;
+    }
+
+    /**
+     * Create an Icon pointing to an image file specified by URI.
+     *
+     * @param uri A uri referring to local content:// or file:// image data.
+     * @see android.graphics.drawable.Icon#createWithContentUri(String)
+     */
+    public static IconCompat createWithContentUri(String uri) {
+        if (uri == null) {
+            throw new IllegalArgumentException("Uri must not be null.");
+        }
+        final IconCompat rep = new IconCompat(TYPE_URI);
+        rep.mObj1 = uri;
+        return rep;
+    }
+
+    /**
+     * Create an Icon pointing to an image file specified by URI.
+     *
+     * @param uri A uri referring to local content:// or file:// image data.
+     * @see android.graphics.drawable.Icon#createWithContentUri(String)
+     */
+    public static IconCompat createWithContentUri(Uri uri) {
+        if (uri == null) {
+            throw new IllegalArgumentException("Uri must not be null.");
+        }
+        return createWithContentUri(uri.toString());
+    }
+
+    private IconCompat(int mType) {
+        this.mType = mType;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @TargetApi(Build.VERSION_CODES.O)
+    public Icon toIcon() {
+        switch (mType) {
+            case TYPE_BITMAP:
+                return Icon.createWithBitmap((Bitmap) mObj1);
+            case TYPE_ADAPTIVE_BITMAP:
+                if (Build.VERSION.SDK_INT >= 26) {
+                    return Icon.createWithAdaptiveBitmap((Bitmap) mObj1);
+                } else {
+                    return Icon.createWithBitmap(createLegacyIconFromAdaptiveIcon((Bitmap) mObj1));
+                }
+            case TYPE_RESOURCE:
+                return Icon.createWithResource((Context) mObj1, mInt1);
+            case TYPE_DATA:
+                return Icon.createWithData((byte[]) mObj1, mInt1, mInt2);
+            case TYPE_URI:
+                return Icon.createWithContentUri((String) mObj1);
+            default:
+                throw new IllegalArgumentException("Unknown type");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void addToShortcutIntent(Intent outIntent) {
+        switch (mType) {
+            case TYPE_BITMAP:
+                outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, (Bitmap) mObj1);
+                break;
+            case TYPE_ADAPTIVE_BITMAP:
+                outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON,
+                        createLegacyIconFromAdaptiveIcon((Bitmap) mObj1));
+                break;
+            case TYPE_RESOURCE:
+                outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+                        Intent.ShortcutIconResource.fromContext((Context) mObj1, mInt1));
+                break;
+            default:
+                throw new IllegalArgumentException("Icon type not supported for intent shortcuts");
+        }
+    }
+
+    /**
+     * Converts a bitmap following the adaptive icon guide lines, into a bitmap following the
+     * shortcut icon guide lines.
+     * The returned bitmap will always have same width and height and clipped to a circle.
+     */
+    @VisibleForTesting
+    static Bitmap createLegacyIconFromAdaptiveIcon(Bitmap adaptiveIconBitmap) {
+        int size = (int) (DEFAULT_VIEW_PORT_SCALE * Math.min(adaptiveIconBitmap.getWidth(),
+                adaptiveIconBitmap.getHeight()));
+
+        Bitmap icon = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(icon);
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+
+        float center = size * 0.5f;
+        float radius = center * ICON_DIAMETER_FACTOR;
+
+        // Draw key shadow
+        float blur = BLUR_FACTOR * size;
+        paint.setColor(Color.TRANSPARENT);
+        paint.setShadowLayer(blur, 0, KEY_SHADOW_OFFSET_FACTOR * size, KEY_SHADOW_ALPHA << 24);
+        canvas.drawCircle(center, center, radius, paint);
+
+        // Draw ambient shadow
+        paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
+        canvas.drawCircle(center, center, radius, paint);
+        paint.clearShadowLayer();
+
+        // Draw the clipped icon
+        paint.setColor(Color.BLACK);
+        BitmapShader shader = new BitmapShader(adaptiveIconBitmap, Shader.TileMode.CLAMP,
+                Shader.TileMode.CLAMP);
+        Matrix shift = new Matrix();
+        shift.setTranslate(-(adaptiveIconBitmap.getWidth() - size) / 2,
+                -(adaptiveIconBitmap.getHeight() - size) / 2);
+        shader.setLocalMatrix(shift);
+        paint.setShader(shader);
+        canvas.drawCircle(center, center, radius, paint);
+
+        canvas.setBitmap(null);
+        return icon;
+    }
+}
diff --git a/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java b/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java
index 177c40a..50d246b 100644
--- a/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java
+++ b/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java
@@ -17,14 +17,16 @@
 package android.support.v4.hardware.display;
 
 import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.Display;
 import android.view.WindowManager;
 
 import java.util.WeakHashMap;
 
 /**
- * Helper for accessing features in {@link android.hardware.display.DisplayManager}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.hardware.display.DisplayManager}.
  */
 public abstract class DisplayManagerCompat {
     private static final WeakHashMap<Context, DisplayManagerCompat> sInstances =
@@ -54,11 +56,10 @@
         synchronized (sInstances) {
             DisplayManagerCompat instance = sInstances.get(context);
             if (instance == null) {
-                final int version = android.os.Build.VERSION.SDK_INT;
-                if (version >= 17) {
-                    instance = new JellybeanMr1Impl(context);
+                if (Build.VERSION.SDK_INT >= 17) {
+                    instance = new DisplayManagerCompatApi17Impl(context);
                 } else {
-                    instance = new LegacyImpl(context);
+                    instance = new DisplayManagerCompatApi14Impl(context);
                 }
                 sInstances.put(context, instance);
             }
@@ -102,11 +103,11 @@
      */
     public abstract Display[] getDisplays(String category);
 
-    private static class LegacyImpl extends DisplayManagerCompat {
+    private static class DisplayManagerCompatApi14Impl extends DisplayManagerCompat {
         private final WindowManager mWindowManager;
 
-        public LegacyImpl(Context context) {
-            mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+        DisplayManagerCompatApi14Impl(Context context) {
+            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         }
 
         @Override
@@ -129,26 +130,27 @@
         }
     }
 
-    private static class JellybeanMr1Impl extends DisplayManagerCompat {
-        private final Object mDisplayManagerObj;
+    @RequiresApi(17)
+    private static class DisplayManagerCompatApi17Impl extends DisplayManagerCompat {
+        private final DisplayManager mDisplayManager;
 
-        public JellybeanMr1Impl(Context context) {
-            mDisplayManagerObj = DisplayManagerJellybeanMr1.getDisplayManager(context);
+        DisplayManagerCompatApi17Impl(Context context) {
+            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
         }
 
         @Override
         public Display getDisplay(int displayId) {
-            return DisplayManagerJellybeanMr1.getDisplay(mDisplayManagerObj, displayId);
+            return mDisplayManager.getDisplay(displayId);
         }
 
         @Override
         public Display[] getDisplays() {
-            return DisplayManagerJellybeanMr1.getDisplays(mDisplayManagerObj);
+            return mDisplayManager.getDisplays();
         }
 
         @Override
         public Display[] getDisplays(String category) {
-            return DisplayManagerJellybeanMr1.getDisplays(mDisplayManagerObj, category);
+            return mDisplayManager.getDisplays(category);
         }
     }
 }
diff --git a/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java b/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
index 26045f7..2065bd3 100644
--- a/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
+++ b/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.CancellationSignal;
 
 import java.security.Signature;
@@ -49,8 +50,7 @@
 
     static final FingerprintManagerCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 23) {
+        if (Build.VERSION.SDK_INT >= 23) {
             IMPL = new Api23FingerprintManagerCompatImpl();
         } else {
             IMPL = new LegacyFingerprintManagerCompatImpl();
@@ -229,6 +229,7 @@
         }
     }
 
+    @RequiresApi(23)
     private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl {
 
         public Api23FingerprintManagerCompatImpl() {
diff --git a/compat/java/android/support/v4/internal/view/SupportMenu.java b/compat/java/android/support/v4/internal/view/SupportMenu.java
index 55b8a95..c072151 100644
--- a/compat/java/android/support/v4/internal/view/SupportMenu.java
+++ b/compat/java/android/support/v4/internal/view/SupportMenu.java
@@ -19,6 +19,7 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.support.annotation.RestrictTo;
+import android.view.KeyEvent;
 
 /**
  * Interface for managing the items in a menu.
@@ -53,6 +54,13 @@
     int CATEGORY_SHIFT = 16;
 
     /**
+     * A mask of all supported modifiers for MenuItem's keyboard shortcuts
+     */
+    int SUPPORTED_MODIFIERS_MASK = KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON
+            | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON
+            | KeyEvent.META_FUNCTION_ON;
+
+    /**
      * Flag which stops the Menu being closed when a sub menu is opened
      */
     int FLAG_KEEP_OPEN_ON_SUBMENU_OPENED = 4;
diff --git a/compat/java/android/support/v4/internal/view/SupportMenuItem.java b/compat/java/android/support/v4/internal/view/SupportMenuItem.java
index a72ae21..e6cdd3d 100644
--- a/compat/java/android/support/v4/internal/view/SupportMenuItem.java
+++ b/compat/java/android/support/v4/internal/view/SupportMenuItem.java
@@ -18,9 +18,11 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ActionProvider;
-import android.support.v4.view.MenuItemCompat;
 import android.view.MenuItem;
 import android.view.View;
 
@@ -79,6 +81,7 @@
      * @see android.app.ActionBar
      * @see #setActionView(View)
      */
+    @Override
     void setShowAsAction(int actionEnum);
 
     /**
@@ -99,6 +102,7 @@
      * @see android.app.ActionBar
      * @see #setActionView(View)
      */
+    @Override
     MenuItem setShowAsActionFlags(int actionEnum);
 
     /**
@@ -113,6 +117,7 @@
      * @return This Item so additional setters can be called.
      * @see #setShowAsAction(int)
      */
+    @Override
     MenuItem setActionView(View view);
 
     /**
@@ -127,6 +132,7 @@
      * @return This Item so additional setters can be called.
      * @see #setShowAsAction(int)
      */
+    @Override
     MenuItem setActionView(int resId);
 
     /**
@@ -136,6 +142,7 @@
      * @see #setActionView(View)
      * @see #setShowAsAction(int)
      */
+    @Override
     View getActionView();
 
     /**
@@ -165,24 +172,29 @@
     /**
      * Expand the action view associated with this menu item. The menu item must have an action view
      * set, as well as the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a
-     * listener has been set using {@link #setSupportOnActionExpandListener(android.support.v4.view.MenuItemCompat.OnActionExpandListener)}
-     * it will have its {@link android.support.v4.view.MenuItemCompat.OnActionExpandListener#onMenuItemActionExpand(MenuItem)} method
-     * invoked. The listener may return false from this method to prevent expanding the action view.
+     * listener has been set using
+     * {@link #setSupportOnActionExpandListener(MenuItem.OnActionExpandListener)}
+     * it will have its {@link MenuItem.OnActionExpandListener#onMenuItemActionExpand(MenuItem)}
+     * method invoked. The listener may return false from this method to prevent expanding the
+     * action view.
      *
      * @return true if the action view was expanded, false otherwise.
      */
+    @Override
     boolean expandActionView();
 
     /**
      * Collapse the action view associated with this menu item. The menu item must have an action
      * view set, as well as the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a
-     * listener has been set using {@link #setSupportOnActionExpandListener(android.support.v4.view.MenuItemCompat.OnActionExpandListener)}
-     * it will have its {@link android.support.v4.view.MenuItemCompat.OnActionExpandListener#onMenuItemActionCollapse(MenuItem)} method
-     * invoked. The listener may return false from this method to prevent collapsing the action
-     * view.
+     * listener has been set using
+     * {@link #setSupportOnActionExpandListener(MenuItem.OnActionExpandListener)}
+     * it will have its {@link MenuItem.OnActionExpandListener#onMenuItemActionCollapse(MenuItem)}
+     * method invoked. The listener may return false from this method to prevent collapsing the
+     * action view.
      *
      * @return true if the action view was collapsed, false otherwise.
      */
+    @Override
     boolean collapseActionView();
 
     /**
@@ -192,17 +204,175 @@
      * @see #expandActionView()
      * @see #collapseActionView()
      * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
-     * @see android.support.v4.view.MenuItemCompat.OnActionExpandListener
+     * @see MenuItem.OnActionExpandListener
      */
+    @Override
     boolean isActionViewExpanded();
 
     /**
-     * Set an {@link android.support.v4.view.MenuItemCompat.OnActionExpandListener} on this menu item to be notified when the associated
-     * action view is expanded or collapsed. The menu item must be configured to expand or collapse
-     * its action view using the flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}.
+     * Change the content description associated with this menu item.
      *
-     * @param listener Listener that will respond to expand/collapse events
-     * @return This menu item instance for call chaining
+     * @param contentDescription The new content description.
+     * @return This menu item instance for call chaining.
      */
-    SupportMenuItem setSupportOnActionExpandListener(MenuItemCompat.OnActionExpandListener listener);
+    @Override
+    SupportMenuItem setContentDescription(CharSequence contentDescription);
+
+    /**
+     * Retrieve the content description associated with this menu item.
+     *
+     * @return The content description.
+     */
+    @Override
+    CharSequence getContentDescription();
+
+    /**
+     * Change the tooltip text associated with this menu item.
+     *
+     * @param tooltipText The new tooltip text.
+     * @return This menu item instance for call chaining.
+     */
+    @Override
+    SupportMenuItem setTooltipText(CharSequence tooltipText);
+
+    /**
+     * Retrieve the tooltip text associated with this menu item.
+     *
+     * @return The tooltip text.
+     */
+    @Override
+    CharSequence getTooltipText();
+
+    /**
+     * Change both the numeric and alphabetic shortcut associated with this
+     * item. Note that the shortcut will be triggered when the key that
+     * generates the given character is pressed along with the corresponding
+     * modifier key. Also note that case is not significant and that alphabetic
+     * shortcut characters will be handled in lower case.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key. This is the shortcut when
+     *        using a numeric (e.g., 12-key) keyboard.
+     * @param numericModifiers The numeric modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The alphabetic modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @return This Item so additional setters can be called.
+     */
+    @Override
+    MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers);
+
+    /**
+     * Change the numeric shortcut and modifiers associated with this item.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key.  This is the shortcut when
+     *                 using a 12-key (numeric) keyboard.
+     * @param numericModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @return This Item so additional setters can be called.
+     */
+    @Override
+    MenuItem setNumericShortcut(char numericChar, int numericModifiers);
+
+    /**
+     * Return the modifiers for this menu item's numeric (12-key) shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the numeric shortcut.
+     */
+    @Override
+    int getNumericModifiers();
+
+    /**
+     * Change the alphabetic shortcut associated with this item. The shortcut
+     * will be triggered when the key that generates the given character is
+     * pressed along with the modifier keys. Case is not significant and shortcut
+     * characters will be displayed in lower case. Note that menu items with
+     * the characters '\b' or '\n' as shortcuts will get triggered by the
+     * Delete key or Carriage Return key, respectively.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @return This Item so additional setters can be called.
+     */
+    @Override
+    MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers);
+
+    /**
+     * Return the modifier for this menu item's alphabetic shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the keyboard shortcut.
+     */
+    @Override
+    int getAlphabeticModifiers();
+
+    /**
+     * Applies a tint to this item's icon. Does not modify the
+     * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link MenuItem#setIcon(Drawable)} or {@link MenuItem#setIcon(int)} will
+     * automatically mutate the icon and apply the specified tint and
+     * tint mode.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @see #getIconTintList()
+     */
+    @Override
+    MenuItem setIconTintList(ColorStateList tint);
+
+    /**
+     * @return the tint applied to this item's icon
+     * @see #setIconTintList(ColorStateList)
+     */
+    @Override
+    ColorStateList getIconTintList();
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setIconTintList(ColorStateList)} to this item's icon. The default mode is
+     * {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @see #setIconTintList(ColorStateList)
+     */
+    @Override
+    MenuItem setIconTintMode(PorterDuff.Mode tintMode);
+
+    /**
+     * Returns the blending mode used to apply the tint to this item's icon, if specified.
+     *
+     * @return the blending mode used to apply the tint to this item's icon
+     * @see #setIconTintMode(PorterDuff.Mode)
+     */
+    @Override
+    PorterDuff.Mode getIconTintMode();
 }
\ No newline at end of file
diff --git a/compat/java/android/support/v4/net/ConnectivityManagerCompat.java b/compat/java/android/support/v4/net/ConnectivityManagerCompat.java
index f9ff971..5a39586 100644
--- a/compat/java/android/support/v4/net/ConnectivityManagerCompat.java
+++ b/compat/java/android/support/v4/net/ConnectivityManagerCompat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.net;
 
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -30,14 +32,15 @@
 import android.net.NetworkInfo;
 import android.os.Build;
 import android.support.annotation.IntDef;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RequiresPermission;
 import android.support.annotation.RestrictTo;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Helper for accessing features in {@link ConnectivityManager} introduced after
- * API level 16 in a backwards compatible fashion.
+ * Helper for accessing features in {@link ConnectivityManager}.
  */
 public final class ConnectivityManagerCompat {
 
@@ -51,7 +54,7 @@
     /** @hide */
     @RestrictTo(LIBRARY_GROUP)
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = false, value = {
+    @IntDef(value = {
             RESTRICT_BACKGROUND_STATUS_DISABLED,
             RESTRICT_BACKGROUND_STATUS_WHITELISTED,
             RESTRICT_BACKGROUND_STATUS_ENABLED,
@@ -82,7 +85,8 @@
      */
     public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3;
 
-    static class BaseConnectivityManagerCompatImpl implements ConnectivityManagerCompatImpl {
+    static class ConnectivityManagerCompatBaseImpl implements ConnectivityManagerCompatImpl {
+        @SuppressWarnings("deprecation")
         @Override
         public boolean isActiveNetworkMetered(ConnectivityManager cm) {
             final NetworkInfo info = cm.getActiveNetworkInfo();
@@ -101,6 +105,8 @@
                 case TYPE_WIMAX:
                     return true;
                 case TYPE_WIFI:
+                case TYPE_BLUETOOTH:
+                case TYPE_ETHERNET:
                     return false;
                 default:
                     // err on side of caution
@@ -114,28 +120,20 @@
         }
     }
 
-    static class HoneycombMR2ConnectivityManagerCompatImpl
-            extends BaseConnectivityManagerCompatImpl {
+    @RequiresApi(16)
+    static class ConnectivityManagerCompatApi16Impl extends ConnectivityManagerCompatBaseImpl {
         @Override
         public boolean isActiveNetworkMetered(ConnectivityManager cm) {
-            return ConnectivityManagerCompatHoneycombMR2.isActiveNetworkMetered(cm);
+            return cm.isActiveNetworkMetered();
         }
     }
 
-    static class JellyBeanConnectivityManagerCompatImpl
-            extends HoneycombMR2ConnectivityManagerCompatImpl {
-        @Override
-        public boolean isActiveNetworkMetered(ConnectivityManager cm) {
-            return ConnectivityManagerCompatJellyBean.isActiveNetworkMetered(cm);
-        }
-    }
-
-    static class Api24ConnectivityManagerCompatImpl
-            extends JellyBeanConnectivityManagerCompatImpl {
+    @RequiresApi(24)
+    static class ConnectivityManagerCompatApi24Impl extends ConnectivityManagerCompatApi16Impl {
         @Override
         public int getRestrictBackgroundStatus(ConnectivityManager cm) {
             //noinspection ResourceType
-            return ConnectivityManagerCompatApi24.getRestrictBackgroundStatus(cm);
+            return cm.getRestrictBackgroundStatus();
         }
     }
 
@@ -143,13 +141,11 @@
 
     static {
         if (Build.VERSION.SDK_INT >= 24) {
-            IMPL = new Api24ConnectivityManagerCompatImpl();
+            IMPL = new ConnectivityManagerCompatApi24Impl();
         } else if (Build.VERSION.SDK_INT >= 16) {
-            IMPL = new JellyBeanConnectivityManagerCompatImpl();
-        } else if (Build.VERSION.SDK_INT >= 13) {
-            IMPL = new HoneycombMR2ConnectivityManagerCompatImpl();
+            IMPL = new ConnectivityManagerCompatApi16Impl();
         } else {
-            IMPL = new BaseConnectivityManagerCompatImpl();
+            IMPL = new ConnectivityManagerCompatBaseImpl();
         }
     }
 
@@ -166,6 +162,7 @@
      * @return {@code true} if large transfers should be avoided, otherwise
      *        {@code false}.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
         return IMPL.isActiveNetworkMetered(cm);
     }
diff --git a/compat/java/android/support/v4/net/TrafficStatsCompat.java b/compat/java/android/support/v4/net/TrafficStatsCompat.java
index 2e0fa69..1b62101 100644
--- a/compat/java/android/support/v4/net/TrafficStatsCompat.java
+++ b/compat/java/android/support/v4/net/TrafficStatsCompat.java
@@ -16,171 +16,85 @@
 
 package android.support.v4.net;
 
+import android.net.TrafficStats;
 import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.RequiresApi;
 
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.net.SocketException;
 
 /**
- * Helper for accessing features in TrafficStats introduced after API level 14
- * in a backwards compatible fashion.
+ * Helper for accessing features in {@link TrafficStats}.
  */
 public final class TrafficStatsCompat {
-
-    interface TrafficStatsCompatImpl {
-        void clearThreadStatsTag();
-        int getThreadStatsTag();
-        void incrementOperationCount(int operationCount);
-        void incrementOperationCount(int tag, int operationCount);
-        void setThreadStatsTag(int tag);
-        void tagSocket(Socket socket) throws SocketException;
-        void untagSocket(Socket socket) throws SocketException;
-        void tagDatagramSocket(DatagramSocket socket) throws SocketException;
-        void untagDatagramSocket(DatagramSocket socket) throws SocketException;
-    }
-
-    static class BaseTrafficStatsCompatImpl implements TrafficStatsCompatImpl {
-        private static class SocketTags {
-            public int statsTag = -1;
-
-            SocketTags() {
-            }
+    static class TrafficStatsCompatBaseImpl {
+        public void tagDatagramSocket(DatagramSocket socket) throws SocketException {
+            final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+            TrafficStats.tagSocket(new DatagramSocketWrapper(socket, pfd.getFileDescriptor()));
+            // The developer is still using the FD, so we need to detach it to
+            // prevent the PFD finalizer from closing it in their face. We had to
+            // wait until after the tagging call above, since detaching clears out
+            // the getFileDescriptor() result which tagging depends on.
+            pfd.detachFd();
         }
 
-        private ThreadLocal<SocketTags> mThreadSocketTags = new ThreadLocal<SocketTags>() {
-            @Override
-            protected SocketTags initialValue() {
-                return new SocketTags();
-            }
-        };
-
-        @Override
-        public void clearThreadStatsTag() {
-            mThreadSocketTags.get().statsTag = -1;
-        }
-
-        @Override
-        public int getThreadStatsTag() {
-            return mThreadSocketTags.get().statsTag;
-        }
-
-        @Override
-        public void incrementOperationCount(int operationCount) {
-        }
-
-        @Override
-        public void incrementOperationCount(int tag, int operationCount) {
-        }
-
-        @Override
-        public void setThreadStatsTag(int tag) {
-            mThreadSocketTags.get().statsTag = tag;
-        }
-
-        @Override
-        public void tagSocket(Socket socket) {
-        }
-
-        @Override
-        public void untagSocket(Socket socket) {
-        }
-
-        @Override
-        public void tagDatagramSocket(DatagramSocket socket) {
-        }
-
-        @Override
-        public void untagDatagramSocket(DatagramSocket socket) {
+        public void untagDatagramSocket(DatagramSocket socket) throws SocketException {
+            final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+            TrafficStats.untagSocket(new DatagramSocketWrapper(socket, pfd.getFileDescriptor()));
+            // The developer is still using the FD, so we need to detach it to
+            // prevent the PFD finalizer from closing it in their face. We had to
+            // wait until after the tagging call above, since detaching clears out
+            // the getFileDescriptor() result which tagging depends on.
+            pfd.detachFd();
         }
     }
 
-    static class IcsTrafficStatsCompatImpl implements TrafficStatsCompatImpl {
-        @Override
-        public void clearThreadStatsTag() {
-            TrafficStatsCompatIcs.clearThreadStatsTag();
-        }
-
-        @Override
-        public int getThreadStatsTag() {
-            return TrafficStatsCompatIcs.getThreadStatsTag();
-        }
-
-        @Override
-        public void incrementOperationCount(int operationCount) {
-            TrafficStatsCompatIcs.incrementOperationCount(operationCount);
-        }
-
-        @Override
-        public void incrementOperationCount(int tag, int operationCount) {
-            TrafficStatsCompatIcs.incrementOperationCount(tag, operationCount);
-        }
-
-        @Override
-        public void setThreadStatsTag(int tag) {
-            TrafficStatsCompatIcs.setThreadStatsTag(tag);
-        }
-
-        @Override
-        public void tagSocket(Socket socket) throws SocketException {
-            TrafficStatsCompatIcs.tagSocket(socket);
-        }
-
-        @Override
-        public void untagSocket(Socket socket) throws SocketException {
-            TrafficStatsCompatIcs.untagSocket(socket);
-        }
-
+    @RequiresApi(24)
+    static class TrafficStatsCompatApi24Impl extends TrafficStatsCompatBaseImpl {
         @Override
         public void tagDatagramSocket(DatagramSocket socket) throws SocketException {
-            TrafficStatsCompatIcs.tagDatagramSocket(socket);
+            TrafficStats.tagDatagramSocket(socket);
         }
 
         @Override
         public void untagDatagramSocket(DatagramSocket socket) throws SocketException {
-            TrafficStatsCompatIcs.untagDatagramSocket(socket);
+            TrafficStats.untagDatagramSocket(socket);
         }
     }
 
-    static class Api24TrafficStatsCompatImpl extends IcsTrafficStatsCompatImpl {
-        @Override
-        public void tagDatagramSocket(DatagramSocket socket) throws SocketException {
-            TrafficStatsCompatApi24.tagDatagramSocket(socket);
-        }
-
-        @Override
-        public void untagDatagramSocket(DatagramSocket socket) throws SocketException {
-            TrafficStatsCompatApi24.untagDatagramSocket(socket);
-        }
-    }
-
-    private static final TrafficStatsCompatImpl IMPL;
+    private static final TrafficStatsCompatBaseImpl IMPL;
 
     static {
-        if ("N".equals(Build.VERSION.CODENAME)) {
-            IMPL = new Api24TrafficStatsCompatImpl();
-        } else if (Build.VERSION.SDK_INT >= 14) {
-            IMPL = new IcsTrafficStatsCompatImpl();
+        if (Build.VERSION.SDK_INT >= 24) {
+            IMPL = new TrafficStatsCompatApi24Impl();
         } else {
-            IMPL = new BaseTrafficStatsCompatImpl();
+            IMPL = new TrafficStatsCompatBaseImpl();
         }
     }
 
     /**
      * Clear active tag used when accounting {@link Socket} traffic originating
      * from the current thread.
+     *
+     * @deprecated Use {@link TrafficStats#clearThreadStatsTag()} directly.
      */
+    @Deprecated
     public static void clearThreadStatsTag() {
-        IMPL.clearThreadStatsTag();
+        TrafficStats.clearThreadStatsTag();
     }
 
     /**
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
+     *
+     * @deprecated Use {@link TrafficStats#getThreadStatsTag()} directly.
      */
+    @Deprecated
     public static int getThreadStatsTag() {
-        return IMPL.getThreadStatsTag();
+        return TrafficStats.getThreadStatsTag();
     }
 
     /**
@@ -189,9 +103,12 @@
      * bytes-per-operation.
      *
      * @param operationCount Number of operations to increment count by.
+     *
+     * @deprecated Use {@link TrafficStats#incrementOperationCount(int)} directly.
      */
+    @Deprecated
     public static void incrementOperationCount(int operationCount) {
-        IMPL.incrementOperationCount(operationCount);
+        TrafficStats.incrementOperationCount(operationCount);
     }
 
     /**
@@ -200,9 +117,12 @@
      *
      * @param tag Accounting tag used in {@link #setThreadStatsTag(int)}.
      * @param operationCount Number of operations to increment count by.
+     *
+     * @deprecated Use {@link TrafficStats#incrementOperationCount(int, int)} directly.
      */
+    @Deprecated
     public static void incrementOperationCount(int tag, int operationCount) {
-        IMPL.incrementOperationCount(tag, operationCount);
+        TrafficStats.incrementOperationCount(tag, operationCount);
     }
 
     /**
@@ -215,9 +135,12 @@
      * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
      * used internally by system services like DownloadManager when performing
      * traffic on behalf of an application.
+     *
+     * @deprecated Use {@link TrafficStats#setThreadStatsTag(int)} directly.
      */
+    @Deprecated
     public static void setThreadStatsTag(int tag) {
-        IMPL.setThreadStatsTag(tag);
+        TrafficStats.setThreadStatsTag(tag);
     }
 
     /**
@@ -227,16 +150,22 @@
      * statistics parameters.
      *
      * @see #setThreadStatsTag(int)
+     *
+     * @deprecated Use {@link TrafficStats#tagSocket(Socket)} directly.
      */
+    @Deprecated
     public static void tagSocket(Socket socket) throws SocketException {
-        IMPL.tagSocket(socket);
+        TrafficStats.tagSocket(socket);
     }
 
     /**
      * Remove any statistics parameters from the given {@link Socket}.
+     *
+     * @deprecated Use {@link TrafficStats#untagSocket(Socket)} directly.
      */
+    @Deprecated
     public static void untagSocket(Socket socket) throws SocketException {
-        IMPL.untagSocket(socket);
+        TrafficStats.untagSocket(socket);
     }
 
     /**
diff --git a/compat/java/android/support/v4/os/AsyncTaskCompat.java b/compat/java/android/support/v4/os/AsyncTaskCompat.java
index 2423c73..ebad879 100644
--- a/compat/java/android/support/v4/os/AsyncTaskCompat.java
+++ b/compat/java/android/support/v4/os/AsyncTaskCompat.java
@@ -17,12 +17,15 @@
 package android.support.v4.os;
 
 import android.os.AsyncTask;
-import android.os.Build;
+
+import java.util.concurrent.Executor;
 
 /**
- * Helper for accessing features in {@link android.os.AsyncTask}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.os.AsyncTask}.
+ *
+ * @deprecated Use {@link android.os.AsyncTask} directly.
  */
+@Deprecated
 public final class AsyncTaskCompat {
 
     /**
@@ -32,21 +35,17 @@
      * @param task The {@link android.os.AsyncTask} to execute.
      * @param params The parameters of the task.
      * @return the instance of AsyncTask.
+     *
+     * @deprecated Use {@link android.os.AsyncTask#executeOnExecutor(Executor, Object[])} directly.
      */
+    @Deprecated
     public static <Params, Progress, Result> AsyncTask<Params, Progress, Result> executeParallel(
             AsyncTask<Params, Progress, Result> task,
             Params... params) {
         if (task == null) {
             throw new IllegalArgumentException("task can not be null");
         }
-
-        if (Build.VERSION.SDK_INT >= 11) {
-            // From API 11 onwards, we need to manually select the THREAD_POOL_EXECUTOR
-            AsyncTaskCompatHoneycomb.executeParallel(task, params);
-        } else {
-            // Before API 11, all tasks were run in parallel
-            task.execute(params);
-        }
+        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
 
         return task;
     }
diff --git a/compat/java/android/support/v4/os/BuildCompat.java b/compat/java/android/support/v4/os/BuildCompat.java
new file mode 100644
index 0000000..c28f3d9
--- /dev/null
+++ b/compat/java/android/support/v4/os/BuildCompat.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+import android.os.Build.VERSION;
+
+/**
+ * This class contains additional platform version checking methods for targeting pre-release
+ * versions of Android.
+ */
+public class BuildCompat {
+    private BuildCompat() {
+    }
+
+    /**
+     * Checks if the device is running on the Android N release or newer.
+     *
+     * @return {@code true} if N APIs are available for use
+     * @deprecated Android N is a finalized release and this method is no longer necessary. It will
+     *             be removed in a future release of the Support Library. Instead, use
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES#N}.
+     */
+    @Deprecated
+    public static boolean isAtLeastN() {
+        return VERSION.SDK_INT >= 24;
+    }
+
+    /**
+     * Checks if the device is running on the Android N MR1 release or newer.
+     *
+     * @return {@code true} if N MR1 APIs are available for use
+     * @deprecated Android N MR1 is a finalized release and this method is no longer necessary. It
+     *             will be removed in a future release of the Support Library. Instead, use
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES#N_MR1}.
+     */
+    @Deprecated
+    public static boolean isAtLeastNMR1() {
+        return VERSION.SDK_INT >= 25;
+    }
+
+    /**
+     * Checks if the device is running on a pre-release version of Android O or newer.
+     * <p>
+     * @return {@code true} if O APIs are available for use, {@code false} otherwise
+     * @deprecated Android O is a finalized release and this method is no longer necessary. It will
+     *             be removed in a future release of the Support Library. Instead use
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES#O}.
+     */
+    public static boolean isAtLeastO() {
+        return VERSION.SDK_INT >= 26;
+    }
+
+    /**
+     * Checks if the device is running on a pre-release version of Android O MR1 or newer.
+     * <p>
+     * <strong>Note:</strong> This method will return {@code false} on devices running release
+     * versions of Android. When Android O MR1 is finalized for release, this method will be
+     * deprecated and all calls should be replaced with
+     * {@code Build.SDK_INT >= Build.VERSION_CODES#O_MR1}.
+     *
+     * @return {@code true} if O MR1 APIs are available for use, {@code false} otherwise
+     */
+    public static boolean isAtLeastOMR1() {
+        return VERSION.CODENAME.startsWith("OMR")
+                || isAtLeastP();
+    }
+
+    /**
+     * Checks if the device is running on a pre-release version of Android P or newer.
+     * <p>
+     * <strong>Note:</strong> This method will return {@code false} on devices running release
+     * versions of Android. When Android P is finalized for release, this method will be deprecated
+     * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES#P}.
+     *
+     * @return {@code true} if P APIs are available for use, {@code false} otherwise
+     */
+    public static boolean isAtLeastP() {
+        return VERSION.CODENAME.equals("P");
+    }
+}
diff --git a/compat/java/android/support/v4/os/CancellationSignal.java b/compat/java/android/support/v4/os/CancellationSignal.java
index 41bdfe6..b6c0dc9 100644
--- a/compat/java/android/support/v4/os/CancellationSignal.java
+++ b/compat/java/android/support/v4/os/CancellationSignal.java
@@ -78,8 +78,8 @@
             if (listener != null) {
                 listener.onCancel();
             }
-            if (obj != null) {
-                CancellationSignalCompatJellybean.cancel(obj);
+            if (obj != null && Build.VERSION.SDK_INT >= 16) {
+                ((android.os.CancellationSignal) obj).cancel();
             }
         } finally {
             synchronized (this) {
@@ -137,9 +137,9 @@
         }
         synchronized (this) {
             if (mCancellationSignalObj == null) {
-                mCancellationSignalObj = CancellationSignalCompatJellybean.create();
+                mCancellationSignalObj = new android.os.CancellationSignal();
                 if (mIsCanceled) {
-                    CancellationSignalCompatJellybean.cancel(mCancellationSignalObj);
+                    ((android.os.CancellationSignal) mCancellationSignalObj).cancel();
                 }
             }
             return mCancellationSignalObj;
diff --git a/compat/java/android/support/v4/os/ConfigurationCompat.java b/compat/java/android/support/v4/os/ConfigurationCompat.java
new file mode 100644
index 0000000..95de506
--- /dev/null
+++ b/compat/java/android/support/v4/os/ConfigurationCompat.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import android.content.res.Configuration;
+
+/**
+ * Helper class which allows access to properties of {@link Configuration} in
+ * a backward compatible fashion.
+ */
+public final class ConfigurationCompat {
+    private ConfigurationCompat() {
+    }
+
+    /**
+     * Get the {@link LocaleListCompat} from the {@link Configuration}.
+     *
+     * @return The locale list.
+     */
+    public static LocaleListCompat getLocales(Configuration configuration) {
+        if (SDK_INT >= 24) {
+            return LocaleListCompat.wrap(configuration.getLocales());
+        } else {
+            return LocaleListCompat.create(configuration.locale);
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/os/EnvironmentCompat.java b/compat/java/android/support/v4/os/EnvironmentCompat.java
index 454065d..3e9e7f9 100644
--- a/compat/java/android/support/v4/os/EnvironmentCompat.java
+++ b/compat/java/android/support/v4/os/EnvironmentCompat.java
@@ -24,8 +24,7 @@
 import java.io.IOException;
 
 /**
- * Helper for accessing features in {@link Environment} introduced after API
- * level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link Environment}.
  */
 public final class EnvironmentCompat {
     private static final String TAG = "EnvironmentCompat";
@@ -53,9 +52,8 @@
      *         {@link Environment#MEDIA_UNMOUNTABLE}.
      */
     public static String getStorageState(File path) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            return EnvironmentCompatKitKat.getStorageState(path);
+        if (Build.VERSION.SDK_INT >= 19) {
+            return Environment.getStorageState(path);
         }
 
         try {
diff --git a/compat/java/android/support/v4/os/LocaleHelper.java b/compat/java/android/support/v4/os/LocaleHelper.java
new file mode 100644
index 0000000..8b933b9
--- /dev/null
+++ b/compat/java/android/support/v4/os/LocaleHelper.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.RestrictTo;
+
+import java.util.Locale;
+
+/**
+ * Helper to deal with new {@link Locale} APIs.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+final class LocaleHelper {
+
+    // Simpleton implementation for Locale.forLanguageTag(...)
+    static Locale forLanguageTag(String str) {
+        if (str.contains("-")) {
+            String[] args = str.split("-");
+            if (args.length > 2) {
+                return new Locale(args[0], args[1], args[2]);
+            } else if (args.length > 1) {
+                return new Locale(args[0], args[1]);
+            } else if (args.length == 1) {
+                return new Locale(args[0]);
+            }
+        } else if (str.contains("_")) {
+            String[] args = str.split("_");
+            if (args.length > 2) {
+                return new Locale(args[0], args[1], args[2]);
+            } else if (args.length > 1) {
+                return new Locale(args[0], args[1]);
+            } else if (args.length == 1) {
+                return new Locale(args[0]);
+            }
+        } else {
+            return new Locale(str);
+        }
+
+        throw new IllegalArgumentException("Can not parse language tag: [" + str + "]");
+    }
+
+    // Simpleton implementation for Locale.toLanguageTag(...)
+    static String toLanguageTag(Locale locale) {
+        StringBuilder buf = new StringBuilder();
+        buf.append(locale.getLanguage());
+        final String country = locale.getCountry();
+        if (country != null && !country.isEmpty()) {
+            buf.append("-");
+            buf.append(locale.getCountry());
+        }
+
+        return buf.toString();
+    }
+}
diff --git a/compat/java/android/support/v4/os/LocaleListCompat.java b/compat/java/android/support/v4/os/LocaleListCompat.java
new file mode 100644
index 0000000..9ccc35b
--- /dev/null
+++ b/compat/java/android/support/v4/os/LocaleListCompat.java
@@ -0,0 +1,367 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+import android.os.Build;
+import android.os.LocaleList;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.Size;
+
+import java.util.Locale;
+
+/**
+ * Helper for accessing features in {@link LocaleList}.
+ */
+public final class LocaleListCompat {
+    static final LocaleListInterface IMPL;
+    private static final LocaleListCompat sEmptyLocaleList = new LocaleListCompat();
+
+
+    static class LocaleListCompatBaseImpl implements LocaleListInterface {
+        private LocaleListHelper mLocaleList = new LocaleListHelper();
+
+        @Override
+        public void setLocaleList(@NonNull Locale... list) {
+            mLocaleList = new LocaleListHelper(list);
+        }
+
+        @Override
+        public Object getLocaleList() {
+            return mLocaleList;
+        }
+
+        @Override
+        public Locale get(int index) {
+            return mLocaleList.get(index);
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return mLocaleList.isEmpty();
+        }
+
+        @Override
+        @IntRange(from = 0)
+        public int size() {
+            return mLocaleList.size();
+        }
+
+        @Override
+        @IntRange(from = -1)
+        public int indexOf(Locale locale) {
+            return mLocaleList.indexOf(locale);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return mLocaleList.equals(((LocaleListCompat) other).unwrap());
+        }
+
+        @Override
+        public int hashCode() {
+            return mLocaleList.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return mLocaleList.toString();
+        }
+
+        @Override
+        public String toLanguageTags() {
+            return mLocaleList.toLanguageTags();
+        }
+
+        @Nullable
+        @Override
+        public Locale getFirstMatch(String[] supportedLocales) {
+            if (mLocaleList != null) {
+                return mLocaleList.getFirstMatch(supportedLocales);
+            }
+            return null;
+        }
+    }
+
+    @RequiresApi(24)
+    static class LocaleListCompatApi24Impl implements LocaleListInterface {
+        private LocaleList mLocaleList = new LocaleList();
+
+        @Override
+        public void setLocaleList(@NonNull Locale... list) {
+            mLocaleList = new LocaleList(list);
+        }
+
+        @Override
+        public Object getLocaleList() {
+            return mLocaleList;
+        }
+
+        @Override
+        public Locale get(int index) {
+            return mLocaleList.get(index);
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return mLocaleList.isEmpty();
+        }
+
+        @Override
+        @IntRange(from = 0)
+        public int size() {
+            return mLocaleList.size();
+        }
+
+        @Override
+        @IntRange(from = -1)
+        public int indexOf(Locale locale) {
+            return mLocaleList.indexOf(locale);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return mLocaleList.equals(((LocaleListCompat) other).unwrap());
+        }
+
+        @Override
+        public int hashCode() {
+            return mLocaleList.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return mLocaleList.toString();
+        }
+
+        @Override
+        public String toLanguageTags() {
+            return mLocaleList.toLanguageTags();
+        }
+
+        @Nullable
+        @Override
+        public Locale getFirstMatch(String[] supportedLocales) {
+            if (mLocaleList != null) {
+                return mLocaleList.getFirstMatch(supportedLocales);
+            }
+            return null;
+        }
+    }
+
+    static {
+        if (Build.VERSION.SDK_INT >= 24) {
+            IMPL = new LocaleListCompatApi24Impl();
+        } else {
+            IMPL = new LocaleListCompatBaseImpl();
+        }
+    }
+
+    private LocaleListCompat() {}
+
+    /**
+     * Creates a new instance of {@link LocaleListCompat} from the Locale list.
+     */
+    @RequiresApi(24)
+    public static LocaleListCompat wrap(Object object) {
+        LocaleListCompat instance = new LocaleListCompat();
+        if (object instanceof LocaleList) {
+            instance.setLocaleList((LocaleList) object);
+
+        }
+        return instance;
+    }
+
+    /**
+     * Gets the underlying framework object.
+     *
+     * @return an android.os.LocaleList object if API &gt;= 24 , or {@link Locale} if not.
+     */
+    @Nullable
+    public Object unwrap() {
+        return IMPL.getLocaleList();
+    }
+
+    /**
+     * Creates a new instance of {@link LocaleListCompat} from the {@link Locale} array.
+     */
+    public static LocaleListCompat create(@NonNull Locale... localeList) {
+        LocaleListCompat instance = new LocaleListCompat();
+        instance.setLocaleListArray(localeList);
+        return instance;
+    }
+
+    /**
+     * Retrieves the {@link Locale} at the specified index.
+     *
+     * @param index The position to retrieve.
+     * @return The {@link Locale} in the given index
+     */
+    public Locale get(int index) {
+        return IMPL.get(index);
+    }
+
+    /**
+     * Returns whether the {@link LocaleListCompat} contains no {@link Locale} items.
+     *
+     * @return {@code true} if this {@link LocaleListCompat} has no {@link Locale} items,
+     *         {@code false} otherwise
+     */
+    public boolean isEmpty() {
+        return IMPL.isEmpty();
+    }
+
+    /**
+     * Returns the number of {@link Locale} items in this {@link LocaleListCompat}.
+     */
+    @IntRange(from = 0)
+    public int size() {
+        return IMPL.size();
+    }
+
+    /**
+     * Searches this {@link LocaleListCompat} for the specified {@link Locale} and returns the
+     * index of the first occurrence.
+     *
+     * @param locale The {@link Locale} to search for.
+     * @return The index of the first occurrence of the {@link Locale} or {@code -1} if the item
+     *         wasn't found
+     */
+    @IntRange(from = -1)
+    public int indexOf(Locale locale) {
+        return IMPL.indexOf(locale);
+    }
+
+    /**
+     * Retrieves a String representation of the language tags in this list.
+     */
+    @NonNull
+    public String toLanguageTags() {
+        return IMPL.toLanguageTags();
+    }
+
+    /**
+     * Returns the first match in the locale list given an unordered array of supported locales
+     * in BCP 47 format.
+     *
+     * @return The first {@link Locale} from this list that appears in the given array, or
+     *         {@code null} if the {@link LocaleListCompat} is empty.
+     */
+    public Locale getFirstMatch(String[] supportedLocales) {
+        return IMPL.getFirstMatch(supportedLocales);
+    }
+
+    /**
+     * Retrieve an empty instance of {@link LocaleList}.
+     */
+    @NonNull
+    public static LocaleListCompat getEmptyLocaleList() {
+        return sEmptyLocaleList;
+    }
+
+    /**
+     * Generates a new LocaleList with the given language tags.
+     *
+     * <p>Note that for API < 24 only the first language tag will be used.</>
+     *
+     * @param list The language tags to be included as a single {@link String} separated by commas.
+     * @return A new instance with the {@link Locale} items identified by the given tags.
+     */
+    @NonNull
+    public static LocaleListCompat forLanguageTags(@Nullable String list) {
+        if (list == null || list.isEmpty()) {
+            return getEmptyLocaleList();
+        } else {
+            final String[] tags = list.split(",");
+            final Locale[] localeArray = new Locale[tags.length];
+            for (int i = 0; i < localeArray.length; i++) {
+                localeArray[i] = Build.VERSION.SDK_INT >= 21
+                        ? Locale.forLanguageTag(tags[i])
+                        : LocaleHelper.forLanguageTag(tags[i]);
+            }
+            LocaleListCompat instance = new LocaleListCompat();
+            instance.setLocaleListArray(localeArray);
+            return instance;
+        }
+    }
+
+    /**
+     * Returns the default locale list, adjusted by moving the default locale to its first
+     * position.
+     */
+    @NonNull @Size(min = 1)
+    public static LocaleListCompat getAdjustedDefault() {
+        if (Build.VERSION.SDK_INT >= 24) {
+            return LocaleListCompat.wrap(LocaleList.getAdjustedDefault());
+        } else {
+            return LocaleListCompat.create(Locale.getDefault());
+        }
+    }
+
+    /**
+     * The result is guaranteed to include the default Locale returned by Locale.getDefault(), but
+     * not necessarily at the top of the list. The default locale not being at the top of the list
+     * is an indication that the system has set the default locale to one of the user's other
+     * preferred locales, having concluded that the primary preference is not supported but a
+     * secondary preference is.
+     *
+     * <p>Note that for API &gt;= 24 the default LocaleList would change if Locale.setDefault() is
+     * called. This method takes that into account by always checking the output of
+     * Locale.getDefault() and recalculating the default LocaleList if needed.</p>
+     */
+    @NonNull @Size(min = 1)
+    public static LocaleListCompat getDefault() {
+        if (Build.VERSION.SDK_INT >= 24) {
+            return LocaleListCompat.wrap(LocaleList.getDefault());
+        } else {
+            return LocaleListCompat.create(Locale.getDefault());
+        }
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return IMPL.equals(other);
+    }
+
+    @Override
+    public int hashCode() {
+        return IMPL.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return IMPL.toString();
+    }
+
+    @RequiresApi(24)
+    private void setLocaleList(LocaleList localeList) {
+        final int localeListSize = localeList.size();
+        if (localeListSize > 0) {
+            Locale[] localeArrayList = new Locale[localeListSize];
+            for (int i = 0; i < localeListSize; i++) {
+                localeArrayList[i] = localeList.get(i);
+            }
+            IMPL.setLocaleList(localeArrayList);
+        }
+    }
+
+    private void setLocaleListArray(Locale... localeArrayList) {
+        IMPL.setLocaleList(localeArrayList);
+    }
+}
diff --git a/compat/java/android/support/v4/os/LocaleListHelper.java b/compat/java/android/support/v4/os/LocaleListHelper.java
new file mode 100644
index 0000000..cfb24fb
--- /dev/null
+++ b/compat/java/android/support/v4/os/LocaleListHelper.java
@@ -0,0 +1,584 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.os.Build;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.Size;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Locale;
+
+/**
+ * LocaleListHelper is an immutable list of Locales, typically used to keep an ordered list of user
+ * preferences for locales.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(14)
+final class LocaleListHelper {
+    private final Locale[] mList;
+    // This is a comma-separated list of the locales in the LocaleListHelper created at construction
+    // time, basically the result of running each locale's toLanguageTag() method and concatenating
+    // them with commas in between.
+    @NonNull
+    private final String mStringRepresentation;
+
+    private static final Locale[] sEmptyList = new Locale[0];
+    private static final LocaleListHelper sEmptyLocaleList = new LocaleListHelper();
+
+    /**
+     * Retrieves the {@link Locale} at the specified index.
+     *
+     * @param index The position to retrieve.
+     * @return The {@link Locale} in the given index.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    Locale get(int index) {
+        return (0 <= index && index < mList.length) ? mList[index] : null;
+    }
+
+    /**
+     * Returns whether the {@link LocaleListHelper} contains no {@link Locale} items.
+     *
+     * @return {@code true} if this {@link LocaleListHelper} has no {@link Locale} items,
+     *         {@code false} otherwise.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    boolean isEmpty() {
+        return mList.length == 0;
+    }
+
+    /**
+     * Returns the number of {@link Locale} items in this {@link LocaleListHelper}.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntRange(from = 0)
+    int size() {
+        return mList.length;
+    }
+
+    /**
+     * Searches this {@link LocaleListHelper} for the specified {@link Locale} and returns the index
+     * of the first occurrence.
+     *
+     * @param locale The {@link Locale} to search for.
+     * @return The index of the first occurrence of the {@link Locale} or {@code -1} if the item
+     *     wasn't found.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntRange(from = -1)
+    int indexOf(Locale locale) {
+        for (int i = 0; i < mList.length; i++) {
+            if (mList[i].equals(locale)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (!(other instanceof LocaleListHelper)) {
+            return false;
+        }
+        final Locale[] otherList = ((LocaleListHelper) other).mList;
+        if (mList.length != otherList.length) {
+            return false;
+        }
+        for (int i = 0; i < mList.length; i++) {
+            if (!mList[i].equals(otherList[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        for (int i = 0; i < mList.length; i++) {
+            result = 31 * result + mList[i].hashCode();
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        for (int i = 0; i < mList.length; i++) {
+            sb.append(mList[i]);
+            if (i < mList.length - 1) {
+                sb.append(',');
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    /**
+     * Retrieves a String representation of the language tags in this list.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @NonNull
+    String toLanguageTags() {
+        return mStringRepresentation;
+    }
+
+    /**
+     * Creates a new {@link LocaleListHelper}.
+     *
+     * <p>For empty lists of {@link Locale} items it is better to use {@link #getEmptyLocaleList()},
+     * which returns a pre-constructed empty list.</p>
+     *
+     * @throws NullPointerException if any of the input locales is <code>null</code>.
+     * @throws IllegalArgumentException if any of the input locales repeat.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    LocaleListHelper(@NonNull Locale... list) {
+        if (list.length == 0) {
+            mList = sEmptyList;
+            mStringRepresentation = "";
+        } else {
+            final Locale[] localeList = new Locale[list.length];
+            final HashSet<Locale> seenLocales = new HashSet<Locale>();
+            final StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < list.length; i++) {
+                final Locale l = list[i];
+                if (l == null) {
+                    throw new NullPointerException("list[" + i + "] is null");
+                } else if (seenLocales.contains(l)) {
+                    throw new IllegalArgumentException("list[" + i + "] is a repetition");
+                } else {
+                    final Locale localeClone = (Locale) l.clone();
+                    localeList[i] = localeClone;
+                    sb.append(LocaleHelper.toLanguageTag(localeClone));
+                    if (i < list.length - 1) {
+                        sb.append(',');
+                    }
+                    seenLocales.add(localeClone);
+                }
+            }
+            mList = localeList;
+            mStringRepresentation = sb.toString();
+        }
+    }
+
+    /**
+     * Constructs a locale list, with the topLocale moved to the front if it already is
+     * in otherLocales, or added to the front if it isn't.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    LocaleListHelper(@NonNull Locale topLocale, LocaleListHelper otherLocales) {
+        if (topLocale == null) {
+            throw new NullPointerException("topLocale is null");
+        }
+
+        final int inputLength = (otherLocales == null) ? 0 : otherLocales.mList.length;
+        int topLocaleIndex = -1;
+        for (int i = 0; i < inputLength; i++) {
+            if (topLocale.equals(otherLocales.mList[i])) {
+                topLocaleIndex = i;
+                break;
+            }
+        }
+
+        final int outputLength = inputLength + (topLocaleIndex == -1 ? 1 : 0);
+        final Locale[] localeList = new Locale[outputLength];
+        localeList[0] = (Locale) topLocale.clone();
+        if (topLocaleIndex == -1) {
+            // topLocale was not in otherLocales
+            for (int i = 0; i < inputLength; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+        } else {
+            for (int i = 0; i < topLocaleIndex; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+            for (int i = topLocaleIndex + 1; i < inputLength; i++) {
+                localeList[i] = (Locale) otherLocales.mList[i].clone();
+            }
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < outputLength; i++) {
+            sb.append(LocaleHelper.toLanguageTag(localeList[i]));
+
+            if (i < outputLength - 1) {
+                sb.append(',');
+            }
+        }
+
+        mList = localeList;
+        mStringRepresentation = sb.toString();
+    }
+
+    /**
+     * Retrieve an empty instance of {@link LocaleListHelper}.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @NonNull
+    static LocaleListHelper getEmptyLocaleList() {
+        return sEmptyLocaleList;
+    }
+
+    /**
+     * Generates a new LocaleListHelper with the given language tags.
+     *
+     * @param list The language tags to be included as a single {@link String} separated by commas.
+     * @return A new instance with the {@link Locale} items identified by the given tags.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @NonNull
+    static LocaleListHelper forLanguageTags(@Nullable String list) {
+        if (list == null || list.isEmpty()) {
+            return getEmptyLocaleList();
+        } else {
+            final String[] tags = list.split(",");
+            final Locale[] localeArray = new Locale[tags.length];
+            for (int i = 0; i < localeArray.length; i++) {
+                localeArray[i] = LocaleHelper.forLanguageTag(tags[i]);
+            }
+            return new LocaleListHelper(localeArray);
+        }
+    }
+
+    private static String getLikelyScript(Locale locale) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            final String script = locale.getScript();
+            if (!script.isEmpty()) {
+                return script;
+            } else {
+                return "";
+            }
+        }
+        return "";
+    }
+
+    private static final String STRING_EN_XA = "en-XA";
+    private static final String STRING_AR_XB = "ar-XB";
+    private static final Locale LOCALE_EN_XA = new Locale("en", "XA");
+    private static final Locale LOCALE_AR_XB = new Locale("ar", "XB");
+    private static final int NUM_PSEUDO_LOCALES = 2;
+
+    private static boolean isPseudoLocale(String locale) {
+        return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale);
+    }
+
+    private static boolean isPseudoLocale(Locale locale) {
+        return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
+    }
+
+    @IntRange(from = 0, to = 1)
+    private static int matchScore(Locale supported, Locale desired) {
+        if (supported.equals(desired)) {
+            return 1;  // return early so we don't do unnecessary computation
+        }
+        if (!supported.getLanguage().equals(desired.getLanguage())) {
+            return 0;
+        }
+        if (isPseudoLocale(supported) || isPseudoLocale(desired)) {
+            // The locales are not the same, but the languages are the same, and one of the locales
+            // is a pseudo-locale. So this is not a match.
+            return 0;
+        }
+        final String supportedScr = getLikelyScript(supported);
+        if (supportedScr.isEmpty()) {
+            // If we can't guess a script, we don't know enough about the locales' language to find
+            // if the locales match. So we fall back to old behavior of matching, which considered
+            // locales with different regions different.
+            final String supportedRegion = supported.getCountry();
+            return (supportedRegion.isEmpty() || supportedRegion.equals(desired.getCountry()))
+                    ? 1
+                    : 0;
+        }
+        final String desiredScr = getLikelyScript(desired);
+        // There is no match if the two locales use different scripts. This will most imporantly
+        // take care of traditional vs simplified Chinese.
+        return supportedScr.equals(desiredScr) ? 1 : 0;
+    }
+
+    private int findFirstMatchIndex(Locale supportedLocale) {
+        for (int idx = 0; idx < mList.length; idx++) {
+            final int score = matchScore(supportedLocale, mList[idx]);
+            if (score > 0) {
+                return idx;
+            }
+        }
+        return Integer.MAX_VALUE;
+    }
+
+    private static final Locale EN_LATN = LocaleHelper.forLanguageTag("en-Latn");
+
+    private int computeFirstMatchIndex(Collection<String> supportedLocales,
+            boolean assumeEnglishIsSupported) {
+        if (mList.length == 1) {  // just one locale, perhaps the most common scenario
+            return 0;
+        }
+        if (mList.length == 0) {  // empty locale list
+            return -1;
+        }
+
+        int bestIndex = Integer.MAX_VALUE;
+        // Try English first, so we can return early if it's in the LocaleListHelper
+        if (assumeEnglishIsSupported) {
+            final int idx = findFirstMatchIndex(EN_LATN);
+            if (idx == 0) { // We have a match on the first locale, which is good enough
+                return 0;
+            } else if (idx < bestIndex) {
+                bestIndex = idx;
+            }
+        }
+        for (String languageTag : supportedLocales) {
+            final Locale supportedLocale = LocaleHelper.forLanguageTag(languageTag);
+            // We expect the average length of locale lists used for locale resolution to be
+            // smaller than three, so it's OK to do this as an O(mn) algorithm.
+            final int idx = findFirstMatchIndex(supportedLocale);
+            if (idx == 0) { // We have a match on the first locale, which is good enough
+                return 0;
+            } else if (idx < bestIndex) {
+                bestIndex = idx;
+            }
+        }
+        if (bestIndex == Integer.MAX_VALUE) {
+            // no match was found, so we fall back to the first locale in the locale list
+            return 0;
+        } else {
+            return bestIndex;
+        }
+    }
+
+    private Locale computeFirstMatch(Collection<String> supportedLocales,
+            boolean assumeEnglishIsSupported) {
+        int bestIndex = computeFirstMatchIndex(supportedLocales, assumeEnglishIsSupported);
+        return bestIndex == -1 ? null : mList[bestIndex];
+    }
+
+    /**
+     * Returns the first match in the locale list given an unordered array of supported locales
+     * in BCP 47 format.
+     *
+     * @return The first {@link Locale} from this list that appears in the given array, or
+     *     {@code null} if the {@link LocaleListHelper} is empty.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Nullable
+    Locale getFirstMatch(String[] supportedLocales) {
+        return computeFirstMatch(Arrays.asList(supportedLocales),
+                false /* assume English is not supported */);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getFirstMatchIndex(String[] supportedLocales) {
+        return computeFirstMatchIndex(Arrays.asList(supportedLocales),
+                false /* assume English is not supported */);
+    }
+
+    /**
+     * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Nullable
+    Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) {
+        return computeFirstMatch(Arrays.asList(supportedLocales),
+                true /* assume English is supported */);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getFirstMatchIndexWithEnglishSupported(Collection<String> supportedLocales) {
+        return computeFirstMatchIndex(supportedLocales, true /* assume English is supported */);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getFirstMatchIndexWithEnglishSupported(String[] supportedLocales) {
+        return getFirstMatchIndexWithEnglishSupported(Arrays.asList(supportedLocales));
+    }
+
+    /**
+     * Returns true if the collection of locale tags only contains empty locales and pseudolocales.
+     * Assumes that there is no repetition in the input.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    static boolean isPseudoLocalesOnly(@Nullable String[] supportedLocales) {
+        if (supportedLocales == null) {
+            return true;
+        }
+
+        if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
+            // This is for optimization. Since there's no repetition in the input, if we have more
+            // than the number of pseudo-locales plus one for the empty string, it's guaranteed
+            // that we have some meaninful locale in the collection, so the list is not "practically
+            // empty".
+            return false;
+        }
+        for (String locale : supportedLocales) {
+            if (!locale.isEmpty() && !isPseudoLocale(locale)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Lock for mutable static fields */
+    private final static Object sLock = new Object();
+
+    @GuardedBy("sLock")
+    private static LocaleListHelper sLastExplicitlySetLocaleList = null;
+    @GuardedBy("sLock")
+    private static LocaleListHelper sDefaultLocaleList = null;
+    @GuardedBy("sLock")
+    private static LocaleListHelper sDefaultAdjustedLocaleList = null;
+    @GuardedBy("sLock")
+    private static Locale sLastDefaultLocale = null;
+
+    /**
+     * The result is guaranteed to include the default Locale returned by Locale.getDefault(), but
+     * not necessarily at the top of the list. The default locale not being at the top of the list
+     * is an indication that the system has set the default locale to one of the user's other
+     * preferred locales, having concluded that the primary preference is not supported but a
+     * secondary preference is.
+     *
+     * <p>Note that the default LocaleListHelper would change if Locale.setDefault() is called. This
+     * method takes that into account by always checking the output of Locale.getDefault() and
+     * recalculating the default LocaleListHelper if needed.</p>
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @NonNull @Size(min = 1)
+    static LocaleListHelper getDefault() {
+        final Locale defaultLocale = Locale.getDefault();
+        synchronized (sLock) {
+            if (!defaultLocale.equals(sLastDefaultLocale)) {
+                sLastDefaultLocale = defaultLocale;
+                // It's either the first time someone has asked for the default locale list, or
+                // someone has called Locale.setDefault() since we last set or adjusted the default
+                // locale list. So let's recalculate the locale list.
+                if (sDefaultLocaleList != null
+                        && defaultLocale.equals(sDefaultLocaleList.get(0))) {
+                    // The default Locale has changed, but it happens to be the first locale in the
+                    // default locale list, so we don't need to construct a new locale list.
+                    return sDefaultLocaleList;
+                }
+                sDefaultLocaleList = new LocaleListHelper(
+                        defaultLocale, sLastExplicitlySetLocaleList);
+                sDefaultAdjustedLocaleList = sDefaultLocaleList;
+            }
+            // sDefaultLocaleList can't be null, since it can't be set to null by
+            // LocaleListHelper.setDefault(), and if getDefault() is called before a call to
+            // setDefault(), sLastDefaultLocale would be null and the check above would set
+            // sDefaultLocaleList.
+            return sDefaultLocaleList;
+        }
+    }
+
+    /**
+     * Returns the default locale list, adjusted by moving the default locale to its first
+     * position.
+     */
+    @NonNull @Size(min = 1)
+    static LocaleListHelper getAdjustedDefault() {
+        getDefault(); // to recalculate the default locale list, if necessary
+        synchronized (sLock) {
+            return sDefaultAdjustedLocaleList;
+        }
+    }
+
+    /**
+     * Also sets the default locale by calling Locale.setDefault() with the first locale in the
+     * list.
+     *
+     * @throws NullPointerException if the input is <code>null</code>.
+     * @throws IllegalArgumentException if the input is empty.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    static void setDefault(@NonNull @Size(min = 1) LocaleListHelper locales) {
+        setDefault(locales, 0);
+    }
+
+    /**
+     * This may be used directly by system processes to set the default locale list for apps. For
+     * such uses, the default locale list would always come from the user preferences, but the
+     * default locale may have been chosen to be a locale other than the first locale in the locale
+     * list (based on the locales the app supports).
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    static void setDefault(@NonNull @Size(min = 1) LocaleListHelper locales,
+            int localeIndex) {
+        if (locales == null) {
+            throw new NullPointerException("locales is null");
+        }
+        if (locales.isEmpty()) {
+            throw new IllegalArgumentException("locales is empty");
+        }
+        synchronized (sLock) {
+            sLastDefaultLocale = locales.get(localeIndex);
+            Locale.setDefault(sLastDefaultLocale);
+            sLastExplicitlySetLocaleList = locales;
+            sDefaultLocaleList = locales;
+            if (localeIndex == 0) {
+                sDefaultAdjustedLocaleList = sDefaultLocaleList;
+            } else {
+                sDefaultAdjustedLocaleList = new LocaleListHelper(
+                        sLastDefaultLocale, sDefaultLocaleList);
+            }
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/os/LocaleListInterface.java b/compat/java/android/support/v4/os/LocaleListInterface.java
new file mode 100644
index 0000000..ce310e6
--- /dev/null
+++ b/compat/java/android/support/v4/os/LocaleListInterface.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.Locale;
+
+/**
+ * Interface describing backwards-compatible LocaleList APIs.
+ *
+ * @hide Internal use only
+ */
+@RestrictTo(LIBRARY_GROUP)
+interface LocaleListInterface {
+    void setLocaleList(@NonNull Locale... list);
+
+    Object getLocaleList();
+
+    Locale get(int index);
+
+    boolean isEmpty();
+
+    @IntRange(from = 0)
+    int size();
+
+    @IntRange(from = -1)
+    int indexOf(Locale locale);
+
+    @Override
+    boolean equals(Object other);
+
+    @Override
+    int hashCode();
+
+    @Override
+    String toString();
+
+    String toLanguageTags();
+
+    @Nullable
+    Locale getFirstMatch(String[] supportedLocales);
+}
diff --git a/compat/java/android/support/v4/os/ParcelableCompat.java b/compat/java/android/support/v4/os/ParcelableCompat.java
index 10c03b5..ee83122 100644
--- a/compat/java/android/support/v4/os/ParcelableCompat.java
+++ b/compat/java/android/support/v4/os/ParcelableCompat.java
@@ -20,9 +20,11 @@
 import android.os.Parcelable;
 
 /**
- * Helper for accessing features in {@link android.os.Parcelable}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.os.Parcelable}.
+ *
+ * @deprecated Use {@link android.os.Parcelable.ClassLoaderCreator} directly.
  */
+@Deprecated
 public final class ParcelableCompat {
 
     /**
@@ -30,25 +32,31 @@
      *
      * @param callbacks Creator callbacks implementation.
      * @return New creator.
+     *
+     * @deprecated Use {@link android.os.Parcelable.ClassLoaderCreator} directly.
      */
+    @Deprecated
     public static <T> Parcelable.Creator<T> newCreator(
             ParcelableCompatCreatorCallbacks<T> callbacks) {
-        if (android.os.Build.VERSION.SDK_INT >= 13) {
-            return ParcelableCompatCreatorHoneycombMR2Stub.instantiate(callbacks);
-        }
-        return new CompatCreator<T>(callbacks);
+        return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);
     }
 
-    static class CompatCreator<T> implements Parcelable.Creator<T> {
-        final ParcelableCompatCreatorCallbacks<T> mCallbacks;
+    static class ParcelableCompatCreatorHoneycombMR2<T>
+            implements Parcelable.ClassLoaderCreator<T> {
+        private final ParcelableCompatCreatorCallbacks<T> mCallbacks;
 
-        public CompatCreator(ParcelableCompatCreatorCallbacks<T> callbacks) {
+        ParcelableCompatCreatorHoneycombMR2(ParcelableCompatCreatorCallbacks<T> callbacks) {
             mCallbacks = callbacks;
         }
 
         @Override
-        public T createFromParcel(Parcel source) {
-            return mCallbacks.createFromParcel(source, null);
+        public T createFromParcel(Parcel in) {
+            return mCallbacks.createFromParcel(in, null);
+        }
+
+        @Override
+        public T createFromParcel(Parcel in, ClassLoader loader) {
+            return mCallbacks.createFromParcel(in, loader);
         }
 
         @Override
diff --git a/compat/java/android/support/v4/os/ParcelableCompatCreatorCallbacks.java b/compat/java/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
new file mode 100644
index 0000000..e5e8a3a
--- /dev/null
+++ b/compat/java/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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.os;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Callbacks a {@link Parcelable} creator should implement.
+ *
+ * @deprecated Use {@link android.os.Parcelable.ClassLoaderCreator} directly.
+ */
+@Deprecated
+public interface ParcelableCompatCreatorCallbacks<T> {
+
+    /**
+     * Create a new instance of the Parcelable class, instantiating it
+     * from the given Parcel whose data had previously been written by
+     * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
+     * using the given ClassLoader.
+     *
+     * @param in The Parcel to read the object's data from.
+     * @param loader The ClassLoader that this object is being created in.
+     * @return Returns a new instance of the Parcelable class.
+     */
+    T createFromParcel(Parcel in, ClassLoader loader);
+
+    /**
+     * Create a new array of the Parcelable class.
+     *
+     * @param size Size of the array.
+     * @return Returns an array of the Parcelable class, with every entry
+     *         initialized to null.
+     */
+    T[] newArray(int size);
+}
diff --git a/compat/java/android/support/v4/os/TraceCompat.java b/compat/java/android/support/v4/os/TraceCompat.java
index a8b62ab..7badc62 100644
--- a/compat/java/android/support/v4/os/TraceCompat.java
+++ b/compat/java/android/support/v4/os/TraceCompat.java
@@ -14,6 +14,7 @@
 package android.support.v4.os;
 
 import android.os.Build;
+import android.os.Trace;
 
 /**
  * Writes trace events to the system trace buffer.  These trace events can be
@@ -41,7 +42,7 @@
 
     public static void beginSection(String sectionName) {
         if (Build.VERSION.SDK_INT >= 18) {
-            TraceJellybeanMR2.beginSection(sectionName);
+            Trace.beginSection(sectionName);
         }
     }
 
@@ -54,7 +55,7 @@
      */
     public static void endSection() {
         if (Build.VERSION.SDK_INT >= 18) {
-            TraceJellybeanMR2.endSection();
+            Trace.endSection();
         }
     }
 
diff --git a/compat/java/android/support/v4/os/UserManagerCompat.java b/compat/java/android/support/v4/os/UserManagerCompat.java
index 409fb15..0a82a5d 100644
--- a/compat/java/android/support/v4/os/UserManagerCompat.java
+++ b/compat/java/android/support/v4/os/UserManagerCompat.java
@@ -17,12 +17,15 @@
 package android.support.v4.os;
 
 import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
 
 /**
- * Helper for accessing features in {@link android.os.UserManager}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.os.UserManager} in a backwards compatible
+ * fashion.
  */
 public class UserManagerCompat {
+
     private UserManagerCompat() {
     }
 
@@ -33,10 +36,11 @@
      * available.
      */
     public static boolean isUserUnlocked(Context context) {
-        if (BuildCompat.isAtLeastN()) {
-            return UserManagerCompatApi24.isUserUnlocked(context);
+        if (Build.VERSION.SDK_INT >= 24) {
+            return context.getSystemService(UserManager.class).isUserUnlocked();
         } else {
             return true;
         }
     }
+
 }
diff --git a/compat/java/android/support/v4/provider/FontRequest.java b/compat/java/android/support/v4/provider/FontRequest.java
new file mode 100644
index 0000000..cb32f06
--- /dev/null
+++ b/compat/java/android/support/v4/provider/FontRequest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.provider;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.ArrayRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.util.Preconditions;
+import android.util.Base64;
+
+import java.util.List;
+
+/**
+ * Information about a font request that may be sent to a Font Provider.
+ */
+public final class FontRequest {
+    private final String mProviderAuthority;
+    private final String mProviderPackage;
+    private final String mQuery;
+    private final List<List<byte[]>> mCertificates;
+    private final int mCertificatesArray;
+
+    // Used for key of the cache
+    private final String mIdentifier;
+
+    /**
+     * @param providerAuthority The authority of the Font Provider to be used for the request.
+     * @param query The query to be sent over to the provider. Refer to your font provider's
+     *         documentation on the format of this string.
+     * @param providerPackage The package for the Font Provider to be used for the request. This is
+     *         used to verify the identity of the provider.
+     * @param certificates The list of sets of hashes for the certificates the provider should be
+     *         signed with. This is used to verify the identity of the provider. Each set in the
+     *         list represents one collection of signature hashes. Refer to your font provider's
+     *         documentation for these values.
+     */
+    public FontRequest(@NonNull String providerAuthority, @NonNull String providerPackage,
+            @NonNull String query, @NonNull List<List<byte[]>> certificates) {
+        mProviderAuthority = Preconditions.checkNotNull(providerAuthority);
+        mProviderPackage = Preconditions.checkNotNull(providerPackage);
+        mQuery = Preconditions.checkNotNull(query);
+        mCertificates = Preconditions.checkNotNull(certificates);
+        mCertificatesArray = 0;
+        mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage)
+                .append("-").append(mQuery).toString();
+    }
+
+    /**
+     * @param providerAuthority The authority of the Font Provider to be used for the request.
+     * @param query The query to be sent over to the provider. Refer to your font provider's
+     *         documentation on the format of this string.
+     * @param providerPackage The package for the Font Provider to be used for the request. This is
+     *         used to verify the identity of the provider.
+     * @param certificates A resource array with the list of sets of hashes for the certificates the
+     *         provider should be signed with. This is used to verify the identity of the provider.
+     *         Each set in the list represents one collection of signature hashes. Refer to your
+     *         font provider's documentation for these values.
+     */
+    public FontRequest(@NonNull String providerAuthority, @NonNull String providerPackage,
+            @NonNull String query, @ArrayRes int certificates) {
+        mProviderAuthority = Preconditions.checkNotNull(providerAuthority);
+        mProviderPackage = Preconditions.checkNotNull(providerPackage);
+        mQuery = Preconditions.checkNotNull(query);
+        mCertificates = null;
+        Preconditions.checkArgument(certificates != 0);
+        mCertificatesArray = certificates;
+        mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage)
+                .append("-").append(mQuery).toString();
+    }
+
+    /**
+     * Returns the selected font provider's authority. This tells the system what font provider
+     * it should request the font from.
+     */
+    public String getProviderAuthority() {
+        return mProviderAuthority;
+    }
+
+    /**
+     * Returns the selected font provider's package. This helps the system verify that the provider
+     * identified by the given authority is the one requested.
+     */
+    public String getProviderPackage() {
+        return mProviderPackage;
+    }
+
+    /**
+     * Returns the query string. Refer to your font provider's documentation on the format of this
+     * string.
+     */
+    public String getQuery() {
+        return mQuery;
+    }
+
+    /**
+     * Returns the list of certificate sets given for this provider. This helps the system verify
+     * that the provider identified by the given authority is the one requested. Note this might
+     * be null if the certificates were provided via a resource id.
+     *
+     * @see #getCertificatesArrayResId()
+     */
+    @Nullable
+    public List<List<byte[]>> getCertificates() {
+        return mCertificates;
+    }
+
+    /**
+     * Returns the array resource id pointing to the certificate sets given for this provider. This
+     * helps the system verify that the provider identified by the given authority is the one
+     * requested. Note that this may be 0 if the certificates were provided as a list.
+     *
+     * @see #getCertificates()
+     */
+    @ArrayRes
+    public int getCertificatesArrayResId() {
+        return mCertificatesArray;
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    public String getIdentifier() {
+        return mIdentifier;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("FontRequest {"
+                + "mProviderAuthority: " + mProviderAuthority
+                + ", mProviderPackage: " + mProviderPackage
+                + ", mQuery: " + mQuery
+                + ", mCertificates:");
+        for (int i = 0; i < mCertificates.size(); i++) {
+            builder.append(" [");
+            List<byte[]> set = mCertificates.get(i);
+            for (int j = 0; j < set.size(); j++) {
+                builder.append(" \"");
+                byte[] array = set.get(j);
+                builder.append(Base64.encodeToString(array, Base64.DEFAULT));
+                builder.append("\"");
+            }
+            builder.append(" ]");
+        }
+        builder.append("}");
+        builder.append("mCertificatesArray: " + mCertificatesArray);
+        return builder.toString();
+    }
+}
diff --git a/compat/java/android/support/v4/provider/FontsContractCompat.java b/compat/java/android/support/v4/provider/FontsContractCompat.java
new file mode 100644
index 0000000..6ad46a1
--- /dev/null
+++ b/compat/java/android/support/v4/provider/FontsContractCompat.java
@@ -0,0 +1,818 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.provider;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static android.support.v4.content.res.FontResourcesParserCompat.FetchStrategy;
+
+import android.annotation.SuppressLint;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.provider.BaseColumns;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.res.FontResourcesParserCompat;
+import android.support.v4.graphics.TypefaceCompat;
+import android.support.v4.graphics.TypefaceCompatUtil;
+import android.support.v4.provider.SelfDestructiveThread.ReplyCallback;
+import android.support.v4.util.LruCache;
+import android.support.v4.util.Preconditions;
+import android.support.v4.util.SimpleArrayMap;
+import android.widget.TextView;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Utility class to deal with Font ContentProviders.
+ */
+public class FontsContractCompat {
+    private static final String TAG = "FontsContractCompat";
+
+    private FontsContractCompat() { }
+
+    /**
+     * Defines the constants used in a response from a Font Provider. The cursor returned from the
+     * query should have the ID column populated with the content uri ID for the resulting font.
+     * This should point to a real file or shared memory, as the client will mmap the given file
+     * descriptor. Pipes, sockets and other non-mmap-able file descriptors will fail to load in the
+     * client application.
+     */
+    public static final class Columns implements BaseColumns {
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * may populate this column with a long for the font file ID. The client will request a file
+         * descriptor to "file/FILE_ID" with this ID immediately under the top-level content URI. If
+         * not present, the client will request a file descriptor to the top-level URI with the
+         * given base font ID. Note that several results may return the same file ID, e.g. for TTC
+         * files with different indices.
+         */
+        public static final String FILE_ID = "file_id";
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * should have this column populated with an int for the ttc index for the resulting font.
+         */
+        public static final String TTC_INDEX = android.provider.FontsContract.Columns.TTC_INDEX;
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * may populate this column with the font variation settings String information for the
+         * font.
+         */
+        public static final String VARIATION_SETTINGS =
+                android.provider.FontsContract.Columns.VARIATION_SETTINGS;
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * should have this column populated with the int weight for the resulting font. This value
+         * should be between 100 and 900. The most common values are 400 for regular weight and 700
+         * for bold weight.
+         */
+        public static final String WEIGHT = android.provider.FontsContract.Columns.WEIGHT;
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * should have this column populated with the int italic for the resulting font. This should
+         * be 0 for regular style and 1 for italic.
+         */
+        public static final String ITALIC = android.provider.FontsContract.Columns.ITALIC;
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * should have this column populated to indicate the result status of the
+         * query. This will be checked before any other data in the cursor. Possible values are
+         * {@link #RESULT_CODE_OK}, {@link #RESULT_CODE_FONT_NOT_FOUND},
+         * {@link #RESULT_CODE_MALFORMED_QUERY} and {@link #RESULT_CODE_FONT_UNAVAILABLE}. If not
+         * present, {@link #RESULT_CODE_OK} will be assumed.
+         */
+        public static final String RESULT_CODE = android.provider.FontsContract.Columns.RESULT_CODE;
+
+        /**
+         * Constant used to represent a result was retrieved successfully. The given fonts will be
+         * attempted to retrieve immediately via
+         * {@link android.content.ContentProvider#openFile(Uri, String)}. See {@link #RESULT_CODE}.
+         */
+        public static final int RESULT_CODE_OK =
+                android.provider.FontsContract.Columns.RESULT_CODE_OK;
+        /**
+         * Constant used to represent a result was not found. See {@link #RESULT_CODE}.
+         */
+        public static final int RESULT_CODE_FONT_NOT_FOUND =
+                android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
+        /**
+         * Constant used to represent a result was found, but cannot be provided at this moment. Use
+         * this to indicate, for example, that a font needs to be fetched from the network. See
+         * {@link #RESULT_CODE}.
+         */
+        public static final int RESULT_CODE_FONT_UNAVAILABLE =
+                android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
+        /**
+         * Constant used to represent that the query was not in a supported format by the provider.
+         * See {@link #RESULT_CODE}.
+         */
+        public static final int RESULT_CODE_MALFORMED_QUERY =
+                android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
+    }
+
+    /**
+     * Constant used to identify the List of {@link ParcelFileDescriptor} item in the Bundle
+     * returned to the ResultReceiver in getFont.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARCEL_FONT_RESULTS = "font_results";
+
+    // Error codes internal to the system, which can not come from a provider. To keep the number
+    // space open for new provider codes, these should all be negative numbers.
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final int RESULT_CODE_PROVIDER_NOT_FOUND = -1;
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final int RESULT_CODE_WRONG_CERTIFICATES = -2;
+    // Note -3 is used by Typeface to indicate the font failed to load.
+
+    private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
+
+    private static final int BACKGROUND_THREAD_KEEP_ALIVE_DURATION_MS = 10000;
+    private static final SelfDestructiveThread sBackgroundThread =
+            new SelfDestructiveThread("fonts", Process.THREAD_PRIORITY_BACKGROUND,
+                    BACKGROUND_THREAD_KEEP_ALIVE_DURATION_MS);
+
+    private static Typeface getFontInternal(final Context context, final FontRequest request,
+            int style) {
+        FontFamilyResult result;
+        try {
+            result = fetchFonts(context, null /* CancellationSignal */, request);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+        if (result.getStatusCode() == FontFamilyResult.STATUS_OK) {
+            return TypefaceCompat.createFromFontInfo(context, null /* CancellationSignal */,
+                    result.getFonts(), style);
+        }
+        return null;
+    }
+
+    private static final Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static final SimpleArrayMap<String, ArrayList<ReplyCallback<Typeface>>>
+            sPendingReplies = new SimpleArrayMap<>();
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    public static Typeface getFontSync(final Context context, final FontRequest request,
+            final @Nullable TextView targetView, @FetchStrategy int strategy, int timeout,
+            final int style) {
+        final String id = request.getIdentifier() + "-" + style;
+        Typeface cached = sTypefaceCache.get(id);
+        if (cached != null) {
+            return cached;
+        }
+
+        final boolean isBlockingFetch =
+                strategy == FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING;
+
+        if (isBlockingFetch && timeout == FontResourcesParserCompat.INFINITE_TIMEOUT_VALUE) {
+            // Wait forever. No need to post to the thread.
+            return getFontInternal(context, request, style);
+        }
+
+        final Callable<Typeface> fetcher = new Callable<Typeface>() {
+            @Override
+            public Typeface call() throws Exception {
+                Typeface typeface = getFontInternal(context, request, style);
+                if (typeface != null) {
+                    sTypefaceCache.put(id, typeface);
+                }
+                return typeface;
+            }
+        };
+
+        if (isBlockingFetch) {
+            try {
+                return sBackgroundThread.postAndWait(fetcher, timeout);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        } else {
+            final WeakReference<TextView> textViewWeak = new WeakReference<TextView>(targetView);
+            final ReplyCallback<Typeface> reply = new ReplyCallback<Typeface>() {
+                @Override
+                public void onReply(final Typeface typeface) {
+                    final TextView textView = textViewWeak.get();
+                    if (textView != null) {
+                        targetView.setTypeface(typeface, style);
+                    }
+                }
+            };
+
+            synchronized (sLock) {
+                if (sPendingReplies.containsKey(id)) {
+                    // Already requested. Do not request the same provider again and insert the
+                    // reply to the queue instead.
+                    sPendingReplies.get(id).add(reply);
+                    return null;
+                }
+                ArrayList<ReplyCallback<Typeface>> pendingReplies = new ArrayList<>();
+                pendingReplies.add(reply);
+                sPendingReplies.put(id, pendingReplies);
+            }
+            sBackgroundThread.postAndReply(fetcher, new ReplyCallback<Typeface>() {
+                @Override
+                public void onReply(final Typeface typeface) {
+                    final ArrayList<ReplyCallback<Typeface>> replies;
+                    synchronized (sLock) {
+                        replies = sPendingReplies.get(id);
+                        sPendingReplies.remove(id);
+                    }
+                    for (int i = 0; i < replies.size(); ++i) {
+                        replies.get(i).onReply(typeface);
+                    }
+                };
+            });
+            return null;
+        }
+    }
+
+    /**
+     * Object represent a font entry in the family returned from {@link #fetchFonts}.
+     */
+    public static class FontInfo {
+        private final Uri mUri;
+        private final int mTtcIndex;
+        private final int mWeight;
+        private final boolean mItalic;
+        private final int mResultCode;
+
+        /**
+         * Creates a Font with all the information needed about a provided font.
+         * @param uri A URI associated to the font file.
+         * @param ttcIndex If providing a TTC_INDEX file, the index to point to. Otherwise, 0.
+         * @param weight An integer that indicates the font weight.
+         * @param italic A boolean that indicates the font is italic style or not.
+         * @param resultCode A boolean that indicates the font contents is ready.
+         */
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        public FontInfo(@NonNull Uri uri, @IntRange(from = 0) int ttcIndex,
+                @IntRange(from = 1, to = 1000) int weight,
+                boolean italic, int resultCode) {
+            mUri = Preconditions.checkNotNull(uri);
+            mTtcIndex = ttcIndex;
+            mWeight = weight;
+            mItalic = italic;
+            mResultCode = resultCode;
+        }
+
+        /**
+         * Returns a URI associated to this record.
+         */
+        public @NonNull Uri getUri() {
+            return mUri;
+        }
+
+        /**
+         * Returns the index to be used to access this font when accessing a TTC file.
+         */
+        public @IntRange(from = 0) int getTtcIndex() {
+            return mTtcIndex;
+        }
+
+        /**
+         * Returns the weight value for this font.
+         */
+        public @IntRange(from = 1, to = 1000) int getWeight() {
+            return mWeight;
+        }
+
+        /**
+         * Returns whether this font is italic.
+         */
+        public boolean isItalic() {
+            return mItalic;
+        }
+
+        /**
+         * Returns result code.
+         *
+         * {@link FontsContractCompat.Columns#RESULT_CODE}
+         */
+        public int getResultCode() {
+            return mResultCode;
+        }
+    }
+
+    /**
+     * Object returned from {@link #fetchFonts}.
+     */
+    public static class FontFamilyResult {
+        /**
+         * Constant represents that the font was successfully retrieved. Note that when this value
+         * is set and {@link #getFonts} returns an empty array, it means there were no fonts
+         * matching the given query.
+         */
+        public static final int STATUS_OK = 0;
+
+        /**
+         * Constant represents that the given certificate was not matched with the provider's
+         * signature. {@link #getFonts} returns null if this status was set.
+         */
+        public static final int STATUS_WRONG_CERTIFICATES = 1;
+
+        /**
+         * Constant represents that the provider returns unexpected data. {@link #getFonts} returns
+         * null if this status was set. For example, this value is set when the font provider
+         * gives invalid format of variation settings.
+         */
+        public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2;
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        @IntDef({STATUS_OK, STATUS_WRONG_CERTIFICATES, STATUS_UNEXPECTED_DATA_PROVIDED})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface FontResultStatus {}
+
+        private final @FontResultStatus int mStatusCode;
+        private final FontInfo[] mFonts;
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        public FontFamilyResult(@FontResultStatus int statusCode, @Nullable FontInfo[] fonts) {
+            mStatusCode = statusCode;
+            mFonts = fonts;
+        }
+
+        public @FontResultStatus int getStatusCode() {
+            return mStatusCode;
+        }
+
+        public FontInfo[] getFonts() {
+            return mFonts;
+        }
+    }
+
+    /**
+     * Interface used to receive asynchronously fetched typefaces.
+     */
+    public static class FontRequestCallback {
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+         * provider was not found on the device.
+         */
+        public static final int FAIL_REASON_PROVIDER_NOT_FOUND = RESULT_CODE_PROVIDER_NOT_FOUND;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+         * provider must be authenticated and the given certificates do not match its signature.
+         */
+        public static final int FAIL_REASON_WRONG_CERTIFICATES = RESULT_CODE_WRONG_CERTIFICATES;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+         * returned by the provider was not loaded properly.
+         */
+        public static final int FAIL_REASON_FONT_LOAD_ERROR = -3;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+         * provider did not return any results for the given query.
+         */
+        public static final int FAIL_REASON_FONT_NOT_FOUND = Columns.RESULT_CODE_FONT_NOT_FOUND;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+         * provider found the queried font, but it is currently unavailable.
+         */
+        public static final int FAIL_REASON_FONT_UNAVAILABLE = Columns.RESULT_CODE_FONT_UNAVAILABLE;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+         * query was not supported by the provider.
+         */
+        public static final int FAIL_REASON_MALFORMED_QUERY = Columns.RESULT_CODE_MALFORMED_QUERY;
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
+                FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE,
+                FAIL_REASON_MALFORMED_QUERY, FAIL_REASON_WRONG_CERTIFICATES })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface FontRequestFailReason {}
+
+        public FontRequestCallback() {}
+
+        /**
+         * Called then a Typeface request done via {@link #requestFont(Context, FontRequest,
+         * FontRequestCallback, Handler)} is complete. Note that this method will not be called if
+         * {@link #onTypefaceRequestFailed(int)} is called instead.
+         * @param typeface  The Typeface object retrieved.
+         */
+        public void onTypefaceRetrieved(Typeface typeface) {}
+
+        /**
+         * Called when a Typeface request done via {@link #requestFont(Context, FontRequest,
+         * FontRequestCallback, Handler)} fails.
+         * @param reason May be one of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
+         *               {@link #FAIL_REASON_FONT_NOT_FOUND},
+         *               {@link #FAIL_REASON_FONT_LOAD_ERROR},
+         *               {@link #FAIL_REASON_FONT_UNAVAILABLE},
+         *               {@link #FAIL_REASON_MALFORMED_QUERY} or
+         *               {@link #FAIL_REASON_WRONG_CERTIFICATES}, or a provider defined positive
+         *               code number.
+         */
+        public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {}
+    }
+
+    /**
+     * Create a typeface object given a font request. The font will be asynchronously fetched,
+     * therefore the result is delivered to the given callback. See {@link FontRequest}.
+     * Only one of the methods in callback will be invoked, depending on whether the request
+     * succeeds or fails. These calls will happen on the caller thread.
+     * @param context A context to be used for fetching from font provider.
+     * @param request A {@link FontRequest} object that identifies the provider and query for the
+     *                request. May not be null.
+     * @param callback A callback that will be triggered when results are obtained. May not be null.
+     * @param handler A handler to be processed the font fetching.
+     */
+    public static void requestFont(final @NonNull Context context,
+            final @NonNull FontRequest request, final @NonNull FontRequestCallback callback,
+            final @NonNull Handler handler) {
+        final Handler callerThreadHandler = new Handler();
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                // TODO: Cache the result.
+                FontFamilyResult result;
+                try {
+                    result = fetchFonts(context, null /* cancellation signal */, request);
+                } catch (PackageManager.NameNotFoundException e) {
+                    callerThreadHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onTypefaceRequestFailed(
+                                    FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND);
+                        }
+                    });
+                    return;
+                }
+
+                if (result.getStatusCode() != FontFamilyResult.STATUS_OK) {
+                    switch (result.getStatusCode()) {
+                        case FontFamilyResult.STATUS_WRONG_CERTIFICATES:
+                            callerThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTypefaceRequestFailed(
+                                            FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES);
+                                }
+                            });
+                            return;
+                        case FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED:
+                            callerThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTypefaceRequestFailed(
+                                            FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                                }
+                            });
+                            return;
+                        default:
+                            // fetchFont returns unexpected status type. Fallback to load error.
+                            callerThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTypefaceRequestFailed(
+                                            FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                                }
+                            });
+                            return;
+                    }
+                }
+
+                final FontInfo[] fonts = result.getFonts();
+                if (fonts == null || fonts.length == 0) {
+                    callerThreadHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onTypefaceRequestFailed(
+                                    FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
+                        }
+                    });
+                    return;
+                }
+                for (final FontInfo font : fonts) {
+                    if (font.getResultCode() != Columns.RESULT_CODE_OK) {
+                        // We proceed if all font entry is ready to use. Otherwise report the first
+                        // error.
+                        final int resultCode = font.getResultCode();
+                        if (resultCode < 0) {
+                            // Negative values are reserved for internal errors. Fallback to load
+                            // error.
+                            callerThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTypefaceRequestFailed(
+                                            FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                                }
+                            });
+                        } else {
+                            callerThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTypefaceRequestFailed(resultCode);
+                                }
+                            });
+                        }
+                        return;
+                    }
+                }
+
+                final Typeface typeface = buildTypeface(context, null /* cancellation signal */,
+                        fonts);
+                if (typeface == null) {
+                    // Something went wrong during reading font files. This happens if the given
+                    // font file is an unsupported font type.
+                    callerThreadHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onTypefaceRequestFailed(
+                                    FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                        }
+                    });
+                    return;
+                }
+
+                callerThreadHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onTypefaceRetrieved(typeface);
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Build a Typeface from an array of {@link FontInfo}
+     *
+     * Results that are marked as not ready will be skipped.
+     *
+     * @param context A {@link Context} that will be used to fetch the font contents.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If
+     *                           the operation is canceled, then {@link
+     *                           android.os.OperationCanceledException} will be thrown.
+     * @param fonts An array of {@link FontInfo} to be used to create a Typeface.
+     * @return A Typeface object. Returns null if typeface creation fails.
+     */
+    public static Typeface buildTypeface(@NonNull Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts) {
+        return TypefaceCompat.createFromFontInfo(context, cancellationSignal, fonts,
+                Typeface.NORMAL);
+    }
+
+    /**
+     * A helper function to create a mapping from {@link Uri} to {@link ByteBuffer}.
+     *
+     * Skip if the file contents is not ready to be read.
+     *
+     * @param context A {@link Context} to be used for resolving content URI in
+     *                {@link FontInfo}.
+     * @param fonts An array of {@link FontInfo}.
+     * @return A map from {@link Uri} to {@link ByteBuffer}.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @RequiresApi(19)
+    public static Map<Uri, ByteBuffer> prepareFontData(Context context, FontInfo[] fonts,
+            CancellationSignal cancellationSignal) {
+        final HashMap<Uri, ByteBuffer> out = new HashMap<>();
+
+        for (FontInfo font : fonts) {
+            if (font.getResultCode() != Columns.RESULT_CODE_OK) {
+                continue;
+            }
+
+            final Uri uri = font.getUri();
+            if (out.containsKey(uri)) {
+                continue;
+            }
+
+            ByteBuffer buffer = TypefaceCompatUtil.mmap(context, cancellationSignal, uri);
+            out.put(uri, buffer);
+        }
+        return Collections.unmodifiableMap(out);
+    }
+
+    /**
+     * Fetch fonts given a font request.
+     *
+     * @param context A {@link Context} to be used for fetching fonts.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If
+     *                           the operation is canceled, then {@link
+     *                           android.os.OperationCanceledException} will be thrown when the
+     *                           query is executed.
+     * @param request A {@link FontRequest} object that identifies the provider and query for the
+     *                request.
+     *
+     * @return {@link FontFamilyResult}
+     *
+     * @throws PackageManager.NameNotFoundException If requested package or authority was not found
+     *      in the system.
+     */
+    @NonNull
+    public static FontFamilyResult fetchFonts(@NonNull Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontRequest request)
+            throws PackageManager.NameNotFoundException {
+        ProviderInfo providerInfo = getProvider(
+                context.getPackageManager(), request, context.getResources());
+        if (providerInfo == null) {
+            return new FontFamilyResult(FontFamilyResult.STATUS_WRONG_CERTIFICATES, null);
+
+        }
+        FontInfo[] fonts = getFontFromProvider(
+                context, request, providerInfo.authority, cancellationSignal);
+        return new FontFamilyResult(FontFamilyResult.STATUS_OK, fonts);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    @RestrictTo(LIBRARY_GROUP)
+    public static @Nullable ProviderInfo getProvider(@NonNull PackageManager packageManager,
+            @NonNull FontRequest request, @Nullable Resources resources)
+            throws PackageManager.NameNotFoundException {
+        String providerAuthority = request.getProviderAuthority();
+        ProviderInfo info = packageManager.resolveContentProvider(providerAuthority, 0);
+        if (info == null) {
+            throw new PackageManager.NameNotFoundException("No package found for authority: "
+                    + providerAuthority);
+        }
+
+        if (!info.packageName.equals(request.getProviderPackage())) {
+            throw new PackageManager.NameNotFoundException("Found content provider "
+                    + providerAuthority
+                    + ", but package was not " + request.getProviderPackage());
+        }
+
+        List<byte[]> signatures;
+        // We correctly check all signatures returned, as advised in the lint error.
+        @SuppressLint("PackageManagerGetSignatures")
+        PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName,
+                PackageManager.GET_SIGNATURES);
+        signatures = convertToByteArrayList(packageInfo.signatures);
+        Collections.sort(signatures, sByteArrayComparator);
+        List<List<byte[]>> requestCertificatesList = getCertificates(request, resources);
+        for (int i = 0; i < requestCertificatesList.size(); ++i) {
+            // Make a copy so we can sort it without modifying the incoming data.
+            List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i));
+            Collections.sort(requestSignatures, sByteArrayComparator);
+            if (equalsByteArrayList(signatures, requestSignatures)) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    private static List<List<byte[]>> getCertificates(FontRequest request, Resources resources) {
+        if (request.getCertificates() != null) {
+            return request.getCertificates();
+        }
+        int resourceId = request.getCertificatesArrayResId();
+        return FontResourcesParserCompat.readCerts(resources, resourceId);
+    }
+
+    private static final Comparator<byte[]> sByteArrayComparator = new Comparator<byte[]>() {
+        @Override
+        public int compare(byte[] l, byte[] r) {
+            if (l.length != r.length) {
+                return l.length - r.length;
+            }
+            for (int i = 0; i < l.length; ++i) {
+                if (l[i] != r[i]) {
+                    return l[i] - r[i];
+                }
+            }
+            return 0;
+        }
+    };
+
+    private static boolean equalsByteArrayList(List<byte[]> signatures,
+            List<byte[]> requestSignatures) {
+        if (signatures.size() != requestSignatures.size()) {
+            return false;
+        }
+        for (int i = 0; i < signatures.size(); ++i) {
+            if (!Arrays.equals(signatures.get(i), requestSignatures.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static List<byte[]> convertToByteArrayList(Signature[] signatures) {
+        List<byte[]> shas = new ArrayList<>();
+        for (int i = 0; i < signatures.length; ++i) {
+            shas.add(signatures[i].toByteArray());
+        }
+        return shas;
+    }
+
+    @VisibleForTesting
+    @NonNull
+    static FontInfo[] getFontFromProvider(Context context, FontRequest request, String authority,
+            CancellationSignal cancellationSignal) {
+        ArrayList<FontInfo> result = new ArrayList<>();
+        final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .build();
+        final Uri fileBaseUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath("file")
+                .build();
+        Cursor cursor = null;
+        try {
+            if (Build.VERSION.SDK_INT > 16) {
+                cursor = context.getContentResolver().query(uri, new String[] {
+                        Columns._ID, Columns.FILE_ID, Columns.TTC_INDEX,
+                        Columns.VARIATION_SETTINGS, Columns.WEIGHT, Columns.ITALIC,
+                        Columns.RESULT_CODE },
+                "query = ?", new String[] { request.getQuery() }, null, cancellationSignal);
+            } else {
+                // No cancellation signal.
+                cursor = context.getContentResolver().query(uri, new String[] {
+                        Columns._ID, Columns.FILE_ID, Columns.TTC_INDEX,
+                        Columns.VARIATION_SETTINGS, Columns.WEIGHT, Columns.ITALIC,
+                        Columns.RESULT_CODE },
+                "query = ?", new String[] { request.getQuery() }, null);
+            }
+            if (cursor != null && cursor.getCount() > 0) {
+                final int resultCodeColumnIndex = cursor.getColumnIndex(Columns.RESULT_CODE);
+                result = new ArrayList<>();
+                final int idColumnIndex = cursor.getColumnIndex(Columns._ID);
+                final int fileIdColumnIndex = cursor.getColumnIndex(Columns.FILE_ID);
+                final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX);
+                final int weightColumnIndex = cursor.getColumnIndex(Columns.WEIGHT);
+                final int italicColumnIndex = cursor.getColumnIndex(Columns.ITALIC);
+                while (cursor.moveToNext()) {
+                    int resultCode = resultCodeColumnIndex != -1
+                            ? cursor.getInt(resultCodeColumnIndex) : Columns.RESULT_CODE_OK;
+                    final int ttcIndex = ttcIndexColumnIndex != -1
+                            ? cursor.getInt(ttcIndexColumnIndex) : 0;
+                    Uri fileUri;
+                    if (fileIdColumnIndex == -1) {
+                        long id = cursor.getLong(idColumnIndex);
+                        fileUri = ContentUris.withAppendedId(uri, id);
+                    } else {
+                        long id = cursor.getLong(fileIdColumnIndex);
+                        fileUri = ContentUris.withAppendedId(fileBaseUri, id);
+                    }
+
+                    int weight = weightColumnIndex != -1 ? cursor.getInt(weightColumnIndex) : 400;
+                    boolean italic = italicColumnIndex != -1 && cursor.getInt(italicColumnIndex)
+                            == 1;
+                    result.add(new FontInfo(fileUri, ttcIndex, weight, italic, resultCode));
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return result.toArray(new FontInfo[0]);
+    }
+}
diff --git a/compat/java/android/support/v4/provider/SelfDestructiveThread.java b/compat/java/android/support/v4/provider/SelfDestructiveThread.java
new file mode 100644
index 0000000..885799b
--- /dev/null
+++ b/compat/java/android/support/v4/provider/SelfDestructiveThread.java
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.provider;
+
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Background thread which is destructed after certain period after all pending activities are
+ * finished.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class SelfDestructiveThread {
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private HandlerThread mThread;
+
+    @GuardedBy("mLock")
+    private Handler mHandler;
+
+    @GuardedBy("mLock")
+    private int mGeneration;  // The thread generation. Only for testing purpose.
+
+    private static final int MSG_INVOKE_RUNNABLE = 1;
+    private static final int MSG_DESTRUCTION = 0;
+
+    private Handler.Callback mCallback = new Handler.Callback() {
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_INVOKE_RUNNABLE:
+                    onInvokeRunnable((Runnable) msg.obj);
+                    return true;
+                case MSG_DESTRUCTION:
+                    onDestruction();
+                    return true;
+            }
+            return true;
+        }
+    };
+
+    // Milliseconds the thread will be destructed after the last activity.
+    private final int mDestructAfterMillisec;
+    private final int mPriority;  // The priority of the thread.
+    private final String mThreadName;  // The name of the thread.
+
+    public SelfDestructiveThread(
+            String threadName, int priority, int destructAfterMillisec) {
+        mThreadName = threadName;
+        mPriority = priority;
+        mDestructAfterMillisec = destructAfterMillisec;
+        mGeneration = 0;
+    }
+
+    /**
+     * Returns true if the thread is alive.
+     */
+    @VisibleForTesting
+    public boolean isRunning() {
+        synchronized (mLock) {
+            return mThread != null;
+        }
+    }
+
+    /**
+     * Returns the thread generation.
+     */
+    @VisibleForTesting
+    public int getGeneration() {
+        synchronized (mLock) {
+            return mGeneration;
+        }
+    }
+
+    private void post(Runnable runnable) {
+        synchronized (mLock) {
+            if (mThread == null) {
+                mThread = new HandlerThread(mThreadName, mPriority);
+                mThread.start();
+                mHandler = new Handler(mThread.getLooper(), mCallback);
+                mGeneration++;
+            }
+            mHandler.removeMessages(MSG_DESTRUCTION);
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_INVOKE_RUNNABLE, runnable));
+        }
+    }
+
+    /**
+     * Reply callback for postAndReply
+     *
+     * @param <T> A type which will be received as the argument.
+     */
+    public interface ReplyCallback<T> {
+        /**
+         * Called when the task was finished.
+         */
+        void onReply(T value);
+    }
+
+    /**
+     * Execute the specific callable object on this thread and call the reply callback on the
+     * calling thread once it finishs.
+     */
+    public <T> void postAndReply(final Callable<T> callable, final ReplyCallback<T> reply) {
+        final Handler callingHandler = new Handler();
+        post(new Runnable() {
+            @Override
+            public void run() {
+                T t;
+                try {
+                    t = callable.call();
+                } catch (Exception e) {
+                    t = null;
+                }
+                final T result = t;
+                callingHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        reply.onReply(result);
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Execute the specified callable object on this thread and returns the returned value to the
+     * caller.
+     *
+     * If the execution takes longer time than specified timeout duration, this function throws
+     * InterruptedException.
+     */
+    public <T> T postAndWait(final Callable<T> callable, int timeoutMillis)
+            throws InterruptedException {
+        final ReentrantLock lock = new ReentrantLock();
+        final Condition cond = lock.newCondition();
+
+        final AtomicReference<T> holder = new AtomicReference();
+        final AtomicBoolean running = new AtomicBoolean(true);
+        post(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    holder.set(callable.call());
+                } catch (Exception e) {
+                    // Do nothing.
+                }
+                lock.lock();
+                try {
+                    running.set(false);
+                    cond.signal();
+                } finally {
+                    lock.unlock();
+                }
+            }
+        });
+
+        lock.lock();
+        try {
+            if (!running.get()) {
+                return holder.get();  // already finished.
+            }
+            long remaining = TimeUnit.MILLISECONDS.toNanos(timeoutMillis);
+            for (;;) {
+                try {
+                    remaining = cond.awaitNanos(remaining);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+                if (!running.get()) {
+                    return holder.get();  // Successfully finished.
+                }
+                if (remaining <= 0) {
+                    throw new InterruptedException("timeout");  // Timeout
+                }
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void onInvokeRunnable(Runnable runnable) {
+        runnable.run();
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_DESTRUCTION);
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DESTRUCTION),
+                    mDestructAfterMillisec);
+        }
+    }
+
+    private void onDestruction() {
+        synchronized (mLock) {
+            if (mHandler.hasMessages(MSG_INVOKE_RUNNABLE)) {
+                // This happens if post() is called after onDestruction and before synchronization
+                // block.
+                return;
+            }
+            mThread.quit();
+            mThread = null;
+            mHandler = null;
+        }
+    }
+}
diff --git a/core-utils/java/android/support/v4/text/BidiFormatter.java b/compat/java/android/support/v4/text/BidiFormatter.java
similarity index 100%
rename from core-utils/java/android/support/v4/text/BidiFormatter.java
rename to compat/java/android/support/v4/text/BidiFormatter.java
diff --git a/compat/java/android/support/v4/text/ICUCompat.java b/compat/java/android/support/v4/text/ICUCompat.java
index 0632513..d5ad0d6 100644
--- a/compat/java/android/support/v4/text/ICUCompat.java
+++ b/compat/java/android/support/v4/text/ICUCompat.java
@@ -16,47 +16,34 @@
 
 package android.support.v4.text;
 
+import android.support.annotation.RequiresApi;
 import android.os.Build;
+import android.support.annotation.Nullable;
 
 import java.util.Locale;
 
 public final class ICUCompat {
-
-    interface ICUCompatImpl {
-        public String maximizeAndGetScript(Locale locale);
-    }
-
-    static class ICUCompatImplBase implements ICUCompatImpl {
-        @Override
-        public String maximizeAndGetScript(Locale locale) {
-            return null;
-        }
-    }
-
-    static class ICUCompatImplIcs implements ICUCompatImpl {
-        @Override
+    static class ICUCompatBaseImpl {
         public String maximizeAndGetScript(Locale locale) {
             return ICUCompatIcs.maximizeAndGetScript(locale);
         }
     }
 
-    static class ICUCompatImplLollipop implements ICUCompatImpl {
+    @RequiresApi(21)
+    static class ICUCompatApi21Impl extends ICUCompatBaseImpl {
         @Override
         public String maximizeAndGetScript(Locale locale) {
-            return ICUCompatApi23.maximizeAndGetScript(locale);
+            return ICUCompatApi21.maximizeAndGetScript(locale);
         }
     }
 
-    private static final ICUCompatImpl IMPL;
+    private static final ICUCompatBaseImpl IMPL;
 
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new ICUCompatImplLollipop();
-        } else if (version >= 14) {
-            IMPL = new ICUCompatImplIcs();
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ICUCompatApi21Impl();
         } else {
-            IMPL = new ICUCompatImplBase();
+            IMPL = new ICUCompatBaseImpl();
         }
     }
 
@@ -81,8 +68,9 @@
      * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
      * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
      *
-     * @return
+     * @return The script for a given Locale if ICU library is available, otherwise null.
      */
+    @Nullable
     public static String maximizeAndGetScript(Locale locale) {
         return IMPL.maximizeAndGetScript(locale);
     }
diff --git a/compat/java/android/support/v4/text/TextUtilsCompat.java b/compat/java/android/support/v4/text/TextUtilsCompat.java
index 6acf644..2a77e7e 100644
--- a/compat/java/android/support/v4/text/TextUtilsCompat.java
+++ b/compat/java/android/support/v4/text/TextUtilsCompat.java
@@ -16,20 +16,40 @@
 
 package android.support.v4.text;
 
-import android.os.Build;
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
+import android.text.TextUtils;
 
 import java.util.Locale;
 
+/**
+ * Backwards compatible version of {@link TextUtils}.
+ */
 public final class TextUtilsCompat {
-    private static class TextUtilsCompatImpl {
-        TextUtilsCompatImpl() {
-        }
+    /**
+     * @deprecated This was never meant to be public. You can create your own empty {@code Locale}
+     * by calling the constructor with empty strings.
+     */
+    @Deprecated
+    public static final Locale ROOT = new Locale("", "");
 
-        @NonNull
-        public String htmlEncode(@NonNull String s) {
+    private static final String ARAB_SCRIPT_SUBTAG = "Arab";
+    private static final String HEBR_SCRIPT_SUBTAG = "Hebr";
+
+    /**
+     * Html-encode the string.
+     *
+     * @param s the string to be encoded
+     * @return the encoded string
+     */
+    @NonNull
+    public static String htmlEncode(@NonNull String s) {
+        if (SDK_INT >= 17) {
+            return TextUtils.htmlEncode(s);
+        } else {
             StringBuilder sb = new StringBuilder();
             char c;
             for (int i = 0; i < s.length(); i++) {
@@ -61,101 +81,57 @@
             }
             return sb.toString();
         }
+    }
 
-        public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
+    /**
+     * Returns the layout direction for a given Locale
+     *
+     * @param locale the {@link Locale} for which we want the layout direction, maybe be
+     *               {@code null}.
+     * @return the layout direction, either {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
+     *         {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
+     */
+    public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
+        if (SDK_INT >= 17) {
+            return TextUtils.getLayoutDirectionFromLocale(locale);
+        } else {
             if (locale != null && !locale.equals(ROOT)) {
                 final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
                 if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
 
                 // This is intentionally limited to Arabic and Hebrew scripts, since older
                 // versions of Android platform only considered those scripts to be right-to-left.
-                if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
-                        scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
+                if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG)
+                        || scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
                     return ViewCompat.LAYOUT_DIRECTION_RTL;
                 }
             }
             return ViewCompat.LAYOUT_DIRECTION_LTR;
         }
-
-        /**
-         * Fallback algorithm to detect the locale direction. Rely on the first char of the
-         * localized locale name. This will not work if the localized locale name is in English
-         * (this is the case for ICU 4.4 and "Urdu" script)
-         *
-         * @param locale
-         * @return the layout direction. This may be one of:
-         * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
-         * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
-         *
-         * Be careful: this code will need to be updated when vertical scripts will be supported
-         */
-        private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
-            switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
-                case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-                case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                    return ViewCompat.LAYOUT_DIRECTION_RTL;
-
-                case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-                default:
-                    return ViewCompat.LAYOUT_DIRECTION_LTR;
-            }
-        }
-    }
-
-    private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl {
-        TextUtilsCompatJellybeanMr1Impl() {
-        }
-
-        @Override
-        @NonNull
-        public String htmlEncode(@NonNull String s) {
-            return TextUtilsCompatJellybeanMr1.htmlEncode(s);
-        }
-
-        @Override
-        public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
-            return TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale);
-        }
-    }
-
-    private static final TextUtilsCompatImpl IMPL;
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 17) { // JellyBean MR1
-            IMPL = new TextUtilsCompatJellybeanMr1Impl();
-        } else {
-            IMPL = new TextUtilsCompatImpl();
-        }
     }
 
     /**
-     * Html-encode the string.
-     * @param s the string to be encoded
-     * @return the encoded string
+     * Fallback algorithm to detect the locale direction. Rely on the first char of the
+     * localized locale name. This will not work if the localized locale name is in English
+     * (this is the case for ICU 4.4 and "Urdu" script)
+     *
+     * @param locale the {@link Locale} for which we want the layout direction, maybe be
+     *               {@code null}.
+     * @return the layout direction, either {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
+     *         {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
      */
-    @NonNull
-    public static String htmlEncode(@NonNull String s) {
-        return IMPL.htmlEncode(s);
+    private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
+        switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+                return ViewCompat.LAYOUT_DIRECTION_RTL;
+
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+            default:
+                return ViewCompat.LAYOUT_DIRECTION_LTR;
+        }
     }
 
-    /**
-     * Return the layout direction for a given Locale
-     *
-     * @param locale the Locale for which we want the layout direction. Can be null.
-     * @return the layout direction. This may be one of:
-     * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
-     * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
-     *
-     * Be careful: this code will need to be updated when vertical scripts will be supported
-     */
-    public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
-        return IMPL.getLayoutDirectionFromLocale(locale);
-    }
-
-    public static final Locale ROOT = new Locale("", "");
-
-    static String ARAB_SCRIPT_SUBTAG = "Arab";
-    static String HEBR_SCRIPT_SUBTAG = "Hebr";
 
     private TextUtilsCompat() {}
 }
diff --git a/compat/java/android/support/v4/text/util/LinkifyCompat.java b/compat/java/android/support/v4/text/util/LinkifyCompat.java
index d933c40..23b3e49 100644
--- a/compat/java/android/support/v4/text/util/LinkifyCompat.java
+++ b/compat/java/android/support/v4/text/util/LinkifyCompat.java
@@ -16,9 +16,13 @@
 
 package android.support.v4.text.util;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.os.Build;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
 import android.support.v4.util.PatternsCompat;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -33,9 +37,9 @@
 import android.widget.TextView;
 
 import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -73,6 +77,8 @@
         }
     };
 
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
     @IntDef(flag = true, value = { Linkify.WEB_URLS, Linkify.EMAIL_ADDRESSES, Linkify.PHONE_NUMBERS,
             Linkify.MAP_ADDRESSES, Linkify.ALL })
     @Retention(RetentionPolicy.SOURCE)
@@ -91,6 +97,9 @@
      *  @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(text, mask);
+        }
         if (mask == 0) {
             return false;
         }
@@ -152,6 +161,9 @@
      *  @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(text, mask);
+        }
         if (mask == 0) {
             return false;
         }
@@ -192,6 +204,10 @@
      */
     public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String scheme) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            Linkify.addLinks(text, pattern, scheme);
+            return;
+        }
         addLinks(text, pattern, scheme, null, null, null);
     }
 
@@ -212,6 +228,10 @@
     public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
             @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            Linkify.addLinks(text, pattern, scheme, matchFilter, transformFilter);
+            return;
+        }
         addLinks(text, pattern, scheme, null, matchFilter, transformFilter);
     }
 
@@ -233,8 +253,12 @@
      *  @param transformFilter Filter to allow the client code to update the link found.
      */
     public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
-            @Nullable  String defaultScheme, @Nullable String[] schemes,
+            @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            Linkify.addLinks(text, pattern, defaultScheme, schemes, matchFilter, transformFilter);
+            return;
+        }
         SpannableString spannable = SpannableString.valueOf(text.getText());
 
         boolean linksAdded = addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
@@ -256,6 +280,9 @@
      */
     public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
             @Nullable String scheme) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(text, pattern, scheme);
+        }
         return addLinks(text, pattern, scheme, null, null, null);
     }
 
@@ -277,6 +304,9 @@
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
             @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(spannable, pattern, scheme, matchFilter, transformFilter);
+        }
         return addLinks(spannable, pattern, scheme, null, matchFilter,
                 transformFilter);
     }
@@ -300,6 +330,10 @@
     public static final 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) {
+            return Linkify.addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
+                    transformFilter);
+        }
         final String[] schemesCopy;
         if (defaultScheme == null) defaultScheme = "";
         if (schemes == null || schemes.length < 1) {
diff --git a/compat/java/android/support/v4/util/ArraySet.java b/compat/java/android/support/v4/util/ArraySet.java
index d03dfd1..ab080fa 100644
--- a/compat/java/android/support/v4/util/ArraySet.java
+++ b/compat/java/android/support/v4/util/ArraySet.java
@@ -16,6 +16,9 @@
 
 package android.support.v4.util;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.RestrictTo;
 import android.util.Log;
 
 import java.lang.reflect.Array;
@@ -153,6 +156,7 @@
         return ~end;
     }
 
+    @SuppressWarnings("ArrayToString")
     private void allocArrays(final int size) {
         if (size == (BASE_SIZE * 2)) {
             synchronized (ArraySet.class) {
@@ -192,6 +196,7 @@
         mArray = new Object[size];
     }
 
+    @SuppressWarnings("ArrayToString")
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE * 2)) {
             synchronized (ArraySet.class) {
@@ -405,6 +410,7 @@
      * The array must already be large enough to contain the item.
      * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public void append(E value) {
         final int index = mSize;
         final int hash = value == null ? 0
diff --git a/compat/java/android/support/v4/util/DebugUtils.java b/compat/java/android/support/v4/util/DebugUtils.java
index 8937b7e..7e35afa 100644
--- a/compat/java/android/support/v4/util/DebugUtils.java
+++ b/compat/java/android/support/v4/util/DebugUtils.java
@@ -21,8 +21,7 @@
 import android.support.annotation.RestrictTo;
 
 /**
- * Helper for accessing features in {@link android.util.DebugUtils}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.util.DebugUtils}.
  *
  * @hide
  */
diff --git a/compat/java/android/support/v4/util/LogWriter.java b/compat/java/android/support/v4/util/LogWriter.java
index 2889f4d..0ab3298 100644
--- a/compat/java/android/support/v4/util/LogWriter.java
+++ b/compat/java/android/support/v4/util/LogWriter.java
@@ -24,8 +24,7 @@
 import java.io.Writer;
 
 /**
- * Helper for accessing features in {@link android.util.LogWriter}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.util.LogWriter}.
  *
  * @hide
  */
diff --git a/compat/java/android/support/v4/util/LruCache.java b/compat/java/android/support/v4/util/LruCache.java
index 524e985..944b354 100644
--- a/compat/java/android/support/v4/util/LruCache.java
+++ b/compat/java/android/support/v4/util/LruCache.java
@@ -17,6 +17,7 @@
 package android.support.v4.util;
 
 import java.util.LinkedHashMap;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -337,7 +338,7 @@
     @Override public synchronized final String toString() {
         int accesses = hitCount + missCount;
         int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
-        return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
+        return String.format(Locale.US, "LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
                 maxSize, hitCount, missCount, hitPercent);
     }
 }
diff --git a/compat/java/android/support/v4/util/MapCollections.java b/compat/java/android/support/v4/util/MapCollections.java
index 441f338..1a0ab6b 100644
--- a/compat/java/android/support/v4/util/MapCollections.java
+++ b/compat/java/android/support/v4/util/MapCollections.java
@@ -20,6 +20,7 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 /**
@@ -49,6 +50,7 @@
 
         @Override
         public T next() {
+            if (!hasNext()) throw new NoSuchElementException();
             Object res = colGetEntry(mIndex, mOffset);
             mIndex++;
             mCanRemove = true;
@@ -84,6 +86,7 @@
 
         @Override
         public Map.Entry<K, V> next() {
+            if (!hasNext()) throw new NoSuchElementException();
             mIndex++;
             mEntryValid = true;
             return this;
diff --git a/compat/java/android/support/v4/util/ObjectsCompat.java b/compat/java/android/support/v4/util/ObjectsCompat.java
new file mode 100644
index 0000000..4a4c844
--- /dev/null
+++ b/compat/java/android/support/v4/util/ObjectsCompat.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package android.support.v4.util;
+
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
+import java.util.Objects;
+
+/**
+ * This class consists of static utility methods for operating on objects.
+ */
+public class ObjectsCompat {
+    private static final ImplBase IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new ImplApi19();
+        } else {
+            IMPL = new ImplBase();
+        }
+    }
+
+    private ObjectsCompat() {
+        // Non-instantiable.
+    }
+
+    /**
+     * Returns {@code true} if the arguments are equal to each other
+     * and {@code false} otherwise.
+     * <p>
+     * Consequently, if both arguments are {@code null}, {@code true}
+     * is returned and if exactly one argument is {@code null}, {@code
+     * false} is returned. Otherwise, equality is determined by using
+     * the {@link Object#equals equals} method of the first
+     * argument.
+     *
+     * @param a an object
+     * @param b an object to be compared with {@code a} for equality
+     * @return {@code true} if the arguments are equal to each other
+     *         and {@code false} otherwise
+     * @see Object#equals(Object)
+     */
+    public static boolean equals(Object a, Object b) {
+        return IMPL.equals(a, b);
+    }
+
+    private static class ImplBase {
+        public boolean equals(Object a, Object b) {
+            return (a == b) || (a != null && a.equals(b));
+        }
+    }
+
+    @RequiresApi(19)
+    private static class ImplApi19 extends ImplBase {
+        @Override
+        public boolean equals(Object a, Object b) {
+            return Objects.equals(a, b);
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/util/Preconditions.java b/compat/java/android/support/v4/util/Preconditions.java
new file mode 100644
index 0000000..711566a
--- /dev/null
+++ b/compat/java/android/support/v4/util/Preconditions.java
@@ -0,0 +1,464 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.util;
+
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.text.TextUtils;
+
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * Simple static methods to be called at the start of your own methods to verify
+ * correct arguments and state.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class Preconditions {
+    public static void checkArgument(boolean expression) {
+        if (!expression) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Ensures that an expression checking an argument is true.
+     *
+     * @param expression the expression to check
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @throws IllegalArgumentException if {@code expression} is false
+     */
+    public static void checkArgument(boolean expression, final Object errorMessage) {
+        if (!expression) {
+            throw new IllegalArgumentException(String.valueOf(errorMessage));
+        }
+    }
+
+    /**
+     * Ensures that an string reference passed as a parameter to the calling
+     * method is not empty.
+     *
+     * @param string an string reference
+     * @return the string reference that was validated
+     * @throws IllegalArgumentException if {@code string} is empty
+     */
+    public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) {
+        if (TextUtils.isEmpty(string)) {
+            throw new IllegalArgumentException();
+        }
+        return string;
+    }
+
+    /**
+     * Ensures that an string reference passed as a parameter to the calling
+     * method is not empty.
+     *
+     * @param string an string reference
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @return the string reference that was validated
+     * @throws IllegalArgumentException if {@code string} is empty
+     */
+    public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string,
+            final Object errorMessage) {
+        if (TextUtils.isEmpty(string)) {
+            throw new IllegalArgumentException(String.valueOf(errorMessage));
+        }
+        return string;
+    }
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
+     *
+     * @param reference an object reference
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    public static @NonNull <T> T checkNotNull(final T reference) {
+        if (reference == null) {
+            throw new NullPointerException();
+        }
+        return reference;
+    }
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
+     *
+     * @param reference an object reference
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
+        if (reference == null) {
+            throw new NullPointerException(String.valueOf(errorMessage));
+        }
+        return reference;
+    }
+
+    /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @param message exception message
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    public static void checkState(final boolean expression, String message) {
+        if (!expression) {
+            throw new IllegalStateException(message);
+        }
+    }
+
+    /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    public static void checkState(final boolean expression) {
+        checkState(expression, null);
+    }
+
+    /**
+     * Check the requested flags, throwing if any requested flags are outside
+     * the allowed set.
+     *
+     * @return the validated requested flags.
+     */
+    public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
+        if ((requestedFlags & allowedFlags) != requestedFlags) {
+            throw new IllegalArgumentException("Requested flags 0x"
+                    + Integer.toHexString(requestedFlags) + ", but only 0x"
+                    + Integer.toHexString(allowedFlags) + " are allowed");
+        }
+
+        return requestedFlags;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric int value
+     * @param errorMessage the exception message to use if the check fails
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static @IntRange(from = 0) int checkArgumentNonnegative(final int value,
+            final String errorMessage) {
+        if (value < 0) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric int value
+     *
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) {
+        if (value < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric long value
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static long checkArgumentNonnegative(final long value) {
+        if (value < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric long value
+     * @param errorMessage the exception message to use if the check fails
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static long checkArgumentNonnegative(final long value, final String errorMessage) {
+        if (value < 0) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is positive.
+     *
+     * @param value a numeric int value
+     * @param errorMessage the exception message to use if the check fails
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was not positive
+     */
+    public static int checkArgumentPositive(final int value, final String errorMessage) {
+        if (value <= 0) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the argument floating point value is a finite number.
+     *
+     * <p>A finite number is defined to be both representable (that is, not NaN) and
+     * not infinite (that is neither positive or negative infinity).</p>
+     *
+     * @param value a floating point value
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated floating point value
+     *
+     * @throws IllegalArgumentException if {@code value} was not finite
+     */
+    public static float checkArgumentFinite(final float value, final String valueName) {
+        if (Float.isNaN(value)) {
+            throw new IllegalArgumentException(valueName + " must not be NaN");
+        } else if (Float.isInfinite(value)) {
+            throw new IllegalArgumentException(valueName + " must not be infinite");
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the argument floating point value is within the inclusive range.
+     *
+     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
+     * will always be out of range.</p>
+     *
+     * @param value a floating point value
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated floating point value
+     *
+     * @throws IllegalArgumentException if {@code value} was not within the range
+     */
+    public static float checkArgumentInRange(float value, float lower, float upper,
+            String valueName) {
+        if (Float.isNaN(value)) {
+            throw new IllegalArgumentException(valueName + " must not be NaN");
+        } else if (value < lower) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.US,
+                            "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
+        } else if (value > upper) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.US,
+                            "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the argument int value is within the inclusive range.
+     *
+     * @param value a int value
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated int value
+     *
+     * @throws IllegalArgumentException if {@code value} was not within the range
+     */
+    public static int checkArgumentInRange(int value, int lower, int upper,
+            String valueName) {
+        if (value < lower) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.US,
+                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+        } else if (value > upper) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.US,
+                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the argument long value is within the inclusive range.
+     *
+     * @param value a long value
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated long value
+     *
+     * @throws IllegalArgumentException if {@code value} was not within the range
+     */
+    public static long checkArgumentInRange(long value, long lower, long upper,
+            String valueName) {
+        if (value < lower) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.US,
+                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+        } else if (value > upper) {
+            throw new IllegalArgumentException(
+                    String.format(Locale.US,
+                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the array is not {@code null}, and none of its elements are {@code null}.
+     *
+     * @param value an array of boxed objects
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated array
+     *
+     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
+     */
+    public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
+        if (value == null) {
+            throw new NullPointerException(valueName + " must not be null");
+        }
+
+        for (int i = 0; i < value.length; ++i) {
+            if (value[i] == null) {
+                throw new NullPointerException(
+                        String.format(Locale.US, "%s[%d] must not be null", valueName, i));
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the {@link Collection} is not {@code null}, and none of its elements are
+     * {@code null}.
+     *
+     * @param value a {@link Collection} of boxed objects
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated {@link Collection}
+     *
+     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
+     */
+    public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull(
+            final C value, final String valueName) {
+        if (value == null) {
+            throw new NullPointerException(valueName + " must not be null");
+        }
+
+        long ctr = 0;
+        for (T elem : value) {
+            if (elem == null) {
+                throw new NullPointerException(
+                        String.format(Locale.US, "%s[%d] must not be null", valueName, ctr));
+            }
+            ++ctr;
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the {@link Collection} is not {@code null}, and contains at least one element.
+     *
+     * @param value a {@link Collection} of boxed elements.
+     * @param valueName the name of the argument to use if the check fails.
+
+     * @return the validated {@link Collection}
+     *
+     * @throws NullPointerException if the {@code value} was {@code null}
+     * @throws IllegalArgumentException if the {@code value} was empty
+     */
+    public static <T> Collection<T> checkCollectionNotEmpty(final Collection<T> value,
+            final String valueName) {
+        if (value == null) {
+            throw new NullPointerException(valueName + " must not be null");
+        }
+        if (value.isEmpty()) {
+            throw new IllegalArgumentException(valueName + " is empty");
+        }
+        return value;
+    }
+
+    /**
+     * Ensures that all elements in the argument floating point array are within the inclusive range
+     *
+     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
+     * will always be out of range.</p>
+     *
+     * @param value a floating point array of values
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated floating point value
+     *
+     * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
+     * @throws NullPointerException if the {@code value} was {@code null}
+     */
+    public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
+            String valueName) {
+        checkNotNull(value, valueName + " must not be null");
+
+        for (int i = 0; i < value.length; ++i) {
+            float v = value[i];
+
+            if (Float.isNaN(v)) {
+                throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
+            } else if (v < lower) {
+                throw new IllegalArgumentException(
+                        String.format(Locale.US, "%s[%d] is out of range of [%f, %f] (too low)",
+                                valueName, i, lower, upper));
+            } else if (v > upper) {
+                throw new IllegalArgumentException(
+                        String.format(Locale.US, "%s[%d] is out of range of [%f, %f] (too high)",
+                                valueName, i, lower, upper));
+            }
+        }
+
+        return value;
+    }
+}
diff --git a/compat/java/android/support/v4/util/SimpleArrayMap.java b/compat/java/android/support/v4/util/SimpleArrayMap.java
index c7d4b5d..06e68f0 100644
--- a/compat/java/android/support/v4/util/SimpleArrayMap.java
+++ b/compat/java/android/support/v4/util/SimpleArrayMap.java
@@ -18,6 +18,7 @@
 
 import android.util.Log;
 
+import java.util.ConcurrentModificationException;
 import java.util.Map;
 
 /**
@@ -33,6 +34,18 @@
     private static final String TAG = "ArrayMap";
 
     /**
+     * Attempt to spot concurrent modifications to this data structure.
+     *
+     * It's best-effort, but any time we can throw something more diagnostic than an
+     * ArrayIndexOutOfBoundsException deep in the ArrayMap internals it's going to
+     * save a lot of development time.
+     *
+     * Good times to look for CME include after any allocArrays() call and at the end of
+     * functions that change mSize (put/remove/clear).
+     */
+    private static final boolean CONCURRENT_MODIFICATION_EXCEPTIONS = true;
+
+    /**
      * The minimum amount by which the capacity of a ArrayMap will increase.
      * This is tuned to be relatively space-efficient.
      */
@@ -58,6 +71,18 @@
     Object[] mArray;
     int mSize;
 
+    private static int binarySearchHashes(int[] hashes, int N, int hash) {
+        try {
+            return ContainerHelpers.binarySearch(hashes, N, hash);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
+                throw new ConcurrentModificationException();
+            } else {
+                throw e; // the cache is poisoned at this point, there's not much we can do
+            }
+        }
+    }
+
     int indexOf(Object key, int hash) {
         final int N = mSize;
 
@@ -66,7 +91,7 @@
             return ~0;
         }
 
-        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
+        int index = binarySearchHashes(mHashes, N, hash);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -104,7 +129,7 @@
             return ~0;
         }
 
-        int index = ContainerHelpers.binarySearch(mHashes, N, 0);
+        int index = binarySearchHashes(mHashes, N, 0);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -134,6 +159,7 @@
         return ~end;
     }
 
+    @SuppressWarnings("ArrayToString")
     private void allocArrays(final int size) {
         if (size == (BASE_SIZE*2)) {
             synchronized (ArrayMap.class) {
@@ -169,6 +195,7 @@
         mArray = new Object[size<<1];
     }
 
+    @SuppressWarnings("ArrayToString")
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE*2)) {
             synchronized (ArrayMap.class) {
@@ -227,7 +254,7 @@
     /**
      * Create a new ArrayMap with the mappings from the given ArrayMap.
      */
-    public SimpleArrayMap(SimpleArrayMap map) {
+    public SimpleArrayMap(SimpleArrayMap<K, V> map) {
         this();
         if (map != null) {
             putAll(map);
@@ -238,11 +265,17 @@
      * Make the array map empty.  All storage is released.
      */
     public void clear() {
-        if (mSize != 0) {
-            freeArrays(mHashes, mArray, mSize);
+        if (mSize > 0) {
+            final int[] ohashes = mHashes;
+            final Object[] oarray = mArray;
+            final int osize = mSize;
             mHashes = ContainerHelpers.EMPTY_INTS;
             mArray = ContainerHelpers.EMPTY_OBJECTS;
             mSize = 0;
+            freeArrays(ohashes, oarray, osize);
+        }
+        if (CONCURRENT_MODIFICATION_EXCEPTIONS && mSize > 0) {
+            throw new ConcurrentModificationException();
         }
     }
 
@@ -251,15 +284,19 @@
      * items.
      */
     public void ensureCapacity(int minimumCapacity) {
+        final int osize = mSize;
         if (mHashes.length < minimumCapacity) {
             final int[] ohashes = mHashes;
             final Object[] oarray = mArray;
             allocArrays(minimumCapacity);
             if (mSize > 0) {
-                System.arraycopy(ohashes, 0, mHashes, 0, mSize);
-                System.arraycopy(oarray, 0, mArray, 0, mSize<<1);
+                System.arraycopy(ohashes, 0, mHashes, 0, osize);
+                System.arraycopy(oarray, 0, mArray, 0, osize<<1);
             }
-            freeArrays(ohashes, oarray, mSize);
+            freeArrays(ohashes, oarray, osize);
+        }
+        if (CONCURRENT_MODIFICATION_EXCEPTIONS && mSize != osize) {
+            throw new ConcurrentModificationException();
         }
     }
 
@@ -371,6 +408,7 @@
      * was no such key.
      */
     public V put(K key, V value) {
+        final int osize = mSize;
         final int hash;
         int index;
         if (key == null) {
@@ -388,9 +426,9 @@
         }
 
         index = ~index;
-        if (mSize >= mHashes.length) {
-            final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
-                    : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
+        if (osize >= mHashes.length) {
+            final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
+                    : (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
 
             if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
 
@@ -398,22 +436,32 @@
             final Object[] oarray = mArray;
             allocArrays(n);
 
+            if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
+                throw new ConcurrentModificationException();
+            }
+
             if (mHashes.length > 0) {
-                if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
+                if (DEBUG) Log.d(TAG, "put: copy 0-" + osize + " to 0");
                 System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
                 System.arraycopy(oarray, 0, mArray, 0, oarray.length);
             }
 
-            freeArrays(ohashes, oarray, mSize);
+            freeArrays(ohashes, oarray, osize);
         }
 
-        if (index < mSize) {
-            if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
+        if (index < osize) {
+            if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (osize-index)
                     + " to " + (index+1));
-            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
+            System.arraycopy(mHashes, index, mHashes, index + 1, osize - index);
             System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
         }
 
+        if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
+            if (osize != mSize || index >= mHashes.length) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
         mHashes[index] = hash;
         mArray[index<<1] = key;
         mArray[(index<<1)+1] = value;
@@ -463,19 +511,22 @@
      */
     public V removeAt(int index) {
         final Object old = mArray[(index << 1) + 1];
-        if (mSize <= 1) {
+        final int osize = mSize;
+        final int nsize;
+        if (osize <= 1) {
             // Now empty.
             if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
-            freeArrays(mHashes, mArray, mSize);
+            freeArrays(mHashes, mArray, osize);
             mHashes = ContainerHelpers.EMPTY_INTS;
             mArray = ContainerHelpers.EMPTY_OBJECTS;
-            mSize = 0;
+            nsize = 0;
         } else {
+            nsize = osize - 1;
             if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
                 // Shrunk enough to reduce size of arrays.  We don't allow it to
                 // shrink smaller than (BASE_SIZE*2) to avoid flapping between
                 // that and BASE_SIZE.
-                final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);
+                final int n = osize > (BASE_SIZE*2) ? (osize + (osize>>1)) : (BASE_SIZE*2);
 
                 if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
 
@@ -483,32 +534,38 @@
                 final Object[] oarray = mArray;
                 allocArrays(n);
 
-                mSize--;
+                if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
+                    throw new ConcurrentModificationException();
+                }
+
                 if (index > 0) {
                     if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
                     System.arraycopy(ohashes, 0, mHashes, 0, index);
                     System.arraycopy(oarray, 0, mArray, 0, index << 1);
                 }
-                if (index < mSize) {
-                    if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize
+                if (index < nsize) {
+                    if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + nsize
                             + " to " + index);
-                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
+                    System.arraycopy(ohashes, index + 1, mHashes, index, nsize - index);
                     System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
-                            (mSize - index) << 1);
+                            (nsize - index) << 1);
                 }
             } else {
-                mSize--;
-                if (index < mSize) {
-                    if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize
+                if (index < nsize) {
+                    if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + nsize
                             + " to " + index);
-                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
+                    System.arraycopy(mHashes, index + 1, mHashes, index, nsize - index);
                     System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
-                            (mSize - index) << 1);
+                            (nsize - index) << 1);
                 }
-                mArray[mSize << 1] = null;
-                mArray[(mSize << 1) + 1] = null;
+                mArray[nsize << 1] = null;
+                mArray[(nsize << 1) + 1] = null;
             }
         }
+        if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
+            throw new ConcurrentModificationException();
+        }
+        mSize = nsize;
         return (V)old;
     }
 
diff --git a/compat/java/android/support/v4/util/TimeUtils.java b/compat/java/android/support/v4/util/TimeUtils.java
index de75846..2d1ce53 100644
--- a/compat/java/android/support/v4/util/TimeUtils.java
+++ b/compat/java/android/support/v4/util/TimeUtils.java
@@ -23,14 +23,14 @@
 import java.io.PrintWriter;
 
 /**
- * Helper for accessing features in {@link android.util.TimeUtils}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.util.TimeUtils}.
  *
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP)
 public final class TimeUtils {
     /** @hide Field length that can hold 999 days of time */
+    @RestrictTo(LIBRARY_GROUP)
     public static final int HUNDRED_DAY_FIELD_LEN = 19;
 
     private static final int SECONDS_PER_MINUTE = 60;
@@ -149,6 +149,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @RestrictTo(LIBRARY_GROUP)
     public static void formatDuration(long duration, StringBuilder builder) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, 0);
@@ -157,6 +158,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @RestrictTo(LIBRARY_GROUP)
     public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, fieldLen);
@@ -165,11 +167,13 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @RestrictTo(LIBRARY_GROUP)
     public static void formatDuration(long duration, PrintWriter pw) {
         formatDuration(duration, pw, 0);
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @RestrictTo(LIBRARY_GROUP)
     public static void formatDuration(long time, long now, PrintWriter pw) {
         if (time == 0) {
             pw.print("--");
diff --git a/compat/java/android/support/v4/view/AccessibilityDelegateCompat.java b/compat/java/android/support/v4/view/AccessibilityDelegateCompat.java
index 920b6e5..66b2cef 100644
--- a/compat/java/android/support/v4/view/AccessibilityDelegateCompat.java
+++ b/compat/java/android/support/v4/view/AccessibilityDelegateCompat.java
@@ -18,15 +18,18 @@
 
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 
 /**
- * Helper for accessing {@link View.AccessibilityDelegate} introduced after
- * API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link AccessibilityDelegate}.
  * <p>
  * <strong>Note:</strong> On platform versions prior to
  * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
@@ -42,103 +45,10 @@
  */
 public class AccessibilityDelegateCompat {
 
-    static interface AccessibilityDelegateImpl {
-        public Object newAccessiblityDelegateDefaultImpl();
-        public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener);
-        public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event);
-        public void onInitializeAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event);
-        public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
-                AccessibilityNodeInfoCompat info);
-        public void onPopulateAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event);
-        public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
-                AccessibilityEvent event);
-        public void sendAccessibilityEvent(Object delegate, View host, int eventType);
-        public void sendAccessibilityEventUnchecked(Object delegate, View host,
-                AccessibilityEvent event);
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
-                View host);
-        public boolean performAccessibilityAction(Object delegate, View host, int action,
-                Bundle args);
-    }
-
-    static class AccessibilityDelegateStubImpl implements AccessibilityDelegateImpl {
-        @Override
-        public Object newAccessiblityDelegateDefaultImpl() {
-            return null;
-        }
-
-        @Override
-        public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener) {
-            return null;
-        }
-
-        @Override
-        public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event) {
-            return false;
-        }
-
-        @Override
-        public void onInitializeAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event) {
-
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
-                AccessibilityNodeInfoCompat info) {
-
-        }
-
-        @Override
-        public void onPopulateAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event) {
-
-        }
-
-        @Override
-        public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
-                AccessibilityEvent event) {
-            return true;
-        }
-
-        @Override
-        public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
-
-        }
-
-        @Override
-        public void sendAccessibilityEventUnchecked(Object delegate, View host,
-                AccessibilityEvent event) {
-
-        }
-
-        @Override
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
-                View host) {
-            return null;
-        }
-
-        @Override
-        public boolean performAccessibilityAction(Object delegate, View host, int action,
-                Bundle args) {
-            return false;
-        }
-    }
-
-    static class AccessibilityDelegateIcsImpl extends AccessibilityDelegateStubImpl {
-        @Override
-        public Object newAccessiblityDelegateDefaultImpl() {
-            return AccessibilityDelegateCompatIcs.newAccessibilityDelegateDefaultImpl();
-        }
-
-        @Override
-        public Object newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat) {
-            return AccessibilityDelegateCompatIcs.newAccessibilityDelegateBridge(
-                    new AccessibilityDelegateCompatIcs.AccessibilityDelegateBridge() {
+    static class AccessibilityDelegateBaseImpl {
+        public AccessibilityDelegate newAccessibilityDelegateBridge(
+                final AccessibilityDelegateCompat compat) {
+            return new AccessibilityDelegate() {
                 @Override
                 public boolean dispatchPopulateAccessibilityEvent(View host,
                         AccessibilityEvent event) {
@@ -151,9 +61,10 @@
                 }
 
                 @Override
-                public void onInitializeAccessibilityNodeInfo(View host, Object info) {
+                public void onInitializeAccessibilityNodeInfo(
+                        View host, AccessibilityNodeInfo info) {
                     compat.onInitializeAccessibilityNodeInfo(host,
-                            new AccessibilityNodeInfoCompat(info));
+                            AccessibilityNodeInfoCompat.wrap(info));
                 }
 
                 @Override
@@ -176,60 +87,28 @@
                 public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
                     compat.sendAccessibilityEventUnchecked(host, event);
                 }
-            });
+            };
         }
 
-        @Override
-        public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event) {
-            return AccessibilityDelegateCompatIcs.dispatchPopulateAccessibilityEvent(delegate,
-                    host, event);
+        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(
+                AccessibilityDelegate delegate, View host) {
+            // Do nothing. Added in API 16.
+            return null;
         }
 
-        @Override
-        public void onInitializeAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event) {
-            AccessibilityDelegateCompatIcs.onInitializeAccessibilityEvent(delegate, host, event);
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
-                AccessibilityNodeInfoCompat info) {
-            AccessibilityDelegateCompatIcs.onInitializeAccessibilityNodeInfo(delegate, host,
-                    info.getInfo());
-        }
-
-        @Override
-        public void onPopulateAccessibilityEvent(Object delegate, View host,
-                AccessibilityEvent event) {
-            AccessibilityDelegateCompatIcs.onPopulateAccessibilityEvent(delegate, host, event);
-        }
-
-        @Override
-        public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
-                AccessibilityEvent event) {
-            return AccessibilityDelegateCompatIcs.onRequestSendAccessibilityEvent(delegate, host,
-                    child, event);
-        }
-
-        @Override
-        public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
-            AccessibilityDelegateCompatIcs.sendAccessibilityEvent(delegate, host, eventType);
-        }
-
-        @Override
-        public void sendAccessibilityEventUnchecked(Object delegate, View host,
-                AccessibilityEvent event) {
-            AccessibilityDelegateCompatIcs.sendAccessibilityEventUnchecked(delegate, host, event);
+        public boolean performAccessibilityAction(AccessibilityDelegate delegate, View host,
+                int action, Bundle args) {
+            // Do nothing. Added in API 16.
+            return false;
         }
     }
 
-    static class AccessibilityDelegateJellyBeanImpl extends AccessibilityDelegateIcsImpl {
+    @RequiresApi(16)
+    static class AccessibilityDelegateApi16Impl extends AccessibilityDelegateBaseImpl {
         @Override
-        public Object newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat) {
-            return AccessibilityDelegateCompatJellyBean.newAccessibilityDelegateBridge(
-                    new AccessibilityDelegateCompatJellyBean
-                            .AccessibilityDelegateBridgeJellyBean() {
+        public AccessibilityDelegate newAccessibilityDelegateBridge(
+                final AccessibilityDelegateCompat compat) {
+            return new AccessibilityDelegate()  {
                 @Override
                 public boolean dispatchPopulateAccessibilityEvent(View host,
                         AccessibilityEvent event) {
@@ -242,9 +121,10 @@
                 }
 
                 @Override
-                public void onInitializeAccessibilityNodeInfo(View host, Object info) {
+                public void onInitializeAccessibilityNodeInfo(
+                        View host, AccessibilityNodeInfo info) {
                     compat.onInitializeAccessibilityNodeInfo(host,
-                            new AccessibilityNodeInfoCompat(info));
+                            AccessibilityNodeInfoCompat.wrap(info));
                 }
 
                 @Override
@@ -269,24 +149,24 @@
                 }
 
                 @Override
-                public Object getAccessibilityNodeProvider(View host) {
+                public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
                     AccessibilityNodeProviderCompat provider =
                         compat.getAccessibilityNodeProvider(host);
-                    return (provider != null) ? provider.getProvider() : null;
+                    return (provider != null)
+                            ? (AccessibilityNodeProvider) provider.getProvider() : null;
                 }
 
                 @Override
                 public boolean performAccessibilityAction(View host, int action, Bundle args) {
                     return compat.performAccessibilityAction(host, action, args);
                 }
-            });
+            };
         }
 
         @Override
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
-                View host) {
-            Object provider = AccessibilityDelegateCompatJellyBean.getAccessibilityNodeProvider(
-                    delegate, host);
+        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(
+                AccessibilityDelegate delegate, View host) {
+            Object provider = delegate.getAccessibilityNodeProvider(host);
             if (provider != null) {
                 return new AccessibilityNodeProviderCompat(provider);
             }
@@ -294,40 +174,37 @@
         }
 
         @Override
-        public boolean performAccessibilityAction(Object delegate, View host, int action,
-                Bundle args) {
-            return AccessibilityDelegateCompatJellyBean.performAccessibilityAction(delegate,
-                    host, action, args);
+        public boolean performAccessibilityAction(AccessibilityDelegate delegate, View host,
+                int action, Bundle args) {
+            return delegate.performAccessibilityAction(host, action, args);
         }
     }
 
-    private static final AccessibilityDelegateImpl IMPL;
-    private static final Object DEFAULT_DELEGATE;
+    private static final AccessibilityDelegateBaseImpl IMPL;
+    private static final AccessibilityDelegate DEFAULT_DELEGATE;
 
     static {
         if (Build.VERSION.SDK_INT >= 16) { // JellyBean
-            IMPL = new AccessibilityDelegateJellyBeanImpl();
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new AccessibilityDelegateIcsImpl();
+            IMPL = new AccessibilityDelegateApi16Impl();
         } else {
-            IMPL = new AccessibilityDelegateStubImpl();
+            IMPL = new AccessibilityDelegateBaseImpl();
         }
-        DEFAULT_DELEGATE = IMPL.newAccessiblityDelegateDefaultImpl();
+        DEFAULT_DELEGATE = new AccessibilityDelegate();
     }
 
-    final Object mBridge;
+    final AccessibilityDelegate mBridge;
 
     /**
      * Creates a new instance.
      */
     public AccessibilityDelegateCompat() {
-        mBridge = IMPL.newAccessiblityDelegateBridge(this);
+        mBridge = IMPL.newAccessibilityDelegateBridge(this);
     }
 
     /**
      * @return The wrapped bridge implementation.
      */
-    Object getBridge() {
+    AccessibilityDelegate getBridge() {
         return mBridge;
     }
 
@@ -346,7 +223,7 @@
      * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
      */
     public void sendAccessibilityEvent(View host, int eventType) {
-        IMPL.sendAccessibilityEvent(DEFAULT_DELEGATE, host, eventType);
+        DEFAULT_DELEGATE.sendAccessibilityEvent(host, eventType);
     }
 
     /**
@@ -368,7 +245,7 @@
      *      View#sendAccessibilityEventUnchecked(AccessibilityEvent)
      */
     public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-        IMPL.sendAccessibilityEventUnchecked(DEFAULT_DELEGATE, host, event);
+        DEFAULT_DELEGATE.sendAccessibilityEventUnchecked(host, event);
     }
 
     /**
@@ -389,7 +266,7 @@
      *      View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
      */
     public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-        return IMPL.dispatchPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event);
+        return DEFAULT_DELEGATE.dispatchPopulateAccessibilityEvent(host, event);
     }
 
     /**
@@ -409,7 +286,7 @@
      *      ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
      */
     public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-        IMPL.onPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event);
+        DEFAULT_DELEGATE.onPopulateAccessibilityEvent(host, event);
     }
 
     /**
@@ -429,7 +306,7 @@
      *      ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
      */
     public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-        IMPL.onInitializeAccessibilityEvent(DEFAULT_DELEGATE, host, event);
+        DEFAULT_DELEGATE.onInitializeAccessibilityEvent(host, event);
     }
 
     /**
@@ -448,7 +325,8 @@
      *      ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
      */
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-        IMPL.onInitializeAccessibilityNodeInfo(DEFAULT_DELEGATE, host, info);
+        DEFAULT_DELEGATE.onInitializeAccessibilityNodeInfo(
+                host, info.unwrap());
     }
 
     /**
@@ -472,7 +350,7 @@
      */
     public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
             AccessibilityEvent event) {
-        return IMPL.onRequestSendAccessibilityEvent(DEFAULT_DELEGATE, host, child, event);
+        return DEFAULT_DELEGATE.onRequestSendAccessibilityEvent(host, child, event);
     }
 
     /**
diff --git a/compat/java/android/support/v4/view/GestureDetectorCompat.java b/compat/java/android/support/v4/view/GestureDetectorCompat.java
index 3809605..fbecce9 100644
--- a/compat/java/android/support/v4/view/GestureDetectorCompat.java
+++ b/compat/java/android/support/v4/view/GestureDetectorCompat.java
@@ -233,8 +233,8 @@
             mVelocityTracker.addMovement(ev);
 
             final boolean pointerUp =
-                    (action & MotionEventCompat.ACTION_MASK) == MotionEventCompat.ACTION_POINTER_UP;
-            final int skipIndex = pointerUp ? MotionEventCompat.getActionIndex(ev) : -1;
+                    (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
+            final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
 
             // Determine focal point
             float sumX = 0, sumY = 0;
@@ -250,160 +250,161 @@
 
             boolean handled = false;
 
-            switch (action & MotionEventCompat.ACTION_MASK) {
-            case MotionEventCompat.ACTION_POINTER_DOWN:
-                mDownFocusX = mLastFocusX = focusX;
-                mDownFocusY = mLastFocusY = focusY;
-                // Cancel long press and taps
-                cancelTaps();
-                break;
+            switch (action & MotionEvent.ACTION_MASK) {
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    mDownFocusX = mLastFocusX = focusX;
+                    mDownFocusY = mLastFocusY = focusY;
+                    // Cancel long press and taps
+                    cancelTaps();
+                    break;
 
-            case MotionEventCompat.ACTION_POINTER_UP:
-                mDownFocusX = mLastFocusX = focusX;
-                mDownFocusY = mLastFocusY = focusY;
+                case MotionEvent.ACTION_POINTER_UP:
+                    mDownFocusX = mLastFocusX = focusX;
+                    mDownFocusY = mLastFocusY = focusY;
 
-                // Check the dot product of current velocities.
-                // If the pointer that left was opposing another velocity vector, clear.
-                mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
-                final int upIndex = MotionEventCompat.getActionIndex(ev);
-                final int id1 = ev.getPointerId(upIndex);
-                final float x1 = VelocityTrackerCompat.getXVelocity(mVelocityTracker, id1);
-                final float y1 = VelocityTrackerCompat.getYVelocity(mVelocityTracker, id1);
-                for (int i = 0; i < count; i++) {
-                    if (i == upIndex) continue;
+                    // Check the dot product of current velocities.
+                    // If the pointer that left was opposing another velocity vector, clear.
+                    mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
+                    final int upIndex = ev.getActionIndex();
+                    final int id1 = ev.getPointerId(upIndex);
+                    final float x1 = mVelocityTracker.getXVelocity(id1);
+                    final float y1 = mVelocityTracker.getYVelocity(id1);
+                    for (int i = 0; i < count; i++) {
+                        if (i == upIndex) continue;
 
-                    final int id2 = ev.getPointerId(i);
-                    final float x = x1 * VelocityTrackerCompat.getXVelocity(mVelocityTracker, id2);
-                    final float y = y1 * VelocityTrackerCompat.getYVelocity(mVelocityTracker, id2);
+                        final int id2 = ev.getPointerId(i);
+                        final float x = x1 * mVelocityTracker.getXVelocity(id2);
+                        final float y = y1 * mVelocityTracker.getYVelocity(id2);
 
-                    final float dot = x + y;
-                    if (dot < 0) {
-                        mVelocityTracker.clear();
+                        final float dot = x + y;
+                        if (dot < 0) {
+                            mVelocityTracker.clear();
+                            break;
+                        }
+                    }
+                    break;
+
+                case MotionEvent.ACTION_DOWN:
+                    if (mDoubleTapListener != null) {
+                        boolean hadTapMessage = mHandler.hasMessages(TAP);
+                        if (hadTapMessage) mHandler.removeMessages(TAP);
+                        if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null)
+                                && hadTapMessage && isConsideredDoubleTap(
+                                        mCurrentDownEvent, mPreviousUpEvent, ev)) {
+                            // This is a second tap
+                            mIsDoubleTapping = true;
+                            // Give a callback with the first tap of the double-tap
+                            handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
+                            // Give a callback with down event of the double-tap
+                            handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+                        } else {
+                            // This is a first tap
+                            mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
+                        }
+                    }
+
+                    mDownFocusX = mLastFocusX = focusX;
+                    mDownFocusY = mLastFocusY = focusY;
+                    if (mCurrentDownEvent != null) {
+                        mCurrentDownEvent.recycle();
+                    }
+                    mCurrentDownEvent = MotionEvent.obtain(ev);
+                    mAlwaysInTapRegion = true;
+                    mAlwaysInBiggerTapRegion = true;
+                    mStillDown = true;
+                    mInLongPress = false;
+                    mDeferConfirmSingleTap = false;
+
+                    if (mIsLongpressEnabled) {
+                        mHandler.removeMessages(LONG_PRESS);
+                        mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+                                + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
+                    }
+                    mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
+                            mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
+                    handled |= mListener.onDown(ev);
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    if (mInLongPress) {
                         break;
                     }
-                }
-                break;
-
-            case MotionEvent.ACTION_DOWN:
-                if (mDoubleTapListener != null) {
-                    boolean hadTapMessage = mHandler.hasMessages(TAP);
-                    if (hadTapMessage) mHandler.removeMessages(TAP);
-                    if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
-                            isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
-                        // This is a second tap
-                        mIsDoubleTapping = true;
-                        // Give a callback with the first tap of the double-tap
-                        handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
-                        // Give a callback with down event of the double-tap
+                    final float scrollX = mLastFocusX - focusX;
+                    final float scrollY = mLastFocusY - focusY;
+                    if (mIsDoubleTapping) {
+                        // Give the move events of the double-tap
                         handled |= mDoubleTapListener.onDoubleTapEvent(ev);
-                    } else {
-                        // This is a first tap
-                        mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
-                    }
-                }
-
-                mDownFocusX = mLastFocusX = focusX;
-                mDownFocusY = mLastFocusY = focusY;
-                if (mCurrentDownEvent != null) {
-                    mCurrentDownEvent.recycle();
-                }
-                mCurrentDownEvent = MotionEvent.obtain(ev);
-                mAlwaysInTapRegion = true;
-                mAlwaysInBiggerTapRegion = true;
-                mStillDown = true;
-                mInLongPress = false;
-                mDeferConfirmSingleTap = false;
-
-                if (mIsLongpressEnabled) {
-                    mHandler.removeMessages(LONG_PRESS);
-                    mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
-                            + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
-                }
-                mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
-                handled |= mListener.onDown(ev);
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (mInLongPress) {
-                    break;
-                }
-                final float scrollX = mLastFocusX - focusX;
-                final float scrollY = mLastFocusY - focusY;
-                if (mIsDoubleTapping) {
-                    // Give the move events of the double-tap
-                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
-                } else if (mAlwaysInTapRegion) {
-                    final int deltaX = (int) (focusX - mDownFocusX);
-                    final int deltaY = (int) (focusY - mDownFocusY);
-                    int distance = (deltaX * deltaX) + (deltaY * deltaY);
-                    if (distance > mTouchSlopSquare) {
+                    } else if (mAlwaysInTapRegion) {
+                        final int deltaX = (int) (focusX - mDownFocusX);
+                        final int deltaY = (int) (focusY - mDownFocusY);
+                        int distance = (deltaX * deltaX) + (deltaY * deltaY);
+                        if (distance > mTouchSlopSquare) {
+                            handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
+                            mLastFocusX = focusX;
+                            mLastFocusY = focusY;
+                            mAlwaysInTapRegion = false;
+                            mHandler.removeMessages(TAP);
+                            mHandler.removeMessages(SHOW_PRESS);
+                            mHandler.removeMessages(LONG_PRESS);
+                        }
+                        if (distance > mTouchSlopSquare) {
+                            mAlwaysInBiggerTapRegion = false;
+                        }
+                    } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
                         handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                         mLastFocusX = focusX;
                         mLastFocusY = focusY;
-                        mAlwaysInTapRegion = false;
+                    }
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                    mStillDown = false;
+                    MotionEvent currentUpEvent = MotionEvent.obtain(ev);
+                    if (mIsDoubleTapping) {
+                        // Finally, give the up event of the double-tap
+                        handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+                    } else if (mInLongPress) {
                         mHandler.removeMessages(TAP);
-                        mHandler.removeMessages(SHOW_PRESS);
-                        mHandler.removeMessages(LONG_PRESS);
-                    }
-                    if (distance > mTouchSlopSquare) {
-                        mAlwaysInBiggerTapRegion = false;
-                    }
-                } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
-                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
-                    mLastFocusX = focusX;
-                    mLastFocusY = focusY;
-                }
-                break;
+                        mInLongPress = false;
+                    } else if (mAlwaysInTapRegion) {
+                        handled = mListener.onSingleTapUp(ev);
+                        if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
+                            mDoubleTapListener.onSingleTapConfirmed(ev);
+                        }
+                    } else {
+                        // A fling must travel the minimum tap distance
+                        final VelocityTracker velocityTracker = mVelocityTracker;
+                        final int pointerId = ev.getPointerId(0);
+                        velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
+                        final float velocityY = velocityTracker.getYVelocity(pointerId);
+                        final float velocityX = velocityTracker.getXVelocity(pointerId);
 
-            case MotionEvent.ACTION_UP:
-                mStillDown = false;
-                MotionEvent currentUpEvent = MotionEvent.obtain(ev);
-                if (mIsDoubleTapping) {
-                    // Finally, give the up event of the double-tap
-                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
-                } else if (mInLongPress) {
-                    mHandler.removeMessages(TAP);
-                    mInLongPress = false;
-                } else if (mAlwaysInTapRegion) {
-                    handled = mListener.onSingleTapUp(ev);
-                    if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
-                        mDoubleTapListener.onSingleTapConfirmed(ev);
+                        if ((Math.abs(velocityY) > mMinimumFlingVelocity)
+                                || (Math.abs(velocityX) > mMinimumFlingVelocity)) {
+                            handled = mListener.onFling(
+                                    mCurrentDownEvent, ev, velocityX, velocityY);
+                        }
                     }
-                } else {
-                    // A fling must travel the minimum tap distance
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    final int pointerId = ev.getPointerId(0);
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
-                    final float velocityY = VelocityTrackerCompat.getYVelocity(
-                            velocityTracker, pointerId);
-                    final float velocityX = VelocityTrackerCompat.getXVelocity(
-                            velocityTracker, pointerId);
-
-                    if ((Math.abs(velocityY) > mMinimumFlingVelocity)
-                            || (Math.abs(velocityX) > mMinimumFlingVelocity)){
-                        handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
+                    if (mPreviousUpEvent != null) {
+                        mPreviousUpEvent.recycle();
                     }
-                }
-                if (mPreviousUpEvent != null) {
-                    mPreviousUpEvent.recycle();
-                }
-                // Hold the event we obtained above - listeners may have changed the original.
-                mPreviousUpEvent = currentUpEvent;
-                if (mVelocityTracker != null) {
-                    // This may have been cleared when we called out to the
-                    // application above.
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
-                mIsDoubleTapping = false;
-                mDeferConfirmSingleTap = false;
-                mHandler.removeMessages(SHOW_PRESS);
-                mHandler.removeMessages(LONG_PRESS);
-                break;
+                    // Hold the event we obtained above - listeners may have changed the original.
+                    mPreviousUpEvent = currentUpEvent;
+                    if (mVelocityTracker != null) {
+                        // This may have been cleared when we called out to the
+                        // application above.
+                        mVelocityTracker.recycle();
+                        mVelocityTracker = null;
+                    }
+                    mIsDoubleTapping = false;
+                    mDeferConfirmSingleTap = false;
+                    mHandler.removeMessages(SHOW_PRESS);
+                    mHandler.removeMessages(LONG_PRESS);
+                    break;
 
-            case MotionEvent.ACTION_CANCEL:
-                cancel();
-                break;
+                case MotionEvent.ACTION_CANCEL:
+                    cancel();
+                    break;
             }
 
             return handled;
diff --git a/compat/java/android/support/v4/view/GravityCompat.java b/compat/java/android/support/v4/view/GravityCompat.java
index cfcdd50..c729a61 100644
--- a/compat/java/android/support/v4/view/GravityCompat.java
+++ b/compat/java/android/support/v4/view/GravityCompat.java
@@ -17,83 +17,15 @@
 
 package android.support.v4.view;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.graphics.Rect;
-import android.os.Build;
 import android.view.Gravity;
 
 /**
  * Compatibility shim for accessing newer functionality from {@link android.view.Gravity}.
  */
 public final class GravityCompat {
-    interface GravityCompatImpl {
-        int getAbsoluteGravity(int gravity, int layoutDirection);
-        void apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection);
-        void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj,
-                Rect outRect, int layoutDirection);
-        void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection);
-    }
-
-    static class GravityCompatImplBase implements GravityCompatImpl {
-        @Override
-        public int getAbsoluteGravity(int gravity, int layoutDirection) {
-            // Just strip off the relative bit to get LEFT/RIGHT.
-            return gravity & ~RELATIVE_LAYOUT_DIRECTION;
-        }
-
-        @Override
-        public void apply(int gravity, int w, int h, Rect container, Rect outRect,
-                int layoutDirection) {
-            Gravity.apply(gravity, w, h, container, outRect);
-        }
-
-        @Override
-        public void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj,
-                Rect outRect, int layoutDirection) {
-            Gravity.apply(gravity, w, h, container, xAdj, yAdj, outRect);
-        }
-
-        @Override
-        public void applyDisplay(int gravity, Rect display, Rect inoutObj,
-                int layoutDirection) {
-            Gravity.applyDisplay(gravity, display, inoutObj);
-        }
-    }
-
-    static class GravityCompatImplJellybeanMr1 implements GravityCompatImpl {
-        @Override
-        public int getAbsoluteGravity(int gravity, int layoutDirection) {
-            return GravityCompatJellybeanMr1.getAbsoluteGravity(gravity, layoutDirection);
-        }
-
-        @Override
-        public void apply(int gravity, int w, int h, Rect container, Rect outRect,
-                int layoutDirection) {
-            GravityCompatJellybeanMr1.apply(gravity, w, h, container, outRect, layoutDirection);
-        }
-
-        @Override
-        public void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj,
-                Rect outRect, int layoutDirection) {
-            GravityCompatJellybeanMr1.apply(gravity, w, h, container, xAdj, yAdj, outRect,
-                    layoutDirection);
-        }
-
-        @Override
-        public void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
-            GravityCompatJellybeanMr1.applyDisplay(gravity, display, inoutObj, layoutDirection);
-        }
-    }
-
-    static final GravityCompatImpl IMPL;
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 17) {
-            IMPL = new GravityCompatImplJellybeanMr1();
-        } else {
-            IMPL = new GravityCompatImplBase();
-        }
-    }
-
     /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
      * absolute LEFT/RIGHT).
      */
@@ -129,7 +61,11 @@
      */
     public static void apply(int gravity, int w, int h, Rect container,
             Rect outRect, int layoutDirection) {
-        IMPL.apply(gravity, w, h, container, outRect, layoutDirection);
+        if (SDK_INT >= 17) {
+            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
+        } else {
+            Gravity.apply(gravity, w, h, container, outRect);
+        }
     }
 
     /**
@@ -159,7 +95,11 @@
      */
     public static void apply(int gravity, int w, int h, Rect container,
             int xAdj, int yAdj, Rect outRect, int layoutDirection) {
-        IMPL.apply(gravity, w, h, container, xAdj, yAdj, outRect, layoutDirection);
+        if (SDK_INT >= 17) {
+            Gravity.apply(gravity, w, h, container, xAdj, yAdj, outRect, layoutDirection);
+        } else {
+            Gravity.apply(gravity, w, h, container, xAdj, yAdj, outRect);
+        }
     }
 
     /**
@@ -183,7 +123,11 @@
      * @see ViewCompat#LAYOUT_DIRECTION_RTL
      */
     public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
-        IMPL.applyDisplay(gravity, display, inoutObj, layoutDirection);
+        if (SDK_INT >= 17) {
+            Gravity.applyDisplay(gravity, display, inoutObj, layoutDirection);
+        } else {
+            Gravity.applyDisplay(gravity, display, inoutObj);
+        }
     }
 
     /**
@@ -198,7 +142,12 @@
      * @return gravity converted to absolute (horizontal) values.
      */
     public static int getAbsoluteGravity(int gravity, int layoutDirection) {
-        return IMPL.getAbsoluteGravity(gravity, layoutDirection);
+        if (SDK_INT >= 17) {
+            return Gravity.getAbsoluteGravity(gravity, layoutDirection);
+        } else {
+            // Just strip off the relative bit to get LEFT/RIGHT.
+            return gravity & ~RELATIVE_LAYOUT_DIRECTION;
+        }
     }
 
     private GravityCompat() {}
diff --git a/compat/java/android/support/v4/view/InputDeviceCompat.java b/compat/java/android/support/v4/view/InputDeviceCompat.java
index 6db92e8..58c9c04 100644
--- a/compat/java/android/support/v4/view/InputDeviceCompat.java
+++ b/compat/java/android/support/v4/view/InputDeviceCompat.java
@@ -188,6 +188,14 @@
     public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
 
     /**
+     * The input source is a rotating encoder device whose motions should be interpreted as akin to
+     * those of a scroll wheel.
+     *
+     * @see #SOURCE_CLASS_NONE
+     */
+    public static final int SOURCE_ROTARY_ENCODER = 0x00400000 | SOURCE_CLASS_NONE;
+
+    /**
      * The input source is a joystick.
      * (It may also be a {@link #SOURCE_GAMEPAD}).
      *
diff --git a/compat/java/android/support/v4/view/KeyEventCompat.java b/compat/java/android/support/v4/view/KeyEventCompat.java
index 471cbc9..85fe686 100644
--- a/compat/java/android/support/v4/view/KeyEventCompat.java
+++ b/compat/java/android/support/v4/view/KeyEventCompat.java
@@ -20,139 +20,55 @@
 import android.view.View;
 
 /**
- * Helper for accessing features in {@link KeyEvent} introduced after
- * API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link KeyEvent}.
+ *
+ * @deprecated Use {@link KeyEvent} directly.
  */
+@Deprecated
 public final class KeyEventCompat {
     /**
-     * Interface for the full API.
+     * @deprecated Call {@link KeyEvent#normalizeMetaState(int)} directly. This method will
+     * be removed in a future release.
      */
-    interface KeyEventVersionImpl {
-        int normalizeMetaState(int metaState);
-        boolean metaStateHasModifiers(int metaState, int modifiers);
-        boolean metaStateHasNoModifiers(int metaState);
-        boolean isCtrlPressed(KeyEvent event);
-    }
-
-    /**
-     * Interface implementation that doesn't use anything about v4 APIs.
-     */
-    static class BaseKeyEventVersionImpl implements KeyEventVersionImpl {
-        private static final int META_MODIFIER_MASK =
-                KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON
-                | KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON
-                | KeyEvent.META_SYM_ON;
-
-        // Mask of all lock key meta states.
-        private static final int META_ALL_MASK = META_MODIFIER_MASK;
-
-        private static int metaStateFilterDirectionalModifiers(int metaState,
-                int modifiers, int basic, int left, int right) {
-            final boolean wantBasic = (modifiers & basic) != 0;
-            final int directional = left | right;
-            final boolean wantLeftOrRight = (modifiers & directional) != 0;
-
-            if (wantBasic) {
-                if (wantLeftOrRight) {
-                    throw new IllegalArgumentException("bad arguments");
-                }
-                return metaState & ~directional;
-            } else if (wantLeftOrRight) {
-                return metaState & ~basic;
-            } else {
-                return metaState;
-            }
-        }
-
-        @Override
-        public int normalizeMetaState(int metaState) {
-            if ((metaState & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON)) != 0) {
-                metaState |= KeyEvent.META_SHIFT_ON;
-            }
-            if ((metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0) {
-                metaState |= KeyEvent.META_ALT_ON;
-            }
-            return metaState & META_ALL_MASK;
-        }
- 
-        @Override
-        public boolean metaStateHasModifiers(int metaState, int modifiers) {
-            metaState = normalizeMetaState(metaState) & META_MODIFIER_MASK;
-            metaState = metaStateFilterDirectionalModifiers(metaState, modifiers,
-                    KeyEvent.META_SHIFT_ON, KeyEvent.META_SHIFT_LEFT_ON, KeyEvent.META_SHIFT_RIGHT_ON);
-            metaState = metaStateFilterDirectionalModifiers(metaState, modifiers,
-                    KeyEvent.META_ALT_ON, KeyEvent.META_ALT_LEFT_ON, KeyEvent.META_ALT_RIGHT_ON);
-            return metaState == modifiers;
-        }
-
-        @Override
-        public boolean metaStateHasNoModifiers(int metaState) {
-            return (normalizeMetaState(metaState) & META_MODIFIER_MASK) == 0;
-        }
-
-        @Override
-        public boolean isCtrlPressed(KeyEvent event) {
-            return false;
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least v11 APIs.
-     */
-    static class HoneycombKeyEventVersionImpl extends BaseKeyEventVersionImpl {
-        @Override
-        public int normalizeMetaState(int metaState) {
-            return KeyEventCompatHoneycomb.normalizeMetaState(metaState);
-        }
-        
-        @Override
-        public boolean metaStateHasModifiers(int metaState, int modifiers) {
-            return KeyEventCompatHoneycomb.metaStateHasModifiers(metaState, modifiers);
-        }
-
-        @Override
-        public boolean metaStateHasNoModifiers(int metaState) {
-            return KeyEventCompatHoneycomb.metaStateHasNoModifiers(metaState);
-        }
-
-        @Override
-        public boolean isCtrlPressed(KeyEvent event) {
-            return KeyEventCompatHoneycomb.isCtrlPressed(event);
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final KeyEventVersionImpl IMPL;
-    static {
-        if (android.os.Build.VERSION.SDK_INT >= 11) {
-            IMPL = new HoneycombKeyEventVersionImpl();
-        } else {
-            IMPL = new BaseKeyEventVersionImpl();
-        }
-    }
-
-    // -------------------------------------------------------------------
-
+    @Deprecated
     public static int normalizeMetaState(int metaState) {
-        return IMPL.normalizeMetaState(metaState);
+        return KeyEvent.normalizeMetaState(metaState);
     }
 
+    /**
+     * @deprecated Call {@link KeyEvent#metaStateHasModifiers(int, int)} directly. This method will
+     * be removed in a future release.
+     */
+    @Deprecated
     public static boolean metaStateHasModifiers(int metaState, int modifiers) {
-        return IMPL.metaStateHasModifiers(metaState, modifiers);
+        return KeyEvent.metaStateHasModifiers(metaState, modifiers);
     }
 
+    /**
+     * @deprecated Call {@link KeyEvent#metaStateHasNoModifiers(int)} directly. This method will be
+     * removed in a future release.
+     */
+    @Deprecated
     public static boolean metaStateHasNoModifiers(int metaState) {
-        return IMPL.metaStateHasNoModifiers(metaState);
+        return KeyEvent.metaStateHasNoModifiers(metaState);
     }
 
+    /**
+     * @deprecated Call {@link KeyEvent#hasModifiers(int)} directly. This method will be removed in
+     * a future release.
+     */
+    @Deprecated
     public static boolean hasModifiers(KeyEvent event, int modifiers) {
-        return IMPL.metaStateHasModifiers(event.getMetaState(), modifiers);
+        return event.hasModifiers(modifiers);
     }
 
+    /**
+     * @deprecated Call {@link KeyEvent#hasNoModifiers()} directly. This method will be removed in a
+     * future release.
+     */
+    @Deprecated
     public static boolean hasNoModifiers(KeyEvent event) {
-        return IMPL.metaStateHasNoModifiers(event.getMetaState());
+        return event.hasNoModifiers();
     }
 
     /**
@@ -190,11 +106,16 @@
     @Deprecated
     public static boolean dispatch(KeyEvent event, KeyEvent.Callback receiver, Object state,
                 Object target) {
-        return event.dispatch(receiver, (KeyEvent.DispatcherState)state, target);
+        return event.dispatch(receiver, (KeyEvent.DispatcherState) state, target);
     }
 
+    /**
+     * @deprecated Call {@link KeyEvent#isCtrlPressed()} directly. This method will be removed
+     * in a future release.
+     */
+    @Deprecated
     public static boolean isCtrlPressed(KeyEvent event) {
-        return IMPL.isCtrlPressed(event);
+        return event.isCtrlPressed();
     }
 
     private KeyEventCompat() {}
diff --git a/compat/java/android/support/v4/view/LayoutInflaterCompat.java b/compat/java/android/support/v4/view/LayoutInflaterCompat.java
index d61896d..0cb2969 100644
--- a/compat/java/android/support/v4/view/LayoutInflaterCompat.java
+++ b/compat/java/android/support/v4/view/LayoutInflaterCompat.java
@@ -16,55 +16,131 @@
 
 package android.support.v4.view;
 
+import android.content.Context;
 import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.AttributeSet;
+import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.View;
+
+import java.lang.reflect.Field;
 
 /**
- * Helper for accessing features in {@link android.view.LayoutInflater}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link LayoutInflater}.
  */
 public final class LayoutInflaterCompat {
+    private static final String TAG = "LayoutInflaterCompatHC";
 
-    interface LayoutInflaterCompatImpl {
-        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory);
-        public LayoutInflaterFactory getFactory(LayoutInflater layoutInflater);
-    }
+    private static Field sLayoutInflaterFactory2Field;
+    private static boolean sCheckedField;
 
-    static class LayoutInflaterCompatImplBase implements LayoutInflaterCompatImpl {
-        @Override
-        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory) {
-            LayoutInflaterCompatBase.setFactory(layoutInflater, factory);
+    @SuppressWarnings("deprecation")
+    static class Factory2Wrapper implements LayoutInflater.Factory2 {
+        final LayoutInflaterFactory mDelegateFactory;
+
+        Factory2Wrapper(LayoutInflaterFactory delegateFactory) {
+            mDelegateFactory = delegateFactory;
         }
 
         @Override
-        public LayoutInflaterFactory getFactory(LayoutInflater layoutInflater) {
-            return LayoutInflaterCompatBase.getFactory(layoutInflater);
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
+            return mDelegateFactory.onCreateView(null, name, context, attrs);
         }
-    }
 
-    static class LayoutInflaterCompatImplV11 extends LayoutInflaterCompatImplBase {
         @Override
-        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory) {
-            LayoutInflaterCompatHC.setFactory(layoutInflater, factory);
+        public View onCreateView(View parent, String name, Context context,
+                AttributeSet attributeSet) {
+            return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
         }
-    }
 
-    static class LayoutInflaterCompatImplV21 extends LayoutInflaterCompatImplV11 {
         @Override
-        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory) {
-            LayoutInflaterCompatLollipop.setFactory(layoutInflater, factory);
+        public String toString() {
+            return getClass().getName() + "{" + mDelegateFactory + "}";
         }
     }
 
-    static final LayoutInflaterCompatImpl IMPL;
+    /**
+     * For APIs < 21, there was a framework bug that prevented a LayoutInflater's
+     * Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater
+     * that already had a Factory2 registered. We work around that bug here. If we can't we
+     * log an error.
+     */
+    static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
+        if (!sCheckedField) {
+            try {
+                sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2");
+                sLayoutInflaterFactory2Field.setAccessible(true);
+            } catch (NoSuchFieldException e) {
+                Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class "
+                        + LayoutInflater.class.getName()
+                        + "; inflation may have unexpected results.", e);
+            }
+            sCheckedField = true;
+        }
+        if (sLayoutInflaterFactory2Field != null) {
+            try {
+                sLayoutInflaterFactory2Field.set(inflater, factory);
+            } catch (IllegalAccessException e) {
+                Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater "
+                        + inflater + "; inflation may have unexpected results.", e);
+            }
+        }
+    }
+
+    static class LayoutInflaterCompatBaseImpl {
+        @SuppressWarnings("deprecation")
+        public void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
+            final LayoutInflater.Factory2 factory2 = factory != null
+                    ? new Factory2Wrapper(factory) : null;
+            setFactory2(inflater, factory2);
+        }
+
+        public void setFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
+            inflater.setFactory2(factory);
+
+            final LayoutInflater.Factory f = inflater.getFactory();
+            if (f instanceof LayoutInflater.Factory2) {
+                // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
+                // We will now try and force set the merged factory to mFactory2
+                forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
+            } else {
+                // Else, we will force set the original wrapped Factory2
+                forceSetFactory2(inflater, factory);
+            }
+        }
+
+        @SuppressWarnings("deprecation")
+        public LayoutInflaterFactory getFactory(LayoutInflater inflater) {
+            LayoutInflater.Factory factory = inflater.getFactory();
+            if (factory instanceof Factory2Wrapper) {
+                return ((Factory2Wrapper) factory).mDelegateFactory;
+            }
+            return null;
+        }
+    }
+
+    @RequiresApi(21)
+    static class LayoutInflaterCompatApi21Impl extends LayoutInflaterCompatBaseImpl {
+        @SuppressWarnings("deprecation")
+        @Override
+        public void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
+            inflater.setFactory2(factory != null ? new Factory2Wrapper(factory) : null);
+        }
+
+        @Override
+        public void setFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
+            inflater.setFactory2(factory);
+        }
+    }
+
+    static final LayoutInflaterCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new LayoutInflaterCompatImplV21();
-        } else if (version >= 11) {
-            IMPL = new LayoutInflaterCompatImplV11();
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new LayoutInflaterCompatApi21Impl();
         } else {
-            IMPL = new LayoutInflaterCompatImplBase();
+            IMPL = new LayoutInflaterCompatBaseImpl();
         }
     }
 
@@ -80,12 +156,29 @@
      * after setting, you can not change the factory.
      *
      * @see LayoutInflater#setFactory(android.view.LayoutInflater.Factory)
+     *
+     * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} instead to set
+     * and {@link LayoutInflater#getFactory2()} to get the factory.
      */
-    public static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
+    @Deprecated
+    public static void setFactory(
+            @NonNull LayoutInflater inflater, @NonNull LayoutInflaterFactory factory) {
         IMPL.setFactory(inflater, factory);
     }
 
     /**
+     * Attach a custom {@link LayoutInflater.Factory2} for creating views while using
+     * this {@link LayoutInflater}. This must not be null, and can only be set once;
+     * after setting, you can not change the factory.
+     *
+     * @see LayoutInflater#setFactory2(android.view.LayoutInflater.Factory2)
+     */
+    public static void setFactory2(
+            @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
+        IMPL.setFactory2(inflater, factory);
+    }
+
+    /**
      * Return the current {@link LayoutInflaterFactory} (or null). This is
      * called on each element name. If the factory returns a View, add that
      * to the hierarchy. If it returns null, proceed to call onCreateView(name).
@@ -94,9 +187,12 @@
      * {@link LayoutInflater}. Will be {@code null} if the inflater does not
      * have a {@link LayoutInflaterFactory} but a raw {@link LayoutInflater.Factory}.
      * @see LayoutInflater#getFactory()
+     *
+     * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} to set and
+     * {@link LayoutInflater#getFactory2()} to get the factory.
      */
+    @Deprecated
     public static LayoutInflaterFactory getFactory(LayoutInflater inflater) {
         return IMPL.getFactory(inflater);
     }
-
 }
diff --git a/compat/java/android/support/v4/view/LayoutInflaterFactory.java b/compat/java/android/support/v4/view/LayoutInflaterFactory.java
new file mode 100644
index 0000000..2ee4704
--- /dev/null
+++ b/compat/java/android/support/v4/view/LayoutInflaterFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Used with {@code LayoutInflaterCompat.setFactory()}. Offers the same API as
+ * {@link android.view.LayoutInflater.Factory2}.
+ *
+ * @deprecated Use {@link android.view.LayoutInflater.Factory2} directly.
+ */
+@Deprecated
+public interface LayoutInflaterFactory {
+
+    /**
+     * Hook you can supply that is called when inflating from a LayoutInflater.
+     * You can use this to customize the tag names available in your XML
+     * layout files.
+     *
+     * @param parent The parent that the created view will be placed
+     * in; <em>note that this may be null</em>.
+     * @param name Tag name to be inflated.
+     * @param context The context the view is being created in.
+     * @param attrs Inflation attributes as specified in XML file.
+     *
+     * @return View Newly created view. Return null for the default
+     *         behavior.
+     */
+    View onCreateView(View parent, String name, Context context, AttributeSet attrs);
+
+}
diff --git a/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java b/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java
index 4e5851e..8c76854 100644
--- a/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java
+++ b/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java
@@ -17,121 +17,16 @@
 
 package android.support.v4.view;
 
-import android.os.Build;
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.view.ViewGroup;
 
 /**
  * Helper for accessing API features in
- * {@link android.view.ViewGroup.MarginLayoutParams MarginLayoutParams} added after API 4.
+ * {@link android.view.ViewGroup.MarginLayoutParams MarginLayoutParams} in a backwards compatible
+ * way.
  */
 public final class MarginLayoutParamsCompat {
-    interface MarginLayoutParamsCompatImpl {
-        int getMarginStart(ViewGroup.MarginLayoutParams lp);
-        int getMarginEnd(ViewGroup.MarginLayoutParams lp);
-        void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart);
-        void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd);
-        boolean isMarginRelative(ViewGroup.MarginLayoutParams lp);
-        int getLayoutDirection(ViewGroup.MarginLayoutParams lp);
-        void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection);
-        void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection);
-    }
-
-    static class MarginLayoutParamsCompatImplBase implements MarginLayoutParamsCompatImpl {
-
-        @Override
-        public int getMarginStart(ViewGroup.MarginLayoutParams lp) {
-            return lp.leftMargin;
-        }
-
-        @Override
-        public int getMarginEnd(ViewGroup.MarginLayoutParams lp) {
-            return lp.rightMargin;
-        }
-
-        @Override
-        public void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart) {
-            lp.leftMargin = marginStart;
-        }
-
-        @Override
-        public void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd) {
-            lp.rightMargin = marginEnd;
-        }
-
-        @Override
-        public boolean isMarginRelative(ViewGroup.MarginLayoutParams lp) {
-            return false;
-        }
-
-        @Override
-        public int getLayoutDirection(ViewGroup.MarginLayoutParams lp) {
-            return ViewCompat.LAYOUT_DIRECTION_LTR;
-        }
-
-        @Override
-        public void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-            // No-op
-        }
-
-        @Override
-        public void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-            // No-op
-        }
-    }
-
-    static class MarginLayoutParamsCompatImplJbMr1 implements MarginLayoutParamsCompatImpl {
-
-        @Override
-        public int getMarginStart(ViewGroup.MarginLayoutParams lp) {
-            return MarginLayoutParamsCompatJellybeanMr1.getMarginStart(lp);
-        }
-
-        @Override
-        public int getMarginEnd(ViewGroup.MarginLayoutParams lp) {
-            return MarginLayoutParamsCompatJellybeanMr1.getMarginEnd(lp);
-        }
-
-        @Override
-        public void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart) {
-            MarginLayoutParamsCompatJellybeanMr1.setMarginStart(lp, marginStart);
-        }
-
-        @Override
-        public void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd) {
-            MarginLayoutParamsCompatJellybeanMr1.setMarginEnd(lp, marginEnd);
-        }
-
-        @Override
-        public boolean isMarginRelative(ViewGroup.MarginLayoutParams lp) {
-            return MarginLayoutParamsCompatJellybeanMr1.isMarginRelative(lp);
-        }
-
-        @Override
-        public int getLayoutDirection(ViewGroup.MarginLayoutParams lp) {
-            return MarginLayoutParamsCompatJellybeanMr1.getLayoutDirection(lp);
-        }
-
-        @Override
-        public void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-            MarginLayoutParamsCompatJellybeanMr1.setLayoutDirection(lp, layoutDirection);
-        }
-
-        @Override
-        public void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-            MarginLayoutParamsCompatJellybeanMr1.resolveLayoutDirection(lp, layoutDirection);
-        }
-    }
-
-    static final MarginLayoutParamsCompatImpl IMPL;
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 17) { // jb-mr1
-            IMPL = new MarginLayoutParamsCompatImplJbMr1();
-        } else {
-            IMPL = new MarginLayoutParamsCompatImplBase();
-        }
-    }
-
     /**
      * Get the relative starting margin that was set.
      *
@@ -144,7 +39,11 @@
      * @return the margin along the starting edge in pixels
      */
     public static int getMarginStart(ViewGroup.MarginLayoutParams lp) {
-        return IMPL.getMarginStart(lp);
+        if (SDK_INT >= 17) {
+            return lp.getMarginStart();
+        } else {
+            return lp.leftMargin;
+        }
     }
 
     /**
@@ -159,7 +58,11 @@
      * @return the margin along the ending edge in pixels
      */
     public static int getMarginEnd(ViewGroup.MarginLayoutParams lp) {
-        return IMPL.getMarginEnd(lp);
+        if (SDK_INT >= 17) {
+            return lp.getMarginEnd();
+        } else {
+            return lp.rightMargin;
+        }
     }
 
     /**
@@ -174,7 +77,11 @@
      * @param marginStart the desired start margin in pixels
      */
     public static void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart) {
-        IMPL.setMarginStart(lp, marginStart);
+        if (SDK_INT >= 17) {
+            lp.setMarginStart(marginStart);
+        } else {
+            lp.leftMargin = marginStart;
+        }
     }
 
     /**
@@ -189,7 +96,11 @@
      * @param marginEnd the desired end margin in pixels
      */
     public static void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd) {
-        IMPL.setMarginEnd(lp, marginEnd);
+        if (SDK_INT >= 17) {
+            lp.setMarginEnd(marginEnd);
+        } else {
+            lp.rightMargin = marginEnd;
+        }
     }
 
     /**
@@ -198,7 +109,11 @@
      * @return true if either marginStart or marginEnd has been set.
      */
     public static boolean isMarginRelative(ViewGroup.MarginLayoutParams lp) {
-        return IMPL.isMarginRelative(lp);
+        if (SDK_INT >= 17) {
+            return lp.isMarginRelative();
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -208,7 +123,13 @@
      * @return the layout direction.
      */
     public static int getLayoutDirection(ViewGroup.MarginLayoutParams lp) {
-        int result = IMPL.getLayoutDirection(lp);
+        int result;
+        if (SDK_INT >= 17) {
+            result = lp.getLayoutDirection();
+        } else {
+            result = ViewCompat.LAYOUT_DIRECTION_LTR;
+        }
+
         if ((result != ViewCompat.LAYOUT_DIRECTION_LTR)
                 && (result != ViewCompat.LAYOUT_DIRECTION_RTL)) {
             // This can happen on older platform releases where the default (unset) layout direction
@@ -226,7 +147,9 @@
      *                     or {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
      */
     public static void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-        IMPL.setLayoutDirection(lp, layoutDirection);
+        if (SDK_INT >= 17) {
+            lp.setLayoutDirection(layoutDirection);
+        }
     }
 
     /**
@@ -235,7 +158,9 @@
      */
     public static void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp,
             int layoutDirection) {
-        IMPL.resolveLayoutDirection(lp, layoutDirection);
+        if (SDK_INT >= 17) {
+            lp.resolveLayoutDirection(layoutDirection);
+        }
     }
 
     private MarginLayoutParamsCompat() {}
diff --git a/compat/java/android/support/v4/view/MenuCompat.java b/compat/java/android/support/v4/view/MenuCompat.java
index a03207f..bbac379 100644
--- a/compat/java/android/support/v4/view/MenuCompat.java
+++ b/compat/java/android/support/v4/view/MenuCompat.java
@@ -19,19 +19,17 @@
 import android.view.MenuItem;
 
 /**
- * Helper for accessing features in {@link android.view.Menu}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.view.Menu}.
  */
 public final class MenuCompat {
     /**
      * Call {@link MenuItem#setShowAsAction(int) MenuItem.setShowAsAction()}.
      *
-     * @deprecated Use {@link MenuItemCompat#setShowAsAction(MenuItem, int)
-     *     MenuItemCompat.setShowAsAction(MenuItem, int)}
+     * @deprecated Use {@link MenuItem#setShowAsAction(int)} directly.
      */
     @Deprecated
     public static void setShowAsAction(MenuItem item, int actionEnum) {
-        MenuItemCompat.setShowAsAction(item, actionEnum);
+        item.setShowAsAction(actionEnum);
     }
 
     private MenuCompat() {}
diff --git a/compat/java/android/support/v4/view/MenuItemCompat.java b/compat/java/android/support/v4/view/MenuItemCompat.java
index 7b57e26..bbbc2e5 100644
--- a/compat/java/android/support/v4/view/MenuItemCompat.java
+++ b/compat/java/android/support/v4/view/MenuItemCompat.java
@@ -16,15 +16,20 @@
 
 package android.support.v4.view;
 
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 
 /**
- * Helper for accessing features in {@link android.view.MenuItem}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.view.MenuItem}.
  * <p class="note"><strong>Note:</strong> You cannot get an instance of this class. Instead,
  * it provides <em>static</em> methods that correspond to the methods in {@link
  * android.view.MenuItem}, but take a {@link android.view.MenuItem} object as an additional
@@ -35,13 +40,19 @@
 
     /**
      * Never show this item as a button in an Action Bar.
+     *
+     * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_NEVER} directly.
      */
+    @Deprecated
     public static final int SHOW_AS_ACTION_NEVER = 0;
 
     /**
      * Show this item as a button in an Action Bar if the system
      * decides there is room for it.
+     *
+     * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_IF_ROOM} directly.
      */
+    @Deprecated
     public static final int SHOW_AS_ACTION_IF_ROOM = 1;
 
     /**
@@ -50,34 +61,49 @@
      * crowd the Action Bar and degrade the user experience on devices with
      * smaller screens. A good rule of thumb is to have no more than 2
      * items set to always show at a time.
+     *
+     * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_ALWAYS} directly.
      */
+    @Deprecated
     public static final int SHOW_AS_ACTION_ALWAYS = 2;
 
     /**
      * When this item is in the action bar, always show it with a
      * text label even if it also has an icon specified.
+     *
+     * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_WITH_TEXT} directly.
      */
+    @Deprecated
     public static final int SHOW_AS_ACTION_WITH_TEXT = 4;
 
     /**
      * This item's action view collapses to a normal menu item.
      * When expanded, the action view temporarily takes over
      * a larger segment of its container.
+     *
+     * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} directly.
      */
+    @Deprecated
     public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8;
 
     /**
      * Interface for the full API.
      */
     interface MenuVersionImpl {
-        void setShowAsAction(MenuItem item, int actionEnum);
-        MenuItem setActionView(MenuItem item, View view);
-        MenuItem setActionView(MenuItem item, int resId);
-        View getActionView(MenuItem item);
-        boolean expandActionView(MenuItem item);
-        boolean collapseActionView(MenuItem item);
-        boolean isActionViewExpanded(MenuItem item);
-        MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener);
+        void setContentDescription(MenuItem item, CharSequence contentDescription);
+        CharSequence getContentDescription(MenuItem item);
+        void setTooltipText(MenuItem item, CharSequence tooltipText);
+        CharSequence getTooltipText(MenuItem item);
+        void setShortcut(MenuItem item, char numericChar, char alphaChar, int numericModifiers,
+                int alphaModifiers);
+        void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers);
+        int getAlphabeticModifiers(MenuItem item);
+        void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers);
+        int getNumericModifiers(MenuItem item);
+        void setIconTintList(MenuItem item, ColorStateList tint);
+        ColorStateList getIconTintList(MenuItem item);
+        void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode);
+        PorterDuff.Mode getIconTintMode(MenuItem item);
     }
 
     /**
@@ -87,7 +113,10 @@
      * @see #expandActionView(android.view.MenuItem)
      * @see #collapseActionView(android.view.MenuItem)
      * @see #setShowAsAction(android.view.MenuItem, int)
+     *
+     * @deprecated Use {@link MenuItem.OnActionExpandListener} directly.
      */
+    @Deprecated
     public interface OnActionExpandListener {
 
         /**
@@ -97,7 +126,7 @@
          * @param item Item that was expanded
          * @return true if the item should expand, false if expansion should be suppressed.
          */
-        public boolean onMenuItemActionExpand(MenuItem item);
+        boolean onMenuItemActionExpand(MenuItem item);
 
         /**
          * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
@@ -106,137 +135,136 @@
          * @param item Item that was collapsed
          * @return true if the item should collapse, false if collapsing should be suppressed.
          */
-        public boolean onMenuItemActionCollapse(MenuItem item);
+        boolean onMenuItemActionCollapse(MenuItem item);
     }
 
-    /**
-     * Interface implementation that doesn't use anything about v4 APIs.
-     */
-    static class BaseMenuVersionImpl implements MenuVersionImpl {
+    static class MenuItemCompatBaseImpl implements MenuVersionImpl {
         @Override
-        public void setShowAsAction(MenuItem item, int actionEnum) {
+        public void setContentDescription(MenuItem item, CharSequence contentDescription) {
         }
 
         @Override
-        public MenuItem setActionView(MenuItem item, View view) {
-            return item;
-        }
-
-        @Override
-        public MenuItem setActionView(MenuItem item, int resId) {
-            return item;
-        }
-
-        @Override
-        public View getActionView(MenuItem item) {
+        public CharSequence getContentDescription(MenuItem item) {
             return null;
         }
 
         @Override
-        public boolean expandActionView(MenuItem item) {
-            return false;
+        public void setTooltipText(MenuItem item, CharSequence tooltipText) {
         }
 
         @Override
-        public boolean collapseActionView(MenuItem item) {
-            return false;
+        public CharSequence getTooltipText(MenuItem item) {
+            return null;
         }
 
         @Override
-        public boolean isActionViewExpanded(MenuItem item) {
-            return false;
+        public void setShortcut(MenuItem item, char numericChar, char alphaChar,
+                int numericModifiers, int alphaModifiers) {
         }
 
         @Override
-        public MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener) {
-            return item;
+        public void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
+        }
+
+        @Override
+        public int getAlphabeticModifiers(MenuItem item) {
+            return 0;
+        }
+
+        @Override
+        public void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
+        }
+
+        @Override
+        public int getNumericModifiers(MenuItem item) {
+            return 0;
+        }
+
+        @Override
+        public void setIconTintList(MenuItem item, ColorStateList tint) {
+        }
+
+        @Override
+        public ColorStateList getIconTintList(MenuItem item) {
+            return null;
+        }
+
+        @Override
+        public void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+        }
+
+        @Override
+        public PorterDuff.Mode getIconTintMode(MenuItem item) {
+            return null;
         }
     }
 
-    /**
-     * Interface implementation for devices with at least v11 APIs.
-     */
-    static class HoneycombMenuVersionImpl implements MenuVersionImpl {
+    @RequiresApi(26)
+    static class MenuItemCompatApi26Impl extends MenuItemCompatBaseImpl {
         @Override
-        public void setShowAsAction(MenuItem item, int actionEnum) {
-            MenuItemCompatHoneycomb.setShowAsAction(item, actionEnum);
+        public void setContentDescription(MenuItem item, CharSequence contentDescription) {
+            item.setContentDescription(contentDescription);
         }
 
         @Override
-        public MenuItem setActionView(MenuItem item, View view) {
-            return MenuItemCompatHoneycomb.setActionView(item, view);
+        public CharSequence getContentDescription(MenuItem item) {
+            return item.getContentDescription();
         }
 
         @Override
-        public MenuItem setActionView(MenuItem item, int resId) {
-            return MenuItemCompatHoneycomb.setActionView(item, resId);
+        public void setTooltipText(MenuItem item, CharSequence tooltipText) {
+            item.setTooltipText(tooltipText);
         }
 
         @Override
-        public View getActionView(MenuItem item) {
-            return MenuItemCompatHoneycomb.getActionView(item);
+        public CharSequence getTooltipText(MenuItem item) {
+            return item.getTooltipText();
         }
 
         @Override
-        public boolean expandActionView(MenuItem item) {
-            return false;
+        public void setShortcut(MenuItem item, char numericChar, char alphaChar,
+                int numericModifiers, int alphaModifiers) {
+            item.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers);
         }
 
         @Override
-        public boolean collapseActionView(MenuItem item) {
-            return false;
+        public void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
+            item.setAlphabeticShortcut(alphaChar, alphaModifiers);
         }
 
         @Override
-        public boolean isActionViewExpanded(MenuItem item) {
-            return false;
+        public int getAlphabeticModifiers(MenuItem item) {
+            return item.getAlphabeticModifiers();
         }
 
         @Override
-        public MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener) {
-            return item;
-        }
-    }
-
-    static class IcsMenuVersionImpl extends HoneycombMenuVersionImpl {
-        @Override
-        public boolean expandActionView(MenuItem item) {
-            return MenuItemCompatIcs.expandActionView(item);
+        public void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
+            item.setNumericShortcut(numericChar, numericModifiers);
         }
 
         @Override
-        public boolean collapseActionView(MenuItem item) {
-            return MenuItemCompatIcs.collapseActionView(item);
+        public int getNumericModifiers(MenuItem item) {
+            return item.getNumericModifiers();
         }
 
         @Override
-        public boolean isActionViewExpanded(MenuItem item) {
-            return MenuItemCompatIcs.isActionViewExpanded(item);
+        public void setIconTintList(MenuItem item, ColorStateList tint) {
+            item.setIconTintList(tint);
         }
 
         @Override
-        public MenuItem setOnActionExpandListener(MenuItem item,
-                final OnActionExpandListener listener) {
-            if (listener == null) {
-                return MenuItemCompatIcs.setOnActionExpandListener(item, null);
-            }
-            /*
-             * MenuItemCompatIcs is a dependency of this segment of the support lib
-             * but not the other way around, so we need to take an extra step here to proxy
-             * to the right types.
-             */
-            return MenuItemCompatIcs.setOnActionExpandListener(item,
-                    new MenuItemCompatIcs.SupportActionExpandProxy() {
-                @Override
-                public boolean onMenuItemActionExpand(MenuItem item) {
-                    return listener.onMenuItemActionExpand(item);
-                }
+        public ColorStateList getIconTintList(MenuItem item) {
+            return item.getIconTintList();
+        }
 
-                @Override
-                public boolean onMenuItemActionCollapse(MenuItem item) {
-                    return listener.onMenuItemActionCollapse(item);
-                }
-            });
+        @Override
+        public void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+            item.setIconTintMode(tintMode);
+        }
+
+        @Override
+        public PorterDuff.Mode getIconTintMode(MenuItem item) {
+            return item.getIconTintMode();
         }
     }
 
@@ -245,12 +273,10 @@
      */
     static final MenuVersionImpl IMPL;
     static {
-        if (Build.VERSION.SDK_INT >= 14) {
-            IMPL = new IcsMenuVersionImpl();
-        } else if (Build.VERSION.SDK_INT >= 11) {
-            IMPL = new HoneycombMenuVersionImpl();
+        if (Build.VERSION.SDK_INT >= 26) {
+            IMPL = new MenuItemCompatApi26Impl();
         } else {
-            IMPL = new BaseMenuVersionImpl();
+            IMPL = new MenuItemCompatBaseImpl();
         }
     }
 
@@ -263,13 +289,12 @@
      *
      * @param item - the item to change
      * @param actionEnum - How the item should display.
+     *
+     * @deprecated Use {@link MenuItem#setShowAsAction(int)} directly.
      */
+    @Deprecated
     public static void setShowAsAction(MenuItem item, int actionEnum) {
-        if (item instanceof SupportMenuItem) {
-            ((SupportMenuItem) item).setShowAsAction(actionEnum);
-        } else {
-            IMPL.setShowAsAction(item, actionEnum);
-        }
+        item.setShowAsAction(actionEnum);
     }
 
     /**
@@ -282,12 +307,12 @@
      * @return This Item so additional setters can be called.
      *
      * @see #setShowAsAction(MenuItem, int)
+     *
+     * @deprecated Use {@link MenuItem#setActionView(View)} directly.
      */
+    @Deprecated
     public static MenuItem setActionView(MenuItem item, View view) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).setActionView(view);
-        }
-        return IMPL.setActionView(item, view);
+        return item.setActionView(view);
     }
 
     /**
@@ -304,12 +329,12 @@
      * @return This Item so additional setters can be called.
      *
      * @see #setShowAsAction(MenuItem, int)
+     *
+     * @deprecated Use {@link MenuItem#setActionView(int)} directly.
      */
+    @Deprecated
     public static MenuItem setActionView(MenuItem item, int resId) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).setActionView(resId);
-        }
-        return IMPL.setActionView(item, resId);
+        return item.setActionView(resId);
     }
 
     /**
@@ -317,12 +342,12 @@
      *
      * @param item the item to query
      * @return This item's action view
+     *
+     * @deprecated Use {@link MenuItem#getActionView()} directly.
      */
+    @Deprecated
     public static View getActionView(MenuItem item) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).getActionView();
-        }
-        return IMPL.getActionView(item);
+        return item.getActionView();
     }
 
     /**
@@ -378,12 +403,12 @@
      * the action view.
      *
      * @return true if the action view was expanded, false otherwise.
+     *
+     * @deprecated Use {@link MenuItem#expandActionView()} directly.
      */
+    @Deprecated
     public static boolean expandActionView(MenuItem item) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).expandActionView();
-        }
-        return IMPL.expandActionView(item);
+        return item.expandActionView();
     }
 
     /**
@@ -397,12 +422,12 @@
      * the action view.
      *
      * @return true if the action view was collapsed, false otherwise.
+     *
+     * @deprecated Use {@link MenuItem#collapseActionView()} directly.
      */
+    @Deprecated
     public static boolean collapseActionView(MenuItem item) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).collapseActionView();
-        }
-        return IMPL.collapseActionView(item);
+        return item.collapseActionView();
     }
 
     /**
@@ -413,12 +438,12 @@
      * @see #collapseActionView(MenuItem)
      * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
      * @see android.support.v4.view.MenuItemCompat.OnActionExpandListener
+     *
+     * @deprecated Use {@link MenuItem#isActionViewExpanded()} directly.
      */
+    @Deprecated
     public static boolean isActionViewExpanded(MenuItem item) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).isActionViewExpanded();
-        }
-        return IMPL.isActionViewExpanded(item);
+        return item.isActionViewExpanded();
     }
 
     /**
@@ -429,13 +454,248 @@
      *
      * @param listener Listener that will respond to expand/collapse events
      * @return This menu item instance for call chaining
+     *
+     * @deprecated Use {@link MenuItem#setOnActionExpandListener(MenuItem.OnActionExpandListener)}
+     * directly.
      */
+    @Deprecated
     public static MenuItem setOnActionExpandListener(MenuItem item,
-            OnActionExpandListener listener) {
+            final OnActionExpandListener listener) {
+        return item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
+            @Override
+            public boolean onMenuItemActionExpand(MenuItem item) {
+                return listener.onMenuItemActionExpand(item);
+            }
+
+            @Override
+            public boolean onMenuItemActionCollapse(MenuItem item) {
+                return listener.onMenuItemActionCollapse(item);
+            }
+        });
+    }
+
+    /**
+     * Change the content description associated with this menu item.
+     *
+     * @param item item to change.
+     * @param contentDescription The new content description.
+     */
+    public static void setContentDescription(MenuItem item, CharSequence contentDescription) {
         if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).setSupportOnActionExpandListener(listener);
+            ((SupportMenuItem) item).setContentDescription(contentDescription);
+        } else {
+            IMPL.setContentDescription(item, contentDescription);
         }
-        return IMPL.setOnActionExpandListener(item, listener);
+    }
+
+    /**
+     * Retrieve the content description associated with this menu item.
+     *
+     * @return The content description.
+     */
+    public static CharSequence getContentDescription(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getContentDescription();
+        }
+        return IMPL.getContentDescription(item);
+    }
+
+    /**
+     * Change the tooltip text associated with this menu item.
+     *
+     * @param item item to change.
+     * @param tooltipText The new tooltip text
+     */
+    public static void setTooltipText(MenuItem item, CharSequence tooltipText) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setTooltipText(tooltipText);
+        } else {
+            IMPL.setTooltipText(item, tooltipText);
+        }
+    }
+
+    /**
+     * Retrieve the tooltip text associated with this menu item.
+     *
+     * @return The tooltip text.
+     */
+    public static CharSequence getTooltipText(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getTooltipText();
+        }
+        return IMPL.getTooltipText(item);
+    }
+
+    /**
+     * Change both the numeric and alphabetic shortcut associated with this
+     * item. Note that the shortcut will be triggered when the key that
+     * generates the given character is pressed along with the corresponding
+     * modifier key. Also note that case is not significant and that alphabetic
+     * shortcut characters will be handled in lower case.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key. This is the shortcut when
+     *        using a numeric (e.g., 12-key) keyboard.
+     * @param numericModifiers The numeric modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The alphabetic modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     */
+    public static void setShortcut(MenuItem item, char numericChar, char alphaChar,
+            int numericModifiers, int alphaModifiers) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setShortcut(numericChar, alphaChar, numericModifiers,
+                    alphaModifiers);
+        } else {
+            IMPL.setShortcut(item, numericChar, alphaChar, numericModifiers, alphaModifiers);
+        }
+    }
+
+    /**
+     * Change the numeric shortcut and modifiers associated with this item.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key.  This is the shortcut when
+     *                 using a 12-key (numeric) keyboard.
+     * @param numericModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     */
+    public static void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setNumericShortcut(numericChar, numericModifiers);
+        } else {
+            IMPL.setNumericShortcut(item, numericChar, numericModifiers);
+        }
+    }
+
+    /**
+     * Return the modifiers for this menu item's numeric (12-key) shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the numeric shortcut.
+     */
+    public static int getNumericModifiers(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getNumericModifiers();
+        }
+        return IMPL.getNumericModifiers(item);
+    }
+
+    /**
+     * Change the alphabetic shortcut associated with this item. The shortcut
+     * will be triggered when the key that generates the given character is
+     * pressed along with the modifier keys. Case is not significant and shortcut
+     * characters will be displayed in lower case. Note that menu items with
+     * the characters '\b' or '\n' as shortcuts will get triggered by the
+     * Delete key or Carriage Return key, respectively.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     */
+    public static void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setAlphabeticShortcut(alphaChar, alphaModifiers);
+        } else {
+            IMPL.setAlphabeticShortcut(item, alphaChar, alphaModifiers);
+        }
+    }
+
+    /**
+     * Return the modifier for this menu item's alphabetic shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the keyboard shortcut.
+     */
+    public static int getAlphabeticModifiers(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getAlphabeticModifiers();
+        }
+        return IMPL.getAlphabeticModifiers(item);
+    }
+
+    /**
+     * Applies a tint to the item's icon. Does not modify the
+     * current tint mode of that item, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link MenuItem#setIcon(Drawable)} or {@link MenuItem#setIcon(int)} will
+     * automatically mutate the icon and apply the specified tint and
+     * tint mode.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @see #getIconTintList(MenuItem)
+     */
+    public static void setIconTintList(MenuItem item, ColorStateList tint) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setIconTintList(tint);
+        } else {
+            IMPL.setIconTintList(item, tint);
+        }
+    }
+
+    /**
+     * @return the tint applied to the item's icon
+     * @see #setIconTintList(MenuItem, ColorStateList)
+     */
+    public static ColorStateList getIconTintList(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getIconTintList();
+        }
+        return IMPL.getIconTintList(item);
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setIconTintList(MenuItem, ColorStateList)} to the item's icon. The default mode is
+     * {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @see #setIconTintList(MenuItem, ColorStateList)
+     */
+    public static void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setIconTintMode(tintMode);
+        } else {
+            IMPL.setIconTintMode(item, tintMode);
+        }
+    }
+
+    /**
+     * Returns the blending mode used to apply the tint to the item's icon, if specified.
+     *
+     * @return the blending mode used to apply the tint to the item's icon
+     * @see #setIconTintMode(MenuItem, PorterDuff.Mode)
+     */
+    public static PorterDuff.Mode getIconTintMode(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getIconTintMode();
+        }
+        return IMPL.getIconTintMode(item);
     }
 
     private MenuItemCompat() {}
diff --git a/compat/java/android/support/v4/view/MotionEventCompat.java b/compat/java/android/support/v4/view/MotionEventCompat.java
index 48ce0f9..367eda2 100644
--- a/compat/java/android/support/v4/view/MotionEventCompat.java
+++ b/compat/java/android/support/v4/view/MotionEventCompat.java
@@ -16,265 +16,298 @@
 
 package android.support.v4.view;
 
-import android.os.Build;
 import android.view.MotionEvent;
 
 /**
- * Helper for accessing features in {@link MotionEvent} introduced
- * after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link MotionEvent}.
  */
 public final class MotionEventCompat {
     /**
-     * Interface for the full API.
-     */
-    interface MotionEventVersionImpl {
-        float getAxisValue(MotionEvent event, int axis);
-        float getAxisValue(MotionEvent event, int axis, int pointerIndex);
-        int getButtonState(MotionEvent event);
-    }
-
-    /**
-     * Interface implementation that doesn't use anything about v4 APIs.
-     */
-    static class BaseMotionEventVersionImpl implements MotionEventVersionImpl {
-        @Override
-        public float getAxisValue(MotionEvent event, int axis) {
-            return 0;
-        }
-
-        @Override
-        public float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
-            return 0;
-        }
-
-        @Override
-        public int getButtonState(MotionEvent event) {
-            return 0;
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least v12 APIs.
-     */
-    static class HoneycombMr1MotionEventVersionImpl extends BaseMotionEventVersionImpl {
-
-        @Override
-        public float getAxisValue(MotionEvent event, int axis) {
-            return MotionEventCompatHoneycombMr1.getAxisValue(event, axis);
-        }
-
-        @Override
-        public float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
-            return MotionEventCompatHoneycombMr1.getAxisValue(event, axis, pointerIndex);
-        }
-    }
-
-
-    /**
-     * Interface implementation for devices with at least v14 APIs.
-     */
-    private static class ICSMotionEventVersionImpl extends HoneycombMr1MotionEventVersionImpl {
-        ICSMotionEventVersionImpl() {
-        }
-
-        @Override
-        public int getButtonState(MotionEvent event) {
-            return MotionEventCompatICS.getButtonState(event);
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final MotionEventVersionImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 14) {
-            IMPL = new ICSMotionEventVersionImpl();
-        } else if (Build.VERSION.SDK_INT >= 12) {
-            IMPL = new HoneycombMr1MotionEventVersionImpl();
-        } else {
-            IMPL = new BaseMotionEventVersionImpl();
-        }
-    }
-
-    // -------------------------------------------------------------------
-
-    /**
      * Synonym for {@link MotionEvent#ACTION_MASK}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_MASK} directly.
      */
+    @Deprecated
     public static final int ACTION_MASK = 0xff;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_POINTER_DOWN}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_POINTER_DOWN} directly.
      */
+    @Deprecated
     public static final int ACTION_POINTER_DOWN = 5;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_POINTER_UP}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_POINTER_UP} directly.
      */
+    @Deprecated
     public static final int ACTION_POINTER_UP = 6;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_HOVER_MOVE}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_HOVER_MOVE} directly.
      */
+    @Deprecated
     public static final int ACTION_HOVER_MOVE = 7;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_SCROLL}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_SCROLL} directly.
      */
+    @Deprecated
     public static final int ACTION_SCROLL = 8;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_MASK}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_POINTER_INDEX_MASK} directly.
      */
+    @Deprecated
     public static final int ACTION_POINTER_INDEX_MASK  = 0xff00;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_SHIFT}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_POINTER_INDEX_SHIFT} directly.
      */
+    @Deprecated
     public static final int ACTION_POINTER_INDEX_SHIFT = 8;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_HOVER_ENTER}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_HOVER_ENTER} directly.
      */
+    @Deprecated
     public static final int ACTION_HOVER_ENTER = 9;
 
     /**
      * Synonym for {@link MotionEvent#ACTION_HOVER_EXIT}.
+     *
+     * @deprecated Use {@link MotionEvent#ACTION_HOVER_EXIT} directly.
      */
+    @Deprecated
     public static final int ACTION_HOVER_EXIT = 10;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_X}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_X} directly.
      */
+    @Deprecated
     public static final int AXIS_X = 0;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_Y}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_Y} directly.
      */
+    @Deprecated
     public static final int AXIS_Y = 1;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_PRESSURE}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_PRESSURE} directly.
      */
+    @Deprecated
     public static final int AXIS_PRESSURE = 2;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_SIZE}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_SIZE} directly.
      */
+    @Deprecated
     public static final int AXIS_SIZE = 3;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_TOUCH_MAJOR}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MAJOR} directly.
      */
+    @Deprecated
     public static final int AXIS_TOUCH_MAJOR = 4;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_TOUCH_MINOR}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MINOR} directly.
      */
+    @Deprecated
     public static final int AXIS_TOUCH_MINOR = 5;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_TOOL_MAJOR}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_TOOL_MAJOR} directly.
      */
+    @Deprecated
     public static final int AXIS_TOOL_MAJOR = 6;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_TOOL_MINOR}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_TOOL_MINOR} directly.
      */
+    @Deprecated
     public static final int AXIS_TOOL_MINOR = 7;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_ORIENTATION}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_ORIENTATION} directly.
      */
+    @Deprecated
     public static final int AXIS_ORIENTATION = 8;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_VSCROLL}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_VSCROLL} directly.
      */
+    @Deprecated
     public static final int AXIS_VSCROLL = 9;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_HSCROLL}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_HSCROLL} directly.
      */
+    @Deprecated
     public static final int AXIS_HSCROLL = 10;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_Z}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_Z} directly.
      */
+    @Deprecated
     public static final int AXIS_Z = 11;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_RX}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_RX} directly.
      */
+    @Deprecated
     public static final int AXIS_RX = 12;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_RY}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_RY} directly.
      */
+    @Deprecated
     public static final int AXIS_RY = 13;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_RZ}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_RZ} directly.
      */
+    @Deprecated
     public static final int AXIS_RZ = 14;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_HAT_X}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_HAT_X} directly.
      */
+    @Deprecated
     public static final int AXIS_HAT_X = 15;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_HAT_Y}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_HAT_Y} directly.
      */
+    @Deprecated
     public static final int AXIS_HAT_Y = 16;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_LTRIGGER}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_LTRIGGER} directly.
      */
+    @Deprecated
     public static final int AXIS_LTRIGGER = 17;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_RTRIGGER}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_RTRIGGER} directly.
      */
+    @Deprecated
     public static final int AXIS_RTRIGGER = 18;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_THROTTLE}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_THROTTLE} directly.
      */
+    @Deprecated
     public static final int AXIS_THROTTLE = 19;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_RUDDER}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_RUDDER} directly.
      */
+    @Deprecated
     public static final int AXIS_RUDDER = 20;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_WHEEL}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_WHEEL} directly.
      */
+    @Deprecated
     public static final int AXIS_WHEEL = 21;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GAS}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GAS} directly.
      */
+    @Deprecated
     public static final int AXIS_GAS = 22;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_BRAKE}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_BRAKE} directly.
      */
+    @Deprecated
     public static final int AXIS_BRAKE = 23;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_DISTANCE}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_DISTANCE} directly.
      */
+    @Deprecated
     public static final int AXIS_DISTANCE = 24;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_TILT}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_TILT} directly.
      */
+    @Deprecated
     public static final int AXIS_TILT = 25;
 
     /**
+     * Synonym for {@link MotionEvent#AXIS_SCROLL}.
+     */
+    public static final int AXIS_SCROLL = 26;
+
+    /**
      * Synonym for {@link MotionEvent#AXIS_RELATIVE_X}.
      */
     public static final int AXIS_RELATIVE_X = 27;
@@ -286,104 +319,162 @@
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_1}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_1} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_1 = 32;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_2}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_2} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_2 = 33;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_3}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_3} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_3 = 34;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_4}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_4} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_4 = 35;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_5}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_5} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_5 = 36;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_6}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_6} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_6 = 37;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_7}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_7} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_7 = 38;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_8}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_8} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_8 = 39;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_9}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_9} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_9 = 40;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_10}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_10} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_10 = 41;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_11}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_11} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_11 = 42;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_12}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_12} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_12 = 43;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_13}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_13} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_13 = 44;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_14}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_14} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_14 = 45;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_15}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_15} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_15 = 46;
 
     /**
      * Synonym for {@link MotionEvent#AXIS_GENERIC_16}.
+     *
+     * @deprecated Use {@link MotionEvent#AXIS_GENERIC_16} directly.
      */
+    @Deprecated
     public static final int AXIS_GENERIC_16 = 47;
 
     /**
      * Synonym for {@link MotionEvent#BUTTON_PRIMARY}.
+     *
+     * @deprecated Use {@link MotionEvent#BUTTON_PRIMARY} directly.
      */
+    @Deprecated
     public static final int BUTTON_PRIMARY = 1;
 
     /**
      * Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK}
      * portion.
+     *
+     * @deprecated Call {@link MotionEvent#getAction()} directly. This method will be
+     * removed in a future release.
      */
+    @Deprecated
     public static int getActionMasked(MotionEvent event) {
-        return event.getAction() & ACTION_MASK;
+        return event.getActionMasked();
     }
 
     /**
      * Call {@link MotionEvent#getAction}, returning only the pointer index
-     * portion
+     * portion.
+     *
+     * @deprecated Call {@link MotionEvent#getActionIndex()} directly. This method will be
+     * removed in a future release.
      */
+    @Deprecated
     public static int getActionIndex(MotionEvent event) {
-        return (event.getAction() & ACTION_POINTER_INDEX_MASK)
-                >> ACTION_POINTER_INDEX_SHIFT;
+        return event.getActionIndex();
     }
 
     /**
@@ -470,9 +561,13 @@
      *
      * @see #AXIS_X
      * @see #AXIS_Y
+     *
+     * @deprecated Call {@link MotionEvent#getAxisValue(int)} directly. This method will be
+     * removed in a future release.
      */
+    @Deprecated
     public static float getAxisValue(MotionEvent event, int axis) {
-        return IMPL.getAxisValue(event, axis);
+        return event.getAxisValue(axis);
     }
 
     /**
@@ -486,18 +581,22 @@
      *
      * @see #AXIS_X
      * @see #AXIS_Y
+     *
+     * @deprecated Call {@link MotionEvent#getAxisValue(int, int)} directly. This method will be
+     * removed in a future release.
      */
+    @Deprecated
     public static float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
-        return IMPL.getAxisValue(event, axis, pointerIndex);
+        return event.getAxisValue(axis, pointerIndex);
     }
 
     /**
-     *
-     * @param event
-     * @return
+     * @deprecated Call {@link MotionEvent#getButtonState()} directly. This method will be
+     * removed in a future release.
      */
+    @Deprecated
     public static int getButtonState(MotionEvent event) {
-        return IMPL.getButtonState(event);
+        return event.getButtonState();
     }
 
     private MotionEventCompat() {}
diff --git a/compat/java/android/support/v4/view/NestedScrollingChild.java b/compat/java/android/support/v4/view/NestedScrollingChild.java
index 40e0a09..f57dd9f 100644
--- a/compat/java/android/support/v4/view/NestedScrollingChild.java
+++ b/compat/java/android/support/v4/view/NestedScrollingChild.java
@@ -17,6 +17,8 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat.ScrollAxis;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -51,7 +53,7 @@
      *
      * @see #isNestedScrollingEnabled()
      */
-    public void setNestedScrollingEnabled(boolean enabled);
+    void setNestedScrollingEnabled(boolean enabled);
 
     /**
      * Returns true if nested scrolling is enabled for this view.
@@ -65,7 +67,7 @@
      *
      * @see #setNestedScrollingEnabled(boolean)
      */
-    public boolean isNestedScrollingEnabled();
+    boolean isNestedScrollingEnabled();
 
     /**
      * Begin a nestable scroll operation along the given axes.
@@ -105,7 +107,7 @@
      * @see #dispatchNestedPreScroll(int, int, int[], int[])
      * @see #dispatchNestedScroll(int, int, int, int, int[])
      */
-    public boolean startNestedScroll(int axes);
+    boolean startNestedScroll(@ScrollAxis int axes);
 
     /**
      * Stop a nested scroll in progress.
@@ -114,7 +116,7 @@
      *
      * @see #startNestedScroll(int)
      */
-    public void stopNestedScroll();
+    void stopNestedScroll();
 
     /**
      * Returns true if this view has a nested scrolling parent.
@@ -124,7 +126,7 @@
      *
      * @return whether this view has a nested scrolling parent
      */
-    public boolean hasNestedScrollingParent();
+    boolean hasNestedScrollingParent();
 
     /**
      * Dispatch one step of a nested scroll in progress.
@@ -149,8 +151,8 @@
      * @return true if the event was dispatched, false if it could not be dispatched.
      * @see #dispatchNestedPreScroll(int, int, int[], int[])
      */
-    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
-            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
+    boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow);
 
     /**
      * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
@@ -171,7 +173,8 @@
      * @return true if the parent consumed some or all of the scroll delta
      * @see #dispatchNestedScroll(int, int, int, int, int[])
      */
-    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
+    boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
+            @Nullable int[] offsetInWindow);
 
     /**
      * Dispatch a fling to a nested scrolling parent.
@@ -191,7 +194,7 @@
      * @param consumed true if the child consumed the fling, false otherwise
      * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
      */
-    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
+    boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
 
     /**
      * Dispatch a fling to a nested scrolling parent before it is processed by this view.
@@ -223,5 +226,5 @@
      * @param velocityY Vertical fling velocity in pixels per second
      * @return true if a nested scrolling parent consumed the fling
      */
-    public boolean dispatchNestedPreFling(float velocityX, float velocityY);
+    boolean dispatchNestedPreFling(float velocityX, float velocityY);
 }
diff --git a/compat/java/android/support/v4/view/NestedScrollingChild2.java b/compat/java/android/support/v4/view/NestedScrollingChild2.java
new file mode 100644
index 0000000..ec31c65
--- /dev/null
+++ b/compat/java/android/support/v4/view/NestedScrollingChild2.java
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+
+package android.support.v4.view;
+
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat.NestedScrollType;
+import android.support.v4.view.ViewCompat.ScrollAxis;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+
+/**
+ * This interface should be implemented by {@link View View} subclasses that wish
+ * to support dispatching nested scrolling operations to a cooperating parent
+ * {@link android.view.ViewGroup ViewGroup}.
+ *
+ * <p>Classes implementing this interface should create a final instance of a
+ * {@link NestedScrollingChildHelper} as a field and delegate any View methods to the
+ * <code>NestedScrollingChildHelper</code> methods of the same signature.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on all versions
+ * of Android.</p>
+ */
+public interface NestedScrollingChild2 extends NestedScrollingChild {
+
+    /**
+     * Begin a nestable scroll operation along the given axes, for the given input type.
+     *
+     * <p>A view starting a nested scroll promises to abide by the following contract:</p>
+     *
+     * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
+     * of a touch scroll type this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
+     * In the case of touch scrolling the nested scroll will be terminated automatically in
+     * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
+     * In the event of programmatic scrolling the caller must explicitly call
+     * {@link #stopNestedScroll(int)} to indicate the end of the nested scroll.</p>
+     *
+     * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
+     * If it returns false the caller may ignore the rest of this contract until the next scroll.
+     * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
+     *
+     * <p>At each incremental step of the scroll the caller should invoke
+     * {@link #dispatchNestedPreScroll(int, int, int[], int[], int) dispatchNestedPreScroll}
+     * once it has calculated the requested scrolling delta. If it returns true the nested scrolling
+     * parent at least partially consumed the scroll and the caller should adjust the amount it
+     * scrolls by.</p>
+     *
+     * <p>After applying the remainder of the scroll delta the caller should invoke
+     * {@link #dispatchNestedScroll(int, int, int, int, int[], int) dispatchNestedScroll}, passing
+     * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
+     * these values differently. See
+     * {@link NestedScrollingParent2#onNestedScroll(View, int, int, int, int, int)}.
+     * </p>
+     *
+     * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
+     *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
+     * @param type the type of input which cause this scroll event
+     * @return true if a cooperative parent was found and nested scrolling has been enabled for
+     *         the current gesture.
+     *
+     * @see #stopNestedScroll(int)
+     * @see #dispatchNestedPreScroll(int, int, int[], int[], int)
+     * @see #dispatchNestedScroll(int, int, int, int, int[], int)
+     */
+    boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type);
+
+    /**
+     * Stop a nested scroll in progress for the given input type.
+     *
+     * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
+     *
+     * @param type the type of input which cause this scroll event
+     * @see #startNestedScroll(int, int)
+     */
+    void stopNestedScroll(@NestedScrollType int type);
+
+    /**
+     * Returns true if this view has a nested scrolling parent for the given input type.
+     *
+     * <p>The presence of a nested scrolling parent indicates that this view has initiated
+     * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
+     *
+     * @param type the type of input which cause this scroll event
+     *
+     * @return whether this view has a nested scrolling parent
+     */
+    boolean hasNestedScrollingParent(@NestedScrollType int type);
+
+    /**
+     * Dispatch one step of a nested scroll in progress.
+     *
+     * <p>Implementations of views that support nested scrolling should call this to report
+     * info about a scroll in progress to the current nested scrolling parent. If a nested scroll
+     * is not currently in progress or nested scrolling is not
+     * {@link #isNestedScrollingEnabled() enabled} for this view this method does nothing.</p>
+     *
+     * <p>Compatible View implementations should also call
+     * {@link #dispatchNestedPreScroll(int, int, int[], int[], int) dispatchNestedPreScroll} before
+     * consuming a component of the scroll event themselves.</p>
+     *
+     * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
+     * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @param type the type of input which cause this scroll event
+     * @return true if the event was dispatched, false if it could not be dispatched.
+     * @see #dispatchNestedPreScroll(int, int, int[], int[], int)
+     */
+    boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,
+            @NestedScrollType int type);
+
+    /**
+     * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
+     *
+     * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
+     * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested
+     * scrolling operation to consume some or all of the scroll operation before the child view
+     * consumes it.</p>
+     *
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
+     *                 and consumed[1] the consumed dy.
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @param type the type of input which cause this scroll event
+     * @return true if the parent consumed some or all of the scroll delta
+     * @see #dispatchNestedScroll(int, int, int, int, int[], int)
+     */
+    boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
+            @Nullable int[] offsetInWindow, @NestedScrollType int type);
+}
diff --git a/compat/java/android/support/v4/view/NestedScrollingParent.java b/compat/java/android/support/v4/view/NestedScrollingParent.java
index bc056b6..4620f01 100644
--- a/compat/java/android/support/v4/view/NestedScrollingParent.java
+++ b/compat/java/android/support/v4/view/NestedScrollingParent.java
@@ -17,6 +17,8 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.NonNull;
+import android.support.v4.view.ViewCompat.ScrollAxis;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -54,11 +56,11 @@
      *
      * @param child Direct child of this ViewParent containing target
      * @param target View that initiated the nested scroll
-     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     * @param axes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
      *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
      * @return true if this ViewParent accepts the nested scroll operation
      */
-    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
+    boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes);
 
     /**
      * React to the successful claiming of a nested scroll operation.
@@ -71,12 +73,12 @@
      *
      * @param child Direct child of this ViewParent containing target
      * @param target View that initiated the nested scroll
-     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     * @param axes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
      *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
      * @see #onStartNestedScroll(View, View, int)
      * @see #onStopNestedScroll(View)
      */
-    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
+    void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes);
 
     /**
      * React to a nested scroll operation ending.
@@ -89,7 +91,7 @@
      *
      * @param target View that initiated the nested scroll
      */
-    public void onStopNestedScroll(View target);
+    void onStopNestedScroll(@NonNull View target);
 
     /**
      * React to a nested scroll in progress.
@@ -112,7 +114,7 @@
      * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
      * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
      */
-    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+    void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
             int dxUnconsumed, int dyUnconsumed);
 
     /**
@@ -135,7 +137,7 @@
      * @param dy Vertical scroll distance in pixels
      * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
      */
-    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
+    void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed);
 
     /**
      * Request a fling from a nested scroll.
@@ -156,7 +158,7 @@
      * @param consumed true if the child consumed the fling, false otherwise
      * @return true if this parent consumed or otherwise reacted to the fling
      */
-    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+    boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed);
 
     /**
      * React to a nested fling before the target view consumes it.
@@ -178,7 +180,7 @@
      * @param velocityY Vertical velocity in pixels per second
      * @return true if this parent consumed the fling ahead of the target view
      */
-    public boolean onNestedPreFling(View target, float velocityX, float velocityY);
+    boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY);
 
     /**
      * Return the current axes of nested scrolling for this NestedScrollingParent.
@@ -192,5 +194,6 @@
      * @see ViewCompat#SCROLL_AXIS_VERTICAL
      * @see ViewCompat#SCROLL_AXIS_NONE
      */
-    public int getNestedScrollAxes();
+    @ScrollAxis
+    int getNestedScrollAxes();
 }
diff --git a/compat/java/android/support/v4/view/NestedScrollingParent2.java b/compat/java/android/support/v4/view/NestedScrollingParent2.java
new file mode 100644
index 0000000..db41c46
--- /dev/null
+++ b/compat/java/android/support/v4/view/NestedScrollingParent2.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+
+package android.support.v4.view;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat.NestedScrollType;
+import android.support.v4.view.ViewCompat.ScrollAxis;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * This interface should be implemented by {@link android.view.ViewGroup ViewGroup} subclasses
+ * that wish to support scrolling operations delegated by a nested child view.
+ *
+ * <p>Classes implementing this interface should create a final instance of a
+ * {@link NestedScrollingParentHelper} as a field and delegate any View or ViewGroup methods
+ * to the <code>NestedScrollingParentHelper</code> methods of the same signature.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on all versions
+ * of Android.</p>
+ */
+public interface NestedScrollingParent2 extends NestedScrollingParent {
+    /**
+     * React to a descendant view initiating a nestable scroll operation, claiming the
+     * nested scroll operation if appropriate.
+     *
+     * <p>This method will be called in response to a descendant view invoking
+     * {@link ViewCompat#startNestedScroll(View, int)}. Each parent up the view hierarchy will be
+     * given an opportunity to respond and claim the nested scrolling operation by returning
+     * <code>true</code>.</p>
+     *
+     * <p>This method may be overridden by ViewParent implementations to indicate when the view
+     * is willing to support a nested scrolling operation that is about to begin. If it returns
+     * true, this ViewParent will become the target view's nested scrolling parent for the duration
+     * of the scroll operation in progress. When the nested scroll is finished this ViewParent
+     * will receive a call to {@link #onStopNestedScroll(View, int)}.
+     * </p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param axes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @param type the type of input which cause this scroll event
+     * @return true if this ViewParent accepts the nested scroll operation
+     */
+    boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes,
+            @NestedScrollType int type);
+
+    /**
+     * React to the successful claiming of a nested scroll operation.
+     *
+     * <p>This method will be called after
+     * {@link #onStartNestedScroll(View, View, int, int) onStartNestedScroll} returns true. It
+     * offers an opportunity for the view and its superclasses to perform initial configuration
+     * for the nested scroll. Implementations of this method should always call their superclass's
+     * implementation of this method if one is present.</p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param axes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @param type the type of input which cause this scroll event
+     * @see #onStartNestedScroll(View, View, int, int)
+     * @see #onStopNestedScroll(View, int)
+     */
+    void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes,
+            @NestedScrollType int type);
+
+    /**
+     * React to a nested scroll operation ending.
+     *
+     * <p>Perform cleanup after a nested scrolling operation.
+     * This method will be called when a nested scroll stops, for example when a nested touch
+     * scroll ends with a {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL} event.
+     * Implementations of this method should always call their superclass's implementation of this
+     * method if one is present.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param type the type of input which cause this scroll event
+     */
+    void onStopNestedScroll(@NonNull View target, @NestedScrollType int type);
+
+    /**
+     * React to a nested scroll in progress.
+     *
+     * <p>This method will be called when the ViewParent's current nested scrolling child view
+     * dispatches a nested scroll event. To receive calls to this method the ViewParent must have
+     * previously returned <code>true</code> for a call to
+     * {@link #onStartNestedScroll(View, View, int, int)}.</p>
+     *
+     * <p>Both the consumed and unconsumed portions of the scroll distance are reported to the
+     * ViewParent. An implementation may choose to use the consumed portion to match or chase scroll
+     * position of multiple child elements, for example. The unconsumed portion may be used to
+     * allow continuous dragging of multiple scrolling or draggable elements, such as scrolling
+     * a list within a vertical drawer where the drawer begins dragging once the edge of inner
+     * scrolling content is reached.</p>
+     *
+     * @param target The descendent view controlling the nested scroll
+     * @param dxConsumed Horizontal scroll distance in pixels already consumed by target
+     * @param dyConsumed Vertical scroll distance in pixels already consumed by target
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
+     * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
+     * @param type the type of input which cause this scroll event
+     */
+    void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type);
+
+    /**
+     * React to a nested scroll in progress before the target view consumes a portion of the scroll.
+     *
+     * <p>When working with nested scrolling often the parent view may want an opportunity
+     * to consume the scroll before the nested scrolling child does. An example of this is a
+     * drawer that contains a scrollable list. The user will want to be able to scroll the list
+     * fully into view before the list itself begins scrolling.</p>
+     *
+     * <p><code>onNestedPreScroll</code> is called when a nested scrolling child invokes
+     * {@link View#dispatchNestedPreScroll(int, int, int[], int[])}. The implementation should
+     * report how any pixels of the scroll reported by dx, dy were consumed in the
+     * <code>consumed</code> array. Index 0 corresponds to dx and index 1 corresponds to dy.
+     * This parameter will never be null. Initial values for consumed[0] and consumed[1]
+     * will always be 0.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
+     * @param type the type of input which cause this scroll event
+     */
+    void onNestedPreScroll(@NonNull View target, int dx, int dy, @Nullable int[] consumed,
+            @NestedScrollType int type);
+
+}
diff --git a/compat/java/android/support/v4/view/PointerIconCompat.java b/compat/java/android/support/v4/view/PointerIconCompat.java
index cea4dfb..d9e65e1 100644
--- a/compat/java/android/support/v4/view/PointerIconCompat.java
+++ b/compat/java/android/support/v4/view/PointerIconCompat.java
@@ -16,17 +16,18 @@
 
 package android.support.v4.view;
 
+import static android.os.Build.VERSION.SDK_INT;
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
+import android.view.PointerIcon;
 
 /**
- * Helper for accessing features in {@link android.view.PointerIcon} introduced after API
- * level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.view.PointerIcon} in a backwards compatible
+ * fashion.
  */
 public final class PointerIconCompat {
     /** Synonym for {@link android.view.PointerIcon#TYPE_NULL} */
@@ -113,55 +114,6 @@
         return mPointerIcon;
     }
 
-    interface PointerIconCompatImpl {
-        Object getSystemIcon(Context context, int style);
-        Object create(Bitmap bitmap, float hotSpotX, float hotSpotY);
-        Object load(Resources resources, int resourceId);
-    }
-
-    static class BasePointerIconCompatImpl implements PointerIconCompatImpl {
-        @Override
-        public Object getSystemIcon(Context context, int style) {
-            return null;
-        }
-
-        @Override
-        public Object create(Bitmap bitmap, float hotSpotX, float hotSpotY) {
-            return null;
-        }
-
-        @Override
-        public Object load(Resources resources, int resourceId) {
-            return null;
-        }
-    }
-
-    static class Api24PointerIconCompatImpl extends BasePointerIconCompatImpl {
-        @Override
-        public Object getSystemIcon(Context context, int style) {
-            return PointerIconCompatApi24.getSystemIcon(context, style);
-        }
-
-        @Override
-        public Object create(Bitmap bitmap, float hotSpotX, float hotSpotY) {
-            return PointerIconCompatApi24.create(bitmap, hotSpotX, hotSpotY);
-        }
-
-        @Override
-        public Object load(Resources resources, int resourceId) {
-            return PointerIconCompatApi24.load(resources, resourceId);
-        }
-    }
-
-    static final PointerIconCompatImpl IMPL;
-    static {
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new Api24PointerIconCompatImpl();
-        } else {
-            IMPL = new BasePointerIconCompatImpl();
-        }
-    }
-
     /**
      * Gets a system pointer icon for the given style.
      * If style is not recognized, returns the default pointer icon.
@@ -173,7 +125,11 @@
      * @throws IllegalArgumentException if context is null.
      */
     public static PointerIconCompat getSystemIcon(Context context, int style) {
-        return new PointerIconCompat(IMPL.getSystemIcon(context, style));
+        if (SDK_INT >= 24) {
+            return new PointerIconCompat(PointerIcon.getSystemIcon(context, style));
+        } else {
+            return new PointerIconCompat(null);
+        }
     }
 
     /**
@@ -190,7 +146,11 @@
      *         parameters are invalid.
      */
     public static PointerIconCompat create(Bitmap bitmap, float hotSpotX, float hotSpotY) {
-        return new PointerIconCompat(IMPL.create(bitmap, hotSpotX, hotSpotY));
+        if (SDK_INT >= 24) {
+            return new PointerIconCompat(PointerIcon.create(bitmap, hotSpotX, hotSpotY));
+        } else {
+            return new PointerIconCompat(null);
+        }
     }
 
     /**
@@ -215,6 +175,10 @@
      * linked in the resource was not found.
      */
     public static PointerIconCompat load(Resources resources, int resourceId) {
-        return new PointerIconCompat(IMPL.load(resources, resourceId));
+        if (SDK_INT >= 24) {
+            return new PointerIconCompat(PointerIcon.load(resources, resourceId));
+        } else {
+            return new PointerIconCompat(null);
+        }
     }
 }
diff --git a/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java b/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java
index 30d0ca1..6233dff 100644
--- a/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java
+++ b/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java
@@ -16,76 +16,69 @@
 
 package android.support.v4.view;
 
+import android.os.Build;
+import android.view.ScaleGestureDetector;
+
 /**
- * Helper for accessing features in <code>ScaleGestureDetector</code> introduced
- * after API level 19 (KitKat) in a backwards compatible fashion.
+ * Helper for accessing features in {@link ScaleGestureDetector}.
  */
 public final class ScaleGestureDetectorCompat {
-    static final ScaleGestureDetectorImpl IMPL;
-
-    interface ScaleGestureDetectorImpl {
-
-        public void setQuickScaleEnabled(Object o, boolean enabled);
-
-        public boolean isQuickScaleEnabled(Object o);
-    }
-
-    private static class BaseScaleGestureDetectorImpl implements ScaleGestureDetectorImpl {
-        BaseScaleGestureDetectorImpl() {
-        }
-
-        @Override
-        public void setQuickScaleEnabled(Object o, boolean enabled) {
-            // Intentionally blank
-        }
-
-        @Override
-        public boolean isQuickScaleEnabled(Object o) {
-            return false;
-        }
-    }
-
-    private static class ScaleGestureDetectorCompatKitKatImpl implements ScaleGestureDetectorImpl {
-        ScaleGestureDetectorCompatKitKatImpl() {
-        }
-
-        @Override
-        public void setQuickScaleEnabled(Object o, boolean enabled) {
-            ScaleGestureDetectorCompatKitKat.setQuickScaleEnabled(o, enabled);
-        }
-
-        @Override
-        public boolean isQuickScaleEnabled(Object o) {
-            return ScaleGestureDetectorCompatKitKat.isQuickScaleEnabled(o);
-        }
-    }
-
-    static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) { // KitKat
-            IMPL = new ScaleGestureDetectorCompatKitKatImpl();
-        } else {
-            IMPL = new BaseScaleGestureDetectorImpl();
-        }
-    }
-
     private ScaleGestureDetectorCompat() {}
 
     /**
-     * Set whether the associated <code>OnScaleGestureListener</code> should receive onScale
-     * callbacks when the user performs a doubleTap followed by a swipe. Note that this is enabled
-     * by default if the app targets API 19 and newer.
+     * Sets whether the associated {@link ScaleGestureDetector.OnScaleGestureListener} should
+     * receive onScale callbacks when the user performs a doubleTap followed by a swipe. Note that
+     * this is enabled by default if the app targets API 19 and newer.
+     *
      * @param enabled true to enable quick scaling, false to disable
+     *
+     * @deprecated Use {@link #setQuickScaleEnabled(ScaleGestureDetector, boolean)} that takes
+     * {@link ScaleGestureDetector} instead of {@link Object}.
      */
+    @Deprecated
     public static void setQuickScaleEnabled(Object scaleGestureDetector, boolean enabled) {
-        IMPL.setQuickScaleEnabled(scaleGestureDetector, enabled);
+        ScaleGestureDetectorCompat.setQuickScaleEnabled(
+                (ScaleGestureDetector) scaleGestureDetector, enabled);
     }
 
     /**
-     * Return whether the quick scale gesture, in which the user performs a double tap followed by a
-     * swipe, should perform scaling. See <code>setQuickScaleEnabled(Object, boolean)<code>.
+     * Sets whether the associated {@link ScaleGestureDetector.OnScaleGestureListener} should
+     * receive onScale callbacks when the user performs a doubleTap followed by a swipe. Note that
+     * this is enabled by default if the app targets API 19 and newer.
+     *
+     * @param enabled true to enable quick scaling, false to disable
      */
+    public static void setQuickScaleEnabled(
+            ScaleGestureDetector scaleGestureDetector, boolean enabled) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            scaleGestureDetector.setQuickScaleEnabled(enabled);
+        }
+    }
+
+    /**
+     * Returns whether the quick scale gesture, in which the user performs a double tap followed by
+     * a swipe, should perform scaling. See
+     * {@link #setQuickScaleEnabled(ScaleGestureDetector, boolean)}.
+     *
+     * @deprecated Use {@link #isQuickScaleEnabled(ScaleGestureDetector)} that takes
+     * {@link ScaleGestureDetector} instead of {@link Object}.
+     */
+    @Deprecated
     public static boolean isQuickScaleEnabled(Object scaleGestureDetector) {
-        return IMPL.isQuickScaleEnabled(scaleGestureDetector); // KitKat
+        return ScaleGestureDetectorCompat.isQuickScaleEnabled(
+                (ScaleGestureDetector) scaleGestureDetector);
+    }
+
+    /**
+     * Returns whether the quick scale gesture, in which the user performs a double tap followed by
+     * a swipe, should perform scaling. See
+     * {@link #setQuickScaleEnabled(ScaleGestureDetector, boolean)}.
+     */
+    public static boolean isQuickScaleEnabled(ScaleGestureDetector scaleGestureDetector) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            return scaleGestureDetector.isQuickScaleEnabled();
+        } else {
+            return false;
+        }
     }
 }
diff --git a/compat/gingerbread/android/support/v4/view/TintableBackgroundView.java b/compat/java/android/support/v4/view/TintableBackgroundView.java
similarity index 100%
rename from compat/gingerbread/android/support/v4/view/TintableBackgroundView.java
rename to compat/java/android/support/v4/view/TintableBackgroundView.java
diff --git a/compat/java/android/support/v4/view/VelocityTrackerCompat.java b/compat/java/android/support/v4/view/VelocityTrackerCompat.java
index 3a02c37..d536085 100644
--- a/compat/java/android/support/v4/view/VelocityTrackerCompat.java
+++ b/compat/java/android/support/v4/view/VelocityTrackerCompat.java
@@ -19,76 +19,34 @@
 import android.view.VelocityTracker;
 
 /**
- * Helper for accessing features in {@link VelocityTracker}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link VelocityTracker}.
+ *
+ * @deprecated Use {@link VelocityTracker} directly.
  */
+@Deprecated
 public final class VelocityTrackerCompat {
     /**
-     * Interface for the full API.
-     */
-    interface VelocityTrackerVersionImpl {
-        public float getXVelocity(VelocityTracker tracker, int pointerId);
-        public float getYVelocity(VelocityTracker tracker, int pointerId);
-    }
-
-    /**
-     * Interface implementation that doesn't use anything about v4 APIs.
-     */
-    static class BaseVelocityTrackerVersionImpl implements VelocityTrackerVersionImpl {
-        @Override
-        public float getXVelocity(VelocityTracker tracker, int pointerId) {
-            return tracker.getXVelocity();
-        }
-        @Override
-        public float getYVelocity(VelocityTracker tracker, int pointerId) {
-            return tracker.getYVelocity();
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least v11 APIs.
-     */
-    static class HoneycombVelocityTrackerVersionImpl implements VelocityTrackerVersionImpl {
-        @Override
-        public float getXVelocity(VelocityTracker tracker, int pointerId) {
-            return VelocityTrackerCompatHoneycomb.getXVelocity(tracker, pointerId);
-        }
-        @Override
-        public float getYVelocity(VelocityTracker tracker, int pointerId) {
-            return VelocityTrackerCompatHoneycomb.getYVelocity(tracker, pointerId);
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final VelocityTrackerVersionImpl IMPL;
-    static {
-        if (android.os.Build.VERSION.SDK_INT >= 11) {
-            IMPL = new HoneycombVelocityTrackerVersionImpl();
-        } else {
-            IMPL = new BaseVelocityTrackerVersionImpl();
-        }
-    }
-
-    // -------------------------------------------------------------------
-
-    /**
      * Call {@link VelocityTracker#getXVelocity(int)}.
      * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} device,
      * returns {@link VelocityTracker#getXVelocity()}.
+     *
+     * @deprecated Use {@link VelocityTracker#getXVelocity(int)} directly.
      */
+    @Deprecated
     public static float getXVelocity(VelocityTracker tracker, int pointerId) {
-        return IMPL.getXVelocity(tracker, pointerId);
+        return tracker.getXVelocity(pointerId);
     }
 
     /**
      * Call {@link VelocityTracker#getYVelocity(int)}.
      * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} device,
      * returns {@link VelocityTracker#getYVelocity()}.
+     *
+     * @deprecated Use {@link VelocityTracker#getYVelocity(int)} directly.
      */
+    @Deprecated
     public static float getYVelocity(VelocityTracker tracker, int pointerId) {
-        return IMPL.getYVelocity(tracker, pointerId);
+        return tracker.getYVelocity(pointerId);
     }
 
     private VelocityTrackerCompat() {}
diff --git a/compat/java/android/support/v4/view/ViewCompat.java b/compat/java/android/support/v4/view/ViewCompat.java
index bacd900..9a66cec 100644
--- a/compat/java/android/support/v4/view/ViewCompat.java
+++ b/compat/java/android/support/v4/view/ViewCompat.java
@@ -18,42 +18,50 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.animation.ValueAnimator;
+import android.content.ClipData;
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.FloatRange;
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.util.Log;
 import android.view.Display;
 import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.WindowInsets;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeProvider;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.Collection;
 import java.util.WeakHashMap;
 
 /**
- * Helper for accessing features in {@link View} introduced after API
- * level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link View}.
  */
 public class ViewCompat {
     private static final String TAG = "ViewCompat";
@@ -107,8 +115,6 @@
     @Deprecated
     public static final int OVER_SCROLL_NEVER = 2;
 
-    private static final long FAKE_FRAME_TIME = 10;
-
     @IntDef({
             IMPORTANT_FOR_ACCESSIBILITY_AUTO,
             IMPORTANT_FOR_ACCESSIBILITY_YES,
@@ -172,13 +178,16 @@
      */
     public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002;
 
-    @IntDef({LAYER_TYPE_NONE, LAYER_TYPE_SOFTWARE, LAYER_TYPE_HARDWARE})
+    @IntDef({View.LAYER_TYPE_NONE, View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE})
     @Retention(RetentionPolicy.SOURCE)
     private @interface LayerType {}
 
     /**
      * Indicates that the view does not have a layer.
+     *
+     * @deprecated Use {@link View#LAYER_TYPE_NONE} directly.
      */
+    @Deprecated
     public static final int LAYER_TYPE_NONE = 0;
 
     /**
@@ -201,7 +210,10 @@
      * potentially be slow (particularly when hardware acceleration is turned on
      * since the layer will have to be uploaded into a hardware texture after every
      * update.)</p>
+     *
+     * @deprecated Use {@link View#LAYER_TYPE_SOFTWARE} directly.
      */
+    @Deprecated
     public static final int LAYER_TYPE_SOFTWARE = 1;
 
     /**
@@ -210,7 +222,7 @@
      * OpenGL hardware) and causes the view to be rendered using Android's hardware
      * rendering pipeline, but only if hardware acceleration is turned on for the
      * view hierarchy. When hardware acceleration is turned off, hardware layers
-     * behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}.</p>
+     * behave exactly as {@link View#LAYER_TYPE_SOFTWARE software layers}.</p>
      *
      * <p>A hardware layer is useful to apply a specific color filter and/or
      * blending mode and/or translucency to a view and all its children.</p>
@@ -221,7 +233,10 @@
      * <p>A hardware layer can also be used to increase the rendering quality when
      * rotation transformations are applied on a view. It can also be used to
      * prevent potential clipping issues when applying 3D transforms on a view.</p>
+     *
+     * @deprecated Use {@link View#LAYER_TYPE_HARDWARE} directly.
      */
+    @Deprecated
     public static final int LAYER_TYPE_HARDWARE = 2;
 
     @IntDef({
@@ -264,13 +279,19 @@
     /**
      * Bits of {@link #getMeasuredWidthAndState} and
      * {@link #getMeasuredWidthAndState} that provide the actual measured size.
+     *
+     * @deprecated Use {@link View#MEASURED_SIZE_MASK} directly.
      */
+    @Deprecated
     public static final int MEASURED_SIZE_MASK = 0x00ffffff;
 
     /**
      * Bits of {@link #getMeasuredWidthAndState} and
      * {@link #getMeasuredWidthAndState} that provide the additional state bits.
+     *
+     * @deprecated Use {@link View#MEASURED_STATE_MASK} directly.
      */
+    @Deprecated
     public static final int MEASURED_STATE_MASK = 0xff000000;
 
     /**
@@ -278,17 +299,31 @@
      * for functions that combine both width and height into a single int,
      * such as {@link #getMeasuredState} and the childState argument of
      * {@link #resolveSizeAndState(int, int, int)}.
+     *
+     * @deprecated Use {@link View#MEASURED_HEIGHT_STATE_SHIFT} directly.
      */
+    @Deprecated
     public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;
 
     /**
      * Bit of {@link #getMeasuredWidthAndState} and
      * {@link #getMeasuredWidthAndState} that indicates the measured size
      * is smaller that the space the view would like to have.
+     *
+     * @deprecated Use {@link View#MEASURED_STATE_TOO_SMALL} directly.
      */
+    @Deprecated
     public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
 
     /**
+     * @hide
+     */
+    @IntDef(value = {SCROLL_AXIS_NONE, SCROLL_AXIS_HORIZONTAL, SCROLL_AXIS_VERTICAL}, flag = true)
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY_GROUP)
+    public @interface ScrollAxis {}
+
+    /**
      * Indicates no axis of view scrolling.
      */
     public static final int SCROLL_AXIS_NONE = 0;
@@ -303,7 +338,27 @@
      */
     public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
 
+    /**
+     * @hide
+     */
+    @IntDef({TYPE_TOUCH, TYPE_NON_TOUCH})
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY_GROUP)
+    public @interface NestedScrollType {}
+
+    /**
+     * Indicates that the input type for the gesture is from a user touching the screen.
+     */
+    public static final int TYPE_TOUCH = 0;
+
+    /**
+     * Indicates that the input type for the gesture is caused by something which is not a user
+     * touching a screen. This is usually from a fling which is settling.
+     */
+    public static final int TYPE_NON_TOUCH = 1;
+
     /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
             value = {
@@ -370,300 +425,160 @@
      */
     public static final int SCROLL_INDICATOR_END = 0x20;
 
-    interface ViewCompatImpl {
-        boolean canScrollHorizontally(View v, int direction);
-        boolean canScrollVertically(View v, int direction);
-        void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
-        void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
-        void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
-        void setAccessibilityDelegate(View v, @Nullable AccessibilityDelegateCompat delegate);
-        boolean hasAccessibilityDelegate(View v);
-        boolean hasTransientState(View view);
-        void setHasTransientState(View view, boolean hasTransientState);
-        void postInvalidateOnAnimation(View view);
-        void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom);
-        void postOnAnimation(View view, Runnable action);
-        void postOnAnimationDelayed(View view, Runnable action, long delayMillis);
-        int getImportantForAccessibility(View view);
-        void setImportantForAccessibility(View view, int mode);
-        boolean isImportantForAccessibility(View view);
-        boolean performAccessibilityAction(View view, int action, Bundle arguments);
-        AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view);
-        float getAlpha(View view);
-        void setLayerType(View view, int layerType, Paint paint);
-        int getLayerType(View view);
-        int getLabelFor(View view);
-        void setLabelFor(View view, int id);
-        void setLayerPaint(View view, Paint paint);
-        int getLayoutDirection(View view);
-        void setLayoutDirection(View view, int layoutDirection);
-        ViewParent getParentForAccessibility(View view);
-        int resolveSizeAndState(int size, int measureSpec, int childMeasuredState);
-        int getMeasuredWidthAndState(View view);
-        int getMeasuredHeightAndState(View view);
-        int getMeasuredState(View view);
-        int getAccessibilityLiveRegion(View view);
-        void setAccessibilityLiveRegion(View view, int mode);
-        int getPaddingStart(View view);
-        int getPaddingEnd(View view);
-        void setPaddingRelative(View view, int start, int top, int end, int bottom);
-        void dispatchStartTemporaryDetach(View view);
-        void dispatchFinishTemporaryDetach(View view);
-        float getX(View view);
-        float getY(View view);
-        float getRotation(View view);
-        float getRotationX(View view);
-        float getRotationY(View view);
-        float getScaleX(View view);
-        float getScaleY(View view);
-        float getTranslationX(View view);
-        float getTranslationY(View view);
-        @Nullable Matrix getMatrix(View view);
-        int getMinimumWidth(View view);
-        int getMinimumHeight(View view);
-        ViewPropertyAnimatorCompat animate(View view);
-        void setRotation(View view, float value);
-        void setRotationX(View view, float value);
-        void setRotationY(View view, float value);
-        void setScaleX(View view, float value);
-        void setScaleY(View view, float value);
-        void setTranslationX(View view, float value);
-        void setTranslationY(View view, float value);
-        void setX(View view, float value);
-        void setY(View view, float value);
-        void setAlpha(View view, float value);
-        void setPivotX(View view, float value);
-        void setPivotY(View view, float value);
-        float getPivotX(View view);
-        float getPivotY(View view);
-        void setElevation(View view, float elevation);
-        float getElevation(View view);
-        void setTranslationZ(View view, float translationZ);
-        float getTranslationZ(View view);
-        void setClipBounds(View view, Rect clipBounds);
-        Rect getClipBounds(View view);
-        void setTransitionName(View view, String transitionName);
-        String getTransitionName(View view);
-        int getWindowSystemUiVisibility(View view);
-        void requestApplyInsets(View view);
-        void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled);
-        boolean getFitsSystemWindows(View view);
-        boolean hasOverlappingRendering(View view);
-        void setFitsSystemWindows(View view, boolean fitSystemWindows);
-        void jumpDrawablesToCurrentState(View v);
-        void setOnApplyWindowInsetsListener(View view, OnApplyWindowInsetsListener listener);
-        WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets);
-        WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets);
-        void setSaveFromParentEnabled(View view, boolean enabled);
-        void setActivated(View view, boolean activated);
-        boolean isPaddingRelative(View view);
-        void setBackground(View view, Drawable background);
-        ColorStateList getBackgroundTintList(View view);
-        void setBackgroundTintList(View view, ColorStateList tintList);
-        PorterDuff.Mode getBackgroundTintMode(View view);
-        void setBackgroundTintMode(View view, PorterDuff.Mode mode);
-        void setNestedScrollingEnabled(View view, boolean enabled);
-        boolean isNestedScrollingEnabled(View view);
-        boolean startNestedScroll(View view, int axes);
-        void stopNestedScroll(View view);
-        boolean hasNestedScrollingParent(View view);
-        boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed, int dxUnconsumed,
-                int dyUnconsumed, int[] offsetInWindow);
-        boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
-                int[] offsetInWindow);
-        boolean dispatchNestedFling(View view, float velocityX, float velocityY, boolean consumed);
-        boolean dispatchNestedPreFling(View view, float velocityX, float velocityY);
-        boolean isInLayout(View view);
-        boolean isLaidOut(View view);
-        boolean isLayoutDirectionResolved(View view);
-        int combineMeasuredStates(int curState, int newState);
-        float getZ(View view);
-        void setZ(View view, float z);
-        boolean isAttachedToWindow(View view);
-        boolean hasOnClickListeners(View view);
-        void setScrollIndicators(View view, int indicators);
-        void setScrollIndicators(View view, int indicators, int mask);
-        int getScrollIndicators(View view);
-        void offsetTopAndBottom(View view, int offset);
-        void offsetLeftAndRight(View view, int offset);
-        void setPointerIcon(View view, PointerIconCompat pointerIcon);
-        Display getDisplay(View view);
-    }
-
-    static class BaseViewCompatImpl implements ViewCompatImpl {
+    static class ViewCompatBaseImpl {
+        private static Field sMinWidthField;
+        private static boolean sMinWidthFieldFetched;
+        private static Field sMinHeightField;
+        private static boolean sMinHeightFieldFetched;
+        private static WeakHashMap<View, String> sTransitionNameMap;
         private Method mDispatchStartTemporaryDetach;
         private Method mDispatchFinishTemporaryDetach;
         private boolean mTempDetachBound;
         WeakHashMap<View, ViewPropertyAnimatorCompat> mViewPropertyAnimatorCompatMap = null;
         private static Method sChildrenDrawingOrderMethod;
+        static Field sAccessibilityDelegateField;
+        static boolean sAccessibilityDelegateCheckFailed = false;
 
-        @Override
-        public boolean canScrollHorizontally(View v, int direction) {
-            return (v instanceof ScrollingView) &&
-                canScrollingViewScrollHorizontally((ScrollingView) v, direction);
-        }
-        @Override
-        public boolean canScrollVertically(View v, int direction) {
-            return (v instanceof ScrollingView) &&
-                    canScrollingViewScrollVertically((ScrollingView) v, direction);
+        public void setAccessibilityDelegate(View v,
+                @Nullable AccessibilityDelegateCompat delegate) {
+            v.setAccessibilityDelegate(delegate == null ? null : delegate.getBridge());
         }
 
-        @Override
-        public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
-            // Do nothing; API doesn't exist
-        }
-
-        @Override
         public boolean hasAccessibilityDelegate(View v) {
-            return false;
+            if (sAccessibilityDelegateCheckFailed) {
+                return false; // View implementation might have changed.
+            }
+            if (sAccessibilityDelegateField == null) {
+                try {
+                    sAccessibilityDelegateField = View.class
+                            .getDeclaredField("mAccessibilityDelegate");
+                    sAccessibilityDelegateField.setAccessible(true);
+                } catch (Throwable t) {
+                    sAccessibilityDelegateCheckFailed = true;
+                    return false;
+                }
+            }
+            try {
+                return sAccessibilityDelegateField.get(v) != null;
+            } catch (Throwable t) {
+                sAccessibilityDelegateCheckFailed = true;
+                return false;
+            }
         }
 
-        @Override
-        public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
-            // Do nothing; API doesn't exist
-        }
-        @Override
-        public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
-         // Do nothing; API doesn't exist
-        }
-        @Override
         public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
-            // Do nothing; API doesn't exist
+            v.onInitializeAccessibilityNodeInfo(info.unwrap());
         }
-        @Override
+
+        @SuppressWarnings("deprecation")
+        public boolean startDragAndDrop(View v, ClipData data, View.DragShadowBuilder shadowBuilder,
+                Object localState, int flags) {
+            return v.startDrag(data, shadowBuilder, localState, flags);
+        }
+
+        public void cancelDragAndDrop(View v) {
+            // no-op
+        }
+
+        public void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder) {
+            // no-op
+        }
+
         public boolean hasTransientState(View view) {
             // A view can't have transient state if transient state wasn't supported.
             return false;
         }
-        @Override
+
         public void setHasTransientState(View view, boolean hasTransientState) {
             // Do nothing; API doesn't exist
         }
-        @Override
+
         public void postInvalidateOnAnimation(View view) {
             view.postInvalidate();
         }
-        @Override
+
         public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) {
             view.postInvalidate(left, top, right, bottom);
         }
-        @Override
+
         public void postOnAnimation(View view, Runnable action) {
             view.postDelayed(action, getFrameTime());
         }
-        @Override
+
         public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
             view.postDelayed(action, getFrameTime() + delayMillis);
         }
+
         long getFrameTime() {
-            return FAKE_FRAME_TIME;
+            return ValueAnimator.getFrameDelay();
         }
-        @Override
+
         public int getImportantForAccessibility(View view) {
             return 0;
         }
-        @Override
-        public void setImportantForAccessibility(View view, int mode) {
 
+        public void setImportantForAccessibility(View view, int mode) {
         }
-        @Override
+
         public boolean isImportantForAccessibility(View view) {
             return true;
         }
-        @Override
+
         public boolean performAccessibilityAction(View view, int action, Bundle arguments) {
             return false;
         }
-        @Override
+
         public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
             return null;
         }
-        @Override
-        public float getAlpha(View view) {
-            return 1.0f;
-        }
-        @Override
-        public void setLayerType(View view, int layerType, Paint paint) {
-            // No-op until layers became available (HC)
-        }
-        @Override
-        public int getLayerType(View view) {
-            return LAYER_TYPE_NONE;
-        }
-        @Override
+
         public int getLabelFor(View view) {
             return 0;
         }
-        @Override
+
         public void setLabelFor(View view, int id) {
-
-        }
-        @Override
-        public void setLayerPaint(View view, Paint p) {
-            // No-op until layers became available (HC)
         }
 
-        @Override
+        public void setLayerPaint(View view, Paint paint) {
+            // Make sure the paint is correct; this will be cheap if it's the same
+            // instance as was used to call setLayerType earlier.
+            view.setLayerType(view.getLayerType(), paint);
+            // This is expensive, but the only way to accomplish this before JB-MR1.
+            view.invalidate();
+        }
+
         public int getLayoutDirection(View view) {
             return LAYOUT_DIRECTION_LTR;
         }
 
-        @Override
         public void setLayoutDirection(View view, int layoutDirection) {
             // No-op
         }
 
-        @Override
         public ViewParent getParentForAccessibility(View view) {
             return view.getParent();
         }
 
-        @Override
-        public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
-            return View.resolveSize(size, measureSpec);
-        }
-
-        @Override
-        public int getMeasuredWidthAndState(View view) {
-            return view.getMeasuredWidth();
-        }
-
-        @Override
-        public int getMeasuredHeightAndState(View view) {
-            return view.getMeasuredHeight();
-        }
-
-        @Override
-        public int getMeasuredState(View view) {
-            return 0;
-        }
-
-        @Override
         public int getAccessibilityLiveRegion(View view) {
             return ACCESSIBILITY_LIVE_REGION_NONE;
         }
 
-        @Override
         public void setAccessibilityLiveRegion(View view, int mode) {
             // No-op
         }
 
-        @Override
         public int getPaddingStart(View view) {
             return view.getPaddingLeft();
         }
 
-        @Override
         public int getPaddingEnd(View view) {
             return view.getPaddingRight();
         }
 
-        @Override
         public void setPaddingRelative(View view, int start, int top, int end, int bottom) {
             view.setPadding(start, top, end, bottom);
         }
 
-        @Override
         public void dispatchStartTemporaryDetach(View view) {
             if (!mTempDetachBound) {
                 bindTempDetach();
@@ -680,7 +595,6 @@
             }
         }
 
-        @Override
         public void dispatchFinishTemporaryDetach(View view) {
             if (!mTempDetachBound) {
                 bindTempDetach();
@@ -697,7 +611,6 @@
             }
         }
 
-        @Override
         public boolean hasOverlappingRendering(View view) {
             return true;
         }
@@ -714,187 +627,106 @@
             mTempDetachBound = true;
         }
 
-        @Override
-        public float getTranslationX(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getTranslationY(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getX(View view) {
-            return view.getLeft();
-        }
-
-        @Override
-        public float getY(View view) {
-            return view.getTop();
-        }
-
-        @Override
-        public float getRotation(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getRotationX(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getRotationY(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getScaleX(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getScaleY(View view) {
-            return 0;
-        }
-
-        @Override
-        public Matrix getMatrix(View view) {
-            return null;
-        }
-
-        @Override
         public int getMinimumWidth(View view) {
-            return ViewCompatBase.getMinimumWidth(view);
+            if (!sMinWidthFieldFetched) {
+                try {
+                    sMinWidthField = View.class.getDeclaredField("mMinWidth");
+                    sMinWidthField.setAccessible(true);
+                } catch (NoSuchFieldException e) {
+                    // Couldn't find the field. Abort!
+                }
+                sMinWidthFieldFetched = true;
+            }
+
+            if (sMinWidthField != null) {
+                try {
+                    return (int) sMinWidthField.get(view);
+                } catch (Exception e) {
+                    // Field get failed. Oh well...
+                }
+            }
+
+            // We failed, return 0
+            return 0;
         }
 
-        @Override
         public int getMinimumHeight(View view) {
-            return ViewCompatBase.getMinimumHeight(view);
+            if (!sMinHeightFieldFetched) {
+                try {
+                    sMinHeightField = View.class.getDeclaredField("mMinHeight");
+                    sMinHeightField.setAccessible(true);
+                } catch (NoSuchFieldException e) {
+                    // Couldn't find the field. Abort!
+                }
+                sMinHeightFieldFetched = true;
+            }
+
+            if (sMinHeightField != null) {
+                try {
+                    return (int) sMinHeightField.get(view);
+                } catch (Exception e) {
+                    // Field get failed. Oh well...
+                }
+            }
+
+            // We failed, return 0
+            return 0;
         }
 
-        @Override
         public ViewPropertyAnimatorCompat animate(View view) {
-            return new ViewPropertyAnimatorCompat(view);
+            if (mViewPropertyAnimatorCompatMap == null) {
+                mViewPropertyAnimatorCompatMap = new WeakHashMap<>();
+            }
+            ViewPropertyAnimatorCompat vpa = mViewPropertyAnimatorCompatMap.get(view);
+            if (vpa == null) {
+                vpa = new ViewPropertyAnimatorCompat(view);
+                mViewPropertyAnimatorCompatMap.put(view, vpa);
+            }
+            return vpa;
         }
 
-        @Override
-        public void setRotation(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setTranslationX(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setTranslationY(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setAlpha(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setRotationX(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setRotationY(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setScaleX(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setScaleY(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setX(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setY(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setPivotX(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public void setPivotY(View view, float value) {
-            // noop
-        }
-
-        @Override
-        public float getPivotX(View view) {
-            return 0;
-        }
-
-        @Override
-        public float getPivotY(View view) {
-            return 0;
-        }
-
-        @Override
         public void setTransitionName(View view, String transitionName) {
+            if (sTransitionNameMap == null) {
+                sTransitionNameMap = new WeakHashMap<>();
+            }
+            sTransitionNameMap.put(view, transitionName);
         }
 
-        @Override
         public String getTransitionName(View view) {
-            return null;
+            if (sTransitionNameMap == null) {
+                return null;
+            }
+            return sTransitionNameMap.get(view);
         }
 
-        @Override
         public int getWindowSystemUiVisibility(View view) {
             return 0;
         }
 
-        @Override
         public void requestApplyInsets(View view) {
         }
 
-        @Override
         public void setElevation(View view, float elevation) {
         }
 
-        @Override
         public float getElevation(View view) {
             return 0f;
         }
 
-        @Override
         public void setTranslationZ(View view, float translationZ) {
         }
 
-        @Override
         public float getTranslationZ(View view) {
             return 0f;
         }
 
-        @Override
         public void setClipBounds(View view, Rect clipBounds) {
         }
 
-        @Override
         public Rect getClipBounds(View view) {
             return null;
         }
 
-        @Override
         public void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) {
             if (sChildrenDrawingOrderMethod == null) {
                 try {
@@ -916,60 +748,33 @@
             }
         }
 
-        @Override
         public boolean getFitsSystemWindows(View view) {
             return false;
         }
 
-        @Override
-        public void setFitsSystemWindows(View view, boolean fitSystemWindows) {
-            // noop
-        }
-
-        @Override
-        public void jumpDrawablesToCurrentState(View view) {
-            // Do nothing; API didn't exist.
-        }
-
-        @Override
         public void setOnApplyWindowInsetsListener(View view,
                 OnApplyWindowInsetsListener listener) {
             // noop
         }
 
-        @Override
         public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
             return insets;
         }
 
-        @Override
         public WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
             return insets;
         }
 
-        @Override
-        public void setSaveFromParentEnabled(View v, boolean enabled) {
-            // noop
-        }
-
-        @Override
-        public void setActivated(View view, boolean activated) {
-            // noop
-        }
-
-        @Override
         public boolean isPaddingRelative(View view) {
             return false;
         }
 
-        @Override
         public void setNestedScrollingEnabled(View view, boolean enabled) {
             if (view instanceof NestedScrollingChild) {
                 ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled);
             }
         }
 
-        @Override
         public boolean isNestedScrollingEnabled(View view) {
             if (view instanceof NestedScrollingChild) {
                 return ((NestedScrollingChild) view).isNestedScrollingEnabled();
@@ -977,56 +782,34 @@
             return false;
         }
 
-        @Override
         public void setBackground(View view, Drawable background) {
             view.setBackgroundDrawable(background);
         }
 
-        @Override
         public ColorStateList getBackgroundTintList(View view) {
-            return ViewCompatBase.getBackgroundTintList(view);
+            return (view instanceof TintableBackgroundView)
+                    ? ((TintableBackgroundView) view).getSupportBackgroundTintList()
+                    : null;
         }
 
-        @Override
         public void setBackgroundTintList(View view, ColorStateList tintList) {
-            ViewCompatBase.setBackgroundTintList(view, tintList);
+            if (view instanceof TintableBackgroundView) {
+                ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList);
+            }
         }
 
-        @Override
         public void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
-            ViewCompatBase.setBackgroundTintMode(view, mode);
+            if (view instanceof TintableBackgroundView) {
+                ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode);
+            }
         }
 
-        @Override
         public PorterDuff.Mode getBackgroundTintMode(View view) {
-            return ViewCompatBase.getBackgroundTintMode(view);
+            return (view instanceof TintableBackgroundView)
+                    ? ((TintableBackgroundView) view).getSupportBackgroundTintMode()
+                    : null;
         }
 
-        private boolean canScrollingViewScrollHorizontally(ScrollingView view, int direction) {
-            final int offset = view.computeHorizontalScrollOffset();
-            final int range = view.computeHorizontalScrollRange() -
-                    view.computeHorizontalScrollExtent();
-            if (range == 0) return false;
-            if (direction < 0) {
-                return offset > 0;
-            } else {
-                return offset < range - 1;
-            }
-        }
-
-        private boolean canScrollingViewScrollVertically(ScrollingView view, int direction) {
-            final int offset = view.computeVerticalScrollOffset();
-            final int range = view.computeVerticalScrollRange() -
-                    view.computeVerticalScrollExtent();
-            if (range == 0) return false;
-            if (direction < 0) {
-                return offset > 0;
-            } else {
-                return offset < range - 1;
-            }
-        }
-
-        @Override
         public boolean startNestedScroll(View view, int axes) {
             if (view instanceof NestedScrollingChild) {
                 return ((NestedScrollingChild) view).startNestedScroll(axes);
@@ -1034,14 +817,12 @@
             return false;
         }
 
-        @Override
         public void stopNestedScroll(View view) {
             if (view instanceof NestedScrollingChild) {
                 ((NestedScrollingChild) view).stopNestedScroll();
             }
         }
 
-        @Override
         public boolean hasNestedScrollingParent(View view) {
             if (view instanceof NestedScrollingChild) {
                 return ((NestedScrollingChild) view).hasNestedScrollingParent();
@@ -1049,7 +830,6 @@
             return false;
         }
 
-        @Override
         public boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
             if (view instanceof NestedScrollingChild) {
@@ -1059,7 +839,6 @@
             return false;
         }
 
-        @Override
         public boolean dispatchNestedPreScroll(View view, int dx, int dy,
                 int[] consumed, int[] offsetInWindow) {
             if (view instanceof NestedScrollingChild) {
@@ -1069,7 +848,6 @@
             return false;
         }
 
-        @Override
         public boolean dispatchNestedFling(View view, float velocityX, float velocityY,
                 boolean consumed) {
             if (view instanceof NestedScrollingChild) {
@@ -1079,7 +857,6 @@
             return false;
         }
 
-        @Override
         public boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
             if (view instanceof NestedScrollingChild) {
                 return ((NestedScrollingChild) view).dispatchNestedPreFling(velocityX, velocityY);
@@ -1087,366 +864,172 @@
             return false;
         }
 
-        @Override
         public boolean isInLayout(View view) {
             return false;
         }
 
-        @Override
         public boolean isLaidOut(View view) {
-            return ViewCompatBase.isLaidOut(view);
+            return view.getWidth() > 0 && view.getHeight() > 0;
         }
 
-        @Override
         public boolean isLayoutDirectionResolved(View view) {
             return false;
         }
 
-        @Override
-        public int combineMeasuredStates(int curState, int newState) {
-            return curState | newState;
-        }
-
-        @Override
         public float getZ(View view) {
             return getTranslationZ(view) + getElevation(view);
         }
 
-        @Override
         public void setZ(View view, float z) {
             // no-op
         }
 
-        @Override
         public boolean isAttachedToWindow(View view) {
-            return ViewCompatBase.isAttachedToWindow(view);
+            return view.getWindowToken() != null;
         }
 
-        @Override
         public boolean hasOnClickListeners(View view) {
             return false;
         }
 
-        @Override
         public int getScrollIndicators(View view) {
             return 0;
         }
 
-        @Override
         public void setScrollIndicators(View view, int indicators) {
             // no-op
         }
 
-        @Override
         public void setScrollIndicators(View view, int indicators, int mask) {
             // no-op
         }
 
-        @Override
         public void offsetLeftAndRight(View view, int offset) {
-            ViewCompatBase.offsetLeftAndRight(view, offset);
+            view.offsetLeftAndRight(offset);
+            if (view.getVisibility() == View.VISIBLE) {
+                tickleInvalidationFlag(view);
+
+                ViewParent parent = view.getParent();
+                if (parent instanceof View) {
+                    tickleInvalidationFlag((View) parent);
+                }
+            }
         }
 
-        @Override
         public void offsetTopAndBottom(View view, int offset) {
-            ViewCompatBase.offsetTopAndBottom(view, offset);
+            view.offsetTopAndBottom(offset);
+            if (view.getVisibility() == View.VISIBLE) {
+                tickleInvalidationFlag(view);
+
+                ViewParent parent = view.getParent();
+                if (parent instanceof View) {
+                    tickleInvalidationFlag((View) parent);
+                }
+            }
         }
 
-        @Override
+        private static void tickleInvalidationFlag(View view) {
+            final float y = view.getTranslationY();
+            view.setTranslationY(y + 1);
+            view.setTranslationY(y);
+        }
+
         public void setPointerIcon(View view, PointerIconCompat pointerIcon) {
             // no-op
         }
 
-        @Override
         public Display getDisplay(View view) {
-            return ViewCompatBase.getDisplay(view);
+            if (isAttachedToWindow(view)) {
+                final WindowManager wm = (WindowManager) view.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+                return wm.getDefaultDisplay();
+            }
+            return null;
+        }
+
+        public void setTooltipText(View view, CharSequence tooltipText) {
+        }
+
+        public int getNextClusterForwardId(@NonNull View view) {
+            return View.NO_ID;
+        }
+
+        public void setNextClusterForwardId(@NonNull View view, int nextClusterForwardId) {
+            // no-op
+        }
+
+        public boolean isKeyboardNavigationCluster(@NonNull View view) {
+            return false;
+        }
+
+        public void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) {
+            // no-op
+        }
+
+        public boolean isFocusedByDefault(@NonNull View view) {
+            return false;
+        }
+
+        public void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) {
+            // no-op
+        }
+
+        public View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster,
+                @FocusDirection int direction) {
+            return null;
+        }
+
+        public void addKeyboardNavigationClusters(@NonNull View view,
+                @NonNull Collection<View> views, int direction) {
+            // no-op
+        }
+
+        public boolean restoreDefaultFocus(@NonNull View view) {
+            return view.requestFocus();
+        }
+
+        public boolean hasExplicitFocusable(@NonNull View view) {
+            return view.hasFocusable();
         }
     }
 
-    static class HCViewCompatImpl extends BaseViewCompatImpl {
-        @Override
-        long getFrameTime() {
-            return ViewCompatHC.getFrameTime();
-        }
-        @Override
-        public float getAlpha(View view) {
-            return ViewCompatHC.getAlpha(view);
-        }
-        @Override
-        public void setLayerType(View view, int layerType, Paint paint) {
-            ViewCompatHC.setLayerType(view, layerType, paint);
-        }
-        @Override
-        public int getLayerType(View view)  {
-            return ViewCompatHC.getLayerType(view);
-        }
-        @Override
-        public void setLayerPaint(View view, Paint paint) {
-            // Make sure the paint is correct; this will be cheap if it's the same
-            // instance as was used to call setLayerType earlier.
-            setLayerType(view, getLayerType(view), paint);
-            // This is expensive, but the only way to accomplish this before JB-MR1.
-            view.invalidate();
-        }
-        @Override
-        public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
-            return ViewCompatHC.resolveSizeAndState(size, measureSpec, childMeasuredState);
-        }
-        @Override
-        public int getMeasuredWidthAndState(View view) {
-            return ViewCompatHC.getMeasuredWidthAndState(view);
-        }
-        @Override
-        public int getMeasuredHeightAndState(View view) {
-            return ViewCompatHC.getMeasuredHeightAndState(view);
-        }
-        @Override
-        public int getMeasuredState(View view) {
-            return ViewCompatHC.getMeasuredState(view);
-        }
-        @Override
-        public float getTranslationX(View view) {
-            return ViewCompatHC.getTranslationX(view);
-        }
-        @Override
-        public float getTranslationY(View view) {
-            return ViewCompatHC.getTranslationY(view);
-        }
-
-        @Override
-        public Matrix getMatrix(View view) {
-            return ViewCompatHC.getMatrix(view);
-        }
-
-        @Override
-        public void setTranslationX(View view, float value) {
-            ViewCompatHC.setTranslationX(view, value);
-        }
-        @Override
-        public void setTranslationY(View view, float value) {
-            ViewCompatHC.setTranslationY(view, value);
-        }
-        @Override
-        public void setAlpha(View view, float value) {
-            ViewCompatHC.setAlpha(view, value);
-        }
-        @Override
-        public void setX(View view, float value) {
-            ViewCompatHC.setX(view, value);
-        }
-        @Override
-        public void setY(View view, float value) {
-            ViewCompatHC.setY(view, value);
-        }
-        @Override
-        public void setRotation(View view, float value) {
-            ViewCompatHC.setRotation(view, value);
-        }
-        @Override
-        public void setRotationX(View view, float value) {
-            ViewCompatHC.setRotationX(view, value);
-        }
-        @Override
-        public void setRotationY(View view, float value) {
-            ViewCompatHC.setRotationY(view, value);
-        }
-        @Override
-        public void setScaleX(View view, float value) {
-            ViewCompatHC.setScaleX(view, value);
-        }
-        @Override
-        public void setScaleY(View view, float value) {
-            ViewCompatHC.setScaleY(view, value);
-        }
-        @Override
-        public void setPivotX(View view, float value) {
-            ViewCompatHC.setPivotX(view, value);
-        }
-        @Override
-        public void setPivotY(View view, float value) {
-            ViewCompatHC.setPivotY(view, value);
-        }
-        @Override
-        public float getX(View view) {
-            return ViewCompatHC.getX(view);
-        }
-
-        @Override
-        public float getY(View view) {
-            return ViewCompatHC.getY(view);
-        }
-
-        @Override
-        public float getRotation(View view) {
-            return ViewCompatHC.getRotation(view);
-        }
-
-        @Override
-        public float getRotationX(View view) {
-            return ViewCompatHC.getRotationX(view);
-        }
-
-        @Override
-        public float getRotationY(View view) {
-            return ViewCompatHC.getRotationY(view);
-        }
-
-        @Override
-        public float getScaleX(View view) {
-            return ViewCompatHC.getScaleX(view);
-        }
-
-        @Override
-        public float getScaleY(View view) {
-            return ViewCompatHC.getScaleY(view);
-        }
-
-        @Override
-        public float getPivotX(View view) {
-            return ViewCompatHC.getPivotX(view);
-        }
-        @Override
-        public float getPivotY(View view) {
-            return ViewCompatHC.getPivotY(view);
-        }
-        @Override
-        public void jumpDrawablesToCurrentState(View view) {
-            ViewCompatHC.jumpDrawablesToCurrentState(view);
-        }
-
-        @Override
-        public void setSaveFromParentEnabled(View view, boolean enabled) {
-            ViewCompatHC.setSaveFromParentEnabled(view, enabled);
-        }
-
-        @Override
-        public void setActivated(View view, boolean activated) {
-            ViewCompatHC.setActivated(view, activated);
-        }
-
-        @Override
-        public int combineMeasuredStates(int curState, int newState) {
-            return ViewCompatHC.combineMeasuredStates(curState, newState);
-        }
-
-        @Override
-        public void offsetLeftAndRight(View view, int offset) {
-            ViewCompatHC.offsetLeftAndRight(view, offset);
-        }
-
-        @Override
-        public void offsetTopAndBottom(View view, int offset) {
-            ViewCompatHC.offsetTopAndBottom(view, offset);
-        }
-    }
-
-    static class ICSViewCompatImpl extends HCViewCompatImpl {
-        static Field mAccessibilityDelegateField;
-        static boolean accessibilityDelegateCheckFailed = false;
-        @Override
-        public boolean canScrollHorizontally(View v, int direction) {
-            return ViewCompatICS.canScrollHorizontally(v, direction);
-        }
-        @Override
-        public boolean canScrollVertically(View v, int direction) {
-            return ViewCompatICS.canScrollVertically(v, direction);
-        }
-        @Override
-        public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
-            ViewCompatICS.onPopulateAccessibilityEvent(v, event);
-        }
-        @Override
-        public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
-            ViewCompatICS.onInitializeAccessibilityEvent(v, event);
-        }
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
-            ViewCompatICS.onInitializeAccessibilityNodeInfo(v, info.getInfo());
-        }
-        @Override
-        public void setAccessibilityDelegate(View v,
-                @Nullable AccessibilityDelegateCompat delegate) {
-            ViewCompatICS.setAccessibilityDelegate(v,
-                    delegate == null ? null : delegate.getBridge());
-        }
-
-        @Override
-        public boolean hasAccessibilityDelegate(View v) {
-            if (accessibilityDelegateCheckFailed) {
-                return false; // View implementation might have changed.
-            }
-            if (mAccessibilityDelegateField == null) {
-                try {
-                    mAccessibilityDelegateField = View.class
-                            .getDeclaredField("mAccessibilityDelegate");
-                    mAccessibilityDelegateField.setAccessible(true);
-                } catch (Throwable t) {
-                    accessibilityDelegateCheckFailed = true;
-                    return false;
-                }
-            }
-            try {
-                return mAccessibilityDelegateField.get(v) != null;
-            } catch (Throwable t) {
-                accessibilityDelegateCheckFailed = true;
-                return false;
-            }
-        }
-
-        @Override
-        public ViewPropertyAnimatorCompat animate(View view) {
-            if (mViewPropertyAnimatorCompatMap == null) {
-                mViewPropertyAnimatorCompatMap = new WeakHashMap<>();
-            }
-            ViewPropertyAnimatorCompat vpa = mViewPropertyAnimatorCompatMap.get(view);
-            if (vpa == null) {
-                vpa = new ViewPropertyAnimatorCompat(view);
-                mViewPropertyAnimatorCompatMap.put(view, vpa);
-            }
-            return vpa;
-        }
-
-        @Override
-        public void setFitsSystemWindows(View view, boolean fitSystemWindows) {
-            ViewCompatICS.setFitsSystemWindows(view, fitSystemWindows);
-        }
-    }
-
-    static class ICSMr1ViewCompatImpl extends ICSViewCompatImpl {
+    @RequiresApi(15)
+    static class ViewCompatApi15Impl extends ViewCompatBaseImpl {
         @Override
         public boolean hasOnClickListeners(View view) {
-            return ViewCompatICSMr1.hasOnClickListeners(view);
+            return view.hasOnClickListeners();
         }
     }
 
-    static class JBViewCompatImpl extends ICSMr1ViewCompatImpl {
+    @RequiresApi(16)
+    static class ViewCompatApi16Impl extends ViewCompatApi15Impl {
         @Override
         public boolean hasTransientState(View view) {
-            return ViewCompatJB.hasTransientState(view);
+            return view.hasTransientState();
         }
         @Override
         public void setHasTransientState(View view, boolean hasTransientState) {
-            ViewCompatJB.setHasTransientState(view, hasTransientState);
+            view.setHasTransientState(hasTransientState);
         }
         @Override
         public void postInvalidateOnAnimation(View view) {
-            ViewCompatJB.postInvalidateOnAnimation(view);
+            view.postInvalidateOnAnimation();
         }
         @Override
         public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) {
-            ViewCompatJB.postInvalidateOnAnimation(view, left, top, right, bottom);
+            view.postInvalidateOnAnimation(left, top, right, bottom);
         }
         @Override
         public void postOnAnimation(View view, Runnable action) {
-            ViewCompatJB.postOnAnimation(view, action);
+            view.postOnAnimation(action);
         }
         @Override
         public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
-            ViewCompatJB.postOnAnimationDelayed(view, action, delayMillis);
+            view.postOnAnimationDelayed(action, delayMillis);
         }
         @Override
         public int getImportantForAccessibility(View view) {
-            return ViewCompatJB.getImportantForAccessibility(view);
+            return view.getImportantForAccessibility();
         }
         @Override
         public void setImportantForAccessibility(View view, int mode) {
@@ -1456,413 +1039,599 @@
             if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
                 mode = IMPORTANT_FOR_ACCESSIBILITY_NO;
             }
-            ViewCompatJB.setImportantForAccessibility(view, mode);
+            //noinspection WrongConstant
+            view.setImportantForAccessibility(mode);
         }
         @Override
         public boolean performAccessibilityAction(View view, int action, Bundle arguments) {
-            return ViewCompatJB.performAccessibilityAction(view, action, arguments);
+            return view.performAccessibilityAction(action, arguments);
         }
         @Override
         public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
-            Object compat = ViewCompatJB.getAccessibilityNodeProvider(view);
-            if (compat != null) {
-                return new AccessibilityNodeProviderCompat(compat);
+            AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+            if (provider != null) {
+                return new AccessibilityNodeProviderCompat(provider);
             }
             return null;
         }
 
         @Override
         public ViewParent getParentForAccessibility(View view) {
-            return ViewCompatJB.getParentForAccessibility(view);
+            return view.getParentForAccessibility();
         }
 
         @Override
         public int getMinimumWidth(View view) {
-            return ViewCompatJB.getMinimumWidth(view);
+            return view.getMinimumWidth();
         }
 
         @Override
         public int getMinimumHeight(View view) {
-            return ViewCompatJB.getMinimumHeight(view);
+            return view.getMinimumHeight();
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public void requestApplyInsets(View view) {
-            ViewCompatJB.requestApplyInsets(view);
+            view.requestFitSystemWindows();
         }
 
         @Override
         public boolean getFitsSystemWindows(View view) {
-            return ViewCompatJB.getFitsSystemWindows(view);
+            return view.getFitsSystemWindows();
         }
 
         @Override
         public boolean hasOverlappingRendering(View view) {
-            return ViewCompatJB.hasOverlappingRendering(view);
+            return view.hasOverlappingRendering();
         }
 
         @Override
         public void setBackground(View view, Drawable background) {
-            ViewCompatJB.setBackground(view, background);
+            view.setBackground(background);
         }
     }
 
-    static class JbMr1ViewCompatImpl extends JBViewCompatImpl {
+    @RequiresApi(17)
+    static class ViewCompatApi17Impl extends ViewCompatApi16Impl {
 
         @Override
         public int getLabelFor(View view) {
-            return ViewCompatJellybeanMr1.getLabelFor(view);
+            return view.getLabelFor();
         }
 
         @Override
         public void setLabelFor(View view, int id) {
-            ViewCompatJellybeanMr1.setLabelFor(view, id);
+            view.setLabelFor(id);
         }
 
         @Override
         public void setLayerPaint(View view, Paint paint) {
-            ViewCompatJellybeanMr1.setLayerPaint(view, paint);
+            view.setLayerPaint(paint);
         }
 
         @Override
         public int getLayoutDirection(View view) {
-            return ViewCompatJellybeanMr1.getLayoutDirection(view);
+            return view.getLayoutDirection();
         }
 
         @Override
         public void setLayoutDirection(View view, int layoutDirection) {
-            ViewCompatJellybeanMr1.setLayoutDirection(view, layoutDirection);
+            view.setLayoutDirection(layoutDirection);
         }
 
         @Override
         public int getPaddingStart(View view) {
-            return ViewCompatJellybeanMr1.getPaddingStart(view);
+            return view.getPaddingStart();
         }
 
         @Override
         public int getPaddingEnd(View view) {
-            return ViewCompatJellybeanMr1.getPaddingEnd(view);
+            return view.getPaddingEnd();
         }
 
         @Override
         public void setPaddingRelative(View view, int start, int top, int end, int bottom) {
-            ViewCompatJellybeanMr1.setPaddingRelative(view, start, top, end, bottom);
+            view.setPaddingRelative(start, top, end, bottom);
         }
 
         @Override
         public int getWindowSystemUiVisibility(View view) {
-            return ViewCompatJellybeanMr1.getWindowSystemUiVisibility(view);
+            return view.getWindowSystemUiVisibility();
         }
 
         @Override
         public boolean isPaddingRelative(View view) {
-            return ViewCompatJellybeanMr1.isPaddingRelative(view);
+            return view.isPaddingRelative();
         }
 
         @Override
         public Display getDisplay(View view) {
-            return ViewCompatJellybeanMr1.getDisplay(view);
+            return view.getDisplay();
         }
     }
 
-    static class JbMr2ViewCompatImpl extends JbMr1ViewCompatImpl {
+    @RequiresApi(18)
+    static class ViewCompatApi18Impl extends ViewCompatApi17Impl {
         @Override
         public void setClipBounds(View view, Rect clipBounds) {
-            ViewCompatJellybeanMr2.setClipBounds(view, clipBounds);
+            view.setClipBounds(clipBounds);
         }
 
         @Override
         public Rect getClipBounds(View view) {
-            return ViewCompatJellybeanMr2.getClipBounds(view);
+            return view.getClipBounds();
         }
 
         @Override
         public boolean isInLayout(View view) {
-            return ViewCompatJellybeanMr2.isInLayout(view);
+            return view.isInLayout();
         }
     }
 
-    static class KitKatViewCompatImpl extends JbMr2ViewCompatImpl {
+    @RequiresApi(19)
+    static class ViewCompatApi19Impl extends ViewCompatApi18Impl {
         @Override
         public int getAccessibilityLiveRegion(View view) {
-            return ViewCompatKitKat.getAccessibilityLiveRegion(view);
+            return view.getAccessibilityLiveRegion();
         }
 
         @Override
         public void setAccessibilityLiveRegion(View view, int mode) {
-            ViewCompatKitKat.setAccessibilityLiveRegion(view, mode);
+            view.setAccessibilityLiveRegion(mode);
         }
 
         @Override
         public void setImportantForAccessibility(View view, int mode) {
-            ViewCompatJB.setImportantForAccessibility(view, mode);
+            view.setImportantForAccessibility(mode);
         }
 
         @Override
         public boolean isLaidOut(View view) {
-            return ViewCompatKitKat.isLaidOut(view);
+            return view.isLaidOut();
         }
 
         @Override
         public boolean isLayoutDirectionResolved(View view) {
-            return ViewCompatKitKat.isLayoutDirectionResolved(view);
+            return view.isLayoutDirectionResolved();
         }
 
         @Override
         public boolean isAttachedToWindow(View view) {
-            return ViewCompatKitKat.isAttachedToWindow(view);
+            return view.isAttachedToWindow();
         }
     }
 
-    static class LollipopViewCompatImpl extends KitKatViewCompatImpl {
+    @RequiresApi(21)
+    static class ViewCompatApi21Impl extends ViewCompatApi19Impl {
+        private static ThreadLocal<Rect> sThreadLocalRect;
+
         @Override
         public void setTransitionName(View view, String transitionName) {
-            ViewCompatLollipop.setTransitionName(view, transitionName);
+            view.setTransitionName(transitionName);
         }
 
         @Override
         public String getTransitionName(View view) {
-            return ViewCompatLollipop.getTransitionName(view);
+            return view.getTransitionName();
         }
 
         @Override
         public void requestApplyInsets(View view) {
-            ViewCompatLollipop.requestApplyInsets(view);
+            view.requestApplyInsets();
         }
 
         @Override
         public void setElevation(View view, float elevation) {
-            ViewCompatLollipop.setElevation(view, elevation);
+            view.setElevation(elevation);
         }
 
         @Override
         public float getElevation(View view) {
-            return ViewCompatLollipop.getElevation(view);
+            return view.getElevation();
         }
 
         @Override
         public void setTranslationZ(View view, float translationZ) {
-            ViewCompatLollipop.setTranslationZ(view, translationZ);
+            view.setTranslationZ(translationZ);
         }
 
         @Override
         public float getTranslationZ(View view) {
-            return ViewCompatLollipop.getTranslationZ(view);
+            return view.getTranslationZ();
         }
 
         @Override
         public void setOnApplyWindowInsetsListener(View view,
                 final OnApplyWindowInsetsListener listener) {
             if (listener == null) {
-                ViewCompatLollipop.setOnApplyWindowInsetsListener(view, null);
+                view.setOnApplyWindowInsetsListener(null);
                 return;
             }
 
-            ViewCompatLollipop.OnApplyWindowInsetsListenerBridge bridge =
-                    new ViewCompatLollipop.OnApplyWindowInsetsListenerBridge() {
-                        @Override
-                        public Object onApplyWindowInsets(View v, Object insets) {
-                            WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets);
-                            compatInsets = listener.onApplyWindowInsets(v, compatInsets);
-                            return WindowInsetsCompat.unwrap(compatInsets);
-                        }
-                    };
-            ViewCompatLollipop.setOnApplyWindowInsetsListener(view, bridge);
+            view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+                @Override
+                public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
+                    WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets);
+                    compatInsets = listener.onApplyWindowInsets(view, compatInsets);
+                    return (WindowInsets) WindowInsetsCompat.unwrap(compatInsets);
+                }
+            });
         }
 
         @Override
         public void setNestedScrollingEnabled(View view, boolean enabled) {
-            ViewCompatLollipop.setNestedScrollingEnabled(view, enabled);
+            view.setNestedScrollingEnabled(enabled);
         }
 
         @Override
         public boolean isNestedScrollingEnabled(View view) {
-            return ViewCompatLollipop.isNestedScrollingEnabled(view);
+            return view.isNestedScrollingEnabled();
         }
 
         @Override
         public boolean startNestedScroll(View view, int axes) {
-            return ViewCompatLollipop.startNestedScroll(view, axes);
+            return view.startNestedScroll(axes);
         }
 
         @Override
         public void stopNestedScroll(View view) {
-            ViewCompatLollipop.stopNestedScroll(view);
+            view.stopNestedScroll();
         }
 
         @Override
         public boolean hasNestedScrollingParent(View view) {
-            return ViewCompatLollipop.hasNestedScrollingParent(view);
+            return view.hasNestedScrollingParent();
         }
 
         @Override
         public boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
-            return ViewCompatLollipop.dispatchNestedScroll(view, dxConsumed, dyConsumed,
-                    dxUnconsumed, dyUnconsumed, offsetInWindow);
+            return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                    offsetInWindow);
         }
 
         @Override
         public boolean dispatchNestedPreScroll(View view, int dx, int dy,
                 int[] consumed, int[] offsetInWindow) {
-            return ViewCompatLollipop.dispatchNestedPreScroll(view, dx, dy, consumed,
-                    offsetInWindow);
+            return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
         }
 
         @Override
         public boolean dispatchNestedFling(View view, float velocityX, float velocityY,
                 boolean consumed) {
-            return ViewCompatLollipop.dispatchNestedFling(view, velocityX, velocityY, consumed);
+            return view.dispatchNestedFling(velocityX, velocityY, consumed);
         }
 
         @Override
         public boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
-            return ViewCompatLollipop.dispatchNestedPreFling(view, velocityX, velocityY);
+            return view.dispatchNestedPreFling(velocityX, velocityY);
         }
 
         @Override
         public boolean isImportantForAccessibility(View view) {
-            return ViewCompatLollipop.isImportantForAccessibility(view);
+            return view.isImportantForAccessibility();
         }
 
         @Override
         public ColorStateList getBackgroundTintList(View view) {
-            return ViewCompatLollipop.getBackgroundTintList(view);
+            return view.getBackgroundTintList();
         }
 
         @Override
         public void setBackgroundTintList(View view, ColorStateList tintList) {
-            ViewCompatLollipop.setBackgroundTintList(view, tintList);
+            view.setBackgroundTintList(tintList);
+
+            if (Build.VERSION.SDK_INT == 21) {
+                // Work around a bug in L that did not update the state of the background
+                // after applying the tint
+                Drawable background = view.getBackground();
+                boolean hasTint = (view.getBackgroundTintList() != null)
+                        && (view.getBackgroundTintMode() != null);
+                if ((background != null) && hasTint) {
+                    if (background.isStateful()) {
+                        background.setState(view.getDrawableState());
+                    }
+                    view.setBackground(background);
+                }
+            }
         }
 
         @Override
         public void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
-            ViewCompatLollipop.setBackgroundTintMode(view, mode);
+            view.setBackgroundTintMode(mode);
+
+            if (Build.VERSION.SDK_INT == 21) {
+                // Work around a bug in L that did not update the state of the background
+                // after applying the tint
+                Drawable background = view.getBackground();
+                boolean hasTint = (view.getBackgroundTintList() != null)
+                        && (view.getBackgroundTintMode() != null);
+                if ((background != null) && hasTint) {
+                    if (background.isStateful()) {
+                        background.setState(view.getDrawableState());
+                    }
+                    view.setBackground(background);
+                }
+            }
         }
 
         @Override
         public PorterDuff.Mode getBackgroundTintMode(View view) {
-            return ViewCompatLollipop.getBackgroundTintMode(view);
+            return view.getBackgroundTintMode();
         }
 
         @Override
         public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
-            return WindowInsetsCompat.wrap(
-                    ViewCompatLollipop.onApplyWindowInsets(v, WindowInsetsCompat.unwrap(insets)));
+            WindowInsets unwrapped = (WindowInsets)  WindowInsetsCompat.unwrap(insets);
+            WindowInsets result = v.onApplyWindowInsets(unwrapped);
+            if (result != unwrapped) {
+                unwrapped = new WindowInsets(result);
+            }
+            return WindowInsetsCompat.wrap(unwrapped);
         }
 
         @Override
         public WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
-            return WindowInsetsCompat.wrap(
-                    ViewCompatLollipop.dispatchApplyWindowInsets(
-                            v, WindowInsetsCompat.unwrap(insets)));
+            WindowInsets unwrapped = (WindowInsets) WindowInsetsCompat.unwrap(insets);
+            WindowInsets result = v.dispatchApplyWindowInsets(unwrapped);
+            if (result != unwrapped) {
+                unwrapped = new WindowInsets(result);
+            }
+            return WindowInsetsCompat.wrap(unwrapped);
         }
 
         @Override
         public float getZ(View view) {
-            return ViewCompatLollipop.getZ(view);
+            return view.getZ();
         }
 
         @Override
         public void setZ(View view, float z) {
-            ViewCompatLollipop.setZ(view, z);
+            view.setZ(z);
         }
 
         @Override
         public void offsetLeftAndRight(View view, int offset) {
-            ViewCompatLollipop.offsetLeftAndRight(view, offset);
+            final Rect parentRect = getEmptyTempRect();
+            boolean needInvalidateWorkaround = false;
+
+            final ViewParent parent = view.getParent();
+            if (parent instanceof View) {
+                final View p = (View) parent;
+                parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
+                // If the view currently does not currently intersect the parent (and is therefore
+                // not displayed) we may need need to invalidate
+                needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
+                        view.getRight(), view.getBottom());
+            }
+
+            // Now offset, invoking the API 11+ implementation (which contains its own workarounds)
+            super.offsetLeftAndRight(view, offset);
+
+            // The view has now been offset, so let's intersect the Rect and invalidate where
+            // the View is now displayed
+            if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
+                    view.getRight(), view.getBottom())) {
+                ((View) parent).invalidate(parentRect);
+            }
         }
 
         @Override
         public void offsetTopAndBottom(View view, int offset) {
-            ViewCompatLollipop.offsetTopAndBottom(view, offset);
+            final Rect parentRect = getEmptyTempRect();
+            boolean needInvalidateWorkaround = false;
+
+            final ViewParent parent = view.getParent();
+            if (parent instanceof View) {
+                final View p = (View) parent;
+                parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
+                // If the view currently does not currently intersect the parent (and is therefore
+                // not displayed) we may need need to invalidate
+                needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
+                        view.getRight(), view.getBottom());
+            }
+
+            // Now offset, invoking the API 11+ implementation (which contains its own workarounds)
+            super.offsetTopAndBottom(view, offset);
+
+            // The view has now been offset, so let's intersect the Rect and invalidate where
+            // the View is now displayed
+            if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
+                    view.getRight(), view.getBottom())) {
+                ((View) parent).invalidate(parentRect);
+            }
+        }
+
+        private static Rect getEmptyTempRect() {
+            if (sThreadLocalRect == null) {
+                sThreadLocalRect = new ThreadLocal<>();
+            }
+            Rect rect = sThreadLocalRect.get();
+            if (rect == null) {
+                rect = new Rect();
+                sThreadLocalRect.set(rect);
+            }
+            rect.setEmpty();
+            return rect;
         }
     }
 
-    static class MarshmallowViewCompatImpl extends LollipopViewCompatImpl {
+    @RequiresApi(23)
+    static class ViewCompatApi23Impl extends ViewCompatApi21Impl {
         @Override
         public void setScrollIndicators(View view, int indicators) {
-            ViewCompatMarshmallow.setScrollIndicators(view, indicators);
+            view.setScrollIndicators(indicators);
         }
 
         @Override
         public void setScrollIndicators(View view, int indicators, int mask) {
-            ViewCompatMarshmallow.setScrollIndicators(view, indicators, mask);
+            view.setScrollIndicators(indicators, mask);
         }
 
         @Override
         public int getScrollIndicators(View view) {
-            return ViewCompatMarshmallow.getScrollIndicators(view);
+            return view.getScrollIndicators();
         }
 
 
         @Override
         public void offsetLeftAndRight(View view, int offset) {
-            ViewCompatMarshmallow.offsetLeftAndRight(view, offset);
+            view.offsetLeftAndRight(offset);
         }
 
         @Override
         public void offsetTopAndBottom(View view, int offset) {
-            ViewCompatMarshmallow.offsetTopAndBottom(view, offset);
+            view.offsetTopAndBottom(offset);
         }
     }
 
-    static class Api24ViewCompatImpl extends MarshmallowViewCompatImpl {
+    @RequiresApi(24)
+    static class ViewCompatApi24Impl extends ViewCompatApi23Impl {
+        @Override
+        public void dispatchStartTemporaryDetach(View view) {
+            view.dispatchStartTemporaryDetach();
+        }
+
+        @Override
+        public void dispatchFinishTemporaryDetach(View view) {
+            view.dispatchFinishTemporaryDetach();
+        }
+
         @Override
         public void setPointerIcon(View view, PointerIconCompat pointerIconCompat) {
-            ViewCompatApi24.setPointerIcon(view,
-                    pointerIconCompat != null ? pointerIconCompat.getPointerIcon() : null);
+            view.setPointerIcon((PointerIcon) (pointerIconCompat != null
+                    ? pointerIconCompat.getPointerIcon() : null));
+        }
+
+        @Override
+        public boolean startDragAndDrop(View view, ClipData data,
+                View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
+            return view.startDragAndDrop(data, shadowBuilder, localState, flags);
+        }
+
+        @Override
+        public void cancelDragAndDrop(View view) {
+            view.cancelDragAndDrop();
+        }
+
+        @Override
+        public void updateDragShadow(View view, View.DragShadowBuilder shadowBuilder) {
+            view.updateDragShadow(shadowBuilder);
         }
     }
 
-    static final ViewCompatImpl IMPL;
+    @RequiresApi(26)
+    static class ViewCompatApi26Impl extends ViewCompatApi24Impl {
+        @Override
+        public void setTooltipText(View view, CharSequence tooltipText) {
+            view.setTooltipText(tooltipText);
+        }
+
+        @Override
+        public int getNextClusterForwardId(@NonNull View view) {
+            return view.getNextClusterForwardId();
+        }
+
+        @Override
+        public void setNextClusterForwardId(@NonNull View view, int nextClusterForwardId) {
+            view.setNextClusterForwardId(nextClusterForwardId);
+        }
+
+        @Override
+        public boolean isKeyboardNavigationCluster(@NonNull View view) {
+            return view.isKeyboardNavigationCluster();
+        }
+
+        @Override
+        public void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) {
+            view.setKeyboardNavigationCluster(isCluster);
+        }
+
+        @Override
+        public boolean isFocusedByDefault(@NonNull View view) {
+            return view.isFocusedByDefault();
+        }
+
+        @Override
+        public void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) {
+            view.setFocusedByDefault(isFocusedByDefault);
+        }
+
+        @Override
+        public View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster,
+                @FocusDirection int direction) {
+            return view.keyboardNavigationClusterSearch(currentCluster, direction);
+        }
+
+        @Override
+        public void addKeyboardNavigationClusters(@NonNull View view,
+                @NonNull Collection<View> views, int direction) {
+            view.addKeyboardNavigationClusters(views, direction);
+        }
+
+        @Override
+        public boolean restoreDefaultFocus(@NonNull View view) {
+            return view.restoreDefaultFocus();
+        }
+
+        @Override
+        public boolean hasExplicitFocusable(@NonNull View view) {
+            return view.hasExplicitFocusable();
+        }
+    }
+
+    static final ViewCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new Api24ViewCompatImpl();
-        } else if (version >= 23) {
-            IMPL = new MarshmallowViewCompatImpl();
-        } else if (version >= 21) {
-            IMPL = new LollipopViewCompatImpl();
-        } else if (version >= 19) {
-            IMPL = new KitKatViewCompatImpl();
-        } else if (version >= 18) {
-            IMPL = new JbMr2ViewCompatImpl();
-        } else if (version >= 17) {
-            IMPL = new JbMr1ViewCompatImpl();
-        } else if (version >= 16) {
-            IMPL = new JBViewCompatImpl();
-        } else if (version >= 15) {
-            IMPL = new ICSMr1ViewCompatImpl();
-        } else if (version >= 14) {
-            IMPL = new ICSViewCompatImpl();
-        } else if (version >= 11) {
-            IMPL = new HCViewCompatImpl();
+        if (Build.VERSION.SDK_INT >= 26) {
+            IMPL = new ViewCompatApi26Impl();
+        } else if (Build.VERSION.SDK_INT >= 24) {
+            IMPL = new ViewCompatApi24Impl();
+        } else if (Build.VERSION.SDK_INT >= 23) {
+            IMPL = new ViewCompatApi23Impl();
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ViewCompatApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new ViewCompatApi19Impl();
+        } else if (Build.VERSION.SDK_INT >= 18) {
+            IMPL = new ViewCompatApi18Impl();
+        } else if (Build.VERSION.SDK_INT >= 17) {
+            IMPL = new ViewCompatApi17Impl();
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            IMPL = new ViewCompatApi16Impl();
+        } else if (Build.VERSION.SDK_INT >= 15) {
+            IMPL = new ViewCompatApi15Impl();
         } else {
-            IMPL = new BaseViewCompatImpl();
+            IMPL = new ViewCompatBaseImpl();
         }
     }
 
     /**
      * Check if this view can be scrolled horizontally in a certain direction.
      *
-     * @param v The View against which to invoke the method.
+     * @param view The View against which to invoke the method.
      * @param direction Negative to check scrolling left, positive to check scrolling right.
      * @return true if this view can be scrolled in the specified direction, false otherwise.
+     *
+     * @deprecated Use {@link View#canScrollHorizontally(int)} directly.
      */
-    public static boolean canScrollHorizontally(View v, int direction) {
-        return IMPL.canScrollHorizontally(v, direction);
+    @Deprecated
+    public static boolean canScrollHorizontally(View view, int direction) {
+        return view.canScrollHorizontally(direction);
     }
 
     /**
      * Check if this view can be scrolled vertically in a certain direction.
      *
-     * @param v The View against which to invoke the method.
+     * @param view The View against which to invoke the method.
      * @param direction Negative to check scrolling up, positive to check scrolling down.
      * @return true if this view can be scrolled in the specified direction, false otherwise.
+     *
+     * @deprecated Use {@link View#canScrollVertically(int)} directly.
      */
-    public static boolean canScrollVertically(View v, int direction) {
-        return IMPL.canScrollVertically(v, direction);
+    @Deprecated
+    public static boolean canScrollVertically(View view, int direction) {
+        return view.canScrollVertically(direction);
     }
 
     /**
@@ -1933,9 +1702,13 @@
      *
      * @see View#sendAccessibilityEvent(int)
      * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+     *
+     * @deprecated Call {@link View#onPopulateAccessibilityEvent(AccessibilityEvent)} directly.
+     * This method will be removed in a future release.
      */
+    @Deprecated
     public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
-        IMPL.onPopulateAccessibilityEvent(v, event);
+        v.onPopulateAccessibilityEvent(event);
     }
 
     /**
@@ -1961,9 +1734,13 @@
      *
      * @see View#sendAccessibilityEvent(int)
      * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+     *
+     * @deprecated Call {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)} directly.
+     * This method will be removed in a future release.
      */
+    @Deprecated
     public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
-        IMPL.onInitializeAccessibilityEvent(v, event);
+        v.onInitializeAccessibilityEvent(event);
     }
 
     /**
@@ -2001,8 +1778,6 @@
      * (as opposed to inheritance). For more details, see
      * {@link AccessibilityDelegateCompat}.
      * <p>
-     * On platform versions prior to API 14, this method is a no-op.
-     * <p>
      * <strong>Note:</strong> On platform versions prior to
      * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
      * views in the {@code android.widget.*} package are called <i>before</i>
@@ -2250,17 +2025,20 @@
      * The opacity of the view. This is a value from 0 to 1, where 0 means the view is
      * completely transparent and 1 means the view is completely opaque.
      *
-     * <p>By default this is 1.0f. Prior to API 11, the returned value is always 1.0f.
+     * <p>By default this is 1.0f.
      * @return The opacity of the view.
+     *
+     * @deprecated Use {@link View#getAlpha()} directly.
      */
+    @Deprecated
     public static float getAlpha(View view) {
-        return IMPL.getAlpha(view);
+        return view.getAlpha();
     }
 
     /**
      * <p>Specifies the type of layer backing this view. The layer can be
-     * {@link #LAYER_TYPE_NONE disabled}, {@link #LAYER_TYPE_SOFTWARE software} or
-     * {@link #LAYER_TYPE_HARDWARE hardware}.</p>
+     * {@link View#LAYER_TYPE_NONE disabled}, {@link View#LAYER_TYPE_SOFTWARE software} or
+     * {@link View#LAYER_TYPE_HARDWARE hardware}.</p>
      *
      * <p>A layer is associated with an optional {@link android.graphics.Paint}
      * instance that controls how the layer is composed on screen. The following
@@ -2277,42 +2055,48 @@
      * equivalent to setting a hardware layer on this view and providing a paint with
      * the desired alpha value.<p>
      *
-     * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled},
-     * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware}
+     * <p>Refer to the documentation of {@link View#LAYER_TYPE_NONE disabled},
+     * {@link View#LAYER_TYPE_SOFTWARE software} and {@link View#LAYER_TYPE_HARDWARE hardware}
      * for more information on when and how to use layers.</p>
      *
      * @param view View to set the layer type for
      * @param layerType The type of layer to use with this view, must be one of
-     *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
-     *        {@link #LAYER_TYPE_HARDWARE}
+     *        {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or
+     *        {@link View#LAYER_TYPE_HARDWARE}
      * @param paint The paint used to compose the layer. This argument is optional
      *        and can be null. It is ignored when the layer type is
-     *        {@link #LAYER_TYPE_NONE}
+     *        {@link View#LAYER_TYPE_NONE}
+     *
+     * @deprecated Use {@link View#setLayerType(int, Paint)} directly.
      */
+    @Deprecated
     public static void setLayerType(View view, @LayerType int layerType, Paint paint) {
-        IMPL.setLayerType(view, layerType, paint);
+        view.setLayerType(layerType, paint);
     }
 
     /**
      * Indicates what type of layer is currently associated with this view. By default
-     * a view does not have a layer, and the layer type is {@link #LAYER_TYPE_NONE}.
+     * a view does not have a layer, and the layer type is {@link View#LAYER_TYPE_NONE}.
      * Refer to the documentation of
      * {@link #setLayerType(android.view.View, int, android.graphics.Paint)}
      * for more information on the different types of layers.
      *
      * @param view The view to fetch the layer type from
-     * @return {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
-     *         {@link #LAYER_TYPE_HARDWARE}
+     * @return {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or
+     *         {@link View#LAYER_TYPE_HARDWARE}
      *
      * @see #setLayerType(android.view.View, int, android.graphics.Paint)
-     * @see #LAYER_TYPE_NONE
-     * @see #LAYER_TYPE_SOFTWARE
-     * @see #LAYER_TYPE_HARDWARE
+     * @see View#LAYER_TYPE_NONE
+     * @see View#LAYER_TYPE_SOFTWARE
+     * @see View#LAYER_TYPE_HARDWARE
+     *
+     * @deprecated Use {@link View#getLayerType()} directly.
      */
+    @Deprecated
     @LayerType
     public static int getLayerType(View view) {
         //noinspection ResourceType
-        return IMPL.getLayerType(view);
+        return view.getLayerType();
     }
 
     /**
@@ -2339,7 +2123,7 @@
 
     /**
      * Updates the {@link Paint} object used with the current layer (used only if the current
-     * layer type is not set to {@link #LAYER_TYPE_NONE}). Changed properties of the Paint
+     * layer type is not set to {@link View#LAYER_TYPE_NONE}). Changed properties of the Paint
      * provided to {@link #setLayerType(android.view.View, int, android.graphics.Paint)}
      * will be used the next time the View is redrawn, but
      * {@link #setLayerPaint(android.view.View, android.graphics.Paint)}
@@ -2363,7 +2147,7 @@
      * @param view View to set a layer paint for
      * @param paint The paint used to compose the layer. This argument is optional
      *        and can be null. It is ignored when the layer type is
-     *        {@link #LAYER_TYPE_NONE}
+     *        {@link View#LAYER_TYPE_NONE}
      *
      * @see #setLayerType(View, int, android.graphics.Paint)
      */
@@ -2444,9 +2228,12 @@
      * @param measureSpec Constraints imposed by the parent
      * @return Size information bit mask as defined by
      * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
+     *
+     * @deprecated Use {@link View#resolveSizeAndState(int, int, int)} directly.
      */
+    @Deprecated
     public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
-        return IMPL.resolveSizeAndState(size, measureSpec, childMeasuredState);
+        return View.resolveSizeAndState(size, measureSpec, childMeasuredState);
     }
 
     /**
@@ -2458,9 +2245,12 @@
      * {@link android.view.View#getWidth()} to see how wide a view is after layout.
      *
      * @return The measured width of this view as a bit mask.
+     *
+     * @deprecated Use {@link View#getMeasuredWidth()} directly.
      */
+    @Deprecated
     public static int getMeasuredWidthAndState(View view) {
-        return IMPL.getMeasuredWidthAndState(view);
+        return view.getMeasuredWidthAndState();
     }
 
     /**
@@ -2472,9 +2262,12 @@
      * {@link android.view.View#getHeight()} to see how wide a view is after layout.
      *
      * @return The measured width of this view as a bit mask.
+     *
+     * @deprecated Use {@link View#getMeasuredHeightAndState()} directly.
      */
+    @Deprecated
     public static int getMeasuredHeightAndState(View view) {
-        return IMPL.getMeasuredHeightAndState(view);
+        return view.getMeasuredHeightAndState();
     }
 
     /**
@@ -2483,9 +2276,12 @@
      * The width component is in the regular bits {@link #MEASURED_STATE_MASK}
      * and the height component is at the shifted bits
      * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
+     *
+     * @deprecated Use {@link View#getMeasuredState()} directly.
      */
+    @Deprecated
     public static int getMeasuredState(View view) {
-        return IMPL.getMeasuredState(view);
+        return view.getMeasuredState();
     }
 
     /**
@@ -2495,9 +2291,12 @@
      * @param newState The new view state to combine.
      * @return Returns a new integer reflecting the combination of the two
      * states.
+     *
+     * @deprecated Use {@link View#combineMeasuredStates(int, int)} directly.
      */
+    @Deprecated
     public static int combineMeasuredStates(int curState, int newState) {
-        return IMPL.combineMeasuredStates(curState, newState);
+        return View.combineMeasuredStates(curState, newState);
     }
 
     /**
@@ -2606,12 +2405,13 @@
      * This position is post-layout, in addition to wherever the object's
      * layout placed it.
      *
-     * <p>Prior to API 11 this will return 0.</p>
-     *
      * @return The horizontal position of this view relative to its left position, in pixels.
+     *
+     * @deprecated Use {@link View#getTranslationX()} directly.
      */
+    @Deprecated
     public static float getTranslationX(View view) {
-        return IMPL.getTranslationX(view);
+        return view.getTranslationX();
     }
 
     /**
@@ -2619,19 +2419,19 @@
      * This position is post-layout, in addition to wherever the object's
      * layout placed it.
      *
-     * <p>Prior to API 11 this will return 0.</p>
-     *
      * @return The vertical position of this view relative to its top position, in pixels.
+     *
+     * @deprecated Use {@link View#getTranslationY()} directly.
      */
+    @Deprecated
     public static float getTranslationY(View view) {
-        return IMPL.getTranslationY(view);
+        return view.getTranslationY();
     }
 
     /**
      * The transform matrix of this view, which is calculated based on the current
      * rotation, scale, and pivot properties.
      * <p>
-     * Prior to 11, this method will return {@code null}.
      *
      * @param view The view whose Matrix will be returned
      * @return The current transform matrix for the view
@@ -2641,16 +2441,19 @@
      * @see #getScaleY(View)
      * @see #getPivotX(View)
      * @see #getPivotY(View)
+     *
+     * @deprecated Use {@link View#getMatrix()} directly.
      */
+    @Deprecated
     @Nullable
     public static Matrix getMatrix(View view) {
-        return IMPL.getMatrix(view);
+        return view.getMatrix();
     }
 
     /**
      * Returns the minimum width of the view.
      *
-     * <p>Prior to API 16 this will return 0.</p>
+     * <p>Prior to API 16, this method may return 0 on some platforms.</p>
      *
      * @return the minimum width the view will try to be.
      */
@@ -2661,7 +2464,7 @@
     /**
      * Returns the minimum height of the view.
      *
-     * <p>Prior to API 16 this will return 0.</p>
+     * <p>Prior to API 16, this method may return 0 on some platforms.</p>
      *
      * @return the minimum height the view will try to be.
      */
@@ -2673,8 +2476,6 @@
      * This method returns a ViewPropertyAnimator object, which can be used to animate
      * specific properties on this View.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
      */
     public static ViewPropertyAnimatorCompat animate(View view) {
@@ -2686,13 +2487,14 @@
      * This effectively positions the object post-layout, in addition to wherever the object's
      * layout placed it.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The horizontal position of this view relative to its left position,
      * in pixels.
+     *
+     * @deprecated Use {@link View#setTranslationX(float)} directly.
      */
+    @Deprecated
     public static void setTranslationX(View view, float value) {
-        IMPL.setTranslationX(view, value);
+        view.setTranslationX(value);
     }
 
     /**
@@ -2700,15 +2502,16 @@
      * This effectively positions the object post-layout, in addition to wherever the object's
      * layout placed it.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The vertical position of this view relative to its top position,
      * in pixels.
      *
      * @attr name android:translationY
+     *
+     * @deprecated Use {@link View#setTranslationY(float)} directly.
      */
+    @Deprecated
     public static void setTranslationY(View view, float value) {
-        IMPL.setTranslationY(view, value);
+        view.setTranslationY(value);
     }
 
     /**
@@ -2719,12 +2522,13 @@
      * performance implications, especially for large views. It is best to use the alpha property
      * sparingly and transiently, as in the case of fading animations.</p>
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The opacity of the view.
+     *
+     * @deprecated Use {@link View#setAlpha(float)} directly.
      */
+    @Deprecated
     public static void setAlpha(View view, @FloatRange(from=0.0, to=1.0) float value) {
-        IMPL.setAlpha(view, value);
+        view.setAlpha(value);
     }
 
     /**
@@ -2733,12 +2537,13 @@
      * the x value passed in and the current left property of the view as determined
      * by the layout bounds.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The visual x position of this view, in pixels.
+     *
+     * @deprecated Use {@link View#setX(float)} directly.
      */
+    @Deprecated
     public static void setX(View view, float value) {
-        IMPL.setX(view, value);
+        view.setX(value);
     }
 
     /**
@@ -2747,24 +2552,26 @@
      * the y value passed in and the current top property of the view as determined by the
      * layout bounds.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The visual y position of this view, in pixels.
+     *
+     * @deprecated Use {@link View#setY(float)} directly.
      */
+    @Deprecated
     public static void setY(View view, float value) {
-        IMPL.setY(view, value);
+        view.setY(value);
     }
 
     /**
      * Sets the degrees that the view is rotated around the pivot point. Increasing values
      * result in clockwise rotation.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The degrees of rotation.
+     *
+     * @deprecated Use {@link View#setRotation(float)} directly.
      */
+    @Deprecated
     public static void setRotation(View view, float value) {
-        IMPL.setRotation(view, value);
+        view.setRotation(value);
     }
 
     /**
@@ -2772,12 +2579,13 @@
      * Increasing values result in clockwise rotation from the viewpoint of looking down the
      * x axis.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The degrees of X rotation.
+     *
+     * @deprecated Use {@link View#setRotationX(float)} directly.
      */
+    @Deprecated
     public static void setRotationX(View view, float value) {
-        IMPL.setRotationX(view, value);
+        view.setRotationX(value);
     }
 
     /**
@@ -2785,47 +2593,50 @@
      * Increasing values result in counter-clockwise rotation from the viewpoint of looking
      * down the y axis.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The degrees of Y rotation.
+     *
+     * @deprecated Use {@link View#setRotationY(float)} directly.
      */
+    @Deprecated
     public static void setRotationY(View view, float value) {
-        IMPL.setRotationY(view, value);
+        view.setRotationY(value);
     }
 
     /**
      * Sets the amount that the view is scaled in x around the pivot point, as a proportion of
      * the view's unscaled width. A value of 1 means that no scaling is applied.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The scaling factor.
+     *
+     * @deprecated Use {@link View#setScaleX(float)} directly.
      */
+    @Deprecated
     public static void setScaleX(View view, float value) {
-        IMPL.setScaleX(view, value);
+        view.setScaleX(value);
     }
 
     /**
      * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of
      * the view's unscaled width. A value of 1 means that no scaling is applied.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The scaling factor.
+     *
+     * @deprecated Use {@link View#setScaleY(float)} directly.
      */
+    @Deprecated
     public static void setScaleY(View view, float value) {
-        IMPL.setScaleY(view, value);
+        view.setScaleY(value);
     }
 
     /**
      * The x location of the point around which the view is
      * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
+     * @deprecated Use {@link View#getPivotX()} directly.
      */
+    @Deprecated
     public static float getPivotX(View view) {
-        return IMPL.getPivotX(view);
+        return view.getPivotX();
     }
 
     /**
@@ -2835,24 +2646,26 @@
      * Setting this property disables this behavior and causes the view to use only the
      * explicitly set pivotX and pivotY values.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The x location of the pivot point.
+     *
+     * @deprecated Use {@link View#setPivotX(float)} directly.
      */
+    @Deprecated
     public static void setPivotX(View view, float value) {
-        IMPL.setPivotX(view, value);
+        view.setPivotX(value);
     }
 
     /**
      * The y location of the point around which the view is {@link #setRotation(View,
      * float) rotated} and {@link #setScaleY(View, float) scaled}.
      *
-     * <p>Prior to API 11 this will return 0.</p>
-     *
      * @return The y location of the pivot point.
+     *
+     * @deprecated Use {@link View#getPivotY()} directly.
      */
+    @Deprecated
     public static float getPivotY(View view) {
-        return IMPL.getPivotY(view);
+        return view.getPivotY();
     }
 
     /**
@@ -2862,40 +2675,69 @@
      * Setting this property disables this behavior and causes the view to use only the
      * explicitly set pivotX and pivotY values.
      *
-     * <p>Prior to API 11 this will have no effect.</p>
-     *
      * @param value The y location of the pivot point.
+     *
+     * @deprecated Use {@link View#setPivotX(float)} directly.
      */
+    @Deprecated
     public static void setPivotY(View view, float value) {
-        IMPL.setPivotY(view, value);
+        view.setPivotY(value);
     }
 
+    /**
+     * @deprecated Use {@link View#getRotation()} directly.
+     */
+    @Deprecated
     public static float getRotation(View view) {
-        return IMPL.getRotation(view);
+        return view.getRotation();
     }
 
+    /**
+     * @deprecated Use {@link View#getRotationX()} directly.
+     */
+    @Deprecated
     public static float getRotationX(View view) {
-        return IMPL.getRotationX(view);
+        return view.getRotationX();
     }
 
+    /**
+     * @deprecated Use {@link View#getRotationY()} directly.
+     */
+    @Deprecated
     public static float getRotationY(View view) {
-        return IMPL.getRotationY(view);
+        return view.getRotationY();
     }
 
+    /**
+     * @deprecated Use {@link View#getScaleX()} directly.
+     */
+    @Deprecated
     public static float getScaleX(View view) {
-        return IMPL.getScaleX(view);
+        return view.getScaleX();
     }
 
+    /**
+     * @deprecated Use {@link View#getScaleY()} directly.
+     */
+    @Deprecated
     public static float getScaleY(View view) {
-        return IMPL.getScaleY(view);
+        return view.getScaleY();
     }
 
+    /**
+     * @deprecated Use {@link View#getX()} directly.
+     */
+    @Deprecated
     public static float getX(View view) {
-        return IMPL.getX(view);
+        return view.getX();
     }
 
+    /**
+     * @deprecated Use {@link View#getY()} directly.
+     */
+    @Deprecated
     public static float getY(View view) {
-        return IMPL.getY(view);
+        return view.getY();
     }
 
     /**
@@ -2996,9 +2838,12 @@
      * such as the status bar and inset its content; that is, controlling whether
      * the default implementation of {@link View#fitSystemWindows(Rect)} will be
      * executed. See that method for more details.
+     *
+     * @deprecated Use {@link View#setFitsSystemWindows(boolean)} directly.
      */
+    @Deprecated
     public static void setFitsSystemWindows(View view, boolean fitSystemWindows) {
-        IMPL.setFitsSystemWindows(view, fitSystemWindows);
+        view.setFitsSystemWindows(fitSystemWindows);
     }
 
     /**
@@ -3007,9 +2852,12 @@
      * <p>
      * On API 21 and above, also calls <code>StateListAnimator#jumpToCurrentState()</code>
      * if there is a StateListAnimator attached to this view.
+     *
+     * @deprecated Use {@link View#jumpDrawablesToCurrentState()} directly.
      */
+    @Deprecated
     public static void jumpDrawablesToCurrentState(View v) {
-        IMPL.jumpDrawablesToCurrentState(v);
+        v.jumpDrawablesToCurrentState();
     }
 
     /**
@@ -3060,9 +2908,12 @@
      *
      * @param enabled Set to false to <em>disable</em> state saving, or true
      * (the default) to allow it.
+     *
+     * @deprecated Use {@link View#setSaveFromParentEnabled(boolean)} directly.
      */
+    @Deprecated
     public static void setSaveFromParentEnabled(View v, boolean enabled) {
-        IMPL.setSaveFromParentEnabled(v, enabled);
+        v.setSaveFromParentEnabled(enabled);
     }
 
     /**
@@ -3073,9 +2924,12 @@
      * user can move views in and out of.
      *
      * @param activated true if the view must be activated, false otherwise
+     *
+     * @deprecated Use {@link View#setActivated(boolean)} directly.
      */
+    @Deprecated
     public static void setActivated(View view, boolean activated) {
-        IMPL.setActivated(view, activated);
+        view.setActivated(activated);
     }
 
     /**
@@ -3176,7 +3030,7 @@
      *
      * @see #isNestedScrollingEnabled(View)
      */
-    public static void setNestedScrollingEnabled(View view, boolean enabled) {
+    public static void setNestedScrollingEnabled(@NonNull View view, boolean enabled) {
         IMPL.setNestedScrollingEnabled(view, enabled);
     }
 
@@ -3192,13 +3046,97 @@
      *
      * @see #setNestedScrollingEnabled(View, boolean)
      */
-    public static boolean isNestedScrollingEnabled(View view) {
+    public static boolean isNestedScrollingEnabled(@NonNull View view) {
         return IMPL.isNestedScrollingEnabled(view);
     }
 
     /**
      * Begin a nestable scroll operation along the given axes.
      *
+     * <p>This version of the method just calls {@link #startNestedScroll(View, int, int)} using
+     * the touch input type.</p>
+     *
+     * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
+     *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
+     * @return true if a cooperative parent was found and nested scrolling has been enabled for
+     *         the current gesture.
+     */
+    public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes) {
+        return IMPL.startNestedScroll(view, axes);
+    }
+
+    /**
+     * Stop a nested scroll in progress.
+     *
+     * <p>This version of the method just calls {@link #stopNestedScroll(View, int)} using the
+     * touch input type.</p>
+     *
+     * @see #startNestedScroll(View, int)
+     */
+    public static void stopNestedScroll(@NonNull View view) {
+        IMPL.stopNestedScroll(view);
+    }
+
+    /**
+     * Returns true if this view has a nested scrolling parent.
+     *
+     * <p>This version of the method just calls {@link #hasNestedScrollingParent(View, int)}
+     * using the touch input type.</p>
+     *
+     * @return whether this view has a nested scrolling parent
+     */
+    public static boolean hasNestedScrollingParent(@NonNull View view) {
+        return IMPL.hasNestedScrollingParent(view);
+    }
+
+    /**
+     * Dispatch one step of a nested scroll in progress.
+     *
+     * <p>This version of the method just calls
+     * {@link #dispatchNestedScroll(View, int, int, int, int, int[], int)} using the touch input
+     * type.</p>
+     *
+     * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
+     * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the event was dispatched, false if it could not be dispatched.
+     */
+    public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {
+        return IMPL.dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                offsetInWindow);
+    }
+
+    /**
+     * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
+     *
+     * <p>This version of the method just calls
+     * {@link #dispatchNestedPreScroll(View, int, int, int[], int[], int)} using the touch input
+     * type.</p>
+     *
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
+     *                 and consumed[1] the consumed dy.
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the parent consumed some or all of the scroll delta
+     */
+    public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy,
+            @Nullable int[] consumed, @Nullable int[] offsetInWindow) {
+        return IMPL.dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow);
+    }
+
+    /**
+     * Begin a nestable scroll operation along the given axes.
+     *
      * <p>A view starting a nested scroll promises to abide by the following contract:</p>
      *
      * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
@@ -3227,6 +3165,7 @@
      *
      * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
      *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
+     * @param type the type of input which cause this scroll event
      * @return true if a cooperative parent was found and nested scrolling has been enabled for
      *         the current gesture.
      *
@@ -3234,8 +3173,14 @@
      * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
      * @see #dispatchNestedScroll(View, int, int, int, int, int[])
      */
-    public static boolean startNestedScroll(View view, int axes) {
-        return IMPL.startNestedScroll(view, axes);
+    public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes,
+            @NestedScrollType int type) {
+        if (view instanceof NestedScrollingChild2) {
+            return ((NestedScrollingChild2) view).startNestedScroll(axes, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            return IMPL.startNestedScroll(view, axes);
+        }
+        return false;
     }
 
     /**
@@ -3243,10 +3188,15 @@
      *
      * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
      *
+     * @param type the type of input which cause this scroll event
      * @see #startNestedScroll(View, int)
      */
-    public static void stopNestedScroll(View view) {
-        IMPL.stopNestedScroll(view);
+    public static void stopNestedScroll(@NonNull View view, @NestedScrollType int type) {
+        if (view instanceof NestedScrollingChild2) {
+            ((NestedScrollingChild2) view).stopNestedScroll(type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            IMPL.stopNestedScroll(view);
+        }
     }
 
     /**
@@ -3255,10 +3205,16 @@
      * <p>The presence of a nested scrolling parent indicates that this view has initiated
      * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
      *
+     * @param type the type of input which cause this scroll event
      * @return whether this view has a nested scrolling parent
      */
-    public static boolean hasNestedScrollingParent(View view) {
-        return IMPL.hasNestedScrollingParent(view);
+    public static boolean hasNestedScrollingParent(@NonNull View view, @NestedScrollType int type) {
+        if (view instanceof NestedScrollingChild2) {
+            ((NestedScrollingChild2) view).hasNestedScrollingParent(type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            return IMPL.hasNestedScrollingParent(view);
+        }
+        return false;
     }
 
     /**
@@ -3281,13 +3237,21 @@
      *                       in local view coordinates of this view from before this operation
      *                       to after it completes. View implementations may use this to adjust
      *                       expected input coordinate tracking.
+     * @param type the type of input which cause this scroll event
      * @return true if the event was dispatched, false if it could not be dispatched.
      * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
      */
-    public static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
-            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
-        return IMPL.dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
-                offsetInWindow);
+    public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,
+            @NestedScrollType int type) {
+        if (view instanceof NestedScrollingChild2) {
+            return ((NestedScrollingChild2) view).dispatchNestedScroll(dxConsumed, dyConsumed,
+                    dxUnconsumed, dyUnconsumed, offsetInWindow, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            return IMPL.dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed,
+                    dyUnconsumed, offsetInWindow);
+        }
+        return false;
     }
 
     /**
@@ -3306,12 +3270,19 @@
      *                       in local view coordinates of this view from before this operation
      *                       to after it completes. View implementations may use this to adjust
      *                       expected input coordinate tracking.
+     * @param type the type of input which cause this scroll event
      * @return true if the parent consumed some or all of the scroll delta
      * @see #dispatchNestedScroll(View, int, int, int, int, int[])
      */
-    public static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
-            int[] offsetInWindow) {
-        return IMPL.dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow);
+    public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy,
+            @Nullable int[] consumed, @Nullable int[] offsetInWindow, @NestedScrollType int type) {
+        if (view instanceof NestedScrollingChild2) {
+            return ((NestedScrollingChild2) view).dispatchNestedPreScroll(dx, dy, consumed,
+                    offsetInWindow, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            return IMPL.dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow);
+        }
+        return false;
     }
 
     /**
@@ -3332,7 +3303,7 @@
      * @param consumed true if the child consumed the fling, false otherwise
      * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
      */
-    public static boolean dispatchNestedFling(View view, float velocityX, float velocityY,
+    public static boolean dispatchNestedFling(@NonNull View view, float velocityX, float velocityY,
             boolean consumed) {
         return IMPL.dispatchNestedFling(view, velocityX, velocityY, consumed);
     }
@@ -3367,7 +3338,8 @@
      * @param velocityY Vertical fling velocity in pixels per second
      * @return true if a nested scrolling parent consumed the fling
      */
-    public static boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
+    public static boolean dispatchNestedPreFling(@NonNull View view, float velocityX,
+            float velocityY) {
         return IMPL.dispatchNestedPreFling(view, velocityX, velocityY);
     }
 
@@ -3582,5 +3554,170 @@
         return IMPL.getDisplay(view);
     }
 
+    /**
+     * Sets the tooltip for the view.
+     *
+     * <p>Prior to API 26 this does nothing. Use TooltipCompat class from v7 appcompat library
+     * for a compatible tooltip implementation.</p>
+     *
+     * @param tooltipText the tooltip text
+     */
+    public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) {
+        IMPL.setTooltipText(view, tooltipText);
+    }
+
+    /**
+     * Start the drag and drop operation.
+     */
+    public static boolean startDragAndDrop(View v, ClipData data,
+            View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
+        return IMPL.startDragAndDrop(v, data, shadowBuilder, localState, flags);
+    }
+
+    /**
+     * Cancel the drag and drop operation.
+     */
+    public static void cancelDragAndDrop(View v) {
+        IMPL.cancelDragAndDrop(v);
+    }
+
+    /**
+     * Update the drag shadow while drag and drop is in progress.
+     */
+    public static void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder) {
+        IMPL.updateDragShadow(v, shadowBuilder);
+    }
+
+    /**
+     * Gets the ID of the next keyboard navigation cluster root.
+     *
+     * @return the next keyboard navigation cluster ID, or {@link View#NO_ID} if the framework
+     *         should decide automatically or API < 26.
+     */
+    public static int getNextClusterForwardId(@NonNull View view) {
+        return IMPL.getNextClusterForwardId(view);
+    }
+
+    /**
+     * Sets the ID of the next keyboard navigation cluster root view. Does nothing if {@code view}
+     * is not a keyboard navigation cluster or if API < 26.
+     *
+     * @param nextClusterForwardId next cluster ID, or {@link View#NO_ID} if the framework
+     *                             should decide automatically.
+     */
+    public static void setNextClusterForwardId(@NonNull View view, int nextClusterForwardId) {
+        IMPL.setNextClusterForwardId(view, nextClusterForwardId);
+    }
+
+    /**
+     * Returns whether {@code view} is a root of a keyboard navigation cluster. Always returns
+     * {@code false} on API < 26.
+     *
+     * @return {@code true} if this view is a root of a cluster, or {@code false} otherwise.
+     */
+    public static boolean isKeyboardNavigationCluster(@NonNull View view) {
+        return IMPL.isKeyboardNavigationCluster(view);
+    }
+
+    /**
+     * Set whether {@code view} is a root of a keyboard navigation cluster. Does nothing if
+     * API < 26.
+     *
+     * @param isCluster {@code true} to mark {@code view} as the root of a cluster, {@code false}
+     *                  to unmark.
+     */
+    public static void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) {
+        IMPL.setKeyboardNavigationCluster(view, isCluster);
+    }
+
+    /**
+     * Returns whether {@code view} should receive focus when the focus is restored for the view
+     * hierarchy containing it. Returns {@code false} on API < 26.
+     * <p>
+     * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
+     * window or serves as a target of cluster navigation.
+     *
+     * @return {@code true} if {@code view} is the default-focus view, {@code false} otherwise.
+     */
+    public static boolean isFocusedByDefault(@NonNull View view) {
+        return IMPL.isFocusedByDefault(view);
+    }
+
+    /**
+     * Sets whether {@code view} should receive focus when the focus is restored for the view
+     * hierarchy containing it.
+     * <p>
+     * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
+     * window or serves as a target of cluster navigation.
+     * <p>
+     * Does nothing on API < 26.
+     *
+     * @param isFocusedByDefault {@code true} to set {@code view} as the default-focus view,
+     *                           {@code false} otherwise.
+     */
+    public static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) {
+        IMPL.setFocusedByDefault(view, isFocusedByDefault);
+    }
+
+    /**
+     * Find the nearest keyboard navigation cluster in the specified direction.
+     * This does not actually give focus to that cluster.
+     *
+     * @param currentCluster The starting point of the search. {@code null} means the current
+     *                       cluster is not found yet.
+     * @param direction Direction to look.
+     *
+     * @return the nearest keyboard navigation cluster in the specified direction, or {@code null}
+     *         if one can't be found or if API < 26.
+     */
+    public static View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster,
+            @FocusDirection int direction) {
+        return IMPL.keyboardNavigationClusterSearch(view, currentCluster, direction);
+    }
+
+    /**
+     * Adds any keyboard navigation cluster roots that are descendants of {@code view} (
+     * including {@code view} if it is a cluster root itself) to {@code views}. Does nothing
+     * on API < 26.
+     *
+     * @param views collection of keyboard navigation cluster roots found so far.
+     * @param direction direction to look.
+     */
+    public static void addKeyboardNavigationClusters(@NonNull View view,
+            @NonNull Collection<View> views, int direction) {
+        IMPL.addKeyboardNavigationClusters(view, views, direction);
+    }
+
+    /**
+     * Gives focus to the default-focus view in the view hierarchy rooted at {@code view}.
+     * If the default-focus view cannot be found or if API < 26, this falls back to calling
+     * {@link View#requestFocus(int)}.
+     *
+     * @return {@code true} if {@code view} or one of its descendants took focus, {@code false}
+     *         otherwise.
+     */
+    public static boolean restoreDefaultFocus(@NonNull View view) {
+        return IMPL.restoreDefaultFocus(view);
+    }
+
+    /**
+     * Returns true if this view is focusable or if it contains a reachable View
+     * for which {@link View#hasExplicitFocusable()} returns {@code true}.
+     * A "reachable hasExplicitFocusable()" is a view whose parents do not block descendants focus.
+     * Only {@link View#VISIBLE} views for which {@link View#getFocusable()} would return
+     * {@link View#FOCUSABLE} are considered focusable.
+     *
+     * <p>This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of
+     * {@link View#hasFocusable()} in that only views explicitly set focusable will cause
+     * this method to return true. A view set to {@link View#FOCUSABLE_AUTO} that resolves
+     * to focusable will not.</p>
+     *
+     * @return {@code true} if the view is focusable or if the view contains a focusable
+     *         view, {@code false} otherwise
+     */
+    public static boolean hasExplicitFocusable(@NonNull View view) {
+        return IMPL.hasExplicitFocusable(view);
+    }
+
     protected ViewCompat() {}
 }
diff --git a/compat/java/android/support/v4/view/ViewConfigurationCompat.java b/compat/java/android/support/v4/view/ViewConfigurationCompat.java
index a30c04b..f14b806 100644
--- a/compat/java/android/support/v4/view/ViewConfigurationCompat.java
+++ b/compat/java/android/support/v4/view/ViewConfigurationCompat.java
@@ -16,68 +16,37 @@
 
 package android.support.v4.view;
 
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.util.Log;
+import android.util.TypedValue;
 import android.view.ViewConfiguration;
 
+import java.lang.reflect.Method;
+
 /**
- * Helper for accessing features in {@link ViewConfiguration}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link ViewConfiguration}.
+ *
+ * @deprecated Use {@link ViewConfiguration} directly.
  */
+@Deprecated
 public final class ViewConfigurationCompat {
-    /**
-     * Interface for the full API.
-     */
-    interface ViewConfigurationVersionImpl {
-        boolean hasPermanentMenuKey(ViewConfiguration config);
-    }
+    private static final String TAG = "ViewConfigCompat";
 
-    /**
-     * Interface implementation that doesn't use anything about v4 APIs.
-     */
-    static class BaseViewConfigurationVersionImpl implements ViewConfigurationVersionImpl {
-        @Override
-        public boolean hasPermanentMenuKey(ViewConfiguration config) {
-            // Pre-HC devices will always have a menu button
-            return true;
-        }
-    }
+    private static Method sGetScaledScrollFactorMethod;
 
-    /**
-     * Interface implementation for devices with at least v11 APIs.
-     */
-    static class HoneycombViewConfigurationVersionImpl extends BaseViewConfigurationVersionImpl {
-        @Override
-        public boolean hasPermanentMenuKey(ViewConfiguration config) {
-            // There is no way to check on Honeycomb so we assume false
-            return false;
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least v14 APIs.
-     */
-    static class IcsViewConfigurationVersionImpl extends HoneycombViewConfigurationVersionImpl {
-        @Override
-        public boolean hasPermanentMenuKey(ViewConfiguration config) {
-            return ViewConfigurationCompatICS.hasPermanentMenuKey(config);
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final ViewConfigurationVersionImpl IMPL;
     static {
-        if (android.os.Build.VERSION.SDK_INT >= 14) {
-            IMPL = new IcsViewConfigurationVersionImpl();
-        } else if (android.os.Build.VERSION.SDK_INT >= 11) {
-            IMPL = new HoneycombViewConfigurationVersionImpl();
-        } else {
-            IMPL = new BaseViewConfigurationVersionImpl();
+        if (Build.VERSION.SDK_INT == 25) {
+            try {
+                sGetScaledScrollFactorMethod =
+                        ViewConfiguration.class.getDeclaredMethod("getScaledScrollFactor");
+            } catch (Exception e) {
+                Log.i(TAG, "Could not find method getScaledScrollFactor() on ViewConfiguration");
+            }
         }
     }
 
-    // -------------------------------------------------------------------
-
     /**
      * Call {@link ViewConfiguration#getScaledPagingTouchSlop()}.
      *
@@ -92,9 +61,63 @@
     /**
      * Report if the device has a permanent menu key available to the user, in a backwards
      * compatible way.
+     *
+     * @deprecated Use {@link ViewConfiguration#hasPermanentMenuKey()} directly.
      */
+    @Deprecated
     public static boolean hasPermanentMenuKey(ViewConfiguration config) {
-        return IMPL.hasPermanentMenuKey(config);
+        return config.hasPermanentMenuKey();
+    }
+
+    /**
+     * @param config Used to get the scaling factor directly from the {@link ViewConfiguration}.
+     * @param context Used to locate a resource value.
+     *
+     * @return Amount to scroll in response to a horizontal {@link MotionEventCompat#ACTION_SCROLL}
+     *         event. Multiply this by the event's axis value to obtain the number of pixels to be
+     *         scrolled.
+     */
+    public static float getScaledHorizontalScrollFactor(@NonNull ViewConfiguration config,
+            @NonNull Context context) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return config.getScaledHorizontalScrollFactor();
+        } else {
+            return getLegacyScrollFactor(config, context);
+        }
+    }
+
+    /**
+     * @param config Used to get the scaling factor directly from the {@link ViewConfiguration}.
+     * @param context Used to locate a resource value.
+     *
+     * @return Amount to scroll in response to a vertical {@link MotionEventCompat#ACTION_SCROLL}
+     *         event. Multiply this by the event's axis value to obtain the number of pixels to be
+     *         scrolled.
+     */
+    public static float getScaledVerticalScrollFactor(@NonNull ViewConfiguration config,
+            @NonNull Context context) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return config.getScaledVerticalScrollFactor();
+        } else {
+            return getLegacyScrollFactor(config, context);
+        }
+    }
+
+    private static float getLegacyScrollFactor(ViewConfiguration config, Context context) {
+        if (android.os.Build.VERSION.SDK_INT >= 25 && sGetScaledScrollFactorMethod != null) {
+            try {
+                return (int) sGetScaledScrollFactorMethod.invoke(config);
+            } catch (Exception e) {
+                Log.i(TAG, "Could not find method getScaledScrollFactor() on ViewConfiguration");
+            }
+        }
+        // Fall back to pre-API-25 behavior.
+        TypedValue outValue = new TypedValue();
+        if (context.getTheme().resolveAttribute(
+                android.R.attr.listPreferredItemHeight, outValue, true)) {
+            return outValue.getDimension(context.getResources().getDisplayMetrics());
+        }
+        return 0;
     }
 
     private ViewConfigurationCompat() {}
diff --git a/compat/java/android/support/v4/view/ViewGroupCompat.java b/compat/java/android/support/v4/view/ViewGroupCompat.java
index 0fa76cd..528d70e 100644
--- a/compat/java/android/support/v4/view/ViewGroupCompat.java
+++ b/compat/java/android/support/v4/view/ViewGroupCompat.java
@@ -17,13 +17,15 @@
 package android.support.v4.view;
 
 import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.v4.view.ViewCompat.ScrollAxis;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
 /**
- * Helper for accessing features in {@link ViewGroup}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link ViewGroup}.
  */
 public final class ViewGroupCompat {
 
@@ -43,49 +45,22 @@
      */
     public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
 
-    interface ViewGroupCompatImpl {
-        boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
-                AccessibilityEvent event);
-        void setMotionEventSplittingEnabled(ViewGroup group, boolean split);
-        int getLayoutMode(ViewGroup group);
-        void setLayoutMode(ViewGroup group, int mode);
-        void setTransitionGroup(ViewGroup group, boolean isTransitionGroup);
-        boolean isTransitionGroup(ViewGroup group);
-        int getNestedScrollAxes(ViewGroup group);
-    }
-
-    static class ViewGroupCompatStubImpl implements ViewGroupCompatImpl {
-        @Override
-        public boolean onRequestSendAccessibilityEvent(
-                ViewGroup group, View child, AccessibilityEvent event) {
-            return true;
-        }
-
-        @Override
-        public void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-            // no-op, didn't exist.
-        }
-
-        @Override
+    static class ViewGroupCompatBaseImpl {
         public int getLayoutMode(ViewGroup group) {
             return LAYOUT_MODE_CLIP_BOUNDS;
         }
 
-        @Override
         public void setLayoutMode(ViewGroup group, int mode) {
             // no-op, didn't exist. Views only support clip bounds.
         }
 
-        @Override
         public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
         }
 
-        @Override
         public boolean isTransitionGroup(ViewGroup group) {
             return false;
         }
 
-        @Override
         public int getNestedScrollAxes(ViewGroup group) {
             if (group instanceof NestedScrollingParent) {
                 return ((NestedScrollingParent) group).getNestedScrollAxes();
@@ -94,63 +69,45 @@
         }
     }
 
-    static class ViewGroupCompatHCImpl extends ViewGroupCompatStubImpl {
-        @Override
-        public void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-            ViewGroupCompatHC.setMotionEventSplittingEnabled(group, split);
-        }
-    }
-
-    static class ViewGroupCompatIcsImpl extends ViewGroupCompatHCImpl {
-        @Override
-        public boolean onRequestSendAccessibilityEvent(
-                ViewGroup group, View child, AccessibilityEvent event) {
-            return ViewGroupCompatIcs.onRequestSendAccessibilityEvent(group, child, event);
-        }
-    }
-
-    static class ViewGroupCompatJellybeanMR2Impl extends ViewGroupCompatIcsImpl {
+    @RequiresApi(18)
+    static class ViewGroupCompatApi18Impl extends ViewGroupCompatBaseImpl {
         @Override
         public int getLayoutMode(ViewGroup group) {
-            return ViewGroupCompatJellybeanMR2.getLayoutMode(group);
+            return group.getLayoutMode();
         }
 
         @Override
         public void setLayoutMode(ViewGroup group, int mode) {
-            ViewGroupCompatJellybeanMR2.setLayoutMode(group, mode);
+            group.setLayoutMode(mode);
         }
     }
 
-    static class ViewGroupCompatLollipopImpl extends ViewGroupCompatJellybeanMR2Impl {
+    @RequiresApi(21)
+    static class ViewGroupCompatApi21Impl extends ViewGroupCompatApi18Impl {
         @Override
         public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-            ViewGroupCompatLollipop.setTransitionGroup(group, isTransitionGroup);
+            group.setTransitionGroup(isTransitionGroup);
         }
 
         @Override
         public boolean isTransitionGroup(ViewGroup group) {
-            return ViewGroupCompatLollipop.isTransitionGroup(group);
+            return group.isTransitionGroup();
         }
 
         @Override
         public int getNestedScrollAxes(ViewGroup group) {
-            return ViewGroupCompatLollipop.getNestedScrollAxes(group);
+            return group.getNestedScrollAxes();
         }
     }
 
-    static final ViewGroupCompatImpl IMPL;
+    static final ViewGroupCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new ViewGroupCompatLollipopImpl();
-        } else if (version >= 18) {
-            IMPL = new ViewGroupCompatJellybeanMR2Impl();
-        } else if (version >= 14) {
-            IMPL = new ViewGroupCompatIcsImpl();
-        } else if (version >= 11) {
-            IMPL = new ViewGroupCompatHCImpl();
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ViewGroupCompatApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 18) {
+            IMPL = new ViewGroupCompatApi18Impl();
         } else {
-            IMPL = new ViewGroupCompatStubImpl();
+            IMPL = new ViewGroupCompatBaseImpl();
         }
     }
 
@@ -173,10 +130,14 @@
      * @param child The child which requests sending the event.
      * @param event The event to be sent.
      * @return True if the event should be sent.
+     *
+     * @deprecated Use {@link ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent)}
+     * directly.
      */
+    @Deprecated
     public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
             AccessibilityEvent event) {
-        return IMPL.onRequestSendAccessibilityEvent(group, child, event);
+        return group.onRequestSendAccessibilityEvent(child, event);
     }
 
     /**
@@ -194,9 +155,12 @@
      * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
      *              child views. <code>false</code> to only allow one child view to be the target of
      *              any MotionEvent received by this ViewGroup.
+     *
+     * @deprecated Use {@link ViewGroup#setMotionEventSplittingEnabled(boolean)} directly.
      */
+    @Deprecated
     public static void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-        IMPL.setMotionEventSplittingEnabled(group, split);
+        group.setMotionEventSplittingEnabled(split);
     }
 
     /**
@@ -261,7 +225,8 @@
      * @see ViewCompat#SCROLL_AXIS_VERTICAL
      * @see ViewCompat#SCROLL_AXIS_NONE
      */
-    public static int getNestedScrollAxes(ViewGroup group) {
+    @ScrollAxis
+    public static int getNestedScrollAxes(@NonNull ViewGroup group) {
         return IMPL.getNestedScrollAxes(group);
     }
 }
diff --git a/compat/java/android/support/v4/view/ViewParentCompat.java b/compat/java/android/support/v4/view/ViewParentCompat.java
index ec97988..bb1587e 100644
--- a/compat/java/android/support/v4/view/ViewParentCompat.java
+++ b/compat/java/android/support/v4/view/ViewParentCompat.java
@@ -16,55 +16,24 @@
 
 package android.support.v4.view;
 
-import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 
 /**
- * Helper for accessing features in {@link ViewParent}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link ViewParent}.
  */
 public final class ViewParentCompat {
 
-    interface ViewParentCompatImpl {
-        public boolean requestSendAccessibilityEvent(
-                ViewParent parent, View child, AccessibilityEvent event);
-        boolean onStartNestedScroll(ViewParent parent, View child, View target,
-                int nestedScrollAxes);
-        void onNestedScrollAccepted(ViewParent parent, View child, View target,
-                int nestedScrollAxes);
-        void onStopNestedScroll(ViewParent parent, View target);
-        void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
-                int dxUnconsumed, int dyUnconsumed);
-        void onNestedPreScroll(ViewParent parent, View target, int dx, int dy, int[] consumed);
-        boolean onNestedFling(ViewParent parent, View target, float velocityX, float velocityY,
-                boolean consumed);
-        boolean onNestedPreFling(ViewParent parent, View target, float velocityX, float velocityY);
-        void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-                View source, int changeType);
-    }
+    private static final String TAG = "ViewParentCompat";
 
-    static class ViewParentCompatStubImpl implements ViewParentCompatImpl {
-        @Override
-        public boolean requestSendAccessibilityEvent(
-                ViewParent parent, View child, AccessibilityEvent event) {
-            // Emulate what ViewRootImpl does in ICS and above.
-            if (child == null) {
-                return false;
-            }
-            final AccessibilityManager manager = (AccessibilityManager) child.getContext()
-                    .getSystemService(Context.ACCESSIBILITY_SERVICE);
-            manager.sendAccessibilityEvent(event);
-            return true;
-        }
-
-        @Override
+    static class ViewParentCompatBaseImpl {
         public boolean onStartNestedScroll(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
             if (parent instanceof NestedScrollingParent) {
@@ -74,7 +43,6 @@
             return false;
         }
 
-        @Override
         public void onNestedScrollAccepted(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
             if (parent instanceof NestedScrollingParent) {
@@ -83,14 +51,12 @@
             }
         }
 
-        @Override
         public void onStopNestedScroll(ViewParent parent, View target) {
             if (parent instanceof NestedScrollingParent) {
                 ((NestedScrollingParent) parent).onStopNestedScroll(target);
             }
         }
 
-        @Override
         public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed) {
             if (parent instanceof NestedScrollingParent) {
@@ -99,7 +65,6 @@
             }
         }
 
-        @Override
         public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
                 int[] consumed) {
             if (parent instanceof NestedScrollingParent) {
@@ -107,7 +72,6 @@
             }
         }
 
-        @Override
         public boolean onNestedFling(ViewParent parent, View target, float velocityX,
                 float velocityY, boolean consumed) {
             if (parent instanceof NestedScrollingParent) {
@@ -117,7 +81,6 @@
             return false;
         }
 
-        @Override
         public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
                 float velocityY) {
             if (parent instanceof NestedScrollingParent) {
@@ -127,88 +90,111 @@
             return false;
         }
 
-        @Override
         public void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
                 View source, int changeType) {
         }
     }
 
-    static class ViewParentCompatICSImpl extends ViewParentCompatStubImpl {
-        @Override
-        public boolean requestSendAccessibilityEvent(
-                ViewParent parent, View child, AccessibilityEvent event) {
-            return ViewParentCompatICS.requestSendAccessibilityEvent(parent, child, event);
-        }
-    }
-
-    static class ViewParentCompatKitKatImpl extends ViewParentCompatICSImpl {
+    @RequiresApi(19)
+    static class ViewParentCompatApi19Impl extends ViewParentCompatBaseImpl {
 
         @Override
         public void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
                 View source, int changeType) {
-            ViewParentCompatKitKat.notifySubtreeAccessibilityStateChanged(parent, child,
-                    source, changeType);
+            parent.notifySubtreeAccessibilityStateChanged(child, source, changeType);
         }
     }
 
-    static class ViewParentCompatLollipopImpl extends ViewParentCompatKitKatImpl {
+    @RequiresApi(21)
+    static class ViewParentCompatApi21Impl extends ViewParentCompatApi19Impl {
         @Override
         public boolean onStartNestedScroll(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
-            return ViewParentCompatLollipop.onStartNestedScroll(parent, child, target,
-                    nestedScrollAxes);
+            try {
+                return parent.onStartNestedScroll(child, target, nestedScrollAxes);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onStartNestedScroll", e);
+                return false;
+            }
         }
 
         @Override
         public void onNestedScrollAccepted(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
-            ViewParentCompatLollipop.onNestedScrollAccepted(parent, child, target,
-                    nestedScrollAxes);
+            try {
+                parent.onNestedScrollAccepted(child, target, nestedScrollAxes);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedScrollAccepted", e);
+            }
         }
 
         @Override
         public void onStopNestedScroll(ViewParent parent, View target) {
-            ViewParentCompatLollipop.onStopNestedScroll(parent, target);
+            try {
+                parent.onStopNestedScroll(target);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onStopNestedScroll", e);
+            }
         }
 
         @Override
         public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed) {
-            ViewParentCompatLollipop.onNestedScroll(parent, target, dxConsumed, dyConsumed,
-                    dxUnconsumed, dyUnconsumed);
+            try {
+                parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedScroll", e);
+            }
         }
 
         @Override
         public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
                 int[] consumed) {
-            ViewParentCompatLollipop.onNestedPreScroll(parent, target, dx, dy, consumed);
+            try {
+                parent.onNestedPreScroll(target, dx, dy, consumed);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedPreScroll", e);
+            }
         }
 
         @Override
         public boolean onNestedFling(ViewParent parent, View target, float velocityX,
                 float velocityY, boolean consumed) {
-            return ViewParentCompatLollipop.onNestedFling(parent, target, velocityX, velocityY,
-                    consumed);
+            try {
+                return parent.onNestedFling(target, velocityX, velocityY, consumed);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedFling", e);
+                return false;
+            }
         }
 
         @Override
         public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
                 float velocityY) {
-            return ViewParentCompatLollipop.onNestedPreFling(parent, target, velocityX, velocityY);
+            try {
+                return parent.onNestedPreFling(target, velocityX, velocityY);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedPreFling", e);
+                return false;
+            }
         }
     }
 
-    static final ViewParentCompatImpl IMPL;
+    static final ViewParentCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new ViewParentCompatLollipopImpl();
-        } else if (version >= 19) {
-            IMPL = new ViewParentCompatKitKatImpl();
-        } else if (version >= 14) {
-            IMPL = new ViewParentCompatICSImpl();
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ViewParentCompatApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new ViewParentCompatApi19Impl();
         } else {
-            IMPL = new ViewParentCompatStubImpl();
+            IMPL = new ViewParentCompatBaseImpl();
         }
     }
 
@@ -233,10 +219,98 @@
      * @param child The child which requests sending the event.
      * @param event The event to be sent.
      * @return True if the event was sent.
+     *
+     * @deprecated Use {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+     * directly.
      */
+    @Deprecated
     public static boolean requestSendAccessibilityEvent(
             ViewParent parent, View child, AccessibilityEvent event) {
-        return IMPL.requestSendAccessibilityEvent(parent, child, event);
+        return parent.requestSendAccessibilityEvent(child, event);
+    }
+
+    /**
+     * React to a descendant view initiating a nestable scroll operation, claiming the
+     * nested scroll operation if appropriate.
+     *
+     * <p>This version of the method just calls
+     * {@link #onStartNestedScroll(ViewParent, View, View, int, int)} using the touch input type.
+     * </p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @return true if this ViewParent accepts the nested scroll operation
+     */
+    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
+            int nestedScrollAxes) {
+        return onStartNestedScroll(parent, child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
+    }
+
+    /**
+     * React to the successful claiming of a nested scroll operation.
+     *
+     * <p>This version of the method just calls
+     * {@link #onNestedScrollAccepted(ViewParent, View, View, int, int)} using the touch input type.
+     * </p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     */
+    public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
+            int nestedScrollAxes) {
+        onNestedScrollAccepted(parent, child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
+    }
+
+    /**
+     * React to a nested scroll operation ending.
+     *
+     * <p>This version of the method just calls {@link #onStopNestedScroll(ViewParent, View)}
+     * using the touch input type.</p>
+     *
+     * @param target View that initiated the nested scroll
+     */
+    public static void onStopNestedScroll(ViewParent parent, View target) {
+        onStopNestedScroll(parent, target, ViewCompat.TYPE_TOUCH);
+    }
+
+    /**
+     * React to a nested scroll in progress.
+     *
+     * <p>This version of the method just calls
+     * {@link #onNestedScroll(ViewParent, View, int, int, int, int, int)} using the touch input
+     * type.</p>
+     *
+     * @param target The descendent view controlling the nested scroll
+     * @param dxConsumed Horizontal scroll distance in pixels already consumed by target
+     * @param dyConsumed Vertical scroll distance in pixels already consumed by target
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
+     * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
+     */
+    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
+            int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+        onNestedScroll(parent, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                ViewCompat.TYPE_TOUCH);
+    }
+
+    /**
+     * React to a nested scroll in progress before the target view consumes a portion of the scroll.
+     *
+     * <p>This version of the method just calls
+     * {@link #onNestedPreScroll(ViewParent, View, int, int, int[], int)} using the touch input
+     * type.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
+     */
+    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
+            int[] consumed) {
+        onNestedPreScroll(parent, target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
     }
 
     /**
@@ -252,18 +326,27 @@
      * is willing to support a nested scrolling operation that is about to begin. If it returns
      * true, this ViewParent will become the target view's nested scrolling parent for the duration
      * of the scroll operation in progress. When the nested scroll is finished this ViewParent
-     * will receive a call to {@link #onStopNestedScroll(ViewParent, View)}.
+     * will receive a call to {@link #onStopNestedScroll(ViewParent, View, int)}.
      * </p>
      *
      * @param child Direct child of this ViewParent containing target
      * @param target View that initiated the nested scroll
      * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
      *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @param type the type of input which cause this scroll event
      * @return true if this ViewParent accepts the nested scroll operation
      */
     public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
-            int nestedScrollAxes) {
-        return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
+            int nestedScrollAxes, int type) {
+        if (parent instanceof NestedScrollingParent2) {
+            // First try the NestedScrollingParent2 API
+            return ((NestedScrollingParent2) parent).onStartNestedScroll(child, target,
+                    nestedScrollAxes, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            // Else if the type is the default (touch), try the NestedScrollingParent API
+            return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
+        }
+        return false;
     }
 
     /**
@@ -279,12 +362,20 @@
      * @param target View that initiated the nested scroll
      * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
      *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @param type the type of input which cause this scroll event
      * @see #onStartNestedScroll(ViewParent, View, View, int)
-     * @see #onStopNestedScroll(ViewParent, View)
+     * @see #onStopNestedScroll(ViewParent, View, int)
      */
     public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
-            int nestedScrollAxes) {
-        IMPL.onNestedScrollAccepted(parent, child, target, nestedScrollAxes);
+            int nestedScrollAxes, int type) {
+        if (parent instanceof NestedScrollingParent2) {
+            // First try the NestedScrollingParent2 API
+            ((NestedScrollingParent2) parent).onNestedScrollAccepted(child, target,
+                    nestedScrollAxes, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            // Else if the type is the default (touch), try the NestedScrollingParent API
+            IMPL.onNestedScrollAccepted(parent, child, target, nestedScrollAxes);
+        }
     }
 
     /**
@@ -297,9 +388,16 @@
      * method if one is present.</p>
      *
      * @param target View that initiated the nested scroll
+     * @param type the type of input which cause this scroll event
      */
-    public static void onStopNestedScroll(ViewParent parent, View target) {
-        IMPL.onStopNestedScroll(parent, target);
+    public static void onStopNestedScroll(ViewParent parent, View target, int type) {
+        if (parent instanceof NestedScrollingParent2) {
+            // First try the NestedScrollingParent2 API
+            ((NestedScrollingParent2) parent).onStopNestedScroll(target, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            // Else if the type is the default (touch), try the NestedScrollingParent API
+            IMPL.onStopNestedScroll(parent, target);
+        }
     }
 
     /**
@@ -308,7 +406,7 @@
      * <p>This method will be called when the ViewParent's current nested scrolling child view
      * dispatches a nested scroll event. To receive calls to this method the ViewParent must have
      * previously returned <code>true</code> for a call to
-     * {@link #onStartNestedScroll(ViewParent, View, View, int)}.</p>
+     * {@link #onStartNestedScroll(ViewParent, View, View, int, int)}.</p>
      *
      * <p>Both the consumed and unconsumed portions of the scroll distance are reported to the
      * ViewParent. An implementation may choose to use the consumed portion to match or chase scroll
@@ -322,10 +420,18 @@
      * @param dyConsumed Vertical scroll distance in pixels already consumed by target
      * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
      * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
+     * @param type the type of input which cause this scroll event
      */
     public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
-            int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
-        IMPL.onNestedScroll(parent, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
+            int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
+        if (parent instanceof NestedScrollingParent2) {
+            // First try the NestedScrollingParent2 API
+            ((NestedScrollingParent2) parent).onNestedScroll(target, dxConsumed, dyConsumed,
+                    dxUnconsumed, dyUnconsumed, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            // Else if the type is the default (touch), try the NestedScrollingParent API
+            IMPL.onNestedScroll(parent, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
+        }
     }
 
     /**
@@ -347,10 +453,17 @@
      * @param dx Horizontal scroll distance in pixels
      * @param dy Vertical scroll distance in pixels
      * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
+     * @param type the type of input which cause this scroll event
      */
     public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
-            int[] consumed) {
-        IMPL.onNestedPreScroll(parent, target, dx, dy, consumed);
+            int[] consumed, int type) {
+        if (parent instanceof NestedScrollingParent2) {
+            // First try the NestedScrollingParent2 API
+            ((NestedScrollingParent2) parent).onNestedPreScroll(target, dx, dy, consumed, type);
+        } else if (type == ViewCompat.TYPE_TOUCH) {
+            // Else if the type is the default (touch), try the NestedScrollingParent API
+            IMPL.onNestedPreScroll(parent, target, dx, dy, consumed);
+        }
     }
 
     /**
@@ -421,5 +534,4 @@
             View source, int changeType) {
         IMPL.notifySubtreeAccessibilityStateChanged(parent, child, source, changeType);
     }
-
 }
diff --git a/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java b/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java
index b92c14d..44b1a11 100644
--- a/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java
+++ b/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java
@@ -15,12 +15,14 @@
  */
 package android.support.v4.view;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.os.Build;
 import android.view.View;
 import android.view.animation.Interpolator;
 
 import java.lang.ref.WeakReference;
-import java.util.WeakHashMap;
 
 public final class ViewPropertyAnimatorCompat {
     private static final String TAG = "ViewAnimatorCompat";
@@ -36,513 +38,50 @@
         mView = new WeakReference<View>(view);
     }
 
-    interface ViewPropertyAnimatorCompatImpl {
-        public void setDuration(ViewPropertyAnimatorCompat vpa, View view, long value);
-        public long getDuration(ViewPropertyAnimatorCompat vpa, View view);
-        public void setInterpolator(ViewPropertyAnimatorCompat vpa, View view, Interpolator value);
-        public Interpolator getInterpolator(ViewPropertyAnimatorCompat vpa, View view);
-        public void setStartDelay(ViewPropertyAnimatorCompat vpa, View view, long value);
-        public long getStartDelay(ViewPropertyAnimatorCompat vpa, View view);
-        public void alpha(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void alphaBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void rotation(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void rotationBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void rotationX(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void rotationXBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void rotationY(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void rotationYBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void scaleX(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void scaleXBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void scaleY(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void scaleYBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void cancel(ViewPropertyAnimatorCompat vpa, View view);
-        public void x(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void xBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void y(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void yBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void z(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void zBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void translationX(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void translationXBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void translationY(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void translationYBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void translationZ(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void translationZBy(ViewPropertyAnimatorCompat vpa, View view, float value);
-        public void start(ViewPropertyAnimatorCompat vpa, View view);
-        public void withLayer(ViewPropertyAnimatorCompat vpa, View view);
-        public void withStartAction(ViewPropertyAnimatorCompat vpa, View view, Runnable runnable);
-        public void withEndAction(ViewPropertyAnimatorCompat vpa, View view, Runnable runnable);
-        public void setListener(ViewPropertyAnimatorCompat vpa, View view,
-                ViewPropertyAnimatorListener listener);
-        public void setUpdateListener(ViewPropertyAnimatorCompat vpa, View view,
-                ViewPropertyAnimatorUpdateListener listener);
-    };
+    static class ViewPropertyAnimatorListenerApi14 implements ViewPropertyAnimatorListener {
+        ViewPropertyAnimatorCompat mVpa;
+        boolean mAnimEndCalled;
 
-    static class BaseViewPropertyAnimatorCompatImpl implements ViewPropertyAnimatorCompatImpl {
-        WeakHashMap<View, Runnable> mStarterMap = null;
-
-        @Override
-        public void setDuration(ViewPropertyAnimatorCompat vpa, View view, long value) {
-            // noop on versions prior to ICS
+        ViewPropertyAnimatorListenerApi14(ViewPropertyAnimatorCompat vpa) {
+            mVpa = vpa;
         }
 
         @Override
-        public void alpha(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
+        public void onAnimationStart(View view) {
+            // Reset our end called flag, since this is a new animation...
+            mAnimEndCalled = false;
 
-        @Override
-        public void translationX(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void translationY(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void withEndAction(ViewPropertyAnimatorCompat vpa, View view, Runnable runnable) {
-            vpa.mEndAction = runnable;
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public long getDuration(ViewPropertyAnimatorCompat vpa, View view) {
-            return 0;
-        }
-
-        @Override
-        public void setInterpolator(ViewPropertyAnimatorCompat vpa, View view, Interpolator value) {
-            // noop on versions prior to ICS
-        }
-
-        @Override
-        public Interpolator getInterpolator(ViewPropertyAnimatorCompat vpa, View view) {
-            return null;
-        }
-
-        @Override
-        public void setStartDelay(ViewPropertyAnimatorCompat vpa, View view, long value) {
-            // noop on versions prior to ICS
-        }
-
-        @Override
-        public long getStartDelay(ViewPropertyAnimatorCompat vpa, View view) {
-            return 0;
-        }
-
-        @Override
-        public void alphaBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void rotation(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void rotationBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void rotationX(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void rotationXBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void rotationY(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void rotationYBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void scaleX(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void scaleXBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void scaleY(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void scaleYBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void cancel(ViewPropertyAnimatorCompat vpa, View view) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void x(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void xBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void y(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void yBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void z(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to Lollipop
-        }
-
-        @Override
-        public void zBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to Lollipop
-        }
-
-        @Override
-        public void translationXBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void translationYBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to ICS
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void translationZ(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to Lollipop
-        }
-
-        @Override
-        public void translationZBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            // noop on versions prior to Lollipop
-        }
-
-        @Override
-        public void start(ViewPropertyAnimatorCompat vpa, View view) {
-            removeStartMessage(view);
-            startAnimation(vpa, view);
-        }
-
-        @Override
-        public void withLayer(ViewPropertyAnimatorCompat vpa, View view) {
-            // noop on versions prior to ICS
-        }
-
-        @Override
-        public void withStartAction(ViewPropertyAnimatorCompat vpa, View view, Runnable runnable) {
-            vpa.mStartAction = runnable;
-            postStartMessage(vpa, view);
-        }
-
-        @Override
-        public void setListener(ViewPropertyAnimatorCompat vpa, View view, ViewPropertyAnimatorListener listener) {
-            view.setTag(LISTENER_TAG_ID, listener);
-        }
-
-        @Override
-        public void setUpdateListener(ViewPropertyAnimatorCompat vpa, View view, ViewPropertyAnimatorUpdateListener listener) {
-            // noop
-        }
-
-        void startAnimation(ViewPropertyAnimatorCompat vpa, View view) {
+            if (mVpa.mOldLayerType > -1) {
+                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            }
+            if (mVpa.mStartAction != null) {
+                Runnable startAction = mVpa.mStartAction;
+                mVpa.mStartAction = null;
+                startAction.run();
+            }
             Object listenerTag = view.getTag(LISTENER_TAG_ID);
             ViewPropertyAnimatorListener listener = null;
             if (listenerTag instanceof ViewPropertyAnimatorListener) {
                 listener = (ViewPropertyAnimatorListener) listenerTag;
             }
-            Runnable startAction = vpa.mStartAction;
-            Runnable endAction = vpa.mEndAction;
-            vpa.mStartAction = null;
-            vpa.mEndAction = null;
-            if (startAction != null) {
-                startAction.run();
-            }
             if (listener != null) {
                 listener.onAnimationStart(view);
-                listener.onAnimationEnd(view);
-            }
-            if (endAction != null) {
-                endAction.run();
-            }
-            if (mStarterMap != null) {
-                mStarterMap.remove(view);
             }
         }
 
-        class Starter implements Runnable {
-            WeakReference<View> mViewRef;
-            ViewPropertyAnimatorCompat mVpa;
-
-            Starter(ViewPropertyAnimatorCompat vpa, View view) {
-                mViewRef = new WeakReference<View>(view);
-                mVpa = vpa;
+        @Override
+        public void onAnimationEnd(View view) {
+            if (mVpa.mOldLayerType > -1) {
+                view.setLayerType(mVpa.mOldLayerType, null);
+                mVpa.mOldLayerType = -1;
             }
-
-            @Override
-            public void run() {
-                final View view = mViewRef.get();
-                if (view != null) {
-                    startAnimation(mVpa, view);
-                }
-            }
-        };
-
-        private void removeStartMessage(View view) {
-            Runnable starter = null;
-            if (mStarterMap != null) {
-                starter = mStarterMap.get(view);
-                if (starter != null) {
-                    view.removeCallbacks(starter);
-                }
-            }
-        }
-
-        private void postStartMessage(ViewPropertyAnimatorCompat vpa, View view) {
-            Runnable starter = null;
-            if (mStarterMap != null) {
-                starter = mStarterMap.get(view);
-            }
-            if (starter == null) {
-                starter = new Starter(vpa, view);
-                if (mStarterMap == null) {
-                    mStarterMap = new WeakHashMap<View, Runnable>();
-                }
-                mStarterMap.put(view, starter);
-            }
-            view.removeCallbacks(starter);
-            view.post(starter);
-        }
-
-    }
-
-    static class ICSViewPropertyAnimatorCompatImpl extends BaseViewPropertyAnimatorCompatImpl {
-        WeakHashMap<View, Integer> mLayerMap = null;
-
-        @Override
-        public void setDuration(ViewPropertyAnimatorCompat vpa, View view, long value) {
-            ViewPropertyAnimatorCompatICS.setDuration(view, value);
-        }
-
-        @Override
-        public void alpha(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.alpha(view, value);
-        }
-
-        @Override
-        public void translationX(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.translationX(view, value);
-        }
-
-        @Override
-        public void translationY(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.translationY(view, value);
-        }
-
-        @Override
-        public long getDuration(ViewPropertyAnimatorCompat vpa, View view) {
-            return ViewPropertyAnimatorCompatICS.getDuration(view);
-        }
-
-        @Override
-        public void setInterpolator(ViewPropertyAnimatorCompat vpa, View view, Interpolator value) {
-            ViewPropertyAnimatorCompatICS.setInterpolator(view, value);
-        }
-
-        @Override
-        public void setStartDelay(ViewPropertyAnimatorCompat vpa, View view, long value) {
-            ViewPropertyAnimatorCompatICS.setStartDelay(view, value);
-        }
-
-        @Override
-        public long getStartDelay(ViewPropertyAnimatorCompat vpa, View view) {
-            return ViewPropertyAnimatorCompatICS.getStartDelay(view);
-        }
-
-        @Override
-        public void alphaBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.alphaBy(view, value);
-        }
-
-        @Override
-        public void rotation(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.rotation(view, value);
-        }
-
-        @Override
-        public void rotationBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.rotationBy(view, value);
-        }
-
-        @Override
-        public void rotationX(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.rotationX(view, value);
-        }
-
-        @Override
-        public void rotationXBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.rotationXBy(view, value);
-        }
-
-        @Override
-        public void rotationY(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.rotationY(view, value);
-        }
-
-        @Override
-        public void rotationYBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.rotationYBy(view, value);
-        }
-
-        @Override
-        public void scaleX(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.scaleX(view, value);
-        }
-
-        @Override
-        public void scaleXBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.scaleXBy(view, value);
-        }
-
-        @Override
-        public void scaleY(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.scaleY(view, value);
-        }
-
-        @Override
-        public void scaleYBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.scaleYBy(view, value);
-        }
-
-        @Override
-        public void cancel(ViewPropertyAnimatorCompat vpa, View view) {
-            ViewPropertyAnimatorCompatICS.cancel(view);
-        }
-
-        @Override
-        public void x(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.x(view, value);
-        }
-
-        @Override
-        public void xBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.xBy(view, value);
-        }
-
-        @Override
-        public void y(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.y(view, value);
-        }
-
-        @Override
-        public void yBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.yBy(view, value);
-        }
-
-        @Override
-        public void translationXBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.translationXBy(view, value);
-        }
-
-        @Override
-        public void translationYBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatICS.translationYBy(view, value);
-        }
-
-        @Override
-        public void start(ViewPropertyAnimatorCompat vpa, View view) {
-            ViewPropertyAnimatorCompatICS.start(view);
-        }
-
-        @Override
-        public void setListener(ViewPropertyAnimatorCompat vpa, View view, ViewPropertyAnimatorListener listener) {
-            view.setTag(LISTENER_TAG_ID, listener);
-            ViewPropertyAnimatorCompatICS.setListener(view, new MyVpaListener(vpa));
-        }
-
-        @Override
-        public void withEndAction(ViewPropertyAnimatorCompat vpa, View view, final Runnable runnable) {
-            ViewPropertyAnimatorCompatICS.setListener(view, new MyVpaListener(vpa));
-            vpa.mEndAction = runnable;
-        }
-
-        @Override
-        public void withStartAction(ViewPropertyAnimatorCompat vpa, View view, final Runnable runnable) {
-            ViewPropertyAnimatorCompatICS.setListener(view, new MyVpaListener(vpa));
-            vpa.mStartAction = runnable;
-        }
-
-        @Override
-        public void withLayer(ViewPropertyAnimatorCompat vpa, View view) {
-            vpa.mOldLayerType = ViewCompat.getLayerType(view);
-            ViewPropertyAnimatorCompatICS.setListener(view, new MyVpaListener(vpa));
-        }
-
-        static class MyVpaListener implements ViewPropertyAnimatorListener {
-            ViewPropertyAnimatorCompat mVpa;
-            boolean mAnimEndCalled;
-
-            MyVpaListener(ViewPropertyAnimatorCompat vpa) {
-                mVpa = vpa;
-            }
-
-            @Override
-            public void onAnimationStart(View view) {
-                // Reset our end called flag, since this is a new animation...
-                mAnimEndCalled = false;
-
-                if (mVpa.mOldLayerType >= 0) {
-                    ViewCompat.setLayerType(view, ViewCompat.LAYER_TYPE_HARDWARE, null);
-                }
-                if (mVpa.mStartAction != null) {
-                    Runnable startAction = mVpa.mStartAction;
-                    mVpa.mStartAction = null;
-                    startAction.run();
+            if (Build.VERSION.SDK_INT >= 16 || !mAnimEndCalled) {
+                // Pre-v16 seems to have a bug where onAnimationEnd is called
+                // twice, therefore we only dispatch on the first call
+                if (mVpa.mEndAction != null) {
+                    Runnable endAction = mVpa.mEndAction;
+                    mVpa.mEndAction = null;
+                    endAction.run();
                 }
                 Object listenerTag = view.getTag(LISTENER_TAG_ID);
                 ViewPropertyAnimatorListener listener = null;
@@ -550,125 +89,22 @@
                     listener = (ViewPropertyAnimatorListener) listenerTag;
                 }
                 if (listener != null) {
-                    listener.onAnimationStart(view);
+                    listener.onAnimationEnd(view);
                 }
+                mAnimEndCalled = true;
             }
+        }
 
-            @Override
-            public void onAnimationEnd(View view) {
-                if (mVpa.mOldLayerType >= 0) {
-                    ViewCompat.setLayerType(view, mVpa.mOldLayerType, null);
-                    mVpa.mOldLayerType = -1;
-                }
-                if (Build.VERSION.SDK_INT >= 16 || !mAnimEndCalled) {
-                    // Pre-v16 seems to have a bug where onAnimationEnd is called
-                    // twice, therefore we only dispatch on the first call
-                    if (mVpa.mEndAction != null) {
-                        Runnable endAction = mVpa.mEndAction;
-                        mVpa.mEndAction = null;
-                        endAction.run();
-                    }
-                    Object listenerTag = view.getTag(LISTENER_TAG_ID);
-                    ViewPropertyAnimatorListener listener = null;
-                    if (listenerTag instanceof ViewPropertyAnimatorListener) {
-                        listener = (ViewPropertyAnimatorListener) listenerTag;
-                    }
-                    if (listener != null) {
-                        listener.onAnimationEnd(view);
-                    }
-                    mAnimEndCalled = true;
-                }
+        @Override
+        public void onAnimationCancel(View view) {
+            Object listenerTag = view.getTag(LISTENER_TAG_ID);
+            ViewPropertyAnimatorListener listener = null;
+            if (listenerTag instanceof ViewPropertyAnimatorListener) {
+                listener = (ViewPropertyAnimatorListener) listenerTag;
             }
-
-            @Override
-            public void onAnimationCancel(View view) {
-                Object listenerTag = view.getTag(LISTENER_TAG_ID);
-                ViewPropertyAnimatorListener listener = null;
-                if (listenerTag instanceof ViewPropertyAnimatorListener) {
-                    listener = (ViewPropertyAnimatorListener) listenerTag;
-                }
-                if (listener != null) {
-                    listener.onAnimationCancel(view);
-                }
+            if (listener != null) {
+                listener.onAnimationCancel(view);
             }
-        };
-    }
-
-    static class JBViewPropertyAnimatorCompatImpl extends ICSViewPropertyAnimatorCompatImpl {
-
-        @Override
-        public void setListener(ViewPropertyAnimatorCompat vpa, View view, ViewPropertyAnimatorListener listener) {
-            ViewPropertyAnimatorCompatJB.setListener(view, listener);
-        }
-
-        @Override
-        public void withStartAction(ViewPropertyAnimatorCompat vpa, View view, Runnable runnable) {
-            ViewPropertyAnimatorCompatJB.withStartAction(view, runnable);
-        }
-
-        @Override
-        public void withEndAction(ViewPropertyAnimatorCompat vpa, View view, Runnable runnable) {
-            ViewPropertyAnimatorCompatJB.withEndAction(view, runnable);
-        }
-
-        @Override
-        public void withLayer(ViewPropertyAnimatorCompat vpa, View view) {
-            ViewPropertyAnimatorCompatJB.withLayer(view);
-        }
-    }
-
-    static class JBMr2ViewPropertyAnimatorCompatImpl extends JBViewPropertyAnimatorCompatImpl {
-
-        @Override
-        public Interpolator getInterpolator(ViewPropertyAnimatorCompat vpa, View view) {
-            return (Interpolator) ViewPropertyAnimatorCompatJellybeanMr2.getInterpolator(view);
-        }
-    }
-
-    static class KitKatViewPropertyAnimatorCompatImpl extends JBMr2ViewPropertyAnimatorCompatImpl {
-        @Override
-        public void setUpdateListener(ViewPropertyAnimatorCompat vpa, View view, ViewPropertyAnimatorUpdateListener listener) {
-            ViewPropertyAnimatorCompatKK.setUpdateListener(view, listener);
-        }
-    }
-
-    static class LollipopViewPropertyAnimatorCompatImpl extends KitKatViewPropertyAnimatorCompatImpl {
-        @Override
-        public void translationZ(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatLollipop.translationZ(view, value);
-        }
-
-        @Override
-        public void translationZBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatLollipop.translationZBy(view, value);
-        }
-
-        @Override
-        public void z(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatLollipop.z(view, value);
-        }
-
-        @Override
-        public void zBy(ViewPropertyAnimatorCompat vpa, View view, float value) {
-            ViewPropertyAnimatorCompatLollipop.zBy(view, value);
-        }
-    }
-
-    static final ViewPropertyAnimatorCompatImpl IMPL;
-    static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new LollipopViewPropertyAnimatorCompatImpl();
-        } else if (version >= 19) {
-            IMPL = new KitKatViewPropertyAnimatorCompatImpl();
-        } else if (version >= 18) {
-            IMPL = new JBMr2ViewPropertyAnimatorCompatImpl();
-        } else if (version >= 16) {
-            IMPL = new JBViewPropertyAnimatorCompatImpl();
-        } else if (version >= 14) {
-            IMPL = new ICSViewPropertyAnimatorCompatImpl();
-        } else {
-            IMPL = new BaseViewPropertyAnimatorCompatImpl();
         }
     }
 
@@ -677,8 +113,6 @@
      * By default, the animator uses the default value for ValueAnimator. Calling this method
      * will cause the declared value to be used instead.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The length of ensuing property animations, in milliseconds. The value
      * cannot be negative.
      * @return This object, allowing calls to methods in this class to be chained.
@@ -686,7 +120,7 @@
     public ViewPropertyAnimatorCompat setDuration(long value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.setDuration(this, view, value);
+            view.animate().setDuration(value);
         }
         return this;
     }
@@ -695,15 +129,13 @@
      * This method will cause the View's <code>alpha</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat alpha(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.alpha(this, view, value);
+            view.animate().alpha(value);
         }
         return this;
     }
@@ -712,15 +144,13 @@
      * This method will cause the View's <code>alpha</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat alphaBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.alphaBy(this, view, value);
+            view.animate().alphaBy(value);
         }
         return this;
     }
@@ -729,15 +159,13 @@
      * This method will cause the View's <code>translationX</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat translationX(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.translationX(this, view, value);
+            view.animate().translationX(value);
         }
         return this;
     }
@@ -746,15 +174,13 @@
      * This method will cause the View's <code>translationY</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat translationY(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.translationY(this, view, value);
+            view.animate().translationY(value);
         }
         return this;
     }
@@ -777,8 +203,6 @@
      *     view.animate().x(200).withEndAction(endAction);
      * </pre>
      *
-     * <p>Prior to API 14, this method will run the action immediately.</p>
-     *
      * <p>For API 14 and 15, this method will run by setting
      * a listener on the ViewPropertyAnimatorCompat object and running the action
      * in that listener's {@link ViewPropertyAnimatorListener#onAnimationEnd(View)} method.</p>
@@ -789,7 +213,12 @@
     public ViewPropertyAnimatorCompat withEndAction(Runnable runnable) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.withEndAction(this, view, runnable);
+            if (Build.VERSION.SDK_INT >= 16) {
+                view.animate().withEndAction(runnable);
+            } else {
+                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
+                mEndAction = runnable;
+            }
         }
         return this;
     }
@@ -799,15 +228,13 @@
      * object, that value is returned. Otherwise, the default value of the underlying Animator
      * is returned.
      *
-     * <p>Prior to API 14, this method will return 0.</p>
-     *
      * @see #setDuration(long)
      * @return The duration of animations, in milliseconds.
      */
     public long getDuration() {
         View view;
         if ((view = mView.get()) != null) {
-            return IMPL.getDuration(this, view);
+            return view.animate().getDuration();
         } else {
             return 0;
         }
@@ -818,15 +245,13 @@
      * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
      * will cause the declared object to be used instead.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The TimeInterpolator to be used for ensuing property animations.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat setInterpolator(Interpolator value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.setInterpolator(this, view, value);
+            view.animate().setInterpolator(value);
         }
         return this;
     }
@@ -834,16 +259,16 @@
     /**
      * Returns the timing interpolator that this animation uses.
      *
-     * <p>Prior to API 14, this method will return null.</p>
-     *
      * @return The timing interpolator for this animation.
      */
     public Interpolator getInterpolator() {
         View view;
         if ((view = mView.get()) != null) {
-            return IMPL.getInterpolator(this, view);
+            if (Build.VERSION.SDK_INT >= 18) {
+                return (Interpolator) view.animate().getInterpolator();
+            }
         }
-        else return null;
+        return null;
     }
 
     /**
@@ -851,8 +276,6 @@
      * By default, the animator uses the default value for ValueAnimator. Calling this method
      * will cause the declared value to be used instead.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The delay of ensuing property animations, in milliseconds. The value
      * cannot be negative.
      * @return This object, allowing calls to methods in this class to be chained.
@@ -860,7 +283,7 @@
     public ViewPropertyAnimatorCompat setStartDelay(long value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.setStartDelay(this, view, value);
+            view.animate().setStartDelay(value);
         }
         return this;
     }
@@ -870,15 +293,13 @@
      * object, that value is returned. Otherwise, the default value of the underlying Animator
      * is returned.
      *
-     * <p>Prior to API 14, this method will return 0.</p>
-     *
      * @see #setStartDelay(long)
      * @return The startDelay of animations, in milliseconds.
      */
     public long getStartDelay() {
         View view;
         if ((view = mView.get()) != null) {
-            return IMPL.getStartDelay(this, view);
+            return view.animate().getStartDelay();
         } else {
             return 0;
         }
@@ -888,15 +309,13 @@
      * This method will cause the View's <code>rotation</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat rotation(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.rotation(this, view, value);
+            view.animate().rotation(value);
         }
         return this;
     }
@@ -905,15 +324,13 @@
      * This method will cause the View's <code>rotation</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat rotationBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.rotationBy(this, view, value);
+            view.animate().rotationBy(value);
         }
         return this;
     }
@@ -922,15 +339,13 @@
      * This method will cause the View's <code>rotationX</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat rotationX(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.rotationX(this, view, value);
+            view.animate().rotationX(value);
         }
         return this;
     }
@@ -939,15 +354,13 @@
      * This method will cause the View's <code>rotationX</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat rotationXBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.rotationXBy(this, view, value);
+            view.animate().rotationXBy(value);
         }
         return this;
     }
@@ -956,15 +369,13 @@
      * This method will cause the View's <code>rotationY</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat rotationY(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.rotationY(this, view, value);
+            view.animate().rotationY(value);
         }
         return this;
     }
@@ -973,15 +384,13 @@
      * This method will cause the View's <code>rotationY</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat rotationYBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.rotationYBy(this, view, value);
+            view.animate().rotationYBy(value);
         }
         return this;
     }
@@ -990,15 +399,13 @@
      * This method will cause the View's <code>scaleX</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat scaleX(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.scaleX(this, view, value);
+            view.animate().scaleX(value);
         }
         return this;
     }
@@ -1007,15 +414,13 @@
      * This method will cause the View's <code>scaleX</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat scaleXBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.scaleXBy(this, view, value);
+            view.animate().scaleXBy(value);
         }
         return this;
     }
@@ -1024,15 +429,13 @@
      * This method will cause the View's <code>scaleY</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat scaleY(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.scaleY(this, view, value);
+            view.animate().scaleY(value);
         }
         return this;
     }
@@ -1041,15 +444,13 @@
      * This method will cause the View's <code>scaleY</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat scaleYBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.scaleYBy(this, view, value);
+            view.animate().scaleYBy(value);
         }
         return this;
     }
@@ -1060,7 +461,7 @@
     public void cancel() {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.cancel(this, view);
+            view.animate().cancel();
         }
     }
 
@@ -1068,15 +469,13 @@
      * This method will cause the View's <code>x</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat x(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.x(this, view, value);
+            view.animate().x(value);
         }
         return this;
     }
@@ -1085,15 +484,13 @@
      * This method will cause the View's <code>x</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat xBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.xBy(this, view, value);
+            view.animate().xBy(value);
         }
         return this;
     }
@@ -1102,15 +499,13 @@
      * This method will cause the View's <code>y</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The value to be animated to.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat y(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.y(this, view, value);
+            view.animate().y(value);
         }
         return this;
     }
@@ -1119,15 +514,13 @@
      * This method will cause the View's <code>y</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat yBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.yBy(this, view, value);
+            view.animate().yBy(value);
         }
         return this;
     }
@@ -1136,15 +529,13 @@
      * This method will cause the View's <code>translationX</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat translationXBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.translationXBy(this, view, value);
+            view.animate().translationXBy(value);
         }
         return this;
     }
@@ -1153,15 +544,13 @@
      * This method will cause the View's <code>translationY</code> property to be animated by the
      * specified value. Animations already running on the property will be canceled.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param value The amount to be animated by, as an offset from the current value.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat translationYBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.translationYBy(this, view, value);
+            view.animate().translationYBy(value);
         }
         return this;
     }
@@ -1178,7 +567,9 @@
     public ViewPropertyAnimatorCompat translationZBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.translationZBy(this, view, value);
+            if (Build.VERSION.SDK_INT >= 21) {
+                view.animate().translationZBy(value);
+            }
         }
         return this;
     }
@@ -1195,7 +586,9 @@
     public ViewPropertyAnimatorCompat translationZ(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.translationZ(this, view, value);
+            if (Build.VERSION.SDK_INT >= 21) {
+                view.animate().translationZ(value);
+            }
         }
         return this;
     }
@@ -1212,7 +605,9 @@
     public ViewPropertyAnimatorCompat z(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.z(this, view, value);
+            if (Build.VERSION.SDK_INT >= 21) {
+                view.animate().z(value);
+            }
         }
         return this;
     }
@@ -1229,7 +624,9 @@
     public ViewPropertyAnimatorCompat zBy(float value) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.zBy(this, view, value);
+            if (Build.VERSION.SDK_INT >= 21) {
+                view.animate().zBy(value);
+            }
         }
         return this;
     }
@@ -1240,37 +637,33 @@
      * if the animations are needed to start immediately and synchronously (not at the time when
      * the next event is processed by the hierarchy, which is when the animations would begin
      * otherwise), then this method can be used.
-     *
-     * <p>Prior to API 14, this method will do nothing.</p>
      */
     public void start() {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.start(this, view);
+            view.animate().start();
         }
     }
 
     /**
      * The View associated with this ViewPropertyAnimator will have its
-     * {@link ViewCompat#setLayerType(View, int, android.graphics.Paint) layer type} set to
-     * {@link ViewCompat#LAYER_TYPE_HARDWARE} for the duration of the next animation.
-     * As stated in the documentation for {@link ViewCompat#LAYER_TYPE_HARDWARE},
+     * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to
+     * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation.
+     * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE},
      * the actual type of layer used internally depends on the runtime situation of the
      * view. If the activity and this view are hardware-accelerated, then the layer will be
      * accelerated as well. If the activity or the view is not accelerated, then the layer will
-     * effectively be the same as {@link ViewCompat#LAYER_TYPE_SOFTWARE}.
+     * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}.
      *
      * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the
      * layer type of the View will be restored when the animation ends to what it was when this
      * method was called, and this setting on ViewPropertyAnimator is only valid for the next
      * animation. Note that calling this method and then independently setting the layer type of
      * the View (by a direct call to
-     * {@link ViewCompat#setLayerType(View, int, android.graphics.Paint)}) will result in some
+     * {@link View#setLayerType(int, android.graphics.Paint)}) will result in some
      * inconsistency, including having the layer type restored to its pre-withLayer()
      * value when the animation ends.</p>
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * <p>For API 14 and 15, this method will run by setting
      * a listener on the ViewPropertyAnimatorCompat object, setting a hardware layer in
      * the listener's {@link ViewPropertyAnimatorListener#onAnimationStart(View)} method,
@@ -1283,7 +676,12 @@
     public ViewPropertyAnimatorCompat withLayer() {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.withLayer(this, view);
+            if (Build.VERSION.SDK_INT >= 16) {
+                view.animate().withLayer();
+            } else {
+                mOldLayerType = view.getLayerType();
+                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
+            }
         }
         return this;
     }
@@ -1296,8 +694,6 @@
      * choreographing ViewPropertyAnimator animations with other animations or actions
      * in the application.
      *
-     * <p>Prior to API 14, this method will run the action immediately.</p>
-     *
      * <p>For API 14 and 15, this method will run by setting
      * a listener on the ViewPropertyAnimatorCompat object and running the action
      * in that listener's {@link ViewPropertyAnimatorListener#onAnimationStart(View)} method.</p>
@@ -1308,7 +704,12 @@
     public ViewPropertyAnimatorCompat withStartAction(Runnable runnable) {
         View view;
         if ((view = mView.get()) != null) {
-            IMPL.withStartAction(this, view, runnable);
+            if (Build.VERSION.SDK_INT >= 16) {
+                view.animate().withStartAction(runnable);
+            } else {
+                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
+                mStartAction = runnable;
+            }
         }
         return this;
     }
@@ -1317,20 +718,46 @@
      * Sets a listener for events in the underlying Animators that run the property
      * animations.
      *
-     * <p>Prior to API 14, this method will do nothing.</p>
-     *
      * @param listener The listener to be called with AnimatorListener events. A value of
      * <code>null</code> removes any existing listener.
      * @return This object, allowing calls to methods in this class to be chained.
      */
-    public ViewPropertyAnimatorCompat setListener(ViewPropertyAnimatorListener listener) {
-        View view;
+    public ViewPropertyAnimatorCompat setListener(final ViewPropertyAnimatorListener listener) {
+        final View view;
         if ((view = mView.get()) != null) {
-            IMPL.setListener(this, view, listener);
+            if (Build.VERSION.SDK_INT >= 16) {
+                setListenerInternal(view, listener);
+            } else {
+                view.setTag(LISTENER_TAG_ID, listener);
+                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
+            }
         }
         return this;
     }
 
+    private void setListenerInternal(final View view, final ViewPropertyAnimatorListener listener) {
+        if (listener != null) {
+            view.animate().setListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    listener.onAnimationCancel(view);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    listener.onAnimationEnd(view);
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    listener.onAnimationStart(view);
+                }
+            });
+        } else {
+            view.animate().setListener(null);
+        }
+    }
+
     /**
      * Sets a listener for update events in the underlying Animator that runs
      * the property animations.
@@ -1342,10 +769,21 @@
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimatorCompat setUpdateListener(
-            ViewPropertyAnimatorUpdateListener listener) {
-        View view;
+            final ViewPropertyAnimatorUpdateListener listener) {
+        final View view;
         if ((view = mView.get()) != null) {
-            IMPL.setUpdateListener(this, view, listener);
+            if (Build.VERSION.SDK_INT >= 19) {
+                ValueAnimator.AnimatorUpdateListener wrapped = null;
+                if (listener != null) {
+                    wrapped = new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                            listener.onAnimationUpdate(view);
+                        }
+                    };
+                }
+                view.animate().setUpdateListener(wrapped);
+            }
         }
         return this;
     }
diff --git a/compat/java/android/support/v4/view/WindowCompat.java b/compat/java/android/support/v4/view/WindowCompat.java
index 9a0773e..cdf4789 100644
--- a/compat/java/android/support/v4/view/WindowCompat.java
+++ b/compat/java/android/support/v4/view/WindowCompat.java
@@ -20,8 +20,7 @@
 import android.view.Window;
 
 /**
- * Helper for accessing features in {@link Window} introduced after API
- * level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link Window}.
  */
 public final class WindowCompat {
     /**
diff --git a/compat/java/android/support/v4/view/WindowInsetsCompat.java b/compat/java/android/support/v4/view/WindowInsetsCompat.java
index 79befe9..1e23ff9 100644
--- a/compat/java/android/support/v4/view/WindowInsetsCompat.java
+++ b/compat/java/android/support/v4/view/WindowInsetsCompat.java
@@ -16,8 +16,10 @@
 
 package android.support.v4.view;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.graphics.Rect;
-import android.os.Build;
+import android.view.WindowInsets;
 
 /**
  * Describes a set of insets for window content.
@@ -27,243 +29,9 @@
  * WindowInsetsCompat instance with the adjusted properties.</p>
  */
 public class WindowInsetsCompat {
-    private interface WindowInsetsCompatImpl {
-        int getSystemWindowInsetLeft(Object insets);
-        int getSystemWindowInsetTop(Object insets);
-        int getSystemWindowInsetRight(Object insets);
-        int getSystemWindowInsetBottom(Object insets);
-        boolean hasSystemWindowInsets(Object insets);
-        boolean hasInsets(Object insets);
-        boolean isConsumed(Object insets);
-        boolean isRound(Object insets);
-        WindowInsetsCompat consumeSystemWindowInsets(Object insets);
-        WindowInsetsCompat replaceSystemWindowInsets(Object insets,
-                int left, int top, int right, int bottom);
-        WindowInsetsCompat replaceSystemWindowInsets(Object insets, Rect systemWindowInsets);
-        int getStableInsetTop(Object insets);
-        int getStableInsetLeft(Object insets);
-        int getStableInsetRight(Object insets);
-        int getStableInsetBottom(Object insets);
-        boolean hasStableInsets(Object insets);
-        WindowInsetsCompat consumeStableInsets(Object insets);
-        Object getSourceWindowInsets(Object src);
-    }
-
-    private static class WindowInsetsCompatBaseImpl implements WindowInsetsCompatImpl {
-        WindowInsetsCompatBaseImpl() {
-        }
-
-        @Override
-        public int getSystemWindowInsetLeft(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public int getSystemWindowInsetTop(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public int getSystemWindowInsetRight(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public int getSystemWindowInsetBottom(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public boolean hasSystemWindowInsets(Object insets) {
-            return false;
-        }
-
-        @Override
-        public boolean hasInsets(Object insets) {
-            return false;
-        }
-
-        @Override
-        public boolean isConsumed(Object insets) {
-            return false;
-        }
-
-        @Override
-        public boolean isRound(Object insets) {
-            return false;
-        }
-
-        @Override
-        public WindowInsetsCompat consumeSystemWindowInsets(Object insets) {
-            return null;
-        }
-
-        @Override
-        public WindowInsetsCompat replaceSystemWindowInsets(Object insets, int left, int top, int right, int bottom) {
-            return null;
-        }
-
-        @Override
-        public WindowInsetsCompat replaceSystemWindowInsets(Object insets, Rect systemWindowInsets) {
-            return null;
-        }
-
-        @Override
-        public int getStableInsetTop(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public int getStableInsetLeft(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public int getStableInsetRight(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public int getStableInsetBottom(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public boolean hasStableInsets(Object insets) {
-            return false;
-        }
-
-        @Override
-        public WindowInsetsCompat consumeStableInsets(Object insets) {
-            return null;
-        }
-
-        @Override
-        public Object getSourceWindowInsets(Object src) {
-            return null;
-        }
-    }
-
-    private static class WindowInsetsCompatApi20Impl extends WindowInsetsCompatBaseImpl {
-        WindowInsetsCompatApi20Impl() {
-        }
-
-        @Override
-        public WindowInsetsCompat consumeSystemWindowInsets(Object insets) {
-            return new WindowInsetsCompat(
-                    WindowInsetsCompatApi20.consumeSystemWindowInsets(insets));
-        }
-
-        @Override
-        public int getSystemWindowInsetBottom(Object insets) {
-            return WindowInsetsCompatApi20.getSystemWindowInsetBottom(insets);
-        }
-
-        @Override
-        public int getSystemWindowInsetLeft(Object insets) {
-            return WindowInsetsCompatApi20.getSystemWindowInsetLeft(insets);
-        }
-
-        @Override
-        public int getSystemWindowInsetRight(Object insets) {
-            return WindowInsetsCompatApi20.getSystemWindowInsetRight(insets);
-        }
-
-        @Override
-        public int getSystemWindowInsetTop(Object insets) {
-            return WindowInsetsCompatApi20.getSystemWindowInsetTop(insets);
-        }
-
-        @Override
-        public boolean hasInsets(Object insets) {
-            return WindowInsetsCompatApi20.hasInsets(insets);
-        }
-
-        @Override
-        public boolean hasSystemWindowInsets(Object insets) {
-            return WindowInsetsCompatApi20.hasSystemWindowInsets(insets);
-        }
-
-        @Override
-        public boolean isRound(Object insets) {
-            return WindowInsetsCompatApi20.isRound(insets);
-        }
-
-        @Override
-        public WindowInsetsCompat replaceSystemWindowInsets(Object insets, int left, int top,
-                int right, int bottom) {
-            return new WindowInsetsCompat(WindowInsetsCompatApi20.replaceSystemWindowInsets(insets,
-                    left, top, right, bottom));
-        }
-
-        @Override
-        public Object getSourceWindowInsets(Object src) {
-            return WindowInsetsCompatApi20.getSourceWindowInsets(src);
-        }
-    }
-
-    private static class WindowInsetsCompatApi21Impl extends WindowInsetsCompatApi20Impl {
-        WindowInsetsCompatApi21Impl() {
-        }
-
-        @Override
-        public WindowInsetsCompat consumeStableInsets(Object insets) {
-            return new WindowInsetsCompat(WindowInsetsCompatApi21.consumeStableInsets(insets));
-        }
-
-        @Override
-        public int getStableInsetBottom(Object insets) {
-            return WindowInsetsCompatApi21.getStableInsetBottom(insets);
-        }
-
-        @Override
-        public int getStableInsetLeft(Object insets) {
-            return WindowInsetsCompatApi21.getStableInsetLeft(insets);
-        }
-
-        @Override
-        public int getStableInsetRight(Object insets) {
-            return WindowInsetsCompatApi21.getStableInsetRight(insets);
-        }
-
-        @Override
-        public int getStableInsetTop(Object insets) {
-            return WindowInsetsCompatApi21.getStableInsetTop(insets);
-        }
-
-        @Override
-        public boolean hasStableInsets(Object insets) {
-            return WindowInsetsCompatApi21.hasStableInsets(insets);
-        }
-
-        @Override
-        public boolean isConsumed(Object insets) {
-            return WindowInsetsCompatApi21.isConsumed(insets);
-        }
-
-        @Override
-        public WindowInsetsCompat replaceSystemWindowInsets(Object insets,
-                Rect systemWindowInsets) {
-            return new WindowInsetsCompat(WindowInsetsCompatApi21.replaceSystemWindowInsets(insets,
-                    systemWindowInsets));
-        }
-    }
-
-    private static final WindowInsetsCompatImpl IMPL;
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new WindowInsetsCompatApi21Impl();
-        } else if (version >= 20) {
-            IMPL = new WindowInsetsCompatApi20Impl();
-        } else {
-            IMPL = new WindowInsetsCompatBaseImpl();
-        }
-    }
-
     private final Object mInsets;
 
-    WindowInsetsCompat(Object insets) {
+    private WindowInsetsCompat(Object insets) {
         mInsets = insets;
     }
 
@@ -273,7 +41,11 @@
      * @param src source from which values are copied
      */
     public WindowInsetsCompat(WindowInsetsCompat src) {
-        mInsets = src == null ? null : IMPL.getSourceWindowInsets(src.mInsets);
+        if (SDK_INT >= 20) {
+            mInsets = src == null ? null : new WindowInsets((WindowInsets) src.mInsets);
+        } else {
+            mInsets = null;
+        }
     }
 
     /**
@@ -286,7 +58,11 @@
      * @return The left system window inset
      */
     public int getSystemWindowInsetLeft() {
-        return IMPL.getSystemWindowInsetLeft(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).getSystemWindowInsetLeft();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -299,7 +75,11 @@
      * @return The top system window inset
      */
     public int getSystemWindowInsetTop() {
-        return IMPL.getSystemWindowInsetTop(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).getSystemWindowInsetTop();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -312,7 +92,11 @@
      * @return The right system window inset
      */
     public int getSystemWindowInsetRight() {
-        return IMPL.getSystemWindowInsetRight(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).getSystemWindowInsetRight();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -325,7 +109,11 @@
      * @return The bottom system window inset
      */
     public int getSystemWindowInsetBottom() {
-        return IMPL.getSystemWindowInsetBottom(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).getSystemWindowInsetBottom();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -338,7 +126,11 @@
      * @return true if any of the system window inset values are nonzero
      */
     public boolean hasSystemWindowInsets() {
-        return IMPL.hasSystemWindowInsets(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).hasSystemWindowInsets();
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -347,7 +139,11 @@
      * @return true if any inset values are nonzero
      */
     public boolean hasInsets() {
-        return IMPL.hasInsets(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).hasInsets();
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -364,7 +160,11 @@
      * @return true if the insets have been fully consumed.
      */
     public boolean isConsumed() {
-        return IMPL.isConsumed(mInsets);
+        if (SDK_INT >= 21) {
+            return ((WindowInsets) mInsets).isConsumed();
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -378,7 +178,11 @@
      * @return True if the window is round
      */
     public boolean isRound() {
-        return IMPL.isRound(mInsets);
+        if (SDK_INT >= 20) {
+            return ((WindowInsets) mInsets).isRound();
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -387,7 +191,11 @@
      * @return A modified copy of this WindowInsets
      */
     public WindowInsetsCompat consumeSystemWindowInsets() {
-        return IMPL.consumeSystemWindowInsets(mInsets);
+        if (SDK_INT >= 20) {
+            return new WindowInsetsCompat(((WindowInsets) mInsets).consumeSystemWindowInsets());
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -401,7 +209,12 @@
      * @return A modified copy of this WindowInsets
      */
     public WindowInsetsCompat replaceSystemWindowInsets(int left, int top, int right, int bottom) {
-        return IMPL.replaceSystemWindowInsets(mInsets, left, top, right, bottom);
+        if (SDK_INT >= 20) {
+            return new WindowInsetsCompat(
+                    ((WindowInsets) mInsets).replaceSystemWindowInsets(left, top, right, bottom));
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -413,7 +226,12 @@
      * @return A modified copy of this WindowInsets
      */
     public WindowInsetsCompat replaceSystemWindowInsets(Rect systemWindowInsets) {
-        return IMPL.replaceSystemWindowInsets(mInsets, systemWindowInsets);
+        if (SDK_INT >= 21) {
+            return new WindowInsetsCompat(
+                    ((WindowInsets) mInsets).replaceSystemWindowInsets(systemWindowInsets));
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -428,10 +246,13 @@
      * @return The top stable inset
      */
     public int getStableInsetTop() {
-        return IMPL.getStableInsetTop(mInsets);
+        if (SDK_INT >= 21) {
+            return ((WindowInsets) mInsets).getStableInsetTop();
+        } else {
+            return 0;
+        }
     }
 
-
     /**
      * Returns the left stable inset in pixels.
      *
@@ -444,7 +265,11 @@
      * @return The left stable inset
      */
     public int getStableInsetLeft() {
-        return IMPL.getStableInsetLeft(mInsets);
+        if (SDK_INT >= 21) {
+            return ((WindowInsets) mInsets).getStableInsetLeft();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -459,7 +284,11 @@
      * @return The right stable inset
      */
     public int getStableInsetRight() {
-        return IMPL.getStableInsetRight(mInsets);
+        if (SDK_INT >= 21) {
+            return ((WindowInsets) mInsets).getStableInsetRight();
+        } else {
+            return 0;
+        }
     }
 
 
@@ -475,7 +304,11 @@
      * @return The bottom stable inset
      */
     public int getStableInsetBottom() {
-        return IMPL.getStableInsetBottom(mInsets);
+        if (SDK_INT >= 21) {
+            return ((WindowInsets) mInsets).getStableInsetBottom();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -490,7 +323,11 @@
      * @return true if any of the stable inset values are nonzero
      */
     public boolean hasStableInsets() {
-        return IMPL.hasStableInsets(mInsets);
+        if (SDK_INT >= 21) {
+            return ((WindowInsets) mInsets).hasStableInsets();
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -499,7 +336,11 @@
      * @return A modified copy of this WindowInsetsCompat
      */
     public WindowInsetsCompat consumeStableInsets() {
-        return IMPL.consumeStableInsets(mInsets);
+        if (SDK_INT >= 21) {
+            return new WindowInsetsCompat(((WindowInsets) mInsets).consumeStableInsets());
+        } else {
+            return null;
+        }
     }
 
     @Override
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java
index 0941fe6..924b482 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java
@@ -17,173 +17,139 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityRecord;
 
 /**
- * Helper for accessing features in {@link AccessibilityEvent}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link AccessibilityEvent}.
  */
 public final class AccessibilityEventCompat {
 
-    static interface AccessibilityEventVersionImpl {
-        int getRecordCount(AccessibilityEvent event);
-        void appendRecord(AccessibilityEvent event, Object record);
-        Object getRecord(AccessibilityEvent event, int index);
-        void setContentChangeTypes(AccessibilityEvent event, int types);
-        int getContentChangeTypes(AccessibilityEvent event);
-        public void setMovementGranularity(AccessibilityEvent event, int granularity);
-        public int getMovementGranularity(AccessibilityEvent event);
-        public void setAction(AccessibilityEvent event, int action);
-        public int getAction(AccessibilityEvent event);
-    }
-
-    static class AccessibilityEventStubImpl implements AccessibilityEventVersionImpl {
-
-        @Override
-        public void appendRecord(AccessibilityEvent event, Object record) {
-
-        }
-
-        @Override
-        public Object getRecord(AccessibilityEvent event, int index) {
-            return null;
-        }
-
-        @Override
+    static class AccessibilityEventCompatBaseImpl {
         public void setContentChangeTypes(AccessibilityEvent event, int types) {
-
         }
 
-        @Override
-        public int getRecordCount(AccessibilityEvent event) {
-            return 0;
-        }
-
-        @Override
         public int getContentChangeTypes(AccessibilityEvent event) {
             return 0;
         }
 
-        @Override
         public void setMovementGranularity(AccessibilityEvent event, int granularity) {
         }
 
-        @Override
         public int getMovementGranularity(AccessibilityEvent event) {
             return 0;
         }
 
-        @Override
         public void setAction(AccessibilityEvent event, int action) {
         }
 
-        @Override
         public int getAction(AccessibilityEvent event) {
             return 0;
         }
     }
 
-    static class AccessibilityEventIcsImpl extends AccessibilityEventStubImpl {
-
-        @Override
-        public void appendRecord(AccessibilityEvent event, Object record) {
-            AccessibilityEventCompatIcs.appendRecord(event, record);
-        }
-
-        @Override
-        public Object getRecord(AccessibilityEvent event, int index) {
-            return AccessibilityEventCompatIcs.getRecord(event, index);
-        }
-
-        @Override
-        public int getRecordCount(AccessibilityEvent event) {
-            return AccessibilityEventCompatIcs.getRecordCount(event);
-        }
-    }
-
-    static class AccessibilityEventJellyBeanImpl extends AccessibilityEventIcsImpl {
+    @RequiresApi(16)
+    static class AccessibilityEventCompatApi16Impl extends AccessibilityEventCompatBaseImpl {
         @Override
         public void setMovementGranularity(AccessibilityEvent event, int granularity) {
-            AccessibilityEventCompatJellyBean.setMovementGranularity(event, granularity);
+            event.setMovementGranularity(granularity);
         }
 
         @Override
         public int getMovementGranularity(AccessibilityEvent event) {
-            return AccessibilityEventCompatJellyBean.getMovementGranularity(event);
+            return event.getMovementGranularity();
         }
 
         @Override
         public void setAction(AccessibilityEvent event, int action) {
-            AccessibilityEventCompatJellyBean.setAction(event, action);
+            event.setAction(action);
         }
 
         @Override
         public int getAction(AccessibilityEvent event) {
-            return AccessibilityEventCompatJellyBean.getAction(event);
+            return event.getAction();
         }
     }
 
-    static class AccessibilityEventKitKatImpl extends AccessibilityEventJellyBeanImpl {
+    @RequiresApi(19)
+    static class AccessibilityEventCompatApi19Impl extends AccessibilityEventCompatApi16Impl {
 
         @Override
         public void setContentChangeTypes(AccessibilityEvent event, int types) {
-            AccessibilityEventCompatKitKat.setContentChangeTypes(event, types);
+            event.setContentChangeTypes(types);
         }
 
         @Override
         public int getContentChangeTypes(AccessibilityEvent event) {
-            return AccessibilityEventCompatKitKat.getContentChangeTypes(event);
+            return event.getContentChangeTypes();
         }
     }
 
-    private final static AccessibilityEventVersionImpl IMPL;
+    private static final AccessibilityEventCompatBaseImpl IMPL;
 
     static {
         if (Build.VERSION.SDK_INT >= 19) { // KitKat
-            IMPL = new AccessibilityEventKitKatImpl();
+            IMPL = new AccessibilityEventCompatApi19Impl();
         } else if (Build.VERSION.SDK_INT >= 16) { // Jellybean
-            IMPL = new AccessibilityEventJellyBeanImpl();
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new AccessibilityEventIcsImpl();
+            IMPL = new AccessibilityEventCompatApi16Impl();
         } else {
-            IMPL = new AccessibilityEventStubImpl();
+            IMPL = new AccessibilityEventCompatBaseImpl();
         }
     }
 
     /**
      * Represents the event of a hover enter over a {@link android.view.View}.
+     * @deprecated Use {@link  AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} directly.
      */
-    public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080;
+    @Deprecated
+    public static final int TYPE_VIEW_HOVER_ENTER = AccessibilityEvent.TYPE_VIEW_HOVER_ENTER;
 
     /**
      * Represents the event of a hover exit over a {@link android.view.View}.
+     * @deprecated Use {@link  AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} directly.
      */
-    public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100;
+    @Deprecated
+    public static final int TYPE_VIEW_HOVER_EXIT = AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
 
     /**
      * Represents the event of starting a touch exploration gesture.
+     * @deprecated Use {@link  AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START} directly.
      */
-    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200;
+    @Deprecated
+    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START =
+            AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
 
     /**
      * Represents the event of ending a touch exploration gesture.
+     * @deprecated Use {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} directly.
      */
-    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
+    @Deprecated
+    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END =
+            AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
 
     /**
      * Represents the event of changing the content of a window.
+     * @deprecated Use {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} directly.
      */
-    public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
+    @Deprecated
+    public static final int TYPE_WINDOW_CONTENT_CHANGED =
+            AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
     /**
      * Represents the event of scrolling a view.
+     * @deprecated Use {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} directly.
      */
-    public static final int TYPE_VIEW_SCROLLED = 0x00001000;
+    @Deprecated
+    public static final int TYPE_VIEW_SCROLLED = AccessibilityEvent.TYPE_VIEW_SCROLLED;
 
     /**
      * Represents the event of changing the selection in an {@link android.widget.EditText}.
+     * @deprecated Use {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} directly.
      */
-    public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000;
+    @Deprecated
+    public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED =
+            AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED;
 
     /**
      * Represents the event of an application making an announcement.
@@ -274,19 +240,22 @@
      * @see AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
      * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
      * @see AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
-     * @see #TYPE_VIEW_HOVER_ENTER
-     * @see #TYPE_VIEW_HOVER_EXIT
-     * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START
-     * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END
-     * @see #TYPE_WINDOW_CONTENT_CHANGED
-     * @see #TYPE_VIEW_SCROLLED
-     * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED
+     * @see AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
+     * @see AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
+     * @see AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
+     * @see AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
+     * @see AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
+     * @see AccessibilityEvent#TYPE_VIEW_SCROLLED
+     * @see AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
      * @see #TYPE_ANNOUNCEMENT
      * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
      * @see #TYPE_GESTURE_DETECTION_START
      * @see #TYPE_GESTURE_DETECTION_END
      * @see #TYPE_TOUCH_INTERACTION_START
      * @see #TYPE_TOUCH_INTERACTION_END
+     * @see #TYPE_WINDOWS_CHANGED
+     * @see #TYPE_VIEW_CONTEXT_CLICKED
+     * @see #TYPE_ASSIST_READING_CONTEXT
      */
     public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
 
@@ -301,9 +270,12 @@
      * Gets the number of records contained in the event.
      *
      * @return The number of records.
+     *
+     * @deprecated Use {@link AccessibilityEvent#getRecordCount()} directly.
      */
+    @Deprecated
     public static int getRecordCount(AccessibilityEvent event) {
-        return IMPL.getRecordCount(event);
+        return event.getRecordCount();
     }
 
     /**
@@ -313,9 +285,12 @@
      * @param record The record to append.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityEvent#appendRecord(AccessibilityRecord)} directly.
      */
+    @Deprecated
     public static void appendRecord(AccessibilityEvent event, AccessibilityRecordCompat record) {
-        IMPL.appendRecord(event, record.getImpl());
+        event.appendRecord((AccessibilityRecord) record.getImpl());
     }
 
     /**
@@ -323,9 +298,12 @@
      *
      * @param index The index.
      * @return The record at the specified index.
+     *
+     * @deprecated Use {@link AccessibilityEvent#getRecord(int)} directly.
      */
+    @Deprecated
     public static AccessibilityRecordCompat getRecord(AccessibilityEvent event, int index) {
-        return new AccessibilityRecordCompat(IMPL.getRecord(event, index));
+        return new AccessibilityRecordCompat(event.getRecord(index));
     }
 
     /**
@@ -340,7 +318,10 @@
      *
      * @param event The from which to create a record.
      * @return An {@link AccessibilityRecordCompat}.
+     *
+     * @deprecated Use the {@link AccessibilityEvent} directly as {@link AccessibilityRecord}.
      */
+    @Deprecated
     public static AccessibilityRecordCompat asRecord(AccessibilityEvent event) {
         return new AccessibilityRecordCompat(event);
     }
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
index 544091a..5767f74 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
@@ -18,182 +18,16 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.os.Build;
-import android.support.v4.view.accessibility.AccessibilityManagerCompatIcs.AccessibilityStateChangeListenerBridge;
-import android.support.v4.view.accessibility.AccessibilityManagerCompatIcs.AccessibilityStateChangeListenerWrapper;
-import android.support.v4.view.accessibility.AccessibilityManagerCompatKitKat.TouchExplorationStateChangeListenerBridge;
-import android.support.v4.view.accessibility.AccessibilityManagerCompatKitKat.TouchExplorationStateChangeListenerWrapper;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.view.accessibility.AccessibilityManager;
 
-import java.util.Collections;
 import java.util.List;
 
 /**
- * Helper for accessing features in {@link AccessibilityManager}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link AccessibilityManager}.
  */
 public final class AccessibilityManagerCompat {
-
-    interface AccessibilityManagerVersionImpl {
-        AccessibilityStateChangeListenerWrapper newAccessibilityStateChangeListener(
-                AccessibilityStateChangeListener listener);
-        boolean addAccessibilityStateChangeListener(AccessibilityManager manager,
-                AccessibilityStateChangeListener listener);
-        boolean removeAccessibilityStateChangeListener(AccessibilityManager manager,
-                AccessibilityStateChangeListener listener);
-        List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
-                AccessibilityManager manager,int feedbackTypeFlags);
-        List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
-                AccessibilityManager manager);
-        boolean isTouchExplorationEnabled(AccessibilityManager manager);
-        TouchExplorationStateChangeListenerWrapper newTouchExplorationStateChangeListener(
-                TouchExplorationStateChangeListener listener);
-        boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,
-                TouchExplorationStateChangeListener listener);
-        boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,
-                TouchExplorationStateChangeListener listener);
-    }
-
-    static class AccessibilityManagerStubImpl implements AccessibilityManagerVersionImpl {
-        @Override
-        public AccessibilityStateChangeListenerWrapper newAccessibilityStateChangeListener(
-                AccessibilityStateChangeListener listener) {
-            return null;
-        }
-
-        @Override
-        public boolean addAccessibilityStateChangeListener(AccessibilityManager manager,
-                AccessibilityStateChangeListener listener) {
-            return false;
-        }
-
-        @Override
-        public boolean removeAccessibilityStateChangeListener(AccessibilityManager manager,
-                AccessibilityStateChangeListener listener) {
-            return false;
-        }
-
-        @Override
-        public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
-                AccessibilityManager manager, int feedbackTypeFlags) {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
-                AccessibilityManager manager) {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public boolean isTouchExplorationEnabled(AccessibilityManager manager) {
-            return false;
-        }
-
-        @Override
-        public TouchExplorationStateChangeListenerWrapper newTouchExplorationStateChangeListener(
-                TouchExplorationStateChangeListener listener) {
-            return null;
-        }
-
-        @Override
-        public boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,
-                TouchExplorationStateChangeListener listener) {
-            return false;
-        }
-
-        @Override
-        public boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,
-                TouchExplorationStateChangeListener listener) {
-            return false;
-        }
-    }
-
-    static class AccessibilityManagerIcsImpl extends AccessibilityManagerStubImpl {
-        @Override
-        public AccessibilityStateChangeListenerWrapper newAccessibilityStateChangeListener(
-                final AccessibilityStateChangeListener listener) {
-            return new AccessibilityStateChangeListenerWrapper(listener,
-                    new AccessibilityStateChangeListenerBridge() {
-                        @Override
-                        public void onAccessibilityStateChanged(boolean enabled) {
-                            listener.onAccessibilityStateChanged(enabled);
-                        }
-                    });
-        }
-
-        @Override
-        public boolean addAccessibilityStateChangeListener(AccessibilityManager manager,
-                AccessibilityStateChangeListener listener) {
-            return AccessibilityManagerCompatIcs.addAccessibilityStateChangeListener(manager,
-                    newAccessibilityStateChangeListener(listener));
-        }
-
-        @Override
-        public boolean removeAccessibilityStateChangeListener(AccessibilityManager manager,
-                AccessibilityStateChangeListener listener) {
-            return AccessibilityManagerCompatIcs.removeAccessibilityStateChangeListener(manager,
-                    newAccessibilityStateChangeListener(listener));
-        }
-
-        @Override
-        public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
-                AccessibilityManager manager, int feedbackTypeFlags) {
-            return AccessibilityManagerCompatIcs.getEnabledAccessibilityServiceList(manager,
-                    feedbackTypeFlags);
-        }
-
-        @Override
-        public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
-                AccessibilityManager manager) {
-            return AccessibilityManagerCompatIcs.getInstalledAccessibilityServiceList(manager);
-        }
-
-        @Override
-        public boolean isTouchExplorationEnabled(AccessibilityManager manager) {
-            return AccessibilityManagerCompatIcs.isTouchExplorationEnabled(manager);
-        }
-    }
-
-    static class AccessibilityManagerKitKatImpl extends AccessibilityManagerIcsImpl {
-        @Override
-        public TouchExplorationStateChangeListenerWrapper newTouchExplorationStateChangeListener(
-                final TouchExplorationStateChangeListener listener) {
-            return new TouchExplorationStateChangeListenerWrapper(listener,
-                    new TouchExplorationStateChangeListenerBridge() {
-                        @Override
-                        public void onTouchExplorationStateChanged(boolean enabled) {
-                            listener.onTouchExplorationStateChanged(enabled);
-                        }
-                    });
-        }
-
-        @Override
-        public boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,
-                TouchExplorationStateChangeListener listener) {
-            return AccessibilityManagerCompatKitKat.addTouchExplorationStateChangeListener(
-                    manager, newTouchExplorationStateChangeListener(listener));
-        }
-
-        @Override
-        public boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,
-                TouchExplorationStateChangeListener listener) {
-            return AccessibilityManagerCompatKitKat.removeTouchExplorationStateChangeListener(
-                    manager, newTouchExplorationStateChangeListener(listener));
-        }
-    }
-
-    static {
-        if (Build.VERSION.SDK_INT >= 19) { // KitKat
-            IMPL = new AccessibilityManagerKitKatImpl();
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new AccessibilityManagerIcsImpl();
-        } else {
-            IMPL = new AccessibilityManagerStubImpl();
-        }
-    }
-
-    private static final AccessibilityManagerVersionImpl IMPL;
-
     /**
      * Registers an {@link AccessibilityManager.AccessibilityStateChangeListener} for changes in
      * the global accessibility state of the system.
@@ -201,10 +35,18 @@
      * @param manager The accessibility manager.
      * @param listener The listener.
      * @return True if successfully registered.
+     *
+     * @deprecated Use {@link AccessibilityManager#addAccessibilityStateChangeListener(
+     *             AccessibilityManager.AccessibilityStateChangeListener)} directly.
      */
+    @Deprecated
     public static boolean addAccessibilityStateChangeListener(AccessibilityManager manager,
             AccessibilityStateChangeListener listener) {
-        return IMPL.addAccessibilityStateChangeListener(manager, listener);
+        if (listener == null) {
+            return false;
+        }
+        return manager.addAccessibilityStateChangeListener(
+                new AccessibilityStateChangeListenerWrapper(listener));
     }
 
     /**
@@ -213,10 +55,51 @@
      * @param manager The accessibility manager.
      * @param listener The listener.
      * @return True if successfully unregistered.
+     *
+     * @deprecated Use {@link AccessibilityManager#removeAccessibilityStateChangeListener(
+     *             AccessibilityManager.AccessibilityStateChangeListener)} directly.
      */
+    @Deprecated
     public static boolean removeAccessibilityStateChangeListener(AccessibilityManager manager,
             AccessibilityStateChangeListener listener) {
-        return IMPL.removeAccessibilityStateChangeListener(manager, listener);
+        if (listener == null) {
+            return false;
+        }
+        return manager.removeAccessibilityStateChangeListener(
+                new AccessibilityStateChangeListenerWrapper(listener));
+    }
+
+    private static class AccessibilityStateChangeListenerWrapper
+            implements AccessibilityManager.AccessibilityStateChangeListener {
+        AccessibilityStateChangeListener mListener;
+
+        AccessibilityStateChangeListenerWrapper(
+                @NonNull AccessibilityStateChangeListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public int hashCode() {
+            return mListener.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            AccessibilityStateChangeListenerWrapper other =
+                    (AccessibilityStateChangeListenerWrapper) o;
+            return mListener.equals(other.mListener);
+        }
+
+        @Override
+        public void onAccessibilityStateChanged(boolean enabled) {
+            mListener.onAccessibilityStateChanged(enabled);
+        }
     }
 
     /**
@@ -224,10 +107,13 @@
      *
      * @param manager The accessibility manager.
      * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+     *
+     * @deprecated Use {@link AccessibilityManager#getInstalledAccessibilityServiceList()} directly.
      */
+    @Deprecated
     public static List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
             AccessibilityManager manager) {
-        return IMPL.getInstalledAccessibilityServiceList(manager);
+        return manager.getInstalledAccessibilityServiceList();
     }
 
     /**
@@ -243,10 +129,14 @@
      * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
      * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
      * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+     *
+     * @deprecated Use {@link AccessibilityManager#getEnabledAccessibilityServiceList(int)}
+     * directly.
      */
+    @Deprecated
     public static List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
             AccessibilityManager manager, int feedbackTypeFlags) {
-        return IMPL.getEnabledAccessibilityServiceList(manager, feedbackTypeFlags);
+        return manager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
     }
 
     /**
@@ -254,9 +144,12 @@
      *
      * @param manager The accessibility manager.
      * @return True if touch exploration is enabled, false otherwise.
+     *
+     * @deprecated Use {@link AccessibilityManager#isTouchExplorationEnabled()} directly.
      */
+    @Deprecated
     public static boolean isTouchExplorationEnabled(AccessibilityManager manager) {
-        return IMPL.isTouchExplorationEnabled(manager);
+        return manager.isTouchExplorationEnabled();
     }
 
     /**
@@ -268,7 +161,15 @@
      */
     public static boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,
             TouchExplorationStateChangeListener listener) {
-        return IMPL.addTouchExplorationStateChangeListener(manager, listener);
+        if (Build.VERSION.SDK_INT >= 19) {
+            if (listener == null) {
+                return false;
+            }
+            return manager.addTouchExplorationStateChangeListener(
+                    new TouchExplorationStateChangeListenerWrapper(listener));
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -279,12 +180,56 @@
      */
     public static boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,
             TouchExplorationStateChangeListener listener) {
-        return IMPL.removeTouchExplorationStateChangeListener(manager, listener);
+        if (Build.VERSION.SDK_INT >= 19) {
+            if (listener == null) {
+                return false;
+            }
+            return manager.removeTouchExplorationStateChangeListener(
+                    new TouchExplorationStateChangeListenerWrapper(listener));
+        } else {
+            return false;
+        }
+    }
+
+    @RequiresApi(19)
+    private static class TouchExplorationStateChangeListenerWrapper
+            implements AccessibilityManager.TouchExplorationStateChangeListener {
+        final TouchExplorationStateChangeListener mListener;
+
+        TouchExplorationStateChangeListenerWrapper(
+                @NonNull TouchExplorationStateChangeListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public int hashCode() {
+            return mListener.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TouchExplorationStateChangeListenerWrapper other =
+                    (TouchExplorationStateChangeListenerWrapper) o;
+            return mListener.equals(other.mListener);
+        }
+
+        @Override
+        public void onTouchExplorationStateChanged(boolean enabled) {
+            mListener.onTouchExplorationStateChanged(enabled);
+        }
     }
 
     /**
      * Listener for the accessibility state.
-     * @deprecated Use {@link AccessibilityStateChangeListener} instead.
+     *
+     * @deprecated Use {@link AccessibilityManager.AccessibilityStateChangeListener} directly
+     * instead of this listener.
      */
     @Deprecated
     public static abstract class AccessibilityStateChangeListenerCompat
@@ -293,13 +238,20 @@
 
     /**
      * Listener for the accessibility state.
+     *
+     * @deprecated Use {@link AccessibilityManager.AccessibilityStateChangeListener} directly
+     * instead of this listener.
      */
+    @Deprecated
     public interface AccessibilityStateChangeListener {
         /**
          * Called back on change in the accessibility state.
          *
          * @param enabled Whether accessibility is enabled.
+         *
+         * @deprecated Use {@link AccessibilityManager.AccessibilityStateChangeListener} directly.
          */
+        @Deprecated
         void onAccessibilityStateChanged(boolean enabled);
     }
 
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
index 0b00094..bed729a 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -21,20 +21,23 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
 import android.support.v4.view.ViewCompat;
 import android.text.InputType;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * Helper for accessing {@link android.view.accessibility.AccessibilityNodeInfo}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link android.view.accessibility.AccessibilityNodeInfo} in a backwards
+ * compatible fashion.
  */
 public class AccessibilityNodeInfoCompat {
 
@@ -723,1619 +726,911 @@
         }
     }
 
-    interface AccessibilityNodeInfoImpl {
-        Object newAccessibilityAction(int actionId, CharSequence label);
-        Object obtain();
-        Object obtain(View source);
-        Object obtain(Object info);
-        Object obtain(View root, int virtualDescendantId);
-        void setSource(Object info, View source);
-        void setSource(Object info, View root, int virtualDescendantId);
-        Object findFocus(Object info, int focus);
-        Object focusSearch(Object info, int direction);
-        int getWindowId(Object info);
-        int getChildCount(Object info);
-        Object getChild(Object info, int index);
-        void addChild(Object info, View child);
-        void addChild(Object info, View child, int virtualDescendantId);
-        boolean removeChild(Object info, View child);
-        boolean removeChild(Object info, View root, int virtualDescendantId);
-        int getActions(Object info);
-        void addAction(Object info, int action);
-        void addAction(Object info, Object action);
-        boolean removeAction(Object info, Object action);
-        int getAccessibilityActionId(Object action);
-        CharSequence getAccessibilityActionLabel(Object action);
-        boolean performAction(Object info, int action);
-        boolean performAction(Object info, int action, Bundle arguments);
-        void setMovementGranularities(Object info, int granularities);
-        int getMovementGranularities(Object info);
-        List<Object> findAccessibilityNodeInfosByText(Object info, String text);
-        Object getParent(Object info);
-        void setParent(Object info, View root, int virtualDescendantId);
-        void setParent(Object info, View parent);
-        void getBoundsInParent(Object info, Rect outBounds);
-        void setBoundsInParent(Object info, Rect bounds);
-        void getBoundsInScreen(Object info, Rect outBounds);
-        void setBoundsInScreen(Object info, Rect bounds);
-        boolean isCheckable(Object info);
-        void setCheckable(Object info, boolean checkable);
-        boolean isChecked(Object info);
-        void setChecked(Object info, boolean checked);
-        boolean isFocusable(Object info);
-        void setFocusable(Object info, boolean focusable);
-        boolean isFocused(Object info);
-        void setFocused(Object info, boolean focused);
-        boolean isVisibleToUser(Object info);
-        void setVisibleToUser(Object info, boolean visibleToUser);
-        boolean isAccessibilityFocused(Object info);
-        void setAccessibilityFocused(Object info, boolean focused);
-        boolean isSelected(Object info);
-        void setSelected(Object info, boolean selected);
-        boolean isClickable(Object info);
-        void setClickable(Object info, boolean clickable);
-        boolean isLongClickable(Object info);
-        void setLongClickable(Object info, boolean longClickable);
-        boolean isEnabled(Object info);
-        void setEnabled(Object info, boolean enabled);
-        boolean isPassword(Object info);
-        void setPassword(Object info, boolean password);
-        boolean isScrollable(Object info);
-        void setScrollable(Object info, boolean scrollable);
-        CharSequence getPackageName(Object info);
-        void setPackageName(Object info, CharSequence packageName);
-        CharSequence getClassName(Object info);
-        void setClassName(Object info, CharSequence className);
-        CharSequence getText(Object info);
-        void setText(Object info, CharSequence text);
-        CharSequence getContentDescription(Object info);
-        void setContentDescription(Object info, CharSequence contentDescription);
-        void recycle(Object info);
-        String getViewIdResourceName(Object info);
-        void setViewIdResourceName(Object info, String viewId);
-        int getLiveRegion(Object info);
-        void setLiveRegion(Object info, int mode);
-        Object getCollectionInfo(Object info);
-        void setCollectionInfo(Object info, Object collectionInfo);
-        Object getCollectionItemInfo(Object info);
-        void setCollectionItemInfo(Object info, Object collectionItemInfo);
-        Object getRangeInfo(Object info);
-        void setRangeInfo(Object info, Object rangeInfo);
-        List<Object> getActionList(Object info);
-        Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical,
-                int selectionMode);
-        Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical);
-        int getCollectionInfoColumnCount(Object info);
-        int getCollectionInfoRowCount(Object info);
-        boolean isCollectionInfoHierarchical(Object info);
-        int getCollectionInfoSelectionMode(Object info);
-        Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
-                int columnSpan, boolean heading, boolean selected);
-        Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
-                int columnSpan, boolean heading);
-        int getCollectionItemColumnIndex(Object info);
-        int getCollectionItemColumnSpan(Object info);
-        int getCollectionItemRowIndex(Object info);
-        int getCollectionItemRowSpan(Object info);
-        boolean isCollectionItemHeading(Object info);
-        boolean isCollectionItemSelected(Object info);
-        Object obtainRangeInfo(int type, float min, float max, float current);
-        Object getTraversalBefore(Object info);
-        void setTraversalBefore(Object info, View view);
-        void setTraversalBefore(Object info, View root, int virtualDescendantId);
-        Object getTraversalAfter(Object info);
-        void setTraversalAfter(Object info, View view);
-        void setTraversalAfter(Object info, View root, int virtualDescendantId);
-        void setContentInvalid(Object info, boolean contentInvalid);
-        boolean isContentInvalid(Object info);
-        void setError(Object info, CharSequence error);
-        CharSequence getError(Object info);
-        void setLabelFor(Object info, View labeled);
-        void setLabelFor(Object info, View root, int virtualDescendantId);
-        Object getLabelFor(Object info);
-        void setLabeledBy(Object info, View labeled);
-        void setLabeledBy(Object info, View root, int virtualDescendantId);
-        Object getLabeledBy(Object info);
-        boolean canOpenPopup(Object info);
-        void setCanOpenPopup(Object info, boolean opensPopup);
-        List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId);
-        Bundle getExtras(Object info);
-        int getInputType(Object info);
-        void setInputType(Object info, int inputType);
-        void setMaxTextLength(Object info, int max);
-        int getMaxTextLength(Object info);
-        void setTextSelection(Object info, int start, int end);
-        int getTextSelectionStart(Object info);
-        int getTextSelectionEnd(Object info);
-        Object getWindow(Object info);
-        boolean isDismissable(Object info);
-        void setDismissable(Object info, boolean dismissable);
-        boolean isEditable(Object info);
-        void setEditable(Object info, boolean editable);
-        int getDrawingOrder(Object info);
-        void setDrawingOrder(Object info, int drawingOrderInParent);
-        boolean isImportantForAccessibility(Object info);
-        void setImportantForAccessibility(Object info, boolean importantForAccessibility);
-        boolean isMultiLine(Object info);
-        void setMultiLine(Object info, boolean multiLine);
-        boolean refresh(Object info);
-        CharSequence getRoleDescription(Object info);
-        void setRoleDescription(Object info, CharSequence roleDescription);
-        Object getActionScrollToPosition();
-        Object getActionSetProgress();
-        boolean isContextClickable(Object info);
-        void setContextClickable(Object info, boolean contextClickable);
-        Object getActionShowOnScreen();
-        Object getActionScrollUp();
-        Object getActionScrollDown();
-        Object getActionScrollLeft();
-        Object getActionScrollRight();
-        Object getActionContextClick();
-    }
-
-    static class AccessibilityNodeInfoStubImpl implements AccessibilityNodeInfoImpl {
-        @Override
+    static class AccessibilityNodeInfoBaseImpl {
         public Object newAccessibilityAction(int actionId, CharSequence label) {
             return null;
         }
 
-        @Override
-        public Object obtain() {
+        public AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
             return null;
         }
 
-        @Override
-        public Object obtain(View source) {
-            return null;
+        public void addAction(AccessibilityNodeInfo info, Object action) {
         }
 
-        @Override
-        public Object obtain(View root, int virtualDescendantId) {
-            return null;
-        }
-
-        @Override
-        public Object obtain(Object info) {
-            return null;
-        }
-
-        @Override
-        public void addAction(Object info, int action) {
-
-        }
-
-        @Override
-        public void addAction(Object info, Object action) {
-
-        }
-
-        @Override
-        public boolean removeAction(Object info, Object action) {
+        public boolean removeAction(AccessibilityNodeInfo info, Object action) {
             return false;
         }
 
-        @Override
         public int getAccessibilityActionId(Object action) {
             return 0;
         }
 
-        @Override
         public CharSequence getAccessibilityActionLabel(Object action) {
             return null;
         }
 
-        @Override
-        public void addChild(Object info, View child) {
-
+        public void addChild(AccessibilityNodeInfo info, View child, int virtualDescendantId) {
         }
 
-        @Override
-        public void addChild(Object info, View child, int virtualDescendantId) {
-
-        }
-
-        @Override
-        public boolean removeChild(Object info, View child) {
+        public boolean removeChild(AccessibilityNodeInfo info, View child) {
             return false;
         }
 
-        @Override
-        public boolean removeChild(Object info, View root, int virtualDescendantId) {
+        public boolean removeChild(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
             return false;
         }
 
-        @Override
-        public List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
-            return Collections.emptyList();
+        public boolean isVisibleToUser(AccessibilityNodeInfo info) {
+            return false;
         }
 
-        @Override
-        public int getActions(Object info) {
+        public boolean isAccessibilityFocused(AccessibilityNodeInfo info) {
+            return false;
+        }
+
+        public boolean performAction(AccessibilityNodeInfo info, int action, Bundle arguments) {
+            return false;
+        }
+
+        public void setMovementGranularities(AccessibilityNodeInfo info, int granularities) {
+        }
+
+        public int getMovementGranularities(AccessibilityNodeInfo info) {
             return 0;
         }
 
-        @Override
-        public void getBoundsInParent(Object info, Rect outBounds) {
-
+        public void setVisibleToUser(AccessibilityNodeInfo info, boolean visibleToUser) {
         }
 
-        @Override
-        public void getBoundsInScreen(Object info, Rect outBounds) {
-
+        public void setAccessibilityFocused(AccessibilityNodeInfo info, boolean focused) {
         }
 
-        @Override
-        public Object getChild(Object info, int index) {
+        public void setSource(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+        }
+
+        public Object findFocus(AccessibilityNodeInfo info, int focus) {
             return null;
         }
 
-        @Override
-        public int getChildCount(Object info) {
-            return 0;
-        }
-
-        @Override
-        public CharSequence getClassName(Object info) {
+        public Object focusSearch(AccessibilityNodeInfo info, int direction) {
             return null;
         }
 
-        @Override
-        public CharSequence getContentDescription(Object info) {
+        public void setParent(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+        }
+
+        public String getViewIdResourceName(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public CharSequence getPackageName(Object info) {
-            return null;
+        public void setViewIdResourceName(AccessibilityNodeInfo info, String viewId) {
         }
 
-        @Override
-        public Object getParent(Object info) {
-            return null;
-        }
-
-        @Override
-        public CharSequence getText(Object info) {
-            return null;
-        }
-
-        @Override
-        public int getWindowId(Object info) {
-            return 0;
-        }
-
-        @Override
-        public boolean isCheckable(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isChecked(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isClickable(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isFocusable(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isFocused(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isVisibleToUser(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isAccessibilityFocused(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isLongClickable(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isPassword(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isScrollable(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean isSelected(Object info) {
-            return false;
-        }
-
-        @Override
-        public boolean performAction(Object info, int action) {
-            return false;
-        }
-
-        @Override
-        public boolean performAction(Object info, int action, Bundle arguments) {
-            return false;
-        }
-
-        @Override
-        public void setMovementGranularities(Object info, int granularities) {
-
-        }
-
-        @Override
-        public int getMovementGranularities(Object info) {
-            return 0;
-        }
-
-        @Override
-        public void setBoundsInParent(Object info, Rect bounds) {
-
-        }
-
-        @Override
-        public void setBoundsInScreen(Object info, Rect bounds) {
-
-        }
-
-        @Override
-        public void setCheckable(Object info, boolean checkable) {
-
-        }
-
-        @Override
-        public void setChecked(Object info, boolean checked) {
-
-        }
-
-        @Override
-        public void setClassName(Object info, CharSequence className) {
-
-        }
-
-        @Override
-        public void setClickable(Object info, boolean clickable) {
-
-        }
-
-        @Override
-        public void setContentDescription(Object info, CharSequence contentDescription) {
-
-        }
-
-        @Override
-        public void setEnabled(Object info, boolean enabled) {
-
-        }
-
-        @Override
-        public void setFocusable(Object info, boolean focusable) {
-
-        }
-
-        @Override
-        public void setFocused(Object info, boolean focused) {
-
-        }
-
-        @Override
-        public void setVisibleToUser(Object info, boolean visibleToUser) {
-
-        }
-
-        @Override
-        public void setAccessibilityFocused(Object info, boolean focused) {
-
-        }
-
-        @Override
-        public void setLongClickable(Object info, boolean longClickable) {
-
-        }
-
-        @Override
-        public void setPackageName(Object info, CharSequence packageName) {
-
-        }
-
-        @Override
-        public void setParent(Object info, View parent) {
-
-        }
-
-        @Override
-        public void setPassword(Object info, boolean password) {
-
-        }
-
-        @Override
-        public void setScrollable(Object info, boolean scrollable) {
-
-        }
-
-        @Override
-        public void setSelected(Object info, boolean selected) {
-
-        }
-
-        @Override
-        public void setSource(Object info, View source) {
-
-        }
-
-        @Override
-        public void setSource(Object info, View root, int virtualDescendantId) {
-
-        }
-
-        @Override
-        public Object findFocus(Object info, int focus) {
-            return null;
-        }
-
-        @Override
-        public Object focusSearch(Object info, int direction) {
-            return null;
-        }
-
-        @Override
-        public void setText(Object info, CharSequence text) {
-
-        }
-
-        @Override
-        public void recycle(Object info) {
-
-        }
-
-        @Override
-        public void setParent(Object info, View root, int virtualDescendantId) {
-
-        }
-
-        @Override
-        public String getViewIdResourceName(Object info) {
-            return null;
-        }
-
-        @Override
-        public void setViewIdResourceName(Object info, String viewId) {
-
-        }
-
-        @Override
-        public int getLiveRegion(Object info) {
+        public int getLiveRegion(AccessibilityNodeInfo info) {
             return ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE;
         }
 
-        @Override
-        public void setLiveRegion(Object info, int mode) {
+        public void setLiveRegion(AccessibilityNodeInfo info, int mode) {
             // No-op
         }
 
-        @Override
-        public Object getCollectionInfo(Object info) {
+        public Object getCollectionInfo(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setCollectionInfo(Object info, Object collectionInfo) {
+        public void setCollectionInfo(AccessibilityNodeInfo info, Object collectionInfo) {
         }
 
-        @Override
-        public Object getCollectionItemInfo(Object info) {
+        public Object getCollectionItemInfo(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setCollectionItemInfo(Object info, Object collectionItemInfo) {
+        public void setCollectionItemInfo(AccessibilityNodeInfo info, Object collectionItemInfo) {
         }
 
-        @Override
-        public Object getRangeInfo(Object info) {
+        public Object getRangeInfo(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setRangeInfo(Object info, Object rangeInfo) {
+        public void setRangeInfo(AccessibilityNodeInfo info, Object rangeInfo) {
         }
 
-        @Override
-        public List<Object> getActionList(Object info) {
+        public List<Object> getActionList(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
         public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical,
                 int selectionMode) {
             return null;
         }
 
-        @Override
         public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical) {
             return null;
         }
 
-        @Override
         public int getCollectionInfoColumnCount(Object info) {
             return 0;
         }
 
-        @Override
         public int getCollectionInfoRowCount(Object info) {
             return 0;
         }
 
-        @Override
         public boolean isCollectionInfoHierarchical(Object info) {
             return false;
         }
 
-        @Override
         public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
                 int columnSpan, boolean heading, boolean selected) {
             return null;
         }
 
-        @Override
         public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
                 int columnSpan, boolean heading) {
             return null;
         }
 
-        @Override
         public int getCollectionItemColumnIndex(Object info) {
             return 0;
         }
 
-        @Override
         public int getCollectionItemColumnSpan(Object info) {
             return 0;
         }
 
-        @Override
         public int getCollectionItemRowIndex(Object info) {
             return 0;
         }
 
-        @Override
         public int getCollectionItemRowSpan(Object info) {
             return 0;
         }
 
-        @Override
         public boolean isCollectionItemHeading(Object info) {
             return false;
         }
 
-        @Override
         public boolean isCollectionItemSelected(Object info) {
             return false;
         }
 
-        @Override
         public Object obtainRangeInfo(int type, float min, float max, float current) {
             return null;
         }
 
-        @Override
-        public Object getTraversalBefore(Object info) {
+        public Object getTraversalBefore(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setTraversalBefore(Object info, View view) {
+        public void setTraversalBefore(AccessibilityNodeInfo info, View view) {
         }
 
-        @Override
-        public void setTraversalBefore(Object info, View root, int virtualDescendantId) {
+        public void setTraversalBefore(AccessibilityNodeInfo info, View root,
+                int virtualDescendantId) {
         }
 
-        @Override
-        public Object getTraversalAfter(Object info) {
+        public Object getTraversalAfter(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setTraversalAfter(Object info, View view) {
+        public void setTraversalAfter(AccessibilityNodeInfo info, View view) {
         }
 
-        @Override
-        public void setTraversalAfter(Object info, View root, int virtualDescendantId) {
+        public void setTraversalAfter(AccessibilityNodeInfo info, View root,
+                int virtualDescendantId) {
         }
 
-        @Override
-        public void setContentInvalid(Object info, boolean contentInvalid) {
+        public void setContentInvalid(AccessibilityNodeInfo info, boolean contentInvalid) {
         }
 
-        @Override
-        public boolean isContentInvalid(Object info) {
+        public boolean isContentInvalid(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public void setError(Object info, CharSequence error) {
+        public void setError(AccessibilityNodeInfo info, CharSequence error) {
         }
 
-        @Override
-        public CharSequence getError(Object info) {
+        public CharSequence getError(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setLabelFor(Object info, View labeled) {
+        public void setLabelFor(AccessibilityNodeInfo info, View labeled) {
         }
 
-        @Override
-        public void setLabelFor(Object info, View root, int virtualDescendantId) {
+        public void setLabelFor(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
         }
 
-        @Override
-        public Object getLabelFor(Object info) {
+        public Object getLabelFor(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setLabeledBy(Object info, View labeled) {
+        public void setLabeledBy(AccessibilityNodeInfo info, View labeled) {
         }
 
-        @Override
-        public void setLabeledBy(Object info, View root, int virtualDescendantId) {
+        public void setLabeledBy(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
         }
 
-        @Override
-        public Object getLabeledBy(Object info){
+        public Object getLabeledBy(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public boolean canOpenPopup(Object info) {
+        public boolean canOpenPopup(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public void setCanOpenPopup(Object info, boolean opensPopup) {
+        public void setCanOpenPopup(AccessibilityNodeInfo info, boolean opensPopup) {
         }
 
-        @Override
-        public List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId) {
-            return  Collections.emptyList();
+        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(
+                AccessibilityNodeInfo info, String viewId) {
+            return Collections.emptyList();
         }
 
-        @Override
-        public Bundle getExtras(Object info) {
+        public Bundle getExtras(AccessibilityNodeInfo info) {
             return new Bundle();
         }
 
-        @Override
-        public int getInputType(Object info) {
+        public int getInputType(AccessibilityNodeInfo info) {
             return InputType.TYPE_NULL;
         }
 
-        @Override
-        public void setInputType(Object info, int inputType) {
+        public void setInputType(AccessibilityNodeInfo info, int inputType) {
         }
 
-        @Override
-        public void setMaxTextLength(Object info, int max) {
+        public void setMaxTextLength(AccessibilityNodeInfo info, int max) {
         }
 
-        @Override
-        public int getMaxTextLength(Object info) {
+        public int getMaxTextLength(AccessibilityNodeInfo info) {
             return -1;
         }
 
-        @Override
-        public void setTextSelection(Object info, int start, int end) {
+        public void setTextSelection(AccessibilityNodeInfo info, int start, int end) {
         }
 
-        @Override
-        public int getTextSelectionStart(Object info) {
+        public int getTextSelectionStart(AccessibilityNodeInfo info) {
             return -1;
         }
 
-        @Override
-        public int getTextSelectionEnd(Object info) {
+        public int getTextSelectionEnd(AccessibilityNodeInfo info) {
             return -1;
         }
 
-        @Override
-        public Object getWindow(Object info) {
+        public Object getWindow(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public boolean isDismissable(Object info) {
+        public boolean isDismissable(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public void setDismissable(Object info, boolean dismissable) {
+        public void setDismissable(AccessibilityNodeInfo info, boolean dismissable) {
         }
 
-        @Override
-        public boolean isEditable(Object info) {
+        public boolean isEditable(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public void setEditable(Object info, boolean editable) {
+        public void setEditable(AccessibilityNodeInfo info, boolean editable) {
         }
 
-        @Override
-        public boolean isMultiLine(Object info) {
+        public boolean isMultiLine(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public void setMultiLine(Object info, boolean multiLine) {
+        public void setMultiLine(AccessibilityNodeInfo info, boolean multiLine) {
         }
 
-        @Override
-        public boolean refresh(Object info) {
+        public boolean refresh(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public CharSequence getRoleDescription(Object info) {
+        public CharSequence getRoleDescription(AccessibilityNodeInfo info) {
             return null;
         }
 
-        @Override
-        public void setRoleDescription(Object info, CharSequence roleDescription) {
+        public void setRoleDescription(AccessibilityNodeInfo info, CharSequence roleDescription) {
         }
 
-        @Override
         public Object getActionScrollToPosition() {
             return null;
         }
 
-        @Override
         public Object getActionSetProgress() {
             return null;
         }
 
-        @Override
-        public boolean isContextClickable(Object info) {
+        public boolean isContextClickable(AccessibilityNodeInfo info) {
             return false;
         }
 
-        @Override
-        public void setContextClickable(Object info, boolean contextClickable) {
+        public void setContextClickable(AccessibilityNodeInfo info, boolean contextClickable) {
             // Do nothing.
         }
 
-        @Override
         public Object getActionShowOnScreen() {
             return null;
         }
 
-        @Override
         public Object getActionScrollUp() {
             return null;
         }
 
-        @Override
         public Object getActionScrollDown() {
             return null;
         }
 
-        @Override
         public Object getActionScrollLeft() {
             return null;
         }
 
-        @Override
         public Object getActionScrollRight() {
             return null;
         }
 
-        @Override
         public Object getActionContextClick() {
             return null;
         }
 
-        @Override
         public int getCollectionInfoSelectionMode(Object info) {
             return 0;
         }
 
-        @Override
-        public int getDrawingOrder(Object info) {
+        public int getDrawingOrder(AccessibilityNodeInfo info) {
             return 0;
         }
 
-        @Override
-        public void setDrawingOrder(Object info, int drawingOrderInParent) {
+        public void setDrawingOrder(AccessibilityNodeInfo info, int drawingOrderInParent) {
         }
 
-        @Override
-        public boolean isImportantForAccessibility(Object info) {
+        public boolean isImportantForAccessibility(AccessibilityNodeInfo info) {
             return true;
         }
 
-        @Override
-        public void setImportantForAccessibility(Object info, boolean importantForAccessibility) {
+        public void setImportantForAccessibility(AccessibilityNodeInfo info,
+                boolean importantForAccessibility) {
         }
     }
 
-    static class AccessibilityNodeInfoIcsImpl extends AccessibilityNodeInfoStubImpl {
+    @RequiresApi(16)
+    static class AccessibilityNodeInfoApi16Impl extends AccessibilityNodeInfoBaseImpl {
         @Override
-        public Object obtain() {
-            return AccessibilityNodeInfoCompatIcs.obtain();
+        public AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
+            return AccessibilityNodeInfo.obtain(root, virtualDescendantId);
         }
 
         @Override
-        public Object obtain(View source) {
-            return AccessibilityNodeInfoCompatIcs.obtain(source);
+        public Object findFocus(AccessibilityNodeInfo info, int focus) {
+            return info.findFocus(focus);
         }
 
         @Override
-        public Object obtain(Object info) {
-            return AccessibilityNodeInfoCompatIcs.obtain(info);
+        public Object focusSearch(AccessibilityNodeInfo info, int direction) {
+            return info.focusSearch(direction);
         }
 
         @Override
-        public void addAction(Object info, int action) {
-            AccessibilityNodeInfoCompatIcs.addAction(info, action);
+        public void addChild(AccessibilityNodeInfo info, View child, int virtualDescendantId) {
+            info.addChild(child, virtualDescendantId);
         }
 
         @Override
-        public void addChild(Object info, View child) {
-            AccessibilityNodeInfoCompatIcs.addChild(info, child);
+        public void setSource(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+            info.setSource(root, virtualDescendantId);
         }
 
         @Override
-        public List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
-            return AccessibilityNodeInfoCompatIcs.findAccessibilityNodeInfosByText(info, text);
+        public boolean isVisibleToUser(AccessibilityNodeInfo info) {
+            return info.isVisibleToUser();
         }
 
         @Override
-        public int getActions(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getActions(info);
+        public void setVisibleToUser(AccessibilityNodeInfo info, boolean visibleToUser) {
+            info.setVisibleToUser(visibleToUser);
         }
 
         @Override
-        public void getBoundsInParent(Object info, Rect outBounds) {
-            AccessibilityNodeInfoCompatIcs.getBoundsInParent(info, outBounds);
+        public boolean isAccessibilityFocused(AccessibilityNodeInfo info) {
+            return info.isAccessibilityFocused();
         }
 
         @Override
-        public void getBoundsInScreen(Object info, Rect outBounds) {
-            AccessibilityNodeInfoCompatIcs.getBoundsInScreen(info, outBounds);
+        public void setAccessibilityFocused(AccessibilityNodeInfo info, boolean focused) {
+            info.setAccessibilityFocused(focused);
         }
 
         @Override
-        public Object getChild(Object info, int index) {
-            return AccessibilityNodeInfoCompatIcs.getChild(info, index);
+        public boolean performAction(AccessibilityNodeInfo info, int action, Bundle arguments) {
+            return info.performAction(action, arguments);
         }
 
         @Override
-        public int getChildCount(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getChildCount(info);
+        public void setMovementGranularities(AccessibilityNodeInfo info, int granularities) {
+            info.setMovementGranularities(granularities);
         }
 
         @Override
-        public CharSequence getClassName(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getClassName(info);
+        public int getMovementGranularities(AccessibilityNodeInfo info) {
+            return info.getMovementGranularities();
         }
 
         @Override
-        public CharSequence getContentDescription(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getContentDescription(info);
-        }
-
-        @Override
-        public CharSequence getPackageName(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getPackageName(info);
-        }
-
-        @Override
-        public Object getParent(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getParent(info);
-        }
-
-        @Override
-        public CharSequence getText(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getText(info);
-        }
-
-        @Override
-        public int getWindowId(Object info) {
-            return AccessibilityNodeInfoCompatIcs.getWindowId(info);
-        }
-
-        @Override
-        public boolean isCheckable(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isCheckable(info);
-        }
-
-        @Override
-        public boolean isChecked(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isChecked(info);
-        }
-
-        @Override
-        public boolean isClickable(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isClickable(info);
-        }
-
-        @Override
-        public boolean isEnabled(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isEnabled(info);
-        }
-
-        @Override
-        public boolean isFocusable(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isFocusable(info);
-        }
-
-        @Override
-        public boolean isFocused(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isFocused(info);
-        }
-
-        @Override
-        public boolean isLongClickable(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isLongClickable(info);
-        }
-
-        @Override
-        public boolean isPassword(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isPassword(info);
-        }
-
-        @Override
-        public boolean isScrollable(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isScrollable(info);
-        }
-
-        @Override
-        public boolean isSelected(Object info) {
-            return AccessibilityNodeInfoCompatIcs.isSelected(info);
-        }
-
-        @Override
-        public boolean performAction(Object info, int action) {
-            return AccessibilityNodeInfoCompatIcs.performAction(info, action);
-        }
-
-        @Override
-        public void setBoundsInParent(Object info, Rect bounds) {
-            AccessibilityNodeInfoCompatIcs.setBoundsInParent(info, bounds);
-        }
-
-        @Override
-        public void setBoundsInScreen(Object info, Rect bounds) {
-            AccessibilityNodeInfoCompatIcs.setBoundsInScreen(info, bounds);
-        }
-
-        @Override
-        public void setCheckable(Object info, boolean checkable) {
-            AccessibilityNodeInfoCompatIcs.setCheckable(info, checkable);
-        }
-
-        @Override
-        public void setChecked(Object info, boolean checked) {
-            AccessibilityNodeInfoCompatIcs.setChecked(info, checked);
-        }
-
-        @Override
-        public void setClassName(Object info, CharSequence className) {
-            AccessibilityNodeInfoCompatIcs.setClassName(info, className);
-        }
-
-        @Override
-        public void setClickable(Object info, boolean clickable) {
-            AccessibilityNodeInfoCompatIcs.setClickable(info, clickable);
-        }
-
-        @Override
-        public void setContentDescription(Object info, CharSequence contentDescription) {
-            AccessibilityNodeInfoCompatIcs.setContentDescription(info, contentDescription);
-        }
-
-        @Override
-        public void setEnabled(Object info, boolean enabled) {
-            AccessibilityNodeInfoCompatIcs.setEnabled(info, enabled);
-        }
-
-        @Override
-        public void setFocusable(Object info, boolean focusable) {
-            AccessibilityNodeInfoCompatIcs.setFocusable(info, focusable);
-        }
-
-        @Override
-        public void setFocused(Object info, boolean focused) {
-            AccessibilityNodeInfoCompatIcs.setFocused(info, focused);
-        }
-
-        @Override
-        public void setLongClickable(Object info, boolean longClickable) {
-            AccessibilityNodeInfoCompatIcs.setLongClickable(info, longClickable);
-        }
-
-        @Override
-        public void setPackageName(Object info, CharSequence packageName) {
-            AccessibilityNodeInfoCompatIcs.setPackageName(info, packageName);
-        }
-
-        @Override
-        public void setParent(Object info, View parent) {
-            AccessibilityNodeInfoCompatIcs.setParent(info, parent);
-        }
-
-        @Override
-        public void setPassword(Object info, boolean password) {
-            AccessibilityNodeInfoCompatIcs.setPassword(info, password);
-        }
-
-        @Override
-        public void setScrollable(Object info, boolean scrollable) {
-            AccessibilityNodeInfoCompatIcs.setScrollable(info, scrollable);
-        }
-
-        @Override
-        public void setSelected(Object info, boolean selected) {
-            AccessibilityNodeInfoCompatIcs.setSelected(info, selected);
-        }
-
-        @Override
-        public void setSource(Object info, View source) {
-            AccessibilityNodeInfoCompatIcs.setSource(info, source);
-        }
-
-        @Override
-        public void setText(Object info, CharSequence text) {
-            AccessibilityNodeInfoCompatIcs.setText(info, text);
-        }
-
-        @Override
-        public void recycle(Object info) {
-            AccessibilityNodeInfoCompatIcs.recycle(info);
+        public void setParent(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+            info.setParent(root, virtualDescendantId);
         }
     }
 
-    static class AccessibilityNodeInfoJellybeanImpl extends AccessibilityNodeInfoIcsImpl {
+    @RequiresApi(17)
+    static class AccessibilityNodeInfoApi17Impl extends AccessibilityNodeInfoApi16Impl {
+
         @Override
-        public Object obtain(View root, int virtualDescendantId) {
-            return AccessibilityNodeInfoCompatJellyBean.obtain(root, virtualDescendantId);
+        public void setLabelFor(AccessibilityNodeInfo info, View labeled) {
+            info.setLabelFor(labeled);
         }
 
         @Override
-        public Object findFocus(Object info, int focus) {
-            return AccessibilityNodeInfoCompatJellyBean.findFocus(info, focus);
+        public void setLabelFor(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+            info.setLabelFor(root, virtualDescendantId);
         }
 
         @Override
-        public Object focusSearch(Object info, int direction) {
-            return AccessibilityNodeInfoCompatJellyBean.focusSearch(info, direction);
+        public Object getLabelFor(AccessibilityNodeInfo info) {
+            return info.getLabelFor();
         }
 
         @Override
-        public void addChild(Object info, View child, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatJellyBean.addChild(info, child, virtualDescendantId);
+        public void setLabeledBy(AccessibilityNodeInfo info, View labeled) {
+            info.setLabeledBy(labeled);
         }
 
         @Override
-        public void setSource(Object info, View root, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatJellyBean.setSource(info, root, virtualDescendantId);
+        public void setLabeledBy(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+            info.setLabeledBy(root, virtualDescendantId);
         }
 
         @Override
-        public boolean isVisibleToUser(Object info) {
-            return AccessibilityNodeInfoCompatJellyBean.isVisibleToUser(info);
-        }
-
-        @Override
-        public void setVisibleToUser(Object info, boolean visibleToUser) {
-            AccessibilityNodeInfoCompatJellyBean.setVisibleToUser(info, visibleToUser);
-        }
-
-        @Override
-        public boolean isAccessibilityFocused(Object info) {
-            return AccessibilityNodeInfoCompatJellyBean.isAccessibilityFocused(info);
-        }
-
-        @Override
-        public void setAccessibilityFocused(Object info, boolean focused) {
-            AccessibilityNodeInfoCompatJellyBean.setAccesibilityFocused(info, focused);
-        }
-
-        @Override
-        public boolean performAction(Object info, int action, Bundle arguments) {
-            return AccessibilityNodeInfoCompatJellyBean.performAction(info, action, arguments);
-        }
-
-        @Override
-        public void setMovementGranularities(Object info, int granularities) {
-            AccessibilityNodeInfoCompatJellyBean.setMovementGranularities(info, granularities);
-        }
-
-        @Override
-        public int getMovementGranularities(Object info) {
-            return AccessibilityNodeInfoCompatJellyBean.getMovementGranularities(info);
-        }
-
-        @Override
-        public void setParent(Object info, View root, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatJellyBean.setParent(info, root, virtualDescendantId);
+        public Object getLabeledBy(AccessibilityNodeInfo info) {
+            return info.getLabeledBy();
         }
     }
 
-    static class AccessibilityNodeInfoJellybeanMr1Impl extends AccessibilityNodeInfoJellybeanImpl {
+    @RequiresApi(18)
+    static class AccessibilityNodeInfoApi18Impl extends AccessibilityNodeInfoApi17Impl {
 
         @Override
-        public void setLabelFor(Object info, View labeled) {
-            AccessibilityNodeInfoCompatJellybeanMr1.setLabelFor(info, labeled);
+        public String getViewIdResourceName(AccessibilityNodeInfo info) {
+            return info.getViewIdResourceName();
         }
 
         @Override
-        public void setLabelFor(Object info, View root, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatJellybeanMr1.setLabelFor(info, root, virtualDescendantId);
+        public void setViewIdResourceName(AccessibilityNodeInfo info, String viewId) {
+            info.setViewIdResourceName(viewId);
         }
 
         @Override
-        public Object getLabelFor(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr1.getLabelFor(info);
+        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(
+                AccessibilityNodeInfo info, String viewId) {
+            return info.findAccessibilityNodeInfosByViewId(viewId);
         }
 
         @Override
-        public void setLabeledBy(Object info, View labeled) {
-            AccessibilityNodeInfoCompatJellybeanMr1.setLabeledBy(info, labeled);
+        public void setTextSelection(AccessibilityNodeInfo info, int start, int end) {
+            info.setTextSelection(start, end);
         }
 
         @Override
-        public void setLabeledBy(Object info, View root, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatJellybeanMr1.setLabeledBy(info, root, virtualDescendantId);
+        public int getTextSelectionStart(AccessibilityNodeInfo info) {
+            return info.getTextSelectionStart();
         }
 
         @Override
-        public Object getLabeledBy(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr1.getLabeledBy(info);
+        public int getTextSelectionEnd(AccessibilityNodeInfo info) {
+            return info.getTextSelectionEnd();
+        }
+
+        @Override
+        public boolean isEditable(AccessibilityNodeInfo info) {
+            return info.isEditable();
+        }
+
+        @Override
+        public void setEditable(AccessibilityNodeInfo info, boolean editable) {
+            info.setEditable(editable);
+        }
+
+        @Override
+        public boolean refresh(AccessibilityNodeInfo info) {
+            return info.refresh();
         }
     }
 
-    static class AccessibilityNodeInfoJellybeanMr2Impl extends
-            AccessibilityNodeInfoJellybeanMr1Impl {
+    @RequiresApi(19)
+    static class AccessibilityNodeInfoApi19Impl extends AccessibilityNodeInfoApi18Impl {
+        private static final String ROLE_DESCRIPTION_KEY =
+                "AccessibilityNodeInfo.roleDescription";
 
         @Override
-        public String getViewIdResourceName(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr2.getViewIdResourceName(info);
+        public int getLiveRegion(AccessibilityNodeInfo info) {
+            return info.getLiveRegion();
         }
 
         @Override
-        public void setViewIdResourceName(Object info, String viewId) {
-            AccessibilityNodeInfoCompatJellybeanMr2.setViewIdResourceName(info, viewId);
+        public void setLiveRegion(AccessibilityNodeInfo info, int mode) {
+            info.setLiveRegion(mode);
         }
 
         @Override
-        public List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId) {
-            return AccessibilityNodeInfoCompatJellybeanMr2.findAccessibilityNodeInfosByViewId(info,
-                    viewId);
+        public Object getCollectionInfo(AccessibilityNodeInfo info) {
+            return info.getCollectionInfo();
         }
 
         @Override
-        public void setTextSelection(Object info, int start, int end) {
-            AccessibilityNodeInfoCompatJellybeanMr2.setTextSelection(info, start, end);
-        }
-
-        @Override
-        public int getTextSelectionStart(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr2.getTextSelectionStart(info);
-        }
-
-        @Override
-        public int getTextSelectionEnd(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr2.getTextSelectionEnd(info);
-        }
-
-        @Override
-        public boolean isEditable(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr2.isEditable(info);
-        }
-
-        @Override
-        public void setEditable(Object info, boolean editable) {
-            AccessibilityNodeInfoCompatJellybeanMr2.setEditable(info, editable);
-        }
-
-        @Override
-        public boolean refresh(Object info) {
-            return AccessibilityNodeInfoCompatJellybeanMr2.refresh(info);
-        }
-    }
-
-    static class AccessibilityNodeInfoKitKatImpl extends AccessibilityNodeInfoJellybeanMr2Impl {
-        @Override
-        public int getLiveRegion(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getLiveRegion(info);
-        }
-
-        @Override
-        public void setLiveRegion(Object info, int mode) {
-            AccessibilityNodeInfoCompatKitKat.setLiveRegion(info, mode);
-        }
-
-        @Override
-        public Object getCollectionInfo(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getCollectionInfo(info);
-        }
-
-        @Override
-        public void setCollectionInfo(Object info, Object collectionInfo) {
-            AccessibilityNodeInfoCompatKitKat.setCollectionInfo(info, collectionInfo);
-        }
-
-        @Override
-        public Object obtainCollectionInfo(int rowCount, int columnCount,
-                boolean hierarchical, int selectionMode) {
-            return AccessibilityNodeInfoCompatKitKat.obtainCollectionInfo(rowCount, columnCount,
-                    hierarchical, selectionMode);
-        }
-
-        @Override
-        public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical) {
-            return AccessibilityNodeInfoCompatKitKat.obtainCollectionInfo(rowCount, columnCount,
-                    hierarchical);
-        }
-
-        @Override
-        public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
-                int columnSpan, boolean heading, boolean selected) {
-            return AccessibilityNodeInfoCompatKitKat
-                    .obtainCollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading);
-        }
-
-        @Override
-        public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
-                int columnSpan, boolean heading) {
-            return AccessibilityNodeInfoCompatKitKat
-                    .obtainCollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading);
-        }
-
-        @Override
-        public int getCollectionInfoColumnCount(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionInfo.getColumnCount(info);
-        }
-
-        @Override
-        public int getCollectionInfoRowCount(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionInfo.getRowCount(info);
-        }
-
-        @Override
-        public boolean isCollectionInfoHierarchical(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionInfo.isHierarchical(info);
-        }
-
-        @Override
-        public Object getCollectionItemInfo(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getCollectionItemInfo(info);
-        }
-
-        @Override
-        public Object getRangeInfo(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getRangeInfo(info);
-        }
-
-        @Override
-        public void setRangeInfo(Object info, Object rangeInfo) {
-            AccessibilityNodeInfoCompatKitKat.setRangeInfo(info, rangeInfo);
-        }
-
-        @Override
-        public int getCollectionItemColumnIndex(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getColumnIndex(info);
-        }
-
-        @Override
-        public int getCollectionItemColumnSpan(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getColumnSpan(info);
-        }
-
-        @Override
-        public int getCollectionItemRowIndex(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getRowIndex(info);
-        }
-
-        @Override
-        public int getCollectionItemRowSpan(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getRowSpan(info);
-        }
-
-        @Override
-        public boolean isCollectionItemHeading(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.isHeading(info);
-        }
-
-        @Override
-        public void setCollectionItemInfo(Object info, Object collectionItemInfo) {
-            AccessibilityNodeInfoCompatKitKat.setCollectionItemInfo(info, collectionItemInfo);
-        }
-
-        @Override
-        public Object obtainRangeInfo(int type, float min, float max, float current) {
-            return AccessibilityNodeInfoCompatKitKat.obtainRangeInfo(type, min, max, current);
-        }
-
-        @Override
-        public void setContentInvalid(Object info, boolean contentInvalid) {
-            AccessibilityNodeInfoCompatKitKat.setContentInvalid(info, contentInvalid);
-        }
-
-        @Override
-        public boolean isContentInvalid(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.isContentInvalid(info);
-        }
-
-        @Override
-        public boolean canOpenPopup(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.canOpenPopup(info);
-        }
-
-        @Override
-        public void setCanOpenPopup(Object info, boolean opensPopup) {
-            AccessibilityNodeInfoCompatKitKat.setCanOpenPopup(info, opensPopup);
-        }
-
-        @Override
-        public Bundle getExtras(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getExtras(info);
-        }
-
-        @Override
-        public int getInputType(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getInputType(info);
-        }
-
-        @Override
-        public void setInputType(Object info, int inputType) {
-            AccessibilityNodeInfoCompatKitKat.setInputType(info, inputType);
-        }
-
-        @Override
-        public boolean isDismissable(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.isDismissable(info);
-        }
-
-        @Override
-        public void setDismissable(Object info, boolean dismissable) {
-            AccessibilityNodeInfoCompatKitKat.setDismissable(info, dismissable);
-        }
-
-        @Override
-        public boolean isMultiLine(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.isMultiLine(info);
-        }
-
-        @Override
-        public void setMultiLine(Object info, boolean multiLine) {
-            AccessibilityNodeInfoCompatKitKat.setMultiLine(info, multiLine);
-        }
-
-        @Override
-        public CharSequence getRoleDescription(Object info) {
-            return AccessibilityNodeInfoCompatKitKat.getRoleDescription(info);
-        }
-
-        @Override
-        public void setRoleDescription(Object info, CharSequence roleDescription) {
-            AccessibilityNodeInfoCompatKitKat.setRoleDescription(info, roleDescription);
-        }
-    }
-
-    static class AccessibilityNodeInfoApi21Impl extends AccessibilityNodeInfoKitKatImpl {
-        @Override
-        public Object newAccessibilityAction(int actionId, CharSequence label) {
-            return AccessibilityNodeInfoCompatApi21.newAccessibilityAction(actionId, label);
-        }
-
-        @Override
-        public List<Object> getActionList(Object info) {
-            return AccessibilityNodeInfoCompatApi21.getActionList(info);
+        public void setCollectionInfo(AccessibilityNodeInfo info, Object collectionInfo) {
+            info.setCollectionInfo((AccessibilityNodeInfo.CollectionInfo) collectionInfo);
         }
 
         @Override
         public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical,
                 int selectionMode) {
-            return AccessibilityNodeInfoCompatApi21.obtainCollectionInfo(rowCount, columnCount,
-                    hierarchical, selectionMode);
+            return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical);
         }
 
         @Override
-        public void addAction(Object info, Object action) {
-            AccessibilityNodeInfoCompatApi21.addAction(info, action);
+        public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical) {
+            return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical);
         }
 
         @Override
-        public boolean removeAction(Object info, Object action) {
-            return AccessibilityNodeInfoCompatApi21.removeAction(info, action);
+        public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
+                int columnSpan, boolean heading, boolean selected) {
+            return AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
+                    columnSpan, heading);
+        }
+
+        @Override
+        public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
+                int columnSpan, boolean heading) {
+            return AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
+                    columnSpan, heading);
+        }
+
+        @Override
+        public int getCollectionInfoColumnCount(Object info) {
+            return ((AccessibilityNodeInfo.CollectionInfo) info).getColumnCount();
+        }
+
+        @Override
+        public int getCollectionInfoRowCount(Object info) {
+            return ((AccessibilityNodeInfo.CollectionInfo) info).getRowCount();
+        }
+
+        @Override
+        public boolean isCollectionInfoHierarchical(Object info) {
+            return ((AccessibilityNodeInfo.CollectionInfo) info).isHierarchical();
+        }
+
+        @Override
+        public Object getCollectionItemInfo(AccessibilityNodeInfo info) {
+            return info.getCollectionItemInfo();
+        }
+
+        @Override
+        public Object getRangeInfo(AccessibilityNodeInfo info) {
+            return info.getRangeInfo();
+        }
+
+        @Override
+        public void setRangeInfo(AccessibilityNodeInfo info, Object rangeInfo) {
+            info.setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo);
+        }
+
+        @Override
+        public int getCollectionItemColumnIndex(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnIndex();
+        }
+
+        @Override
+        public int getCollectionItemColumnSpan(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnSpan();
+        }
+
+        @Override
+        public int getCollectionItemRowIndex(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowIndex();
+        }
+
+        @Override
+        public int getCollectionItemRowSpan(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowSpan();
+        }
+
+        @Override
+        public boolean isCollectionItemHeading(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).isHeading();
+        }
+
+        @Override
+        public void setCollectionItemInfo(AccessibilityNodeInfo info, Object collectionItemInfo) {
+            info.setCollectionItemInfo(
+                    (AccessibilityNodeInfo.CollectionItemInfo) collectionItemInfo);
+        }
+
+        @Override
+        public Object obtainRangeInfo(int type, float min, float max, float current) {
+            return AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current);
+        }
+
+        @Override
+        public void setContentInvalid(AccessibilityNodeInfo info, boolean contentInvalid) {
+            info.setContentInvalid(contentInvalid);
+        }
+
+        @Override
+        public boolean isContentInvalid(AccessibilityNodeInfo info) {
+            return info.isContentInvalid();
+        }
+
+        @Override
+        public boolean canOpenPopup(AccessibilityNodeInfo info) {
+            return info.canOpenPopup();
+        }
+
+        @Override
+        public void setCanOpenPopup(AccessibilityNodeInfo info, boolean opensPopup) {
+            info.setCanOpenPopup(opensPopup);
+        }
+
+        @Override
+        public Bundle getExtras(AccessibilityNodeInfo info) {
+            return info.getExtras();
+        }
+
+        @Override
+        public int getInputType(AccessibilityNodeInfo info) {
+            return info.getInputType();
+        }
+
+        @Override
+        public void setInputType(AccessibilityNodeInfo info, int inputType) {
+            info.setInputType(inputType);
+        }
+
+        @Override
+        public boolean isDismissable(AccessibilityNodeInfo info) {
+            return info.isDismissable();
+        }
+
+        @Override
+        public void setDismissable(AccessibilityNodeInfo info, boolean dismissable) {
+            info.setDismissable(dismissable);
+        }
+
+        @Override
+        public boolean isMultiLine(AccessibilityNodeInfo info) {
+            return info.isMultiLine();
+        }
+
+        @Override
+        public void setMultiLine(AccessibilityNodeInfo info, boolean multiLine) {
+            info.setMultiLine(multiLine);
+        }
+
+        @Override
+        public CharSequence getRoleDescription(AccessibilityNodeInfo info) {
+            Bundle extras = getExtras(info);
+            return extras.getCharSequence(ROLE_DESCRIPTION_KEY);
+        }
+
+        @Override
+        public void setRoleDescription(AccessibilityNodeInfo info, CharSequence roleDescription) {
+            Bundle extras = getExtras(info);
+            extras.putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
+        }
+    }
+
+    @RequiresApi(21)
+    static class AccessibilityNodeInfoApi21Impl extends AccessibilityNodeInfoApi19Impl {
+        @Override
+        public Object newAccessibilityAction(int actionId, CharSequence label) {
+            return new AccessibilityNodeInfo.AccessibilityAction(actionId, label);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public List<Object> getActionList(AccessibilityNodeInfo info) {
+            Object result = info.getActionList();
+            return (List<Object>) result;
+        }
+
+        @Override
+        public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical,
+                int selectionMode) {
+            return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical,
+                    selectionMode);
+        }
+
+        @Override
+        public void addAction(AccessibilityNodeInfo info, Object action) {
+            info.addAction((AccessibilityNodeInfo.AccessibilityAction) action);
+        }
+
+        @Override
+        public boolean removeAction(AccessibilityNodeInfo info, Object action) {
+            return info.removeAction((AccessibilityNodeInfo.AccessibilityAction) action);
         }
 
         @Override
         public int getAccessibilityActionId(Object action) {
-            return AccessibilityNodeInfoCompatApi21.getAccessibilityActionId(action);
+            return ((AccessibilityNodeInfo.AccessibilityAction) action).getId();
         }
 
         @Override
         public CharSequence getAccessibilityActionLabel(Object action) {
-            return AccessibilityNodeInfoCompatApi21.getAccessibilityActionLabel(action);
+            return ((AccessibilityNodeInfo.AccessibilityAction) action).getLabel();
         }
 
         @Override
         public Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
                 int columnSpan, boolean heading, boolean selected) {
-            return AccessibilityNodeInfoCompatApi21.obtainCollectionItemInfo(rowIndex, rowSpan,
-                    columnIndex, columnSpan, heading, selected);
+            return AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
+                    columnSpan, heading, selected);
         }
 
         @Override
         public boolean isCollectionItemSelected(Object info) {
-            return AccessibilityNodeInfoCompatApi21.CollectionItemInfo.isSelected(info);
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).isSelected();
         }
 
         @Override
-        public CharSequence getError(Object info) {
-            return AccessibilityNodeInfoCompatApi21.getError(info);
+        public CharSequence getError(AccessibilityNodeInfo info) {
+            return info.getError();
         }
 
         @Override
-        public void setError(Object info, CharSequence error) {
-            AccessibilityNodeInfoCompatApi21.setError(info, error);
+        public void setError(AccessibilityNodeInfo info, CharSequence error) {
+            info.setError(error);
         }
 
         @Override
-        public void setMaxTextLength(Object info, int max) {
-            AccessibilityNodeInfoCompatApi21.setMaxTextLength(info, max);
+        public void setMaxTextLength(AccessibilityNodeInfo info, int max) {
+            info.setMaxTextLength(max);
         }
 
         @Override
-        public int getMaxTextLength(Object info) {
-            return AccessibilityNodeInfoCompatApi21.getMaxTextLength(info);
+        public int getMaxTextLength(AccessibilityNodeInfo info) {
+            return info.getMaxTextLength();
         }
 
         @Override
-        public Object getWindow(Object info) {
-            return AccessibilityNodeInfoCompatApi21.getWindow(info);
+        public Object getWindow(AccessibilityNodeInfo info) {
+            return info.getWindow();
         }
 
         @Override
-        public boolean removeChild(Object info, View child) {
-            return AccessibilityNodeInfoCompatApi21.removeChild(info, child);
+        public boolean removeChild(AccessibilityNodeInfo info, View child) {
+            return info.removeChild(child);
         }
 
         @Override
-        public boolean removeChild(Object info, View root, int virtualDescendantId) {
-            return AccessibilityNodeInfoCompatApi21.removeChild(info, root, virtualDescendantId);
+        public boolean removeChild(AccessibilityNodeInfo info, View root, int virtualDescendantId) {
+            return info.removeChild(root, virtualDescendantId);
         }
 
         @Override
         public int getCollectionInfoSelectionMode(Object info) {
-            return AccessibilityNodeInfoCompatApi21.CollectionInfo.getSelectionMode(info);
+            return ((AccessibilityNodeInfo.CollectionInfo) info).getSelectionMode();
         }
     }
 
+    @RequiresApi(22)
     static class AccessibilityNodeInfoApi22Impl extends AccessibilityNodeInfoApi21Impl {
         @Override
-        public Object getTraversalBefore(Object info) {
-            return AccessibilityNodeInfoCompatApi22.getTraversalBefore(info);
+        public Object getTraversalBefore(AccessibilityNodeInfo info) {
+            return info.getTraversalBefore();
         }
 
         @Override
-        public void setTraversalBefore(Object info, View view) {
-            AccessibilityNodeInfoCompatApi22.setTraversalBefore(info, view);
+        public void setTraversalBefore(AccessibilityNodeInfo info, View view) {
+            info.setTraversalBefore(view);
         }
 
         @Override
-        public void setTraversalBefore(Object info, View root, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatApi22.setTraversalBefore(info, root, virtualDescendantId);
+        public void setTraversalBefore(AccessibilityNodeInfo info, View root,
+                int virtualDescendantId) {
+            info.setTraversalBefore(root, virtualDescendantId);
         }
 
         @Override
-        public Object getTraversalAfter(Object info) {
-            return AccessibilityNodeInfoCompatApi22.getTraversalAfter(info);
+        public Object getTraversalAfter(AccessibilityNodeInfo info) {
+            return info.getTraversalAfter();
         }
 
         @Override
-        public void setTraversalAfter(Object info, View view) {
-            AccessibilityNodeInfoCompatApi22.setTraversalAfter(info, view);
+        public void setTraversalAfter(AccessibilityNodeInfo info, View view) {
+            info.setTraversalAfter(view);
         }
 
         @Override
-        public void setTraversalAfter(Object info, View root, int virtualDescendantId) {
-            AccessibilityNodeInfoCompatApi22.setTraversalAfter(info, root, virtualDescendantId);
+        public void setTraversalAfter(AccessibilityNodeInfo info, View root,
+                int virtualDescendantId) {
+            info.setTraversalAfter(root, virtualDescendantId);
         }
     }
 
+    @RequiresApi(23)
     static class AccessibilityNodeInfoApi23Impl extends AccessibilityNodeInfoApi22Impl {
         @Override
         public Object getActionScrollToPosition() {
-            return AccessibilityNodeInfoCompatApi23.getActionScrollToPosition();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION;
         }
 
         @Override
         public Object getActionShowOnScreen() {
-            return AccessibilityNodeInfoCompatApi23.getActionShowOnScreen();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_ON_SCREEN;
         }
 
         @Override
         public Object getActionScrollUp() {
-            return AccessibilityNodeInfoCompatApi23.getActionScrollUp();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP;
         }
 
         @Override
         public Object getActionScrollDown() {
-            return AccessibilityNodeInfoCompatApi23.getActionScrollDown();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN;
         }
 
         @Override
         public Object getActionScrollLeft() {
-            return AccessibilityNodeInfoCompatApi23.getActionScrollLeft();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT;
         }
 
         @Override
         public Object getActionScrollRight() {
-            return AccessibilityNodeInfoCompatApi23.getActionScrollRight();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT;
         }
 
         @Override
         public Object getActionContextClick() {
-            return AccessibilityNodeInfoCompatApi23.getActionContextClick();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK;
         }
 
         @Override
-        public boolean isContextClickable(Object info) {
-            return AccessibilityNodeInfoCompatApi23.isContextClickable(info);
+        public boolean isContextClickable(AccessibilityNodeInfo info) {
+            return info.isContextClickable();
         }
 
         @Override
-        public void setContextClickable(Object info, boolean contextClickable) {
-            AccessibilityNodeInfoCompatApi23.setContextClickable(info, contextClickable);
+        public void setContextClickable(AccessibilityNodeInfo info, boolean contextClickable) {
+            info.setContextClickable(contextClickable);
         }
     }
 
+    @RequiresApi(24)
     static class AccessibilityNodeInfoApi24Impl extends AccessibilityNodeInfoApi23Impl {
         @Override
         public Object getActionSetProgress() {
-            return AccessibilityNodeInfoCompatApi24.getActionSetProgress();
+            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS;
         }
 
         @Override
-        public int getDrawingOrder(Object info) {
-            return AccessibilityNodeInfoCompatApi24.getDrawingOrder(info);
+        public int getDrawingOrder(AccessibilityNodeInfo info) {
+            return info.getDrawingOrder();
         }
 
         @Override
-        public void setDrawingOrder(Object info, int drawingOrderInParent) {
-            AccessibilityNodeInfoCompatApi24.setDrawingOrder(info, drawingOrderInParent);
+        public void setDrawingOrder(AccessibilityNodeInfo info, int drawingOrderInParent) {
+            info.setDrawingOrder(drawingOrderInParent);
         }
 
         @Override
-        public boolean isImportantForAccessibility(Object info) {
-            return AccessibilityNodeInfoCompatApi24.isImportantForAccessibility(info);
+        public boolean isImportantForAccessibility(AccessibilityNodeInfo info) {
+            return info.isImportantForAccessibility();
         }
 
         @Override
-        public void setImportantForAccessibility(Object info, boolean importantForAccessibility) {
-            AccessibilityNodeInfoCompatApi24.setImportantForAccessibility(
-                    info, importantForAccessibility);
+        public void setImportantForAccessibility(AccessibilityNodeInfo info,
+                boolean importantForAccessibility) {
+            info.setImportantForAccessibility(importantForAccessibility);
         }
 
     }
@@ -2350,23 +1645,21 @@
         } else if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new AccessibilityNodeInfoApi21Impl();
         } else if (Build.VERSION.SDK_INT >= 19) { // KitKat
-            IMPL = new AccessibilityNodeInfoKitKatImpl();
+            IMPL = new AccessibilityNodeInfoApi19Impl();
         } else if (Build.VERSION.SDK_INT >= 18) { // JellyBean MR2
-            IMPL = new AccessibilityNodeInfoJellybeanMr2Impl();
+            IMPL = new AccessibilityNodeInfoApi18Impl();
         } else if (Build.VERSION.SDK_INT >= 17) { // JellyBean MR1
-            IMPL = new AccessibilityNodeInfoJellybeanMr1Impl();
+            IMPL = new AccessibilityNodeInfoApi17Impl();
         } else if (Build.VERSION.SDK_INT >= 16) { // JellyBean
-            IMPL = new AccessibilityNodeInfoJellybeanImpl();
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new AccessibilityNodeInfoIcsImpl();
+            IMPL = new AccessibilityNodeInfoApi16Impl();
         } else {
-            IMPL = new AccessibilityNodeInfoStubImpl();
+            IMPL = new AccessibilityNodeInfoBaseImpl();
         }
     }
 
-    static final AccessibilityNodeInfoImpl IMPL;
+    static final AccessibilityNodeInfoBaseImpl IMPL;
 
-    private final Object mInfo;
+    private final AccessibilityNodeInfo mInfo;
 
     /**
      *  android.support.v4.widget.ExploreByTouchHelper.HOST_ID = -1;
@@ -2767,14 +2060,41 @@
      * {@link android.view.accessibility.AccessibilityNodeInfo}.
      *
      * @param info The info.
+     *
+     * @deprecated Use {@link #wrap(AccessibilityNodeInfo)} instead.
      */
+    @Deprecated
     public AccessibilityNodeInfoCompat(Object info) {
+        mInfo = (AccessibilityNodeInfo) info;
+    }
+
+    private AccessibilityNodeInfoCompat(AccessibilityNodeInfo info) {
         mInfo = info;
     }
 
     /**
-     * @return The wrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
+     * Creates a new instance wrapping an
+     * {@link android.view.accessibility.AccessibilityNodeInfo}.
+     *
+     * @param info The info.
      */
+    public static AccessibilityNodeInfoCompat wrap(@NonNull AccessibilityNodeInfo info) {
+        return new AccessibilityNodeInfoCompat(info);
+    }
+
+    /**
+     * @return The unwrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
+     */
+    public AccessibilityNodeInfo unwrap() {
+        return mInfo;
+    }
+
+    /**
+     * @return The wrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
+     *
+     * @deprecated Use {@link #unwrap()} instead.
+     */
+    @Deprecated
     public Object getInfo() {
         return mInfo;
     }
@@ -2787,7 +2107,7 @@
      * @see #setSource(View)
      */
     public static AccessibilityNodeInfoCompat obtain(View source) {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.obtain(source));
+        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(source));
     }
 
     /**
@@ -2811,7 +2131,7 @@
      * @return An instance.
      */
     public static AccessibilityNodeInfoCompat obtain() {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.obtain());
+        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain());
     }
 
     /**
@@ -2822,7 +2142,7 @@
      * @return An instance.
      */
     public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.obtain(info.mInfo));
+        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(info.mInfo));
     }
 
     /**
@@ -2831,7 +2151,7 @@
      * @param source The info source.
      */
     public void setSource(View source) {
-        IMPL.setSource(mInfo, source);
+        mInfo.setSource(source);
     }
 
     /**
@@ -2896,7 +2216,7 @@
      * @return The window id.
      */
     public int getWindowId() {
-        return IMPL.getWindowId(mInfo);
+        return mInfo.getWindowId();
     }
 
     /**
@@ -2905,7 +2225,7 @@
      * @return The child count.
      */
     public int getChildCount() {
-        return IMPL.getChildCount(mInfo);
+        return mInfo.getChildCount();
     }
 
     /**
@@ -2922,7 +2242,7 @@
      *             AccessibilityService.
      */
     public AccessibilityNodeInfoCompat getChild(int index) {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getChild(mInfo, index));
+        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getChild(index));
     }
 
     /**
@@ -2937,7 +2257,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void addChild(View child) {
-        IMPL.addChild(mInfo, child);
+        mInfo.addChild(child);
     }
 
     /**
@@ -3000,7 +2320,7 @@
      * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
      */
     public int getActions() {
-        return IMPL.getActions(mInfo);
+        return mInfo.getActions();
     }
 
     /**
@@ -3015,7 +2335,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void addAction(int action) {
-        IMPL.addAction(mInfo, action);
+        mInfo.addAction(action);
     }
 
     /**
@@ -3064,7 +2384,7 @@
      *             AccessibilityService.
      */
     public boolean performAction(int action) {
-        return IMPL.performAction(mInfo, action);
+        return mInfo.performAction(action);
     }
 
     /**
@@ -3124,11 +2444,11 @@
      */
     public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
         List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
-        List<Object> infos = IMPL.findAccessibilityNodeInfosByText(mInfo, text);
+        List<AccessibilityNodeInfo> infos = mInfo.findAccessibilityNodeInfosByText(text);
         final int infoCount = infos.size();
         for (int i = 0; i < infoCount; i++) {
-            Object info = infos.get(i);
-            result.add(new AccessibilityNodeInfoCompat(info));
+            AccessibilityNodeInfo info = infos.get(i);
+            result.add(AccessibilityNodeInfoCompat.wrap(info));
         }
         return result;
     }
@@ -3144,7 +2464,7 @@
      * @return The parent.
      */
     public AccessibilityNodeInfoCompat getParent() {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getParent(mInfo));
+        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getParent());
     }
 
     /**
@@ -3159,7 +2479,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setParent(View parent) {
-        IMPL.setParent(mInfo, parent);
+        mInfo.setParent(parent);
     }
 
     /**
@@ -3192,7 +2512,7 @@
      * @param outBounds The output node bounds.
      */
     public void getBoundsInParent(Rect outBounds) {
-        IMPL.getBoundsInParent(mInfo, outBounds);
+        mInfo.getBoundsInParent(outBounds);
     }
 
     /**
@@ -3204,10 +2524,10 @@
      * </p>
      *
      * @param bounds The node bounds.
-     *@throws IllegalStateException If called from an AccessibilityService.
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setBoundsInParent(Rect bounds) {
-        IMPL.setBoundsInParent(mInfo, bounds);
+        mInfo.setBoundsInParent(bounds);
     }
 
     /**
@@ -3216,7 +2536,7 @@
      * @param outBounds The output node bounds.
      */
     public void getBoundsInScreen(Rect outBounds) {
-        IMPL.getBoundsInScreen(mInfo, outBounds);
+        mInfo.getBoundsInScreen(outBounds);
     }
 
     /**
@@ -3231,7 +2551,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setBoundsInScreen(Rect bounds) {
-        IMPL.setBoundsInScreen(mInfo, bounds);
+        mInfo.setBoundsInScreen(bounds);
     }
 
     /**
@@ -3240,7 +2560,7 @@
      * @return True if the node is checkable.
      */
     public boolean isCheckable() {
-        return IMPL.isCheckable(mInfo);
+        return mInfo.isCheckable();
     }
 
     /**
@@ -3255,7 +2575,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setCheckable(boolean checkable) {
-        IMPL.setCheckable(mInfo, checkable);
+        mInfo.setCheckable(checkable);
     }
 
     /**
@@ -3264,7 +2584,7 @@
      * @return True if the node is checked.
      */
     public boolean isChecked() {
-        return IMPL.isChecked(mInfo);
+        return mInfo.isChecked();
     }
 
     /**
@@ -3279,7 +2599,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setChecked(boolean checked) {
-        IMPL.setChecked(mInfo, checked);
+        mInfo.setChecked(checked);
     }
 
     /**
@@ -3288,7 +2608,7 @@
      * @return True if the node is focusable.
      */
     public boolean isFocusable() {
-        return IMPL.isFocusable(mInfo);
+        return mInfo.isFocusable();
     }
 
     /**
@@ -3303,7 +2623,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setFocusable(boolean focusable) {
-        IMPL.setFocusable(mInfo, focusable);
+        mInfo.setFocusable(focusable);
     }
 
     /**
@@ -3312,7 +2632,7 @@
      * @return True if the node is focused.
      */
     public boolean isFocused() {
-        return IMPL.isFocused(mInfo);
+        return mInfo.isFocused();
     }
 
     /**
@@ -3327,7 +2647,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setFocused(boolean focused) {
-        IMPL.setFocused(mInfo, focused);
+        mInfo.setFocused(focused);
     }
 
     /**
@@ -3386,7 +2706,7 @@
      * @return True if the node is selected.
      */
     public boolean isSelected() {
-        return IMPL.isSelected(mInfo);
+        return mInfo.isSelected();
     }
 
     /**
@@ -3401,7 +2721,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setSelected(boolean selected) {
-        IMPL.setSelected(mInfo, selected);
+        mInfo.setSelected(selected);
     }
 
     /**
@@ -3410,7 +2730,7 @@
      * @return True if the node is clickable.
      */
     public boolean isClickable() {
-        return IMPL.isClickable(mInfo);
+        return mInfo.isClickable();
     }
 
     /**
@@ -3425,7 +2745,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setClickable(boolean clickable) {
-        IMPL.setClickable(mInfo, clickable);
+        mInfo.setClickable(clickable);
     }
 
     /**
@@ -3434,7 +2754,7 @@
      * @return True if the node is long clickable.
      */
     public boolean isLongClickable() {
-        return IMPL.isLongClickable(mInfo);
+        return mInfo.isLongClickable();
     }
 
     /**
@@ -3449,7 +2769,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setLongClickable(boolean longClickable) {
-        IMPL.setLongClickable(mInfo, longClickable);
+        mInfo.setLongClickable(longClickable);
     }
 
     /**
@@ -3458,7 +2778,7 @@
      * @return True if the node is enabled.
      */
     public boolean isEnabled() {
-        return IMPL.isEnabled(mInfo);
+        return mInfo.isEnabled();
     }
 
     /**
@@ -3473,7 +2793,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEnabled(boolean enabled) {
-        IMPL.setEnabled(mInfo, enabled);
+        mInfo.setEnabled(enabled);
     }
 
     /**
@@ -3482,7 +2802,7 @@
      * @return True if the node is a password.
      */
     public boolean isPassword() {
-        return IMPL.isPassword(mInfo);
+        return mInfo.isPassword();
     }
 
     /**
@@ -3497,7 +2817,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPassword(boolean password) {
-        IMPL.setPassword(mInfo, password);
+        mInfo.setPassword(password);
     }
 
     /**
@@ -3506,7 +2826,7 @@
      * @return True if the node is scrollable, false otherwise.
      */
     public boolean isScrollable() {
-        return IMPL.isScrollable(mInfo);
+        return mInfo.isScrollable();
     }
 
     /**
@@ -3521,7 +2841,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setScrollable(boolean scrollable) {
-        IMPL.setScrollable(mInfo, scrollable);
+        mInfo.setScrollable(scrollable);
     }
 
     /**
@@ -3557,7 +2877,7 @@
      * @return The package name.
      */
     public CharSequence getPackageName() {
-        return IMPL.getPackageName(mInfo);
+        return mInfo.getPackageName();
     }
 
     /**
@@ -3572,7 +2892,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPackageName(CharSequence packageName) {
-        IMPL.setPackageName(mInfo, packageName);
+        mInfo.setPackageName(packageName);
     }
 
     /**
@@ -3581,7 +2901,7 @@
      * @return The class name.
      */
     public CharSequence getClassName() {
-        return IMPL.getClassName(mInfo);
+        return mInfo.getClassName();
     }
 
     /**
@@ -3596,7 +2916,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setClassName(CharSequence className) {
-        IMPL.setClassName(mInfo, className);
+        mInfo.setClassName(className);
     }
 
     /**
@@ -3605,7 +2925,7 @@
      * @return The text.
      */
     public CharSequence getText() {
-        return IMPL.getText(mInfo);
+        return mInfo.getText();
     }
 
     /**
@@ -3620,7 +2940,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setText(CharSequence text) {
-        IMPL.setText(mInfo, text);
+        mInfo.setText(text);
     }
 
     /**
@@ -3629,7 +2949,7 @@
      * @return The content description.
      */
     public CharSequence getContentDescription() {
-        return IMPL.getContentDescription(mInfo);
+        return mInfo.getContentDescription();
     }
 
     /**
@@ -3644,7 +2964,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setContentDescription(CharSequence contentDescription) {
-        IMPL.setContentDescription(mInfo, contentDescription);
+        mInfo.setContentDescription(contentDescription);
     }
 
     /**
@@ -3655,7 +2975,7 @@
      * @throws IllegalStateException If the info is already recycled.
      */
     public void recycle() {
-        IMPL.recycle(mInfo);
+        mInfo.recycle();
     }
 
     /**
@@ -4045,11 +3365,11 @@
      * @return A list of node info.
      */
     public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
-        List<Object> nodes = IMPL.findAccessibilityNodeInfosByViewId(mInfo, viewId);
+        List<AccessibilityNodeInfo> nodes = IMPL.findAccessibilityNodeInfosByViewId(mInfo, viewId);
         if (nodes != null) {
             List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
-            for (Object node : nodes) {
-                result.add(new AccessibilityNodeInfoCompat(node));
+            for (AccessibilityNodeInfo node : nodes) {
+                result.add(AccessibilityNodeInfoCompat.wrap(node));
             }
             return result;
         } else {
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
index 19d4776..211a0bc 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
@@ -19,14 +19,13 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.view.View;
+import android.support.annotation.RequiresApi;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}.
  */
 public class AccessibilityNodeProviderCompat {
 
@@ -41,6 +40,7 @@
         }
     }
 
+    @RequiresApi(16)
     private static class AccessibilityNodeProviderJellyBeanImpl
             extends AccessibilityNodeProviderStubImpl {
         AccessibilityNodeProviderJellyBeanImpl() {
@@ -69,7 +69,7 @@
                                 final int infoCount = compatInfos.size();
                                 for (int i = 0; i < infoCount; i++) {
                                     AccessibilityNodeInfoCompat infoCompat = compatInfos.get(i);
-                                    infos.add(infoCompat.getInfo());
+                                    infos.add(infoCompat.unwrap());
                                 }
                                 return infos;
                             }
@@ -83,13 +83,14 @@
                             if (compatInfo == null) {
                                 return null;
                             } else {
-                                return compatInfo.getInfo();
+                                return compatInfo.unwrap();
                             }
                         }
                     });
         }
     }
 
+    @RequiresApi(19)
     private static class AccessibilityNodeProviderKitKatImpl
             extends AccessibilityNodeProviderStubImpl {
         AccessibilityNodeProviderKitKatImpl() {
@@ -118,7 +119,7 @@
                                 final int infoCount = compatInfos.size();
                                 for (int i = 0; i < infoCount; i++) {
                                     AccessibilityNodeInfoCompat infoCompat = compatInfos.get(i);
-                                    infos.add(infoCompat.getInfo());
+                                    infos.add(infoCompat.unwrap());
                                 }
                                 return infos;
                             }
@@ -131,7 +132,7 @@
                             if (compatInfo == null) {
                                 return null;
                             } else {
-                                return compatInfo.getInfo();
+                                return compatInfo.unwrap();
                             }
                         }
 
@@ -141,7 +142,7 @@
                             if (compatInfo == null) {
                                 return null;
                             } else {
-                                return compatInfo.getInfo();
+                                return compatInfo.unwrap();
                             }
                         }
                     });
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
index 9d00c58..395af49 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
@@ -18,551 +18,82 @@
 
 import android.os.Build;
 import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityRecord;
 
-import java.util.Collections;
 import java.util.List;
 
 /**
- * Helper for accessing {@link android.view.accessibility.AccessibilityRecord}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link AccessibilityRecord}.
  */
 public class AccessibilityRecordCompat {
 
-    static interface AccessibilityRecordImpl {
-        public Object obtain();
-        public Object obtain(Object record);
-        public void setSource(Object record, View source);
-        public void setSource(Object record, View root, int virtualDescendantId);
-        public AccessibilityNodeInfoCompat getSource(Object record);
-        public int getWindowId(Object record);
-        public boolean isChecked(Object record);
-        public void setChecked(Object record, boolean isChecked);
-        public boolean isEnabled(Object record);
-        public void setEnabled(Object record, boolean isEnabled);
-        public boolean isPassword(Object record);
-        public void setPassword(Object record, boolean isPassword);
-        public boolean isFullScreen(Object record);
-        public void setFullScreen(Object record, boolean isFullScreen);
-        public boolean isScrollable(Object record);
-        public void setScrollable(Object record, boolean scrollable);
-        public int getItemCount(Object record);
-        public void setItemCount(Object record, int itemCount);
-        public int getCurrentItemIndex(Object record);
-        public void setCurrentItemIndex(Object record, int currentItemIndex);
-        public int getFromIndex(Object record);
-        public void setFromIndex(Object record, int fromIndex);
-        public int getToIndex(Object record);
-        public void setToIndex(Object record, int toIndex);
-        public int getScrollX(Object record);
-        public void setScrollX(Object record, int scrollX);
-        public int getScrollY(Object record);
-        public void setScrollY(Object record, int scrollY);
-        public int getMaxScrollX(Object record);
-        public void setMaxScrollX(Object record, int maxScrollX);
-        public int getMaxScrollY(Object record);
-        public void setMaxScrollY(Object record, int maxScrollY);
-        public int getAddedCount(Object record);
-        public void setAddedCount(Object record, int addedCount);
-        public int getRemovedCount(Object record);
-        public void setRemovedCount(Object record, int removedCount);
-        public CharSequence getClassName(Object record);
-        public void setClassName(Object record, CharSequence className);
-        public List<CharSequence> getText(Object record);
-        public CharSequence getBeforeText(Object record);
-        public void setBeforeText(Object record, CharSequence beforeText);
-        public CharSequence getContentDescription(Object record);
-        public void setContentDescription(Object record, CharSequence contentDescription);
-        public Parcelable getParcelableData(Object record);
-        public void setParcelableData(Object record, Parcelable parcelableData);
-        public void recycle(Object record);
-    }
-
-    static class AccessibilityRecordStubImpl implements AccessibilityRecordImpl {
-        @Override
-        public Object obtain() {
-            return null;
-        }
-
-        @Override
-        public Object obtain(Object record) {
-            return null;
-        }
-
-        @Override
-        public int getAddedCount(Object record) {
+    static class AccessibilityRecordCompatBaseImpl {
+        public int getMaxScrollX(AccessibilityRecord record) {
             return 0;
         }
 
-        @Override
-        public CharSequence getBeforeText(Object record) {
-            return null;
-        }
-
-        @Override
-        public CharSequence getClassName(Object record) {
-            return null;
-        }
-
-        @Override
-        public CharSequence getContentDescription(Object record) {
-            return null;
-        }
-
-        @Override
-        public int getCurrentItemIndex(Object record) {
+        public int getMaxScrollY(AccessibilityRecord record) {
             return 0;
         }
 
-        @Override
-        public int getFromIndex(Object record) {
-            return 0;
+        public void setMaxScrollX(AccessibilityRecord record, int maxScrollX) {
         }
 
-        @Override
-        public int getItemCount(Object record) {
-            return 0;
+        public void setMaxScrollY(AccessibilityRecord record, int maxScrollY) {
         }
 
-        @Override
-        public int getMaxScrollX(Object record) {
-            return 0;
-        }
-
-        @Override
-        public int getMaxScrollY(Object record) {
-            return 0;
-        }
-
-        @Override
-        public Parcelable getParcelableData(Object record) {
-            return null;
-        }
-
-        @Override
-        public int getRemovedCount(Object record) {
-            return 0;
-        }
-
-        @Override
-        public int getScrollX(Object record) {
-            return 0;
-        }
-
-        @Override
-        public int getScrollY(Object record) {
-            return 0;
-        }
-
-        @Override
-        public AccessibilityNodeInfoCompat getSource(Object record) {
-            return null;
-        }
-
-        @Override
-        public List<CharSequence> getText(Object record) {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public int getToIndex(Object record) {
-            return 0;
-        }
-
-        @Override
-        public int getWindowId(Object record) {
-            return 0;
-        }
-
-        @Override
-        public boolean isChecked(Object record) {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(Object record) {
-            return false;
-        }
-
-        @Override
-        public boolean isFullScreen(Object record) {
-            return false;
-        }
-
-        @Override
-        public boolean isPassword(Object record) {
-            return false;
-        }
-
-        @Override
-        public boolean isScrollable(Object record) {
-            return false;
-        }
-
-        @Override
-        public void recycle(Object record) {
-
-        }
-
-        @Override
-        public void setAddedCount(Object record, int addedCount) {
-
-        }
-
-        @Override
-        public void setBeforeText(Object record, CharSequence beforeText) {
-
-        }
-
-        @Override
-        public void setChecked(Object record, boolean isChecked) {
-
-        }
-
-        @Override
-        public void setClassName(Object record, CharSequence className) {
-
-        }
-
-        @Override
-        public void setContentDescription(Object record, CharSequence contentDescription) {
-
-        }
-
-        @Override
-        public void setCurrentItemIndex(Object record, int currentItemIndex) {
-
-        }
-
-        @Override
-        public void setEnabled(Object record, boolean isEnabled) {
-
-        }
-
-        @Override
-        public void setFromIndex(Object record, int fromIndex) {
-
-        }
-
-        @Override
-        public void setFullScreen(Object record, boolean isFullScreen) {
-
-        }
-
-        @Override
-        public void setItemCount(Object record, int itemCount) {
-
-        }
-
-        @Override
-        public void setMaxScrollX(Object record, int maxScrollX) {
-
-        }
-
-        @Override
-        public void setMaxScrollY(Object record, int maxScrollY) {
-
-        }
-
-        @Override
-        public void setParcelableData(Object record, Parcelable parcelableData) {
-
-        }
-
-        @Override
-        public void setPassword(Object record, boolean isPassword) {
-
-        }
-
-        @Override
-        public void setRemovedCount(Object record, int removedCount) {
-
-        }
-
-        @Override
-        public void setScrollX(Object record, int scrollX) {
-
-        }
-
-        @Override
-        public void setScrollY(Object record, int scrollY) {
-
-        }
-
-        @Override
-        public void setScrollable(Object record, boolean scrollable) {
-
-        }
-
-        @Override
-        public void setSource(Object record, View source) {
-
-        }
-
-        @Override
-        public void setSource(Object record, View root, int virtualDescendantId) {
-
-        }
-
-        @Override
-        public void setToIndex(Object record, int toIndex) {
-
+        public void setSource(AccessibilityRecord record, View root, int virtualDescendantId) {
         }
     }
 
-    static class AccessibilityRecordIcsImpl extends AccessibilityRecordStubImpl {
+    @RequiresApi(15)
+    static class AccessibilityRecordCompatApi15Impl extends AccessibilityRecordCompatBaseImpl {
         @Override
-        public Object obtain() {
-            return AccessibilityRecordCompatIcs.obtain();
+        public int getMaxScrollX(AccessibilityRecord record) {
+            return record.getMaxScrollX();
         }
 
         @Override
-        public Object obtain(Object record) {
-            return AccessibilityRecordCompatIcs.obtain(record);
+        public int getMaxScrollY(AccessibilityRecord record) {
+            return record.getMaxScrollY();
         }
 
         @Override
-        public int getAddedCount(Object record) {
-            return AccessibilityRecordCompatIcs.getAddedCount(record);
+        public void setMaxScrollX(AccessibilityRecord record, int maxScrollX) {
+            record.setMaxScrollX(maxScrollX);
         }
 
         @Override
-        public CharSequence getBeforeText(Object record) {
-            return AccessibilityRecordCompatIcs.getBeforeText(record);
-        }
-
-        @Override
-        public CharSequence getClassName(Object record) {
-            return AccessibilityRecordCompatIcs.getClassName(record);
-        }
-
-        @Override
-        public CharSequence getContentDescription(Object record) {
-            return AccessibilityRecordCompatIcs.getContentDescription(record);
-        }
-
-        @Override
-        public int getCurrentItemIndex(Object record) {
-            return AccessibilityRecordCompatIcs.getCurrentItemIndex(record);
-        }
-
-        @Override
-        public int getFromIndex(Object record) {
-            return AccessibilityRecordCompatIcs.getFromIndex(record);
-        }
-
-        @Override
-        public int getItemCount(Object record) {
-            return AccessibilityRecordCompatIcs.getItemCount(record);
-        }
-
-        @Override
-        public Parcelable getParcelableData(Object record) {
-            return AccessibilityRecordCompatIcs.getParcelableData(record);
-        }
-
-        @Override
-        public int getRemovedCount(Object record) {
-            return AccessibilityRecordCompatIcs.getRemovedCount(record);
-        }
-
-        @Override
-        public int getScrollX(Object record) {
-            return AccessibilityRecordCompatIcs.getScrollX(record);
-        }
-
-        @Override
-        public int getScrollY(Object record) {
-            return AccessibilityRecordCompatIcs.getScrollY(record);
-        }
-
-        @Override
-        public AccessibilityNodeInfoCompat getSource(Object record) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
-                    AccessibilityRecordCompatIcs.getSource(record));
-        }
-
-        @Override
-        public List<CharSequence> getText(Object record) {
-            return AccessibilityRecordCompatIcs.getText(record);
-        }
-
-        @Override
-        public int getToIndex(Object record) {
-            return AccessibilityRecordCompatIcs.getToIndex(record);
-        }
-
-        @Override
-        public int getWindowId(Object record) {
-            return AccessibilityRecordCompatIcs.getWindowId(record);
-        }
-
-        @Override
-        public boolean isChecked(Object record) {
-            return AccessibilityRecordCompatIcs.isChecked(record);
-        }
-
-        @Override
-        public boolean isEnabled(Object record) {
-            return AccessibilityRecordCompatIcs.isEnabled(record);
-        }
-
-        @Override
-        public boolean isFullScreen(Object record) {
-            return AccessibilityRecordCompatIcs.isFullScreen(record);
-        }
-
-        @Override
-        public boolean isPassword(Object record) {
-            return AccessibilityRecordCompatIcs.isPassword(record);
-        }
-
-        @Override
-        public boolean isScrollable(Object record) {
-            return AccessibilityRecordCompatIcs.isScrollable(record);
-        }
-
-        @Override
-        public void recycle(Object record) {
-            AccessibilityRecordCompatIcs.recycle(record);
-        }
-
-        @Override
-        public void setAddedCount(Object record, int addedCount) {
-            AccessibilityRecordCompatIcs.setAddedCount(record, addedCount);
-        }
-
-        @Override
-        public void setBeforeText(Object record, CharSequence beforeText) {
-            AccessibilityRecordCompatIcs.setBeforeText(record, beforeText);
-        }
-
-        @Override
-        public void setChecked(Object record, boolean isChecked) {
-            AccessibilityRecordCompatIcs.setChecked(record, isChecked);
-        }
-
-        @Override
-        public void setClassName(Object record, CharSequence className) {
-            AccessibilityRecordCompatIcs.setClassName(record, className);
-        }
-
-        @Override
-        public void setContentDescription(Object record, CharSequence contentDescription) {
-            AccessibilityRecordCompatIcs.setContentDescription(record, contentDescription);
-        }
-
-        @Override
-        public void setCurrentItemIndex(Object record, int currentItemIndex) {
-            AccessibilityRecordCompatIcs.setCurrentItemIndex(record, currentItemIndex);
-        }
-
-        @Override
-        public void setEnabled(Object record, boolean isEnabled) {
-            AccessibilityRecordCompatIcs.setEnabled(record, isEnabled);
-        }
-
-        @Override
-        public void setFromIndex(Object record, int fromIndex) {
-            AccessibilityRecordCompatIcs.setFromIndex(record, fromIndex);
-        }
-
-        @Override
-        public void setFullScreen(Object record, boolean isFullScreen) {
-            AccessibilityRecordCompatIcs.setFullScreen(record, isFullScreen);
-        }
-
-        @Override
-        public void setItemCount(Object record, int itemCount) {
-            AccessibilityRecordCompatIcs.setItemCount(record, itemCount);
-        }
-
-        @Override
-        public void setParcelableData(Object record, Parcelable parcelableData) {
-            AccessibilityRecordCompatIcs.setParcelableData(record, parcelableData);
-        }
-
-        @Override
-        public void setPassword(Object record, boolean isPassword) {
-            AccessibilityRecordCompatIcs.setPassword(record, isPassword);
-        }
-
-        @Override
-        public void setRemovedCount(Object record, int removedCount) {
-            AccessibilityRecordCompatIcs.setRemovedCount(record, removedCount);
-        }
-
-        @Override
-        public void setScrollX(Object record, int scrollX) {
-            AccessibilityRecordCompatIcs.setScrollX(record, scrollX);
-        }
-
-        @Override
-        public void setScrollY(Object record, int scrollY) {
-            AccessibilityRecordCompatIcs.setScrollY(record, scrollY);
-        }
-
-        @Override
-        public void setScrollable(Object record, boolean scrollable) {
-            AccessibilityRecordCompatIcs.setScrollable(record, scrollable);
-        }
-
-        @Override
-        public void setSource(Object record, View source) {
-            AccessibilityRecordCompatIcs.setSource(record, source);
-        }
-
-        @Override
-        public void setToIndex(Object record, int toIndex) {
-            AccessibilityRecordCompatIcs.setToIndex(record, toIndex);
+        public void setMaxScrollY(AccessibilityRecord record, int maxScrollY) {
+            record.setMaxScrollY(maxScrollY);
         }
     }
 
-    static class AccessibilityRecordIcsMr1Impl extends AccessibilityRecordIcsImpl {
+    @RequiresApi(16)
+    static class AccessibilityRecordCompatApi16Impl extends AccessibilityRecordCompatApi15Impl {
         @Override
-        public int getMaxScrollX(Object record) {
-            return AccessibilityRecordCompatIcsMr1.getMaxScrollX(record);
-        }
-
-        @Override
-        public int getMaxScrollY(Object record) {
-            return AccessibilityRecordCompatIcsMr1.getMaxScrollY(record);
-        }
-
-        @Override
-        public void setMaxScrollX(Object record, int maxScrollX) {
-            AccessibilityRecordCompatIcsMr1.setMaxScrollX(record, maxScrollX);
-        }
-
-        @Override
-        public void setMaxScrollY(Object record, int maxScrollY) {
-            AccessibilityRecordCompatIcsMr1.setMaxScrollY(record, maxScrollY);
-        }
-    }
-
-    static class AccessibilityRecordJellyBeanImpl extends AccessibilityRecordIcsMr1Impl {
-        @Override
-        public void setSource(Object record, View root, int virtualDescendantId) {
-            AccessibilityRecordCompatJellyBean.setSource(record, root, virtualDescendantId);
+        public void setSource(AccessibilityRecord record, View root, int virtualDescendantId) {
+            record.setSource(root, virtualDescendantId);
         }
     }
 
     static {
         if (Build.VERSION.SDK_INT >= 16) { // JellyBean
-            IMPL = new AccessibilityRecordJellyBeanImpl();
+            IMPL = new AccessibilityRecordCompatApi16Impl();
         } else if (Build.VERSION.SDK_INT >= 15) {  // ICS MR1
-            IMPL = new AccessibilityRecordIcsMr1Impl();
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new AccessibilityRecordIcsImpl();
+            IMPL = new AccessibilityRecordCompatApi15Impl();
         } else {
-            IMPL = new AccessibilityRecordStubImpl();
+            IMPL = new AccessibilityRecordCompatBaseImpl();
         }
     }
 
-    private static final AccessibilityRecordImpl IMPL;
+    private static final AccessibilityRecordCompatBaseImpl IMPL;
 
-    private final Object mRecord;
+    private final AccessibilityRecord mRecord;
 
     /**
      * @deprecated This is not type safe. If you want to modify an
@@ -573,7 +104,7 @@
      */
     @Deprecated
     public AccessibilityRecordCompat(Object record) {
-        mRecord = record;
+        mRecord = (AccessibilityRecord) record;
     }
 
     /**
@@ -593,9 +124,12 @@
      * given record.
      *
      * @return An instance.
+     *
+     * @deprecated Use {@link AccessibilityRecord#obtain(AccessibilityRecord)} directly.
      */
+    @Deprecated
     public static AccessibilityRecordCompat obtain(AccessibilityRecordCompat record) {
-       return new AccessibilityRecordCompat(IMPL.obtain(record.mRecord));
+        return new AccessibilityRecordCompat(AccessibilityRecord.obtain(record.mRecord));
     }
 
     /**
@@ -603,9 +137,12 @@
      * instantiated.
      *
      * @return An instance.
+     *
+     * @deprecated Use {@link AccessibilityRecord#obtain()} directly.
      */
+    @Deprecated
     public static AccessibilityRecordCompat obtain() {
-        return new AccessibilityRecordCompat(IMPL.obtain());
+        return new AccessibilityRecordCompat(AccessibilityRecord.obtain());
     }
 
     /**
@@ -614,9 +151,12 @@
      * @param source The source.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setSource(View)} directly.
      */
+    @Deprecated
     public void setSource(View source) {
-        IMPL.setSource(mRecord, source);
+        mRecord.setSource(source);
     }
 
     /**
@@ -632,9 +172,32 @@
      *
      * @param root The root of the virtual subtree.
      * @param virtualDescendantId The id of the virtual descendant.
+     *
+     * @deprecated Use {@link #setSource(AccessibilityRecord, View, int)} instead.
      */
+    @Deprecated
     public void setSource(View root, int virtualDescendantId) {
-        IMPL.setSource(mRecord, root, virtualDescendantId);
+        AccessibilityRecordCompat.setSource(mRecord, root, virtualDescendantId);
+    }
+
+    /**
+     * Sets the source to be a virtual descendant of the given <code>root</code>.
+     * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+     * is set as the source.
+     * <p>
+     * A virtual descendant is an imaginary View that is reported as a part of the view
+     * hierarchy for accessibility purposes. This enables custom views that draw complex
+     * content to report them selves as a tree of virtual views, thus conveying their
+     * logical structure.
+     * </p>
+     *
+     * @param record The {@link AccessibilityRecord} instance to use.
+     * @param root The root of the virtual subtree.
+     * @param virtualDescendantId The id of the virtual descendant.
+     */
+    public static void setSource(@NonNull AccessibilityRecord record, View root,
+            int virtualDescendantId) {
+        IMPL.setSource(record, root, virtualDescendantId);
     }
 
     /**
@@ -648,27 +211,36 @@
      *</p>
      *
      * @return The info of the source.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getSource()} directly.
      */
+    @Deprecated
     public AccessibilityNodeInfoCompat getSource() {
-        return IMPL.getSource(mRecord);
+        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mRecord.getSource());
     }
 
     /**
      * Gets the id of the window from which the event comes from.
      *
      * @return The window id.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getWindowId()} directly.
      */
+    @Deprecated
     public int getWindowId() {
-        return IMPL.getWindowId(mRecord);
+        return mRecord.getWindowId();
     }
 
     /**
      * Gets if the source is checked.
      *
      * @return True if the view is checked, false otherwise.
+     *
+     * @deprecated Use {@link AccessibilityRecord#isChecked()} directly.
      */
+    @Deprecated
     public boolean isChecked() {
-        return IMPL.isChecked(mRecord);
+        return mRecord.isChecked();
     }
 
     /**
@@ -677,18 +249,24 @@
      * @param isChecked True if the view is checked, false otherwise.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setChecked(boolean)} directly.
      */
+    @Deprecated
     public void setChecked(boolean isChecked) {
-        IMPL.setChecked(mRecord, isChecked);
+        mRecord.setChecked(isChecked);
     }
 
     /**
      * Gets if the source is enabled.
      *
      * @return True if the view is enabled, false otherwise.
+     *
+     * @deprecated Use {@link AccessibilityRecord#isEnabled()} directly.
      */
+    @Deprecated
     public boolean isEnabled() {
-        return IMPL.isEnabled(mRecord);
+        return mRecord.isEnabled();
     }
 
     /**
@@ -697,18 +275,24 @@
      * @param isEnabled True if the view is enabled, false otherwise.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#isEnabled()} directly.
      */
+    @Deprecated
     public void setEnabled(boolean isEnabled) {
-        IMPL.setEnabled(mRecord, isEnabled);
+        mRecord.setEnabled(isEnabled);
     }
 
     /**
      * Gets if the source is a password field.
      *
      * @return True if the view is a password field, false otherwise.
+     *
+     * @deprecated Use {@link AccessibilityRecord#isPassword()} directly.
      */
+    @Deprecated
     public boolean isPassword() {
-        return IMPL.isPassword(mRecord);
+        return mRecord.isPassword();
     }
 
     /**
@@ -717,18 +301,24 @@
      * @param isPassword True if the view is a password field, false otherwise.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setPassword(boolean)} directly.
      */
+    @Deprecated
     public void setPassword(boolean isPassword) {
-        IMPL.setPassword(mRecord, isPassword);
+        mRecord.setPassword(isPassword);
     }
 
     /**
      * Gets if the source is taking the entire screen.
      *
      * @return True if the source is full screen, false otherwise.
+     *
+     * @deprecated Use {@link AccessibilityRecord#isFullScreen()} directly.
      */
+    @Deprecated
     public boolean isFullScreen() {
-        return IMPL.isFullScreen(mRecord);
+        return mRecord.isFullScreen();
     }
 
     /**
@@ -737,18 +327,24 @@
      * @param isFullScreen True if the source is full screen, false otherwise.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setFullScreen(boolean)} directly.
      */
+    @Deprecated
     public void setFullScreen(boolean isFullScreen) {
-        IMPL.setFullScreen(mRecord, isFullScreen);
+        mRecord.setFullScreen(isFullScreen);
     }
 
     /**
      * Gets if the source is scrollable.
      *
      * @return True if the source is scrollable, false otherwise.
+     *
+     * @deprecated Use {@link AccessibilityRecord#isScrollable()} directly.
      */
+    @Deprecated
     public boolean isScrollable() {
-        return IMPL.isScrollable(mRecord);
+        return mRecord.isScrollable();
     }
 
     /**
@@ -757,18 +353,24 @@
      * @param scrollable True if the source is scrollable, false otherwise.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setScrollable(boolean)} directly.
      */
+    @Deprecated
     public void setScrollable(boolean scrollable) {
-        IMPL.setScrollable(mRecord, scrollable);
+        mRecord.setScrollable(scrollable);
     }
 
     /**
      * Gets the number of items that can be visited.
      *
      * @return The number of items.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getItemCount()} directly.
      */
+    @Deprecated
     public int getItemCount() {
-        return IMPL.getItemCount(mRecord);
+        return mRecord.getItemCount();
     }
 
     /**
@@ -777,18 +379,24 @@
      * @param itemCount The number of items.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setItemCount(int)} directly.
      */
+    @Deprecated
     public void setItemCount(int itemCount) {
-        IMPL.setItemCount(mRecord, itemCount);
+        mRecord.setItemCount(itemCount);
     }
 
     /**
      * Gets the index of the source in the list of items the can be visited.
      *
      * @return The current item index.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getCurrentItemIndex()} directly.
      */
+    @Deprecated
     public int getCurrentItemIndex() {
-        return IMPL.getCurrentItemIndex(mRecord);
+        return mRecord.getCurrentItemIndex();
     }
 
     /**
@@ -797,9 +405,12 @@
      * @param currentItemIndex The current item index.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setCurrentItemIndex(int)} directly.
      */
+    @Deprecated
     public void setCurrentItemIndex(int currentItemIndex) {
-        IMPL.setCurrentItemIndex(mRecord, currentItemIndex);
+        mRecord.setCurrentItemIndex(currentItemIndex);
     }
 
     /**
@@ -809,9 +420,12 @@
      *
      * @return The index of the first character or selection
      *        start or the first visible item.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getFromIndex()} directly.
      */
+    @Deprecated
     public int getFromIndex() {
-        return IMPL.getFromIndex(mRecord);
+        return mRecord.getFromIndex();
     }
 
     /**
@@ -823,9 +437,12 @@
      *        start or the first visible item.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setFromIndex(int)} directly.
      */
+    @Deprecated
     public void setFromIndex(int fromIndex) {
-        IMPL.setFromIndex(mRecord, fromIndex);
+        mRecord.setFromIndex(fromIndex);
     }
 
     /**
@@ -833,9 +450,12 @@
      * visible item when scrolling.
      *
      * @return The index of selection end or last item index.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getToIndex()} directly.
      */
+    @Deprecated
     public int getToIndex() {
-        return IMPL.getToIndex(mRecord);
+        return mRecord.getToIndex();
     }
 
     /**
@@ -843,89 +463,160 @@
      * visible item when scrolling.
      *
      * @param toIndex The index of selection end or last item index.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setToIndex(int)} directly.
      */
+    @Deprecated
     public void setToIndex(int toIndex) {
-        IMPL.setToIndex(mRecord, toIndex);
+        mRecord.setToIndex(toIndex);
     }
 
     /**
      * Gets the scroll offset of the source left edge in pixels.
      *
      * @return The scroll.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getScrollX()} directly.
      */
+    @Deprecated
     public int getScrollX() {
-        return IMPL.getScrollX(mRecord);
+        return mRecord.getScrollX();
     }
 
     /**
      * Sets the scroll offset of the source left edge in pixels.
      *
      * @param scrollX The scroll.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setScrollX(int)} directly.
      */
+    @Deprecated
     public void setScrollX(int scrollX) {
-        IMPL.setScrollX(mRecord, scrollX);
+        mRecord.setScrollX(scrollX);
     }
 
     /**
      * Gets the scroll offset of the source top edge in pixels.
      *
      * @return The scroll.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getScrollY()} directly.
      */
+    @Deprecated
     public int getScrollY() {
-        return IMPL.getScrollY(mRecord);
+        return mRecord.getScrollY();
     }
 
     /**
      * Sets the scroll offset of the source top edge in pixels.
      *
      * @param scrollY The scroll.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setScrollY(int)} directly.
      */
+    @Deprecated
     public void setScrollY(int scrollY) {
-        IMPL.setScrollY(mRecord, scrollY);
+        mRecord.setScrollY(scrollY);
     }
 
     /**
      * Gets the max scroll offset of the source left edge in pixels.
      *
      * @return The max scroll.
+     *
+     * @deprecated Use {@link #getMaxScrollX(AccessibilityRecord)} instead.
      */
+    @Deprecated
     public int getMaxScrollX() {
-        return IMPL.getMaxScrollX(mRecord);
+        return AccessibilityRecordCompat.getMaxScrollX(mRecord);
     }
+
+    /**
+     * Gets the max scroll offset of the source left edge in pixels.
+     *
+     * @param record The {@link AccessibilityRecord} instance to use.
+     * @return The max scroll.
+     */
+    public static int getMaxScrollX(AccessibilityRecord record) {
+        return IMPL.getMaxScrollX(record);
+    }
+
     /**
      * Sets the max scroll offset of the source left edge in pixels.
      *
      * @param maxScrollX The max scroll.
+     *
+     * @deprecated Use {@link #setMaxScrollX(AccessibilityRecord, int)} instead.
      */
+    @Deprecated
     public void setMaxScrollX(int maxScrollX) {
-        IMPL.setMaxScrollX(mRecord, maxScrollX);
+        AccessibilityRecordCompat.setMaxScrollX(mRecord, maxScrollX);
+    }
+
+    /**
+     * Sets the max scroll offset of the source left edge in pixels.
+     *
+     * @param record The {@link AccessibilityRecord} instance to use.
+     * @param maxScrollX The max scroll.
+     */
+    public static void setMaxScrollX(AccessibilityRecord record, int maxScrollX) {
+        IMPL.setMaxScrollX(record, maxScrollX);
     }
 
     /**
      * Gets the max scroll offset of the source top edge in pixels.
      *
      * @return The max scroll.
+     *
+     * @deprecated Use {@link #getMaxScrollY(AccessibilityRecord)} instead.
      */
+    @Deprecated
     public int getMaxScrollY() {
-        return IMPL.getMaxScrollY(mRecord);
+        return AccessibilityRecordCompat.getMaxScrollY(mRecord);
+    }
+
+    /**
+     * Gets the max scroll offset of the source top edge in pixels.
+     *
+     * @param record The {@link AccessibilityRecord} instance to use.
+     * @return The max scroll.
+     */
+    public static int getMaxScrollY(AccessibilityRecord record) {
+        return IMPL.getMaxScrollY(record);
     }
 
     /**
      * Sets the max scroll offset of the source top edge in pixels.
      *
      * @param maxScrollY The max scroll.
+     *
+     * @deprecated Use {@link #setMaxScrollY(AccessibilityRecord, int)} instead.
      */
+    @Deprecated
     public void setMaxScrollY(int maxScrollY) {
-        IMPL.setMaxScrollY(mRecord, maxScrollY);
+        AccessibilityRecordCompat.setMaxScrollY(mRecord, maxScrollY);
+    }
+
+    /**
+     * Sets the max scroll offset of the source top edge in pixels.
+     *
+     * @param record The {@link AccessibilityRecord} instance to use.
+     * @param maxScrollY The max scroll.
+     */
+    public static void setMaxScrollY(AccessibilityRecord record, int maxScrollY) {
+        IMPL.setMaxScrollY(record, maxScrollY);
     }
 
     /**
      * Gets the number of added characters.
      *
      * @return The number of added characters.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getAddedCount()} directly.
      */
+    @Deprecated
     public int getAddedCount() {
-        return IMPL.getAddedCount(mRecord);
+        return mRecord.getAddedCount();
     }
 
     /**
@@ -934,18 +625,24 @@
      * @param addedCount The number of added characters.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setAddedCount(int)} directly.
      */
+    @Deprecated
     public void setAddedCount(int addedCount) {
-        IMPL.setAddedCount(mRecord, addedCount);
+        mRecord.setAddedCount(addedCount);
     }
 
     /**
      * Gets the number of removed characters.
      *
      * @return The number of removed characters.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getRemovedCount()} directly.
      */
+    @Deprecated
     public int getRemovedCount() {
-        return IMPL.getRemovedCount(mRecord);
+        return mRecord.getRemovedCount();
     }
 
     /**
@@ -954,18 +651,24 @@
      * @param removedCount The number of removed characters.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setRemovedCount(int)} directly.
      */
+    @Deprecated
     public void setRemovedCount(int removedCount) {
-        IMPL.setRemovedCount(mRecord, removedCount);
+        mRecord.setRemovedCount(removedCount);
     }
 
     /**
      * Gets the class name of the source.
      *
      * @return The class name.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getClassName()} directly.
      */
+    @Deprecated
     public CharSequence getClassName() {
-        return IMPL.getClassName(mRecord);
+        return mRecord.getClassName();
     }
 
     /**
@@ -974,9 +677,12 @@
      * @param className The lass name.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setClassName(CharSequence)} directly.
      */
+    @Deprecated
     public void setClassName(CharSequence className) {
-        IMPL.setClassName(mRecord, className);
+        mRecord.setClassName(className);
     }
 
     /**
@@ -984,18 +690,24 @@
      * of the text. Specifically, the lower the index the higher the priority.
      *
      * @return The text.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getText()} directly.
      */
+    @Deprecated
     public List<CharSequence> getText() {
-        return IMPL.getText(mRecord);
+        return mRecord.getText();
     }
 
     /**
      * Sets the text before a change.
      *
      * @return The text before the change.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getBeforeText()} directly.
      */
+    @Deprecated
     public CharSequence getBeforeText() {
-        return IMPL.getBeforeText(mRecord);
+        return mRecord.getBeforeText();
     }
 
     /**
@@ -1004,18 +716,24 @@
      * @param beforeText The text before the change.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setBeforeText(CharSequence)} directly.
      */
+    @Deprecated
     public void setBeforeText(CharSequence beforeText) {
-        IMPL.setBeforeText(mRecord, beforeText);
+        mRecord.setBeforeText(beforeText);
     }
 
     /**
      * Gets the description of the source.
      *
      * @return The description.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getContentDescription()} directly.
      */
+    @Deprecated
     public CharSequence getContentDescription() {
-        return IMPL.getContentDescription(mRecord);
+        return mRecord.getContentDescription();
     }
 
     /**
@@ -1024,18 +742,24 @@
      * @param contentDescription The description.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setContentDescription(CharSequence)} directly.
      */
+    @Deprecated
     public void setContentDescription(CharSequence contentDescription) {
-        IMPL.setContentDescription(mRecord, contentDescription);
+        mRecord.setContentDescription(contentDescription);
     }
 
     /**
      * Gets the {@link Parcelable} data.
      *
      * @return The parcelable data.
+     *
+     * @deprecated Use {@link AccessibilityRecord#getParcelableData()} directly.
      */
+    @Deprecated
     public Parcelable getParcelableData() {
-        return IMPL.getParcelableData(mRecord);
+        return mRecord.getParcelableData();
     }
 
     /**
@@ -1044,9 +768,12 @@
      * @param parcelableData The parcelable data.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     *
+     * @deprecated Use {@link AccessibilityRecord#setParcelableData(Parcelable)} directly.
      */
+    @Deprecated
     public void setParcelableData(Parcelable parcelableData) {
-        IMPL.setParcelableData(mRecord, parcelableData);
+        mRecord.setParcelableData(parcelableData);
     }
 
     /**
@@ -1057,17 +784,27 @@
      * </p>
      *
      * @throws IllegalStateException If the record is already recycled.
+     *
+     * @deprecated Use {@link AccessibilityRecord#recycle()} directly.
      */
+    @Deprecated
     public void recycle() {
-        IMPL.recycle(mRecord);
+        mRecord.recycle();
     }
 
+    /**
+     * @deprecated Use {@link AccessibilityRecord#hashCode()} directly.
+     */
+    @Deprecated
     @Override
     public int hashCode() {
         return (mRecord == null) ? 0 : mRecord.hashCode();
     }
 
-
+    /**
+     * @deprecated Use {@link AccessibilityRecord} directly.
+     */
+    @Deprecated
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
index bec1dd5..be5d29f 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
@@ -16,219 +16,15 @@
 
 package android.support.v4.view.accessibility;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.graphics.Rect;
-import android.os.Build;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 /**
- * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo}.
  */
 public class AccessibilityWindowInfoCompat {
-
-    private interface AccessibilityWindowInfoImpl {
-        Object obtain();
-        Object obtain(Object info);
-        int getType(Object info);
-        int getLayer(Object info);
-        Object getRoot(Object info);
-        Object getParent(Object info);
-        int getId(Object info);
-        void getBoundsInScreen(Object info, Rect outBounds);
-        boolean isActive(Object info);
-        boolean isFocused(Object info);
-        boolean isAccessibilityFocused(Object info);
-        int getChildCount(Object info);
-        Object getChild(Object info, int index);
-        CharSequence getTitle(Object info);
-        Object getAnchor(Object info);
-        void recycle(Object info);
-    }
-
-    private static class AccessibilityWindowInfoStubImpl implements AccessibilityWindowInfoImpl {
-
-        AccessibilityWindowInfoStubImpl() {
-        }
-
-        @Override
-        public Object obtain() {
-            return null;
-        }
-
-        @Override
-        public Object obtain(Object info) {
-            return null;
-        }
-
-        @Override
-        public int getType(Object info) {
-            return UNDEFINED;
-        }
-
-        @Override
-        public int getLayer(Object info) {
-            return UNDEFINED;
-        }
-
-        @Override
-        public Object getRoot(Object info) {
-            return null;
-        }
-
-        @Override
-        public Object getParent(Object info) {
-            return null;
-        }
-
-        @Override
-        public int getId(Object info) {
-            return UNDEFINED;
-        }
-
-        @Override
-        public void getBoundsInScreen(Object info, Rect outBounds) {
-        }
-
-        @Override
-        public boolean isActive(Object info) {
-            return true;
-        }
-
-        @Override
-        public boolean isFocused(Object info) {
-            return true;
-        }
-
-        @Override
-        public boolean isAccessibilityFocused(Object info) {
-            return true;
-        }
-
-        @Override
-        public int getChildCount(Object info) {
-            return 0;
-        }
-
-        @Override
-        public Object getChild(Object info, int index) {
-            return null;
-        }
-
-        @Override
-        public void recycle(Object info) {
-        }
-
-        @Override
-        public CharSequence getTitle(Object info) {
-            return null;
-        }
-
-        @Override
-        public Object getAnchor(Object info) {
-            return null;
-        }
-    }
-
-    private static class AccessibilityWindowInfoApi21Impl extends AccessibilityWindowInfoStubImpl {
-        AccessibilityWindowInfoApi21Impl() {
-        }
-
-        @Override
-        public Object obtain() {
-            return AccessibilityWindowInfoCompatApi21.obtain();
-        }
-
-        @Override
-        public Object obtain(Object info) {
-            return AccessibilityWindowInfoCompatApi21.obtain(info);
-        }
-
-        @Override
-        public int getType(Object info) {
-            return AccessibilityWindowInfoCompatApi21.getType(info);
-        }
-
-        @Override
-        public int getLayer(Object info) {
-            return AccessibilityWindowInfoCompatApi21.getLayer(info);
-        }
-
-        @Override
-        public Object getRoot(Object info) {
-            return AccessibilityWindowInfoCompatApi21.getRoot(info);
-        }
-
-        @Override
-        public Object getParent(Object info) {
-            return AccessibilityWindowInfoCompatApi21.getParent(info);
-        }
-
-        @Override
-        public int getId(Object info) {
-            return AccessibilityWindowInfoCompatApi21.getId(info);
-        }
-
-        @Override
-        public void getBoundsInScreen(Object info, Rect outBounds) {
-            AccessibilityWindowInfoCompatApi21.getBoundsInScreen(info, outBounds);
-        }
-
-        @Override
-        public boolean isActive(Object info) {
-            return AccessibilityWindowInfoCompatApi21.isActive(info);
-        }
-
-        @Override
-        public boolean isFocused(Object info) {
-            return AccessibilityWindowInfoCompatApi21.isFocused(info);
-        }
-
-        @Override
-        public boolean isAccessibilityFocused(Object info) {
-            return AccessibilityWindowInfoCompatApi21.isAccessibilityFocused(info);
-        }
-
-        @Override
-        public int getChildCount(Object info) {
-            return AccessibilityWindowInfoCompatApi21.getChildCount(info);
-        }
-
-        @Override
-        public Object getChild(Object info, int index) {
-            return AccessibilityWindowInfoCompatApi21.getChild(info, index);
-        }
-
-        @Override
-        public void recycle(Object info) {
-            AccessibilityWindowInfoCompatApi21.recycle(info);
-        }
-    }
-
-    private static class AccessibilityWindowInfoApi24Impl extends AccessibilityWindowInfoApi21Impl {
-        AccessibilityWindowInfoApi24Impl() {
-        }
-
-        @Override
-        public CharSequence getTitle(Object info) {
-            return AccessibilityWindowInfoCompatApi24.getTitle(info);
-        }
-
-        @Override
-        public Object getAnchor(Object info) {
-            return AccessibilityWindowInfoCompatApi24.getAnchor(info);
-        }
-    }
-
-    static {
-        if (Build.VERSION.SDK_INT >= 24) {
-            IMPL = new AccessibilityWindowInfoApi24Impl();
-        } else  if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new AccessibilityWindowInfoApi21Impl();
-        } else {
-            IMPL = new AccessibilityWindowInfoStubImpl();
-        }
-    }
-
-    private static final AccessibilityWindowInfoImpl IMPL;
     private Object mInfo;
 
     private static final int UNDEFINED = -1;
@@ -298,7 +94,11 @@
      * @see #TYPE_ACCESSIBILITY_OVERLAY
      */
     public int getType() {
-        return IMPL.getType(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).getType();
+        } else {
+            return UNDEFINED;
+        }
     }
 
     /**
@@ -308,7 +108,11 @@
      * @return The window layer.
      */
     public int getLayer() {
-        return IMPL.getLayer(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).getLayer();
+        } else {
+            return UNDEFINED;
+        }
     }
 
     /**
@@ -317,7 +121,12 @@
      * @return The root node.
      */
     public AccessibilityNodeInfoCompat getRoot() {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getRoot(mInfo));
+        if (SDK_INT >= 21) {
+            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
+                    ((AccessibilityWindowInfo) mInfo).getRoot());
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -326,7 +135,11 @@
      * @return The parent window.
      */
     public AccessibilityWindowInfoCompat getParent() {
-        return wrapNonNullInstance(IMPL.getParent(mInfo));
+        if (SDK_INT >= 21) {
+            return wrapNonNullInstance(((AccessibilityWindowInfo) mInfo).getParent());
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -335,7 +148,11 @@
      * @return windowId The window id.
      */
     public int getId() {
-        return IMPL.getId(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).getId();
+        } else {
+            return UNDEFINED;
+        }
     }
 
     /**
@@ -344,7 +161,9 @@
      * @param outBounds The out window bounds.
      */
     public void getBoundsInScreen(Rect outBounds) {
-        IMPL.getBoundsInScreen(mInfo, outBounds);
+        if (SDK_INT >= 21) {
+            ((AccessibilityWindowInfo) mInfo).getBoundsInScreen(outBounds);
+        }
     }
 
     /**
@@ -355,7 +174,11 @@
      * @return Whether this is the active window.
      */
     public boolean isActive() {
-        return IMPL.isActive(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).isActive();
+        } else {
+            return true;
+        }
     }
 
     /**
@@ -364,7 +187,11 @@
      * @return Whether has input focus.
      */
     public boolean isFocused() {
-        return IMPL.isFocused(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).isFocused();
+        } else {
+            return true;
+        }
     }
 
     /**
@@ -373,7 +200,11 @@
      * @return Whether has accessibility focus.
      */
     public boolean isAccessibilityFocused() {
-        return IMPL.isAccessibilityFocused(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).isAccessibilityFocused();
+        } else {
+            return true;
+        }
     }
 
     /**
@@ -382,7 +213,11 @@
      * @return The child count.
      */
     public int getChildCount() {
-        return IMPL.getChildCount(mInfo);
+        if (SDK_INT >= 21) {
+            return ((AccessibilityWindowInfo) mInfo).getChildCount();
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -392,7 +227,11 @@
      * @return The child.
      */
     public AccessibilityWindowInfoCompat getChild(int index) {
-        return wrapNonNullInstance(IMPL.getChild(mInfo, index));
+        if (SDK_INT >= 21) {
+            return wrapNonNullInstance(((AccessibilityWindowInfo) mInfo).getChild(index));
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -402,7 +241,11 @@
      * explicitly set, or {@code null} if neither is available.
      */
     public CharSequence getTitle() {
-        return IMPL.getTitle(mInfo);
+        if (SDK_INT >= 24) {
+            return ((AccessibilityWindowInfo) mInfo).getTitle();
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -411,7 +254,12 @@
      * @return The anchor node, or {@code null} if none exists.
      */
     public AccessibilityNodeInfoCompat getAnchor() {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getAnchor(mInfo));
+        if (SDK_INT >= 24) {
+            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
+                    ((AccessibilityWindowInfo) mInfo).getAnchor());
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -421,7 +269,11 @@
      * @return An instance.
      */
     public static AccessibilityWindowInfoCompat obtain() {
-        return wrapNonNullInstance(IMPL.obtain());
+        if (SDK_INT >= 21) {
+            return wrapNonNullInstance(AccessibilityWindowInfo.obtain());
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -433,7 +285,14 @@
      * @return An instance.
      */
     public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) {
-        return info == null ? null : wrapNonNullInstance(IMPL.obtain(info.mInfo));
+        if (SDK_INT >= 21) {
+            return info == null
+                    ? null
+                    : wrapNonNullInstance(
+                            AccessibilityWindowInfo.obtain((AccessibilityWindowInfo) info.mInfo));
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -445,7 +304,9 @@
      * @throws IllegalStateException If the info is already recycled.
      */
     public void recycle() {
-        IMPL.recycle(mInfo);
+        if (SDK_INT >= 21) {
+            ((AccessibilityWindowInfo) mInfo).recycle();
+        }
     }
 
     @Override
diff --git a/compat/java/android/support/v4/view/animation/PathInterpolatorCompat.java b/compat/java/android/support/v4/view/animation/PathInterpolatorCompat.java
index 3451fe0..767b977 100644
--- a/compat/java/android/support/v4/view/animation/PathInterpolatorCompat.java
+++ b/compat/java/android/support/v4/view/animation/PathInterpolatorCompat.java
@@ -19,6 +19,7 @@
 import android.graphics.Path;
 import android.os.Build;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 /**
  * Helper for creating path-based {@link Interpolator} instances. On API 21 or newer, the
@@ -45,9 +46,9 @@
      */
     public static Interpolator create(Path path) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return PathInterpolatorCompatApi21.create(path);
+            return new PathInterpolator(path);
         }
-        return PathInterpolatorCompatBase.create(path);
+        return new PathInterpolatorApi14(path);
     }
 
     /**
@@ -60,9 +61,9 @@
      */
     public static Interpolator create(float controlX, float controlY) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return PathInterpolatorCompatApi21.create(controlX, controlY);
+            return new PathInterpolator(controlX, controlY);
         }
-        return PathInterpolatorCompatBase.create(controlX, controlY);
+        return new PathInterpolatorApi14(controlX, controlY);
     }
 
     /**
@@ -78,8 +79,8 @@
     public static Interpolator create(float controlX1, float controlY1,
             float controlX2, float controlY2) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return PathInterpolatorCompatApi21.create(controlX1, controlY1, controlX2, controlY2);
+            return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
         }
-        return PathInterpolatorCompatBase.create(controlX1, controlY1, controlX2, controlY2);
+        return new PathInterpolatorApi14(controlX1, controlY1, controlX2, controlY2);
     }
 }
diff --git a/compat/java/android/support/v4/widget/AutoSizeableTextView.java b/compat/java/android/support/v4/widget/AutoSizeableTextView.java
new file mode 100644
index 0000000..c0b13e3
--- /dev/null
+++ b/compat/java/android/support/v4/widget/AutoSizeableTextView.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.util.TypedValue;
+
+/**
+ * Interface which allows a {@link android.widget.TextView} to receive background auto-sizing calls
+ * from {@link TextViewCompat} when running on API v26 devices or lower.
+ *
+ * @hide Internal use only
+ */
+@RestrictTo(LIBRARY_GROUP)
+public interface AutoSizeableTextView {
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds by using the default auto-size configuration.
+     *
+     * @param autoSizeTextType the type of auto-size. Must be one of
+     *        {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *        {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}
+     *
+     * @see #getAutoSizeTextType()
+     */
+    void setAutoSizeTextTypeWithDefaults(@TextViewCompat.AutoSizeTextType int autoSizeTextType);
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If all the configuration params are valid the type of auto-size is
+     * set to {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param autoSizeMinTextSize the minimum text size available for auto-size
+     * @param autoSizeMaxTextSize the maximum text size available for auto-size
+     * @param autoSizeStepGranularity the auto-size step granularity. It is used in conjunction with
+     *                                the minimum and maximum text size in order to build the set of
+     *                                text sizes the system uses to choose from when auto-sizing
+     * @param unit the desired dimension unit for all sizes above. See {@link TypedValue} for the
+     *             possible dimension units
+     *
+     * @throws IllegalArgumentException if any of the configuration params are invalid.
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     * @see #getAutoSizeMinTextSize()
+     * @see #getAutoSizeMaxTextSize()
+     * @see #getAutoSizeStepGranularity()
+     * @see #getAutoSizeTextAvailableSizes()
+     */
+    void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException;
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If at least one value from the <code>presetSizes</code> is valid
+     * then the type of auto-size is set to {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param presetSizes an {@code int} array of sizes in pixels
+     * @param unit the desired dimension unit for the preset sizes above. See {@link TypedValue} for
+     *             the possible dimension units
+     *
+     * @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid.
+     *_
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #getAutoSizeMinTextSize()
+     * @see #getAutoSizeMaxTextSize()
+     * @see #getAutoSizeTextAvailableSizes()
+     */
+    void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException;
+
+    /**
+     * Returns the type of auto-size set for this widget.
+     *
+     * @return an {@code int} corresponding to one of the auto-size types:
+     *         {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *         {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     */
+    @TextViewCompat.AutoSizeTextType
+    int getAutoSizeTextType();
+
+    /**
+     * @return the current auto-size step granularity in pixels.
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     */
+    int getAutoSizeStepGranularity();
+
+    /**
+     * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     */
+    int getAutoSizeMinTextSize();
+
+    /**
+     * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     */
+    int getAutoSizeMaxTextSize();
+
+    /**
+     * @return the current auto-size {@code int} sizes array (in pixels).
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     */
+    int[] getAutoSizeTextAvailableSizes();
+}
diff --git a/compat/java/android/support/v4/widget/CompoundButtonCompat.java b/compat/java/android/support/v4/widget/CompoundButtonCompat.java
index 52cdc49..a858b09 100644
--- a/compat/java/android/support/v4/widget/CompoundButtonCompat.java
+++ b/compat/java/android/support/v4/widget/CompoundButtonCompat.java
@@ -22,89 +22,113 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.util.Log;
 import android.widget.CompoundButton;
 
+import java.lang.reflect.Field;
+
 /**
- * Helper for accessing {@link android.widget.CompoundButton} methods introduced after
- * API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link android.widget.CompoundButton}.
  */
 public final class CompoundButtonCompat {
 
-    private static final CompoundButtonCompatImpl IMPL;
+    private static final CompoundButtonCompatBaseImpl IMPL;
 
     static {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk >= 23) {
-            IMPL = new Api23CompoundButtonImpl();
-        } else if (sdk >= 21) {
-            IMPL = new LollipopCompoundButtonImpl();
+        if (Build.VERSION.SDK_INT >= 23) {
+            IMPL = new CompoundButtonCompatApi23Impl();
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new CompoundButtonCompatApi21Impl();
         } else {
-            IMPL = new BaseCompoundButtonCompat();
+            IMPL = new CompoundButtonCompatBaseImpl();
         }
     }
 
-    interface CompoundButtonCompatImpl {
-        void setButtonTintList(CompoundButton button, ColorStateList tint);
-        ColorStateList getButtonTintList(CompoundButton button);
-        void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode);
-        PorterDuff.Mode getButtonTintMode(CompoundButton button);
-        Drawable getButtonDrawable(CompoundButton button);
+    static class CompoundButtonCompatBaseImpl {
+        private static final String TAG = "CompoundButtonCompat";
+
+        private static Field sButtonDrawableField;
+        private static boolean sButtonDrawableFieldFetched;
+
+        public void setButtonTintList(CompoundButton button, ColorStateList tint) {
+            if (button instanceof TintableCompoundButton) {
+                ((TintableCompoundButton) button).setSupportButtonTintList(tint);
+            }
+        }
+
+        public ColorStateList getButtonTintList(CompoundButton button) {
+            if (button instanceof TintableCompoundButton) {
+                return ((TintableCompoundButton) button).getSupportButtonTintList();
+            }
+            return null;
+        }
+
+        public void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
+            if (button instanceof TintableCompoundButton) {
+                ((TintableCompoundButton) button).setSupportButtonTintMode(tintMode);
+            }
+        }
+
+        public PorterDuff.Mode getButtonTintMode(CompoundButton button) {
+            if (button instanceof TintableCompoundButton) {
+                return ((TintableCompoundButton) button).getSupportButtonTintMode();
+            }
+            return null;
+        }
+
+        public Drawable getButtonDrawable(CompoundButton button) {
+            if (!sButtonDrawableFieldFetched) {
+                try {
+                    sButtonDrawableField = CompoundButton.class.getDeclaredField("mButtonDrawable");
+                    sButtonDrawableField.setAccessible(true);
+                } catch (NoSuchFieldException e) {
+                    Log.i(TAG, "Failed to retrieve mButtonDrawable field", e);
+                }
+                sButtonDrawableFieldFetched = true;
+            }
+
+            if (sButtonDrawableField != null) {
+                try {
+                    return (Drawable) sButtonDrawableField.get(button);
+                } catch (IllegalAccessException e) {
+                    Log.i(TAG, "Failed to get button drawable via reflection", e);
+                    sButtonDrawableField = null;
+                }
+            }
+            return null;
+        }
     }
 
-    static class BaseCompoundButtonCompat implements CompoundButtonCompatImpl {
+    @RequiresApi(21)
+    static class CompoundButtonCompatApi21Impl extends CompoundButtonCompatBaseImpl {
         @Override
         public void setButtonTintList(CompoundButton button, ColorStateList tint) {
-            CompoundButtonCompatGingerbread.setButtonTintList(button, tint);
+            button.setButtonTintList(tint);
         }
 
         @Override
         public ColorStateList getButtonTintList(CompoundButton button) {
-            return CompoundButtonCompatGingerbread.getButtonTintList(button);
+            return button.getButtonTintList();
         }
 
         @Override
         public void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
-            CompoundButtonCompatGingerbread.setButtonTintMode(button, tintMode);
+            button.setButtonTintMode(tintMode);
         }
 
         @Override
         public PorterDuff.Mode getButtonTintMode(CompoundButton button) {
-            return CompoundButtonCompatGingerbread.getButtonTintMode(button);
-        }
-
-        @Override
-        public Drawable getButtonDrawable(CompoundButton button) {
-            return CompoundButtonCompatGingerbread.getButtonDrawable(button);
+            return button.getButtonTintMode();
         }
     }
 
-    static class LollipopCompoundButtonImpl extends BaseCompoundButtonCompat {
-        @Override
-        public void setButtonTintList(CompoundButton button, ColorStateList tint) {
-            CompoundButtonCompatLollipop.setButtonTintList(button, tint);
-        }
-
-        @Override
-        public ColorStateList getButtonTintList(CompoundButton button) {
-            return CompoundButtonCompatLollipop.getButtonTintList(button);
-        }
-
-        @Override
-        public void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
-            CompoundButtonCompatLollipop.setButtonTintMode(button, tintMode);
-        }
-
-        @Override
-        public PorterDuff.Mode getButtonTintMode(CompoundButton button) {
-            return CompoundButtonCompatLollipop.getButtonTintMode(button);
-        }
-    }
-
-    static class Api23CompoundButtonImpl extends LollipopCompoundButtonImpl {
+    @RequiresApi(23)
+    static class CompoundButtonCompatApi23Impl extends CompoundButtonCompatApi21Impl {
         @Override
         public Drawable getButtonDrawable(CompoundButton button) {
-            return CompoundButtonCompatApi23.getButtonDrawable(button);
+            return button.getButtonDrawable();
         }
     }
 
@@ -155,7 +179,7 @@
     /**
      * @return the blending mode used to apply the tint to the button drawable
      * @attr name android:buttonTintMode
-     * @see #setButtonTintMode(PorterDuff.Mode)
+     * @see #setButtonTintMode(CompoundButton, PorterDuff.Mode)
      */
     @Nullable
     public static PorterDuff.Mode getButtonTintMode(@NonNull CompoundButton button) {
diff --git a/compat/java/android/support/v4/widget/EdgeEffectCompat.java b/compat/java/android/support/v4/widget/EdgeEffectCompat.java
index d65fb67..9293e60 100644
--- a/compat/java/android/support/v4/widget/EdgeEffectCompat.java
+++ b/compat/java/android/support/v4/widget/EdgeEffectCompat.java
@@ -18,142 +18,41 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.widget.EdgeEffect;
 
 /**
- * Helper for accessing {@link android.widget.EdgeEffect} introduced after
- * API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link android.widget.EdgeEffect}.
  *
- * This class is used to access {@link android.widget.EdgeEffect} on platform versions     
- * that support it. When running on older platforms it will result in no-ops. It should     
- * be used by views that wish to use the standard Android visual effects at the edges       
+ * This class is used to access {@link android.widget.EdgeEffect} on platform versions
+ * that support it. When running on older platforms it will result in no-ops. It should
+ * be used by views that wish to use the standard Android visual effects at the edges
  * of scrolling containers.
  */
 public final class EdgeEffectCompat {
-    private Object mEdgeEffect;
+    private EdgeEffect mEdgeEffect;
 
-    private static final EdgeEffectImpl IMPL;
+    private static final EdgeEffectBaseImpl IMPL;
 
     static {
         if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new EdgeEffectLollipopImpl(); // Lollipop
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new EdgeEffectIcsImpl();
+            IMPL = new EdgeEffectApi21Impl();
         } else {
-            IMPL = new BaseEdgeEffectImpl();
+            IMPL = new EdgeEffectBaseImpl();
         }
     }
 
-    interface EdgeEffectImpl {
-        public Object newEdgeEffect(Context context);
-        public void setSize(Object edgeEffect, int width, int height);
-        public boolean isFinished(Object edgeEffect);
-        public void finish(Object edgeEffect);
-        public boolean onPull(Object edgeEffect, float deltaDistance);
-        public boolean onRelease(Object edgeEffect);
-        public boolean onAbsorb(Object edgeEffect, int velocity);
-        public boolean draw(Object edgeEffect, Canvas canvas);
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement);
-    }
-
-    /**
-     * Null implementation to use pre-ICS
-     */
-    static class BaseEdgeEffectImpl implements EdgeEffectImpl {
-        @Override
-        public Object newEdgeEffect(Context context) {
-            return null;
-        }
-
-        @Override
-        public void setSize(Object edgeEffect, int width, int height) {
-        }
-
-        @Override
-        public boolean isFinished(Object edgeEffect) {
-            return true;
-        }
-
-        @Override
-        public void finish(Object edgeEffect) {
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance) {
-            return false;
-        }
-
-        @Override
-        public boolean onRelease(Object edgeEffect) {
-            return false;
-        }
-
-        @Override
-        public boolean onAbsorb(Object edgeEffect, int velocity) {
-            return false;
-        }
-
-        @Override
-        public boolean draw(Object edgeEffect, Canvas canvas) {
-            return false;
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-            return false;
+    static class EdgeEffectBaseImpl {
+        public void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
+            edgeEffect.onPull(deltaDistance);
         }
     }
 
-    static class EdgeEffectIcsImpl implements EdgeEffectImpl {
+    @RequiresApi(21)
+    static class EdgeEffectApi21Impl extends EdgeEffectBaseImpl {
         @Override
-        public Object newEdgeEffect(Context context) {
-            return EdgeEffectCompatIcs.newEdgeEffect(context);
-        }
-
-        @Override
-        public void setSize(Object edgeEffect, int width, int height) {
-            EdgeEffectCompatIcs.setSize(edgeEffect, width, height);
-        }
-
-        @Override
-        public boolean isFinished(Object edgeEffect) {
-            return EdgeEffectCompatIcs.isFinished(edgeEffect);
-        }
-
-        @Override
-        public void finish(Object edgeEffect) {
-            EdgeEffectCompatIcs.finish(edgeEffect);
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance) {
-            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
-        }
-
-        @Override
-        public boolean onRelease(Object edgeEffect) {
-            return EdgeEffectCompatIcs.onRelease(edgeEffect);
-        }
-
-        @Override
-        public boolean onAbsorb(Object edgeEffect, int velocity) {
-            return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity);
-        }
-
-        @Override
-        public boolean draw(Object edgeEffect, Canvas canvas) {
-            return EdgeEffectCompatIcs.draw(edgeEffect, canvas);
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
-        }
-    }
-
-    static class EdgeEffectLollipopImpl extends EdgeEffectIcsImpl {
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-            return EdgeEffectCompatLollipop.onPull(edgeEffect, deltaDistance, displacement);
+        public void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
+            edgeEffect.onPull(deltaDistance, displacement);
         }
     }
 
@@ -164,9 +63,12 @@
      * on the newly constructed object will be mocked/no-ops.</p>
      *
      * @param context Context to use for theming the effect
+     *
+     * @deprecated Use {@link EdgeEffect} constructor directly.
      */
+    @Deprecated
     public EdgeEffectCompat(Context context) {
-        mEdgeEffect = IMPL.newEdgeEffect(context);
+        mEdgeEffect = new EdgeEffect(context);
     }
 
     /**
@@ -174,9 +76,12 @@
      *
      * @param width Effect width in pixels
      * @param height Effect height in pixels
+     *
+     * @deprecated Use {@link EdgeEffect#setSize(int, int)} directly.
      */
+    @Deprecated
     public void setSize(int width, int height) {
-        IMPL.setSize(mEdgeEffect, width, height);
+        mEdgeEffect.setSize(width, height);
     }
 
     /**
@@ -185,17 +90,23 @@
      * drawing pass to continue the animation.
      *
      * @return true if animation is finished, false if drawing should continue on the next frame.
+     *
+     * @deprecated Use {@link EdgeEffect#isFinished()} directly.
      */
+    @Deprecated
     public boolean isFinished() {
-        return IMPL.isFinished(mEdgeEffect);
+        return mEdgeEffect.isFinished();
     }
 
     /**
      * Immediately finish the current animation.
      * After this call {@link #isFinished()} will return true.
+     *
+     * @deprecated Use {@link EdgeEffect#finish()} directly.
      */
+    @Deprecated
     public void finish() {
-        IMPL.finish(mEdgeEffect);
+        mEdgeEffect.finish();
     }
 
     /**
@@ -208,11 +119,13 @@
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
      * @return true if the host view should call invalidate, false if it should not.
-     * @deprecated use {@link #onPull(float, float)}
+     *
+     * @deprecated Use {@link #onPull(EdgeEffect, float, float)}.
      */
     @Deprecated
     public boolean onPull(float deltaDistance) {
-        return IMPL.onPull(mEdgeEffect, deltaDistance);
+        mEdgeEffect.onPull(deltaDistance);
+        return true;
     }
 
     /**
@@ -221,6 +134,9 @@
      * The host view should always {@link android.view.View#invalidate()} if this method
      * returns true and draw the results accordingly.
      *
+     * Views using {@link EdgeEffect} should favor {@link EdgeEffect#onPull(float, float)} when
+     * the displacement of the pull point is known.
+     *
      * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
@@ -228,9 +144,34 @@
      *                     initiating the pull. In the case of touch this is the finger position.
      *                     Values may be from 0-1.
      * @return true if the host view should call invalidate, false if it should not.
+     *
+     * @deprecated Use {@link EdgeEffect#onPull(float)} directly.
      */
+    @Deprecated
     public boolean onPull(float deltaDistance, float displacement) {
-        return IMPL.onPull(mEdgeEffect, deltaDistance, displacement);
+        IMPL.onPull(mEdgeEffect, deltaDistance, displacement);
+        return true;
+    }
+
+    /**
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} after call this method
+     * and draw the results accordingly.
+     *
+     * @param edgeEffect The EdgeEffect that is attached to the view that is getting pulled away
+     *                   from an edge by the user.
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
+     * @param displacement The displacement from the starting side of the effect of the point
+     *                     initiating the pull. In the case of touch this is the finger position.
+     *                     Values may be from 0-1.
+     *
+     * @see {@link EdgeEffect#onPull(float, float)}
+     */
+    public static void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
+        IMPL.onPull(edgeEffect, deltaDistance, displacement);
     }
 
     /**
@@ -240,9 +181,13 @@
      * returns true and thereby draw the results accordingly.
      *
      * @return true if the host view should invalidate, false if it should not.
+     *
+     * @deprecated Use {@link EdgeEffect#onRelease()} directly.
      */
+    @Deprecated
     public boolean onRelease() {
-        return IMPL.onRelease(mEdgeEffect);
+        mEdgeEffect.onRelease();
+        return mEdgeEffect.isFinished();
     }
 
     /**
@@ -255,9 +200,13 @@
      *
      * @param velocity Velocity at impact in pixels per second.
      * @return true if the host view should invalidate, false if it should not.
+     *
+     * @deprecated Use {@link EdgeEffect#onAbsorb(int)} directly.
      */
+    @Deprecated
     public boolean onAbsorb(int velocity) {
-        return IMPL.onAbsorb(mEdgeEffect, velocity);
+        mEdgeEffect.onAbsorb(velocity);
+        return true;
     }
 
     /**
@@ -269,8 +218,11 @@
      * @param canvas Canvas to draw into
      * @return true if drawing should continue beyond this frame to continue the
      *         animation
+     *
+     * @deprecated Use {@link EdgeEffect#draw(Canvas)} directly.
      */
+    @Deprecated
     public boolean draw(Canvas canvas) {
-        return IMPL.draw(mEdgeEffect, canvas);
+        return mEdgeEffect.draw(canvas);
     }
 }
diff --git a/compat/java/android/support/v4/widget/ImageViewCompat.java b/compat/java/android/support/v4/widget/ImageViewCompat.java
index 3c0b357..acaaf63 100644
--- a/compat/java/android/support/v4/widget/ImageViewCompat.java
+++ b/compat/java/android/support/v4/widget/ImageViewCompat.java
@@ -18,11 +18,13 @@
 
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.widget.ImageView;
 
 /**
- * Helper for accessing features in {@link ImageView} introduced in later platform releases
- * in a backwards compatible fashion.
+ * Helper for accessing features in {@link ImageView}.
  */
 public class ImageViewCompat {
     interface ImageViewCompatImpl {
@@ -38,44 +40,81 @@
     static class BaseViewCompatImpl implements ImageViewCompatImpl {
         @Override
         public ColorStateList getImageTintList(ImageView view) {
-            return ImageViewCompatBase.getImageTintList(view);
+            return (view instanceof TintableImageSourceView)
+                    ? ((TintableImageSourceView) view).getSupportImageTintList()
+                    : null;
         }
 
         @Override
         public void setImageTintList(ImageView view, ColorStateList tintList) {
-            ImageViewCompatBase.setImageTintList(view, tintList);
+            if (view instanceof TintableImageSourceView) {
+                ((TintableImageSourceView) view).setSupportImageTintList(tintList);
+            }
         }
 
         @Override
         public void setImageTintMode(ImageView view, PorterDuff.Mode mode) {
-            ImageViewCompatBase.setImageTintMode(view, mode);
+            if (view instanceof TintableImageSourceView) {
+                ((TintableImageSourceView) view).setSupportImageTintMode(mode);
+            }
         }
 
         @Override
         public PorterDuff.Mode getImageTintMode(ImageView view) {
-            return ImageViewCompatBase.getImageTintMode(view);
+            return (view instanceof TintableImageSourceView)
+                    ? ((TintableImageSourceView) view).getSupportImageTintMode()
+                    : null;
         }
     }
 
+    @RequiresApi(21)
     static class LollipopViewCompatImpl extends BaseViewCompatImpl {
         @Override
         public ColorStateList getImageTintList(ImageView view) {
-            return ImageViewCompatLollipop.getImageTintList(view);
+            return view.getImageTintList();
         }
 
         @Override
         public void setImageTintList(ImageView view, ColorStateList tintList) {
-            ImageViewCompatLollipop.setImageTintList(view, tintList);
+            view.setImageTintList(tintList);
+
+            if (Build.VERSION.SDK_INT == 21) {
+                // Work around a bug in L that did not update the state of the image source
+                // after applying the tint
+                Drawable imageViewDrawable = view.getDrawable();
+                boolean hasTint = (view.getImageTintList() != null)
+                        && (view.getImageTintMode() != null);
+                if ((imageViewDrawable != null) && hasTint) {
+                    if (imageViewDrawable.isStateful()) {
+                        imageViewDrawable.setState(view.getDrawableState());
+                    }
+                    view.setImageDrawable(imageViewDrawable);
+                }
+            }
         }
 
         @Override
         public void setImageTintMode(ImageView view, PorterDuff.Mode mode) {
-            ImageViewCompatLollipop.setImageTintMode(view, mode);
+            view.setImageTintMode(mode);
+
+            if (Build.VERSION.SDK_INT == 21) {
+                // Work around a bug in L that did not update the state of the image source
+                // after applying the tint
+                Drawable imageViewDrawable = view.getDrawable();
+                boolean hasTint = (view.getImageTintList() != null)
+                        && (view.getImageTintMode() != null);
+                if ((imageViewDrawable != null) && hasTint) {
+                    if (imageViewDrawable.isStateful()) {
+                        imageViewDrawable.setState(view.getDrawableState());
+                    }
+                    view.setImageDrawable(imageViewDrawable);
+                }
+            }
         }
 
         @Override
         public PorterDuff.Mode getImageTintMode(ImageView view) {
-            return ImageViewCompatLollipop.getImageTintMode(view);
+            return view.getImageTintMode();
         }
     }
 
diff --git a/compat/java/android/support/v4/widget/ListPopupWindowCompat.java b/compat/java/android/support/v4/widget/ListPopupWindowCompat.java
index 8d34312..ab86e58 100644
--- a/compat/java/android/support/v4/widget/ListPopupWindowCompat.java
+++ b/compat/java/android/support/v4/widget/ListPopupWindowCompat.java
@@ -16,54 +16,15 @@
 
 package android.support.v4.widget;
 
+import android.os.Build;
 import android.view.View;
 import android.view.View.OnTouchListener;
+import android.widget.ListPopupWindow;
 
 /**
- * Helper for accessing features in ListPopupWindow introduced after API level 4
- * in a backwards compatible fashion.
+ * Helper for accessing features in {@link ListPopupWindow}.
  */
 public final class ListPopupWindowCompat {
-    /**
-     * Interface for the full API.
-     */
-    interface ListPopupWindowImpl {
-        public OnTouchListener createDragToOpenListener(Object listPopupWindow, View src);
-    }
-
-    /**
-     * Interface implementation that doesn't use anything above v4 APIs.
-     */
-    static class BaseListPopupWindowImpl implements ListPopupWindowImpl {
-        @Override
-        public OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
-            return null;
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least KitKat APIs.
-     */
-    static class KitKatListPopupWindowImpl extends BaseListPopupWindowImpl {
-        @Override
-        public OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
-            return ListPopupWindowCompatKitKat.createDragToOpenListener(listPopupWindow, src);
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final ListPopupWindowImpl IMPL;
-    static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            IMPL = new KitKatListPopupWindowImpl();
-        } else {
-            IMPL = new BaseListPopupWindowImpl();
-        }
-    }
-
     private ListPopupWindowCompat() {
         // This class is not publicly instantiable.
     }
@@ -79,7 +40,7 @@
      * currently touched list item.
      * <p>
      * Example usage:
-     * 
+     *
      * <pre>
      * ListPopupWindow myPopup = new ListPopupWindow(context);
      * myPopup.setAnchor(myAnchor);
@@ -90,10 +51,49 @@
      * @param listPopupWindow the ListPopupWindow against which to invoke the
      *            method
      * @param src the view on which the resulting listener will be set
-     * @return a touch listener that controls drag-to-open behavior, or null on
+     * @return a touch listener that controls drag-to-open behavior, or {@code null} on
+     *         unsupported APIs
+     *
+     * @deprecated Use {@link #createDragToOpenListener(ListPopupWindow, View)} that takes in
+     * {@link ListPopupWindow} instead of {@link Object}.
+     */
+    @Deprecated
+    public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
+        return ListPopupWindowCompat.createDragToOpenListener(
+                (ListPopupWindow) listPopupWindow, src);
+    }
+
+    /**
+     * On API {@link android.os.Build.VERSION_CODES#KITKAT} and higher, returns
+     * an {@link OnTouchListener} that can be added to the source view to
+     * implement drag-to-open behavior. Generally, the source view should be the
+     * same view that was passed to ListPopupWindow.setAnchorView(View).
+     * <p>
+     * When the listener is set on a view, touching that view and dragging
+     * outside of its bounds will open the popup window. Lifting will select the
+     * currently touched list item.
+     * <p>
+     * Example usage:
+     *
+     * <pre>
+     * ListPopupWindow myPopup = new ListPopupWindow(context);
+     * myPopup.setAnchor(myAnchor);
+     * OnTouchListener dragListener = myPopup.createDragToOpenListener(myAnchor);
+     * myAnchor.setOnTouchListener(dragListener);
+     * </pre>
+     *
+     * @param listPopupWindow the ListPopupWindow against which to invoke the
+     *            method
+     * @param src the view on which the resulting listener will be set
+     * @return a touch listener that controls drag-to-open behavior, or {@code null} on
      *         unsupported APIs
      */
-    public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
-        return IMPL.createDragToOpenListener(listPopupWindow, src);
+    public static OnTouchListener createDragToOpenListener(
+            ListPopupWindow listPopupWindow, View src) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            return listPopupWindow.createDragToOpenListener(src);
+        } else {
+            return null;
+        }
     }
 }
diff --git a/compat/java/android/support/v4/widget/ListViewCompat.java b/compat/java/android/support/v4/widget/ListViewCompat.java
index 6e98540..59ee741 100644
--- a/compat/java/android/support/v4/widget/ListViewCompat.java
+++ b/compat/java/android/support/v4/widget/ListViewCompat.java
@@ -18,11 +18,11 @@
 
 import android.os.Build;
 import android.support.annotation.NonNull;
+import android.view.View;
 import android.widget.ListView;
 
 /**
- * Helper for accessing features in {@link ListView} introduced after API level
- * 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link ListView}
  */
 public final class ListViewCompat {
 
@@ -34,9 +34,55 @@
      */
     public static void scrollListBy(@NonNull ListView listView, int y) {
         if (Build.VERSION.SDK_INT >= 19) {
-            ListViewCompatKitKat.scrollListBy(listView, y);
+            // Call the framework version directly
+            listView.scrollListBy(y);
         } else {
-            ListViewCompatGingerbread.scrollListBy(listView, y);
+            // provide backport on earlier versions
+            final int firstPosition = listView.getFirstVisiblePosition();
+            if (firstPosition == ListView.INVALID_POSITION) {
+                return;
+            }
+
+            final View firstView = listView.getChildAt(0);
+            if (firstView == null) {
+                return;
+            }
+
+            final int newTop = firstView.getTop() - y;
+            listView.setSelectionFromTop(firstPosition, newTop);
+        }
+    }
+
+    /**
+     * Check if the items in the list can be scrolled in a certain direction.
+     *
+     * @param direction Negative to check scrolling up, positive to check
+     *            scrolling down.
+     * @return true if the list can be scrolled in the specified direction,
+     *         false otherwise.
+     * @see #scrollListBy(ListView, int)
+     */
+    public static boolean canScrollList(@NonNull ListView listView, int direction) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            // Call the framework version directly
+            return listView.canScrollList(direction);
+        } else {
+            // provide backport on earlier versions
+            final int childCount = listView.getChildCount();
+            if (childCount == 0) {
+                return false;
+            }
+
+            final int firstPosition = listView.getFirstVisiblePosition();
+            if (direction > 0) {
+                final int lastBottom = listView.getChildAt(childCount - 1).getBottom();
+                final int lastPosition = firstPosition + childCount;
+                return lastPosition < listView.getCount()
+                        || (lastBottom > listView.getHeight() - listView.getListPaddingBottom());
+            } else {
+                final int firstTop = listView.getChildAt(0).getTop();
+                return firstPosition > 0 || firstTop < listView.getListPaddingTop();
+            }
         }
     }
 
diff --git a/compat/java/android/support/v4/widget/PopupMenuCompat.java b/compat/java/android/support/v4/widget/PopupMenuCompat.java
index 3651429..639a84b 100644
--- a/compat/java/android/support/v4/widget/PopupMenuCompat.java
+++ b/compat/java/android/support/v4/widget/PopupMenuCompat.java
@@ -16,53 +16,14 @@
 
 package android.support.v4.widget;
 
+import android.os.Build;
 import android.view.View.OnTouchListener;
+import android.widget.PopupMenu;
 
 /**
- * Helper for accessing features in PopupMenu introduced after API level 4 in a
- * backwards compatible fashion.
+ * Helper for accessing features in {@link PopupMenu}.
  */
 public final class PopupMenuCompat {
-    /**
-     * Interface for the full API.
-     */
-    interface PopupMenuImpl {
-        public OnTouchListener getDragToOpenListener(Object popupMenu);
-    }
-
-    /**
-     * Interface implementation that doesn't use anything above v4 APIs.
-     */
-    static class BasePopupMenuImpl implements PopupMenuImpl {
-        @Override
-        public OnTouchListener getDragToOpenListener(Object popupMenu) {
-            return null;
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least KitKat APIs.
-     */
-    static class KitKatPopupMenuImpl extends BasePopupMenuImpl {
-        @Override
-        public OnTouchListener getDragToOpenListener(Object popupMenu) {
-            return PopupMenuCompatKitKat.getDragToOpenListener(popupMenu);
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final PopupMenuImpl IMPL;
-    static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) {
-            IMPL = new KitKatPopupMenuImpl();
-        } else {
-            IMPL = new BasePopupMenuImpl();
-        }
-    }
-
     private PopupMenuCompat() {
         // This class is not publicly instantiable.
     }
@@ -83,10 +44,14 @@
      * </pre>
      *
      * @param popupMenu the PopupMenu against which to invoke the method
-     * @return a touch listener that controls drag-to-open behavior, or null on
+     * @return a touch listener that controls drag-to-open behavior, or {@code null} on
      *         unsupported APIs
      */
     public static OnTouchListener getDragToOpenListener(Object popupMenu) {
-        return IMPL.getDragToOpenListener(popupMenu);
+        if (Build.VERSION.SDK_INT >= 19) {
+            return ((PopupMenu) popupMenu).getDragToOpenListener();
+        } else {
+            return null;
+        }
     }
 }
diff --git a/compat/java/android/support/v4/widget/PopupWindowCompat.java b/compat/java/android/support/v4/widget/PopupWindowCompat.java
index 2047662..d846b40 100644
--- a/compat/java/android/support/v4/widget/PopupWindowCompat.java
+++ b/compat/java/android/support/v4/widget/PopupWindowCompat.java
@@ -16,41 +16,29 @@
 
 package android.support.v4.widget;
 
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
-import android.view.WindowManager;
 import android.widget.PopupWindow;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
 /**
- * Helper for accessing features in PopupWindow introduced after API level 4
- * in a backwards compatible fashion.
+ * Helper for accessing features in {@link PopupWindow}.
  */
 public final class PopupWindowCompat {
-    /**
-     * Interface for the full API.
-     */
-    interface PopupWindowImpl {
-        void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, int gravity);
-        void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor);
-        boolean getOverlapAnchor(PopupWindow popupWindow);
-        void setWindowLayoutType(PopupWindow popupWindow, int layoutType);
-        int getWindowLayoutType(PopupWindow popupWindow);
-    }
 
-    /**
-     * Interface implementation that doesn't use anything above v4 APIs.
-     */
-    static class BasePopupWindowImpl implements PopupWindowImpl {
+    static class PopupWindowCompatBaseImpl {
         private static Method sSetWindowLayoutTypeMethod;
         private static boolean sSetWindowLayoutTypeMethodAttempted;
         private static Method sGetWindowLayoutTypeMethod;
         private static boolean sGetWindowLayoutTypeMethodAttempted;
 
-        @Override
         public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
                 int gravity) {
             final int hgrav = GravityCompat.getAbsoluteGravity(gravity,
@@ -63,17 +51,14 @@
             popup.showAsDropDown(anchor, xoff, yoff);
         }
 
-        @Override
         public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
             // noop
         }
 
-        @Override
         public boolean getOverlapAnchor(PopupWindow popupWindow) {
             return false;
         }
 
-        @Override
         public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
             if (!sSetWindowLayoutTypeMethodAttempted) {
                 try {
@@ -95,7 +80,6 @@
             }
         }
 
-        @Override
         public int getWindowLayoutType(PopupWindow popupWindow) {
             if (!sGetWindowLayoutTypeMethodAttempted) {
                 try {
@@ -122,62 +106,90 @@
     /**
      * Interface implementation for devices with at least KitKat APIs.
      */
-    static class KitKatPopupWindowImpl extends BasePopupWindowImpl {
+    @RequiresApi(19)
+    static class PopupWindowCompatApi19Impl extends PopupWindowCompatBaseImpl {
         @Override
         public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
                 int gravity) {
-            PopupWindowCompatKitKat.showAsDropDown(popup, anchor, xoff, yoff, gravity);
+            popup.showAsDropDown(anchor, xoff, yoff, gravity);
         }
     }
 
-    static class Api21PopupWindowImpl extends KitKatPopupWindowImpl {
+    @RequiresApi(21)
+    static class PopupWindowCompatApi21Impl extends PopupWindowCompatApi19Impl {
+        private static final String TAG = "PopupWindowCompatApi21";
+
+        private static Field sOverlapAnchorField;
+
+        static {
+            try {
+                sOverlapAnchorField = PopupWindow.class.getDeclaredField("mOverlapAnchor");
+                sOverlapAnchorField.setAccessible(true);
+            } catch (NoSuchFieldException e) {
+                Log.i(TAG, "Could not fetch mOverlapAnchor field from PopupWindow", e);
+            }
+        }
+
         @Override
         public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-            PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor);
+            if (sOverlapAnchorField != null) {
+                try {
+                    sOverlapAnchorField.set(popupWindow, overlapAnchor);
+                } catch (IllegalAccessException e) {
+                    Log.i(TAG, "Could not set overlap anchor field in PopupWindow", e);
+                }
+            }
         }
 
         @Override
         public boolean getOverlapAnchor(PopupWindow popupWindow) {
-            return PopupWindowCompatApi21.getOverlapAnchor(popupWindow);
+            if (sOverlapAnchorField != null) {
+                try {
+                    return (Boolean) sOverlapAnchorField.get(popupWindow);
+                } catch (IllegalAccessException e) {
+                    Log.i(TAG, "Could not get overlap anchor field in PopupWindow", e);
+                }
+            }
+            return false;
         }
     }
 
-    static class Api23PopupWindowImpl extends Api21PopupWindowImpl {
+    @RequiresApi(23)
+    static class PopupWindowCompatApi23Impl extends PopupWindowCompatApi21Impl {
         @Override
         public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-            PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor);
+            popupWindow.setOverlapAnchor(overlapAnchor);
         }
 
         @Override
         public boolean getOverlapAnchor(PopupWindow popupWindow) {
-            return PopupWindowCompatApi23.getOverlapAnchor(popupWindow);
+            return popupWindow.getOverlapAnchor();
         }
 
         @Override
         public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
-            PopupWindowCompatApi23.setWindowLayoutType(popupWindow, layoutType);
+            popupWindow.setWindowLayoutType(layoutType);
         }
 
         @Override
         public int getWindowLayoutType(PopupWindow popupWindow) {
-            return PopupWindowCompatApi23.getWindowLayoutType(popupWindow);
+            return popupWindow.getWindowLayoutType();
         }
     }
 
     /**
      * Select the correct implementation to use for the current platform.
      */
-    static final PopupWindowImpl IMPL;
+    static final PopupWindowCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 23) {
-            IMPL = new Api23PopupWindowImpl();
-        } else if (version >= 21) {
-            IMPL = new Api21PopupWindowImpl();
-        } else if (version >= 19) {
-            IMPL = new KitKatPopupWindowImpl();
+        if (Build.VERSION.SDK_INT >= 23) {
+            IMPL = new PopupWindowCompatApi23Impl();
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new PopupWindowCompatApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new PopupWindowCompatApi19Impl();
         } else {
-            IMPL = new BasePopupWindowImpl();
+            IMPL = new PopupWindowCompatBaseImpl();
         }
     }
 
@@ -228,12 +240,12 @@
 
     /**
      * Set the layout type for this window. This value will be passed through to
-     * {@link WindowManager.LayoutParams#type} therefore the value should match any value
-     * {@link WindowManager.LayoutParams#type} accepts.
+     * {@link android.view.WindowManager.LayoutParams#type} therefore the value should match any
+     * value {@link android.view.WindowManager.LayoutParams#type} accepts.
      *
      * @param layoutType Layout type for this window.
      *
-     * @see WindowManager.LayoutParams#type
+     * @see android.view.WindowManager.LayoutParams#type
      */
     public static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
         IMPL.setWindowLayoutType(popupWindow, layoutType);
diff --git a/compat/java/android/support/v4/widget/ScrollerCompat.java b/compat/java/android/support/v4/widget/ScrollerCompat.java
index 0161574..8e8645c 100644
--- a/compat/java/android/support/v4/widget/ScrollerCompat.java
+++ b/compat/java/android/support/v4/widget/ScrollerCompat.java
@@ -17,11 +17,8 @@
 package android.support.v4.widget;
 
 import android.content.Context;
-import android.os.Build;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.OverScroller;
-import android.widget.Scroller;
 
 /**
  * Provides access to new {@link android.widget.Scroller Scroller} APIs when available.
@@ -29,25 +26,34 @@
  * <p>This class provides a platform version-independent mechanism for obeying the
  * current device's preferred scroll physics and fling behavior. It offers a subset of
  * the APIs from Scroller or OverScroller.</p>
+ *
+ * @deprecated Use {@link OverScroller} directly.
  */
+@Deprecated
 public final class ScrollerCompat {
     OverScroller mScroller;
-    private final boolean mIsIcsOrNewer;
 
+    /**
+     * @deprecated Use {@link OverScroller} constructor directly.
+     */
+    @Deprecated
     public static ScrollerCompat create(Context context) {
         return create(context, null);
     }
 
+    /**
+     * @deprecated Use {@link OverScroller} constructor directly.
+     */
+    @Deprecated
     public static ScrollerCompat create(Context context, Interpolator interpolator) {
-        return new ScrollerCompat(Build.VERSION.SDK_INT >= 14, context, interpolator);
+        return new ScrollerCompat(context, interpolator);
     }
 
     /**
      * Package protected constructor that allows to specify if API version is newer than ICS.
      * It is useful for unit testing.
      */
-    ScrollerCompat(boolean isIcsOrNewer, Context context, Interpolator interpolator) {
-        mIsIcsOrNewer = isIcsOrNewer;
+    ScrollerCompat(Context context, Interpolator interpolator) {
         mScroller = interpolator != null ?
                 new OverScroller(context, interpolator) : new OverScroller(context);
     }
@@ -56,7 +62,10 @@
      * Returns whether the scroller has finished scrolling.
      *
      * @return True if the scroller has finished scrolling, false otherwise.
+     *
+     * @deprecated Use {@link OverScroller#isFinished()} directly.
      */
+    @Deprecated
     public boolean isFinished() {
         return mScroller.isFinished();
     }
@@ -65,7 +74,10 @@
      * Returns the current X offset in the scroll.
      *
      * @return The new X offset as an absolute distance from the origin.
+     *
+     * @deprecated Use {@link OverScroller#getCurrX()} directly.
      */
+    @Deprecated
     public int getCurrX() {
         return mScroller.getCurrX();
     }
@@ -74,21 +86,30 @@
      * Returns the current Y offset in the scroll.
      *
      * @return The new Y offset as an absolute distance from the origin.
+     *
+     * @deprecated Use {@link OverScroller#getCurrY()} directly.
      */
+    @Deprecated
     public int getCurrY() {
         return mScroller.getCurrY();
     }
 
     /**
      * @return The final X position for the scroll in progress, if known.
+     *
+     * @deprecated Use {@link OverScroller#getFinalX()} directly.
      */
+    @Deprecated
     public int getFinalX() {
         return mScroller.getFinalX();
     }
 
     /**
      * @return The final Y position for the scroll in progress, if known.
+     *
+     * @deprecated Use {@link OverScroller#getFinalY()} directly.
      */
+    @Deprecated
     public int getFinalY() {
         return mScroller.getFinalY();
     }
@@ -96,22 +117,27 @@
     /**
      * Returns the current velocity on platform versions that support it.
      *
-     * <p>The device must support at least API level 14 (Ice Cream Sandwich).
-     * On older platform versions this method will return 0. This method should
-     * only be used as input for nonessential visual effects such as {@link EdgeEffectCompat}.</p>
+     * <p> This method should only be used as input for nonessential visual effects such as
+     * {@link EdgeEffectCompat}.</p>
      *
      * @return The original velocity less the deceleration. Result may be
      * negative.
+     *
+     * @deprecated Use {@link OverScroller#getCurrVelocity()} directly.
      */
+    @Deprecated
     public float getCurrVelocity() {
-        return mIsIcsOrNewer ? ScrollerCompatIcs.getCurrVelocity(mScroller) : 0;
+        return mScroller.getCurrVelocity();
     }
 
     /**
      * Call this when you want to know the new location.  If it returns true,
      * the animation is not yet finished.  loc will be altered to provide the
      * new location.
+     *
+     * @deprecated Use {@link OverScroller#computeScrollOffset()} directly.
      */
+    @Deprecated
     public boolean computeScrollOffset() {
         return mScroller.computeScrollOffset();
     }
@@ -129,7 +155,10 @@
      *        content to the left.
      * @param dy Vertical distance to travel. Positive numbers will scroll the
      *        content up.
+     *
+     * @deprecated Use {@link OverScroller#getCurrX()} directly.
      */
+    @Deprecated
     public void startScroll(int startX, int startY, int dx, int dy) {
         mScroller.startScroll(startX, startY, dx, dy);
     }
@@ -146,7 +175,10 @@
      * @param dy Vertical distance to travel. Positive numbers will scroll the
      *        content up.
      * @param duration Duration of the scroll in milliseconds.
+     *
+     * @deprecated Use {@link OverScroller#startScroll(int, int, int, int, int)} directly.
      */
+    @Deprecated
     public void startScroll(int startX, int startY, int dx, int dy, int duration) {
         mScroller.startScroll(startX, startY, dx, dy, duration);
     }
@@ -169,7 +201,10 @@
      *        point.
      * @param maxY Maximum Y value. The scroller will not scroll past this
      *        point.
+     *
+     * @deprecated Use {@link OverScroller#fling(int, int, int, int, int, int, int, int)} directly.
      */
+    @Deprecated
     public void fling(int startX, int startY, int velocityX, int velocityY,
             int minX, int maxX, int minY, int maxY) {
         mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
@@ -197,7 +232,11 @@
      *            direction will be possible.
      * @param overY Overfling range. If > 0, vertical overfling in either
      *            direction will be possible.
+     *
+     * @deprecated Use {@link OverScroller#fling(int, int, int, int, int, int, int, int, int, int)}
+     * directly.
      */
+    @Deprecated
     public void fling(int startX, int startY, int velocityX, int velocityY,
             int minX, int maxX, int minY, int maxY, int overX, int overY) {
         mScroller.fling(startX, startY, velocityX, velocityY,
@@ -215,7 +254,10 @@
      * @param maxY Maximum valid Y value
      * @return true if a springback was initiated, false if startX and startY were
      *          already within the valid range.
+     *
+     * @deprecated Use {@link OverScroller#springBack(int, int, int, int, int, int)} directly.
      */
+    @Deprecated
     public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
         return mScroller.springBack(startX, startY, minX, maxX, minY, maxY);
     }
@@ -223,7 +265,10 @@
     /**
      * Stops the animation. Aborting the animation causes the scroller to move to the final x and y
      * position.
+     *
+     * @deprecated Use {@link OverScroller#abortAnimation()} directly.
      */
+    @Deprecated
     public void abortAnimation() {
         mScroller.abortAnimation();
     }
@@ -241,7 +286,10 @@
      * @param finalX Desired final X position
      * @param overX Magnitude of overscroll allowed. This should be the maximum
      *              desired distance from finalX. Absolute value - must be positive.
+     *
+     * @deprecated Use {@link OverScroller#notifyHorizontalEdgeReached(int, int, int)} directly.
      */
+    @Deprecated
     public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
         mScroller.notifyHorizontalEdgeReached(startX, finalX, overX);
     }
@@ -258,7 +306,10 @@
      * @param finalY Desired final Y position
      * @param overY Magnitude of overscroll allowed. This should be the maximum
      *              desired distance from finalY. Absolute value - must be positive.
+     *
+     * @deprecated Use {@link OverScroller#notifyVerticalEdgeReached(int, int, int)} directly.
      */
+    @Deprecated
     public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
         mScroller.notifyVerticalEdgeReached(startY, finalY, overY);
     }
@@ -275,7 +326,10 @@
      *
      * @return true when the current position is overscrolled and in the process of
      *         interpolating back to a valid value.
+     *
+     * @deprecated Use {@link OverScroller#isOverScrolled()} directly.
      */
+    @Deprecated
     public boolean isOverScrolled() {
         return mScroller.isOverScrolled();
     }
diff --git a/compat/java/android/support/v4/widget/SearchViewCompat.java b/compat/java/android/support/v4/widget/SearchViewCompat.java
index 2b69f55..b176467 100644
--- a/compat/java/android/support/v4/widget/SearchViewCompat.java
+++ b/compat/java/android/support/v4/widget/SearchViewCompat.java
@@ -17,267 +17,27 @@
 package android.support.v4.widget;
 
 import android.app.SearchManager;
+import android.app.SearchableInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.os.Build;
 import android.view.View;
+import android.widget.SearchView;
 import android.widget.TextView;
 
 /**
- * Helper for accessing features in {@link android.widget.SearchView}
- * introduced after API level 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link SearchView}.
+ *
+ * @deprecated Use {@link SearchView} directly.
  */
+@Deprecated
 public final class SearchViewCompat {
-
-    interface SearchViewCompatImpl {
-        View newSearchView(Context context);
-        void setSearchableInfo(View searchView, ComponentName searchableComponent);
-        void setImeOptions(View searchView, int imeOptions);
-        void setInputType(View searchView, int inputType);
-        Object newOnQueryTextListener(OnQueryTextListener listener);
-        void setOnQueryTextListener(View searchView, OnQueryTextListener listener);
-        Object newOnCloseListener(OnCloseListener listener);
-        void setOnCloseListener(View searchView, OnCloseListener listener);
-        CharSequence getQuery(View searchView);
-        void setQuery(View searchView, CharSequence query, boolean submit);
-        void setQueryHint(View searchView, CharSequence hint);
-        void setIconified(View searchView, boolean iconify);
-        boolean isIconified(View searchView);
-        void setSubmitButtonEnabled(View searchView, boolean enabled);
-        boolean isSubmitButtonEnabled(View searchView);
-        void setQueryRefinementEnabled(View searchView, boolean enable);
-        boolean isQueryRefinementEnabled(View searchView);
-        void setMaxWidth(View searchView, int maxpixels);
-    }
-
-    static class SearchViewCompatStubImpl implements SearchViewCompatImpl {
-
-        @Override
-        public View newSearchView(Context context) {
-            return null;
+    private static void checkIfLegalArg(View searchView) {
+        if (searchView == null) {
+            throw new IllegalArgumentException("searchView must be non-null");
         }
-
-        @Override
-        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
-        }
-
-        @Override
-        public void setImeOptions(View searchView, int imeOptions) {
-        }
-
-        @Override
-        public void setInputType(View searchView, int inputType) {
-        }
-
-        @Override
-        public Object newOnQueryTextListener(OnQueryTextListener listener) {
-            return null;
-        }
-
-        @Override
-        public void setOnQueryTextListener(View searchView, OnQueryTextListener listener) {
-        }
-
-        @Override
-        public Object newOnCloseListener(OnCloseListener listener) {
-            return null;
-        }
-
-        @Override
-        public void setOnCloseListener(View searchView, OnCloseListener listener) {
-        }
-
-        @Override
-        public CharSequence getQuery(View searchView) {
-            return null;
-        }
-
-        @Override
-        public void setQuery(View searchView, CharSequence query, boolean submit) {
-        }
-
-        @Override
-        public void setQueryHint(View searchView, CharSequence hint) {
-        }
-
-        @Override
-        public void setIconified(View searchView, boolean iconify) {
-        }
-
-        @Override
-        public boolean isIconified(View searchView) {
-            return true;
-        }
-
-        @Override
-        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
-        }
-
-        @Override
-        public boolean isSubmitButtonEnabled(View searchView) {
-            return false;
-        }
-
-        @Override
-        public void setQueryRefinementEnabled(View searchView, boolean enable) {
-        }
-
-        @Override
-        public boolean isQueryRefinementEnabled(View searchView) {
-            return false;
-        }
-
-        @Override
-        public void setMaxWidth(View searchView, int maxpixels) {
-        }
-    }
-
-    static class SearchViewCompatHoneycombImpl extends SearchViewCompatStubImpl {
-
-        @Override
-        public View newSearchView(Context context) {
-            return SearchViewCompatHoneycomb.newSearchView(context);
-        }
-
-        @Override
-        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setSearchableInfo(searchView, searchableComponent);
-        }
-
-        @Override
-        public Object newOnQueryTextListener(final OnQueryTextListener listener) {
-            return SearchViewCompatHoneycomb.newOnQueryTextListener(
-                    new SearchViewCompatHoneycomb.OnQueryTextListenerCompatBridge() {
-                        @Override
-                        public boolean onQueryTextSubmit(String query) {
-                            return listener.onQueryTextSubmit(query);
-                        }
-                        @Override
-                        public boolean onQueryTextChange(String newText) {
-                            return listener.onQueryTextChange(newText);
-                        }
-                    });
-        }
-
-        @Override
-        public void setOnQueryTextListener(View searchView, OnQueryTextListener listener) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setOnQueryTextListener(searchView,
-                    newOnQueryTextListener(listener));
-        }
-
-        @Override
-        public Object newOnCloseListener(final OnCloseListener listener) {
-            return SearchViewCompatHoneycomb.newOnCloseListener(
-                    new SearchViewCompatHoneycomb.OnCloseListenerCompatBridge() {
-                        @Override
-                        public boolean onClose() {
-                            return listener.onClose();
-                        }
-                    });
-        }
-
-        @Override
-        public void setOnCloseListener(View searchView, OnCloseListener listener) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setOnCloseListener(searchView, newOnCloseListener(listener));
-        }
-
-        @Override
-        public CharSequence getQuery(View searchView) {
-            checkIfLegalArg(searchView);
-            return SearchViewCompatHoneycomb.getQuery(searchView);
-        }
-
-        @Override
-        public void setQuery(View searchView, CharSequence query, boolean submit) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setQuery(searchView, query, submit);
-        }
-
-        @Override
-        public void setQueryHint(View searchView, CharSequence hint) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setQueryHint(searchView, hint);
-        }
-
-        @Override
-        public void setIconified(View searchView, boolean iconify) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setIconified(searchView, iconify);
-        }
-
-        @Override
-        public boolean isIconified(View searchView) {
-            checkIfLegalArg(searchView);
-            return SearchViewCompatHoneycomb.isIconified(searchView);
-        }
-
-        @Override
-        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setSubmitButtonEnabled(searchView, enabled);
-        }
-
-        @Override
-        public boolean isSubmitButtonEnabled(View searchView) {
-            checkIfLegalArg(searchView);
-            return SearchViewCompatHoneycomb.isSubmitButtonEnabled(searchView);
-        }
-
-        @Override
-        public void setQueryRefinementEnabled(View searchView, boolean enable) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setQueryRefinementEnabled(searchView, enable);
-        }
-
-        @Override
-        public boolean isQueryRefinementEnabled(View searchView) {
-            checkIfLegalArg(searchView);
-            return SearchViewCompatHoneycomb.isQueryRefinementEnabled(searchView);
-        }
-
-        @Override
-        public void setMaxWidth(View searchView, int maxpixels) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatHoneycomb.setMaxWidth(searchView, maxpixels);
-        }
-
-        protected void checkIfLegalArg(View searchView) {
-            SearchViewCompatHoneycomb.checkIfLegalArg(searchView);
-        }
-    }
-
-    static class SearchViewCompatIcsImpl extends SearchViewCompatHoneycombImpl {
-
-        @Override
-        public View newSearchView(Context context) {
-            return SearchViewCompatIcs.newSearchView(context);
-        }
-
-        @Override
-        public void setImeOptions(View searchView, int imeOptions) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatIcs.setImeOptions(searchView, imeOptions);
-        }
-
-        @Override
-        public void setInputType(View searchView, int inputType) {
-            checkIfLegalArg(searchView);
-            SearchViewCompatIcs.setInputType(searchView, inputType);
-        }
-    }
-
-    private static final SearchViewCompatImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new SearchViewCompatIcsImpl();
-        } else if (Build.VERSION.SDK_INT >= 11) { // Honeycomb
-            IMPL = new SearchViewCompatHoneycombImpl();
-        } else {
-            IMPL = new SearchViewCompatStubImpl();
+        if (!(searchView instanceof SearchView)) {
+            throw new IllegalArgumentException("searchView must be an instance of "
+                    + "android.widget.SearchView");
         }
     }
 
@@ -291,9 +51,12 @@
      * @param context The Context the view is running in.
      * @return A SearchView instance if the class is present on the current
      *         platform, null otherwise.
+     *
+     * @deprecated Use {@link SearchView} constructor directly.
      */
+    @Deprecated
     public static View newSearchView(Context context) {
-        return IMPL.newSearchView(context);
+        return new SearchView(context);
     }
 
     /**
@@ -305,9 +68,16 @@
      * @param searchableComponent The application component whose
      * {@link android.app.SearchableInfo} should be loaded and applied to
      * the SearchView.
+     *
+     * @deprecated Use {@link SearchView#setSearchableInfo(SearchableInfo)} directly.
      */
+    @Deprecated
     public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
-        IMPL.setSearchableInfo(searchView, searchableComponent);
+        checkIfLegalArg(searchView);
+        SearchManager searchManager = (SearchManager)
+                searchView.getContext().getSystemService(Context.SEARCH_SERVICE);
+        ((SearchView) searchView).setSearchableInfo(
+                searchManager.getSearchableInfo(searchableComponent));
     }
 
     /**
@@ -318,9 +88,13 @@
      * @see TextView#setImeOptions(int)
      * @param searchView The SearchView to operate on.
      * @param imeOptions the options to set on the query text field
+     *
+     * @deprecated Use {@link SearchView#setImeOptions(int)} directly.
      */
+    @Deprecated
     public static void setImeOptions(View searchView, int imeOptions) {
-        IMPL.setImeOptions(searchView, imeOptions);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setImeOptions(imeOptions);
     }
 
     /**
@@ -331,9 +105,13 @@
      * @see TextView#setInputType(int)
      * @param searchView The SearchView to operate on.
      * @param inputType the input type to set on the query text field
+     *
+     * @deprecated Use {@link SearchView#setInputType(int)} directly.
      */
+    @Deprecated
     public static void setInputType(View searchView, int inputType) {
-        IMPL.setInputType(searchView, inputType);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setInputType(inputType);
     }
 
     /**
@@ -342,13 +120,33 @@
      * @param searchView The SearchView in which to register the listener.
      * @param listener the listener object that receives callbacks when the user performs
      *     actions in the SearchView such as clicking on buttons or typing a query.
+     *
+     * @deprecated Use {@link SearchView#setOnQueryTextListener(SearchView.OnQueryTextListener)}
+     * directly.
      */
+    @Deprecated
     public static void setOnQueryTextListener(View searchView, OnQueryTextListener listener) {
-        IMPL.setOnQueryTextListener(searchView, listener);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setOnQueryTextListener(newOnQueryTextListener(listener));
+    }
+
+    private static SearchView.OnQueryTextListener newOnQueryTextListener(
+            final OnQueryTextListener listener) {
+        return new SearchView.OnQueryTextListener() {
+            @Override
+            public boolean onQueryTextSubmit(String query) {
+                return listener.onQueryTextSubmit(query);
+            }
+
+            @Override
+            public boolean onQueryTextChange(String newText) {
+                return listener.onQueryTextChange(newText);
+            }
+        };
     }
 
     /**
-     * @deprecated Use {@link OnQueryTextListener} instead.
+     * @deprecated Use {@link SearchView.OnQueryTextListener} instead.
      */
     @Deprecated
     public static abstract class OnQueryTextListenerCompat implements OnQueryTextListener {
@@ -364,8 +162,9 @@
     }
 
     /**
-     * Callbacks for changes to the query text.
+     * @deprecated Use {@link SearchView.OnQueryTextListener} instead.
      */
+    @Deprecated
     public interface OnQueryTextListener {
         /**
          * Called when the user submits the query. This could be due to a key press on the
@@ -397,13 +196,26 @@
      *
      * @param searchView The SearchView in which to register the listener.
      * @param listener the listener to call when the user closes the SearchView.
+     *
+     * @deprecated Use {@link SearchView#setOnCloseListener(SearchView.OnCloseListener)} directly.
      */
+    @Deprecated
     public static void setOnCloseListener(View searchView, OnCloseListener listener) {
-        IMPL.setOnCloseListener(searchView, listener);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setOnCloseListener(newOnCloseListener(listener));
+    }
+
+    private static SearchView.OnCloseListener newOnCloseListener(final OnCloseListener listener) {
+        return new SearchView.OnCloseListener() {
+            @Override
+            public boolean onClose() {
+                return listener.onClose();
+            }
+        };
     }
 
     /**
-     * @deprecated Use {@link OnCloseListener} instead.
+     * @deprecated Use {@link SearchView.OnCloseListener} instead.
      */
     @Deprecated
     public static abstract class OnCloseListenerCompat implements OnCloseListener {
@@ -415,7 +227,10 @@
 
     /**
      * Callback for closing the query UI.
+     *
+     * @deprecated Use {@link SearchView.OnCloseListener} instead.
      */
+    @Deprecated
     public interface OnCloseListener {
         /**
          * The user is attempting to close the SearchView.
@@ -432,9 +247,13 @@
      * @param searchView The SearchView to operate on.
      *
      * @return the query string
+     *
+     * @deprecated Use {@link SearchView#getQuery()} directly.
      */
+    @Deprecated
     public static CharSequence getQuery(View searchView) {
-        return IMPL.getQuery(searchView);
+        checkIfLegalArg(searchView);
+        return ((SearchView) searchView).getQuery();
     }
 
     /**
@@ -445,9 +264,13 @@
      * text field.
      * @param submit whether to submit the query right now or only update the contents of
      * text field.
+     *
+     * @deprecated Use {@link SearchView#setQuery(CharSequence, boolean)} directly.
      */
+    @Deprecated
     public static void setQuery(View searchView, CharSequence query, boolean submit) {
-        IMPL.setQuery(searchView, query, submit);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setQuery(query, submit);
     }
 
     /**
@@ -456,9 +279,13 @@
      *
      * @param searchView The SearchView to operate on.
      * @param hint the hint text to display
+     *
+     * @deprecated Use {@link SearchView#setQueryHint(CharSequence)} directly.
      */
+    @Deprecated
     public static void setQueryHint(View searchView, CharSequence hint) {
-        IMPL.setQueryHint(searchView, hint);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setQueryHint(hint);
     }
 
     /**
@@ -471,9 +298,13 @@
      * @param searchView The SearchView to operate on.
      * @param iconify a true value will collapse the SearchView to an icon, while a false will
      * expand it.
+     *
+     * @deprecated Use {@link SearchView#setIconified(boolean)} directly.
      */
+    @Deprecated
     public static void setIconified(View searchView, boolean iconify) {
-        IMPL.setIconified(searchView, iconify);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setIconified(iconify);
     }
 
     /**
@@ -482,9 +313,13 @@
      * @param searchView The SearchView to operate on.
      * @return true if the SearchView is currently iconified, false if the search field is
      * fully visible.
+     *
+     * @deprecated Use {@link SearchView#isIconified()} directly.
      */
+    @Deprecated
     public static boolean isIconified(View searchView) {
-        return IMPL.isIconified(searchView);
+        checkIfLegalArg(searchView);
+        return ((SearchView) searchView).isIconified();
     }
 
     /**
@@ -495,9 +330,13 @@
      * @param searchView The SearchView to operate on.
      * @param enabled true to show a submit button for submitting queries, false if a submit
      * button is not required.
+     *
+     * @deprecated Use {@link SearchView#setSubmitButtonEnabled(boolean)} directly.
      */
+    @Deprecated
     public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
-        IMPL.setSubmitButtonEnabled(searchView, enabled);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setSubmitButtonEnabled(enabled);
     }
 
     /**
@@ -505,9 +344,13 @@
      *
      * @param searchView The SearchView to operate on.
      * @return whether the submit button is enabled automatically when necessary
+     *
+     * @deprecated Use {@link SearchView#isSubmitButtonEnabled()} directly.
      */
+    @Deprecated
     public static boolean isSubmitButtonEnabled(View searchView) {
-        return IMPL.isSubmitButtonEnabled(searchView);
+        checkIfLegalArg(searchView);
+        return ((SearchView) searchView).isSubmitButtonEnabled();
     }
 
     /**
@@ -524,25 +367,37 @@
      *
      * @see SearchManager#SUGGEST_COLUMN_FLAGS
      * @see SearchManager#FLAG_QUERY_REFINEMENT
+     *
+     * @deprecated Use {@link SearchView#setQueryRefinementEnabled(boolean)} directly.
      */
+    @Deprecated
     public static void setQueryRefinementEnabled(View searchView, boolean enable) {
-        IMPL.setQueryRefinementEnabled(searchView, enable);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setQueryRefinementEnabled(enable);
     }
 
     /**
      * Returns whether query refinement is enabled for all items or only specific ones.
      * @param searchView The SearchView to operate on.
      * @return true if enabled for all items, false otherwise.
+     *
+     * @deprecated Use {@link SearchView#isQueryRefinementEnabled()} directly.
      */
+    @Deprecated
     public static boolean isQueryRefinementEnabled(View searchView) {
-        return IMPL.isQueryRefinementEnabled(searchView);
+        checkIfLegalArg(searchView);
+        return ((SearchView) searchView).isQueryRefinementEnabled();
     }
 
     /**
      * Makes the view at most this many pixels wide
      * @param searchView The SearchView to operate on.
+     *
+     * @deprecated Use {@link SearchView#setMaxWidth(int)} directly.
      */
+    @Deprecated
     public static void setMaxWidth(View searchView, int maxpixels) {
-        IMPL.setMaxWidth(searchView, maxpixels);
+        checkIfLegalArg(searchView);
+        ((SearchView) searchView).setMaxWidth(maxpixels);
     }
 }
diff --git a/compat/java/android/support/v4/widget/TextViewCompat.java b/compat/java/android/support/v4/widget/TextViewCompat.java
index faa252a..d7ca21e 100644
--- a/compat/java/android/support/v4/widget/TextViewCompat.java
+++ b/compat/java/android/support/v4/widget/TextViewCompat.java
@@ -16,176 +16,368 @@
 
 package android.support.v4.widget;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
 import android.support.annotation.StyleRes;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
 import android.widget.TextView;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
+
 /**
- * Helper for accessing features in {@link TextView} introduced after API level
- * 4 in a backwards compatible fashion.
+ * Helper for accessing features in {@link TextView}.
  */
 public final class TextViewCompat {
 
+    /**
+     * The TextView does not auto-size text (default).
+     */
+    public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0;
+
+    /**
+     * The TextView scales text size both horizontally and vertically to fit within the
+     * container.
+     */
+    public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1;
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_UNIFORM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AutoSizeTextType {}
+
     // Hide constructor
     private TextViewCompat() {}
 
-    interface TextViewCompatImpl {
-        void setCompoundDrawablesRelative(@NonNull TextView textView,
-                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-                @Nullable Drawable bottom);
-        void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-                @Nullable Drawable bottom);
-        void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
-                @DrawableRes int bottom);
-        int getMaxLines(TextView textView);
-        int getMinLines(TextView textView);
-        void setTextAppearance(@NonNull TextView textView, @StyleRes int resId);
-        Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView);
-    }
+    static class TextViewCompatBaseImpl {
+        private static final String LOG_TAG = "TextViewCompatBase";
+        private static final int LINES = 1;
 
-    static class BaseTextViewCompatImpl implements TextViewCompatImpl {
-        @Override
+        private static Field sMaximumField;
+        private static boolean sMaximumFieldFetched;
+        private static Field sMaxModeField;
+        private static boolean sMaxModeFieldFetched;
+
+        private static Field sMinimumField;
+        private static boolean sMinimumFieldFetched;
+        private static Field sMinModeField;
+        private static boolean sMinModeFieldFetched;
+
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
             textView.setCompoundDrawables(start, top, end, bottom);
         }
 
-        @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
             textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
         }
 
-        @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
                 @DrawableRes int bottom) {
             textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
         }
 
-        @Override
+        private static Field retrieveField(String fieldName) {
+            Field field = null;
+            try {
+                field = TextView.class.getDeclaredField(fieldName);
+                field.setAccessible(true);
+            } catch (NoSuchFieldException e) {
+                Log.e(LOG_TAG, "Could not retrieve " + fieldName + " field.");
+            }
+            return field;
+        }
+
+        private static int retrieveIntFromField(Field field, TextView textView) {
+            try {
+                return field.getInt(textView);
+            } catch (IllegalAccessException e) {
+                Log.d(LOG_TAG, "Could not retrieve value of " + field.getName() + " field.");
+            }
+            return -1;
+        }
+
         public int getMaxLines(TextView textView) {
-            return TextViewCompatGingerbread.getMaxLines(textView);
+            if (!sMaxModeFieldFetched) {
+                sMaxModeField = retrieveField("mMaxMode");
+                sMaxModeFieldFetched = true;
+            }
+            if (sMaxModeField != null && retrieveIntFromField(sMaxModeField, textView) == LINES) {
+                // If the max mode is using lines, we can grab the maximum value
+                if (!sMaximumFieldFetched) {
+                    sMaximumField = retrieveField("mMaximum");
+                    sMaximumFieldFetched = true;
+                }
+                if (sMaximumField != null) {
+                    return retrieveIntFromField(sMaximumField, textView);
+                }
+            }
+            return -1;
         }
 
-        @Override
         public int getMinLines(TextView textView) {
-            return TextViewCompatGingerbread.getMinLines(textView);
+            if (!sMinModeFieldFetched) {
+                sMinModeField = retrieveField("mMinMode");
+                sMinModeFieldFetched = true;
+            }
+            if (sMinModeField != null && retrieveIntFromField(sMinModeField, textView) == LINES) {
+                // If the min mode is using lines, we can grab the maximum value
+                if (!sMinimumFieldFetched) {
+                    sMinimumField = retrieveField("mMinimum");
+                    sMinimumFieldFetched = true;
+                }
+                if (sMinimumField != null) {
+                    return retrieveIntFromField(sMinimumField, textView);
+                }
+            }
+            return -1;
         }
 
-        @Override
+        @SuppressWarnings("deprecation")
         public void setTextAppearance(TextView textView, @StyleRes int resId) {
-            TextViewCompatGingerbread.setTextAppearance(textView, resId);
+            textView.setTextAppearance(textView.getContext(), resId);
         }
 
-        @Override
         public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-            return TextViewCompatGingerbread.getCompoundDrawablesRelative(textView);
+            return textView.getCompoundDrawables();
+        }
+
+        public void setAutoSizeTextTypeWithDefaults(TextView textView, int autoSizeTextType) {
+            if (textView instanceof AutoSizeableTextView) {
+                ((AutoSizeableTextView) textView).setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+            }
+        }
+
+        public void setAutoSizeTextTypeUniformWithConfiguration(
+                TextView textView,
+                int autoSizeMinTextSize,
+                int autoSizeMaxTextSize,
+                int autoSizeStepGranularity,
+                int unit) throws IllegalArgumentException {
+            if (textView instanceof AutoSizeableTextView) {
+                ((AutoSizeableTextView) textView).setAutoSizeTextTypeUniformWithConfiguration(
+                        autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+            }
+        }
+
+        public void setAutoSizeTextTypeUniformWithPresetSizes(TextView textView,
+                @NonNull int[] presetSizes, int unit) throws IllegalArgumentException {
+            if (textView instanceof AutoSizeableTextView) {
+                ((AutoSizeableTextView) textView).setAutoSizeTextTypeUniformWithPresetSizes(
+                        presetSizes, unit);
+            }
+        }
+
+        public int getAutoSizeTextType(TextView textView) {
+            if (textView instanceof AutoSizeableTextView) {
+                return ((AutoSizeableTextView) textView).getAutoSizeTextType();
+            }
+            return AUTO_SIZE_TEXT_TYPE_NONE;
+        }
+
+        public int getAutoSizeStepGranularity(TextView textView) {
+            if (textView instanceof AutoSizeableTextView) {
+                return ((AutoSizeableTextView) textView).getAutoSizeStepGranularity();
+            }
+            return -1;
+        }
+
+        public int getAutoSizeMinTextSize(TextView textView) {
+            if (textView instanceof AutoSizeableTextView) {
+                return ((AutoSizeableTextView) textView).getAutoSizeMinTextSize();
+            }
+            return -1;
+        }
+
+        public int getAutoSizeMaxTextSize(TextView textView) {
+            if (textView instanceof AutoSizeableTextView) {
+                return ((AutoSizeableTextView) textView).getAutoSizeMaxTextSize();
+            }
+            return -1;
+        }
+
+        public int[] getAutoSizeTextAvailableSizes(TextView textView) {
+            if (textView instanceof AutoSizeableTextView) {
+                return ((AutoSizeableTextView) textView).getAutoSizeTextAvailableSizes();
+            }
+            return new int[0];
         }
     }
 
-    static class JbTextViewCompatImpl extends BaseTextViewCompatImpl {
+    @RequiresApi(16)
+    static class TextViewCompatApi16Impl extends TextViewCompatBaseImpl {
         @Override
         public int getMaxLines(TextView textView) {
-            return TextViewCompatJb.getMaxLines(textView);
+            return textView.getMaxLines();
         }
 
         @Override
         public int getMinLines(TextView textView) {
-            return TextViewCompatJb.getMinLines(textView);
+            return textView.getMinLines();
         }
     }
 
-    static class JbMr1TextViewCompatImpl extends JbTextViewCompatImpl {
+    @RequiresApi(17)
+    static class TextViewCompatApi17Impl extends TextViewCompatApi16Impl {
         @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
-            TextViewCompatJbMr1.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+            boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            textView.setCompoundDrawables(rtl ? end : start, top, rtl ? start : end, bottom);
         }
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
-            TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
-                    start, top, end, bottom);
+            boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            textView.setCompoundDrawablesWithIntrinsicBounds(rtl ? end : start, top,
+                    rtl ? start : end,  bottom);
         }
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
                 @DrawableRes int bottom) {
-            TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
-                    start, top, end, bottom);
+            boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            textView.setCompoundDrawablesWithIntrinsicBounds(rtl ? end : start, top,
+                    rtl ? start : end, bottom);
         }
 
         @Override
         public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-            return TextViewCompatJbMr1.getCompoundDrawablesRelative(textView);
+            final boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            final Drawable[] compounds = textView.getCompoundDrawables();
+            if (rtl) {
+                // If we're on RTL, we need to invert the horizontal result like above
+                final Drawable start = compounds[2];
+                final Drawable end = compounds[0];
+                compounds[0] = start;
+                compounds[2] = end;
+            }
+            return compounds;
         }
     }
 
-    static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl {
+    @RequiresApi(18)
+    static class TextViewCompatApi18Impl extends TextViewCompatApi17Impl {
         @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
-            TextViewCompatJbMr2.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+            textView.setCompoundDrawablesRelative(start, top, end, bottom);
         }
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
-            TextViewCompatJbMr2
-                    .setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end,
-                            bottom);
+            textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
         }
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
                 @DrawableRes int bottom) {
-            TextViewCompatJbMr2.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
-                    start, top, end, bottom);
+            textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
         }
 
         @Override
         public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-            return TextViewCompatJbMr2.getCompoundDrawablesRelative(textView);
+            return textView.getCompoundDrawablesRelative();
         }
     }
 
-    static class Api23TextViewCompatImpl extends JbMr2TextViewCompatImpl {
+    @RequiresApi(23)
+    static class TextViewCompatApi23Impl extends TextViewCompatApi18Impl {
         @Override
         public void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
-            TextViewCompatApi23.setTextAppearance(textView, resId);
+            textView.setTextAppearance(resId);
         }
     }
 
-    static final TextViewCompatImpl IMPL;
+    @RequiresApi(26)
+    static class TextViewCompatApi26Impl extends TextViewCompatApi23Impl {
+        @Override
+        public void setAutoSizeTextTypeWithDefaults(TextView textView, int autoSizeTextType) {
+            textView.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+        }
+
+        @Override
+        public void setAutoSizeTextTypeUniformWithConfiguration(
+                TextView textView,
+                int autoSizeMinTextSize,
+                int autoSizeMaxTextSize,
+                int autoSizeStepGranularity,
+                int unit) throws IllegalArgumentException {
+            textView.setAutoSizeTextTypeUniformWithConfiguration(
+                    autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+        }
+
+        @Override
+        public void setAutoSizeTextTypeUniformWithPresetSizes(TextView textView,
+                @NonNull int[] presetSizes, int unit) throws IllegalArgumentException {
+            textView.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+        }
+
+        @Override
+        public int getAutoSizeTextType(TextView textView) {
+            return textView.getAutoSizeTextType();
+        }
+
+        @Override
+        public int getAutoSizeStepGranularity(TextView textView) {
+            return textView.getAutoSizeStepGranularity();
+        }
+
+        @Override
+        public int getAutoSizeMinTextSize(TextView textView) {
+            return textView.getAutoSizeMinTextSize();
+        }
+
+        @Override
+        public int getAutoSizeMaxTextSize(TextView textView) {
+            return textView.getAutoSizeMaxTextSize();
+        }
+
+        @Override
+        public int[] getAutoSizeTextAvailableSizes(TextView textView) {
+            return textView.getAutoSizeTextAvailableSizes();
+        }
+    }
+
+    static final TextViewCompatBaseImpl IMPL;
 
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 23) {
-            IMPL = new Api23TextViewCompatImpl();
-        } else if (version >= 18) {
-            IMPL = new JbMr2TextViewCompatImpl();
-        } else if (version >= 17) {
-            IMPL = new JbMr1TextViewCompatImpl();
-        } else if (version >= 16) {
-            IMPL = new JbTextViewCompatImpl();
+        if (Build.VERSION.SDK_INT >= 26) {
+            IMPL = new TextViewCompatApi26Impl();
+        } else if (Build.VERSION.SDK_INT >= 23) {
+            IMPL = new TextViewCompatApi23Impl();
+        } else if (Build.VERSION.SDK_INT >= 18) {
+            IMPL = new TextViewCompatApi18Impl();
+        } else if (Build.VERSION.SDK_INT >= 17) {
+            IMPL = new TextViewCompatApi17Impl();
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            IMPL = new TextViewCompatApi16Impl();
         } else {
-            IMPL = new BaseTextViewCompatImpl();
+            IMPL = new TextViewCompatBaseImpl();
         }
     }
 
@@ -289,4 +481,119 @@
     public static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
         return IMPL.getCompoundDrawablesRelative(textView);
     }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds by using the default auto-size configuration.
+     *
+     * @param autoSizeTextType the type of auto-size. Must be one of
+     *        {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *        {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}
+     *
+     * @attr name android:autoSizeTextType
+     */
+    public static void setAutoSizeTextTypeWithDefaults(TextView textView, int autoSizeTextType) {
+        IMPL.setAutoSizeTextTypeWithDefaults(textView, autoSizeTextType);
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If all the configuration params are valid the type of auto-size is
+     * set to {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param autoSizeMinTextSize the minimum text size available for auto-size
+     * @param autoSizeMaxTextSize the maximum text size available for auto-size
+     * @param autoSizeStepGranularity the auto-size step granularity. It is used in conjunction with
+     *                                the minimum and maximum text size in order to build the set of
+     *                                text sizes the system uses to choose from when auto-sizing
+     * @param unit the desired dimension unit for all sizes above. See {@link TypedValue} for the
+     *             possible dimension units
+     *
+     * @throws IllegalArgumentException if any of the configuration params are invalid.
+     *
+     * @attr name android:autoSizeTextType
+     * @attr name android:autoSizeTextType
+     * @attr name android:autoSizeMinTextSize
+     * @attr name android:autoSizeMaxTextSize
+     * @attr name android:autoSizeStepGranularity
+     */
+    public static void setAutoSizeTextTypeUniformWithConfiguration(
+            TextView textView,
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        IMPL.setAutoSizeTextTypeUniformWithConfiguration(textView, autoSizeMinTextSize,
+                autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If at least one value from the <code>presetSizes</code> is valid
+     * then the type of auto-size is set to {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param presetSizes an {@code int} array of sizes in pixels
+     * @param unit the desired dimension unit for the preset sizes above. See {@link TypedValue} for
+     *             the possible dimension units
+     *
+     * @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid.
+     *_
+     * @attr name android:autoSizeTextType
+     * @attr name android:autoSizePresetSizes
+     */
+    public static void setAutoSizeTextTypeUniformWithPresetSizes(TextView textView,
+            @NonNull int[] presetSizes, int unit) throws IllegalArgumentException {
+        IMPL.setAutoSizeTextTypeUniformWithPresetSizes(textView, presetSizes, unit);
+    }
+
+    /**
+     * Returns the type of auto-size set for this widget.
+     *
+     * @return an {@code int} corresponding to one of the auto-size types:
+     *         {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *         {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}
+     *
+     * @attr name android:autoSizeTextType
+     */
+    public static int getAutoSizeTextType(TextView textView) {
+        return IMPL.getAutoSizeTextType(textView);
+    }
+
+    /**
+     * @return the current auto-size step granularity in pixels.
+     *
+     * @attr name android:autoSizeStepGranularity
+     */
+    public static int getAutoSizeStepGranularity(TextView textView) {
+        return IMPL.getAutoSizeStepGranularity(textView);
+    }
+
+    /**
+     * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
+     *
+     * @attr name android:autoSizeMinTextSize
+     */
+    public static int getAutoSizeMinTextSize(TextView textView) {
+        return IMPL.getAutoSizeMinTextSize(textView);
+    }
+
+    /**
+     * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
+     *
+     * @attr name android:autoSizeMaxTextSize
+     */
+    public static int getAutoSizeMaxTextSize(TextView textView) {
+        return IMPL.getAutoSizeMaxTextSize(textView);
+    }
+
+    /**
+     * @return the current auto-size {@code int} sizes array (in pixels).
+     *
+     * @attr name android:autoSizePresetSizes
+     */
+    public static int[] getAutoSizeTextAvailableSizes(TextView textView) {
+        return IMPL.getAutoSizeTextAvailableSizes(textView);
+    }
 }
diff --git a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
deleted file mode 100644
index 656fae9..0000000
--- a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.widget.TextView;
-
-import java.lang.reflect.Field;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(9)
-@TargetApi(9)
-class TextViewCompatGingerbread {
-
-    private static final String LOG_TAG = "TextViewCompatGingerbread";
-    private static final int LINES = 1;
-
-    private static Field sMaximumField;
-    private static boolean sMaximumFieldFetched;
-    private static Field sMaxModeField;
-    private static boolean sMaxModeFieldFetched;
-
-    private static Field sMinimumField;
-    private static boolean sMinimumFieldFetched;
-    private static Field sMinModeField;
-    private static boolean sMinModeFieldFetched;
-
-    static int getMaxLines(TextView textView) {
-        if (!sMaxModeFieldFetched) {
-            sMaxModeField = retrieveField("mMaxMode");
-            sMaxModeFieldFetched = true;
-        }
-        if (sMaxModeField != null && retrieveIntFromField(sMaxModeField, textView) == LINES) {
-            // If the max mode is using lines, we can grab the maximum value
-            if (!sMaximumFieldFetched) {
-                sMaximumField = retrieveField("mMaximum");
-                sMaximumFieldFetched = true;
-            }
-            if (sMaximumField != null) {
-                return retrieveIntFromField(sMaximumField, textView);
-            }
-        }
-        return -1;
-    }
-
-    static int getMinLines(TextView textView) {
-        if (!sMinModeFieldFetched) {
-            sMinModeField = retrieveField("mMinMode");
-            sMinModeFieldFetched = true;
-        }
-        if (sMinModeField != null && retrieveIntFromField(sMinModeField, textView) == LINES) {
-            // If the min mode is using lines, we can grab the maximum value
-            if (!sMinimumFieldFetched) {
-                sMinimumField = retrieveField("mMinimum");
-                sMinimumFieldFetched = true;
-            }
-            if (sMinimumField != null) {
-                return retrieveIntFromField(sMinimumField, textView);
-            }
-        }
-        return -1;
-    }
-
-    private static Field retrieveField(String fieldName) {
-        Field field = null;
-        try {
-            field = TextView.class.getDeclaredField(fieldName);
-            field.setAccessible(true);
-        } catch (NoSuchFieldException e) {
-            Log.e(LOG_TAG, "Could not retrieve " + fieldName + " field.");
-        }
-        return field;
-    }
-
-    private static int retrieveIntFromField(Field field, TextView textView) {
-        try {
-            return field.getInt(textView);
-        } catch (IllegalAccessException e) {
-            Log.d(LOG_TAG, "Could not retrieve value of " + field.getName() + " field.");
-        }
-        return -1;
-    }
-
-    static void setTextAppearance(TextView textView, int resId) {
-        textView.setTextAppearance(textView.getContext(), resId);
-    }
-
-    static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-        return textView.getCompoundDrawables();
-    }
-}
diff --git a/compat/java/android/support/v4/widget/TintableCompoundButton.java b/compat/java/android/support/v4/widget/TintableCompoundButton.java
new file mode 100644
index 0000000..f739fac
--- /dev/null
+++ b/compat/java/android/support/v4/widget/TintableCompoundButton.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+
+/**
+ * Interface which allows a {@link android.widget.CompoundButton} to receive tinting
+ * calls from {@code CompoundButtonCompat} when running on API v20 devices or lower.
+ */
+public interface TintableCompoundButton {
+
+    /**
+     * Applies a tint to the button drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to
+     * {@link android.widget.CompoundButton#setButtonDrawable(Drawable) setButtonDrawable(Drawable)}
+     * should automatically mutate the drawable and apply the specified tint and tint mode.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     */
+    void setSupportButtonTintList(@Nullable ColorStateList tint);
+
+    /**
+     * Returns the tint applied to the button drawable
+     *
+     * @see #setSupportButtonTintList(ColorStateList)
+     */
+    @Nullable
+    ColorStateList getSupportButtonTintList();
+
+    /**
+     * Specifies the blending mode which should be used to apply the tint specified by
+     * {@link #setSupportButtonTintList(ColorStateList)} to the button drawable. The
+     * default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @see #getSupportButtonTintMode()
+     * @see android.support.v4.graphics.drawable.DrawableCompat#setTintMode(Drawable,
+     * PorterDuff.Mode)
+     */
+    void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode);
+
+    /**
+     * Returns the blending mode used to apply the tint to the button drawable
+     *
+     * @see #setSupportButtonTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    PorterDuff.Mode getSupportButtonTintMode();
+}
diff --git a/compat/gingerbread/android/support/v4/widget/TintableImageSourceView.java b/compat/java/android/support/v4/widget/TintableImageSourceView.java
similarity index 100%
rename from compat/gingerbread/android/support/v4/widget/TintableImageSourceView.java
rename to compat/java/android/support/v4/widget/TintableImageSourceView.java
diff --git a/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
deleted file mode 100644
index eecc581..0000000
--- a/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.content.res;
-
-import android.content.res.Resources;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(17)
-@TargetApi(17)
-class ConfigurationHelperJellybeanMr1 {
-    static int getDensityDpi(@NonNull Resources resources) {
-        return resources.getConfiguration().densityDpi;
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
deleted file mode 100644
index d8da519..0000000
--- a/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-
-/**
- * Implementation of drawable compatibility that can call Jellybean MR1 APIs.
- */
-
-@RequiresApi(17)
-@TargetApi(17)
-class DrawableCompatJellybeanMr1 {
-
-    private static final String TAG = "DrawableCompatJellybeanMr1";
-
-    private static Method sSetLayoutDirectionMethod;
-    private static boolean sSetLayoutDirectionMethodFetched;
-
-    private static Method sGetLayoutDirectionMethod;
-    private static boolean sGetLayoutDirectionMethodFetched;
-
-    public static boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
-        if (!sSetLayoutDirectionMethodFetched) {
-            try {
-                sSetLayoutDirectionMethod =
-                        Drawable.class.getDeclaredMethod("setLayoutDirection", int.class);
-                sSetLayoutDirectionMethod.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                Log.i(TAG, "Failed to retrieve setLayoutDirection(int) method", e);
-            }
-            sSetLayoutDirectionMethodFetched = true;
-        }
-
-        if (sSetLayoutDirectionMethod != null) {
-            try {
-                sSetLayoutDirectionMethod.invoke(drawable, layoutDirection);
-                return true;
-            } catch (Exception e) {
-                Log.i(TAG, "Failed to invoke setLayoutDirection(int) via reflection", e);
-                sSetLayoutDirectionMethod = null;
-            }
-        }
-        return false;
-    }
-
-    public static int getLayoutDirection(Drawable drawable) {
-        if (!sGetLayoutDirectionMethodFetched) {
-            try {
-                sGetLayoutDirectionMethod = Drawable.class.getDeclaredMethod("getLayoutDirection");
-                sGetLayoutDirectionMethod.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                Log.i(TAG, "Failed to retrieve getLayoutDirection() method", e);
-            }
-            sGetLayoutDirectionMethodFetched = true;
-        }
-
-        if (sGetLayoutDirectionMethod != null) {
-            try {
-                return (int) sGetLayoutDirectionMethod.invoke(drawable);
-            } catch (Exception e) {
-                Log.i(TAG, "Failed to invoke getLayoutDirection() via reflection", e);
-                sGetLayoutDirectionMethod = null;
-            }
-        }
-        return -1;
-    }
-
-}
diff --git a/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
deleted file mode 100644
index 9cb5637..0000000
--- a/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 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.hardware.display;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.view.Display;
-
-@RequiresApi(17)
-@TargetApi(17)
-final class DisplayManagerJellybeanMr1 {
-    public static Object getDisplayManager(Context context) {
-        return context.getSystemService(Context.DISPLAY_SERVICE);
-    }
-
-    public static Display getDisplay(Object displayManagerObj, int displayId) {
-        return ((android.hardware.display.DisplayManager)displayManagerObj).getDisplay(displayId);
-    }
-
-    public static Display[] getDisplays(Object displayManagerObj) {
-        return ((android.hardware.display.DisplayManager)displayManagerObj).getDisplays();
-    }
-
-    public static Display[] getDisplays(Object displayManagerObj, String category) {
-        return ((android.hardware.display.DisplayManager)displayManagerObj).getDisplays(category);
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
deleted file mode 100644
index ad354e7..0000000
--- a/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.text;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.text.TextUtils;
-
-import java.util.Locale;
-
-/**
- * Jellybean MR1 - specific TextUtils API access.
- */
-
-@RequiresApi(17)
-@TargetApi(17)
-class TextUtilsCompatJellybeanMr1 {
-    @NonNull
-    public static String htmlEncode(@NonNull String s) {
-        return TextUtils.htmlEncode(s);
-    }
-
-    public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
-        return TextUtils.getLayoutDirectionFromLocale(locale);
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
deleted file mode 100644
index efb1d77..0000000
--- a/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.Gravity;
-
-@RequiresApi(17)
-@TargetApi(17)
-class GravityCompatJellybeanMr1 {
-
-    public static int getAbsoluteGravity(int gravity, int layoutDirection) {
-        return Gravity.getAbsoluteGravity(gravity, layoutDirection);
-    }
-
-    public static void apply(int gravity, int w, int h, Rect container, Rect outRect,
-            int layoutDirection) {
-        Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
-    }
-
-    public static void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj,
-            Rect outRect, int layoutDirection) {
-        Gravity.apply(gravity, w, h, container, xAdj, yAdj, outRect, layoutDirection);
-    }
-
-    public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
-        Gravity.applyDisplay(gravity, display, inoutObj, layoutDirection);
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
deleted file mode 100644
index c446abd..0000000
--- a/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ViewGroup;
-
-@RequiresApi(17)
-@TargetApi(17)
-class MarginLayoutParamsCompatJellybeanMr1 {
-    public static int getMarginStart(ViewGroup.MarginLayoutParams lp) {
-        return lp.getMarginStart();
-    }
-
-    public static int getMarginEnd(ViewGroup.MarginLayoutParams lp) {
-        return lp.getMarginEnd();
-    }
-
-    public static void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart) {
-        lp.setMarginStart(marginStart);
-    }
-
-    public static void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd) {
-        lp.setMarginEnd(marginEnd);
-    }
-
-    public static boolean isMarginRelative(ViewGroup.MarginLayoutParams lp) {
-        return lp.isMarginRelative();
-    }
-
-    public static int getLayoutDirection(ViewGroup.MarginLayoutParams lp) {
-        return lp.getLayoutDirection();
-    }
-
-    public static void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-        lp.setLayoutDirection(layoutDirection);
-    }
-
-    public static void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp,
-            int layoutDirection) {
-        lp.resolveLayoutDirection(layoutDirection);
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
deleted file mode 100644
index 79b5ce2..0000000
--- a/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2012 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.view;
-
-import android.annotation.TargetApi;
-import android.graphics.Paint;
-import android.support.annotation.RequiresApi;
-import android.view.Display;
-import android.view.View;
-
-/**
- * Jellybean MR1 - specific View API access.
- */
-
-@RequiresApi(17)
-@TargetApi(17)
-class ViewCompatJellybeanMr1 {
-
-    public static int getLabelFor(View view) {
-        return view.getLabelFor();
-    }
-
-    public static void setLabelFor(View view, int id) {
-        view.setLabelFor(id);
-    }
-
-    public static void setLayerPaint(View view, Paint paint) {
-        view.setLayerPaint(paint);
-    }
-
-    public static int getLayoutDirection(View view) {
-        return view.getLayoutDirection();
-    }
-
-    public static void setLayoutDirection(View view, int layoutDirection) {
-        view.setLayoutDirection(layoutDirection);
-    }
-
-    public static int getPaddingStart(View view) {
-        return view.getPaddingStart();
-    }
-
-    public static int getPaddingEnd(View view) {
-        return view.getPaddingEnd();
-    }
-
-    public static void setPaddingRelative(View view, int start, int top, int end, int bottom) {
-        view.setPaddingRelative(start, top, end, bottom);
-    }
-
-    public static int getWindowSystemUiVisibility(View view) {
-        return view.getWindowSystemUiVisibility();
-    }
-
-    public static boolean isPaddingRelative(View view) {
-        return view.isPaddingRelative();
-    }
-
-    public static Display getDisplay(View view) {
-        return view.getDisplay();
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
deleted file mode 100644
index aa20646..0000000
--- a/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 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.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-@RequiresApi(17)
-@TargetApi(17)
-class AccessibilityNodeInfoCompatJellybeanMr1 {
-
-    public static void setLabelFor(Object info, View labeled) {
-        ((AccessibilityNodeInfo) info).setLabelFor(labeled);
-    }
-
-    public static void setLabelFor(Object info, View root, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).setLabelFor(root, virtualDescendantId);
-    }
-
-    public static Object getLabelFor(Object info) {
-        return ((AccessibilityNodeInfo) info).getLabelFor();
-    }
-
-    public static void setLabeledBy(Object info, View labeled) {
-        ((AccessibilityNodeInfo) info).setLabeledBy(labeled);
-    }
-
-    public static void setLabeledBy(Object info, View root, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).setLabeledBy(root, virtualDescendantId);
-    }
-
-    public static Object getLabeledBy(Object info) {
-        return ((AccessibilityNodeInfo) info).getLabeledBy();
-    }
-}
diff --git a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
deleted file mode 100644
index c8dbf98..0000000
--- a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.annotation.TargetApi;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.widget.TextView;
-
-@RequiresApi(17)
-@TargetApi(17)
-class TextViewCompatJbMr1 {
-
-    public static void setCompoundDrawablesRelative(@NonNull TextView textView,
-            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-            @Nullable Drawable bottom) {
-        boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        textView.setCompoundDrawables(rtl ? end : start, top, rtl ? start : end, bottom);
-    }
-
-    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-            @Nullable Drawable bottom) {
-        boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        textView.setCompoundDrawablesWithIntrinsicBounds(rtl ? end : start, top, rtl ? start : end,
-                bottom);
-    }
-
-    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            int start, int top, int end, int bottom) {
-        boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        textView.setCompoundDrawablesWithIntrinsicBounds(rtl ? end : start, top, rtl ? start : end,
-                bottom);
-    }
-
-    public static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-        final boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        final Drawable[] compounds = textView.getCompoundDrawables();
-        if (rtl) {
-            // If we're on RTL, we need to invert the horizontal result like above
-            final Drawable start = compounds[2];
-            final Drawable end = compounds[0];
-            compounds[0] = start;
-            compounds[2] = end;
-        }
-        return compounds;
-    }
-
-}
diff --git a/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java b/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
deleted file mode 100644
index acc72b1..0000000
--- a/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2013 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.accessibilityservice;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * ICS implementation of the new APIs in AccessibilityServiceInfo.
- */
-
-@RequiresApi(18)
-@TargetApi(18)
-class AccessibilityServiceInfoCompatJellyBeanMr2 {
-
-    public static int getCapabilities(AccessibilityServiceInfo info) {
-        return info.getCapabilities();
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
deleted file mode 100644
index 598ff31..0000000
--- a/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.os.Bundle;
-import android.os.IBinder;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * @hide
- */
-@RequiresApi(18)
-@TargetApi(18)
-class BundleCompatJellybeanMR2 {
-    public static IBinder getBinder(Bundle bundle, String key) {
-        return bundle.getBinder(key);
-    }
-
-    public static void putBinder(Bundle bundle, String key, IBinder binder) {
-        bundle.putBinder(key, binder);
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
deleted file mode 100644
index 20739d1..0000000
--- a/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.graphics;
-
-import android.graphics.Bitmap;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(18)
-@TargetApi(18)
-class BitmapCompatJellybeanMR2 {
-    public static boolean hasMipMap(Bitmap bitmap) {
-        return bitmap.hasMipMap();
-    }
-
-    public static void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
-        bitmap.setHasMipMap(hasMipMap);
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
deleted file mode 100644
index a41816d..0000000
--- a/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.os;
-
-import android.os.Trace;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(18)
-@TargetApi(18)
-class TraceJellybeanMR2 {
-    public static void beginSection(String section) {
-        Trace.beginSection(section);
-    }
-
-    public static void endSection() {
-        Trace.endSection();
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
deleted file mode 100644
index 46c5d4e..0000000
--- a/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-/**
- * Jellybean MR2 - specific View API access.
- */
-
-@RequiresApi(18)
-@TargetApi(18)
-class ViewCompatJellybeanMr2 {
-
-    public static Rect getClipBounds(View view) {
-        return view.getClipBounds();
-    }
-
-    public static void setClipBounds(View view, Rect clipBounds) {
-        view.setClipBounds(clipBounds);
-    }
-
-    public static boolean isInLayout(View view) {
-        return view.isInLayout();
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
deleted file mode 100644
index e1c8532..0000000
--- a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ViewGroup;
-
-@RequiresApi(18)
-@TargetApi(18)
-class ViewGroupCompatJellybeanMR2 {
-    public static int getLayoutMode(ViewGroup group) {
-        return group.getLayoutMode();
-    }
-
-    public static void setLayoutMode(ViewGroup group, int mode) {
-        group.setLayoutMode(mode);
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
deleted file mode 100644
index 14e76a9..0000000
--- a/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-@RequiresApi(18)
-@TargetApi(18)
-class ViewPropertyAnimatorCompatJellybeanMr2 {
-    public static Interpolator getInterpolator(View view) {
-        return (Interpolator) view.animate().getInterpolator();
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
deleted file mode 100644
index 82bfa11..0000000
--- a/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 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.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import java.util.List;
-
-@RequiresApi(18)
-@TargetApi(18)
-class AccessibilityNodeInfoCompatJellybeanMr2 {
-
-    public static void setViewIdResourceName(Object info, String viewId) {
-        ((AccessibilityNodeInfo) info).setViewIdResourceName(viewId);
-    }
-
-    public static String getViewIdResourceName(Object info) {
-        return ((AccessibilityNodeInfo) info).getViewIdResourceName();
-    }
-
-    @SuppressWarnings("unchecked")
-    public static List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId) {
-        Object result = ((AccessibilityNodeInfo) info).findAccessibilityNodeInfosByViewId(viewId);
-        return (List<Object>) result;
-    }
-
-    public static void setTextSelection(Object info, int start, int end) {
-        ((AccessibilityNodeInfo) info).setTextSelection(start, end);
-    }
-
-    public static int getTextSelectionStart(Object info) {
-        return ((AccessibilityNodeInfo) info).getTextSelectionStart();
-    }
-
-    public static int getTextSelectionEnd(Object info) {
-        return ((AccessibilityNodeInfo) info).getTextSelectionEnd();
-    }
-
-    public static boolean isEditable(Object info) {
-        return ((AccessibilityNodeInfo) info).isEditable();
-    }
-
-    public static void setEditable(Object info, boolean editable) {
-        ((AccessibilityNodeInfo) info).setEditable(editable);
-    }
-
-    public static boolean refresh(Object info) {
-        return ((AccessibilityNodeInfo) info).refresh();
-    }
-}
diff --git a/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java b/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
deleted file mode 100644
index d80725b..0000000
--- a/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.annotation.TargetApi;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.widget.TextView;
-
-@RequiresApi(18)
-@TargetApi(18)
-class TextViewCompatJbMr2 {
-
-    public static void setCompoundDrawablesRelative(@NonNull TextView textView,
-            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-            @Nullable Drawable bottom) {
-        textView.setCompoundDrawablesRelative(start, top, end, bottom);
-    }
-
-    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-            @Nullable Drawable bottom) {
-        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
-    }
-
-    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
-            @DrawableRes int bottom) {
-        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
-    }
-
-    public static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-        return textView.getCompoundDrawablesRelative();
-    }
-
-}
diff --git a/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java b/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
deleted file mode 100644
index d42cefc..0000000
--- a/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.accessibilityservice;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.pm.PackageManager;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * JB implementation of the new APIs in AccessibilityServiceInfo.
- */
-
-@RequiresApi(16)
-@TargetApi(16)
-class AccessibilityServiceInfoCompatJellyBean {
-
-    public static String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
-        return info.loadDescription(pm);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/app/ActivityCompatJB.java b/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
deleted file mode 100644
index ad1c9aa..0000000
--- a/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(16)
-@TargetApi(16)
-class ActivityCompatJB {
-    public static void startActivityForResult(Activity activity, Intent intent, int requestCode, Bundle options) {
-        activity.startActivityForResult(intent, requestCode, options);
-    }
-
-    public static void startIntentSenderForResult(Activity activity, IntentSender intent,
-            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
-            Bundle options) throws IntentSender.SendIntentException {
-        activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
-                flagsValues, extraFlags, options);
-    }
-
-    public static void finishAffinity(Activity activity) {
-        activity.finishAffinity();
-    }
-}
diff --git a/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java b/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
deleted file mode 100644
index 1655c4b..0000000
--- a/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.annotation.TargetApi;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-@RequiresApi(16)
-@TargetApi(16)
-class ActivityOptionsCompatJB {
-
-    public static ActivityOptionsCompatJB makeCustomAnimation(Context context,
-            int enterResId, int exitResId) {
-        return new ActivityOptionsCompatJB(
-            ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
-    }
-
-    public static ActivityOptionsCompatJB makeScaleUpAnimation(View source,
-            int startX, int startY, int startWidth, int startHeight) {
-        return new ActivityOptionsCompatJB(
-            ActivityOptions.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight));
-    }
-
-    public static ActivityOptionsCompatJB makeThumbnailScaleUpAnimation(View source,
-            Bitmap thumbnail, int startX, int startY) {
-        return new ActivityOptionsCompatJB(
-            ActivityOptions.makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY));
-    }
-
-    private final ActivityOptions mActivityOptions;
-
-    private ActivityOptionsCompatJB(ActivityOptions activityOptions) {
-        mActivityOptions = activityOptions;
-    }
-
-    public Bundle toBundle() {
-        return mActivityOptions.toBundle();
-    }
-
-    public void update(ActivityOptionsCompatJB otherOptions) {
-        mActivityOptions.update(otherOptions.mActivityOptions);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/app/NotificationBuilderWithActions.java b/compat/jellybean/android/support/v4/app/NotificationBuilderWithActions.java
index 8e8d8ce..d83b164 100644
--- a/compat/jellybean/android/support/v4/app/NotificationBuilderWithActions.java
+++ b/compat/jellybean/android/support/v4/app/NotificationBuilderWithActions.java
@@ -20,5 +20,5 @@
  * Interface implemented by notification compat builders that support adding actions.
  */
 interface NotificationBuilderWithActions {
-    public void addAction(NotificationCompatBase.Action action);
+    void addAction(NotificationCompatBase.Action action);
 }
diff --git a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
index c50e5f0..426dad9 100644
--- a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -33,18 +32,11 @@
 import java.util.List;
 
 @RequiresApi(16)
-@TargetApi(16)
 class NotificationCompatJellybean {
     public static final String TAG = "NotificationCompat";
 
     // Extras keys used for Jellybean SDK and above.
-    static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
-    static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
-    static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
-    static final String EXTRA_GROUP_KEY = "android.support.groupKey";
-    static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
-    static final String EXTRA_SORT_KEY = "android.support.sortKey";
-    static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    static final String EXTRA_DATA_ONLY_REMOTE_INPUTS = "android.support.dataRemoteInputs";
     static final String EXTRA_ALLOW_GENERATED_REPLIES = "android.support.allowGeneratedReplies";
 
     // Bundle keys for storing action fields in a bundle
@@ -53,7 +45,7 @@
     private static final String KEY_ACTION_INTENT = "actionIntent";
     private static final String KEY_EXTRAS = "extras";
     private static final String KEY_REMOTE_INPUTS = "remoteInputs";
-    private static final String KEY_ALLOW_GENERATED_REPLIES = "allowGeneratedReplies";
+    private static final String KEY_DATA_ONLY_REMOTE_INPUTS = "dataOnlyRemoteInputs";
 
     private static final Object sExtrasLock = new Object();
     private static Field sExtrasField;
@@ -113,18 +105,18 @@
                 mExtras.putAll(extras);
             }
             if (localOnly) {
-                mExtras.putBoolean(EXTRA_LOCAL_ONLY, true);
+                mExtras.putBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY, true);
             }
             if (groupKey != null) {
-                mExtras.putString(EXTRA_GROUP_KEY, groupKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_GROUP_KEY, groupKey);
                 if (groupSummary) {
-                    mExtras.putBoolean(EXTRA_GROUP_SUMMARY, true);
+                    mExtras.putBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY, true);
                 } else {
-                    mExtras.putBoolean(EXTRA_USE_SIDE_CHANNEL, true);
+                    mExtras.putBoolean(NotificationManagerCompat.EXTRA_USE_SIDE_CHANNEL, true);
                 }
             }
             if (sortKey != null) {
-                mExtras.putString(EXTRA_SORT_KEY, sortKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_SORT_KEY, sortKey);
             }
             mContentView = contentView;
             mBigContentView = bigContentView;
@@ -140,6 +132,7 @@
             return b;
         }
 
+        @Override
         public Notification build() {
             Notification notif = b.build();
             // Merge in developer provided extras, but let the values already set
@@ -155,7 +148,8 @@
             SparseArray<Bundle> actionExtrasMap = buildActionExtrasMap(mActionExtrasList);
             if (actionExtrasMap != null) {
                 // Add the action extras sparse array if any action was added with extras.
-                getExtras(notif).putSparseParcelableArray(EXTRA_ACTION_EXTRAS, actionExtrasMap);
+                getExtras(notif).putSparseParcelableArray(
+                        NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
             }
             if (mContentView != null) {
                 notif.contentView = mContentView;
@@ -262,15 +256,20 @@
             RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory, int icon,
             CharSequence title, PendingIntent actionIntent, Bundle extras) {
         RemoteInputCompatBase.RemoteInput[] remoteInputs = null;
+        RemoteInputCompatBase.RemoteInput[] dataOnlyRemoteInputs = null;
         boolean allowGeneratedReplies = false;
         if (extras != null) {
             remoteInputs = RemoteInputCompatJellybean.fromBundleArray(
-                    BundleUtil.getBundleArrayFromBundle(extras, EXTRA_REMOTE_INPUTS),
+                    BundleUtil.getBundleArrayFromBundle(extras,
+                            NotificationCompatExtras.EXTRA_REMOTE_INPUTS),
+                    remoteInputFactory);
+            dataOnlyRemoteInputs = RemoteInputCompatJellybean.fromBundleArray(
+                    BundleUtil.getBundleArrayFromBundle(extras, EXTRA_DATA_ONLY_REMOTE_INPUTS),
                     remoteInputFactory);
             allowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES);
         }
         return factory.build(icon, title, actionIntent, extras, remoteInputs,
-                allowGeneratedReplies);
+                dataOnlyRemoteInputs, allowGeneratedReplies);
     }
 
     public static Bundle writeActionAndGetExtras(
@@ -278,9 +277,13 @@
         builder.addAction(action.getIcon(), action.getTitle(), action.getActionIntent());
         Bundle actionExtras = new Bundle(action.getExtras());
         if (action.getRemoteInputs() != null) {
-            actionExtras.putParcelableArray(EXTRA_REMOTE_INPUTS,
+            actionExtras.putParcelableArray(NotificationCompatExtras.EXTRA_REMOTE_INPUTS,
                     RemoteInputCompatJellybean.toBundleArray(action.getRemoteInputs()));
         }
+        if (action.getDataOnlyRemoteInputs() != null) {
+            actionExtras.putParcelableArray(EXTRA_DATA_ONLY_REMOTE_INPUTS,
+                    RemoteInputCompatJellybean.toBundleArray(action.getDataOnlyRemoteInputs()));
+        }
         actionExtras.putBoolean(EXTRA_ALLOW_GENERATED_REPLIES,
                 action.getAllowGeneratedReplies());
         return actionExtras;
@@ -305,7 +308,7 @@
                     Bundle extras = getExtras(notif);
                     if (extras != null) {
                         SparseArray<Bundle> actionExtrasMap = extras.getSparseParcelableArray(
-                                EXTRA_ACTION_EXTRAS);
+                                NotificationCompatExtras.EXTRA_ACTION_EXTRAS);
                         if (actionExtrasMap != null) {
                             actionExtras = actionExtrasMap.get(actionIndex);
                         }
@@ -339,6 +342,7 @@
         }
     }
 
+    @SuppressWarnings("LiteralClassName")
     private static boolean ensureActionReflectionReadyLocked() {
         if (sActionsAccessFailed) {
             return false;
@@ -392,7 +396,11 @@
                 bundle.getBundle(KEY_EXTRAS),
                 RemoteInputCompatJellybean.fromBundleArray(
                         BundleUtil.getBundleArrayFromBundle(bundle, KEY_REMOTE_INPUTS),
-                        remoteInputFactory), allowGeneratedReplies);
+                        remoteInputFactory),
+                RemoteInputCompatJellybean.fromBundleArray(
+                        BundleUtil.getBundleArrayFromBundle(bundle, KEY_DATA_ONLY_REMOTE_INPUTS),
+                        remoteInputFactory),
+                allowGeneratedReplies);
     }
 
     public static ArrayList<Parcelable> getParcelableArrayListForActions(
@@ -425,20 +433,4 @@
                 action.getRemoteInputs()));
         return bundle;
     }
-
-    public static boolean getLocalOnly(Notification notif) {
-        return getExtras(notif).getBoolean(EXTRA_LOCAL_ONLY);
-    }
-
-    public static String getGroup(Notification n) {
-        return getExtras(n).getString(EXTRA_GROUP_KEY);
-    }
-
-    public static boolean isGroupSummary(Notification n) {
-        return getExtras(n).getBoolean(EXTRA_GROUP_SUMMARY);
-    }
-
-    public static String getSortKey(Notification n) {
-        return getExtras(n).getString(EXTRA_SORT_KEY);
-    }
 }
diff --git a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
index 2fa9adc..0402a91 100644
--- a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
@@ -19,32 +19,44 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
 @RequiresApi(16)
-@TargetApi(16)
 class RemoteInputCompatJellybean {
-    /** Label used to denote the clip data type used for remote input transport */
-    public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
-
-    /** Extra added to a clip data intent object to hold the results bundle. */
-    public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    /** Extra added to a clip data intent object to hold the data results bundle. */
+    private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
+            "android.remoteinput.dataTypeResultsData";
 
     private static final String KEY_RESULT_KEY = "resultKey";
     private static final String KEY_LABEL = "label";
     private static final String KEY_CHOICES = "choices";
     private static final String KEY_ALLOW_FREE_FORM_INPUT = "allowFreeFormInput";
     private static final String KEY_EXTRAS = "extras";
+    private static final String KEY_ALLOWED_DATA_TYPES = "allowedDataTypes";
 
     static RemoteInputCompatBase.RemoteInput fromBundle(Bundle data,
             RemoteInputCompatBase.RemoteInput.Factory factory) {
+        ArrayList<String> allowedDataTypesAsList = data.getStringArrayList(KEY_ALLOWED_DATA_TYPES);
+        Set<String> allowedDataTypes = new HashSet<>();
+        if (allowedDataTypesAsList != null) {
+            for (String type : allowedDataTypesAsList) {
+                allowedDataTypes.add(type);
+            }
+        }
         return factory.build(data.getString(KEY_RESULT_KEY),
                 data.getCharSequence(KEY_LABEL),
                 data.getCharSequenceArray(KEY_CHOICES),
                 data.getBoolean(KEY_ALLOW_FREE_FORM_INPUT),
-                data.getBundle(KEY_EXTRAS));
+                data.getBundle(KEY_EXTRAS),
+                allowedDataTypes);
     }
 
     static Bundle toBundle(RemoteInputCompatBase.RemoteInput remoteInput) {
@@ -54,6 +66,15 @@
         data.putCharSequenceArray(KEY_CHOICES, remoteInput.getChoices());
         data.putBoolean(KEY_ALLOW_FREE_FORM_INPUT, remoteInput.getAllowFreeFormInput());
         data.putBundle(KEY_EXTRAS, remoteInput.getExtras());
+
+        Set<String> allowedDataTypes = remoteInput.getAllowedDataTypes();
+        if (allowedDataTypes != null && !allowedDataTypes.isEmpty()) {
+            ArrayList<String> allowedDataTypesAsList = new ArrayList<>(allowedDataTypes.size());
+            for (String type : allowedDataTypes) {
+                allowedDataTypesAsList.add(type);
+            }
+            data.putStringArrayList(KEY_ALLOWED_DATA_TYPES, allowedDataTypesAsList);
+        }
         return data;
     }
 
@@ -81,6 +102,92 @@
     }
 
     static Bundle getResultsFromIntent(Intent intent) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            return null;
+        }
+        return clipDataIntent.getExtras().getParcelable(RemoteInput.EXTRA_RESULTS_DATA);
+    }
+
+    static Map<String, Uri> getDataResultsFromIntent(Intent intent, String remoteInputResultKey) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            return null;
+        }
+        Map<String, Uri> results = new HashMap<>();
+        Bundle extras = clipDataIntent.getExtras();
+        for (String key : extras.keySet()) {
+            if (key.startsWith(EXTRA_DATA_TYPE_RESULTS_DATA)) {
+                String mimeType = key.substring(EXTRA_DATA_TYPE_RESULTS_DATA.length());
+                if (mimeType == null || mimeType.isEmpty()) {
+                    continue;
+                }
+                Bundle bundle = clipDataIntent.getBundleExtra(key);
+                String uriStr = bundle.getString(remoteInputResultKey);
+                if (uriStr == null || uriStr.isEmpty()) {
+                    continue;
+                }
+                results.put(mimeType, Uri.parse(uriStr));
+            }
+        }
+        return results.isEmpty() ? null : results;
+    }
+
+    static void addResultsToIntent(RemoteInputCompatBase.RemoteInput[] remoteInputs, Intent intent,
+            Bundle results) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        Bundle resultsBundle = clipDataIntent.getBundleExtra(RemoteInput.EXTRA_RESULTS_DATA);
+        if (resultsBundle == null) {
+            resultsBundle = new Bundle();
+        }
+        for (RemoteInputCompatBase.RemoteInput remoteInput : remoteInputs) {
+            Object result = results.get(remoteInput.getResultKey());
+            if (result instanceof CharSequence) {
+                resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
+            }
+        }
+        clipDataIntent.putExtra(RemoteInput.EXTRA_RESULTS_DATA, resultsBundle);
+        intent.setClipData(ClipData.newIntent(RemoteInput.RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    /**
+     * Same as {@link #addResultsToIntent} but for setting data results.
+     * @param remoteInput The remote input for which results are being provided
+     * @param intent The intent to add remote input results to. The {@link ClipData}
+     *               field of the intent will be modified to contain the results.
+     * @param results A map of mime type to the Uri result for that mime type.
+     */
+    public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+            Map<String, Uri> results) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        for (Map.Entry<String, Uri> entry : results.entrySet()) {
+            String mimeType = entry.getKey();
+            Uri uri = entry.getValue();
+            if (mimeType == null) {
+                continue;
+            }
+            Bundle resultsBundle =
+                    clipDataIntent.getBundleExtra(getExtraResultsKeyForData(mimeType));
+            if (resultsBundle == null) {
+                resultsBundle = new Bundle();
+            }
+            resultsBundle.putString(remoteInput.getResultKey(), uri.toString());
+            clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle);
+        }
+        intent.setClipData(ClipData.newIntent(RemoteInput.RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    private static String getExtraResultsKeyForData(String mimeType) {
+        return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
+    }
+
+    private static Intent getClipDataIntentFromIntent(Intent intent) {
         ClipData clipData = intent.getClipData();
         if (clipData == null) {
             return null;
@@ -89,23 +196,9 @@
         if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
             return null;
         }
-        if (clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
-            return clipData.getItemAt(0).getIntent().getExtras().getParcelable(EXTRA_RESULTS_DATA);
+        if (!clipDescription.getLabel().equals(RemoteInput.RESULTS_CLIP_LABEL)) {
+            return null;
         }
-        return null;
-    }
-
-    static void addResultsToIntent(RemoteInputCompatBase.RemoteInput[] remoteInputs, Intent intent,
-            Bundle results) {
-        Bundle resultsBundle = new Bundle();
-        for (RemoteInputCompatBase.RemoteInput remoteInput : remoteInputs) {
-            Object result = results.get(remoteInput.getResultKey());
-            if (result instanceof CharSequence) {
-                resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
-            }
-        }
-        Intent clipIntent = new Intent();
-        clipIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
-        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipIntent));
+        return clipData.getItemAt(0).getIntent();
     }
 }
diff --git a/compat/jellybean/android/support/v4/app/ShareCompatJB.java b/compat/jellybean/android/support/v4/app/ShareCompatJB.java
deleted file mode 100644
index 58eaa23..0000000
--- a/compat/jellybean/android/support/v4/app/ShareCompatJB.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.text.Html;
-
-@RequiresApi(16)
-@TargetApi(16)
-class ShareCompatJB {
-    public static String escapeHtml(CharSequence html) {
-        return Html.escapeHtml(html);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java b/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
deleted file mode 100644
index ea4d610..0000000
--- a/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2013 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.content;
-
-import android.content.ContentResolver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.OperationCanceledException;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(16)
-@TargetApi(16)
-class ContentResolverCompatJellybean {
-
-    public static Cursor query(ContentResolver resolver, Uri uri, String[] projection,
-            String selection, String[] selectionArgs, String sortOrder,
-            Object cancellationSignalObj) {
-        return resolver.query(uri, projection, selection, selectionArgs, sortOrder,
-                (android.os.CancellationSignal)cancellationSignalObj);
-    }
-
-    static boolean isFrameworkOperationCanceledException(Exception e) {
-        return e instanceof OperationCanceledException;
-    }
-}
diff --git a/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java b/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
deleted file mode 100644
index c00a971..0000000
--- a/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2012 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.content;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(16)
-@TargetApi(16)
-class ContextCompatJellybean {
-
-    public static void startActivities(Context context, Intent[] intents, Bundle options) {
-        context.startActivities(intents, options);
-    }
-
-    public static void startActivity(Context context, Intent intent, Bundle options) {
-        context.startActivity(intent, options);
-    }
-
-}
diff --git a/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java b/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
deleted file mode 100644
index 64272b8..0000000
--- a/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import android.net.ConnectivityManager;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of ConnectivityManagerCompat that can use Jellybean APIs.
- */
-
-@RequiresApi(16)
-@TargetApi(16)
-class ConnectivityManagerCompatJellyBean {
-    public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
-        return cm.isActiveNetworkMetered();
-    }
-}
diff --git a/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java b/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
deleted file mode 100644
index 127fdbf..0000000
--- a/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2013 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.os;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(16)
-@TargetApi(16)
-class CancellationSignalCompatJellybean {
-    public static Object create() {
-        return new android.os.CancellationSignal();
-    }
-
-    public static void cancel(Object cancellationSignalObj) {
-        ((android.os.CancellationSignal)cancellationSignalObj).cancel();
-    }
-}
diff --git a/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java b/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
deleted file mode 100644
index e588892..0000000
--- a/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2012 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.view;
-
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-
-/**
- * JellyBean specific AccessibilityDelegate API implementation.
- */
-
-@RequiresApi(16)
-@TargetApi(16)
-class AccessibilityDelegateCompatJellyBean {
-
-    public interface AccessibilityDelegateBridgeJellyBean {
-        public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event);
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event);
-        public void onInitializeAccessibilityNodeInfo(View host, Object info);
-        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event);
-        public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-            AccessibilityEvent event);
-        public void sendAccessibilityEvent(View host, int eventType);
-        public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event);
-        public Object getAccessibilityNodeProvider(View host);
-        public boolean performAccessibilityAction(View host, int action, Bundle args);
-    }
-
-    public static Object newAccessibilityDelegateBridge(
-            final AccessibilityDelegateBridgeJellyBean bridge) {
-        return new AccessibilityDelegate() {
-
-            @Override
-            public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-                return bridge.dispatchPopulateAccessibilityEvent(host, event);
-            }
-
-            @Override
-            public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-                bridge.onInitializeAccessibilityEvent(host, event);
-            }
-
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                bridge.onInitializeAccessibilityNodeInfo(host, info);
-            }
-
-            @Override
-            public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-                bridge.onPopulateAccessibilityEvent(host, event);
-            }
-
-            @Override
-            public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-                    AccessibilityEvent event) {
-                return bridge.onRequestSendAccessibilityEvent(host, child, event);
-            }
-
-            @Override
-            public void sendAccessibilityEvent(View host, int eventType) {
-                bridge.sendAccessibilityEvent(host, eventType);
-            }
-
-            @Override
-            public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-                bridge.sendAccessibilityEventUnchecked(host, event);
-            }
-
-            @Override
-            public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
-                return (AccessibilityNodeProvider) bridge.getAccessibilityNodeProvider(host);
-            }
-
-            @Override
-            public boolean performAccessibilityAction(View host, int action, Bundle args) {
-                return bridge.performAccessibilityAction(host, action, args);
-            }
-        };
-    }
-
-    public static Object getAccessibilityNodeProvider(Object delegate,
-            View host) {
-        return ((AccessibilityDelegate) delegate).getAccessibilityNodeProvider(host);
-    }
-
-    public static boolean performAccessibilityAction(Object delegate, View host, int action,
-            Bundle args) {
-        return ((AccessibilityDelegate) delegate).performAccessibilityAction(host, action, args);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/view/ViewCompatJB.java b/compat/jellybean/android/support/v4/view/ViewCompatJB.java
deleted file mode 100644
index ccf34ba..0000000
--- a/compat/jellybean/android/support/v4/view/ViewCompatJB.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2012 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.view;
-
-import android.annotation.TargetApi;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewParent;
-
-/**
- * Jellybean-specific View API access
- */
-@RequiresApi(16)
-@TargetApi(16)
-class ViewCompatJB {
-
-    public static boolean hasTransientState(View view) {
-        return view.hasTransientState();
-    }
-
-    public static void setHasTransientState(View view, boolean hasTransientState) {
-        view.setHasTransientState(hasTransientState);
-    }
-
-    public static void postInvalidateOnAnimation(View view) {
-        view.postInvalidateOnAnimation();
-    }
-
-    public static void postInvalidateOnAnimation(View view, int left, int top,
-            int right, int bottom) {
-        view.postInvalidate(left, top, right, bottom);
-    }
-
-    public static void postOnAnimation(View view, Runnable action) {
-        view.postOnAnimation(action);
-    }
-
-    public static void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
-        view.postOnAnimationDelayed(action, delayMillis);
-    }
-
-    public static int getImportantForAccessibility(View view) {
-        return view.getImportantForAccessibility();
-    }
-
-    public static void setImportantForAccessibility(View view, int mode) {
-        view.setImportantForAccessibility(mode);
-    }
-
-    public static boolean performAccessibilityAction(View view, int action, Bundle arguments) {
-        return view.performAccessibilityAction(action, arguments);
-    }
-
-    public static Object getAccessibilityNodeProvider(View view) {
-        return view.getAccessibilityNodeProvider();
-    }
-
-    public static ViewParent getParentForAccessibility(View view) {
-        return view.getParentForAccessibility();
-    }
-
-    public static int getMinimumWidth(View view) {
-        return view.getMinimumWidth();
-    }
-
-    public static int getMinimumHeight(View view) {
-        return view.getMinimumHeight();
-    }
-
-    public static void requestApplyInsets(View view) {
-        view.requestFitSystemWindows();
-    }
-
-    public static boolean getFitsSystemWindows(View view) {
-        return view.getFitsSystemWindows();
-    }
-
-    public static boolean hasOverlappingRendering(View view) {
-        return view.hasOverlappingRendering();
-    }
-
-    public static void setBackground(View view, Drawable drawable) {
-        view.setBackground(drawable);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java b/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
deleted file mode 100644
index 8e327fe..0000000
--- a/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.view;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-@RequiresApi(16)
-@TargetApi(16)
-class ViewPropertyAnimatorCompatJB {
-
-    public static void withStartAction(View view, Runnable runnable) {
-        view.animate().withStartAction(runnable);
-    }
-
-    public static void withEndAction(View view, Runnable runnable) {
-        view.animate().withEndAction(runnable);
-    }
-
-    public static void withLayer(View view) {
-        view.animate().withLayer();
-    }
-
-    public static void setListener(final View view,
-            final ViewPropertyAnimatorListener listener) {
-        if (listener != null) {
-            view.animate().setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    listener.onAnimationCancel(view);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    listener.onAnimationEnd(view);
-                }
-
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    listener.onAnimationStart(view);
-                }
-            });
-        } else {
-            view.animate().setListener(null);
-        }
-    }
-}
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
deleted file mode 100644
index 9c9ef09..0000000
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityEvent;
-
-@RequiresApi(16)
-@TargetApi(16)
-class AccessibilityEventCompatJellyBean {
-    public static void setMovementGranularity(AccessibilityEvent event, int granularity) {
-        event.setMovementGranularity(granularity);
-    }
-
-    public static int getMovementGranularity(AccessibilityEvent event) {
-        return event.getMovementGranularity();
-    }
-
-    public static void setAction(AccessibilityEvent event, int action) {
-        event.setAction(action);
-    }
-
-    public static int getAction(AccessibilityEvent event) {
-        return event.getAction();
-    }
-}
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
deleted file mode 100644
index a095b10..0000000
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2012 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.view.accessibility;
-
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-/**
- * JellyBean specific AccessibilityNodeInfo API implementation.
- */
-
-@RequiresApi(16)
-@TargetApi(16)
-class AccessibilityNodeInfoCompatJellyBean {
-
-    public static void addChild(Object info, View child, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).addChild(child, virtualDescendantId);
-    }
-
-    public static void setSource(Object info, View root, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).setSource(root, virtualDescendantId);
-    }
-
-    public static boolean isVisibleToUser(Object info) {
-        return ((AccessibilityNodeInfo) info).isVisibleToUser();
-    }
-
-    public static void setVisibleToUser(Object info, boolean visibleToUser) {
-        ((AccessibilityNodeInfo) info).setVisibleToUser(visibleToUser);
-    }
-
-    public static boolean performAction(Object info, int action, Bundle arguments) {
-        return ((AccessibilityNodeInfo) info).performAction(action, arguments);
-    }
-
-    public static void setMovementGranularities(Object info, int granularities) {
-        ((AccessibilityNodeInfo) info).setMovementGranularities(granularities);
-    }
-
-    public static int getMovementGranularities(Object info) {
-        return ((AccessibilityNodeInfo) info).getMovementGranularities();
-    }
-
-    public static Object obtain(View root, int virtualDescendantId) {
-        return AccessibilityNodeInfo.obtain(root, virtualDescendantId);
-    }
-
-    public static Object findFocus(Object info, int focus) {
-        return ((AccessibilityNodeInfo) info).findFocus(focus);
-    }
-
-    public static Object focusSearch(Object info, int direction) {
-        return ((AccessibilityNodeInfo) info).focusSearch(direction);
-    }
-
-    public static void setParent(Object info, View root, int virtualDescendantId) {
-        ((AccessibilityNodeInfo) info).setParent(root, virtualDescendantId);
-    }
-
-    public static boolean isAccessibilityFocused(Object info) {
-        return ((AccessibilityNodeInfo) info).isAccessibilityFocused();
-    }
-
-    public static void setAccesibilityFocused(Object info, boolean focused) {
-        ((AccessibilityNodeInfo) info).setAccessibilityFocused(focused);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
index 195e2f3..3fd5f14 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
@@ -18,7 +18,6 @@
 
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
@@ -29,7 +28,6 @@
  */
 
 @RequiresApi(16)
-@TargetApi(16)
 class AccessibilityNodeProviderCompatJellyBean {
     interface AccessibilityNodeInfoBridge {
         public Object createAccessibilityNodeInfo(int virtualViewId);
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
deleted file mode 100644
index e5a51b7..0000000
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2012 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.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.accessibility.AccessibilityRecord;
-
-/**
- * JellyBean specific AccessibilityRecord API implementation.
- */
-
-@RequiresApi(16)
-@TargetApi(16)
-class AccessibilityRecordCompatJellyBean {
-
-    public static void setSource(Object record, View root, int virtualDescendantId) {
-        ((AccessibilityRecord) record).setSource(root, virtualDescendantId);
-    }
-}
diff --git a/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java b/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
deleted file mode 100644
index 4fd4c4e..0000000
--- a/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.widget.TextView;
-
-@RequiresApi(16)
-@TargetApi(16)
-class TextViewCompatJb {
-    static int getMaxLines(TextView textView) {
-        return textView.getMaxLines();
-    }
-
-    static int getMinLines(TextView textView) {
-        return textView.getMinLines();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java b/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
deleted file mode 100644
index f14b553..0000000
--- a/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.app.ActivityManager;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class ActivityManagerCompatKitKat {
-    public static boolean isLowRamDevice(ActivityManager am) {
-        return am.isLowRamDevice();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
index 5b11daf..f40fd1b 100644
--- a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
@@ -22,7 +22,6 @@
 import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.util.SparseArray;
 import android.widget.RemoteViews;
 
@@ -30,7 +29,6 @@
 import java.util.List;
 
 @RequiresApi(19)
-@TargetApi(19)
 class NotificationCompatKitKat {
     public static class Builder implements NotificationBuilderWithBuilderAccessor,
             NotificationBuilderWithActions {
@@ -83,18 +81,18 @@
                         people.toArray(new String[people.size()]));
             }
             if (localOnly) {
-                mExtras.putBoolean(NotificationCompatJellybean.EXTRA_LOCAL_ONLY, true);
+                mExtras.putBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY, true);
             }
             if (groupKey != null) {
-                mExtras.putString(NotificationCompatJellybean.EXTRA_GROUP_KEY, groupKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_GROUP_KEY, groupKey);
                 if (groupSummary) {
-                    mExtras.putBoolean(NotificationCompatJellybean.EXTRA_GROUP_SUMMARY, true);
+                    mExtras.putBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY, true);
                 } else {
-                    mExtras.putBoolean(NotificationCompatJellybean.EXTRA_USE_SIDE_CHANNEL, true);
+                    mExtras.putBoolean(NotificationManagerCompat.EXTRA_USE_SIDE_CHANNEL, true);
                 }
             }
             if (sortKey != null) {
-                mExtras.putString(NotificationCompatJellybean.EXTRA_SORT_KEY, sortKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_SORT_KEY, sortKey);
             }
             mContentView = contentView;
             mBigContentView = bigContentView;
@@ -117,7 +115,7 @@
             if (actionExtrasMap != null) {
                 // Add the action extras sparse array if any action was added with extras.
                 mExtras.putSparseParcelableArray(
-                        NotificationCompatJellybean.EXTRA_ACTION_EXTRAS, actionExtrasMap);
+                        NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
             }
             b.setExtras(mExtras);
             Notification notification = b.build();
@@ -131,41 +129,17 @@
         }
     }
 
-    public static Bundle getExtras(Notification notif) {
-        return notif.extras;
-    }
-
-    public static int getActionCount(Notification notif) {
-        return notif.actions != null ? notif.actions.length : 0;
-    }
-
     public static NotificationCompatBase.Action getAction(Notification notif,
             int actionIndex, NotificationCompatBase.Action.Factory factory,
             RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
         Notification.Action action = notif.actions[actionIndex];
         Bundle actionExtras = null;
         SparseArray<Bundle> actionExtrasMap = notif.extras.getSparseParcelableArray(
-                NotificationCompatJellybean.EXTRA_ACTION_EXTRAS);
+                NotificationCompatExtras.EXTRA_ACTION_EXTRAS);
         if (actionExtrasMap != null) {
             actionExtras = actionExtrasMap.get(actionIndex);
         }
         return NotificationCompatJellybean.readAction(factory, remoteInputFactory,
                 action.icon, action.title, action.actionIntent, actionExtras);
     }
-
-    public static boolean getLocalOnly(Notification notif) {
-        return notif.extras.getBoolean(NotificationCompatJellybean.EXTRA_LOCAL_ONLY);
-    }
-
-    public static String getGroup(Notification notif) {
-        return notif.extras.getString(NotificationCompatJellybean.EXTRA_GROUP_KEY);
-    }
-
-    public static boolean isGroupSummary(Notification notif) {
-        return notif.extras.getBoolean(NotificationCompatJellybean.EXTRA_GROUP_SUMMARY);
-    }
-
-    public static String getSortKey(Notification notif) {
-        return notif.extras.getString(NotificationCompatJellybean.EXTRA_SORT_KEY);
-    }
 }
diff --git a/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
deleted file mode 100644
index 24bacba..0000000
--- a/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.app;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-@RequiresApi(19)
-@TargetApi(19)
-class NotificationManagerCompatKitKat {
-    private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
-    private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
-
-    public static boolean areNotificationsEnabled(Context context) {
-        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        ApplicationInfo appInfo = context.getApplicationInfo();
-        String pkg = context.getApplicationContext().getPackageName();
-        int uid = appInfo.uid;
-        try {
-            Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
-            Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE,
-                    Integer.TYPE, String.class);
-            Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
-            int value = (int) opPostNotificationValue.get(Integer.class);
-            return ((int) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg)
-                    == AppOpsManager.MODE_ALLOWED);
-        } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException |
-                InvocationTargetException | IllegalAccessException | RuntimeException e) {
-            return true;
-        }
-    }
-}
diff --git a/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java b/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
deleted file mode 100644
index 6c1bc91..0000000
--- a/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 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.content;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.io.File;
-
-@RequiresApi(19)
-@TargetApi(19)
-class ContextCompatKitKat {
-    public static File[] getExternalCacheDirs(Context context) {
-        return context.getExternalCacheDirs();
-    }
-
-    public static File[] getExternalFilesDirs(Context context, String type) {
-        return context.getExternalFilesDirs(type);
-    }
-
-    public static File[] getObbDirs(Context context) {
-        return context.getObbDirs();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java b/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
deleted file mode 100644
index ba05a32..0000000
--- a/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.graphics;
-
-import android.graphics.Bitmap;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of BitmapCompat that can use KitKat APIs.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class BitmapCompatKitKat {
-
-    static int getAllocationByteCount(Bitmap bitmap) {
-        return bitmap.getAllocationByteCount();
-    }
-
-}
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
deleted file mode 100644
index b63ea3f..0000000
--- a/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2013 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.graphics.drawable;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-/**
- * Implementation of drawable compatibility that can call KitKat APIs.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class DrawableCompatKitKat {
-    public static void setAutoMirrored(Drawable drawable, boolean mirrored) {
-        drawable.setAutoMirrored(mirrored);
-    }
-
-    public static boolean isAutoMirrored(Drawable drawable) {
-        return drawable.isAutoMirrored();
-    }
-
-    public static Drawable wrapForTinting(Drawable drawable) {
-        if (!(drawable instanceof TintAwareDrawable)) {
-            return new DrawableWrapperKitKat(drawable);
-        }
-        return drawable;
-    }
-
-    public static int getAlpha(Drawable drawable) {
-        return drawable.getAlpha();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperApi19.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperApi19.java
new file mode 100644
index 0000000..7707591
--- /dev/null
+++ b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperApi19.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics.drawable;
+
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+class DrawableWrapperApi19 extends DrawableWrapperApi14 {
+
+    DrawableWrapperApi19(Drawable drawable) {
+        super(drawable);
+    }
+
+    DrawableWrapperApi19(DrawableWrapperState state, Resources resources) {
+        super(state, resources);
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        mDrawable.setAutoMirrored(mirrored);
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mDrawable.isAutoMirrored();
+    }
+
+    @NonNull
+    @Override
+    DrawableWrapperState mutateConstantState() {
+        return new DrawableWrapperStateKitKat(mState, null);
+    }
+
+    private static class DrawableWrapperStateKitKat extends DrawableWrapperState {
+        DrawableWrapperStateKitKat(@Nullable DrawableWrapperState orig,
+                @Nullable Resources res) {
+            super(orig, res);
+        }
+
+        @Override
+        public Drawable newDrawable(@Nullable Resources res) {
+            return new DrawableWrapperApi19(this, res);
+        }
+    }
+}
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
deleted file mode 100644
index b758563..0000000
--- a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.graphics.drawable;
-
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class DrawableWrapperKitKat extends DrawableWrapperHoneycomb {
-
-    DrawableWrapperKitKat(Drawable drawable) {
-        super(drawable);
-    }
-
-    DrawableWrapperKitKat(DrawableWrapperState state, Resources resources) {
-        super(state, resources);
-    }
-
-    @Override
-    public void setAutoMirrored(boolean mirrored) {
-        mDrawable.setAutoMirrored(mirrored);
-    }
-
-    @Override
-    public boolean isAutoMirrored() {
-        return mDrawable.isAutoMirrored();
-    }
-
-    @NonNull
-    @Override
-    DrawableWrapperState mutateConstantState() {
-        return new DrawableWrapperStateKitKat(mState, null);
-    }
-
-    private static class DrawableWrapperStateKitKat extends DrawableWrapperState {
-        DrawableWrapperStateKitKat(@Nullable DrawableWrapperState orig,
-                @Nullable Resources res) {
-            super(orig, res);
-        }
-
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new DrawableWrapperKitKat(this, res);
-        }
-    }
-}
diff --git a/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java b/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
deleted file mode 100644
index b835950..0000000
--- a/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2013 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.os;
-
-import android.os.Environment;
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-
-import java.io.File;
-
-@RequiresApi(19)
-@TargetApi(19)
-class EnvironmentCompatKitKat {
-    public static String getStorageState(File path) {
-        return Environment.getStorageState(path);
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java b/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
deleted file mode 100644
index 7e873e4..0000000
--- a/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.ScaleGestureDetector;
-
-/**
- * Implementation of ScaleGestureDetector compatibility that can call KitKat APIs. This class is an
- * implementation detail for ScaleGestureDetectorCompat and should not be used directly.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class ScaleGestureDetectorCompatKitKat {
-
-    private ScaleGestureDetectorCompatKitKat() {
-    }
-
-    public static void setQuickScaleEnabled(Object scaleGestureDetector, boolean enabled) {
-        ((ScaleGestureDetector) scaleGestureDetector).setQuickScaleEnabled(enabled);
-    }
-
-    public static boolean isQuickScaleEnabled(Object scaleGestureDetector) {
-        return ((ScaleGestureDetector) scaleGestureDetector).isQuickScaleEnabled();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
deleted file mode 100644
index d864e7b..0000000
--- a/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-
-/**
- * KitKat-specific View API implementation.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class ViewCompatKitKat {
-    public static int getAccessibilityLiveRegion(View view) {
-        return view.getAccessibilityLiveRegion();
-    }
-
-    public static void setAccessibilityLiveRegion(View view, int mode) {
-        view.setAccessibilityLiveRegion(mode);
-    }
-
-    public static boolean isLaidOut(View view) {
-        return view.isLaidOut();
-    }
-
-    public static boolean isAttachedToWindow(View view) {
-        return view.isAttachedToWindow();
-    }
-
-    public static boolean isLayoutDirectionResolved(View view) {
-        return view.isLayoutDirectionResolved();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
deleted file mode 100644
index 5a00d0c..0000000
--- a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.ViewParent;
-
-@RequiresApi(19)
-@TargetApi(19)
-class ViewParentCompatKitKat {
-    public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-            View source, int changeType) {
-        parent.notifySubtreeAccessibilityStateChanged(child, source, changeType);
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java b/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
deleted file mode 100644
index f507a89..0000000
--- a/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.view;
-
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-@RequiresApi(19)
-@TargetApi(19)
-class ViewPropertyAnimatorCompatKK {
-
-    public static void setUpdateListener(final View view,
-            final ViewPropertyAnimatorUpdateListener listener) {
-        ValueAnimator.AnimatorUpdateListener wrapped = null;
-        if (listener != null) {
-            wrapped = new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                    listener.onAnimationUpdate(view);
-                }
-            };
-        }
-        view.animate().setUpdateListener(wrapped);
-    }
-
-}
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
deleted file mode 100644
index 55e2faa..0000000
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityEvent;
-
-@RequiresApi(19)
-@TargetApi(19)
-class AccessibilityEventCompatKitKat {
-    public static  void setContentChangeTypes(AccessibilityEvent event, int changeTypes) {
-        event.setContentChangeTypes(changeTypes);
-    }
-
-    public static int getContentChangeTypes(AccessibilityEvent event) {
-        return event.getContentChangeTypes();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
deleted file mode 100644
index cc94249..0000000
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view.accessibility;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
-
-/**
- * KitKat-specific AccessibilityManager API implementation.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class AccessibilityManagerCompatKitKat {
-
-    public static class TouchExplorationStateChangeListenerWrapper
-            implements TouchExplorationStateChangeListener {
-        final Object mListener;
-        final TouchExplorationStateChangeListenerBridge mListenerBridge;
-
-        public TouchExplorationStateChangeListenerWrapper(Object listener,
-                TouchExplorationStateChangeListenerBridge listenerBridge) {
-            mListener = listener;
-            mListenerBridge = listenerBridge;
-        }
-
-        @Override
-        public int hashCode() {
-            return mListener == null ? 0 : mListener.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            TouchExplorationStateChangeListenerWrapper other =
-                    (TouchExplorationStateChangeListenerWrapper) o;
-            return mListener == null ? other.mListener == null : mListener.equals(other.mListener);
-        }
-
-        @Override
-        public void onTouchExplorationStateChanged(boolean enabled) {
-            mListenerBridge.onTouchExplorationStateChanged(enabled);
-        }
-    }
-
-    interface TouchExplorationStateChangeListenerBridge {
-        void onTouchExplorationStateChanged(boolean enabled);
-    }
-
-    public static Object newTouchExplorationStateChangeListener(
-            final TouchExplorationStateChangeListenerBridge bridge) {
-        return new TouchExplorationStateChangeListener() {
-            @Override
-            public void onTouchExplorationStateChanged(boolean enabled) {
-                bridge.onTouchExplorationStateChanged(enabled);
-            }
-        };
-    }
-
-    public static boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,
-            Object listener) {
-        return manager.addTouchExplorationStateChangeListener(
-                (TouchExplorationStateChangeListener) listener);
-    }
-
-    public static boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,
-            Object listener) {
-        return manager.removeTouchExplorationStateChangeListener(
-                (TouchExplorationStateChangeListener) listener);
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
index 7bb8b1e..83a0c66 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
@@ -16,9 +16,7 @@
 
 package android.support.v4.view.accessibility;
 
-import android.os.Bundle;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
@@ -26,168 +24,7 @@
  */
 
 @RequiresApi(19)
-@TargetApi(19)
 class AccessibilityNodeInfoCompatKitKat {
-    private static final byte TRAIT_UNSET = -1;
-    private static final String TRAITS_KEY =
-            "android.view.accessibility.AccessibilityNodeInfo.traits";
-    private static final long TRAIT_HAS_IMAGE = 0x00000001;
-    private static final String ROLE_DESCRIPTION_KEY =
-            "AccessibilityNodeInfo.roleDescription";
-
-    static int getLiveRegion(Object info) {
-        return ((AccessibilityNodeInfo) info).getLiveRegion();
-    }
-
-    static void setLiveRegion(Object info, int mode) {
-        ((AccessibilityNodeInfo) info).setLiveRegion(mode);
-    }
-
-    static Object getCollectionInfo(Object info) {
-        return ((AccessibilityNodeInfo) info).getCollectionInfo();
-    }
-
-    static Object getCollectionItemInfo(Object info) {
-        return ((AccessibilityNodeInfo) info).getCollectionItemInfo();
-    }
-
-    public static void setCollectionInfo(Object info, Object collectionInfo) {
-        ((AccessibilityNodeInfo) info).setCollectionInfo(
-                (AccessibilityNodeInfo.CollectionInfo)collectionInfo);
-    }
-
-    public static void setCollectionItemInfo(Object info, Object collectionItemInfo) {
-        ((AccessibilityNodeInfo) info).setCollectionItemInfo(
-                (AccessibilityNodeInfo.CollectionItemInfo) collectionItemInfo);
-    }
-
-    static Object getRangeInfo(Object info) {
-        return ((AccessibilityNodeInfo) info).getRangeInfo();
-    }
-
-    public static void setRangeInfo(Object info, Object rangeInfo) {
-        ((AccessibilityNodeInfo) info).setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo);
-    }
-
-    public static Object obtainCollectionInfo(int rowCount, int columnCount,
-            boolean hierarchical, int selectionMode) {
-        return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical);
-    }
-
-    public static Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical) {
-        return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical);
-    }
-
-    public static Object obtainCollectionItemInfo(int rowIndex, int rowSpan, int columnIndex,
-            int columnSpan, boolean heading) {
-        return AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
-                columnSpan, heading);
-    }
-
-    public static void setContentInvalid(Object info, boolean contentInvalid) {
-        ((AccessibilityNodeInfo) info).setContentInvalid(contentInvalid);
-    }
-
-    public static boolean isContentInvalid(Object info) {
-        return ((AccessibilityNodeInfo) info).isContentInvalid();
-    }
-
-    public static boolean canOpenPopup(Object info) {
-        return ((AccessibilityNodeInfo) info).canOpenPopup();
-    }
-
-    public static void setCanOpenPopup(Object info, boolean opensPopup) {
-        ((AccessibilityNodeInfo) info).setCanOpenPopup(opensPopup);
-    }
-
-    public static Bundle getExtras(Object info) {
-        return ((AccessibilityNodeInfo) info).getExtras();
-    }
-
-    private static long getTraits(Object info) {
-        return getExtras(info).getLong(TRAITS_KEY, TRAIT_UNSET);
-    }
-
-    private static void setTrait(Object info, long trait) {
-        Bundle extras = getExtras(info);
-        long traits = extras.getLong(TRAITS_KEY, 0);
-        extras.putLong(TRAITS_KEY, traits | trait);
-    }
-
-    public static int getInputType(Object info) {
-        return ((AccessibilityNodeInfo) info).getInputType();
-    }
-
-    public static void setInputType(Object info, int inputType) {
-        ((AccessibilityNodeInfo) info).setInputType(inputType);
-    }
-
-    public static boolean isDismissable(Object info) {
-        return ((AccessibilityNodeInfo) info).isDismissable();
-    }
-
-    public static void setDismissable(Object info, boolean dismissable) {
-        ((AccessibilityNodeInfo) info).setDismissable(dismissable);
-    }
-
-    public static boolean isMultiLine(Object info) {
-        return ((AccessibilityNodeInfo) info).isMultiLine();
-    }
-
-    public static void setMultiLine(Object info, boolean multiLine) {
-        ((AccessibilityNodeInfo) info).setMultiLine(multiLine);
-    }
-
-    public static CharSequence getRoleDescription(Object info) {
-        Bundle extras = getExtras(info);
-        return extras.getCharSequence(ROLE_DESCRIPTION_KEY);
-    }
-
-    public static void setRoleDescription(Object info, CharSequence roleDescription) {
-        Bundle extras = getExtras(info);
-        extras.putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
-    }
-
-    public static Object obtainRangeInfo(int type, float min, float max, float current) {
-        return AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current);
-    }
-
-    static class CollectionInfo {
-        static int getColumnCount(Object info) {
-            return ((AccessibilityNodeInfo.CollectionInfo) info).getColumnCount();
-        }
-
-        static int getRowCount(Object info) {
-            return ((AccessibilityNodeInfo.CollectionInfo) info).getRowCount();
-        }
-
-        static boolean isHierarchical(Object info) {
-            return ((AccessibilityNodeInfo.CollectionInfo) info).isHierarchical();
-        }
-    }
-
-    static class CollectionItemInfo {
-        static int getColumnIndex(Object info) {
-            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnIndex();
-        }
-
-        static int getColumnSpan(Object info) {
-            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnSpan();
-        }
-
-        static int getRowIndex(Object info) {
-            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowIndex();
-        }
-
-        static int getRowSpan(Object info) {
-            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowSpan();
-        }
-
-        static boolean isHeading(Object info) {
-            return ((AccessibilityNodeInfo.CollectionItemInfo) info).isHeading();
-        }
-    }
-
     static class RangeInfo {
         static float getCurrent(Object info) {
             return ((AccessibilityNodeInfo.RangeInfo) info).getCurrent();
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
index a2bbbdc..892c48a 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
@@ -18,7 +18,6 @@
 
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
@@ -29,7 +28,6 @@
  */
 
 @RequiresApi(19)
-@TargetApi(19)
 class AccessibilityNodeProviderCompatKitKat {
     interface AccessibilityNodeInfoBridge {
         public Object createAccessibilityNodeInfo(int virtualViewId);
diff --git a/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
deleted file mode 100644
index 9678dba..0000000
--- a/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 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.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.widget.ListPopupWindow;
-
-/**
- * Implementation of ListPopupWindow compatibility that can call KitKat APIs.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class ListPopupWindowCompatKitKat {
-    public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
-        return ((ListPopupWindow) listPopupWindow).createDragToOpenListener(src);
-    }
-}
diff --git a/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java b/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
deleted file mode 100644
index ab2ff53..0000000
--- a/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.widget.ListView;
-
-@RequiresApi(19)
-@TargetApi(19)
-class ListViewCompatKitKat {
-    static void scrollListBy(final ListView listView, int y) {
-        listView.scrollListBy(y);
-    }
-}
diff --git a/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
deleted file mode 100644
index da7bc7e..0000000
--- a/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2013 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.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View.OnTouchListener;
-import android.widget.PopupMenu;
-
-/**
- * Implementation of PopupMenu compatibility that can call KitKat APIs.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class PopupMenuCompatKitKat {
-    public static OnTouchListener getDragToOpenListener(Object popupMenu) {
-        return ((PopupMenu) popupMenu).getDragToOpenListener();
-    }
-}
diff --git a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
deleted file mode 100644
index 20b0af4..0000000
--- a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 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.widget;
-
-import android.support.annotation.RequiresApi;
-import android.annotation.TargetApi;
-import android.view.View;
-import android.widget.PopupWindow;
-
-/**
- * Implementation of PopupWindow compatibility that can call KitKat APIs.
- */
-
-@RequiresApi(19)
-@TargetApi(19)
-class PopupWindowCompatKitKat {
-    public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
-            int gravity) {
-        popup.showAsDropDown(anchor, xoff, yoff, gravity);
-    }
-}
diff --git a/compat/lint-baseline.xml b/compat/lint-baseline.xml
new file mode 100644
index 0000000..3919eba
--- /dev/null
+++ b/compat/lint-baseline.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="MissingPermission"
+        message="Missing permissions required by ConnectivityManager.getActiveNetworkInfo: android.permission.ACCESS_NETWORK_STATE"
+        errorLine1="            final NetworkInfo info = cm.getActiveNetworkInfo();"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="java/android/support/v4/net/ConnectivityManagerCompat.java"
+            line="92"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="MissingPermission"
+        message="Missing permissions required by ConnectivityManager.isActiveNetworkMetered: android.permission.ACCESS_NETWORK_STATE"
+        errorLine1="            return cm.isActiveNetworkMetered();"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="java/android/support/v4/net/ConnectivityManagerCompat.java"
+            line="127"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="MissingPermission"
+        message="Missing permissions required by ConnectivityManager.getNetworkInfo: android.permission.ACCESS_NETWORK_STATE"
+        errorLine1="            return cm.getNetworkInfo(info.getType());"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="java/android/support/v4/net/ConnectivityManagerCompat.java"
+            line="180"
+            column="20"/>
+    </issue>
+
+</issues>
diff --git a/compat/res-public/values/public_attrs.xml b/compat/res-public/values/public_attrs.xml
new file mode 100644
index 0000000..e45f8c2
--- /dev/null
+++ b/compat/res-public/values/public_attrs.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<!-- Definitions of attributes to be exposed as public -->
+<resources>
+     <public type="attr" name="fontProviderAuthority"/>
+     <public type="attr" name="fontProviderPackage"/>
+     <public type="attr" name="fontProviderQuery"/>
+     <public type="attr" name="fontProviderCerts"/>
+     <public type="attr" name="fontProviderFetchStrategy"/>
+     <public type="attr" name="fontProviderFetchTimeout"/>
+     <public type="attr" name="fontStyle"/>
+     <public type="attr" name="font"/>
+     <public type="attr" name="fontWeight"/>
+</resources>
diff --git a/compat/res-public/values/public_styles.xml b/compat/res-public/values/public_styles.xml
new file mode 100644
index 0000000..ee726c7
--- /dev/null
+++ b/compat/res-public/values/public_styles.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<!-- Definitions of styles to be exposed as public -->
+<resources>
+    <public type="style" name="TextAppearance.Compat.Notification"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Title"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Info"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Line2"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Time"/>
+</resources>
diff --git a/v7/appcompat/res/drawable-hdpi/notification_bg_low_normal.9.png b/compat/res/drawable-hdpi/notification_bg_low_normal.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-hdpi/notification_bg_low_normal.9.png
rename to compat/res/drawable-hdpi/notification_bg_low_normal.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/notification_bg_low_pressed.9.png b/compat/res/drawable-hdpi/notification_bg_low_pressed.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-hdpi/notification_bg_low_pressed.9.png
rename to compat/res/drawable-hdpi/notification_bg_low_pressed.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/notification_bg_normal.9.png b/compat/res/drawable-hdpi/notification_bg_normal.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-hdpi/notification_bg_normal.9.png
rename to compat/res/drawable-hdpi/notification_bg_normal.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/notification_bg_normal_pressed.9.png b/compat/res/drawable-hdpi/notification_bg_normal_pressed.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-hdpi/notification_bg_normal_pressed.9.png
rename to compat/res/drawable-hdpi/notification_bg_normal_pressed.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/compat/res/drawable-hdpi/notify_panel_notification_icon_bg.png
similarity index 100%
rename from v7/appcompat/res/drawable-hdpi/notify_panel_notification_icon_bg.png
rename to compat/res/drawable-hdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/notification_bg_low_normal.9.png b/compat/res/drawable-mdpi/notification_bg_low_normal.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-mdpi/notification_bg_low_normal.9.png
rename to compat/res/drawable-mdpi/notification_bg_low_normal.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/notification_bg_low_pressed.9.png b/compat/res/drawable-mdpi/notification_bg_low_pressed.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-mdpi/notification_bg_low_pressed.9.png
rename to compat/res/drawable-mdpi/notification_bg_low_pressed.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/notification_bg_normal.9.png b/compat/res/drawable-mdpi/notification_bg_normal.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-mdpi/notification_bg_normal.9.png
rename to compat/res/drawable-mdpi/notification_bg_normal.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/notification_bg_normal_pressed.9.png b/compat/res/drawable-mdpi/notification_bg_normal_pressed.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-mdpi/notification_bg_normal_pressed.9.png
rename to compat/res/drawable-mdpi/notification_bg_normal_pressed.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/compat/res/drawable-mdpi/notify_panel_notification_icon_bg.png
similarity index 100%
rename from v7/appcompat/res/drawable-mdpi/notify_panel_notification_icon_bg.png
rename to compat/res/drawable-mdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/compat/res/drawable-v21/notification_action_background.xml b/compat/res/drawable-v21/notification_action_background.xml
new file mode 100644
index 0000000..f3b47cb
--- /dev/null
+++ b/compat/res/drawable-v21/notification_action_background.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/ripple_material_light">
+    <item android:id="@android:id/mask">
+        <inset xmlns:android="http://schemas.android.com/apk/res/android"
+               android:insetLeft="@dimen/compat_button_inset_horizontal_material"
+               android:insetTop="@dimen/compat_button_inset_vertical_material"
+               android:insetRight="@dimen/compat_button_inset_horizontal_material"
+               android:insetBottom="@dimen/compat_button_inset_vertical_material">
+            <shape android:shape="rectangle">
+                <corners android:radius="@dimen/compat_control_corner_material" />
+                <solid android:color="@android:color/white" />
+                <padding android:left="@dimen/compat_button_padding_horizontal_material"
+                         android:top="@dimen/compat_button_padding_vertical_material"
+                         android:right="@dimen/compat_button_padding_horizontal_material"
+                         android:bottom="@dimen/compat_button_padding_vertical_material" />
+            </shape>
+        </inset>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable-xhdpi/notification_bg_low_normal.9.png b/compat/res/drawable-xhdpi/notification_bg_low_normal.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-xhdpi/notification_bg_low_normal.9.png
rename to compat/res/drawable-xhdpi/notification_bg_low_normal.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/notification_bg_low_pressed.9.png b/compat/res/drawable-xhdpi/notification_bg_low_pressed.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-xhdpi/notification_bg_low_pressed.9.png
rename to compat/res/drawable-xhdpi/notification_bg_low_pressed.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/notification_bg_normal.9.png b/compat/res/drawable-xhdpi/notification_bg_normal.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-xhdpi/notification_bg_normal.9.png
rename to compat/res/drawable-xhdpi/notification_bg_normal.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/notification_bg_normal_pressed.9.png b/compat/res/drawable-xhdpi/notification_bg_normal_pressed.9.png
similarity index 100%
rename from v7/appcompat/res/drawable-xhdpi/notification_bg_normal_pressed.9.png
rename to compat/res/drawable-xhdpi/notification_bg_normal_pressed.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/compat/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
similarity index 100%
rename from v7/appcompat/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
rename to compat/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/v7/appcompat/res/drawable/notification_bg.xml b/compat/res/drawable/notification_bg.xml
similarity index 100%
rename from v7/appcompat/res/drawable/notification_bg.xml
rename to compat/res/drawable/notification_bg.xml
diff --git a/v7/appcompat/res/drawable/notification_bg_low.xml b/compat/res/drawable/notification_bg_low.xml
similarity index 100%
rename from v7/appcompat/res/drawable/notification_bg_low.xml
rename to compat/res/drawable/notification_bg_low.xml
diff --git a/v7/appcompat/res/drawable/notification_icon_background.xml b/compat/res/drawable/notification_icon_background.xml
similarity index 100%
rename from v7/appcompat/res/drawable/notification_icon_background.xml
rename to compat/res/drawable/notification_icon_background.xml
diff --git a/v7/appcompat/res/drawable/notification_tile_bg.xml b/compat/res/drawable/notification_tile_bg.xml
similarity index 100%
rename from v7/appcompat/res/drawable/notification_tile_bg.xml
rename to compat/res/drawable/notification_tile_bg.xml
diff --git a/compat/res/layout-v16/notification_template_custom_big.xml b/compat/res/layout-v16/notification_template_custom_big.xml
new file mode 100644
index 0000000..d0519dc
--- /dev/null
+++ b/compat/res/layout-v16/notification_template_custom_big.xml
@@ -0,0 +1,117 @@
+<?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
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_background"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" >
+    <ImageView android:id="@+id/icon"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+        android:scaleType="center"
+    />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:orientation="vertical" >
+        <LinearLayout
+            android:id="@+id/notification_main_column_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/notification_large_icon_width"
+            android:layout_marginStart="@dimen/notification_large_icon_width"
+            android:paddingTop="@dimen/notification_main_column_padding_top"
+            android:minHeight="@dimen/notification_large_icon_height"
+            android:orientation="horizontal">
+            <FrameLayout
+                android:id="@+id/notification_main_column"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_marginLeft="@dimen/notification_content_margin_start"
+                android:layout_marginStart="@dimen/notification_content_margin_start"
+                android:layout_marginBottom="8dp"
+                android:layout_marginRight="8dp"
+                android:layout_marginEnd="8dp" />
+            <FrameLayout
+                android:id="@+id/right_side"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="8dp"
+                android:layout_marginEnd="8dp"
+                android:paddingTop="@dimen/notification_right_side_padding_top">
+                <ViewStub android:id="@+id/time"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end|top"
+                    android:visibility="gone"
+                    android:layout="@layout/notification_template_part_time" />
+                <ViewStub android:id="@+id/chronometer"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end|top"
+                    android:visibility="gone"
+                    android:layout="@layout/notification_template_part_chronometer" />
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:layout_gravity="end|bottom"
+                    android:layout_marginTop="20dp">
+                    <TextView android:id="@+id/info"
+                        android:textAppearance="@style/TextAppearance.Compat.Notification.Info"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                    />
+                    <ImageView android:id="@+id/right_icon"
+                        android:layout_width="16dp"
+                        android:layout_height="16dp"
+                        android:layout_gravity="center"
+                        android:layout_marginLeft="8dp"
+                        android:layout_marginStart="8dp"
+                        android:scaleType="centerInside"
+                        android:visibility="gone"
+                        android:alpha="0.6"
+                    />
+                </LinearLayout>
+            </FrameLayout>
+        </LinearLayout>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="1px"
+            android:id="@+id/action_divider"
+            android:visibility="gone"
+            android:layout_marginLeft="@dimen/notification_large_icon_width"
+            android:layout_marginStart="@dimen/notification_large_icon_width"
+            android:background="?android:attr/dividerHorizontal" />
+        <LinearLayout
+            android:id="@+id/actions"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:visibility="gone"
+            android:showDividers="middle"
+            android:divider="?android:attr/listDivider"
+            android:dividerPadding="12dp"
+            android:layout_marginLeft="@dimen/notification_large_icon_width"
+            android:layout_marginStart="@dimen/notification_large_icon_width" >
+            <!-- actions will be added here -->
+        </LinearLayout>
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/compat/res/layout-v21/notification_action.xml b/compat/res/layout-v21/notification_action.xml
new file mode 100644
index 0000000..7199c25
--- /dev/null
+++ b/compat/res/layout-v21/notification_action.xml
@@ -0,0 +1,41 @@
+<?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"
+    style="@style/Widget.Compat.NotificationActionContainer"
+    android:id="@+id/action_container"
+    android:layout_width="0dp"
+    android:layout_weight="1"
+    android:layout_height="48dp"
+    android:paddingStart="4dp"
+    android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/action_image"
+        android:layout_width="@dimen/notification_action_icon_size"
+        android:layout_height="@dimen/notification_action_icon_size"
+        android:layout_gravity="center|start"
+        android:scaleType="centerInside"/>
+    <TextView
+        style="@style/Widget.Compat.NotificationActionText"
+        android:id="@+id/action_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center|start"
+        android:paddingStart="4dp"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:clickable="false"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/compat/res/layout-v21/notification_action_tombstone.xml b/compat/res/layout-v21/notification_action_tombstone.xml
new file mode 100644
index 0000000..7ef38fa
--- /dev/null
+++ b/compat/res/layout-v21/notification_action_tombstone.xml
@@ -0,0 +1,48 @@
+<?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"
+    style="@style/Widget.Compat.NotificationActionContainer"
+    android:id="@+id/action_container"
+    android:layout_width="0dp"
+    android:layout_weight="1"
+    android:layout_height="48dp"
+    android:paddingStart="4dp"
+    android:orientation="horizontal"
+    android:enabled="false"
+    android:background="@null">
+    <ImageView
+        android:id="@+id/action_image"
+        android:layout_width="@dimen/notification_action_icon_size"
+        android:layout_height="@dimen/notification_action_icon_size"
+        android:layout_gravity="center|start"
+        android:scaleType="centerInside"
+        android:enabled="false"
+        android:alpha="0.5"/>
+    <TextView
+        style="@style/Widget.Compat.NotificationActionText"
+        android:id="@+id/action_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center|start"
+        android:paddingStart="4dp"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:clickable="false"
+        android:enabled="false"
+        android:alpha="0.5"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/compat/res/layout-v21/notification_template_custom_big.xml b/compat/res/layout-v21/notification_template_custom_big.xml
new file mode 100644
index 0000000..9e3666e
--- /dev/null
+++ b/compat/res/layout-v21/notification_template_custom_big.xml
@@ -0,0 +1,90 @@
+<?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
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_background"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" >
+    <include layout="@layout/notification_template_icon_group"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+    />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:layout_marginStart="@dimen/notification_large_icon_width"
+        android:orientation="vertical" >
+        <LinearLayout
+            android:id="@+id/notification_main_column_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/notification_large_icon_height"
+            android:orientation="horizontal">
+            <FrameLayout
+                android:id="@+id/notification_main_column"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_marginEnd="8dp"
+                android:layout_marginBottom="8dp"/>
+            <FrameLayout
+                android:id="@+id/right_side"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="8dp"
+                android:paddingTop="@dimen/notification_right_side_padding_top">
+                <ViewStub android:id="@+id/time"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end|top"
+                    android:visibility="gone"
+                    android:layout="@layout/notification_template_part_time" />
+                <ViewStub android:id="@+id/chronometer"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end|top"
+                    android:visibility="gone"
+                    android:layout="@layout/notification_template_part_chronometer" />
+                <TextView android:id="@+id/info"
+                    android:textAppearance="@style/TextAppearance.Compat.Notification.Info"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="20dp"
+                    android:layout_gravity="end|bottom"
+                    android:singleLine="true"
+                />
+            </FrameLayout>
+        </LinearLayout>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:id="@+id/action_divider"
+            android:visibility="gone"
+            android:background="#29000000" />
+        <LinearLayout
+            android:id="@+id/actions"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="-8dp"
+            android:orientation="horizontal"
+            android:visibility="gone"
+        >
+            <!-- actions will be added here -->
+        </LinearLayout>
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/compat/res/layout-v21/notification_template_icon_group.xml b/compat/res/layout-v21/notification_template_icon_group.xml
new file mode 100644
index 0000000..8fadd67
--- /dev/null
+++ b/compat/res/layout-v21/notification_template_icon_group.xml
@@ -0,0 +1,42 @@
+<?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.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/notification_large_icon_width"
+    android:layout_height="@dimen/notification_large_icon_height"
+    android:id="@+id/icon_group"
+>
+    <ImageView android:id="@+id/icon"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/notification_big_circle_margin"
+        android:layout_marginBottom="@dimen/notification_big_circle_margin"
+        android:layout_marginStart="@dimen/notification_big_circle_margin"
+        android:layout_marginEnd="@dimen/notification_big_circle_margin"
+        android:scaleType="centerInside"
+    />
+    <ImageView android:id="@+id/right_icon"
+        android:layout_width="@dimen/notification_right_icon_size"
+        android:layout_height="@dimen/notification_right_icon_size"
+        android:layout_gravity="end|bottom"
+        android:scaleType="centerInside"
+        android:visibility="gone"
+        android:layout_marginEnd="8dp"
+        android:layout_marginBottom="8dp"
+    />
+</FrameLayout>
diff --git a/compat/res/layout/notification_action.xml b/compat/res/layout/notification_action.xml
new file mode 100644
index 0000000..3ffd9a1
--- /dev/null
+++ b/compat/res/layout/notification_action.xml
@@ -0,0 +1,44 @@
+<?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"
+    style="@style/Widget.Compat.NotificationActionContainer"
+    android:id="@+id/action_container"
+    android:layout_width="0dp"
+    android:layout_weight="1"
+    android:layout_height="48dp"
+    android:paddingLeft="4dp"
+    android:paddingStart="4dp"
+    android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/action_image"
+        android:layout_width="@dimen/notification_action_icon_size"
+        android:layout_height="@dimen/notification_action_icon_size"
+        android:layout_gravity="center|start"
+        android:scaleType="centerInside"/>
+    <TextView
+        style="@style/Widget.Compat.NotificationActionText"
+        android:id="@+id/action_text"
+        android:textColor="#ccc"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center|start"
+        android:paddingLeft="4dp"
+        android:paddingStart="4dp"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:clickable="false"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/compat/res/layout/notification_action_tombstone.xml b/compat/res/layout/notification_action_tombstone.xml
new file mode 100644
index 0000000..e9d4e37
--- /dev/null
+++ b/compat/res/layout/notification_action_tombstone.xml
@@ -0,0 +1,51 @@
+<?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"
+    style="@style/Widget.Compat.NotificationActionContainer"
+    android:id="@+id/action_container"
+    android:layout_width="0dp"
+    android:layout_weight="1"
+    android:layout_height="48dp"
+    android:paddingLeft="4dp"
+    android:paddingStart="4dp"
+    android:orientation="horizontal"
+    android:enabled="false"
+    android:background="@null">
+    <ImageView
+        android:id="@+id/action_image"
+        android:layout_width="@dimen/notification_action_icon_size"
+        android:layout_height="@dimen/notification_action_icon_size"
+        android:layout_gravity="center|start"
+        android:scaleType="centerInside"
+        android:enabled="false"
+        android:alpha="0.5"/>
+    <TextView
+        style="@style/Widget.Compat.NotificationActionText"
+        android:id="@+id/action_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center|start"
+        android:textColor="#ccc"
+        android:paddingLeft="4dp"
+        android:paddingStart="4dp"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:clickable="false"
+        android:enabled="false"
+        android:alpha="0.5"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/compat/res/layout/notification_template_custom_big.xml b/compat/res/layout/notification_template_custom_big.xml
new file mode 100644
index 0000000..8aaf9d5
--- /dev/null
+++ b/compat/res/layout/notification_template_custom_big.xml
@@ -0,0 +1,76 @@
+<?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 android:id="@+id/notification_main_column_container"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/notification_large_icon_height"
+    android:orientation="horizontal"
+    android:paddingRight="12dp"
+    android:paddingEnd="12dp">
+    <ImageView android:id="@+id/icon"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+        android:background="@drawable/notification_tile_bg"
+        android:scaleType="center"
+    />
+    <FrameLayout
+        android:id="@+id/notification_main_column"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="12dp"
+        android:paddingStart="12dp"
+        android:paddingTop="@dimen/notification_main_column_padding_top"
+        android:layout_weight="1"/>
+    <FrameLayout
+        android:id="@+id/right_side"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="12dp"
+        android:paddingLeft="12dp">
+        <include
+            layout="@layout/notification_template_part_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end|top"
+            android:visibility="gone"/>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end|bottom"
+            android:layout_marginTop="18dp"
+            android:orientation="horizontal">
+            <TextView android:id="@+id/info"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:singleLine="true"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Info"
+            />
+            <ImageView android:id="@+id/right_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="center"
+                android:layout_marginLeft="8dp"
+                android:alpha="0.7"
+                android:scaleType="center"
+                android:visibility="gone"
+            />
+        </LinearLayout>
+    </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/compat/res/layout/notification_template_icon_group.xml b/compat/res/layout/notification_template_icon_group.xml
new file mode 100644
index 0000000..f225737
--- /dev/null
+++ b/compat/res/layout/notification_template_icon_group.xml
@@ -0,0 +1,24 @@
+<?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
+  -->
+
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/icon"
+    android:layout_width="@dimen/notification_large_icon_width"
+    android:layout_height="@dimen/notification_large_icon_height"
+    android:scaleType="centerCrop"
+/>
diff --git a/compat/res/layout/notification_template_part_chronometer.xml b/compat/res/layout/notification_template_part_chronometer.xml
new file mode 100644
index 0000000..245353b
--- /dev/null
+++ b/compat/res/layout/notification_template_part_chronometer.xml
@@ -0,0 +1,23 @@
+<?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
+  -->
+
+<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
+    android:textAppearance="@style/TextAppearance.Compat.Notification.Time"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:singleLine="true"
+    />
diff --git a/compat/res/layout/notification_template_part_time.xml b/compat/res/layout/notification_template_part_time.xml
new file mode 100644
index 0000000..9a78a93
--- /dev/null
+++ b/compat/res/layout/notification_template_part_time.xml
@@ -0,0 +1,23 @@
+<?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
+  -->
+
+<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
+    android:textAppearance="@style/TextAppearance.Compat.Notification.Time"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:singleLine="true"
+    />
diff --git a/compat/res/values-af/strings.xml b/compat/res/values-af/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-am/strings.xml b/compat/res/values-am/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ar/strings.xml b/compat/res/values-ar/strings.xml
new file mode 100644
index 0000000..d9094fb
--- /dev/null
+++ b/compat/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"+999"</string>
+</resources>
diff --git a/compat/res/values-az/strings.xml b/compat/res/values-az/strings.xml
new file mode 100644
index 0000000..f2e04fe
--- /dev/null
+++ b/compat/res/values-az/strings.xml
@@ -0,0 +1,24 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- String.format failed for translation -->
+    <!-- no translation found for abc_shareactionprovider_share_with_application (7165123711973476752) -->
+    <skip />
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-b+sr+Latn/strings.xml b/compat/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-be/strings.xml b/compat/res/values-be/strings.xml
new file mode 100644
index 0000000..ae35194
--- /dev/null
+++ b/compat/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"больш за 999"</string>
+</resources>
diff --git a/compat/res/values-bg/strings.xml b/compat/res/values-bg/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-bn/strings.xml b/compat/res/values-bn/strings.xml
new file mode 100644
index 0000000..01516f8
--- /dev/null
+++ b/compat/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"৯৯৯+"</string>
+</resources>
diff --git a/compat/res/values-bs/strings.xml b/compat/res/values-bs/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-ca/strings.xml b/compat/res/values-ca/strings.xml
new file mode 100644
index 0000000..d9094fb
--- /dev/null
+++ b/compat/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"+999"</string>
+</resources>
diff --git a/compat/res/values-cs/strings.xml b/compat/res/values-cs/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-da/strings.xml b/compat/res/values-da/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-de/strings.xml b/compat/res/values-de/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-el/strings.xml b/compat/res/values-el/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-en-rAU/strings.xml b/compat/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-en-rGB/strings.xml b/compat/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-en-rIN/strings.xml b/compat/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-es-rUS/strings.xml b/compat/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-es/strings.xml b/compat/res/values-es/strings.xml
new file mode 100644
index 0000000..d9094fb
--- /dev/null
+++ b/compat/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"+999"</string>
+</resources>
diff --git a/compat/res/values-et/strings.xml b/compat/res/values-et/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-eu/strings.xml b/compat/res/values-eu/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-fa/strings.xml b/compat/res/values-fa/strings.xml
new file mode 100644
index 0000000..0470999
--- /dev/null
+++ b/compat/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"۹۹۹+"</string>
+</resources>
diff --git a/compat/res/values-fi/strings.xml b/compat/res/values-fi/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-fr-rCA/strings.xml b/compat/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-fr/strings.xml b/compat/res/values-fr/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-gl/strings.xml b/compat/res/values-gl/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-gu/strings.xml b/compat/res/values-gu/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-hi/strings.xml b/compat/res/values-hi/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-hr/strings.xml b/compat/res/values-hr/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-hu/strings.xml b/compat/res/values-hu/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-hy/strings.xml b/compat/res/values-hy/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-in/strings.xml b/compat/res/values-in/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-is/strings.xml b/compat/res/values-is/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-it/strings.xml b/compat/res/values-it/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-iw/strings.xml b/compat/res/values-iw/strings.xml
new file mode 100644
index 0000000..6799e67
--- /dev/null
+++ b/compat/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"‎999+‎"</string>
+</resources>
diff --git a/compat/res/values-ja/strings.xml b/compat/res/values-ja/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ka/strings.xml b/compat/res/values-ka/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-kk/strings.xml b/compat/res/values-kk/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-km/strings.xml b/compat/res/values-km/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-kn/strings.xml b/compat/res/values-kn/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ko/strings.xml b/compat/res/values-ko/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ky/strings.xml b/compat/res/values-ky/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-lo/strings.xml b/compat/res/values-lo/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-lt/strings.xml b/compat/res/values-lt/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-lv/strings.xml b/compat/res/values-lv/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-mk/strings.xml b/compat/res/values-mk/strings.xml
new file mode 100644
index 0000000..8c8797d
--- /dev/null
+++ b/compat/res/values-mk/strings.xml
@@ -0,0 +1,27 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- String.format failed for translation -->
+    <!-- no translation found for abc_action_bar_home_description_format (1397052879051804371) -->
+    <skip />
+    <!-- String.format failed for translation -->
+    <!-- no translation found for abc_shareactionprovider_share_with_application (7165123711973476752) -->
+    <skip />
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ml/strings.xml b/compat/res/values-ml/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-mn/strings.xml b/compat/res/values-mn/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-mr/strings.xml b/compat/res/values-mr/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ms/strings.xml b/compat/res/values-ms/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-my/strings.xml b/compat/res/values-my/strings.xml
new file mode 100644
index 0000000..cfad64a
--- /dev/null
+++ b/compat/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"၉၉၉+"</string>
+</resources>
diff --git a/compat/res/values-nb/strings.xml b/compat/res/values-nb/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ne/strings.xml b/compat/res/values-ne/strings.xml
new file mode 100644
index 0000000..3dd1d09
--- /dev/null
+++ b/compat/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"९९९+"</string>
+</resources>
diff --git a/compat/res/values-nl/strings.xml b/compat/res/values-nl/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-pa/strings.xml b/compat/res/values-pa/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-pl/strings.xml b/compat/res/values-pl/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-port/bools.xml b/compat/res/values-port/bools.xml
new file mode 100644
index 0000000..25053be
--- /dev/null
+++ b/compat/res/values-port/bools.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<resources>
+
+    <bool name="abc_action_bar_embed_tabs">false</bool>
+
+</resources>
diff --git a/compat/res/values-pt-rBR/strings.xml b/compat/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-pt-rPT/strings.xml b/compat/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..d9094fb
--- /dev/null
+++ b/compat/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"+999"</string>
+</resources>
diff --git a/compat/res/values-pt/strings.xml b/compat/res/values-pt/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ro/strings.xml b/compat/res/values-ro/strings.xml
new file mode 100644
index 0000000..42598dc
--- /dev/null
+++ b/compat/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"˃999"</string>
+</resources>
diff --git a/compat/res/values-ru/strings.xml b/compat/res/values-ru/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-si/strings.xml b/compat/res/values-si/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-sk/strings.xml b/compat/res/values-sk/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-sl/strings.xml b/compat/res/values-sl/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-sq/strings.xml b/compat/res/values-sq/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-sr/strings.xml b/compat/res/values-sr/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-sv/strings.xml b/compat/res/values-sv/strings.xml
new file mode 100644
index 0000000..df46d35
--- /dev/null
+++ b/compat/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+</resources>
diff --git a/compat/res/values-sw/strings.xml b/compat/res/values-sw/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ta/strings.xml b/compat/res/values-ta/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-te/strings.xml b/compat/res/values-te/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-th/strings.xml b/compat/res/values-th/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-tl/strings.xml b/compat/res/values-tl/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-tr/strings.xml b/compat/res/values-tr/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-uk/strings.xml b/compat/res/values-uk/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-ur/strings.xml b/compat/res/values-ur/strings.xml
new file mode 100644
index 0000000..6799e67
--- /dev/null
+++ b/compat/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"‎999+‎"</string>
+</resources>
diff --git a/compat/res/values-uz/strings.xml b/compat/res/values-uz/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/v7/appcompat/res/values-v16/dimens.xml b/compat/res/values-v16/dimens.xml
similarity index 100%
rename from v7/appcompat/res/values-v16/dimens.xml
rename to compat/res/values-v16/dimens.xml
diff --git a/v7/appcompat/res/values-v21/colors.xml b/compat/res/values-v21/colors.xml
similarity index 100%
rename from v7/appcompat/res/values-v21/colors.xml
rename to compat/res/values-v21/colors.xml
diff --git a/compat/res/values-v21/dimens.xml b/compat/res/values-v21/dimens.xml
new file mode 100644
index 0000000..b56faf1
--- /dev/null
+++ b/compat/res/values-v21/dimens.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<resources>
+    <!-- the margin at the beginning of the notification content -->
+    <dimen name="notification_content_margin_start">0dp</dimen>
+    <!-- image margin on the large icon in the narrow media template -->
+    <dimen name="notification_media_narrow_margin">12dp</dimen>
+    <!-- the top padding of the notification content -->
+    <dimen name="notification_main_column_padding_top">0dp</dimen>
+</resources>
diff --git a/compat/res/values-v21/styles.xml b/compat/res/values-v21/styles.xml
new file mode 100644
index 0000000..fb87295
--- /dev/null
+++ b/compat/res/values-v21/styles.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ 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>
+
+    <!-- Use platform styles -->
+    <style name="TextAppearance.Compat.Notification"
+        parent="@android:style/TextAppearance.Material.Notification"/>
+
+    <style name="TextAppearance.Compat.Notification.Title"
+        parent="@android:style/TextAppearance.Material.Notification.Title"/>
+
+    <style name="TextAppearance.Compat.Notification.Info"
+        parent="@android:style/TextAppearance.Material.Notification.Info"/>
+
+    <style name="TextAppearance.Compat.Notification.Time"
+        parent="@android:style/TextAppearance.Material.Notification.Time"/>
+
+    <style name="Widget.Compat.NotificationActionText" parent="">
+        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textColor">@color/secondary_text_default_material_light</item>
+        <item name="android:textSize">@dimen/notification_action_text_size</item>
+    </style>
+
+    <style name="Widget.Compat.NotificationActionContainer" parent="">
+        <item name="android:background">@drawable/notification_action_background</item>
+    </style>
+
+</resources>
diff --git a/compat/res/values-vi/strings.xml b/compat/res/values-vi/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-zh-rCN/strings.xml b/compat/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-zh-rHK/strings.xml b/compat/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..d509360
--- /dev/null
+++ b/compat/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999 +"</string>
+</resources>
diff --git a/compat/res/values-zh-rTW/strings.xml b/compat/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values-zu/strings.xml b/compat/res/values-zu/strings.xml
new file mode 100644
index 0000000..4640923
--- /dev/null
+++ b/compat/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
+</resources>
diff --git a/compat/res/values/attrs.xml b/compat/res/values/attrs.xml
new file mode 100644
index 0000000..1833794
--- /dev/null
+++ b/compat/res/values/attrs.xml
@@ -0,0 +1,78 @@
+<?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>
+    <!-- Attributes that are read when parsing a <fontfamily> tag. -->
+    <declare-styleable name="FontFamily">
+        <!-- The authority of the Font Provider to be used for the request. -->
+        <attr name="fontProviderAuthority" format="string" />
+        <!-- The package for the Font Provider to be used for the request. This is used to verify
+        the identity of the provider. -->
+        <attr name="fontProviderPackage" format="string" />
+        <!-- The query to be sent over to the provider. Refer to your font provider's documentation
+        on the format of this string. -->
+        <attr name="fontProviderQuery" format="string" />
+        <!-- The sets of hashes for the certificates the provider should be signed with. This is
+        used to verify the identity of the provider, and is only required if the provider is not
+        part of the system image. This value may point to one list or a list of lists, where each
+        individual list represents one collection of signature hashes. Refer to your font provider's
+        documentation for these values. -->
+        <attr name="fontProviderCerts" format="reference" />
+        <!-- The strategy to be used when fetching font data from a font provider in XML layouts.
+          -->
+        <attr name="fontProviderFetchStrategy">
+            <!-- The blocking font fetch works as follows.
+              First, check the local cache, then if the requested font is not cached, request the
+              font from the provider and wait until it is finished.  You can change the length of
+              the timeout by modifying fontProviderFetchTimeout.  If the timeout happens, the
+              default typeface will be used instead. -->
+            <enum name="blocking" value="0" />
+            <!-- The async font fetch works as follows.
+              First, check the local cache, then if the requeted font is not cached, trigger a
+              request the font and continue with layout inflation. Once the font fetch succeeds, the
+              target text view will be refreshed with the downloaded font data. The
+              fontProviderFetchTimeout will be ignored if async loading is specified. -->
+            <enum name="async" value="1" />
+        </attr>
+        <!-- The length of the timeout during fetching. -->
+        <attr name="fontProviderFetchTimeout" format="integer">
+          <!-- A special value for the timeout. In this case, the blocking font fetching will not
+            timeout and wait until a reply is received from the font provider. -->
+            <enum name="forever" value="-1" />
+        </attr>
+    </declare-styleable>
+
+    <!-- Attributes that are read when parsing a <font> tag, which is a child of
+         <font-family>. This represents an actual font file and its attributes. -->
+    <declare-styleable name="FontFamilyFont">
+        <!-- The style of the given font file. This will be used when the font is being loaded into
+         the font stack and will override any style information in the font's header tables. If
+         unspecified, the value in the font's header tables will be used. -->
+        <attr name="fontStyle">
+            <enum name="normal" value="0" />
+            <enum name="italic" value="1" />
+        </attr>
+        <!-- The reference to the font file to be used. This should be a file in the res/font folder
+         and should therefore have an R reference value. E.g. @font/myfont -->
+        <attr name="font" format="reference" />
+        <!-- The weight of the given font file. This will be used when the font is being loaded into
+         the font stack and will override any weight information in the font's header tables. Must
+         be a positive number, a multiple of 100, and between 100 and 900, inclusive. The most
+         common values are 400 for regular weight and 700 for bold weight. If unspecified, the value
+         in the font's header tables will be used. -->
+        <attr name="fontWeight" format="integer" />
+    </declare-styleable>
+</resources>
diff --git a/compat/res/values/colors.xml b/compat/res/values/colors.xml
new file mode 100644
index 0000000..a3eccb7
--- /dev/null
+++ b/compat/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>
+    <drawable name="notification_template_icon_bg">#3333B5E5</drawable>
+    <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
+    <color name="notification_action_color_filter">#ffffffff</color>
+    <color name="notification_icon_bg_color">#ff9e9e9e</color>
+</resources>
diff --git a/compat/res/values/colors_material.xml b/compat/res/values/colors_material.xml
new file mode 100644
index 0000000..c6cffdc
--- /dev/null
+++ b/compat/res/values/colors_material.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<!-- Colors specific to Material themes. -->
+<resources>
+    <!-- 12% black (foreground) -->
+    <color name="ripple_material_light">#1f000000</color>
+    <!-- 54% black -->
+    <color name="secondary_text_default_material_light">#8a000000</color>
+</resources>
diff --git a/compat/res/values/config.xml b/compat/res/values/config.xml
new file mode 100644
index 0000000..42633cc
--- /dev/null
+++ b/compat/res/values/config.xml
@@ -0,0 +1,26 @@
+<?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.
+  -->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <!-- Maximum numerical value that will be shown in a status bar
+         notification icon or in the notification itself. Will be replaced
+         with @string/status_bar_notification_info_overflow when shown in the
+         UI. -->
+    <integer name="status_bar_notification_info_maxnum">999</integer>
+</resources>
diff --git a/compat/res/values/dimens.xml b/compat/res/values/dimens.xml
new file mode 100644
index 0000000..1dcff5e
--- /dev/null
+++ b/compat/res/values/dimens.xml
@@ -0,0 +1,73 @@
+<?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>
+    <dimen name="compat_button_inset_vertical_material">6dp</dimen>
+    <dimen name="compat_button_inset_horizontal_material">4dp</dimen>
+    <!-- Default inner padding within buttons -->
+    <dimen name="compat_button_padding_vertical_material">4dp</dimen>
+    <dimen name="compat_button_padding_horizontal_material">8dp</dimen>
+
+    <!-- Default rounded corner for controls -->
+    <dimen name="compat_control_corner_material">2dp</dimen>
+
+    <!-- The width of the big icons in notifications. -->
+    <dimen name="notification_large_icon_width">64dp</dimen>
+
+    <!-- The width of the big icons in notifications. -->
+    <dimen name="notification_large_icon_height">64dp</dimen>
+
+    <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info,
+         Time) -->
+    <dimen name="notification_subtext_size">13sp</dimen>
+
+    <!-- Size of notification action text -->
+    <dimen name="notification_action_text_size">13sp</dimen>
+
+    <!-- Top padding for notifications in the standard layout. -->
+    <dimen name="notification_top_pad">10dp</dimen>
+
+    <!-- Top padding for notification when text is large -->
+    <dimen name="notification_top_pad_large_text">5dp</dimen>
+
+    <!-- The size of the action icons -->
+    <dimen name="notification_action_icon_size">32dp</dimen>
+
+    <!-- the size of the small icon on the right of the largeIcon -->
+    <dimen name="notification_right_icon_size">16dp</dimen>
+
+    <!-- the padding of the small icon to the circle -->
+    <dimen name="notification_small_icon_background_padding">3dp</dimen>
+
+    <!-- the side margin of the big notification circle -->
+    <dimen name="notification_big_circle_margin">12dp</dimen>
+
+    <!-- small icon size when placed as large icon -->
+    <dimen name="notification_small_icon_size_as_large">24dp</dimen>
+
+    <!-- the margin at the beginning of the notification content -->
+    <dimen name="notification_content_margin_start">8dp</dimen>
+
+    <!-- image margin on the large icon in the narrow media template -->
+    <dimen name="notification_media_narrow_margin">@dimen/notification_content_margin_start</dimen>
+
+    <!-- the top padding of the notification content -->
+    <dimen name="notification_main_column_padding_top">10dp</dimen>
+
+    <!-- the paddingtop on the right side of the notification (for time etc.) -->
+    <dimen name="notification_right_side_padding_top">2dp</dimen>
+</resources>
diff --git a/compat/res/values/ids.xml b/compat/res/values/ids.xml
new file mode 100644
index 0000000..e83d5f9
--- /dev/null
+++ b/compat/res/values/ids.xml
@@ -0,0 +1,23 @@
+<?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>
+    <item type="id" name="title"/>
+    <item type="id" name="text"/>
+    <item type="id" name="text2"/>
+    <item type="id" name="line1"/>
+    <item type="id" name="line3"/>
+</resources>
diff --git a/compat/res/values/strings.xml b/compat/res/values/strings.xml
new file mode 100644
index 0000000..ceeb183
--- /dev/null
+++ b/compat/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+    <!-- Text to use when the number in a notification info is too large
+         (greater than status_bar_notification_info_maxnum, defined in
+         values/config.xml) and must be truncated. May need to be localized
+         for most appropriate textual indicator of "more than X".
+         [CHAR LIMIT=4] -->
+    <string name="status_bar_notification_info_overflow">999+</string>
+</resources>
diff --git a/compat/res/values/styles.xml b/compat/res/values/styles.xml
new file mode 100644
index 0000000..3503e77
--- /dev/null
+++ b/compat/res/values/styles.xml
@@ -0,0 +1,38 @@
+<?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="TextAppearance.Compat.Notification"
+        parent="@android:style/TextAppearance.StatusBar.EventContent"/>
+
+    <style name="TextAppearance.Compat.Notification.Title"
+        parent="@android:style/TextAppearance.StatusBar.EventContent.Title"/>
+
+    <style name="TextAppearance.Compat.Notification.Info">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+    <style name="TextAppearance.Compat.Notification.Time">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+    <style name="TextAppearance.Compat.Notification.Line2"
+         parent="TextAppearance.Compat.Notification.Info" />
+
+    <style name="Widget.Compat.NotificationActionText" parent=""/>
+    <style name="Widget.Compat.NotificationActionContainer" parent=""/>
+</resources>
diff --git a/compat/tests/AndroidManifest.xml b/compat/tests/AndroidManifest.xml
index 8b44567..4988845 100644
--- a/compat/tests/AndroidManifest.xml
+++ b/compat/tests/AndroidManifest.xml
@@ -15,35 +15,38 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.compat.test">
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.VIBRATE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
     <application
-            android:supportsRtl="true"
-            android:theme="@style/TestActivityTheme">
-        <uses-library android:name="android.test.runner" />
+        android:supportsRtl="true"
+        android:theme="@style/TestActivityTheme">
+        <activity android:name="android.support.v4.widget.ListViewTestActivity"/>
+
         <activity android:name="android.support.v4.widget.TextViewTestActivity"/>
 
         <activity android:name="android.support.v4.view.VpaActivity"/>
 
         <activity
             android:name="android.support.v4.ThemedYellowActivity"
-            android:theme="@style/YellowTheme" />
+            android:theme="@style/YellowTheme"/>
 
         <activity android:name="android.support.v4.view.ViewCompatActivity"/>
 
-        <activity android:name="android.support.v4.app.TestSupportActivity" />
+        <activity android:name="android.support.v4.app.TestSupportActivity"/>
+
+        <provider android:name="android.support.v4.provider.MockFontProvider"
+                  android:authorities="android.support.provider.fonts.font"
+                  android:exported="false"
+                  android:multiprocess="true"/>
+
+        <service android:name="android.support.v4.app.JobIntentServiceTest$TargetService"
+                 android:permission="android.permission.BIND_JOB_SERVICE"/>
+
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.support.v4.test"
-                     />
 </manifest>
diff --git a/compat/tests/assets/fonts/large_a.ttf b/compat/tests/assets/fonts/large_a.ttf
new file mode 100644
index 0000000..0e778b6
--- /dev/null
+++ b/compat/tests/assets/fonts/large_a.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_a.ttx b/compat/tests/assets/fonts/large_a.ttx
new file mode 100644
index 0000000..c453414
--- /dev/null
+++ b/compat/tests/assets/fonts/large_a.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="3em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_a.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/large_b.ttf b/compat/tests/assets/fonts/large_b.ttf
new file mode 100644
index 0000000..b8ce6ce
--- /dev/null
+++ b/compat/tests/assets/fonts/large_b.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_b.ttx b/compat/tests/assets/fonts/large_b.ttx
new file mode 100644
index 0000000..0274e9c
--- /dev/null
+++ b/compat/tests/assets/fonts/large_b.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="3em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_b.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/large_c.ttf b/compat/tests/assets/fonts/large_c.ttf
new file mode 100644
index 0000000..dd5fa50
--- /dev/null
+++ b/compat/tests/assets/fonts/large_c.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_c.ttx b/compat/tests/assets/fonts/large_c.ttx
new file mode 100644
index 0000000..d3657b1
--- /dev/null
+++ b/compat/tests/assets/fonts/large_c.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="3em" />
+      <map code="0x0064" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_c.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/large_d.ttf b/compat/tests/assets/fonts/large_d.ttf
new file mode 100644
index 0000000..b791100
--- /dev/null
+++ b/compat/tests/assets/fonts/large_d.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_d.ttx b/compat/tests/assets/fonts/large_d.ttx
new file mode 100644
index 0000000..12f668c
--- /dev/null
+++ b/compat/tests/assets/fonts/large_d.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="3em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_d.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/samplefont.ttf b/compat/tests/assets/fonts/samplefont.ttf
new file mode 100644
index 0000000..5fccad2
--- /dev/null
+++ b/compat/tests/assets/fonts/samplefont.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/samplefont.ttx b/compat/tests/assets/fonts/samplefont.ttx
new file mode 100644
index 0000000..f618123
--- /dev/null
+++ b/compat/tests/assets/fonts/samplefont.ttx
@@ -0,0 +1,180 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="a" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/fonts_readme.txt b/compat/tests/fonts_readme.txt
new file mode 100644
index 0000000..f0de576
--- /dev/null
+++ b/compat/tests/fonts_readme.txt
@@ -0,0 +1,15 @@
+All fonts included in this project follow the below copyright and licensing:
+
+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.
\ No newline at end of file
diff --git a/compat/tests/java/android/support/v4/app/JobIntentServiceTest.java b/compat/tests/java/android/support/v4/app/JobIntentServiceTest.java
new file mode 100644
index 0000000..c5fa715
--- /dev/null
+++ b/compat/tests/java/android/support/v4/app/JobIntentServiceTest.java
@@ -0,0 +1,469 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class JobIntentServiceTest {
+    static final String TAG = "JobIntentServiceTest";
+
+    static final int JOB_ID = 0x1000;
+
+    static final Object sLock = new Object();
+    static CountDownLatch sReadyToRunLatch;
+    static CountDownLatch sServiceWaitingLatch;
+    static CountDownLatch sServiceStoppedLatch;
+    static CountDownLatch sWaitCompleteLatch;
+    static CountDownLatch sServiceFinishedLatch;
+
+    static boolean sFinished;
+    static ArrayList<Intent> sFinishedWork;
+    static String sFinishedErrorMsg;
+    static String sLastServiceState;
+
+    Context mContext;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    public static final class TestIntentItem implements Parcelable {
+        public static final int FLAG_WAIT = 1 << 0;
+        public static final int FLAG_STOPPED_AFTER_WAIT = 1 << 1;
+
+        public final Intent intent;
+        public final TestIntentItem[] subitems;
+        public final int flags;
+        public final Uri[] requireUrisGranted;
+        public final Uri[] requireUrisNotGranted;
+
+        public TestIntentItem(Intent intent) {
+            this.intent = intent;
+            subitems = null;
+            flags = 0;
+            requireUrisGranted = null;
+            requireUrisNotGranted = null;
+        }
+
+        public TestIntentItem(Intent intent, int flags) {
+            this.intent = intent;
+            subitems = null;
+            this.flags = flags;
+            intent.putExtra("flags", flags);
+            requireUrisGranted = null;
+            requireUrisNotGranted = null;
+        }
+
+        public TestIntentItem(Intent intent, TestIntentItem[] subitems) {
+            this.intent = intent;
+            this.subitems = subitems;
+            intent.putExtra("subitems", subitems);
+            flags = 0;
+            requireUrisGranted = null;
+            requireUrisNotGranted = null;
+        }
+
+        public TestIntentItem(Intent intent, Uri[] requireUrisGranted,
+                Uri[] requireUrisNotGranted) {
+            this.intent = intent;
+            subitems = null;
+            flags = 0;
+            this.requireUrisGranted = requireUrisGranted;
+            this.requireUrisNotGranted = requireUrisNotGranted;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(64);
+            sb.append("TestIntentItem { ");
+            sb.append(intent);
+            sb.append(" }");
+            return sb.toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            intent.writeToParcel(parcel, flags);
+            parcel.writeTypedArray(subitems, flags);
+            parcel.writeInt(flags);
+        }
+
+        TestIntentItem(Parcel parcel) {
+            intent = Intent.CREATOR.createFromParcel(parcel);
+            subitems = parcel.createTypedArray(CREATOR);
+            flags = parcel.readInt();
+            requireUrisGranted = null;
+            requireUrisNotGranted = null;
+        }
+
+        public static final Parcelable.Creator<TestIntentItem> CREATOR =
+                new Parcelable.Creator<TestIntentItem>() {
+
+                    public TestIntentItem createFromParcel(Parcel source) {
+                        return new TestIntentItem(source);
+                    }
+
+                    public TestIntentItem[] newArray(int size) {
+                        return new TestIntentItem[size];
+                    }
+                };
+    }
+
+    static void initStatics() {
+        synchronized (sLock) {
+            sReadyToRunLatch = new CountDownLatch(1);
+            sServiceWaitingLatch = new CountDownLatch(1);
+            sServiceStoppedLatch = new CountDownLatch(1);
+            sWaitCompleteLatch = new CountDownLatch(1);
+            sServiceFinishedLatch = new CountDownLatch(1);
+            sFinished = false;
+            sFinishedWork = null;
+            sFinishedErrorMsg = null;
+        }
+    }
+
+    static void allowServiceToRun() {
+        sReadyToRunLatch.countDown();
+    }
+
+    static void serviceReportWaiting() {
+        sServiceWaitingLatch.countDown();
+    }
+
+    static void ensureServiceWaiting() {
+        try {
+            if (!sServiceWaitingLatch.await(10, TimeUnit.SECONDS)) {
+                fail("Timed out waiting for wait, service state " + sLastServiceState);
+            }
+        } catch (InterruptedException e) {
+            fail("Interrupted waiting for service to wait: " + e);
+        }
+    }
+
+    static void serviceReportStopped() {
+        sServiceStoppedLatch.countDown();
+    }
+
+    static void ensureServiceStopped() {
+        try {
+            if (!sServiceStoppedLatch.await(10, TimeUnit.SECONDS)) {
+                fail("Timed out waiting for stop, service state " + sLastServiceState);
+            }
+        } catch (InterruptedException e) {
+            fail("Interrupted waiting for service to stop: " + e);
+        }
+    }
+
+    static void allowServiceToResumeFromWait() {
+        sWaitCompleteLatch.countDown();
+    }
+
+    static void finishServiceExecution(ArrayList<Intent> work, String errorMsg) {
+        synchronized (sLock) {
+            if (!sFinished) {
+                sFinishedWork = work;
+                sFinishedErrorMsg = errorMsg;
+                sServiceFinishedLatch.countDown();
+            }
+        }
+    }
+
+    static void updateServiceState(String msg) {
+        synchronized (sLock) {
+            sLastServiceState = msg;
+        }
+    }
+
+    void waitServiceFinish() {
+        try {
+            if (!sServiceFinishedLatch.await(10, TimeUnit.SECONDS)) {
+                synchronized (sLock) {
+                    if (sFinishedErrorMsg != null) {
+                        fail("Timed out waiting for finish, service state " + sLastServiceState
+                                + ", had error: " + sFinishedErrorMsg);
+                    }
+                    fail("Timed out waiting for finish, service state " + sLastServiceState);
+                }
+            }
+        } catch (InterruptedException e) {
+            fail("Interrupted waiting for service to finish: " + e);
+        }
+        synchronized (sLock) {
+            if (sFinishedErrorMsg != null) {
+                fail(sFinishedErrorMsg);
+            }
+        }
+    }
+
+    public static class TargetService extends JobIntentService {
+        final ArrayList<Intent> mReceivedWork = new ArrayList<>();
+
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            updateServiceState("Creating: " + this);
+            Log.i(TAG, "Creating: " + this);
+            Log.i(TAG, "Waiting for ready to run...");
+            try {
+                if (!sReadyToRunLatch.await(10, TimeUnit.SECONDS)) {
+                    finishServiceExecution(null, "Timeout waiting for ready");
+                }
+            } catch (InterruptedException e) {
+                finishServiceExecution(null, "Interrupted waiting for ready: " + e);
+            }
+            updateServiceState("Past ready to run");
+            Log.i(TAG, "Running!");
+        }
+
+        @Override
+        protected void onHandleWork(@Nullable Intent intent) {
+            Log.i(TAG, "Handling work: " + intent);
+            updateServiceState("Handling work: " + intent);
+            mReceivedWork.add(intent);
+            intent.setExtrasClassLoader(TestIntentItem.class.getClassLoader());
+            int flags = intent.getIntExtra("flags", 0);
+            if ((flags & TestIntentItem.FLAG_WAIT) != 0) {
+                serviceReportWaiting();
+                try {
+                    if (!sWaitCompleteLatch.await(10, TimeUnit.SECONDS)) {
+                        finishServiceExecution(null, "Timeout waiting for wait complete");
+                    }
+                } catch (InterruptedException e) {
+                    finishServiceExecution(null, "Interrupted waiting for wait complete: " + e);
+                }
+                if ((flags & TestIntentItem.FLAG_STOPPED_AFTER_WAIT) != 0) {
+                    if (!isStopped()) {
+                        finishServiceExecution(null, "Service not stopped after waiting");
+                    }
+                }
+            }
+            Parcelable[] subitems = intent.getParcelableArrayExtra("subitems");
+            if (subitems != null) {
+                for (Parcelable pitem : subitems) {
+                    JobIntentService.enqueueWork(this, TargetService.class,
+                            JOB_ID, ((TestIntentItem) pitem).intent);
+                }
+            }
+        }
+
+        @Override
+        public boolean onStopCurrentWork() {
+            serviceReportStopped();
+            return super.onStopCurrentWork();
+        }
+
+        @Override
+        public void onDestroy() {
+            Log.i(TAG, "Destroying: " + this);
+            updateServiceState("Destroying: " + this);
+            finishServiceExecution(mReceivedWork, null);
+            super.onDestroy();
+        }
+    }
+
+    private boolean intentEquals(Intent i1, Intent i2) {
+        if (i1 == i2) {
+            return true;
+        }
+        if (i1 == null || i2 == null) {
+            return false;
+        }
+        return i1.filterEquals(i2);
+    }
+
+    private void compareIntents(TestIntentItem[] expected, ArrayList<Intent> received) {
+        if (received == null) {
+            fail("Didn't receive any expected work.");
+        }
+        ArrayList<TestIntentItem> expectedArray = new ArrayList<>();
+        for (int i = 0; i < expected.length; i++) {
+            expectedArray.add(expected[i]);
+        }
+
+        ComponentName serviceComp = new ComponentName(mContext, TargetService.class.getName());
+
+        for (int i = 0; i < received.size(); i++) {
+            Intent r = received.get(i);
+            if (i < expected.length && expected[i].subitems != null) {
+                TestIntentItem[] sub = expected[i].subitems;
+                for (int j = 0; j < sub.length; j++) {
+                    expectedArray.add(sub[j]);
+                }
+            }
+            if (i >= expectedArray.size()) {
+                fail("Received more than " + expected.length + " work items, first extra is "
+                        + r);
+            }
+            if (r.getComponent() != null) {
+                // Intents we get back from the compat service will have a component... make
+                // sure that is correct, and then erase it so the intentEquals() will pass.
+                assertEquals(serviceComp, r.getComponent());
+                r.setComponent(null);
+            }
+            if (!intentEquals(r, expectedArray.get(i).intent)) {
+                fail("Received intent #" + i + " " + r + " but expected " + expected[i]);
+            }
+        }
+        if (received.size() < expected.length) {
+            fail("Received only " + received.size() + " work items, but expected "
+                    + expected.length);
+        }
+    }
+
+    /**
+     * Test simple case of enqueueing one piece of work.
+     */
+    @MediumTest
+    @Test
+    public void testEnqueueOne() throws Throwable {
+        initStatics();
+
+        TestIntentItem[] items = new TestIntentItem[] {
+                new TestIntentItem(new Intent("FIRST")),
+        };
+
+        for (TestIntentItem item : items) {
+            JobIntentService.enqueueWork(mContext, TargetService.class, JOB_ID, item.intent);
+        }
+        allowServiceToRun();
+
+        waitServiceFinish();
+        compareIntents(items, sFinishedWork);
+    }
+
+    /**
+     * Test case of enqueueing multiple pieces of work.
+     */
+    @MediumTest
+    @Test
+    public void testEnqueueMultiple() throws Throwable {
+        initStatics();
+
+        TestIntentItem[] items = new TestIntentItem[] {
+                new TestIntentItem(new Intent("FIRST")),
+                new TestIntentItem(new Intent("SECOND")),
+                new TestIntentItem(new Intent("THIRD")),
+                new TestIntentItem(new Intent("FOURTH")),
+        };
+
+        for (TestIntentItem item : items) {
+            JobIntentService.enqueueWork(mContext, TargetService.class, JOB_ID, item.intent);
+        }
+        allowServiceToRun();
+
+        waitServiceFinish();
+        compareIntents(items, sFinishedWork);
+    }
+
+    /**
+     * Test case of enqueueing multiple pieces of work.
+     */
+    @MediumTest
+    @Test
+    public void testEnqueueSubWork() throws Throwable {
+        initStatics();
+
+        TestIntentItem[] items = new TestIntentItem[] {
+                new TestIntentItem(new Intent("FIRST")),
+                new TestIntentItem(new Intent("SECOND")),
+                new TestIntentItem(new Intent("THIRD"), new TestIntentItem[] {
+                        new TestIntentItem(new Intent("FIFTH")),
+                        new TestIntentItem(new Intent("SIXTH")),
+                        new TestIntentItem(new Intent("SEVENTH")),
+                        new TestIntentItem(new Intent("EIGTH")),
+                }),
+                new TestIntentItem(new Intent("FOURTH")),
+        };
+
+        for (TestIntentItem item : items) {
+            JobIntentService.enqueueWork(mContext, TargetService.class, JOB_ID, item.intent);
+        }
+        allowServiceToRun();
+
+        waitServiceFinish();
+        compareIntents(items, sFinishedWork);
+    }
+
+    /**
+     * Test case of job stopping while it is doing work.
+     */
+    @MediumTest
+    @Test
+    @RequiresApi(26)
+    public void testStopWhileWorking() throws Throwable {
+        if (Build.VERSION.SDK_INT < 26) {
+            // This test only makes sense when running on top of JobScheduler.
+            return;
+        }
+
+        initStatics();
+
+        TestIntentItem[] items = new TestIntentItem[] {
+                new TestIntentItem(new Intent("FIRST"),
+                        TestIntentItem.FLAG_WAIT | TestIntentItem.FLAG_STOPPED_AFTER_WAIT),
+        };
+
+        for (TestIntentItem item : items) {
+            JobIntentService.enqueueWork(mContext, TargetService.class, JOB_ID, item.intent);
+        }
+        allowServiceToRun();
+        ensureServiceWaiting();
+
+        // At this point we will make the job stop...  this isn't normally how this would
+        // happen with an IntentJobService, and doing it this way breaks re-delivery of
+        // work, but we have CTS tests for the underlying redlivery mechanism.
+        ((JobScheduler) mContext.getApplicationContext().getSystemService(
+                Context.JOB_SCHEDULER_SERVICE)).cancel(JOB_ID);
+        ensureServiceStopped();
+
+        allowServiceToResumeFromWait();
+
+        waitServiceFinish();
+        compareIntents(items, sFinishedWork);
+    }
+}
diff --git a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
index 9978959..dd870dd 100644
--- a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
+++ b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
@@ -16,13 +16,27 @@
 
 package android.support.v4.app;
 
+import static android.support.v4.app.NotificationCompat.DEFAULT_ALL;
+import static android.support.v4.app.NotificationCompat.DEFAULT_LIGHTS;
+import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
+import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
+import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -32,14 +46,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+
 
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class NotificationCompatTest extends BaseInstrumentationTestCase<TestSupportActivity> {
+    private static final String TEXT_RESULT_KEY = "text";
+    private static final String DATA_RESULT_KEY = "data";
+    private static final String EXTRA_COLORIZED = "android.colorized";
 
     Context mContext;
 
     public NotificationCompatTest() {
-      super(TestSupportActivity.class);
+        super(TestSupportActivity.class);
     }
 
     @Before
@@ -47,18 +67,108 @@
         mContext = mActivityTestRule.getActivity();
     }
 
-    @SmallTest
+    @Test
+    public void testBadgeIcon() throws Throwable {
+        int badgeIcon = NotificationCompat.BADGE_ICON_SMALL;
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setBadgeIconType(badgeIcon)
+                .build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertEquals(badgeIcon, NotificationCompat.getBadgeIconType(n));
+        } else {
+            assertEquals(NotificationCompat.BADGE_ICON_NONE,
+                    NotificationCompat.getBadgeIconType(n));
+        }
+    }
+
+    @Test
+    public void testTimeout() throws Throwable {
+        long timeout = 23552;
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setTimeoutAfter(timeout)
+                .build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertEquals(timeout, NotificationCompat.getTimeoutAfter(n));
+        } else {
+            assertEquals(0, NotificationCompat.getTimeoutAfter(n));
+        }
+    }
+
+    @Test
+    public void testShortcutId() throws Throwable {
+        String shortcutId = "fgdfg";
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setShortcutId(shortcutId)
+                .build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertEquals(shortcutId, NotificationCompat.getShortcutId(n));
+        } else {
+            assertEquals(null, NotificationCompat.getShortcutId(n));
+        }
+    }
+
+    @Test
+    public void testNotificationChannel() throws Throwable {
+        String channelId = "new ID";
+        Notification n  = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setChannelId(channelId)
+                .build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertEquals(channelId, NotificationCompat.getChannelId(n));
+        } else {
+            assertNull(NotificationCompat.getChannelId(n));
+        }
+    }
+
+    @Test
+    public void testNotificationChannel_assignedFromBuilder() throws Throwable {
+        String channelId = "new ID";
+        Notification n  = new NotificationCompat.Builder(mActivityTestRule.getActivity(), channelId)
+                .build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertEquals(channelId, NotificationCompat.getChannelId(n));
+        } else {
+            assertNull(NotificationCompat.getChannelId(n));
+        }
+    }
+
+    @Test
+    public void testNotificationActionBuilder_assignsColorized() throws Throwable {
+        Notification n = newNotificationBuilder().setColorized(true).build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            Bundle extras = NotificationCompat.getExtras(n);
+            assertTrue(Boolean.TRUE.equals(extras.get(EXTRA_COLORIZED)));
+        }
+    }
+
+    @Test
+    public void testNotificationActionBuilder_unassignesColorized() throws Throwable {
+        Notification n = newNotificationBuilder().setColorized(false).build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            Bundle extras = NotificationCompat.getExtras(n);
+            assertTrue(Boolean.FALSE.equals(extras.get(EXTRA_COLORIZED)));
+        }
+    }
+
+    @Test
+    public void testNotificationActionBuilder_doesntAssignColorized() throws Throwable {
+        Notification n = newNotificationBuilder().build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            Bundle extras = NotificationCompat.getExtras(n);
+            assertFalse(extras.containsKey(EXTRA_COLORIZED));
+        }
+    }
+
     @Test
     public void testNotificationActionBuilder_copiesRemoteInputs() throws Throwable {
         NotificationCompat.Action a = newActionBuilder()
-                .addRemoteInput(new RemoteInput("a", "b", null, false, null)).build();
+                .addRemoteInput(new RemoteInput("a", "b", null, false, null, null)).build();
 
         NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
 
         assertSame(a.getRemoteInputs()[0], aCopy.getRemoteInputs()[0]);
     }
 
-    @SmallTest
     @Test
     public void testNotificationActionBuilder_copiesAllowGeneratedReplies() throws Throwable {
         NotificationCompat.Action a = newActionBuilder()
@@ -69,7 +179,18 @@
         assertEquals(a.getAllowGeneratedReplies(), aCopy.getAllowGeneratedReplies());
     }
 
-    @SmallTest
+    @SdkSuppress(minSdkVersion = 24)
+    @TargetApi(24)
+    @Test
+    public void testFrameworkNotificationActionBuilder_setAllowGeneratedRepliesTrue()
+            throws Throwable {
+        Notification notif = new Notification.Builder(mContext)
+                .addAction(new Notification.Action.Builder(0, "title", null)
+                        .setAllowGeneratedReplies(true).build()).build();
+        NotificationCompat.Action action = NotificationCompat.getAction(notif, 0);
+        assertTrue(action.getAllowGeneratedReplies());
+    }
+
     @Test
     public void testNotificationActionBuilder_defaultAllowGeneratedRepliesTrue() throws Throwable {
         NotificationCompat.Action a = newActionBuilder().build();
@@ -77,7 +198,6 @@
         assertTrue(a.getAllowGeneratedReplies());
     }
 
-    @SmallTest
     @Test
     public void testNotificationAction_defaultAllowGeneratedRepliesTrue() throws Throwable {
         NotificationCompat.Action a = new NotificationCompat.Action(0, null, null);
@@ -85,7 +205,6 @@
         assertTrue(a.getAllowGeneratedReplies());
     }
 
-    @SmallTest
     @Test
     public void testNotificationActionBuilder_setAllowGeneratedRepliesFalse() throws Throwable {
         NotificationCompat.Action a = newActionBuilder()
@@ -95,7 +214,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
-    @SmallTest
+    @TargetApi(17)
     @Test
     public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesTrue()
             throws Throwable {
@@ -109,7 +228,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
-    @SmallTest
+    @TargetApi(17)
     @Test
     public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesFalse()
             throws Throwable {
@@ -136,9 +255,196 @@
         assertTrue(new NotificationCompat.WearableExtender(notification).getActions().size() == 0);
     }
 
-    private NotificationCompat.Action.Builder newActionBuilder() {
+    @Test
+    public void testNotificationActionBuilder_setDataOnlyRemoteInput() throws Throwable {
+        NotificationCompat.Action a = newActionBuilder()
+                .addRemoteInput(newDataOnlyRemoteInput()).build();
+        RemoteInput[] textInputs = a.getRemoteInputs();
+        assertTrue(textInputs == null || textInputs.length == 0);
+        verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
+    }
+
+    @Test
+    public void testNotificationActionBuilder_setTextAndDataOnlyRemoteInput() throws Throwable {
+        NotificationCompat.Action a = newActionBuilder()
+                .addRemoteInput(newDataOnlyRemoteInput())
+                .addRemoteInput(newTextRemoteInput())
+                .build();
+
+        verifyRemoteInputArrayHasSingleResult(a.getRemoteInputs(), TEXT_RESULT_KEY);
+        verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
+    }
+
+    @Test
+    public void testMessage_setAndGetExtras() throws Throwable {
+        String extraKey = "extra_key";
+        CharSequence extraValue = "extra_value";
+        NotificationCompat.MessagingStyle.Message m =
+                new NotificationCompat.MessagingStyle.Message("text", 0 /*timestamp */, "sender");
+        m.getExtras().putCharSequence(extraKey, extraValue);
+        assertEquals(extraValue, m.getExtras().getCharSequence(extraKey));
+
+        ArrayList<NotificationCompat.MessagingStyle.Message> messages = new ArrayList<>(1);
+        messages.add(m);
+        Bundle[] bundleArray =
+                NotificationCompat.MessagingStyle.Message.getBundleArrayForMessages(messages);
+        assertEquals(1, bundleArray.length);
+        NotificationCompat.MessagingStyle.Message fromBundle =
+                NotificationCompat.MessagingStyle.Message.getMessageFromBundle(bundleArray[0]);
+        assertEquals(extraValue, fromBundle.getExtras().getCharSequence(extraKey));
+    }
+
+    @Test
+    public void testGetGroupAlertBehavior() throws Throwable {
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
+                .build();
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertEquals(GROUP_ALERT_CHILDREN, NotificationCompat.getGroupAlertBehavior(n));
+        } else {
+            assertEquals(GROUP_ALERT_ALL, NotificationCompat.getGroupAlertBehavior(n));
+        }
+    }
+
+    @Test
+    public void testGroupAlertBehavior_mutesGroupNotifications() throws Throwable {
+        // valid between api 20, when groups were added, and api 25, the last to use sound
+        // and vibration from the notification itself
+
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
+                .setVibrate(new long[] {235})
+                .setSound(Uri.EMPTY)
+                .setDefaults(DEFAULT_ALL)
+                .setGroup("grouped")
+                .setGroupSummary(true)
+                .build();
+
+        Notification n2 = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
+                .setVibrate(new long[] {235})
+                .setSound(Uri.EMPTY)
+                .setDefaults(DEFAULT_ALL)
+                .setGroup("grouped")
+                .setGroupSummary(false)
+                .build();
+
+        if (Build.VERSION.SDK_INT >= 20 && !(Build.VERSION.SDK_INT >= 26)) {
+            assertNull(n.sound);
+            assertNull(n.vibrate);
+            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n.defaults & DEFAULT_SOUND) == 0);
+            assertTrue((n.defaults & DEFAULT_VIBRATE) == 0);
+
+            assertNull(n2.sound);
+            assertNull(n2.vibrate);
+            assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n2.defaults & DEFAULT_SOUND) == 0);
+            assertTrue((n2.defaults & DEFAULT_VIBRATE) == 0);
+        } else if (Build.VERSION.SDK_INT < 20) {
+            assertNotNull(n.sound);
+            assertNotNull(n.vibrate);
+            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n.defaults & DEFAULT_SOUND) != 0);
+            assertTrue((n.defaults & DEFAULT_VIBRATE) != 0);
+
+            assertNotNull(n2.sound);
+            assertNotNull(n2.vibrate);
+            assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n2.defaults & DEFAULT_SOUND) != 0);
+            assertTrue((n2.defaults & DEFAULT_VIBRATE) != 0);
+        }
+    }
+
+    @Test
+    public void testGroupAlertBehavior_doesNotMuteIncorrectGroupNotifications() throws Throwable {
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
+                .setVibrate(new long[] {235})
+                .setSound(Uri.EMPTY)
+                .setDefaults(DEFAULT_ALL)
+                .setGroup("grouped")
+                .setGroupSummary(true)
+                .build();
+
+        Notification n2 = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
+                .setVibrate(new long[] {235})
+                .setSound(Uri.EMPTY)
+                .setDefaults(DEFAULT_ALL)
+                .setGroup("grouped")
+                .setGroupSummary(false)
+                .build();
+
+        Notification n3 = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setVibrate(new long[] {235})
+                .setSound(Uri.EMPTY)
+                .setDefaults(DEFAULT_ALL)
+                .setGroup("grouped")
+                .setGroupSummary(false)
+                .build();
+
+        if (Build.VERSION.SDK_INT >= 20 && !(Build.VERSION.SDK_INT >= 26)) {
+            assertNotNull(n.sound);
+            assertNotNull(n.vibrate);
+            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n.defaults & DEFAULT_SOUND) != 0);
+            assertTrue((n.defaults & DEFAULT_VIBRATE) != 0);
+
+            assertNotNull(n2.sound);
+            assertNotNull(n2.vibrate);
+            assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n2.defaults & DEFAULT_SOUND) != 0);
+            assertTrue((n2.defaults & DEFAULT_VIBRATE) != 0);
+
+            assertNotNull(n3.sound);
+            assertNotNull(n3.vibrate);
+            assertTrue((n3.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n3.defaults & DEFAULT_SOUND) != 0);
+            assertTrue((n3.defaults & DEFAULT_VIBRATE) != 0);
+        }
+    }
+
+    @Test
+    public void testGroupAlertBehavior_doesNotMuteNonGroupNotifications() throws Throwable {
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
+                .setVibrate(new long[] {235})
+                .setSound(Uri.EMPTY)
+                .setDefaults(DEFAULT_ALL)
+                .setGroup(null)
+                .setGroupSummary(false)
+                .build();
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            assertNotNull(n.sound);
+            assertNotNull(n.vibrate);
+            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
+            assertTrue((n.defaults & DEFAULT_SOUND) != 0);
+            assertTrue((n.defaults & DEFAULT_VIBRATE) != 0);
+        }
+    }
+
+    private static RemoteInput newDataOnlyRemoteInput() {
+        return new RemoteInput.Builder(DATA_RESULT_KEY)
+            .setAllowFreeFormInput(false)
+            .setAllowDataType("mimeType", true)
+            .build();
+    }
+
+    private static RemoteInput newTextRemoteInput() {
+        return new RemoteInput.Builder(TEXT_RESULT_KEY).build();  // allowFreeForm defaults to true
+    }
+
+    private static void verifyRemoteInputArrayHasSingleResult(
+            RemoteInput[] remoteInputs, String expectedResultKey) {
+        assertTrue(remoteInputs != null && remoteInputs.length == 1);
+        assertEquals(expectedResultKey, remoteInputs[0].getResultKey());
+    }
+
+    private static NotificationCompat.Action.Builder newActionBuilder() {
         return new NotificationCompat.Action.Builder(0, "title", null);
     }
+
     private NotificationCompat.Builder newNotificationBuilder() {
         return new NotificationCompat.Builder(mContext)
                 .setSmallIcon(0)
diff --git a/compat/tests/java/android/support/v4/app/RemoteInputTest.java b/compat/tests/java/android/support/v4/app/RemoteInputTest.java
new file mode 100644
index 0000000..55d6cca
--- /dev/null
+++ b/compat/tests/java/android/support/v4/app/RemoteInputTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.BaseInstrumentationTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RemoteInputTest extends BaseInstrumentationTestCase<TestSupportActivity> {
+    private static final String RESULT_KEY = "result_key";  // value doesn't matter
+    private static final String MIME_TYPE = "mimeType";  // value doesn't matter
+
+    public RemoteInputTest() {
+        super(TestSupportActivity.class);
+    }
+
+    @Test
+    public void testRemoteInputBuilder_setDataOnly() throws Throwable {
+        RemoteInput input = newDataOnlyRemoteInput();
+
+        assertTrue(input.isDataOnly());
+        assertFalse(input.getAllowFreeFormInput());
+        assertTrue(input.getChoices() == null || input.getChoices().length == 0);
+        assertEquals(1, input.getAllowedDataTypes().size());
+        assertTrue(input.getAllowedDataTypes().contains(MIME_TYPE));
+    }
+
+    @Test
+    public void testRemoteInputBuilder_setTextOnly() throws Throwable {
+        RemoteInput input = newTextRemoteInput();
+
+        assertFalse(input.isDataOnly());
+        assertTrue(input.getAllowFreeFormInput());
+        assertTrue(input.getChoices() == null || input.getChoices().length == 0);
+        assertTrue(input.getAllowedDataTypes() == null || input.getAllowedDataTypes().isEmpty());
+    }
+
+    @Test
+    public void testRemoteInputBuilder_setChoicesOnly() throws Throwable {
+        RemoteInput input = newChoicesOnlyRemoteInput();
+
+        assertFalse(input.isDataOnly());
+        assertFalse(input.getAllowFreeFormInput());
+        assertTrue(input.getChoices() != null && input.getChoices().length > 0);
+        assertTrue(input.getAllowedDataTypes() == null || input.getAllowedDataTypes().isEmpty());
+    }
+
+    @Test
+    public void testRemoteInputBuilder_setDataAndTextAndChoices() throws Throwable {
+        CharSequence[] choices = new CharSequence[2];
+        choices[0] = "first";
+        choices[1] = "second";
+        RemoteInput input =
+                new RemoteInput.Builder(RESULT_KEY)
+                .setChoices(choices)
+                .setAllowDataType(MIME_TYPE, true)
+                .build();
+
+        assertFalse(input.isDataOnly());
+        assertTrue(input.getAllowFreeFormInput());
+        assertTrue(input.getChoices() != null && input.getChoices().length > 0);
+        assertEquals(1, input.getAllowedDataTypes().size());
+        assertTrue(input.getAllowedDataTypes().contains(MIME_TYPE));
+    }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
+    @Test
+    public void testRemoteInputBuilder_addAndGetDataResultsFromIntent() throws Throwable {
+        Uri uri = Uri.parse("Some Uri");
+        RemoteInput input = newDataOnlyRemoteInput();
+        Intent intent = new Intent();
+        Map<String, Uri> putResults = new HashMap<>();
+        putResults.put(MIME_TYPE, uri);
+        RemoteInput.addDataResultToIntent(input, intent, putResults);
+
+        verifyIntentHasDataResults(intent, uri);
+    }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
+    @Test
+    public void testRemoteInputBuilder_addAndGetTextResultsFromIntent() throws Throwable {
+        CharSequence charSequence = "value doesn't matter";
+        RemoteInput input = newTextRemoteInput();
+        Intent intent = new Intent();
+        Bundle putResults = new Bundle();
+        putResults.putCharSequence(input.getResultKey(), charSequence);
+        RemoteInput[] arr = new RemoteInput[1];
+        arr[0] = input;
+        RemoteInput.addResultsToIntent(arr, intent, putResults);
+
+        verifyIntentHasTextResults(intent, charSequence);
+    }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
+    @Test
+    public void testRemoteInputBuilder_addAndGetDataAndTextResultsFromIntentDataFirst()
+            throws Throwable {
+        CharSequence charSequence = "value doesn't matter";
+        Uri uri = Uri.parse("Some Uri");
+        RemoteInput input =
+                new RemoteInput.Builder(RESULT_KEY)
+                .setAllowDataType(MIME_TYPE, true)
+                .build();
+        Intent intent = new Intent();
+
+        Map<String, Uri> dataResults = new HashMap<>();
+        dataResults.put(MIME_TYPE, uri);
+        RemoteInput.addDataResultToIntent(input, intent, dataResults);
+
+        Bundle textResults = new Bundle();
+        textResults.putCharSequence(input.getResultKey(), charSequence);
+        RemoteInput[] arr = new RemoteInput[1];
+        arr[0] = input;
+        RemoteInput.addResultsToIntent(arr, intent, textResults);
+
+        verifyIntentHasTextResults(intent, charSequence);
+        verifyIntentHasDataResults(intent, uri);
+    }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
+    @Test
+    public void testRemoteInputBuilder_addAndGetDataAndTextResultsFromIntentTextFirst()
+            throws Throwable {
+        CharSequence charSequence = "value doesn't matter";
+        Uri uri = Uri.parse("Some Uri");
+        RemoteInput input =
+                new RemoteInput.Builder(RESULT_KEY)
+                .setAllowDataType(MIME_TYPE, true)
+                .build();
+        Intent intent = new Intent();
+
+        Bundle textResults = new Bundle();
+        textResults.putCharSequence(input.getResultKey(), charSequence);
+        RemoteInput[] arr = new RemoteInput[1];
+        arr[0] = input;
+        RemoteInput.addResultsToIntent(arr, intent, textResults);
+
+        Map<String, Uri> dataResults = new HashMap<>();
+        dataResults.put(MIME_TYPE, uri);
+        RemoteInput.addDataResultToIntent(input, intent, dataResults);
+
+        verifyIntentHasTextResults(intent, charSequence);
+        verifyIntentHasDataResults(intent, uri);
+    }
+
+    private static void verifyIntentHasTextResults(Intent intent, CharSequence expected) {
+        Bundle getResults = RemoteInput.getResultsFromIntent(intent);
+        assertNotNull(getResults);
+        assertTrue(getResults.containsKey(RESULT_KEY));
+        assertEquals(expected, getResults.getCharSequence(RESULT_KEY, "default"));
+    }
+
+    private static void verifyIntentHasDataResults(Intent intent, Uri expectedUri) {
+        Map<String, Uri> getResults = RemoteInput.getDataResultsFromIntent(intent, RESULT_KEY);
+        assertNotNull(getResults);
+        assertEquals(1, getResults.size());
+        assertTrue(getResults.containsKey(MIME_TYPE));
+        assertEquals(expectedUri, getResults.get(MIME_TYPE));
+    }
+
+    private static RemoteInput newTextRemoteInput() {
+        return new RemoteInput.Builder(RESULT_KEY).build();  // allowFreeForm defaults to true
+    }
+
+    private static RemoteInput newChoicesOnlyRemoteInput() {
+        CharSequence[] choices = new CharSequence[2];
+        choices[0] = "first";
+        choices[1] = "second";
+        return new RemoteInput.Builder(RESULT_KEY)
+            .setAllowFreeFormInput(false)
+            .setChoices(choices)
+            .build();
+    }
+
+    private static RemoteInput newDataOnlyRemoteInput() {
+        return new RemoteInput.Builder(RESULT_KEY)
+            .setAllowFreeFormInput(false)
+            .setAllowDataType(MIME_TYPE, true)
+            .build();
+    }
+}
diff --git a/compat/tests/java/android/support/v4/content/pm/ShortcutManagerCompatTest.java b/compat/tests/java/android/support/v4/content/pm/ShortcutManagerCompatTest.java
new file mode 100644
index 0000000..77e7ae8
--- /dev/null
+++ b/compat/tests/java/android/support/v4/content/pm/ShortcutManagerCompatTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.support.v4.app.TestSupportActivity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class ShortcutManagerCompatTest extends BaseInstrumentationTestCase<TestSupportActivity> {
+
+    Context mContext;
+    ShortcutInfoCompat mInfoCompat;
+
+    public ShortcutManagerCompatTest() {
+        super(TestSupportActivity.class);
+    }
+
+    @Before
+    public void setup() {
+        mContext = spy(mActivityTestRule.getActivity());
+        mInfoCompat = new ShortcutInfoCompat.Builder(mContext, "test-id")
+                .setIcon(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888))
+                .setShortLabel("Test shortcut")
+                .setIntent(new Intent("Dummy"))
+                .build();
+    }
+
+    @Test
+    @SmallTest
+    @TargetApi(26)
+    public void testIsRequestPinShortcutSupported_v26() throws Throwable {
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            return;
+        }
+
+        ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
+        doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE));
+        when(mockShortcutManager.isRequestPinShortcutSupported()).thenReturn(true, false, true);
+
+        assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+        assertFalse(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+        assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+        verify(mockShortcutManager, times(3)).isRequestPinShortcutSupported();
+    }
+
+    @Test
+    @SmallTest
+    @TargetApi(26)
+    public void testRequestPinShortcut_v26()  throws Throwable {
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            return;
+        }
+
+        ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
+        doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE));
+        when(mockShortcutManager.requestPinShortcut(
+                any(ShortcutInfo.class), any(IntentSender.class))).thenReturn(true);
+
+        assertTrue(ShortcutManagerCompat.requestPinShortcut(mContext, mInfoCompat, null));
+        ArgumentCaptor<ShortcutInfo> captor = ArgumentCaptor.forClass(ShortcutInfo.class);
+        verify(mockShortcutManager, times(1)).requestPinShortcut(captor.capture(),
+                (IntentSender) isNull());
+        assertEquals("test-id", captor.getValue().getId());
+    }
+
+    @Test
+    @SmallTest
+    @TargetApi(26)
+    public void testCreateShortcutResultIntent_v26()  throws Throwable {
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            return;
+        }
+
+        ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
+        doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE));
+
+        when(mockShortcutManager.createShortcutResultIntent(any(ShortcutInfo.class)))
+                .thenReturn(new Intent("some-dummy-action"));
+
+        Intent result = ShortcutManagerCompat.createShortcutResultIntent(mContext, mInfoCompat);
+        verifyLegacyIntent(result);
+        assertEquals("some-dummy-action", result.getAction());
+
+        ArgumentCaptor<ShortcutInfo> captor = ArgumentCaptor.forClass(ShortcutInfo.class);
+        verify(mockShortcutManager, times(1)).createShortcutResultIntent(captor.capture());
+        assertEquals("test-id", captor.getValue().getId());
+    }
+
+    @SmallTest
+    @Test
+    public void testIsRequestPinShortcutSupported_v4() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return;
+        }
+        setMockPm(mockResolveInfo(null));
+        assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+
+        // We do not have the permission
+        setMockPm(mockResolveInfo("com.android.permission.something-we-dont-have"));
+        assertFalse(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+
+        // There are no receivers
+        setMockPm();
+        assertFalse(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+
+        // At least one receiver is supported
+        setMockPm(mockResolveInfo("com.android.permission.something-we-dont-have"),
+                mockResolveInfo(null));
+        assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+
+        // We have the permission
+        setMockPm(mockResolveInfo(ShortcutManagerCompat.INSTALL_SHORTCUT_PERMISSION));
+        assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext));
+    }
+
+    @LargeTest
+    @Test
+    public void testRequestPinShortcut_v4_noCallback()  throws Throwable {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return;
+        }
+
+        setMockPm(mockResolveInfo(null));
+
+        BlockingBroadcastReceiver receiver =
+                new BlockingBroadcastReceiver(ShortcutManagerCompat.ACTION_INSTALL_SHORTCUT);
+        assertTrue(ShortcutManagerCompat.requestPinShortcut(mContext, mInfoCompat, null));
+        verifyLegacyIntent(receiver.blockingGetIntent());
+    }
+
+    @MediumTest
+    @Test
+    public void testRequestPinShortcut_v4_withCallback()  throws Throwable {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return;
+        }
+
+        setMockPm(mockResolveInfo(null));
+
+        BlockingBroadcastReceiver receiver =
+                new BlockingBroadcastReceiver(ShortcutManagerCompat.ACTION_INSTALL_SHORTCUT);
+        BlockingBroadcastReceiver callback =
+                new BlockingBroadcastReceiver("shortcut-callback");
+
+        assertTrue(ShortcutManagerCompat.requestPinShortcut(mContext, mInfoCompat,
+                PendingIntent.getBroadcast(mContext, 0, new Intent("shortcut-callback"),
+                        PendingIntent.FLAG_ONE_SHOT).getIntentSender()));
+        verifyLegacyIntent(receiver.blockingGetIntent());
+        assertNotNull(callback.blockingGetIntent());
+    }
+
+    @SmallTest
+    @Test
+    public void testCreateShortcutResultIntent_v4() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return;
+        }
+
+        verifyLegacyIntent(ShortcutManagerCompat.createShortcutResultIntent(mContext, mInfoCompat));
+    }
+
+    private void verifyLegacyIntent(Intent intent) {
+        assertNotNull(intent);
+        assertEquals("Test shortcut", intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
+        assertEquals("Dummy", ((Intent) intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT))
+                .getAction());
+    }
+
+    private void setMockPm(ResolveInfo... infos) {
+        PackageManager pm = mock(PackageManager.class);
+        when(pm.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(Arrays.asList(infos));
+        reset(mContext);
+        doReturn(pm).when(mContext).getPackageManager();
+    }
+
+    private ResolveInfo mockResolveInfo(String permission) {
+        ActivityInfo aInfo = new ActivityInfo();
+        aInfo.packageName = mContext.getPackageName();
+        aInfo.permission = permission;
+        ResolveInfo rInfo = new ResolveInfo();
+        rInfo.activityInfo = aInfo;
+        return rInfo;
+    }
+
+    private class BlockingBroadcastReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private Intent mIntent;
+
+        BlockingBroadcastReceiver(String action) {
+            mContext.registerReceiver(this, new IntentFilter(action));
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mIntent = intent;
+            mLatch.countDown();
+        }
+
+        public Intent blockingGetIntent() throws InterruptedException {
+            mLatch.await(5, TimeUnit.SECONDS);
+            mContext.unregisterReceiver(this);
+            return mIntent;
+        }
+    }
+}
diff --git a/compat/tests/java/android/support/v4/content/res/FontResourcesParserCompatTest.java b/compat/tests/java/android/support/v4/content/res/FontResourcesParserCompatTest.java
new file mode 100644
index 0000000..e7f40cf
--- /dev/null
+++ b/compat/tests/java/android/support/v4/content/res/FontResourcesParserCompatTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.content.res;
+
+import static android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry;
+import static android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
+import static android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
+import static android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.SuppressLint;
+import android.app.Instrumentation;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.support.compat.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.provider.FontRequest;
+import android.util.Base64;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Tests for {@link FontResourcesParserCompat}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontResourcesParserCompatTest {
+
+    private Instrumentation mInstrumentation;
+    private Resources mResources;
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mResources = mInstrumentation.getContext().getResources();
+    }
+
+    @Test
+    public void testParse() throws XmlPullParserException, IOException {
+        @SuppressLint("ResourceType")
+        XmlResourceParser parser = mResources.getXml(R.font.samplexmlfont);
+
+        FamilyResourceEntry result = FontResourcesParserCompat.parse(parser, mResources);
+
+        assertNotNull(result);
+        FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) result;
+        FontFileResourceEntry[] fileEntries = filesEntry.getEntries();
+        assertEquals(4, fileEntries.length);
+        FontFileResourceEntry font1 = fileEntries[0];
+        assertEquals(400, font1.getWeight());
+        assertEquals(false, font1.isItalic());
+        assertEquals(R.font.samplefont, font1.getResourceId());
+        FontFileResourceEntry font2 = fileEntries[1];
+        assertEquals(400, font2.getWeight());
+        assertEquals(true, font2.isItalic());
+        assertEquals(R.font.samplefont2, font2.getResourceId());
+        FontFileResourceEntry font3 = fileEntries[2];
+        assertEquals(700, font3.getWeight());
+        assertEquals(false, font3.isItalic());
+        assertEquals(R.font.samplefont3, font3.getResourceId());
+        FontFileResourceEntry font4 = fileEntries[3];
+        assertEquals(700, font4.getWeight());
+        assertEquals(true, font4.isItalic());
+        assertEquals(R.font.samplefont4, font4.getResourceId());
+    }
+
+    @Test
+    public void testParseDownloadableFont() throws IOException, XmlPullParserException {
+        @SuppressLint("ResourceType")
+        XmlResourceParser parser = mResources.getXml(R.font.samplexmldownloadedfont);
+
+        FamilyResourceEntry result = FontResourcesParserCompat.parse(parser, mResources);
+
+        assertNotNull(result);
+        ProviderResourceEntry providerEntry = (ProviderResourceEntry) result;
+        FontRequest request = providerEntry.getRequest();
+        assertEquals("com.example.test.fontprovider.authority",
+                request.getProviderAuthority());
+        assertEquals("com.example.test.fontprovider.package", request.getProviderPackage());
+        assertEquals("MyRequestedFont", request.getQuery());
+    }
+
+    @Test
+    public void testReadCertsSingleArray() {
+        List<List<byte[]>> result = FontResourcesParserCompat.readCerts(mResources, R.array.certs1);
+
+        assertEquals(1, result.size());
+        List<byte[]> firstSet = result.get(0);
+        assertEquals(2, firstSet.size());
+        String firstValue = Base64.encodeToString(firstSet.get(0), Base64.DEFAULT).trim();
+        assertEquals("MIIEqDCCA5CgAwIBAgIJANWFuGx9", firstValue);
+        String secondValue = Base64.encodeToString(firstSet.get(1), Base64.DEFAULT).trim();
+        assertEquals("UEChMHQW5kcm9pZDEQMA4GA=", secondValue);
+    }
+
+    @Test
+    public void testReadCertsMultiArray() {
+        List<List<byte[]>> result =
+                FontResourcesParserCompat.readCerts(mResources, R.array.certarray);
+
+        assertEquals(2, result.size());
+        List<byte[]> firstSet = result.get(0);
+        assertEquals(2, firstSet.size());
+        String firstValue = Base64.encodeToString(firstSet.get(0), Base64.DEFAULT).trim();
+        assertEquals("MIIEqDCCA5CgAwIBAgIJANWFuGx9", firstValue);
+        String secondValue = Base64.encodeToString(firstSet.get(1), Base64.DEFAULT).trim();
+        assertEquals("UEChMHQW5kcm9pZDEQMA4GA=", secondValue);
+        List<byte[]> secondSet = result.get(1);
+        assertEquals(2, secondSet.size());
+        String thirdValue = Base64.encodeToString(secondSet.get(0), Base64.DEFAULT).trim();
+        assertEquals("MDEyMzM2NTZaMIGUMQswCQYD", thirdValue);
+        String fourthValue = Base64.encodeToString(secondSet.get(1), Base64.DEFAULT).trim();
+        assertEquals("DHThvbbR24kT9ixcOd9W+EY=", fourthValue);
+    }
+}
diff --git a/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java b/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
index e1180a0..56f5ab4 100644
--- a/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
+++ b/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
@@ -16,9 +16,15 @@
 package android.support.v4.content.res;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
 
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.compat.test.R;
@@ -32,11 +38,13 @@
 
 @SmallTest
 public class ResourcesCompatTest {
+    private Context mContext;
     private Resources mResources;
 
     @Before
     public void setup() {
-        mResources = InstrumentationRegistry.getContext().getResources();
+        mContext = InstrumentationRegistry.getContext();
+        mResources = mContext.getResources();
     }
 
     @Test
@@ -278,4 +286,57 @@
         TestUtils.assertAllPixelsOfColor("Themed lilac density-aware drawable load : xxhigh color",
                 themedLilacDrawableForXXHighDensity, 0xFFF080F0);
     }
+
+    @Test(expected = Resources.NotFoundException.class)
+    public void testGetFont_invalidResourceId() {
+        ResourcesCompat.getFont(mContext, -1);
+    }
+
+    @Test
+    public void testGetFont_fontFile() {
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplefont);
+
+        assertNotNull(font);
+        assertNotSame(Typeface.DEFAULT, font);
+    }
+
+    @Test
+    public void testGetFont_xmlFile() {
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplexmlfont);
+
+        assertNotNull(font);
+        assertNotSame(Typeface.DEFAULT, font);
+    }
+
+    @Test
+    public void testGetFont_invalidXmlFile() {
+        try {
+            assertNull(
+                    ResourcesCompat.getFont(mContext, R.font.invalid_xmlfamily));
+        } catch (Resources.NotFoundException e) {
+            // pass
+        }
+
+        try {
+            assertNull(ResourcesCompat.getFont(mContext, R.font.invalid_xmlempty));
+        } catch (Resources.NotFoundException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void testGetFont_fontFileIsCached() {
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplefont);
+        Typeface font2 = ResourcesCompat.getFont(mContext, R.font.samplefont);
+
+        assertSame(font, font2);
+    }
+
+    @Test
+    public void testGetFont_xmlFileIsCached() {
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplexmlfont);
+        Typeface font2 = ResourcesCompat.getFont(mContext, R.font.samplexmlfont);
+
+        assertSame(font, font2);
+    }
 }
diff --git a/compat/tests/java/android/support/v4/graphics/PaintCompatHasGlyphTest.java b/compat/tests/java/android/support/v4/graphics/PaintCompatHasGlyphTest.java
index 26f0691..f17c881 100644
--- a/compat/tests/java/android/support/v4/graphics/PaintCompatHasGlyphTest.java
+++ b/compat/tests/java/android/support/v4/graphics/PaintCompatHasGlyphTest.java
@@ -47,6 +47,7 @@
                 {"\t\t\t", false},  // more white space
                 {"☺", SDK_INT >= 16}, // glyph added in API 16
                 {"\uD83D\uDC66\uD83C\uDFFF", SDK_INT >= 24}, // glyph added in API 24
+                {"\uD83C\uDDEF\uD83C\uDDF5", SDK_INT >= 20}, // Japan flag emoji, added in API 20
         });
     }
 
diff --git a/compat/tests/java/android/support/v4/graphics/TypefaceCompatTest.java b/compat/tests/java/android/support/v4/graphics/TypefaceCompatTest.java
new file mode 100644
index 0000000..dab7f0f
--- /dev/null
+++ b/compat/tests/java/android/support/v4/graphics/TypefaceCompatTest.java
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.SuppressLint;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.compat.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.testutils.PollingCheck;
+import android.support.v4.content.res.FontResourcesParserCompat;
+import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry;
+import android.support.v4.provider.FontRequest;
+import android.support.v4.provider.MockFontProvider;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+public class TypefaceCompatTest {
+
+    public Context mContext;
+    public Resources mResources;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mResources = mContext.getResources();
+        MockFontProvider.prepareFontFiles(mContext);
+    }
+
+    @After
+    public void tearDown() {
+        MockFontProvider.cleanUpFontFiles(mContext);
+    }
+
+    // Signature to be used for authentication to access content provider.
+    // In this test case, the content provider and consumer live in the same package, self package's
+    // signature works.
+    private static final List<List<byte[]>> SIGNATURE;
+    static {
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        try {
+            PackageManager manager = context.getPackageManager();
+            PackageInfo info = manager.getPackageInfo(
+                    context.getPackageName(), PackageManager.GET_SIGNATURES);
+            ArrayList<byte[]> out = new ArrayList<>();
+            for (Signature sig : info.signatures) {
+                out.add(sig.toByteArray());
+            }
+            SIGNATURE = new ArrayList<>();
+            SIGNATURE.add(out);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Helper method to get the used font resource id by typeface.
+     *
+     * If the typeface is created from one of the R.font.large_a, R.font.large_b, R.font.large_c or
+     * R.font.large_d resource, this method returns the resource id used by the typeface.
+     */
+    private static int getSelectedFontResourceId(Typeface typeface) {
+        // The glyph for "a" in R.font.large_a font has a 3em width and glyph for "b", "c" and "d"
+        // have 1em width. Similarly, The glyph for "b" in R.font.large_b font, the glyph for "c"
+        // in R.font.large_c font, the glyph for "d" in R.font.large_d font has 3em width and the
+        // glyph for the rest characters have 1em. Thus we can get the resource id of the source
+        // font file by comparing width of "a", "b", "c" and "d".
+        Paint p = new Paint();
+        p.setTypeface(typeface);
+        final int[] ids = { R.font.large_a, R.font.large_b, R.font.large_c, R.font.large_d };
+        final float[] widths = {
+            p.measureText("a"), p.measureText("b"), p.measureText("c"), p.measureText("d")
+        };
+
+        int maxIndex = Integer.MIN_VALUE;
+        float maxValue = Float.MIN_VALUE;
+        for (int i = 0; i < widths.length; ++i) {
+            if (maxValue < widths[i]) {
+                maxIndex = i;
+                maxValue = widths[i];
+            }
+        }
+        return ids[maxIndex];
+    }
+
+    /**
+     * Helper method to obtain ProviderResourceEntry with overwriting correct signatures.
+     */
+    private ProviderResourceEntry getProviderResourceEntry(int id) {
+        final ProviderResourceEntry entry;
+        try {
+            entry = (ProviderResourceEntry) FontResourcesParserCompat.parse(
+                    mResources.getXml(id), mResources);
+        } catch (XmlPullParserException | IOException e) {
+            throw new RuntimeException(e);
+        }
+        final FontRequest parsedRequest = entry.getRequest();
+        final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(),
+                parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE);
+        return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout());
+    }
+
+    @Test
+    public void testCreateFromResourcesFamilyXml_resourceFont_syncloading() throws Exception {
+        Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.NORMAL, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.NORMAL);
+        assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.ITALIC, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.ITALIC);
+        assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.BOLD, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.BOLD);
+        assertEquals(R.font.large_c, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.BOLD_ITALIC, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC);
+        assertEquals(R.font.large_d, getSelectedFontResourceId(typeface));
+    }
+
+    @Test
+    public void testCreateFromResourcesFamilyXml_resourceFont_asyncloading() throws Exception {
+        Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+        final TextView textView = new TextView(mContext);
+        PollingCheck.PollingCheckCondition condition = new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return textView.getTypeface() != null;
+            }
+        };
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.NORMAL, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_a, getSelectedFontResourceId(textView.getTypeface()));
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.ITALIC, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_b, getSelectedFontResourceId(textView.getTypeface()));
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.BOLD, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_c, getSelectedFontResourceId(textView.getTypeface()));
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.BOLD_ITALIC, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_d, getSelectedFontResourceId(textView.getTypeface()));
+    }
+
+    @Test
+    public void testCreateFromResourcesFamilyXml_resourceFont() throws Exception {
+        @SuppressLint("ResourceType")
+        // We are retrieving the XML font as an XML resource for testing purposes.
+        final FamilyResourceEntry entry = FontResourcesParserCompat.parse(
+                mResources.getXml(R.font.styletestfont), mResources);
+        Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.NORMAL, null /* text view */);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.NORMAL));
+        typeface = Typeface.create(typeface, Typeface.NORMAL);
+        // styletestfont has a node of fontStyle="normal" fontWeight="400" font="@font/large_a".
+        assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.ITALIC, null);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.ITALIC));
+        typeface = Typeface.create(typeface, Typeface.ITALIC);
+        // styletestfont has a node of fontStyle="italic" fontWeight="400" font="@font/large_b".
+        assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.BOLD, null);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.BOLD));
+        typeface = Typeface.create(typeface, Typeface.BOLD);
+        // styletestfont has a node of fontStyle="normal" fontWeight="700" font="@font/large_c".
+        assertEquals(R.font.large_c, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.BOLD_ITALIC, null);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.BOLD_ITALIC));
+        typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC);
+        // styletestfont has a node of fontStyle="italic" fontWeight="700" font="@font/large_d".
+        assertEquals(R.font.large_d, getSelectedFontResourceId(typeface));
+    }
+
+    @Test
+    public void testCreateFromResourcesFontFile() {
+        Typeface typeface = TypefaceCompat.createFromResourcesFontFile(mContext, mResources,
+                R.font.large_a, "res/font/large_a.ttf", Typeface.NORMAL);
+        assertNotNull(typeface);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.large_a, Typeface.NORMAL));
+        assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFontFile(mContext, mResources, R.font.large_b,
+                "res/font/large_b.ttf", Typeface.NORMAL);
+        assertNotNull(typeface);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.large_b, Typeface.NORMAL));
+        assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
+    }
+}
diff --git a/compat/tests/java/android/support/v4/graphics/drawable/IconCompatTest.java b/compat/tests/java/android/support/v4/graphics/drawable/IconCompatTest.java
new file mode 100644
index 0000000..d87ddac
--- /dev/null
+++ b/compat/tests/java/android/support/v4/graphics/drawable/IconCompatTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.graphics.drawable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.compat.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IconCompatTest {
+
+    private void verifyClippedCircle(Bitmap bitmap, int fillColor, int size) {
+        assertEquals(size, bitmap.getHeight());
+        assertEquals(bitmap.getWidth(), bitmap.getHeight());
+        assertEquals(fillColor, bitmap.getPixel(size / 2, size / 2));
+
+        assertEquals(Color.TRANSPARENT, bitmap.getPixel(0, 0));
+        assertEquals(Color.TRANSPARENT, bitmap.getPixel(0, size - 1));
+        assertEquals(Color.TRANSPARENT, bitmap.getPixel(size - 1, 0));
+        assertEquals(Color.TRANSPARENT, bitmap.getPixel(size - 1, size - 1));
+    }
+
+    @Test
+    public void testClipAdaptiveIcon() throws Throwable {
+        Bitmap source = Bitmap.createBitmap(200, 150, Bitmap.Config.ARGB_8888);
+        source.eraseColor(Color.RED);
+        Bitmap result = IconCompat.createLegacyIconFromAdaptiveIcon(source);
+        verifyClippedCircle(result, Color.RED, 100);
+    }
+
+    @Test
+    public void testCreateWithBitmap_legacy() {
+        Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(Color.RED);
+        Intent intent = new Intent();
+        IconCompat.createWithBitmap(bitmap).addToShortcutIntent(intent);
+        assertEquals(bitmap, intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @TargetApi(Build.VERSION_CODES.M)
+    public void testCreateWithBitmap() {
+        Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(Color.RED);
+        IconCompat compat = IconCompat.createWithBitmap(bitmap);
+        Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext());
+        assertTrue(d instanceof BitmapDrawable);
+        assertEquals(bitmap, ((BitmapDrawable) d).getBitmap());
+    }
+
+    @Test
+    public void testCreateWithAdaptiveBitmap_legacy() {
+        Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(Color.GREEN);
+        Intent intent = new Intent();
+        IconCompat.createWithAdaptiveBitmap(bitmap).addToShortcutIntent(intent);
+
+        Bitmap clipped = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+        verifyClippedCircle(clipped, Color.GREEN, clipped.getWidth());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @TargetApi(Build.VERSION_CODES.O)
+    public void testCreateWithAdaptiveBitmap() {
+        Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(Color.GREEN);
+        IconCompat compat = IconCompat.createWithAdaptiveBitmap(bitmap);
+        Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext());
+        if (Build.VERSION.SDK_INT >= 26) {
+            assertTrue(d instanceof AdaptiveIconDrawable);
+        } else {
+            assertTrue(d instanceof BitmapDrawable);
+            Bitmap clipped = ((BitmapDrawable) d).getBitmap();
+            verifyClippedCircle(clipped, Color.GREEN, clipped.getWidth());
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @TargetApi(Build.VERSION_CODES.M)
+    public void testCreateWithData() {
+        Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(Color.YELLOW);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+        byte[] bytes = out.toByteArray();
+
+        byte[] resultCopy = Arrays.copyOf(bytes, bytes.length + 100);
+        // Shift all elements by 20
+        for (int i = bytes.length - 1; i >= 0; i--) {
+            resultCopy[i + 20] = resultCopy[i];
+        }
+
+        IconCompat compat = IconCompat.createWithData(resultCopy, 20, bytes.length);
+        Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext());
+        assertTrue(d instanceof BitmapDrawable);
+        assertTrue(bitmap.sameAs(((BitmapDrawable) d).getBitmap()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @TargetApi(Build.VERSION_CODES.M)
+    public void testCreateWithResource() {
+        Context context = InstrumentationRegistry.getContext();
+        Drawable original = context.getDrawable(R.drawable.test_drawable_red);
+
+        IconCompat compat = IconCompat.createWithResource(context, R.drawable.test_drawable_red);
+        Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext());
+
+        // Drawables are same classes
+        assertEquals(original.getClass(), d.getClass());
+
+        Bitmap orgBitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        original.setBounds(0, 0, 200, 200);
+        original.draw(new Canvas(orgBitmap));
+
+        Bitmap compatBitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+        d.setBounds(0, 0, 200, 200);
+        d.draw(new Canvas(compatBitmap));
+
+        // Drawables behave the same
+        assertTrue(orgBitmap.sameAs(compatBitmap));
+    }
+}
diff --git a/compat/tests/java/android/support/v4/os/LocaleListCompatTest.java b/compat/tests/java/android/support/v4/os/LocaleListCompatTest.java
new file mode 100644
index 0000000..a97a033
--- /dev/null
+++ b/compat/tests/java/android/support/v4/os/LocaleListCompatTest.java
@@ -0,0 +1,419 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.os;
+
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertSame;
+import static junit.framework.TestCase.fail;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Tests for {@link LocaleListCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LocaleListCompatTest {
+
+    @Test
+    public void testEmptyLocaleList() {
+        LocaleListCompat ll = LocaleListCompat.create();
+        assertNotNull(ll);
+        assertTrue(ll.isEmpty());
+        assertEquals(0, ll.size());
+        assertNull(ll.get(0));
+        assertNull(ll.get(1));
+        assertNull(ll.get(10));
+
+        ll = LocaleListCompat.create(new Locale[0]);
+        assertNotNull(ll);
+        assertTrue(ll.isEmpty());
+        assertEquals(0, ll.size());
+        assertNull(ll.get(0));
+        assertNull(ll.get(1));
+        assertNull(ll.get(10));
+    }
+
+    @Test
+    public void testOneMemberLocaleList() {
+        final LocaleListCompat ll = LocaleListCompat.create(Locale.US);
+        assertNotNull(ll);
+        assertFalse(ll.isEmpty());
+        assertEquals(1, ll.size());
+        assertEquals(Locale.US, ll.get(0));
+        assertNull(ll.get(10));
+    }
+
+    @Test
+    public void testTwoMemberLocaleList() {
+        final Locale enPH = forLanguageTag("en-PH");
+        final Locale[] la = {enPH, Locale.US};
+        final LocaleListCompat ll = LocaleListCompat.create(la);
+        assertNotNull(ll);
+        assertFalse(ll.isEmpty());
+        assertEquals(2, ll.size());
+        assertEquals(enPH, ll.get(0));
+        assertEquals(Locale.US, ll.get(1));
+        assertNull(ll.get(10));
+    }
+
+    @Test
+    public void testNullArgument() {
+        try {
+            LocaleListCompat.create((Locale) null);
+            fail("Initializing a LocaleListCompat with a null argument should throw.");
+        } catch (Throwable e) {
+            assertEquals(NullPointerException.class, e.getClass());
+        }
+        try {
+            LocaleListCompat.create((Locale[]) null);
+            fail("Initializing a LocaleListCompat with a null array should throw.");
+        } catch (Throwable e) {
+            assertEquals(NullPointerException.class, e.getClass());
+        }
+    }
+
+    @Test
+    public void testNullArguments() {
+        final Locale[] la = {Locale.US, null};
+        try {
+            LocaleListCompat.create(la);
+            fail("Initializing a LocaleListCompat with an array containing null should throw.");
+        } catch (Throwable e) {
+            assertEquals(NullPointerException.class, e.getClass());
+        }
+    }
+
+    @Test
+    public void testRepeatedArguments() {
+        final Locale[] la = {Locale.US, Locale.US};
+        try {
+            LocaleListCompat.create(la);
+            fail("Initializing a LocaleListCompat with an array containing duplicates should "
+                    + "throw.");
+        } catch (Throwable e) {
+            assertEquals(IllegalArgumentException.class, e.getClass());
+        }
+    }
+
+    @Test
+    public void testIndexOf() {
+        final LocaleListCompat empty = LocaleListCompat.create();
+        assertEquals(-1, empty.indexOf(Locale.US));
+
+        final LocaleListCompat oneMember = LocaleListCompat.create(Locale.US);
+        assertEquals(0, oneMember.indexOf(Locale.US));
+        assertEquals(-1, oneMember.indexOf(Locale.ENGLISH));
+
+        final LocaleListCompat twoMember = LocaleListCompat.forLanguageTags("en,fr");
+        assertEquals(0, twoMember.indexOf(forLanguageTag("en")));
+        assertEquals(1, twoMember.indexOf(forLanguageTag("fr")));
+        assertEquals(-1, twoMember.indexOf(forLanguageTag("en-US")));
+    }
+
+    @Test
+    public void testToString() {
+        LocaleListCompat ll = LocaleListCompat.create();
+        assertEquals("[]", ll.toString());
+
+        final Locale[] la1 = {Locale.US};
+        ll = LocaleListCompat.create(la1);
+        assertEquals("[" + Locale.US.toString() + "]", ll.toString());
+
+        final Locale[] la2 = {Locale.US, Locale.FRENCH};
+        ll = LocaleListCompat.create(la2);
+        assertEquals("[" + Locale.US.toString() + "," + Locale.FRENCH.toString() + "]",
+                ll.toString());
+    }
+
+    @Test
+    public void testToLanguageTags() {
+        LocaleListCompat ll = LocaleListCompat.create();
+        assertEquals("", ll.toLanguageTags());
+
+        final Locale[] la1 = {Locale.US};
+        ll = LocaleListCompat.create(la1);
+        assertEquals(toLanguageTag(Locale.US), ll.toLanguageTags());
+
+        final Locale[] la2 = {Locale.US, Locale.FRENCH};
+        ll = LocaleListCompat.create(la2);
+        assertEquals(toLanguageTag(Locale.US) + "," + toLanguageTag(Locale.FRENCH),
+                ll.toLanguageTags());
+    }
+
+    @Test
+    public void testGetEmptyLocaleList() {
+        LocaleListCompat empty = LocaleListCompat.getEmptyLocaleList();
+        LocaleListCompat anotherEmpty = LocaleListCompat.getEmptyLocaleList();
+        LocaleListCompat constructedEmpty = LocaleListCompat.create();
+
+        assertEquals(constructedEmpty, empty);
+        assertSame(empty, anotherEmpty);
+    }
+
+    @Test
+    public void testForLanguageTags() {
+        assertEquals(LocaleListCompat.getEmptyLocaleList(), LocaleListCompat.forLanguageTags(null));
+        assertEquals(LocaleListCompat.getEmptyLocaleList(), LocaleListCompat.forLanguageTags(""));
+
+        assertEquals(LocaleListCompat.create(forLanguageTag("en-US")),
+                LocaleListCompat.forLanguageTags("en-US"));
+
+        final Locale[] la = {forLanguageTag("en-PH"), forLanguageTag("en-US")};
+        assertEquals(LocaleListCompat.create(la),
+                LocaleListCompat.forLanguageTags("en-PH,en-US"));
+    }
+
+    @Test
+    public void testGetDefault() {
+        final LocaleListCompat ll = LocaleListCompat.getDefault();
+        assertNotNull(ll);
+        assertTrue(ll.size() >= 1);
+
+        final Locale defaultLocale = Locale.getDefault();
+        assertTrue(ll.indexOf(defaultLocale) != -1);
+    }
+
+    @Test
+    public void testGetAdjustedDefault() {
+        final LocaleListCompat ll = LocaleListCompat.getDefault();
+        assertNotNull(ll);
+        assertTrue(ll.size() >= 1);
+
+        final Locale defaultLocale = Locale.getDefault();
+        assertTrue(ll.indexOf(defaultLocale) == 0);
+    }
+
+    @Test
+    public void testGetFirstMatch_noAssets() {
+        String[] noAssets = {};
+        assertNull(LocaleListCompat.getEmptyLocaleList().getFirstMatch(noAssets));
+        assertEquals(
+                forLanguageTag("fr-BE"),
+                LocaleListCompat.forLanguageTags("fr-BE").getFirstMatch(noAssets));
+        assertEquals(
+                forLanguageTag("fr-BE"),
+                LocaleListCompat.forLanguageTags("fr-BE,nl-BE").getFirstMatch(noAssets));
+    }
+
+    @Test
+    public void testGetFirstMatch_oneAsset() {
+        String[] oneDutchAsset = {"nl"};
+        assertEquals(
+                forLanguageTag("fr-BE"),
+                LocaleListCompat.forLanguageTags("fr-BE").getFirstMatch(oneDutchAsset));
+        assertEquals(
+                forLanguageTag("nl-BE"),
+                LocaleListCompat.forLanguageTags("nl-BE").getFirstMatch(oneDutchAsset));
+        assertEquals(
+                forLanguageTag("nl-BE"),
+                LocaleListCompat.forLanguageTags("fr-BE,nl-BE").getFirstMatch(oneDutchAsset));
+        assertEquals(
+                forLanguageTag("nl-BE"),
+                LocaleListCompat.forLanguageTags("nl-BE,fr-BE").getFirstMatch(oneDutchAsset));
+    }
+
+    @Test
+    public void testGetFirstMatch_twoAssets() {
+        String[] FrenchAndDutchAssets = {"fr", "nl"};
+        assertEquals(
+                forLanguageTag("fr-BE"),
+                LocaleListCompat.forLanguageTags("fr-BE").getFirstMatch(FrenchAndDutchAssets));
+        assertEquals(
+                forLanguageTag("nl-BE"),
+                LocaleListCompat.forLanguageTags("nl-BE").getFirstMatch(FrenchAndDutchAssets));
+        assertEquals(
+                forLanguageTag("fr-BE"),
+                LocaleListCompat.forLanguageTags("fr-BE,nl-BE")
+                        .getFirstMatch(FrenchAndDutchAssets));
+        assertEquals(
+                forLanguageTag("nl-BE"),
+                LocaleListCompat.forLanguageTags("nl-BE,fr-BE")
+                        .getFirstMatch(FrenchAndDutchAssets));
+    }
+
+    @SdkSuppress(minSdkVersion = 24)
+    @TargetApi(24)
+    @Test
+    public void testGetFirstMatch_oneChineseAsset() {
+        String[] oneChineseAsset = {"zh-CN"};  // Assumed to mean zh-Hans-CN
+        // The following Chinese examples would all match, so they will be chosen.
+        assertEquals(
+                forLanguageTag("zh"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh").getFirstMatch(oneChineseAsset));
+        assertEquals(
+                forLanguageTag("zh-CN"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-CN").getFirstMatch(oneChineseAsset));
+        assertEquals(
+                forLanguageTag("zh-Hans"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-Hans").getFirstMatch(oneChineseAsset));
+        assertEquals(
+                forLanguageTag("zh-Hans-CN"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-Hans-CN")
+                        .getFirstMatch(oneChineseAsset));
+        assertEquals(
+                forLanguageTag("zh-Hans-HK"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-Hans-HK")
+                        .getFirstMatch(oneChineseAsset));
+
+        // The following Chinese examples wouldn't match, so the first locale will be chosen
+        // instead.
+        assertEquals(
+                forLanguageTag("ko-KR"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-TW").getFirstMatch(oneChineseAsset));
+        assertEquals(
+                forLanguageTag("ko-KR"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-Hant").getFirstMatch(oneChineseAsset));
+        assertEquals(
+                forLanguageTag("ko-KR"),
+                LocaleListCompat.forLanguageTags("ko-KR,zh-Hant-TW")
+                        .getFirstMatch(oneChineseAsset));
+    }
+
+    @SdkSuppress(minSdkVersion = 24)
+    @TargetApi(24)
+    @Test
+    public void testGetFirstMatch_serbianCyrillic() {
+        String[] oneSerbianAsset = {"sr"};  // Assumed to mean sr-Cyrl-RS
+        // The following Serbian examples would all match, so they will be chosen.
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr").getFirstMatch(oneSerbianAsset));
+        assertEquals(
+                forLanguageTag("sr-RS"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-RS").getFirstMatch(oneSerbianAsset));
+        assertEquals(
+                forLanguageTag("sr-Cyrl"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-Cyrl").getFirstMatch(oneSerbianAsset));
+        assertEquals(
+                forLanguageTag("sr-Cyrl-RS"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-Cyrl-RS")
+                        .getFirstMatch(oneSerbianAsset));
+        assertEquals(
+                forLanguageTag("sr-Cyrl-ME"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-Cyrl-ME")
+                        .getFirstMatch(oneSerbianAsset));
+
+        // The following Serbian examples wouldn't match, so the first locale will be chosen
+        // instead.
+        assertEquals(
+                forLanguageTag("hr-HR"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-ME").getFirstMatch(oneSerbianAsset));
+        assertEquals(
+                forLanguageTag("hr-HR"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-Latn").getFirstMatch(oneSerbianAsset));
+        assertEquals(
+                forLanguageTag("hr-HR"),
+                LocaleListCompat.forLanguageTags("hr-HR,sr-Latn-ME")
+                        .getFirstMatch(oneSerbianAsset));
+    }
+
+    @Test
+    public void testGetFirstMatch_LtrPseudoLocale() {
+        String[] onePseudoLocale = {"en-XA"};
+        // "en-XA" matches itself
+        assertEquals(
+                forLanguageTag("en-XA"),
+                LocaleListCompat.forLanguageTags("sr,en-XA").getFirstMatch(onePseudoLocale));
+
+        // "en-XA" doesn't match "en" or "en-US"
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("sr,en").getFirstMatch(onePseudoLocale));
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("sr,en-US").getFirstMatch(onePseudoLocale));
+    }
+
+    @Test
+    public void testGetFirstMatch_RtlPseudoLocale() {
+        String[] onePseudoLocale = {"ar-XB"};
+        // "ar-XB" matches itself
+        assertEquals(
+                forLanguageTag("ar-XB"),
+                LocaleListCompat.forLanguageTags("sr,ar-XB").getFirstMatch(onePseudoLocale));
+
+        // "ar-XB" doesn't match "ar" or "ar-EG"
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("sr,ar").getFirstMatch(onePseudoLocale));
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("sr,ar-EG").getFirstMatch(onePseudoLocale));
+    }
+
+    @Test
+    public void testGetFirstMatch_privateUseWithoutCountry() {
+        String[] onePrivateLocale = {"qaa"};
+        // "qaa" supports itself and "qaa-CA"
+        assertEquals(
+                forLanguageTag("qaa"),
+                LocaleListCompat.forLanguageTags("sr,qaa").getFirstMatch(onePrivateLocale));
+        assertEquals(
+                forLanguageTag("qaa-CA"),
+                LocaleListCompat.forLanguageTags("sr,qaa-CA").getFirstMatch(onePrivateLocale));
+    }
+
+    @Test
+    public void testGetFirstMatch_privateUseWithCountry() {
+        String[] onePrivateLocale = {"qaa-US"};
+        // "qaa-US" supports itself
+        assertEquals(
+                forLanguageTag("qaa-US"),
+                LocaleListCompat.forLanguageTags("sr,qaa-US").getFirstMatch(onePrivateLocale));
+
+        // "qaa-US" doesn't support "qaa" or "qaa-CA"
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("sr,qaa-CA").getFirstMatch(onePrivateLocale));
+        assertEquals(
+                forLanguageTag("sr"),
+                LocaleListCompat.forLanguageTags("sr,qaa").getFirstMatch(onePrivateLocale));
+    }
+
+    private Locale forLanguageTag(String str) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            return Locale.forLanguageTag(str);
+        } else {
+            return LocaleHelper.forLanguageTag(str);
+        }
+    }
+
+    private String toLanguageTag(Locale locale) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            return locale.toLanguageTag();
+        } else {
+            return LocaleHelper.toLanguageTag(locale);
+        }
+    }
+
+}
diff --git a/compat/tests/java/android/support/v4/provider/FontRequestTest.java b/compat/tests/java/android/support/v4/provider/FontRequestTest.java
new file mode 100644
index 0000000..4703459
--- /dev/null
+++ b/compat/tests/java/android/support/v4/provider/FontRequestTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.provider;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Base64;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link FontRequest}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontRequestTest {
+    private static final String PROVIDER = "com.test.fontprovider.authority";
+    private static final String QUERY = "query";
+    private static final String PACKAGE = "com.test.fontprovider.package";
+    private static final byte[] BYTE_ARRAY =
+            Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
+    private static final List<List<byte[]>> CERTS = Arrays.asList(Arrays.asList(BYTE_ARRAY));
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_nullAuthority() {
+        new FontRequest(null, PACKAGE, QUERY, CERTS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_nullPackage() {
+        new FontRequest(PROVIDER, null, QUERY, CERTS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_nullQuery() {
+        new FontRequest(PROVIDER, PACKAGE, null, CERTS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_nullCerts() {
+        new FontRequest(PROVIDER, PACKAGE, QUERY, null);
+    }
+}
diff --git a/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java b/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java
new file mode 100644
index 0000000..66bdd50
--- /dev/null
+++ b/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java
@@ -0,0 +1,416 @@
+/*
+ * 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.
+ */
+package android.support.v4.provider;
+
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_FONT_NOT_FOUND;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_FONT_UNAVAILABLE;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_MALFORMED_QUERY;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_OK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
+import android.content.pm.Signature;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.provider.FontsContractCompat.FontFamilyResult;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.util.Base64;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Unit tests for {@link FontsContractCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FontsContractCompatTest {
+    private static final String AUTHORITY = "android.support.provider.fonts.font";
+    private static final String PACKAGE = "android.support.compat.test";
+
+    // Signature to be used for authentication to access content provider.
+    // In this test case, the content provider and consumer live in the same package, self package's
+    // signature works.
+    private static final List<List<byte[]>> SIGNATURE;
+    static {
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        try {
+            PackageManager manager = context.getPackageManager();
+            PackageInfo info = manager.getPackageInfo(
+                    context.getPackageName(), PackageManager.GET_SIGNATURES);
+            ArrayList<byte[]> out = new ArrayList<>();
+            for (Signature sig : info.signatures) {
+                out.add(sig.toByteArray());
+            }
+            SIGNATURE = new ArrayList<>();
+            SIGNATURE.add(out);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static final byte[] BYTE_ARRAY =
+            Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
+    // Use a different instance to test byte array comparison
+    private static final byte[] BYTE_ARRAY_COPY =
+            Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
+    private static final byte[] BYTE_ARRAY_2 =
+            Base64.decode("e04fd020ea3a6910a2d808002b32", Base64.DEFAULT);
+    private Instrumentation mInstrumentation;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+        MockFontProvider.prepareFontFiles(
+                InstrumentationRegistry.getInstrumentation().getTargetContext());
+    }
+
+    @After
+    public void tearDown() {
+        MockFontProvider.cleanUpFontFiles(
+                InstrumentationRegistry.getInstrumentation().getTargetContext());
+    }
+
+    private static class TestCallback extends FontsContractCompat.FontRequestCallback {
+        private Typeface mTypeface;
+
+        private int mSuccessCallCount;
+        private int mFailedCallCount;
+
+        public void onTypefaceRetrieved(Typeface typeface) {
+            mTypeface = typeface;
+            mSuccessCallCount++;
+        }
+
+        public void onTypefaceRequestFailed(int reason) {
+            mFailedCallCount++;
+        }
+
+        public Typeface getTypeface() {
+            return mTypeface;
+        }
+
+        public int getSuccessCallCount() {
+            return mSuccessCallCount;
+        }
+
+        public int getFailedCallCount() {
+            return mFailedCallCount;
+        }
+    }
+
+    @Test
+    public void typefaceNotCacheTest() throws NameNotFoundException {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE);
+        FontFamilyResult result = FontsContractCompat.fetchFonts(
+                mContext, null /* cancellation signal */, request);
+        assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+        Typeface typeface = FontsContractCompat.buildTypeface(
+                mContext, null /* cancellation signal */, result.getFonts());
+
+        FontFamilyResult result2 = FontsContractCompat.fetchFonts(
+                mContext, null /* cancellation signal */, request);
+        assertEquals(FontFamilyResult.STATUS_OK, result2.getStatusCode());
+        Typeface typeface2 = FontsContractCompat.buildTypeface(
+                mContext, null /* cancellation signal */, result2.getFonts());
+
+        // Neither fetchFonts nor buildTypeface should cache the Typeface.
+        assertNotSame(typeface, typeface2);
+    }
+
+    @Test
+    public void testGetFontFromProvider_resultOK() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.SINGLE_FONT_FAMILY2_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+        assertNotNull(fonts);
+        assertEquals(1, fonts.length);
+        FontInfo font = fonts[0];
+        assertEquals(0, font.getTtcIndex());
+        assertEquals(700, font.getWeight());
+        assertTrue(font.isItalic());
+        assertNotNull(font.getUri());
+        assertEquals(RESULT_CODE_OK, font.getResultCode());
+    }
+
+    @Test
+    public void testGetFontFromProvider_providerDoesntReturnAllFields() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.MANDATORY_FIELDS_ONLY_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+        assertNotNull(fonts);
+        assertEquals(1, fonts.length);
+        FontInfo font = fonts[0];
+        assertEquals(0, font.getTtcIndex());
+        assertEquals(RESULT_CODE_OK, font.getResultCode());
+    }
+
+    @Test
+    public void testGetFontFromProvider_resultFontNotFound() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.NOT_FOUND_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+        assertNotNull(fonts);
+        assertEquals(1, fonts.length);
+        FontInfo font = fonts[0];
+        assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode());
+    }
+
+    @Test
+    public void testGetFontFromProvider_resultFontUnavailable() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.UNAVAILABLE_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+
+        assertNotNull(fonts);
+        assertEquals(1, fonts.length);
+        FontInfo font = fonts[0];
+        assertEquals(RESULT_CODE_FONT_UNAVAILABLE, font.getResultCode());
+    }
+
+    @Test
+    public void testGetFontFromProvider_resultMalformedQuery() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.MALFORMED_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+
+        assertNotNull(fonts);
+        assertEquals(1, fonts.length);
+        FontInfo font = fonts[0];
+        assertEquals(RESULT_CODE_MALFORMED_QUERY, font.getResultCode());
+    }
+
+    @Test
+    public void testGetFontFromProvider_resultFontNotFoundSecondRow() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.NOT_FOUND_SECOND_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+
+        assertNotNull(fonts);
+        assertEquals(2, fonts.length);
+
+        FontInfo font = fonts[0];
+        assertEquals(RESULT_CODE_OK, font.getResultCode());
+
+        font = fonts[1];
+        assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode());
+    }
+
+    @Test
+    public void testGetFontFromProvider_resultFontNotFoundOtherRow() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.NOT_FOUND_THIRD_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+
+        assertNotNull(fonts);
+        assertEquals(3, fonts.length);
+
+        FontInfo font = fonts[0];
+        assertEquals(RESULT_CODE_OK, font.getResultCode());
+
+        font = fonts[1];
+        assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode());
+
+        font = fonts[2];
+        assertEquals(RESULT_CODE_OK, font.getResultCode());
+    }
+
+    public void testGetFontFromProvider_resultCodeIsNegativeNumber() {
+        FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.NEGATIVE_ERROR_CODE_QUERY, SIGNATURE);
+        FontInfo[] fonts = FontsContractCompat.getFontFromProvider(
+                mContext, request, AUTHORITY, null);
+
+
+        assertNotNull(fonts);
+        assertEquals(1, fonts.length);
+        FontInfo font = fonts[0];
+        assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode());
+    }
+
+    @Test
+    public void testGetProvider_providerNotFound() {
+        PackageManager packageManager = mock(PackageManager.class);
+        when(packageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(null);
+
+        FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "query", SIGNATURE);
+        try {
+            FontsContractCompat.getProvider(packageManager, request, null);
+            fail();
+        } catch (NameNotFoundException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void testGetProvider_noCerts()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mContext.getPackageManager();
+
+        List<List<byte[]>> emptyList = Collections.emptyList();
+
+        FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "query", emptyList);
+        assertNull(FontsContractCompat.getProvider(packageManager, request, null));
+    }
+
+    @Test
+    public void testGetProvider_wrongCerts()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mock(PackageManager.class);
+        setupPackageManager(packageManager);
+
+        byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
+        List<byte[]> certList = Arrays.asList(wrongCert);
+        FontRequest requestWrongCerts = new FontRequest(
+                AUTHORITY, PACKAGE, "query", Arrays.asList(certList));
+
+        assertNull(FontsContractCompat.getProvider(packageManager, requestWrongCerts, null));
+    }
+
+    @Test
+    public void testGetProvider_correctCerts()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mock(PackageManager.class);
+        ProviderInfo info = setupPackageManager(packageManager);
+
+        List<byte[]> certList = Arrays.asList(BYTE_ARRAY);
+        FontRequest requestRightCerts = new FontRequest(
+                AUTHORITY, PACKAGE, "query", Arrays.asList(certList));
+        ProviderInfo result =
+                FontsContractCompat.getProvider(packageManager, requestRightCerts, null);
+
+        assertEquals(info, result);
+    }
+
+    @Test
+    public void testGetProvider_moreCerts()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mock(PackageManager.class);
+        setupPackageManager(packageManager);
+
+        byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
+        List<byte[]> certList = Arrays.asList(wrongCert, BYTE_ARRAY);
+        FontRequest requestRightCerts = new FontRequest(
+                AUTHORITY, PACKAGE, "query", Arrays.asList(certList));
+        assertNull(FontsContractCompat.getProvider(packageManager, requestRightCerts, null));
+    }
+
+    @Test
+    public void testGetProvider_duplicateCerts()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mock(PackageManager.class);
+        setupPackageManager(packageManager);
+        PackageInfo packageInfo = new PackageInfo();
+        Signature signature = mock(Signature.class);
+        when(signature.toByteArray()).thenReturn(BYTE_ARRAY_COPY);
+        Signature signature2 = mock(Signature.class);
+        when(signature2.toByteArray()).thenReturn(BYTE_ARRAY_COPY);
+        packageInfo.packageName = PACKAGE;
+        packageInfo.signatures = new Signature[] { signature, signature2 };
+        when(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
+
+        // The provider has {BYTE_ARRAY_COPY, BYTE_ARRAY_COPY}, the request has
+        // {BYTE_ARRAY_2, BYTE_ARRAY_COPY}.
+        List<byte[]> certList = Arrays.asList(BYTE_ARRAY_2, BYTE_ARRAY_COPY);
+        FontRequest requestRightCerts = new FontRequest(
+                AUTHORITY, PACKAGE, "query", Arrays.asList(certList));
+        assertNull(FontsContractCompat.getProvider(packageManager, requestRightCerts, null));
+    }
+
+    @Test
+    public void testGetProvider_correctCertsSeveralSets()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mock(PackageManager.class);
+        ProviderInfo info = setupPackageManager(packageManager);
+
+        List<List<byte[]>> certList = new ArrayList<>();
+        byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
+        certList.add(Arrays.asList(wrongCert));
+        certList.add(Arrays.asList(BYTE_ARRAY));
+        FontRequest requestRightCerts = new FontRequest(AUTHORITY, PACKAGE, "query", certList);
+        ProviderInfo result =
+                FontsContractCompat.getProvider(packageManager, requestRightCerts, null);
+
+        assertEquals(info, result);
+    }
+
+    @Test
+    public void testGetProvider_wrongPackage()
+            throws PackageManager.NameNotFoundException {
+        PackageManager packageManager = mContext.getPackageManager();
+
+        List<List<byte[]>> certList = new ArrayList<>();
+        certList.add(Arrays.asList(BYTE_ARRAY));
+        FontRequest requestRightCerts = new FontRequest(
+                AUTHORITY, "com.wrong.package.name", "query", certList);
+        try {
+            FontsContractCompat.getProvider(packageManager, requestRightCerts, null);
+            fail();
+        } catch (NameNotFoundException e) {
+            // pass
+        }
+    }
+
+    private ProviderInfo setupPackageManager(PackageManager packageManager)
+            throws PackageManager.NameNotFoundException {
+        ProviderInfo info = new ProviderInfo();
+        info.packageName = PACKAGE;
+        info.applicationInfo = new ApplicationInfo();
+        when(packageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info);
+        PackageInfo packageInfo = new PackageInfo();
+        Signature signature = mock(Signature.class);
+        when(signature.toByteArray()).thenReturn(BYTE_ARRAY_COPY);
+        packageInfo.packageName = PACKAGE;
+        packageInfo.signatures = new Signature[] { signature };
+        when(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
+        return info;
+    }
+}
diff --git a/compat/tests/java/android/support/v4/provider/MockFontProvider.java b/compat/tests/java/android/support/v4/provider/MockFontProvider.java
new file mode 100644
index 0000000..f07d92d
--- /dev/null
+++ b/compat/tests/java/android/support/v4/provider/MockFontProvider.java
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.support.v4.provider.FontsContractCompat.Columns;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a test Content Provider implementing {@link FontsContractCompat}.
+ */
+public class MockFontProvider extends ContentProvider {
+    static final String[] FONT_FILES = {
+            "samplefont.ttf", "large_a.ttf", "large_b.ttf", "large_c.ttf", "large_d.ttf"
+    };
+    private static final int SAMPLE_FONT_FILE_0_ID = 0;
+    private static final int LARGE_A_FILE_ID = 1;
+    private static final int LARGE_B_FILE_ID = 2;
+    private static final int LARGE_C_FILE_ID = 3;
+    private static final int LARGE_D_FILE_ID = 4;
+
+    static final String SINGLE_FONT_FAMILY_QUERY = "singleFontFamily";
+    static final String SINGLE_FONT_FAMILY2_QUERY = "singleFontFamily2";
+    static final String NOT_FOUND_QUERY = "notFound";
+    static final String UNAVAILABLE_QUERY = "unavailable";
+    static final String MALFORMED_QUERY = "malformed";
+    static final String NOT_FOUND_SECOND_QUERY = "notFoundSecond";
+    static final String NOT_FOUND_THIRD_QUERY = "notFoundThird";
+    static final String NEGATIVE_ERROR_CODE_QUERY = "negativeCode";
+    static final String MANDATORY_FIELDS_ONLY_QUERY = "mandatoryFields";
+    static final String STYLE_TEST_QUERY = "styleTest";
+
+    static class Font {
+        Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
+                int resultCode, boolean returnAllFields) {
+            mId = id;
+            mFileId = fileId;
+            mTtcIndex = ttcIndex;
+            mVarSettings = varSettings;
+            mWeight = weight;
+            mItalic = italic;
+            mResultCode = resultCode;
+            mReturnAllFields = returnAllFields;
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        public int getTtcIndex() {
+            return mTtcIndex;
+        }
+
+        public String getVarSettings() {
+            return mVarSettings;
+        }
+
+        public int getWeight() {
+            return mWeight;
+        }
+
+        public int getItalic() {
+            return mItalic;
+        }
+
+        public int getResultCode() {
+            return mResultCode;
+        }
+
+        public int getFileId() {
+            return mFileId;
+        }
+
+        public boolean isReturnAllFields() {
+            return mReturnAllFields;
+        }
+
+        private final int mId;
+        private final int mFileId;
+        private final int mTtcIndex;
+        private final String mVarSettings;
+        private final int mWeight;
+        private final int mItalic;
+        private final int mResultCode;
+        private final boolean mReturnAllFields;
+    };
+
+    private static final Map<String, Font[]> QUERY_MAP;
+    static {
+        HashMap<String, Font[]> map = new HashMap<>();
+        int id = 1;
+
+        map.put(SINGLE_FONT_FAMILY_QUERY, new Font[] {
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, "'wght' 100", 400, 0,
+                        Columns.RESULT_CODE_OK, true),
+        });
+
+        map.put(SINGLE_FONT_FAMILY2_QUERY, new Font[] {
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, "'wght' 100", 700, 1,
+                        Columns.RESULT_CODE_OK, true),
+        });
+
+        map.put(NOT_FOUND_QUERY, new Font[] {
+                new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
+        });
+
+        map.put(UNAVAILABLE_QUERY, new Font[] {
+                new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_UNAVAILABLE, true),
+        });
+
+        map.put(MALFORMED_QUERY, new Font[] {
+                new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_MALFORMED_QUERY, true),
+        });
+
+        map.put(NOT_FOUND_SECOND_QUERY, new Font[] {
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
+                        true),
+                new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
+        });
+
+        map.put(NOT_FOUND_THIRD_QUERY, new Font[] {
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
+                        true),
+                new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
+                        true),
+        });
+
+        map.put(NEGATIVE_ERROR_CODE_QUERY, new Font[] {
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, -5, true),
+        });
+
+        map.put(MANDATORY_FIELDS_ONLY_QUERY, new Font[] {
+                new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0,
+                        Columns.RESULT_CODE_OK, false),
+        });
+
+        map.put(STYLE_TEST_QUERY, new Font[] {
+                new Font(id++, LARGE_A_FILE_ID, 0, null, 400, 0 /* normal */,
+                        Columns.RESULT_CODE_OK, true),
+                new Font(id++, LARGE_B_FILE_ID, 0, null, 400, 1 /* italic */,
+                        Columns.RESULT_CODE_OK, true),
+                new Font(id++, LARGE_C_FILE_ID, 0, null, 700, 0 /* normal */,
+                        Columns.RESULT_CODE_OK, true),
+                new Font(id++, LARGE_D_FILE_ID, 0, null, 700, 1 /* italic */,
+                        Columns.RESULT_CODE_OK, true),
+        });
+
+        QUERY_MAP = Collections.unmodifiableMap(map);
+    }
+
+    private static Cursor buildCursor(Font[] in) {
+        if (!in[0].mReturnAllFields) {
+            MatrixCursor cursor = new MatrixCursor(new String[] { Columns._ID, Columns.FILE_ID });
+            for (Font font : in) {
+                cursor.addRow(new Object[] { font.getId(), font.getFileId() });
+            }
+            return cursor;
+        }
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.WEIGHT,
+                Columns.ITALIC, Columns.RESULT_CODE, Columns.FILE_ID});
+        for (Font font : in) {
+            cursor.addRow(
+                    new Object[] { font.getId(), font.getTtcIndex(), font.getVarSettings(),
+                    font.getWeight(), font.getItalic(), font.getResultCode(), font.getFileId() });
+        }
+        return cursor;
+    }
+
+    public static void prepareFontFiles(Context context) {
+        final AssetManager mgr = context.getAssets();
+        for (String file : FONT_FILES) {
+            InputStream is = null;
+            try {
+                is = mgr.open("fonts/" + file);
+                File copied = getCopiedFile(context, file);
+                File parent = copied.getParentFile();
+                if (!parent.isDirectory()) {
+                    parent.mkdirs();
+                    parent.setReadable(true, false);
+                    parent.setExecutable(true, false);
+                }
+                copy(is, copied);
+                copied.setReadable(true, false);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        // Do nothing.
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The caller is responsible for closing the given InputStream.
+     */
+    private static void copy(InputStream is, File file) throws IOException {
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(file, false);
+            byte[] buffer = new byte[1024];
+            int readLen;
+            while ((readLen = is.read(buffer)) != -1) {
+                fos.write(buffer, 0, readLen);
+            }
+        } finally {
+            if (fos != null) {
+                fos.close();
+            }
+        }
+    }
+
+    public static void cleanUpFontFiles(Context context) {
+        for (String file : FONT_FILES) {
+            getCopiedFile(context, file).delete();
+        }
+    }
+
+    public static File getCopiedFile(Context context, String path) {
+        final File cacheDir = new File(context.getFilesDir(), "fontCache");
+        return new File(cacheDir, path);
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) {
+        final int id = (int) ContentUris.parseId(uri);
+        final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]);
+        try {
+            return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(
+                    "Failed to found font file. You might forget call prepareFontFiles in setUp");
+        }
+    }
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "vnd.android.cursor.dir/vnd.android.provider.font";
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return buildCursor(QUERY_MAP.get(selectionArgs[0]));
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("insert is not supported.");
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("delete is not supported.");
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("update is not supported.");
+    }
+}
diff --git a/compat/tests/java/android/support/v4/provider/SelfDestructiveThreadTest.java b/compat/tests/java/android/support/v4/provider/SelfDestructiveThreadTest.java
new file mode 100644
index 0000000..84fd77f
--- /dev/null
+++ b/compat/tests/java/android/support/v4/provider/SelfDestructiveThreadTest.java
@@ -0,0 +1,274 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.provider;
+
+import static android.support.v4.provider.SelfDestructiveThread.ReplyCallback;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.os.Process;
+import android.support.annotation.GuardedBy;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Tests for {@link SelfDestructiveThread}
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class SelfDestructiveThreadTest {
+    private static final int DEFAULT_TIMEOUT = 1000;
+
+    private void waitUntilDestruction(SelfDestructiveThread thread, long timeoutMs) {
+        if (!thread.isRunning()) {
+            return;
+        }
+        final long deadlineNanoTime =
+                System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
+        final long timeSliceMs = 50;
+        do {
+            try {
+                Thread.sleep(timeSliceMs);
+            } catch (InterruptedException e) {
+                // ignore.
+            }
+            if (!thread.isRunning()) {
+                return;
+            }
+        } while (System.nanoTime() < deadlineNanoTime);
+        throw new RuntimeException("Timeout for waiting thread destruction.");
+    }
+
+    private void waitMillis(long ms) {
+        final long deadlineNanoTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(ms);
+        for (;;) {
+            final long now = System.nanoTime();
+            if (deadlineNanoTime < now) {
+                return;
+            }
+            try {
+                Thread.sleep(TimeUnit.NANOSECONDS.toMillis(deadlineNanoTime - now));
+            } catch (InterruptedException e) {
+                // ignore.
+            }
+        }
+    }
+
+    @Test
+    public void testDestruction() throws InterruptedException {
+        final int destructAfterLastActivityInMs = 300;
+        final SelfDestructiveThread thread = new SelfDestructiveThread(
+                "test", Process.THREAD_PRIORITY_BACKGROUND, destructAfterLastActivityInMs);
+        thread.postAndWait(new Callable<Object>() {
+            @Override
+            public Object call() throws Exception {
+                return null;
+            }
+        }, DEFAULT_TIMEOUT);
+        waitUntilDestruction(thread, DEFAULT_TIMEOUT);
+        assertFalse(thread.isRunning());
+    }
+
+    @Test
+    public void testReconstruction() throws InterruptedException {
+        final int destructAfterLastActivityInMs = 300;
+        final SelfDestructiveThread thread = new SelfDestructiveThread(
+                "test", Process.THREAD_PRIORITY_BACKGROUND, destructAfterLastActivityInMs);
+        Integer generation = thread.postAndWait(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return thread.getGeneration();
+            }
+        }, DEFAULT_TIMEOUT);
+        assertNotNull(generation);
+        waitUntilDestruction(thread, DEFAULT_TIMEOUT);
+        Integer nextGeneration = thread.postAndWait(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return thread.getGeneration();
+            }
+        }, DEFAULT_TIMEOUT);
+        assertNotNull(nextGeneration);
+        assertNotEquals(generation.intValue(), nextGeneration.intValue());
+    }
+
+    @Test
+    public void testReuseSameThread() throws InterruptedException {
+        final int destructAfterLastActivityInMs = 300;
+        final SelfDestructiveThread thread = new SelfDestructiveThread(
+                "test", Process.THREAD_PRIORITY_BACKGROUND, destructAfterLastActivityInMs);
+        Integer generation = thread.postAndWait(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return thread.getGeneration();
+            }
+        }, DEFAULT_TIMEOUT);
+        assertNotNull(generation);
+        Integer nextGeneration = thread.postAndWait(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return thread.getGeneration();
+            }
+        }, DEFAULT_TIMEOUT);
+        assertNotNull(nextGeneration);
+        waitUntilDestruction(thread, DEFAULT_TIMEOUT);
+        assertEquals(generation.intValue(), nextGeneration.intValue());
+    }
+
+    @Test
+    public void testReuseSameThread_Multiple() throws InterruptedException {
+        final int destructAfterLastActivityInMs = 300;
+        final SelfDestructiveThread thread = new SelfDestructiveThread(
+                "test", Process.THREAD_PRIORITY_BACKGROUND, destructAfterLastActivityInMs);
+        Integer generation = thread.postAndWait(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return thread.getGeneration();
+            }
+        }, DEFAULT_TIMEOUT);
+        assertNotNull(generation);
+        int firstGeneration = generation.intValue();
+        for (int i = 0; i < 10; ++i) {
+            // Less than renewal duration, so that the same thread must be used.
+            waitMillis(destructAfterLastActivityInMs / 2);
+            Integer nextGeneration = thread.postAndWait(new Callable<Integer>() {
+                @Override
+                public Integer call() throws Exception {
+                    return thread.getGeneration();
+                }
+            }, DEFAULT_TIMEOUT);
+            assertNotNull(nextGeneration);
+            assertEquals(firstGeneration, nextGeneration.intValue());
+        }
+        waitUntilDestruction(thread, DEFAULT_TIMEOUT);
+    }
+
+    @Test
+    public void testTimeout() {
+        final int destructAfterLastActivityInMs = 300;
+        final SelfDestructiveThread thread = new SelfDestructiveThread(
+                "test", Process.THREAD_PRIORITY_BACKGROUND, destructAfterLastActivityInMs);
+
+        final int timeoutMs = 300;
+        try {
+            thread.postAndWait(new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                    waitMillis(timeoutMs * 3);  // Wait longer than timeout.
+                    return new Object();
+                }
+            }, timeoutMs);
+            fail();
+        } catch (InterruptedException e) {
+             // pass
+        }
+    }
+
+    private class WaitableReplyCallback implements ReplyCallback<Integer> {
+        private final ReentrantLock mLock = new ReentrantLock();
+        private final Condition mCond = mLock.newCondition();
+
+        @GuardedBy("mLock")
+        private Integer mValue;
+
+        private static final int NOT_STARTED = 0;
+        private static final int WAITING = 1;
+        private static final int FINISHED = 2;
+        private static final int TIMEOUT = 3;
+        @GuardedBy("mLock")
+        int mState = NOT_STARTED;
+
+        @Override
+        public void onReply(Integer value) {
+            mLock.lock();
+            try {
+                if (mState != TIMEOUT) {
+                    mValue = value;
+                    mState = FINISHED;
+                }
+                mCond.signalAll();
+            } finally {
+                mLock.unlock();
+            }
+        }
+
+        public Integer waitUntil(long timeoutMillis) {
+            mLock.lock();
+            try {
+                if (mState == FINISHED) {
+                    return mValue;
+                }
+                mState = WAITING;
+                long remaining = TimeUnit.MILLISECONDS.toNanos(timeoutMillis);
+                while (mState == WAITING) {
+                    try {
+                        remaining = mCond.awaitNanos(remaining);
+                    } catch (InterruptedException e) {
+                        // Ignore.
+                    }
+                    if (mState == FINISHED) {
+                        return mValue;
+                    }
+                    if (remaining <= 0) {
+                        mState = TIMEOUT;
+                        fail("Timeout");
+                    }
+                }
+                throw new IllegalStateException("mState becomes unexpected state");
+            } finally {
+                mLock.unlock();
+            }
+        }
+    }
+
+    @Test
+    public void testPostAndReply() {
+        final int destructAfterLastActivityInMs = 300;
+        final Integer expectedResult = 123;
+
+        final Callable<Integer> callable = new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return expectedResult;
+            }
+        };
+        final WaitableReplyCallback reply = new WaitableReplyCallback();
+        final SelfDestructiveThread thread = new SelfDestructiveThread(
+                "test", Process.THREAD_PRIORITY_BACKGROUND, destructAfterLastActivityInMs);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                thread.postAndReply(callable, reply);
+            }
+        });
+
+        assertEquals(expectedResult, reply.waitUntil(DEFAULT_TIMEOUT));
+    }
+}
diff --git a/compat/tests/java/android/support/v4/testutils/TestUtils.java b/compat/tests/java/android/support/v4/testutils/TestUtils.java
index 0bb8e1f..70be082 100644
--- a/compat/tests/java/android/support/v4/testutils/TestUtils.java
+++ b/compat/tests/java/android/support/v4/testutils/TestUtils.java
@@ -17,9 +17,6 @@
 
 package android.support.v4.testutils;
 
-import java.lang.IllegalArgumentException;
-import java.lang.RuntimeException;
-
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -27,8 +24,6 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
 
 import junit.framework.Assert;
 
diff --git a/core-utils/tests/java/android/support/v4/text/BidiFormatterTest.java b/compat/tests/java/android/support/v4/text/BidiFormatterTest.java
similarity index 100%
rename from core-utils/tests/java/android/support/v4/text/BidiFormatterTest.java
rename to compat/tests/java/android/support/v4/text/BidiFormatterTest.java
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 775eb1d..d79c789 100644
--- a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
+++ b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
@@ -49,6 +49,7 @@
             "(test:)?[a-zA-Z0-9]+(\\.pattern)?");
 
     private MatchFilter mMatchFilterStartWithDot = new MatchFilter() {
+        @Override
         public final boolean acceptMatch(final CharSequence s, final int start, final int end) {
             if (start == 0) {
                 return true;
@@ -63,6 +64,7 @@
     };
 
     private TransformFilter mTransformFilterUpperChar = new TransformFilter() {
+        @Override
         public final String transformUrl(final Matcher match, String url) {
             StringBuilder buffer = new StringBuilder();
             String matchingRegion = match.group();
@@ -243,12 +245,21 @@
             assertTrue(LinkifyCompat.addLinks(spannable, Linkify.ALL));
             URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
             assertEquals(3, spans.length);
-            assertEquals("tel:8005551233", spans[0].getURL());
-            assertEquals("mailto:800-555-1211@gmail.com", spans[1].getURL());
-            assertEquals("http://800-555-1222.com", spans[2].getURL());
+            assertTrue(containsUrl(spans, "tel:8005551233"));
+            assertTrue(containsUrl(spans, "mailto:800-555-1211@gmail.com"));
+            assertTrue(containsUrl(spans, "http://800-555-1222.com"));
         }
     }
 
+    private boolean containsUrl(URLSpan[] spans, String expectedValue) {
+        for (URLSpan span : spans) {
+            if (span.getURL().equals(expectedValue)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Test
     public void testAddLinks_addsLinksWhenDefaultSchemeIsNull() {
         Spannable spannable = new SpannableString("any https://android.com any android.com any");
diff --git a/compat/tests/java/android/support/v4/util/ArrayMapCompatTest.java b/compat/tests/java/android/support/v4/util/ArrayMapCompatTest.java
new file mode 100644
index 0000000..c00d264
--- /dev/null
+++ b/compat/tests/java/android/support/v4/util/ArrayMapCompatTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package android.support.v4.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ArrayMapCompatTest {
+
+    @Test
+    public void testCanNotIteratePastEnd_entrySetIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList(
+                entryOf("key 1", "value 1"),
+                entryOf("key 2", "value 2")
+        ));
+        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+
+        // Assert iteration over the expected two entries in any order
+        assertTrue(iterator.hasNext());
+        Map.Entry<String, String> firstEntry = copyOf(iterator.next());
+        assertTrue(expectedEntriesToIterate.remove(firstEntry));
+
+        assertTrue(iterator.hasNext());
+        Map.Entry<String, String> secondEntry = copyOf(iterator.next());
+        assertTrue(expectedEntriesToIterate.remove(secondEntry));
+
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+
+    private static <K, V> Map.Entry<K, V> entryOf(K key, V value) {
+        return new AbstractMap.SimpleEntry<>(key, value);
+    }
+
+    private static <K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) {
+        return entryOf(entry.getKey(), entry.getValue());
+    }
+
+    @Test
+    public void testCanNotIteratePastEnd_keySetIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2"));
+        Iterator<String> iterator = map.keySet().iterator();
+
+        // Assert iteration over the expected two keys in any order
+        assertTrue(iterator.hasNext());
+        String firstKey = iterator.next();
+        assertTrue(expectedKeysToIterate.remove(firstKey));
+
+        assertTrue(iterator.hasNext());
+        String secondKey = iterator.next();
+        assertTrue(expectedKeysToIterate.remove(secondKey));
+
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+
+    @Test
+    public void testCanNotIteratePastEnd_valuesIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2"));
+        Iterator<String> iterator = map.values().iterator();
+
+        // Assert iteration over the expected two values in any order
+        assertTrue(iterator.hasNext());
+        String firstValue = iterator.next();
+        assertTrue(expectedValuesToIterate.remove(firstValue));
+
+        assertTrue(iterator.hasNext());
+        String secondValue = iterator.next();
+        assertTrue(expectedValuesToIterate.remove(secondValue));
+
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+}
diff --git a/compat/tests/java/android/support/v4/util/ArraySetCompatTest.java b/compat/tests/java/android/support/v4/util/ArraySetCompatTest.java
new file mode 100644
index 0000000..10a0b1b
--- /dev/null
+++ b/compat/tests/java/android/support/v4/util/ArraySetCompatTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+package android.support.v4.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ArraySetCompatTest {
+    @Test
+    public void testCanNotIteratePastEnd() {
+        ArraySet<String> set = new ArraySet<>();
+        set.add("value");
+        Iterator<String> iterator = set.iterator();
+
+        assertTrue(iterator.hasNext());
+        assertEquals("value", iterator.next());
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+}
diff --git a/compat/tests/java/android/support/v4/util/ObjectsCompatTest.java b/compat/tests/java/android/support/v4/util/ObjectsCompatTest.java
new file mode 100644
index 0000000..9fdf451
--- /dev/null
+++ b/compat/tests/java/android/support/v4/util/ObjectsCompatTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.util;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for ObjectsCompat
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ObjectsCompatTest {
+
+    @Test
+    public void testEquals() throws Exception {
+        String a = "aaa";
+        String b = "bbb";
+        String c = new String(a);
+        String n = null;
+
+        assertFalse(ObjectsCompat.equals(a, b));
+        assertFalse(ObjectsCompat.equals(a, n));
+        assertFalse(ObjectsCompat.equals(n, a));
+
+        assertTrue(ObjectsCompat.equals(n, n));
+        assertTrue(ObjectsCompat.equals(a, a));
+        assertTrue(ObjectsCompat.equals(a, c));
+    }
+
+}
diff --git a/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java b/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java
new file mode 100644
index 0000000..350b917
--- /dev/null
+++ b/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.util;
+
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ConcurrentModificationException;
+import java.util.Locale;
+
+/**
+ * Unit tests for SimpleArrayMap
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SimpleArrayMapTest {
+    private static final String TAG = "SimpleArrayMapTest";
+    SimpleArrayMap<String, String> map = new SimpleArrayMap<>();
+    private boolean mDone;
+
+    /**
+     * Attempt to generate a ConcurrentModificationException in ArrayMap.
+     */
+    @Test
+    public void testConcurrentModificationException() throws Exception {
+        final int TEST_LEN_MS = 5000;
+        Log.d(TAG, "Starting SimpleArrayMap concurrency test");
+        mDone = false;
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                int i = 0;
+                while (!mDone) {
+                    try {
+                        map.put(String.format(Locale.US, "key %d", i++), "B_DONT_DO_THAT");
+                    } catch (ArrayIndexOutOfBoundsException e) {
+                        // SimpleArrayMap is not thread safe, so lots of concurrent modifications
+                        // can still cause data corruption
+                        Log.w(TAG, "concurrent modification uncaught, causing indexing failure", e);
+                    } catch (ClassCastException e) {
+                        // cache corruption should not occur as it is hard to trace and one thread
+                        // may corrupt the pool for all threads in the same process.
+                        Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
+                        fail();
+                    } catch (ConcurrentModificationException e) {
+                    }
+                }
+            }
+        }).start();
+        for (int i = 0; i < (TEST_LEN_MS / 100); i++) {
+            try {
+                Thread.sleep(100);
+                map.clear();
+            } catch (InterruptedException e) {
+            } catch (ArrayIndexOutOfBoundsException e) {
+                Log.w(TAG, "concurrent modification uncaught, causing indexing failure");
+            } catch (ClassCastException e) {
+                Log.e(TAG, "concurrent modification uncaught, causing cache corruption");
+                fail();
+            } catch (ConcurrentModificationException e) {
+            }
+        }
+        mDone = true;
+    }
+
+    /**
+     * Check to make sure the same operations behave as expected in a single thread.
+     */
+    @Test
+    public void testNonConcurrentAccesses() throws Exception {
+        for (int i = 0; i < 100000; i++) {
+            try {
+                map.put(String.format(Locale.US, "key %d", i++), "B_DONT_DO_THAT");
+                if (i % 500 == 0) {
+                    map.clear();
+                }
+            } catch (ConcurrentModificationException e) {
+                Log.e(TAG, "concurrent modification caught on single thread", e);
+                fail();
+            }
+        }
+    }
+}
diff --git a/compat/tests/java/android/support/v4/view/PointerIconCompatTest.java b/compat/tests/java/android/support/v4/view/PointerIconCompatTest.java
new file mode 100644
index 0000000..901c7ea
--- /dev/null
+++ b/compat/tests/java/android/support/v4/view/PointerIconCompatTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+package android.support.v4.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.support.compat.test.R;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.PointerIcon;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+public class PointerIconCompatTest extends BaseInstrumentationTestCase<ViewCompatActivity> {
+
+    private View mView;
+    private Activity mActivity;
+
+    public PointerIconCompatTest() {
+        super(ViewCompatActivity.class);
+    }
+
+    @Before
+    public void setUp() {
+        mActivity = mActivityTestRule.getActivity();
+        mView = mActivity.findViewById(R.id.view);
+    }
+
+    private void compareSystemIcon(int type, int compatType) {
+        ViewCompat.setPointerIcon(mView, PointerIconCompat.getSystemIcon(mActivity, compatType));
+        assertEquals(PointerIcon.getSystemIcon(mActivity, type), mView.getPointerIcon());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSystemIcon() {
+        compareSystemIcon(PointerIcon.TYPE_ALIAS, PointerIconCompat.TYPE_ALIAS);
+        compareSystemIcon(PointerIcon.TYPE_ALL_SCROLL, PointerIconCompat.TYPE_ALL_SCROLL);
+        compareSystemIcon(PointerIcon.TYPE_ARROW, PointerIconCompat.TYPE_ARROW);
+        compareSystemIcon(PointerIcon.TYPE_CELL, PointerIconCompat.TYPE_CELL);
+        compareSystemIcon(PointerIcon.TYPE_CONTEXT_MENU, PointerIconCompat.TYPE_CONTEXT_MENU);
+        compareSystemIcon(PointerIcon.TYPE_COPY, PointerIconCompat.TYPE_COPY);
+        compareSystemIcon(PointerIcon.TYPE_CROSSHAIR, PointerIconCompat.TYPE_CROSSHAIR);
+        compareSystemIcon(PointerIcon.TYPE_DEFAULT, PointerIconCompat.TYPE_DEFAULT);
+        compareSystemIcon(PointerIcon.TYPE_GRAB, PointerIconCompat.TYPE_GRAB);
+        compareSystemIcon(PointerIcon.TYPE_GRABBING, PointerIconCompat.TYPE_GRABBING);
+        compareSystemIcon(PointerIcon.TYPE_HAND, PointerIconCompat.TYPE_HAND);
+        compareSystemIcon(PointerIcon.TYPE_HELP, PointerIconCompat.TYPE_HELP);
+        compareSystemIcon(PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW,
+                PointerIconCompat.TYPE_HORIZONTAL_DOUBLE_ARROW);
+        compareSystemIcon(PointerIcon.TYPE_NO_DROP, PointerIconCompat.TYPE_NO_DROP);
+        compareSystemIcon(PointerIcon.TYPE_NULL, PointerIconCompat.TYPE_NULL);
+        compareSystemIcon(PointerIcon.TYPE_TEXT, PointerIconCompat.TYPE_TEXT);
+        compareSystemIcon(PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW,
+                PointerIconCompat.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW);
+        compareSystemIcon(PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW,
+                PointerIconCompat.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW);
+        compareSystemIcon(PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW,
+                PointerIconCompat.TYPE_VERTICAL_DOUBLE_ARROW);
+        compareSystemIcon(PointerIcon.TYPE_VERTICAL_TEXT,
+                PointerIconCompat.TYPE_VERTICAL_TEXT);
+        compareSystemIcon(PointerIcon.TYPE_WAIT, PointerIconCompat.TYPE_WAIT);
+        compareSystemIcon(PointerIcon.TYPE_ZOOM_IN, PointerIconCompat.TYPE_ZOOM_IN);
+        compareSystemIcon(PointerIcon.TYPE_ZOOM_OUT, PointerIconCompat.TYPE_ZOOM_OUT);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testNullIcon() {
+        ViewCompat.setPointerIcon(mView, null);
+        assertNull(mView.getPointerIcon());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testBitmapIcon() {
+        Bitmap bitmap = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
+        ViewCompat.setPointerIcon(mView, PointerIconCompat.create(bitmap, 0, 0));
+        assertNotNull(mView.getPointerIcon());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testResourceIcon() {
+        ViewCompat.setPointerIcon(mView,
+                PointerIconCompat.load(mActivity.getResources(), R.drawable.pointer_icon));
+        assertNotNull(mView.getPointerIcon());
+    }
+}
diff --git a/compat/tests/java/android/support/v4/view/ViewCompatTest.java b/compat/tests/java/android/support/v4/view/ViewCompatTest.java
index f107fd5..730260d 100644
--- a/compat/tests/java/android/support/v4/view/ViewCompatTest.java
+++ b/compat/tests/java/android/support/v4/view/ViewCompatTest.java
@@ -69,4 +69,11 @@
         assertNull(display);
     }
 
+    @Test
+    public void testTransitionName() {
+        final View view = new View(mActivityTestRule.getActivity());
+        ViewCompat.setTransitionName(view, "abc");
+        assertEquals("abc", ViewCompat.getTransitionName(view));
+    }
+
 }
diff --git a/compat/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java b/compat/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java
deleted file mode 100644
index b1642f3..0000000
--- a/compat/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.widget;
-
-public class GingerbreadScrollerCompatTest extends ScrollerCompatTestBase {
-    public GingerbreadScrollerCompatTest() {
-        super(false);
-    }
-}
diff --git a/compat/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java b/compat/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java
deleted file mode 100644
index 1516d54..0000000
--- a/compat/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.widget;
-
-public class IcsScrollerCompatTest extends ScrollerCompatTestBase {
-    public IcsScrollerCompatTest() {
-        super(true);
-    }
-}
diff --git a/compat/tests/java/android/support/v4/widget/ListViewCompatTest.java b/compat/tests/java/android/support/v4/widget/ListViewCompatTest.java
new file mode 100644
index 0000000..27b1487
--- /dev/null
+++ b/compat/tests/java/android/support/v4/widget/ListViewCompatTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.compat.test.R;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+public class ListViewCompatTest extends BaseInstrumentationTestCase<ListViewTestActivity> {
+    private ListView mListView;
+
+    public ListViewCompatTest() {
+        super(ListViewTestActivity.class);
+    }
+
+    @Before
+    public void setUp() throws Throwable {
+        mListView = mActivityTestRule.getActivity().findViewById(R.id.content);
+        runOnMainAndLayoutSync(mActivityTestRule, mListView, new Runnable() {
+            @Override
+            public void run() {
+                mListView.setAdapter(new BaseAdapter() {
+                    @Override
+                    public int getCount() {
+                        return 500;
+                    }
+
+                    @Override
+                    public Object getItem(int position) {
+                        return null;
+                    }
+
+                    @Override
+                    public long getItemId(int position) {
+                        return position;
+                    }
+
+                    @SuppressLint("SetTextI18n")
+                    @Override
+                    public View getView(int position, View convertView, ViewGroup parent) {
+                        if (convertView == null) {
+                            convertView = LayoutInflater.from(mListView.getContext()).inflate(
+                                    R.layout.list_view_row, parent, false);
+                        }
+                        TextView result = (TextView) convertView;
+                        result.setText("row #" + (position + 1));
+                        return result;
+                    }
+                });
+            }
+        }, false);
+    }
+
+    private void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
+            @NonNull final View view, @Nullable final Runnable runner, final boolean forceLayout)
+            throws Throwable {
+        final View rootView = view.getRootView();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        activityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+                        // countdown immediately since the layout we were waiting on has happened
+                        latch.countDown();
+                    }
+                };
+
+                rootView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
+
+                if (runner != null) {
+                    runner.run();
+                }
+
+                if (forceLayout) {
+                    rootView.requestLayout();
+                }
+            }
+        });
+
+        try {
+            Assert.assertTrue("Expected layout pass within 5 seconds",
+                    latch.await(5, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testCanScroll() throws Throwable {
+        final int itemCount = mListView.getAdapter().getCount();
+
+        assertEquals(0, mListView.getFirstVisiblePosition());
+
+        // Verify that when we're at the top of the list, we can't scroll up but we can scroll
+        // down.
+        assertFalse(ListViewCompat.canScrollList(mListView, -1));
+        assertTrue(ListViewCompat.canScrollList(mListView, 1));
+
+        // Scroll down to the very end of the list
+        runOnMainAndLayoutSync(mActivityTestRule, mListView,
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        mListView.setStackFromBottom(true);
+                    }
+                }, false);
+        assertEquals(itemCount - 1, mListView.getLastVisiblePosition());
+
+        // Verify that when we're at the bottom of the list, we can't scroll down but we can scroll
+        // up.
+        assertFalse(ListViewCompat.canScrollList(mListView, 1));
+        assertTrue(ListViewCompat.canScrollList(mListView, -1));
+    }
+}
diff --git a/compat/tests/java/android/support/v4/widget/ListViewTestActivity.java b/compat/tests/java/android/support/v4/widget/ListViewTestActivity.java
new file mode 100644
index 0000000..762889f
--- /dev/null
+++ b/compat/tests/java/android/support/v4/widget/ListViewTestActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import android.support.compat.test.R;
+import android.support.v4.BaseTestActivity;
+
+public class ListViewTestActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.list_view_activity;
+    }
+}
diff --git a/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java b/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
index ac6cdd0..41821ee 100644
--- a/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
+++ b/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
@@ -32,26 +32,19 @@
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
-public abstract class ScrollerCompatTestBase {
+public class ScrollerCompatTestBase {
 
     private static final boolean DEBUG = false;
 
-    private final String TAG;
-
-    private final boolean mIsIcsOrNewer;
+    private static final String TAG = "ScrollerCompatTest";
 
     private ScrollerCompat mScroller;
 
-    public ScrollerCompatTestBase(boolean isIcsOrNewer) {
-        mIsIcsOrNewer = isIcsOrNewer;
-        TAG = "ScrollerCompatTest ICS or newer:" + isIcsOrNewer;
-    }
 
     protected void createScroller(Interpolator interpolator)
             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
             InstantiationException {
-        mScroller = new ScrollerCompat(mIsIcsOrNewer, InstrumentationRegistry.getContext(),
-                interpolator);
+        mScroller = new ScrollerCompat(InstrumentationRegistry.getContext(), interpolator);
     }
 
     @Test
diff --git a/compat/tests/res/drawable/pointer_icon.xml b/compat/tests/res/drawable/pointer_icon.xml
new file mode 100644
index 0000000..afb5a1a
--- /dev/null
+++ b/compat/tests/res/drawable/pointer_icon.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/test_drawable"
+    android:hotSpotX="16dp"
+    android:hotSpotY="16dp" />
diff --git a/compat/tests/res/font/dummyproviderfont.xml b/compat/tests/res/font/dummyproviderfont.xml
new file mode 100644
index 0000000..6295c91
--- /dev/null
+++ b/compat/tests/res/font/dummyproviderfont.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:fontProviderAuthority="android.provider.fonts.font"
+    app:fontProviderPackage="android.support.compat.test"
+    app:fontProviderQuery="styleTest" >
+</font-family>
diff --git a/compat/tests/res/font/invalid_font.ttf b/compat/tests/res/font/invalid_font.ttf
new file mode 100644
index 0000000..4898a2b
--- /dev/null
+++ b/compat/tests/res/font/invalid_font.ttf
@@ -0,0 +1 @@
+Invalid Font File.
diff --git a/compat/tests/res/font/invalid_xmlempty.xml b/compat/tests/res/font/invalid_xmlempty.xml
new file mode 100644
index 0000000..545704f
--- /dev/null
+++ b/compat/tests/res/font/invalid_xmlempty.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources />
diff --git a/compat/tests/res/font/invalid_xmlfamily.xml b/compat/tests/res/font/invalid_xmlfamily.xml
new file mode 100644
index 0000000..ea9ec97
--- /dev/null
+++ b/compat/tests/res/font/invalid_xmlfamily.xml
@@ -0,0 +1,3 @@
+<invalid-tag xmlns:app="http://schemas.android.com/apk/res-auto">
+  <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/samplefont" />
+</invalid-tag>
diff --git a/compat/tests/res/font/invalid_xmlfont.xml b/compat/tests/res/font/invalid_xmlfont.xml
new file mode 100644
index 0000000..4d3afc2
--- /dev/null
+++ b/compat/tests/res/font/invalid_xmlfont.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
+  <!-- the tag inside font-family must be 'font' -->
+  <ttf app:fontStyle="normal" app:fontWeight="400" app:font="@font/samplefont" />
+</font-family>
diff --git a/compat/tests/res/font/invalid_xmlfont_contains_invalid_font_file.xml b/compat/tests/res/font/invalid_xmlfont_contains_invalid_font_file.xml
new file mode 100644
index 0000000..f876e15
--- /dev/null
+++ b/compat/tests/res/font/invalid_xmlfont_contains_invalid_font_file.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
+  <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/invalid_font" />
+</font-family>
diff --git a/compat/tests/res/font/large_a.ttf b/compat/tests/res/font/large_a.ttf
new file mode 100644
index 0000000..0e778b6
--- /dev/null
+++ b/compat/tests/res/font/large_a.ttf
Binary files differ
diff --git a/compat/tests/res/font/large_b.ttf b/compat/tests/res/font/large_b.ttf
new file mode 100644
index 0000000..b8ce6ce
--- /dev/null
+++ b/compat/tests/res/font/large_b.ttf
Binary files differ
diff --git a/compat/tests/res/font/large_c.ttf b/compat/tests/res/font/large_c.ttf
new file mode 100644
index 0000000..dd5fa50
--- /dev/null
+++ b/compat/tests/res/font/large_c.ttf
Binary files differ
diff --git a/compat/tests/res/font/large_d.ttf b/compat/tests/res/font/large_d.ttf
new file mode 100644
index 0000000..b791100
--- /dev/null
+++ b/compat/tests/res/font/large_d.ttf
Binary files differ
diff --git a/compat/tests/res/font/samplefont.ttf b/compat/tests/res/font/samplefont.ttf
new file mode 100644
index 0000000..5fccad2
--- /dev/null
+++ b/compat/tests/res/font/samplefont.ttf
Binary files differ
diff --git a/compat/tests/res/font/samplefont2.ttf b/compat/tests/res/font/samplefont2.ttf
new file mode 100644
index 0000000..f31b8a9
--- /dev/null
+++ b/compat/tests/res/font/samplefont2.ttf
Binary files differ
diff --git a/compat/tests/res/font/samplefont3.ttf b/compat/tests/res/font/samplefont3.ttf
new file mode 100644
index 0000000..9c850ab
--- /dev/null
+++ b/compat/tests/res/font/samplefont3.ttf
Binary files differ
diff --git a/compat/tests/res/font/samplefont4.ttf b/compat/tests/res/font/samplefont4.ttf
new file mode 100644
index 0000000..9c850ab
--- /dev/null
+++ b/compat/tests/res/font/samplefont4.ttf
Binary files differ
diff --git a/compat/tests/res/font/samplexmldownloadedfont.xml b/compat/tests/res/font/samplexmldownloadedfont.xml
new file mode 100644
index 0000000..659d196
--- /dev/null
+++ b/compat/tests/res/font/samplexmldownloadedfont.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:fontProviderAuthority="com.example.test.fontprovider.authority"
+    app:fontProviderPackage="com.example.test.fontprovider.package"
+    app:fontProviderQuery="MyRequestedFont">
+</font-family>
diff --git a/compat/tests/res/font/samplexmlfont.xml b/compat/tests/res/font/samplexmlfont.xml
new file mode 100644
index 0000000..8a6876d
--- /dev/null
+++ b/compat/tests/res/font/samplexmlfont.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:app="http://schemas.android.com/apk/res-auto">
+    <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/samplefont" />
+    <font app:fontStyle="italic" app:fontWeight="400" app:font="@font/samplefont2" />
+    <font app:fontStyle="normal" app:fontWeight="700" app:font="@font/samplefont3" />
+    <font app:fontStyle="italic" app:fontWeight="700" app:font="@font/samplefont4" />
+</font-family>
diff --git a/compat/tests/res/font/styletest_async_providerfont.xml b/compat/tests/res/font/styletest_async_providerfont.xml
new file mode 100644
index 0000000..aa28179
--- /dev/null
+++ b/compat/tests/res/font/styletest_async_providerfont.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:fontProviderAuthority="android.support.provider.fonts.font"
+    app:fontProviderPackage="android.support.compat.test"
+    app:fontProviderQuery="styleTest"
+    app:fontProviderFetchStrategy="async">
+</font-family>
diff --git a/compat/tests/res/font/styletest_sync_providerfont.xml b/compat/tests/res/font/styletest_sync_providerfont.xml
new file mode 100644
index 0000000..f89301b
--- /dev/null
+++ b/compat/tests/res/font/styletest_sync_providerfont.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:fontProviderAuthority="android.support.provider.fonts.font"
+    app:fontProviderPackage="android.support.compat.test"
+    app:fontProviderQuery="styleTest"
+    app:fontProviderFetchStrategy="blocking"
+    app:fontProviderFetchTimeout="forever">
+</font-family>
diff --git a/compat/tests/res/font/styletestfont.xml b/compat/tests/res/font/styletestfont.xml
new file mode 100644
index 0000000..dca3fe2
--- /dev/null
+++ b/compat/tests/res/font/styletestfont.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
+    <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/large_a" />
+    <font app:fontStyle="italic" app:fontWeight="400" app:font="@font/large_b" />
+    <font app:fontStyle="normal" app:fontWeight="700" app:font="@font/large_c" />
+    <font app:fontStyle="italic" app:fontWeight="700" app:font="@font/large_d" />
+</font-family>
diff --git a/compat/tests/res/layout/list_view_activity.xml b/compat/tests/res/layout/list_view_activity.xml
new file mode 100644
index 0000000..1a8d2c6
--- /dev/null
+++ b/compat/tests/res/layout/list_view_activity.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<ListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="48dp"
+    android:clipToPadding="false"/>
diff --git a/compat/tests/res/layout/list_view_row.xml b/compat/tests/res/layout/list_view_row.xml
new file mode 100644
index 0000000..f871960
--- /dev/null
+++ b/compat/tests/res/layout/list_view_row.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"/>
\ No newline at end of file
diff --git a/compat/tests/res/values-hdpi/dimens.xml b/compat/tests/res/values-hdpi/dimens.xml
index eb1ff54..3126a6e 100755
--- a/compat/tests/res/values-hdpi/dimens.xml
+++ b/compat/tests/res/values-hdpi/dimens.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <dimen name="density_aware_size">14dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/compat/tests/res/values-mdpi/dimens.xml b/compat/tests/res/values-mdpi/dimens.xml
index 5766379..ada2cae 100755
--- a/compat/tests/res/values-mdpi/dimens.xml
+++ b/compat/tests/res/values-mdpi/dimens.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <dimen name="density_aware_size">12dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/compat/tests/res/values-xhdpi/dimens.xml b/compat/tests/res/values-xhdpi/dimens.xml
index a25d23d..21125b8 100755
--- a/compat/tests/res/values-xhdpi/dimens.xml
+++ b/compat/tests/res/values-xhdpi/dimens.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <dimen name="density_aware_size">16dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/compat/tests/res/values-xxhdpi/dimens.xml b/compat/tests/res/values-xxhdpi/dimens.xml
index 399cde1..aaee862 100755
--- a/compat/tests/res/values-xxhdpi/dimens.xml
+++ b/compat/tests/res/values-xxhdpi/dimens.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <dimen name="density_aware_size">18dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/compat/tests/res/values/arrays.xml b/compat/tests/res/values/arrays.xml
new file mode 100644
index 0000000..1aa87fd
--- /dev/null
+++ b/compat/tests/res/values/arrays.xml
@@ -0,0 +1,33 @@
+<?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>
+    <string-array name="certs1">
+        <item>MIIEqDCCA5CgAwIBAgIJANWFuGx9</item>
+        <item>UEChMHQW5kcm9pZDEQMA4GA=</item>
+    </string-array>
+
+    <string-array name="certs2">
+        <item>MDEyMzM2NTZaMIGUMQswCQYD</item>
+        <item>DHThvbbR24kT9ixcOd9W+EY=</item>
+    </string-array>
+
+    <array name="certarray">
+        <item>@array/certs1</item>
+        <item>@array/certs2</item>
+    </array>
+</resources>
\ No newline at end of file
diff --git a/compat/tests/res/values/attrs.xml b/compat/tests/res/values/attrs.xml
index 36d1e9e..eb82fb3 100644
--- a/compat/tests/res/values/attrs.xml
+++ b/compat/tests/res/values/attrs.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <attr name="theme_color_default" format="reference" />
     <attr name="theme_color_focused" format="reference" />
     <attr name="theme_color_pressed" format="reference" />
diff --git a/compat/tests/res/values/colors.xml b/compat/tests/res/values/colors.xml
index 15158cf..0902707 100644
--- a/compat/tests/res/values/colors.xml
+++ b/compat/tests/res/values/colors.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <color name="text_color">#FF8090</color>
 
     <color name="selector_color_default">#70A0C0</color>
diff --git a/compat/tests/res/values/dimens.xml b/compat/tests/res/values/dimens.xml
index 5c82462..5c47040 100644
--- a/compat/tests/res/values/dimens.xml
+++ b/compat/tests/res/values/dimens.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <dimen name="text_medium_size">20sp</dimen>
 
     <dimen name="drawable_small_size">12dip</dimen>
diff --git a/compat/tests/res/values/strings.xml b/compat/tests/res/values/strings.xml
index 4aebfae..d43d30e 100644
--- a/compat/tests/res/values/strings.xml
+++ b/compat/tests/res/values/strings.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <!-- Short text for testing. -->
     <string name="test_text_short">Lorem ipsum</string>
     <!-- Medium text for testing. -->
diff --git a/compat/tests/res/values/styles.xml b/compat/tests/res/values/styles.xml
index 447d5ec..b74bacc 100644
--- a/compat/tests/res/values/styles.xml
+++ b/compat/tests/res/values/styles.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <style name="TestActivityTheme">
         <item name="android:windowAnimationStyle">@null</item>
     </style>
diff --git a/core-ui/Android.mk b/core-ui/Android.mk
index eb9acca..bb7a4be 100644
--- a/core-ui/Android.mk
+++ b/core-ui/Android.mk
@@ -26,14 +26,8 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-core-ui
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under,honeycomb) \
-    $(call all-java-files-under,ics) \
-    $(call all-java-files-under,jellybean-mr2) \
-    $(call all-java-files-under,api21) \
-    $(call all-java-files-under,java)
+LOCAL_SRC_FILES := $(call all-java-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
diff --git a/core-ui/AndroidManifest-make.xml b/core-ui/AndroidManifest-make.xml
deleted file mode 100644
index 9bcc44e..0000000
--- a/core-ui/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.coreui">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.coreui"/>
-    <application />
-</manifest>
diff --git a/core-ui/AndroidManifest.xml b/core-ui/AndroidManifest.xml
index 646626e..b952b5e 100644
--- a/core-ui/AndroidManifest.xml
+++ b/core-ui/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.coreui">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.coreui"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.coreui"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java b/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
deleted file mode 100644
index ff2e93d..0000000
--- a/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.widget;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-
-/**
- * Provides functionality for DrawerLayout unique to API 21
- */
-@RequiresApi(21)
-@TargetApi(21)
-class DrawerLayoutCompatApi21 {
-
-    private static final int[] THEME_ATTRS = {
-            android.R.attr.colorPrimaryDark
-    };
-
-    public static void configureApplyInsets(View drawerLayout) {
-        if (drawerLayout instanceof DrawerLayoutImpl) {
-            drawerLayout.setOnApplyWindowInsetsListener(new InsetsListener());
-            drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
-        }
-    }
-
-    public static void dispatchChildInsets(View child, Object insets, int gravity) {
-        WindowInsets wi = (WindowInsets) insets;
-        if (gravity == Gravity.LEFT) {
-            wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(),
-                    wi.getSystemWindowInsetTop(), 0, wi.getSystemWindowInsetBottom());
-        } else if (gravity == Gravity.RIGHT) {
-            wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(),
-                    wi.getSystemWindowInsetRight(), wi.getSystemWindowInsetBottom());
-        }
-        child.dispatchApplyWindowInsets(wi);
-    }
-
-    public static void applyMarginInsets(ViewGroup.MarginLayoutParams lp, Object insets,
-            int gravity) {
-        WindowInsets wi = (WindowInsets) insets;
-        if (gravity == Gravity.LEFT) {
-            wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(),
-                    wi.getSystemWindowInsetTop(), 0, wi.getSystemWindowInsetBottom());
-        } else if (gravity == Gravity.RIGHT) {
-            wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(),
-                    wi.getSystemWindowInsetRight(), wi.getSystemWindowInsetBottom());
-        }
-        lp.leftMargin = wi.getSystemWindowInsetLeft();
-        lp.topMargin = wi.getSystemWindowInsetTop();
-        lp.rightMargin = wi.getSystemWindowInsetRight();
-        lp.bottomMargin = wi.getSystemWindowInsetBottom();
-    }
-
-    public static int getTopInset(Object insets) {
-        return insets != null ? ((WindowInsets) insets).getSystemWindowInsetTop() : 0;
-    }
-
-    public static Drawable getDefaultStatusBarBackground(Context context) {
-        final TypedArray a = context.obtainStyledAttributes(THEME_ATTRS);
-        try {
-            return a.getDrawable(0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    static class InsetsListener implements View.OnApplyWindowInsetsListener {
-        @Override
-        public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
-            final DrawerLayoutImpl drawerLayout = (DrawerLayoutImpl) v;
-            drawerLayout.setChildInsets(insets, insets.getSystemWindowInsetTop() > 0);
-            return insets.consumeSystemWindowInsets();
-        }
-    }
-}
diff --git a/core-ui/api21/android/support/v4/widget/DrawerLayoutImpl.java b/core-ui/api21/android/support/v4/widget/DrawerLayoutImpl.java
deleted file mode 100644
index 02545c1..0000000
--- a/core-ui/api21/android/support/v4/widget/DrawerLayoutImpl.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.widget;
-
-/**
- * Interface used to communicate from the v21-specific code for configuring a DrawerLayout
- * to the DrawerLayout itself.
- */
-interface DrawerLayoutImpl {
-    void setChildInsets(Object insets, boolean drawStatusBar);
-}
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 0b94a96..e25b916 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -1,107 +1,43 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-core-ui'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-compat')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+    api project(':support-compat')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+    androidTestImplementation project(':support-testutils')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
-                'honeycomb',
-                'ics',
-                'jellybean-mr2',
-                'api21',
                 'java'
         ]
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
     }
 
     buildTypes.all {
         consumerProguardFiles 'proguard-rules.pro'
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
-
     testOptions {
         unitTests.returnDefaultValues = true
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-        exclude('android/content/pm/**')
-        exclude('android/service/media/**')
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android Support Library core UI'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v4'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java b/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
deleted file mode 100644
index bd4fd97..0000000
--- a/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2013 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.app;
-
-import android.R;
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.Activity;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import java.lang.reflect.Method;
-
-/**
- * This class encapsulates some awful hacks.
- *
- * Before JB-MR2 (API 18) it was not possible to change the home-as-up indicator glyph
- * in an action bar without some really gross hacks. Since the MR2 SDK is not published as of
- * this writing, the new API is accessed via reflection here if available.
- */
-@RequiresApi(11)
-@TargetApi(11)
-class ActionBarDrawerToggleHoneycomb {
-    private static final String TAG = "ActionBarDrawerToggleHoneycomb";
-
-    private static final int[] THEME_ATTRS = new int[] {
-            R.attr.homeAsUpIndicator
-    };
-
-    public static Object setActionBarUpIndicator(Object info, Activity activity,
-            Drawable drawable, int contentDescRes) {
-        if (info == null) {
-            info = new SetIndicatorInfo(activity);
-        }
-        final SetIndicatorInfo sii = (SetIndicatorInfo) info;
-        if (sii.setHomeAsUpIndicator != null) {
-            try {
-                final ActionBar actionBar = activity.getActionBar();
-                sii.setHomeAsUpIndicator.invoke(actionBar, drawable);
-                sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
-            } catch (Exception e) {
-                Log.w(TAG, "Couldn't set home-as-up indicator via JB-MR2 API", e);
-            }
-        } else if (sii.upIndicatorView != null) {
-            sii.upIndicatorView.setImageDrawable(drawable);
-        } else {
-            Log.w(TAG, "Couldn't set home-as-up indicator");
-        }
-        return info;
-    }
-
-    public static Object setActionBarDescription(Object info, Activity activity,
-            int contentDescRes) {
-        if (info == null) {
-            info = new SetIndicatorInfo(activity);
-        }
-        final SetIndicatorInfo sii = (SetIndicatorInfo) info;
-        if (sii.setHomeAsUpIndicator != null) {
-            try {
-                final ActionBar actionBar = activity.getActionBar();
-                sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
-                if (Build.VERSION.SDK_INT <= 19) {
-                    // For API 19 and earlier, we need to manually force the
-                    // action bar to generate a new content description.
-                    actionBar.setSubtitle(actionBar.getSubtitle());
-                }
-            } catch (Exception e) {
-                Log.w(TAG, "Couldn't set content description via JB-MR2 API", e);
-            }
-        }
-        return info;
-    }
-
-    public static Drawable getThemeUpIndicator(Activity activity) {
-        final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS);
-        final Drawable result = a.getDrawable(0);
-        a.recycle();
-        return result;
-    }
-
-    private static class SetIndicatorInfo {
-        public Method setHomeAsUpIndicator;
-        public Method setHomeActionContentDescription;
-        public ImageView upIndicatorView;
-
-        SetIndicatorInfo(Activity activity) {
-            try {
-                setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod("setHomeAsUpIndicator",
-                        Drawable.class);
-                setHomeActionContentDescription = ActionBar.class.getDeclaredMethod(
-                        "setHomeActionContentDescription", Integer.TYPE);
-
-                // If we got the method we won't need the stuff below.
-                return;
-            } catch (NoSuchMethodException e) {
-                // Oh well. We'll use the other mechanism below instead.
-            }
-
-            final View home = activity.findViewById(android.R.id.home);
-            if (home == null) {
-                // Action bar doesn't have a known configuration, an OEM messed with things.
-                return;
-            }
-
-            final ViewGroup parent = (ViewGroup) home.getParent();
-            final int childCount = parent.getChildCount();
-            if (childCount != 2) {
-                // No idea which one will be the right one, an OEM messed with things.
-                return;
-            }
-
-            final View first = parent.getChildAt(0);
-            final View second = parent.getChildAt(1);
-            final View up = first.getId() == android.R.id.home ? second : first;
-
-            if (up instanceof ImageView) {
-                // Jackpot! (Probably...)
-                upIndicatorView = (ImageView) up;
-            }
-        }
-    }
-}
diff --git a/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java b/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
deleted file mode 100644
index c8e70bd..0000000
--- a/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2012 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.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.text.method.SingleLineTransformationMethod;
-import android.view.View;
-import android.widget.TextView;
-
-import java.util.Locale;
-
-@RequiresApi(14)
-@TargetApi(14)
-class PagerTitleStripIcs {
-    public static void setSingleLineAllCaps(TextView text) {
-        text.setTransformationMethod(new SingleLineAllCapsTransform(text.getContext()));
-    }
-
-    private static class SingleLineAllCapsTransform extends SingleLineTransformationMethod {
-        private static final String TAG = "SingleLineAllCapsTransform";
-
-        private Locale mLocale;
-
-        public SingleLineAllCapsTransform(Context context) {
-            mLocale = context.getResources().getConfiguration().locale;
-        }
-
-        @Override
-        public CharSequence getTransformation(CharSequence source, View view) {
-            source = super.getTransformation(source, view);
-            return source != null ? source.toString().toUpperCase(mLocale) : null;
-        }
-    }
-}
diff --git a/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java b/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
index 92af2de..d95e42c 100644
--- a/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
+++ b/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
@@ -17,25 +17,31 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
+import android.app.ActionBar;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
 import android.support.annotation.StringRes;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.DrawerLayout;
+import android.util.Log;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.lang.reflect.Method;
 
 /**
  * This class provides a handy way to tie together the functionality of
@@ -108,99 +114,11 @@
         void setActionBarDescription(@StringRes int contentDescRes);
     }
 
-    private interface ActionBarDrawerToggleImpl {
-        Drawable getThemeUpIndicator(Activity activity);
-        Object setActionBarUpIndicator(Object info, Activity activity,
-                Drawable themeImage, int contentDescRes);
-        Object setActionBarDescription(Object info, Activity activity, int contentDescRes);
-    }
+    private static final String TAG = "ActionBarDrawerToggle";
 
-    private static class ActionBarDrawerToggleImplBase implements ActionBarDrawerToggleImpl {
-        ActionBarDrawerToggleImplBase() {
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator(Activity activity) {
-            return null;
-        }
-
-        @Override
-        public Object setActionBarUpIndicator(Object info, Activity activity,
-                Drawable themeImage, int contentDescRes) {
-            // No action bar to set.
-            return info;
-        }
-
-        @Override
-        public Object setActionBarDescription(Object info, Activity activity, int contentDescRes) {
-            // No action bar to set
-            return info;
-        }
-    }
-
-    @RequiresApi(11)
-    @TargetApi(11)
-    private static class ActionBarDrawerToggleImplHC implements ActionBarDrawerToggleImpl {
-        ActionBarDrawerToggleImplHC() {
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator(Activity activity) {
-            return ActionBarDrawerToggleHoneycomb.getThemeUpIndicator(activity);
-        }
-
-        @Override
-        public Object setActionBarUpIndicator(Object info, Activity activity,
-                Drawable themeImage, int contentDescRes) {
-            return ActionBarDrawerToggleHoneycomb.setActionBarUpIndicator(info, activity,
-                    themeImage, contentDescRes);
-        }
-
-        @Override
-        public Object setActionBarDescription(Object info, Activity activity, int contentDescRes) {
-            return ActionBarDrawerToggleHoneycomb.setActionBarDescription(info, activity,
-                    contentDescRes);
-        }
-    }
-
-    @RequiresApi(18)
-    @TargetApi(18)
-    private static class ActionBarDrawerToggleImplJellybeanMR2
-            implements ActionBarDrawerToggleImpl {
-        ActionBarDrawerToggleImplJellybeanMR2() {
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator(Activity activity) {
-            return ActionBarDrawerToggleJellybeanMR2.getThemeUpIndicator(activity);
-        }
-
-        @Override
-        public Object setActionBarUpIndicator(Object info, Activity activity,
-                Drawable themeImage, int contentDescRes) {
-            return ActionBarDrawerToggleJellybeanMR2.setActionBarUpIndicator(info, activity,
-                    themeImage, contentDescRes);
-        }
-
-        @Override
-        public Object setActionBarDescription(Object info, Activity activity, int contentDescRes) {
-            return ActionBarDrawerToggleJellybeanMR2.setActionBarDescription(info, activity,
-                    contentDescRes);
-        }
-    }
-
-    private static final ActionBarDrawerToggleImpl IMPL;
-
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 18) {
-            IMPL = new ActionBarDrawerToggleImplJellybeanMR2();
-        } else if (version >= 11) {
-            IMPL = new ActionBarDrawerToggleImplHC();
-        } else {
-            IMPL = new ActionBarDrawerToggleImplBase();
-        }
-    }
+    private static final int[] THEME_ATTRS = new int[] {
+            android.R.attr.homeAsUpIndicator
+    };
 
     /** Fraction of its total width by which to offset the toggle drawable. */
     private static final float TOGGLE_DRAWABLE_OFFSET = 1 / 3f;
@@ -221,7 +139,7 @@
     private final int mOpenDrawerContentDescRes;
     private final int mCloseDrawerContentDescRes;
 
-    private Object mSetIndicatorInfo;
+    private SetIndicatorInfo mSetIndicatorInfo;
 
     /**
      * Construct a new ActionBarDrawerToggle.
@@ -492,29 +410,133 @@
     public void onDrawerStateChanged(int newState) {
     }
 
-    Drawable getThemeUpIndicator() {
+    private Drawable getThemeUpIndicator() {
         if (mActivityImpl != null) {
             return mActivityImpl.getThemeUpIndicator();
         }
-        return IMPL.getThemeUpIndicator(mActivity);
+        if (Build.VERSION.SDK_INT >= 18) {
+            final ActionBar actionBar = mActivity.getActionBar();
+            final Context context;
+            if (actionBar != null) {
+                context = actionBar.getThemedContext();
+            } else {
+                context = mActivity;
+            }
+
+            final TypedArray a = context.obtainStyledAttributes(null, THEME_ATTRS,
+                    android.R.attr.actionBarStyle, 0);
+            final Drawable result = a.getDrawable(0);
+            a.recycle();
+            return result;
+        } else {
+            final TypedArray a = mActivity.obtainStyledAttributes(THEME_ATTRS);
+            final Drawable result = a.getDrawable(0);
+            a.recycle();
+            return result;
+        }
     }
 
-    void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
+    private void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
         if (mActivityImpl != null) {
             mActivityImpl.setActionBarUpIndicator(upDrawable, contentDescRes);
             return;
         }
-        mSetIndicatorInfo = IMPL
-                .setActionBarUpIndicator(mSetIndicatorInfo, mActivity, upDrawable, contentDescRes);
+        if (Build.VERSION.SDK_INT >= 18) {
+            final ActionBar actionBar = mActivity.getActionBar();
+            if (actionBar != null) {
+                actionBar.setHomeAsUpIndicator(upDrawable);
+                actionBar.setHomeActionContentDescription(contentDescRes);
+            }
+        } else {
+            if (mSetIndicatorInfo == null) {
+                mSetIndicatorInfo = new SetIndicatorInfo(mActivity);
+            }
+            if (mSetIndicatorInfo.mSetHomeAsUpIndicator != null) {
+                try {
+                    final ActionBar actionBar = mActivity.getActionBar();
+                    mSetIndicatorInfo.mSetHomeAsUpIndicator.invoke(actionBar, upDrawable);
+                    mSetIndicatorInfo.mSetHomeActionContentDescription.invoke(
+                            actionBar, contentDescRes);
+                } catch (Exception e) {
+                    Log.w(TAG, "Couldn't set home-as-up indicator via JB-MR2 API", e);
+                }
+            } else if (mSetIndicatorInfo.mUpIndicatorView != null) {
+                mSetIndicatorInfo.mUpIndicatorView.setImageDrawable(upDrawable);
+            } else {
+                Log.w(TAG, "Couldn't set home-as-up indicator");
+            }
+        }
     }
 
-    void setActionBarDescription(int contentDescRes) {
+    private void setActionBarDescription(int contentDescRes) {
         if (mActivityImpl != null) {
             mActivityImpl.setActionBarDescription(contentDescRes);
             return;
         }
-        mSetIndicatorInfo = IMPL
-                .setActionBarDescription(mSetIndicatorInfo, mActivity, contentDescRes);
+        if (Build.VERSION.SDK_INT >= 18) {
+            final ActionBar actionBar = mActivity.getActionBar();
+            if (actionBar != null) {
+                actionBar.setHomeActionContentDescription(contentDescRes);
+            }
+        } else {
+            if (mSetIndicatorInfo == null) {
+                mSetIndicatorInfo = new SetIndicatorInfo(mActivity);
+            }
+            if (mSetIndicatorInfo.mSetHomeAsUpIndicator != null) {
+                try {
+                    final ActionBar actionBar = mActivity.getActionBar();
+                    mSetIndicatorInfo.mSetHomeActionContentDescription.invoke(
+                            actionBar, contentDescRes);
+                    // For API 19 and earlier, we need to manually force the
+                    // action bar to generate a new content description.
+                    actionBar.setSubtitle(actionBar.getSubtitle());
+                } catch (Exception e) {
+                    Log.w(TAG, "Couldn't set content description via JB-MR2 API", e);
+                }
+            }
+        }
+    }
+
+    private static class SetIndicatorInfo {
+        Method mSetHomeAsUpIndicator;
+        Method mSetHomeActionContentDescription;
+        ImageView mUpIndicatorView;
+
+        SetIndicatorInfo(Activity activity) {
+            try {
+                mSetHomeAsUpIndicator = ActionBar.class.getDeclaredMethod("setHomeAsUpIndicator",
+                        Drawable.class);
+                mSetHomeActionContentDescription = ActionBar.class.getDeclaredMethod(
+                        "setHomeActionContentDescription", Integer.TYPE);
+
+                // If we got the method we won't need the stuff below.
+                return;
+            } catch (NoSuchMethodException e) {
+                // Oh well. We'll use the other mechanism below instead.
+            }
+
+            final View home = activity.findViewById(android.R.id.home);
+            if (home == null) {
+                // Action bar doesn't have a known configuration, an OEM messed with things.
+                return;
+            }
+
+            final ViewGroup parent = (ViewGroup) home.getParent();
+            final int childCount = parent.getChildCount();
+            if (childCount != 2) {
+                // No idea which one will be the right one, an OEM messed with things.
+                return;
+            }
+
+            final View first = parent.getChildAt(0);
+            final View second = parent.getChildAt(1);
+            final View up = first.getId() == android.R.id.home ? second : first;
+
+            if (up instanceof ImageView) {
+                // Jackpot! (Probably...)
+                mUpIndicatorView = (ImageView) up;
+            }
+        }
     }
 
     private class SlideDrawable extends InsetDrawable implements Drawable.Callback {
@@ -555,7 +577,7 @@
         }
 
         @Override
-        public void draw(Canvas canvas) {
+        public void draw(@NonNull Canvas canvas) {
             copyBounds(mTmpRect);
             canvas.save();
 
diff --git a/core-ui/java/android/support/v4/view/AbsSavedState.java b/core-ui/java/android/support/v4/view/AbsSavedState.java
index 02e89b6..4cf38ac 100644
--- a/core-ui/java/android/support/v4/view/AbsSavedState.java
+++ b/core-ui/java/android/support/v4/view/AbsSavedState.java
@@ -18,8 +18,6 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 
 /**
  * A {@link Parcelable} implementation that should be used by inheritance
@@ -83,20 +81,24 @@
         dest.writeParcelable(mSuperState, flags);
     }
 
-    public static final Parcelable.Creator<AbsSavedState> CREATOR = ParcelableCompat.newCreator(
-            new ParcelableCompatCreatorCallbacks<AbsSavedState>() {
-                @Override
-                public AbsSavedState createFromParcel(Parcel in, ClassLoader loader) {
-                    Parcelable superState = in.readParcelable(loader);
-                    if (superState != null) {
-                        throw new IllegalStateException("superState must be null");
-                    }
-                    return EMPTY_STATE;
-                }
+    public static final Creator<AbsSavedState> CREATOR = new ClassLoaderCreator<AbsSavedState>() {
+        @Override
+        public AbsSavedState createFromParcel(Parcel in, ClassLoader loader) {
+            Parcelable superState = in.readParcelable(loader);
+            if (superState != null) {
+                throw new IllegalStateException("superState must be null");
+            }
+            return EMPTY_STATE;
+        }
 
-                @Override
-                public AbsSavedState[] newArray(int size) {
-                    return new AbsSavedState[size];
-                }
-            });
+        @Override
+        public AbsSavedState createFromParcel(Parcel in) {
+            return createFromParcel(in, null);
+        }
+
+        @Override
+        public AbsSavedState[] newArray(int size) {
+            return new AbsSavedState[size];
+        }
+    };
 }
diff --git a/core-ui/java/android/support/v4/view/AsyncLayoutInflater.java b/core-ui/java/android/support/v4/view/AsyncLayoutInflater.java
index e638d05..e194a50 100644
--- a/core-ui/java/android/support/v4/view/AsyncLayoutInflater.java
+++ b/core-ui/java/android/support/v4/view/AsyncLayoutInflater.java
@@ -169,28 +169,35 @@
         private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);
         private SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);
 
+        // Extracted to its own method to ensure locals have a constrained liveness
+        // scope by the GC. This is needed to avoid keeping previous request references
+        // alive for an indeterminate amount of time, see b/33158143 for details
+        public void runInner() {
+            InflateRequest request;
+            try {
+                request = mQueue.take();
+            } catch (InterruptedException ex) {
+                // Odd, just continue
+                Log.w(TAG, ex);
+                return;
+            }
+
+            try {
+                request.view = request.inflater.mInflater.inflate(
+                        request.resid, request.parent, false);
+            } catch (RuntimeException ex) {
+                // Probably a Looper failure, retry on the UI thread
+                Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI"
+                        + " thread", ex);
+            }
+            Message.obtain(request.inflater.mHandler, 0, request)
+                    .sendToTarget();
+        }
+
         @Override
         public void run() {
             while (true) {
-                InflateRequest request;
-                try {
-                    request = mQueue.take();
-                } catch (InterruptedException ex) {
-                    // Odd, just continue
-                    Log.w(TAG, ex);
-                    continue;
-                }
-
-                try {
-                    request.view = request.inflater.mInflater.inflate(
-                            request.resid, request.parent, false);
-                } catch (RuntimeException ex) {
-                    // Probably a Looper failure, retry on the UI thread
-                    Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI"
-                            + " thread", ex);
-                }
-                Message.obtain(request.inflater.mHandler, 0, request)
-                        .sendToTarget();
+                runInner();
             }
         }
 
diff --git a/core-ui/java/android/support/v4/view/NestedScrollingChildHelper.java b/core-ui/java/android/support/v4/view/NestedScrollingChildHelper.java
index b79f533..69a8bfd 100644
--- a/core-ui/java/android/support/v4/view/NestedScrollingChildHelper.java
+++ b/core-ui/java/android/support/v4/view/NestedScrollingChildHelper.java
@@ -17,6 +17,13 @@
 
 package android.support.v4.view;
 
+import static android.support.v4.view.ViewCompat.TYPE_NON_TOUCH;
+import static android.support.v4.view.ViewCompat.TYPE_TOUCH;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat.NestedScrollType;
+import android.support.v4.view.ViewCompat.ScrollAxis;
 import android.view.View;
 import android.view.ViewParent;
 
@@ -36,15 +43,16 @@
  * 5.0 Lollipop and newer.</p>
  */
 public class NestedScrollingChildHelper {
+    private ViewParent mNestedScrollingParentTouch;
+    private ViewParent mNestedScrollingParentNonTouch;
     private final View mView;
-    private ViewParent mNestedScrollingParent;
     private boolean mIsNestedScrollingEnabled;
     private int[] mTempNestedScrollConsumed;
 
     /**
      * Construct a new helper for a given view.
      */
-    public NestedScrollingChildHelper(View view) {
+    public NestedScrollingChildHelper(@NonNull View view) {
         mView = view;
     }
 
@@ -79,7 +87,7 @@
 
     /**
      * Check if this view has a nested scrolling parent view currently receiving events for
-     * a nested scroll in progress.
+     * a nested scroll in progress with the type of touch.
      *
      * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
      * method/{@link android.support.v4.view.NestedScrollingChild} interface method with the same
@@ -88,7 +96,21 @@
      * @return true if this view has a nested scrolling parent, false otherwise
      */
     public boolean hasNestedScrollingParent() {
-        return mNestedScrollingParent != null;
+        return hasNestedScrollingParent(TYPE_TOUCH);
+    }
+
+    /**
+     * Check if this view has a nested scrolling parent view currently receiving events for
+     * a nested scroll in progress with the given type.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link android.support.v4.view.NestedScrollingChild} interface method with the same
+     * signature to implement the standard policy.</p>
+     *
+     * @return true if this view has a nested scrolling parent, false otherwise
+     */
+    public boolean hasNestedScrollingParent(@NestedScrollType int type) {
+        return getNestedScrollingParentForType(type) != null;
     }
 
     /**
@@ -102,8 +124,24 @@
      *             See {@link android.support.v4.view.NestedScrollingChild#startNestedScroll(int)}.
      * @return true if a cooperating parent view was found and nested scrolling started successfully
      */
-    public boolean startNestedScroll(int axes) {
-        if (hasNestedScrollingParent()) {
+    public boolean startNestedScroll(@ScrollAxis int axes) {
+        return startNestedScroll(axes, TYPE_TOUCH);
+    }
+
+    /**
+     * Start a new nested scroll for this view.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link android.support.v4.view.NestedScrollingChild2} interface method with the same
+     * signature to implement the standard policy.</p>
+     *
+     * @param axes Supported nested scroll axes.
+     *             See {@link android.support.v4.view.NestedScrollingChild2#startNestedScroll(int,
+     *             int)}.
+     * @return true if a cooperating parent view was found and nested scrolling started successfully
+     */
+    public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {
+        if (hasNestedScrollingParent(type)) {
             // Already in progress
             return true;
         }
@@ -111,9 +149,9 @@
             ViewParent p = mView.getParent();
             View child = mView;
             while (p != null) {
-                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
-                    mNestedScrollingParent = p;
-                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
+                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
+                    setNestedScrollingParentForType(type, p);
+                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
                     return true;
                 }
                 if (p instanceof View) {
@@ -133,9 +171,21 @@
      * signature to implement the standard policy.</p>
      */
     public void stopNestedScroll() {
-        if (mNestedScrollingParent != null) {
-            ViewParentCompat.onStopNestedScroll(mNestedScrollingParent, mView);
-            mNestedScrollingParent = null;
+        stopNestedScroll(TYPE_TOUCH);
+    }
+
+    /**
+     * Stop a nested scroll in progress.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link android.support.v4.view.NestedScrollingChild2} interface method with the same
+     * signature to implement the standard policy.</p>
+     */
+    public void stopNestedScroll(@NestedScrollType int type) {
+        ViewParent parent = getNestedScrollingParentForType(type);
+        if (parent != null) {
+            ViewParentCompat.onStopNestedScroll(parent, mView, type);
+            setNestedScrollingParentForType(type, null);
         }
     }
 
@@ -149,8 +199,29 @@
      * @return true if the parent consumed any of the nested scroll
      */
     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
-            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
-        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {
+        return dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                offsetInWindow, TYPE_TOUCH);
+    }
+
+    /**
+     * Dispatch one step of a nested scrolling operation to the current nested scrolling parent.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link android.support.v4.view.NestedScrollingChild2} interface method with the same
+     * signature to implement the standard policy.</p>
+     *
+     * @return true if the parent consumed any of the nested scroll
+     */
+    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,
+            @NestedScrollType int type) {
+        if (isNestedScrollingEnabled()) {
+            final ViewParent parent = getNestedScrollingParentForType(type);
+            if (parent == null) {
+                return false;
+            }
+
             if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
                 int startX = 0;
                 int startY = 0;
@@ -160,8 +231,8 @@
                     startY = offsetInWindow[1];
                 }
 
-                ViewParentCompat.onNestedScroll(mNestedScrollingParent, mView, dxConsumed,
-                        dyConsumed, dxUnconsumed, dyUnconsumed);
+                ViewParentCompat.onNestedScroll(parent, mView, dxConsumed,
+                        dyConsumed, dxUnconsumed, dyUnconsumed, type);
 
                 if (offsetInWindow != null) {
                     mView.getLocationInWindow(offsetInWindow);
@@ -187,8 +258,28 @@
      *
      * @return true if the parent consumed any of the nested scroll
      */
-    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
-        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
+            @Nullable int[] offsetInWindow) {
+        return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, TYPE_TOUCH);
+    }
+
+    /**
+     * Dispatch one step of a nested pre-scrolling operation to the current nested scrolling parent.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link android.support.v4.view.NestedScrollingChild2} interface method with the same
+     * signature to implement the standard policy.</p>
+     *
+     * @return true if the parent consumed any of the nested scroll
+     */
+    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
+            @Nullable int[] offsetInWindow, @NestedScrollType int type) {
+        if (isNestedScrollingEnabled()) {
+            final ViewParent parent = getNestedScrollingParentForType(type);
+            if (parent == null) {
+                return false;
+            }
+
             if (dx != 0 || dy != 0) {
                 int startX = 0;
                 int startY = 0;
@@ -206,7 +297,7 @@
                 }
                 consumed[0] = 0;
                 consumed[1] = 0;
-                ViewParentCompat.onNestedPreScroll(mNestedScrollingParent, mView, dx, dy, consumed);
+                ViewParentCompat.onNestedPreScroll(parent, mView, dx, dy, consumed, type);
 
                 if (offsetInWindow != null) {
                     mView.getLocationInWindow(offsetInWindow);
@@ -232,9 +323,12 @@
      * @return true if the parent consumed the nested fling
      */
     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
-        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
-            return ViewParentCompat.onNestedFling(mNestedScrollingParent, mView, velocityX,
-                    velocityY, consumed);
+        if (isNestedScrollingEnabled()) {
+            ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);
+            if (parent != null) {
+                return ViewParentCompat.onNestedFling(parent, mView, velocityX,
+                        velocityY, consumed);
+            }
         }
         return false;
     }
@@ -249,9 +343,12 @@
      * @return true if the parent consumed the nested fling
      */
     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
-        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
-            return ViewParentCompat.onNestedPreFling(mNestedScrollingParent, mView, velocityX,
-                    velocityY);
+        if (isNestedScrollingEnabled()) {
+            ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);
+            if (parent != null) {
+                return ViewParentCompat.onNestedPreFling(parent, mView, velocityX,
+                        velocityY);
+            }
         }
         return false;
     }
@@ -277,7 +374,28 @@
      *
      * @param child Child view stopping its nested scroll. This may not be a direct child view.
      */
-    public void onStopNestedScroll(View child) {
+    public void onStopNestedScroll(@NonNull View child) {
         ViewCompat.stopNestedScroll(mView);
     }
+
+    private ViewParent getNestedScrollingParentForType(@NestedScrollType int type) {
+        switch (type) {
+            case TYPE_TOUCH:
+                return mNestedScrollingParentTouch;
+            case TYPE_NON_TOUCH:
+                return mNestedScrollingParentNonTouch;
+        }
+        return null;
+    }
+
+    private void setNestedScrollingParentForType(@NestedScrollType int type, ViewParent p) {
+        switch (type) {
+            case TYPE_TOUCH:
+                mNestedScrollingParentTouch = p;
+                break;
+            case TYPE_NON_TOUCH:
+                mNestedScrollingParentNonTouch = p;
+                break;
+        }
+    }
 }
diff --git a/core-ui/java/android/support/v4/view/NestedScrollingParentHelper.java b/core-ui/java/android/support/v4/view/NestedScrollingParentHelper.java
index d76aff7..ad06097 100644
--- a/core-ui/java/android/support/v4/view/NestedScrollingParentHelper.java
+++ b/core-ui/java/android/support/v4/view/NestedScrollingParentHelper.java
@@ -17,6 +17,9 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.NonNull;
+import android.support.v4.view.ViewCompat.NestedScrollType;
+import android.support.v4.view.ViewCompat.ScrollAxis;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -43,7 +46,7 @@
     /**
      * Construct a new helper for a given ViewGroup
      */
-    public NestedScrollingParentHelper(ViewGroup viewGroup) {
+    public NestedScrollingParentHelper(@NonNull ViewGroup viewGroup) {
         mViewGroup = viewGroup;
     }
 
@@ -55,7 +58,21 @@
      * subclass method/{@link android.support.v4.view.NestedScrollingParent} interface method with
      * the same signature to implement the standard policy.</p>
      */
-    public void onNestedScrollAccepted(View child, View target, int axes) {
+    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
+            @ScrollAxis int axes) {
+        onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH);
+    }
+
+    /**
+     * Called when a nested scrolling operation initiated by a descendant view is accepted
+     * by this ViewGroup.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.ViewGroup ViewGroup}
+     * subclass method/{@link android.support.v4.view.NestedScrollingParent2} interface method with
+     * the same signature to implement the standard policy.</p>
+     */
+    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
+            @ScrollAxis int axes, @NestedScrollType int type) {
         mNestedScrollAxes = axes;
     }
 
@@ -66,6 +83,7 @@
      * subclass method/{@link android.support.v4.view.NestedScrollingParent} interface method with
      * the same signature to implement the standard policy.</p>
      */
+    @ScrollAxis
     public int getNestedScrollAxes() {
         return mNestedScrollAxes;
     }
@@ -76,10 +94,19 @@
      * <p>This is a delegate method. Call it from your {@link android.view.ViewGroup ViewGroup}
      * subclass method/{@link android.support.v4.view.NestedScrollingParent} interface method with
      * the same signature to implement the standard policy.</p>
-     *
-     * @param target View that initiated the nested scroll
      */
-    public void onStopNestedScroll(View target) {
+    public void onStopNestedScroll(@NonNull View target) {
+        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
+    }
+
+    /**
+     * React to a nested scroll operation ending.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.ViewGroup ViewGroup}
+     * subclass method/{@link android.support.v4.view.NestedScrollingParent2} interface method with
+     * the same signature to implement the standard policy.</p>
+     */
+    public void onStopNestedScroll(@NonNull View target, @NestedScrollType int type) {
         mNestedScrollAxes = 0;
     }
 }
diff --git a/core-ui/java/android/support/v4/view/PagerTitleStrip.java b/core-ui/java/android/support/v4/view/PagerTitleStrip.java
index d569cd4..b63e4f4 100644
--- a/core-ui/java/android/support/v4/view/PagerTitleStrip.java
+++ b/core-ui/java/android/support/v4/view/PagerTitleStrip.java
@@ -24,14 +24,17 @@
 import android.support.annotation.FloatRange;
 import android.support.v4.widget.TextViewCompat;
 import android.text.TextUtils.TruncateAt;
+import android.text.method.SingleLineTransformationMethod;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.Gravity;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.TextView;
 
 import java.lang.ref.WeakReference;
+import java.util.Locale;
 
 /**
  * PagerTitleStrip is a non-interactive indicator of the current, next,
@@ -47,8 +50,6 @@
  */
 @ViewPager.DecorView
 public class PagerTitleStrip extends ViewGroup {
-    private static final String TAG = "PagerTitleStrip";
-
     ViewPager mPager;
     TextView mPrevText;
     TextView mCurrText;
@@ -83,35 +84,22 @@
     private int mNonPrimaryAlpha;
     int mTextColor;
 
-    interface PagerTitleStripImpl {
-        void setSingleLineAllCaps(TextView text);
-    }
+    private static class SingleLineAllCapsTransform extends SingleLineTransformationMethod {
+        private Locale mLocale;
 
-    static class PagerTitleStripImplBase implements PagerTitleStripImpl {
-        @Override
-        public void setSingleLineAllCaps(TextView text) {
-            text.setSingleLine();
+        SingleLineAllCapsTransform(Context context) {
+            mLocale = context.getResources().getConfiguration().locale;
         }
-    }
 
-    static class PagerTitleStripImplIcs implements PagerTitleStripImpl {
         @Override
-        public void setSingleLineAllCaps(TextView text) {
-            PagerTitleStripIcs.setSingleLineAllCaps(text);
-        }
-    }
-
-    private static final PagerTitleStripImpl IMPL;
-    static {
-        if (android.os.Build.VERSION.SDK_INT >= 14) {
-            IMPL = new PagerTitleStripImplIcs();
-        } else {
-            IMPL = new PagerTitleStripImplBase();
+        public CharSequence getTransformation(CharSequence source, View view) {
+            source = super.getTransformation(source, view);
+            return source != null ? source.toString().toUpperCase(mLocale) : null;
         }
     }
 
     private static void setSingleLineAllCaps(TextView text) {
-        IMPL.setSingleLineAllCaps(text);
+        text.setTransformationMethod(new SingleLineAllCapsTransform(text.getContext()));
     }
 
     public PagerTitleStrip(Context context) {
@@ -454,9 +442,9 @@
             height = Math.max(minHeight, textHeight + heightPadding);
         }
 
-        final int childState = ViewCompat.getMeasuredState(mCurrText);
-        final int measuredHeight = ViewCompat.resolveSizeAndState(height, heightMeasureSpec,
-                childState << ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
+        final int childState = mCurrText.getMeasuredState();
+        final int measuredHeight = View.resolveSizeAndState(height, heightMeasureSpec,
+                childState << View.MEASURED_HEIGHT_STATE_SHIFT);
         setMeasuredDimension(widthSize, measuredHeight);
     }
 
diff --git a/core-ui/java/android/support/v4/view/ViewPager.java b/core-ui/java/android/support/v4/view/ViewPager.java
index 47d224f..8cd973c 100644
--- a/core-ui/java/android/support/v4/view/ViewPager.java
+++ b/core-ui/java/android/support/v4/view/ViewPager.java
@@ -23,7 +23,6 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -33,12 +32,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.support.v4.widget.EdgeEffectCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.FocusFinder;
@@ -53,6 +48,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
+import android.widget.EdgeEffect;
 import android.widget.Scroller;
 
 import java.lang.annotation.ElementType;
@@ -60,7 +56,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -227,8 +222,8 @@
     private boolean mFakeDragging;
     private long mFakeDragBeginTime;
 
-    private EdgeEffectCompat mLeftEdge;
-    private EdgeEffectCompat mRightEdge;
+    private EdgeEffect mLeftEdge;
+    private EdgeEffect mRightEdge;
 
     private boolean mFirstLayout = true;
     private boolean mNeedCalculatePageOffsets = false;
@@ -241,7 +236,6 @@
     private List<OnAdapterChangeListener> mAdapterChangeListeners;
     private PageTransformer mPageTransformer;
     private int mPageTransformerLayerType;
-    private Method mSetChildrenDrawingOrderEnabled;
 
     private static final int DRAW_ORDER_DEFAULT = 0;
     private static final int DRAW_ORDER_FORWARD = 1;
@@ -409,8 +403,8 @@
         mTouchSlop = configuration.getScaledPagingTouchSlop();
         mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mLeftEdge = new EdgeEffectCompat(context);
-        mRightEdge = new EdgeEffectCompat(context);
+        mLeftEdge = new EdgeEffect(context);
+        mRightEdge = new EdgeEffect(context);
 
         mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
         mCloseEnough = (int) (CLOSE_ENOUGH * density);
@@ -751,22 +745,20 @@
      * the scroll position is changed. This allows the application to apply custom property
      * transformations to each page, overriding the default sliding behavior.
      *
-     * <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did not exist.
-     * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.
-     * By default, calling this method will cause contained pages to use
-     * {@link ViewCompat#LAYER_TYPE_HARDWARE}. This layer type allows custom alpha transformations,
+     * <p><em>Note:</em> By default, calling this method will cause contained pages to use
+     * {@link View#LAYER_TYPE_HARDWARE}. This layer type allows custom alpha transformations,
      * but it will cause issues if any of your pages contain a {@link android.view.SurfaceView}
      * and you have not called {@link android.view.SurfaceView#setZOrderOnTop(boolean)} to put that
      * {@link android.view.SurfaceView} above your app content. To disable this behavior, call
      * {@link #setPageTransformer(boolean,PageTransformer,int)} and pass
-     * {@link ViewCompat#LAYER_TYPE_NONE} for {@code pageLayerType}.</p>
+     * {@link View#LAYER_TYPE_NONE} for {@code pageLayerType}.</p>
      *
      * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
      *                            to be drawn from last to first instead of first to last.
      * @param transformer PageTransformer that will modify each page's animation properties
      */
     public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
-        setPageTransformer(reverseDrawingOrder, transformer, ViewCompat.LAYER_TYPE_HARDWARE);
+        setPageTransformer(reverseDrawingOrder, transformer, View.LAYER_TYPE_HARDWARE);
     }
 
     /**
@@ -774,51 +766,27 @@
      * the scroll position is changed. This allows the application to apply custom property
      * transformations to each page, overriding the default sliding behavior.
      *
-     * <p><em>Note:</em> Prior to Android 3.0 ({@link Build.VERSION_CODES#HONEYCOMB API 11}),
-     * the property animation APIs did not exist. As a result, setting a PageTransformer prior
-     * to API 11 will have no effect.</p>
-     *
      * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
      *                            to be drawn from last to first instead of first to last.
      * @param transformer PageTransformer that will modify each page's animation properties
      * @param pageLayerType View layer type that should be used for ViewPager pages. It should be
-     *                      either {@link ViewCompat#LAYER_TYPE_HARDWARE},
-     *                      {@link ViewCompat#LAYER_TYPE_SOFTWARE}, or
-     *                      {@link ViewCompat#LAYER_TYPE_NONE}.
+     *                      either {@link View#LAYER_TYPE_HARDWARE},
+     *                      {@link View#LAYER_TYPE_SOFTWARE}, or
+     *                      {@link View#LAYER_TYPE_NONE}.
      */
     public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer,
             int pageLayerType) {
-        if (Build.VERSION.SDK_INT >= 11) {
-            final boolean hasTransformer = transformer != null;
-            final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
-            mPageTransformer = transformer;
-            setChildrenDrawingOrderEnabledCompat(hasTransformer);
-            if (hasTransformer) {
-                mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
-                mPageTransformerLayerType = pageLayerType;
-            } else {
-                mDrawingOrder = DRAW_ORDER_DEFAULT;
-            }
-            if (needsPopulate) populate();
+        final boolean hasTransformer = transformer != null;
+        final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
+        mPageTransformer = transformer;
+        setChildrenDrawingOrderEnabled(hasTransformer);
+        if (hasTransformer) {
+            mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
+            mPageTransformerLayerType = pageLayerType;
+        } else {
+            mDrawingOrder = DRAW_ORDER_DEFAULT;
         }
-    }
-
-    void setChildrenDrawingOrderEnabledCompat(boolean enable) {
-        if (Build.VERSION.SDK_INT >= 7) {
-            if (mSetChildrenDrawingOrderEnabled == null) {
-                try {
-                    mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(
-                            "setChildrenDrawingOrderEnabled", new Class[] { Boolean.TYPE });
-                } catch (NoSuchMethodException e) {
-                    Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e);
-                }
-            }
-            try {
-                mSetChildrenDrawingOrderEnabled.invoke(this, enable);
-            } catch (Exception e) {
-                Log.e(TAG, "Error changing children drawing order", e);
-            }
-        }
+        if (needsPopulate) populate();
     }
 
     @Override
@@ -949,7 +917,7 @@
     // of travel has on the overall snap duration.
     float distanceInfluenceForSnapDuration(float f) {
         f -= 0.5f; // center the values about 0.
-        f *= 0.3f * Math.PI / 2.0f;
+        f *= 0.3f * (float) Math.PI / 2.0f;
         return (float) Math.sin(f);
     }
 
@@ -1433,17 +1401,21 @@
                     + " position=" + position + "}";
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
+
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
 
         SavedState(Parcel in, ClassLoader loader) {
             super(in, loader);
@@ -2035,8 +2007,8 @@
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             final int layerType = enable
-                    ? mPageTransformerLayerType : ViewCompat.LAYER_TYPE_NONE;
-            ViewCompat.setLayerType(getChildAt(i), layerType, null);
+                    ? mPageTransformerLayerType : View.LAYER_TYPE_NONE;
+            getChildAt(i).setLayerType(layerType, null);
         }
     }
 
@@ -2048,7 +2020,7 @@
          * scrolling there.
          */
 
-        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
+        final int action = ev.getAction() & MotionEvent.ACTION_MASK;
 
         // Always take care of the touch gesture being complete.
         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
@@ -2164,7 +2136,7 @@
                 break;
             }
 
-            case MotionEventCompat.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
                 break;
         }
@@ -2209,7 +2181,7 @@
         final int action = ev.getAction();
         boolean needsInvalidate = false;
 
-        switch (action & MotionEventCompat.ACTION_MASK) {
+        switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 mScroller.abortAnimation();
                 mPopulatePending = false;
@@ -2266,8 +2238,7 @@
                 if (mIsBeingDragged) {
                     final VelocityTracker velocityTracker = mVelocityTracker;
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
-                            velocityTracker, mActivePointerId);
+                    int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
                     mPopulatePending = true;
                     final int width = getClientWidth();
                     final int scrollX = getScrollX();
@@ -2292,14 +2263,14 @@
                     needsInvalidate = resetTouch();
                 }
                 break;
-            case MotionEventCompat.ACTION_POINTER_DOWN: {
-                final int index = MotionEventCompat.getActionIndex(ev);
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
                 final float x = ev.getX(index);
                 mLastMotionX = x;
                 mActivePointerId = ev.getPointerId(index);
                 break;
             }
-            case MotionEventCompat.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
                 mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
                 break;
@@ -2314,7 +2285,9 @@
         boolean needsInvalidate;
         mActivePointerId = INVALID_POINTER;
         endDrag();
-        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
+        mLeftEdge.onRelease();
+        mRightEdge.onRelease();
+        needsInvalidate = mLeftEdge.isFinished() || mRightEdge.isFinished();
         return needsInvalidate;
     }
 
@@ -2354,13 +2327,15 @@
         if (scrollX < leftBound) {
             if (leftAbsolute) {
                 float over = leftBound - scrollX;
-                needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
+                mLeftEdge.onPull(Math.abs(over) / width);
+                needsInvalidate = true;
             }
             scrollX = leftBound;
         } else if (scrollX > rightBound) {
             if (rightAbsolute) {
                 float over = scrollX - rightBound;
-                needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
+                mRightEdge.onPull(Math.abs(over) / width);
+                needsInvalidate = true;
             }
             scrollX = rightBound;
         }
@@ -2575,8 +2550,7 @@
         if (mAdapter != null) {
             final VelocityTracker velocityTracker = mVelocityTracker;
             velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-            int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
-                    velocityTracker, mActivePointerId);
+            int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
             mPopulatePending = true;
             final int width = getClientWidth();
             final int scrollX = getScrollX();
@@ -2659,7 +2633,7 @@
     }
 
     private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+        final int pointerIndex = ev.getActionIndex();
         final int pointerId = ev.getPointerId(pointerIndex);
         if (pointerId == mActivePointerId) {
             // This was our active pointer going up. Choose a new
@@ -2705,6 +2679,7 @@
      * @return Whether this ViewPager can be scrolled in the specified direction. It will always
      *         return false if the specified direction is 0.
      */
+    @Override
     public boolean canScrollHorizontally(int direction) {
         if (mAdapter == null) {
             return false;
@@ -2752,7 +2727,7 @@
             }
         }
 
-        return checkV && ViewCompat.canScrollHorizontally(v, -dx);
+        return checkV && v.canScrollHorizontally(-dx);
     }
 
     @Override
@@ -2774,20 +2749,24 @@
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
             switch (event.getKeyCode()) {
                 case KeyEvent.KEYCODE_DPAD_LEFT:
-                    handled = arrowScroll(FOCUS_LEFT);
+                    if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+                        handled = pageLeft();
+                    } else {
+                        handled = arrowScroll(FOCUS_LEFT);
+                    }
                     break;
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    handled = arrowScroll(FOCUS_RIGHT);
+                    if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+                        handled = pageRight();
+                    } else {
+                        handled = arrowScroll(FOCUS_RIGHT);
+                    }
                     break;
                 case KeyEvent.KEYCODE_TAB:
-                    if (Build.VERSION.SDK_INT >= 11) {
-                        // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD
-                        // before Android 3.0. Ignore the tab key on those devices.
-                        if (KeyEventCompat.hasNoModifiers(event)) {
-                            handled = arrowScroll(FOCUS_FORWARD);
-                        } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
-                            handled = arrowScroll(FOCUS_BACKWARD);
-                        }
+                    if (event.hasNoModifiers()) {
+                        handled = arrowScroll(FOCUS_FORWARD);
+                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+                        handled = arrowScroll(FOCUS_BACKWARD);
                     }
                     break;
             }
@@ -3053,14 +3032,11 @@
         public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
             super.onInitializeAccessibilityEvent(host, event);
             event.setClassName(ViewPager.class.getName());
-            final AccessibilityRecordCompat recordCompat =
-                    AccessibilityEventCompat.asRecord(event);
-            recordCompat.setScrollable(canScroll());
-            if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED
-                    && mAdapter != null) {
-                recordCompat.setItemCount(mAdapter.getCount());
-                recordCompat.setFromIndex(mCurItem);
-                recordCompat.setToIndex(mCurItem);
+            event.setScrollable(canScroll());
+            if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
+                event.setItemCount(mAdapter.getCount());
+                event.setFromIndex(mCurItem);
+                event.setToIndex(mCurItem);
             }
         }
 
diff --git a/core-ui/java/android/support/v4/widget/AutoScrollHelper.java b/core-ui/java/android/support/v4/widget/AutoScrollHelper.java
index 4291a25..d0407be 100644
--- a/core-ui/java/android/support/v4/widget/AutoScrollHelper.java
+++ b/core-ui/java/android/support/v4/widget/AutoScrollHelper.java
@@ -18,7 +18,6 @@
 
 import android.content.res.Resources;
 import android.os.SystemClock;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
@@ -457,7 +456,7 @@
             return false;
         }
 
-        final int action = MotionEventCompat.getActionMasked(event);
+        final int action = event.getActionMasked();
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mNeedsCancel = true;
@@ -493,8 +492,8 @@
         final int verticalDirection = scroller.getVerticalDirection();
         final int horizontalDirection = scroller.getHorizontalDirection();
 
-        return verticalDirection != 0 && canTargetScrollVertically(verticalDirection)
-                || horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection);
+        return (verticalDirection != 0 && canTargetScrollVertically(verticalDirection))
+                || (horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection));
     }
 
     /**
diff --git a/core-ui/java/android/support/v4/widget/CircleImageView.java b/core-ui/java/android/support/v4/widget/CircleImageView.java
index e582882..24a175d 100644
--- a/core-ui/java/android/support/v4/widget/CircleImageView.java
+++ b/core-ui/java/android/support/v4/widget/CircleImageView.java
@@ -26,6 +26,7 @@
 import android.graphics.drawable.shapes.OvalShape;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewCompat;
+import android.view.View;
 import android.view.animation.Animation;
 import android.widget.ImageView;
 
@@ -62,7 +63,7 @@
         } else {
             OvalShape oval = new OvalShadow(mShadowRadius);
             circle = new ShapeDrawable(oval);
-            ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint());
+            setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint());
             circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
                     KEY_SHADOW_COLOR);
             final int padding = mShadowRadius;
diff --git a/core-ui/java/android/support/v4/widget/CircularProgressDrawable.java b/core-ui/java/android/support/v4/widget/CircularProgressDrawable.java
new file mode 100644
index 0000000..ac29541
--- /dev/null
+++ b/core-ui/java/android/support/v4/widget/CircularProgressDrawable.java
@@ -0,0 +1,945 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.v4.util.Preconditions;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.util.DisplayMetrics;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Drawable that renders the animated indeterminate progress indicator in the Material design style
+ * without depending on API level 11.
+ *
+ * <p>While this may be used to draw an indeterminate spinner using {@link #start()} and {@link
+ * #stop()} methods, this may also be used to draw a progress arc using {@link
+ * #setStartEndTrim(float, float)} method. CircularProgressDrawable also supports adding an arrow
+ * at the end of the arc by {@link #setArrowEnabled(boolean)} and {@link #setArrowDimensions(float,
+ * float)} methods.
+ *
+ * <p>To use one of the pre-defined sizes instead of using your own, {@link #setStyle(int)} should
+ * be called with one of the {@link #DEFAULT} or {@link #LARGE} styles as its parameter. Doing it
+ * so will update the arrow dimensions, ring size and stroke width to fit the one specified.
+ *
+ * <p>If no center radius is set via {@link #setCenterRadius(float)} or {@link #setStyle(int)}
+ * methods, CircularProgressDrawable will fill the bounds set via {@link #setBounds(Rect)}.
+ */
+public class CircularProgressDrawable extends Drawable implements Animatable {
+    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+    private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({LARGE, DEFAULT})
+    public @interface ProgressDrawableSize {
+    }
+
+    /** Maps to ProgressBar.Large style. */
+    public static final int LARGE = 0;
+
+    private static final float CENTER_RADIUS_LARGE = 11f;
+    private static final float STROKE_WIDTH_LARGE = 3f;
+    private static final int ARROW_WIDTH_LARGE = 12;
+    private static final int ARROW_HEIGHT_LARGE = 6;
+
+    /** Maps to ProgressBar default style. */
+    public static final int DEFAULT = 1;
+
+    private static final float CENTER_RADIUS = 7.5f;
+    private static final float STROKE_WIDTH = 2.5f;
+    private static final int ARROW_WIDTH = 10;
+    private static final int ARROW_HEIGHT = 5;
+
+    /**
+     * This is the default set of colors that's used in spinner. {@link
+     * #setColorSchemeColors(int...)} allows modifying colors.
+     */
+    private static final int[] COLORS = new int[]{
+            Color.BLACK
+    };
+
+    /**
+     * The value in the linear interpolator for animating the drawable at which
+     * the color transition should start
+     */
+    private static final float COLOR_CHANGE_OFFSET = 0.75f;
+    private static final float SHRINK_OFFSET = 0.5f;
+
+    /** The duration of a single progress spin in milliseconds. */
+    private static final int ANIMATION_DURATION = 1332;
+
+    /** Full rotation that's done for the animation duration in degrees. */
+    private static final float GROUP_FULL_ROTATION = 1080f / 5f;
+
+    /** The indicator ring, used to manage animation state. */
+    private final Ring mRing;
+
+    /** Canvas rotation in degrees. */
+    private float mRotation;
+
+    /** Maximum length of the progress arc during the animation. */
+    private static final float MAX_PROGRESS_ARC = .8f;
+    /** Minimum length of the progress arc during the animation. */
+    private static final float MIN_PROGRESS_ARC = .01f;
+
+    /** Rotation applied to ring during the animation, to complete it to a full circle. */
+    private static final float RING_ROTATION = 1f - (MAX_PROGRESS_ARC - MIN_PROGRESS_ARC);
+
+    private Resources mResources;
+    private Animator mAnimator;
+    private float mRotationCount;
+    private boolean mFinishing;
+
+    /**
+     * @param context application context
+     */
+    public CircularProgressDrawable(Context context) {
+        mResources = Preconditions.checkNotNull(context).getResources();
+
+        mRing = new Ring();
+        mRing.setColors(COLORS);
+
+        setStrokeWidth(STROKE_WIDTH);
+        setupAnimators();
+    }
+
+    /** Sets all parameters at once in dp. */
+    private void setSizeParameters(float centerRadius, float strokeWidth, float arrowWidth,
+            float arrowHeight) {
+        final Ring ring = mRing;
+        final DisplayMetrics metrics = mResources.getDisplayMetrics();
+        final float screenDensity = metrics.density;
+
+        ring.setStrokeWidth(strokeWidth * screenDensity);
+        ring.setCenterRadius(centerRadius * screenDensity);
+        ring.setColorIndex(0);
+        ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
+    }
+
+    /**
+     * Sets the overall size for the progress spinner. This updates the radius
+     * and stroke width of the ring, and arrow dimensions.
+     *
+     * @param size one of {@link #LARGE} or {@link #DEFAULT}
+     */
+    public void setStyle(@ProgressDrawableSize int size) {
+        if (size == LARGE) {
+            setSizeParameters(CENTER_RADIUS_LARGE, STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE,
+                    ARROW_HEIGHT_LARGE);
+        } else {
+            setSizeParameters(CENTER_RADIUS, STROKE_WIDTH, ARROW_WIDTH, ARROW_HEIGHT);
+        }
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the stroke width for the progress spinner in pixels.
+     *
+     * @return stroke width in pixels
+     */
+    public float getStrokeWidth() {
+        return mRing.getStrokeWidth();
+    }
+
+    /**
+     * Sets the stroke width for the progress spinner in pixels.
+     *
+     * @param strokeWidth stroke width in pixels
+     */
+    public void setStrokeWidth(float strokeWidth) {
+        mRing.setStrokeWidth(strokeWidth);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the center radius for the progress spinner in pixels.
+     *
+     * @return center radius in pixels
+     */
+    public float getCenterRadius() {
+        return mRing.getCenterRadius();
+    }
+
+    /**
+     * Sets the center radius for the progress spinner in pixels. If set to 0, this drawable will
+     * fill the bounds when drawn.
+     *
+     * @param centerRadius center radius in pixels
+     */
+    public void setCenterRadius(float centerRadius) {
+        mRing.setCenterRadius(centerRadius);
+        invalidateSelf();
+    }
+
+    /**
+     * Sets the stroke cap of the progress spinner. Default stroke cap is {@link Paint.Cap#SQUARE}.
+     *
+     * @param strokeCap stroke cap
+     */
+    public void setStrokeCap(Paint.Cap strokeCap) {
+        mRing.setStrokeCap(strokeCap);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the stroke cap of the progress spinner.
+     *
+     * @return stroke cap
+     */
+    public Paint.Cap getStrokeCap() {
+        return mRing.getStrokeCap();
+    }
+
+    /**
+     * Returns the arrow width in pixels.
+     *
+     * @return arrow width in pixels
+     */
+    public float getArrowWidth() {
+        return mRing.getArrowWidth();
+    }
+
+    /**
+     * Returns the arrow height in pixels.
+     *
+     * @return arrow height in pixels
+     */
+    public float getArrowHeight() {
+        return mRing.getArrowHeight();
+    }
+
+    /**
+     * Sets the dimensions of the arrow at the end of the spinner in pixels.
+     *
+     * @param width width of the baseline of the arrow in pixels
+     * @param height distance from tip of the arrow to its baseline in pixels
+     */
+    public void setArrowDimensions(float width, float height) {
+        mRing.setArrowDimensions(width, height);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns {@code true} if the arrow at the end of the spinner is shown.
+     *
+     * @return {@code true} if the arrow is shown, {@code false} otherwise.
+     */
+    public boolean getArrowEnabled() {
+        return mRing.getShowArrow();
+    }
+
+    /**
+     * Sets if the arrow at the end of the spinner should be shown.
+     *
+     * @param show {@code true} if the arrow should be drawn, {@code false} otherwise
+     */
+    public void setArrowEnabled(boolean show) {
+        mRing.setShowArrow(show);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the scale of the arrow at the end of the spinner.
+     *
+     * @return scale of the arrow
+     */
+    public float getArrowScale() {
+        return mRing.getArrowScale();
+    }
+
+    /**
+     * Sets the scale of the arrow at the end of the spinner.
+     *
+     * @param scale scaling that will be applied to the arrow's both width and height when drawing.
+     */
+    public void setArrowScale(float scale) {
+        mRing.setArrowScale(scale);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the start trim for the progress spinner arc
+     *
+     * @return start trim from [0..1]
+     */
+    public float getStartTrim() {
+        return mRing.getStartTrim();
+    }
+
+    /**
+     * Returns the end trim for the progress spinner arc
+     *
+     * @return end trim from [0..1]
+     */
+    public float getEndTrim() {
+        return mRing.getEndTrim();
+    }
+
+    /**
+     * Sets the start and end trim for the progress spinner arc. 0 corresponds to the geometric
+     * angle of 0 degrees (3 o'clock on a watch) and it increases clockwise, coming to a full circle
+     * at 1.
+     *
+     * @param start starting position of the arc from [0..1]
+     * @param end ending position of the arc from [0..1]
+     */
+    public void setStartEndTrim(float start, float end) {
+        mRing.setStartTrim(start);
+        mRing.setEndTrim(end);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the amount of rotation applied to the progress spinner.
+     *
+     * @return amount of rotation from [0..1]
+     */
+    public float getProgressRotation() {
+        return mRing.getRotation();
+    }
+
+    /**
+     * Sets the amount of rotation to apply to the progress spinner.
+     *
+     * @param rotation rotation from [0..1]
+     */
+    public void setProgressRotation(float rotation) {
+        mRing.setRotation(rotation);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the background color of the circle drawn inside the drawable.
+     *
+     * @return an ARGB color
+     */
+    public int getBackgroundColor() {
+        return mRing.getBackgroundColor();
+    }
+
+    /**
+     * Sets the background color of the circle inside the drawable. Calling {@link
+     * #setAlpha(int)} does not affect the visibility background color, so it should be set
+     * separately if it needs to be hidden or visible.
+     *
+     * @param color an ARGB color
+     */
+    public void setBackgroundColor(int color) {
+        mRing.setBackgroundColor(color);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the colors used in the progress animation
+     *
+     * @return list of ARGB colors
+     */
+    public int[] getColorSchemeColors() {
+        return mRing.getColors();
+    }
+
+    /**
+     * Sets the colors used in the progress animation from a color list. The first color will also
+     * be the color to be used if animation is not started yet.
+     *
+     * @param colors list of ARGB colors to be used in the spinner
+     */
+    public void setColorSchemeColors(int... colors) {
+        mRing.setColors(colors);
+        mRing.setColorIndex(0);
+        invalidateSelf();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Rect bounds = getBounds();
+        canvas.save();
+        canvas.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
+        mRing.draw(canvas, bounds);
+        canvas.restore();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mRing.setAlpha(alpha);
+        invalidateSelf();
+    }
+
+    @Override
+    public int getAlpha() {
+        return mRing.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mRing.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    private void setRotation(float rotation) {
+        mRotation = rotation;
+    }
+
+    private float getRotation() {
+        return mRotation;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mAnimator.isRunning();
+    }
+
+    /**
+     * Starts the animation for the spinner.
+     */
+    @Override
+    public void start() {
+        mAnimator.cancel();
+        mRing.storeOriginals();
+        // Already showing some part of the ring
+        if (mRing.getEndTrim() != mRing.getStartTrim()) {
+            mFinishing = true;
+            mAnimator.setDuration(ANIMATION_DURATION / 2);
+            mAnimator.start();
+        } else {
+            mRing.setColorIndex(0);
+            mRing.resetOriginals();
+            mAnimator.setDuration(ANIMATION_DURATION);
+            mAnimator.start();
+        }
+    }
+
+    /**
+     * Stops the animation for the spinner.
+     */
+    @Override
+    public void stop() {
+        mAnimator.cancel();
+        setRotation(0);
+        mRing.setShowArrow(false);
+        mRing.setColorIndex(0);
+        mRing.resetOriginals();
+        invalidateSelf();
+    }
+
+    // Adapted from ArgbEvaluator.java
+    private int evaluateColorChange(float fraction, int startValue, int endValue) {
+        int startA = (startValue >> 24) & 0xff;
+        int startR = (startValue >> 16) & 0xff;
+        int startG = (startValue >> 8) & 0xff;
+        int startB = startValue & 0xff;
+
+        int endA = (endValue >> 24) & 0xff;
+        int endR = (endValue >> 16) & 0xff;
+        int endG = (endValue >> 8) & 0xff;
+        int endB = endValue & 0xff;
+
+        return (startA + (int) (fraction * (endA - startA))) << 24
+                | (startR + (int) (fraction * (endR - startR))) << 16
+                | (startG + (int) (fraction * (endG - startG))) << 8
+                | (startB + (int) (fraction * (endB - startB)));
+    }
+
+    /**
+     * Update the ring color if this is within the last 25% of the animation.
+     * The new ring color will be a translation from the starting ring color to
+     * the next color.
+     */
+    private void updateRingColor(float interpolatedTime, Ring ring) {
+        if (interpolatedTime > COLOR_CHANGE_OFFSET) {
+            ring.setColor(evaluateColorChange((interpolatedTime - COLOR_CHANGE_OFFSET)
+                            / (1f - COLOR_CHANGE_OFFSET), ring.getStartingColor(),
+                    ring.getNextColor()));
+        } else {
+            ring.setColor(ring.getStartingColor());
+        }
+    }
+
+    /**
+     * Update the ring start and end trim if the animation is finishing (i.e. it started with
+     * already visible progress, so needs to shrink back down before starting the spinner).
+     */
+    private void applyFinishTranslation(float interpolatedTime, Ring ring) {
+        // shrink back down and complete a full rotation before
+        // starting other circles
+        // Rotation goes between [0..1].
+        updateRingColor(interpolatedTime, ring);
+        float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)
+                + 1f);
+        final float startTrim = ring.getStartingStartTrim()
+                + (ring.getStartingEndTrim() - MIN_PROGRESS_ARC - ring.getStartingStartTrim())
+                * interpolatedTime;
+        ring.setStartTrim(startTrim);
+        ring.setEndTrim(ring.getStartingEndTrim());
+        final float rotation = ring.getStartingRotation()
+                + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
+        ring.setRotation(rotation);
+    }
+
+    /**
+     * Update the ring start and end trim according to current time of the animation.
+     */
+    private void applyTransformation(float interpolatedTime, Ring ring, boolean lastFrame) {
+        if (mFinishing) {
+            applyFinishTranslation(interpolatedTime, ring);
+            // Below condition is to work around a ValueAnimator issue where onAnimationRepeat is
+            // called before last frame (1f).
+        } else if (interpolatedTime != 1f || lastFrame) {
+            final float startingRotation = ring.getStartingRotation();
+            float startTrim, endTrim;
+
+            if (interpolatedTime < SHRINK_OFFSET) { // Expansion occurs on first half of animation
+                final float scaledTime = interpolatedTime / SHRINK_OFFSET;
+                startTrim = ring.getStartingStartTrim();
+                endTrim = startTrim + ((MAX_PROGRESS_ARC - MIN_PROGRESS_ARC)
+                        * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime) + MIN_PROGRESS_ARC);
+            } else { // Shrinking occurs on second half of animation
+                float scaledTime = (interpolatedTime - SHRINK_OFFSET) / (1f - SHRINK_OFFSET);
+                endTrim = ring.getStartingStartTrim() + (MAX_PROGRESS_ARC - MIN_PROGRESS_ARC);
+                startTrim = endTrim - ((MAX_PROGRESS_ARC - MIN_PROGRESS_ARC)
+                        * (1f - MATERIAL_INTERPOLATOR.getInterpolation(scaledTime))
+                        + MIN_PROGRESS_ARC);
+            }
+
+            final float rotation = startingRotation + (RING_ROTATION * interpolatedTime);
+            float groupRotation = GROUP_FULL_ROTATION * (interpolatedTime + mRotationCount);
+
+            ring.setStartTrim(startTrim);
+            ring.setEndTrim(endTrim);
+            ring.setRotation(rotation);
+            setRotation(groupRotation);
+        }
+    }
+
+    private void setupAnimators() {
+        final Ring ring = mRing;
+        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float interpolatedTime = (float) animation.getAnimatedValue();
+                updateRingColor(interpolatedTime, ring);
+                applyTransformation(interpolatedTime, ring, false);
+                invalidateSelf();
+            }
+        });
+        animator.setRepeatCount(ValueAnimator.INFINITE);
+        animator.setRepeatMode(ValueAnimator.RESTART);
+        animator.setInterpolator(LINEAR_INTERPOLATOR);
+        animator.addListener(new Animator.AnimatorListener() {
+
+            @Override
+            public void onAnimationStart(Animator animator) {
+                mRotationCount = 0;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                // do nothing
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                // do nothing
+            }
+
+            @Override
+            public void onAnimationRepeat(Animator animator) {
+                applyTransformation(1f, ring, true);
+                ring.storeOriginals();
+                ring.goToNextColor();
+                if (mFinishing) {
+                    // finished closing the last ring from the swipe gesture; go
+                    // into progress mode
+                    mFinishing = false;
+                    animator.cancel();
+                    animator.setDuration(ANIMATION_DURATION);
+                    animator.start();
+                    ring.setShowArrow(false);
+                } else {
+                    mRotationCount = mRotationCount + 1;
+                }
+            }
+        });
+        mAnimator = animator;
+    }
+
+    /**
+     * A private class to do all the drawing of CircularProgressDrawable, which includes background,
+     * progress spinner and the arrow. This class is to separate drawing from animation.
+     */
+    private static class Ring {
+        final RectF mTempBounds = new RectF();
+        final Paint mPaint = new Paint();
+        final Paint mArrowPaint = new Paint();
+        final Paint mCirclePaint = new Paint();
+
+        float mStartTrim = 0f;
+        float mEndTrim = 0f;
+        float mRotation = 0f;
+        float mStrokeWidth = 5f;
+
+        int[] mColors;
+        // mColorIndex represents the offset into the available mColors that the
+        // progress circle should currently display. As the progress circle is
+        // animating, the mColorIndex moves by one to the next available color.
+        int mColorIndex;
+        float mStartingStartTrim;
+        float mStartingEndTrim;
+        float mStartingRotation;
+        boolean mShowArrow;
+        Path mArrow;
+        float mArrowScale = 1;
+        float mRingCenterRadius;
+        int mArrowWidth;
+        int mArrowHeight;
+        int mAlpha = 255;
+        int mCurrentColor;
+
+        Ring() {
+            mPaint.setStrokeCap(Paint.Cap.SQUARE);
+            mPaint.setAntiAlias(true);
+            mPaint.setStyle(Style.STROKE);
+
+            mArrowPaint.setStyle(Paint.Style.FILL);
+            mArrowPaint.setAntiAlias(true);
+
+            mCirclePaint.setColor(Color.TRANSPARENT);
+        }
+
+        /**
+         * Sets the dimensions of the arrowhead.
+         *
+         * @param width width of the hypotenuse of the arrow head
+         * @param height height of the arrow point
+         */
+        void setArrowDimensions(float width, float height) {
+            mArrowWidth = (int) width;
+            mArrowHeight = (int) height;
+        }
+
+        void setStrokeCap(Paint.Cap strokeCap) {
+            mPaint.setStrokeCap(strokeCap);
+        }
+
+        Paint.Cap getStrokeCap() {
+            return mPaint.getStrokeCap();
+        }
+
+        float getArrowWidth() {
+            return mArrowWidth;
+        }
+
+        float getArrowHeight() {
+            return mArrowHeight;
+        }
+
+        /**
+         * Draw the progress spinner
+         */
+        void draw(Canvas c, Rect bounds) {
+            final RectF arcBounds = mTempBounds;
+            float arcRadius = mRingCenterRadius + mStrokeWidth / 2f;
+            if (mRingCenterRadius <= 0) {
+                // If center radius is not set, fill the bounds
+                arcRadius = Math.min(bounds.width(), bounds.height()) / 2f - Math.max(
+                        (mArrowWidth * mArrowScale) / 2f, mStrokeWidth / 2f);
+            }
+            arcBounds.set(bounds.centerX() - arcRadius,
+                    bounds.centerY() - arcRadius,
+                    bounds.centerX() + arcRadius,
+                    bounds.centerY() + arcRadius);
+
+            final float startAngle = (mStartTrim + mRotation) * 360;
+            final float endAngle = (mEndTrim + mRotation) * 360;
+            float sweepAngle = endAngle - startAngle;
+
+            mPaint.setColor(mCurrentColor);
+            mPaint.setAlpha(mAlpha);
+
+            // Draw the background first
+            float inset = mStrokeWidth / 2f; // Calculate inset to draw inside the arc
+            arcBounds.inset(inset, inset); // Apply inset
+            c.drawCircle(arcBounds.centerX(), arcBounds.centerY(), arcBounds.width() / 2f,
+                    mCirclePaint);
+            arcBounds.inset(-inset, -inset); // Revert the inset
+
+            c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
+
+            drawTriangle(c, startAngle, sweepAngle, arcBounds);
+        }
+
+        void drawTriangle(Canvas c, float startAngle, float sweepAngle, RectF bounds) {
+            if (mShowArrow) {
+                if (mArrow == null) {
+                    mArrow = new android.graphics.Path();
+                    mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD);
+                } else {
+                    mArrow.reset();
+                }
+                float centerRadius = Math.min(bounds.width(), bounds.height()) / 2f;
+                float inset = mArrowWidth * mArrowScale / 2f;
+                // Update the path each time. This works around an issue in SKIA
+                // where concatenating a rotation matrix to a scale matrix
+                // ignored a starting negative rotation. This appears to have
+                // been fixed as of API 21.
+                mArrow.moveTo(0, 0);
+                mArrow.lineTo(mArrowWidth * mArrowScale, 0);
+                mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
+                        * mArrowScale));
+                mArrow.offset(centerRadius + bounds.centerX() - inset,
+                        bounds.centerY() + mStrokeWidth / 2f);
+                mArrow.close();
+                // draw a triangle
+                mArrowPaint.setColor(mCurrentColor);
+                mArrowPaint.setAlpha(mAlpha);
+                c.save();
+                c.rotate(startAngle + sweepAngle, bounds.centerX(),
+                        bounds.centerY());
+                c.drawPath(mArrow, mArrowPaint);
+                c.restore();
+            }
+        }
+
+        /**
+         * Sets the colors the progress spinner alternates between.
+         *
+         * @param colors array of ARGB colors. Must be non-{@code null}.
+         */
+        void setColors(@NonNull int[] colors) {
+            mColors = colors;
+            // if colors are reset, make sure to reset the color index as well
+            setColorIndex(0);
+        }
+
+        int[] getColors() {
+            return mColors;
+        }
+
+        /**
+         * Sets the absolute color of the progress spinner. This is should only
+         * be used when animating between current and next color when the
+         * spinner is rotating.
+         *
+         * @param color an ARGB color
+         */
+        void setColor(int color) {
+            mCurrentColor = color;
+        }
+
+        /**
+         * Sets the background color of the circle inside the spinner.
+         */
+        void setBackgroundColor(int color) {
+            mCirclePaint.setColor(color);
+        }
+
+        int getBackgroundColor() {
+            return mCirclePaint.getColor();
+        }
+
+        /**
+         * @param index index into the color array of the color to display in
+         *              the progress spinner.
+         */
+        void setColorIndex(int index) {
+            mColorIndex = index;
+            mCurrentColor = mColors[mColorIndex];
+        }
+
+        /**
+         * @return int describing the next color the progress spinner should use when drawing.
+         */
+        int getNextColor() {
+            return mColors[getNextColorIndex()];
+        }
+
+        int getNextColorIndex() {
+            return (mColorIndex + 1) % (mColors.length);
+        }
+
+        /**
+         * Proceed to the next available ring color. This will automatically
+         * wrap back to the beginning of colors.
+         */
+        void goToNextColor() {
+            setColorIndex(getNextColorIndex());
+        }
+
+        void setColorFilter(ColorFilter filter) {
+            mPaint.setColorFilter(filter);
+        }
+
+        /**
+         * @param alpha alpha of the progress spinner and associated arrowhead.
+         */
+        void setAlpha(int alpha) {
+            mAlpha = alpha;
+        }
+
+        /**
+         * @return current alpha of the progress spinner and arrowhead
+         */
+        int getAlpha() {
+            return mAlpha;
+        }
+
+        /**
+         * @param strokeWidth set the stroke width of the progress spinner in pixels.
+         */
+        void setStrokeWidth(float strokeWidth) {
+            mStrokeWidth = strokeWidth;
+            mPaint.setStrokeWidth(strokeWidth);
+        }
+
+        float getStrokeWidth() {
+            return mStrokeWidth;
+        }
+
+        void setStartTrim(float startTrim) {
+            mStartTrim = startTrim;
+        }
+
+        float getStartTrim() {
+            return mStartTrim;
+        }
+
+        float getStartingStartTrim() {
+            return mStartingStartTrim;
+        }
+
+        float getStartingEndTrim() {
+            return mStartingEndTrim;
+        }
+
+        int getStartingColor() {
+            return mColors[mColorIndex];
+        }
+
+        void setEndTrim(float endTrim) {
+            mEndTrim = endTrim;
+        }
+
+        float getEndTrim() {
+            return mEndTrim;
+        }
+
+        void setRotation(float rotation) {
+            mRotation = rotation;
+        }
+
+        float getRotation() {
+            return mRotation;
+        }
+
+        /**
+         * @param centerRadius inner radius in px of the circle the progress spinner arc traces
+         */
+        void setCenterRadius(float centerRadius) {
+            mRingCenterRadius = centerRadius;
+        }
+
+        float getCenterRadius() {
+            return mRingCenterRadius;
+        }
+
+        /**
+         * @param show {@code true} if should show the arrow head on the progress spinner
+         */
+        void setShowArrow(boolean show) {
+            if (mShowArrow != show) {
+                mShowArrow = show;
+            }
+        }
+
+        boolean getShowArrow() {
+            return mShowArrow;
+        }
+
+        /**
+         * @param scale scale of the arrowhead for the spinner
+         */
+        void setArrowScale(float scale) {
+            if (scale != mArrowScale) {
+                mArrowScale = scale;
+            }
+        }
+
+        float getArrowScale() {
+            return mArrowScale;
+        }
+
+        /**
+         * @return The amount the progress spinner is currently rotated, between [0..1].
+         */
+        float getStartingRotation() {
+            return mStartingRotation;
+        }
+
+        /**
+         * If the start / end trim are offset to begin with, store them so that animation starts
+         * from that offset.
+         */
+        void storeOriginals() {
+            mStartingStartTrim = mStartTrim;
+            mStartingEndTrim = mEndTrim;
+            mStartingRotation = mRotation;
+        }
+
+        /**
+         * Reset the progress spinner to default rotation, start and end angles.
+         */
+        void resetOriginals() {
+            mStartingStartTrim = 0;
+            mStartingEndTrim = 0;
+            mStartingRotation = 0;
+            setStartTrim(0);
+            setEndTrim(0);
+            setRotation(0);
+        }
+    }
+}
diff --git a/core-ui/java/android/support/v4/widget/DrawerLayout.java b/core-ui/java/android/support/v4/widget/DrawerLayout.java
index f48b036..c7b40e9 100644
--- a/core-ui/java/android/support/v4/widget/DrawerLayout.java
+++ b/core-ui/java/android/support/v4/widget/DrawerLayout.java
@@ -17,6 +17,9 @@
 
 package android.support.v4.widget;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -34,14 +37,12 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.GravityCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewGroupCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -53,6 +54,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 
 import java.lang.annotation.Retention;
@@ -93,9 +95,13 @@
  * href="{@docRoot}training/implementing-navigation/nav-drawer.html">Creating a Navigation
  * Drawer</a>.</p>
  */
-public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl {
+public class DrawerLayout extends ViewGroup {
     private static final String TAG = "DrawerLayout";
 
+    private static final int[] THEME_ATTRS = {
+            android.R.attr.colorPrimaryDark
+    };
+
     @IntDef({STATE_IDLE, STATE_DRAGGING, STATE_SETTLING})
     @Retention(RetentionPolicy.SOURCE)
     private @interface State {}
@@ -142,7 +148,8 @@
      */
     public static final int LOCK_MODE_UNDEFINED = 3;
 
-    @IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END})
+    @IntDef(value = {Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END},
+            flag = true)
     @Retention(RetentionPolicy.SOURCE)
     private @interface EdgeGravity {}
 
@@ -289,79 +296,6 @@
         }
     }
 
-    interface DrawerLayoutCompatImpl {
-        void configureApplyInsets(View drawerLayout);
-        void dispatchChildInsets(View child, Object insets, int drawerGravity);
-        void applyMarginInsets(MarginLayoutParams lp, Object insets, int drawerGravity);
-        int getTopInset(Object lastInsets);
-        Drawable getDefaultStatusBarBackground(Context context);
-    }
-
-    static class DrawerLayoutCompatImplBase implements DrawerLayoutCompatImpl {
-        @Override
-        public void configureApplyInsets(View drawerLayout) {
-            // This space for rent
-        }
-
-        @Override
-        public void dispatchChildInsets(View child, Object insets, int drawerGravity) {
-            // This space for rent
-        }
-
-        @Override
-        public void applyMarginInsets(MarginLayoutParams lp, Object insets, int drawerGravity) {
-            // This space for rent
-        }
-
-        @Override
-        public int getTopInset(Object insets) {
-            return 0;
-        }
-
-        @Override
-        public Drawable getDefaultStatusBarBackground(Context context) {
-            return null;
-        }
-    }
-
-    static class DrawerLayoutCompatImplApi21 implements DrawerLayoutCompatImpl {
-        @Override
-        public void configureApplyInsets(View drawerLayout) {
-            DrawerLayoutCompatApi21.configureApplyInsets(drawerLayout);
-        }
-
-        @Override
-        public void dispatchChildInsets(View child, Object insets, int drawerGravity) {
-            DrawerLayoutCompatApi21.dispatchChildInsets(child, insets, drawerGravity);
-        }
-
-        @Override
-        public void applyMarginInsets(MarginLayoutParams lp, Object insets, int drawerGravity) {
-            DrawerLayoutCompatApi21.applyMarginInsets(lp, insets, drawerGravity);
-        }
-
-        @Override
-        public int getTopInset(Object insets) {
-            return DrawerLayoutCompatApi21.getTopInset(insets);
-        }
-
-        @Override
-        public Drawable getDefaultStatusBarBackground(Context context) {
-            return DrawerLayoutCompatApi21.getDefaultStatusBarBackground(context);
-        }
-    }
-
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new DrawerLayoutCompatImplApi21();
-        } else {
-            IMPL = new DrawerLayoutCompatImplBase();
-        }
-    }
-
-    static final DrawerLayoutCompatImpl IMPL;
-
     public DrawerLayout(Context context) {
         this(context, null);
     }
@@ -399,8 +333,27 @@
         ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
         ViewGroupCompat.setMotionEventSplittingEnabled(this, false);
         if (ViewCompat.getFitsSystemWindows(this)) {
-            IMPL.configureApplyInsets(this);
-            mStatusBarBackground = IMPL.getDefaultStatusBarBackground(context);
+            if (Build.VERSION.SDK_INT >= 21) {
+                setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+                    @TargetApi(21)
+                    @Override
+                    public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
+                        final DrawerLayout drawerLayout = (DrawerLayout) view;
+                        drawerLayout.setChildInsets(insets, insets.getSystemWindowInsetTop() > 0);
+                        return insets.consumeSystemWindowInsets();
+                    }
+                });
+                setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+                final TypedArray a = context.obtainStyledAttributes(THEME_ATTRS);
+                try {
+                    mStatusBarBackground = a.getDrawable(0);
+                } finally {
+                    a.recycle();
+                }
+            } else {
+                mStatusBarBackground = null;
+            }
         }
 
         mDrawerElevation = DRAWER_ELEVATION * density;
@@ -442,7 +395,7 @@
      * @hide Internal use only; called to apply window insets when configured
      * with fitsSystemWindows="true"
      */
-    @Override
+    @RestrictTo(LIBRARY_GROUP)
     public void setChildInsets(Object insets, boolean draw) {
         mLastInsets = insets;
         mDrawStatusBarBackground = draw;
@@ -889,8 +842,7 @@
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
-            if (!isDrawerOpen && !isDrawerView(child)
-                    || isDrawerOpen && child == drawerView) {
+            if ((!isDrawerOpen && !isDrawerView(child)) || (isDrawerOpen && child == drawerView)) {
                 // Drawer is closed and this is a content view or this is an
                 // open drawer view, so it should be visible.
                 ViewCompat.setImportantForAccessibility(child,
@@ -1066,9 +1018,36 @@
             if (applyInsets) {
                 final int cgrav = GravityCompat.getAbsoluteGravity(lp.gravity, layoutDirection);
                 if (ViewCompat.getFitsSystemWindows(child)) {
-                    IMPL.dispatchChildInsets(child, mLastInsets, cgrav);
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        WindowInsets wi = (WindowInsets) mLastInsets;
+                        if (cgrav == Gravity.LEFT) {
+                            wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(),
+                                    wi.getSystemWindowInsetTop(), 0,
+                                    wi.getSystemWindowInsetBottom());
+                        } else if (cgrav == Gravity.RIGHT) {
+                            wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(),
+                                    wi.getSystemWindowInsetRight(),
+                                    wi.getSystemWindowInsetBottom());
+                        }
+                        child.dispatchApplyWindowInsets(wi);
+                    }
                 } else {
-                    IMPL.applyMarginInsets(lp, mLastInsets, cgrav);
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        WindowInsets wi = (WindowInsets) mLastInsets;
+                        if (cgrav == Gravity.LEFT) {
+                            wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(),
+                                    wi.getSystemWindowInsetTop(), 0,
+                                    wi.getSystemWindowInsetBottom());
+                        } else if (cgrav == Gravity.RIGHT) {
+                            wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(),
+                                    wi.getSystemWindowInsetRight(),
+                                    wi.getSystemWindowInsetBottom());
+                        }
+                        lp.leftMargin = wi.getSystemWindowInsetLeft();
+                        lp.topMargin = wi.getSystemWindowInsetTop();
+                        lp.rightMargin = wi.getSystemWindowInsetRight();
+                        lp.bottomMargin = wi.getSystemWindowInsetBottom();
+                    }
                 }
             }
 
@@ -1276,8 +1255,9 @@
         }
         mScrimOpacity = scrimOpacity;
 
-        // "|" used on purpose; both need to run.
-        if (mLeftDragger.continueSettling(true) | mRightDragger.continueSettling(true)) {
+        boolean leftDraggerSettling = mLeftDragger.continueSettling(true);
+        boolean rightDraggerSettling = mRightDragger.continueSettling(true);
+        if (leftDraggerSettling || rightDraggerSettling) {
             ViewCompat.postInvalidateOnAnimation(this);
         }
     }
@@ -1333,6 +1313,7 @@
         invalidate();
     }
 
+    @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         resolveShadowDrawables();
     }
@@ -1341,7 +1322,13 @@
     public void onDraw(Canvas c) {
         super.onDraw(c);
         if (mDrawStatusBarBackground && mStatusBarBackground != null) {
-            final int inset = IMPL.getTopInset(mLastInsets);
+            final int inset;
+            if (Build.VERSION.SDK_INT >= 21) {
+                inset = mLastInsets != null
+                        ? ((WindowInsets) mLastInsets).getSystemWindowInsetTop() : 0;
+            } else {
+                inset = 0;
+            }
             if (inset > 0) {
                 mStatusBarBackground.setBounds(0, 0, getWidth(), inset);
                 mStatusBarBackground.draw(c);
@@ -1432,9 +1419,10 @@
         return false;
     }
 
+    @SuppressWarnings("ShortCircuitBoolean")
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
 
         // "|" used deliberately here; both methods should be invoked.
         final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev)
@@ -1487,7 +1475,7 @@
         final int action = ev.getAction();
         boolean wantTouchEvents = true;
 
-        switch (action & MotionEventCompat.ACTION_MASK) {
+        switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 final float x = ev.getX();
                 final float y = ev.getY();
@@ -2036,18 +2024,22 @@
             dest.writeInt(lockModeEnd);
         }
 
-        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 
     private class ViewDragCallback extends ViewDragHelper.Callback {
@@ -2127,10 +2119,10 @@
 
             int left;
             if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.LEFT)) {
-                left = xvel > 0 || xvel == 0 && offset > 0.5f ? 0 : -childWidth;
+                left = xvel > 0 || (xvel == 0 && offset > 0.5f) ? 0 : -childWidth;
             } else {
                 final int width = getWidth();
-                left = xvel < 0 || xvel == 0 && offset > 0.5f ? width - childWidth : width;
+                left = xvel < 0 || (xvel == 0 && offset > 0.5f) ? width - childWidth : width;
             }
 
             mDragger.settleCapturedViewAt(left, releasedChild.getTop());
@@ -2375,7 +2367,7 @@
         }
     }
 
-    final class ChildAccessibilityDelegate extends AccessibilityDelegateCompat {
+    static final class ChildAccessibilityDelegate extends AccessibilityDelegateCompat {
         @Override
         public void onInitializeAccessibilityNodeInfo(View child,
                 AccessibilityNodeInfoCompat info) {
diff --git a/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java b/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
index d30b0cf..8a29eff 100644
--- a/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -23,14 +23,11 @@
 import android.support.annotation.Nullable;
 import android.support.v4.util.SparseArrayCompat;
 import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.KeyEventCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewCompat.FocusDirection;
 import android.support.v4.view.ViewCompat.FocusRealDirection;
 import android.support.v4.view.ViewParentCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityManagerCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -40,6 +37,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityRecord;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -178,18 +176,17 @@
      * @return Whether the hover event was handled.
      */
     public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
-        if (!mManager.isEnabled()
-                || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
+        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
             return false;
         }
 
         switch (event.getAction()) {
-            case MotionEventCompat.ACTION_HOVER_MOVE:
-            case MotionEventCompat.ACTION_HOVER_ENTER:
+            case MotionEvent.ACTION_HOVER_MOVE:
+            case MotionEvent.ACTION_HOVER_ENTER:
                 final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
                 updateHoveredVirtualView(virtualViewId);
                 return (virtualViewId != INVALID_ID);
-            case MotionEventCompat.ACTION_HOVER_EXIT:
+            case MotionEvent.ACTION_HOVER_EXIT:
                 if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
                     updateHoveredVirtualView(INVALID_ID);
                     return true;
@@ -223,7 +220,7 @@
                 case KeyEvent.KEYCODE_DPAD_UP:
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
                 case KeyEvent.KEYCODE_DPAD_DOWN:
-                    if (KeyEventCompat.hasNoModifiers(event)) {
+                    if (event.hasNoModifiers()) {
                         final int direction = keyToDirection(keyCode);
                         final int count = 1 + event.getRepeatCount();
                         for (int i = 0; i < count; i++) {
@@ -237,7 +234,7 @@
                     break;
                 case KeyEvent.KEYCODE_DPAD_CENTER:
                 case KeyEvent.KEYCODE_ENTER:
-                    if (KeyEventCompat.hasNoModifiers(event)) {
+                    if (event.hasNoModifiers()) {
                         if (event.getRepeatCount() == 0) {
                             clickKeyboardFocusedVirtualView();
                             handled = true;
@@ -245,9 +242,9 @@
                     }
                     break;
                 case KeyEvent.KEYCODE_TAB:
-                    if (KeyEventCompat.hasNoModifiers(event)) {
+                    if (event.hasNoModifiers()) {
                         handled = moveFocus(View.FOCUS_FORWARD, null);
-                    } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
+                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
                         handled = moveFocus(View.FOCUS_BACKWARD, null);
                     }
                     break;
@@ -618,9 +615,9 @@
 
         // Stay consistent with framework behavior by sending ENTER/EXIT pairs
         // in reverse order. This is accurate as of API 18.
-        sendEventForVirtualView(virtualViewId, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
         sendEventForVirtualView(
-                previousVirtualViewId, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
+                previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
     }
 
     /**
@@ -651,7 +648,7 @@
      */
     private AccessibilityEvent createEventForHost(int eventType) {
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        ViewCompat.onInitializeAccessibilityEvent(mHost, event);
+        mHost.onInitializeAccessibilityEvent(event);
         return event;
     }
 
@@ -675,16 +672,15 @@
      */
     private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
         final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
 
         // Allow the client to override these properties,
-        record.getText().add(node.getText());
-        record.setContentDescription(node.getContentDescription());
-        record.setScrollable(node.isScrollable());
-        record.setPassword(node.isPassword());
-        record.setEnabled(node.isEnabled());
-        record.setChecked(node.isChecked());
+        event.getText().add(node.getText());
+        event.setContentDescription(node.getContentDescription());
+        event.setScrollable(node.isScrollable());
+        event.setPassword(node.isPassword());
+        event.setEnabled(node.isEnabled());
+        event.setChecked(node.isChecked());
 
         // Allow the client to populate the event.
         onPopulateEventForVirtualView(virtualViewId, event);
@@ -696,8 +692,8 @@
         }
 
         // Don't allow the client to override these properties.
-        record.setClassName(node.getClassName());
-        record.setSource(mHost, virtualViewId);
+        event.setClassName(node.getClassName());
+        AccessibilityRecordCompat.setSource(event, mHost, virtualViewId);
         event.setPackageName(mHost.getContext().getPackageName());
 
         return event;
@@ -879,11 +875,13 @@
         if (mHost.getLocalVisibleRect(mTempVisibleRect)) {
             mTempVisibleRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
                     mTempGlobalRect[1] - mHost.getScrollY());
-            mTempScreenRect.intersect(mTempVisibleRect);
-            node.setBoundsInScreen(mTempScreenRect);
+            final boolean intersects = mTempScreenRect.intersect(mTempVisibleRect);
+            if (intersects) {
+                node.setBoundsInScreen(mTempScreenRect);
 
-            if (isVisibleToUser(mTempScreenRect)) {
-                node.setVisibleToUser(true);
+                if (isVisibleToUser(mTempScreenRect)) {
+                    node.setVisibleToUser(true);
+                }
             }
         }
 
@@ -941,7 +939,7 @@
         ViewParent viewParent = mHost.getParent();
         while (viewParent instanceof View) {
             final View view = (View) viewParent;
-            if ((ViewCompat.getAlpha(view) <= 0) || (view.getVisibility() != View.VISIBLE)) {
+            if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
                 return false;
             }
             viewParent = view.getParent();
@@ -964,8 +962,7 @@
      * @return whether this virtual view actually took accessibility focus
      */
     private boolean requestAccessibilityFocus(int virtualViewId) {
-        if (!mManager.isEnabled()
-                || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
+        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
             return false;
         }
         // TODO: Check virtual view visibility.
@@ -1103,7 +1100,7 @@
      * <li>package name, set to the package of the host view's
      * {@link Context}, see {@link AccessibilityEvent#setPackageName}
      * <li>event source, set to the host view and virtual view identifier,
-     * see {@link AccessibilityRecordCompat#setSource(View, int)}
+     * see {@link AccessibilityRecordCompat#setSource(AccessibilityRecord, View, int)}
      * </ul>
      *
      * @param virtualViewId The virtual view id for the item for which to
diff --git a/core-ui/java/android/support/v4/widget/MaterialProgressDrawable.java b/core-ui/java/android/support/v4/widget/MaterialProgressDrawable.java
deleted file mode 100644
index 12b3a74..0000000
--- a/core-ui/java/android/support/v4/widget/MaterialProgressDrawable.java
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.util.DisplayMetrics;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.Transformation;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Fancy progress indicator for Material theme.
- */
-class MaterialProgressDrawable extends Drawable implements Animatable {
-    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
-    static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
-
-    private static final float FULL_ROTATION = 1080.0f;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({LARGE, DEFAULT})
-    public @interface ProgressDrawableSize {}
-
-    // Maps to ProgressBar.Large style
-    static final int LARGE = 0;
-    // Maps to ProgressBar default style
-    static final int DEFAULT = 1;
-
-    // Maps to ProgressBar default style
-    private static final int CIRCLE_DIAMETER = 40;
-    private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width
-    private static final float STROKE_WIDTH = 2.5f;
-
-    // Maps to ProgressBar.Large style
-    private static final int CIRCLE_DIAMETER_LARGE = 56;
-    private static final float CENTER_RADIUS_LARGE = 12.5f;
-    private static final float STROKE_WIDTH_LARGE = 3f;
-
-    private static final int[] COLORS = new int[] {
-        Color.BLACK
-    };
-
-    /**
-     * The value in the linear interpolator for animating the drawable at which
-     * the color transition should start
-     */
-    private static final float COLOR_START_DELAY_OFFSET = 0.75f;
-    private static final float END_TRIM_START_DELAY_OFFSET = 0.5f;
-    private static final float START_TRIM_DURATION_OFFSET = 0.5f;
-
-    /** The duration of a single progress spin in milliseconds. */
-    private static final int ANIMATION_DURATION = 1332;
-
-    /** The number of points in the progress "star". */
-    private static final float NUM_POINTS = 5f;
-    /** The list of animators operating on this drawable. */
-    private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();
-
-    /** The indicator ring, used to manage animation state. */
-    private final Ring mRing;
-
-    /** Canvas rotation in degrees. */
-    private float mRotation;
-
-    /** Layout info for the arrowhead in dp */
-    private static final int ARROW_WIDTH = 10;
-    private static final int ARROW_HEIGHT = 5;
-    private static final float ARROW_OFFSET_ANGLE = 5;
-
-    /** Layout info for the arrowhead for the large spinner in dp */
-    private static final int ARROW_WIDTH_LARGE = 12;
-    private static final int ARROW_HEIGHT_LARGE = 6;
-    private static final float MAX_PROGRESS_ARC = .8f;
-
-    private Resources mResources;
-    private View mParent;
-    private Animation mAnimation;
-    float mRotationCount;
-    private double mWidth;
-    private double mHeight;
-    boolean mFinishing;
-
-    MaterialProgressDrawable(Context context, View parent) {
-        mParent = parent;
-        mResources = context.getResources();
-
-        mRing = new Ring(mCallback);
-        mRing.setColors(COLORS);
-
-        updateSizes(DEFAULT);
-        setupAnimators();
-    }
-
-    private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,
-            double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {
-        final Ring ring = mRing;
-        final DisplayMetrics metrics = mResources.getDisplayMetrics();
-        final float screenDensity = metrics.density;
-
-        mWidth = progressCircleWidth * screenDensity;
-        mHeight = progressCircleHeight * screenDensity;
-        ring.setStrokeWidth((float) strokeWidth * screenDensity);
-        ring.setCenterRadius(centerRadius * screenDensity);
-        ring.setColorIndex(0);
-        ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
-        ring.setInsets((int) mWidth, (int) mHeight);
-    }
-
-    /**
-     * Set the overall size for the progress spinner. This updates the radius
-     * and stroke width of the ring.
-     *
-     * @param size One of {@link MaterialProgressDrawable.LARGE} or
-     *            {@link MaterialProgressDrawable.DEFAULT}
-     */
-    public void updateSizes(@ProgressDrawableSize int size) {
-        if (size == LARGE) {
-            setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,
-                    STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);
-        } else {
-            setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,
-                    ARROW_WIDTH, ARROW_HEIGHT);
-        }
-    }
-
-    /**
-     * @param show Set to true to display the arrowhead on the progress spinner.
-     */
-    public void showArrow(boolean show) {
-        mRing.setShowArrow(show);
-    }
-
-    /**
-     * @param scale Set the scale of the arrowhead for the spinner.
-     */
-    public void setArrowScale(float scale) {
-        mRing.setArrowScale(scale);
-    }
-
-    /**
-     * Set the start and end trim for the progress spinner arc.
-     *
-     * @param startAngle start angle
-     * @param endAngle end angle
-     */
-    public void setStartEndTrim(float startAngle, float endAngle) {
-        mRing.setStartTrim(startAngle);
-        mRing.setEndTrim(endAngle);
-    }
-
-    /**
-     * Set the amount of rotation to apply to the progress spinner.
-     *
-     * @param rotation Rotation is from [0..1]
-     */
-    public void setProgressRotation(float rotation) {
-        mRing.setRotation(rotation);
-    }
-
-    /**
-     * Update the background color of the circle image view.
-     */
-    public void setBackgroundColor(int color) {
-        mRing.setBackgroundColor(color);
-    }
-
-    /**
-     * Set the colors used in the progress animation from color resources.
-     * The first color will also be the color of the bar that grows in response
-     * to a user swipe gesture.
-     *
-     * @param colors
-     */
-    public void setColorSchemeColors(int... colors) {
-        mRing.setColors(colors);
-        mRing.setColorIndex(0);
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return (int) mHeight;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return (int) mWidth;
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        final Rect bounds = getBounds();
-        final int saveCount = c.save();
-        c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
-        mRing.draw(c, bounds);
-        c.restoreToCount(saveCount);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mRing.setAlpha(alpha);
-    }
-
-    public int getAlpha() {
-        return mRing.getAlpha();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mRing.setColorFilter(colorFilter);
-    }
-
-    @SuppressWarnings("unused")
-    void setRotation(float rotation) {
-        mRotation = rotation;
-        invalidateSelf();
-    }
-
-    @SuppressWarnings("unused")
-    private float getRotation() {
-        return mRotation;
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public boolean isRunning() {
-        final ArrayList<Animation> animators = mAnimators;
-        final int N = animators.size();
-        for (int i = 0; i < N; i++) {
-            final Animation animator = animators.get(i);
-            if (animator.hasStarted() && !animator.hasEnded()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void start() {
-        mAnimation.reset();
-        mRing.storeOriginals();
-        // Already showing some part of the ring
-        if (mRing.getEndTrim() != mRing.getStartTrim()) {
-            mFinishing = true;
-            mAnimation.setDuration(ANIMATION_DURATION / 2);
-            mParent.startAnimation(mAnimation);
-        } else {
-            mRing.setColorIndex(0);
-            mRing.resetOriginals();
-            mAnimation.setDuration(ANIMATION_DURATION);
-            mParent.startAnimation(mAnimation);
-        }
-    }
-
-    @Override
-    public void stop() {
-        mParent.clearAnimation();
-        setRotation(0);
-        mRing.setShowArrow(false);
-        mRing.setColorIndex(0);
-        mRing.resetOriginals();
-    }
-
-    float getMinProgressArc(Ring ring) {
-        return (float) Math.toRadians(
-                ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius()));
-    }
-
-    // Adapted from ArgbEvaluator.java
-    private int evaluateColorChange(float fraction, int startValue, int endValue) {
-        int startInt = (Integer) startValue;
-        int startA = (startInt >> 24) & 0xff;
-        int startR = (startInt >> 16) & 0xff;
-        int startG = (startInt >> 8) & 0xff;
-        int startB = startInt & 0xff;
-
-        int endInt = (Integer) endValue;
-        int endA = (endInt >> 24) & 0xff;
-        int endR = (endInt >> 16) & 0xff;
-        int endG = (endInt >> 8) & 0xff;
-        int endB = endInt & 0xff;
-
-        return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
-                | (int) ((startR + (int) (fraction * (endR - startR))) << 16)
-                | (int) ((startG + (int) (fraction * (endG - startG))) << 8)
-                | (int) ((startB + (int) (fraction * (endB - startB))));
-    }
-
-    /**
-     * Update the ring color if this is within the last 25% of the animation.
-     * The new ring color will be a translation from the starting ring color to
-     * the next color.
-     */
-    void updateRingColor(float interpolatedTime, Ring ring) {
-        if (interpolatedTime > COLOR_START_DELAY_OFFSET) {
-            // scale the interpolatedTime so that the full
-            // transformation from 0 - 1 takes place in the
-            // remaining time
-            ring.setColor(evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)
-                    / (1.0f - COLOR_START_DELAY_OFFSET), ring.getStartingColor(),
-                    ring.getNextColor()));
-        }
-    }
-
-    void applyFinishTranslation(float interpolatedTime, Ring ring) {
-        // shrink back down and complete a full rotation before
-        // starting other circles
-        // Rotation goes between [0..1].
-        updateRingColor(interpolatedTime, ring);
-        float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)
-                + 1f);
-        final float minProgressArc = getMinProgressArc(ring);
-        final float startTrim = ring.getStartingStartTrim()
-                + (ring.getStartingEndTrim() - minProgressArc - ring.getStartingStartTrim())
-                * interpolatedTime;
-        ring.setStartTrim(startTrim);
-        ring.setEndTrim(ring.getStartingEndTrim());
-        final float rotation = ring.getStartingRotation()
-                + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
-        ring.setRotation(rotation);
-    }
-
-    private void setupAnimators() {
-        final Ring ring = mRing;
-        final Animation animation = new Animation() {
-                @Override
-            public void applyTransformation(float interpolatedTime, Transformation t) {
-                if (mFinishing) {
-                    applyFinishTranslation(interpolatedTime, ring);
-                } else {
-                    // The minProgressArc is calculated from 0 to create an
-                    // angle that matches the stroke width.
-                    final float minProgressArc = getMinProgressArc(ring);
-                    final float startingEndTrim = ring.getStartingEndTrim();
-                    final float startingTrim = ring.getStartingStartTrim();
-                    final float startingRotation = ring.getStartingRotation();
-
-                    updateRingColor(interpolatedTime, ring);
-
-                    // Moving the start trim only occurs in the first 50% of a
-                    // single ring animation
-                    if (interpolatedTime <= START_TRIM_DURATION_OFFSET) {
-                        // scale the interpolatedTime so that the full
-                        // transformation from 0 - 1 takes place in the
-                        // remaining time
-                        final float scaledTime = (interpolatedTime)
-                                / (1.0f - START_TRIM_DURATION_OFFSET);
-                        final float startTrim = startingTrim
-                                + ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR
-                                        .getInterpolation(scaledTime));
-                        ring.setStartTrim(startTrim);
-                    }
-
-                    // Moving the end trim starts after 50% of a single ring
-                    // animation completes
-                    if (interpolatedTime > END_TRIM_START_DELAY_OFFSET) {
-                        // scale the interpolatedTime so that the full
-                        // transformation from 0 - 1 takes place in the
-                        // remaining time
-                        final float minArc = MAX_PROGRESS_ARC - minProgressArc;
-                        float scaledTime = (interpolatedTime - START_TRIM_DURATION_OFFSET)
-                                / (1.0f - START_TRIM_DURATION_OFFSET);
-                        final float endTrim = startingEndTrim
-                                + (minArc * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime));
-                        ring.setEndTrim(endTrim);
-                    }
-
-                    final float rotation = startingRotation + (0.25f * interpolatedTime);
-                    ring.setRotation(rotation);
-
-                    float groupRotation = ((FULL_ROTATION / NUM_POINTS) * interpolatedTime)
-                            + (FULL_ROTATION * (mRotationCount / NUM_POINTS));
-                    setRotation(groupRotation);
-                }
-            }
-        };
-        animation.setRepeatCount(Animation.INFINITE);
-        animation.setRepeatMode(Animation.RESTART);
-        animation.setInterpolator(LINEAR_INTERPOLATOR);
-        animation.setAnimationListener(new Animation.AnimationListener() {
-
-                @Override
-            public void onAnimationStart(Animation animation) {
-                mRotationCount = 0;
-            }
-
-                @Override
-            public void onAnimationEnd(Animation animation) {
-                // do nothing
-            }
-
-                @Override
-            public void onAnimationRepeat(Animation animation) {
-                ring.storeOriginals();
-                ring.goToNextColor();
-                ring.setStartTrim(ring.getEndTrim());
-                if (mFinishing) {
-                    // finished closing the last ring from the swipe gesture; go
-                    // into progress mode
-                    mFinishing = false;
-                    animation.setDuration(ANIMATION_DURATION);
-                    ring.setShowArrow(false);
-                } else {
-                    mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
-                }
-            }
-        });
-        mAnimation = animation;
-    }
-
-    private final Callback mCallback = new Callback() {
-        @Override
-        public void invalidateDrawable(Drawable d) {
-            invalidateSelf();
-        }
-
-        @Override
-        public void scheduleDrawable(Drawable d, Runnable what, long when) {
-            scheduleSelf(what, when);
-        }
-
-        @Override
-        public void unscheduleDrawable(Drawable d, Runnable what) {
-            unscheduleSelf(what);
-        }
-    };
-
-    private static class Ring {
-        private final RectF mTempBounds = new RectF();
-        private final Paint mPaint = new Paint();
-        private final Paint mArrowPaint = new Paint();
-
-        private final Callback mCallback;
-
-        private float mStartTrim = 0.0f;
-        private float mEndTrim = 0.0f;
-        private float mRotation = 0.0f;
-        private float mStrokeWidth = 5.0f;
-        private float mStrokeInset = 2.5f;
-
-        private int[] mColors;
-        // mColorIndex represents the offset into the available mColors that the
-        // progress circle should currently display. As the progress circle is
-        // animating, the mColorIndex moves by one to the next available color.
-        private int mColorIndex;
-        private float mStartingStartTrim;
-        private float mStartingEndTrim;
-        private float mStartingRotation;
-        private boolean mShowArrow;
-        private Path mArrow;
-        private float mArrowScale;
-        private double mRingCenterRadius;
-        private int mArrowWidth;
-        private int mArrowHeight;
-        private int mAlpha;
-        private final Paint mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        private int mBackgroundColor;
-        private int mCurrentColor;
-
-        Ring(Callback callback) {
-            mCallback = callback;
-
-            mPaint.setStrokeCap(Paint.Cap.SQUARE);
-            mPaint.setAntiAlias(true);
-            mPaint.setStyle(Style.STROKE);
-
-            mArrowPaint.setStyle(Paint.Style.FILL);
-            mArrowPaint.setAntiAlias(true);
-        }
-
-        public void setBackgroundColor(int color) {
-            mBackgroundColor = color;
-        }
-
-        /**
-         * Set the dimensions of the arrowhead.
-         *
-         * @param width Width of the hypotenuse of the arrow head
-         * @param height Height of the arrow point
-         */
-        public void setArrowDimensions(float width, float height) {
-            mArrowWidth = (int) width;
-            mArrowHeight = (int) height;
-        }
-
-        /**
-         * Draw the progress spinner
-         */
-        public void draw(Canvas c, Rect bounds) {
-            final RectF arcBounds = mTempBounds;
-            arcBounds.set(bounds);
-            arcBounds.inset(mStrokeInset, mStrokeInset);
-
-            final float startAngle = (mStartTrim + mRotation) * 360;
-            final float endAngle = (mEndTrim + mRotation) * 360;
-            float sweepAngle = endAngle - startAngle;
-
-            mPaint.setColor(mCurrentColor);
-            c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
-
-            drawTriangle(c, startAngle, sweepAngle, bounds);
-
-            if (mAlpha < 255) {
-                mCirclePaint.setColor(mBackgroundColor);
-                mCirclePaint.setAlpha(255 - mAlpha);
-                c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,
-                        mCirclePaint);
-            }
-        }
-
-        private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
-            if (mShowArrow) {
-                if (mArrow == null) {
-                    mArrow = new android.graphics.Path();
-                    mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD);
-                } else {
-                    mArrow.reset();
-                }
-
-                // Adjust the position of the triangle so that it is inset as
-                // much as the arc, but also centered on the arc.
-                float inset = (int) mStrokeInset / 2 * mArrowScale;
-                float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
-                float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
-
-                // Update the path each time. This works around an issue in SKIA
-                // where concatenating a rotation matrix to a scale matrix
-                // ignored a starting negative rotation. This appears to have
-                // been fixed as of API 21.
-                mArrow.moveTo(0, 0);
-                mArrow.lineTo(mArrowWidth * mArrowScale, 0);
-                mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
-                        * mArrowScale));
-                mArrow.offset(x - inset, y);
-                mArrow.close();
-                // draw a triangle
-                mArrowPaint.setColor(mCurrentColor);
-                c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
-                        bounds.exactCenterY());
-                c.drawPath(mArrow, mArrowPaint);
-            }
-        }
-
-        /**
-         * Set the colors the progress spinner alternates between.
-         *
-         * @param colors Array of integers describing the colors. Must be non-<code>null</code>.
-         */
-        public void setColors(@NonNull int[] colors) {
-            mColors = colors;
-            // if colors are reset, make sure to reset the color index as well
-            setColorIndex(0);
-        }
-
-        /**
-         * Set the absolute color of the progress spinner. This is should only
-         * be used when animating between current and next color when the
-         * spinner is rotating.
-         *
-         * @param color int describing the color.
-         */
-        public void setColor(int color) {
-            mCurrentColor = color;
-        }
-
-        /**
-         * @param index Index into the color array of the color to display in
-         *            the progress spinner.
-         */
-        public void setColorIndex(int index) {
-            mColorIndex = index;
-            mCurrentColor = mColors[mColorIndex];
-        }
-
-        /**
-         * @return int describing the next color the progress spinner should use when drawing.
-         */
-        public int getNextColor() {
-            return mColors[getNextColorIndex()];
-        }
-
-        private int getNextColorIndex() {
-            return (mColorIndex + 1) % (mColors.length);
-        }
-
-        /**
-         * Proceed to the next available ring color. This will automatically
-         * wrap back to the beginning of colors.
-         */
-        public void goToNextColor() {
-            setColorIndex(getNextColorIndex());
-        }
-
-        public void setColorFilter(ColorFilter filter) {
-            mPaint.setColorFilter(filter);
-            invalidateSelf();
-        }
-
-        /**
-         * @param alpha Set the alpha of the progress spinner and associated arrowhead.
-         */
-        public void setAlpha(int alpha) {
-            mAlpha = alpha;
-        }
-
-        /**
-         * @return Current alpha of the progress spinner and arrowhead.
-         */
-        public int getAlpha() {
-            return mAlpha;
-        }
-
-        /**
-         * @param strokeWidth Set the stroke width of the progress spinner in pixels.
-         */
-        public void setStrokeWidth(float strokeWidth) {
-            mStrokeWidth = strokeWidth;
-            mPaint.setStrokeWidth(strokeWidth);
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getStrokeWidth() {
-            return mStrokeWidth;
-        }
-
-        @SuppressWarnings("unused")
-        public void setStartTrim(float startTrim) {
-            mStartTrim = startTrim;
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getStartTrim() {
-            return mStartTrim;
-        }
-
-        public float getStartingStartTrim() {
-            return mStartingStartTrim;
-        }
-
-        public float getStartingEndTrim() {
-            return mStartingEndTrim;
-        }
-
-        public int getStartingColor() {
-            return mColors[mColorIndex];
-        }
-
-        @SuppressWarnings("unused")
-        public void setEndTrim(float endTrim) {
-            mEndTrim = endTrim;
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getEndTrim() {
-            return mEndTrim;
-        }
-
-        @SuppressWarnings("unused")
-        public void setRotation(float rotation) {
-            mRotation = rotation;
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getRotation() {
-            return mRotation;
-        }
-
-        public void setInsets(int width, int height) {
-            final float minEdge = (float) Math.min(width, height);
-            float insets;
-            if (mRingCenterRadius <= 0 || minEdge < 0) {
-                insets = (float) Math.ceil(mStrokeWidth / 2.0f);
-            } else {
-                insets = (float) (minEdge / 2.0f - mRingCenterRadius);
-            }
-            mStrokeInset = insets;
-        }
-
-        @SuppressWarnings("unused")
-        public float getInsets() {
-            return mStrokeInset;
-        }
-
-        /**
-         * @param centerRadius Inner radius in px of the circle the progress
-         *            spinner arc traces.
-         */
-        public void setCenterRadius(double centerRadius) {
-            mRingCenterRadius = centerRadius;
-        }
-
-        public double getCenterRadius() {
-            return mRingCenterRadius;
-        }
-
-        /**
-         * @param show Set to true to show the arrow head on the progress spinner.
-         */
-        public void setShowArrow(boolean show) {
-            if (mShowArrow != show) {
-                mShowArrow = show;
-                invalidateSelf();
-            }
-        }
-
-        /**
-         * @param scale Set the scale of the arrowhead for the spinner.
-         */
-        public void setArrowScale(float scale) {
-            if (scale != mArrowScale) {
-                mArrowScale = scale;
-                invalidateSelf();
-            }
-        }
-
-        /**
-         * @return The amount the progress spinner is currently rotated, between [0..1].
-         */
-        public float getStartingRotation() {
-            return mStartingRotation;
-        }
-
-        /**
-         * If the start / end trim are offset to begin with, store them so that
-         * animation starts from that offset.
-         */
-        public void storeOriginals() {
-            mStartingStartTrim = mStartTrim;
-            mStartingEndTrim = mEndTrim;
-            mStartingRotation = mRotation;
-        }
-
-        /**
-         * Reset the progress spinner to default rotation, start and end angles.
-         */
-        public void resetOriginals() {
-            mStartingStartTrim = 0;
-            mStartingEndTrim = 0;
-            mStartingRotation = 0;
-            setStartTrim(0);
-            setEndTrim(0);
-            setRotation(0);
-        }
-
-        private void invalidateSelf() {
-            mCallback.invalidateDrawable(null);
-        }
-    }
-}
diff --git a/core-ui/java/android/support/v4/widget/NestedScrollView.java b/core-ui/java/android/support/v4/widget/NestedScrollView.java
index 44cc043..517686f 100644
--- a/core-ui/java/android/support/v4/widget/NestedScrollView.java
+++ b/core-ui/java/android/support/v4/widget/NestedScrollView.java
@@ -29,15 +29,12 @@
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.InputDeviceCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.NestedScrollingChild;
+import android.support.v4.view.NestedScrollingChild2;
 import android.support.v4.view.NestedScrollingChildHelper;
 import android.support.v4.view.NestedScrollingParent;
 import android.support.v4.view.NestedScrollingParentHelper;
 import android.support.v4.view.ScrollingView;
-import android.support.v4.view.VelocityTrackerCompat;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.util.AttributeSet;
@@ -53,7 +50,9 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
+import android.widget.EdgeEffect;
 import android.widget.FrameLayout;
+import android.widget.OverScroller;
 import android.widget.ScrollView;
 
 import java.util.List;
@@ -64,7 +63,7 @@
  * Nested scrolling is enabled by default.
  */
 public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
-        NestedScrollingChild, ScrollingView {
+        NestedScrollingChild2, ScrollingView {
     static final int ANIMATED_SCROLL_GAP = 250;
 
     static final float MAX_SCROLL_FACTOR = 0.5f;
@@ -96,9 +95,9 @@
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
-    private ScrollerCompat mScroller;
-    private EdgeEffectCompat mEdgeGlowTop;
-    private EdgeEffectCompat mEdgeGlowBottom;
+    private OverScroller mScroller;
+    private EdgeEffect mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowBottom;
 
     /**
      * Position of the last motion event.
@@ -159,6 +158,8 @@
     private final int[] mScrollConsumed = new int[2];
     private int mNestedYOffset;
 
+    private int mLastScrollerY;
+
     /**
      * Sentinel value for no current active pointer.
      * Used by {@link #mActivePointerId}.
@@ -226,16 +227,31 @@
     }
 
     @Override
+    public boolean startNestedScroll(int axes, int type) {
+        return mChildHelper.startNestedScroll(axes, type);
+    }
+
+    @Override
     public void stopNestedScroll() {
         mChildHelper.stopNestedScroll();
     }
 
     @Override
+    public void stopNestedScroll(int type) {
+        mChildHelper.stopNestedScroll(type);
+    }
+
+    @Override
     public boolean hasNestedScrollingParent() {
         return mChildHelper.hasNestedScrollingParent();
     }
 
     @Override
+    public boolean hasNestedScrollingParent(int type) {
+        return mChildHelper.hasNestedScrollingParent(type);
+    }
+
+    @Override
     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
             int dyUnconsumed, int[] offsetInWindow) {
         return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
@@ -243,11 +259,24 @@
     }
 
     @Override
+    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
+            int dyUnconsumed, int[] offsetInWindow, int type) {
+        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                offsetInWindow, type);
+    }
+
+    @Override
     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
         return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
     }
 
     @Override
+    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
+            int type) {
+        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
+    }
+
+    @Override
     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
         return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
     }
@@ -312,6 +341,7 @@
 
     // ScrollView import
 
+    @Override
     public boolean shouldDelayChildPressedState() {
         return true;
     }
@@ -356,7 +386,7 @@
     }
 
     private void initScrollView() {
-        mScroller = ScrollerCompat.create(getContext(), null);
+        mScroller = new OverScroller(getContext());
         setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setWillNotDraw(false);
@@ -605,7 +635,6 @@
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         /*
@@ -624,7 +653,7 @@
             return true;
         }
 
-        switch (action & MotionEventCompat.ACTION_MASK) {
+        switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_MOVE: {
                 /*
                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
@@ -667,7 +696,7 @@
 
             case MotionEvent.ACTION_DOWN: {
                 final int y = (int) ev.getY();
-                if (!inChild((int) ev.getX(), (int) y)) {
+                if (!inChild((int) ev.getX(), y)) {
                     mIsBeingDragged = false;
                     recycleVelocityTracker();
                     break;
@@ -690,7 +719,7 @@
                 */
                 mScroller.computeScrollOffset();
                 mIsBeingDragged = !mScroller.isFinished();
-                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
+                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
                 break;
             }
 
@@ -703,9 +732,9 @@
                 if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
                     ViewCompat.postInvalidateOnAnimation(this);
                 }
-                stopNestedScroll();
+                stopNestedScroll(ViewCompat.TYPE_TOUCH);
                 break;
-            case MotionEventCompat.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
                 break;
         }
@@ -723,7 +752,7 @@
 
         MotionEvent vtev = MotionEvent.obtain(ev);
 
-        final int actionMasked = MotionEventCompat.getActionMasked(ev);
+        final int actionMasked = ev.getActionMasked();
 
         if (actionMasked == MotionEvent.ACTION_DOWN) {
             mNestedYOffset = 0;
@@ -753,7 +782,7 @@
                 // Remember where the motion event started
                 mLastMotionY = (int) ev.getY();
                 mActivePointerId = ev.getPointerId(0);
-                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
+                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
                 break;
             }
             case MotionEvent.ACTION_MOVE:
@@ -765,7 +794,8 @@
 
                 final int y = (int) ev.getY(activePointerIndex);
                 int deltaY = mLastMotionY - y;
-                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
+                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset,
+                        ViewCompat.TYPE_TOUCH)) {
                     deltaY -= mScrollConsumed[1];
                     vtev.offsetLocation(0, mScrollOffset[1]);
                     mNestedYOffset += mScrollOffset[1];
@@ -795,14 +825,15 @@
                     // Calling overScrollByCompat will call onOverScrolled, which
                     // calls onScrollChanged if applicable.
                     if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0,
-                            0, true) && !hasNestedScrollingParent()) {
+                            0, true) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) {
                         // Break our velocity if we hit a scroll barrier.
                         mVelocityTracker.clear();
                     }
 
                     final int scrolledDeltaY = getScrollY() - oldY;
                     final int unconsumedY = deltaY - scrolledDeltaY;
-                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
+                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset,
+                            ViewCompat.TYPE_TOUCH)) {
                         mLastMotionY -= mScrollOffset[1];
                         vtev.offsetLocation(0, mScrollOffset[1]);
                         mNestedYOffset += mScrollOffset[1];
@@ -810,13 +841,13 @@
                         ensureGlows();
                         final int pulledToY = oldY + deltaY;
                         if (pulledToY < 0) {
-                            mEdgeGlowTop.onPull((float) deltaY / getHeight(),
+                            EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(),
                                     ev.getX(activePointerIndex) / getWidth());
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
                         } else if (pulledToY > range) {
-                            mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
+                            EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(),
                                     1.f - ev.getX(activePointerIndex)
                                             / getWidth());
                             if (!mEdgeGlowTop.isFinished()) {
@@ -831,18 +862,14 @@
                 }
                 break;
             case MotionEvent.ACTION_UP:
-                if (mIsBeingDragged) {
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
-                            mActivePointerId);
-
-                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                        flingWithNestedDispatch(-initialVelocity);
-                    } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
-                            getScrollRange())) {
-                        ViewCompat.postInvalidateOnAnimation(this);
-                    }
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+                if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                    flingWithNestedDispatch(-initialVelocity);
+                } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
+                        getScrollRange())) {
+                    ViewCompat.postInvalidateOnAnimation(this);
                 }
                 mActivePointerId = INVALID_POINTER;
                 endDrag();
@@ -857,13 +884,13 @@
                 mActivePointerId = INVALID_POINTER;
                 endDrag();
                 break;
-            case MotionEventCompat.ACTION_POINTER_DOWN: {
-                final int index = MotionEventCompat.getActionIndex(ev);
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
                 mLastMotionY = (int) ev.getY(index);
                 mActivePointerId = ev.getPointerId(index);
                 break;
             }
-            case MotionEventCompat.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
                 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
                 break;
@@ -877,8 +904,7 @@
     }
 
     private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = (ev.getAction() & MotionEventCompat.ACTION_POINTER_INDEX_MASK)
-                >> MotionEventCompat.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerIndex = ev.getActionIndex();
         final int pointerId = ev.getPointerId(pointerIndex);
         if (pointerId == mActivePointerId) {
             // This was our active pointer going up. Choose a new
@@ -893,13 +919,13 @@
         }
     }
 
+    @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
             switch (event.getAction()) {
-                case MotionEventCompat.ACTION_SCROLL: {
+                case MotionEvent.ACTION_SCROLL: {
                     if (!mIsBeingDragged) {
-                        final float vscroll = MotionEventCompat.getAxisValue(event,
-                                MotionEventCompat.AXIS_VSCROLL);
+                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
                         if (vscroll != 0) {
                             final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
                             final int range = getScrollRange();
@@ -992,7 +1018,7 @@
             clampedY = true;
         }
 
-        if (clampedY) {
+        if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
             mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
         }
 
@@ -1423,29 +1449,51 @@
     @Override
     public void computeScroll() {
         if (mScroller.computeScrollOffset()) {
-            int oldX = getScrollX();
-            int oldY = getScrollY();
-            int x = mScroller.getCurrX();
-            int y = mScroller.getCurrY();
+            final int x = mScroller.getCurrX();
+            final int y = mScroller.getCurrY();
 
-            if (oldX != x || oldY != y) {
+            int dy = y - mLastScrollerY;
+
+            // Dispatch up to parent
+            if (dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH)) {
+                dy -= mScrollConsumed[1];
+            }
+
+            if (dy != 0) {
                 final int range = getScrollRange();
-                final int overscrollMode = getOverScrollMode();
-                final boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS
-                        || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+                final int oldScrollY = getScrollY();
 
-                overScrollByCompat(x - oldX, y - oldY, oldX, oldY, 0, range,
-                        0, 0, false);
+                overScrollByCompat(0, dy, getScrollX(), oldScrollY, 0, range, 0, 0, false);
 
-                if (canOverscroll) {
-                    ensureGlows();
-                    if (y <= 0 && oldY > 0) {
-                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-                    } else if (y >= range && oldY < range) {
-                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+                final int scrolledDeltaY = getScrollY() - oldScrollY;
+                final int unconsumedY = dy - scrolledDeltaY;
+
+                if (!dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, null,
+                        ViewCompat.TYPE_NON_TOUCH)) {
+                    final int mode = getOverScrollMode();
+                    final boolean canOverscroll = mode == OVER_SCROLL_ALWAYS
+                            || (mode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+                    if (canOverscroll) {
+                        ensureGlows();
+                        if (y <= 0 && oldScrollY > 0) {
+                            mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
+                        } else if (y >= range && oldScrollY < range) {
+                            mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+                        }
                     }
                 }
             }
+
+            // Finally update the scroll positions and post an invalidation
+            mLastScrollerY = y;
+            ViewCompat.postInvalidateOnAnimation(this);
+        } else {
+            // We can't scroll any more, so stop any indirect scrolling
+            if (hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
+                stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
+            }
+            // and reset the scroller y
+            mLastScrollerY = 0;
         }
     }
 
@@ -1698,12 +1746,13 @@
      */
     public void fling(int velocityY) {
         if (getChildCount() > 0) {
-            int height = getHeight() - getPaddingBottom() - getPaddingTop();
-            int bottom = getChildAt(0).getHeight();
-
-            mScroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0,
-                    Math.max(0, bottom - height), 0, height / 2);
-
+            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
+            mScroller.fling(getScrollX(), getScrollY(), // start
+                    0, velocityY, // velocities
+                    0, 0, // x
+                    Integer.MIN_VALUE, Integer.MAX_VALUE, // y
+                    0, 0); // overscroll
+            mLastScrollerY = getScrollY();
             ViewCompat.postInvalidateOnAnimation(this);
         }
     }
@@ -1714,9 +1763,7 @@
                 && (scrollY < getScrollRange() || velocityY < 0);
         if (!dispatchNestedPreFling(0, velocityY)) {
             dispatchNestedFling(0, velocityY, canFling);
-            if (canFling) {
-                fling(velocityY);
-            }
+            fling(velocityY);
         }
     }
 
@@ -1724,7 +1771,7 @@
         mIsBeingDragged = false;
 
         recycleVelocityTracker();
-        stopNestedScroll();
+        stopNestedScroll(ViewCompat.TYPE_TOUCH);
 
         if (mEdgeGlowTop != null) {
             mEdgeGlowTop.onRelease();
@@ -1754,8 +1801,8 @@
         if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
             if (mEdgeGlowTop == null) {
                 Context context = getContext();
-                mEdgeGlowTop = new EdgeEffectCompat(context);
-                mEdgeGlowBottom = new EdgeEffectCompat(context);
+                mEdgeGlowTop = new EdgeEffect(context);
+                mEdgeGlowBottom = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowTop = null;
@@ -1946,13 +1993,12 @@
             super.onInitializeAccessibilityEvent(host, event);
             final NestedScrollView nsvHost = (NestedScrollView) host;
             event.setClassName(ScrollView.class.getName());
-            final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
             final boolean scrollable = nsvHost.getScrollRange() > 0;
-            record.setScrollable(scrollable);
-            record.setScrollX(nsvHost.getScrollX());
-            record.setScrollY(nsvHost.getScrollY());
-            record.setMaxScrollX(nsvHost.getScrollX());
-            record.setMaxScrollY(nsvHost.getScrollRange());
+            event.setScrollable(scrollable);
+            event.setScrollX(nsvHost.getScrollX());
+            event.setScrollY(nsvHost.getScrollY());
+            AccessibilityRecordCompat.setMaxScrollX(event, nsvHost.getScrollX());
+            AccessibilityRecordCompat.setMaxScrollY(event, nsvHost.getScrollRange());
         }
     }
 }
diff --git a/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java b/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
index 1e5479b..e3f2462 100644
--- a/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
+++ b/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -31,19 +30,16 @@
 import android.os.Parcelable;
 import android.support.annotation.ColorInt;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.RequiresApi;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
@@ -194,16 +190,14 @@
 
     private final Rect mTmpRect = new Rect();
 
-    final ArrayList<DisableLayerRunnable> mPostedRunnables =
-            new ArrayList<DisableLayerRunnable>();
+    final ArrayList<DisableLayerRunnable> mPostedRunnables = new ArrayList<>();
 
     static final SlidingPanelLayoutImpl IMPL;
 
     static {
-        final int deviceVersion = Build.VERSION.SDK_INT;
-        if (deviceVersion >= 17) {
+        if (Build.VERSION.SDK_INT >= 17) {
             IMPL = new SlidingPanelLayoutImplJBMR1();
-        } else if (deviceVersion >= 16) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             IMPL = new SlidingPanelLayoutImplJB();
         } else {
             IMPL = new SlidingPanelLayoutImplBase();
@@ -265,8 +259,6 @@
         final float density = context.getResources().getDisplayMetrics().density;
         mOverhangSize = (int) (DEFAULT_OVERHANG_SIZE * density + 0.5f);
 
-        final ViewConfiguration viewConfig = ViewConfiguration.get(context);
-
         setWillNotDraw(false);
 
         ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
@@ -485,7 +477,7 @@
         }
 
         int layoutHeight = 0;
-        int maxLayoutHeight = -1;
+        int maxLayoutHeight = 0;
         switch (heightMode) {
             case MeasureSpec.EXACTLY:
                 layoutHeight = maxLayoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
@@ -766,7 +758,7 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
 
         // Preserve the open state based on the last view that was touched.
         if (!mCanSlide && action == MotionEvent.ACTION_DOWN && getChildCount() > 1) {
@@ -832,10 +824,9 @@
 
         mDragHelper.processTouchEvent(ev);
 
-        final int action = ev.getAction();
         boolean wantTouchEvents = true;
 
-        switch (action & MotionEventCompat.ACTION_MASK) {
+        switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 final float x = ev.getX();
                 final float y = ev.getY();
@@ -985,11 +976,11 @@
                 lp.dimPaint = new Paint();
             }
             lp.dimPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_OVER));
-            if (ViewCompat.getLayerType(v) != ViewCompat.LAYER_TYPE_HARDWARE) {
-                ViewCompat.setLayerType(v, ViewCompat.LAYER_TYPE_HARDWARE, lp.dimPaint);
+            if (v.getLayerType() != View.LAYER_TYPE_HARDWARE) {
+                v.setLayerType(View.LAYER_TYPE_HARDWARE, lp.dimPaint);
             }
             invalidateChildRegion(v);
-        } else if (ViewCompat.getLayerType(v) != ViewCompat.LAYER_TYPE_NONE) {
+        } else if (v.getLayerType() != View.LAYER_TYPE_NONE) {
             if (lp.dimPaint != null) {
                 lp.dimPaint.setColorFilter(null);
             }
@@ -1016,28 +1007,7 @@
             canvas.clipRect(mTmpRect);
         }
 
-        if (Build.VERSION.SDK_INT >= 11) { // HC
-            result = super.drawChild(canvas, child, drawingTime);
-        } else {
-            if (lp.dimWhenOffset && mSlideOffset > 0) {
-                if (!child.isDrawingCacheEnabled()) {
-                    child.setDrawingCacheEnabled(true);
-                }
-                final Bitmap cache = child.getDrawingCache();
-                if (cache != null) {
-                    canvas.drawBitmap(cache, child.getLeft(), child.getTop(), lp.dimPaint);
-                    result = false;
-                } else {
-                    Log.e(TAG, "drawChild: child view " + child + " returned null drawing cache");
-                    result = super.drawChild(canvas, child, drawingTime);
-                }
-            } else {
-                if (child.isDrawingCacheEnabled()) {
-                    child.setDrawingCacheEnabled(false);
-                }
-                result = super.drawChild(canvas, child, drawingTime);
-            }
-        }
+        result = super.drawChild(canvas, child, drawingTime);
 
         canvas.restoreToCount(save);
 
@@ -1249,7 +1219,7 @@
             }
         }
 
-        return checkV && ViewCompat.canScrollHorizontally(v, (isLayoutRtlSupport() ? dx : -dx));
+        return checkV && v.canScrollHorizontally((isLayoutRtlSupport() ? dx : -dx));
     }
 
     boolean isDimmed(View child) {
@@ -1481,18 +1451,22 @@
             out.writeInt(isOpen ? 1 : 0);
         }
 
-        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, null);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 
     interface SlidingPanelLayoutImpl {
@@ -1507,6 +1481,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class SlidingPanelLayoutImplJB extends SlidingPanelLayoutImplBase {
         /*
          * Private API hacks! Nasty! Bad!
@@ -1551,6 +1526,7 @@
         }
     }
 
+    @RequiresApi(17)
     static class SlidingPanelLayoutImplJBMR1 extends SlidingPanelLayoutImplBase {
         @Override
         public void invalidateChildRegion(SlidingPaneLayout parent, View child) {
@@ -1654,7 +1630,7 @@
         @Override
         public void run() {
             if (mChildView.getParent() == SlidingPaneLayout.this) {
-                ViewCompat.setLayerType(mChildView, ViewCompat.LAYER_TYPE_NONE, null);
+                mChildView.setLayerType(View.LAYER_TYPE_NONE, null);
                 invalidateChildRegion(mChildView);
             }
             mPostedRunnables.remove(this);
diff --git a/core-ui/java/android/support/v4/widget/Space.java b/core-ui/java/android/support/v4/widget/Space.java
index 4857479..77a2d2e 100644
--- a/core-ui/java/android/support/v4/widget/Space.java
+++ b/core-ui/java/android/support/v4/widget/Space.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.widget;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
@@ -48,6 +49,7 @@
      * @param canvas an unused parameter.
      */
     @Override
+    @SuppressLint("MissingSuperCall")
     public void draw(Canvas canvas) {
     }
 
diff --git a/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java b/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
index 107b9e0..d36ae22 100644
--- a/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
+++ b/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.widget;
 
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.annotation.ColorInt;
@@ -24,7 +23,6 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.NestedScrollingChild;
 import android.support.v4.view.NestedScrollingChildHelper;
 import android.support.v4.view.NestedScrollingParent;
@@ -42,6 +40,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Transformation;
 import android.widget.AbsListView;
+import android.widget.ListView;
 
 /**
  * The SwipeRefreshLayout should be used whenever the user can refresh the
@@ -67,9 +66,9 @@
 public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingParent,
         NestedScrollingChild {
     // Maps to ProgressBar.Large style
-    public static final int LARGE = MaterialProgressDrawable.LARGE;
+    public static final int LARGE = CircularProgressDrawable.LARGE;
     // Maps to ProgressBar default style
-    public static final int DEFAULT = MaterialProgressDrawable.DEFAULT;
+    public static final int DEFAULT = CircularProgressDrawable.DEFAULT;
 
     @VisibleForTesting
     static final int CIRCLE_DIAMETER = 40;
@@ -147,7 +146,7 @@
 
     int mSpinnerOffsetEnd;
 
-    MaterialProgressDrawable mProgress;
+    CircularProgressDrawable mProgress;
 
     private Animation mScaleAnimation;
 
@@ -177,7 +176,6 @@
         public void onAnimationRepeat(Animation animation) {
         }
 
-        @SuppressLint("NewApi")
         @Override
         public void onAnimationEnd(Animation animation) {
             if (mRefreshing) {
@@ -205,8 +203,7 @@
         if (mScale) {
             setAnimationProgress(0 /* animation complete and view is hidden */);
         } else {
-            setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop,
-                    true /* requires update */);
+            setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop);
         }
         mCurrentTargetOffsetTop = mCircleView.getTop();
     }
@@ -225,7 +222,6 @@
         reset();
     }
 
-    @SuppressLint("NewApi")
     private void setColorViewAlpha(int targetAlpha) {
         mCircleView.getBackground().setAlpha(targetAlpha);
         mProgress.setAlpha(targetAlpha);
@@ -298,11 +294,11 @@
      * One of DEFAULT, or LARGE.
      */
     public void setSize(int size) {
-        if (size != MaterialProgressDrawable.LARGE && size != MaterialProgressDrawable.DEFAULT) {
+        if (size != CircularProgressDrawable.LARGE && size != CircularProgressDrawable.DEFAULT) {
             return;
         }
         final DisplayMetrics metrics = getResources().getDisplayMetrics();
-        if (size == MaterialProgressDrawable.LARGE) {
+        if (size == CircularProgressDrawable.LARGE) {
             mCircleDiameter = (int) (CIRCLE_DIAMETER_LARGE * metrics.density);
         } else {
             mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);
@@ -311,7 +307,7 @@
         // update by setting it to null before updating its size and then
         // re-setting it
         mCircleView.setImageDrawable(null);
-        mProgress.updateSizes(size);
+        mProgress.setStyle(size);
         mCircleView.setImageDrawable(mProgress);
     }
 
@@ -380,8 +376,8 @@
 
     private void createProgressView() {
         mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT);
-        mProgress = new MaterialProgressDrawable(getContext(), this);
-        mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);
+        mProgress = new CircularProgressDrawable(getContext());
+        mProgress.setStyle(CircularProgressDrawable.DEFAULT);
         mCircleView.setImageDrawable(mProgress);
         mCircleView.setVisibility(View.GONE);
         addView(mCircleView);
@@ -396,13 +392,6 @@
     }
 
     /**
-     * Pre API 11, alpha is used to make the progress circle appear instead of scale.
-     */
-    private boolean isAlphaUsedForScale() {
-        return android.os.Build.VERSION.SDK_INT < 11;
-    }
-
-    /**
      * Notify the widget that refresh state has changed. Do not call this when
      * refresh is triggered by a swipe gesture.
      *
@@ -418,8 +407,7 @@
             } else {
                 endTarget = mSpinnerOffsetEnd;
             }
-            setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop,
-                    true /* requires update */);
+            setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop);
             mNotify = false;
             startScaleUpAnimation(mRefreshListener);
         } else {
@@ -427,7 +415,6 @@
         }
     }
 
-    @SuppressLint("NewApi")
     private void startScaleUpAnimation(AnimationListener listener) {
         mCircleView.setVisibility(View.VISIBLE);
         if (android.os.Build.VERSION.SDK_INT >= 11) {
@@ -455,12 +442,8 @@
      * @param progress
      */
     void setAnimationProgress(float progress) {
-        if (isAlphaUsedForScale()) {
-            setColorViewAlpha((int) (progress * MAX_ALPHA));
-        } else {
-            ViewCompat.setScaleX(mCircleView, progress);
-            ViewCompat.setScaleY(mCircleView, progress);
-        }
+        mCircleView.setScaleX(progress);
+        mCircleView.setScaleY(progress);
     }
 
     private void setRefreshing(boolean refreshing, final boolean notify) {
@@ -489,23 +472,15 @@
         mCircleView.startAnimation(mScaleDownAnimation);
     }
 
-    @SuppressLint("NewApi")
     private void startProgressAlphaStartAnimation() {
         mAlphaStartAnimation = startAlphaAnimation(mProgress.getAlpha(), STARTING_PROGRESS_ALPHA);
     }
 
-    @SuppressLint("NewApi")
     private void startProgressAlphaMaxAnimation() {
         mAlphaMaxAnimation = startAlphaAnimation(mProgress.getAlpha(), MAX_ALPHA);
     }
 
-    @SuppressLint("NewApi")
     private Animation startAlphaAnimation(final int startingAlpha, final int endingAlpha) {
-        // Pre API 11, alpha is used in place of scale. Don't also use it to
-        // show the trigger point.
-        if (mScale && isAlphaUsedForScale()) {
-            return null;
-        }
         Animation alpha = new Animation() {
             @Override
             public void applyTransformation(float interpolatedTime, Transformation t) {
@@ -545,14 +520,13 @@
      */
     public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
         mCircleView.setBackgroundColor(color);
-        mProgress.setBackgroundColor(color);
     }
 
     /**
      * @deprecated Use {@link #setColorSchemeResources(int...)}
      */
     @Deprecated
-    public void setColorScheme(@ColorInt int... colors) {
+    public void setColorScheme(@ColorRes int... colors) {
         setColorSchemeResources(colors);
     }
 
@@ -683,18 +657,10 @@
         if (mChildScrollUpCallback != null) {
             return mChildScrollUpCallback.canChildScrollUp(this, mTarget);
         }
-        if (android.os.Build.VERSION.SDK_INT < 14) {
-            if (mTarget instanceof AbsListView) {
-                final AbsListView absListView = (AbsListView) mTarget;
-                return absListView.getChildCount() > 0
-                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
-                                .getTop() < absListView.getPaddingTop());
-            } else {
-                return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0;
-            }
-        } else {
-            return ViewCompat.canScrollVertically(mTarget, -1);
+        if (mTarget instanceof ListView) {
+            return ListViewCompat.canScrollList((ListView) mTarget, -1);
         }
+        return mTarget.canScrollVertically(-1);
     }
 
     /**
@@ -710,7 +676,7 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         ensureTarget();
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
         int pointerIndex;
 
         if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
@@ -725,7 +691,7 @@
 
         switch (action) {
             case MotionEvent.ACTION_DOWN:
-                setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true);
+                setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop());
                 mActivePointerId = ev.getPointerId(0);
                 mIsBeingDragged = false;
 
@@ -750,7 +716,7 @@
                 startDragging(y);
                 break;
 
-            case MotionEventCompat.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
                 break;
 
@@ -931,9 +897,8 @@
         return animation != null && animation.hasStarted() && !animation.hasEnded();
     }
 
-    @SuppressLint("NewApi")
     private void moveSpinner(float overscrollTop) {
-        mProgress.showArrow(true);
+        mProgress.setArrowEnabled(true);
         float originalDragPercent = overscrollTop / mTotalDragDistance;
 
         float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
@@ -953,8 +918,8 @@
             mCircleView.setVisibility(View.VISIBLE);
         }
         if (!mScale) {
-            ViewCompat.setScaleX(mCircleView, 1f);
-            ViewCompat.setScaleY(mCircleView, 1f);
+            mCircleView.setScaleX(1f);
+            mCircleView.setScaleY(1f);
         }
 
         if (mScale) {
@@ -978,7 +943,7 @@
 
         float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f;
         mProgress.setProgressRotation(rotation);
-        setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop, true /* requires update */);
+        setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop);
     }
 
     private void finishSpinner(float overscrollTop) {
@@ -1010,13 +975,13 @@
                 };
             }
             animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
-            mProgress.showArrow(false);
+            mProgress.setArrowEnabled(false);
         }
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
         int pointerIndex = -1;
 
         if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
@@ -1055,8 +1020,8 @@
                 }
                 break;
             }
-            case MotionEventCompat.ACTION_POINTER_DOWN: {
-                pointerIndex = MotionEventCompat.getActionIndex(ev);
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                pointerIndex = ev.getActionIndex();
                 if (pointerIndex < 0) {
                     Log.e(LOG_TAG,
                             "Got ACTION_POINTER_DOWN event but have an invalid action index.");
@@ -1066,7 +1031,7 @@
                 break;
             }
 
-            case MotionEventCompat.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
                 break;
 
@@ -1093,7 +1058,6 @@
         return true;
     }
 
-    @SuppressLint("NewApi")
     private void startDragging(float y) {
         final float yDiff = y - mInitialDownY;
         if (yDiff > mTouchSlop && !mIsBeingDragged) {
@@ -1144,7 +1108,7 @@
             }
             targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
             int offset = targetTop - mCircleView.getTop();
-            setTargetOffsetTopAndBottom(offset, false /* requires update */);
+            setTargetOffsetTopAndBottom(offset);
             mProgress.setArrowScale(1 - interpolatedTime);
         }
     };
@@ -1153,7 +1117,7 @@
         int targetTop = 0;
         targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime));
         int offset = targetTop - mCircleView.getTop();
-        setTargetOffsetTopAndBottom(offset, false /* requires update */);
+        setTargetOffsetTopAndBottom(offset);
     }
 
     private final Animation mAnimateToStartPosition = new Animation() {
@@ -1163,15 +1127,10 @@
         }
     };
 
-    @SuppressLint("NewApi")
     private void startScaleDownReturnToStartAnimation(int from,
             Animation.AnimationListener listener) {
         mFrom = from;
-        if (isAlphaUsedForScale()) {
-            mStartingScale = mProgress.getAlpha();
-        } else {
-            mStartingScale = ViewCompat.getScaleX(mCircleView);
-        }
+        mStartingScale = mCircleView.getScaleX();
         mScaleDownToStartAnimation = new Animation() {
             @Override
             public void applyTransformation(float interpolatedTime, Transformation t) {
@@ -1188,17 +1147,14 @@
         mCircleView.startAnimation(mScaleDownToStartAnimation);
     }
 
-    void setTargetOffsetTopAndBottom(int offset, boolean requiresUpdate) {
+    void setTargetOffsetTopAndBottom(int offset) {
         mCircleView.bringToFront();
         ViewCompat.offsetTopAndBottom(mCircleView, offset);
         mCurrentTargetOffsetTop = mCircleView.getTop();
-        if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) {
-            invalidate();
-        }
     }
 
     private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+        final int pointerIndex = ev.getActionIndex();
         final int pointerId = ev.getPointerId(pointerIndex);
         if (pointerId == mActivePointerId) {
             // This was our active pointer going up. Choose a new
diff --git a/core-ui/java/android/support/v4/widget/ViewDragHelper.java b/core-ui/java/android/support/v4/widget/ViewDragHelper.java
index 171e292..c222c17 100644
--- a/core-ui/java/android/support/v4/widget/ViewDragHelper.java
+++ b/core-ui/java/android/support/v4/widget/ViewDragHelper.java
@@ -18,8 +18,6 @@
 package android.support.v4.widget;
 
 import android.content.Context;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -28,6 +26,7 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
+import android.widget.OverScroller;
 
 import java.util.Arrays;
 
@@ -130,7 +129,7 @@
     private int mEdgeSize;
     private int mTrackingEdges;
 
-    private ScrollerCompat mScroller;
+    private OverScroller mScroller;
 
     private final Callback mCallback;
 
@@ -391,7 +390,7 @@
         mTouchSlop = vc.getScaledTouchSlop();
         mMaxVelocity = vc.getScaledMaximumFlingVelocity();
         mMinVelocity = vc.getScaledMinimumFlingVelocity();
-        mScroller = ScrollerCompat.create(context, sInterpolator);
+        mScroller = new OverScroller(context, sInterpolator);
     }
 
     /**
@@ -570,8 +569,8 @@
         }
 
         return forceSettleCapturedViewAt(finalLeft, finalTop,
-                (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
-                (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId));
+                (int) mVelocityTracker.getXVelocity(mActivePointerId),
+                (int) mVelocityTracker.getYVelocity(mActivePointerId));
     }
 
     /**
@@ -682,7 +681,7 @@
 
     private float distanceInfluenceForSnapDuration(float f) {
         f -= 0.5f; // center the values about 0.
-        f *= 0.3f * Math.PI / 2.0f;
+        f *= 0.3f * (float) Math.PI / 2.0f;
         return (float) Math.sin(f);
     }
 
@@ -703,8 +702,8 @@
         }
 
         mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(),
-                (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
-                (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
+                (int) mVelocityTracker.getXVelocity(mActivePointerId),
+                (int) mVelocityTracker.getYVelocity(mActivePointerId),
                 minLeft, maxLeft, minTop, maxTop);
 
         setDragState(STATE_SETTLING);
@@ -939,8 +938,7 @@
             }
         }
 
-        return checkV && (ViewCompat.canScrollHorizontally(v, -dx)
-                || ViewCompat.canScrollVertically(v, -dy));
+        return checkV && (v.canScrollHorizontally(-dx) || v.canScrollVertically(-dy));
     }
 
     /**
@@ -951,8 +949,8 @@
      * @return true if the parent view should return true from onInterceptTouchEvent
      */
     public boolean shouldInterceptTouchEvent(MotionEvent ev) {
-        final int action = MotionEventCompat.getActionMasked(ev);
-        final int actionIndex = MotionEventCompat.getActionIndex(ev);
+        final int action = ev.getActionMasked();
+        final int actionIndex = ev.getActionIndex();
 
         if (action == MotionEvent.ACTION_DOWN) {
             // Reset things for a new event stream, just in case we didn't get
@@ -986,7 +984,7 @@
                 break;
             }
 
-            case MotionEventCompat.ACTION_POINTER_DOWN: {
+            case MotionEvent.ACTION_POINTER_DOWN: {
                 final int pointerId = ev.getPointerId(actionIndex);
                 final float x = ev.getX(actionIndex);
                 final float y = ev.getY(actionIndex);
@@ -1041,12 +1039,10 @@
                         final int targetTop = oldTop + (int) dy;
                         final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,
                                 (int) dy);
-                        final int horizontalDragRange = mCallback.getViewHorizontalDragRange(
-                                toCapture);
-                        final int verticalDragRange = mCallback.getViewVerticalDragRange(toCapture);
-                        if ((horizontalDragRange == 0 || horizontalDragRange > 0
-                                && newLeft == oldLeft) && (verticalDragRange == 0
-                                || verticalDragRange > 0 && newTop == oldTop)) {
+                        final int hDragRange = mCallback.getViewHorizontalDragRange(toCapture);
+                        final int vDragRange = mCallback.getViewVerticalDragRange(toCapture);
+                        if ((hDragRange == 0 || (hDragRange > 0 && newLeft == oldLeft))
+                                && (vDragRange == 0 || (vDragRange > 0 && newTop == oldTop))) {
                             break;
                         }
                     }
@@ -1064,7 +1060,7 @@
                 break;
             }
 
-            case MotionEventCompat.ACTION_POINTER_UP: {
+            case MotionEvent.ACTION_POINTER_UP: {
                 final int pointerId = ev.getPointerId(actionIndex);
                 clearMotionHistory(pointerId);
                 break;
@@ -1087,8 +1083,8 @@
      * @param ev The touch event received by the parent view
      */
     public void processTouchEvent(MotionEvent ev) {
-        final int action = MotionEventCompat.getActionMasked(ev);
-        final int actionIndex = MotionEventCompat.getActionIndex(ev);
+        final int action = ev.getActionMasked();
+        final int actionIndex = ev.getActionIndex();
 
         if (action == MotionEvent.ACTION_DOWN) {
             // Reset things for a new event stream, just in case we didn't get
@@ -1122,7 +1118,7 @@
                 break;
             }
 
-            case MotionEventCompat.ACTION_POINTER_DOWN: {
+            case MotionEvent.ACTION_POINTER_DOWN: {
                 final int pointerId = ev.getPointerId(actionIndex);
                 final float x = ev.getX(actionIndex);
                 final float y = ev.getY(actionIndex);
@@ -1195,7 +1191,7 @@
                 break;
             }
 
-            case MotionEventCompat.ACTION_POINTER_UP: {
+            case MotionEvent.ACTION_POINTER_UP: {
                 final int pointerId = ev.getPointerId(actionIndex);
                 if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
                     // Try to find another pointer that's still holding on to the captured view.
@@ -1405,10 +1401,10 @@
     private void releaseViewForPointerUp() {
         mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
         final float xvel = clampMag(
-                VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
+                mVelocityTracker.getXVelocity(mActivePointerId),
                 mMinVelocity, mMaxVelocity);
         final float yvel = clampMag(
-                VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
+                mVelocityTracker.getYVelocity(mActivePointerId),
                 mMinVelocity, mMaxVelocity);
         dispatchViewReleased(xvel, yvel);
     }
diff --git a/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java b/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
deleted file mode 100644
index f4dba39..0000000
--- a/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 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.app;
-
-import android.R;
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(18)
-@TargetApi(18)
-class ActionBarDrawerToggleJellybeanMR2 {
-    private static final String TAG = "ActionBarDrawerToggleImplJellybeanMR2";
-
-    private static final int[] THEME_ATTRS = new int[] {
-            R.attr.homeAsUpIndicator
-    };
-
-    public static Object setActionBarUpIndicator(Object info, Activity activity,
-            Drawable drawable, int contentDescRes) {
-        final ActionBar actionBar = activity.getActionBar();
-        if (actionBar != null) {
-            actionBar.setHomeAsUpIndicator(drawable);
-            actionBar.setHomeActionContentDescription(contentDescRes);
-        }
-        return info;
-    }
-
-    public static Object setActionBarDescription(Object info, Activity activity,
-            int contentDescRes) {
-        final ActionBar actionBar = activity.getActionBar();
-        if (actionBar != null) {
-            actionBar.setHomeActionContentDescription(contentDescRes);
-        }
-        return info;
-    }
-
-    public static Drawable getThemeUpIndicator(Activity activity) {
-        final ActionBar actionBar = activity.getActionBar();
-        final Context context;
-        if (actionBar != null) {
-            context = actionBar.getThemedContext();
-        } else {
-            context = activity;
-        }
-
-        final TypedArray a = context.obtainStyledAttributes(null, THEME_ATTRS,
-                R.attr.actionBarStyle, 0);
-        final Drawable result = a.getDrawable(0);
-        a.recycle();
-        return result;
-    }
-}
diff --git a/core-ui/lint-baseline.xml b/core-ui/lint-baseline.xml
new file mode 100644
index 0000000..8961812
--- /dev/null
+++ b/core-ui/lint-baseline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="WrongConstant"
+        message="Must be one or more of: Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END"
+        errorLine1="                            + gravityToString(childGravity) + &quot; but this &quot; + TAG + &quot; already has a &quot;"
+        errorLine2="                                              ~~~~~~~~~~~~">
+        <location
+            file="java/android/support/v4/widget/DrawerLayout.java"
+            line="1075"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one or more of: Canvas.ALL_SAVE_FLAG"
+        errorLine1="        final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="java/android/support/v4/widget/SlidingPaneLayout.java"
+            line="997"
+            column="38"/>
+    </issue>
+
+</issues>
diff --git a/core-ui/tests/AndroidManifest.xml b/core-ui/tests/AndroidManifest.xml
index 7c39d4d..c1b1a75 100644
--- a/core-ui/tests/AndroidManifest.xml
+++ b/core-ui/tests/AndroidManifest.xml
@@ -15,13 +15,8 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.coreui.test">
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.VIBRATE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
@@ -29,19 +24,18 @@
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
 
     <application
-            android:supportsRtl="true"
-            android:theme="@style/TestActivityTheme">
-        <uses-library android:name="android.test.runner" />
+        android:supportsRtl="true"
+        android:theme="@style/TestActivityTheme">
         <activity android:name="android.support.v4.widget.ExploreByTouchHelperTestActivity"/>
 
+        <activity android:name="android.support.v4.widget.CircularProgressDrawableActivity"/>
+
         <activity android:name="android.support.v4.widget.SwipeRefreshLayoutActivity"/>
 
         <activity android:name="android.support.v4.view.ViewPagerWithTitleStripActivity"/>
 
         <activity android:name="android.support.v4.view.ViewPagerWithTabStripActivity"/>
+
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.support.coreui.test"
-                     />
 </manifest>
diff --git a/core-ui/tests/java/android/support/v4/testutils/PollingCheck.java b/core-ui/tests/java/android/support/v4/testutils/PollingCheck.java
deleted file mode 100644
index b4271f4..0000000
--- a/core-ui/tests/java/android/support/v4/testutils/PollingCheck.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.testutils;
-
-import java.util.concurrent.Callable;
-
-import junit.framework.Assert;
-
-public abstract class PollingCheck {
-    private static final long TIME_SLICE = 50;
-    private long mTimeout = 3000;
-
-    public static interface PollingCheckCondition {
-        boolean canProceed();
-    }
-
-    public PollingCheck() {
-    }
-
-    public PollingCheck(long timeout) {
-        mTimeout = timeout;
-    }
-
-    protected abstract boolean check();
-
-    public void run() {
-        if (check()) {
-            return;
-        }
-
-        long timeout = mTimeout;
-        while (timeout > 0) {
-            try {
-                Thread.sleep(TIME_SLICE);
-            } catch (InterruptedException e) {
-                Assert.fail("unexpected InterruptedException");
-            }
-
-            if (check()) {
-                return;
-            }
-
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail("unexpected timeout");
-    }
-
-    public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
-            throws Exception {
-        while (timeout > 0) {
-            if (condition.call()) {
-                return;
-            }
-
-            Thread.sleep(TIME_SLICE);
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail(message.toString());
-    }
-
-    public static void waitFor(final PollingCheckCondition condition) {
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-
-    public static void waitFor(long timeout, final PollingCheckCondition condition) {
-        new PollingCheck(timeout) {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-}
\ No newline at end of file
diff --git a/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java b/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java
index c6f07e5..3884e8f 100644
--- a/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java
+++ b/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java
@@ -16,14 +16,9 @@
 
 package android.support.v4.testutils;
 
-import java.lang.String;
-import java.util.List;
-
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.test.espresso.matcher.BoundedMatcher;
-import android.support.v4.testutils.TestUtils;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,6 +30,8 @@
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 
+import java.util.List;
+
 public class TestUtilsMatchers {
     /**
      * Returns a matcher that matches views which have specific background color.
diff --git a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
index 0631299..855d01d 100644
--- a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
+++ b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
@@ -16,6 +16,7 @@
 package android.support.v4.view;
 
 import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.pressKey;
 import static android.support.test.espresso.action.ViewActions.swipeLeft;
 import static android.support.test.espresso.action.ViewActions.swipeRight;
 import static android.support.test.espresso.assertion.PositionAssertions.isBelow;
@@ -46,6 +47,7 @@
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -61,14 +63,18 @@
 import android.graphics.Color;
 import android.support.coreui.test.R;
 import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.EspressoKey;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.support.v4.testutils.TestUtilsMatchers;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import org.junit.After;
@@ -97,7 +103,7 @@
         protected ArrayList<Pair<String, Q>> mEntries = new ArrayList<>();
 
         public void add(String title, Q content) {
-            mEntries.add(new Pair(title, content));
+            mEntries.add(new Pair<>(title, content));
         }
 
         @Override
@@ -202,6 +208,43 @@
         }
     }
 
+    protected static class ButtonPagerAdapter extends BasePagerAdapter<Integer> {
+        private ArrayList<Button[]> mButtons = new ArrayList<>();
+
+        @Override
+        public void add(String title, Integer content) {
+            super.add(title, content);
+            mButtons.add(new Button[3]);
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            final LinearLayout view = new LinearLayout(container.getContext());
+            view.setBackgroundColor(mEntries.get(position).second);
+            view.setOrientation(LinearLayout.HORIZONTAL);
+            configureInstantiatedItem(view, position);
+
+            for (int i = 0; i < 3; ++i) {
+                Button but = new Button(container.getContext());
+                but.setText("" + i);
+                but.setFocusableInTouchMode(true);
+                view.addView(but, ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT);
+                mButtons.get(position)[i] = but;
+            }
+
+            // Unlike ListView adapters, the ViewPager adapter is responsible
+            // for adding the view to the container.
+            container.addView(view);
+
+            return new ViewHolder(view, position);
+        }
+
+        public View getButton(int page, int idx) {
+            return mButtons.get(page)[idx];
+        }
+    }
+
     public BaseViewPagerTest(Class<T> activityClass) {
         super(activityClass);
     }
@@ -282,13 +325,13 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testPageSelectionsImmediate() {
         verifyPageSelections(false);
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testPageSelectionsSmooth() {
         verifyPageSelections(true);
     }
@@ -355,7 +398,7 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testPageSwipes() {
         verifyPageChangeViewActions(wrap(swipeLeft()), wrap(swipeRight()));
     }
@@ -367,7 +410,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testPageSwipesComposite() {
         assertEquals("Initial state", 0, mViewPager.getCurrentItem());
 
@@ -425,13 +468,13 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testPageContentImmediate() {
         verifyPageContent(false);
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testPageContentSmooth() {
         verifyPageContent(true);
     }
@@ -504,13 +547,13 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testAdapterChangeImmediate() {
         verifyAdapterChange(false);
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testAdapterChangeSmooth() {
         verifyAdapterChange(true);
     }
@@ -603,13 +646,13 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testPagerStripImmediate() {
         verifyPagerStrip(false);
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testPagerStripSmooth() {
         verifyPagerStrip(true);
     }
@@ -660,7 +703,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testPageScrollStateChangedImmediate() {
         // Note that all the actions tested in this method are immediate (no scrolling) and
         // as such we test that we do not get any calls to onPageScrollStateChanged in any of them
@@ -984,7 +1027,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testPageScrollPositionChangesImmediate() {
         // Scroll one page to the right
         verifyScrollCallbacksToHigherPage(scrollRight(false), 1);
@@ -1031,4 +1074,33 @@
         // Swipe one more page to the right
         verifyScrollCallbacksToLowerPage(wrap(swipeRight()), 0);
     }
+
+    @Test
+    @LargeTest
+    public void testKeyboardNavigation() {
+        ButtonPagerAdapter adapter = new ButtonPagerAdapter();
+        adapter.add("Red", Color.RED);
+        adapter.add("Green", Color.GREEN);
+        adapter.add("Blue", Color.BLUE);
+        onView(withId(R.id.pager)).perform(setAdapter(adapter), scrollToPage(0, false));
+        View firstButton = adapter.getButton(0, 0);
+        firstButton.requestFocus();
+        assertTrue(firstButton.isFocused());
+        assertEquals(0, mViewPager.getCurrentItem());
+
+        // Normal arrows should traverse contents first
+        onView(is(firstButton)).perform(pressKey(KeyEvent.KEYCODE_DPAD_RIGHT));
+        assertEquals(0, mViewPager.getCurrentItem());
+        assertTrue(adapter.getButton(0, 1).isFocused());
+
+        // Alt arrows should change page even if there are more focusables in that direction
+        onView(is(adapter.getButton(0, 1))).perform(pressKey(new EspressoKey.Builder()
+                .withAltPressed(true).withKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT).build()));
+        assertEquals(1, mViewPager.getCurrentItem());
+        assertTrue(adapter.getButton(1, 0).isFocused());
+
+        // Normal arrows should change page if there are no more focusables in that direction
+        onView(is(adapter.getButton(1, 0))).perform(pressKey(KeyEvent.KEYCODE_DPAD_LEFT));
+        assertEquals(0, mViewPager.getCurrentItem());
+    }
 }
diff --git a/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableActivity.java b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableActivity.java
new file mode 100644
index 0000000..743cd7d
--- /dev/null
+++ b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import android.support.coreui.test.R;
+import android.support.v4.BaseTestActivity;
+
+public class CircularProgressDrawableActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.circular_progress_drawable_activity;
+    }
+}
diff --git a/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableTest.java b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableTest.java
new file mode 100644
index 0000000..cf31952
--- /dev/null
+++ b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.support.test.filters.SmallTest;
+import android.support.v4.BaseInstrumentationTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+/**
+ * Tests for CircularProgressDrawable
+ */
+public class CircularProgressDrawableTest extends
+        BaseInstrumentationTestCase<CircularProgressDrawableActivity> {
+
+    public CircularProgressDrawableTest() {
+        super(CircularProgressDrawableActivity.class);
+    }
+
+    private CircularProgressDrawable mDrawableUnderTest;
+
+    @Mock
+    Canvas mMockCanvas;
+
+    @Before
+    public void setUp() {
+        Context context = mActivityTestRule.getActivity().getApplicationContext();
+        mMockCanvas = mock(Canvas.class);
+        mDrawableUnderTest = new CircularProgressDrawable(context);
+    }
+
+    @Test
+    @SmallTest
+    public void sizeIsSquareBasedOnSmallerEdgeWithNoCenterRadius() {
+        int width = 100;
+        int height = 50;
+        mDrawableUnderTest.setBounds(new Rect(0, 0, width, height));
+        mDrawableUnderTest.draw(mMockCanvas);
+
+        ArgumentCaptor<RectF> captor = ArgumentCaptor.forClass(RectF.class);
+        verify(mMockCanvas).drawArc(captor.capture(), anyFloat(), anyFloat(), anyBoolean(),
+                any(Paint.class));
+
+        assertTrue(captor.getValue().width() == captor.getValue().height());
+        assertTrue(captor.getValue().width() <= width);
+        assertTrue(captor.getValue().width() <= height);
+    }
+
+    @Test
+    @SmallTest
+    public void setCenterRadiusFixesSize() {
+        float radius = 10f;
+        float strokeWidth = 4f;
+        mDrawableUnderTest.setCenterRadius(radius);
+        mDrawableUnderTest.setStrokeWidth(strokeWidth);
+        mDrawableUnderTest.setBounds(new Rect(0, 0, 100, 50));
+        mDrawableUnderTest.draw(mMockCanvas);
+
+        ArgumentCaptor<RectF> boundsCaptor = ArgumentCaptor.forClass(RectF.class);
+        verify(mMockCanvas).drawArc(boundsCaptor.capture(), anyFloat(), anyFloat(), anyBoolean(),
+                any(Paint.class));
+
+        assertEquals((radius + strokeWidth / 2f) * 2, boundsCaptor.getValue().width(), 0.5);
+        assertEquals((radius + strokeWidth / 2f) * 2, boundsCaptor.getValue().height(), 0.5);
+    }
+}
diff --git a/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java b/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
index 4a10559..04a2835 100644
--- a/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
+++ b/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
@@ -34,10 +34,11 @@
 
 import android.support.coreui.test.R;
 import android.support.test.espresso.action.ViewActions;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
+import android.support.testutils.PollingCheck;
 import android.support.v4.BaseInstrumentationTestCase;
-import android.support.v4.testutils.PollingCheck;
 import android.view.View;
 
 import org.junit.Before;
@@ -141,7 +142,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testSwipeDownToRefreshInitiallyDisabled() throws Throwable {
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
diff --git a/core-ui/tests/res/layout/circular_progress_drawable_activity.xml b/core-ui/tests/res/layout/circular_progress_drawable_activity.xml
new file mode 100644
index 0000000..6bb6beb
--- /dev/null
+++ b/core-ui/tests/res/layout/circular_progress_drawable_activity.xml
@@ -0,0 +1,25 @@
+<?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"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <ImageView android:id="@+id/imageView" android:layout_width="match_parent"
+               android:layout_height="match_parent"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/core-ui/tests/res/values/colors.xml b/core-ui/tests/res/values/colors.xml
index d0d5309..3c7bf7c 100644
--- a/core-ui/tests/res/values/colors.xml
+++ b/core-ui/tests/res/values/colors.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <color name="text_color">#FF8090</color>
 
     <color name="test_red">#FF6030</color>
diff --git a/core-ui/tests/res/values/dimens.xml b/core-ui/tests/res/values/dimens.xml
index c3617a9..d473645 100644
--- a/core-ui/tests/res/values/dimens.xml
+++ b/core-ui/tests/res/values/dimens.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <dimen name="text_medium_size">20sp</dimen>
 </resources>
\ No newline at end of file
diff --git a/core-ui/tests/res/values/strings.xml b/core-ui/tests/res/values/strings.xml
index 2fa1430..e605c2c 100644
--- a/core-ui/tests/res/values/strings.xml
+++ b/core-ui/tests/res/values/strings.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <string name="hello">Hello World</string>
 </resources>
\ No newline at end of file
diff --git a/core-ui/tests/res/values/styles.xml b/core-ui/tests/res/values/styles.xml
index ae6325b..047e2d0 100644
--- a/core-ui/tests/res/values/styles.xml
+++ b/core-ui/tests/res/values/styles.xml
@@ -13,13 +13,8 @@
      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">
-    <style name="TestActivityTheme">
+<resources>
+    <style name="TestActivityTheme" parent="android:Theme.Holo">
         <item name="android:windowAnimationStyle">@null</item>
     </style>
-    <style name="TextMediumStyle" parent="@android:style/TextAppearance.Medium">
-        <item name="android:textSize">@dimen/text_medium_size</item>
-        <item name="android:textColor">@color/text_color</item>
-        <item name="android:textStyle">italic</item>
-    </style>
 </resources>
\ No newline at end of file
diff --git a/core-utils/Android.mk b/core-utils/Android.mk
index a65a2cd..5ea200e 100644
--- a/core-utils/Android.mk
+++ b/core-utils/Android.mk
@@ -28,16 +28,10 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
     $(call all-java-files-under,gingerbread) \
-    $(call all-java-files-under,honeycomb) \
-    $(call all-java-files-under,jellybean) \
     $(call all-java-files-under,kitkat) \
-    $(call all-java-files-under,api20) \
     $(call all-java-files-under,api21) \
-    $(call all-java-files-under,api23) \
-    $(call all-java-files-under,api24) \
     $(call all-java-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
diff --git a/core-utils/AndroidManifest-make.xml b/core-utils/AndroidManifest-make.xml
deleted file mode 100644
index 586a28e..0000000
--- a/core-utils/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.coreutils">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.coreutils"/>
-    <application />
-</manifest>
diff --git a/core-utils/AndroidManifest.xml b/core-utils/AndroidManifest.xml
index 6e730b9..5c10a5b 100644
--- a/core-utils/AndroidManifest.xml
+++ b/core-utils/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.coreutils">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.coreutils"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.coreutils"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java b/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
deleted file mode 100644
index 831e9dd..0000000
--- a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.print;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-
-/**
- * Api20 specific PrintManager API implementation.
- */
-@RequiresApi(20)
-@TargetApi(20)
-class PrintHelperApi20 extends PrintHelperKitkat {
-    PrintHelperApi20(Context context) {
-        super(context);
-
-        /**
-         * There is a bug in the PrintActivity that causes it to ignore the orientation
-         */
-        mPrintActivityRespectsOrientation = false;
-    }
-}
\ No newline at end of file
diff --git a/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java b/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
index d521293..2aa4345 100644
--- a/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
+++ b/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.graphics.drawable;
 
-import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Outline;
@@ -26,7 +25,6 @@
 import android.view.View;
 
 @RequiresApi(21)
-@TargetApi(21)
 class RoundedBitmapDrawable21 extends RoundedBitmapDrawable {
     protected RoundedBitmapDrawable21(Resources res, Bitmap bitmap) {
         super(res, bitmap);
diff --git a/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java b/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
index 03667b3..90e0038 100644
--- a/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
+++ b/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.provider;
 
-import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
@@ -28,14 +27,17 @@
 import java.util.ArrayList;
 
 @RequiresApi(21)
-@TargetApi(21)
 class DocumentsContractApi21 {
     private static final String TAG = "DocumentFile";
 
     public static Uri createFile(Context context, Uri self, String mimeType,
             String displayName) {
-        return DocumentsContract.createDocument(context.getContentResolver(), self, mimeType,
-                displayName);
+        try {
+            return DocumentsContract.createDocument(context.getContentResolver(), self, mimeType,
+                    displayName);
+        } catch (Exception e) {
+            return null;
+        }
     }
 
     public static Uri createDirectory(Context context, Uri self, String displayName) {
@@ -73,7 +75,12 @@
     }
 
     public static Uri renameTo(Context context, Uri self, String displayName) {
-        return DocumentsContract.renameDocument(context.getContentResolver(), self, displayName);
+        try {
+            return DocumentsContract.renameDocument(context.getContentResolver(), self,
+                    displayName);
+        } catch (Exception e) {
+            return null;
+        }
     }
 
     private static void closeQuietly(AutoCloseable closeable) {
diff --git a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java b/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
deleted file mode 100644
index e2f6d69..0000000
--- a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.print;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.print.PrintAttributes;
-import android.support.annotation.RequiresApi;
-
-/**
- * Api23 specific PrintManager API implementation.
- */
-@RequiresApi(23)
-@TargetApi(23)
-class PrintHelperApi23 extends PrintHelperApi20 {
-    @Override
-    protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
-        PrintAttributes.Builder b = super.copyAttributes(other);
-
-        if (other.getDuplexMode() != 0) {
-            b.setDuplexMode(other.getDuplexMode());
-        }
-
-        return b;
-    }
-
-    PrintHelperApi23(Context context) {
-        super(context);
-
-        mIsMinMarginsHandlingCorrect = false;
-    }
-}
diff --git a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java b/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
deleted file mode 100644
index 36edfbd..0000000
--- a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.print;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-
-/**
- * Api24 specific PrintManager API implementation.
- */
-@RequiresApi(24)
-@TargetApi(24)
-class PrintHelperApi24 extends PrintHelperApi23 {
-    PrintHelperApi24(Context context) {
-        super(context);
-
-        mIsMinMarginsHandlingCorrect = true;
-        mPrintActivityRespectsOrientation = true;
-    }
-}
\ No newline at end of file
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index d40781d..234ae39 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -1,36 +1,28 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-core-utils'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-compat')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+    api project(':support-compat')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
                 'gingerbread',
-                'honeycomb',
-                'jellybean',
                 'kitkat',
                 'api20',
                 'api21',
@@ -38,66 +30,11 @@
                 'api24',
                 'java'
         ]
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-        exclude('android/content/pm/**')
-        exclude('android/service/media/**')
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android Support Library core utils'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v4'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java b/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
index 72b6abb..d515561 100644
--- a/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
+++ b/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
@@ -15,7 +15,6 @@
  */
 package android.support.v4.graphics.drawable;
 
-import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
@@ -43,7 +42,6 @@
  * </p>
  */
 @RequiresApi(9)
-@TargetApi(9)
 public abstract class RoundedBitmapDrawable extends Drawable {
     private static final int DEFAULT_PAINT_FLAGS =
             Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
@@ -278,6 +276,7 @@
         }
     }
 
+    @Override
     public int getAlpha() {
         return mPaint.getAlpha();
     }
@@ -288,6 +287,7 @@
         invalidateSelf();
     }
 
+    @Override
     public ColorFilter getColorFilter() {
         return mPaint.getColorFilter();
     }
diff --git a/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java b/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
deleted file mode 100644
index d970019..0000000
--- a/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-
-/**
- * Implementation of TaskStackBuilder that can call Honeycomb APIs.
- */
-@RequiresApi(11)
-@TargetApi(11)
-class TaskStackBuilderHoneycomb {
-    public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
-            Intent[] intents, int flags) {
-        return PendingIntent.getActivities(context, requestCode, intents, flags);
-    }
-}
diff --git a/core-utils/java/android/support/v4/app/AppLaunchChecker.java b/core-utils/java/android/support/v4/app/AppLaunchChecker.java
index 86219d4..f8beb91 100644
--- a/core-utils/java/android/support/v4/app/AppLaunchChecker.java
+++ b/core-utils/java/android/support/v4/app/AppLaunchChecker.java
@@ -19,7 +19,6 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
diff --git a/core-utils/java/android/support/v4/app/FrameMetricsAggregator.java b/core-utils/java/android/support/v4/app/FrameMetricsAggregator.java
new file mode 100644
index 0000000..48beb2f
--- /dev/null
+++ b/core-utils/java/android/support/v4/app/FrameMetricsAggregator.java
@@ -0,0 +1,464 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.Activity;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.util.SparseIntArray;
+import android.view.Window;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * This class can be used to record and return data about per-frame durations. It returns those
+ * results in an array per metric type, with the results indicating how many samples were
+ * recorded for each duration value. The details of the durations data are described in
+ * {@link #getMetrics()}.
+ * <p>
+ * For more information on the various metrics tracked, see the documentation for the
+ * <a href="https://developer.android.com/reference/android/view/FrameMetrics.html">FrameMetrics
+ * </a> API added in API 24 as well as the
+ * <a href="https://developer.android.com/studio/profile/dev-options-rendering.html">GPU Profiling
+ * guide</a>.
+ */
+public class FrameMetricsAggregator {
+
+    private static final String TAG = "FrameMetrics";
+    private static final boolean DBG = false;
+
+    /**
+     * The index in the metrics array where the data for {@link #TOTAL_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int TOTAL_INDEX          = 0;
+    /**
+     * The index in the metrics array where the data for {@link #INPUT_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int INPUT_INDEX          = 1;
+    /**
+     * The index in the metrics array where the data for {@link #LAYOUT_MEASURE_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int LAYOUT_MEASURE_INDEX = 2;
+    /**
+     * The index in the metrics array where the data for {@link #DRAW_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int DRAW_INDEX           = 3;
+    /**
+     * The index in the metrics array where the data for {@link #SYNC_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int SYNC_INDEX           = 4;
+    /**
+     * The index in the metrics array where the data for {@link #SYNC_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int COMMAND_INDEX        = 5;
+    /**
+     * The index in the metrics array where the data for {@link #COMMAND_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int SWAP_INDEX           = 6;
+    /**
+     * The index in the metrics array where the data for {@link #DELAY_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int DELAY_INDEX          = 7;
+    /**
+     * The index in the metrics array where the data for {@link #ANIMATION_DURATION}
+     * is stored.
+     * @see #getMetrics()
+     */
+    public static final int ANIMATION_INDEX      = 8;
+    private static final int LAST_INDEX          = 8;
+
+    /**
+     * A flag indicating that the metrics should track the total duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int TOTAL_DURATION          = 1 << TOTAL_INDEX;
+    /**
+     * A flag indicating that the metrics should track the input duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int INPUT_DURATION          = 1 << INPUT_INDEX;
+    /**
+     * A flag indicating that the metrics should track the layout duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int LAYOUT_MEASURE_DURATION = 1 << LAYOUT_MEASURE_INDEX;
+    /**
+     * A flag indicating that the metrics should track the draw duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int DRAW_DURATION           = 1 << DRAW_INDEX;
+    /**
+     * A flag indicating that the metrics should track the sync duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int SYNC_DURATION           = 1 << SYNC_INDEX;
+    /**
+     * A flag indicating that the metrics should track the command duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int COMMAND_DURATION        = 1 << COMMAND_INDEX;
+    /**
+     * A flag indicating that the metrics should track the swap duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int SWAP_DURATION           = 1 << SWAP_INDEX;
+    /**
+     * A flag indicating that the metrics should track the delay duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int DELAY_DURATION          = 1 << DELAY_INDEX;
+    /**
+     * A flag indicating that the metrics should track the animation duration. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate all of the metrics that should be tracked for that activity.
+     */
+    public static final int ANIMATION_DURATION      = 1 << ANIMATION_INDEX;
+    /**
+     * A flag indicating that the metrics should track all durations. This is
+     * a shorthand for OR'ing all of the duration flags. This
+     * flag may be OR'd with the other flags here when calling {@link #FrameMetricsAggregator(int)}
+     * to indicate the metrics that should be tracked for that activity.
+     */
+    public static final int EVERY_DURATION          = 0x1ff;
+
+    private FrameMetricsBaseImpl mInstance;
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            flag = true,
+            value = {
+                    TOTAL_DURATION,
+                    INPUT_DURATION,
+                    LAYOUT_MEASURE_DURATION,
+                    DRAW_DURATION,
+                    SYNC_DURATION,
+                    COMMAND_DURATION,
+                    SWAP_DURATION,
+                    DELAY_DURATION,
+                    ANIMATION_DURATION,
+                    EVERY_DURATION
+            })
+    public @interface MetricType {}
+
+    /**
+     * Constructs a FrameMetricsAggregator object that will track {@link #TOTAL_DURATION}
+     * metrics. If more fine-grained metrics are needed, use {@link #FrameMetricsAggregator(int)}
+     * instead.
+     */
+    public FrameMetricsAggregator() {
+        this(TOTAL_DURATION);
+    }
+
+    /**
+     * Constructs a FrameMetricsAggregator object that will track the metrics specified bty
+     * {@code metricTypeFlags}, which is a value derived by OR'ing together metrics constants
+     * such as {@link #TOTAL_DURATION} to specify all metrics that should be tracked. For example,
+     * {@code TOTAL_DURATION | DRAW_DURATION} will track both the total and draw durations
+     * for every frame.
+     *
+     * @param metricTypeFlags A bitwise collection of flags indicating which metrics should
+     * be recorded.
+     */
+    public FrameMetricsAggregator(@MetricType int metricTypeFlags) {
+        if (Build.VERSION.SDK_INT >= 24) {
+            mInstance = new FrameMetricsApi24Impl(metricTypeFlags);
+        } else {
+            mInstance = new FrameMetricsBaseImpl();
+        }
+    }
+
+    /**
+     * Starts recording frame metrics for the given activity.
+     *
+     * @param activity The Activity object which will have its metrics measured.
+     */
+    public void add(@NonNull Activity activity) {
+        mInstance.add(activity);
+    }
+
+    /**
+     * Stops recording metrics for {@code activity} and returns the collected metrics so far.
+     * Recording will continue if there are still other activities being tracked. Calling
+     * remove() does not reset the metrics array; you must call {@link #reset()} to clear the
+     * data.
+     *
+     * @param activity The Activity to stop tracking metrics for.
+     * @return An array whose index refers to the type of metric stored in that item's
+     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
+     * the {@code [TOTAL_INDEX]} item.
+     * @see #getMetrics()
+     */
+    @Nullable
+    public SparseIntArray[] remove(@NonNull Activity activity) {
+        return mInstance.remove(activity);
+    }
+
+    /**
+     * Stops recording metrics for all Activities currently being tracked. Like {@link
+     * #remove(Activity)}, this method returns the currently-collected metrics. Calling
+     * stop() does not reset the metrics array; you must call {@link #reset()} to clear the
+     * data.
+     *
+     * @return An array whose index refers to the type of metric stored in that item's
+     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
+     * the {@code [TOTAL_INDEX]} item.
+     * @see #remove(Activity)
+     * @see #getMetrics()
+     */
+    @Nullable
+    public SparseIntArray[] stop() {
+        return mInstance.stop();
+    }
+
+    /**
+     * Resets the metrics data and returns the currently-collected metrics.
+     *
+     * @return An array whose index refers to the type of metric stored in that item's
+     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
+     * the {@code [TOTAL_INDEX]} item.
+     * @see #getMetrics()
+     */
+    @Nullable
+    public SparseIntArray[] reset() {
+        return mInstance.reset();
+    }
+
+    /**
+     * Returns the currently-collected metrics in an array of SparseIntArray objects.
+     * The index of the array indicates which metric's data is stored in that
+     * SparseIntArray object. For example, results for total duration will be in
+     * the {@code [TOTAL_INDEX]} item.
+     * <p>
+     * The return value may be null if no metrics were tracked. This is especially true on releases
+     * earlier than API 24, as the FrameMetrics system does not exist on these earlier release.
+     * If the return value is not null, any of the objects at a given index in the array
+     * may still be null, which indicates that data was not being tracked for that type of metric.
+     * For example, if the FrameMetricsAggregator was created with a call to
+     * {@code new FrameMetricsAggregator(TOTAL_DURATION | DRAW_DURATION)}, then the SparseIntArray
+     * at index {@code INPUT_INDEX} will be null.
+     * <p>
+     * For a given non-null SparseIntArray, the results stored are the number of samples at
+     * each millisecond value (rounded). For example, if a data sample consisted of total
+     * durations of 5.1ms, 5.8ms, 6.1ms, and 8.2ms, the SparseIntArray at {@code [TOTAL_DURATION]}
+     * would have key-value pairs (5, 1), (6, 2), (8, 1).
+     *
+     * @return An array whose index refers to the type of metric stored in that item's
+     * SparseIntArray object, e.g., data for {@code TOTAL_DURATION} is stored in
+     * the {@code [TOTAL_INDEX]} item.
+     */
+    @Nullable
+    public SparseIntArray[] getMetrics() {
+        return mInstance.getMetrics();
+    }
+
+    /**
+     * Base implementation noops everything - there's no data to return on pre-API24 releases.
+     */
+    private static class FrameMetricsBaseImpl {
+
+        public void add(Activity activity) {
+        }
+
+        public SparseIntArray[] remove(Activity activity) {
+            return null;
+        }
+
+        public SparseIntArray[] stop() {
+            return null;
+        }
+
+        public SparseIntArray[] getMetrics() {
+            return null;
+        }
+
+        public SparseIntArray[] reset() {
+            return null;
+        }
+    }
+
+    @RequiresApi(24)
+    private static class FrameMetricsApi24Impl extends FrameMetricsBaseImpl {
+
+        private static final int NANOS_PER_MS = 1000000;
+        // rounding value adds half a millisecond, for rounding to nearest ms
+        private static final int NANOS_ROUNDING_VALUE = NANOS_PER_MS / 2;
+        private int mTrackingFlags;
+        private SparseIntArray[] mMetrics = new SparseIntArray[LAST_INDEX + 1];
+        private ArrayList<WeakReference<Activity>> mActivities = new ArrayList<>();
+        private static HandlerThread sHandlerThread = null;
+        private static Handler sHandler = null;
+
+        FrameMetricsApi24Impl(int trackingFlags) {
+            mTrackingFlags = trackingFlags;
+        }
+
+        Window.OnFrameMetricsAvailableListener mListener =
+                new Window.OnFrameMetricsAvailableListener() {
+            @Override
+            public void onFrameMetricsAvailable(Window window,
+                    android.view.FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
+                if ((mTrackingFlags & TOTAL_DURATION) != 0) {
+                    addDurationItem(mMetrics[TOTAL_INDEX],
+                            frameMetrics.getMetric(android.view.FrameMetrics.TOTAL_DURATION));
+                }
+                if ((mTrackingFlags & INPUT_DURATION) != 0) {
+                    addDurationItem(mMetrics[INPUT_INDEX],
+                            frameMetrics.getMetric(
+                                    android.view.FrameMetrics.INPUT_HANDLING_DURATION));
+                }
+                if ((mTrackingFlags & LAYOUT_MEASURE_DURATION) != 0) {
+                    addDurationItem(mMetrics[LAYOUT_MEASURE_INDEX],
+                            frameMetrics.getMetric(
+                                    android.view.FrameMetrics.LAYOUT_MEASURE_DURATION));
+                }
+                if ((mTrackingFlags & DRAW_DURATION) != 0) {
+                    addDurationItem(mMetrics[DRAW_INDEX],
+                            frameMetrics.getMetric(android.view.FrameMetrics.DRAW_DURATION));
+                }
+                if ((mTrackingFlags & SYNC_DURATION) != 0) {
+                    addDurationItem(mMetrics[SYNC_INDEX],
+                            frameMetrics.getMetric(android.view.FrameMetrics.SYNC_DURATION));
+                }
+                if ((mTrackingFlags & SWAP_DURATION) != 0) {
+                    addDurationItem(mMetrics[SWAP_INDEX],
+                            frameMetrics.getMetric(
+                                    android.view.FrameMetrics.SWAP_BUFFERS_DURATION));
+                }
+                if ((mTrackingFlags & COMMAND_DURATION) != 0) {
+                    addDurationItem(mMetrics[COMMAND_INDEX],
+                            frameMetrics.getMetric(
+                                    android.view.FrameMetrics.COMMAND_ISSUE_DURATION));
+                }
+                if ((mTrackingFlags & DELAY_DURATION) != 0) {
+                    addDurationItem(mMetrics[DELAY_INDEX],
+                            frameMetrics.getMetric(
+                                    android.view.FrameMetrics.UNKNOWN_DELAY_DURATION));
+                }
+                if ((mTrackingFlags & ANIMATION_DURATION) != 0) {
+                    addDurationItem(mMetrics[ANIMATION_INDEX],
+                            frameMetrics.getMetric(
+                                    android.view.FrameMetrics.ANIMATION_DURATION));
+                }
+            }
+        };
+
+        void addDurationItem(SparseIntArray buckets, long duration) {
+            if (buckets != null) {
+                int durationMs = (int) ((duration + NANOS_ROUNDING_VALUE) / NANOS_PER_MS);
+                if (duration >= 0) {
+                    // ignore values < 0; something must have gone wrong
+                    int oldValue = buckets.get(durationMs);
+                    buckets.put(durationMs, (oldValue + 1));
+                }
+            }
+        }
+
+        @Override
+        public void add(Activity activity) {
+            if (sHandlerThread == null) {
+                sHandlerThread = new HandlerThread("FrameMetricsAggregator");
+                sHandlerThread.start();
+                sHandler = new Handler(sHandlerThread.getLooper());
+            }
+            for (int i = 0; i <= LAST_INDEX; ++i) {
+                if (mMetrics[i] == null && (mTrackingFlags & (1 << i)) != 0) {
+                    mMetrics[i] = new SparseIntArray();
+                }
+            }
+            activity.getWindow().addOnFrameMetricsAvailableListener(mListener, sHandler);
+            mActivities.add(new WeakReference<>(activity));
+        }
+
+        @Override
+        public SparseIntArray[] remove(Activity activity) {
+            for (WeakReference<Activity> activityRef : mActivities) {
+                if (activityRef.get() == activity) {
+                    mActivities.remove(activityRef);
+                    break;
+                }
+            }
+            activity.getWindow().removeOnFrameMetricsAvailableListener(mListener);
+            return mMetrics;
+        }
+
+        @Override
+        public SparseIntArray[] stop() {
+            int size = mActivities.size();
+            for (int i = size - 1; i >= 0; i--) {
+                WeakReference<Activity> ref = mActivities.get(i);
+                Activity activity = ref.get();
+                if (ref.get() != null) {
+                    activity.getWindow().removeOnFrameMetricsAvailableListener(mListener);
+                    mActivities.remove(i);
+                }
+            }
+            return mMetrics;
+        }
+
+        @Override
+        public SparseIntArray[] getMetrics() {
+            return mMetrics;
+        }
+
+        @Override
+        public SparseIntArray[] reset() {
+            SparseIntArray[] returnVal = mMetrics;
+            mMetrics = new SparseIntArray[LAST_INDEX + 1];
+            return returnVal;
+        }
+
+    }
+
+}
diff --git a/core-utils/java/android/support/v4/app/NavUtils.java b/core-utils/java/android/support/v4/app/NavUtils.java
index f9661a7..99d4493 100644
--- a/core-utils/java/android/support/v4/app/NavUtils.java
+++ b/core-utils/java/android/support/v4/app/NavUtils.java
@@ -23,8 +23,8 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
 import android.support.annotation.Nullable;
-import android.support.v4.content.IntentCompat;
 import android.util.Log;
 
 /**
@@ -39,108 +39,6 @@
     private static final String TAG = "NavUtils";
     public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
 
-    interface NavUtilsImpl {
-        Intent getParentActivityIntent(Activity activity);
-        boolean shouldUpRecreateTask(Activity activity, Intent targetIntent);
-        void navigateUpTo(Activity activity, Intent upIntent);
-        String getParentActivityName(Context context, ActivityInfo info);
-    }
-
-    static class NavUtilsImplBase implements NavUtilsImpl {
-
-        @Override
-        public Intent getParentActivityIntent(Activity activity) {
-            String parentName = NavUtils.getParentActivityName(activity);
-            if (parentName == null) return null;
-
-            // If the parent itself has no parent, generate a main activity intent.
-            final ComponentName target = new ComponentName(activity, parentName);
-            try {
-                final String grandparent = NavUtils.getParentActivityName(activity, target);
-                final Intent parentIntent = grandparent == null
-                        ? IntentCompat.makeMainActivity(target)
-                        : new Intent().setComponent(target);
-                return parentIntent;
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "getParentActivityIntent: bad parentActivityName '" + parentName +
-                        "' in manifest");
-                return null;
-            }
-        }
-
-        @Override
-        public boolean shouldUpRecreateTask(Activity activity, Intent targetIntent) {
-            String action = activity.getIntent().getAction();
-            return action != null && !action.equals(Intent.ACTION_MAIN);
-        }
-
-        @Override
-        public void navigateUpTo(Activity activity, Intent upIntent) {
-            upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-            activity.startActivity(upIntent);
-            activity.finish();
-        }
-
-        @Override
-        public String getParentActivityName(Context context, ActivityInfo info) {
-            if (info.metaData == null) return null;
-            String parentActivity = info.metaData.getString(PARENT_ACTIVITY);
-            if (parentActivity == null) return null;
-            if (parentActivity.charAt(0) == '.') {
-                parentActivity = context.getPackageName() + parentActivity;
-            }
-            return parentActivity;
-        }
-    }
-
-    static class NavUtilsImplJB extends NavUtilsImplBase {
-
-        @Override
-        public Intent getParentActivityIntent(Activity activity) {
-            // Prefer the "real" JB definition if available,
-            // else fall back to the meta-data element.
-            Intent result = NavUtilsJB.getParentActivityIntent(activity);
-            if (result == null) {
-                result = superGetParentActivityIntent(activity);
-            }
-            return result;
-        }
-
-        Intent superGetParentActivityIntent(Activity activity) {
-            return super.getParentActivityIntent(activity);
-        }
-
-        @Override
-        public boolean shouldUpRecreateTask(Activity activity, Intent targetIntent) {
-            return NavUtilsJB.shouldUpRecreateTask(activity, targetIntent);
-        }
-
-        @Override
-        public void navigateUpTo(Activity activity, Intent upIntent) {
-            NavUtilsJB.navigateUpTo(activity, upIntent);
-        }
-
-        @Override
-        public String getParentActivityName(Context context, ActivityInfo info) {
-            String result = NavUtilsJB.getParentActivityName(info);
-            if (result == null) {
-                result = super.getParentActivityName(context, info);
-            }
-            return result;
-        }
-    }
-
-    private static final NavUtilsImpl IMPL;
-
-    static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 16) {
-            IMPL = new NavUtilsImplJB();
-        } else {
-            IMPL = new NavUtilsImplBase();
-        }
-    }
-
     /**
      * Returns true if sourceActivity should recreate the task when navigating 'up'
      * by using targetIntent.
@@ -156,7 +54,12 @@
      *         should be used for the destination
      */
     public static boolean shouldUpRecreateTask(Activity sourceActivity, Intent targetIntent) {
-        return IMPL.shouldUpRecreateTask(sourceActivity, targetIntent);
+        if (Build.VERSION.SDK_INT >= 16) {
+            return sourceActivity.shouldUpRecreateTask(targetIntent);
+        } else {
+            String action = sourceActivity.getIntent().getAction();
+            return action != null && !action.equals(Intent.ACTION_MAIN);
+        }
     }
 
     /**
@@ -199,7 +102,13 @@
      * @param upIntent An intent representing the target destination for up navigation
      */
     public static void navigateUpTo(Activity sourceActivity, Intent upIntent) {
-        IMPL.navigateUpTo(sourceActivity, upIntent);
+        if (Build.VERSION.SDK_INT >= 16) {
+            sourceActivity.navigateUpTo(upIntent);
+        } else {
+            upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            sourceActivity.startActivity(upIntent);
+            sourceActivity.finish();
+        }
     }
 
     /**
@@ -213,7 +122,29 @@
      * @return a new Intent targeting the defined parent activity of sourceActivity
      */
     public static Intent getParentActivityIntent(Activity sourceActivity) {
-        return IMPL.getParentActivityIntent(sourceActivity);
+        if (Build.VERSION.SDK_INT >= 16) {
+            // Prefer the "real" JB definition if available,
+            // else fall back to the meta-data element.
+            Intent result = sourceActivity.getParentActivityIntent();
+            if (result != null) {
+                return result;
+            }
+        }
+        String parentName = NavUtils.getParentActivityName(sourceActivity);
+        if (parentName == null) return null;
+
+        // If the parent itself has no parent, generate a main activity intent.
+        final ComponentName target = new ComponentName(sourceActivity, parentName);
+        try {
+            final String grandparent = NavUtils.getParentActivityName(sourceActivity, target);
+            return grandparent == null
+                    ? Intent.makeMainActivity(target)
+                    : new Intent().setComponent(target);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "getParentActivityIntent: bad parentActivityName '" + parentName
+                    + "' in manifest");
+            return null;
+        }
     }
 
     /**
@@ -236,7 +167,7 @@
         final ComponentName target = new ComponentName(context, parentActivity);
         final String grandparent = getParentActivityName(context, target);
         final Intent parentIntent = grandparent == null
-                ? IntentCompat.makeMainActivity(target)
+                ? Intent.makeMainActivity(target)
                 : new Intent().setComponent(target);
         return parentIntent;
     }
@@ -261,7 +192,7 @@
                 componentName.getPackageName(), parentActivity);
         final String grandparent = getParentActivityName(context, target);
         final Intent parentIntent = grandparent == null
-                ? IntentCompat.makeMainActivity(target)
+                ? Intent.makeMainActivity(target)
                 : new Intent().setComponent(target);
         return parentIntent;
     }
@@ -299,7 +230,22 @@
             throws NameNotFoundException {
         PackageManager pm = context.getPackageManager();
         ActivityInfo info = pm.getActivityInfo(componentName, PackageManager.GET_META_DATA);
-        String parentActivity = IMPL.getParentActivityName(context, info);
+        if (Build.VERSION.SDK_INT >= 16) {
+            String result = info.parentActivityName;
+            if (result != null) {
+                return result;
+            }
+        }
+        if (info.metaData == null) {
+            return null;
+        }
+        String parentActivity = info.metaData.getString(PARENT_ACTIVITY);
+        if (parentActivity == null) {
+            return null;
+        }
+        if (parentActivity.charAt(0) == '.') {
+            parentActivity = context.getPackageName() + parentActivity;
+        }
         return parentActivity;
     }
 
diff --git a/core-utils/java/android/support/v4/app/TaskStackBuilder.java b/core-utils/java/android/support/v4/app/TaskStackBuilder.java
index 0358b46..dc9a2dc 100644
--- a/core-utils/java/android/support/v4/app/TaskStackBuilder.java
+++ b/core-utils/java/android/support/v4/app/TaskStackBuilder.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.app;
 
+import android.support.annotation.RequiresApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -25,7 +26,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.content.IntentCompat;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -73,49 +73,33 @@
         Intent getSupportParentActivityIntent();
     }
 
-    interface TaskStackBuilderImpl {
-        PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
-                int flags, Bundle options);
-    }
-
-    static class TaskStackBuilderImplBase implements TaskStackBuilderImpl {
+    static class TaskStackBuilderBaseImpl {
         public PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
                 int flags, Bundle options) {
-            Intent topIntent = new Intent(intents[intents.length - 1]);
-            topIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            return PendingIntent.getActivity(context, requestCode, topIntent, flags);
+            intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+            return PendingIntent.getActivities(context, requestCode, intents, flags);
         }
     }
 
-    static class TaskStackBuilderImplHoneycomb implements TaskStackBuilderImpl {
+    @RequiresApi(16)
+    static class TaskStackBuilderApi16Impl extends TaskStackBuilderBaseImpl {
+        @Override
         public PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
                 int flags, Bundle options) {
-            intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                    IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
-                    IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
-            return TaskStackBuilderHoneycomb.getActivitiesPendingIntent(context, requestCode,
-                    intents, flags);
+            intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+            return PendingIntent.getActivities(context, requestCode, intents, flags, options);
         }
     }
 
-    static class TaskStackBuilderImplJellybean implements TaskStackBuilderImpl {
-        public PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
-                int flags, Bundle options) {
-            intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                    IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
-                    IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
-            return TaskStackBuilderJellybean.getActivitiesPendingIntent(context, requestCode,
-                    intents, flags, options);
-        }
-    }
-
-    private static final TaskStackBuilderImpl IMPL;
+    private static final TaskStackBuilderBaseImpl IMPL;
 
     static {
-        if (Build.VERSION.SDK_INT >= 11) {
-            IMPL = new TaskStackBuilderImplHoneycomb();
+        if (Build.VERSION.SDK_INT >= 16) {
+            IMPL = new TaskStackBuilderApi16Impl();
         } else {
-            IMPL = new TaskStackBuilderImplBase();
+            IMPL = new TaskStackBuilderBaseImpl();
         }
     }
 
@@ -287,6 +271,7 @@
     /**
      * @deprecated Use editIntentAt instead
      */
+    @Override
     @Deprecated
     public Iterator<Intent> iterator() {
         return mIntents.iterator();
@@ -322,9 +307,8 @@
         }
 
         Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
-        intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
-                IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
+        intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
         if (!ContextCompat.startActivities(mSourceContext, intents, options)) {
             Intent topIntent = new Intent(intents[intents.length - 1]);
             topIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -367,9 +351,8 @@
         }
 
         Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
-        intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
-                IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
+        intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
         // Appropriate flags will be added by the call below.
         return IMPL.getPendingIntent(mSourceContext, intents, requestCode, flags, options);
     }
@@ -385,9 +368,8 @@
         Intent[] intents = new Intent[mIntents.size()];
         if (intents.length == 0) return intents;
 
-        intents[0] = new Intent(mIntents.get(0)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
-                IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
+        intents[0] = new Intent(mIntents.get(0)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
         for (int i = 1; i < intents.length; i++) {
             intents[i] = new Intent(mIntents.get(i));
         }
diff --git a/core-utils/java/android/support/v4/content/FileProvider.java b/core-utils/java/android/support/v4/content/FileProvider.java
index 9e82d63..c49fc12 100644
--- a/core-utils/java/android/support/v4/content/FileProvider.java
+++ b/core-utils/java/android/support/v4/content/FileProvider.java
@@ -32,6 +32,7 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.OpenableColumns;
+import android.support.annotation.GuardedBy;
 import android.text.TextUtils;
 import android.webkit.MimeTypeMap;
 
@@ -339,7 +340,7 @@
 
     private static final File DEVICE_ROOT = new File("/");
 
-    // @GuardedBy("sCache")
+    @GuardedBy("sCache")
     private static HashMap<String, PathStrategy> sCache = new HashMap<String, PathStrategy>();
 
     private PathStrategy mStrategy;
diff --git a/core-utils/java/android/support/v4/content/LocalBroadcastManager.java b/core-utils/java/android/support/v4/content/LocalBroadcastManager.java
index 9f4c35c..324bb30 100644
--- a/core-utils/java/android/support/v4/content/LocalBroadcastManager.java
+++ b/core-utils/java/android/support/v4/content/LocalBroadcastManager.java
@@ -44,10 +44,11 @@
  * </ul>
  */
 public final class LocalBroadcastManager {
-    private static class ReceiverRecord {
+    private static final class ReceiverRecord {
         final IntentFilter filter;
         final BroadcastReceiver receiver;
         boolean broadcasting;
+        boolean dead;
 
         ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
             filter = _filter;
@@ -61,12 +62,15 @@
             builder.append(receiver);
             builder.append(" filter=");
             builder.append(filter);
+            if (dead) {
+                builder.append(" DEAD");
+            }
             builder.append("}");
             return builder.toString();
         }
     }
 
-    private static class BroadcastRecord {
+    private static final class BroadcastRecord {
         final Intent intent;
         final ArrayList<ReceiverRecord> receivers;
 
@@ -81,13 +85,11 @@
 
     private final Context mAppContext;
 
-    private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
-            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
-    private final HashMap<String, ArrayList<ReceiverRecord>> mActions
-            = new HashMap<String, ArrayList<ReceiverRecord>>();
+    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
+            = new HashMap<>();
+    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
 
-    private final ArrayList<BroadcastRecord> mPendingBroadcasts
-            = new ArrayList<BroadcastRecord>();
+    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
 
     static final int MSG_EXEC_PENDING_BROADCASTS = 1;
 
@@ -133,12 +135,12 @@
     public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
         synchronized (mReceivers) {
             ReceiverRecord entry = new ReceiverRecord(filter, receiver);
-            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
+            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
             if (filters == null) {
-                filters = new ArrayList<IntentFilter>(1);
+                filters = new ArrayList<>(1);
                 mReceivers.put(receiver, filters);
             }
-            filters.add(filter);
+            filters.add(entry);
             for (int i=0; i<filter.countActions(); i++) {
                 String action = filter.getAction(i);
                 ArrayList<ReceiverRecord> entries = mActions.get(action);
@@ -162,20 +164,22 @@
      */
     public void unregisterReceiver(BroadcastReceiver receiver) {
         synchronized (mReceivers) {
-            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
+            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
             if (filters == null) {
                 return;
             }
-            for (int i=0; i<filters.size(); i++) {
-                IntentFilter filter = filters.get(i);
-                for (int j=0; j<filter.countActions(); j++) {
-                    String action = filter.getAction(j);
-                    ArrayList<ReceiverRecord> receivers = mActions.get(action);
+            for (int i=filters.size()-1; i>=0; i--) {
+                final ReceiverRecord filter = filters.get(i);
+                filter.dead = true;
+                for (int j=0; j<filter.filter.countActions(); j++) {
+                    final String action = filter.filter.getAction(j);
+                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                     if (receivers != null) {
-                        for (int k=0; k<receivers.size(); k++) {
-                            if (receivers.get(k).receiver == receiver) {
+                        for (int k=receivers.size()-1; k>=0; k--) {
+                            final ReceiverRecord rec = receivers.get(k);
+                            if (rec.receiver == receiver) {
+                                rec.dead = true;
                                 receivers.remove(k);
-                                k--;
                             }
                         }
                         if (receivers.size() <= 0) {
@@ -196,6 +200,10 @@
      *     Intent will receive the broadcast.
      *
      * @see #registerReceiver
+     *
+     * @return Returns true if the intent has been scheduled for delivery to one or more
+     * broadcast receivers.  (Note tha delivery may not ultimately take place if one of those
+     * receivers is unregistered before it is dispatched.)
      */
     public boolean sendBroadcast(Intent intent) {
         synchronized (mReceivers) {
@@ -281,7 +289,7 @@
 
     private void executePendingBroadcasts() {
         while (true) {
-            BroadcastRecord[] brs = null;
+            final BroadcastRecord[] brs;
             synchronized (mReceivers) {
                 final int N = mPendingBroadcasts.size();
                 if (N <= 0) {
@@ -292,9 +300,13 @@
                 mPendingBroadcasts.clear();
             }
             for (int i=0; i<brs.length; i++) {
-                BroadcastRecord br = brs[i];
-                for (int j=0; j<br.receivers.size(); j++) {
-                    br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
+                final BroadcastRecord br = brs[i];
+                final int nbr = br.receivers.size();
+                for (int j=0; j<nbr; j++) {
+                    final ReceiverRecord rec = br.receivers.get(j);
+                    if (!rec.dead) {
+                        rec.receiver.onReceive(mAppContext, br.intent);
+                    }
                 }
             }
         }
diff --git a/core-utils/java/android/support/v4/content/MimeTypeFilter.java b/core-utils/java/android/support/v4/content/MimeTypeFilter.java
new file mode 100644
index 0000000..8734c4d
--- /dev/null
+++ b/core-utils/java/android/support/v4/content/MimeTypeFilter.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.content;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Provides utility methods for matching MIME type filters used in ContentProvider.
+ *
+ * <p>Wildcards are allowed only instead of the entire type or subtype with a tree prefix.
+ * Eg. image\/*, *\/* is a valid filter and will match image/jpeg, but image/j* is invalid and
+ * it will not match image/jpeg. Suffixes and parameters are not supported, and they are treated
+ * as part of the subtype during matching. Neither type nor subtype can be empty.
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike the formal
+ * RFC definitions. As a result, you should always write these elements with lower case letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that they are converted to
+ * lower case.</em>
+ *
+ * <p>MIME types can be null or ill-formatted. In such case they won't match anything.
+ *
+ * <p>MIME type filters must be correctly formatted, or an exception will be thrown.
+ */
+public final class MimeTypeFilter {
+
+    private MimeTypeFilter() {
+    }
+
+    private static boolean mimeTypeAgainstFilter(
+            @NonNull String[] mimeTypeParts, @NonNull String[] filterParts) {
+        if (filterParts.length != 2) {
+            throw new IllegalArgumentException(
+                    "Ill-formatted MIME type filter. Must be type/subtype.");
+        }
+        if (filterParts[0].isEmpty() || filterParts[1].isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Ill-formatted MIME type filter. Type or subtype empty.");
+        }
+        if (mimeTypeParts.length != 2) {
+            return false;
+        }
+        if (!"*".equals(filterParts[0])
+                && !filterParts[0].equals(mimeTypeParts[0])) {
+            return false;
+        }
+        if (!"*".equals(filterParts[1])
+                && !filterParts[1].equals(mimeTypeParts[1])) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Matches one nullable MIME type against one MIME type filter.
+     * @return True if the {@code mimeType} matches the {@code filter}.
+     */
+    public static boolean matches(@Nullable String mimeType, @NonNull String filter) {
+        if (mimeType == null) {
+            return false;
+        }
+
+        final String[] mimeTypeParts = mimeType.split("/");
+        final String[] filterParts = filter.split("/");
+
+        return mimeTypeAgainstFilter(mimeTypeParts, filterParts);
+    }
+
+    /**
+     * Matches one nullable MIME type against an array of MIME type filters.
+     * @return The first matching filter, or null if nothing matches.
+     */
+    public static String matches(
+            @Nullable String mimeType, @NonNull String[] filters) {
+        if (mimeType == null) {
+            return null;
+        }
+
+        final String[] mimeTypeParts = mimeType.split("/");
+        for (String filter : filters) {
+            final String[] filterParts = filter.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                return filter;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Matches multiple MIME types against an array of MIME type filters.
+     * @return The first matching MIME type, or null if nothing matches.
+     */
+    public static String matches(
+            @Nullable String[] mimeTypes, @NonNull String filter) {
+        if (mimeTypes == null) {
+            return null;
+        }
+
+        final String[] filterParts = filter.split("/");
+        for (String mimeType : mimeTypes) {
+            final String[] mimeTypeParts = mimeType.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                return mimeType;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Matches multiple MIME types against an array of MIME type filters.
+     * @return The list of matching MIME types, or empty array if nothing matches.
+     */
+    public static String[] matchesMany(
+            @Nullable String[] mimeTypes, @NonNull String filter) {
+        if (mimeTypes == null) {
+            return new String[] {};
+        }
+
+        final ArrayList<String> list = new ArrayList<>();
+        final String[] filterParts = filter.split("/");
+        for (String mimeType : mimeTypes) {
+            final String[] mimeTypeParts = mimeType.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                list.add(mimeType);
+            }
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+}
diff --git a/core-utils/java/android/support/v4/content/PermissionChecker.java b/core-utils/java/android/support/v4/content/PermissionChecker.java
index 35cd398..f0c7e74 100644
--- a/core-utils/java/android/support/v4/content/PermissionChecker.java
+++ b/core-utils/java/android/support/v4/content/PermissionChecker.java
@@ -16,12 +16,15 @@
 
 package android.support.v4.content;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Process;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
 import android.support.v4.app.AppOpsManagerCompat;
 
 import java.lang.annotation.Retention;
@@ -61,6 +64,8 @@
     /** Permission result: The permission is denied because the app op is not allowed. */
     public static final int PERMISSION_DENIED_APP_OP =  PackageManager.PERMISSION_DENIED  - 1;
 
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
     @IntDef({PERMISSION_GRANTED,
             PERMISSION_DENIED,
             PERMISSION_DENIED_APP_OP})
diff --git a/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java b/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java
index fcfc674..b0cd653 100644
--- a/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java
+++ b/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java
@@ -25,7 +25,7 @@
 import android.util.SparseArray;
 
 /**
- * Helper for the common pattern of implementing a {@link BroadcastReceiver}
+ * This helper is for an old pattern of implementing a {@link BroadcastReceiver}
  * that receives a device wakeup event and then passes the work off
  * to a {@link android.app.Service}, while ensuring that the
  * device does not go back to sleep during the transition.
@@ -57,12 +57,20 @@
  *
  * {@sample frameworks/support/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulService.java
  *      complete}
+ *
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#O Android O}, background check
+ * restrictions make this class no longer generally useful.  (It is generally not safe to
+ * start a service from the receipt of a broadcast, because you don't have any guarantees
+ * that your app is in the foreground at this point and thus allowed to do so.)  Instead,
+ * developers should use android.app.job.JobScheduler to schedule a job, and this
+ * does not require that the app hold a wake lock while doing so (the system will take
+ * care of holding a wake lock for the job).
  */
+@Deprecated
 public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
     private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid";
 
-    private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks
-            = new SparseArray<PowerManager.WakeLock>();
+    private static final SparseArray<PowerManager.WakeLock> sActiveWakeLocks = new SparseArray<>();
     private static int mNextId = 1;
 
     /**
@@ -80,7 +88,7 @@
      * Context.startService}.
      */
     public static ComponentName startWakefulService(Context context, Intent intent) {
-        synchronized (mActiveWakeLocks) {
+        synchronized (sActiveWakeLocks) {
             int id = mNextId;
             mNextId++;
             if (mNextId <= 0) {
@@ -97,8 +105,8 @@
             PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                     "wake:" + comp.flattenToShortString());
             wl.setReferenceCounted(false);
-            wl.acquire(60*1000);
-            mActiveWakeLocks.put(id, wl);
+            wl.acquire(60 * 1000);
+            sActiveWakeLocks.put(id, wl);
             return comp;
         }
     }
@@ -116,11 +124,11 @@
         if (id == 0) {
             return false;
         }
-        synchronized (mActiveWakeLocks) {
-            PowerManager.WakeLock wl = mActiveWakeLocks.get(id);
+        synchronized (sActiveWakeLocks) {
+            PowerManager.WakeLock wl = sActiveWakeLocks.get(id);
             if (wl != null) {
                 wl.release();
-                mActiveWakeLocks.remove(id);
+                sActiveWakeLocks.remove(id);
                 return true;
             }
             // We return true whether or not we actually found the wake lock
@@ -129,7 +137,7 @@
             // We just log a warning here if there is no wake lock found, which could
             // happen for example if this function is called twice on the same
             // intent or the process is killed and restarted before processing the intent.
-            Log.w("WakefulBroadcastReceiver", "No active wake lock id #" + id);
+            Log.w("WakefulBroadcastReceiv.", "No active wake lock id #" + id);
             return true;
         }
     }
diff --git a/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java b/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java
deleted file mode 100644
index be4ff11..0000000
--- a/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.content.res;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.AnyRes;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleableRes;
-import android.util.TypedValue;
-
-/**
- * Compat methods for accessing TypedArray values.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class TypedArrayUtils {
-    public static boolean getBoolean(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex, boolean defaultValue) {
-        boolean val = a.getBoolean(fallbackIndex, defaultValue);
-        return a.getBoolean(index, val);
-    }
-
-    public static Drawable getDrawable(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex) {
-        Drawable val = a.getDrawable(index);
-        if (val == null) {
-            val = a.getDrawable(fallbackIndex);
-        }
-        return val;
-    }
-
-    public static int getInt(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex, int defaultValue) {
-        int val = a.getInt(fallbackIndex, defaultValue);
-        return a.getInt(index, val);
-    }
-
-    public static @AnyRes int getResourceId(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex, @AnyRes int defaultValue) {
-        int val = a.getResourceId(fallbackIndex, defaultValue);
-        return a.getResourceId(index, val);
-    }
-
-    public static String getString(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex) {
-        String val = a.getString(index);
-        if (val == null) {
-            val = a.getString(fallbackIndex);
-        }
-        return val;
-    }
-
-    public static CharSequence getText(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex) {
-        CharSequence val = a.getText(index);
-        if (val == null) {
-            val = a.getText(fallbackIndex);
-        }
-        return val;
-    }
-
-    public static CharSequence[] getTextArray(TypedArray a, @StyleableRes int index,
-            @StyleableRes int fallbackIndex) {
-        CharSequence[] val = a.getTextArray(index);
-        if (val == null) {
-            val = a.getTextArray(fallbackIndex);
-        }
-        return val;
-    }
-
-    public static int getAttr(Context context, int attr, int fallbackAttr) {
-        TypedValue value = new TypedValue();
-        context.getTheme().resolveAttribute(attr, value, true);
-        if (value.resourceId != 0) {
-            return attr;
-        }
-        return fallbackAttr;
-    }
-}
diff --git a/core-utils/java/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java b/core-utils/java/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java
index 5efc74b..5e144c7 100644
--- a/core-utils/java/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java
+++ b/core-utils/java/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java
@@ -31,7 +31,7 @@
  * either from Bitmaps directly, or from streams and files.
  */
 public final class RoundedBitmapDrawableFactory {
-    private static final String TAG = "RoundedBitmapDrawableFactory";
+    private static final String TAG = "RoundedBitmapDrawableFa";
 
     private static class DefaultRoundedBitmapDrawable extends RoundedBitmapDrawable {
         DefaultRoundedBitmapDrawable(Resources res, Bitmap bitmap) {
diff --git a/core-utils/java/android/support/v4/math/MathUtils.java b/core-utils/java/android/support/v4/math/MathUtils.java
new file mode 100644
index 0000000..25f8644
--- /dev/null
+++ b/core-utils/java/android/support/v4/math/MathUtils.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.math;
+
+/**
+ * A utility class providing functions useful for common mathematical operations.
+ */
+public class MathUtils {
+
+    private MathUtils() {}
+
+    /**
+     * This method takes a numerical value and ensures it fits in a given numerical range. If the
+     * number is smaller than the minimum required by the range, then the minimum of the range will
+     * be returned. If the number is higher than the maximum allowed by the range then the maximum
+     * of the range will be returned.
+     *
+     * @param value the value to be clamped.
+     * @param min minimum resulting value.
+     * @param max maximum resulting value.
+     *
+     * @return the clamped value.
+     */
+    public static float clamp(float value, float min, float max) {
+        if (value < min) {
+            return min;
+        } else if (value > max) {
+            return max;
+        }
+        return value;
+    }
+
+    /**
+     * This method takes a numerical value and ensures it fits in a given numerical range. If the
+     * number is smaller than the minimum required by the range, then the minimum of the range will
+     * be returned. If the number is higher than the maximum allowed by the range then the maximum
+     * of the range will be returned.
+     *
+     * @param value the value to be clamped.
+     * @param min minimum resulting value.
+     * @param max maximum resulting value.
+     *
+     * @return the clamped value.
+     */
+    public static double clamp(double value, double min, double max) {
+        if (value < min) {
+            return min;
+        } else if (value > max) {
+            return max;
+        }
+        return value;
+    }
+
+    /**
+     * This method takes a numerical value and ensures it fits in a given numerical range. If the
+     * number is smaller than the minimum required by the range, then the minimum of the range will
+     * be returned. If the number is higher than the maximum allowed by the range then the maximum
+     * of the range will be returned.
+     *
+     * @param value the value to be clamped.
+     * @param min minimum resulting value.
+     * @param max maximum resulting value.
+     *
+     * @return the clamped value.
+     */
+    public static int clamp(int value, int min, int max) {
+        if (value < min) {
+            return min;
+        } else if (value > max) {
+            return max;
+        }
+        return value;
+    }
+}
diff --git a/core-utils/java/android/support/v4/print/PrintHelper.java b/core-utils/java/android/support/v4/print/PrintHelper.java
index 87899e2..ce342e3 100644
--- a/core-utils/java/android/support/v4/print/PrintHelper.java
+++ b/core-utils/java/android/support/v4/print/PrintHelper.java
@@ -18,11 +18,36 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.os.Build;
-
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.pdf.PdfDocument;
 import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
+import android.print.pdf.PrintedPdfDocument;
+import android.support.annotation.IntDef;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
 
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Helper for printing bitmaps.
@@ -41,12 +66,12 @@
     /**
      * this is a black and white image
      */
-    public static final int COLOR_MODE_MONOCHROME = 1;
+    public static final int COLOR_MODE_MONOCHROME = PrintAttributes.COLOR_MODE_MONOCHROME;
 
     /**
      * this is a color image (default)
      */
-    public static final int COLOR_MODE_COLOR = 2;
+    public static final int COLOR_MODE_COLOR = PrintAttributes.COLOR_MODE_COLOR;
 
     /**
      * Print the image in landscape orientation (default).
@@ -64,14 +89,25 @@
      * document to print or printing was cancelled.
      */
     public interface OnPrintFinishCallback {
-
         /**
          * Called when a print operation is finished.
          */
-        public void onFinish();
+        void onFinish();
     }
 
-    PrintHelperVersionImpl mImpl;
+    @IntDef({SCALE_MODE_FIT, SCALE_MODE_FILL})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface ScaleMode {}
+
+    @IntDef({COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface ColorMode {}
+
+    @IntDef({ORIENTATION_LANDSCAPE, ORIENTATION_PORTRAIT})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface Orientation {}
+
+    private final PrintHelperVersionImpl mImpl;
 
     /**
      * Gets whether the system supports printing.
@@ -79,67 +115,72 @@
      * @return True if printing is supported.
      */
     public static boolean systemSupportsPrint() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            // Supported on Android 4.4 or later.
-            return true;
-        }
-        return false;
+        // Supported on Android 4.4 or later.
+        return Build.VERSION.SDK_INT >= 19;
     }
 
     /**
      * Interface implemented by classes that support printing
      */
-    static interface PrintHelperVersionImpl {
+    interface PrintHelperVersionImpl {
 
-        public void setScaleMode(int scaleMode);
+        void setScaleMode(int scaleMode);
 
-        public int getScaleMode();
+        int getScaleMode();
 
-        public void setColorMode(int colorMode);
+        void setColorMode(int colorMode);
 
-        public int getColorMode();
+        int getColorMode();
 
-        public void setOrientation(int orientation);
+        void setOrientation(int orientation);
 
-        public int getOrientation();
+        int getOrientation();
 
-        public void printBitmap(String jobName, Bitmap bitmap, OnPrintFinishCallback callback);
+        void printBitmap(String jobName, Bitmap bitmap, OnPrintFinishCallback callback);
 
-        public void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback)
+        void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback)
                 throws FileNotFoundException;
     }
 
     /**
      * Implementation used when we do not support printing
      */
-    private static final class PrintHelperStubImpl implements PrintHelperVersionImpl {
-        int mScaleMode = SCALE_MODE_FILL;
-        int mColorMode = COLOR_MODE_COLOR;
-        int mOrientation = ORIENTATION_LANDSCAPE;
+    private static final class PrintHelperStub implements PrintHelperVersionImpl {
+        @ScaleMode int mScaleMode = SCALE_MODE_FILL;
+        @ColorMode int mColorMode = COLOR_MODE_COLOR;
+        @Orientation int mOrientation = ORIENTATION_LANDSCAPE;
+
         @Override
-        public void setScaleMode(int scaleMode) {
+        public void setScaleMode(@ScaleMode int scaleMode) {
             mScaleMode = scaleMode;
         }
 
+        @ScaleMode
+        @Override
+        public int getScaleMode() {
+            return mScaleMode;
+        }
+
+        @ColorMode
         @Override
         public int getColorMode() {
             return mColorMode;
         }
 
         @Override
-        public void setColorMode(int colorMode) {
+        public void setColorMode(@ColorMode int colorMode) {
             mColorMode = colorMode;
         }
 
         @Override
-        public void setOrientation(int orientation) { mOrientation = orientation; }
+        public void setOrientation(@Orientation int orientation) {
+            mOrientation = orientation;
+        }
 
+        @Orientation
         @Override
-        public int getOrientation() { return mOrientation; }
-
-        @Override
-        public int getScaleMode() {
-            return mScaleMode;
+        public int getOrientation() {
+            return mOrientation;
         }
 
         @Override
@@ -152,133 +193,710 @@
     }
 
     /**
-     * Generic implementation for KitKat to Api24
+     * Kitkat specific PrintManager API implementation.
      */
-    private static class PrintHelperImpl<RealHelper extends PrintHelperKitkat>
-            implements PrintHelperVersionImpl {
-        private final RealHelper mPrintHelper;
+    @RequiresApi(19)
+    private static class PrintHelperApi19 implements PrintHelperVersionImpl{
+        private static final String LOG_TAG = "PrintHelperApi19";
+        // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
+        private static final int MAX_PRINT_SIZE = 3500;
+        final Context mContext;
+        BitmapFactory.Options mDecodeOptions = null;
+        private final Object mLock = new Object();
 
-        protected PrintHelperImpl(RealHelper helper) {
-            mPrintHelper = helper;
+        /**
+         * Whether the PrintActivity respects the suggested orientation
+         */
+        protected boolean mPrintActivityRespectsOrientation;
+
+        /**
+         * Whether the print subsystem handles min margins correctly. If not the print helper needs
+         * to fake this.
+         */
+        protected boolean mIsMinMarginsHandlingCorrect;
+
+        @ScaleMode int mScaleMode = SCALE_MODE_FILL;
+
+        @ColorMode int mColorMode = COLOR_MODE_COLOR;
+
+        @Orientation int mOrientation;
+
+        PrintHelperApi19(Context context) {
+            mPrintActivityRespectsOrientation = true;
+            mIsMinMarginsHandlingCorrect = true;
+
+            mContext = context;
         }
 
+        /**
+         * Selects whether the image will fill the paper and be cropped
+         * <p/>
+         * {@link #SCALE_MODE_FIT}
+         * or whether the image will be scaled but leave white space
+         * {@link #SCALE_MODE_FILL}.
+         *
+         * @param scaleMode {@link #SCALE_MODE_FIT} or
+         *                  {@link #SCALE_MODE_FILL}
+         */
         @Override
-        public void setScaleMode(int scaleMode) {
-            mPrintHelper.setScaleMode(scaleMode);
+        public void setScaleMode(@ScaleMode int scaleMode) {
+            mScaleMode = scaleMode;
         }
 
+        /**
+         * Returns the scale mode with which the image will fill the paper.
+         *
+         * @return The scale Mode: {@link #SCALE_MODE_FIT} or
+         * {@link #SCALE_MODE_FILL}
+         */
+        @ScaleMode
         @Override
         public int getScaleMode() {
-            return mPrintHelper.getScaleMode();
+            return mScaleMode;
         }
 
+        /**
+         * Sets whether the image will be printed in color (default)
+         * {@link #COLOR_MODE_COLOR} or in back and white
+         * {@link #COLOR_MODE_MONOCHROME}.
+         *
+         * @param colorMode The color mode which is one of
+         *                  {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}.
+         */
         @Override
-        public void setColorMode(int colorMode) {
-            mPrintHelper.setColorMode(colorMode);
+        public void setColorMode(@ColorMode int colorMode) {
+            mColorMode = colorMode;
         }
 
+        /**
+         * Sets whether to select landscape (default), {@link #ORIENTATION_LANDSCAPE}
+         * or portrait {@link #ORIENTATION_PORTRAIT}
+         * @param orientation The page orientation which is one of
+         *                    {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}.
+         */
         @Override
-        public int getColorMode() {
-            return mPrintHelper.getColorMode();
+        public void setOrientation(@Orientation int orientation) {
+            mOrientation = orientation;
         }
 
-        @Override
-        public void setOrientation(int orientation) {
-            mPrintHelper.setOrientation(orientation);
-        }
-
+        /**
+         * Gets the page orientation with which the image will be printed.
+         *
+         * @return The preferred orientation which is one of
+         * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}
+         */
+        @Orientation
         @Override
         public int getOrientation() {
-            return mPrintHelper.getOrientation();
+            /// Unset defaults to landscape but might turn image
+            if (mOrientation == 0) {
+                return ORIENTATION_LANDSCAPE;
+            }
+            return mOrientation;
         }
 
+        /**
+         * Gets the color mode with which the image will be printed.
+         *
+         * @return The color mode which is one of {@link #COLOR_MODE_COLOR}
+         * and {@link #COLOR_MODE_MONOCHROME}.
+         */
+        @ColorMode
         @Override
-        public void printBitmap(String jobName, Bitmap bitmap,
+        public int getColorMode() {
+            return mColorMode;
+        }
+
+        /**
+         * Check if the supplied bitmap should best be printed on a portrait orientation paper.
+         *
+         * @param bitmap The bitmap to be printed.
+         * @return true iff the picture should best be printed on a portrait orientation paper.
+         */
+        private static boolean isPortrait(Bitmap bitmap) {
+            return bitmap.getWidth() <= bitmap.getHeight();
+        }
+
+        /**
+         * Create a build with a copy from the other print attributes.
+         *
+         * @param other The other print attributes
+         *
+         * @return A builder that will build print attributes that match the other attributes
+         */
+        protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
+            PrintAttributes.Builder b = (new PrintAttributes.Builder())
+                    .setMediaSize(other.getMediaSize())
+                    .setResolution(other.getResolution())
+                    .setMinMargins(other.getMinMargins());
+
+            if (other.getColorMode() != 0) {
+                b.setColorMode(other.getColorMode());
+            }
+
+            return b;
+        }
+
+        /**
+         * Prints a bitmap.
+         *
+         * @param jobName The print job name.
+         * @param bitmap  The bitmap to print.
+         * @param callback Optional callback to observe when printing is finished.
+         */
+        @Override
+        public void printBitmap(final String jobName, final Bitmap bitmap,
                 final OnPrintFinishCallback callback) {
-            PrintHelperKitkat.OnPrintFinishCallback delegateCallback = null;
-            if (callback != null) {
-                delegateCallback = new PrintHelperKitkat.OnPrintFinishCallback() {
-                    @Override
-                    public void onFinish() {
-                        callback.onFinish();
-                    }
-                };
+            if (bitmap == null) {
+                return;
             }
-            mPrintHelper.printBitmap(jobName, bitmap, delegateCallback);
+
+            final int fittingMode = mScaleMode; // grab the fitting mode at time of call
+            PrintManager printManager =
+                    (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
+            PrintAttributes.MediaSize mediaSize;
+            if (isPortrait(bitmap)) {
+                mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
+            } else {
+                mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
+            }
+            PrintAttributes attr = new PrintAttributes.Builder()
+                    .setMediaSize(mediaSize)
+                    .setColorMode(mColorMode)
+                    .build();
+
+            printManager.print(jobName,
+                    new PrintDocumentAdapter() {
+                        private PrintAttributes mAttributes;
+
+                        @Override
+                        public void onLayout(PrintAttributes oldPrintAttributes,
+                                PrintAttributes newPrintAttributes,
+                                CancellationSignal cancellationSignal,
+                                LayoutResultCallback layoutResultCallback,
+                                Bundle bundle) {
+
+                            mAttributes = newPrintAttributes;
+
+                            PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
+                                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                    .setPageCount(1)
+                                    .build();
+                            boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+                            layoutResultCallback.onLayoutFinished(info, changed);
+                        }
+
+                        @Override
+                        public void onWrite(PageRange[] pageRanges,
+                                ParcelFileDescriptor fileDescriptor,
+                                CancellationSignal cancellationSignal,
+                                WriteResultCallback writeResultCallback) {
+                            writeBitmap(mAttributes, fittingMode, bitmap, fileDescriptor,
+                                    cancellationSignal, writeResultCallback);
+                        }
+
+                        @Override
+                        public void onFinish() {
+                            if (callback != null) {
+                                callback.onFinish();
+                            }
+                        }
+                    }, attr);
         }
 
+        /**
+         * Calculates the transform the print an Image to fill the page
+         *
+         * @param imageWidth  with of bitmap
+         * @param imageHeight height of bitmap
+         * @param content     The output page dimensions
+         * @param fittingMode The mode of fitting {@link #SCALE_MODE_FILL} vs
+         *                    {@link #SCALE_MODE_FIT}
+         * @return Matrix to be used in canvas.drawBitmap(bitmap, matrix, null) call
+         */
+        private Matrix getMatrix(int imageWidth, int imageHeight, RectF content,
+                @ScaleMode int fittingMode) {
+            Matrix matrix = new Matrix();
+
+            // Compute and apply scale to fill the page.
+            float scale = content.width() / imageWidth;
+            if (fittingMode == SCALE_MODE_FILL) {
+                scale = Math.max(scale, content.height() / imageHeight);
+            } else {
+                scale = Math.min(scale, content.height() / imageHeight);
+            }
+            matrix.postScale(scale, scale);
+
+            // Center the content.
+            final float translateX = (content.width()
+                    - imageWidth * scale) / 2;
+            final float translateY = (content.height()
+                    - imageHeight * scale) / 2;
+            matrix.postTranslate(translateX, translateY);
+            return matrix;
+        }
+
+        /**
+         * Write a bitmap for a PDF document.
+         *
+         * @param attributes          The print attributes
+         * @param fittingMode         How to fit the bitmap
+         * @param bitmap              The bitmap to write
+         * @param fileDescriptor      The file to write to
+         * @param cancellationSignal  Signal cancelling operation
+         * @param writeResultCallback Callback to call once written
+         */
+        private void writeBitmap(final PrintAttributes attributes, final int fittingMode,
+                final Bitmap bitmap, final ParcelFileDescriptor fileDescriptor,
+                final CancellationSignal cancellationSignal,
+                final PrintDocumentAdapter.WriteResultCallback writeResultCallback) {
+            final PrintAttributes pdfAttributes;
+            if (mIsMinMarginsHandlingCorrect) {
+                pdfAttributes = attributes;
+            } else {
+                // If the handling of any margin != 0 is broken, strip the margins and add them to
+                // the bitmap later
+                pdfAttributes = copyAttributes(attributes)
+                        .setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)).build();
+            }
+
+            (new AsyncTask<Void, Void, Throwable>() {
+                @Override
+                protected Throwable doInBackground(Void... params) {
+                    try {
+                        if (cancellationSignal.isCanceled()) {
+                            return null;
+                        }
+
+                        PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
+                                pdfAttributes);
+
+                        Bitmap maybeGrayscale = convertBitmapForColorMode(bitmap,
+                                pdfAttributes.getColorMode());
+
+                        if (cancellationSignal.isCanceled()) {
+                            return null;
+                        }
+
+                        try {
+                            PdfDocument.Page page = pdfDocument.startPage(1);
+
+                            RectF contentRect;
+                            if (mIsMinMarginsHandlingCorrect) {
+                                contentRect = new RectF(page.getInfo().getContentRect());
+                            } else {
+                                // Create dummy doc that has the margins to compute correctly sized
+                                // content rectangle
+                                PrintedPdfDocument dummyDocument = new PrintedPdfDocument(mContext,
+                                        attributes);
+                                PdfDocument.Page dummyPage = dummyDocument.startPage(1);
+                                contentRect = new RectF(dummyPage.getInfo().getContentRect());
+                                dummyDocument.finishPage(dummyPage);
+                                dummyDocument.close();
+                            }
+
+                            // Resize bitmap
+                            Matrix matrix = getMatrix(
+                                    maybeGrayscale.getWidth(), maybeGrayscale.getHeight(),
+                                    contentRect, fittingMode);
+
+                            if (mIsMinMarginsHandlingCorrect) {
+                                // The pdfDocument takes care of the positioning and margins
+                            } else {
+                                // Move it to the correct position.
+                                matrix.postTranslate(contentRect.left, contentRect.top);
+
+                                // Cut off margins
+                                page.getCanvas().clipRect(contentRect);
+                            }
+
+                            // Draw the bitmap.
+                            page.getCanvas().drawBitmap(maybeGrayscale, matrix, null);
+
+                            // Finish the page.
+                            pdfDocument.finishPage(page);
+
+                            if (cancellationSignal.isCanceled()) {
+                                return null;
+                            }
+
+                            // Write the document.
+                            pdfDocument.writeTo(
+                                    new FileOutputStream(fileDescriptor.getFileDescriptor()));
+                            return null;
+                        } finally {
+                            pdfDocument.close();
+
+                            if (fileDescriptor != null) {
+                                try {
+                                    fileDescriptor.close();
+                                } catch (IOException ioe) {
+                                    // ignore
+                                }
+                            }
+                            // If we created a new instance for grayscaling, then recycle it here.
+                            if (maybeGrayscale != bitmap) {
+                                maybeGrayscale.recycle();
+                            }
+                        }
+                    } catch (Throwable t) {
+                        return t;
+                    }
+                }
+
+                @Override
+                protected void onPostExecute(Throwable throwable) {
+                    if (cancellationSignal.isCanceled()) {
+                        // Cancelled.
+                        writeResultCallback.onWriteCancelled();
+                    } else if (throwable == null) {
+                        // Done.
+                        writeResultCallback.onWriteFinished(
+                                new PageRange[] { PageRange.ALL_PAGES });
+                    } else {
+                        // Failed.
+                        Log.e(LOG_TAG, "Error writing printed content", throwable);
+                        writeResultCallback.onWriteFailed(null);
+                    }
+                }
+            }).execute();
+        }
+
+        /**
+         * Prints an image located at the Uri. Image types supported are those of
+         * <code>BitmapFactory.decodeStream</code> (JPEG, GIF, PNG, BMP, WEBP)
+         *
+         * @param jobName   The print job name.
+         * @param imageFile The <code>Uri</code> pointing to an image to print.
+         * @param callback Optional callback to observe when printing is finished.
+         * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
+         */
         @Override
-        public void printBitmap(String jobName, Uri imageFile,
-                final OnPrintFinishCallback callback) throws FileNotFoundException {
-            PrintHelperKitkat.OnPrintFinishCallback delegateCallback = null;
-            if (callback != null) {
-                delegateCallback = new PrintHelperKitkat.OnPrintFinishCallback() {
-                    @Override
-                    public void onFinish() {
+        public void printBitmap(final String jobName, final Uri imageFile,
+                final OnPrintFinishCallback callback)
+                throws FileNotFoundException {
+            final int fittingMode = mScaleMode;
+
+            PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
+                private PrintAttributes mAttributes;
+                AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
+                Bitmap mBitmap = null;
+
+                @Override
+                public void onLayout(final PrintAttributes oldPrintAttributes,
+                        final PrintAttributes newPrintAttributes,
+                        final CancellationSignal cancellationSignal,
+                        final LayoutResultCallback layoutResultCallback,
+                        Bundle bundle) {
+
+                    synchronized (this) {
+                        mAttributes = newPrintAttributes;
+                    }
+
+                    if (cancellationSignal.isCanceled()) {
+                        layoutResultCallback.onLayoutCancelled();
+                        return;
+                    }
+                    // we finished the load
+                    if (mBitmap != null) {
+                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
+                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                .setPageCount(1)
+                                .build();
+                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+                        layoutResultCallback.onLayoutFinished(info, changed);
+                        return;
+                    }
+
+                    mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
+                        @Override
+                        protected void onPreExecute() {
+                            // First register for cancellation requests.
+                            cancellationSignal.setOnCancelListener(
+                                    new CancellationSignal.OnCancelListener() {
+                                        @Override
+                                        public void onCancel() { // on different thread
+                                            cancelLoad();
+                                            cancel(false);
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        protected Bitmap doInBackground(Uri... uris) {
+                            try {
+                                return loadConstrainedBitmap(imageFile);
+                            } catch (FileNotFoundException e) {
+                          /* ignore */
+                            }
+                            return null;
+                        }
+
+                        @Override
+                        protected void onPostExecute(Bitmap bitmap) {
+                            super.onPostExecute(bitmap);
+
+                            // If orientation was not set by the caller, try to fit the bitmap on
+                            // the current paper by potentially rotating the bitmap by 90 degrees.
+                            if (bitmap != null
+                                    && (!mPrintActivityRespectsOrientation || mOrientation == 0)) {
+                                PrintAttributes.MediaSize mediaSize;
+
+                                synchronized (this) {
+                                    mediaSize = mAttributes.getMediaSize();
+                                }
+
+                                if (mediaSize != null) {
+                                    if (mediaSize.isPortrait() != isPortrait(bitmap)) {
+                                        Matrix rotation = new Matrix();
+
+                                        rotation.postRotate(90);
+                                        bitmap = Bitmap.createBitmap(bitmap, 0, 0,
+                                                bitmap.getWidth(), bitmap.getHeight(), rotation,
+                                                true);
+                                    }
+                                }
+                            }
+
+                            mBitmap = bitmap;
+                            if (bitmap != null) {
+                                PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
+                                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                        .setPageCount(1)
+                                        .build();
+
+                                boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+
+                                layoutResultCallback.onLayoutFinished(info, changed);
+
+                            } else {
+                                layoutResultCallback.onLayoutFailed(null);
+                            }
+                            mLoadBitmap = null;
+                        }
+
+                        @Override
+                        protected void onCancelled(Bitmap result) {
+                            // Task was cancelled, report that.
+                            layoutResultCallback.onLayoutCancelled();
+                            mLoadBitmap = null;
+                        }
+                    }.execute();
+                }
+
+                private void cancelLoad() {
+                    synchronized (mLock) { // prevent race with set null below
+                        if (mDecodeOptions != null) {
+                            mDecodeOptions.requestCancelDecode();
+                            mDecodeOptions = null;
+                        }
+                    }
+                }
+
+                @Override
+                public void onFinish() {
+                    super.onFinish();
+                    cancelLoad();
+                    if (mLoadBitmap != null) {
+                        mLoadBitmap.cancel(true);
+                    }
+                    if (callback != null) {
                         callback.onFinish();
                     }
-                };
+                    if (mBitmap != null) {
+                        mBitmap.recycle();
+                        mBitmap = null;
+                    }
+                }
+
+                @Override
+                public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
+                        CancellationSignal cancellationSignal,
+                        WriteResultCallback writeResultCallback) {
+                    writeBitmap(mAttributes, fittingMode, mBitmap, fileDescriptor,
+                            cancellationSignal, writeResultCallback);
+                }
+            };
+
+            PrintManager printManager =
+                    (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
+            PrintAttributes.Builder builder = new PrintAttributes.Builder();
+            builder.setColorMode(mColorMode);
+
+            if (mOrientation == ORIENTATION_LANDSCAPE || mOrientation == 0) {
+                builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE);
+            } else if (mOrientation == ORIENTATION_PORTRAIT) {
+                builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_PORTRAIT);
             }
-            mPrintHelper.printBitmap(jobName, imageFile, delegateCallback);
+            PrintAttributes attr = builder.build();
+
+            printManager.print(jobName, printDocumentAdapter, attr);
+        }
+
+        /**
+         * Loads a bitmap while limiting its size
+         *
+         * @param uri           location of a valid image
+         * @return the Bitmap
+         * @throws FileNotFoundException if the Uri does not point to an image
+         */
+        private Bitmap loadConstrainedBitmap(Uri uri)
+                throws FileNotFoundException {
+            if (uri == null || mContext == null) {
+                throw new IllegalArgumentException("bad argument to getScaledBitmap");
+            }
+            // Get width and height of stored bitmap
+            BitmapFactory.Options opt = new BitmapFactory.Options();
+            opt.inJustDecodeBounds = true;
+            loadBitmap(uri, opt);
+
+            int w = opt.outWidth;
+            int h = opt.outHeight;
+
+            // If bitmap cannot be decoded, return null
+            if (w <= 0 || h <= 0) {
+                return null;
+            }
+
+            // Find best downsampling size
+            int imageSide = Math.max(w, h);
+
+            int sampleSize = 1;
+            while (imageSide > MAX_PRINT_SIZE) {
+                imageSide >>>= 1;
+                sampleSize <<= 1;
+            }
+
+            // Make sure sample size is reasonable
+            if (sampleSize <= 0 || 0 >= (Math.min(w, h) / sampleSize)) {
+                return null;
+            }
+            BitmapFactory.Options decodeOptions;
+            synchronized (mLock) { // prevent race with set null below
+                mDecodeOptions = new BitmapFactory.Options();
+                mDecodeOptions.inMutable = true;
+                mDecodeOptions.inSampleSize = sampleSize;
+                decodeOptions = mDecodeOptions;
+            }
+            try {
+                return loadBitmap(uri, decodeOptions);
+            } finally {
+                synchronized (mLock) {
+                    mDecodeOptions = null;
+                }
+            }
+        }
+
+        /**
+         * Returns the bitmap from the given uri loaded using the given options.
+         * Returns null on failure.
+         */
+        private Bitmap loadBitmap(Uri uri, BitmapFactory.Options o) throws FileNotFoundException {
+            if (uri == null || mContext == null) {
+                throw new IllegalArgumentException("bad argument to loadBitmap");
+            }
+            InputStream is = null;
+            try {
+                is = mContext.getContentResolver().openInputStream(uri);
+                return BitmapFactory.decodeStream(is, null, o);
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException t) {
+                        Log.w(LOG_TAG, "close fail ", t);
+                    }
+                }
+            }
+        }
+
+        private Bitmap convertBitmapForColorMode(Bitmap original, @ColorMode int colorMode) {
+            if (colorMode != COLOR_MODE_MONOCHROME) {
+                return original;
+            }
+            // Create a grayscale bitmap
+            Bitmap grayscale = Bitmap.createBitmap(original.getWidth(), original.getHeight(),
+                    Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(grayscale);
+            Paint p = new Paint();
+            ColorMatrix cm = new ColorMatrix();
+            cm.setSaturation(0);
+            ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
+            p.setColorFilter(f);
+            c.drawBitmap(original, 0, 0, p);
+            c.setBitmap(null);
+
+            return grayscale;
         }
     }
 
     /**
-     * Implementation used on KitKat
+     * Api20 specific PrintManager API implementation.
      */
-    private static final class PrintHelperKitkatImpl extends PrintHelperImpl<PrintHelperKitkat> {
-        PrintHelperKitkatImpl(Context context) {
-            super(new PrintHelperKitkat(context));
+    @RequiresApi(20)
+    private static class PrintHelperApi20 extends PrintHelperApi19 {
+        PrintHelperApi20(Context context) {
+            super(context);
+
+
+            // There is a bug in the PrintActivity that causes it to ignore the orientation
+            mPrintActivityRespectsOrientation = false;
         }
     }
 
     /**
-     * Implementation used on Api20 to Api22
+     * Api23 specific PrintManager API implementation.
      */
-    private static final class PrintHelperApi20Impl extends PrintHelperImpl<PrintHelperApi20> {
-        PrintHelperApi20Impl(Context context) {
-            super(new PrintHelperApi20(context));
+    @RequiresApi(23)
+    private static class PrintHelperApi23 extends PrintHelperApi20 {
+        @Override
+        protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
+            PrintAttributes.Builder b = super.copyAttributes(other);
+
+            if (other.getDuplexMode() != 0) {
+                b.setDuplexMode(other.getDuplexMode());
+            }
+
+            return b;
+        }
+
+        PrintHelperApi23(Context context) {
+            super(context);
+
+            mIsMinMarginsHandlingCorrect = false;
         }
     }
 
     /**
-     * Implementation used on Api23
+     * Api24 specific PrintManager API implementation.
      */
-    private static final class PrintHelperApi23Impl extends PrintHelperImpl<PrintHelperApi23> {
-        PrintHelperApi23Impl(Context context) {
-            super(new PrintHelperApi23(context));
-        }
-    }
+    @RequiresApi(24)
+    private static class PrintHelperApi24 extends PrintHelperApi23 {
+        PrintHelperApi24(Context context) {
+            super(context);
 
-
-    /**
-     * Implementation used on Api24 and above
-     */
-    private static final class PrintHelperApi24Impl extends PrintHelperImpl<PrintHelperApi24> {
-        PrintHelperApi24Impl(Context context) {
-            super(new PrintHelperApi24(context));
+            mIsMinMarginsHandlingCorrect = true;
+            mPrintActivityRespectsOrientation = true;
         }
     }
 
     /**
-     * Returns the PrintHelper that can be used to print images.
+     * Constructs the PrintHelper that can be used to print images.
      *
      * @param context A context for accessing system resources.
-     * @return the <code>PrintHelper</code> to support printing images.
      */
     public PrintHelper(Context context) {
-        if (systemSupportsPrint()) {
-            if (Build.VERSION.SDK_INT >= 24) {
-                mImpl = new PrintHelperApi24Impl(context);
-            } else if (Build.VERSION.SDK_INT >= 23) {
-                mImpl = new PrintHelperApi23Impl(context);
-            } else if (Build.VERSION.SDK_INT >= 20) {
-                mImpl = new PrintHelperApi20Impl(context);
-            } else {
-                mImpl = new PrintHelperKitkatImpl(context);
-            }
+        if (Build.VERSION.SDK_INT >= 24) {
+            mImpl = new PrintHelperApi24(context);
+        } else if (Build.VERSION.SDK_INT >= 23) {
+            mImpl = new PrintHelperApi23(context);
+        } else if (Build.VERSION.SDK_INT >= 20) {
+            mImpl = new PrintHelperApi20(context);
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            mImpl = new PrintHelperApi19(context);
         } else {
-            mImpl = new PrintHelperStubImpl();
+            // System does not support printing.
+            mImpl = new PrintHelperStub();
         }
     }
 
@@ -291,7 +909,7 @@
      * @param scaleMode {@link #SCALE_MODE_FIT} or
      *                  {@link #SCALE_MODE_FILL}
      */
-    public void setScaleMode(int scaleMode) {
+    public void setScaleMode(@ScaleMode int scaleMode) {
         mImpl.setScaleMode(scaleMode);
     }
 
@@ -301,6 +919,7 @@
      * @return The scale Mode: {@link #SCALE_MODE_FIT} or
      * {@link #SCALE_MODE_FILL}
      */
+    @ScaleMode
     public int getScaleMode() {
         return mImpl.getScaleMode();
     }
@@ -313,7 +932,7 @@
      * @param colorMode The color mode which is one of
      * {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}.
      */
-    public void setColorMode(int colorMode) {
+    public void setColorMode(@ColorMode int colorMode) {
         mImpl.setColorMode(colorMode);
     }
 
@@ -323,6 +942,7 @@
      * @return The color mode which is one of {@link #COLOR_MODE_COLOR}
      * and {@link #COLOR_MODE_MONOCHROME}.
      */
+    @ColorMode
     public int getColorMode() {
         return mImpl.getColorMode();
     }
diff --git a/core-utils/java/android/support/v4/provider/DocumentFile.java b/core-utils/java/android/support/v4/provider/DocumentFile.java
index c573db0..2d1550d 100644
--- a/core-utils/java/android/support/v4/provider/DocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/DocumentFile.java
@@ -107,8 +107,7 @@
      *            {@link Intent#ACTION_CREATE_DOCUMENT} request.
      */
     public static DocumentFile fromSingleUri(Context context, Uri singleUri) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             return new SingleDocumentFile(null, context, singleUri);
         } else {
             return null;
@@ -125,8 +124,7 @@
      *            {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
      */
     public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             return new TreeDocumentFile(null, context,
                     DocumentsContractApi21.prepareTreeUri(treeUri));
         } else {
@@ -139,8 +137,7 @@
      * {@link android.provider.DocumentsProvider}.
      */
     public static boolean isDocumentUri(Context context, Uri uri) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             return DocumentsContractApi19.isDocumentUri(context, uri);
         } else {
             return false;
diff --git a/core-utils/java/android/support/v4/provider/SingleDocumentFile.java b/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
index 3a4ccf2..77c4e49 100644
--- a/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
@@ -18,8 +18,9 @@
 
 import android.content.Context;
 import android.net.Uri;
-import android.support.v4.provider.DocumentsContractApi19;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(19)
 class SingleDocumentFile extends DocumentFile {
     private Context mContext;
     private Uri mUri;
diff --git a/core-utils/java/android/support/v4/provider/TreeDocumentFile.java b/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
index 02975bd..cb8979d 100644
--- a/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
@@ -18,7 +18,9 @@
 
 import android.content.Context;
 import android.net.Uri;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(21)
 class TreeDocumentFile extends DocumentFile {
     private Context mContext;
     private Uri mUri;
diff --git a/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java b/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
deleted file mode 100644
index 86a28b3..0000000
--- a/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(16)
-@TargetApi(16)
-class NavUtilsJB {
-    public static Intent getParentActivityIntent(Activity activity) {
-        return activity.getParentActivityIntent();
-    }
-
-    public static boolean shouldUpRecreateTask(Activity activity, Intent targetIntent) {
-        return activity.shouldUpRecreateTask(targetIntent);
-    }
-
-    public static void navigateUpTo(Activity activity, Intent upIntent) {
-        activity.navigateUpTo(upIntent);
-    }
-
-    public static String getParentActivityName(ActivityInfo info) {
-        return info.parentActivityName;
-    }
-}
diff --git a/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java b/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
deleted file mode 100644
index 96c3dda..0000000
--- a/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2012 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.app;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(16)
-@TargetApi(16)
-class TaskStackBuilderJellybean {
-
-    public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
-            Intent[] intents, int flags, Bundle options) {
-        return PendingIntent.getActivities(context, requestCode, intents, flags, options);
-    }
-
-}
diff --git a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java b/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
deleted file mode 100644
index 08a41b2..0000000
--- a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- * Copyright (C) 2013 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.print;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.pdf.PdfDocument.Page;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.print.PrintAttributes.MediaSize;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentInfo;
-import android.print.PrintManager;
-import android.print.pdf.PrintedPdfDocument;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Kitkat specific PrintManager API implementation.
- */
-@RequiresApi(19)
-@TargetApi(19)
-class PrintHelperKitkat {
-    private static final String LOG_TAG = "PrintHelperKitkat";
-    // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
-    private final static int MAX_PRINT_SIZE = 3500;
-    final Context mContext;
-    BitmapFactory.Options mDecodeOptions = null;
-    private final Object mLock = new Object();
-    /**
-     * image will be scaled but leave white space
-     */
-    public static final int SCALE_MODE_FIT = 1;
-    /**
-     * image will fill the paper and be cropped (default)
-     */
-    public static final int SCALE_MODE_FILL = 2;
-
-    /**
-     * select landscape (default)
-     */
-    public static final int ORIENTATION_LANDSCAPE = 1;
-
-    /**
-     * select portrait
-     */
-    public static final int ORIENTATION_PORTRAIT = 2;
-
-    /**
-     * this is a black and white image
-     */
-    public static final int COLOR_MODE_MONOCHROME = 1;
-    /**
-     * this is a color image (default)
-     */
-    public static final int COLOR_MODE_COLOR = 2;
-
-    public interface OnPrintFinishCallback {
-        public void onFinish();
-    }
-
-    /**
-     * Whether the PrintActivity respects the suggested orientation
-     */
-    protected boolean mPrintActivityRespectsOrientation;
-
-    /**
-     * Whether the print subsystem handles min margins correctly. If not the print helper needs to
-     * fake this.
-     */
-    protected boolean mIsMinMarginsHandlingCorrect;
-
-    int mScaleMode = SCALE_MODE_FILL;
-
-    int mColorMode = COLOR_MODE_COLOR;
-
-    int mOrientation;
-
-    PrintHelperKitkat(Context context) {
-        mPrintActivityRespectsOrientation = true;
-        mIsMinMarginsHandlingCorrect = true;
-
-        mContext = context;
-    }
-
-    /**
-     * Selects whether the image will fill the paper and be cropped
-     * <p/>
-     * {@link #SCALE_MODE_FIT}
-     * or whether the image will be scaled but leave white space
-     * {@link #SCALE_MODE_FILL}.
-     *
-     * @param scaleMode {@link #SCALE_MODE_FIT} or
-     *                  {@link #SCALE_MODE_FILL}
-     */
-    public void setScaleMode(int scaleMode) {
-        mScaleMode = scaleMode;
-    }
-
-    /**
-     * Returns the scale mode with which the image will fill the paper.
-     *
-     * @return The scale Mode: {@link #SCALE_MODE_FIT} or
-     * {@link #SCALE_MODE_FILL}
-     */
-    public int getScaleMode() {
-        return mScaleMode;
-    }
-
-    /**
-     * Sets whether the image will be printed in color (default)
-     * {@link #COLOR_MODE_COLOR} or in back and white
-     * {@link #COLOR_MODE_MONOCHROME}.
-     *
-     * @param colorMode The color mode which is one of
-     *                  {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}.
-     */
-    public void setColorMode(int colorMode) {
-        mColorMode = colorMode;
-    }
-
-    /**
-     * Sets whether to select landscape (default), {@link #ORIENTATION_LANDSCAPE}
-     * or portrait {@link #ORIENTATION_PORTRAIT}
-     * @param orientation The page orientation which is one of
-     *                    {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}.
-     */
-    public void setOrientation(int orientation) {
-        mOrientation = orientation;
-    }
-
-    /**
-     * Gets the page orientation with which the image will be printed.
-     *
-     * @return The preferred orientation which is one of
-     * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}
-     */
-    public int getOrientation() {
-        /// Unset defaults to landscape but might turn image
-        if (mOrientation == 0) {
-            return ORIENTATION_LANDSCAPE;
-        }
-        return mOrientation;
-    }
-
-    /**
-     * Gets the color mode with which the image will be printed.
-     *
-     * @return The color mode which is one of {@link #COLOR_MODE_COLOR}
-     * and {@link #COLOR_MODE_MONOCHROME}.
-     */
-    public int getColorMode() {
-        return mColorMode;
-    }
-
-    /**
-     * Check if the supplied bitmap should best be printed on a portrait orientation paper.
-     *
-     * @param bitmap The bitmap to be printed.
-     * @return true iff the picture should best be printed on a portrait orientation paper.
-     */
-    private static boolean isPortrait(Bitmap bitmap) {
-        if (bitmap.getWidth() <= bitmap.getHeight()) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Create a build with a copy from the other print attributes.
-     *
-     * @param other The other print attributes
-     *
-     * @return A builder that will build print attributes that match the other attributes
-     */
-    protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
-        PrintAttributes.Builder b = (new PrintAttributes.Builder())
-                .setMediaSize(other.getMediaSize())
-                .setResolution(other.getResolution())
-                .setMinMargins(other.getMinMargins());
-
-        if (other.getColorMode() != 0) {
-            b.setColorMode(other.getColorMode());
-        }
-
-        return b;
-    }
-
-    /**
-     * Prints a bitmap.
-     *
-     * @param jobName The print job name.
-     * @param bitmap  The bitmap to print.
-     * @param callback Optional callback to observe when printing is finished.
-     */
-    public void printBitmap(final String jobName, final Bitmap bitmap,
-            final OnPrintFinishCallback callback) {
-        if (bitmap == null) {
-            return;
-        }
-        final int fittingMode = mScaleMode; // grab the fitting mode at time of call
-        PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
-        PrintAttributes.MediaSize mediaSize;
-        if (isPortrait(bitmap)) {
-            mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
-        } else {
-            mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
-        }
-        PrintAttributes attr = new PrintAttributes.Builder()
-                .setMediaSize(mediaSize)
-                .setColorMode(mColorMode)
-                .build();
-
-        printManager.print(jobName,
-                new PrintDocumentAdapter() {
-                    private PrintAttributes mAttributes;
-
-                    @Override
-                    public void onLayout(PrintAttributes oldPrintAttributes,
-                                         PrintAttributes newPrintAttributes,
-                                         CancellationSignal cancellationSignal,
-                                         LayoutResultCallback layoutResultCallback,
-                                         Bundle bundle) {
-
-                        mAttributes = newPrintAttributes;
-
-                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                .setPageCount(1)
-                                .build();
-                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-                        layoutResultCallback.onLayoutFinished(info, changed);
-                    }
-
-                    @Override
-                    public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
-                                        CancellationSignal cancellationSignal,
-                                        WriteResultCallback writeResultCallback) {
-                        writeBitmap(mAttributes, fittingMode, bitmap, fileDescriptor,
-                                cancellationSignal, writeResultCallback);
-                    }
-
-                    @Override
-                    public void onFinish() {
-                        if (callback != null) {
-                            callback.onFinish();
-                        }
-                    }
-                }, attr);
-    }
-
-    /**
-     * Calculates the transform the print an Image to fill the page
-     *
-     * @param imageWidth  with of bitmap
-     * @param imageHeight height of bitmap
-     * @param content     The output page dimensions
-     * @param fittingMode The mode of fitting {@link #SCALE_MODE_FILL} vs {@link #SCALE_MODE_FIT}
-     * @return Matrix to be used in canvas.drawBitmap(bitmap, matrix, null) call
-     */
-    private Matrix getMatrix(int imageWidth, int imageHeight, RectF content, int fittingMode) {
-        Matrix matrix = new Matrix();
-
-        // Compute and apply scale to fill the page.
-        float scale = content.width() / imageWidth;
-        if (fittingMode == SCALE_MODE_FILL) {
-            scale = Math.max(scale, content.height() / imageHeight);
-        } else {
-            scale = Math.min(scale, content.height() / imageHeight);
-        }
-        matrix.postScale(scale, scale);
-
-        // Center the content.
-        final float translateX = (content.width()
-                - imageWidth * scale) / 2;
-        final float translateY = (content.height()
-                - imageHeight * scale) / 2;
-        matrix.postTranslate(translateX, translateY);
-        return matrix;
-    }
-
-    /**
-     * Write a bitmap for a PDF document.
-     *
-     * @param attributes          The print attributes
-     * @param fittingMode         How to fit the bitmap
-     * @param bitmap              The bitmap to write
-     * @param fileDescriptor      The file to write to
-     * @param cancellationSignal  Signal cancelling operation
-     * @param writeResultCallback Callback to call once written
-     */
-    private void writeBitmap(final PrintAttributes attributes, final int fittingMode,
-            final Bitmap bitmap, final ParcelFileDescriptor fileDescriptor,
-            final CancellationSignal cancellationSignal,
-            final PrintDocumentAdapter.WriteResultCallback writeResultCallback) {
-        final PrintAttributes pdfAttributes;
-        if (mIsMinMarginsHandlingCorrect) {
-            pdfAttributes = attributes;
-        } else {
-            // If the handling of any margin != 0 is broken, strip the margins and add them to the
-            // bitmap later
-            pdfAttributes = copyAttributes(attributes)
-                    .setMinMargins(new PrintAttributes.Margins(0,0,0,0)).build();
-        }
-
-        (new AsyncTask<Void, Void, Throwable>() {
-            @Override
-            protected Throwable doInBackground(Void... params) {
-                try {
-                    if (cancellationSignal.isCanceled()) {
-                        return null;
-                    }
-
-                    PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
-                            pdfAttributes);
-
-                    Bitmap maybeGrayscale = convertBitmapForColorMode(bitmap,
-                            pdfAttributes.getColorMode());
-
-                    if (cancellationSignal.isCanceled()) {
-                        return null;
-                    }
-
-                    try {
-                        Page page = pdfDocument.startPage(1);
-
-                        RectF contentRect;
-                        if (mIsMinMarginsHandlingCorrect) {
-                            contentRect = new RectF(page.getInfo().getContentRect());
-                        } else {
-                            // Create dummy doc that has the margins to compute correctly sized
-                            // content rectangle
-                            PrintedPdfDocument dummyDocument = new PrintedPdfDocument(mContext,
-                                    attributes);
-                            Page dummyPage = dummyDocument.startPage(1);
-                            contentRect = new RectF(dummyPage.getInfo().getContentRect());
-                            dummyDocument.finishPage(dummyPage);
-                            dummyDocument.close();
-                        }
-
-                        // Resize bitmap
-                        Matrix matrix = getMatrix(
-                                maybeGrayscale.getWidth(), maybeGrayscale.getHeight(),
-                                contentRect, fittingMode);
-
-                        if (mIsMinMarginsHandlingCorrect) {
-                            // The pdfDocument takes care of the positioning and margins
-                        } else {
-                            // Move it to the correct position.
-                            matrix.postTranslate(contentRect.left, contentRect.top);
-
-                            // Cut off margins
-                            page.getCanvas().clipRect(contentRect);
-                        }
-
-                        // Draw the bitmap.
-                        page.getCanvas().drawBitmap(maybeGrayscale, matrix, null);
-
-                        // Finish the page.
-                        pdfDocument.finishPage(page);
-
-                        if (cancellationSignal.isCanceled()) {
-                            return null;
-                        }
-
-                        // Write the document.
-                        pdfDocument
-                                .writeTo(new FileOutputStream(fileDescriptor.getFileDescriptor()));
-                        return null;
-                    } finally {
-                        pdfDocument.close();
-
-                        if (fileDescriptor != null) {
-                            try {
-                                fileDescriptor.close();
-                            } catch (IOException ioe) {
-                                // ignore
-                            }
-                        }
-                        // If we created a new instance for grayscaling, then recycle it here.
-                        if (maybeGrayscale != bitmap) {
-                            maybeGrayscale.recycle();
-                        }
-                    }
-                } catch (Throwable t) {
-                    return t;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(Throwable throwable) {
-                if (cancellationSignal.isCanceled()) {
-                    // Cancelled.
-                    writeResultCallback.onWriteCancelled();
-                } else if (throwable == null) {
-                    // Done.
-                    writeResultCallback.onWriteFinished(new PageRange[] { PageRange.ALL_PAGES });
-                } else {
-                    // Failed.
-                    Log.e(LOG_TAG, "Error writing printed content", throwable);
-                    writeResultCallback.onWriteFailed(null);
-                }
-            }
-        }).execute();
-    }
-
-    /**
-     * Prints an image located at the Uri. Image types supported are those of
-     * <code>BitmapFactory.decodeStream</code> (JPEG, GIF, PNG, BMP, WEBP)
-     *
-     * @param jobName   The print job name.
-     * @param imageFile The <code>Uri</code> pointing to an image to print.
-     * @param callback Optional callback to observe when printing is finished.
-     * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
-     */
-    public void printBitmap(final String jobName, final Uri imageFile,
-            final OnPrintFinishCallback callback) throws FileNotFoundException {
-        final int fittingMode = mScaleMode;
-
-        PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
-            private PrintAttributes mAttributes;
-            AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
-            Bitmap mBitmap = null;
-
-            @Override
-            public void onLayout(final PrintAttributes oldPrintAttributes,
-                                 final PrintAttributes newPrintAttributes,
-                                 final CancellationSignal cancellationSignal,
-                                 final LayoutResultCallback layoutResultCallback,
-                                 Bundle bundle) {
-
-                synchronized (this) {
-                    mAttributes = newPrintAttributes;
-                }
-
-                if (cancellationSignal.isCanceled()) {
-                    layoutResultCallback.onLayoutCancelled();
-                    return;
-                }
-                // we finished the load
-                if (mBitmap != null) {
-                    PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                            .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                            .setPageCount(1)
-                            .build();
-                    boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-                    layoutResultCallback.onLayoutFinished(info, changed);
-                    return;
-                }
-
-                mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
-                    @Override
-                    protected void onPreExecute() {
-                        // First register for cancellation requests.
-                        cancellationSignal.setOnCancelListener(
-                                new CancellationSignal.OnCancelListener() {
-                                    @Override
-                                    public void onCancel() { // on different thread
-                                        cancelLoad();
-                                        cancel(false);
-                                    }
-                                });
-                    }
-
-                    @Override
-                    protected Bitmap doInBackground(Uri... uris) {
-                        try {
-                            return loadConstrainedBitmap(imageFile, MAX_PRINT_SIZE);
-                        } catch (FileNotFoundException e) {
-                          /* ignore */
-                        }
-                        return null;
-                    }
-
-                    @Override
-                    protected void onPostExecute(Bitmap bitmap) {
-                        super.onPostExecute(bitmap);
-
-                        // If orientation was not set by the caller, try to fit the bitmap on
-                        // the current paper by potentially rotating the bitmap by 90 degrees.
-                        if (bitmap != null
-                                && (!mPrintActivityRespectsOrientation || mOrientation == 0)) {
-                            MediaSize mediaSize;
-
-                            synchronized (this) {
-                                mediaSize = mAttributes.getMediaSize();
-                            }
-
-                            if (mediaSize != null) {
-                                if (mediaSize.isPortrait() != isPortrait(bitmap)) {
-                                    Matrix rotation = new Matrix();
-
-                                    rotation.postRotate(90);
-                                    bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
-                                            bitmap.getHeight(), rotation, true);
-                                }
-                            }
-                        }
-
-                        mBitmap = bitmap;
-                        if (bitmap != null) {
-                            PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                    .setPageCount(1)
-                                    .build();
-
-                            boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-
-                            layoutResultCallback.onLayoutFinished(info, changed);
-
-                        } else {
-                            layoutResultCallback.onLayoutFailed(null);
-                        }
-                        mLoadBitmap = null;
-                    }
-
-                    @Override
-                    protected void onCancelled(Bitmap result) {
-                        // Task was cancelled, report that.
-                        layoutResultCallback.onLayoutCancelled();
-                        mLoadBitmap = null;
-                    }
-                }.execute();
-            }
-
-            private void cancelLoad() {
-                synchronized (mLock) { // prevent race with set null below
-                    if (mDecodeOptions != null) {
-                        mDecodeOptions.requestCancelDecode();
-                        mDecodeOptions = null;
-                    }
-                }
-            }
-
-            @Override
-            public void onFinish() {
-                super.onFinish();
-                cancelLoad();
-                if (mLoadBitmap != null) {
-                    mLoadBitmap.cancel(true);
-                }
-                if (callback != null) {
-                    callback.onFinish();
-                }
-                if (mBitmap != null) {
-                    mBitmap.recycle();
-                    mBitmap = null;
-                }
-            }
-
-            @Override
-            public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
-                                CancellationSignal cancellationSignal,
-                                WriteResultCallback writeResultCallback) {
-                writeBitmap(mAttributes, fittingMode, mBitmap, fileDescriptor, cancellationSignal,
-                        writeResultCallback);
-            }
-        };
-
-        PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
-        PrintAttributes.Builder builder = new PrintAttributes.Builder();
-        builder.setColorMode(mColorMode);
-
-        if (mOrientation == ORIENTATION_LANDSCAPE || mOrientation == 0) {
-            builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE);
-        } else if (mOrientation == ORIENTATION_PORTRAIT) {
-            builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_PORTRAIT);
-        }
-        PrintAttributes attr = builder.build();
-
-        printManager.print(jobName, printDocumentAdapter, attr);
-    }
-
-    /**
-     * Loads a bitmap while limiting its size
-     *
-     * @param uri           location of a valid image
-     * @param maxSideLength the maximum length of a size
-     * @return the Bitmap
-     * @throws FileNotFoundException if the Uri does not point to an image
-     */
-    private Bitmap loadConstrainedBitmap(Uri uri, int maxSideLength) throws FileNotFoundException {
-        if (maxSideLength <= 0 || uri == null || mContext == null) {
-            throw new IllegalArgumentException("bad argument to getScaledBitmap");
-        }
-        // Get width and height of stored bitmap
-        BitmapFactory.Options opt = new BitmapFactory.Options();
-        opt.inJustDecodeBounds = true;
-        loadBitmap(uri, opt);
-
-        int w = opt.outWidth;
-        int h = opt.outHeight;
-
-        // If bitmap cannot be decoded, return null
-        if (w <= 0 || h <= 0) {
-            return null;
-        }
-
-        // Find best downsampling size
-        int imageSide = Math.max(w, h);
-
-        int sampleSize = 1;
-        while (imageSide > maxSideLength) {
-            imageSide >>>= 1;
-            sampleSize <<= 1;
-        }
-
-        // Make sure sample size is reasonable
-        if (sampleSize <= 0 || 0 >= (int) (Math.min(w, h) / sampleSize)) {
-            return null;
-        }
-        BitmapFactory.Options decodeOptions = null;
-        synchronized (mLock) { // prevent race with set null below
-            mDecodeOptions = new BitmapFactory.Options();
-            mDecodeOptions.inMutable = true;
-            mDecodeOptions.inSampleSize = sampleSize;
-            decodeOptions = mDecodeOptions;
-        }
-        try {
-            return loadBitmap(uri, decodeOptions);
-        } finally {
-            synchronized (mLock) {
-                mDecodeOptions = null;
-            }
-        }
-    }
-
-    /**
-     * Returns the bitmap from the given uri loaded using the given options.
-     * Returns null on failure.
-     */
-    private Bitmap loadBitmap(Uri uri, BitmapFactory.Options o) throws FileNotFoundException {
-        if (uri == null || mContext == null) {
-            throw new IllegalArgumentException("bad argument to loadBitmap");
-        }
-        InputStream is = null;
-        try {
-            is = mContext.getContentResolver().openInputStream(uri);
-            return BitmapFactory.decodeStream(is, null, o);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException t) {
-                    Log.w(LOG_TAG, "close fail ", t);
-                }
-            }
-        }
-    }
-
-    private Bitmap convertBitmapForColorMode(Bitmap original, int colorMode) {
-        if (colorMode != COLOR_MODE_MONOCHROME) {
-            return original;
-        }
-        // Create a grayscale bitmap
-        Bitmap grayscale = Bitmap.createBitmap(original.getWidth(), original.getHeight(),
-                Config.ARGB_8888);
-        Canvas c = new Canvas(grayscale);
-        Paint p = new Paint();
-        ColorMatrix cm = new ColorMatrix();
-        cm.setSaturation(0);
-        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
-        p.setColorFilter(f);
-        c.drawBitmap(original, 0, 0, p);
-        c.setBitmap(null);
-
-        return grayscale;
-    }
-}
diff --git a/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java b/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
index f164f17..a429d8d 100644
--- a/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
+++ b/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.provider;
 
-import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -29,7 +28,6 @@
 import android.util.Log;
 
 @RequiresApi(19)
-@TargetApi(19)
 class DocumentsContractApi19 {
     private static final String TAG = "DocumentFile";
 
@@ -139,7 +137,11 @@
     }
 
     public static boolean delete(Context context, Uri self) {
-        return DocumentsContract.deleteDocument(context.getContentResolver(), self);
+        try {
+            return DocumentsContract.deleteDocument(context.getContentResolver(), self);
+        } catch (Exception e) {
+            return false;
+        }
     }
 
     public static boolean exists(Context context, Uri self) {
diff --git a/core-utils/lint-baseline.xml b/core-utils/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/core-utils/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/core-utils/tests/AndroidManifest.xml b/core-utils/tests/AndroidManifest.xml
index 8a30a93..e3d060c 100644
--- a/core-utils/tests/AndroidManifest.xml
+++ b/core-utils/tests/AndroidManifest.xml
@@ -15,31 +15,27 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.coreutils.test">
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.VIBRATE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.READ_CONTACTS"/>
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
 
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
     <application>
-        <uses-library android:name="android.test.runner" />
         <activity android:name="android.support.v4.widget.test.TextViewTestActivity"/>
+        <activity android:name="android.support.v4.app.FrameMetricsActivity"/>
+        <activity android:name="android.support.v4.app.FrameMetricsSubActivity"/>
         <activity android:name="android.support.v4.widget.TestActivity"/>
         <activity android:name="android.support.v4.provider.GrantActivity"
                   android:label="_GrantActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
         <provider
@@ -49,11 +45,8 @@
             android:grantUriPermissions="true">
             <meta-data
                 android:name="android.support.FILE_PROVIDER_PATHS"
-                android:resource="@xml/paths" />
+                android:resource="@xml/paths"/>
         </provider>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.support.coreutils.test"
-                     />
 </manifest>
diff --git a/core-utils/tests/java/android/support/v4/app/FrameMetricsActivity.java b/core-utils/tests/java/android/support/v4/app/FrameMetricsActivity.java
new file mode 100644
index 0000000..d78c50b
--- /dev/null
+++ b/core-utils/tests/java/android/support/v4/app/FrameMetricsActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+public class FrameMetricsActivity extends Activity {
+
+    LinearLayout mLayout;
+    View mView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLayout = new LinearLayout(this);
+        mLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        mLayout.setBackgroundColor(Color.BLUE);
+        setContentView(mLayout);
+
+        mView = new View(this);
+        mView.setLayoutParams(new LinearLayout.LayoutParams(500, 500));
+        mView.setBackgroundColor(Color.GREEN);
+        mLayout.addView(mView);
+    }
+
+    public void invalidate() {
+        mLayout.invalidate();
+        mLayout.setBackgroundColor(Color.rgb((int) (255 * Math.random()),
+                (int) (255 * Math.random()), (int) (255 * Math.random())));
+    }
+}
diff --git a/core-utils/tests/java/android/support/v4/app/FrameMetricsAggregatorTest.java b/core-utils/tests/java/android/support/v4/app/FrameMetricsAggregatorTest.java
new file mode 100644
index 0000000..3a1a36e
--- /dev/null
+++ b/core-utils/tests/java/android/support/v4/app/FrameMetricsAggregatorTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseIntArray;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FrameMetricsAggregatorTest {
+
+    @Rule
+    public final ActivityTestRule<FrameMetricsActivity> mActivityTestRule;
+    private FrameMetricsSubActivity mSecondActivity = null;
+
+
+    public FrameMetricsAggregatorTest() {
+        mActivityTestRule = new ActivityTestRule<>(FrameMetricsActivity.class);
+    }
+
+    @Test
+    @MediumTest
+    public void testFrameMetrics() throws Throwable {
+        FrameMetricsAggregator metrics = new FrameMetricsAggregator();
+
+        // Check that getMetrics() returns empty results
+        SparseIntArray[] durations = metrics.getMetrics();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNull(sparseArray);
+            }
+        }
+
+        final FrameMetricsActivity activity = mActivityTestRule.getActivity();
+        metrics.add(activity);
+
+        // Check that getMetrics() returns results only for TOTAL_DURATION
+        durations = metrics.getMetrics();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (int i = 0; i < durations.length; ++i) {
+                if (i == FrameMetricsAggregator.TOTAL_INDEX) {
+                    assertNotNull(durations[i]);
+                } else {
+                    assertNull(durations[i]);
+                }
+            }
+        }
+
+        // Start tracking all durations
+        metrics = new FrameMetricsAggregator(FrameMetricsAggregator.EVERY_DURATION);
+        metrics.add(activity);
+
+        // Check that getMetrics() returns results for all durations
+        durations = metrics.getMetrics();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNotNull(sparseArray);
+            }
+        }
+
+        long startTime = System.currentTimeMillis();
+        while (System.currentTimeMillis() - startTime < 1000) {
+            if (mSecondActivity == null && System.currentTimeMillis() - startTime > 400) {
+                Intent intent = new Intent(activity, FrameMetricsSubActivity.class)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mSecondActivity = (FrameMetricsSubActivity)
+                        InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+                metrics.add(mSecondActivity);
+            }
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    activity.invalidate();
+                }
+            });
+            try {
+                Thread.sleep(10);
+            } catch (Exception e) {
+            }
+        }
+
+        durations = metrics.getMetrics();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNotNull(sparseArray);
+                assertTrue(sparseArray.size() > 0);
+            }
+        }
+        durations = metrics.remove(mSecondActivity);
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNotNull(sparseArray);
+                assertTrue(sparseArray.size() > 0);
+            }
+        }
+        durations = metrics.stop();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNotNull(sparseArray);
+                assertTrue(sparseArray.size() > 0);
+            }
+        }
+        durations = metrics.reset();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNotNull(sparseArray);
+                assertTrue(sparseArray.size() > 0);
+            }
+        }
+        durations = metrics.getMetrics();
+        if (Build.VERSION.SDK_INT < 24) {
+            assertNull(durations);
+        } else {
+            assertNotNull(durations);
+            for (SparseIntArray sparseArray : durations) {
+                assertNull(sparseArray);
+            }
+        }
+
+    }
+
+}
diff --git a/core-utils/tests/java/android/support/v4/app/FrameMetricsSubActivity.java b/core-utils/tests/java/android/support/v4/app/FrameMetricsSubActivity.java
new file mode 100644
index 0000000..b2136fa
--- /dev/null
+++ b/core-utils/tests/java/android/support/v4/app/FrameMetricsSubActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+public class FrameMetricsSubActivity extends Activity {
+
+    LinearLayout mLayout;
+    View mView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLayout = new LinearLayout(this);
+        mLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        mLayout.setBackgroundColor(Color.CYAN);
+        setContentView(mLayout);
+
+        mView = new View(this);
+        mView.setLayoutParams(new LinearLayout.LayoutParams(500, 500));
+        mView.setBackgroundColor(Color.RED);
+        mLayout.addView(mView);
+    }
+}
diff --git a/core-utils/tests/java/android/support/v4/content/MimeTypeFilterTest.java b/core-utils/tests/java/android/support/v4/content/MimeTypeFilterTest.java
new file mode 100644
index 0000000..21748ff
--- /dev/null
+++ b/core-utils/tests/java/android/support/v4/content/MimeTypeFilterTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 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.content;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.MoreAsserts;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link MimeTypeFilter}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MimeTypeFilterTest {
+    @Test
+    public void matchesBasic() throws Exception {
+        assertTrue(MimeTypeFilter.matches("image/jpeg", "*/*"));
+        assertTrue(MimeTypeFilter.matches("image/jpeg", "image/*"));
+        assertTrue(MimeTypeFilter.matches("image/jpeg", "image/jpeg"));
+        assertTrue(MimeTypeFilter.matches("image/jpeg", "*/jpeg"));
+
+        // These matchers are case *sensitive*.
+        assertFalse(MimeTypeFilter.matches("ImAgE/JpEg", "iMaGe/*"));
+        assertFalse(MimeTypeFilter.matches("IMAGE/JPEG", "image/jpeg"));
+        assertFalse(MimeTypeFilter.matches("image/jpeg", "IMAGE/JPEG"));
+
+        assertFalse(MimeTypeFilter.matches("image/jpeg", "image/png"));
+        assertFalse(MimeTypeFilter.matches("image/jpeg", "video/jpeg"));
+
+        assertFalse(MimeTypeFilter.matches((String) null, "*/*"));
+        assertFalse(MimeTypeFilter.matches((String) null, "image/"));
+        assertFalse(MimeTypeFilter.matches((String) null, "image/jpeg"));
+
+        // Null and invalid MIME types.
+        assertFalse(MimeTypeFilter.matches((String) null, "*/*"));
+        assertFalse(MimeTypeFilter.matches("", "*/*"));
+        assertFalse(MimeTypeFilter.matches("image/", "*/*"));
+        assertFalse(MimeTypeFilter.matches("*/", "*/*"));
+    }
+
+    @Test
+    public void matchesManyFilters() throws Exception {
+        assertEquals("*/*", MimeTypeFilter.matches("image/jpeg", new String[] {"*/*"}));
+        assertEquals("image/*", MimeTypeFilter.matches("image/jpeg", new String[] {"image/*"}));
+        assertEquals("image/jpeg", MimeTypeFilter.matches(
+                "image/jpeg", new String[] {"image/jpeg"}));
+
+        assertEquals("*/*", MimeTypeFilter.matches(
+                "image/jpeg", new String[] {"not/matching", "*/*"}));
+        assertEquals("image/*", MimeTypeFilter.matches(
+                "image/jpeg", new String[] {"image/*", "image/jpeg"}));
+        assertEquals("image/jpeg", MimeTypeFilter.matches(
+                "image/jpeg", new String[] {"image/jpeg", "image/png"}));
+
+        assertNull(MimeTypeFilter.matches(
+                "ImAgE/JpEg", new String[] {"iMaGe/*", "image/*"}));
+        assertEquals("*/jpeg", MimeTypeFilter.matches(
+                "image/jpeg", new String[] {"*/png", "*/jpeg"}));
+
+        assertNull(MimeTypeFilter.matches("image/jpeg", new String[] {}));
+
+        assertNull(MimeTypeFilter.matches("image/jpeg", new String[] {"image/png", "video/jpeg"}));
+        assertNull(MimeTypeFilter.matches("image/jpeg", new String[] {"video/jpeg", "image/png"}));
+
+        assertNull(MimeTypeFilter.matches(null, new String[] {"*/*"}));
+        assertNull(MimeTypeFilter.matches(null, new String[] {"image/"}));
+        assertNull(MimeTypeFilter.matches(null, new String[] {"image/jpeg"}));
+
+        // Null and invalid MIME types.
+        assertNull(MimeTypeFilter.matches((String) null, new String[] { "*/*" }));
+        assertNull(MimeTypeFilter.matches("", new String[] { "*/*" }));
+        assertNull(MimeTypeFilter.matches("image/", new String[] { "*/*" }));
+        assertNull(MimeTypeFilter.matches("*/", new String[] { "*/*" }));
+    }
+
+    @Test
+    public void matchesManyMimeTypes() throws Exception {
+        MoreAsserts.assertEquals(new String[] {"image/jpeg", "image/png"},
+                MimeTypeFilter.matchesMany(new String[] {"image/jpeg", "image/png"}, "image/*"));
+        MoreAsserts.assertEquals(new String[] {"image/png"},
+                MimeTypeFilter.matchesMany(new String[] {"image/jpeg", "image/png"}, "image/png"));
+        MoreAsserts.assertEquals(new String[] {},
+                MimeTypeFilter.matchesMany(new String[] {"image/jpeg", "image/png"}, "*/JpEg"));
+
+        MoreAsserts.assertEquals(new String[] {},
+                MimeTypeFilter.matchesMany(new String[] {"*/", "image/"}, "*/*"));
+        MoreAsserts.assertEquals(new String[] {},
+                MimeTypeFilter.matchesMany(new String[] {}, "*/*"));
+    }
+
+    @Test
+    public void illegalFilters() throws Exception {
+        try {
+            MimeTypeFilter.matches("image/jpeg", "");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", "*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", "*/");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", "/*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", "*/*/*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches(new String[] { "image/jpeg" }, "");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches(new String[] { "image/jpeg" }, "*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches(new String[] { "image/jpeg" }, "*/");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches(new String[] { "image/jpeg" }, "/*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches(new String[] { "image/jpeg" }, "*/*/*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", new String[] { "" });
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", new String[] { "*" });
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", new String[] { "*/" });
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", new String[] { "/*" });
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matches("image/jpeg", new String[] { "*/*/*" });
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matchesMany(new String[] { "image/jpeg" }, "");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matchesMany(new String[] { "image/jpeg" }, "*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matchesMany(new String[] { "image/jpeg" }, "*/");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matchesMany(new String[] { "image/jpeg" }, "/*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+
+        try {
+            MimeTypeFilter.matchesMany(new String[] { "image/jpeg" }, "*/*/*");
+            Assert.fail("Illegal filter, should throw.");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+    }
+}
diff --git a/core-utils/tests/java/android/support/v4/math/MathUtilsTest.java b/core-utils/tests/java/android/support/v4/math/MathUtilsTest.java
new file mode 100644
index 0000000..526f3a6
--- /dev/null
+++ b/core-utils/tests/java/android/support/v4/math/MathUtilsTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.math;
+
+import static org.junit.Assert.assertEquals;
+
+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 MathUtilsTest {
+
+    @Test
+    public void testClamp() {
+        // Int
+        assertEquals(0, MathUtils.clamp(-4, 0, 7));
+        assertEquals(3, MathUtils.clamp(3, -2, 7));
+        assertEquals(0, MathUtils.clamp(0, 0, 7));
+        assertEquals(7, MathUtils.clamp(7, 0, 7));
+        assertEquals(7, MathUtils.clamp(8, -2, 7));
+
+        // Double
+        assertEquals(0.0, MathUtils.clamp(-0.4, 0.0, 7.0), 0.0);
+        assertEquals(3.0, MathUtils.clamp(3.0, 0.0, 7.0), 0.0);
+        assertEquals(0.1, MathUtils.clamp(0.1, 0.0, 7.0), 0.0);
+        assertEquals(7.0, MathUtils.clamp(7.0, 0.0, 7.0), 0.0);
+        assertEquals(-0.6, MathUtils.clamp(-0.7, -0.6, 7.0), 0.0);
+
+        // Float
+        assertEquals(0.0f, MathUtils.clamp(-0.4f, 0.0f, 7.0f), 0.0f);
+        assertEquals(3.0f, MathUtils.clamp(3.0f, 0.0f, 7.0f), 0.0f);
+        assertEquals(0.1f, MathUtils.clamp(0.1f, 0.0f, 7.0f), 0.0f);
+        assertEquals(7.0f, MathUtils.clamp(7.0f, 0.0f, 7.0f), 0.0f);
+        assertEquals(-0.6f, MathUtils.clamp(-0.7f, -0.6f, 7.0f), 0.0f);
+    }
+}
diff --git a/core-utils/tests/java/android/support/v4/provider/GrantActivity.java b/core-utils/tests/java/android/support/v4/provider/GrantActivity.java
index c4dbb27..a354201 100644
--- a/core-utils/tests/java/android/support/v4/provider/GrantActivity.java
+++ b/core-utils/tests/java/android/support/v4/provider/GrantActivity.java
@@ -20,7 +20,6 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.v4.provider.DocumentFileTest;
 
 /**
  * Stub activity used to request a permission grant for
diff --git a/customtabs/Android.mk b/customtabs/Android.mk
index e6f6ead..cfd9971 100644
--- a/customtabs/Android.mk
+++ b/customtabs/Android.mk
@@ -31,7 +31,6 @@
     $(call all-java-files-under,src) \
     $(call all-Iaidl-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-annotations \
     android-support-compat
diff --git a/customtabs/AndroidManifest-make.xml b/customtabs/AndroidManifest-make.xml
deleted file mode 100644
index 212fab9..0000000
--- a/customtabs/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.customtabs">
-    <uses-sdk android:minSdkVersion="15"/>
-    <application />
-</manifest>
diff --git a/customtabs/build.gradle b/customtabs/build.gradle
index a2ad274..2576d29 100644
--- a/customtabs/build.gradle
+++ b/customtabs/build.gradle
@@ -1,91 +1,35 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'customtabs'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-annotations')
+    api project(':support-compat')
+    api project(':support-annotations')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation project(':support-testutils')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 15
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['src']
         main.aidl.srcDirs = ['src']
         main.res.srcDir 'res'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'java'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir('tests/src/')
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Custom Tabs'
-                description "Android Support Custom Tabs"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Custom Tabs'
+    inceptionYear '2015'
+    description 'Android Support Custom Tabs'
 }
diff --git a/customtabs/lint-baseline.xml b/customtabs/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/customtabs/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/customtabs/tests/AndroidManifest.xml b/customtabs/tests/AndroidManifest.xml
index 6fe8ad9..644f9e0 100644
--- a/customtabs/tests/AndroidManifest.xml
+++ b/customtabs/tests/AndroidManifest.xml
@@ -15,20 +15,14 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.customtabs.test">
-    <uses-sdk
-        android:minSdkVersion="15"
-        android:targetSdkVersion="23"
-        tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-              android.support.test.espresso, android.support.test.espresso.idling" />
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application>
-        <uses-library android:name="android.test.runner" />
-        <activity   android:name="android.support.customtabs.TestActivity"/>
+        <activity android:name="android.support.customtabs.TestActivity"/>
 
-        <service    android:name="android.support.customtabs.PostMessageService"/>
+        <service android:name="android.support.customtabs.PostMessageService"/>
 
-        <service    android:name="android.support.customtabs.TestCustomTabsService"/>
+        <service android:name="android.support.customtabs.TestCustomTabsService"/>
     </application>
 </manifest>
diff --git a/customtabs/tests/src/android/support/customtabs/PollingCheck.java b/customtabs/tests/src/android/support/customtabs/PollingCheck.java
deleted file mode 100644
index 0163e94..0000000
--- a/customtabs/tests/src/android/support/customtabs/PollingCheck.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.customtabs;
-
-import junit.framework.Assert;
-
-import java.util.concurrent.Callable;
-
-public abstract class PollingCheck {
-    private static final long TIME_SLICE = 50;
-    private long mTimeout = 3000;
-
-    public interface PollingCheckCondition {
-        boolean canProceed();
-    }
-
-    public PollingCheck() {
-    }
-
-    public PollingCheck(long timeout) {
-        mTimeout = timeout;
-    }
-
-    protected abstract boolean check();
-
-    public void run() {
-        if (check()) {
-            return;
-        }
-
-        long timeout = mTimeout;
-        while (timeout > 0) {
-            try {
-                Thread.sleep(TIME_SLICE);
-            } catch (InterruptedException e) {
-                Assert.fail("unexpected InterruptedException");
-            }
-
-            if (check()) {
-                return;
-            }
-
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail("unexpected timeout");
-    }
-
-    public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
-            throws Exception {
-        while (timeout > 0) {
-            if (condition.call()) {
-                return;
-            }
-
-            Thread.sleep(TIME_SLICE);
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail(message.toString());
-    }
-
-    public static void waitFor(final PollingCheckCondition condition) {
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-
-    public static void waitFor(long timeout, final PollingCheckCondition condition) {
-        new PollingCheck(timeout) {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-}
diff --git a/customtabs/tests/src/android/support/customtabs/PostMessageServiceConnectionTest.java b/customtabs/tests/src/android/support/customtabs/PostMessageServiceConnectionTest.java
index a2b1b31..07d21a8 100644
--- a/customtabs/tests/src/android/support/customtabs/PostMessageServiceConnectionTest.java
+++ b/customtabs/tests/src/android/support/customtabs/PostMessageServiceConnectionTest.java
@@ -26,6 +26,7 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.rule.ServiceTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -61,6 +62,7 @@
         mContext = mActivityTestRule.getActivity();
         mConnection = new PostMessageServiceConnection(
                 new CustomTabsSessionToken(mCallback.getStub())) {
+            @Override
             public void onPostMessageServiceConnected() {
                 mServiceConnected = true;
             }
diff --git a/customtabs/tests/src/android/support/customtabs/PostMessageTest.java b/customtabs/tests/src/android/support/customtabs/PostMessageTest.java
index d20a06d..e832b23 100644
--- a/customtabs/tests/src/android/support/customtabs/PostMessageTest.java
+++ b/customtabs/tests/src/android/support/customtabs/PostMessageTest.java
@@ -29,6 +29,7 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.rule.ServiceTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/design/Android.mk b/design/Android.mk
index 2e634eb..08f8815 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -31,14 +31,9 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
     $(call all-java-files-under,base) \
-    $(call all-java-files-under,gingerbread) \
-    $(call all-java-files-under,honeycomb) \
-    $(call all-java-files-under,honeycomb-mr1) \
-    $(call all-java-files-under,ics) \
     $(call all-java-files-under,lollipop) \
     $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-transition \
     android-support-v7-appcompat \
diff --git a/design/AndroidManifest-make.xml b/design/AndroidManifest-make.xml
deleted file mode 100644
index d51186d..0000000
--- a/design/AndroidManifest-make.xml
+++ /dev/null
@@ -1,22 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.design">
-    <uses-sdk android:minSdkVersion="9"
-              tools:overrideLibrary="android.support.transition"/>
-    <application />
-</manifest>
diff --git a/design/AndroidManifest.xml b/design/AndroidManifest.xml
index 81924cb..afae16e 100644
--- a/design/AndroidManifest.xml
+++ b/design/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.design">
-    <uses-sdk android:minSdkVersion="9"
+    <uses-sdk android:minSdkVersion="14"
               tools:overrideLibrary="android.support.transition"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
diff --git a/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java b/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java
deleted file mode 100644
index 22501c1..0000000
--- a/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.internal;
-
-import android.view.ViewGroup;
-
-class BottomNavigationAnimationHelperBase {
-    void beginDelayedTransition(ViewGroup view) {
-        // Do nothing.
-    }
-}
diff --git a/design/base/android/support/design/widget/AnimationUtils.java b/design/base/android/support/design/widget/AnimationUtils.java
index 8ef1722..3613afd 100644
--- a/design/base/android/support/design/widget/AnimationUtils.java
+++ b/design/base/android/support/design/widget/AnimationUtils.java
@@ -19,7 +19,6 @@
 import android.support.v4.view.animation.FastOutLinearInInterpolator;
 import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.support.v4.view.animation.LinearOutSlowInInterpolator;
-import android.view.animation.Animation;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
@@ -43,18 +42,4 @@
         return startValue + Math.round(fraction * (endValue - startValue));
     }
 
-    static class AnimationListenerAdapter implements Animation.AnimationListener {
-        @Override
-        public void onAnimationStart(Animation animation) {
-        }
-
-        @Override
-        public void onAnimationEnd(Animation animation) {
-        }
-
-        @Override
-        public void onAnimationRepeat(Animation animation) {
-        }
-    }
-
 }
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
index 36ccbf2..132cd81 100644
--- a/design/base/android/support/design/widget/FloatingActionButtonImpl.java
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -16,6 +16,9 @@
 
 package android.support.design.widget;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
@@ -23,15 +26,21 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.view.ViewCompat;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.animation.Interpolator;
 
-abstract class FloatingActionButtonImpl {
-
+@RequiresApi(14)
+class FloatingActionButtonImpl {
     static final Interpolator ANIM_INTERPOLATOR = AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR;
     static final long PRESSED_ANIM_DURATION = 100;
     static final long PRESSED_ANIM_DELAY = 100;
@@ -42,6 +51,12 @@
 
     int mAnimState = ANIM_STATE_NONE;
 
+    private final StateListAnimator mStateListAnimator;
+
+    ShadowDrawableWrapper mShadowDrawable;
+
+    private float mRotation;
+
     Drawable mShapeDrawable;
     Drawable mRippleDrawable;
     CircularBorderDrawable mBorderDrawable;
@@ -51,8 +66,8 @@
     float mPressedTranslationZ;
 
     interface InternalVisibilityChangedListener {
-        public void onShown();
-        public void onHidden();
+        void onShown();
+        void onHidden();
     }
 
     static final int SHOW_HIDE_ANIM_DURATION = 200;
@@ -66,26 +81,92 @@
 
     final VisibilityAwareImageButton mView;
     final ShadowViewDelegate mShadowViewDelegate;
-    final ValueAnimatorCompat.Creator mAnimatorCreator;
 
     private final Rect mTmpRect = new Rect();
     private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
 
     FloatingActionButtonImpl(VisibilityAwareImageButton view,
-            ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
+            ShadowViewDelegate shadowViewDelegate) {
         mView = view;
         mShadowViewDelegate = shadowViewDelegate;
-        mAnimatorCreator = animatorCreator;
+
+        mStateListAnimator = new StateListAnimator();
+
+        // Elevate with translationZ when pressed or focused
+        mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
+                createAnimator(new ElevateToTranslationZAnimation()));
+        mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
+                createAnimator(new ElevateToTranslationZAnimation()));
+        // Reset back to elevation by default
+        mStateListAnimator.addState(ENABLED_STATE_SET,
+                createAnimator(new ResetElevationAnimation()));
+        // Set to 0 when disabled
+        mStateListAnimator.addState(EMPTY_STATE_SET,
+                createAnimator(new DisabledElevationAnimation()));
+
+        mRotation = mView.getRotation();
     }
 
-    abstract void setBackgroundDrawable(ColorStateList backgroundTint,
-            PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth);
+    void setBackgroundDrawable(ColorStateList backgroundTint,
+            PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
+        // Now we need to tint the original background with the tint, using
+        // an InsetDrawable if we have a border width
+        mShapeDrawable = DrawableCompat.wrap(createShapeDrawable());
+        DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
+        if (backgroundTintMode != null) {
+            DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
+        }
 
-    abstract void setBackgroundTintList(ColorStateList tint);
+        // Now we created a mask Drawable which will be used for touch feedback.
+        GradientDrawable touchFeedbackShape = createShapeDrawable();
 
-    abstract void setBackgroundTintMode(PorterDuff.Mode tintMode);
+        // We'll now wrap that touch feedback mask drawable with a ColorStateList. We do not need
+        // to inset for any border here as LayerDrawable will nest the padding for us
+        mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
+        DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
 
-    abstract void setRippleColor(int rippleColor);
+        final Drawable[] layers;
+        if (borderWidth > 0) {
+            mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
+            layers = new Drawable[] {mBorderDrawable, mShapeDrawable, mRippleDrawable};
+        } else {
+            mBorderDrawable = null;
+            layers = new Drawable[] {mShapeDrawable, mRippleDrawable};
+        }
+
+        mContentBackground = new LayerDrawable(layers);
+
+        mShadowDrawable = new ShadowDrawableWrapper(
+                mView.getContext(),
+                mContentBackground,
+                mShadowViewDelegate.getRadius(),
+                mElevation,
+                mElevation + mPressedTranslationZ);
+        mShadowDrawable.setAddPaddingForCorners(false);
+        mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
+    }
+
+    void setBackgroundTintList(ColorStateList tint) {
+        if (mShapeDrawable != null) {
+            DrawableCompat.setTintList(mShapeDrawable, tint);
+        }
+        if (mBorderDrawable != null) {
+            mBorderDrawable.setBorderTint(tint);
+        }
+    }
+
+    void setBackgroundTintMode(PorterDuff.Mode tintMode) {
+        if (mShapeDrawable != null) {
+            DrawableCompat.setTintMode(mShapeDrawable, tintMode);
+        }
+    }
+
+
+    void setRippleColor(int rippleColor) {
+        if (mRippleDrawable != null) {
+            DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
+        }
+    }
 
     final void setElevation(float elevation) {
         if (mElevation != elevation) {
@@ -94,7 +175,9 @@
         }
     }
 
-    abstract float getElevation();
+    float getElevation() {
+        return mElevation;
+    }
 
     final void setPressedTranslationZ(float translationZ) {
         if (mPressedTranslationZ != translationZ) {
@@ -103,21 +186,130 @@
         }
     }
 
-    abstract void onElevationsChanged(float elevation, float pressedTranslationZ);
+    void onElevationsChanged(float elevation, float pressedTranslationZ) {
+        if (mShadowDrawable != null) {
+            mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
+            updatePadding();
+        }
+    }
 
-    abstract void onDrawableStateChanged(int[] state);
+    void onDrawableStateChanged(int[] state) {
+        mStateListAnimator.setState(state);
+    }
 
-    abstract void jumpDrawableToCurrentState();
+    void jumpDrawableToCurrentState() {
+        mStateListAnimator.jumpToCurrentState();
+    }
 
-    abstract void hide(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
+    void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
+        if (isOrWillBeHidden()) {
+            // We either are or will soon be hidden, skip the call
+            return;
+        }
 
-    abstract void show(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
+        mView.animate().cancel();
+
+        if (shouldAnimateVisibilityChange()) {
+            mAnimState = ANIM_STATE_HIDING;
+
+            mView.animate()
+                    .scaleX(0f)
+                    .scaleY(0f)
+                    .alpha(0f)
+                    .setDuration(SHOW_HIDE_ANIM_DURATION)
+                    .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
+                    .setListener(new AnimatorListenerAdapter() {
+                        private boolean mCancelled;
+
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            mView.internalSetVisibility(View.VISIBLE, fromUser);
+                            mCancelled = false;
+                        }
+
+                        @Override
+                        public void onAnimationCancel(Animator animation) {
+                            mCancelled = true;
+                        }
+
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mAnimState = ANIM_STATE_NONE;
+
+                            if (!mCancelled) {
+                                mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE,
+                                        fromUser);
+                                if (listener != null) {
+                                    listener.onHidden();
+                                }
+                            }
+                        }
+                    });
+        } else {
+            // If the view isn't laid out, or we're in the editor, don't run the animation
+            mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
+            if (listener != null) {
+                listener.onHidden();
+            }
+        }
+    }
+
+    void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
+        if (isOrWillBeShown()) {
+            // We either are or will soon be visible, skip the call
+            return;
+        }
+
+        mView.animate().cancel();
+
+        if (shouldAnimateVisibilityChange()) {
+            mAnimState = ANIM_STATE_SHOWING;
+
+            if (mView.getVisibility() != View.VISIBLE) {
+                // If the view isn't visible currently, we'll animate it from a single pixel
+                mView.setAlpha(0f);
+                mView.setScaleY(0f);
+                mView.setScaleX(0f);
+            }
+
+            mView.animate()
+                    .scaleX(1f)
+                    .scaleY(1f)
+                    .alpha(1f)
+                    .setDuration(SHOW_HIDE_ANIM_DURATION)
+                    .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            mView.internalSetVisibility(View.VISIBLE, fromUser);
+                        }
+
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mAnimState = ANIM_STATE_NONE;
+                            if (listener != null) {
+                                listener.onShown();
+                            }
+                        }
+                    });
+        } else {
+            mView.internalSetVisibility(View.VISIBLE, fromUser);
+            mView.setAlpha(1f);
+            mView.setScaleY(1f);
+            mView.setScaleX(1f);
+            if (listener != null) {
+                listener.onShown();
+            }
+        }
+    }
 
     final Drawable getContentBackground() {
         return mContentBackground;
     }
 
-    abstract void onCompatShadowChanged();
+    void onCompatShadowChanged() {
+        // Ignore pre-v21
+    }
 
     final void updatePadding() {
         Rect rect = mTmpRect;
@@ -126,7 +318,9 @@
         mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
     }
 
-    abstract void getPadding(Rect rect);
+    void getPadding(Rect rect) {
+        mShadowDrawable.getPadding(rect);
+    }
 
     void onPaddingUpdated(Rect padding) {}
 
@@ -145,7 +339,7 @@
     }
 
     boolean requirePreDrawListener() {
-        return false;
+        return true;
     }
 
     CircularBorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
@@ -166,6 +360,11 @@
     }
 
     void onPreDraw() {
+        final float rotation = mView.getRotation();
+        if (mRotation != rotation) {
+            mRotation = rotation;
+            updateFromViewRotation();
+        }
     }
 
     private void ensurePreDrawListener() {
@@ -210,4 +409,123 @@
             return mAnimState != ANIM_STATE_SHOWING;
         }
     }
+
+    private ValueAnimator createAnimator(@NonNull ShadowAnimatorImpl impl) {
+        final ValueAnimator animator = new ValueAnimator();
+        animator.setInterpolator(ANIM_INTERPOLATOR);
+        animator.setDuration(PRESSED_ANIM_DURATION);
+        animator.addListener(impl);
+        animator.addUpdateListener(impl);
+        animator.setFloatValues(0, 1);
+        return animator;
+    }
+
+    private abstract class ShadowAnimatorImpl extends AnimatorListenerAdapter
+            implements ValueAnimator.AnimatorUpdateListener {
+        private boolean mValidValues;
+        private float mShadowSizeStart;
+        private float mShadowSizeEnd;
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animator) {
+            if (!mValidValues) {
+                mShadowSizeStart = mShadowDrawable.getShadowSize();
+                mShadowSizeEnd = getTargetShadowSize();
+                mValidValues = true;
+            }
+
+            mShadowDrawable.setShadowSize(mShadowSizeStart
+                    + ((mShadowSizeEnd - mShadowSizeStart) * animator.getAnimatedFraction()));
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animator) {
+            mShadowDrawable.setShadowSize(mShadowSizeEnd);
+            mValidValues = false;
+        }
+
+        /**
+         * @return the shadow size we want to animate to.
+         */
+        protected abstract float getTargetShadowSize();
+    }
+
+    private class ResetElevationAnimation extends ShadowAnimatorImpl {
+        ResetElevationAnimation() {
+        }
+
+        @Override
+        protected float getTargetShadowSize() {
+            return mElevation;
+        }
+    }
+
+    private class ElevateToTranslationZAnimation extends ShadowAnimatorImpl {
+        ElevateToTranslationZAnimation() {
+        }
+
+        @Override
+        protected float getTargetShadowSize() {
+            return mElevation + mPressedTranslationZ;
+        }
+    }
+
+    private class DisabledElevationAnimation extends ShadowAnimatorImpl {
+        DisabledElevationAnimation() {
+        }
+
+        @Override
+        protected float getTargetShadowSize() {
+            return 0f;
+        }
+    }
+
+    private static ColorStateList createColorStateList(int selectedColor) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
+
+        states[i] = FOCUSED_ENABLED_STATE_SET;
+        colors[i] = selectedColor;
+        i++;
+
+        states[i] = PRESSED_ENABLED_STATE_SET;
+        colors[i] = selectedColor;
+        i++;
+
+        // Default enabled state
+        states[i] = new int[0];
+        colors[i] = Color.TRANSPARENT;
+        i++;
+
+        return new ColorStateList(states, colors);
+    }
+
+    private boolean shouldAnimateVisibilityChange() {
+        return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
+    }
+
+    private void updateFromViewRotation() {
+        if (Build.VERSION.SDK_INT == 19) {
+            // KitKat seems to have an issue with views which are rotated with angles which are
+            // not divisible by 90. Worked around by moving to software rendering in these cases.
+            if ((mRotation % 90) != 0) {
+                if (mView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
+                    mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+                }
+            } else {
+                if (mView.getLayerType() != View.LAYER_TYPE_NONE) {
+                    mView.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+            }
+        }
+
+        // Offset any View rotation
+        if (mShadowDrawable != null) {
+            mShadowDrawable.setRotation(-mRotation);
+        }
+        if (mBorderDrawable != null) {
+            mBorderDrawable.setRotation(-mRotation);
+        }
+    }
 }
diff --git a/design/base/android/support/design/widget/MathUtils.java b/design/base/android/support/design/widget/MathUtils.java
deleted file mode 100644
index b509815..0000000
--- a/design/base/android/support/design/widget/MathUtils.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-class MathUtils {
-
-    static int constrain(int amount, int low, int high) {
-        return amount < low ? low : (amount > high ? high : amount);
-    }
-
-    static float constrain(float amount, float low, float high) {
-        return amount < low ? low : (amount > high ? high : amount);
-    }
-
-}
diff --git a/design/base/android/support/design/widget/StateListAnimator.java b/design/base/android/support/design/widget/StateListAnimator.java
index 4378ef9..aef24be 100644
--- a/design/base/android/support/design/widget/StateListAnimator.java
+++ b/design/base/android/support/design/widget/StateListAnimator.java
@@ -16,6 +16,9 @@
 
 package android.support.design.widget;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.util.StateSet;
 
 import java.util.ArrayList;
@@ -25,17 +28,17 @@
     private final ArrayList<Tuple> mTuples = new ArrayList<>();
 
     private Tuple mLastMatch = null;
-    ValueAnimatorCompat mRunningAnimator = null;
+    ValueAnimator mRunningAnimator = null;
 
-    private final ValueAnimatorCompat.AnimatorListener mAnimationListener
-            = new ValueAnimatorCompat.AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(ValueAnimatorCompat animator) {
-            if (mRunningAnimator == animator) {
-                mRunningAnimator = null;
-            }
-        }
-    };
+    private final ValueAnimator.AnimatorListener mAnimationListener =
+            new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    if (mRunningAnimator == animator) {
+                        mRunningAnimator = null;
+                    }
+                }
+            };
 
     /**
      * Associates the given Animation with the provided drawable state specs so that it will be run
@@ -44,7 +47,7 @@
      * @param specs    The drawable state specs to match against
      * @param animator The animator to run when the specs match
      */
-    public void addState(int[] specs, ValueAnimatorCompat animator) {
+    public void addState(int[] specs, ValueAnimator animator) {
         Tuple tuple = new Tuple(specs, animator);
         animator.addListener(mAnimationListener);
         mTuples.add(tuple);
@@ -103,9 +106,9 @@
 
     static class Tuple {
         final int[] mSpecs;
-        final ValueAnimatorCompat mAnimator;
+        final ValueAnimator mAnimator;
 
-        Tuple(int[] specs, ValueAnimatorCompat animator) {
+        Tuple(int[] specs, ValueAnimator animator) {
             mSpecs = specs;
             mAnimator = animator;
         }
diff --git a/design/base/android/support/design/widget/ValueAnimatorCompat.java b/design/base/android/support/design/widget/ValueAnimatorCompat.java
deleted file mode 100644
index 6c52db3..0000000
--- a/design/base/android/support/design/widget/ValueAnimatorCompat.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-import android.support.annotation.NonNull;
-import android.view.animation.Interpolator;
-
-/**
- * This class offers a very small subset of {@code ValueAnimator}'s API, but works pre-v11 too.
- * <p>
- * You shouldn't not instantiate this directly. Instead use {@code ViewUtils.createAnimator()}.
- */
-class ValueAnimatorCompat {
-
-    interface AnimatorUpdateListener {
-        /**
-         * <p>Notifies the occurrence of another frame of the animation.</p>
-         *
-         * @param animator The animation which was repeated.
-         */
-        void onAnimationUpdate(ValueAnimatorCompat animator);
-    }
-
-    /**
-     * An animation listener receives notifications from an animation.
-     * Notifications indicate animation related events, such as the end or the
-     * repetition of the animation.
-     */
-    interface AnimatorListener {
-        /**
-         * <p>Notifies the start of the animation.</p>
-         *
-         * @param animator The started animation.
-         */
-        void onAnimationStart(ValueAnimatorCompat animator);
-        /**
-         * <p>Notifies the end of the animation. This callback is not invoked
-         * for animations with repeat count set to INFINITE.</p>
-         *
-         * @param animator The animation which reached its end.
-         */
-        void onAnimationEnd(ValueAnimatorCompat animator);
-        /**
-         * <p>Notifies the cancellation of the animation. This callback is not invoked
-         * for animations with repeat count set to INFINITE.</p>
-         *
-         * @param animator The animation which was canceled.
-         */
-        void onAnimationCancel(ValueAnimatorCompat animator);
-    }
-
-    static class AnimatorListenerAdapter implements AnimatorListener {
-        @Override
-        public void onAnimationStart(ValueAnimatorCompat animator) {
-        }
-
-        @Override
-        public void onAnimationEnd(ValueAnimatorCompat animator) {
-        }
-
-        @Override
-        public void onAnimationCancel(ValueAnimatorCompat animator) {
-        }
-    }
-
-    interface Creator {
-        @NonNull
-        ValueAnimatorCompat createAnimator();
-    }
-
-    static abstract class Impl {
-        interface AnimatorUpdateListenerProxy {
-            void onAnimationUpdate();
-        }
-
-        interface AnimatorListenerProxy {
-            void onAnimationStart();
-            void onAnimationEnd();
-            void onAnimationCancel();
-        }
-
-        abstract void start();
-        abstract boolean isRunning();
-        abstract void setInterpolator(Interpolator interpolator);
-        abstract void addListener(AnimatorListenerProxy listener);
-        abstract void addUpdateListener(AnimatorUpdateListenerProxy updateListener);
-        abstract void setIntValues(int from, int to);
-        abstract int getAnimatedIntValue();
-        abstract void setFloatValues(float from, float to);
-        abstract float getAnimatedFloatValue();
-        abstract void setDuration(long duration);
-        abstract void cancel();
-        abstract float getAnimatedFraction();
-        abstract void end();
-        abstract long getDuration();
-    }
-
-    private final Impl mImpl;
-
-    ValueAnimatorCompat(Impl impl) {
-        mImpl = impl;
-    }
-
-    public void start() {
-        mImpl.start();
-    }
-
-    public boolean isRunning() {
-        return mImpl.isRunning();
-    }
-
-    public void setInterpolator(Interpolator interpolator) {
-        mImpl.setInterpolator(interpolator);
-    }
-
-    public void addUpdateListener(final AnimatorUpdateListener updateListener) {
-        if (updateListener != null) {
-            mImpl.addUpdateListener(new Impl.AnimatorUpdateListenerProxy() {
-                @Override
-                public void onAnimationUpdate() {
-                    updateListener.onAnimationUpdate(ValueAnimatorCompat.this);
-                }
-            });
-        } else {
-            mImpl.addUpdateListener(null);
-        }
-    }
-
-    public void addListener(final AnimatorListener listener) {
-        if (listener != null) {
-            mImpl.addListener(new Impl.AnimatorListenerProxy() {
-                @Override
-                public void onAnimationStart() {
-                    listener.onAnimationStart(ValueAnimatorCompat.this);
-                }
-
-                @Override
-                public void onAnimationEnd() {
-                    listener.onAnimationEnd(ValueAnimatorCompat.this);
-                }
-
-                @Override
-                public void onAnimationCancel() {
-                    listener.onAnimationCancel(ValueAnimatorCompat.this);
-                }
-            });
-        } else {
-            mImpl.addListener(null);
-        }
-    }
-
-    public void setIntValues(int from, int to) {
-        mImpl.setIntValues(from, to);
-    }
-
-    public int getAnimatedIntValue() {
-        return mImpl.getAnimatedIntValue();
-    }
-
-    public void setFloatValues(float from, float to) {
-        mImpl.setFloatValues(from, to);
-    }
-
-    public float getAnimatedFloatValue() {
-        return mImpl.getAnimatedFloatValue();
-    }
-
-    public void setDuration(long duration) {
-        mImpl.setDuration(duration);
-    }
-
-    public void cancel() {
-        mImpl.cancel();
-    }
-
-    public float getAnimatedFraction() {
-        return mImpl.getAnimatedFraction();
-    }
-
-    public void end() {
-        mImpl.end();
-    }
-
-    public long getDuration() {
-        return mImpl.getDuration();
-    }
-}
diff --git a/design/build.gradle b/design/build.gradle
index d5f83b8..23cbeb7 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -1,48 +1,36 @@
-apply plugin: 'com.android.library'
-
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'design'
 
 dependencies {
-    compile project(':support-v4')
-    compile project(':support-appcompat-v7')
-    compile project(':support-recyclerview-v7')
-    compile project(':support-transition')
+    api project(':support-v4')
+    api project(':support-appcompat-v7')
+    api project(':support-recyclerview-v7')
+    api project(':support-transition')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
-        exclude module: 'support-annotations'
-    }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
-        exclude module: 'support-annotations'
-    }
-    androidTestCompile ("com.android.support.test.espresso:espresso-contrib:${project.rootProject.ext.espressoVersion}") {
-        exclude group: 'com.android.support'
-    }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
-    testCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation libs.test_runner,      { exclude module: 'support-annotations' }
+    androidTestImplementation libs.espresso_core,    { exclude module: 'support-annotations' }
+    androidTestImplementation libs.espresso_contrib, { exclude group: 'com.android.support' }
+    androidTestImplementation libs.mockito_core,     { exclude group: 'net.bytebuddy' } // DexMaker has it"s own MockMaker
+    androidTestImplementation libs.dexmaker_mockito, { exclude group: 'net.bytebuddy' } // DexMaker has it"s own MockMaker
+    androidTestImplementation project(':support-testutils')
+
+    testImplementation libs.junit
+    testImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
                 'base',
                 'gingerbread',
-                'honeycomb',
-                'honeycomb-mr1',
                 'ics',
                 'lollipop',
                 'src'
@@ -53,19 +41,9 @@
         ]
         main.resources.srcDir 'src'
 
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-
         test.java.srcDir 'jvm-tests/src'
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
-
     buildTypes.all {
         consumerProguardFiles 'proguard-rules.pro'
     }
@@ -81,52 +59,8 @@
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Design Support Library'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Design Support Library'
+    inceptionYear '2015'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
 }
diff --git a/design/gingerbread/android/support/design/widget/FloatingActionButtonGingerbread.java b/design/gingerbread/android/support/design/widget/FloatingActionButtonGingerbread.java
deleted file mode 100644
index 6edc9e4..0000000
--- a/design/gingerbread/android/support/design/widget/FloatingActionButtonGingerbread.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.design.R;
-import android.support.design.widget.AnimationUtils.AnimationListenerAdapter;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.view.View;
-import android.view.animation.Animation;
-
-class FloatingActionButtonGingerbread extends FloatingActionButtonImpl {
-
-    private final StateListAnimator mStateListAnimator;
-
-    ShadowDrawableWrapper mShadowDrawable;
-
-    FloatingActionButtonGingerbread(VisibilityAwareImageButton view,
-            ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
-        super(view, shadowViewDelegate, animatorCreator);
-
-        mStateListAnimator = new StateListAnimator();
-
-        // Elevate with translationZ when pressed or focused
-        mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
-                createAnimator(new ElevateToTranslationZAnimation()));
-        mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
-                createAnimator(new ElevateToTranslationZAnimation()));
-        // Reset back to elevation by default
-        mStateListAnimator.addState(ENABLED_STATE_SET,
-                createAnimator(new ResetElevationAnimation()));
-        // Set to 0 when disabled
-        mStateListAnimator.addState(EMPTY_STATE_SET,
-                createAnimator(new DisabledElevationAnimation()));
-    }
-
-    @Override
-    void setBackgroundDrawable(ColorStateList backgroundTint,
-            PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
-        // Now we need to tint the original background with the tint, using
-        // an InsetDrawable if we have a border width
-        mShapeDrawable = DrawableCompat.wrap(createShapeDrawable());
-        DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
-        if (backgroundTintMode != null) {
-            DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
-        }
-
-        // Now we created a mask Drawable which will be used for touch feedback.
-        GradientDrawable touchFeedbackShape = createShapeDrawable();
-
-        // We'll now wrap that touch feedback mask drawable with a ColorStateList. We do not need
-        // to inset for any border here as LayerDrawable will nest the padding for us
-        mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
-        DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
-
-        final Drawable[] layers;
-        if (borderWidth > 0) {
-            mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
-            layers = new Drawable[] {mBorderDrawable, mShapeDrawable, mRippleDrawable};
-        } else {
-            mBorderDrawable = null;
-            layers = new Drawable[] {mShapeDrawable, mRippleDrawable};
-        }
-
-        mContentBackground = new LayerDrawable(layers);
-
-        mShadowDrawable = new ShadowDrawableWrapper(
-                mView.getContext(),
-                mContentBackground,
-                mShadowViewDelegate.getRadius(),
-                mElevation,
-                mElevation + mPressedTranslationZ);
-        mShadowDrawable.setAddPaddingForCorners(false);
-        mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
-    }
-
-    @Override
-    void setBackgroundTintList(ColorStateList tint) {
-        if (mShapeDrawable != null) {
-            DrawableCompat.setTintList(mShapeDrawable, tint);
-        }
-        if (mBorderDrawable != null) {
-            mBorderDrawable.setBorderTint(tint);
-        }
-    }
-
-    @Override
-    void setBackgroundTintMode(PorterDuff.Mode tintMode) {
-        if (mShapeDrawable != null) {
-            DrawableCompat.setTintMode(mShapeDrawable, tintMode);
-        }
-    }
-
-    @Override
-    void setRippleColor(int rippleColor) {
-        if (mRippleDrawable != null) {
-            DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
-        }
-    }
-
-    @Override
-    float getElevation() {
-        return mElevation;
-    }
-
-    @Override
-    void onElevationsChanged(float elevation, float pressedTranslationZ) {
-        if (mShadowDrawable != null) {
-            mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
-            updatePadding();
-        }
-    }
-
-    @Override
-    void onDrawableStateChanged(int[] state) {
-        mStateListAnimator.setState(state);
-    }
-
-    @Override
-    void jumpDrawableToCurrentState() {
-        mStateListAnimator.jumpToCurrentState();
-    }
-
-    @Override
-    void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
-        if (isOrWillBeHidden()) {
-            // We either are or will soon be hidden, skip the call
-            return;
-        }
-
-        mAnimState = ANIM_STATE_HIDING;
-
-        Animation anim = android.view.animation.AnimationUtils.loadAnimation(
-                mView.getContext(), R.anim.design_fab_out);
-        anim.setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR);
-        anim.setDuration(SHOW_HIDE_ANIM_DURATION);
-        anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                mAnimState = ANIM_STATE_NONE;
-                mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
-                if (listener != null) {
-                    listener.onHidden();
-                }
-            }
-        });
-        mView.startAnimation(anim);
-    }
-
-    @Override
-    void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
-        if (isOrWillBeShown()) {
-            // We either are or will soon be visible, skip the call
-            return;
-        }
-
-        mAnimState = ANIM_STATE_SHOWING;
-
-        mView.internalSetVisibility(View.VISIBLE, fromUser);
-        Animation anim = android.view.animation.AnimationUtils.loadAnimation(
-                mView.getContext(), R.anim.design_fab_in);
-        anim.setDuration(SHOW_HIDE_ANIM_DURATION);
-        anim.setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
-        anim.setAnimationListener(new AnimationListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                mAnimState = ANIM_STATE_NONE;
-                if (listener != null) {
-                    listener.onShown();
-                }
-            }
-        });
-        mView.startAnimation(anim);
-    }
-
-    @Override
-    void onCompatShadowChanged() {
-        // Ignore pre-v21
-    }
-
-    @Override
-    void getPadding(Rect rect) {
-        mShadowDrawable.getPadding(rect);
-    }
-
-    private ValueAnimatorCompat createAnimator(@NonNull ShadowAnimatorImpl impl) {
-        final ValueAnimatorCompat animator = mAnimatorCreator.createAnimator();
-        animator.setInterpolator(ANIM_INTERPOLATOR);
-        animator.setDuration(PRESSED_ANIM_DURATION);
-        animator.addListener(impl);
-        animator.addUpdateListener(impl);
-        animator.setFloatValues(0, 1);
-        return animator;
-    }
-
-    private abstract class ShadowAnimatorImpl extends ValueAnimatorCompat.AnimatorListenerAdapter
-            implements ValueAnimatorCompat.AnimatorUpdateListener {
-        private boolean mValidValues;
-        private float mShadowSizeStart;
-        private float mShadowSizeEnd;
-
-        @Override
-        public void onAnimationUpdate(ValueAnimatorCompat animator) {
-            if (!mValidValues) {
-                mShadowSizeStart = mShadowDrawable.getShadowSize();
-                mShadowSizeEnd = getTargetShadowSize();
-                mValidValues = true;
-            }
-
-            mShadowDrawable.setShadowSize(mShadowSizeStart
-                    + ((mShadowSizeEnd - mShadowSizeStart) * animator.getAnimatedFraction()));
-        }
-
-        @Override
-        public void onAnimationEnd(ValueAnimatorCompat animator) {
-            mShadowDrawable.setShadowSize(mShadowSizeEnd);
-            mValidValues = false;
-        }
-
-        /**
-         * @return the shadow size we want to animate to.
-         */
-        protected abstract float getTargetShadowSize();
-    }
-
-    private class ResetElevationAnimation extends ShadowAnimatorImpl {
-        ResetElevationAnimation() {
-        }
-
-        @Override
-        protected float getTargetShadowSize() {
-            return mElevation;
-        }
-    }
-
-    private class ElevateToTranslationZAnimation extends ShadowAnimatorImpl {
-        ElevateToTranslationZAnimation() {
-        }
-
-        @Override
-        protected float getTargetShadowSize() {
-            return mElevation + mPressedTranslationZ;
-        }
-    }
-
-    private class DisabledElevationAnimation extends ShadowAnimatorImpl {
-        DisabledElevationAnimation() {
-        }
-
-        @Override
-        protected float getTargetShadowSize() {
-            return 0f;
-        }
-    }
-
-    private static ColorStateList createColorStateList(int selectedColor) {
-        final int[][] states = new int[3][];
-        final int[] colors = new int[3];
-        int i = 0;
-
-        states[i] = FOCUSED_ENABLED_STATE_SET;
-        colors[i] = selectedColor;
-        i++;
-
-        states[i] = PRESSED_ENABLED_STATE_SET;
-        colors[i] = selectedColor;
-        i++;
-
-        // Default enabled state
-        states[i] = new int[0];
-        colors[i] = Color.TRANSPARENT;
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-}
\ No newline at end of file
diff --git a/design/gingerbread/android/support/design/widget/ValueAnimatorCompatImplGingerbread.java b/design/gingerbread/android/support/design/widget/ValueAnimatorCompatImplGingerbread.java
deleted file mode 100644
index 81e59b6..0000000
--- a/design/gingerbread/android/support/design/widget/ValueAnimatorCompatImplGingerbread.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-
-/**
- * A 'fake' ValueAnimator implementation which uses a Runnable.
- */
-class ValueAnimatorCompatImplGingerbread extends ValueAnimatorCompat.Impl {
-
-    private static final int HANDLER_DELAY = 10;
-    private static final int DEFAULT_DURATION = 200;
-
-    private static final Handler sHandler = new Handler(Looper.getMainLooper());
-
-    private long mStartTime;
-    private boolean mIsRunning;
-    private float mAnimatedFraction;
-
-    private final int[] mIntValues = new int[2];
-    private final float[] mFloatValues = new float[2];
-
-    private long mDuration = DEFAULT_DURATION;
-    private Interpolator mInterpolator;
-    private ArrayList<AnimatorListenerProxy> mListeners;
-    private ArrayList<AnimatorUpdateListenerProxy> mUpdateListeners;
-
-    private final Runnable mRunnable = new Runnable() {
-        public void run() {
-            update();
-        }
-    };
-
-    @Override
-    public void start() {
-        if (mIsRunning) {
-            // If we're already running, ignore
-            return;
-        }
-        if (mInterpolator == null) {
-            mInterpolator = new AccelerateDecelerateInterpolator();
-        }
-        mIsRunning = true;
-
-        // Reset the animated fraction
-        mAnimatedFraction = 0f;
-
-        startInternal();
-    }
-
-    final void startInternal() {
-        mStartTime = SystemClock.uptimeMillis();
-        dispatchAnimationUpdate();
-        dispatchAnimationStart();
-        // Now start our animation ticker
-        sHandler.postDelayed(mRunnable, HANDLER_DELAY);
-    }
-
-    @Override
-    public boolean isRunning() {
-        return mIsRunning;
-    }
-
-    @Override
-    public void setInterpolator(Interpolator interpolator) {
-        mInterpolator = interpolator;
-    }
-
-    @Override
-    public void addListener(AnimatorListenerProxy listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<>();
-        }
-        mListeners.add(listener);
-    }
-
-    @Override
-    public void addUpdateListener(AnimatorUpdateListenerProxy updateListener) {
-        if (mUpdateListeners == null) {
-            mUpdateListeners = new ArrayList<>();
-        }
-        mUpdateListeners.add(updateListener);
-    }
-
-    @Override
-    public void setIntValues(int from, int to) {
-        mIntValues[0] = from;
-        mIntValues[1] = to;
-    }
-
-    @Override
-    public int getAnimatedIntValue() {
-        return AnimationUtils.lerp(mIntValues[0], mIntValues[1], getAnimatedFraction());
-    }
-
-    @Override
-    public void setFloatValues(float from, float to) {
-        mFloatValues[0] = from;
-        mFloatValues[1] = to;
-    }
-
-    @Override
-    public float getAnimatedFloatValue() {
-        return AnimationUtils.lerp(mFloatValues[0], mFloatValues[1], getAnimatedFraction());
-    }
-
-    @Override
-    public void setDuration(long duration) {
-        mDuration = duration;
-    }
-
-    @Override
-    public void cancel() {
-        mIsRunning = false;
-        sHandler.removeCallbacks(mRunnable);
-
-        dispatchAnimationCancel();
-        dispatchAnimationEnd();
-    }
-
-    @Override
-    public float getAnimatedFraction() {
-        return mAnimatedFraction;
-    }
-
-    @Override
-    public void end() {
-        if (mIsRunning) {
-            mIsRunning = false;
-            sHandler.removeCallbacks(mRunnable);
-            // Set our animated fraction to 1
-            mAnimatedFraction = 1f;
-            dispatchAnimationUpdate();
-            dispatchAnimationEnd();
-        }
-    }
-
-    @Override
-    public long getDuration() {
-        return mDuration;
-    }
-
-    final void update() {
-        if (mIsRunning) {
-            // Update the animated fraction
-            final long elapsed = SystemClock.uptimeMillis() - mStartTime;
-            final float linearFraction = MathUtils.constrain(elapsed / (float) mDuration, 0f, 1f);
-            mAnimatedFraction = mInterpolator != null
-                    ? mInterpolator.getInterpolation(linearFraction)
-                    : linearFraction;
-
-            // If we're running, dispatch to the update listeners
-            dispatchAnimationUpdate();
-
-            // Check to see if we've passed the animation duration
-            if (SystemClock.uptimeMillis() >= (mStartTime + mDuration)) {
-                mIsRunning = false;
-
-                dispatchAnimationEnd();
-            }
-        }
-
-        if (mIsRunning) {
-            // If we're still running, post another delayed runnable
-            sHandler.postDelayed(mRunnable, HANDLER_DELAY);
-        }
-    }
-
-    private void dispatchAnimationUpdate() {
-        if (mUpdateListeners != null) {
-            for (int i = 0, count = mUpdateListeners.size(); i < count; i++) {
-                mUpdateListeners.get(i).onAnimationUpdate();
-            }
-        }
-    }
-
-    private void dispatchAnimationStart() {
-        if (mListeners != null) {
-            for (int i = 0, count = mListeners.size(); i < count; i++) {
-                mListeners.get(i).onAnimationStart();
-            }
-        }
-    }
-
-    private void dispatchAnimationCancel() {
-        if (mListeners != null) {
-            for (int i = 0, count = mListeners.size(); i < count; i++) {
-                mListeners.get(i).onAnimationCancel();
-            }
-        }
-    }
-
-    private void dispatchAnimationEnd() {
-        if (mListeners != null) {
-            for (int i = 0, count = mListeners.size(); i < count; i++) {
-                mListeners.get(i).onAnimationEnd();
-            }
-        }
-    }
-}
diff --git a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
deleted file mode 100644
index e1f287b..0000000
--- a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.animation.Interpolator;
-
-@RequiresApi(12)
-@TargetApi(12)
-class ValueAnimatorCompatImplHoneycombMr1 extends ValueAnimatorCompat.Impl {
-
-    private final ValueAnimator mValueAnimator;
-
-    ValueAnimatorCompatImplHoneycombMr1() {
-        mValueAnimator = new ValueAnimator();
-    }
-
-    @Override
-    public void start() {
-        mValueAnimator.start();
-    }
-
-    @Override
-    public boolean isRunning() {
-        return mValueAnimator.isRunning();
-    }
-
-    @Override
-    public void setInterpolator(Interpolator interpolator) {
-        mValueAnimator.setInterpolator(interpolator);
-    }
-
-    @Override
-    public void addUpdateListener(final AnimatorUpdateListenerProxy updateListener) {
-        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                updateListener.onAnimationUpdate();
-            }
-        });
-    }
-
-    @Override
-    public void addListener(final AnimatorListenerProxy listener) {
-        mValueAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animator) {
-                listener.onAnimationStart();
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animator) {
-                listener.onAnimationEnd();
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animator) {
-                listener.onAnimationCancel();
-            }
-        });
-    }
-
-    @Override
-    public void setIntValues(int from, int to) {
-        mValueAnimator.setIntValues(from, to);
-    }
-
-    @Override
-    public int getAnimatedIntValue() {
-        return (int) mValueAnimator.getAnimatedValue();
-    }
-
-    @Override
-    public void setFloatValues(float from, float to) {
-        mValueAnimator.setFloatValues(from, to);
-    }
-
-    @Override
-    public float getAnimatedFloatValue() {
-        return (float) mValueAnimator.getAnimatedValue();
-    }
-
-    @Override
-    public void setDuration(long duration) {
-        mValueAnimator.setDuration(duration);
-    }
-
-    @Override
-    public void cancel() {
-        mValueAnimator.cancel();
-    }
-
-    @Override
-    public float getAnimatedFraction() {
-        return mValueAnimator.getAnimatedFraction();
-    }
-
-    @Override
-    public void end() {
-        mValueAnimator.end();
-    }
-
-    @Override
-    public long getDuration() {
-        return mValueAnimator.getDuration();
-    }
-}
diff --git a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java b/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
deleted file mode 100644
index 49a07cd..0000000
--- a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-import android.annotation.TargetApi;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-@RequiresApi(11)
-@TargetApi(11)
-class ViewGroupUtilsHoneycomb {
-    private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
-    private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
-
-    public static void offsetDescendantRect(ViewGroup group, View child, Rect rect) {
-        Matrix m = sMatrix.get();
-        if (m == null) {
-            m = new Matrix();
-            sMatrix.set(m);
-        } else {
-            m.reset();
-        }
-
-        offsetDescendantMatrix(group, child, m);
-
-        RectF rectF = sRectF.get();
-        if (rectF == null) {
-            rectF = new RectF();
-            sRectF.set(rectF);
-        }
-        rectF.set(rect);
-        m.mapRect(rectF);
-        rect.set((int) (rectF.left + 0.5f), (int) (rectF.top + 0.5f),
-                (int) (rectF.right + 0.5f), (int) (rectF.bottom + 0.5f));
-    }
-
-    static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {
-        final ViewParent parent = view.getParent();
-        if (parent instanceof View && parent != target) {
-            final View vp = (View) parent;
-            offsetDescendantMatrix(target, vp, m);
-            m.preTranslate(-vp.getScrollX(), -vp.getScrollY());
-        }
-
-        m.preTranslate(view.getLeft(), view.getTop());
-
-        if (!view.getMatrix().isIdentity()) {
-            m.preConcat(view.getMatrix());
-        }
-    }
-}
diff --git a/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java b/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java
deleted file mode 100644
index 6681d0b..0000000
--- a/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.internal;
-
-import android.support.transition.AutoTransition;
-import android.support.transition.TransitionManager;
-import android.support.transition.TransitionSet;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.view.ViewGroup;
-
-class BottomNavigationAnimationHelperIcs extends BottomNavigationAnimationHelperBase {
-    private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
-
-    private final TransitionSet mSet;
-
-    BottomNavigationAnimationHelperIcs() {
-        mSet = new AutoTransition();
-        mSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
-        mSet.setDuration(ACTIVE_ANIMATION_DURATION_MS);
-        mSet.setInterpolator(new FastOutSlowInInterpolator());
-        TextScale textScale = new TextScale();
-        mSet.addTransition(textScale);
-    }
-
-    void beginDelayedTransition(ViewGroup view) {
-        TransitionManager.beginDelayedTransition(view, mSet);
-    }
-}
diff --git a/design/ics/android/support/design/internal/TextScale.java b/design/ics/android/support/design/internal/TextScale.java
deleted file mode 100644
index c017223..0000000
--- a/design/ics/android/support/design/internal/TextScale.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.internal;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.support.transition.Transition;
-import android.support.transition.TransitionValues;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import java.util.Map;
-
-/**
- * @hide
- */
-@RequiresApi(14)
-@TargetApi(14)
-public class TextScale extends Transition {
-    private static final String PROPNAME_SCALE = "android:textscale:scale";
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    private void captureValues(TransitionValues transitionValues) {
-        if (transitionValues.view instanceof TextView) {
-            TextView textview = (TextView) transitionValues.view;
-            transitionValues.values.put(PROPNAME_SCALE, textview.getScaleX());
-        }
-    }
-
-    @Override
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        if (startValues == null || endValues == null || !(startValues.view instanceof TextView)
-                || !(endValues.view instanceof TextView)) {
-            return null;
-        }
-        final TextView view = (TextView) endValues.view;
-        Map<String, Object> startVals = startValues.values;
-        Map<String, Object> endVals = endValues.values;
-        final float startSize = startVals.get(PROPNAME_SCALE) != null ? (float) startVals.get(
-                PROPNAME_SCALE) : 1f;
-        final float endSize = endVals.get(PROPNAME_SCALE) != null ? (float) endVals.get(
-                PROPNAME_SCALE) : 1f;
-        if (startSize == endSize) {
-            return null;
-        }
-
-        ValueAnimator animator = ValueAnimator.ofFloat(startSize, endSize);
-
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                float animatedValue = (float) valueAnimator.getAnimatedValue();
-                view.setScaleX(animatedValue);
-                view.setScaleY(animatedValue);
-            }
-        });
-        return animator;
-    }
-}
diff --git a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java b/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
deleted file mode 100644
index 73b4cac..0000000
--- a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-
-@RequiresApi(14)
-@TargetApi(14)
-class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
-
-    private float mRotation;
-
-    FloatingActionButtonIcs(VisibilityAwareImageButton view,
-            ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
-        super(view, shadowViewDelegate, animatorCreator);
-        mRotation = mView.getRotation();
-    }
-
-    @Override
-    boolean requirePreDrawListener() {
-        return true;
-    }
-
-    @Override
-    void onPreDraw() {
-        final float rotation = mView.getRotation();
-        if (mRotation != rotation) {
-            mRotation = rotation;
-            updateFromViewRotation();
-        }
-    }
-
-    @Override
-    void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
-        if (isOrWillBeHidden()) {
-            // We either are or will soon be hidden, skip the call
-            return;
-        }
-
-        mView.animate().cancel();
-
-        if (shouldAnimateVisibilityChange()) {
-            mAnimState = ANIM_STATE_HIDING;
-
-            mView.animate()
-                    .scaleX(0f)
-                    .scaleY(0f)
-                    .alpha(0f)
-                    .setDuration(SHOW_HIDE_ANIM_DURATION)
-                    .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
-                    .setListener(new AnimatorListenerAdapter() {
-                        private boolean mCancelled;
-
-                        @Override
-                        public void onAnimationStart(Animator animation) {
-                            mView.internalSetVisibility(View.VISIBLE, fromUser);
-                            mCancelled = false;
-                        }
-
-                        @Override
-                        public void onAnimationCancel(Animator animation) {
-                            mCancelled = true;
-                        }
-
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mAnimState = ANIM_STATE_NONE;
-
-                            if (!mCancelled) {
-                                mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE,
-                                        fromUser);
-                                if (listener != null) {
-                                    listener.onHidden();
-                                }
-                            }
-                        }
-                    });
-        } else {
-            // If the view isn't laid out, or we're in the editor, don't run the animation
-            mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
-            if (listener != null) {
-                listener.onHidden();
-            }
-        }
-    }
-
-    @Override
-    void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
-        if (isOrWillBeShown()) {
-            // We either are or will soon be visible, skip the call
-            return;
-        }
-
-        mView.animate().cancel();
-
-        if (shouldAnimateVisibilityChange()) {
-            mAnimState = ANIM_STATE_SHOWING;
-
-            if (mView.getVisibility() != View.VISIBLE) {
-                // If the view isn't visible currently, we'll animate it from a single pixel
-                mView.setAlpha(0f);
-                mView.setScaleY(0f);
-                mView.setScaleX(0f);
-            }
-
-            mView.animate()
-                    .scaleX(1f)
-                    .scaleY(1f)
-                    .alpha(1f)
-                    .setDuration(SHOW_HIDE_ANIM_DURATION)
-                    .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
-                    .setListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationStart(Animator animation) {
-                            mView.internalSetVisibility(View.VISIBLE, fromUser);
-                        }
-
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mAnimState = ANIM_STATE_NONE;
-                            if (listener != null) {
-                                listener.onShown();
-                            }
-                        }
-                    });
-        } else {
-            mView.internalSetVisibility(View.VISIBLE, fromUser);
-            mView.setAlpha(1f);
-            mView.setScaleY(1f);
-            mView.setScaleX(1f);
-            if (listener != null) {
-                listener.onShown();
-            }
-        }
-    }
-
-    private boolean shouldAnimateVisibilityChange() {
-        return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
-    }
-
-    private void updateFromViewRotation() {
-        if (Build.VERSION.SDK_INT == 19) {
-            // KitKat seems to have an issue with views which are rotated with angles which are
-            // not divisible by 90. Worked around by moving to software rendering in these cases.
-            if ((mRotation % 90) != 0) {
-                if (mView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
-                    mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-                }
-            } else {
-                if (mView.getLayerType() != View.LAYER_TYPE_NONE) {
-                    mView.setLayerType(View.LAYER_TYPE_NONE, null);
-                }
-            }
-        }
-
-        // Offset any View rotation
-        if (mShadowDrawable != null) {
-            mShadowDrawable.setRotation(-mRotation);
-        }
-        if (mBorderDrawable != null) {
-            mBorderDrawable.setRotation(-mRotation);
-        }
-    }
-}
diff --git a/design/lint-baseline.xml b/design/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/design/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java b/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
index ed03d35..8008404 100644
--- a/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
+++ b/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
@@ -16,7 +16,6 @@
 
 package android.support.design.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.Outline;
 import android.support.annotation.RequiresApi;
 
@@ -24,7 +23,6 @@
  * Lollipop version of {@link CircularBorderDrawable}.
  */
 @RequiresApi(21)
-@TargetApi(21)
 class CircularBorderDrawableLollipop extends CircularBorderDrawable {
 
     @Override
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index 24ef314..0df83da 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -16,10 +16,10 @@
 
 package android.support.design.widget;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.StateListAnimator;
-import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
@@ -33,15 +33,17 @@
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.view.View;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RequiresApi(21)
-@TargetApi(21)
-class FloatingActionButtonLollipop extends FloatingActionButtonIcs {
+class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
 
     private InsetDrawable mInsetDrawable;
 
     FloatingActionButtonLollipop(VisibilityAwareImageButton view,
-            ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
-        super(view, shadowViewDelegate, animatorCreator);
+            ShadowViewDelegate shadowViewDelegate) {
+        super(view, shadowViewDelegate);
     }
 
     @Override
@@ -82,8 +84,7 @@
 
     @Override
     void onElevationsChanged(final float elevation, final float pressedTranslationZ) {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk == 21) {
+        if (Build.VERSION.SDK_INT == 21) {
             // Animations produce NPE in version 21. Bluntly set the values instead (matching the
             // logic in the animations below).
             if (mView.isEnabled()) {
@@ -118,16 +119,19 @@
 
             // Animate translationZ to 0 if not pressed
             set = new AnimatorSet();
-            set.playSequentially(
-                    ObjectAnimator.ofFloat(mView, "elevation", elevation).setDuration(0),
-                    // This is a no-op animation which exists here only for introducing the duration
-                    // because setting the delay (on the next animation) via "setDelay" or "after"
-                    // can trigger a NPE between android versions 22 and 24 (due to a framework
-                    // bug). The issue has been fixed in version 25.
-                    ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, mView.getTranslationZ())
-                            .setDuration(PRESSED_ANIM_DELAY),
-                    ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, 0f)
-                            .setDuration(PRESSED_ANIM_DURATION));
+            List<Animator> animators = new ArrayList<>();
+            animators.add(ObjectAnimator.ofFloat(mView, "elevation", elevation).setDuration(0));
+            if (Build.VERSION.SDK_INT >= 22 && Build.VERSION.SDK_INT <= 24) {
+                // This is a no-op animation which exists here only for introducing the duration
+                // because setting the delay (on the next animation) via "setDelay" or "after"
+                // can trigger a NPE between android versions 22 and 24 (due to a framework
+                // bug). The issue has been fixed in version 25.
+                animators.add(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z,
+                        mView.getTranslationZ()).setDuration(PRESSED_ANIM_DELAY));
+            }
+            animators.add(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, 0f)
+                    .setDuration(PRESSED_ANIM_DURATION));
+            set.playSequentially(animators.toArray(new ObjectAnimator[0]));
             set.setInterpolator(ANIM_INTERPOLATOR);
             stateListAnimator.addState(ENABLED_STATE_SET, set);
 
diff --git a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
index 8dfa926..5927e9b 100644
--- a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
+++ b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorInflater;
 import android.animation.ObjectAnimator;
 import android.animation.StateListAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.annotation.RequiresApi;
@@ -29,7 +28,6 @@
 import android.view.ViewOutlineProvider;
 
 @RequiresApi(21)
-@TargetApi(21)
 class ViewUtilsLollipop {
 
     private static final int[] STATE_LIST_ANIM_ATTRS = new int[] {android.R.attr.stateListAnimator};
diff --git a/design/res/anim/design_fab_in.xml b/design/res/anim/design_fab_in.xml
deleted file mode 100644
index 294050f..0000000
--- a/design/res/anim/design_fab_in.xml
+++ /dev/null
@@ -1,30 +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.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha android:fromAlpha="0.0"
-           android:toAlpha="1.0"/>
-
-    <scale android:fromXScale="0.0"
-           android:fromYScale="0.0"
-           android:toXScale="1.0"
-           android:toYScale="1.0"
-           android:pivotX="50%"
-           android:pivotY="50%"/>
-
-</set>
diff --git a/design/res/anim/design_fab_out.xml b/design/res/anim/design_fab_out.xml
deleted file mode 100644
index 0f80a9a..0000000
--- a/design/res/anim/design_fab_out.xml
+++ /dev/null
@@ -1,30 +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.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha android:fromAlpha="1.0"
-           android:toAlpha="0.0"/>
-
-    <scale android:fromXScale="1.0"
-           android:fromYScale="1.0"
-           android:toXScale="0.0"
-           android:toYScale="0.0"
-           android:pivotX="50%"
-           android:pivotY="50%"/>
-
-</set>
diff --git a/design/res/layout/design_bottom_sheet_dialog.xml b/design/res/layout/design_bottom_sheet_dialog.xml
index 29212ed..28e023c 100644
--- a/design/res/layout/design_bottom_sheet_dialog.xml
+++ b/design/res/layout/design_bottom_sheet_dialog.xml
@@ -43,7 +43,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal|top"
-            android:clickable="true"
             app:layout_behavior="@string/bottom_sheet_behavior"/>
 
     </android.support.design.widget.CoordinatorLayout>
diff --git a/design/res/values-v21/styles.xml b/design/res/values-v21/styles.xml
index 9886c15..c692579 100644
--- a/design/res/values-v21/styles.xml
+++ b/design/res/values-v21/styles.xml
@@ -16,7 +16,9 @@
 -->
 <resources>
 
-    <style name="Widget.Design.AppBarLayout" parent="Base.Widget.Design.AppBarLayout">
+    <style name="Base.Widget.Design.AppBarLayout" parent="Base.V21.Widget.Design.AppBarLayout" />
+
+    <style name="Base.V21.Widget.Design.AppBarLayout" parent="Base.V14.Widget.Design.AppBarLayout">
         <item name="android:stateListAnimator">@animator/design_appbar_state_list_animator</item>
     </style>
 
diff --git a/design/res/values-v26/styles.xml b/design/res/values-v26/styles.xml
new file mode 100644
index 0000000..f20a058
--- /dev/null
+++ b/design/res/values-v26/styles.xml
@@ -0,0 +1,27 @@
+<?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="Base.Widget.Design.AppBarLayout" parent="Base.V26.Widget.Design.AppBarLayout" />
+
+    <style name="Base.V26.Widget.Design.AppBarLayout" parent="Base.V21.Widget.Design.AppBarLayout">
+        <item name="android:keyboardNavigationCluster">true</item>
+        <item name="android:touchscreenBlocksFocus">true</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index 41e0c0f..b378849 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -284,6 +284,8 @@
         <!-- The initial expanded state for the AppBarLayout. This only takes effect when this
              view is a direct child of a CoordinatorLayout. -->
         <attr name="expanded" format="boolean" />
+        <attr name="android:keyboardNavigationCluster" />
+        <attr name="android:touchscreenBlocksFocus" />
     </declare-styleable>
 
     <declare-styleable name="AppBarLayoutStates">
@@ -460,7 +462,8 @@
         <attr name="bottomSheetStyle" format="reference" />
 
         <!-- Text color used to indicate an error has occurred. -->
-        <attr name="textColorError" format="color" />
+        <!-- {@deprecated Use colorError} -->
+        <attr name="textColorError" format="reference|color" />
     </declare-styleable>
 
     <declare-styleable name="BottomNavigationView">
diff --git a/design/res/values/colors.xml b/design/res/values/colors.xml
index eb18f05..88fe630 100644
--- a/design/res/values/colors.xml
+++ b/design/res/values/colors.xml
@@ -33,9 +33,6 @@
     <!-- Shadow color for the furthest pixels of a shadow -->
     <color name="design_fab_shadow_end_color">@android:color/transparent</color>
 
-    <color name="design_textinput_error_color_light">#FFD50000</color>
-    <color name="design_textinput_error_color_dark">#FFFF6E6E</color>
-
     <color name="design_snackbar_background_color">#323232</color>
 
     <color name="design_bottom_navigation_shadow_color">#14000000</color>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index 0e4b391..93fb7eb 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -108,7 +108,9 @@
         <item name="statusBarScrim">?attr/colorPrimaryDark</item>
     </style>
 
-    <style name="Base.Widget.Design.AppBarLayout" parent="android:Widget">
+    <style name="Base.Widget.Design.AppBarLayout" parent="Base.V14.Widget.Design.AppBarLayout" />
+
+    <style name="Base.V14.Widget.Design.AppBarLayout" parent="android:Widget">
         <item name="android:background">?attr/colorPrimary</item>
     </style>
 
diff --git a/design/res/values/themes.xml b/design/res/values/themes.xml
index 5768692..a7bd92d 100644
--- a/design/res/values/themes.xml
+++ b/design/res/values/themes.xml
@@ -29,11 +29,11 @@
     </style>
 
     <style name="Theme.Design" parent="Theme.AppCompat">
-        <item name="textColorError">@color/design_textinput_error_color_dark</item>
+        <item name="textColorError">?attr/colorError</item>
     </style>
 
     <style name="Theme.Design.Light" parent="Theme.AppCompat.Light">
-        <item name="textColorError">@color/design_textinput_error_color_light</item>
+        <item name="textColorError">?attr/colorError</item>
     </style>
 
     <style name="Theme.Design.NoActionBar">
diff --git a/design/src/.readme b/design/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/design/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/design/src/android/support/design/internal/BaselineLayout.java b/design/src/android/support/design/internal/BaselineLayout.java
index 23a04cd..0bfdf24 100644
--- a/design/src/android/support/design/internal/BaselineLayout.java
+++ b/design/src/android/support/design/internal/BaselineLayout.java
@@ -17,8 +17,6 @@
 package android.support.design.internal;
 
 import android.content.Context;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.ViewUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -44,6 +42,7 @@
         super(context, attrs, defStyleAttr);
     }
 
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int count = getChildCount();
         int maxWidth = 0;
@@ -66,8 +65,7 @@
             }
             maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
             maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(child));
+            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
         }
         if (maxChildBaseline != -1) {
             maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
@@ -77,8 +75,8 @@
         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
         setMeasuredDimension(
-                ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec,
+                View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+                View.resolveSizeAndState(maxHeight, heightMeasureSpec,
                         childState << MEASURED_HEIGHT_STATE_SHIFT));
     }
 
diff --git a/design/src/android/support/design/internal/BottomNavigationItemView.java b/design/src/android/support/design/internal/BottomNavigationItemView.java
index ad05e76..fe5e636 100644
--- a/design/src/android/support/design/internal/BottomNavigationItemView.java
+++ b/design/src/android/support/design/internal/BottomNavigationItemView.java
@@ -31,6 +31,7 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v7.view.menu.MenuItemImpl;
 import android.support.v7.view.menu.MenuView;
+import android.support.v7.widget.TooltipCompat;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -85,9 +86,9 @@
 
         LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item, this, true);
         setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
-        mIcon = (ImageView) findViewById(R.id.icon);
-        mSmallLabel = (TextView) findViewById(R.id.smallLabel);
-        mLargeLabel = (TextView) findViewById(R.id.largeLabel);
+        mIcon = findViewById(R.id.icon);
+        mSmallLabel = findViewById(R.id.smallLabel);
+        mLargeLabel = findViewById(R.id.largeLabel);
     }
 
     @Override
@@ -99,6 +100,8 @@
         setIcon(itemData.getIcon());
         setTitle(itemData.getTitle());
         setId(itemData.getItemId());
+        setContentDescription(itemData.getContentDescription());
+        TooltipCompat.setTooltipText(this, itemData.getTooltipText());
     }
 
     public void setItemPosition(int position) {
@@ -122,7 +125,6 @@
     public void setTitle(CharSequence title) {
         mSmallLabel.setText(title);
         mLargeLabel.setText(title);
-        setContentDescription(title);
     }
 
     @Override
@@ -132,10 +134,10 @@
 
     @Override
     public void setChecked(boolean checked) {
-        ViewCompat.setPivotX(mLargeLabel, mLargeLabel.getWidth() / 2);
-        ViewCompat.setPivotY(mLargeLabel, mLargeLabel.getBaseline());
-        ViewCompat.setPivotX(mSmallLabel, mSmallLabel.getWidth() / 2);
-        ViewCompat.setPivotY(mSmallLabel, mSmallLabel.getBaseline());
+        mLargeLabel.setPivotX(mLargeLabel.getWidth() / 2);
+        mLargeLabel.setPivotY(mLargeLabel.getBaseline());
+        mSmallLabel.setPivotX(mSmallLabel.getWidth() / 2);
+        mSmallLabel.setPivotY(mSmallLabel.getBaseline());
         if (mShiftingMode) {
             if (checked) {
                 LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
@@ -143,16 +145,16 @@
                 iconParams.topMargin = mDefaultMargin;
                 mIcon.setLayoutParams(iconParams);
                 mLargeLabel.setVisibility(VISIBLE);
-                ViewCompat.setScaleX(mLargeLabel, 1f);
-                ViewCompat.setScaleY(mLargeLabel, 1f);
+                mLargeLabel.setScaleX(1f);
+                mLargeLabel.setScaleY(1f);
             } else {
                 LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
                 iconParams.gravity = Gravity.CENTER;
                 iconParams.topMargin = mDefaultMargin;
                 mIcon.setLayoutParams(iconParams);
                 mLargeLabel.setVisibility(INVISIBLE);
-                ViewCompat.setScaleX(mLargeLabel, 0.5f);
-                ViewCompat.setScaleY(mLargeLabel, 0.5f);
+                mLargeLabel.setScaleX(0.5f);
+                mLargeLabel.setScaleY(0.5f);
             }
             mSmallLabel.setVisibility(INVISIBLE);
         } else {
@@ -164,10 +166,10 @@
                 mLargeLabel.setVisibility(VISIBLE);
                 mSmallLabel.setVisibility(INVISIBLE);
 
-                ViewCompat.setScaleX(mLargeLabel, 1f);
-                ViewCompat.setScaleY(mLargeLabel, 1f);
-                ViewCompat.setScaleX(mSmallLabel, mScaleUpFactor);
-                ViewCompat.setScaleY(mSmallLabel, mScaleUpFactor);
+                mLargeLabel.setScaleX(1f);
+                mLargeLabel.setScaleY(1f);
+                mSmallLabel.setScaleX(mScaleUpFactor);
+                mSmallLabel.setScaleY(mScaleUpFactor);
             } else {
                 LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
                 iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
@@ -176,10 +178,10 @@
                 mLargeLabel.setVisibility(INVISIBLE);
                 mSmallLabel.setVisibility(VISIBLE);
 
-                ViewCompat.setScaleX(mLargeLabel, mScaleDownFactor);
-                ViewCompat.setScaleY(mLargeLabel, mScaleDownFactor);
-                ViewCompat.setScaleX(mSmallLabel, 1f);
-                ViewCompat.setScaleY(mSmallLabel, 1f);
+                mLargeLabel.setScaleX(mScaleDownFactor);
+                mLargeLabel.setScaleY(mScaleDownFactor);
+                mSmallLabel.setScaleX(1f);
+                mSmallLabel.setScaleY(1f);
             }
         }
 
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index 316d271..bf33454 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -21,12 +21,15 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
-import android.os.Build;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.design.R;
+import android.support.transition.AutoTransition;
+import android.support.transition.TransitionManager;
+import android.support.transition.TransitionSet;
 import android.support.v4.util.Pools;
 import android.support.v4.view.ViewCompat;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.support.v7.view.menu.MenuBuilder;
 import android.support.v7.view.menu.MenuItemImpl;
 import android.support.v7.view.menu.MenuView;
@@ -40,12 +43,14 @@
  */
 @RestrictTo(LIBRARY_GROUP)
 public class BottomNavigationMenuView extends ViewGroup implements MenuView {
+    private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
+
+    private final TransitionSet mSet;
     private final int mInactiveItemMaxWidth;
     private final int mInactiveItemMinWidth;
     private final int mActiveItemMaxWidth;
     private final int mItemHeight;
     private final OnClickListener mOnClickListener;
-    private final BottomNavigationAnimationHelperBase mAnimationHelper;
     private final Pools.Pool<BottomNavigationItemView> mItemPool = new Pools.SynchronizedPool<>(5);
 
     private boolean mShiftingMode = true;
@@ -76,11 +81,11 @@
                 R.dimen.design_bottom_navigation_active_item_max_width);
         mItemHeight = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_height);
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            mAnimationHelper = new BottomNavigationAnimationHelperIcs();
-        } else {
-            mAnimationHelper = new BottomNavigationAnimationHelperBase();
-        }
+        mSet = new AutoTransition();
+        mSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
+        mSet.setDuration(ACTIVE_ANIMATION_DURATION_MS);
+        mSet.setInterpolator(new FastOutSlowInInterpolator());
+        mSet.addTransition(new TextScale());
 
         mOnClickListener = new OnClickListener() {
             @Override
@@ -147,9 +152,9 @@
             totalWidth += child.getMeasuredWidth();
         }
         setMeasuredDimension(
-                ViewCompat.resolveSizeAndState(totalWidth,
+                View.resolveSizeAndState(totalWidth,
                         MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), 0),
-                ViewCompat.resolveSizeAndState(mItemHeight, heightSpec, 0));
+                View.resolveSizeAndState(mItemHeight, heightSpec, 0));
     }
 
     @Override
@@ -300,7 +305,7 @@
         }
         if (previousSelectedId != mSelectedItemId) {
             // Note: this has to be called before BottomNavigationItemView#initialize().
-            mAnimationHelper.beginDelayedTransition(this);
+            TransitionManager.beginDelayedTransition(this, mSet);
         }
 
         for (int i = 0; i < menuSize; i++) {
diff --git a/design/src/android/support/design/internal/ForegroundLinearLayout.java b/design/src/android/support/design/internal/ForegroundLinearLayout.java
index c75603b..6d90503 100644
--- a/design/src/android/support/design/internal/ForegroundLinearLayout.java
+++ b/design/src/android/support/design/internal/ForegroundLinearLayout.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -84,6 +83,7 @@
      * @return foreground gravity.
      * @see #setForegroundGravity(int)
      */
+    @Override
     public int getForegroundGravity() {
         return mForegroundGravity;
     }
@@ -94,6 +94,7 @@
      * @param foregroundGravity See {@link android.view.Gravity}
      * @see #getForegroundGravity()
      */
+    @Override
     public void setForegroundGravity(int foregroundGravity) {
         if (mForegroundGravity != foregroundGravity) {
             if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
@@ -121,7 +122,6 @@
     }
 
     @RequiresApi(11)
-    @TargetApi(11)
     @Override
     public void jumpDrawablesToCurrentState() {
         super.jumpDrawablesToCurrentState();
@@ -146,6 +146,7 @@
      *
      * @param drawable The Drawable to be drawn on top of the children.
      */
+    @Override
     public void setForeground(Drawable drawable) {
         if (mForeground != drawable) {
             if (mForeground != null) {
@@ -179,6 +180,7 @@
      *
      * @return A Drawable or null if no foreground was set.
      */
+    @Override
     public Drawable getForeground() {
         return mForeground;
     }
@@ -227,7 +229,6 @@
     }
 
     @RequiresApi(21)
-    @TargetApi(21)
     @Override
     public void drawableHotspotChanged(float x, float y) {
         super.drawableHotspotChanged(x, y);
diff --git a/design/src/android/support/design/internal/NavigationMenuItemView.java b/design/src/android/support/design/internal/NavigationMenuItemView.java
index 8bece63..eea9e90 100644
--- a/design/src/android/support/design/internal/NavigationMenuItemView.java
+++ b/design/src/android/support/design/internal/NavigationMenuItemView.java
@@ -35,6 +35,7 @@
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.view.menu.MenuItemImpl;
 import android.support.v7.view.menu.MenuView;
+import android.support.v7.widget.TooltipCompat;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
@@ -95,7 +96,7 @@
         LayoutInflater.from(context).inflate(R.layout.design_navigation_menu_item, this, true);
         mIconSize = context.getResources().getDimensionPixelSize(
                 R.dimen.design_navigation_icon_size);
-        mTextView = (CheckedTextView) findViewById(R.id.design_menu_item_text);
+        mTextView = findViewById(R.id.design_menu_item_text);
         mTextView.setDuplicateParentStateEnabled(true);
         ViewCompat.setAccessibilityDelegate(mTextView, mAccessibilityDelegate);
     }
@@ -116,6 +117,8 @@
         setTitle(itemData.getTitle());
         setIcon(itemData.getIcon());
         setActionView(itemData.getActionView());
+        setContentDescription(itemData.getContentDescription());
+        TooltipCompat.setTooltipText(this, itemData.getTooltipText());
         adjustAppearance();
     }
 
diff --git a/design/src/android/support/design/internal/ParcelableSparseArray.java b/design/src/android/support/design/internal/ParcelableSparseArray.java
index 746ba00..b29000e 100644
--- a/design/src/android/support/design/internal/ParcelableSparseArray.java
+++ b/design/src/android/support/design/internal/ParcelableSparseArray.java
@@ -21,8 +21,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.util.SparseArray;
 
 /**
@@ -65,18 +63,21 @@
         parcel.writeParcelableArray(values, flags);
     }
 
-    public static final Parcelable.Creator<ParcelableSparseArray> CREATOR =
-            ParcelableCompat
-                    .newCreator(new ParcelableCompatCreatorCallbacks<ParcelableSparseArray>() {
-                        @Override
-                        public ParcelableSparseArray createFromParcel(Parcel source,
-                                ClassLoader loader) {
-                            return new ParcelableSparseArray(source, loader);
-                        }
+    public static final Creator<ParcelableSparseArray> CREATOR =
+            new ClassLoaderCreator<ParcelableSparseArray>() {
+                @Override
+                public ParcelableSparseArray createFromParcel(Parcel source, ClassLoader loader) {
+                    return new ParcelableSparseArray(source, loader);
+                }
 
-                        @Override
-                        public ParcelableSparseArray[] newArray(int size) {
-                            return new ParcelableSparseArray[size];
-                        }
-                    });
+                @Override
+                public ParcelableSparseArray createFromParcel(Parcel source) {
+                    return new ParcelableSparseArray(source, null);
+                }
+
+                @Override
+                public ParcelableSparseArray[] newArray(int size) {
+                    return new ParcelableSparseArray[size];
+                }
+            };
 }
diff --git a/design/src/android/support/design/internal/SnackbarContentLayout.java b/design/src/android/support/design/internal/SnackbarContentLayout.java
index dca1d6b..2abf012 100644
--- a/design/src/android/support/design/internal/SnackbarContentLayout.java
+++ b/design/src/android/support/design/internal/SnackbarContentLayout.java
@@ -57,8 +57,8 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mMessageView = (TextView) findViewById(R.id.snackbar_text);
-        mActionView = (Button) findViewById(R.id.snackbar_action);
+        mMessageView = findViewById(R.id.snackbar_text);
+        mActionView = findViewById(R.id.snackbar_action);
     }
 
     public TextView getMessageView() {
@@ -131,26 +131,26 @@
 
     @Override
     public void animateContentIn(int delay, int duration) {
-        ViewCompat.setAlpha(mMessageView, 0f);
-        ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
+        mMessageView.setAlpha(0f);
+        mMessageView.animate().alpha(1f).setDuration(duration)
                 .setStartDelay(delay).start();
 
         if (mActionView.getVisibility() == VISIBLE) {
-            ViewCompat.setAlpha(mActionView, 0f);
-            ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
+            mActionView.setAlpha(0f);
+            mActionView.animate().alpha(1f).setDuration(duration)
                     .setStartDelay(delay).start();
         }
     }
 
     @Override
     public void animateContentOut(int delay, int duration) {
-        ViewCompat.setAlpha(mMessageView, 1f);
-        ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
+        mMessageView.setAlpha(1f);
+        mMessageView.animate().alpha(0f).setDuration(duration)
                 .setStartDelay(delay).start();
 
         if (mActionView.getVisibility() == VISIBLE) {
-            ViewCompat.setAlpha(mActionView, 1f);
-            ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
+            mActionView.setAlpha(1f);
+            mActionView.animate().alpha(0f).setDuration(duration)
                     .setStartDelay(delay).start();
         }
     }
diff --git a/design/src/android/support/design/internal/TextScale.java b/design/src/android/support/design/internal/TextScale.java
new file mode 100644
index 0000000..06c9472
--- /dev/null
+++ b/design/src/android/support/design/internal/TextScale.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package android.support.design.internal;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.transition.Transition;
+import android.support.transition.TransitionValues;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Map;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(14)
+public class TextScale extends Transition {
+    private static final String PROPNAME_SCALE = "android:textscale:scale";
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        if (transitionValues.view instanceof TextView) {
+            TextView textview = (TextView) transitionValues.view;
+            transitionValues.values.put(PROPNAME_SCALE, textview.getScaleX());
+        }
+    }
+
+    @Override
+    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+            TransitionValues endValues) {
+        if (startValues == null || endValues == null || !(startValues.view instanceof TextView)
+                || !(endValues.view instanceof TextView)) {
+            return null;
+        }
+        final TextView view = (TextView) endValues.view;
+        Map<String, Object> startVals = startValues.values;
+        Map<String, Object> endVals = endValues.values;
+        final float startSize = startVals.get(PROPNAME_SCALE) != null ? (float) startVals.get(
+                PROPNAME_SCALE) : 1f;
+        final float endSize = endVals.get(PROPNAME_SCALE) != null ? (float) endVals.get(
+                PROPNAME_SCALE) : 1f;
+        if (startSize == endSize) {
+            return null;
+        }
+
+        ValueAnimator animator = ValueAnimator.ofFloat(startSize, endSize);
+
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                float animatedValue = (float) valueAnimator.getAnimatedValue();
+                view.setScaleX(animatedValue);
+                view.setScaleY(animatedValue);
+            }
+        });
+        return animator;
+    }
+}
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index 6215057..8304cd6 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -17,9 +17,8 @@
 package android.support.design.widget;
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.design.widget.ViewUtils.objectEquals;
 
-import android.annotation.TargetApi;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -33,8 +32,8 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.support.design.R;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
+import android.support.v4.math.MathUtils;
+import android.support.v4.util.ObjectsCompat;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
@@ -179,6 +178,18 @@
             ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
                     this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
         }
+        if (Build.VERSION.SDK_INT >= 26) {
+            // In O+, we have these values set in the style. Since there is no defStyleAttr for
+            // AppBarLayout at the AppCompat level, check for these attributes here.
+            if (a.hasValue(R.styleable.AppBarLayout_android_keyboardNavigationCluster)) {
+                this.setKeyboardNavigationCluster(a.getBoolean(
+                        R.styleable.AppBarLayout_android_keyboardNavigationCluster, false));
+            }
+            if (a.hasValue(R.styleable.AppBarLayout_android_touchscreenBlocksFocus)) {
+                this.setTouchscreenBlocksFocus(a.getBoolean(
+                        R.styleable.AppBarLayout_android_touchscreenBlocksFocus, false));
+            }
+        }
         a.recycle();
 
         ViewCompat.setOnApplyWindowInsetsListener(this,
@@ -591,7 +602,7 @@
         }
 
         // If our insets have changed, keep them and invalidate the scroll ranges...
-        if (!objectEquals(mLastInsets, newInsets)) {
+        if (!ObjectsCompat.equals(mLastInsets, newInsets)) {
             mLastInsets = newInsets;
             invalidateScrollRanges();
         }
@@ -696,14 +707,12 @@
         }
 
         @RequiresApi(19)
-        @TargetApi(19)
         public LayoutParams(LinearLayout.LayoutParams source) {
             // The copy constructor called here only exists on API 19+.
             super(source);
         }
 
         @RequiresApi(19)
-        @TargetApi(19)
         public LayoutParams(LayoutParams source) {
             // The copy constructor called here only exists on API 19+.
             super(source);
@@ -796,11 +805,7 @@
         }
 
         private int mOffsetDelta;
-
-        private boolean mSkipNestedPreScroll;
-        private boolean mWasNestedFlung;
-
-        private ValueAnimatorCompat mOffsetAnimator;
+        private ValueAnimator mOffsetAnimator;
 
         private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
         private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
@@ -817,7 +822,7 @@
 
         @Override
         public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
-                View directTargetChild, View target, int nestedScrollAxes) {
+                View directTargetChild, View target, int nestedScrollAxes, int type) {
             // Return true if we're nested scrolling vertically, and we have scrollable children
             // and the scrolling view is big enough to scroll
             final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
@@ -837,8 +842,8 @@
 
         @Override
         public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
-                View target, int dx, int dy, int[] consumed) {
-            if (dy != 0 && !mSkipNestedPreScroll) {
+                View target, int dx, int dy, int[] consumed, int type) {
+            if (dy != 0) {
                 int min, max;
                 if (dy < 0) {
                     // We're scrolling down
@@ -849,81 +854,36 @@
                     min = -child.getUpNestedPreScrollRange();
                     max = 0;
                 }
-                consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
+                if (min != max) {
+                    consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
+                }
             }
         }
 
         @Override
         public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
-                View target, int dxConsumed, int dyConsumed,
-                int dxUnconsumed, int dyUnconsumed) {
+                View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
+                int type) {
             if (dyUnconsumed < 0) {
                 // If the scrolling view is scrolling down but not consuming, it's probably be at
                 // the top of it's content
                 scroll(coordinatorLayout, child, dyUnconsumed,
                         -child.getDownNestedScrollRange(), 0);
-                // Set the expanding flag so that onNestedPreScroll doesn't handle any events
-                mSkipNestedPreScroll = true;
-            } else {
-                // As we're no longer handling nested scrolls, reset the skip flag
-                mSkipNestedPreScroll = false;
             }
         }
 
         @Override
         public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,
-                View target) {
-            if (!mWasNestedFlung) {
+                View target, int type) {
+            if (type == ViewCompat.TYPE_TOUCH) {
                 // If we haven't been flung then let's see if the current view has been set to snap
                 snapToChildIfNeeded(coordinatorLayout, abl);
             }
 
-            // Reset the flags
-            mSkipNestedPreScroll = false;
-            mWasNestedFlung = false;
             // Keep a reference to the previous nested scrolling child
             mLastNestedScrollingChildRef = new WeakReference<>(target);
         }
 
-        @Override
-        public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,
-                final AppBarLayout child, View target, float velocityX, float velocityY,
-                boolean consumed) {
-            boolean flung = false;
-
-            if (!consumed) {
-                // It has been consumed so let's fling ourselves
-                flung = fling(coordinatorLayout, child, -child.getTotalScrollRange(),
-                        0, -velocityY);
-            } else {
-                // If we're scrolling up and the child also consumed the fling. We'll fake scroll
-                // up to our 'collapsed' offset
-                if (velocityY < 0) {
-                    // We're scrolling down
-                    final int targetScroll = -child.getTotalScrollRange()
-                            + child.getDownNestedPreScrollRange();
-                    if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
-                        // If we're currently not expanded more than the target scroll, we'll
-                        // animate a fling
-                        animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY);
-                        flung = true;
-                    }
-                } else {
-                    // We're scrolling up
-                    final int targetScroll = -child.getUpNestedPreScrollRange();
-                    if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
-                        // If we're currently not expanded less than the target scroll, we'll
-                        // animate a fling
-                        animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY);
-                        flung = true;
-                    }
-                }
-            }
-
-            mWasNestedFlung = flung;
-            return flung;
-        }
-
         /**
          * Set a callback to control any {@link AppBarLayout} dragging.
          *
@@ -960,13 +920,13 @@
             }
 
             if (mOffsetAnimator == null) {
-                mOffsetAnimator = ViewUtils.createAnimator();
+                mOffsetAnimator = new ValueAnimator();
                 mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
-                mOffsetAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+                mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                     @Override
-                    public void onAnimationUpdate(ValueAnimatorCompat animator) {
+                    public void onAnimationUpdate(ValueAnimator animation) {
                         setHeaderTopBottomOffset(coordinatorLayout, child,
-                                animator.getAnimatedIntValue());
+                                (int) animation.getAnimatedValue());
                     }
                 });
             } else {
@@ -1025,7 +985,7 @@
                             ? snapBottom
                             : snapTop;
                     animateOffsetTo(coordinatorLayout, abl,
-                            MathUtils.constrain(newOffset, -abl.getTotalScrollRange(), 0), 0);
+                            MathUtils.clamp(newOffset, -abl.getTotalScrollRange(), 0), 0);
                 }
             }
         }
@@ -1099,7 +1059,7 @@
             // We may have changed size, so let's constrain the top and bottom offset correctly,
             // just in case we're out of the bounds
             setTopAndBottomOffset(
-                    MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
+                    MathUtils.clamp(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
 
             // Update the AppBarLayout's drawable state for any elevation changes.
             // This is needed so that the elevation is set in the first layout, so that
@@ -1124,7 +1084,7 @@
                 // If we have a reference to a scrolling view, check it
                 final View scrollingView = mLastNestedScrollingChildRef.get();
                 return scrollingView != null && scrollingView.isShown()
-                        && !ViewCompat.canScrollVertically(scrollingView, -1);
+                        && !scrollingView.canScrollVertically(-1);
             } else {
                 // Otherwise we assume that the scrolling view hasn't been scrolled and can drag.
                 return true;
@@ -1156,7 +1116,7 @@
             if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
                 // If we have some scrolling range, and we're currently within the min and max
                 // offsets, calculate a new offset
-                newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
+                newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset);
                 if (curOffset != newOffset) {
                     final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
                             ? interpolateOffset(appBarLayout, newOffset)
@@ -1375,18 +1335,22 @@
                 dest.writeByte((byte) (firstVisibleChildAtMinimumHeight ? 1 : 0));
             }
 
-            public static final Parcelable.Creator<SavedState> CREATOR =
-                    ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
-                        @Override
-                        public SavedState createFromParcel(Parcel source, ClassLoader loader) {
-                            return new SavedState(source, loader);
-                        }
+            public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+                @Override
+                public SavedState createFromParcel(Parcel source, ClassLoader loader) {
+                    return new SavedState(source, loader);
+                }
 
-                        @Override
-                        public SavedState[] newArray(int size) {
-                            return new SavedState[size];
-                        }
-                    });
+                @Override
+                public SavedState createFromParcel(Parcel source) {
+                    return new SavedState(source, null);
+                }
+
+                @Override
+                public SavedState[] newArray(int size) {
+                    return new SavedState[size];
+                }
+            };
         }
     }
 
diff --git a/design/src/android/support/design/widget/BaseTransientBottomBar.java b/design/src/android/support/design/widget/BaseTransientBottomBar.java
index 9035f82..18c9ef9 100644
--- a/design/src/android/support/design/widget/BaseTransientBottomBar.java
+++ b/design/src/android/support/design/widget/BaseTransientBottomBar.java
@@ -19,6 +19,9 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 import static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Build;
@@ -31,7 +34,6 @@
 import android.support.annotation.RestrictTo;
 import android.support.design.R;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.WindowInsetsCompat;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -168,6 +170,12 @@
     static final int MSG_SHOW = 0;
     static final int MSG_DISMISS = 1;
 
+    // On JB/KK versions of the platform sometimes View.setTranslationY does not
+    // result in layout / draw pass, and CoordinatorLayout relies on a draw pass to
+    // happen to sync vertical positioning of all its child views
+    private static final boolean USE_OFFSET_API = (Build.VERSION.SDK_INT >= 16)
+            && (Build.VERSION.SDK_INT <= 19);
+
     static {
         sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
             @Override
@@ -486,27 +494,48 @@
     }
 
     void animateViewIn() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            ViewCompat.setTranslationY(mView, mView.getHeight());
-            ViewCompat.animate(mView)
-                    .translationY(0f)
-                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
-                    .setDuration(ANIMATION_DURATION)
-                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationStart(View view) {
-                            mContentViewCallback.animateContentIn(
-                                    ANIMATION_DURATION - ANIMATION_FADE_DURATION,
-                                    ANIMATION_FADE_DURATION);
-                        }
+        if (Build.VERSION.SDK_INT >= 12) {
+            final int viewHeight = mView.getHeight();
+            if (USE_OFFSET_API) {
+                ViewCompat.offsetTopAndBottom(mView, viewHeight);
+            } else {
+                mView.setTranslationY(viewHeight);
+            }
+            final ValueAnimator animator = new ValueAnimator();
+            animator.setIntValues(viewHeight, 0);
+            animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+            animator.setDuration(ANIMATION_DURATION);
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animator) {
+                    mContentViewCallback.animateContentIn(
+                            ANIMATION_DURATION - ANIMATION_FADE_DURATION,
+                            ANIMATION_FADE_DURATION);
+                }
 
-                        @Override
-                        public void onAnimationEnd(View view) {
-                            onViewShown();
-                        }
-                    }).start();
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    onViewShown();
+                }
+            });
+            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                private int mPreviousAnimatedIntValue = viewHeight;
+
+                @Override
+                public void onAnimationUpdate(ValueAnimator animator) {
+                    int currentAnimatedIntValue = (int) animator.getAnimatedValue();
+                    if (USE_OFFSET_API) {
+                        ViewCompat.offsetTopAndBottom(mView,
+                                currentAnimatedIntValue - mPreviousAnimatedIntValue);
+                    } else {
+                        mView.setTranslationY(currentAnimatedIntValue);
+                    }
+                    mPreviousAnimatedIntValue = currentAnimatedIntValue;
+                }
+            });
+            animator.start();
         } else {
-            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
+            final Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
                     R.anim.design_snackbar_in);
             anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
             anim.setDuration(ANIMATION_DURATION);
@@ -527,24 +556,40 @@
     }
 
     private void animateViewOut(final int event) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            ViewCompat.animate(mView)
-                    .translationY(mView.getHeight())
-                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
-                    .setDuration(ANIMATION_DURATION)
-                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationStart(View view) {
-                            mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
-                        }
+        if (Build.VERSION.SDK_INT >= 12) {
+            final ValueAnimator animator = new ValueAnimator();
+            animator.setIntValues(0, mView.getHeight());
+            animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+            animator.setDuration(ANIMATION_DURATION);
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animator) {
+                    mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
+                }
 
-                        @Override
-                        public void onAnimationEnd(View view) {
-                            onViewHidden(event);
-                        }
-                    }).start();
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    onViewHidden(event);
+                }
+            });
+            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                private int mPreviousAnimatedIntValue = 0;
+
+                @Override
+                public void onAnimationUpdate(ValueAnimator animator) {
+                    int currentAnimatedIntValue = (int) animator.getAnimatedValue();
+                    if (USE_OFFSET_API) {
+                        ViewCompat.offsetTopAndBottom(mView,
+                                currentAnimatedIntValue - mPreviousAnimatedIntValue);
+                    } else {
+                        mView.setTranslationY(currentAnimatedIntValue);
+                    }
+                    mPreviousAnimatedIntValue = currentAnimatedIntValue;
+                }
+            });
+            animator.start();
         } else {
-            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
+            final Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
                     R.anim.design_snackbar_out);
             anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
             anim.setDuration(ANIMATION_DURATION);
diff --git a/design/src/android/support/design/widget/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
index 01ed8de..61dba87 100644
--- a/design/src/android/support/design/widget/BottomNavigationView.java
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -31,8 +31,6 @@
 import android.support.design.internal.BottomNavigationMenuView;
 import android.support.design.internal.BottomNavigationPresenter;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.content.res.AppCompatResources;
@@ -459,17 +457,21 @@
             menuPresenterState = in.readBundle(loader);
         }
 
-        public static final Creator<SavedState> CREATOR =
-                ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 }
diff --git a/design/src/android/support/design/widget/BottomSheetBehavior.java b/design/src/android/support/design/widget/BottomSheetBehavior.java
index 4419d86..a28d3be 100644
--- a/design/src/android/support/design/widget/BottomSheetBehavior.java
+++ b/design/src/android/support/design/widget/BottomSheetBehavior.java
@@ -27,11 +27,8 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.support.design.R;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
+import android.support.v4.math.MathUtils;
 import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.ViewDragHelper;
 import android.util.AttributeSet;
@@ -260,7 +257,7 @@
             mIgnoreEvents = true;
             return false;
         }
-        int action = MotionEventCompat.getActionMasked(event);
+        int action = event.getActionMasked();
         // Record the velocity
         if (action == MotionEvent.ACTION_DOWN) {
             reset();
@@ -311,7 +308,7 @@
         if (!child.isShown()) {
             return false;
         }
-        int action = MotionEventCompat.getActionMasked(event);
+        int action = event.getActionMasked();
         if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
             return true;
         }
@@ -362,7 +359,7 @@
                 setStateInternal(STATE_DRAGGING);
             }
         } else if (dy < 0) { // Downward
-            if (!ViewCompat.canScrollVertically(target, -1)) {
+            if (!target.canScrollVertically(-1)) {
                 if (newTop <= mMaxOffset || mHideable) {
                     consumed[1] = dy;
                     ViewCompat.offsetTopAndBottom(child, -dy);
@@ -617,7 +614,7 @@
 
     private float getYVelocity() {
         mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-        return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId);
+        return mVelocityTracker.getYVelocity(mActivePointerId);
     }
 
     void startSettlingAnimation(View child, int state) {
@@ -631,9 +628,11 @@
         } else {
             throw new IllegalArgumentException("Illegal state argument: " + state);
         }
-        setStateInternal(STATE_SETTLING);
         if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
+            setStateInternal(STATE_SETTLING);
             ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
+        } else {
+            setStateInternal(state);
         }
     }
 
@@ -649,7 +648,7 @@
             }
             if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
                 View scroll = mNestedScrollingChildRef.get();
-                if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) {
+                if (scroll != null && scroll.canScrollVertically(-1)) {
                     // Let the content scroll up
                     return false;
                 }
@@ -703,7 +702,7 @@
 
         @Override
         public int clampViewPositionVertical(View child, int top, int dy) {
-            return MathUtils.constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
+            return MathUtils.clamp(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
         }
 
         @Override
@@ -786,18 +785,22 @@
             out.writeInt(state);
         }
 
-        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 
     /**
diff --git a/design/src/android/support/design/widget/BottomSheetDialog.java b/design/src/android/support/design/widget/BottomSheetDialog.java
index ca89b20..19b5782 100644
--- a/design/src/android/support/design/widget/BottomSheetDialog.java
+++ b/design/src/android/support/design/widget/BottomSheetDialog.java
@@ -29,6 +29,7 @@
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v7.app.AppCompatDialog;
 import android.util.TypedValue;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -171,6 +172,13 @@
                 return super.performAccessibilityAction(host, action, args);
             }
         });
+        bottomSheet.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                // Consume the event and prevent it from falling through
+                return true;
+            }
+        });
         return container;
     }
 
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 8d88bbb..a33cabc 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -27,7 +27,7 @@
 import android.graphics.Typeface;
 import android.os.Build;
 import android.support.annotation.ColorInt;
-import android.support.design.R;
+import android.support.v4.math.MathUtils;
 import android.support.v4.text.TextDirectionHeuristicsCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
@@ -271,14 +271,14 @@
     }
 
     void setCollapsedTypeface(Typeface typeface) {
-        if (mCollapsedTypeface != typeface) {
+        if (areTypefacesDifferent(mCollapsedTypeface, typeface)) {
             mCollapsedTypeface = typeface;
             recalculate();
         }
     }
 
     void setExpandedTypeface(Typeface typeface) {
-        if (mExpandedTypeface != typeface) {
+        if (areTypefacesDifferent(mExpandedTypeface, typeface)) {
             mExpandedTypeface = typeface;
             recalculate();
         }
@@ -305,7 +305,7 @@
      * A value of {@code 1.0} indicates that the layout is fully collapsed.
      */
     void setExpansionFraction(float fraction) {
-        fraction = MathUtils.constrain(fraction, 0f, 1f);
+        fraction = MathUtils.clamp(fraction, 0f, 1f);
 
         if (fraction != mExpandedFraction) {
             mExpandedFraction = fraction;
@@ -542,6 +542,10 @@
         ViewCompat.postInvalidateOnAnimation(mView);
     }
 
+    private boolean areTypefacesDifferent(Typeface first, Typeface second) {
+        return (first != null && !first.equals(second)) || (first == null && second != null);
+    }
+
     private void calculateUsingTextSize(final float textSize) {
         if (mText == null) return;
 
@@ -555,14 +559,14 @@
         if (isClose(textSize, mCollapsedTextSize)) {
             newTextSize = mCollapsedTextSize;
             mScale = 1f;
-            if (mCurrentTypeface != mCollapsedTypeface) {
+            if (areTypefacesDifferent(mCurrentTypeface, mCollapsedTypeface)) {
                 mCurrentTypeface = mCollapsedTypeface;
                 updateDrawText = true;
             }
             availableWidth = collapsedWidth;
         } else {
             newTextSize = mExpandedTextSize;
-            if (mCurrentTypeface != mExpandedTypeface) {
+            if (areTypefacesDifferent(mCurrentTypeface, mExpandedTypeface)) {
                 mCurrentTypeface = mExpandedTypeface;
                 updateDrawText = true;
             }
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 5f8de36..0051de9 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -17,10 +17,8 @@
 package android.support.design.widget;
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.design.widget.MathUtils.constrain;
-import static android.support.design.widget.ViewUtils.objectEquals;
 
-import android.annotation.TargetApi;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
@@ -41,6 +39,8 @@
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.math.MathUtils;
+import android.support.v4.util.ObjectsCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
@@ -125,7 +125,7 @@
     Drawable mStatusBarScrim;
     private int mScrimAlpha;
     private boolean mScrimsAreShown;
-    private ValueAnimatorCompat mScrimAnimator;
+    private ValueAnimator mScrimAnimator;
     private long mScrimAnimationDuration;
     private int mScrimVisibleHeightTrigger = -1;
 
@@ -270,7 +270,7 @@
         }
 
         // If our insets have changed, keep them and invalidate the scroll ranges...
-        if (!objectEquals(mLastInsets, newInsets)) {
+        if (!ObjectsCompat.equals(mLastInsets, newInsets)) {
             mLastInsets = newInsets;
             requestLayout();
         }
@@ -342,7 +342,7 @@
 
         if (mToolbarId != -1) {
             // If we have an ID set, try and find it and it's direct parent to us
-            mToolbar = (Toolbar) findViewById(mToolbarId);
+            mToolbar = findViewById(mToolbarId);
             if (mToolbar != null) {
                 mToolbarDirectChild = findDirectChild(mToolbar);
             }
@@ -408,6 +408,16 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         ensureToolbar();
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        final int mode = MeasureSpec.getMode(heightMeasureSpec);
+        final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
+        if (mode == MeasureSpec.UNSPECIFIED && topInset > 0) {
+            // If we have a top inset and we're set to wrap_content height we need to make sure
+            // we add the top inset to our height, therefore we re-measure
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                    getMeasuredHeight() + topInset, MeasureSpec.EXACTLY);
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
     }
 
     @Override
@@ -595,16 +605,16 @@
     private void animateScrim(int targetAlpha) {
         ensureToolbar();
         if (mScrimAnimator == null) {
-            mScrimAnimator = ViewUtils.createAnimator();
+            mScrimAnimator = new ValueAnimator();
             mScrimAnimator.setDuration(mScrimAnimationDuration);
             mScrimAnimator.setInterpolator(
                     targetAlpha > mScrimAlpha
                             ? AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR
                             : AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
-            mScrimAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+            mScrimAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
-                public void onAnimationUpdate(ValueAnimatorCompat animator) {
-                    setScrimAlpha(animator.getAnimatedIntValue());
+                public void onAnimationUpdate(ValueAnimator animator) {
+                    setScrimAlpha((int) animator.getAnimatedValue());
                 }
             });
         } else if (mScrimAnimator.isRunning()) {
@@ -1185,7 +1195,6 @@
         }
 
         @RequiresApi(19)
-        @TargetApi(19)
         public LayoutParams(FrameLayout.LayoutParams source) {
             // The copy constructor called here only exists on API 19+.
             super(source);
@@ -1271,8 +1280,8 @@
 
                 switch (lp.mCollapseMode) {
                     case LayoutParams.COLLAPSE_MODE_PIN:
-                        offsetHelper.setTopAndBottomOffset(
-                                constrain(-verticalOffset, 0, getMaxOffsetForPinChild(child)));
+                        offsetHelper.setTopAndBottomOffset(MathUtils.clamp(
+                                -verticalOffset, 0, getMaxOffsetForPinChild(child)));
                         break;
                     case LayoutParams.COLLAPSE_MODE_PARALLAX:
                         offsetHelper.setTopAndBottomOffset(
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index e0ec883..818e3a6 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -17,7 +17,6 @@
 package android.support.design.widget;
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.design.widget.ViewUtils.objectEquals;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -45,15 +44,17 @@
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
+import android.support.v4.math.MathUtils;
+import android.support.v4.util.ObjectsCompat;
 import android.support.v4.util.Pools;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.GravityCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.NestedScrollingParent;
+import android.support.v4.view.NestedScrollingParent2;
 import android.support.v4.view.NestedScrollingParentHelper;
 import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewCompat.NestedScrollType;
+import android.support.v4.view.ViewCompat.ScrollAxis;
 import android.support.v4.view.WindowInsetsCompat;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -106,7 +107,7 @@
  * {@link CoordinatorLayout.LayoutParams#dodgeInsetEdges} will be moved appropriately so that the
  * views do not overlap.</p>
  */
-public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent {
+public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent2 {
     static final String TAG = "CoordinatorLayout";
     static final String WIDGET_PACKAGE_NAME;
 
@@ -176,7 +177,6 @@
     private int[] mKeylines;
 
     private View mBehaviorTouchView;
-    private View mNestedScrollingDirectChild;
     private View mNestedScrollingTarget;
 
     private OnPreDrawListener mOnPreDrawListener;
@@ -214,7 +214,7 @@
             final float density = res.getDisplayMetrics().density;
             final int count = mKeylines.length;
             for (int i = 0; i < count; i++) {
-                mKeylines[i] *= density;
+                mKeylines[i] = (int) (mKeylines[i] * density);
             }
         }
         mStatusBarBackground = a.getDrawable(R.styleable.CoordinatorLayout_statusBarBackground);
@@ -351,7 +351,7 @@
     }
 
     final WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {
-        if (!objectEquals(mLastInsets, insets)) {
+        if (!ObjectsCompat.equals(mLastInsets, insets)) {
             mLastInsets = insets;
             mDrawStatusBarBackground = insets != null && insets.getSystemWindowInsetTop() > 0;
             setWillNotDraw(!mDrawStatusBarBackground && getBackground() == null);
@@ -421,7 +421,7 @@
 
         MotionEvent cancelEvent = null;
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
 
         final List<View> topmostChildList = mTempList1;
         getTopSortedChildren(topmostChildList);
@@ -489,7 +489,7 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         MotionEvent cancelEvent = null;
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
 
         // Make sure we reset in case we had missed a previous important event.
         if (action == MotionEvent.ACTION_DOWN) {
@@ -515,7 +515,7 @@
         boolean cancelSuper = false;
         MotionEvent cancelEvent = null;
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
 
         if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) {
             // Safe since performIntercept guarantees that
@@ -627,7 +627,8 @@
             }
             if (defaultBehavior != null) {
                 try {
-                    result.setBehavior(defaultBehavior.value().newInstance());
+                    result.setBehavior(
+                            defaultBehavior.value().getDeclaredConstructor().newInstance());
                 } catch (Exception e) {
                     Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
                             " could not be instantiated. Did you forget a default constructor?", e);
@@ -656,14 +657,13 @@
                     continue;
                 }
                 final View other = getChildAt(j);
-                final LayoutParams otherLp = getResolvedLayoutParams(other);
-                if (otherLp.dependsOn(this, other, view)) {
+                if (lp.dependsOn(this, view, other)) {
                     if (!mChildDag.contains(other)) {
                         // Make sure that the other node is added
                         mChildDag.addNode(other);
                     }
                     // Now add the dependency to the graph
-                    mChildDag.addEdge(view, other);
+                    mChildDag.addEdge(other, view);
                 }
             }
         }
@@ -792,14 +792,13 @@
 
             heightUsed = Math.max(heightUsed, heightPadding + child.getMeasuredHeight() +
                     lp.topMargin + lp.bottomMargin);
-            childState = ViewCompat.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(child));
+            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
         }
 
-        final int width = ViewCompat.resolveSizeAndState(widthUsed, widthMeasureSpec,
-                childState & ViewCompat.MEASURED_STATE_MASK);
-        final int height = ViewCompat.resolveSizeAndState(heightUsed, heightMeasureSpec,
-                childState << ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
+        final int width = View.resolveSizeAndState(widthUsed, widthMeasureSpec,
+                childState & View.MEASURED_STATE_MASK);
+        final int height = View.resolveSizeAndState(heightUsed, heightMeasureSpec,
+                childState << View.MEASURED_HEIGHT_STATE_SHIFT);
         setMeasuredDimension(width, height);
     }
 
@@ -1211,7 +1210,7 @@
                     mScrimPaint = new Paint();
                 }
                 mScrimPaint.setColor(lp.mBehavior.getScrimColor(this, child));
-                mScrimPaint.setAlpha(MathUtils.constrain(Math.round(255 * scrimAlpha), 0, 255));
+                mScrimPaint.setAlpha(MathUtils.clamp(Math.round(255 * scrimAlpha), 0, 255));
 
                 final int saved = canvas.save();
                 if (child.isOpaque()) {
@@ -1371,9 +1370,9 @@
         if (behavior != null && behavior.getInsetDodgeRect(this, child, dodgeRect)) {
             // Make sure that the rect is within the view's bounds
             if (!bounds.contains(dodgeRect)) {
-                    throw new IllegalArgumentException("Rect should be within the child's bounds."
-                            + " Rect:" + dodgeRect.toShortString()
-                            + " | Bounds:" + bounds.toShortString());
+                throw new IllegalArgumentException("Rect should be within the child's bounds."
+                        + " Rect:" + dodgeRect.toShortString()
+                        + " | Bounds:" + bounds.toShortString());
             }
         } else {
             dodgeRect.set(bounds);
@@ -1701,6 +1700,11 @@
 
     @Override
     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public boolean onStartNestedScroll(View child, View target, int axes, int type) {
         boolean handled = false;
 
         final int childCount = getChildCount();
@@ -1713,13 +1717,12 @@
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
             final Behavior viewBehavior = lp.getBehavior();
             if (viewBehavior != null) {
-                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,
-                        nestedScrollAxes);
+                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child,
+                        target, axes, type);
                 handled |= accepted;
-
-                lp.acceptNestedScroll(accepted);
+                lp.setNestedScrollAccepted(type, accepted);
             } else {
-                lp.acceptNestedScroll(false);
+                lp.setNestedScrollAccepted(type, false);
             }
         }
         return handled;
@@ -1727,52 +1730,67 @@
 
     @Override
     public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
-        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
-        mNestedScrollingDirectChild = child;
+        onNestedScrollAccepted(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes, int type) {
+        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes, type);
         mNestedScrollingTarget = target;
 
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             final View view = getChildAt(i);
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted()) {
+            if (!lp.isNestedScrollAccepted(type)) {
                 continue;
             }
 
             final Behavior viewBehavior = lp.getBehavior();
             if (viewBehavior != null) {
-                viewBehavior.onNestedScrollAccepted(this, view, child, target, nestedScrollAxes);
+                viewBehavior.onNestedScrollAccepted(this, view, child, target,
+                        nestedScrollAxes, type);
             }
         }
     }
 
     @Override
     public void onStopNestedScroll(View target) {
-        mNestedScrollingParentHelper.onStopNestedScroll(target);
+        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onStopNestedScroll(View target, int type) {
+        mNestedScrollingParentHelper.onStopNestedScroll(target, type);
 
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             final View view = getChildAt(i);
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted()) {
+            if (!lp.isNestedScrollAccepted(type)) {
                 continue;
             }
 
             final Behavior viewBehavior = lp.getBehavior();
             if (viewBehavior != null) {
-                viewBehavior.onStopNestedScroll(this, view, target);
+                viewBehavior.onStopNestedScroll(this, view, target, type);
             }
-            lp.resetNestedScroll();
+            lp.resetNestedScroll(type);
             lp.resetChangedAfterNestedScroll();
         }
-
-        mNestedScrollingDirectChild = null;
         mNestedScrollingTarget = null;
     }
 
     @Override
     public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
             int dxUnconsumed, int dyUnconsumed) {
+        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, int type) {
         final int childCount = getChildCount();
         boolean accepted = false;
 
@@ -1784,14 +1802,14 @@
             }
 
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted()) {
+            if (!lp.isNestedScrollAccepted(type)) {
                 continue;
             }
 
             final Behavior viewBehavior = lp.getBehavior();
             if (viewBehavior != null) {
                 viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed,
-                        dxUnconsumed, dyUnconsumed);
+                        dxUnconsumed, dyUnconsumed, type);
                 accepted = true;
             }
         }
@@ -1803,6 +1821,11 @@
 
     @Override
     public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int  type) {
         int xConsumed = 0;
         int yConsumed = 0;
         boolean accepted = false;
@@ -1816,14 +1839,14 @@
             }
 
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted()) {
+            if (!lp.isNestedScrollAccepted(type)) {
                 continue;
             }
 
             final Behavior viewBehavior = lp.getBehavior();
             if (viewBehavior != null) {
                 mTempIntPair[0] = mTempIntPair[1] = 0;
-                viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair);
+                viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair, type);
 
                 xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
                         : Math.min(xConsumed, mTempIntPair[0]);
@@ -1855,7 +1878,7 @@
             }
 
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted()) {
+            if (!lp.isNestedScrollAccepted(ViewCompat.TYPE_TOUCH)) {
                 continue;
             }
 
@@ -1884,7 +1907,7 @@
             }
 
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted()) {
+            if (!lp.isNestedScrollAccepted(ViewCompat.TYPE_TOUCH)) {
                 continue;
             }
 
@@ -2165,15 +2188,6 @@
         }
 
         /**
-         * @deprecated this method is not called anymore. You can safely remove all usages
-         * and implementations. This method will be removed in a future release.
-         */
-        @Deprecated
-        public boolean isDirty(CoordinatorLayout parent, V child) {
-            return false;
-        }
-
-        /**
          * Called when the parent CoordinatorLayout is about to measure the given child view.
          *
          * <p>This method can be used to perform custom or modified measurement of a child view
@@ -2253,6 +2267,17 @@
             return lp.mBehaviorTag;
         }
 
+        /**
+         * @deprecated You should now override
+         * {@link #onStartNestedScroll(CoordinatorLayout, View, View, View, int, int)}. This
+         * method will still continue to be called if the type is {@link ViewCompat#TYPE_TOUCH}.
+         */
+        @Deprecated
+        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
+                @ScrollAxis int axes) {
+            return false;
+        }
 
         /**
          * Called when a descendant of the CoordinatorLayout attempts to initiate a nested scroll.
@@ -2268,19 +2293,37 @@
          * @param directTargetChild the child view of the CoordinatorLayout that either is or
          *                          contains the target of the nested scroll operation
          * @param target the descendant view of the CoordinatorLayout initiating the nested scroll
-         * @param nestedScrollAxes the axes that this nested scroll applies to. See
+         * @param axes the axes that this nested scroll applies to. See
          *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
          *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
+         * @param type the type of input which cause this scroll event
          * @return true if the Behavior wishes to accept this nested scroll
          *
-         * @see NestedScrollingParent#onStartNestedScroll(View, View, int)
+         * @see NestedScrollingParent2#onStartNestedScroll(View, View, int, int)
          */
-        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
-                V child, View directTargetChild, View target, int nestedScrollAxes) {
+        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
+                @ScrollAxis int axes, @NestedScrollType int type) {
+            if (type == ViewCompat.TYPE_TOUCH) {
+                return onStartNestedScroll(coordinatorLayout, child, directTargetChild,
+                        target, axes);
+            }
             return false;
         }
 
         /**
+         * @deprecated You should now override
+         * {@link #onNestedScrollAccepted(CoordinatorLayout, View, View, View, int, int)}. This
+         * method will still continue to be called if the type is {@link ViewCompat#TYPE_TOUCH}.
+         */
+        @Deprecated
+        public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
+                @ScrollAxis int axes) {
+            // Do nothing
+        }
+
+        /**
          * Called when a nested scroll has been accepted by the CoordinatorLayout.
          *
          * <p>Any Behavior associated with any direct child of the CoordinatorLayout may elect
@@ -2294,14 +2337,30 @@
          * @param directTargetChild the child view of the CoordinatorLayout that either is or
          *                          contains the target of the nested scroll operation
          * @param target the descendant view of the CoordinatorLayout initiating the nested scroll
-         * @param nestedScrollAxes the axes that this nested scroll applies to. See
+         * @param axes the axes that this nested scroll applies to. See
          *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
          *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
+         * @param type the type of input which cause this scroll event
          *
-         * @see NestedScrollingParent#onNestedScrollAccepted(View, View, int)
+         * @see NestedScrollingParent2#onNestedScrollAccepted(View, View, int, int)
          */
-        public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,
-                View directTargetChild, View target, int nestedScrollAxes) {
+        public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
+                @ScrollAxis int axes, @NestedScrollType int type) {
+            if (type == ViewCompat.TYPE_TOUCH) {
+                onNestedScrollAccepted(coordinatorLayout, child, directTargetChild,
+                        target, axes);
+            }
+        }
+
+        /**
+         * @deprecated You should now override
+         * {@link #onStopNestedScroll(CoordinatorLayout, View, View, int)}. This method will still
+         * continue to be called if the type is {@link ViewCompat#TYPE_TOUCH}.
+         */
+        @Deprecated
+        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View target) {
             // Do nothing
         }
 
@@ -2322,10 +2381,27 @@
          * @param child the child view of the CoordinatorLayout this Behavior is associated with
          * @param target the descendant view of the CoordinatorLayout that initiated
          *               the nested scroll
+         * @param type the type of input which cause this scroll event
          *
-         * @see NestedScrollingParent#onStopNestedScroll(View)
+         * @see NestedScrollingParent2#onStopNestedScroll(View, int)
          */
-        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
+        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View target, @NestedScrollType int type) {
+            if (type == ViewCompat.TYPE_TOUCH) {
+                onStopNestedScroll(coordinatorLayout, child, target);
+            }
+        }
+
+        /**
+         * @deprecated You should now override
+         * {@link #onNestedScroll(CoordinatorLayout, View, View, int, int, int, int, int)}.
+         * This method will still continue to be called if the type is
+         * {@link ViewCompat#TYPE_TOUCH}.
+         */
+        @Deprecated
+        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child,
+                @NonNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed) {
             // Do nothing
         }
 
@@ -2354,11 +2430,28 @@
          *                     operation, but requested by the user
          * @param dyUnconsumed vertical pixels not consumed by the target's own scrolling operation,
          *                     but requested by the user
+         * @param type the type of input which cause this scroll event
          *
-         * @see NestedScrollingParent#onNestedScroll(View, int, int, int, int)
+         * @see NestedScrollingParent2#onNestedScroll(View, int, int, int, int, int)
          */
-        public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,
-                int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child,
+                @NonNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type) {
+            if (type == ViewCompat.TYPE_TOUCH) {
+                onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
+                        dxUnconsumed, dyUnconsumed);
+            }
+        }
+
+        /**
+         * @deprecated You should now override
+         * {@link #onNestedPreScroll(CoordinatorLayout, View, View, int, int, int[], int)}.
+         * This method will still continue to be called if the type is
+         * {@link ViewCompat#TYPE_TOUCH}.
+         */
+        @Deprecated
+        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed) {
             // Do nothing
         }
 
@@ -2387,12 +2480,16 @@
          * @param consumed out parameter. consumed[0] should be set to the distance of dx that
          *                 was consumed, consumed[1] should be set to the distance of dy that
          *                 was consumed
+         * @param type the type of input which cause this scroll event
          *
-         * @see NestedScrollingParent#onNestedPreScroll(View, int, int, int[])
+         * @see NestedScrollingParent2#onNestedPreScroll(View, int, int, int[], int)
          */
-        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
-                int dx, int dy, int[] consumed) {
-            // Do nothing
+        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed,
+                @NestedScrollType int type) {
+            if (type == ViewCompat.TYPE_TOUCH) {
+                onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
+            }
         }
 
         /**
@@ -2422,8 +2519,9 @@
          *
          * @see NestedScrollingParent#onNestedFling(View, float, float, boolean)
          */
-        public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target,
-                float velocityX, float velocityY, boolean consumed) {
+        public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View target, float velocityX, float velocityY,
+                boolean consumed) {
             return false;
         }
 
@@ -2450,8 +2548,8 @@
          *
          * @see NestedScrollingParent#onNestedPreFling(View, float, float)
          */
-        public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
-                float velocityX, float velocityY) {
+        public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout,
+                @NonNull V child, @NonNull View target, float velocityX, float velocityY) {
             return false;
         }
 
@@ -2614,7 +2712,8 @@
         View mAnchorDirectChild;
 
         private boolean mDidBlockInteraction;
-        private boolean mDidAcceptNestedScroll;
+        private boolean mDidAcceptNestedScrollTouch;
+        private boolean mDidAcceptNestedScrollNonTouch;
         private boolean mDidChangeAfterNestedScroll;
 
         final Rect mLastChildRect = new Rect();
@@ -2805,16 +2904,29 @@
             mDidBlockInteraction = false;
         }
 
-        void resetNestedScroll() {
-            mDidAcceptNestedScroll = false;
+        void resetNestedScroll(int type) {
+            setNestedScrollAccepted(type, false);
         }
 
-        void acceptNestedScroll(boolean accept) {
-            mDidAcceptNestedScroll = accept;
+        void setNestedScrollAccepted(int type, boolean accept) {
+            switch (type) {
+                case ViewCompat.TYPE_TOUCH:
+                    mDidAcceptNestedScrollTouch = accept;
+                    break;
+                case ViewCompat.TYPE_NON_TOUCH:
+                    mDidAcceptNestedScrollNonTouch = accept;
+                    break;
+            }
         }
 
-        boolean isNestedScrollAccepted() {
-            return mDidAcceptNestedScroll;
+        boolean isNestedScrollAccepted(int type) {
+            switch (type) {
+                case ViewCompat.TYPE_TOUCH:
+                    return mDidAcceptNestedScrollTouch;
+                case ViewCompat.TYPE_NON_TOUCH:
+                    return mDidAcceptNestedScrollNonTouch;
+            }
+            return false;
         }
 
         boolean getChangedAfterNestedScroll() {
@@ -3108,17 +3220,22 @@
 
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
-            @Override
-            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                return new SavedState(in, loader);
-            }
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new ClassLoaderCreator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                        return new SavedState(in, loader);
+                    }
 
-            @Override
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        });
+                    @Override
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in, null);
+                    }
+
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
     }
 }
diff --git a/design/src/android/support/design/widget/DirectedAcyclicGraph.java b/design/src/android/support/design/widget/DirectedAcyclicGraph.java
index 189daf4..85a32cd 100644
--- a/design/src/android/support/design/widget/DirectedAcyclicGraph.java
+++ b/design/src/android/support/design/widget/DirectedAcyclicGraph.java
@@ -97,7 +97,7 @@
      * @return a list containing any outgoing edges, or null if there are none.
      */
     @Nullable
-    List getOutgoingEdges(@NonNull T node) {
+    List<T> getOutgoingEdges(@NonNull T node) {
         ArrayList<T> result = null;
         for (int i = 0, size = mGraph.size(); i < size; i++) {
             ArrayList<T> edges = mGraph.valueAt(i);
diff --git a/design/src/android/support/design/widget/DrawableUtils.java b/design/src/android/support/design/widget/DrawableUtils.java
index 1e2e6cf..df1c04b 100644
--- a/design/src/android/support/design/widget/DrawableUtils.java
+++ b/design/src/android/support/design/widget/DrawableUtils.java
@@ -18,10 +18,8 @@
 
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
-import android.os.Build;
 import android.util.Log;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
 /**
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index 1597386..b938836 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -36,7 +35,6 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.design.R;
 import android.support.design.widget.FloatingActionButtonImpl.InternalVisibilityChangedListener;
-import android.support.v4.content.res.ConfigurationHelper;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.AppCompatImageHelper;
 import android.util.AttributeSet;
@@ -250,6 +248,7 @@
      *
      * @param tint the tint to apply, may be {@code null} to clear tint
      */
+    @Override
     public void setBackgroundTintList(@Nullable ColorStateList tint) {
         if (mBackgroundTint != tint) {
             mBackgroundTint = tint;
@@ -279,6 +278,7 @@
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
      */
+    @Override
     public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
         if (mBackgroundTintMode != tintMode) {
             mBackgroundTintMode = tintMode;
@@ -439,8 +439,8 @@
         switch (size) {
             case SIZE_AUTO:
                 // If we're set to auto, grab the size from resources and refresh
-                final int width = ConfigurationHelper.getScreenWidthDp(res);
-                final int height = ConfigurationHelper.getScreenHeightDp(res);
+                final int width = res.getConfiguration().screenWidthDp;
+                final int height = res.getConfiguration().screenHeightDp;
                 return Math.max(width, height) < AUTO_MINI_LARGEST_SCREEN_WIDTH
                         ? getSizeDimension(SIZE_MINI)
                         : getSizeDimension(SIZE_NORMAL);
@@ -470,7 +470,6 @@
         getImpl().onDrawableStateChanged(getDrawableState());
     }
 
-    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
     @Override
     public void jumpDrawablesToCurrentState() {
         super.jumpDrawablesToCurrentState();
@@ -797,16 +796,10 @@
     }
 
     private FloatingActionButtonImpl createImpl() {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk >= 21) {
-            return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl(),
-                    ViewUtils.DEFAULT_ANIMATOR_CREATOR);
-        } else if (sdk >= 14) {
-            return new FloatingActionButtonIcs(this, new ShadowDelegateImpl(),
-                    ViewUtils.DEFAULT_ANIMATOR_CREATOR);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl());
         } else {
-            return new FloatingActionButtonGingerbread(this, new ShadowDelegateImpl(),
-                    ViewUtils.DEFAULT_ANIMATOR_CREATOR);
+            return new FloatingActionButtonImpl(this, new ShadowDelegateImpl());
         }
     }
 
diff --git a/design/src/android/support/design/widget/HeaderBehavior.java b/design/src/android/support/design/widget/HeaderBehavior.java
index 5e555de..a5d0edf 100644
--- a/design/src/android/support/design/widget/HeaderBehavior.java
+++ b/design/src/android/support/design/widget/HeaderBehavior.java
@@ -18,15 +18,14 @@
 
 import android.content.Context;
 import android.support.design.widget.CoordinatorLayout.Behavior;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
+import android.support.v4.math.MathUtils;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.ScrollerCompat;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.widget.OverScroller;
 
 /**
  * The {@link Behavior} for a view that sits vertically above scrolling a view.
@@ -37,7 +36,7 @@
     private static final int INVALID_POINTER = -1;
 
     private Runnable mFlingRunnable;
-    ScrollerCompat mScroller;
+    OverScroller mScroller;
 
     private boolean mIsBeingDragged;
     private int mActivePointerId = INVALID_POINTER;
@@ -64,7 +63,7 @@
             return true;
         }
 
-        switch (MotionEventCompat.getActionMasked(ev)) {
+        switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 mIsBeingDragged = false;
                 final int x = (int) ev.getX();
@@ -122,7 +121,7 @@
             mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();
         }
 
-        switch (MotionEventCompat.getActionMasked(ev)) {
+        switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 final int x = (int) ev.getX();
                 final int y = (int) ev.getY();
@@ -167,8 +166,7 @@
                 if (mVelocityTracker != null) {
                     mVelocityTracker.addMovement(ev);
                     mVelocityTracker.computeCurrentVelocity(1000);
-                    float yvel = VelocityTrackerCompat.getYVelocity(mVelocityTracker,
-                            mActivePointerId);
+                    float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
                     fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel);
                 }
                 // $FALLTHROUGH
@@ -203,7 +201,7 @@
         if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
             // If we have some scrolling range, and we're currently within the min and max
             // offsets, calculate a new offset
-            newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
+            newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset);
 
             if (curOffset != newOffset) {
                 setTopAndBottomOffset(newOffset);
@@ -233,7 +231,7 @@
         }
 
         if (mScroller == null) {
-            mScroller = ScrollerCompat.create(layout.getContext());
+            mScroller = new OverScroller(layout.getContext());
         }
 
         mScroller.fling(
diff --git a/design/src/android/support/design/widget/HeaderScrollingViewBehavior.java b/design/src/android/support/design/widget/HeaderScrollingViewBehavior.java
index d1bec2d..81ddde5 100644
--- a/design/src/android/support/design/widget/HeaderScrollingViewBehavior.java
+++ b/design/src/android/support/design/widget/HeaderScrollingViewBehavior.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.support.design.widget.CoordinatorLayout.Behavior;
+import android.support.v4.math.MathUtils;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
@@ -142,7 +143,7 @@
     }
 
     final int getOverlapPixelsForOffset(final View header) {
-        return mOverlayTop == 0 ? 0 : MathUtils.constrain(
+        return mOverlayTop == 0 ? 0 : MathUtils.clamp(
                 (int) (getOverlapRatioForOffset(header) * mOverlayTop), 0, mOverlayTop);
     }
 
diff --git a/design/src/android/support/design/widget/NavigationView.java b/design/src/android/support/design/widget/NavigationView.java
index 044f096..8fc8c76 100644
--- a/design/src/android/support/design/widget/NavigationView.java
+++ b/design/src/android/support/design/widget/NavigationView.java
@@ -36,8 +36,6 @@
 import android.support.design.internal.NavigationMenuPresenter;
 import android.support.design.internal.ScrimInsetsFrameLayout;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
@@ -475,18 +473,22 @@
             dest.writeBundle(menuState);
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
             @Override
-            public SavedState createFromParcel(Parcel parcel, ClassLoader loader) {
-                return new SavedState(parcel, loader);
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
+
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
             }
 
             @Override
             public SavedState[] newArray(int size) {
                 return new SavedState[size];
             }
-        });
+        };
     }
 
 }
diff --git a/design/src/android/support/design/widget/SwipeDismissBehavior.java b/design/src/android/support/design/widget/SwipeDismissBehavior.java
index 1bf3d5a..d857334 100644
--- a/design/src/android/support/design/widget/SwipeDismissBehavior.java
+++ b/design/src/android/support/design/widget/SwipeDismissBehavior.java
@@ -21,7 +21,6 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.ViewDragHelper;
 import android.view.MotionEvent;
@@ -174,7 +173,7 @@
     public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
         boolean dispatchEventToHelper = mInterceptingEvents;
 
-        switch (MotionEventCompat.getActionMasked(event)) {
+        switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mInterceptingEvents = parent.isPointInChildBounds(child,
                         (int) event.getX(), (int) event.getY());
@@ -345,13 +344,13 @@
                     + child.getWidth() * mAlphaEndSwipeDistance;
 
             if (left <= startAlphaDistance) {
-                ViewCompat.setAlpha(child, 1f);
+                child.setAlpha(1f);
             } else if (left >= endAlphaDistance) {
-                ViewCompat.setAlpha(child, 0f);
+                child.setAlpha(0f);
             } else {
                 // We're between the start and end distances
                 final float distance = fraction(startAlphaDistance, endAlphaDistance, left);
-                ViewCompat.setAlpha(child, clamp(0f, 1f - distance, 1f));
+                child.setAlpha(clamp(0f, 1f - distance, 1f));
             }
         }
     };
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index d3a8dd1..9b81465 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -21,7 +21,9 @@
 import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
 import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
 
-import android.annotation.TargetApi;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -29,7 +31,6 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.annotation.ColorInt;
@@ -50,6 +51,7 @@
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.content.res.AppCompatResources;
+import android.support.v7.widget.TooltipCompat;
 import android.text.Layout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -66,7 +68,6 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -271,7 +272,7 @@
     private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>();
     private OnTabSelectedListener mCurrentVpSelectedListener;
 
-    private ValueAnimatorCompat mScrollAnimator;
+    private ValueAnimator mScrollAnimator;
 
     ViewPager mViewPager;
     private PagerAdapter mPagerAdapter;
@@ -1096,19 +1097,19 @@
 
     private void ensureScrollAnimator() {
         if (mScrollAnimator == null) {
-            mScrollAnimator = ViewUtils.createAnimator();
+            mScrollAnimator = new ValueAnimator();
             mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
             mScrollAnimator.setDuration(ANIMATION_DURATION);
-            mScrollAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+            mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
-                public void onAnimationUpdate(ValueAnimatorCompat animator) {
-                    scrollTo(animator.getAnimatedIntValue(), 0);
+                public void onAnimationUpdate(ValueAnimator animator) {
+                    scrollTo((int) animator.getAnimatedValue(), 0);
                 }
             });
         }
     }
 
-    void setScrollAnimatorListener(ValueAnimatorCompat.AnimatorListener listener) {
+    void setScrollAnimatorListener(Animator.AnimatorListener listener) {
         ensureScrollAnimator();
         mScrollAnimator.addListener(listener);
     }
@@ -1499,7 +1500,7 @@
         }
     }
 
-    class TabView extends LinearLayout implements OnLongClickListener {
+    class TabView extends LinearLayout {
         private Tab mTab;
         private TextView mTextView;
         private ImageView mIconView;
@@ -1564,7 +1565,6 @@
             }
         }
 
-        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
         @Override
         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
             super.onInitializeAccessibilityEvent(event);
@@ -1572,7 +1572,6 @@
             event.setClassName(ActionBar.Tab.class.getName());
         }
 
-        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
         @Override
         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
             super.onInitializeAccessibilityNodeInfo(info);
@@ -1767,44 +1766,7 @@
                     iconView.requestLayout();
                 }
             }
-
-            if (!hasText && !TextUtils.isEmpty(contentDesc)) {
-                setOnLongClickListener(this);
-            } else {
-                setOnLongClickListener(null);
-                setLongClickable(false);
-            }
-        }
-
-        @Override
-        public boolean onLongClick(final View v) {
-            final int[] screenPos = new int[2];
-            final Rect displayFrame = new Rect();
-            getLocationOnScreen(screenPos);
-            getWindowVisibleDisplayFrame(displayFrame);
-
-            final Context context = getContext();
-            final int width = getWidth();
-            final int height = getHeight();
-            final int midy = screenPos[1] + height / 2;
-            int referenceX = screenPos[0] + width / 2;
-            if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
-                final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-                referenceX = screenWidth - referenceX; // mirror
-            }
-
-            Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
-                    Toast.LENGTH_SHORT);
-            if (midy < displayFrame.height()) {
-                // Show below the tab view
-                cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX,
-                        screenPos[1] + height - displayFrame.top);
-            } else {
-                // Show along the bottom center
-                cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
-            }
-            cheatSheet.show();
-            return true;
+            TooltipCompat.setTooltipText(this, hasText ? null : contentDesc);
         }
 
         public Tab getTab() {
@@ -1826,10 +1788,12 @@
         int mSelectedPosition = -1;
         float mSelectionOffset;
 
+        private int mLayoutDirection = -1;
+
         private int mIndicatorLeft = -1;
         private int mIndicatorRight = -1;
 
-        private ValueAnimatorCompat mIndicatorAnimator;
+        private ValueAnimator mIndicatorAnimator;
 
         SlidingTabStrip(Context context) {
             super(context);
@@ -1876,6 +1840,21 @@
         }
 
         @Override
+        public void onRtlPropertiesChanged(int layoutDirection) {
+            super.onRtlPropertiesChanged(layoutDirection);
+
+            // Workaround for a bug before Android M where LinearLayout did not relayout itself when
+            // layout direction changed.
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+                //noinspection WrongConstant
+                if (mLayoutDirection != layoutDirection) {
+                    requestLayout();
+                    mLayoutDirection = layoutDirection;
+                }
+            }
+        }
+
+        @Override
         protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
@@ -2026,22 +2005,22 @@
             }
 
             if (startLeft != targetLeft || startRight != targetRight) {
-                ValueAnimatorCompat animator = mIndicatorAnimator = ViewUtils.createAnimator();
+                ValueAnimator animator = mIndicatorAnimator = new ValueAnimator();
                 animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
                 animator.setDuration(duration);
                 animator.setFloatValues(0, 1);
-                animator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                     @Override
-                    public void onAnimationUpdate(ValueAnimatorCompat animator) {
+                    public void onAnimationUpdate(ValueAnimator animator) {
                         final float fraction = animator.getAnimatedFraction();
                         setIndicatorPosition(
                                 AnimationUtils.lerp(startLeft, targetLeft, fraction),
                                 AnimationUtils.lerp(startRight, targetRight, fraction));
                     }
                 });
-                animator.addListener(new ValueAnimatorCompat.AnimatorListenerAdapter() {
+                animator.addListener(new AnimatorListenerAdapter() {
                     @Override
-                    public void onAnimationEnd(ValueAnimatorCompat animator) {
+                    public void onAnimationEnd(Animator animator) {
                         mSelectedPosition = position;
                         mSelectionOffset = 0f;
                     }
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 10da563..c9e8010 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -16,6 +16,9 @@
 
 package android.support.design.widget;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
@@ -39,13 +42,10 @@
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.widget.Space;
 import android.support.v4.widget.TextViewCompat;
@@ -64,6 +64,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.EditText;
@@ -121,6 +122,7 @@
 
     private final FrameLayout mInputFrame;
     EditText mEditText;
+    private CharSequence mOriginalHint;
 
     private boolean mHintEnabled;
     private CharSequence mHint;
@@ -168,7 +170,7 @@
     final CollapsingTextHelper mCollapsingTextHelper = new CollapsingTextHelper(this);
 
     private boolean mHintAnimationEnabled;
-    private ValueAnimatorCompat mAnimator;
+    private ValueAnimator mAnimator;
 
     private boolean mHasReconstructedEditTextBackground;
     private boolean mInDrawableStateChanged;
@@ -291,7 +293,8 @@
      * @param typeface typeface to use, or {@code null} to use the default.
      */
     public void setTypeface(@Nullable Typeface typeface) {
-        if (typeface != mTypeface) {
+        if ((mTypeface != null && !mTypeface.equals(typeface))
+                || (mTypeface == null && typeface != null)) {
             mTypeface = typeface;
 
             mCollapsingTextHelper.setTypefaces(typeface);
@@ -312,6 +315,24 @@
         return mTypeface;
     }
 
+    @Override
+    public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
+        if (mOriginalHint == null || mEditText == null) {
+            super.dispatchProvideAutofillStructure(structure, flags);
+            return;
+        }
+
+        // Temporarily sets child's hint to its original value so it is properly set in the
+        // child's ViewStructure.
+        final CharSequence hint = mEditText.getHint();
+        mEditText.setHint(mOriginalHint);
+        try {
+            super.dispatchProvideAutofillStructure(structure, flags);
+        } finally {
+            mEditText.setHint(hint);
+        }
+    }
+
     private void setEditText(EditText editText) {
         // If we already have an EditText, throw an exception
         if (mEditText != null) {
@@ -363,7 +384,9 @@
 
         // If we do not have a valid hint, try and retrieve it from the EditText, if enabled
         if (mHintEnabled && TextUtils.isEmpty(mHint)) {
-            setHint(mEditText.getHint());
+            // Save the hint so it can be restored on dispatchProvideAutofillStructure();
+            mOriginalHint = mEditText.getHint();
+            setHint(mOriginalHint);
             // Clear the EditText's hint as we will display it ourselves
             mEditText.setHint(null);
         }
@@ -595,7 +618,7 @@
     public void setErrorEnabled(boolean enabled) {
         if (mErrorEnabled != enabled) {
             if (mErrorView != null) {
-                ViewCompat.animate(mErrorView).cancel();
+                mErrorView.animate().cancel();
             }
 
             if (enabled) {
@@ -625,8 +648,8 @@
                     // we manually set something appropriate
                     TextViewCompat.setTextAppearance(mErrorView,
                             android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Caption);
-                    mErrorView.setTextColor(ContextCompat.getColor(
-                            getContext(), R.color.design_textinput_error_color_light));
+                    mErrorView.setTextColor(ContextCompat.getColor(getContext(),
+                            android.support.v7.appcompat.R.color.error_color_material));
                 }
                 mErrorView.setVisibility(INVISIBLE);
                 ViewCompat.setAccessibilityLiveRegion(mErrorView,
@@ -698,43 +721,43 @@
         mErrorShown = !TextUtils.isEmpty(error);
 
         // Cancel any on-going animation
-        ViewCompat.animate(mErrorView).cancel();
+        mErrorView.animate().cancel();
 
         if (mErrorShown) {
             mErrorView.setText(error);
             mErrorView.setVisibility(VISIBLE);
 
             if (animate) {
-                if (ViewCompat.getAlpha(mErrorView) == 1f) {
+                if (mErrorView.getAlpha() == 1f) {
                     // If it's currently 100% show, we'll animate it from 0
-                    ViewCompat.setAlpha(mErrorView, 0f);
+                    mErrorView.setAlpha(0f);
                 }
-                ViewCompat.animate(mErrorView)
+                mErrorView.animate()
                         .alpha(1f)
                         .setDuration(ANIMATION_DURATION)
                         .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
-                        .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                        .setListener(new AnimatorListenerAdapter() {
                             @Override
-                            public void onAnimationStart(View view) {
-                                view.setVisibility(VISIBLE);
+                            public void onAnimationStart(Animator animator) {
+                                mErrorView.setVisibility(VISIBLE);
                             }
                         }).start();
             } else {
                 // Set alpha to 1f, just in case
-                ViewCompat.setAlpha(mErrorView, 1f);
+                mErrorView.setAlpha(1f);
             }
         } else {
             if (mErrorView.getVisibility() == VISIBLE) {
                 if (animate) {
-                    ViewCompat.animate(mErrorView)
+                    mErrorView.animate()
                             .alpha(0f)
                             .setDuration(ANIMATION_DURATION)
                             .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
-                            .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                            .setListener(new AnimatorListenerAdapter() {
                                 @Override
-                                public void onAnimationEnd(View view) {
+                                public void onAnimationEnd(Animator animator) {
                                     mErrorView.setText(error);
-                                    view.setVisibility(INVISIBLE);
+                                    mErrorView.setVisibility(INVISIBLE);
                                 }
                             }).start();
                 } else {
@@ -769,8 +792,8 @@
                     // we manually set something appropriate
                     TextViewCompat.setTextAppearance(mCounterView,
                             android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Caption);
-                    mCounterView.setTextColor(ContextCompat.getColor(
-                            getContext(), R.color.design_textinput_error_color_light));
+                    mCounterView.setTextColor(ContextCompat.getColor(getContext(),
+                            android.support.v7.appcompat.R.color.error_color_material));
                 }
                 addIndicator(mCounterView, -1);
                 if (mEditText == null) {
@@ -938,6 +961,7 @@
 
     static class SavedState extends AbsSavedState {
         CharSequence error;
+        boolean isPasswordToggledVisible;
 
         SavedState(Parcelable superState) {
             super(superState);
@@ -946,6 +970,7 @@
         SavedState(Parcel source, ClassLoader loader) {
             super(source, loader);
             error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            isPasswordToggledVisible = (source.readInt() == 1);
 
         }
 
@@ -953,6 +978,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             super.writeToParcel(dest, flags);
             TextUtils.writeToParcel(error, dest, flags);
+            dest.writeInt(isPasswordToggledVisible ? 1 : 0);
         }
 
         @Override
@@ -962,18 +988,22 @@
                     + " error=" + error + "}";
         }
 
-        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 
     @Override
@@ -983,6 +1013,7 @@
         if (mErrorShown) {
             ss.error = getError();
         }
+        ss.isPasswordToggledVisible = mPasswordToggledVisible;
         return ss;
     }
 
@@ -995,6 +1026,9 @@
         SavedState ss = (SavedState) state;
         super.onRestoreInstanceState(ss.getSuperState());
         setError(ss.error);
+        if (ss.isPasswordToggledVisible) {
+            passwordVisibilityToggleRequested(true);
+        }
         requestLayout();
     }
 
@@ -1073,7 +1107,7 @@
                 mPasswordToggleView.setOnClickListener(new View.OnClickListener() {
                     @Override
                     public void onClick(View view) {
-                        passwordVisibilityToggleRequested();
+                        passwordVisibilityToggleRequested(false);
                     }
                 });
             }
@@ -1286,7 +1320,7 @@
         applyPasswordToggleTint();
     }
 
-    void passwordVisibilityToggleRequested() {
+    private void passwordVisibilityToggleRequested(boolean shouldSkipAnimations) {
         if (mPasswordToggleEnabled) {
             // Store the current cursor position
             final int selection = mEditText.getSelectionEnd();
@@ -1300,6 +1334,9 @@
             }
 
             mPasswordToggleView.setChecked(mPasswordToggledVisible);
+            if (shouldSkipAnimations) {
+                mPasswordToggleView.jumpDrawablesToCurrentState();
+            }
 
             // And restore the cursor position
             mEditText.setSelection(selection);
@@ -1420,13 +1457,13 @@
             return;
         }
         if (mAnimator == null) {
-            mAnimator = ViewUtils.createAnimator();
+            mAnimator = new ValueAnimator();
             mAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR);
             mAnimator.setDuration(ANIMATION_DURATION);
-            mAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
-                public void onAnimationUpdate(ValueAnimatorCompat animator) {
-                    mCollapsingTextHelper.setExpansionFraction(animator.getAnimatedFloatValue());
+                public void onAnimationUpdate(ValueAnimator animator) {
+                    mCollapsingTextHelper.setExpansionFraction((float) animator.getAnimatedValue());
                 }
             });
         }
diff --git a/design/src/android/support/design/widget/ViewGroupUtils.java b/design/src/android/support/design/widget/ViewGroupUtils.java
index bb5e1b6..0545516 100644
--- a/design/src/android/support/design/widget/ViewGroupUtils.java
+++ b/design/src/android/support/design/widget/ViewGroupUtils.java
@@ -16,51 +16,16 @@
 
 package android.support.design.widget;
 
+import android.graphics.Matrix;
 import android.graphics.Rect;
-import android.os.Build;
+import android.graphics.RectF;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 
 class ViewGroupUtils {
-
-    private interface ViewGroupUtilsImpl {
-        void offsetDescendantRect(ViewGroup parent, View child, Rect rect);
-    }
-
-    private static class ViewGroupUtilsImplBase implements ViewGroupUtilsImpl {
-        ViewGroupUtilsImplBase() {
-        }
-
-        @Override
-        public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) {
-            parent.offsetDescendantRectToMyCoords(child, rect);
-            // View#offsetDescendantRectToMyCoords includes scroll offsets of the last child.
-            // We need to reverse it here so that we get the rect of the view itself rather
-            // than its content.
-            rect.offset(child.getScrollX(), child.getScrollY());
-        }
-    }
-
-    private static class ViewGroupUtilsImplHoneycomb implements ViewGroupUtilsImpl {
-        ViewGroupUtilsImplHoneycomb() {
-        }
-
-        @Override
-        public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) {
-            ViewGroupUtilsHoneycomb.offsetDescendantRect(parent, child, rect);
-        }
-    }
-
-    private static final ViewGroupUtilsImpl IMPL;
-
-    static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 11) {
-            IMPL = new ViewGroupUtilsImplHoneycomb();
-        } else {
-            IMPL = new ViewGroupUtilsImplBase();
-        }
-    }
+    private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
+    private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
 
     /**
      * This is a port of the common
@@ -72,7 +37,25 @@
      * @param rect (in/out) the rect to offset from descendant to this view's coordinate system
      */
     static void offsetDescendantRect(ViewGroup parent, View descendant, Rect rect) {
-        IMPL.offsetDescendantRect(parent, descendant, rect);
+        Matrix m = sMatrix.get();
+        if (m == null) {
+            m = new Matrix();
+            sMatrix.set(m);
+        } else {
+            m.reset();
+        }
+
+        offsetDescendantMatrix(parent, descendant, m);
+
+        RectF rectF = sRectF.get();
+        if (rectF == null) {
+            rectF = new RectF();
+            sRectF.set(rectF);
+        }
+        rectF.set(rect);
+        m.mapRect(rectF);
+        rect.set((int) (rectF.left + 0.5f), (int) (rectF.top + 0.5f),
+                (int) (rectF.right + 0.5f), (int) (rectF.bottom + 0.5f));
     }
 
     /**
@@ -87,4 +70,18 @@
         offsetDescendantRect(parent, descendant, out);
     }
 
+    private static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {
+        final ViewParent parent = view.getParent();
+        if (parent instanceof View && parent != target) {
+            final View vp = (View) parent;
+            offsetDescendantMatrix(target, vp, m);
+            m.preTranslate(-vp.getScrollX(), -vp.getScrollY());
+        }
+
+        m.preTranslate(view.getLeft(), view.getTop());
+
+        if (!view.getMatrix().isIdentity()) {
+            m.preConcat(view.getMatrix());
+        }
+    }
 }
diff --git a/design/src/android/support/design/widget/ViewUtils.java b/design/src/android/support/design/widget/ViewUtils.java
index f49d836..c09eac1 100644
--- a/design/src/android/support/design/widget/ViewUtils.java
+++ b/design/src/android/support/design/widget/ViewUtils.java
@@ -17,28 +17,8 @@
 package android.support.design.widget;
 
 import android.graphics.PorterDuff;
-import android.os.Build;
 
 class ViewUtils {
-
-    static final ValueAnimatorCompat.Creator DEFAULT_ANIMATOR_CREATOR
-            = new ValueAnimatorCompat.Creator() {
-        @Override
-        public ValueAnimatorCompat createAnimator() {
-            return new ValueAnimatorCompat(Build.VERSION.SDK_INT >= 12
-                    ? new ValueAnimatorCompatImplHoneycombMr1()
-                    : new ValueAnimatorCompatImplGingerbread());
-        }
-    };
-
-    static ValueAnimatorCompat createAnimator() {
-        return DEFAULT_ANIMATOR_CREATOR.createAnimator();
-    }
-
-    static boolean objectEquals(Object a, Object b) {
-        return (a == b) || (a != null && a.equals(b));
-    }
-
     static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
         switch (value) {
             case 3:
diff --git a/design/tests/AndroidManifest.xml b/design/tests/AndroidManifest.xml
index 6122490..c4673b3 100755
--- a/design/tests/AndroidManifest.xml
+++ b/design/tests/AndroidManifest.xml
@@ -15,78 +15,71 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.design.test">
-
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application
-            android:supportsRtl="true"
-            android:theme="@style/Theme.Design">
-        <uses-library android:name="android.test.runner"/>
+        android:supportsRtl="true"
+        android:theme="@style/Theme.Design">
 
         <activity
-                android:theme="@style/Theme.AppCompat.NoActionBar"
-                android:name="android.support.design.widget.TabLayoutWithViewPagerActivity"/>
+            android:name="android.support.design.widget.TabLayoutWithViewPagerActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:theme="@style/Theme.AppCompat.NoActionBar"
-                android:name="android.support.design.widget.SnackbarActivity"/>
+            android:name="android.support.design.widget.SnackbarActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-            android:theme="@style/Theme.TranslucentNavBar"
-            android:name="android.support.design.widget.SnackbarActivityWithTranslucentNavBar"/>
+            android:name="android.support.design.widget.SnackbarActivityWithTranslucentNavBar"
+            android:theme="@style/Theme.TranslucentNavBar"/>
 
         <activity
-            android:theme="@style/Theme.AppCompat.NoActionBar"
-            android:name="android.support.design.widget.SnackbarActivityWithFAB"/>
+            android:name="android.support.design.widget.SnackbarActivityWithFAB"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:theme="@style/Theme.TranslucentStatus"
-                android:name="android.support.design.widget.DynamicCoordinatorLayoutActivity"/>
+            android:name="android.support.design.widget.DynamicCoordinatorLayoutActivity"
+            android:theme="@style/Theme.TranslucentStatus"/>
 
         <activity
-                android:theme="@style/Theme.AppCompat.NoActionBar"
-                android:name="android.support.design.widget.TabLayoutPoolingActivity"/>
+            android:name="android.support.design.widget.TabLayoutPoolingActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:theme="@style/Theme.AppCompat.NoActionBar"
-                android:name="android.support.design.widget.BottomSheetBehaviorActivity"/>
+            android:name="android.support.design.widget.BottomSheetBehaviorActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-            android:theme="@style/Theme.AppCompat.NoActionBar"
-            android:name="android.support.design.widget.BottomSheetBehaviorWithInsetsActivity"/>
+            android:name="android.support.design.widget.BottomSheetBehaviorWithInsetsActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:theme="@style/Theme.AppCompat.NoActionBar"
-                android:name="android.support.design.widget.BottomSheetDialogActivity"/>
+            android:name="android.support.design.widget.BottomSheetDialogActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-            android:theme="@style/Theme.AppCompat.NoActionBar"
-            android:name="android.support.design.widget.CoordinatorLayoutActivity"/>
+            android:name="android.support.design.widget.CoordinatorLayoutActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:name="android.support.design.widget.FloatingActionButtonActivity"
-                android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
+            android:name="android.support.design.widget.FloatingActionButtonActivity"
+            android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
 
         <activity
-            android:theme="@style/Theme.AppCompat.NoActionBar"
-            android:name="android.support.design.widget.NavigationViewActivity"/>
+            android:name="android.support.design.widget.NavigationViewActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-            android:theme="@style/Theme.AppCompat.NoActionBar"
-            android:name="android.support.design.widget.BottomNavigationViewActivity"/>
+            android:name="android.support.design.widget.BottomNavigationViewActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-              android:theme="@style/Theme.AppCompat.NoActionBar"
-              android:name="android.support.design.widget.TextInputLayoutActivity"/>
+            android:name="android.support.design.widget.TextInputLayoutActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:name="android.support.v7.app.AppCompatActivity"/>
+            android:name="android.support.v7.app.AppCompatActivity"/>
 
         <activity
             android:name="android.support.design.widget.AppBarLayoutCollapsePinTestActivity"
@@ -98,8 +91,4 @@
 
     </application>
 
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.design.test"/>
-
 </manifest>
diff --git a/design/tests/res/layout/design_appbar_toolbar_collapse_sole_toolbar.xml b/design/tests/res/layout/design_appbar_toolbar_collapse_sole_toolbar.xml
new file mode 100644
index 0000000..d2f3588
--- /dev/null
+++ b/design/tests/res/layout/design_appbar_toolbar_collapse_sole_toolbar.xml
@@ -0,0 +1,51 @@
+<?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.
+  -->
+
+<android.support.design.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/col"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <android.support.design.widget.AppBarLayout
+        android:id="@+id/app_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fitsSystemWindows="true"
+        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+        <android.support.design.widget.CollapsingToolbarLayout
+            android:id="@+id/collapsing_app_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed">
+
+            <android.support.v7.widget.Toolbar
+                android:id="@+id/toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/actionBarSize"
+                app:layout_collapseMode="pin"/>
+
+        </android.support.design.widget.CollapsingToolbarLayout>
+
+    </android.support.design.widget.AppBarLayout>
+
+    <include layout="@layout/include_appbar_scrollview"/>
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/design/tests/res/menu/bottom_navigation_view_content.xml b/design/tests/res/menu/bottom_navigation_view_content.xml
index 6d19060..d61d6d8 100644
--- a/design/tests/res/menu/bottom_navigation_view_content.xml
+++ b/design/tests/res/menu/bottom_navigation_view_content.xml
@@ -13,14 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
     <item android:id="@+id/destination_home"
+          android:icon="@drawable/test_drawable_red"
           android:title="@string/navigate_home"
-          android:icon="@drawable/test_drawable_red" />
+          app:contentDescription="@string/navigate_home"
+          app:tooltipText="@string/navigate_home"/>
     <item android:id="@+id/destination_profile"
+          android:icon="@drawable/test_drawable_green"
           android:title="@string/navigate_profile"
-          android:icon="@drawable/test_drawable_green" />
+          app:contentDescription="@string/navigate_profile"
+          app:tooltipText="@string/navigate_profile"/>
     <item android:id="@+id/destination_people"
+          android:icon="@drawable/test_drawable_blue"
           android:title="@string/navigate_people"
-          android:icon="@drawable/test_drawable_blue" />
+          app:contentDescription="@string/navigate_people"
+          app:tooltipText="@string/navigate_people"/>
 </menu>
diff --git a/design/tests/res/values/ids.xml b/design/tests/res/values/ids.xml
index 52b8356..73540b7 100644
--- a/design/tests/res/values/ids.xml
+++ b/design/tests/res/values/ids.xml
@@ -26,4 +26,5 @@
     <item name="page_9" type="id"/>
     <item name="textinputlayout" type="id"/>
     <item name="textinputedittext" type="id"/>
+    <item name="anchor" type="id"/>
 </resources>
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/custom/TestFloatingBehavior.java b/design/tests/src/android/support/design/custom/TestFloatingBehavior.java
index a508736..8e00fa6 100644
--- a/design/tests/src/android/support/design/custom/TestFloatingBehavior.java
+++ b/design/tests/src/android/support/design/custom/TestFloatingBehavior.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.support.design.widget.CoordinatorLayout;
 import android.support.design.widget.Snackbar;
-import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.TextView;
@@ -42,8 +41,8 @@
     @Override
     public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child,
             View dependency) {
-        ViewCompat.setTranslationY(child, Math.min(0,
-                ViewCompat.getTranslationY(dependency) - dependency.getHeight()));
+        child.setTranslationY(Math.min(0,
+                dependency.getTranslationY() - dependency.getHeight()));
         return true;
     }
 }
diff --git a/design/tests/src/android/support/design/testutils/ActivityUtils.java b/design/tests/src/android/support/design/testutils/ActivityUtils.java
new file mode 100644
index 0000000..1ed6a3f
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/ActivityUtils.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+package android.support.design.testutils;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.Looper;
+import android.support.test.rule.ActivityTestRule;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility methods for testing activities.
+ */
+public class ActivityUtils {
+    private static final Runnable DO_NOTHING = new Runnable() {
+        @Override
+        public void run() {
+        }
+    };
+
+    public static void waitForExecution(
+            final ActivityTestRule<? extends RecreatedAppCompatActivity> rule) {
+        // Wait for two cycles. When starting a postponed transition, it will post to
+        // the UI thread and then the execution will be added onto the queue after that.
+        // The two-cycle wait makes sure fragments have the opportunity to complete both
+        // before returning.
+        try {
+            rule.runOnUiThread(DO_NOTHING);
+            rule.runOnUiThread(DO_NOTHING);
+        } catch (Throwable throwable) {
+            throw new RuntimeException(throwable);
+        }
+    }
+
+    private static void runOnUiThreadRethrow(
+            ActivityTestRule<? extends RecreatedAppCompatActivity> rule, Runnable r) {
+        if (Looper.getMainLooper() == Looper.myLooper()) {
+            r.run();
+        } else {
+            try {
+                rule.runOnUiThread(r);
+            } catch (Throwable t) {
+                throw new RuntimeException(t);
+            }
+        }
+    }
+
+    /**
+     * Restarts the RecreatedAppCompatActivity and waits for the new activity to be resumed.
+     *
+     * @return The newly-restarted RecreatedAppCompatActivity
+     */
+    public static <T extends RecreatedAppCompatActivity> T recreateActivity(
+            ActivityTestRule<? extends RecreatedAppCompatActivity> rule, final T activity)
+            throws InterruptedException {
+        // Now switch the orientation
+        RecreatedAppCompatActivity.sResumed = new CountDownLatch(1);
+        RecreatedAppCompatActivity.sDestroyed = new CountDownLatch(1);
+
+        runOnUiThreadRethrow(rule, new Runnable() {
+            @Override
+            public void run() {
+                activity.recreate();
+            }
+        });
+        assertTrue(RecreatedAppCompatActivity.sResumed.await(1, TimeUnit.SECONDS));
+        assertTrue(RecreatedAppCompatActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+        T newActivity = (T) RecreatedAppCompatActivity.sActivity;
+
+        waitForExecution(rule);
+
+        RecreatedAppCompatActivity.clearState();
+        return newActivity;
+    }
+}
diff --git a/design/tests/src/android/support/design/testutils/RecreatedAppCompatActivity.java b/design/tests/src/android/support/design/testutils/RecreatedAppCompatActivity.java
new file mode 100644
index 0000000..52ba059
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/RecreatedAppCompatActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.support.design.testutils;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Activity that keeps track of resume / destroy lifecycle events, as well as of the last
+ * instance of itself.
+ */
+public class RecreatedAppCompatActivity extends AppCompatActivity {
+    // These must be cleared after each test using clearState()
+    public static RecreatedAppCompatActivity sActivity;
+    public static CountDownLatch sResumed;
+    public static CountDownLatch sDestroyed;
+
+    public static void clearState() {
+        sActivity = null;
+        sResumed = null;
+        sDestroyed = null;
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        sActivity = this;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (sResumed != null) {
+            sResumed.countDown();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (sDestroyed != null) {
+            sDestroyed.countDown();
+        }
+    }
+}
diff --git a/design/tests/src/android/support/design/testutils/TestUtils.java b/design/tests/src/android/support/design/testutils/TestUtils.java
index 2f9187d..b9aae7a 100644
--- a/design/tests/src/android/support/design/testutils/TestUtils.java
+++ b/design/tests/src/android/support/design/testutils/TestUtils.java
@@ -16,10 +16,7 @@
 
 package android.support.design.testutils;
 
-import android.app.Activity;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -127,20 +124,4 @@
             }
         }
     }
-
-    /**
-     * Rotates the given Activity to either portrait or landscape, depending on the current
-     * orientation.
-     */
-    public static void rotateOrientation(@NonNull Activity activity) {
-        switch (activity.getResources().getConfiguration().orientation) {
-            case Configuration.ORIENTATION_PORTRAIT:
-                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
-                break;
-            case Configuration.ORIENTATION_LANDSCAPE:
-            default:
-                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-                break;
-        }
-    }
 }
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java b/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
index f49644f..c4d4520 100755
--- a/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
+++ b/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
@@ -19,9 +19,11 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
 
 import android.graphics.Typeface;
+import android.support.design.R;
 import android.support.design.widget.TextInputLayout;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.matcher.ViewMatchers;
 import android.view.View;
 
 import org.hamcrest.Matcher;
@@ -196,4 +198,32 @@
         };
     }
 
+    /**
+     * Toggles password.
+     */
+    public static ViewAction clickPasswordToggle() {
+        return new ViewAction() {
+
+            @Override
+            public Matcher<View> getConstraints() {
+                return ViewMatchers.isAssignableFrom(TextInputLayout.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Clicks the password toggle";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                TextInputLayout textInputLayout = (TextInputLayout) view;
+                // Reach in and find the password toggle since we don't have a public API
+                // to get a reference to it
+                View passwordToggle = textInputLayout.findViewById(R.id.text_input_password_toggle);
+                passwordToggle.performClick();
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
 }
diff --git a/design/tests/src/android/support/design/testutils/TextInputLayoutMatchers.java b/design/tests/src/android/support/design/testutils/TextInputLayoutMatchers.java
index d13a39d..d67e555 100755
--- a/design/tests/src/android/support/design/testutils/TextInputLayoutMatchers.java
+++ b/design/tests/src/android/support/design/testutils/TextInputLayoutMatchers.java
@@ -16,8 +16,11 @@
 
 package android.support.design.testutils;
 
+import android.support.design.R;
+import android.support.design.widget.CheckableImageButton;
 import android.support.design.widget.TextInputLayout;
 import android.text.TextUtils;
+import android.view.View;
 
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -29,7 +32,7 @@
      * Returns a matcher that matches TextInputLayouts with non-empty content descriptions for
      * the password toggle.
      */
-    public static Matcher hasPasswordToggleContentDescription() {
+    public static Matcher passwordToggleHasContentDescription() {
         return new TypeSafeMatcher<TextInputLayout>() {
             @Override
             public void describeTo(Description description) {
@@ -39,9 +42,53 @@
 
             @Override
             protected boolean matchesSafely(TextInputLayout item) {
-                return !TextUtils.isEmpty(item.getPasswordVisibilityToggleContentDescription());
+                // Reach in and find the password toggle since we don't have a public API
+                // to get a reference to it
+                View passwordToggle = item.findViewById(R.id.text_input_password_toggle);
+                return !TextUtils.isEmpty(item.getPasswordVisibilityToggleContentDescription())
+                    && !TextUtils.isEmpty(passwordToggle.getContentDescription());
             }
         };
     }
 
+    /**
+     * Returns a matcher that matches TextInputLayouts with non-displayed password toggles
+     */
+    public static Matcher doesNotShowPasswordToggle() {
+        return new TypeSafeMatcher<TextInputLayout>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("TextInputLayout shows password toggle.");
+            }
+
+            @Override
+            protected boolean matchesSafely(TextInputLayout item) {
+                // Reach in and find the password toggle since we don't have a public API
+                // to get a reference to it
+                View passwordToggle = item.findViewById(R.id.text_input_password_toggle);
+                return passwordToggle.getVisibility() != View.VISIBLE;
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches TextInputLayouts with non-displayed password toggles
+     */
+    public static Matcher passwordToggleIsNotChecked() {
+        return new TypeSafeMatcher<TextInputLayout>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("TextInputLayout has checked password toggle.");
+            }
+
+            @Override
+            protected boolean matchesSafely(TextInputLayout item) {
+                // Reach in and find the password toggle since we don't have a public API
+                // to get a reference to it
+                CheckableImageButton passwordToggle = (CheckableImageButton) item.findViewById(
+                        R.id.text_input_password_toggle);
+                return !passwordToggle.isChecked();
+            }
+        };
+    }
 }
diff --git a/design/tests/src/android/support/design/testutils/ViewPagerActions.java b/design/tests/src/android/support/design/testutils/ViewPagerActions.java
index e7b4bf7..1a147cf 100644
--- a/design/tests/src/android/support/design/testutils/ViewPagerActions.java
+++ b/design/tests/src/android/support/design/testutils/ViewPagerActions.java
@@ -16,22 +16,17 @@
 
 package android.support.design.testutils;
 
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+
 import android.support.annotation.Nullable;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
 import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.PagerTitleStrip;
 import android.support.v4.view.ViewPager;
 import android.view.View;
-import android.widget.TextView;
-import org.hamcrest.Matcher;
 
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+import org.hamcrest.Matcher;
 
 public class ViewPagerActions {
     /**
@@ -203,31 +198,4 @@
             }
         };
     }
-
-    /**
-     * Moves <code>ViewPager</code> to specific page.
-     */
-    public static ViewAction notifyAdapterContentChange() {
-        return new ViewAction() {
-            @Override
-            public Matcher<View> getConstraints() {
-                return isAssignableFrom(ViewPager.class);
-            }
-
-            @Override
-            public String getDescription() {
-                return "ViewPager notify on adapter content change";
-            }
-
-            @Override
-            public void perform(UiController uiController, View view) {
-                uiController.loopMainThreadUntilIdle();
-
-                ViewPager viewPager = (ViewPager) view;
-                viewPager.getAdapter().notifyDataSetChanged();
-
-                uiController.loopMainThreadUntilIdle();
-            }
-        };
-    }
 }
diff --git a/design/tests/src/android/support/design/testutils/ViewStructureImpl.java b/design/tests/src/android/support/design/testutils/ViewStructureImpl.java
new file mode 100644
index 0000000..c7369e9
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/ViewStructureImpl.java
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+package android.support.design.testutils;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.view.ViewStructure;
+import android.view.ViewStructure.HtmlInfo.Builder;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Simple implementation of {@link ViewStructure} that's easier to use than a Mockito mock.
+ *
+ * <p>Currently supports only {@code hint}, {@code className}, and child-related methods.
+ */
+public class ViewStructureImpl extends ViewStructure {
+
+    private CharSequence mHint;
+    private String mClassName;
+    private ViewStructureImpl[] mChildren;
+
+    @Override
+    public void setHint(CharSequence hint) {
+        mHint = hint;
+    }
+
+    // Supported methods
+    @Override
+    public CharSequence getHint() {
+        return mHint;
+    }
+
+    @Override
+    public void setChildCount(int num) {
+        mChildren = new ViewStructureImpl[num];
+    }
+
+    @Override
+    public void setClassName(String className) {
+        mClassName = className;
+    }
+
+    public String getClassName() {
+        return mClassName;
+    }
+
+    @Override
+    public int addChildCount(int num) {
+        if (mChildren == null) {
+            setChildCount(num);
+            return 0;
+        }
+        final int start = mChildren.length;
+        ViewStructureImpl[] newArray = new ViewStructureImpl[start + num];
+        System.arraycopy(mChildren, 0, newArray, 0, start);
+        mChildren = newArray;
+        return start;
+    }
+
+    @Override
+    public int getChildCount() {
+        if (mChildren == null) {
+            return 0;
+        }
+        return mChildren.length;
+    }
+
+    public ViewStructureImpl getChildAt(int index) {
+        return mChildren[index];
+    }
+
+    @Override
+    public ViewStructure newChild(int index) {
+        final ViewStructureImpl child = new ViewStructureImpl();
+        mChildren[index] = child;
+        return child;
+    }
+
+    @Override
+    public ViewStructure asyncNewChild(int index) {
+        return newChild(index);
+    }
+
+
+    // Unsupported methods
+    @Override
+    public void setId(int id, String packageName, String typeName, String entryName) {
+    }
+
+    @Override
+    public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
+    }
+
+    @Override
+    public void setTransformation(Matrix matrix) {
+    }
+
+    @Override
+    public void setElevation(float elevation) {
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+    }
+
+    public void setAssistBlocked(boolean state) {
+    }
+
+    @Override
+    public void setEnabled(boolean state) {
+    }
+
+    @Override
+    public void setClickable(boolean state) {
+    }
+
+    @Override
+    public void setLongClickable(boolean state) {
+    }
+
+    @Override
+    public void setContextClickable(boolean state) {
+    }
+
+    @Override
+    public void setFocusable(boolean state) {
+    }
+
+    @Override
+    public void setFocused(boolean state) {
+    }
+
+    @Override
+    public void setAccessibilityFocused(boolean state) {
+    }
+
+    @Override
+    public void setCheckable(boolean state) {
+    }
+
+    @Override
+    public void setChecked(boolean state) {
+    }
+
+    @Override
+    public void setSelected(boolean state) {
+    }
+
+    @Override
+    public void setActivated(boolean state) {
+    }
+
+    @Override
+    public void setOpaque(boolean opaque) {
+    }
+
+    @Override
+    public void setContentDescription(CharSequence contentDescription) {
+    }
+
+    @Override
+    public void setText(CharSequence text) {
+    }
+
+    @Override
+    public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+    }
+
+    @Override
+    public void setTextStyle(float size, int fgColor, int bgColor, int style) {
+    }
+
+    @Override
+    public void setTextLines(int[] charOffsets, int[] baselines) {
+    }
+
+    @Override
+    public CharSequence getText() {
+        return null;
+    }
+
+    @Override
+    public int getTextSelectionStart() {
+        return 0;
+    }
+
+    @Override
+    public int getTextSelectionEnd() {
+        return 0;
+    }
+
+    @Override
+    public Bundle getExtras() {
+        return null;
+    }
+
+    @Override
+    public boolean hasExtras() {
+        return false;
+    }
+
+    @Override
+    public AutofillId getAutofillId() {
+        return null;
+    }
+
+    @Override
+    public void setAutofillId(AutofillId id) {
+    }
+
+    public void setAutofillId(ViewStructure parent, int virtualId) {
+    }
+
+    @Override
+    public void setAutofillId(AutofillId parentId, int virtualId) {
+    }
+
+    @Override
+    public void setAutofillType(int type) {
+    }
+
+    @Override
+    public void setAutofillHints(String[] hint) {
+    }
+
+    @Override
+    public void setAutofillValue(AutofillValue value) {
+    }
+
+    @Override
+    public void setAutofillOptions(CharSequence[] options) {
+    }
+
+    @Override
+    public void setInputType(int inputType) {
+    }
+
+    @Override
+    public void setDataIsSensitive(boolean sensitive) {
+    }
+
+    @Override
+    public void asyncCommit() {
+    }
+
+    public Rect getTempRect() {
+        return null;
+    }
+
+    @Override
+    public void setWebDomain(String domain) {
+    }
+
+    @Override
+    public void setLocaleList(LocaleList localeList) {
+    }
+
+    @Override
+    public Builder newHtmlInfoBuilder(String tagName) {
+        return null;
+    }
+
+    @Override
+    public void setHtmlInfo(HtmlInfo htmlInfo) {
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java b/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java
index 38ea4fc..d7ea68f 100644
--- a/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java
+++ b/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java
@@ -28,7 +28,7 @@
 
     @Override
     protected void onContentViewSet() {
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
     }
 }
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java
index 52f4ab2..b9a6518 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java
@@ -18,28 +18,36 @@
 
 import static android.support.design.testutils.AppBarLayoutMatchers.isCollapsed;
 import static android.support.design.testutils.SwipeUtils.swipeUp;
-import static android.support.design.testutils.TestUtils.rotateOrientation;
 import static android.support.design.testutils.TestUtilsMatchers.hasZ;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
-import android.app.Activity;
 import android.support.design.test.R;
+import android.support.design.testutils.ActivityUtils;
+import android.support.test.filters.LargeTest;
 
+import org.junit.Before;
 import org.junit.Test;
 
+@LargeTest
 public class AppBarWithCollapsingToolbarStateRestoreTest
         extends BaseInstrumentationTestCase<AppBarLayoutCollapsePinTestActivity> {
 
+    private AppBarLayoutCollapsePinTestActivity mActivity;
+
     public AppBarWithCollapsingToolbarStateRestoreTest() {
         super(AppBarLayoutCollapsePinTestActivity.class);
     }
 
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.getActivity();
+    }
+
     @Test
-    public void testRotateAndRestore() {
-        Activity activity = mActivityTestRule.getActivity();
-        final AppBarLayout appBar = (AppBarLayout) activity.findViewById(R.id.app_bar);
+    public void testRecreateAndRestore() throws Throwable {
+        final AppBarLayout appBar = (AppBarLayout) mActivity.findViewById(R.id.app_bar);
 
         // Swipe up and collapse the AppBarLayout
         onView(withId(R.id.coordinator_layout))
@@ -51,13 +59,12 @@
                 .check(matches(hasZ()))
                 .check(matches(isCollapsed()));
 
-        // Now rotate the Activity
-        rotateOrientation(activity);
+        mActivity = ActivityUtils.recreateActivity(mActivityTestRule, mActivity);
+        ActivityUtils.waitForExecution(mActivityTestRule);
 
         // And check that the app bar still is restored correctly
         onView(withId(R.id.app_bar))
                 .check(matches(hasZ()))
                 .check(matches(isCollapsed()));
     }
-
 }
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
index aeca0be..a9fa46a 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
@@ -21,21 +21,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-import android.os.Build;
-import android.os.SystemClock;
 import android.support.design.test.R;
-import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.Suppress;
+import android.support.testutils.PollingCheck;
 import android.widget.ImageView;
 
 import org.junit.Test;
 
 @LargeTest
 public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
-    @Suppress
-    @FlakyTest(bugId = 30701044)
+
     @Test
     public void testPinnedToolbar() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin,
@@ -141,8 +136,6 @@
         assertScrimAlpha(0);
     }
 
-    @Suppress
-    @FlakyTest(bugId = 30701044)
     @Test
     public void testScrollingToolbar() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_scroll,
@@ -253,8 +246,6 @@
         assertScrimAlpha(0);
     }
 
-    @Suppress
-    @FlakyTest(bugId = 30701044)
     @Test
     public void testScrollingToolbarEnterAlways() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_scroll_enteralways,
@@ -360,8 +351,6 @@
         assertScrimAlpha(0);
     }
 
-    @Suppress
-    @FlakyTest(bugId = 30701044)
     @Test
     public void testPinnedToolbarAndAnchoredFab() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin_with_fab,
@@ -398,16 +387,19 @@
         // this test needs to be tied to the internal implementation details of running animation
         // that scales the FAB to 0/0 scales and interpolates its alpha to 0. Since that animation
         // starts running partway through our swipe gesture and may complete a bit later then
-        // the swipe gesture, sleep for a bit to catch the "final" state of the FAB.
-        SystemClock.sleep(200);
+        // the swipe gesture, poll to catch the "final" state of the FAB.
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return fab.getScaleX() == 0.0f;
+            }
+        });
 
         // At this point the FAB should be scaled to 0/0 and set at alpha 0. Since the relevant
         // getter methods are only available on v11+, wrap the asserts with build version check.
-        if (Build.VERSION.SDK_INT >= 11) {
-            assertEquals(0.0f, fab.getScaleX(), 0.0f);
-            assertEquals(0.0f, fab.getScaleY(), 0.0f);
-            assertEquals(0.0f, fab.getAlpha(), 0.0f);
-        }
+        assertEquals(0.0f, fab.getScaleX(), 0.0f);
+        assertEquals(0.0f, fab.getScaleY(), 0.0f);
+        assertEquals(0.0f, fab.getAlpha(), 0.0f);
 
         // Perform a swipe-down gesture across the horizontal center of the screen.
         performVerticalSwipeDownGesture(
@@ -416,20 +408,20 @@
                 originalAppbarBottom,
                 longSwipeAmount);
 
-        // Same as for swipe-up gesture - sleep for a bit to catch the "final" visible state of
-        // the FAB.
-        SystemClock.sleep(200);
+        // Same as for swipe-up gesture.
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return fab.getScaleX() == 1.0f;
+            }
+        });
 
         // At this point the FAB should be scaled back to its original size and be at full opacity.
-        if (Build.VERSION.SDK_INT >= 11) {
-            assertEquals(1.0f, fab.getScaleX(), 0.0f);
-            assertEquals(1.0f, fab.getScaleY(), 0.0f);
-            assertEquals(1.0f, fab.getAlpha(), 0.0f);
-        }
+        assertEquals(1.0f, fab.getScaleX(), 0.0f);
+        assertEquals(1.0f, fab.getScaleY(), 0.0f);
+        assertEquals(1.0f, fab.getAlpha(), 0.0f);
     }
 
-    @Suppress
-    @FlakyTest(bugId = 30701044)
     @Test
     public void testPinnedToolbarAndParallaxImage() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_with_image,
@@ -528,15 +520,7 @@
 
     }
 
-    /**
-     * This test only runs on API 11+ since FrameLayout (which CollapsingToolbarLayout
-     * inherits from) has an issue with measuring children with margins when run on earlier
-     * versions of the platform.
-     */
-    @Suppress
-    @FlakyTest(bugId = 30701044)
     @Test
-    @SdkSuppress(minSdkVersion = 11)
     public void testPinnedToolbarWithMargins() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin_margins,
                 R.string.design_appbar_collapsing_toolbar_pin_margins);
@@ -590,4 +574,17 @@
         assertEquals(appbarOnScreenXY[1] + appbarHeight,
                 toolbarOnScreenXY[1] + toolbarHeight + toolbarLp.bottomMargin, 1);
     }
+
+    @Test
+    public void testSingleToolbarWithInset() throws Throwable {
+        configureContent(R.layout.design_appbar_toolbar_collapse_sole_toolbar,
+                R.string.design_appbar_collapsing_toolbar_pin_margins);
+
+        final int[] appbarOnScreenXY = new int[2];
+        final int[] toolbarOnScreenXY = new int[2];
+        mAppBar.getLocationOnScreen(appbarOnScreenXY);
+        mToolbar.getLocationOnScreen(toolbarOnScreenXY);
+
+        assertEquals(appbarOnScreenXY[1] + mAppBar.getTopInset(), toolbarOnScreenXY[1], 1);
+    }
 }
diff --git a/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java b/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
index 0c09e7b..e06fa0a 100644
--- a/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
+++ b/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
@@ -17,17 +17,18 @@
 package android.support.design.widget;
 
 import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
+import android.support.test.rule.BootlegActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public abstract class BaseInstrumentationTestCase<A extends Activity> {
     @Rule
-    public final ActivityTestRule<A> mActivityTestRule;
+    public final BootlegActivityTestRule<A> mActivityTestRule;
 
     protected BaseInstrumentationTestCase(Class<A> activityClass) {
-        mActivityTestRule = new ActivityTestRule<A>(activityClass);
+        mActivityTestRule = new BootlegActivityTestRule<>(activityClass);
     }
 }
diff --git a/design/tests/src/android/support/design/widget/BaseTestActivity.java b/design/tests/src/android/support/design/widget/BaseTestActivity.java
index 0c801cb..bdeb231 100755
--- a/design/tests/src/android/support/design/widget/BaseTestActivity.java
+++ b/design/tests/src/android/support/design/widget/BaseTestActivity.java
@@ -17,10 +17,10 @@
 package android.support.design.widget;
 
 import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
+import android.support.design.testutils.RecreatedAppCompatActivity;
 import android.view.WindowManager;
 
-abstract class BaseTestActivity extends AppCompatActivity {
+abstract class BaseTestActivity extends RecreatedAppCompatActivity {
 
     private boolean mDestroyed;
 
diff --git a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
index 5a536f6..3365fac 100644
--- a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
@@ -38,17 +38,25 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.Activity;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Parcelable;
 import android.support.annotation.ColorInt;
 import android.support.design.test.R;
 import android.support.design.testutils.TestDrawable;
 import android.support.design.testutils.TestUtilsMatchers;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.v4.content.res.ResourcesCompat;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+import android.view.View;
+import android.view.ViewGroup;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -110,7 +118,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testNavigationSelectionListener() {
         BottomNavigationView.OnNavigationItemSelectedListener mockedListener =
                 mock(BottomNavigationView.OnNavigationItemSelectedListener.class);
@@ -376,6 +384,27 @@
     @UiThreadTest
     @Test
     @SmallTest
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+    public void testPointerIcon() throws Throwable {
+        final Activity activity = mActivityTestRule.getActivity();
+        final PointerIcon expectedIcon = PointerIcon.getSystemIcon(activity, PointerIcon.TYPE_HAND);
+        final MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
+        final Menu menu = mBottomNavigation.getMenu();
+        for (int i = 0; i < menu.size(); i++) {
+            final MenuItem item = menu.getItem(i);
+            assertTrue(item.isEnabled());
+            final View itemView = activity.findViewById(item.getItemId());
+            assertEquals(expectedIcon, itemView.onResolvePointerIcon(event, 0));
+            item.setEnabled(false);
+            assertEquals(null, itemView.onResolvePointerIcon(event, 0));
+            item.setEnabled(true);
+            assertEquals(expectedIcon, itemView.onResolvePointerIcon(event, 0));
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    @SmallTest
     public void testClearingMenu() throws Throwable {
         mBottomNavigation.getMenu().clear();
         assertEquals(0, mBottomNavigation.getMenu().size());
@@ -406,6 +435,22 @@
         });
     }
 
+    @UiThreadTest
+    @Test
+    @SmallTest
+    public void testContentDescription() {
+        ViewGroup menuView = (ViewGroup) mBottomNavigation.getChildAt(0);
+        final int count = menuView.getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = menuView.getChildAt(i);
+            // We're using the same strings for content description
+            assertEquals(mMenuStringContent.get(child.getId()),
+                    child.getContentDescription().toString());
+        }
+
+        menuView.getChildAt(0).getContentDescription();
+    }
+
     private void checkAndVerifyExclusiveItem(final Menu menu, final int id) throws Throwable {
         menu.findItem(id).setChecked(true);
         for (int i = 0; i < menu.size(); i++) {
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorActivity.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorActivity.java
index 6e04241..6ab6287 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorActivity.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorActivity.java
@@ -40,10 +40,10 @@
 
     @Override
     protected void onContentViewSet() {
-        mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator);
-        mBottomSheet = (LinearLayout) findViewById(R.id.bottom_sheet);
+        mCoordinatorLayout = findViewById(R.id.coordinator);
+        mBottomSheet = findViewById(R.id.bottom_sheet);
         mBehavior = BottomSheetBehavior.from(mBottomSheet);
-        mFab = (FloatingActionButton) findViewById(R.id.fab);
+        mFab = findViewById(R.id.fab);
         Intent intent = getIntent();
         if (intent != null) {
             int initialState = intent.getIntExtra(EXTRA_INITIAL_STATE, -1);
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
index 24d8eee..6d040d4 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
@@ -715,6 +715,24 @@
     }
 
     @Test
+    @MediumTest
+    public void testExpandedPeekHeight() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // Make the peek height as tall as the bottom sheet.
+                BottomSheetBehavior behavior = getBehavior();
+                behavior.setPeekHeight(getBottomSheet().getHeight());
+                assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_COLLAPSED));
+            }
+        });
+        // Both of these will not animate the sheet , but the state should be changed.
+        checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
+        checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed());
+    }
+
+    @Test
+    @SmallTest
     public void testFindScrollingChildEnabled() {
         Context context = mActivityTestRule.getActivity();
         NestedScrollView disabledParent = new NestedScrollView(context);
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java
index 4e3bbf5..51ab0fa 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java
@@ -28,7 +28,6 @@
 import android.support.test.espresso.ViewAssertion;
 import android.support.test.espresso.action.ViewActions;
 import android.support.test.filters.MediumTest;
-import android.support.v4.view.MotionEventCompat;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -52,7 +51,7 @@
 
         @Override
         public boolean onTouch(View v, MotionEvent event) {
-            switch (MotionEventCompat.getActionMasked(event)) {
+            switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                     mDown = true;
                     break;
diff --git a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
index 4c6bbbf..c434f9f 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
@@ -20,11 +20,13 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
@@ -91,6 +93,59 @@
 
     @Test
     @MediumTest
+    public void testTouchInside() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                showDialog();
+                // Confirms that the dialog is shown
+                assertThat(mDialog.isShowing(), is(true));
+                FrameLayout bottomSheet = (FrameLayout) mDialog
+                        .findViewById(R.id.design_bottom_sheet);
+                // The bottom sheet is not clickable
+                assertNotNull(bottomSheet);
+                assertThat(bottomSheet.isClickable(), is(false));
+            }
+        });
+        // Click on the bottom sheet
+        Espresso.onView(ViewMatchers.withId(R.id.design_bottom_sheet))
+                .perform(ViewActions.click());
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // Confirm that touch didn't fall through as outside touch
+                assertThat(mDialog.isShowing(), is(true));
+            }
+        });
+    }
+
+    @Test
+    @MediumTest
+    public void testClickContent() throws Throwable {
+        final View.OnClickListener mockListener = mock(View.OnClickListener.class);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                showDialog();
+                // Confirms that the dialog is shown
+                assertThat(mDialog.isShowing(), is(true));
+                FrameLayout bottomSheet = (FrameLayout) mDialog
+                        .findViewById(R.id.design_bottom_sheet);
+                // Set up an OnClickListener to the content of the bottom sheet
+                assertNotNull(bottomSheet);
+                View child = bottomSheet.getChildAt(0);
+                child.setOnClickListener(mockListener);
+            }
+        });
+        // Click on the bottom sheet; since the whole sheet is occupied with its only child, this
+        // clicks the child
+        Espresso.onView(ViewMatchers.withParent(ViewMatchers.withId(R.id.design_bottom_sheet)))
+                .perform(ViewActions.click());
+        verify(mockListener, times(1)).onClick(any(View.class));
+    }
+
+    @Test
+    @MediumTest
     public void testShortDialog() throws Throwable {
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutActivity.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutActivity.java
index 7d52666..a42ac8d 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutActivity.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutActivity.java
@@ -31,8 +31,8 @@
 
     @Override
     protected void onContentViewSet() {
-        mContainer = (FrameLayout) findViewById(R.id.container);
-        mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator);
+        mContainer = findViewById(R.id.container);
+        mCoordinatorLayout = findViewById(R.id.coordinator);
     }
 
 }
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index 73ad193..3588043 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -37,12 +37,16 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.TargetApi;
 import android.app.Instrumentation;
+import android.content.Context;
 import android.graphics.Rect;
+import android.support.annotation.NonNull;
 import android.support.design.test.R;
 import android.support.design.testutils.CoordinatorLayoutUtils;
 import android.support.design.testutils.CoordinatorLayoutUtils.DependentBehavior;
 import android.support.design.widget.CoordinatorLayout.Behavior;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.v4.view.GravityCompat;
@@ -57,6 +61,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -76,6 +81,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
+    @TargetApi(21)
     public void testSetFitSystemWindows() throws Throwable {
         final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
@@ -733,4 +739,38 @@
             return true;
         }
     }
+
+    @UiThreadTest
+    @Test
+    public void testAnchorDependencyGraph() throws Throwable {
+        final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
+
+        // Override hashcode because of implementation of SimpleArrayMap used in
+        // DirectedAcyclicGraph used for sorting dependencies. Hashcode of anchored view has to be
+        // greater than of the one it is anchored to in order to reproduce the error.
+        final View anchor = createViewWithHashCode(col.getContext(), 2);
+        anchor.setId(R.id.anchor);
+
+        final View ship = createViewWithHashCode(col.getContext(), 3);
+        final CoordinatorLayout.LayoutParams lp = col.generateDefaultLayoutParams();
+        lp.setAnchorId(R.id.anchor);
+
+        col.addView(anchor);
+        col.addView(ship, lp);
+
+        // Get dependencies immediately to avoid possible call to onMeasure(), since error
+        // only happens on first computing of sorted dependencies.
+        List<View> dependencySortedChildren = col.getDependencySortedChildren();
+        assertThat(dependencySortedChildren, is(Arrays.asList(anchor, ship)));
+    }
+
+    @NonNull
+    private View createViewWithHashCode(final Context context, final int hashCode) {
+        return new View(context) {
+            @Override
+            public int hashCode() {
+                return hashCode;
+            }
+        };
+    }
 }
diff --git a/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java b/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java
index 2cdc990..30f04ce 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java
@@ -32,6 +32,7 @@
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.v7.widget.AppCompatTextView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -99,6 +100,8 @@
         }
     }
 
+    // Test is flaky on API 19
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
     @Test
     public void testBuiltInSliding() {
         onView(withId(R.id.coordinator_stub)).perform(
diff --git a/design/tests/src/android/support/design/widget/CustomSnackbarTest.java b/design/tests/src/android/support/design/widget/CustomSnackbarTest.java
index cec87da..4fc2525 100644
--- a/design/tests/src/android/support/design/widget/CustomSnackbarTest.java
+++ b/design/tests/src/android/support/design/widget/CustomSnackbarTest.java
@@ -40,8 +40,8 @@
 import android.support.design.testutils.SnackbarUtils;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.ViewInteraction;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
 import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
 
@@ -98,15 +98,15 @@
                 new BaseTransientBottomBar.ContentViewCallback() {
                     @Override
                     public void animateContentIn(int delay, int duration) {
-                        ViewCompat.setAlpha(content, 0f);
-                        ViewCompat.animate(content).alpha(1f).setDuration(duration)
+                        content.setAlpha(0f);
+                        content.animate().alpha(1f).setDuration(duration)
                                 .setStartDelay(delay).start();
                     }
 
                     @Override
                     public void animateContentOut(int delay, int duration) {
-                        ViewCompat.setAlpha(content, 1f);
-                        ViewCompat.animate(content).alpha(0f).setDuration(duration)
+                        content.setAlpha(1f);
+                        content.animate().alpha(0f).setDuration(duration)
                                 .setStartDelay(delay).start();
                     }
                 };
@@ -114,7 +114,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testBasicContent() throws Throwable {
         // Verify different combinations of snackbar content (title / subtitle and action)
         // and duration
diff --git a/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java b/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
index e8cc701..1037235 100644
--- a/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
+++ b/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
@@ -53,6 +53,8 @@
 import android.support.test.espresso.action.GeneralSwipeAction;
 import android.support.test.espresso.action.Press;
 import android.support.test.espresso.action.Swipe;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.v4.content.ContextCompat;
 import android.view.Gravity;
@@ -60,7 +62,6 @@
 
 import org.junit.Test;
 
-@SmallTest
 public class FloatingActionButtonTest
         extends BaseInstrumentationTestCase<FloatingActionButtonActivity> {
 
@@ -68,6 +69,7 @@
         super(FloatingActionButtonActivity.class);
     }
 
+    @SmallTest
     @Test
     public void testDefaultBackgroundTint() {
         final int colorAccent = TestUtils.getThemeAttrColor(
@@ -76,6 +78,7 @@
                 .check(matches(withFabBackgroundFill(colorAccent)));
     }
 
+    @SmallTest
     @Test
     public void testSetTintOnDefaultBackgroundTint() {
         onView(withId(R.id.fab_standard))
@@ -83,12 +86,14 @@
                 .check(matches(withFabBackgroundFill(Color.GREEN)));
     }
 
+    @SmallTest
     @Test
     public void testDeclaredBackgroundTint() {
         onView(withId(R.id.fab_tint))
                 .check(matches(withFabBackgroundFill(Color.MAGENTA)));
     }
 
+    @SmallTest
     @Test
     public void testSetTintOnDeclaredBackgroundTint() {
         onView(withId(R.id.fab_tint))
@@ -96,6 +101,7 @@
                 .check(matches(withFabBackgroundFill(Color.GREEN)));
     }
 
+    @SmallTest
     @Test
     public void testSetStatefulTintAcrossStateChanges() {
         final Activity activity = mActivityTestRule.getActivity();
@@ -118,6 +124,7 @@
                 .check(matches(withFabBackgroundFill(normal)));
     }
 
+    @SmallTest
     @Test
     public void testDeclaredStatefulTintAcrossStateChanges() {
         final Activity activity = mActivityTestRule.getActivity();
@@ -132,12 +139,14 @@
                 .check(matches(withFabBackgroundFill(disabled)));
     }
 
+    @SmallTest
     @Test
     public void setVectorDrawableSrc() {
         onView(withId(R.id.fab_standard))
                 .perform(setImageResource(R.drawable.vector_icon));
     }
 
+    @SmallTest
     @Test
     public void testSetMiniSize() {
         final int miniSize = mActivityTestRule.getActivity().getResources()
@@ -148,6 +157,7 @@
                 .check(matches(withFabContentHeight(miniSize)));
     }
 
+    @SmallTest
     @Test
     public void testSetSizeToggle() {
         final int miniSize = mActivityTestRule.getActivity().getResources()
@@ -164,6 +174,7 @@
                 .check(matches(withFabContentHeight(normalSize)));
     }
 
+    @SmallTest
     @Test
     public void testOffset() {
         onView(withId(R.id.fab_standard))
@@ -175,6 +186,7 @@
                 .check(matches(withFabContentAreaOnMargins(Gravity.RIGHT | Gravity.BOTTOM)));
     }
 
+    @SmallTest
     @Test
     public void testHideShow() {
         onView(withId(R.id.fab_standard))
@@ -183,6 +195,7 @@
                 .check(matches(isDisplayed()));
     }
 
+    @MediumTest
     @Test
     public void testShowHide() {
         onView(withId(R.id.fab_standard))
@@ -191,6 +204,7 @@
                 .check(matches(not(isDisplayed())));
     }
 
+    @LargeTest
     @Test
     public void testClickableTouchAndDragOffView() {
         onView(withId(R.id.fab_standard))
@@ -232,6 +246,7 @@
                 .check(matches(not(isPressed())));
     }
 
+    @MediumTest
     @Test
     public void testOnClickListener() {
         final View.OnClickListener listener = mock(View.OnClickListener.class);
@@ -245,6 +260,7 @@
         verify(listener, times(1)).onClick(view);
     }
 
+    @SmallTest
     @Test
     public void testSetCompatElevation() {
         onView(withId(R.id.fab_standard))
diff --git a/design/tests/src/android/support/design/widget/NavigationViewTest.java b/design/tests/src/android/support/design/widget/NavigationViewTest.java
index 8d6746d..e7e922a 100755
--- a/design/tests/src/android/support/design/widget/NavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/NavigationViewTest.java
@@ -61,6 +61,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Parcelable;
@@ -406,6 +407,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 11)
+    @TargetApi(11)
     @Test
     public void testHeaderState() {
         // Open our drawer
@@ -445,6 +447,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 11)
+    @TargetApi(11)
     @Test
     public void testActionViewState() {
         // Open our drawer
diff --git a/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java b/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java
index 7d90a2b..5ffe3ca 100644
--- a/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java
+++ b/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java
@@ -20,13 +20,13 @@
 
 import android.support.design.test.R;
 import android.support.design.testutils.SnackbarUtils;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.view.View;
 
 import org.junit.Before;
 import org.junit.Test;
 
-@MediumTest
+@LargeTest
 public class SnackbarTestWithFAB extends BaseInstrumentationTestCase<SnackbarActivityWithFAB> {
 
     private static final String MESSAGE_TEXT = "Test Message";
diff --git a/design/tests/src/android/support/design/widget/TabLayoutTest.java b/design/tests/src/android/support/design/widget/TabLayoutTest.java
index e9255fa..730ff56 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutTest.java
@@ -33,17 +33,23 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.os.Build;
 import android.support.design.test.R;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.IdlingResource;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
+import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.view.InflateException;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.View;
 
 import org.junit.Test;
@@ -192,6 +198,24 @@
         assertTabCustomViewSelected(tabs);
     }
 
+    @Test
+    @UiThreadTest
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+    public void testPointerIcon() {
+        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        final TabLayout tabLayout = (TabLayout) inflater.inflate(R.layout.design_tabs_items, null);
+        final PointerIcon expectedIcon =
+                PointerIcon.getSystemIcon(mActivityTestRule.getActivity(), PointerIcon.TYPE_HAND);
+
+        final int tabCount = tabLayout.getTabCount();
+        assertEquals(3, tabCount);
+
+        final MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
+        for (int i = 0; i < tabCount; i++) {
+            assertEquals(expectedIcon, tabLayout.getTabAt(i).mView.onResolvePointerIcon(event, 0));
+        }
+    }
+
     private static void assertTabCustomViewSelected(final TabLayout tabLayout) {
         for (int i = 0, count = tabLayout.getTabCount(); i < count; i++) {
             final TabLayout.Tab tab = tabLayout.getTabAt(i);
@@ -207,6 +231,7 @@
         testSetScrollPosition(true);
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN_MR1)
     @Test
     public void setScrollPositionRtl() throws Throwable {
         testSetScrollPosition(false);
@@ -266,14 +291,14 @@
         private ResourceCallback mCallback;
 
         TabLayoutScrollIdlingResource(final TabLayout tabLayout) {
-            tabLayout.setScrollAnimatorListener(new ValueAnimatorCompat.AnimatorListenerAdapter() {
+            tabLayout.setScrollAnimatorListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationStart(ValueAnimatorCompat animator) {
+                public void onAnimationStart(Animator animator) {
                     setIdle(false);
                 }
 
                 @Override
-                public void onAnimationEnd(ValueAnimatorCompat animator) {
+                public void onAnimationEnd(Animator animator) {
                     setIdle(true);
                 }
             });
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
index ff92b36..39db827 100644
--- a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
@@ -26,7 +26,7 @@
 
     @Override
     protected void onContentViewSet() {
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
     }
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
index 00810d5..09bf43c 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
@@ -16,7 +16,6 @@
 package android.support.design.widget;
 
 import static android.support.design.testutils.TabLayoutActions.setupWithViewPager;
-import static android.support.design.testutils.ViewPagerActions.notifyAdapterContentChange;
 import static android.support.design.testutils.ViewPagerActions.setAdapter;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
@@ -42,6 +41,8 @@
 import android.support.design.testutils.TestUtilsActions;
 import android.support.design.testutils.TestUtilsMatchers;
 import android.support.design.testutils.ViewPagerActions;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
@@ -71,7 +72,7 @@
         protected ArrayList<Pair<String, Q>> mEntries = new ArrayList<>();
 
         public void add(String title, Q content) {
-            mEntries.add(new Pair(title, content));
+            mEntries.add(new Pair<>(title, content));
         }
 
         @Override
@@ -176,6 +177,63 @@
         }
     }
 
+    private static <Q> ViewAction addItemToPager(final String title, final Q content) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(ViewPager.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Add item and notify on content change";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                final ViewPager viewPager = (ViewPager) view;
+                final BasePagerAdapter<Q> viewPagerAdapter =
+                        (BasePagerAdapter<Q>) viewPager.getAdapter();
+                viewPagerAdapter.add(title, content);
+                viewPagerAdapter.notifyDataSetChanged();
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    private static <Q> ViewAction addItemsToPager(final String[] title, final Q[] content) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(ViewPager.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Add items and notify on content change";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                final ViewPager viewPager = (ViewPager) view;
+                final BasePagerAdapter<Q> viewPagerAdapter =
+                        (BasePagerAdapter<Q>) viewPager.getAdapter();
+                int itemCount = title.length;
+                for (int i = 0; i < itemCount; i++) {
+                    viewPagerAdapter.add(title[i], content[i]);
+                }
+                viewPagerAdapter.notifyDataSetChanged();
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
     public TabLayoutWithViewPagerTest() {
         super(TabLayoutWithViewPagerActivity.class);
     }
@@ -307,15 +365,15 @@
         assertEquals("Initial adapter page count", 3, initialAdapter.getCount());
 
         // Add two more entries to our adapter
-        mDefaultPagerAdapter.add("Yellow", Color.YELLOW);
-        mDefaultPagerAdapter.add("Magenta", Color.MAGENTA);
-        final int newItemCount = mDefaultPagerAdapter.getCount();
-        onView(withId(R.id.tabs_viewpager)).perform(notifyAdapterContentChange());
+        onView(withId(R.id.tabs_viewpager)).perform(
+                addItemsToPager(new String[] { "Yellow", "Magenta"},
+                        new Integer[] { Color.YELLOW, Color.MAGENTA }));
 
         // We have more comprehensive test coverage for changing the ViewPager adapter in v4/tests.
         // Here we are focused on testing the continuous integration of TabLayout with the new
         // content of ViewPager
 
+        final int newItemCount = mDefaultPagerAdapter.getCount();
         assertEquals("Matching item count", newItemCount, mTabLayout.getTabCount());
 
         for (int i = 0; i < newItemCount; i++) {
@@ -337,15 +395,13 @@
         assertEquals("Initial adapter class", ColorPagerAdapter.class, initialAdapter.getClass());
         assertEquals("Initial adapter page count", 3, initialAdapter.getCount());
 
-        // Add two more entries to our adapter
-        mDefaultPagerAdapter.add("Yellow", Color.YELLOW);
-        mDefaultPagerAdapter.add("Magenta", Color.MAGENTA);
-        final int newItemCount = mDefaultPagerAdapter.getCount();
-
-        // Notify the adapter that it has changed
-        onView(withId(R.id.tabs_viewpager)).perform(notifyAdapterContentChange());
+        // Add two entries to our adapter
+        onView(withId(R.id.tabs_viewpager)).perform(
+                addItemsToPager(new String[] { "Yellow", "Magenta"},
+                        new Integer[] { Color.YELLOW, Color.MAGENTA }));
 
         // Assert that the TabLayout did not update and add the new items
+        final int newItemCount = mDefaultPagerAdapter.getCount();
         assertNotEquals("Matching item count", newItemCount, mTabLayout.getTabCount());
     }
 
@@ -428,9 +484,7 @@
 
         // Add a bunch of tabs and verify that all of them are visible on the screen
         for (int i = 0; i < 8; i++) {
-            newAdapter.add("Title " + i, "Body " + i);
-            onView(withId(R.id.tabs_viewpager)).perform(
-                    notifyAdapterContentChange());
+            onView(withId(R.id.tabs_viewpager)).perform(addItemToPager("Title " + i, "Body " + i));
 
             int expectedTabCount = i + 1;
             assertEquals("Tab count after adding #" + i, expectedTabCount,
@@ -499,9 +553,7 @@
                 tabTitleBuilder.append(titleComponent);
             }
             final String tabTitle = tabTitleBuilder.toString();
-            newAdapter.add(tabTitle, "Body " + i);
-            onView(withId(R.id.tabs_viewpager)).perform(
-                    notifyAdapterContentChange());
+            onView(withId(R.id.tabs_viewpager)).perform(addItemToPager(tabTitle, "Body " + i));
 
             int expectedTabCount = i + 1;
             // Check that all tabs are at least as wide as min width *and* at most as wide as max
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java b/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java
index 1ae3a29..613ae6e 100644
--- a/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java
@@ -16,7 +16,6 @@
 package android.support.design.widget;
 
 import android.support.design.test.R;
-import android.support.v7.widget.Toolbar;
 
 public class TextInputLayoutActivity extends BaseTestActivity {
     @Override
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 929fcd7..52471a9 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -21,6 +21,7 @@
 import static android.support.design.testutils.TestUtilsMatchers.withCompoundDrawable;
 import static android.support.design.testutils.TestUtilsMatchers.withTextColor;
 import static android.support.design.testutils.TestUtilsMatchers.withTypeface;
+import static android.support.design.testutils.TextInputLayoutActions.clickPasswordToggle;
 import static android.support.design.testutils.TextInputLayoutActions.setCounterEnabled;
 import static android.support.design.testutils.TextInputLayoutActions.setCounterMaxLength;
 import static android.support.design.testutils.TextInputLayoutActions.setError;
@@ -29,8 +30,10 @@
 import static android.support.design.testutils.TextInputLayoutActions
         .setPasswordVisibilityToggleEnabled;
 import static android.support.design.testutils.TextInputLayoutActions.setTypeface;
+import static android.support.design.testutils.TextInputLayoutMatchers.doesNotShowPasswordToggle;
 import static android.support.design.testutils.TextInputLayoutMatchers
-        .hasPasswordToggleContentDescription;
+        .passwordToggleHasContentDescription;
+import static android.support.design.testutils.TextInputLayoutMatchers.passwordToggleIsNotChecked;
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
@@ -38,17 +41,18 @@
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.contrib.AccessibilityChecks.accessibilityAssertion;
-import static android.support.test.espresso.matcher.ViewMatchers.hasContentDescription;
 import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
-import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.AllOf.allOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
@@ -58,14 +62,22 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Parcelable;
 import android.support.design.test.R;
+import android.support.design.testutils.ActivityUtils;
+import android.support.design.testutils.RecreatedAppCompatActivity;
 import android.support.design.testutils.TestUtils;
+import android.support.design.testutils.ViewStructureImpl;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.v4.widget.TextViewCompat;
+import android.text.method.PasswordTransformationMethod;
+import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -163,7 +175,7 @@
         assertNotEquals(INPUT_TEXT, textInput.getLayout().getText().toString());
 
         // Now click the toggle button
-        onView(withId(R.id.text_input_password_toggle)).perform(click());
+        onView(withId(R.id.textinput_password)).perform(clickPasswordToggle());
 
         // And assert that the password is not disguised
         assertEquals(INPUT_TEXT, textInput.getLayout().getText().toString());
@@ -185,7 +197,7 @@
                 .perform(setPasswordVisibilityToggleEnabled(false));
 
         // Check that the password toggle view is not visible
-        onView(withId(R.id.text_input_password_toggle)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.textinput_password)).check(matches(doesNotShowPasswordToggle()));
         // ...and that the password is disguised still
         assertNotEquals(INPUT_TEXT, textInput.getLayout().getText().toString());
     }
@@ -201,7 +213,7 @@
         assertNotEquals(INPUT_TEXT, textInput.getLayout().getText().toString());
 
         // Now click the toggle button
-        onView(withId(R.id.text_input_password_toggle)).perform(click());
+        onView(withId(R.id.textinput_password)).perform(clickPasswordToggle());
         // Disable the password toggle
         onView(withId(R.id.textinput_password))
                 .perform(setPasswordVisibilityToggleEnabled(false));
@@ -248,7 +260,7 @@
 
         // Type some text on the EditText and then click the toggle button
         onView(withId(R.id.textinput_edittext_pwd)).perform(typeText(INPUT_TEXT));
-        onView(withId(R.id.text_input_password_toggle)).perform(click());
+        onView(withId(R.id.textinput_password)).perform(clickPasswordToggle());
 
         // Disable the password toggle, and then re-enable it
         onView(withId(R.id.textinput_password))
@@ -257,7 +269,7 @@
 
         // Check that the password is disguised and the toggle button reflects the same state
         assertNotEquals(INPUT_TEXT, textInput.getLayout().getText().toString());
-        onView(withId(R.id.text_input_password_toggle)).check(matches(not(isChecked())));
+        onView(withId(R.id.textinput_password)).check(matches(passwordToggleIsNotChecked()));
     }
 
     @Test
@@ -304,6 +316,30 @@
         assertEquals(INPUT_TEXT, info.hintText);
     }
 
+    @UiThreadTest
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    public void testDispatchProvideAutofillStructure() {
+        final Activity activity = mActivityTestRule.getActivity();
+
+        final TextInputLayout layout = activity.findViewById(R.id.textinput);
+
+        final ViewStructureImpl structure = new ViewStructureImpl();
+        layout.dispatchProvideAutofillStructure(structure, 0);
+
+        assertEquals(2, structure.getChildCount()); // EditText and TextView
+
+        // Asserts the structure.
+        final ViewStructureImpl childStructure = structure.getChildAt(0);
+        assertEquals(EditText.class.getName(), childStructure.getClassName());
+        assertEquals("Hint to the user", childStructure.getHint());
+
+        // Make sure the widget's hint was restored.
+        assertEquals("Hint to the user", layout.getHint());
+        final EditText editText = activity.findViewById(R.id.textinput_edittext);
+        assertNull(editText.getHint());
+    }
+
     /**
      * Regression test for b/31663756.
      */
@@ -402,13 +438,10 @@
 
     @Test
     public void testPasswordToggleHasDefaultContentDescription() {
-        // Check that the TextInputLayout says that it has a content description
+        // Check that the TextInputLayout says that it has a content description and that the
+        // underlying toggle has content description as well
         onView(withId(R.id.textinput_password))
-                .check(matches(hasPasswordToggleContentDescription()));
-
-        // Check that the underlying toggle view says that it also has a content description
-        onView(withId(R.id.text_input_password_toggle))
-                .check(matches(hasContentDescription()));
+                .check(matches(passwordToggleHasContentDescription()));
     }
 
     /**
@@ -417,8 +450,8 @@
      */
     @Test
     public void testPasswordToggleIsAccessible() {
-        onView(withId(R.id.text_input_password_toggle))
-                .check(accessibilityAssertion());
+        onView(allOf(withId(R.id.text_input_password_toggle),
+                isDescendantOfA(withId(R.id.textinput_password)))).check(accessibilityAssertion());
     }
 
     @Test
@@ -461,8 +494,7 @@
 
     @Test
     public void testTextSetViaAttributeCollapsedHint() {
-        onView(withId(R.id.textinput_with_text))
-                .check(isHintExpanded(false));
+        onView(withId(R.id.textinput_with_text)).check(isHintExpanded(false));
     }
 
     @Test
@@ -480,6 +512,25 @@
                 .check(matches(hasFocus()));
     }
 
+    @Test
+    @LargeTest
+    public void testSaveAndRestorePasswordVisibility() throws Throwable {
+        // Type some text on the EditText
+        onView(withId(R.id.textinput_edittext_pwd)).perform(typeText(INPUT_TEXT));
+        onView(withId(R.id.textinput_password)).check(isPasswordToggledVisible(false));
+
+        // Toggle password to be shown as plain text
+        onView(withId(R.id.textinput_password)).perform(clickPasswordToggle());
+        onView(withId(R.id.textinput_password)).check(isPasswordToggledVisible(true));
+
+        RecreatedAppCompatActivity activity = mActivityTestRule.getActivity();
+        activity = ActivityUtils.recreateActivity(mActivityTestRule, activity);
+        ActivityUtils.waitForExecution(mActivityTestRule);
+
+        // Check that the password is still toggled to be shown as plain text
+        onView(withId(R.id.textinput_password)).check(isPasswordToggledVisible(true));
+    }
+
     static ViewAssertion isHintExpanded(final boolean expanded) {
         return new ViewAssertion() {
             @Override
@@ -489,4 +540,20 @@
             }
         };
     }
+
+    static ViewAssertion isPasswordToggledVisible(final boolean isToggledVisible) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewFoundException) {
+                assertTrue(view instanceof TextInputLayout);
+                EditText editText = ((TextInputLayout) view).getEditText();
+                TransformationMethod transformationMethod = editText.getTransformationMethod();
+                if (isToggledVisible) {
+                    assertNull(transformationMethod);
+                } else {
+                    assertEquals(PasswordTransformationMethod.getInstance(), transformationMethod);
+                }
+            }
+        };
+    }
 }
diff --git a/design/tests/src/android/support/test/rule/BootlegActivityTestRule.java b/design/tests/src/android/support/test/rule/BootlegActivityTestRule.java
new file mode 100644
index 0000000..b12edfa
--- /dev/null
+++ b/design/tests/src/android/support/test/rule/BootlegActivityTestRule.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.support.test.rule;
+
+import android.app.Activity;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.intercepting.SingleActivityFactory;
+
+/**
+ * An extension of existing {@link ActivityTestRule} that contains a fix for finishActivity()
+ * method (cr/138555678).
+ *
+ * Remove this once we move to Android Test Runner 0.7.
+ */
+public class BootlegActivityTestRule<T extends Activity> extends ActivityTestRule {
+    public BootlegActivityTestRule(Class activityClass) {
+        super(activityClass);
+    }
+
+    public BootlegActivityTestRule(Class<T> activityClass, boolean initialTouchMode) {
+        super(activityClass, initialTouchMode);
+    }
+
+    public BootlegActivityTestRule(Class<T> activityClass, boolean initialTouchMode,
+            boolean launchActivity) {
+        super(activityClass, initialTouchMode, launchActivity);
+    }
+
+    public BootlegActivityTestRule(
+            SingleActivityFactory activityFactory,
+            boolean initialTouchMode, boolean launchActivity) {
+        super(activityFactory, initialTouchMode, launchActivity);
+    }
+
+    public BootlegActivityTestRule(Class<T> activityClass,
+            @NonNull String targetPackage, int launchFlags,
+            boolean initialTouchMode, boolean launchActivity) {
+        super(activityClass, targetPackage, launchFlags, initialTouchMode, launchActivity);
+    }
+
+    @Override
+    public void finishActivity() {
+        super.finishActivity();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Override
+    public T getActivity() {
+        return (T) super.getActivity();
+    }
+}
diff --git a/development/checkstyle/.gitignore b/development/checkstyle/.gitignore
new file mode 100644
index 0000000..d163863
--- /dev/null
+++ b/development/checkstyle/.gitignore
@@ -0,0 +1 @@
+build/
\ No newline at end of file
diff --git a/development/checkstyle/LICENSE b/development/checkstyle/LICENSE
new file mode 100644
index 0000000..c1f5472
--- /dev/null
+++ b/development/checkstyle/LICENSE
@@ -0,0 +1,502 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/development/checkstyle/README b/development/checkstyle/README
new file mode 100644
index 0000000..8f9bf38
--- /dev/null
+++ b/development/checkstyle/README
@@ -0,0 +1,16 @@
+Description:
+Checkstyle is used by developers to validate Java code style before running
+repo upload. This project implements Checkstyle checks specific to the Android
+support library.
+
+Projects used:
+* Name: Checkstyle
+  Description: Checkstyle is a development tool to help programmers write Java
+               code that adheres to a coding standard.
+  URL: http://checkstyle.sourceforge.net/
+  Version: 6.12.1
+  License: LGPL 2.1
+  License File: LICENSE
+  Local Modifications:
+  - The only source file used here is MissingDeprecatedCheck, which was adapted
+    to create MissingRestrictToCheck.
diff --git a/development/checkstyle/build.gradle b/development/checkstyle/build.gradle
new file mode 100644
index 0000000..636fef3
--- /dev/null
+++ b/development/checkstyle/build.gradle
@@ -0,0 +1,22 @@
+apply plugin: 'java'
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
+
+dependencies {
+    compile files(
+            '../../../../prebuilts/checkstyle/checkstyle.jar',
+            '../../../../prebuilts/sdk/current/support/annotations/android-support-annotations.jar')
+}
+
+sourceSets {
+    main.java.srcDir 'src'
+}
+
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
+
+jar {
+    from sourceSets.main.output
+    baseName = "com.android.support.checkstyle"
+    destinationDir = new File("prebuilt")
+}
diff --git a/development/checkstyle/config/support-lib.xml b/development/checkstyle/config/support-lib.xml
new file mode 100644
index 0000000..43bbb82
--- /dev/null
+++ b/development/checkstyle/config/support-lib.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd" [
+    <!ENTITY defaultCopyrightCheck SYSTEM "../../../../../prebuilts/checkstyle/default-copyright-check.xml">
+    <!ENTITY defaultJavadocChecks SYSTEM "../../../../../prebuilts/checkstyle/default-javadoc-checks.xml">
+    <!ENTITY defaultTreewalkerChecks SYSTEM "../../../../../prebuilts/checkstyle/default-treewalker-checks.xml">
+    <!ENTITY defaultModuleChecks SYSTEM "../../../../../prebuilts/checkstyle/default-module-checks.xml">
+    ]>
+
+<module name="Checker">
+    &defaultModuleChecks;
+    &defaultCopyrightCheck;
+    <module name="TreeWalker">
+        &defaultJavadocChecks;
+        &defaultTreewalkerChecks;
+
+        <!-- Custom support library check for @RestrictTo / @hide. -->
+        <module name="com.android.support.checkstyle.MismatchedAnnotationCheck">
+            <property name="severity" value="error" />
+            <property name="tag" value="hide" />
+            <property name="annotation" value="android.support.annotation.RestrictTo" />
+            <property name="messageKey" value="annotation.missing.hide" />
+            <message key="annotation.missing.hide" value="Must include both @RestrictTo annotation and @hide Javadoc tag."/>
+        </module>
+        <!-- Custom support library check for methods with @Test to have test size annotation -->
+        <module name="com.android.support.checkstyle.TestSizeAnnotationCheck">
+            <property name="severity" value="error" />
+        </module>
+    </module>
+</module>
diff --git a/development/checkstyle/gradle/wrapper/gradle-wrapper.jar b/development/checkstyle/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
--- /dev/null
+++ b/development/checkstyle/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/development/checkstyle/gradle/wrapper/gradle-wrapper.properties b/development/checkstyle/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4815c65
--- /dev/null
+++ b/development/checkstyle/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Aug 16 10:43:36 PDT 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=../../../../../../tools/external/gradle/gradle-3.3-bin.zip
diff --git a/development/checkstyle/gradlew b/development/checkstyle/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/development/checkstyle/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/development/checkstyle/prebuilt/com.android.support.checkstyle.jar b/development/checkstyle/prebuilt/com.android.support.checkstyle.jar
new file mode 100644
index 0000000..e3ecdc0
--- /dev/null
+++ b/development/checkstyle/prebuilt/com.android.support.checkstyle.jar
Binary files differ
diff --git a/development/checkstyle/src/com/android/support/checkstyle/MismatchedAnnotationCheck.java b/development/checkstyle/src/com/android/support/checkstyle/MismatchedAnnotationCheck.java
new file mode 100644
index 0000000..a1a60df
--- /dev/null
+++ b/development/checkstyle/src/com/android/support/checkstyle/MismatchedAnnotationCheck.java
@@ -0,0 +1,184 @@
+/*
+ * checkstyle: Checks Java source code for adherence to a set of rules.
+ * Copyright (C) 2001-2016 the original author or authors.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package com.android.support.checkstyle;
+
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TextBlock;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.utils.AnnotationUtility;
+import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to verify that both an annotation and javadoc tag are
+ * present when either one is present.
+ * <p>
+ * Typically, both ways of flagging APIs serve their own purposes. Annotations
+ * are used for compilers and development tools, while javadoc tags are used
+ * for documentation.
+ * <p>
+ * In some cases, the presence of an annotations implies the presence of a
+ * javadoc tag (or vice versa). For example, in the case of the
+ * {@literal @}Deprecated annotation, the {@literal @}deprecated tag should
+ * also be present. In the case of the {@literal @}RestrictTo tag, the
+ * {@literal @}hide tag should also be present.
+ * <p>
+ * To configure this check, do the following:
+ * <pre>
+ *     &lt;module name="MismatchedAnnotationCheck"&gt;
+ *       &lt;property name="tag" value="hide" /&gt;
+ *       &lt;property name="annotation" value="android.support.annotation.RestrictTo" /&gt;
+ *       &lt;property name="messageKey" value="annotation.missing.hide" /&gt;
+ *       &lt;message key="annotation.missing.hide"
+ *                   value="Must include both {@literal @}RestrictTo annotation
+ *                          and {@literal @}hide Javadoc tag." /&gt;
+ *     &lt;/module&gt;
+ * </pre>
+ */
+@SuppressWarnings("unused")
+public final class MismatchedAnnotationCheck extends AbstractCheck {
+
+    /** Key for the warning message text by check properties. */
+    private static final String MSG_KEY_JAVADOC_DUPLICATE_TAG = "javadoc.duplicateTag";
+
+    /** Key for the warning message text by check properties. */
+    private static final String MSG_KEY_JAVADOC_MISSING = "javadoc.missing";
+
+    /** Javadoc tag. */
+    private String mTag;
+
+    /** Pattern for matching javadoc tag. */
+    private Pattern mMatchTag;
+
+    /** Simple annotation name. */
+    private String mAnnotationSimpleName;
+
+    /** Fully-qualified annotation name. */
+    private String mAnnotation;
+
+    /** Key for the warning message text specified by check properties. */
+    private String mMessageKey;
+
+    @Override
+    public int[] getDefaultTokens() {
+        return getAcceptableTokens();
+    }
+
+    /**
+     * Sets javadoc tag.
+     *
+     * @param tag javadoc tag to check
+     */
+    @SuppressWarnings("unused")
+    public void setTag(String tag) {
+        mTag = tag;
+
+        // Tag may either have a description or be on a line by itself.
+        mMatchTag = CommonUtils.createPattern("@" + tag + "(?:\\s|$)");
+    }
+
+    /**
+     * Sets annotation tag.
+     *
+     * @param annotation annotation to check
+     */
+    @SuppressWarnings("unused")
+    public void setAnnotation(String annotation) {
+        mAnnotation = annotation;
+
+        // Extract the simple class name.
+        final int lastDollar = annotation.lastIndexOf('$');
+        final int lastSep = lastDollar >= 0 ? lastDollar : annotation.lastIndexOf('.');
+        mAnnotationSimpleName = annotation.substring(lastSep + 1);
+    }
+
+
+    /**
+     * Sets annotation tag.
+     *
+     * @param messageKey key to use for failed check message
+     */
+    @SuppressWarnings("unused")
+    public void setMessageKey(String messageKey) {
+        mMessageKey = messageKey;
+    }
+
+    @Override
+    public int[] getAcceptableTokens() {
+        return new int[] {
+                TokenTypes.INTERFACE_DEF,
+                TokenTypes.CLASS_DEF,
+                TokenTypes.ANNOTATION_DEF,
+                TokenTypes.ENUM_DEF,
+                TokenTypes.METHOD_DEF,
+                TokenTypes.CTOR_DEF,
+                TokenTypes.VARIABLE_DEF,
+                TokenTypes.ENUM_CONSTANT_DEF,
+                TokenTypes.ANNOTATION_FIELD_DEF,
+        };
+    }
+
+    @Override
+    public int[] getRequiredTokens() {
+        return getAcceptableTokens();
+    }
+
+    @Override
+    public void visitToken(final DetailAST ast) {
+        final boolean containsAnnotation =
+                AnnotationUtility.containsAnnotation(ast, mAnnotationSimpleName)
+                        || AnnotationUtility.containsAnnotation(ast, mAnnotation);
+        final boolean containsJavadocTag = containsJavadocTag(ast);
+        if (containsAnnotation ^ containsJavadocTag) {
+            log(ast.getLineNo(), mMessageKey);
+        }
+    }
+
+    /**
+     * Checks to see if the text block contains the tag.
+     *
+     * @param ast the AST being visited
+     * @return true if contains the tag
+     */
+    private boolean containsJavadocTag(final DetailAST ast) {
+        final TextBlock javadoc = getFileContents().getJavadocBefore(ast.getLineNo());
+        if (javadoc == null) {
+            return false;
+        }
+
+        int currentLine = javadoc.getStartLineNo();
+        boolean found = false;
+
+        final String[] lines = javadoc.getText();
+        for (String line : lines) {
+            if (mMatchTag.matcher(line).find()) {
+                if (found) {
+                    log(currentLine, MSG_KEY_JAVADOC_DUPLICATE_TAG, mTag);
+                }
+                found = true;
+            }
+            currentLine++;
+        }
+
+        return found;
+    }
+}
diff --git a/development/checkstyle/src/com/android/support/checkstyle/TestSizeAnnotationCheck.java b/development/checkstyle/src/com/android/support/checkstyle/TestSizeAnnotationCheck.java
new file mode 100644
index 0000000..60c745a
--- /dev/null
+++ b/development/checkstyle/src/com/android/support/checkstyle/TestSizeAnnotationCheck.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package com.android.support.checkstyle;
+
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.utils.AnnotationUtility;
+
+/**
+ * A check that verifies that all the methods marked with @Test have a matching test size
+ * annotation such as @SmallTest, @MediumTest or @LargeTest. This is needed to make sure
+ * that newly added tests get run in the automatted test runner.
+ */
+public class TestSizeAnnotationCheck extends AbstractCheck {
+    private static final String SMALL_TEST = "SmallTest";
+    private static final String MEDIUM_TEST = "MediumTest";
+    private static final String LARGE_TEST = "LargeTest";
+    private static final String TEST = "Test";
+
+    private static final String MESSAGE = "Method with @Test annotation must have a @SmallTest, "
+            + "@MediumTest, or @LargeTest annotation. See https://goo.gl/c2I0WP for recommended "
+            + "timeouts";
+
+    @Override
+    public int[] getDefaultTokens() {
+        return new int[]{TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF};
+    }
+
+    @Override
+    public void visitToken(DetailAST ast) {
+        final boolean classHasTestSizeAnnotation =
+                AnnotationUtility.containsAnnotation(ast, SMALL_TEST)
+                        || AnnotationUtility.containsAnnotation(ast, MEDIUM_TEST)
+                        || AnnotationUtility.containsAnnotation(ast, LARGE_TEST);
+
+        DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
+        for (DetailAST child = objBlock.getFirstChild();
+                child != null; child = child.getNextSibling()) {
+            if (child.getType() == TokenTypes.METHOD_DEF
+                    && AnnotationUtility.containsAnnotation(child, TEST)) {
+                final boolean methodHasTestSizeAnnotation =
+                        AnnotationUtility.containsAnnotation(child, SMALL_TEST)
+                                || AnnotationUtility.containsAnnotation(child, MEDIUM_TEST)
+                                || AnnotationUtility.containsAnnotation(child, LARGE_TEST);
+                if (!classHasTestSizeAnnotation && !methodHasTestSizeAnnotation) {
+                    log(child.getLineNo(), MESSAGE);
+                }
+            }
+        }
+    }
+}
diff --git a/development/keystore/debug.keystore b/development/keystore/debug.keystore
new file mode 100644
index 0000000..3eace72
--- /dev/null
+++ b/development/keystore/debug.keystore
Binary files differ
diff --git a/development/refaster/IsAtLeastO.java b/development/refaster/IsAtLeastO.java
new file mode 100644
index 0000000..9198441
--- /dev/null
+++ b/development/refaster/IsAtLeastO.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+import android.os.Build.VERSION;
+import android.support.v4.os.BuildCompat;
+
+import com.google.errorprone.refaster.annotation.AfterTemplate;
+import com.google.errorprone.refaster.annotation.AlsoNegation;
+import com.google.errorprone.refaster.annotation.BeforeTemplate;
+
+/**
+ * Replace usages of BuildCompat.isAtLeastO() with SDK_INT check.
+ */
+public class IsAtLeastO {
+    @BeforeTemplate
+    boolean usingAtLeastO() {
+        return BuildCompat.isAtLeastO();
+    }
+
+    @AfterTemplate
+    @AlsoNegation
+    boolean optimizedMethod() {
+        return VERSION.SDK_INT >= 26;
+    }
+}
diff --git a/development/refaster/README b/development/refaster/README
new file mode 100644
index 0000000..daf8f0d
--- /dev/null
+++ b/development/refaster/README
@@ -0,0 +1,25 @@
+Author: aurimas@google.com
+Updated: 6/6/2017
+
+Instructions on how to compile and apply refaster rules to support library
+
+0. Download error-prone and refaster jars
+http://errorprone.info/docs/refaster will have up to date instructions
+
+1. Compile the refaster rule (in this example IsAtLeastO.java)
+java -cp /path/to/android.jar:/path/to/support-compat.jar:javac-9-dev-r3297-4.jar:error_prone_refaster-2.0.18.jar com.google.errorprone.refaster.RefasterRuleCompiler IsAtLeastO.java --out `pwd`/myrule.refaster
+
+2. Update build to use the refaster rule
+Add compiler args to error-prone in SupportLibraryPlugin.groovy
+'-XepPatchChecks:refaster:/path/to/refaster/myrule.refaster',
+'-XepPatchLocation:' + project.projectDir
+
+3. Compile support library using the refaster rule
+./gradlew assembleErrorProne
+
+4. Apply patches
+error-prone will produce patch files like "design/error-prone.patch" and to apply them, cd into the
+directory e.g. "design" and then run:
+patch -p0 -u -i error-prone.patch
+
+5. Rules have been applied! Celebrate!
\ No newline at end of file
diff --git a/documents-archive/Android.mk b/documents-archive/Android.mk
deleted file mode 100644
index 32ec7d6..0000000
--- a/documents-archive/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Here is the final static library that apps can link against.
-# Applications that use this library must specify
-#
-#   LOCAL_STATIC_ANDROID_LIBRARIES := \
-#       android-support-documents-archive \
-#       android-support-v4 \
-#       android-support-annotations
-#
-# in their makefiles to include the resources and their dependencies in their package.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-documents-archive
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations \
-    android-support-v4
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/documents-archive/AndroidManifest.xml b/documents-archive/AndroidManifest.xml
deleted file mode 100644
index 2cd0f7a..0000000
--- a/documents-archive/AndroidManifest.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
diff --git a/documents-archive/src/android/support/provider/DocumentArchive.java b/documents-archive/src/android/support/provider/DocumentArchive.java
deleted file mode 100644
index 4b0b0b9..0000000
--- a/documents-archive/src/android/support/provider/DocumentArchive.java
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.graphics.Point;
-import android.media.ExifInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsProvider;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.webkit.MimeTypeMap;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Stack;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * Provides basic implementation for creating, extracting and accessing
- * files within archives exposed by a document provider. The id delimiter
- * must be a character which is not used in document ids generated by the
- * document provider.
- *
- * <p>This class is thread safe.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class DocumentArchive implements Closeable {
-    private static final String TAG = "DocumentArchive";
-
-    private static final String[] DEFAULT_PROJECTION = new String[] {
-            Document.COLUMN_DOCUMENT_ID,
-            Document.COLUMN_DISPLAY_NAME,
-            Document.COLUMN_MIME_TYPE,
-            Document.COLUMN_SIZE,
-            Document.COLUMN_FLAGS
-    };
-
-    private final Context mContext;
-    private final String mDocumentId;
-    private final char mIdDelimiter;
-    private final Uri mNotificationUri;
-    private final ZipFile mZipFile;
-    private final ExecutorService mExecutor;
-    private final Map<String, ZipEntry> mEntries;
-    private final Map<String, List<ZipEntry>> mTree;
-
-    private DocumentArchive(
-            Context context,
-            File file,
-            String documentId,
-            char idDelimiter,
-            @Nullable Uri notificationUri)
-            throws IOException {
-        mContext = context;
-        mDocumentId = documentId;
-        mIdDelimiter = idDelimiter;
-        mNotificationUri = notificationUri;
-        mZipFile = new ZipFile(file);
-        mExecutor = Executors.newSingleThreadExecutor();
-
-        // Build the tree structure in memory.
-        mTree = new HashMap<String, List<ZipEntry>>();
-        mTree.put("/", new ArrayList<ZipEntry>());
-
-        mEntries = new HashMap<String, ZipEntry>();
-        ZipEntry entry;
-        final List<? extends ZipEntry> entries = Collections.list(mZipFile.entries());
-        final Stack<ZipEntry> stack = new Stack<>();
-        for (int i = entries.size() - 1; i >= 0; i--) {
-            entry = entries.get(i);
-            if (entry.isDirectory() != entry.getName().endsWith("/")) {
-                throw new IOException(
-                        "Directories must have a trailing slash, and files must not.");
-            }
-            if (mEntries.containsKey(entry.getName())) {
-                throw new IOException("Multiple entries with the same name are not supported.");
-            }
-            mEntries.put(entry.getName(), entry);
-            if (entry.isDirectory()) {
-                mTree.put(entry.getName(), new ArrayList<ZipEntry>());
-            }
-            stack.push(entry);
-        }
-
-        int delimiterIndex;
-        String parentPath;
-        ZipEntry parentEntry;
-        List<ZipEntry> parentList;
-
-        while (stack.size() > 0) {
-            entry = stack.pop();
-
-            delimiterIndex = entry.getName().lastIndexOf('/', entry.isDirectory()
-                    ? entry.getName().length() - 2 : entry.getName().length() - 1);
-            parentPath =
-                    delimiterIndex != -1 ? entry.getName().substring(0, delimiterIndex) + "/" : "/";
-            parentList = mTree.get(parentPath);
-
-            if (parentList == null) {
-                parentEntry = mEntries.get(parentPath);
-                if (parentEntry == null) {
-                    // The ZIP file doesn't contain all directories leading to the entry.
-                    // It's rare, but can happen in a valid ZIP archive. In such case create a
-                    // fake ZipEntry and add it on top of the stack to process it next.
-                    parentEntry = new ZipEntry(parentPath);
-                    parentEntry.setSize(0);
-                    parentEntry.setTime(entry.getTime());
-                    mEntries.put(parentPath, parentEntry);
-                    stack.push(parentEntry);
-                }
-                parentList = new ArrayList<ZipEntry>();
-                mTree.put(parentPath, parentList);
-            }
-
-            parentList.add(entry);
-        }
-    }
-
-    /**
-     * Creates a DocumentsArchive instance for opening, browsing and accessing
-     * documents within the archive passed as a local file.
-     *
-     * @param context Context of the provider.
-     * @param File Local file containing the archive.
-     * @param documentId ID of the archive document.
-     * @param idDelimiter Delimiter for constructing IDs of documents within the archive.
-     *            The delimiter must never be used for IDs of other documents.
-     * @param Uri notificationUri Uri for notifying that the archive file has changed.
-     * @see createForParcelFileDescriptor(DocumentsProvider, ParcelFileDescriptor, String, char,
-     *          Uri)
-     */
-    public static DocumentArchive createForLocalFile(
-            Context context, File file, String documentId, char idDelimiter,
-            @Nullable Uri notificationUri)
-            throws IOException {
-        return new DocumentArchive(context, file, documentId, idDelimiter, notificationUri);
-    }
-
-    /**
-     * Creates a DocumentsArchive instance for opening, browsing and accessing
-     * documents within the archive passed as a file descriptor.
-     *
-     * <p>Note, that this method should be used only if the document does not exist
-     * on the local storage. A snapshot file will be created, which may be slower
-     * and consume significant resources, in contrast to using
-     * {@see createForLocalFile(Context, File, String, char, Uri}.
-     *
-     * @param context Context of the provider.
-     * @param descriptor File descriptor for the archive's contents.
-     * @param documentId ID of the archive document.
-     * @param idDelimiter Delimiter for constructing IDs of documents within the archive.
-     *            The delimiter must never be used for IDs of other documents.
-     * @param Uri notificationUri Uri for notifying that the archive file has changed.
-     * @see createForLocalFile(Context, File, String, char, Uri)
-     */
-    public static DocumentArchive createForParcelFileDescriptor(
-            Context context, ParcelFileDescriptor descriptor, String documentId,
-            char idDelimiter, @Nullable Uri notificationUri)
-            throws IOException {
-        File snapshotFile = null;
-        try {
-            // Create a copy of the archive, as ZipFile doesn't operate on streams.
-            // Moreover, ZipInputStream would be inefficient for large files on
-            // pipes.
-            snapshotFile = File.createTempFile("android.support.provider.snapshot{",
-                    "}.zip", context.getCacheDir());
-
-            try (
-                final FileOutputStream outputStream =
-                        new ParcelFileDescriptor.AutoCloseOutputStream(
-                                ParcelFileDescriptor.open(
-                                        snapshotFile, ParcelFileDescriptor.MODE_WRITE_ONLY));
-                final ParcelFileDescriptor.AutoCloseInputStream inputStream =
-                        new ParcelFileDescriptor.AutoCloseInputStream(descriptor);
-            ) {
-                final byte[] buffer = new byte[32 * 1024];
-                int bytes;
-                while ((bytes = inputStream.read(buffer)) != -1) {
-                    outputStream.write(buffer, 0, bytes);
-                }
-                outputStream.flush();
-                return new DocumentArchive(context, snapshotFile, documentId, idDelimiter,
-                        notificationUri);
-            }
-        } finally {
-            // On UNIX the file will be still available for processes which opened it, even
-            // after deleting it. Remove it ASAP, as it won't be used by anyone else.
-            if (snapshotFile != null) {
-                snapshotFile.delete();
-            }
-        }
-    }
-
-    /**
-     * Lists child documents of an archive or a directory within an
-     * archive. Must be called only for archives with supported mime type,
-     * or for documents within archives.
-     *
-     * @see DocumentsProvider.queryChildDocuments(String, String[], String)
-     */
-    public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
-            @Nullable String sortOrder) throws FileNotFoundException {
-        final ParsedDocumentId parsedParentId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedParentId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-
-        final String parentPath = parsedParentId.mPath != null ? parsedParentId.mPath : "/";
-        final MatrixCursor result = new MatrixCursor(
-                projection != null ? projection : DEFAULT_PROJECTION);
-        if (mNotificationUri != null) {
-            result.setNotificationUri(mContext.getContentResolver(), mNotificationUri);
-        }
-
-        final List<ZipEntry> parentList = mTree.get(parentPath);
-        if (parentList == null) {
-            throw new FileNotFoundException();
-        }
-        for (final ZipEntry entry : parentList) {
-            addCursorRow(result, entry);
-        }
-        return result;
-    }
-
-    /**
-     * Returns a MIME type of a document within an archive.
-     *
-     * @see DocumentsProvider.getDocumentType(String)
-     */
-    public String getDocumentType(String documentId) throws FileNotFoundException {
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-        return getMimeTypeForEntry(entry);
-    }
-
-    /**
-     * Returns true if a document within an archive is a child or any descendant of the archive
-     * document or another document within the archive.
-     *
-     * @see DocumentsProvider.isChildDocument(String, String)
-     */
-    public boolean isChildDocument(String parentDocumentId, String documentId) {
-        final ParsedDocumentId parsedParentId = ParsedDocumentId.fromDocumentId(
-                parentDocumentId, mIdDelimiter);
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedParentId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath,
-                "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            return false;
-        }
-
-        if (parsedParentId.mPath == null) {
-            // No need to compare paths. Every file in the archive is a child of the archive
-            // file.
-            return true;
-        }
-
-        final ZipEntry parentEntry = mEntries.get(parsedParentId.mPath);
-        if (parentEntry == null || !parentEntry.isDirectory()) {
-            return false;
-        }
-
-        final String parentPath = entry.getName();
-
-        // Add a trailing slash even if it's not a directory, so it's easy to check if the
-        // entry is a descendant.
-        final String pathWithSlash = entry.isDirectory() ? entry.getName() : entry.getName() + "/";
-        return pathWithSlash.startsWith(parentPath) && !parentPath.equals(pathWithSlash);
-    }
-
-    /**
-     * Returns metadata of a document within an archive.
-     *
-     * @see DocumentsProvider.queryDocument(String, String[])
-     */
-    public Cursor queryDocument(String documentId, @Nullable String[] projection)
-            throws FileNotFoundException {
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-
-        final MatrixCursor result = new MatrixCursor(
-                projection != null ? projection : DEFAULT_PROJECTION);
-        if (mNotificationUri != null) {
-            result.setNotificationUri(mContext.getContentResolver(), mNotificationUri);
-        }
-        addCursorRow(result, entry);
-        return result;
-    }
-
-    /**
-     * Opens a file within an archive.
-     *
-     * @see DocumentsProvider.openDocument(String, String, CancellationSignal))
-     */
-    public ParcelFileDescriptor openDocument(
-            String documentId, String mode, @Nullable final CancellationSignal signal)
-            throws FileNotFoundException {
-        Preconditions.checkArgumentEquals("r", mode,
-                "Invalid mode. Only reading \"r\" supported, but got: \"%s\".");
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-
-        ParcelFileDescriptor[] pipe;
-        InputStream inputStream = null;
-        try {
-            pipe = ParcelFileDescriptor.createReliablePipe();
-            inputStream = mZipFile.getInputStream(entry);
-        } catch (IOException e) {
-            if (inputStream != null) {
-                IoUtils.closeQuietly(inputStream);
-            }
-            // Ideally we'd simply throw IOException to the caller, but for consistency
-            // with DocumentsProvider::openDocument, converting it to IllegalStateException.
-            throw new IllegalStateException("Failed to open the document.", e);
-        }
-        final ParcelFileDescriptor outputPipe = pipe[1];
-        final InputStream finalInputStream = inputStream;
-        mExecutor.execute(
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        try (final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
-                                new ParcelFileDescriptor.AutoCloseOutputStream(outputPipe)) {
-                            try {
-                                final byte buffer[] = new byte[32 * 1024];
-                                int bytes;
-                                while ((bytes = finalInputStream.read(buffer)) != -1) {
-                                    if (Thread.interrupted()) {
-                                        throw new InterruptedException();
-                                    }
-                                    if (signal != null) {
-                                        signal.throwIfCanceled();
-                                    }
-                                    outputStream.write(buffer, 0, bytes);
-                                }
-                            } catch (IOException | InterruptedException e) {
-                                // Catch the exception before the outer try-with-resource closes the
-                                // pipe with close() instead of closeWithError().
-                                try {
-                                    outputPipe.closeWithError(e.getMessage());
-                                } catch (IOException e2) {
-                                    Log.e(TAG, "Failed to close the pipe after an error.", e2);
-                                }
-                            }
-                        } catch (OperationCanceledException e) {
-                            // Cancelled gracefully.
-                        } catch (IOException e) {
-                            Log.e(TAG, "Failed to close the output stream gracefully.", e);
-                        } finally {
-                            IoUtils.closeQuietly(finalInputStream);
-                        }
-                    }
-                });
-
-        return pipe[0];
-    }
-
-    /**
-     * Opens a thumbnail of a file within an archive.
-     *
-     * @see DocumentsProvider.openDocumentThumbnail(String, Point, CancellationSignal))
-     */
-    public AssetFileDescriptor openDocumentThumbnail(
-            String documentId, Point sizeHint, final CancellationSignal signal)
-            throws FileNotFoundException {
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-        Preconditions.checkArgument(getDocumentType(documentId).startsWith("image/"),
-                "Thumbnails only supported for image/* MIME type.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-
-        InputStream inputStream = null;
-        try {
-            inputStream = mZipFile.getInputStream(entry);
-            final ExifInterface exif = new ExifInterface(inputStream);
-            if (exif.hasThumbnail()) {
-                Bundle extras = null;
-                switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
-                    case ExifInterface.ORIENTATION_ROTATE_90:
-                        extras = new Bundle(1);
-                        extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 90);
-                        break;
-                    case ExifInterface.ORIENTATION_ROTATE_180:
-                        extras = new Bundle(1);
-                        extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 180);
-                        break;
-                    case ExifInterface.ORIENTATION_ROTATE_270:
-                        extras = new Bundle(1);
-                        extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 270);
-                        break;
-                }
-                final long[] range = exif.getThumbnailRange();
-                return new AssetFileDescriptor(
-                        openDocument(documentId, "r", signal), range[0], range[1], extras);
-            }
-        } catch (IOException e) {
-            // Ignore the exception, as reading the EXIF may legally fail.
-            Log.e(TAG, "Failed to obtain thumbnail from EXIF.", e);
-        } finally {
-            IoUtils.closeQuietly(inputStream);
-        }
-
-        return new AssetFileDescriptor(
-                openDocument(documentId, "r", signal), 0, entry.getSize(), null);
-    }
-
-    /**
-     * Schedules a gracefully close of the archive after any opened files are closed.
-     *
-     * <p>This method does not block until shutdown. Once called, other methods should not be
-     * called.
-     */
-    @Override
-    public void close() {
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                IoUtils.closeQuietly(mZipFile);
-            }
-        });
-        mExecutor.shutdown();
-    }
-
-    private void addCursorRow(MatrixCursor cursor, ZipEntry entry) {
-        final MatrixCursor.RowBuilder row = cursor.newRow();
-        final ParsedDocumentId parsedId = new ParsedDocumentId(mDocumentId, entry.getName());
-        row.add(Document.COLUMN_DOCUMENT_ID, parsedId.toDocumentId(mIdDelimiter));
-
-        final File file = new File(entry.getName());
-        row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
-        row.add(Document.COLUMN_SIZE, entry.getSize());
-
-        final String mimeType = getMimeTypeForEntry(entry);
-        row.add(Document.COLUMN_MIME_TYPE, mimeType);
-
-        final int flags = mimeType.startsWith("image/") ? Document.FLAG_SUPPORTS_THUMBNAIL : 0;
-        row.add(Document.COLUMN_FLAGS, flags);
-    }
-
-    private String getMimeTypeForEntry(ZipEntry entry) {
-        if (entry.isDirectory()) {
-            return Document.MIME_TYPE_DIR;
-        }
-
-        final int lastDot = entry.getName().lastIndexOf('.');
-        if (lastDot >= 0) {
-            final String extension = entry.getName().substring(lastDot + 1).toLowerCase(Locale.US);
-            final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
-            if (mimeType != null) {
-                return mimeType;
-            }
-        }
-
-        return "application/octet-stream";
-    }
-};
diff --git a/documents-archive/src/android/support/provider/DocumentArchiveHelper.java b/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
deleted file mode 100644
index 6c5e198..0000000
--- a/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.res.AssetFileDescriptor;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsProvider;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.util.LruCache;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Provides basic implementation for creating, extracting and accessing
- * files within archives exposed by a document provider.
- *
- * <p>This class is thread safe. All methods can be called on any thread without
- * synchronization.
- *
- * TODO: Update the documentation. b/26047732
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class DocumentArchiveHelper implements Closeable {
-    /**
-     * Cursor column to be used for passing the local file path for documents.
-     * If it's not specified, then a snapshot will be created, which is slower
-     * and consumes more resources.
-     *
-     * <p>Type: STRING
-     */
-    public static final String COLUMN_LOCAL_FILE_PATH = "local_file_path";
-
-    private static final String TAG = "DocumentArchiveHelper";
-    private static final int OPENED_ARCHIVES_CACHE_SIZE = 4;
-    private static final String[] ZIP_MIME_TYPES = {
-            "application/zip", "application/x-zip", "application/x-zip-compressed"
-    };
-
-    private final DocumentsProvider mProvider;
-    private final char mIdDelimiter;
-
-    // @GuardedBy("mArchives")
-    private final LruCache<String, Loader> mArchives =
-            new LruCache<String, Loader>(OPENED_ARCHIVES_CACHE_SIZE) {
-                @Override
-                public void entryRemoved(boolean evicted, String key,
-                        Loader oldValue, Loader newValue) {
-                    oldValue.getWriteLock().lock();
-                    try {
-                        oldValue.get().close();
-                    } catch (FileNotFoundException e) {
-                        Log.e(TAG, "Failed to close an archive as it no longer exists.");
-                    } finally {
-                        oldValue.getWriteLock().unlock();
-                    }
-                }
-            };
-
-    /**
-     * Creates a helper for handling archived documents.
-     *
-     * @param provider Instance of a documents provider which provides archived documents.
-     * @param idDelimiter A character used to create document IDs within archives. Can be any
-     *            character which is not used in any other document ID. If your provider uses
-     *            numbers as document IDs, the delimiter can be eg. a colon. However if your
-     *            provider uses paths, then a delimiter can be any character not allowed in the
-     *            path, which is often \0.
-     */
-    public DocumentArchiveHelper(DocumentsProvider provider, char idDelimiter) {
-        mProvider = provider;
-        mIdDelimiter = idDelimiter;
-    }
-
-    /**
-     * Lists child documents of an archive or a directory within an
-     * archive. Must be called only for archives with supported mime type,
-     * or for documents within archives.
-     *
-     * @see DocumentsProvider.queryChildDocuments(String, String[], String)
-     */
-    public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
-            @Nullable String sortOrder)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().queryChildDocuments(documentId, projection, sortOrder);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns a MIME type of a document within an archive.
-     *
-     * @see DocumentsProvider.getDocumentType(String)
-     */
-    public String getDocumentType(String documentId) throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().getDocumentType(documentId);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns true if a document within an archive is a child or any descendant of the archive
-     * document or another document within the archive.
-     *
-     * @see DocumentsProvider.isChildDocument(String, String)
-     */
-    public boolean isChildDocument(String parentDocumentId, String documentId) {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().isChildDocument(parentDocumentId, documentId);
-        } catch (FileNotFoundException e) {
-            throw new IllegalStateException(e);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns metadata of a document within an archive.
-     *
-     * @see DocumentsProvider.queryDocument(String, String[])
-     */
-    public Cursor queryDocument(String documentId, @Nullable String[] projection)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().queryDocument(documentId, projection);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Opens a file within an archive.
-     *
-     * @see DocumentsProvider.openDocument(String, String, CancellationSignal))
-     */
-    public ParcelFileDescriptor openDocument(
-            String documentId, String mode, final CancellationSignal signal)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().openDocument(documentId, mode, signal);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Opens a thumbnail of a file within an archive.
-     *
-     * @see DocumentsProvider.openDocumentThumbnail(String, Point, CancellationSignal))
-     */
-    public AssetFileDescriptor openDocumentThumbnail(
-            String documentId, Point sizeHint, final CancellationSignal signal)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().openDocumentThumbnail(documentId, sizeHint, signal);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns true if the passed document ID is for a document within an archive.
-     */
-    public boolean isArchivedDocument(String documentId) {
-        return ParsedDocumentId.hasPath(documentId, mIdDelimiter);
-    }
-
-    /**
-     * Returns true if the passed mime type is supported by the helper.
-     */
-    public boolean isSupportedArchiveType(String mimeType) {
-        for (final String zipMimeType : ZIP_MIME_TYPES) {
-            if (zipMimeType.equals(mimeType)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Closes the helper and disposes all existing archives. It will block until all ongoing
-     * operations on each opened archive are finished.
-     */
-    @Override
-    public void close() {
-        synchronized (mArchives) {
-            mArchives.evictAll();
-        }
-    }
-
-    /**
-     * Releases resources for an archive with the specified document ID. It will block until all
-     * operations on the archive are finished. If not opened, the method does nothing.
-     *
-     * <p>Calling this method is optional. The helper automatically closes the least recently used
-     * archives if too many archives are opened.
-     *
-     * @param archiveDocumentId ID of the archive file.
-     */
-    public void closeArchive(String documentId) {
-        synchronized (mArchives) {
-            mArchives.remove(documentId);
-        }
-    }
-
-    private Loader obtainInstance(String documentId) throws FileNotFoundException {
-        Loader loader;
-        synchronized (mArchives) {
-            loader = getInstanceUncheckedLocked(documentId);
-            loader.getReadLock().lock();
-        }
-        return loader;
-    }
-
-    private void releaseInstance(@Nullable Loader loader) {
-        if (loader != null) {
-            loader.getReadLock().unlock();
-        }
-    }
-
-    private Loader getInstanceUncheckedLocked(String documentId)
-            throws FileNotFoundException {
-        try {
-            final ParsedDocumentId id = ParsedDocumentId.fromDocumentId(documentId, mIdDelimiter);
-            if (mArchives.get(id.mArchiveId) != null) {
-                return mArchives.get(id.mArchiveId);
-            }
-
-            final Cursor cursor = mProvider.queryDocument(id.mArchiveId, new String[]
-                    { Document.COLUMN_MIME_TYPE, COLUMN_LOCAL_FILE_PATH });
-            cursor.moveToFirst();
-            final String mimeType = cursor.getString(cursor.getColumnIndex(
-                    Document.COLUMN_MIME_TYPE));
-            Preconditions.checkArgument(isSupportedArchiveType(mimeType),
-                    "Unsupported archive type.");
-            final int columnIndex = cursor.getColumnIndex(COLUMN_LOCAL_FILE_PATH);
-            final String localFilePath = columnIndex != -1 ? cursor.getString(columnIndex) : null;
-            final File localFile = localFilePath != null ? new File(localFilePath) : null;
-            final Uri notificationUri = cursor.getNotificationUri();
-            final Loader loader = new Loader(mProvider, localFile, id, mIdDelimiter,
-                    notificationUri);
-
-            // Remove the instance from mArchives collection once the archive file changes.
-            if (notificationUri != null) {
-                final LruCache<String, Loader> finalArchives = mArchives;
-                mProvider.getContext().getContentResolver().registerContentObserver(notificationUri,
-                        false,
-                        new ContentObserver(null) {
-                            @Override
-                            public void onChange(boolean selfChange, Uri uri) {
-                                synchronized (mArchives) {
-                                    final Loader currentLoader = mArchives.get(id.mArchiveId);
-                                    if (currentLoader == loader) {
-                                        mArchives.remove(id.mArchiveId);
-                                    }
-                                }
-                            }
-                        });
-            }
-
-            mArchives.put(id.mArchiveId, loader);
-            return loader;
-        } catch (IOException e) {
-            // DocumentsProvider doesn't use IOException. For consistency convert it to
-            // IllegalStateException.
-            throw new IllegalStateException(e);
-        }
-    }
-
-    /**
-     * Loads an instance of DocumentArchive lazily.
-     */
-    private static final class Loader {
-        private final DocumentsProvider mProvider;
-        private final File mLocalFile;
-        private final ParsedDocumentId mId;
-        private final char mIdDelimiter;
-        private final Uri mNotificationUri;
-        private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
-        private DocumentArchive mArchive = null;
-
-        Loader(DocumentsProvider provider, @Nullable File localFile, ParsedDocumentId id,
-                char idDelimiter, Uri notificationUri) {
-            this.mProvider = provider;
-            this.mLocalFile = localFile;
-            this.mId = id;
-            this.mIdDelimiter = idDelimiter;
-            this.mNotificationUri = notificationUri;
-        }
-
-        synchronized DocumentArchive get() throws FileNotFoundException {
-            if (mArchive != null) {
-                return mArchive;
-            }
-
-            try {
-                if (mLocalFile != null) {
-                    mArchive = DocumentArchive.createForLocalFile(
-                            mProvider.getContext(), mLocalFile, mId.mArchiveId, mIdDelimiter,
-                            mNotificationUri);
-                } else {
-                    mArchive = DocumentArchive.createForParcelFileDescriptor(
-                            mProvider.getContext(),
-                            mProvider.openDocument(mId.mArchiveId, "r", null /* signal */),
-                            mId.mArchiveId, mIdDelimiter, mNotificationUri);
-                }
-            } catch (IOException e) {
-                throw new IllegalStateException(e);
-            }
-
-            return mArchive;
-        }
-
-        Lock getReadLock() {
-            return mLock.readLock();
-        }
-
-        Lock getWriteLock() {
-            return mLock.writeLock();
-        }
-    }
-}
diff --git a/documents-archive/src/android/support/provider/IoUtils.java b/documents-archive/src/android/support/provider/IoUtils.java
deleted file mode 100644
index 55c293d..0000000
--- a/documents-archive/src/android/support/provider/IoUtils.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-
-import java.io.Closeable;
-import java.io.InputStream;
-
-/**
- * Simple static methods to perform common IO operations.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-final class IoUtils {
-    static void closeQuietly(@Nullable Closeable closeable) {
-       if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException e) {
-                throw e;
-            } catch (Exception e) {
-                // Ignore.
-            }
-        }
-    }
-
-    static void closeQuietly(@Nullable InputStream stream) {
-       if (stream != null) {
-            try {
-                stream.close();
-            } catch (RuntimeException e) {
-                throw e;
-            } catch (Exception e) {
-                // Ignore.
-            }
-        }
-    }
-}
diff --git a/documents-archive/src/android/support/provider/ParsedDocumentId.java b/documents-archive/src/android/support/provider/ParsedDocumentId.java
deleted file mode 100644
index a8c7810..0000000
--- a/documents-archive/src/android/support/provider/ParsedDocumentId.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-class ParsedDocumentId {
-    public final String mArchiveId;
-    public final String mPath;
-
-    public ParsedDocumentId(String archiveId, String path) {
-        mArchiveId = archiveId;
-        mPath = path;
-    }
-
-    static public ParsedDocumentId fromDocumentId(String documentId, char idDelimiter) {
-        final int delimiterPosition = documentId.indexOf(idDelimiter);
-        if (delimiterPosition == -1) {
-            return new ParsedDocumentId(documentId, null);
-        } else {
-            return new ParsedDocumentId(documentId.substring(0, delimiterPosition),
-                    documentId.substring((delimiterPosition + 1)));
-        }
-    }
-
-    static public boolean hasPath(String documentId, char idDelimiter) {
-        return documentId.indexOf(idDelimiter) != -1;
-    }
-
-    public String toDocumentId(char idDelimiter) {
-        if (mPath == null) {
-            return mArchiveId;
-        } else {
-            return mArchiveId + idDelimiter + mPath;
-        }
-    }
-};
diff --git a/documents-archive/src/android/support/provider/Preconditions.java b/documents-archive/src/android/support/provider/Preconditions.java
deleted file mode 100644
index e3971aa..0000000
--- a/documents-archive/src/android/support/provider/Preconditions.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.text.TextUtils;
-
-/**
- * Simple static methods to be called at the start of your own methods to verify
- * correct arguments and state.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-final class Preconditions {
-    static void checkArgument(boolean expression, String message) {
-        if (!expression) {
-            throw new IllegalArgumentException(message);
-        }
-    }
-
-    static void checkArgumentNotNull(Object object, String message) {
-        if (object == null) {
-            throw new IllegalArgumentException(message);
-        }
-    }
-
-    static void checkArgumentEquals(String expected, @Nullable String actual, String message) {
-        if (!TextUtils.equals(expected, actual)) {
-            throw new IllegalArgumentException(String.format(message, String.valueOf(expected),
-                    String.valueOf(actual)));
-        }
-    }
-
-    static void checkState(boolean expression, String message) {
-        if (!expression) {
-            throw new IllegalStateException(message);
-        }
-    }
-}
diff --git a/documents-archive/tests/Android.mk b/documents-archive/tests/Android.mk
deleted file mode 100644
index 84cc3c3..0000000
--- a/documents-archive/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    android-support-documents-archive
-LOCAL_AAPT_FLAGS := --auto-add-overlay -0 zip
-LOCAL_PACKAGE_NAME := AndroidSupportDocumentsArchiveTests
-
-include $(BUILD_PACKAGE)
diff --git a/documents-archive/tests/AndroidManifest.xml b/documents-archive/tests/AndroidManifest.xml
deleted file mode 100644
index 47da733..0000000
--- a/documents-archive/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="android.support.provider.tests">
-    <application>
-        <uses-library android:name="android.test.runner" />
-        <provider
-            android:name="android.support.provider.tests.StubProvider"
-            android:authorities="android.support.provider.tests.mystubprovider"
-            android:grantUriPermissions="true"
-            android:exported="true"
-            android:permission="android.permission.MANAGE_DOCUMENTS" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="android.support.provider.tests"
-        android:label="Tests for android.support.provider." />
-</manifest>
diff --git a/documents-archive/tests/res/raw/archive.zip b/documents-archive/tests/res/raw/archive.zip
deleted file mode 100644
index c3b8d22..0000000
--- a/documents-archive/tests/res/raw/archive.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/res/raw/empty_dirs.zip b/documents-archive/tests/res/raw/empty_dirs.zip
deleted file mode 100644
index 1dd2251..0000000
--- a/documents-archive/tests/res/raw/empty_dirs.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/res/raw/no_dirs.zip b/documents-archive/tests/res/raw/no_dirs.zip
deleted file mode 100644
index e178ae1..0000000
--- a/documents-archive/tests/res/raw/no_dirs.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java b/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
deleted file mode 100644
index 9845412..0000000
--- a/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider.tests;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.support.provider.DocumentArchive;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-
-/**
- * Tests for DocumentArchive.
- */
-public class DocumentArchiveTest extends AndroidTestCase {
-    private static final String DOCUMENT_ID = "document-id";
-    private static final char DELIMITER = ':';
-    private static final String NOTIFICATION_URI = "content://notification-uri";
-    private DocumentArchive mArchive = null;
-
-    public void loadArchive(int resource) {
-        // Extract the file from resources.
-        File file = null;
-        try {
-            file = File.createTempFile("android.support.provider.tests{",
-                    "}.zip", mContext.getCacheDir());
-            try (
-                final FileOutputStream outputStream =
-                        new ParcelFileDescriptor.AutoCloseOutputStream(
-                                ParcelFileDescriptor.open(
-                                        file, ParcelFileDescriptor.MODE_WRITE_ONLY));
-                final InputStream inputStream =
-                        mContext.getResources().openRawResource(resource);
-            ) {
-                final byte[] buffer = new byte[32 * 1024];
-                int bytes;
-                while ((bytes = inputStream.read(buffer)) != -1) {
-                    outputStream.write(buffer, 0, bytes);
-                }
-                outputStream.flush();
-                mArchive = DocumentArchive.createForLocalFile(
-                      mContext,
-                      file,
-                      DOCUMENT_ID,
-                      DELIMITER,
-                      Uri.parse(NOTIFICATION_URI));
-
-            }
-        } catch (IOException e) {
-            fail(String.valueOf(e));
-        } finally {
-            // On UNIX the file will be still available for processes which opened it, even
-            // after deleting it. Remove it ASAP, as it won't be used by anyone else.
-            if (file != null) {
-                file.delete();
-            }
-        }
-    }
-
-    @Override
-    public void tearDown() {
-        if (mArchive != null) {
-            mArchive.close();
-        }
-    }
-
-    public void testQueryChildDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir1/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir1",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertTrue(cursor.moveToNext());
-        assertEquals("document-id:dir2/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir2",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertTrue(cursor.moveToNext());
-        assertEquals("document-id:file1.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("file1.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals("text/plain",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(13,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertFalse(cursor.moveToNext());
-
-        // Check if querying children works too.
-        final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
-        assertTrue(childCursor.moveToFirst());
-        assertEquals("document-id:dir1/cherries.txt",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("cherries.txt",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals("text/plain",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(17,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-    }
-
-    public void testQueryChildDocument_NoDirs() throws IOException {
-        loadArchive(R.raw.no_dirs);
-        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir1/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir1",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(cursor.moveToNext());
-
-        final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
-        assertTrue(childCursor.moveToFirst());
-        assertEquals("document-id:dir1/dir2/",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir2",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(childCursor.moveToNext());
-
-        final Cursor childCursor2 = mArchive.queryChildDocuments(
-                "document-id:dir1/dir2/", null, null);
-
-        assertTrue(childCursor2.moveToFirst());
-        assertEquals("document-id:dir1/dir2/cherries.txt",
-                childCursor2.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertFalse(childCursor2.moveToNext());
-    }
-
-    public void testQueryChildDocument_EmptyDirs() throws IOException {
-        loadArchive(R.raw.empty_dirs);
-        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir1/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir1",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(cursor.moveToNext());
-
-        final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
-        assertTrue(childCursor.moveToFirst());
-        assertEquals("document-id:dir1/dir2/",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir2",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertTrue(childCursor.moveToNext());
-        assertEquals("document-id:dir1/dir3/",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir3",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(cursor.moveToNext());
-
-        final Cursor childCursor2 = mArchive.queryChildDocuments(
-                "document-id:dir1/dir2/", null, null);
-        assertFalse(childCursor2.moveToFirst());
-
-        final Cursor childCursor3 = mArchive.queryChildDocuments(
-                "document-id:dir1/dir3/", null, null);
-        assertFalse(childCursor3.moveToFirst());
-    }
-
-    public void testGetDocumentType() throws IOException {
-        loadArchive(R.raw.archive);
-        assertEquals(Document.MIME_TYPE_DIR, mArchive.getDocumentType("document-id:dir1/"));
-        assertEquals("text/plain", mArchive.getDocumentType("document-id:file1.txt"));
-    }
-
-    public void testIsChildDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1/"));
-        assertFalse(mArchive.isChildDocument(DOCUMENT_ID, "document-id:this-does-not-exist"));
-        assertTrue(mArchive.isChildDocument("document-id:dir1/", "document-id:dir1/cherries.txt"));
-        assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1/cherries.txt"));
-    }
-
-    public void testQueryDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        final Cursor cursor = mArchive.queryDocument("document-id:dir2/strawberries.txt", null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir2/strawberries.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("strawberries.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals("text/plain",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(21,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-    }
-
-    public void testOpenDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        final ParcelFileDescriptor descriptor = mArchive.openDocument(
-                "document-id:dir2/strawberries.txt", "r", null /* signal */);
-        try (final ParcelFileDescriptor.AutoCloseInputStream inputStream =
-                new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) {
-            assertEquals("I love strawberries!", new Scanner(inputStream).nextLine());
-        }
-    }
-}
diff --git a/documents-archive/tests/src/android/support/provider/IntegrationTest.java b/documents-archive/tests/src/android/support/provider/IntegrationTest.java
deleted file mode 100644
index 0445d82..0000000
--- a/documents-archive/tests/src/android/support/provider/IntegrationTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider.tests;
-
-import android.content.ContentProviderClient;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.IllegalArgumentException;
-import java.util.Scanner;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Integration tests for DocumentsProvider and DocumentArchiveHelper.
- *
- * <p>Only checks if the provider, then helper are forwarding the calls to the
- * underlying {@code ArchiveDocument} correctly. More detailed output testing is
- * done in {@code DocumentArchiveTest}.
- */
-public class IntegrationTest extends AndroidTestCase {
-    private ContentProviderClient mClient;
-
-    @Override
-    public void setUp() throws RemoteException {
-        mClient = getContext().getContentResolver().acquireContentProviderClient(
-                StubProvider.AUTHORITY);
-        assertNotNull(mClient);
-        mClient.call("reset", null, null);
-    }
-
-    @Override
-    public void tearDown() {
-        if (mClient != null) {
-            mClient.release();
-            mClient = null;
-        }
-    }
-
-    public void testQueryForChildren() throws IOException {
-        final Cursor cursor = mContext.getContentResolver().query(
-                DocumentsContract.buildChildDocumentsUri(
-                        StubProvider.AUTHORITY, StubProvider.DOCUMENT_ID),
-                        null, null, null, null);
-        assertEquals(3, cursor.getCount());
-    }
-
-    public void testQueryForDocument_Archive()
-            throws IOException, RemoteException, InterruptedException {
-        final Cursor cursor = mContext.getContentResolver().query(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.DOCUMENT_ID),
-                        null, null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertTrue(cursor.moveToFirst());
-        assertEquals(Document.FLAG_ARCHIVE,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_FLAGS)));
-    }
-
-    public void testQueryForDocument_ArchiveDescendant()
-            throws IOException, RemoteException, InterruptedException {
-        final Cursor cursor = mContext.getContentResolver().query(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
-                        null, null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertEquals(StubProvider.NOTIFY_URI, cursor.getNotificationUri());
-
-        final CountDownLatch changeSignal = new CountDownLatch(1);
-        final ContentObserver observer = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                changeSignal.countDown();
-            }
-        };
-
-        try {
-            getContext().getContentResolver().registerContentObserver(
-                    cursor.getNotificationUri(), false /* notifyForDescendants */, observer);
-
-            // Simulate deleting the archive file, then confirm that the notification is
-            // propagated and the archive closed.
-            mClient.call("delete", null, null);
-            changeSignal.await();
-
-            mContext.getContentResolver().query(
-                    DocumentsContract.buildChildDocumentsUri(
-                            StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
-                            null, null, null, null);
-            fail("Expected IllegalStateException, but succeeded.");
-        } catch (IllegalStateException e) {
-            // Expected, as the file is gone.
-        } finally {
-            getContext().getContentResolver().unregisterContentObserver(observer);
-        }
-    }
-
-    public void testGetType() throws IOException {
-        assertEquals("text/plain", mContext.getContentResolver().getType(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID)));
-    }
-
-    public void testOpenFileDescriptor() throws IOException {
-        final ParcelFileDescriptor descriptor = mContext.getContentResolver().openFileDescriptor(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
-                        "r", null);
-        assertNotNull(descriptor);
-    }
-}
diff --git a/documents-archive/tests/src/android/support/provider/StubProvider.java b/documents-archive/tests/src/android/support/provider/StubProvider.java
deleted file mode 100644
index 3f72cd2..0000000
--- a/documents-archive/tests/src/android/support/provider/StubProvider.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider.tests;
-
-import android.database.Cursor;
-import android.database.MatrixCursor.RowBuilder;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsProvider;
-import android.support.provider.DocumentArchiveHelper;
-import android.util.Log;
-
-import java.io.FileNotFoundException;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Stub provider for testing support for archives.
- */
-public class StubProvider extends DocumentsProvider {
-    public static final String AUTHORITY = "android.support.provider.tests.mystubprovider";
-    public static final String DOCUMENT_ID = "document-id";
-    public static final String FILE_DOCUMENT_ID = "document-id:dir1/cherries.txt";
-    public static final Uri NOTIFY_URI = DocumentsContract.buildRootsUri(AUTHORITY);
-
-    private static final String TAG = "StubProvider";
-    private static final String[] DEFAULT_PROJECTION = new String[] {
-            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_SIZE,
-            Document.COLUMN_MIME_TYPE, Document.COLUMN_FLAGS,
-            DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH
-    };
-
-    public File file;
-    public DocumentArchiveHelper archiveHelper;
-    public boolean simulatedDelete = false;
-
-    @Override
-    public Bundle call(String method, String args, Bundle extras) {
-        switch (method) {
-            case "reset":
-                simulatedDelete = false;
-                getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
-                return null;
-            case "delete":
-                simulatedDelete = true;
-                getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
-                return null;
-            default:
-                return super.call(method, args, extras);
-        }
-    }
-
-    @Override
-    public boolean onCreate() {
-        try {
-            archiveHelper = new DocumentArchiveHelper(this, ':');
-            file = TestUtils.createFileFromResource(getContext(), R.raw.archive);
-            return true;
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to initialize StubProvider.");
-            return false;
-        }
-    }
-
-    @Override
-    public ParcelFileDescriptor openDocument(
-            String documentId, String mode, CancellationSignal signal)
-            throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(documentId)) {
-            return archiveHelper.openDocument(documentId, mode, signal);
-        }
-
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Cursor queryChildDocuments(
-            String parentDocumentId, String[] projection, String sortOrder)
-            throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(parentDocumentId) ||
-                archiveHelper.isSupportedArchiveType(getDocumentType(parentDocumentId))) {
-            return archiveHelper.queryChildDocuments(parentDocumentId, projection, sortOrder);
-        }
-
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Cursor queryDocument(String documentId, String[] projection)
-            throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(documentId)) {
-            return archiveHelper.queryDocument(documentId, projection);
-        }
-
-        if (DOCUMENT_ID.equals(documentId)) {
-            if (simulatedDelete) {
-                throw new FileNotFoundException();
-            }
-
-            final MatrixCursor result = new MatrixCursor(
-                    projection != null ? projection : DEFAULT_PROJECTION);
-            result.setNotificationUri(getContext().getContentResolver(), NOTIFY_URI);
-            final RowBuilder row = result.newRow();
-            row.add(Document.COLUMN_DOCUMENT_ID, DOCUMENT_ID);
-            row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
-            row.add(Document.COLUMN_SIZE, file.length());
-            row.add(Document.COLUMN_MIME_TYPE, "application/zip");
-            final int flags = archiveHelper.isSupportedArchiveType("application/zip")
-                    ? Document.FLAG_ARCHIVE : 0;
-            row.add(Document.COLUMN_FLAGS, flags);
-            row.add(DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH, file.getPath());
-            return result;
-        }
-
-        throw new FileNotFoundException();
-    }
-
-    @Override
-    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String getDocumentType(String documentId) throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(documentId)) {
-            return archiveHelper.getDocumentType(documentId);
-        }
-
-        if (DOCUMENT_ID.equals(documentId)) {
-            return "application/zip";
-        }
-
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/documents-archive/tests/src/android/support/provider/TestUtils.java b/documents-archive/tests/src/android/support/provider/TestUtils.java
deleted file mode 100644
index 17ec3e1..0000000
--- a/documents-archive/tests/src/android/support/provider/TestUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.provider.tests;
-
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Utilities for tests.
- */
-final class TestUtils {
-    /**
-     * Saves a file from resources to a temporary location and returns a File instance for it.
-     *
-     * @param id Resource ID
-     */
-    static File createFileFromResource(Context context, int id) throws IOException {
-        final File file = File.createTempFile("android.support.provider.tests{",
-                "}.zip", context.getCacheDir());
-        try (
-            final FileOutputStream outputStream =
-                    new ParcelFileDescriptor.AutoCloseOutputStream(
-                            ParcelFileDescriptor.open(
-                                    file, ParcelFileDescriptor.MODE_WRITE_ONLY));
-            final InputStream inputStream = context.getResources().openRawResource(id);
-        ) {
-            final byte[] buffer = new byte[32 * 1024];
-            int bytes;
-            while ((bytes = inputStream.read(buffer)) != -1) {
-                outputStream.write(buffer, 0, bytes);
-            }
-            outputStream.flush();
-            return file;
-        }
-    }
-}
diff --git a/droiddoc.mk b/droiddoc.mk
new file mode 100644
index 0000000..0913f5f
--- /dev/null
+++ b/droiddoc.mk
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+# API Level information for the Support Library, which is currently
+# included as part of the core framework docs build.
+SUPPORT_PATH := $(call my-dir)
+
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+    -since $(SUPPORT_PATH)/api/22.0.0.txt 22.0.0 \
+    -since $(SUPPORT_PATH)/api/22.1.0.txt 22.1.0 \
+    -since $(SUPPORT_PATH)/api/22.2.0.txt 22.2.0 \
+    -since $(SUPPORT_PATH)/api/22.2.1.txt 22.2.1 \
+    -since $(SUPPORT_PATH)/api/23.0.0.txt 23.0.0 \
+    -since $(SUPPORT_PATH)/api/23.1.0.txt 23.1.0 \
+    -since $(SUPPORT_PATH)/api/23.1.1.txt 23.1.1 \
+    -since $(SUPPORT_PATH)/api/23.2.0.txt 23.2.0 \
+    -since $(SUPPORT_PATH)/api/23.2.1.txt 23.2.1 \
+    -since $(SUPPORT_PATH)/api/23.4.0.txt 23.4.0 \
+    -since $(SUPPORT_PATH)/api/24.0.0.txt 24.0.0 \
+    -since $(SUPPORT_PATH)/api/24.1.0.txt 24.1.0 \
+    -since $(SUPPORT_PATH)/api/24.2.0.txt 24.2.0 \
+    -since $(SUPPORT_PATH)/api/25.0.0.txt 25.0.0 \
+    -since $(SUPPORT_PATH)/api/25.1.0.txt 25.1.0 \
+    -since $(SUPPORT_PATH)/api/25.2.0.txt 25.2.0 \
+    -since $(SUPPORT_PATH)/api/25.3.0.txt 25.3.0 \
+    -since $(SUPPORT_PATH)/api/25.4.0.txt 25.4.0 \
+    -since $(SUPPORT_PATH)/api/26.0.0-alpha1.txt 26.0.0-alpha1 \
+    -since $(SUPPORT_PATH)/api/26.0.0-beta1.txt 26.0.0-beta1 \
+    -since $(SUPPORT_PATH)/api/26.0.0-beta2.txt 26.0.0-beta2
diff --git a/dynamic-animation/Android.mk b/dynamic-animation/Android.mk
index b8ba139..1b33094 100644
--- a/dynamic-animation/Android.mk
+++ b/dynamic-animation/Android.mk
@@ -20,7 +20,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
diff --git a/dynamic-animation/AndroidManifest-make.xml b/dynamic-animation/AndroidManifest-make.xml
deleted file mode 100644
index bfe97cc..0000000
--- a/dynamic-animation/AndroidManifest-make.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index 907fff8..d51c8b9 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -1,110 +1,32 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-dynamic-animation'
 
 dependencies {
-    compile project(':support-core-utils')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
-        exclude module: 'support-annotations'
-    }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
-        exclude module: 'support-annotations'
-    }
+    api project(':support-core-utils')
 
-    testCompile 'junit:junit:4.12'
-    testCompile "org.mockito:mockito-core:1.9.5"
-
-    testCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
+    androidTestImplementation (libs.espresso_core) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
         minSdkVersion 16
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
         main.res.srcDirs 'res', 'res-public'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
-
-    lintOptions {
-        // TODO: fix errors and reenable.
-        abortOnError false
-    }
-
-    packagingOptions {
-        exclude 'LICENSE.txt'
-    }
-
-    testOptions {
-        unitTests.returnDefaultValues = true
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support DynamicAnimation v16'
-                description "Physics-based animation in support library, where the animations are driven by physics force. You can use this Animation library to create smooth and realistic animations."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2017'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support DynamicAnimation'
+    inceptionYear '2017'
+    description "Physics-based animation in support library, where the animations are driven by physics force. You can use this Animation library to create smooth and realistic animations."
 }
\ No newline at end of file
diff --git a/dynamic-animation/lint-baseline.xml b/dynamic-animation/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/dynamic-animation/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/dynamic-animation/src/android/support/animation/AnimationHandler.java b/dynamic-animation/src/android/support/animation/AnimationHandler.java
index fac9cd2..e4cf7c7 100644
--- a/dynamic-animation/src/android/support/animation/AnimationHandler.java
+++ b/dynamic-animation/src/android/support/animation/AnimationHandler.java
@@ -178,7 +178,7 @@
     /**
      * Default provider of timing pulse that uses Choreographer for frame callbacks.
      */
-    private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
+    private static class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
 
         final Choreographer mChoreographer = Choreographer.getInstance();
 
diff --git a/dynamic-animation/src/android/support/animation/DynamicAnimation.java b/dynamic-animation/src/android/support/animation/DynamicAnimation.java
index 2895d80..8ea48b9 100644
--- a/dynamic-animation/src/android/support/animation/DynamicAnimation.java
+++ b/dynamic-animation/src/android/support/animation/DynamicAnimation.java
@@ -16,9 +16,9 @@
 
 package android.support.animation;
 
-import android.os.Build;
 import android.os.Looper;
 import android.support.annotation.FloatRange;
+import android.support.v4.view.ViewCompat;
 import android.util.AndroidRuntimeException;
 import android.view.View;
 
@@ -85,18 +85,12 @@
     public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") {
         @Override
         public void setValue(View view, float value) {
-            if (isZSupported()) {
-                view.setTranslationZ(value);
-            }
+            ViewCompat.setTranslationZ(view, value);
         }
 
         @Override
         public float getValue(View view) {
-            if (isZSupported()) {
-                return view.getTranslationZ();
-            } else {
-                return 0;
-            }
+            return ViewCompat.getTranslationZ(view);
         }
     };
 
@@ -211,18 +205,12 @@
     public static final ViewProperty Z = new ViewProperty("z") {
         @Override
         public void setValue(View view, float value) {
-            if (isZSupported()) {
-                view.setZ(value);
-            }
+            ViewCompat.setZ(view, value);
         }
 
         @Override
         public float getValue(View view) {
-            if (isZSupported()) {
-                return view.getZ();
-            } else {
-                return 0;
-            }
+            return ViewCompat.getZ(view);
         }
     };
 
@@ -392,7 +380,8 @@
     }
 
     /**
-     * Start velocity of the animation. Default velocity is 0. Unit: pixel/second
+     * Start velocity of the animation. Default velocity is 0. Unit: change in property per
+     * second (e.g. pixels per second, scale/alpha value change per second).
      *
      * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity
      * through touch events), it is recommended to define such a value in dp/second and convert it
@@ -405,7 +394,7 @@
      *         getResources().getDisplayMetrics());
      * </pre>
      *
-     * @param startVelocity start velocity of the animation in pixel/second
+     * @param startVelocity start velocity of the animation
      * @return the Animation whose start velocity is being set
      */
     public T setStartVelocity(float startVelocity) {
@@ -692,13 +681,6 @@
     }
 
     /**
-     * Returns whether z and translationZ are supported on the current build version.
-     */
-    private static boolean isZSupported() {
-        return Build.VERSION.SDK_INT >= 21;
-    }
-
-    /**
      * Updates the property value through the corresponding setter.
      */
     void setPropertyValue(float value) {
diff --git a/dynamic-animation/src/android/support/animation/SpringAnimation.java b/dynamic-animation/src/android/support/animation/SpringAnimation.java
index 5a9a432..5a51d95 100644
--- a/dynamic-animation/src/android/support/animation/SpringAnimation.java
+++ b/dynamic-animation/src/android/support/animation/SpringAnimation.java
@@ -18,7 +18,6 @@
 
 import android.os.Looper;
 import android.util.AndroidRuntimeException;
-import android.view.View;
 
 /**
  * SpringAnimation is an animation that is driven by a {@link SpringForce}. The spring force defines
@@ -109,33 +108,6 @@
     }
 
     /**
-     * @deprecated This API is being replaced with
-     * {@link #SpringAnimation(Object, FloatPropertyCompat)}.
-     *
-     * <p><b>Note: </b> Migration to the new API should require no modification to callers of this
-     * deprecated API.  The new API's parameters are the base class of the original's parameters and
-     * therefore is compatible to calls to this deprecated method.
-     */
-    @Deprecated
-    public SpringAnimation(View v, ViewProperty property) {
-        super(v, property);
-    }
-
-    /**
-     * @deprecated This API is being replaced with
-     * {@link #SpringAnimation(Object, FloatPropertyCompat, float)}.
-     *
-     * <p><b>Note: </b> Migration to the new API should require no modification to callers of this
-     * deprecated API.  The new API's parameters are the base class of the original's parameters and
-     * therefore is compatible to calls to this deprecated method.
-     */
-    @Deprecated
-    public SpringAnimation(View v, ViewProperty property, float finalPosition) {
-        super(v, property);
-        mSpring = new SpringForce(finalPosition);
-    }
-
-    /**
      * Returns the spring that the animation uses for animations.
      *
      * @return the spring that the animation uses for animations
diff --git a/dynamic-animation/tests/AndroidManifest.xml b/dynamic-animation/tests/AndroidManifest.xml
index 2703ffc..79b4331 100755
--- a/dynamic-animation/tests/AndroidManifest.xml
+++ b/dynamic-animation/tests/AndroidManifest.xml
@@ -15,15 +15,10 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.animation.test">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
-    <uses-sdk
-        android:minSdkVersion="16"
-        android:targetSdkVersion="23"/>
     <application android:supportsRtl="true">
-        <uses-library android:name="android.test.runner"/>
-
         <activity android:name="android.support.dynamicanimation.tests.AnimationActivity"/>
     </application>
 </manifest>
diff --git a/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java b/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
index eb4eb5f..1e891eb 100644
--- a/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
+++ b/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
@@ -641,8 +641,6 @@
             }
         });
 
-        View mockView = mock(View.class);
-
         final SpringAnimation[] anims = new SpringAnimation[properties.length];
         final DynamicAnimation.OnAnimationUpdateListener[] mockListeners =
                 new DynamicAnimation.OnAnimationUpdateListener[properties.length];
diff --git a/emoji/Android.mk b/emoji/Android.mk
new file mode 100644
index 0000000..337f5b9
--- /dev/null
+++ b/emoji/Android.mk
@@ -0,0 +1,16 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/emoji/appcompat/Android.mk b/emoji/appcompat/Android.mk
new file mode 100644
index 0000000..54f6b14
--- /dev/null
+++ b/emoji/appcompat/Android.mk
@@ -0,0 +1,41 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-emoji-appcompat \
+#       android-support-v7-appcompat \
+#       android-support-emoji \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
+#
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-support-emoji-appcompat
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations \
+    android-support-v7-appcompat \
+    android-support-emoji \
+    android-support-compat
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/emoji/appcompat/AndroidManifest.xml b/emoji/appcompat/AndroidManifest.xml
new file mode 100644
index 0000000..b44bda7
--- /dev/null
+++ b/emoji/appcompat/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.text.emoji.appcompat">
+    <uses-sdk android:minSdkVersion="14"/>
+    <application>
+        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+    </application>
+</manifest>
diff --git a/emoji/appcompat/build.gradle b/emoji/appcompat/build.gradle
new file mode 100644
index 0000000..b95842e
--- /dev/null
+++ b/emoji/appcompat/build.gradle
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+apply plugin: android.support.SupportLibraryPlugin
+archivesBaseName = 'support-emoji-appcompat'
+
+dependencies {
+    api fileTree(include: ['*.jar'], dir: 'libs')
+    api project(':support-emoji')
+    api project(':support-appcompat-v7')
+
+    androidTestImplementation (libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation (libs.espresso_core) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 14
+    }
+
+    sourceSets {
+        main.java.srcDir 'src'
+    }
+}
+
+supportLibrary {
+    name 'Android Emoji AppCompat'
+    inceptionYear '2017'
+    description 'EmojiCompat Widgets for AppCompat integration'
+}
\ No newline at end of file
diff --git a/emoji/appcompat/lint-baseline.xml b/emoji/appcompat/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/emoji/appcompat/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatButton.java b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatButton.java
new file mode 100644
index 0000000..b9e6df0
--- /dev/null
+++ b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatButton.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import android.content.Context;
+import android.support.v7.widget.AppCompatButton;
+import android.text.InputFilter;
+import android.util.AttributeSet;
+
+/**
+ * AppCompatButton widget enhanced with emoji capability by using {@link EmojiTextViewHelper}. When
+ * used on devices running API 18 or below, this widget acts as a regular {@link AppCompatButton}.
+ */
+public class EmojiAppCompatButton extends AppCompatButton {
+    private EmojiTextViewHelper mEmojiTextViewHelper;
+
+    /**
+     * Prevent calling {@link #init()} multiple times in case super() constructors
+     * call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiAppCompatButton(Context context) {
+        super(context);
+        init();
+    }
+
+    public EmojiAppCompatButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public EmojiAppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        if (!mInitialized) {
+            mInitialized = true;
+            getEmojiTextViewHelper().updateTransformationMethod();
+        }
+    }
+
+    @Override
+    public void setFilters(InputFilter[] filters) {
+        super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+    }
+
+    @Override
+    public void setAllCaps(boolean allCaps) {
+        super.setAllCaps(allCaps);
+        getEmojiTextViewHelper().setAllCaps(allCaps);
+    }
+
+    private EmojiTextViewHelper getEmojiTextViewHelper() {
+        if (mEmojiTextViewHelper == null) {
+            mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+        }
+        return mEmojiTextViewHelper;
+    }
+}
diff --git a/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java
new file mode 100644
index 0000000..87c17c2
--- /dev/null
+++ b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import android.content.Context;
+import android.support.annotation.IntRange;
+import android.support.annotation.Nullable;
+import android.support.text.emoji.EmojiCompat;
+import android.support.v7.widget.AppCompatEditText;
+import android.util.AttributeSet;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+/**
+ * AppCompatEditText widget enhanced with emoji capability by using {@link EmojiEditTextHelper}.
+ * When used on devices running API 18 or below, this widget acts as a regular
+ * {@link AppCompatEditText}.
+ *
+ * @attr ref android.support.text.emoji.R.styleable#EmojiEditText_maxEmojiCount
+ */
+public class EmojiAppCompatEditText extends AppCompatEditText {
+    private EmojiEditTextHelper mEmojiEditTextHelper;
+
+    /**
+     * Prevent calling {@link #init(AttributeSet, int)} multiple times in case super() constructors
+     * call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiAppCompatEditText(Context context) {
+        super(context);
+        init(null /*attrs*/, 0 /*defStyleAttr*/);
+    }
+
+    public EmojiAppCompatEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(attrs, android.support.v7.appcompat.R.attr.editTextStyle);
+    }
+
+    public EmojiAppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(attrs, defStyleAttr);
+    }
+
+    private void init(@Nullable AttributeSet attrs, int defStyleAttr) {
+        if (!mInitialized) {
+            mInitialized = true;
+            final EditTextAttributeHelper attrHelper = new EditTextAttributeHelper(this, attrs,
+                    defStyleAttr, 0);
+            setMaxEmojiCount(attrHelper.getMaxEmojiCount());
+            setKeyListener(super.getKeyListener());
+        }
+    }
+
+    @Override
+    public void setKeyListener(android.text.method.KeyListener input) {
+        super.setKeyListener(getEmojiEditTextHelper().getKeyListener(input));
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+        return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
+    }
+
+    /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     *
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     *
+     * @attr ref android.support.text.emoji.R.styleable#EmojiEditText_maxEmojiCount
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        getEmojiEditTextHelper().setMaxEmojiCount(maxEmojiCount);
+    }
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     *
+     * @attr ref android.support.text.emoji.R.styleable#EmojiEditText_maxEmojiCount
+     */
+    public int getMaxEmojiCount() {
+        return getEmojiEditTextHelper().getMaxEmojiCount();
+    }
+
+    private EmojiEditTextHelper getEmojiEditTextHelper() {
+        if (mEmojiEditTextHelper == null) {
+            mEmojiEditTextHelper = new EmojiEditTextHelper(this);
+        }
+        return mEmojiEditTextHelper;
+    }
+}
diff --git a/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatTextView.java b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatTextView.java
new file mode 100644
index 0000000..262cc07
--- /dev/null
+++ b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatTextView.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import android.content.Context;
+import android.support.v7.widget.AppCompatTextView;
+import android.text.InputFilter;
+import android.util.AttributeSet;
+
+/**
+ * AppCompatTextView widget enhanced with emoji capability by using {@link EmojiTextViewHelper}.
+ * When used on devices running API 18 or below, this widget acts as a regular
+ * {@link AppCompatTextView}.
+ */
+public class EmojiAppCompatTextView extends AppCompatTextView {
+    private EmojiTextViewHelper mEmojiTextViewHelper;
+
+    /**
+     * Prevent calling {@link #init()} multiple times in case super() constructors
+     * call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiAppCompatTextView(Context context) {
+        super(context);
+        init();
+    }
+
+    public EmojiAppCompatTextView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public EmojiAppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        if (!mInitialized) {
+            mInitialized = true;
+            getEmojiTextViewHelper().updateTransformationMethod();
+        }
+    }
+
+    @Override
+    public void setFilters(InputFilter[] filters) {
+        super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+    }
+
+    @Override
+    public void setAllCaps(boolean allCaps) {
+        super.setAllCaps(allCaps);
+        getEmojiTextViewHelper().setAllCaps(allCaps);
+    }
+
+    private EmojiTextViewHelper getEmojiTextViewHelper() {
+        if (mEmojiTextViewHelper == null) {
+            mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+        }
+        return mEmojiTextViewHelper;
+    }
+}
diff --git a/emoji/bundled/Android.mk b/emoji/bundled/Android.mk
new file mode 100644
index 0000000..3b6b181
--- /dev/null
+++ b/emoji/bundled/Android.mk
@@ -0,0 +1,39 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-emoji-bundled \
+#       android-support-emoji \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
+#
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-support-emoji-bundled
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations \
+    android-support-emoji \
+    android-support-compat
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/emoji/bundled/AndroidManifest.xml b/emoji/bundled/AndroidManifest.xml
new file mode 100644
index 0000000..3d3a684
--- /dev/null
+++ b/emoji/bundled/AndroidManifest.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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.text.emoji.bundled">
+    <uses-sdk android:minSdkVersion="14"/>
+    <application>
+        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/emoji/bundled/build.gradle b/emoji/bundled/build.gradle
new file mode 100644
index 0000000..34a644f
--- /dev/null
+++ b/emoji/bundled/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: android.support.SupportLibraryPlugin
+archivesBaseName = 'support-emoji-bundled'
+
+ext {
+    fontDir = project(':noto-emoji-compat').projectDir
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 14
+    }
+
+    sourceSets {
+        main.java.srcDir 'src'
+        main.assets.srcDirs new File(fontDir, "font").getAbsolutePath()
+    }
+}
+
+dependencies {
+    api project(':support-emoji')
+}
+
+supportLibrary {
+    name 'Android Emoji Compat'
+    inceptionYear '2017'
+    description 'Library bundled with assets to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters.'
+
+    license {
+        name 'SIL Open Font License, Version 1.1'
+        url  'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web'
+    }
+
+    license {
+        name 'Unicode, Inc. License'
+        url 'http://www.unicode.org/copyright.html#License'
+    }
+}
\ No newline at end of file
diff --git a/emoji/bundled/lint-baseline.xml b/emoji/bundled/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/emoji/bundled/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java b/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
new file mode 100644
index 0000000..97ea363
--- /dev/null
+++ b/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.bundled;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.MetadataRepo;
+import android.support.v4.util.Preconditions;
+
+/**
+ * {@link EmojiCompat.Config} implementation that loads the metadata using AssetManager and
+ * bundled resources.
+ * <p/>
+ * <pre><code>EmojiCompat.init(new BundledEmojiCompatConfig(context));</code></pre>
+ *
+ * @see EmojiCompat
+ */
+public class BundledEmojiCompatConfig extends EmojiCompat.Config {
+
+    /**
+     * Default constructor.
+     *
+     * @param context Context instance
+     */
+    public BundledEmojiCompatConfig(@NonNull Context context) {
+        super(new BundledMetadataLoader(context));
+    }
+
+    private static class BundledMetadataLoader implements EmojiCompat.MetadataRepoLoader {
+        private final Context mContext;
+
+        private BundledMetadataLoader(@NonNull Context context) {
+            mContext = context.getApplicationContext();
+        }
+
+        @Override
+        @RequiresApi(19)
+        public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+            Preconditions.checkNotNull(loaderCallback, "loaderCallback cannot be null");
+            final InitRunnable runnable = new InitRunnable(mContext, loaderCallback);
+            final Thread thread = new Thread(runnable);
+            thread.setDaemon(false);
+            thread.start();
+        }
+    }
+
+    @RequiresApi(19)
+    private static class InitRunnable implements Runnable {
+        private static final String FONT_NAME = "NotoColorEmojiCompat.ttf";
+        private final EmojiCompat.MetadataRepoLoaderCallback mLoaderCallback;
+        private final Context mContext;
+
+        private InitRunnable(final Context context,
+                final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+            mContext = context;
+            mLoaderCallback = loaderCallback;
+        }
+
+        @Override
+        public void run() {
+            try {
+                final AssetManager assetManager = mContext.getAssets();
+                final MetadataRepo resourceIndex = MetadataRepo.create(assetManager, FONT_NAME);
+                mLoaderCallback.onLoaded(resourceIndex);
+            } catch (Throwable t) {
+                mLoaderCallback.onFailed(t);
+            }
+        }
+    }
+}
diff --git a/emoji/core/Android.mk b/emoji/core/Android.mk
new file mode 100644
index 0000000..774ba29
--- /dev/null
+++ b/emoji/core/Android.mk
@@ -0,0 +1,40 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-emoji \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
+#
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-support-emoji
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    noto-emoji-compat-java
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations \
+    android-support-compat
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/emoji/core/AndroidManifest.xml b/emoji/core/AndroidManifest.xml
new file mode 100644
index 0000000..1a7b7f4
--- /dev/null
+++ b/emoji/core/AndroidManifest.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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.text.emoji">
+    <uses-sdk android:minSdkVersion="14"/>
+    <application>
+        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+    </application>
+</manifest>
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
new file mode 100644
index 0000000..e71c16c
--- /dev/null
+++ b/emoji/core/build.gradle
@@ -0,0 +1,71 @@
+apply plugin: android.support.SupportLibraryPlugin
+archivesBaseName = 'support-emoji'
+
+ext {
+    fontDir = project(':noto-emoji-compat').projectDir
+}
+
+configurations {
+    repackage
+}
+
+dependencies {
+    repackage project(path: ':noto-emoji-compat', configuration: "parser")
+    // Wrap the noto-emoji-compat dependency in a FileCollection so that the Android Gradle plugin
+    // treats this as local jar and package it inside the aar.
+    api files(configurations.repackage)
+
+    api project(':support-compat')
+
+    androidTestImplementation (libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation (libs.espresso_core) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+    androidTestImplementation project(':support-testutils')
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 14
+    }
+
+    sourceSets {
+        main.java {
+            srcDirs = ['src']
+        }
+        main.res.srcDirs = ['res', 'res-public']
+        main.resources {
+            srcDirs = [fontDir.getAbsolutePath()]
+            includes = ["LICENSE_UNICODE", "LICENSE_OFL"]
+        }
+
+        androidTest {
+            assets {
+                srcDirs = [new File(fontDir, "font").getAbsolutePath(),
+                           new File(fontDir, "supported-emojis").getAbsolutePath()]
+            }
+        }
+    }
+}
+
+supportLibrary {
+    name 'Android Emoji Compat'
+    inceptionYear '2017'
+    description 'Core library to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters.'
+
+    license {
+        name 'SIL Open Font License, Version 1.1'
+        url  'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web'
+    }
+
+    license {
+        name 'Unicode, Inc. License'
+        url 'http://www.unicode.org/copyright.html#License'
+    }
+}
diff --git a/emoji/core/lint-baseline.xml b/emoji/core/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/emoji/core/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/emoji/core/res-public/values/public_attrs.xml b/emoji/core/res-public/values/public_attrs.xml
new file mode 100644
index 0000000..56634ff
--- /dev/null
+++ b/emoji/core/res-public/values/public_attrs.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.
+  -->
+
+<!-- Definitions of attributes to be exposed as public -->
+<resources>
+    <public type="attr" name="maxEmojiCount"/>
+    <public type="attr" name="emojiReplaceStrategy"/>
+</resources>
diff --git a/emoji/core/res/layout/input_method_extract_view.xml b/emoji/core/res/layout/input_method_extract_view.xml
new file mode 100644
index 0000000..7eb3d1c
--- /dev/null
+++ b/emoji/core/res/layout/input_method_extract_view.xml
@@ -0,0 +1,48 @@
+<?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.
+  -->
+
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.text.emoji.widget.EmojiExtractEditText
+        android:id="@+id/android:inputExtractEditText"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="top"
+        android:inputType="text"
+        android:minLines="1"
+        android:scrollbars="vertical"/>
+
+    <FrameLayout
+        android:id="@+id/inputExtractAccessories"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingEnd="8dip"
+        android:paddingStart="8dip">
+
+        <android.support.text.emoji.widget.ExtractButtonCompat
+            android:id="@+id/inputExtractAction"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"/>
+
+    </FrameLayout>
+
+</merge>
\ No newline at end of file
diff --git a/emoji/core/res/values/attrs.xml b/emoji/core/res/values/attrs.xml
new file mode 100644
index 0000000..77254a4
--- /dev/null
+++ b/emoji/core/res/values/attrs.xml
@@ -0,0 +1,33 @@
+<?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>
+    <declare-styleable name="EmojiEditText">
+        <attr name="maxEmojiCount" format="integer"/>
+    </declare-styleable>
+    <declare-styleable name="EmojiExtractTextLayout">
+        <attr name="emojiReplaceStrategy" format="enum">
+            <!-- Replace strategy that uses the value given in EmojiCompat.Config. Default
+            value. -->
+            <enum name="defaultStrategy" value="0" />
+            <!-- Replace strategy to add EmojiSpans for all emoji that were found. -->
+            <enum name="all" value="1" />
+            <!-- Replace strategy to add EmojiSpans only for emoji that do not exist in the
+            system. -->
+            <enum name="nonExistent" value="2" />
+        </attr>
+    </declare-styleable>
+</resources>
diff --git a/emoji/core/src/android/support/text/emoji/EmojiCompat.java b/emoji/core/src/android/support/text/emoji/EmojiCompat.java
new file mode 100644
index 0000000..f258c12
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/EmojiCompat.java
@@ -0,0 +1,1059 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.AnyThread;
+import android.support.annotation.CheckResult;
+import android.support.annotation.ColorInt;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.util.ArraySet;
+import android.support.v4.util.Preconditions;
+import android.text.Editable;
+import android.text.method.KeyListener;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Main class to keep Android devices up to date with the newest emojis by adding {@link EmojiSpan}s
+ * to a given {@link CharSequence}. It is a singleton class that can be configured using a {@link
+ * EmojiCompat.Config} instance.
+ * <p/>
+ * EmojiCompat has to be initialized using {@link #init(EmojiCompat.Config)} function before it can
+ * process a {@link CharSequence}.
+ * <pre><code>EmojiCompat.init(&#47;* a config instance *&#47;);</code></pre>
+ * <p/>
+ * It is suggested to make the initialization as early as possible in your app. Please check {@link
+ * EmojiCompat.Config} for more configuration parameters.
+ * <p/>
+ * During initialization information about emojis is loaded on a background thread. Before the
+ * EmojiCompat instance is initialized, calls to functions such as {@link
+ * EmojiCompat#process(CharSequence)} will throw an exception. You can use the {@link InitCallback}
+ * class to be informed about the state of initialization.
+ * <p/>
+ * After initialization the {@link #get()} function can be used to get the configured instance and
+ * the {@link #process(CharSequence)} function can be used to update a CharSequence with emoji
+ * EmojiSpans.
+ * <p/>
+ * <pre><code>CharSequence processedSequence = EmojiCompat.get().process("some string")</pre>
+ */
+@AnyThread
+public class EmojiCompat {
+    /**
+     * Key in {@link EditorInfo#extras} that represents the emoji metadata version used by the
+     * widget. The existence of the value means that the widget is using EmojiCompat.
+     * <p/>
+     * If exists, the value for the key is an {@code int} and can be used to query EmojiCompat to
+     * see whether the widget has the ability to display a certain emoji using
+     * {@link #hasEmojiGlyph(CharSequence, int)}.
+     */
+    public static final String EDITOR_INFO_METAVERSION_KEY =
+            "android.support.text.emoji.emojiCompat_metadataVersion";
+
+    /**
+     * Key in {@link EditorInfo#extras} that represents {@link
+     * EmojiCompat.Config#setReplaceAll(boolean)} configuration parameter. The key is added only if
+     * EmojiCompat is used by the widget. If exists, the value is a boolean.
+     */
+    public static final String EDITOR_INFO_REPLACE_ALL_KEY =
+            "android.support.text.emoji.emojiCompat_replaceAll";
+
+    /**
+     * EmojiCompat is initializing.
+     */
+    public static final int LOAD_STATE_LOADING = 0;
+
+    /**
+     * EmojiCompat successfully initialized.
+     */
+    public static final int LOAD_STATE_SUCCEEDED = 1;
+
+    /**
+     * An unrecoverable error occurred during initialization of EmojiCompat. Calls to functions
+     * such as {@link #process(CharSequence)} will fail.
+     */
+    public static final int LOAD_STATE_FAILED = 2;
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({LOAD_STATE_LOADING, LOAD_STATE_SUCCEEDED, LOAD_STATE_FAILED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LoadState {
+    }
+
+    /**
+     * Replace strategy that uses the value given in {@link EmojiCompat.Config}.
+     */
+    public static final int REPLACE_STRATEGY_DEFAULT = 0;
+
+    /**
+     * Replace strategy to add {@link EmojiSpan}s for all emoji that were found.
+     */
+    public static final int REPLACE_STRATEGY_ALL = 1;
+
+    /**
+     * Replace strategy to add {@link EmojiSpan}s only for emoji that do not exist in the system.
+     */
+    public static final int REPLACE_STRATEGY_NON_EXISTENT = 2;
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({REPLACE_STRATEGY_DEFAULT, REPLACE_STRATEGY_NON_EXISTENT, REPLACE_STRATEGY_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReplaceStrategy {
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    static final int EMOJI_COUNT_UNLIMITED = Integer.MAX_VALUE;
+
+    private static final Object sInstanceLock = new Object();
+
+    @GuardedBy("sInstanceLock")
+    private static volatile EmojiCompat sInstance;
+
+    private final ReadWriteLock mInitLock;
+
+    @GuardedBy("mInitLock")
+    private final Set<InitCallback> mInitCallbacks;
+
+    @GuardedBy("mInitLock")
+    @LoadState
+    private int mLoadState;
+
+    /**
+     * Handler with main looper to run the callbacks on.
+     */
+    private final Handler mMainHandler;
+
+    /**
+     * Helper class for pre 19 compatibility.
+     */
+    private final CompatInternal mHelper;
+
+    /**
+     * Metadata loader instance given in the Config instance.
+     */
+    private final MetadataRepoLoader mMetadataLoader;
+
+    /**
+     * @see Config#setReplaceAll(boolean)
+     */
+    private final boolean mReplaceAll;
+
+    /**
+     * @see Config#setEmojiSpanIndicatorEnabled(boolean)
+     */
+    private final boolean mEmojiSpanIndicatorEnabled;
+
+    /**
+     * @see Config#setEmojiSpanIndicatorColor(int)
+     */
+    private final int mEmojiSpanIndicatorColor;
+
+    /**
+     * Private constructor for singleton instance.
+     *
+     * @see #init(Config)
+     */
+    private EmojiCompat(@NonNull final Config config) {
+        mInitLock = new ReentrantReadWriteLock();
+        mReplaceAll = config.mReplaceAll;
+        mEmojiSpanIndicatorEnabled = config.mEmojiSpanIndicatorEnabled;
+        mEmojiSpanIndicatorColor = config.mEmojiSpanIndicatorColor;
+        mMetadataLoader = config.mMetadataLoader;
+        mMainHandler = new Handler(Looper.getMainLooper());
+        mInitCallbacks = new ArraySet<>();
+        if (config.mInitCallbacks != null && !config.mInitCallbacks.isEmpty()) {
+            mInitCallbacks.addAll(config.mInitCallbacks);
+        }
+        mHelper = Build.VERSION.SDK_INT < 19 ? new CompatInternal(this) : new CompatInternal19(
+                this);
+        loadMetadata();
+    }
+
+    /**
+     * Initialize the singleton instance with a configuration. When used on devices running API 18
+     * or below, the singleton instance is immediately moved into {@link #LOAD_STATE_SUCCEEDED}
+     * state without loading any metadata.
+     *
+     * @see EmojiCompat.Config
+     */
+    public static EmojiCompat init(@NonNull final Config config) {
+        if (sInstance == null) {
+            synchronized (sInstanceLock) {
+                if (sInstance == null) {
+                    sInstance = new EmojiCompat(config);
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    /**
+     * Used by the tests to reset EmojiCompat with a new configuration. Every time it is called a
+     * new instance is created with the new configuration.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @VisibleForTesting
+    public static EmojiCompat reset(@NonNull final Config config) {
+        synchronized (sInstanceLock) {
+            sInstance = new EmojiCompat(config);
+        }
+        return sInstance;
+    }
+
+    /**
+     * Used by the tests to reset EmojiCompat with a new singleton instance.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @VisibleForTesting
+    public static EmojiCompat reset(final EmojiCompat emojiCompat) {
+        synchronized (sInstanceLock) {
+            sInstance = emojiCompat;
+        }
+        return sInstance;
+    }
+
+    /**
+     * Used by the tests to set GlyphChecker for EmojiProcessor.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @VisibleForTesting
+    void setGlyphChecker(@NonNull final EmojiProcessor.GlyphChecker glyphChecker) {
+        mHelper.setGlyphChecker(glyphChecker);
+    }
+
+    /**
+     * Return singleton EmojiCompat instance. Should be called after
+     * {@link #init(EmojiCompat.Config)} is called to initialize the singleton instance.
+     *
+     * @return EmojiCompat instance
+     *
+     * @throws IllegalStateException if called before {@link #init(EmojiCompat.Config)}
+     */
+    public static EmojiCompat get() {
+        synchronized (sInstanceLock) {
+            Preconditions.checkState(sInstance != null,
+                    "EmojiCompat is not initialized. Please call EmojiCompat.init() first");
+            return sInstance;
+        }
+    }
+
+    private void loadMetadata() {
+        mInitLock.writeLock().lock();
+        try {
+            mLoadState = LOAD_STATE_LOADING;
+        } finally {
+            mInitLock.writeLock().unlock();
+        }
+
+        mHelper.loadMetadata();
+    }
+
+    private void onMetadataLoadSuccess() {
+        final Collection<InitCallback> initCallbacks = new ArrayList<>();
+        mInitLock.writeLock().lock();
+        try {
+            mLoadState = LOAD_STATE_SUCCEEDED;
+            initCallbacks.addAll(mInitCallbacks);
+            mInitCallbacks.clear();
+        } finally {
+            mInitLock.writeLock().unlock();
+        }
+
+        mMainHandler.post(new ListenerDispatcher(initCallbacks, mLoadState));
+    }
+
+    private void onMetadataLoadFailed(@Nullable final Throwable throwable) {
+        final Collection<InitCallback> initCallbacks = new ArrayList<>();
+        mInitLock.writeLock().lock();
+        try {
+            mLoadState = LOAD_STATE_FAILED;
+            initCallbacks.addAll(mInitCallbacks);
+            mInitCallbacks.clear();
+        } finally {
+            mInitLock.writeLock().unlock();
+        }
+        mMainHandler.post(new ListenerDispatcher(initCallbacks, mLoadState, throwable));
+    }
+
+    /**
+     * Registers an initialization callback. If the initialization is already completed by the time
+     * the listener is added, the callback functions are called immediately. Callbacks are called on
+     * the main looper.
+     * <p/>
+     * When used on devices running API 18 or below, {@link InitCallback#onInitialized()} is called
+     * without loading any metadata. In such cases {@link InitCallback#onFailed(Throwable)} is never
+     * called.
+     *
+     * @param initCallback the initialization callback to register, cannot be {@code null}
+     *
+     * @see #unregisterInitCallback(InitCallback)
+     */
+    public void registerInitCallback(@NonNull InitCallback initCallback) {
+        Preconditions.checkNotNull(initCallback, "initCallback cannot be null");
+
+        mInitLock.writeLock().lock();
+        try {
+            if (mLoadState == LOAD_STATE_SUCCEEDED || mLoadState == LOAD_STATE_FAILED) {
+                mMainHandler.post(new ListenerDispatcher(initCallback, mLoadState));
+            } else {
+                mInitCallbacks.add(initCallback);
+            }
+        } finally {
+            mInitLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Unregisters a callback that was added before.
+     *
+     * @param initCallback the callback to be removed, cannot be {@code null}
+     */
+    public void unregisterInitCallback(@NonNull InitCallback initCallback) {
+        Preconditions.checkNotNull(initCallback, "initCallback cannot be null");
+        mInitLock.writeLock().lock();
+        try {
+            mInitCallbacks.remove(initCallback);
+        } finally {
+            mInitLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Returns loading state of the EmojiCompat instance. When used on devices running API 18 or
+     * below always returns {@link #LOAD_STATE_SUCCEEDED}.
+     *
+     * @return one of {@link #LOAD_STATE_LOADING}, {@link #LOAD_STATE_SUCCEEDED},
+     * {@link #LOAD_STATE_FAILED}
+     */
+    public @LoadState int getLoadState() {
+        mInitLock.readLock().lock();
+        try {
+            return mLoadState;
+        } finally {
+            mInitLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * @return {@code true} if EmojiCompat is successfully initialized
+     */
+    private boolean isInitialized() {
+        return getLoadState() == LOAD_STATE_SUCCEEDED;
+    }
+
+    /**
+     * @return whether a background should be drawn for the emoji.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    boolean isEmojiSpanIndicatorEnabled() {
+        return mEmojiSpanIndicatorEnabled;
+    }
+
+    /**
+     * @return whether a background should be drawn for the emoji.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @ColorInt int getEmojiSpanIndicatorColor() {
+        return mEmojiSpanIndicatorColor;
+    }
+
+    /**
+     * Handles onKeyDown commands from a {@link KeyListener} and if {@code keyCode} is one of
+     * {@link KeyEvent#KEYCODE_DEL} or {@link KeyEvent#KEYCODE_FORWARD_DEL} it tries to delete an
+     * {@link EmojiSpan} from an {@link Editable}. Returns {@code true} if an {@link EmojiSpan} is
+     * deleted with the characters it covers.
+     * <p/>
+     * If there is a selection where selection start is not equal to selection end, does not
+     * delete.
+     * <p/>
+     * When used on devices running API 18 or below, always returns {@code false}.
+     *
+     * @param editable Editable instance passed to {@link KeyListener#onKeyDown(android.view.View,
+     *                 Editable, int, KeyEvent)}
+     * @param keyCode keyCode passed to {@link KeyListener#onKeyDown(android.view.View, Editable,
+     *                int, KeyEvent)}
+     * @param event KeyEvent passed to {@link KeyListener#onKeyDown(android.view.View, Editable,
+     *              int, KeyEvent)}
+     *
+     * @return {@code true} if an {@link EmojiSpan} is deleted
+     */
+    public static boolean handleOnKeyDown(@NonNull final Editable editable, final int keyCode,
+            final KeyEvent event) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            return EmojiProcessor.handleOnKeyDown(editable, keyCode, event);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Handles deleteSurroundingText commands from {@link InputConnection} and tries to delete an
+     * {@link EmojiSpan} from an {@link Editable}. Returns {@code true} if an {@link EmojiSpan} is
+     * deleted.
+     * <p/>
+     * If there is a selection where selection start is not equal to selection end, does not
+     * delete.
+     * <p/>
+     * When used on devices running API 18 or below, always returns {@code false}.
+     *
+     * @param inputConnection InputConnection instance
+     * @param editable TextView.Editable instance
+     * @param beforeLength the number of characters before the cursor to be deleted
+     * @param afterLength the number of characters after the cursor to be deleted
+     * @param inCodePoints {@code true} if length parameters are in codepoints
+     *
+     * @return {@code true} if an {@link EmojiSpan} is deleted
+     */
+    public static boolean handleDeleteSurroundingText(
+            @NonNull final InputConnection inputConnection, @NonNull final Editable editable,
+            @IntRange(from = 0) final int beforeLength, @IntRange(from = 0) final int afterLength,
+            final boolean inCodePoints) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            return EmojiProcessor.handleDeleteSurroundingText(inputConnection, editable,
+                    beforeLength, afterLength, inCodePoints);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns {@code true} if EmojiCompat is capable of rendering an emoji. When used on devices
+     * running API 18 or below, always returns {@code false}.
+     *
+     * @param sequence CharSequence representing the emoji
+     *
+     * @return {@code true} if EmojiCompat can render given emoji, cannot be {@code null}
+     *
+     * @throws IllegalStateException if not initialized yet
+     */
+    public boolean hasEmojiGlyph(@NonNull final CharSequence sequence) {
+        Preconditions.checkState(isInitialized(), "Not initialized yet");
+        Preconditions.checkNotNull(sequence, "sequence cannot be null");
+        return mHelper.hasEmojiGlyph(sequence);
+    }
+
+    /**
+     * Returns {@code true} if EmojiCompat is capable of rendering an emoji at the given metadata
+     * version. When used on devices running API 18 or below, always returns {@code false}.
+     *
+     * @param sequence CharSequence representing the emoji
+     * @param metadataVersion the metadata version to check against, should be greater than or
+     *                        equal to {@code 0},
+     *
+     * @return {@code true} if EmojiCompat can render given emoji, cannot be {@code null}
+     *
+     * @throws IllegalStateException if not initialized yet
+     */
+    public boolean hasEmojiGlyph(@NonNull final CharSequence sequence,
+            @IntRange(from = 0) final int metadataVersion) {
+        Preconditions.checkState(isInitialized(), "Not initialized yet");
+        Preconditions.checkNotNull(sequence, "sequence cannot be null");
+        return mHelper.hasEmojiGlyph(sequence, metadataVersion);
+    }
+
+    /**
+     * Checks a given CharSequence for emojis, and adds EmojiSpans if any emojis are found. When
+     * used on devices running API 18 or below, returns the given {@code charSequence} without
+     * processing it.
+     *
+     * @param charSequence CharSequence to add the EmojiSpans
+     *
+     * @throws IllegalStateException if not initialized yet
+     * @see #process(CharSequence, int, int)
+     */
+    @CheckResult
+    public CharSequence process(@NonNull final CharSequence charSequence) {
+        // since charSequence might be null here we have to check it. Passing through here to the
+        // main function so that it can do all the checks including isInitialized. It will also
+        // be the main point that decides what to return.
+        //noinspection ConstantConditions
+        @IntRange(from = 0) final int length = charSequence == null ? 0 : charSequence.length();
+        return process(charSequence, 0, length);
+    }
+
+    /**
+     * Checks a given CharSequence for emojis, and adds EmojiSpans if any emojis are found.
+     * <p>
+     * <ul>
+     * <li>If no emojis are found, {@code charSequence} given as the input is returned without
+     * any changes. i.e. charSequence is a String, and no emojis are found, the same String is
+     * returned.</li>
+     * <li>If the given input is not a Spannable (such as String), and at least one emoji is found
+     * a new {@link android.text.Spannable} instance is returned. </li>
+     * <li>If the given input is a Spannable, the same instance is returned. </li>
+     * </ul>
+     * When used on devices running API 18 or below, returns the given {@code charSequence} without
+     * processing it.
+     *
+     * @param charSequence CharSequence to add the EmojiSpans, cannot be {@code null}
+     * @param start start index in the charSequence to look for emojis, should be greater than or
+     *              equal to {@code 0}, also less than {@code charSequence.length()}
+     * @param end end index in the charSequence to look for emojis, should be greater than or
+     *            equal to {@code start} parameter, also less than {@code charSequence.length()}
+     *
+     * @throws IllegalStateException if not initialized yet
+     * @throws IllegalArgumentException in the following cases:
+     *                                  {@code start < 0}, {@code end < 0}, {@code end < start},
+     *                                  {@code start > charSequence.length()},
+     *                                  {@code end > charSequence.length()}
+     */
+    @CheckResult
+    public CharSequence process(@NonNull final CharSequence charSequence,
+            @IntRange(from = 0) final int start, @IntRange(from = 0) final int end) {
+        return process(charSequence, start, end, EMOJI_COUNT_UNLIMITED);
+    }
+
+    /**
+     * Checks a given CharSequence for emojis, and adds EmojiSpans if any emojis are found.
+     * <p>
+     * <ul>
+     * <li>If no emojis are found, {@code charSequence} given as the input is returned without
+     * any changes. i.e. charSequence is a String, and no emojis are found, the same String is
+     * returned.</li>
+     * <li>If the given input is not a Spannable (such as String), and at least one emoji is found
+     * a new {@link android.text.Spannable} instance is returned. </li>
+     * <li>If the given input is a Spannable, the same instance is returned. </li>
+     * </ul>
+     * When used on devices running API 18 or below, returns the given {@code charSequence} without
+     * processing it.
+     *
+     * @param charSequence CharSequence to add the EmojiSpans, cannot be {@code null}
+     * @param start start index in the charSequence to look for emojis, should be greater than or
+     *              equal to {@code 0}, also less than {@code charSequence.length()}
+     * @param end end index in the charSequence to look for emojis, should be greater than or
+     *            equal to {@code start} parameter, also less than {@code charSequence.length()}
+     * @param maxEmojiCount maximum number of emojis in the {@code charSequence}, should be greater
+     *                      than or equal to {@code 0}
+     *
+     * @throws IllegalStateException if not initialized yet
+     * @throws IllegalArgumentException in the following cases:
+     *                                  {@code start < 0}, {@code end < 0}, {@code end < start},
+     *                                  {@code start > charSequence.length()},
+     *                                  {@code end > charSequence.length()}
+     *                                  {@code maxEmojiCount < 0}
+     */
+    @CheckResult
+    public CharSequence process(@NonNull final CharSequence charSequence,
+            @IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
+            @IntRange(from = 0) final int maxEmojiCount) {
+        return process(charSequence, start, end, maxEmojiCount, REPLACE_STRATEGY_DEFAULT);
+    }
+
+    /**
+     * Checks a given CharSequence for emojis, and adds EmojiSpans if any emojis are found.
+     * <p>
+     * <ul>
+     * <li>If no emojis are found, {@code charSequence} given as the input is returned without
+     * any changes. i.e. charSequence is a String, and no emojis are found, the same String is
+     * returned.</li>
+     * <li>If the given input is not a Spannable (such as String), and at least one emoji is found
+     * a new {@link android.text.Spannable} instance is returned. </li>
+     * <li>If the given input is a Spannable, the same instance is returned. </li>
+     * </ul>
+     * When used on devices running API 18 or below, returns the given {@code charSequence} without
+     * processing it.
+     *
+     * @param charSequence CharSequence to add the EmojiSpans, cannot be {@code null}
+     * @param start start index in the charSequence to look for emojis, should be greater than or
+     *              equal to {@code 0}, also less than {@code charSequence.length()}
+     * @param end end index in the charSequence to look for emojis, should be greater than or
+     *            equal to {@code start} parameter, also less than {@code charSequence.length()}
+     * @param maxEmojiCount maximum number of emojis in the {@code charSequence}, should be greater
+     *                      than or equal to {@code 0}
+     * @param replaceStrategy whether to replace all emoji with {@link EmojiSpan}s, should be one of
+     *                        {@link #REPLACE_STRATEGY_DEFAULT},
+     *                        {@link #REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link #REPLACE_STRATEGY_ALL}
+     *
+     * @throws IllegalStateException if not initialized yet
+     * @throws IllegalArgumentException in the following cases:
+     *                                  {@code start < 0}, {@code end < 0}, {@code end < start},
+     *                                  {@code start > charSequence.length()},
+     *                                  {@code end > charSequence.length()}
+     *                                  {@code maxEmojiCount < 0}
+     */
+    @CheckResult
+    public CharSequence process(@NonNull final CharSequence charSequence,
+            @IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
+            @IntRange(from = 0) final int maxEmojiCount, @ReplaceStrategy int replaceStrategy) {
+        Preconditions.checkState(isInitialized(), "Not initialized yet");
+        Preconditions.checkArgumentNonnegative(start, "start cannot be negative");
+        Preconditions.checkArgumentNonnegative(end, "end cannot be negative");
+        Preconditions.checkArgumentNonnegative(maxEmojiCount, "maxEmojiCount cannot be negative");
+        Preconditions.checkArgument(start <= end, "start should be <= than end");
+
+        // early return since there is nothing to do
+        //noinspection ConstantConditions
+        if (charSequence == null) {
+            return charSequence;
+        }
+
+        Preconditions.checkArgument(start <= charSequence.length(),
+                "start should be < than charSequence length");
+        Preconditions.checkArgument(end <= charSequence.length(),
+                "end should be < than charSequence length");
+
+        // early return since there is nothing to do
+        if (charSequence.length() == 0 || start == end) {
+            return charSequence;
+        }
+
+        final boolean replaceAll;
+        switch (replaceStrategy) {
+            case REPLACE_STRATEGY_ALL:
+                replaceAll = true;
+                break;
+            case REPLACE_STRATEGY_NON_EXISTENT:
+                replaceAll = false;
+                break;
+            case REPLACE_STRATEGY_DEFAULT:
+            default:
+                replaceAll = mReplaceAll;
+                break;
+        }
+
+        return mHelper.process(charSequence, start, end, maxEmojiCount, replaceAll);
+    }
+
+    /**
+     * Returns signature for the currently loaded emoji assets. The signature is a SHA that is
+     * constructed using emoji assets. Can be used to detect if currently loaded asset is different
+     * then previous executions. When used on devices running API 18 or below, returns empty string.
+     *
+     * @throws IllegalStateException if not initialized yet
+     */
+    @NonNull
+    public String getAssetSignature() {
+        Preconditions.checkState(isInitialized(), "Not initialized yet");
+        return mHelper.getAssetSignature();
+    }
+
+    /**
+     * Updates the EditorInfo attributes in order to communicate information to Keyboards. When
+     * used on devices running API 18 or below, does not update EditorInfo attributes.
+     *
+     * @param outAttrs EditorInfo instance passed to
+     *                 {@link android.widget.TextView#onCreateInputConnection(EditorInfo)}
+     *
+     * @see #EDITOR_INFO_METAVERSION_KEY
+     * @see #EDITOR_INFO_REPLACE_ALL_KEY
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void updateEditorInfoAttrs(@NonNull final EditorInfo outAttrs) {
+        //noinspection ConstantConditions
+        if (isInitialized() && outAttrs != null && outAttrs.extras != null) {
+            mHelper.updateEditorInfoAttrs(outAttrs);
+        }
+    }
+
+    /**
+     * Factory class that creates the EmojiSpans. By default it creates {@link TypefaceEmojiSpan}.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @RequiresApi(19)
+    static class SpanFactory {
+        /**
+         * Create EmojiSpan instance.
+         *
+         * @param metadata EmojiMetadata instance
+         *
+         * @return EmojiSpan instance
+         */
+        EmojiSpan createSpan(@NonNull final EmojiMetadata metadata) {
+            return new TypefaceEmojiSpan(metadata);
+        }
+    }
+
+    /**
+     * Listener class for the initialization of the EmojiCompat.
+     */
+    public abstract static class InitCallback {
+        /**
+         * Called when EmojiCompat is initialized and the emoji data is loaded. When used on devices
+         * running API 18 or below, this function is always called.
+         */
+        public void onInitialized() {
+        }
+
+        /**
+         * Called when an unrecoverable error occurs during EmojiCompat initialization. When used on
+         * devices running API 18 or below, this function is never called.
+         */
+        public void onFailed(@Nullable Throwable throwable) {
+        }
+    }
+
+    /**
+     * Interface to load emoji metadata.
+     */
+    public interface MetadataRepoLoader {
+        /**
+         * Start loading the metadata. When the loading operation is finished {@link
+         * MetadataRepoLoaderCallback#onLoaded(MetadataRepo)} or
+         * {@link MetadataRepoLoaderCallback#onFailed(Throwable)} should be called. When used on
+         * devices running API 18 or below, this function is never called.
+         *
+         * @param loaderCallback callback to signal the loading state
+         */
+        void load(@NonNull MetadataRepoLoaderCallback loaderCallback);
+    }
+
+    /**
+     * Callback to inform EmojiCompat about the state of the metadata load. Passed to
+     * MetadataRepoLoader during {@link MetadataRepoLoader#load(MetadataRepoLoaderCallback)} call.
+     */
+    public abstract static class MetadataRepoLoaderCallback {
+        /**
+         * Called by {@link MetadataRepoLoader} when metadata is loaded successfully.
+         *
+         * @param metadataRepo MetadataRepo instance, cannot be {@code null}
+         */
+        public abstract void onLoaded(@NonNull MetadataRepo metadataRepo);
+
+        /**
+         * Called by {@link MetadataRepoLoader} if an error occurs while loading the metadata.
+         *
+         * @param throwable the exception that caused the failure, {@code nullable}
+         */
+        public abstract void onFailed(@Nullable Throwable throwable);
+    }
+
+    /**
+     * Configuration class for EmojiCompat. Changes to the values will be ignored after
+     * {@link #init(Config)} is called.
+     *
+     * @see #init(EmojiCompat.Config)
+     */
+    public abstract static class Config {
+        private final MetadataRepoLoader mMetadataLoader;
+        private boolean mReplaceAll;
+        private Set<InitCallback> mInitCallbacks;
+        private boolean mEmojiSpanIndicatorEnabled;
+        private int mEmojiSpanIndicatorColor = Color.GREEN;
+
+        /**
+         * Default constructor.
+         *
+         * @param metadataLoader MetadataRepoLoader instance, cannot be {@code null}
+         */
+        protected Config(@NonNull final MetadataRepoLoader metadataLoader) {
+            Preconditions.checkNotNull(metadataLoader, "metadataLoader cannot be null.");
+            mMetadataLoader = metadataLoader;
+        }
+
+        /**
+         * Registers an initialization callback.
+         *
+         * @param initCallback the initialization callback to register, cannot be {@code null}
+         *
+         * @return EmojiCompat.Config instance
+         */
+        public Config registerInitCallback(@NonNull InitCallback initCallback) {
+            Preconditions.checkNotNull(initCallback, "initCallback cannot be null");
+            if (mInitCallbacks == null) {
+                mInitCallbacks = new ArraySet<>();
+            }
+
+            mInitCallbacks.add(initCallback);
+
+            return this;
+        }
+
+        /**
+         * Unregisters a callback that was added before.
+         *
+         * @param initCallback the initialization callback to be removed, cannot be {@code null}
+         *
+         * @return EmojiCompat.Config instance
+         */
+        public Config unregisterInitCallback(@NonNull InitCallback initCallback) {
+            Preconditions.checkNotNull(initCallback, "initCallback cannot be null");
+            if (mInitCallbacks != null) {
+                mInitCallbacks.remove(initCallback);
+            }
+            return this;
+        }
+
+        /**
+         * Determines whether EmojiCompat should replace all the emojis it finds with the
+         * EmojiSpans. By default EmojiCompat tries its best to understand if the system already
+         * can render an emoji and do not replace those emojis.
+         *
+         * @param replaceAll replace all emojis found with EmojiSpans
+         *
+         * @return EmojiCompat.Config instance
+         */
+        public Config setReplaceAll(final boolean replaceAll) {
+            mReplaceAll = replaceAll;
+            return this;
+        }
+
+        /**
+         * Determines whether a background will be drawn for the emojis that are found and
+         * replaced by EmojiCompat. Should be used only for debugging purposes. The indicator color
+         * can be set using {@link #setEmojiSpanIndicatorColor(int)}.
+         *
+         * @param emojiSpanIndicatorEnabled when {@code true} a background is drawn for each emoji
+         *                                  that is replaced
+         */
+        public Config setEmojiSpanIndicatorEnabled(boolean emojiSpanIndicatorEnabled) {
+            mEmojiSpanIndicatorEnabled = emojiSpanIndicatorEnabled;
+            return this;
+        }
+
+        /**
+         * Sets the color used as emoji span indicator. The default value is
+         * {@link Color#GREEN Color.GREEN}.
+         *
+         * @see #setEmojiSpanIndicatorEnabled(boolean)
+         */
+        public Config setEmojiSpanIndicatorColor(@ColorInt int color) {
+            mEmojiSpanIndicatorColor = color;
+            return this;
+        }
+
+        /**
+         * Returns the {@link MetadataRepoLoader}.
+         */
+        protected final MetadataRepoLoader getMetadataRepoLoader() {
+            return mMetadataLoader;
+        }
+    }
+
+    /**
+     * Runnable to call success/failure case for the listeners.
+     */
+    private static class ListenerDispatcher implements Runnable {
+        private final List<InitCallback> mInitCallbacks;
+        private final Throwable mThrowable;
+        private final int mLoadState;
+
+        @SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+        ListenerDispatcher(@NonNull final InitCallback initCallback,
+                @LoadState final int loadState) {
+            this(Arrays.asList(Preconditions.checkNotNull(initCallback,
+                    "initCallback cannot be null")), loadState, null);
+        }
+
+        ListenerDispatcher(@NonNull final Collection<InitCallback> initCallbacks,
+                @LoadState final int loadState) {
+            this(initCallbacks, loadState, null);
+        }
+
+        ListenerDispatcher(@NonNull final Collection<InitCallback> initCallbacks,
+                @LoadState final int loadState,
+                @Nullable final Throwable throwable) {
+            Preconditions.checkNotNull(initCallbacks, "initCallbacks cannot be null");
+            mInitCallbacks = new ArrayList<>(initCallbacks);
+            mLoadState = loadState;
+            mThrowable = throwable;
+        }
+
+        @Override
+        public void run() {
+            final int size = mInitCallbacks.size();
+            switch (mLoadState) {
+                case LOAD_STATE_SUCCEEDED:
+                    for (int i = 0; i < size; i++) {
+                        mInitCallbacks.get(i).onInitialized();
+                    }
+                    break;
+                case LOAD_STATE_FAILED:
+                default:
+                    for (int i = 0; i < size; i++) {
+                        mInitCallbacks.get(i).onFailed(mThrowable);
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Internal helper class to behave no-op for certain functions.
+     */
+    private static class CompatInternal {
+        final EmojiCompat mEmojiCompat;
+
+        CompatInternal(EmojiCompat emojiCompat) {
+            mEmojiCompat = emojiCompat;
+        }
+
+        void loadMetadata() {
+            // Moves into LOAD_STATE_SUCCESS state immediately.
+            mEmojiCompat.onMetadataLoadSuccess();
+        }
+
+        boolean hasEmojiGlyph(@NonNull final CharSequence sequence) {
+            // Since no metadata is loaded, EmojiCompat cannot detect or render any emojis.
+            return false;
+        }
+
+        boolean hasEmojiGlyph(@NonNull final CharSequence sequence, final int metadataVersion) {
+            // Since no metadata is loaded, EmojiCompat cannot detect or render any emojis.
+            return false;
+        }
+
+        CharSequence process(@NonNull final CharSequence charSequence,
+                @IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
+                @IntRange(from = 0) final int maxEmojiCount, boolean replaceAll) {
+            // Returns the given charSequence as it is.
+            return charSequence;
+        }
+
+        void updateEditorInfoAttrs(@NonNull final EditorInfo outAttrs) {
+            // Does not add any EditorInfo attributes.
+        }
+
+        void setGlyphChecker(@NonNull EmojiProcessor.GlyphChecker glyphChecker) {
+            // intentionally empty
+        }
+
+        String getAssetSignature() {
+            return "";
+        }
+    }
+
+    @RequiresApi(19)
+    private static final class CompatInternal19 extends CompatInternal {
+        /**
+         * Responsible to process a CharSequence and add the spans. @{code Null} until the time the
+         * metadata is loaded.
+         */
+        private volatile EmojiProcessor mProcessor;
+
+        /**
+         * Keeps the information about emojis. Null until the time the data is loaded.
+         */
+        private volatile MetadataRepo mMetadataRepo;
+
+
+        CompatInternal19(EmojiCompat emojiCompat) {
+            super(emojiCompat);
+        }
+
+        @Override
+        void loadMetadata() {
+            try {
+                final MetadataRepoLoaderCallback callback = new MetadataRepoLoaderCallback() {
+                    @Override
+                    public void onLoaded(@NonNull MetadataRepo metadataRepo) {
+                        onMetadataLoadSuccess(metadataRepo);
+                    }
+
+                    @Override
+                    public void onFailed(@Nullable Throwable throwable) {
+                        mEmojiCompat.onMetadataLoadFailed(throwable);
+                    }
+                };
+                mEmojiCompat.mMetadataLoader.load(callback);
+            } catch (Throwable t) {
+                mEmojiCompat.onMetadataLoadFailed(t);
+            }
+        }
+
+        private void onMetadataLoadSuccess(@NonNull final MetadataRepo metadataRepo) {
+            //noinspection ConstantConditions
+            if (metadataRepo == null) {
+                mEmojiCompat.onMetadataLoadFailed(
+                        new IllegalArgumentException("metadataRepo cannot be null"));
+                return;
+            }
+
+            mMetadataRepo = metadataRepo;
+            mProcessor = new EmojiProcessor(mMetadataRepo, new SpanFactory());
+
+            mEmojiCompat.onMetadataLoadSuccess();
+        }
+
+        @Override
+        boolean hasEmojiGlyph(@NonNull CharSequence sequence) {
+            return mProcessor.getEmojiMetadata(sequence) != null;
+        }
+
+        @Override
+        boolean hasEmojiGlyph(@NonNull CharSequence sequence, int metadataVersion) {
+            final EmojiMetadata emojiMetadata = mProcessor.getEmojiMetadata(sequence);
+            return emojiMetadata != null && emojiMetadata.getCompatAdded() <= metadataVersion;
+        }
+
+        @Override
+        CharSequence process(@NonNull CharSequence charSequence, int start, int end,
+                int maxEmojiCount, boolean replaceAll) {
+            return mProcessor.process(charSequence, start, end, maxEmojiCount, replaceAll);
+        }
+
+        @Override
+        void updateEditorInfoAttrs(@NonNull EditorInfo outAttrs) {
+            outAttrs.extras.putInt(EDITOR_INFO_METAVERSION_KEY, mMetadataRepo.getMetadataVersion());
+            outAttrs.extras.putBoolean(EDITOR_INFO_REPLACE_ALL_KEY, mEmojiCompat.mReplaceAll);
+        }
+
+        @Override
+        void setGlyphChecker(@NonNull EmojiProcessor.GlyphChecker glyphChecker) {
+            mProcessor.setGlyphChecker(glyphChecker);
+        }
+
+        @Override
+        String getAssetSignature() {
+            final String sha = mMetadataRepo.getMetadataList().sourceSha();
+            return sha == null ? "" : sha;
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/EmojiMetadata.java b/emoji/core/src/android/support/text/emoji/EmojiMetadata.java
new file mode 100644
index 0000000..810aa89
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/EmojiMetadata.java
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.annotation.AnyThread;
+import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.flatbuffer.MetadataItem;
+import android.support.text.emoji.flatbuffer.MetadataList;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Information about a single emoji.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@AnyThread
+@RequiresApi(19)
+public class EmojiMetadata {
+    /**
+     * Defines whether the system can render the emoji.
+     */
+    @IntDef({HAS_GLYPH_UNKNOWN, HAS_GLYPH_ABSENT, HAS_GLYPH_EXISTS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HasGlyph {
+    }
+
+    /**
+     * Not calculated on device yet.
+     */
+    public static final int HAS_GLYPH_UNKNOWN = 0;
+
+    /**
+     * Device cannot render the emoji.
+     */
+    public static final int HAS_GLYPH_ABSENT = 1;
+
+    /**
+     * Device can render the emoji.
+     */
+    public static final int HAS_GLYPH_EXISTS = 2;
+
+    /**
+     * @see #getMetadataItem()
+     */
+    private static final ThreadLocal<MetadataItem> sMetadataItem = new ThreadLocal<>();
+
+    /**
+     * Index of the EmojiMetadata in {@link MetadataList}.
+     */
+    private final int mIndex;
+
+    /**
+     * MetadataRepo that holds this instance.
+     */
+    private final MetadataRepo mMetadataRepo;
+
+    /**
+     * Whether the system can render the emoji. Calculated at runtime on the device.
+     */
+    @HasGlyph
+    private volatile int mHasGlyph = HAS_GLYPH_UNKNOWN;
+
+    EmojiMetadata(@NonNull final MetadataRepo metadataRepo, @IntRange(from = 0) final int index) {
+        mMetadataRepo = metadataRepo;
+        mIndex = index;
+    }
+
+    /**
+     * Draws the emoji represented by this EmojiMetadata onto a canvas with origin at (x,y), using
+     * the specified paint.
+     *
+     * @param canvas Canvas to be drawn
+     * @param x x-coordinate of the origin of the emoji being drawn
+     * @param y y-coordinate of the baseline of the emoji being drawn
+     * @param paint Paint used for the text (e.g. color, size, style)
+     */
+    public void draw(@NonNull final Canvas canvas, final float x, final float y,
+            @NonNull final Paint paint) {
+        final Typeface typeface = mMetadataRepo.getTypeface();
+        final Typeface oldTypeface = paint.getTypeface();
+        paint.setTypeface(typeface);
+        // MetadataRepo.getEmojiCharArray() is a continous array of chars that is used to store the
+        // chars for emojis. since all emojis are mapped to a single codepoint, and since it is 2
+        // chars wide, we assume that the start index of the current emoji is mIndex * 2, and it is
+        // 2 chars long.
+        final int charArrayStartIndex = mIndex * 2;
+        canvas.drawText(mMetadataRepo.getEmojiCharArray(), charArrayStartIndex, 2, x, y, paint);
+        paint.setTypeface(oldTypeface);
+    }
+
+    /**
+     * @return a ThreadLocal instance of MetadataItem for this EmojiMetadata
+     */
+    private MetadataItem getMetadataItem() {
+        MetadataItem result = sMetadataItem.get();
+        if (result == null) {
+            result = new MetadataItem();
+            sMetadataItem.set(result);
+        }
+        // MetadataList is a wrapper around the metadata ByteBuffer. MetadataItem is a wrapper with
+        // an index (pointer) on this ByteBuffer that represents a single emoji. Both are FlatBuffer
+        // classes that wraps a ByteBuffer and gives access to the information in it. In order not
+        // to create a wrapper class for each EmojiMetadata, we use mIndex as the index of the
+        // MetadataItem in the ByteBuffer. We need to reiniitalize the current thread local instance
+        // by executing the statement below. All the statement does is to set an int index in
+        // MetadataItem. the same instance is used by all EmojiMetadata classes in the same thread.
+        mMetadataRepo.getMetadataList().list(result, mIndex);
+        return result;
+    }
+
+    /**
+     * @return unique id for the emoji
+     */
+    public int getId() {
+        return getMetadataItem().id();
+    }
+
+    /**
+     * @return width of the emoji image
+     */
+    public short getWidth() {
+        return getMetadataItem().width();
+    }
+
+    /**
+     * @return height of the emoji image
+     */
+    public short getHeight() {
+        return getMetadataItem().height();
+    }
+
+    /**
+     * @return in which metadata version the emoji was added to metadata
+     */
+    public short getCompatAdded() {
+        return getMetadataItem().compatAdded();
+    }
+
+    /**
+     * @return first SDK that the support for this emoji was added
+     */
+    public short getSdkAdded() {
+        return getMetadataItem().sdkAdded();
+    }
+
+    /**
+     * @return whether the emoji is in Emoji Presentation by default (without emoji
+     * style selector 0xFE0F)
+     */
+    @HasGlyph
+    public int getHasGlyph() {
+        return mHasGlyph;
+    }
+
+    /**
+     * Set whether the system can render the emoji.
+     *
+     * @param hasGlyph {@code true} if system can render the emoji
+     */
+    public void setHasGlyph(boolean hasGlyph) {
+        mHasGlyph = hasGlyph ? HAS_GLYPH_EXISTS : HAS_GLYPH_ABSENT;
+    }
+
+    /**
+     * @return whether the emoji is in Emoji Presentation by default (without emoji
+     *         style selector 0xFE0F)
+     */
+    public boolean isDefaultEmoji() {
+        return getMetadataItem().emojiStyle();
+    }
+
+    /**
+     * @param index index of the codepoint
+     *
+     * @return the codepoint at index
+     */
+    public int getCodepointAt(int index) {
+        return getMetadataItem().codepoints(index);
+    }
+
+    /**
+     * @return the length of the codepoints for this emoji
+     */
+    public int getCodepointsLength() {
+        return getMetadataItem().codepointsLength();
+    }
+
+}
diff --git a/emoji/core/src/android/support/text/emoji/EmojiProcessor.java b/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
new file mode 100644
index 0000000..da54754
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
@@ -0,0 +1,776 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.os.Build;
+import android.support.annotation.AnyThread;
+import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.widget.SpannableBuilder;
+import android.support.v4.graphics.PaintCompat;
+import android.support.v4.util.Preconditions;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.method.KeyListener;
+import android.text.method.MetaKeyKeyListener;
+import android.view.KeyEvent;
+import android.view.inputmethod.InputConnection;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Processes the CharSequence and adds the emojis.
+ *
+ * @hide
+ */
+@AnyThread
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+final class EmojiProcessor {
+
+    /**
+     * State transition commands.
+     */
+    @IntDef({ACTION_ADVANCE_BOTH, ACTION_ADVANCE_END, ACTION_FLUSH})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface Action {
+    }
+
+    /**
+     * Advance the end pointer in CharSequence and reset the start to be the end.
+     */
+    private static final int ACTION_ADVANCE_BOTH = 1;
+
+    /**
+     * Advance end pointer in CharSequence.
+     */
+    private static final int ACTION_ADVANCE_END = 2;
+
+    /**
+     * Add a new emoji with the metadata in {@link ProcessorSm#getFlushMetadata()}. Advance end
+     * pointer in CharSequence and reset the start to be the end.
+     */
+    private static final int ACTION_FLUSH = 3;
+
+    /**
+     * Factory used to create EmojiSpans.
+     */
+    private final EmojiCompat.SpanFactory mSpanFactory;
+
+    /**
+     * Emoji metadata repository.
+     */
+    private final MetadataRepo mMetadataRepo;
+
+    /**
+     * Utility class that checks if the system can render a given glyph.
+     */
+    private GlyphChecker mGlyphChecker = new GlyphChecker();
+
+    EmojiProcessor(@NonNull final MetadataRepo metadataRepo,
+            @NonNull final EmojiCompat.SpanFactory spanFactory) {
+        mSpanFactory = spanFactory;
+        mMetadataRepo = metadataRepo;
+    }
+
+    EmojiMetadata getEmojiMetadata(@NonNull final CharSequence charSequence) {
+        final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode());
+        final int end = charSequence.length();
+        int currentOffset = 0;
+
+        while (currentOffset < end) {
+            final int codePoint = Character.codePointAt(charSequence, currentOffset);
+            final int action = sm.check(codePoint);
+            if (action != ACTION_ADVANCE_END) {
+                return null;
+            }
+            currentOffset += Character.charCount(codePoint);
+        }
+
+        if (sm.isInFlushableState()) {
+            return sm.getCurrentMetadata();
+        }
+
+        return null;
+    }
+
+    /**
+     * Checks a given CharSequence for emojis, and adds EmojiSpans if any emojis are found.
+     * <p>
+     * <ul>
+     * <li>If no emojis are found, {@code charSequence} given as the input is returned without
+     * any changes. i.e. charSequence is a String, and no emojis are found, the same String is
+     * returned.</li>
+     * <li>If the given input is not a Spannable (such as String), and at least one emoji is found
+     * a new {@link android.text.Spannable} instance is returned. </li>
+     * <li>If the given input is a Spannable, the same instance is returned. </li>
+     * </ul>
+     *
+     * @param charSequence CharSequence to add the EmojiSpans, cannot be {@code null}
+     * @param start start index in the charSequence to look for emojis, should be greater than or
+     *              equal to {@code 0}, also less than {@code charSequence.length()}
+     * @param end end index in the charSequence to look for emojis, should be greater than or
+     *            equal to {@code start} parameter, also less than {@code charSequence.length()}
+     * @param maxEmojiCount maximum number of emojis in the {@code charSequence}, should be greater
+     *                      than or equal to {@code 0}
+     * @param replaceAll whether to replace all emoji with {@link EmojiSpan}s
+     */
+    CharSequence process(@NonNull final CharSequence charSequence, @IntRange(from = 0) int start,
+            @IntRange(from = 0) int end, @IntRange(from = 0) int maxEmojiCount,
+            final boolean replaceAll) {
+        final boolean isSpannableBuilder = charSequence instanceof SpannableBuilder;
+        if (isSpannableBuilder) {
+            ((SpannableBuilder) charSequence).beginBatchEdit();
+        }
+
+        try {
+            Spannable spannable = null;
+            // if it is a spannable already, use the same instance to add/remove EmojiSpans.
+            // otherwise wait until the the first EmojiSpan found in order to change the result
+            // into a Spannable.
+            if (isSpannableBuilder || charSequence instanceof Spannable) {
+                spannable = (Spannable) charSequence;
+            }
+
+            if (spannable != null) {
+                final EmojiSpan[] spans = spannable.getSpans(start, end, EmojiSpan.class);
+                if (spans != null && spans.length > 0) {
+                    // remove existing spans, and realign the start, end according to spans
+                    // if start or end is in the middle of an emoji they should be aligned
+                    final int length = spans.length;
+                    for (int index = 0; index < length; index++) {
+                        final EmojiSpan span = spans[index];
+                        final int spanStart = spannable.getSpanStart(span);
+                        final int spanEnd = spannable.getSpanEnd(span);
+                        // Remove span only when its spanStart is NOT equal to current end.
+                        // During add operation an emoji at index 0 is added with 0-1 as start and
+                        // end indices. Therefore if there are emoji spans at [0-1] and [1-2]
+                        // and end is 1, the span between 0-1 should be deleted, not 1-2.
+                        if (spanStart != end) {
+                            spannable.removeSpan(span);
+                        }
+                        start = Math.min(spanStart, start);
+                        end = Math.max(spanEnd, end);
+                    }
+                }
+            }
+
+            if (start == end || start >= charSequence.length()) {
+                return charSequence;
+            }
+
+            // calculate max number of emojis that can be added. since getSpans call is a relatively
+            // expensive operation, do it only when maxEmojiCount is not unlimited.
+            if (maxEmojiCount != EmojiCompat.EMOJI_COUNT_UNLIMITED && spannable != null) {
+                maxEmojiCount -= spannable.getSpans(0, spannable.length(), EmojiSpan.class).length;
+            }
+            // add new ones
+            int addedCount = 0;
+            final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode());
+
+            int currentOffset = start;
+            int codePoint = Character.codePointAt(charSequence, currentOffset);
+
+            while (currentOffset < end && addedCount < maxEmojiCount) {
+                final int action = sm.check(codePoint);
+
+                switch (action) {
+                    case ACTION_ADVANCE_BOTH:
+                        currentOffset += Character.charCount(codePoint);
+                        start = currentOffset;
+                        if (currentOffset < end) {
+                            codePoint = Character.codePointAt(charSequence, currentOffset);
+                        }
+                        break;
+                    case ACTION_ADVANCE_END:
+                        currentOffset += Character.charCount(codePoint);
+                        if (currentOffset < end) {
+                            codePoint = Character.codePointAt(charSequence, currentOffset);
+                        }
+                        break;
+                    case ACTION_FLUSH:
+                        if (replaceAll || !hasGlyph(charSequence, start, currentOffset,
+                                sm.getFlushMetadata())) {
+                            if (spannable == null) {
+                                spannable = new SpannableString(charSequence);
+                            }
+                            addEmoji(spannable, sm.getFlushMetadata(), start, currentOffset);
+                            addedCount++;
+                        }
+                        start = currentOffset;
+                        break;
+                }
+            }
+
+            // After the last codepoint is consumed the state machine might be in a state where it
+            // identified an emoji before. i.e. abc[women-emoji] when the last codepoint is consumed
+            // state machine is waiting to see if there is an emoji sequence (i.e. ZWJ).
+            // Need to check if it is in such a state.
+            if (sm.isInFlushableState() && addedCount < maxEmojiCount) {
+                if (replaceAll || !hasGlyph(charSequence, start, currentOffset,
+                        sm.getCurrentMetadata())) {
+                    if (spannable == null) {
+                        spannable = new SpannableString(charSequence);
+                    }
+                    addEmoji(spannable, sm.getCurrentMetadata(), start, currentOffset);
+                    addedCount++;
+                }
+            }
+            return spannable == null ? charSequence : spannable;
+        } finally {
+            if (isSpannableBuilder) {
+                ((SpannableBuilder) charSequence).endBatchEdit();
+            }
+        }
+    }
+
+    /**
+     * Handles onKeyDown commands from a {@link KeyListener} and if {@code keyCode} is one of
+     * {@link KeyEvent#KEYCODE_DEL} or {@link KeyEvent#KEYCODE_FORWARD_DEL} it tries to delete an
+     * {@link EmojiSpan} from an {@link Editable}. Returns {@code true} if an {@link EmojiSpan} is
+     * deleted with the characters it covers.
+     * <p/>
+     * If there is a selection where selection start is not equal to selection end, does not
+     * delete.
+     *
+     * @param editable Editable instance passed to {@link KeyListener#onKeyDown(android.view.View,
+     *                 Editable, int, KeyEvent)}
+     * @param keyCode keyCode passed to {@link KeyListener#onKeyDown(android.view.View, Editable,
+     *                int, KeyEvent)}
+     * @param event KeyEvent passed to {@link KeyListener#onKeyDown(android.view.View, Editable,
+     *              int, KeyEvent)}
+     *
+     * @return {@code true} if an {@link EmojiSpan} is deleted
+     */
+    static boolean handleOnKeyDown(@NonNull final Editable editable, final int keyCode,
+            final KeyEvent event) {
+        final boolean handled;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DEL:
+                handled = delete(editable, event, false /*forwardDelete*/);
+                break;
+            case KeyEvent.KEYCODE_FORWARD_DEL:
+                handled = delete(editable, event, true /*forwardDelete*/);
+                break;
+            default:
+                handled = false;
+                break;
+        }
+
+        if (handled) {
+            MetaKeyKeyListener.adjustMetaAfterKeypress(editable);
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean delete(final Editable content, final KeyEvent event,
+            final boolean forwardDelete) {
+        if (hasModifiers(event)) {
+            return false;
+        }
+
+        final int start = Selection.getSelectionStart(content);
+        final int end = Selection.getSelectionEnd(content);
+        if (hasInvalidSelection(start, end)) {
+            return false;
+        }
+
+        final EmojiSpan[] spans = content.getSpans(start, end, EmojiSpan.class);
+        if (spans != null && spans.length > 0) {
+            final int length = spans.length;
+            for (int index = 0; index < length; index++) {
+                final EmojiSpan span = spans[index];
+                final int spanStart = content.getSpanStart(span);
+                final int spanEnd = content.getSpanEnd(span);
+                if ((forwardDelete && spanStart == start)
+                        || (!forwardDelete && spanEnd == start)
+                        || (start > spanStart && start < spanEnd)) {
+                    content.delete(spanStart, spanEnd);
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Handles deleteSurroundingText commands from {@link InputConnection} and tries to delete an
+     * {@link EmojiSpan} from an {@link Editable}. Returns {@code true} if an {@link EmojiSpan} is
+     * deleted.
+     * <p/>
+     * If there is a selection where selection start is not equal to selection end, does not
+     * delete.
+     *
+     * @param inputConnection InputConnection instance
+     * @param editable TextView.Editable instance
+     * @param beforeLength the number of characters before the cursor to be deleted
+     * @param afterLength the number of characters after the cursor to be deleted
+     * @param inCodePoints {@code true} if length parameters are in codepoints
+     *
+     * @return {@code true} if an {@link EmojiSpan} is deleted
+     */
+    static boolean handleDeleteSurroundingText(@NonNull final InputConnection inputConnection,
+            @NonNull final Editable editable, @IntRange(from = 0) final int beforeLength,
+            @IntRange(from = 0) final int afterLength, final boolean inCodePoints) {
+        //noinspection ConstantConditions
+        if (editable == null || inputConnection == null) {
+            return false;
+        }
+
+        if (beforeLength < 0 || afterLength < 0) {
+            return false;
+        }
+
+        final int selectionStart = Selection.getSelectionStart(editable);
+        final int selectionEnd = Selection.getSelectionEnd(editable);
+
+        if (hasInvalidSelection(selectionStart, selectionEnd)) {
+            return false;
+        }
+
+        int start;
+        int end;
+        if (inCodePoints) {
+            // go backwards in terms of codepoints
+            start = CodepointIndexFinder.findIndexBackward(editable, selectionStart,
+                    Math.max(beforeLength, 0));
+            end = CodepointIndexFinder.findIndexForward(editable, selectionEnd,
+                    Math.max(afterLength, 0));
+
+            if (start == CodepointIndexFinder.INVALID_INDEX
+                    || end == CodepointIndexFinder.INVALID_INDEX) {
+                return false;
+            }
+        } else {
+            start = Math.max(selectionStart - beforeLength, 0);
+            end = Math.min(selectionEnd + afterLength, editable.length());
+        }
+
+        final EmojiSpan[] spans = editable.getSpans(start, end, EmojiSpan.class);
+        if (spans != null && spans.length > 0) {
+            final int length = spans.length;
+            for (int index = 0; index < length; index++) {
+                final EmojiSpan span = spans[index];
+                int spanStart = editable.getSpanStart(span);
+                int spanEnd = editable.getSpanEnd(span);
+                start = Math.min(spanStart, start);
+                end = Math.max(spanEnd, end);
+            }
+
+            start = Math.max(start, 0);
+            end = Math.min(end, editable.length());
+
+            inputConnection.beginBatchEdit();
+            editable.delete(start, end);
+            inputConnection.endBatchEdit();
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean hasInvalidSelection(final int start, final int end) {
+        return start == -1 || end == -1 || start != end;
+    }
+
+    private static boolean hasModifiers(KeyEvent event) {
+        return !KeyEvent.metaStateHasNoModifiers(event.getMetaState());
+    }
+
+    private void addEmoji(@NonNull final Spannable spannable, final EmojiMetadata metadata,
+            final int start, final int end) {
+        final EmojiSpan span = mSpanFactory.createSpan(metadata);
+        spannable.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+    }
+
+    /**
+     * Checks whether the current OS can render a given emoji. Used by the system to decide if an
+     * emoji span should be added. If the system cannot render it, an emoji span will be added.
+     * Used only for the case where replaceAll is set to {@code false}.
+     *
+     * @param charSequence the CharSequence that the emoji is in
+     * @param start start index of the emoji in the CharSequence
+     * @param end end index of the emoji in the CharSequence
+     * @param metadata EmojiMetadata instance for the emoji
+     *
+     * @return {@code true} if the OS can render emoji, {@code false} otherwise
+     */
+    private boolean hasGlyph(final CharSequence charSequence, int start, final int end,
+            final EmojiMetadata metadata) {
+        // For pre M devices, heuristic in PaintCompat can result in false positives. we are
+        // adding another heuristic using the sdkAdded field. if the emoji was added to OS
+        // at a later version we assume that the system probably cannot render it.
+        if (Build.VERSION.SDK_INT < 23 && metadata.getSdkAdded() > Build.VERSION.SDK_INT) {
+            return false;
+        }
+
+        // if the existence is not calculated yet
+        if (metadata.getHasGlyph() == EmojiMetadata.HAS_GLYPH_UNKNOWN) {
+            final boolean hasGlyph = mGlyphChecker.hasGlyph(charSequence, start, end);
+            metadata.setHasGlyph(hasGlyph);
+        }
+
+        return metadata.getHasGlyph() == EmojiMetadata.HAS_GLYPH_EXISTS;
+    }
+
+    /**
+     * Set the GlyphChecker instance used by EmojiProcessor. Used for testing.
+     */
+    void setGlyphChecker(@NonNull final GlyphChecker glyphChecker) {
+        Preconditions.checkNotNull(glyphChecker);
+        mGlyphChecker = glyphChecker;
+    }
+
+    /**
+     * State machine for walking over the metadata trie.
+     */
+    static final class ProcessorSm {
+
+        private static final int STATE_DEFAULT = 1;
+        private static final int STATE_WALKING = 2;
+
+        private int mState = STATE_DEFAULT;
+
+        /**
+         * Root of the trie
+         */
+        private final MetadataRepo.Node mRootNode;
+
+        /**
+         * Pointer to the node after last codepoint.
+         */
+        private MetadataRepo.Node mCurrentNode;
+
+        /**
+         * The node where ACTION_FLUSH is called. Required since after flush action is
+         * returned mCurrentNode is reset to be the root.
+         */
+        private MetadataRepo.Node mFlushNode;
+
+        /**
+         * The code point that was checked.
+         */
+        private int mLastCodepoint;
+
+        /**
+         * Level for mCurrentNode. Root is 0.
+         */
+        private int mCurrentDepth;
+
+        ProcessorSm(MetadataRepo.Node rootNode) {
+            mRootNode = rootNode;
+            mCurrentNode = rootNode;
+        }
+
+        @Action
+        int check(final int codePoint) {
+            final int action;
+            MetadataRepo.Node node = mCurrentNode.get(codePoint);
+            switch (mState) {
+                case STATE_WALKING:
+                    if (node != null) {
+                        mCurrentNode = node;
+                        mCurrentDepth += 1;
+                        action = ACTION_ADVANCE_END;
+                    } else {
+                        if (isTextStyle(codePoint)) {
+                            action = reset();
+                        } else if (isEmojiStyle(codePoint)) {
+                            action = ACTION_ADVANCE_END;
+                        } else if (mCurrentNode.getData() != null) {
+                            if (mCurrentDepth == 1) {
+                                if (mCurrentNode.getData().isDefaultEmoji()
+                                        || isEmojiStyle(mLastCodepoint)) {
+                                    mFlushNode = mCurrentNode;
+                                    action = ACTION_FLUSH;
+                                    reset();
+                                } else {
+                                    action = reset();
+                                }
+                            } else {
+                                mFlushNode = mCurrentNode;
+                                action = ACTION_FLUSH;
+                                reset();
+                            }
+                        } else {
+                            action = reset();
+                        }
+                    }
+                    break;
+                case STATE_DEFAULT:
+                default:
+                    if (node == null) {
+                        action = reset();
+                    } else {
+                        mState = STATE_WALKING;
+                        mCurrentNode = node;
+                        mCurrentDepth = 1;
+                        action = ACTION_ADVANCE_END;
+                    }
+                    break;
+            }
+
+            mLastCodepoint = codePoint;
+            return action;
+        }
+
+        @Action
+        private int reset() {
+            mState = STATE_DEFAULT;
+            mCurrentNode = mRootNode;
+            mCurrentDepth = 0;
+            return ACTION_ADVANCE_BOTH;
+        }
+
+        /**
+         * @return the metadata node when ACTION_FLUSH is returned
+         */
+        EmojiMetadata getFlushMetadata() {
+            return mFlushNode.getData();
+        }
+
+        /**
+         * @return current pointer to the metadata node in the trie
+         */
+        EmojiMetadata getCurrentMetadata() {
+            return mCurrentNode.getData();
+        }
+
+        /**
+         * Need for the case where input is consumed, but action_flush was not called. For example
+         * when the char sequence has single codepoint character which is a default emoji. State
+         * machine will wait for the next.
+         *
+         * @return whether the current state requires an emoji to be added
+         */
+        boolean isInFlushableState() {
+            return mState == STATE_WALKING && mCurrentNode.getData() != null
+                    && (mCurrentNode.getData().isDefaultEmoji()
+                    || isEmojiStyle(mLastCodepoint)
+                    || mCurrentDepth > 1);
+        }
+
+        /**
+         * @param codePoint CodePoint to check
+         *
+         * @return {@code true} if the codepoint is a emoji style standardized variation selector
+         */
+        private static boolean isEmojiStyle(int codePoint) {
+            return codePoint == 0xFE0F;
+        }
+
+        /**
+         * @param codePoint CodePoint to check
+         *
+         * @return {@code true} if the codepoint is a text style standardized variation selector
+         */
+        private static boolean isTextStyle(int codePoint) {
+            return codePoint == 0xFE0E;
+        }
+    }
+
+    /**
+     * Copy of BaseInputConnection findIndexBackward and findIndexForward functions.
+     */
+    private static final class CodepointIndexFinder {
+        private static final int INVALID_INDEX = -1;
+
+        /**
+         * Find start index of the character in {@code cs} that is {@code numCodePoints} behind
+         * starting from {@code from}.
+         *
+         * @param cs CharSequence to work on
+         * @param from the index to start going backwards
+         * @param numCodePoints the number of codepoints
+         *
+         * @return start index of the character
+         */
+        private static int findIndexBackward(final CharSequence cs, final int from,
+                final int numCodePoints) {
+            int currentIndex = from;
+            boolean waitingHighSurrogate = false;
+            final int length = cs.length();
+            if (currentIndex < 0 || length < currentIndex) {
+                return INVALID_INDEX;  // The starting point is out of range.
+            }
+            if (numCodePoints < 0) {
+                return INVALID_INDEX;  // Basically this should not happen.
+            }
+            int remainingCodePoints = numCodePoints;
+            while (true) {
+                if (remainingCodePoints == 0) {
+                    return currentIndex;  // Reached to the requested length in code points.
+                }
+
+                --currentIndex;
+                if (currentIndex < 0) {
+                    if (waitingHighSurrogate) {
+                        return INVALID_INDEX;  // An invalid surrogate pair is found.
+                    }
+                    return 0;  // Reached to the beginning of the text w/o any invalid surrogate
+                    // pair.
+                }
+                final char c = cs.charAt(currentIndex);
+                if (waitingHighSurrogate) {
+                    if (!Character.isHighSurrogate(c)) {
+                        return INVALID_INDEX;  // An invalid surrogate pair is found.
+                    }
+                    waitingHighSurrogate = false;
+                    --remainingCodePoints;
+                    continue;
+                }
+                if (!Character.isSurrogate(c)) {
+                    --remainingCodePoints;
+                    continue;
+                }
+                if (Character.isHighSurrogate(c)) {
+                    return INVALID_INDEX;  // A invalid surrogate pair is found.
+                }
+                waitingHighSurrogate = true;
+            }
+        }
+
+        /**
+         * Find start index of the character in {@code cs} that is {@code numCodePoints} ahead
+         * starting from {@code from}.
+         *
+         * @param cs CharSequence to work on
+         * @param from the index to start going forward
+         * @param numCodePoints the number of codepoints
+         *
+         * @return start index of the character
+         */
+        private static int findIndexForward(final CharSequence cs, final int from,
+                final int numCodePoints) {
+            int currentIndex = from;
+            boolean waitingLowSurrogate = false;
+            final int length = cs.length();
+            if (currentIndex < 0 || length < currentIndex) {
+                return INVALID_INDEX;  // The starting point is out of range.
+            }
+            if (numCodePoints < 0) {
+                return INVALID_INDEX;  // Basically this should not happen.
+            }
+            int remainingCodePoints = numCodePoints;
+
+            while (true) {
+                if (remainingCodePoints == 0) {
+                    return currentIndex;  // Reached to the requested length in code points.
+                }
+
+                if (currentIndex >= length) {
+                    if (waitingLowSurrogate) {
+                        return INVALID_INDEX;  // An invalid surrogate pair is found.
+                    }
+                    return length;  // Reached to the end of the text w/o any invalid surrogate
+                    // pair.
+                }
+                final char c = cs.charAt(currentIndex);
+                if (waitingLowSurrogate) {
+                    if (!Character.isLowSurrogate(c)) {
+                        return INVALID_INDEX;  // An invalid surrogate pair is found.
+                    }
+                    --remainingCodePoints;
+                    waitingLowSurrogate = false;
+                    ++currentIndex;
+                    continue;
+                }
+                if (!Character.isSurrogate(c)) {
+                    --remainingCodePoints;
+                    ++currentIndex;
+                    continue;
+                }
+                if (Character.isLowSurrogate(c)) {
+                    return INVALID_INDEX;  // A invalid surrogate pair is found.
+                }
+                waitingLowSurrogate = true;
+                ++currentIndex;
+            }
+        }
+    }
+
+    /**
+     * Utility class that checks if the system can render a given glyph.
+     *
+     * @hide
+     */
+    @AnyThread
+    @RestrictTo(LIBRARY_GROUP)
+    public static class GlyphChecker {
+        /**
+         * Default text size for {@link #mTextPaint}.
+         */
+        private static final int PAINT_TEXT_SIZE = 10;
+
+        /**
+         * Used to create strings required by
+         * {@link PaintCompat#hasGlyph(android.graphics.Paint, String)}.
+         */
+        private static final ThreadLocal<StringBuilder> sStringBuilder = new ThreadLocal<>();
+
+        /**
+         * TextPaint used during {@link PaintCompat#hasGlyph(android.graphics.Paint, String)} check.
+         */
+        private final TextPaint mTextPaint;
+
+        GlyphChecker() {
+            mTextPaint = new TextPaint();
+            mTextPaint.setTextSize(PAINT_TEXT_SIZE);
+        }
+
+        /**
+         * Returns whether the system can render an emoji.
+         *
+         * @param charSequence the CharSequence that the emoji is in
+         * @param start start index of the emoji in the CharSequence
+         * @param end end index of the emoji in the CharSequence
+         *
+         * @return {@code true} if the OS can render emoji, {@code false} otherwise
+         */
+        public boolean hasGlyph(final CharSequence charSequence, int start, final int end) {
+            final StringBuilder builder = getStringBuilder();
+            builder.setLength(0);
+
+            while (start < end) {
+                builder.append(charSequence.charAt(start));
+                start++;
+            }
+
+            return PaintCompat.hasGlyph(mTextPaint, builder.toString());
+        }
+
+        private static StringBuilder getStringBuilder() {
+            if (sStringBuilder.get() == null) {
+                sStringBuilder.set(new StringBuilder());
+            }
+            return sStringBuilder.get();
+        }
+
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/EmojiSpan.java b/emoji/core/src/android/support/text/emoji/EmojiSpan.java
new file mode 100644
index 0000000..1e76c78
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/EmojiSpan.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Paint;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.util.Preconditions;
+import android.text.style.ReplacementSpan;
+
+/**
+ * Base span class for the emoji replacement. When an emoji is found and needs to be replaced in a
+ * CharSequence, an instance of this class is added to the CharSequence.
+ */
+@RequiresApi(19)
+public abstract class EmojiSpan extends ReplacementSpan {
+
+    /**
+     * Temporary object to calculate the size of the span.
+     */
+    private final Paint.FontMetricsInt mTmpFontMetrics = new Paint.FontMetricsInt();
+
+    /**
+     * Information about emoji. This is not parcelled since we do not want multiple objects
+     * representing same emoji to be in memory. When unparcelled, EmojiSpan tries to set it back
+     * using the singleton EmojiCompat instance.
+     */
+    private final EmojiMetadata mMetadata;
+
+    /**
+     * Cached width of the span. Width is calculated according to the font metrics.
+     */
+    private short mWidth = -1;
+
+    /**
+     * Cached height of the span. Height is calculated according to the font metrics.
+     */
+    private short mHeight = -1;
+
+    /**
+     * Cached ratio of current font height to emoji image height.
+     */
+    private float mRatio = 1.0f;
+
+    /**
+     * Default constructor.
+     *
+     * @param metadata information about the emoji, cannot be {@code null}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    EmojiSpan(@NonNull final EmojiMetadata metadata) {
+        Preconditions.checkNotNull(metadata, "metadata cannot be null");
+        mMetadata = metadata;
+    }
+
+    @Override
+    public int getSize(@NonNull final Paint paint, final CharSequence text, final int start,
+            final int end, final Paint.FontMetricsInt fm) {
+        paint.getFontMetricsInt(mTmpFontMetrics);
+        final int fontHeight = Math.abs(mTmpFontMetrics.descent - mTmpFontMetrics.ascent);
+
+        mRatio = fontHeight * 1.0f / mMetadata.getHeight();
+        mHeight = (short) (mMetadata.getHeight() * mRatio);
+        mWidth = (short) (mMetadata.getWidth() * mRatio);
+
+        if (fm != null) {
+            fm.ascent = mTmpFontMetrics.ascent;
+            fm.descent = mTmpFontMetrics.descent;
+            fm.top = mTmpFontMetrics.top;
+            fm.bottom = mTmpFontMetrics.bottom;
+        }
+
+        return mWidth;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    final EmojiMetadata getMetadata() {
+        return mMetadata;
+    }
+
+    /**
+     * @return width of the span
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    final int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * @return height of the span
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    final int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    final float getRatio() {
+        return mRatio;
+    }
+
+    /**
+     * @return unique id for the emoji that this EmojiSpan is used for
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @VisibleForTesting
+    public final int getId() {
+        return getMetadata().getId();
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java b/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java
new file mode 100644
index 0000000..05f367c
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java
@@ -0,0 +1,360 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.SystemClock;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.graphics.TypefaceCompatUtil;
+import android.support.v4.provider.FontRequest;
+import android.support.v4.provider.FontsContractCompat;
+import android.support.v4.provider.FontsContractCompat.FontFamilyResult;
+import android.support.v4.util.Preconditions;
+
+import java.nio.ByteBuffer;
+
+/**
+ * {@link EmojiCompat.Config} implementation that asynchronously fetches the required font and the
+ * metadata using a {@link FontRequest}. FontRequest should be constructed to fetch an EmojiCompat
+ * compatible emoji font.
+ * <p/>
+ */
+public class FontRequestEmojiCompatConfig extends EmojiCompat.Config {
+
+    /**
+     * Retry policy used when the font provider is not ready to give the font file.
+     *
+     * To control the thread the retries are handled on, see
+     * {@link FontRequestEmojiCompatConfig#setHandler}.
+     */
+    public abstract static class RetryPolicy {
+        /**
+         * Called each time the metadata loading fails.
+         *
+         * This is primarily due to a pending download of the font.
+         * If a value larger than zero is returned, metadata loader will retry after the given
+         * milliseconds.
+         * <br />
+         * If {@code zero} is returned, metadata loader will retry immediately.
+         * <br/>
+         * If a value less than 0 is returned, the metadata loader will stop retrying and
+         * EmojiCompat will get into {@link EmojiCompat#LOAD_STATE_FAILED} state.
+         * <p/>
+         * Note that the retry may happen earlier than you specified if the font provider notifies
+         * that the download is completed.
+         *
+         * @return long milliseconds to wait until next retry
+         */
+        public abstract long getRetryDelay();
+    }
+
+    /**
+     * A retry policy implementation that doubles the amount of time in between retries.
+     *
+     * If downloading hasn't finish within given amount of time, this policy give up and the
+     * EmojiCompat will get into {@link EmojiCompat#LOAD_STATE_FAILED} state.
+     */
+    public static class ExponentialBackoffRetryPolicy extends RetryPolicy {
+        private final long mTotalMs;
+        private long mRetryOrigin;
+
+        /**
+         * @param totalMs A total amount of time to wait in milliseconds.
+         */
+        public ExponentialBackoffRetryPolicy(long totalMs) {
+            mTotalMs = totalMs;
+        }
+
+        @Override
+        public long getRetryDelay() {
+            if (mRetryOrigin == 0) {
+                mRetryOrigin = SystemClock.uptimeMillis();
+                // Since download may be completed after getting query result and before registering
+                // observer, requesting later at the same time.
+                return 0;
+            } else {
+                // Retry periodically since we can't trust notify change event. Some font provider
+                // may not notify us.
+                final long elapsedMillis = SystemClock.uptimeMillis() - mRetryOrigin;
+                if (elapsedMillis > mTotalMs) {
+                    return -1;  // Give up since download hasn't finished in 10 min.
+                }
+                // Wait until the same amount of the time from the first scheduled time, but adjust
+                // the minimum request interval is 1 sec and never exceeds 10 min in total.
+                return Math.min(Math.max(elapsedMillis, 1000), mTotalMs - elapsedMillis);
+            }
+        }
+    };
+
+    /**
+     * @param context Context instance, cannot be {@code null}
+     * @param request {@link FontRequest} to fetch the font asynchronously, cannot be {@code null}
+     */
+    public FontRequestEmojiCompatConfig(@NonNull Context context, @NonNull FontRequest request) {
+        super(new FontRequestMetadataLoader(context, request, DEFAULT_FONTS_CONTRACT));
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public FontRequestEmojiCompatConfig(@NonNull Context context, @NonNull FontRequest request,
+            @NonNull FontProviderHelper fontProviderHelper) {
+        super(new FontRequestMetadataLoader(context, request, fontProviderHelper));
+    }
+
+    /**
+     * Sets the custom handler to be used for initialization.
+     *
+     * Since font fetch take longer time, the metadata loader will fetch the fonts on the background
+     * thread. You can pass your own handler for this background fetching. This handler is also used
+     * for retrying.
+     *
+     * @param handler A {@link Handler} to be used for initialization. Can be {@code null}. In case
+     *               of {@code null}, the metadata loader creates own {@link HandlerThread} for
+     *               initialization.
+     */
+    public FontRequestEmojiCompatConfig setHandler(Handler handler) {
+        ((FontRequestMetadataLoader) getMetadataRepoLoader()).setHandler(handler);
+        return this;
+    }
+
+    /**
+     * Sets the retry policy.
+     *
+     * {@see RetryPolicy}
+     * @param policy The policy to be used when the font provider is not ready to give the font
+     *              file. Can be {@code null}. In case of {@code null}, the metadata loader never
+     *              retries.
+     */
+    public FontRequestEmojiCompatConfig setRetryPolicy(RetryPolicy policy) {
+        ((FontRequestMetadataLoader) getMetadataRepoLoader()).setRetryPolicy(policy);
+        return this;
+    }
+
+    /**
+     * MetadataRepoLoader implementation that uses FontsContractCompat and TypefaceCompat to load a
+     * given FontRequest.
+     */
+    private static class FontRequestMetadataLoader implements EmojiCompat.MetadataRepoLoader {
+        private final Context mContext;
+        private final FontRequest mRequest;
+        private final FontProviderHelper mFontProviderHelper;
+
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private Handler mHandler;
+        @GuardedBy("mLock")
+        private HandlerThread mThread;
+        @GuardedBy("mLock")
+        private @Nullable RetryPolicy mRetryPolicy;
+
+        // Following three variables must be touched only on the thread associated with mHandler.
+        private EmojiCompat.MetadataRepoLoaderCallback mCallback;
+        private ContentObserver mObserver;
+        private Runnable mHandleMetadataCreationRunner;
+
+        FontRequestMetadataLoader(@NonNull Context context, @NonNull FontRequest request,
+                @NonNull FontProviderHelper fontProviderHelper) {
+            Preconditions.checkNotNull(context, "Context cannot be null");
+            Preconditions.checkNotNull(request, "FontRequest cannot be null");
+            mContext = context.getApplicationContext();
+            mRequest = request;
+            mFontProviderHelper = fontProviderHelper;
+        }
+
+        public void setHandler(Handler handler) {
+            synchronized (mLock) {
+                mHandler = handler;
+            }
+        }
+
+        public void setRetryPolicy(RetryPolicy policy) {
+            synchronized (mLock) {
+                mRetryPolicy = policy;
+            }
+        }
+
+        @Override
+        @RequiresApi(19)
+        public void load(@NonNull final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+            Preconditions.checkNotNull(loaderCallback, "LoaderCallback cannot be null");
+            synchronized (mLock) {
+                if (mHandler == null) {
+                    // Developer didn't give a thread for fetching. Create our own one.
+                    mThread = new HandlerThread("emojiCompat", Process.THREAD_PRIORITY_BACKGROUND);
+                    mThread.start();
+                    mHandler = new Handler(mThread.getLooper());
+                }
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCallback = loaderCallback;
+                        createMetadata();
+                    }
+                });
+            }
+        }
+
+        private FontsContractCompat.FontInfo retrieveFontInfo() {
+            final FontsContractCompat.FontFamilyResult result;
+            try {
+                result = mFontProviderHelper.fetchFonts(mContext, mRequest);
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException("provider not found", e);
+            }
+            if (result.getStatusCode() != FontsContractCompat.FontFamilyResult.STATUS_OK) {
+                throw new RuntimeException("fetchFonts failed (" + result.getStatusCode() + ")");
+            }
+            final FontsContractCompat.FontInfo[] fonts = result.getFonts();
+            if (fonts == null || fonts.length == 0) {
+                throw new RuntimeException("fetchFonts failed (empty result)");
+            }
+            return fonts[0];  // Assuming the GMS Core provides only one font file.
+        }
+
+        // Must be called on the mHandler.
+        @RequiresApi(19)
+        private void scheduleRetry(Uri uri, long waitMs) {
+            synchronized (mLock) {
+                if (mObserver == null) {
+                    mObserver = new ContentObserver(mHandler) {
+                        @Override
+                        public void onChange(boolean selfChange, Uri uri) {
+                            createMetadata();
+                        }
+                    };
+                    mFontProviderHelper.registerObserver(mContext, uri, mObserver);
+                }
+                if (mHandleMetadataCreationRunner == null) {
+                    mHandleMetadataCreationRunner = new Runnable() {
+                        @Override
+                        public void run() {
+                            createMetadata();
+                        }
+                    };
+                }
+                mHandler.postDelayed(mHandleMetadataCreationRunner, waitMs);
+            }
+        }
+
+        // Must be called on the mHandler.
+        private void cleanUp() {
+            mCallback = null;
+            if (mObserver != null) {
+                mFontProviderHelper.unregisterObserver(mContext, mObserver);
+                mObserver = null;
+            }
+            synchronized (mLock) {
+                mHandler.removeCallbacks(mHandleMetadataCreationRunner);
+                if (mThread != null) {
+                    mThread.quit();
+                }
+                mHandler = null;
+                mThread = null;
+            }
+        }
+
+        // Must be called on the mHandler.
+        @RequiresApi(19)
+        private void createMetadata() {
+            if (mCallback == null) {
+                return;  // Already handled or cancelled. Do nothing.
+            }
+            try {
+                final FontsContractCompat.FontInfo font = retrieveFontInfo();
+
+                final int resultCode = font.getResultCode();
+                if (resultCode == FontsContractCompat.Columns.RESULT_CODE_FONT_UNAVAILABLE) {
+                    // The font provider is now downloading. Ask RetryPolicy for when to retry next.
+                    synchronized (mLock) {
+                        if (mRetryPolicy != null) {
+                            final long delayMs = mRetryPolicy.getRetryDelay();
+                            if (delayMs >= 0) {
+                                scheduleRetry(font.getUri(), delayMs);
+                                return;
+                            }
+                        }
+                    }
+                }
+
+                if (resultCode != FontsContractCompat.Columns.RESULT_CODE_OK) {
+                    throw new RuntimeException("fetchFonts result is not OK. (" + resultCode + ")");
+                }
+
+                // TODO: Good to add new API to create Typeface from FD not to open FD twice.
+                final Typeface typeface = mFontProviderHelper.buildTypeface(mContext, font);
+                final ByteBuffer buffer = TypefaceCompatUtil.mmap(mContext, null, font.getUri());
+                if (buffer == null) {
+                    throw new RuntimeException("Unable to open file.");
+                }
+                mCallback.onLoaded(MetadataRepo.create(typeface, buffer));
+                cleanUp();
+            } catch (Throwable t) {
+                mCallback.onFailed(t);
+                cleanUp();
+            }
+        }
+    }
+
+    /**
+     * Delegate class for mocking FontsContractCompat.fetchFonts.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static class FontProviderHelper {
+        /** Calls FontsContractCompat.fetchFonts. */
+        public FontFamilyResult fetchFonts(@NonNull Context context,
+                @NonNull FontRequest request) throws NameNotFoundException {
+            return FontsContractCompat.fetchFonts(context, null /* cancellation signal */, request);
+        }
+
+        /** Calls FontsContractCompat.buildTypeface. */
+        public Typeface buildTypeface(@NonNull Context context,
+                @NonNull FontsContractCompat.FontInfo font) throws NameNotFoundException {
+            return FontsContractCompat.buildTypeface(context, null /* cancellation signal */,
+                new FontsContractCompat.FontInfo[] { font });
+        }
+
+        /** Calls Context.getContentObserver().registerObserver */
+        public void registerObserver(@NonNull Context context, @NonNull Uri uri,
+                @NonNull ContentObserver observer) {
+            context.getContentResolver().registerContentObserver(
+                    uri, false /* notifyForDescendants */, observer);
+
+        }
+        /** Calls Context.getContentObserver().unregisterObserver */
+        public void unregisterObserver(@NonNull Context context,
+                @NonNull ContentObserver observer) {
+            context.getContentResolver().unregisterContentObserver(observer);
+        }
+    };
+
+    private static final FontProviderHelper DEFAULT_FONTS_CONTRACT = new FontProviderHelper();
+
+}
diff --git a/emoji/core/src/android/support/text/emoji/MetadataListReader.java b/emoji/core/src/android/support/text/emoji/MetadataListReader.java
new file mode 100644
index 0000000..6034726
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/MetadataListReader.java
@@ -0,0 +1,343 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.res.AssetManager;
+import android.support.annotation.AnyThread;
+import android.support.annotation.IntRange;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.flatbuffer.MetadataList;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Reads the emoji metadata from a given InputStream or ByteBuffer.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@AnyThread
+@RequiresApi(19)
+class MetadataListReader {
+
+    /**
+     * Meta tag for emoji metadata. This string is used by the font update script to insert the
+     * emoji meta into the font. This meta table contains the list of all emojis which are stored in
+     * binary format using FlatBuffers. This flat list is later converted by the system into a trie.
+     * {@code int} representation for "Emji"
+     *
+     * @see MetadataRepo
+     */
+    private static final int EMJI_TAG = 'E' << 24 | 'm' << 16 | 'j' << 8 | 'i';
+
+    /**
+     * Deprecated meta tag name. Do not use, kept for compatibility reasons, will be removed soon.
+     */
+    private static final int EMJI_TAG_DEPRECATED = 'e' << 24 | 'm' << 16 | 'j' << 8 | 'i';
+
+    /**
+     * The name of the meta table in the font. int representation for "meta"
+     */
+    private static final int META_TABLE_NAME = 'm' << 24 | 'e' << 16 | 't' << 8 | 'a';
+
+    /**
+     * Construct MetadataList from an input stream. Does not close the given InputStream, therefore
+     * it is caller's responsibility to properly close the stream.
+     *
+     * @param inputStream InputStream to read emoji metadata from
+     */
+    static MetadataList read(InputStream inputStream) throws IOException {
+        final OpenTypeReader openTypeReader = new InputStreamOpenTypeReader(inputStream);
+        final OffsetInfo offsetInfo = findOffsetInfo(openTypeReader);
+        // skip to where metadata is
+        openTypeReader.skip((int) (offsetInfo.getStartOffset() - openTypeReader.getPosition()));
+        // allocate a ByteBuffer and read into it since FlatBuffers can read only from a ByteBuffer
+        final ByteBuffer buffer = ByteBuffer.allocate((int) offsetInfo.getLength());
+        final int numRead = inputStream.read(buffer.array());
+        if (numRead != offsetInfo.getLength()) {
+            throw new IOException("Needed " + offsetInfo.getLength() + " bytes, got " + numRead);
+        }
+
+        return MetadataList.getRootAsMetadataList(buffer);
+    }
+
+    /**
+     * Construct MetadataList from a byte buffer.
+     *
+     * @param byteBuffer ByteBuffer to read emoji metadata from
+     */
+    static MetadataList read(final ByteBuffer byteBuffer) throws IOException {
+        final ByteBuffer newBuffer = byteBuffer.duplicate();
+        final OpenTypeReader reader = new ByteBufferReader(newBuffer);
+        final OffsetInfo offsetInfo = findOffsetInfo(reader);
+        // skip to where metadata is
+        newBuffer.position((int) offsetInfo.getStartOffset());
+        return MetadataList.getRootAsMetadataList(newBuffer);
+    }
+
+    /**
+     * Construct MetadataList from an asset.
+     *
+     * @param assetManager AssetManager instance
+     * @param assetPath asset manager path of the file that the Typeface and metadata will be
+     *                  created from
+     */
+    static MetadataList read(AssetManager assetManager, String assetPath)
+            throws IOException {
+        try (InputStream inputStream = assetManager.open(assetPath)) {
+            return read(inputStream);
+        }
+    }
+
+    /**
+     * Finds the start offset and length of the emoji metadata in the font.
+     *
+     * @return OffsetInfo which contains start offset and length of the emoji metadata in the font
+     *
+     * @throws IOException
+     */
+    private static OffsetInfo findOffsetInfo(OpenTypeReader reader) throws IOException {
+        // skip sfnt version
+        reader.skip(OpenTypeReader.UINT32_BYTE_COUNT);
+        // start of Table Count
+        final int tableCount = reader.readUnsignedShort();
+        if (tableCount > 100) {
+            //something is wrong quit
+            throw new IOException("Cannot read metadata.");
+        }
+        //skip to begining of tables data
+        reader.skip(OpenTypeReader.UINT16_BYTE_COUNT * 3);
+
+        long metaOffset = -1;
+        for (int i = 0; i < tableCount; i++) {
+            final int tag = reader.readTag();
+            // skip checksum
+            reader.skip(OpenTypeReader.UINT32_BYTE_COUNT);
+            final long offset = reader.readUnsignedInt();
+            // skip mLength
+            reader.skip(OpenTypeReader.UINT32_BYTE_COUNT);
+            if (META_TABLE_NAME == tag) {
+                metaOffset = offset;
+                break;
+            }
+        }
+
+        if (metaOffset != -1) {
+            // skip to the begining of meta tables.
+            reader.skip((int) (metaOffset - reader.getPosition()));
+            // skip minorVersion, majorVersion, flags, reserved,
+            reader.skip(
+                    OpenTypeReader.UINT16_BYTE_COUNT * 2 + OpenTypeReader.UINT32_BYTE_COUNT * 2);
+            final long mapsCount = reader.readUnsignedInt();
+            for (int i = 0; i < mapsCount; i++) {
+                final int tag = reader.readTag();
+                final long dataOffset = reader.readUnsignedInt();
+                final long dataLength = reader.readUnsignedInt();
+                if (EMJI_TAG == tag || EMJI_TAG_DEPRECATED == tag) {
+                    return new OffsetInfo(dataOffset + metaOffset, dataLength);
+                }
+            }
+        }
+
+        throw new IOException("Cannot read metadata.");
+    }
+
+    /**
+     * Start offset and length of the emoji metadata in the font.
+     */
+    private static class OffsetInfo {
+        private final long mStartOffset;
+        private final long mLength;
+
+        OffsetInfo(long startOffset, long length) {
+            mStartOffset = startOffset;
+            mLength = length;
+        }
+
+        long getStartOffset() {
+            return mStartOffset;
+        }
+
+        long getLength() {
+            return mLength;
+        }
+    }
+
+    private static int toUnsignedShort(final short value) {
+        return value & 0xFFFF;
+    }
+
+    private static long toUnsignedInt(final int value) {
+        return value & 0xFFFFFFFFL;
+    }
+
+    private interface OpenTypeReader {
+        int UINT16_BYTE_COUNT = 2;
+        int UINT32_BYTE_COUNT = 4;
+
+        /**
+         * Reads an {@code OpenType uint16}.
+         *
+         * @throws IOException
+         */
+        int readUnsignedShort() throws IOException;
+
+        /**
+         * Reads an {@code OpenType uint32}.
+         *
+         * @throws IOException
+         */
+        long readUnsignedInt() throws IOException;
+
+        /**
+         * Reads an {@code OpenType Tag}.
+         *
+         * @throws IOException
+         */
+        int readTag() throws IOException;
+
+        /**
+         * Skip the given amount of numOfBytes
+         *
+         * @throws IOException
+         */
+        void skip(int numOfBytes) throws IOException;
+
+        /**
+         * @return the position of the reader
+         */
+        long getPosition();
+    }
+
+    /**
+     * Reads {@code OpenType} data from an {@link InputStream}.
+     */
+    private static class InputStreamOpenTypeReader implements OpenTypeReader {
+
+        private final byte[] mByteArray;
+        private final ByteBuffer mByteBuffer;
+        private final InputStream mInputStream;
+        private long mPosition = 0;
+
+        /**
+         * Constructs the reader with the given InputStream. Does not close the InputStream, it is
+         * caller's responsibility to close it.
+         *
+         * @param inputStream InputStream to read from
+         */
+        InputStreamOpenTypeReader(final InputStream inputStream) {
+            mInputStream = inputStream;
+            mByteArray = new byte[UINT32_BYTE_COUNT];
+            mByteBuffer = ByteBuffer.wrap(mByteArray);
+            mByteBuffer.order(ByteOrder.BIG_ENDIAN);
+        }
+
+        @Override
+        public int readUnsignedShort() throws IOException {
+            mByteBuffer.position(0);
+            read(UINT16_BYTE_COUNT);
+            return toUnsignedShort(mByteBuffer.getShort());
+        }
+
+        @Override
+        public long readUnsignedInt() throws IOException {
+            mByteBuffer.position(0);
+            read(UINT32_BYTE_COUNT);
+            return toUnsignedInt(mByteBuffer.getInt());
+        }
+
+        @Override
+        public int readTag() throws IOException {
+            mByteBuffer.position(0);
+            read(UINT32_BYTE_COUNT);
+            return mByteBuffer.getInt();
+        }
+
+        @Override
+        public void skip(int numOfBytes) throws IOException {
+            while (numOfBytes > 0) {
+                long skipped = mInputStream.skip(numOfBytes);
+                if (skipped < 1) {
+                    throw new IOException("Skip didn't move at least 1 byte forward");
+                }
+                numOfBytes -= skipped;
+                mPosition += skipped;
+            }
+        }
+
+        @Override
+        public long getPosition() {
+            return mPosition;
+        }
+
+        private void read(@IntRange(from = 0, to = UINT32_BYTE_COUNT) final int numOfBytes)
+                throws IOException {
+            if (mInputStream.read(mByteArray, 0, numOfBytes) != numOfBytes) {
+                throw new IOException("read failed");
+            }
+            mPosition += numOfBytes;
+        }
+    }
+
+    /**
+     * Reads OpenType data from a ByteBuffer.
+     */
+    private static class ByteBufferReader implements OpenTypeReader {
+
+        private final ByteBuffer mByteBuffer;
+
+        /**
+         * Constructs the reader with the given ByteBuffer.
+         *
+         * @param byteBuffer ByteBuffer to read from
+         */
+        ByteBufferReader(final ByteBuffer byteBuffer) {
+            mByteBuffer = byteBuffer;
+            mByteBuffer.order(ByteOrder.BIG_ENDIAN);
+        }
+
+        @Override
+        public int readUnsignedShort() throws IOException {
+            return toUnsignedShort(mByteBuffer.getShort());
+        }
+
+        @Override
+        public long readUnsignedInt() throws IOException {
+            return toUnsignedInt(mByteBuffer.getInt());
+        }
+
+        @Override
+        public int readTag() throws IOException {
+            return mByteBuffer.getInt();
+        }
+
+        @Override
+        public void skip(final int numOfBytes) throws IOException {
+            mByteBuffer.position(mByteBuffer.position() + numOfBytes);
+        }
+
+        @Override
+        public long getPosition() {
+            return mByteBuffer.position();
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/MetadataRepo.java b/emoji/core/src/android/support/text/emoji/MetadataRepo.java
new file mode 100644
index 0000000..e86277e
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/MetadataRepo.java
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.res.AssetManager;
+import android.graphics.Typeface;
+import android.support.annotation.AnyThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.text.emoji.flatbuffer.MetadataList;
+import android.support.v4.util.Preconditions;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Class to hold the emoji metadata required to process and draw emojis.
+ */
+@AnyThread
+@RequiresApi(19)
+public final class MetadataRepo {
+    /**
+     * The default children size of the root node.
+     */
+    private static final int DEFAULT_ROOT_SIZE = 1024;
+
+    /**
+     * MetadataList that contains the emoji metadata.
+     */
+    private final MetadataList mMetadataList;
+
+    /**
+     * char presentation of all EmojiMetadata's in a single array. All emojis we have are mapped to
+     * Private Use Area A, in the range U+F0000..U+FFFFD. Therefore each emoji takes 2 chars.
+     */
+    private final char[] mEmojiCharArray;
+
+    /**
+     * Empty root node of the trie.
+     */
+    private final Node mRootNode;
+
+    /**
+     * Typeface to be used to render emojis.
+     */
+    private final Typeface mTypeface;
+
+    /**
+     * Constructor used for tests.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    MetadataRepo() {
+        mTypeface = null;
+        mMetadataList = null;
+        mRootNode = new Node(DEFAULT_ROOT_SIZE);
+        mEmojiCharArray = new char[0];
+    }
+
+    /**
+     * Private constructor that is called by one of {@code create} methods.
+     *
+     * @param typeface Typeface to be used to render emojis
+     * @param metadataList MetadataList that contains the emoji metadata
+     */
+    private MetadataRepo(@NonNull final Typeface typeface,
+            @NonNull final MetadataList metadataList) {
+        mTypeface = typeface;
+        mMetadataList = metadataList;
+        mRootNode = new Node(DEFAULT_ROOT_SIZE);
+        mEmojiCharArray = new char[mMetadataList.listLength() * 2];
+        constructIndex(mMetadataList);
+    }
+
+    /**
+     * Construct MetadataRepo from an input stream. The library does not close the given
+     * InputStream, therefore it is caller's responsibility to properly close the stream.
+     *
+     * @param typeface Typeface to be used to render emojis
+     * @param inputStream InputStream to read emoji metadata from
+     */
+    public static MetadataRepo create(@NonNull final Typeface typeface,
+            @NonNull final InputStream inputStream) throws IOException {
+        return new MetadataRepo(typeface, MetadataListReader.read(inputStream));
+    }
+
+    /**
+     * Construct MetadataRepo from a byte buffer. The position of the ByteBuffer will change, it is
+     * caller's responsibility to reposition the buffer if required.
+     *
+     * @param typeface Typeface to be used to render emojis
+     * @param byteBuffer ByteBuffer to read emoji metadata from
+     */
+    public static MetadataRepo create(@NonNull final Typeface typeface,
+            @NonNull final ByteBuffer byteBuffer) throws IOException {
+        return new MetadataRepo(typeface, MetadataListReader.read(byteBuffer));
+    }
+
+    /**
+     * Construct MetadataRepo from an asset.
+     *
+     * @param assetManager AssetManager instance
+     * @param assetPath asset manager path of the file that the Typeface and metadata will be
+     *                  created from
+     */
+    public static MetadataRepo create(@NonNull final AssetManager assetManager,
+            final String assetPath) throws IOException {
+        final Typeface typeface = Typeface.createFromAsset(assetManager, assetPath);
+        return new MetadataRepo(typeface, MetadataListReader.read(assetManager, assetPath));
+    }
+
+    /**
+     * Read emoji metadata list and construct the trie.
+     */
+    private void constructIndex(final MetadataList metadataList) {
+        int length = metadataList.listLength();
+        for (int i = 0; i < length; i++) {
+            final EmojiMetadata metadata = new EmojiMetadata(this, i);
+            //since all emojis are mapped to a single codepoint in Private Use Area A they are 2
+            //chars wide
+            //noinspection ResultOfMethodCallIgnored
+            Character.toChars(metadata.getId(), mEmojiCharArray, i * 2);
+            put(metadata);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    Typeface getTypeface() {
+        return mTypeface;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getMetadataVersion() {
+        return mMetadataList.version();
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    Node getRootNode() {
+        return mRootNode;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public char[] getEmojiCharArray() {
+        return mEmojiCharArray;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public MetadataList getMetadataList() {
+        return mMetadataList;
+    }
+
+    /**
+     * Add an EmojiMetadata to the index.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @VisibleForTesting
+    void put(@NonNull final EmojiMetadata data) {
+        Preconditions.checkNotNull(data, "emoji metadata cannot be null");
+        Preconditions.checkArgument(data.getCodepointsLength() > 0,
+                "invalid metadata codepoint length");
+
+        mRootNode.put(data, 0, data.getCodepointsLength() - 1);
+    }
+
+    /**
+     * Trie node that holds mapping from emoji codepoint(s) to EmojiMetadata. A single codepoint
+     * emoji is represented by a child of the root node.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    static class Node {
+        private final SparseArray<Node> mChildren;
+        private EmojiMetadata mData;
+
+        private Node() {
+            this(1);
+        }
+
+        private Node(final int defaultChildrenSize) {
+            mChildren = new SparseArray<>(defaultChildrenSize);
+        }
+
+        Node get(final int key) {
+            return mChildren == null ? null : mChildren.get(key);
+        }
+
+        final EmojiMetadata getData() {
+            return mData;
+        }
+
+        private void put(@NonNull final EmojiMetadata data, final int start, final int end) {
+            Node node = get(data.getCodepointAt(start));
+            if (node == null) {
+                node = new Node();
+                mChildren.put(data.getCodepointAt(start), node);
+            }
+
+            if (end > start) {
+                node.put(data, start + 1, end);
+            } else {
+                node.mData = data;
+            }
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/TypefaceEmojiSpan.java b/emoji/core/src/android/support/text/emoji/TypefaceEmojiSpan.java
new file mode 100644
index 0000000..f64330d
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/TypefaceEmojiSpan.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.text.TextPaint;
+
+/**
+ * EmojiSpan subclass used to render emojis using Typeface.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+public final class TypefaceEmojiSpan extends EmojiSpan {
+
+    /**
+     * Paint object used to draw a background in debug mode.
+     */
+    private static Paint sDebugPaint;
+
+    /**
+     * Default constructor.
+     *
+     * @param metadata metadata representing the emoji that this span will draw
+     */
+    public TypefaceEmojiSpan(final EmojiMetadata metadata) {
+        super(metadata);
+    }
+
+    @Override
+    public void draw(@NonNull final Canvas canvas, final CharSequence text,
+            @IntRange(from = 0) final int start, @IntRange(from = 0) final int end, final float x,
+            final int top, final int y, final int bottom, @NonNull final Paint paint) {
+        if (EmojiCompat.get().isEmojiSpanIndicatorEnabled()) {
+            canvas.drawRect(x, top , x + getWidth(), bottom, getDebugPaint());
+        }
+        getMetadata().draw(canvas, x, y, paint);
+    }
+
+    private static Paint getDebugPaint() {
+        if (sDebugPaint == null) {
+            sDebugPaint = new TextPaint();
+            sDebugPaint.setColor(EmojiCompat.get().getEmojiSpanIndicatorColor());
+            sDebugPaint.setStyle(Paint.Style.FILL);
+        }
+        return sDebugPaint;
+    }
+
+
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java b/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
new file mode 100644
index 0000000..381a02c
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.R;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Helper class to parse EmojiCompat EditText attributes.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class EditTextAttributeHelper {
+    static final int MAX_EMOJI_COUNT = Integer.MAX_VALUE;
+    private int mMaxEmojiCount;
+
+    public EditTextAttributeHelper(@NonNull View view, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        if (attrs != null) {
+            final Context context = view.getContext();
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EmojiEditText,
+                    defStyleAttr, defStyleRes);
+            mMaxEmojiCount = a.getInteger(R.styleable.EmojiEditText_maxEmojiCount, MAX_EMOJI_COUNT);
+            a.recycle();
+        }
+    }
+
+    public int getMaxEmojiCount() {
+        return mMaxEmojiCount;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiButton.java b/emoji/core/src/android/support/text/emoji/widget/EmojiButton.java
new file mode 100644
index 0000000..65afd9c
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiButton.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.text.InputFilter;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+/**
+ * Button widget enhanced with emoji capability by using {@link EmojiTextViewHelper}. When used
+ * on devices running API 18 or below, this widget acts as a regular {@link Button}.
+ */
+public class EmojiButton extends Button {
+    private EmojiTextViewHelper mEmojiTextViewHelper;
+
+    /**
+     * Prevent calling {@link #init()} multiple times in case super() constructors
+     * call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiButton(Context context) {
+        super(context);
+        init();
+    }
+
+    public EmojiButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public EmojiButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public EmojiButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    private void init() {
+        if (!mInitialized) {
+            mInitialized = true;
+            getEmojiTextViewHelper().updateTransformationMethod();
+        }
+    }
+
+    @Override
+    public void setFilters(InputFilter[] filters) {
+        super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+    }
+
+    @Override
+    public void setAllCaps(boolean allCaps) {
+        super.setAllCaps(allCaps);
+        getEmojiTextViewHelper().setAllCaps(allCaps);
+    }
+
+    private EmojiTextViewHelper getEmojiTextViewHelper() {
+        if (mEmojiTextViewHelper == null) {
+            mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+        }
+        return mEmojiTextViewHelper;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java b/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java
new file mode 100644
index 0000000..a0e8a69
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.IntRange;
+import android.support.annotation.Nullable;
+import android.support.text.emoji.EmojiCompat;
+import android.util.AttributeSet;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+
+/**
+ * EditText widget enhanced with emoji capability by using {@link EmojiEditTextHelper}. When used
+ * on devices running API 18 or below, this widget acts as a regular {@link EditText}.
+ *
+ * @attr ref android.support.text.emoji.R.styleable#EmojiEditText_maxEmojiCount
+ */
+public class EmojiEditText extends EditText {
+    private EmojiEditTextHelper mEmojiEditTextHelper;
+
+    /**
+     * Prevent calling {@link #init(AttributeSet, int, int)} multiple times in case super()
+     * constructors call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiEditText(Context context) {
+        super(context);
+        init(null /*attrs*/, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+    }
+
+    public EmojiEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(attrs, android.R.attr.editTextStyle, 0 /*defStyleRes*/);
+    }
+
+    public EmojiEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(attrs, defStyleAttr, 0 /*defStyleRes*/);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public EmojiEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(attrs, defStyleAttr, defStyleRes);
+    }
+
+    private void init(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        if (!mInitialized) {
+            mInitialized = true;
+            final EditTextAttributeHelper attrHelper = new EditTextAttributeHelper(this, attrs,
+                    defStyleAttr, defStyleRes);
+            setMaxEmojiCount(attrHelper.getMaxEmojiCount());
+            setKeyListener(super.getKeyListener());
+        }
+    }
+
+    @Override
+    public void setKeyListener(android.text.method.KeyListener keyListener) {
+        super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+        return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
+    }
+
+    /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     *
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     *
+     * @attr ref android.support.text.emoji.R.styleable#EmojiEditText_maxEmojiCount
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        getEmojiEditTextHelper().setMaxEmojiCount(maxEmojiCount);
+    }
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     *
+     * @attr ref android.support.text.emoji.R.styleable#EmojiEditText_maxEmojiCount
+     */
+    public int getMaxEmojiCount() {
+        return getEmojiEditTextHelper().getMaxEmojiCount();
+    }
+
+    private EmojiEditTextHelper getEmojiEditTextHelper() {
+        if (mEmojiEditTextHelper == null) {
+            mEmojiEditTextHelper = new EmojiEditTextHelper(this);
+        }
+        return mEmojiEditTextHelper;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java b/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java
new file mode 100644
index 0000000..edc511f
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.os.Build;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.EmojiSpan;
+import android.support.v4.util.Preconditions;
+import android.text.method.KeyListener;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/**
+ * Utility class to enhance custom EditText widgets with {@link EmojiCompat}.
+ * <p/>
+ * <pre>
+ * public class MyEmojiEditText extends EditText {
+ *      public MyEmojiEditText(Context context) {
+ *          super(context);
+ *          init();
+ *      }
+ *      // ...
+ *      private void init() {
+ *          super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener()));
+ *      }
+ *
+ *      {@literal @}Override
+ *      public void setKeyListener(android.text.method.KeyListener keyListener) {
+ *          super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
+ *      }
+ *
+ *      {@literal @}Override
+ *      public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ *          InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+ *          return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
+ *      }
+ *
+ *      private EmojiEditTextHelper getEmojiEditTextHelper() {
+ *          if (mEmojiEditTextHelper == null) {
+ *              mEmojiEditTextHelper = new EmojiEditTextHelper(this);
+ *          }
+ *          return mEmojiEditTextHelper;
+ *      }
+ * }
+ * </pre>
+ *
+ */
+public final class EmojiEditTextHelper {
+    private final HelperInternal mHelper;
+    private int mMaxEmojiCount = EditTextAttributeHelper.MAX_EMOJI_COUNT;
+    @EmojiCompat.ReplaceStrategy
+    private int mEmojiReplaceStrategy = EmojiCompat.REPLACE_STRATEGY_DEFAULT;
+
+    /**
+     * Default constructor.
+     *
+     * @param editText EditText instance
+     */
+    public EmojiEditTextHelper(@NonNull final EditText editText) {
+        Preconditions.checkNotNull(editText, "editText cannot be null");
+        mHelper = Build.VERSION.SDK_INT >= 19 ? new HelperInternal19(editText)
+                : new HelperInternal();
+    }
+
+    /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     * <p/>
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     *
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        Preconditions.checkArgumentNonnegative(maxEmojiCount,
+                "maxEmojiCount should be greater than 0");
+        mMaxEmojiCount = maxEmojiCount;
+        mHelper.setMaxEmojiCount(maxEmojiCount);
+    }
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public int getMaxEmojiCount() {
+        return mMaxEmojiCount;
+    }
+
+    /**
+     * Attaches EmojiCompat KeyListener to the widget. Should be called from {@link
+     * TextView#setKeyListener(KeyListener)}. Existing keyListener is wrapped into EmojiCompat
+     * KeyListener. When used on devices running API 18 or below, this method returns
+     * {@code keyListener} that is given as a parameter.
+     *
+     * @param keyListener KeyListener passed into {@link TextView#setKeyListener(KeyListener)}
+     *
+     * @return a new KeyListener instance that wraps {@code keyListener}.
+     */
+    @NonNull
+    public KeyListener getKeyListener(@NonNull final KeyListener keyListener) {
+        Preconditions.checkNotNull(keyListener, "keyListener cannot be null");
+        return mHelper.getKeyListener(keyListener);
+    }
+
+    /**
+     * Updates the InputConnection with emoji support. Should be called from {@link
+     * TextView#onCreateInputConnection(EditorInfo)}. When used on devices running API 18 or below,
+     * this method returns {@code inputConnection} that is given as a parameter.
+     *
+     * @param inputConnection InputConnection instance created by TextView
+     * @param outAttrs        EditorInfo passed into
+     *                        {@link TextView#onCreateInputConnection(EditorInfo)}
+     *
+     * @return a new InputConnection instance that wraps {@code inputConnection}
+     */
+    @NonNull
+    public InputConnection onCreateInputConnection(@NonNull final InputConnection inputConnection,
+            @NonNull final EditorInfo outAttrs) {
+        Preconditions.checkNotNull(inputConnection, "inputConnection cannot be null");
+        return mHelper.onCreateInputConnection(inputConnection, outAttrs);
+    }
+
+    /**
+     * Sets whether to replace all emoji with {@link EmojiSpan}s. Default value is
+     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
+     *
+     * @param replaceStrategy should be one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
+        mEmojiReplaceStrategy = replaceStrategy;
+        mHelper.setEmojiReplaceStrategy(replaceStrategy);
+    }
+
+    /**
+     * Returns whether to replace all emoji with {@link EmojiSpan}s. Default value is
+     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
+     *
+     * @return one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getEmojiReplaceStrategy() {
+        return mEmojiReplaceStrategy;
+    }
+
+    private static class HelperInternal {
+
+        KeyListener getKeyListener(@NonNull KeyListener keyListener) {
+            return keyListener;
+        }
+
+        InputConnection onCreateInputConnection(@NonNull InputConnection inputConnection,
+                @NonNull EditorInfo outAttrs) {
+            return inputConnection;
+        }
+
+        void setMaxEmojiCount(int maxEmojiCount) {
+            // do nothing
+        }
+
+        void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
+            // do nothing
+        }
+    }
+
+    @RequiresApi(19)
+    private static class HelperInternal19 extends HelperInternal {
+        private final EditText mEditText;
+        private final EmojiTextWatcher mTextWatcher;
+
+        HelperInternal19(@NonNull EditText editText) {
+            mEditText = editText;
+            mTextWatcher = new EmojiTextWatcher(mEditText);
+            mEditText.addTextChangedListener(mTextWatcher);
+            mEditText.setEditableFactory(EmojiEditableFactory.getInstance());
+        }
+
+        @Override
+        void setMaxEmojiCount(int maxEmojiCount) {
+            mTextWatcher.setMaxEmojiCount(maxEmojiCount);
+        }
+
+        @Override
+        void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
+            mTextWatcher.setEmojiReplaceStrategy(replaceStrategy);
+        }
+
+        @Override
+        KeyListener getKeyListener(@NonNull final KeyListener keyListener) {
+            if (keyListener instanceof EmojiKeyListener) {
+                return keyListener;
+            }
+            return new EmojiKeyListener(keyListener);
+        }
+
+        @Override
+        InputConnection onCreateInputConnection(@NonNull final InputConnection inputConnection,
+                @NonNull final EditorInfo outAttrs) {
+            if (inputConnection instanceof EmojiInputConnection) {
+                return inputConnection;
+            }
+            return new EmojiInputConnection(mEditText, inputConnection, outAttrs);
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiEditableFactory.java b/emoji/core/src/android/support/text/emoji/widget/EmojiEditableFactory.java
new file mode 100644
index 0000000..9793c9d
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiEditableFactory.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.Editable;
+
+/**
+ * EditableFactory used to improve editing operations on an EditText.
+ * <p>
+ * EditText uses DynamicLayout, which attaches to the Spannable instance that is being edited using
+ * ChangeWatcher. ChangeWatcher implements SpanWatcher and Textwatcher. Currently every delete/add
+ * operation is reported to DynamicLayout, for every span that has changed. For each change,
+ * DynamicLayout performs some expensive computations. i.e. if there is 100 EmojiSpans and the first
+ * span is deleted, DynamicLayout gets 99 calls about the change of position occurred in the
+ * remaining spans. This causes a huge delay in response time.
+ * <p>
+ * Since "android.text.DynamicLayout$ChangeWatcher" class is not a public class,
+ * EmojiEditableFactory checks if the watcher is in the classpath, and if so uses the modified
+ * Spannable which reduces the total number of calls to DynamicLayout for operations that affect
+ * EmojiSpans.
+ *
+ * @see SpannableBuilder
+ */
+final class EmojiEditableFactory extends Editable.Factory {
+    private static final Object sInstanceLock = new Object();
+    @GuardedBy("sInstanceLock")
+    private static volatile Editable.Factory sInstance;
+
+    @Nullable private static Class<?> sWatcherClass;
+
+    @SuppressLint("PrivateApi")
+    private EmojiEditableFactory() {
+        try {
+            String className = "android.text.DynamicLayout$ChangeWatcher";
+            sWatcherClass = getClass().getClassLoader().loadClass(className);
+        } catch (Throwable t) {
+            // ignore
+        }
+    }
+
+    public static Editable.Factory getInstance() {
+        if (sInstance == null) {
+            synchronized (sInstanceLock) {
+                if (sInstance == null) {
+                    sInstance = new EmojiEditableFactory();
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    @Override
+    public Editable newEditable(@NonNull final CharSequence source) {
+        if (sWatcherClass != null) {
+            return SpannableBuilder.create(sWatcherClass, source);
+        }
+        return super.newEditable(source);
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiExtractEditText.java b/emoji/core/src/android/support/text/emoji/widget/EmojiExtractEditText.java
new file mode 100644
index 0000000..ca1868e
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiExtractEditText.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.inputmethodservice.ExtractEditText;
+import android.os.Build;
+import android.support.annotation.IntRange;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.EmojiSpan;
+import android.util.AttributeSet;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+/**
+ * ExtractEditText widget enhanced with emoji capability by using {@link EmojiEditTextHelper}.
+ * When used on devices running API 18 or below, this widget acts as a {@link ExtractEditText} and
+ * does not provide any emoji compatibility feature.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class EmojiExtractEditText extends ExtractEditText {
+    private EmojiEditTextHelper mEmojiEditTextHelper;
+
+    /**
+     * Prevent calling {@link #init(AttributeSet, int)} multiple times in case super() constructors
+     * call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiExtractEditText(Context context) {
+        super(context);
+        init(null /*attrs*/, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+    }
+
+    public EmojiExtractEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(attrs, android.R.attr.editTextStyle, 0 /*defStyleRes*/);
+    }
+
+    public EmojiExtractEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(attrs, defStyleAttr, 0 /*defStyleRes*/);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public EmojiExtractEditText(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(attrs, defStyleAttr, defStyleRes);
+    }
+
+    private void init(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        if (!mInitialized) {
+            mInitialized = true;
+            final EditTextAttributeHelper attrHelper = new EditTextAttributeHelper(this, attrs,
+                    defStyleAttr, defStyleRes);
+            setMaxEmojiCount(attrHelper.getMaxEmojiCount());
+            setKeyListener(super.getKeyListener());
+        }
+    }
+
+    @Override
+    public void setKeyListener(android.text.method.KeyListener keyListener) {
+        super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+        return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
+    }
+
+    /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        getEmojiEditTextHelper().setMaxEmojiCount(maxEmojiCount);
+    }
+
+    /**
+     * Sets whether to replace all emoji with {@link EmojiSpan}s. Default value is
+     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
+     *
+     * @param replaceStrategy should be one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
+     */
+    public void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
+        getEmojiEditTextHelper().setEmojiReplaceStrategy(replaceStrategy);
+    }
+
+    /**
+     * Returns whether to replace all emoji with {@link EmojiSpan}s. Default value is
+     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
+     *
+     * @return one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
+     */
+    public int getEmojiReplaceStrategy() {
+        return getEmojiEditTextHelper().getEmojiReplaceStrategy();
+    }
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public int getMaxEmojiCount() {
+        return getEmojiEditTextHelper().getMaxEmojiCount();
+    }
+
+    private EmojiEditTextHelper getEmojiEditTextHelper() {
+        if (mEmojiEditTextHelper == null) {
+            mEmojiEditTextHelper = new EmojiEditTextHelper(this);
+        }
+        return mEmojiEditTextHelper;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiExtractTextLayout.java b/emoji/core/src/android/support/text/emoji/widget/EmojiExtractTextLayout.java
new file mode 100644
index 0000000..c75d6d0
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiExtractTextLayout.java
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.inputmethodservice.InputMethodService;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.EmojiSpan;
+import android.support.text.emoji.R;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.LinearLayout;
+
+/**
+ * Layout that contains emoji compatibility enhanced ExtractEditText. Should be used by
+ * {@link InputMethodService} implementations.
+ * <p/>
+ * Call {@link #onUpdateExtractingViews(InputMethodService, EditorInfo)} from
+ * {@link InputMethodService#onUpdateExtractingViews(EditorInfo)
+ * InputMethodService#onUpdateExtractingViews(EditorInfo)}.
+ * <pre>
+ * public class MyInputMethodService extends InputMethodService {
+ *     // ..
+ *     {@literal @}Override
+ *     public View onCreateExtractTextView() {
+ *         mExtractView = getLayoutInflater().inflate(R.layout.emoji_input_method_extract_layout,
+ *                 null);
+ *         return mExtractView;
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void onUpdateExtractingViews(EditorInfo ei) {
+ *         mExtractView.onUpdateExtractingViews(this, ei);
+ *     }
+ * }
+ * </pre>
+ *
+ * @attr ref android.support.text.emoji.R.styleable#EmojiExtractTextLayout_emojiReplaceStrategy
+ */
+public class EmojiExtractTextLayout extends LinearLayout {
+
+    private ExtractButtonCompat mExtractAction;
+    private EmojiExtractEditText mExtractEditText;
+    private ViewGroup mExtractAccessories;
+    private View.OnClickListener mButtonOnClickListener;
+
+    /**
+     * Prevent calling {@link #init(Context, AttributeSet, int)}} multiple times in case super()
+     * constructors call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiExtractTextLayout(Context context) {
+        super(context);
+        init(context, null /*attrs*/, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+    }
+
+    public EmojiExtractTextLayout(Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+    }
+
+    public EmojiExtractTextLayout(Context context,
+            @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr, 0 /*defStyleRes*/);
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public EmojiExtractTextLayout(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        if (!mInitialized) {
+            mInitialized = true;
+            setOrientation(HORIZONTAL);
+            final View view = LayoutInflater.from(context)
+                    .inflate(R.layout.input_method_extract_view, this /*root*/,
+                            true /*attachToRoot*/);
+            mExtractAccessories = view.findViewById(R.id.inputExtractAccessories);
+            mExtractAction = view.findViewById(R.id.inputExtractAction);
+            mExtractEditText = view.findViewById(android.R.id.inputExtractEditText);
+
+            if (attrs != null) {
+                final TypedArray a = context.obtainStyledAttributes(attrs,
+                        R.styleable.EmojiExtractTextLayout, defStyleAttr, defStyleRes);
+                final int replaceStrategy = a.getInteger(
+                        R.styleable.EmojiExtractTextLayout_emojiReplaceStrategy,
+                        EmojiCompat.REPLACE_STRATEGY_DEFAULT);
+                mExtractEditText.setEmojiReplaceStrategy(replaceStrategy);
+                a.recycle();
+            }
+        }
+    }
+
+    /**
+     * Sets whether to replace all emoji with {@link EmojiSpan}s. Default value is
+     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
+     *
+     * @param replaceStrategy should be one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
+     *
+     * @attr ref android.support.text.emoji.R.styleable#EmojiExtractTextLayout_emojiReplaceStrategy
+     */
+    public void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
+        mExtractEditText.setEmojiReplaceStrategy(replaceStrategy);
+    }
+
+    /**
+     * Returns whether to replace all emoji with {@link EmojiSpan}s. Default value is
+     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
+     *
+     * @return one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
+     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
+     *
+     * @attr ref android.support.text.emoji.R.styleable#EmojiExtractTextLayout_emojiReplaceStrategy
+     */
+    public int getEmojiReplaceStrategy() {
+        return mExtractEditText.getEmojiReplaceStrategy();
+    }
+
+    /**
+     * Initializes the layout. Call this function from
+     * {@link InputMethodService#onUpdateExtractingViews(EditorInfo)
+     * InputMethodService#onUpdateExtractingViews(EditorInfo)}.
+     */
+    public void onUpdateExtractingViews(InputMethodService inputMethodService, EditorInfo ei) {
+        // the following code is ported as it is from InputMethodService.onUpdateExtractingViews
+        if (!inputMethodService.isExtractViewShown()) {
+            return;
+        }
+
+        if (mExtractAccessories == null) {
+            return;
+        }
+
+        final boolean hasAction = ei.actionLabel != null
+                || ((ei.imeOptions & EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE
+                && (ei.imeOptions & EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0
+                && ei.inputType != InputType.TYPE_NULL);
+
+        if (hasAction) {
+            mExtractAccessories.setVisibility(View.VISIBLE);
+            if (mExtractAction != null) {
+                if (ei.actionLabel != null) {
+                    mExtractAction.setText(ei.actionLabel);
+                } else {
+                    mExtractAction.setText(inputMethodService.getTextForImeAction(ei.imeOptions));
+                }
+                mExtractAction.setOnClickListener(getButtonClickListener(inputMethodService));
+            }
+        } else {
+            mExtractAccessories.setVisibility(View.GONE);
+            if (mExtractAction != null) {
+                mExtractAction.setOnClickListener(null);
+            }
+        }
+    }
+
+    private View.OnClickListener getButtonClickListener(
+            final InputMethodService inputMethodService) {
+        if (mButtonOnClickListener == null) {
+            mButtonOnClickListener = new ButtonOnclickListener(inputMethodService);
+        }
+        return mButtonOnClickListener;
+    }
+
+    private static final class ButtonOnclickListener implements View.OnClickListener {
+        private final InputMethodService mInputMethodService;
+
+        ButtonOnclickListener(InputMethodService inputMethodService) {
+            mInputMethodService = inputMethodService;
+        }
+
+        /**
+         * The following code is ported as it is from InputMethodService.mActionClickListener.
+         */
+        @Override
+        public void onClick(View v) {
+            final EditorInfo ei = mInputMethodService.getCurrentInputEditorInfo();
+            final InputConnection ic = mInputMethodService.getCurrentInputConnection();
+            if (ei != null && ic != null) {
+                if (ei.actionId != 0) {
+                    ic.performEditorAction(ei.actionId);
+                } else if ((ei.imeOptions & EditorInfo.IME_MASK_ACTION)
+                        != EditorInfo.IME_ACTION_NONE) {
+                    ic.performEditorAction(ei.imeOptions & EditorInfo.IME_MASK_ACTION);
+                }
+            }
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java b/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java
new file mode 100644
index 0000000..6232c52
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.text.Editable;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.widget.TextView;
+
+/**
+ * InputConnectionWrapper for EditText delete operations. Keyboard does not have knowledge about
+ * emojis and therefore might send commands to delete a part of the emoji sequence which creates
+ * invalid codeunits/getCodepointAt in the text.
+ * <p/>
+ * This class tries to correctly delete an emoji checking if there is an emoji span.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+final class EmojiInputConnection extends InputConnectionWrapper {
+    private final TextView mTextView;
+
+    EmojiInputConnection(
+            @NonNull final TextView textView,
+            @NonNull final InputConnection inputConnection,
+            @NonNull final EditorInfo outAttrs) {
+        super(inputConnection, false);
+        mTextView = textView;
+        EmojiCompat.get().updateEditorInfoAttrs(outAttrs);
+    }
+
+    @Override
+    public boolean deleteSurroundingText(final int beforeLength, final int afterLength) {
+        final boolean result = EmojiCompat.handleDeleteSurroundingText(this, getEditable(),
+                beforeLength, afterLength, false /*inCodePoints*/);
+        return result || super.deleteSurroundingText(beforeLength, afterLength);
+    }
+
+    @Override
+    public boolean deleteSurroundingTextInCodePoints(final int beforeLength,
+            final int afterLength) {
+        final boolean result = EmojiCompat.handleDeleteSurroundingText(this, getEditable(),
+                beforeLength, afterLength, true /*inCodePoints*/);
+        return result || super.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+    }
+
+    private Editable getEditable() {
+        return mTextView.getEditableText();
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java b/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java
new file mode 100644
index 0000000..ba4201e
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.EmojiCompat.InitCallback;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.widget.TextView;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+/**
+ * InputFilter to add EmojiSpans to the CharSequence set in a TextView. Unlike EditText where a
+ * TextWatcher is used to enhance the CharSequence, InputFilter is used on TextView. The reason is
+ * that if you add a TextWatcher to a TextView, its internal layout mechanism change, and therefore
+ * depending on the CharSequence provided, adding a TextWatcher might have performance side
+ * effects.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+final class EmojiInputFilter implements android.text.InputFilter {
+    private final TextView mTextView;
+    private InitCallback mInitCallback;
+
+    EmojiInputFilter(@NonNull final TextView textView) {
+        mTextView = textView;
+    }
+
+    @Override
+    public CharSequence filter(final CharSequence source, final int sourceStart,
+            final int sourceEnd, final Spanned dest, final int destStart, final int destEnd) {
+        if (mTextView.isInEditMode()) {
+            return source;
+        }
+
+        switch (EmojiCompat.get().getLoadState()){
+            case EmojiCompat.LOAD_STATE_SUCCEEDED:
+                boolean process = true;
+                if (destEnd == 0 && destStart == 0 && dest.length() == 0) {
+                    final CharSequence oldText = mTextView.getText();
+                    if (source == oldText) {
+                        process = false;
+                    }
+                }
+
+                if (process && source != null) {
+                    final CharSequence text;
+                    if (sourceStart == 0 && sourceEnd == source.length()) {
+                        text = source;
+                    } else {
+                        text = source.subSequence(sourceStart, sourceEnd);
+                    }
+                    return EmojiCompat.get().process(text, 0, text.length());
+                }
+
+                return source;
+            case EmojiCompat.LOAD_STATE_LOADING:
+                EmojiCompat.get().registerInitCallback(getInitCallback());
+                return source;
+
+            case EmojiCompat.LOAD_STATE_FAILED:
+            default:
+                return source;
+        }
+    }
+
+    private InitCallback getInitCallback() {
+        if (mInitCallback == null) {
+            mInitCallback = new InitCallbackImpl(mTextView);
+        }
+        return mInitCallback;
+    }
+
+    private static class InitCallbackImpl extends InitCallback {
+        private final Reference<TextView> mViewRef;
+
+        InitCallbackImpl(TextView textView) {
+            mViewRef = new WeakReference<>(textView);
+        }
+
+        @Override
+        public void onInitialized() {
+            super.onInitialized();
+            final TextView textView = mViewRef.get();
+            if (textView != null && textView.isAttachedToWindow()) {
+                final CharSequence result = EmojiCompat.get().process(textView.getText());
+
+                final int selectionStart = Selection.getSelectionStart(result);
+                final int selectionEnd = Selection.getSelectionEnd(result);
+
+                textView.setText(result);
+
+                if (result instanceof Spannable) {
+                    updateSelection((Spannable) result, selectionStart, selectionEnd);
+                }
+            }
+        }
+    }
+
+    static void updateSelection(Spannable spannable, final int start, final int end) {
+        if (start >= 0 && end >= 0) {
+            Selection.setSelection(spannable, start, end);
+        } else if (start >= 0) {
+            Selection.setSelection(spannable, start);
+        } else if (end >= 0) {
+            Selection.setSelection(spannable, end);
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiKeyListener.java b/emoji/core/src/android/support/text/emoji/widget/EmojiKeyListener.java
new file mode 100644
index 0000000..6d94f14
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiKeyListener.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.text.Editable;
+import android.view.KeyEvent;
+import android.view.View;
+
+/**
+ * KeyListener class to handle delete operations correctly.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+final class EmojiKeyListener implements android.text.method.KeyListener {
+    private final android.text.method.KeyListener mKeyListener;
+
+    EmojiKeyListener(android.text.method.KeyListener keyListener) {
+        mKeyListener = keyListener;
+    }
+
+    @Override
+    public int getInputType() {
+        return mKeyListener.getInputType();
+    }
+
+    @Override
+    public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
+        final boolean result = EmojiCompat.handleOnKeyDown(content, keyCode, event);
+        return result || mKeyListener.onKeyDown(view, content, keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(View view, Editable text, int keyCode, KeyEvent event) {
+        return mKeyListener.onKeyUp(view, text, keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyOther(View view, Editable text, KeyEvent event) {
+        return mKeyListener.onKeyOther(view, text, event);
+    }
+
+    @Override
+    public void clearMetaKeyState(View view, Editable content, int states) {
+        mKeyListener.clearMetaKeyState(view, content, states);
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTextView.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTextView.java
new file mode 100644
index 0000000..3e450dc
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTextView.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.text.InputFilter;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * TextView widget enhanced with emoji capability by using {@link EmojiTextViewHelper}. When used
+ * on devices running API 18 or below, this widget acts as a regular {@link TextView}.
+ */
+public class EmojiTextView extends TextView {
+    private EmojiTextViewHelper mEmojiTextViewHelper;
+
+    /**
+     * Prevent calling {@link #init()} multiple times in case super() constructors
+     * call other constructors.
+     */
+    private boolean mInitialized;
+
+    public EmojiTextView(Context context) {
+        super(context);
+        init();
+    }
+
+    public EmojiTextView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    private void init() {
+        if (!mInitialized) {
+            mInitialized = true;
+            getEmojiTextViewHelper().updateTransformationMethod();
+        }
+    }
+
+    @Override
+    public void setFilters(InputFilter[] filters) {
+        super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+    }
+
+    @Override
+    public void setAllCaps(boolean allCaps) {
+        super.setAllCaps(allCaps);
+        getEmojiTextViewHelper().setAllCaps(allCaps);
+    }
+
+    private EmojiTextViewHelper getEmojiTextViewHelper() {
+        if (mEmojiTextViewHelper == null) {
+            mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+        }
+        return mEmojiTextViewHelper;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTextViewHelper.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTextViewHelper.java
new file mode 100644
index 0000000..abd7410
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTextViewHelper.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.text.emoji.EmojiCompat;
+import android.support.v4.util.Preconditions;
+import android.text.InputFilter;
+import android.text.method.PasswordTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.widget.TextView;
+
+/**
+ * Utility class to enhance custom TextView widgets with {@link EmojiCompat}.
+ * <pre>
+ * public class MyEmojiTextView extends TextView {
+ *     public MyEmojiTextView(Context context) {
+ *         super(context);
+ *         init();
+ *     }
+ *     // ..
+ *     private void init() {
+ *         getEmojiTextViewHelper().updateTransformationMethod();
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void setFilters(InputFilter[] filters) {
+ *         super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void setAllCaps(boolean allCaps) {
+ *         super.setAllCaps(allCaps);
+ *         getEmojiTextViewHelper().setAllCaps(allCaps);
+ *     }
+ *
+ *     private EmojiTextViewHelper getEmojiTextViewHelper() {
+ *         if (mEmojiTextViewHelper == null) {
+ *             mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+ *         }
+ *         return mEmojiTextViewHelper;
+ *     }
+ * }
+ * </pre>
+ */
+public final class EmojiTextViewHelper {
+
+    private final HelperInternal mHelper;
+
+    /**
+     * Default constructor.
+     *
+     * @param textView TextView instance
+     */
+    public EmojiTextViewHelper(@NonNull TextView textView) {
+        Preconditions.checkNotNull(textView, "textView cannot be null");
+        mHelper = Build.VERSION.SDK_INT >= 19 ? new HelperInternal19(textView)
+                : new HelperInternal();
+    }
+
+    /**
+     * Updates widget's TransformationMethod so that the transformed text can be processed.
+     * Should be called in the widget constructor. When used on devices running API 18 or below,
+     * this method does nothing.
+     *
+     * @see #wrapTransformationMethod(TransformationMethod)
+     */
+    public void updateTransformationMethod() {
+        mHelper.updateTransformationMethod();
+    }
+
+    /**
+     * Appends EmojiCompat InputFilters to the widget InputFilters. Should be called by {@link
+     * TextView#setFilters(InputFilter[])} to update the InputFilters. When used on devices running
+     * API 18 or below, this method returns {@code filters} that is given as a parameter.
+     *
+     * @param filters InputFilter array passed to {@link TextView#setFilters(InputFilter[])}
+     *
+     * @return same copy if the array already contains EmojiCompat InputFilter. A new array copy if
+     * not.
+     */
+    @NonNull
+    public InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
+        return mHelper.getFilters(filters);
+    }
+
+    /**
+     * Returns transformation method that can update the transformed text to display emojis. When
+     * used on devices running API 18 or below, this method returns {@code transformationMethod}
+     * that is given as a parameter.
+     *
+     * @param transformationMethod instance to be wrapped
+     */
+    @Nullable
+    public TransformationMethod wrapTransformationMethod(
+            @Nullable TransformationMethod transformationMethod) {
+        return mHelper.wrapTransformationMethod(transformationMethod);
+    }
+
+    /**
+     * Call when allCaps is set on TextView. When used on devices running API 18 or below, this
+     * method does nothing.
+     *
+     * @param allCaps allCaps parameter passed to {@link TextView#setAllCaps(boolean)}
+     */
+    public void setAllCaps(boolean allCaps) {
+        mHelper.setAllCaps(allCaps);
+    }
+
+    private static class HelperInternal {
+
+        void updateTransformationMethod() {
+            // do nothing
+        }
+
+        InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
+            return filters;
+        }
+
+        TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) {
+            return transformationMethod;
+        }
+
+        void setAllCaps(boolean allCaps) {
+            // do nothing
+        }
+    }
+
+    @RequiresApi(19)
+    private static class HelperInternal19 extends HelperInternal {
+        private final TextView mTextView;
+        private final EmojiInputFilter mEmojiInputFilter;
+
+        HelperInternal19(TextView textView) {
+            mTextView = textView;
+            mEmojiInputFilter = new EmojiInputFilter(textView);
+        }
+
+        @Override
+        void updateTransformationMethod() {
+            final TransformationMethod tm = mTextView.getTransformationMethod();
+            if (tm != null && !(tm instanceof PasswordTransformationMethod)) {
+                mTextView.setTransformationMethod(wrapTransformationMethod(tm));
+            }
+        }
+
+        @Override
+        InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
+            final int count = filters.length;
+            for (int i = 0; i < count; i++) {
+                if (filters[i] instanceof EmojiInputFilter) {
+                    return filters;
+                }
+            }
+            final InputFilter[] newFilters = new InputFilter[filters.length + 1];
+            System.arraycopy(filters, 0, newFilters, 0, count);
+            newFilters[count] = mEmojiInputFilter;
+            return newFilters;
+        }
+
+        @Override
+        TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) {
+            if (transformationMethod instanceof EmojiTransformationMethod) {
+                return transformationMethod;
+            }
+            return new EmojiTransformationMethod(transformationMethod);
+        }
+
+        @Override
+        void setAllCaps(boolean allCaps) {
+            // When allCaps is set to false TextView sets the transformation method to be null. We
+            // are only interested when allCaps is set to true in order to wrap the original method.
+            if (allCaps) {
+                updateTransformationMethod();
+            }
+        }
+
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
new file mode 100644
index 0000000..de3aea1
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.EmojiCompat.InitCallback;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.widget.EditText;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+/**
+ * TextWatcher used for an EditText.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+final class EmojiTextWatcher implements android.text.TextWatcher {
+    private final EditText mEditText;
+    private InitCallback mInitCallback;
+    private int mMaxEmojiCount = EditTextAttributeHelper.MAX_EMOJI_COUNT;
+    @EmojiCompat.ReplaceStrategy
+    private int mEmojiReplaceStrategy = EmojiCompat.REPLACE_STRATEGY_DEFAULT;
+
+    EmojiTextWatcher(EditText editText) {
+        mEditText = editText;
+    }
+
+    void setMaxEmojiCount(int maxEmojiCount) {
+        this.mMaxEmojiCount = maxEmojiCount;
+    }
+
+    int getMaxEmojiCount() {
+        return mMaxEmojiCount;
+    }
+
+    @EmojiCompat.ReplaceStrategy int getEmojiReplaceStrategy() {
+        return mEmojiReplaceStrategy;
+    }
+
+    void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
+        mEmojiReplaceStrategy = replaceStrategy;
+    }
+
+    @Override
+    public void onTextChanged(CharSequence charSequence, final int start, final int before,
+            final int after) {
+        if (mEditText.isInEditMode()) {
+            return;
+        }
+
+        //before > after --> a deletion occured
+        if (before <= after && charSequence instanceof Spannable) {
+            switch (EmojiCompat.get().getLoadState()){
+                case EmojiCompat.LOAD_STATE_SUCCEEDED:
+                    final Spannable s = (Spannable) charSequence;
+                    EmojiCompat.get().process(s, start, start + after, mMaxEmojiCount,
+                            mEmojiReplaceStrategy);
+                    break;
+                case EmojiCompat.LOAD_STATE_LOADING:
+                    EmojiCompat.get().registerInitCallback(getInitCallback());
+                    break;
+                case EmojiCompat.LOAD_STATE_FAILED:
+                default:
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        // do nothing
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        // do nothing
+    }
+
+    private InitCallback getInitCallback() {
+        if (mInitCallback == null) {
+            mInitCallback = new InitCallbackImpl(mEditText);
+        }
+        return mInitCallback;
+    }
+
+    private static class InitCallbackImpl extends InitCallback {
+        private final Reference<EditText> mViewRef;
+
+        InitCallbackImpl(EditText editText) {
+            mViewRef = new WeakReference<>(editText);
+        }
+
+        @Override
+        public void onInitialized() {
+            super.onInitialized();
+            final EditText editText = mViewRef.get();
+            if (editText != null && editText.isAttachedToWindow()) {
+                final Editable text = editText.getEditableText();
+
+                final int selectionStart = Selection.getSelectionStart(text);
+                final int selectionEnd = Selection.getSelectionEnd(text);
+
+                EmojiCompat.get().process(text);
+
+                EmojiInputFilter.updateSelection(text, selectionStart, selectionEnd);
+            }
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java
new file mode 100644
index 0000000..dac9905
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiCompat;
+import android.text.method.TransformationMethod;
+import android.view.View;
+
+/**
+ * TransformationMethod wrapper in order to update transformed text with emojis.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(19)
+class EmojiTransformationMethod implements TransformationMethod {
+    private final TransformationMethod mTransformationMethod;
+
+    EmojiTransformationMethod(TransformationMethod transformationMethod) {
+        mTransformationMethod = transformationMethod;
+    }
+
+    @Override
+    public CharSequence getTransformation(@Nullable CharSequence source, @NonNull final View view) {
+        if (view.isInEditMode()) {
+            return source;
+        }
+
+        if (mTransformationMethod != null) {
+            source = mTransformationMethod.getTransformation(source, view);
+        }
+
+        if (source != null) {
+            switch (EmojiCompat.get().getLoadState()){
+                case EmojiCompat.LOAD_STATE_SUCCEEDED:
+                    return EmojiCompat.get().process(source);
+                case EmojiCompat.LOAD_STATE_LOADING:
+                case EmojiCompat.LOAD_STATE_FAILED:
+                default:
+                    break;
+            }
+        }
+        return source;
+    }
+
+    @Override
+    public void onFocusChanged(final View view, final CharSequence sourceText,
+            final boolean focused, final int direction, final Rect previouslyFocusedRect) {
+        if (mTransformationMethod != null) {
+            mTransformationMethod.onFocusChanged(view, sourceText, focused, direction,
+                    previouslyFocusedRect);
+        }
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/ExtractButtonCompat.java b/emoji/core/src/android/support/text/emoji/widget/ExtractButtonCompat.java
new file mode 100644
index 0000000..fc8eb78
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/ExtractButtonCompat.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+/**
+ * Support library implementation for ExtractButton. Used by {@link EmojiExtractViewHelper} while
+ * inflating {@link EmojiExtractEditText} for keyboard use.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class ExtractButtonCompat extends Button {
+    public ExtractButtonCompat(Context context) {
+        super(context, null);
+    }
+
+    public ExtractButtonCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ExtractButtonCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public ExtractButtonCompat(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Pretend like the window this view is in always has focus, so it will
+     * highlight when selected.
+     */
+    @Override
+    public boolean hasWindowFocus() {
+        return isEnabled() && getVisibility() == VISIBLE ? true : false;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/SpannableBuilder.java b/emoji/core/src/android/support/text/emoji/widget/SpannableBuilder.java
new file mode 100644
index 0000000..c60a90c
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/SpannableBuilder.java
@@ -0,0 +1,434 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.EmojiSpan;
+import android.support.v4.util.Preconditions;
+import android.text.Editable;
+import android.text.SpanWatcher;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextWatcher;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * When setSpan functions is called on EmojiSpannableBuilder, it checks if the mObject is instance
+ * of the DynamicLayout$ChangeWatcher. if so, it wraps it into another listener mObject
+ * (WatcherWrapper) that implements the same interfaces.
+ * <p>
+ * During a span change event WatcherWrapper’s functions are fired, it checks if the span is an
+ * EmojiSpan, and prevents the ChangeWatcher being fired for that span. WatcherWrapper informs
+ * ChangeWatcher only once at the end of the edit. Important point is, the block operation is
+ * applied only for EmojiSpans. Therefore any other span change operation works the same way as in
+ * the framework.
+ *
+ * @hide
+ * @see EmojiEditableFactory
+ */
+@RestrictTo(LIBRARY_GROUP)
+public final class SpannableBuilder extends SpannableStringBuilder {
+    /**
+     * DynamicLayout$ChangeWatcher class.
+     */
+    private final Class<?> mWatcherClass;
+
+    /**
+     * All WatcherWrappers.
+     */
+    private final List<WatcherWrapper> mWatchers = new ArrayList<>();
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    SpannableBuilder(@NonNull Class<?> watcherClass) {
+        Preconditions.checkNotNull(watcherClass, "watcherClass cannot be null");
+        mWatcherClass = watcherClass;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    SpannableBuilder(@NonNull Class<?> watcherClass, @NonNull CharSequence text) {
+        super(text);
+        Preconditions.checkNotNull(watcherClass, "watcherClass cannot be null");
+        mWatcherClass = watcherClass;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    SpannableBuilder(@NonNull Class<?> watcherClass, @NonNull CharSequence text, int start,
+            int end) {
+        super(text, start, end);
+        Preconditions.checkNotNull(watcherClass, "watcherClass cannot be null");
+        mWatcherClass = watcherClass;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    static SpannableBuilder create(@NonNull Class<?> clazz, @NonNull CharSequence text) {
+        return new SpannableBuilder(clazz, text);
+    }
+
+    /**
+     * Checks whether the mObject is instance of the DynamicLayout$ChangeWatcher.
+     *
+     * @param object mObject to be checked
+     *
+     * @return true if mObject is instance of the DynamicLayout$ChangeWatcher.
+     */
+    private boolean isWatcher(@Nullable Object object) {
+        return object != null && isWatcher(object.getClass());
+    }
+
+    /**
+     * Checks whether the class is DynamicLayout$ChangeWatcher.
+     *
+     * @param clazz class to be checked
+     *
+     * @return true if class is DynamicLayout$ChangeWatcher.
+     */
+    private boolean isWatcher(@NonNull Class<?> clazz) {
+        return mWatcherClass == clazz;
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        return new SpannableBuilder(mWatcherClass, this, start, end);
+    }
+
+    /**
+     * If the span being added is instance of DynamicLayout$ChangeWatcher, wrap the watcher in
+     * another internal watcher that will prevent EmojiSpan events to be fired to DynamicLayout. Set
+     * this new mObject as the span.
+     */
+    @Override
+    public void setSpan(Object what, int start, int end, int flags) {
+        if (isWatcher(what)) {
+            final WatcherWrapper span = new WatcherWrapper(what);
+            mWatchers.add(span);
+            what = span;
+        }
+        super.setSpan(what, start, end, flags);
+    }
+
+    /**
+     * If previously a DynamicLayout$ChangeWatcher was wrapped in a WatcherWrapper, return the
+     * correct Object that the client has set.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
+        if (isWatcher(kind)) {
+            final WatcherWrapper[] spans = super.getSpans(queryStart, queryEnd,
+                    WatcherWrapper.class);
+            final T[] result = (T[]) Array.newInstance(kind, spans.length);
+            for (int i = 0; i < spans.length; i++) {
+                result[i] = (T) spans[i].mObject;
+            }
+            return result;
+        }
+        return super.getSpans(queryStart, queryEnd, kind);
+    }
+
+    /**
+     * If the client wants to remove the DynamicLayout$ChangeWatcher span, remove the WatcherWrapper
+     * instead.
+     */
+    @Override
+    public void removeSpan(Object what) {
+        final WatcherWrapper watcher;
+        if (isWatcher(what)) {
+            watcher = getWatcherFor(what);
+            if (watcher != null) {
+                what = watcher;
+            }
+        } else {
+            watcher = null;
+        }
+
+        super.removeSpan(what);
+
+        if (watcher != null) {
+            mWatchers.remove(watcher);
+        }
+    }
+
+    /**
+     * Return the correct start for the DynamicLayout$ChangeWatcher span.
+     */
+    @Override
+    public int getSpanStart(Object tag) {
+        if (isWatcher(tag)) {
+            final WatcherWrapper watcher = getWatcherFor(tag);
+            if (watcher != null) {
+                tag = watcher;
+            }
+        }
+        return super.getSpanStart(tag);
+    }
+
+    /**
+     * Return the correct end for the DynamicLayout$ChangeWatcher span.
+     */
+    @Override
+    public int getSpanEnd(Object tag) {
+        if (isWatcher(tag)) {
+            final WatcherWrapper watcher = getWatcherFor(tag);
+            if (watcher != null) {
+                tag = watcher;
+            }
+        }
+        return super.getSpanEnd(tag);
+    }
+
+    /**
+     * Return the correct flags for the DynamicLayout$ChangeWatcher span.
+     */
+    @Override
+    public int getSpanFlags(Object tag) {
+        if (isWatcher(tag)) {
+            final WatcherWrapper watcher = getWatcherFor(tag);
+            if (watcher != null) {
+                tag = watcher;
+            }
+        }
+        return super.getSpanFlags(tag);
+    }
+
+    /**
+     * Return the correct transition for the DynamicLayout$ChangeWatcher span.
+     */
+    @Override
+    public int nextSpanTransition(int start, int limit, Class type) {
+        if (isWatcher(type)) {
+            type = WatcherWrapper.class;
+        }
+        return super.nextSpanTransition(start, limit, type);
+    }
+
+    /**
+     * Find the WatcherWrapper for a given DynamicLayout$ChangeWatcher.
+     *
+     * @param object DynamicLayout$ChangeWatcher mObject
+     *
+     * @return WatcherWrapper that wraps the mObject.
+     */
+    private WatcherWrapper getWatcherFor(Object object) {
+        for (int i = 0; i < mWatchers.size(); i++) {
+            WatcherWrapper watcher = mWatchers.get(i);
+            if (watcher.mObject == object) {
+                return watcher;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void beginBatchEdit() {
+        blockWatchers();
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void endBatchEdit() {
+        unblockwatchers();
+        fireWatchers();
+    }
+
+    /**
+     * Block all watcher wrapper events.
+     */
+    private void blockWatchers() {
+        for (int i = 0; i < mWatchers.size(); i++) {
+            mWatchers.get(i).blockCalls();
+        }
+    }
+
+    /**
+     * Unblock all watcher wrapper events.
+     */
+    private void unblockwatchers() {
+        for (int i = 0; i < mWatchers.size(); i++) {
+            mWatchers.get(i).unblockCalls();
+        }
+    }
+
+    /**
+     * Unblock all watcher wrapper events. Called by editing operations, namely
+     * {@link SpannableStringBuilder#replace(int, int, CharSequence)}.
+     */
+    private void fireWatchers() {
+        for (int i = 0; i < mWatchers.size(); i++) {
+            mWatchers.get(i).onTextChanged(this, 0, this.length(), this.length());
+        }
+    }
+
+    @Override
+    public SpannableStringBuilder replace(int start, int end, CharSequence tb) {
+        blockWatchers();
+        super.replace(start, end, tb);
+        unblockwatchers();
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart,
+            int tbend) {
+        blockWatchers();
+        super.replace(start, end, tb, tbstart, tbend);
+        unblockwatchers();
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder insert(int where, CharSequence tb) {
+        super.insert(where, tb);
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder insert(int where, CharSequence tb, int start, int end) {
+        super.insert(where, tb, start, end);
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder delete(int start, int end) {
+        super.delete(start, end);
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder append(CharSequence text) {
+        super.append(text);
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder append(char text) {
+        super.append(text);
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder append(CharSequence text, int start, int end) {
+        super.append(text, start, end);
+        return this;
+    }
+
+    @Override
+    public SpannableStringBuilder append(CharSequence text, Object what, int flags) {
+        super.append(text, what, flags);
+        return this;
+    }
+
+    /**
+     * Wraps a DynamicLayout$ChangeWatcher in order to prevent firing of events to DynamicLayout.
+     */
+    private static class WatcherWrapper implements TextWatcher, SpanWatcher {
+        private final Object mObject;
+        private final AtomicInteger mBlockCalls = new AtomicInteger(0);
+
+        WatcherWrapper(Object object) {
+            this.mObject = object;
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            ((TextWatcher) mObject).beforeTextChanged(s, start, count, after);
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            ((TextWatcher) mObject).onTextChanged(s, start, before, count);
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            ((TextWatcher) mObject).afterTextChanged(s);
+        }
+
+        /**
+         * Prevent the onSpanAdded calls to DynamicLayout$ChangeWatcher if in a replace operation
+         * (mBlockCalls is set) and the span that is added is an EmojiSpan.
+         */
+        @Override
+        public void onSpanAdded(Spannable text, Object what, int start, int end) {
+            if (mBlockCalls.get() > 0 && isEmojiSpan(what)) {
+                return;
+            }
+            ((SpanWatcher) mObject).onSpanAdded(text, what, start, end);
+        }
+
+        /**
+         * Prevent the onSpanRemoved calls to DynamicLayout$ChangeWatcher if in a replace operation
+         * (mBlockCalls is set) and the span that is added is an EmojiSpan.
+         */
+        @Override
+        public void onSpanRemoved(Spannable text, Object what, int start, int end) {
+            if (mBlockCalls.get() > 0 && isEmojiSpan(what)) {
+                return;
+            }
+            ((SpanWatcher) mObject).onSpanRemoved(text, what, start, end);
+        }
+
+        /**
+         * Prevent the onSpanChanged calls to DynamicLayout$ChangeWatcher if in a replace operation
+         * (mBlockCalls is set) and the span that is added is an EmojiSpan.
+         */
+        @Override
+        public void onSpanChanged(Spannable text, Object what, int ostart, int oend, int nstart,
+                int nend) {
+            if (mBlockCalls.get() > 0 && isEmojiSpan(what)) {
+                return;
+            }
+            ((SpanWatcher) mObject).onSpanChanged(text, what, ostart, oend, nstart, nend);
+        }
+
+        final void blockCalls() {
+            mBlockCalls.incrementAndGet();
+        }
+
+        final void unblockCalls() {
+            mBlockCalls.decrementAndGet();
+        }
+
+        private boolean isEmojiSpan(final Object span) {
+            return span instanceof EmojiSpan;
+        }
+    }
+
+}
diff --git a/emoji/core/tests/AndroidManifest.xml b/emoji/core/tests/AndroidManifest.xml
new file mode 100644
index 0000000..061e751
--- /dev/null
+++ b/emoji/core/tests/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.text.emoji">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
+
+    <application>
+        <activity android:name=".TestActivity"/>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/emoji/core/tests/java/android/support/text/emoji/AllEmojisTest.java b/emoji/core/tests/java/android/support/text/emoji/AllEmojisTest.java
new file mode 100644
index 0000000..b48a1be
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/AllEmojisTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiAt;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+
+import static junit.framework.TestCase.assertTrue;
+
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.text.emoji.util.TestString;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Reads raw/allemojis.txt which includes all the emojis known to human kind and tests that
+ * EmojiCompat creates EmojiSpans for each one of them.
+ */
+@SmallTest
+@RunWith(Parameterized.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class AllEmojisTest {
+
+    /**
+     * String representation for a single emoji
+     */
+    private String mString;
+
+    /**
+     * Codepoints of emoji for better assert error message.
+     */
+    private String mCodepoints;
+
+    @BeforeClass
+    public static void setup() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() throws IOException {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final InputStream inputStream = context.getAssets().open("emojis.txt");
+        try {
+            final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+            final Collection<Object[]> data = new ArrayList<>();
+            final StringBuilder stringBuilder = new StringBuilder();
+            final StringBuilder codePointsBuilder = new StringBuilder();
+
+            String s;
+            while ((s = reader.readLine()) != null) {
+                s = s.trim();
+                // pass comments
+                if (s.isEmpty() || s.startsWith("#")) continue;
+
+                stringBuilder.setLength(0);
+                codePointsBuilder.setLength(0);
+
+                // emoji codepoints are space separated: i.e. 0x1f1e6 0x1f1e8
+                final String[] split = s.split(" ");
+
+                for (int index = 0; index < split.length; index++) {
+                    final String part = split[index].trim();
+                    codePointsBuilder.append(part);
+                    codePointsBuilder.append(",");
+                    stringBuilder.append(Character.toChars(Integer.parseInt(part, 16)));
+                }
+                data.add(new Object[]{stringBuilder.toString(), codePointsBuilder.toString()});
+            }
+
+            return data;
+        } finally {
+            inputStream.close();
+        }
+
+    }
+
+    public AllEmojisTest(String string, String codepoints) {
+        mString = string;
+        mCodepoints = codepoints;
+    }
+
+    @Test
+    public void testEmoji() {
+        assertTrue("EmojiCompat should have emoji: " + mCodepoints,
+                EmojiCompat.get().hasEmojiGlyph(mString));
+        assertEmojiCompatAddsEmoji(mString);
+    }
+
+    private void assertEmojiCompatAddsEmoji(final String str) {
+        TestString string = new TestString(str);
+        CharSequence sequence = EmojiCompat.get().process(string.toString());
+        assertThat(sequence, hasEmojiCount(1));
+        assertThat(sequence, hasEmojiAt(string.emojiStartIndex(), string.emojiEndIndex()));
+
+        // case where Emoji is in the middle of string
+        string = new TestString(str).withPrefix().withSuffix();
+        sequence = EmojiCompat.get().process(string.toString());
+        assertThat(sequence, hasEmojiCount(1));
+        assertThat(sequence, hasEmojiAt(string.emojiStartIndex(), string.emojiEndIndex()));
+
+        // case where Emoji is at the end of string
+        string = new TestString(str).withSuffix();
+        sequence = EmojiCompat.get().process(string.toString());
+        assertThat(sequence, hasEmojiCount(1));
+        assertThat(sequence, hasEmojiAt(string.emojiStartIndex(), string.emojiEndIndex()));
+    }
+
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java b/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java
new file mode 100644
index 0000000..538ce99
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_SINGLE_CODEPOINT;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.util.TestString;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ConfigTest {
+
+    Context mContext;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_throwsExceptionIfMetadataLoaderNull() {
+        new TestConfigBuilder.TestConfig(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testInitCallback_throwsExceptionIfNull() {
+        new ValidTestConfig().registerInitCallback(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testUnregisterInitCallback_throwsExceptionIfNull() {
+        new ValidTestConfig().unregisterInitCallback(null);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testBuild_withDefaultValues() {
+        final EmojiCompat.Config config = new ValidTestConfig().setReplaceAll(true);
+
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+
+        final CharSequence processed = emojiCompat.process(new TestString(EMOJI_SINGLE_CODEPOINT)
+                .toString());
+        assertThat(processed, hasEmojiCount(1));
+        assertThat(processed, hasEmoji(EMOJI_SINGLE_CODEPOINT));
+    }
+
+    @Test
+    public void testInitCallback_callsSuccessCallback() {
+        final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
+
+        final EmojiCompat.Config config = new ValidTestConfig().registerInitCallback(initCallback1)
+                .registerInitCallback(initCallback2);
+        EmojiCompat.reset(config);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback1, times(1)).onInitialized();
+        verify(initCallback2, times(1)).onInitialized();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19) //Fail callback never called for pre 19
+    @TargetApi(19)
+    public void testInitCallback_callsFailCallback() {
+        final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.MetadataRepoLoader loader = mock(EmojiCompat.MetadataRepoLoader.class);
+        doThrow(new RuntimeException("")).when(loader)
+                .load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+
+        final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader)
+                .registerInitCallback(initCallback1)
+                .registerInitCallback(initCallback2);
+        EmojiCompat.reset(config);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback1, times(1)).onFailed(any(Throwable.class));
+        verify(initCallback2, times(1)).onFailed(any(Throwable.class));
+    }
+
+    @Test
+    public void testBuild_withEmojiSpanIndicator() {
+        EmojiCompat.Config config = new ValidTestConfig();
+        EmojiCompat emojiCompat = EmojiCompat.reset(config);
+
+        assertFalse(emojiCompat.isEmojiSpanIndicatorEnabled());
+
+        config = new ValidTestConfig().setEmojiSpanIndicatorEnabled(true);
+        emojiCompat = EmojiCompat.reset(config);
+
+        assertTrue(emojiCompat.isEmojiSpanIndicatorEnabled());
+    }
+
+    @Test
+    public void testBuild_withEmojiSpanIndicatorColor() {
+        EmojiCompat.Config config = new ValidTestConfig();
+        EmojiCompat emojiCompat = EmojiCompat.reset(config);
+
+        assertEquals(Color.GREEN, emojiCompat.getEmojiSpanIndicatorColor());
+
+        config = new ValidTestConfig().setEmojiSpanIndicatorColor(Color.RED);
+        emojiCompat = EmojiCompat.reset(config);
+
+        assertEquals(Color.RED, emojiCompat.getEmojiSpanIndicatorColor());
+    }
+
+    @Test
+    public void testBuild_defaultEmojiSpanIndicatorColor() {
+        final EmojiCompat.Config config = new ValidTestConfig().setEmojiSpanIndicatorEnabled(true);
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+
+        assertTrue(emojiCompat.isEmojiSpanIndicatorEnabled());
+    }
+
+    private static class ValidTestConfig extends EmojiCompat.Config {
+        ValidTestConfig() {
+            super(new TestConfigBuilder.TestEmojiDataLoader());
+        }
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
new file mode 100644
index 0000000..ee31ed7
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
@@ -0,0 +1,749 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.text.emoji.TestConfigBuilder.TestConfig;
+import static android.support.text.emoji.TestConfigBuilder.WaitingDataLoader;
+import static android.support.text.emoji.TestConfigBuilder.config;
+import static android.support.text.emoji.util.Emoji.CHAR_DEFAULT_EMOJI_STYLE;
+import static android.support.text.emoji.util.Emoji.CHAR_DEFAULT_TEXT_STYLE;
+import static android.support.text.emoji.util.Emoji.CHAR_DIGIT;
+import static android.support.text.emoji.util.Emoji.CHAR_FITZPATRICK;
+import static android.support.text.emoji.util.Emoji.CHAR_VS_EMOJI;
+import static android.support.text.emoji.util.Emoji.CHAR_VS_TEXT;
+import static android.support.text.emoji.util.Emoji.DEFAULT_TEXT_STYLE;
+import static android.support.text.emoji.util.Emoji.EMOJI_ASTERISK_KEYCAP;
+import static android.support.text.emoji.util.Emoji.EMOJI_DIGIT_ES;
+import static android.support.text.emoji.util.Emoji.EMOJI_DIGIT_ES_KEYCAP;
+import static android.support.text.emoji.util.Emoji.EMOJI_DIGIT_KEYCAP;
+import static android.support.text.emoji.util.Emoji.EMOJI_FLAG;
+import static android.support.text.emoji.util.Emoji.EMOJI_GENDER;
+import static android.support.text.emoji.util.Emoji.EMOJI_GENDER_WITHOUT_VS;
+import static android.support.text.emoji.util.Emoji.EMOJI_REGIONAL_SYMBOL;
+import static android.support.text.emoji.util.Emoji.EMOJI_SINGLE_CODEPOINT;
+import static android.support.text.emoji.util.Emoji.EMOJI_SKIN_MODIFIER;
+import static android.support.text.emoji.util.Emoji.EMOJI_SKIN_MODIFIER_TYPE_ONE;
+import static android.support.text.emoji.util.Emoji.EMOJI_SKIN_MODIFIER_WITH_VS;
+import static android.support.text.emoji.util.Emoji.EMOJI_UNKNOWN_FLAG;
+import static android.support.text.emoji.util.Emoji.EMOJI_WITH_ZWJ;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiAt;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+import static android.support.text.emoji.util.KeyboardUtil.del;
+
+import static junit.framework.TestCase.assertFalse;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat.Config;
+import android.support.text.emoji.util.Emoji.EmojiMapping;
+import android.support.text.emoji.util.TestString;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.SpannedString;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiCompatTest {
+
+    @Before
+    public void setup() {
+        EmojiCompat.reset(config());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testGet_throwsException() {
+        EmojiCompat.reset((EmojiCompat) null);
+        EmojiCompat.get();
+    }
+
+    @Test
+    public void testProcess_doesNothing_withNullCharSequence() {
+        assertNull(EmojiCompat.get().process(null));
+    }
+
+    @Test
+    public void testProcess_returnsEmptySpanned_withEmptyString() {
+        final CharSequence charSequence = EmojiCompat.get().process("");
+        assertNotNull(charSequence);
+        assertEquals(0, charSequence.length());
+        assertThat(charSequence, not(hasEmoji()));
+    }
+
+    @SuppressLint("Range")
+    @Test(expected = IllegalArgumentException.class)
+    public void testProcess_withNegativeStartValue() {
+        EmojiCompat.get().process("a", -1, 1);
+    }
+
+    @SuppressLint("Range")
+    @Test(expected = IllegalArgumentException.class)
+    public void testProcess_withNegativeEndValue() {
+        EmojiCompat.get().process("a", 1, -1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testProcess_withStartSmallerThanEndValue() {
+        EmojiCompat.get().process("aa", 1, 0);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testProcess_withStartGreaterThanLength() {
+        EmojiCompat.get().process("a", 2, 2);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testProcess_withEndGreaterThanLength() {
+        EmojiCompat.get().process("a", 0, 2);
+    }
+
+    @Test
+    public void testProcessWithStartEnd_withNoOpValues() {
+        final Spannable spannable = new SpannableString(new TestString('a')
+                .withPrefix().withSuffix().toString());
+        // early return check
+        assertSame(spannable, EmojiCompat.get().process(spannable, 0, 0));
+        assertSame(spannable, EmojiCompat.get().process(spannable, 1, 1));
+        assertSame(spannable, EmojiCompat.get().process(spannable, spannable.length(),
+                spannable.length()));
+    }
+
+    @Test
+    public void testProcess_doesNotAddEmojiSpan() {
+        final String string = "abc";
+        final CharSequence charSequence = EmojiCompat.get().process(string);
+        assertNotNull(charSequence);
+        assertEquals(string, charSequence.toString());
+        assertThat(charSequence, not(hasEmoji()));
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testProcess_returnsSameCharSequence_pre19() {
+        assertNull(EmojiCompat.get().process(null));
+
+        CharSequence testString = "abc";
+        assertSame(testString, EmojiCompat.get().process(testString));
+
+        testString = new SpannableString("abc");
+        assertSame(testString, EmojiCompat.get().process(testString));
+
+        testString = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString();
+        assertSame(testString, EmojiCompat.get().process(testString));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsSingleCodePointEmoji() {
+        assertCodePointMatch(EMOJI_SINGLE_CODEPOINT);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsFlagEmoji() {
+        assertCodePointMatch(EMOJI_FLAG);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsUnknownFlagEmoji() {
+        assertCodePointMatch(EMOJI_UNKNOWN_FLAG);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsRegionalIndicatorSymbol() {
+        assertCodePointMatch(EMOJI_REGIONAL_SYMBOL);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsKeyCapEmoji() {
+        assertCodePointMatch(EMOJI_DIGIT_KEYCAP);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_doesNotAddEmojiForNumbers() {
+        assertCodePointDoesNotMatch(new int[] {CHAR_DIGIT});
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_doesNotAddEmojiForNumbers_1() {
+        final TestString string = new TestString(EMOJI_SINGLE_CODEPOINT).append('1', 'f');
+        CharSequence charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, hasEmojiCount(1));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsVariantSelectorEmoji() {
+        assertCodePointMatch(EMOJI_DIGIT_ES);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_doesNotAddVariantSelectorTextStyle() {
+        assertCodePointDoesNotMatch(new int[]{CHAR_DIGIT, CHAR_VS_TEXT});
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsVariantSelectorAndKeyCapEmoji() {
+        assertCodePointMatch(EMOJI_DIGIT_ES_KEYCAP);
+    }
+
+    @Test
+    public void testProcess_doesNotAddEmoji_forVariantBaseWithoutSelector() {
+        assertCodePointDoesNotMatch(new int[]{CHAR_DIGIT});
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsAsteriskKeyCapEmoji() {
+        assertCodePointMatch(EMOJI_ASTERISK_KEYCAP);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsSkinModifierEmoji() {
+        assertCodePointMatch(EMOJI_SKIN_MODIFIER);
+        assertCodePointMatch(EMOJI_SKIN_MODIFIER_TYPE_ONE);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsSkinModifierEmoji_withVariantSelector() {
+        assertCodePointMatch(EMOJI_SKIN_MODIFIER_WITH_VS);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsSkinModifierEmoji_270c_withVariantSelector() {
+        // 0x270c is a Standardized Variant Base, Emoji Modifier Base and also Emoji
+        // therefore it is different than i.e. 0x1f3c3. The code actually failed for this test
+        // at first.
+        assertCodePointMatch(0xF0734, new int[]{0x270C, CHAR_VS_EMOJI, CHAR_FITZPATRICK});
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_defaultStyleDoesNotAddSpan() {
+        assertCodePointDoesNotMatch(new int[]{CHAR_DEFAULT_TEXT_STYLE});
+        assertCodePointMatch(DEFAULT_TEXT_STYLE);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_defaultEmojiStyle_withTextStyleVs() {
+        assertCodePointMatch(EMOJI_SINGLE_CODEPOINT.id(),
+                new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_EMOJI});
+        assertCodePointDoesNotMatch(new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_TEXT});
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_genderEmoji() {
+        assertCodePointMatch(EMOJI_GENDER);
+        assertCodePointMatch(EMOJI_GENDER_WITHOUT_VS);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_standardizedVariantEmojiExceptions() {
+        final int[][] exceptions = new int[][]{
+                {0x2600, 0xF034D},
+                {0x2601, 0xF0167},
+                {0x260E, 0xF034E},
+                {0x261D, 0xF0227},
+                {0x263A, 0xF02A6},
+                {0x2660, 0xF0350},
+                {0x2663, 0xF033F},
+                {0x2665, 0xF033B},
+                {0x2666, 0xF033E},
+                {0x270C, 0xF0079},
+                {0x2744, 0xF0342},
+                {0x2764, 0xF0362}
+        };
+
+        for (int i = 0; i < exceptions.length; i++) {
+            final int[] codepoints = new int[]{exceptions[i][0]};
+            assertCodePointMatch(exceptions[i][1], codepoints);
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_addsZwjEmoji() {
+        assertCodePointMatch(EMOJI_WITH_ZWJ);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_doesNotAddEmojiForNumbersAfterZwjEmo() {
+        TestString string = new TestString(EMOJI_WITH_ZWJ).append(0x20, 0x2B, 0x31)
+                .withSuffix().withPrefix();
+        CharSequence charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, hasEmojiAt(EMOJI_WITH_ZWJ, string.emojiStartIndex(),
+                string.emojiEndIndex() - 3));
+        assertThat(charSequence, hasEmojiCount(1));
+
+        string = new TestString(EMOJI_WITH_ZWJ).withSuffix().withPrefix();
+        charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, hasEmojiCount(1));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_withAppend() {
+        final Editable editable = new SpannableStringBuilder(new TestString('a').withPrefix()
+                .withSuffix().toString());
+        final int start = 1;
+        final int end = start + EMOJI_SINGLE_CODEPOINT.charCount();
+        editable.insert(start, new TestString(EMOJI_SINGLE_CODEPOINT).toString());
+        EmojiCompat.get().process(editable, start, end);
+        assertThat(editable, hasEmojiCount(1));
+        assertThat(editable, hasEmojiAt(EMOJI_SINGLE_CODEPOINT, start, end));
+    }
+
+    @Test
+    public void testProcess_doesNotCreateSpannable_ifNoEmoji() {
+        CharSequence processed = EmojiCompat.get().process("abc");
+        assertNotNull(processed);
+        assertThat(processed, instanceOf(String.class));
+
+        processed = EmojiCompat.get().process(new SpannedString("abc"));
+        assertNotNull(processed);
+        assertThat(processed, instanceOf(SpannedString.class));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_reprocess() {
+        final String string = new TestString(EMOJI_SINGLE_CODEPOINT)
+                .append(EMOJI_SINGLE_CODEPOINT)
+                .append(EMOJI_SINGLE_CODEPOINT)
+                .withPrefix().withSuffix().toString();
+
+        Spannable processed = (Spannable) EmojiCompat.get().process(string);
+        assertThat(processed, hasEmojiCount(3));
+
+        final EmojiSpan[] spans = processed.getSpans(0, processed.length(), EmojiSpan.class);
+        final Set<EmojiSpan> spanSet = new HashSet<>();
+        Collections.addAll(spanSet, spans);
+
+        processed = (Spannable) EmojiCompat.get().process(processed);
+        assertThat(processed, hasEmojiCount(3));
+        // new spans should be new instances
+        final EmojiSpan[] newSpans = processed.getSpans(0, processed.length(), EmojiSpan.class);
+        for (int i = 0; i < newSpans.length; i++) {
+            assertFalse(spanSet.contains(newSpans[i]));
+        }
+    }
+
+    @SuppressLint("Range")
+    @Test(expected = IllegalArgumentException.class)
+    public void testProcess_throwsException_withMaxEmojiSetToNegative() {
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+
+        final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+                -1 /*maxEmojiCount*/);
+
+        assertThat(processed, not(hasEmoji()));
+    }
+
+    @Test
+    public void testProcess_withMaxEmojiSetToZero() {
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+
+        final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+                0 /*maxEmojiCount*/);
+
+        assertThat(processed, not(hasEmoji()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_withMaxEmojiSetToOne() {
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+
+        final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+                1 /*maxEmojiCount*/);
+
+        assertThat(processed, hasEmojiCount(1));
+        assertThat(processed, hasEmoji(EMOJI_SINGLE_CODEPOINT));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_withMaxEmojiSetToLessThenExistingSpanCount() {
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT)
+                .append(EMOJI_SINGLE_CODEPOINT)
+                .append(EMOJI_SINGLE_CODEPOINT)
+                .toString();
+
+        // add 2 spans
+        final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 2);
+
+        assertThat(processed, hasEmojiCount(2));
+
+        // use the Spannable with 2 spans, but use maxEmojiCount=1, start from the beginning of
+        // last (3rd) emoji
+        EmojiCompat.get().process(processed, original.length() - EMOJI_SINGLE_CODEPOINT.charCount(),
+                original.length(), 1 /*maxEmojiCount*/);
+
+        // expectation: there are still 2 emojis
+        assertThat(processed, hasEmojiCount(2));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_withMaxEmojiSet_withExistingEmojis() {
+        // test string with two emoji characters
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT)
+                .append(EMOJI_FLAG).toString();
+
+        // process and add 1 EmojiSpan, maxEmojiCount=1
+        CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+                1 /*maxEmojiCount*/);
+
+        // assert that there is a single emoji
+        assertThat(processed, hasEmojiCount(1));
+        assertThat(processed,
+                hasEmojiAt(EMOJI_SINGLE_CODEPOINT, 0, EMOJI_SINGLE_CODEPOINT.charCount()));
+
+        // call process again with the charSequence that already has 1 span
+        processed = EmojiCompat.get().process(processed, EMOJI_SINGLE_CODEPOINT.charCount(),
+                processed.length(), 1 /*maxEmojiCount*/);
+
+        // assert that there is still a single emoji
+        assertThat(processed, hasEmojiCount(1));
+        assertThat(processed,
+                hasEmojiAt(EMOJI_SINGLE_CODEPOINT, 0, EMOJI_SINGLE_CODEPOINT.charCount()));
+
+        // make the same call, this time with maxEmojiCount=2
+        processed = EmojiCompat.get().process(processed, EMOJI_SINGLE_CODEPOINT.charCount(),
+                processed.length(), 2 /*maxEmojiCount*/);
+
+        // assert that it contains 2 emojis
+        assertThat(processed, hasEmojiCount(2));
+        assertThat(processed,
+                hasEmojiAt(EMOJI_SINGLE_CODEPOINT, 0, EMOJI_SINGLE_CODEPOINT.charCount()));
+        assertThat(processed,
+                hasEmojiAt(EMOJI_FLAG, EMOJI_SINGLE_CODEPOINT.charCount(),
+                        original.length()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_withReplaceNonExistent_callsGlyphChecker() {
+        final Config config = TestConfigBuilder.config().setReplaceAll(true);
+        EmojiCompat.reset(config);
+
+        final EmojiProcessor.GlyphChecker glyphChecker = mock(EmojiProcessor.GlyphChecker.class);
+        when(glyphChecker.hasGlyph(any(CharSequence.class), anyInt(), anyInt())).thenReturn(true);
+        EmojiCompat.get().setGlyphChecker(glyphChecker);
+
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+
+        CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+                Integer.MAX_VALUE /*maxEmojiCount*/, EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT);
+
+        // when function overrides config level replaceAll, a call to GlyphChecker is expected.
+        verify(glyphChecker, times(1)).hasGlyph(any(CharSequence.class), anyInt(), anyInt());
+
+        // since replaceAll is false, there should be no EmojiSpans
+        assertThat(processed, not(hasEmoji()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testProcess_withReplaceDefault_doesNotCallGlyphChecker() {
+        final Config config = TestConfigBuilder.config().setReplaceAll(true);
+        EmojiCompat.reset(config);
+
+        final EmojiProcessor.GlyphChecker glyphChecker = mock(EmojiProcessor.GlyphChecker.class);
+        when(glyphChecker.hasGlyph(any(CharSequence.class), anyInt(), anyInt())).thenReturn(true);
+        EmojiCompat.get().setGlyphChecker(glyphChecker);
+
+        final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+        // call without replaceAll, config value (true) should be used
+        final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+                Integer.MAX_VALUE /*maxEmojiCount*/, EmojiCompat.REPLACE_STRATEGY_DEFAULT);
+
+        // replaceAll=true should not call hasGlyph
+        verify(glyphChecker, times(0)).hasGlyph(any(CharSequence.class), anyInt(), anyInt());
+
+        assertThat(processed, hasEmojiCount(1));
+        assertThat(processed, hasEmoji(EMOJI_SINGLE_CODEPOINT));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testHasEmojiGlyph_withNullCharSequence() {
+        EmojiCompat.get().hasEmojiGlyph(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testHasEmojiGlyph_withMetadataVersion_withNullCharSequence() {
+        EmojiCompat.get().hasEmojiGlyph(null, Integer.MAX_VALUE);
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testHasEmojiGlyph_pre19() {
+        String sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString();
+        assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence));
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testHasEmojiGlyph_withMetaVersion_pre19() {
+        String sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString();
+        assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence, Integer.MAX_VALUE));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testHasEmojiGlyph_returnsTrueForExistingEmoji() {
+        final String sequence = new TestString(EMOJI_FLAG).toString();
+        assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence));
+    }
+
+    @Test
+    public void testHasGlyph_returnsFalseForNonExistentEmoji() {
+        final String sequence = new TestString(EMOJI_FLAG).append(0x1111).toString();
+        assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testHashEmojiGlyph_withDefaultEmojiStyles() {
+        String sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString();
+        assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence));
+
+        sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_EMOJI}).toString();
+        assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence));
+
+        sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_TEXT}).toString();
+        assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testHashEmojiGlyph_withMetadataVersion() {
+        final String sequence = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+        assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence, 0));
+        assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence, Integer.MAX_VALUE));
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testGetLoadState_returnsSuccess_pre19() {
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testGetLoadState_returnsSuccessIfLoadSuccess() throws InterruptedException {
+        final WaitingDataLoader metadataLoader = new WaitingDataLoader(true /*success*/);
+        final Config config = new TestConfig(metadataLoader);
+        EmojiCompat.reset(config);
+
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_LOADING);
+
+        metadataLoader.getLoaderLatch().countDown();
+        metadataLoader.getTestLatch().await();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testGetLoadState_returnsFailIfLoadFail() throws InterruptedException {
+        final WaitingDataLoader metadataLoader = new WaitingDataLoader(false/*fail*/);
+        final Config config = new TestConfig(metadataLoader);
+        EmojiCompat.reset(config);
+
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_LOADING);
+
+        metadataLoader.getLoaderLatch().countDown();
+        metadataLoader.getTestLatch().await();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_FAILED);
+    }
+
+    @Test
+    public void testUpdateEditorInfoAttrs_doesNotSetKeyIfNotInitialized() {
+        final EditorInfo editorInfo = new EditorInfo();
+        editorInfo.extras = new Bundle();
+
+        final WaitingDataLoader metadataLoader = new WaitingDataLoader();
+        final Config config = new TestConfig(metadataLoader);
+        EmojiCompat.reset(config);
+
+        EmojiCompat.get().updateEditorInfoAttrs(editorInfo);
+
+        final Bundle extras = editorInfo.extras;
+        assertFalse(extras.containsKey(EmojiCompat.EDITOR_INFO_METAVERSION_KEY));
+        assertFalse(extras.containsKey(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY));
+
+        metadataLoader.getLoaderLatch().countDown();
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testGetAssetSignature() {
+        final String signature = EmojiCompat.get().getAssetSignature();
+        assertTrue(signature.isEmpty());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testGetAssetSignature_api19() {
+        final String signature = EmojiCompat.get().getAssetSignature();
+        assertNotNull(signature);
+        assertFalse(signature.isEmpty());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testUpdateEditorInfoAttrs_setsKeysIfInitialized() {
+        final EditorInfo editorInfo = new EditorInfo();
+        editorInfo.extras = new Bundle();
+        Config config = new TestConfig().setReplaceAll(false);
+        EmojiCompat.reset(config);
+        EmojiCompat.get().updateEditorInfoAttrs(editorInfo);
+
+        final Bundle extras = editorInfo.extras;
+        assertTrue(extras.containsKey(EmojiCompat.EDITOR_INFO_METAVERSION_KEY));
+        assertTrue(extras.getInt(EmojiCompat.EDITOR_INFO_METAVERSION_KEY) > 0);
+        assertTrue(extras.containsKey(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY));
+        assertFalse(extras.getBoolean(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY));
+
+        config = new TestConfig().setReplaceAll(true);
+        EmojiCompat.reset(config);
+        EmojiCompat.get().updateEditorInfoAttrs(editorInfo);
+
+        assertTrue(extras.containsKey(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY));
+        assertTrue(extras.getBoolean(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY));
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testHandleDeleteSurroundingText_pre19() {
+        final TestString testString = new TestString(EMOJI_SINGLE_CODEPOINT);
+        final InputConnection inputConnection = mock(InputConnection.class);
+        final Editable editable = spy(new SpannableStringBuilder(testString.toString()));
+
+        Selection.setSelection(editable, testString.emojiEndIndex());
+
+        reset(editable);
+        reset(inputConnection);
+        verifyNoMoreInteractions(editable);
+        verifyNoMoreInteractions(inputConnection);
+
+        // try backwards delete 1 character
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(inputConnection, editable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 18)
+    public void testOnKeyDown_pre19() {
+        final TestString testString = new TestString(EMOJI_SINGLE_CODEPOINT);
+        final Editable editable = spy(new SpannableStringBuilder(testString.toString()));
+        Selection.setSelection(editable, testString.emojiEndIndex());
+        final KeyEvent event = del();
+
+        reset(editable);
+        verifyNoMoreInteractions(editable);
+
+        assertFalse(EmojiCompat.handleOnKeyDown(editable, event.getKeyCode(), event));
+    }
+
+    private void assertCodePointMatch(EmojiMapping emoji) {
+        assertCodePointMatch(emoji.id(), emoji.codepoints());
+    }
+
+    private void assertCodePointMatch(int id, int[] codepoints) {
+        TestString string = new TestString(codepoints);
+        CharSequence charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, hasEmojiAt(id, string.emojiStartIndex(), string.emojiEndIndex()));
+
+        // case where Emoji is in the middle of string
+        string = new TestString(codepoints).withPrefix().withSuffix();
+        charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, hasEmojiAt(id, string.emojiStartIndex(), string.emojiEndIndex()));
+
+        // case where Emoji is at the end of string
+        string = new TestString(codepoints).withSuffix();
+        charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, hasEmojiAt(id, string.emojiStartIndex(), string.emojiEndIndex()));
+    }
+
+    private void assertCodePointDoesNotMatch(int[] codepoints) {
+        TestString string = new TestString(codepoints);
+        CharSequence charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, not(hasEmoji()));
+
+        string = new TestString(codepoints).withSuffix().withPrefix();
+        charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, not(hasEmoji()));
+
+        string = new TestString(codepoints).withPrefix();
+        charSequence = EmojiCompat.get().process(string.toString());
+        assertThat(charSequence, not(hasEmoji()));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiKeyboardTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiKeyboardTest.java
new file mode 100644
index 0000000..1c647ce
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiKeyboardTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_WITH_ZWJ;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiAt;
+import static android.support.text.emoji.util.KeyboardUtil.del;
+import static android.support.text.emoji.util.KeyboardUtil.forwardDel;
+
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.Suppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
+import android.support.text.emoji.test.R;
+import android.support.text.emoji.util.KeyboardUtil;
+import android.support.text.emoji.util.TestString;
+import android.text.Editable;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+@Suppress
+public class EmojiKeyboardTest {
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
+            TestActivity.class);
+    private Instrumentation mInstrumentation;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testAppendWithSoftKeyboard() throws Exception {
+        TestActivity activity = mActivityRule.getActivity();
+        final EditText editText = (EditText) activity.findViewById(R.id.editText);
+        final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
+                .withSuffix();
+
+        final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+                mInstrumentation, editText);
+        KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection,
+                string.toString());
+        Editable editable = editText.getEditableText();
+
+        assertThat(editable, hasEmojiAt(EMOJI_WITH_ZWJ, string.emojiStartIndex(),
+                string.emojiEndIndex()));
+    }
+
+    @Test
+    public void testBackDeleteWithSoftKeyboard() throws Exception {
+        TestActivity activity = mActivityRule.getActivity();
+        final EditText editText = (EditText) activity.findViewById(R.id.editText);
+        final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
+                .withSuffix();
+        final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+                mInstrumentation, editText);
+        KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+        // assert that emoji is there
+        final Editable editable = editText.getEditableText();
+        assertThat(editable, hasEmoji());
+
+        // put selection at the end of emoji and back delete
+        KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+                string.emojiEndIndex());
+        KeyboardUtil.deleteSurroundingText(mInstrumentation, inputConnection, 1, 0);
+
+        assertThat(editable, not(hasEmoji()));
+    }
+
+    @Test
+    public void testForwardDeleteWithSoftKeyboard() throws Exception {
+        TestActivity activity = mActivityRule.getActivity();
+        final EditText editText = (EditText) activity.findViewById(R.id.editText);
+        final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
+                .withSuffix();
+        final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+                mInstrumentation, editText);
+        KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+        // assert that emoji is there
+        final Editable editable = editText.getEditableText();
+        assertThat(editable, hasEmoji());
+
+        // put selection at the begining of emoji and forward delete
+        KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+                string.emojiStartIndex());
+        KeyboardUtil.deleteSurroundingText(mInstrumentation, inputConnection, 0, 1);
+
+
+        assertThat(editable, not(hasEmoji()));
+    }
+
+    @Test
+    public void testBackDeleteWithHardwareKeyboard() throws Exception {
+        TestActivity activity = mActivityRule.getActivity();
+        final EditText editText = (EditText) activity.findViewById(R.id.editText);
+        final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
+                .withSuffix();
+        final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+                mInstrumentation, editText);
+        KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+        // assert that emoji is there
+        final Editable editable = editText.getEditableText();
+        assertThat(editable, hasEmoji());
+
+        // put selection at the end of emoji and back delete
+        KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+                string.emojiEndIndex());
+        mInstrumentation.sendKeySync(del());
+        mInstrumentation.waitForIdleSync();
+
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return not(hasEmoji()).matches(true);
+            }
+        });
+        assertThat(editable, not(hasEmoji()));
+    }
+
+    @Test
+    public void testForwardDeleteWithHardwareKeyboard() throws Exception {
+        TestActivity activity = mActivityRule.getActivity();
+        final EditText editText = (EditText) activity.findViewById(R.id.editText);
+        final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
+                .withSuffix();
+        final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+                mInstrumentation, editText);
+        KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+        // assert that emoji is there
+        final Editable editable = editText.getEditableText();
+        assertThat(editable, hasEmoji());
+
+        // put selection at the begining of emoji and forward delete
+        KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+                string.emojiStartIndex());
+        mInstrumentation.sendKeySync(forwardDel());
+        mInstrumentation.waitForIdleSync();
+
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return not(hasEmoji()).matches(true);
+            }
+        });
+        assertThat(editable, not(hasEmoji()));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiSpanInstrumentationTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiSpanInstrumentationTest.java
new file mode 100644
index 0000000..f30df8f
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiSpanInstrumentationTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_SINGLE_CODEPOINT;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.util.TestString;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.style.RelativeSizeSpan;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class EmojiSpanInstrumentationTest {
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
+            TestActivity.class);
+    private Instrumentation mInstrumentation;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testGetSize_withRelativeSizeSpan() {
+        final TestActivity activity = mActivityRule.getActivity();
+        final TextView textView = (TextView) activity.findViewById(
+                android.support.text.emoji.test.R.id.text);
+
+        // create a string with single codepoint emoji
+        final TestString string = new TestString(EMOJI_SINGLE_CODEPOINT).withPrefix().withSuffix();
+        final CharSequence charSequence = EmojiCompat.get().process(string.toString());
+        assertNotNull(charSequence);
+        assertThat(charSequence, hasEmojiCount(1));
+
+        final Spannable spanned = (Spannable) charSequence;
+        final EmojiSpan[] spans = spanned.getSpans(0, charSequence.length(), EmojiSpan.class);
+        final EmojiSpan span = spans[0];
+
+        // set text to the charSequence with the EmojiSpan
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                textView.setText(charSequence);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // record height of the default span
+        final int defaultHeight = span.getHeight();
+
+        // cover the charsequence with RelativeSizeSpan which will triple the size of the
+        // characters.
+        final float multiplier = 3.0f;
+        final RelativeSizeSpan sizeSpan = new RelativeSizeSpan(multiplier);
+        spanned.setSpan(sizeSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        // set the new text
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                textView.setText(charSequence);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // record the height measured after RelativeSizeSpan
+        final int heightWithRelativeSpan = span.getHeight();
+
+        // accept 1sp error rate.
+        final float delta = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+                mInstrumentation.getTargetContext().getResources().getDisplayMetrics());
+        assertEquals(defaultHeight * 3, heightWithRelativeSpan, delta);
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiSpanTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiSpanTest.java
new file mode 100644
index 0000000..816ab64
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiSpanTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextPaint;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class EmojiSpanTest {
+
+    @Before
+    public void setup() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Test
+    public void testGetSize() {
+        final short dimensionX = 18;
+        final short dimensionY = 20;
+        final int fontHeight = 10;
+        final float expectedRatio = fontHeight * 1.0f / dimensionY;
+        final TextPaint paint = mock(TextPaint.class);
+
+        // mock TextPaint to return test font metrics
+        when(paint.getFontMetricsInt(any(FontMetricsInt.class))).thenAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                final FontMetricsInt fontMetrics = (FontMetricsInt) invocation.getArguments()[0];
+                fontMetrics.ascent = 0;
+                fontMetrics.descent = -fontHeight;
+                return null;
+            }
+        });
+
+        final EmojiMetadata metadata = mock(EmojiMetadata.class);
+        when(metadata.getWidth()).thenReturn(dimensionX);
+        when(metadata.getHeight()).thenReturn(dimensionY);
+        final EmojiSpan span = new TypefaceEmojiSpan(metadata);
+
+        final int resultSize = span.getSize(paint, "", 0, 0, null);
+        assertEquals((int) (dimensionX * expectedRatio), resultSize);
+        assertEquals(expectedRatio, span.getRatio());
+        assertEquals((int) (dimensionX * expectedRatio), span.getWidth());
+        assertEquals((int) (dimensionY * expectedRatio), span.getHeight());
+    }
+
+    @Test
+    public void testBackgroundIndicator() {
+        // control the size of the emoji span
+        final EmojiMetadata metadata = mock(EmojiMetadata.class);
+        when(metadata.getWidth()).thenReturn((short) 10);
+        when(metadata.getHeight()).thenReturn((short) 10);
+
+        final EmojiSpan span = new TypefaceEmojiSpan(metadata);
+        final int spanWidth = span.getSize(mock(Paint.class), "", 0, 0, null);
+        // prepare parameters for draw() call
+        final Canvas canvas = mock(Canvas.class);
+        final float x = 10;
+        final int top = 15;
+        final int y = 20;
+        final int bottom = 30;
+
+        // verify the case where indicators are disabled
+        EmojiCompat.reset(TestConfigBuilder.config().setEmojiSpanIndicatorEnabled(false));
+        span.draw(canvas, "a", 0 /*start*/, 1 /*end*/, x, top, y, bottom, mock(Paint.class));
+
+        verify(canvas, times(0)).drawRect(eq(x), eq((float) top), eq(x + spanWidth),
+                eq((float) bottom), any(Paint.class));
+
+        // verify the case where indicators are enabled
+        EmojiCompat.reset(TestConfigBuilder.config().setEmojiSpanIndicatorEnabled(true));
+        reset(canvas);
+        span.draw(canvas, "a", 0 /*start*/, 1 /*end*/, x, top, y, bottom, mock(Paint.class));
+
+        verify(canvas, times(1)).drawRect(eq(x), eq((float) top), eq(x + spanWidth),
+                eq((float) bottom), any(Paint.class));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java b/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java
new file mode 100644
index 0000000..ce2b098
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java
@@ -0,0 +1,598 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji;
+
+import static android.content.res.AssetManager.ACCESS_BUFFER;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_FONT_NOT_FOUND;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_FONT_UNAVAILABLE;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_MALFORMED_QUERY;
+import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_OK;
+import static android.support.v4.provider.FontsContractCompat.FontFamilyResult.STATUS_OK;
+import static android.support.v4.provider.FontsContractCompat.FontFamilyResult.STATUS_WRONG_CERTIFICATES;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.provider.FontRequest;
+import android.support.v4.provider.FontsContractCompat.FontFamilyResult;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontRequestEmojiCompatConfigTest {
+    private static final int DEFAULT_TIMEOUT_MILLIS = 3000;
+    private Context mContext;
+    private FontRequest mFontRequest;
+    private FontRequestEmojiCompatConfig.FontProviderHelper mFontProviderHelper;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getContext();
+        mFontRequest = new FontRequest("authority", "package", "query",
+                new ArrayList<List<byte[]>>());
+        mFontProviderHelper = mock(FontRequestEmojiCompatConfig.FontProviderHelper.class);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_withNullContext() {
+        new FontRequestEmojiCompatConfig(null, mFontRequest);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructor_withNullFontRequest() {
+        new FontRequestEmojiCompatConfig(mContext, null);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_whenGetFontThrowsException() throws NameNotFoundException {
+        final Exception exception = new RuntimeException();
+        doThrow(exception).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext, mFontRequest,
+                mFontProviderHelper);
+
+        config.getMetadataRepoLoader().load(callback);
+        callback.await(DEFAULT_TIMEOUT_MILLIS);
+        verify(callback, times(1)).onFailed(same(exception));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_providerNotFound() throws NameNotFoundException {
+        doThrow(new NameNotFoundException()).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                mFontRequest, mFontProviderHelper);
+
+        config.getMetadataRepoLoader().load(callback);
+        callback.await(DEFAULT_TIMEOUT_MILLIS);
+
+        final ArgumentCaptor<Throwable> argumentCaptor = ArgumentCaptor.forClass(Throwable.class);
+        verify(callback, times(1)).onFailed(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue().getMessage(), containsString("provider not found"));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_wrongCertificate() throws NameNotFoundException {
+        verifyLoaderOnFailedCalled(STATUS_WRONG_CERTIFICATES, null /* fonts */,
+                "fetchFonts failed (" + STATUS_WRONG_CERTIFICATES + ")");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_fontNotFound() throws NameNotFoundException {
+        verifyLoaderOnFailedCalled(STATUS_OK,
+                getTestFontInfoWithInvalidPath(RESULT_CODE_FONT_NOT_FOUND),
+                "fetchFonts result is not OK. (" + RESULT_CODE_FONT_NOT_FOUND + ")");
+    }
+
+    @Test@SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_fontUnavailable() throws NameNotFoundException {
+        verifyLoaderOnFailedCalled(STATUS_OK,
+                getTestFontInfoWithInvalidPath(RESULT_CODE_FONT_UNAVAILABLE),
+                "fetchFonts result is not OK. (" + RESULT_CODE_FONT_UNAVAILABLE + ")");
+    }
+
+    @Test@SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_malformedQuery() throws NameNotFoundException {
+        verifyLoaderOnFailedCalled(STATUS_OK,
+                getTestFontInfoWithInvalidPath(RESULT_CODE_MALFORMED_QUERY),
+                "fetchFonts result is not OK. (" + RESULT_CODE_MALFORMED_QUERY + ")");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_resultNotFound() throws NameNotFoundException {
+        verifyLoaderOnFailedCalled(STATUS_OK, new FontInfo[] {},
+                "fetchFonts failed (empty result)");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_nullFontInfo() throws NameNotFoundException {
+        verifyLoaderOnFailedCalled(STATUS_OK, null /* fonts */,
+                "fetchFonts failed (empty result)");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_cannotLoadTypeface() throws NameNotFoundException {
+        // getTestFontInfoWithInvalidPath returns FontInfo with invalid path to file.
+        verifyLoaderOnFailedCalled(STATUS_OK,
+                getTestFontInfoWithInvalidPath(RESULT_CODE_OK),
+                "Unable to open file.");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_success() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final FontInfo[] fonts =  new FontInfo[] {
+                new FontInfo(Uri.fromFile(file), 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_OK)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                mFontRequest, mFontProviderHelper);
+
+        config.getMetadataRepoLoader().load(callback);
+        callback.await(DEFAULT_TIMEOUT_MILLIS);
+        verify(callback, times(1)).onLoaded(any(MetadataRepo.class));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_retryPolicy() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final FontInfo[] fonts =  new FontInfo[] {
+                new FontInfo(Uri.fromFile(file), 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_FONT_UNAVAILABLE)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final WaitingRetryPolicy retryPolicy = spy(new WaitingRetryPolicy(-1, 1));
+        final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                mFontRequest, mFontProviderHelper).setRetryPolicy(retryPolicy);
+
+        config.getMetadataRepoLoader().load(callback);
+        callback.await(DEFAULT_TIMEOUT_MILLIS);
+        verify(callback, never()).onLoaded(any(MetadataRepo.class));
+        verify(callback, times(1)).onFailed(any(Throwable.class));
+        verify(retryPolicy, times(1)).getRetryDelay();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_keepRetryingAndGiveUp() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final FontInfo[] fonts =  new FontInfo[] {
+                new FontInfo(Uri.fromFile(file), 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_FONT_UNAVAILABLE)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final WaitingRetryPolicy retryPolicy = spy(new WaitingRetryPolicy(500, 1));
+        final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                mFontRequest, mFontProviderHelper).setRetryPolicy(retryPolicy);
+
+        config.getMetadataRepoLoader().load(callback);
+        retryPolicy.await(DEFAULT_TIMEOUT_MILLIS);
+        verify(callback, never()).onLoaded(any(MetadataRepo.class));
+        verify(callback, never()).onFailed(any(Throwable.class));
+        verify(retryPolicy, atLeastOnce()).getRetryDelay();
+        retryPolicy.changeReturnValue(-1);
+        callback.await(DEFAULT_TIMEOUT_MILLIS);
+        verify(callback, never()).onLoaded(any(MetadataRepo.class));
+        verify(callback, times(1)).onFailed(any(Throwable.class));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_keepRetryingAndFail() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final Uri uri = Uri.fromFile(file);
+
+        final FontInfo[] fonts = new FontInfo[] {
+                new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_FONT_UNAVAILABLE)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final WaitingRetryPolicy retryPolicy = spy(new WaitingRetryPolicy(500, 1));
+
+        HandlerThread thread = new HandlerThread("testThread");
+        thread.start();
+        try {
+            Handler handler = new Handler(thread.getLooper());
+
+            final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                    mFontRequest, mFontProviderHelper).setHandler(handler)
+                    .setRetryPolicy(retryPolicy);
+
+            config.getMetadataRepoLoader().load(callback);
+            retryPolicy.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, never()).onLoaded(any(MetadataRepo.class));
+            verify(callback, never()).onFailed(any(Throwable.class));
+            verify(retryPolicy, atLeastOnce()).getRetryDelay();
+
+            // To avoid race condition, change the fetchFonts result on the handler thread.
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        final FontInfo[] fontsSuccess = new FontInfo[] {
+                                new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                                        false /* italic */, RESULT_CODE_FONT_NOT_FOUND)
+                        };
+
+                        doReturn(new FontFamilyResult(STATUS_OK, fontsSuccess)).when(
+                                mFontProviderHelper).fetchFonts(any(Context.class),
+                                any(FontRequest.class));
+                    } catch (NameNotFoundException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+
+            callback.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, never()).onLoaded(any(MetadataRepo.class));
+            verify(callback, times(1)).onFailed(any(Throwable.class));
+        } finally {
+            thread.quit();
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_keepRetryingAndSuccess() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final Uri uri = Uri.fromFile(file);
+
+        final FontInfo[] fonts = new FontInfo[]{
+                new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_FONT_UNAVAILABLE)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final WaitingRetryPolicy retryPolicy = spy(new WaitingRetryPolicy(500, 1));
+
+        HandlerThread thread = new HandlerThread("testThread");
+        thread.start();
+        try {
+            Handler handler = new Handler(thread.getLooper());
+
+            final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                    mFontRequest, mFontProviderHelper).setHandler(handler)
+                    .setRetryPolicy(retryPolicy);
+
+            config.getMetadataRepoLoader().load(callback);
+            retryPolicy.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, never()).onLoaded(any(MetadataRepo.class));
+            verify(callback, never()).onFailed(any(Throwable.class));
+            verify(retryPolicy, atLeastOnce()).getRetryDelay();
+
+            final FontInfo[] fontsSuccess = new FontInfo[]{
+                    new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                            false /* italic */, RESULT_CODE_OK)
+            };
+
+            // To avoid race condition, change the fetchFonts result on the handler thread.
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        doReturn(new FontFamilyResult(STATUS_OK, fontsSuccess)).when(
+                                mFontProviderHelper).fetchFonts(any(Context.class),
+                                any(FontRequest.class));
+                    } catch (NameNotFoundException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+
+            callback.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, times(1)).onLoaded(any(MetadataRepo.class));
+            verify(callback, never()).onFailed(any(Throwable.class));
+        } finally {
+            thread.quit();
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_ObserverNotifyAndSuccess() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final Uri uri = Uri.fromFile(file);
+        final FontInfo[] fonts = new FontInfo[]{
+                new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_FONT_UNAVAILABLE)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final WaitingRetryPolicy retryPolicy = spy(new WaitingRetryPolicy(500, 2));
+
+        HandlerThread thread = new HandlerThread("testThread");
+        thread.start();
+        try {
+            Handler handler = new Handler(thread.getLooper());
+            final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                    mFontRequest, mFontProviderHelper).setHandler(handler)
+                    .setRetryPolicy(retryPolicy);
+
+            ArgumentCaptor<ContentObserver> observerCaptor =
+                    ArgumentCaptor.forClass(ContentObserver.class);
+
+            config.getMetadataRepoLoader().load(callback);
+            retryPolicy.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, never()).onLoaded(any(MetadataRepo.class));
+            verify(callback, never()).onFailed(any(Throwable.class));
+            verify(retryPolicy, atLeastOnce()).getRetryDelay();
+            verify(mFontProviderHelper, times(1)).registerObserver(
+                    any(Context.class), eq(uri), observerCaptor.capture());
+
+            final FontInfo[] fontsSuccess = new FontInfo[]{
+                    new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                            false /* italic */, RESULT_CODE_OK)
+            };
+            doReturn(new FontFamilyResult(STATUS_OK, fontsSuccess)).when(
+                    mFontProviderHelper).fetchFonts(any(Context.class), any(FontRequest.class));
+
+            final ContentObserver observer = observerCaptor.getValue();
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    observer.onChange(false /* self change */, uri);
+                }
+            });
+
+            callback.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, times(1)).onLoaded(any(MetadataRepo.class));
+            verify(callback, never()).onFailed(any(Throwable.class));
+        } finally {
+            thread.quit();
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testLoad_ObserverNotifyAndFail() throws IOException, NameNotFoundException {
+        final File file = loadFont(mContext, "NotoColorEmojiCompat.ttf");
+        final Uri uri = Uri.fromFile(file);
+        final FontInfo[] fonts = new FontInfo[]{
+                new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                        false /* italic */, RESULT_CODE_FONT_UNAVAILABLE)
+        };
+        doReturn(new FontFamilyResult(STATUS_OK, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final WaitingRetryPolicy retryPolicy = spy(new WaitingRetryPolicy(500, 2));
+
+        HandlerThread thread = new HandlerThread("testThread");
+        thread.start();
+        try {
+            Handler handler = new Handler(thread.getLooper());
+            final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext,
+                    mFontRequest, mFontProviderHelper).setHandler(handler)
+                    .setRetryPolicy(retryPolicy);
+
+            ArgumentCaptor<ContentObserver> observerCaptor =
+                    ArgumentCaptor.forClass(ContentObserver.class);
+
+            config.getMetadataRepoLoader().load(callback);
+            retryPolicy.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, never()).onLoaded(any(MetadataRepo.class));
+            verify(callback, never()).onFailed(any(Throwable.class));
+            verify(retryPolicy, atLeastOnce()).getRetryDelay();
+            verify(mFontProviderHelper, times(1)).registerObserver(
+                    any(Context.class), eq(uri), observerCaptor.capture());
+
+            final FontInfo[] fontsSuccess = new FontInfo[]{
+                    new FontInfo(uri, 0 /* ttc index */, 400 /* weight */,
+                            false /* italic */, RESULT_CODE_FONT_NOT_FOUND)
+            };
+            doReturn(new FontFamilyResult(STATUS_OK, fontsSuccess)).when(
+                    mFontProviderHelper).fetchFonts(any(Context.class), any(FontRequest.class));
+
+            final ContentObserver observer = observerCaptor.getValue();
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    observer.onChange(false /* self change */, uri);
+                }
+            });
+
+            callback.await(DEFAULT_TIMEOUT_MILLIS);
+            verify(callback, never()).onLoaded(any(MetadataRepo.class));
+            verify(callback, times(1)).onFailed(any(Throwable.class));
+        } finally {
+            thread.quit();
+        }
+    }
+
+    private void verifyLoaderOnFailedCalled(final int statusCode,
+            final FontInfo[] fonts, String exceptionMessage) throws NameNotFoundException {
+        doReturn(new FontFamilyResult(statusCode, fonts)).when(mFontProviderHelper).fetchFonts(
+                any(Context.class), any(FontRequest.class));
+        final WaitingLoaderCallback callback = spy(new WaitingLoaderCallback());
+        final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(mContext, mFontRequest,
+                mFontProviderHelper);
+
+        config.getMetadataRepoLoader().load(callback);
+        callback.await(DEFAULT_TIMEOUT_MILLIS);
+
+        final ArgumentCaptor<Throwable> argumentCaptor = ArgumentCaptor.forClass(Throwable.class);
+        verify(callback, times(1)).onFailed(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue().getMessage(), containsString(exceptionMessage));
+    }
+
+    public static class WaitingRetryPolicy extends FontRequestEmojiCompatConfig.RetryPolicy {
+        private final CountDownLatch mLatch;
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private long mReturnValue;
+
+        public WaitingRetryPolicy(long returnValue, int callCount) {
+            mLatch = new CountDownLatch(callCount);
+            synchronized (mLock) {
+                mReturnValue = returnValue;
+            }
+        }
+
+        @Override
+        public long getRetryDelay() {
+            mLatch.countDown();
+            synchronized (mLock) {
+                return mReturnValue;
+            }
+        }
+
+        public void changeReturnValue(long value) {
+            synchronized (mLock) {
+                mReturnValue = value;
+            }
+        }
+
+        public void await(long timeoutMillis) {
+            try {
+                mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public static class WaitingLoaderCallback extends EmojiCompat.MetadataRepoLoaderCallback {
+        final CountDownLatch mLatch;
+
+        public WaitingLoaderCallback() {
+            mLatch = new CountDownLatch(1);
+        }
+
+        @Override
+        public void onLoaded(@NonNull MetadataRepo metadataRepo) {
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onFailed(@Nullable Throwable throwable) {
+            mLatch.countDown();
+        }
+
+        public void await(long timeoutMillis) {
+            try {
+                mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public static File loadFont(Context context, String fileName) {
+        File cacheFile = new File(context.getCacheDir(), fileName);
+        try {
+            copyToCacheFile(context, fileName, cacheFile);
+            return cacheFile;
+        } catch (IOException e) {
+            fail();
+        }
+        return null;
+    }
+
+    private static void copyToCacheFile(final Context context, final String assetPath,
+            final File cacheFile) throws IOException {
+        try (InputStream is = context.getAssets().open(assetPath, ACCESS_BUFFER);
+             FileOutputStream fos = new FileOutputStream(cacheFile, false)) {
+            byte[] buffer = new byte[1024];
+            int readLen;
+            while ((readLen = is.read(buffer)) != -1) {
+                fos.write(buffer, 0, readLen);
+            }
+        }
+    }
+
+    private FontInfo[] getTestFontInfoWithInvalidPath(int resultCode) {
+        return new FontInfo[] { new FontInfo(Uri.parse("file:///some/dummy/file"),
+                0 /* ttc index */, 400 /* weight */, false /* italic */, resultCode) };
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/HardDeleteTest.java b/emoji/core/tests/java/android/support/text/emoji/HardDeleteTest.java
new file mode 100644
index 0000000..58fe21d
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/HardDeleteTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_FLAG;
+import static android.support.text.emoji.util.Emoji.EMOJI_GENDER;
+import static android.support.text.emoji.util.Emoji.EMOJI_WITH_ZWJ;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+import static android.support.text.emoji.util.KeyboardUtil.altDel;
+import static android.support.text.emoji.util.KeyboardUtil.ctrlDel;
+import static android.support.text.emoji.util.KeyboardUtil.del;
+import static android.support.text.emoji.util.KeyboardUtil.fnDel;
+import static android.support.text.emoji.util.KeyboardUtil.forwardDel;
+import static android.support.text.emoji.util.KeyboardUtil.shiftDel;
+import static android.support.text.emoji.util.KeyboardUtil.zero;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.TestCase.assertEquals;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.util.TestString;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.view.KeyEvent;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+public class HardDeleteTest {
+
+    private TestString mTestString;
+    private Editable mEditable;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mTestString = new TestString(EMOJI_WITH_ZWJ).withPrefix().withSuffix();
+        mEditable = new SpannableStringBuilder(mTestString.toString());
+        EmojiCompat.get().process(mEditable);
+        assertThat(mEditable, hasEmojiCount(1));
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_whenKeyCodeIsNotDelOrForwardDel() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = zero();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withOtherModifiers() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = fnDel();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withAltModifier() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = altDel();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withCtrlModifier() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = ctrlDel();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withShiftModifier() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = shiftDel();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withSelectionLongerThanZeroLength() {
+        // when there is a selection which is longer than 0, it should not delete.
+        Selection.setSelection(mEditable, 0, mEditable.length());
+        final KeyEvent event = del();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withoutEmojiSpans() {
+        final Editable editable = new SpannableStringBuilder("abc");
+        Selection.setSelection(editable, 1);
+        final KeyEvent event = del();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_whenNoSpansBefore() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        final KeyEvent event = del();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_deletesEmoji() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = del();
+        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotForwardDeleteEmoji_withNoSpansAfter() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = forwardDel();
+        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_forwardDeletesEmoji() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        final KeyEvent event = forwardDel();
+        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_deletesEmoji_ifSelectionIsInSpanBoundaries() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex() + 1);
+        final KeyEvent delEvent = del();
+        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, delEvent.getKeyCode(), delEvent));
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_deletesEmoji_ifSelectionIsInSpanBoundaries_withForwardDel() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex() + 1);
+        final KeyEvent forwardDelEvent = forwardDel();
+        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, forwardDelEvent.getKeyCode(),
+                forwardDelEvent));
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testOnKeyDown_deletesOnlyEmojiBeforeTheCursor() {
+        // contains three emojis
+        mTestString = new TestString(EMOJI_FLAG)
+                .append(EMOJI_WITH_ZWJ)
+                .append(EMOJI_GENDER)
+                .withPrefix().withSuffix();
+        mEditable = new SpannableStringBuilder(mTestString.toString());
+        EmojiCompat.get().process(mEditable);
+
+        // put the cursor after the second emoji
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex()
+                + EMOJI_FLAG.charCount()
+                + EMOJI_WITH_ZWJ.charCount());
+
+        // delete
+        final KeyEvent forwardDelEvent = del();
+        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, forwardDelEvent.getKeyCode(),
+                forwardDelEvent));
+
+        assertThat(mEditable, hasEmojiCount(2));
+        assertThat(mEditable, hasEmoji(EMOJI_FLAG));
+        assertThat(mEditable, hasEmoji(EMOJI_GENDER));
+
+        assertEquals(new TestString(EMOJI_FLAG).append(EMOJI_GENDER)
+                .withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java b/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
new file mode 100644
index 0000000..ec6291d
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.TestConfigBuilder.TestConfig;
+import android.support.text.emoji.TestConfigBuilder.WaitingDataLoader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InitCallbackTest {
+
+    @Test
+    public void testRegisterInitCallback_callsSuccessCallback() {
+        final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
+
+        final EmojiCompat.Config config = TestConfigBuilder.config();
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+        emojiCompat.registerInitCallback(initCallback1);
+        emojiCompat.registerInitCallback(initCallback2);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback1, times(1)).onInitialized();
+        verify(initCallback2, times(1)).onInitialized();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testRegisterInitCallback_callsFailCallback() {
+        final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.MetadataRepoLoader loader = mock(EmojiCompat.MetadataRepoLoader.class);
+        doThrow(new RuntimeException("")).when(loader)
+                .load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+
+        final EmojiCompat.Config config = new TestConfig(loader);
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+        emojiCompat.registerInitCallback(initCallback1);
+        emojiCompat.registerInitCallback(initCallback2);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback1, times(1)).onFailed(nullable(Throwable.class));
+        verify(initCallback2, times(1)).onFailed(nullable(Throwable.class));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testRegisterInitCallback_callsFailCallback_whenOnFailCalledByLoader() {
+        final EmojiCompat.InitCallback initCallback = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.MetadataRepoLoader loader = new EmojiCompat.MetadataRepoLoader() {
+            @Override
+            public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+                loaderCallback.onFailed(new RuntimeException(""));
+            }
+        };
+
+        final EmojiCompat.Config config = new TestConfig(loader);
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+        emojiCompat.registerInitCallback(initCallback);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback, times(1)).onFailed(nullable(Throwable.class));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testRegisterInitCallback_callsFailCallback_whenMetadataRepoIsNull() {
+        final EmojiCompat.InitCallback initCallback = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.MetadataRepoLoader loader = new EmojiCompat.MetadataRepoLoader() {
+            @Override
+            public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+                loaderCallback.onLoaded(null);
+            }
+        };
+
+        final EmojiCompat.Config config = new TestConfig(loader);
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+        emojiCompat.registerInitCallback(initCallback);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback, times(1)).onFailed(nullable(Throwable.class));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testUnregisterInitCallback_doesNotInteractWithCallback()
+            throws InterruptedException {
+        // will be registered
+        final EmojiCompat.InitCallback callback = mock(EmojiCompat.InitCallback.class);
+        // will be registered, and then unregistered before metadata load is complete
+        final EmojiCompat.InitCallback callbackUnregister = mock(EmojiCompat.InitCallback.class);
+        // will be registered to config
+        final EmojiCompat.InitCallback callbackConfigUnregister = mock(
+                EmojiCompat.InitCallback.class);
+        // will be registered to config and then unregistered
+        final EmojiCompat.InitCallback callbackConfig = mock(EmojiCompat.InitCallback.class);
+
+        //make sure that loader does not load before unregister
+        final WaitingDataLoader metadataLoader = new WaitingDataLoader(false/*fail*/);
+        final EmojiCompat.Config config = new TestConfig(metadataLoader)
+                .registerInitCallback(callbackConfig)
+                .registerInitCallback(callbackConfigUnregister)
+                .unregisterInitCallback(callbackConfigUnregister);
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+        // register before metadata is loaded
+        emojiCompat.registerInitCallback(callbackUnregister);
+        emojiCompat.registerInitCallback(callback);
+
+        // unregister before metadata is loaded
+        emojiCompat.unregisterInitCallback(callbackUnregister);
+
+        // fire metadata loaded event
+        metadataLoader.getLoaderLatch().countDown();
+        metadataLoader.getTestLatch().await();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(callbackUnregister, times(0)).onFailed(any(Throwable.class));
+        verify(callbackConfigUnregister, times(0)).onFailed(nullable(Throwable.class));
+        verify(callback, times(1)).onFailed(nullable(Throwable.class));
+        verify(callbackConfig, times(1)).onFailed(nullable(Throwable.class));
+    }
+
+    @Test
+    public void testInitCallback_addedToConfigAndInstance_callsSuccess() {
+        final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
+        final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
+
+        final EmojiCompat.Config config = TestConfigBuilder.config()
+                .registerInitCallback(initCallback1);
+        final EmojiCompat emojiCompat = EmojiCompat.reset(config);
+        emojiCompat.registerInitCallback(initCallback2);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(initCallback1, times(1)).onInitialized();
+        verify(initCallback2, times(1)).onInitialized();
+    }
+
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/MetadataRepoTest.java b/emoji/core/tests/java/android/support/text/emoji/MetadataRepoTest.java
new file mode 100644
index 0000000..2e58d95
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/MetadataRepoTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.MetadataRepo.Node;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+public class MetadataRepoTest {
+
+    MetadataRepo mMetadataRepo;
+
+    @Before
+    public void clearResourceIndex() {
+        mMetadataRepo = new MetadataRepo();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testPut_withNullMetadata() {
+        mMetadataRepo.put(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPut_withEmptyKeys() {
+        mMetadataRepo.put(new TestEmojiMetadata(new int[0]));
+    }
+
+    @Test
+    public void testPut_withSingleCodePointMapping() {
+        final int[] codePoint = new int[]{1};
+        final TestEmojiMetadata metadata = new TestEmojiMetadata(codePoint);
+        mMetadataRepo.put(metadata);
+        assertSame(metadata, getNode(codePoint));
+    }
+
+    @Test
+    public void testPut_withMultiCodePointsMapping() {
+        final int[] codePoint = new int[]{1, 2, 3, 4};
+        final TestEmojiMetadata metadata = new TestEmojiMetadata(codePoint);
+        mMetadataRepo.put(metadata);
+        assertSame(metadata, getNode(codePoint));
+
+        assertEquals(null, getNode(new int[]{1}));
+        assertEquals(null, getNode(new int[]{1, 2}));
+        assertEquals(null, getNode(new int[]{1, 2, 3}));
+        assertEquals(null, getNode(new int[]{1, 2, 3, 5}));
+    }
+
+    @Test
+    public void testPut_sequentialCodePoints() {
+        final int[] codePoint1 = new int[]{1, 2, 3, 4};
+        final EmojiMetadata metadata1 = new TestEmojiMetadata(codePoint1);
+
+        final int[] codePoint2 = new int[]{1, 2, 3};
+        final EmojiMetadata metadata2 = new TestEmojiMetadata(codePoint2);
+
+        final int[] codePoint3 = new int[]{1, 2};
+        final EmojiMetadata metadata3 = new TestEmojiMetadata(codePoint3);
+
+        mMetadataRepo.put(metadata1);
+        mMetadataRepo.put(metadata2);
+        mMetadataRepo.put(metadata3);
+
+        assertSame(metadata1, getNode(codePoint1));
+        assertSame(metadata2, getNode(codePoint2));
+        assertSame(metadata3, getNode(codePoint3));
+
+        assertEquals(null, getNode(new int[]{1}));
+        assertEquals(null, getNode(new int[]{1, 2, 3, 4, 5}));
+    }
+
+    final EmojiMetadata getNode(final int[] codepoints) {
+        return getNode(mMetadataRepo.getRootNode(), codepoints, 0);
+    }
+
+    final EmojiMetadata getNode(Node node, final int[] codepoints, int start) {
+        if (codepoints.length < start) return null;
+        if (codepoints.length == start) return node.getData();
+
+        final Node childNode = node.get(codepoints[start]);
+        if (childNode == null) return null;
+        return getNode(childNode, codepoints, start + 1);
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java b/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java
new file mode 100644
index 0000000..1a25aa6
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_FLAG;
+import static android.support.text.emoji.util.Emoji.EMOJI_WITH_ZWJ;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.TestCase.assertEquals;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.annotation.SuppressLint;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.util.Emoji;
+import android.support.text.emoji.util.TestString;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.view.inputmethod.InputConnection;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+public class SoftDeleteTest {
+    private InputConnection mInputConnection;
+    private TestString mTestString;
+    private Editable mEditable;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mInputConnection = mock(InputConnection.class);
+        mTestString = new TestString(Emoji.EMOJI_WITH_ZWJ).withPrefix().withSuffix();
+        mEditable = new SpannableStringBuilder(mTestString.toString());
+        EmojiCompat.get().process(mEditable);
+        assertThat(mEditable, hasEmojiCount(1));
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+    }
+
+    @Test
+    public void testDelete_doesNotDelete_whenSelectionIsUndefined() {
+        // no selection is set on editable
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_doesNotDelete_whenThereIsSelectionLongerThanZero() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex(),
+                mTestString.emojiEndIndex() + 1);
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_withNullEditable() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, null,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_withNullInputConnection() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(null, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @SuppressLint("Range")
+    @Test
+    public void testDelete_withInvalidLength() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                -1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @SuppressLint("Range")
+    @Test
+    public void testDelete_withInvalidAfterLength() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, -1 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_backward() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        // backwards delete 1 character, it will delete the emoji
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_backward_inCodepoints() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        // backwards delete 1 character, it will delete the emoji
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, true /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_forward() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+
+        // forward delete 1 character, it will dele the emoji.
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, 1 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_forward_inCodepoints() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+
+        // forward delete 1 codepoint, it will delete the emoji.
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, 1 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_backward_doesNotDeleteWhenSelectionAtCharSequenceStart() {
+        // make sure selection at 0 does not do something weird for backward delete
+        Selection.setSelection(mEditable, 0);
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_forward_doesNotDeleteWhenSelectionAtCharSequenceEnd() {
+        // make sure selection at end does not do something weird for forward delete
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, 1 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, hasEmoji());
+        assertEquals(mTestString.toString(), mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_withMultipleCharacters() {
+        // prepare string as abc[emoji]def
+        mTestString = new TestString(EMOJI_FLAG);
+        mEditable = new SpannableStringBuilder("abc" + mTestString.toString() + "def");
+        EmojiCompat.get().process(mEditable);
+
+        // set the selection in the middle of emoji
+        Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
+
+        // delete 4 characters forward, 4 character backwards
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                4 /*beforeLength*/, 4 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals("af", mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_withMultipleCodepoints() {
+        // prepare string as abc[emoji]def
+        mTestString = new TestString(EMOJI_FLAG);
+        mEditable = new SpannableStringBuilder("abc" + mTestString.toString() + "def");
+        EmojiCompat.get().process(mEditable);
+
+        // set the selection in the middle of emoji
+        Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
+
+        // delete 3 codepoints forward, 3 codepoints backwards
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                3 /*beforeLength*/, 3 /*afterLength*/, true /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals("af", mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_withMultipleCharacters_withDeleteLengthLongerThanString() {
+        // prepare string as abc[emoji]def
+        mTestString = new TestString(EMOJI_FLAG);
+        mEditable = new SpannableStringBuilder("abc" + mTestString.toString() + "def");
+        EmojiCompat.get().process(mEditable);
+
+        // set the selection in the middle of emoji
+        Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
+
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                100 /*beforeLength*/, 100 /*afterLength*/, false /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals("", mEditable.toString());
+    }
+
+    @Test
+    public void testDelete_withMultipleCodepoints_withDeleteLengthLongerThanString() {
+        // prepare string as abc[emoji]def
+        mTestString = new TestString(EMOJI_FLAG);
+        mEditable = new SpannableStringBuilder("abc" + mTestString.toString() + "def");
+        EmojiCompat.get().process(mEditable);
+
+        // set the selection in the middle of emoji
+        Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
+
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                100 /*beforeLength*/, 100 /*afterLength*/, true /*inCodePoints*/));
+
+        assertThat(mEditable, not(hasEmoji()));
+        assertEquals("", mEditable.toString());
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/TestActivity.java b/emoji/core/tests/java/android/support/text/emoji/TestActivity.java
new file mode 100644
index 0000000..06dfcf9
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/TestActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.text.emoji.test.R;
+
+public class TestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_default);
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java b/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java
new file mode 100644
index 0000000..242d62b
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import static org.junit.Assert.fail;
+
+import android.content.res.AssetManager;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+
+import java.util.concurrent.CountDownLatch;
+
+public class TestConfigBuilder {
+    public static EmojiCompat.Config config() {
+        return new TestConfig().setReplaceAll(true);
+    }
+
+    public static class TestConfig extends EmojiCompat.Config {
+        TestConfig() {
+            super(new TestEmojiDataLoader());
+        }
+
+        TestConfig(final EmojiCompat.MetadataRepoLoader metadataLoader) {
+            super(metadataLoader);
+        }
+    }
+
+    public static class WaitingDataLoader implements EmojiCompat.MetadataRepoLoader {
+        private final CountDownLatch mLoaderLatch;
+        private final CountDownLatch mTestLatch;
+        private final boolean mSuccess;
+
+        public WaitingDataLoader(boolean success) {
+            mLoaderLatch = new CountDownLatch(1);
+            mTestLatch = new CountDownLatch(1);
+            mSuccess = success;
+        }
+
+        public WaitingDataLoader() {
+            this(true);
+        }
+
+        public CountDownLatch getLoaderLatch() {
+            return mLoaderLatch;
+        }
+
+        public CountDownLatch getTestLatch() {
+            return mTestLatch;
+        }
+
+        @Override
+        public void load(@NonNull final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mLoaderLatch.await();
+                        if (mSuccess) {
+                            loaderCallback.onLoaded(new MetadataRepo());
+                        } else {
+                            loaderCallback.onFailed(null);
+                        }
+
+                        mTestLatch.countDown();
+                    } catch (Throwable e) {
+                        fail();
+                    }
+                }
+            }).start();
+        }
+    }
+
+    public static class TestEmojiDataLoader implements EmojiCompat.MetadataRepoLoader {
+        static final Object sMetadataRepoLock = new Object();
+        // keep a static instance to in order not to slow down the tests
+        @GuardedBy("sMetadataRepoLock")
+        static volatile MetadataRepo sMetadataRepo;
+
+        TestEmojiDataLoader() {
+        }
+
+        @Override
+        public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+            if (sMetadataRepo == null) {
+                synchronized (sMetadataRepoLock) {
+                    if (sMetadataRepo == null) {
+                        try {
+                            final AssetManager assetManager =
+                                    InstrumentationRegistry.getContext().getAssets();
+                            sMetadataRepo = MetadataRepo.create(assetManager,
+                                    "NotoColorEmojiCompat.ttf");
+                        } catch (Throwable e) {
+                            loaderCallback.onFailed(e);
+                            throw new RuntimeException(e);
+                        }
+                    }
+                }
+            }
+
+            loaderCallback.onLoaded(sMetadataRepo);
+        }
+    }
+
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/TestEmojiMetadata.java b/emoji/core/tests/java/android/support/text/emoji/TestEmojiMetadata.java
new file mode 100644
index 0000000..6b61756
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/TestEmojiMetadata.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+public class TestEmojiMetadata extends EmojiMetadata {
+    private final int[] mCodePoints;
+    private int mId;
+
+    TestEmojiMetadata(int[] codePoints, int id) {
+        super(null, 0);
+        mCodePoints = codePoints;
+        mId = id;
+    }
+
+    TestEmojiMetadata(int[] codePoints) {
+        this(codePoints, 0);
+    }
+
+    @Override
+    public int getId() {
+        return mId;
+    }
+
+    @Override
+    public int getCodepointAt(int index) {
+        return mCodePoints[index];
+    }
+
+    @Override
+    public int getCodepointsLength() {
+        return mCodePoints.length;
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/UninitializedStateTest.java b/emoji/core/tests/java/android/support/text/emoji/UninitializedStateTest.java
new file mode 100644
index 0000000..4f8fa58
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/UninitializedStateTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji;
+
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.TestConfigBuilder.TestConfig;
+import android.support.text.emoji.TestConfigBuilder.WaitingDataLoader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+public class UninitializedStateTest {
+
+    private WaitingDataLoader mWaitingDataLoader;
+
+    @Before
+    public void setup() {
+        mWaitingDataLoader = new WaitingDataLoader(true);
+        final EmojiCompat.Config config = new TestConfig(mWaitingDataLoader);
+        EmojiCompat.reset(config);
+    }
+
+    @After
+    public void after() {
+        mWaitingDataLoader.getLoaderLatch().countDown();
+        mWaitingDataLoader.getTestLatch().countDown();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testHasEmojiGlyph() {
+        EmojiCompat.get().hasEmojiGlyph("anystring");
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testHasEmojiGlyph_withMetadataVersion() {
+        EmojiCompat.get().hasEmojiGlyph("anystring", 1);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testProcess() {
+        EmojiCompat.get().process("anystring");
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testProcess_withStartEnd() {
+        EmojiCompat.get().process("anystring", 1, 2);
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/util/Emoji.java b/emoji/core/tests/java/android/support/text/emoji/util/Emoji.java
new file mode 100644
index 0000000..d38e580
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/util/Emoji.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.util;
+
+import android.support.annotation.NonNull;
+
+public class Emoji {
+
+    public static final int CHAR_KEYCAP = 0x20E3;
+    public static final int CHAR_DIGIT = 0x0039;
+    public static final int CHAR_ZWJ = 0x200D;
+    public static final int CHAR_VS_EMOJI = 0xFE0f;
+    public static final int CHAR_VS_TEXT = 0xFE0E;
+    public static final int CHAR_FITZPATRICK = 0x1F3FE;
+    public static final int CHAR_FITZPATRICK_TYPE_1 = 0x1F3fB;
+    public static final int CHAR_DEFAULT_TEXT_STYLE = 0x26F9;
+    public static final int CHAR_DEFAULT_EMOJI_STYLE = 0x1f3A2;
+    public static final int CHAR_FEMALE_SIGN = 0x2640;
+    public static final int CHAR_MAN = 0x1F468;
+    public static final int CHAR_HEART = 0x2764;
+    public static final int CHAR_KISS = 0x1F48B;
+    public static final int CHAR_REGIONAL_SYMBOL = 0x1F1E8;
+    public static final int CHAR_ASTERISK = 0x002A;
+
+    public static final EmojiMapping EMOJI_SINGLE_CODEPOINT = new EmojiMapping(
+            new int[]{CHAR_DEFAULT_EMOJI_STYLE}, 0xF01B4);
+
+    public static final EmojiMapping EMOJI_WITH_ZWJ = new EmojiMapping(
+            new int[]{CHAR_MAN, CHAR_ZWJ, CHAR_HEART, CHAR_VS_EMOJI, CHAR_ZWJ, CHAR_KISS, CHAR_ZWJ,
+                    CHAR_MAN}, 0xF051F);
+
+    public static final EmojiMapping EMOJI_GENDER = new EmojiMapping(new int[]{
+            CHAR_DEFAULT_TEXT_STYLE, CHAR_VS_EMOJI, CHAR_ZWJ, CHAR_FEMALE_SIGN}, 0xF0950);
+
+    public static final EmojiMapping EMOJI_FLAG = new EmojiMapping(
+            new int[]{CHAR_REGIONAL_SYMBOL, CHAR_REGIONAL_SYMBOL}, 0xF03A0);
+
+    public static final EmojiMapping EMOJI_GENDER_WITHOUT_VS = new EmojiMapping(
+            new int[]{CHAR_DEFAULT_TEXT_STYLE, CHAR_ZWJ, CHAR_FEMALE_SIGN}, 0xF0950);
+
+    public static final EmojiMapping DEFAULT_TEXT_STYLE = new EmojiMapping(
+            new int[]{CHAR_DEFAULT_TEXT_STYLE, CHAR_VS_EMOJI}, 0xF04C6);
+
+    public static final EmojiMapping EMOJI_REGIONAL_SYMBOL = new EmojiMapping(
+            new int[]{CHAR_REGIONAL_SYMBOL}, 0xF0025);
+
+    public static final EmojiMapping EMOJI_UNKNOWN_FLAG = new EmojiMapping(
+            new int[]{0x1F1FA, 0x1F1F3}, 0xF0599);
+
+    public static final EmojiMapping EMOJI_DIGIT_ES = new EmojiMapping(
+            new int[]{CHAR_DIGIT, CHAR_VS_EMOJI}, 0xF0340);
+
+    public static final EmojiMapping EMOJI_DIGIT_KEYCAP = new EmojiMapping(
+            new int[]{CHAR_DIGIT, CHAR_KEYCAP}, 0xF0377);
+
+    public static final EmojiMapping EMOJI_DIGIT_ES_KEYCAP = new EmojiMapping(
+            new int[]{CHAR_DIGIT, CHAR_VS_EMOJI, CHAR_KEYCAP}, 0xF0377);
+
+    public static final EmojiMapping EMOJI_ASTERISK_KEYCAP = new EmojiMapping(
+            new int[]{CHAR_ASTERISK, CHAR_VS_EMOJI, CHAR_KEYCAP}, 0xF051D);
+
+    public static final EmojiMapping EMOJI_SKIN_MODIFIER = new EmojiMapping(
+            new int[]{CHAR_MAN, CHAR_FITZPATRICK}, 0xF0603);
+
+    public static final EmojiMapping EMOJI_SKIN_MODIFIER_TYPE_ONE = new EmojiMapping(
+            new int[]{CHAR_MAN, CHAR_FITZPATRICK_TYPE_1}, 0xF0606);
+
+    public static final EmojiMapping EMOJI_SKIN_MODIFIER_WITH_VS = new EmojiMapping(
+            new int[]{CHAR_MAN, CHAR_VS_EMOJI, CHAR_FITZPATRICK_TYPE_1}, 0xF0606);
+
+    public static class EmojiMapping {
+        private final int[] mCodepoints;
+        private final int mId;
+
+        private EmojiMapping(@NonNull final int[] codepoints, final int id) {
+            mCodepoints = codepoints;
+            mId = id;
+        }
+
+        public final int[] codepoints() {
+            return mCodepoints;
+        }
+
+        public final int id() {
+            return mId;
+        }
+
+        public final int charCount() {
+            int count = 0;
+            for (int i = 0; i < mCodepoints.length; i++) {
+                count += Character.charCount(mCodepoints[i]);
+            }
+            return count;
+        }
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java b/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java
new file mode 100644
index 0000000..2915acd
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.util;
+
+import static org.mockito.Matchers.argThat;
+
+import android.support.text.emoji.EmojiSpan;
+import android.text.Spanned;
+import android.text.TextUtils;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.mockito.ArgumentMatcher;
+
+/**
+ * Utility class that includes matchers specific to emojis and EmojiSpans.
+ */
+public class EmojiMatcher {
+
+    public static Matcher<CharSequence> hasEmojiAt(final int id, final int start,
+            final int end) {
+        return new EmojiResourceMatcher(id, start, end);
+    }
+
+    public static Matcher<CharSequence> hasEmojiAt(final Emoji.EmojiMapping emojiMapping,
+            final int start, final int end) {
+        return new EmojiResourceMatcher(emojiMapping.id(), start, end);
+    }
+
+    public static Matcher<CharSequence> hasEmojiAt(final int start, final int end) {
+        return new EmojiResourceMatcher(-1, start, end);
+    }
+
+    public static Matcher<CharSequence> hasEmoji(final int id) {
+        return new EmojiResourceMatcher(id, -1, -1);
+    }
+
+    public static Matcher<CharSequence> hasEmoji(final Emoji.EmojiMapping emojiMapping) {
+        return new EmojiResourceMatcher(emojiMapping.id(), -1, -1);
+    }
+
+    public static Matcher<CharSequence> hasEmoji() {
+        return new EmojiSpanMatcher();
+    }
+
+    public static Matcher<CharSequence> hasEmojiCount(final int count) {
+        return new EmojiCountMatcher(count);
+    }
+
+    public static <T extends CharSequence> T sameCharSequence(final T expected) {
+        return argThat(new ArgumentMatcher<T>() {
+            @Override
+            public boolean matches(T o) {
+                if (o instanceof CharSequence) {
+                    return TextUtils.equals(expected, o);
+                }
+                return false;
+            }
+
+            @Override
+            public String toString() {
+                return "doesn't match " + expected;
+            }
+        });
+    }
+
+    private static class EmojiSpanMatcher extends TypeSafeMatcher<CharSequence> {
+
+        private EmojiSpan[] mSpans;
+
+        EmojiSpanMatcher() {
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("should have EmojiSpans");
+        }
+
+        @Override
+        protected void describeMismatchSafely(final CharSequence charSequence,
+                Description mismatchDescription) {
+            mismatchDescription.appendText(" has no EmojiSpans");
+        }
+
+        @Override
+        protected boolean matchesSafely(final CharSequence charSequence) {
+            if (charSequence == null) return false;
+            if (!(charSequence instanceof Spanned)) return false;
+            mSpans = ((Spanned) charSequence).getSpans(0, charSequence.length(), EmojiSpan.class);
+            return mSpans.length != 0;
+        }
+    }
+
+    private static class EmojiCountMatcher extends TypeSafeMatcher<CharSequence> {
+
+        private final int mCount;
+        private EmojiSpan[] mSpans;
+
+        EmojiCountMatcher(final int count) {
+            mCount = count;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("should have ").appendValue(mCount).appendText(" EmojiSpans");
+        }
+
+        @Override
+        protected void describeMismatchSafely(final CharSequence charSequence,
+                Description mismatchDescription) {
+            mismatchDescription.appendText(" has ");
+            if (mSpans == null) {
+                mismatchDescription.appendValue("no");
+            } else {
+                mismatchDescription.appendValue(mSpans.length);
+            }
+
+            mismatchDescription.appendText(" EmojiSpans");
+        }
+
+        @Override
+        protected boolean matchesSafely(final CharSequence charSequence) {
+            if (charSequence == null) return false;
+            if (!(charSequence instanceof Spanned)) return false;
+            mSpans = ((Spanned) charSequence).getSpans(0, charSequence.length(), EmojiSpan.class);
+            return mSpans.length == mCount;
+        }
+    }
+
+    private static class EmojiResourceMatcher extends TypeSafeMatcher<CharSequence> {
+        private static final int ERR_NONE = 0;
+        private static final int ERR_SPANNABLE_NULL = 1;
+        private static final int ERR_NO_SPANS = 2;
+        private static final int ERR_WRONG_INDEX = 3;
+        private final int mResId;
+        private final int mStart;
+        private final int mEnd;
+        private int mError = ERR_NONE;
+        private int mActualStart = -1;
+        private int mActualEnd = -1;
+
+        EmojiResourceMatcher(int resId, int start, int end) {
+            mResId = resId;
+            mStart = start;
+            mEnd = end;
+        }
+
+        @Override
+        public void describeTo(final Description description) {
+            if (mResId == -1) {
+                description.appendText("should have EmojiSpan at ")
+                        .appendValue("[" + mStart + "," + mEnd + "]");
+            } else if (mStart == -1 && mEnd == -1) {
+                description.appendText("should have EmojiSpan with resource id ")
+                        .appendValue(Integer.toHexString(mResId));
+            } else {
+                description.appendText("should have EmojiSpan with resource id ")
+                        .appendValue(Integer.toHexString(mResId))
+                        .appendText(" at ")
+                        .appendValue("[" + mStart + "," + mEnd + "]");
+            }
+        }
+
+        @Override
+        protected void describeMismatchSafely(final CharSequence charSequence,
+                Description mismatchDescription) {
+            int offset = 0;
+            mismatchDescription.appendText("[");
+            while (offset < charSequence.length()) {
+                int codepoint = Character.codePointAt(charSequence, offset);
+                mismatchDescription.appendText(Integer.toHexString(codepoint));
+                offset += Character.charCount(codepoint);
+                if (offset < charSequence.length()) {
+                    mismatchDescription.appendText(",");
+                }
+            }
+            mismatchDescription.appendText("]");
+
+            switch (mError) {
+                case ERR_NO_SPANS:
+                    mismatchDescription.appendText(" had no spans");
+                    break;
+                case ERR_SPANNABLE_NULL:
+                    mismatchDescription.appendText(" was null");
+                    break;
+                case ERR_WRONG_INDEX:
+                    mismatchDescription.appendText(" had Emoji at ")
+                            .appendValue("[" + mActualStart + "," + mActualEnd + "]");
+                    break;
+                default:
+                    mismatchDescription.appendText(" does not have an EmojiSpan with given "
+                            + "resource id ");
+            }
+        }
+
+        @Override
+        protected boolean matchesSafely(final CharSequence charSequence) {
+            if (charSequence == null) {
+                mError = ERR_SPANNABLE_NULL;
+                return false;
+            }
+
+            if (!(charSequence instanceof Spanned)) {
+                mError = ERR_NO_SPANS;
+                return false;
+            }
+
+            Spanned spanned = (Spanned) charSequence;
+            final EmojiSpan[] spans = spanned.getSpans(0, charSequence.length(), EmojiSpan.class);
+
+            if (spans.length == 0) {
+                mError = ERR_NO_SPANS;
+                return false;
+            }
+
+            if (mStart == -1 && mEnd == -1) {
+                for (int index = 0; index < spans.length; index++) {
+                    if (mResId == spans[index].getId()) {
+                        return true;
+                    }
+                }
+                return false;
+            } else {
+                for (int index = 0; index < spans.length; index++) {
+                    if (mResId == -1 || mResId == spans[index].getId()) {
+                        mActualStart = spanned.getSpanStart(spans[index]);
+                        mActualEnd = spanned.getSpanEnd(spans[index]);
+                        if (mActualStart == mStart && mActualEnd == mEnd) {
+                            return true;
+                        }
+                    }
+                }
+
+                if (mActualStart != -1 && mActualEnd != -1) {
+                    mError = ERR_WRONG_INDEX;
+                }
+
+                return false;
+            }
+        }
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java b/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java
new file mode 100644
index 0000000..0c89b2d
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.util;
+
+import android.app.Instrumentation;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.method.QwertyKeyListener;
+import android.text.method.TextKeyListener;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.TextView;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Utility class for KeyEvents
+ */
+public class KeyboardUtil {
+    private static final int ALT = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON;
+    private static final int CTRL = KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON;
+    private static final int SHIFT = KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON;
+    private static final int FN = KeyEvent.META_FUNCTION_ON;
+
+    public static KeyEvent zero() {
+        return keyEvent(KeyEvent.KEYCODE_0);
+    }
+
+    public static KeyEvent del() {
+        return keyEvent(KeyEvent.KEYCODE_DEL);
+    }
+
+    public static KeyEvent altDel() {
+        return keyEvent(KeyEvent.KEYCODE_DEL, ALT);
+    }
+
+    public static KeyEvent ctrlDel() {
+        return keyEvent(KeyEvent.KEYCODE_DEL, CTRL);
+    }
+
+    public static KeyEvent shiftDel() {
+        return keyEvent(KeyEvent.KEYCODE_DEL, SHIFT);
+    }
+
+    public static KeyEvent fnDel() {
+        return keyEvent(KeyEvent.KEYCODE_DEL, FN);
+    }
+
+    public static KeyEvent forwardDel() {
+        return keyEvent(KeyEvent.KEYCODE_FORWARD_DEL);
+    }
+
+    public static KeyEvent keyEvent(int keycode, int metaState) {
+        final long currentTime = System.currentTimeMillis();
+        return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode, 0, metaState);
+    }
+
+    public static KeyEvent keyEvent(int keycode) {
+        final long currentTime = System.currentTimeMillis();
+        return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode, 0);
+    }
+
+    public static void setComposingTextInBatch(final Instrumentation instrumentation,
+            final InputConnection inputConnection, final CharSequence text)
+            throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                inputConnection.beginBatchEdit();
+                inputConnection.setComposingText(text, 1);
+                inputConnection.endBatchEdit();
+                latch.countDown();
+            }
+        });
+
+        latch.await();
+        instrumentation.waitForIdleSync();
+    }
+
+    public static void deleteSurroundingText(final Instrumentation instrumentation,
+            final InputConnection inputConnection, final int before, final int after)
+            throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                inputConnection.beginBatchEdit();
+                inputConnection.deleteSurroundingText(before, after);
+                inputConnection.endBatchEdit();
+                latch.countDown();
+            }
+        });
+        latch.await();
+        instrumentation.waitForIdleSync();
+    }
+
+    public static void setSelection(Instrumentation instrumentation, final Spannable spannable,
+            final int start) throws InterruptedException {
+        setSelection(instrumentation, spannable, start, start);
+    }
+
+    public static void setSelection(Instrumentation instrumentation, final Spannable spannable,
+            final int start, final int end) throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                Selection.setSelection(spannable, start, end);
+                latch.countDown();
+            }
+        });
+        latch.await();
+        instrumentation.waitForIdleSync();
+    }
+
+    public static InputConnection initTextViewForSimulatedIme(Instrumentation instrumentation,
+            final TextView textView) throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                textView.setKeyListener(
+                        QwertyKeyListener.getInstance(false, TextKeyListener.Capitalize.NONE));
+                textView.setText("", TextView.BufferType.EDITABLE);
+                latch.countDown();
+            }
+        });
+        latch.await();
+        instrumentation.waitForIdleSync();
+        return textView.onCreateInputConnection(new EditorInfo());
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/util/TestString.java b/emoji/core/tests/java/android/support/text/emoji/util/TestString.java
new file mode 100644
index 0000000..8f2331e
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/util/TestString.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class used to create strings with emojis during tests.
+ */
+public class TestString {
+
+    private static final List<Integer> EMPTY_LIST = new ArrayList<>();
+
+    private static final String EXTRA = "ab";
+    private final List<Integer> mCodePoints;
+    private String mString;
+    private final String mValue;
+    private boolean mHasSuffix;
+    private boolean mHasPrefix;
+
+    public TestString(int... codePoints) {
+        if (codePoints.length == 0) {
+            mCodePoints = EMPTY_LIST;
+        } else {
+            mCodePoints = new ArrayList<>();
+            append(codePoints);
+        }
+        mValue = null;
+    }
+
+    public TestString(Emoji.EmojiMapping emojiMapping) {
+        this(emojiMapping.codepoints());
+    }
+
+    public TestString(String string) {
+        mCodePoints = EMPTY_LIST;
+        mValue = string;
+    }
+
+    public TestString append(int... codePoints) {
+        for (int i = 0; i < codePoints.length; i++) {
+            mCodePoints.add(codePoints[i]);
+        }
+        return this;
+    }
+
+    public TestString append(Emoji.EmojiMapping emojiMapping) {
+        return append(emojiMapping.codepoints());
+    }
+
+    public TestString withSuffix() {
+        mHasSuffix = true;
+        return this;
+    }
+
+    public TestString withPrefix() {
+        mHasPrefix = true;
+        return this;
+    }
+
+    @SuppressWarnings("ForLoopReplaceableByForEach")
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        if (mHasPrefix) {
+            builder.append(EXTRA);
+        }
+
+        for (int index = 0; index < mCodePoints.size(); index++) {
+            builder.append(Character.toChars(mCodePoints.get(index)));
+        }
+
+        if (mValue != null) {
+            builder.append(mValue);
+        }
+
+        if (mHasSuffix) {
+            builder.append(EXTRA);
+        }
+        mString = builder.toString();
+        return mString;
+    }
+
+    public int emojiStartIndex() {
+        if (mHasPrefix) return EXTRA.length();
+        return 0;
+    }
+
+    public int emojiEndIndex() {
+        if (mHasSuffix) return mString.lastIndexOf(EXTRA);
+        return mString.length();
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java
new file mode 100644
index 0000000..d2bd722
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextWatcher;
+import android.text.method.KeyListener;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(maxSdkVersion = 18)
+public class EmojiEditTextHelperPre19Test {
+    EmojiEditTextHelper mEmojiEditTextHelper;
+
+    @Before
+    public void setup() {
+        final EditText editText = mock(EditText.class);
+        mEmojiEditTextHelper = new EmojiEditTextHelper(editText);
+        verifyNoMoreInteractions(editText);
+    }
+
+    @Test
+    public void testGetKeyListener_returnsSameKeyListener() {
+        final KeyListener param = mock(KeyListener.class);
+        final KeyListener keyListener = mEmojiEditTextHelper.getKeyListener(
+                param);
+
+        assertSame(param, keyListener);
+    }
+
+    @Test
+    public void testGetOnCreateInputConnection_returnsSameInputConnection() {
+        final InputConnection param = mock(InputConnection.class);
+        final InputConnection inputConnection = mEmojiEditTextHelper.onCreateInputConnection(param,
+                null);
+
+        assertSame(param, inputConnection);
+    }
+
+    @Test
+    public void testDoesNotAttachTextWatcher() {
+        final EditText editText = mock(EditText.class);
+
+        mEmojiEditTextHelper = new EmojiEditTextHelper(editText);
+
+        verify(editText, times(0)).addTextChangedListener(any(TextWatcher.class));
+    }
+
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java
new file mode 100644
index 0000000..efc7ca0
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.TargetApi;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.text.TextWatcher;
+import android.text.method.KeyListener;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class EmojiEditTextHelperTest {
+    EmojiEditTextHelper mEmojiEditTextHelper;
+    EditText mEditText;
+
+    @Before
+    public void setup() {
+        EmojiCompat.reset(mock(EmojiCompat.class));
+        mEditText = new EditText(InstrumentationRegistry.getTargetContext());
+        mEmojiEditTextHelper = new EmojiEditTextHelper(mEditText);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testGetKeyListener_withNull_throwsException() {
+        mEmojiEditTextHelper.getKeyListener(null);
+    }
+
+    @Test
+    public void testGetKeyListener_returnsEmojiKeyListener() {
+        final KeyListener keyListener = mEmojiEditTextHelper.getKeyListener(
+                mock(KeyListener.class));
+
+        assertThat(keyListener, instanceOf(EmojiKeyListener.class));
+    }
+
+    @Test
+    public void testGetKeyListener_doesNotCreateNewInstance() {
+        KeyListener mockKeyListener = mock(KeyListener.class);
+        final KeyListener keyListener1 = mEmojiEditTextHelper.getKeyListener(mockKeyListener);
+        final KeyListener keyListener2 = mEmojiEditTextHelper.getKeyListener(keyListener1);
+        assertSame(keyListener1, keyListener2);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testGetOnCreateInputConnection_withNull_throwsException() {
+        mEmojiEditTextHelper.onCreateInputConnection(null, null);
+    }
+
+    @Test
+    public void testGetOnCreateInputConnection_returnsEmojiInputConnection() {
+        final InputConnection inputConnection = mEmojiEditTextHelper.onCreateInputConnection(
+                mock(InputConnection.class), null);
+
+        assertThat(inputConnection, instanceOf(EmojiInputConnection.class));
+    }
+
+    @Test
+    public void testGetOnCreateInputConnection_doesNotCreateNewInstance() {
+        final InputConnection ic1 = mEmojiEditTextHelper.onCreateInputConnection(
+                mock(InputConnection.class), null);
+        final InputConnection ic2 = mEmojiEditTextHelper.onCreateInputConnection(ic1, null);
+
+        assertSame(ic1, ic2);
+    }
+
+    @Test
+    public void testAttachesTextWatcher() {
+        mEditText = mock(EditText.class);
+        mEmojiEditTextHelper = new EmojiEditTextHelper(mEditText);
+
+        final ArgumentCaptor<TextWatcher> argumentCaptor = ArgumentCaptor.forClass(
+                TextWatcher.class);
+
+        verify(mEditText, times(1)).addTextChangedListener(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue(), instanceOf(EmojiTextWatcher.class));
+    }
+
+    @Test
+    public void testSetMaxCount() {
+        mEditText = mock(EditText.class);
+        mEmojiEditTextHelper = new EmojiEditTextHelper(mEditText);
+        // capture TextWatcher
+        final ArgumentCaptor<TextWatcher> argumentCaptor = ArgumentCaptor.forClass(
+                TextWatcher.class);
+        verify(mEditText, times(1)).addTextChangedListener(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue(), instanceOf(EmojiTextWatcher.class));
+        final EmojiTextWatcher emojiTextWatcher = (EmojiTextWatcher) argumentCaptor.getValue();
+
+        mEmojiEditTextHelper.setMaxEmojiCount(1);
+
+        assertEquals(1, emojiTextWatcher.getMaxEmojiCount());
+    }
+
+    @Test
+    public void testSetEmojiReplaceStrategy() {
+        mEditText = mock(EditText.class);
+        mEmojiEditTextHelper = new EmojiEditTextHelper(mEditText);
+
+        //assert the default value
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_DEFAULT,
+                mEmojiEditTextHelper.getEmojiReplaceStrategy());
+
+        // capture TextWatcher
+        final ArgumentCaptor<TextWatcher> argumentCaptor = ArgumentCaptor.forClass(
+                TextWatcher.class);
+        verify(mEditText, times(1)).addTextChangedListener(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue(), instanceOf(EmojiTextWatcher.class));
+        final EmojiTextWatcher emojiTextWatcher = (EmojiTextWatcher) argumentCaptor.getValue();
+
+        mEmojiEditTextHelper.setEmojiReplaceStrategy(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT);
+
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT,
+                mEmojiEditTextHelper.getEmojiReplaceStrategy());
+
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT,
+                emojiTextWatcher.getEmojiReplaceStrategy());
+    }
+
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextTest.java
new file mode 100644
index 0000000..e4b452c
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_SINGLE_CODEPOINT;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.TestActivity;
+import android.support.text.emoji.TestConfigBuilder;
+import android.support.text.emoji.test.R;
+import android.support.text.emoji.util.TestString;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiEditTextTest {
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
+            TestActivity.class);
+    private Instrumentation mInstrumentation;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testInflateWithMaxEmojiCount() {
+        final TestActivity activity = mActivityRule.getActivity();
+        final EmojiEditText editText = activity.findViewById(R.id.editTextWithMaxCount);
+
+        // value set in XML
+        assertEquals(5, editText.getMaxEmojiCount());
+
+        // set max emoji count
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setMaxEmojiCount(1);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(1, editText.getMaxEmojiCount());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testSetMaxCount() {
+        final TestActivity activity = mActivityRule.getActivity();
+        final EmojiEditText editText = activity.findViewById(R.id.editTextWithMaxCount);
+
+        // set max emoji count to 1 and set text with 2 emojis
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setMaxEmojiCount(1);
+                final String string = new TestString(EMOJI_SINGLE_CODEPOINT).append(
+                        EMOJI_SINGLE_CODEPOINT).toString();
+                editText.setText(string);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertThat(editText.getText(), hasEmojiCount(1));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditableFactoryTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditableFactoryTest.java
new file mode 100644
index 0000000..d6e0fa8
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditableFactoryTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.annotation.SuppressLint;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiMetadata;
+import android.support.text.emoji.EmojiSpan;
+import android.support.text.emoji.TypefaceEmojiSpan;
+import android.text.Editable;
+import android.text.SpannableString;
+import android.text.Spanned;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiEditableFactoryTest {
+
+    @Test
+    public void testGetInstance() {
+        final Editable.Factory instance = EmojiEditableFactory.getInstance();
+        assertNotNull(instance);
+
+        final Editable.Factory instance2 = EmojiEditableFactory.getInstance();
+        assertSame(instance, instance2);
+    }
+
+    @Test
+    public void testNewEditable_returnsEditable() {
+        final Editable editable = EmojiEditableFactory.getInstance().newEditable("abc");
+        assertNotNull(editable);
+        assertThat(editable, instanceOf(Editable.class));
+    }
+
+    @Test
+    public void testNewEditable_preservesCharSequenceData() {
+        final String string = "abc";
+        final SpannableString str = new SpannableString(string);
+        final EmojiMetadata metadata = mock(EmojiMetadata.class);
+        final EmojiSpan span = new TypefaceEmojiSpan(metadata);
+        str.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        final Editable editable = EmojiEditableFactory.getInstance().newEditable(str);
+        assertNotNull(editable);
+        assertEquals(string, editable.toString());
+        final EmojiSpan[] spans = editable.getSpans(0, 1, EmojiSpan.class);
+        assertThat(spans, arrayWithSize(1));
+        assertSame(spans[0], span);
+    }
+
+    @SuppressLint("PrivateApi")
+    @Test
+    public void testNewEditable_returnsEmojiSpannableIfWatcherClassExists() {
+        Class clazz = null;
+        try {
+            String className = "android.text.DynamicLayout$ChangeWatcher";
+            clazz = getClass().getClassLoader().loadClass(className);
+        } catch (Throwable t) {
+            // ignore
+        }
+
+        if (clazz == null) {
+            final Editable editable = EmojiEditableFactory.getInstance().newEditable("");
+            assertThat(editable, instanceOf(Editable.class));
+        } else {
+            final Editable editable = EmojiEditableFactory.getInstance().newEditable("");
+            assertThat(editable, instanceOf(SpannableBuilder.class));
+        }
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiExtractTextLayoutTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiExtractTextLayoutTest.java
new file mode 100644
index 0000000..8e1c6cf
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiExtractTextLayoutTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.EmojiMatcher.sameCharSequence;
+
+import static junit.framework.TestCase.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.inputmethodservice.InputMethodService;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.R;
+import android.text.InputType;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiExtractTextLayoutTest {
+
+    private InputMethodService mInputMethodService;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(mock(EmojiCompat.class));
+    }
+
+    @Before
+    public void setup() {
+        mInputMethodService = mock(InputMethodService.class);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testInflate() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final EmojiExtractTextLayout layout = (EmojiExtractTextLayout) LayoutInflater.from(context)
+                .inflate(android.support.text.emoji.test.R.layout.extract_view, null);
+
+        final EmojiExtractEditText extractEditText = layout.findViewById(
+                android.R.id.inputExtractEditText);
+        assertNotNull(extractEditText);
+
+        final ViewGroup inputExtractAccessories = layout.findViewById(
+                R.id.inputExtractAccessories);
+        assertNotNull(inputExtractAccessories);
+
+        final ExtractButtonCompat extractButton = inputExtractAccessories.findViewById(
+                R.id.inputExtractAction);
+        assertNotNull(extractButton);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSetEmojiReplaceStrategy() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+
+        final EmojiExtractTextLayout layout = (EmojiExtractTextLayout) LayoutInflater.from(context)
+                .inflate(android.support.text.emoji.test.R.layout.extract_view_with_attrs, null);
+
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT, layout.getEmojiReplaceStrategy());
+
+        final EmojiExtractEditText extractEditText = layout.findViewById(
+                android.R.id.inputExtractEditText);
+        assertNotNull(extractEditText);
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT,
+                extractEditText.getEmojiReplaceStrategy());
+
+        layout.setEmojiReplaceStrategy(EmojiCompat.REPLACE_STRATEGY_ALL);
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_ALL, layout.getEmojiReplaceStrategy());
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_ALL, extractEditText.getEmojiReplaceStrategy());
+    }
+
+    @Test
+    @UiThreadTest
+    @SdkSuppress(minSdkVersion = 19)
+    public void testSetEmojiReplaceStrategyCallEmojiCompatWithCorrectStrategy() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+
+        final EmojiExtractTextLayout layout = (EmojiExtractTextLayout) LayoutInflater.from(context)
+                .inflate(android.support.text.emoji.test.R.layout.extract_view_with_attrs, null);
+
+        final EmojiExtractEditText extractEditText = layout.findViewById(
+                android.R.id.inputExtractEditText);
+        assertNotNull(layout);
+        assertNotNull(extractEditText);
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT, layout.getEmojiReplaceStrategy());
+
+        final EmojiCompat emojiCompat = mock(EmojiCompat.class);
+        when(emojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
+        EmojiCompat.reset(emojiCompat);
+
+        final String testString = "anytext";
+        extractEditText.setText(testString);
+
+        verify(emojiCompat, times(1)).process(sameCharSequence(testString), anyInt(), anyInt(),
+                anyInt(), eq(EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testOnUpdateExtractingViews() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final EmojiExtractTextLayout layout = (EmojiExtractTextLayout) LayoutInflater.from(context)
+                .inflate(android.support.text.emoji.test.R.layout.extract_view, null);
+
+        final EditorInfo editorInfo = new EditorInfo();
+        editorInfo.actionLabel = "My Action Label";
+        editorInfo.imeOptions = EditorInfo.IME_ACTION_SEND;
+        editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
+
+        when(mInputMethodService.isExtractViewShown()).thenReturn(true);
+
+        final ViewGroup inputExtractAccessories = layout.findViewById(
+                R.id.inputExtractAccessories);
+        inputExtractAccessories.setVisibility(View.GONE);
+
+        final ExtractButtonCompat extractButton = inputExtractAccessories.findViewById(
+                R.id.inputExtractAction);
+
+        layout.onUpdateExtractingViews(mInputMethodService, editorInfo);
+
+        assertEquals(View.VISIBLE, inputExtractAccessories.getVisibility());
+        assertEquals(editorInfo.actionLabel, extractButton.getText());
+        assertTrue(extractButton.hasOnClickListeners());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testOnUpdateExtractingViews_hidesAccessoriesIfNoAction() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final EmojiExtractTextLayout layout = (EmojiExtractTextLayout) LayoutInflater.from(context)
+                .inflate(android.support.text.emoji.test.R.layout.extract_view, null);
+
+        final EditorInfo editorInfo = new EditorInfo();
+        editorInfo.imeOptions = EditorInfo.IME_ACTION_NONE;
+        when(mInputMethodService.isExtractViewShown()).thenReturn(true);
+
+        final ViewGroup inputExtractAccessories = layout.findViewById(
+                R.id.inputExtractAccessories);
+        final ExtractButtonCompat extractButton = inputExtractAccessories.findViewById(
+                R.id.inputExtractAction);
+
+        layout.onUpdateExtractingViews(mInputMethodService, editorInfo);
+
+        assertEquals(View.GONE, inputExtractAccessories.getVisibility());
+        assertFalse(extractButton.hasOnClickListeners());
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java
new file mode 100644
index 0000000..0958978
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.TestConfigBuilder;
+import android.support.text.emoji.util.Emoji;
+import android.support.text.emoji.util.TestString;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class EmojiInputConnectionTest {
+
+    private InputConnection mInputConnection;
+    private TestString mTestString;
+    private Editable mEditable;
+    private EmojiInputConnection mEmojiEmojiInputConnection;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mTestString = new TestString(Emoji.EMOJI_WITH_ZWJ).withPrefix().withSuffix();
+        mEditable = new SpannableStringBuilder(mTestString.toString());
+        mInputConnection = mock(InputConnection.class);
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final TextView textView = spy(new TextView(context));
+        EmojiCompat.get().process(mEditable);
+        assertThat(mEditable, hasEmoji());
+
+        doReturn(mEditable).when(textView).getEditableText();
+        when(mInputConnection.deleteSurroundingText(anyInt(), anyInt())).thenReturn(false);
+        setupDeleteSurroundingText();
+
+        mEmojiEmojiInputConnection = new EmojiInputConnection(textView, mInputConnection,
+                new EditorInfo());
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    private void setupDeleteSurroundingText() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            when(mInputConnection.deleteSurroundingTextInCodePoints(anyInt(), anyInt())).thenReturn(
+                    false);
+        }
+    }
+
+    @Test
+    public void testDeleteSurroundingText_doesNotDelete() {
+        Selection.setSelection(mEditable, 0, mEditable.length());
+        assertFalse(mEmojiEmojiInputConnection.deleteSurroundingText(1, 0));
+        verify(mInputConnection, times(1)).deleteSurroundingText(1, 0);
+    }
+
+    @Test
+    public void testDeleteSurroundingText_deletesEmojiBackward() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        assertTrue(mEmojiEmojiInputConnection.deleteSurroundingText(1, 0));
+        verify(mInputConnection, never()).deleteSurroundingText(anyInt(), anyInt());
+    }
+
+    @Test
+    public void testDeleteSurroundingText_doesNotDeleteEmojiIfSelectionAtStartIndex() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        assertFalse(mEmojiEmojiInputConnection.deleteSurroundingText(1, 0));
+        verify(mInputConnection, times(1)).deleteSurroundingText(1, 0);
+    }
+
+    @Test
+    public void testDeleteSurroundingText_deletesEmojiForward() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        assertTrue(mEmojiEmojiInputConnection.deleteSurroundingText(0, 1));
+        verify(mInputConnection, never()).deleteSurroundingText(anyInt(), anyInt());
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+    @Test
+    public void testDeleteSurroundingTextInCodePoints_doesNotDelete() {
+        Selection.setSelection(mEditable, 0, mEditable.length());
+        assertFalse(mEmojiEmojiInputConnection.deleteSurroundingTextInCodePoints(1, 0));
+        verify(mInputConnection, times(1)).deleteSurroundingTextInCodePoints(1, 0);
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+    @Test
+    public void testDeleteSurroundingTextInCodePoints_deletesEmojiBackward() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        assertTrue(mEmojiEmojiInputConnection.deleteSurroundingTextInCodePoints(1, 0));
+        verify(mInputConnection, never()).deleteSurroundingTextInCodePoints(anyInt(), anyInt());
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+    @Test
+    public void testDeleteSurroundingTextInCodePoints_deletesEmojiForward() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        assertTrue(mEmojiEmojiInputConnection.deleteSurroundingTextInCodePoints(0, 1));
+        verify(mInputConnection, never()).deleteSurroundingTextInCodePoints(anyInt(), anyInt());
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+    @Test
+    public void testDeleteSurroundingTextInCodePoints_doesNotDeleteEmojiIfSelectionAtStartIndex() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        assertFalse(mEmojiEmojiInputConnection.deleteSurroundingTextInCodePoints(1, 0));
+        verify(mInputConnection, times(1)).deleteSurroundingTextInCodePoints(1, 0);
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
new file mode 100644
index 0000000..690a946
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.EmojiMatcher.sameCharSequence;
+
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertSame;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiInputFilterTest {
+
+    private EmojiInputFilter mInputFilter;
+    private EmojiCompat mEmojiCompat;
+
+    @Before
+    public void setup() {
+        final TextView textView = mock(TextView.class);
+        mEmojiCompat = mock(EmojiCompat.class);
+        EmojiCompat.reset(mEmojiCompat);
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
+        mInputFilter = new EmojiInputFilter(textView);
+    }
+
+    @Test
+    public void testFilter_withNullSource() {
+        assertNull(mInputFilter.filter(null, 0, 1, null, 0, 1));
+        verify(mEmojiCompat, never()).process(any(CharSequence.class));
+        verify(mEmojiCompat, never()).process(any(CharSequence.class), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testFilter_withString() {
+        final String testString = "abc";
+        when(mEmojiCompat.process(any(CharSequence.class), anyInt(), anyInt()))
+                .thenReturn(new SpannableString(testString));
+        final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
+
+        assertNotNull(result);
+        assertTrue(result instanceof Spannable);
+        verify(mEmojiCompat, times(1)).process(sameCharSequence("a"), eq(0), eq(1));
+    }
+
+    @Test
+    public void testFilter_withSpannable() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.process(any(Spannable.class), anyInt(), anyInt())).thenReturn(testString);
+
+        final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
+
+        assertNotNull(result);
+        assertSame(result, testString);
+        verify(mEmojiCompat, times(1)).process(sameCharSequence(testString.subSequence(0, 1)),
+                eq(0), eq(1));
+    }
+
+    @Test
+    public void testFilter_whenEmojiCompatLoading() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_LOADING);
+
+        final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
+
+        assertNotNull(result);
+        assertSame(result, testString);
+        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
+        verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+    }
+
+    @Test
+    public void testFilter_whenEmojiCompatLoadFailed() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILED);
+
+        final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
+
+        assertNotNull(result);
+        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
+        verify(mEmojiCompat, times(0)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiKeyListenerTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiKeyListenerTest.java
new file mode 100644
index 0000000..06a5e68
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiKeyListenerTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_WITH_ZWJ;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmoji;
+import static android.support.text.emoji.util.KeyboardUtil.altDel;
+import static android.support.text.emoji.util.KeyboardUtil.ctrlDel;
+import static android.support.text.emoji.util.KeyboardUtil.del;
+import static android.support.text.emoji.util.KeyboardUtil.fnDel;
+import static android.support.text.emoji.util.KeyboardUtil.forwardDel;
+import static android.support.text.emoji.util.KeyboardUtil.shiftDel;
+import static android.support.text.emoji.util.KeyboardUtil.zero;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.annotation.TargetApi;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.TestConfigBuilder;
+import android.support.text.emoji.util.TestString;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.method.KeyListener;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class EmojiKeyListenerTest {
+
+    private KeyListener mKeyListener;
+    private TestString mTestString;
+    private Editable mEditable;
+    private EmojiKeyListener mEmojiKeyListener;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mKeyListener = mock(KeyListener.class);
+        mTestString = new TestString(EMOJI_WITH_ZWJ).withPrefix().withSuffix();
+        mEditable = new SpannableStringBuilder(mTestString.toString());
+        mEmojiKeyListener = new EmojiKeyListener(mKeyListener);
+        EmojiCompat.get().process(mEditable);
+        assertThat(mEditable, hasEmoji());
+
+        when(mKeyListener.onKeyDown(any(View.class), any(Editable.class), anyInt(),
+                any(KeyEvent.class))).thenReturn(false);
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_whenKeyCodeIsNotDelOrForwardDel() {
+        Selection.setSelection(mEditable, 0);
+        final KeyEvent event = zero();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withOtherModifiers() {
+        Selection.setSelection(mEditable, 0);
+        final KeyEvent event = fnDel();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withAltModifier() {
+        Selection.setSelection(mEditable, 0);
+        final KeyEvent event = altDel();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withCtrlModifier() {
+        Selection.setSelection(mEditable, 0);
+        final KeyEvent event = ctrlDel();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withShiftModifier() {
+        Selection.setSelection(mEditable, 0);
+        final KeyEvent event = shiftDel();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withSelection() {
+        Selection.setSelection(mEditable, 0, mEditable.length());
+        final KeyEvent event = del();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_withoutEmojiSpans() {
+        Editable editable = new SpannableStringBuilder("abc");
+        Selection.setSelection(editable, 1);
+        final KeyEvent event = del();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, editable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(editable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotDelete_whenNoSpansBefore() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        final KeyEvent event = del();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_deletesEmoji() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = del();
+        assertTrue(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verifyNoMoreInteractions(mKeyListener);
+    }
+
+    @Test
+    public void testOnKeyDown_doesNotForwardDeleteEmoji_withNoSpansAfter() {
+        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
+        final KeyEvent event = forwardDel();
+        assertFalse(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verify(mKeyListener, times(1)).onKeyDown((View) eq(null), same(mEditable),
+                eq(event.getKeyCode()), same(event));
+    }
+
+    @Test
+    public void testOnKeyDown_forwardDeletesEmoji() {
+        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
+        final KeyEvent event = forwardDel();
+        assertTrue(mEmojiKeyListener.onKeyDown(null, mEditable, event.getKeyCode(), event));
+        verifyNoMoreInteractions(mKeyListener);
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextViewHelperPre19Test.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextViewHelperPre19Test.java
new file mode 100644
index 0000000..6d68aad
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextViewHelperPre19Test.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.InputFilter;
+import android.text.method.TransformationMethod;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(maxSdkVersion = 18)
+public class EmojiTextViewHelperPre19Test {
+    EmojiTextViewHelper mTextViewHelper;
+    TextView mTextView;
+
+    @Before
+    public void setup() {
+        mTextView = new TextView(InstrumentationRegistry.getTargetContext());
+        mTextViewHelper = new EmojiTextViewHelper(mTextView);
+    }
+
+    @Test
+    public void testUpdateTransformationMethod_doesNotUpdateTransformationMethod() {
+        final TransformationMethod tm = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(tm);
+
+        mTextViewHelper.updateTransformationMethod();
+
+        assertSame(tm, mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testGetFilters_returnsSameFilters() {
+        final InputFilter existingFilter = mock(InputFilter.class);
+        final InputFilter[] filters = new InputFilter[]{existingFilter};
+
+        final InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
+
+        assertSame(filters, newFilters);
+    }
+
+    @Test
+    public void testGetTransformationMethod_returnSameTransformationMethod() {
+        assertNull(mTextViewHelper.wrapTransformationMethod(null));
+
+        final TransformationMethod tm = mock(TransformationMethod.class);
+        assertSame(tm, mTextViewHelper.wrapTransformationMethod(tm));
+    }
+
+    @Test
+    public void testSetAllCaps_doesNotUpdateTransformationMethod() {
+        final TransformationMethod tm = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(tm);
+        mTextViewHelper.setAllCaps(true);
+        assertSame(tm, mTextView.getTransformationMethod());
+
+        mTextViewHelper.setAllCaps(false);
+        assertSame(tm, mTextView.getTransformationMethod());
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextViewHelperTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextViewHelperTest.java
new file mode 100644
index 0000000..9a5ccbd
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextViewHelperTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.annotation.TargetApi;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.InputFilter;
+import android.text.method.PasswordTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+@TargetApi(19)
+public class EmojiTextViewHelperTest {
+    EmojiTextViewHelper mTextViewHelper;
+    TextView mTextView;
+
+    @Before
+    public void setup() {
+        mTextView = new TextView(InstrumentationRegistry.getTargetContext());
+        mTextViewHelper = new EmojiTextViewHelper(mTextView);
+    }
+
+    @Test
+    public void testUpdateTransformationMethod() {
+        mTextView.setTransformationMethod(mock(TransformationMethod.class));
+
+        mTextViewHelper.updateTransformationMethod();
+
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void testUpdateTransformationMethod_doesNotUpdateForPasswordTransformation() {
+        final PasswordTransformationMethod transformationMethod =
+                new PasswordTransformationMethod();
+        mTextView.setTransformationMethod(transformationMethod);
+
+        mTextViewHelper.updateTransformationMethod();
+
+        assertEquals(transformationMethod, mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testUpdateTransformationMethod_doesNotCreateNewInstance() {
+        mTextView.setTransformationMethod(mock(TransformationMethod.class));
+
+        mTextViewHelper.updateTransformationMethod();
+        final TransformationMethod tm = mTextView.getTransformationMethod();
+        assertThat(tm, instanceOf(EmojiTransformationMethod.class));
+
+        // call the function again
+        mTextViewHelper.updateTransformationMethod();
+        assertSame(tm, mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testGetFilters() {
+        final InputFilter existingFilter = mock(InputFilter.class);
+        final InputFilter[] filters = new InputFilter[]{existingFilter};
+
+        final InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
+
+        assertEquals(2, newFilters.length);
+        assertThat(Arrays.asList(newFilters), hasItem(existingFilter));
+        assertNotNull(findEmojiInputFilter(newFilters));
+    }
+
+    @Test
+    public void testGetFilters_doesNotAddSecondInstance() {
+        final InputFilter existingFilter = mock(InputFilter.class);
+        final InputFilter[] filters = new InputFilter[]{existingFilter};
+
+        InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
+        EmojiInputFilter emojiInputFilter = findEmojiInputFilter(newFilters);
+        assertNotNull(emojiInputFilter);
+
+        // run it again with the updated filters and see that it does not add new filter
+        newFilters = mTextViewHelper.getFilters(newFilters);
+
+        assertEquals(2, newFilters.length);
+        assertThat(Arrays.asList(newFilters), hasItem(existingFilter));
+        assertThat(Arrays.asList(newFilters), hasItem(emojiInputFilter));
+    }
+
+    private EmojiInputFilter findEmojiInputFilter(final InputFilter[] filters) {
+        for (int i = 0; i < filters.length; i++) {
+            if (filters[i] instanceof EmojiInputFilter) {
+                return (EmojiInputFilter) filters[i];
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testWrapTransformationMethod() {
+        assertThat(mTextViewHelper.wrapTransformationMethod(null),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void testWrapTransformationMethod_doesNotCreateNewInstance() {
+        final TransformationMethod tm1 = mTextViewHelper.wrapTransformationMethod(null);
+        final TransformationMethod tm2 = mTextViewHelper.wrapTransformationMethod(tm1);
+        assertSame(tm1, tm2);
+    }
+
+    @Test
+    public void testSetAllCaps_withTrueSetsTransformationMethod() {
+        mTextView.setTransformationMethod(mock(TransformationMethod.class));
+        mTextViewHelper.setAllCaps(true);
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void testSetAllCaps_withFalseDoesNotSetTransformationMethod() {
+        mTextView.setTransformationMethod(null);
+        mTextViewHelper.setAllCaps(false);
+        assertNull(mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testSetAllCaps_withPasswordTransformationDoesNotSetTransformationMethod() {
+        final PasswordTransformationMethod transformationMethod =
+                new PasswordTransformationMethod();
+        mTextView.setTransformationMethod(transformationMethod);
+        mTextViewHelper.setAllCaps(true);
+        assertSame(transformationMethod, mTextView.getTransformationMethod());
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
new file mode 100644
index 0000000..88549cf
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.EmojiMatcher.sameCharSequence;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiTextWatcherTest {
+
+    private EmojiTextWatcher mTextWatcher;
+    private EmojiCompat mEmojiCompat;
+
+    @Before
+    public void setup() {
+        final EditText editText = mock(EditText.class);
+        mEmojiCompat = mock(EmojiCompat.class);
+        EmojiCompat.reset(mEmojiCompat);
+        mTextWatcher = new EmojiTextWatcher(editText);
+    }
+
+    @Test
+    public void testOnTextChanged_callsProcess() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
+
+        mTextWatcher.onTextChanged(testString, 0, 0, 1);
+
+        verify(mEmojiCompat, times(1)).process(sameCharSequence(testString), eq(0), eq(1),
+                eq(Integer.MAX_VALUE), anyInt());
+        verify(mEmojiCompat, times(0)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+    }
+
+    @Test
+    public void testOnTextChanged_whenEmojiCompatLoading() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_LOADING);
+
+        mTextWatcher.onTextChanged(testString, 0, 0, 1);
+
+        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt(), anyInt(),
+                anyInt());
+        verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+    }
+
+    @Test
+    public void testOnTextChanged_whenEmojiCompatLoadFailed() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILED);
+
+        mTextWatcher.onTextChanged(testString, 0, 0, 1);
+
+        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt(), anyInt(),
+                anyInt());
+        verify(mEmojiCompat, times(0)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+    }
+
+    @Test
+    public void testSetEmojiReplaceStrategy() {
+        final Spannable testString = new SpannableString("abc");
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
+
+        assertEquals(EmojiCompat.REPLACE_STRATEGY_DEFAULT, mTextWatcher.getEmojiReplaceStrategy());
+
+        mTextWatcher.onTextChanged(testString, 0, 0, 1);
+
+        verify(mEmojiCompat, times(1)).process(any(Spannable.class), anyInt(), anyInt(), anyInt(),
+                eq(EmojiCompat.REPLACE_STRATEGY_DEFAULT));
+
+        mTextWatcher.setEmojiReplaceStrategy(EmojiCompat.REPLACE_STRATEGY_ALL);
+
+        mTextWatcher.onTextChanged(testString, 0, 0, 1);
+
+        verify(mEmojiCompat, times(1)).process(any(Spannable.class), anyInt(), anyInt(), anyInt(),
+                eq(EmojiCompat.REPLACE_STRATEGY_ALL));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/SpannableBuilderTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/SpannableBuilderTest.java
new file mode 100644
index 0000000..2359b3b
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/SpannableBuilderTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+package android.support.text.emoji.widget;
+
+import static junit.framework.Assert.assertSame;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.withSettings;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiSpan;
+import android.text.Editable;
+import android.text.SpanWatcher;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.TextWatcher;
+import android.text.style.QuoteSpan;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannableBuilderTest {
+
+    private TextWatcher mWatcher;
+    private Class mClass;
+
+    @Before
+    public void setup() {
+        mWatcher = mock(TextWatcher.class, withSettings().extraInterfaces(SpanWatcher.class));
+        mClass = mWatcher.getClass();
+    }
+
+    @Test
+    public void testConstructor() {
+        new SpannableBuilder(mClass);
+
+        new SpannableBuilder(mClass, "abc");
+
+        new SpannableBuilder(mClass, "abc", 0, 3);
+
+        // test spannable copying? do I need it?
+    }
+
+    @Test
+    public void testSubSequence() {
+        final SpannableBuilder spannable = new SpannableBuilder(mClass, "abc");
+        final QuoteSpan span1 = mock(QuoteSpan.class);
+        final QuoteSpan span2 = mock(QuoteSpan.class);
+        spannable.setSpan(span1, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        spannable.setSpan(span2, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        final CharSequence subsequence = spannable.subSequence(0, 1);
+        assertNotNull(subsequence);
+        assertThat(subsequence, instanceOf(SpannableBuilder.class));
+
+        final QuoteSpan[] spans = spannable.getSpans(0, 1, QuoteSpan.class);
+        assertThat(spans, arrayWithSize(1));
+        assertSame(spans[0], span1);
+    }
+
+    @Test
+    public void testSetAndGetSpan() {
+        final SpannableBuilder spannable = new SpannableBuilder(mClass, "abcde");
+        spannable.setSpan(mWatcher, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+        // getSpans should return the span
+        Object[] spans = spannable.getSpans(0, spannable.length(), mClass);
+        assertNotNull(spans);
+        assertThat(spans, arrayWithSize(1));
+        assertSame(mWatcher, spans[0]);
+
+        // span attributes should be correct
+        assertEquals(1, spannable.getSpanStart(mWatcher));
+        assertEquals(2, spannable.getSpanEnd(mWatcher));
+        assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, spannable.getSpanFlags(mWatcher));
+
+        // should remove the span
+        spannable.removeSpan(mWatcher);
+        spans = spannable.getSpans(0, spannable.length(), QuoteSpan.class);
+        assertNotNull(spans);
+        assertThat(spans, arrayWithSize(0));
+    }
+
+    @Test
+    public void testNextSpanTransition() {
+        final SpannableBuilder spannable = new SpannableBuilder(mClass, "abcde");
+        spannable.setSpan(mWatcher, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        final int start = spannable.nextSpanTransition(0, spannable.length(), mClass);
+        Assert.assertEquals(1, start);
+    }
+
+    @Test
+    public void testBlocksSpanCallbacks_forEmojiSpans() {
+        final EmojiSpan span = mock(EmojiSpan.class);
+        final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456");
+        spannable.setSpan(mWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        reset(mWatcher);
+
+        spannable.delete(0, 3);
+
+        // verify that characters are deleted
+        assertEquals("456", spannable.toString());
+        // verify EmojiSpan is deleted
+        EmojiSpan[] spans = spannable.getSpans(0, spannable.length(), EmojiSpan.class);
+        assertThat(spans, arrayWithSize(0));
+
+        // verify the call to span callbacks are blocked
+        verify((SpanWatcher) mWatcher, never()).onSpanRemoved(any(Spannable.class),
+                same(span), anyInt(), anyInt());
+        verify((SpanWatcher) mWatcher, never()).onSpanAdded(any(Spannable.class),
+                same(span), anyInt(), anyInt());
+        verify((SpanWatcher) mWatcher, never()).onSpanChanged(any(Spannable.class),
+                same(span), anyInt(), anyInt(), anyInt(), anyInt());
+
+        // verify the call to TextWatcher callbacks are called
+        verify(mWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(),
+                anyInt(), anyInt());
+        verify(mWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(),
+                anyInt());
+        verify(mWatcher, times(1)).afterTextChanged(any(Editable.class));
+    }
+
+    @Test
+    public void testDoesNotBlockSpanCallbacks_forNonEmojiSpans() {
+        final QuoteSpan span = mock(QuoteSpan.class);
+        final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456");
+        spannable.setSpan(mWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        reset(mWatcher);
+
+        spannable.delete(0, 3);
+
+        // verify that characters are deleted
+        assertEquals("456", spannable.toString());
+        // verify QuoteSpan is deleted
+        QuoteSpan[] spans = spannable.getSpans(0, spannable.length(), QuoteSpan.class);
+        assertThat(spans, arrayWithSize(0));
+
+        // verify the call to span callbacks are not blocked
+        verify((SpanWatcher) mWatcher, times(1)).onSpanRemoved(any(Spannable.class),
+                anyObject(), anyInt(), anyInt());
+
+        // verify the call to TextWatcher callbacks are called
+        verify(mWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(), anyInt(),
+                anyInt());
+        verify(mWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(),
+                anyInt());
+        verify(mWatcher, times(1)).afterTextChanged(any(Editable.class));
+    }
+
+    @Test
+    public void testDoesNotBlockSpanCallbacksForOtherWatchers() {
+        final TextWatcher textWatcher = mock(TextWatcher.class);
+        final SpanWatcher spanWatcher = mock(SpanWatcher.class);
+
+        final EmojiSpan span = mock(EmojiSpan.class);
+        final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456");
+        spannable.setSpan(textWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        spannable.setSpan(spanWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        reset(textWatcher);
+
+        spannable.delete(0, 3);
+
+        // verify that characters are deleted
+        assertEquals("456", spannable.toString());
+        // verify EmojiSpan is deleted
+        EmojiSpan[] spans = spannable.getSpans(0, spannable.length(), EmojiSpan.class);
+        assertThat(spans, arrayWithSize(0));
+
+        // verify the call to span callbacks are blocked
+        verify(spanWatcher, times(1)).onSpanRemoved(any(Spannable.class), same(span),
+                anyInt(), anyInt());
+
+        // verify the call to TextWatcher callbacks are called
+        verify(textWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(),
+                anyInt(), anyInt());
+        verify(textWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(),
+                anyInt());
+        verify(textWatcher, times(1)).afterTextChanged(any(Editable.class));
+    }
+}
diff --git a/emoji/core/tests/res/layout/activity_default.xml b/emoji/core/tests/res/layout/activity_default.xml
new file mode 100644
index 0000000..27477ec
--- /dev/null
+++ b/emoji/core/tests/res/layout/activity_default.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              android:id="@+id/root"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="8sp"/>
+
+    <android.support.text.emoji.widget.EmojiEditText
+        android:id="@+id/editText"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <android.support.text.emoji.widget.EmojiEditText
+        android:id="@+id/editTextWithMaxCount"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:maxEmojiCount="5"/>
+
+</LinearLayout>
diff --git a/emoji/core/tests/res/layout/extract_view.xml b/emoji/core/tests/res/layout/extract_view.xml
new file mode 100644
index 0000000..ba72433
--- /dev/null
+++ b/emoji/core/tests/res/layout/extract_view.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.
+  -->
+
+<android.support.text.emoji.widget.EmojiExtractTextLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/emoji/core/tests/res/layout/extract_view_with_attrs.xml b/emoji/core/tests/res/layout/extract_view_with_attrs.xml
new file mode 100644
index 0000000..0c16e7d
--- /dev/null
+++ b/emoji/core/tests/res/layout/extract_view_with_attrs.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<android.support.text.emoji.widget.EmojiExtractTextLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/emojiExtractTextLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:emojiReplaceStrategy="nonExistent"/>
\ No newline at end of file
diff --git a/exifinterface/Android.mk b/exifinterface/Android.mk
index 4d5887c..9da8bc5 100644
--- a/exifinterface/Android.mk
+++ b/exifinterface/Android.mk
@@ -25,7 +25,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/exifinterface/AndroidManifest-make.xml b/exifinterface/AndroidManifest-make.xml
deleted file mode 100644
index 4812a71..0000000
--- a/exifinterface/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.exifinterface">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application />
-</manifest>
diff --git a/exifinterface/AndroidManifest.xml b/exifinterface/AndroidManifest.xml
index af3dece..344b99b 100644
--- a/exifinterface/AndroidManifest.xml
+++ b/exifinterface/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.exifinterface">
-    <uses-sdk android:minSdkVersion="9"/>
+    <uses-sdk android:minSdkVersion="14"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/exifinterface/build.gradle b/exifinterface/build.gradle
index c65cf71..634fd28 100644
--- a/exifinterface/build.gradle
+++ b/exifinterface/build.gradle
@@ -1,83 +1,27 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'exifinterface'
 
 dependencies {
-    compile project(':support-annotations')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['src']
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+        main.res.srcDirs = ['res']
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support ExifInterface'
-                description "Android Support ExifInterface"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2016'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support ExifInterface'
+    inceptionYear '2016'
+    description 'Android Support ExifInterface'
 }
diff --git a/exifinterface/lint-baseline.xml b/exifinterface/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/exifinterface/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/exifinterface/src/android/support/media/ExifInterface.java b/exifinterface/src/android/support/media/ExifInterface.java
index d2bec3a..aa19cbe 100644
--- a/exifinterface/src/android/support/media/ExifInterface.java
+++ b/exifinterface/src/android/support/media/ExifInterface.java
@@ -19,6 +19,7 @@
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.location.Location;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.util.Log;
@@ -49,9 +50,10 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -390,10 +392,15 @@
     public static final int ORIENTATION_FLIP_VERTICAL = 4;  // upside down mirror
     // flipped about top-left <--> bottom-right axis
     public static final int ORIENTATION_TRANSPOSE = 5;
-    public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 cw to right it
+    public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 degree clockwise
     // flipped about top-right <--> bottom-left axis
     public static final int ORIENTATION_TRANSVERSE = 7;
-    public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 to right it
+    public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 degree clockwise
+    private static final List<Integer> ROTATION_ORDER = Arrays.asList(ORIENTATION_NORMAL,
+            ORIENTATION_ROTATE_90, ORIENTATION_ROTATE_180, ORIENTATION_ROTATE_270);
+    private static final List<Integer> FLIPPED_ROTATION_ORDER = Arrays.asList(
+            ORIENTATION_FLIP_HORIZONTAL, ORIENTATION_TRANSVERSE, ORIENTATION_FLIP_VERTICAL,
+            ORIENTATION_TRANSPOSE);
 
     // Constants used for white balance
     public static final int WHITEBALANCE_AUTO = 0;
@@ -402,7 +409,7 @@
     // Maximum size for checking file type signature (see image_type_recognition_lite.cc)
     private static final int SIGNATURE_CHECK_SIZE = 5000;
 
-    private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
+    static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
     private static final String RAF_SIGNATURE = "FUJIFILMCCD-RAW";
     private static final int RAF_OFFSET_TO_JPEG_IMAGE_OFFSET = 84;
     private static final int RAF_INFO_SIZE = 160;
@@ -438,11 +445,11 @@
     // image metadata from GPS longitude to camera model name.
 
     // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2)
-    private static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
-    private static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
+    static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
+    static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
 
     // TIFF Header Fixed Constant (see JEITA CP-3451C Section 4.5.2)
-    private static final byte START_CODE = 0x2a; // 42
+    static final byte START_CODE = 0x2a; // 42
     private static final int IFD_OFFSET = 8;
 
     // Formats for the value in IFD entry (See TIFF 6.0 Section 2, "Image File Directory".)
@@ -461,12 +468,12 @@
     // Format indicating a new IFD entry (See Adobe PageMaker® 6.0 TIFF Technical Notes, "New Tag")
     private static final int IFD_FORMAT_IFD = 13;
     // Names for the data formats for debugging purpose.
-    private static final String[] IFD_FORMAT_NAMES = new String[] {
+    static final String[] IFD_FORMAT_NAMES = new String[] {
             "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
             "SLONG", "SRATIONAL", "SINGLE", "DOUBLE"
     };
     // Sizes of the components of each IFD value format
-    private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
+    static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
             0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1
     };
     private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
@@ -518,6 +525,10 @@
         public final long numerator;
         public final long denominator;
 
+        private Rational(double value) {
+            this((long) (value * 10000), 10000);
+        }
+
         private Rational(long numerator, long denominator) {
             // Handle erroneous case
             if (denominator == 0) {
@@ -904,7 +915,7 @@
     }
 
     // A class for indicating EXIF tag.
-    private static class ExifTag {
+    static class ExifTag {
         public final int number;
         public final String name;
         public final int primaryFormat;
@@ -923,6 +934,24 @@
             this.primaryFormat = primaryFormat;
             this.secondaryFormat = secondaryFormat;
         }
+
+        private boolean isFormatCompatible(int format) {
+            if (primaryFormat == IFD_FORMAT_UNDEFINED || format == IFD_FORMAT_UNDEFINED) {
+                return true;
+            } else if (primaryFormat == format || secondaryFormat == format) {
+                return true;
+            } else if ((primaryFormat == IFD_FORMAT_ULONG || secondaryFormat == IFD_FORMAT_ULONG)
+                    && format == IFD_FORMAT_USHORT) {
+                return true;
+            } else if ((primaryFormat == IFD_FORMAT_SLONG || secondaryFormat == IFD_FORMAT_SLONG)
+                    && format == IFD_FORMAT_SSHORT) {
+                return true;
+            } else if ((primaryFormat == IFD_FORMAT_DOUBLE || secondaryFormat == IFD_FORMAT_DOUBLE)
+                    && format == IFD_FORMAT_SINGLE) {
+                return true;
+            }
+            return false;
+        }
     }
 
     // Primary image IFD TIFF tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
@@ -1151,19 +1180,19 @@
             IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF})
     public @interface IfdType {}
 
-    private static final int IFD_TYPE_PRIMARY = 0;
+    static final int IFD_TYPE_PRIMARY = 0;
     private static final int IFD_TYPE_EXIF = 1;
     private static final int IFD_TYPE_GPS = 2;
     private static final int IFD_TYPE_INTEROPERABILITY = 3;
-    private static final int IFD_TYPE_THUMBNAIL = 4;
-    private static final int IFD_TYPE_PREVIEW = 5;
+    static final int IFD_TYPE_THUMBNAIL = 4;
+    static final int IFD_TYPE_PREVIEW = 5;
     private static final int IFD_TYPE_ORF_MAKER_NOTE = 6;
     private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7;
     private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8;
     private static final int IFD_TYPE_PEF = 9;
 
     // List of Exif tag groups
-    private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
+    static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
             IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
             IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS, ORF_MAKER_NOTE_TAGS, ORF_CAMERA_SETTINGS_TAGS,
             ORF_IMAGE_PROCESSING_TAGS, PEF_TAGS
@@ -1185,14 +1214,19 @@
             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
 
     // Mappings from tag number to tag name and each item represents one IFD tag group.
-    private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
+    @SuppressWarnings("unchecked")
+    private static final HashMap<Integer, ExifTag>[] sExifTagMapsForReading =
+            new HashMap[EXIF_TAGS.length];
     // Mappings from tag name to tag number and each item represents one IFD tag group.
-    private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
+    @SuppressWarnings("unchecked")
+    private static final HashMap<String, ExifTag>[] sExifTagMapsForWriting =
+            new HashMap[EXIF_TAGS.length];
     private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
             TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
             TAG_GPS_TIMESTAMP));
     // Mappings from tag number to IFD type for pointer tags.
-    private static final HashMap sExifPointerTagMap = new HashMap();
+    @SuppressWarnings("unchecked")
+    private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap();
 
     // See JPEG File Interchange Format Version 1.02.
     // The following values are defined for handling JPEG streams. In this implementation, we are
@@ -1201,11 +1235,11 @@
 
     private static final Charset ASCII = Charset.forName("US-ASCII");
     // Identifier for EXIF APP1 segment in JPEG
-    private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
+    static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
     // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
     // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
     // of frame(baseline DCT) and the image size info exists in its beginning part.
-    private static final byte MARKER = (byte) 0xff;
+    static final byte MARKER = (byte) 0xff;
     private static final byte MARKER_SOI = (byte) 0xd8;
     private static final byte MARKER_SOF0 = (byte) 0xc0;
     private static final byte MARKER_SOF1 = (byte) 0xc1;
@@ -1221,9 +1255,9 @@
     private static final byte MARKER_SOF14 = (byte) 0xce;
     private static final byte MARKER_SOF15 = (byte) 0xcf;
     private static final byte MARKER_SOS = (byte) 0xda;
-    private static final byte MARKER_APP1 = (byte) 0xe1;
+    static final byte MARKER_APP1 = (byte) 0xe1;
     private static final byte MARKER_COM = (byte) 0xfe;
-    private static final byte MARKER_EOI = (byte) 0xd9;
+    static final byte MARKER_EOI = (byte) 0xd9;
 
     // Supported Image File Types
     private static final int IMAGE_TYPE_UNKNOWN = 0;
@@ -1245,8 +1279,8 @@
 
         // Build up the hash tables to look up Exif tags for reading Exif tags.
         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
-            sExifTagMapsForReading[ifdType] = new HashMap();
-            sExifTagMapsForWriting[ifdType] = new HashMap();
+            sExifTagMapsForReading[ifdType] = new HashMap<>();
+            sExifTagMapsForWriting[ifdType] = new HashMap<>();
             for (ExifTag tag : EXIF_TAGS[ifdType]) {
                 sExifTagMapsForReading[ifdType].put(tag.number, tag);
                 sExifTagMapsForWriting[ifdType].put(tag.name, tag);
@@ -1265,7 +1299,8 @@
     private final String mFilename;
     private final AssetManager.AssetInputStream mAssetInputStream;
     private int mMimeType;
-    private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
+    @SuppressWarnings("unchecked")
+    private final HashMap<String, ExifAttribute>[] mAttributes = new HashMap[EXIF_TAGS.length];
     private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
     private boolean mHasThumbnail;
     // The following values used for indicating a thumbnail position.
@@ -1333,9 +1368,9 @@
         // Retrieves all tag groups. The value from primary image tag group has a higher priority
         // than the value from the thumbnail tag group if there are more than one candidates.
         for (int i = 0; i < EXIF_TAGS.length; ++i) {
-            Object value = mAttributes[i].get(tag);
+            ExifAttribute value = mAttributes[i].get(tag);
             if (value != null) {
-                return (ExifAttribute) value;
+                return value;
             }
         }
         return null;
@@ -1422,7 +1457,7 @@
     }
 
     /**
-     * Set the value of the specified tag.
+     * Sets the value of the specified tag.
      *
      * @param tag the name of the tag.
      * @param value the value of the tag.
@@ -1441,7 +1476,7 @@
             } else {
                 try {
                     double doubleValue = Double.parseDouble(value);
-                    value = (long) (doubleValue * 10000L) + "/10000";
+                    value = new Rational(doubleValue).toString();
                 } catch (NumberFormatException e) {
                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
                     return;
@@ -1453,13 +1488,12 @@
             if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
                 continue;
             }
-            final Object obj = sExifTagMapsForWriting[i].get(tag);
-            if (obj != null) {
+            final ExifTag exifTag = sExifTagMapsForWriting[i].get(tag);
+            if (exifTag != null) {
                 if (value == null) {
                     mAttributes[i].remove(tag);
                     continue;
                 }
-                final ExifTag exifTag = (ExifTag) obj;
                 Pair<Integer, Integer> guess = guessDataFormat(value);
                 int dataFormat;
                 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
@@ -1563,6 +1597,122 @@
     }
 
     /**
+     * Resets the {@link #TAG_ORIENTATION} of the image to be {@link #ORIENTATION_NORMAL}.
+     */
+    public void resetOrientation() {
+        setAttribute(TAG_ORIENTATION, Integer.toString(ORIENTATION_NORMAL));
+    }
+
+    /**
+     * Rotates the image by the given degree clockwise. The degree should be a multiple of
+     * 90 (e.g, 90, 180, -90, etc.).
+     *
+     * @param degree The degree of rotation.
+     */
+    public void rotate(int degree) {
+        if (degree % 90 !=0) {
+            throw new IllegalArgumentException("degree should be a multiple of 90");
+        }
+
+        int currentOrientation = getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
+        int currentIndex, newIndex;
+        int resultOrientation;
+        if (ROTATION_ORDER.contains(currentOrientation)) {
+            currentIndex = ROTATION_ORDER.indexOf(currentOrientation);
+            newIndex = (currentIndex + degree / 90) % 4;
+            newIndex += newIndex < 0 ? 4 : 0;
+            resultOrientation = ROTATION_ORDER.get(newIndex);
+        } else if (FLIPPED_ROTATION_ORDER.contains(currentOrientation)) {
+            currentIndex = FLIPPED_ROTATION_ORDER.indexOf(currentOrientation);
+            newIndex = (currentIndex + degree / 90) % 4;
+            newIndex += newIndex < 0 ? 4 : 0;
+            resultOrientation = FLIPPED_ROTATION_ORDER.get(newIndex);
+        } else {
+            resultOrientation = ORIENTATION_UNDEFINED;
+        }
+
+        setAttribute(TAG_ORIENTATION, Integer.toString(resultOrientation));
+    }
+
+    /**
+     * Flips the image vertically.
+     */
+    public void flipVertically() {
+        int currentOrientation = getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
+        int resultOrientation;
+        switch (currentOrientation) {
+            case ORIENTATION_FLIP_HORIZONTAL:
+                resultOrientation = ORIENTATION_ROTATE_180;
+                break;
+            case ORIENTATION_ROTATE_180:
+                resultOrientation = ORIENTATION_FLIP_HORIZONTAL;
+                break;
+            case ORIENTATION_FLIP_VERTICAL:
+                resultOrientation = ORIENTATION_NORMAL;
+                break;
+            case ORIENTATION_TRANSPOSE:
+                resultOrientation = ORIENTATION_ROTATE_270;
+                break;
+            case ORIENTATION_ROTATE_90:
+                resultOrientation = ORIENTATION_TRANSVERSE;
+                break;
+            case ORIENTATION_TRANSVERSE:
+                resultOrientation = ORIENTATION_ROTATE_90;
+                break;
+            case ORIENTATION_ROTATE_270:
+                resultOrientation = ORIENTATION_TRANSPOSE;
+                break;
+            case ORIENTATION_NORMAL:
+                resultOrientation = ORIENTATION_FLIP_VERTICAL;
+                break;
+            case ORIENTATION_UNDEFINED:
+            default:
+                resultOrientation = ORIENTATION_UNDEFINED;
+                break;
+        }
+        setAttribute(TAG_ORIENTATION, Integer.toString(resultOrientation));
+    }
+
+    /**
+     * Flips the image horizontally.
+     */
+    public void flipHorizontally() {
+        int currentOrientation = getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
+        int resultOrientation;
+        switch (currentOrientation) {
+            case ORIENTATION_FLIP_HORIZONTAL:
+                resultOrientation = ORIENTATION_NORMAL;
+                break;
+            case ORIENTATION_ROTATE_180:
+                resultOrientation = ORIENTATION_FLIP_VERTICAL;
+                break;
+            case ORIENTATION_FLIP_VERTICAL:
+                resultOrientation = ORIENTATION_ROTATE_180;
+                break;
+            case ORIENTATION_TRANSPOSE:
+                resultOrientation = ORIENTATION_ROTATE_90;
+                break;
+            case ORIENTATION_ROTATE_90:
+                resultOrientation = ORIENTATION_TRANSPOSE;
+                break;
+            case ORIENTATION_TRANSVERSE:
+                resultOrientation = ORIENTATION_ROTATE_270;
+                break;
+            case ORIENTATION_ROTATE_270:
+                resultOrientation = ORIENTATION_TRANSVERSE;
+                break;
+            case ORIENTATION_NORMAL:
+                resultOrientation = ORIENTATION_FLIP_HORIZONTAL;
+                break;
+            case ORIENTATION_UNDEFINED:
+            default:
+                resultOrientation = ORIENTATION_UNDEFINED;
+                break;
+        }
+        setAttribute(TAG_ORIENTATION, Integer.toString(resultOrientation));
+    }
+
+    /**
      * Update the values of the tags in the tag groups if any value for the tag already was stored.
      *
      * @param tag the name of the tag.
@@ -1600,7 +1750,7 @@
         try {
             // Initialize mAttributes.
             for (int i = 0; i < EXIF_TAGS.length; ++i) {
-                mAttributes[i] = new HashMap();
+                mAttributes[i] = new HashMap<>();
             }
 
             // Check file type
@@ -1667,8 +1817,8 @@
     private void printAttributes() {
         for (int i = 0; i < mAttributes.length; ++i) {
             Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
-            for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
-                final ExifAttribute tagValue = (ExifAttribute) entry.getValue();
+            for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) {
+                final ExifAttribute tagValue = entry.getValue();
                 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
                         + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
             }
@@ -1895,6 +2045,28 @@
     }
 
     /**
+     * Sets the GPS-related information. It will set GPS processing method, latitude and longitude
+     * values, GPS timestamp, and speed information at the same time.
+     *
+     * @param location the {@link Location} object returned by GPS service.
+     */
+    public void setGpsInfo(Location location) {
+        if (location == null) {
+            return;
+        }
+        setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, location.getProvider());
+        setLatLong(location.getLatitude(), location.getLongitude());
+        setAltitude(location.getAltitude());
+        // Location objects store speeds in m/sec. Translates it to km/hr here.
+        setAttribute(TAG_GPS_SPEED_REF, "K");
+        setAttribute(TAG_GPS_SPEED, new Rational(location.getSpeed()
+                * TimeUnit.HOURS.toSeconds(1) / 1000).toString());
+        String[] dateTime = sFormatter.format(new Date(location.getTime())).split("\\s+");
+        setAttribute(ExifInterface.TAG_GPS_DATESTAMP, dateTime[0]);
+        setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, dateTime[1]);
+    }
+
+    /**
      * Sets the latitude and longitude values.
      *
      * @param latitude the decimal value of latitude. Must be a valid double value between -90.0 and
@@ -1935,6 +2107,27 @@
     }
 
     /**
+     * Sets the altitude in meters.
+     */
+    public void setAltitude(double altitude) {
+        String ref = altitude >= 0 ? "0" : "1";
+        setAttribute(TAG_GPS_ALTITUDE, new Rational(Math.abs(altitude)).toString());
+        setAttribute(TAG_GPS_ALTITUDE_REF, ref);
+    }
+
+    /**
+     * Set the date time value.
+     *
+     * @param timeStamp number of milliseconds since Jan. 1, 1970, midnight local time.
+     * @hide
+     */
+    public void setDateTime(long timeStamp) {
+        long sub = timeStamp % 1000;
+        setAttribute(TAG_DATETIME, sFormatter.format(new Date(timeStamp)));
+        setAttribute(TAG_SUBSEC_TIME, Long.toString(sub));
+    }
+
+    /**
      * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
      * Returns -1 if the date time information if not available.
      * @hide
@@ -2417,16 +2610,16 @@
             readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
 
             // Retrieve & update preview image offset & length values
-            ExifAttribute imageLengthAttribute = (ExifAttribute)
+            ExifAttribute imageStartAttribute = (ExifAttribute)
                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
-            ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
+            ExifAttribute imageLengthAttribute = (ExifAttribute)
                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
 
-            if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
+            if (imageStartAttribute != null && imageLengthAttribute != null) {
                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
-                        imageLengthAttribute);
+                        imageStartAttribute);
                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
-                        bitsPerSampleAttribute);
+                        imageLengthAttribute);
             }
 
             // TODO: Check this behavior in other ORF files
@@ -2599,9 +2792,9 @@
     }
 
     private void addDefaultValuesForCompatibility() {
-        // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
+        // If DATETIME tag has no value, then set the value to DATETIME_ORIGINAL tag's.
         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
-        if (valueOfDateTimeOriginal != null) {
+        if (valueOfDateTimeOriginal != null && getAttribute(TAG_DATETIME) == null) {
             mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
                     ExifAttribute.createString(valueOfDateTimeOriginal));
         }
@@ -2680,15 +2873,14 @@
         }
         // See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
         short numberOfDirectoryEntry = dataInputStream.readShort();
+        if (DEBUG) {
+            Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
+        }
         if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
             // Return if the size of entries is too big.
             return;
         }
 
-        if (DEBUG) {
-            Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
-        }
-
         // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory".
         for (short i = 0; i < numberOfDirectoryEntry; ++i) {
             int tagNumber = dataInputStream.readUnsignedShort();
@@ -2712,7 +2904,13 @@
                 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
             } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
                 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
+            } else if (!tag.isFormatCompatible(dataFormat)) {
+                Log.w(TAG, "Skip the tag entry since data format (" + IFD_FORMAT_NAMES[dataFormat]
+                        + ") is unexpected for tag: " + tag.name);
             } else {
+                if (dataFormat == IFD_FORMAT_UNDEFINED) {
+                    dataFormat = tag.primaryFormat;
+                }
                 byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
                 if (byteCount < 0 || byteCount > Integer.MAX_VALUE) {
                     Log.w(TAG, "Skip the tag entry since the number of components is invalid: "
@@ -2772,7 +2970,7 @@
             }
 
             // Recursively parse IFD when a IFD pointer tag appears.
-            Object nextIfdType = sExifPointerTagMap.get(tagNumber);
+            Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
             if (DEBUG) {
                 Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
             }
@@ -2808,7 +3006,7 @@
                 }
                 if (offset > 0L && offset < dataInputStream.mLength) {
                     dataInputStream.seek(offset);
-                    readImageFileDirectory(dataInputStream, (int) nextIfdType);
+                    readImageFileDirectory(dataInputStream, nextIfdType);
                 } else {
                     Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
                 }
@@ -3098,7 +3296,7 @@
         if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
             if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) {
                 mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW];
-                mAttributes[IFD_TYPE_PREVIEW] = new HashMap();
+                mAttributes[IFD_TYPE_PREVIEW] = new HashMap<>();
             }
         }
 
@@ -3235,8 +3433,8 @@
         // value which has a bigger size than 4 bytes.
         for (int i = 0; i < EXIF_TAGS.length; ++i) {
             int sum = 0;
-            for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
-                final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue();
+            for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) {
+                final ExifAttribute exifAttribute = entry.getValue();
                 final int size = exifAttribute.size();
                 if (size > 4) {
                     sum += size;
@@ -3303,12 +3501,11 @@
 
                 // Write entry info
                 int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
-                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
+                for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) {
                     // Convert tag name to tag number.
-                    final ExifTag tag =
-                            (ExifTag) sExifTagMapsForWriting[ifdType].get(entry.getKey());
+                    final ExifTag tag = sExifTagMapsForWriting[ifdType].get(entry.getKey());
                     final int tagNumber = tag.number;
-                    final ExifAttribute attribute = (ExifAttribute) entry.getValue();
+                    final ExifAttribute attribute = entry.getValue();
                     final int size = attribute.size();
 
                     dataOutputStream.writeUnsignedShort(tagNumber);
@@ -3338,8 +3535,8 @@
                 }
 
                 // Write values of data field exceeding 4 bytes after the next offset.
-                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
-                    ExifAttribute attribute = (ExifAttribute) entry.getValue();
+                for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) {
+                    ExifAttribute attribute = entry.getValue();
 
                     if (attribute.bytes.length > 4) {
                         dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
@@ -3619,6 +3816,7 @@
             return skipped;
         }
 
+        @Override
         public int readUnsignedShort() throws IOException {
             mPosition += 2;
             if (mPosition > mLength) {
@@ -3697,10 +3895,12 @@
             mByteOrder = byteOrder;
         }
 
+        @Override
         public void write(byte[] bytes) throws IOException {
             mOutputStream.write(bytes);
         }
 
+        @Override
         public void write(byte[] bytes, int offset, int length) throws IOException {
             mOutputStream.write(bytes, offset, length);
         }
@@ -3777,7 +3977,7 @@
 
             if (firstImageLengthValue < secondImageLengthValue &&
                     firstImageWidthValue < secondImageWidthValue) {
-                HashMap tempMap = mAttributes[firstIfdType];
+                HashMap<String, ExifAttribute> tempMap = mAttributes[firstIfdType];
                 mAttributes[firstIfdType] = mAttributes[secondIfdType];
                 mAttributes[secondIfdType] = tempMap;
             }
diff --git a/exifinterface/tests/AndroidManifest.xml b/exifinterface/tests/AndroidManifest.xml
index a74fd33..91e9859 100644
--- a/exifinterface/tests/AndroidManifest.xml
+++ b/exifinterface/tests/AndroidManifest.xml
@@ -15,18 +15,9 @@
    limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.support.media.test">
+          package="android.support.media.test">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
-    <uses-sdk android:minSdkVersion="9"/>
-    <application>
-        <uses-library android:name="android.test.runner"/>
-    </application>
-
-    <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="android.support.media.test"/>
-
 </manifest>
\ No newline at end of file
diff --git a/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java b/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java
index ee9e38e..07ba52d 100644
--- a/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java
+++ b/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java
@@ -25,9 +25,11 @@
 
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.location.Location;
 import android.os.Environment;
 import android.support.exifinterface.test.R;
 import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
@@ -46,6 +48,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test {@link ExifInterface}.
@@ -66,6 +71,8 @@
 
     private static final String TEST_TEMP_FILE_NAME = "testImage";
     private static final double DELTA = 1e-8;
+    // We translate double to rational in a 1/10000 precision.
+    private static final double RATIONAL_DELTA = 0.0001;
     private static final int TEST_LAT_LONG_VALUES_ARRAY_LENGTH = 8;
     private static final double[] TEST_LATITUDE_VALID_VALUES = new double[]
             {0, 45, 90, -60, 0.00000001, -89.999999999, 14.2465923626, -68.3434534737};
@@ -77,12 +84,97 @@
     private static final double[] TEST_LONGITUDE_INVALID_VALUES = new double[]
             {Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 180.0000000001,
                     263.34763236326, -1e10, 347.325252623, -4000.346323236};
+    private static final double[] TEST_ALTITUDE_VALUES = new double[]
+            {0, -2000, 10000, -355.99999999999, 18.02038};
+    private static final int[][] TEST_ROTATION_STATE_MACHINE = {
+            {ExifInterface.ORIENTATION_UNDEFINED, -90, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_UNDEFINED, 0, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_UNDEFINED, 90, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_UNDEFINED, 180, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_UNDEFINED, 270, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_UNDEFINED, 540, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_NORMAL, -90, ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_NORMAL, 0, ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_NORMAL, 90, ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_NORMAL, 180,ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_NORMAL, 270,ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_NORMAL, 540,ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_ROTATE_90, -90, ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_ROTATE_90, 0, ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_ROTATE_90, 90, ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_ROTATE_90, 180,ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_ROTATE_90, 270,ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_ROTATE_90, 540,ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_ROTATE_180, -90, ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_ROTATE_180, 0, ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_ROTATE_180, 90, ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_ROTATE_180, 180,ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_ROTATE_180, 270,ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_ROTATE_180, 540,ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_ROTATE_270, -90, ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_ROTATE_270, 0, ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_ROTATE_270, 90, ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_ROTATE_270, 180,ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_ROTATE_270, 270,ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_ROTATE_270, 540,ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, -90, ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, 0, ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, 90, ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, 180,
+                    ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, 270,ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, 540,
+                    ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, -90, ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, 0,
+                    ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, 90, ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, 180,
+                    ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, 270, ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, 540,
+                    ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_TRANSPOSE, -90, ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_TRANSPOSE, 0, ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_TRANSPOSE, 90, ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_TRANSPOSE, 180,ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_TRANSPOSE, 270,ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_TRANSPOSE, 540,ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_TRANSVERSE, -90, ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_TRANSVERSE, 0, ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_TRANSVERSE, 90, ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_TRANSVERSE, 180,ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_TRANSVERSE, 270,ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_TRANSVERSE, 540,ExifInterface.ORIENTATION_TRANSPOSE},
+    };
+    private static final int[][] TEST_FLIP_VERTICALLY_STATE_MACHINE = {
+            {ExifInterface.ORIENTATION_UNDEFINED, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_ROTATE_270, ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_TRANSPOSE, ExifInterface.ORIENTATION_ROTATE_270},
+            {ExifInterface.ORIENTATION_TRANSVERSE, ExifInterface.ORIENTATION_ROTATE_90}
+    };
+    private static final int[][] TEST_FLIP_HORIZONTALLY_STATE_MACHINE = {
+            {ExifInterface.ORIENTATION_UNDEFINED, ExifInterface.ORIENTATION_UNDEFINED},
+            {ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_FLIP_HORIZONTAL},
+            {ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_TRANSPOSE},
+            {ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_FLIP_VERTICAL},
+            {ExifInterface.ORIENTATION_ROTATE_270, ExifInterface.ORIENTATION_TRANSVERSE},
+            {ExifInterface.ORIENTATION_FLIP_VERTICAL, ExifInterface.ORIENTATION_ROTATE_180},
+            {ExifInterface.ORIENTATION_FLIP_HORIZONTAL, ExifInterface.ORIENTATION_NORMAL},
+            {ExifInterface.ORIENTATION_TRANSPOSE, ExifInterface.ORIENTATION_ROTATE_90},
+            {ExifInterface.ORIENTATION_TRANSVERSE, ExifInterface.ORIENTATION_ROTATE_270}
+    };
 
     private static final String[] EXIF_TAGS = {
             ExifInterface.TAG_MAKE,
             ExifInterface.TAG_MODEL,
             ExifInterface.TAG_F_NUMBER,
-            ExifInterface.TAG_DATETIME,
+            ExifInterface.TAG_DATETIME_ORIGINAL,
             ExifInterface.TAG_EXPOSURE_TIME,
             ExifInterface.TAG_FLASH,
             ExifInterface.TAG_FOCAL_LENGTH,
@@ -118,7 +210,7 @@
         public final String make;
         public final String model;
         public final float aperture;
-        public final String datetime;
+        public final String dateTimeOriginal;
         public final float exposureTime;
         public final float flash;
         public final String focalLength;
@@ -161,7 +253,7 @@
             make = getString(typedArray, 7);
             model = getString(typedArray, 8);
             aperture = typedArray.getFloat(9, 0f);
-            datetime = getString(typedArray, 10);
+            dateTimeOriginal = getString(typedArray, 10);
             exposureTime = typedArray.getFloat(11, 0f);
             flash = typedArray.getFloat(12, 0f);
             focalLength = getString(typedArray, 13);
@@ -236,21 +328,122 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDoNotFailOnCorruptedImage() throws Throwable {
-        // To keep the compatibility with old versions of ExifInterface, even on a corrupted image,
-        // it shouldn't raise any exceptions except an IOException when unable to open a file.
-        byte[] bytes = new byte[1024];
-        try {
-            new ExifInterface(new ByteArrayInputStream(bytes));
-            // Always success
-        } catch (IOException e) {
-            fail("Should not reach here!");
+        // ExifInterface shouldn't raise any exceptions except an IOException when unable to open
+        // a file, even with a corrupted image. Generates 10,000 randomly corrupted image stream
+        // for testing. Uses Epoch date count as random seed so that we can reproduce a broken test.
+        Random random = new Random(System.currentTimeMillis() / 86400);
+        byte[] bytes = new byte[8096];
+        ByteBuffer buffer = ByteBuffer.wrap(bytes);
+        for (int i = 0; i < 10000; i++) {
+            buffer.clear();
+            random.nextBytes(bytes);
+            if (!randomlyCorrupted(random)) {
+                buffer.put(ExifInterface.JPEG_SIGNATURE);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.put(ExifInterface.MARKER_APP1);
+            }
+            buffer.putShort((short) (random.nextInt(100) + 300));
+            if (!randomlyCorrupted(random)) {
+                buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.put((byte) 0);
+                buffer.put(ExifInterface.START_CODE);
+            }
+            buffer.putInt(8);
+
+            // Primary Tags
+            int numberOfDirectory = random.nextInt(8) + 1;
+            if (!randomlyCorrupted(random)) {
+                buffer.putShort((short) numberOfDirectory);
+            }
+            for (int j = 0; j < numberOfDirectory; j++) {
+                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PRIMARY, random);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.putInt(buffer.position() - 8);
+            }
+
+            // Thumbnail Tags
+            numberOfDirectory = random.nextInt(8) + 1;
+            if (!randomlyCorrupted(random)) {
+                buffer.putShort((short) numberOfDirectory);
+            }
+            for (int j = 0; j < numberOfDirectory; j++) {
+                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_THUMBNAIL, random);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.putInt(buffer.position() - 8);
+            }
+
+            // Preview Tags
+            numberOfDirectory = random.nextInt(8) + 1;
+            if (!randomlyCorrupted(random)) {
+                buffer.putShort((short) numberOfDirectory);
+            }
+            for (int j = 0; j < numberOfDirectory; j++) {
+                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PREVIEW, random);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.putInt(buffer.position() - 8);
+            }
+
+            if (!randomlyCorrupted(random)) {
+                buffer.put(ExifInterface.MARKER);
+            }
+            if (!randomlyCorrupted(random)) {
+                buffer.put(ExifInterface.MARKER_EOI);
+            }
+
+            try {
+                new ExifInterface(new ByteArrayInputStream(bytes));
+                // Always success
+            } catch (IOException e) {
+                fail("Should not reach here!");
+            }
         }
     }
 
     @Test
     @SmallTest
+    public void testSetGpsInfo() throws IOException {
+        final String provider = "ExifInterfaceTest";
+        final long timestamp = System.currentTimeMillis();
+        final float speedInMeterPerSec = 36.627533f;
+        Location location = new Location(provider);
+        location.setLatitude(TEST_LATITUDE_VALID_VALUES[TEST_LATITUDE_VALID_VALUES.length - 1]);
+        location.setLongitude(TEST_LONGITUDE_VALID_VALUES[TEST_LONGITUDE_VALID_VALUES.length - 1]);
+        location.setAltitude(TEST_ALTITUDE_VALUES[TEST_ALTITUDE_VALUES.length - 1]);
+        location.setSpeed(speedInMeterPerSec);
+        location.setTime(timestamp);
+        ExifInterface exif = createTestExifInterface();
+        exif.setGpsInfo(location);
+
+        double[] latLong = exif.getLatLong();
+        assertNotNull(latLong);
+        assertEquals(TEST_LATITUDE_VALID_VALUES[TEST_LATITUDE_VALID_VALUES.length - 1],
+                latLong[0], DELTA);
+        assertEquals(TEST_LONGITUDE_VALID_VALUES[TEST_LONGITUDE_VALID_VALUES.length - 1],
+                latLong[1], DELTA);
+        assertEquals(TEST_ALTITUDE_VALUES[TEST_ALTITUDE_VALUES.length - 1], exif.getAltitude(0),
+                RATIONAL_DELTA);
+        assertEquals("K", exif.getAttribute(ExifInterface.TAG_GPS_SPEED_REF));
+        assertEquals(speedInMeterPerSec, exif.getAttributeDouble(ExifInterface.TAG_GPS_SPEED, 0.0)
+                * 1000 / TimeUnit.HOURS.toSeconds(1), RATIONAL_DELTA);
+        assertEquals(provider, exif.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD));
+        // GPS time's precision is secs.
+        assertEquals(TimeUnit.MILLISECONDS.toSeconds(timestamp),
+                TimeUnit.MILLISECONDS.toSeconds(exif.getGpsDateTime()));
+    }
+
+    @Test
+    @SmallTest
     public void testSetLatLong_withValidValues() throws IOException {
         for (int i = 0; i < TEST_LAT_LONG_VALUES_ARRAY_LENGTH; i++) {
             ExifInterface exif = createTestExifInterface();
@@ -295,6 +488,110 @@
         }
     }
 
+    @Test
+    @SmallTest
+    public void testSetAltitude() throws IOException {
+        for (int i = 0; i < TEST_ALTITUDE_VALUES.length; i++) {
+            ExifInterface exif = createTestExifInterface();
+            exif.setAltitude(TEST_ALTITUDE_VALUES[i]);
+            assertEquals(TEST_ALTITUDE_VALUES[i], exif.getAltitude(Double.NaN), RATIONAL_DELTA);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testSetDateTime() throws IOException {
+        final String dateTimeValue = "2017:02:02 22:22:22";
+        final String dateTimeOriginalValue = "2017:01:01 11:11:11";
+
+        File imageFile = new File(
+                Environment.getExternalStorageDirectory(), EXIF_BYTE_ORDER_II_JPEG);
+        ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
+        exif.setAttribute(ExifInterface.TAG_DATETIME, dateTimeValue);
+        exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTimeOriginalValue);
+        exif.saveAttributes();
+
+        // Check that the DATETIME value is not overwritten by DATETIME_ORIGINAL's value.
+        exif = new ExifInterface(imageFile.getAbsolutePath());
+        assertEquals(dateTimeValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
+        assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL));
+
+        // Now remove the DATETIME value.
+        exif.setAttribute(ExifInterface.TAG_DATETIME, null);
+        exif.saveAttributes();
+
+        // When the DATETIME has no value, then it should be set to DATETIME_ORIGINAL's value.
+        exif = new ExifInterface(imageFile.getAbsolutePath());
+        assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
+
+        long currentTimeStamp = System.currentTimeMillis();
+        exif.setDateTime(currentTimeStamp);
+        exif.saveAttributes();
+        exif = new ExifInterface(imageFile.getAbsolutePath());
+        assertEquals(currentTimeStamp, exif.getDateTime());
+    }
+
+    @Test
+    @SmallTest
+    public void testRotation() throws IOException {
+        File imageFile = new File(
+                Environment.getExternalStorageDirectory(), EXIF_BYTE_ORDER_II_JPEG);
+        ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
+
+        int num;
+        // Test flip vertically.
+        for (num = 0; num < TEST_FLIP_VERTICALLY_STATE_MACHINE.length; num++) {
+            exif.setAttribute(ExifInterface.TAG_ORIENTATION,
+                    Integer.toString(TEST_FLIP_VERTICALLY_STATE_MACHINE[num][0]));
+            exif.flipVertically();
+            exif.saveAttributes();
+            exif = new ExifInterface(imageFile.getAbsolutePath());
+            assertIntTag(exif, ExifInterface.TAG_ORIENTATION,
+                    TEST_FLIP_VERTICALLY_STATE_MACHINE[num][1]);
+
+        }
+
+        // Test flip horizontally.
+        for (num = 0; num < TEST_FLIP_VERTICALLY_STATE_MACHINE.length; num++) {
+            exif.setAttribute(ExifInterface.TAG_ORIENTATION,
+                    Integer.toString(TEST_FLIP_HORIZONTALLY_STATE_MACHINE[num][0]));
+            exif.flipHorizontally();
+            exif.saveAttributes();
+            exif = new ExifInterface(imageFile.getAbsolutePath());
+            assertIntTag(exif, ExifInterface.TAG_ORIENTATION,
+                    TEST_FLIP_HORIZONTALLY_STATE_MACHINE[num][1]);
+
+        }
+
+        // Test rotate by degrees
+        exif.setAttribute(ExifInterface.TAG_ORIENTATION,
+                Integer.toString(ExifInterface.ORIENTATION_NORMAL));
+        try {
+            exif.rotate(108);
+            fail("Rotate with 108 degree should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Success
+        }
+
+        for (num = 0; num < TEST_ROTATION_STATE_MACHINE.length; num++) {
+            exif.setAttribute(ExifInterface.TAG_ORIENTATION,
+                    Integer.toString(TEST_ROTATION_STATE_MACHINE[num][0]));
+            exif.rotate(TEST_ROTATION_STATE_MACHINE[num][1]);
+            exif.saveAttributes();
+            exif = new ExifInterface(imageFile.getAbsolutePath());
+            assertIntTag(exif, ExifInterface.TAG_ORIENTATION, TEST_ROTATION_STATE_MACHINE[num][2]);
+        }
+
+        // Test reset the rotation.
+        exif.setAttribute(ExifInterface.TAG_ORIENTATION,
+                Integer.toString(ExifInterface.ORIENTATION_FLIP_HORIZONTAL));
+        exif.resetOrientation();
+        exif.saveAttributes();
+        exif = new ExifInterface(imageFile.getAbsolutePath());
+        assertIntTag(exif, ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+
+    }
+
     private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
         // Prints thumbnail information.
         if (exifInterface.hasThumbnail()) {
@@ -390,7 +687,8 @@
         assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
         assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
         assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture);
-        assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
+        assertStringTag(exifInterface, ExifInterface.TAG_DATETIME_ORIGINAL,
+                expectedValue.dateTimeOriginal);
         assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
         assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
         assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
@@ -482,6 +780,31 @@
         // about writing back in here.
     }
 
+    private void generateRandomExifTag(ByteBuffer buffer, int ifdType, Random random) {
+        ExifInterface.ExifTag[] tagGroup = ExifInterface.EXIF_TAGS[ifdType];
+        ExifInterface.ExifTag tag = tagGroup[random.nextInt(tagGroup.length)];
+        if (!randomlyCorrupted(random)) {
+            buffer.putShort((short) tag.number);
+        }
+        int dataFormat = random.nextInt(ExifInterface.IFD_FORMAT_NAMES.length);
+        if (!randomlyCorrupted(random)) {
+            buffer.putShort((short) dataFormat);
+        }
+        buffer.putInt(1);
+        int dataLength = ExifInterface.IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
+        if (dataLength > 4) {
+            buffer.putShort((short) random.nextInt(8096 - dataLength));
+            buffer.position(buffer.position() + 2);
+        } else {
+            buffer.position(buffer.position() + 4);
+        }
+    }
+
+    private boolean randomlyCorrupted(Random random) {
+        // Corrupts somewhere in a possibility of 1/500.
+        return random.nextInt(500) == 0;
+    }
+
     private void closeQuietly(Closeable closeable) {
         if (closeable != null) {
             try {
diff --git a/fragment/Android.mk b/fragment/Android.mk
index f55cfb7..cc1b5f8 100644
--- a/fragment/Android.mk
+++ b/fragment/Android.mk
@@ -30,18 +30,12 @@
 LOCAL_MODULE := android-support-fragment
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
-    $(call all-java-files-under, gingerbread) \
-    $(call all-java-files-under, honeycomb) \
-    $(call all-java-files-under, jellybean) \
-    $(call all-java-files-under, api21) \
     $(call all-java-files-under, java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-core-ui \
     android-support-core-utils \
-    android-support-media-compat \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/fragment/AndroidManifest-make.xml b/fragment/AndroidManifest-make.xml
deleted file mode 100644
index 54e61d3..0000000
--- a/fragment/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.fragment">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.fragment"/>
-    <application />
-</manifest>
diff --git a/fragment/AndroidManifest.xml b/fragment/AndroidManifest.xml
index 6dcb960..725fe43 100644
--- a/fragment/AndroidManifest.xml
+++ b/fragment/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.fragment">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.fragment"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.fragment"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
deleted file mode 100644
index 879be4a..0000000
--- a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.annotation.TargetApi;
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.transition.TransitionSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-@RequiresApi(21)
-@TargetApi(21)
-class FragmentTransitionCompat21 {
-
-    /**
-     * Returns a clone of a transition or null if it is null
-     */
-    public static Object cloneTransition(Object transition) {
-        Transition copy = null;
-        if (transition != null) {
-            copy = ((Transition) transition).clone();
-        }
-        return copy;
-    }
-
-    /**
-     * Wraps a transition in a TransitionSet and returns the set. If transition is null, null is
-     * returned.
-     */
-    public static Object wrapTransitionInSet(Object transition) {
-        if (transition == null) {
-            return null;
-        }
-        TransitionSet transitionSet = new TransitionSet();
-        transitionSet.addTransition((Transition) transition);
-        return transitionSet;
-    }
-
-    /**
-     * Finds all children of the shared elements and sets the wrapping TransitionSet
-     * targets to point to those. It also limits transitions that have no targets to the
-     * specific shared elements. This allows developers to target child views of the
-     * shared elements specifically, but this doesn't happen by default.
-     */
-    public static void setSharedElementTargets(Object transitionObj,
-            View nonExistentView, ArrayList<View> sharedViews) {
-        TransitionSet transition = (TransitionSet) transitionObj;
-        final List<View> views = transition.getTargets();
-        views.clear();
-        final int count = sharedViews.size();
-        for (int i = 0; i < count; i++) {
-            final View view = sharedViews.get(i);
-            bfsAddViewChildren(views, view);
-        }
-        views.add(nonExistentView);
-        sharedViews.add(nonExistentView);
-        addTargets(transition, sharedViews);
-    }
-
-    /**
-     * Uses a breadth-first scheme to add startView and all of its children to views.
-     * It won't add a child if it is already in views.
-     */
-    private static void bfsAddViewChildren(final List<View> views, final View startView) {
-        final int startIndex = views.size();
-        if (containedBeforeIndex(views, startView, startIndex)) {
-            return; // This child is already in the list, so all its children are also.
-        }
-        views.add(startView);
-        for (int index = startIndex; index < views.size(); index++) {
-            final View view = views.get(index);
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                final int childCount =  viewGroup.getChildCount();
-                for (int childIndex = 0; childIndex < childCount; childIndex++) {
-                    final View child = viewGroup.getChildAt(childIndex);
-                    if (!containedBeforeIndex(views, child, startIndex)) {
-                        views.add(child);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Does a linear search through views for view, limited to maxIndex.
-     */
-    private static boolean containedBeforeIndex(final List<View> views, final View view,
-            final int maxIndex) {
-        for (int i = 0; i < maxIndex; i++) {
-            if (views.get(i) == view) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Sets a transition epicenter to the rectangle of a given View.
-     */
-    public static void setEpicenter(Object transitionObj, View view) {
-        if (view != null) {
-            Transition transition = (Transition) transitionObj;
-            final Rect epicenter = new Rect();
-            getBoundsOnScreen(view, epicenter);
-
-            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
-                @Override
-                public Rect onGetEpicenter(Transition transition) {
-                    return epicenter;
-                }
-            });
-        }
-    }
-
-    /**
-     * Replacement for view.getBoundsOnScreen because that is not public. This returns a rect
-     * containing the bounds relative to the screen that the view is in.
-     */
-    public static void getBoundsOnScreen(View view, Rect epicenter) {
-        int[] loc = new int[2];
-        view.getLocationOnScreen(loc);
-        epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
-    }
-
-    /**
-     * This method adds views as targets to the transition, but only if the transition
-     * doesn't already have a target. It is best for views to contain one View object
-     * that does not exist in the view hierarchy (state.nonExistentView) so that
-     * when they are removed later, a list match will suffice to remove the targets.
-     * Otherwise, if you happened to have targeted the exact views for the transition,
-     * the replaceTargets call will remove them unexpectedly.
-     */
-    public static void addTargets(Object transitionObj, ArrayList<View> views) {
-        Transition transition = (Transition) transitionObj;
-        if (transition == null) {
-            return;
-        }
-        if (transition instanceof TransitionSet) {
-            TransitionSet set = (TransitionSet) transition;
-            int numTransitions = set.getTransitionCount();
-            for (int i = 0; i < numTransitions; i++) {
-                Transition child = set.getTransitionAt(i);
-                addTargets(child, views);
-            }
-        } else if (!hasSimpleTarget(transition)) {
-            List<View> targets = transition.getTargets();
-            if (isNullOrEmpty(targets)) {
-                // We can just add the target views
-                int numViews = views.size();
-                for (int i = 0; i < numViews; i++) {
-                    transition.addTarget(views.get(i));
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if there are any targets based on ID, transition or type.
-     */
-    private static boolean hasSimpleTarget(Transition transition) {
-        return !isNullOrEmpty(transition.getTargetIds())
-                || !isNullOrEmpty(transition.getTargetNames())
-                || !isNullOrEmpty(transition.getTargetTypes());
-    }
-
-    /**
-     * Simple utility to detect if a list is null or has no elements.
-     */
-    private static boolean isNullOrEmpty(List list) {
-        return list == null || list.isEmpty();
-    }
-
-    /**
-     * Creates a TransitionSet that plays all passed transitions together. Any null
-     * transitions passed will not be added to the set. If all are null, then an empty
-     * TransitionSet will be returned.
-     */
-    public static Object mergeTransitionsTogether(Object transition1, Object transition2,
-            Object transition3) {
-        TransitionSet transitionSet = new TransitionSet();
-        if (transition1 != null) {
-            transitionSet.addTransition((Transition) transition1);
-        }
-        if (transition2 != null) {
-            transitionSet.addTransition((Transition) transition2);
-        }
-        if (transition3 != null) {
-            transitionSet.addTransition((Transition) transition3);
-        }
-        return transitionSet;
-    }
-
-    /**
-     * After the transition completes, the fragment's view is set to GONE and the exiting
-     * views are set to VISIBLE.
-     */
-    public static void scheduleHideFragmentView(Object exitTransitionObj, final View fragmentView,
-            final ArrayList<View> exitingViews) {
-        Transition exitTransition = (Transition) exitTransitionObj;
-        exitTransition.addListener(new Transition.TransitionListener() {
-            @Override
-            public void onTransitionStart(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionEnd(Transition transition) {
-                transition.removeListener(this);
-                fragmentView.setVisibility(View.GONE);
-                final int numViews = exitingViews.size();
-                for (int i = 0; i < numViews; i++) {
-                    exitingViews.get(i).setVisibility(View.VISIBLE);
-                }
-            }
-
-            @Override
-            public void onTransitionCancel(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionPause(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionResume(Transition transition) {
-            }
-        });
-    }
-
-    /**
-     * Combines enter, exit, and shared element transition so that they play in the proper
-     * sequence. First the exit transition plays along with the shared element transition.
-     * When the exit transition completes, the enter transition starts. The shared element
-     * transition can continue running while the enter transition plays.
-     *
-     * @return A TransitionSet with all of enter, exit, and shared element transitions in
-     * it (modulo null values), ordered such that they play in the proper sequence.
-     */
-    public static Object mergeTransitionsInSequence(Object exitTransitionObj,
-            Object enterTransitionObj, Object sharedElementTransitionObj) {
-        // First do exit, then enter, but allow shared element transition to happen
-        // during both.
-        Transition staggered = null;
-        final Transition exitTransition = (Transition) exitTransitionObj;
-        final Transition enterTransition = (Transition) enterTransitionObj;
-        final Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
-        if (exitTransition != null && enterTransition != null) {
-            staggered = new TransitionSet()
-                    .addTransition(exitTransition)
-                    .addTransition(enterTransition)
-                    .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
-        } else if (exitTransition != null) {
-            staggered = exitTransition;
-        } else if (enterTransition != null) {
-            staggered = enterTransition;
-        }
-        if (sharedElementTransition != null) {
-            TransitionSet together = new TransitionSet();
-            if (staggered != null) {
-                together.addTransition(staggered);
-            }
-            together.addTransition(sharedElementTransition);
-            return together;
-        } else {
-            return staggered;
-        }
-    }
-
-    /**
-     * Calls {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)}.
-     */
-    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
-        TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
-    }
-
-    /**
-     * Prepares for setting the shared element names by gathering the names of the incoming
-     * shared elements and clearing them. {@link #setNameOverridesOptimized(View, ArrayList,
-     * ArrayList, ArrayList, Map)} must be called after this to complete setting the shared element
-     * name overrides. This must be called before
-     * {@link #beginDelayedTransition(ViewGroup, Object)}.
-     */
-    public static ArrayList<String> prepareSetNameOverridesOptimized(
-            final ArrayList<View> sharedElementsIn) {
-        final ArrayList<String> names = new ArrayList<>();
-        final int numSharedElements = sharedElementsIn.size();
-        for (int i = 0; i < numSharedElements; i++) {
-            final View view = sharedElementsIn.get(i);
-            names.add(view.getTransitionName());
-            view.setTransitionName(null);
-        }
-        return names;
-    }
-
-    /**
-     * Changes the shared element names for the incoming shared eleemnts to match those of the
-     * outgoing shared elements. This also temporarily clears the shared element names of the
-     * outgoing shared elements. Must be called after
-     * {@link #beginDelayedTransition(ViewGroup, Object)}.
-     */
-    public static void setNameOverridesOptimized(final View sceneRoot,
-            final ArrayList<View> sharedElementsOut, final ArrayList<View> sharedElementsIn,
-            final ArrayList<String> inNames, final Map<String, String> nameOverrides) {
-        final int numSharedElements = sharedElementsIn.size();
-        final ArrayList<String> outNames = new ArrayList<>();
-
-        for (int i = 0; i < numSharedElements; i++) {
-            final View view = sharedElementsOut.get(i);
-            final String name = view.getTransitionName();
-            outNames.add(name);
-            if (name == null) {
-                continue;
-            }
-            view.setTransitionName(null);
-            final String inName = nameOverrides.get(name);
-            for (int j = 0; j < numSharedElements; j++) {
-                if (inName.equals(inNames.get(j))) {
-                    sharedElementsIn.get(j).setTransitionName(name);
-                    break;
-                }
-            }
-        }
-
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                for (int i = 0; i < numSharedElements; i++) {
-                    sharedElementsIn.get(i).setTransitionName(inNames.get(i));
-                    sharedElementsOut.get(i).setTransitionName(outNames.get(i));
-                }
-            }
-        });
-    }
-
-    /**
-     * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
-     * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
-     *                           a normal View or a ViewGroup with
-     *                           {@link android.view.ViewGroup#isTransitionGroup()} true.
-     * @param view The base of the view hierarchy to look in.
-     */
-    public static void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                if (viewGroup.isTransitionGroup()) {
-                    transitioningViews.add(viewGroup);
-                } else {
-                    int count = viewGroup.getChildCount();
-                    for (int i = 0; i < count; i++) {
-                        View child = viewGroup.getChildAt(i);
-                        captureTransitioningViews(transitioningViews, child);
-                    }
-                }
-            } else {
-                transitioningViews.add(view);
-            }
-        }
-    }
-
-    /**
-     * Finds all views that have transition names in the hierarchy under the given view and
-     * stores them in {@code namedViews} map with the name as the key.
-     */
-    public static void findNamedViews(Map<String, View> namedViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            String transitionName = view.getTransitionName();
-            if (transitionName != null) {
-                namedViews.put(transitionName, view);
-            }
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                int count = viewGroup.getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View child = viewGroup.getChildAt(i);
-                    findNamedViews(namedViews, child);
-                }
-            }
-        }
-    }
-
-    public static void setNameOverridesUnoptimized(final View sceneRoot,
-            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                final int numSharedElements = sharedElementsIn.size();
-                for (int i = 0; i < numSharedElements; i++) {
-                    View view = sharedElementsIn.get(i);
-                    String name = view.getTransitionName();
-                    if (name != null) {
-                        String inName = findKeyForValue(nameOverrides, name);
-                        view.setTransitionName(inName);
-                    }
-                }
-            }
-        });
-    }
-
-    /**
-     * Utility to find the String key in {@code map} that maps to {@code value}.
-     */
-    private static String findKeyForValue(Map<String, String> map, String value) {
-        for (Map.Entry<String, String> entry : map.entrySet()) {
-            if (value.equals(entry.getValue())) {
-                return entry.getKey();
-            }
-        }
-        return null;
-    }
-
-    /**
-     * After the transition has started, remove all targets that we added to the transitions
-     * so that the transitions are left in a clean state.
-     */
-    public static void scheduleRemoveTargets(final Object overallTransitionObj,
-            final Object enterTransition, final ArrayList<View> enteringViews,
-            final Object exitTransition, final ArrayList<View> exitingViews,
-            final Object sharedElementTransition, final ArrayList<View> sharedElementsIn) {
-        final Transition overallTransition = (Transition) overallTransitionObj;
-        overallTransition.addListener(new Transition.TransitionListener() {
-            @Override
-            public void onTransitionStart(Transition transition) {
-                if (enterTransition != null) {
-                    replaceTargets(enterTransition, enteringViews, null);
-                }
-                if (exitTransition != null) {
-                    replaceTargets(exitTransition, exitingViews, null);
-                }
-                if (sharedElementTransition != null) {
-                    replaceTargets(sharedElementTransition, sharedElementsIn, null);
-                }
-            }
-
-            @Override
-            public void onTransitionEnd(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionCancel(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionPause(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionResume(Transition transition) {
-            }
-        });
-    }
-
-    /**
-     * Swap the targets for the shared element transition from those Views in sharedElementsOut
-     * to those in sharedElementsIn
-     */
-    public static void swapSharedElementTargets(Object sharedElementTransitionObj,
-            ArrayList<View> sharedElementsOut, ArrayList<View> sharedElementsIn) {
-        TransitionSet sharedElementTransition = (TransitionSet) sharedElementTransitionObj;
-        if (sharedElementTransition != null) {
-            sharedElementTransition.getTargets().clear();
-            sharedElementTransition.getTargets().addAll(sharedElementsIn);
-            replaceTargets(sharedElementTransition, sharedElementsOut, sharedElementsIn);
-        }
-    }
-
-
-    /**
-     * This method removes the views from transitions that target ONLY those views and
-     * replaces them with the new targets list.
-     * The views list should match those added in addTargets and should contain
-     * one view that is not in the view hierarchy (state.nonExistentView).
-     */
-    public static void replaceTargets(Object transitionObj, ArrayList<View> oldTargets,
-            ArrayList<View> newTargets) {
-        Transition transition = (Transition) transitionObj;
-        if (transition instanceof TransitionSet) {
-            TransitionSet set = (TransitionSet) transition;
-            int numTransitions = set.getTransitionCount();
-            for (int i = 0; i < numTransitions; i++) {
-                Transition child = set.getTransitionAt(i);
-                replaceTargets(child, oldTargets, newTargets);
-            }
-        } else if (!hasSimpleTarget(transition)) {
-            List<View> targets = transition.getTargets();
-            if (targets != null && targets.size() == oldTargets.size()
-                    && targets.containsAll(oldTargets)) {
-                // We have an exact match. We must have added these earlier in addTargets
-                final int targetCount = newTargets == null ? 0 : newTargets.size();
-                for (int i = 0; i < targetCount; i++) {
-                    transition.addTarget(newTargets.get(i));
-                }
-                for (int i = oldTargets.size() - 1; i >= 0; i--) {
-                    transition.removeTarget(oldTargets.get(i));
-                }
-            }
-        }
-    }
-
-    /**
-     * Adds a View target to a transition. If transitionObj is null, nothing is done.
-     */
-    public static void addTarget(Object transitionObj, View view) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.addTarget(view);
-        }
-    }
-
-    /**
-     * Remove a View target to a transition. If transitionObj is null, nothing is done.
-     */
-    public static void removeTarget(Object transitionObj, View view) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.removeTarget(view);
-        }
-    }
-
-    /**
-     * Sets the epicenter of a transition to a rect object. The object can be modified until
-     * the transition runs.
-     */
-    public static void setEpicenter(Object transitionObj, final Rect epicenter) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
-                @Override
-                public Rect onGetEpicenter(Transition transition) {
-                    if (epicenter == null || epicenter.isEmpty()) {
-                        return null;
-                    }
-                    return epicenter;
-                }
-            });
-        }
-    }
-
-    public static void scheduleNameReset(final ViewGroup sceneRoot,
-            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                final int numSharedElements = sharedElementsIn.size();
-                for (int i = 0; i < numSharedElements; i++) {
-                    final View view = sharedElementsIn.get(i);
-                    final String name = view.getTransitionName();
-                    final String inName = nameOverrides.get(name);
-                    view.setTransitionName(inName);
-                }
-            }
-        });
-    }
-}
diff --git a/fragment/build.gradle b/fragment/build.gradle
index 7c9098a..b50b691 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -1,102 +1,34 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-fragment'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-media-compat')
-    compile project(':support-core-ui')
-    compile project(':support-core-utils')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-compat')
+    api project(':support-core-ui')
+    api project(':support-core-utils')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = [
-                'gingerbread',
-                'honeycomb',
-                'jellybean',
-                'api21',
-                'java'
-        ]
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+        main.java.srcDirs = ['java']
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-        exclude('android/content/pm/**')
-        exclude('android/service/media/**')
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android Support Library fragment'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
 }
 
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v4'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java b/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
deleted file mode 100644
index 01e9f22..0000000
--- a/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Base class for {@code FragmentActivity} to be able to use Gingerbread APIs.
- *
- * @hide
- */
-@RequiresApi(9)
-@TargetApi(9)
-abstract class BaseFragmentActivityGingerbread extends SupportActivity {
-
-    // We need to keep track of whether startIntentSenderForResult originated from a Fragment, so we
-    // can conditionally check whether the requestCode collides with our reserved ID space for the
-    // request index (see above). Unfortunately we can't just call
-    // super.startIntentSenderForResult(...) to bypass the check when the call didn't come from a
-    // fragment, since we need to use the ActivityCompat version for backward compatibility.
-    boolean mStartedIntentSenderFromFragment;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        if (Build.VERSION.SDK_INT < 11 && getLayoutInflater().getFactory() == null) {
-            // On pre-HC devices we need to manually install ourselves as a Factory.
-            // On HC and above, we are automatically installed as a private factory
-            getLayoutInflater().setFactory(this);
-        }
-
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public View onCreateView(String name, Context context, AttributeSet attrs) {
-        final View v = dispatchFragmentsOnCreateView(null, name, context, attrs);
-        if (v == null) {
-            return super.onCreateView(name, context, attrs);
-        }
-        return v;
-    }
-
-    abstract View dispatchFragmentsOnCreateView(View parent, String name,
-            Context context, AttributeSet attrs);
-
-
-    @Override
-    public void startIntentSenderForResult(IntentSender intent, int requestCode,
-            @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
-            throws IntentSender.SendIntentException {
-        // If this was started from a Fragment we've already checked the upper 16 bits were not in
-        // use, and then repurposed them for the Fragment's index.
-        if (!mStartedIntentSenderFromFragment) {
-            if (requestCode != -1) {
-                checkForValidRequestCode(requestCode);
-            }
-        }
-        super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues,
-                extraFlags);
-    }
-
-    /**
-     * Checks whether the given request code is a valid code by masking it with 0xffff0000. Throws
-     * an {@link IllegalArgumentException} if the code is not valid.
-     */
-    static void checkForValidRequestCode(int requestCode) {
-        if ((requestCode & 0xffff0000) != 0) {
-            throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
-        }
-    }
-}
diff --git a/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java b/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
deleted file mode 100644
index 95f3d8d..0000000
--- a/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.app;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Base class for {@code FragmentActivity} to be able to use v11 APIs.
- *
- * @hide
- */
-abstract class BaseFragmentActivityHoneycomb extends BaseFragmentActivityGingerbread {
-
-    @Override
-    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        final View v = dispatchFragmentsOnCreateView(parent, name, context, attrs);
-        if (v == null && Build.VERSION.SDK_INT >= 11) {
-            // If we're running on HC or above, let the super have a go
-            return super.onCreateView(parent, name, context, attrs);
-        }
-        return v;
-    }
-
-}
diff --git a/fragment/java/android/support/v4/app/BackStackRecord.java b/fragment/java/android/support/v4/app/BackStackRecord.java
index 923c366..5f99d06 100644
--- a/fragment/java/android/support/v4/app/BackStackRecord.java
+++ b/fragment/java/android/support/v4/app/BackStackRecord.java
@@ -42,7 +42,7 @@
     final CharSequence mBreadCrumbShortTitleText;
     final ArrayList<String> mSharedElementSourceNames;
     final ArrayList<String> mSharedElementTargetNames;
-    final boolean mAllowOptimization;
+    final boolean mReorderingAllowed;
 
     public BackStackState(BackStackRecord bse) {
         final int numOps = bse.mOps.size();
@@ -72,7 +72,7 @@
         mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
         mSharedElementSourceNames = bse.mSharedElementSourceNames;
         mSharedElementTargetNames = bse.mSharedElementTargetNames;
-        mAllowOptimization = bse.mAllowOptimization;
+        mReorderingAllowed = bse.mReorderingAllowed;
     }
 
     public BackStackState(Parcel in) {
@@ -87,7 +87,7 @@
         mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mSharedElementSourceNames = in.createStringArrayList();
         mSharedElementTargetNames = in.createStringArrayList();
-        mAllowOptimization = in.readInt() != 0;
+        mReorderingAllowed = in.readInt() != 0;
     }
 
     public BackStackRecord instantiate(FragmentManagerImpl fm) {
@@ -128,7 +128,7 @@
         bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
         bse.mSharedElementSourceNames = mSharedElementSourceNames;
         bse.mSharedElementTargetNames = mSharedElementTargetNames;
-        bse.mAllowOptimization = mAllowOptimization;
+        bse.mReorderingAllowed = mReorderingAllowed;
         bse.bumpBackStackNesting(1);
         return bse;
     }
@@ -151,7 +151,7 @@
         TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
         dest.writeStringList(mSharedElementSourceNames);
         dest.writeStringList(mSharedElementTargetNames);
-        dest.writeInt(mAllowOptimization ? 1 : 0);
+        dest.writeInt(mReorderingAllowed ? 1 : 0);
     }
 
     public static final Parcelable.Creator<BackStackState> CREATOR
@@ -186,6 +186,8 @@
     static final int OP_SHOW = 5;
     static final int OP_DETACH = 6;
     static final int OP_ATTACH = 7;
+    static final int OP_SET_PRIMARY_NAV = 8;
+    static final int OP_UNSET_PRIMARY_NAV = 9;
 
     static final class Op {
         int cmd;
@@ -194,6 +196,14 @@
         int exitAnim;
         int popEnterAnim;
         int popExitAnim;
+
+        Op() {
+        }
+
+        Op(int cmd, Fragment fragment) {
+            this.cmd = cmd;
+            this.fragment = fragment;
+        }
     }
 
     ArrayList<Op> mOps = new ArrayList<>();
@@ -216,7 +226,9 @@
 
     ArrayList<String> mSharedElementSourceNames;
     ArrayList<String> mSharedElementTargetNames;
-    boolean mAllowOptimization = false;
+    boolean mReorderingAllowed = false;
+
+    ArrayList<Runnable> mCommitRunnables;
 
     @Override
     public String toString() {
@@ -292,6 +304,8 @@
                     case OP_SHOW: cmdStr="SHOW"; break;
                     case OP_DETACH: cmdStr="DETACH"; break;
                     case OP_ATTACH: cmdStr="ATTACH"; break;
+                    case OP_SET_PRIMARY_NAV: cmdStr="SET_PRIMARY_NAV"; break;
+                    case OP_UNSET_PRIMARY_NAV: cmdStr="UNSET_PRIMARY_NAV";break;
                     default: cmdStr="cmd=" + op.cmd; break;
                 }
                 writer.print(prefix); writer.print("  Op #"); writer.print(opNum);
@@ -410,10 +424,7 @@
             fragment.mContainerId = fragment.mFragmentId = containerViewId;
         }
 
-        Op op = new Op();
-        op.cmd = opcmd;
-        op.fragment = fragment;
-        addOp(op);
+        addOp(new Op(opcmd, fragment));
     }
 
     @Override
@@ -433,50 +444,42 @@
 
     @Override
     public FragmentTransaction remove(Fragment fragment) {
-        Op op = new Op();
-        op.cmd = OP_REMOVE;
-        op.fragment = fragment;
-        addOp(op);
+        addOp(new Op(OP_REMOVE, fragment));
 
         return this;
     }
 
     @Override
     public FragmentTransaction hide(Fragment fragment) {
-        Op op = new Op();
-        op.cmd = OP_HIDE;
-        op.fragment = fragment;
-        addOp(op);
+        addOp(new Op(OP_HIDE, fragment));
 
         return this;
     }
 
     @Override
     public FragmentTransaction show(Fragment fragment) {
-        Op op = new Op();
-        op.cmd = OP_SHOW;
-        op.fragment = fragment;
-        addOp(op);
+        addOp(new Op(OP_SHOW, fragment));
 
         return this;
     }
 
     @Override
     public FragmentTransaction detach(Fragment fragment) {
-        Op op = new Op();
-        op.cmd = OP_DETACH;
-        op.fragment = fragment;
-        addOp(op);
+        addOp(new Op(OP_DETACH, fragment));
 
         return this;
     }
 
     @Override
     public FragmentTransaction attach(Fragment fragment) {
-        Op op = new Op();
-        op.cmd = OP_ATTACH;
-        op.fragment = fragment;
-        addOp(op);
+        addOp(new Op(OP_ATTACH, fragment));
+
+        return this;
+    }
+
+    @Override
+    public FragmentTransaction setPrimaryNavigationFragment(Fragment fragment) {
+        addOp(new Op(OP_SET_PRIMARY_NAV, fragment));
 
         return this;
     }
@@ -605,6 +608,28 @@
     }
 
     @Override
+    public FragmentTransaction runOnCommit(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable cannot be null");
+        }
+        disallowAddToBackStack();
+        if (mCommitRunnables == null) {
+            mCommitRunnables = new ArrayList<>();
+        }
+        mCommitRunnables.add(runnable);
+        return this;
+    }
+
+    public void runOnCommitRunnables() {
+        if (mCommitRunnables != null) {
+            for (int i = 0, N = mCommitRunnables.size(); i < N; i++) {
+                mCommitRunnables.get(i).run();
+            }
+            mCommitRunnables = null;
+        }
+    }
+
+    @Override
     public int commit() {
         return commitInternal(false);
     }
@@ -627,11 +652,16 @@
     }
 
     @Override
-    public FragmentTransaction setAllowOptimization(boolean allowOptimization) {
-        mAllowOptimization = allowOptimization;
+    public FragmentTransaction setReorderingAllowed(boolean reorderingAllowed) {
+        mReorderingAllowed = reorderingAllowed;
         return this;
     }
 
+    @Override
+    public FragmentTransaction setAllowOptimization(boolean allowOptimization) {
+        return setReorderingAllowed(allowOptimization);
+    }
+
     int commitInternal(boolean allowStateLoss) {
         if (mCommitted) throw new IllegalStateException("commit already called");
         if (FragmentManagerImpl.DEBUG) {
@@ -678,7 +708,8 @@
         final int numOps = mOps.size();
         for (int opNum = 0; opNum < numOps; opNum++) {
             final Op op = mOps.get(opNum);
-            if (op.fragment.mContainerId == containerId) {
+            final int fragContainer = op.fragment != null ? op.fragment.mContainerId : 0;
+            if (fragContainer != 0 && fragContainer == containerId) {
                 return true;
             }
         }
@@ -693,7 +724,7 @@
         int lastContainer = -1;
         for (int opNum = 0; opNum < numOps; opNum++) {
             final Op op = mOps.get(opNum);
-            final int container = op.fragment.mContainerId;
+            final int container = op.fragment != null ? op.fragment.mContainerId : 0;
             if (container != 0 && container != lastContainer) {
                 lastContainer = container;
                 for (int i = startIndex; i < endIndex; i++) {
@@ -701,7 +732,9 @@
                     final int numThoseOps = record.mOps.size();
                     for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
                         final Op thatOp = record.mOps.get(thoseOpIndex);
-                        if (thatOp.fragment.mContainerId == container) {
+                        final int thatContainer = thatOp.fragment != null
+                                ? thatOp.fragment.mContainerId : 0;
+                        if (thatContainer == container) {
                             return true;
                         }
                     }
@@ -720,7 +753,9 @@
         for (int opNum = 0; opNum < numOps; opNum++) {
             final Op op = mOps.get(opNum);
             final Fragment f = op.fragment;
-            f.setNextTransition(mTransition, mTransitionStyle);
+            if (f != null) {
+                f.setNextTransition(mTransition, mTransitionStyle);
+            }
             switch (op.cmd) {
                 case OP_ADD:
                     f.setNextAnim(op.enterAnim);
@@ -746,14 +781,20 @@
                     f.setNextAnim(op.enterAnim);
                     mManager.attachFragment(f);
                     break;
+                case OP_SET_PRIMARY_NAV:
+                    mManager.setPrimaryNavigationFragment(f);
+                    break;
+                case OP_UNSET_PRIMARY_NAV:
+                    mManager.setPrimaryNavigationFragment(null);
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
             }
-            if (!mAllowOptimization && op.cmd != OP_ADD) {
+            if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
                 mManager.moveFragmentToExpectedState(f);
             }
         }
-        if (!mAllowOptimization) {
+        if (!mReorderingAllowed) {
             // Added fragments are added at the end to comply with prior behavior.
             mManager.moveToState(mManager.mCurState, true);
         }
@@ -761,16 +802,19 @@
 
     /**
      * Reverses the execution of the operations within this transaction. The Fragment states will
-     * only be modified if optimizations are not allowed.
+     * only be modified if reordering is not allowed.
      *
      * @param moveToState {@code true} if added fragments should be moved to their final state
-     *                    in unoptimized transactions
+     *                    in ordered transactions
      */
     void executePopOps(boolean moveToState) {
         for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
             final Op op = mOps.get(opNum);
             Fragment f = op.fragment;
-            f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+            if (f != null) {
+                f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition),
+                        mTransitionStyle);
+            }
             switch (op.cmd) {
                 case OP_ADD:
                     f.setNextAnim(op.popExitAnim);
@@ -796,28 +840,48 @@
                     f.setNextAnim(op.popExitAnim);
                     mManager.detachFragment(f);
                     break;
+                case OP_SET_PRIMARY_NAV:
+                    mManager.setPrimaryNavigationFragment(null);
+                    break;
+                case OP_UNSET_PRIMARY_NAV:
+                    mManager.setPrimaryNavigationFragment(f);
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
             }
-            if (!mAllowOptimization && op.cmd != OP_REMOVE) {
+            if (!mReorderingAllowed && op.cmd != OP_REMOVE && f != null) {
                 mManager.moveFragmentToExpectedState(f);
             }
         }
-        if (!mAllowOptimization && moveToState) {
+        if (!mReorderingAllowed && moveToState) {
             mManager.moveToState(mManager.mCurState, true);
         }
     }
 
     /**
-     * Removes all OP_REPLACE ops and replaces them with the proper add and remove
-     * operations that are equivalent to the replace. This must be called prior to
-     * {@link #executeOps()} or any other call that operations on mOps.
+     * Expands all meta-ops into their more primitive equivalents. This must be called prior to
+     * {@link #executeOps()} or any other call that operations on mOps for forward navigation.
+     * It should not be called for pop/reverse navigation operations.
+     *
+     * <p>Removes all OP_REPLACE ops and replaces them with the proper add and remove
+     * operations that are equivalent to the replace.</p>
+     *
+     * <p>Adds OP_UNSET_PRIMARY_NAV ops to match OP_SET_PRIMARY_NAV, OP_REMOVE and OP_DETACH
+     * ops so that we can restore the old primary nav fragment later. Since callers call this
+     * method in a loop before running ops from several transactions at once, the caller should
+     * pass the return value from this method as the oldPrimaryNav parameter for the next call.
+     * The first call in such a loop should pass the value of
+     * {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
      *
      * @param added Initialized to the fragments that are in the mManager.mAdded, this
      *              will be modified to contain the fragments that will be in mAdded
      *              after the execution ({@link #executeOps()}.
+     * @param oldPrimaryNav The tracked primary navigation fragment as of the beginning of
+     *                      this set of ops
+     * @return the new oldPrimaryNav fragment after this record's ops would be run
      */
-    void expandReplaceOps(ArrayList<Fragment> added) {
+    @SuppressWarnings("ReferenceEquality")
+    Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
         for (int opNum = 0; opNum < mOps.size(); opNum++) {
             final Op op = mOps.get(opNum);
             switch (op.cmd) {
@@ -826,22 +890,33 @@
                     added.add(op.fragment);
                     break;
                 case OP_REMOVE:
-                case OP_DETACH:
+                case OP_DETACH: {
                     added.remove(op.fragment);
-                    break;
+                    if (op.fragment == oldPrimaryNav) {
+                        mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
+                        opNum++;
+                        oldPrimaryNav = null;
+                    }
+                }
+                break;
                 case OP_REPLACE: {
-                    Fragment f = op.fragment;
-                    int containerId = f.mContainerId;
+                    final Fragment f = op.fragment;
+                    final int containerId = f.mContainerId;
                     boolean alreadyAdded = false;
                     for (int i = added.size() - 1; i >= 0; i--) {
-                        Fragment old = added.get(i);
+                        final Fragment old = added.get(i);
                         if (old.mContainerId == containerId) {
                             if (old == f) {
                                 alreadyAdded = true;
                             } else {
-                                Op removeOp = new Op();
-                                removeOp.cmd = OP_REMOVE;
-                                removeOp.fragment = old;
+                                // This is duplicated from above since we only make
+                                // a single pass for expanding ops. Unset any outgoing primary nav.
+                                if (old == oldPrimaryNav) {
+                                    mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
+                                    opNum++;
+                                    oldPrimaryNav = null;
+                                }
+                                final Op removeOp = new Op(OP_REMOVE, old);
                                 removeOp.enterAnim = op.enterAnim;
                                 removeOp.popEnterAnim = op.popEnterAnim;
                                 removeOp.exitAnim = op.exitAnim;
@@ -861,8 +936,18 @@
                     }
                 }
                 break;
+                case OP_SET_PRIMARY_NAV: {
+                    // It's ok if this is null, that means we will restore to no active
+                    // primary navigation fragment on a pop.
+                    mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, oldPrimaryNav));
+                    opNum++;
+                    // Will be set by the OP_SET_PRIMARY_NAV we inserted before when run
+                    oldPrimaryNav = op.fragment;
+                }
+                break;
             }
         }
+        return oldPrimaryNav;
     }
 
     /**
@@ -871,8 +956,11 @@
      * @param added Initialized to the fragments that are in the mManager.mAdded, this
      *              will be modified to contain the fragments that will be in mAdded
      *              after the execution ({@link #executeOps()}.
+     * @param oldPrimaryNav The tracked primary navigation fragment as of the beginning of
+     *                      this set of ops
+     * @return the new oldPrimaryNav fragment after this record's ops would be popped
      */
-    void trackAddedFragmentsInPop(ArrayList<Fragment> added) {
+    Fragment trackAddedFragmentsInPop(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
         for (int opNum = 0; opNum < mOps.size(); opNum++) {
             final Op op = mOps.get(opNum);
             switch (op.cmd) {
@@ -884,8 +972,15 @@
                 case OP_DETACH:
                     added.add(op.fragment);
                     break;
+                case OP_UNSET_PRIMARY_NAV:
+                    oldPrimaryNav = op.fragment;
+                    break;
+                case OP_SET_PRIMARY_NAV:
+                    oldPrimaryNav = null;
+                    break;
             }
         }
+        return oldPrimaryNav;
     }
 
     boolean isPostponed() {
@@ -909,8 +1004,8 @@
 
     private static boolean isFragmentPostponed(Op op) {
         final Fragment fragment = op.fragment;
-        return (fragment.mAdded && fragment.mView != null && !fragment.mDetached
-                && !fragment.mHidden && fragment.isPostponed());
+        return fragment != null && fragment.mAdded && fragment.mView != null && !fragment.mDetached
+                && !fragment.mHidden && fragment.isPostponed();
     }
 
     @Override
diff --git a/fragment/java/android/support/v4/app/BaseFragmentActivityApi14.java b/fragment/java/android/support/v4/app/BaseFragmentActivityApi14.java
new file mode 100644
index 0000000..f60d6ce
--- /dev/null
+++ b/fragment/java/android/support/v4/app/BaseFragmentActivityApi14.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.util.AttributeSet;
+import android.view.View;
+
+@RequiresApi(14)
+abstract class BaseFragmentActivityApi14 extends SupportActivity {
+
+    // We need to keep track of whether startIntentSenderForResult originated from a Fragment, so we
+    // can conditionally check whether the requestCode collides with our reserved ID space for the
+    // request index (see above). Unfortunately we can't just call
+    // super.startIntentSenderForResult(...) to bypass the check when the call didn't come from a
+    // fragment, since we need to use the ActivityCompat version for backward compatibility.
+    boolean mStartedIntentSenderFromFragment;
+
+    @Override
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        final View v = dispatchFragmentsOnCreateView(parent, name, context, attrs);
+        if (v == null) {
+            return super.onCreateView(parent, name, context, attrs);
+        }
+        return v;
+    }
+
+    @Override
+    public View onCreateView(String name, Context context, AttributeSet attrs) {
+        final View v = dispatchFragmentsOnCreateView(null, name, context, attrs);
+        if (v == null) {
+            return super.onCreateView(name, context, attrs);
+        }
+        return v;
+    }
+
+    abstract View dispatchFragmentsOnCreateView(View parent, String name,
+            Context context, AttributeSet attrs);
+
+    @Override
+    public void startIntentSenderForResult(IntentSender intent, int requestCode,
+            @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+            throws IntentSender.SendIntentException {
+        // If this was started from a Fragment we've already checked the upper 16 bits were not in
+        // use, and then repurposed them for the Fragment's index.
+        if (!mStartedIntentSenderFromFragment) {
+            if (requestCode != -1) {
+                checkForValidRequestCode(requestCode);
+            }
+        }
+        super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues,
+                extraFlags);
+    }
+
+    /**
+     * Checks whether the given request code is a valid code by masking it with 0xffff0000. Throws
+     * an {@link IllegalArgumentException} if the code is not valid.
+     */
+    static void checkForValidRequestCode(int requestCode) {
+        if ((requestCode & 0xffff0000) != 0) {
+            throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
+        }
+    }
+}
diff --git a/fragment/java/android/support/v4/app/BaseFragmentActivityApi16.java b/fragment/java/android/support/v4/app/BaseFragmentActivityApi16.java
new file mode 100644
index 0000000..0af2cec
--- /dev/null
+++ b/fragment/java/android/support/v4/app/BaseFragmentActivityApi16.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Base class for {@code FragmentActivity} to be able to use v16 APIs.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+abstract class BaseFragmentActivityApi16 extends BaseFragmentActivityApi14 {
+
+    // We need to keep track of whether startActivityForResult originated from a Fragment, so we
+    // can conditionally check whether the requestCode collides with our reserved ID space for the
+    // request index (see above). Unfortunately we can't just call
+    // super.startActivityForResult(...) to bypass the check when the call didn't come from a
+    // fragment, since we need to use the ActivityCompat version for backward compatibility.
+    boolean mStartedActivityFromFragment;
+
+    @RequiresApi(16)
+    @Override
+    public void startActivityForResult(Intent intent, int requestCode,
+            @Nullable Bundle options) {
+        // If this was started from a Fragment we've already checked the upper 16 bits were not in
+        // use, and then repurposed them for the Fragment's index.
+        if (!mStartedActivityFromFragment) {
+            if (requestCode != -1) {
+                checkForValidRequestCode(requestCode);
+            }
+        }
+        super.startActivityForResult(intent, requestCode, options);
+    }
+
+    @RequiresApi(16)
+    @Override
+    public void startIntentSenderForResult(IntentSender intent, int requestCode,
+            @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+            Bundle options) throws IntentSender.SendIntentException {
+        // If this was started from a Fragment we've already checked the upper 16 bits were not in
+        // use, and then repurposed them for the Fragment's index.
+        if (!mStartedIntentSenderFromFragment) {
+            if (requestCode != -1) {
+                checkForValidRequestCode(requestCode);
+            }
+        }
+        super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues,
+                extraFlags, options);
+    }
+}
diff --git a/fragment/java/android/support/v4/app/Fragment.java b/fragment/java/android/support/v4/app/Fragment.java
index 8c54d13..f8e3d47 100644
--- a/fragment/java/android/support/v4/app/Fragment.java
+++ b/fragment/java/android/support/v4/app/Fragment.java
@@ -18,6 +18,7 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.animation.Animator;
 import android.app.Activity;
 import android.content.ComponentCallbacks;
 import android.content.Context;
@@ -54,6 +55,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
 
 final class FragmentState implements Parcelable {
     final String mClassName;
@@ -66,9 +68,9 @@
     final boolean mDetached;
     final Bundle mArguments;
     final boolean mHidden;
-    
+
     Bundle mSavedFragmentState;
-    
+
     Fragment mInstance;
 
     public FragmentState(Fragment frag) {
@@ -83,7 +85,7 @@
         mArguments = frag.mArguments;
         mHidden = frag.mHidden;
     }
-    
+
     public FragmentState(Parcel in) {
         mClassName = in.readString();
         mIndex = in.readInt();
@@ -98,15 +100,19 @@
         mSavedFragmentState = in.readBundle();
     }
 
-    public Fragment instantiate(FragmentHostCallback host, Fragment parent,
-            FragmentManagerNonConfig childNonConfig) {
+    public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
+            Fragment parent, FragmentManagerNonConfig childNonConfig) {
         if (mInstance == null) {
             final Context context = host.getContext();
             if (mArguments != null) {
                 mArguments.setClassLoader(context.getClassLoader());
             }
 
-            mInstance = Fragment.instantiate(context, mClassName, mArguments);
+            if (container != null) {
+                mInstance = container.instantiate(context, mClassName, mArguments);
+            } else {
+                mInstance = Fragment.instantiate(context, mClassName, mArguments);
+            }
 
             if (mSavedFragmentState != null) {
                 mSavedFragmentState.setClassLoader(context.getClassLoader());
@@ -149,7 +155,7 @@
         dest.writeInt(mHidden? 1 : 0);
         dest.writeBundle(mSavedFragmentState);
     }
-    
+
     public static final Parcelable.Creator<FragmentState> CREATOR
             = new Parcelable.Creator<FragmentState>() {
         @Override
@@ -191,19 +197,19 @@
     static final int STOPPED = 3;          // Fully created, not started.
     static final int STARTED = 4;          // Created and started, not resumed.
     static final int RESUMED = 5;          // Created started and resumed.
-    
+
     int mState = INITIALIZING;
-    
+
     // When instantiated from saved state, this is the saved state.
     Bundle mSavedFragmentState;
     SparseArray<Parcelable> mSavedViewState;
-    
+
     // Index into active fragment array.
     int mIndex = -1;
-    
+
     // Internal unique name for this fragment;
     String mWho;
-    
+
     // Construction arguments;
     Bundle mArguments;
 
@@ -218,19 +224,23 @@
 
     // True if the fragment is in the list of added fragments.
     boolean mAdded;
-    
+
     // If set this fragment is being removed from its activity.
     boolean mRemoving;
-    
+
     // Set to true if this fragment was instantiated from a layout file.
     boolean mFromLayout;
-    
+
     // Set to true when the view has actually been inflated in its layout.
     boolean mInLayout;
 
     // True if this fragment has been restored from previously saved state.
     boolean mRestored;
 
+    // True if performCreateView has been called and a matching call to performDestroyView
+    // has not yet happened.
+    boolean mPerformedCreateView;
+
     // Number of active back stack entries this fragment is in.
     int mBackStackNesting;
 
@@ -256,19 +266,19 @@
     // was dynamically added to the view hierarchy, or the ID supplied in
     // layout.
     int mFragmentId;
-    
+
     // When a fragment is being dynamically added to the view hierarchy, this
     // is the identifier of the parent container it is being added to.
     int mContainerId;
-    
+
     // The optional named tag for this fragment -- usually used to find
     // fragments that are not part of the layout.
     String mTag;
-    
+
     // Set to true when the app has requested that this fragment be hidden
     // from the user.
     boolean mHidden;
-    
+
     // Set to true when the app has requested that this fragment be deactivated.
     boolean mDetached;
 
@@ -278,7 +288,7 @@
 
     // If set this fragment is being retained across the current config change.
     boolean mRetaining;
-    
+
     // If set this fragment has menu items to contribute.
     boolean mHasMenu;
 
@@ -287,20 +297,20 @@
 
     // Used to verify that subclasses call through to super class.
     boolean mCalled;
-    
+
     // The parent container of the fragment after dynamically added to UI.
     ViewGroup mContainer;
-    
+
     // The View generated for this fragment.
     View mView;
-    
+
     // The real inner view that will save/restore state.
     View mInnerView;
 
     // Whether this fragment should defer starting until after other fragments
     // have been started and their loaders are finished.
     boolean mDeferStart;
-    
+
     // Hint provided by the app that this fragment is currently visible to the user.
     boolean mUserVisibleHint = true;
 
@@ -330,6 +340,11 @@
     // getLayoutInflater()
     LayoutInflater mLayoutInflater;
 
+    // Keep track of whether or not this Fragment has run performCreate(). Retained instance
+    // fragments can have mRetaining set to true without going through creation, so we must
+    // track it separately.
+    boolean mIsCreated;
+
     /**
      * State information that has been retrieved from a fragment instance
      * through {@link FragmentManager#saveFragmentInstanceState(Fragment)
@@ -391,7 +406,7 @@
      * will not be called when the fragment is re-instantiated; instead,
      * arguments can be supplied by the caller with {@link #setArguments}
      * and later retrieved by the Fragment with {@link #getArguments}.
-     * 
+     *
      * <p>Applications should generally not implement a constructor. Prefer
      * {@link #onAttach(Context)} instead. It is the first place application code can run where
      * the fragment is ready to be used - the point where the fragment is actually associated with
@@ -431,10 +446,10 @@
                 clazz = context.getClassLoader().loadClass(fname);
                 sClassMap.put(fname, clazz);
             }
-            Fragment f = (Fragment)clazz.newInstance();
+            Fragment f = (Fragment) clazz.getConstructor().newInstance();
             if (args != null) {
                 args.setClassLoader(f.getClass().getClassLoader());
-                f.mArguments = args;
+                f.setArguments(args);
             }
             return f;
         } catch (ClassNotFoundException e) {
@@ -449,6 +464,12 @@
             throw new InstantiationException("Unable to instantiate fragment " + fname
                     + ": make sure class name exists, is public, and has an"
                     + " empty constructor that is public", e);
+        } catch (NoSuchMethodException e) {
+            throw new InstantiationException("Unable to instantiate fragment " + fname
+                    + ": could not find Fragment constructor", e);
+        } catch (InvocationTargetException e) {
+            throw new InstantiationException("Unable to instantiate fragment " + fname
+                    + ": calling Fragment constructor caused an exception", e);
         }
     }
 
@@ -473,7 +494,7 @@
             return false;
         }
     }
-    
+
     final void restoreViewState(Bundle savedInstanceState) {
         if (mSavedViewState != null) {
             mInnerView.restoreHierarchyState(mSavedViewState);
@@ -513,7 +534,7 @@
     @Override final public int hashCode() {
         return super.hashCode();
     }
-    
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
@@ -533,7 +554,7 @@
         sb.append('}');
         return sb.toString();
     }
-    
+
     /**
      * Return the identifier this fragment is known by.  This is either
      * the android:id value supplied in a layout or the container view ID
@@ -542,24 +563,24 @@
     final public int getId() {
         return mFragmentId;
     }
-    
+
     /**
      * Get the tag name of the fragment, if specified.
      */
     final public String getTag() {
         return mTag;
     }
-    
+
     /**
-     * Supply the construction arguments for this fragment.  This can only
-     * be called before the fragment has been attached to its activity; that
-     * is, you should call it immediately after constructing the fragment.  The
-     * arguments supplied here will be retained across fragment destroy and
+     * Supply the construction arguments for this fragment.
+     * The arguments supplied here will be retained across fragment destroy and
      * creation.
+     * <p>This method cannot be called if the fragment is added to a FragmentManager and
+     * if {@link #isStateSaved()} would return true.</p>
      */
     public void setArguments(Bundle args) {
-        if (mIndex >= 0) {
-            throw new IllegalStateException("Fragment already active");
+        if (mIndex >= 0 && isStateSaved()) {
+            throw new IllegalStateException("Fragment already active and state has been saved");
         }
         mArguments = args;
     }
@@ -573,6 +594,21 @@
     }
 
     /**
+     * Returns true if this fragment is added and its state has already been saved
+     * by its host. Any operations that would change saved state should not be performed
+     * if this method returns true, and some operations such as {@link #setArguments(Bundle)}
+     * will fail.
+     *
+     * @return true if this fragment's state has already been saved by its host
+     */
+    public final boolean isStateSaved() {
+        if (mFragmentManager == null) {
+            return false;
+        }
+        return mFragmentManager.isStateSaved();
+    }
+
+    /**
      * Set the initial saved state that this Fragment should restore itself
      * from when first being constructed, as returned by
      * {@link FragmentManager#saveFragmentInstanceState(Fragment)
@@ -599,7 +635,26 @@
      * @param requestCode Optional request code, for convenience if you
      * are going to call back with {@link #onActivityResult(int, int, Intent)}.
      */
+    @SuppressWarnings("ReferenceEquality")
     public void setTargetFragment(Fragment fragment, int requestCode) {
+        // Don't allow a caller to set a target fragment in another FragmentManager,
+        // but there's a snag: people do set target fragments before fragments get added.
+        // We'll have the FragmentManager check that for validity when we move
+        // the fragments to a valid state.
+        final FragmentManager mine = getFragmentManager();
+        final FragmentManager theirs = fragment != null ? fragment.getFragmentManager() : null;
+        if (mine != null && theirs != null && mine != theirs) {
+            throw new IllegalArgumentException("Fragment " + fragment
+                    + " must share the same FragmentManager to be set as a target fragment");
+        }
+
+        // Don't let someone create a cycle.
+        for (Fragment check = fragment; check != null; check = check.getTargetFragment()) {
+            if (check == this) {
+                throw new IllegalArgumentException("Setting " + fragment + " as the target of "
+                        + this + " would create a target cycle");
+            }
+        }
         mTarget = fragment;
         mTargetRequestCode = requestCode;
     }
@@ -759,7 +814,7 @@
     final public boolean isRemoving() {
         return mRemoving;
     }
-    
+
     /**
      * Return true if the layout is included as part of an activity view
      * hierarchy via the &lt;fragment&gt; tag.  This will always be true when
@@ -778,17 +833,17 @@
     final public boolean isResumed() {
         return mState >= RESUMED;
     }
-    
+
     /**
      * Return true if the fragment is currently visible to the user.  This means
-     * it: (1) has been added, (2) has its view attached to the window, and 
+     * it: (1) has been added, (2) has its view attached to the window, and
      * (3) is not hidden.
      */
     final public boolean isVisible() {
         return isAdded() && !isHidden() && mView != null
                 && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
     }
-    
+
     /**
      * Return true if the fragment has been hidden.  By default fragments
      * are shown.  You can find out about changes to this state with
@@ -820,7 +875,7 @@
      */
     public void onHiddenChanged(boolean hidden) {
     }
-    
+
     /**
      * Control whether a fragment instance is retained across Activity
      * re-creation (such as from a configuration change).  This can only
@@ -838,11 +893,11 @@
     public void setRetainInstance(boolean retain) {
         mRetainInstance = retain;
     }
-    
+
     final public boolean getRetainInstance() {
         return mRetainInstance;
     }
-    
+
     /**
      * Report that this fragment would like to participate in populating
      * the options menu by receiving a call to {@link #onCreateOptionsMenu}
@@ -1186,7 +1241,7 @@
         }
         LayoutInflater result = mHost.onGetLayoutInflater();
         getChildFragmentManager(); // Init if needed; use raw implementation below.
-        LayoutInflaterCompat.setFactory(result, mChildFragmentManager.getLayoutInflaterFactory());
+        LayoutInflaterCompat.setFactory2(result, mChildFragmentManager.getLayoutInflaterFactory());
         return result;
     }
 
@@ -1197,7 +1252,7 @@
      * tag in a layout file.  Note this is <em>before</em> the fragment's
      * {@link #onAttach(Activity)} has been called; all you should do here is
      * parse the attributes and save them away.
-     * 
+     *
      * <p>This is called every time the fragment is inflated, even if it is
      * being inflated into a new instance with saved state.  It typically makes
      * sense to re-parse the parameters each time, to allow them to change with
@@ -1213,7 +1268,7 @@
      * declaration for the styleable used here is:</p>
      *
      * {@sample frameworks/support/samples/Support4Demos/res/values/attrs.xml fragment_arguments}
-     * 
+     *
      * <p>The fragment can then be declared within its activity's content layout
      * through a tag like this:</p>
      *
@@ -1293,17 +1348,49 @@
     }
 
     /**
-     * Called when a fragment loads an animation.
+     * Called when a fragment loads an animation. Note that if
+     * {@link FragmentTransaction#setCustomAnimations(int, int)} was called with
+     * {@link Animator} resources instead of {@link Animation} resources, {@code nextAnim}
+     * will be an animator resource.
+     *
+     * @param transit The value set in {@link FragmentTransaction#setTransition(int)} or 0 if not
+     *                set.
+     * @param enter {@code true} when the fragment is added/attached/shown or {@code false} when
+     *              the fragment is removed/detached/hidden.
+     * @param nextAnim The resource set in
+     *                 {@link FragmentTransaction#setCustomAnimations(int, int)},
+     *                 {@link FragmentTransaction#setCustomAnimations(int, int, int, int)}, or
+     *                 0 if neither was called. The value will depend on the current operation.
      */
     public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
         return null;
     }
 
     /**
+     * Called when a fragment loads an animator. This will be called when
+     * {@link #onCreateAnimation(int, boolean, int)} returns null. Note that if
+     * {@link FragmentTransaction#setCustomAnimations(int, int)} was called with
+     * {@link Animation} resources instead of {@link Animator} resources, {@code nextAnim}
+     * will be an animation resource.
+     *
+     * @param transit The value set in {@link FragmentTransaction#setTransition(int)} or 0 if not
+     *                set.
+     * @param enter {@code true} when the fragment is added/attached/shown or {@code false} when
+     *              the fragment is removed/detached/hidden.
+     * @param nextAnim The resource set in
+     *                 {@link FragmentTransaction#setCustomAnimations(int, int)},
+     *                 {@link FragmentTransaction#setCustomAnimations(int, int, int, int)}, or
+     *                 0 if neither was called. The value will depend on the current operation.
+     */
+    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
+        return null;
+    }
+
+    /**
      * Called to do initial creation of a fragment.  This is called after
      * {@link #onAttach(Activity)} and before
      * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
-     * 
+     *
      * <p>Note that this can be called while the fragment's activity is
      * still in the process of being created.  As such, you can not rely
      * on things like the activity's content view hierarchy being initialized
@@ -1312,7 +1399,7 @@
      *
      * <p>Any restored child fragments will be created before the base
      * <code>Fragment.onCreate</code> method returns.</p>
-     * 
+     *
      * @param savedInstanceState If the fragment is being re-created from
      * a previous saved state, this is the state.
      */
@@ -1358,10 +1445,10 @@
      * This is optional, and non-graphical fragments can return null (which
      * is the default implementation).  This will be called between
      * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}.
-     * 
+     *
      * <p>If you return a View from here, you will later be called in
      * {@link #onDestroyView} when the view is being released.
-     * 
+     *
      * @param inflater The LayoutInflater object that can be used to inflate
      * any views in the fragment,
      * @param container If non-null, this is the parent view that the fragment's
@@ -1369,7 +1456,7 @@
      * but this can be used to generate the LayoutParams of the view.
      * @param savedInstanceState If non-null, this fragment is being re-constructed
      * from a previous saved state as given here.
-     * 
+     *
      * @return Return the View for the fragment's UI, or null.
      */
     @Nullable
@@ -1394,14 +1481,14 @@
     /**
      * Get the root view for the fragment's layout (the one returned by {@link #onCreateView}),
      * if provided.
-     * 
+     *
      * @return The fragment's root view, or null if it has no layout.
      */
     @Nullable
     public View getView() {
         return mView;
     }
-    
+
     /**
      * Called when the fragment's activity has been created and this
      * fragment's view hierarchy instantiated.  It can be used to do final
@@ -1466,7 +1553,7 @@
     public void onResume() {
         mCalled = true;
     }
-    
+
     /**
      * Called to ask the fragment to save its current dynamic state, so it
      * can later be reconstructed in a new instance of its process is
@@ -1523,7 +1610,7 @@
     public void onPause() {
         mCalled = true;
     }
-    
+
     /**
      * Called when the Fragment is no longer started.  This is generally
      * tied to {@link Activity#onStop() Activity.onStop} of the containing
@@ -1539,7 +1626,7 @@
     public void onLowMemory() {
         mCalled = true;
     }
-    
+
     /**
      * Called when the view previously created by {@link #onCreateView} has
      * been detached from the fragment.  The next time the fragment needs
@@ -1553,7 +1640,7 @@
     public void onDestroyView() {
         mCalled = true;
     }
-    
+
     /**
      * Called when the fragment is no longer in use.  This is called
      * after {@link #onStop()} and before {@link #onDetach()}.
@@ -1609,16 +1696,16 @@
     public void onDetach() {
         mCalled = true;
     }
-    
+
     /**
      * Initialize the contents of the Fragment host's standard options menu.  You
      * should place your menu items in to <var>menu</var>.  For this method
      * to be called, you must have first called {@link #setHasOptionsMenu}.  See
      * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu}
      * for more information.
-     * 
+     *
      * @param menu The options menu in which you place your items.
-     * 
+     *
      * @see #setHasOptionsMenu
      * @see #onPrepareOptionsMenu
      * @see #onOptionsItemSelected
@@ -1633,10 +1720,10 @@
      * dynamically modify the contents.  See
      * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu}
      * for more information.
-     * 
+     *
      * @param menu The options menu as last shown or first initialized by
      *             onCreateOptionsMenu().
-     * 
+     *
      * @see #setHasOptionsMenu
      * @see #onCreateOptionsMenu
      */
@@ -1652,7 +1739,7 @@
      */
     public void onDestroyOptionsMenu() {
     }
-    
+
     /**
      * This hook is called whenever an item in your options menu is selected.
      * The default implementation simply returns false to have the normal
@@ -1660,15 +1747,15 @@
      * its Handler as appropriate).  You can use this method for any items
      * for which you would like to do processing without those other
      * facilities.
-     * 
+     *
      * <p>Derived classes should call through to the base class for it to
      * perform the default menu handling.
-     * 
+     *
      * @param item The menu item that was selected.
-     * 
+     *
      * @return boolean Return false to allow normal menu processing to
      *         proceed, true to consume it here.
-     * 
+     *
      * @see #onCreateOptionsMenu
      */
     public boolean onOptionsItemSelected(MenuItem item) {
@@ -1678,13 +1765,13 @@
     /**
      * This hook is called whenever the options menu is being closed (either by the user canceling
      * the menu with the back/menu button, or when an item is selected).
-     *  
+     *
      * @param menu The options menu as last shown or first initialized by
      *             onCreateOptionsMenu().
      */
     public void onOptionsMenuClosed(Menu menu) {
     }
-    
+
     /**
      * Called when a context menu for the {@code view} is about to be shown.
      * Unlike {@link #onCreateOptionsMenu}, this will be called every
@@ -1713,25 +1800,25 @@
      * {@link OnCreateContextMenuListener} on the view to this fragment, so
      * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be
      * called when it is time to show the context menu.
-     * 
+     *
      * @see #unregisterForContextMenu(View)
      * @param view The view that should show a context menu.
      */
     public void registerForContextMenu(View view) {
         view.setOnCreateContextMenuListener(this);
     }
-    
+
     /**
      * Prevents a context menu to be shown for the given view. This method will
      * remove the {@link OnCreateContextMenuListener} on the view.
-     * 
+     *
      * @see #registerForContextMenu(View)
      * @param view The view that should stop showing a context menu.
      */
     public void unregisterForContextMenu(View view) {
         view.setOnCreateContextMenuListener(null);
     }
-    
+
     /**
      * This hook is called whenever an item in a context menu is selected. The
      * default implementation simply returns false to have the normal processing
@@ -1744,7 +1831,7 @@
      * <p>
      * Derived classes should call through to the base class for it to perform
      * the default menu handling.
-     * 
+     *
      * @param item The context menu item that was selected.
      * @return boolean Return false to allow normal context menu processing to
      *         proceed, true to consume it here.
@@ -2055,11 +2142,12 @@
      * independent containers will not interfere with each other's postponement.
      * <p>
      * Calling postponeEnterTransition on Fragments with a null View will not postpone the
-     * transition. Likewise, postponement only works if FragmentTransaction optimizations are
+     * transition. Likewise, postponement only works if
+     * {@link FragmentTransaction#setReorderingAllowed(boolean) FragmentTransaction reordering} is
      * enabled.
      *
      * @see Activity#postponeEnterTransition()
-     * @see FragmentTransaction#setAllowOptimization(boolean)
+     * @see FragmentTransaction#setReorderingAllowed(boolean)
      */
     public void postponeEnterTransition() {
         ensureAnimationInfo().mEnterTransitionPostponed = true;
@@ -2224,6 +2312,11 @@
             public boolean onHasView() {
                 return (mView != null);
             }
+
+            @Override
+            public Fragment instantiate(Context context, String className, Bundle arguments) {
+                return mHost.instantiate(context, className, arguments);
+            }
         }, this);
     }
 
@@ -2234,6 +2327,7 @@
         mState = CREATED;
         mCalled = false;
         onCreate(savedInstanceState);
+        mIsCreated = true;
         if (!mCalled) {
             throw new SuperNotCalledException("Fragment " + this
                     + " did not call through to super.onCreate()");
@@ -2245,6 +2339,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.noteStateNotSaved();
         }
+        mPerformedCreateView = true;
         return onCreateView(inflater, container, savedInstanceState);
     }
 
@@ -2485,6 +2580,7 @@
         if (mLoaderManager != null) {
             mLoaderManager.doReportNextStart();
         }
+        mPerformedCreateView = false;
     }
 
     void performDestroy() {
@@ -2493,6 +2589,7 @@
         }
         mState = INITIALIZING;
         mCalled = false;
+        mIsCreated = false;
         onDestroy();
         if (!mCalled) {
             throw new SuperNotCalledException("Fragment " + this
@@ -2609,6 +2706,17 @@
         ensureAnimationInfo().mAnimatingAway = view;
     }
 
+    void setAnimator(Animator animator) {
+        ensureAnimationInfo().mAnimator = animator;
+    }
+
+    Animator getAnimator() {
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mAnimator;
+    }
+
     int getStateAfterAnimating() {
         if (mAnimationInfo == null) {
             return 0;
@@ -2658,6 +2766,10 @@
         // view that is animating.
         View mAnimatingAway;
 
+        // Non-null if the fragment's view hierarchy is currently animating away with an
+        // animator instead of an animation.
+        Animator mAnimator;
+
         // If mAnimatingAway != null, this is the state we should move to once the
         // animation is done.
         int mStateAfterAnimating;
diff --git a/fragment/java/android/support/v4/app/FragmentActivity.java b/fragment/java/android/support/v4/app/FragmentActivity.java
index 78e5370..3c89a8d 100644
--- a/fragment/java/android/support/v4/app/FragmentActivity.java
+++ b/fragment/java/android/support/v4/app/FragmentActivity.java
@@ -18,13 +18,11 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -34,7 +32,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
-import android.support.v4.media.session.MediaControllerCompat;
 import android.support.v4.util.SimpleArrayMap;
 import android.support.v4.util.SparseArrayCompat;
 import android.util.AttributeSet;
@@ -43,7 +40,6 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.Window;
 
 import java.io.FileDescriptor;
@@ -64,21 +60,11 @@
  * <li> <p>When using the <code>&lt;fragment></code> tag, this implementation can not
  * use the parent view's ID as the new fragment's ID.  You must explicitly
  * specify an ID (or tag) in the <code>&lt;fragment></code>.</p>
- * <li> <p>Prior to Honeycomb (3.0), an activity's state was saved before pausing.
- * Fragments are a significant amount of new state, and dynamic enough that one
- * often wants them to change between pausing and stopping.  These classes
- * throw an exception if you try to change the fragment state after it has been
- * saved, to avoid accidental loss of UI state.  However this is too restrictive
- * prior to Honeycomb, where the state is saved before pausing.  To address this,
- * when running on platforms prior to Honeycomb an exception will not be thrown
- * if you change fragments between the state save and the activity being stopped.
- * This means that in some cases if the activity is restored from its last saved
- * state, this may be a snapshot slightly before what the user last saw.</p>
  * </ul>
  */
-public class FragmentActivity extends BaseFragmentActivityJB implements
+public class FragmentActivity extends BaseFragmentActivityApi16 implements
         ActivityCompat.OnRequestPermissionsResultCallback,
-        ActivityCompatApi23.RequestPermissionsRequestCodeValidator {
+        ActivityCompat.RequestPermissionsRequestCodeValidator {
     private static final String TAG = "FragmentActivity";
 
     static final String FRAGMENTS_TAG = "android:support:fragments";
@@ -117,7 +103,6 @@
     boolean mReallyStopped = true;
     boolean mRetaining;
 
-    boolean mOptionsMenuInvalidated;
     boolean mRequestedPermissionsFromFragment;
 
     // A hint for the next candidate request index. Request indicies are ints between 0 and 2^16-1
@@ -175,49 +160,21 @@
      */
     @Override
     public void onBackPressed() {
-        if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
+        FragmentManager fragmentManager = mFragments.getSupportFragmentManager();
+        final boolean isStateSaved = fragmentManager.isStateSaved();
+        if (isStateSaved && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
+            // Older versions will throw an exception from the framework
+            // FragmentManager.popBackStackImmediate(), so we'll just
+            // return here. The Activity is likely already on its way out
+            // since the fragmentManager has already been saved.
+            return;
+        }
+        if (isStateSaved || !fragmentManager.popBackStackImmediate()) {
             super.onBackPressed();
         }
     }
 
     /**
-     * Sets a {@link MediaControllerCompat} for later retrieval via
-     * {@link #getSupportMediaController()}.
-     *
-     * <p>On API 21 and later, this controller will be tied to the window of the activity and
-     * media key and volume events which are received while the Activity is in the foreground
-     * will be forwarded to the controller and used to invoke transport controls or adjust the
-     * volume. Prior to API 21, the global handling of media key and volume events through an
-     * active {@link android.support.v4.media.session.MediaSessionCompat} and media button receiver
-     * will still be respected.</p>
-     *
-     * @param mediaController The controller for the session which should receive
-     *     media keys and volume changes on API 21 and later.
-     * @see #getSupportMediaController()
-     * @see #setMediaController(android.media.session.MediaController)
-     * @deprecated Use {@link MediaControllerCompat#setMediaController} instead. This API will be
-     * removed in a future release.
-     */
-    @Deprecated
-    final public void setSupportMediaController(MediaControllerCompat mediaController) {
-        MediaControllerCompat.setMediaController(this, mediaController);
-    }
-
-    /**
-     * Retrieves the current {@link MediaControllerCompat} for sending media key and volume events.
-     *
-     * @return The controller which should receive events.
-     * @see #setSupportMediaController(MediaControllerCompat)
-     * @see #getMediaController()
-     * @deprecated Use {@link MediaControllerCompat#getMediaController} instead. This API will be
-     * removed in a future release.
-     */
-    @Deprecated
-    final public MediaControllerCompat getSupportMediaController() {
-        return MediaControllerCompat.getMediaController(this);
-    }
-
-    /**
      * Reverses the Activity Scene entry Transition and triggers the calling Activity
      * to reverse its exit Transition. When the exit Transition completes,
      * {@link #finish()} is called. If no entry Transition was used, finish() is called
@@ -280,6 +237,7 @@
      *
      * @param isInMultiWindowMode True if the activity is in multi-window mode.
      */
+    @Override
     @CallSuper
     public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
         mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode);
@@ -294,6 +252,7 @@
      *
      * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode.
      */
+    @Override
     @CallSuper
     public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
         mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
@@ -361,13 +320,7 @@
         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
             boolean show = super.onCreatePanelMenu(featureId, menu);
             show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
-            if (android.os.Build.VERSION.SDK_INT >= 11) {
-                return show;
-            }
-            // Prior to Honeycomb, the framework can't invalidate the options
-            // menu, so we must always say we have one in case the app later
-            // invalidates it and needs to have it shown.
-            return true;
+            return show;
         }
         return super.onCreatePanelMenu(featureId, menu);
     }
@@ -467,6 +420,7 @@
     /**
      * Hook in to note that fragment state is no longer saved.
      */
+    @Override
     public void onStateNotSaved() {
         mFragments.noteStateNotSaved();
     }
@@ -515,11 +469,6 @@
     @Override
     public boolean onPreparePanel(int featureId, View view, Menu menu) {
         if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
-            if (mOptionsMenuInvalidated) {
-                mOptionsMenuInvalidated = false;
-                menu.clear();
-                onCreatePanelMenu(featureId, menu);
-            }
             boolean goforit = onPrepareOptionsPanel(view, menu);
             goforit |= mFragments.dispatchPrepareOptionsMenu(menu);
             return goforit;
@@ -656,18 +605,12 @@
      * <p>Invalidate the activity's options menu. This will cause relevant presentations
      * of the menu to fully update via calls to onCreateOptionsMenu and
      * onPrepareOptionsMenu the next time the menu is requested.
+     *
+     * @deprecated Call {@link Activity#invalidateOptionsMenu} directly.
      */
+    @Deprecated
     public void supportInvalidateOptionsMenu() {
-        if (android.os.Build.VERSION.SDK_INT >= 11) {
-            // If we are running on HC or greater, we can use the framework
-            // API to invalidate the options menu.
-            ActivityCompatHoneycomb.invalidateOptionsMenu(this);
-            return;
-        }
-
-        // Whoops, older platform...  we'll use a hack, to manually rebuild
-        // the options menu the next time it is prepared.
-        mOptionsMenuInvalidated = true;
+        invalidateOptionsMenu();
     }
 
     /**
@@ -680,11 +623,9 @@
      * closed for you after you return.
      * @param args additional arguments to the dump request.
      */
+    @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (Build.VERSION.SDK_INT >= 11) {
-            // XXX This can only work if we can call the super-class impl. :/
-            //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args);
-        }
+        super.dump(prefix, fd, writer, args);
         writer.print(prefix); writer.print("Local FragmentActivity ");
                 writer.print(Integer.toHexString(System.identityHashCode(this)));
                 writer.println(" State:");
@@ -696,95 +637,6 @@
                 writer.println(mReallyStopped);
         mFragments.dumpLoaders(innerPrefix, fd, writer, args);
         mFragments.getSupportFragmentManager().dump(prefix, fd, writer, args);
-        writer.print(prefix); writer.println("View Hierarchy:");
-        dumpViewHierarchy(prefix + "  ", writer, getWindow().getDecorView());
-    }
-
-    private static String viewToString(View view) {
-        StringBuilder out = new StringBuilder(128);
-        out.append(view.getClass().getName());
-        out.append('{');
-        out.append(Integer.toHexString(System.identityHashCode(view)));
-        out.append(' ');
-        switch (view.getVisibility()) {
-            case View.VISIBLE: out.append('V'); break;
-            case View.INVISIBLE: out.append('I'); break;
-            case View.GONE: out.append('G'); break;
-            default: out.append('.'); break;
-        }
-        out.append(view.isFocusable() ? 'F' : '.');
-        out.append(view.isEnabled() ? 'E' : '.');
-        out.append(view.willNotDraw() ? '.' : 'D');
-        out.append(view.isHorizontalScrollBarEnabled()? 'H' : '.');
-        out.append(view.isVerticalScrollBarEnabled() ? 'V' : '.');
-        out.append(view.isClickable() ? 'C' : '.');
-        out.append(view.isLongClickable() ? 'L' : '.');
-        out.append(' ');
-        out.append(view.isFocused() ? 'F' : '.');
-        out.append(view.isSelected() ? 'S' : '.');
-        out.append(view.isPressed() ? 'P' : '.');
-        out.append(' ');
-        out.append(view.getLeft());
-        out.append(',');
-        out.append(view.getTop());
-        out.append('-');
-        out.append(view.getRight());
-        out.append(',');
-        out.append(view.getBottom());
-        final int id = view.getId();
-        if (id != View.NO_ID) {
-            out.append(" #");
-            out.append(Integer.toHexString(id));
-            final Resources r = view.getResources();
-            if (id != 0 && r != null) {
-                try {
-                    String pkgname;
-                    switch (id&0xff000000) {
-                        case 0x7f000000:
-                            pkgname="app";
-                            break;
-                        case 0x01000000:
-                            pkgname="android";
-                            break;
-                        default:
-                            pkgname = r.getResourcePackageName(id);
-                            break;
-                    }
-                    String typename = r.getResourceTypeName(id);
-                    String entryname = r.getResourceEntryName(id);
-                    out.append(" ");
-                    out.append(pkgname);
-                    out.append(":");
-                    out.append(typename);
-                    out.append("/");
-                    out.append(entryname);
-                } catch (Resources.NotFoundException e) {
-                }
-            }
-        }
-        out.append("}");
-        return out.toString();
-    }
-
-    private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
-        writer.print(prefix);
-        if (view == null) {
-            writer.println("null");
-            return;
-        }
-        writer.println(viewToString(view));
-        if (!(view instanceof ViewGroup)) {
-            return;
-        }
-        ViewGroup grp = (ViewGroup)view;
-        final int N = grp.getChildCount();
-        if (N <= 0) {
-            return;
-        }
-        prefix = prefix + "  ";
-        for (int i=0; i<N; i++) {
-            dumpViewHierarchy(prefix, writer, grp.getChildAt(i));
-        }
     }
 
     void doReallyStop(boolean retaining) {
@@ -1010,7 +862,6 @@
             super(FragmentActivity.this /*fragmentActivity*/);
         }
 
-        @SuppressLint("NewApi")
         @Override
         public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
             FragmentActivity.this.dump(prefix, fd, writer, args);
diff --git a/fragment/java/android/support/v4/app/FragmentContainer.java b/fragment/java/android/support/v4/app/FragmentContainer.java
index 1367540..d15953b 100644
--- a/fragment/java/android/support/v4/app/FragmentContainer.java
+++ b/fragment/java/android/support/v4/app/FragmentContainer.java
@@ -1,5 +1,7 @@
 package android.support.v4.app;
 
+import android.content.Context;
+import android.os.Bundle;
 import android.support.annotation.IdRes;
 import android.support.annotation.Nullable;
 import android.view.View;
@@ -20,4 +22,14 @@
      * Return {@code true} if the container holds any view.
      */
     public abstract boolean onHasView();
+
+
+    /**
+     * Creates an instance of the specified fragment, can be overridden to construct fragments
+     * with dependencies, or change the fragment being constructed. By default just calls
+     * {@link Fragment#instantiate(Context, String, Bundle)}.
+     */
+    public Fragment instantiate(Context context, String className, Bundle arguments) {
+        return Fragment.instantiate(context, className, arguments);
+    }
 }
diff --git a/fragment/java/android/support/v4/app/FragmentHostCallback.java b/fragment/java/android/support/v4/app/FragmentHostCallback.java
index 115425c..7dc9f59 100644
--- a/fragment/java/android/support/v4/app/FragmentHostCallback.java
+++ b/fragment/java/android/support/v4/app/FragmentHostCallback.java
@@ -350,8 +350,7 @@
 
     void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
         if (loaderManagers != null) {
-            final int numLoaderManagers = loaderManagers.size();
-            for (int i = 0; i < numLoaderManagers; i++) {
+            for (int i = 0, N = loaderManagers.size(); i < N; i++) {
                 ((LoaderManagerImpl) loaderManagers.valueAt(i)).updateHostController(this);
             }
         }
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index 5f618bb..288fe57 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -18,6 +18,12 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources.NotFoundException;
@@ -29,14 +35,13 @@
 import android.os.Parcelable;
 import android.support.annotation.CallSuper;
 import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StringRes;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.util.ArraySet;
 import android.support.v4.util.DebugUtils;
 import android.support.v4.util.LogWriter;
 import android.support.v4.util.Pair;
-import android.support.v4.view.LayoutInflaterFactory;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -391,6 +396,20 @@
     public abstract void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb);
 
     /**
+     * Return the currently active primary navigation fragment for this FragmentManager.
+     * The primary navigation fragment is set by fragment transactions using
+     * {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}.
+     *
+     * <p>The primary navigation fragment's
+     * {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
+     * to process delegated navigation actions such as {@link #popBackStack()} if no ID
+     * or transaction name is provided to pop to.</p>
+     *
+     * @return the fragment designated as the primary navigation fragment
+     */
+    public abstract Fragment getPrimaryNavigationFragment();
+
+    /**
      * Print the FragmentManager's state into the given stream.
      *
      * @param prefix Text to print at the front of each line.
@@ -410,14 +429,27 @@
     }
 
     /**
+     * Returns {@code true} if the FragmentManager's state has already been saved
+     * by its host. Any operations that would change saved state should not be performed
+     * if this method returns true. For example, any popBackStack() method, such as
+     * {@link #popBackStackImmediate()} or any FragmentTransaction using
+     * {@link FragmentTransaction#commit()} instead of
+     * {@link FragmentTransaction#commitAllowingStateLoss()} will change
+     * the state and will result in an error.
+     *
+     * @return true if this FragmentManager's state has already been saved by its host
+     */
+    public abstract boolean isStateSaved();
+
+    /**
      * Callback interface for listening to fragment state changes that happen
      * within a given FragmentManager.
      */
     public abstract static class FragmentLifecycleCallbacks {
         /**
          * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
-         * This is a good time to inject any required dependencies for the fragment before any of
-         * the fragment's lifecycle methods are invoked.
+         * This is a good time to inject any required dependencies or perform other configuration
+         * for the fragment before any of the fragment's lifecycle methods are invoked.
          *
          * @param fm Host FragmentManager
          * @param f Fragment changing state
@@ -436,6 +468,18 @@
         public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}
 
         /**
+         * Called right before the fragment's {@link Fragment#onCreate(Bundle)} method is called.
+         * This is a good time to inject any required dependencies or perform other configuration
+         * for the fragment.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         * @param savedInstanceState Saved instance bundle from a previous instance
+         */
+        public void onFragmentPreCreated(FragmentManager fm, Fragment f,
+                Bundle savedInstanceState) {}
+
+        /**
          * Called after the fragment has returned from the FragmentManager's call to
          * {@link Fragment#onCreate(Bundle)}. This will only happen once for any given
          * fragment instance, though the fragment may be attached and detached multiple times.
@@ -549,6 +593,7 @@
     FragmentState[] mActive;
     int[] mAdded;
     BackStackState[] mBackStack;
+    int mPrimaryNavActiveIndex = -1;
     int mNextFragmentIndex;
 
     public FragmentManagerState() {
@@ -558,6 +603,7 @@
         mActive = in.createTypedArray(FragmentState.CREATOR);
         mAdded = in.createIntArray();
         mBackStack = in.createTypedArray(BackStackState.CREATOR);
+        mPrimaryNavActiveIndex = in.readInt();
         mNextFragmentIndex = in.readInt();
     }
 
@@ -571,6 +617,7 @@
         dest.writeTypedArray(mActive, flags);
         dest.writeIntArray(mAdded);
         dest.writeTypedArray(mBackStack, flags);
+        dest.writeInt(mPrimaryNavActiveIndex);
         dest.writeInt(mNextFragmentIndex);
     }
 
@@ -591,91 +638,21 @@
 /**
  * Container for fragments associated with an activity.
  */
-final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
+final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
     static boolean DEBUG = false;
     static final String TAG = "FragmentManager";
 
-    static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11;
-
     static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
     static final String TARGET_STATE_TAG = "android:target_state";
     static final String VIEW_STATE_TAG = "android:view_state";
     static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
 
-    static class AnimateOnHWLayerIfNeededListener implements AnimationListener {
-        private AnimationListener mOriginalListener;
-        private boolean mShouldRunOnHWLayer;
-        View mView;
-
-        public AnimateOnHWLayerIfNeededListener(final View v, Animation anim) {
-            if (v == null || anim == null) {
-                return;
-            }
-            mView = v;
-        }
-
-        public AnimateOnHWLayerIfNeededListener(final View v, Animation anim,
-                AnimationListener listener) {
-            if (v == null || anim == null) {
-                return;
-            }
-            mOriginalListener = listener;
-            mView = v;
-            mShouldRunOnHWLayer = true;
-        }
-
-        @Override
-        @CallSuper
-        public void onAnimationStart(Animation animation) {
-            if (mOriginalListener != null) {
-                mOriginalListener.onAnimationStart(animation);
-            }
-        }
-
-        @Override
-        @CallSuper
-        public void onAnimationEnd(Animation animation) {
-            if (mView != null && mShouldRunOnHWLayer) {
-                // If we're attached to a window, assume we're in the normal performTraversals
-                // drawing path for Animations running. It's not safe to change the layer type
-                // during drawing, so post it to the View to run later. If we're not attached
-                // or we're running on N and above, post it to the view. If we're not on N and
-                // not attached, do it right now since existing platform versions don't run the
-                // hwui renderer for detached views off the UI thread making changing layer type
-                // safe, but posting may not be.
-                // Prior to N posting to a detached view from a non-Looper thread could cause
-                // leaks, since the thread-local run queue on a non-Looper thread would never
-                // be flushed.
-                if (ViewCompat.isAttachedToWindow(mView) || BuildCompat.isAtLeastN()) {
-                    mView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_NONE, null);
-                        }
-                    });
-                } else {
-                    ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_NONE, null);
-                }
-            }
-            if (mOriginalListener != null) {
-                mOriginalListener.onAnimationEnd(animation);
-            }
-        }
-
-        @Override
-        public void onAnimationRepeat(Animation animation) {
-            if (mOriginalListener != null) {
-                mOriginalListener.onAnimationRepeat(animation);
-            }
-        }
-    }
-
     ArrayList<OpGenerator> mPendingActions;
     boolean mExecutingActions;
 
     int mNextFragmentIndex = 0;
 
-    ArrayList<Fragment> mAdded;
+    final ArrayList<Fragment> mAdded = new ArrayList<>();
     SparseArray<Fragment> mActive;
     ArrayList<BackStackRecord> mBackStack;
     ArrayList<Fragment> mCreatedMenus;
@@ -685,12 +662,14 @@
     ArrayList<Integer> mAvailBackStackIndices;
 
     ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
-    private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
+    private final CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>>
+            mLifecycleCallbacks = new CopyOnWriteArrayList<>();
 
     int mCurState = Fragment.INITIALIZING;
     FragmentHostCallback mHost;
     FragmentContainer mContainer;
     Fragment mParent;
+    Fragment mPrimaryNav;
 
     static Field sAnimationListenerField = null;
 
@@ -700,7 +679,7 @@
     String mNoTransactionsBecause;
     boolean mHavePendingDeferredStart;
 
-    // Temporary vars for optimizing execution of BackStackRecords:
+    // Temporary vars for removing redundant operations in BackStackRecords:
     ArrayList<BackStackRecord> mTmpRecords;
     ArrayList<Boolean> mTmpIsPop;
     ArrayList<Fragment> mTmpAddedFragments;
@@ -712,6 +691,9 @@
     // Postponed transactions.
     ArrayList<StartEnterTransitionListener> mPostponedTransactions;
 
+    // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved()
+    FragmentManagerNonConfig mSavedNonConfig;
+
     Runnable mExecCommit = new Runnable() {
         @Override
         public void run() {
@@ -719,23 +701,51 @@
         }
     };
 
-    static boolean modifiesAlpha(Animation anim) {
-        if (anim instanceof AlphaAnimation) {
+    static boolean modifiesAlpha(AnimationOrAnimator anim) {
+        if (anim.animation instanceof AlphaAnimation) {
             return true;
-        } else if (anim instanceof AnimationSet) {
-            List<Animation> anims = ((AnimationSet) anim).getAnimations();
+        } else if (anim.animation instanceof AnimationSet) {
+            List<Animation> anims = ((AnimationSet) anim.animation).getAnimations();
             for (int i = 0; i < anims.size(); i++) {
                 if (anims.get(i) instanceof AlphaAnimation) {
                     return true;
                 }
             }
+            return false;
+        } else {
+            return modifiesAlpha(anim.animator);
+        }
+    }
+
+    static boolean modifiesAlpha(Animator anim) {
+        if (anim == null) {
+            return false;
+        }
+        if (anim instanceof ValueAnimator) {
+            ValueAnimator valueAnim = (ValueAnimator) anim;
+            PropertyValuesHolder[] values = valueAnim.getValues();
+            for (int i = 0; i < values.length; i++) {
+                if (("alpha").equals(values[i].getPropertyName())) {
+                    return true;
+                }
+            }
+        } else if (anim instanceof AnimatorSet) {
+            List<Animator> animList = ((AnimatorSet) anim).getChildAnimations();
+            for (int i = 0; i < animList.size(); i++) {
+                if (modifiesAlpha(animList.get(i))) {
+                    return true;
+                }
+            }
         }
         return false;
     }
 
-    static boolean shouldRunOnHWLayer(View v, Animation anim) {
+    static boolean shouldRunOnHWLayer(View v, AnimationOrAnimator anim) {
+        if (v == null || anim == null) {
+            return false;
+        }
         return Build.VERSION.SDK_INT >= 19
-                && ViewCompat.getLayerType(v) == ViewCompat.LAYER_TYPE_NONE
+                && v.getLayerType() == View.LAYER_TYPE_NONE
                 && ViewCompat.hasOverlappingRendering(v)
                 && modifiesAlpha(anim);
     }
@@ -824,11 +834,21 @@
         execPendingActions();
         ensureExecReady(true);
 
+        if (mPrimaryNav != null // We have a primary nav fragment
+                && id < 0 // No valid id (since they're local)
+                && name == null) { // no name to pop to (since they're local)
+            final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager();
+            if (childManager != null && childManager.popBackStackImmediate()) {
+                // We did something, just not to this specific FragmentManager. Return true.
+                return true;
+            }
+        }
+
         boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags);
         if (executePop) {
             mExecutingActions = true;
             try {
-                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
             } finally {
                 cleanupExec();
             }
@@ -889,7 +909,7 @@
 
     @Override
     public List<Fragment> getFragments() {
-        if (mAdded == null) {
+        if (mAdded.isEmpty()) {
             return Collections.EMPTY_LIST;
         }
         synchronized (mAdded) {
@@ -982,15 +1002,16 @@
             }
         }
 
-        if (mAdded != null) {
-            N = mAdded.size();
-            if (N > 0) {
-                writer.print(prefix); writer.println("Added Fragments:");
-                for (int i=0; i<N; i++) {
-                    Fragment f = mAdded.get(i);
-                    writer.print(prefix); writer.print("  #"); writer.print(i);
-                            writer.print(": "); writer.println(f.toString());
-                }
+        N = mAdded.size();
+        if (N > 0) {
+            writer.print(prefix); writer.println("Added Fragments:");
+            for (int i = 0; i < N; i++) {
+                Fragment f = mAdded.get(i);
+                writer.print(prefix);
+                writer.print("  #");
+                writer.print(i);
+                writer.print(": ");
+                writer.println(f.toString());
             }
         }
 
@@ -1076,7 +1097,7 @@
 
     static final int ANIM_DUR = 220;
 
-    static Animation makeOpenCloseAnimation(Context context, float startScale,
+    static AnimationOrAnimator makeOpenCloseAnimation(Context context, float startScale,
             float endScale, float startAlpha, float endAlpha) {
         AnimationSet set = new AnimationSet(false);
         ScaleAnimation scale = new ScaleAnimation(startScale, endScale, startScale, endScale,
@@ -1088,28 +1109,66 @@
         alpha.setInterpolator(DECELERATE_CUBIC);
         alpha.setDuration(ANIM_DUR);
         set.addAnimation(alpha);
-        return set;
+        return new AnimationOrAnimator(set);
     }
 
-    static Animation makeFadeAnimation(Context context, float start, float end) {
+    static AnimationOrAnimator makeFadeAnimation(Context context, float start, float end) {
         AlphaAnimation anim = new AlphaAnimation(start, end);
         anim.setInterpolator(DECELERATE_CUBIC);
         anim.setDuration(ANIM_DUR);
-        return anim;
+        return new AnimationOrAnimator(anim);
     }
 
-    Animation loadAnimation(Fragment fragment, int transit, boolean enter,
+    AnimationOrAnimator loadAnimation(Fragment fragment, int transit, boolean enter,
             int transitionStyle) {
-        Animation animObj = fragment.onCreateAnimation(transit, enter, fragment.getNextAnim());
-        if (animObj != null) {
-            return animObj;
+        int nextAnim = fragment.getNextAnim();
+        Animation animation = fragment.onCreateAnimation(transit, enter, nextAnim);
+        if (animation != null) {
+            return new AnimationOrAnimator(animation);
         }
 
-        if (fragment.getNextAnim() != 0) {
-            Animation anim = AnimationUtils.loadAnimation(mHost.getContext(),
-                    fragment.getNextAnim());
-            if (anim != null) {
-                return anim;
+        Animator animator = fragment.onCreateAnimator(transit, enter, nextAnim);
+        if (animator != null) {
+            return new AnimationOrAnimator(animator);
+        }
+
+        if (nextAnim != 0) {
+            String dir = mHost.getContext().getResources().getResourceTypeName(nextAnim);
+            boolean isAnim = "anim".equals(dir);
+            boolean successfulLoad = false;
+            if (isAnim) {
+                // try AnimationUtils first
+                try {
+                    animation = AnimationUtils.loadAnimation(mHost.getContext(), nextAnim);
+                    if (animation != null) {
+                        return new AnimationOrAnimator(animation);
+                    }
+                    // A null animation may be returned and that is acceptable
+                    successfulLoad = true; // succeeded in loading animation, but it is null
+                } catch (NotFoundException e) {
+                    throw e; // Rethrow it -- the resource should be found if it is provided.
+                } catch (RuntimeException e) {
+                    // Other exceptions can occur when loading an Animator from AnimationUtils.
+                }
+            }
+            if (!successfulLoad) {
+                // try Animator
+                try {
+                    animator = AnimatorInflater.loadAnimator(mHost.getContext(), nextAnim);
+                    if (animator != null) {
+                        return new AnimationOrAnimator(animator);
+                    }
+                } catch (RuntimeException e) {
+                    if (isAnim) {
+                        // Rethrow it -- we already tried AnimationUtils and it failed.
+                        throw e;
+                    }
+                    // Otherwise, it is probably an animation resource
+                    animation = AnimationUtils.loadAnimation(mHost.getContext(), nextAnim);
+                    if (animation != null) {
+                        return new AnimationOrAnimator(animation);
+                    }
+                }
             }
         }
 
@@ -1137,6 +1196,7 @@
                 return makeFadeAnimation(mHost.getContext(), 1, 0);
         }
 
+        // TODO: remove or fix transitionStyle -- it apparently never worked.
         if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
             transitionStyle = mHost.onGetWindowAnimations();
         }
@@ -1176,36 +1236,49 @@
      * animations that already have listeners should do the layer change operations
      * in their existing listeners, rather than calling this function.
      */
-    private void setHWLayerAnimListenerIfAlpha(final View v, Animation anim) {
+    private static void setHWLayerAnimListenerIfAlpha(final View v, AnimationOrAnimator anim) {
         if (v == null || anim == null) {
             return;
         }
         if (shouldRunOnHWLayer(v, anim)) {
-            AnimationListener originalListener = null;
-            try {
-                if (sAnimationListenerField == null) {
-                    sAnimationListenerField = Animation.class.getDeclaredField("mListener");
-                    sAnimationListenerField.setAccessible(true);
-                }
-                originalListener = (AnimationListener) sAnimationListenerField.get(anim);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "No field with the name mListener is found in Animation class", e);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Cannot access Animation's mListener field", e);
+            if (anim.animator != null) {
+                anim.animator.addListener(new AnimatorOnHWLayerIfNeededListener(v));
+            } else {
+                AnimationListener originalListener = getAnimationListener(anim.animation);
+                // If there's already a listener set on the animation, we need wrap the new listener
+                // around the existing listener, so that they will both get animation listener
+                // callbacks.
+                v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                anim.animation.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v,
+                        originalListener));
             }
-            // If there's already a listener set on the animation, we need wrap the new listener
-            // around the existing listener, so that they will both get animation listener
-            // callbacks.
-            ViewCompat.setLayerType(v, ViewCompat.LAYER_TYPE_HARDWARE, null);
-            anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, anim,
-                    originalListener));
         }
     }
 
+    /**
+     * Returns an existing AnimationListener on an Animation or {@code null} if none exists.
+     */
+    private static AnimationListener getAnimationListener(Animation animation) {
+        AnimationListener originalListener = null;
+        try {
+            if (sAnimationListenerField == null) {
+                sAnimationListenerField = Animation.class.getDeclaredField("mListener");
+                sAnimationListenerField.setAccessible(true);
+            }
+            originalListener = (AnimationListener) sAnimationListenerField.get(animation);
+        } catch (NoSuchFieldException e) {
+            Log.e(TAG, "No field with the name mListener is found in Animation class", e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Cannot access Animation's mListener field", e);
+        }
+        return originalListener;
+    }
+
     boolean isStateAtLeast(int state) {
         return mCurState >= state;
     }
 
+    @SuppressWarnings("ReferenceEquality")
     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
             boolean keepActive) {
         // Fragments that are not currently added will sit in the onCreate() state.
@@ -1226,90 +1299,96 @@
         if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
             newState = Fragment.STOPPED;
         }
-        if (f.mState < newState) {
+        if (f.mState <= newState) {
             // For fragments that are created from a layout, when restoring from
             // state we don't want to allow them to be created until they are
             // being reloaded from the layout.
             if (f.mFromLayout && !f.mInLayout) {
                 return;
             }
-            if (f.getAnimatingAway() != null) {
+            if (f.getAnimatingAway() != null || f.getAnimator() != null) {
                 // The fragment is currently being animated...  but!  Now we
                 // want to move our state back up.  Give up on waiting for the
                 // animation, move to whatever the final state should be once
                 // the animation is done, and then we can proceed from there.
                 f.setAnimatingAway(null);
+                f.setAnimator(null);
                 moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
             }
             switch (f.mState) {
                 case Fragment.INITIALIZING:
-                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
-                    if (f.mSavedFragmentState != null) {
-                        f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
-                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
-                                FragmentManagerImpl.VIEW_STATE_TAG);
-                        f.mTarget = getFragment(f.mSavedFragmentState,
-                                FragmentManagerImpl.TARGET_STATE_TAG);
-                        if (f.mTarget != null) {
-                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
-                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
-                        }
-                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
-                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
-                        if (!f.mUserVisibleHint) {
-                            f.mDeferStart = true;
-                            if (newState > Fragment.STOPPED) {
-                                newState = Fragment.STOPPED;
+                    if (newState > Fragment.INITIALIZING) {
+                        if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
+                        if (f.mSavedFragmentState != null) {
+                            f.mSavedFragmentState.setClassLoader(mHost.getContext()
+                                    .getClassLoader());
+                            f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
+                                    FragmentManagerImpl.VIEW_STATE_TAG);
+                            f.mTarget = getFragment(f.mSavedFragmentState,
+                                    FragmentManagerImpl.TARGET_STATE_TAG);
+                            if (f.mTarget != null) {
+                                f.mTargetRequestCode = f.mSavedFragmentState.getInt(
+                                        FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
+                            }
+                            f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
+                                    FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
+                            if (!f.mUserVisibleHint) {
+                                f.mDeferStart = true;
+                                if (newState > Fragment.STOPPED) {
+                                    newState = Fragment.STOPPED;
+                                }
                             }
                         }
-                    }
-                    f.mHost = mHost;
-                    f.mParentFragment = mParent;
-                    f.mFragmentManager = mParent != null
-                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
-                    dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
-                    f.mCalled = false;
-                    f.onAttach(mHost.getContext());
-                    if (!f.mCalled) {
-                        throw new SuperNotCalledException("Fragment " + f
-                                + " did not call through to super.onAttach()");
-                    }
-                    if (f.mParentFragment == null) {
-                        mHost.onAttachFragment(f);
-                    } else {
-                        f.mParentFragment.onAttachFragment(f);
-                    }
-                    dispatchOnFragmentAttached(f, mHost.getContext(), false);
 
-                    if (!f.mRetaining) {
-                        f.performCreate(f.mSavedFragmentState);
-                        dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
-                    } else {
-                        f.restoreChildFragmentState(f.mSavedFragmentState);
-                        f.mState = Fragment.CREATED;
-                    }
-                    f.mRetaining = false;
-                    if (f.mFromLayout) {
-                        // For fragments that are part of the content view
-                        // layout, we need to instantiate the view immediately
-                        // and the inflater will take care of adding it.
-                        f.mView = f.performCreateView(f.performGetLayoutInflater(
-                                f.mSavedFragmentState), null, f.mSavedFragmentState);
-                        if (f.mView != null) {
-                            f.mInnerView = f.mView;
-                            if (Build.VERSION.SDK_INT >= 11) {
-                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
-                            } else {
-                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
+                        f.mHost = mHost;
+                        f.mParentFragment = mParent;
+                        f.mFragmentManager = mParent != null
+                                ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
+
+                        // If we have a target fragment, push it along to at least CREATED
+                        // so that this one can rely on it as an initialized dependency.
+                        if (f.mTarget != null) {
+                            if (mActive.get(f.mTarget.mIndex) != f.mTarget) {
+                                throw new IllegalStateException("Fragment " + f
+                                        + " declared target fragment " + f.mTarget
+                                        + " that does not belong to this FragmentManager!");
                             }
-                            if (f.mHidden) f.mView.setVisibility(View.GONE);
-                            f.onViewCreated(f.mView, f.mSavedFragmentState);
-                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
-                        } else {
-                            f.mInnerView = null;
+                            if (f.mTarget.mState < Fragment.CREATED) {
+                                moveToState(f.mTarget, Fragment.CREATED, 0, 0, true);
+                            }
                         }
+
+                        dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
+                        f.mCalled = false;
+                        f.onAttach(mHost.getContext());
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onAttach()");
+                        }
+                        if (f.mParentFragment == null) {
+                            mHost.onAttachFragment(f);
+                        } else {
+                            f.mParentFragment.onAttachFragment(f);
+                        }
+                        dispatchOnFragmentAttached(f, mHost.getContext(), false);
+
+                        if (!f.mIsCreated) {
+                            dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
+                            f.performCreate(f.mSavedFragmentState);
+                            dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
+                        } else {
+                            f.restoreChildFragmentState(f.mSavedFragmentState);
+                            f.mState = Fragment.CREATED;
+                        }
+                        f.mRetaining = false;
                     }
+                    // fall through
                 case Fragment.CREATED:
+                    // This is outside the if statement below on purpose; we want this to run
+                    // even if we do a moveToState from CREATED => *, CREATED => CREATED, and
+                    // * => CREATED as part of the case fallthrough above.
+                    ensureInflatedFragmentView(f);
+
                     if (newState > Fragment.CREATED) {
                         if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                         if (!f.mFromLayout) {
@@ -1341,11 +1420,7 @@
                                     f.mSavedFragmentState), container, f.mSavedFragmentState);
                             if (f.mView != null) {
                                 f.mInnerView = f.mView;
-                                if (Build.VERSION.SDK_INT >= 11) {
-                                    ViewCompat.setSaveFromParentEnabled(f.mView, false);
-                                } else {
-                                    f.mView = NoSaveStateFrameLayout.wrap(f.mView);
-                                }
+                                f.mView.setSaveFromParentEnabled(false);
                                 if (container != null) {
                                     container.addView(f.mView);
                                 }
@@ -1371,16 +1446,19 @@
                         }
                         f.mSavedFragmentState = null;
                     }
+                    // fall through
                 case Fragment.ACTIVITY_CREATED:
                     if (newState > Fragment.ACTIVITY_CREATED) {
                         f.mState = Fragment.STOPPED;
                     }
+                    // fall through
                 case Fragment.STOPPED:
                     if (newState > Fragment.STOPPED) {
                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                         f.performStart();
                         dispatchOnFragmentStarted(f, false);
                     }
+                    // fall through
                 case Fragment.STARTED:
                     if (newState > Fragment.STARTED) {
                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
@@ -1398,17 +1476,20 @@
                         f.performPause();
                         dispatchOnFragmentPaused(f, false);
                     }
+                    // fall through
                 case Fragment.STARTED:
                     if (newState < Fragment.STARTED) {
                         if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                         f.performStop();
                         dispatchOnFragmentStopped(f, false);
                     }
+                    // fall through
                 case Fragment.STOPPED:
                     if (newState < Fragment.STOPPED) {
                         if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                         f.performReallyStop();
                     }
+                    // fall through
                 case Fragment.ACTIVITY_CREATED:
                     if (newState < Fragment.ACTIVITY_CREATED) {
                         if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
@@ -1422,7 +1503,10 @@
                         f.performDestroyView();
                         dispatchOnFragmentViewDestroyed(f, false);
                         if (f.mView != null && f.mContainer != null) {
-                            Animation anim = null;
+                            // Stop any current animations:
+                            f.mView.clearAnimation();
+                            f.mContainer.endViewTransition(f.mView);
+                            AnimationOrAnimator anim = null;
                             if (mCurState > Fragment.INITIALIZING && !mDestroyed
                                     && f.mView.getVisibility() == View.VISIBLE
                                     && f.mPostponedAlpha >= 0) {
@@ -1431,46 +1515,36 @@
                             }
                             f.mPostponedAlpha = 0;
                             if (anim != null) {
-                                final Fragment fragment = f;
-                                f.setAnimatingAway(f.mView);
-                                f.setStateAfterAnimating(newState);
-                                final View viewToAnimate = f.mView;
-                                anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
-                                        viewToAnimate, anim) {
-                                    @Override
-                                    public void onAnimationEnd(Animation animation) {
-                                        super.onAnimationEnd(animation);
-                                        if (fragment.getAnimatingAway() != null) {
-                                            fragment.setAnimatingAway(null);
-                                            moveToState(fragment, fragment.getStateAfterAnimating(),
-                                                    0, 0, false);
-                                        }
-                                    }
-                                });
-                                f.mView.startAnimation(anim);
+                                animateRemoveFragment(f, anim, newState);
                             }
                             f.mContainer.removeView(f.mView);
                         }
                         f.mContainer = null;
                         f.mView = null;
                         f.mInnerView = null;
+                        f.mInLayout = false;
                     }
+                    // fall through
                 case Fragment.CREATED:
                     if (newState < Fragment.CREATED) {
                         if (mDestroyed) {
+                            // The fragment's containing activity is
+                            // being destroyed, but this fragment is
+                            // currently animating away.  Stop the
+                            // animation right now -- it is not needed,
+                            // and we can't wait any more on destroying
+                            // the fragment.
                             if (f.getAnimatingAway() != null) {
-                                // The fragment's containing activity is
-                                // being destroyed, but this fragment is
-                                // currently animating away.  Stop the
-                                // animation right now -- it is not needed,
-                                // and we can't wait any more on destroying
-                                // the fragment.
                                 View v = f.getAnimatingAway();
                                 f.setAnimatingAway(null);
                                 v.clearAnimation();
+                            } else if (f.getAnimator() != null) {
+                                Animator animator = f.getAnimator();
+                                f.setAnimator(null);
+                                animator.cancel();
                             }
                         }
-                        if (f.getAnimatingAway() != null) {
+                        if (f.getAnimatingAway() != null || f.getAnimator() != null) {
                             // We are waiting for the fragment's view to finish
                             // animating away.  Just make a note of the state
                             // the fragment now should move to once the animation
@@ -1509,10 +1583,79 @@
         }
     }
 
+    /**
+     * Animates the removal of a fragment with the given animator or animation. After animating,
+     * the fragment's view will be removed from the hierarchy.
+     *
+     * @param fragment The fragment to animate out
+     * @param anim The animator or animation to run on the fragment's view
+     * @param newState The final state after animating.
+     */
+    private void animateRemoveFragment(@NonNull final Fragment fragment,
+            @NonNull AnimationOrAnimator anim, final int newState) {
+        final View viewToAnimate = fragment.mView;
+        fragment.setStateAfterAnimating(newState);
+        if (anim.animation != null) {
+            Animation animation = anim.animation;
+            fragment.setAnimatingAway(fragment.mView);
+            AnimationListener listener = getAnimationListener(animation);
+            animation.setAnimationListener(new AnimationListenerWrapper(listener) {
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    super.onAnimationEnd(animation);
+                    if (fragment.getAnimatingAway() != null) {
+                        fragment.setAnimatingAway(null);
+                        moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false);
+                    }
+                }
+            });
+            setHWLayerAnimListenerIfAlpha(viewToAnimate, anim);
+            fragment.mView.startAnimation(animation);
+        } else {
+            final Animator animator = anim.animator;
+            fragment.setAnimator(anim.animator);
+            final ViewGroup container = fragment.mContainer;
+            if (container != null) {
+                container.startViewTransition(viewToAnimate);
+            }
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator anim) {
+                    if (container != null) {
+                        container.endViewTransition(viewToAnimate);
+                    }
+                    if (fragment.getAnimator() != null) {
+                        fragment.setAnimator(null);
+                        moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false);
+                    }
+                }
+            });
+            animator.setTarget(fragment.mView);
+            setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+            animator.start();
+        }
+    }
+
     void moveToState(Fragment f) {
         moveToState(f, mCurState, 0, 0, false);
     }
 
+    void ensureInflatedFragmentView(Fragment f) {
+        if (f.mFromLayout && !f.mPerformedCreateView) {
+            f.mView = f.performCreateView(f.performGetLayoutInflater(
+                    f.mSavedFragmentState), null, f.mSavedFragmentState);
+            if (f.mView != null) {
+                f.mInnerView = f.mView;
+                f.mView.setSaveFromParentEnabled(false);
+                if (f.mHidden) f.mView.setVisibility(View.GONE);
+                f.onViewCreated(f.mView, f.mSavedFragmentState);
+                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
+            } else {
+                f.mInnerView = null;
+            }
+        }
+    }
+
     /**
      * Fragments that have been shown or hidden don't have their visibility changed or
      * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
@@ -1525,20 +1668,48 @@
      */
     void completeShowHideFragment(final Fragment fragment) {
         if (fragment.mView != null) {
-            Animation anim = loadAnimation(fragment, fragment.getNextTransition(),
+            AnimationOrAnimator anim = loadAnimation(fragment, fragment.getNextTransition(),
                     !fragment.mHidden, fragment.getNextTransitionStyle());
-            if (anim != null) {
+            if (anim != null && anim.animator != null) {
+                anim.animator.setTarget(fragment.mView);
+                if (fragment.mHidden) {
+                    if (fragment.isHideReplaced()) {
+                        fragment.setHideReplaced(false);
+                    } else {
+                        final ViewGroup container = fragment.mContainer;
+                        final View animatingView = fragment.mView;
+                        container.startViewTransition(animatingView);
+                        // Delay the actual hide operation until the animation finishes,
+                        // otherwise the fragment will just immediately disappear
+                        anim.animator.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                container.endViewTransition(animatingView);
+                                animation.removeListener(this);
+                                if (fragment.mView != null) {
+                                    fragment.mView.setVisibility(View.GONE);
+                                }
+                            }
+                        });
+                    }
+                } else {
+                    fragment.mView.setVisibility(View.VISIBLE);
+                }
                 setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
-                fragment.mView.startAnimation(anim);
-                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
-                anim.start();
-            }
-            final int visibility = fragment.mHidden && !fragment.isHideReplaced()
-                    ? View.GONE
-                    : View.VISIBLE;
-            fragment.mView.setVisibility(visibility);
-            if (fragment.isHideReplaced()) {
-                fragment.setHideReplaced(false);
+                anim.animator.start();
+            } else {
+                if (anim != null) {
+                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+                    fragment.mView.startAnimation(anim.animation);
+                    anim.animation.start();
+                }
+                final int visibility = fragment.mHidden && !fragment.isHideReplaced()
+                        ? View.GONE
+                        : View.VISIBLE;
+                fragment.mView.setVisibility(visibility);
+                if (fragment.isHideReplaced()) {
+                    fragment.setHideReplaced(false);
+                }
             }
         }
         if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
@@ -1584,19 +1755,22 @@
             }
             if (f.mIsNewlyAdded && f.mContainer != null) {
                 // Make it visible and run the animations
-                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
-                    f.mView.setVisibility(View.VISIBLE);
-                } else if (f.mPostponedAlpha > 0f) {
+                if (f.mPostponedAlpha > 0f) {
                     f.mView.setAlpha(f.mPostponedAlpha);
                 }
                 f.mPostponedAlpha = 0f;
                 f.mIsNewlyAdded = false;
                 // run animations:
-                Animation anim = loadAnimation(f, f.getNextTransition(), true,
+                AnimationOrAnimator anim = loadAnimation(f, f.getNextTransition(), true,
                         f.getNextTransitionStyle());
                 if (anim != null) {
                     setHWLayerAnimListenerIfAlpha(f.mView, anim);
-                    f.mView.startAnimation(anim);
+                    if (anim.animation != null) {
+                        f.mView.startAnimation(anim.animation);
+                    } else {
+                        anim.animator.setTarget(f.mView);
+                        anim.animator.start();
+                    }
                 }
             }
         }
@@ -1629,14 +1803,12 @@
             boolean loadersRunning = false;
 
             // Must add them in the proper order. mActive fragments may be out of order
-            if (mAdded != null) {
-                final int numAdded = mAdded.size();
-                for (int i = 0; i < numAdded; i++) {
-                    Fragment f = mAdded.get(i);
-                    moveFragmentToExpectedState(f);
-                    if (f.mLoaderManager != null) {
-                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
-                    }
+            final int numAdded = mAdded.size();
+            for (int i = 0; i < numAdded; i++) {
+                Fragment f = mAdded.get(i);
+                moveFragmentToExpectedState(f);
+                if (f.mLoaderManager != null) {
+                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
                 }
             }
 
@@ -1703,9 +1875,6 @@
     }
 
     public void addFragment(Fragment fragment, boolean moveToStateNow) {
-        if (mAdded == null) {
-            mAdded = new ArrayList<Fragment>();
-        }
         if (DEBUG) Log.v(TAG, "add: " + fragment);
         makeActive(fragment);
         if (!fragment.mDetached) {
@@ -1733,10 +1902,8 @@
         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
         final boolean inactive = !fragment.isInBackStack();
         if (!fragment.mDetached || inactive) {
-            if (mAdded != null) {
-                synchronized (mAdded) {
-                    mAdded.remove(fragment);
-                }
+            synchronized (mAdded) {
+                mAdded.remove(fragment);
             }
             if (fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
@@ -1784,11 +1951,9 @@
             fragment.mDetached = true;
             if (fragment.mAdded) {
                 // We are not already in back stack, so need to remove the fragment.
-                if (mAdded != null) {
-                    if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
-                    synchronized (mAdded) {
-                        mAdded.remove(fragment);
-                    }
+                if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
+                synchronized (mAdded) {
+                    mAdded.remove(fragment);
                 }
                 if (fragment.mHasMenu && fragment.mMenuVisible) {
                     mNeedMenuInvalidate = true;
@@ -1803,9 +1968,6 @@
         if (fragment.mDetached) {
             fragment.mDetached = false;
             if (!fragment.mAdded) {
-                if (mAdded == null) {
-                    mAdded = new ArrayList<Fragment>();
-                }
                 if (mAdded.contains(fragment)) {
                     throw new IllegalStateException("Fragment already added: " + fragment);
                 }
@@ -1823,13 +1985,11 @@
 
     @Override
     public Fragment findFragmentById(int id) {
-        if (mAdded != null) {
-            // First look through added fragments.
-            for (int i=mAdded.size()-1; i>=0; i--) {
-                Fragment f = mAdded.get(i);
-                if (f != null && f.mFragmentId == id) {
-                    return f;
-                }
+        // First look through added fragments.
+        for (int i = mAdded.size() - 1; i >= 0; i--) {
+            Fragment f = mAdded.get(i);
+            if (f != null && f.mFragmentId == id) {
+                return f;
             }
         }
         if (mActive != null) {
@@ -1846,7 +2006,7 @@
 
     @Override
     public Fragment findFragmentByTag(String tag) {
-        if (mAdded != null && tag != null) {
+        if (tag != null) {
             // First look through added fragments.
             for (int i=mAdded.size()-1; i>=0; i--) {
                 Fragment f = mAdded.get(i);
@@ -1890,6 +2050,11 @@
         }
     }
 
+    @Override
+    public boolean isStateSaved() {
+        return mStateSaved;
+    }
+
     /**
      * Adds an action to the queue of pending actions.
      *
@@ -2031,7 +2196,7 @@
         if (action.generateOps(mTmpRecords, mTmpIsPop)) {
             mExecutingActions = true;
             try {
-                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
             } finally {
                 cleanupExec();
             }
@@ -2061,7 +2226,7 @@
         while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
             mExecutingActions = true;
             try {
-                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
             } finally {
                 cleanupExec();
             }
@@ -2109,19 +2274,20 @@
     }
 
     /**
-     * Optimizes BackStackRecord operations. This method merges operations of proximate records
-     * that allow optimization. See {@link FragmentTransaction#setAllowOptimization(boolean)}.
+     * Remove redundant BackStackRecord operations and executes them. This method merges operations
+     * of proximate records that allow reordering. See
+     * {@link FragmentTransaction#setReorderingAllowed(boolean)}.
      * <p>
      * For example, a transaction that adds to the back stack and then another that pops that
-     * back stack record will be optimized.
+     * back stack record will be optimized to remove the unnecessary operation.
      * <p>
      * Likewise, two transactions committed that are executed at the same time will be optimized
-     * as well as two pop operations executed together.
+     * to remove the redundant operations as well as two pop operations executed together.
      *
      * @param records The records pending execution
      * @param isRecordPop The direction that these records are being run.
      */
-    private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
+    private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
             ArrayList<Boolean> isRecordPop) {
         if (records == null || records.isEmpty()) {
             return;
@@ -2137,24 +2303,25 @@
         final int numRecords = records.size();
         int startIndex = 0;
         for (int recordNum = 0; recordNum < numRecords; recordNum++) {
-            final boolean canOptimize = records.get(recordNum).mAllowOptimization;
-            if (!canOptimize) {
+            final boolean canReorder = records.get(recordNum).mReorderingAllowed;
+            if (!canReorder) {
                 // execute all previous transactions
                 if (startIndex != recordNum) {
                     executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                 }
-                // execute all unoptimized pop operations together or one add operation
-                int optimizeEnd = recordNum + 1;
+                // execute all pop operations that don't allow reordering together or
+                // one add operation
+                int reorderingEnd = recordNum + 1;
                 if (isRecordPop.get(recordNum)) {
-                    while (optimizeEnd < numRecords
-                            && isRecordPop.get(optimizeEnd)
-                            && !records.get(optimizeEnd).mAllowOptimization) {
-                        optimizeEnd++;
+                    while (reorderingEnd < numRecords
+                            && isRecordPop.get(reorderingEnd)
+                            && !records.get(reorderingEnd).mReorderingAllowed) {
+                        reorderingEnd++;
                     }
                 }
-                executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
-                startIndex = optimizeEnd;
-                recordNum = optimizeEnd - 1;
+                executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
+                startIndex = reorderingEnd;
+                recordNum = reorderingEnd - 1;
             }
         }
         if (startIndex != numRecords) {
@@ -2163,45 +2330,44 @@
     }
 
     /**
-     * Optimizes a subset of a list of BackStackRecords, all of which either allow optimization or
-     * do not allow optimization.
-     * @param records A list of BackStackRecords that are to be optimized
+     * Executes a subset of a list of BackStackRecords, all of which either allow reordering or
+     * do not allow ordering.
+     * @param records A list of BackStackRecords that are to be executed
      * @param isRecordPop The direction that these records are being run.
-     * @param startIndex The index of the first record in <code>records</code> to be optimized
-     * @param endIndex One more than the final record index in <code>records</code> to optimize.
+     * @param startIndex The index of the first record in <code>records</code> to be executed
+     * @param endIndex One more than the final record index in <code>records</code> to executed.
      */
     private void executeOpsTogether(ArrayList<BackStackRecord> records,
             ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
-        final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
+        final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
         boolean addToBackStack = false;
         if (mTmpAddedFragments == null) {
             mTmpAddedFragments = new ArrayList<>();
         } else {
             mTmpAddedFragments.clear();
         }
-        if (mAdded != null) {
-            mTmpAddedFragments.addAll(mAdded);
-        }
+        mTmpAddedFragments.addAll(mAdded);
+        Fragment oldPrimaryNav = getPrimaryNavigationFragment();
         for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
             final BackStackRecord record = records.get(recordNum);
             final boolean isPop = isRecordPop.get(recordNum);
             if (!isPop) {
-                record.expandReplaceOps(mTmpAddedFragments);
+                oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
             } else {
-                record.trackAddedFragmentsInPop(mTmpAddedFragments);
+                oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
             }
             addToBackStack = addToBackStack || record.mAddToBackStack;
         }
         mTmpAddedFragments.clear();
 
-        if (!allowOptimization) {
+        if (!allowReordering) {
             FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
                     false);
         }
         executeOps(records, isRecordPop, startIndex, endIndex);
 
         int postponeIndex = endIndex;
-        if (allowOptimization) {
+        if (allowReordering) {
             ArraySet<Fragment> addedFragments = new ArraySet<>();
             addAddedFragments(addedFragments);
             postponeIndex = postponePostponableTransactions(records, isRecordPop,
@@ -2209,7 +2375,7 @@
             makeRemovedFragmentsInvisible(addedFragments);
         }
 
-        if (postponeIndex != startIndex && allowOptimization) {
+        if (postponeIndex != startIndex && allowReordering) {
             // need to run something now
             FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
                     postponeIndex, true);
@@ -2223,6 +2389,7 @@
                 freeBackStackIndex(record.mIndex);
                 record.mIndex = -1;
             }
+            record.runOnCommitRunnables();
         }
         if (addToBackStack) {
             reportBackStackChanged();
@@ -2231,8 +2398,7 @@
 
     /**
      * Any fragments that were removed because they have been postponed should have their views
-     * made invisible by setting their alpha to 0 on API >= 11 or setting visibility to INVISIBLE
-     * on API < 11.
+     * made invisible by setting their alpha to 0.
      *
      * @param fragments The fragments that were added during operation execution. Only the ones
      *                  that are no longer added will have their alpha changed.
@@ -2243,12 +2409,8 @@
             final Fragment fragment = fragments.valueAt(i);
             if (!fragment.mAdded) {
                 final View view = fragment.getView();
-                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
-                    fragment.getView().setVisibility(View.INVISIBLE);
-                } else {
-                    fragment.mPostponedAlpha = view.getAlpha();
-                    view.setAlpha(0f);
-                }
+                fragment.mPostponedAlpha = view.getAlpha();
+                view.setAlpha(0f);
             }
         }
     }
@@ -2321,11 +2483,15 @@
      */
     private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
             boolean moveToState) {
+        if (isPop) {
+            record.executePopOps(moveToState);
+        } else {
+            record.executeOps();
+        }
         ArrayList<BackStackRecord> records = new ArrayList<>(1);
         ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
         records.add(record);
         isRecordPop.add(isPop);
-        executeOps(records, isRecordPop, 0, 1);
         if (runTransitions) {
             FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
         }
@@ -2341,8 +2507,7 @@
                 Fragment fragment = mActive.valueAt(i);
                 if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded
                         && record.interactsWith(fragment.mContainerId)) {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
-                            && fragment.mPostponedAlpha > 0) {
+                    if (fragment.mPostponedAlpha > 0) {
                         fragment.mView.setAlpha(fragment.mPostponedAlpha);
                     }
                     if (moveToState) {
@@ -2429,7 +2594,7 @@
         }
         // We want to leave the fragment in the started state
         final int state = Math.min(mCurState, Fragment.STARTED);
-        final int numAdded = mAdded == null ? 0 : mAdded.size();
+        final int numAdded = mAdded.size();
         for (int i = 0; i < numAdded; i++) {
             Fragment fragment = mAdded.get(i);
             if (fragment.mState < state) {
@@ -2461,19 +2626,23 @@
         final int numFragments = mActive == null ? 0 : mActive.size();
         for (int i = 0; i < numFragments; i++) {
             Fragment fragment = mActive.valueAt(i);
-            if (fragment != null && fragment.getAnimatingAway() != null) {
-                // Give up waiting for the animation and just end it.
-                final int stateAfterAnimating = fragment.getStateAfterAnimating();
-                final View animatingAway = fragment.getAnimatingAway();
-                fragment.setAnimatingAway(null);
-                Animation animation = animatingAway.getAnimation();
-                if (animation != null) {
-                    animation.cancel();
-                    // force-clear the animation, as Animation#cancel() doesn't work prior to N,
-                    // and will instead cause the animation to infinitely loop
-                    animatingAway.clearAnimation();
+            if (fragment != null) {
+                if (fragment.getAnimatingAway() != null) {
+                    // Give up waiting for the animation and just end it.
+                    final int stateAfterAnimating = fragment.getStateAfterAnimating();
+                    final View animatingAway = fragment.getAnimatingAway();
+                    fragment.setAnimatingAway(null);
+                    Animation animation = animatingAway.getAnimation();
+                    if (animation != null) {
+                        animation.cancel();
+                        // force-clear the animation, as Animation#cancel() doesn't work prior to N,
+                        // and will instead cause the animation to infinitely loop
+                        animatingAway.clearAnimation();
+                    }
+                    moveToState(fragment, stateAfterAnimating, 0, 0, false);
+                } else if (fragment.getAnimator() != null) {
+                    fragment.getAnimator().end();
                 }
-                moveToState(fragment, stateAfterAnimating, 0, 0, false);
             }
         }
     }
@@ -2490,20 +2659,20 @@
      */
     private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
             ArrayList<Boolean> isPop) {
-        int numActions;
+        boolean didSomething = false;
         synchronized (this) {
             if (mPendingActions == null || mPendingActions.size() == 0) {
                 return false;
             }
 
-            numActions = mPendingActions.size();
+            final int numActions = mPendingActions.size();
             for (int i = 0; i < numActions; i++) {
-                mPendingActions.get(i).generateOps(records, isPop);
+                didSomething |= mPendingActions.get(i).generateOps(records, isPop);
             }
             mPendingActions.clear();
             mHost.getHandler().removeCallbacks(mExecCommit);
         }
-        return numActions > 0;
+        return didSomething;
     }
 
     void doPendingDeferredStart() {
@@ -2595,6 +2764,35 @@
     }
 
     FragmentManagerNonConfig retainNonConfig() {
+        setRetaining(mSavedNonConfig);
+        return mSavedNonConfig;
+    }
+
+    /**
+     * Recurse the FragmentManagerNonConfig fragments and set the mRetaining to true. This
+     * was previously done while saving the non-config state, but that has been moved to
+     * {@link #saveNonConfig()} called from {@link #saveAllState()}. If mRetaining is set too
+     * early, the fragment won't be destroyed when the FragmentManager is destroyed.
+     */
+    private static void setRetaining(FragmentManagerNonConfig nonConfig) {
+        if (nonConfig == null) {
+            return;
+        }
+        List<Fragment> fragments = nonConfig.getFragments();
+        if (fragments != null) {
+            for (Fragment fragment : fragments) {
+                fragment.mRetaining = true;
+            }
+        }
+        List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs();
+        if (children != null) {
+            for (FragmentManagerNonConfig child : children) {
+                setRetaining(child);
+            }
+        }
+    }
+
+    void saveNonConfig() {
         ArrayList<Fragment> fragments = null;
         ArrayList<FragmentManagerNonConfig> childFragments = null;
         if (mActive != null) {
@@ -2606,34 +2804,37 @@
                             fragments = new ArrayList<Fragment>();
                         }
                         fragments.add(f);
-                        f.mRetaining = true;
                         f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
                         if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                     }
-                    boolean addedChild = false;
+                    FragmentManagerNonConfig child;
                     if (f.mChildFragmentManager != null) {
-                        FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
-                        if (child != null) {
-                            if (childFragments == null) {
-                                childFragments = new ArrayList<FragmentManagerNonConfig>();
-                                for (int j = 0; j < i; j++) {
-                                    childFragments.add(null);
-                                }
-                            }
-                            childFragments.add(child);
-                            addedChild = true;
+                        f.mChildFragmentManager.saveNonConfig();
+                        child = f.mChildFragmentManager.mSavedNonConfig;
+                    } else {
+                        // f.mChildNonConfig may be not null, when the parent fragment is
+                        // in the backstack.
+                        child = f.mChildNonConfig;
+                    }
+
+                    if (childFragments == null && child != null) {
+                        childFragments = new ArrayList<>(mActive.size());
+                        for (int j = 0; j < i; j++) {
+                            childFragments.add(null);
                         }
                     }
-                    if (childFragments != null && !addedChild) {
-                        childFragments.add(null);
+
+                    if (childFragments != null) {
+                        childFragments.add(child);
                     }
                 }
             }
         }
         if (fragments == null && childFragments == null) {
-            return null;
+            mSavedNonConfig = null;
+        } else {
+            mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments);
         }
-        return new FragmentManagerNonConfig(fragments, childFragments);
     }
 
     void saveFragmentViewState(Fragment f) {
@@ -2693,18 +2894,8 @@
         endAnimatingAwayFragments();
         execPendingActions();
 
-        if (HONEYCOMB) {
-            // As of Honeycomb, we save state after pausing.  Prior to that
-            // it is before pausing.  With fragments this is an issue, since
-            // there are many things you may do after pausing but before
-            // stopping that change the fragment state.  For those older
-            // devices, we will not at this point say that we have saved
-            // the state, so we will allow them to continue doing fragment
-            // transactions.  This retains the same semantics as Honeycomb,
-            // though you do have the risk of losing the very most recent state
-            // if the process is killed...  we'll live with that.
-            mStateSaved = true;
-        }
+        mStateSaved = true;
+        mSavedNonConfig = null;
 
         if (mActive == null || mActive.size() <= 0) {
             return null;
@@ -2767,18 +2958,18 @@
         BackStackState[] backStack = null;
 
         // Build list of currently added fragments.
-        if (mAdded != null) {
-            N = mAdded.size();
-            if (N > 0) {
-                added = new int[N];
-                for (int i=0; i<N; i++) {
-                    added[i] = mAdded.get(i).mIndex;
-                    if (added[i] < 0) {
-                        throwException(new IllegalStateException(
-                                "Failure saving state: active " + mAdded.get(i)
-                                + " has cleared index: " + added[i]));
-                    }
-                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+        N = mAdded.size();
+        if (N > 0) {
+            added = new int[N];
+            for (int i = 0; i < N; i++) {
+                added[i] = mAdded.get(i).mIndex;
+                if (added[i] < 0) {
+                    throwException(new IllegalStateException(
+                            "Failure saving state: active " + mAdded.get(i)
+                            + " has cleared index: " + added[i]));
+                }
+                if (DEBUG) {
+                    Log.v(TAG, "saveAllState: adding fragment #" + i
                             + ": " + mAdded.get(i));
                 }
             }
@@ -2801,7 +2992,11 @@
         fms.mActive = active;
         fms.mAdded = added;
         fms.mBackStack = backStack;
+        if (mPrimaryNav != null) {
+            fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
+        }
         fms.mNextFragmentIndex = mNextFragmentIndex;
+        saveNonConfig();
         return fms;
     }
 
@@ -2857,7 +3052,7 @@
                 if (childNonConfigs != null && i < childNonConfigs.size()) {
                     childNonConfig = childNonConfigs.get(i);
                 }
-                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
+                Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                 mActive.put(f.mIndex, f);
                 // Now that the fragment is instantiated (or came from being
@@ -2884,8 +3079,8 @@
         }
 
         // Build the list of currently added fragments.
+        mAdded.clear();
         if (fms.mAdded != null) {
-            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
             for (int i=0; i<fms.mAdded.length; i++) {
                 Fragment f = mActive.get(fms.mAdded[i]);
                 if (f == null) {
@@ -2901,8 +3096,6 @@
                     mAdded.add(f);
                 }
             }
-        } else {
-            mAdded = null;
         }
 
         // Build the back stack.
@@ -2927,6 +3120,9 @@
             mBackStack = null;
         }
 
+        if (fms.mPrimaryNavActiveIndex >= 0) {
+            mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex);
+        }
         this.mNextFragmentIndex = fms.mNextFragmentIndex;
     }
 
@@ -2954,8 +3150,9 @@
     }
 
     public void noteStateNotSaved() {
+        mSavedNonConfig = null;
         mStateSaved = false;
-        final int addedCount = mAdded == null ? 0 : mAdded.size();
+        final int addedCount = mAdded.size();
         for (int i = 0; i < addedCount; i++) {
             Fragment fragment = mAdded.get(i);
             if (fragment != null) {
@@ -2966,36 +3163,26 @@
 
     public void dispatchCreate() {
         mStateSaved = false;
-        mExecutingActions = true;
-        moveToState(Fragment.CREATED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.CREATED);
     }
 
     public void dispatchActivityCreated() {
         mStateSaved = false;
-        mExecutingActions = true;
-        moveToState(Fragment.ACTIVITY_CREATED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.ACTIVITY_CREATED);
     }
 
     public void dispatchStart() {
         mStateSaved = false;
-        mExecutingActions = true;
-        moveToState(Fragment.STARTED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.STARTED);
     }
 
     public void dispatchResume() {
         mStateSaved = false;
-        mExecutingActions = true;
-        moveToState(Fragment.RESUMED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.RESUMED);
     }
 
     public void dispatchPause() {
-        mExecutingActions = true;
-        moveToState(Fragment.STARTED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.STARTED);
     }
 
     public void dispatchStop() {
@@ -3004,38 +3191,37 @@
         // them.
         mStateSaved = true;
 
-        mExecutingActions = true;
-        moveToState(Fragment.STOPPED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.STOPPED);
     }
 
     public void dispatchReallyStop() {
-        mExecutingActions = true;
-        moveToState(Fragment.ACTIVITY_CREATED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.ACTIVITY_CREATED);
     }
 
     public void dispatchDestroyView() {
-        mExecutingActions = true;
-        moveToState(Fragment.CREATED, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.CREATED);
     }
 
     public void dispatchDestroy() {
         mDestroyed = true;
         execPendingActions();
-        mExecutingActions = true;
-        moveToState(Fragment.INITIALIZING, false);
-        mExecutingActions = false;
+        dispatchStateChange(Fragment.INITIALIZING);
         mHost = null;
         mContainer = null;
         mParent = null;
     }
 
-    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
-        if (mAdded == null) {
-            return;
+    private void dispatchStateChange(int nextState) {
+        try {
+            mExecutingActions = true;
+            moveToState(nextState, false);
+        } finally {
+            mExecutingActions = false;
         }
+        execPendingActions();
+    }
+
+    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
         for (int i = mAdded.size() - 1; i >= 0; --i) {
             final android.support.v4.app.Fragment f = mAdded.get(i);
             if (f != null) {
@@ -3045,9 +3231,6 @@
     }
 
     public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        if (mAdded == null) {
-            return;
-        }
         for (int i = mAdded.size() - 1; i >= 0; --i) {
             final android.support.v4.app.Fragment f = mAdded.get(i);
             if (f != null) {
@@ -3057,23 +3240,19 @@
     }
 
     public void dispatchConfigurationChanged(Configuration newConfig) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performConfigurationChanged(newConfig);
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performConfigurationChanged(newConfig);
             }
         }
     }
 
     public void dispatchLowMemory() {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performLowMemory();
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performLowMemory();
             }
         }
     }
@@ -3081,17 +3260,15 @@
     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         boolean show = false;
         ArrayList<Fragment> newMenus = null;
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performCreateOptionsMenu(menu, inflater)) {
-                        show = true;
-                        if (newMenus == null) {
-                            newMenus = new ArrayList<Fragment>();
-                        }
-                        newMenus.add(f);
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performCreateOptionsMenu(menu, inflater)) {
+                    show = true;
+                    if (newMenus == null) {
+                        newMenus = new ArrayList<Fragment>();
                     }
+                    newMenus.add(f);
                 }
             }
         }
@@ -3112,13 +3289,11 @@
 
     public boolean dispatchPrepareOptionsMenu(Menu menu) {
         boolean show = false;
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performPrepareOptionsMenu(menu)) {
-                        show = true;
-                    }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performPrepareOptionsMenu(menu)) {
+                    show = true;
                 }
             }
         }
@@ -3126,13 +3301,11 @@
     }
 
     public boolean dispatchOptionsItemSelected(MenuItem item) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performOptionsItemSelected(item)) {
-                        return true;
-                    }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performOptionsItemSelected(item)) {
+                    return true;
                 }
             }
         }
@@ -3140,13 +3313,11 @@
     }
 
     public boolean dispatchContextItemSelected(MenuItem item) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performContextItemSelected(item)) {
-                        return true;
-                    }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performContextItemSelected(item)) {
+                    return true;
                 }
             }
         }
@@ -3154,29 +3325,37 @@
     }
 
     public void dispatchOptionsMenuClosed(Menu menu) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performOptionsMenuClosed(menu);
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performOptionsMenuClosed(menu);
             }
         }
     }
 
-    public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
-            boolean recursive) {
-        if (mLifecycleCallbacks == null) {
-            mLifecycleCallbacks = new CopyOnWriteArrayList<>();
+    @SuppressWarnings("ReferenceEquality")
+    public void setPrimaryNavigationFragment(Fragment f) {
+        if (f != null && (mActive.get(f.mIndex) != f
+            || (f.mHost != null && f.getFragmentManager() != this))) {
+            throw new IllegalArgumentException("Fragment " + f
+                    + " is not an active fragment of FragmentManager " + this);
         }
-        mLifecycleCallbacks.add(new Pair(cb, recursive));
+        mPrimaryNav = f;
     }
 
-    public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
+    @Override
+    public Fragment getPrimaryNavigationFragment() {
+        return mPrimaryNav;
+    }
 
+    @Override
+    public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
+            boolean recursive) {
+        mLifecycleCallbacks.add(new Pair<>(cb, recursive));
+    }
+
+    @Override
+    public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
         synchronized (mLifecycleCallbacks) {
             for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) {
                 if (mLifecycleCallbacks.get(i).first == cb) {
@@ -3195,9 +3374,6 @@
                         .dispatchOnFragmentPreAttached(f, context, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentPreAttached(this, f, context);
@@ -3213,9 +3389,6 @@
                         .dispatchOnFragmentAttached(f, context, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentAttached(this, f, context);
@@ -3223,6 +3396,22 @@
         }
     }
 
+    void dispatchOnFragmentPreCreated(Fragment f, Bundle savedInstanceState,
+            boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentPreCreated(f, savedInstanceState, true);
+            }
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentPreCreated(this, f, savedInstanceState);
+            }
+        }
+    }
+
     void dispatchOnFragmentCreated(Fragment f, Bundle savedInstanceState, boolean onlyRecursive) {
         if (mParent != null) {
             FragmentManager parentManager = mParent.getFragmentManager();
@@ -3231,9 +3420,6 @@
                         .dispatchOnFragmentCreated(f, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentCreated(this, f, savedInstanceState);
@@ -3250,9 +3436,6 @@
                         .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentActivityCreated(this, f, savedInstanceState);
@@ -3269,9 +3452,6 @@
                         .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentViewCreated(this, f, v, savedInstanceState);
@@ -3287,9 +3467,6 @@
                         .dispatchOnFragmentStarted(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentStarted(this, f);
@@ -3305,9 +3482,6 @@
                         .dispatchOnFragmentResumed(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentResumed(this, f);
@@ -3323,9 +3497,6 @@
                         .dispatchOnFragmentPaused(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentPaused(this, f);
@@ -3341,9 +3512,6 @@
                         .dispatchOnFragmentStopped(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentStopped(this, f);
@@ -3359,9 +3527,6 @@
                         .dispatchOnFragmentSaveInstanceState(f, outState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentSaveInstanceState(this, f, outState);
@@ -3377,9 +3542,6 @@
                         .dispatchOnFragmentViewDestroyed(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentViewDestroyed(this, f);
@@ -3395,9 +3557,6 @@
                         .dispatchOnFragmentDestroyed(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentDestroyed(this, f);
@@ -3413,9 +3572,6 @@
                         .dispatchOnFragmentDetached(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentDetached(this, f);
@@ -3505,7 +3661,7 @@
                 + Integer.toHexString(id) + " fname=" + fname
                 + " existing=" + fragment);
         if (fragment == null) {
-            fragment = Fragment.instantiate(context, fname);
+            fragment = mContainer.instantiate(context, fname, null);
             fragment.mFromLayout = true;
             fragment.mFragmentId = id != 0 ? id : containerId;
             fragment.mContainerId = containerId;
@@ -3537,7 +3693,9 @@
         }
 
         // If we haven't finished entering the CREATED state ourselves yet,
-        // push the inflated child fragment along.
+        // push the inflated child fragment along. This will ensureInflatedFragmentView
+        // at the right phase of the lifecycle so that we will have mView populated
+        // for compliant fragments below.
         if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
             moveToState(fragment, Fragment.CREATED, 0, 0, false);
         } else {
@@ -3557,7 +3715,12 @@
         return fragment.mView;
     }
 
-    LayoutInflaterFactory getLayoutInflaterFactory() {
+    @Override
+    public View onCreateView(String name, Context context, AttributeSet attrs) {
+        return onCreateView(null, name, context, attrs);
+    }
+
+    LayoutInflater.Factory2 getLayoutInflaterFactory() {
         return this;
     }
 
@@ -3607,6 +3770,16 @@
         @Override
         public boolean generateOps(ArrayList<BackStackRecord> records,
                 ArrayList<Boolean> isRecordPop) {
+            if (mPrimaryNav != null // We have a primary nav fragment
+                    && mId < 0 // No valid id (since they're local)
+                    && mName == null) { // no name to pop to (since they're local)
+                final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager();
+                if (childManager != null && childManager.popBackStackImmediate()) {
+                    // We didn't add any operations for this FragmentManager even though
+                    // a child did do work.
+                    return false;
+                }
+            }
             return popBackStackState(records, isRecordPop, mName, mId, mFlags);
         }
     }
@@ -3686,4 +3859,123 @@
             mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
         }
     }
+
+    /**
+     * Contains either an animator or animation. One of these should be null.
+     */
+    private static class AnimationOrAnimator {
+        public final Animation animation;
+        public final Animator animator;
+
+        private AnimationOrAnimator(Animation animation) {
+            this.animation = animation;
+            this.animator = null;
+            if (animation == null) {
+                throw new IllegalStateException("Animation cannot be null");
+            }
+        }
+
+        private AnimationOrAnimator(Animator animator) {
+            this.animation = null;
+            this.animator = animator;
+            if (animator == null) {
+                throw new IllegalStateException("Animator cannot be null");
+            }
+        }
+    }
+
+    /**
+     * Wrap an AnimationListener that can be null. This allows us to chain animation listeners.
+     */
+    private static class AnimationListenerWrapper implements AnimationListener {
+        private final AnimationListener mWrapped;
+
+        private AnimationListenerWrapper(AnimationListener wrapped) {
+            mWrapped = wrapped;
+        }
+
+        @CallSuper
+        @Override
+        public void onAnimationStart(Animation animation) {
+            if (mWrapped != null) {
+                mWrapped.onAnimationStart(animation);
+            }
+        }
+
+        @CallSuper
+        @Override
+        public void onAnimationEnd(Animation animation) {
+            if (mWrapped != null) {
+                mWrapped.onAnimationEnd(animation);
+            }
+        }
+
+        @CallSuper
+        @Override
+        public void onAnimationRepeat(Animation animation) {
+            if (mWrapped != null) {
+                mWrapped.onAnimationRepeat(animation);
+            }
+        }
+    }
+
+    /**
+     * Reset the layer type to LAYER_TYPE_NONE at the end of an animation.
+     */
+    private static class AnimateOnHWLayerIfNeededListener extends AnimationListenerWrapper  {
+        View mView;
+
+        AnimateOnHWLayerIfNeededListener(final View v, AnimationListener listener) {
+            super(listener);
+            mView = v;
+        }
+
+        @Override
+        @CallSuper
+        public void onAnimationEnd(Animation animation) {
+            // If we're attached to a window, assume we're in the normal performTraversals
+            // drawing path for Animations running. It's not safe to change the layer type
+            // during drawing, so post it to the View to run later. If we're not attached
+            // or we're running on N and above, post it to the view. If we're not on N and
+            // not attached, do it right now since existing platform versions don't run the
+            // hwui renderer for detached views off the UI thread making changing layer type
+            // safe, but posting may not be.
+            // Prior to N posting to a detached view from a non-Looper thread could cause
+            // leaks, since the thread-local run queue on a non-Looper thread would never
+            // be flushed.
+            if (ViewCompat.isAttachedToWindow(mView) || Build.VERSION.SDK_INT >= 24) {
+                mView.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mView.setLayerType(View.LAYER_TYPE_NONE, null);
+                    }
+                });
+            } else {
+                mView.setLayerType(View.LAYER_TYPE_NONE, null);
+            }
+            super.onAnimationEnd(animation);
+        }
+    }
+
+    /**
+     * Set the layer type to LAYER_TYPE_HARDWARE while an animator is running.
+     */
+    private static class AnimatorOnHWLayerIfNeededListener extends AnimatorListenerAdapter  {
+        View mView;
+
+        AnimatorOnHWLayerIfNeededListener(final View v) {
+            mView = v;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mView.setLayerType(View.LAYER_TYPE_NONE, null);
+            animation.removeListener(this);
+        }
+    }
 }
diff --git a/fragment/java/android/support/v4/app/FragmentPagerAdapter.java b/fragment/java/android/support/v4/app/FragmentPagerAdapter.java
index 096995d..61b181d 100644
--- a/fragment/java/android/support/v4/app/FragmentPagerAdapter.java
+++ b/fragment/java/android/support/v4/app/FragmentPagerAdapter.java
@@ -83,6 +83,7 @@
         }
     }
 
+    @SuppressWarnings("ReferenceEquality")
     @Override
     public Object instantiateItem(ViewGroup container, int position) {
         if (mCurTransaction == null) {
@@ -121,6 +122,7 @@
         mCurTransaction.detach((Fragment)object);
     }
 
+    @SuppressWarnings("ReferenceEquality")
     @Override
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
         Fragment fragment = (Fragment)object;
diff --git a/fragment/java/android/support/v4/app/FragmentStatePagerAdapter.java b/fragment/java/android/support/v4/app/FragmentStatePagerAdapter.java
index 1a4a1cd..fc27c4f 100644
--- a/fragment/java/android/support/v4/app/FragmentStatePagerAdapter.java
+++ b/fragment/java/android/support/v4/app/FragmentStatePagerAdapter.java
@@ -62,7 +62,7 @@
  *      complete}
  */
 public abstract class FragmentStatePagerAdapter extends PagerAdapter {
-    private static final String TAG = "FragmentStatePagerAdapter";
+    private static final String TAG = "FragmentStatePagerAdapt";
     private static final boolean DEBUG = false;
 
     private final FragmentManager mFragmentManager;
@@ -145,6 +145,7 @@
     }
 
     @Override
+    @SuppressWarnings("ReferenceEquality")
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
         Fragment fragment = (Fragment)object;
         if (fragment != mCurrentPrimaryItem) {
diff --git a/fragment/java/android/support/v4/app/FragmentTransaction.java b/fragment/java/android/support/v4/app/FragmentTransaction.java
index 0171681..75c6f88 100644
--- a/fragment/java/android/support/v4/app/FragmentTransaction.java
+++ b/fragment/java/android/support/v4/app/FragmentTransaction.java
@@ -18,7 +18,9 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.os.Bundle;
 import android.support.annotation.AnimRes;
+import android.support.annotation.AnimatorRes;
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
@@ -148,6 +150,24 @@
     public abstract FragmentTransaction attach(Fragment fragment);
 
     /**
+     * Set a currently active fragment in this FragmentManager as the primary navigation fragment.
+     *
+     * <p>The primary navigation fragment's
+     * {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
+     * to process delegated navigation actions such as {@link FragmentManager#popBackStack()}
+     * if no ID or transaction name is provided to pop to. Navigation operations outside of the
+     * fragment system may choose to delegate those actions to the primary navigation fragment
+     * as returned by {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
+     *
+     * <p>The fragment provided must currently be added to the FragmentManager to be set as
+     * a primary navigation fragment, or previously added as part of this transaction.</p>
+     *
+     * @param fragment the fragment to set as the primary navigation fragment
+     * @return the same FragmentTransaction instance
+     */
+    public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);
+
+    /**
      * @return <code>true</code> if this transaction contains no operations,
      * <code>false</code> otherwise.
      */
@@ -185,18 +205,35 @@
      * Set specific animation resources to run for the fragments that are
      * entering and exiting in this transaction. These animations will not be
      * played when popping the back stack.
+     *
+     * @param enter An animation or animator resource ID used for the enter animation on the
+     *              view of the fragment being added or attached.
+     * @param exit An animation or animator resource ID used for the exit animation on the
+     *             view of the fragment being removed or detached.
      */
-    public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,
-            @AnimRes int exit);
+    public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter,
+            @AnimatorRes @AnimRes int exit);
 
     /**
      * Set specific animation resources to run for the fragments that are
      * entering and exiting in this transaction. The <code>popEnter</code>
      * and <code>popExit</code> animations will be played for enter/exit
      * operations specifically when popping the back stack.
+     *
+     * @param enter An animation or animator resource ID used for the enter animation on the
+     *              view of the fragment being added or attached.
+     * @param exit An animation or animator resource ID used for the exit animation on the
+     *             view of the fragment being removed or detached.
+     * @param popEnter An animation or animator resource ID used for the enter animation on the
+     *                 view of the fragment being readded or reattached caused by
+     *                 {@link FragmentManager#popBackStack()} or similar methods.
+     * @param popExit An animation or animator resource ID used for the enter animation on the
+     *                view of the fragment being removed or detached caused by
+     *                {@link FragmentManager#popBackStack()} or similar methods.
      */
-    public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,
-            @AnimRes int exit, @AnimRes int popEnter, @AnimRes int popExit);
+    public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter,
+            @AnimatorRes @AnimRes int exit, @AnimatorRes @AnimRes int popEnter,
+            @AnimatorRes @AnimRes int popExit);
 
     /**
      * Used with custom Transitions to map a View from a removed or hidden
@@ -282,29 +319,64 @@
 
     /**
      * Sets whether or not to allow optimizing operations within and across
-     * transactions. Optimizing fragment transaction's operations can eliminate
+     * transactions. This will remove redundant operations, eliminating
      * operations that cancel. For example, if two transactions are executed
      * together, one that adds a fragment A and the next replaces it with fragment B,
      * the operations will cancel and only fragment B will be added. That means that
      * fragment A may not go through the creation/destruction lifecycle.
      * <p>
-     * The side effect of optimization is that fragments may have state changes
+     * The side effect of removing redundant operations is that fragments may have state changes
      * out of the expected order. For example, one transaction adds fragment A,
-     * a second adds fragment B, then a third removes fragment A. Without optimization,
-     * fragment B could expect that while it is being created, fragment A will also
+     * a second adds fragment B, then a third removes fragment A. Without removing the redundant
+     * operations, fragment B could expect that while it is being created, fragment A will also
      * exist because fragment A will be removed after fragment B was added.
-     * With optimization, fragment B cannot expect fragment A to exist when
+     * With removing redundant operations, fragment B cannot expect fragment A to exist when
      * it has been created because fragment A's add/remove will be optimized out.
      * <p>
+     * It can also reorder the state changes of Fragments to allow for better Transitions.
+     * Added Fragments may have {@link Fragment#onCreate(Bundle)} called before replaced
+     * Fragments have {@link Fragment#onDestroy()} called.
+     * <p>
+     * {@link Fragment#postponeEnterTransition()} requires {@code setReorderingAllowed(true)}.
+     * <p>
      * The default is {@code false}.
      *
-     * @param allowOptimization {@code true} to enable optimizing operations
-     *                          or {@code false} to disable optimizing
+     * @param reorderingAllowed {@code true} to enable optimizing out redundant operations
+     *                          or {@code false} to disable optimizing out redundant
      *                          operations on this transaction.
      */
+    public abstract FragmentTransaction setReorderingAllowed(boolean reorderingAllowed);
+
+    /**
+     * @deprecated This has been renamed {@link #setReorderingAllowed(boolean)}.
+     */
+    @Deprecated
     public abstract FragmentTransaction setAllowOptimization(boolean allowOptimization);
 
     /**
+     * Add a Runnable to this transaction that will be run after this transaction has
+     * been committed. If fragment transactions are {@link #setReorderingAllowed(boolean) optimized}
+     * this may be after other subsequent fragment operations have also taken place, or operations
+     * in this transaction may have been optimized out due to the presence of a subsequent
+     * fragment transaction in the batch.
+     *
+     * <p>If a transaction is committed using {@link #commitAllowingStateLoss()} this runnable
+     * may be executed when the FragmentManager is in a state where new transactions may not
+     * be committed without allowing state loss.</p>
+     *
+     * <p><code>runOnCommit</code> may not be used with transactions
+     * {@link #addToBackStack(String) added to the back stack} as Runnables cannot be persisted
+     * with back stack state. {@link IllegalStateException} will be thrown if
+     * {@link #addToBackStack(String)} has been previously called for this transaction
+     * or if it is called after a call to <code>runOnCommit</code>.</p>
+     *
+     * @param runnable Runnable to add
+     * @return this FragmentTransaction
+     * @throws IllegalStateException if {@link #addToBackStack(String)} has been called
+     */
+    public abstract FragmentTransaction runOnCommit(Runnable runnable);
+
+    /**
      * Schedules a commit of this transaction.  The commit does
      * not happen immediately; it will be scheduled as work on the main thread
      * to be done the next time that thread is ready.
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
index f93a5c3..c5485d7 100644
--- a/fragment/java/android/support/v4/app/FragmentTransition.java
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -17,6 +17,7 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.view.ViewCompat;
 import android.util.SparseArray;
@@ -29,9 +30,9 @@
 import java.util.Map;
 
 /**
- * Contains the Fragment Transition functionality for both optimized and unoptimized
- * Fragment Transactions. With optimized fragment transactions, all Views have been
- * added to the View hierarchy prior to calling startTransitions. With unoptimized
+ * Contains the Fragment Transition functionality for both ordered and reordered
+ * Fragment Transactions. With reordered fragment transactions, all Views have been
+ * added to the View hierarchy prior to calling startTransitions. With ordered
  * fragment transactions, Views will be removed and added after calling startTransitions.
  */
 class FragmentTransition {
@@ -40,14 +41,16 @@
      * REPLACE operations have already been replaced by add/remove operations.
      */
     private static final int[] INVERSE_OPS = {
-            BackStackRecord.OP_NULL,   // inverse of OP_NULL (error)
-            BackStackRecord.OP_REMOVE, // inverse of OP_ADD
-            BackStackRecord.OP_NULL,   // inverse of OP_REPLACE (error)
-            BackStackRecord.OP_ADD,    // inverse of OP_REMOVE
-            BackStackRecord.OP_SHOW,   // inverse of OP_HIDE
-            BackStackRecord.OP_HIDE,   // inverse of OP_SHOW
-            BackStackRecord.OP_ATTACH, // inverse of OP_DETACH
-            BackStackRecord.OP_DETACH, // inverse of OP_ATTACH
+            BackStackRecord.OP_NULL,              // inverse of OP_NULL (error)
+            BackStackRecord.OP_REMOVE,            // inverse of OP_ADD
+            BackStackRecord.OP_NULL,              // inverse of OP_REPLACE (error)
+            BackStackRecord.OP_ADD,               // inverse of OP_REMOVE
+            BackStackRecord.OP_SHOW,              // inverse of OP_HIDE
+            BackStackRecord.OP_HIDE,              // inverse of OP_SHOW
+            BackStackRecord.OP_ATTACH,            // inverse of OP_DETACH
+            BackStackRecord.OP_DETACH,            // inverse of OP_ATTACH
+            BackStackRecord.OP_UNSET_PRIMARY_NAV, // inverse of OP_SET_PRIMARY_NAV
+            BackStackRecord.OP_SET_PRIMARY_NAV,   // inverse of OP_UNSET_PRIMARY_NAV
     };
 
     /**
@@ -59,9 +62,9 @@
      * {@link Fragment#getSharedElementReturnTransition()} and the entering
      * {@link Fragment#getReenterTransition()} will be run.
      * <p>
-     * With optimized Fragment Transitions, all Views have been added to the
+     * With reordered Fragment Transitions, all Views have been added to the
      * View hierarchy prior to calling this method. The incoming Fragment's Views
-     * will be INVISIBLE. With unoptimized Fragment Transitions, this method
+     * will be INVISIBLE. With ordered Fragment Transitions, this method
      * is called before any change has been made to the hierarchy. That means
      * that the added Fragments have not created their Views yet and the hierarchy
      * is unknown.
@@ -73,45 +76,48 @@
      *                   part of this transition.
      * @param endIndex One past the last index into records and isRecordPop to execute
      *                 as part of this transition.
-     * @param isOptimized true if this is an optimized transaction, meaning that the
+     * @param isReordered true if this is a reordered transaction, meaning that the
      *                    Views of incoming fragments have been added. false if the
      *                    transaction has yet to be run and Views haven't been created.
      */
     static void startTransitions(FragmentManagerImpl fragmentManager,
             ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
-            int startIndex, int endIndex, boolean isOptimized) {
-        if (fragmentManager.mCurState < Fragment.CREATED
-                || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            int startIndex, int endIndex, boolean isReordered) {
+        if (fragmentManager.mCurState < Fragment.CREATED) {
             return;
         }
-        SparseArray<FragmentContainerTransition> transitioningFragments =
-                new SparseArray<>();
-        for (int i = startIndex; i < endIndex; i++) {
-            final BackStackRecord record = records.get(i);
-            final boolean isPop = isRecordPop.get(i);
-            if (isPop) {
-                calculatePopFragments(record, transitioningFragments, isOptimized);
-            } else {
-                calculateFragments(record, transitioningFragments, isOptimized);
-            }
-        }
 
-        if (transitioningFragments.size() != 0) {
-            final View nonExistentView = new View(fragmentManager.mHost.getContext());
-            final int numContainers = transitioningFragments.size();
-            for (int i = 0; i < numContainers; i++) {
-                int containerId = transitioningFragments.keyAt(i);
-                ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
-                        records, isRecordPop, startIndex, endIndex);
-
-                FragmentContainerTransition containerTransition = transitioningFragments.valueAt(i);
-
-                if (isOptimized) {
-                    configureTransitionsOptimized(fragmentManager, containerId,
-                            containerTransition, nonExistentView, nameOverrides);
+        if (Build.VERSION.SDK_INT >= 21) {
+            SparseArray<FragmentContainerTransition> transitioningFragments =
+                    new SparseArray<>();
+            for (int i = startIndex; i < endIndex; i++) {
+                final BackStackRecord record = records.get(i);
+                final boolean isPop = isRecordPop.get(i);
+                if (isPop) {
+                    calculatePopFragments(record, transitioningFragments, isReordered);
                 } else {
-                    configureTransitionsUnoptimized(fragmentManager, containerId,
-                            containerTransition, nonExistentView, nameOverrides);
+                    calculateFragments(record, transitioningFragments, isReordered);
+                }
+            }
+
+            if (transitioningFragments.size() != 0) {
+                final View nonExistentView = new View(fragmentManager.mHost.getContext());
+                final int numContainers = transitioningFragments.size();
+                for (int i = 0; i < numContainers; i++) {
+                    int containerId = transitioningFragments.keyAt(i);
+                    ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
+                            records, isRecordPop, startIndex, endIndex);
+
+                    FragmentContainerTransition containerTransition =
+                            transitioningFragments.valueAt(i);
+
+                    if (isReordered) {
+                        configureTransitionsReordered(fragmentManager, containerId,
+                                containerTransition, nonExistentView, nameOverrides);
+                    } else {
+                        configureTransitionsOrdered(fragmentManager, containerId,
+                                containerTransition, nonExistentView, nameOverrides);
+                    }
                 }
             }
         }
@@ -170,7 +176,7 @@
 
     /**
      * Configures a transition for a single fragment container for which the transaction was
-     * optimized. That means that all Fragment Views have been added and incoming fragment
+     * reordered. That means that all Fragment Views have been added and incoming fragment
      * Views are marked invisible.
      *
      * @param fragmentManager The executing FragmentManagerImpl
@@ -183,7 +189,8 @@
      *                      the final fragment's Views as given in
      *                      {@link FragmentTransaction#addSharedElement(View, String)}.
      */
-    private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
+    @RequiresApi(21)
+    private static void configureTransitionsReordered(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
         ViewGroup sceneRoot = null;
@@ -203,7 +210,7 @@
         Object enterTransition = getEnterTransition(inFragment, inIsPop);
         Object exitTransition = getExitTransition(outFragment, outIsPop);
 
-        Object sharedElementTransition = configureSharedElementsOptimized(sceneRoot,
+        Object sharedElementTransition = configureSharedElementsReordered(sceneRoot,
                 nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
                 enterTransition, exitTransition);
 
@@ -226,12 +233,12 @@
         if (transition != null) {
             replaceHide(exitTransition, outFragment, exitingViews);
             ArrayList<String> inNames =
-                    FragmentTransitionCompat21.prepareSetNameOverridesOptimized(sharedElementsIn);
+                    FragmentTransitionCompat21.prepareSetNameOverridesReordered(sharedElementsIn);
             FragmentTransitionCompat21.scheduleRemoveTargets(transition,
                     enterTransition, enteringViews, exitTransition, exitingViews,
                     sharedElementTransition, sharedElementsIn);
             FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
-            FragmentTransitionCompat21.setNameOverridesOptimized(sceneRoot, sharedElementsOut,
+            FragmentTransitionCompat21.setNameOverridesReordered(sceneRoot, sharedElementsOut,
                     sharedElementsIn, inNames, nameOverrides);
             setViewVisibility(enteringViews, View.VISIBLE);
             FragmentTransitionCompat21.swapSharedElementTargets(sharedElementTransition,
@@ -244,6 +251,7 @@
      * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
      * transition, make the fragment's view GONE.
      */
+    @RequiresApi(21)
     private static void replaceHide(Object exitTransition, Fragment exitingFragment,
             final ArrayList<View> exitingViews) {
         if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
@@ -263,7 +271,7 @@
 
     /**
      * Configures a transition for a single fragment container for which the transaction was
-     * not optimized. That means that the transaction has not been executed yet, so incoming
+     * ordrered. That means that the transaction has not been executed yet, so incoming
      * Views are not yet known.
      *
      * @param fragmentManager The executing FragmentManagerImpl
@@ -276,7 +284,8 @@
      *                      the final fragment's Views as given in
      *                      {@link FragmentTransaction#addSharedElement(View, String)}.
      */
-    private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
+    @RequiresApi(21)
+    private static void configureTransitionsOrdered(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
         ViewGroup sceneRoot = null;
@@ -297,7 +306,7 @@
         ArrayList<View> sharedElementsOut = new ArrayList<>();
         ArrayList<View> sharedElementsIn = new ArrayList<>();
 
-        Object sharedElementTransition = configureSharedElementsUnoptimized(sceneRoot,
+        Object sharedElementTransition = configureSharedElementsOrdered(sceneRoot,
                 nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
                 enterTransition, exitTransition);
 
@@ -327,7 +336,7 @@
                     sharedElementTransition, sharedElementsIn);
             scheduleTargetChange(sceneRoot, inFragment, nonExistentView, sharedElementsIn,
                     enterTransition, enteringViews, exitTransition, exitingViews);
-            FragmentTransitionCompat21.setNameOverridesUnoptimized(sceneRoot, sharedElementsIn,
+            FragmentTransitionCompat21.setNameOverridesOrdered(sceneRoot, sharedElementsIn,
                     nameOverrides);
 
             FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
@@ -337,7 +346,7 @@
     }
 
     /**
-     * This method is used for fragment transitions for unoptimized transactions to change the
+     * This method is used for fragment transitions for ordrered transactions to change the
      * enter and exit transition targets after the call to
      * {@link FragmentTransitionCompat21#beginDelayedTransition(ViewGroup, Object)}. The exit
      * transition must ensure that it does not target any Views and the enter transition must start
@@ -353,6 +362,7 @@
      * @param exitTransition The exit transition of the outgoing fragment
      * @param exitingViews The exiting views of the outgoing fragment
      */
+    @RequiresApi(21)
     private static void scheduleTargetChange(final ViewGroup sceneRoot,
             final Fragment inFragment, final View nonExistentView,
             final ArrayList<View> sharedElementsIn,
@@ -395,6 +405,7 @@
      * @return A TransitionSet wrapping the shared element transition or null if no such transition
      * exists.
      */
+    @RequiresApi(21)
     private static Object getSharedElementTransition(Fragment inFragment,
             Fragment outFragment, boolean isPop) {
         if (inFragment == null || outFragment == null) {
@@ -409,6 +420,7 @@
     /**
      * Returns a clone of the enter transition or null if no such transition exists.
      */
+    @RequiresApi(21)
     private static Object getEnterTransition(Fragment inFragment, boolean isPop) {
         if (inFragment == null) {
             return null;
@@ -421,6 +433,7 @@
     /**
      * Returns a clone of the exit transition or null if no such transition exists.
      */
+    @RequiresApi(21)
     private static Object getExitTransition(Fragment outFragment, boolean isPop) {
         if (outFragment == null) {
             return null;
@@ -431,7 +444,7 @@
     }
 
     /**
-     * Configures the shared elements of an optimized fragment transaction's transition.
+     * Configures the shared elements of a reordered fragment transaction's transition.
      * This retrieves the shared elements of the outgoing and incoming fragments, maps the
      * views, and sets up the epicenter on the transitions.
      * <p>
@@ -457,7 +470,8 @@
      *                       epicenter
      * @return The shared element transition or null if no shared elements exist
      */
-    private static Object configureSharedElementsOptimized(final ViewGroup sceneRoot,
+    @RequiresApi(21)
+    private static Object configureSharedElementsReordered(final ViewGroup sceneRoot,
             final View nonExistentView, final ArrayMap<String, String> nameOverrides,
             final FragmentContainerTransition fragments,
             final ArrayList<View> sharedElementsOut,
@@ -558,7 +572,7 @@
     }
 
     /**
-     * Configures the shared elements of an unoptimized fragment transaction's transition.
+     * Configures the shared elements of an ordered fragment transaction's transition.
      * This retrieves the shared elements of the incoming fragments, and schedules capturing
      * the incoming fragment's shared elements. It also maps the views, and sets up the epicenter
      * on the transitions.
@@ -585,7 +599,8 @@
      *                       epicenter
      * @return The shared element transition or null if no shared elements exist
      */
-    private static Object configureSharedElementsUnoptimized(final ViewGroup sceneRoot,
+    @RequiresApi(21)
+    private static Object configureSharedElementsOrdered(final ViewGroup sceneRoot,
             final View nonExistentView, final ArrayMap<String, String> nameOverrides,
             final FragmentContainerTransition fragments,
             final ArrayList<View> sharedElementsOut,
@@ -681,6 +696,7 @@
      * @return The mapping of shared element names to the Views in the hierarchy or null
      * if there is no shared element transition.
      */
+    @RequiresApi(21)
     private static ArrayMap<String, View> captureOutSharedElements(
             ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
             FragmentContainerTransition fragments) {
@@ -736,6 +752,7 @@
      * @return The mapping of shared element names to the Views in the hierarchy or null
      * if there is no shared element transition.
      */
+    @RequiresApi(21)
     private static ArrayMap<String, View> captureInSharedElements(
             ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
             FragmentContainerTransition fragments) {
@@ -831,6 +848,7 @@
      * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
      * @param outTransaction The transaction that caused the fragment to be removed.
      */
+    @RequiresApi(21)
     private static void setOutEpicenter(Object sharedElementTransition,
             Object exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
             BackStackRecord outTransaction) {
@@ -895,6 +913,7 @@
         }
     }
 
+    @RequiresApi(21)
     private static ArrayList<View> configureEnteringExitingViews(Object transition,
             Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
         ArrayList<View> viewList = null;
@@ -932,6 +951,7 @@
      * Merges exit, shared element, and enter transitions so that they act together or
      * sequentially as defined in the fragments.
      */
+    @RequiresApi(21)
     private static Object mergeTransitions(Object enterTransition,
             Object exitTransition, Object sharedElementTransition, Fragment inFragment,
             boolean isPop) {
@@ -969,11 +989,11 @@
      */
     public static void calculateFragments(BackStackRecord transaction,
             SparseArray<FragmentContainerTransition> transitioningFragments,
-            boolean isOptimized) {
+            boolean isReordered) {
         final int numOps = transaction.mOps.size();
         for (int opNum = 0; opNum < numOps; opNum++) {
             final BackStackRecord.Op op = transaction.mOps.get(opNum);
-            addToFirstInLastOut(transaction, op, transitioningFragments, false, isOptimized);
+            addToFirstInLastOut(transaction, op, transitioningFragments, false, isReordered);
         }
     }
 
@@ -986,14 +1006,14 @@
      *                               this method.
      */
     public static void calculatePopFragments(BackStackRecord transaction,
-            SparseArray<FragmentContainerTransition> transitioningFragments, boolean isOptimized) {
+            SparseArray<FragmentContainerTransition> transitioningFragments, boolean isReordered) {
         if (!transaction.mManager.mContainer.onHasView()) {
             return; // nothing to see, so no transitions
         }
         final int numOps = transaction.mOps.size();
         for (int opNum = numOps - 1; opNum >= 0; opNum--) {
             final BackStackRecord.Op op = transaction.mOps.get(opNum);
-            addToFirstInLastOut(transaction, op, transitioningFragments, true, isOptimized);
+            addToFirstInLastOut(transaction, op, transitioningFragments, true, isReordered);
         }
     }
 
@@ -1006,14 +1026,18 @@
      * @param transitioningFragments A structure holding the first in and last out fragments
      *                               for each fragment container.
      * @param isPop Is the operation a pop?
-     * @param isOptimizedTransaction True if the operations have been partially executed and the
+     * @param isReorderedTransaction True if the operations have been partially executed and the
      *                               added fragments have Views in the hierarchy or false if the
      *                               operations haven't been executed yet.
      */
+    @SuppressWarnings("ReferenceEquality")
     private static void addToFirstInLastOut(BackStackRecord transaction, BackStackRecord.Op op,
             SparseArray<FragmentContainerTransition> transitioningFragments, boolean isPop,
-            boolean isOptimizedTransaction) {
+            boolean isReorderedTransaction) {
         final Fragment fragment = op.fragment;
+        if (fragment == null) {
+            return; // no fragment, no transition
+        }
         final int containerId = fragment.mContainerId;
         if (containerId == 0) {
             return; // no container, no transition
@@ -1025,7 +1049,7 @@
         boolean wasAdded = false;
         switch (command) {
             case BackStackRecord.OP_SHOW:
-                if (isOptimizedTransaction) {
+                if (isReorderedTransaction) {
                     setLastIn = fragment.mHiddenChanged && !fragment.mHidden && fragment.mAdded;
                 } else {
                     setLastIn = fragment.mHidden;
@@ -1034,7 +1058,7 @@
                 break;
             case BackStackRecord.OP_ADD:
             case BackStackRecord.OP_ATTACH:
-                if (isOptimizedTransaction) {
+                if (isReorderedTransaction) {
                     setLastIn = fragment.mIsNewlyAdded;
                 } else {
                     setLastIn = !fragment.mAdded && !fragment.mHidden;
@@ -1042,7 +1066,7 @@
                 wasAdded = true;
                 break;
             case BackStackRecord.OP_HIDE:
-                if (isOptimizedTransaction) {
+                if (isReorderedTransaction) {
                     setFirstOut = fragment.mHiddenChanged && fragment.mAdded && fragment.mHidden;
                 } else {
                     setFirstOut = fragment.mAdded && !fragment.mHidden;
@@ -1051,7 +1075,7 @@
                 break;
             case BackStackRecord.OP_REMOVE:
             case BackStackRecord.OP_DETACH:
-                if (isOptimizedTransaction) {
+                if (isReorderedTransaction) {
                     setFirstOut = !fragment.mAdded && fragment.mView != null
                             && fragment.mView.getVisibility() == View.VISIBLE
                             && fragment.mPostponedAlpha >= 0;
@@ -1069,18 +1093,18 @@
             containerTransition.lastInIsPop = isPop;
             containerTransition.lastInTransaction = transaction;
         }
-        if (!isOptimizedTransaction && wasAdded) {
+        if (!isReorderedTransaction && wasAdded) {
             if (containerTransition != null && containerTransition.firstOut == fragment) {
                 containerTransition.firstOut = null;
             }
 
-            /**
+            /*
              * Ensure that fragments that are entering are at least at the CREATED state
              * so that they may load Transitions using TransitionInflater.
              */
             FragmentManagerImpl manager = transaction.mManager;
             if (fragment.mState < Fragment.CREATED && manager.mCurState >= Fragment.CREATED
-                    && !transaction.mAllowOptimization) {
+                    && !transaction.mReorderingAllowed) {
                 manager.makeActive(fragment);
                 manager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
             }
@@ -1093,7 +1117,7 @@
             containerTransition.firstOutTransaction = transaction;
         }
 
-        if (!isOptimizedTransaction && wasRemoved
+        if (!isReorderedTransaction && wasRemoved
                 && (containerTransition != null && containerTransition.lastIn == fragment)) {
             containerTransition.lastIn = null;
         }
diff --git a/fragment/java/android/support/v4/app/FragmentTransitionCompat21.java b/fragment/java/android/support/v4/app/FragmentTransitionCompat21.java
new file mode 100644
index 0000000..600b1b8
--- /dev/null
+++ b/fragment/java/android/support/v4/app/FragmentTransitionCompat21.java
@@ -0,0 +1,573 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@RequiresApi(21)
+class FragmentTransitionCompat21 {
+
+    /**
+     * Returns a clone of a transition or null if it is null
+     */
+    public static Object cloneTransition(Object transition) {
+        Transition copy = null;
+        if (transition != null) {
+            copy = ((Transition) transition).clone();
+        }
+        return copy;
+    }
+
+    /**
+     * Wraps a transition in a TransitionSet and returns the set. If transition is null, null is
+     * returned.
+     */
+    public static Object wrapTransitionInSet(Object transition) {
+        if (transition == null) {
+            return null;
+        }
+        TransitionSet transitionSet = new TransitionSet();
+        transitionSet.addTransition((Transition) transition);
+        return transitionSet;
+    }
+
+    /**
+     * Finds all children of the shared elements and sets the wrapping TransitionSet
+     * targets to point to those. It also limits transitions that have no targets to the
+     * specific shared elements. This allows developers to target child views of the
+     * shared elements specifically, but this doesn't happen by default.
+     */
+    public static void setSharedElementTargets(Object transitionObj,
+            View nonExistentView, ArrayList<View> sharedViews) {
+        TransitionSet transition = (TransitionSet) transitionObj;
+        final List<View> views = transition.getTargets();
+        views.clear();
+        final int count = sharedViews.size();
+        for (int i = 0; i < count; i++) {
+            final View view = sharedViews.get(i);
+            bfsAddViewChildren(views, view);
+        }
+        views.add(nonExistentView);
+        sharedViews.add(nonExistentView);
+        addTargets(transition, sharedViews);
+    }
+
+    /**
+     * Uses a breadth-first scheme to add startView and all of its children to views.
+     * It won't add a child if it is already in views.
+     */
+    private static void bfsAddViewChildren(final List<View> views, final View startView) {
+        final int startIndex = views.size();
+        if (containedBeforeIndex(views, startView, startIndex)) {
+            return; // This child is already in the list, so all its children are also.
+        }
+        views.add(startView);
+        for (int index = startIndex; index < views.size(); index++) {
+            final View view = views.get(index);
+            if (view instanceof ViewGroup) {
+                ViewGroup viewGroup = (ViewGroup) view;
+                final int childCount =  viewGroup.getChildCount();
+                for (int childIndex = 0; childIndex < childCount; childIndex++) {
+                    final View child = viewGroup.getChildAt(childIndex);
+                    if (!containedBeforeIndex(views, child, startIndex)) {
+                        views.add(child);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Does a linear search through views for view, limited to maxIndex.
+     */
+    private static boolean containedBeforeIndex(final List<View> views, final View view,
+            final int maxIndex) {
+        for (int i = 0; i < maxIndex; i++) {
+            if (views.get(i) == view) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets a transition epicenter to the rectangle of a given View.
+     */
+    public static void setEpicenter(Object transitionObj, View view) {
+        if (view != null) {
+            Transition transition = (Transition) transitionObj;
+            final Rect epicenter = new Rect();
+            getBoundsOnScreen(view, epicenter);
+
+            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+                @Override
+                public Rect onGetEpicenter(Transition transition) {
+                    return epicenter;
+                }
+            });
+        }
+    }
+
+    /**
+     * Replacement for view.getBoundsOnScreen because that is not public. This returns a rect
+     * containing the bounds relative to the screen that the view is in.
+     */
+    public static void getBoundsOnScreen(View view, Rect epicenter) {
+        int[] loc = new int[2];
+        view.getLocationOnScreen(loc);
+        epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
+    }
+
+    /**
+     * This method adds views as targets to the transition, but only if the transition
+     * doesn't already have a target. It is best for views to contain one View object
+     * that does not exist in the view hierarchy (state.nonExistentView) so that
+     * when they are removed later, a list match will suffice to remove the targets.
+     * Otherwise, if you happened to have targeted the exact views for the transition,
+     * the replaceTargets call will remove them unexpectedly.
+     */
+    public static void addTargets(Object transitionObj, ArrayList<View> views) {
+        Transition transition = (Transition) transitionObj;
+        if (transition == null) {
+            return;
+        }
+        if (transition instanceof TransitionSet) {
+            TransitionSet set = (TransitionSet) transition;
+            int numTransitions = set.getTransitionCount();
+            for (int i = 0; i < numTransitions; i++) {
+                Transition child = set.getTransitionAt(i);
+                addTargets(child, views);
+            }
+        } else if (!hasSimpleTarget(transition)) {
+            List<View> targets = transition.getTargets();
+            if (isNullOrEmpty(targets)) {
+                // We can just add the target views
+                int numViews = views.size();
+                for (int i = 0; i < numViews; i++) {
+                    transition.addTarget(views.get(i));
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if there are any targets based on ID, transition or type.
+     */
+    private static boolean hasSimpleTarget(Transition transition) {
+        return !isNullOrEmpty(transition.getTargetIds())
+                || !isNullOrEmpty(transition.getTargetNames())
+                || !isNullOrEmpty(transition.getTargetTypes());
+    }
+
+    /**
+     * Simple utility to detect if a list is null or has no elements.
+     */
+    private static boolean isNullOrEmpty(List list) {
+        return list == null || list.isEmpty();
+    }
+
+    /**
+     * Creates a TransitionSet that plays all passed transitions together. Any null
+     * transitions passed will not be added to the set. If all are null, then an empty
+     * TransitionSet will be returned.
+     */
+    public static Object mergeTransitionsTogether(Object transition1, Object transition2,
+            Object transition3) {
+        TransitionSet transitionSet = new TransitionSet();
+        if (transition1 != null) {
+            transitionSet.addTransition((Transition) transition1);
+        }
+        if (transition2 != null) {
+            transitionSet.addTransition((Transition) transition2);
+        }
+        if (transition3 != null) {
+            transitionSet.addTransition((Transition) transition3);
+        }
+        return transitionSet;
+    }
+
+    /**
+     * After the transition completes, the fragment's view is set to GONE and the exiting
+     * views are set to VISIBLE.
+     */
+    public static void scheduleHideFragmentView(Object exitTransitionObj, final View fragmentView,
+            final ArrayList<View> exitingViews) {
+        Transition exitTransition = (Transition) exitTransitionObj;
+        exitTransition.addListener(new Transition.TransitionListener() {
+            @Override
+            public void onTransitionStart(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+                transition.removeListener(this);
+                fragmentView.setVisibility(View.GONE);
+                final int numViews = exitingViews.size();
+                for (int i = 0; i < numViews; i++) {
+                    exitingViews.get(i).setVisibility(View.VISIBLE);
+                }
+            }
+
+            @Override
+            public void onTransitionCancel(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionPause(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionResume(Transition transition) {
+            }
+        });
+    }
+
+    /**
+     * Combines enter, exit, and shared element transition so that they play in the proper
+     * sequence. First the exit transition plays along with the shared element transition.
+     * When the exit transition completes, the enter transition starts. The shared element
+     * transition can continue running while the enter transition plays.
+     *
+     * @return A TransitionSet with all of enter, exit, and shared element transitions in
+     * it (modulo null values), ordered such that they play in the proper sequence.
+     */
+    public static Object mergeTransitionsInSequence(Object exitTransitionObj,
+            Object enterTransitionObj, Object sharedElementTransitionObj) {
+        // First do exit, then enter, but allow shared element transition to happen
+        // during both.
+        Transition staggered = null;
+        final Transition exitTransition = (Transition) exitTransitionObj;
+        final Transition enterTransition = (Transition) enterTransitionObj;
+        final Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
+        if (exitTransition != null && enterTransition != null) {
+            staggered = new TransitionSet()
+                    .addTransition(exitTransition)
+                    .addTransition(enterTransition)
+                    .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+        } else if (exitTransition != null) {
+            staggered = exitTransition;
+        } else if (enterTransition != null) {
+            staggered = enterTransition;
+        }
+        if (sharedElementTransition != null) {
+            TransitionSet together = new TransitionSet();
+            if (staggered != null) {
+                together.addTransition(staggered);
+            }
+            together.addTransition(sharedElementTransition);
+            return together;
+        } else {
+            return staggered;
+        }
+    }
+
+    /**
+     * Calls {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)}.
+     */
+    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
+        TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
+    }
+
+    /**
+     * Prepares for setting the shared element names by gathering the names of the incoming
+     * shared elements and clearing them. {@link #setNameOverridesReordered(View, ArrayList,
+     * ArrayList, ArrayList, Map)} must be called after this to complete setting the shared element
+     * name overrides. This must be called before
+     * {@link #beginDelayedTransition(ViewGroup, Object)}.
+     */
+    public static ArrayList<String> prepareSetNameOverridesReordered(
+            final ArrayList<View> sharedElementsIn) {
+        final ArrayList<String> names = new ArrayList<>();
+        final int numSharedElements = sharedElementsIn.size();
+        for (int i = 0; i < numSharedElements; i++) {
+            final View view = sharedElementsIn.get(i);
+            names.add(view.getTransitionName());
+            view.setTransitionName(null);
+        }
+        return names;
+    }
+
+    /**
+     * Changes the shared element names for the incoming shared eleemnts to match those of the
+     * outgoing shared elements. This also temporarily clears the shared element names of the
+     * outgoing shared elements. Must be called after
+     * {@link #beginDelayedTransition(ViewGroup, Object)}.
+     */
+    public static void setNameOverridesReordered(final View sceneRoot,
+            final ArrayList<View> sharedElementsOut, final ArrayList<View> sharedElementsIn,
+            final ArrayList<String> inNames, final Map<String, String> nameOverrides) {
+        final int numSharedElements = sharedElementsIn.size();
+        final ArrayList<String> outNames = new ArrayList<>();
+
+        for (int i = 0; i < numSharedElements; i++) {
+            final View view = sharedElementsOut.get(i);
+            final String name = view.getTransitionName();
+            outNames.add(name);
+            if (name == null) {
+                continue;
+            }
+            view.setTransitionName(null);
+            final String inName = nameOverrides.get(name);
+            for (int j = 0; j < numSharedElements; j++) {
+                if (inName.equals(inNames.get(j))) {
+                    sharedElementsIn.get(j).setTransitionName(name);
+                    break;
+                }
+            }
+        }
+
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < numSharedElements; i++) {
+                    sharedElementsIn.get(i).setTransitionName(inNames.get(i));
+                    sharedElementsOut.get(i).setTransitionName(outNames.get(i));
+                }
+            }
+        });
+    }
+
+    /**
+     * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
+     * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
+     *                           a normal View or a ViewGroup with
+     *                           {@link android.view.ViewGroup#isTransitionGroup()} true.
+     * @param view The base of the view hierarchy to look in.
+     */
+    public static void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
+        if (view.getVisibility() == View.VISIBLE) {
+            if (view instanceof ViewGroup) {
+                ViewGroup viewGroup = (ViewGroup) view;
+                if (viewGroup.isTransitionGroup()) {
+                    transitioningViews.add(viewGroup);
+                } else {
+                    int count = viewGroup.getChildCount();
+                    for (int i = 0; i < count; i++) {
+                        View child = viewGroup.getChildAt(i);
+                        captureTransitioningViews(transitioningViews, child);
+                    }
+                }
+            } else {
+                transitioningViews.add(view);
+            }
+        }
+    }
+
+    /**
+     * Finds all views that have transition names in the hierarchy under the given view and
+     * stores them in {@code namedViews} map with the name as the key.
+     */
+    public static void findNamedViews(Map<String, View> namedViews, View view) {
+        if (view.getVisibility() == View.VISIBLE) {
+            String transitionName = view.getTransitionName();
+            if (transitionName != null) {
+                namedViews.put(transitionName, view);
+            }
+            if (view instanceof ViewGroup) {
+                ViewGroup viewGroup = (ViewGroup) view;
+                int count = viewGroup.getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View child = viewGroup.getChildAt(i);
+                    findNamedViews(namedViews, child);
+                }
+            }
+        }
+    }
+
+    public static void setNameOverridesOrdered(final View sceneRoot,
+            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                final int numSharedElements = sharedElementsIn.size();
+                for (int i = 0; i < numSharedElements; i++) {
+                    View view = sharedElementsIn.get(i);
+                    String name = view.getTransitionName();
+                    if (name != null) {
+                        String inName = findKeyForValue(nameOverrides, name);
+                        view.setTransitionName(inName);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Utility to find the String key in {@code map} that maps to {@code value}.
+     */
+    private static String findKeyForValue(Map<String, String> map, String value) {
+        for (Map.Entry<String, String> entry : map.entrySet()) {
+            if (value.equals(entry.getValue())) {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * After the transition has started, remove all targets that we added to the transitions
+     * so that the transitions are left in a clean state.
+     */
+    public static void scheduleRemoveTargets(final Object overallTransitionObj,
+            final Object enterTransition, final ArrayList<View> enteringViews,
+            final Object exitTransition, final ArrayList<View> exitingViews,
+            final Object sharedElementTransition, final ArrayList<View> sharedElementsIn) {
+        final Transition overallTransition = (Transition) overallTransitionObj;
+        overallTransition.addListener(new Transition.TransitionListener() {
+            @Override
+            public void onTransitionStart(Transition transition) {
+                if (enterTransition != null) {
+                    replaceTargets(enterTransition, enteringViews, null);
+                }
+                if (exitTransition != null) {
+                    replaceTargets(exitTransition, exitingViews, null);
+                }
+                if (sharedElementTransition != null) {
+                    replaceTargets(sharedElementTransition, sharedElementsIn, null);
+                }
+            }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionCancel(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionPause(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionResume(Transition transition) {
+            }
+        });
+    }
+
+    /**
+     * Swap the targets for the shared element transition from those Views in sharedElementsOut
+     * to those in sharedElementsIn
+     */
+    public static void swapSharedElementTargets(Object sharedElementTransitionObj,
+            ArrayList<View> sharedElementsOut, ArrayList<View> sharedElementsIn) {
+        TransitionSet sharedElementTransition = (TransitionSet) sharedElementTransitionObj;
+        if (sharedElementTransition != null) {
+            sharedElementTransition.getTargets().clear();
+            sharedElementTransition.getTargets().addAll(sharedElementsIn);
+            replaceTargets(sharedElementTransition, sharedElementsOut, sharedElementsIn);
+        }
+    }
+
+
+    /**
+     * This method removes the views from transitions that target ONLY those views and
+     * replaces them with the new targets list.
+     * The views list should match those added in addTargets and should contain
+     * one view that is not in the view hierarchy (state.nonExistentView).
+     */
+    public static void replaceTargets(Object transitionObj, ArrayList<View> oldTargets,
+            ArrayList<View> newTargets) {
+        Transition transition = (Transition) transitionObj;
+        if (transition instanceof TransitionSet) {
+            TransitionSet set = (TransitionSet) transition;
+            int numTransitions = set.getTransitionCount();
+            for (int i = 0; i < numTransitions; i++) {
+                Transition child = set.getTransitionAt(i);
+                replaceTargets(child, oldTargets, newTargets);
+            }
+        } else if (!hasSimpleTarget(transition)) {
+            List<View> targets = transition.getTargets();
+            if (targets != null && targets.size() == oldTargets.size()
+                    && targets.containsAll(oldTargets)) {
+                // We have an exact match. We must have added these earlier in addTargets
+                final int targetCount = newTargets == null ? 0 : newTargets.size();
+                for (int i = 0; i < targetCount; i++) {
+                    transition.addTarget(newTargets.get(i));
+                }
+                for (int i = oldTargets.size() - 1; i >= 0; i--) {
+                    transition.removeTarget(oldTargets.get(i));
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a View target to a transition. If transitionObj is null, nothing is done.
+     */
+    public static void addTarget(Object transitionObj, View view) {
+        if (transitionObj != null) {
+            Transition transition = (Transition) transitionObj;
+            transition.addTarget(view);
+        }
+    }
+
+    /**
+     * Remove a View target to a transition. If transitionObj is null, nothing is done.
+     */
+    public static void removeTarget(Object transitionObj, View view) {
+        if (transitionObj != null) {
+            Transition transition = (Transition) transitionObj;
+            transition.removeTarget(view);
+        }
+    }
+
+    /**
+     * Sets the epicenter of a transition to a rect object. The object can be modified until
+     * the transition runs.
+     */
+    public static void setEpicenter(Object transitionObj, final Rect epicenter) {
+        if (transitionObj != null) {
+            Transition transition = (Transition) transitionObj;
+            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+                @Override
+                public Rect onGetEpicenter(Transition transition) {
+                    if (epicenter == null || epicenter.isEmpty()) {
+                        return null;
+                    }
+                    return epicenter;
+                }
+            });
+        }
+    }
+
+    public static void scheduleNameReset(final ViewGroup sceneRoot,
+            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                final int numSharedElements = sharedElementsIn.size();
+                for (int i = 0; i < numSharedElements; i++) {
+                    final View view = sharedElementsIn.get(i);
+                    final String name = view.getTransitionName();
+                    final String inName = nameOverrides.get(name);
+                    view.setTransitionName(inName);
+                }
+            }
+        });
+    }
+}
diff --git a/fragment/java/android/support/v4/app/NoSaveStateFrameLayout.java b/fragment/java/android/support/v4/app/NoSaveStateFrameLayout.java
deleted file mode 100644
index 74cb319..0000000
--- a/fragment/java/android/support/v4/app/NoSaveStateFrameLayout.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2011 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.app;
-
-import android.content.Context;
-import android.os.Parcelable;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * Pre-Honeycomb versions of the platform don't have {@link View#setSaveFromParentEnabled(boolean)},
- * so instead we insert this between the view and its parent.
- */
-class NoSaveStateFrameLayout extends FrameLayout {
-    static ViewGroup wrap(View child) {
-        NoSaveStateFrameLayout wrapper = new NoSaveStateFrameLayout(child.getContext());
-        ViewGroup.LayoutParams childParams = child.getLayoutParams();
-        if (childParams != null) {
-            wrapper.setLayoutParams(childParams);
-        }
-        NoSaveStateFrameLayout.LayoutParams lp = new NoSaveStateFrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-        child.setLayoutParams(lp);
-        wrapper.addView(child);
-        return wrapper;
-    }
-
-    public NoSaveStateFrameLayout(Context context) {
-        super(context);
-    }
-
-    /**
-     * Override to prevent freezing of any child views.
-     */
-    @Override
-    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
-        dispatchFreezeSelfOnly(container);
-    }
-
-    /**
-     * Override to prevent thawing of any child views.
-     */
-    @Override
-    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
-        dispatchThawSelfOnly(container);
-    }
-}
diff --git a/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java b/fragment/java/android/support/v4/app/OneShotPreDrawListener.java
similarity index 100%
rename from fragment/api21/android/support/v4/app/OneShotPreDrawListener.java
rename to fragment/java/android/support/v4/app/OneShotPreDrawListener.java
diff --git a/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java b/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
deleted file mode 100644
index 37fbaeb..0000000
--- a/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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
- */
-
-package android.support.v4.app;
-
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-
-/**
- * Base class for {@code FragmentActivity} to be able to use v16 APIs.
- *
- * @hide
- */
-abstract class BaseFragmentActivityJB extends BaseFragmentActivityHoneycomb {
-
-    // We need to keep track of whether startActivityForResult originated from a Fragment, so we
-    // can conditionally check whether the requestCode collides with our reserved ID space for the
-    // request index (see above). Unfortunately we can't just call
-    // super.startActivityForResult(...) to bypass the check when the call didn't come from a
-    // fragment, since we need to use the ActivityCompat version for backward compatibility.
-    boolean mStartedActivityFromFragment;
-
-    @RequiresApi(16)
-    @Override
-    public void startActivityForResult(Intent intent, int requestCode,
-            @Nullable Bundle options) {
-        // If this was started from a Fragment we've already checked the upper 16 bits were not in
-        // use, and then repurposed them for the Fragment's index.
-        if (!mStartedActivityFromFragment) {
-            if (requestCode != -1) {
-                checkForValidRequestCode(requestCode);
-            }
-        }
-        super.startActivityForResult(intent, requestCode, options);
-    }
-
-    @RequiresApi(16)
-    @Override
-    public void startIntentSenderForResult(IntentSender intent, int requestCode,
-            @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
-            Bundle options) throws IntentSender.SendIntentException {
-        // If this was started from a Fragment we've already checked the upper 16 bits were not in
-        // use, and then repurposed them for the Fragment's index.
-        if (!mStartedIntentSenderFromFragment) {
-            if (requestCode != -1) {
-                checkForValidRequestCode(requestCode);
-            }
-        }
-        super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues,
-                extraFlags, options);
-    }
-}
diff --git a/fragment/lint-baseline.xml b/fragment/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/fragment/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/fragment/tests/AndroidManifest.xml b/fragment/tests/AndroidManifest.xml
index b442c85..e6a520e 100644
--- a/fragment/tests/AndroidManifest.xml
+++ b/fragment/tests/AndroidManifest.xml
@@ -15,13 +15,8 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.fragment.test">
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.VIBRATE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
@@ -29,22 +24,20 @@
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
 
     <application
-            android:supportsRtl="true"
-            android:theme="@style/TestActivityTheme">
-        <uses-library android:name="android.test.runner" />
+        android:supportsRtl="true"
+        android:theme="@style/TestActivityTheme">
         <activity android:name="android.support.v4.app.test.FragmentTestActivity"/>
 
-        <activity android:name="android.support.v4.app.test.EmptyFragmentTestActivity" />
+        <activity android:name="android.support.v4.app.test.EmptyFragmentTestActivity"/>
 
-        <activity android:name="android.support.v4.app.test.FragmentResultActivity" />
+        <activity android:name="android.support.v4.app.test.FragmentResultActivity"/>
 
-        <activity android:name="android.support.v4.app.test.LoaderActivity" />
+        <activity android:name="android.support.v4.app.test.LoaderActivity"/>
 
         <activity android:name="android.support.v4.app.test.NewIntentActivity"
-                  android:launchMode="singleInstance" />
+                  android:launchMode="singleInstance"/>
+
+        <activity android:name="android.support.v4.app.test.NonConfigOnStopActivity"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.support.fragment.test"
-                     />
 </manifest>
diff --git a/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
index c82b61d..1ccb87b 100644
--- a/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
+++ b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
@@ -18,6 +18,7 @@
 import android.os.SystemClock;
 
 import org.mockito.exceptions.base.MockitoAssertionError;
+import org.mockito.internal.verification.VerificationModeFactory;
 import org.mockito.internal.verification.api.VerificationData;
 import org.mockito.invocation.Invocation;
 import org.mockito.verification.VerificationMode;
@@ -74,6 +75,11 @@
                     + data.getWanted().toString());
         }
 
+        @Override
+        public VerificationMode description(String description) {
+            return VerificationModeFactory.description(this, description);
+        }
+
         private void markAllInvocationsAsVerified(VerificationData data) {
             for (Invocation invocation : data.getAllInvocations()) {
                 invocation.markVerified();
diff --git a/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
index a822a0c..8c3d6ed 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Instrumentation;
-import android.os.Build;
 import android.os.Parcelable;
 import android.support.annotation.AnimRes;
 import android.support.fragment.test.R;
@@ -235,7 +234,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .add(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -260,7 +259,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .remove(fragment)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -277,7 +276,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -288,7 +287,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -296,9 +295,7 @@
         assertPostponed(fragment2, 0);
         assertNotNull(fragment1.getView());
         assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
-            assertEquals(1f, fragment1.getView().getAlpha(), 0f);
-        }
+        assertEquals(1f, fragment1.getView().getAlpha(), 0f);
         assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
 
         fragment2.startPostponedEnterTransition();
@@ -315,7 +312,7 @@
         final AnimatorFragment fragment1 = new AnimatorFragment();
         fm.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         assertEquals(0, fragment1.numAnimators);
@@ -327,7 +324,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -339,9 +336,7 @@
 
         assertNotNull(fragment1.getView());
         assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
-            assertEquals(1f, fragment1.getView().getAlpha(), 0f);
-        }
+        assertEquals(1f, fragment1.getView().getAlpha(), 0f);
         assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
         assertTrue(fragment1.isAdded());
 
@@ -413,6 +408,40 @@
         assertNotNull(fragment1restored.getView());
     }
 
+    // When an animation is running on a Fragment's View, the view shouldn't be
+    // prevented from being removed. There's no way to directly test this, so we have to
+    // test to see if the animation is still running.
+    @Test
+    public void clearAnimations() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final View fragmentView = fragment1.getView();
+
+        final TranslateAnimation xAnimation = new TranslateAnimation(0, 1000, 0, 0);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                fragmentView.startAnimation(xAnimation);
+            }
+        });
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertNull(fragmentView.getAnimation());
+            }
+        });
+    }
+
     private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
         assertFragmentAnimation(fragment, 1, true, ENTER);
 
@@ -463,12 +492,8 @@
     private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
             throws InterruptedException {
         assertTrue(fragment.mOnCreateViewCalled);
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
-            assertEquals(View.INVISIBLE, fragment.getView().getVisibility());
-        } else {
-            assertEquals(View.VISIBLE, fragment.getView().getVisibility());
-            assertEquals(0f, fragment.getView().getAlpha(), 0f);
-        }
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+        assertEquals(0f, fragment.getView().getAlpha(), 0f);
         assertEquals(expectedAnimators, fragment.numAnimators);
     }
 
diff --git a/fragment/tests/java/android/support/v4/app/FragmentAnimatorTest.java b/fragment/tests/java/android/support/v4/app/FragmentAnimatorTest.java
new file mode 100644
index 0000000..ce88e8f
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentAnimatorTest.java
@@ -0,0 +1,587 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.os.Build;
+import android.os.Parcelable;
+import android.support.annotation.AnimatorRes;
+import android.support.annotation.RequiresApi;
+import android.support.fragment.test.R;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.support.v4.view.ViewCompat;
+import android.util.Pair;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+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 FragmentAnimatorTest {
+    // These are pretend resource IDs for animators. We don't need real ones since we
+    // load them by overriding onCreateAnimator
+    @AnimatorRes
+    private static final int ENTER = 1;
+    @AnimatorRes
+    private static final int EXIT = 2;
+    @AnimatorRes
+    private static final int POP_ENTER = 3;
+    @AnimatorRes
+    private static final int POP_EXIT = 4;
+
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    @Before
+    public void setupContainer() {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+    }
+
+    // Ensure that adding and popping a Fragment uses the enter and popExit animators
+    @Test
+    public void addAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .add(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that removing and popping a Fragment uses the exit and popEnter animators
+    @Test
+    public void removeAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment, "1")
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .remove(fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPopEnter(fragment);
+    }
+
+    // Ensure that showing and popping a Fragment uses the enter and popExit animators
+    // This tests reordered transactions
+    @Test
+    public void showAnimatorsReordered() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(View.GONE, fragment.getView().getVisibility());
+
+            }
+        });
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .show(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+            }
+        });
+
+        assertEnterPopExit(fragment);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(View.GONE, fragment.getView().getVisibility());
+            }
+        });
+    }
+
+    // Ensure that showing and popping a Fragment uses the enter and popExit animators
+    // This tests ordered transactions
+    @Test
+    public void showAnimatorsOrdered() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .hide(fragment)
+                .setReorderingAllowed(false)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(View.GONE, fragment.getView().getVisibility());
+
+            }
+        });
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .show(fragment)
+                .setReorderingAllowed(false)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+            }
+        });
+
+        assertEnterPopExit(fragment);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(View.GONE, fragment.getView().getVisibility());
+            }
+        });
+    }
+
+    // Ensure that hiding and popping a Fragment uses the exit and popEnter animators
+    @Test
+    public void hideAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment, "1")
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .hide(fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPopEnter(fragment);
+    }
+
+    // Ensure that attaching and popping a Fragment uses the enter and popExit animators
+    @Test
+    public void attachAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .detach(fragment)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .attach(fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that detaching and popping a Fragment uses the exit and popEnter animators
+    @Test
+    public void detachAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment, "1")
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .detach(fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPopEnter(fragment);
+    }
+
+    // Replace should exit the existing fragments and enter the added fragment, then
+    // popping should popExit the removed fragment and popEnter the added fragments
+    @Test
+    public void replaceAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment1 = new AnimatorFragment();
+        final AnimatorFragment fragment2 = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .add(R.id.fragmentContainer, fragment2, "2")
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final AnimatorFragment fragment3 = new AnimatorFragment();
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .replace(R.id.fragmentContainer, fragment3)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertFragmentAnimation(fragment1, 1, false, EXIT);
+        assertFragmentAnimation(fragment2, 1, false, EXIT);
+        assertFragmentAnimation(fragment3, 1, true, ENTER);
+
+        fm.popBackStack();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
+        final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
+        final AnimatorFragment replacement2 = (AnimatorFragment) fm.findFragmentByTag("1");
+        int expectedAnimations = replacement1 == fragment1 ? 2 : 1;
+        assertFragmentAnimation(replacement1, expectedAnimations, true, POP_ENTER);
+        assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
+    }
+
+    // Ensure that adding and popping a Fragment uses the enter and popExit animators,
+    // but the animators are delayed when an entering Fragment is postponed.
+    @Test
+    public void postponedAddAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fragment.postponeEnterTransition();
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .add(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponed(fragment, 0);
+        fragment.startPostponedEnterTransition();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that removing and popping a Fragment uses the exit and popEnter animators,
+    // but the animators are delayed when an entering Fragment is postponed.
+    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Test
+    public void postponedRemoveAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment, "1")
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .remove(fragment)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPostponedPopEnter(fragment);
+    }
+
+    // Ensure that adding and popping a Fragment is postponed in both directions
+    // when the fragments have been marked for postponing.
+    @Test
+    public void postponedAddRemove() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment1 = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final AnimatorFragment fragment2 = new AnimatorFragment();
+        fragment2.postponeEnterTransition();
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponed(fragment2, 0);
+        assertNotNull(fragment1.getView());
+        assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+        assertEquals(1f, fragment1.getView().getAlpha(), 0f);
+        assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
+
+        fragment2.startPostponedEnterTransition();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPostponedPopEnter(fragment1);
+    }
+
+    // Popping a postponed transaction should result in no animators
+    @Test
+    public void popPostponed() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment1 = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertEquals(0, fragment1.numAnimators);
+
+        final AnimatorFragment fragment2 = new AnimatorFragment();
+        fragment2.postponeEnterTransition();
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponed(fragment2, 0);
+
+        // Now pop the postponed transaction
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertNotNull(fragment1.getView());
+        assertEquals(1f, fragment1.getView().getAlpha(), 0f);
+        assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
+        assertTrue(fragment1.isAdded());
+
+        assertNull(fragment2.getView());
+        assertFalse(fragment2.isAdded());
+
+        assertEquals(0, fragment1.numAnimators);
+        assertEquals(0, fragment2.numAnimators);
+        assertNull(fragment1.animator);
+        assertNull(fragment2.animator);
+    }
+
+    // Make sure that if the state was saved while a Fragment was animating that its
+    // state is proper after restoring.
+    @Test
+    public void saveWhileAnimatingAway() throws Throwable {
+        final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc1, null);
+
+        final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+        StrictViewFragment fragment1 = new StrictViewFragment();
+        fragment1.setLayoutId(R.layout.scene1);
+        fm1.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        StrictViewFragment fragment2 = new StrictViewFragment();
+
+        fm1.beginTransaction()
+                .setCustomAnimations(0, 0, 0, R.animator.slow_fade_out)
+                .replace(R.id.fragmentContainer, fragment2, "2")
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule, fm1);
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm1.popBackStack();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule, fm1);
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        // Now fragment2 should be animating away
+        assertFalse(fragment2.isAdded());
+        assertEquals(fragment2, fm1.findFragmentByTag("2")); // still exists because it is animating
+
+        Pair<Parcelable, FragmentManagerNonConfig> state =
+                FragmentTestUtil.destroy(mActivityRule, fc1);
+
+        final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc2, state);
+
+        final FragmentManager fm2 = fc2.getSupportFragmentManager();
+        Fragment fragment2restored = fm2.findFragmentByTag("2");
+        assertNull(fragment2restored);
+
+        Fragment fragment1restored = fm2.findFragmentByTag("1");
+        assertNotNull(fragment1restored);
+        assertNotNull(fragment1restored.getView());
+    }
+
+    private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
+        assertFragmentAnimation(fragment, 1, true, ENTER);
+
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.popBackStack();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertFragmentAnimation(fragment, 2, false, POP_EXIT);
+    }
+
+    private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
+        assertFragmentAnimation(fragment, 1, false, EXIT);
+
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.popBackStack();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
+
+        boolean isSameFragment = replacement == fragment;
+        int expectedAnimators = isSameFragment ? 2 : 1;
+        assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
+    }
+
+    private void assertExitPostponedPopEnter(AnimatorFragment fragment) throws Throwable {
+        assertFragmentAnimation(fragment, 1, false, EXIT);
+
+        fragment.postponeEnterTransition();
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponed(fragment, 1);
+
+        fragment.startPostponedEnterTransition();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertFragmentAnimation(fragment, 2, true, POP_ENTER);
+    }
+
+    private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
+            boolean isEnter, int animatorResourceId) throws InterruptedException {
+        assertEquals(numAnimators, fragment.numAnimators);
+        assertEquals(isEnter, fragment.enter);
+        assertEquals(animatorResourceId, fragment.resourceId);
+        assertNotNull(fragment.animator);
+        assertTrue(fragment.wasStarted);
+        assertTrue(fragment.endLatch.await(200, TimeUnit.MILLISECONDS));
+    }
+
+    private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
+            throws InterruptedException {
+        assertTrue(fragment.mOnCreateViewCalled);
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+        assertEquals(0f, fragment.getView().getAlpha(), 0f);
+        assertEquals(expectedAnimators, fragment.numAnimators);
+    }
+
+    public static class AnimatorFragment extends StrictViewFragment {
+        public int numAnimators;
+        public Animator animator;
+        public boolean enter;
+        public int resourceId;
+        public boolean wasStarted;
+        public CountDownLatch endLatch;
+
+        @Override
+        public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
+            if (nextAnim == 0) {
+                return null;
+            }
+            this.numAnimators++;
+            this.wasStarted = false;
+            this.animator = ValueAnimator.ofFloat(0, 1).setDuration(1);
+            this.endLatch = new CountDownLatch(1);
+            this.animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    wasStarted = true;
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    endLatch.countDown();
+                }
+            });
+            this.resourceId = nextAnim;
+            this.enter = enter;
+            return this.animator;
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
index f19adb9..09f071e 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
@@ -668,6 +668,239 @@
         assertFalse(fragment1.mCalledOnResume);
     }
 
+    @Test
+    @UiThreadTest
+    public void testIsStateSaved() throws Throwable {
+        FragmentController fc = startupFragmentController(null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        Fragment f = new StrictFragment();
+        fm.beginTransaction()
+                .add(f, "1")
+                .commitNow();
+
+        assertFalse("fragment reported state saved while resumed", f.isStateSaved());
+
+        fc.dispatchPause();
+        fc.saveAllState();
+
+        assertTrue("fragment reported state not saved after saveAllState", f.isStateSaved());
+
+        fc.dispatchStop();
+        fc.dispatchReallyStop();
+
+        assertTrue("fragment reported state not saved after stop", f.isStateSaved());
+
+        fc.dispatchDestroy();
+
+        assertFalse("fragment reported state saved after destroy", f.isStateSaved());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSetArgumentsLifecycle() throws Throwable {
+        FragmentController fc = startupFragmentController(null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        Fragment f = new StrictFragment();
+        f.setArguments(new Bundle());
+
+        fm.beginTransaction()
+                .add(f, "1")
+                .commitNow();
+
+        f.setArguments(new Bundle());
+
+        fc.dispatchPause();
+        fc.saveAllState();
+
+        boolean threw = false;
+        try {
+            f.setArguments(new Bundle());
+        } catch (IllegalStateException ise) {
+            threw = true;
+        }
+        assertTrue("fragment allowed setArguments after state save", threw);
+
+        fc.dispatchStop();
+        fc.dispatchReallyStop();
+
+        threw = false;
+        try {
+            f.setArguments(new Bundle());
+        } catch (IllegalStateException ise) {
+            threw = true;
+        }
+        assertTrue("fragment allowed setArguments after stop", threw);
+
+        fc.dispatchDestroy();
+
+        // Fully destroyed, so fragments have been removed.
+        f.setArguments(new Bundle());
+    }
+
+    /*
+     * Test that target fragments are in a useful state when we restore them, even if they're
+     * on the back stack.
+     */
+
+    @Test
+    @UiThreadTest
+    public void targetFragmentRestoreLifecycleStateBackStack() throws Throwable {
+        final FragmentController fc1 = FragmentController.createController(
+                new HostCallbacks(mActivityRule.getActivity()));
+
+        final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+        fc1.attachHost(null);
+        fc1.dispatchCreate();
+
+        final Fragment target = new TargetFragment();
+        fm1.beginTransaction().add(target, "target").commitNow();
+
+        final Fragment referrer = new ReferrerFragment();
+        referrer.setTargetFragment(target, 0);
+
+        fm1.beginTransaction()
+                .remove(target)
+                .add(referrer, "referrer")
+                .addToBackStack(null)
+                .commit();
+
+        fc1.dispatchActivityCreated();
+        fc1.noteStateNotSaved();
+        fc1.execPendingActions();
+        fc1.doLoaderStart();
+        fc1.dispatchStart();
+        fc1.reportLoaderStart();
+        fc1.dispatchResume();
+        fc1.execPendingActions();
+
+        // Bring the state back down to destroyed, simulating an activity restart
+        fc1.dispatchPause();
+        final Parcelable savedState = fc1.saveAllState();
+        final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
+        fc1.dispatchStop();
+        fc1.dispatchReallyStop();
+        fc1.dispatchDestroy();
+
+        final FragmentController fc2 = FragmentController.createController(
+                new HostCallbacks(mActivityRule.getActivity()));
+        final FragmentManager fm2 = fc2.getSupportFragmentManager();
+
+        fc2.attachHost(null);
+        fc2.restoreAllState(savedState, nonconf);
+        fc2.dispatchCreate();
+
+        fc2.dispatchActivityCreated();
+        fc2.noteStateNotSaved();
+        fc2.execPendingActions();
+        fc2.doLoaderStart();
+        fc2.dispatchStart();
+        fc2.reportLoaderStart();
+        fc2.dispatchResume();
+        fc2.execPendingActions();
+
+        // Bring the state back down to destroyed before we finish the test
+        fc2.dispatchPause();
+        fc2.saveAllState();
+        fc2.dispatchStop();
+        fc2.dispatchReallyStop();
+        fc2.dispatchDestroy();
+    }
+
+    @Test
+    @UiThreadTest
+    public void targetFragmentRestoreLifecycleStateManagerOrder() throws Throwable {
+        final FragmentController fc1 = FragmentController.createController(
+                new HostCallbacks(mActivityRule.getActivity()));
+
+        final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+        fc1.attachHost(null);
+        fc1.dispatchCreate();
+
+        final Fragment target1 = new TargetFragment();
+        final Fragment referrer1 = new ReferrerFragment();
+        referrer1.setTargetFragment(target1, 0);
+
+        fm1.beginTransaction().add(target1, "target1").add(referrer1, "referrer1").commitNow();
+
+        final Fragment target2 = new TargetFragment();
+        final Fragment referrer2 = new ReferrerFragment();
+        referrer2.setTargetFragment(target2, 0);
+
+        // Order shouldn't matter.
+        fm1.beginTransaction().add(referrer2, "referrer2").add(target2, "target2").commitNow();
+
+        fc1.dispatchActivityCreated();
+        fc1.noteStateNotSaved();
+        fc1.execPendingActions();
+        fc1.doLoaderStart();
+        fc1.dispatchStart();
+        fc1.reportLoaderStart();
+        fc1.dispatchResume();
+        fc1.execPendingActions();
+
+        // Bring the state back down to destroyed, simulating an activity restart
+        fc1.dispatchPause();
+        final Parcelable savedState = fc1.saveAllState();
+        final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
+        fc1.dispatchStop();
+        fc1.dispatchReallyStop();
+        fc1.dispatchDestroy();
+
+        final FragmentController fc2 = FragmentController.createController(
+                new HostCallbacks(mActivityRule.getActivity()));
+        final FragmentManager fm2 = fc2.getSupportFragmentManager();
+
+        fc2.attachHost(null);
+        fc2.restoreAllState(savedState, nonconf);
+        fc2.dispatchCreate();
+
+        fc2.dispatchActivityCreated();
+        fc2.noteStateNotSaved();
+        fc2.execPendingActions();
+        fc2.doLoaderStart();
+        fc2.dispatchStart();
+        fc2.reportLoaderStart();
+        fc2.dispatchResume();
+        fc2.execPendingActions();
+
+        // Bring the state back down to destroyed before we finish the test
+        fc2.dispatchPause();
+        fc2.saveAllState();
+        fc2.dispatchStop();
+        fc2.dispatchReallyStop();
+        fc2.dispatchDestroy();
+    }
+
+    @Test
+    public void targetFragmentNoCycles() throws Throwable {
+        final Fragment one = new Fragment();
+        final Fragment two = new Fragment();
+        final Fragment three = new Fragment();
+
+        try {
+            one.setTargetFragment(two, 0);
+            two.setTargetFragment(three, 0);
+            three.setTargetFragment(one, 0);
+            assertTrue("creating a fragment target cycle did not throw IllegalArgumentException",
+                    false);
+        } catch (IllegalArgumentException e) {
+            // Success!
+        }
+    }
+
+    @Test
+    public void targetFragmentSetClear() throws Throwable {
+        final Fragment one = new Fragment();
+        final Fragment two = new Fragment();
+
+        one.setTargetFragment(two, 0);
+        one.setTargetFragment(null, 0);
+    }
+
     /**
      * FragmentActivity should not raise the state of a Fragment while it is being destroyed.
      */
@@ -731,6 +964,52 @@
     }
 
     /**
+     * Check that retained fragments in the backstack correctly restored after two "configChanges"
+     */
+    @Test
+    @UiThreadTest
+    public void retainedFragmentInBackstack() throws Throwable {
+        FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        Fragment fragment1 = new StrictFragment();
+        fm.beginTransaction()
+                .add(fragment1, "1")
+                .addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+
+        Fragment child = new StrictFragment();
+        child.setRetainInstance(true);
+        fragment1.getChildFragmentManager().beginTransaction()
+                .add(child, "child").commit();
+        fragment1.getChildFragmentManager().executePendingTransactions();
+
+        Fragment fragment2 = new StrictFragment();
+        fm.beginTransaction()
+                .remove(fragment1)
+                .add(fragment2, "2")
+                .addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+
+        Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                FragmentTestUtil.destroy(mActivityRule, fc);
+
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+        savedState = FragmentTestUtil.destroy(mActivityRule, fc);
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+        fm = fc.getSupportFragmentManager();
+        fm.popBackStackImmediate();
+        Fragment retainedChild = fm.findFragmentByTag("1")
+                .getChildFragmentManager().findFragmentByTag("child");
+        assertEquals(child, retainedChild);
+    }
+
+    /**
      * When a fragment has been optimized out, it state should still be saved during
      * save and restore instance state.
      */
@@ -768,6 +1047,146 @@
         assertEquals(1, fragment1.getValue());
     }
 
+    /**
+     * When there are no retained instance fragments, the FragmentManagerNonConfig should be
+     * null
+     */
+    @Test
+    @UiThreadTest
+    public void nullNonConfig() throws Throwable {
+        FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        Fragment fragment1 = new StrictFragment();
+        fm.beginTransaction()
+                .add(fragment1, "1")
+                .addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+        Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                FragmentTestUtil.destroy(mActivityRule, fc);
+        assertNull(savedState.second);
+    }
+
+    /**
+     * When the FragmentManager state changes, the pending transactions should execute.
+     */
+    @Test
+    @UiThreadTest
+    public void runTransactionsOnChange() throws Throwable {
+        FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        RemoveHelloInOnResume fragment1 = new RemoveHelloInOnResume();
+        StrictFragment fragment2 = new StrictFragment();
+        fm.beginTransaction()
+                .add(fragment1, "1")
+                .setReorderingAllowed(false)
+                .commit();
+        fm.beginTransaction()
+                .add(fragment2, "Hello")
+                .setReorderingAllowed(false)
+                .commit();
+        fm.executePendingTransactions();
+
+        assertEquals(2, fm.getFragments().size());
+        assertTrue(fm.getFragments().contains(fragment1));
+        assertTrue(fm.getFragments().contains(fragment2));
+
+        Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                FragmentTestUtil.destroy(mActivityRule, fc);
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+        fm = fc.getSupportFragmentManager();
+
+        assertEquals(1, fm.getFragments().size());
+        for (Fragment fragment : fm.getFragments()) {
+            assertTrue(fragment instanceof RemoveHelloInOnResume);
+        }
+    }
+
+    /**
+     * When a retained instance fragment is saved while in the back stack, it should go
+     * through onCreate() when it is popped back.
+     */
+    @Test
+    @UiThreadTest
+    public void retainInstanceWithOnCreate() throws Throwable {
+        FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        OnCreateFragment fragment1 = new OnCreateFragment();
+
+        fm.beginTransaction()
+                .add(fragment1, "1")
+                .commit();
+        fm.beginTransaction()
+                .remove(fragment1)
+                .addToBackStack(null)
+                .commit();
+
+        Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                FragmentTestUtil.destroy(mActivityRule, fc);
+        Pair<Parcelable, FragmentManagerNonConfig> restartState =
+                Pair.create(savedState.first, null);
+
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, restartState);
+
+        // Save again, but keep the state
+        savedState = FragmentTestUtil.destroy(mActivityRule, fc);
+
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+
+        fm = fc.getSupportFragmentManager();
+
+        fm.popBackStackImmediate();
+        OnCreateFragment fragment2 = (OnCreateFragment) fm.findFragmentByTag("1");
+        assertTrue(fragment2.onCreateCalled);
+        fm.popBackStackImmediate();
+    }
+
+    /**
+     * A retained instance fragment should go through onCreate() once, even through save and
+     * restore.
+     */
+    @Test
+    @UiThreadTest
+    public void retainInstanceOneOnCreate() throws Throwable {
+        FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        OnCreateFragment fragment = new OnCreateFragment();
+
+        fm.beginTransaction()
+                .add(fragment, "fragment")
+                .commit();
+        fm.executePendingTransactions();
+
+        fm.beginTransaction()
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+
+        assertTrue(fragment.onCreateCalled);
+        fragment.onCreateCalled = false;
+
+        Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                FragmentTestUtil.destroy(mActivityRule, fc);
+
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+        fm = fc.getSupportFragmentManager();
+
+        fm.popBackStackImmediate();
+        assertFalse(fragment.onCreateCalled);
+    }
+
     private void assertAnimationsMatch(FragmentManager fm, int enter, int exit, int popEnter,
             int popExit) {
         FragmentManagerImpl fmImpl = (FragmentManagerImpl) fm;
@@ -1035,6 +1454,33 @@
         }
     }
 
+    public static class TargetFragment extends Fragment {
+        public boolean calledCreate;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            calledCreate = true;
+        }
+    }
+
+    public static class ReferrerFragment extends Fragment {
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            Fragment target = getTargetFragment();
+            assertNotNull("target fragment was null during referrer onCreate", target);
+
+            if (!(target instanceof TargetFragment)) {
+                throw new IllegalStateException("target fragment was not a TargetFragment");
+            }
+
+            assertTrue("target fragment has not yet been created",
+                    ((TargetFragment) target).calledCreate);
+        }
+    }
+
     public static class SaveStateFragment extends Fragment {
         private static final String VALUE_KEY = "SaveStateFragment.mValue";
         private int mValue;
@@ -1063,4 +1509,29 @@
             return mValue;
         }
     }
+
+    public static class RemoveHelloInOnResume extends Fragment {
+        @Override
+        public void onResume() {
+            super.onResume();
+            Fragment fragment = getFragmentManager().findFragmentByTag("Hello");
+            if (fragment != null) {
+                getFragmentManager().beginTransaction().remove(fragment).commit();
+            }
+        }
+    }
+
+    public static class OnCreateFragment extends Fragment {
+        public boolean onCreateCalled;
+
+        public OnCreateFragment() {
+            setRetainInstance(true);
+        }
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            onCreateCalled = true;
+        }
+    }
 }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentManagerNonConfigTest.java b/fragment/tests/java/android/support/v4/app/FragmentManagerNonConfigTest.java
new file mode 100644
index 0000000..eeae2b4
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentManagerNonConfigTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app;
+
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.NonConfigOnStopActivity;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentManagerNonConfigTest {
+
+    @Rule
+    public ActivityTestRule<NonConfigOnStopActivity> mActivityRule =
+            new ActivityTestRule<>(NonConfigOnStopActivity.class);
+
+    /**
+     * When a fragment is added during onStop(), it shouldn't show up in non-config
+     * state when restored.
+     */
+    @Test
+    public void nonConfigStop() throws Throwable {
+        FragmentActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+                mActivityRule.getActivity());
+
+        // A fragment was added in onStop(), but we shouldn't see it here...
+        assertTrue(activity.getSupportFragmentManager().getFragments().isEmpty());
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
deleted file mode 100644
index c2fb8bc..0000000
--- a/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.app;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Instrumentation;
-import android.support.fragment.test.R;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.app.test.FragmentTestActivity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class FragmentOptimizationTest {
-    @Rule
-    public ActivityTestRule<FragmentTestActivity> mActivityRule =
-            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
-
-    private ViewGroup mContainer;
-    private FragmentManager mFM;
-    private Instrumentation mInstrumentation;
-
-    @Before
-    public void setup() {
-        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
-        mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
-        mFM = mActivityRule.getActivity().getSupportFragmentManager();
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-    }
-
-    // Test that when you add and replace a fragment that only the replace's add
-    // actually creates a View.
-    @Test
-    public void addReplace() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        final StrictViewFragment fragment2 = new StrictViewFragment();
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .replace(R.id.fragmentContainer, fragment2)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-        assertEquals(0, fragment1.onCreateViewCount);
-        FragmentTestUtil.assertChildren(mContainer, fragment2);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.popBackStack();
-                mFM.popBackStack();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer);
-    }
-
-    // Test that it is possible to merge a transaction that starts with pop and adds
-    // the same view back again.
-    @Test
-    public void startWithPop() throws Throwable {
-        // Start with a single fragment on the back stack
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        // Now pop and add
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.popBackStack();
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        assertEquals(1, fragment1.onCreateViewCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer);
-        assertEquals(1, fragment1.onCreateViewCount);
-    }
-
-    // Popping the back stack in the middle of other operations doesn't fool it.
-    @Test
-    public void middlePop() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        final CountCallsFragment fragment2 = new CountCallsFragment();
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.popBackStack();
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment2)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer, fragment2);
-        assertEquals(0, fragment1.onAttachCount);
-        assertEquals(1, fragment2.onCreateViewCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer);
-        assertEquals(1, fragment2.onDetachCount);
-    }
-
-    // ensure that removing a view after adding it is optimized into no
-    // View being created. Hide still gets notified.
-    @Test
-    public void optimizeRemove() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        final int[] id = new int[1];
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                id[0] = mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .remove(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer);
-        assertEquals(0, fragment1.onCreateViewCount);
-        assertEquals(1, fragment1.onHideCount);
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(0, fragment1.onDetachCount);
-        assertEquals(1, fragment1.onAttachCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule, id[0],
-                FragmentManager.POP_BACK_STACK_INCLUSIVE);
-        FragmentTestUtil.assertChildren(mContainer);
-        assertEquals(0, fragment1.onCreateViewCount);
-        assertEquals(1, fragment1.onHideCount);
-        assertEquals(1, fragment1.onShowCount);
-        assertEquals(1, fragment1.onDetachCount);
-        assertEquals(1, fragment1.onAttachCount);
-    }
-
-    // Ensure that removing and adding the same view results in no operation
-    @Test
-    public void optimizeAdd() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        int id = mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        assertEquals(1, fragment1.onCreateViewCount);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .remove(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        // should be optimized out
-        assertEquals(1, fragment1.onCreateViewCount);
-
-        mFM.popBackStack(id, 0);
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        // optimize out going back, too
-        assertEquals(1, fragment1.onCreateViewCount);
-    }
-
-    // detaching, then attaching results in on change. Hide still functions
-    @Test
-    public void optimizeAttach() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        int id = mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        assertEquals(1, fragment1.onAttachCount);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .detach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .attach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        // can optimize out the detach/attach
-        assertEquals(0, fragment1.onDestroyViewCount);
-        assertEquals(1, fragment1.onHideCount);
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onCreateViewCount);
-        assertEquals(1, fragment1.onAttachCount);
-        assertEquals(0, fragment1.onDetachCount);
-
-        mFM.popBackStack(id, 0);
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        // optimized out again, but not the show
-        assertEquals(0, fragment1.onDestroyViewCount);
-        assertEquals(1, fragment1.onHideCount);
-        assertEquals(1, fragment1.onShowCount);
-        assertEquals(1, fragment1.onCreateViewCount);
-        assertEquals(1, fragment1.onAttachCount);
-        assertEquals(0, fragment1.onDetachCount);
-    }
-
-    // attaching, then detaching shouldn't result in a View being created
-    @Test
-    public void optimizeDetach() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        int id = mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .detach(fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-
-        // the add detach is not fully optimized out
-        assertEquals(1, fragment1.onAttachCount);
-        assertEquals(0, fragment1.onDetachCount);
-        assertTrue(fragment1.isDetached());
-        assertEquals(0, fragment1.onCreateViewCount);
-        FragmentTestUtil.assertChildren(mContainer);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .attach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .detach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        FragmentTestUtil.assertChildren(mContainer);
-        // can optimize out the attach/detach, and the hide call
-        assertEquals(1, fragment1.onAttachCount);
-        assertEquals(0, fragment1.onDetachCount);
-        assertEquals(1, fragment1.onHideCount);
-        assertTrue(fragment1.isHidden());
-        assertEquals(0, fragment1.onShowCount);
-
-        mFM.popBackStack(id, 0);
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer);
-
-        // we can optimize out the attach/detach on the way back
-        assertEquals(1, fragment1.onAttachCount);
-        assertEquals(0, fragment1.onDetachCount);
-        assertEquals(1, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-        assertFalse(fragment1.isHidden());
-    }
-
-    // show, then hide should optimize out
-    @Test
-    public void optimizeHide() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        int id = mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .hide(fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .show(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .remove(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        // optimize out hide/show
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        // still optimized out
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .show(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        // The show/hide can be optimized out and nothing should change.
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .show(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .detach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .attach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        // the detach/attach should not affect the show/hide, so show/hide should cancel each other
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(1, fragment1.onHideCount);
-    }
-
-    // hiding and showing the same view should optimize out
-    @Test
-    public void optimizeShow() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        int id = mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(0, fragment1.onHideCount);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .hide(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .detach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .attach(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .show(fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-        // can optimize out the show/hide
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(0, fragment1.onHideCount);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule, id,
-                FragmentManager.POP_BACK_STACK_INCLUSIVE);
-        assertEquals(0, fragment1.onShowCount);
-        assertEquals(0, fragment1.onHideCount);
-    }
-
-    // The View order shouldn't be messed up by optimization -- a view that
-    // is optimized to not remove/add should be in its correct position after
-    // the transaction completes.
-    @Test
-    public void viewOrder() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        int id = mFM.beginTransaction()
-                .add(R.id.fragmentContainer, fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        FragmentTestUtil.executePendingTransactions(mActivityRule);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-
-        final CountCallsFragment fragment2 = new CountCallsFragment();
-
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .replace(R.id.fragmentContainer, fragment2)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer, fragment2, fragment1);
-
-        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
-        FragmentTestUtil.assertChildren(mContainer, fragment1);
-    }
-
-    // Popping an added transaction results in no operation
-    @Test
-    public void addPopBackStack() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.popBackStack();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer);
-
-        // Was never instantiated because it was popped before anything could happen
-        assertEquals(0, fragment1.onCreateViewCount);
-    }
-
-    // A non-back-stack transaction doesn't interfere with back stack add/pop
-    // optimization.
-    @Test
-    public void popNonBackStack() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        final CountCallsFragment fragment2 = new CountCallsFragment();
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .replace(R.id.fragmentContainer, fragment2)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.popBackStack();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer, fragment2);
-
-        // It should be optimized with the replace, so no View creation
-        assertEquals(0, fragment1.onCreateViewCount);
-    }
-
-    // When optimization is disabled, the transaction prior to the disabled optimization
-    // transaction should all be run prior to running the non-optimized transaction.
-    @Test
-    public void noOptimization() throws Throwable {
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        final CountCallsFragment fragment2 = new CountCallsFragment();
-        mInstrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mFM.beginTransaction()
-                        .add(R.id.fragmentContainer, fragment1)
-                        .addToBackStack(null)
-                        .setAllowOptimization(true)
-                        .commit();
-                mFM.beginTransaction()
-                        .replace(R.id.fragmentContainer, fragment2)
-                        .addToBackStack(null)
-                        .setAllowOptimization(false)
-                        .commit();
-                mFM.executePendingTransactions();
-            }
-        });
-        FragmentTestUtil.assertChildren(mContainer, fragment2);
-
-        // No optimization, so fragment1 should have created its View
-        assertEquals(1, fragment1.onCreateViewCount);
-    }
-
-    // Test that a fragment view that is created with focus has focus after the transaction
-    // completes.
-    @UiThreadTest
-    @Test
-    public void focusedView() throws Throwable {
-        mActivityRule.getActivity().setContentView(R.layout.double_container);
-        mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
-        EditText firstEditText = new EditText(mContainer.getContext());
-        mContainer.addView(firstEditText);
-        firstEditText.requestFocus();
-
-        assertTrue(firstEditText.isFocused());
-        final CountCallsFragment fragment1 = new CountCallsFragment();
-        final CountCallsFragment fragment2 = new CountCallsFragment();
-        fragment2.setLayoutId(R.layout.with_edit_text);
-        mFM.beginTransaction()
-                .add(R.id.fragmentContainer2, fragment1)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        mFM.beginTransaction()
-                .replace(R.id.fragmentContainer2, fragment2)
-                .addToBackStack(null)
-                .setAllowOptimization(true)
-                .commit();
-        mFM.executePendingTransactions();
-        final View editText = fragment2.getView().findViewById(R.id.editText);
-        assertTrue(editText.isFocused());
-        assertFalse(firstEditText.isFocused());
-    }
-}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentReorderingTest.java b/fragment/tests/java/android/support/v4/app/FragmentReorderingTest.java
new file mode 100644
index 0000000..0f4090e
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentReorderingTest.java
@@ -0,0 +1,682 @@
+/*
+ * 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.
+ */
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentReorderingTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    private ViewGroup mContainer;
+    private FragmentManager mFM;
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setup() {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        mFM = mActivityRule.getActivity().getSupportFragmentManager();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    // Test that when you add and replace a fragment that only the replace's add
+    // actually creates a View.
+    @Test
+    public void addReplace() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        assertEquals(0, fragment1.onCreateViewCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.popBackStack();
+                mFM.popBackStack();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer);
+    }
+
+    // Test that it is possible to merge a transaction that starts with pop and adds
+    // the same view back again.
+    @Test
+    public void startWithPop() throws Throwable {
+        // Start with a single fragment on the back stack
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        // Now pop and add
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.popBackStack();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        assertEquals(1, fragment1.onCreateViewCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(1, fragment1.onCreateViewCount);
+    }
+
+    // Popping the back stack in the middle of other operations doesn't fool it.
+    @Test
+    public void middlePop() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.popBackStack();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+        assertEquals(0, fragment1.onAttachCount);
+        assertEquals(1, fragment2.onCreateViewCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(1, fragment2.onDetachCount);
+    }
+
+    // ensure that removing a view after adding it is optimized into no
+    // View being created. Hide still gets notified.
+    @Test
+    public void removeRedundantRemove() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final int[] id = new int[1];
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                id[0] = mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .remove(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(0, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(1, fragment1.onAttachCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id[0],
+                FragmentManager.POP_BACK_STACK_INCLUSIVE);
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(0, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(1, fragment1.onShowCount);
+        assertEquals(1, fragment1.onDetachCount);
+        assertEquals(1, fragment1.onAttachCount);
+    }
+
+    // Ensure that removing and adding the same view results in no operation
+    @Test
+    public void removeRedundantAdd() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(1, fragment1.onCreateViewCount);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .remove(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // should be optimized out
+        assertEquals(1, fragment1.onCreateViewCount);
+
+        mFM.popBackStack(id, 0);
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // optimize out going back, too
+        assertEquals(1, fragment1.onCreateViewCount);
+    }
+
+    // detaching, then attaching results in on change. Hide still functions
+    @Test
+    public void removeRedundantAttach() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(1, fragment1.onAttachCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // can optimize out the detach/attach
+        assertEquals(0, fragment1.onDestroyViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+
+        mFM.popBackStack(id, 0);
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        // optimized out again, but not the show
+        assertEquals(0, fragment1.onDestroyViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(1, fragment1.onShowCount);
+        assertEquals(1, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+    }
+
+    // attaching, then detaching shouldn't result in a View being created
+    @Test
+    public void removeRedundantDetach() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .detach(fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        // the add detach is not fully optimized out
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertTrue(fragment1.isDetached());
+        assertEquals(0, fragment1.onCreateViewCount);
+        FragmentTestUtil.assertChildren(mContainer);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer);
+        // can optimize out the attach/detach, and the hide call
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertTrue(fragment1.isHidden());
+        assertEquals(0, fragment1.onShowCount);
+
+        mFM.popBackStack(id, 0);
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer);
+
+        // we can optimize out the attach/detach on the way back
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(1, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertFalse(fragment1.isHidden());
+    }
+
+    // show, then hide should optimize out
+    @Test
+    public void removeRedundantHide() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .hide(fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .remove(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // optimize out hide/show
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        // still optimized out
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        // The show/hide can be optimized out and nothing should change.
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        // the detach/attach should not affect the show/hide, so show/hide should cancel each other
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+    }
+
+    // hiding and showing the same view should optimize out
+    @Test
+    public void removeRedundantShow() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onHideCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // can optimize out the show/hide
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id,
+                FragmentManager.POP_BACK_STACK_INCLUSIVE);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onHideCount);
+    }
+
+    // The View order shouldn't be messed up by reordering -- a view that
+    // is optimized to not remove/add should be in its correct position after
+    // the transaction completes.
+    @Test
+    public void viewOrder() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2, fragment1);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+    }
+
+    // Popping an added transaction results in no operation
+    @Test
+    public void addPopBackStack() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.popBackStack();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer);
+
+        // Was never instantiated because it was popped before anything could happen
+        assertEquals(0, fragment1.onCreateViewCount);
+    }
+
+    // A non-back-stack transaction doesn't interfere with back stack add/pop
+    // optimization.
+    @Test
+    public void popNonBackStack() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.popBackStack();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+        // It should be optimized with the replace, so no View creation
+        assertEquals(0, fragment1.onCreateViewCount);
+    }
+
+    // When reordering is disabled, the transaction prior to the disabled reordering
+    // transaction should all be run prior to running the ordered transaction.
+    @Test
+    public void noReordering() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .setReorderingAllowed(false)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+        // No reordering, so fragment1 should have created its View
+        assertEquals(1, fragment1.onCreateViewCount);
+    }
+
+    // Test that a fragment view that is created with focus has focus after the transaction
+    // completes.
+    @UiThreadTest
+    @Test
+    public void focusedView() throws Throwable {
+        mActivityRule.getActivity().setContentView(R.layout.double_container);
+        mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+        EditText firstEditText = new EditText(mContainer.getContext());
+        mContainer.addView(firstEditText);
+        firstEditText.requestFocus();
+
+        assertTrue(firstEditText.isFocused());
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        fragment2.setLayoutId(R.layout.with_edit_text);
+        mFM.beginTransaction()
+                .add(R.id.fragmentContainer2, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        mFM.beginTransaction()
+                .replace(R.id.fragmentContainer2, fragment2)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+        mFM.executePendingTransactions();
+        final View editText = fragment2.getView().findViewById(R.id.editText);
+        assertTrue(editText.isFocused());
+        assertFalse(firstEditText.isFocused());
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java b/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
index 6aadeb7..fe7045c 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
@@ -26,7 +26,6 @@
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SdkSuppress;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
@@ -98,7 +97,6 @@
         });
     }
 
-    @SdkSuppress(minSdkVersion = 11)
     @Test
     public void testBackPressWithFrameworkFragment() throws Throwable {
         final Activity activity = mActivityRule.getActivity();
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTest.java b/fragment/tests/java/android/support/v4/app/FragmentTest.java
index 35f8e35..90deab9 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTest.java
@@ -23,6 +23,7 @@
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
@@ -72,7 +73,7 @@
         assertEquals(1, fragment2.createOrder);
     }
 
-    @SmallTest
+    @LargeTest
     @Test
     public void testChildFragmentManagerGone() throws Throwable {
         final FragmentA fragmentA = new FragmentA();
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
index 9ee0c48..1da1af6 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
@@ -16,9 +16,11 @@
 package android.support.v4.app;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcelable;
@@ -26,12 +28,15 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.v4.app.test.FragmentTestActivity;
+import android.support.v4.app.test.RecreatedActivity;
 import android.util.Pair;
 import android.view.ViewGroup;
 import android.view.animation.Animation;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 public class FragmentTestUtil {
     private static final Runnable DO_NOTHING = new Runnable() {
@@ -224,16 +229,50 @@
      * Allocates until a garbage collection occurs.
      */
     public static void forceGC() {
-        // Do it twice so that we know we're not in the middle of the first collection when
-        // returning.
-        for (int i = 0; i < 2; i++) {
-            // Use a random index in the list to detect the garbage collection each time because
-            // .get() may accidentally trigger a strong reference during collection.
-            ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
-            do {
-                WeakReference<byte[]> arr = new WeakReference<byte[]>(new byte[100]);
-                leak.add(arr);
-            } while (leak.get((int) (Math.random() * leak.size())).get() != null);
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
+            // The following works on O+
+            Runtime.getRuntime().gc();
+            Runtime.getRuntime().gc();
+            Runtime.getRuntime().runFinalization();
+        } else {
+            // The following works on older versions
+            for (int i = 0; i < 2; i++) {
+                // Use a random index in the list to detect the garbage collection each time because
+                // .get() may accidentally trigger a strong reference during collection.
+                ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
+                do {
+                    WeakReference<byte[]> arr = new WeakReference<byte[]>(new byte[100]);
+                    leak.add(arr);
+                } while (leak.get((int) (Math.random() * leak.size())).get() != null);
+            }
         }
     }
+
+    /**
+     * Restarts the RecreatedActivity and waits for the new activity to be resumed.
+     *
+     * @return The newly-restarted Activity
+     */
+    public static <T extends RecreatedActivity> T recreateActivity(
+            ActivityTestRule<? extends RecreatedActivity> rule, final T activity)
+            throws InterruptedException {
+        // Now switch the orientation
+        RecreatedActivity.sResumed = new CountDownLatch(1);
+        RecreatedActivity.sDestroyed = new CountDownLatch(1);
+
+        runOnUiThreadRethrow(rule, new Runnable() {
+            @Override
+            public void run() {
+                activity.recreate();
+            }
+        });
+        assertTrue(RecreatedActivity.sResumed.await(1, TimeUnit.SECONDS));
+        assertTrue(RecreatedActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+        T newActivity = (T) RecreatedActivity.sActivity;
+
+        waitForExecution(rule);
+
+        RecreatedActivity.clearState();
+        return newActivity;
+    }
 }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java
index 60f93fd..4d82831 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java
@@ -15,10 +15,10 @@
  */
 package android.support.v4.app;
 
+import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
@@ -31,6 +31,7 @@
 import android.support.annotation.Nullable;
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
@@ -251,34 +252,57 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
+    @Test
+    public void testPostOnCommit() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final boolean[] ran = new boolean[1];
+                FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+                fm.beginTransaction().runOnCommit(new Runnable() {
+                    @Override
+                    public void run() {
+                        ran[0] = true;
+                    }
+                }).commit();
+                fm.executePendingTransactions();
+
+                assertTrue("runOnCommit runnable never ran", ran[0]);
+
+                ran[0] = false;
+
+                boolean threw = false;
+                try {
+                    fm.beginTransaction().runOnCommit(new Runnable() {
+                        @Override
+                        public void run() {
+                            ran[0] = true;
+                        }
+                    }).addToBackStack(null).commit();
+                } catch (IllegalStateException ise) {
+                    threw = true;
+                }
+
+                fm.executePendingTransactions();
+
+                assertTrue("runOnCommit was allowed to be called for back stack transaction",
+                        threw);
+                assertFalse("runOnCommit runnable for back stack transaction was run", ran[0]);
+            }
+        });
+    }
+
     /**
-     * onNewIntent() should note that the state is not saved so that child fragment
-     * managers can execute transactions.
+     * Test to ensure that when onBackPressed() is received that there is no crash.
      */
     @Test
-    public void newIntentUnlocks() throws Throwable {
+    @UiThreadTest
+    public void crashOnBackPressed() throws Throwable {
         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        Intent intent1 = new Intent(mActivity, NewIntentActivity.class)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        NewIntentActivity newIntentActivity =
-                (NewIntentActivity) instrumentation.startActivitySync(intent1);
-        FragmentTestUtil.waitForExecution(mActivityRule);
-
-        Intent intent2 = new Intent(mActivity, FragmentTestActivity.class);
-        intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        Activity coveringActivity = instrumentation.startActivitySync(intent2);
-        FragmentTestUtil.waitForExecution(mActivityRule);
-
-        Intent intent3 = new Intent(mActivity, NewIntentActivity.class)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mActivity.startActivity(intent3);
-        assertTrue(newIntentActivity.newIntent.await(1, TimeUnit.SECONDS));
-        FragmentTestUtil.waitForExecution(mActivityRule);
-
-        for (Fragment fragment : newIntentActivity.getSupportFragmentManager().getFragments()) {
-            // There really should only be one fragment in newIntentActivity.
-            assertEquals(1, fragment.getChildFragmentManager().getFragments().size());
-        }
+        Bundle outState = new Bundle();
+        FragmentTestActivity activity = mActivityRule.getActivity();
+        instrumentation.callActivityOnSaveInstanceState(activity, outState);
+        activity.onBackPressed();
     }
 
     // Ensure that getFragments() works during transactions, even if it is run off thread
@@ -393,6 +417,36 @@
         });
     }
 
+    /**
+     * onNewIntent() should note that the state is not saved so that child fragment
+     * managers can execute transactions.
+     */
+    @Test
+    public void newIntentUnlocks() throws Throwable {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Intent intent1 = new Intent(mActivity, NewIntentActivity.class)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        NewIntentActivity newIntentActivity =
+                (NewIntentActivity) instrumentation.startActivitySync(intent1);
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        Intent intent2 = new Intent(mActivity, FragmentTestActivity.class);
+        intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Activity coveringActivity = instrumentation.startActivitySync(intent2);
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        Intent intent3 = new Intent(mActivity, NewIntentActivity.class)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mActivity.startActivity(intent3);
+        assertTrue(newIntentActivity.newIntent.await(1, TimeUnit.SECONDS));
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        for (Fragment fragment : newIntentActivity.getSupportFragmentManager().getFragments()) {
+            // There really should only be one fragment in newIntentActivity.
+            assertEquals(1, fragment.getChildFragmentManager().getFragments().size());
+        }
+    }
+
     private void getFragmentsUntilSize(int expectedSize) {
         final long endTime = SystemClock.uptimeMillis() + 3000;
 
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
index c5f1fe9..4f9016d 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
@@ -53,7 +53,7 @@
 @RunWith(Parameterized.class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 public class FragmentTransitionTest {
-    private final boolean mOptimize;
+    private final boolean mReorderingAllowed;
 
     @Parameterized.Parameters
     public static Object[] data() {
@@ -71,8 +71,8 @@
     private int mOnBackStackChangedTimes;
     private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener;
 
-    public FragmentTransitionTest(final boolean optimize) {
-        mOptimize = optimize;
+    public FragmentTransitionTest(final boolean reordering) {
+        mReorderingAllowed = reordering;
     }
 
     @Before
@@ -107,7 +107,7 @@
 
         // exit transition
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .remove(fragment)
                 .addToBackStack(null)
                 .commit();
@@ -184,7 +184,7 @@
             @Override
             public void run() {
                 mFragmentManager.beginTransaction()
-                        .setAllowOptimization(mOptimize)
+                        .setReorderingAllowed(mReorderingAllowed)
                         .replace(R.id.fragmentContainer, fragment2)
                         .replace(R.id.fragmentContainer, fragment1)
                         .replace(R.id.fragmentContainer, fragment2)
@@ -226,7 +226,7 @@
         TransitionFragment fragment2 = new TransitionFragment();
         fragment2.setLayoutId(R.layout.scene1);
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
                 .addToBackStack(null)
@@ -343,7 +343,7 @@
         mFragmentManager.beginTransaction()
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .addToBackStack(null)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -410,7 +410,7 @@
         mFragmentManager.beginTransaction()
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .addToBackStack(null)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -465,7 +465,7 @@
                 .addSharedElement(startGreen, "greenSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         assertEquals(2, mOnBackStackChangedTimes);
@@ -538,7 +538,7 @@
         final View startGreen = findGreen();
 
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .add(R.id.fragmentContainer, fragment2)
                 .hide(fragment1)
                 .addToBackStack(null)
@@ -589,7 +589,7 @@
         final View startGreen = findGreen();
 
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .add(R.id.fragmentContainer, fragment2)
                 .detach(fragment1)
                 .addToBackStack(null)
@@ -636,7 +636,7 @@
         mFragmentManager.beginTransaction()
                 .addSharedElement(startBlue, "fooSquare")
                 .replace(R.id.fragmentContainer, fragment2)
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .addToBackStack(null)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -647,7 +647,7 @@
         final View endBlue = findBlue();
         final View endGreen = findGreen();
 
-        if (mOptimize) {
+        if (mReorderingAllowed) {
             verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
         } else {
             verifyAndClearTransition(fragment1.exitTransition, startBlueBounds, startGreen);
@@ -687,14 +687,14 @@
     // Test that invisible fragment views don't participate in transitions
     @Test
     public void invisibleNoTransitions() throws Throwable {
-        if (!mOptimize) {
-            return; // only optimized transitions can avoid interaction
+        if (!mReorderingAllowed) {
+            return; // only reordered transitions can avoid interaction
         }
         // enter transition
         TransitionFragment fragment = new InvisibleFragment();
         fragment.setLayoutId(R.layout.scene1);
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .add(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
                 .commit();
@@ -704,7 +704,7 @@
 
         // exit transition
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .remove(fragment)
                 .addToBackStack(null)
                 .commit();
@@ -736,7 +736,7 @@
         fragment2.setLayoutId(R.layout.scene2);
 
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
@@ -762,7 +762,7 @@
                 FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
                 fm.popBackStack();
                 fm.beginTransaction()
-                        .setAllowOptimization(mOptimize)
+                        .setReorderingAllowed(mReorderingAllowed)
                         .replace(R.id.fragmentContainer, fragment3)
                         .addToBackStack(null)
                         .commit();
@@ -773,8 +773,8 @@
         FragmentTestUtil.executePendingTransactions(mActivityRule);
 
         fragment2.waitForTransition();
-        // It does not transition properly for unoptimized transactions, though.
-        if (mOptimize) {
+        // It does not transition properly for ordered transactions, though.
+        if (mReorderingAllowed) {
             verifyAndClearTransition(fragment2.returnTransition, null, midGreen, midBlue);
             final View endGreen = findGreen();
             final View endBlue = findBlue();
@@ -785,7 +785,7 @@
         } else {
             // fragment3 doesn't get a transition since it conflicts with the pop transition
             verifyNoOtherTransitions(fragment3);
-            // Everything else is just doing its best. Unoptimized transactions can't handle
+            // Everything else is just doing its best. Ordered transactions can't handle
             // multiple transitions acting together except for popping multiple together.
         }
     }
@@ -794,7 +794,7 @@
         TransitionFragment fragment1 = new TransitionFragment();
         fragment1.setLayoutId(R.layout.scene1);
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
                 .commit();
@@ -888,7 +888,7 @@
         final Rect startBlueRect = getBoundsOnScreen(startBlue);
 
         mFragmentManager.beginTransaction()
-                .setAllowOptimization(mOptimize)
+                .setReorderingAllowed(mReorderingAllowed)
                 .addSharedElement(startBlue, sharedElementName)
                 .replace(R.id.fragmentContainer, to)
                 .addToBackStack(null)
@@ -922,7 +922,7 @@
     private void verifyCrossTransition(boolean swapSource,
             TransitionFragment from1, TransitionFragment from2) throws Throwable {
         final int startNumOnBackStackChanged = mOnBackStackChangedTimes;
-        final int changesPerOperation = mOptimize ? 1 : 2;
+        final int changesPerOperation = mReorderingAllowed ? 1 : 2;
 
         final TransitionFragment to1 = new TransitionFragment();
         to1.setLayoutId(R.layout.scene2);
@@ -945,13 +945,13 @@
             @Override
             public void run() {
                 mFragmentManager.beginTransaction()
-                        .setAllowOptimization(mOptimize)
+                        .setReorderingAllowed(mReorderingAllowed)
                         .addSharedElement(fromShared1, "blueSquare")
                         .replace(R.id.fragmentContainer1, to1)
                         .addToBackStack(null)
                         .commit();
                 mFragmentManager.beginTransaction()
-                        .setAllowOptimization(mOptimize)
+                        .setReorderingAllowed(mReorderingAllowed)
                         .addSharedElement(fromShared2, sharedElementName)
                         .replace(R.id.fragmentContainer2, to2)
                         .addToBackStack(null)
diff --git a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
index ace24e9..78750ae 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
@@ -312,19 +312,14 @@
         FragmentTestUtil.assertChildren(container);
     }
 
-    // Removing a fragment that isn't in should throw
+    // Removing a fragment that isn't in should not throw
     @Test
     public void removeNothThere() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
         final StrictViewFragment fragment = new StrictViewFragment();
         fm.beginTransaction().remove(fragment).commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Removing a fragment that isn't in should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
     // Hide a fragment and its View should be GONE. Then pop it and the View should be VISIBLE
@@ -356,7 +351,7 @@
         assertEquals(View.VISIBLE, fragment.getView().getVisibility());
     }
 
-    // Hiding a hidden fragment should throw
+    // Hiding a hidden fragment should not throw
     @Test
     public void doubleHide() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -367,15 +362,10 @@
                 .hide(fragment)
                 .hide(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Hiding a hidden fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
-    // Hiding a non-existing fragment should throw
+    // Hiding a non-existing fragment should not throw
     @Test
     public void hideUnAdded() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -384,12 +374,7 @@
         fm.beginTransaction()
                 .hide(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Hiding a non-existing fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
     // Show a hidden fragment and its View should be VISIBLE. Then pop it and the View should be
@@ -423,7 +408,7 @@
         assertEquals(View.GONE, fragment.getView().getVisibility());
     }
 
-    // Showing a shown fragment should throw
+    // Showing a shown fragment should not throw
     @Test
     public void showShown() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -433,15 +418,10 @@
                 .add(R.id.fragmentContainer, fragment)
                 .show(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Showing a visible fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
-    // Showing a non-existing fragment should throw
+    // Showing a non-existing fragment should not throw
     @Test
     public void showUnAdded() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -450,12 +430,7 @@
         fm.beginTransaction()
                 .show(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Showing a non-existing fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
     // Detaching a fragment should remove the View from the hierarchy. Then popping it should
@@ -521,7 +496,7 @@
         assertEquals(View.GONE, fragment.getView().getVisibility());
     }
 
-    // Detaching a detached fragment should throw
+    // Detaching a detached fragment should not throw
     @Test
     public void detachDetatched() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -532,15 +507,10 @@
                 .detach(fragment)
                 .detach(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Detaching a detached fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
-    // Detaching a non-existing fragment should throw
+    // Detaching a non-existing fragment should not throw
     @Test
     public void detachUnAdded() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -549,12 +519,7 @@
         fm.beginTransaction()
                 .detach(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Detaching a non-existing fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
     // Attaching a fragment should add the View back into the hierarchy. Then popping it should
@@ -622,7 +587,7 @@
         assertTrue(fragment.isHidden());
     }
 
-    // Attaching an attached fragment should throw
+    // Attaching an attached fragment should not throw
     @Test
     public void attachAttached() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -632,15 +597,10 @@
                 .add(R.id.fragmentContainer, fragment)
                 .attach(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Attaching an attached fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
-    // Attaching a non-existing fragment should throw
+    // Attaching a non-existing fragment should not throw
     @Test
     public void attachUnAdded() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -649,12 +609,7 @@
         fm.beginTransaction()
                 .attach(fragment)
                 .commit();
-        try {
-            FragmentTestUtil.executePendingTransactions(mActivityRule);
-            fail("Attaching a non-existing fragment should throw an exception");
-        } catch (Throwable t) {
-            // expected
-        }
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
     }
 
     // Simple replace of one fragment in a container. Popping should replace it back again
@@ -941,11 +896,11 @@
         FragmentTestUtil.assertChildren(container, fragment3);
     }
 
-    // Ensure that non-optimized transactions are executed individually rather than together.
+    // Ensure that ordered transactions are executed individually rather than together.
     // This forces references from one fragment to another that should be executed earlier
     // to work.
     @Test
-    public void nonOptimizeTogether() throws Throwable {
+    public void orderedOperationsTogether() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
         ViewGroup container = (ViewGroup)
                 mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
@@ -961,12 +916,12 @@
             public void run() {
                 fm.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
-                        .setAllowOptimization(false)
+                        .setReorderingAllowed(false)
                         .addToBackStack(null)
                         .commit();
                 fm.beginTransaction()
                         .add(R.id.squareContainer, fragment2)
-                        .setAllowOptimization(false)
+                        .setReorderingAllowed(false)
                         .addToBackStack(null)
                         .commit();
                 fm.executePendingTransactions();
@@ -1003,7 +958,7 @@
         FragmentTestUtil.assertChildren(innerContainer, fragment2);
     }
 
-    // Popping the backstack with non-optimized fragments should execute the operations together.
+    // Popping the backstack with ordered fragments should execute the operations together.
     // When a non-backstack fragment will be raised, it should not be destroyed.
     @Test
     public void popToNonBackStackFragment() throws Throwable {
diff --git a/fragment/tests/java/android/support/v4/app/LoaderTest.java b/fragment/tests/java/android/support/v4/app/LoaderTest.java
index f1eb328..b581fe7 100644
--- a/fragment/tests/java/android/support/v4/app/LoaderTest.java
+++ b/fragment/tests/java/android/support/v4/app/LoaderTest.java
@@ -22,11 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.app.Instrumentation;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
@@ -38,7 +34,6 @@
 import android.support.v4.content.AsyncTaskLoader;
 import android.support.v4.content.Loader;
 
-import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -56,25 +51,18 @@
     public ActivityTestRule<LoaderActivity> mActivityRule =
             new ActivityTestRule(LoaderActivity.class);
 
-    @After
-    public void clearActivity() {
-        LoaderActivity.clearState();
-    }
-
     /**
      * Test to ensure that there is no Activity leak due to Loader
      */
     @Test
     public void testLeak() throws Throwable {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        Intent intent = new Intent(mActivityRule.getActivity(), LoaderActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        LoaderActivity.sResumed = new CountDownLatch(1);
-        instrumentation.startActivitySync(intent);
-        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
+        // Restart the activity because mActivityRule keeps a strong reference to the
+        // old activity.
+        LoaderActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+                mActivityRule.getActivity());
 
         LoaderFragment fragment = new LoaderFragment();
-        FragmentManager fm = LoaderActivity.sActivity.getSupportFragmentManager();
+        FragmentManager fm = activity.getSupportFragmentManager();
 
         fm.beginTransaction()
                 .add(fragment, "1")
@@ -88,14 +76,18 @@
                 .commit();
 
         FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+        fm = null; // clear it so that it can be released
 
         WeakReference<LoaderActivity> weakActivity = new WeakReference(LoaderActivity.sActivity);
 
-        if (!switchOrientation()) {
-            return; // can't switch orientation for square screens
-        }
+        activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
 
-        // Now force a garbage collection.
+        // Wait for everything to settle. We have to make sure that the old Activity
+        // is ready to be collected.
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        // Force a garbage collection.
         FragmentTestUtil.forceGC();
         assertNull(weakActivity.get());
     }
@@ -109,19 +101,18 @@
 
         assertEquals("Loaded!", activity.textView.getText().toString());
 
-        if (!switchOrientation()) {
-            return; // can't switch orientation for square screens
-        }
+        activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
 
         // After orientation change, the text should still be loaded properly
-        activity = LoaderActivity.sActivity;
         assertEquals("Loaded!", activity.textView.getText().toString());
     }
 
     /**
      * When a change is interrupted with stop, the data in the LoaderManager remains stale.
      */
-    @Test
+    //@Test
     public void noStaleData() throws Throwable {
         final LoaderActivity activity = mActivityRule.getActivity();
         final String[] value = new String[] { "First Value" };
@@ -201,28 +192,6 @@
         assertEquals("Second Value", activity.textViewB.getText().toString());
     }
 
-    private boolean switchOrientation() throws InterruptedException {
-        LoaderActivity activity = LoaderActivity.sActivity;
-
-        int currentOrientation = activity.getResources().getConfiguration().orientation;
-
-        int nextOrientation;
-        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
-            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-        } else {
-            return false; // Don't know what to do with square or unknown orientations
-        }
-
-        // Now switch the orientation
-        LoaderActivity.sResumed = new CountDownLatch(1);
-
-        activity.setRequestedOrientation(nextOrientation);
-        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
-        return true;
-    }
-
 
     public static class LoaderFragment extends Fragment {
         private static final int LOADER_ID = 1;
diff --git a/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java b/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java
index 39e19be..4d63c27 100644
--- a/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java
+++ b/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java
@@ -60,6 +60,7 @@
         fragmentManager.beginTransaction().add(mParentFragment, "parent").commit();
         final CountDownLatch latch = new CountDownLatch(1);
         mActivityRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 fragmentManager.executePendingTransactions();
                 latch.countDown();
@@ -89,6 +90,7 @@
 
         final CountDownLatch latch = new CountDownLatch(1);
         mActivityRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mParentFragment.getChildFragment().startActivityForResult(
                         new Intent(Intent.ACTION_CALL),
diff --git a/fragment/tests/java/android/support/v4/app/NestedInflatedFragmentTest.java b/fragment/tests/java/android/support/v4/app/NestedInflatedFragmentTest.java
new file mode 100644
index 0000000..b4316da
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/NestedInflatedFragmentTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+
+package android.support.v4.app;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.fragment.test.R;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class NestedInflatedFragmentTest {
+    private static final String TAG = "NestedInflatedFragmentTest";
+
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<>(FragmentTestActivity.class);
+
+    @Test
+    @UiThreadTest
+    public void inflatedChildFragment() throws Throwable {
+        final FragmentTestActivity activity = mActivityRule.getActivity();
+        final FragmentManager fm = activity.getSupportFragmentManager();
+
+        ParentFragment parentFragment = new ParentFragment();
+        fm.beginTransaction().add(android.R.id.content, parentFragment).commitNow();
+
+        fm.beginTransaction().replace(android.R.id.content, new SimpleFragment())
+                .addToBackStack(null).commit();
+        fm.executePendingTransactions();
+
+        fm.popBackStackImmediate();
+    }
+
+    public static class ParentFragment extends Fragment {
+        @Nullable
+        @Override
+        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                @Nullable Bundle savedInstanceState) {
+            return inflater.inflate(R.layout.nested_inflated_fragment_parent, container, false);
+        }
+    }
+
+    public static class InflatedChildFragment extends Fragment {
+        @Nullable
+        @Override
+        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                @Nullable Bundle savedInstanceState) {
+            return inflater.inflate(R.layout.nested_inflated_fragment_child, container, false);
+        }
+    }
+
+    public static class SimpleFragment extends Fragment {
+        @Nullable
+        @Override
+        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                @Nullable Bundle savedInstanceState) {
+            TextView textView = new TextView(inflater.getContext());
+            textView.setText("Simple fragment");
+            return textView;
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
index dbe0422..bdb73c2 100644
--- a/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
+++ b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
@@ -62,7 +62,7 @@
         mBeginningFragment = new PostponedFragment1();
         fm.beginTransaction()
                 .add(R.id.fragmentContainer, mBeginningFragment)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -83,7 +83,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -109,6 +109,59 @@
         assertBackTransition(fragment, mBeginningFragment);
     }
 
+    // Ensure that replacing a fragment doesn't cause problems with the back stack nesting level
+    @Test
+    public void backStackNestingLevel() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment1 = new TransitionFragment2();
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+
+        // make sure transition ran
+        assertForwardTransition(mBeginningFragment, fragment1);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        // should be postponed going back
+        assertPostponedTransition(fragment1, mBeginningFragment, null);
+
+        // start the postponed transition
+        mBeginningFragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment1, mBeginningFragment);
+
+        startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment2 = new TransitionFragment2();
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .setReorderingAllowed(true)
+                .commit();
+
+        // make sure transition ran
+        assertForwardTransition(mBeginningFragment, fragment2);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        // should be postponed going back
+        assertPostponedTransition(fragment2, mBeginningFragment, null);
+
+        // start the postponed transition
+        mBeginningFragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment2, mBeginningFragment);
+    }
+
     // Ensure that postponed transition is forced after another has been committed.
     // This tests when the transactions are executed together
     @Test
@@ -129,14 +182,14 @@
                         .addSharedElement(startBlue, "blueSquare")
                         .replace(R.id.fragmentContainer, fragment2)
                         .addToBackStack(null)
-                        .setAllowOptimization(true)
+                        .setReorderingAllowed(true)
                         .commit();
 
                 fm.beginTransaction()
                         .addSharedElement(startBlue, "blueSquare")
                         .replace(R.id.fragmentContainer, fragment3)
                         .addToBackStack(null)
-                        .setAllowOptimization(true)
+                        .setReorderingAllowed(true)
                         .commit();
             }
         });
@@ -181,7 +234,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -193,7 +246,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment3)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         // This should cancel the mBeginningFragment -> fragment2 transition
@@ -245,7 +298,7 @@
                 .attach(fragment2)
                 .show(fragment2)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -276,7 +329,7 @@
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
         fm.beginTransaction()
                 .remove(mBeginningFragment)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
@@ -287,7 +340,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -306,7 +359,7 @@
                 .addSharedElement(startBlue1, "blueSquare")
                 .replace(R.id.fragmentContainer1, fragment3)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -319,7 +372,7 @@
                 .addSharedElement(startBlue2, "blueSquare")
                 .replace(R.id.fragmentContainer2, fragment4)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -374,7 +427,7 @@
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
         fm.beginTransaction()
                 .remove(mBeginningFragment)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
@@ -385,7 +438,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -404,7 +457,7 @@
                 .addSharedElement(startBlue1, "blueSquare")
                 .replace(R.id.fragmentContainer1, fragment3)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -417,7 +470,7 @@
                 .addSharedElement(startBlue2, "blueSquare")
                 .replace(R.id.fragmentContainer2, fragment4)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -472,7 +525,7 @@
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
         fm.beginTransaction()
                 .remove(mBeginningFragment)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
@@ -483,7 +536,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -504,7 +557,7 @@
                 .replace(R.id.fragmentContainer1, fragment3)
                 .add(strictFragment1, "1")
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -522,7 +575,7 @@
                         .replace(R.id.fragmentContainer2, fragment4)
                         .remove(strictFragment1)
                         .add(strictFragment2, "2")
-                        .setAllowOptimization(true)
+                        .setReorderingAllowed(true)
                         .commitNow();
             }
         });
@@ -560,7 +613,7 @@
                 .addSharedElement(startBlue1, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -572,7 +625,7 @@
                 fm.beginTransaction()
                         .addSharedElement(startBlue2, "blueSquare")
                         .replace(R.id.fragmentContainer, fragment1)
-                        .setAllowOptimization(true)
+                        .setReorderingAllowed(true)
                         .commitNow();
             }
         });
@@ -592,7 +645,7 @@
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
         fm.beginTransaction()
                 .remove(mBeginningFragment)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
@@ -601,7 +654,7 @@
 
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -612,7 +665,7 @@
         // Create a postponed transaction that removes a view
         fm.beginTransaction()
                 .replace(R.id.fragmentContainer1, fragment2)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         assertPostponedTransition(fragment1, fragment2, null);
@@ -621,7 +674,7 @@
         // Create a transaction that doesn't interfere with the previously postponed one
         fm.beginTransaction()
                 .replace(R.id.fragmentContainer2, fragment3)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -647,7 +700,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -684,7 +737,7 @@
         fm1.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1, "1")
                 .addToBackStack(null)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -729,7 +782,7 @@
         fm.beginTransaction()
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment)
-                .setAllowOptimization(true)
+                .setReorderingAllowed(true)
                 .addToBackStack(null)
                 .commit();
 
@@ -905,4 +958,13 @@
                     .commitNow();
         }
     }
+
+    public static class TransitionFragment2 extends TransitionFragment {
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            super.onCreateView(inflater, container, savedInstanceState);
+            return inflater.inflate(R.layout.scene2, container, false);
+        }
+    }
 }
diff --git a/fragment/tests/java/android/support/v4/app/PrimaryNavFragmentTest.java b/fragment/tests/java/android/support/v4/app/PrimaryNavFragmentTest.java
new file mode 100644
index 0000000..6895ce9
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/PrimaryNavFragmentTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.EmptyFragmentTestActivity;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class PrimaryNavFragmentTest {
+    @Rule
+    public ActivityTestRule<EmptyFragmentTestActivity> mActivityRule =
+            new ActivityTestRule<EmptyFragmentTestActivity>(EmptyFragmentTestActivity.class);
+
+    @Test
+    public void delegateBackToPrimaryNav() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictFragment strictFragment = new StrictFragment();
+
+        fm.beginTransaction().add(strictFragment, null).setPrimaryNavigationFragment(strictFragment)
+                .commit();
+        executePendingTransactions(fm);
+
+        assertSame("new fragment is not primary nav fragment", strictFragment,
+                fm.getPrimaryNavigationFragment());
+
+        final StrictFragment child = new StrictFragment();
+        FragmentManager cfm = strictFragment.getChildFragmentManager();
+        cfm.beginTransaction().add(child, null).addToBackStack(null).commit();
+        executePendingTransactions(cfm);
+
+        assertEquals("child transaction not on back stack", 1, cfm.getBackStackEntryCount());
+
+        // Should execute the pop for the child fragmentmanager
+        assertTrue("popBackStackImmediate returned no action performed",
+                popBackStackImmediate(fm));
+
+        assertEquals("child transaction still on back stack", 0, cfm.getBackStackEntryCount());
+    }
+
+    @Test
+    public void popPrimaryNav() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictFragment strictFragment1 = new StrictFragment();
+
+        fm.beginTransaction().add(strictFragment1, null)
+                .setPrimaryNavigationFragment(strictFragment1)
+                .commit();
+        executePendingTransactions(fm);
+
+        assertSame("new fragment is not primary nav fragment", strictFragment1,
+                fm.getPrimaryNavigationFragment());
+
+        fm.beginTransaction().remove(strictFragment1).addToBackStack(null).commit();
+        executePendingTransactions(fm);
+
+        assertNull("primary nav fragment is not null after remove",
+                fm.getPrimaryNavigationFragment());
+
+        popBackStackImmediate(fm);
+
+        assertSame("primary nav fragment was not restored on pop", strictFragment1,
+                fm.getPrimaryNavigationFragment());
+
+        final StrictFragment strictFragment2 = new StrictFragment();
+        fm.beginTransaction().remove(strictFragment1).add(strictFragment2, null)
+                .setPrimaryNavigationFragment(strictFragment2).addToBackStack(null).commit();
+        executePendingTransactions(fm);
+
+        assertSame("primary nav fragment not updated to new fragment", strictFragment2,
+                fm.getPrimaryNavigationFragment());
+
+        popBackStackImmediate(fm);
+
+        assertSame("primary nav fragment not restored on pop", strictFragment1,
+                fm.getPrimaryNavigationFragment());
+
+        fm.beginTransaction().setPrimaryNavigationFragment(strictFragment1)
+                .addToBackStack(null).commit();
+        executePendingTransactions(fm);
+
+        assertSame("primary nav fragment not retained when set again in new transaction",
+                strictFragment1, fm.getPrimaryNavigationFragment());
+        popBackStackImmediate(fm);
+
+        assertSame("same primary nav fragment not retained when set primary nav transaction popped",
+                strictFragment1, fm.getPrimaryNavigationFragment());
+    }
+
+    @Test
+    public void replacePrimaryNav() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictFragment strictFragment1 = new StrictFragment();
+
+        fm.beginTransaction().add(android.R.id.content, strictFragment1)
+                .setPrimaryNavigationFragment(strictFragment1).commit();
+        executePendingTransactions(fm);
+
+        assertSame("new fragment is not primary nav fragment", strictFragment1,
+                fm.getPrimaryNavigationFragment());
+
+        final StrictFragment strictFragment2 = new StrictFragment();
+        fm.beginTransaction().replace(android.R.id.content, strictFragment2)
+                .addToBackStack(null).commit();
+
+        executePendingTransactions(fm);
+
+        assertNull("primary nav fragment not null after replace",
+                fm.getPrimaryNavigationFragment());
+
+        popBackStackImmediate(fm);
+
+        assertSame("primary nav fragment not restored after popping replace", strictFragment1,
+                fm.getPrimaryNavigationFragment());
+
+        fm.beginTransaction().setPrimaryNavigationFragment(null).commit();
+        executePendingTransactions(fm);
+
+        assertNull("primary nav fragment not null after explicit set to null",
+                fm.getPrimaryNavigationFragment());
+
+        fm.beginTransaction().replace(android.R.id.content, strictFragment2)
+                .setPrimaryNavigationFragment(strictFragment2).addToBackStack(null).commit();
+        executePendingTransactions(fm);
+
+        assertSame("primary nav fragment not set correctly after replace", strictFragment2,
+                fm.getPrimaryNavigationFragment());
+
+        popBackStackImmediate(fm);
+
+        assertNull("primary nav fragment not null after popping replace",
+                fm.getPrimaryNavigationFragment());
+    }
+
+    private void executePendingTransactions(final FragmentManager fm) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                fm.executePendingTransactions();
+            }
+        });
+    }
+
+    private boolean popBackStackImmediate(final FragmentManager fm) throws Throwable {
+        final boolean[] result = new boolean[1];
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                result[0] = fm.popBackStackImmediate();
+            }
+        });
+        return result[0];
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/StrictFragment.java b/fragment/tests/java/android/support/v4/app/StrictFragment.java
index 033eb65..10ec94d 100644
--- a/fragment/tests/java/android/support/v4/app/StrictFragment.java
+++ b/fragment/tests/java/android/support/v4/app/StrictFragment.java
@@ -102,8 +102,8 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        if (mCalledOnCreate) {
-            throw new IllegalStateException("onCreate called more than once");
+        if (mCalledOnCreate && !mCalledOnDestroy) {
+            throw new IllegalStateException("onCreate called more than once with no onDestroy");
         }
         mCalledOnCreate = true;
         checkState("onCreate", ATTACHED);
diff --git a/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java b/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java
index 5e81568..8a051f4 100644
--- a/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java
+++ b/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java
@@ -20,35 +20,22 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.fragment.test.R;
-import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.AsyncTaskLoader;
 import android.support.v4.content.Loader;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import java.util.concurrent.CountDownLatch;
-
-public class LoaderActivity extends FragmentActivity {
-    // These must be cleared after each test using clearState()
-    public static LoaderActivity sActivity;
-    public static CountDownLatch sResumed;
-
+public class LoaderActivity extends RecreatedActivity {
     public TextView textView;
     public TextView textViewB;
 
-    public static void clearState() {
-        sActivity = null;
-        sResumed = null;
-    }
-
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        sActivity = this;
 
         setContentView(R.layout.fragment_a);
-        textView = (TextView) findViewById(R.id.textA);
+        textView = findViewById(R.id.textA);
         ViewGroup container = (ViewGroup) textView.getParent();
         textViewB = new TextView(this);
         textViewB.setId(R.id.textB);
@@ -59,9 +46,6 @@
     protected void onResume() {
         super.onResume();
         getSupportLoaderManager().initLoader(0, null, new TextLoaderCallback());
-        if (sResumed != null) {
-            sResumed.countDown();
-        }
     }
 
     class TextLoaderCallback implements LoaderManager.LoaderCallbacks<String> {
diff --git a/fragment/tests/java/android/support/v4/app/test/NonConfigOnStopActivity.java b/fragment/tests/java/android/support/v4/app/test/NonConfigOnStopActivity.java
new file mode 100644
index 0000000..fc03b50
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/test/NonConfigOnStopActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app.test;
+
+import android.support.v4.app.Fragment;
+
+public class NonConfigOnStopActivity extends RecreatedActivity {
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        getSupportFragmentManager()
+                .beginTransaction()
+                .add(new RetainedFragment(), "1")
+                .commitNowAllowingStateLoss();
+    }
+
+    public static class RetainedFragment extends Fragment {
+        public RetainedFragment() {
+            setRetainInstance(true);
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/test/RecreatedActivity.java b/fragment/tests/java/android/support/v4/app/test/RecreatedActivity.java
new file mode 100644
index 0000000..c298a88
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/test/RecreatedActivity.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.app.test;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+
+import java.util.concurrent.CountDownLatch;
+
+public class RecreatedActivity extends FragmentActivity {
+    // These must be cleared after each test using clearState()
+    public static RecreatedActivity sActivity;
+    public static CountDownLatch sResumed;
+    public static CountDownLatch sDestroyed;
+
+    public static void clearState() {
+        sActivity = null;
+        sResumed = null;
+        sDestroyed = null;
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        sActivity = this;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (sResumed != null) {
+            sResumed.countDown();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (sDestroyed != null) {
+            sDestroyed.countDown();
+        }
+    }
+}
diff --git a/fragment/tests/res/animator/slow_fade_out.xml b/fragment/tests/res/animator/slow_fade_out.xml
new file mode 100644
index 0000000..1d7b956
--- /dev/null
+++ b/fragment/tests/res/animator/slow_fade_out.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:duration="5000"
+                android:propertyName="alpha"
+                android:valueFrom="1.0"
+                android:valueTo="0.0"/>
diff --git a/fragment/tests/res/layout/nested_inflated_fragment_child.xml b/fragment/tests/res/layout/nested_inflated_fragment_child.xml
new file mode 100644
index 0000000..0bb98d2
--- /dev/null
+++ b/fragment/tests/res/layout/nested_inflated_fragment_child.xml
@@ -0,0 +1,24 @@
+<?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"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+    <TextView android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="Test" />
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/nested_inflated_fragment_parent.xml b/fragment/tests/res/layout/nested_inflated_fragment_parent.xml
new file mode 100644
index 0000000..665a705
--- /dev/null
+++ b/fragment/tests/res/layout/nested_inflated_fragment_parent.xml
@@ -0,0 +1,25 @@
+<?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"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+    <fragment android:name="android.support.v4.app.NestedInflatedFragmentTest$InflatedChildFragment"
+              android:id="@+id/child_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/values/colors.xml b/fragment/tests/res/values/colors.xml
index d0d5309..3c7bf7c 100644
--- a/fragment/tests/res/values/colors.xml
+++ b/fragment/tests/res/values/colors.xml
@@ -13,7 +13,7 @@
      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">
+<resources>
     <color name="text_color">#FF8090</color>
 
     <color name="test_red">#FF6030</color>
diff --git a/fragment/tests/res/values/dimens.xml b/fragment/tests/res/values/dimens.xml
index c3617a9..d473645 100644
--- a/fragment/tests/res/values/dimens.xml
+++ b/fragment/tests/res/values/dimens.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <dimen name="text_medium_size">20sp</dimen>
 </resources>
\ No newline at end of file
diff --git a/fragment/tests/res/values/strings.xml b/fragment/tests/res/values/strings.xml
index 2fa1430..e605c2c 100644
--- a/fragment/tests/res/values/strings.xml
+++ b/fragment/tests/res/values/strings.xml
@@ -13,6 +13,6 @@
      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">
+<resources>
     <string name="hello">Hello World</string>
 </resources>
\ No newline at end of file
diff --git a/fragment/tests/res/values/styles.xml b/fragment/tests/res/values/styles.xml
index ae6325b..3e8e01c 100644
--- a/fragment/tests/res/values/styles.xml
+++ b/fragment/tests/res/values/styles.xml
@@ -13,13 +13,8 @@
      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">
+<resources>
     <style name="TestActivityTheme">
         <item name="android:windowAnimationStyle">@null</item>
     </style>
-    <style name="TextMediumStyle" parent="@android:style/TextAppearance.Medium">
-        <item name="android:textSize">@dimen/text_medium_size</item>
-        <item name="android:textColor">@color/text_color</item>
-        <item name="android:textStyle">italic</item>
-    </style>
 </resources>
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index fb870a9..9b77c57 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,6 @@
-org.gradle.jvmargs=-Xmx3g
+org.gradle.jvmargs=-Xmx4g
 org.gradle.daemon=true
-org.gradle.configureondemand=true
+# Disabled due to b/63329112
+# org.gradle.configureondemand=true
 org.gradle.parallel=true
+android.useDexArchive=false
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 84939b4..2a386c4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-3.3-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-4.1-milestone-1-bin.zip
diff --git a/gradlew b/gradlew
index 30aa96f..4c2ef38 100755
--- a/gradlew
+++ b/gradlew
@@ -6,14 +6,6 @@
 ##
 ##############################################################################
 
-# Pick the correct fullsdk for this OS.
-if [ "$os" = "Darwin" ]; then
-    plat="darwin"
-else
-    plat="linux"
-fi
-DEFAULT_JVM_OPTS="-DLINT_API_DATABASE=../../prebuilts/fullsdk-$plat/platform-tools/api/api-versions.xml"
-
 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 
 APP_NAME="Gradle"
@@ -69,6 +61,14 @@
 
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 
+# Pick the correct fullsdk for this OS.
+if [ $darwin == "true" ]; then
+    plat="darwin"
+else
+    plat="linux"
+fi
+DEFAULT_JVM_OPTS="-DLINT_API_DATABASE=$APP_HOME/../../prebuilts/fullsdk-$plat/platform-tools/api/api-versions.xml"
+
 # Determine the Java command to use to start the JVM.
 if [ -n "$JAVA_HOME" ] ; then
     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index a7cd3e1..b06fd76 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -25,7 +25,7 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, static/src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/static/res
-LOCAL_MANIFEST_FILE := static/AndroidManifest-make.xml
+LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
@@ -44,7 +44,7 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, animated/src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/animated/res
-LOCAL_MANIFEST_FILE := animated/AndroidManifest-make.xml
+LOCAL_MANIFEST_FILE := animated/AndroidManifest.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-vectordrawable \
diff --git a/graphics/drawable/animated/AndroidManifest-make.xml b/graphics/drawable/animated/AndroidManifest-make.xml
deleted file mode 100644
index 98f9e17..0000000
--- a/graphics/drawable/animated/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.graphics.drawable.animated">
-    <application/>
-</manifest>
diff --git a/graphics/drawable/animated/build.gradle b/graphics/drawable/animated/build.gradle
index c3a1755..5fc9edd 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -1,40 +1,27 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'animated-vector-drawable'
 
 dependencies {
-    compile project(':support-vector-drawable')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-vector-drawable')
+    api project(':support-core-ui')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    compile project(path: ':support-core-ui')
-    testCompile 'junit:junit:4.12'
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
     defaultConfig {
-        minSdkVersion 11
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 
     aaptOptions {
@@ -46,52 +33,8 @@
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support AnimatedVectorDrawable'
-                description "Android Support AnimatedVectorDrawable"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
+supportLibrary {
+    name 'Android Support AnimatedVectorDrawable'
+    inceptionYear '2015'
+    description 'Android Support AnimatedVectorDrawable'
+}
\ No newline at end of file
diff --git a/graphics/drawable/animated/lint-baseline.xml b/graphics/drawable/animated/lint-baseline.xml
new file mode 100644
index 0000000..0668235
--- /dev/null
+++ b/graphics/drawable/animated/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="ResourceType"
+        message="Expected resource of type anim"
+        errorLine1="            parser = resources.getAnimation(id);"
+        errorLine2="                                            ~~">
+        <location
+            file="src/android/support/graphics/drawable/AnimatorInflaterCompat.java"
+            line="130"
+            column="45"/>
+    </issue>
+
+</issues>
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/Animatable2Compat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/Animatable2Compat.java
index d9d0f36..232dc81 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/Animatable2Compat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/Animatable2Compat.java
@@ -25,7 +25,7 @@
 import android.support.annotation.RequiresApi;
 
 /**
- * Abstract class that drawables supporting animations and callbacks should extend in support lib.
+ * Interface that drawables supporting animations and callbacks should extend in support lib.
  */
 public interface Animatable2Compat extends Animatable {
 
@@ -51,7 +51,7 @@
     void clearAnimationCallbacks();
 
     /**
-     * Animation callback interface. Used to notify animation events.
+     * Abstract class for animation callback. Used to notify animation events.
      */
     abstract class AnimationCallback {
         /**
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
index 5337dd1..cff61bc 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -36,7 +35,9 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.util.ArrayMap;
 import android.util.AttributeSet;
@@ -130,16 +131,11 @@
  * <li>Path Morphing (PathType evaluator). This is used for morphing one path into another.</li>
  * <li>Path Interpolation. This is used to defined a flexible interpolator (represented as a path)
  * instead of the system defined ones like LinearInterpolator.</li>
- * </ul>
- * <p/>
- * But not support this one feature yet:
- * <ul>
  * <li>Animating 2 values in one ObjectAnimator according to one path's X value and Y value. One
- * usage is moving one object in both X and Y dimensions along an path.</li> *
+ * usage is moving one object in both X and Y dimensions along an path.</li>
  * </ul>
  */
 
-@SuppressLint("NewApi")
 public class AnimatedVectorDrawableCompat extends VectorDrawableCommon
         implements Animatable2Compat {
     private static final String LOGTAG = "AnimatedVDCompat";
@@ -220,6 +216,7 @@
         }
         Resources resources = context.getResources();
         try {
+            //noinspection AndroidLintResourceType - Parse drawable as XML.
             final XmlPullParser parser = resources.getXml(resId);
             final AttributeSet attrs = Xml.asAttributeSet(parser);
             int type;
@@ -261,7 +258,7 @@
      */
     @Override
     public ConstantState getConstantState() {
-        if (mDelegateDrawable != null) {
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
             return new AnimatedVectorDrawableDelegateState(mDelegateDrawable.getConstantState());
         }
         // We can't support constant state in older platform.
@@ -423,7 +420,7 @@
     @Override
     public void setAutoMirrored(boolean mirrored) {
         if (mDelegateDrawable != null) {
-            mDelegateDrawable.setAutoMirrored(mirrored);
+            DrawableCompat.setAutoMirrored(mDelegateDrawable, mirrored);
             return;
         }
         mAnimatedVectorState.mVectorDrawable.setAutoMirrored(mirrored);
@@ -530,6 +527,7 @@
      * Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains
      * a delegated VectorDrawable instance.
      */
+    @RequiresApi(24)
     private static class AnimatedVectorDrawableDelegateState extends ConstantState {
         private final ConstantState mDelegateState;
 
@@ -686,6 +684,7 @@
     @Override
     public boolean isRunning() {
         if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning();
         }
         return mAnimatedVectorState.mAnimatorSet.isRunning();
@@ -694,6 +693,7 @@
     @Override
     public void start() {
         if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             ((AnimatedVectorDrawable) mDelegateDrawable).start();
             return;
         }
@@ -709,6 +709,7 @@
     @Override
     public void stop() {
         if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             ((AnimatedVectorDrawable) mDelegateDrawable).stop();
             return;
         }
@@ -736,6 +737,7 @@
      * A helper function to unregister the Animatable2Compat callback from the platform's
      * Animatable2 callback, while keeping the internal array of callback up to date.
      */
+    @RequiresApi(23)
     private static boolean unregisterPlatformCallback(AnimatedVectorDrawable dr,
             Animatable2Compat.AnimationCallback callback) {
         return dr.unregisterAnimationCallback(callback.getPlatformCallback());
@@ -745,6 +747,7 @@
     public void registerAnimationCallback(@NonNull Animatable2Compat.AnimationCallback
             callback) {
         if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             registerPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
             return;
         }
@@ -797,6 +800,7 @@
      * A helper function to register the Animatable2Compat callback on the platform's Animatable2
      * callback.
      */
+    @RequiresApi(23)
     private static void registerPlatformCallback(@NonNull AnimatedVectorDrawable avd,
             @NonNull final Animatable2Compat.AnimationCallback callback) {
         avd.registerAnimationCallback(callback.getPlatformCallback());
@@ -816,6 +820,7 @@
     public boolean unregisterAnimationCallback(
             @NonNull Animatable2Compat.AnimationCallback callback) {
         if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             unregisterPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
         }
 
@@ -835,6 +840,7 @@
     @Override
     public void clearAnimationCallbacks() {
         if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             ((AnimatedVectorDrawable) mDelegateDrawable).clearAnimationCallbacks();
             return;
         }
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatorInflaterCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatorInflaterCompat.java
index 1fb12fb..cfededb 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatorInflaterCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatorInflaterCompat.java
@@ -16,6 +16,10 @@
 
 package android.support.graphics.drawable;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import static java.lang.Math.min;
+
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorSet;
@@ -30,8 +34,13 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
 import android.os.Build;
 import android.support.annotation.AnimatorRes;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.graphics.PathParser;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.TypedValue;
@@ -55,14 +64,14 @@
  * <em>something</em> file.)
  * @hide
  */
+@RestrictTo(LIBRARY_GROUP)
 public class AnimatorInflaterCompat {
     private static final String TAG = "AnimatorInflater";
     /**
      * These flags are used when parsing AnimatorSet objects
      */
     private static final int TOGETHER = 0;
-    private static final int SEQUENTIALLY = 1;
-
+    private static final int MAX_NUM_POINTS = 100;
     /**
      * Enum values used in XML attributes to indicate the value for mValueType
      */
@@ -74,9 +83,6 @@
 
     private static final boolean DBG_ANIMATOR_INFLATER = false;
 
-    // used to calculate changing configs for resource references
-    private static final TypedValue sTmpTypedValue = new TypedValue();
-
     /**
      * Loads an {@link Animator} object from a context
      *
@@ -99,14 +105,13 @@
     }
 
     /**
-     * Loads an {@link Animator} object from a resource
+     * Loads an {@link Animator} object from a resource, context is for loading interpolator.
      *
      * @param resources The resources
      * @param theme     The theme
      * @param id        The resource id of the animation to load
      * @return The animator object reference by the specified id
      * @throws NotFoundException when the animation cannot be loaded
-     * @hide
      */
     public static Animator loadAnimator(Context context, Resources resources, Theme theme,
             @AnimatorRes int id) throws NotFoundException {
@@ -357,8 +362,6 @@
             if (pvh != null) {
                 anim.setValues(pvh);
             }
-        } else {
-            throw new IllegalArgumentException("no valueFrom or no valueTo");
         }
         anim.setDuration(duration);
         anim.setStartDelay(startDelay);
@@ -377,7 +380,6 @@
     /**
      * Setup ObjectAnimator's property or values from pathData.
      *
-     * @param getFloats           True if the value type is float.
      * @param anim                The target Animator which will be updated.
      * @param arrayObjectAnimator TypedArray for the ObjectAnimator.
      * @param pixelSize           The relative pixel size, used to calculate the
@@ -397,7 +399,24 @@
         // 3) PathInterpolator can also define a path (in pathData) for its interpolation curve.
         // Here we are dealing with case 2:
         if (pathData != null) {
-            Log.e(TAG, "We don't support moving along path yet");
+            String propertyXName = TypedArrayUtils.getNamedString(arrayObjectAnimator, parser,
+                    "propertyXName", AndroidResources.STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_X_NAME);
+            String propertyYName = TypedArrayUtils.getNamedString(arrayObjectAnimator, parser,
+                    "propertyYName", AndroidResources.STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_Y_NAME);
+
+
+            if (valueType == VALUE_TYPE_PATH || valueType == VALUE_TYPE_UNDEFINED) {
+                // When pathData is defined, we are in case #2 mentioned above. ValueType can only
+                // be float type, or int type. Otherwise we fallback to default type.
+                valueType = VALUE_TYPE_FLOAT;
+            }
+            if (propertyXName == null && propertyYName == null) {
+                throw new InflateException(arrayObjectAnimator.getPositionDescription()
+                        + " propertyXName or propertyYName is needed for PathData");
+            } else {
+                Path path = PathParser.createPathFromPathData(pathData);
+                setupPathMotion(path, oa,  0.5f * pixelSize, propertyXName, propertyYName);
+            }
         } else {
             String propertyName =
                     TypedArrayUtils.getNamedString(arrayObjectAnimator, parser, "propertyName",
@@ -410,6 +429,71 @@
 
     }
 
+    private static void setupPathMotion(Path path, ObjectAnimator oa, float precision,
+            String propertyXName, String propertyYName) {
+        // Measure the total length the whole path.
+        final PathMeasure measureForTotalLength = new PathMeasure(path, false);
+        float totalLength = 0;
+        // The sum of the previous contour plus the current one. Using the sum here b/c we want to
+        // directly substract from it later.
+        ArrayList<Float> contourLengths = new ArrayList<>();
+        contourLengths.add(0f);
+        do {
+            final float pathLength = measureForTotalLength.getLength();
+            totalLength += pathLength;
+            contourLengths.add(totalLength);
+
+        } while (measureForTotalLength.nextContour());
+
+        // Now determine how many sample points we need, and the step for next sample.
+        final PathMeasure pathMeasure = new PathMeasure(path, false);
+
+        final int numPoints = min(MAX_NUM_POINTS, (int) (totalLength / precision) + 1);
+
+        float[] mX = new float[numPoints];
+        float[] mY = new float[numPoints];
+        final float[] position = new float[2];
+
+        int contourIndex = 0;
+        float step = totalLength / (numPoints - 1);
+        float currentDistance = 0;
+
+        // For each sample point, determine whether we need to move on to next contour.
+        // After we find the right contour, then sample it using the current distance value minus
+        // 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];
+            currentDistance += step;
+            if ((contourIndex + 1) < contourLengths.size()
+                    && currentDistance > contourLengths.get(contourIndex + 1)) {
+                currentDistance -= contourLengths.get(contourIndex + 1);
+                contourIndex++;
+                pathMeasure.nextContour();
+            }
+        }
+
+        // Given the x and y value of the sample points, setup the ObjectAnimator properly.
+        PropertyValuesHolder x = null;
+        PropertyValuesHolder y = null;
+        if (propertyXName != null) {
+            x = PropertyValuesHolder.ofFloat(propertyXName, mX);
+        }
+        if (propertyYName != null) {
+            y = PropertyValuesHolder.ofFloat(propertyYName, mY);
+        }
+        if (x == null) {
+            oa.setValues(y);
+        } else if (y == null) {
+            oa.setValues(x);
+        } else {
+            oa.setValues(x, y);
+        }
+    }
+
     private static Animator createAnimatorFromXml(Context context, Resources res, Theme theme,
             XmlPullParser parser,
             float pixelSize)
@@ -771,7 +855,7 @@
                     Keyframe.ofInt(fraction);
         }
 
-        final int resID = TypedArrayUtils.getNamedResId(a, parser, "interpolator",
+        final int resID = TypedArrayUtils.getNamedResourceId(a, parser, "interpolator",
                 AndroidResources.STYLEABLE_KEYFRAME_INTERPOLATOR, 0);
         if (resID > 0) {
             final Interpolator interpolator = AnimationUtilsCompat.loadInterpolator(context, resID);
@@ -815,7 +899,7 @@
         parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale,
                 parser);
 
-        final int resID = TypedArrayUtils.getNamedResId(arrayAnimator, parser, "interpolator",
+        final int resID = TypedArrayUtils.getNamedResourceId(arrayAnimator, parser, "interpolator",
                 AndroidResources.STYLEABLE_ANIMATOR_INTERPOLATOR, 0);
         if (resID > 0) {
             final Interpolator interpolator = AnimationUtilsCompat.loadInterpolator(context, resID);
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/ArgbEvaluator.java b/graphics/drawable/animated/src/android/support/graphics/drawable/ArgbEvaluator.java
index cca0de6..f9e2985 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/ArgbEvaluator.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/ArgbEvaluator.java
@@ -16,14 +16,18 @@
 
 package android.support.graphics.drawable;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
+import android.support.annotation.RestrictTo;
 
 /**
  * This evaluator can be used to perform type interpolation between integer
  * values that represent ARGB colors.
  * @hide
  */
+@RestrictTo(LIBRARY_GROUP)
 public class ArgbEvaluator implements TypeEvaluator {
     private static final ArgbEvaluator sInstance = new ArgbEvaluator();
 
@@ -33,7 +37,6 @@
      * be used in multiple <code>Animator</code>s because it holds no state.
      *
      * @return An instance of <code>ArgbEvalutor</code>.
-     * @hide
      */
     public static ArgbEvaluator getInstance() {
         return sInstance;
@@ -55,6 +58,7 @@
      * color channels and interpolating each one separately, recombining the
      * resulting values in the same way.
      */
+    @Override
     public Object evaluate(float fraction, Object startValue, Object endValue) {
         int startInt = (Integer) startValue;
         float startA = ((startInt >> 24) & 0xff) / 255.0f;
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/PathInterpolatorCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/PathInterpolatorCompat.java
index 0288fe4..081676e 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/PathInterpolatorCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/PathInterpolatorCompat.java
@@ -26,6 +26,8 @@
 import android.graphics.Path;
 import android.graphics.PathMeasure;
 import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.graphics.PathParser;
 import android.util.AttributeSet;
 import android.view.InflateException;
 import android.view.animation.Interpolator;
diff --git a/graphics/drawable/animated/tests/AndroidManifest.xml b/graphics/drawable/animated/tests/AndroidManifest.xml
index 8999852..a43ebd4 100644
--- a/graphics/drawable/animated/tests/AndroidManifest.xml
+++ b/graphics/drawable/animated/tests/AndroidManifest.xml
@@ -15,20 +15,11 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.graphics.drawable.animated.test">
-    <uses-sdk
-            android:minSdkVersion="11"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                    android.support.test.espresso, android.support.test.espresso.idling" />
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <application>
         <activity android:name="android.support.graphics.drawable.tests.DrawableStubActivity"/>
     </application>
-
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.graphics.drawable.animated.test" />
 </manifest>
diff --git a/graphics/drawable/animated/tests/res/anim/path_motion_object.xml b/graphics/drawable/animated/tests/res/anim/path_motion_object.xml
new file mode 100644
index 0000000..43305af
--- /dev/null
+++ b/graphics/drawable/animated/tests/res/anim/path_motion_object.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="300"
+    android:propertyXName="translateX"
+    android:propertyYName="translateY"
+    android:pathData="M0 0 M 18 18 M 0 0 m -35, 0  a 35,35 0 1,0 70,0  a 35,35 0 1,0 -70,0  m 70, 0 a 35,35 0 1,0 -70,0  a 35,35 0 1,0 70,0"/>
diff --git a/graphics/drawable/animated/tests/res/drawable/animation_path_morphing_rect2.xml b/graphics/drawable/animated/tests/res/drawable/animation_path_morphing_rect2.xml
new file mode 100644
index 0000000..c849405
--- /dev/null
+++ b/graphics/drawable/animated/tests/res/drawable/animation_path_morphing_rect2.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/rect">
+
+    <target
+            android:name="rectBody"
+            android:animation="@anim/animation_rect"/>
+</animated-vector>
\ No newline at end of file
diff --git a/graphics/drawable/animated/tests/res/drawable/animation_path_motion_rect.xml b/graphics/drawable/animated/tests/res/drawable/animation_path_motion_rect.xml
new file mode 100644
index 0000000..4ebd613
--- /dev/null
+++ b/graphics/drawable/animated/tests/res/drawable/animation_path_motion_rect.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ 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.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/rect">
+
+    <target
+            android:name="rectGroup"
+            android:animation="@anim/path_motion_object"/>
+</animated-vector>
\ No newline at end of file
diff --git a/graphics/drawable/animated/tests/res/drawable/rect.xml b/graphics/drawable/animated/tests/res/drawable/rect.xml
index 5b162f5..e7998be 100644
--- a/graphics/drawable/animated/tests/res/drawable/rect.xml
+++ b/graphics/drawable/animated/tests/res/drawable/rect.xml
@@ -19,16 +19,11 @@
         android:viewportHeight="24.0"
         android:viewportWidth="24.0">
     <group
-            android:name="rectGroup"
-            android:pivotX="12"
-            android:pivotY="12"
-            android:rotation="0">
+        android:name="rectGroup">
         <path
-                android:name="rectBody"
-                android:fillColor="#ff0000"
-                android:pathData="M0,0L24,0L24,24L0,24z" />
+            android:name="rectBody"
+            android:fillColor="#ff0000"
+            android:pathData="M0,0L24,0L24,24L0,24z"/>
 
     </group>
-
-
 </vector>
diff --git a/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java
new file mode 100644
index 0000000..6bae146
--- /dev/null
+++ b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+package android.support.graphics.drawable.tests;
+
+import static android.support.graphics.drawable.tests.DrawableUtils.saveVectorDrawableIntoPNG;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.graphics.drawable.Animatable2Compat.AnimationCallback;
+import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
+import android.support.graphics.drawable.animated.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@MediumTest
+@RunWith(Parameterized.class)
+public class AnimatedVectorDrawableParameterizedTest {
+    @Rule public final ActivityTestRule<DrawableStubActivity> mActivityTestRule =
+            new ActivityTestRule<>(DrawableStubActivity.class);;
+
+    private static final int IMAGE_WIDTH = 64;
+    private static final int IMAGE_HEIGHT = 64;
+    private static final boolean DBG_DUMP_PNG = false;
+
+    private Context mContext;
+    private Resources mResources;
+    private int mResId;
+    private int mStartExpected;
+    private int mEndExpected;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                { R.drawable.animation_path_morphing_rect, 0xffff0000, 0x0},
+                { R.drawable.animation_path_motion_rect, 0xffff0000, 0x0},
+        });
+    }
+
+    public AnimatedVectorDrawableParameterizedTest(final int resId, int startExpected,
+            int endExpected) throws Throwable {
+        mResId = resId;
+        mStartExpected = startExpected;
+        mEndExpected = endExpected;
+    }
+
+    /**
+     * Render AVD with path morphing, make sure the bitmap is different when it render at the start
+     * and the end.
+     *
+     * @throws Exception for time out or I/O problem while dumping debug images.
+     */
+    @Test
+    public void testPathMorphing() throws Exception {
+        final Object lock = new Object();
+        final Bitmap bitmap = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_WIDTH,
+                Bitmap.Config.ARGB_8888);
+        final Canvas c = new Canvas(bitmap);
+
+        final AnimatedVectorDrawableCompat avd = AnimatedVectorDrawableCompat.create(mContext,
+                mResId);
+        avd.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
+
+        bitmap.eraseColor(0);
+        avd.draw(c);
+        int centerColor = bitmap.getPixel(IMAGE_WIDTH / 2 , IMAGE_WIDTH / 2);
+        assertTrue(centerColor == mStartExpected);
+
+        if (DBG_DUMP_PNG) {
+            saveVectorDrawableIntoPNG(mResources, bitmap, -1, "start");
+        }
+
+        avd.registerAnimationCallback(new AnimationCallback() {
+            @Override
+            public void onAnimationStart(Drawable drawable) {
+                // Nothing to do.
+            }
+
+            @Override
+            public void onAnimationEnd(Drawable drawable) {
+                bitmap.eraseColor(0);
+                drawable.draw(c);
+                int centerColor = bitmap.getPixel(IMAGE_WIDTH / 2 , IMAGE_WIDTH / 2);
+                assertTrue(centerColor == mEndExpected);
+
+                synchronized (lock) {
+                    lock.notify();
+                }
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                avd.start();
+            }
+        });
+
+        synchronized (lock) {
+            lock.wait(1000);
+        }
+
+        if (DBG_DUMP_PNG) {
+            saveVectorDrawableIntoPNG(mResources, bitmap, -1, "ended");
+        }
+    }
+
+    @Before
+    public void setup() throws Exception {
+        mContext = mActivityTestRule.getActivity();
+        mResources = mContext.getResources();
+    }
+}
diff --git a/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java
index b598f23..55a5e1a 100644
--- a/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java
+++ b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java
@@ -51,6 +51,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -468,7 +469,7 @@
         final Canvas c = new Canvas(bitmap);
 
         final AnimatedVectorDrawableCompat avd = AnimatedVectorDrawableCompat.create(mContext,
-                R.drawable.animation_path_morphing_rect);
+                R.drawable.animation_path_morphing_rect2);
         avd.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
 
         bitmap.eraseColor(0);
@@ -515,22 +516,17 @@
         }
     }
 
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
     /**
      * Make sure when path didn't match, we got an exception.
      */
     @Test
     @UiThreadTest
     public void testPathMorphingException() throws Exception {
-        boolean hasException = false;
-        try {
-            final AnimatedVectorDrawableCompat avd = AnimatedVectorDrawableCompat.create(mContext,
+        thrown.expect(RuntimeException.class);
+        final AnimatedVectorDrawableCompat avd = AnimatedVectorDrawableCompat.create(mContext,
                     R.drawable.animation_path_morphing_rect_exception);
-        } catch (Exception e) {
-            // Expected to come in here, so nothing happen.
-            hasException = true;
-        }
-
-        assertTrue(hasException);
-
     }
 }
diff --git a/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/PathInterpolatorValueParameterizedTest.java b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/PathInterpolatorValueParameterizedTest.java
new file mode 100644
index 0000000..1bab232
--- /dev/null
+++ b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/PathInterpolatorValueParameterizedTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.support.graphics.drawable.tests;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.support.graphics.drawable.AnimationUtilsCompat;
+import android.support.graphics.drawable.animated.test.R;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.view.animation.Interpolator;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@MediumTest
+@RunWith(Parameterized.class)
+public class PathInterpolatorValueParameterizedTest {
+    private static final float EPSILON = 1e-3f;
+    @Rule
+    public ActivityTestRule<DrawableStubActivity> mActivityRule =
+            new ActivityTestRule<>(DrawableStubActivity.class);
+
+    private Activity mActivity = null;
+    private int mResId;
+    private float mExpected;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {R.interpolator.control_points_interpolator, 0.89f},
+                {R.interpolator.single_control_point_interpolator, 0.086f},
+                {R.interpolator.path_interpolator, 0.85f}
+        });
+    }
+
+    public PathInterpolatorValueParameterizedTest(final int resId, float expected)
+            throws Throwable {
+        mResId = resId;
+        mExpected = expected;
+    }
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testPathInterpolator() throws Exception {
+        Interpolator interpolator = AnimationUtilsCompat.loadInterpolator(mActivity, mResId);
+        float value = interpolator.getInterpolation(0.5f);
+        float delta = Math.abs(value - mExpected);
+        assertTrue("value " + value + " is different than expected " + mExpected, delta < EPSILON);
+    }
+}
diff --git a/graphics/drawable/static/AndroidManifest-make.xml b/graphics/drawable/static/AndroidManifest-make.xml
deleted file mode 100644
index 8674cb4..0000000
--- a/graphics/drawable/static/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.support.graphics.drawable">
-    <application/>
-</manifest>
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index fdb306c..913729a 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -1,42 +1,24 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-vector-drawable'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-compat')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+    api project(':support-compat')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
-        exclude module: 'support-annotations'
-    }
-    testCompile 'junit:junit:4.12'
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-
+        minSdkVersion 14
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 
     aaptOptions {
@@ -44,52 +26,8 @@
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support VectorDrawable'
-                description "Android Support VectorDrawable"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support VectorDrawable'
+    inceptionYear '2015'
+    description 'Android Support VectorDrawable'
 }
diff --git a/graphics/drawable/static/lint-baseline.xml b/graphics/drawable/static/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/graphics/drawable/static/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/AndroidResources.java b/graphics/drawable/static/src/android/support/graphics/drawable/AndroidResources.java
index 5db0b8b..31370a2 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/AndroidResources.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/AndroidResources.java
@@ -14,6 +14,8 @@
 
 package android.support.graphics.drawable;
 
+import android.support.annotation.StyleableRes;
+
 class AndroidResources {
 
     // Resources ID generated in the latest R.java for framework.
@@ -78,7 +80,9 @@
     static final int[] STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET = {
             android.R.attr.name, android.R.attr.animation
     };
+    @StyleableRes
     static final int STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_ANIMATION = 1;
+    @StyleableRes
     static final int STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_NAME = 0;
 
     /////////////////////////////////////////////////////////////////////
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/PathParser.java b/graphics/drawable/static/src/android/support/graphics/drawable/PathParser.java
deleted file mode 100644
index 086fc9d..0000000
--- a/graphics/drawable/static/src/android/support/graphics/drawable/PathParser.java
+++ /dev/null
@@ -1,743 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.graphics.drawable;
-
-import android.graphics.Path;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-// This class is a duplicate from the PathParser.java of frameworks/base, with slight
-// update on incompatible API like copyOfRange().
-class PathParser {
-    private static final String LOGTAG = "PathParser";
-
-    // Copy from Arrays.copyOfRange() which is only available from API level 9.
-
-    /**
-     * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
-     * end (exclusive). The original order of elements is preserved.
-     * If {@code end} is greater than {@code original.length}, the result is padded
-     * with the value {@code 0.0f}.
-     *
-     * @param original the original array
-     * @param start    the start index, inclusive
-     * @param end      the end index, exclusive
-     * @return the new array
-     * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
-     * @throws IllegalArgumentException       if {@code start > end}
-     * @throws NullPointerException           if {@code original == null}
-     */
-    static float[] copyOfRange(float[] original, int start, int end) {
-        if (start > end) {
-            throw new IllegalArgumentException();
-        }
-        int originalLength = original.length;
-        if (start < 0 || start > originalLength) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        int resultLength = end - start;
-        int copyLength = Math.min(resultLength, originalLength - start);
-        float[] result = new float[resultLength];
-        System.arraycopy(original, start, result, 0, copyLength);
-        return result;
-    }
-
-    /**
-     * @param pathData The string representing a path, the same as "d" string in svg file.
-     * @return the generated Path object.
-     */
-    public static Path createPathFromPathData(String pathData) {
-        Path path = new Path();
-        PathDataNode[] nodes = createNodesFromPathData(pathData);
-        if (nodes != null) {
-            try {
-                PathDataNode.nodesToPath(nodes, path);
-            } catch (RuntimeException e) {
-                throw new RuntimeException("Error in parsing " + pathData, e);
-            }
-            return path;
-        }
-        return null;
-    }
-
-    /**
-     * @param pathData The string representing a path, the same as "d" string in svg file.
-     * @return an array of the PathDataNode.
-     */
-    public static PathDataNode[] createNodesFromPathData(String pathData) {
-        if (pathData == null) {
-            return null;
-        }
-        int start = 0;
-        int end = 1;
-
-        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
-        while (end < pathData.length()) {
-            end = nextStart(pathData, end);
-            String s = pathData.substring(start, end).trim();
-            if (s.length() > 0) {
-                float[] val = getFloats(s);
-                addNode(list, s.charAt(0), val);
-            }
-
-            start = end;
-            end++;
-        }
-        if ((end - start) == 1 && start < pathData.length()) {
-            addNode(list, pathData.charAt(start), new float[0]);
-        }
-        return list.toArray(new PathDataNode[list.size()]);
-    }
-
-    /**
-     * @param source The array of PathDataNode to be duplicated.
-     * @return a deep copy of the <code>source</code>.
-     */
-    public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
-        if (source == null) {
-            return null;
-        }
-        PathDataNode[] copy = new PathParser.PathDataNode[source.length];
-        for (int i = 0; i < source.length; i++) {
-            copy[i] = new PathDataNode(source[i]);
-        }
-        return copy;
-    }
-
-    /**
-     * @param nodesFrom The source path represented in an array of PathDataNode
-     * @param nodesTo   The target path represented in an array of PathDataNode
-     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
-     */
-    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
-        if (nodesFrom == null || nodesTo == null) {
-            return false;
-        }
-
-        if (nodesFrom.length != nodesTo.length) {
-            return false;
-        }
-
-        for (int i = 0; i < nodesFrom.length; i++) {
-            if (nodesFrom[i].mType != nodesTo[i].mType
-                    || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Update the target's data to match the source.
-     * Before calling this, make sure canMorph(target, source) is true.
-     *
-     * @param target The target path represented in an array of PathDataNode
-     * @param source The source path represented in an array of PathDataNode
-     */
-    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
-        for (int i = 0; i < source.length; i++) {
-            target[i].mType = source[i].mType;
-            for (int j = 0; j < source[i].mParams.length; j++) {
-                target[i].mParams[j] = source[i].mParams[j];
-            }
-        }
-    }
-
-    private static int nextStart(String s, int end) {
-        char c;
-
-        while (end < s.length()) {
-            c = s.charAt(end);
-            // Note that 'e' or 'E' are not valid path commands, but could be
-            // used for floating point numbers' scientific notation.
-            // Therefore, when searching for next command, we should ignore 'e'
-            // and 'E'.
-            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
-                    && c != 'e' && c != 'E') {
-                return end;
-            }
-            end++;
-        }
-        return end;
-    }
-
-    private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
-        list.add(new PathDataNode(cmd, val));
-    }
-
-    private static class ExtractFloatResult {
-        // We need to return the position of the next separator and whether the
-        // next float starts with a '-' or a '.'.
-        int mEndPosition;
-        boolean mEndWithNegOrDot;
-
-        ExtractFloatResult() {
-        }
-    }
-
-    /**
-     * Parse the floats in the string.
-     * This is an optimized version of parseFloat(s.split(",|\\s"));
-     *
-     * @param s the string containing a command and list of floats
-     * @return array of floats
-     */
-    private static float[] getFloats(String s) {
-        if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
-            return new float[0];
-        }
-        try {
-            float[] results = new float[s.length()];
-            int count = 0;
-            int startPosition = 1;
-            int endPosition = 0;
-
-            ExtractFloatResult result = new ExtractFloatResult();
-            int totalLength = s.length();
-
-            // The startPosition should always be the first character of the
-            // current number, and endPosition is the character after the current
-            // number.
-            while (startPosition < totalLength) {
-                extract(s, startPosition, result);
-                endPosition = result.mEndPosition;
-
-                if (startPosition < endPosition) {
-                    results[count++] = Float.parseFloat(
-                            s.substring(startPosition, endPosition));
-                }
-
-                if (result.mEndWithNegOrDot) {
-                    // Keep the '-' or '.' sign with next number.
-                    startPosition = endPosition;
-                } else {
-                    startPosition = endPosition + 1;
-                }
-            }
-            return copyOfRange(results, 0, count);
-        } catch (NumberFormatException e) {
-            throw new RuntimeException("error in parsing \"" + s + "\"", e);
-        }
-    }
-
-    /**
-     * Calculate the position of the next comma or space or negative sign
-     *
-     * @param s      the string to search
-     * @param start  the position to start searching
-     * @param result the result of the extraction, including the position of the
-     *               the starting position of next number, whether it is ending with a '-'.
-     */
-    private static void extract(String s, int start, ExtractFloatResult result) {
-        // Now looking for ' ', ',', '.' or '-' from the start.
-        int currentIndex = start;
-        boolean foundSeparator = false;
-        result.mEndWithNegOrDot = false;
-        boolean secondDot = false;
-        boolean isExponential = false;
-        for (; currentIndex < s.length(); currentIndex++) {
-            boolean isPrevExponential = isExponential;
-            isExponential = false;
-            char currentChar = s.charAt(currentIndex);
-            switch (currentChar) {
-                case ' ':
-                case ',':
-                    foundSeparator = true;
-                    break;
-                case '-':
-                    // The negative sign following a 'e' or 'E' is not a separator.
-                    if (currentIndex != start && !isPrevExponential) {
-                        foundSeparator = true;
-                        result.mEndWithNegOrDot = true;
-                    }
-                    break;
-                case '.':
-                    if (!secondDot) {
-                        secondDot = true;
-                    } else {
-                        // This is the second dot, and it is considered as a separator.
-                        foundSeparator = true;
-                        result.mEndWithNegOrDot = true;
-                    }
-                    break;
-                case 'e':
-                case 'E':
-                    isExponential = true;
-                    break;
-            }
-            if (foundSeparator) {
-                break;
-            }
-        }
-        // When there is nothing found, then we put the end position to the end
-        // of the string.
-        result.mEndPosition = currentIndex;
-    }
-
-    /**
-     * Each PathDataNode represents one command in the "d" attribute of the svg
-     * file.
-     * An array of PathDataNode can represent the whole "d" attribute.
-     */
-    public static class PathDataNode {
-        /*package*/
-        char mType;
-        float[] mParams;
-
-        PathDataNode(char type, float[] params) {
-            this.mType = type;
-            this.mParams = params;
-        }
-
-        PathDataNode(PathDataNode n) {
-            mType = n.mType;
-            mParams = copyOfRange(n.mParams, 0, n.mParams.length);
-        }
-
-        /**
-         * Convert an array of PathDataNode to Path.
-         *
-         * @param node The source array of PathDataNode.
-         * @param path The target Path object.
-         */
-        public static void nodesToPath(PathDataNode[] node, Path path) {
-            float[] current = new float[6];
-            char previousCommand = 'm';
-            for (int i = 0; i < node.length; i++) {
-                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
-                previousCommand = node[i].mType;
-            }
-        }
-
-        /**
-         * The current PathDataNode will be interpolated between the
-         * <code>nodeFrom</code> and <code>nodeTo</code> according to the
-         * <code>fraction</code>.
-         *
-         * @param nodeFrom The start value as a PathDataNode.
-         * @param nodeTo   The end value as a PathDataNode
-         * @param fraction The fraction to interpolate.
-         */
-        public void interpolatePathDataNode(PathDataNode nodeFrom,
-                PathDataNode nodeTo, float fraction) {
-            for (int i = 0; i < nodeFrom.mParams.length; i++) {
-                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
-                        + nodeTo.mParams[i] * fraction;
-            }
-        }
-
-        private static void addCommand(Path path, float[] current,
-                char previousCmd, char cmd, float[] val) {
-
-            int incr = 2;
-            float currentX = current[0];
-            float currentY = current[1];
-            float ctrlPointX = current[2];
-            float ctrlPointY = current[3];
-            float currentSegmentStartX = current[4];
-            float currentSegmentStartY = current[5];
-            float reflectiveCtrlPointX;
-            float reflectiveCtrlPointY;
-
-            switch (cmd) {
-                case 'z':
-                case 'Z':
-                    path.close();
-                    // Path is closed here, but we need to move the pen to the
-                    // closed position. So we cache the segment's starting position,
-                    // and restore it here.
-                    currentX = currentSegmentStartX;
-                    currentY = currentSegmentStartY;
-                    ctrlPointX = currentSegmentStartX;
-                    ctrlPointY = currentSegmentStartY;
-                    path.moveTo(currentX, currentY);
-                    break;
-                case 'm':
-                case 'M':
-                case 'l':
-                case 'L':
-                case 't':
-                case 'T':
-                    incr = 2;
-                    break;
-                case 'h':
-                case 'H':
-                case 'v':
-                case 'V':
-                    incr = 1;
-                    break;
-                case 'c':
-                case 'C':
-                    incr = 6;
-                    break;
-                case 's':
-                case 'S':
-                case 'q':
-                case 'Q':
-                    incr = 4;
-                    break;
-                case 'a':
-                case 'A':
-                    incr = 7;
-                    break;
-            }
-
-            for (int k = 0; k < val.length; k += incr) {
-                switch (cmd) {
-                    case 'm': // moveto - Start a new sub-path (relative)
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        if (k > 0) {
-                            // According to the spec, if a moveto is followed by multiple
-                            // pairs of coordinates, the subsequent pairs are treated as
-                            // implicit lineto commands.
-                            path.rLineTo(val[k + 0], val[k + 1]);
-                        } else {
-                            path.rMoveTo(val[k + 0], val[k + 1]);
-                            currentSegmentStartX = currentX;
-                            currentSegmentStartY = currentY;
-                        }
-                        break;
-                    case 'M': // moveto - Start a new sub-path
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        if (k > 0) {
-                            // According to the spec, if a moveto is followed by multiple
-                            // pairs of coordinates, the subsequent pairs are treated as
-                            // implicit lineto commands.
-                            path.lineTo(val[k + 0], val[k + 1]);
-                        } else {
-                            path.moveTo(val[k + 0], val[k + 1]);
-                            currentSegmentStartX = currentX;
-                            currentSegmentStartY = currentY;
-                        }
-                        break;
-                    case 'l': // lineto - Draw a line from the current point (relative)
-                        path.rLineTo(val[k + 0], val[k + 1]);
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'L': // lineto - Draw a line from the current point
-                        path.lineTo(val[k + 0], val[k + 1]);
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
-                        path.rLineTo(val[k + 0], 0);
-                        currentX += val[k + 0];
-                        break;
-                    case 'H': // horizontal lineto - Draws a horizontal line
-                        path.lineTo(val[k + 0], currentY);
-                        currentX = val[k + 0];
-                        break;
-                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
-                        path.rLineTo(0, val[k + 0]);
-                        currentY += val[k + 0];
-                        break;
-                    case 'V': // vertical lineto - Draws a vertical line from the current point
-                        path.lineTo(currentX, val[k + 0]);
-                        currentY = val[k + 0];
-                        break;
-                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
-                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
-                                val[k + 4], val[k + 5]);
-
-                        ctrlPointX = currentX + val[k + 2];
-                        ctrlPointY = currentY + val[k + 3];
-                        currentX += val[k + 4];
-                        currentY += val[k + 5];
-
-                        break;
-                    case 'C': // curveto - Draws a cubic Bézier curve
-                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
-                                val[k + 4], val[k + 5]);
-                        currentX = val[k + 4];
-                        currentY = val[k + 5];
-                        ctrlPointX = val[k + 2];
-                        ctrlPointY = val[k + 3];
-                        break;
-                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
-                        reflectiveCtrlPointX = 0;
-                        reflectiveCtrlPointY = 0;
-                        if (previousCmd == 'c' || previousCmd == 's'
-                                || previousCmd == 'C' || previousCmd == 'S') {
-                            reflectiveCtrlPointX = currentX - ctrlPointX;
-                            reflectiveCtrlPointY = currentY - ctrlPointY;
-                        }
-                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1],
-                                val[k + 2], val[k + 3]);
-
-                        ctrlPointX = currentX + val[k + 0];
-                        ctrlPointY = currentY + val[k + 1];
-                        currentX += val[k + 2];
-                        currentY += val[k + 3];
-                        break;
-                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
-                        reflectiveCtrlPointX = currentX;
-                        reflectiveCtrlPointY = currentY;
-                        if (previousCmd == 'c' || previousCmd == 's'
-                                || previousCmd == 'C' || previousCmd == 'S') {
-                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
-                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
-                        }
-                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = val[k + 0];
-                        ctrlPointY = val[k + 1];
-                        currentX = val[k + 2];
-                        currentY = val[k + 3];
-                        break;
-                    case 'q': // Draws a quadratic Bézier (relative)
-                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = currentX + val[k + 0];
-                        ctrlPointY = currentY + val[k + 1];
-                        currentX += val[k + 2];
-                        currentY += val[k + 3];
-                        break;
-                    case 'Q': // Draws a quadratic Bézier
-                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = val[k + 0];
-                        ctrlPointY = val[k + 1];
-                        currentX = val[k + 2];
-                        currentY = val[k + 3];
-                        break;
-                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
-                        reflectiveCtrlPointX = 0;
-                        reflectiveCtrlPointY = 0;
-                        if (previousCmd == 'q' || previousCmd == 't'
-                                || previousCmd == 'Q' || previousCmd == 'T') {
-                            reflectiveCtrlPointX = currentX - ctrlPointX;
-                            reflectiveCtrlPointY = currentY - ctrlPointY;
-                        }
-                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1]);
-                        ctrlPointX = currentX + reflectiveCtrlPointX;
-                        ctrlPointY = currentY + reflectiveCtrlPointY;
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
-                        reflectiveCtrlPointX = currentX;
-                        reflectiveCtrlPointY = currentY;
-                        if (previousCmd == 'q' || previousCmd == 't'
-                                || previousCmd == 'Q' || previousCmd == 'T') {
-                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
-                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
-                        }
-                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1]);
-                        ctrlPointX = reflectiveCtrlPointX;
-                        ctrlPointY = reflectiveCtrlPointY;
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'a': // Draws an elliptical arc
-                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
-                        drawArc(path,
-                                currentX,
-                                currentY,
-                                val[k + 5] + currentX,
-                                val[k + 6] + currentY,
-                                val[k + 0],
-                                val[k + 1],
-                                val[k + 2],
-                                val[k + 3] != 0,
-                                val[k + 4] != 0);
-                        currentX += val[k + 5];
-                        currentY += val[k + 6];
-                        ctrlPointX = currentX;
-                        ctrlPointY = currentY;
-                        break;
-                    case 'A': // Draws an elliptical arc
-                        drawArc(path,
-                                currentX,
-                                currentY,
-                                val[k + 5],
-                                val[k + 6],
-                                val[k + 0],
-                                val[k + 1],
-                                val[k + 2],
-                                val[k + 3] != 0,
-                                val[k + 4] != 0);
-                        currentX = val[k + 5];
-                        currentY = val[k + 6];
-                        ctrlPointX = currentX;
-                        ctrlPointY = currentY;
-                        break;
-                }
-                previousCmd = cmd;
-            }
-            current[0] = currentX;
-            current[1] = currentY;
-            current[2] = ctrlPointX;
-            current[3] = ctrlPointY;
-            current[4] = currentSegmentStartX;
-            current[5] = currentSegmentStartY;
-        }
-
-        private static void drawArc(Path p,
-                float x0,
-                float y0,
-                float x1,
-                float y1,
-                float a,
-                float b,
-                float theta,
-                boolean isMoreThanHalf,
-                boolean isPositiveArc) {
-
-            /* Convert rotation angle from degrees to radians */
-            double thetaD = Math.toRadians(theta);
-            /* Pre-compute rotation matrix entries */
-            double cosTheta = Math.cos(thetaD);
-            double sinTheta = Math.sin(thetaD);
-            /* Transform (x0, y0) and (x1, y1) into unit space */
-            /* using (inverse) rotation, followed by (inverse) scale */
-            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
-            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
-            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
-            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
-            /* Compute differences and averages */
-            double dx = x0p - x1p;
-            double dy = y0p - y1p;
-            double xm = (x0p + x1p) / 2;
-            double ym = (y0p + y1p) / 2;
-            /* Solve for intersecting unit circles */
-            double dsq = dx * dx + dy * dy;
-            if (dsq == 0.0) {
-                Log.w(LOGTAG, " Points are coincident");
-                return; /* Points are coincident */
-            }
-            double disc = 1.0 / dsq - 1.0 / 4.0;
-            if (disc < 0.0) {
-                Log.w(LOGTAG, "Points are too far apart " + dsq);
-                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
-                drawArc(p, x0, y0, x1, y1, a * adjust,
-                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
-                return; /* Points are too far apart */
-            }
-            double s = Math.sqrt(disc);
-            double sdx = s * dx;
-            double sdy = s * dy;
-            double cx;
-            double cy;
-            if (isMoreThanHalf == isPositiveArc) {
-                cx = xm - sdy;
-                cy = ym + sdx;
-            } else {
-                cx = xm + sdy;
-                cy = ym - sdx;
-            }
-
-            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
-
-            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
-
-            double sweep = (eta1 - eta0);
-            if (isPositiveArc != (sweep >= 0)) {
-                if (sweep > 0) {
-                    sweep -= 2 * Math.PI;
-                } else {
-                    sweep += 2 * Math.PI;
-                }
-            }
-
-            cx *= a;
-            cy *= b;
-            double tcx = cx;
-            cx = cx * cosTheta - cy * sinTheta;
-            cy = tcx * sinTheta + cy * cosTheta;
-
-            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
-        }
-
-        /**
-         * Converts an arc to cubic Bezier segments and records them in p.
-         *
-         * @param p     The target for the cubic Bezier segments
-         * @param cx    The x coordinate center of the ellipse
-         * @param cy    The y coordinate center of the ellipse
-         * @param a     The radius of the ellipse in the horizontal direction
-         * @param b     The radius of the ellipse in the vertical direction
-         * @param e1x   E(eta1) x coordinate of the starting point of the arc
-         * @param e1y   E(eta2) y coordinate of the starting point of the arc
-         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
-         * @param start The start angle of the arc on the ellipse
-         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
-         */
-        private static void arcToBezier(Path p,
-                double cx,
-                double cy,
-                double a,
-                double b,
-                double e1x,
-                double e1y,
-                double theta,
-                double start,
-                double sweep) {
-            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
-            // and http://www.spaceroots.org/documents/ellipse/node22.html
-
-            // Maximum of 45 degrees per cubic Bezier segment
-            int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
-
-            double eta1 = start;
-            double cosTheta = Math.cos(theta);
-            double sinTheta = Math.sin(theta);
-            double cosEta1 = Math.cos(eta1);
-            double sinEta1 = Math.sin(eta1);
-            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
-            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
-            double anglePerSegment = sweep / numSegments;
-            for (int i = 0; i < numSegments; i++) {
-                double eta2 = eta1 + anglePerSegment;
-                double sinEta2 = Math.sin(eta2);
-                double cosEta2 = Math.cos(eta2);
-                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
-                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
-                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
-                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
-                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
-                double alpha =
-                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
-                double q1x = e1x + alpha * ep1x;
-                double q1y = e1y + alpha * ep1y;
-                double q2x = e2x - alpha * ep2x;
-                double q2y = e2y - alpha * ep2y;
-
-                // Adding this no-op call to workaround a proguard related issue.
-                p.rLineTo(0, 0);
-
-                p.cubicTo((float) q1x,
-                        (float) q1y,
-                        (float) q2x,
-                        (float) q2y,
-                        (float) e2x,
-                        (float) e2y);
-                eta1 = eta2;
-                e1x = e2x;
-                e1y = e2y;
-                ep1x = ep2x;
-                ep1y = ep2y;
-            }
-        }
-    }
-}
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/TypedArrayUtils.java b/graphics/drawable/static/src/android/support/graphics/drawable/TypedArrayUtils.java
deleted file mode 100644
index 1e196e9..0000000
--- a/graphics/drawable/static/src/android/support/graphics/drawable/TypedArrayUtils.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.graphics.drawable;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-
-import org.xmlpull.v1.XmlPullParser;
-
-class TypedArrayUtils {
-    private static final String NAMESPACE = "http://schemas.android.com/apk/res/android";
-
-    public static boolean hasAttribute(XmlPullParser parser, String attrName) {
-        return parser.getAttributeValue(NAMESPACE, attrName) != null;
-    }
-
-    public static float getNamedFloat(TypedArray a, XmlPullParser parser, String attrName,
-                                      int resId, float defaultValue) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return defaultValue;
-        } else {
-            return a.getFloat(resId, defaultValue);
-        }
-    }
-
-    public static boolean getNamedBoolean(TypedArray a, XmlPullParser parser, String attrName,
-                                          int resId, boolean defaultValue) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return defaultValue;
-        } else {
-            return a.getBoolean(resId, defaultValue);
-        }
-    }
-
-    public static int getNamedInt(TypedArray a, XmlPullParser parser, String attrName,
-                                  int resId, int defaultValue) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return defaultValue;
-        } else {
-            return a.getInt(resId, defaultValue);
-        }
-    }
-
-    public static int getNamedColor(TypedArray a, XmlPullParser parser, String attrName,
-                                    int resId, int defaultValue) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return defaultValue;
-        } else {
-            return a.getColor(resId, defaultValue);
-        }
-    }
-
-    public static String getNamedString(TypedArray a, XmlPullParser parser, String attrName,
-            int resId) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return null;
-        } else {
-            return a.getString(resId);
-        }
-    }
-
-    public static int getNamedResId(TypedArray a, XmlPullParser parser, String attrName,
-            int resId, int defaultValue) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return defaultValue;
-        } else {
-            return a.getResourceId(resId, defaultValue);
-        }
-    }
-
-    public static TypedValue peekNamedValue(TypedArray a, XmlPullParser parser, String attrName,
-            int resId) {
-        final boolean hasAttr = hasAttribute(parser, attrName);
-        if (!hasAttr) {
-            return null;
-        } else {
-            return a.peekValue(resId);
-        }
-    }
-
-    /**
-     * Obtains styled attributes from the theme, if available, or unstyled
-     * resources if the theme is null.
-     */
-    public static TypedArray obtainAttributes(
-            Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
-        if (theme == null) {
-            return res.obtainAttributes(set, attrs);
-        }
-        return theme.obtainStyledAttributes(set, attrs, 0, 0);
-    }
-}
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
index fb13e92..57d9b2b 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
@@ -28,7 +28,7 @@
  */
 abstract class VectorDrawableCommon extends Drawable implements TintAwareDrawable {
 
-    // Drawable delegation for Lollipop and above.
+    // Drawable delegation for supported API levels.
     Drawable mDelegateDrawable;
 
     @Override
@@ -67,7 +67,6 @@
 
     @Override
     public void setHotspot(float x, float y) {
-        // API >= 21 only.
         if (mDelegateDrawable != null) {
             DrawableCompat.setHotspot(mDelegateDrawable, x, y);
         }
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index 69b34a0..b68ef1b 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -40,8 +40,11 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.graphics.PathParser;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.util.ArrayMap;
 import android.util.AttributeSet;
@@ -262,7 +265,7 @@
 
     @Override
     public ConstantState getConstantState() {
-        if (mDelegateDrawable != null) {
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
             // Such that the configuration can be refreshed.
             return new VectorDrawableDelegateState(mDelegateDrawable.getConstantState());
         }
@@ -390,7 +393,6 @@
         return new PorterDuffColorFilter(color, tintMode);
     }
 
-    @SuppressLint("NewApi")
     @Override
     public void setTint(int tint) {
         if (mDelegateDrawable != null) {
@@ -517,11 +519,11 @@
      */
     @RestrictTo(LIBRARY_GROUP)
     public float getPixelSize() {
-        if (mVectorState == null && mVectorState.mVPathRenderer == null ||
-                mVectorState.mVPathRenderer.mBaseWidth == 0 ||
-                mVectorState.mVPathRenderer.mBaseHeight == 0 ||
-                mVectorState.mVPathRenderer.mViewportHeight == 0 ||
-                mVectorState.mVPathRenderer.mViewportWidth == 0) {
+        if (mVectorState == null || mVectorState.mVPathRenderer == null
+                || mVectorState.mVPathRenderer.mBaseWidth == 0
+                || mVectorState.mVPathRenderer.mBaseHeight == 0
+                || mVectorState.mVPathRenderer.mViewportHeight == 0
+                || mVectorState.mVPathRenderer.mViewportWidth == 0) {
             return 1; // fall back to 1:1 pixel mapping.
         }
         float intrinsicWidth = mVectorState.mVPathRenderer.mBaseWidth;
@@ -541,7 +543,6 @@
      * @param theme the theme of this vector drawable, it can be null.
      * @return a new VectorDrawableCompat or null if parsing error is found.
      */
-    @SuppressLint("NewApi")
     @Nullable
     public static VectorDrawableCompat create(@NonNull Resources res, @DrawableRes int resId,
                                               @Nullable Theme theme) {
@@ -554,7 +555,7 @@
         }
 
         try {
-            final XmlPullParser parser = res.getXml(resId);
+            @SuppressLint("ResourceType") final XmlPullParser parser = res.getXml(resId);
             final AttributeSet attrs = Xml.asAttributeSet(parser);
             int type;
             while ((type = parser.next()) != XmlPullParser.START_TAG &&
@@ -579,7 +580,6 @@
      * document, tries to create a Drawable from that tag. Returns {@code null}
      * if the tag is not a valid drawable.
      */
-    @SuppressLint("NewApi")
     public static VectorDrawableCompat createFromXmlInner(Resources r, XmlPullParser parser,
             AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
         final VectorDrawableCompat drawable = new VectorDrawableCompat();
@@ -594,7 +594,6 @@
         return color;
     }
 
-    @SuppressLint("NewApi")
     @Override
     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
             throws XmlPullParserException, IOException {
@@ -818,12 +817,12 @@
     }
 
     // We don't support RTL auto mirroring since the getLayoutDirection() is for API 17+.
-    @SuppressLint("NewApi")
     private boolean needMirroring() {
-        if (Build.VERSION.SDK_INT < 17) {
-            return false;
+        if (Build.VERSION.SDK_INT >= 17) {
+            return isAutoMirrored()
+                    && DrawableCompat.getLayoutDirection(this) == LayoutDirection.RTL;
         } else {
-            return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+            return false;
         }
     }
 
@@ -883,6 +882,7 @@
      * Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains
      * a delegated VectorDrawable instance.
      */
+    @RequiresApi(24)
     private static class VectorDrawableDelegateState extends ConstantState {
         private final ConstantState mDelegateState;
 
diff --git a/graphics/drawable/static/tests/AndroidManifest.xml b/graphics/drawable/static/tests/AndroidManifest.xml
index 3a6942d..2c1faaa 100644
--- a/graphics/drawable/static/tests/AndroidManifest.xml
+++ b/graphics/drawable/static/tests/AndroidManifest.xml
@@ -15,18 +15,8 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.graphics.drawable.test">
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                android.support.test.espresso, android.support.test.espresso.idling" />
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-    <application/>
-
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.graphics.drawable.test" />
 </manifest>
diff --git a/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java b/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
index f1b0b9a..8be9aab 100644
--- a/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
+++ b/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
@@ -121,7 +121,7 @@
     // exactly with the golden image.
     // We can increase the threshold if the Skia is drawing with some variance
     // on different devices. So far, the tests show they are matching correctly.
-    private static final float PIXEL_ERROR_THRESHOLD = 0.3f;
+    private static final float PIXEL_ERROR_THRESHOLD = 0.33f;
     private static final float PIXEL_DIFF_COUNT_THRESHOLD = 0.1f;
     private static final float PIXEL_DIFF_THRESHOLD = 0.025f;
 
diff --git a/lifecycle/.gitignore b/lifecycle/.gitignore
new file mode 100644
index 0000000..be4e6f1
--- /dev/null
+++ b/lifecycle/.gitignore
@@ -0,0 +1,4 @@
+local.properties
+maven-repo/
+build/
+*.DS_Store
diff --git a/lifecycle/common/build.gradle b/lifecycle/common/build.gradle
new file mode 100644
index 0000000..ac9b2e2f
--- /dev/null
+++ b/lifecycle/common/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'java'
+apply plugin: 'maven'
+
+sourceCompatibility = 1.7
+
+dependencies {
+    testCompile libs.junit
+    testCompile libs.mockito_core
+    compile libs.support.annotations
+}
+
+archivesBaseName = "common"
+
+createAndroidCheckstyle(project)
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/GenericLifecycleObserver.java b/lifecycle/common/src/main/java/android/arch/lifecycle/GenericLifecycleObserver.java
new file mode 100644
index 0000000..a34bfbf
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/GenericLifecycleObserver.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * Internal class that can receive any lifecycle change and dispatch it to the receiver.
+ * @hide
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public interface GenericLifecycleObserver extends LifecycleObserver {
+    /**
+     * Called when a state transition event happens.
+     *
+     * @param source The source of the event
+     * @param event The event
+     */
+    void onStateChanged(LifecycleOwner source, Lifecycle.Event event);
+
+    /**
+     * Returns the actual receiver of the event
+     *
+     * @return The actual receiver
+     */
+    Object getReceiver();
+}
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java b/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java
new file mode 100644
index 0000000..fcbd50a
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.MainThread;
+
+/**
+ * Defines an object that has an Android Lifecycle. {@link android.support.v4.app.Fragment Fragment}
+ * and {@link android.support.v4.app.FragmentActivity FragmentActivity} classes implement
+ * {@link LifecycleOwner} interface which has the {@link LifecycleOwner#getLifecycle()
+ * getLifecycle} method to access the Lifecycle. You can also implement {@link LifecycleOwner}
+ * in your own classes.
+ * <p>
+ * {@link Event#ON_CREATE}, {@link Event#ON_START}, {@link Event#ON_RESUME} events in this class
+ * are dispatched <b>after</b> the {@link LifecycleOwner}'s related method returns.
+ * {@link Event#ON_PAUSE}, {@link Event#ON_STOP}, {@link Event#ON_DESTROY} events in this class
+ * are dispatched <b>before</b> the {@link LifecycleOwner}'s related method is called.
+ * For instance, {@link Event#ON_START} will be dispatched after
+ * {@link android.app.Activity#onStart onStart} returns, {@link Event#ON_STOP} will be dispatched
+ * before {@link android.app.Activity#onStop onStop} is called.
+ * This gives you certain guarantees on which state the owner is in.
+ * <p>
+ * Lifecycle events are observed using annotations.
+ * <pre>
+ * class TestObserver implements LifecycleObserver {
+ *   {@literal @}OnLifecycleEvent(ON_STOP)
+ *   void onStopped() {}
+ * }
+ * </pre>
+ * <p>
+ * Multiple methods can observe the same event.
+ * <pre>
+ * class TestObserver implements LifecycleObserver {
+ *   {@literal @}OnLifecycleEvent(ON_STOP)
+ *   void onStoppedFirst() {}
+ *   {@literal @}OnLifecycleEvent(ON_STOP)
+ *   void onStoppedSecond() {}
+ * }
+ * </pre>
+ * <p>
+ * Observer methods can receive zero or one argument.
+ * If used, the first argument must be of type {@link LifecycleOwner}.
+ * Methods annotated with {@link Event#ON_ANY} can receive the second argument, which must be
+ * of type {@link Event}.
+ * <pre>
+ * class TestObserver implements LifecycleObserver {
+ *   {@literal @}OnLifecycleEvent(ON_CREATE)
+ *   void onCreated(LifecycleOwner source) {}
+ *   {@literal @}OnLifecycleEvent(ON_ANY)
+ *   void onAny(LifecycleOwner source, Event event) {}
+ * }
+ * </pre>
+ * These additional parameters are provided to allow you to conveniently observe multiple providers
+ * and events without tracking them manually.
+ */
+public abstract class Lifecycle {
+    /**
+     * Adds a LifecycleObserver that will be notified when the LifecycleOwner changes
+     * state.
+     * <p>
+     * The given observer will be brought to the current state of the LifecycleOwner.
+     * For example, if the LifecycleOwner is in {@link State#STARTED} state, the given observer
+     * will receive {@link Event#ON_CREATE}, {@link Event#ON_START} events.
+     *
+     * @param observer The observer to notify.
+     */
+    @MainThread
+    public abstract void addObserver(LifecycleObserver observer);
+
+    /**
+     * Removes the given observer from the observers list.
+     * <p>
+     * If this method is called while a state change is being dispatched,
+     * <ul>
+     * <li>If the given observer has not yet received that event, it will not receive it.
+     * <li>If the given observer has more than 1 method that observes the currently dispatched
+     * event and at least one of them received the event, all of them will receive the event and
+     * the removal will happen afterwards.
+     * </ul>
+     *
+     * @param observer The observer to be removed.
+     */
+    @MainThread
+    public abstract void removeObserver(LifecycleObserver observer);
+
+    /**
+     * Returns the current state of the Lifecycle.
+     *
+     * @return The current state of the Lifecycle.
+     */
+    @MainThread
+    public abstract State getCurrentState();
+
+    @SuppressWarnings("WeakerAccess")
+    public enum Event {
+        /**
+         * Constant for onCreate event of the {@link LifecycleOwner}.
+         */
+        ON_CREATE,
+        /**
+         * Constant for onStart event of the {@link LifecycleOwner}.
+         */
+        ON_START,
+        /**
+         * Constant for onResume event of the {@link LifecycleOwner}.
+         */
+        ON_RESUME,
+        /**
+         * Constant for onPause event of the {@link LifecycleOwner}.
+         */
+        ON_PAUSE,
+        /**
+         * Constant for onStop event of the {@link LifecycleOwner}.
+         */
+        ON_STOP,
+        /**
+         * Constant for onDestroy event of the {@link LifecycleOwner}.
+         */
+        ON_DESTROY,
+        /**
+         * An {@link Event Event} constant that can be used to match all events.
+         */
+        ON_ANY
+    }
+
+    /**
+     * Lifecycle states. You can consider the states as the nodes in a graph and
+     * {@link Event}s as the edges between these nodes.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public enum State {
+        /**
+         * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
+         * any more events. For instance, for an {@link android.app.Activity}, this state is reached
+         * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
+         */
+        DESTROYED,
+
+        /**
+         * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
+         * the state when it is constructed but has not received
+         * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
+         */
+        INITIALIZED,
+
+        /**
+         * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
+         * is reached in two cases:
+         * <ul>
+         *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
+         *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
+         * </ul>
+         */
+        CREATED,
+
+        /**
+         * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
+         * is reached in two cases:
+         * <ul>
+         *     <li>after {@link android.app.Activity#onStart() onStart} call;
+         *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
+         * </ul>
+         */
+        STARTED,
+
+        /**
+         * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
+         * is reached after {@link android.app.Activity#onResume() onResume} is called.
+         */
+        RESUMED;
+
+        /**
+         * Compares if this State is greater or equal to the given {@code state}.
+         *
+         * @param state State to compare with
+         * @return true if this State is greater or equal to the given {@code state}
+         */
+        public boolean isAtLeast(State state) {
+            return compareTo(state) >= 0;
+        }
+    }
+}
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/LifecycleObserver.java b/lifecycle/common/src/main/java/android/arch/lifecycle/LifecycleObserver.java
new file mode 100644
index 0000000..94670bf
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/LifecycleObserver.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * Marks a class as a LifecycleObserver. It does not have any methods, instead, relies on
+ * {@link OnLifecycleEvent} annotated methods.
+ * <p>
+ * @see Lifecycle Lifecycle - for samples and usage patterns.
+ */
+@SuppressWarnings("WeakerAccess")
+public interface LifecycleObserver {
+
+}
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/LifecycleOwner.java b/lifecycle/common/src/main/java/android/arch/lifecycle/LifecycleOwner.java
new file mode 100644
index 0000000..934cf3a
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/LifecycleOwner.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * A class that has an Android lifecycle. These events can be used by custom components to
+ * handle lifecycle changes without implementing any code inside the Activity or the Fragment.
+ *
+ * @see Lifecycle
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public interface LifecycleOwner {
+    /**
+     * Returns the Lifecycle of the provider.
+     *
+     * @return The lifecycle of the provider.
+     */
+    Lifecycle getLifecycle();
+}
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycling.java b/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycling.java
new file mode 100644
index 0000000..295f731
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycling.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Internal class to handle lifecycle conversion etc.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class Lifecycling {
+    private static Constructor<? extends GenericLifecycleObserver> sREFLECTIVE;
+
+    static {
+        try {
+            sREFLECTIVE = ReflectiveGenericLifecycleObserver.class
+                    .getDeclaredConstructor(Object.class);
+        } catch (NoSuchMethodException ignored) {
+
+        }
+    }
+
+    private static Map<Class, Constructor<? extends GenericLifecycleObserver>> sCallbackCache =
+            new HashMap<>();
+
+    @NonNull
+    static GenericLifecycleObserver getCallback(Object object) {
+        if (object instanceof GenericLifecycleObserver) {
+            return (GenericLifecycleObserver) object;
+        }
+        //noinspection TryWithIdenticalCatches
+        try {
+            final Class<?> klass = object.getClass();
+            Constructor<? extends GenericLifecycleObserver> cachedConstructor = sCallbackCache.get(
+                    klass);
+            if (cachedConstructor != null) {
+                return cachedConstructor.newInstance(object);
+            }
+            cachedConstructor = getGeneratedAdapterConstructor(klass);
+            if (cachedConstructor != null) {
+                if (!cachedConstructor.isAccessible()) {
+                    cachedConstructor.setAccessible(true);
+                }
+            } else {
+                cachedConstructor = sREFLECTIVE;
+            }
+            sCallbackCache.put(klass, cachedConstructor);
+            return cachedConstructor.newInstance(object);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    private static Constructor<? extends GenericLifecycleObserver> getGeneratedAdapterConstructor(
+            Class<?> klass) {
+        final String fullPackage = klass.getPackage().getName();
+
+        String name = klass.getCanonicalName();
+        // anonymous class bug:35073837
+        if (name == null) {
+            return null;
+        }
+        final String adapterName = getAdapterName(fullPackage.isEmpty() ? name :
+                name.substring(fullPackage.length() + 1));
+        try {
+            @SuppressWarnings("unchecked")
+            final Class<? extends GenericLifecycleObserver> aClass =
+                    (Class<? extends GenericLifecycleObserver>) Class.forName(
+                            fullPackage.isEmpty() ? adapterName : fullPackage + "." + adapterName);
+            return aClass.getDeclaredConstructor(klass);
+        } catch (ClassNotFoundException e) {
+            final Class<?> superclass = klass.getSuperclass();
+            if (superclass != null) {
+                return getGeneratedAdapterConstructor(superclass);
+            }
+        } catch (NoSuchMethodException e) {
+            // this should not happen
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+
+    static String getAdapterName(String className) {
+        return className.replace(".", "_") + "_LifecycleAdapter";
+    }
+}
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/OnLifecycleEvent.java b/lifecycle/common/src/main/java/android/arch/lifecycle/OnLifecycleEvent.java
new file mode 100644
index 0000000..9a86b0c
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/OnLifecycleEvent.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@SuppressWarnings("unused")
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface OnLifecycleEvent {
+    Lifecycle.Event value();
+}
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java b/lifecycle/common/src/main/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
new file mode 100644
index 0000000..7de32db
--- /dev/null
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
@@ -0,0 +1,224 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.arch.lifecycle.Lifecycle.Event;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * An internal implementation of {@link GenericLifecycleObserver} that relies on reflection.
+ */
+class ReflectiveGenericLifecycleObserver implements GenericLifecycleObserver {
+    private final Object mWrapped;
+    private final CallbackInfo mInfo;
+    @SuppressWarnings("WeakerAccess")
+    static final Map<Class, CallbackInfo> sInfoCache = new HashMap<>();
+
+    ReflectiveGenericLifecycleObserver(Object wrapped) {
+        mWrapped = wrapped;
+        mInfo = getInfo(mWrapped.getClass());
+    }
+
+    @Override
+    public void onStateChanged(LifecycleOwner source, Event event) {
+        invokeCallbacks(mInfo, source, event);
+    }
+
+    private void invokeMethodsForEvent(List<MethodReference> handlers, LifecycleOwner source,
+            Event event) {
+        if (handlers != null) {
+            for (int i = handlers.size() - 1; i >= 0; i--) {
+                MethodReference reference = handlers.get(i);
+                invokeCallback(reference, source, event);
+            }
+        }
+    }
+
+    @SuppressWarnings("ConstantConditions")
+    private void invokeCallbacks(CallbackInfo info, LifecycleOwner source, Event event) {
+        invokeMethodsForEvent(info.mEventToHandlers.get(event), source, event);
+        invokeMethodsForEvent(info.mEventToHandlers.get(Event.ON_ANY), source, event);
+    }
+
+    private void invokeCallback(MethodReference reference, LifecycleOwner source, Event event) {
+        //noinspection TryWithIdenticalCatches
+        try {
+            switch (reference.mCallType) {
+                case CALL_TYPE_NO_ARG:
+                    reference.mMethod.invoke(mWrapped);
+                    break;
+                case CALL_TYPE_PROVIDER:
+                    reference.mMethod.invoke(mWrapped, source);
+                    break;
+                case CALL_TYPE_PROVIDER_WITH_EVENT:
+                    reference.mMethod.invoke(mWrapped, source, event);
+                    break;
+            }
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException("Failed to call observer method", e.getCause());
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Object getReceiver() {
+        return mWrapped;
+    }
+
+    private static CallbackInfo getInfo(Class klass) {
+        CallbackInfo existing = sInfoCache.get(klass);
+        if (existing != null) {
+            return existing;
+        }
+        existing = createInfo(klass);
+        return existing;
+    }
+
+    private static void verifyAndPutHandler(Map<MethodReference, Event> handlers,
+            MethodReference newHandler, Event newEvent, Class klass) {
+        Event event = handlers.get(newHandler);
+        if (event != null && newEvent != event) {
+            Method method = newHandler.mMethod;
+            throw new IllegalArgumentException(
+                    "Method " + method.getName() + " in " + klass.getName()
+                            + " already declared with different @OnLifecycleEvent value: previous"
+                            + " value " + event + ", new value " + newEvent);
+        }
+        if (event == null) {
+            handlers.put(newHandler, newEvent);
+        }
+    }
+
+    private static CallbackInfo createInfo(Class klass) {
+        Class superclass = klass.getSuperclass();
+        Map<MethodReference, Event> handlerToEvent = new HashMap<>();
+        if (superclass != null) {
+            CallbackInfo superInfo = getInfo(superclass);
+            if (superInfo != null) {
+                handlerToEvent.putAll(superInfo.mHandlerToEvent);
+            }
+        }
+
+        Method[] methods = klass.getDeclaredMethods();
+
+        Class[] interfaces = klass.getInterfaces();
+        for (Class intrfc : interfaces) {
+            for (Entry<MethodReference, Event> entry : getInfo(intrfc).mHandlerToEvent.entrySet()) {
+                verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass);
+            }
+        }
+
+        for (Method method : methods) {
+            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
+            if (annotation == null) {
+                continue;
+            }
+            Class<?>[] params = method.getParameterTypes();
+            int callType = CALL_TYPE_NO_ARG;
+            if (params.length > 0) {
+                callType = CALL_TYPE_PROVIDER;
+                if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
+                    throw new IllegalArgumentException(
+                            "invalid parameter type. Must be one and instanceof LifecycleOwner");
+                }
+            }
+            Event event = annotation.value();
+
+            if (params.length > 1) {
+                callType = CALL_TYPE_PROVIDER_WITH_EVENT;
+                if (!params[1].isAssignableFrom(Event.class)) {
+                    throw new IllegalArgumentException(
+                            "invalid parameter type. second arg must be an event");
+                }
+                if (event != Event.ON_ANY) {
+                    throw new IllegalArgumentException(
+                            "Second arg is supported only for ON_ANY value");
+                }
+            }
+            if (params.length > 2) {
+                throw new IllegalArgumentException("cannot have more than 2 params");
+            }
+            MethodReference methodReference = new MethodReference(callType, method);
+            verifyAndPutHandler(handlerToEvent, methodReference, event, klass);
+        }
+        CallbackInfo info = new CallbackInfo(handlerToEvent);
+        sInfoCache.put(klass, info);
+        return info;
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    static class CallbackInfo {
+        final Map<Event, List<MethodReference>> mEventToHandlers;
+        final Map<MethodReference, Event> mHandlerToEvent;
+
+        CallbackInfo(Map<MethodReference, Event> handlerToEvent) {
+            mHandlerToEvent = handlerToEvent;
+            mEventToHandlers = new HashMap<>();
+            for (Entry<MethodReference, Event> entry : handlerToEvent.entrySet()) {
+                Event event = entry.getValue();
+                List<MethodReference> methodReferences = mEventToHandlers.get(event);
+                if (methodReferences == null) {
+                    methodReferences = new ArrayList<>();
+                    mEventToHandlers.put(event, methodReferences);
+                }
+                methodReferences.add(entry.getKey());
+            }
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    static class MethodReference {
+        final int mCallType;
+        final Method mMethod;
+
+        MethodReference(int callType, Method method) {
+            mCallType = callType;
+            mMethod = method;
+            mMethod.setAccessible(true);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            MethodReference that = (MethodReference) o;
+            return mCallType == that.mCallType && mMethod.getName().equals(that.mMethod.getName());
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * mCallType + mMethod.getName().hashCode();
+        }
+    }
+
+    private static final int CALL_TYPE_NO_ARG = 0;
+    private static final int CALL_TYPE_PROVIDER = 1;
+    private static final int CALL_TYPE_PROVIDER_WITH_EVENT = 2;
+}
diff --git a/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java b/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
new file mode 100644
index 0000000..68f04e8
--- /dev/null
+++ b/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
@@ -0,0 +1,365 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+import static android.arch.lifecycle.Lifecycle.State.INITIALIZED;
+import static android.arch.lifecycle.Lifecycle.State.RESUMED;
+import static android.arch.lifecycle.Lifecycle.State.STARTED;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Matchers;
+
+@RunWith(JUnit4.class)
+public class ReflectiveGenericLifecycleObserverTest {
+    private LifecycleOwner mOwner;
+    private Lifecycle mLifecycle;
+
+    @Before
+    public void initMocks() {
+        mOwner = mock(LifecycleOwner.class);
+        mLifecycle = mock(Lifecycle.class);
+        when(mOwner.getLifecycle()).thenReturn(mLifecycle);
+    }
+
+    @Test
+    public void anyState() {
+        AnyStateListener obj = mock(AnyStateListener.class);
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        when(mLifecycle.getCurrentState()).thenReturn(STARTED);
+        observer.onStateChanged(mOwner, ON_CREATE);
+        verify(obj).onAnyState(mOwner, ON_CREATE);
+        reset(obj);
+
+        observer.onStateChanged(mOwner, ON_START);
+        verify(obj).onAnyState(mOwner, ON_START);
+        reset(obj);
+
+        observer.onStateChanged(mOwner, ON_RESUME);
+        verify(obj).onAnyState(mOwner, ON_RESUME);
+        reset(obj);
+
+        observer.onStateChanged(mOwner, ON_PAUSE);
+        verify(obj).onAnyState(mOwner, ON_PAUSE);
+        reset(obj);
+
+        observer.onStateChanged(mOwner, ON_STOP);
+        verify(obj).onAnyState(mOwner, ON_STOP);
+        reset(obj);
+
+        observer.onStateChanged(mOwner, ON_DESTROY);
+        verify(obj).onAnyState(mOwner, ON_DESTROY);
+        reset(obj);
+    }
+
+    private static class AnyStateListener implements LifecycleObserver {
+        @OnLifecycleEvent(ON_ANY)
+        void onAnyState(LifecycleOwner owner, Lifecycle.Event event) {
+
+        }
+    }
+
+    @Test
+    public void singleMethod() {
+        CreatedStateListener obj = mock(CreatedStateListener.class);
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        when(mLifecycle.getCurrentState()).thenReturn(CREATED);
+        observer.onStateChanged(mOwner, ON_CREATE);
+        verify(obj).onCreated();
+        verify(obj).onCreated(mOwner);
+    }
+
+    private static class CreatedStateListener implements LifecycleObserver {
+        @OnLifecycleEvent(ON_CREATE)
+        void onCreated() {
+
+        }
+        @SuppressWarnings("UnusedParameters")
+        @OnLifecycleEvent(ON_CREATE)
+        void onCreated(LifecycleOwner provider) {
+
+        }
+    }
+
+    @Test
+    public void eachEvent() {
+        AllMethodsListener obj = mock(AllMethodsListener.class);
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        when(mLifecycle.getCurrentState()).thenReturn(CREATED);
+
+        observer.onStateChanged(mOwner, ON_CREATE);
+        verify(obj).created();
+        reset(obj);
+
+        when(mLifecycle.getCurrentState()).thenReturn(STARTED);
+        observer.onStateChanged(mOwner, ON_START);
+        verify(obj).started();
+        reset(obj);
+
+        when(mLifecycle.getCurrentState()).thenReturn(RESUMED);
+        observer.onStateChanged(mOwner, ON_RESUME);
+        verify(obj).resumed();
+        reset(obj);
+
+        when(mLifecycle.getCurrentState()).thenReturn(STARTED);
+        observer.onStateChanged(mOwner, ON_PAUSE);
+        verify(obj).paused();
+        reset(obj);
+
+        when(mLifecycle.getCurrentState()).thenReturn(CREATED);
+        observer.onStateChanged(mOwner, ON_STOP);
+        verify(obj).stopped();
+        reset(obj);
+
+        when(mLifecycle.getCurrentState()).thenReturn(INITIALIZED);
+        observer.onStateChanged(mOwner, ON_DESTROY);
+        verify(obj).destroyed();
+        reset(obj);
+    }
+
+
+    private static class AllMethodsListener implements LifecycleObserver {
+        @OnLifecycleEvent(ON_CREATE)
+        void created() {}
+
+        @OnLifecycleEvent(ON_START)
+        void started() {}
+
+        @OnLifecycleEvent(ON_RESUME)
+        void resumed() {}
+
+        @OnLifecycleEvent(ON_PAUSE)
+        void paused() {}
+
+        @OnLifecycleEvent(ON_STOP)
+        void stopped() {}
+
+        @OnLifecycleEvent(ON_DESTROY)
+        void destroyed() {
+        }
+    }
+
+    @Test
+    public void testFailingObserver() {
+        class UnprecedentedError extends Error {
+        }
+
+        LifecycleObserver obj = new LifecycleObserver() {
+            @OnLifecycleEvent(ON_START)
+            void started() {
+                throw new UnprecedentedError();
+            }
+        };
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        try {
+            observer.onStateChanged(mOwner, ON_START);
+        } catch (Exception e) {
+            assertThat("exception cause is wrong",
+                    e.getCause() instanceof UnprecedentedError);
+        }
+    }
+
+    @Test
+    public void testPrivateObserverMethods() {
+        class ObserverWithPrivateMethod implements LifecycleObserver {
+            boolean mCalled = false;
+            @OnLifecycleEvent(ON_START)
+            private void started() {
+                mCalled = true;
+            }
+        }
+
+        ObserverWithPrivateMethod obj = mock(ObserverWithPrivateMethod.class);
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        observer.onStateChanged(mOwner, ON_START);
+        assertThat(obj.mCalled, is(true));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWrongFirstParam1() {
+        LifecycleObserver observer = new LifecycleObserver() {
+            @OnLifecycleEvent(ON_START)
+            private void started(Lifecycle.Event e) {
+            }
+        };
+        new ReflectiveGenericLifecycleObserver(observer);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWrongFirstParam2() {
+        LifecycleObserver observer = new LifecycleObserver() {
+            @OnLifecycleEvent(ON_ANY)
+            private void started(Lifecycle l, Lifecycle.Event e) {
+            }
+        };
+        new ReflectiveGenericLifecycleObserver(observer);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWrongSecondParam() {
+        LifecycleObserver observer = new LifecycleObserver() {
+            @OnLifecycleEvent(ON_START)
+            private void started(LifecycleOwner owner, Lifecycle l) {
+            }
+        };
+        new ReflectiveGenericLifecycleObserver(observer);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testThreeParams() {
+        LifecycleObserver observer = new LifecycleObserver() {
+            @OnLifecycleEvent(ON_ANY)
+            private void started(LifecycleOwner owner, Lifecycle.Event e, int i) {
+            }
+        };
+        new ReflectiveGenericLifecycleObserver(observer);
+    }
+
+    class BaseClass1 implements LifecycleObserver {
+        @OnLifecycleEvent(ON_START)
+        void foo(LifecycleOwner owner) {
+        }
+    }
+
+    class DerivedClass1 extends BaseClass1 {
+        @OnLifecycleEvent(ON_STOP)
+        void foo(LifecycleOwner owner) {
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidSuper1() {
+        new ReflectiveGenericLifecycleObserver(new DerivedClass1());
+    }
+
+    class BaseClass2 implements LifecycleObserver {
+        @OnLifecycleEvent(ON_START)
+        void foo(LifecycleOwner owner) {
+        }
+    }
+
+    class DerivedClass2 extends BaseClass1 {
+        @OnLifecycleEvent(ON_STOP)
+        void foo() {
+        }
+    }
+
+    @Test
+    public void testValidSuper1() {
+        DerivedClass2 obj = mock(DerivedClass2.class);
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        observer.onStateChanged(mock(LifecycleOwner.class), ON_START);
+        verify(obj).foo(Matchers.<LifecycleOwner>any());
+        verify(obj, never()).foo();
+        reset(obj);
+        observer.onStateChanged(mock(LifecycleOwner.class), ON_STOP);
+        verify(obj).foo();
+        verify(obj, never()).foo(Matchers.<LifecycleOwner>any());
+    }
+
+    class BaseClass3 implements LifecycleObserver {
+        @OnLifecycleEvent(ON_START)
+        void foo(LifecycleOwner owner) {
+        }
+    }
+
+    interface Interface3 extends LifecycleObserver {
+        @OnLifecycleEvent(ON_STOP)
+        void foo(LifecycleOwner owner);
+    }
+
+    class DerivedClass3 extends BaseClass3 implements Interface3 {
+        @Override
+        public void foo(LifecycleOwner owner) {
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidSuper2() {
+        new ReflectiveGenericLifecycleObserver(new DerivedClass3());
+    }
+
+    class BaseClass4 implements LifecycleObserver {
+        @OnLifecycleEvent(ON_START)
+        void foo(LifecycleOwner owner) {
+        }
+    }
+
+    interface Interface4 extends LifecycleObserver {
+        @OnLifecycleEvent(ON_START)
+        void foo(LifecycleOwner owner);
+    }
+
+    class DerivedClass4 extends BaseClass4 implements Interface4 {
+        @Override
+        @OnLifecycleEvent(ON_START)
+        public void foo(LifecycleOwner owner) {
+        }
+
+        @OnLifecycleEvent(ON_START)
+        public void foo() {
+        }
+    }
+
+    @Test
+    public void testValidSuper2() {
+        DerivedClass4 obj = mock(DerivedClass4.class);
+        ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
+        observer.onStateChanged(mock(LifecycleOwner.class), ON_START);
+        verify(obj).foo(Matchers.<LifecycleOwner>any());
+        verify(obj).foo();
+    }
+
+    interface InterfaceStart extends LifecycleObserver {
+        @OnLifecycleEvent(ON_START)
+        void foo(LifecycleOwner owner);
+    }
+
+    interface InterfaceStop extends LifecycleObserver {
+        @OnLifecycleEvent(ON_STOP)
+        void foo(LifecycleOwner owner);
+    }
+
+    class DerivedClass5 implements InterfaceStart, InterfaceStop {
+        @Override
+        public void foo(LifecycleOwner owner) {
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidSuper3() {
+        new ReflectiveGenericLifecycleObserver(new DerivedClass5());
+    }
+}
diff --git a/lifecycle/compiler/build.gradle b/lifecycle/compiler/build.gradle
new file mode 100644
index 0000000..f0d2a60
--- /dev/null
+++ b/lifecycle/compiler/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'kotlin'
+apply plugin: 'maven'
+apply plugin: 'checkstyle'
+
+sourceSets {
+    test.java.srcDirs += 'src/tests/kotlin'
+}
+
+// Temporary hack to stop AS to adding two guavas into test's classpath
+configurations.all {
+    resolutionStrategy {
+        force libs.guava
+    }
+}
+
+dependencies {
+    compile project(":lifecycle:common")
+    compile libs.kotlin.stdlib
+    compile libs.auto_common
+    compile libs.javapoet
+    testCompile libs.google_compile_testing
+    testCompile files(org.gradle.internal.jvm.Jvm.current().getToolsJar())
+}
+
+archivesBaseName = "compiler"
+
+createKotlinCheckstyle(project)
diff --git a/lifecycle/compiler/reset_results.py b/lifecycle/compiler/reset_results.py
new file mode 100644
index 0000000..994f20d
--- /dev/null
+++ b/lifecycle/compiler/reset_results.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+#
+# 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.
+#
+
+""" Run this script when you need to update test-data/expected."""
+import sys
+
+if len(sys.argv) != 2:
+    print("You need to specify the only one param: file with test failures")
+    sys.exit()
+
+with open(sys.argv[1]) as f:
+    content = f.readlines()
+
+with open("src/tests/test-data/expected/license.txt") as license:
+    licenseLines = license.readlines()
+
+
+def writeToFile(fileName, lines):
+    file = open("src/tests/test-data/expected/" + fileName, "w")
+    for line in lines:
+        file.write(line)
+
+    file.close()
+
+
+state = 0
+filename = ""
+expected = "Expected file:"
+fileLines = []
+for line in content:
+    if (state == 0 and line.startswith(expected)):
+        state = 1
+        filename  = line[line.rfind("/") + 1 : len(line) - 2]
+        print(filename)
+
+    if state == 1 and line.startswith("Actual Source:"):
+        state = 2
+        continue
+
+    if state == 2:
+        fileLines.append(line)
+
+    if state == 2 and line.rstrip() == "}":
+        writeToFile(filename, licenseLines + fileLines[1:])
+        state = 0
+        fileLines = []
+
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt
new file mode 100644
index 0000000..8dac863
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+object ErrorMessages {
+    const val TOO_MANY_ARGS = "callback method cannot have more than 2 parameters"
+    const val TOO_MANY_ARGS_NOT_ON_ANY = "only callback annotated with ON_ANY " +
+            "can have 2 parameters"
+    const val INVALID_SECOND_ARGUMENT = "2nd argument of a callback method" +
+            " must be Lifecycle.Event and represent the current event"
+    const val INVALID_FIRST_ARGUMENT = "1st argument of a callback method must be " +
+            "a LifecycleOwner which represents the source of the event"
+    const val INVALID_METHOD_MODIFIER = "method marked with OnLifecycleEvent annotation can " +
+            "not be private"
+    const val INVALID_CLASS_MODIFIER = "class containing OnLifecycleEvent methods can not be " +
+            "private"
+    const val INVALID_STATE_OVERRIDE_METHOD = "overridden method must handle the same " +
+            "onState changes as original method"
+    const val INVALID_ENCLOSING_ELEMENT =
+            "Parent of OnLifecycleEvent should be a class or interface"
+    const val INVALID_ANNOTATED_ELEMENT = "OnLifecycleEvent can only be added to methods"
+
+}
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/LifecycleProcessor.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/LifecycleProcessor.kt
new file mode 100644
index 0000000..81bcc95
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/LifecycleProcessor.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.annotation.processing.SupportedAnnotationTypes
+import javax.annotation.processing.SupportedSourceVersion
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.TypeElement
+
+@SupportedAnnotationTypes("android.arch.lifecycle.OnLifecycleEvent")
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+class LifecycleProcessor : AbstractProcessor() {
+    override fun process(annotations: MutableSet<out TypeElement>,
+                         roundEnv: RoundEnvironment): Boolean {
+        val input = collectAndVerifyInput(processingEnv, roundEnv)
+        writeModels(transformToOutput(processingEnv, input), processingEnv.filer)
+        return true
+    }
+}
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/elements_ext.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/elements_ext.kt
new file mode 100644
index 0000000..eedb3af
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/elements_ext.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import com.google.auto.common.MoreElements
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.PackageElement
+
+fun Element.getPackage(): PackageElement = MoreElements.getPackage(this)
+
+fun Element.getPackageQName() = getPackage().qualifiedName.toString()
+
+fun ExecutableElement.name() = simpleName.toString()
+
+fun ExecutableElement.isPackagePrivate() = !modifiers.any {
+    it == Modifier.PUBLIC || it == Modifier.PROTECTED || it == Modifier.PRIVATE
+}
+
+fun ExecutableElement.isProtected() = modifiers.contains(Modifier.PROTECTED)
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt
new file mode 100644
index 0000000..5183df5
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import android.arch.lifecycle.model.EventMethod
+import android.arch.lifecycle.model.LifecycleObserverInfo
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.annotation.processing.ProcessingEnvironment
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.tools.Diagnostic
+
+fun collectAndVerifyInput(processingEnv: ProcessingEnvironment,
+                          roundEnv: RoundEnvironment): Map<TypeElement, LifecycleObserverInfo> {
+    val validator = Validator(processingEnv)
+
+    return roundEnv.getElementsAnnotatedWith(OnLifecycleEvent::class.java).map { elem ->
+        if (elem.kind != ElementKind.METHOD) {
+            validator.printErrorMessage(ErrorMessages.INVALID_ANNOTATED_ELEMENT, elem)
+            null
+        } else {
+            val enclosingElement = elem.enclosingElement
+            val onState = elem.getAnnotation(OnLifecycleEvent::class.java)
+            val method = MoreElements.asExecutable(elem)
+            if (validator.validateClass(enclosingElement)
+                    && validator.validateMethod(method, onState.value)) {
+                EventMethod(method, onState, MoreElements.asType(enclosingElement))
+            } else {
+                null
+            }
+        }
+    }
+            .filterNotNull()
+            .groupBy { MoreElements.asType(it.method.enclosingElement) }
+            .mapValues { entry -> LifecycleObserverInfo(entry.key, entry.value) }
+
+}
+
+class Validator(val processingEnv: ProcessingEnvironment) {
+
+    fun printErrorMessage(msg: CharSequence, elem: Element) {
+        processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg, elem)
+    }
+
+    fun validateParam(param: VariableElement,
+                      expectedType: Class<*>, errorMsg: String): Boolean {
+        if (!MoreTypes.isTypeOf(expectedType, param.asType())) {
+            printErrorMessage(errorMsg, param)
+            return false
+        }
+        return true
+    }
+
+    fun validateMethod(method: ExecutableElement, event: Lifecycle.Event): Boolean {
+        if (Modifier.PRIVATE in method.modifiers) {
+            printErrorMessage(ErrorMessages.INVALID_METHOD_MODIFIER, method)
+            return false
+        }
+        val params = method.parameters
+        if ((params.size > 2)) {
+            printErrorMessage(ErrorMessages.TOO_MANY_ARGS, method)
+            return false
+        }
+
+        if (params.size == 2 && event != Lifecycle.Event.ON_ANY) {
+            printErrorMessage(ErrorMessages.TOO_MANY_ARGS_NOT_ON_ANY, method)
+            return false
+        }
+
+        if (params.size == 2 && !validateParam(params[1], Lifecycle.Event::class.java,
+                ErrorMessages.INVALID_SECOND_ARGUMENT)) {
+            return false
+        }
+
+        if (params.size > 0) {
+            return validateParam(params[0], LifecycleOwner::class.java,
+                    ErrorMessages.INVALID_FIRST_ARGUMENT)
+        }
+        return true
+    }
+
+    fun validateClass(classElement: Element): Boolean {
+        if (classElement.kind != ElementKind.CLASS && classElement.kind != ElementKind.INTERFACE) {
+            printErrorMessage(ErrorMessages.INVALID_ENCLOSING_ELEMENT, classElement)
+            return false
+        }
+        if (Modifier.PRIVATE in classElement.modifiers) {
+            printErrorMessage(ErrorMessages.INVALID_CLASS_MODIFIER, classElement)
+            return false
+        }
+        return true
+    }
+}
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/AdapterClass.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/AdapterClass.kt
new file mode 100644
index 0000000..1e76fa8
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/AdapterClass.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.model
+
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.TypeElement
+
+data class AdapterClass(val type: TypeElement,
+                        val calls: List<EventMethodCall>,
+                        val syntheticMethods: Set<ExecutableElement>)
\ No newline at end of file
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/EventMethod.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/EventMethod.kt
new file mode 100644
index 0000000..a3d4712
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/EventMethod.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.model
+
+import android.arch.lifecycle.OnLifecycleEvent
+import android.arch.lifecycle.getPackageQName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.TypeElement
+
+data class EventMethod(val method: ExecutableElement,
+                       val onLifecycleEvent: OnLifecycleEvent,
+                       val type: TypeElement) {
+
+    fun packageName() = type.getPackageQName()
+}
+
+data class EventMethodCall(val method: EventMethod, val syntheticAccess: TypeElement? = null)
\ No newline at end of file
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/LifecycleObserverInfo.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/LifecycleObserverInfo.kt
new file mode 100644
index 0000000..d8bc364
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/model/LifecycleObserverInfo.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.model
+
+import javax.lang.model.element.TypeElement
+
+data class LifecycleObserverInfo(
+        val type: TypeElement,
+        val methods: List<EventMethod>)
\ No newline at end of file
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt
new file mode 100644
index 0000000..66fabf7
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import android.arch.lifecycle.model.AdapterClass
+import android.arch.lifecycle.model.EventMethod
+import android.arch.lifecycle.model.EventMethodCall
+import android.arch.lifecycle.model.LifecycleObserverInfo
+import com.google.auto.common.MoreTypes
+import com.google.common.collect.HashMultimap
+import java.util.LinkedList
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.NoType
+import javax.lang.model.type.TypeMirror
+import javax.tools.Diagnostic
+
+
+private fun superObservers(world: Map<TypeElement, LifecycleObserverInfo>,
+                           observer: LifecycleObserverInfo): List<LifecycleObserverInfo> {
+    val stack = LinkedList<TypeMirror>()
+    stack += observer.type.interfaces.reversed()
+    stack += observer.type.superclass
+    val result = mutableListOf<LifecycleObserverInfo>()
+    while (stack.isNotEmpty()) {
+        val typeMirror = stack.removeLast()
+        if (typeMirror is NoType) {
+            continue
+        }
+        val type = MoreTypes.asTypeElement(typeMirror)
+        val currentObserver = world[type]
+        if (currentObserver != null) {
+            result.add(currentObserver)
+        } else {
+            stack += type.interfaces.reversed()
+            stack += type.superclass
+        }
+    }
+    return result
+}
+
+private fun mergeAndVerifyMethods(processingEnv: ProcessingEnvironment,
+                                  type: TypeElement,
+                                  classMethods: List<EventMethod>,
+                                  parentMethods: List<EventMethod>): List<EventMethod> {
+    // need to update parent methods like that because:
+    // 1. visibility can be expanded
+    // 2. we want to preserve order
+    val updatedParentMethods = parentMethods.map { parentMethod ->
+        val overrideMethod = classMethods.find { (method) ->
+            processingEnv.elementUtils.overrides(method, parentMethod.method, type)
+        }
+        if (overrideMethod != null) {
+            if (overrideMethod.onLifecycleEvent != parentMethod.onLifecycleEvent) {
+                processingEnv.messager.printMessage(Diagnostic.Kind.ERROR,
+                        ErrorMessages.INVALID_STATE_OVERRIDE_METHOD, overrideMethod.method)
+            }
+            overrideMethod
+        } else {
+            parentMethod
+        }
+    }
+    return updatedParentMethods + classMethods.filterNot { updatedParentMethods.contains(it) }
+}
+
+fun flattenObservers(processingEnv: ProcessingEnvironment,
+                     world: Map<TypeElement, LifecycleObserverInfo>): List<LifecycleObserverInfo> {
+    val flattened: MutableMap<LifecycleObserverInfo, LifecycleObserverInfo> = mutableMapOf()
+    val superObservers = world.mapValues { superObservers(world, it.value) }
+
+    fun traverse(observer: LifecycleObserverInfo) {
+        if (observer in flattened) {
+            return
+        }
+        val observers = superObservers[observer.type]!!
+        if (observers.isEmpty()) {
+            flattened[observer] = observer
+            return
+        }
+        observers.filter { it !in flattened }.forEach(::traverse)
+        val methods = observers
+                .map(flattened::get)
+                .fold(emptyList<EventMethod>()) { list, parentObserver ->
+                    mergeAndVerifyMethods(processingEnv, observer.type, parentObserver!!.methods, list)
+                }
+
+        flattened[observer] = LifecycleObserverInfo(observer.type,
+                mergeAndVerifyMethods(processingEnv, observer.type, observer.methods, methods))
+    }
+
+    world.values.forEach(::traverse)
+    return flattened.values.toList()
+}
+
+fun transformToOutput(processingEnv: ProcessingEnvironment,
+                      world: Map<TypeElement, LifecycleObserverInfo>): List<AdapterClass> {
+    val flatObservers = flattenObservers(processingEnv, world)
+    val syntheticMethods = HashMultimap.create<TypeElement, EventMethodCall>()
+    val adapterCalls = flatObservers.map { (type, methods) ->
+        val calls = methods.map { eventMethod ->
+            val executable = eventMethod.method
+            if (type.getPackageQName() != eventMethod.packageName()
+                    && (executable.isPackagePrivate() || executable.isProtected())) {
+                EventMethodCall(eventMethod, eventMethod.type)
+            } else {
+                EventMethodCall(eventMethod)
+            }
+        }
+        calls.filter { it.syntheticAccess != null }.forEach { eventMethod ->
+            syntheticMethods.put(eventMethod.method.type, eventMethod)
+        }
+        type to calls
+    }.toMap()
+
+    return adapterCalls.map { (type, calls) ->
+        val methods = syntheticMethods.get(type) ?: setOf()
+        val synthetic = methods.map { eventMethod -> eventMethod!!.method.method }.toSet()
+        AdapterClass(type, calls, synthetic)
+    }
+}
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/writer.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/writer.kt
new file mode 100644
index 0000000..1cd20ed
--- /dev/null
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/writer.kt
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import android.arch.lifecycle.model.AdapterClass
+import android.arch.lifecycle.model.EventMethodCall
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.annotation.processing.Filer
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+
+fun writeModels(infos: List<AdapterClass>, filer: Filer) {
+    infos.forEach({ adapter -> writeAdapter(adapter, filer) })
+}
+
+private val LIFECYCLE_OWNER = ClassName.get(LifecycleOwner::class.java)
+private val LIFECYCLE_EVENT = Lifecycle.Event::class.java
+
+private val T = "\$T"
+private val N = "\$N"
+private val L = "\$L"
+
+private fun writeAdapter(adapter: AdapterClass, filer: Filer) {
+    val ownerParam = ParameterSpec.builder(LIFECYCLE_OWNER, "owner").build()
+    val eventParam = ParameterSpec.builder(ClassName.get(LIFECYCLE_EVENT), "event").build()
+    val receiverName = "mReceiver"
+    val receiverField = FieldSpec.builder(ClassName.get(adapter.type), receiverName,
+            Modifier.FINAL).build()
+
+    val dispatchMethodBuilder = MethodSpec.methodBuilder("onStateChanged")
+            .returns(TypeName.VOID)
+            .addParameter(ownerParam)
+            .addParameter(eventParam)
+            .addModifiers(Modifier.PUBLIC)
+            .addAnnotation(Override::class.java)
+    val dispatchMethod = dispatchMethodBuilder.apply {
+        adapter.calls
+                .groupBy { (eventMethod) -> eventMethod.onLifecycleEvent.value }
+                .forEach { entry ->
+                    val event = entry.key
+                    val calls = entry.value
+                    if (event == Lifecycle.Event.ON_ANY) {
+                        writeMethodCalls(eventParam, calls, ownerParam, receiverField)
+                    } else {
+                        beginControlFlow("if ($N == $T.$L)", eventParam, LIFECYCLE_EVENT, event)
+                                .writeMethodCalls(eventParam, calls, ownerParam, receiverField)
+                        endControlFlow()
+                    }
+                }
+    }.build()
+
+    @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+    val getWrappedMethod = MethodSpec.methodBuilder("getReceiver")
+            .returns(ClassName.get(Object::class.java))
+            .addModifiers(Modifier.PUBLIC)
+            .addStatement("return $N", receiverField)
+            .build()
+
+    val receiverParam = ParameterSpec.builder(
+            ClassName.get(adapter.type), "receiver").build()
+
+    val syntheticMethods = adapter.syntheticMethods.map {
+        val method = MethodSpec.methodBuilder(syntheticName(it))
+                .returns(TypeName.VOID)
+                .addModifiers(Modifier.PUBLIC)
+                .addModifiers(Modifier.STATIC)
+                .addParameter(receiverParam)
+        if (it.parameters.size >= 1) {
+            method.addParameter(ownerParam)
+        }
+        if (it.parameters.size == 2) {
+            method.addParameter(eventParam)
+        }
+
+        val count = it.parameters.size
+        val paramString = generateParamString(count)
+        method.addStatement("$N.$L($paramString)", receiverParam, it.name(),
+                *takeParams(count, ownerParam, eventParam))
+        method.build()
+    }
+
+    val constructor = MethodSpec.constructorBuilder()
+            .addParameter(receiverParam)
+            .addStatement("this.$N = $N", receiverField, receiverParam)
+            .build()
+
+    val adapterName = getAdapterName(adapter.type)
+    val adapterTypeSpec = TypeSpec.classBuilder(adapterName)
+            .addModifiers(Modifier.PUBLIC)
+            .addSuperinterface(ClassName.get(GenericLifecycleObserver::class.java))
+            .addField(receiverField)
+            .addMethod(constructor)
+            .addMethod(dispatchMethod)
+            .addMethod(getWrappedMethod)
+            .addMethods(syntheticMethods)
+            .build()
+    JavaFile.builder(adapter.type.getPackageQName(), adapterTypeSpec)
+            .build().writeTo(filer)
+}
+
+private fun MethodSpec.Builder.writeMethodCalls(eventParam: ParameterSpec,
+                                                calls: List<EventMethodCall>,
+                                                ownerParam: ParameterSpec,
+                                                receiverField: FieldSpec) {
+    calls.forEach { (method, syntheticAccess) ->
+        val count = method.method.parameters.size
+        if (syntheticAccess == null) {
+            val paramString = generateParamString(count)
+            addStatement("$N.$L($paramString)", receiverField,
+                    method.method.name(),
+                    *takeParams(count, ownerParam, eventParam))
+
+        } else {
+            val originalType = syntheticAccess
+            val paramString = generateParamString(count + 1)
+            val className = ClassName.get(originalType.getPackageQName(),
+                    getAdapterName(originalType))
+            addStatement("$T.$L($paramString)", className,
+                    syntheticName(method.method),
+                    *takeParams(count + 1, receiverField, ownerParam,
+                            eventParam))
+        }
+    }
+}
+
+private fun syntheticName(method: ExecutableElement) = "__synthetic_" + method.simpleName
+
+private fun takeParams(count: Int, vararg params: Any) = params.take(count).toTypedArray()
+
+private fun generateParamString(count: Int) = (0..(count - 1)).joinToString(",") { N }
+
+private fun getAdapterName(type: TypeElement): String {
+    val packageElement = type.getPackage()
+    val qName = type.qualifiedName.toString()
+    val partialName = if (packageElement.isUnnamed) qName else qName.substring(
+            packageElement.qualifiedName.toString().length + 1)
+    return Lifecycling.getAdapterName(partialName)
+}
diff --git a/lifecycle/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/lifecycle/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..cdabd71
--- /dev/null
+++ b/lifecycle/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.arch.lifecycle.LifecycleProcessor
diff --git a/lifecycle/compiler/src/main/resources/NOTICE.txt b/lifecycle/compiler/src/main/resources/NOTICE.txt
new file mode 100644
index 0000000..28a4a72
--- /dev/null
+++ b/lifecycle/compiler/src/main/resources/NOTICE.txt
@@ -0,0 +1,1077 @@
+List of 3rd party licenses:
+-----------------------------------------------------------------------------
+* javapoet.jar (com.squareup:javapoet:1.8.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* kotlin-stdlib.jar (org.jetbrains.kotlin:kotlin-stdlib:1.1.1)
+
+ ****** NOTICE:
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Kotlin Compiler distribution.                 ==
+   =========================================================================
+
+   Kotlin Compiler
+   Copyright 2010-2015 JetBrains s.r.o and respective authors and developers
+
+ ****** LICENSE:
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * 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.
+ */
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* auto-common.jar (com.google.auto:auto-common:0.6)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* annotations.jar (org.jetbrains:annotations:13.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* guava.jar (com.google.guava:guava:18.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
diff --git a/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/InvalidCasesTest.kt b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/InvalidCasesTest.kt
new file mode 100644
index 0000000..3e502d3
--- /dev/null
+++ b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/InvalidCasesTest.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import android.arch.lifecycle.utils.processClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class InvalidCasesTest(val name: String, val errorMsg: String) {
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "failingCase({0})")
+        fun data() : Collection<Array<Any>> = listOf(
+                arrayOf<Any>("foo.InvalidFirstArg1", ErrorMessages.INVALID_FIRST_ARGUMENT),
+                arrayOf<Any>("foo.InvalidFirstArg2", ErrorMessages.INVALID_FIRST_ARGUMENT),
+                arrayOf<Any>("foo.InvalidSecondArg", ErrorMessages.INVALID_SECOND_ARGUMENT),
+                arrayOf<Any>("foo.TooManyArgs1", ErrorMessages.TOO_MANY_ARGS),
+                arrayOf<Any>("foo.TooManyArgs2", ErrorMessages.TOO_MANY_ARGS_NOT_ON_ANY),
+                arrayOf<Any>("foo.InvalidMethodModifier",
+                        ErrorMessages.INVALID_METHOD_MODIFIER),
+                arrayOf<Any>("foo.InvalidClassModifier", ErrorMessages.INVALID_CLASS_MODIFIER),
+                arrayOf<Any>("foo.InvalidInheritance1",
+                        ErrorMessages.INVALID_STATE_OVERRIDE_METHOD),
+                arrayOf<Any>("foo.InvalidInheritance2",
+                        ErrorMessages.INVALID_STATE_OVERRIDE_METHOD)
+        )
+    }
+
+    @Test
+    fun shouldFailWithError() {
+        processClass(name).failsToCompile().withErrorContaining(errorMsg)
+    }
+}
diff --git a/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt
new file mode 100644
index 0000000..66429a2
--- /dev/null
+++ b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle
+
+import android.arch.lifecycle.utils.load
+import android.arch.lifecycle.utils.processClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ValidCasesTest {
+    @Test
+    fun testTest() {
+        processClass("foo.Bar").compilesWithoutError()
+    }
+
+    @Test
+    fun testOnAny() {
+        processClass("foo.OnAnyMethod").compilesWithoutError().and().generatesSources(
+                load("foo.OnAnyMethod_LifecycleAdapter", "expected")
+        )
+    }
+
+    @Test
+    fun testInheritance() {
+        processClass("foo.InheritanceOk1").compilesWithoutError()
+    }
+
+    @Test
+    fun testInheritance2() {
+        processClass("foo.InheritanceOk2").compilesWithoutError().and().generatesSources(
+                load("foo.InheritanceOk2Base_LifecycleAdapter", "expected"),
+                load("foo.InheritanceOk2Derived_LifecycleAdapter", "expected")
+        )
+    }
+
+    @Test
+    fun testInheritance3() {
+        processClass("foo.InheritanceOk3").compilesWithoutError().and().generatesSources(
+                load("foo.InheritanceOk3Base_LifecycleAdapter", "expected"),
+                load("foo.InheritanceOk3Derived_LifecycleAdapter", "expected")
+        )
+    }
+
+    @Test
+    fun testNoPackageClass() {
+        processClass("NoPackageOk").compilesWithoutError()
+    }
+
+    @Test
+    fun testInterface1(){
+        processClass("foo.InterfaceOk1").compilesWithoutError()
+    }
+
+    @Test
+    fun testInterface2() {
+        processClass("foo.InterfaceOk2").compilesWithoutError().and().generatesSources(
+                load("foo.InterfaceOk2Base_LifecycleAdapter", "expected"),
+                load("foo.InterfaceOk2Derived_LifecycleAdapter", "expected"),
+                load("foo.InterfaceOk2Interface_LifecycleAdapter", "expected")
+        )
+    }
+
+    @Test
+    fun testInheritanceDifferentPackages1() {
+        processClass("foo.DifferentPackagesBase1",
+                "bar.DifferentPackagesDerived1").compilesWithoutError().and().generatesSources(
+                load("foo.DifferentPackagesBase1_LifecycleAdapter", "expected"),
+                load("bar.DifferentPackagesDerived1_LifecycleAdapter", "expected")
+        )
+    }
+
+    @Test
+    fun testInheritanceDifferentPackages2() {
+        processClass("foo.DifferentPackagesBase2",
+                "bar.DifferentPackagesDerived2").compilesWithoutError().and().generatesSources(
+                load("foo.DifferentPackagesBase2_LifecycleAdapter", "expected"),
+                load("bar.DifferentPackagesDerived2_LifecycleAdapter", "expected")
+        )
+    }
+}
diff --git a/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/utils/TestUtils.kt b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/utils/TestUtils.kt
new file mode 100644
index 0000000..d3b13c2
--- /dev/null
+++ b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/utils/TestUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.utils
+
+import android.arch.lifecycle.LifecycleProcessor
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourceSubjectFactory
+import com.google.testing.compile.JavaSourcesSubject
+import java.io.File
+import java.nio.charset.Charset
+import javax.tools.JavaFileObject
+
+fun load(fullClassName: String, folder: String): JavaFileObject {
+    val folderPath = "src/tests/test-data/${if (folder.isEmpty()) "" else folder + "/"}"
+    val split = fullClassName.split(".")
+    val code = File("$folderPath/${split.last()}.java").readText(Charset.defaultCharset())
+    return JavaFileObjects.forSourceString(fullClassName, code)
+}
+
+fun processClass(className: String, vararg fullClassNames: String): CompileTester {
+    val javaFiles = fullClassNames.map { load(it, "") }.toTypedArray()
+    val processedWith = JavaSourcesSubject.assertThat(load(className, ""), *javaFiles)
+            .processedWith(LifecycleProcessor())
+    return checkNotNull(processedWith)
+}
diff --git a/lifecycle/compiler/src/tests/test-data/Bar.java b/lifecycle/compiler/src/tests/test-data/Bar.java
new file mode 100644
index 0000000..773948e
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/Bar.java
@@ -0,0 +1,40 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class Bar {
+    @OnLifecycleEvent(ON_START)
+    public void doOnStart() {
+    }
+
+    @OnLifecycleEvent(ON_STOP)
+    public void doOnStop1Arg(LifecycleOwner provider) {
+    }
+
+    @OnLifecycleEvent(ON_STOP)
+    public void doOnStop2Args(LifecycleOwner provider) {
+    }
+
+    public static class Inner1 {
+        @OnLifecycleEvent(ON_START)
+        public void doOnStart() {
+        }
+
+        public static class Inner2 {
+            @OnLifecycleEvent(ON_START)
+            public void doOnStart() {
+            }
+
+            public static class Inner3 {
+                @OnLifecycleEvent(ON_START)
+                public void doOnStart() {
+                }
+            }
+        }
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/DifferentPackagesBase1.java b/lifecycle/compiler/src/tests/test-data/DifferentPackagesBase1.java
new file mode 100644
index 0000000..cc9d5d5
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/DifferentPackagesBase1.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class DifferentPackagesBase1 {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop(LifecycleOwner provider){}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/DifferentPackagesBase2.java b/lifecycle/compiler/src/tests/test-data/DifferentPackagesBase2.java
new file mode 100644
index 0000000..e31921a
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/DifferentPackagesBase2.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class DifferentPackagesPreBase2 {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop(LifecycleOwner provider){}
+}
+
+public class DifferentPackagesBase2 extends DifferentPackagesPreBase2 {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider){}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/DifferentPackagesDerived1.java b/lifecycle/compiler/src/tests/test-data/DifferentPackagesDerived1.java
new file mode 100644
index 0000000..8ad4c91
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/DifferentPackagesDerived1.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package bar;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+import foo.DifferentPackagesBase1;
+
+public class DifferentPackagesDerived1 extends DifferentPackagesBase1 {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop2(LifecycleOwner provider){}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/DifferentPackagesDerived2.java b/lifecycle/compiler/src/tests/test-data/DifferentPackagesDerived2.java
new file mode 100644
index 0000000..648f628
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/DifferentPackagesDerived2.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package bar;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+import foo.DifferentPackagesBase2;
+
+public class DifferentPackagesDerived2 extends DifferentPackagesBase2 {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop2(LifecycleOwner provider){}
+}
diff --git a/documents-archive/tests/NO_DOCS b/lifecycle/compiler/src/tests/test-data/IGNORE_CHECKSTYLE
similarity index 100%
rename from documents-archive/tests/NO_DOCS
rename to lifecycle/compiler/src/tests/test-data/IGNORE_CHECKSTYLE
diff --git a/lifecycle/compiler/src/tests/test-data/InheritanceOk1.java b/lifecycle/compiler/src/tests/test-data/InheritanceOk1.java
new file mode 100644
index 0000000..79c2151
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InheritanceOk1.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class Base1 {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider) {
+    }
+}
+
+class Proxy extends Base1 {
+}
+
+class Derived1 extends Proxy {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop2(LifecycleOwner provider) {
+    }
+}
+
+class Derived2 extends Proxy {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop2(LifecycleOwner provider) {
+    }
+}
+
+class Base2 {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider) {
+    }
+}
+
+class Derived3 extends Base2 {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop2(LifecycleOwner provider) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InheritanceOk2.java b/lifecycle/compiler/src/tests/test-data/InheritanceOk2.java
new file mode 100644
index 0000000..31f0a41
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InheritanceOk2.java
@@ -0,0 +1,19 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class InheritanceOk2Base {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider) {
+    }
+}
+
+class InheritanceOk2Derived extends InheritanceOk2Base {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop2(LifecycleOwner provider) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InheritanceOk3.java b/lifecycle/compiler/src/tests/test-data/InheritanceOk3.java
new file mode 100644
index 0000000..50c4623
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InheritanceOk3.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class InheritanceOk3Base {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider) {
+    }
+}
+
+class InheritanceOk3Derived extends InheritanceOk3Base {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InterfaceOk1.java b/lifecycle/compiler/src/tests/test-data/InterfaceOk1.java
new file mode 100644
index 0000000..3584f6d
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InterfaceOk1.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+interface InterfaceOk1 {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop(LifecycleOwner provider);
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InterfaceOk2.java b/lifecycle/compiler/src/tests/test-data/InterfaceOk2.java
new file mode 100644
index 0000000..58688d3
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InterfaceOk2.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class InterfaceOk2Base {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop1(LifecycleOwner provider) {
+    }
+}
+
+interface InterfaceOk2Interface {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop2(LifecycleOwner provider);
+}
+
+class InterfaceOk2Proxy extends InterfaceOk2Base {
+}
+
+class InterfaceOk2Derived extends InterfaceOk2Proxy implements InterfaceOk2Interface {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop3(LifecycleOwner provider) {
+    }
+
+    public void onStop2(LifecycleOwner provider) {}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidClassModifier.java b/lifecycle/compiler/src/tests/test-data/InvalidClassModifier.java
new file mode 100644
index 0000000..4c30bb6
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidClassModifier.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class InvalidClassModifier {
+    private static class Inner {
+        @OnLifecycleEvent(ON_STOP)
+        private void onStop() {
+        }
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidFirstArg1.java b/lifecycle/compiler/src/tests/test-data/InvalidFirstArg1.java
new file mode 100644
index 0000000..26bfc23
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidFirstArg1.java
@@ -0,0 +1,12 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class InvalidFirstArg1 {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(Event event) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidFirstArg2.java b/lifecycle/compiler/src/tests/test-data/InvalidFirstArg2.java
new file mode 100644
index 0000000..459c316
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidFirstArg2.java
@@ -0,0 +1,12 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class InvalidFirstArg2 {
+    @OnLifecycleEvent(ON_ANY)
+    public void onStop(Event e2, Event event) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidInheritance1.java b/lifecycle/compiler/src/tests/test-data/InvalidInheritance1.java
new file mode 100644
index 0000000..69f6bb2
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidInheritance1.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class Base {
+    @OnLifecycleEvent(ON_STOP)
+    void foo() {
+    }
+}
+
+class Derived extends Base {
+    @OnLifecycleEvent(ON_START)
+    void foo() {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidInheritance2.java b/lifecycle/compiler/src/tests/test-data/InvalidInheritance2.java
new file mode 100644
index 0000000..b714e47
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidInheritance2.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+interface Base {
+    @OnLifecycleEvent(ON_STOP)
+    void foo();
+}
+
+class Derived implements Base {
+    @OnLifecycleEvent(ON_START)
+    public void foo(){}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidMethodModifier.java b/lifecycle/compiler/src/tests/test-data/InvalidMethodModifier.java
new file mode 100644
index 0000000..6540333
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidMethodModifier.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class InvalidMethodModifier {
+    @OnLifecycleEvent(ON_STOP)
+    private void onStop() {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/InvalidSecondArg.java b/lifecycle/compiler/src/tests/test-data/InvalidSecondArg.java
new file mode 100644
index 0000000..e98d915
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/InvalidSecondArg.java
@@ -0,0 +1,12 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class InvalidSecondArg {
+    @OnLifecycleEvent(ON_ANY)
+    public void onStop(LifecycleOwner provider, Object lastEvent) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/NoPackageOk.java b/lifecycle/compiler/src/tests/test-data/NoPackageOk.java
new file mode 100644
index 0000000..a00283d
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/NoPackageOk.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+class NoPackageOk {
+    @OnLifecycleEvent(ON_STOP)
+    void onStop(LifecycleOwner provider){}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/OnAnyMethod.java b/lifecycle/compiler/src/tests/test-data/OnAnyMethod.java
new file mode 100644
index 0000000..f03f1cd
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/OnAnyMethod.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class OnAnyMethod {
+
+    @OnLifecycleEvent(ON_STOP)
+    void onStop(LifecycleOwner provider){}
+
+    @OnLifecycleEvent(ON_ANY)
+    void any(LifecycleOwner provider){}
+
+
+    @OnLifecycleEvent(ON_ANY)
+    void any(LifecycleOwner provider, Event event){}
+}
diff --git a/lifecycle/compiler/src/tests/test-data/TooManyArgs1.java b/lifecycle/compiler/src/tests/test-data/TooManyArgs1.java
new file mode 100644
index 0000000..0d65098
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/TooManyArgs1.java
@@ -0,0 +1,13 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class TooManyArgs1 {
+    @OnLifecycleEvent(ON_ANY)
+    public void onAny(LifecycleOwner provider, Event event, int x) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/TooManyArgs2.java b/lifecycle/compiler/src/tests/test-data/TooManyArgs2.java
new file mode 100644
index 0000000..f332d0b
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/TooManyArgs2.java
@@ -0,0 +1,13 @@
+package foo;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class TooManyArgs2 {
+    @OnLifecycleEvent(ON_STOP)
+    public void onStop(LifecycleOwner provider, Event event) {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesBase1_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesBase1_LifecycleAdapter.java
new file mode 100644
index 0000000..0728bf5
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesBase1_LifecycleAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class DifferentPackagesBase1_LifecycleAdapter implements GenericLifecycleObserver {
+  final DifferentPackagesBase1 mReceiver;
+
+  DifferentPackagesBase1_LifecycleAdapter(DifferentPackagesBase1 receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+
+  public static void __synthetic_onStop(DifferentPackagesBase1 receiver, LifecycleOwner owner) {
+    receiver.onStop(owner);
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesBase2_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesBase2_LifecycleAdapter.java
new file mode 100644
index 0000000..cb6d6d3
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesBase2_LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class DifferentPackagesBase2_LifecycleAdapter implements GenericLifecycleObserver {
+  final DifferentPackagesBase2 mReceiver;
+
+  DifferentPackagesBase2_LifecycleAdapter(DifferentPackagesBase2 receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesDerived1_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesDerived1_LifecycleAdapter.java
new file mode 100644
index 0000000..fe6dd24
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesDerived1_LifecycleAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package bar;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import foo.DifferentPackagesBase1_LifecycleAdapter;
+import java.lang.Object;
+import java.lang.Override;
+
+public class DifferentPackagesDerived1_LifecycleAdapter implements GenericLifecycleObserver {
+  final DifferentPackagesDerived1 mReceiver;
+
+  DifferentPackagesDerived1_LifecycleAdapter(DifferentPackagesDerived1 receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      DifferentPackagesBase1_LifecycleAdapter.__synthetic_onStop(mReceiver,owner);
+      mReceiver.onStop2(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesDerived2_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesDerived2_LifecycleAdapter.java
new file mode 100644
index 0000000..261dce1
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/DifferentPackagesDerived2_LifecycleAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package bar;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class DifferentPackagesDerived2_LifecycleAdapter implements GenericLifecycleObserver {
+  final DifferentPackagesDerived2 mReceiver;
+
+  DifferentPackagesDerived2_LifecycleAdapter(DifferentPackagesDerived2 receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+      mReceiver.onStop2(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk2Base_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk2Base_LifecycleAdapter.java
new file mode 100644
index 0000000..3712e48
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk2Base_LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InheritanceOk2Base_LifecycleAdapter implements GenericLifecycleObserver {
+  final InheritanceOk2Base mReceiver;
+
+  InheritanceOk2Base_LifecycleAdapter(InheritanceOk2Base receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk2Derived_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk2Derived_LifecycleAdapter.java
new file mode 100644
index 0000000..1549387
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk2Derived_LifecycleAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InheritanceOk2Derived_LifecycleAdapter implements GenericLifecycleObserver {
+  final InheritanceOk2Derived mReceiver;
+
+  InheritanceOk2Derived_LifecycleAdapter(InheritanceOk2Derived receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+      mReceiver.onStop2(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk3Base_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk3Base_LifecycleAdapter.java
new file mode 100644
index 0000000..1647443
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk3Base_LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InheritanceOk3Base_LifecycleAdapter implements GenericLifecycleObserver {
+  final InheritanceOk3Base mReceiver;
+
+  InheritanceOk3Base_LifecycleAdapter(InheritanceOk3Base receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk3Derived_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk3Derived_LifecycleAdapter.java
new file mode 100644
index 0000000..e41b9eb
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InheritanceOk3Derived_LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InheritanceOk3Derived_LifecycleAdapter implements GenericLifecycleObserver {
+  final InheritanceOk3Derived mReceiver;
+
+  InheritanceOk3Derived_LifecycleAdapter(InheritanceOk3Derived receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Base_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Base_LifecycleAdapter.java
new file mode 100644
index 0000000..22de775
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Base_LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InterfaceOk2Base_LifecycleAdapter implements GenericLifecycleObserver {
+  final InterfaceOk2Base mReceiver;
+
+  InterfaceOk2Base_LifecycleAdapter(InterfaceOk2Base receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop1(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Derived_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Derived_LifecycleAdapter.java
new file mode 100644
index 0000000..9ba0461
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Derived_LifecycleAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InterfaceOk2Derived_LifecycleAdapter implements GenericLifecycleObserver {
+  final InterfaceOk2Derived mReceiver;
+
+  InterfaceOk2Derived_LifecycleAdapter(InterfaceOk2Derived receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop1(owner);
+      mReceiver.onStop2(owner);
+      mReceiver.onStop3(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Interface_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Interface_LifecycleAdapter.java
new file mode 100644
index 0000000..a7fba29
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/InterfaceOk2Interface_LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class InterfaceOk2Interface_LifecycleAdapter implements GenericLifecycleObserver {
+  final InterfaceOk2Interface mReceiver;
+
+  InterfaceOk2Interface_LifecycleAdapter(InterfaceOk2Interface receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop2(owner);
+    }
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/OnAnyMethod_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/OnAnyMethod_LifecycleAdapter.java
new file mode 100644
index 0000000..dc59207
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/OnAnyMethod_LifecycleAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package foo;
+
+import android.arch.lifecycle.GenericLifecycleObserver;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import java.lang.Object;
+import java.lang.Override;
+
+public class OnAnyMethod_LifecycleAdapter implements GenericLifecycleObserver {
+  final OnAnyMethod mReceiver;
+
+  OnAnyMethod_LifecycleAdapter(OnAnyMethod receiver) {
+    this.mReceiver = receiver;
+  }
+
+  @Override
+  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
+    if (event == Lifecycle.Event.ON_STOP) {
+      mReceiver.onStop(owner);
+    }
+    mReceiver.any(owner);
+    mReceiver.any(owner,event);
+  }
+
+  public Object getReceiver() {
+    return mReceiver;
+  }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/license.txt b/lifecycle/compiler/src/tests/test-data/expected/license.txt
new file mode 100644
index 0000000..a538eb5
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/license.txt
@@ -0,0 +1,15 @@
+/*
+ * 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.
+ */
diff --git a/lifecycle/extensions/.gitignore b/lifecycle/extensions/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/lifecycle/extensions/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/lifecycle/extensions/build.gradle b/lifecycle/extensions/build.gradle
new file mode 100644
index 0000000..4facb68
--- /dev/null
+++ b/lifecycle/extensions/build.gradle
@@ -0,0 +1,58 @@
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+}
+dependencies {
+    compile project(":lifecycle:common")
+    compile project(":lifecycle:runtime")
+    compile project(":arch:common")
+    compile project(":arch:runtime")
+    compile libs.support.fragments
+
+    testCompile project(":arch:core-testing")
+    testCompile libs.junit
+    testCompile libs.mockito_core
+
+    testCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile libs.support.app_compat
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+}
+
+createAndroidCheckstyle(project)
+
+//noinspection GroovyUnusedAssignment
+archivesBaseName = "extensions"
diff --git a/lifecycle/extensions/proguard-rules.pro b/lifecycle/extensions/proguard-rules.pro
new file mode 100644
index 0000000..a24a5d3
--- /dev/null
+++ b/lifecycle/extensions/proguard-rules.pro
@@ -0,0 +1,7 @@
+-keep class * extends android.arch.lifecycle.ViewModel {
+    <init>();
+}
+
+-keep class * extends android.arch.lifecycle.AndroidViewModel {
+    <init>(android.app.Application);
+}
\ No newline at end of file
diff --git a/lifecycle/extensions/src/androidTest/AndroidManifest.xml b/lifecycle/extensions/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..08e1de6
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.lifecycle.extensions.test">
+
+    <application>
+        <activity android:name="android.arch.lifecycle.viewmodeltest.ViewModelActivity"
+                  android:theme="@style/Base.Theme.AppCompat">
+        </activity>
+        <activity android:name="android.arch.lifecycle.EmptyActivity"/>
+        <activity android:name="android.arch.lifecycle.activity.FragmentLifecycleActivity"
+            android:theme="@style/Base.Theme.AppCompat"/>
+        <activity android:name="android.arch.lifecycle.activity.EmptyActivity"/>
+        <service android:name="android.arch.lifecycle.service.TestService"/>
+    </application>
+
+</manifest>
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/EmptyActivity.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/EmptyActivity.java
new file mode 100644
index 0000000..738bd66
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/EmptyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.v4.app.FragmentActivity;
+
+public class EmptyActivity extends FragmentActivity {
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/FragmentLifecycleInActivityTest.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/FragmentLifecycleInActivityTest.java
new file mode 100644
index 0000000..70a4465
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/FragmentLifecycleInActivityTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.app.Instrumentation;
+import android.arch.lifecycle.activity.FragmentLifecycleActivity;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.Fragment;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(Parameterized.class)
+public class FragmentLifecycleInActivityTest {
+
+    private static final long TIMEOUT = 2; //sec
+
+    @Rule
+    public ActivityTestRule<FragmentLifecycleActivity> mActivityRule =
+            new ActivityTestRule<>(FragmentLifecycleActivity.class, false, false);
+
+    private Instrumentation mInstrumentation;
+
+    @SuppressWarnings("WeakerAccess")
+    @Parameterized.Parameter
+    public boolean mNested;
+
+    @Parameterized.Parameters(name = "nested_{0}")
+    public static Object[][] params() {
+        return new Object[][]{new Object[]{false}, new Object[]{true}};
+    }
+
+    @Before
+    public void getInstrumentation() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    private void reset() {
+        mActivityRule.getActivity().resetEvents();
+    }
+
+    @Test
+    public void testFullEvents() throws Throwable {
+        final FragmentLifecycleActivity activity = launchActivity();
+        waitForIdle();
+        assertEvents(ON_CREATE, ON_START, ON_RESUME);
+        reset();
+        finishActivity(activity);
+        assertEvents(ON_PAUSE, ON_STOP, ON_DESTROY);
+    }
+
+    @Test
+    public void testStopStart() throws Throwable {
+        final FragmentLifecycleActivity activity = launchActivity();
+        waitForIdle();
+        assertEvents(ON_CREATE, ON_START, ON_RESUME);
+        reset();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mInstrumentation.callActivityOnPause(activity);
+                mInstrumentation.callActivityOnStop(activity);
+            }
+        });
+        waitForIdle();
+        assertEvents(ON_PAUSE, ON_STOP);
+        reset();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mInstrumentation.callActivityOnStart(activity);
+                mInstrumentation.callActivityOnResume(activity);
+            }
+        });
+        waitForIdle();
+        assertEvents(ON_START, ON_RESUME);
+    }
+
+    private FragmentLifecycleActivity launchActivity() throws Throwable {
+        Intent intent = FragmentLifecycleActivity.intentFor(mInstrumentation.getTargetContext(),
+                mNested);
+        final FragmentLifecycleActivity activity = mActivityRule.launchActivity(intent);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Fragment main = activity.getSupportFragmentManager()
+                        .findFragmentByTag(FragmentLifecycleActivity.MAIN_TAG);
+                assertThat("test sanity", main, notNullValue());
+                Fragment nestedFragment = main.getChildFragmentManager()
+                        .findFragmentByTag(FragmentLifecycleActivity.NESTED_TAG);
+                assertThat("test sanity", nestedFragment != null, is(mNested));
+            }
+        });
+        assertThat(activity.getObservedOwner(), instanceOf(
+                mNested ? FragmentLifecycleActivity.NestedFragment.class
+                        : FragmentLifecycleActivity.MainFragment.class
+        ));
+        return activity;
+    }
+
+    private void waitForIdle() {
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private void finishActivity(final FragmentLifecycleActivity activity)
+            throws InterruptedException {
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                activity.finish();
+            }
+        });
+        assertThat(activity.awaitForDestruction(TIMEOUT, TimeUnit.SECONDS), is(true));
+    }
+
+    private void assertEvents(Lifecycle.Event... events) {
+        assertThat(mActivityRule.getActivity().getLoggedEvents(), is(Arrays.asList(events)));
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/FragmentOperationsLifecycleTest.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
new file mode 100644
index 0000000..be062cb
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import static java.util.Arrays.asList;
+
+import android.arch.lifecycle.activity.EmptyActivity;
+import android.arch.lifecycle.extensions.test.R;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.FragmentManager;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentOperationsLifecycleTest {
+
+    @Rule
+    public ActivityTestRule<EmptyActivity> mActivityTestRule = new ActivityTestRule<>(
+            EmptyActivity.class);
+
+    @Test
+    @UiThreadTest
+    public void addRemoveFragment() {
+        EmptyActivity activity = mActivityTestRule.getActivity();
+        LifecycleFragment fragment = new LifecycleFragment();
+        FragmentManager fm = activity.getSupportFragmentManager();
+        fm.beginTransaction().add(fragment, "tag").commitNow();
+        CollectingObserver observer = observeAndCollectIn(fragment);
+        assertThat(observer.getEventsAndReset(), is(asList(ON_CREATE, ON_START, ON_RESUME)));
+        fm.beginTransaction().remove(fragment).commitNow();
+        assertThat(observer.getEventsAndReset(), is(asList(ON_PAUSE, ON_STOP, ON_DESTROY)));
+        fm.beginTransaction().add(fragment, "tag").commitNow();
+        assertThat(observer.getEventsAndReset(), is(asList(ON_CREATE, ON_START, ON_RESUME)));
+    }
+
+    @Test
+    @UiThreadTest
+    public void fragmentInBackstack() {
+        EmptyActivity activity = mActivityTestRule.getActivity();
+        LifecycleFragment fragment1 = new LifecycleFragment();
+        FragmentManager fm = activity.getSupportFragmentManager();
+        fm.beginTransaction().add(R.id.fragment_container, fragment1, "tag").addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+        CollectingObserver observer1 = observeAndCollectIn(fragment1);
+        assertThat(observer1.getEventsAndReset(), is(asList(ON_CREATE, ON_START, ON_RESUME)));
+
+        LifecycleFragment fragment2 = new LifecycleFragment();
+        fm.beginTransaction().replace(R.id.fragment_container, fragment2).addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+
+        CollectingObserver observer2 = observeAndCollectIn(fragment2);
+        assertThat(observer1.getEventsAndReset(), is(asList(ON_PAUSE, ON_STOP)));
+        assertThat(observer2.getEventsAndReset(), is(asList(ON_CREATE, ON_START, ON_RESUME)));
+
+        assertThat(fm.popBackStackImmediate(), is(true));
+        assertThat(observer1.getEventsAndReset(), is(asList(ON_START, ON_RESUME)));
+        assertThat(observer2.getEventsAndReset(), is(asList(ON_PAUSE, ON_STOP, ON_DESTROY)));
+
+        assertThat(fm.popBackStackImmediate(), is(true));
+        assertThat(observer1.getEventsAndReset(), is(asList(ON_PAUSE, ON_STOP, ON_DESTROY)));
+    }
+
+    private static CollectingObserver observeAndCollectIn(LifecycleFragment fragment) {
+        CollectingObserver observer = new CollectingObserver();
+        fragment.getLifecycle().addObserver(observer);
+        return observer;
+    }
+
+    private static class CollectingObserver implements LifecycleObserver {
+        final List<Lifecycle.Event> mCollectedEvents = new ArrayList<>();
+
+        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
+        public void anyEvent(LifecycleOwner owner, Lifecycle.Event event) {
+            mCollectedEvents.add(event);
+        }
+
+        List<Lifecycle.Event> getEventsAndReset() {
+            ArrayList<Lifecycle.Event> events = new ArrayList<>(mCollectedEvents);
+            mCollectedEvents.clear();
+            return events;
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ServiceLifecycleTest.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ServiceLifecycleTest.java
new file mode 100644
index 0000000..fe0a306
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ServiceLifecycleTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.service.TestService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.content.LocalBroadcastManager;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ServiceLifecycleTest {
+
+    private static final int RETRY_NUMBER = 5;
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(1);
+
+    private Intent mServiceIntent;
+
+    private volatile List<Event> mLoggerEvents;
+    private EventLogger mLogger;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        mServiceIntent = new Intent(context, TestService.class);
+        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(TestService.ACTION_LOG_EVENT);
+
+        // Overcautiousness: each EventLogger has its own events list, so one bad test won't spoil
+        // others.
+        mLoggerEvents = new ArrayList<>();
+        mLogger = new EventLogger(mLoggerEvents);
+        localBroadcastManager.registerReceiver(mLogger, intentFilter);
+
+    }
+
+    @After
+    public void tearDown() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
+        localBroadcastManager.unregisterReceiver(mLogger);
+        mLogger = null;
+        mLoggerEvents = null;
+    }
+
+    @Test
+    public void testUnboundedService() throws TimeoutException, InterruptedException {
+        Context context = InstrumentationRegistry.getTargetContext();
+        context.startService(mServiceIntent);
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+        context.stopService(mServiceIntent);
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY);
+    }
+
+    @Test
+    public void testBoundedService() throws TimeoutException, InterruptedException {
+        ServiceConnection connection = bindToService();
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+        InstrumentationRegistry.getTargetContext().unbindService(connection);
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY);
+    }
+
+    @Test
+    public void testStartBindUnbindStop() throws InterruptedException {
+        Context context = InstrumentationRegistry.getTargetContext();
+        context.startService(mServiceIntent);
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        ServiceConnection connection = bindToService();
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.unbindService(connection);
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // service is still started (stopServices/stopSelf weren't called)
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.stopService(mServiceIntent);
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY);
+    }
+
+    @Test
+    public void testStartBindStopUnbind() throws InterruptedException {
+        Context context = InstrumentationRegistry.getTargetContext();
+        context.startService(mServiceIntent);
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        ServiceConnection connection = bindToService();
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.stopService(mServiceIntent);
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // service is still bound
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.unbindService(connection);
+        awaitAndAssertEvents(ON_CREATE, ON_START,
+                ON_STOP, ON_DESTROY);
+    }
+
+    @Test
+    public void testBindStartUnbindStop() throws InterruptedException {
+        Context context = InstrumentationRegistry.getTargetContext();
+        ServiceConnection connection = bindToService();
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+
+        context.startService(mServiceIntent);
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.unbindService(connection);
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // service is still started (stopServices/stopSelf weren't called)
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.stopService(mServiceIntent);
+        awaitAndAssertEvents(ON_CREATE, ON_START,
+                ON_STOP, ON_DESTROY);
+    }
+
+    @Test
+    public void testBindStartStopUnbind() throws InterruptedException {
+        Context context = InstrumentationRegistry.getTargetContext();
+        ServiceConnection connection = bindToService();
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.startService(mServiceIntent);
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.stopService(mServiceIntent);
+        // Precaution: give a chance to dispatch events
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // service is still bound
+        awaitAndAssertEvents(ON_CREATE, ON_START);
+
+        context.unbindService(connection);
+        awaitAndAssertEvents(ON_CREATE, ON_START,
+                ON_STOP, ON_DESTROY);
+    }
+
+    // can't use ServiceTestRule because it proxies connection, so we can't use unbindService method
+    private ServiceConnection bindToService() throws InterruptedException {
+        Context context = InstrumentationRegistry.getTargetContext();
+        final CountDownLatch latch = new CountDownLatch(1);
+        ServiceConnection connection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                latch.countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+
+            }
+        };
+
+        boolean success = context.bindService(mServiceIntent, connection, Context.BIND_AUTO_CREATE);
+        assertThat(success, is(true));
+        boolean awaited = latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertThat(awaited, is(true));
+        return connection;
+    }
+
+    private void awaitAndAssertEvents(Event... events) throws InterruptedException {
+        //noinspection SynchronizeOnNonFinalField
+        synchronized (mLoggerEvents) {
+            int retryCount = 0;
+            while (mLoggerEvents.size() < events.length && retryCount++ < RETRY_NUMBER) {
+                mLoggerEvents.wait(TIMEOUT);
+            }
+            assertThat(mLoggerEvents, is(Arrays.asList(events)));
+        }
+    }
+
+    private static class EventLogger extends BroadcastReceiver {
+        private final List<Event> mLoggerEvents;
+
+        private EventLogger(List<Event> loggerEvents) {
+            mLoggerEvents = loggerEvents;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLoggerEvents) {
+                mLoggerEvents.add((Event) intent.getSerializableExtra(TestService.EXTRA_KEY_EVENT));
+                mLoggerEvents.notifyAll();
+            }
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ViewModelTest.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ViewModelTest.java
new file mode 100644
index 0000000..98ce027
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ViewModelTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.app.Instrumentation;
+import android.arch.lifecycle.viewmodeltest.TestViewModel;
+import android.arch.lifecycle.viewmodeltest.ViewModelActivity;
+import android.arch.lifecycle.viewmodeltest.ViewModelActivity.ViewModelFragment;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import org.junit.Rule;
+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 ViewModelTest {
+    private static final int TIMEOUT = 2; // secs
+
+    @Rule
+    public ActivityTestRule<ViewModelActivity> mActivityRule =
+            new ActivityTestRule<>(ViewModelActivity.class);
+
+    @Test
+    public void ensureSameViewHolders() throws Throwable {
+        final TestViewModel[] activityModel = new TestViewModel[1];
+        final TestViewModel[] defaultActivityModel = new TestViewModel[1];
+        final TestViewModel[] fragment1Model = new TestViewModel[1];
+        final TestViewModel[] fragment2Model = new TestViewModel[1];
+        final ViewModelActivity[] viewModelActivity = new ViewModelActivity[1];
+        viewModelActivity[0] = mActivityRule.getActivity();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ViewModelFragment fragment1 = getFragment(viewModelActivity[0],
+                        ViewModelActivity.FRAGMENT_TAG_1);
+                ViewModelFragment fragment2 = getFragment(viewModelActivity[0],
+                        ViewModelActivity.FRAGMENT_TAG_2);
+                assertThat(fragment1, notNullValue());
+                assertThat(fragment2, notNullValue());
+                assertThat(fragment1.activityModel, is(fragment2.activityModel));
+                assertThat(fragment1.fragmentModel, not(is(fragment2.activityModel)));
+                assertThat(mActivityRule.getActivity().activityModel, is(fragment1.activityModel));
+                activityModel[0] = mActivityRule.getActivity().activityModel;
+                defaultActivityModel[0] = mActivityRule.getActivity().defaultActivityModel;
+                assertThat(defaultActivityModel[0], not(is(activityModel[0])));
+                fragment1Model[0] = fragment1.fragmentModel;
+                fragment2Model[0] = fragment2.fragmentModel;
+            }
+        });
+        viewModelActivity[0] = recreateActivity();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ViewModelFragment fragment1 = getFragment(viewModelActivity[0],
+                        ViewModelActivity.FRAGMENT_TAG_1);
+                ViewModelFragment fragment2 = getFragment(viewModelActivity[0],
+                        ViewModelActivity.FRAGMENT_TAG_2);
+                assertThat(fragment1, notNullValue());
+                assertThat(fragment2, notNullValue());
+
+                assertThat(fragment1.activityModel, is(activityModel[0]));
+                assertThat(fragment2.activityModel, is(activityModel[0]));
+                assertThat(fragment1.fragmentModel, is(fragment1Model[0]));
+                assertThat(fragment2.fragmentModel, is(fragment2Model[0]));
+                assertThat(fragment1.defaultActivityModel, is(defaultActivityModel[0]));
+                assertThat(fragment2.defaultActivityModel, is(defaultActivityModel[0]));
+                assertThat(mActivityRule.getActivity().activityModel, is(activityModel[0]));
+                assertThat(mActivityRule.getActivity().defaultActivityModel,
+                        is(defaultActivityModel[0]));
+            }
+        });
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetApplication() {
+        TestViewModel activityModel = mActivityRule.getActivity().activityModel;
+        assertThat(activityModel.getApplication(),
+                is(InstrumentationRegistry.getTargetContext().getApplicationContext()));
+    }
+
+    @Test
+    public void testOnClear() throws Throwable {
+        final ViewModelActivity activity = mActivityRule.getActivity();
+        final CountDownLatch latch = new CountDownLatch(1);
+        final LifecycleObserver observer = new LifecycleObserver() {
+            @SuppressWarnings("unused")
+            @OnLifecycleEvent(ON_RESUME)
+            void onResume() {
+                try {
+                    final FragmentManager manager = activity.getSupportFragmentManager();
+                    LifecycleFragment fragment = new LifecycleFragment();
+                    manager.beginTransaction().add(fragment, "temp").commitNow();
+                    ViewModel1 vm = ViewModelProviders.of(fragment).get(ViewModel1.class);
+                    assertThat(vm.mCleared, is(false));
+                    manager.beginTransaction().remove(fragment).commitNow();
+                    assertThat(vm.mCleared, is(true));
+                } finally {
+                    latch.countDown();
+                }
+            }
+        };
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.getLifecycle().addObserver(observer);
+            }
+        });
+        assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS), is(true));
+    }
+
+    private ViewModelFragment getFragment(FragmentActivity activity, String tag) {
+        return (ViewModelFragment) activity.getSupportFragmentManager()
+                .findFragmentByTag(tag);
+    }
+
+    private ViewModelActivity recreateActivity() throws Throwable {
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                ViewModelActivity.class.getCanonicalName(), null, false);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.addMonitor(monitor);
+        final ViewModelActivity previous = mActivityRule.getActivity();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                previous.recreate();
+            }
+        });
+        ViewModelActivity result;
+
+        // this guarantee that we will reinstall monitor between notifications about onDestroy
+        // and onCreate
+        //noinspection SynchronizationOnLocalVariableOrMethodParameter
+        synchronized (monitor) {
+            do {
+                // the documentation says "Block until an Activity is created
+                // that matches this monitor." This statement is true, but there are some other
+                // true statements like: "Block until an Activity is destroyed" or
+                // "Block until an Activity is resumed"...
+
+                // this call will release synchronization monitor's monitor
+                result = (ViewModelActivity) monitor.waitForActivityWithTimeout(4000);
+                if (result == null) {
+                    throw new RuntimeException("Timeout. Failed to recreate an activity");
+                }
+            } while (result == previous);
+        }
+        return result;
+    }
+
+    public static class ViewModel1 extends ViewModel {
+        boolean mCleared = false;
+
+        @Override
+        protected void onCleared() {
+            mCleared = true;
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ViewModelTestInTransaction.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ViewModelTestInTransaction.java
new file mode 100644
index 0000000..3832b3b
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/ViewModelTestInTransaction.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.Fragment;
+
+import android.arch.lifecycle.viewmodeltest.TestViewModel;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewModelTestInTransaction {
+
+    @Rule
+    public ActivityTestRule<EmptyActivity> mActivityRule =
+            new ActivityTestRule<>(EmptyActivity.class);
+
+    @Test
+    @UiThreadTest
+    public void testViewModelInTransactionActivity() {
+        EmptyActivity activity = mActivityRule.getActivity();
+        TestFragment fragment = new TestFragment();
+        activity.getSupportFragmentManager().beginTransaction().add(fragment, "tag").commitNow();
+        TestViewModel viewModel = ViewModelProviders.of(activity).get(TestViewModel.class);
+        assertThat(viewModel, is(fragment.mViewModel));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testViewModelInTransactionFragment() {
+        EmptyActivity activity = mActivityRule.getActivity();
+        ParentFragment parent = new ParentFragment();
+        activity.getSupportFragmentManager().beginTransaction().add(parent, "parent").commitNow();
+        assertThat(parent.mExecuted, is(true));
+    }
+
+
+    public static class ParentFragment extends Fragment {
+
+        private boolean mExecuted;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            TestFragment fragment = new TestFragment();
+            getChildFragmentManager().beginTransaction().add(fragment, "tag").commitNow();
+            TestViewModel viewModel = ViewModelProviders.of(this).get(TestViewModel.class);
+            assertThat(viewModel, is(fragment.mViewModel));
+            mExecuted = true;
+        }
+    }
+
+    public static class TestFragment extends Fragment {
+
+        TestViewModel mViewModel;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            Fragment parentFragment = getParentFragment();
+            ViewModelProvider provider = parentFragment != null
+                    ? ViewModelProviders.of(parentFragment) : ViewModelProviders.of(getActivity());
+            mViewModel = provider.get(TestViewModel.class);
+            assertThat(mViewModel, notNullValue());
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/activity/EmptyActivity.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/activity/EmptyActivity.java
new file mode 100644
index 0000000..017fff4
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/activity/EmptyActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.activity;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import android.arch.lifecycle.LifecycleActivity;
+import android.arch.lifecycle.extensions.test.R;
+
+public class EmptyActivity extends LifecycleActivity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/activity/FragmentLifecycleActivity.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
new file mode 100644
index 0000000..f4485e8
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.activity;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleFragment;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.arch.lifecycle.extensions.test.R;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class FragmentLifecycleActivity extends AppCompatActivity {
+    public static final String NESTED_TAG = "nested_fragment";
+    public static final String MAIN_TAG = "main_fragment";
+    private static final String EXTRA_NESTED = "nested";
+
+    private final List<Lifecycle.Event> mLoggedEvents = Collections
+            .synchronizedList(new ArrayList<Lifecycle.Event>());
+    private LifecycleOwner mObservedOwner;
+    private final CountDownLatch mDestroyLatch = new CountDownLatch(1);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        MainFragment fragment;
+        fragment = new MainFragment();
+        boolean nested = getIntent().getBooleanExtra(EXTRA_NESTED, false);
+        if (nested) {
+            fragment.mNestedFragment = new NestedFragment();
+        }
+        observe(nested ? fragment.mNestedFragment : fragment);
+        getSupportFragmentManager().beginTransaction()
+                .add(R.id.fragment_container, fragment, MAIN_TAG)
+                .commit();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mDestroyLatch.countDown();
+    }
+
+    public void resetEvents() {
+        mLoggedEvents.clear();
+    }
+
+    public static class MainFragment extends LifecycleFragment {
+        @Nullable
+        LifecycleFragment mNestedFragment;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            if (mNestedFragment != null) {
+                getChildFragmentManager().beginTransaction()
+                        .add(mNestedFragment, NESTED_TAG)
+                        .commit();
+            }
+        }
+    }
+
+    public static class NestedFragment extends LifecycleFragment {
+    }
+
+    public static Intent intentFor(Context context, boolean nested) {
+        Intent intent = new Intent(context, FragmentLifecycleActivity.class);
+        intent.putExtra(EXTRA_NESTED, nested);
+        return intent;
+    }
+
+    public void observe(LifecycleOwner provider) {
+        mObservedOwner = provider;
+        provider.getLifecycle().addObserver(new LifecycleObserver() {
+            @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
+            public void anyEvent(LifecycleOwner owner, Lifecycle.Event event) {
+                mLoggedEvents.add(event);
+            }
+        });
+    }
+
+    public List<Lifecycle.Event> getLoggedEvents() {
+        return mLoggedEvents;
+    }
+
+    public LifecycleOwner getObservedOwner() {
+        return mObservedOwner;
+    }
+
+    public boolean awaitForDestruction(long timeout, TimeUnit timeUnit)
+            throws InterruptedException {
+        return mDestroyLatch.await(timeout, timeUnit);
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/service/TestService.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/service/TestService.java
new file mode 100644
index 0000000..fcf06cc
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/service/TestService.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.service;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LifecycleService;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.support.annotation.Nullable;
+import android.support.v4.content.LocalBroadcastManager;
+
+public class TestService extends LifecycleService {
+
+    public static final String ACTION_LOG_EVENT = "ACTION_LOG_EVENT";
+    public static final String EXTRA_KEY_EVENT = "EXTRA_KEY_EVENT";
+
+    private final IBinder mBinder = new Binder();
+
+    public TestService() {
+        getLifecycle().addObserver(new LifecycleObserver() {
+            @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
+            public void anyEvent(LifecycleOwner owner, Lifecycle.Event event) {
+                Context context = (TestService) owner;
+                Intent intent = new Intent(ACTION_LOG_EVENT);
+                intent.putExtra(EXTRA_KEY_EVENT, event);
+                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+            }
+        });
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        super.onBind(intent);
+        return mBinder;
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/viewmodeltest/TestViewModel.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/viewmodeltest/TestViewModel.java
new file mode 100644
index 0000000..ec3550c
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/viewmodeltest/TestViewModel.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.viewmodeltest;
+
+import android.app.Application;
+
+import android.arch.lifecycle.AndroidViewModel;
+
+public class TestViewModel extends AndroidViewModel {
+
+    public TestViewModel(Application application) {
+        super(application);
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
new file mode 100644
index 0000000..5ef9f16
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/java/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.viewmodeltest;
+
+import android.arch.lifecycle.extensions.test.R;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import android.arch.lifecycle.LifecycleActivity;
+import android.arch.lifecycle.LifecycleFragment;
+import android.arch.lifecycle.ViewModelProviders;
+
+public class ViewModelActivity extends LifecycleActivity {
+    public static final String KEY_FRAGMENT_MODEL = "fragment-model";
+    public static final String KEY_ACTIVITY_MODEL = "activity-model";
+    public static final String FRAGMENT_TAG_1 = "f1";
+    public static final String FRAGMENT_TAG_2 = "f2";
+
+    public TestViewModel activityModel;
+    public TestViewModel defaultActivityModel;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_model);
+        if (savedInstanceState == null) {
+            getSupportFragmentManager().beginTransaction()
+                    .add(R.id.fragment_container, new ViewModelFragment(), FRAGMENT_TAG_1)
+                    .add(new ViewModelFragment(), FRAGMENT_TAG_2)
+                    .commit();
+        }
+        activityModel = ViewModelProviders.of(this).get(KEY_ACTIVITY_MODEL, TestViewModel.class);
+        defaultActivityModel = ViewModelProviders.of(this).get(TestViewModel.class);
+    }
+
+    public static class ViewModelFragment extends LifecycleFragment {
+        public TestViewModel fragmentModel;
+        public TestViewModel activityModel;
+        public TestViewModel defaultActivityModel;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            fragmentModel = ViewModelProviders.of(this).get(KEY_FRAGMENT_MODEL,
+                    TestViewModel.class);
+            activityModel = ViewModelProviders.of(getActivity()).get(KEY_ACTIVITY_MODEL,
+                    TestViewModel.class);
+            defaultActivityModel = ViewModelProviders.of(getActivity()).get(TestViewModel.class);
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/androidTest/res/layout/activity_main.xml b/lifecycle/extensions/src/androidTest/res/layout/activity_main.xml
new file mode 100644
index 0000000..305bccc
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/res/layout/activity_main.xml
@@ -0,0 +1,28 @@
+<?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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/activity_main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="android.arch.lifecycle.activity.FragmentLifecycleActivity">
+    <FrameLayout android:id="@+id/fragment_container"
+                 android:layout_width="match_parent"
+                 android:layout_height="match_parent"></FrameLayout>
+</RelativeLayout>
diff --git a/lifecycle/extensions/src/androidTest/res/layout/activity_view_model.xml b/lifecycle/extensions/src/androidTest/res/layout/activity_view_model.xml
new file mode 100644
index 0000000..eace2e9
--- /dev/null
+++ b/lifecycle/extensions/src/androidTest/res/layout/activity_view_model.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout android:id="@+id/fragment_container"
+                 android:layout_width="match_parent"
+                 android:layout_height="match_parent"></FrameLayout>
+</RelativeLayout>
diff --git a/lifecycle/extensions/src/main/AndroidManifest.xml b/lifecycle/extensions/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f7698eb
--- /dev/null
+++ b/lifecycle/extensions/src/main/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.lifecycle.extensions">
+    <application>
+        <provider android:authorities="${applicationId}.lifecycle-trojan"
+            android:multiprocess="true"
+            android:exported="false"
+            android:name="android.arch.lifecycle.LifecycleRuntimeTrojanProvider"/>
+    </application>
+</manifest>
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/AndroidViewModel.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/AndroidViewModel.java
new file mode 100644
index 0000000..2c7e173
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/AndroidViewModel.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Application;
+
+/**
+ * Application context aware {@link ViewModel}.
+ * <p>
+ * Subclasses must have a constructor which accepts {@link Application} as the only parameter.
+ * <p>
+ */
+public class AndroidViewModel extends ViewModel {
+    private Application mApplication;
+
+    public AndroidViewModel(Application application) {
+        mApplication = application;
+    }
+
+    /**
+     * Return the application.
+     */
+    public <T extends Application> T getApplication() {
+        return (T) mApplication;
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ComputableLiveData.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ComputableLiveData.java
new file mode 100644
index 0000000..fe18243
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ComputableLiveData.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A LiveData class that can be invalidated & computed on demand.
+ * <p>
+ * This is an internal class for now, might be public if we see the necessity.
+ *
+ * @param <T> The type of the live data
+ * @hide internal
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class ComputableLiveData<T> {
+
+    private final LiveData<T> mLiveData;
+
+    private AtomicBoolean mInvalid = new AtomicBoolean(true);
+    private AtomicBoolean mComputing = new AtomicBoolean(false);
+
+    /**
+     * Creates a computable live data which is computed when there are active observers.
+     * <p>
+     * It can also be invalidated via {@link #invalidate()} which will result in a call to
+     * {@link #compute()} if there are active observers (or when they start observing)
+     */
+    @SuppressWarnings("WeakerAccess")
+    public ComputableLiveData() {
+        mLiveData = new LiveData<T>() {
+            @Override
+            protected void onActive() {
+                // TODO if we make this class public, we should accept an executor
+                AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
+            }
+        };
+    }
+
+    /**
+     * Returns the LiveData managed by this class.
+     *
+     * @return A LiveData that is controlled by ComputableLiveData.
+     */
+    @SuppressWarnings("WeakerAccess")
+    @NonNull
+    public LiveData<T> getLiveData() {
+        return mLiveData;
+    }
+
+    @VisibleForTesting
+    final Runnable mRefreshRunnable = new Runnable() {
+        @WorkerThread
+        @Override
+        public void run() {
+            boolean computed;
+            do {
+                computed = false;
+                // compute can happen only in 1 thread but no reason to lock others.
+                if (mComputing.compareAndSet(false, true)) {
+                    // as long as it is invalid, keep computing.
+                    try {
+                        T value = null;
+                        while (mInvalid.compareAndSet(true, false)) {
+                            computed = true;
+                            value = compute();
+                        }
+                        if (computed) {
+                            mLiveData.postValue(value);
+                        }
+                    } finally {
+                        // release compute lock
+                        mComputing.set(false);
+                    }
+                }
+                // check invalid after releasing compute lock to avoid the following scenario.
+                // Thread A runs compute()
+                // Thread A checks invalid, it is false
+                // Main thread sets invalid to true
+                // Thread B runs, fails to acquire compute lock and skips
+                // Thread A releases compute lock
+                // We've left invalid in set state. The check below recovers.
+            } while (computed && mInvalid.get());
+        }
+    };
+
+    // invalidation check always happens on the main thread
+    @VisibleForTesting
+    final Runnable mInvalidationRunnable = new Runnable() {
+        @MainThread
+        @Override
+        public void run() {
+            boolean isActive = mLiveData.hasActiveObservers();
+            if (mInvalid.compareAndSet(false, true)) {
+                if (isActive) {
+                    // TODO if we make this class public, we should accept an executor.
+                    AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
+                }
+            }
+        }
+    };
+
+    /**
+     * Invalidates the LiveData.
+     * <p>
+     * When there are active observers, this will trigger a call to {@link #compute()}.
+     */
+    public void invalidate() {
+        AppToolkitTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @WorkerThread
+    protected abstract T compute();
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/EmptyActivityLifecycleCallbacks.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/EmptyActivityLifecycleCallbacks.java
new file mode 100644
index 0000000..ed41f2d
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/EmptyActivityLifecycleCallbacks.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+class EmptyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
+    @Override
+    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+    }
+
+    @Override
+    public void onActivityStarted(Activity activity) {
+    }
+
+    @Override
+    public void onActivityResumed(Activity activity) {
+    }
+
+    @Override
+    public void onActivityPaused(Activity activity) {
+    }
+
+    @Override
+    public void onActivityStopped(Activity activity) {
+    }
+
+    @Override
+    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+    }
+
+    @Override
+    public void onActivityDestroyed(Activity activity) {
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java
new file mode 100644
index 0000000..100d10a
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/HolderFragment.java
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class HolderFragment extends Fragment {
+    private static final String LOG_TAG = "ViewModelStores";
+
+    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final String HOLDER_TAG =
+            "android.arch.lifecycle.state.StateProviderHolderFragment";
+
+    private ViewModelStore mViewModelStore = new ViewModelStore();
+
+    public HolderFragment() {
+        setRetainInstance(true);
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        sHolderFragmentManager.holderFragmentCreated(this);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mViewModelStore.clear();
+    }
+
+    public ViewModelStore getViewModelStore() {
+        return mViewModelStore;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
+        return sHolderFragmentManager.holderFragmentFor(activity);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static HolderFragment holderFragmentFor(Fragment fragment) {
+        return sHolderFragmentManager.holderFragmentFor(fragment);
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    static class HolderFragmentManager {
+        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
+        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
+
+        private ActivityLifecycleCallbacks mActivityCallbacks =
+                new EmptyActivityLifecycleCallbacks() {
+                    @Override
+                    public void onActivityDestroyed(Activity activity) {
+                        HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
+                        if (fragment != null) {
+                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
+                        }
+                    }
+                };
+
+        private boolean mActivityCallbacksIsAdded = false;
+
+        private FragmentLifecycleCallbacks mParentDestroyedCallback =
+                new FragmentLifecycleCallbacks() {
+                    @Override
+                    public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
+                        super.onFragmentDestroyed(fm, parentFragment);
+                        HolderFragment fragment = mNotCommittedFragmentHolders.remove(
+                                parentFragment);
+                        if (fragment != null) {
+                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
+                        }
+                    }
+                };
+
+        void holderFragmentCreated(Fragment holderFragment) {
+            Fragment parentFragment = holderFragment.getParentFragment();
+            if (parentFragment != null) {
+                mNotCommittedFragmentHolders.remove(parentFragment);
+                parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
+                        mParentDestroyedCallback);
+            } else {
+                mNotCommittedActivityHolders.remove(holderFragment.getActivity());
+            }
+        }
+
+        private static HolderFragment findHolderFragment(FragmentManager manager) {
+            if (manager.isDestroyed()) {
+                throw new IllegalStateException("Can't access ViewModels from onDestroy");
+            }
+
+            Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
+            if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
+                throw new IllegalStateException("Unexpected "
+                        + "fragment instance was returned by HOLDER_TAG");
+            }
+            return (HolderFragment) fragmentByTag;
+        }
+
+        private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
+            HolderFragment holder = new HolderFragment();
+            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
+            return holder;
+        }
+
+        HolderFragment holderFragmentFor(FragmentActivity activity) {
+            FragmentManager fm = activity.getSupportFragmentManager();
+            HolderFragment holder = findHolderFragment(fm);
+            if (holder != null) {
+                return holder;
+            }
+            holder = mNotCommittedActivityHolders.get(activity);
+            if (holder != null) {
+                return holder;
+            }
+
+            if (!mActivityCallbacksIsAdded) {
+                mActivityCallbacksIsAdded = true;
+                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
+            }
+            holder = createHolderFragment(fm);
+            mNotCommittedActivityHolders.put(activity, holder);
+            return holder;
+        }
+
+        HolderFragment holderFragmentFor(Fragment parentFragment) {
+            FragmentManager fm = parentFragment.getChildFragmentManager();
+            HolderFragment holder = findHolderFragment(fm);
+            if (holder != null) {
+                return holder;
+            }
+            holder = mNotCommittedFragmentHolders.get(parentFragment);
+            if (holder != null) {
+                return holder;
+            }
+
+            parentFragment.getFragmentManager()
+                    .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
+            holder = createHolderFragment(fm);
+            mNotCommittedFragmentHolders.put(parentFragment, holder);
+            return holder;
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleActivity.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleActivity.java
new file mode 100644
index 0000000..196a8d7
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Activity that implements {@link LifecycleOwner}.
+ * <p>
+ * This class is a temporary implementation detail until Lifecycles are integrated with support
+ * library.
+ */
+public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {
+
+    private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
+
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mRegistry;
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleDispatcher.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleDispatcher.java
new file mode 100644
index 0000000..9fdec95
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleDispatcher.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+
+import android.app.Activity;
+import android.app.Application;
+import android.arch.lifecycle.Lifecycle.State;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * When initialized, it hooks into the Activity callback of the Application and observes
+ * Activities. It is responsible to hook in child-fragments to activities and fragments to report
+ * their lifecycle events. Another responsibility of this class is to mark as stopped all lifecycle
+ * providers related to an activity as soon it is not safe to run a fragment transaction in this
+ * activity.
+ */
+class LifecycleDispatcher {
+
+    private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+            + ".LifecycleDispatcher.report_fragment_tag";
+
+    private static AtomicBoolean sInitialized = new AtomicBoolean(false);
+
+    static void init(Context context) {
+        if (sInitialized.getAndSet(true)) {
+            return;
+        }
+        ((Application) context.getApplicationContext())
+                .registerActivityLifecycleCallbacks(new DispatcherActivityCallback());
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
+        private final FragmentCallback mFragmentCallback;
+
+        DispatcherActivityCallback() {
+            mFragmentCallback = new FragmentCallback();
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+            if (activity instanceof FragmentActivity) {
+                ((FragmentActivity) activity).getSupportFragmentManager()
+                        .registerFragmentLifecycleCallbacks(mFragmentCallback, true);
+            }
+            ReportFragment.injectIfNeededIn(activity);
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            if (activity instanceof FragmentActivity) {
+                markState((FragmentActivity) activity, CREATED);
+            }
+        }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+            if (activity instanceof FragmentActivity) {
+                markState((FragmentActivity) activity, CREATED);
+            }
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    public static class DestructionReportFragment extends Fragment {
+        @Override
+        public void onPause() {
+            super.onPause();
+            dispatch(ON_PAUSE);
+        }
+
+        @Override
+        public void onStop() {
+            super.onStop();
+            dispatch(ON_STOP);
+        }
+
+        @Override
+        public void onDestroy() {
+            super.onDestroy();
+            dispatch(ON_DESTROY);
+        }
+
+        protected void dispatch(Lifecycle.Event event) {
+            dispatchIfLifecycleOwner(getParentFragment(), event);
+        }
+    }
+
+    private static void markState(FragmentManager manager, State state) {
+        Collection<Fragment> fragments = manager.getFragments();
+        if (fragments == null) {
+            return;
+        }
+        for (Fragment fragment : fragments) {
+            if (fragment == null) {
+                continue;
+            }
+            markStateIn(fragment, state);
+            if (fragment.isAdded()) {
+                markState(fragment.getChildFragmentManager(), state);
+            }
+        }
+    }
+
+    private static void markStateIn(Object object, State state) {
+        if (object instanceof LifecycleRegistryOwner) {
+            LifecycleRegistry registry = ((LifecycleRegistryOwner) object).getLifecycle();
+            registry.markState(state);
+        }
+    }
+
+    private static void markState(FragmentActivity activity, State state) {
+        markStateIn(activity, state);
+        markState(activity.getSupportFragmentManager(), state);
+    }
+
+    private static void dispatchIfLifecycleOwner(Fragment fragment, Lifecycle.Event event) {
+        if (fragment instanceof LifecycleRegistryOwner) {
+            ((LifecycleRegistryOwner) fragment).getLifecycle().handleLifecycleEvent(event);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    static class FragmentCallback extends FragmentManager.FragmentLifecycleCallbacks {
+
+        @Override
+        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
+            dispatchIfLifecycleOwner(f, ON_CREATE);
+
+            if (!(f instanceof LifecycleRegistryOwner)) {
+                return;
+            }
+
+            if (f.getChildFragmentManager().findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
+                f.getChildFragmentManager().beginTransaction().add(new DestructionReportFragment(),
+                        REPORT_FRAGMENT_TAG).commit();
+            }
+        }
+
+        @Override
+        public void onFragmentStarted(FragmentManager fm, Fragment f) {
+            dispatchIfLifecycleOwner(f, ON_START);
+        }
+
+        @Override
+        public void onFragmentResumed(FragmentManager fm, Fragment f) {
+            dispatchIfLifecycleOwner(f, ON_RESUME);
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleFragment.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleFragment.java
new file mode 100644
index 0000000..d8dbf38
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleFragment.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.v4.app.Fragment;
+
+/**
+ * A fragment that is also a {@link LifecycleOwner}.
+ * <p>
+ * This class is a temporary implementation detail until Lifecycles are integrated with support
+ * library.
+ */
+// This class will be removed once we integrate with Fragment library.
+public class LifecycleFragment extends Fragment implements LifecycleRegistryOwner {
+    LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mLifecycleRegistry;
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java
new file mode 100644
index 0000000..ac278c0
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Internal class to initialize Lifecycles.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class LifecycleRuntimeTrojanProvider extends ContentProvider {
+    @Override
+    public boolean onCreate() {
+        LifecycleDispatcher.init(getContext());
+        ProcessLifecycleOwner.init(getContext());
+        return true;
+    }
+
+    @Nullable
+    @Override
+    public Cursor query(@NonNull Uri uri, String[] strings, String s, String[] strings1,
+            String s1) {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public String getType(@NonNull Uri uri) {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
+        return null;
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, String s, String[] strings) {
+        return 0;
+    }
+
+    @Override
+    public int update(@NonNull Uri uri, ContentValues contentValues, String s, String[] strings) {
+        return 0;
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleService.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleService.java
new file mode 100644
index 0000000..adc4ffc
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleService.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.support.annotation.CallSuper;
+import android.support.annotation.Nullable;
+
+/**
+ * A Service that is also a {@link LifecycleOwner}.
+ */
+public class LifecycleService extends Service implements LifecycleOwner {
+
+    private final ServiceLifecycleDispatcher mDispatcher = new ServiceLifecycleDispatcher(this);
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        mDispatcher.onServicePreSuperOnCreate();
+        super.onCreate();
+    }
+
+    @CallSuper
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        mDispatcher.onServicePreSuperOnBind();
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @CallSuper
+    @Override
+    public void onStart(Intent intent, int startId) {
+        mDispatcher.onServicePreSuperOnStart();
+        super.onStart(intent, startId);
+    }
+
+    // this method is added only to annotate it with @CallSuper.
+    // In usual service super.onStartCommand is no-op, but in LifecycleService
+    // it results in mDispatcher.onServicePreSuperOnStart() call, because
+    // super.onStartCommand calls onStart().
+    @CallSuper
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @CallSuper
+    @Override
+    public void onDestroy() {
+        mDispatcher.onServicePreSuperOnDestroy();
+        super.onDestroy();
+    }
+
+    @Override
+    public Lifecycle getLifecycle() {
+        return mDispatcher.getLifecycle();
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LiveData.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LiveData.java
new file mode 100644
index 0000000..d689442
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LiveData.java
@@ -0,0 +1,413 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
+import static android.arch.lifecycle.Lifecycle.State.STARTED;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.internal.SafeIterableMap;
+import android.arch.lifecycle.Lifecycle.State;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * LiveData is a data holder class that can be observed within a given lifecycle.
+ * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
+ * this observer will be notified about modifications of the wrapped data only if the paired
+ * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
+ * {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
+ * {@link #observeForever(Observer)} is considered as always active and thus will be always notified
+ * about modifications. For those observers, you should manually call
+ * {@link #removeObserver(Observer)}.
+ *
+ * <p> An observer added with a Lifecycle will be automatically removed if the corresponding
+ * Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for
+ * activities and fragments where they can safely observe LiveData and not worry about leaks:
+ * they will be instantly unsubscribed when they are destroyed.
+ *
+ * <p>
+ * In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
+ * to get notified when number of active {@link Observer}s change between 0 and 1.
+ * This allows LiveData to release any heavy resources when it does not have any Observers that
+ * are actively observing.
+ * <p>
+ * This class is designed to hold individual data fields of {@link ViewModel},
+ * but can also be used for sharing data between different modules in your application
+ * in a decoupled fashion.
+ *
+ * @param <T> The type of data hold by this instance
+ * @see ViewModel
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+// TODO: Thread checks are too strict right now, we may consider automatically moving them to main
+// thread.
+public abstract class LiveData<T> {
+    private final Object mDataLock = new Object();
+    static final int START_VERSION = -1;
+    private static final Object NOT_SET = new Object();
+
+    private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
+
+        private LifecycleRegistry mRegistry = init();
+
+        private LifecycleRegistry init() {
+            LifecycleRegistry registry = new LifecycleRegistry(this);
+            registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+            registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+            registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+            return registry;
+        }
+
+        @Override
+        public Lifecycle getLifecycle() {
+            return mRegistry;
+        }
+    };
+
+    private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
+            new SafeIterableMap<>();
+
+    // how many observers are in active state
+    private int mActiveCount = 0;
+    private Object mData = NOT_SET;
+    // when setData is called, we set the pending data and actual data swap happens on the main
+    // thread
+    private volatile Object mPendingData = NOT_SET;
+    private int mVersion = START_VERSION;
+
+    private boolean mDispatchingValue;
+    @SuppressWarnings("FieldCanBeLocal")
+    private boolean mDispatchInvalidated;
+    private final Runnable mPostValueRunnable = new Runnable() {
+        @Override
+        public void run() {
+            Object newValue;
+            synchronized (mDataLock) {
+                newValue = mPendingData;
+                mPendingData = NOT_SET;
+            }
+            //noinspection unchecked
+            setValue((T) newValue);
+        }
+    };
+
+    private void considerNotify(LifecycleBoundObserver observer) {
+        if (!observer.active) {
+            return;
+        }
+        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
+        //
+        // we still first check observer.active to keep it as the entrance for events. So even if
+        // the observer moved to an active state, if we've not received that event, we better not
+        // notify for a more predictable notification order.
+        if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
+            return;
+        }
+        if (observer.lastVersion >= mVersion) {
+            return;
+        }
+        observer.lastVersion = mVersion;
+        //noinspection unchecked
+        observer.observer.onChanged((T) mData);
+    }
+
+    private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
+        if (mDispatchingValue) {
+            mDispatchInvalidated = true;
+            return;
+        }
+        mDispatchingValue = true;
+        do {
+            mDispatchInvalidated = false;
+            if (initiator != null) {
+                considerNotify(initiator);
+                initiator = null;
+            } else {
+                for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
+                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
+                    considerNotify(iterator.next().getValue());
+                    if (mDispatchInvalidated) {
+                        break;
+                    }
+                }
+            }
+        } while (mDispatchInvalidated);
+        mDispatchingValue = false;
+    }
+
+    /**
+     * Adds the given observer to the observers list within the lifespan of the given
+     * owner. The events are dispatched on the main thread. If LiveData already has data
+     * set, it will be delivered to the observer.
+     * <p>
+     * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
+     * or {@link Lifecycle.State#RESUMED} state (active).
+     * <p>
+     * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
+     * automatically be removed.
+     * <p>
+     * When data changes while the {@code owner} is not active, it will not receive any updates.
+     * If it becomes active again, it will receive the last available data automatically.
+     * <p>
+     * LiveData keeps a strong reference to the observer and the owner as long as the
+     * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
+     * the observer &amp; the owner.
+     * <p>
+     * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
+     * ignores the call.
+     * <p>
+     * If the given owner, observer tuple is already in the list, the call is ignored.
+     * If the observer is already in the list with another owner, LiveData throws an
+     * {@link IllegalArgumentException}.
+     *
+     * @param owner    The LifecycleOwner which controls the observer
+     * @param observer The observer that will receive the events
+     */
+    @MainThread
+    public void observe(LifecycleOwner owner, Observer<T> observer) {
+        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
+            // ignore
+            return;
+        }
+        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
+        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
+        if (existing != null && existing.owner != wrapper.owner) {
+            throw new IllegalArgumentException("Cannot add the same observer"
+                    + " with different lifecycles");
+        }
+        if (existing != null) {
+            return;
+        }
+        owner.getLifecycle().addObserver(wrapper);
+        wrapper.activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
+    }
+
+    /**
+     * Adds the given observer to the observers list. This call is similar to
+     * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which
+     * is always active. This means that the given observer will receive all events and will never
+     * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
+     * observing this LiveData.
+     * While LiveData has one of such observers, it will be considered
+     * as active.
+     * <p>
+     * If the observer was already added with an owner to this LiveData, LiveData throws an
+     * {@link IllegalArgumentException}.
+     *
+     * @param observer The observer that will receive the events
+     */
+    @MainThread
+    public void observeForever(Observer<T> observer) {
+        observe(ALWAYS_ON, observer);
+    }
+
+    /**
+     * Removes the given observer from the observers list.
+     *
+     * @param observer The Observer to receive events.
+     */
+    @MainThread
+    public void removeObserver(final Observer<T> observer) {
+        assertMainThread("removeObserver");
+        LifecycleBoundObserver removed = mObservers.remove(observer);
+        if (removed == null) {
+            return;
+        }
+        removed.owner.getLifecycle().removeObserver(removed);
+        removed.activeStateChanged(false);
+    }
+
+    /**
+     * Removes all observers that are tied to the given {@link LifecycleOwner}.
+     *
+     * @param owner The {@code LifecycleOwner} scope for the observers to be removed.
+     */
+    @MainThread
+    public void removeObservers(final LifecycleOwner owner) {
+        assertMainThread("removeObservers");
+        for (Map.Entry<Observer<T>, LifecycleBoundObserver> entry : mObservers) {
+            if (entry.getValue().owner == owner) {
+                removeObserver(entry.getKey());
+            }
+        }
+    }
+
+    /**
+     * Posts a task to a main thread to set the given value. So if you have a following code
+     * executed in the main thread:
+     * <pre class="prettyprint">
+     * liveData.postValue("a");
+     * liveData.setValue("b");
+     * </pre>
+     * The value "b" would be set at first and later the main thread would override it with
+     * the value "a".
+     * <p>
+     * If you called this method multiple times before a main thread executed a posted task, only
+     * the last value would be dispatched.
+     *
+     * @param value The new value
+     */
+    protected void postValue(T value) {
+        boolean postTask;
+        synchronized (mDataLock) {
+            postTask = mPendingData == NOT_SET;
+            mPendingData = value;
+        }
+        if (!postTask) {
+            return;
+        }
+        AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
+    }
+
+    /**
+     * Sets the value. If there are active observers, the value will be dispatched to them.
+     * <p>
+     * This method must be called from the main thread. If you need set a value from a background
+     * thread, you can use {@link #postValue(Object)}
+     *
+     * @param value The new value
+     */
+    @MainThread
+    protected void setValue(T value) {
+        assertMainThread("setValue");
+        mVersion++;
+        mData = value;
+        dispatchingValue(null);
+    }
+
+    /**
+     * Returns the current value.
+     * Note that calling this method on a background thread does not guarantee that the latest
+     * value set will be received.
+     *
+     * @return the current value
+     */
+    @Nullable
+    public T getValue() {
+        // we do not return pending data here to be able to serve a consistent view to the main
+        // thread.
+        Object data = mData;
+        if (mData != NOT_SET) {
+            //noinspection unchecked
+            return (T) mData;
+        }
+        return null;
+    }
+
+    int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Called when the number of active observers change to 1 from 0.
+     * <p>
+     * This callback can be used to know that this LiveData is being used thus should be kept
+     * up to date.
+     */
+    protected void onActive() {
+
+    }
+
+    /**
+     * Called when the number of active observers change from 1 to 0.
+     * <p>
+     * This does not mean that there are no observers left, there may still be observers but their
+     * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}
+     * (like an Activity in the back stack).
+     * <p>
+     * You can check if there are observers via {@link #hasObservers()}.
+     */
+    protected void onInactive() {
+
+    }
+
+    /**
+     * Returns true if this LiveData has observers.
+     *
+     * @return true if this LiveData has observers
+     */
+    public boolean hasObservers() {
+        return mObservers.size() > 0;
+    }
+
+    /**
+     * Returns true if this LiveData has active observers.
+     *
+     * @return true if this LiveData has active observers
+     */
+    public boolean hasActiveObservers() {
+        return mActiveCount > 0;
+    }
+
+    class LifecycleBoundObserver implements LifecycleObserver {
+        public final LifecycleOwner owner;
+        public final Observer<T> observer;
+        public boolean active;
+        public int lastVersion = START_VERSION;
+
+        LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
+            this.owner = owner;
+            this.observer = observer;
+        }
+
+        @SuppressWarnings("unused")
+        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
+        void onStateChange() {
+            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
+                removeObserver(observer);
+                return;
+            }
+            // immediately set active state, so we'd never dispatch anything to inactive
+            // owner
+            activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
+
+        }
+
+        void activeStateChanged(boolean newActive) {
+            if (newActive == active) {
+                return;
+            }
+            active = newActive;
+            boolean wasInactive = LiveData.this.mActiveCount == 0;
+            LiveData.this.mActiveCount += active ? 1 : -1;
+            if (wasInactive && active) {
+                onActive();
+            }
+            if (LiveData.this.mActiveCount == 0 && !active) {
+                onInactive();
+            }
+            if (active) {
+                dispatchingValue(this);
+            }
+        }
+    }
+
+    static boolean isActiveState(State state) {
+        return state.isAtLeast(STARTED);
+    }
+
+    private void assertMainThread(String methodName) {
+        if (!AppToolkitTaskExecutor.getInstance().isMainThread()) {
+            throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
+                    + " thread");
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/MediatorLiveData.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/MediatorLiveData.java
new file mode 100644
index 0000000..a7d090b
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/MediatorLiveData.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.arch.core.internal.SafeIterableMap;
+import android.support.annotation.CallSuper;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+
+import java.util.Map;
+
+/**
+ * {@link LiveData} subclass which may observer other {@code LiveData} objects and react on
+ * {@code OnChanged} events from them.
+ * <p>
+ * This class correctly propagates its active/inactive states down to source {@code LiveData}
+ * objects.
+ *
+ * @param <T> The type of data hold by this instance
+ */
+@SuppressWarnings("WeakerAccess")
+public class MediatorLiveData<T> extends MutableLiveData<T> {
+    private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
+
+    /**
+     * Starts to listen the given {@code source} LiveData, {@code onChanged} observer will be called
+     * when {@code source} value was changed.
+     * <p>
+     * {@code onChanged} callback will be called only when this {@code MediatorLiveData} is active.
+     * <p> If the given LiveData is already added as a source but with a different Observer,
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * @param source    the {@code LiveData} to listen to
+     * @param onChanged The observer that will receive the events
+     * @param <S>       The type of data hold by {@code source} LiveData
+     */
+    @MainThread
+    public <S> void addSource(LiveData<S> source, Observer<S> onChanged) {
+        Source<S> e = new Source<>(source, onChanged);
+        Source<?> existing = mSources.putIfAbsent(source, e);
+        if (existing != null && existing.mObserver != onChanged) {
+            throw new IllegalArgumentException(
+                    "This source was already added with the different observer");
+        }
+        if (existing != null) {
+            return;
+        }
+        if (hasActiveObservers()) {
+            e.plug();
+        }
+    }
+
+    /**
+     * Stops to listen the given {@code LiveData}.
+     *
+     * @param toRemote {@code LiveData} to stop to listen
+     * @param <S>      the type of data hold by {@code source} LiveData
+     */
+    @MainThread
+    public <S> void removeSource(LiveData<S> toRemote) {
+        Source<?> source = mSources.remove(toRemote);
+        if (source != null) {
+            source.unplug();
+        }
+    }
+
+    @CallSuper
+    @Override
+    protected void onActive() {
+        for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
+            source.getValue().plug();
+        }
+    }
+
+    @CallSuper
+    @Override
+    protected void onInactive() {
+        for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
+            source.getValue().unplug();
+        }
+    }
+
+    private static class Source<V> {
+        final LiveData<V> mLiveData;
+        final Observer<V> mObserver;
+        int mVersion = START_VERSION;
+
+        Source(LiveData<V> liveData, final Observer<V> observer) {
+            mLiveData = liveData;
+            mObserver = new Observer<V>() {
+                @Override
+                public void onChanged(@Nullable V v) {
+                    if (mVersion != mLiveData.getVersion()) {
+                        mVersion = mLiveData.getVersion();
+                        observer.onChanged(v);
+                    }
+                }
+            };
+        }
+
+        void plug() {
+            mLiveData.observeForever(mObserver);
+        }
+
+        void unplug() {
+            mLiveData.removeObserver(mObserver);
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/MutableLiveData.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/MutableLiveData.java
new file mode 100644
index 0000000..ecd7752
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/MutableLiveData.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
+ *
+ * @param <T> The type of data hold by this instance
+ */
+@SuppressWarnings("WeakerAccess")
+public class MutableLiveData<T> extends LiveData<T> {
+    @Override
+    public void postValue(T value) {
+        super.postValue(value);
+    }
+
+    @Override
+    public void setValue(T value) {
+        super.setValue(value);
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/Observer.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/Observer.java
new file mode 100644
index 0000000..0e36775
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/Observer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.Nullable;
+
+/**
+ * A simple callback that can receive from {@link LiveData}.
+ *
+ * @param <T> The type of the parameter
+ *
+ * @see LiveData LiveData - for a usage description.
+ */
+public interface Observer<T> {
+    /**
+     * Called when the data is changed.
+     * @param t  The new data
+     */
+    void onChanged(@Nullable T t);
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java
new file mode 100644
index 0000000..e2a1256
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Activity;
+import android.app.Application;
+import android.arch.lifecycle.ReportFragment.ActivityInitializationListener;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.VisibleForTesting;
+
+/**
+ * Class that provides lifecycle for the whole application process.
+ * <p>
+ * You can consider this LifecycleOwner as the composite of all of your Activities, except that
+ * {@link Lifecycle.Event#ON_CREATE} will be dispatched once and {@link Lifecycle.Event#ON_DESTROY}
+ * will never be dispatched. Other lifecycle events will be dispatched with following rules:
+ * ProcessLifecycleOwner will dispatch {@link Lifecycle.Event#ON_START},
+ * {@link Lifecycle.Event#ON_RESUME} events, as a first activity moves through these events.
+ * {@link Lifecycle.Event#ON_PAUSE}, {@link Lifecycle.Event#ON_STOP}, events will be dispatched with
+ * a <b>delay</b> after a last activity
+ * passed through them. This delay is long enough to guarantee that ProcessLifecycleOwner
+ * won't send any events if activities are destroyed and recreated due to a
+ * configuration change.
+ *
+ * <p>
+ * It is useful for use cases where you would like to react on your app coming to the foreground or
+ * going to the background and you don't need a milliseconds accuracy in receiving lifecycle
+ * events.
+ */
+@SuppressWarnings("WeakerAccess")
+public class ProcessLifecycleOwner implements LifecycleOwner {
+
+    @VisibleForTesting
+    static final long TIMEOUT_MS = 700; //mls
+
+    // ground truth counters
+    private int mStartedCounter = 0;
+    private int mResumedCounter = 0;
+
+    private boolean mPauseSent = true;
+    private boolean mStopSent = true;
+
+    private Handler mHandler;
+    private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
+
+    private Runnable mDelayedPauseRunnable = new Runnable() {
+        @Override
+        public void run() {
+            dispatchPauseIfNeeded();
+            dispatchStopIfNeeded();
+        }
+    };
+
+    private ActivityInitializationListener mInitializationListener =
+            new ActivityInitializationListener() {
+                @Override
+                public void onCreate() {
+                }
+
+                @Override
+                public void onStart() {
+                    activityStarted();
+                }
+
+                @Override
+                public void onResume() {
+                    activityResumed();
+                }
+            };
+
+    private static final ProcessLifecycleOwner sInstance = new ProcessLifecycleOwner();
+
+    /**
+     * The LifecycleOwner for the whole application process. Note that if your application
+     * has multiple processes, this provider does not know about other processes.
+     *
+     * @return {@link LifecycleOwner} for the whole application.
+     */
+    public static LifecycleOwner get() {
+        return sInstance;
+    }
+
+    static void init(Context context) {
+        sInstance.attach(context);
+    }
+
+    void activityStarted() {
+        mStartedCounter++;
+        if (mStartedCounter == 1 && mStopSent) {
+            mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+            mStopSent = false;
+        }
+    }
+
+    void activityResumed() {
+        mResumedCounter++;
+        if (mResumedCounter == 1) {
+            if (mPauseSent) {
+                mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+                mPauseSent = false;
+            } else {
+                mHandler.removeCallbacks(mDelayedPauseRunnable);
+            }
+        }
+    }
+
+    void activityPaused() {
+        mResumedCounter--;
+        if (mResumedCounter == 0) {
+            mHandler.postDelayed(mDelayedPauseRunnable, TIMEOUT_MS);
+        }
+    }
+
+    void activityStopped() {
+        mStartedCounter--;
+        dispatchStopIfNeeded();
+    }
+
+    private void dispatchPauseIfNeeded() {
+        if (mResumedCounter == 0) {
+            mPauseSent = true;
+            mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
+        }
+    }
+
+    private void dispatchStopIfNeeded() {
+        if (mStartedCounter == 0 && mPauseSent) {
+            mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+            mStopSent = true;
+        }
+    }
+
+    private ProcessLifecycleOwner() {
+    }
+
+    void attach(Context context) {
+        mHandler = new Handler();
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        Application app = (Application) context.getApplicationContext();
+        app.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
+            @Override
+            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+                ReportFragment  .get(activity).setProcessListener(mInitializationListener);
+            }
+
+            @Override
+            public void onActivityPaused(Activity activity) {
+                activityPaused();
+            }
+
+            @Override
+            public void onActivityStopped(Activity activity) {
+                activityStopped();
+            }
+        });
+    }
+
+    @Override
+    public Lifecycle getLifecycle() {
+        return mRegistry;
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ServiceLifecycleDispatcher.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ServiceLifecycleDispatcher.java
new file mode 100644
index 0000000..6067d7b
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ServiceLifecycleDispatcher.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+
+/**
+ * Helper class to dispatch lifecycle events for a service. Use it only if it is impossible
+ * to use {@link LifecycleService}.
+ */
+@SuppressWarnings("WeakerAccess")
+public class ServiceLifecycleDispatcher {
+    private final LifecycleRegistry mRegistry;
+    private final Handler mHandler;
+    private DispatchRunnable mLastDispatchRunnable;
+
+    /**
+     * @param provider {@link LifecycleOwner} for a service, usually it is a service itself
+     */
+    public ServiceLifecycleDispatcher(@NonNull LifecycleOwner provider) {
+        mRegistry = new LifecycleRegistry(provider);
+        mHandler = new Handler();
+    }
+
+    private void postDispatchRunnable(Lifecycle.Event event) {
+        if (mLastDispatchRunnable != null) {
+            mLastDispatchRunnable.run();
+        }
+        mLastDispatchRunnable = new DispatchRunnable(mRegistry, event);
+        mHandler.postAtFrontOfQueue(mLastDispatchRunnable);
+    }
+
+    /**
+     * Must be a first call in {@link Service#onCreate()} method, even before super.onCreate call.
+     */
+    public void onServicePreSuperOnCreate() {
+        postDispatchRunnable(Lifecycle.Event.ON_CREATE);
+    }
+
+    /**
+     * Must be a first call in {@link Service#onBind(Intent)} method, even before super.onBind
+     * call.
+     */
+    public void onServicePreSuperOnBind() {
+        postDispatchRunnable(Lifecycle.Event.ON_START);
+    }
+
+    /**
+     * Must be a first call in {@link Service#onStart(Intent, int)} or
+     * {@link Service#onStartCommand(Intent, int, int)} methods, even before
+     * a corresponding super call.
+     */
+    public void onServicePreSuperOnStart() {
+        postDispatchRunnable(Lifecycle.Event.ON_START);
+    }
+
+    /**
+     * Must be a first call in {@link Service#onDestroy()} method, even before super.OnDestroy
+     * call.
+     */
+    public void onServicePreSuperOnDestroy() {
+        postDispatchRunnable(Lifecycle.Event.ON_STOP);
+        postDispatchRunnable(Lifecycle.Event.ON_DESTROY);
+    }
+
+    /**
+     * @return {@link Lifecycle} for the given {@link LifecycleOwner}
+     */
+    public Lifecycle getLifecycle() {
+        return mRegistry;
+    }
+
+    static class DispatchRunnable implements Runnable {
+        private final LifecycleRegistry mRegistry;
+        final Lifecycle.Event mEvent;
+        private boolean mWasExecuted = false;
+
+        DispatchRunnable(@NonNull LifecycleRegistry registry, Lifecycle.Event event) {
+            mRegistry = registry;
+            mEvent = event;
+        }
+
+        @Override
+        public void run() {
+            if (!mWasExecuted) {
+                mRegistry.handleLifecycleEvent(mEvent);
+                mWasExecuted = true;
+            }
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/Transformations.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/Transformations.java
new file mode 100644
index 0000000..c316563
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/Transformations.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.arch.core.util.Function;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+
+/**
+ * Transformations for a {@link LiveData} class.
+ */
+@SuppressWarnings("WeakerAccess")
+public class Transformations {
+
+    private Transformations() {
+    }
+
+    /**
+     * Applies the given function on the main thread to each value emitted by {@code source}
+     * LiveData and returns LiveData, which emits resulting values.
+     * <p>
+     * The given function {@code func} will be executed on the main thread.
+     *
+     * @param source a {@code LiveData} to listen to
+     * @param func   a function to apply
+     * @param <X>    a type of {@code source} LiveData
+     * @param <Y>    a type of resulting LiveData.
+     * @return a LiveData which emits resulting values
+     */
+    @MainThread
+    public static <X, Y> LiveData<Y> map(LiveData<X> source, final Function<X, Y> func) {
+        final MediatorLiveData<Y> result = new MediatorLiveData<>();
+        result.addSource(source, new Observer<X>() {
+            @Override
+            public void onChanged(@Nullable X x) {
+                result.setValue(func.apply(x));
+            }
+        });
+        return result;
+    }
+
+    /**
+     * Creates a LiveData, let's name it {@code swLiveData}, which follows next flow:
+     * it reacts on changes of {@code trigger} LiveData, applies the given function to new value of
+     * {@code trigger} LiveData and sets resulting LiveData as a "backing" LiveData
+     * to {@code swLiveData}.
+     * "Backing" LiveData means, that all events emitted by it will retransmitted
+     * by {@code swLiveData}.
+     * <p>
+     * If the given function returns null, then {@code swLiveData} is not "backed" by any other
+     * LiveData.
+     * <p>
+     * The given function {@code func} will be executed on the main thread.
+     *
+     * @param trigger a {@code LiveData} to listen to
+     * @param func    a function which creates "backing" LiveData
+     * @param <X>     a type of {@code source} LiveData
+     * @param <Y>     a type of resulting LiveData
+     */
+    @MainThread
+    public static <X, Y> LiveData<Y> switchMap(LiveData<X> trigger,
+            final Function<X, LiveData<Y>> func) {
+        final MediatorLiveData<Y> result = new MediatorLiveData<>();
+        result.addSource(trigger, new Observer<X>() {
+            LiveData<Y> mSource;
+
+            @Override
+            public void onChanged(@Nullable X x) {
+                LiveData<Y> newLiveData = func.apply(x);
+                if (mSource == newLiveData) {
+                    return;
+                }
+                if (mSource != null) {
+                    result.removeSource(mSource);
+                }
+                mSource = newLiveData;
+                if (mSource != null) {
+                    result.addSource(mSource, new Observer<Y>() {
+                        @Override
+                        public void onChanged(@Nullable Y y) {
+                            result.setValue(y);
+                        }
+                    });
+                }
+            }
+        });
+        return result;
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModel.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModel.java
new file mode 100644
index 0000000..0310c46
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModel.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * ViewModel is a class that is responsible for preparing and managing the data for
+ * an {@link android.app.Activity Activity} or a {@link android.support.v4.app.Fragment Fragment}.
+ * It also handles the communication of the Activity / Fragment with the rest of the application
+ * (e.g. calling the business logic classes).
+ * <p>
+ * A ViewModel is always created in association with a scope (an fragment or an activity) and will
+ * be retained as long as the scope is alive. E.g. if it is an Activity, until it is
+ * finished.
+ * <p>
+ * In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a
+ * configuration change (e.g. rotation). The new instance of the owner will just re-connected to the
+ * existing ViewModel.
+ * <p>
+ * The purpose of the ViewModel is to acquire and keep the information that is necessary for an
+ * Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the
+ * ViewModel. ViewModels usually expose this information via {@link LiveData} or Android Data
+ * Binding. You can also use any observability construct from you favorite framework.
+ * <p>
+ * ViewModel's only responsibility is to manage the data for the UI. It <b>should never</b> access
+ * your view hierarchy or hold a reference back to the Activity or the Fragment.
+ * <p>
+ * Typical usage from an Activity standpoint would be:
+ * <pre>
+ * public class UserActivity extends Activity {
+ *
+ *     {@literal @}Override
+ *     protected void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState);
+ *         setContentView(R.layout.user_activity_layout);
+ *         final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
+ *         viewModel.userLiveData.observer(this, new Observer<User>() {
+ *            {@literal @}Override
+ *             public void onChanged(@Nullable User data) {
+ *                 // update ui.
+ *             }
+ *         });
+ *         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
+ *             {@literal @}Override
+ *             public void onClick(View v) {
+ *                  viewModel.doAction();
+ *             }
+ *         });
+ *     }
+ * }
+ * </pre>
+ *
+ * ViewModel would be:
+ * <pre>
+ * public class UserModel extends ViewModel {
+ *     public final LiveData&lt;User&gt; userLiveData = new LiveData<>();
+ *
+ *     public UserModel() {
+ *         // trigger user load.
+ *     }
+ *
+ *     void doAction() {
+ *         // depending on the action, do necessary business logic calls and update the
+ *         // userLiveData.
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>
+ * ViewModels can also be used as a communication layer between different Fragments of an Activity.
+ * Each Fragment can acquire the ViewModel using the same key via their Activity. This allows
+ * communication between Fragments in a de-coupled fashion such that they never need to talk to
+ * the other Fragment directly.
+ * <pre>
+ * public class MyFragment extends Fragment {
+ *     public void onStart() {
+ *         UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
+ *     }
+ * }
+ * </pre>
+ * </>
+ */
+public abstract class ViewModel {
+    /**
+     * This method will be called when this ViewModel is no longer used and will be destroyed.
+     * <p>
+     * It is useful when ViewModel observes some data and you need to clear this subscription to
+     * prevent a leak of this ViewModel.
+     */
+    @SuppressWarnings("WeakerAccess")
+    protected void onCleared() {
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProvider.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProvider.java
new file mode 100644
index 0000000..7ef591f
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProvider.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+
+/**
+ * An utility class that provides {@code ViewModels} for a scope.
+ * <p>
+ * Default {@code ViewModelProvider} for an {@code Activity} or a {@code Fragment} can be obtained
+ * from {@link android.arch.lifecycle.ViewModelProviders} class.
+ */
+@SuppressWarnings("WeakerAccess")
+public class ViewModelProvider {
+
+    private static final String DEFAULT_KEY =
+            "android.arch.lifecycle.ViewModelProvider.DefaultKey";
+
+    /**
+     * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
+     */
+    public interface Factory {
+        /**
+         * Creates a new instance of the given {@code Class}.
+         * <p>
+         *
+         * @param modelClass a {@code Class} whose instance is requested
+         * @param <T>        The type parameter for the ViewModel.
+         * @return a newly created ViewModel
+         */
+        <T extends ViewModel> T create(Class<T> modelClass);
+    }
+
+    private final Factory mFactory;
+    private final ViewModelStore mViewModelStore;
+
+    /**
+     * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
+     * {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
+     *
+     * @param owner   a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
+     *                retain {@code ViewModels}
+     * @param factory a {@code Factory} which will be used to instantiate
+     *                new {@code ViewModels}
+     */
+    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
+        this(owner.getViewModelStore(), factory);
+    }
+
+    /**
+     * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
+     * {@code Factory} and retain them in the given {@code store}.
+     *
+     * @param store   {@code ViewModelStore} where ViewModels will be stored.
+     * @param factory factory a {@code Factory} which will be used to instantiate
+     *                new {@code ViewModels}
+     */
+    public ViewModelProvider(ViewModelStore store, Factory factory) {
+        mFactory = factory;
+        this.mViewModelStore = store;
+    }
+
+    /**
+     * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
+     * an activity), associated with this {@code ViewModelProvider}.
+     * <p>
+     * The created ViewModel is associated with the given scope and will be retained
+     * as long as the scope is alive (e.g. if it is an activity, until it is
+     * finished or process is killed).
+     *
+     * @param modelClass The class of the ViewModel to create an instance of it if it is not
+     *                   present.
+     * @param <T>        The type parameter for the ViewModel.
+     * @return A ViewModel that is an instance of the given type {@code T}.
+     */
+    public <T extends ViewModel> T get(Class<T> modelClass) {
+        String canonicalName = modelClass.getCanonicalName();
+        if (canonicalName == null) {
+            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
+        }
+        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
+    }
+
+    /**
+     * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
+     * an activity), associated with this {@code ViewModelProvider}.
+     * <p>
+     * The created ViewModel is associated with the given scope and will be retained
+     * as long as the scope is alive (e.g. if it is an activity, until it is
+     * finished or process is killed).
+     *
+     * @param key        The key to use to identify the ViewModel.
+     * @param modelClass The class of the ViewModel to create an instance of it if it is not
+     *                   present.
+     * @param <T>        The type parameter for the ViewModel.
+     * @return A ViewModel that is an instance of the given type {@code T}.
+     */
+    @NonNull
+    @MainThread
+    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
+        ViewModel viewModel = mViewModelStore.get(key);
+
+        if (modelClass.isInstance(viewModel)) {
+            //noinspection unchecked
+            return (T) viewModel;
+        } else {
+            //noinspection StatementWithEmptyBody
+            if (viewModel != null) {
+                // TODO: log a warning.
+            }
+        }
+
+        viewModel = mFactory.create(modelClass);
+        mViewModelStore.put(key, viewModel);
+        //noinspection unchecked
+        return (T) viewModel;
+    }
+
+    /**
+     * Simple factory, which calls empty constructor on the give class.
+     */
+    public static class NewInstanceFactory implements Factory {
+
+        @Override
+        public <T extends ViewModel> T create(Class<T> modelClass) {
+            //noinspection TryWithIdenticalCatches
+            try {
+                return modelClass.newInstance();
+            } catch (InstantiationException e) {
+                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
+            }
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java
new file mode 100644
index 0000000..f64365b
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelProviders.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.annotation.SuppressLint;
+import android.app.Application;
+import android.arch.lifecycle.ViewModelProvider.Factory;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Utilities methods for {@link ViewModelStore} class.
+ */
+public class ViewModelProviders {
+
+    @SuppressLint("StaticFieldLeak")
+    private static DefaultFactory sDefaultFactory;
+
+    private static void initializeFactoryIfNeeded(Application application) {
+        if (sDefaultFactory == null) {
+            sDefaultFactory = new DefaultFactory(application);
+        }
+    }
+
+    /**
+     * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
+     * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
+     * <p>
+     * It uses {@link DefaultFactory} to instantiate new ViewModels.
+     *
+     * @param fragment a fragment, in whose scope ViewModels should be retained
+     * @return a ViewModelProvider instance
+     */
+    @MainThread
+    public static ViewModelProvider of(@NonNull Fragment fragment) {
+        FragmentActivity activity = fragment.getActivity();
+        if (activity == null) {
+            throw new IllegalArgumentException(
+                    "Can't create ViewModelProvider for detached fragment");
+        }
+        initializeFactoryIfNeeded(activity.getApplication());
+        return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory);
+    }
+
+    /**
+     * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
+     * is alive. More detailed explanation is in {@link ViewModel}.
+     * <p>
+     * It uses {@link DefaultFactory} to instantiate new ViewModels.
+     *
+     * @param activity an activity, in whose scope ViewModels should be retained
+     * @return a ViewModelProvider instance
+     */
+    @MainThread
+    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
+        initializeFactoryIfNeeded(activity.getApplication());
+        return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
+    }
+
+    /**
+     * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
+     * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
+     * <p>
+     * It uses the given {@link Factory} to instantiate new ViewModels.
+     *
+     * @param fragment a fragment, in whose scope ViewModels should be retained
+     * @param factory  a {@code Factory} to instantiate new ViewModels
+     * @return a ViewModelProvider instance
+     */
+    @MainThread
+    public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
+        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
+    }
+
+    /**
+     * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
+     * is alive. More detailed explanation is in {@link ViewModel}.
+     * <p>
+     * It uses the given {@link Factory} to instantiate new ViewModels.
+     *
+     * @param activity an activity, in whose scope ViewModels should be retained
+     * @param factory  a {@code Factory} to instantiate new ViewModels
+     * @return a ViewModelProvider instance
+     */
+    @MainThread
+    public static ViewModelProvider of(@NonNull FragmentActivity activity,
+            @NonNull Factory factory) {
+        return new ViewModelProvider(ViewModelStores.of(activity), factory);
+    }
+
+    /**
+     * {@link Factory} which may create {@link AndroidViewModel} and
+     * {@link ViewModel}, which have an empty constructor.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory {
+
+        private Application mApplication;
+
+        /**
+         * Creates a {@code DefaultFactory}
+         *
+         * @param application an application to pass in {@link AndroidViewModel}
+         */
+        public DefaultFactory(@NonNull Application application) {
+            mApplication = application;
+        }
+
+        @Override
+        public <T extends ViewModel> T create(Class<T> modelClass) {
+            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
+                //noinspection TryWithIdenticalCatches
+                try {
+                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
+                } catch (NoSuchMethodException e) {
+                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
+                } catch (InstantiationException e) {
+                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
+                } catch (InvocationTargetException e) {
+                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
+                }
+            }
+            return super.create(modelClass);
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStore.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStore.java
new file mode 100644
index 0000000..a1864bb
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStore.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import java.util.HashMap;
+
+/**
+ * Class to store {@code ViewModels}.
+ * <p>
+ * An instance of {@code ViewModelStore} must be retained through configuration changes:
+ * if an owner of this {@code ViewModelStore} is destroyed and recreated due to configuration
+ * changes, new instance of an owner should still have the same old instance of
+ * {@code ViewModelStore}.
+ * <p>
+ * If an owner of this {@code ViewModelStore} is destroyed and is not going to be recreated,
+ * then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
+ * be notified that they are no longer used.
+ * <p>
+ * {@link android.arch.lifecycle.ViewModelStores} provides a {@code ViewModelStore} for
+ * activities and fragments.
+ */
+public class ViewModelStore {
+
+    private final HashMap<String, ViewModel> mMap = new HashMap<>();
+
+    final void put(String key, ViewModel viewModel) {
+        ViewModel oldViewModel = mMap.get(key);
+        if (oldViewModel != null) {
+            oldViewModel.onCleared();
+        }
+        mMap.put(key, viewModel);
+    }
+
+    final ViewModel get(String key) {
+        return mMap.get(key);
+    }
+
+    /**
+     *  Clears internal storage and notifies ViewModels that they are no longer used.
+     */
+    public final void clear() {
+        for (ViewModel vm : mMap.values()) {
+            vm.onCleared();
+        }
+        mMap.clear();
+    }
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStoreOwner.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStoreOwner.java
new file mode 100644
index 0000000..5058305
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStoreOwner.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * A scope that owns {@link ViewModelStore}.
+ * <p>
+ * A responsibility of an implementation of this interface is to retain owned ViewModelStore
+ * during the configuration changes and call {@link ViewModelStore#clear()}, when this scope is
+ * going to be destroyed.
+ */
+@SuppressWarnings("WeakerAccess")
+public interface ViewModelStoreOwner {
+    /**
+     * Returns owned {@link ViewModelStore}
+     *
+     * @return a {@code ViewModelStore}
+     */
+    ViewModelStore getViewModelStore();
+}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java
new file mode 100644
index 0000000..8c17dd9
--- /dev/null
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ViewModelStores.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.HolderFragment.holderFragmentFor;
+
+import android.support.annotation.MainThread;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Factory methods for {@link ViewModelStore} class.
+ */
+@SuppressWarnings("WeakerAccess")
+public class ViewModelStores {
+
+    private ViewModelStores() {
+    }
+
+    /**
+     * Returns the {@link ViewModelStore} of the given activity.
+     *
+     * @param activity an activity whose {@code ViewModelStore} is requested
+     * @return a {@code ViewModelStore}
+     */
+    @MainThread
+    public static ViewModelStore of(FragmentActivity activity) {
+        return holderFragmentFor(activity).getViewModelStore();
+    }
+
+    /**
+     * Returns the {@link ViewModelStore} of the given fragment.
+     *
+     * @param fragment a fragment whose {@code ViewModelStore} is requested
+     * @return a {@code ViewModelStore}
+     */
+    @MainThread
+    public static ViewModelStore of(Fragment fragment) {
+        return holderFragmentFor(fragment).getViewModelStore();
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java
new file mode 100644
index 0000000..0a3fbed
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.TaskExecutor;
+import android.arch.core.executor.TaskExecutorWithFakeMainThread;
+import android.arch.lifecycle.util.InstantTaskExecutor;
+import android.support.annotation.Nullable;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RunWith(JUnit4.class)
+public class ComputableLiveDataTest {
+    private TaskExecutor mTaskExecutor;
+    private TestLifecycleOwner mLifecycleOwner;
+
+    @Before
+    public void setup() {
+        mLifecycleOwner = new TestLifecycleOwner();
+    }
+
+    @Before
+    public void swapExecutorDelegate() {
+        mTaskExecutor = spy(new InstantTaskExecutor());
+        AppToolkitTaskExecutor.getInstance().setDelegate(mTaskExecutor);
+    }
+
+    @After
+    public void removeExecutorDelegate() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    @Test
+    public void noComputeWithoutObservers() {
+        final TestComputable computable = new TestComputable();
+        verify(mTaskExecutor, never()).executeOnDiskIO(computable.mRefreshRunnable);
+        verify(mTaskExecutor, never()).executeOnDiskIO(computable.mInvalidationRunnable);
+    }
+
+    @Test
+    public void noConcurrentCompute() throws InterruptedException {
+        TaskExecutorWithFakeMainThread executor = new TaskExecutorWithFakeMainThread(2);
+        AppToolkitTaskExecutor.getInstance().setDelegate(executor);
+        try {
+            // # of compute calls
+            final Semaphore computeCounter = new Semaphore(0);
+            // available permits for computation
+            final Semaphore computeLock = new Semaphore(0);
+            final TestComputable computable = new TestComputable(1, 2) {
+                @Override
+                protected Integer compute() {
+                    try {
+                        computeCounter.release(1);
+                        computeLock.tryAcquire(1, 20, TimeUnit.SECONDS);
+                    } catch (InterruptedException e) {
+                        throw new AssertionError(e);
+                    }
+                    return super.compute();
+                }
+            };
+            final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
+            //noinspection unchecked
+            Observer<Integer> observer = mock(Observer.class);
+            computable.getLiveData().observeForever(observer);
+            verify(observer, never()).onChanged(anyInt());
+            // wait for first compute call
+            assertThat(computeCounter.tryAcquire(1, 2, TimeUnit.SECONDS), is(true));
+            // re-invalidate while in compute
+            computable.invalidate();
+            computable.invalidate();
+            computable.invalidate();
+            computable.invalidate();
+            // ensure another compute call does not arrive
+            assertThat(computeCounter.tryAcquire(1, 2, TimeUnit.SECONDS), is(false));
+            // allow computation to finish
+            computeLock.release(2);
+            // wait for the second result, first will be skipped due to invalidation during compute
+            verify(observer, timeout(2000)).onChanged(captor.capture());
+            assertThat(captor.getAllValues(), is(Collections.singletonList(2)));
+            reset(observer);
+            // allow all computations to run, there should not be any.
+            computeLock.release(100);
+            // unfortunately, Mockito.after is not available in 1.9.5
+            executor.drainTasks(2);
+            // assert no other results arrive
+            verify(observer, never()).onChanged(anyInt());
+        } finally {
+            AppToolkitTaskExecutor.getInstance().setDelegate(null);
+        }
+    }
+
+    @Test
+    public void addingObserverShouldTriggerAComputation() {
+        TestComputable computable = new TestComputable(1);
+        mLifecycleOwner.handleEvent(Lifecycle.Event.ON_CREATE);
+        final AtomicInteger mValue = new AtomicInteger(-1);
+        computable.getLiveData().observe(mLifecycleOwner, new Observer<Integer>() {
+            @Override
+            public void onChanged(@Nullable Integer integer) {
+                //noinspection ConstantConditions
+                mValue.set(integer);
+            }
+        });
+        verify(mTaskExecutor, never()).executeOnDiskIO(any(Runnable.class));
+        assertThat(mValue.get(), is(-1));
+        mLifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        verify(mTaskExecutor).executeOnDiskIO(computable.mRefreshRunnable);
+        assertThat(mValue.get(), is(1));
+    }
+
+    @Test
+    public void invalidationShouldNotReTriggerComputationIfObserverIsInActive() {
+        TestComputable computable = new TestComputable(1, 2);
+        mLifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        final AtomicInteger mValue = new AtomicInteger(-1);
+        computable.getLiveData().observe(mLifecycleOwner, new Observer<Integer>() {
+            @Override
+            public void onChanged(@Nullable Integer integer) {
+                //noinspection ConstantConditions
+                mValue.set(integer);
+            }
+        });
+        assertThat(mValue.get(), is(1));
+        mLifecycleOwner.handleEvent(Lifecycle.Event.ON_STOP);
+        computable.invalidate();
+        reset(mTaskExecutor);
+        verify(mTaskExecutor, never()).executeOnDiskIO(computable.mRefreshRunnable);
+        assertThat(mValue.get(), is(1));
+    }
+
+    @Test
+    public void invalidationShouldReTriggerQueryIfObserverIsActive() {
+        TestComputable computable = new TestComputable(1, 2);
+        mLifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        final AtomicInteger mValue = new AtomicInteger(-1);
+        computable.getLiveData().observe(mLifecycleOwner, new Observer<Integer>() {
+            @Override
+            public void onChanged(@Nullable Integer integer) {
+                //noinspection ConstantConditions
+                mValue.set(integer);
+            }
+        });
+        assertThat(mValue.get(), is(1));
+        computable.invalidate();
+        assertThat(mValue.get(), is(2));
+    }
+
+    static class TestComputable extends ComputableLiveData<Integer> {
+        final int[] mValues;
+        AtomicInteger mValueCounter;
+
+        TestComputable(int... values) {
+            mValueCounter = new AtomicInteger();
+            mValues = values;
+        }
+
+        @Override
+        protected Integer compute() {
+            return mValues[mValueCounter.getAndIncrement()];
+        }
+    }
+
+    static class TestLifecycleOwner implements LifecycleOwner {
+        private LifecycleRegistry mLifecycle;
+
+        TestLifecycleOwner() {
+            mLifecycle = new LifecycleRegistry(this);
+        }
+
+        @Override
+        public Lifecycle getLifecycle() {
+            return mLifecycle;
+        }
+
+        void handleEvent(Lifecycle.Event event) {
+            mLifecycle.handleLifecycleEvent(event);
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/DispatcherActivityCallbackTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/DispatcherActivityCallbackTest.java
new file mode 100644
index 0000000..1431089
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/DispatcherActivityCallbackTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class DispatcherActivityCallbackTest {
+    @Test
+    public void onCreateFrameworkActivity() {
+        LifecycleDispatcher.DispatcherActivityCallback callback =
+                new LifecycleDispatcher.DispatcherActivityCallback();
+        Activity activity = mock(Activity.class);
+        checkReportFragment(callback, activity);
+    }
+
+    @Test
+    public void onCreateFragmentActivity() {
+        LifecycleDispatcher.DispatcherActivityCallback callback =
+                new LifecycleDispatcher.DispatcherActivityCallback();
+        FragmentActivity activity = mock(FragmentActivity.class);
+        FragmentManager fragmentManager = mock(FragmentManager.class);
+        when(activity.getSupportFragmentManager()).thenReturn(fragmentManager);
+
+        checkReportFragment(callback, activity);
+
+        verify(activity).getSupportFragmentManager();
+        verify(fragmentManager).registerFragmentLifecycleCallbacks(
+                any(FragmentManager.FragmentLifecycleCallbacks.class), eq(true));
+    }
+
+    @SuppressLint("CommitTransaction")
+    private void checkReportFragment(LifecycleDispatcher.DispatcherActivityCallback callback,
+            Activity activity) {
+        android.app.FragmentManager fm = mock(android.app.FragmentManager.class);
+        FragmentTransaction transaction = mock(FragmentTransaction.class);
+        when(activity.getFragmentManager()).thenReturn(fm);
+        when(fm.beginTransaction()).thenReturn(transaction);
+        when(transaction.add(any(Fragment.class), anyString())).thenReturn(transaction);
+        callback.onActivityCreated(activity, mock(Bundle.class));
+        verify(activity).getFragmentManager();
+        verify(fm).beginTransaction();
+        verify(transaction).add(any(ReportFragment.class), anyString());
+        verify(transaction).commit();
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java
new file mode 100644
index 0000000..f401e1c
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.lifecycle.util.InstantTaskExecutor;
+import android.support.annotation.Nullable;
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SuppressWarnings({"unchecked"})
+@SmallTest
+public class LiveDataTest {
+    private PublicLiveData<String> mLiveData;
+    private LifecycleOwner mOwner;
+    private LifecycleRegistry mRegistry;
+    private MethodExec mActiveObserversChanged;
+    private boolean mInObserver;
+
+    @Before
+    public void init() {
+        mLiveData = new PublicLiveData<>();
+        mOwner = mock(LifecycleOwner.class);
+        mRegistry = new LifecycleRegistry(mOwner);
+        when(mOwner.getLifecycle()).thenReturn(mRegistry);
+        mActiveObserversChanged = mock(MethodExec.class);
+        mLiveData.activeObserversChanged = mActiveObserversChanged;
+        mInObserver = false;
+    }
+
+    @Before
+    public void swapExecutorDelegate() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
+    }
+
+    @After
+    public void removeExecutorDelegate() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    @Test
+    public void testObserverToggle() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mLiveData.observe(mOwner, observer);
+
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+        assertThat(mLiveData.hasObservers(), is(true));
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+
+        mLiveData.removeObserver(observer);
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+        assertThat(mLiveData.hasObservers(), is(false));
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+    }
+
+    @Test
+    public void testActiveObserverToggle() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mLiveData.observe(mOwner, observer);
+
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+        assertThat(mLiveData.hasObservers(), is(true));
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(mActiveObserversChanged).onCall(true);
+        assertThat(mLiveData.hasActiveObservers(), is(true));
+        reset(mActiveObserversChanged);
+
+        mRegistry.handleLifecycleEvent(ON_STOP);
+        verify(mActiveObserversChanged).onCall(false);
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+        assertThat(mLiveData.hasObservers(), is(true));
+
+        reset(mActiveObserversChanged);
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(mActiveObserversChanged).onCall(true);
+        assertThat(mLiveData.hasActiveObservers(), is(true));
+        assertThat(mLiveData.hasObservers(), is(true));
+
+        reset(mActiveObserversChanged);
+        mLiveData.removeObserver(observer);
+        verify(mActiveObserversChanged).onCall(false);
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+        assertThat(mLiveData.hasObservers(), is(false));
+
+        verifyNoMoreInteractions(mActiveObserversChanged);
+    }
+
+    @Test
+    public void testReAddSameObserverTuple() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mLiveData.observe(mOwner, observer);
+        mLiveData.observe(mOwner, observer);
+        assertThat(mLiveData.hasObservers(), is(true));
+    }
+
+    @Test
+    public void testAdd2ObserversWithSameOwnerAndRemove() {
+        Observer<String> o1 = (Observer<String>) mock(Observer.class);
+        Observer<String> o2 = (Observer<String>) mock(Observer.class);
+        mLiveData.observe(mOwner, o1);
+        mLiveData.observe(mOwner, o2);
+        assertThat(mLiveData.hasObservers(), is(true));
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(mActiveObserversChanged).onCall(true);
+        mLiveData.setValue("a");
+        verify(o1).onChanged("a");
+        verify(o2).onChanged("a");
+
+        mLiveData.removeObservers(mOwner);
+
+        assertThat(mLiveData.hasObservers(), is(false));
+        assertThat(mRegistry.getObserverCount(), is(0));
+    }
+
+    @Test
+    public void testAddSameObserverIn2LifecycleOwners() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        LifecycleOwner owner2 = mock(LifecycleOwner.class);
+        LifecycleRegistry registry2 = new LifecycleRegistry(owner2);
+        when(owner2.getLifecycle()).thenReturn(registry2);
+
+        mLiveData.observe(mOwner, observer);
+        Throwable throwable = null;
+        try {
+            mLiveData.observe(owner2, observer);
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertThat(throwable, instanceOf(IllegalArgumentException.class));
+        //noinspection ConstantConditions
+        assertThat(throwable.getMessage(),
+                is("Cannot add the same observer with different lifecycles"));
+    }
+
+    @Test
+    public void testRemoveDestroyedObserver() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mLiveData.observe(mOwner, observer);
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(mActiveObserversChanged).onCall(true);
+        assertThat(mLiveData.hasObservers(), is(true));
+        assertThat(mLiveData.hasActiveObservers(), is(true));
+
+        reset(mActiveObserversChanged);
+
+        mRegistry.handleLifecycleEvent(ON_DESTROY);
+        assertThat(mLiveData.hasObservers(), is(false));
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+        verify(mActiveObserversChanged).onCall(false);
+    }
+
+    @Test
+    public void testInactiveRegistry() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mRegistry.handleLifecycleEvent(ON_DESTROY);
+        mLiveData.observe(mOwner, observer);
+        assertThat(mLiveData.hasObservers(), is(false));
+    }
+
+    @Test
+    public void testNotifyActiveInactive() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mRegistry.handleLifecycleEvent(ON_CREATE);
+        mLiveData.observe(mOwner, observer);
+        mLiveData.setValue("a");
+        verify(observer, never()).onChanged(anyString());
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(observer).onChanged("a");
+
+        mLiveData.setValue("b");
+        verify(observer).onChanged("b");
+
+        mRegistry.handleLifecycleEvent(ON_STOP);
+        mLiveData.setValue("c");
+        verify(observer, never()).onChanged("c");
+
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(observer).onChanged("c");
+
+        reset(observer);
+        mRegistry.handleLifecycleEvent(ON_STOP);
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(observer, never()).onChanged(anyString());
+    }
+
+    @Test
+    public void testStopObservingOwner_onDestroy() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mRegistry.handleLifecycleEvent(ON_CREATE);
+        mLiveData.observe(mOwner, observer);
+        assertThat(mRegistry.getObserverCount(), is(1));
+        mRegistry.handleLifecycleEvent(ON_DESTROY);
+        assertThat(mRegistry.getObserverCount(), is(0));
+    }
+
+    @Test
+    public void testStopObservingOwner_onStopObserving() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mRegistry.handleLifecycleEvent(ON_CREATE);
+        mLiveData.observe(mOwner, observer);
+        assertThat(mRegistry.getObserverCount(), is(1));
+
+        mLiveData.removeObserver(observer);
+        assertThat(mRegistry.getObserverCount(), is(0));
+    }
+
+    @Test
+    public void testActiveChangeInCallback() {
+        mRegistry.handleLifecycleEvent(ON_START);
+        Observer<String> observer1 = spy(new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                mRegistry.handleLifecycleEvent(ON_STOP);
+                assertThat(mLiveData.hasObservers(), is(true));
+                assertThat(mLiveData.hasActiveObservers(), is(false));
+            }
+        });
+        final Observer observer2 = mock(Observer.class);
+        mLiveData.observe(mOwner, observer1);
+        mLiveData.observe(mOwner, observer2);
+        mLiveData.setValue("bla");
+        verify(observer1).onChanged(anyString());
+        verify(observer2, Mockito.never()).onChanged(anyString());
+        assertThat(mLiveData.hasObservers(), is(true));
+        assertThat(mLiveData.hasActiveObservers(), is(false));
+    }
+
+    @Test
+    public void testActiveChangeInCallback2() {
+        Observer<String> observer1 = spy(new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                assertThat(mInObserver, is(false));
+                mInObserver = true;
+                mRegistry.handleLifecycleEvent(ON_START);
+                assertThat(mLiveData.hasActiveObservers(), is(true));
+                mInObserver = false;
+            }
+        });
+        final Observer observer2 = spy(new FailReentranceObserver());
+        mLiveData.observeForever(observer1);
+        mLiveData.observe(mOwner, observer2);
+        mLiveData.setValue("bla");
+        verify(observer1).onChanged(anyString());
+        verify(observer2).onChanged(anyString());
+        assertThat(mLiveData.hasObservers(), is(true));
+        assertThat(mLiveData.hasActiveObservers(), is(true));
+    }
+
+    @Test
+    public void testObserverRemovalInCallback() {
+        mRegistry.handleLifecycleEvent(ON_START);
+        Observer<String> observer = spy(new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                assertThat(mLiveData.hasObservers(), is(true));
+                mLiveData.removeObserver(this);
+                assertThat(mLiveData.hasObservers(), is(false));
+            }
+        });
+        mLiveData.observe(mOwner, observer);
+        mLiveData.setValue("bla");
+        verify(observer).onChanged(anyString());
+        assertThat(mLiveData.hasObservers(), is(false));
+    }
+
+    @Test
+    public void testObserverAdditionInCallback() {
+        mRegistry.handleLifecycleEvent(ON_START);
+        final Observer observer2 = spy(new FailReentranceObserver());
+        Observer<String> observer1 = spy(new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                assertThat(mInObserver, is(false));
+                mInObserver = true;
+                mLiveData.observe(mOwner, observer2);
+                assertThat(mLiveData.hasObservers(), is(true));
+                assertThat(mLiveData.hasActiveObservers(), is(true));
+                mInObserver = false;
+            }
+        });
+        mLiveData.observe(mOwner, observer1);
+        mLiveData.setValue("bla");
+        verify(observer1).onChanged(anyString());
+        verify(observer2).onChanged(anyString());
+        assertThat(mLiveData.hasObservers(), is(true));
+        assertThat(mLiveData.hasActiveObservers(), is(true));
+    }
+
+    @Test
+    public void testObserverWithoutLifecycleOwner() {
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mLiveData.setValue("boring");
+        mLiveData.observeForever(observer);
+        verify(mActiveObserversChanged).onCall(true);
+        verify(observer).onChanged("boring");
+        mLiveData.setValue("tihs");
+        verify(observer).onChanged("tihs");
+        mLiveData.removeObserver(observer);
+        verify(mActiveObserversChanged).onCall(false);
+        mLiveData.setValue("boring");
+        reset(observer);
+        verify(observer, never()).onChanged(anyString());
+    }
+
+    @Test
+    public void testSetValueDuringSetValue() {
+        mRegistry.handleLifecycleEvent(ON_START);
+        final Observer observer1 = spy(new Observer<String>() {
+            @Override
+            public void onChanged(String o) {
+                assertThat(mInObserver, is(false));
+                mInObserver = true;
+                if (o.equals(("bla"))) {
+                    mLiveData.setValue("gt");
+                }
+                mInObserver = false;
+            }
+        });
+        final Observer observer2 = spy(new FailReentranceObserver());
+        mLiveData.observe(mOwner, observer1);
+        mLiveData.observe(mOwner, observer2);
+        mLiveData.setValue("bla");
+        verify(observer1, Mockito.atMost(2)).onChanged("gt");
+        verify(observer2, Mockito.atMost(2)).onChanged("gt");
+    }
+
+    @Test
+    public void testDataChangeDuringStateChange() {
+        mRegistry.handleLifecycleEvent(ON_START);
+        mRegistry.addObserver(new LifecycleObserver() {
+            @OnLifecycleEvent(ON_STOP)
+            public void onStop() {
+                // change data in onStop, observer should not be called!
+                mLiveData.setValue("b");
+            }
+        });
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        mLiveData.setValue("a");
+        mLiveData.observe(mOwner, observer);
+        verify(observer).onChanged("a");
+        mRegistry.handleLifecycleEvent(ON_PAUSE);
+        mRegistry.handleLifecycleEvent(ON_STOP);
+        verify(observer, never()).onChanged("b");
+
+        mRegistry.handleLifecycleEvent(ON_RESUME);
+        verify(observer).onChanged("b");
+    }
+
+    @Test
+    public void testNotCallInactiveWithObserveForever() {
+        mRegistry.handleLifecycleEvent(ON_START);
+        Observer<String> observer = (Observer<String>) mock(Observer.class);
+        Observer<String> observer2 = (Observer<String>) mock(Observer.class);
+        mLiveData.observe(mOwner, observer);
+        mLiveData.observeForever(observer2);
+        verify(mActiveObserversChanged).onCall(true);
+        reset(mActiveObserversChanged);
+        mRegistry.handleLifecycleEvent(ON_STOP);
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+        mRegistry.handleLifecycleEvent(ON_START);
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    static class PublicLiveData<T> extends LiveData<T> {
+        // cannot spy due to internal calls
+        public MethodExec activeObserversChanged;
+
+        @Override
+        protected void onActive() {
+            if (activeObserversChanged != null) {
+                activeObserversChanged.onCall(true);
+            }
+        }
+
+        @Override
+        protected void onInactive() {
+            if (activeObserversChanged != null) {
+                activeObserversChanged.onCall(false);
+            }
+        }
+    }
+
+    private class FailReentranceObserver<T> implements Observer<T> {
+        @Override
+        public void onChanged(@Nullable T t) {
+            assertThat(mInObserver, is(false));
+        }
+    }
+
+    interface MethodExec {
+        void onCall(boolean value);
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java
new file mode 100644
index 0000000..f522230
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.lifecycle.util.InstantTaskExecutor;
+import android.support.annotation.Nullable;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SuppressWarnings("unchecked")
+@SmallTest
+@RunWith(JUnit4.class)
+public class MediatorLiveDataTest {
+
+    private LifecycleOwner mOwner;
+    private LifecycleRegistry mRegistry;
+    private MediatorLiveData<String> mMediator;
+    private LiveData<String> mSource;
+    private boolean mSourceActive;
+
+    @Before
+    public void setup() {
+        mOwner = mock(LifecycleOwner.class);
+        mRegistry = new LifecycleRegistry(mOwner);
+        when(mOwner.getLifecycle()).thenReturn(mRegistry);
+        mMediator = new MediatorLiveData<>();
+        mSource = new LiveData<String>() {
+            @Override
+            protected void onActive() {
+                mSourceActive = true;
+            }
+
+            @Override
+            protected void onInactive() {
+                mSourceActive = false;
+            }
+        };
+        mSourceActive = false;
+        mMediator.observe(mOwner, mock(Observer.class));
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+    }
+
+    @Before
+    public void swapExecutorDelegate() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
+    }
+
+    @Test
+    public void testSingleDelivery() {
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        mSource.setValue("flatfoot");
+        verify(observer).onChanged("flatfoot");
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        reset(observer);
+        verify(observer, never()).onChanged(any());
+    }
+
+    @Test
+    public void testChangeWhileInactive() {
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        mMediator.observe(mOwner, mock(Observer.class));
+        mSource.setValue("one");
+        verify(observer).onChanged("one");
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        reset(observer);
+        mSource.setValue("flatfoot");
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        verify(observer).onChanged("flatfoot");
+    }
+
+
+    @Test
+    public void testAddSourceToActive() {
+        mSource.setValue("flatfoot");
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        verify(observer).onChanged("flatfoot");
+    }
+
+    @Test
+    public void testAddSourceToInActive() {
+        mSource.setValue("flatfoot");
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        verify(observer, never()).onChanged(any());
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        verify(observer).onChanged("flatfoot");
+    }
+
+    @Test
+    public void testRemoveSource() {
+        mSource.setValue("flatfoot");
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        verify(observer).onChanged("flatfoot");
+        mMediator.removeSource(mSource);
+        reset(observer);
+        mSource.setValue("failure");
+        verify(observer, never()).onChanged(any());
+    }
+
+    @Test
+    public void testSourceInactive() {
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        assertThat(mSourceActive, is(true));
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        assertThat(mSourceActive, is(false));
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        assertThat(mSourceActive, is(true));
+    }
+
+    @Test
+    public void testNoLeakObserver() {
+        // Imitates a destruction of a ViewModel: a listener of LiveData is destroyed,
+        // a reference to MediatorLiveData is cleaned up. In this case we shouldn't leak
+        // MediatorLiveData as an observer of mSource.
+        assertThat(mSource.hasObservers(), is(false));
+        Observer observer = mock(Observer.class);
+        mMediator.addSource(mSource, observer);
+        assertThat(mSource.hasObservers(), is(true));
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
+        mMediator = null;
+        assertThat(mSource.hasObservers(), is(false));
+    }
+
+    @Test
+    public void testMultipleSources() {
+        Observer observer1 = mock(Observer.class);
+        mMediator.addSource(mSource, observer1);
+        MutableLiveData<Integer> source2 = new MutableLiveData<>();
+        Observer observer2 = mock(Observer.class);
+        mMediator.addSource(source2, observer2);
+        mSource.setValue("flatfoot");
+        verify(observer1).onChanged("flatfoot");
+        verify(observer2, never()).onChanged(any());
+        reset(observer1, observer2);
+        source2.setValue(1703);
+        verify(observer1, never()).onChanged(any());
+        verify(observer2).onChanged(1703);
+        reset(observer1, observer2);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        mSource.setValue("failure");
+        source2.setValue(0);
+        verify(observer1, never()).onChanged(any());
+        verify(observer2, never()).onChanged(any());
+    }
+
+    @Test
+    public void removeSourceDuringOnActive() {
+        // to trigger ConcurrentModificationException,
+        // we have to call remove from a collection during "for" loop.
+        // ConcurrentModificationException is thrown from next() method of an iterator
+        // so this modification shouldn't be at the last iteration,
+        // because if it is a last iteration, then next() wouldn't be called.
+        // And the last: an order of an iteration over sources is not defined,
+        // so I have to call it remove operation  from all observers.
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        Observer<String> removingObserver = new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                mMediator.removeSource(mSource);
+            }
+        };
+        mMediator.addSource(mSource, removingObserver);
+        MutableLiveData<String> source2 = new MutableLiveData<>();
+        source2.setValue("nana");
+        mMediator.addSource(source2, removingObserver);
+        mSource.setValue("petjack");
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void reAddSameSourceWithDifferentObserver() {
+        mMediator.addSource(mSource, mock(Observer.class));
+        mMediator.addSource(mSource, mock(Observer.class));
+    }
+
+    @Test
+    public void addSourceDuringOnActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        mSource.setValue("a");
+        mMediator.addSource(mSource, new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                MutableLiveData<String> source = new MutableLiveData<>();
+                source.setValue("b");
+                mMediator.addSource(source, new Observer<String>() {
+                    @Override
+                    public void onChanged(@Nullable String s) {
+                        mMediator.setValue("c");
+                    }
+                });
+            }
+        });
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        assertThat(mMediator.getValue(), is("c"));
+    }
+
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/ThreadedLiveDataTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ThreadedLiveDataTest.java
new file mode 100644
index 0000000..e578546
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ThreadedLiveDataTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.arch.core.executor.JunitTaskExecutorRule;
+import android.arch.core.executor.TaskExecutor;
+import android.support.annotation.Nullable;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+public class ThreadedLiveDataTest {
+
+    private static final int TIMEOUT_SECS = 3;
+
+    @Rule
+    public JunitTaskExecutorRule mTaskExecutorRule = new JunitTaskExecutorRule(1, false);
+
+    private LiveData<String> mLiveData;
+    private LifecycleOwner mLifecycleOwner;
+    private LifecycleRegistry mRegistry;
+
+    @Before
+    public void init() {
+        mLiveData = new MutableLiveData<>();
+        mLifecycleOwner = mock(LifecycleOwner.class);
+        mRegistry = new LifecycleRegistry(mLifecycleOwner);
+        when(mLifecycleOwner.getLifecycle()).thenReturn(mRegistry);
+    }
+
+    @Test
+    public void testPostValue() throws InterruptedException {
+        final TaskExecutor taskExecutor = mTaskExecutorRule.getTaskExecutor();
+        final CountDownLatch finishTestLatch = new CountDownLatch(1);
+        final Observer<String> observer = new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String newValue) {
+                try {
+                    assertThat(taskExecutor.isMainThread(), is(true));
+                    assertThat(newValue, is("success"));
+                } finally {
+                    finishTestLatch.countDown();
+                }
+            }
+        };
+        taskExecutor.executeOnMainThread(new Runnable() {
+            @Override
+            public void run() {
+                mRegistry.handleLifecycleEvent(ON_START);
+                mLiveData.observe(mLifecycleOwner, observer);
+                final CountDownLatch latch = new CountDownLatch(1);
+                taskExecutor.executeOnDiskIO(new Runnable() {
+                    @Override
+                    public void run() {
+                        mLiveData.postValue("fail");
+                        mLiveData.postValue("success");
+                        latch.countDown();
+                    }
+                });
+                try {
+                    assertThat(latch.await(TIMEOUT_SECS, TimeUnit.SECONDS), is(true));
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        assertThat(finishTestLatch.await(TIMEOUT_SECS, TimeUnit.SECONDS), is(true));
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/TransformationsTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/TransformationsTest.java
new file mode 100644
index 0000000..8c808ed
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/TransformationsTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.util.Function;
+import android.arch.lifecycle.util.InstantTaskExecutor;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SuppressWarnings("unchecked")
+@SmallTest
+@RunWith(JUnit4.class)
+public class TransformationsTest {
+
+    private LifecycleOwner mOwner;
+
+    @Before
+    public void swapExecutorDelegate() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
+    }
+
+    @Before
+    public void setup() {
+        mOwner = mock(LifecycleOwner.class);
+        LifecycleRegistry registry = new LifecycleRegistry(mOwner);
+        when(mOwner.getLifecycle()).thenReturn(registry);
+        registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+    }
+
+    @Test
+    public void testMap() {
+        LiveData<String> source = new MutableLiveData<>();
+        LiveData<Integer> mapped = Transformations.map(source, new Function<String, Integer>() {
+            @Override
+            public Integer apply(String input) {
+                return input.length();
+            }
+        });
+        Observer<Integer> observer = mock(Observer.class);
+        mapped.observe(mOwner, observer);
+        source.setValue("four");
+        verify(observer).onChanged(4);
+    }
+
+    @Test
+    public void testSwitchMap() {
+        LiveData<Integer> trigger = new MutableLiveData<>();
+        final LiveData<String> first = new MutableLiveData<>();
+        final LiveData<String> second = new MutableLiveData<>();
+        LiveData<String> result = Transformations.switchMap(trigger,
+                new Function<Integer, LiveData<String>>() {
+                    @Override
+                    public LiveData<String> apply(Integer input) {
+                        if (input == 1) {
+                            return first;
+                        } else {
+                            return second;
+                        }
+                    }
+                });
+
+        Observer<String> observer = mock(Observer.class);
+        result.observe(mOwner, observer);
+        verify(observer, never()).onChanged(anyString());
+        first.setValue("first");
+        trigger.setValue(1);
+        verify(observer).onChanged("first");
+        second.setValue("second");
+        reset(observer);
+        verify(observer, never()).onChanged(anyString());
+        trigger.setValue(2);
+        verify(observer).onChanged("second");
+        reset(observer);
+        first.setValue("failure");
+        verify(observer, never()).onChanged(anyString());
+    }
+
+    @Test
+    public void testSwitchMap2() {
+        LiveData<Integer> trigger = new MutableLiveData<>();
+        final LiveData<String> first = new MutableLiveData<>();
+        final LiveData<String> second = new MutableLiveData<>();
+        LiveData<String> result = Transformations.switchMap(trigger,
+                new Function<Integer, LiveData<String>>() {
+                    @Override
+                    public LiveData<String> apply(Integer input) {
+                        if (input == 1) {
+                            return first;
+                        } else {
+                            return second;
+                        }
+                    }
+                });
+
+        Observer<String> observer = mock(Observer.class);
+        result.observe(mOwner, observer);
+
+        verify(observer, never()).onChanged(anyString());
+        trigger.setValue(1);
+        verify(observer, never()).onChanged(anyString());
+        first.setValue("fi");
+        verify(observer).onChanged("fi");
+        first.setValue("rst");
+        verify(observer).onChanged("rst");
+
+        second.setValue("second");
+        reset(observer);
+        verify(observer, never()).onChanged(anyString());
+        trigger.setValue(2);
+        verify(observer).onChanged("second");
+        reset(observer);
+        first.setValue("failure");
+        verify(observer, never()).onChanged(anyString());
+    }
+
+    @Test
+    public void testNoRedispatchSwitchMap() {
+        LiveData<Integer> trigger = new MutableLiveData<>();
+        final LiveData<String> first = new MutableLiveData<>();
+        LiveData<String> result = Transformations.switchMap(trigger,
+                new Function<Integer, LiveData<String>>() {
+                    @Override
+                    public LiveData<String> apply(Integer input) {
+                        return first;
+                    }
+                });
+
+        Observer<String> observer = mock(Observer.class);
+        result.observe(mOwner, observer);
+        verify(observer, never()).onChanged(anyString());
+        first.setValue("first");
+        trigger.setValue(1);
+        verify(observer).onChanged("first");
+        reset(observer);
+        trigger.setValue(2);
+        verify(observer, never()).onChanged(anyString());
+    }
+
+    @Test
+    public void testSwitchMapToNull() {
+        LiveData<Integer> trigger = new MutableLiveData<>();
+        final LiveData<String> first = new MutableLiveData<>();
+        LiveData<String> result = Transformations.switchMap(trigger,
+                new Function<Integer, LiveData<String>>() {
+                    @Override
+                    public LiveData<String> apply(Integer input) {
+                        if (input == 1) {
+                            return first;
+                        } else {
+                            return null;
+                        }
+                    }
+                });
+
+        Observer<String> observer = mock(Observer.class);
+        result.observe(mOwner, observer);
+        verify(observer, never()).onChanged(anyString());
+        first.setValue("first");
+        trigger.setValue(1);
+        verify(observer).onChanged("first");
+        reset(observer);
+
+        trigger.setValue(2);
+        verify(observer, never()).onChanged(anyString());
+        assertThat(first.hasObservers(), is(false));
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java
new file mode 100644
index 0000000..61760fc
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ViewModelProviderTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.lifecycle.ViewModelProvider.NewInstanceFactory;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ViewModelProviderTest {
+
+    private ViewModelProvider mViewModelProvider;
+
+    @Before
+    public void setup() {
+        mViewModelProvider = new ViewModelProvider(new ViewModelStore(), new NewInstanceFactory());
+    }
+
+    @Test
+    public void twoViewModelsWithSameKey() throws Throwable {
+        String key = "the_key";
+        ViewModel1 vm1 = mViewModelProvider.get(key, ViewModel1.class);
+        assertThat(vm1.mCleared, is(false));
+        ViewModel2 vw2 = mViewModelProvider.get(key, ViewModel2.class);
+        assertThat(vw2, notNullValue());
+        assertThat(vm1.mCleared, is(true));
+    }
+
+
+    @Test
+    public void localViewModel() throws Throwable {
+        class VM extends ViewModel1 {
+        }
+        try {
+            mViewModelProvider.get(VM.class);
+            Assert.fail();
+        } catch (IllegalArgumentException ignored) {
+        }
+    }
+
+    @Test
+    public void twoViewModels() {
+        ViewModel1 model1 = mViewModelProvider.get(ViewModel1.class);
+        ViewModel2 model2 = mViewModelProvider.get(ViewModel2.class);
+        assertThat(mViewModelProvider.get(ViewModel1.class), is(model1));
+        assertThat(mViewModelProvider.get(ViewModel2.class), is(model2));
+    }
+
+    @Test
+    public void testOwnedBy() {
+        final ViewModelStore store = new ViewModelStore();
+        ViewModelStoreOwner owner = new ViewModelStoreOwner() {
+            @Override
+            public ViewModelStore getViewModelStore() {
+                return store;
+            }
+        };
+        ViewModelProvider provider = new ViewModelProvider(owner, new NewInstanceFactory());
+        ViewModel1 viewModel = provider.get(ViewModel1.class);
+        assertThat(viewModel, is(provider.get(ViewModel1.class)));
+    }
+
+    public static class ViewModel1 extends ViewModel {
+        boolean mCleared;
+
+        @Override
+        protected void onCleared() {
+            mCleared = true;
+        }
+    }
+
+    public static class ViewModel2 extends ViewModel {
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/ViewModelStoreTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ViewModelStoreTest.java
new file mode 100644
index 0000000..cfeefb7
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/ViewModelStoreTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ViewModelStoreTest {
+
+    @Test
+    public void testClear() {
+        ViewModelStore store = new ViewModelStore();
+        TestViewModel viewModel1 = new TestViewModel();
+        TestViewModel viewModel2 = new TestViewModel();
+        store.put("a", viewModel1);
+        store.put("b", viewModel2);
+        assertThat(viewModel1.mCleared, is(false));
+        assertThat(viewModel2.mCleared, is(false));
+        store.clear();
+        assertThat(viewModel1.mCleared, is(true));
+        assertThat(viewModel2.mCleared, is(true));
+        assertThat(store.get("a"), nullValue());
+        assertThat(store.get("b"), nullValue());
+    }
+
+    static class TestViewModel extends ViewModel {
+        boolean mCleared = false;
+
+        @Override
+        protected void onCleared() {
+            mCleared = true;
+        }
+    }
+}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/util/InstantTaskExecutor.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/util/InstantTaskExecutor.java
new file mode 100644
index 0000000..52318fd
--- /dev/null
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/util/InstantTaskExecutor.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.util;
+
+import android.arch.core.executor.TaskExecutor;
+
+public class InstantTaskExecutor extends TaskExecutor {
+    @Override
+    public void executeOnDiskIO(Runnable runnable) {
+        runnable.run();
+    }
+
+    @Override
+    public void postToMainThread(Runnable runnable) {
+        runnable.run();
+    }
+
+    @Override
+    public boolean isMainThread() {
+        return true;
+    }
+}
diff --git a/lifecycle/gradle.properties b/lifecycle/gradle.properties
new file mode 100644
index 0000000..88d2a75
--- /dev/null
+++ b/lifecycle/gradle.properties
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+
+org.gradle.jvmargs=-Xmx3000M
\ No newline at end of file
diff --git a/lifecycle/gradle/wrapper/gradle-wrapper.jar b/lifecycle/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
--- /dev/null
+++ b/lifecycle/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/lifecycle/gradle/wrapper/gradle-wrapper.properties b/lifecycle/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..467c103
--- /dev/null
+++ b/lifecycle/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-all.zip
diff --git a/lifecycle/gradlew b/lifecycle/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/lifecycle/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/lifecycle/gradlew.bat b/lifecycle/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/lifecycle/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/lifecycle/integration-tests/test-app/src/androidTest/java/com/android/support/lifecycle/LiveDataTransactionTest.java b/lifecycle/integration-tests/test-app/src/androidTest/java/com/android/support/lifecycle/LiveDataTransactionTest.java
new file mode 100644
index 0000000..443662a
--- /dev/null
+++ b/lifecycle/integration-tests/test-app/src/androidTest/java/com/android/support/lifecycle/LiveDataTransactionTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package com.android.support.lifecycle;
+
+import static android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks;
+
+import static com.android.support.lifecycle.testapp.LiveDataTestActivity.LIVE_DATA_VALUE;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+
+import com.android.support.lifecycle.testapp.LiveDataTestActivity;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+@SmallTest
+public class LiveDataTransactionTest {
+
+    @Rule
+    public ActivityTestRule<LiveDataTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(LiveDataTestActivity.class);
+
+    private boolean mVisited;
+
+    @Test
+    public void transactionInOnStateChanged() throws Throwable {
+        LiveDataTestActivity activity = mActivityTestRule.getActivity();
+
+        activity.getSupportFragmentManager().registerFragmentLifecycleCallbacks(
+                new FragmentLifecycleCallbacks() {
+                    @Override
+                    public void onFragmentCreated(FragmentManager fm, Fragment f,
+                            Bundle savedInstanceState) {
+                    }
+                }, true);
+        mActivityTestRule.runOnUiThread(() -> {
+            assertThat(activity.fragmentsNumber,  /** 2^MAX_DEPTH - 1 */ is(31));
+            activity.viewModel.liveData.observe(activity,
+                    s -> Assert.fail("savedInstance state triggered an update"));
+        });
+        LiveDataTestActivity newActivity = TestUtils.recreateActivity(activity,
+                mActivityTestRule);
+        TestUtils.waitTillResumed(newActivity, mActivityTestRule);
+        mActivityTestRule.runOnUiThread(() -> {
+            newActivity.viewModel.liveData.observe(newActivity,
+                    s -> {
+                        assertThat(s, is(LIVE_DATA_VALUE));
+                        mVisited = true;
+                    });
+            assertThat(newActivity.fragmentsNumber, /** 2 * (2^MAX_DEPTH - 1) + 1 */is(63));
+            assertThat(mVisited, is(true));
+        });
+    }
+
+}
diff --git a/lifecycle/integration-tests/test-app/src/main/java/com/android/support/lifecycle/testapp/LiveDataTestActivity.java b/lifecycle/integration-tests/test-app/src/main/java/com/android/support/lifecycle/testapp/LiveDataTestActivity.java
new file mode 100644
index 0000000..5bbd368
--- /dev/null
+++ b/lifecycle/integration-tests/test-app/src/main/java/com/android/support/lifecycle/testapp/LiveDataTestActivity.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package com.android.support.lifecycle.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import com.android.support.lifecycle.LifecycleActivity;
+import com.android.support.lifecycle.LifecycleFragment;
+import com.android.support.lifecycle.LifecycleProvider;
+import com.android.support.lifecycle.LiveData;
+import com.android.support.lifecycle.ViewModel;
+import com.android.support.lifecycle.ViewModelStore;
+
+/**
+ * activity for LiveDataTransactionTest
+ */
+public class LiveDataTestActivity extends LifecycleActivity {
+
+    public static final String LIVE_DATA_VALUE = "saveInstanceState";
+    private static final int MAX_DEPTH = 5;
+    private static final String VM_TAG = "test";
+
+    /** view model*/
+    public LiveDataViewModel viewModel;
+    /** counter of created  */
+    public int fragmentsNumber;
+
+    /** ViewModel class */
+    public static class LiveDataViewModel extends ViewModel {
+        public LiveData<String> liveData = new LiveData<>();
+    }
+
+    /** Counting Fragment */
+    public static class CountingFragment extends LifecycleFragment {
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            ((LiveDataTestActivity) getActivity()).fragmentsNumber++;
+        }
+    }
+
+    /** a fragment which injects new fragment on new value of livedata */
+    public static class InternalFragment extends CountingFragment {
+
+        int mDepth = MAX_DEPTH;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            LiveDataViewModel liveDataViewModel = ViewModelStore.get(
+                    (LifecycleProvider) getActivity(), VM_TAG, LiveDataViewModel.class);
+            liveDataViewModel.liveData.observe(this, s ->
+                    getChildFragmentManager().beginTransaction().add(new CountingFragment(),
+                            s).commitNow());
+
+            if (mDepth == MAX_DEPTH) {
+                return;
+            }
+            InternalFragment aFragment = new InternalFragment();
+            aFragment.mDepth = mDepth + 1;
+            InternalFragment bFragment = new InternalFragment();
+            bFragment.mDepth = mDepth + 1;
+            getChildFragmentManager().beginTransaction()
+                    .add(aFragment, getTag() + "_" + mDepth + "_a")
+                    .add(bFragment, getTag() + "_" + mDepth + "_b")
+                    .commitNow();
+        }
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        viewModel = ViewModelStore.get(this, VM_TAG, LiveDataViewModel.class);
+        viewModel.liveData.observe(this, s ->
+                getSupportFragmentManager().beginTransaction().add(new CountingFragment(),
+                        s).commit());
+        String tag = "0_a";
+        if (getSupportFragmentManager().findFragmentByTag(tag) == null) {
+            InternalFragment internalFragment = new InternalFragment();
+            internalFragment.mDepth = 1;
+            getSupportFragmentManager().beginTransaction().add(internalFragment, tag).commitNow();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        viewModel.liveData.setValue(LIVE_DATA_VALUE);
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/.gitignore b/lifecycle/integration-tests/testapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/lifecycle/integration-tests/testapp/build.gradle b/lifecycle/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..b0da333
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/build.gradle
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+project.ext.noDocs = true
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        applicationId "android.arch.lifecycle.testapp"
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    buildTypes {
+        // test coverage does not work w/ jack
+        debug {
+            testCoverageEnabled = false
+        }
+        release {
+            testCoverageEnabled = false
+        }
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    // IJ canont figure out transitive dependencies so need to declare them.
+    compile project(":lifecycle:common")
+    compile project(":lifecycle:runtime")
+    compile project(":lifecycle:extensions")
+    annotationProcessor project(":lifecycle:compiler")
+    androidTestAnnotationProcessor project(":lifecycle:compiler")
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    testCompile libs.junit
+}
+createAndroidCheckstyle(project)
+
+tasks['check'].dependsOn(tasks['connectedCheck'])
+
+uploadArchives.enabled = false
diff --git a/lifecycle/integration-tests/testapp/proguard-rules.pro b/lifecycle/integration-tests/testapp/proguard-rules.pro
new file mode 100644
index 0000000..575c04e
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/sergeyv/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java
new file mode 100644
index 0000000..ee4e661
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import android.app.Activity;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.testapp.CollectingActivity;
+import android.arch.lifecycle.testapp.FrameworkLifecycleRegistryActivity;
+import android.arch.lifecycle.testapp.FullLifecycleTestActivity;
+import android.arch.lifecycle.testapp.SupportLifecycleRegistryActivity;
+import android.arch.lifecycle.testapp.TestEvent;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.util.Pair;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class ActivityFullLifecycleTest {
+    @Rule
+    public ActivityTestRule activityTestRule =
+            new ActivityTestRule<>(FullLifecycleTestActivity.class);
+
+    @Parameterized.Parameters
+    public static Class[] params() {
+        return new Class[]{FullLifecycleTestActivity.class,
+                SupportLifecycleRegistryActivity.class,
+                FrameworkLifecycleRegistryActivity.class};
+    }
+
+    public ActivityFullLifecycleTest(Class<? extends Activity> activityClass) {
+        //noinspection unchecked
+        activityTestRule = new ActivityTestRule(activityClass);
+    }
+
+
+    @Test
+    public void testFullLifecycle() throws InterruptedException {
+        Activity activity = activityTestRule.getActivity();
+        List<Pair<TestEvent, Event>> results = ((CollectingActivity) activity)
+                .waitForCollectedEvents();
+
+        Event[] expectedEvents =
+                new Event[]{ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
+
+        List<Pair<TestEvent, Event>> expected = new ArrayList<>();
+        boolean beforeResume = true;
+        for (Event i : expectedEvents) {
+            if (beforeResume) {
+                expected.add(new Pair<>(ACTIVITY_CALLBACK, i));
+                expected.add(new Pair<>(LIFECYCLE_EVENT, i));
+            } else {
+                expected.add(new Pair<>(LIFECYCLE_EVENT, i));
+                expected.add(new Pair<>(ACTIVITY_CALLBACK, i));
+            }
+            if (i == ON_RESUME) {
+                beforeResume = false;
+            }
+        }
+        assertThat(results, is(expected));
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java
new file mode 100644
index 0000000..3397f5f
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.TestUtils.recreateActivity;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.iterableWithSize;
+
+import static java.util.Arrays.asList;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.testapp.EmptyActivity;
+import android.arch.lifecycle.testapp.R;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentInBackStackLifecycleTest {
+    @Rule
+    public ActivityTestRule<EmptyActivity> activityTestRule = new ActivityTestRule<>(
+            EmptyActivity.class);
+
+    @Test
+    public void test() throws Throwable {
+        final ArrayList<Event> collectedEvents = new ArrayList<>();
+        LifecycleObserver collectingObserver = new LifecycleObserver() {
+            @OnLifecycleEvent(Event.ON_ANY)
+            void onAny(LifecycleOwner owner, Event event) {
+                collectedEvents.add(event);
+            }
+        };
+        final FragmentActivity activity = activityTestRule.getActivity();
+        activityTestRule.runOnUiThread(() -> {
+            FragmentManager fm = activity.getSupportFragmentManager();
+            LifecycleFragment fragment = new LifecycleFragment();
+            fm.beginTransaction().add(R.id.fragment_container, fragment, "tag").addToBackStack(null)
+                    .commit();
+            fm.executePendingTransactions();
+
+            fragment.getLifecycle().addObserver(collectingObserver);
+            LifecycleFragment fragment2 = new LifecycleFragment();
+            fm.beginTransaction().replace(R.id.fragment_container, fragment2).addToBackStack(null)
+                    .commit();
+            fm.executePendingTransactions();
+            assertThat(collectedEvents, is(asList(ON_CREATE, ON_START, ON_RESUME,
+                    ON_PAUSE, ON_STOP)));
+            collectedEvents.clear();
+        });
+        EmptyActivity newActivity = recreateActivity(activityTestRule.getActivity(),
+                activityTestRule);
+
+        assertThat(collectedEvents, is(asList(ON_DESTROY)));
+        collectedEvents.clear();
+        EmptyActivity lastActivity = recreateActivity(newActivity, activityTestRule);
+        activityTestRule.runOnUiThread(() -> {
+            FragmentManager fm = lastActivity.getSupportFragmentManager();
+            LifecycleFragment fragment = (LifecycleFragment) fm.findFragmentByTag("tag");
+            fragment.getLifecycle().addObserver(collectingObserver);
+            assertThat(collectedEvents, iterableWithSize(0));
+            fm.popBackStackImmediate();
+            assertThat(collectedEvents, is(asList(ON_CREATE, ON_START, ON_RESUME)));
+        });
+    }
+
+
+}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java
new file mode 100644
index 0000000..e80e11c
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.TestUtils.waitTillResumed;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import android.app.Instrumentation;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.testapp.NavigationDialogActivity;
+import android.arch.lifecycle.testapp.NavigationTestActivityFirst;
+import android.arch.lifecycle.testapp.NavigationTestActivitySecond;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProcessOwnerTest {
+
+    @Rule
+    public ActivityTestRule<NavigationTestActivityFirst> activityTestRule =
+            new ActivityTestRule<>(NavigationTestActivityFirst.class);
+
+    static class ProcessObserver implements LifecycleObserver {
+        volatile boolean mChangedState;
+
+        @OnLifecycleEvent(Event.ON_ANY)
+        void onEvent() {
+            mChangedState = true;
+        }
+    }
+
+    private ProcessObserver mObserver = new ProcessObserver();
+
+    @After
+    public void tearDown() {
+        try {
+            // reassure that our observer is removed.
+            removeProcessObserver(mObserver);
+        } catch (Throwable throwable) {
+            throwable.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testNavigation() throws Throwable {
+        LifecycleActivity firstActivity = setupObserverOnResume();
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                NavigationTestActivitySecond.class.getCanonicalName(), null, false);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.addMonitor(monitor);
+
+        Intent intent = new Intent(firstActivity, NavigationTestActivitySecond.class);
+        firstActivity.finish();
+        firstActivity.startActivity(intent);
+
+        LifecycleActivity secondActivity = (LifecycleActivity) monitor.waitForActivity();
+        assertThat("Failed to navigate", secondActivity, notNullValue());
+        checkProcessObserverSilent(secondActivity);
+    }
+
+    @Test
+    public void testRecreation() throws Throwable {
+        LifecycleActivity activity = setupObserverOnResume();
+        LifecycleActivity recreated = TestUtils.recreateActivity(activity, activityTestRule);
+        assertThat("Failed to recreate", recreated, notNullValue());
+        checkProcessObserverSilent(recreated);
+    }
+
+    @Test
+    public void testPressHomeButton() throws Throwable {
+        setupObserverOnResume();
+
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                NavigationDialogActivity.class.getCanonicalName(), null, false);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.addMonitor(monitor);
+
+        NavigationTestActivityFirst activity = activityTestRule.getActivity();
+        activity.startActivity(new Intent(activity, NavigationDialogActivity.class));
+        LifecycleActivity dialogActivity = (LifecycleActivity) monitor.waitForActivity();
+        checkProcessObserverSilent(dialogActivity);
+
+        List<Event> events = Collections.synchronizedList(new ArrayList<>());
+
+        LifecycleObserver collectingObserver = new LifecycleObserver() {
+            @OnLifecycleEvent(Event.ON_ANY)
+            public void onStateChanged(LifecycleOwner provider, Event event) {
+                events.add(event);
+            }
+        };
+        addProcessObserver(collectingObserver);
+        events.clear();
+        assertThat(activity.moveTaskToBack(true), is(true));
+        Thread.sleep(ProcessLifecycleOwner.TIMEOUT_MS * 2);
+        assertThat(events.toArray(), is(new Event[]{ON_PAUSE, ON_STOP}));
+        events.clear();
+        Context context = InstrumentationRegistry.getContext();
+        context.startActivity(new Intent(activity, NavigationDialogActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        waitTillResumed(dialogActivity, activityTestRule);
+        assertThat(events.toArray(), is(new Event[]{ON_START, ON_RESUME}));
+        removeProcessObserver(collectingObserver);
+        dialogActivity.finish();
+    }
+
+    private LifecycleActivity setupObserverOnResume() throws Throwable {
+        LifecycleActivity firstActivity = activityTestRule.getActivity();
+        waitTillResumed(firstActivity, activityTestRule);
+        addProcessObserver(mObserver);
+        mObserver.mChangedState = false;
+        return firstActivity;
+    }
+
+    private void addProcessObserver(LifecycleObserver observer) throws Throwable {
+        activityTestRule.runOnUiThread(() ->
+                ProcessLifecycleOwner.get().getLifecycle().addObserver(observer));
+    }
+
+    private void removeProcessObserver(LifecycleObserver observer) throws Throwable {
+        activityTestRule.runOnUiThread(() ->
+                ProcessLifecycleOwner.get().getLifecycle().removeObserver(observer));
+    }
+
+    private void checkProcessObserverSilent(LifecycleActivity activity) throws Throwable {
+        waitTillResumed(activity, activityTestRule);
+        assertThat(mObserver.mChangedState, is(false));
+        activityTestRule.runOnUiThread(() ->
+                ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/SimpleAppFullLifecycleTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/SimpleAppFullLifecycleTest.java
new file mode 100644
index 0000000..44d1e8a
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/SimpleAppFullLifecycleTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isIn;
+import static org.hamcrest.Matchers.notNullValue;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.Lifecycle.State;
+import android.arch.lifecycle.testapp.SimpleAppLifecycleTestActivity;
+import android.arch.lifecycle.testapp.SimpleAppLifecycleTestActivity.TestEventType;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class SimpleAppFullLifecycleTest {
+
+    @SuppressWarnings("unchecked")
+    private static final Pair[] EXPECTED_EVENTS_CONSTRUCTION =
+            new Pair[] {
+                new Pair(TestEventType.PROCESS_EVENT, Event.ON_CREATE),
+                new Pair(TestEventType.ACTIVITY_EVENT, Event.ON_CREATE),
+                new Pair(TestEventType.PROCESS_EVENT, Event.ON_START),
+                new Pair(TestEventType.ACTIVITY_EVENT, Event.ON_START),
+                new Pair(TestEventType.PROCESS_EVENT, Event.ON_RESUME),
+                new Pair(TestEventType.ACTIVITY_EVENT, Event.ON_RESUME),
+            };
+
+    @SuppressWarnings("unchecked")
+    private static final Pair[] EXPECTED_EVENTS_DESTRUCTION =
+            new Pair[]{
+
+                    new Pair(TestEventType.ACTIVITY_EVENT, Event.ON_PAUSE),
+                    new Pair(TestEventType.ACTIVITY_EVENT, Event.ON_STOP),
+                    new Pair(TestEventType.ACTIVITY_EVENT, Event.ON_DESTROY),
+
+                    new Pair(TestEventType.PROCESS_EVENT, Event.ON_PAUSE),
+                    new Pair(TestEventType.PROCESS_EVENT, Event.ON_STOP),
+            };
+    @Rule
+    public ActivityTestRule<SimpleAppLifecycleTestActivity> activityTestRule =
+            new ActivityTestRule<>(SimpleAppLifecycleTestActivity.class, false, false);
+
+    @Before
+    public void setup() {
+        // cool down period, so application state will become DESTROYED
+        try {
+            Thread.sleep(ProcessLifecycleOwner.TIMEOUT_MS * 2);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        SimpleAppLifecycleTestActivity.startProcessObserver();
+    }
+
+    @After
+    public void tearDown() {
+        SimpleAppLifecycleTestActivity.stopProcessObserver();
+    }
+
+    @Test
+    public void testFullLifecycle() throws InterruptedException {
+        State currentState = ProcessLifecycleOwner.get().getLifecycle().getCurrentState();
+        assertThat(currentState, is(CREATED));
+        activityTestRule.launchActivity(null);
+        List<Pair<TestEventType, Event>> events = SimpleAppLifecycleTestActivity.awaitForEvents();
+        assertThat("Failed to await for events", events, notNullValue());
+        //noinspection ConstantConditions
+        assertThat(events.subList(0, 6).toArray(), is(EXPECTED_EVENTS_CONSTRUCTION));
+
+        // TODO: bug 35122523
+        for (Pair<TestEventType, Event> event: events.subList(6, 11)) {
+            assertThat(event, isIn(EXPECTED_EVENTS_DESTRUCTION));
+        }
+    }
+
+}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/SynchronousActivityLifecycleTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/SynchronousActivityLifecycleTest.java
new file mode 100644
index 0000000..141d612
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/SynchronousActivityLifecycleTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Instrumentation;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.testapp.LifecycleTestActivity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.UiThreadTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Method;
+
+/**
+ * It tests that an event is dispatched immediately after a call of corresponding OnXXX method
+ * during an execution of performXXX
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SynchronousActivityLifecycleTest {
+
+    @Rule
+    public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();
+
+    @Test
+    public void testOnCreateCall() throws Throwable {
+        testSynchronousCall(Event.ON_CREATE,
+                activity -> {
+                },
+                activity -> getInstrumentation().callActivityOnCreate(activity, null));
+    }
+
+    @Test
+    public void testOnStartCall() throws Throwable {
+        testSynchronousCall(Lifecycle.Event.ON_START,
+                activity -> getInstrumentation().callActivityOnCreate(activity, null),
+                SynchronousActivityLifecycleTest::performStart);
+    }
+
+    @Test
+    public void testOnResumeCall() throws Throwable {
+        testSynchronousCall(Lifecycle.Event.ON_RESUME,
+                activity -> {
+                    getInstrumentation().callActivityOnCreate(activity, null);
+                    performStart(activity);
+                },
+                SynchronousActivityLifecycleTest::performResume);
+    }
+
+    @Test
+    public void testOnStopCall() throws Throwable {
+        testSynchronousCall(Lifecycle.Event.ON_STOP,
+                activity -> {
+                    getInstrumentation().callActivityOnCreate(activity, null);
+                    performStart(activity);
+                },
+                SynchronousActivityLifecycleTest::performStop);
+    }
+
+    @Test
+    public void testOnDestroyCall() throws Throwable {
+        testSynchronousCall(Lifecycle.Event.ON_DESTROY,
+                activity -> getInstrumentation().callActivityOnCreate(activity, null),
+                activity -> getInstrumentation().callActivityOnDestroy(activity));
+    }
+
+    public void testSynchronousCall(Event event, ActivityCall preInit, ActivityCall call)
+            throws Throwable {
+        uiThreadTestRule.runOnUiThread(() -> {
+            Intent intent = new Intent();
+            ComponentName cn = new ComponentName(LifecycleTestActivity.class.getPackage().getName(),
+                    LifecycleTestActivity.class.getName());
+            intent.setComponent(cn);
+            Instrumentation instrumentation = getInstrumentation();
+            try {
+                Application app =
+                        (Application) instrumentation.getTargetContext().getApplicationContext();
+                LifecycleTestActivity testActivity =
+                        (LifecycleTestActivity) instrumentation.newActivity(
+                                LifecycleTestActivity.class, instrumentation.getTargetContext(),
+                                null, app, intent, new ActivityInfo(), "bla", null, null, null);
+                preInit.call(testActivity);
+                TestObserver testObserver = new TestObserver(testActivity, event);
+                testActivity.getLifecycle().addObserver(testObserver);
+                testObserver.unmute();
+                call.call(testActivity);
+
+                assertThat(testObserver.mEventReceived, is(true));
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        });
+    }
+
+    // Instrumentation.callOnActivityCreate calls performCreate on mActivity,
+    // but Instrumentation.callOnActivityStart calls onStart instead of performStart. ¯\_(ツ)_/¯
+    private static void performStart(Activity activity) {
+        try {
+            Method m = Activity.class.getDeclaredMethod("performStart");
+            m.setAccessible(true);
+            m.invoke(activity);
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    private static void performResume(Activity activity) {
+        try {
+            Method m = Activity.class.getDeclaredMethod("performResume");
+            m.setAccessible(true);
+            m.invoke(activity);
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+
+    private static void performStop(Activity activity) {
+        try {
+            if (Build.VERSION.SDK_INT >= 24) {
+                Method m = Activity.class.getDeclaredMethod("performStop", boolean.class);
+                m.setAccessible(true);
+                m.invoke(activity, false);
+            } else {
+                Method m = Activity.class.getDeclaredMethod("performStop");
+                m.setAccessible(true);
+                m.invoke(activity);
+            }
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    private static class TestObserver implements GenericLifecycleObserver {
+        private final LifecycleTestActivity mActivity;
+        private final Event mExpectedEvent;
+        boolean mEventReceived = false;
+        boolean mMuted = true;
+
+        private TestObserver(LifecycleTestActivity activity, Event expectedEvent) {
+            this.mActivity = activity;
+            this.mExpectedEvent = expectedEvent;
+        }
+
+        void unmute() {
+            mMuted = false;
+        }
+
+        @Override
+        public void onStateChanged(LifecycleOwner lifecycleOwner, Event event) {
+            if (mMuted) {
+                return;
+            }
+            assertThat(event, is(mExpectedEvent));
+            assertThat(mActivity.mLifecycleCallFinished, is(true));
+            mEventReceived = true;
+        }
+
+        @Override
+        public Object getReceiver() {
+            return null;
+        }
+    }
+
+    private interface ActivityCall {
+        void call(Activity activity);
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java
new file mode 100644
index 0000000..c5a520f
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.State.RESUMED;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.Instrumentation.ActivityMonitor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+
+import java.util.concurrent.CountDownLatch;
+
+public class TestUtils {
+
+    private static final long TIMEOUT_MS = 2000;
+
+    @SuppressWarnings("unchecked")
+    public static <T extends Activity> T recreateActivity(final T activity, ActivityTestRule rule)
+            throws Throwable {
+        ActivityMonitor monitor = new ActivityMonitor(
+                activity.getClass().getCanonicalName(), null, false);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.addMonitor(monitor);
+        rule.runOnUiThread(activity::recreate);
+        T result;
+
+        // this guarantee that we will reinstall monitor between notifications about onDestroy
+        // and onCreate
+        //noinspection SynchronizationOnLocalVariableOrMethodParameter
+        synchronized (monitor) {
+            do {
+                // the documetation says "Block until an Activity is created
+                // that matches this monitor." This statement is true, but there are some other
+                // true statements like: "Block until an Activity is destoyed" or
+                // "Block until an Activity is resumed"...
+
+                // this call will release synchronization monitor's monitor
+                result = (T) monitor.waitForActivityWithTimeout(TIMEOUT_MS);
+                if (result == null) {
+                    throw new RuntimeException("Timeout. Failed to recreate an activity");
+                }
+            } while (result == activity);
+        }
+        return result;
+    }
+
+    static void waitTillResumed(final LifecycleActivity a, ActivityTestRule<?> activityRule)
+            throws Throwable {
+        final CountDownLatch latch = new CountDownLatch(1);
+        activityRule.runOnUiThread(() -> {
+            Lifecycle.State currentState = a.getLifecycle().getCurrentState();
+            if (currentState == RESUMED) {
+                latch.countDown();
+            }
+            a.getLifecycle().addObserver(new LifecycleObserver() {
+                @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+                public void onStateChanged(LifecycleOwner provider) {
+                    latch.countDown();
+                    provider.getLifecycle().removeObserver(this);
+                }
+            });
+        });
+        latch.await();
+    }
+
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml b/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..bf88f97
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,75 @@
+<?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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" package="android.arch.lifecycle.testapp">
+
+    <application android:allowBackup="true" android:label="Test App" android:supportsRtl="true"
+        tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
+        <activity android:name="android.arch.lifecycle.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.arch.lifecycle.testapp.LifecycleTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="android.arch.lifecycle.testapp.FullLifecycleTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="android.arch.lifecycle.testapp.SupportLifecycleRegistryActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="android.arch.lifecycle.testapp.FrameworkLifecycleRegistryActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="android.arch.lifecycle.testapp.SimpleAppLifecycleTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="android.arch.lifecycle.testapp.NavigationTestActivityFirst"
+            android:launchMode="singleTask">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="android.arch.lifecycle.testapp.EmptyActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".NavigationTestActivitySecond" />
+        <activity android:name=".NavigationDialogActivity"
+            android:launchMode="singleTask"
+            android:theme="@android:style/Theme.DeviceDefault.Dialog" />
+    </application>
+</manifest>
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingActivity.java
new file mode 100644
index 0000000..6e243b6
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.arch.lifecycle.Lifecycle;
+import android.util.Pair;
+
+import java.util.List;
+
+/**
+ * For activities that collect their events.
+ */
+public interface CollectingActivity {
+    long TIMEOUT = 5;
+
+    /**
+     * Return collected events
+     *
+     * @return The list of collected events.
+     * @throws InterruptedException
+     */
+    List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents() throws InterruptedException;
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/EmptyActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/EmptyActivity.java
new file mode 100644
index 0000000..68a82e1
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/EmptyActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * empty activity
+ */
+public class EmptyActivity extends FragmentActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.empty_activity_layout);
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
new file mode 100644
index 0000000..d8f4fb3
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+
+import android.app.Activity;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LifecycleRegistryOwner;
+import android.os.Bundle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * LifecycleRegistryOwner that extends framework activity.
+ */
+public class FrameworkLifecycleRegistryActivity extends Activity implements
+        LifecycleRegistryOwner, CollectingActivity {
+    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mLifecycleRegistry;
+    }
+
+    private List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents = new ArrayList<>();
+    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+    private CountDownLatch mLatch = new CountDownLatch(1);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_CREATE));
+        getLifecycle().addObserver(mTestObserver);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_START));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_RESUME));
+        finish();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_DESTROY));
+        mLatch.countDown();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_STOP));
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_PAUSE));
+    }
+
+    /**
+     * awaits for all events and returns them.
+     */
+    @Override
+    public List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents()
+            throws InterruptedException {
+        mLatch.await(TIMEOUT, TimeUnit.SECONDS);
+        return mCollectedEvents;
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
new file mode 100644
index 0000000..5972b16
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleActivity;
+import android.os.Bundle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity for testing full lifecycle
+ */
+public class FullLifecycleTestActivity extends LifecycleActivity implements CollectingActivity {
+
+    private List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents = new ArrayList<>();
+    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+    private CountDownLatch mLatch = new CountDownLatch(1);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_CREATE));
+        getLifecycle().addObserver(mTestObserver);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_START));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_RESUME));
+        finish();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_DESTROY));
+        mLatch.countDown();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_STOP));
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_PAUSE));
+    }
+
+    /**
+     * awaits for all events and returns them.
+     */
+    @Override
+    public List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents()
+            throws InterruptedException {
+        mLatch.await(TIMEOUT, TimeUnit.SECONDS);
+        return mCollectedEvents;
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleTestActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleTestActivity.java
new file mode 100644
index 0000000..093ec7f
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleTestActivity.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.arch.lifecycle.LifecycleActivity;
+import android.os.Bundle;
+
+/**
+ * Activity for testing events by themselves
+ */
+public class LifecycleTestActivity extends LifecycleActivity {
+
+    /**
+     * identifies that
+     */
+    public boolean mLifecycleCallFinished;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        mLifecycleCallFinished = false;
+        super.onCreate(savedInstanceState);
+        mLifecycleCallFinished = true;
+    }
+
+    @Override
+    protected void onStart() {
+        mLifecycleCallFinished = false;
+        super.onStart();
+        mLifecycleCallFinished = true;
+    }
+
+    @Override
+    protected void onResume() {
+        mLifecycleCallFinished = false;
+        super.onResume();
+        mLifecycleCallFinished = true;
+    }
+
+    @Override
+    protected void onPause() {
+        mLifecycleCallFinished = false;
+        super.onPause();
+        mLifecycleCallFinished = true;
+    }
+
+    @Override
+    protected void onStop() {
+        mLifecycleCallFinished = false;
+        super.onStop();
+        mLifecycleCallFinished = true;
+    }
+
+    @Override
+    protected void onDestroy() {
+        mLifecycleCallFinished = false;
+        super.onDestroy();
+        mLifecycleCallFinished = true;
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/MainActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/MainActivity.java
new file mode 100644
index 0000000..b9d5914
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/MainActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Simple test activity
+ */
+public class MainActivity extends FragmentActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity);
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java
new file mode 100644
index 0000000..709bd8d
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.arch.lifecycle.LifecycleActivity;
+
+/**
+ *  an activity with Dialog theme.
+ */
+public class NavigationDialogActivity extends LifecycleActivity {
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
new file mode 100644
index 0000000..f1847c9
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.arch.lifecycle.LifecycleActivity;
+
+/**
+ * Activity for ProcessOwnerTest
+ */
+public class NavigationTestActivityFirst extends LifecycleActivity {
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
new file mode 100644
index 0000000..221e927
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.arch.lifecycle.LifecycleActivity;
+
+/**
+ * Activity for ProcessOwnerTest
+ */
+public class NavigationTestActivitySecond extends LifecycleActivity {
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
new file mode 100644
index 0000000..6d61c5e
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleActivity;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.arch.lifecycle.ProcessLifecycleOwner;
+import android.os.Bundle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity for SimpleAppFullLifecycleTest
+ */
+public class SimpleAppLifecycleTestActivity extends LifecycleActivity {
+
+    public enum TestEventType {
+        PROCESS_EVENT,
+        ACTIVITY_EVENT
+    }
+
+    private static final long TIMEOUT_SECS = 10; // secs
+
+    static class TestObserver implements LifecycleObserver {
+
+        private TestEventType mType;
+
+        TestObserver(TestEventType type) {
+            mType = type;
+        }
+
+        @SuppressWarnings("unused")
+        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
+        void onEvent(LifecycleOwner provider, Lifecycle.Event event) {
+            sCollectedEvents.add(new Pair<>(mType, event));
+            sLatch.countDown();
+        }
+    }
+
+    static List<Pair<TestEventType, Lifecycle.Event>> sCollectedEvents = new ArrayList<>();
+    static CountDownLatch sLatch = new CountDownLatch(11);
+
+    /**
+     * start process observer
+     */
+    public static void startProcessObserver() {
+        ProcessLifecycleOwner.get().getLifecycle().addObserver(sProcessObserver);
+    }
+
+    /**
+     * stop process observer
+     */
+    public static void stopProcessObserver() {
+        ProcessLifecycleOwner.get().getLifecycle().removeObserver(sProcessObserver);
+    }
+
+    private static TestObserver sProcessObserver = new TestObserver(TestEventType.PROCESS_EVENT);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getLifecycle().addObserver(new TestObserver(TestEventType.ACTIVITY_EVENT));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        finish();
+    }
+
+    /**
+     * returns collected events
+     */
+    public static List<Pair<TestEventType, Lifecycle.Event>> awaitForEvents()
+            throws InterruptedException {
+        boolean success = sLatch.await(TIMEOUT_SECS, TimeUnit.SECONDS);
+        return success ? sCollectedEvents : null;
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java
new file mode 100644
index 0000000..c46c6d3
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LifecycleRegistryOwner;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * LifecycleRegistryOwner that extends FragmentActivity.
+ */
+public class SupportLifecycleRegistryActivity extends FragmentActivity implements
+        LifecycleRegistryOwner, CollectingActivity {
+    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mLifecycleRegistry;
+    }
+
+    private List<Pair<TestEvent, Event>> mCollectedEvents = new ArrayList<>();
+    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+    private CountDownLatch mLatch = new CountDownLatch(1);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_CREATE));
+        getLifecycle().addObserver(mTestObserver);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_START));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_RESUME));
+        finish();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_DESTROY));
+        mLatch.countDown();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_STOP));
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_PAUSE));
+    }
+
+    /**
+     * awaits for all events and returns them.
+     */
+    @Override
+    public List<Pair<TestEvent, Event>> waitForCollectedEvents() throws InterruptedException {
+        mLatch.await(TIMEOUT, TimeUnit.SECONDS);
+        return mCollectedEvents;
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java
new file mode 100644
index 0000000..0929f84
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+public enum TestEvent {
+    ACTIVITY_CALLBACK,
+    LIFECYCLE_EVENT
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java
new file mode 100644
index 0000000..c611239
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.util.Pair;
+
+import java.util.List;
+
+class TestObserver implements LifecycleObserver {
+    private final List<Pair<TestEvent, Event>> mCollectedEvents;
+
+    TestObserver(List<Pair<TestEvent, Event>> collectedEvents) {
+        mCollectedEvents = collectedEvents;
+    }
+
+    @OnLifecycleEvent(ON_CREATE)
+    public void create(LifecycleOwner pr) {
+        mCollectedEvents.add(new Pair<>(LIFECYCLE_EVENT, ON_CREATE));
+    }
+
+    @OnLifecycleEvent(ON_START)
+    public void start(LifecycleOwner pr) {
+        mCollectedEvents.add(new Pair<>(LIFECYCLE_EVENT, ON_START));
+    }
+
+    @OnLifecycleEvent(ON_RESUME)
+    public void resume(LifecycleOwner pr) {
+        mCollectedEvents.add(new Pair<>(LIFECYCLE_EVENT, ON_RESUME));
+    }
+    @OnLifecycleEvent(ON_PAUSE)
+    public void pause(LifecycleOwner pr) {
+        mCollectedEvents.add(new Pair<>(LIFECYCLE_EVENT, ON_PAUSE));
+    }
+
+    @OnLifecycleEvent(ON_STOP)
+    public void stop(LifecycleOwner pr) {
+        mCollectedEvents.add(new Pair<>(LIFECYCLE_EVENT, ON_STOP));
+    }
+
+    @OnLifecycleEvent(ON_DESTROY)
+    public void destroy(LifecycleOwner pr) {
+        mCollectedEvents.add(new Pair<>(LIFECYCLE_EVENT, ON_DESTROY));
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/UsualFragment.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/UsualFragment.java
new file mode 100644
index 0000000..fb6cae0
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/UsualFragment.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Simple fragment which does nothing.
+ */
+public class UsualFragment extends Fragment {
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return new View(getContext());
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml b/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml
new file mode 100644
index 0000000..a4e9513
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml
@@ -0,0 +1,32 @@
+<?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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/root">
+        <fragment
+            android:id="@+id/main_fragment"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:tag="fragment_tag"
+            android:name="android.arch.lifecycle.testapp.UsualFragment"
+            tools:context="android.arch.lifecycle.testapp.MainActivity">
+        </fragment>
+    </FrameLayout>
+</FrameLayout>
diff --git a/lifecycle/integration-tests/testapp/src/main/res/layout/empty_activity_layout.xml b/lifecycle/integration-tests/testapp/src/main/res/layout/empty_activity_layout.xml
new file mode 100644
index 0000000..d476848
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/res/layout/empty_activity_layout.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/activity_main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="android.arch.lifecycle.activity.FragmentLifecycleActivity">
+    <FrameLayout
+        android:id="@+id/fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</RelativeLayout>
diff --git a/lifecycle/reactivestreams/.gitignore b/lifecycle/reactivestreams/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/lifecycle/reactivestreams/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/lifecycle/reactivestreams/build.gradle b/lifecycle/reactivestreams/build.gradle
new file mode 100644
index 0000000..67da2ca
--- /dev/null
+++ b/lifecycle/reactivestreams/build.gradle
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+sourceCompatibility = '1.7'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+}
+
+allprojects {
+    dependencies {
+        compile project(":arch:common")
+        compile project(":lifecycle:common")
+        compile project(":lifecycle:extensions")
+        compile project(":lifecycle:runtime")
+        compile libs.support.annotations
+        compile libs.reactive_streams
+
+        testCompile libs.junit
+        testCompile libs.rx_java
+
+        testCompile(libs.test_runner) {
+            exclude module: 'support-annotations'
+        }
+        androidTestCompile libs.support.app_compat
+    }
+}
+
+createAndroidCheckstyle(project)
+
+archivesBaseName = "reactivestreams"
diff --git a/lifecycle/reactivestreams/proguard-rules.pro b/lifecycle/reactivestreams/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/lifecycle/reactivestreams/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/lifecycle/reactivestreams/src/androidTest/AndroidManifest.xml b/lifecycle/reactivestreams/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..722f480
--- /dev/null
+++ b/lifecycle/reactivestreams/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.lifecycle.reactivestreams.test">
+
+    <application>
+        <activity android:name="android.arch.lifecycle.viewmodeltest.ViewModelActivity"
+                  android:theme="@style/Base.Theme.AppCompat">
+        </activity>
+    </application>
+
+</manifest>
diff --git a/lifecycle/reactivestreams/src/main/AndroidManifest.xml b/lifecycle/reactivestreams/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2210041
--- /dev/null
+++ b/lifecycle/reactivestreams/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.lifecycle.reactivestreams">
+</manifest>
diff --git a/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java b/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java
new file mode 100644
index 0000000..0be0149
--- /dev/null
+++ b/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.support.annotation.Nullable;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Adapts {@link LiveData} input and output to the ReactiveStreams spec.
+ */
+@SuppressWarnings("WeakerAccess")
+public final class LiveDataReactiveStreams {
+    private LiveDataReactiveStreams() {
+    }
+
+    /**
+     * Adapts the given {@link LiveData} stream to a ReactiveStreams {@link Publisher}.
+     *
+     * <p>
+     * By using a good publisher implementation such as RxJava 2.x Flowables, most consumers will
+     * be able to let the library deal with backpressure using operators and not need to worry about
+     * ever manually calling {@link Subscription#request}.
+     *
+     * <p>
+     * On subscription to the publisher, the observer will attach to the given {@link LiveData}.
+     * Once {@link Subscription#request) is called on the subscription object, an observer will be
+     * connected to the data stream. Calling request(Long.MAX_VALUE) is equivalent to creating an
+     * unbounded stream with no backpressure. If request with a finite count reaches 0, the observer
+     * will buffer the latest item and emit it to the subscriber when data is again requested. Any
+     * other items emitted during the time there was no backpressure requested will be dropped.
+     */
+    public static <T> Publisher<T> toPublisher(
+            final LifecycleOwner lifecycle, final LiveData<T> liveData) {
+
+        return new Publisher<T>() {
+            boolean mObserving;
+            boolean mCanceled;
+            long mRequested;
+            @Nullable
+            T mLatest;
+
+            @Override
+            public void subscribe(final Subscriber<? super T> subscriber) {
+                final Observer<T> observer = new Observer<T>() {
+                    @Override
+                    public void onChanged(@Nullable T t) {
+                        if (mCanceled) {
+                            return;
+                        }
+                        if (mRequested > 0) {
+                            mLatest = null;
+                            subscriber.onNext(t);
+                            if (mRequested != Long.MAX_VALUE) {
+                                mRequested--;
+                            }
+                        } else {
+                            mLatest = t;
+                        }
+                    }
+                };
+
+                subscriber.onSubscribe(new Subscription() {
+                    @Override
+                    public void request(final long n) {
+                        if (n < 0 || mCanceled) {
+                            return;
+                        }
+                        AppToolkitTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mCanceled) {
+                                    return;
+                                }
+                                // Prevent overflowage.
+                                mRequested = mRequested + n >= mRequested
+                                        ? mRequested + n : Long.MAX_VALUE;
+                                if (!mObserving) {
+                                    mObserving = true;
+                                    liveData.observe(lifecycle, observer);
+                                } else if (mLatest != null) {
+                                    observer.onChanged(mLatest);
+                                    mLatest = null;
+                                }
+                            }
+                        });
+                    }
+
+                    @Override
+                    public void cancel() {
+                        if (mCanceled) {
+                            return;
+                        }
+                        AppToolkitTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mCanceled) {
+                                    return;
+                                }
+                                if (mObserving) {
+                                    liveData.removeObserver(observer);
+                                    mObserving = false;
+                                }
+                                mLatest = null;
+                                mCanceled = true;
+                            }
+                        });
+                    }
+                });
+            }
+
+        };
+    }
+
+    /**
+     * Creates an Observable {@link LiveData} stream from a ReactiveStreams publisher.
+     */
+    public static <T> LiveData<T> fromPublisher(final Publisher<T> publisher) {
+        MutableLiveData<T> liveData = new MutableLiveData<>();
+        // Since we don't have a way to directly observe cancels, weakly hold the live data.
+        final WeakReference<MutableLiveData<T>> liveDataRef = new WeakReference<>(liveData);
+
+        publisher.subscribe(new Subscriber<T>() {
+            @Override
+            public void onSubscribe(Subscription s) {
+                // Don't worry about backpressure. If the stream is too noisy then backpressure can
+                // be handled upstream.
+                s.request(Long.MAX_VALUE);
+            }
+
+            @Override
+            public void onNext(final T t) {
+                final LiveData<T> liveData = liveDataRef.get();
+                if (liveData != null) {
+                    liveData.postValue(t);
+                }
+            }
+
+            @Override
+            public void onError(Throwable t) {
+                // Errors should be handled upstream, so propagate as a crash.
+                throw new RuntimeException(t);
+            }
+
+            @Override
+            public void onComplete() {
+            }
+        });
+
+        return liveData;
+    }
+
+}
diff --git a/lifecycle/reactivestreams/src/test/java/android/arch/lifecycle/LiveDataReactiveStreamsTest.java b/lifecycle/reactivestreams/src/test/java/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
new file mode 100644
index 0000000..87fba27
--- /dev/null
+++ b/lifecycle/reactivestreams/src/test/java/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.State.RESUMED;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.TaskExecutor;
+import android.support.annotation.Nullable;
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import io.reactivex.Flowable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Consumer;
+import io.reactivex.processors.PublishProcessor;
+import io.reactivex.processors.ReplayProcessor;
+import io.reactivex.schedulers.TestScheduler;
+import io.reactivex.subjects.AsyncSubject;
+
+@SmallTest
+public class LiveDataReactiveStreamsTest {
+    private static final Lifecycle sLifecycle = new Lifecycle() {
+        @Override
+        public void addObserver(LifecycleObserver observer) {
+        }
+
+        @Override
+        public void removeObserver(LifecycleObserver observer) {
+        }
+
+        @Override
+        public State getCurrentState() {
+            return RESUMED;
+        }
+    };
+    private static final LifecycleOwner S_LIFECYCLE_OWNER = new LifecycleOwner() {
+
+        @Override
+        public Lifecycle getLifecycle() {
+            return sLifecycle;
+        }
+
+    };
+
+    private final List<String> mLiveDataOutput = new ArrayList<>();
+    private final Observer<String> mObserver = new Observer<String>() {
+        @Override
+        public void onChanged(@Nullable String s) {
+            mLiveDataOutput.add(s);
+        }
+    };
+
+    private final ReplayProcessor<String> mOutputProcessor = ReplayProcessor.create();
+
+    private static final TestScheduler sBackgroundScheduler = new TestScheduler();
+    private Thread mTestThread;
+
+    @Before
+    public void init() {
+        mTestThread = Thread.currentThread();
+        AppToolkitTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+
+            @Override
+            public void executeOnDiskIO(Runnable runnable) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public void postToMainThread(Runnable runnable) {
+                // Wrong implementation, but it is fine for test
+                runnable.run();
+            }
+
+            @Override
+            public boolean isMainThread() {
+                return Thread.currentThread() == mTestThread;
+            }
+
+        });
+    }
+
+    @After
+    public void removeExecutorDelegate() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    @Test
+    public void convertsFromPublisher() {
+        PublishProcessor<String> processor = PublishProcessor.create();
+        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
+
+        liveData.observe(S_LIFECYCLE_OWNER, mObserver);
+
+        processor.onNext("foo");
+        processor.onNext("bar");
+        processor.onNext("baz");
+
+        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "bar", "baz")));
+    }
+
+    @Test
+    public void convertsFromPublisherWithMultipleObservers() {
+        final List<String> output2 = new ArrayList<>();
+        PublishProcessor<String> processor = PublishProcessor.create();
+        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
+
+        liveData.observe(S_LIFECYCLE_OWNER, mObserver);
+
+        processor.onNext("foo");
+        processor.onNext("bar");
+
+        // The second mObserver should only get the newest value and any later values.
+        liveData.observe(S_LIFECYCLE_OWNER, new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                output2.add(s);
+            }
+        });
+
+        processor.onNext("baz");
+
+        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "bar", "baz")));
+        assertThat(output2, is(Arrays.asList("bar", "baz")));
+    }
+
+    @Test
+    public void convertsFromAsyncPublisher() {
+        Flowable<String> input = Flowable.just("foo")
+                .concatWith(Flowable.just("bar", "baz").observeOn(sBackgroundScheduler));
+        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(input);
+
+        liveData.observe(S_LIFECYCLE_OWNER, mObserver);
+
+        assertThat(mLiveDataOutput, is(Collections.singletonList("foo")));
+        sBackgroundScheduler.triggerActions();
+        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "bar", "baz")));
+    }
+
+    @Test
+    public void convertsToPublisherWithSyncData() {
+        MutableLiveData<String> liveData = new MutableLiveData<>();
+        liveData.setValue("foo");
+        assertThat(liveData.getValue(), is("foo"));
+
+        Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+                .subscribe(mOutputProcessor);
+
+        liveData.setValue("bar");
+        liveData.setValue("baz");
+
+        assertThat(
+                mOutputProcessor.getValues(new String[]{}),
+                is(new String[] {"foo", "bar", "baz"}));
+    }
+
+    @Test
+    public void convertingToPublisherIsCancelable() {
+        MutableLiveData<String> liveData = new MutableLiveData<>();
+        liveData.setValue("foo");
+        assertThat(liveData.getValue(), is("foo"));
+
+        Disposable disposable = Flowable
+                .fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+                .subscribe(new Consumer<String>() {
+                    @Override
+                    public void accept(String s) throws Exception {
+                        mLiveDataOutput.add(s);
+                    }
+                });
+
+        liveData.setValue("bar");
+        liveData.setValue("baz");
+
+        assertThat(liveData.hasObservers(), is(true));
+        disposable.dispose();
+
+        liveData.setValue("fizz");
+        liveData.setValue("buzz");
+
+        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "bar", "baz")));
+        // Canceling disposable should also remove livedata mObserver.
+        assertThat(liveData.hasObservers(), is(false));
+    }
+
+    @Test
+    public void convertsToPublisherWithBackpressure() {
+        MutableLiveData<String> liveData = new MutableLiveData<>();
+
+        final AsyncSubject<Subscription> subscriptionSubject = AsyncSubject.create();
+
+        Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+                .subscribe(new Subscriber<String>() {
+                    @Override
+                    public void onSubscribe(Subscription s) {
+                        subscriptionSubject.onNext(s);
+                        subscriptionSubject.onComplete();
+                    }
+
+                    @Override
+                    public void onNext(String s) {
+                        mOutputProcessor.onNext(s);
+                    }
+
+                    @Override
+                    public void onError(Throwable t) {
+                        throw new RuntimeException(t);
+                    }
+
+                    @Override
+                    public void onComplete() {
+                    }
+                });
+
+        // Subscription should have happened synchronously. If it didn't, this will deadlock.
+        final Subscription subscription = subscriptionSubject.blockingSingle();
+
+        subscription.request(1);
+        assertThat(mOutputProcessor.getValues(new String[]{}), is(new String[] {}));
+
+        liveData.setValue("foo");
+        assertThat(mOutputProcessor.getValues(new String[]{}), is(new String[] {"foo"}));
+
+        subscription.request(2);
+        liveData.setValue("baz");
+        liveData.setValue("fizz");
+
+        assertThat(
+                mOutputProcessor.getValues(new String[]{}),
+                is(new String[] {"foo", "baz", "fizz"}));
+
+        // 'nyan' will be dropped as there is nothing currently requesting a stream.
+        liveData.setValue("nyan");
+        liveData.setValue("cat");
+
+        assertThat(
+                mOutputProcessor.getValues(new String[]{}),
+                is(new String[] {"foo", "baz", "fizz"}));
+
+        // When a new request comes in, the latest value will be pushed.
+        subscription.request(1);
+        assertThat(
+                mOutputProcessor.getValues(new String[]{}),
+                is(new String[] {"foo", "baz", "fizz", "cat"}));
+    }
+
+    @Test
+    public void convertsToPublisherWithAsyncData() {
+        MutableLiveData<String> liveData = new MutableLiveData<>();
+
+        Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+                .observeOn(sBackgroundScheduler)
+                .subscribe(mOutputProcessor);
+
+        liveData.setValue("foo");
+
+        assertThat(mOutputProcessor.getValues(new String[]{}), is(new String[] {}));
+        sBackgroundScheduler.triggerActions();
+        assertThat(mOutputProcessor.getValues(new String[]{}), is(new String[] {"foo"}));
+
+        liveData.setValue("bar");
+        liveData.setValue("baz");
+
+        assertThat(mOutputProcessor.getValues(new String[]{}), is(new String[] {"foo"}));
+        sBackgroundScheduler.triggerActions();
+        assertThat(mOutputProcessor.getValues(
+                new String[]{}),
+                is(new String[] {"foo", "bar", "baz"}));
+    }
+}
diff --git a/lifecycle/runtime/.gitignore b/lifecycle/runtime/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/lifecycle/runtime/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/lifecycle/runtime/build.gradle b/lifecycle/runtime/build.gradle
new file mode 100644
index 0000000..3eb40e4
--- /dev/null
+++ b/lifecycle/runtime/build.gradle
@@ -0,0 +1,48 @@
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+}
+
+dependencies {
+    compile project(":lifecycle:common")
+    compile project(":arch:common")
+    // necessary for IJ to resolve dependencies.
+    compile libs.support.annotations
+
+    testCompile libs.junit
+    testCompile libs.mockito_core
+
+    testCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+}
+
+createAndroidCheckstyle(project)
+
+archivesBaseName = "runtime"
diff --git a/lifecycle/runtime/proguard-rules.pro b/lifecycle/runtime/proguard-rules.pro
new file mode 100644
index 0000000..25f3e87
--- /dev/null
+++ b/lifecycle/runtime/proguard-rules.pro
@@ -0,0 +1,16 @@
+-keepattributes *Annotation*
+
+-keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
+    <fields>;
+}
+
+-keep class * implements android.arch.lifecycle.LifecycleObserver {
+}
+
+-keep class * implements android.arch.lifecycle.GenericLifecycleObserver {
+    <init>(...);
+}
+
+-keepclassmembers class ** {
+    @android.arch.lifecycle.OnLifecycleEvent *;
+}
\ No newline at end of file
diff --git a/lifecycle/runtime/src/main/AndroidManifest.xml b/lifecycle/runtime/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..274a076
--- /dev/null
+++ b/lifecycle/runtime/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.lifecycle">
+</manifest>
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
new file mode 100644
index 0000000..b83e6b8
--- /dev/null
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
+import static android.arch.lifecycle.Lifecycle.State.INITIALIZED;
+import static android.arch.lifecycle.Lifecycle.State.RESUMED;
+import static android.arch.lifecycle.Lifecycle.State.STARTED;
+
+import android.arch.core.internal.FastSafeIterableMap;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+/**
+ * An implementation of {@link Lifecycle} that can handle multiple observers.
+ * <p>
+ * It is used by Fragments and Support Library Activities. You can also directly use it if you have
+ * a custom LifecycleOwner.
+ */
+public class LifecycleRegistry extends Lifecycle {
+
+    /**
+     * Custom list that keeps observers and can handle removals / additions during traversal.
+     *
+     * Invariant: at any moment of time for observer1 & observer2:
+     * if addition_order(observer1) < addition_order(observer2), then
+     * state(observer1) >= state(observer2),
+     */
+    private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
+            new FastSafeIterableMap<>();
+    /**
+     * Current state
+     */
+    private State mState;
+    /**
+     * The provider that owns this Lifecycle.
+     */
+    private final LifecycleOwner mLifecycleOwner;
+
+    private int mAddingObserverCounter = 0;
+
+    private boolean mHandlingEvent = false;
+    private boolean mNewEventOccurred = false;
+
+    // we have to keep it for cases:
+    // void onStart() {
+    //     mRegistry.removeObserver(this);
+    //     mRegistry.add(newObserver);
+    // }
+    // newObserver should be brought only to CREATED state during the execution of
+    // this onStart method. our invariant with mObserverMap doesn't help, because parent observer
+    // is no longer in the map.
+    private ArrayList<State> mParentStates = new ArrayList<>();
+
+    /**
+     * Creates a new LifecycleRegistry for the given provider.
+     * <p>
+     * You should usually create this inside your LifecycleOwner class's constructor and hold
+     * onto the same instance.
+     *
+     * @param provider The owner LifecycleOwner
+     */
+    public LifecycleRegistry(@NonNull LifecycleOwner provider) {
+        mLifecycleOwner = provider;
+        mState = INITIALIZED;
+    }
+
+    /**
+     * Only marks the current state as the given value. It doesn't dispatch any event to its
+     * listeners.
+     *
+     * @param state new state
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void markState(State state) {
+        mState = state;
+    }
+
+    /**
+     * Sets the current state and notifies the observers.
+     * <p>
+     * Note that if the {@code currentState} is the same state as the last call to this method,
+     * calling this method has no effect.
+     *
+     * @param event The event that was received
+     */
+    public void handleLifecycleEvent(Lifecycle.Event event) {
+        mState = getStateAfter(event);
+        if (mHandlingEvent || mAddingObserverCounter != 0) {
+            mNewEventOccurred = true;
+            // we will figure out what to do on upper level.
+            return;
+        }
+        mHandlingEvent = true;
+        sync();
+        mHandlingEvent = false;
+    }
+
+    private boolean isSynced() {
+        if (mObserverMap.size() == 0) {
+            return true;
+        }
+        State eldestObserverState = mObserverMap.eldest().getValue().mState;
+        State newestObserverState = mObserverMap.newest().getValue().mState;
+        return eldestObserverState == newestObserverState && mState == newestObserverState;
+    }
+
+    private State calculateTargetState(LifecycleObserver observer) {
+        Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer);
+
+        State siblingState = previous != null ? previous.getValue().mState : null;
+        State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1)
+                : null;
+        return min(min(mState, siblingState), parentState);
+    }
+
+    @Override
+    public void addObserver(LifecycleObserver observer) {
+        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
+        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
+        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
+
+        if (previous != null) {
+            return;
+        }
+
+        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
+
+        State targetState = calculateTargetState(observer);
+        mAddingObserverCounter++;
+        while ((statefulObserver.mState.compareTo(targetState) < 0
+                && mObserverMap.contains(observer))) {
+            pushParentState(statefulObserver.mState);
+            statefulObserver.dispatchEvent(mLifecycleOwner, upEvent(statefulObserver.mState));
+            popParentState();
+            // mState / subling may have been changed recalculate
+            targetState = calculateTargetState(observer);
+        }
+
+        if (!isReentrance) {
+            // we do sync only on the top level.
+            sync();
+        }
+        mAddingObserverCounter--;
+    }
+
+    private void popParentState() {
+        mParentStates.remove(mParentStates.size() - 1);
+    }
+
+    private void pushParentState(State state) {
+        mParentStates.add(state);
+    }
+
+    @Override
+    public void removeObserver(LifecycleObserver observer) {
+        // we consciously decided not to send destruction events here in opposition to addObserver.
+        // Our reasons for that:
+        // 1. These events haven't yet happened at all. In contrast to events in addObservers, that
+        // actually occurred but earlier.
+        // 2. There are cases when removeObserver happens as a consequence of some kind of fatal
+        // event. If removeObserver method sends destruction events, then a clean up routine becomes
+        // more cumbersome. More specific example of that is: your LifecycleObserver listens for
+        // a web connection, in the usual routine in OnStop method you report to a server that a
+        // session has just ended and you close the connection. Now let's assume now that you
+        // lost an internet and as a result you removed this observer. If you get destruction
+        // events in removeObserver, you should have a special case in your onStop method that
+        // checks if your web connection died and you shouldn't try to report anything to a server.
+        mObserverMap.remove(observer);
+    }
+
+    /**
+     * The number of observers.
+     *
+     * @return The number of observers.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public int getObserverCount() {
+        return mObserverMap.size();
+    }
+
+    @Override
+    public State getCurrentState() {
+        return mState;
+    }
+
+    static State getStateAfter(Event event) {
+        switch (event) {
+            case ON_CREATE:
+            case ON_STOP:
+                return CREATED;
+            case ON_START:
+            case ON_PAUSE:
+                return STARTED;
+            case ON_RESUME:
+                return RESUMED;
+            case ON_DESTROY:
+                return DESTROYED;
+            case ON_ANY:
+                break;
+        }
+        throw new IllegalArgumentException("Unexpected event value " + event);
+    }
+
+    private static Event downEvent(State state) {
+        switch (state) {
+            case INITIALIZED:
+                throw new IllegalArgumentException();
+            case CREATED:
+                return ON_DESTROY;
+            case STARTED:
+                return ON_STOP;
+            case RESUMED:
+                return ON_PAUSE;
+            case DESTROYED:
+                throw new IllegalArgumentException();
+        }
+        throw new IllegalArgumentException("Unexpected state value " + state);
+    }
+
+    private static Event upEvent(State state) {
+        switch (state) {
+            case INITIALIZED:
+            case DESTROYED:
+                return ON_CREATE;
+            case CREATED:
+                return ON_START;
+            case STARTED:
+                return ON_RESUME;
+            case RESUMED:
+                throw new IllegalArgumentException();
+        }
+        throw new IllegalArgumentException("Unexpected state value " + state);
+    }
+
+    private void forwardPass() {
+        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
+                mObserverMap.iteratorWithAdditions();
+        while (ascendingIterator.hasNext() && !mNewEventOccurred) {
+            Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
+            ObserverWithState observer = entry.getValue();
+            while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
+                    && mObserverMap.contains(entry.getKey()))) {
+                pushParentState(observer.mState);
+                observer.dispatchEvent(mLifecycleOwner, upEvent(observer.mState));
+                popParentState();
+            }
+        }
+    }
+
+    private void backwardPass() {
+        Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
+                mObserverMap.descendingIterator();
+        while (descendingIterator.hasNext() && !mNewEventOccurred) {
+            Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
+            ObserverWithState observer = entry.getValue();
+            while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
+                    && mObserverMap.contains(entry.getKey()))) {
+                Event event = downEvent(observer.mState);
+                pushParentState(getStateAfter(event));
+                observer.dispatchEvent(mLifecycleOwner, event);
+                popParentState();
+            }
+        }
+    }
+
+    // happens only on the top of stack (never in reentrance),
+    // so it doesn't have to take in account parents
+    private void sync() {
+        while (!isSynced()) {
+            mNewEventOccurred = false;
+            // no need to check eldest for nullability, because isSynced does it for us.
+            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
+                backwardPass();
+            }
+            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
+            if (!mNewEventOccurred && newest != null
+                    && mState.compareTo(newest.getValue().mState) > 0) {
+                forwardPass();
+            }
+        }
+        mNewEventOccurred = false;
+    }
+
+    static State min(@NonNull State state1, @Nullable State state2) {
+        return state2 != null && state2.compareTo(state1) < 0 ? state2 : state1;
+    }
+
+    static class ObserverWithState {
+        State mState;
+        GenericLifecycleObserver mLifecycleObserver;
+
+        ObserverWithState(LifecycleObserver observer, State initialState) {
+            mLifecycleObserver = Lifecycling.getCallback(observer);
+            mState = initialState;
+        }
+
+        void dispatchEvent(LifecycleOwner owner, Event event) {
+            State newState = getStateAfter(event);
+            mState = min(mState, newState);
+            mLifecycleObserver.onStateChanged(owner, event);
+            mState = newState;
+        }
+    }
+}
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java
new file mode 100644
index 0000000..634cfc3
--- /dev/null
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+/**
+ * Specialization of {@link LifecycleOwner} that explicitly returns {@link LifecycleRegistry}.
+ * <p>
+ * This method may be used if an object which updates state of {@link Lifecycle} doesn't own it.
+ * <p>
+ * This class is a temporary implementation detail until Lifecycles are integrated with support
+ * library.
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public interface LifecycleRegistryOwner extends LifecycleOwner {
+    @Override
+    LifecycleRegistry getLifecycle();
+}
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java
new file mode 100644
index 0000000..490028c
--- /dev/null
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Internal class that dispatches initialization events.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class ReportFragment extends Fragment {
+
+    private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+            + ".LifecycleDispatcher.report_fragment_tag";
+
+    public static void injectIfNeededIn(Activity activity) {
+        // ProcessLifecycleOwner should always correctly work and some activities may not extend
+        // FragmentActivity from support lib, so we use framework fragments for activities
+        android.app.FragmentManager manager = activity.getFragmentManager();
+        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
+            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
+            // Hopefully, we are the first to make a transaction.
+            manager.executePendingTransactions();
+        }
+    }
+
+    static ReportFragment get(Activity activity) {
+        return (ReportFragment) activity.getFragmentManager().findFragmentByTag(
+                REPORT_FRAGMENT_TAG);
+    }
+
+    private ActivityInitializationListener mProcessListener;
+
+    private void dispatchCreate(ActivityInitializationListener listener) {
+        if (listener != null) {
+            listener.onCreate();
+        }
+    }
+
+    private void dispatchStart(ActivityInitializationListener listener) {
+        if (listener != null) {
+            listener.onStart();
+        }
+    }
+
+    private void dispatchResume(ActivityInitializationListener listener) {
+        if (listener != null) {
+            listener.onResume();
+        }
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        dispatchCreate(mProcessListener);
+        dispatch(Lifecycle.Event.ON_CREATE);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        dispatchStart(mProcessListener);
+        dispatch(Lifecycle.Event.ON_START);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        dispatchResume(mProcessListener);
+        dispatch(Lifecycle.Event.ON_RESUME);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        dispatch(Lifecycle.Event.ON_PAUSE);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        dispatch(Lifecycle.Event.ON_STOP);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        dispatch(Lifecycle.Event.ON_DESTROY);
+        // just want to be sure that we won't leak reference to an activity
+        mProcessListener = null;
+    }
+
+    private void dispatch(Lifecycle.Event event) {
+        if (getActivity() instanceof LifecycleRegistryOwner) {
+            ((LifecycleRegistryOwner) getActivity()).getLifecycle().handleLifecycleEvent(event);
+        }
+    }
+
+    void setProcessListener(ActivityInitializationListener processListener) {
+        mProcessListener = processListener;
+    }
+
+    interface ActivityInitializationListener {
+        void onCreate();
+
+        void onStart();
+
+        void onResume();
+    }
+}
diff --git a/lifecycle/runtime/src/test/java/android/arch/lifecycle/LifecycleRegistryTest.java b/lifecycle/runtime/src/test/java/android/arch/lifecycle/LifecycleRegistryTest.java
new file mode 100644
index 0000000..ef498ee
--- /dev/null
+++ b/lifecycle/runtime/src/test/java/android/arch/lifecycle/LifecycleRegistryTest.java
@@ -0,0 +1,608 @@
+/*
+ * 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.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class LifecycleRegistryTest {
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private LifecycleRegistry mRegistry;
+
+    @Before
+    public void init() {
+        mLifecycleOwner = mock(LifecycleOwner.class);
+        mLifecycle = mock(Lifecycle.class);
+        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle);
+        mRegistry = new LifecycleRegistry(mLifecycleOwner);
+    }
+
+    @Test
+    public void addRemove() {
+        LifecycleObserver observer = mock(LifecycleObserver.class);
+        mRegistry.addObserver(observer);
+        assertThat(mRegistry.getObserverCount(), is(1));
+        mRegistry.removeObserver(observer);
+        assertThat(mRegistry.getObserverCount(), is(0));
+    }
+
+    @Test
+    public void addGenericAndObserve() {
+        GenericLifecycleObserver generic = mock(GenericLifecycleObserver.class);
+        mRegistry.addObserver(generic);
+        dispatchEvent(ON_CREATE);
+        verify(generic).onStateChanged(mLifecycleOwner, ON_CREATE);
+        reset(generic);
+        dispatchEvent(ON_CREATE);
+        verify(generic, never()).onStateChanged(mLifecycleOwner, ON_CREATE);
+    }
+
+    @Test
+    public void addRegularClass() {
+        TestObserver testObserver = mock(TestObserver.class);
+        mRegistry.addObserver(testObserver);
+        dispatchEvent(ON_START);
+        verify(testObserver, never()).onStop();
+        dispatchEvent(ON_STOP);
+        verify(testObserver).onStop();
+    }
+
+    @Test
+    public void add2RemoveOne() {
+        TestObserver observer1 = mock(TestObserver.class);
+        TestObserver observer2 = mock(TestObserver.class);
+        TestObserver observer3 = mock(TestObserver.class);
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+        mRegistry.addObserver(observer3);
+
+        dispatchEvent(ON_CREATE);
+
+        verify(observer1).onCreate();
+        verify(observer2).onCreate();
+        verify(observer3).onCreate();
+        reset(observer1, observer2, observer3);
+
+        mRegistry.removeObserver(observer2);
+        dispatchEvent(ON_START);
+
+        verify(observer1).onStart();
+        verify(observer2, never()).onStart();
+        verify(observer3).onStart();
+    }
+
+    @Test
+    public void removeWhileTraversing() {
+        final TestObserver observer2 = mock(TestObserver.class);
+        TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            public void onCreate() {
+                mRegistry.removeObserver(observer2);
+            }
+        });
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+        dispatchEvent(ON_CREATE);
+        verify(observer2, never()).onCreate();
+        verify(observer1).onCreate();
+    }
+
+    @Test
+    public void constructionOrder() {
+        fullyInitializeRegistry();
+        final TestObserver observer = mock(TestObserver.class);
+        mRegistry.addObserver(observer);
+        InOrder inOrder = inOrder(observer);
+        inOrder.verify(observer).onCreate();
+        inOrder.verify(observer).onStart();
+        inOrder.verify(observer).onResume();
+    }
+
+    @Test
+    public void constructionDestruction1() {
+        fullyInitializeRegistry();
+        final TestObserver observer = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                dispatchEvent(ON_PAUSE);
+            }
+        });
+        mRegistry.addObserver(observer);
+        InOrder constructionOrder = inOrder(observer);
+        constructionOrder.verify(observer).onCreate();
+        constructionOrder.verify(observer).onStart();
+        constructionOrder.verify(observer, never()).onResume();
+    }
+
+    @Test
+    public void constructionDestruction2() {
+        fullyInitializeRegistry();
+        final TestObserver observer = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                dispatchEvent(ON_PAUSE);
+                dispatchEvent(ON_STOP);
+                dispatchEvent(ON_DESTROY);
+            }
+        });
+        mRegistry.addObserver(observer);
+        InOrder orderVerifier = inOrder(observer);
+        orderVerifier.verify(observer).onCreate();
+        orderVerifier.verify(observer).onStart();
+        orderVerifier.verify(observer).onStop();
+        orderVerifier.verify(observer).onDestroy();
+        orderVerifier.verify(observer, never()).onResume();
+    }
+
+    @Test
+    public void twoObserversChangingState() {
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onCreate() {
+                dispatchEvent(ON_START);
+            }
+        });
+        final TestObserver observer2 = mock(TestObserver.class);
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+        dispatchEvent(ON_CREATE);
+        verify(observer1, times(1)).onCreate();
+        verify(observer2, times(1)).onCreate();
+        verify(observer1, times(1)).onStart();
+        verify(observer2, times(1)).onStart();
+    }
+
+    @Test
+    public void addDuringTraversing() {
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            public void onStart() {
+                mRegistry.addObserver(observer3);
+            }
+        });
+        final TestObserver observer2 = mock(TestObserver.class);
+
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+
+        dispatchEvent(ON_CREATE);
+        dispatchEvent(ON_START);
+
+        InOrder inOrder = inOrder(observer1, observer2, observer3);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer3).onStart();
+    }
+
+    @Test
+    public void addDuringAddition() {
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer2 = spy(new TestObserver() {
+            @Override
+            public void onCreate() {
+                mRegistry.addObserver(observer3);
+            }
+        });
+
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            public void onResume() {
+                mRegistry.addObserver(observer2);
+            }
+        });
+
+        mRegistry.addObserver(observer1);
+
+        dispatchEvent(ON_CREATE);
+        dispatchEvent(ON_START);
+        dispatchEvent(ON_RESUME);
+
+        InOrder inOrder = inOrder(observer1, observer2, observer3);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer1).onResume();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer2).onResume();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer3).onStart();
+        inOrder.verify(observer3).onResume();
+    }
+
+    @Test
+    public void subscribeToDead() {
+        dispatchEvent(ON_CREATE);
+        final TestObserver observer1 = mock(TestObserver.class);
+        mRegistry.addObserver(observer1);
+        verify(observer1).onCreate();
+        dispatchEvent(ON_DESTROY);
+        verify(observer1).onDestroy();
+        final TestObserver observer2 = mock(TestObserver.class);
+        mRegistry.addObserver(observer2);
+        verify(observer2, never()).onCreate();
+        reset(observer1);
+        dispatchEvent(ON_CREATE);
+        verify(observer1).onCreate();
+        verify(observer2).onCreate();
+    }
+
+    @Test
+    public void downEvents() {
+        fullyInitializeRegistry();
+        final TestObserver observer1 = mock(TestObserver.class);
+        final TestObserver observer2 = mock(TestObserver.class);
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+        InOrder orderVerifier = inOrder(observer1, observer2);
+        dispatchEvent(ON_PAUSE);
+        orderVerifier.verify(observer2).onPause();
+        orderVerifier.verify(observer1).onPause();
+        dispatchEvent(ON_STOP);
+        orderVerifier.verify(observer2).onStop();
+        orderVerifier.verify(observer1).onStop();
+        dispatchEvent(ON_DESTROY);
+        orderVerifier.verify(observer2).onDestroy();
+        orderVerifier.verify(observer1).onDestroy();
+    }
+
+    @Test
+    public void downEventsAddition() {
+        dispatchEvent(ON_CREATE);
+        dispatchEvent(ON_START);
+        final TestObserver observer1 = mock(TestObserver.class);
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer2 = spy(new TestObserver() {
+            @Override
+            void onStop() {
+                mRegistry.addObserver(observer3);
+            }
+        });
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+        InOrder orderVerifier = inOrder(observer1, observer2, observer3);
+        dispatchEvent(ON_STOP);
+        orderVerifier.verify(observer2).onStop();
+        orderVerifier.verify(observer3).onCreate();
+        orderVerifier.verify(observer1).onStop();
+        dispatchEvent(ON_DESTROY);
+        orderVerifier.verify(observer3).onDestroy();
+        orderVerifier.verify(observer2).onDestroy();
+        orderVerifier.verify(observer1).onDestroy();
+    }
+
+    @Test
+    public void downEventsRemoveAll() {
+        fullyInitializeRegistry();
+        final TestObserver observer1 = mock(TestObserver.class);
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer2 = spy(new TestObserver() {
+            @Override
+            void onStop() {
+                mRegistry.removeObserver(observer3);
+                mRegistry.removeObserver(this);
+                mRegistry.removeObserver(observer1);
+                assertThat(mRegistry.getObserverCount(), is(0));
+            }
+        });
+        mRegistry.addObserver(observer1);
+        mRegistry.addObserver(observer2);
+        mRegistry.addObserver(observer3);
+        InOrder orderVerifier = inOrder(observer1, observer2, observer3);
+        dispatchEvent(ON_PAUSE);
+        orderVerifier.verify(observer3).onPause();
+        orderVerifier.verify(observer2).onPause();
+        orderVerifier.verify(observer1).onPause();
+        dispatchEvent(ON_STOP);
+        orderVerifier.verify(observer3).onStop();
+        orderVerifier.verify(observer2).onStop();
+        orderVerifier.verify(observer1, never()).onStop();
+        dispatchEvent(ON_PAUSE);
+        orderVerifier.verify(observer3, never()).onPause();
+        orderVerifier.verify(observer2, never()).onPause();
+        orderVerifier.verify(observer1, never()).onPause();
+    }
+
+    @Test
+    public void deadParentInAddition() {
+        fullyInitializeRegistry();
+        final TestObserver observer2 = mock(TestObserver.class);
+        final TestObserver observer3 = mock(TestObserver.class);
+
+        TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                mRegistry.removeObserver(this);
+                assertThat(mRegistry.getObserverCount(), is(0));
+                mRegistry.addObserver(observer2);
+                mRegistry.addObserver(observer3);
+            }
+        });
+
+        mRegistry.addObserver(observer1);
+
+        InOrder inOrder = inOrder(observer1, observer2, observer3);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer2).onResume();
+        inOrder.verify(observer3).onStart();
+        inOrder.verify(observer3).onResume();
+    }
+
+    @Test
+    public void deadParentWhileTraversing() {
+        final TestObserver observer2 = mock(TestObserver.class);
+        final TestObserver observer3 = mock(TestObserver.class);
+        TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                mRegistry.removeObserver(this);
+                assertThat(mRegistry.getObserverCount(), is(0));
+                mRegistry.addObserver(observer2);
+                mRegistry.addObserver(observer3);
+            }
+        });
+        InOrder inOrder = inOrder(observer1, observer2, observer3);
+        mRegistry.addObserver(observer1);
+        dispatchEvent(ON_CREATE);
+        dispatchEvent(ON_START);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer3).onStart();
+    }
+
+    @Test
+    public void removeCascade() {
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer4 = mock(TestObserver.class);
+
+        final TestObserver observer2 = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                mRegistry.removeObserver(this);
+            }
+        });
+
+        TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onResume() {
+                mRegistry.removeObserver(this);
+                mRegistry.addObserver(observer2);
+                mRegistry.addObserver(observer3);
+                mRegistry.addObserver(observer4);
+            }
+        });
+        fullyInitializeRegistry();
+        mRegistry.addObserver(observer1);
+        InOrder inOrder = inOrder(observer1, observer2, observer3, observer4);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer1).onResume();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer3).onStart();
+        inOrder.verify(observer4).onCreate();
+        inOrder.verify(observer4).onStart();
+        inOrder.verify(observer3).onResume();
+        inOrder.verify(observer4).onResume();
+    }
+
+    @Test
+    public void changeStateDuringDescending() {
+        final TestObserver observer2 = mock(TestObserver.class);
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onPause() {
+                // but tonight I bounce back
+                mRegistry.handleLifecycleEvent(ON_RESUME);
+                mRegistry.addObserver(observer2);
+            }
+        });
+        fullyInitializeRegistry();
+        mRegistry.addObserver(observer1);
+        mRegistry.handleLifecycleEvent(ON_PAUSE);
+        InOrder inOrder = inOrder(observer1, observer2);
+        inOrder.verify(observer1).onPause();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer1).onResume();
+        inOrder.verify(observer2).onResume();
+    }
+
+    @Test
+    public void siblingLimitationCheck() {
+        fullyInitializeRegistry();
+        final TestObserver observer2 = mock(TestObserver.class);
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                mRegistry.addObserver(observer2);
+            }
+
+            @Override
+            void onResume() {
+                mRegistry.addObserver(observer3);
+            }
+        });
+        mRegistry.addObserver(observer1);
+        InOrder inOrder = inOrder(observer1, observer2, observer3);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer1).onResume();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer2).onStart();
+        inOrder.verify(observer2).onResume();
+        inOrder.verify(observer3).onStart();
+        inOrder.verify(observer3).onResume();
+    }
+
+    @Test
+    public void siblingRemovalLimitationCheck1() {
+        fullyInitializeRegistry();
+        final TestObserver observer2 = mock(TestObserver.class);
+        final TestObserver observer3 = mock(TestObserver.class);
+        final TestObserver observer4 = mock(TestObserver.class);
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                mRegistry.addObserver(observer2);
+            }
+
+            @Override
+            void onResume() {
+                mRegistry.removeObserver(observer2);
+                mRegistry.addObserver(observer3);
+                mRegistry.addObserver(observer4);
+            }
+        });
+        mRegistry.addObserver(observer1);
+        InOrder inOrder = inOrder(observer1, observer2, observer3, observer4);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer1).onResume();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer3).onStart();
+        inOrder.verify(observer4).onCreate();
+        inOrder.verify(observer4).onStart();
+        inOrder.verify(observer3).onResume();
+        inOrder.verify(observer4).onResume();
+    }
+
+    @Test
+    public void siblingRemovalLimitationCheck2() {
+        fullyInitializeRegistry();
+        final TestObserver observer2 = mock(TestObserver.class);
+        final TestObserver observer3 = spy(new TestObserver() {
+            @Override
+            void onCreate() {
+                mRegistry.removeObserver(observer2);
+            }
+        });
+        final TestObserver observer4 = mock(TestObserver.class);
+        final TestObserver observer1 = spy(new TestObserver() {
+            @Override
+            void onStart() {
+                mRegistry.addObserver(observer2);
+            }
+
+            @Override
+            void onResume() {
+                mRegistry.addObserver(observer3);
+                mRegistry.addObserver(observer4);
+            }
+        });
+
+        mRegistry.addObserver(observer1);
+        InOrder inOrder = inOrder(observer1, observer2, observer3, observer4);
+        inOrder.verify(observer1).onCreate();
+        inOrder.verify(observer1).onStart();
+        inOrder.verify(observer2).onCreate();
+        inOrder.verify(observer1).onResume();
+        inOrder.verify(observer3).onCreate();
+        inOrder.verify(observer3).onStart();
+        inOrder.verify(observer4).onCreate();
+        inOrder.verify(observer4).onStart();
+        inOrder.verify(observer3).onResume();
+        inOrder.verify(observer4).onResume();
+    }
+
+    @Test
+    public void sameObserverReAddition() {
+        TestObserver observer = mock(TestObserver.class);
+        mRegistry.addObserver(observer);
+        mRegistry.removeObserver(observer);
+        mRegistry.addObserver(observer);
+        dispatchEvent(ON_CREATE);
+        verify(observer).onCreate();
+    }
+
+    private void dispatchEvent(Lifecycle.Event event) {
+        when(mLifecycle.getCurrentState()).thenReturn(LifecycleRegistry.getStateAfter(event));
+        mRegistry.handleLifecycleEvent(event);
+    }
+
+    private void fullyInitializeRegistry() {
+        dispatchEvent(ON_CREATE);
+        dispatchEvent(ON_START);
+        dispatchEvent(ON_RESUME);
+    }
+
+    private abstract class TestObserver implements LifecycleObserver {
+        @OnLifecycleEvent(ON_CREATE)
+        void onCreate() {
+        }
+
+        @OnLifecycleEvent(ON_START)
+        void onStart() {
+        }
+
+        @OnLifecycleEvent(ON_RESUME)
+        void onResume() {
+        }
+
+        @OnLifecycleEvent(ON_PAUSE)
+        void onPause() {
+        }
+
+        @OnLifecycleEvent(ON_STOP)
+        void onStop() {
+        }
+
+        @OnLifecycleEvent(ON_DESTROY)
+        void onDestroy() {
+        }
+    }
+}
diff --git a/makeFlatfootRepo.sh b/makeFlatfootRepo.sh
new file mode 100755
index 0000000..1e5e817
--- /dev/null
+++ b/makeFlatfootRepo.sh
@@ -0,0 +1,3 @@
+# temporary script to make flatfoot repo, next to the support repo
+cd lifecycle && ./gradlew uploadArchives --info
+cd ../room && ./gradlew uploadArchives --info
diff --git a/media-compat/Android.mk b/media-compat/Android.mk
index 0050ea4..6727cdc 100644
--- a/media-compat/Android.mk
+++ b/media-compat/Android.mk
@@ -28,8 +28,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
 LOCAL_SRC_FILES := \
-    $(call all-java-files-under,ics) \
-    $(call all-java-files-under,jellybean-mr2) \
     $(call all-java-files-under,kitkat) \
     $(call all-java-files-under,api21) \
     $(call all-java-files-under,api22) \
@@ -38,7 +36,6 @@
     $(call all-java-files-under,java) \
     $(call all-Iaidl-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
diff --git a/media-compat/AndroidManifest-make.xml b/media-compat/AndroidManifest-make.xml
deleted file mode 100644
index c971549..0000000
--- a/media-compat/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.mediacompat">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.mediacompat"/>
-    <application />
-</manifest>
diff --git a/media-compat/AndroidManifest.xml b/media-compat/AndroidManifest.xml
index 7e9421e..f5e74a2 100644
--- a/media-compat/AndroidManifest.xml
+++ b/media-compat/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.mediacompat">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.mediacompat"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.mediacompat"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/media-compat/api21/android/support/v4/media/AudioAttributesCompatApi21.java b/media-compat/api21/android/support/v4/media/AudioAttributesCompatApi21.java
new file mode 100644
index 0000000..6d53b80
--- /dev/null
+++ b/media-compat/api21/android/support/v4/media/AudioAttributesCompatApi21.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.media;
+
+import android.media.AudioAttributes;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(21)
+class AudioAttributesCompatApi21 {
+    private static final String TAG = "AudioAttributesCompat";
+
+    // used to introspect AudioAttributes @hidden API
+    // I'm sorry, CheckStyle, but this is much more readable
+    private static Method sAudioAttributesToLegacyStreamType;
+
+    public static int toLegacyStreamType(Wrapper aaWrap) {
+        final AudioAttributes aaObject = aaWrap.unwrap();
+        try {
+            if (sAudioAttributesToLegacyStreamType == null) {
+                sAudioAttributesToLegacyStreamType = AudioAttributes.class.getMethod(
+                        "toLegacyStreamType", AudioAttributes.class);
+            }
+            Object result = sAudioAttributesToLegacyStreamType.invoke(
+                    null, aaObject);
+            return (Integer) result;
+        } catch (NoSuchMethodException | InvocationTargetException
+                | IllegalAccessException | ClassCastException e) {
+            Log.w(TAG, "getLegacyStreamType() failed on API21+", e);
+            return -1; // AudioSystem.STREAM_DEFAULT
+        }
+    }
+
+    static final class Wrapper {
+        private AudioAttributes mWrapped;
+        private Wrapper(AudioAttributes obj) {
+            mWrapped = obj;
+        }
+        public static Wrapper wrap(@NonNull AudioAttributes obj) {
+            if (obj == null) {
+                throw new IllegalArgumentException("AudioAttributesApi21.Wrapper cannot wrap null");
+            }
+            return new Wrapper(obj);
+        }
+        public AudioAttributes unwrap() {
+            return mWrapped;
+        }
+    }
+}
diff --git a/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index 9a0b0f3..0634309 100644
--- a/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.browse.MediaBrowser;
@@ -27,7 +26,6 @@
 import java.util.List;
 
 @RequiresApi(21)
-@TargetApi(21)
 class MediaBrowserCompatApi21 {
     static final String NULL_MEDIA_ITEM_ID =
             "android.support.v4.media.MediaBrowserCompat.NULL_MEDIA_ITEM";
diff --git a/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
index aa63588..cff846d 100644
--- a/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.media.browse.MediaBrowser;
@@ -31,7 +30,6 @@
 import java.util.List;
 
 @RequiresApi(21)
-@TargetApi(21)
 class MediaBrowserServiceCompatApi21 {
 
     public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
diff --git a/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
index 556d092..8361711 100644
--- a/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -15,7 +15,6 @@
  */
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.media.MediaDescription;
 import android.net.Uri;
@@ -24,7 +23,6 @@
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(21)
-@TargetApi(21)
 class MediaDescriptionCompatApi21 {
 
     public static String getMediaId(Object descriptionObj) {
diff --git a/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
index ed30c29..6020a08 100644
--- a/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.media.MediaMetadata;
 import android.media.Rating;
@@ -26,7 +25,6 @@
 import java.util.Set;
 
 @RequiresApi(21)
-@TargetApi(21)
 class MediaMetadataCompatApi21 {
     public static Set<String> keySet(Object metadataObj) {
         return ((MediaMetadata)metadataObj).keySet();
diff --git a/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java b/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
index ab5e4ef..6af9903 100644
--- a/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
+++ b/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.media.browse.MediaBrowser;
 import android.support.annotation.RequiresApi;
 
@@ -28,7 +27,6 @@
  * An adapter class for accessing the hidden framework classes, ParceledListSlice using reflection.
  */
 @RequiresApi(21)
-@TargetApi(21)
 class ParceledListSliceAdapterApi21 {
     private static Constructor sConstructor;
     static {
diff --git a/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java b/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
index 66f5144..cefbf59 100644
--- a/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
@@ -16,12 +16,10 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.media.VolumeProvider;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(21)
-@TargetApi(21)
 class VolumeProviderCompatApi21 {
     public static Object createVolumeProvider(int volumeControl, int maxVolume, int currentVolume,
             final Delegate delegate) {
diff --git a/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
index c498f7f..c13d901 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -37,7 +36,6 @@
 import java.util.List;
 
 @RequiresApi(21)
-@TargetApi(21)
 class MediaControllerCompatApi21 {
     public static Object fromToken(Context context, Object sessionToken) {
         return new MediaController(context, (MediaSession.Token) sessionToken);
diff --git a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index 38c42cb..698e37d 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -32,13 +31,16 @@
 import android.os.Parcelable;
 import android.os.ResultReceiver;
 import android.support.annotation.RequiresApi;
+import android.util.Log;
 
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
 
 @RequiresApi(21)
-@TargetApi(21)
 class MediaSessionCompatApi21 {
+    static final String TAG = "MediaSessionCompatApi21";
+
     public static Object createSession(Context context, String tag) {
         return new MediaSession(context, tag);
     }
@@ -136,20 +138,37 @@
         ((MediaSession) sessionObj).setExtras(extras);
     }
 
-    interface Callback extends MediaSessionCompatApi19.Callback {
-        public void onCommand(String command, Bundle extras, ResultReceiver cb);
-        public boolean onMediaButtonEvent(Intent mediaButtonIntent);
-        public void onPlay();
-        public void onPlayFromMediaId(String mediaId, Bundle extras);
-        public void onPlayFromSearch(String search, Bundle extras);
-        public void onSkipToQueueItem(long id);
-        public void onPause();
-        public void onSkipToNext();
-        public void onSkipToPrevious();
-        public void onFastForward();
-        public void onRewind();
-        public void onStop();
-        public void onCustomAction(String action, Bundle extras);
+    public static boolean hasCallback(Object sessionObj) {
+        Field callbackField = null;
+        try {
+            callbackField = sessionObj.getClass().getDeclaredField("mCallback");
+            if (callbackField != null) {
+                callbackField.setAccessible(true);
+                return callbackField.get(sessionObj) != null;
+            }
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            Log.w(TAG, "Failed to get mCallback object.");
+        }
+        return false;
+    }
+
+    interface Callback {
+        void onCommand(String command, Bundle extras, ResultReceiver cb);
+        boolean onMediaButtonEvent(Intent mediaButtonIntent);
+        void onPlay();
+        void onPlayFromMediaId(String mediaId, Bundle extras);
+        void onPlayFromSearch(String search, Bundle extras);
+        void onSkipToQueueItem(long id);
+        void onPause();
+        void onSkipToNext();
+        void onSkipToPrevious();
+        void onFastForward();
+        void onRewind();
+        void onStop();
+        void onSeekTo(long position);
+        void onSetRating(Object ratingObject);
+        void onSetRating(Object ratingObject, Bundle extras);
+        void onCustomAction(String action, Bundle extras);
     }
 
     static class CallbackProxy<T extends Callback> extends MediaSession.Callback {
diff --git a/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java b/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
index df6a203..577f35d 100644
--- a/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
@@ -24,7 +23,6 @@
 import java.util.List;
 
 @RequiresApi(21)
-@TargetApi(21)
 class PlaybackStateCompatApi21 {
     public static int getState(Object stateObj) {
         return ((PlaybackState)stateObj).getState();
diff --git a/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java b/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
index 687e965..b7ddc99 100644
--- a/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
+++ b/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
@@ -15,12 +15,10 @@
  */
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.media.session.MediaSession;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(22)
-@TargetApi(22)
 class MediaSessionCompatApi22 {
 
     public static void setRatingType(Object sessionObj, int type) {
diff --git a/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java b/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
index ff398b9..6be3f4b 100644
--- a/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
+++ b/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
@@ -24,7 +23,6 @@
 import java.util.List;
 
 @RequiresApi(22)
-@TargetApi(22)
 class PlaybackStateCompatApi22 {
     public static Bundle getExtras(Object stateObj) {
         return ((PlaybackState)stateObj).getExtras();
diff --git a/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
index dfab20b..0a12597 100644
--- a/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -16,14 +16,12 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.media.browse.MediaBrowser;
 import android.os.Parcel;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(23)
-@TargetApi(23)
 class MediaBrowserCompatApi23 {
 
     public static Object createItemCallback(ItemCallback callback) {
diff --git a/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
index 1091dec..7b30ad5 100644
--- a/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.media.browse.MediaBrowser;
 import android.os.Parcel;
@@ -24,7 +23,6 @@
 import android.support.v4.media.MediaBrowserServiceCompatApi21.ResultWrapper;
 
 @RequiresApi(23)
-@TargetApi(23)
 class MediaBrowserServiceCompatApi23 {
 
     public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
diff --git a/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
index 862fbbf..957cc54 100644
--- a/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
@@ -15,13 +15,11 @@
  */
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.media.MediaDescription;
 import android.net.Uri;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(23)
-@TargetApi(23)
 class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
     public static Uri getMediaUri(Object descriptionObj) {
         return ((MediaDescription) descriptionObj).getMediaUri();
diff --git a/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java b/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
index 92e49fc..12ce345 100644
--- a/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
@@ -16,14 +16,12 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(23)
-@TargetApi(23)
 class MediaControllerCompatApi23 {
 
     public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
diff --git a/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java b/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
index ddb25fc..2818f02 100644
--- a/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
@@ -16,13 +16,11 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(23)
-@TargetApi(23)
 class MediaSessionCompatApi23 {
 
     public static Object createCallback(Callback callback) {
diff --git a/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java b/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
new file mode 100644
index 0000000..4842cc1
--- /dev/null
+++ b/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.media;
+
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+
+import java.util.List;
+
+@RequiresApi(24)
+class MediaBrowserCompatApi24 {
+    public static Object createSubscriptionCallback(SubscriptionCallback callback) {
+        return new SubscriptionCallbackProxy<>(callback);
+    }
+
+    public static void subscribe(Object browserObj, String parentId, Bundle options,
+            Object subscriptionCallbackObj) {
+        ((MediaBrowser) browserObj).subscribe(parentId, options,
+                (MediaBrowser.SubscriptionCallback) subscriptionCallbackObj);
+    }
+
+    public static void unsubscribe(Object browserObj, String parentId,
+            Object subscriptionCallbackObj) {
+        ((MediaBrowser) browserObj).unsubscribe(parentId,
+                (MediaBrowser.SubscriptionCallback) subscriptionCallbackObj);
+    }
+
+    interface SubscriptionCallback extends MediaBrowserCompatApi21.SubscriptionCallback {
+        void onChildrenLoaded(@NonNull String parentId, List<?> children, @NonNull Bundle options);
+        void onError(@NonNull String parentId, @NonNull  Bundle options);
+    }
+
+    static class SubscriptionCallbackProxy<T extends SubscriptionCallback>
+            extends MediaBrowserCompatApi21.SubscriptionCallbackProxy<T> {
+        public SubscriptionCallbackProxy(T callback) {
+            super(callback);
+        }
+
+        @Override
+        public void onChildrenLoaded(@NonNull String parentId,
+                List<MediaBrowser.MediaItem> children, @NonNull Bundle options) {
+            mSubscriptionCallback.onChildrenLoaded(parentId, children, options);
+        }
+
+        @Override
+        public void onError(@NonNull String parentId, @NonNull Bundle options) {
+            mSubscriptionCallback.onError(parentId, options);
+        }
+    }
+}
diff --git a/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java b/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
index 2440864..a7e5e19 100644
--- a/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.media.browse.MediaBrowser;
 import android.os.Bundle;
@@ -30,7 +29,6 @@
 import java.util.List;
 
 @RequiresApi(24)
-@TargetApi(24)
 class MediaBrowserServiceCompatApi24 {
     private static final String TAG = "MBSCompatApi24";
 
diff --git a/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java b/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
index 3c8b650..c336d70 100644
--- a/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
@@ -16,14 +16,12 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(24)
-@TargetApi(24)
 class MediaControllerCompatApi24 {
 
     public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
diff --git a/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java b/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
index 506b04f..cd358e5 100644
--- a/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
@@ -16,7 +16,6 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Bundle;
@@ -27,7 +26,6 @@
 import java.lang.reflect.Method;
 
 @RequiresApi(24)
-@TargetApi(24)
 class MediaSessionCompatApi24 {
     private static final String TAG = "MediaSessionCompatApi24";
 
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index 8dd44bf..f4e3839 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -1,33 +1,28 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-media-compat'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-compat')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+    api project(':support-compat')
+
+    androidTestImplementation(libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation(libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+    androidTestImplementation project(':support-testutils')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
-                'ics',
                 'jellybean-mr2',
                 'kitkat',
                 'api21',
@@ -37,67 +32,16 @@
                 'java'
         ]
         main.aidl.srcDirs = ['java']
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
+        main.res.srcDirs 'res', 'res-public'
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-        exclude('android/content/pm/**')
-        exclude('android/service/media/**')
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v4'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Library media compat'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
 }
diff --git a/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java b/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
deleted file mode 100644
index b6f7a38..0000000
--- a/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.media.session;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.media.AudioManager;
-import android.media.MediaMetadataRetriever;
-import android.media.RemoteControlClient;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(14)
-@TargetApi(14)
-class MediaSessionCompatApi14 {
-    /***** RemoteControlClient States, we only need none as the others were public *******/
-    final static int RCC_PLAYSTATE_NONE = 0;
-
-    /***** MediaSession States *******/
-    final static int STATE_NONE = 0;
-    final static int STATE_STOPPED = 1;
-    final static int STATE_PAUSED = 2;
-    final static int STATE_PLAYING = 3;
-    final static int STATE_FAST_FORWARDING = 4;
-    final static int STATE_REWINDING = 5;
-    final static int STATE_BUFFERING = 6;
-    final static int STATE_ERROR = 7;
-    final static int STATE_CONNECTING = 8;
-    final static int STATE_SKIPPING_TO_PREVIOUS = 9;
-    final static int STATE_SKIPPING_TO_NEXT = 10;
-    final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
-
-    /***** PlaybackState actions *****/
-    private static final long ACTION_STOP = 1 << 0;
-    private static final long ACTION_PAUSE = 1 << 1;
-    private static final long ACTION_PLAY = 1 << 2;
-    private static final long ACTION_REWIND = 1 << 3;
-    private static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
-    private static final long ACTION_SKIP_TO_NEXT = 1 << 5;
-    private static final long ACTION_FAST_FORWARD = 1 << 6;
-    private static final long ACTION_PLAY_PAUSE = 1 << 9;
-
-    /***** MediaMetadata keys ********/
-    private static final String METADATA_KEY_ART = "android.media.metadata.ART";
-    private static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
-    private static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
-    private static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
-    private static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
-    private static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
-    private static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
-    private static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
-    private static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
-    private static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
-    private static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
-    private static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
-    private static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-    private static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-    private static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-
-    public static Object createRemoteControlClient(PendingIntent mbIntent) {
-        return new RemoteControlClient(mbIntent);
-    }
-
-    public static void setState(Object rccObj, int state) {
-        ((RemoteControlClient) rccObj).setPlaybackState(getRccStateFromState(state));
-    }
-
-    public static void setTransportControlFlags(Object rccObj, long actions) {
-        ((RemoteControlClient) rccObj).setTransportControlFlags(
-                getRccTransportControlFlagsFromActions(actions));
-    }
-
-    public static void setMetadata(Object rccObj, Bundle metadata) {
-        RemoteControlClient.MetadataEditor editor = ((RemoteControlClient) rccObj).editMetadata(
-                true);
-        buildOldMetadata(metadata, editor);
-        editor.apply();
-    }
-
-    public static void registerRemoteControlClient(Context context, Object rccObj) {
-        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        am.registerRemoteControlClient((RemoteControlClient) rccObj);
-    }
-
-    public static void unregisterRemoteControlClient(Context context, Object rccObj) {
-        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        am.unregisterRemoteControlClient((RemoteControlClient) rccObj);
-    }
-
-    static int getRccStateFromState(int state) {
-        switch (state) {
-            case STATE_CONNECTING:
-            case STATE_BUFFERING:
-                return RemoteControlClient.PLAYSTATE_BUFFERING;
-            case STATE_ERROR:
-                return RemoteControlClient.PLAYSTATE_ERROR;
-            case STATE_FAST_FORWARDING:
-                return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
-            case STATE_NONE:
-                return RCC_PLAYSTATE_NONE;
-            case STATE_PAUSED:
-                return RemoteControlClient.PLAYSTATE_PAUSED;
-            case STATE_PLAYING:
-                return RemoteControlClient.PLAYSTATE_PLAYING;
-            case STATE_REWINDING:
-                return RemoteControlClient.PLAYSTATE_REWINDING;
-            case STATE_SKIPPING_TO_PREVIOUS:
-                return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
-            case STATE_SKIPPING_TO_NEXT:
-            case STATE_SKIPPING_TO_QUEUE_ITEM:
-                return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
-            case STATE_STOPPED:
-                return RemoteControlClient.PLAYSTATE_STOPPED;
-            default:
-                return -1;
-        }
-    }
-
-    static int getRccTransportControlFlagsFromActions(long actions) {
-        int transportControlFlags = 0;
-        if ((actions & ACTION_STOP) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP;
-        }
-        if ((actions & ACTION_PAUSE) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
-        }
-        if ((actions & ACTION_PLAY) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
-        }
-        if ((actions & ACTION_REWIND) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
-        }
-        if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
-        }
-        if ((actions & ACTION_SKIP_TO_NEXT) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
-        }
-        if ((actions & ACTION_FAST_FORWARD) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
-        }
-        if ((actions & ACTION_PLAY_PAUSE) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
-        }
-        return transportControlFlags;
-    }
-
-    static void buildOldMetadata(Bundle metadata, RemoteControlClient.MetadataEditor editor) {
-        if (metadata == null) {
-            return;
-        }
-        if (metadata.containsKey(METADATA_KEY_ART)) {
-            Bitmap art = metadata.getParcelable(METADATA_KEY_ART);
-            editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
-        } else if (metadata.containsKey(METADATA_KEY_ALBUM_ART)) {
-            // Fall back to album art if the track art wasn't available
-            Bitmap art = metadata.getParcelable(METADATA_KEY_ALBUM_ART);
-            editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
-        }
-        if (metadata.containsKey(METADATA_KEY_ALBUM)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
-                    metadata.getString(METADATA_KEY_ALBUM));
-        }
-        if (metadata.containsKey(METADATA_KEY_ALBUM_ARTIST)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
-                    metadata.getString(METADATA_KEY_ALBUM_ARTIST));
-        }
-        if (metadata.containsKey(METADATA_KEY_ARTIST)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST,
-                    metadata.getString(METADATA_KEY_ARTIST));
-        }
-        if (metadata.containsKey(METADATA_KEY_AUTHOR)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR,
-                    metadata.getString(METADATA_KEY_AUTHOR));
-        }
-        if (metadata.containsKey(METADATA_KEY_COMPILATION)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
-                    metadata.getString(METADATA_KEY_COMPILATION));
-        }
-        if (metadata.containsKey(METADATA_KEY_COMPOSER)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER,
-                    metadata.getString(METADATA_KEY_COMPOSER));
-        }
-        if (metadata.containsKey(METADATA_KEY_DATE)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE,
-                    metadata.getString(METADATA_KEY_DATE));
-        }
-        if (metadata.containsKey(METADATA_KEY_DISC_NUMBER)) {
-            editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
-                    metadata.getLong(METADATA_KEY_DISC_NUMBER));
-        }
-        if (metadata.containsKey(METADATA_KEY_DURATION)) {
-            editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
-                    metadata.getLong(METADATA_KEY_DURATION));
-        }
-        if (metadata.containsKey(METADATA_KEY_GENRE)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE,
-                    metadata.getString(METADATA_KEY_GENRE));
-        }
-        if (metadata.containsKey(METADATA_KEY_TITLE)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
-                    metadata.getString(METADATA_KEY_TITLE));
-        }
-        if (metadata.containsKey(METADATA_KEY_TRACK_NUMBER)) {
-            editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
-                    metadata.getLong(METADATA_KEY_TRACK_NUMBER));
-        }
-        if (metadata.containsKey(METADATA_KEY_WRITER)) {
-            editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER,
-                    metadata.getString(METADATA_KEY_WRITER));
-        }
-    }
-}
diff --git a/media-compat/java/android/support/v4/media/AudioAttributesCompat.java b/media-compat/java/android/support/v4/media/AudioAttributesCompat.java
new file mode 100644
index 0000000..1bd09de
--- /dev/null
+++ b/media-compat/java/android/support/v4/media/AudioAttributesCompat.java
@@ -0,0 +1,813 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.media;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.os.Build;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.util.SparseIntArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about an audio stream.
+ *
+ * <p><code>AudioAttributesCompat</code> supersede the notion of stream types (see for instance
+ * {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM}) for defining the behavior
+ * of audio playback. Attributes allow an application to specify more information than is conveyed
+ * in a stream type by allowing the application to define:
+ *
+ * <ul>
+ * <li>usage: "why" you are playing a sound, what is this sound used for. This is achieved with
+ * the "usage" information. Examples of usage are {@link #USAGE_MEDIA} and {@link
+ * #USAGE_ALARM}. These two examples are the closest to stream types, but more detailed use
+ * cases are available. Usage information is more expressive than a stream type, and allows
+ * certain platforms or routing policies to use this information for more refined volume or
+ * routing decisions. Usage is the most important information to supply in <code>
+ * AudioAttributesCompat</code> and it is recommended to build any instance with this
+ * information supplied, see {@link AudioAttributesCompat.Builder} for exceptions.
+ * <li>content type: "what" you are playing. The content type expresses the general category of
+ * the content. This information is optional. But in case it is known (for instance {@link
+ * #CONTENT_TYPE_MOVIE} for a movie streaming service or {@link #CONTENT_TYPE_MUSIC} for a
+ * music playback application) this information might be used by the audio framework to
+ * selectively configure some audio post-processing blocks.
+ * <li>flags: "how" is playback to be affected, see the flag definitions for the specific playback
+ * behaviors they control.
+ * </ul>
+ *
+ * <p><code>AudioAttributesCompat</code> instance is built through its builder, {@link
+ * AudioAttributesCompat.Builder}. Also see {@link android.media.AudioAttributes} for the framework
+ * implementation of this class.
+ */
+public class AudioAttributesCompat {
+    private static final String TAG = "AudioAttributesCompat";
+
+    /**
+     * Content type value to use when the content type is unknown, or other than the ones defined.
+     */
+    public static final int CONTENT_TYPE_UNKNOWN = AudioAttributes.CONTENT_TYPE_UNKNOWN;
+    /** Content type value to use when the content type is speech. */
+    public static final int CONTENT_TYPE_SPEECH = AudioAttributes.CONTENT_TYPE_SPEECH;
+    /** Content type value to use when the content type is music. */
+    public static final int CONTENT_TYPE_MUSIC = AudioAttributes.CONTENT_TYPE_MUSIC;
+    /**
+     * Content type value to use when the content type is a soundtrack, typically accompanying a
+     * movie or TV program.
+     */
+    public static final int CONTENT_TYPE_MOVIE = AudioAttributes.CONTENT_TYPE_MOVIE;
+    /**
+     * Content type value to use when the content type is a sound used to accompany a user action,
+     * such as a beep or sound effect expressing a key click, or event, such as the type of a sound
+     * for a bonus being received in a game. These sounds are mostly synthesized or short Foley
+     * sounds.
+     */
+    public static final int CONTENT_TYPE_SONIFICATION = AudioAttributes.CONTENT_TYPE_SONIFICATION;
+
+    /** Usage value to use when the usage is unknown. */
+    public static final int USAGE_UNKNOWN = AudioAttributes.USAGE_UNKNOWN;
+    /** Usage value to use when the usage is media, such as music, or movie soundtracks. */
+    public static final int USAGE_MEDIA = AudioAttributes.USAGE_MEDIA;
+    /** Usage value to use when the usage is voice communications, such as telephony or VoIP. */
+    public static final int USAGE_VOICE_COMMUNICATION = AudioAttributes.USAGE_VOICE_COMMUNICATION;
+    /**
+     * Usage value to use when the usage is in-call signalling, such as with a "busy" beep, or DTMF
+     * tones.
+     */
+    public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING =
+             AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
+    /** Usage value to use when the usage is an alarm (e.g. wake-up alarm). */
+    public static final int USAGE_ALARM = AudioAttributes.USAGE_ALARM;
+    /**
+     * Usage value to use when the usage is notification. See other notification usages for more
+     * specialized uses.
+     */
+    public static final int USAGE_NOTIFICATION = AudioAttributes.USAGE_NOTIFICATION;
+    /** Usage value to use when the usage is telephony ringtone. */
+    public static final int USAGE_NOTIFICATION_RINGTONE =
+             AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+    /**
+     * Usage value to use when the usage is a request to enter/end a communication, such as a VoIP
+     * communication or video-conference.
+     */
+    public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST =
+             AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST;
+    /**
+     * Usage value to use when the usage is notification for an "instant" communication such as a
+     * chat, or SMS.
+     */
+    public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT =
+             AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT;
+    /**
+     * Usage value to use when the usage is notification for a non-immediate type of communication
+     * such as e-mail.
+     */
+    public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED =
+             AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED;
+    /**
+     * Usage value to use when the usage is to attract the user's attention, such as a reminder or
+     * low battery warning.
+     */
+    public static final int USAGE_NOTIFICATION_EVENT = AudioAttributes.USAGE_NOTIFICATION_EVENT;
+    /** Usage value to use when the usage is for accessibility, such as with a screen reader. */
+    public static final int USAGE_ASSISTANCE_ACCESSIBILITY =
+             AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
+    /** Usage value to use when the usage is driving or navigation directions. */
+    public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE =
+             AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
+    /** Usage value to use when the usage is sonification, such as with user interface sounds. */
+    public static final int USAGE_ASSISTANCE_SONIFICATION =
+             AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
+    /** Usage value to use when the usage is for game audio. */
+    public static final int USAGE_GAME = AudioAttributes.USAGE_GAME;
+
+    // usage not available to clients
+    private static final int USAGE_VIRTUAL_SOURCE = 15; // AudioAttributes.USAGE_VIRTUAL_SOURCE;
+    /**
+     * Usage value to use for audio responses to user queries, audio instructions or help
+     * utterances.
+     */
+    public static final int USAGE_ASSISTANT = AudioAttributes.USAGE_ASSISTANT;
+
+    /**
+     * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
+     * if applicable.
+     */
+
+    // private API
+    private static final int SUPPRESSIBLE_NOTIFICATION = 1;
+
+    private static final int SUPPRESSIBLE_CALL = 2;
+    private static final SparseIntArray SUPPRESSIBLE_USAGES;
+
+    // used by tests
+    private static boolean sForceLegacyBehavior;
+
+    static {
+        SUPPRESSIBLE_USAGES = new SparseIntArray();
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION, SUPPRESSIBLE_NOTIFICATION);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_RINGTONE, SUPPRESSIBLE_CALL);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_COMMUNICATION_REQUEST, SUPPRESSIBLE_CALL);
+        SUPPRESSIBLE_USAGES.put(
+                USAGE_NOTIFICATION_COMMUNICATION_INSTANT, SUPPRESSIBLE_NOTIFICATION);
+        SUPPRESSIBLE_USAGES.put(
+                USAGE_NOTIFICATION_COMMUNICATION_DELAYED, SUPPRESSIBLE_NOTIFICATION);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT, SUPPRESSIBLE_NOTIFICATION);
+    }
+
+    private static final int[] SDK_USAGES = {
+            USAGE_UNKNOWN,
+            USAGE_MEDIA,
+            USAGE_VOICE_COMMUNICATION,
+            USAGE_VOICE_COMMUNICATION_SIGNALLING,
+            USAGE_ALARM,
+            USAGE_NOTIFICATION,
+            USAGE_NOTIFICATION_RINGTONE,
+            USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+            USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+            USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+            USAGE_NOTIFICATION_EVENT,
+            USAGE_ASSISTANCE_ACCESSIBILITY,
+            USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+            USAGE_ASSISTANCE_SONIFICATION,
+            USAGE_GAME,
+            USAGE_ASSISTANT,
+    };
+
+    /** Flag defining a behavior where the audibility of the sound will be ensured by the system. */
+    public static final int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
+
+    // flags for @hide API so we can create a proper flags mask
+    private static final int FLAG_SECURE = 0x1 << 1;
+    private static final int FLAG_SCO = 0x1 << 2;
+    private static final int FLAG_BEACON = 0x1 << 3;
+
+    /** Flag requesting the use of an output stream supporting hardware A/V synchronization. */
+    public static final int FLAG_HW_AV_SYNC = 0x1 << 4;
+
+    // more @hide flags
+    private static final int FLAG_HW_HOTWORD = 0x1 << 5;
+    private static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1 << 6;
+    private static final int FLAG_BYPASS_MUTE = 0x1 << 7;
+    private static final int FLAG_LOW_LATENCY = 0x1 << 8;
+    private static final int FLAG_DEEP_BUFFER = 0x1 << 9;
+
+    private static final int FLAG_ALL =
+            (FLAG_AUDIBILITY_ENFORCED
+                    | FLAG_SECURE
+                    | FLAG_SCO
+                    | FLAG_BEACON
+                    | FLAG_HW_AV_SYNC
+                    | FLAG_HW_HOTWORD
+                    | FLAG_BYPASS_INTERRUPTION_POLICY
+                    | FLAG_BYPASS_MUTE
+                    | FLAG_LOW_LATENCY
+                    | FLAG_DEEP_BUFFER);
+    private static final int FLAG_ALL_PUBLIC =
+            (FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY);
+
+    int mUsage = USAGE_UNKNOWN;
+    int mContentType = CONTENT_TYPE_UNKNOWN;
+    int mFlags = 0x0;
+    Integer mLegacyStream;
+    private AudioAttributesCompatApi21.Wrapper mAudioAttributesWrapper;
+
+    private AudioAttributesCompat() {
+    }
+
+    /**
+     * Returns the stream type matching the given attributes for volume control. Use this method to
+     * derive the stream type needed to configure the volume control slider in an {@link
+     * android.app.Activity} with {@link android.app.Activity#setVolumeControlStream(int)}. <br>
+     * Do not use this method to set the stream type on an audio player object (e.g. {@link
+     * android.media.AudioTrack}, {@link android.media.MediaPlayer}) as this is deprecated;
+     * use <code>AudioAttributes</code> instead.
+     *
+     * @return a valid stream type for <code>Activity</code> or stream volume control that matches
+     * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct
+     * match. Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value for {@link
+     * AudioManager#setStreamVolume(int, int, int)}.
+     */
+    public int getVolumeControlStream() {
+        if (this == null) {
+            throw new IllegalArgumentException("Invalid null audio attributes");
+        }
+        if (Build.VERSION.SDK_INT >= 26
+                && !sForceLegacyBehavior
+                && unwrap() != null) {
+            return ((AudioAttributes) unwrap()).getVolumeControlStream();
+        }
+        return toVolumeStreamType(true, this);
+    }
+
+    // public API unique to AudioAttributesCompat
+
+    /**
+     * If the current SDK level is 21 or higher, return the {@link AudioAttributes} object inside
+     * this {@link AudioAttributesCompat}. Otherwise <code>null</code>.
+     *
+     * @return the underlying {@link AudioAttributes} object or null
+     */
+    @Nullable
+    public Object unwrap() {
+        if (mAudioAttributesWrapper != null) {
+            return mAudioAttributesWrapper.unwrap();
+        }
+        return null;
+    }
+
+    /**
+     * Return a stream type passed to {@link Builder#setLegacyStreamType(int)}, or -1 if no legacy
+     * stream is available
+     *
+     * @return the stream type {@see AudioManager}
+     */
+    public int getLegacyStreamType() {
+        // case 1: developer explicitly set a legacy stream,
+        // so just hand that back
+        if (mLegacyStream != null) {
+            return mLegacyStream;
+        }
+
+        // case 2: API 21+ and we have a real AudioAttributes
+        // the same caveats in AudioAttributes#toLegacyStreamTyoe apply:
+        // only use this for volume control
+        if (Build.VERSION.SDK_INT >= 21) {
+            if (!sForceLegacyBehavior) {
+                return AudioAttributesCompatApi21.toLegacyStreamType(mAudioAttributesWrapper);
+            }
+        }
+
+        // case 3: developer set up AudioAttrs using new flags/usage APIs
+        // but we are running pre-API21, so use the heuristic below
+        return toVolumeStreamType(false, mFlags, mUsage);
+    }
+
+    /**
+     * Create an {@link AudioAttributesCompat} given an API 21 {@link AudioAttributes} object.
+     *
+     * @param aa an instance of {@link AudioAttributes}
+     * @return the new <code>AudioAttributesCompat</code>, or <code>null</code> on API &lt; 21
+     */
+    @Nullable
+    public static AudioAttributesCompat wrap(@NonNull final Object aa) {
+        if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior) {
+            final AudioAttributesCompat aac = new AudioAttributesCompat();
+            aac.mAudioAttributesWrapper =
+                    AudioAttributesCompatApi21.Wrapper.wrap((AudioAttributes) aa);
+            return aac;
+        }
+        return null;
+    }
+
+    // The rest of this file implements an approximation to AudioAttributes using old stream types
+
+    /**
+     * Return the content type.
+     *
+     * @return one of the values that can be set in {@link Builder#setContentType(int)}
+     */
+    public int getContentType() {
+        if (Build.VERSION.SDK_INT >= 21
+                && !sForceLegacyBehavior
+                && mAudioAttributesWrapper != null) {
+            return mAudioAttributesWrapper.unwrap().getContentType();
+        } else {
+            return mContentType;
+        }
+    }
+
+    /**
+     * Return the usage.
+     *
+     * @return one of the values that can be set in {@link Builder#setUsage(int)}
+     */
+    public @AttributeUsage int getUsage() {
+        if (Build.VERSION.SDK_INT >= 21
+                && !sForceLegacyBehavior
+                && mAudioAttributesWrapper != null) {
+            return mAudioAttributesWrapper.unwrap().getUsage();
+        } else {
+            return mUsage;
+        }
+    }
+
+    /**
+     * Return the flags.
+     *
+     * @return a combined mask of all flags
+     */
+    public int getFlags() {
+        if (Build.VERSION.SDK_INT >= 21
+                && !sForceLegacyBehavior
+                && mAudioAttributesWrapper != null) {
+            return mAudioAttributesWrapper.unwrap().getFlags();
+        } else {
+            int flags = mFlags;
+            int legacyStream = getLegacyStreamType();
+            if (legacyStream == AudioManagerHidden.STREAM_BLUETOOTH_SCO) {
+                flags |= FLAG_SCO;
+            } else if (legacyStream == AudioManagerHidden.STREAM_SYSTEM_ENFORCED) {
+                flags |= FLAG_AUDIBILITY_ENFORCED;
+            }
+            return flags & FLAG_ALL_PUBLIC;
+        }
+    }
+
+    /**
+     * Builder class for {@link AudioAttributesCompat} objects.
+     *
+     * <p>example:
+     *
+     * <pre class="prettyprint">
+     * new AudioAttributes.Builder()
+     * .setUsage(AudioAttributes.USAGE_MEDIA)
+     * .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+     * .build();
+     * </pre>
+     *
+     * <p>By default all types of information (usage, content type, flags) conveyed by an <code>
+     * AudioAttributesCompat</code> instance are set to "unknown". Unknown information will be
+     * interpreted as a default value that is dependent on the context of use, for instance a {@link
+     * android.media.MediaPlayer} will use a default usage of
+     * {@link AudioAttributesCompat#USAGE_MEDIA}. See also {@link AudioAttributes.Builder}.
+     */
+    public static class Builder {
+        private int mUsage = USAGE_UNKNOWN;
+        private int mContentType = CONTENT_TYPE_UNKNOWN;
+        private int mFlags = 0x0;
+        private Integer mLegacyStream;
+        private Object mAAObject;
+
+        /**
+         * Constructs a new Builder with the defaults. By default, usage and content type are
+         * respectively {@link AudioAttributesCompat#USAGE_UNKNOWN} and {@link
+         * AudioAttributesCompat#CONTENT_TYPE_UNKNOWN}, and flags are 0. It is recommended to
+         * configure the usage (with {@link #setUsage(int)}) or deriving attributes from a legacy
+         * stream type (with {@link #setLegacyStreamType(int)}) before calling {@link #build()} to
+         * override any default playback behavior in terms of routing and volume management.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given AudioAttributes
+         *
+         * @param aa the AudioAttributesCompat object whose data will be reused in the new Builder.
+         */
+        public Builder(AudioAttributesCompat aa) {
+            mUsage = aa.mUsage;
+            mContentType = aa.mContentType;
+            mFlags = aa.mFlags;
+            mLegacyStream = aa.mLegacyStream;
+            mAAObject = aa.unwrap();
+        }
+
+        /**
+         * Combines all of the attributes that have been set and return a new {@link
+         * AudioAttributesCompat} object.
+         *
+         * @return a new {@link AudioAttributesCompat} object
+         */
+        public AudioAttributesCompat build() {
+            if (!sForceLegacyBehavior && Build.VERSION.SDK_INT >= 21) {
+                // API21
+                if (mAAObject != null) {
+                    // best case: underlying real AudioAttributes
+                    return wrap(mAAObject);
+                } else {
+                    // set up an API21 builder with whatever we have
+                    AudioAttributes.Builder api21Builder =
+                             new AudioAttributes.Builder()
+                                .setContentType(mContentType)
+                                .setFlags(mFlags)
+                                .setUsage(mUsage);
+                    if (mLegacyStream != null) {
+                        // if a legacy stream was specified, throw that in
+                        api21Builder.setLegacyStreamType(mLegacyStream);
+                    }
+                    return wrap(api21Builder.build());
+                }
+            } else {
+                // pre-API21
+                final AudioAttributesCompat aac = new AudioAttributesCompat();
+                aac.mContentType = mContentType;
+                aac.mFlags = mFlags;
+                aac.mUsage = mUsage;
+                aac.mLegacyStream = mLegacyStream;
+                aac.mAudioAttributesWrapper = null;
+                return aac;
+            }
+        }
+
+        /**
+         * Sets the attribute describing what is the intended use of the the audio signal, such as
+         * alarm or ringtone.
+         *
+         * @param usage one of {@link AudioAttributesCompat#USAGE_UNKNOWN}, {@link
+         *              AudioAttributesCompat#USAGE_MEDIA}, {@link
+         *              AudioAttributesCompat#USAGE_VOICE_COMMUNICATION}, {@link
+         *              AudioAttributesCompat#USAGE_VOICE_COMMUNICATION_SIGNALLING}, {@link
+         *              AudioAttributesCompat#USAGE_ALARM},
+         *              {@link AudioAttributesCompat#USAGE_NOTIFICATION},
+         *              {@link AudioAttributesCompat#USAGE_NOTIFICATION_RINGTONE}, {@link
+         *              AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, {@link
+         *              AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, {@link
+         *              AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, {@link
+         *              AudioAttributesCompat#USAGE_NOTIFICATION_EVENT}, {@link
+         *              AudioAttributesCompat#USAGE_ASSISTANT}, {@link
+         *              AudioAttributesCompat#USAGE_ASSISTANCE_ACCESSIBILITY}, {@link
+         *              AudioAttributesCompat#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, {@link
+         *              AudioAttributesCompat#USAGE_ASSISTANCE_SONIFICATION}, {@link
+         *              AudioAttributesCompat#USAGE_GAME}.
+         * @return the same Builder instance.
+         */
+        public Builder setUsage(@AttributeUsage int usage) {
+            switch (usage) {
+                case USAGE_UNKNOWN:
+                case USAGE_MEDIA:
+                case USAGE_VOICE_COMMUNICATION:
+                case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+                case USAGE_ALARM:
+                case USAGE_NOTIFICATION:
+                case USAGE_NOTIFICATION_RINGTONE:
+                case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+                case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+                case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+                case USAGE_NOTIFICATION_EVENT:
+                case USAGE_ASSISTANCE_ACCESSIBILITY:
+                case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+                case USAGE_ASSISTANCE_SONIFICATION:
+                case USAGE_GAME:
+                case USAGE_VIRTUAL_SOURCE:
+                    mUsage = usage;
+                    break;
+                case USAGE_ASSISTANT:
+                    if (!sForceLegacyBehavior && Build.VERSION.SDK_INT > 25) {
+                        mUsage = usage;
+                    } else {
+                        mUsage = USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
+                    }
+                    break;
+                default:
+                    mUsage = USAGE_UNKNOWN;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the attribute describing the content type of the audio signal, such as speech, or
+         * music.
+         *
+         * @param contentType the content type values, one of {@link
+         *                    AudioAttributesCompat#CONTENT_TYPE_MOVIE}, {@link
+         *                    AudioAttributesCompat#CONTENT_TYPE_MUSIC}, {@link
+         *                    AudioAttributesCompat#CONTENT_TYPE_SONIFICATION}, {@link
+         *                    AudioAttributesCompat#CONTENT_TYPE_SPEECH}, {@link
+         *                    AudioAttributesCompat#CONTENT_TYPE_UNKNOWN}.
+         * @return the same Builder instance.
+         */
+        public Builder setContentType(@AttributeContentType int contentType) {
+            switch (contentType) {
+                case CONTENT_TYPE_UNKNOWN:
+                case CONTENT_TYPE_MOVIE:
+                case CONTENT_TYPE_MUSIC:
+                case CONTENT_TYPE_SONIFICATION:
+                case CONTENT_TYPE_SPEECH:
+                    mContentType = contentType;
+                    break;
+                default:
+                    mUsage = CONTENT_TYPE_UNKNOWN;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the combination of flags.
+         *
+         * <p>This is a bitwise OR with the existing flags.
+         *
+         * @param flags a combination of {@link AudioAttributesCompat#FLAG_AUDIBILITY_ENFORCED},
+         *              {@link AudioAttributesCompat#FLAG_HW_AV_SYNC}.
+         * @return the same Builder instance.
+         */
+        public Builder setFlags(int flags) {
+            flags &= AudioAttributesCompat.FLAG_ALL;
+            mFlags |= flags;
+            return this;
+        }
+
+        /**
+         * Create an {@link AudioAttributesCompat} that best approximates the specified {@link
+         * AudioManager} stream type constant.
+         *
+         * @param streamType one of <code>AudioManager.STREAM_*</code>
+         * @return this same Builder
+         */
+        public Builder setLegacyStreamType(int streamType) {
+            if (streamType == AudioManagerHidden.STREAM_ACCESSIBILITY) {
+                throw new IllegalArgumentException(
+                        "STREAM_ACCESSIBILITY is not a legacy stream "
+                                + "type that was used for audio playback");
+            }
+            mLegacyStream = streamType;
+            mUsage = usageForStreamType(streamType);
+            return this;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        if (Build.VERSION.SDK_INT >= 21
+                && !sForceLegacyBehavior
+                && mAudioAttributesWrapper != null) {
+            return mAudioAttributesWrapper.unwrap().hashCode();
+        }
+
+        return Arrays.hashCode(new Object[] {mContentType, mFlags, mUsage, mLegacyStream});
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("AudioAttributesCompat:");
+        if (unwrap() != null) {
+            sb.append(" audioattributes=").append(unwrap());
+        } else {
+            if (mLegacyStream != null) {
+                sb.append(" stream=").append(mLegacyStream);
+                sb.append(" derived");
+            }
+            sb.append(" usage=")
+                    .append(usageToString())
+                    .append(" content=")
+                    .append(mContentType)
+                    .append(" flags=0x")
+                    .append(Integer.toHexString(mFlags).toUpperCase());
+        }
+        return sb.toString();
+    }
+
+    String usageToString() {
+        return usageToString(mUsage);
+    }
+
+    static String usageToString(int usage) {
+        switch (usage) {
+            case USAGE_UNKNOWN:
+                return new String("USAGE_UNKNOWN");
+            case USAGE_MEDIA:
+                return new String("USAGE_MEDIA");
+            case USAGE_VOICE_COMMUNICATION:
+                return new String("USAGE_VOICE_COMMUNICATION");
+            case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+                return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING");
+            case USAGE_ALARM:
+                return new String("USAGE_ALARM");
+            case USAGE_NOTIFICATION:
+                return new String("USAGE_NOTIFICATION");
+            case USAGE_NOTIFICATION_RINGTONE:
+                return new String("USAGE_NOTIFICATION_RINGTONE");
+            case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+                return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST");
+            case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+                return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT");
+            case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+                return new String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED");
+            case USAGE_NOTIFICATION_EVENT:
+                return new String("USAGE_NOTIFICATION_EVENT");
+            case USAGE_ASSISTANCE_ACCESSIBILITY:
+                return new String("USAGE_ASSISTANCE_ACCESSIBILITY");
+            case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+                return new String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE");
+            case USAGE_ASSISTANCE_SONIFICATION:
+                return new String("USAGE_ASSISTANCE_SONIFICATION");
+            case USAGE_GAME:
+                return new String("USAGE_GAME");
+            case USAGE_ASSISTANT:
+                return new String("USAGE_ASSISTANT");
+            default:
+                return new String("unknown usage " + usage);
+        }
+    }
+
+    private abstract static class AudioManagerHidden {
+        public static final int STREAM_BLUETOOTH_SCO = 6;
+        public static final int STREAM_SYSTEM_ENFORCED = 7;
+        public static final int STREAM_TTS = 9;
+        public static final int STREAM_ACCESSIBILITY = 10;
+    }
+
+    private static int usageForStreamType(int streamType) {
+        switch (streamType) {
+            case AudioManager.STREAM_VOICE_CALL:
+                return USAGE_VOICE_COMMUNICATION;
+            case AudioManagerHidden.STREAM_SYSTEM_ENFORCED:
+            case AudioManager.STREAM_SYSTEM:
+                return USAGE_ASSISTANCE_SONIFICATION;
+            case AudioManager.STREAM_RING:
+                return USAGE_NOTIFICATION_RINGTONE;
+            case AudioManager.STREAM_MUSIC:
+                return USAGE_MEDIA;
+            case AudioManager.STREAM_ALARM:
+                return USAGE_ALARM;
+            case AudioManager.STREAM_NOTIFICATION:
+                return USAGE_NOTIFICATION;
+            case AudioManagerHidden.STREAM_BLUETOOTH_SCO:
+                return USAGE_VOICE_COMMUNICATION;
+            case AudioManager.STREAM_DTMF:
+                return USAGE_VOICE_COMMUNICATION_SIGNALLING;
+            case AudioManagerHidden.STREAM_ACCESSIBILITY:
+                return USAGE_ASSISTANCE_ACCESSIBILITY;
+            case AudioManagerHidden.STREAM_TTS:
+            default:
+                return USAGE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Prevent AudioAttributes from being used even on platforms that support it.
+     *
+     * @hide For testing only.
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static void setForceLegacyBehavior(boolean force) {
+        sForceLegacyBehavior = force;
+    }
+
+    static int toVolumeStreamType(boolean fromGetVolumeControlStream, AudioAttributesCompat aa) {
+        return toVolumeStreamType(fromGetVolumeControlStream, aa.getFlags(), aa.getUsage());
+    }
+
+    static int toVolumeStreamType(
+            boolean fromGetVolumeControlStream, int flags, @AttributeUsage int usage) {
+        // flags to stream type mapping
+        if ((flags & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) {
+            return fromGetVolumeControlStream
+                    ? AudioManager.STREAM_SYSTEM
+                    : AudioManagerHidden.STREAM_SYSTEM_ENFORCED;
+        }
+        if ((flags & FLAG_SCO) == FLAG_SCO) {
+            return fromGetVolumeControlStream
+                    ? AudioManager.STREAM_VOICE_CALL
+                    : AudioManagerHidden.STREAM_BLUETOOTH_SCO;
+        }
+
+        // usage to stream type mapping
+        switch (usage) {
+            case USAGE_MEDIA:
+            case USAGE_GAME:
+            case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+            case USAGE_ASSISTANT:
+                return AudioManager.STREAM_MUSIC;
+            case USAGE_ASSISTANCE_SONIFICATION:
+                return AudioManager.STREAM_SYSTEM;
+            case USAGE_VOICE_COMMUNICATION:
+                return AudioManager.STREAM_VOICE_CALL;
+            case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+                return fromGetVolumeControlStream
+                        ? AudioManager.STREAM_VOICE_CALL
+                        : AudioManager.STREAM_DTMF;
+            case USAGE_ALARM:
+                return AudioManager.STREAM_ALARM;
+            case USAGE_NOTIFICATION_RINGTONE:
+                return AudioManager.STREAM_RING;
+            case USAGE_NOTIFICATION:
+            case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+            case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+            case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+            case USAGE_NOTIFICATION_EVENT:
+                return AudioManager.STREAM_NOTIFICATION;
+            case USAGE_ASSISTANCE_ACCESSIBILITY:
+                return AudioManagerHidden.STREAM_ACCESSIBILITY;
+            case USAGE_UNKNOWN:
+                return fromGetVolumeControlStream
+                        ? AudioManager.USE_DEFAULT_STREAM_TYPE
+                        : AudioManager.STREAM_MUSIC;
+            default:
+                if (fromGetVolumeControlStream) {
+                    throw new IllegalArgumentException(
+                            "Unknown usage value " + usage + " in audio attributes");
+                } else {
+                    return AudioManager.STREAM_MUSIC;
+                }
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final AudioAttributesCompat that = (AudioAttributesCompat) o;
+
+        if (Build.VERSION.SDK_INT >= 21
+                && !sForceLegacyBehavior
+                && mAudioAttributesWrapper != null) {
+            return mAudioAttributesWrapper.unwrap().equals(that.unwrap());
+        }
+
+        return ((mContentType == that.getContentType())
+                && (mFlags == that.getFlags())
+                && (mUsage == that.getUsage())
+                && (mLegacyStream != null ? mLegacyStream.equals(that.mLegacyStream)
+                        : that.mLegacyStream == null)); // query the slot directly, don't guess
+    }
+
+    /** @hide */
+    @IntDef({
+            USAGE_UNKNOWN,
+            USAGE_MEDIA,
+            USAGE_VOICE_COMMUNICATION,
+            USAGE_VOICE_COMMUNICATION_SIGNALLING,
+            USAGE_ALARM,
+            USAGE_NOTIFICATION,
+            USAGE_NOTIFICATION_RINGTONE,
+            USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+            USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+            USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+            USAGE_NOTIFICATION_EVENT,
+            USAGE_ASSISTANCE_ACCESSIBILITY,
+            USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+            USAGE_ASSISTANCE_SONIFICATION,
+            USAGE_GAME,
+            USAGE_ASSISTANT,
+    })
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttributeUsage {
+    }
+
+    /** @hide */
+    @IntDef({
+            CONTENT_TYPE_UNKNOWN,
+            CONTENT_TYPE_SPEECH,
+            CONTENT_TYPE_MUSIC,
+            CONTENT_TYPE_MOVIE,
+            CONTENT_TYPE_SONIFICATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY_GROUP)
+    public @interface AttributeContentType {
+    }
+}
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
index 10feb0d..7116464 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
@@ -24,7 +24,8 @@
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REMOVE_SUBSCRIPTION;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_SEARCH;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_SEND_CUSTOM_ACTION;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
+import static android.support.v4.media.MediaBrowserProtocol
+        .CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_VERSION_CURRENT;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CALLBACK_TOKEN;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION;
@@ -49,6 +50,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.BadParcelableException;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -62,12 +64,12 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.media.session.IMediaSession;
 import android.support.v4.media.session.MediaControllerCompat.TransportControls;
 import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.os.ResultReceiver;
 import android.support.v4.util.ArrayMap;
 import android.text.TextUtils;
@@ -89,6 +91,12 @@
  * </p><p>
  * All callback methods will be called from the thread on which the browser was constructed.
  * </p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about building your media application, read the
+ * <a href="{@docRoot}guide/topics/media-apps/index.html">Media Apps</a> developer guide.</p>
+ * </div>
  */
 public final class MediaBrowserCompat {
     static final String TAG = "MediaBrowserCompat";
@@ -112,6 +120,47 @@
      */
     public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
 
+    /**
+     * Used as a string extra field to denote the target {@link MediaItem}.
+     *
+     * @see #CUSTOM_ACTION_DOWNLOAD
+     * @see #CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE
+     */
+    public static final String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+
+    /**
+     * Used as a float extra field to denote the current progress during download. The value of this
+     * field must be a float number within [0.0, 1.0].
+     *
+     * @see #CUSTOM_ACTION_DOWNLOAD
+     * @see CustomActionCallback#onProgressUpdate
+     */
+    public static final String EXTRA_DOWNLOAD_PROGRESS =
+            "android.media.browse.extra.DOWNLOAD_PROGRESS";
+
+    /**
+     * Predefined custom action to ask the connected service to download a specific
+     * {@link MediaItem} for offline playback. The id of the media item must be passed in an extra
+     * bundle. The download progress might be delivered to the browser via
+     * {@link CustomActionCallback#onProgressUpdate}.
+     *
+     * @see #EXTRA_MEDIA_ID
+     * @see #EXTRA_DOWNLOAD_PROGRESS
+     * @see #CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE
+     */
+    public static final String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+
+    /**
+     * Predefined custom action to ask the connected service to remove the downloaded file of
+     * {@link MediaItem} by the {@link #CUSTOM_ACTION_DOWNLOAD download} action. The id of the
+     * media item must be passed in an extra bundle.
+     *
+     * @see #EXTRA_MEDIA_ID
+     * @see #CUSTOM_ACTION_DOWNLOAD
+     */
+    public static final String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE =
+            "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+
     private final MediaBrowserImpl mImpl;
 
     /**
@@ -130,7 +179,11 @@
      */
     public MediaBrowserCompat(Context context, ComponentName serviceComponent,
             ConnectionCallback callback, Bundle rootHints) {
-        if (Build.VERSION.SDK_INT >= 23) {
+        // To workaround an issue of {@link #unsubscribe(String, SubscriptionCallback)} on API 24
+        // and 25 devices, use the support library version of implementation on those devices.
+        if (Build.VERSION.SDK_INT >= 26) {
+            mImpl = new MediaBrowserImplApi24(context, serviceComponent, callback, rootHints);
+        } else if (Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints);
         } else if (Build.VERSION.SDK_INT >= 21) {
             mImpl = new MediaBrowserImplApi21(context, serviceComponent, callback, rootHints);
@@ -359,6 +412,8 @@
      *            empty string.
      * @param extras The bundle of service-specific arguments to send to the media browser service.
      * @param callback The callback to receive the result of the custom action.
+     * @see #CUSTOM_ACTION_DOWNLOAD
+     * @see #CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE
      */
     public void sendCustomAction(@NonNull String action, Bundle extras,
             @Nullable CustomActionCallback callback) {
@@ -621,7 +676,11 @@
         WeakReference<Subscription> mSubscriptionRef;
 
         public SubscriptionCallback() {
-            if (Build.VERSION.SDK_INT >= 21) {
+            if (Build.VERSION.SDK_INT >= 26) {
+                mSubscriptionCallbackObj =
+                        MediaBrowserCompatApi24.createSubscriptionCallback(new StubApi24());
+                mToken = null;
+            } else if (Build.VERSION.SDK_INT >= 21) {
                 mSubscriptionCallbackObj =
                         MediaBrowserCompatApi21.createSubscriptionCallback(new StubApi21());
                 mToken = new Binder();
@@ -681,7 +740,7 @@
         }
 
         private void setSubscription(Subscription subscription) {
-            mSubscriptionRef = new WeakReference(subscription);
+            mSubscriptionRef = new WeakReference<>(subscription);
         }
 
         private class StubApi21 implements MediaBrowserCompatApi21.SubscriptionCallback {
@@ -738,6 +797,24 @@
             }
 
         }
+
+        private class StubApi24 extends StubApi21
+                implements MediaBrowserCompatApi24.SubscriptionCallback {
+            StubApi24() {
+            }
+
+            @Override
+            public void onChildrenLoaded(@NonNull String parentId, List<?> children,
+                    @NonNull Bundle options) {
+                SubscriptionCallback.this.onChildrenLoaded(
+                        parentId, MediaItem.fromMediaItemList(children), options);
+            }
+
+            @Override
+            public void onError(@NonNull String parentId, @NonNull Bundle options) {
+                SubscriptionCallback.this.onError(parentId, options);
+            }
+        }
     }
 
     /**
@@ -868,9 +945,10 @@
         void subscribe(@NonNull String parentId, Bundle options,
                 @NonNull SubscriptionCallback callback);
         void unsubscribe(@NonNull String parentId, SubscriptionCallback callback);
-        void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb);
+        void getItem(@NonNull String mediaId, @NonNull ItemCallback cb);
         void search(@NonNull String query, Bundle extras, @NonNull SearchCallback callback);
-        void sendCustomAction(String action, Bundle extras, final CustomActionCallback callback);
+        void sendCustomAction(@NonNull String action, Bundle extras,
+                @Nullable CustomActionCallback callback);
     }
 
     interface MediaBrowserServiceCallbackImpl {
@@ -922,63 +1000,60 @@
 
         @Override
         public void connect() {
-            if (mState != CONNECT_STATE_DISCONNECTED) {
-                throw new IllegalStateException("connect() called while not disconnected (state="
-                        + getStateLabel(mState) + ")");
-            }
-            // TODO: remove this extra check.
-            if (DEBUG) {
-                if (mServiceConnection != null) {
-                    throw new RuntimeException("mServiceConnection should be null. Instead it is "
-                            + mServiceConnection);
-                }
-            }
-            if (mServiceBinderWrapper != null) {
-                throw new RuntimeException("mServiceBinderWrapper should be null. Instead it is "
-                        + mServiceBinderWrapper);
-            }
-            if (mCallbacksMessenger != null) {
-                throw new RuntimeException("mCallbacksMessenger should be null. Instead it is "
-                        + mCallbacksMessenger);
+            if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
+                throw new IllegalStateException("connect() called while neigther disconnecting nor "
+                        + "disconnected (state=" + getStateLabel(mState) + ")");
             }
 
             mState = CONNECT_STATE_CONNECTING;
-
-            final Intent intent = new Intent(MediaBrowserServiceCompat.SERVICE_INTERFACE);
-            intent.setComponent(mServiceComponent);
-
-            final ServiceConnection thisConnection = mServiceConnection =
-                    new MediaServiceConnection();
-
-            boolean bound = false;
-            try {
-                bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed binding to service " + mServiceComponent);
-            }
-
-            if (!bound) {
-                // Tell them that it didn't work. We are already on the main thread,
-                // but we don't want to do callbacks inside of connect(). So post it,
-                // and then check that we are on the same ServiceConnection. We know
-                // we won't also get an onServiceConnected or onServiceDisconnected,
-                // so we won't be doing double callbacks.
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        // Ensure that nobody else came in or tried to connect again.
-                        if (thisConnection == mServiceConnection) {
-                            forceCloseConnection();
-                            mCallback.onConnectionFailed();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    // mState could be changed by the Runnable of disconnect()
+                    if (mState == CONNECT_STATE_DISCONNECTING) {
+                        return;
+                    }
+                    mState = CONNECT_STATE_CONNECTING;
+                    // TODO: remove this extra check.
+                    if (DEBUG) {
+                        if (mServiceConnection != null) {
+                            throw new RuntimeException("mServiceConnection should be null. Instead "
+                                    + "it is " + mServiceConnection);
                         }
                     }
-                });
-            }
+                    if (mServiceBinderWrapper != null) {
+                        throw new RuntimeException("mServiceBinderWrapper should be null. Instead "
+                                + "it is " + mServiceBinderWrapper);
+                    }
+                    if (mCallbacksMessenger != null) {
+                        throw new RuntimeException("mCallbacksMessenger should be null. Instead "
+                                + "it is " + mCallbacksMessenger);
+                    }
 
-            if (DEBUG) {
-                Log.d(TAG, "connect...");
-                dump();
-            }
+                    final Intent intent = new Intent(MediaBrowserServiceCompat.SERVICE_INTERFACE);
+                    intent.setComponent(mServiceComponent);
+
+                    mServiceConnection = new MediaServiceConnection();
+                    boolean bound = false;
+                    try {
+                        bound = mContext.bindService(intent, mServiceConnection,
+                                Context.BIND_AUTO_CREATE);
+                    } catch (Exception ex) {
+                        Log.e(TAG, "Failed binding to service " + mServiceComponent);
+                    }
+
+                    if (!bound) {
+                        // Tell them that it didn't work.
+                        forceCloseConnection();
+                        mCallback.onConnectionFailed();
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "connect...");
+                        dump();
+                    }
+                }
+            });
         }
 
         @Override
@@ -990,6 +1065,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
+                    // connect() could be called before this. Then we will disconnect and reconnect.
                     if (mCallbacksMessenger != null) {
                         try {
                             mServiceBinderWrapper.disconnect(mCallbacksMessenger);
@@ -999,7 +1075,13 @@
                             Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
                         }
                     }
+                    int state = mState;
                     forceCloseConnection();
+                    // If the state was not CONNECT_STATE_DISCONNECTING, keep the state so that
+                    // the operation came after disconnect() can be handled properly.
+                    if (state != CONNECT_STATE_DISCONNECTING) {
+                        mState = state;
+                    }
                     if (DEBUG) {
                         Log.d(TAG, "disconnect...");
                         dump();
@@ -1082,7 +1164,7 @@
                 mSubscriptions.put(parentId, sub);
             }
             Bundle copiedOptions = options == null ? null : new Bundle(options);
-            sub.putCallback(copiedOptions, callback);
+            sub.putCallback(mContext, copiedOptions, callback);
 
             // If we are connected, tell the service that we are watching. If we aren't
             // connected, the service will be told when we connect.
@@ -1208,12 +1290,14 @@
             } catch (RemoteException e) {
                 Log.i(TAG, "Remote error sending a custom action: action=" + action + ", extras="
                         + extras, e);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onError(action, extras, null);
-                    }
-                });
+                if (callback != null) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onError(action, extras, null);
+                        }
+                    });
+                }
             }
         }
 
@@ -1306,7 +1390,7 @@
             }
 
             // Tell the app.
-            SubscriptionCallback subscriptionCallback = subscription.getCallback(options);
+            SubscriptionCallback subscriptionCallback = subscription.getCallback(mContext, options);
             if (subscriptionCallback != null) {
                 if (options == null) {
                     if (list == null) {
@@ -1347,9 +1431,11 @@
         /**
          * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
          */
+        @SuppressWarnings("ReferenceEquality")
         private boolean isCurrent(Messenger callback, String funcName) {
-            if (mCallbacksMessenger != callback) {
-                if (mState != CONNECT_STATE_DISCONNECTED) {
+            if (mCallbacksMessenger != callback || mState == CONNECT_STATE_DISCONNECTING
+                    || mState == CONNECT_STATE_DISCONNECTED) {
+                if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
                     Log.i(TAG, funcName + " for " + mServiceComponent + " with mCallbacksMessenger="
                             + mCallbacksMessenger + " this=" + this);
                 }
@@ -1473,8 +1559,10 @@
              * Return true if this is the current ServiceConnection. Also logs if it's not.
              */
             boolean isCurrent(String funcName) {
-                if (mServiceConnection != this) {
-                    if (mState != CONNECT_STATE_DISCONNECTED) {
+                if (mServiceConnection != this || mState == CONNECT_STATE_DISCONNECTING
+                        || mState == CONNECT_STATE_DISCONNECTED) {
+                    if (mState != CONNECT_STATE_DISCONNECTING
+                            && mState != CONNECT_STATE_DISCONNECTED) {
                         // Check mState, because otherwise this log is noisy.
                         Log.i(TAG, funcName + " for " + mServiceComponent +
                                 " with mServiceConnection=" + mServiceConnection + " this=" + this);
@@ -1486,8 +1574,10 @@
         }
     }
 
+    @RequiresApi(21)
     static class MediaBrowserImplApi21 implements MediaBrowserImpl, MediaBrowserServiceCallbackImpl,
             ConnectionCallback.ConnectionCallbackInternal {
+        final Context mContext;
         protected final Object mBrowserObj;
         protected final Bundle mRootHints;
         protected final CallbackHandler mHandler = new CallbackHandler(this);
@@ -1499,6 +1589,7 @@
 
         public MediaBrowserImplApi21(Context context, ComponentName serviceComponent,
                 ConnectionCallback callback, Bundle rootHints) {
+            mContext = context;
             if (rootHints == null) {
                 rootHints = new Bundle();
             }
@@ -1569,7 +1660,7 @@
             }
             callback.setSubscription(sub);
             Bundle copiedOptions = options == null ? null : new Bundle(options);
-            sub.putCallback(copiedOptions, callback);
+            sub.putCallback(mContext, copiedOptions, callback);
 
             if (mServiceBinderWrapper == null) {
                 // TODO: When MediaBrowser is connected to framework's MediaBrowserService,
@@ -1717,8 +1808,8 @@
         }
 
         @Override
-        public void sendCustomAction(final String action, final Bundle extras,
-                final CustomActionCallback callback) {
+        public void sendCustomAction(@NonNull final String action, final Bundle extras,
+                @Nullable final CustomActionCallback callback) {
             if (!isConnected()) {
                 throw new IllegalStateException("Cannot send a custom action (" + action + ") with "
                         + "extras " + extras + " because the browser is not connected to the "
@@ -1726,12 +1817,14 @@
             }
             if (mServiceBinderWrapper == null) {
                 Log.i(TAG, "The connected service doesn't support sendCustomAction.");
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onError(action, extras, null);
-                    }
-                });
+                if (callback != null) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onError(action, extras, null);
+                        }
+                    });
+                }
             }
 
             ResultReceiver receiver = new CustomActionResultReceiver(action, extras, callback,
@@ -1742,12 +1835,14 @@
             } catch (RemoteException e) {
                 Log.i(TAG, "Remote error sending a custom action: action=" + action + ", extras="
                         + extras, e);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onError(action, extras, null);
-                    }
-                });
+                if (callback != null) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onError(action, extras, null);
+                        }
+                    });
+                }
             }
         }
 
@@ -1801,6 +1896,7 @@
         }
 
         @Override
+        @SuppressWarnings("ReferenceEquality")
         public void onLoadChildren(Messenger callback, String parentId, List list, Bundle options) {
             if (mCallbacksMessenger != callback) {
                 return;
@@ -1816,7 +1912,7 @@
             }
 
             // Tell the app.
-            SubscriptionCallback subscriptionCallback = subscription.getCallback(options);
+            SubscriptionCallback subscriptionCallback = subscription.getCallback(mContext, options);
             if (subscriptionCallback != null) {
                 if (options == null) {
                     if (list == null) {
@@ -1835,6 +1931,7 @@
         }
     }
 
+    @RequiresApi(23)
     static class MediaBrowserImplApi23 extends MediaBrowserImplApi21 {
         public MediaBrowserImplApi23(Context context, ComponentName serviceComponent,
                 ConnectionCallback callback, Bundle rootHints) {
@@ -1851,13 +1948,44 @@
         }
     }
 
+    // TODO: Rename to MediaBrowserImplApi26 once O is released
+    @RequiresApi(26)
+    static class MediaBrowserImplApi24 extends MediaBrowserImplApi23 {
+        public MediaBrowserImplApi24(Context context, ComponentName serviceComponent,
+                ConnectionCallback callback, Bundle rootHints) {
+            super(context, serviceComponent, callback, rootHints);
+        }
+
+        @Override
+        public void subscribe(@NonNull String parentId, @NonNull Bundle options,
+                @NonNull SubscriptionCallback callback) {
+            if (options == null) {
+                MediaBrowserCompatApi21.subscribe(
+                        mBrowserObj, parentId, callback.mSubscriptionCallbackObj);
+            } else {
+                MediaBrowserCompatApi24.subscribe(
+                        mBrowserObj, parentId, options, callback.mSubscriptionCallbackObj);
+            }
+        }
+
+        @Override
+        public void unsubscribe(@NonNull String parentId, SubscriptionCallback callback) {
+            if (callback == null) {
+                MediaBrowserCompatApi21.unsubscribe(mBrowserObj, parentId);
+            } else {
+                MediaBrowserCompatApi24.unsubscribe(mBrowserObj, parentId,
+                        callback.mSubscriptionCallbackObj);
+            }
+        }
+    }
+
     private static class Subscription {
         private final List<SubscriptionCallback> mCallbacks;
         private final List<Bundle> mOptionsList;
 
         public Subscription() {
-            mCallbacks = new ArrayList();
-            mOptionsList = new ArrayList();
+            mCallbacks = new ArrayList<>();
+            mOptionsList = new ArrayList<>();
         }
 
         public boolean isEmpty() {
@@ -1872,7 +2000,10 @@
             return mCallbacks;
         }
 
-        public SubscriptionCallback getCallback(Bundle options) {
+        public SubscriptionCallback getCallback(Context context, Bundle options) {
+            if (options != null) {
+                options.setClassLoader(context.getClassLoader());
+            }
             for (int i = 0; i < mOptionsList.size(); ++i) {
                 if (MediaBrowserCompatUtils.areSameOptions(mOptionsList.get(i), options)) {
                     return mCallbacks.get(i);
@@ -1881,7 +2012,10 @@
             return null;
         }
 
-        public void putCallback(Bundle options, SubscriptionCallback callback) {
+        public void putCallback(Context context, Bundle options, SubscriptionCallback callback) {
+            if (options != null) {
+                options.setClassLoader(context.getClassLoader());
+            }
             for (int i = 0; i < mOptionsList.size(); ++i) {
                 if (MediaBrowserCompatUtils.areSameOptions(mOptionsList.get(i), options)) {
                     mCallbacks.set(i, callback);
@@ -1910,26 +2044,38 @@
             }
             Bundle data = msg.getData();
             data.setClassLoader(MediaSessionCompat.class.getClassLoader());
-            switch (msg.what) {
-                case SERVICE_MSG_ON_CONNECT:
-                    mCallbackImplRef.get().onServiceConnected(mCallbacksMessengerRef.get(),
-                            data.getString(DATA_MEDIA_ITEM_ID),
-                            (MediaSessionCompat.Token) data.getParcelable(DATA_MEDIA_SESSION_TOKEN),
-                            data.getBundle(DATA_ROOT_HINTS));
-                    break;
-                case SERVICE_MSG_ON_CONNECT_FAILED:
-                    mCallbackImplRef.get().onConnectionFailed(mCallbacksMessengerRef.get());
-                    break;
-                case SERVICE_MSG_ON_LOAD_CHILDREN:
-                    mCallbackImplRef.get().onLoadChildren(mCallbacksMessengerRef.get(),
-                            data.getString(DATA_MEDIA_ITEM_ID),
-                            data.getParcelableArrayList(DATA_MEDIA_ITEM_LIST),
-                            data.getBundle(DATA_OPTIONS));
-                    break;
-                default:
-                    Log.w(TAG, "Unhandled message: " + msg
-                            + "\n  Client version: " + CLIENT_VERSION_CURRENT
-                            + "\n  Service version: " + msg.arg1);
+            MediaBrowserServiceCallbackImpl serviceCallback = mCallbackImplRef.get();
+            Messenger callbacksMessenger = mCallbacksMessengerRef.get();
+            try {
+                switch (msg.what) {
+                    case SERVICE_MSG_ON_CONNECT:
+                        serviceCallback.onServiceConnected(callbacksMessenger,
+                                data.getString(DATA_MEDIA_ITEM_ID),
+                                (MediaSessionCompat.Token) data.getParcelable(
+                                        DATA_MEDIA_SESSION_TOKEN),
+                                data.getBundle(DATA_ROOT_HINTS));
+                        break;
+                    case SERVICE_MSG_ON_CONNECT_FAILED:
+                        serviceCallback.onConnectionFailed(callbacksMessenger);
+                        break;
+                    case SERVICE_MSG_ON_LOAD_CHILDREN:
+                        serviceCallback.onLoadChildren(callbacksMessenger,
+                                data.getString(DATA_MEDIA_ITEM_ID),
+                                data.getParcelableArrayList(DATA_MEDIA_ITEM_LIST),
+                                data.getBundle(DATA_OPTIONS));
+                        break;
+                    default:
+                        Log.w(TAG, "Unhandled message: " + msg
+                                + "\n  Client version: " + CLIENT_VERSION_CURRENT
+                                + "\n  Service version: " + msg.arg1);
+                }
+            } catch (BadParcelableException e) {
+                // Do not print the exception here, since it is already done by the Parcel class.
+                Log.e(TAG, "Could not unparcel the data.");
+                // If an error happened while connecting, disconnect from the service.
+                if (msg.what == SERVICE_MSG_ON_CONNECT) {
+                    serviceCallback.onConnectionFailed(callbacksMessenger);
+                }
             }
         }
 
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
index 7ff6a20..7c4c761 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -64,6 +64,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.media.session.IMediaSession;
@@ -104,11 +105,19 @@
  *     &lt;/intent-filter>
  * &lt;/service>
  * </pre>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about building your media application, read the
+ * <a href="{@docRoot}guide/topics/media-apps/index.html">Media Apps</a> developer guide.</p>
+ * </div>
  */
 public abstract class MediaBrowserServiceCompat extends Service {
     static final String TAG = "MBServiceCompat";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final float EPSILON = 0.00001f;
+
     private MediaBrowserServiceImpl mImpl;
 
     /**
@@ -228,8 +237,10 @@
         }
     }
 
+    @RequiresApi(21)
     class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl,
             MediaBrowserServiceCompatApi21.ServiceCompatProxy {
+        final List<Bundle> mRootExtrasList = new ArrayList<>();
         Object mServiceObj;
         Messenger mMessenger;
 
@@ -246,8 +257,23 @@
         }
 
         @Override
-        public void setSessionToken(MediaSessionCompat.Token token) {
-            MediaBrowserServiceCompatApi21.setSessionToken(mServiceObj, token.getToken());
+        public void setSessionToken(final MediaSessionCompat.Token token) {
+            mHandler.postOrRun(new Runnable() {
+                @Override
+                public void run() {
+                    if (!mRootExtrasList.isEmpty()) {
+                        IMediaSession extraBinder = token.getExtraBinder();
+                        if (extraBinder != null) {
+                            for (Bundle rootExtras : mRootExtrasList) {
+                                BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
+                                        extraBinder.asBinder());
+                            }
+                        }
+                        mRootExtrasList.clear();
+                    }
+                    MediaBrowserServiceCompatApi21.setSessionToken(mServiceObj, token.getToken());
+                }
+            });
         }
 
         @Override
@@ -303,6 +329,8 @@
                     IMediaSession extraBinder = mSession.getExtraBinder();
                     BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
                             extraBinder == null ? null : extraBinder.asBinder());
+                } else {
+                    mRootExtrasList.add(rootExtras);
                 }
             }
             BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
@@ -347,6 +375,7 @@
         }
     }
 
+    @RequiresApi(23)
     class MediaBrowserServiceImplApi23 extends MediaBrowserServiceImplApi21 implements
             MediaBrowserServiceCompatApi23.ServiceCompatProxy {
         @Override
@@ -381,6 +410,65 @@
         }
     }
 
+    // TODO: Rename to MediaBrowserServiceImplApi26 once O is released
+    @RequiresApi(26)
+    class MediaBrowserServiceImplApi24 extends MediaBrowserServiceImplApi23 implements
+            MediaBrowserServiceCompatApi24.ServiceCompatProxy {
+        @Override
+        public void onCreate() {
+            mServiceObj = MediaBrowserServiceCompatApi24.createService(
+                    MediaBrowserServiceCompat.this, this);
+            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
+        }
+
+        @Override
+        public void notifyChildrenChanged(final String parentId, final Bundle options) {
+            if (options == null) {
+                MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
+            } else {
+                MediaBrowserServiceCompatApi24.notifyChildrenChanged(mServiceObj, parentId,
+                        options);
+            }
+        }
+
+        @Override
+        public void onLoadChildren(String parentId,
+                final MediaBrowserServiceCompatApi24.ResultWrapper resultWrapper, Bundle options) {
+            final Result<List<MediaBrowserCompat.MediaItem>> result
+                    = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
+                @Override
+                void onResultSent(List<MediaBrowserCompat.MediaItem> list) {
+                    List<Parcel> parcelList = null;
+                    if (list != null) {
+                        parcelList = new ArrayList<>();
+                        for (MediaBrowserCompat.MediaItem item : list) {
+                            Parcel parcel = Parcel.obtain();
+                            item.writeToParcel(parcel, 0);
+                            parcelList.add(parcel);
+                        }
+                    }
+                    resultWrapper.sendResult(parcelList, getFlags());
+                }
+
+                @Override
+                public void detach() {
+                    resultWrapper.detach();
+                }
+            };
+            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result, options);
+        }
+
+        @Override
+        public Bundle getBrowserRootHints() {
+            // If EXTRA_MESSENGER_BINDER is used, mCurConnection is not null.
+            if (mCurConnection != null) {
+                return mCurConnection.rootHints == null ? null
+                        : new Bundle(mCurConnection.rootHints);
+            }
+            return MediaBrowserServiceCompatApi24.getBrowserRootHints(mServiceObj);
+        }
+    }
+
     private final class ServiceHandler extends Handler {
         private final ServiceBinderImpl mServiceBinderImpl = new ServiceBinderImpl();
 
@@ -463,12 +551,12 @@
     /**
      * All the info about a connection.
      */
-    private class ConnectionRecord {
+    private static class ConnectionRecord {
         String pkg;
         Bundle rootHints;
         ServiceCallbacks callbacks;
         BrowserRoot root;
-        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap();
+        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
 
         ConnectionRecord() {
         }
@@ -529,6 +617,7 @@
                 throw new IllegalStateException("sendProgressUpdate() called when either "
                         + "sendResult() or sendError() had already been called for: " + mDebug);
             }
+            checkExtraFields(extras);
             mSendProgressUpdateCalled = true;
             onProgressUpdateSent(extras);
         }
@@ -603,6 +692,19 @@
             throw new UnsupportedOperationException("It is not supported to send an error for "
                     + mDebug);
         }
+
+        private void checkExtraFields(Bundle extras) {
+            if (extras == null) {
+                return;
+            }
+            if (extras.containsKey(MediaBrowserCompat.EXTRA_DOWNLOAD_PROGRESS)) {
+                float value = extras.getFloat(MediaBrowserCompat.EXTRA_DOWNLOAD_PROGRESS);
+                if (value < -EPSILON || value > 1.0f + EPSILON) {
+                    throw new IllegalArgumentException("The value of the EXTRA_DOWNLOAD_PROGRESS "
+                            + "field must be a float number within [0.0, 1.0].");
+                }
+            }
+        }
     }
 
     private class ServiceBinderImpl {
@@ -819,7 +921,7 @@
                 throws RemoteException;
     }
 
-    private class ServiceCallbacksCompat implements ServiceCallbacks {
+    private static class ServiceCallbacksCompat implements ServiceCallbacks {
         final Messenger mCallbacks;
 
         ServiceCallbacksCompat(Messenger callbacks) {
@@ -875,7 +977,9 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        if (Build.VERSION.SDK_INT >= 23) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            mImpl = new MediaBrowserServiceImplApi24();
+        } else if (Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaBrowserServiceImplApi23();
         } else if (Build.VERSION.SDK_INT >= 21) {
             mImpl = new MediaBrowserServiceImplApi21();
@@ -1040,6 +1144,8 @@
      * @param action The custom action sent from the media browser.
      * @param extras The bundle of service-specific arguments sent from the media browser.
      * @param result The {@link Result} to send the result of the requested custom action.
+     * @see MediaBrowserCompat#CUSTOM_ACTION_DOWNLOAD
+     * @see MediaBrowserCompat#CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE
      */
     public void onCustomAction(@NonNull String action, Bundle extras,
             @NonNull Result<Bundle> result) {
diff --git a/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java b/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java
index 1a617e9..97068f1 100644
--- a/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -96,6 +96,40 @@
     public static final long BT_FOLDER_TYPE_YEARS = 6;
 
     /**
+     * Used as a long extra field to indicate the download status of the media item. The value
+     * should be one of the following:
+     * <ul>
+     * <li>{@link #STATUS_NOT_DOWNLOADED}</li>
+     * <li>{@link #STATUS_DOWNLOADING}</li>
+     * <li>{@link #STATUS_DOWNLOADED}</li>
+     * </ul>
+     *
+     * @see #getExtras()
+     */
+    public static final String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+
+    /**
+     * The status value to indicate the media item is not downloaded.
+     *
+     * @see #EXTRA_DOWNLOAD_STATUS
+     */
+    public static final long STATUS_NOT_DOWNLOADED = 0;
+
+    /**
+     * The status value to indicate the media item is being downloaded.
+     *
+     * @see #EXTRA_DOWNLOAD_STATUS
+     */
+    public static final long STATUS_DOWNLOADING = 1;
+
+    /**
+     * The status value to indicate the media item is downloaded for later offline playback.
+     *
+     * @see #EXTRA_DOWNLOAD_STATUS
+     */
+    public static final long STATUS_DOWNLOADED = 2;
+
+    /**
      * Custom key to store a media URI on API 21-22 devices (before it became part of the
      * framework class) when parceling/converting to and from framework objects.
      *
@@ -334,44 +368,44 @@
      *         none.
      */
     public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) {
-        if (descriptionObj == null || Build.VERSION.SDK_INT < 21) {
+        if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
+            Builder bob = new Builder();
+            bob.setMediaId(MediaDescriptionCompatApi21.getMediaId(descriptionObj));
+            bob.setTitle(MediaDescriptionCompatApi21.getTitle(descriptionObj));
+            bob.setSubtitle(MediaDescriptionCompatApi21.getSubtitle(descriptionObj));
+            bob.setDescription(MediaDescriptionCompatApi21.getDescription(descriptionObj));
+            bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj));
+            bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj));
+            Bundle extras = MediaDescriptionCompatApi21.getExtras(descriptionObj);
+            Uri mediaUri = extras == null ? null :
+                    (Uri) extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
+            if (mediaUri != null) {
+                if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
+                    // The extras were only created for the media URI, so we set it back to null to
+                    // ensure mediaDescriptionCompat.getExtras() equals
+                    // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
+                    extras = null;
+                } else {
+                    // Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
+                    // equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
+                    // .getExtras().keySet()
+                    extras.remove(DESCRIPTION_KEY_MEDIA_URI);
+                    extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
+                }
+            }
+            bob.setExtras(extras);
+            if (mediaUri != null) {
+                bob.setMediaUri(mediaUri);
+            } else if (Build.VERSION.SDK_INT >= 23) {
+                bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj));
+            }
+            MediaDescriptionCompat descriptionCompat = bob.build();
+            descriptionCompat.mDescriptionObj = descriptionObj;
+
+            return descriptionCompat;
+        } else {
             return null;
         }
-
-        Builder bob = new Builder();
-        bob.setMediaId(MediaDescriptionCompatApi21.getMediaId(descriptionObj));
-        bob.setTitle(MediaDescriptionCompatApi21.getTitle(descriptionObj));
-        bob.setSubtitle(MediaDescriptionCompatApi21.getSubtitle(descriptionObj));
-        bob.setDescription(MediaDescriptionCompatApi21.getDescription(descriptionObj));
-        bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj));
-        bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj));
-        Bundle extras = MediaDescriptionCompatApi21.getExtras(descriptionObj);
-        Uri mediaUri = extras == null ? null :
-                (Uri) extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
-        if (mediaUri != null) {
-            if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
-                // The extras were only created for the media URI, so we set it back to null to
-                // ensure mediaDescriptionCompat.getExtras() equals
-                // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
-                extras = null;
-            } else {
-                // Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
-                // equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
-                // .getExtras().keySet()
-                extras.remove(DESCRIPTION_KEY_MEDIA_URI);
-                extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
-            }
-        }
-        bob.setExtras(extras);
-        if (mediaUri != null) {
-            bob.setMediaUri(mediaUri);
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj));
-        }
-        MediaDescriptionCompat descriptionCompat = bob.build();
-        descriptionCompat.mDescriptionObj = descriptionObj;
-
-        return descriptionCompat;
     }
 
     public static final Parcelable.Creator<MediaDescriptionCompat> CREATOR =
diff --git a/media-compat/java/android/support/v4/media/MediaMetadataCompat.java b/media-compat/java/android/support/v4/media/MediaMetadataCompat.java
index f8c1f57..3ddf255 100644
--- a/media-compat/java/android/support/v4/media/MediaMetadataCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaMetadataCompat.java
@@ -245,6 +245,19 @@
     public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
 
     /**
+     * The download status of the media which will be used for later offline playback. It should be
+     * one of the following:
+     *
+     * <ul>
+     * <li>{@link MediaDescriptionCompat#STATUS_NOT_DOWNLOADED}</li>
+     * <li>{@link MediaDescriptionCompat#STATUS_DOWNLOADING}</li>
+     * <li>{@link MediaDescriptionCompat#STATUS_DOWNLOADED}</li>
+     * </ul>
+     */
+    public static final String METADATA_KEY_DOWNLOAD_STATUS =
+            "android.media.metadata.DOWNLOAD_STATUS";
+
+    /**
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
@@ -263,7 +276,7 @@
     @RestrictTo(LIBRARY_GROUP)
     @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
             METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE,
-            METADATA_KEY_ADVERTISEMENT})
+            METADATA_KEY_ADVERTISEMENT, METADATA_KEY_DOWNLOAD_STATUS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface LongKey {}
 
@@ -321,6 +334,7 @@
         METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
         METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
     }
 
     private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
@@ -517,10 +531,17 @@
         bob.setIconBitmap(icon);
         bob.setIconUri(iconUri);
         bob.setMediaUri(mediaUri);
+
+        Bundle bundle = new Bundle();
         if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) {
-            Bundle bundle = new Bundle();
             bundle.putLong(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE,
                     getLong(METADATA_KEY_BT_FOLDER_TYPE));
+        }
+        if (mBundle.containsKey(METADATA_KEY_DOWNLOAD_STATUS)) {
+            bundle.putLong(MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
+                    getLong(METADATA_KEY_DOWNLOAD_STATUS));
+        }
+        if (!bundle.isEmpty()) {
             bob.setExtras(bundle);
         }
         mDescription = bob.build();
@@ -580,17 +601,17 @@
      *         none.
      */
     public static MediaMetadataCompat fromMediaMetadata(Object metadataObj) {
-        if (metadataObj == null || Build.VERSION.SDK_INT < 21) {
+        if (metadataObj != null && Build.VERSION.SDK_INT >= 21) {
+            Parcel p = Parcel.obtain();
+            MediaMetadataCompatApi21.writeToParcel(metadataObj, p, 0);
+            p.setDataPosition(0);
+            MediaMetadataCompat metadata = MediaMetadataCompat.CREATOR.createFromParcel(p);
+            p.recycle();
+            metadata.mMetadataObj = metadataObj;
+            return metadata;
+        } else {
             return null;
         }
-
-        Parcel p = Parcel.obtain();
-        MediaMetadataCompatApi21.writeToParcel(metadataObj, p, 0);
-        p.setDataPosition(0);
-        MediaMetadataCompat metadata = MediaMetadataCompat.CREATOR.createFromParcel(p);
-        p.recycle();
-        metadata.mMetadataObj = metadataObj;
-        return metadata;
     }
 
     /**
@@ -604,15 +625,13 @@
      *         if none.
      */
     public Object getMediaMetadata() {
-        if (mMetadataObj != null || Build.VERSION.SDK_INT < 21) {
-            return mMetadataObj;
+        if (mMetadataObj == null && Build.VERSION.SDK_INT >= 21) {
+            Parcel p = Parcel.obtain();
+            writeToParcel(p, 0);
+            p.setDataPosition(0);
+            mMetadataObj = MediaMetadataCompatApi21.createFromParcel(p);
+            p.recycle();
         }
-
-        Parcel p = Parcel.obtain();
-        writeToParcel(p, 0);
-        p.setDataPosition(0);
-        mMetadataObj = MediaMetadataCompatApi21.createFromParcel(p);
-        p.recycle();
         return mMetadataObj;
     }
 
@@ -659,11 +678,6 @@
          * Create a Builder using a {@link MediaMetadataCompat} instance to set
          * initial values, but replace bitmaps with a scaled down copy if they
          * are larger than maxBitmapSize.
-         * <p>
-         * This also deep-copies the bitmaps for {@link #METADATA_KEY_ART} and
-         * {@link #METADATA_KEY_ALBUM_ART} on
-         * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWITCH} and later
-         * to prevent bitmaps from being recycled by RCC.
          *
          * @param source The original metadata to copy.
          * @param maxBitmapSize The maximum height/width for bitmaps contained
@@ -675,13 +689,10 @@
             this(source);
             for (String key : mBundle.keySet()) {
                 Object value = mBundle.get(key);
-                if (value != null && value instanceof Bitmap) {
+                if (value instanceof Bitmap) {
                     Bitmap bmp = (Bitmap) value;
                     if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
                         putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
-                    } else if (Build.VERSION.SDK_INT >= 14 &&
-                            (key.equals(METADATA_KEY_ART) || key.equals(METADATA_KEY_ALBUM_ART))) {
-                        putBitmap(key, bmp.copy(bmp.getConfig(), false));
                     }
                 }
             }
@@ -771,7 +782,9 @@
          * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
          * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
          * <li>{@link #METADATA_KEY_YEAR}</li>
+         * <li>{@link #METADATA_KEY_BT_FOLDER_TYPE}</li>
          * <li>{@link #METADATA_KEY_ADVERTISEMENT}</li>
+         * <li>{@link #METADATA_KEY_DOWNLOAD_STATUS}</li>
          * </ul>
          *
          * @param key The key for referencing this value
diff --git a/media-compat/java/android/support/v4/media/RatingCompat.java b/media-compat/java/android/support/v4/media/RatingCompat.java
index f61a686..b538cac 100644
--- a/media-compat/java/android/support/v4/media/RatingCompat.java
+++ b/media-compat/java/android/support/v4/media/RatingCompat.java
@@ -296,6 +296,7 @@
                 if (isRated()) {
                     return mRatingValue;
                 }
+                // fall through
             default:
                 return -1.0f;
         }
@@ -324,37 +325,38 @@
      * @return An equivalent {@link RatingCompat} object, or null if none.
      */
     public static RatingCompat fromRating(Object ratingObj) {
-        if (ratingObj == null || Build.VERSION.SDK_INT < 19) {
+        if (ratingObj != null && Build.VERSION.SDK_INT >= 19) {
+            final int ratingStyle = RatingCompatKitkat.getRatingStyle(ratingObj);
+            final RatingCompat rating;
+            if (RatingCompatKitkat.isRated(ratingObj)) {
+                switch (ratingStyle) {
+                    case RATING_HEART:
+                        rating = newHeartRating(RatingCompatKitkat.hasHeart(ratingObj));
+                        break;
+                    case RATING_THUMB_UP_DOWN:
+                        rating = newThumbRating(RatingCompatKitkat.isThumbUp(ratingObj));
+                        break;
+                    case RATING_3_STARS:
+                    case RATING_4_STARS:
+                    case RATING_5_STARS:
+                        rating = newStarRating(ratingStyle,
+                                RatingCompatKitkat.getStarRating(ratingObj));
+                        break;
+                    case RATING_PERCENTAGE:
+                        rating = newPercentageRating(
+                                RatingCompatKitkat.getPercentRating(ratingObj));
+                        break;
+                    default:
+                        return null;
+                }
+            } else {
+                rating = newUnratedRating(ratingStyle);
+            }
+            rating.mRatingObj = ratingObj;
+            return rating;
+        } else {
             return null;
         }
-
-        final int ratingStyle = RatingCompatKitkat.getRatingStyle(ratingObj);
-        final RatingCompat rating;
-        if (RatingCompatKitkat.isRated(ratingObj)) {
-            switch (ratingStyle) {
-                case RATING_HEART:
-                    rating = newHeartRating(RatingCompatKitkat.hasHeart(ratingObj));
-                    break;
-                case RATING_THUMB_UP_DOWN:
-                    rating = newThumbRating(RatingCompatKitkat.isThumbUp(ratingObj));
-                    break;
-                case RATING_3_STARS:
-                case RATING_4_STARS:
-                case RATING_5_STARS:
-                    rating = newStarRating(ratingStyle,
-                            RatingCompatKitkat.getStarRating(ratingObj));
-                    break;
-                case RATING_PERCENTAGE:
-                    rating = newPercentageRating(RatingCompatKitkat.getPercentRating(ratingObj));
-                    break;
-                default:
-                    return null;
-            }
-        } else {
-            rating = newUnratedRating(ratingStyle);
-        }
-        rating.mRatingObj = ratingObj;
-        return rating;
     }
 
     /**
@@ -366,30 +368,30 @@
      * @return An equivalent {@link android.media.Rating} object, or null if none.
      */
     public Object getRating() {
-        if (mRatingObj != null || Build.VERSION.SDK_INT < 19) {
-            return mRatingObj;
-        }
-
-        if (isRated()) {
-            switch (mRatingStyle) {
-                case RATING_HEART:
-                    mRatingObj = RatingCompatKitkat.newHeartRating(hasHeart());
-                    break;
-                case RATING_THUMB_UP_DOWN:
-                    mRatingObj = RatingCompatKitkat.newThumbRating(isThumbUp());
-                    break;
-                case RATING_3_STARS:
-                case RATING_4_STARS:
-                case RATING_5_STARS:
-                    mRatingObj = RatingCompatKitkat.newStarRating(mRatingStyle, getStarRating());
-                    break;
-                case RATING_PERCENTAGE:
-                    mRatingObj = RatingCompatKitkat.newPercentageRating(getPercentRating());
-                default:
-                    return null;
+        if (mRatingObj == null && Build.VERSION.SDK_INT >= 19) {
+            if (isRated()) {
+                switch (mRatingStyle) {
+                    case RATING_HEART:
+                        mRatingObj = RatingCompatKitkat.newHeartRating(hasHeart());
+                        break;
+                    case RATING_THUMB_UP_DOWN:
+                        mRatingObj = RatingCompatKitkat.newThumbRating(isThumbUp());
+                        break;
+                    case RATING_3_STARS:
+                    case RATING_4_STARS:
+                    case RATING_5_STARS:
+                        mRatingObj = RatingCompatKitkat.newStarRating(mRatingStyle,
+                                getStarRating());
+                        break;
+                    case RATING_PERCENTAGE:
+                        mRatingObj = RatingCompatKitkat.newPercentageRating(getPercentRating());
+                        break;
+                    default:
+                        return null;
+                }
+            } else {
+                mRatingObj = RatingCompatKitkat.newUnratedRating(mRatingStyle);
             }
-        } else {
-            mRatingObj = RatingCompatKitkat.newUnratedRating(mRatingStyle);
         }
         return mRatingObj;
     }
diff --git a/media-compat/java/android/support/v4/media/TransportController.java b/media-compat/java/android/support/v4/media/TransportController.java
deleted file mode 100644
index d1d2649..0000000
--- a/media-compat/java/android/support/v4/media/TransportController.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2013 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.media;
-
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-
-/**
- * Base interface to controlling a media transport.  This is the
- * interface for implementing things like on-screen controls: it
- * allows them to request changes in playback, retrieve the current
- * playback state, and monitor for changes to the playback state.
- *
- * @deprecated Use {@link MediaControllerCompat}.
- */
-@Deprecated
-public abstract class TransportController {
-    /**
-     * @deprecated Use {@link MediaControllerCompat}.
-     */
-    @Deprecated
-    public TransportController() {
-    }
-
-    /**
-     * Start listening to changes in playback state.
-     *
-     * @deprecated Use
-     *         {@link MediaControllerCompat#registerCallback(MediaControllerCompat.Callback)}.
-     */
-    @Deprecated
-    public abstract void registerStateListener(TransportStateListener listener);
-
-    /**
-     * Stop listening to changes in playback state.
-     *
-     * @deprecated Use
-     *         {@link MediaControllerCompat#unregisterCallback(MediaControllerCompat.Callback)}.
-     */
-    @Deprecated
-    public abstract void unregisterStateListener(TransportStateListener listener);
-
-    /**
-     * Request that the player start its playback at its current position.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#play}.
-     */
-    @Deprecated
-    public abstract void startPlaying();
-
-    /**
-     * Request that the player pause its playback and stay at its current position.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#pause}.
-     */
-    @Deprecated
-    public abstract void pausePlaying();
-
-    /**
-     * Request that the player stop its playback; it may clear its state in whatever
-     * way is appropriate.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#stop}.
-     */
-    @Deprecated
-    public abstract void stopPlaying();
-
-    /**
-     * Retrieve the total duration of the media stream, in milliseconds.
-     *
-     * @deprecated Use {@link MediaMetadataCompat#METADATA_KEY_DURATION}.
-     */
-    @Deprecated
-    public abstract long getDuration();
-
-    /**
-     * Retrieve the current playback location in the media stream, in milliseconds.
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getPosition} and
-     *         {@link PlaybackStateCompat#getLastPositionUpdateTime}.
-     */
-    @Deprecated
-    public abstract long getCurrentPosition();
-
-    /**
-     * Move to a new location in the media stream.
-     * @param pos Position to move to, in milliseconds.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#seekTo}.
-     */
-    @Deprecated
-    public abstract void seekTo(long pos);
-
-    /**
-     * Return whether the player is currently playing its stream.
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getState}.
-     */
-    @Deprecated
-    public abstract boolean isPlaying();
-
-    /**
-     * Retrieve amount, in percentage (0-100), that the media stream has been buffered
-     * on to the local device.  Return 100 if the stream is always local.
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getBufferedPosition} and
-     *         {@link MediaMetadataCompat#METADATA_KEY_DURATION}.
-     */
-    @Deprecated
-    public abstract int getBufferPercentage();
-
-    /**
-     * Retrieve the flags for the media transport control buttons that this transport supports.
-     * Result is a combination of the following flags:
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PREVIOUS},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_REWIND},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PLAY},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PLAY_PAUSE},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PAUSE},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_STOP},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_FAST_FORWARD},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_NEXT}
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getActions}.
-     */
-    @Deprecated
-    public abstract int getTransportControlFlags();
-}
diff --git a/media-compat/java/android/support/v4/media/TransportMediator.java b/media-compat/java/android/support/v4/media/TransportMediator.java
deleted file mode 100644
index 40c245c..0000000
--- a/media-compat/java/android/support/v4/media/TransportMediator.java
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * Copyright (C) 2013 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.media;
-
-import android.app.Activity;
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.RemoteControlClient;
-import android.os.Build;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import android.view.KeyEvent;
-import android.view.View;
-
-import java.util.ArrayList;
-
-/**
- * Helper for implementing a media transport control (with play, pause, skip, and
- * other media actions).  Takes care of both key events and advanced features
- * like {@link android.media.RemoteControlClient}.  This class is intended to
- * serve as an intermediary between transport controls (whether they be on-screen
- * controls, hardware buttons, remote controls) and the actual player.  The player
- * is represented by a single {@link TransportPerformer} that must be supplied to
- * this class.  On-screen controls that want to control and show the state of the
- * player should do this through calls to the {@link TransportController} interface.
- *
- * <p>Here is a simple but fairly complete sample of a video player that is built
- * around this class.  Note that the MediaController class used here is not the one
- * included in the standard Android framework, but a custom implementation.  Real
- * applications often implement their own transport controls, or you can copy the
- * implementation here out of Support4Demos.</p>
- *
- * {@sample frameworks/support/samples/Support4Demos/src/com/example/android/supportv4/media/TransportControllerActivity.java
- *      complete}
- *
- * @deprecated Use {@link MediaControllerCompat}.
- */
-@Deprecated
-public class TransportMediator extends TransportController {
-    final Context mContext;
-    final TransportPerformer mCallbacks;
-    final AudioManager mAudioManager;
-    final View mView;
-    final Object mDispatcherState;
-    final TransportMediatorJellybeanMR2 mController;
-    final ArrayList<TransportStateListener> mListeners
-            = new ArrayList<TransportStateListener>();
-    final TransportMediatorCallback mTransportKeyCallback
-            = new TransportMediatorCallback() {
-        @Override
-        public void handleKey(KeyEvent key) {
-            key.dispatch(mKeyEventCallback);
-        }
-        @Override
-        public void handleAudioFocusChange(int focusChange) {
-            mCallbacks.onAudioFocusChange(focusChange);
-        }
-
-        @Override
-        public long getPlaybackPosition() {
-            return mCallbacks.onGetCurrentPosition();
-        }
-
-        @Override
-        public void playbackPositionUpdate(long newPositionMs) {
-            mCallbacks.onSeekTo(newPositionMs);
-        }
-    };
-
-    /**
-     * Synonym for {@link KeyEvent#KEYCODE_MEDIA_PLAY KeyEvent.KEYCODE_MEDIA_PLAY}
-     *
-     * @deprecated Use {@link KeyEvent#KEYCODE_MEDIA_PLAY}.
-     */
-    @Deprecated
-    public static final int KEYCODE_MEDIA_PLAY = 126;
-    /**
-     * Synonym for {@link KeyEvent#KEYCODE_MEDIA_PAUSE KeyEvent.KEYCODE_MEDIA_PAUSE}
-     *
-     * @deprecated Use {@link KeyEvent#KEYCODE_MEDIA_PAUSE}.
-     */
-    @Deprecated
-    public static final int KEYCODE_MEDIA_PAUSE = 127;
-    /**
-     * Synonym for {@link KeyEvent#KEYCODE_MEDIA_RECORD KeyEvent.KEYCODE_MEDIA_RECORD}
-     *
-     * @deprecated Use {@link KeyEvent#KEYCODE_MEDIA_RECORD}.
-     */
-    @Deprecated
-    public static final int KEYCODE_MEDIA_RECORD = 130;
-
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PREVIOUS
-     * RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_PREVIOUS}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_REWIND
-     * RemoteControlClient.FLAG_KEY_MEDIA_REWIND}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_REWIND}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY
-     * RemoteControlClient.FLAG_KEY_MEDIA_PLAY}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_PLAY}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY_PAUSE
-     * RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_PLAY_PAUSE}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PAUSE
-     * RemoteControlClient.FLAG_KEY_MEDIA_PAUSE}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_PAUSE}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_STOP
-     * RemoteControlClient.FLAG_KEY_MEDIA_STOP}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_STOP}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_FAST_FORWARD
-     * RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_FAST_FORWARD}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
-    /**
-     * Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_NEXT
-     * RemoteControlClient.FLAG_KEY_MEDIA_NEXT}
-     *
-     * @deprecated Use {@link RemoteControlClient#FLAG_KEY_MEDIA_NEXT}.
-     */
-    @Deprecated
-    public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
-
-    static boolean isMediaKey(int keyCode) {
-        switch (keyCode) {
-            case KEYCODE_MEDIA_PLAY:
-            case KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    final KeyEvent.Callback mKeyEventCallback = new KeyEvent.Callback() {
-        @Override
-        public boolean onKeyDown(int keyCode, KeyEvent event) {
-            return isMediaKey(keyCode) ? mCallbacks.onMediaButtonDown(keyCode, event) : false;
-        }
-
-        @Override
-        public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-            return false;
-        }
-
-        @Override
-        public boolean onKeyUp(int keyCode, KeyEvent event) {
-            return isMediaKey(keyCode) ? mCallbacks.onMediaButtonUp(keyCode, event) : false;
-        }
-
-        @Override
-        public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
-            return false;
-        }
-    };
-
-    /**
-     * @deprecated Use {@link MediaControllerCompat}.
-     */
-    @Deprecated
-    public TransportMediator(Activity activity, TransportPerformer callbacks) {
-        this(activity, null, callbacks);
-    }
-
-    /**
-     * @deprecated Use {@link MediaControllerCompat}.
-     */
-    @Deprecated
-    public TransportMediator(View view, TransportPerformer callbacks) {
-        this(null, view, callbacks);
-    }
-
-    private TransportMediator(Activity activity, View view, TransportPerformer callbacks) {
-        mContext = activity != null ? activity : view.getContext();
-        mCallbacks = callbacks;
-        mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
-        mView = activity != null ? activity.getWindow().getDecorView() : view;
-        mDispatcherState = mView.getKeyDispatcherState();
-        if (Build.VERSION.SDK_INT >= 18) { // JellyBean MR2
-            mController = new TransportMediatorJellybeanMR2(mContext, mAudioManager,
-                    mView, mTransportKeyCallback);
-        } else {
-            mController = null;
-        }
-    }
-
-    /**
-     * Return the {@link android.media.RemoteControlClient} associated with this transport.
-     * This returns a generic Object since the RemoteControlClient is not availble before
-     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}.  Further, this class
-     * will not use RemoteControlClient in its implementation until
-     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}.  You should always check for
-     * null here and not do anything with the RemoteControlClient if none is given; this
-     * way you don't need to worry about the current platform API version.
-     *
-     * <p>Note that this class takes possession of the
-     * {@link android.media.RemoteControlClient.OnGetPlaybackPositionListener} and
-     * {@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener} callbacks;
-     * you will interact with these through
-     * {@link TransportPerformer#onGetCurrentPosition() TransportPerformer.onGetCurrentPosition} and
-     * {@link TransportPerformer#onSeekTo TransportPerformer.onSeekTo}, respectively.</p>
-     *
-     * @deprecated Use {@link MediaControllerCompat}.
-     */
-    @Deprecated
-    public Object getRemoteControlClient() {
-        return mController != null ? mController.getRemoteControlClient() : null;
-    }
-
-    /**
-     * Must call from {@link Activity#dispatchKeyEvent Activity.dispatchKeyEvent} to give
-     * the transport an opportunity to intercept media keys.  Any such keys will show up
-     * in {@link TransportPerformer}.
-     * @param event
-     *
-     * @deprecated Use {@link MediaControllerCompat#dispatchMediaButtonEvent}.
-     */
-    @Deprecated
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return event.dispatch(mKeyEventCallback, (KeyEvent.DispatcherState) mDispatcherState, this);
-    }
-
-    /**
-     * Start listening to changes in playback state.
-     *
-     * @deprecated Use
-     *         {@link MediaControllerCompat#registerCallback(MediaControllerCompat.Callback)}.
-     */
-    @Deprecated
-    @Override
-    public void registerStateListener(TransportStateListener listener) {
-        mListeners.add(listener);
-    }
-
-    /**
-     * Stop listening to changes in playback state.
-     *
-     * @deprecated Use
-     *         {@link MediaControllerCompat#unregisterCallback(MediaControllerCompat.Callback)}.
-     */
-    @Deprecated
-    @Override
-    public void unregisterStateListener(TransportStateListener listener) {
-        mListeners.remove(listener);
-    }
-
-    private TransportStateListener[] getListeners() {
-        if (mListeners.size() <= 0) {
-            return null;
-        }
-        TransportStateListener listeners[] = new TransportStateListener[mListeners.size()];
-        mListeners.toArray(listeners);
-        return listeners;
-    }
-
-    private void reportPlayingChanged() {
-        TransportStateListener[] listeners = getListeners();
-        if (listeners != null) {
-            for (TransportStateListener listener : listeners) {
-                listener.onPlayingChanged(this);
-            }
-        }
-    }
-
-    private void reportTransportControlsChanged() {
-        TransportStateListener[] listeners = getListeners();
-        if (listeners != null) {
-            for (TransportStateListener listener : listeners) {
-                listener.onTransportControlsChanged(this);
-            }
-        }
-    }
-
-    private void pushControllerState() {
-        if (mController != null) {
-            mController.refreshState(mCallbacks.onIsPlaying(),
-                    mCallbacks.onGetCurrentPosition(),
-                    mCallbacks.onGetTransportControlFlags());
-        }
-    }
-
-    /**
-     * @deprecated Not needed when you use {@link MediaControllerCompat}.
-     */
-    @Deprecated
-    public void refreshState() {
-        pushControllerState();
-        reportPlayingChanged();
-        reportTransportControlsChanged();
-    }
-
-    /**
-     * Move the controller into the playing state.  This updates the remote control
-     * client to indicate it is playing, and takes audio focus for the app.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#play}.
-     */
-    @Deprecated
-    @Override
-    public void startPlaying() {
-        if (mController != null) {
-            mController.startPlaying();
-        }
-        mCallbacks.onStart();
-        pushControllerState();
-        reportPlayingChanged();
-    }
-
-    /**
-     * Move the controller into the paused state.  This updates the remote control
-     * client to indicate it is paused, but keeps audio focus.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#pause}.
-     */
-    @Deprecated
-    @Override
-    public void pausePlaying() {
-        if (mController != null) {
-            mController.pausePlaying();
-        }
-        mCallbacks.onPause();
-        pushControllerState();
-        reportPlayingChanged();
-    }
-
-    /**
-     * Move the controller into the stopped state.  This updates the remote control
-     * client to indicate it is stopped, and removes audio focus from the app.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#stop}.
-     */
-    @Deprecated
-    @Override
-    public void stopPlaying() {
-        if (mController != null) {
-            mController.stopPlaying();
-        }
-        mCallbacks.onStop();
-        pushControllerState();
-        reportPlayingChanged();
-    }
-
-    /**
-     * Retrieve the total duration of the media stream, in milliseconds.
-     *
-     * @deprecated Use {@link MediaMetadataCompat#METADATA_KEY_DURATION}.
-     */
-    @Deprecated
-    @Override
-    public long getDuration() {
-        return mCallbacks.onGetDuration();
-    }
-
-    /**
-     * Retrieve the current playback location in the media stream, in milliseconds.
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getPosition} and
-     *         {@link PlaybackStateCompat#getLastPositionUpdateTime}.
-     */
-    @Deprecated
-    @Override
-    public long getCurrentPosition() {
-        return mCallbacks.onGetCurrentPosition();
-    }
-
-    /**
-     * Move to a new location in the media stream.
-     * @param pos Position to move to, in milliseconds.
-     *
-     * @deprecated Use {@link MediaControllerCompat.TransportControls#seekTo}.
-     */
-    @Deprecated
-    @Override
-    public void seekTo(long pos) {
-        mCallbacks.onSeekTo(pos);
-    }
-
-    /**
-     * Return whether the player is currently playing its stream.
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getState}.
-     */
-    @Deprecated
-    @Override
-    public boolean isPlaying() {
-        return mCallbacks.onIsPlaying();
-    }
-
-    /**
-     * Retrieve amount, in percentage (0-100), that the media stream has been buffered
-     * on to the local device.  Return 100 if the stream is always local.
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getBufferedPosition} and
-     *         {@link MediaMetadataCompat#METADATA_KEY_DURATION}.
-     */
-    @Deprecated
-    @Override
-    public int getBufferPercentage() {
-        return mCallbacks.onGetBufferPercentage();
-    }
-
-    /**
-     * Retrieve the flags for the media transport control buttons that this transport supports.
-     * Result is a combination of the following flags:
-     *      {@link #FLAG_KEY_MEDIA_PREVIOUS},
-     *      {@link #FLAG_KEY_MEDIA_REWIND},
-     *      {@link #FLAG_KEY_MEDIA_PLAY},
-     *      {@link #FLAG_KEY_MEDIA_PLAY_PAUSE},
-     *      {@link #FLAG_KEY_MEDIA_PAUSE},
-     *      {@link #FLAG_KEY_MEDIA_STOP},
-     *      {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
-     *      {@link #FLAG_KEY_MEDIA_NEXT}
-     *
-     * @deprecated Use {@link PlaybackStateCompat#getActions}.
-     */
-    @Deprecated
-    @Override
-    public int getTransportControlFlags() {
-        return mCallbacks.onGetTransportControlFlags();
-    }
-
-    /**
-     * Optionally call when no longer using the TransportController.  Its resources
-     * will also be automatically cleaned up when your activity/view is detached from
-     * its window, so you don't normally need to call this explicitly.
-     *
-     * @deprecated Not needed when you use {@link MediaControllerCompat}.
-     */
-    @Deprecated
-    public void destroy() {
-        mController.destroy();
-    }
-}
diff --git a/media-compat/java/android/support/v4/media/TransportPerformer.java b/media-compat/java/android/support/v4/media/TransportPerformer.java
deleted file mode 100644
index 79e4869..0000000
--- a/media-compat/java/android/support/v4/media/TransportPerformer.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2013 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.media;
-
-import android.os.SystemClock;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.media.session.PlaybackStateCompat.Builder;
-import android.view.KeyEvent;
-
-/**
- * Implemented by the playback side of the media system, to respond to
- * requests to perform actions and to retrieve its current state.  These
- * requests may either come from key events dispatched directly to your UI, or
- * events sent over a media button event receiver that this class keeps active
- * while your window is in focus.
- *
- * @deprecated Use {@link MediaSessionCompat}.
- */
-@Deprecated
-public abstract class TransportPerformer {
-    /**
-     * @deprecated Use {@link MediaSessionCompat}.
-     */
-    @Deprecated
-    public TransportPerformer() {
-    }
-
-    /**
-     * Request to start playback on the media, resuming from whatever current state
-     * (position etc) it is in.
-     *
-     * @deprecated Use {@link MediaSessionCompat.Callback#onPlay}.
-     */
-    @Deprecated
-    public abstract void onStart();
-
-    /**
-     * Request to pause playback of the media, staying at the current playback position
-     * and other state so a later call to {@link #onStart()} will resume at the same place.
-     *
-     * @deprecated Use {@link MediaSessionCompat.Callback#onPause}.
-     */
-    @Deprecated
-    public abstract void onPause();
-
-    /**
-     * Request to completely stop playback of the media, clearing whatever state the
-     * player thinks is appropriate.
-     *
-     * @deprecated Use {@link MediaSessionCompat.Callback#onStop}.
-     */
-    @Deprecated
-    public abstract void onStop();
-
-    /**
-     * Request to return the duration of the current media, in milliseconds.
-     *
-     * @deprecated Use {@link MediaMetadataCompat.Builder#putLong} with
-     *         {@link MediaMetadataCompat#METADATA_KEY_DURATION}.
-     */
-    @Deprecated
-    public abstract long onGetDuration();
-
-    /**
-     * Request to return the current playback position, in milliseconds.
-     *
-     * @deprecated Use {@link Builder#setState(int, long, float)}.
-     */
-    @Deprecated
-    public abstract long onGetCurrentPosition();
-
-    /**
-     * Request to move the current playback position.
-     * @param pos New position to move to, in milliseconds.
-     *
-     * @deprecated Use {@link MediaSessionCompat.Callback#onSeekTo}.
-     */
-    @Deprecated
-    public abstract void onSeekTo(long pos);
-
-    /**
-     * Request to find out whether the player is currently playing its media.
-     *
-     * @deprecated Use {@link Builder#setState(int, long, float)}.
-     */
-    @Deprecated
-    public abstract boolean onIsPlaying();
-
-    /**
-     * Request to find out how much of the media has been buffered on the local device.
-     * @return Return a percentage (0-100) indicating how much of the total data
-     * has been buffered.  The default implementation returns 100, meaning the content
-     * is always on the local device.
-     *
-     * @deprecated Use {@link Builder#setBufferedPosition}.
-     */
-    @Deprecated
-    public int onGetBufferPercentage() {
-        return 100;
-    }
-
-    /**
-     * Retrieves the flags for the media transport control buttons that this transport supports.
-     * Result is a combination of the following flags:
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PREVIOUS},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_REWIND},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PLAY},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PLAY_PAUSE},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PAUSE},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_STOP},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_FAST_FORWARD},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_NEXT}
-     *
-     * <p>The default implementation returns:
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PLAY},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PLAY_PAUSE},
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_PAUSE}, and
-     *      {@link TransportMediator#FLAG_KEY_MEDIA_STOP}</p>
-     *
-     * @deprecated Use {@link Builder#setActions}.
-     */
-    @Deprecated
-    public int onGetTransportControlFlags() {
-        return TransportMediator.FLAG_KEY_MEDIA_PLAY
-                | TransportMediator.FLAG_KEY_MEDIA_PLAY_PAUSE
-                | TransportMediator.FLAG_KEY_MEDIA_PAUSE
-                | TransportMediator.FLAG_KEY_MEDIA_STOP;
-    }
-
-    /**
-     * Report that a media button has been pressed.  This is like
-     * {@link android.view.KeyEvent.Callback#onKeyDown(int, android.view.KeyEvent)} but
-     * will only deliver media keys.  The default implementation handles these keys:
-     * <ul>
-     *     <li>KEYCODE_MEDIA_PLAY: call {@link #onStart}</li>
-     *     <li>KEYCODE_MEDIA_PAUSE: call {@link #onPause}</li>
-     *     <li>KEYCODE_MEDIA_STOP: call {@link #onStop}</li>
-     *     <li>KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_HEADSETHOOK: call {@link #onPause}
-     *          if {@link #onIsPlaying()} returns true, otherwise call {@link #onStart}</li>
-     * </ul>
-     * @param keyCode The code of the media key.
-     * @param event The full key event.
-     * @return Indicate whether the key has been consumed.  The default
-     * implementation always returns true.  This only matters for keys
-     * being dispatched here from
-     * {@link TransportMediator#dispatchKeyEvent(android.view.KeyEvent)
-     * TransportController.dispatchKeyEvent}, and determines whether the key
-     * continues on to its default key handling (which for media keys means
-     * being delivered to the current media remote control, which should
-     * be us).
-     *
-     * @deprecated Use {@link MediaSessionCompat.Callback#onMediaButtonEvent}.
-     */
-    @Deprecated
-    public boolean onMediaButtonDown(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case TransportMediator.KEYCODE_MEDIA_PLAY:
-                onStart();
-                return true;
-            case TransportMediator.KEYCODE_MEDIA_PAUSE:
-                onPause();
-                return true;
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-                onStop();
-                return true;
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-                if (onIsPlaying()) {
-                    onPause();
-                } else {
-                    onStart();
-                }
-        }
-        return true;
-    }
-
-    /**
-     * Report that a media button has been released.  This is like
-     * {@link KeyEvent.Callback#onKeyUp(int, android.view.KeyEvent)} but
-     * will only deliver media keys.  The default implementation does nothing.
-     * @param keyCode The code of the media key.
-     * @param event The full key event.
-     * @return Indicate whether the key has been consumed.  The default
-     * implementation always returns true.  This only matters for keys
-     * being dispatched here from
-     * {@link TransportMediator#dispatchKeyEvent(android.view.KeyEvent)
-     * TransportController.dispatchKeyEvent}, and determines whether the key
-     * continues on to its default key handling (which for media keys means
-     * being delivered to the current media remote control, which should
-     * be us).
-     *
-     * @deprecated Use {@link MediaSessionCompat.Callback#onMediaButtonEvent}.
-     */
-    @Deprecated
-    public boolean onMediaButtonUp(int keyCode, KeyEvent event) {
-        return true;
-    }
-
-    // Copy constants from framework since we can't link to them.
-    static final int AUDIOFOCUS_GAIN = 1;
-    static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
-    static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
-    static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
-    static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
-    static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
-            -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
-
-    /**
-     * Report that audio focus has changed on the app.  This only happens if
-     * you have indicated you have started playing with
-     * {@link TransportMediator#startPlaying TransportController.startPlaying},
-     * which takes audio focus for you.
-     * @param focusChange The type of focus change, as per
-     * {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange(int)
-     * OnAudioFocusChangeListener.onAudioFocusChange}.  The default implementation will
-     * deliver a {@link KeyEvent#KEYCODE_MEDIA_STOP}
-     * when receiving {@link android.media.AudioManager#AUDIOFOCUS_LOSS}.
-     *
-     * @deprecated You must implement your own audio focus handler.
-     */
-    @Deprecated
-    public void onAudioFocusChange(int focusChange) {
-        int keyCode = 0;
-        switch (focusChange) {
-            case AUDIOFOCUS_LOSS:
-                // This will cause us to stop playback, which means we drop audio focus
-                // so we will not get any further audio focus gain.
-                keyCode = TransportMediator.KEYCODE_MEDIA_PAUSE;
-                break;
-        }
-        if (keyCode != 0) {
-            final long now = SystemClock.uptimeMillis();
-            onMediaButtonDown(keyCode, new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0));
-            onMediaButtonUp(keyCode, new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0));
-        }
-    }
-}
diff --git a/media-compat/java/android/support/v4/media/TransportStateListener.java b/media-compat/java/android/support/v4/media/TransportStateListener.java
deleted file mode 100644
index 554983e..0000000
--- a/media-compat/java/android/support/v4/media/TransportStateListener.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 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.media;
-
-import android.support.v4.media.session.MediaControllerCompat.Callback;
-
-/**
- * A listener for playback changes that can be registered with
- * {@link TransportController}.
- *
- * @deprecated Use {@link Callback}.
- */
-@Deprecated
-public class TransportStateListener {
-    /**
-     * @deprecated Use {@link Callback}.
-     */
-    @Deprecated
-    public TransportStateListener() {
-    }
-
-    /**
-     * The play state of the transport changed.  Use
-     * {@link android.support.v4.media.TransportController#isPlaying()
-     * TransportController.isPlaying()} to determine the new state.
-     *
-     * @deprecated Use {@link Callback#onPlaybackStateChanged}.
-     */
-    @Deprecated
-    public void onPlayingChanged(TransportController controller) {
-    }
-
-    /**
-     * The available controls of the transport changed.  Use
-     * {@link TransportController#getTransportControlFlags()}
-     * TransportController.getTransportControlFlags()} to determine the new state.
-     *
-     * @deprecated Use {@link Callback#onPlaybackStateChanged}.
-     */
-    @Deprecated
-    public void onTransportControlsChanged(TransportController controller) {
-    }
-}
diff --git a/media-compat/java/android/support/v4/media/VolumeProviderCompat.java b/media-compat/java/android/support/v4/media/VolumeProviderCompat.java
index a3aa047..3085969 100644
--- a/media-compat/java/android/support/v4/media/VolumeProviderCompat.java
+++ b/media-compat/java/android/support/v4/media/VolumeProviderCompat.java
@@ -121,7 +121,7 @@
     public final void setCurrentVolume(int currentVolume) {
         mCurrentVolume = currentVolume;
         Object volumeProviderObj = getVolumeProvider();
-        if (volumeProviderObj != null) {
+        if (volumeProviderObj != null && Build.VERSION.SDK_INT >= 21) {
             VolumeProviderCompatApi21.setCurrentVolume(volumeProviderObj, currentVolume);
         }
         if (mCallback != null) {
@@ -164,23 +164,22 @@
      * @return An equivalent {@link android.media.VolumeProvider} object, or null if none.
      */
     public Object getVolumeProvider() {
-        if (mVolumeProviderObj != null || Build.VERSION.SDK_INT < 21) {
-            return mVolumeProviderObj;
+        if (mVolumeProviderObj == null && Build.VERSION.SDK_INT >= 21) {
+            mVolumeProviderObj = VolumeProviderCompatApi21.createVolumeProvider(
+                    mControlType, mMaxVolume, mCurrentVolume,
+                    new VolumeProviderCompatApi21.Delegate() {
+
+                        @Override
+                        public void onSetVolumeTo(int volume) {
+                            VolumeProviderCompat.this.onSetVolumeTo(volume);
+                        }
+
+                        @Override
+                        public void onAdjustVolume(int direction) {
+                            VolumeProviderCompat.this.onAdjustVolume(direction);
+                        }
+                    });
         }
-
-        mVolumeProviderObj = VolumeProviderCompatApi21.createVolumeProvider(
-                mControlType, mMaxVolume, mCurrentVolume, new VolumeProviderCompatApi21.Delegate() {
-
-            @Override
-            public void onSetVolumeTo(int volume) {
-                VolumeProviderCompat.this.onSetVolumeTo(volume);
-            }
-
-            @Override
-            public void onAdjustVolume(int direction) {
-                VolumeProviderCompat.this.onAdjustVolume(direction);
-            }
-        });
         return mVolumeProviderObj;
     }
 
diff --git a/media-compat/java/android/support/v4/media/app/NotificationCompat.java b/media-compat/java/android/support/v4/media/app/NotificationCompat.java
new file mode 100644
index 0000000..be1d423
--- /dev/null
+++ b/media-compat/java/android/support/v4/media/app/NotificationCompat.java
@@ -0,0 +1,502 @@
+/*
+ * 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.
+ */
+
+package android.support.v4.media.app;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static android.support.v4.app.NotificationCompat.COLOR_DEFAULT;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.media.session.MediaSession;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.mediacompat.R;
+import android.support.v4.app.BundleCompat;
+import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.view.View;
+import android.widget.RemoteViews;
+
+/**
+ * Class containing media specfic {@link android.support.v4.app.NotificationCompat.Style styles}
+ * that you can use with {@link android.support.v4.app.NotificationCompat.Builder#setStyle}.
+ */
+public class NotificationCompat {
+
+    private NotificationCompat() {
+    }
+
+    /**
+     * Notification style for media playback notifications.
+     *
+     * In the expanded form, up to 5
+     * {@link android.support.v4.app.NotificationCompat.Action actions} specified with
+     * {@link android.support.v4.app.NotificationCompat.Builder
+     * #addAction(int, CharSequence, PendingIntent) addAction} will be shown as icon-only
+     * pushbuttons, suitable for transport controls. The Bitmap given to
+     * {@link android.support.v4.app.NotificationCompat.Builder
+     * #setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will
+     * be treated as album artwork.
+     *
+     * Unlike the other styles provided here, MediaStyle can also modify the standard-size
+     * content view; by providing action indices to
+     * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
+     * in the standard view alongside the usual content.
+     *
+     * Notifications created with MediaStyle will have their category set to
+     * {@link android.support.v4.app.NotificationCompat#CATEGORY_TRANSPORT CATEGORY_TRANSPORT}
+     * unless you set a different category using
+     * {@link android.support.v4.app.NotificationCompat.Builder#setCategory(String)
+     * setCategory()}.
+     *
+     * Finally, if you attach a {@link MediaSession.Token} using
+     * {@link android.support.v4.media.app.NotificationCompat.MediaStyle#setMediaSession}, the
+     * System UI can identify this as a notification representing an active media session and
+     * respond accordingly (by showing album artwork in the lockscreen, for example).
+     *
+     * To use this style with your Notification, feed it to
+     * {@link android.support.v4.app.NotificationCompat.Builder#setStyle} like so:
+     * <pre class="prettyprint">
+     * Notification noti = new NotificationCompat.Builder()
+     *     .setSmallIcon(R.drawable.ic_stat_player)
+     *     .setContentTitle(&quot;Track title&quot;)
+     *     .setContentText(&quot;Artist - Album&quot;)
+     *     .setLargeIcon(albumArtBitmap))
+     *     .setStyle(<b>new NotificationCompat.MediaStyle()</b>
+     *         .setMediaSession(mySession))
+     *     .build();
+     * </pre>
+     *
+     * @see Notification#bigContentView
+     */
+    public static class MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+
+        /**
+         * Extracts a {@link MediaSessionCompat.Token} from the extra values
+         * in the {@link MediaStyle} {@link Notification notification}.
+         *
+         * @param notification The notification to extract a {@link MediaSessionCompat.Token} from.
+         * @return The {@link MediaSessionCompat.Token} in the {@code notification} if it contains,
+         *         null otherwise.
+         */
+        public static MediaSessionCompat.Token getMediaSession(Notification notification) {
+            Bundle extras = android.support.v4.app.NotificationCompat.getExtras(notification);
+            if (extras != null) {
+                if (Build.VERSION.SDK_INT >= 21) {
+                    Object tokenInner = extras.getParcelable(
+                            android.support.v4.app.NotificationCompat.EXTRA_MEDIA_SESSION);
+                    if (tokenInner != null) {
+                        return MediaSessionCompat.Token.fromToken(tokenInner);
+                    }
+                } else {
+                    IBinder tokenInner = BundleCompat.getBinder(extras,
+                            android.support.v4.app.NotificationCompat.EXTRA_MEDIA_SESSION);
+                    if (tokenInner != null) {
+                        Parcel p = Parcel.obtain();
+                        p.writeStrongBinder(tokenInner);
+                        p.setDataPosition(0);
+                        MediaSessionCompat.Token token =
+                                MediaSessionCompat.Token.CREATOR.createFromParcel(p);
+                        p.recycle();
+                        return token;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
+        private static final int MAX_MEDIA_BUTTONS = 5;
+
+        int[] mActionsToShowInCompact = null;
+        MediaSessionCompat.Token mToken;
+        boolean mShowCancelButton;
+        PendingIntent mCancelButtonIntent;
+
+        public MediaStyle() {
+        }
+
+        public MediaStyle(android.support.v4.app.NotificationCompat.Builder builder) {
+            setBuilder(builder);
+        }
+
+        /**
+         * Requests up to 3 actions (by index in the order of addition) to be shown in the compact
+         * notification view.
+         *
+         * @param actions the indices of the actions to show in the compact notification view
+         */
+        public MediaStyle setShowActionsInCompactView(int...actions) {
+            mActionsToShowInCompact = actions;
+            return this;
+        }
+
+        /**
+         * Attaches a {@link MediaSessionCompat.Token} to this Notification
+         * to provide additional playback information and control to the SystemUI.
+         */
+        public MediaStyle setMediaSession(MediaSessionCompat.Token token) {
+            mToken = token;
+            return this;
+        }
+
+        /**
+         * Sets whether a cancel button at the top right should be shown in the notification on
+         * platforms before Lollipop.
+         *
+         * <p>Prior to Lollipop, there was a bug in the framework which prevented the developer to
+         * make a notification dismissable again after having used the same notification as the
+         * ongoing notification for a foreground service. When the notification was posted by
+         * {@link android.app.Service#startForeground}, but then the service exited foreground mode
+         * via {@link android.app.Service#stopForeground}, without removing the notification, the
+         * notification stayed ongoing, and thus not dismissable.
+         *
+         * <p>This is a common scenario for media notifications, as this is exactly the service
+         * lifecycle that happens when playing/pausing media. Thus, a workaround is provided by the
+         * support library: Instead of making the notification ongoing depending on the playback
+         * state, the support library provides the ability to add an explicit cancel button to the
+         * notification.
+         *
+         * <p>Note that the notification is enforced to be ongoing if a cancel button is shown to
+         * provide a consistent user experience.
+         *
+         * <p>Also note that this method is a no-op when running on Lollipop and later.
+         *
+         * @param show whether to show a cancel button
+         */
+        public MediaStyle setShowCancelButton(boolean show) {
+            if (Build.VERSION.SDK_INT < 21) {
+                mShowCancelButton = show;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the pending intent to be sent when the cancel button is pressed. See {@link
+         * #setShowCancelButton}.
+         *
+         * @param pendingIntent the intent to be sent when the cancel button is pressed
+         */
+        public MediaStyle setCancelButtonIntent(PendingIntent pendingIntent) {
+            mCancelButtonIntent = pendingIntent;
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 21) {
+                builder.getBuilder().setStyle(
+                        fillInMediaStyle(new Notification.MediaStyle()));
+            } else if (mShowCancelButton) {
+                builder.getBuilder().setOngoing(true);
+            }
+        }
+
+        @RequiresApi(21)
+        Notification.MediaStyle fillInMediaStyle(Notification.MediaStyle style) {
+            if (mActionsToShowInCompact != null) {
+                style.setShowActionsInCompactView(mActionsToShowInCompact);
+            }
+            if (mToken != null) {
+                style.setMediaSession((MediaSession.Token) mToken.getToken());
+            }
+            return style;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 21) {
+                // No custom content view required
+                return null;
+            }
+            return generateContentView();
+        }
+
+        RemoteViews generateContentView() {
+            RemoteViews view = applyStandardTemplate(false /* showSmallIcon */,
+                    getContentViewLayoutResource(), true /* fitIn1U */);
+
+            final int numActions = mBuilder.mActions.size();
+            final int numActionsInCompact = mActionsToShowInCompact == null
+                    ? 0
+                    : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+            view.removeAllViews(R.id.media_actions);
+            if (numActionsInCompact > 0) {
+                for (int i = 0; i < numActionsInCompact; i++) {
+                    if (i >= numActions) {
+                        throw new IllegalArgumentException(String.format(
+                                "setShowActionsInCompactView: action %d out of bounds (max %d)",
+                                i, numActions - 1));
+                    }
+
+                    final android.support.v4.app.NotificationCompat.Action action =
+                            mBuilder.mActions.get(mActionsToShowInCompact[i]);
+                    final RemoteViews button = generateMediaActionButton(action);
+                    view.addView(R.id.media_actions, button);
+                }
+            }
+            if (mShowCancelButton) {
+                view.setViewVisibility(R.id.end_padder, View.GONE);
+                view.setViewVisibility(R.id.cancel_action, View.VISIBLE);
+                view.setOnClickPendingIntent(R.id.cancel_action, mCancelButtonIntent);
+                view.setInt(R.id.cancel_action, "setAlpha", mBuilder.mContext
+                        .getResources().getInteger(R.integer.cancel_button_image_alpha));
+            } else {
+                view.setViewVisibility(R.id.end_padder, View.VISIBLE);
+                view.setViewVisibility(R.id.cancel_action, View.GONE);
+            }
+            return view;
+        }
+
+        private RemoteViews generateMediaActionButton(
+                android.support.v4.app.NotificationCompat.Action action) {
+            final boolean tombstone = (action.getActionIntent() == null);
+            RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
+                    R.layout.notification_media_action);
+            button.setImageViewResource(R.id.action0, action.getIcon());
+            if (!tombstone) {
+                button.setOnClickPendingIntent(R.id.action0, action.getActionIntent());
+            }
+            if (Build.VERSION.SDK_INT >= 15) {
+                button.setContentDescription(R.id.action0, action.getTitle());
+            }
+            return button;
+        }
+
+        int getContentViewLayoutResource() {
+            return R.layout.notification_template_media;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 21) {
+                // No custom content view required
+                return null;
+            }
+            return generateBigContentView();
+        }
+
+        RemoteViews generateBigContentView() {
+            final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
+            RemoteViews big = applyStandardTemplate(false /* showSmallIcon */,
+                    getBigContentViewLayoutResource(actionCount), false /* fitIn1U */);
+
+            big.removeAllViews(R.id.media_actions);
+            if (actionCount > 0) {
+                for (int i = 0; i < actionCount; i++) {
+                    final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
+                    big.addView(R.id.media_actions, button);
+                }
+            }
+            if (mShowCancelButton) {
+                big.setViewVisibility(R.id.cancel_action, View.VISIBLE);
+                big.setInt(R.id.cancel_action, "setAlpha", mBuilder.mContext
+                        .getResources().getInteger(R.integer.cancel_button_image_alpha));
+                big.setOnClickPendingIntent(R.id.cancel_action, mCancelButtonIntent);
+            } else {
+                big.setViewVisibility(R.id.cancel_action, View.GONE);
+            }
+            return big;
+        }
+
+        int getBigContentViewLayoutResource(int actionCount) {
+            return actionCount <= 3
+                    ? R.layout.notification_template_big_media_narrow
+                    : R.layout.notification_template_big_media;
+        }
+    }
+
+    /**
+     * Notification style for media custom views that are decorated by the system.
+     *
+     * <p>Instead of providing a media notification that is completely custom, a developer can set
+     * this style and still obtain system decorations like the notification header with the expand
+     * affordance and actions.
+     *
+     * <p>Use {@link android.support.v4.app.NotificationCompat.Builder
+     * #setCustomContentView(RemoteViews)},
+     * {@link android.support.v4.app.NotificationCompat.Builder
+     * #setCustomBigContentView(RemoteViews)} and
+     * {@link android.support.v4.app.NotificationCompat.Builder
+     * #setCustomHeadsUpContentView(RemoteViews)} to set the
+     * corresponding custom views to display.
+     *
+     * <p>To use this style with your Notification, feed it to
+     * {@link android.support.v4.app.NotificationCompat.Builder
+     * #setStyle(android.support.v4.app.NotificationCompat.Style)} like so:
+     * <pre class="prettyprint">
+     * Notification noti = new NotificationCompat.Builder()
+     *     .setSmallIcon(R.drawable.ic_stat_player)
+     *     .setLargeIcon(albumArtBitmap))
+     *     .setCustomContentView(contentView)
+     *     .setStyle(<b>new NotificationCompat.DecoratedMediaCustomViewStyle()</b>
+     *          .setMediaSession(mySession))
+     *     .build();
+     * </pre>
+     *
+     * <p>If you are using this style, consider using the corresponding styles like
+     * {@link android.support.mediacompat.R.style#TextAppearance_Compat_Notification_Media} or
+     * {@link
+     * android.support.mediacompat.R.style#TextAppearance_Compat_Notification_Title_Media} in
+     * your custom views in order to get the correct styling on each platform version.
+     *
+     * @see android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle
+     * @see MediaStyle
+     */
+    public static class DecoratedMediaCustomViewStyle extends MediaStyle {
+
+        public DecoratedMediaCustomViewStyle() {
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                builder.getBuilder().setStyle(
+                        fillInMediaStyle(new Notification.DecoratedMediaCustomViewStyle()));
+            } else {
+                super.apply(builder);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                // No custom content view required
+                return null;
+            }
+            boolean hasContentView = mBuilder.getContentView() != null;
+            if (Build.VERSION.SDK_INT >= 21) {
+                // If we are on L/M the media notification will only be colored if the expanded
+                // version is of media style, so we have to create a custom view for the collapsed
+                // version as well in that case.
+                boolean createCustomContent = hasContentView
+                        || mBuilder.getBigContentView() != null;
+                if (createCustomContent) {
+                    RemoteViews contentView = generateContentView();
+                    if (hasContentView) {
+                        buildIntoRemoteViews(contentView, mBuilder.getContentView());
+                    }
+                    setBackgroundColor(contentView);
+                    return contentView;
+                }
+            } else {
+                RemoteViews contentView = generateContentView();
+                if (hasContentView) {
+                    buildIntoRemoteViews(contentView, mBuilder.getContentView());
+                    return contentView;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        int getContentViewLayoutResource() {
+            return mBuilder.getContentView() != null
+                    ? R.layout.notification_template_media_custom
+                    : super.getContentViewLayoutResource();
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                // No custom big content view required
+                return null;
+            }
+            RemoteViews innerView = mBuilder.getBigContentView() != null
+                    ? mBuilder.getBigContentView()
+                    : mBuilder.getContentView();
+            if (innerView == null) {
+                // No expandable notification
+                return null;
+            }
+            RemoteViews bigContentView = generateBigContentView();
+            buildIntoRemoteViews(bigContentView, innerView);
+            if (Build.VERSION.SDK_INT >= 21) {
+                setBackgroundColor(bigContentView);
+            }
+            return bigContentView;
+        }
+
+        @Override
+        int getBigContentViewLayoutResource(int actionCount) {
+            return actionCount <= 3
+                    ? R.layout.notification_template_big_media_narrow_custom
+                    : R.layout.notification_template_big_media_custom;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        @Override
+        public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                // No custom heads up content view required
+                return null;
+            }
+            RemoteViews innerView = mBuilder.getHeadsUpContentView() != null
+                    ? mBuilder.getHeadsUpContentView()
+                    : mBuilder.getContentView();
+            if (innerView == null) {
+                // No expandable notification
+                return null;
+            }
+            RemoteViews headsUpContentView = generateBigContentView();
+            buildIntoRemoteViews(headsUpContentView, innerView);
+            if (Build.VERSION.SDK_INT >= 21) {
+                setBackgroundColor(headsUpContentView);
+            }
+            return headsUpContentView;
+        }
+
+        private void setBackgroundColor(RemoteViews views) {
+            int color = mBuilder.getColor() != COLOR_DEFAULT
+                    ? mBuilder.getColor()
+                    : mBuilder.mContext.getResources().getColor(
+                            R.color.notification_material_background_media_default_color);
+            views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color);
+        }
+    }
+}
diff --git a/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl b/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl
index ac0de3d..4d82a72 100644
--- a/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl
+++ b/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl
@@ -38,6 +38,7 @@
     void onExtrasChanged(in Bundle extras);
     void onVolumeInfoChanged(in ParcelableVolumeInfo info);
     void onRepeatModeChanged(int repeatMode);
-    void onShuffleModeChanged(boolean enabled);
+    void onShuffleModeChangedDeprecated(boolean enabled);
     void onCaptioningEnabledChanged(boolean enabled);
+    void onShuffleModeChanged(int shuffleMode);
 }
diff --git a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
index 39cfbe6..3926ac2 100644
--- a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
+++ b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
@@ -34,7 +34,7 @@
  * @hide
  */
 interface IMediaSession {
-    // Next ID: 46
+    // Next ID: 48
     void sendCommand(String command, in Bundle args, in MediaSessionCompat.ResultReceiverWrapper cb) = 0;
     boolean sendMediaButton(in KeyEvent mediaButton) = 1;
     void registerCallbackListener(in IMediaControllerCallback cb) = 2;
@@ -55,7 +55,8 @@
     int getRatingType() = 31;
     boolean isCaptioningEnabled() = 44;
     int getRepeatMode() = 36;
-    boolean isShuffleModeEnabled() = 37;
+    boolean isShuffleModeEnabledDeprecated() = 37;
+    int getShuffleMode() = 46;
     void addQueueItem(in MediaDescriptionCompat description) = 40;
     void addQueueItemAt(in MediaDescriptionCompat description, int index) = 41;
     void removeQueueItem(in MediaDescriptionCompat description) = 42;
@@ -79,8 +80,10 @@
     void rewind() = 22;
     void seekTo(long pos) = 23;
     void rate(in RatingCompat rating) = 24;
+    void rateWithExtras(in RatingCompat rating, in Bundle extras) = 50;
     void setCaptioningEnabled(boolean enabled) = 45;
     void setRepeatMode(int repeatMode) = 38;
-    void setShuffleModeEnabled(boolean shuffleMode) = 39;
+    void setShuffleModeEnabledDeprecated(boolean shuffleMode) = 39;
+    void setShuffleMode(int shuffleMode) = 47;
     void sendCustomAction(String action, in Bundle args) = 25;
 }
diff --git a/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java b/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java
index 1e05889..d411131 100644
--- a/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java
+++ b/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java
@@ -24,6 +24,9 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.os.RemoteException;
+import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.MediaBrowserServiceCompat;
 import android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction;
 import android.util.Log;
@@ -32,9 +35,8 @@
 import java.util.List;
 
 /**
- * A media button receiver receives and helps translate hardware media playback buttons,
- * such as those found on wired and wireless headsets, into the appropriate callbacks
- * in your app.
+ * A media button receiver receives and helps translate hardware media playback buttons, such as
+ * those found on wired and wireless headsets, into the appropriate callbacks in your app.
  * <p />
  * You can add this MediaButtonReceiver to your app by adding it directly to your
  * AndroidManifest.xml:
@@ -45,12 +47,19 @@
  *   &lt;/intent-filter&gt;
  * &lt;/receiver&gt;
  * </pre>
- * This class assumes you have a {@link Service} in your app that controls
- * media playback via a {@link MediaSessionCompat} - all {@link Intent}s received by
- * the MediaButtonReceiver will be forwarded to that service.
+ *
+ * This class assumes you have a {@link Service} in your app that controls media playback via a
+ * {@link MediaSessionCompat}. Once a key event is received by MediaButtonReceiver, this class tries
+ * to find a {@link Service} that can handle {@link Intent#ACTION_MEDIA_BUTTON}, and a
+ * {@link MediaBrowserServiceCompat} in turn. If an appropriate service is found, this class
+ * forwards the key event to the service. If neither is available or more than one valid
+ * service/media browser service is found, an {@link IllegalStateException} will be thrown. Thus,
+ * your app should have one of the following services to get a key event properly.
  * <p />
- * First priority is given to a {@link Service}
- * that includes an intent filter that handles {@link Intent#ACTION_MEDIA_BUTTON}:
+ *
+ * <h4>Service Handling ACTION_MEDIA_BUTTON</h4>
+ * A service can receive a key event by including an intent filter that handles
+ * {@link Intent#ACTION_MEDIA_BUTTON}:
  * <pre>
  * &lt;service android:name="com.example.android.MediaPlaybackService" &gt;
  *   &lt;intent-filter&gt;
@@ -59,14 +68,9 @@
  * &lt;/service&gt;
  * </pre>
  *
- * If such a {@link Service} is not found, MediaButtonReceiver will attempt to
- * find a media browser service implementation.
- * If neither is available or more than one valid service/media browser service is found, an
- * {@link IllegalStateException} will be thrown.
- * <p />
  * Events can then be handled in {@link Service#onStartCommand(Intent, int, int)} by calling
- * {@link MediaButtonReceiver#handleIntent(MediaSessionCompat, Intent)}, passing in
- * your current {@link MediaSessionCompat}:
+ * {@link MediaButtonReceiver#handleIntent(MediaSessionCompat, Intent)}, passing in your current
+ * {@link MediaSessionCompat}:
  * <pre>
  * private MediaSessionCompat mMediaSessionCompat = ...;
  *
@@ -76,57 +80,104 @@
  * }
  * </pre>
  *
- * This ensures that the correct callbacks to {@link MediaSessionCompat.Callback}
- * will be triggered based on the incoming {@link KeyEvent}.
+ * This ensures that the correct callbacks to {@link MediaSessionCompat.Callback} will be triggered
+ * based on the incoming {@link KeyEvent}.
+ * <p class="note"><strong>Note:</strong> Once the service is started, it must start to run in the
+ * foreground.</p>
+ *
+ * <h4>MediaBrowserService</h4>
+ * If you already have a {@link MediaBrowserServiceCompat} in your app, MediaButtonReceiver will
+ * deliver the received key events to the {@link MediaBrowserServiceCompat} by default. You can
+ * handle them in your {@link MediaSessionCompat.Callback}.
  */
 public class MediaButtonReceiver extends BroadcastReceiver {
     private static final String TAG = "MediaButtonReceiver";
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-        queryIntent.setPackage(context.getPackageName());
-        PackageManager pm = context.getPackageManager();
-        List<ResolveInfo> resolveInfos = pm.queryIntentServices(queryIntent, 0);
-        if (resolveInfos.isEmpty()) {
-            // Fall back to looking for any available media browser service
-            queryIntent.setAction(MediaBrowserServiceCompat.SERVICE_INTERFACE);
-            resolveInfos = pm.queryIntentServices(queryIntent, 0);
+        if (intent == null
+                || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
+                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
+            Log.d(TAG, "Ignore unsupported intent: " + intent);
+            return;
         }
-        if (resolveInfos.isEmpty()) {
-            throw new IllegalStateException("Could not find any Service that handles " +
-                    Intent.ACTION_MEDIA_BUTTON + " or a media browser service implementation");
-        } else if (resolveInfos.size() != 1) {
-            throw new IllegalStateException("Expected 1 Service that handles " +
-                    queryIntent.getAction() + ", found " + resolveInfos.size() );
+        ComponentName mediaButtonServiceComponentName =
+                getServiceComponentByAction(context, Intent.ACTION_MEDIA_BUTTON);
+        if (mediaButtonServiceComponentName != null) {
+            intent.setComponent(mediaButtonServiceComponentName);
+            startForegroundService(context, intent);
+            return;
         }
-        ResolveInfo resolveInfo = resolveInfos.get(0);
-        ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
-                resolveInfo.serviceInfo.name);
-        intent.setComponent(componentName);
-        context.startService(intent);
+        ComponentName mediaBrowserServiceComponentName = getServiceComponentByAction(context,
+                MediaBrowserServiceCompat.SERVICE_INTERFACE);
+        if (mediaBrowserServiceComponentName != null) {
+            PendingResult pendingResult = goAsync();
+            Context applicationContext = context.getApplicationContext();
+            MediaButtonConnectionCallback connectionCallback =
+                    new MediaButtonConnectionCallback(applicationContext, intent, pendingResult);
+            MediaBrowserCompat mediaBrowser = new MediaBrowserCompat(applicationContext,
+                    mediaBrowserServiceComponentName, connectionCallback, null);
+            connectionCallback.setMediaBrowser(mediaBrowser);
+            mediaBrowser.connect();
+            return;
+        }
+        throw new IllegalStateException("Could not find any Service that handles "
+                + Intent.ACTION_MEDIA_BUTTON + " or implements a media browser service.");
     }
 
+    private static class MediaButtonConnectionCallback extends
+            MediaBrowserCompat.ConnectionCallback {
+        private final Context mContext;
+        private final Intent mIntent;
+        private final PendingResult mPendingResult;
+
+        private MediaBrowserCompat mMediaBrowser;
+
+        MediaButtonConnectionCallback(Context context, Intent intent, PendingResult pendingResult) {
+            mContext = context;
+            mIntent = intent;
+            mPendingResult = pendingResult;
+        }
+
+        void setMediaBrowser(MediaBrowserCompat mediaBrowser) {
+            mMediaBrowser = mediaBrowser;
+        }
+
+        @Override
+        public void onConnected() {
+            try {
+                MediaControllerCompat mediaController = new MediaControllerCompat(mContext,
+                        mMediaBrowser.getSessionToken());
+                KeyEvent ke = mIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+                mediaController.dispatchMediaButtonEvent(ke);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to create a media controller", e);
+            }
+            finish();
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            finish();
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            finish();
+        }
+
+        private void finish() {
+            mMediaBrowser.disconnect();
+            mPendingResult.finish();
+        }
+    };
+
     /**
      * Extracts any available {@link KeyEvent} from an {@link Intent#ACTION_MEDIA_BUTTON}
      * intent, passing it onto the {@link MediaSessionCompat} using
      * {@link MediaControllerCompat#dispatchMediaButtonEvent(KeyEvent)}, which in turn
      * will trigger callbacks to the {@link MediaSessionCompat.Callback} registered via
      * {@link MediaSessionCompat#setCallback(MediaSessionCompat.Callback)}.
-     * <p />
-     * The returned {@link KeyEvent} is non-null if any {@link KeyEvent} is found and can
-     * be used if any additional processing is needed beyond what is done in the
-     * {@link MediaSessionCompat.Callback}. An example of is to prevent redelivery of a
-     * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} Intent in the case of the Service being
-     * restarted (which, by default, will redeliver the last received Intent).
-     * <pre>
-     * KeyEvent keyEvent = MediaButtonReceiver.handleIntent(mediaSession, intent);
-     * if (keyEvent != null && keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
-     *   Intent emptyIntent = new Intent(intent);
-     *   emptyIntent.setAction("");
-     *   startService(emptyIntent);
-     * }
-     * </pre>
      * @param mediaSessionCompat A {@link MediaSessionCompat} that has a
      *            {@link MediaSessionCompat.Callback} set.
      * @param intent The intent to parse.
@@ -231,4 +282,29 @@
         }
         return null;
     }
+
+    private static void startForegroundService(Context context, Intent intent) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            context.startForegroundService(intent);
+        } else {
+            context.startService(intent);
+        }
+    }
+
+    private static ComponentName getServiceComponentByAction(Context context, String action) {
+        PackageManager pm = context.getPackageManager();
+        Intent queryIntent = new Intent(action);
+        queryIntent.setPackage(context.getPackageName());
+        List<ResolveInfo> resolveInfos = pm.queryIntentServices(queryIntent, 0 /* flags */);
+        if (resolveInfos.size() == 1) {
+            ResolveInfo resolveInfo = resolveInfos.get(0);
+            return new ComponentName(resolveInfo.serviceInfo.packageName,
+                    resolveInfo.serviceInfo.name);
+        } else if (resolveInfos.isEmpty()) {
+            return null;
+        } else {
+            throw new IllegalStateException("Expected 1 service that handles " + action + ", found "
+                    + resolveInfos.size());
+        }
+    }
 }
diff --git a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
index 13661ab..d3c403a 100644
--- a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -20,6 +20,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -28,7 +29,8 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.support.annotation.VisibleForTesting;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.app.SupportActivity;
 import android.support.v4.media.MediaDescriptionCompat;
@@ -44,6 +46,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -69,6 +72,12 @@
  * <li>{@link #getRepeatMode()}</li>
  * <li>{@link #isShuffleModeEnabled()}</li>
  * </ul></p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about building your media application, read the
+ * <a href="{@docRoot}guide/topics/media-apps/index.html">Media Apps</a> developer guide.</p>
+ * </div>
  */
 public final class MediaControllerCompat {
     static final String TAG = "MediaControllerCompat";
@@ -102,24 +111,24 @@
     }
 
     /**
-     * Sets a {@link MediaControllerCompat} for later retrieval via
-     * {@link #getMediaController()}.
+     * Sets a {@link MediaControllerCompat} in the {@code activity} for later retrieval via
+     * {@link #getMediaController(Activity)}.
      *
-     * <p>On API 21 and later, this controller will be tied to the window of the activity and
-     * media key and volume events which are received while the Activity is in the foreground
-     * will be forwarded to the controller and used to invoke transport controls or adjust the
-     * volume. Prior to API 21, the global handling of media key and volume events through an
-     * active {@link android.support.v4.media.session.MediaSessionCompat} and media button receiver
-     * will still be respected.</p>
+     * <p>This is compatible with {@link Activity#setMediaController(MediaController)}.
+     * If {@code activity} inherits {@link android.support.v4.app.FragmentActivity}, the
+     * {@code mediaController} will be saved in the {@code activity}. In addition to that,
+     * on API 21 and later, {@link Activity#setMediaController(MediaController)} will be
+     * called.</p>
      *
+     * @param activity The activity to set the {@code mediaController} in, must not be null.
      * @param mediaController The controller for the session which should receive
      *     media keys and volume changes on API 21 and later.
-     * @see #getMediaController()
+     * @see #getMediaController(Activity)
      * @see Activity#setMediaController(android.media.session.MediaController)
      */
-    public static void setMediaController(Activity activity,
+    public static void setMediaController(@NonNull Activity activity,
             MediaControllerCompat mediaController) {
-        if (activity instanceof  SupportActivity) {
+        if (activity instanceof SupportActivity) {
             ((SupportActivity) activity).putExtraData(
                     new MediaControllerExtraData(mediaController));
         }
@@ -134,13 +143,17 @@
     }
 
     /**
-     * Retrieves the current {@link MediaControllerCompat} for sending media key and volume events.
+     * Retrieves the {@link MediaControllerCompat} set in the activity by
+     * {@link #setMediaController(Activity, MediaControllerCompat)} for sending media key and volume
+     * events.
      *
+     * <p>This is compatible with {@link Activity#getMediaController()}.</p>
+     *
+     * @param activity The activity to get the media controller from, must not be null.
      * @return The controller which should receive events.
-     * @see #setMediaController(Activity,MediaControllerCompat)
-     * @see #getMediaController()
+     * @see #setMediaController(Activity, MediaControllerCompat)
      */
-    public static MediaControllerCompat getMediaController(Activity activity) {
+    public static MediaControllerCompat getMediaController(@NonNull Activity activity) {
         if (activity instanceof SupportActivity) {
             MediaControllerExtraData extraData =
                     ((SupportActivity) activity).getExtraData(MediaControllerExtraData.class);
@@ -161,15 +174,35 @@
         return null;
     }
 
+    private static void validateCustomAction(String action, Bundle args) {
+        if (action == null) {
+            return;
+        }
+        switch(action) {
+            case MediaSessionCompat.ACTION_FOLLOW:
+            case MediaSessionCompat.ACTION_UNFOLLOW:
+                if (args == null
+                        || !args.containsKey(MediaSessionCompat.ARGUMENT_MEDIA_ATTRIBUTE)) {
+                    throw new IllegalArgumentException("An extra field "
+                            + MediaSessionCompat.ARGUMENT_MEDIA_ATTRIBUTE + " is required "
+                            + "for this action " + action + ".");
+                }
+                break;
+        }
+    }
+
     private final MediaControllerImpl mImpl;
     private final MediaSessionCompat.Token mToken;
+    // This set is used to keep references to registered callbacks to prevent them being GCed,
+    // since we only keep weak references for callbacks in this class and its inner classes.
+    private final HashSet<Callback> mRegisteredCallbacks = new HashSet<>();
 
     /**
      * Creates a media controller from a session.
      *
      * @param session The session to be controlled.
      */
-    public MediaControllerCompat(Context context, MediaSessionCompat session) {
+    public MediaControllerCompat(Context context, @NonNull MediaSessionCompat session) {
         if (session == null) {
             throw new IllegalArgumentException("session must not be null");
         }
@@ -193,7 +226,7 @@
      * @param sessionToken The token of the session to be controlled.
      * @throws RemoteException if the session is not accessible.
      */
-    public MediaControllerCompat(Context context, MediaSessionCompat.Token sessionToken)
+    public MediaControllerCompat(Context context, @NonNull MediaSessionCompat.Token sessionToken)
             throws RemoteException {
         if (sessionToken == null) {
             throw new IllegalArgumentException("sessionToken must not be null");
@@ -212,7 +245,7 @@
     }
 
     /**
-     * Get a {@link TransportControls} instance for this session.
+     * Gets a {@link TransportControls} instance for this session.
      *
      * @return A controls instance
      */
@@ -221,7 +254,7 @@
     }
 
     /**
-     * Send the specified media button event to the session. Only media keys can
+     * Sends the specified media button event to the session. Only media keys can
      * be sent by this method, other keys will be ignored.
      *
      * @param keyEvent The media button event to dispatch.
@@ -235,7 +268,7 @@
     }
 
     /**
-     * Get the current playback state for this session.
+     * Gets the current playback state for this session.
      *
      * @return The current PlaybackState or null
      */
@@ -244,7 +277,7 @@
     }
 
     /**
-     * Get the current metadata for this session.
+     * Gets the current metadata for this session.
      *
      * @return The current MediaMetadata or null.
      */
@@ -253,17 +286,17 @@
     }
 
     /**
-     * Get the current play queue for this session if one is set. If you only
+     * Gets the current play queue for this session if one is set. If you only
      * care about the current item {@link #getMetadata()} should be used.
      *
      * @return The current play queue or null.
      */
-    public List<MediaSessionCompat.QueueItem> getQueue() {
+    public List<QueueItem> getQueue() {
         return mImpl.getQueue();
     }
 
     /**
-     * Add a queue item from the given {@code description} at the end of the play queue
+     * Adds a queue item from the given {@code description} at the end of the play queue
      * of this session. Not all sessions may support this. To know whether the session supports
      * this, get the session's flags with {@link #getFlags()} and check that the flag
      * {@link MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS} is set.
@@ -279,7 +312,7 @@
     }
 
     /**
-     * Add a queue item from the given {@code description} at the specified position
+     * Adds a queue item from the given {@code description} at the specified position
      * in the play queue of this session. Shifts the queue item currently at that position
      * (if any) and any subsequent queue items to the right (adds one to their indices).
      * Not all sessions may support this. To know whether the session supports this,
@@ -299,7 +332,7 @@
     }
 
     /**
-     * Remove the first occurrence of the specified {@link MediaSessionCompat.QueueItem}
+     * Removes the first occurrence of the specified {@link MediaSessionCompat.QueueItem}
      * with the given {@link MediaDescriptionCompat description} in the play queue of the
      * associated session. Not all sessions may support this. To know whether the session supports
      * this, get the session's flags with {@link #getFlags()} and check that the flag
@@ -316,7 +349,7 @@
     }
 
     /**
-     * Remove an queue item at the specified position in the play queue
+     * Removes an queue item at the specified position in the play queue
      * of this session. Not all sessions may support this. To know whether the session supports
      * this, get the session's flags with {@link #getFlags()} and check that the flag
      * {@link MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS} is set.
@@ -325,27 +358,35 @@
      * @throws UnsupportedOperationException If this session doesn't support this.
      * @see #getFlags()
      * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS
+     * @deprecated Use {@link #removeQueueItem(MediaDescriptionCompat)} instead.
      */
+    @Deprecated
     public void removeQueueItemAt(int index) {
-        mImpl.removeQueueItemAt(index);
+        List<QueueItem> queue = getQueue();
+        if (queue != null && index >= 0 && index < queue.size()) {
+            QueueItem item = queue.get(index);
+            if (item != null) {
+                removeQueueItem(item.getDescription());
+            }
+        }
     }
 
     /**
-     * Get the queue title for this session.
+     * Gets the queue title for this session.
      */
     public CharSequence getQueueTitle() {
         return mImpl.getQueueTitle();
     }
 
     /**
-     * Get the extras for this session.
+     * Gets the extras for this session.
      */
     public Bundle getExtras() {
         return mImpl.getExtras();
     }
 
     /**
-     * Get the rating type supported by the session. One of:
+     * Gets the rating type supported by the session. One of:
      * <ul>
      * <li>{@link RatingCompat#RATING_NONE}</li>
      * <li>{@link RatingCompat#RATING_HEART}</li>
@@ -363,7 +404,7 @@
     }
 
     /**
-     * Return whether captioning is enabled for this session.
+     * Returns whether captioning is enabled for this session.
      *
      * @return {@code true} if captioning is enabled, {@code false} if disabled or not set.
      */
@@ -372,7 +413,7 @@
     }
 
     /**
-     * Get the repeat mode for this session.
+     * Gets the repeat mode for this session.
      *
      * @return The latest repeat mode set to the session, or
      *         {@link PlaybackStateCompat#REPEAT_MODE_NONE} if not set.
@@ -382,16 +423,28 @@
     }
 
     /**
-     * Return whether the shuffle mode is enabled for this session.
+     * Returns whether the shuffle mode is enabled for this session.
      *
      * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set.
+     * @deprecated Use {@link #getShuffleMode} instead.
      */
+    @Deprecated
     public boolean isShuffleModeEnabled() {
         return mImpl.isShuffleModeEnabled();
     }
 
     /**
-     * Get the flags for this session. Flags are defined in
+     * Gets the shuffle mode for this session.
+     *
+     * @return The latest shuffle mode set to the session, or
+     *         {@link PlaybackStateCompat#SHUFFLE_MODE_NONE} if not set.
+     */
+    public int getShuffleMode() {
+        return mImpl.getShuffleMode();
+    }
+
+    /**
+     * Gets the flags for this session. Flags are defined in
      * {@link MediaSessionCompat}.
      *
      * @return The current set of flags for the session.
@@ -401,7 +454,7 @@
     }
 
     /**
-     * Get the current playback info for this session.
+     * Gets the current playback info for this session.
      *
      * @return The current playback info or null.
      */
@@ -410,7 +463,7 @@
     }
 
     /**
-     * Get an intent for launching UI associated with this session if one
+     * Gets an intent for launching UI associated with this session if one
      * exists.
      *
      * @return A {@link PendingIntent} to launch UI or null.
@@ -420,7 +473,7 @@
     }
 
     /**
-     * Get the token for the session this controller is connected to.
+     * Gets the token for the session this controller is connected to.
      *
      * @return The session's token.
      */
@@ -429,7 +482,7 @@
     }
 
     /**
-     * Set the volume of the output this session is playing on. The command will
+     * Sets the volume of the output this session is playing on. The command will
      * be ignored if it does not support
      * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in
      * {@link AudioManager} may be used to affect the handling.
@@ -444,7 +497,7 @@
     }
 
     /**
-     * Adjust the volume of the output this session is playing on. The direction
+     * Adjusts the volume of the output this session is playing on. The direction
      * must be one of {@link AudioManager#ADJUST_LOWER},
      * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
      * The command will be ignored if the session does not support
@@ -466,7 +519,7 @@
      *
      * @param callback The callback object, must not be null.
      */
-    public void registerCallback(Callback callback) {
+    public void registerCallback(@NonNull Callback callback) {
         registerCallback(callback, null);
     }
 
@@ -478,27 +531,34 @@
      * @param handler The handler to post updates on. If null the callers thread
      *            will be used.
      */
-    public void registerCallback(Callback callback, Handler handler) {
+    public void registerCallback(@NonNull Callback callback, Handler handler) {
         if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
+            throw new IllegalArgumentException("callback must not be null");
         }
         if (handler == null) {
             handler = new Handler();
         }
+        callback.setHandler(handler);
         mImpl.registerCallback(callback, handler);
+        mRegisteredCallbacks.add(callback);
     }
 
     /**
-     * Stop receiving updates on the specified callback. If an update has
+     * Stops receiving updates on the specified callback. If an update has
      * already been posted you may still receive it after calling this method.
      *
      * @param callback The callback to remove
      */
-    public void unregisterCallback(Callback callback) {
+    public void unregisterCallback(@NonNull Callback callback) {
         if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
+            throw new IllegalArgumentException("callback must not be null");
         }
-        mImpl.unregisterCallback(callback);
+        try {
+            mRegisteredCallbacks.remove(callback);
+            mImpl.unregisterCallback(callback);
+        } finally {
+            callback.setHandler(null);
+        }
     }
 
     /**
@@ -510,15 +570,15 @@
      * @param params Any parameters to include with the command
      * @param cb The callback to receive the result on
      */
-    public void sendCommand(String command, Bundle params, ResultReceiver cb) {
+    public void sendCommand(@NonNull String command, Bundle params, ResultReceiver cb) {
         if (TextUtils.isEmpty(command)) {
-            throw new IllegalArgumentException("command cannot be null or empty");
+            throw new IllegalArgumentException("command must neither be null nor empty");
         }
         mImpl.sendCommand(command, params, cb);
     }
 
     /**
-     * Get the session owner's package name.
+     * Gets the session owner's package name.
      *
      * @return The package name of of the session owner.
      */
@@ -549,13 +609,11 @@
         MessageHandler mHandler;
         boolean mHasExtraCallback;
 
-        boolean mRegistered = false;
-
         public Callback() {
             if (android.os.Build.VERSION.SDK_INT >= 21) {
-                mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21());
+                mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21(this));
             } else {
-                mCallbackObj = new StubCompat();
+                mCallbackObj = new StubCompat(this);
             }
         }
 
@@ -602,7 +660,7 @@
          *            include the currently playing item as well as previous and
          *            upcoming items if applicable.
          */
-        public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
+        public void onQueueChanged(List<QueueItem> queue) {
         }
 
         /**
@@ -616,7 +674,7 @@
         }
 
         /**
-         * Override to handle chagnes to the {@link MediaSessionCompat} extras.
+         * Override to handle changes to the {@link MediaSessionCompat} extras.
          *
          * @param extras The extras that can include other information
          *            associated with the {@link MediaSessionCompat}.
@@ -646,7 +704,8 @@
          * @param repeatMode The repeat mode. It should be one of followings:
          *                   {@link PlaybackStateCompat#REPEAT_MODE_NONE},
          *                   {@link PlaybackStateCompat#REPEAT_MODE_ONE},
-         *                   {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+         *                   {@link PlaybackStateCompat#REPEAT_MODE_ALL},
+         *                   {@link PlaybackStateCompat#REPEAT_MODE_GROUP}
          */
         public void onRepeatModeChanged(@PlaybackStateCompat.RepeatMode int repeatMode) {
         }
@@ -655,141 +714,244 @@
          * Override to handle changes to the shuffle mode.
          *
          * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise.
+         * @deprecated Use {@link #onShuffleModeChanged(int)} instead.
          */
+        @Deprecated
         public void onShuffleModeChanged(boolean enabled) {
         }
 
+        /**
+         * Override to handle changes to the shuffle mode.
+         *
+         * @param shuffleMode The shuffle mode. Must be one of the followings:
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_NONE},
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_ALL},
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_GROUP}
+         */
+        public void onShuffleModeChanged(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+        }
+
         @Override
         public void binderDied() {
             onSessionDestroyed();
         }
 
         /**
-         * Set the handler to use for pre 21 callbacks.
+         * Set the handler to use for callbacks.
          */
-        private void setHandler(Handler handler) {
-            mHandler = new MessageHandler(handler.getLooper());
+        void setHandler(Handler handler) {
+            if (handler == null) {
+                if (mHandler != null) {
+                    mHandler.mRegistered = false;
+                    mHandler.removeCallbacksAndMessages(null);
+                    mHandler = null;
+                }
+            } else {
+                mHandler = new MessageHandler(handler.getLooper());
+                mHandler.mRegistered = true;
+            }
         }
 
-        private class StubApi21 implements MediaControllerCompatApi21.Callback {
-            StubApi21() {
+        void postToHandler(int what, Object obj, Bundle data) {
+            if (mHandler != null) {
+                Message msg = mHandler.obtainMessage(what, obj);
+                msg.setData(data);
+                msg.sendToTarget();
+            }
+        }
+
+        private static class StubApi21 implements MediaControllerCompatApi21.Callback {
+            private final WeakReference<MediaControllerCompat.Callback> mCallback;
+
+            StubApi21(MediaControllerCompat.Callback callback) {
+                mCallback = new WeakReference<>(callback);
             }
 
             @Override
             public void onSessionDestroyed() {
-                Callback.this.onSessionDestroyed();
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.onSessionDestroyed();
+                }
             }
 
             @Override
             public void onSessionEvent(String event, Bundle extras) {
-                if (mHasExtraCallback && android.os.Build.VERSION.SDK_INT < 23) {
-                    // Ignore. ExtraCallback will handle this.
-                } else {
-                    Callback.this.onSessionEvent(event, extras);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    if (callback.mHasExtraCallback && android.os.Build.VERSION.SDK_INT < 23) {
+                        // Ignore. ExtraCallback will handle this.
+                    } else {
+                        callback.onSessionEvent(event, extras);
+                    }
                 }
             }
 
             @Override
             public void onPlaybackStateChanged(Object stateObj) {
-                if (mHasExtraCallback) {
-                    // Ignore. ExtraCallback will handle this.
-                } else {
-                    Callback.this.onPlaybackStateChanged(
-                            PlaybackStateCompat.fromPlaybackState(stateObj));
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    if (callback.mHasExtraCallback) {
+                        // Ignore. ExtraCallback will handle this.
+                    } else {
+                        callback.onPlaybackStateChanged(
+                                PlaybackStateCompat.fromPlaybackState(stateObj));
+                    }
                 }
             }
 
             @Override
             public void onMetadataChanged(Object metadataObj) {
-                Callback.this.onMetadataChanged(MediaMetadataCompat.fromMediaMetadata(metadataObj));
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.onMetadataChanged(MediaMetadataCompat.fromMediaMetadata(metadataObj));
+                }
             }
 
             @Override
             public void onQueueChanged(List<?> queue) {
-                Callback.this.onQueueChanged(QueueItem.fromQueueItemList(queue));
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.onQueueChanged(QueueItem.fromQueueItemList(queue));
+                }
             }
 
             @Override
             public void onQueueTitleChanged(CharSequence title) {
-                Callback.this.onQueueTitleChanged(title);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.onQueueTitleChanged(title);
+                }
             }
 
             @Override
             public void onExtrasChanged(Bundle extras) {
-                Callback.this.onExtrasChanged(extras);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.onExtrasChanged(extras);
+                }
             }
 
             @Override
             public void onAudioInfoChanged(
                     int type, int stream, int control, int max, int current) {
-                Callback.this.onAudioInfoChanged(
-                        new PlaybackInfo(type, stream, control, max, current));
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.onAudioInfoChanged(
+                            new PlaybackInfo(type, stream, control, max, current));
+                }
             }
         }
 
-        private class StubCompat extends IMediaControllerCallback.Stub {
+        private static class StubCompat extends IMediaControllerCallback.Stub {
+            private final WeakReference<MediaControllerCompat.Callback> mCallback;
 
-            StubCompat() {
+            StubCompat(MediaControllerCompat.Callback callback) {
+                mCallback = new WeakReference<>(callback);
             }
 
             @Override
             public void onEvent(String event, Bundle extras) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_EVENT, event, extras);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_EVENT, event, extras);
+                }
             }
 
             @Override
             public void onSessionDestroyed() throws RemoteException {
-                mHandler.post(MessageHandler.MSG_DESTROYED, null, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_DESTROYED, null, null);
+                }
             }
 
             @Override
             public void onPlaybackStateChanged(PlaybackStateCompat state) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state, null);
+                }
             }
 
             @Override
             public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_METADATA, metadata, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_METADATA, metadata, null);
+                }
             }
 
             @Override
             public void onQueueChanged(List<QueueItem> queue) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_QUEUE, queue, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_QUEUE, queue, null);
+                }
             }
 
             @Override
             public void onQueueTitleChanged(CharSequence title) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE, title, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_QUEUE_TITLE, title, null);
+                }
             }
 
             @Override
             public void onCaptioningEnabledChanged(boolean enabled) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_CAPTIONING_ENABLED, enabled, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(
+                            MessageHandler.MSG_UPDATE_CAPTIONING_ENABLED, enabled, null);
+                }
             }
 
             @Override
             public void onRepeatModeChanged(int repeatMode) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE, repeatMode, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_REPEAT_MODE, repeatMode, null);
+                }
             }
 
             @Override
-            public void onShuffleModeChanged(boolean enabled) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE, enabled, null);
+            public void onShuffleModeChangedDeprecated(boolean enabled) throws RemoteException {
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(
+                            MessageHandler.MSG_UPDATE_SHUFFLE_MODE_DEPRECATED, enabled, null);
+                }
+            }
+
+            @Override
+            public void onShuffleModeChanged(int shuffleMode) throws RemoteException {
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(
+                            MessageHandler.MSG_UPDATE_SHUFFLE_MODE, shuffleMode, null);
+                }
             }
 
             @Override
             public void onExtrasChanged(Bundle extras) throws RemoteException {
-                mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS, extras, null);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_EXTRAS, extras, null);
+                }
             }
 
             @Override
             public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
-                PlaybackInfo pi = null;
-                if (info != null) {
-                    pi = new PlaybackInfo(info.volumeType, info.audioStream, info.controlType,
-                            info.maxVolume, info.currentVolume);
+                MediaControllerCompat.Callback callback = mCallback.get();
+                if (callback != null) {
+                    PlaybackInfo pi = null;
+                    if (info != null) {
+                        pi = new PlaybackInfo(info.volumeType, info.audioStream, info.controlType,
+                                info.maxVolume, info.currentVolume);
+                    }
+                    callback.postToHandler(MessageHandler.MSG_UPDATE_VOLUME, pi, null);
                 }
-                mHandler.post(MessageHandler.MSG_UPDATE_VOLUME, pi, null);
             }
         }
 
@@ -803,10 +965,13 @@
             private static final int MSG_UPDATE_EXTRAS = 7;
             private static final int MSG_DESTROYED = 8;
             private static final int MSG_UPDATE_REPEAT_MODE = 9;
-            private static final int MSG_UPDATE_SHUFFLE_MODE = 10;
+            private static final int MSG_UPDATE_SHUFFLE_MODE_DEPRECATED = 10;
             private static final int MSG_UPDATE_CAPTIONING_ENABLED = 11;
+            private static final int MSG_UPDATE_SHUFFLE_MODE = 12;
 
-            public MessageHandler(Looper looper) {
+            boolean mRegistered = false;
+
+            MessageHandler(Looper looper) {
                 super(looper);
             }
 
@@ -826,7 +991,7 @@
                         onMetadataChanged((MediaMetadataCompat) msg.obj);
                         break;
                     case MSG_UPDATE_QUEUE:
-                        onQueueChanged((List<MediaSessionCompat.QueueItem>) msg.obj);
+                        onQueueChanged((List<QueueItem>) msg.obj);
                         break;
                     case MSG_UPDATE_QUEUE_TITLE:
                         onQueueTitleChanged((CharSequence) msg.obj);
@@ -837,9 +1002,12 @@
                     case MSG_UPDATE_REPEAT_MODE:
                         onRepeatModeChanged((int) msg.obj);
                         break;
-                    case MSG_UPDATE_SHUFFLE_MODE:
+                    case MSG_UPDATE_SHUFFLE_MODE_DEPRECATED:
                         onShuffleModeChanged((boolean) msg.obj);
                         break;
+                    case MSG_UPDATE_SHUFFLE_MODE:
+                        onShuffleModeChanged((int) msg.obj);
+                        break;
                     case MSG_UPDATE_EXTRAS:
                         onExtrasChanged((Bundle) msg.obj);
                         break;
@@ -851,12 +1019,6 @@
                         break;
                 }
             }
-
-            public void post(int what, Object obj, Bundle data) {
-                Message msg = obtainMessage(what, obj);
-                msg.setData(data);
-                msg.sendToTarget();
-            }
         }
     }
 
@@ -865,27 +1027,29 @@
      * to send media transport commands to the session.
      */
     public static abstract class TransportControls {
+        /**
+         * Used as an integer extra field in {@link #playFromMediaId(String, Bundle)} or
+         * {@link #prepareFromMediaId(String, Bundle)} to indicate the stream type to be used by the
+         * media player when playing or preparing the specified media id. See {@link AudioManager}
+         * for a list of stream types.
+         */
+        public static final String EXTRA_LEGACY_STREAM_TYPE =
+                "android.media.session.extra.LEGACY_STREAM_TYPE";
+
         TransportControls() {
         }
 
         /**
-         * Request that the player prepare its playback without audio focus. In other words, other
-         * session can continue to play during the preparation of this session. This method can be
-         * used to speed up the start of the playback. Once the preparation is done, the session
-         * will change its playback state to {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards,
-         * {@link #play} can be called to start playback. If the preparation is not needed,
-         * {@link #play} can be directly called without this method.
+         * Request that the player prepare for playback. This can decrease the time it takes to
+         * start playback when a play command is received. Preparation is not required. You can
+         * call {@link #play} without calling this method beforehand.
          */
         public abstract void prepare();
 
         /**
-         * Request that the player prepare playback for a specific media id. In other words, other
-         * session can continue to play during the preparation of this session. This method can be
-         * used to speed up the start of the playback. Once the preparation is
-         * done, the session will change its playback state to
-         * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to
-         * start playback. If the preparation is not needed, {@link #playFromMediaId} can
-         * be directly called without this method.
+         * Request that the player prepare playback for a specific media id. This can decrease the
+         * time it takes to start playback when a play command is received. Preparation is not
+         * required. You can call {@link #playFromMediaId} without calling this method beforehand.
          *
          * @param mediaId The id of the requested media.
          * @param extras Optional extras that can include extra information about the media item
@@ -894,14 +1058,10 @@
         public abstract void prepareFromMediaId(String mediaId, Bundle extras);
 
         /**
-         * Request that the player prepare playback for a specific search query.
-         * An empty or null query should be treated as a request to prepare any
-         * music. In other words, other session can continue to play during
-         * the preparation of this session. This method can be used to speed up the start of the
-         * playback. Once the preparation is done, the session will change its playback state to
-         * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to
-         * start playback. If the preparation is not needed, {@link #playFromSearch} can be directly
-         * called without this method.
+         * Request that the player prepare playback for a specific search query. This can decrease
+         * the time it takes to start playback when a play command is received. An empty or null
+         * query should be treated as a request to prepare any music. Preparation is not required.
+         * You can call {@link #playFromSearch} without calling this method beforehand.
          *
          * @param query The search query.
          * @param extras Optional extras that can include extra information
@@ -910,13 +1070,9 @@
         public abstract void prepareFromSearch(String query, Bundle extras);
 
         /**
-         * Request that the player prepare playback for a specific {@link Uri}.
-         * In other words, other session can continue to play during the preparation of this
-         * session. This method can be used to speed up the start of the playback.
-         * Once the preparation is done, the session will change its playback state to
-         * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to
-         * start playback. If the preparation is not needed, {@link #playFromUri} can be directly
-         * called without this method.
+         * Request that the player prepare playback for a specific {@link Uri}. This can decrease
+         * the time it takes to start playback when a play command is received. Preparation is not
+         * required. You can call {@link #playFromUri} without calling this method beforehand.
          *
          * @param uri The URI of the requested media.
          * @param extras Optional extras that can include extra information about the media item
@@ -959,7 +1115,7 @@
         public abstract void playFromUri(Uri uri, Bundle extras);
 
         /**
-         * Play an item with a specific id in the play queue. If you specify an
+         * Plays an item with a specific id in the play queue. If you specify an
          * id that is not in the play queue, the behavior is undefined.
          */
         public abstract void skipToQueueItem(long id);
@@ -977,69 +1133,96 @@
         public abstract void stop();
 
         /**
-         * Move to a new location in the media stream.
+         * Moves to a new location in the media stream.
          *
          * @param pos Position to move to, in milliseconds.
          */
         public abstract void seekTo(long pos);
 
         /**
-         * Start fast forwarding. If playback is already fast forwarding this
+         * Starts fast forwarding. If playback is already fast forwarding this
          * may increase the rate.
          */
         public abstract void fastForward();
 
         /**
-         * Skip to the next item.
+         * Skips to the next item.
          */
         public abstract void skipToNext();
 
         /**
-         * Start rewinding. If playback is already rewinding this may increase
+         * Starts rewinding. If playback is already rewinding this may increase
          * the rate.
          */
         public abstract void rewind();
 
         /**
-         * Skip to the previous item.
+         * Skips to the previous item.
          */
         public abstract void skipToPrevious();
 
         /**
-         * Rate the current content. This will cause the rating to be set for
-         * the current user. The Rating type must match the type returned by
-         * {@link #getRatingType()}.
+         * Rates the current content. This will cause the rating to be set for
+         * the current user. The rating type of the given {@link RatingCompat} must match the type
+         * returned by {@link #getRatingType()}.
          *
          * @param rating The rating to set for the current content
          */
         public abstract void setRating(RatingCompat rating);
 
         /**
-         * Enable/disable captioning for this session.
+         * Rates a media item. This will cause the rating to be set for
+         * the specific media item. The rating type of the given {@link RatingCompat} must match
+         * the type returned by {@link #getRatingType()}.
+         *
+         * @param rating The rating to set for the media item.
+         * @param extras Optional arguments that can include information about the media item
+         *               to be rated.
+         *
+         * @see MediaSessionCompat#ARGUMENT_MEDIA_ATTRIBUTE
+         * @see MediaSessionCompat#ARGUMENT_MEDIA_ATTRIBUTE_VALUE
+         */
+        public abstract void setRating(RatingCompat rating, Bundle extras);
+
+        /**
+         * Enables/disables captioning for this session.
          *
          * @param enabled {@code true} to enable captioning, {@code false} to disable.
          */
         public abstract void setCaptioningEnabled(boolean enabled);
 
         /**
-         * Set the repeat mode for this session.
+         * Sets the repeat mode for this session.
          *
          * @param repeatMode The repeat mode. Must be one of the followings:
          *                   {@link PlaybackStateCompat#REPEAT_MODE_NONE},
          *                   {@link PlaybackStateCompat#REPEAT_MODE_ONE},
-         *                   {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+         *                   {@link PlaybackStateCompat#REPEAT_MODE_ALL},
+         *                   {@link PlaybackStateCompat#REPEAT_MODE_GROUP}
          */
         public abstract void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode);
 
         /**
-         * Set the shuffle mode for this session.
+         * Sets the shuffle mode for this session.
          *
          * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
+         * @deprecated Use {@link #setShuffleMode} instead.
          */
+        @Deprecated
         public abstract void setShuffleModeEnabled(boolean enabled);
 
         /**
-         * Send a custom action for the {@link MediaSessionCompat} to perform.
+         * Sets the shuffle mode for this session.
+         *
+         * @param shuffleMode The shuffle mode. Must be one of the followings:
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_NONE},
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_ALL},
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_GROUP}
+         */
+        public abstract void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode);
+
+        /**
+         * Sends a custom action for the {@link MediaSessionCompat} to perform.
          *
          * @param customAction The action to perform.
          * @param args Optional arguments to supply to the
@@ -1049,13 +1232,15 @@
                 Bundle args);
 
         /**
-         * Send the id and args from a custom action for the
+         * Sends the id and args from a custom action for the
          * {@link MediaSessionCompat} to perform.
          *
          * @see #sendCustomAction(PlaybackStateCompat.CustomAction action,
          *      Bundle args)
          * @see MediaSessionCompat#ACTION_FLAG_AS_INAPPROPRIATE
          * @see MediaSessionCompat#ACTION_SKIP_AD
+         * @see MediaSessionCompat#ACTION_FOLLOW
+         * @see MediaSessionCompat#ACTION_UNFOLLOW
          * @param action The action identifier of the
          *            {@link PlaybackStateCompat.CustomAction} as specified by
          *            the {@link MediaSessionCompat}.
@@ -1094,7 +1279,7 @@
         }
 
         /**
-         * Get the type of volume handling, either local or remote. One of:
+         * Gets the type of volume handling, either local or remote. One of:
          * <ul>
          * <li>{@link PlaybackInfo#PLAYBACK_TYPE_LOCAL}</li>
          * <li>{@link PlaybackInfo#PLAYBACK_TYPE_REMOTE}</li>
@@ -1107,7 +1292,7 @@
         }
 
         /**
-         * Get the stream this is currently controlling volume on. When the volume
+         * Gets the stream this is currently controlling volume on. When the volume
          * type is {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} this value does not
          * have meaning and should be ignored.
          *
@@ -1119,7 +1304,7 @@
         }
 
         /**
-         * Get the type of volume control that can be used. One of:
+         * Gets the type of volume control that can be used. One of:
          * <ul>
          * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}</li>
          * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE}</li>
@@ -1134,7 +1319,7 @@
         }
 
         /**
-         * Get the maximum volume that may be set for this session.
+         * Gets the maximum volume that may be set for this session.
          *
          * @return The maximum allowed volume where this session is playing.
          */
@@ -1143,7 +1328,7 @@
         }
 
         /**
-         * Get the current volume for this session.
+         * Gets the current volume for this session.
          *
          * @return The current volume where this session is playing.
          */
@@ -1161,17 +1346,17 @@
         PlaybackStateCompat getPlaybackState();
         MediaMetadataCompat getMetadata();
 
-        List<MediaSessionCompat.QueueItem> getQueue();
+        List<QueueItem> getQueue();
         void addQueueItem(MediaDescriptionCompat description);
         void addQueueItem(MediaDescriptionCompat description, int index);
         void removeQueueItem(MediaDescriptionCompat description);
-        void removeQueueItemAt(int index);
         CharSequence getQueueTitle();
         Bundle getExtras();
         int getRatingType();
         boolean isCaptioningEnabled();
         int getRepeatMode();
         boolean isShuffleModeEnabled();
+        int getShuffleMode();
         long getFlags();
         PlaybackInfo getPlaybackInfo();
         PendingIntent getSessionActivity();
@@ -1200,8 +1385,6 @@
             try {
                 mBinder.asBinder().linkToDeath(callback, 0);
                 mBinder.registerCallbackListener((IMediaControllerCallback) callback.mCallbackObj);
-                callback.setHandler(handler);
-                callback.mRegistered = true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in registerCallback.", e);
                 callback.onSessionDestroyed();
@@ -1217,7 +1400,6 @@
                 mBinder.unregisterCallbackListener(
                         (IMediaControllerCallback) callback.mCallbackObj);
                 mBinder.asBinder().unlinkToDeath(callback, 0);
-                callback.mRegistered = false;
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in unregisterCallback.", e);
             }
@@ -1266,7 +1448,7 @@
         }
 
         @Override
-        public List<MediaSessionCompat.QueueItem> getQueue() {
+        public List<QueueItem> getQueue() {
             try {
                 return mBinder.getQueue();
             } catch (RemoteException e) {
@@ -1318,20 +1500,6 @@
         }
 
         @Override
-        public void removeQueueItemAt(int index) {
-            try {
-                long flags = mBinder.getFlags();
-                if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
-                    throw new UnsupportedOperationException(
-                            "This session doesn't support queue management operations");
-                }
-                mBinder.removeQueueItemAt(index);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Dead object in removeQueueItemAt.", e);
-            }
-        }
-
-        @Override
         public CharSequence getQueueTitle() {
             try {
                 return mBinder.getQueueTitle();
@@ -1384,7 +1552,7 @@
         @Override
         public boolean isShuffleModeEnabled() {
             try {
-                return mBinder.isShuffleModeEnabled();
+                return mBinder.isShuffleModeEnabledDeprecated();
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in isShuffleModeEnabled.", e);
             }
@@ -1392,6 +1560,16 @@
         }
 
         @Override
+        public int getShuffleMode() {
+            try {
+                return mBinder.getShuffleMode();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in getShuffleMode.", e);
+            }
+            return 0;
+        }
+
+        @Override
         public long getFlags() {
             try {
                 return mBinder.getFlags();
@@ -1629,6 +1807,15 @@
         }
 
         @Override
+        public void setRating(RatingCompat rating, Bundle extras) {
+            try {
+                mBinder.rateWithExtras(rating, extras);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setRating.", e);
+            }
+        }
+
+        @Override
         public void setCaptioningEnabled(boolean enabled) {
             try {
                 mBinder.setCaptioningEnabled(enabled);
@@ -1649,19 +1836,29 @@
         @Override
         public void setShuffleModeEnabled(boolean enabled) {
             try {
-                mBinder.setShuffleModeEnabled(enabled);
+                mBinder.setShuffleModeEnabledDeprecated(enabled);
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in setShuffleModeEnabled.", e);
             }
         }
 
         @Override
+        public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+            try {
+                mBinder.setShuffleMode(shuffleMode);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setShuffleMode.", e);
+            }
+        }
+
+        @Override
         public void sendCustomAction(CustomAction customAction, Bundle args) {
             sendCustomAction(customAction.getAction(), args);
         }
 
         @Override
         public void sendCustomAction(String action, Bundle args) {
+            validateCustomAction(action, args);
             try {
                 mBinder.sendCustomAction(action, args);
             } catch (RemoteException e) {
@@ -1670,14 +1867,16 @@
         }
     }
 
+    @RequiresApi(21)
     static class MediaControllerImplApi21 implements MediaControllerImpl {
         protected final Object mControllerObj;
 
+        private final List<Callback> mPendingCallbacks = new ArrayList<>();
+
         // Extra binder is used for applying the framework change of new APIs and bug fixes
         // after API 21.
         private IMediaSession mExtraBinder;
         private HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>();
-        private List<Callback> mPendingCallbacks = new ArrayList<>();
 
         public MediaControllerImplApi21(Context context, MediaSessionCompat session) {
             mControllerObj = MediaControllerCompatApi21.fromToken(context,
@@ -1704,7 +1903,6 @@
             MediaControllerCompatApi21.registerCallback(
                     mControllerObj, callback.mCallbackObj, handler);
             if (mExtraBinder != null) {
-                callback.setHandler(handler);
                 ExtraCallback extraCallback = new ExtraCallback(callback);
                 mCallbackMap.put(callback, extraCallback);
                 callback.mHasExtraCallback = true;
@@ -1714,7 +1912,6 @@
                     Log.e(TAG, "Dead object in registerCallback.", e);
                 }
             } else {
-                callback.setHandler(handler);
                 synchronized (mPendingCallbacks) {
                     mPendingCallbacks.add(callback);
                 }
@@ -1771,10 +1968,9 @@
         }
 
         @Override
-        public List<MediaSessionCompat.QueueItem> getQueue() {
+        public List<QueueItem> getQueue() {
             List<Object> queueObjs = MediaControllerCompatApi21.getQueue(mControllerObj);
-            return queueObjs != null ? MediaSessionCompat.QueueItem.fromQueueItemList(queueObjs)
-                    : null;
+            return queueObjs != null ? QueueItem.fromQueueItemList(queueObjs) : null;
         }
 
         @Override
@@ -1815,18 +2011,6 @@
         }
 
         @Override
-        public void removeQueueItemAt(int index) {
-            long flags = getFlags();
-            if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
-                throw new UnsupportedOperationException(
-                        "This session doesn't support queue management operations");
-            }
-            Bundle params = new Bundle();
-            params.putInt(COMMAND_ARGUMENT_INDEX, index);
-            sendCommand(COMMAND_REMOVE_QUEUE_ITEM_AT, params, null);
-        }
-
-        @Override
         public CharSequence getQueueTitle() {
             return MediaControllerCompatApi21.getQueueTitle(mControllerObj);
         }
@@ -1876,7 +2060,7 @@
         public boolean isShuffleModeEnabled() {
             if (mExtraBinder != null) {
                 try {
-                    return mExtraBinder.isShuffleModeEnabled();
+                    return mExtraBinder.isShuffleModeEnabledDeprecated();
                 } catch (RemoteException e) {
                     Log.e(TAG, "Dead object in isShuffleModeEnabled.", e);
                 }
@@ -1885,6 +2069,18 @@
         }
 
         @Override
+        public int getShuffleMode() {
+            if (mExtraBinder != null) {
+                try {
+                    return mExtraBinder.getShuffleMode();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Dead object in getShuffleMode.", e);
+                }
+            }
+            return PlaybackStateCompat.SHUFFLE_MODE_NONE;
+        }
+
+        @Override
         public long getFlags() {
             return MediaControllerCompatApi21.getFlags(mControllerObj);
         }
@@ -1976,21 +2172,9 @@
             }
         }
 
-        private class ExtraCallback extends IMediaControllerCallback.Stub {
-            private Callback mCallback;
-
+        private static class ExtraCallback extends Callback.StubCompat {
             ExtraCallback(Callback callback) {
-                mCallback = callback;
-            }
-
-            @Override
-            public void onEvent(final String event, final Bundle extras) throws RemoteException {
-                mCallback.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCallback.onSessionEvent(event, extras);
-                    }
-                });
+                super(callback);
             }
 
             @Override
@@ -2000,17 +2184,6 @@
             }
 
             @Override
-            public void onPlaybackStateChanged(final PlaybackStateCompat state)
-                    throws RemoteException {
-                mCallback.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCallback.onPlaybackStateChanged(state);
-                    }
-                });
-            }
-
-            @Override
             public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException {
                 // Will not be called.
                 throw new AssertionError();
@@ -2029,36 +2202,6 @@
             }
 
             @Override
-            public void onCaptioningEnabledChanged(final boolean enabled) throws RemoteException {
-                mCallback.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCallback.onCaptioningEnabledChanged(enabled);
-                    }
-                });
-            }
-
-            @Override
-            public void onRepeatModeChanged(final int repeatMode) throws RemoteException {
-                mCallback.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCallback.onRepeatModeChanged(repeatMode);
-                    }
-                });
-            }
-
-            @Override
-            public void onShuffleModeChanged(final boolean enabled) throws RemoteException {
-                mCallback.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCallback.onShuffleModeChanged(enabled);
-                    }
-                });
-            }
-
-            @Override
             public void onExtrasChanged(Bundle extras) throws RemoteException {
                 // Will not be called.
                 throw new AssertionError();
@@ -2155,6 +2298,14 @@
         }
 
         @Override
+        public void setRating(RatingCompat rating, Bundle extras) {
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_RATING, rating);
+            bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
+            sendCustomAction(MediaSessionCompat.ACTION_SET_RATING, bundle);
+        }
+
+        @Override
         public void setCaptioningEnabled(boolean enabled) {
             Bundle bundle = new Bundle();
             bundle.putBoolean(MediaSessionCompat.ACTION_ARGUMENT_CAPTIONING_ENABLED, enabled);
@@ -2176,6 +2327,13 @@
         }
 
         @Override
+        public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+            Bundle bundle = new Bundle();
+            bundle.putInt(MediaSessionCompat.ACTION_ARGUMENT_SHUFFLE_MODE, shuffleMode);
+            sendCustomAction(MediaSessionCompat.ACTION_SET_SHUFFLE_MODE, bundle);
+        }
+
+        @Override
         public void playFromMediaId(String mediaId, Bundle extras) {
             MediaControllerCompatApi21.TransportControls.playFromMediaId(mControlsObj, mediaId,
                     extras);
@@ -2206,17 +2364,20 @@
 
         @Override
         public void sendCustomAction(CustomAction customAction, Bundle args) {
+            validateCustomAction(customAction.getAction(), args);
             MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj,
                     customAction.getAction(), args);
         }
 
         @Override
         public void sendCustomAction(String action, Bundle args) {
+            validateCustomAction(action, args);
             MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj, action,
                     args);
         }
     }
 
+    @RequiresApi(23)
     static class MediaControllerImplApi23 extends MediaControllerImplApi21 {
 
         public MediaControllerImplApi23(Context context, MediaSessionCompat session) {
@@ -2235,6 +2396,7 @@
         }
     }
 
+    @RequiresApi(23)
     static class TransportControlsApi23 extends TransportControlsApi21 {
 
         public TransportControlsApi23(Object controlsObj) {
@@ -2248,6 +2410,7 @@
         }
     }
 
+    @RequiresApi(24)
     static class MediaControllerImplApi24 extends MediaControllerImplApi23 {
 
         public MediaControllerImplApi24(Context context, MediaSessionCompat session) {
@@ -2266,6 +2429,7 @@
         }
     }
 
+    @RequiresApi(24)
     static class TransportControlsApi24 extends TransportControlsApi23 {
 
         public TransportControlsApi24(Object controlsObj) {
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index eac0d6d..aaf0a62 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2014 The Android Open Source Project
  *
@@ -25,8 +24,14 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.media.MediaMetadataEditor;
+import android.media.MediaMetadataRetriever;
+import android.media.Rating;
+import android.media.RemoteControlClient;
 import android.net.Uri;
+import android.os.BadParcelableException;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -40,6 +45,7 @@
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.media.MediaDescriptionCompat;
@@ -50,6 +56,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.KeyEvent;
+import android.view.ViewConfiguration;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -82,6 +89,12 @@
  * This is a helper for accessing features in
  * {@link android.media.session.MediaSession} introduced after API level 4 in a
  * backwards compatible fashion.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about building your media application, read the
+ * <a href="{@docRoot}guide/topics/media-apps/index.html">Media Apps</a> developer guide.</p>
+ * </div>
  */
 public class MediaSessionCompat {
     static final String TAG = "MediaSessionCompat";
@@ -102,19 +115,19 @@
     public @interface SessionFlags {}
 
     /**
-     * Set this flag on the session to indicate that it can handle media button
+     * Sets this flag on the session to indicate that it can handle media button
      * events.
      */
     public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
 
     /**
-     * Set this flag on the session to indicate that it handles transport
+     * Sets this flag on the session to indicate that it handles transport
      * control commands through its {@link Callback}.
      */
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
     /**
-     * Set this flag on the session to indicate that it handles queue
+     * Sets this flag on the session to indicate that it handles queue
      * management commands through its {@link Callback}.
      */
     public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2;
@@ -132,8 +145,77 @@
      *
      * @see Callback#onCustomAction
      */
-    public static final String ACTION_SKIP_AD =
-            "android.support.v4.media.session.action.SKIP_AD";
+    public static final String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+
+    /**
+     * Predefined custom action to follow an artist, album, or playlist. The extra bundle must have
+     * {@link #ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the follow action. The
+     * bundle can also have an optional string argument,
+     * {@link #ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to follow (e.g., the
+     * name of the artist to follow). If this argument is omitted, the currently playing media will
+     * be the target of the action. Thus, the session must perform the follow action with the
+     * current metadata. If there's no specified attribute in the current metadata, the controller
+     * must not omit this argument.
+     *
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE_VALUE
+     * @see Callback#onCustomAction
+     */
+    public static final String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+
+    /**
+     * Predefined custom action to unfollow an artist, album, or playlist. The extra bundle must
+     * have {@link #ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the unfollow action.
+     * The bundle can also have an optional string argument,
+     * {@link #ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to unfollow (e.g., the
+     * name of the artist to unfollow). If this argument is omitted, the currently playing media
+     * will be the target of the action. Thus, the session must perform the unfollow action with the
+     * current metadata. If there's no specified attribute in the current metadata, the controller
+     * must not omit this argument.
+     *
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE_VALUE
+     * @see Callback#onCustomAction
+     */
+    public static final String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+
+    /**
+     * Argument to indicate the media attribute. It should be one of the following:
+     * <ul>
+     * <li>{@link #MEDIA_ATTRIBUTE_ARTIST}</li>
+     * <li>{@link #MEDIA_ATTRIBUTE_PLAYLIST}</li>
+     * <li>{@link #MEDIA_ATTRIBUTE_ALBUM}</li>
+     * </ul>
+     */
+    public static final String ARGUMENT_MEDIA_ATTRIBUTE =
+            "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+
+    /**
+     * String argument to indicate the value of the media attribute (e.g., the name of the artist).
+     */
+    public static final String ARGUMENT_MEDIA_ATTRIBUTE_VALUE =
+            "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+
+    /**
+     * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the artist.
+     *
+     * @see ARGUMENT_MEDIA_ATTRIBUTE
+     */
+    public static final int MEDIA_ATTRIBUTE_ARTIST = 0;
+
+    /**
+     * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the album.
+     *
+     * @see ARGUMENT_MEDIA_ATTRIBUTE
+     */
+    public static final int MEDIA_ATTRIBUTE_ALBUM = 1;
+
+    /**
+     * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the playlist.
+     *
+     * @see ARGUMENT_MEDIA_ATTRIBUTE
+     */
+    public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2;
 
     /**
      * Custom action to invoke playFromUri() for the forward compatibility.
@@ -183,6 +265,18 @@
             "android.support.v4.media.session.action.SET_SHUFFLE_MODE_ENABLED";
 
     /**
+     * Custom action to invoke setShuffleMode() for the forward compatibility.
+     */
+    static final String ACTION_SET_SHUFFLE_MODE =
+            "android.support.v4.media.session.action.SET_SHUFFLE_MODE";
+
+    /**
+     * Custom action to invoke setRating() with extra fields.
+     */
+    static final String ACTION_SET_RATING =
+            "android.support.v4.media.session.action.SET_RATING";
+
+    /**
      * Argument for use with {@link #ACTION_PREPARE_FROM_MEDIA_ID} indicating media id to play.
      */
     static final String ACTION_ARGUMENT_MEDIA_ID =
@@ -202,6 +296,12 @@
             "android.support.v4.media.session.action.ARGUMENT_URI";
 
     /**
+     * Argument for use with {@link #ACTION_SET_RATING} indicating the rate to be set.
+     */
+    static final String ACTION_ARGUMENT_RATING =
+            "android.support.v4.media.session.action.ARGUMENT_RATING";
+
+    /**
      * Argument for use with various actions indicating extra bundle.
      */
     static final String ACTION_ARGUMENT_EXTRAS =
@@ -227,6 +327,12 @@
     static final String ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED =
             "android.support.v4.media.session.action.ARGUMENT_SHUFFLE_MODE_ENABLED";
 
+    /**
+     * Argument for use with {@link #ACTION_SET_SHUFFLE_MODE} indicating shuffle mode.
+     */
+    static final String ACTION_ARGUMENT_SHUFFLE_MODE =
+            "android.support.v4.media.session.action.ARGUMENT_SHUFFLE_MODE";
+
     static final String EXTRA_BINDER = "android.support.v4.media.session.EXTRA_BINDER";
 
     // Maximum size of the bitmap in dp.
@@ -300,9 +406,13 @@
         }
         if (android.os.Build.VERSION.SDK_INT >= 21) {
             mImpl = new MediaSessionImplApi21(context, tag);
-            mImpl.setMediaButtonReceiver(mbrIntent);
             // Set default callback to respond to controllers' extra binder requests.
             setCallback(new Callback() {});
+            mImpl.setMediaButtonReceiver(mbrIntent);
+        } else if (android.os.Build.VERSION.SDK_INT >= 19) {
+            mImpl = new MediaSessionImplApi19(context, tag, mbrComponent, mbrIntent);
+        } else if (android.os.Build.VERSION.SDK_INT >= 18) {
+            mImpl = new MediaSessionImplApi18(context, tag, mbrComponent, mbrIntent);
         } else {
             mImpl = new MediaSessionImplBase(context, tag, mbrComponent, mbrIntent);
         }
@@ -316,7 +426,8 @@
 
     private MediaSessionCompat(Context context, MediaSessionImpl impl) {
         mImpl = impl;
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
+        if (android.os.Build.VERSION.SDK_INT >= 21
+                && !MediaSessionCompatApi21.hasCallback(impl.getMediaSession())) {
             // Set default callback to respond to controllers' extra binder requests.
             setCallback(new Callback() {});
         }
@@ -324,7 +435,7 @@
     }
 
     /**
-     * Add a callback to receive updates on for the MediaSession. This includes
+     * Adds a callback to receive updates on for the MediaSession. This includes
      * media button and volume events. The caller's thread will be used to post
      * events.
      *
@@ -335,7 +446,7 @@
     }
 
     /**
-     * Set the callback to receive updates for the MediaSession. This includes
+     * Sets the callback to receive updates for the MediaSession. This includes
      * media button and volume events. Set the callback to null to stop
      * receiving events.
      *
@@ -347,7 +458,7 @@
     }
 
     /**
-     * Set an intent for launching UI for this Session. This can be used as a
+     * Sets an intent for launching UI for this Session. This can be used as a
      * quick link to an ongoing media screen. The intent should be for an
      * activity that may be started using
      * {@link Activity#startActivity(Intent)}.
@@ -359,7 +470,7 @@
     }
 
     /**
-     * Set a pending intent for your media button receiver to allow restarting
+     * Sets a pending intent for your media button receiver to allow restarting
      * playback after the session has been stopped. If your app is started in
      * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
      * the pending intent.
@@ -376,7 +487,7 @@
     }
 
     /**
-     * Set any flags for the session.
+     * Sets any flags for the session.
      *
      * @param flags The flags to set for this session.
      */
@@ -385,7 +496,7 @@
     }
 
     /**
-     * Set the stream this session is playing on. This will affect the system's
+     * Sets the stream this session is playing on. This will affect the system's
      * volume handling for this session. If {@link #setPlaybackToRemote} was
      * previously called it will stop receiving volume commands and the system
      * will begin sending volume changes to the appropriate stream.
@@ -399,7 +510,7 @@
     }
 
     /**
-     * Configure this session to use remote volume handling. This must be called
+     * Configures this session to use remote volume handling. This must be called
      * to receive volume button events, otherwise the system will adjust the
      * current stream volume for this session. If {@link #setPlaybackToLocal}
      * was previously called that stream will stop receiving volume changes for
@@ -421,7 +532,7 @@
     }
 
     /**
-     * Set if this session is currently active and ready to receive commands. If
+     * Sets if this session is currently active and ready to receive commands. If
      * set to false your session's controller may not be discoverable. You must
      * set the session to active before it can start receiving media button
      * events or transport commands.
@@ -441,7 +552,7 @@
     }
 
     /**
-     * Get the current active state of this session.
+     * Gets the current active state of this session.
      *
      * @return True if the session is active, false otherwise.
      */
@@ -450,7 +561,7 @@
     }
 
     /**
-     * Send a proprietary event to all MediaControllers listening to this
+     * Sends a proprietary event to all MediaControllers listening to this
      * Session. It's up to the Controller/Session owner to determine the meaning
      * of any events.
      *
@@ -474,7 +585,7 @@
     }
 
     /**
-     * Retrieve a token object that can be used by apps to create a
+     * Retrieves a token object that can be used by apps to create a
      * {@link MediaControllerCompat} for interacting with this session. The
      * owner of the session is responsible for deciding how to distribute these
      * tokens.
@@ -492,7 +603,7 @@
     }
 
     /**
-     * Get a controller for this session. This is a convenience method to avoid
+     * Gets a controller for this session. This is a convenience method to avoid
      * having to cache your own controller in process.
      *
      * @return A controller for this session.
@@ -502,7 +613,7 @@
     }
 
     /**
-     * Update the current playback state.
+     * Updates the current playback state.
      *
      * @param state The current state of playback
      */
@@ -511,7 +622,7 @@
     }
 
     /**
-     * Update the current metadata. New metadata can be created using
+     * Updates the current metadata. New metadata can be created using
      * {@link android.support.v4.media.MediaMetadataCompat.Builder}. This operation may take time
      * proportional to the size of the bitmap to replace large bitmaps with a scaled down copy.
      *
@@ -523,7 +634,7 @@
     }
 
     /**
-     * Update the list of items in the play queue. It is an ordered list and
+     * Updates the list of items in the play queue. It is an ordered list and
      * should contain the current item, and previous or upcoming items if they
      * exist. Specify null if there is no current play queue.
      * <p>
@@ -538,7 +649,7 @@
     }
 
     /**
-     * Set the title of the play queue. The UI should display this title along
+     * Sets the title of the play queue. The UI should display this title along
      * with the play queue itself. e.g. "Play Queue", "Now Playing", or an album
      * name.
      *
@@ -549,7 +660,7 @@
     }
 
     /**
-     * Set the style of rating used by this session. Apps trying to set the
+     * Sets the style of rating used by this session. Apps trying to set the
      * rating should use this style. Must be one of the following:
      * <ul>
      * <li>{@link RatingCompat#RATING_NONE}</li>
@@ -566,7 +677,7 @@
     }
 
     /**
-     * Enable/disable captioning for this session.
+     * Enables/disables captioning for this session.
      *
      * @param enabled {@code true} to enable captioning, {@code false} to disable.
      */
@@ -575,7 +686,7 @@
     }
 
     /**
-     * Set the repeat mode for this session.
+     * Sets the repeat mode for this session.
      * <p>
      * Note that if this method is not called before, {@link MediaControllerCompat#getRepeatMode}
      * will return {@link PlaybackStateCompat#REPEAT_MODE_NONE}.
@@ -583,26 +694,44 @@
      * @param repeatMode The repeat mode. Must be one of the followings:
      *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
      *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
-     *            {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+     *            {@link PlaybackStateCompat#REPEAT_MODE_ALL},
+     *            {@link PlaybackStateCompat#REPEAT_MODE_GROUP}
      */
     public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
         mImpl.setRepeatMode(repeatMode);
     }
 
     /**
-     * Set the shuffle mode for this session.
+     * Sets the shuffle mode for this session.
      * <p>
      * Note that if this method is not called before,
      * {@link MediaControllerCompat#isShuffleModeEnabled} will return {@code false}.
      *
      * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
+     * @deprecated Use {@link #setShuffleMode} instead.
      */
+    @Deprecated
     public void setShuffleModeEnabled(boolean enabled) {
         mImpl.setShuffleModeEnabled(enabled);
     }
 
     /**
-     * Set some extras that can be associated with the
+     * Sets the shuffle mode for this session.
+     * <p>
+     * Note that if this method is not called before, {@link MediaControllerCompat#getShuffleMode}
+     * will return {@link PlaybackStateCompat#SHUFFLE_MODE_NONE}.
+     *
+     * @param shuffleMode The shuffle mode. Must be one of the followings:
+     *                    {@link PlaybackStateCompat#SHUFFLE_MODE_NONE},
+     *                    {@link PlaybackStateCompat#SHUFFLE_MODE_ALL},
+     *                    {@link PlaybackStateCompat#SHUFFLE_MODE_GROUP}
+     */
+    public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+        mImpl.setShuffleMode(shuffleMode);
+    }
+
+    /**
+     * Sets some extras that can be associated with the
      * {@link MediaSessionCompat}. No assumptions should be made as to how a
      * {@link MediaControllerCompat} will handle these extras. Keys should be
      * fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
@@ -689,28 +818,12 @@
      * @param context The context to use to create the session.
      * @param mediaSession A {@link android.media.session.MediaSession} object.
      * @return An equivalent {@link MediaSessionCompat} object, or null if none.
-     * @deprecated Use {@link #fromMediaSession(Context, Object)} instead.
-     */
-    @Deprecated
-    public static MediaSessionCompat obtain(Context context, Object mediaSession) {
-        return fromMediaSession(context, mediaSession);
-    }
-
-    /**
-     * Creates an instance from a framework {@link android.media.session.MediaSession} object.
-     * <p>
-     * This method is only supported on API 21+. On API 20 and below, it returns null.
-     * </p>
-     *
-     * @param context The context to use to create the session.
-     * @param mediaSession A {@link android.media.session.MediaSession} object.
-     * @return An equivalent {@link MediaSessionCompat} object, or null if none.
      */
     public static MediaSessionCompat fromMediaSession(Context context, Object mediaSession) {
-        if (context == null || mediaSession == null || Build.VERSION.SDK_INT < 21) {
-            return null;
+        if (context != null && mediaSession != null && Build.VERSION.SDK_INT >= 21) {
+            return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession));
         }
-        return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession));
+        return null;
     }
 
     private static PlaybackStateCompat getStateWithUpdatedPosition(
@@ -752,7 +865,9 @@
      */
     public abstract static class Callback {
         final Object mCallbackObj;
-        WeakReference<MediaSessionImpl> mSessionImpl;
+        private WeakReference<MediaSessionImpl> mSessionImpl;
+        private CallbackHandler mCallbackHandler = null;
+        private boolean mMediaPlayPauseKeyPending;
 
         public Callback() {
             if (android.os.Build.VERSION.SDK_INT >= 24) {
@@ -766,6 +881,14 @@
             }
         }
 
+        private void setSessionImpl(MediaSessionImpl impl, Handler handler) {
+            mSessionImpl = new WeakReference<MediaSessionImpl>(impl);
+            if (mCallbackHandler != null) {
+                mCallbackHandler.removeCallbacksAndMessages(null);
+            }
+            mCallbackHandler = new CallbackHandler(handler.getLooper());
+        }
+
         /**
          * Called when a controller has sent a custom command to this session.
          * The owner of the session may handle custom commands but is not
@@ -780,30 +903,92 @@
 
         /**
          * Override to handle media button events.
+         * <p>
+         * The double tap of {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} or {@link
+         * KeyEvent#KEYCODE_HEADSETHOOK} will call the {@link #onSkipToNext} by default.
          *
          * @param mediaButtonEvent The media button event intent.
          * @return True if the event was handled, false otherwise.
          */
         public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
+            MediaSessionImpl impl = mSessionImpl.get();
+            if (impl == null || mCallbackHandler == null) {
+                return false;
+            }
+            KeyEvent keyEvent = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+            if (keyEvent == null || keyEvent.getAction() != KeyEvent.ACTION_DOWN) {
+                return false;
+            }
+            int keyCode = keyEvent.getKeyCode();
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                    if (keyEvent.getRepeatCount() > 0) {
+                        // Consider long-press as a single tap.
+                        handleMediaPlayPauseKeySingleTapIfPending();
+                    } else if (mMediaPlayPauseKeyPending) {
+                        mCallbackHandler.removeMessages(
+                                CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+                        mMediaPlayPauseKeyPending = false;
+                        PlaybackStateCompat state = impl.getPlaybackState();
+                        long validActions = state == null ? 0 : state.getActions();
+                        // Consider double tap as the next.
+                        if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
+                            onSkipToNext();
+                        }
+                    } else {
+                        mMediaPlayPauseKeyPending = true;
+                        mCallbackHandler.sendEmptyMessageDelayed(
+                                CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
+                                ViewConfiguration.getDoubleTapTimeout());
+                    }
+                    return true;
+                default:
+                    // If another key is pressed within double tap timeout, consider the pending
+                    // pending play/pause as a single tap to handle media keys in order.
+                    handleMediaPlayPauseKeySingleTapIfPending();
+                    break;
+            }
             return false;
         }
 
+        private void handleMediaPlayPauseKeySingleTapIfPending() {
+            if (!mMediaPlayPauseKeyPending) {
+                return;
+            }
+            mMediaPlayPauseKeyPending = false;
+            mCallbackHandler.removeMessages(
+                    CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+            MediaSessionImpl impl = mSessionImpl.get();
+            if (impl == null) {
+                return;
+            }
+            PlaybackStateCompat state = impl.getPlaybackState();
+            long validActions = state == null ? 0 : state.getActions();
+            boolean isPlaying = state != null
+                    && state.getState() == PlaybackStateCompat.STATE_PLAYING;
+            boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
+                        | PlaybackStateCompat.ACTION_PLAY)) != 0;
+            boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
+                        | PlaybackStateCompat.ACTION_PAUSE)) != 0;
+            if (isPlaying && canPause) {
+                onPause();
+            } else if (!isPlaying && canPlay) {
+                onPlay();
+            }
+        }
+
         /**
-         * Override to handle requests to prepare playback. During the preparation, a session
-         * should not hold audio focus in order to allow other session play seamlessly.
-         * The state of playback should be updated to {@link PlaybackStateCompat#STATE_PAUSED}
-         * after the preparation is done.
+         * Override to handle requests to prepare playback. Override {@link #onPlay} to handle
+         * requests for starting playback.
          */
         public void onPrepare() {
         }
 
         /**
          * Override to handle requests to prepare for playing a specific mediaId that was provided
-         * by your app. During the preparation, a session should not hold audio focus in order to
-         * allow other session play seamlessly. The state of playback should be updated to
-         * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. The playback
-         * of the prepared content should start in the implementation of {@link #onPlay}. Override
-         * {@link #onPlayFromMediaId} to handle requests for starting playback without preparation.
+         * by your app. Override {@link #onPlayFromMediaId} to handle requests for starting
+         * playback.
          */
         public void onPrepareFromMediaId(String mediaId, Bundle extras) {
         }
@@ -811,24 +996,17 @@
         /**
          * Override to handle requests to prepare playback from a search query. An
          * empty query indicates that the app may prepare any music. The
-         * implementation should attempt to make a smart choice about what to
-         * play. During the preparation, a session should not hold audio focus in order to allow
-         * other session play seamlessly. The state of playback should be updated to
-         * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done.
-         * The playback of the prepared content should start in the implementation of
-         * {@link #onPlay}. Override {@link #onPlayFromSearch} to handle requests for
-         * starting playback without preparation.
+         * implementation should attempt to make a smart choice about what to play.
+         * Override {@link #onPlayFromSearch} to handle requests
+         * for starting playback.
          */
         public void onPrepareFromSearch(String query, Bundle extras) {
         }
 
         /**
          * Override to handle requests to prepare a specific media item represented by a URI.
-         * During the preparation, a session should not hold audio focus in order to allow other
-         * session play seamlessly. The state of playback should be updated to
-         * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. The playback of
-         * the prepared content should start in the implementation of {@link #onPlay}. Override
-         * {@link #onPlayFromUri} to handle requests for starting playback without preparation.
+         * Override {@link #onPlayFromUri} to handle requests
+         * for starting playback.
          */
         public void onPrepareFromUri(Uri uri, Bundle extras) {
         }
@@ -915,12 +1093,21 @@
         /**
          * Override to handle the item being rated.
          *
-         * @param rating
+         * @param rating The rating being set.
          */
         public void onSetRating(RatingCompat rating) {
         }
 
         /**
+         * Override to handle the item being rated.
+         *
+         * @param rating The rating being set.
+         * @param extras The extras can include information about the media item being rated.
+         */
+        public void onSetRating(RatingCompat rating, Bundle extras) {
+        }
+
+        /**
          * Override to handle requests to enable/disable captioning.
          *
          * @param enabled {@code true} to enable captioning, {@code false} to disable.
@@ -938,7 +1125,8 @@
          * @param repeatMode The repeat mode which is one of followings:
          *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
          *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
-         *            {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ALL},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_GROUP}
          */
         public void onSetRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
         }
@@ -951,11 +1139,28 @@
          * {@link MediaControllerCompat#isShuffleModeEnabled} could return an invalid value.
          *
          * @param enabled true when the shuffle mode is enabled, false otherwise.
+         * @deprecated Use {@link #onSetShuffleMode} instead.
          */
+        @Deprecated
         public void onSetShuffleModeEnabled(boolean enabled) {
         }
 
         /**
+         * Override to handle the setting of the shuffle mode.
+         * <p>
+         * You should call {@link #setShuffleMode} before the end of this method in order to
+         * notify the change to the {@link MediaControllerCompat}, or
+         * {@link MediaControllerCompat#getShuffleMode} could return an invalid value.
+         *
+         * @param shuffleMode The shuffle mode which is one of followings:
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_NONE},
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_ALL},
+         *                    {@link PlaybackStateCompat#SHUFFLE_MODE_GROUP}
+         */
+        public void onSetShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+        }
+
+        /**
          * Called when a {@link MediaControllerCompat} wants a
          * {@link PlaybackStateCompat.CustomAction} to be performed.
          *
@@ -965,6 +1170,8 @@
          *            {@link MediaControllerCompat}.
          * @see #ACTION_FLAG_AS_INAPPROPRIATE
          * @see #ACTION_SKIP_AD
+         * @see #ACTION_FOLLOW
+         * @see #ACTION_UNFOLLOW
          */
         public void onCustomAction(String action, Bundle extras) {
         }
@@ -1007,10 +1214,28 @@
          * specified position in the play queue.
          *
          * @param index The index of the element to be removed.
+         * @deprecated {@link #onRemoveQueueItem} will be called instead.
          */
+        @Deprecated
         public void onRemoveQueueItemAt(int index) {
         }
 
+        private class CallbackHandler extends Handler {
+            private static final int MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 1;
+
+            CallbackHandler(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT) {
+                    handleMediaPlayPauseKeySingleTapIfPending();
+                }
+            }
+        }
+
+        @RequiresApi(21)
         private class StubApi21 implements MediaSessionCompatApi21.Callback {
 
             StubApi21() {
@@ -1018,36 +1243,50 @@
 
             @Override
             public void onCommand(String command, Bundle extras, ResultReceiver cb) {
-                if (command.equals(MediaControllerCompat.COMMAND_GET_EXTRA_BINDER)) {
-                    MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
-                    if (impl != null) {
-                        Bundle result = new Bundle();
-                        IMediaSession extraBinder = impl.getSessionToken().getExtraBinder();
-                        BundleCompat.putBinder(result, EXTRA_BINDER,
-                                extraBinder == null ? null : extraBinder.asBinder());
-                        cb.send(0, result);
+                try {
+                    if (command.equals(MediaControllerCompat.COMMAND_GET_EXTRA_BINDER)) {
+                        MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
+                        if (impl != null) {
+                            Bundle result = new Bundle();
+                            IMediaSession extraBinder = impl.getSessionToken().getExtraBinder();
+                            BundleCompat.putBinder(result, EXTRA_BINDER,
+                                    extraBinder == null ? null : extraBinder.asBinder());
+                            cb.send(0, result);
+                        }
+                    } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) {
+                        extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
+                        Callback.this.onAddQueueItem(
+                                (MediaDescriptionCompat) extras.getParcelable(
+                                        MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
+                    } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM_AT)) {
+                        extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
+                        Callback.this.onAddQueueItem(
+                                (MediaDescriptionCompat) extras.getParcelable(
+                                        MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION),
+                                extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX));
+                    } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM)) {
+                        extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
+                        Callback.this.onRemoveQueueItem(
+                                (MediaDescriptionCompat) extras.getParcelable(
+                                        MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
+                    } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM_AT)) {
+                        MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
+                        if (impl != null && impl.mQueue != null) {
+                            int index =
+                                    extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX, -1);
+                            QueueItem item = (index >= 0 && index < impl.mQueue.size())
+                                    ? impl.mQueue.get(index) : null;
+                            if (item != null) {
+                                Callback.this.onRemoveQueueItem(item.getDescription());
+                            }
+                        }
+                    } else {
+                        Callback.this.onCommand(command, extras, cb);
                     }
-                } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) {
-                    extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
-                    Callback.this.onAddQueueItem(
-                            (MediaDescriptionCompat) extras.getParcelable(
-                                    MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
-                } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM_AT)) {
-                    extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
-                    Callback.this.onAddQueueItem(
-                            (MediaDescriptionCompat) extras.getParcelable(
-                                    MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION),
-                            extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX));
-                } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM)) {
-                    extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
-                    Callback.this.onRemoveQueueItem(
-                            (MediaDescriptionCompat) extras.getParcelable(
-                                    MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
-                } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM_AT)) {
-                    Callback.this.onRemoveQueueItemAt(
-                            extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX));
-                } else {
-                    Callback.this.onCommand(command, extras, cb);
+                } catch (BadParcelableException e) {
+                    // Do not print the exception here, since it is already done by the Parcel
+                    // class.
+                    Log.e(TAG, "Could not unparcel the extra data.");
                 }
             }
 
@@ -1117,6 +1356,11 @@
             }
 
             @Override
+            public void onSetRating(Object ratingObj, Bundle extras) {
+                Callback.this.onSetRating(RatingCompat.fromRating(ratingObj), extras);
+            }
+
+            @Override
             public void onCustomAction(String action, Bundle extras) {
                 if (action.equals(ACTION_PLAY_FROM_URI)) {
                     Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI);
@@ -1145,12 +1389,21 @@
                 } else if (action.equals(ACTION_SET_SHUFFLE_MODE_ENABLED)) {
                     boolean enabled = extras.getBoolean(ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED);
                     Callback.this.onSetShuffleModeEnabled(enabled);
+                } else if (action.equals(ACTION_SET_SHUFFLE_MODE)) {
+                    int shuffleMode = extras.getInt(ACTION_ARGUMENT_SHUFFLE_MODE);
+                    Callback.this.onSetShuffleMode(shuffleMode);
+                } else if (action.equals(ACTION_SET_RATING)) {
+                    extras.setClassLoader(RatingCompat.class.getClassLoader());
+                    RatingCompat rating = extras.getParcelable(ACTION_ARGUMENT_RATING);
+                    Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS);
+                    Callback.this.onSetRating(rating, bundle);
                 } else {
                     Callback.this.onCustomAction(action, extras);
                 }
             }
         }
 
+        @RequiresApi(23)
         private class StubApi23 extends StubApi21 implements MediaSessionCompatApi23.Callback {
 
             StubApi23() {
@@ -1162,6 +1415,7 @@
             }
         }
 
+        @RequiresApi(24)
         private class StubApi24 extends StubApi23 implements MediaSessionCompatApi24.Callback {
 
             StubApi24() {
@@ -1237,10 +1491,10 @@
          */
         @RestrictTo(LIBRARY_GROUP)
         public static Token fromToken(Object token, IMediaSession extraBinder) {
-            if (token == null || android.os.Build.VERSION.SDK_INT < 21) {
-                return null;
+            if (token != null && android.os.Build.VERSION.SDK_INT >= 21) {
+                return new Token(MediaSessionCompatApi21.verifyToken(token), extraBinder);
             }
-            return new Token(MediaSessionCompatApi21.verifyToken(token), extraBinder);
+            return null;
         }
 
         @Override
@@ -1307,21 +1561,21 @@
 
         public static final Parcelable.Creator<Token> CREATOR
                 = new Parcelable.Creator<Token>() {
-            @Override
-            public Token createFromParcel(Parcel in) {
-                Object inner;
-                if (android.os.Build.VERSION.SDK_INT >= 21) {
-                    inner = in.readParcelable(null);
-                } else {
-                    inner = in.readStrongBinder();
-                }
-                return new Token(inner);
-            }
+                    @Override
+                    public Token createFromParcel(Parcel in) {
+                        Object inner;
+                        if (android.os.Build.VERSION.SDK_INT >= 21) {
+                            inner = in.readParcelable(null);
+                        } else {
+                            inner = in.readStrongBinder();
+                        }
+                        return new Token(inner);
+                    }
 
-            @Override
-            public Token[] newArray(int size) {
-                return new Token[size];
-            }
+                    @Override
+                    public Token[] newArray(int size) {
+                        return new Token[size];
+                    }
         };
     }
 
@@ -1341,7 +1595,7 @@
         private Object mItem;
 
         /**
-         * Create a new {@link MediaSessionCompat.QueueItem}.
+         * Creates a new {@link MediaSessionCompat.QueueItem}.
          *
          * @param description The {@link MediaDescriptionCompat} for this item.
          * @param id An identifier for this item. It must be unique within the
@@ -1369,14 +1623,14 @@
         }
 
         /**
-         * Get the description for this item.
+         * Gets the description for this item.
          */
         public MediaDescriptionCompat getDescription() {
             return mDescription;
         }
 
         /**
-         * Get the queue id for this item.
+         * Gets the queue id for this item.
          */
         public long getQueueId() {
             return mId;
@@ -1394,7 +1648,7 @@
         }
 
         /**
-         * Get the underlying
+         * Gets the underlying
          * {@link android.media.session.MediaSession.QueueItem}.
          * <p>
          * On builds before {@link android.os.Build.VERSION_CODES#LOLLIPOP} null
@@ -1421,22 +1675,6 @@
          *
          * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object.
          * @return An equivalent {@link QueueItem} object, or null if none.
-         * @deprecated Use {@link #fromQueueItem(Object)} instead.
-         */
-        @Deprecated
-        public static QueueItem obtain(Object queueItem) {
-            return fromQueueItem(queueItem);
-        }
-
-        /**
-         * Creates an instance from a framework {@link android.media.session.MediaSession.QueueItem}
-         * object.
-         * <p>
-         * This method is only supported on API 21+. On API 20 and below, it returns null.
-         * </p>
-         *
-         * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object.
-         * @return An equivalent {@link QueueItem} object, or null if none.
          */
         public static QueueItem fromQueueItem(Object queueItem) {
             if (queueItem == null || Build.VERSION.SDK_INT < 21) {
@@ -1547,6 +1785,7 @@
         void release();
         Token getSessionToken();
         void setPlaybackState(PlaybackStateCompat state);
+        PlaybackStateCompat getPlaybackState();
         void setMetadata(MediaMetadataCompat metadata);
 
         void setSessionActivity(PendingIntent pi);
@@ -1559,6 +1798,7 @@
         void setCaptioningEnabled(boolean enabled);
         void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode);
         void setShuffleModeEnabled(boolean enabled);
+        void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode);
         void setExtras(Bundle extras);
 
         Object getMediaSession();
@@ -1569,15 +1809,18 @@
     }
 
     static class MediaSessionImplBase implements MediaSessionImpl {
+        /***** RemoteControlClient States, we only need none as the others were public *******/
+        static final int RCC_PLAYSTATE_NONE = 0;
+
         private final Context mContext;
         private final ComponentName mMediaButtonReceiverComponentName;
         private final PendingIntent mMediaButtonReceiverIntent;
-        private final Object mRccObj;
         private final MediaSessionStub mStub;
         private final Token mToken;
         final String mPackageName;
         final String mTag;
         final AudioManager mAudioManager;
+        final RemoteControlClient mRcc;
 
         final Object mLock = new Object();
         final RemoteCallbackList<IMediaControllerCallback> mControllerCallbacks
@@ -1585,9 +1828,9 @@
 
         private MessageHandler mHandler;
         boolean mDestroyed = false;
-        private boolean mIsActive = false;
-        private boolean mIsRccRegistered = false;
+        boolean mIsActive = false;
         private boolean mIsMbrRegistered = false;
+        private boolean mIsRccRegistered = false;
         volatile Callback mCallback;
 
         @SessionFlags int mFlags;
@@ -1600,6 +1843,7 @@
         @RatingCompat.Style int mRatingType;
         boolean mCaptioningEnabled;
         @PlaybackStateCompat.RepeatMode int mRepeatMode;
+        @PlaybackStateCompat.ShuffleMode int mShuffleMode;
         boolean mShuffleModeEnabled;
         Bundle mExtras;
 
@@ -1639,55 +1883,22 @@
             mRatingType = RatingCompat.RATING_NONE;
             mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
             mLocalStream = AudioManager.STREAM_MUSIC;
-            if (android.os.Build.VERSION.SDK_INT >= 14) {
-                mRccObj = MediaSessionCompatApi14.createRemoteControlClient(mbrIntent);
-            } else {
-                mRccObj = null;
-            }
+            mRcc = new RemoteControlClient(mbrIntent);
         }
 
         @Override
         public void setCallback(Callback callback, Handler handler) {
             mCallback = callback;
-            if (callback == null) {
-                // There's nothing to unregister on API < 18 since media buttons
-                // all go through the media button receiver
-                if (android.os.Build.VERSION.SDK_INT >= 18) {
-                    MediaSessionCompatApi18.setOnPlaybackPositionUpdateListener(mRccObj, null);
-                }
-                if (android.os.Build.VERSION.SDK_INT >= 19) {
-                    MediaSessionCompatApi19.setOnMetadataUpdateListener(mRccObj, null);
-                }
-            } else {
+            if (callback != null) {
                 if (handler == null) {
                     handler = new Handler();
                 }
                 synchronized (mLock) {
+                    if (mHandler != null) {
+                        mHandler.removeCallbacksAndMessages(null);
+                    }
                     mHandler = new MessageHandler(handler.getLooper());
-                }
-                MediaSessionCompatApi19.Callback cb19 = new MediaSessionCompatApi19.Callback() {
-                    @Override
-                    public void onSetRating(Object ratingObj) {
-                        postToHandler(MessageHandler.MSG_RATE,
-                                RatingCompat.fromRating(ratingObj));
-                    }
-
-                    @Override
-                    public void onSeekTo(long pos) {
-                        postToHandler(MessageHandler.MSG_SEEK_TO, pos);
-                    }
-                };
-                if (android.os.Build.VERSION.SDK_INT >= 18) {
-                    Object onPositionUpdateObj = MediaSessionCompatApi18
-                            .createPlaybackPositionUpdateListener(cb19);
-                    MediaSessionCompatApi18.setOnPlaybackPositionUpdateListener(mRccObj,
-                            onPositionUpdateObj);
-                }
-                if (android.os.Build.VERSION.SDK_INT >= 19) {
-                    Object onMetadataUpdateObj = MediaSessionCompatApi19
-                            .createMetadataUpdateListener(cb19);
-                    MediaSessionCompatApi19.setOnMetadataUpdateListener(mRccObj,
-                            onMetadataUpdateObj);
+                    mCallback.setSessionImpl(this, handler);
                 }
             }
         }
@@ -1805,35 +2016,91 @@
                 return;
             }
             if (state == null) {
-                if (android.os.Build.VERSION.SDK_INT >= 14) {
-                    MediaSessionCompatApi14.setState(mRccObj, PlaybackStateCompat.STATE_NONE);
-                    MediaSessionCompatApi14.setTransportControlFlags(mRccObj, 0);
-                }
+                mRcc.setPlaybackState(0);
+                mRcc.setTransportControlFlags(0);
             } else {
                 // Set state
-                if (android.os.Build.VERSION.SDK_INT >= 18) {
-                    MediaSessionCompatApi18.setState(mRccObj, state.getState(), state.getPosition(),
-                            state.getPlaybackSpeed(), state.getLastPositionUpdateTime());
-                } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-                    MediaSessionCompatApi14.setState(mRccObj, state.getState());
-                }
+                setRccState(state);
 
                 // Set transport control flags
-                if (android.os.Build.VERSION.SDK_INT >= 19) {
-                    MediaSessionCompatApi19.setTransportControlFlags(mRccObj, state.getActions());
-                } else if (android.os.Build.VERSION.SDK_INT >= 18) {
-                    MediaSessionCompatApi18.setTransportControlFlags(mRccObj, state.getActions());
-                } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-                    MediaSessionCompatApi14.setTransportControlFlags(mRccObj, state.getActions());
-                }
+                mRcc.setTransportControlFlags(
+                        getRccTransportControlFlagsFromActions(state.getActions()));
             }
         }
 
         @Override
+        public PlaybackStateCompat getPlaybackState() {
+            synchronized (mLock) {
+                return mState;
+            }
+        }
+
+        void setRccState(PlaybackStateCompat state) {
+            mRcc.setPlaybackState(getRccStateFromState(state.getState()));
+        }
+
+        int getRccStateFromState(int state) {
+            switch (state) {
+                case PlaybackStateCompat.STATE_CONNECTING:
+                case PlaybackStateCompat.STATE_BUFFERING:
+                    return RemoteControlClient.PLAYSTATE_BUFFERING;
+                case PlaybackStateCompat.STATE_ERROR:
+                    return RemoteControlClient.PLAYSTATE_ERROR;
+                case PlaybackStateCompat.STATE_FAST_FORWARDING:
+                    return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
+                case PlaybackStateCompat.STATE_NONE:
+                    return RCC_PLAYSTATE_NONE;
+                case PlaybackStateCompat.STATE_PAUSED:
+                    return RemoteControlClient.PLAYSTATE_PAUSED;
+                case PlaybackStateCompat.STATE_PLAYING:
+                    return RemoteControlClient.PLAYSTATE_PLAYING;
+                case PlaybackStateCompat.STATE_REWINDING:
+                    return RemoteControlClient.PLAYSTATE_REWINDING;
+                case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS:
+                    return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
+                case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT:
+                case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM:
+                    return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
+                case PlaybackStateCompat.STATE_STOPPED:
+                    return RemoteControlClient.PLAYSTATE_STOPPED;
+                default:
+                    return -1;
+            }
+        }
+
+        int getRccTransportControlFlagsFromActions(long actions) {
+            int transportControlFlags = 0;
+            if ((actions & PlaybackStateCompat.ACTION_STOP) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_PLAY) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
+            }
+            return transportControlFlags;
+        }
+
+        @Override
         public void setMetadata(MediaMetadataCompat metadata) {
             if (metadata != null) {
-                // Clones the given {@link MediaMetadataCompat}, deep-copying bitmaps in the
-                // metadata if necessary. Bitmaps can be scaled down if they are large.
+                // Clones {@link MediaMetadataCompat} and scales down bitmaps if they are large.
                 metadata = new MediaMetadataCompat.Builder(metadata, sMaxBitmapSize).build();
             }
 
@@ -1845,14 +2112,85 @@
                 // Don't set metadata until after the rcc has been registered
                 return;
             }
-            if (android.os.Build.VERSION.SDK_INT >= 19) {
-                MediaSessionCompatApi19.setMetadata(mRccObj,
-                        metadata == null ? null : metadata.getBundle(),
-                        mState == null ? 0 : mState.getActions());
-            } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-                MediaSessionCompatApi14.setMetadata(mRccObj,
-                        metadata == null ? null : metadata.getBundle());
+            RemoteControlClient.MetadataEditor editor = buildRccMetadata(
+                    metadata == null ? null : metadata.getBundle());
+            editor.apply();
+        }
+
+        RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) {
+            RemoteControlClient.MetadataEditor editor = mRcc.editMetadata(true);
+            if (metadata == null) {
+                return editor;
             }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)) {
+                Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ART);
+                if (art != null) {
+                    // Clone the bitmap to prevent it from being recycled by RCC.
+                    art = art.copy(art.getConfig(), false);
+                }
+                editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
+            } else if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) {
+                // Fall back to album art if the track art wasn't available
+                Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
+                if (art != null) {
+                    // Clone the bitmap to prevent it from being recycled by RCC.
+                    art = art.copy(art.getConfig(), false);
+                }
+                editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_AUTHOR)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_AUTHOR));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPILATION)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPILATION));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPOSER)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPOSER));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DATE)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_DATE));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_GENRE)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_WRITER)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_WRITER));
+            }
+            return editor;
         }
 
         @Override
@@ -1886,7 +2224,7 @@
 
         @Override
         public Object getRemoteControlClient() {
-            return mRccObj;
+            return null;
         }
 
         @Override
@@ -1924,82 +2262,75 @@
         }
 
         @Override
+        public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+            if (mShuffleMode != shuffleMode) {
+                mShuffleMode = shuffleMode;
+                sendShuffleMode(shuffleMode);
+            }
+        }
+
+        @Override
         public void setExtras(Bundle extras) {
             mExtras = extras;
             sendExtras(extras);
         }
 
-        // Registers/unregisters the RCC and MediaButtonEventReceiver as needed.
-        private boolean update() {
+        // Registers/unregisters components as needed.
+        boolean update() {
             boolean registeredRcc = false;
             if (mIsActive) {
-                // Register a MBR if it's supported, unregister it
-                // if support was removed.
+                // Register a MBR if it's supported, unregister it if support was removed.
                 if (!mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) != 0) {
-                    if (android.os.Build.VERSION.SDK_INT >= 18) {
-                        MediaSessionCompatApi18.registerMediaButtonEventReceiver(mContext,
-                                mMediaButtonReceiverIntent,
-                                mMediaButtonReceiverComponentName);
-                    } else {
-                        AudioManager am = (AudioManager) mContext.getSystemService(
-                                Context.AUDIO_SERVICE);
-                        am.registerMediaButtonEventReceiver(mMediaButtonReceiverComponentName);
-                    }
+                    registerMediaButtonEventReceiver(mMediaButtonReceiverIntent,
+                            mMediaButtonReceiverComponentName);
                     mIsMbrRegistered = true;
                 } else if (mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) == 0) {
-                    if (android.os.Build.VERSION.SDK_INT >= 18) {
-                        MediaSessionCompatApi18.unregisterMediaButtonEventReceiver(mContext,
-                                mMediaButtonReceiverIntent,
-                                mMediaButtonReceiverComponentName);
-                    } else {
-                        AudioManager am = (AudioManager) mContext.getSystemService(
-                                Context.AUDIO_SERVICE);
-                        am.unregisterMediaButtonEventReceiver(mMediaButtonReceiverComponentName);
-                    }
+                    unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent,
+                            mMediaButtonReceiverComponentName);
                     mIsMbrRegistered = false;
                 }
-                // On API 14+ register a RCC if it's supported, unregister it if
-                // not.
-                if (android.os.Build.VERSION.SDK_INT >= 14) {
-                    if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
-                        MediaSessionCompatApi14.registerRemoteControlClient(mContext, mRccObj);
-                        mIsRccRegistered = true;
-                        registeredRcc = true;
-                    } else if (mIsRccRegistered
-                            && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) {
-                        // RCC keeps the state while the system resets its state internally when
-                        // we register RCC. Reset the state so that the states in RCC and the system
-                        // are in sync when we re-register the RCC.
-                        MediaSessionCompatApi14.setState(mRccObj, PlaybackStateCompat.STATE_NONE);
-                        MediaSessionCompatApi14.unregisterRemoteControlClient(mContext, mRccObj);
-                        mIsRccRegistered = false;
-                    }
+                // Register a RCC if it's supported, unregister it if support was removed.
+                if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
+                    mAudioManager.registerRemoteControlClient(mRcc);
+                    mIsRccRegistered = true;
+                    registeredRcc = true;
+                } else if (mIsRccRegistered
+                        && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) {
+                    // RCC keeps the state while the system resets its state internally when
+                    // we register RCC. Reset the state so that the states in RCC and the system
+                    // are in sync when we re-register the RCC.
+                    mRcc.setPlaybackState(0);
+                    mAudioManager.unregisterRemoteControlClient(mRcc);
+                    mIsRccRegistered = false;
                 }
             } else {
                 // When inactive remove any registered components.
                 if (mIsMbrRegistered) {
-                    if (android.os.Build.VERSION.SDK_INT >= 18) {
-                        MediaSessionCompatApi18.unregisterMediaButtonEventReceiver(mContext,
-                                mMediaButtonReceiverIntent, mMediaButtonReceiverComponentName);
-                    } else {
-                        AudioManager am = (AudioManager) mContext.getSystemService(
-                                Context.AUDIO_SERVICE);
-                        am.unregisterMediaButtonEventReceiver(mMediaButtonReceiverComponentName);
-                    }
+                    unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent,
+                            mMediaButtonReceiverComponentName);
                     mIsMbrRegistered = false;
                 }
                 if (mIsRccRegistered) {
                     // RCC keeps the state while the system resets its state internally when
                     // we register RCC. Reset the state so that the states in RCC and the system
                     // are in sync when we re-register the RCC.
-                    MediaSessionCompatApi14.setState(mRccObj, PlaybackStateCompat.STATE_NONE);
-                    MediaSessionCompatApi14.unregisterRemoteControlClient(mContext, mRccObj);
+                    mRcc.setPlaybackState(0);
+                    mAudioManager.unregisterRemoteControlClient(mRcc);
                     mIsRccRegistered = false;
                 }
             }
             return registeredRcc;
         }
 
+        void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) {
+            mAudioManager.registerMediaButtonEventReceiver(mbrComponent);
+        }
+
+        void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent,
+                ComponentName mbrComponent) {
+            mAudioManager.unregisterMediaButtonEventReceiver(mbrComponent);
+        }
+
         void adjustVolume(int direction, int flags) {
             if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
                 if (mVolumeProvider != null) {
@@ -2134,7 +2465,19 @@
             for (int i = size - 1; i >= 0; i--) {
                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
                 try {
-                    cb.onShuffleModeChanged(enabled);
+                    cb.onShuffleModeChangedDeprecated(enabled);
+                } catch (RemoteException e) {
+                }
+            }
+            mControllerCallbacks.finishBroadcast();
+        }
+
+        private void sendShuffleMode(int shuffleMode) {
+            int size = mControllerCallbacks.beginBroadcast();
+            for (int i = size - 1; i >= 0; i--) {
+                IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
+                try {
+                    cb.onShuffleModeChanged(shuffleMode);
                 } catch (RemoteException e) {
                 }
             }
@@ -2337,6 +2680,11 @@
             }
 
             @Override
+            public void rateWithExtras(RatingCompat rating, Bundle extras) throws RemoteException {
+                postToHandler(MessageHandler.MSG_RATE_EXTRA, rating, extras);
+            }
+
+            @Override
             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
                 postToHandler(MessageHandler.MSG_SET_CAPTIONING_ENABLED, enabled);
             }
@@ -2347,11 +2695,16 @@
             }
 
             @Override
-            public void setShuffleModeEnabled(boolean enabled) throws RemoteException {
+            public void setShuffleModeEnabledDeprecated(boolean enabled) throws RemoteException {
                 postToHandler(MessageHandler.MSG_SET_SHUFFLE_MODE_ENABLED, enabled);
             }
 
             @Override
+            public void setShuffleMode(int shuffleMode) throws RemoteException {
+                postToHandler(MessageHandler.MSG_SET_SHUFFLE_MODE, shuffleMode);
+            }
+
+            @Override
             public void sendCustomAction(String action, Bundle args)
                     throws RemoteException {
                 postToHandler(MessageHandler.MSG_CUSTOM_ACTION, action, args);
@@ -2430,11 +2783,17 @@
             }
 
             @Override
-            public boolean isShuffleModeEnabled() {
+            public boolean isShuffleModeEnabledDeprecated() {
                 return mShuffleModeEnabled;
             }
 
             @Override
+            @PlaybackStateCompat.ShuffleMode
+            public int getShuffleMode() {
+                return mShuffleMode;
+            }
+
+            @Override
             public boolean isTransportControlEnabled() {
                 return (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0;
             }
@@ -2452,7 +2811,7 @@
             }
         }
 
-        private class MessageHandler extends Handler {
+        class MessageHandler extends Handler {
 
             private static final int MSG_COMMAND = 1;
             private static final int MSG_ADJUST_VOLUME = 2;
@@ -2473,6 +2832,7 @@
             private static final int MSG_REWIND = 17;
             private static final int MSG_SEEK_TO = 18;
             private static final int MSG_RATE = 19;
+            private static final int MSG_RATE_EXTRA = 31;
             private static final int MSG_CUSTOM_ACTION = 20;
             private static final int MSG_MEDIA_BUTTON = 21;
             private static final int MSG_SET_VOLUME = 22;
@@ -2483,6 +2843,7 @@
             private static final int MSG_REMOVE_QUEUE_ITEM = 27;
             private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28;
             private static final int MSG_SET_CAPTIONING_ENABLED = 29;
+            private static final int MSG_SET_SHUFFLE_MODE = 30;
 
             // KeyEvent constants only available on API 11+
             private static final int KEYCODE_MEDIA_PAUSE = 127;
@@ -2581,6 +2942,9 @@
                     case MSG_RATE:
                         cb.onSetRating((RatingCompat) msg.obj);
                         break;
+                    case MSG_RATE_EXTRA:
+                        cb.onSetRating((RatingCompat) msg.obj, msg.getData());
+                        break;
                     case MSG_CUSTOM_ACTION:
                         cb.onCustomAction((String) msg.obj, msg.getData());
                         break;
@@ -2594,7 +2958,13 @@
                         cb.onRemoveQueueItem((MediaDescriptionCompat) msg.obj);
                         break;
                     case MSG_REMOVE_QUEUE_ITEM_AT:
-                        cb.onRemoveQueueItemAt(msg.arg1);
+                        if (mQueue != null) {
+                            QueueItem item = (msg.arg1 >= 0 && msg.arg1 < mQueue.size())
+                                    ? mQueue.get(msg.arg1) : null;
+                            if (item != null) {
+                                cb.onRemoveQueueItem(item.getDescription());
+                            }
+                        }
                         break;
                     case MSG_ADJUST_VOLUME:
                         adjustVolume(msg.arg1, 0);
@@ -2611,6 +2981,9 @@
                     case MSG_SET_SHUFFLE_MODE_ENABLED:
                         cb.onSetShuffleModeEnabled((boolean) msg.obj);
                         break;
+                    case MSG_SET_SHUFFLE_MODE:
+                        cb.onSetShuffleMode(msg.arg1);
+                        break;
                 }
             }
 
@@ -2659,23 +3032,166 @@
                         break;
                     case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                     case KeyEvent.KEYCODE_HEADSETHOOK:
-                        boolean isPlaying = mState != null
-                                && mState.getState() == PlaybackStateCompat.STATE_PLAYING;
-                        boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
-                                | PlaybackStateCompat.ACTION_PLAY)) != 0;
-                        boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
-                                | PlaybackStateCompat.ACTION_PAUSE)) != 0;
-                        if (isPlaying && canPause) {
-                            cb.onPause();
-                        } else if (!isPlaying && canPlay) {
-                            cb.onPlay();
-                        }
+                        Log.w(TAG, "KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_HEADSETHOOK are handled"
+                                + " already");
                         break;
                 }
             }
         }
     }
 
+    @RequiresApi(18)
+    static class MediaSessionImplApi18 extends MediaSessionImplBase {
+        private static boolean sIsMbrPendingIntentSupported = true;
+
+        MediaSessionImplApi18(Context context, String tag, ComponentName mbrComponent,
+                PendingIntent mbrIntent) {
+            super(context, tag, mbrComponent, mbrIntent);
+        }
+
+        @Override
+        public void setCallback(Callback callback, Handler handler) {
+            super.setCallback(callback, handler);
+            if (callback == null) {
+                mRcc.setPlaybackPositionUpdateListener(null);
+            } else {
+                RemoteControlClient.OnPlaybackPositionUpdateListener listener =
+                        new RemoteControlClient.OnPlaybackPositionUpdateListener() {
+                            @Override
+                            public void onPlaybackPositionUpdate(long newPositionMs) {
+                                postToHandler(MessageHandler.MSG_SEEK_TO, newPositionMs);
+                            }
+                        };
+                mRcc.setPlaybackPositionUpdateListener(listener);
+            }
+        }
+
+        @Override
+        void setRccState(PlaybackStateCompat state) {
+            long position = state.getPosition();
+            float speed = state.getPlaybackSpeed();
+            long updateTime = state.getLastPositionUpdateTime();
+            long currTime = SystemClock.elapsedRealtime();
+            if (state.getState() == PlaybackStateCompat.STATE_PLAYING && position > 0) {
+                long diff = 0;
+                if (updateTime > 0) {
+                    diff = currTime - updateTime;
+                    if (speed > 0 && speed != 1f) {
+                        diff = (long) (diff * speed);
+                    }
+                }
+                position += diff;
+            }
+            mRcc.setPlaybackState(getRccStateFromState(state.getState()), position, speed);
+        }
+
+        @Override
+        int getRccTransportControlFlagsFromActions(long actions) {
+            int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions);
+            if ((actions & PlaybackStateCompat.ACTION_SEEK_TO) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
+            }
+            return transportControlFlags;
+        }
+
+        @Override
+        void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) {
+            // Some Android implementations are not able to register a media button event receiver
+            // using a PendingIntent but need a ComponentName instead. These will raise a
+            // NullPointerException.
+            if (sIsMbrPendingIntentSupported) {
+                try {
+                    mAudioManager.registerMediaButtonEventReceiver(mbrIntent);
+                } catch (NullPointerException e) {
+                    Log.w(TAG, "Unable to register media button event receiver with "
+                            + "PendingIntent, falling back to ComponentName.");
+                    sIsMbrPendingIntentSupported = false;
+                }
+            }
+
+            if (!sIsMbrPendingIntentSupported) {
+                super.registerMediaButtonEventReceiver(mbrIntent, mbrComponent);
+            }
+        }
+
+        @Override
+        void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent,
+                ComponentName mbrComponent) {
+            if (sIsMbrPendingIntentSupported) {
+                mAudioManager.unregisterMediaButtonEventReceiver(mbrIntent);
+            } else {
+                super.unregisterMediaButtonEventReceiver(mbrIntent, mbrComponent);
+            }
+        }
+    }
+
+    @RequiresApi(19)
+    static class MediaSessionImplApi19 extends MediaSessionImplApi18 {
+        MediaSessionImplApi19(Context context, String tag, ComponentName mbrComponent,
+                PendingIntent mbrIntent) {
+            super(context, tag, mbrComponent, mbrIntent);
+        }
+
+        @Override
+        public void setCallback(Callback callback, Handler handler) {
+            super.setCallback(callback, handler);
+            if (callback == null) {
+                mRcc.setMetadataUpdateListener(null);
+            } else {
+                RemoteControlClient.OnMetadataUpdateListener listener =
+                        new RemoteControlClient.OnMetadataUpdateListener() {
+                            @Override
+                            public void onMetadataUpdate(int key, Object newValue) {
+                                if (key == MediaMetadataEditor.RATING_KEY_BY_USER
+                                        && newValue instanceof Rating) {
+                                    postToHandler(MessageHandler.MSG_RATE,
+                                            RatingCompat.fromRating(newValue));
+                                }
+                            }
+                        };
+                mRcc.setMetadataUpdateListener(listener);
+            }
+        }
+
+        @Override
+        int getRccTransportControlFlagsFromActions(long actions) {
+            int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions);
+            if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_RATING;
+            }
+            return transportControlFlags;
+        }
+
+        @Override
+        RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) {
+            RemoteControlClient.MetadataEditor editor = super.buildRccMetadata(metadata);
+            long actions = mState == null ? 0 : mState.getActions();
+            if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) {
+                editor.addEditableKey(RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER);
+            }
+
+            if (metadata == null) {
+                return editor;
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_YEAR)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_YEAR,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_YEAR));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_RATING)) {
+                // Do not remove casting here. Without this, a crash will happen in API 19.
+                ((MediaMetadataEditor) editor).putObject(MediaMetadataEditor.RATING_KEY_BY_OTHERS,
+                        metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_RATING));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_USER_RATING)) {
+                // Do not remove casting here. Without this, a crash will happen in API 19.
+                ((MediaMetadataEditor) editor).putObject(MediaMetadataEditor.RATING_KEY_BY_USER,
+                        metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_USER_RATING));
+            }
+            return editor;
+        }
+    }
+
+    @RequiresApi(21)
     static class MediaSessionImplApi21 implements MediaSessionImpl {
         private final Object mSessionObj;
         private final Token mToken;
@@ -2685,11 +3201,13 @@
                 new RemoteCallbackList<>();
 
         private PlaybackStateCompat mPlaybackState;
+        private List<QueueItem> mQueue;
         private MediaMetadataCompat mMetadata;
         @RatingCompat.Style int mRatingType;
         boolean mCaptioningEnabled;
         @PlaybackStateCompat.RepeatMode int mRepeatMode;
         boolean mShuffleModeEnabled;
+        @PlaybackStateCompat.ShuffleMode int mShuffleMode;
 
         public MediaSessionImplApi21(Context context, String tag) {
             mSessionObj = MediaSessionCompatApi21.createSession(context, tag);
@@ -2708,7 +3226,7 @@
             MediaSessionCompatApi21.setCallback(mSessionObj,
                     callback == null ? null : callback.mCallbackObj, handler);
             if (callback != null) {
-                callback.mSessionImpl = new WeakReference<MediaSessionImpl>(this);
+                callback.setSessionImpl(this, handler);
             }
         }
 
@@ -2782,6 +3300,11 @@
         }
 
         @Override
+        public PlaybackStateCompat getPlaybackState() {
+            return mPlaybackState;
+        }
+
+        @Override
         public void setMetadata(MediaMetadataCompat metadata) {
             mMetadata = metadata;
             MediaSessionCompatApi21.setMetadata(mSessionObj,
@@ -2800,6 +3323,7 @@
 
         @Override
         public void setQueue(List<QueueItem> queue) {
+            mQueue = queue;
             List<Object> queueObjs = null;
             if (queue != null) {
                 queueObjs = new ArrayList<>();
@@ -2864,7 +3388,23 @@
                 for (int i = size - 1; i >= 0; i--) {
                     IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
                     try {
-                        cb.onShuffleModeChanged(enabled);
+                        cb.onShuffleModeChangedDeprecated(enabled);
+                    } catch (RemoteException e) {
+                    }
+                }
+                mExtraControllerCallbacks.finishBroadcast();
+            }
+        }
+
+        @Override
+        public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
+            if (mShuffleMode != shuffleMode) {
+                mShuffleMode = shuffleMode;
+                int size = mExtraControllerCallbacks.beginBroadcast();
+                for (int i = size - 1; i >= 0; i--) {
+                    IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
+                    try {
+                        cb.onShuffleModeChanged(shuffleMode);
                     } catch (RemoteException e) {
                     }
                 }
@@ -3067,6 +3607,12 @@
             }
 
             @Override
+            public void rateWithExtras(RatingCompat rating, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
                 // Will not be called.
                 throw new AssertionError();
@@ -3079,7 +3625,13 @@
             }
 
             @Override
-            public void setShuffleModeEnabled(boolean enabled) throws RemoteException {
+            public void setShuffleModeEnabledDeprecated(boolean enabled) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void setShuffleMode(int shuffleMode) throws RemoteException {
                 // Will not be called.
                 throw new AssertionError();
             }
@@ -3161,11 +3713,17 @@
             }
 
             @Override
-            public boolean isShuffleModeEnabled() {
+            public boolean isShuffleModeEnabledDeprecated() {
                 return mShuffleModeEnabled;
             }
 
             @Override
+            @PlaybackStateCompat.ShuffleMode
+            public int getShuffleMode() {
+                return mShuffleMode;
+            }
+
+            @Override
             public boolean isTransportControlEnabled() {
                 // Will not be called.
                 throw new AssertionError();
diff --git a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
index 956abb6..8c96163 100644
--- a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -327,6 +327,68 @@
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
+    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL, REPEAT_MODE_GROUP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RepeatMode {}
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback will be stopped at the end of the playing media list.
+     */
+    public static final int REPEAT_MODE_NONE = 0;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback of the current playing media item will be repeated.
+     */
+    public static final int REPEAT_MODE_ONE = 1;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback of the playing media list will be repeated.
+     */
+    public static final int REPEAT_MODE_ALL = 2;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback of the playing media group will be repeated.
+     * A group is a logical block of media items which is specified in the section 5.7 of the
+     * Bluetooth AVRCP 1.6.
+     */
+    public static final int REPEAT_MODE_GROUP = 3;
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ShuffleMode {}
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setShuffleMode}
+     * to indicate that the media list will be played in order.
+     */
+    public static final int SHUFFLE_MODE_NONE = 0;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setShuffleMode}
+     * to indicate that the media list will be played in shuffled order.
+     */
+    public static final int SHUFFLE_MODE_ALL = 1;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setShuffleMode}
+     * to indicate that the media group will be played in shuffled order.
+     * A group is a logical block of media items which is specified in the section 5.7 of the
+     * Bluetooth AVRCP 1.6.
+     */
+    public static final int SHUFFLE_MODE_GROUP = 2;
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
     @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
             ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
             ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
@@ -408,31 +470,6 @@
      */
     public static final int ERROR_CODE_END_OF_QUEUE = 11;
 
-    /**
-     * @hide
-     */
-    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RepeatMode {}
-
-    /**
-     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
-     * to indicate that the playback will be stopped at the end of the playing media list.
-     */
-    public static final int REPEAT_MODE_NONE = 0;
-
-    /**
-     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
-     * to indicate that the playback of the current playing media item will be repeated.
-     */
-    public static final int REPEAT_MODE_ONE = 1;
-
-    /**
-     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
-     * to indicate that the playback of the playing media list will be repeated.
-     */
-    public static final int REPEAT_MODE_ALL = 2;
-
     // KeyEvent constants only available on API 11+
     private static final int KEYCODE_MEDIA_PAUSE = 127;
     private static final int KEYCODE_MEDIA_PLAY = 126;
@@ -721,35 +758,38 @@
      * @return An equivalent {@link PlaybackStateCompat} object, or null if none.
      */
     public static PlaybackStateCompat fromPlaybackState(Object stateObj) {
-        if (stateObj == null || Build.VERSION.SDK_INT < 21) {
+        if (stateObj != null && Build.VERSION.SDK_INT >= 21) {
+            List<Object> customActionObjs = PlaybackStateCompatApi21.getCustomActions(stateObj);
+            List<PlaybackStateCompat.CustomAction> customActions = null;
+            if (customActionObjs != null) {
+                customActions = new ArrayList<>(customActionObjs.size());
+                for (Object customActionObj : customActionObjs) {
+                    customActions.add(CustomAction.fromCustomAction(customActionObj));
+                }
+            }
+            Bundle extras;
+            if (Build.VERSION.SDK_INT >= 22) {
+                extras = PlaybackStateCompatApi22.getExtras(stateObj);
+            } else {
+                extras = null;
+            }
+            PlaybackStateCompat state = new PlaybackStateCompat(
+                    PlaybackStateCompatApi21.getState(stateObj),
+                    PlaybackStateCompatApi21.getPosition(stateObj),
+                    PlaybackStateCompatApi21.getBufferedPosition(stateObj),
+                    PlaybackStateCompatApi21.getPlaybackSpeed(stateObj),
+                    PlaybackStateCompatApi21.getActions(stateObj),
+                    ERROR_CODE_UNKNOWN_ERROR,
+                    PlaybackStateCompatApi21.getErrorMessage(stateObj),
+                    PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj),
+                    customActions,
+                    PlaybackStateCompatApi21.getActiveQueueItemId(stateObj),
+                    extras);
+            state.mStateObj = stateObj;
+            return state;
+        } else {
             return null;
         }
-
-        List<Object> customActionObjs = PlaybackStateCompatApi21.getCustomActions(stateObj);
-        List<PlaybackStateCompat.CustomAction> customActions = null;
-        if (customActionObjs != null) {
-            customActions = new ArrayList<>(customActionObjs.size());
-            for (Object customActionObj : customActionObjs) {
-                customActions.add(CustomAction.fromCustomAction(customActionObj));
-            }
-        }
-        Bundle extras = Build.VERSION.SDK_INT >= 22
-                ? PlaybackStateCompatApi22.getExtras(stateObj)
-                : null;
-        PlaybackStateCompat state = new PlaybackStateCompat(
-                PlaybackStateCompatApi21.getState(stateObj),
-                PlaybackStateCompatApi21.getPosition(stateObj),
-                PlaybackStateCompatApi21.getBufferedPosition(stateObj),
-                PlaybackStateCompatApi21.getPlaybackSpeed(stateObj),
-                PlaybackStateCompatApi21.getActions(stateObj),
-                ERROR_CODE_UNKNOWN_ERROR,
-                PlaybackStateCompatApi21.getErrorMessage(stateObj),
-                PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj),
-                customActions,
-                PlaybackStateCompatApi21.getActiveQueueItemId(stateObj),
-                extras);
-        state.mStateObj = stateObj;
-        return state;
     }
 
     /**
@@ -761,25 +801,25 @@
      * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none.
      */
     public Object getPlaybackState() {
-        if (mStateObj != null || Build.VERSION.SDK_INT < 21) {
-            return mStateObj;
-        }
-
-        List<Object> customActions = null;
-        if (mCustomActions != null) {
-            customActions = new ArrayList<>(mCustomActions.size());
-            for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
-                customActions.add(customAction.getCustomAction());
+        if (mStateObj == null && Build.VERSION.SDK_INT >= 21) {
+            List<Object> customActions = null;
+            if (mCustomActions != null) {
+                customActions = new ArrayList<>(mCustomActions.size());
+                for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
+                    customActions.add(customAction.getCustomAction());
+                }
             }
-        }
-        if (Build.VERSION.SDK_INT >= 22) {
-            mStateObj = PlaybackStateCompatApi22.newInstance(mState, mPosition, mBufferedPosition,
-                    mSpeed, mActions, mErrorMessage, mUpdateTime,
-                    customActions, mActiveItemId, mExtras);
-        } else {
-            mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition, mBufferedPosition,
-                    mSpeed, mActions, mErrorMessage, mUpdateTime,
-                    customActions, mActiveItemId);
+            if (Build.VERSION.SDK_INT >= 22) {
+                mStateObj = PlaybackStateCompatApi22.newInstance(mState, mPosition,
+                        mBufferedPosition,
+                        mSpeed, mActions, mErrorMessage, mUpdateTime,
+                        customActions, mActiveItemId, mExtras);
+            } else {
+                //noinspection AndroidLintNewApi - NewApi lint fails to handle nested checks.
+                mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition,
+                        mBufferedPosition, mSpeed, mActions, mErrorMessage, mUpdateTime,
+                        customActions, mActiveItemId);
+            }
         }
         return mStateObj;
     }
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
deleted file mode 100644
index 4abf568..0000000
--- a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2013 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.media;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.KeyEvent;
-
-@RequiresApi(18)
-@TargetApi(18)
-interface TransportMediatorCallback {
-    public void handleKey(KeyEvent key);
-    public void handleAudioFocusChange(int focusChange);
-    public long getPlaybackPosition();
-    public void playbackPositionUpdate(long newPositionMs);
-}
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
deleted file mode 100644
index aebf7ca..0000000
--- a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2013 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.media;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.media.RemoteControlClient;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-@RequiresApi(18)
-@TargetApi(18)
-class TransportMediatorJellybeanMR2 {
-    final Context mContext;
-    final AudioManager mAudioManager;
-    final View mTargetView;
-    final TransportMediatorCallback mTransportCallback;
-    final String mReceiverAction;
-    final IntentFilter mReceiverFilter;
-    final Intent mIntent;
-    final ViewTreeObserver.OnWindowAttachListener mWindowAttachListener =
-            new ViewTreeObserver.OnWindowAttachListener() {
-                @Override
-                public void onWindowAttached() {
-                    windowAttached();
-                }
-                @Override
-                public void onWindowDetached() {
-                    windowDetached();
-                }
-            };
-    final ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusListener =
-            new ViewTreeObserver.OnWindowFocusChangeListener() {
-                @Override
-                public void onWindowFocusChanged(boolean hasFocus) {
-                    if (hasFocus) gainFocus();
-                    else loseFocus();
-                }
-            };
-    final BroadcastReceiver mMediaButtonReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            try {
-                KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
-                mTransportCallback.handleKey(event);
-            } catch (ClassCastException e) {
-                Log.w("TransportController", e);
-            }
-        }
-    };
-    AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener
-            = new AudioManager.OnAudioFocusChangeListener() {
-        @Override
-        public void onAudioFocusChange(int focusChange) {
-            mTransportCallback.handleAudioFocusChange(focusChange);
-        }
-    };
-    final RemoteControlClient.OnGetPlaybackPositionListener mGetPlaybackPositionListener
-            = new RemoteControlClient.OnGetPlaybackPositionListener() {
-                @Override
-                public long onGetPlaybackPosition() {
-                    return mTransportCallback.getPlaybackPosition();
-                }
-            };
-    final RemoteControlClient.OnPlaybackPositionUpdateListener mPlaybackPositionUpdateListener
-            = new RemoteControlClient.OnPlaybackPositionUpdateListener() {
-                public void onPlaybackPositionUpdate(long newPositionMs) {
-                    mTransportCallback.playbackPositionUpdate(newPositionMs);
-                }
-            };
- 
-    PendingIntent mPendingIntent;
-    RemoteControlClient mRemoteControl;
-    boolean mFocused;
-    int mPlayState = 0;
-    boolean mAudioFocused;
-
-    public TransportMediatorJellybeanMR2(Context context, AudioManager audioManager,
-            View view, TransportMediatorCallback transportCallback) {
-        mContext = context;
-        mAudioManager = audioManager;
-        mTargetView = view;
-        mTransportCallback = transportCallback;
-        mReceiverAction = context.getPackageName() + ":transport:" + System.identityHashCode(this);
-        mIntent = new Intent(mReceiverAction);
-        mIntent.setPackage(context.getPackageName());
-        mReceiverFilter = new IntentFilter();
-        mReceiverFilter.addAction(mReceiverAction);
-        mTargetView.getViewTreeObserver().addOnWindowAttachListener(mWindowAttachListener);
-        mTargetView.getViewTreeObserver().addOnWindowFocusChangeListener(mWindowFocusListener);
-    }
-
-    public Object getRemoteControlClient() {
-        return mRemoteControl;
-    }
-
-    public void destroy() {
-        windowDetached();
-        mTargetView.getViewTreeObserver().removeOnWindowAttachListener(mWindowAttachListener);
-        mTargetView.getViewTreeObserver().removeOnWindowFocusChangeListener(mWindowFocusListener);
-    }
-
-    void windowAttached() {
-        mContext.registerReceiver(mMediaButtonReceiver, mReceiverFilter);
-        mPendingIntent = PendingIntent.getBroadcast(mContext, 0, mIntent,
-                PendingIntent.FLAG_CANCEL_CURRENT);
-        mRemoteControl = new RemoteControlClient(mPendingIntent);
-        mRemoteControl.setOnGetPlaybackPositionListener(mGetPlaybackPositionListener);
-        mRemoteControl.setPlaybackPositionUpdateListener(mPlaybackPositionUpdateListener);
-    }
-
-    void gainFocus() {
-        if (!mFocused) {
-            mFocused = true;
-            mAudioManager.registerMediaButtonEventReceiver(mPendingIntent);
-            mAudioManager.registerRemoteControlClient(mRemoteControl);
-            if (mPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
-                takeAudioFocus();
-            }
-        }
-    }
-
-    void takeAudioFocus() {
-        if (!mAudioFocused) {
-            mAudioFocused = true;
-            mAudioManager.requestAudioFocus(mAudioFocusChangeListener,
-                    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
-        }
-    }
-
-    public void startPlaying() {
-        if (mPlayState != RemoteControlClient.PLAYSTATE_PLAYING) {
-            mPlayState = RemoteControlClient.PLAYSTATE_PLAYING;
-            mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
-        }
-        if (mFocused) {
-            takeAudioFocus();
-        }
-    }
-
-    public void refreshState(boolean playing, long position, int transportControls) {
-        if (mRemoteControl != null) {
-            mRemoteControl.setPlaybackState(playing ? RemoteControlClient.PLAYSTATE_PLAYING
-                    : RemoteControlClient.PLAYSTATE_STOPPED, position, playing ? 1 : 0);
-            mRemoteControl.setTransportControlFlags(transportControls);
-        }
-    }
-
-    public void pausePlaying() {
-        if (mPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
-            mPlayState = RemoteControlClient.PLAYSTATE_PAUSED;
-            mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
-        }
-        dropAudioFocus();
-    }
-
-    public void stopPlaying() {
-        if (mPlayState != RemoteControlClient.PLAYSTATE_STOPPED) {
-            mPlayState = RemoteControlClient.PLAYSTATE_STOPPED;
-            mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
-        }
-        dropAudioFocus();
-    }
-
-    void dropAudioFocus() {
-        if (mAudioFocused) {
-            mAudioFocused = false;
-            mAudioManager.abandonAudioFocus(mAudioFocusChangeListener);
-        }
-    }
-
-    void loseFocus() {
-        dropAudioFocus();
-        if (mFocused) {
-            mFocused = false;
-            mAudioManager.unregisterRemoteControlClient(mRemoteControl);
-            mAudioManager.unregisterMediaButtonEventReceiver(mPendingIntent);
-        }
-    }
-
-    void windowDetached() {
-        loseFocus();
-        if (mPendingIntent != null) {
-            mContext.unregisterReceiver(mMediaButtonReceiver);
-            mPendingIntent.cancel();
-            mPendingIntent = null;
-            mRemoteControl = null;
-        }
-    }
-}
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java b/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
deleted file mode 100644
index 3f323a1..0000000
--- a/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.media.session;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.RemoteControlClient;
-import android.os.SystemClock;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-@RequiresApi(18)
-@TargetApi(18)
-class MediaSessionCompatApi18 {
-    private static final String TAG = "MediaSessionCompatApi18";
-
-    /***** PlaybackState actions *****/
-    private static final long ACTION_SEEK_TO = 1 << 8;
-
-    private static boolean sIsMbrPendingIntentSupported = true;
-
-    public static Object createPlaybackPositionUpdateListener(Callback callback) {
-        return new OnPlaybackPositionUpdateListener<Callback>(callback);
-    }
-
-    public static void registerMediaButtonEventReceiver(Context context, PendingIntent pi,
-            ComponentName cn) {
-        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-
-        // Some Android implementations are not able to register a media button event receiver
-        // using a PendingIntent but need a ComponentName instead. These will raise a
-        // NullPointerException.
-        if (sIsMbrPendingIntentSupported) {
-            try {
-                am.registerMediaButtonEventReceiver(pi);
-            } catch (NullPointerException e) {
-                Log.w(TAG, "Unable to register media button event receiver with "
-                        + "PendingIntent, falling back to ComponentName.");
-                sIsMbrPendingIntentSupported = false;
-            }
-        }
-
-        if (!sIsMbrPendingIntentSupported) {
-          am.registerMediaButtonEventReceiver(cn);
-        }
-    }
-
-    public static void unregisterMediaButtonEventReceiver(Context context, PendingIntent pi,
-            ComponentName cn) {
-        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        if (sIsMbrPendingIntentSupported) {
-            am.unregisterMediaButtonEventReceiver(pi);
-        } else {
-            am.unregisterMediaButtonEventReceiver(cn);
-        }
-    }
-
-    public static void setState(Object rccObj, int state, long position, float speed,
-            long updateTime) {
-        long currTime = SystemClock.elapsedRealtime();
-        if (state == MediaSessionCompatApi14.STATE_PLAYING && position > 0) {
-            long diff = 0;
-            if (updateTime > 0) {
-                diff = currTime - updateTime;
-                if (speed > 0 && speed != 1f) {
-                    diff *= speed;
-                }
-            }
-            position += diff;
-        }
-        state = MediaSessionCompatApi14.getRccStateFromState(state);
-        ((RemoteControlClient) rccObj).setPlaybackState(state, position, speed);
-    }
-
-    public static void setTransportControlFlags(Object rccObj, long actions) {
-        ((RemoteControlClient) rccObj).setTransportControlFlags(
-                getRccTransportControlFlagsFromActions(actions));
-    }
-
-    public static void setOnPlaybackPositionUpdateListener(Object rccObj,
-            Object onPositionUpdateObj) {
-        ((RemoteControlClient) rccObj).setPlaybackPositionUpdateListener(
-                (RemoteControlClient.OnPlaybackPositionUpdateListener) onPositionUpdateObj);
-    }
-
-    static int getRccTransportControlFlagsFromActions(long actions) {
-        int transportControlFlags =
-                MediaSessionCompatApi14.getRccTransportControlFlagsFromActions(actions);
-        if ((actions & ACTION_SEEK_TO) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
-        }
-        return transportControlFlags;
-    }
-
-    static class OnPlaybackPositionUpdateListener<T extends Callback>
-            implements RemoteControlClient.OnPlaybackPositionUpdateListener {
-        protected final T mCallback;
-
-        public OnPlaybackPositionUpdateListener(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onPlaybackPositionUpdate(long newPositionMs) {
-            mCallback.onSeekTo(newPositionMs);
-        }
-    }
-
-    interface Callback {
-        public void onSeekTo(long pos);
-    }
-}
diff --git a/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java b/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
index 5efdc58..1d3fa50 100644
--- a/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
+++ b/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
@@ -16,12 +16,10 @@
 
 package android.support.v4.media;
 
-import android.annotation.TargetApi;
 import android.media.Rating;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(19)
-@TargetApi(19)
 class RatingCompatKitkat {
     public static Object newUnratedRating(int ratingStyle) {
         return Rating.newUnratedRating(ratingStyle);
diff --git a/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java b/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
deleted file mode 100644
index 94b446b..0000000
--- a/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.
- */
-package android.support.v4.media.session;
-
-import android.annotation.TargetApi;
-import android.media.MediaMetadataEditor;
-import android.media.MediaMetadataRetriever;
-import android.media.Rating;
-import android.media.RemoteControlClient;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class MediaSessionCompatApi19 {
-    /***** PlaybackState actions *****/
-    private static final long ACTION_SET_RATING = 1 << 7;
-
-    /***** MediaMetadata keys ********/
-    private static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
-    private static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
-    private static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
-
-    public static void setTransportControlFlags(Object rccObj, long actions) {
-        ((RemoteControlClient) rccObj).setTransportControlFlags(
-                getRccTransportControlFlagsFromActions(actions));
-    }
-
-    public static Object createMetadataUpdateListener(Callback callback) {
-        return new OnMetadataUpdateListener<Callback>(callback);
-    }
-
-    public static void setMetadata(Object rccObj, Bundle metadata, long actions) {
-        RemoteControlClient.MetadataEditor editor = ((RemoteControlClient) rccObj).editMetadata(
-                true);
-        MediaSessionCompatApi14.buildOldMetadata(metadata, editor);
-        addNewMetadata(metadata, editor);
-        if ((actions & ACTION_SET_RATING) != 0) {
-            editor.addEditableKey(RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER);
-        }
-        editor.apply();
-    }
-
-    public static void setOnMetadataUpdateListener(Object rccObj, Object onMetadataUpdateObj) {
-        ((RemoteControlClient) rccObj).setMetadataUpdateListener(
-                (RemoteControlClient.OnMetadataUpdateListener) onMetadataUpdateObj);
-    }
-
-    static int getRccTransportControlFlagsFromActions(long actions) {
-        int transportControlFlags =
-                MediaSessionCompatApi18.getRccTransportControlFlagsFromActions(actions);
-        if ((actions & ACTION_SET_RATING) != 0) {
-            transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_RATING;
-        }
-        return transportControlFlags;
-    }
-
-    static void addNewMetadata(Bundle metadata, RemoteControlClient.MetadataEditor editor) {
-        if (metadata == null) {
-            return;
-        }
-        if (metadata.containsKey(METADATA_KEY_YEAR)) {
-            editor.putLong(MediaMetadataRetriever.METADATA_KEY_YEAR,
-                    metadata.getLong(METADATA_KEY_YEAR));
-        }
-        if (metadata.containsKey(METADATA_KEY_RATING)) {
-            editor.putObject(MediaMetadataEditor.RATING_KEY_BY_OTHERS,
-                    metadata.getParcelable(METADATA_KEY_RATING));
-        }
-        if (metadata.containsKey(METADATA_KEY_USER_RATING)) {
-            editor.putObject(MediaMetadataEditor.RATING_KEY_BY_USER,
-                    metadata.getParcelable(METADATA_KEY_USER_RATING));
-        }
-    }
-
-    static class OnMetadataUpdateListener<T extends Callback> implements
-            RemoteControlClient.OnMetadataUpdateListener {
-        protected final T mCallback;
-
-        public OnMetadataUpdateListener(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onMetadataUpdate(int key, Object newValue) {
-            if (key == MediaMetadataEditor.RATING_KEY_BY_USER && newValue instanceof Rating) {
-                mCallback.onSetRating(newValue);
-            }
-        }
-    }
-
-    interface Callback extends MediaSessionCompatApi18.Callback {
-        public void onSetRating(Object ratingObj);
-    }
-}
diff --git a/media-compat/lint-baseline.xml b/media-compat/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/media-compat/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/media-compat/proguard-rules.pro b/media-compat/proguard-rules.pro
new file mode 100644
index 0000000..416b2e8
--- /dev/null
+++ b/media-compat/proguard-rules.pro
@@ -0,0 +1,18 @@
+# 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.
+
+# Prevent Parcelable objects from being removed or renamed.
+-keep class android.support.v4.media.** implements android.os.Parcelable {
+    public static final android.os.Parcelable$Creator *;
+}
\ No newline at end of file
diff --git a/media-compat/res-public/values/public_styles.xml b/media-compat/res-public/values/public_styles.xml
new file mode 100644
index 0000000..9113f6e
--- /dev/null
+++ b/media-compat/res-public/values/public_styles.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<!-- Definitions of styles to be exposed as public -->
+<resources>
+    <public type="style" name="TextAppearance.Compat.Notification.Media"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Title.Media"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Info.Media"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Time.Media"/>
+    <public type="style" name="TextAppearance.Compat.Notification.Line2.Media"/>
+</resources>
diff --git a/media-compat/res/layout/notification_media_action.xml b/media-compat/res/layout/notification_media_action.xml
new file mode 100644
index 0000000..f71acdd
--- /dev/null
+++ b/media-compat/res/layout/notification_media_action.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    style="?android:attr/borderlessButtonStyle"
+    android:id="@+id/action0"
+    android:layout_width="48dp"
+    android:layout_height="match_parent"
+    android:layout_marginLeft="2dp"
+    android:layout_marginRight="2dp"
+    android:layout_weight="1"
+    android:gravity="center"/>
\ No newline at end of file
diff --git a/media-compat/res/layout/notification_media_cancel_action.xml b/media-compat/res/layout/notification_media_cancel_action.xml
new file mode 100644
index 0000000..b0b50b4
--- /dev/null
+++ b/media-compat/res/layout/notification_media_cancel_action.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="?android:attr/borderlessButtonStyle"
+    android:id="@+id/cancel_action"
+    android:layout_width="48dp"
+    android:layout_height="match_parent"
+    android:layout_marginLeft="2dp"
+    android:layout_marginRight="2dp"
+    android:layout_weight="1"
+    android:src="@android:drawable/ic_menu_close_clear_cancel"
+    android:gravity="center"
+    android:visibility="gone"/>
\ No newline at end of file
diff --git a/media-compat/res/layout/notification_template_big_media.xml b/media-compat/res/layout/notification_template_big_media.xml
new file mode 100644
index 0000000..8dd93dc
--- /dev/null
+++ b/media-compat/res/layout/notification_template_big_media.xml
@@ -0,0 +1,60 @@
+<?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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="128dp"
+    >
+    <include layout="@layout/notification_template_icon_group"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+    />
+    <include layout="@layout/notification_media_cancel_action"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginLeft="2dp"
+        android:layout_marginRight="2dp"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentEnd="true" />
+    <include layout="@layout/notification_template_lines_media"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="fill_vertical"
+        android:layout_marginLeft="@dimen/notification_large_icon_width"
+        android:layout_marginStart="@dimen/notification_large_icon_width"
+        android:layout_toLeftOf="@id/cancel_action"
+        android:layout_toStartOf="@id/cancel_action"/>
+    <LinearLayout
+        android:id="@+id/media_actions"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_alignParentBottom="true"
+        android:layout_marginLeft="12dp"
+        android:layout_marginRight="12dp"
+        android:orientation="horizontal"
+        android:layoutDirection="ltr"
+        >
+        <!-- media buttons will be added here -->
+    </LinearLayout>
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_above="@id/media_actions"
+        android:id="@+id/action_divider"
+        android:background="?android:attr/dividerHorizontal" />
+</RelativeLayout>
diff --git a/media-compat/res/layout/notification_template_big_media_custom.xml b/media-compat/res/layout/notification_template_big_media_custom.xml
new file mode 100644
index 0000000..cccda1d
--- /dev/null
+++ b/media-compat/res/layout/notification_template_big_media_custom.xml
@@ -0,0 +1,107 @@
+<?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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="128dp"
+    >
+    <include layout="@layout/notification_template_icon_group"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+    />
+    <include layout="@layout/notification_media_cancel_action"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginLeft="2dp"
+        android:layout_marginRight="2dp"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentEnd="true"/>
+    <LinearLayout
+        android:id="@+id/notification_main_column_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/notification_large_icon_height"
+        android:layout_marginStart="@dimen/notification_large_icon_height"
+        android:minHeight="@dimen/notification_large_icon_height"
+        android:paddingTop="@dimen/notification_main_column_padding_top"
+        android:orientation="horizontal"
+        android:layout_toLeftOf="@id/cancel_action"
+        android:layout_toStartOf="@id/cancel_action">
+        <FrameLayout
+            android:id="@+id/notification_main_column"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginLeft="@dimen/notification_content_margin_start"
+            android:layout_marginStart="@dimen/notification_content_margin_start"
+            android:layout_marginRight="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginBottom="8dp"
+        />
+        <FrameLayout
+            android:id="@+id/right_side"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="8dp"
+            android:layout_marginEnd="8dp"
+            android:paddingTop="@dimen/notification_right_side_padding_top">
+            <DateTimeView android:id="@+id/time"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:layout_gravity="end|top"
+                android:visibility="gone"
+            />
+            <Chronometer android:id="@+id/chronometer"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:layout_gravity="end|top"
+                android:visibility="gone"
+            />
+            <TextView android:id="@+id/info"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Info.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:layout_gravity="end|bottom"
+                android:singleLine="true"
+            />
+        </FrameLayout>
+    </LinearLayout>
+    <LinearLayout
+        android:id="@+id/media_actions"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_alignParentBottom="true"
+        android:layout_marginLeft="12dp"
+        android:layout_marginRight="12dp"
+        android:orientation="horizontal"
+        android:layoutDirection="ltr"
+        >
+        <!-- media buttons will be added here -->
+    </LinearLayout>
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_above="@id/media_actions"
+        android:id="@+id/action_divider"
+        android:background="?android:attr/dividerHorizontal" />
+</RelativeLayout>
diff --git a/media-compat/res/layout/notification_template_big_media_narrow.xml b/media-compat/res/layout/notification_template_big_media_narrow.xml
new file mode 100644
index 0000000..ca8e289
--- /dev/null
+++ b/media-compat/res/layout/notification_template_big_media_narrow.xml
@@ -0,0 +1,68 @@
+<?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.
+  -->
+
+<!-- Layout to be used with only max 3 actions. It has a much larger picture at the left side-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="128dp"
+    >
+    <ImageView android:id="@+id/icon"
+        android:layout_width="128dp"
+        android:layout_height="128dp"
+        android:scaleType="centerCrop"
+        />
+
+    <include layout="@layout/notification_media_cancel_action"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginLeft="2dp"
+        android:layout_marginRight="2dp"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentEnd="true"/>
+
+    <include layout="@layout/notification_template_lines_media"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="128dp"
+        android:layout_marginStart="128dp"
+        android:layout_toLeftOf="@id/cancel_action"
+        android:layout_toStartOf="@id/cancel_action"/>
+
+    <LinearLayout
+        android:id="@+id/media_actions"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_toRightOf="@id/icon"
+        android:layout_toEndOf="@id/icon"
+        android:layout_alignParentBottom="true"
+        android:layout_marginLeft="12dp"
+        android:layout_marginRight="12dp"
+        android:orientation="horizontal"
+        android:layoutDirection="ltr"
+        >
+        <!-- media buttons will be added here -->
+    </LinearLayout>
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_toRightOf="@id/icon"
+        android:layout_toEndOf="@id/icon"
+        android:layout_above="@id/media_actions"
+        android:id="@+id/action_divider"
+        android:background="?android:attr/dividerHorizontal" />
+</RelativeLayout>
diff --git a/media-compat/res/layout/notification_template_big_media_narrow_custom.xml b/media-compat/res/layout/notification_template_big_media_narrow_custom.xml
new file mode 100644
index 0000000..db78467
--- /dev/null
+++ b/media-compat/res/layout/notification_template_big_media_narrow_custom.xml
@@ -0,0 +1,115 @@
+<?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.
+  -->
+
+<!-- Layout to be used with only max 3 actions. It has a much larger picture at the left side-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="128dp"
+    >
+    <ImageView android:id="@+id/icon"
+        android:layout_width="128dp"
+        android:layout_height="128dp"
+        android:scaleType="centerCrop"
+        />
+
+    <include layout="@layout/notification_media_cancel_action"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginLeft="2dp"
+        android:layout_marginRight="2dp"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentEnd="true"/>
+
+    <LinearLayout
+        android:id="@+id/notification_main_column_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="128dp"
+        android:layout_marginStart="128dp"
+        android:minHeight="@dimen/notification_large_icon_height"
+        android:paddingTop="@dimen/notification_main_column_padding_top"
+        android:orientation="horizontal"
+        android:layout_toLeftOf="@id/cancel_action"
+        android:layout_toStartOf="@id/cancel_action">
+        <FrameLayout
+            android:id="@+id/notification_main_column"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginLeft="@dimen/notification_media_narrow_margin"
+            android:layout_marginStart="@dimen/notification_media_narrow_margin"
+            android:layout_marginRight="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginBottom="8dp"/>
+        <FrameLayout
+            android:id="@+id/right_side"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="8dp"
+            android:layout_marginEnd="8dp"
+            android:paddingTop="@dimen/notification_right_side_padding_top">
+            <DateTimeView android:id="@+id/time"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:layout_gravity="end|top"
+                android:visibility="gone"
+            />
+            <Chronometer android:id="@+id/chronometer"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:layout_gravity="end|top"
+                android:visibility="gone"
+            />
+            <TextView android:id="@+id/info"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Info.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:layout_gravity="end|bottom"
+                android:singleLine="true"
+            />
+        </FrameLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/media_actions"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_toRightOf="@id/icon"
+        android:layout_toEndOf="@id/icon"
+        android:layout_alignParentBottom="true"
+        android:layout_marginLeft="12dp"
+        android:layout_marginRight="12dp"
+        android:orientation="horizontal"
+        android:layoutDirection="ltr"
+        >
+        <!-- media buttons will be added here -->
+    </LinearLayout>
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_toRightOf="@id/icon"
+        android:layout_toEndOf="@id/icon"
+        android:layout_above="@id/media_actions"
+        android:id="@+id/action_divider"
+        android:background="?android:attr/dividerHorizontal" />
+</RelativeLayout>
diff --git a/media-compat/res/layout/notification_template_lines_media.xml b/media-compat/res/layout/notification_template_lines_media.xml
new file mode 100644
index 0000000..385a01b
--- /dev/null
+++ b/media-compat/res/layout/notification_template_lines_media.xml
@@ -0,0 +1,112 @@
+<?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"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingRight="8dp"
+    android:paddingEnd="8dp"
+    android:paddingTop="2dp"
+    android:paddingBottom="2dp"
+    >
+    <LinearLayout
+        android:id="@+id/line1"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="6dp"
+        android:layout_marginLeft="@dimen/notification_content_margin_start"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:orientation="horizontal"
+        >
+        <TextView android:id="@+id/title"
+            android:textAppearance="@style/TextAppearance.Compat.Notification.Title.Media"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:layout_weight="1"
+            />
+        <DateTimeView android:id="@+id/time"
+            android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:layout_gravity="center"
+            android:layout_weight="0"
+            android:visibility="gone"
+            android:paddingLeft="8dp"
+            android:paddingStart="8dp"
+        />
+        <Chronometer android:id="@+id/chronometer"
+            android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:layout_gravity="center"
+            android:layout_weight="0"
+            android:visibility="gone"
+            android:paddingLeft="8dp"
+            android:paddingStart="8dp"
+        />
+    </LinearLayout>
+    <TextView android:id="@+id/text2"
+        android:textAppearance="@style/TextAppearance.Compat.Notification.Line2.Media"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="-2dp"
+        android:layout_marginBottom="-2dp"
+        android:layout_marginLeft="@dimen/notification_content_margin_start"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:singleLine="true"
+        android:fadingEdge="horizontal"
+        android:ellipsize="marquee"
+        android:visibility="gone"
+        />
+    <LinearLayout
+        android:id="@+id/line3"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:layout_marginLeft="@dimen/notification_content_margin_start"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        >
+        <TextView android:id="@+id/text"
+            android:textAppearance="@style/TextAppearance.Compat.Notification.Media"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+        <TextView android:id="@+id/info"
+            android:textAppearance="@style/TextAppearance.Compat.Notification.Info.Media"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_weight="0"
+            android:singleLine="true"
+            android:gravity="center"
+            android:paddingLeft="8dp"
+            android:paddingStart="8dp"
+            />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/media-compat/res/layout/notification_template_media.xml b/media-compat/res/layout/notification_template_media.xml
new file mode 100644
index 0000000..f57070a
--- /dev/null
+++ b/media-compat/res/layout/notification_template_media.xml
@@ -0,0 +1,51 @@
+<?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"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="64dp"
+    android:orientation="horizontal"
+    >
+    <include layout="@layout/notification_template_icon_group"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+    />
+    <include layout="@layout/notification_template_lines_media"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+    <LinearLayout
+        android:id="@+id/media_actions"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical|end"
+        android:orientation="horizontal"
+        android:layoutDirection="ltr"
+        >
+        <!-- media buttons will be added here -->
+    </LinearLayout>
+    <include layout="@layout/notification_media_cancel_action"
+        android:layout_width="48dp"
+        android:layout_height="match_parent"
+        android:layout_marginRight="6dp"
+        android:layout_marginEnd="6dp"/>
+    <ImageView android:id="@+id/end_padder"
+        android:layout_width="6dp"
+        android:layout_height="match_parent"
+        />
+</LinearLayout>
diff --git a/media-compat/res/layout/notification_template_media_custom.xml b/media-compat/res/layout/notification_template_media_custom.xml
new file mode 100644
index 0000000..36a479c
--- /dev/null
+++ b/media-compat/res/layout/notification_template_media_custom.xml
@@ -0,0 +1,100 @@
+<?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"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="64dp"
+    android:orientation="horizontal"
+    >
+    <include layout="@layout/notification_template_icon_group"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+    />
+    <LinearLayout
+        android:id="@+id/notification_main_column_container"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/notification_main_column_padding_top"
+        android:minHeight="@dimen/notification_large_icon_height"
+        android:orientation="horizontal"
+        android:layout_toLeftOf="@id/cancel_action"
+        android:layout_toStartOf="@id/cancel_action">
+        <FrameLayout
+            android:id="@+id/notification_main_column"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginLeft="@dimen/notification_content_margin_start"
+            android:layout_marginStart="@dimen/notification_content_margin_start"
+            android:layout_marginRight="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginBottom="8dp"/>
+        <FrameLayout
+            android:id="@+id/right_side"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="8dp"
+            android:layout_marginEnd="8dp"
+            android:paddingTop="@dimen/notification_right_side_padding_top">
+            <DateTimeView android:id="@+id/time"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:layout_gravity="end|top"
+                android:visibility="gone"
+            />
+            <Chronometer android:id="@+id/chronometer"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Time.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:layout_gravity="end|top"
+                android:visibility="gone"
+            />
+            <TextView android:id="@+id/info"
+                android:textAppearance="@style/TextAppearance.Compat.Notification.Info.Media"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:layout_gravity="end|bottom"
+                android:singleLine="true"
+            />
+        </FrameLayout>
+    </LinearLayout>
+    <LinearLayout
+        android:id="@+id/media_actions"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical|end"
+        android:orientation="horizontal"
+        android:layoutDirection="ltr"
+        >
+        <!-- media buttons will be added here -->
+    </LinearLayout>
+    <include layout="@layout/notification_media_cancel_action"
+        android:layout_width="48dp"
+        android:layout_height="match_parent"
+        android:layout_marginRight="6dp"
+        android:layout_marginEnd="6dp"/>
+    <ImageView android:id="@+id/end_padder"
+        android:layout_width="6dp"
+        android:layout_height="match_parent"
+        />
+</LinearLayout>
diff --git a/media-compat/res/values-v21/styles.xml b/media-compat/res/values-v21/styles.xml
new file mode 100644
index 0000000..b785197
--- /dev/null
+++ b/media-compat/res/values-v21/styles.xml
@@ -0,0 +1,33 @@
+<!--
+  ~ 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="TextAppearance.Compat.Notification.Media" >
+        <item name="android:textColor">@color/secondary_text_default_material_dark</item>
+    </style>
+
+    <style name="TextAppearance.Compat.Notification.Title.Media" >
+        <item name="android:textColor">@color/primary_text_default_material_dark</item>
+    </style>
+
+    <style name="TextAppearance.Compat.Notification.Info.Media">
+        <item name="android:textColor">@color/secondary_text_default_material_dark</item>
+    </style>
+
+    <style name="TextAppearance.Compat.Notification.Time.Media">
+        <item name="android:textColor">@color/secondary_text_default_material_dark</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/media-compat/res/values-v24/styles.xml b/media-compat/res/values-v24/styles.xml
new file mode 100644
index 0000000..31b4224
--- /dev/null
+++ b/media-compat/res/values-v24/styles.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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>
+    <!-- Use platform styles, Media is dark again -->
+    <style name="TextAppearance.Compat.Notification.Media" />
+
+    <style name="TextAppearance.Compat.Notification.Title.Media" />
+
+    <style name="TextAppearance.Compat.Notification.Info.Media" />
+
+    <style name="TextAppearance.Compat.Notification.Time.Media" />
+</resources>
\ No newline at end of file
diff --git a/media-compat/res/values/colors.xml b/media-compat/res/values/colors.xml
new file mode 100644
index 0000000..67a2521
--- /dev/null
+++ b/media-compat/res/values/colors.xml
@@ -0,0 +1,23 @@
+<?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>
+
+    <!-- The color of the material notification background for media notifications when no custom
+     color is specified -->
+    <color name="notification_material_background_media_default_color">#ff424242</color>
+
+</resources>
\ No newline at end of file
diff --git a/media-compat/res/values/colors_material.xml b/media-compat/res/values/colors_material.xml
new file mode 100644
index 0000000..9eea922
--- /dev/null
+++ b/media-compat/res/values/colors_material.xml
@@ -0,0 +1,26 @@
+<?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.
+  -->
+
+<!-- Colors specific to Material themes. -->
+<resources>
+
+    <!-- 100% white -->
+    <color name="primary_text_default_material_dark">#ffffffff</color>
+    <!-- 70% white -->
+    <color name="secondary_text_default_material_dark">#b3ffffff</color>
+
+</resources>
diff --git a/media-compat/res/values/config.xml b/media-compat/res/values/config.xml
new file mode 100644
index 0000000..15c4328
--- /dev/null
+++ b/media-compat/res/values/config.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+
+    <integer name="cancel_button_image_alpha">127</integer>
+
+</resources>
diff --git a/media-compat/res/values/styles.xml b/media-compat/res/values/styles.xml
new file mode 100644
index 0000000..ce351a4
--- /dev/null
+++ b/media-compat/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?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="TextAppearance.Compat.Notification.Media" />
+
+    <style name="TextAppearance.Compat.Notification.Title.Media" />
+
+    <style name="TextAppearance.Compat.Notification.Info.Media"/>
+
+    <style name="TextAppearance.Compat.Notification.Time.Media"/>
+
+    <style name="TextAppearance.Compat.Notification.Line2.Media" parent="TextAppearance.Compat.Notification.Info.Media"/>
+</resources>
diff --git a/media-compat/tests/AndroidManifest.xml b/media-compat/tests/AndroidManifest.xml
index eda8df2..6d9a4f6 100644
--- a/media-compat/tests/AndroidManifest.xml
+++ b/media-compat/tests/AndroidManifest.xml
@@ -15,20 +15,13 @@
    limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.mediacompat.test">
-
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application android:supportsRtl="true">
-        <uses-library android:name="android.test.runner"/>
-        <receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
+        <receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
             <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
+                <action android:name="android.intent.action.MEDIA_BUTTON"/>
             </intent-filter>
         </receiver>
         <service android:name="android.support.v4.media.StubMediaBrowserServiceCompat">
@@ -42,15 +35,12 @@
                 <action android:name="android.media.browse.MediaBrowserService"/>
             </intent-filter>
         </service>
-        <service android:name="android.support.v4.media.StubMediaBrowserServiceCompatWithDelayedMediaSession">
+        <service
+            android:name="android.support.v4.media.StubMediaBrowserServiceCompatWithDelayedMediaSession">
             <intent-filter>
                 <action android:name="android.media.browse.MediaBrowserService"/>
             </intent-filter>
         </service>
     </application>
 
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.mediacompat.test"/>
-
 </manifest>
diff --git a/media-compat/tests/src/android/support/v4/media/AudioAttributesCompatTest.java b/media-compat/tests/src/android/support/v4/media/AudioAttributesCompatTest.java
new file mode 100644
index 0000000..d66c7fc
--- /dev/null
+++ b/media-compat/tests/src/android/support/v4/media/AudioAttributesCompatTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+package android.support.v4.media;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.os.Build;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Test {@link AudioAttributesCompat}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AudioAttributesCompatTest {
+    // some macros for conciseness
+    static final 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) {
+        return new AudioAttributesCompat.Builder().setLegacyStreamType(legacyStream);
+    }
+
+    // some objects we'll toss around
+    Object mMediaAA;
+    AudioAttributesCompat mMediaAAC,
+            mMediaLegacyAAC,
+            mMediaAACFromAA,
+            mNotificationAAC,
+            mNotificationLegacyAAC;
+
+    @Before
+    @SdkSuppress(minSdkVersion = 21)
+    public void setUpApi21() {
+        if (Build.VERSION.SDK_INT < 21) return;
+        mMediaAA =
+                new AudioAttributes.Builder()
+                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+                        .setUsage(AudioAttributes.USAGE_MEDIA)
+                        .build();
+        mMediaAACFromAA = AudioAttributesCompat.wrap((AudioAttributes) mMediaAA);
+    }
+
+    @Before
+    public void setUp() {
+        mMediaAAC =
+                mkBuilder(AudioAttributesCompat.CONTENT_TYPE_MUSIC,
+                        AudioAttributesCompat.USAGE_MEDIA).build();
+        mMediaLegacyAAC = mkBuilder(AudioManager.STREAM_MUSIC).build();
+        mNotificationAAC =
+                mkBuilder(AudioAttributesCompat.CONTENT_TYPE_SONIFICATION,
+                        AudioAttributesCompat.USAGE_NOTIFICATION)
+                        .build();
+        mNotificationLegacyAAC = mkBuilder(AudioManager.STREAM_NOTIFICATION).build();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 21)
+    public void testCreateWithAudioAttributesApi21() {
+        assertThat(mMediaAACFromAA, not(equalTo(null)));
+        assertThat((AudioAttributes) mMediaAACFromAA.unwrap(), equalTo(mMediaAA));
+        assertThat(
+                (AudioAttributes) mMediaAACFromAA.unwrap(),
+                equalTo(new AudioAttributes.Builder((AudioAttributes) mMediaAA).build()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 21)
+    public void testEqualityApi21() {
+        assertThat("self equality", mMediaAACFromAA, equalTo(mMediaAACFromAA));
+        assertThat("different things", mMediaAACFromAA, not(equalTo(mNotificationAAC)));
+    }
+
+    @Test
+    public void testEquality() {
+        assertThat("self equality", mMediaAAC, equalTo(mMediaAAC));
+        assertThat(
+                "equal to clone",
+                mMediaAAC,
+                equalTo(new AudioAttributesCompat.Builder(mMediaAAC).build()));
+        assertThat("different things are different", mMediaAAC, not(equalTo(mNotificationAAC)));
+        assertThat("different things are different 2", mNotificationAAC, not(equalTo(mMediaAAC)));
+        assertThat(
+                "equal to clone 2",
+                mNotificationAAC,
+                equalTo(new AudioAttributesCompat.Builder(mNotificationAAC).build()));
+    }
+
+    @Test
+    public void testGetters() {
+        assertThat(mMediaAAC.getContentType(), equalTo(AudioAttributesCompat.CONTENT_TYPE_MUSIC));
+        assertThat(mMediaAAC.getUsage(), equalTo(AudioAttributesCompat.USAGE_MEDIA));
+        assertThat(mMediaAAC.getFlags(), equalTo(0));
+    }
+
+    @Test
+    public void testLegacyStreamTypeInference() {
+        assertThat(mMediaAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
+        assertThat(mMediaLegacyAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
+        assertThat(
+                mNotificationAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_NOTIFICATION));
+        assertThat(
+                mNotificationLegacyAAC.getLegacyStreamType(),
+                equalTo(AudioManager.STREAM_NOTIFICATION));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 21)
+    public void testLegacyStreamTypeInferenceApi21() {
+        assertThat(mMediaAACFromAA.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
+    }
+
+    @Test
+    public void testLegacyStreamTypeInferenceInLegacyMode() {
+        // the builders behave differently based on the value of this only-for-testing global
+        // so we need our very own objects inside this method
+        AudioAttributesCompat.setForceLegacyBehavior(true);
+
+        AudioAttributesCompat mediaAAC =
+                mkBuilder(AudioAttributesCompat.CONTENT_TYPE_MUSIC,
+                        AudioAttributesCompat.USAGE_MEDIA).build();
+        AudioAttributesCompat mediaLegacyAAC = mkBuilder(AudioManager.STREAM_MUSIC).build();
+
+        AudioAttributesCompat notificationAAC =
+                mkBuilder(AudioAttributesCompat.CONTENT_TYPE_SONIFICATION,
+                        AudioAttributesCompat.USAGE_NOTIFICATION)
+                        .build();
+        AudioAttributesCompat notificationLegacyAAC =
+                mkBuilder(AudioManager.STREAM_NOTIFICATION).build();
+
+        assertThat(mediaAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
+        assertThat(mediaLegacyAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
+        assertThat(
+                notificationAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_NOTIFICATION));
+        assertThat(
+                notificationLegacyAAC.getLegacyStreamType(),
+                equalTo(AudioManager.STREAM_NOTIFICATION));
+    }
+
+    @After
+    public void cleanUp() {
+        AudioAttributesCompat.setForceLegacyBehavior(false);
+    }
+}
diff --git a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
index 50ff13b..ab21eda 100644
--- a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
@@ -20,17 +20,21 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.fail;
+import static junit.framework.Assert.fail;
 
 import android.content.ComponentName;
 import android.os.Bundle;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
 import android.support.v4.media.MediaBrowserCompat.MediaItem;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -63,19 +67,24 @@
     private static final ComponentName TEST_REMOTE_BROWSER_SERVICE = new ComponentName(
             "android.support.mediacompat.test",
             "android.support.v4.media.StubRemoteMediaBrowserServiceCompat");
-
     private static final ComponentName TEST_INVALID_BROWSER_SERVICE = new ComponentName(
             "invalid.package", "invalid.ServiceClassName");
-    private final StubConnectionCallback mConnectionCallback = new StubConnectionCallback();
-    private final StubSubscriptionCallback mSubscriptionCallback = new StubSubscriptionCallback();
-    private final StubItemCallback mItemCallback = new StubItemCallback();
 
     private MediaBrowserCompat mMediaBrowser;
+    private StubConnectionCallback mConnectionCallback;
+    private StubSubscriptionCallback mSubscriptionCallback;
+    private StubItemCallback mItemCallback;
+
+    @Before
+    public void setUp() {
+        mConnectionCallback = new StubConnectionCallback();
+        mSubscriptionCallback = new StubSubscriptionCallback();
+        mItemCallback = new StubItemCallback();
+    }
 
     @Test
     @SmallTest
-    public void testMediaBrowser() {
-        resetCallbacks();
+    public void testMediaBrowser() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         assertFalse(mMediaBrowser.isConnected());
 
@@ -96,13 +105,11 @@
                 return !mMediaBrowser.isConnected();
             }
         }.run();
-        assertFalse(mMediaBrowser.isConnected());
     }
 
     @Test
     @SmallTest
-    public void testMediaBrowserWithRemoteService() {
-        resetCallbacks();
+    public void testMediaBrowserWithRemoteService() throws Exception {
         createMediaBrowser(TEST_REMOTE_BROWSER_SERVICE);
         assertFalse(mMediaBrowser.isConnected());
 
@@ -122,13 +129,11 @@
                 return !mMediaBrowser.isConnected();
             }
         }.run();
-        assertFalse(mMediaBrowser.isConnected());
     }
 
     @Test
     @SmallTest
-    public void testConnectTwice() {
-        resetCallbacks();
+    public void testConnectTwice() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         try {
@@ -141,65 +146,102 @@
 
     @Test
     @SmallTest
-    public void testConnectionFailed() {
-        resetCallbacks();
+    public void testConnectionFailed() throws Exception {
         createMediaBrowser(TEST_INVALID_BROWSER_SERVICE);
 
-        mMediaBrowser.connect();
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mConnectionCallback.mConnectionFailedCount > 0
-                        && mConnectionCallback.mConnectedCount == 0
-                        && mConnectionCallback.mConnectionSuspendedCount == 0;
-            }
-        }.run();
+        synchronized (mConnectionCallback.mWaitLock) {
+            mMediaBrowser.connect();
+            mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+        }
+        assertTrue(mConnectionCallback.mConnectionFailedCount > 0);
+        assertEquals(0, mConnectionCallback.mConnectedCount);
+        assertEquals(0, mConnectionCallback.mConnectionSuspendedCount);
     }
 
     @Test
     @SmallTest
-    public void testSecondConnection() {
-        resetCallbacks();
+    public void testReconnection() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser.connect();
+                // Reconnect before the first connection was established.
+                mMediaBrowser.disconnect();
+                mMediaBrowser.connect();
+            }
+        });
+
+        synchronized (mConnectionCallback.mWaitLock) {
+            mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(1, mConnectionCallback.mConnectedCount);
+        }
+
+        synchronized (mSubscriptionCallback.mWaitLock) {
+            // Test subscribe.
+            resetCallbacks();
+            mMediaBrowser.subscribe(
+                    StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, mSubscriptionCallback);
+            mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mSubscriptionCallback.mChildrenLoadedCount > 0);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT,
+                    mSubscriptionCallback.mLastParentId);
+        }
+
+        synchronized (mItemCallback.mWaitLock) {
+            // Test getItem.
+            resetCallbacks();
+            mMediaBrowser.getItem(
+                    StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0], mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0],
+                    mItemCallback.mLastMediaItem.getMediaId());
+        }
+
+        // Reconnect after connection was established.
+        mMediaBrowser.disconnect();
+        resetCallbacks();
         connectMediaBrowserService();
 
-        mMediaBrowser.disconnect();
-        // We need to sleep some time here, because the browser may not be ready to connect yet.
+        synchronized (mItemCallback.mWaitLock) {
+            // Test getItem.
+            resetCallbacks();
+            mMediaBrowser.getItem(
+                    StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0], mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0],
+                    mItemCallback.mLastMediaItem.getMediaId());
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testConnectionCallbackNotCalledAfterDisconnect() {
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser.connect();
+                mMediaBrowser.disconnect();
+                resetCallbacks();
+            }
+        });
+
         try {
             Thread.sleep(SLEEP_MS);
         } catch (InterruptedException e) {
             fail("Unexpected InterruptedException occurred.");
         }
-
-        // Connect to the service again.
-        resetCallbacks();
-        connectMediaBrowserService();
-
-        // Test subscribe.
-        resetCallbacks();
-        mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, mSubscriptionCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mSubscriptionCallback.mChildrenLoadedCount > 0;
-            }
-        }.run();
-
-        // Test getItem.
-        resetCallbacks();
-        mMediaBrowser.getItem(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0], mItemCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mItemCallback.mLastMediaItem != null;
-            }
-        }.run();
+        assertEquals(0, mConnectionCallback.mConnectedCount);
+        assertEquals(0, mConnectionCallback.mConnectionFailedCount);
+        assertEquals(0, mConnectionCallback.mConnectionSuspendedCount);
     }
 
     @Test
     @SmallTest
     public void testGetServiceComponentBeforeConnection() {
-        resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         try {
             ComponentName serviceComponent = mMediaBrowser.getServiceComponent();
@@ -211,25 +253,23 @@
 
     @Test
     @SmallTest
-    public void testSubscribe() {
-        resetCallbacks();
+    public void testSubscribe() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, mSubscriptionCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mSubscriptionCallback.mChildrenLoadedCount > 0;
-            }
-        }.run();
 
-        assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT,
-                mSubscriptionCallback.mLastParentId);
-        assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length,
-                mSubscriptionCallback.mLastChildMediaItems.size());
-        for (int i = 0; i < StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length; ++i) {
-            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[i],
-                    mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+        synchronized (mSubscriptionCallback.mWaitLock) {
+            mMediaBrowser.subscribe(
+                    StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, mSubscriptionCallback);
+            mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mSubscriptionCallback.mChildrenLoadedCount > 0);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT,
+                    mSubscriptionCallback.mLastParentId);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length,
+                    mSubscriptionCallback.mLastChildMediaItems.size());
+            for (int i = 0; i < StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length; ++i) {
+                assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[i],
+                        mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+            }
         }
 
         // Test unsubscribe.
@@ -251,7 +291,7 @@
 
     @Test
     @SmallTest
-    public void testSubscribeWithOptions() {
+    public void testSubscribeWithOptions() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         final int pageSize = 3;
@@ -259,30 +299,31 @@
                 (StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length - 1) / pageSize;
         Bundle options = new Bundle();
         options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
-        for (int page = 0; page <= lastPage; ++page) {
-            resetCallbacks();
-            options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
-            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
-                    mSubscriptionCallback);
-            new PollingCheck(TIME_OUT_MS) {
-                @Override
-                protected boolean check() {
-                    return mSubscriptionCallback.mChildrenLoadedWithOptionCount > 0;
+
+        synchronized (mSubscriptionCallback.mWaitLock) {
+            for (int page = 0; page <= lastPage; ++page) {
+                resetCallbacks();
+                options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+                mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
+                        mSubscriptionCallback);
+                mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
+                assertTrue(mSubscriptionCallback.mChildrenLoadedWithOptionCount > 0);
+                assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT,
+                        mSubscriptionCallback.mLastParentId);
+                if (page != lastPage) {
+                    assertEquals(pageSize, mSubscriptionCallback.mLastChildMediaItems.size());
+                } else {
+                    assertEquals(
+                            (StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length - 1) % pageSize
+                                    + 1,
+                            mSubscriptionCallback.mLastChildMediaItems.size());
                 }
-            }.run();
-            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT,
-                    mSubscriptionCallback.mLastParentId);
-            if (page != lastPage) {
-                assertEquals(pageSize, mSubscriptionCallback.mLastChildMediaItems.size());
-            } else {
-                assertEquals(
-                        (StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN.length - 1) % pageSize + 1,
-                        mSubscriptionCallback.mLastChildMediaItems.size());
-            }
-            // Check whether all the items in the current page are loaded.
-            for (int i = 0; i < mSubscriptionCallback.mLastChildMediaItems.size(); ++i) {
-                assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[page * pageSize + i],
-                        mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+                // Check whether all the items in the current page are loaded.
+                for (int i = 0; i < mSubscriptionCallback.mLastChildMediaItems.size(); ++i) {
+                    assertEquals(
+                            StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[page * pageSize + i],
+                            mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+                }
             }
         }
 
@@ -306,27 +347,22 @@
 
     @Test
     @SmallTest
-    public void testSubscribeInvalidItem() {
-        resetCallbacks();
+    public void testSubscribeInvalidItem() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID,
-                mSubscriptionCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mSubscriptionCallback.mLastErrorId != null;
-            }
-        }.run();
 
-        assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID,
-                mSubscriptionCallback.mLastErrorId);
+        synchronized (mSubscriptionCallback.mWaitLock) {
+            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID,
+                    mSubscriptionCallback);
+            mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID,
+                    mSubscriptionCallback.mLastErrorId);
+        }
     }
 
     @Test
     @SmallTest
-    public void testSubscribeInvalidItemWithOptions() {
-        resetCallbacks();
+    public void testSubscribeInvalidItemWithOptions() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
 
@@ -335,26 +371,24 @@
         Bundle options = new Bundle();
         options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
         options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
-        mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID, options,
-                mSubscriptionCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mSubscriptionCallback.mLastErrorId != null;
-            }
-        }.run();
 
-        assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID,
-                mSubscriptionCallback.mLastErrorId);
-        assertEquals(page,
-                mSubscriptionCallback.mLastOptions.getInt(MediaBrowserCompat.EXTRA_PAGE));
-        assertEquals(pageSize,
-                mSubscriptionCallback.mLastOptions.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE));
+        synchronized (mSubscriptionCallback.mWaitLock) {
+            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID, options,
+                    mSubscriptionCallback);
+            mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID,
+                    mSubscriptionCallback.mLastErrorId);
+            assertNotNull(mSubscriptionCallback.mLastOptions);
+            assertEquals(page,
+                    mSubscriptionCallback.mLastOptions.getInt(MediaBrowserCompat.EXTRA_PAGE));
+            assertEquals(pageSize,
+                    mSubscriptionCallback.mLastOptions.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE));
+        }
     }
 
     @Test
     @SmallTest
-    public void testUnsubscribeForMultipleSubscriptions() {
+    public void testUnsubscribeForMultipleSubscriptions() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
@@ -368,15 +402,13 @@
             Bundle options = new Bundle();
             options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
             options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
-            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options, callback);
-
+            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
+                    callback);
+            synchronized (callback.mWaitLock) {
+                callback.mWaitLock.wait(TIME_OUT_MS);
+            }
             // Each onChildrenLoaded() must be called.
-            new PollingCheck(TIME_OUT_MS) {
-                @Override
-                protected boolean check() {
-                    return callback.mChildrenLoadedWithOptionCount == 1;
-                }
-            }.run();
+            assertEquals(1, callback.mChildrenLoadedWithOptionCount);
         }
 
         // Reset callbacks and unsubscribe.
@@ -402,8 +434,8 @@
     }
 
     @Test
-    @SmallTest
-    public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() {
+    @MediumTest
+    public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
@@ -417,15 +449,13 @@
             Bundle options = new Bundle();
             options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
             options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
-            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options, callback);
-
+            mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
+                    callback);
+            synchronized (callback.mWaitLock) {
+                callback.mWaitLock.wait(TIME_OUT_MS);
+            }
             // Each onChildrenLoaded() must be called.
-            new PollingCheck(TIME_OUT_MS) {
-                @Override
-                protected boolean check() {
-                    return callback.mChildrenLoadedWithOptionCount == 1;
-                }
-            }.run();
+            assertEquals(1, callback.mChildrenLoadedWithOptionCount);
         }
 
         // Unsubscribe existing subscriptions one-by-one.
@@ -464,61 +494,49 @@
 
     @Test
     @SmallTest
-    public void testGetItem() {
-        resetCallbacks();
+    public void testGetItem() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.getItem(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0], mItemCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mItemCallback.mLastMediaItem != null;
-            }
-        }.run();
 
-        assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0],
-                mItemCallback.mLastMediaItem.getMediaId());
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0],
+                    mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertNotNull(mItemCallback.mLastMediaItem);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_CHILDREN[0],
+                    mItemCallback.mLastMediaItem.getMediaId());
+        }
+    }
+
+    @Test
+    @LargeTest
+    public void testGetItemWhenOnLoadItemIsNotImplemented() throws Exception {
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(
+                    StubMediaBrowserServiceCompat.MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED,
+                    mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED,
+                    mItemCallback.mLastErrorId);
+        }
     }
 
     @Test
     @SmallTest
-    public void testGetItemWhenOnLoadItemIsNotImplemented() {
-        resetCallbacks();
-        createMediaBrowser(TEST_BROWSER_SERVICE);
-        connectMediaBrowserService();
-        mMediaBrowser.getItem(StubMediaBrowserServiceCompat.MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED,
-                mItemCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mItemCallback.mLastErrorId != null;
-            }
-        }.run();
-
-        assertEquals(StubMediaBrowserServiceCompat.MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED,
-                mItemCallback.mLastErrorId);
-    }
-
-    @Test
-    @SmallTest
-    public void testGetItemWhenMediaIdIsInvalid() {
-        resetCallbacks();
+    public void testGetItemWhenMediaIdIsInvalid() throws Exception {
         mItemCallback.mLastMediaItem = new MediaItem(new MediaDescriptionCompat.Builder()
                 .setMediaId("dummy_id").build(), MediaItem.FLAG_BROWSABLE);
 
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.getItem(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID, mItemCallback);
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                // MediaBrowserServiceCompat.onLoadItem implementations must send null result when
-                // the given media id is invalid.
-                return mItemCallback.mLastMediaItem == null;
-            }
-        }.run();
-
-        assertNull(mItemCallback.mLastErrorId);
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(StubMediaBrowserServiceCompat.MEDIA_ID_INVALID, mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertNull(mItemCallback.mLastMediaItem);
+            assertNull(mItemCallback.mLastErrorId);
+        }
     }
 
     private void createMediaBrowser(final ComponentName component) {
@@ -531,14 +549,11 @@
         });
     }
 
-    private void connectMediaBrowserService() {
-        mMediaBrowser.connect();
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return mConnectionCallback.mConnectedCount > 0;
-            }
-        }.run();
+    private void connectMediaBrowserService() throws Exception {
+        synchronized (mConnectionCallback.mWaitLock) {
+            mMediaBrowser.connect();
+            mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+        }
     }
 
     private void resetCallbacks() {
@@ -547,7 +562,8 @@
         mItemCallback.reset();
     }
 
-    private static class StubConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
+    private class StubConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
+        Object mWaitLock = new Object();
         volatile int mConnectedCount;
         volatile int mConnectionFailedCount;
         volatile int mConnectionSuspendedCount;
@@ -560,21 +576,31 @@
 
         @Override
         public void onConnected() {
-            mConnectedCount++;
+            synchronized (mWaitLock) {
+                mConnectedCount++;
+                mWaitLock.notify();
+            }
         }
 
         @Override
         public void onConnectionFailed() {
-            mConnectionFailedCount++;
+            synchronized (mWaitLock) {
+                mConnectionFailedCount++;
+                mWaitLock.notify();
+            }
         }
 
         @Override
         public void onConnectionSuspended() {
-            mConnectionSuspendedCount++;
+            synchronized (mWaitLock) {
+                mConnectionSuspendedCount++;
+                mWaitLock.notify();
+            }
         }
     }
 
-    private static class StubSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
+    private class StubSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
+        Object mWaitLock = new Object();
         private volatile int mChildrenLoadedCount;
         private volatile int mChildrenLoadedWithOptionCount;
         private volatile String mLastErrorId;
@@ -593,32 +619,45 @@
 
         @Override
         public void onChildrenLoaded(String parentId, List<MediaItem> children) {
-            mChildrenLoadedCount++;
-            mLastParentId = parentId;
-            mLastChildMediaItems = children;
+            synchronized (mWaitLock) {
+                mChildrenLoadedCount++;
+                mLastParentId = parentId;
+                mLastChildMediaItems = children;
+                mWaitLock.notify();
+            }
         }
 
         @Override
         public void onChildrenLoaded(String parentId, List<MediaItem> children, Bundle options) {
-            mChildrenLoadedWithOptionCount++;
-            mLastParentId = parentId;
-            mLastOptions = options;
-            mLastChildMediaItems = children;
+            synchronized (mWaitLock) {
+                mChildrenLoadedWithOptionCount++;
+                mLastParentId = parentId;
+                mLastOptions = options;
+                mLastChildMediaItems = children;
+                mWaitLock.notify();
+            }
         }
 
         @Override
         public void onError(String id) {
-            mLastErrorId = id;
+            synchronized (mWaitLock) {
+                mLastErrorId = id;
+                mWaitLock.notify();
+            }
         }
 
         @Override
         public void onError(String id, Bundle options) {
-            mLastErrorId = id;
-            mLastOptions = options;
+            synchronized (mWaitLock) {
+                mLastErrorId = id;
+                mLastOptions = options;
+                mWaitLock.notify();
+            }
         }
     }
 
-    private static class StubItemCallback extends MediaBrowserCompat.ItemCallback {
+    private class StubItemCallback extends MediaBrowserCompat.ItemCallback {
+        Object mWaitLock = new Object();
         private volatile MediaItem mLastMediaItem;
         private volatile String mLastErrorId;
 
@@ -629,12 +668,18 @@
 
         @Override
         public void onItemLoaded(MediaItem item) {
-            mLastMediaItem = item;
+            synchronized (mWaitLock) {
+                mLastMediaItem = item;
+                mWaitLock.notify();
+            }
         }
 
         @Override
         public void onError(String id) {
-            mLastErrorId = id;
+            synchronized (mWaitLock) {
+                mLastErrorId = id;
+                mWaitLock.notify();
+            }
         }
     }
 }
diff --git a/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java b/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java
index 8aa5a10..4ceac10 100644
--- a/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java
@@ -26,9 +26,10 @@
 import android.content.ComponentName;
 import android.os.Build;
 import android.os.Bundle;
-import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
 import android.support.v4.media.MediaBrowserCompat.MediaItem;
 
 import org.junit.Before;
@@ -143,7 +144,7 @@
     }
 
     @Test
-    @LargeTest
+    @MediumTest
     public void testDelayedNotifyChildrenChanged() throws Exception {
         synchronized (mWaitLock) {
             mSubscriptionCallback.reset();
@@ -168,9 +169,8 @@
         }
     }
 
-    // TODO(hdmoon): Uncomment after fixing failing tests. (Fails on API >= 24)
-    // @Test
-    // @SmallTest
+    @Test
+    @MediumTest
     public void testDelayedItem() throws Exception {
         synchronized (mWaitLock) {
             mItemCallback.reset();
@@ -381,6 +381,8 @@
     @Test
     @SmallTest
     public void testDelayedSetSessionToken() throws Exception {
+        // This test has no meaning in API 21. The framework MediaBrowserService just connects to
+        // the media browser without waiting setMediaSession() to be called.
         if (Build.VERSION.SDK_INT == 21) {
             return;
         }
@@ -404,6 +406,11 @@
             StubMediaBrowserServiceCompatWithDelayedMediaSession.sInstance.callSetSessionToken();
             mWaitLock.wait(TIME_OUT_MS);
             assertEquals(1, callback.mConnectedCount);
+
+            if (Build.VERSION.SDK_INT >= 21) {
+                assertNotNull(
+                        mMediaBrowserForDelayedMediaSession.getSessionToken().getExtraBinder());
+            }
         }
     }
 
@@ -575,6 +582,5 @@
                 mWaitLock.notify();
             }
         }
-    };
-
+    }
 }
diff --git a/media-compat/tests/src/android/support/v4/media/PollingCheck.java b/media-compat/tests/src/android/support/v4/media/PollingCheck.java
deleted file mode 100644
index 222753f..0000000
--- a/media-compat/tests/src/android/support/v4/media/PollingCheck.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.media;
-
-import junit.framework.Assert;
-
-import java.util.concurrent.Callable;
-
-public abstract class PollingCheck {
-    private static final long TIME_SLICE = 50;
-    private long mTimeout = 3000;
-
-    public interface PollingCheckCondition {
-        boolean canProceed();
-    }
-
-    public PollingCheck() {
-    }
-
-    public PollingCheck(long timeout) {
-        mTimeout = timeout;
-    }
-
-    protected abstract boolean check();
-
-    public void run() {
-        if (check()) {
-            return;
-        }
-
-        long timeout = mTimeout;
-        while (timeout > 0) {
-            try {
-                Thread.sleep(TIME_SLICE);
-            } catch (InterruptedException e) {
-                Assert.fail("unexpected InterruptedException");
-            }
-
-            if (check()) {
-                return;
-            }
-
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail("unexpected timeout");
-    }
-
-    public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
-            throws Exception {
-        while (timeout > 0) {
-            if (condition.call()) {
-                return;
-            }
-
-            Thread.sleep(TIME_SLICE);
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail(message.toString());
-    }
-
-    public static void waitFor(final PollingCheckCondition condition) {
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-
-    public static void waitFor(long timeout, final PollingCheckCondition condition) {
-        new PollingCheck(timeout) {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-}
diff --git a/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java b/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java
index e7adc79..c817dce 100644
--- a/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java
+++ b/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java
@@ -46,7 +46,7 @@
             MEDIA_ID_CHILDREN_DELAYED
     };
 
-    static final String SEARCH_QUERY = "test_media_children";
+    static final String SEARCH_QUERY = "children_2";
     static final String SEARCH_QUERY_FOR_NO_RESULT = "query no result";
     static final String SEARCH_QUERY_FOR_ERROR = "query for error";
 
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
index d72e499..6103121 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
@@ -45,6 +45,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Test {@link MediaControllerCompat}.
  */
@@ -61,6 +64,7 @@
     private static final long TEST_POSITION = 1000000L;
     private static final float TEST_PLAYBACK_SPEED = 3.0f;
 
+
     private final Object mWaitLock = new Object();
     private Handler mHandler = new Handler(Looper.getMainLooper());
     private MediaSessionCompat mSession;
@@ -127,45 +131,51 @@
     @Test
     @SmallTest
     public void testAddRemoveQueueItems() throws Exception {
-        final String mediaId = "media_id";
-        final String mediaTitle = "media_title";
-        MediaDescriptionCompat itemDescription = new MediaDescriptionCompat.Builder()
-                .setMediaId(mediaId).setTitle(mediaTitle).build();
+        final String mediaId1 = "media_id_1";
+        final String mediaTitle1 = "media_title_1";
+        MediaDescriptionCompat itemDescription1 = new MediaDescriptionCompat.Builder()
+                .setMediaId(mediaId1).setTitle(mediaTitle1).build();
+
+        final String mediaId2 = "media_id_2";
+        final String mediaTitle2 = "media_title_2";
+        MediaDescriptionCompat itemDescription2 = new MediaDescriptionCompat.Builder()
+                .setMediaId(mediaId2).setTitle(mediaTitle2).build();
 
         synchronized (mWaitLock) {
             mCallback.reset();
-            mController.addQueueItem(itemDescription);
+            mController.addQueueItem(itemDescription1);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnAddQueueItemCalled);
             assertEquals(-1, mCallback.mQueueIndex);
-            assertEquals(mediaId, mCallback.mQueueDescription.getMediaId());
-            assertEquals(mediaTitle, mCallback.mQueueDescription.getTitle());
+            assertEquals(mediaId1, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle1, mCallback.mQueueDescription.getTitle());
 
             mCallback.reset();
-            mController.addQueueItem(itemDescription, 0);
+            mController.addQueueItem(itemDescription2, 0);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnAddQueueItemAtCalled);
             assertEquals(0, mCallback.mQueueIndex);
-            assertEquals(mediaId, mCallback.mQueueDescription.getMediaId());
-            assertEquals(mediaTitle, mCallback.mQueueDescription.getTitle());
+            assertEquals(mediaId2, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle2, mCallback.mQueueDescription.getTitle());
 
             mCallback.reset();
             mController.removeQueueItemAt(0);
             mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mCallback.mOnRemoveQueueItemAtCalled);
-            assertEquals(0, mCallback.mQueueIndex);
+            assertTrue(mCallback.mOnRemoveQueueItemCalled);
+            assertEquals(mediaId2, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle2, mCallback.mQueueDescription.getTitle());
 
             mCallback.reset();
-            mController.removeQueueItem(itemDescription);
+            mController.removeQueueItem(itemDescription1);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnRemoveQueueItemCalled);
-            assertEquals(mediaId, mCallback.mQueueDescription.getMediaId());
-            assertEquals(mediaTitle, mCallback.mQueueDescription.getTitle());
+            assertEquals(mediaId1, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle1, mCallback.mQueueDescription.getTitle());
 
             // Try to modify the queue when the session does not support queue management.
             mSession.setFlags(0);
             try {
-                mController.addQueueItem(itemDescription);
+                mController.addQueueItem(itemDescription1);
                 fail();
             } catch (UnsupportedOperationException e) {
                 // Expected.
@@ -278,9 +288,17 @@
             assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating(), DELTA);
 
             mCallback.reset();
-            final String mediaId = "test-media-id";
             final Bundle extras = new Bundle();
             extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+            controls.setRating(rating, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSetRatingCalled);
+            assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+            assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating(), DELTA);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
+            final String mediaId = "test-media-id";
             controls.playFromMediaId(mediaId, extras);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnPlayFromMediaIdCalled);
@@ -374,6 +392,12 @@
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnSetShuffleModeEnabledCalled);
             assertEquals(ENABLED, mCallback.mShuffleModeEnabled);
+
+            mCallback.reset();
+            controls.setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_ALL);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSetShuffleModeCalled);
+            assertEquals(PlaybackStateCompat.SHUFFLE_MODE_ALL, mCallback.mShuffleMode);
         }
     }
 
@@ -444,8 +468,10 @@
         private boolean mCaptioningEnabled;
         private int mRepeatMode;
         private boolean mShuffleModeEnabled;
+        private int mShuffleMode;
         private int mQueueIndex;
         private MediaDescriptionCompat mQueueDescription;
+        private List<MediaSessionCompat.QueueItem> mQueue = new ArrayList<>();
 
         private boolean mOnPlayCalled;
         private boolean mOnPauseCalled;
@@ -469,10 +495,10 @@
         private boolean mOnSetCaptioningEnabledCalled;
         private boolean mOnSetRepeatModeCalled;
         private boolean mOnSetShuffleModeEnabledCalled;
+        private boolean mOnSetShuffleModeCalled;
         private boolean mOnAddQueueItemCalled;
         private boolean mOnAddQueueItemAtCalled;
         private boolean mOnRemoveQueueItemCalled;
-        private boolean mOnRemoveQueueItemAtCalled;
 
         public void reset() {
             mSeekPosition = -1;
@@ -488,6 +514,7 @@
             mCaptioningEnabled = false;
             mShuffleModeEnabled = false;
             mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
+            mShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
             mQueueIndex = -1;
             mQueueDescription = null;
 
@@ -513,10 +540,10 @@
             mOnSetCaptioningEnabledCalled = false;
             mOnSetRepeatModeCalled = false;
             mOnSetShuffleModeEnabledCalled = false;
+            mOnSetShuffleModeCalled = false;
             mOnAddQueueItemCalled = false;
             mOnAddQueueItemAtCalled = false;
             mOnRemoveQueueItemCalled = false;
-            mOnRemoveQueueItemAtCalled = false;
         }
 
         @Override
@@ -594,6 +621,16 @@
         }
 
         @Override
+        public void onSetRating(RatingCompat rating, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnSetRatingCalled = true;
+                mRating = rating;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
         public void onPlayFromMediaId(String mediaId, Bundle extras) {
             synchronized (mWaitLock) {
                 mOnPlayFromMediaIdCalled = true;
@@ -705,6 +742,8 @@
             synchronized (mWaitLock) {
                 mOnAddQueueItemCalled = true;
                 mQueueDescription = description;
+                mQueue.add(new MediaSessionCompat.QueueItem(description, mQueue.size()));
+                mSession.setQueue(mQueue);
                 mWaitLock.notify();
             }
         }
@@ -715,6 +754,8 @@
                 mOnAddQueueItemAtCalled = true;
                 mQueueIndex = index;
                 mQueueDescription = description;
+                mQueue.add(index, new MediaSessionCompat.QueueItem(description, mQueue.size()));
+                mSession.setQueue(mQueue);
                 mWaitLock.notify();
             }
         }
@@ -723,7 +764,14 @@
         public void onRemoveQueueItem(MediaDescriptionCompat description) {
             synchronized (mWaitLock) {
                 mOnRemoveQueueItemCalled = true;
-                mQueueDescription = description;
+                String mediaId = description.getMediaId();
+                for (int i = mQueue.size() - 1; i >= 0; --i) {
+                    if (mediaId.equals(mQueue.get(i).getDescription().getMediaId())) {
+                        mQueueDescription = mQueue.remove(i).getDescription();
+                        mSession.setQueue(mQueue);
+                        break;
+                    }
+                }
                 mWaitLock.notify();
             }
         }
@@ -747,10 +795,10 @@
         }
 
         @Override
-        public void onRemoveQueueItemAt(int index) {
+        public void onSetShuffleMode(int shuffleMode) {
             synchronized (mWaitLock) {
-                mOnRemoveQueueItemAtCalled = true;
-                mQueueIndex = index;
+                mOnSetShuffleModeCalled = true;
+                mShuffleMode = shuffleMode;
                 mWaitLock.notify();
             }
         }
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
index fabe72d..b44a085 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
@@ -18,6 +18,7 @@
 
 import static android.support.test.InstrumentationRegistry.getContext;
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_RATING;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -30,8 +31,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -51,14 +52,18 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test {@link MediaSessionCompat}.
  */
 @RunWith(AndroidJUnit4.class)
 public class MediaSessionCompatTest {
-    // The maximum time to wait for an operation.
+    // The maximum time to wait for an operation, that is expected to happen.
     private static final long TIME_OUT_MS = 3000L;
+    // The maximum time to wait for an operation, that is expected not to happen.
+    private static final long WAIT_TIME_MS = 30L;
     private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
     private static final String TEST_SESSION_TAG = "test-session-tag";
     private static final String TEST_KEY = "test-key";
@@ -120,6 +125,30 @@
     }
 
     /**
+     * Tests that a session can be created from the framework session object and the callback
+     * set on the framework session object before fromSession() is called works properly.
+     */
+    @Test
+    @SmallTest
+    public void testFromSession() throws Exception {
+        if (android.os.Build.VERSION.SDK_INT < 21) {
+            // MediaSession was introduced from API level 21.
+            return;
+        }
+        MediaSessionCallback callback = new MediaSessionCallback();
+        callback.reset(1);
+        mSession.setCallback(callback, new Handler(Looper.getMainLooper()));
+        MediaSessionCompat session = MediaSessionCompat.fromMediaSession(
+                getContext(), mSession.getMediaSession());
+        assertEquals(session.getSessionToken(), mSession.getSessionToken());
+        synchronized (mWaitLock) {
+            session.getController().getTransportControls().play();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(1, callback.mOnPlayCalledCount);
+        }
+    }
+
+    /**
      * Tests MediaSessionCompat.Token created in the constructor of MediaSessionCompat.
      */
     @Test
@@ -189,8 +218,12 @@
         controller.registerCallback(mCallback, mHandler);
         synchronized (mWaitLock) {
             mCallback.resetLocked();
-            MediaMetadataCompat metadata =
-                    new MediaMetadataCompat.Builder().putString(TEST_KEY, TEST_VALUE).build();
+            RatingCompat rating = RatingCompat.newHeartRating(true);
+            MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
+                    .putString(TEST_KEY, TEST_VALUE)
+                    .putRating(METADATA_KEY_RATING, rating)
+                    .build();
+            mSession.setActive(true);
             mSession.setMetadata(metadata);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnMetadataChangedCalled);
@@ -202,10 +235,73 @@
             metadataOut = controller.getMetadata();
             assertNotNull(metadataOut);
             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+            assertNotNull(metadataOut.getRating(METADATA_KEY_RATING));
+            RatingCompat ratingOut = metadataOut.getRating(METADATA_KEY_RATING);
+            assertEquals(rating.getRatingStyle(), ratingOut.getRatingStyle());
+            assertEquals(rating.getPercentRating(), ratingOut.getPercentRating(), 0.0f);
         }
     }
 
     /**
+     * Tests {@link MediaSessionCompat#setMetadata} with artwork bitmaps.
+     */
+    @Test
+    @SmallTest
+    public void testSetMetadataWithArtworks() throws Exception {
+        MediaControllerCompat controller = mSession.getController();
+        final Bitmap bitmapSmall = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        final Bitmap bitmapLarge = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ALPHA_8);
+
+        controller.registerCallback(mCallback, mHandler);
+        mSession.setActive(true);
+        synchronized (mWaitLock) {
+            mCallback.resetLocked();
+            MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
+                    .putString(TEST_KEY, TEST_VALUE)
+                    .putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmapSmall)
+                    .build();
+            mSession.setMetadata(metadata);
+            mWaitLock.wait(TIME_OUT_MS);
+
+            assertTrue(mCallback.mOnMetadataChangedCalled);
+            MediaMetadataCompat metadataOut = mCallback.mMediaMetadata;
+            assertNotNull(metadataOut);
+            assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+            Bitmap bitmapSmallOut = metadataOut.getBitmap(MediaMetadataCompat.METADATA_KEY_ART);
+            assertNotNull(bitmapSmallOut);
+            assertEquals(bitmapSmall.getHeight(), bitmapSmallOut.getHeight());
+            assertEquals(bitmapSmall.getWidth(), bitmapSmallOut.getWidth());
+            assertEquals(bitmapSmall.getConfig(), bitmapSmallOut.getConfig());
+
+            metadata = new MediaMetadataCompat.Builder()
+                    .putString(TEST_KEY, TEST_VALUE)
+                    .putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmapLarge)
+                    .build();
+            mSession.setMetadata(metadata);
+            mWaitLock.wait(TIME_OUT_MS);
+
+            assertTrue(mCallback.mOnMetadataChangedCalled);
+            metadataOut = mCallback.mMediaMetadata;
+            assertNotNull(metadataOut);
+            assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+            Bitmap bitmapLargeOut = metadataOut.getBitmap(MediaMetadataCompat.METADATA_KEY_ART);
+            assertNotNull(bitmapLargeOut);
+            // Don't check size here because large bitmaps can be scaled down.
+            assertEquals(bitmapLarge.getConfig(), bitmapLargeOut.getConfig());
+
+            assertFalse(bitmapSmall.isRecycled());
+            assertFalse(bitmapLarge.isRecycled());
+            assertFalse(bitmapSmallOut.isRecycled());
+            assertFalse(bitmapLargeOut.isRecycled());
+            bitmapSmallOut.recycle();
+            bitmapLargeOut.recycle();
+        }
+        bitmapSmall.recycle();
+        bitmapLarge.recycle();
+    }
+
+    /**
      * Tests {@link MediaSessionCompat#setPlaybackState}.
      */
     @Test
@@ -362,13 +458,32 @@
             mCallback.resetLocked();
             mSession.setShuffleModeEnabled(shuffleModeEnabled);
             mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mCallback.mOnShuffleModeChangedCalled);
+            assertTrue(mCallback.mOnShuffleModeChangedDeprecatedCalled);
             assertEquals(shuffleModeEnabled, mCallback.mShuffleModeEnabled);
             assertEquals(shuffleModeEnabled, controller.isShuffleModeEnabled());
         }
     }
 
     /**
+     * Tests {@link MediaSessionCompat#setShuffleMode}.
+     */
+    @Test
+    @SmallTest
+    public void testSetShuffleMode() throws Exception {
+        final int shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_ALL;
+        MediaControllerCompat controller = mSession.getController();
+        controller.registerCallback(mCallback, mHandler);
+        synchronized (mWaitLock) {
+            mCallback.resetLocked();
+            mSession.setShuffleMode(shuffleMode);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnShuffleModeChangedCalled);
+            assertEquals(shuffleMode, mCallback.mShuffleMode);
+            assertEquals(shuffleMode, controller.getShuffleMode());
+        }
+    }
+
+    /**
      * Tests {@link MediaSessionCompat#sendSessionEvent}.
      */
     @Test
@@ -485,71 +600,138 @@
         PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0);
         mSession.setMediaButtonReceiver(pi);
 
-        long supportedActions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE
+        // Set state to STATE_PLAYING to get higher priority.
+        setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertEquals(1, sessionCallback.mOnPlayCalledCount);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PAUSE);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnPauseCalled);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_NEXT);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnSkipToNextCalled);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnSkipToPreviousCalled);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnStopCalled);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnFastForwardCalled);
+
+        sessionCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_REWIND);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnRewindCalled);
+
+        // Test PLAY_PAUSE button twice.
+        // First, send PLAY_PAUSE button event while in STATE_PAUSED.
+        sessionCallback.reset(1);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertEquals(1, sessionCallback.mOnPlayCalledCount);
+
+        // Next, send PLAY_PAUSE button event while in STATE_PLAYING.
+        sessionCallback.reset(1);
+        setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertTrue(sessionCallback.mOnPauseCalled);
+
+        // Double tap of PLAY_PAUSE is the next track.
+        sessionCallback.reset(2);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertFalse(sessionCallback.await(WAIT_TIME_MS));
+        assertTrue(sessionCallback.mOnSkipToNextCalled);
+        assertEquals(0, sessionCallback.mOnPlayCalledCount);
+        assertFalse(sessionCallback.mOnPauseCalled);
+
+        // Test PLAY_PAUSE button long-press.
+        // It should be the same as the single short-press.
+        sessionCallback.reset(1);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertEquals(1, sessionCallback.mOnPlayCalledCount);
+
+        // Double tap of PLAY_PAUSE should be handled once.
+        // Initial down event from the second press within double tap time-out will make
+        // onSkipToNext() to be called, so further down events shouldn't be handled again.
+        sessionCallback.reset(2);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
+        assertFalse(sessionCallback.await(WAIT_TIME_MS));
+        assertTrue(sessionCallback.mOnSkipToNextCalled);
+        assertEquals(0, sessionCallback.mOnPlayCalledCount);
+        assertFalse(sessionCallback.mOnPauseCalled);
+
+        // Test PLAY_PAUSE button short-press followed by the long-press.
+        // Initial long-press of the PLAY_PAUSE is considered as the single short-press already,
+        // so it shouldn't be used as the first tap of the double tap.
+        sessionCallback.reset(2);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        // onMediaButtonEvent() calls either onPlay() or onPause() depending on the playback state,
+        // so onPlay() should be called twice while onPause() isn't called.
+        assertEquals(1, sessionCallback.mOnPlayCalledCount);
+        assertTrue(sessionCallback.mOnPauseCalled);
+        assertFalse(sessionCallback.mOnSkipToNextCalled);
+
+        // If another media key is pressed while the double tap of PLAY_PAUSE,
+        // PLAY_PAUSE should be handles as normal.
+        sessionCallback.reset(3);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertFalse(sessionCallback.mOnSkipToNextCalled);
+        assertTrue(sessionCallback.mOnStopCalled);
+        assertEquals(2, sessionCallback.mOnPlayCalledCount);
+
+        // Test if media keys are handled in order.
+        sessionCallback.reset(2);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+        assertTrue(sessionCallback.await(TIME_OUT_MS));
+        assertEquals(1, sessionCallback.mOnPlayCalledCount);
+        assertTrue(sessionCallback.mOnStopCalled);
+        synchronized (mWaitLock) {
+            assertEquals(PlaybackStateCompat.STATE_STOPPED,
+                    mSession.getController().getPlaybackState().getState());
+        }
+    }
+
+    private void setPlaybackState(int state) {
+        final long allActions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE
                 | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP
                 | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
                 | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
                 | PlaybackStateCompat.ACTION_FAST_FORWARD | PlaybackStateCompat.ACTION_REWIND;
-
-        // Set state to STATE_PLAYING to get higher priority.
-        PlaybackStateCompat defaultState = new PlaybackStateCompat.Builder()
-                .setActions(supportedActions)
-                .setState(PlaybackStateCompat.STATE_PLAYING, 0L, 0.0f)
-                .build();
-        mSession.setPlaybackState(defaultState);
-
+        PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder().setActions(allActions)
+                .setState(state, 0L, 0.0f).build();
         synchronized (mWaitLock) {
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnPlayCalled);
-
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PAUSE);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnPauseCalled);
-
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_NEXT);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnSkipToNextCalled);
-
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnSkipToPreviousCalled);
-
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnStopCalled);
-
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnFastForwardCalled);
-
-            sessionCallback.reset();
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_REWIND);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnRewindCalled);
-
-            // Test PLAY_PAUSE button twice.
-            // First, send PLAY_PAUSE button event while in STATE_PAUSED.
-            sessionCallback.reset();
-            mSession.setPlaybackState(new PlaybackStateCompat.Builder().setActions(supportedActions)
-                    .setState(PlaybackStateCompat.STATE_PAUSED, 0L, 0.0f).build());
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnPlayCalled);
-
-            // Next, send PLAY_PAUSE button event while in STATE_PLAYING.
-            sessionCallback.reset();
-            mSession.setPlaybackState(new PlaybackStateCompat.Builder().setActions(supportedActions)
-                    .setState(PlaybackStateCompat.STATE_PLAYING, 0L, 0.0f).build());
-            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(sessionCallback.mOnPauseCalled);
+            mSession.setPlaybackState(playbackState);
         }
     }
 
@@ -625,9 +807,23 @@
     }
 
     private void sendMediaKeyInputToController(int keyCode) {
+        sendMediaKeyInputToController(keyCode, false);
+    }
+
+    private void sendMediaKeyInputToController(int keyCode, boolean isLongPress) {
         MediaControllerCompat controller = mSession.getController();
-        controller.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
-        controller.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
+        long currentTimeMs = System.currentTimeMillis();
+        KeyEvent down = new KeyEvent(
+                currentTimeMs, currentTimeMs, KeyEvent.ACTION_DOWN, keyCode, 0);
+        controller.dispatchMediaButtonEvent(down);
+        if (isLongPress) {
+            KeyEvent longPress = new KeyEvent(
+                    currentTimeMs, System.currentTimeMillis(), KeyEvent.ACTION_DOWN, keyCode, 1);
+            controller.dispatchMediaButtonEvent(longPress);
+        }
+        KeyEvent up = new KeyEvent(
+                currentTimeMs, System.currentTimeMillis(), KeyEvent.ACTION_UP, keyCode, 0);
+        controller.dispatchMediaButtonEvent(up);
     }
 
     private class MediaControllerCallback extends MediaControllerCompat.Callback {
@@ -641,6 +837,7 @@
         private volatile boolean mOnSessionEventCalled;
         private volatile boolean mOnCaptioningEnabledChangedCalled;
         private volatile boolean mOnRepeatModeChangedCalled;
+        private volatile boolean mOnShuffleModeChangedDeprecatedCalled;
         private volatile boolean mOnShuffleModeChangedCalled;
 
         private volatile PlaybackStateCompat mPlaybackState;
@@ -653,6 +850,7 @@
         private volatile boolean mCaptioningEnabled;
         private volatile int mRepeatMode;
         private volatile boolean mShuffleModeEnabled;
+        private volatile int mShuffleMode;
 
         public void resetLocked() {
             mOnPlaybackStateChangedCalled = false;
@@ -664,6 +862,7 @@
             mOnSessionDestroyedCalled = false;
             mOnSessionEventCalled = false;
             mOnRepeatModeChangedCalled = false;
+            mOnShuffleModeChangedDeprecatedCalled = false;
             mOnShuffleModeChangedCalled = false;
 
             mPlaybackState = null;
@@ -675,6 +874,7 @@
             mCaptioningEnabled = false;
             mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
             mShuffleModeEnabled = false;
+            mShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
         }
 
         @Override
@@ -770,15 +970,25 @@
         @Override
         public void onShuffleModeChanged(boolean enabled) {
             synchronized (mWaitLock) {
-                mOnShuffleModeChangedCalled = true;
+                mOnShuffleModeChangedDeprecatedCalled = true;
                 mShuffleModeEnabled = enabled;
                 mWaitLock.notify();
             }
         }
+
+        @Override
+        public void onShuffleModeChanged(int shuffleMode) {
+            synchronized (mWaitLock) {
+                mOnShuffleModeChangedCalled = true;
+                mShuffleMode = shuffleMode;
+                mWaitLock.notify();
+            }
+        }
     }
 
     private class MediaSessionCallback extends MediaSessionCompat.Callback {
-        private boolean mOnPlayCalled;
+        private CountDownLatch mLatch;
+        private int mOnPlayCalledCount;
         private boolean mOnPauseCalled;
         private boolean mOnStopCalled;
         private boolean mOnFastForwardCalled;
@@ -786,8 +996,9 @@
         private boolean mOnSkipToPreviousCalled;
         private boolean mOnSkipToNextCalled;
 
-        public void reset() {
-            mOnPlayCalled = false;
+        public void reset(int count) {
+            mLatch = new CountDownLatch(count);
+            mOnPlayCalledCount = 0;
             mOnPauseCalled = false;
             mOnStopCalled = false;
             mOnFastForwardCalled = false;
@@ -796,60 +1007,57 @@
             mOnSkipToNextCalled = false;
         }
 
+        public boolean await(long timeoutMs) {
+            try {
+                return mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+
         @Override
         public void onPlay() {
-            synchronized (mWaitLock) {
-                mOnPlayCalled = true;
-                mWaitLock.notify();
-            }
+            mOnPlayCalledCount++;
+            setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+            mLatch.countDown();
         }
 
         @Override
         public void onPause() {
-            synchronized (mWaitLock) {
-                mOnPauseCalled = true;
-                mWaitLock.notify();
-            }
+            mOnPauseCalled = true;
+            setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+            mLatch.countDown();
         }
 
         @Override
         public void onStop() {
-            synchronized (mWaitLock) {
-                mOnStopCalled = true;
-                mWaitLock.notify();
-            }
+            mOnStopCalled = true;
+            setPlaybackState(PlaybackStateCompat.STATE_STOPPED);
+            mLatch.countDown();
         }
 
         @Override
         public void onFastForward() {
-            synchronized (mWaitLock) {
-                mOnFastForwardCalled = true;
-                mWaitLock.notify();
-            }
+            mOnFastForwardCalled = true;
+            mLatch.countDown();
         }
 
         @Override
         public void onRewind() {
-            synchronized (mWaitLock) {
-                mOnRewindCalled = true;
-                mWaitLock.notify();
-            }
+            mOnRewindCalled = true;
+            mLatch.countDown();
         }
 
         @Override
         public void onSkipToPrevious() {
-            synchronized (mWaitLock) {
-                mOnSkipToPreviousCalled = true;
-                mWaitLock.notify();
-            }
+            mOnSkipToPreviousCalled = true;
+            mLatch.countDown();
         }
 
         @Override
         public void onSkipToNext() {
-            synchronized (mWaitLock) {
-                mOnSkipToNextCalled = true;
-                mWaitLock.notify();
-            }
+            mOnSkipToNextCalled = true;
+            mLatch.countDown();
         }
     }
 }
diff --git a/paging/common/build.gradle b/paging/common/build.gradle
new file mode 100644
index 0000000..4b0dad1
--- /dev/null
+++ b/paging/common/build.gradle
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'java'
+apply plugin: 'maven'
+
+sourceCompatibility = 1.7
+// disable paging for now.
+project.ext.noDocs = true
+uploadArchives.enabled = false
+dependencies {
+    compile libs.support.annotations
+    compile project(":arch:common")
+
+    testCompile libs.junit
+    testCompile libs.mockito_core
+    compile 'com.android.support:support-annotations:25.3.1'
+    compile project(path: ':arch:common')
+    compile 'junit:junit:4.12'
+}
+
+archivesBaseName = "common"
+
+createAndroidCheckstyle(project)
diff --git a/paging/common/src/main/java/android/arch/util/paging/CountedDataSource.java b/paging/common/src/main/java/android/arch/util/paging/CountedDataSource.java
new file mode 100644
index 0000000..dd7d41c
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/CountedDataSource.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+
+import java.util.List;
+
+/**
+ * Incremental data loader for paging content when the total number of items is known.
+ * <p>
+ * All loading methods in CountedDataSource should only be called on a background thread.
+ * loadAfter and loadBefore specifically may be called in parallel with each other, if loading in
+ * both directions is required (often for prefetching).
+ * <p>
+ * Items returned from a CountedDataSource must be treated as immutable in all properties used by
+ * {@link #loadAfter(int, Object, int)} and {@link #loadBefore(int, Object, int)}. For example, if
+ * loadAfter(...User user...) is called, with <code>user.id</code> being used to load users after
+ * that user's ID, the <code>id</code> field in User must remain immutable.
+ * <p>
+ * If the loadAfter and loadBefore methods only require indices to function, CountedDataSource does
+ * not require items returned to be immutable in any way.
+ *
+ * @param <Type> Type of the items this CountedDataSource will produce.
+ */
+public abstract class CountedDataSource<Type> extends DataSourceBase {
+    /**
+     * @return The number of items in the DataSource.
+     */
+    @WorkerThread
+    public abstract int loadCount();
+
+    /**
+     * Load initial data, starting after the passed position.
+     *
+     * @param position Position just before data to be loaded. -1 is passed to request data
+     *                 from beginning of data source.
+     * @param pageSize Suggested number of items to load.
+     * @return List of initial items, representing data starting at position + 1. Null if the
+     *         DataSource is no longer valid, and should not be queried again.
+     */
+    @WorkerThread
+    @Nullable
+    public abstract List<Type> loadAfterInitial(int position, int pageSize);
+
+    /**
+     * Load data after the given position / item.
+     * <p>
+     * It's valid to return a different list size than the page size, if it's easier for this data
+     * source. It is generally safer to increase number loaded than reduce.
+     *
+     * @param currentEndIndex Load items after this index, starting with currentEndIndex + 1.
+     * @param currentEndItem Load items after this item, can be used for precise querying based on
+     *                       item contents.
+     * @param pageSize Suggested number of items to load.
+     * @return List of items, starting at position currentEndIndex + 1. Null if the data source is
+     *         no longer valid, and should not be queried again.
+     */
+    @WorkerThread
+    @Nullable
+    public abstract List<Type> loadAfter(int currentEndIndex,
+            @NonNull Type currentEndItem, int pageSize);
+
+    /**
+     * Load data before the given position / item.
+     * <p>
+     * It's valid to return a different list size than the page size, if it's easier for this data
+     * source. It is generally safer to increase number loaded than reduce.
+     *
+     * @param currentBeginIndex Load items before this index, starting with currentBeginIndex - 1.
+     * @param currentBeginItem Load items after this item, can be used for precise querying based on
+     *                         item contents.
+     * @param pageSize Suggested number of items to load.
+     * @return List of items, in descending order, starting at position currentBeginIndex - 1.
+     */
+    @WorkerThread
+    @Nullable
+    public abstract List<Type> loadBefore(int currentBeginIndex,
+            @NonNull Type currentBeginItem, int pageSize);
+}
diff --git a/paging/common/src/main/java/android/arch/util/paging/DataSource.java b/paging/common/src/main/java/android/arch/util/paging/DataSource.java
new file mode 100644
index 0000000..e6af7e7
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/DataSource.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+
+import java.util.List;
+
+
+/**
+ * Incremental data loader for list paging.
+ *
+ * @param <Key> Type of the key used to load initial data.
+ * @param <Type> Type of the items this CountedDataSource will produce.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class DataSource<Key, Type> extends DataSourceBase {
+
+    /**
+     * Get key from item.
+     *
+     * @param item The item.
+     * @return The Key.
+     */
+    public abstract Key getKey(@NonNull Type item);
+
+    /**
+     * Load initial data, starting after the passed key.
+     *
+     * @param key Key just before the data to be loaded.
+     * @param pageSize Suggested number of items to load.
+     * @return List of initial items, representing data starting at key + 1. Null if the
+     *         DataSource is no longer valid, and should not be queried again.
+     */
+    @WorkerThread
+    @Nullable
+    public abstract List<Type> loadAfterInitial(@Nullable Key key, int pageSize);
+
+    /**
+     * Load data, starting after the passed item.
+     *
+     * @param currentEndItem Load items after this item, can be used for precise querying based on
+     *                       item contents.
+     * @param pageSize Suggested number of items to load.
+     * @return List of initial items, representing data starting at key + 1. Null if the
+     *         DataSource is no longer valid, and should not be queried again.
+     */
+    @WorkerThread
+    @Nullable
+    public abstract List<Type> loadAfter(@NonNull Type currentEndItem, int pageSize);
+
+    /**
+     * Load data, before the passed item.
+     *
+     * @param currentBeginItem Load items after this item, can be used for precise querying based on
+     *                         item contents.
+     * @param pageSize Suggested number of items to load.
+     * @return List of initial items, representing data starting at key + 1. Null if the
+     *         DataSource is no longer valid, and should not be queried again.
+     */
+    @WorkerThread
+    @Nullable
+    public abstract List<Type> loadBefore(@NonNull Type currentBeginItem, int pageSize);
+}
diff --git a/paging/common/src/main/java/android/arch/util/paging/DataSourceBase.java b/paging/common/src/main/java/android/arch/util/paging/DataSourceBase.java
new file mode 100644
index 0000000..50d245d
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/DataSourceBase.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.AnyThread;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Data Source base class, with common invalidation functionality.
+ */
+class DataSourceBase {
+    public interface InvalidatedCallback {
+        /*
+         * Called when the data backing the list has become invalid. This callback is typically used
+         * to signal that a new data source is needed, if the data source remains in use.
+         * <p>
+         * This callback will be invoked on the thread that calls {@link #invalidate()}. It is valid
+         * for the data source to invalidate itself during its load methods, or for an outside
+         * source to invalidate it.
+         */
+        @AnyThread
+        void onInvalidated();
+    }
+
+    private AtomicBoolean mInvalid = new AtomicBoolean(false);
+
+    private CopyOnWriteArrayList<InvalidatedCallback> mOnInvalidatedCallbacks =
+            new CopyOnWriteArrayList<>();
+    /**
+     * Add a callback to invoke when the DataSource is first invalidated.
+     * <p>
+     * Once invalidated, a data source will not become valid again.
+     * <p>
+     * A data source will only invoke its callbacks once - the first time {@link #invalidate()}
+     * is called, on that thread.
+     *
+     * @param onInvalidatedCallback The callback, will be invoked on thread that
+     *                              {@link #invalidate()} is called on.
+     */
+    @AnyThread
+    public void addInvalidatedCallback(InvalidatedCallback onInvalidatedCallback) {
+        mOnInvalidatedCallbacks.add(onInvalidatedCallback);
+    }
+
+    /**
+     * Remove a previously added invalidate callback.
+     *
+     * @param onInvalidatedCallback The previously added callback.
+     */
+    @AnyThread
+    public void removeInvalidatedCallback(InvalidatedCallback onInvalidatedCallback) {
+        mOnInvalidatedCallbacks.remove(onInvalidatedCallback);
+    }
+
+    /**
+     * Signal the data source to stop loading, and notify its callback.
+     * <p>
+     * If invalidate has already been called, this method does nothing.
+     */
+    @AnyThread
+    public void invalidate() {
+        if (mInvalid.compareAndSet(false, true)) {
+            for (InvalidatedCallback callback : mOnInvalidatedCallbacks) {
+                callback.onInvalidated();
+            }
+        }
+    }
+
+    /**
+     * @return True if the data source is invalid, and can no longer return data.
+     */
+    public boolean isInvalid() {
+        return mInvalid.get();
+    }
+}
diff --git a/paging/common/src/main/java/android/arch/util/paging/LazyList.java b/paging/common/src/main/java/android/arch/util/paging/LazyList.java
new file mode 100644
index 0000000..706df63
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/LazyList.java
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.arch.core.internal.SafeIterableMap;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Lazy-loading list, which defaults each entry to be null and pages content in on-demand,
+ * with prefetching.
+ *
+ * When a list item is read as null, content is paged in from the CountedDataSource asynchronously.
+ *
+ * This class is NOT thread safe. All accesses to get()/access() and mutations should be made on the
+ * thread defined by the mainThreadExecutor.
+ *
+ * @param <Type> Data type held by this list.
+ */
+public class LazyList<Type> extends PagerBase<Type> {
+    private int mMissedMinIndex = Integer.MAX_VALUE;
+    private int mMissedMaxIndex = -1;
+
+    private int mStartPos; // position is inclusive
+    private final int mSize;
+
+    // last position accessed via get()
+    private volatile int mLastAccessed;
+
+    private SafeIterableMap<ChangeCallback,
+            ChangeCallbackWrapper> mCallbacks = new SafeIterableMap<>();
+
+    private CountedDataSource<Type> mCountedDataSource;
+
+    @WorkerThread
+    public LazyList(@NonNull CountedDataSource<Type> countedDataSource,
+            @NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor,
+            @NonNull ListConfig config) {
+        super(mainThreadExecutor, backgroundThreadExecutor, config);
+        mCountedDataSource = countedDataSource;
+        mSize = mCountedDataSource.loadCount();
+    }
+
+    // ---- List ----
+
+    @Override
+    @Nullable
+    public Type get(int index) {
+        if (index < 0 || index >= mSize) {
+            throw new IndexOutOfBoundsException(index + " is out of bounds. Size:" + mSize);
+        }
+
+        mLastAccessed = index;
+
+        final boolean initialLoadComplete = mItems != null;
+
+        // note: intentionally avoid triggering prefetching, so that prefetches aren't outstanding
+        // when LazyList is passed to consumers
+
+        // prefetch
+        int minTarget = Math.max(0, index - mConfig.mPrefetchDistance);
+        int maxTarget = Math.min(mSize, index + mConfig.mPrefetchDistance);
+        if (minTarget < mMissedMinIndex) {
+            mMissedMinIndex = minTarget;
+            if (initialLoadComplete) {
+                loadBeforeIfNeeded();
+            }
+        }
+        if (maxTarget > mMissedMaxIndex) {
+            mMissedMaxIndex = maxTarget;
+            if (initialLoadComplete) {
+                loadAfterIfNeeded();
+            }
+        }
+
+        if (initialLoadComplete) {
+            return access(index);
+        } else {
+            triggerInitialLoad(getInitialLoadPositionFor(index));
+            return null;
+        }
+    }
+
+    @Override
+    public int size() {
+        return mSize;
+    }
+
+    @Override
+    @Nullable
+    public Type access(int index) {
+        if (index < 0 || index >= mSize) {
+            throw new IndexOutOfBoundsException(index + " is out of bounds. Size:" + mSize);
+        }
+        if (mItems == null || index < mStartPos || index >= mStartPos + mItems.size()) {
+            return null;
+        } else {
+            return mItems.get(index - mStartPos);
+        }
+    }
+
+    // ---- Initial data ----
+
+    private int getInitialLoadPositionFor(int index) {
+        return Math.max(-1, index - mConfig.mPageSize / 2);
+    }
+
+    /**
+     * @return Suggested initial load position for a new LazyList to load the initial state at.
+     */
+    public int getInitialLoadPosition() {
+        return getInitialLoadPositionFor(mLastAccessed);
+    }
+
+    @MainThread
+    private void triggerInitialLoad(final int loadAfterPos) {
+        if (mInitialized) {
+            return;
+        }
+        mInitialized = true;
+        mBackgroundThreadExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (mInvalid.get()) {
+                    return;
+                }
+                // find a good start point to center the index
+                final List<Type> load = mCountedDataSource.loadAfterInitial(
+                        loadAfterPos, mConfig.mPageSize);
+
+                if (load != null) {
+                    mMainThreadExecutor.execute(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (mInvalid.get()) {
+                                return;
+                            }
+                            setInitialDataAndDispatch(loadAfterPos, load);
+                        }
+                    });
+                } else {
+                    freeze();
+                }
+            }
+        });
+    }
+
+    /**
+     *
+     * @param loadAfterPos Position to pass to {@link CountedDataSource#loadAfterInitial(int, int)}
+     *                     for warmup.
+     * @return True on success.
+     */
+    public boolean internalInit(int loadAfterPos)  {
+        List<Type> load = mCountedDataSource.loadAfterInitial(loadAfterPos, mConfig.mPageSize);
+        if (load != null) {
+            setInitialDataAndDispatch(loadAfterPos, load);
+            mInitialized = true;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void setInitialDataAndDispatch(int loadAfterPos, List<Type> items) {
+        mStartPos = loadAfterPos + 1;
+        setInitialData(items);
+        dispatchChange();
+    }
+
+    // ---- Callback ----
+
+    /**
+     * Adds a change callback, to observe when new content is loaded into the list. The callback is
+     * invoked once nulls in the list are loaded from the data source.
+     *
+     * @param callback The callback, invoked on the main thread.
+     *
+     * @see #removeCallback(ChangeCallback)
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void addCallback(ChangeCallback callback) {
+        int start = mStartPos;
+        int end = mStartPos + (mItems == null ? 0 : mItems.size());
+        ChangeCallbackWrapper wrapper = new ChangeCallbackWrapper(callback, start, end);
+        // do not notify the latest data so the observer can distinguish whether the data has
+        // really changed or not.
+        mCallbacks.putIfAbsent(callback, wrapper);
+    }
+
+    /**
+     * Removes a previously added change callback.
+     *
+     * @param callback The callback, invoked on the main thread.
+     *
+     * @see #addCallback(ChangeCallback)
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void removeCallback(ChangeCallback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    private void dispatchChange() {
+        int start = mStartPos;
+        int end = mStartPos + mItems.size();
+        for (Map.Entry<ChangeCallback, ChangeCallbackWrapper> mCallback : mCallbacks) {
+            mCallback.getValue().dispatchLoaded(start, end);
+        }
+    }
+
+    /**
+     * Callback that signals when new content is loaded in the list.
+     */
+    public abstract static class ChangeCallback {
+        /**
+         * Called when new content is loaded into the LazyList.
+         *
+         * @param start Start index of the newly loaded items.
+         * @param count Number of newly loaded items.
+         */
+        public abstract void onLoaded(int start, int count);
+    }
+
+    private static class ChangeCallbackWrapper {
+        private final ChangeCallback mChangeCallback;
+        private int mKnownStart;
+        private int mKnownEnd;
+
+        ChangeCallbackWrapper(@NonNull ChangeCallback changeCallback,
+                int knownStart, int knownEnd) {
+            mChangeCallback = changeCallback;
+            mKnownStart = knownStart;
+            mKnownEnd = knownEnd;
+        }
+
+        void dispatchLoaded(int currentStart, int currentEnd) {
+            if (currentEnd > mKnownEnd) {
+                mChangeCallback.onLoaded(mKnownEnd, currentEnd - mKnownEnd);
+                mKnownEnd = currentEnd;
+            }
+            if (currentStart < mKnownStart) {
+                mChangeCallback.onLoaded(currentStart, mKnownStart - currentStart);
+                mKnownStart = currentStart;
+            }
+        }
+    }
+
+
+    // ---- PagerBase implementation ----
+
+    @Override
+    void onItemsPrepended(int count) {
+        mStartPos -= count;
+        dispatchChange();
+    }
+
+    @Override
+    void onItemsAppended(int count) {
+        dispatchChange();
+    }
+
+    @Override
+    void loadBeforeIfNeeded() {
+        if (mMissedMinIndex >= mStartPos) {
+            // done loading
+            mMissedMinIndex = Integer.MAX_VALUE;
+        } else {
+            // not done, resume loading
+            loadBefore(mStartPos);
+        }
+    }
+
+    @Override
+    void loadAfterIfNeeded() {
+        if (mMissedMaxIndex <= mStartPos + mItems.size() - 1) {
+            // done loading
+            mMissedMaxIndex = 0;
+        } else {
+            // not done, resume loading
+            loadAfter(mStartPos + mItems.size() - 1);
+        }
+    }
+
+    @Nullable
+    @Override
+    List<Type> loadBeforeImpl(int position, Type item) {
+        return mCountedDataSource.loadBefore(position, item, mConfig.mPageSize);
+    }
+
+    @Nullable
+    @Override
+    List<Type> loadAfterImpl(int position, Type item) {
+        return mCountedDataSource.loadAfter(position, item, mConfig.mPageSize);
+    }
+}
diff --git a/paging/common/src/main/java/android/arch/util/paging/ListConfig.java b/paging/common/src/main/java/android/arch/util/paging/ListConfig.java
new file mode 100644
index 0000000..8c3777c
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/ListConfig.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+/**
+ * Configuration object for list paging. Used to inform how and when a {@link LazyList} loads
+ * content from a {@link CountedDataSource}.
+ */
+public class ListConfig {
+    final int mPageSize;
+    final int mPrefetchDistance;
+
+    ListConfig(int pageSize, int prefetchDistance) {
+        mPageSize = pageSize;
+        mPrefetchDistance = prefetchDistance;
+    }
+
+    /**
+     * Creates a {@link Builder} that can be used to construct a {@link ListConfig}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder class for {@link ListConfig}.
+     * <p>
+     * You must at minimum specify a {@link Builder#pageSize(int)}.
+     */
+    public static class Builder {
+        private int mPageSize = -1;
+        private int mPrefetchDistance = -1;
+
+        /**
+         * Defines the number of items loaded at once from the DataSource.
+         * <p>
+         * Should be several times the number of visible items onscreen.
+         */
+        public Builder pageSize(int pageSize) {
+            this.mPageSize = pageSize;
+            return this;
+        }
+
+        /**
+         * Defines how far from the edge of loaded content an access must be to trigger further
+         * loading. Defaults to page size.
+         * <p>
+         * A value of 0 indicates that no list items will be loaded before they are first requested.
+         * <p>
+         * Should be several times the number of visible items onscreen.
+         */
+        public Builder prefetchDistance(int prefetchDistance) {
+            this.mPrefetchDistance = prefetchDistance;
+            return this;
+        }
+
+        /**
+         * Creates a {@link ListConfig} with the given parameters.
+         *
+         * @return A new ListConfig.
+         */
+        public ListConfig create() {
+            if (mPageSize < 1) {
+                throw new IllegalArgumentException("Page size must be a positive number");
+            }
+            if (mPrefetchDistance < 0) {
+                mPrefetchDistance = mPageSize;
+            }
+            return new ListConfig(mPageSize, mPrefetchDistance);
+        }
+    }
+}
diff --git a/paging/common/src/main/java/android/arch/util/paging/PagedList.java b/paging/common/src/main/java/android/arch/util/paging/PagedList.java
new file mode 100644
index 0000000..283b062
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/PagedList.java
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.arch.core.internal.SafeIterableMap;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Lazy loading list that starts at size 0, and expands its size by loading pages
+ * asynchronously from a DataSource.
+ *
+ * @param <Type> Data type held by this list.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class PagedList<Type> extends PagerBase<Type> {
+    private static final int INVALID_DATA_POSITION = -1;
+
+    // Distance from front of list that should be prefetched.
+    private int mMinTarget = 0;
+
+    // Distance from tail of list that should be prefetched.
+    private int mMaxTarget = 0;
+
+    /**
+     * Stores the delta between the anchor and the most recently accessed item.
+     *
+     * The most recently accessed item's index can be referenced
+     * by adding this value to {@link #mAnchor}.
+     */
+    private volatile int mInterestedKeyOffset = -1;
+
+    // anchor position used to calculate deltas based on what revision observers last saw
+    private int mAnchor = 0;
+
+    private DataSource<Object, Type> mDataSource;
+
+    private SafeIterableMap<ChangeCallback,
+                ChangeCallbackWrapper> mCallbacks = new SafeIterableMap<>();
+
+    @WorkerThread
+    public <Key> PagedList(@NonNull DataSource<Key, Type> dataSource,
+            @NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor,
+            @NonNull ListConfig config) {
+        super(mainThreadExecutor, backgroundThreadExecutor, config);
+        if (config.mPrefetchDistance <= 0) {
+            // TODO: catch this in PagedListAdapterHelper too
+            throw new IllegalArgumentException("PagedList requires a positive prefetch distance"
+                    + " to be able to trigger page loading.");
+        }
+
+        mDataSource = (DataSource<Object, Type>) dataSource;
+    }
+
+    // ---- List ----
+
+    @Override
+    @NonNull
+    public Type get(int index) {
+        if (index < 0 || index >= size()) {
+            throw new IllegalArgumentException();
+        }
+        final int prefetchDistance = mConfig.mPrefetchDistance;
+        int minTarget = prefetchDistance - index;
+        if (minTarget > mMinTarget) {
+            mMinTarget = minTarget;
+            loadBeforeIfNeeded();
+        }
+        int maxTarget = index + prefetchDistance - size();
+        if (maxTarget > mMaxTarget) {
+            mMaxTarget = maxTarget;
+            loadAfterIfNeeded();
+        }
+
+        mInterestedKeyOffset = index - mAnchor;
+
+        return mItems.get(index);
+    }
+
+    @Override
+    @NonNull
+    public Type access(int index) {
+        if (index < 0 || index >= size()) {
+            throw new IllegalArgumentException();
+        }
+        return mItems.get(index);
+    }
+
+    @Override
+    public int size() {
+        return mItems == null ? 0 : mItems.size();
+    }
+
+
+    // ---- Initial data ----
+
+    /**
+     * Post a background task to execute loadAfter(null), and initialize the PagedList
+     * with its result.
+     */
+    @MainThread
+    public <Key> void triggerInitialLoad(final Key initialLoadKey) {
+        if (mInitialized) {
+            return;
+        }
+        mInitialized = true;
+        mBackgroundThreadExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (mInvalid.get()) {
+                    return;
+                }
+
+                final List<Type> initialData =
+                        mDataSource.loadAfterInitial(initialLoadKey, mConfig.mPageSize);
+                if (initialData != null) {
+                    mMainThreadExecutor.execute(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (mInvalid.get()) {
+                                return;
+                            }
+                            setInitialDataAndDispatch(initialData);
+                        }
+                    });
+                } else {
+                    freeze();
+                }
+            }
+        });
+    }
+
+    /**
+     * Initialize the PagedList from the most-recently-accessed position information from the 'old'
+     * PagedList.
+     *
+     * This may be called on a background thread, and will only access immutable-once-invalid state
+     * in the old list, plus the volatile mInterestedKeyOffset field.
+     */
+    @WorkerThread
+    public boolean initializeFrom(PagedList<Type> old) {
+        // Note: even though old may be actively used on the foreground thread, we carefully only
+        // access parts that are immutable once old is invalid:
+        // mAnchor, and mItems (since no further prepends/appends can occur)
+
+        int targetIndex = (old.mAnchor + old.mInterestedKeyOffset);
+        int loadAfterIndex = Math.max(0, targetIndex - mConfig.mPageSize / 2) - 1;
+
+        Object loadAfterKey = null;
+        if (loadAfterIndex >= 0) {
+            Type loadAfterItem = old.mItems.get(loadAfterIndex);
+            loadAfterKey = mDataSource.getKey(loadAfterItem);
+        }
+
+        List<Type> initialData =
+                mDataSource.loadAfterInitial(loadAfterKey, mConfig.mPageSize);
+        if (initialData != null) {
+            setInitialDataAndDispatch(initialData);
+            mInitialized = true;
+            return true;
+        } else {
+            freeze();
+            return false;
+        }
+    }
+
+    // ---- Callback ----
+
+    private void setInitialDataAndDispatch(List<Type> items) {
+        // NOTE: while it's possible to prefetch given initial data, but we defer first prefetch
+        // until a consumer triggers it, so that the list is immutable until get() is called.
+
+        setInitialData(items);
+        dispatchChange();
+    }
+
+    /**
+     * Adds a change callback, to observe when new content is loaded into the list. The callback is
+     * invoked when the list has newly loaded content prepended, or appended.
+     *
+     * @param callback The callback, invoked on the main thread.
+     *
+     * @see #removeCallback(ChangeCallback)
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void addCallback(ChangeCallback callback) {
+        ChangeCallbackWrapper wrapper = new ChangeCallbackWrapper(callback, mAnchor, size());
+        // do not notify the latest data so the observer can distinguish whether the data has
+        // really changed or not.
+        mCallbacks.putIfAbsent(callback, wrapper);
+    }
+
+    /**
+     * Removes a previously added change callback.
+     *
+     * @param callback The callback, invoked on the main thread.
+     *
+     * @see #addCallback(ChangeCallback)
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void removeCallback(ChangeCallback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    private void dispatchChange() {
+        int anchor = mAnchor;
+        int size = mItems.size();
+        for (Map.Entry<ChangeCallback, ChangeCallbackWrapper> mCallback : mCallbacks) {
+            mCallback.getValue().dispatchLoaded(anchor, size);
+        }
+    }
+
+    /**
+     * Callback that signals when new content is loaded in the list.
+     */
+    public abstract static class ChangeCallback {
+        /**
+         * Called when new content is inserted into the PagedList.
+         *
+         * @param start Index where the items have been inserted.
+         * @param count Number of newly inserted items.
+         */
+        public abstract void onInserted(int start, int count);
+    }
+
+    static class ChangeCallbackWrapper {
+        final ChangeCallback mChangeCallback;
+        private int mKnownAnchor;
+        private int mKnownSize;
+
+        ChangeCallbackWrapper(@NonNull ChangeCallback changeCallback,
+                int knownAnchor, int knownSize) {
+            mChangeCallback = changeCallback;
+            mKnownAnchor = knownAnchor;
+            mKnownSize = knownSize;
+        }
+
+        void dispatchLoaded(int currentAnchor, int currentSize) {
+            if (currentAnchor > mKnownAnchor) {
+                int itemsAddedToFront = currentAnchor - mKnownAnchor;
+                mChangeCallback.onInserted(0, itemsAddedToFront);
+                mKnownSize += itemsAddedToFront;
+                mKnownAnchor = currentAnchor;
+            }
+            if (mKnownSize < currentSize) {
+                int itemsAddedToBack = currentSize - mKnownSize;
+                mChangeCallback.onInserted(mKnownSize, itemsAddedToBack);
+                mKnownSize += itemsAddedToBack;
+            }
+        }
+    }
+
+
+    // ---- PagerBase implementation ----
+
+    @Override
+    void onItemsPrepended(int count) {
+        if (mMinTarget > 0) {
+            mMinTarget -= count;
+        }
+        mAnchor += count;
+        dispatchChange();
+    }
+
+    @Override
+    void onItemsAppended(int count) {
+        if (mMaxTarget > 0) {
+            mMaxTarget -= count;
+        }
+        dispatchChange();
+    }
+
+    @Override
+    void loadBeforeIfNeeded() {
+        if (mMinTarget > 0) {
+            loadBefore(INVALID_DATA_POSITION);
+        }
+    }
+
+    @Override
+    void loadAfterIfNeeded() {
+        if (mMaxTarget > 0) {
+            loadAfter(INVALID_DATA_POSITION);
+        }
+    }
+
+    @Nullable
+    @Override
+    List<Type> loadBeforeImpl(int position, Type item) {
+        return mDataSource.loadBefore(item, mConfig.mPageSize);
+    }
+
+    @Nullable
+    @Override
+    List<Type> loadAfterImpl(int position, Type item) {
+        return mDataSource.loadAfter(item, mConfig.mPageSize);
+    }
+}
diff --git a/paging/common/src/main/java/android/arch/util/paging/PagerBase.java b/paging/common/src/main/java/android/arch/util/paging/PagerBase.java
new file mode 100644
index 0000000..d53446d
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/util/paging/PagerBase.java
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @param <Type> Data type held by this list.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class PagerBase<Type> {
+    List<Type> mItems;
+
+    private boolean mLoadBefore = false;
+    private boolean mLoadAfter = false;
+
+    /**
+     * Gets an item from the specified index, and loads content appropriately as a side effect.
+     * Must be between 0 and {@link #size()}.
+     *
+     * @param index Index of item to return.
+     * @return Item requested.
+     */
+    public abstract Type get(int index);
+
+    /**
+     * Gets an item from the specified index, but without side effects. Must be between 0 and
+     * {@link #size()}.
+     *
+     * @param index Index of item to return.
+     * @return Item requested.
+     */
+    public abstract Type access(int index);
+
+    /**
+     * @return Size of list.
+     */
+    public abstract int size();
+
+    boolean mInitialized;
+
+    @NonNull
+    final Executor mMainThreadExecutor;
+    @NonNull
+    final Executor mBackgroundThreadExecutor;
+
+    @Nullable
+    abstract List<Type> loadBeforeImpl(int position, Type item);
+
+    @Nullable
+    abstract List<Type> loadAfterImpl(int position, Type item);
+
+    // trigger loadBefore if positions loaded after/before aren't good enough
+    abstract void loadBeforeIfNeeded();
+
+    abstract void loadAfterIfNeeded();
+
+    // used to communicated to list provider to issue a new version
+    abstract void onItemsPrepended(int count);
+
+    abstract void onItemsAppended(int count);
+
+    AtomicBoolean mInvalid = new AtomicBoolean(false);
+    final ListConfig mConfig;
+
+    @WorkerThread
+    PagerBase(@NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor,
+            ListConfig configuration) {
+        mConfig = configuration;
+        mMainThreadExecutor = mainThreadExecutor;
+        mBackgroundThreadExecutor = backgroundThreadExecutor;
+    }
+
+    void freeze() {
+        mInvalid.set(true);
+    }
+
+    void setInitialData(List<Type> items) {
+        mItems = new ArrayList<>(items);
+        if (mItems.size() == 0) {
+            // No data loaded, so display no data, and don't try to load more.
+            mLoadBefore = mLoadAfter = true;
+        }
+        loadAfterIfNeeded();
+        loadBeforeIfNeeded();
+    }
+
+    @MainThread
+    protected void loadBefore(final int position) {
+        if (mLoadBefore) {
+            return;
+        }
+        mLoadBefore = true;
+
+        final Type item = mItems.get(0);
+        mBackgroundThreadExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+
+                if (mInvalid.get()) {
+                    return;
+                }
+
+                final List<Type> data = loadBeforeImpl(position, item);
+                if (data != null) {
+                    mMainThreadExecutor.execute(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (mInvalid.get()) {
+                                return;
+                            }
+                            prepend(data);
+                        }
+                    });
+                } else {
+                    freeze();
+                }
+            }
+        });
+    }
+
+    @MainThread
+    protected void loadAfter(final int position) {
+        if (mLoadAfter) {
+            return;
+        }
+        mLoadAfter = true;
+
+        final Type item = mItems.get(mItems.size() - 1);
+        mBackgroundThreadExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (mInvalid.get()) {
+                    return;
+                }
+
+                final List<Type> data = loadAfterImpl(position, item);
+                if (data != null) {
+                    mMainThreadExecutor.execute(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (mInvalid.get()) {
+                                return;
+                            }
+                            append(data);
+                        }
+                    });
+                } else {
+                    freeze();
+                }
+            }
+        });
+    }
+
+    @MainThread
+    private void prepend(List<Type> before) {
+        final int count = before.size();
+        if (count == 0) {
+            // Nothing returned from source, stop loading in this direction
+            return;
+        }
+
+        Collections.reverse(before);
+        mItems.addAll(0, before);
+        onItemsPrepended(count);
+
+        mLoadBefore = false;
+        loadBeforeIfNeeded();
+    }
+
+    @MainThread
+    private void append(List<Type> after) {
+        final int count = after.size();
+        if (count == 0) {
+            // Nothing returned from source, stop loading in this direction
+            return;
+        }
+
+        mItems.addAll(after);
+        onItemsAppended(count);
+
+        mLoadAfter = false;
+        loadAfterIfNeeded();
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/util/paging/LazyListTest.java b/paging/common/src/test/java/android/arch/util/paging/LazyListTest.java
new file mode 100644
index 0000000..7bede2e
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/util/paging/LazyListTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+@RunWith(JUnit4.class)
+public class LazyListTest {
+    private TestExecutor mMainThread = new TestExecutor();
+    private TestExecutor mBackgroundThread = new TestExecutor();
+    private TestCountedDataSource mDataSource;
+
+    private LazyList.ChangeCallback mChangeCallback = Mockito.mock(LazyList.ChangeCallback.class);
+
+    private LazyList<User> createLazyList(int pageSize, int prefetchCount) {
+        LazyList<User> lazyList = new LazyList<>(mDataSource, mMainThread, mBackgroundThread,
+                 new ListConfig(pageSize, prefetchCount));
+        lazyList.addCallback(mChangeCallback);
+        return lazyList;
+    }
+
+    @Before
+    public void setup() {
+        mDataSource = new TestCountedDataSource();
+    }
+
+    @Test
+    public void initial() {
+        LazyList<User> lazyList = createLazyList(20, 5);
+        assertNull(lazyList.get(0));
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 20);
+
+        lazyList = createLazyList(10, 5);
+        assertNull(lazyList.get(0));
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 10);
+    }
+
+    @Test
+    public void initialReturnsEmpty() {
+        mDataSource.setCount(200);
+        LazyList<User> lazyList = createLazyList(20, 5);
+        assertEquals(200, lazyList.size());
+
+
+        // trigger initial load that returns nothing
+        mDataSource.setCount(0);
+        lazyList.get(0);
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 0);
+
+        // now further accesses don't trigger loads
+        mDataSource.setCount(200);
+        lazyList.get(100);
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 0);
+    }
+
+    @Test
+    public void initialPrefetch() {
+        LazyList<User> lazyList = createLazyList(20, 20);
+        assertNull(lazyList.get(9));
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 40);
+    }
+
+    @Test
+    public void initialPrefetchMultiple() {
+        LazyList<User> lazyList = createLazyList(20, 90);
+        lazyList.get(0);
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 100);
+    }
+
+    @Test
+    public void incrementalLoading() {
+        LazyList<User> lazyList = createLazyList(20, 20);
+        lazyList.get(0);
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 40);
+
+        lazyList.get(39);
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 60);
+
+        lazyList.get(41);
+        drain();
+        TestCountedDataSource.verifyRange(lazyList, 0, 80);
+    }
+
+    private static <T> List<T> reverse(List<T> input) {
+        ArrayList<T> result = new ArrayList<>(input);
+        Collections.reverse(result);
+        return result;
+    }
+
+    private void drain() {
+        boolean executed;
+        do {
+            executed = mBackgroundThread.executeAll();
+            executed |= mMainThread.executeAll();
+        } while (executed);
+
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/util/paging/PagedListTest.java b/paging/common/src/test/java/android/arch/util/paging/PagedListTest.java
new file mode 100644
index 0000000..8cd4665
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/util/paging/PagedListTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import static junit.framework.TestCase.fail;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class PagedListTest {
+    private TestExecutor mMainThread = new TestExecutor();
+    private TestExecutor mBackgroundThread = new TestExecutor();
+    private TestDataSource mDataSource = new TestDataSource();
+
+    private PagedList.ChangeCallback mChangeCallback = Mockito.mock(PagedList.ChangeCallback.class);
+
+    @Test
+    public void callbackSimple() {
+        PagedList.ChangeCallback callback = Mockito.mock(PagedList.ChangeCallback.class);
+        PagedList.ChangeCallbackWrapper wrapper =
+                new PagedList.ChangeCallbackWrapper(callback, 0, 0);
+
+        wrapper.dispatchLoaded(0, 10);
+        verify(callback).onInserted(0, 10);
+        verifyNoMoreInteractions(callback);
+
+        wrapper.dispatchLoaded(0, 10);
+        verifyNoMoreInteractions(callback);
+
+        wrapper.dispatchLoaded(0, 20);
+        verify(callback).onInserted(10, 10);
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void callbackComplex() {
+        PagedList.ChangeCallback callback = Mockito.mock(PagedList.ChangeCallback.class);
+        PagedList.ChangeCallbackWrapper wrapper =
+                new PagedList.ChangeCallbackWrapper(callback, 0, 0);
+
+        wrapper.dispatchLoaded(0, 8);
+        verify(callback).onInserted(0, 8);
+        verifyNoMoreInteractions(callback);
+
+        wrapper.dispatchLoaded(10, 18);
+        verify(callback).onInserted(0, 10);
+        verifyNoMoreInteractions(callback);
+
+        wrapper.dispatchLoaded(10, 22);
+        verify(callback).onInserted(18, 4);
+        verifyNoMoreInteractions(callback);
+
+        // Note: prepend before append is impl detail
+        wrapper.dispatchLoaded(12, 25);
+        verify(callback).onInserted(0, 2);
+        verify(callback).onInserted(24, 1);
+        verifyNoMoreInteractions(callback);
+    }
+
+    private PagedList<User> createPagedList(int pageSize, int prefetchDistance) {
+        PagedList<User> pagedList = new PagedList<>(
+                mDataSource, mMainThread, mBackgroundThread,
+                new ListConfig(pageSize, prefetchDistance));
+        pagedList.addCallback(mChangeCallback);
+        return pagedList;
+    }
+
+    private PagedList<User> createPagedListInitializedFrom(PagedList<User> old) {
+        PagedList<User> pagedList = new PagedList<>(
+                mDataSource, mMainThread, mBackgroundThread,
+                old.mConfig);
+        if (!pagedList.initializeFrom(old)) {
+            fail();
+        }
+        return pagedList;
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void requirePrefetch() {
+        createPagedList(20, 0);
+    }
+
+    @Test
+    public void initial() {
+        PagedList<User> pagedList = createPagedList(20, 5);
+        pagedList.triggerInitialLoad(null);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 20);
+
+        pagedList = createPagedList(10, 5);
+        pagedList.triggerInitialLoad(null);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 10);
+    }
+
+    @Test
+    public void initialPrefetch() {
+        PagedList<User> pagedList = createPagedList(20, 20);
+        pagedList.triggerInitialLoad(null);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 20);
+
+        // Note: pagedList doesn't prefetch after initial load until first get call
+        pagedList.get(10);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 40);
+    }
+
+    @Test
+    public void initialPrefetchMultiple() {
+        PagedList<User> pagedList = createPagedList(20, 90);
+        pagedList.triggerInitialLoad(null);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 20);
+
+        // Note: pagedList doesn't prefetch after initial load until first get call
+        pagedList.get(0);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 100);
+    }
+
+    @Test
+    public void incrementalLoading() {
+        PagedList<User> pagedList = createPagedList(20, 20);
+        pagedList.triggerInitialLoad(null);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 20);
+
+        pagedList.get(5);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 40);
+
+        pagedList.get(39);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 60);
+
+        pagedList.get(41);
+        drain();
+        TestDataSource.verifyRange(pagedList, 0, 80);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void get_tooEarly() {
+        PagedList<User> pagedList = createPagedList(20, 20);
+        pagedList.triggerInitialLoad(null);
+        assertEquals(0, pagedList.size());
+        pagedList.get(0); // task hasn't run, zero size currently
+    }
+
+    @Test
+    public void initializeFrom() {
+        PagedList<User> pagedList = createPagedList(20, 20);
+        pagedList.triggerInitialLoad(null);
+        drain();
+    }
+
+    private void drain() {
+        boolean executed;
+        do {
+            executed = mBackgroundThread.executeAll();
+            executed |= mMainThread.executeAll();
+        } while (executed);
+
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/util/paging/TestCountedDataSource.java b/paging/common/src/test/java/android/arch/util/paging/TestCountedDataSource.java
new file mode 100644
index 0000000..04ad89a
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/util/paging/TestCountedDataSource.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestCountedDataSource extends CountedDataSource<User> {
+    private static final ArrayList<User> sUsers = TestDataSource.sUsers;
+
+    private int mCount = sUsers.size();
+
+    static void verifyRange(LazyList<User> list, int start, int end) {
+        assertEquals("size should be same", end - start, list.mItems.size());
+        for (int i = 0; i < list.mItems.size(); i++) {
+            // NOTE: avoid getter, to avoid signaling
+            assertSame(sUsers.get(start + i), list.mItems.get(i));
+        }
+    }
+
+    public void setCount(int count) {
+        assertTrue(count <= sUsers.size());
+        mCount = count;
+    }
+
+    @Override
+    public int loadCount() {
+        return mCount;
+    }
+
+    private List<User> getClampedRange(int start, int end) {
+        start = Math.max(0, start);
+        end = Math.min(loadCount(), end);
+        return sUsers.subList(start, end);
+    }
+
+    @Nullable
+    @Override
+    public List<User> loadAfterInitial(int position, int pageSize) {
+        return getClampedRange(position + 1, position + 1 + pageSize);
+    }
+
+    @Nullable
+    @Override
+    public List<User> loadAfter(int currentEndIndex, @NonNull User currentEndItem, int pageSize) {
+        return getClampedRange(currentEndIndex + 1, currentEndIndex + 1 + pageSize);
+    }
+
+    @Nullable
+    @Override
+    public List<User> loadBefore(int currentBeginIndex, @NonNull User currentBeginItem,
+            int pageSize) {
+        return getClampedRange(currentBeginIndex - 1 - pageSize, currentBeginIndex);
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/util/paging/TestDataSource.java b/paging/common/src/test/java/android/arch/util/paging/TestDataSource.java
new file mode 100644
index 0000000..11fd2eb
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/util/paging/TestDataSource.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestDataSource extends DataSource<String, User> {
+    static final ArrayList<User> sUsers = new ArrayList<>();
+    static {
+        for (int i = 0; i < 300; i++) {
+            sUsers.add(new User(String.format("%03d", i), "Usernr " + i));
+        }
+    }
+
+    static void verifyRange(PagedList<User> list, int start, int end) {
+        assertEquals(end - start, list.size());
+        for (int i = 0; i < list.size(); i++) {
+            // NOTE: avoid getter, to avoid signaling
+            assertSame(sUsers.get(start + i), list.mItems.get(i));
+        }
+    }
+
+    @Override
+    public String getKey(@NonNull User item) {
+        return item.name;
+    }
+
+    @Nullable
+    @Override
+    public List<User> loadAfterInitial(@Nullable String itemName, int pageSize) {
+        if (itemName == null) {
+            itemName = "";
+        }
+        ArrayList<User> users = new ArrayList<>();
+        int index;
+        for (index = 0; index < sUsers.size(); index++) {
+            if (itemName.compareTo(sUsers.get(index).name) < 0) {
+                break;
+            }
+        }
+        for (int i = 0; i < pageSize && index + i < sUsers.size(); i++) {
+            users.add(sUsers.get(index + i));
+        }
+        return users;
+    }
+
+    @Nullable
+    @Override
+    public List<User> loadAfter(@NonNull User currentEndItem, int pageSize) {
+        return loadAfterInitial(currentEndItem.name, pageSize);
+    }
+
+    @Nullable
+    @Override
+    public List<User> loadBefore(@NonNull User currentBeginItem, int pageSize) {
+        ArrayList<User> users = new ArrayList<>();
+        int index;
+        for (index = sUsers.size() - 1; index >= 0; index--) {
+            if (currentBeginItem.name.compareTo(sUsers.get(index).name) > 0) {
+                break;
+            }
+        }
+        for (int i = 0; i < pageSize && index - i >= 0; i++) {
+            users.add(sUsers.get(index - i));
+        }
+        return users;
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/util/paging/TestExecutor.java b/paging/common/src/test/java/android/arch/util/paging/TestExecutor.java
new file mode 100644
index 0000000..6a3f188
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/util/paging/TestExecutor.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.NonNull;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+
+public class TestExecutor implements Executor {
+    private Queue<Runnable> mTasks = new LinkedList<>();
+
+    @Override
+    public void execute(@NonNull Runnable command) {
+        mTasks.add(command);
+    }
+
+    boolean executeAll() {
+        boolean consumed = !mTasks.isEmpty();
+        Runnable task;
+        while ((task = mTasks.poll()) != null) {
+            task.run();
+        }
+        return consumed;
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/util/paging/User.java b/paging/common/src/test/java/android/arch/util/paging/User.java
new file mode 100644
index 0000000..f486955
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/util/paging/User.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+public class User {
+    public final String name;
+    public final String info;
+
+    public User(String name, String info) {
+        this.name = name;
+        this.info = info;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}
diff --git a/paging/integration-tests/testapp/.gitignore b/paging/integration-tests/testapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/paging/integration-tests/testapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..5356dd4
--- /dev/null
+++ b/paging/integration-tests/testapp/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+apply plugin: 'com.android.application'
+
+project.ext.noDocs = true
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+    }
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile project(':arch:runtime')
+    compile project(':arch:common')
+    compile project(':paging:common')
+    compile project(':lifecycle:extensions')
+    compile project(':lifecycle:runtime')
+    compile project(':lifecycle:common')
+    compile project(':paging:runtime')
+    compile 'com.android.support:multidex:1.0.1'
+
+    compile libs.support.recyclerview
+    compile libs.support.app_compat
+}
+
+createAndroidCheckstyle(project)
+tasks['check'].dependsOn(tasks['connectedCheck'])
+
+uploadArchives.enabled = false
diff --git a/paging/integration-tests/testapp/proguard-rules.pro b/paging/integration-tests/testapp/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/paging/integration-tests/testapp/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/paging/integration-tests/testapp/src/main/AndroidManifest.xml b/paging/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fc86e8d
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.paging.integration.testapp">
+    <application
+        android:allowBackup="true"
+        android:supportsRtl="true">
+        <activity
+            android:name=".lazylist.LazyListRecyclerViewActivity"
+            android:label="LazyList"
+            android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".pagedlist.PagedListRecyclerViewActivity"
+            android:label="PagedList"
+            android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+  </manifest>
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/Item.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/Item.java
new file mode 100644
index 0000000..0a7b0f0
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/Item.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp;
+
+import android.arch.util.paging.DiffCallback;
+import android.support.annotation.NonNull;
+
+/**
+ * Sample item.
+ */
+public class Item {
+    public final int id;
+    public final String text;
+    public final int bgColor;
+
+    public Item(int id, String text, int bgColor) {
+        this.id = id;
+        this.text = text;
+        this.bgColor = bgColor;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Item item = (Item) o;
+        return this.id == item.id
+                && this.bgColor == item.bgColor
+                && this.text.equals(item.text);
+    }
+
+    public static final DiffCallback<Item> DIFF_CALLBACK = new DiffCallback<Item>() {
+        @Override
+        public boolean areContentsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
+            return oldItem.equals(newItem);
+        }
+
+        @Override
+        public boolean areItemsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
+            return oldItem.id == newItem.id;
+        }
+    };
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/ItemCountedDataSource.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/ItemCountedDataSource.java
new file mode 100644
index 0000000..89dd844
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/ItemCountedDataSource.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp;
+
+import android.arch.util.paging.CountedDataSource;
+import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sample data source with artificial data.
+ */
+public class ItemCountedDataSource extends CountedDataSource<Item> {
+    @ColorInt
+    private static final int[] COLORS = new int[] {
+            Color.RED,
+            Color.BLUE,
+            Color.BLACK,
+    };
+
+    private static int sGenerationId;
+    private final int mGenerationId = sGenerationId++;
+
+    @Override
+    public int loadCount() {
+        return 10000;
+    }
+
+    @Nullable
+    @Override
+    public List<Item> loadAfterInitial(int position, int pageSize) {
+        return createItems(position + 1, pageSize, 1);
+    }
+
+    @Nullable
+    @Override
+    public List<Item> loadAfter(int currentEndIndex, @NonNull Item currentEndItem, int pageSize) {
+        return createItems(currentEndIndex + 1, pageSize, 1);
+    }
+
+    @Nullable
+    @Override
+    public List<Item> loadBefore(int currentBeginIndex, @NonNull Item currentBeginItem,
+            int pageSize) {
+        return createItems(currentBeginIndex - 1, pageSize, -1);
+    }
+
+    @Nullable
+    private List<Item> createItems(int start, int count, int direction) {
+        if (isInvalid()) {
+            // abort!
+            return null;
+        }
+
+        List<Item> items = new ArrayList<>();
+        int end = Math.max(-1, Math.min(loadCount(), start + direction * count));
+        int bgColor = COLORS[mGenerationId % COLORS.length];
+
+        try {
+            Thread.sleep(300);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        for (int i = start; i != end; i += direction) {
+            items.add(new Item(i, "item " + i, bgColor));
+        }
+
+        if (isInvalid()) {
+            // abort!
+            return null;
+        }
+        return items;
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/ItemDataSource.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/ItemDataSource.java
new file mode 100644
index 0000000..cbfc247
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/ItemDataSource.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp;
+
+import android.arch.util.paging.DataSource;
+import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sample data source with artificial data.
+ */
+public class ItemDataSource extends DataSource<Integer, Item> {
+    private static final int COUNT = 10000;
+
+    @ColorInt
+    private static final int[] COLORS = new int[] {
+            Color.RED,
+            Color.BLUE,
+            Color.BLACK,
+    };
+
+
+    private static int sGenerationId;
+    private final int mGenerationId = sGenerationId++;
+
+    @Override
+    public Integer getKey(@NonNull Item item) {
+        return item.id;
+    }
+
+    @Nullable
+    @Override
+    public List<Item> loadAfterInitial(@Nullable Integer position, int pageSize) {
+        if (position == null) {
+            position = -1;
+        }
+        return createItems(position + 1, pageSize, 1);
+    }
+
+    @Nullable
+    @Override
+    public List<Item> loadAfter(@NonNull Item currentEndItem, int pageSize) {
+        return createItems(currentEndItem.id + 1, pageSize, 1);
+    }
+
+    @Nullable
+    @Override
+    public List<Item> loadBefore(@NonNull Item currentBeginItem, int pageSize) {
+        return createItems(currentBeginItem.id - 1, pageSize, -1);
+    }
+
+    private List<Item> createItems(int start, int count, int direction) {
+        if (isInvalid()) {
+            // abort!
+            return null;
+        }
+
+        List<Item> items = new ArrayList<>();
+        int end = Math.max(-1, Math.min(COUNT, start + direction * count));
+        int bgColor = COLORS[mGenerationId % COLORS.length];
+
+        try {
+            Thread.sleep(300);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        for (int i = start; i != end; i += direction) {
+            items.add(new Item(i, "item " + i, bgColor));
+        }
+
+        if (isInvalid()) {
+            // abort!
+            return null;
+        }
+        return items;
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListItemAdapter.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListItemAdapter.java
new file mode 100644
index 0000000..f24b717
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListItemAdapter.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp.lazylist;
+
+import android.arch.paging.integration.testapp.Item;
+import android.arch.util.paging.LazyList;
+import android.arch.util.paging.LazyListAdapterHelper;
+import android.graphics.Color;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Sample LazyList adapter, which uses a LazyListAdapterHelper.
+ */
+public class LazyListItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+    private final LazyListAdapterHelper<Item> mHelper;
+
+    LazyListItemAdapter() {
+        mHelper = LazyListAdapterHelper.<Item>builder()
+                .adapter(this)
+                .diffCallback(Item.DIFF_CALLBACK)
+                .create();
+    }
+
+    void setLazyList(LazyList<Item> list) {
+        mHelper.setLazyList(list);
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        return new RecyclerView.ViewHolder(new TextView(parent.getContext())) {
+        };
+    }
+
+    @Override
+    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        Item item = mHelper.get(position);
+        ((TextView) (holder.itemView)).setText(item == null ? "loading" : item.text);
+        holder.itemView.setBackgroundColor(item == null ? Color.TRANSPARENT : item.bgColor);
+        holder.itemView.setMinimumHeight(400);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mHelper.getItemCount();
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListItemViewModel.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListItemViewModel.java
new file mode 100644
index 0000000..3d0b3b4
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListItemViewModel.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp.lazylist;
+
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.ViewModel;
+import android.arch.paging.integration.testapp.Item;
+import android.arch.paging.integration.testapp.ItemCountedDataSource;
+import android.arch.util.paging.CountedDataSource;
+import android.arch.util.paging.LazyList;
+import android.arch.util.paging.ListConfig;
+import android.arch.util.paging.LiveLazyListProvider;
+
+/**
+ * Sample ViewModel backed by an artificial data source
+ */
+public class LazyListItemViewModel extends ViewModel {
+    private final LiveData<LazyList<Item>> mLiveLazyList;
+    private ItemCountedDataSource mDataSource;
+
+    public LazyListItemViewModel() {
+        mLiveLazyList = new LiveLazyListProvider<Item>() {
+            @Override
+            protected CountedDataSource<Item> createDataSource() {
+                mDataSource = new ItemCountedDataSource();
+                return mDataSource;
+            }
+        }.create(ListConfig.builder().pageSize(20).prefetchDistance(40).create());
+    }
+
+    void invalidateList() {
+        if (mDataSource != null) {
+            mDataSource.invalidate();
+        }
+    }
+
+    LiveData<LazyList<Item>> getLazyList() {
+        return mLiveLazyList;
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListRecyclerViewActivity.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListRecyclerViewActivity.java
new file mode 100644
index 0000000..3e499d3
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/lazylist/LazyListRecyclerViewActivity.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp.lazylist;
+
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LifecycleRegistryOwner;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.arch.paging.integration.testapp.Item;
+import android.arch.paging.integration.testapp.R;
+import android.arch.util.paging.LazyList;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * Sample LazyList activity.
+ */
+public class LazyListRecyclerViewActivity extends AppCompatActivity
+        implements LifecycleRegistryOwner {
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final LazyListItemViewModel viewModel = ViewModelProviders.of(this)
+                .get(LazyListItemViewModel.class);
+        setContentView(R.layout.activity_recycler_view);
+
+        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
+        final LazyListItemAdapter adapter = new LazyListItemAdapter();
+
+
+        // TODO: helper initialization, with owner, recyclerview, LiveData<LazyList<Item>>
+        viewModel.getLazyList().observe(this, new Observer<LazyList<Item>>() {
+            @Override
+            public void onChanged(@Nullable LazyList<Item> itemLazyList) {
+                adapter.setLazyList(itemLazyList);
+                if (recyclerView.getAdapter() == null) {
+                    recyclerView.setAdapter(adapter);
+                }
+            }
+        });
+        final Button button = (Button) findViewById(R.id.button);
+        button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                viewModel.invalidateList();
+            }
+        });
+    }
+
+    private LifecycleRegistry  mLifecycleRegistry = new LifecycleRegistry(this);
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mLifecycleRegistry;
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListItemAdapter.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListItemAdapter.java
new file mode 100644
index 0000000..3f41267
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListItemAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp.pagedlist;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.paging.integration.testapp.Item;
+import android.arch.util.paging.PagedList;
+import android.arch.util.paging.PagedListAdapterHelper;
+import android.arch.util.paging.PagerBaseAdapterHelper;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Sample PagedList adapter, which uses a PagedListAdapterHelper.
+ */
+public class PagedListItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private final PagedListAdapterHelper<Item> mHelper;
+
+    PagedListItemAdapter() {
+        mHelper = new PagedListAdapterHelper<>(
+                AppToolkitTaskExecutor.getMainThreadExecutor(),
+                AppToolkitTaskExecutor.getIOThreadExecutor(),
+                new PagerBaseAdapterHelper.AdapterCallback(this),
+                Item.DIFF_CALLBACK);
+    }
+
+    void setPagedList(PagedList<Item> list) {
+        mHelper.setPagedList(list);
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        RecyclerView.ViewHolder holder = new RecyclerView.ViewHolder(
+                new TextView(parent.getContext())) {};
+        holder.itemView.setMinimumHeight(400);
+        return holder;
+    }
+
+    @Override
+    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        Item item = mHelper.get(position);
+        ((TextView) (holder.itemView)).setText(item.text);
+        holder.itemView.setBackgroundColor(item.bgColor);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mHelper.getItemCount();
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListItemViewModel.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListItemViewModel.java
new file mode 100644
index 0000000..e8fe13f
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListItemViewModel.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp.pagedlist;
+
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.ViewModel;
+import android.arch.paging.integration.testapp.Item;
+import android.arch.paging.integration.testapp.ItemDataSource;
+import android.arch.util.paging.DataSource;
+import android.arch.util.paging.ListConfig;
+import android.arch.util.paging.LivePagedListProvider;
+import android.arch.util.paging.PagedList;
+
+/**
+ * Sample ViewModel backed by an artificial data source
+ */
+public class PagedListItemViewModel extends ViewModel {
+    private final LiveData<PagedList<Item>> mLivePagedList;
+    private ItemDataSource mDataSource;
+
+    public PagedListItemViewModel() {
+        mLivePagedList = new LivePagedListProvider<Integer, Item>() {
+            @Override
+            protected DataSource<Integer, Item> createDataSource() {
+                mDataSource = new ItemDataSource();
+                return mDataSource;
+            }
+        }.create(ListConfig.builder().pageSize(20).prefetchDistance(40).create());
+    }
+
+    void invalidateList() {
+        if (mDataSource != null) {
+            mDataSource.invalidate();
+        }
+    }
+
+    LiveData<PagedList<Item>> getPagedList() {
+        return mLivePagedList;
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListRecyclerViewActivity.java b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListRecyclerViewActivity.java
new file mode 100644
index 0000000..28e70f5
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/java/android/arch/paging/integration/testapp/pagedlist/PagedListRecyclerViewActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.arch.paging.integration.testapp.pagedlist;
+
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LifecycleRegistryOwner;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.arch.paging.integration.testapp.Item;
+import android.arch.paging.integration.testapp.R;
+import android.arch.util.paging.PagedList;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * Sample PagedList activity.
+ */
+public class PagedListRecyclerViewActivity extends AppCompatActivity
+        implements LifecycleRegistryOwner {
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final PagedListItemViewModel viewModel = ViewModelProviders.of(this)
+                .get(PagedListItemViewModel.class);
+        setContentView(R.layout.activity_recycler_view);
+
+        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
+        final PagedListItemAdapter adapter = new PagedListItemAdapter();
+
+        // TODO: Create and use PagedListAdapterHelper.builder
+        viewModel.getPagedList().observe(this, new Observer<PagedList<Item>>() {
+            @Override
+            public void onChanged(@Nullable PagedList<Item> itemPagedList) {
+                adapter.setPagedList(itemPagedList);
+                if (recyclerView.getAdapter() == null && itemPagedList != null) {
+                    itemPagedList.triggerInitialLoad(null); // TODO: Persist
+                    recyclerView.setAdapter(adapter);
+                }
+            }
+        });
+        final Button button = (Button) findViewById(R.id.button);
+        button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                viewModel.invalidateList();
+            }
+        });
+    }
+
+    private LifecycleRegistry  mLifecycleRegistry = new LifecycleRegistry(this);
+
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mLifecycleRegistry;
+    }
+}
diff --git a/paging/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml b/paging/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
new file mode 100644
index 0000000..73a3e89
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
@@ -0,0 +1,43 @@
+<?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.
+  -->
+<RelativeLayout
+    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:id="@+id/activity_recycler_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".lazylist.LazyListRecyclerViewActivity">
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/recyclerview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layoutManager="LinearLayoutManager"
+        android:clipToPadding="false"
+        android:paddingBottom="@dimen/activity_vertical_margin"
+        android:paddingLeft="@dimen/activity_horizontal_margin"
+        android:paddingRight="@dimen/activity_horizontal_margin"
+        android:paddingTop="@dimen/activity_vertical_margin"
+    />
+    <Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:text="Update"/>
+</RelativeLayout>
diff --git a/paging/integration-tests/testapp/src/main/res/values-w820dp/dimens.xml b/paging/integration-tests/testapp/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..edff918
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,23 @@
+
+<!--
+~ 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>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/paging/integration-tests/testapp/src/main/res/values/dimens.xml b/paging/integration-tests/testapp/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..3358489
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+~ 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>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/paging/integration-tests/testapp/src/main/res/values/strings.xml b/paging/integration-tests/testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bed7093
--- /dev/null
+++ b/paging/integration-tests/testapp/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+<!--
+~ 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>
+</resources>
diff --git a/paging/runtime/build.gradle b/paging/runtime/build.gradle
new file mode 100644
index 0000000..ba3a764
--- /dev/null
+++ b/paging/runtime/build.gradle
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+// disable paging for now.
+project.ext.noDocs = true
+uploadArchives.enabled = false
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile project(":arch:runtime")
+    compile project(":paging:common")
+    compile project(":lifecycle:runtime")
+    compile project(':lifecycle:extensions')
+
+    compile libs.support.recyclerview
+    testCompile libs.junit
+    androidTestCompile libs.mockito_core
+    androidTestCompile libs.dexmaker_mockito
+    testCompile libs.support.annotations
+
+    androidTestCompile libs.junit
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+
+}
+
+archivesBaseName = "runtime"
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/paging/runtime/proguard-rules.pro b/paging/runtime/proguard-rules.pro
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/paging/runtime/proguard-rules.pro
@@ -0,0 +1 @@
+
diff --git a/paging/runtime/src/androidTest/java/android/arch/util/paging/LazyListAdapterHelperTest.java b/paging/runtime/src/androidTest/java/android/arch/util/paging/LazyListAdapterHelperTest.java
new file mode 100644
index 0000000..a8b9572
--- /dev/null
+++ b/paging/runtime/src/androidTest/java/android/arch/util/paging/LazyListAdapterHelperTest.java
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.v7.util.ListUpdateCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class LazyListAdapterHelperTest {
+    public class TestExecutor implements Executor {
+        private Queue<Runnable> mTasks = new LinkedList<>();
+        @Override
+        public void execute(@NonNull Runnable command) {
+            mTasks.add(command);
+        }
+
+        boolean executeAll() {
+            boolean consumed = !mTasks.isEmpty();
+            Runnable task;
+            while ((task = mTasks.poll()) != null) {
+                task.run();
+            }
+            return consumed;
+        }
+    }
+
+    private TestExecutor mMainThread = new TestExecutor();
+    private TestExecutor mBackgroundThread = new TestExecutor();
+    private CountedDataSource<Item> mDataSource = new CountedDataSource<Item>() {
+        @Override
+        public int loadCount() {
+            return mItemData.size();
+        }
+
+        private List<Item> getClampedRange(int start, int end) {
+            start = Math.max(0, start);
+            end = Math.min(loadCount(), end);
+            return mItemData.subList(start, end);
+        }
+
+        @Override
+        public List<Item> loadAfterInitial(int position, int pageSize) {
+            return getClampedRange(position + 1, position + pageSize + 1);
+        }
+
+        @Override
+        public List<Item> loadAfter(int currentEndIndex, @NonNull Item currentEndItem,
+                int pageSize) {
+            return getClampedRange(currentEndIndex + 1, currentEndIndex + 1 + pageSize);
+        }
+
+        @Override
+        public List<Item> loadBefore(int currentBeginIndex, @NonNull Item currentBeginItem,
+                int pageSize) {
+            return getClampedRange(currentBeginIndex - 1 - pageSize, currentBeginIndex);
+        }
+    };
+
+    private static class Item {
+        int mId = 0;
+        int mGeneration = 0;
+
+        static final DiffCallback<Item> DIFF_CALLBACK = new DiffCallback<Item>() {
+            @Override
+            public boolean areContentsTheSame(@NonNull Item oldItem,
+                    @NonNull Item newItem) {
+                return oldItem.mId == newItem.mId;
+            }
+
+            @Override
+            public boolean areItemsTheSame(@NonNull Item oldItem,
+                    @NonNull Item newItem) {
+                return oldItem.equals(newItem);
+            }
+        };
+    }
+
+    private List<Item> mItemData = new ArrayList<>();
+
+    @Before
+    public void setup() {
+        for (int i = 0; i < 100; i++) {
+            Item item = new Item();
+            item.mId = i;
+            item.mGeneration = 0;
+            mItemData.add(item);
+        }
+    }
+
+    private LazyList<Item> createLazyList() {
+        return new LazyList<>(mDataSource, mMainThread, mBackgroundThread,
+                ListConfig.builder().pageSize(10).prefetchDistance(10).create());
+    }
+
+    private LazyListAdapterHelper<Item> createHelper(ListUpdateCallback callback) {
+        return LazyListAdapterHelper.<Item>builder()
+                .mainThreadExecutor(mMainThread)
+                .backgroundThreadExecutor(mBackgroundThread)
+                .updateCallback(callback)
+                .diffCallback(Item.DIFF_CALLBACK).create();
+    }
+
+    @Test
+    public void create() {
+        ListUpdateCallback callback = Mockito.mock(ListUpdateCallback.class);
+        createHelper(callback);
+        verifyZeroInteractions(callback);
+    }
+
+    @Test
+    public void setEmpty() {
+        ListUpdateCallback callback = Mockito.mock(ListUpdateCallback.class);
+        LazyListAdapterHelper<Item> helper = createHelper(callback);
+        verifyZeroInteractions(callback);
+
+        LazyList<Item> list = createLazyList();
+        helper.setLazyList(list);
+        drain();
+        verify(callback).onInserted(0, 100);
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void setEmptyAndLoad() {
+        ListUpdateCallback callback = Mockito.mock(ListUpdateCallback.class);
+        LazyListAdapterHelper<Item> helper = createHelper(callback);
+        verifyZeroInteractions(callback);
+
+        LazyList<Item> list = createLazyList();
+        helper.setLazyList(list);
+        drain();
+        verify(callback).onInserted(0, 100);
+        verifyNoMoreInteractions(callback);
+
+        helper.get(0);
+        verifyNoMoreInteractions(callback);
+        drain();
+        verify(callback).onChanged(0, 10, null);
+        verify(callback).onChanged(10, 10, null);
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void setEmptyAndClear() {
+        ListUpdateCallback callback = Mockito.mock(ListUpdateCallback.class);
+        LazyListAdapterHelper<Item> helper = createHelper(callback);
+        verifyZeroInteractions(callback);
+
+        LazyList<Item> list = createLazyList();
+        helper.setLazyList(list);
+        drain();
+        verify(callback).onInserted(0, 100);
+        verifyNoMoreInteractions(callback);
+
+        helper.setLazyList(null);
+        verify(callback).onRemoved(0, 100);
+        drain();
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void setNonEmpty() {
+        ListUpdateCallback callback = Mockito.mock(ListUpdateCallback.class);
+        LazyListAdapterHelper<Item> helper = createHelper(callback);
+
+        LazyList<Item> list = createLazyList();
+        list.get(0);
+        drain();
+
+        verifyZeroInteractions(callback);
+
+        // setting doesn't trigger callbacks
+        helper.setLazyList(list);
+        drain();
+        verify(callback).onInserted(0, 100);
+        verifyNoMoreInteractions(callback);
+
+        // helper has correct data, but will trigger loads on access
+        assertNotNull(helper.get(19));
+        assertNull(helper.get(20));
+        verifyNoMoreInteractions(callback);
+        drain();
+        verify(callback).onChanged(20, 10, null);
+        verify(callback).onChanged(30, 10, null);
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void simpleDiffCalc() {
+        ListUpdateCallback callback = Mockito.mock(ListUpdateCallback.class);
+        LazyListAdapterHelper<Item> helper = createHelper(callback);
+        verifyZeroInteractions(callback);
+
+        LazyList<Item> list = createLazyList();
+        helper.setLazyList(list);
+        drain();
+        verify(callback).onInserted(0, 100);
+        verifyNoMoreInteractions(callback);
+
+        list = createLazyList();
+        list.get(0); // TODO: also test more interesting number
+        drain();
+        verifyNoMoreInteractions(callback);
+
+        helper.setLazyList(list);
+        verifyNoMoreInteractions(callback);
+        drain();
+
+        // NOTE: these aren't ideal, but they're what we currently produce.
+        // Should ideally be onChanged(0, 20)
+        verify(callback).onRemoved(80, 20);
+        verify(callback).onInserted(0, 20);
+        verifyNoMoreInteractions(callback);
+    }
+
+
+    private void drain() {
+        boolean executed;
+        do {
+            executed = mBackgroundThread.executeAll();
+            executed |= mMainThread.executeAll();
+        } while (executed);
+
+    }
+}
diff --git a/paging/runtime/src/main/AndroidManifest.xml b/paging/runtime/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..95247e5
--- /dev/null
+++ b/paging/runtime/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.util.paging.runtime">
+</manifest>
diff --git a/paging/runtime/src/main/java/android/arch/util/paging/DiffCallback.java b/paging/runtime/src/main/java/android/arch/util/paging/DiffCallback.java
new file mode 100644
index 0000000..de66424
--- /dev/null
+++ b/paging/runtime/src/main/java/android/arch/util/paging/DiffCallback.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.NonNull;
+
+/**
+ * Interface that informs {@link LazyListAdapterHelper} how to compute list updates when using
+ * {@link android.support.v7.util.DiffUtil} on a background thread.
+ * <p>
+ * The AdapterHelper will pass items from different lists to this callback in order to implement
+ * the {@link android.support.v7.util.DiffUtil.Callback} it uses to compute differences between
+ * lists.
+ *
+ * @param <Value> Type of items to compare.
+ */
+public abstract class DiffCallback<Value> {
+
+    /**
+     * Called to decide whether two objects represent the same item.
+     *
+     * @param oldItem The item in the old list.
+     * @param newItem The item in the new list.
+     * @return True if the two items represent the same object or false if they are different.
+     */
+    public abstract boolean areItemsTheSame(@NonNull Value oldItem, @NonNull Value newItem);
+
+    /**
+     * Called to decide whether two items have the same data. This information is used to detect if
+     * the contents of an item have changed.
+     *
+     * @param oldItem The item in the old list.
+     * @param newItem The item in the new list.
+     * @return True if the contents of the items are the same or false if they are different.
+     */
+    public abstract boolean areContentsTheSame(@NonNull Value oldItem, @NonNull Value newItem);
+}
diff --git a/paging/runtime/src/main/java/android/arch/util/paging/LazyListAdapterHelper.java b/paging/runtime/src/main/java/android/arch/util/paging/LazyListAdapterHelper.java
new file mode 100644
index 0000000..64931e9
--- /dev/null
+++ b/paging/runtime/src/main/java/android/arch/util/paging/LazyListAdapterHelper.java
@@ -0,0 +1,367 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.util.ListUpdateCallback;
+import android.support.v7.widget.RecyclerView;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper object for mapping a {@link LazyList} into a
+ * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter} - both the internal
+ * paging of the list as more data is loaded, and updates in the form of new LazyLists.
+ * <p>
+ * The LazyListAdapterHelper can take a {@link LiveData} of LazyList and present the data simply for
+ * an adapter. It listens to LazyList loading callbacks, and uses DiffUtil on a background thread to
+ * compute updates as new LazyLists are received.
+ * <p>
+ * It provides a simple list-like API with {@link #get(int)} and {@link #getItemCount()} for an
+ * adapter to acquire and present data objects.
+ * <p>
+ * A complete usage pattern with Room would look like this:
+ * <pre>
+ * {@literal @}Dao
+ * interface UserDao {
+ *     {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
+ *     LiveLazyListProvider&lt;User> usersByLastName();
+ * }
+ *
+ * class MyViewModel extends ViewModel {
+ *     public final LiveData&lt;LazyList&lt;User>> usersList;
+ *     public MyViewModel(UserDao userDao) {
+ *         usersList = userDao.usersByLastName().create(
+ *                        ListConfig.builder()
+ *                                  .pageSize(50)
+ *                                  .prefetchDistance(50)
+ *                                  .create());
+ *     }
+ * }
+ *
+ * class MyActivity extends Activity implements LifecycleRegistryOwner {
+ *     {@literal @}Override
+ *     public void onCreate(Bundle savedState) {
+ *         super.onCreate(savedState);
+ *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ *         RecyclerView recyclerView = findViewById(R.id.user_list);
+ *         UserAdapter&lt;User> adapter = new UserAdapter(
+ *                  LazyListAdapterHelper.&lt;User>builder()
+ *                                       .lifecycle(this)
+ *                                       .diffCallback(User.DIFF_CALLBACK)
+ *                                       .source(viewModel.usersList));
+ *         recyclerView.setAdapter(adapter);
+ *     }
+ * }
+ *
+ * class UserAdapter extends RecyclerView.Adapter&lt;UserViewHolder> {
+ *     private final LazyListAdapterHelper&lt;User> helper;
+ *     public UserAdapter(LazyListAdapterHelper.Builder&lt;User> builder) {
+ *         helper = builder.adapter(this).create();
+ *     }
+ *     {@literal @}Override
+ *     public int getItemCount() {
+ *         return mHelper.getItemCount();
+ *     }
+ *     {@literal @}Override
+ *     public void onBindViewHolder(UserViewHolder holder, int position) {
+ *         User user = mHelper.get(position);
+ *         if (user == null) {
+ *             // AdapterHelper will automatically call invalidate on this row when the actual
+ *             // object is loaded from the database
+ *             holder.clear();
+ *         } else {
+ *             holder.bindTo(user);
+ *         }
+ *     }
+ * }
+ * </pre>
+ *
+ * @param <Value> Type of the LazyLists this helper will receive.
+ */
+public class LazyListAdapterHelper<Value> extends PagerBaseAdapterHelper<Value> {
+    /**
+     * set to false if the helper is observing a LiveData.
+     */
+    private final boolean mCanSetList;
+    private LazyList.ChangeCallback mChangeCallback = new LazyList.ChangeCallback() {
+        @Override
+        public void onLoaded(int start, int count) {
+            mListUpdateCallback.onChanged(start, count, null);
+        }
+    };
+
+    private LazyListAdapterHelper(
+            @NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor,
+            @NonNull ListUpdateCallback listUpdateCallback,
+            @NonNull DiffCallback<Value> diffCallback, boolean canSetList) {
+        super(mainThreadExecutor, backgroundThreadExecutor, listUpdateCallback, diffCallback);
+        mCanSetList = canSetList;
+    }
+
+    /**
+     * Returns the item at the given index.
+     * <p>
+     * Might be null if the item is not loaded yet.
+     *
+     * @param index The position of the item
+     * @return The item at the given index or {@code null} if it is not loaded into memory yet.
+     */
+    @Override
+    @Nullable
+    public Value get(int index) {
+        return super.get(index);
+    }
+
+    /**
+     * Sets the lazy list for this adapter helper. If you are manually observing the
+     * {@link LazyList} for changes, you should call this method with the new
+     * {@link LazyList} when the previous one is invalidated.
+     * <p>
+     * Adapter helper will calculate the diff between this list and the previous one on a background
+     * thread then replace the data with this one, while unsubscribing from the previous list and
+     * subscribing to the new one.
+     * <p>
+     * If you have already provided a {@link LiveData} source via {@link Builder#source(LiveData)},
+     * calling this method will throw an {@link IllegalStateException}.
+     *
+     * @param newList The new LazyList to observe.
+     */
+    @MainThread
+    public void setLazyList(@Nullable LazyList<Value> newList) {
+        if (mCanSetList) {
+            setPagerBase(newList);
+        } else {
+            throw new IllegalStateException("When an AdapterHelper is observing a LiveData, you"
+                    + " cannot set the list on it because it will be overridden by the LiveData"
+                    + " source");
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @MainThread
+    void internalSetLazyList(@Nullable LazyList<Value> newList) {
+        setPagerBase(newList);
+    }
+
+    @Override
+    void addCallback(PagerBase<Value> list) {
+        ((LazyList<Value>) list).addCallback(mChangeCallback);
+    }
+
+    @Override
+    void removeCallback(PagerBase<Value> list) {
+        ((LazyList<Value>) list).removeCallback(mChangeCallback);
+    }
+
+    /**
+     * Creates a {@link Builder} that can be used to construct a {@link LazyListAdapterHelper}.
+     *
+     * @param <Value> The type parameter for the {@link LazyListAdapterHelper}.
+     * @return a new {@link Builder}.
+     */
+    public static <Value> Builder<Value> builder() {
+        return new Builder<>();
+    }
+
+
+    /**
+     * Builder class for {@link LazyListAdapterHelper}.
+     * <p>
+     * You must at least provide an {@link DiffCallback} and also one of the
+     * {@link Builder#adapter(RecyclerView.Adapter)} or
+     * {@link Builder#updateCallback(ListUpdateCallback)}.
+     *
+     * @param <Value> Data type held by the adapter helper.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static class Builder<Value> {
+        private DiffCallback<Value> mDiffCallback;
+        private ListUpdateCallback mUpdateCallback;
+        private LifecycleOwner mLifecycle;
+        private LiveData<LazyList<Value>> mLiveData;
+        private Executor mMainThreadExecutor;
+        private Executor mBackgroundThreadExecutor;
+
+        /**
+         * Sets the {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}
+         * instance that will receive the update events.
+         * <p>
+         * If you have a more complex case where your adapter has additional items from different
+         * data sources, you can use the {@link #updateCallback(ListUpdateCallback)} to manually
+         * dispatch changes to your adapter.
+         *
+         * @param adapter The adapter to receive change/move/insert/remove updates when the data
+         *                provided by the helper changes.
+         */
+        public Builder<Value> adapter(RecyclerView.Adapter adapter) {
+            return updateCallback(new PagerBaseAdapterHelper.AdapterCallback(adapter));
+        }
+
+        /**
+         * Sets the ListUpdateCallback that will receive updates as the data maintained by the
+         * helper is updated.
+         * <p>
+         * In simple cases, you can instead pass your Adapter to
+         * {@link #adapter(RecyclerView.Adapter)} to receive updates in the form of e.g.
+         * {@link android.support.v7.widget.RecyclerView.Adapter#notifyItemRangeChanged(int, int)}
+         * or
+         * {@link android.support.v7.widget.RecyclerView.Adapter#notifyItemRangeInserted(int, int)}
+         * on your adapter.
+         *
+         * @param callback The callback to receive change/move/insert/remove updates when the data
+         *                 provided by the helper changes.
+         */
+        public Builder<Value> updateCallback(ListUpdateCallback callback) {
+            mUpdateCallback = callback;
+            return this;
+        }
+
+        /**
+         * The {@link DiffCallback} to be used while diffing an old list with the updated one.
+         * Must be provided.
+         *
+         * @param diffCallback The {@link DiffCallback} instance to compare items in the list.
+         * @return this
+         */
+        public Builder<Value> diffCallback(DiffCallback<Value> diffCallback) {
+            mDiffCallback = diffCallback;
+            return this;
+        }
+
+        /**
+         * Assigns a lifecycle to the {@link LazyListAdapterHelper} so that it can cancel the
+         * observers automatically when the lifecycle is destroyed. This is especially useful if
+         * the actual {@link LazyList} is owned by a
+         * {@link android.arch.lifecycle.ViewModel ViewModel} or any other class that outlives
+         * the {@link android.app.Activity Activity} or the
+         * {@link android.support.v4.app.Fragment Fragment}.
+         * <p>
+         * Optional. If you don't provide this and the {@link LazyList} outlives the
+         * {@link android.support.v7.widget.RecyclerView.Adapter Adapter}, you should call
+         * {@link LazyListAdapterHelper#setLazyList(LazyList)} with {@code null} when the UI
+         * element is destroyed.
+         *
+         * @param lifecycleOwner The {@link LifecycleOwner} where the adapter lives.
+         * @return this
+         */
+        public Builder<Value> lifecycle(LifecycleOwner lifecycleOwner) {
+            mLifecycle = lifecycleOwner;
+            return this;
+        }
+
+        /**
+         * If provided, the created {@link LazyListAdapterHelper} will observe the given
+         * {@code source} automatically. If you provide a {@code source}, you must also provide a
+         * {@link LifecycleOwner} via the {@link #lifecycle(LifecycleOwner)} method.
+         *
+         * @param source The LiveData source that should be observed.
+         * @return this
+         */
+        public Builder<Value> source(LiveData<LazyList<Value>> source) {
+            mLiveData = source;
+            return this;
+        }
+
+        /**
+         * If provided, {@link LazyListAdapterHelper} will use the given executor to execute adapter
+         * update notifications on the main thread.
+         * <p>
+         * If not provided, it will default to the UI thread.
+         *
+         * @param executor The executor which can run tasks in the UI thread.
+         * @return this
+         */
+        public Builder<Value> mainThreadExecutor(Executor executor) {
+            mMainThreadExecutor = executor;
+            return this;
+        }
+
+        /**
+         * If provided, {@link LazyListAdapterHelper} will use the given executor to calculate the
+         * diff between an old and a new list.
+         * <p>
+         * If not provided, defaults to the IO thread pool from Architecture Components.
+         *
+         * @param executor The background executor to run list diffing.
+         * @return this
+         */
+        public Builder<Value> backgroundThreadExecutor(Executor executor) {
+            mBackgroundThreadExecutor = executor;
+            return this;
+        }
+
+        /**
+         * Creates a {@link LazyListAdapterHelper} with the given parameters.
+         *
+         * @return A new LazyListAdapterHelper.
+         */
+        public LazyListAdapterHelper<Value> create() {
+            if (mDiffCallback == null) {
+                throw new IllegalArgumentException("Must provide a diffCallback");
+            }
+            if (mUpdateCallback == null) {
+                throw new IllegalArgumentException(
+                        "must provide either an adapter or update callback");
+            }
+            if (mBackgroundThreadExecutor == null) {
+                mBackgroundThreadExecutor = AppToolkitTaskExecutor.getIOThreadExecutor();
+            }
+            if (mMainThreadExecutor == null) {
+                mMainThreadExecutor = AppToolkitTaskExecutor.getMainThreadExecutor();
+            }
+            final LazyListAdapterHelper<Value> result =
+                    new LazyListAdapterHelper<>(
+                            mMainThreadExecutor,
+                            mBackgroundThreadExecutor,
+                            mUpdateCallback, mDiffCallback,
+                            mLiveData == null);
+
+            if (mLifecycle != null) {
+                mLifecycle.getLifecycle().addObserver(new LifecycleObserver() {
+                    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+                    public void clear() {
+                        result.internalSetLazyList(null);
+                    }
+                });
+            }
+            if (mLiveData != null) {
+                if (mLifecycle == null) {
+                    throw new IllegalArgumentException(
+                            "If you provide a LiveData to be observed, you must also provide a"
+                                    + " Livecycle via the lifecycle() method.");
+                }
+                mLiveData.observe(mLifecycle, new Observer<LazyList<Value>>() {
+                    @Override
+                    public void onChanged(@Nullable LazyList<Value> valueLazyList) {
+                        result.internalSetLazyList(valueLazyList);
+                    }
+                });
+            }
+            return result;
+        }
+    }
+}
diff --git a/paging/runtime/src/main/java/android/arch/util/paging/LiveLazyListProvider.java b/paging/runtime/src/main/java/android/arch/util/paging/LiveLazyListProvider.java
new file mode 100644
index 0000000..f39ce85
--- /dev/null
+++ b/paging/runtime/src/main/java/android/arch/util/paging/LiveLazyListProvider.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.lifecycle.ComputableLiveData;
+import android.arch.lifecycle.LiveData;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+
+/**
+ * This class wraps to {@link android.arch.util.paging.CountedDataSource CountedDataSource} and can
+ * provide a {@link android.arch.lifecycle.LiveData LiveData} of
+ * {@link android.arch.util.paging.LazyList LazyList}.
+ *
+ * @param <T> Data type produced by the CountedDataSource, and held by the LazyLists.
+ *
+ * @see LazyListAdapterHelper
+ */
+public abstract class LiveLazyListProvider<T> {
+
+    /**
+     * Construct a new data source to be wrapped in a new LazyList, which will be returned through
+     * the LiveData.
+     *
+     * @return The data source.
+     */
+    @SuppressWarnings("WeakerAccess")
+    @WorkerThread
+    protected abstract CountedDataSource<T> createDataSource();
+
+    /**
+     * Creates a LiveData of LazyLists, given the ListConfig.
+     * <p>
+     * This LiveData can be passed to a {@link LazyListAdapterHelper} to be displayed with a
+     * {@link android.support.v7.widget.RecyclerView}.
+     *
+     * @param configuration ListConfig to use with created LazyLists. This specifies how the lists
+     *                      will load data.
+     *
+     * @return The LiveData of LazyLists.
+     */
+    public LiveData<LazyList<T>> create(final ListConfig configuration) {
+        return new ComputableLiveData<LazyList<T>>() {
+            @Nullable
+            private LazyList<T> mList;
+            @Nullable
+            private CountedDataSource<T> mDataSource;
+
+            private final DataSourceBase.InvalidatedCallback mCallback =
+                    new DataSourceBase.InvalidatedCallback() {
+                @Override
+                public void onInvalidated() {
+                    invalidate();
+                }
+            };
+
+            @Override
+            protected LazyList<T> compute() {
+                int loadAfterPos = mList == null ? -2 : mList.getInitialLoadPosition();
+
+                boolean done = true;
+                do {
+                    if (mDataSource != null) {
+                        mDataSource.removeInvalidatedCallback(mCallback);
+                    }
+
+                    mDataSource = createDataSource();
+                    mDataSource.addInvalidatedCallback(mCallback);
+                    mList = new LazyList<>(mDataSource,
+                            AppToolkitTaskExecutor.getMainThreadExecutor(),
+                            AppToolkitTaskExecutor.getIOThreadExecutor(),
+                            configuration);
+                    if (loadAfterPos >= -1) {
+                        done = mList.internalInit(loadAfterPos);
+                    }
+                } while (!done);
+                return mList;
+            }
+        }.getLiveData();
+    }
+}
diff --git a/paging/runtime/src/main/java/android/arch/util/paging/LivePagedListProvider.java b/paging/runtime/src/main/java/android/arch/util/paging/LivePagedListProvider.java
new file mode 100644
index 0000000..177e639
--- /dev/null
+++ b/paging/runtime/src/main/java/android/arch/util/paging/LivePagedListProvider.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.lifecycle.ComputableLiveData;
+import android.arch.lifecycle.LiveData;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+
+/**
+ * @param <K> Key type of the DataSource, used to initialize PagedLists.
+ * @param <T> Data type produced by the DataSource, and held by the PagedLists.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class LivePagedListProvider<K, T> {
+
+    /**
+     * Construct a new data source to be wrapped in a new PagedList, which will be returned through
+     * the LiveData.
+     *
+     * @return The data source.
+     */
+    @SuppressWarnings("WeakerAccess")
+    @WorkerThread
+    protected abstract DataSource<K, T> createDataSource();
+
+    /**
+     * Creates a LiveData of PagedLists, given the ListConfig.
+     * <p>
+     * This LiveData can be passed to a {@link PagedListAdapterHelper} to be displayed with a
+     * {@link android.support.v7.widget.RecyclerView}.
+     *
+     * @param configuration ListConfig to use with created PagedLists. This specifies how the lists
+     *                      will load data.
+     *
+     * @return The LiveData of LazyLists.
+     */
+    public LiveData<PagedList<T>> create(final ListConfig configuration) {
+        return new ComputableLiveData<PagedList<T>>() {
+            @Nullable
+            private PagedList<T> mList;
+            @Nullable
+            private DataSource<K, T> mDataSource;
+
+            private final DataSourceBase.InvalidatedCallback mCallback =
+                    new DataSourceBase.InvalidatedCallback() {
+                @Override
+                public void onInvalidated() {
+                    invalidate();
+                }
+            };
+
+            @Override
+            protected PagedList<T> compute() {
+                PagedList<T> old = mList;
+
+                boolean done = true;
+                do {
+                    if (mDataSource != null) {
+                        mDataSource.removeInvalidatedCallback(mCallback);
+                    }
+
+                    mDataSource = createDataSource();
+                    mDataSource.addInvalidatedCallback(mCallback);
+                    mList = new PagedList<>(mDataSource,
+                            AppToolkitTaskExecutor.getMainThreadExecutor(),
+                            AppToolkitTaskExecutor.getIOThreadExecutor(),
+                            configuration);
+                    if (old != null) {
+                        done = mList.initializeFrom(old);
+                    }
+                } while (!done);
+                return mList;
+            }
+        }.getLiveData();
+    }
+}
diff --git a/paging/runtime/src/main/java/android/arch/util/paging/PagedListAdapterHelper.java b/paging/runtime/src/main/java/android/arch/util/paging/PagedListAdapterHelper.java
new file mode 100644
index 0000000..0f29cbb
--- /dev/null
+++ b/paging/runtime/src/main/java/android/arch/util/paging/PagedListAdapterHelper.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v7.util.ListUpdateCallback;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper object for mapping a {@link PagedList} into a
+ * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter} - both the paging in
+ * of new content as more data is loaded, and updates in the form of new PagedLists.
+ *
+ * @param <Value> Type of the PagedLists this helper will receive.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class PagedListAdapterHelper<Value> extends PagerBaseAdapterHelper<Value> {
+    private PagedList.ChangeCallback mChangeCallback = new PagedList.ChangeCallback() {
+        @Override
+        public void onInserted(int start, int count) {
+            mListUpdateCallback.onInserted(start, count);
+        }
+    };
+
+    public PagedListAdapterHelper(
+            @NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor,
+            @NonNull ListUpdateCallback listUpdateCallback,
+            @NonNull DiffCallback<Value> diffCallback) {
+        super(mainThreadExecutor, backgroundThreadExecutor, listUpdateCallback, diffCallback);
+    }
+
+    @Override
+    @NonNull
+    public Value get(int index) {
+        return super.get(index);
+    }
+
+    /**
+     * Sets the paged list for this adapter helper. If you are manually observing the
+     * {@link PagedList} for changes, you should call this method with the new
+     * {@link PagedList} when the previous one is invalidated.
+     * <p>
+     * Adapter helper will calculate the diff between this list and the previous one on a background
+     * thread then replace the data with this one, while unsubscribing from the previous list and
+     * subscribing to the new one.
+     *
+     * @param newList The new PagedList to observe.
+     */
+    @MainThread
+    public void setPagedList(@Nullable PagedList<Value> newList) {
+        setPagerBase(newList);
+    }
+
+    @Override
+    void addCallback(PagerBase<Value> list) {
+        ((PagedList<Value>) list).addCallback(mChangeCallback);
+    }
+
+    @Override
+    void removeCallback(PagerBase<Value> list) {
+        ((PagedList<Value>) list).removeCallback(mChangeCallback);
+    }
+}
diff --git a/paging/runtime/src/main/java/android/arch/util/paging/PagerBaseAdapterHelper.java b/paging/runtime/src/main/java/android/arch/util/paging/PagerBaseAdapterHelper.java
new file mode 100644
index 0000000..659298f
--- /dev/null
+++ b/paging/runtime/src/main/java/android/arch/util/paging/PagerBaseAdapterHelper.java
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+import android.support.v7.util.DiffUtil;
+import android.support.v7.util.ListUpdateCallback;
+import android.support.v7.widget.RecyclerView;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @param <Value> Type of the PagerBases this helper will receive.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class PagerBaseAdapterHelper<Value> {
+    final ListUpdateCallback mListUpdateCallback;
+    private final DiffCallback<Value> mDiffCallback;
+    private final Executor mMainThreadExecutor;
+    private final Executor mBackgroundThreadExecutor;
+
+    /**
+     * Default ListUpdateCallback that dispatches directly to an adapter. Can be replaced by a
+     * custom ListUpdateCallback if e.g. your adapter has a header in it, and so has an offset
+     * between list positions and adapter positions.
+     */
+    public static class AdapterCallback implements ListUpdateCallback {
+        private final RecyclerView.Adapter mAdapter;
+
+        public AdapterCallback(RecyclerView.Adapter adapter) {
+            mAdapter = adapter;
+        }
+
+        @Override
+        public void onInserted(int position, int count) {
+            mAdapter.notifyItemRangeInserted(position, count);
+        }
+
+        @Override
+        public void onRemoved(int position, int count) {
+            mAdapter.notifyItemRangeRemoved(position, count);
+        }
+
+        @Override
+        public void onMoved(int fromPosition, int toPosition) {
+            mAdapter.notifyItemMoved(fromPosition, toPosition);
+        }
+
+        @Override
+        public void onChanged(int position, int count, Object payload) {
+            mAdapter.notifyItemRangeChanged(position, count, payload);
+        }
+    }
+
+    PagerBaseAdapterHelper(
+            @NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor,
+            @NonNull ListUpdateCallback listUpdateCallback,
+            @NonNull DiffCallback<Value> diffCallback) {
+        mMainThreadExecutor = mainThreadExecutor;
+        mBackgroundThreadExecutor = backgroundThreadExecutor;
+        mListUpdateCallback = listUpdateCallback;
+        mDiffCallback = diffCallback;
+    }
+
+    private PagerBase<Value> mList;
+    private PagerBase<Value> mProcessingList;
+    private PagerBase<Value> mQueuedList;
+    private DiffUtil.DiffResult mDiffResult;
+
+    /**
+     * Get the current list item at the given index. Index must be less than value of
+     * {@link #getItemCount()}.
+     */
+    public Value get(int index) {
+        return mList.get(index);
+    }
+
+    /**
+     * Returns the number of items in the list. This count includes all items (even the ones that
+     * are not yet loaded into memory).
+     *
+     * @return The number of items in the list.
+     */
+    public int getItemCount() {
+        return mList == null ? 0 : mList.size();
+    }
+
+    @NonNull
+    private DiffUtil.DiffResult computeDiff(
+            final PagerBase<Value> oldList, final PagerBase<Value> newList) {
+        return DiffUtil.calculateDiff(new DiffUtil.Callback() {
+            @Nullable
+            @Override
+            public Object getChangePayload(int oldItemPosition, int newItemPosition) {
+                return null;
+            }
+
+            @Override
+            public int getOldListSize() {
+                return oldList.size();
+            }
+
+            @Override
+            public int getNewListSize() {
+                return newList.size();
+            }
+
+            @Override
+            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+                Value oldItem = oldList.access(oldItemPosition);
+                Value newItem = newList.access(newItemPosition);
+                if (oldItem == newItem) {
+                    return true;
+                }
+                if (oldItem == null || newItem == null) {
+                    return false;
+                }
+                return mDiffCallback.areItemsTheSame(oldItem, newItem);
+            }
+
+            @Override
+            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+                Value oldItem = oldList.access(oldItemPosition);
+                Value newItem = newList.access(newItemPosition);
+                if (oldItem == newItem) {
+                    return true;
+                }
+                if (oldItem == null || newItem == null) {
+                    return false;
+                }
+
+                return mDiffCallback.areContentsTheSame(oldItem, newItem);
+            }
+        }, false);
+    }
+
+    private void applyDiff(DiffUtil.DiffResult diffResult) {
+        diffResult.dispatchUpdatesTo(mListUpdateCallback);
+    }
+
+    private Runnable mBackgroundDiffRunnable = new Runnable() {
+        @WorkerThread
+        @Override
+        public void run() {
+            mDiffResult = computeDiff(mList, mProcessingList);
+            mMainThreadExecutor.execute(mApplyDiffRunnable);
+        }
+    };
+
+    private Runnable mApplyDiffRunnable = new Runnable() {
+        @MainThread
+        @Override
+        public void run() {
+            if (mQueuedList == null) {
+                // null list queued, trigger immediately
+                removeCallback(mList);
+                mListUpdateCallback.onRemoved(0, mList.size());
+                mList = null;
+                mProcessingList = null;
+                mDiffResult = null;
+                return;
+            }
+
+            // apply processing list, dispatching updates
+            removeCallback(mList);
+            mList = mProcessingList;
+            addCallback(mList);
+
+            applyDiff(mDiffResult);
+            mDiffResult = null;
+
+            if (mQueuedList == mProcessingList) {
+                // no further work
+                mProcessingList = null;
+                mQueuedList = null;
+            } else {
+                // enqueue diff for most recent PagerBase
+                mProcessingList = mQueuedList;
+                mBackgroundThreadExecutor.execute(mBackgroundDiffRunnable);
+            }
+        }
+    };
+
+    @MainThread
+    void setPagerBase(@Nullable PagerBase<Value> newList) {
+        if (mList == null) {
+            // first list
+            mList = newList;
+            if (mList != null) {
+                addCallback(mList);
+                mListUpdateCallback.onInserted(0, mList.size());
+            }
+        } else if (mProcessingList == null) {
+            if (newList == null) {
+                // swap in immediately
+                removeCallback(mList);
+                mListUpdateCallback.onRemoved(0, mList.size());
+                mList = null;
+            } else {
+                // not working on a list, enqueue runnable
+                mProcessingList = newList;
+                mQueuedList = newList;
+                mBackgroundThreadExecutor.execute(mBackgroundDiffRunnable);
+            }
+        } else {
+            // already working on new list, enqueue work. Note that this includes a null list,
+            // which is greedily applied in mApplyDiffRunnable. For simplicity, we don't attempt to
+            mQueuedList = newList;
+        }
+
+    }
+
+    abstract void addCallback(PagerBase<Value> list);
+    abstract void removeCallback(PagerBase<Value> list);
+
+    @Override
+    public String toString() {
+        return mList + ", " + mProcessingList + ", " + mQueuedList;
+    }
+}
diff --git a/pathmap.mk b/pathmap.mk
new file mode 100644
index 0000000..3e73be7
--- /dev/null
+++ b/pathmap.mk
@@ -0,0 +1,71 @@
+#
+# 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.
+#
+
+#
+# A list of all source roots under frameworks/support.
+#
+FRAMEWORKS_SUPPORT_SUBDIRS := \
+    annotations \
+    compat \
+    core-ui \
+    core-utils \
+    customtabs \
+    design \
+    dynamic-animation \
+    exifinterface \
+    fragment \
+    media-compat \
+    percent \
+    recommendation \
+    transition \
+    tv-provider \
+    v7/cardview \
+    v7/gridlayout \
+    v7/mediarouter \
+    v7/palette \
+    v7/preference \
+    v13 \
+    v14/preference \
+    v17/leanback \
+    v17/preference-leanback \
+    wear
+
+#
+# A version of FRAMEWORKS_SUPPORT_SUBDIRS that is expanded to full paths from
+# the root of the tree.
+#
+FRAMEWORKS_SUPPORT_JAVA_SRC_DIRS := \
+    $(addprefix frameworks/support/,$(FRAMEWORKS_SUPPORT_SUBDIRS)) \
+    frameworks/support/graphics/drawable/animated \
+    frameworks/support/graphics/drawable/static \
+    frameworks/support/v7/appcompat/src \
+    frameworks/support/v7/recyclerview/src \
+    frameworks/support/emoji/core/src \
+    frameworks/support/emoji/appcompat/src \
+    frameworks/support/emoji/bundled/src
+
+#
+# A list of support library modules.
+#
+FRAMEWORKS_SUPPORT_JAVA_LIBRARIES := \
+    $(foreach dir,$(FRAMEWORKS_SUPPORT_SUBDIRS),android-support-$(subst /,-,$(dir))) \
+    android-support-vectordrawable \
+    android-support-animatedvectordrawable \
+    android-support-v7-appcompat \
+    android-support-v7-recyclerview \
+    android-support-emoji \
+    android-support-emoji-appcompat \
+    android-support-emoji-bundled
diff --git a/percent/Android.mk b/percent/Android.mk
index aaeb65a..b569224 100644
--- a/percent/Android.mk
+++ b/percent/Android.mk
@@ -27,7 +27,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := android-support-v4
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/percent/AndroidManifest-make.xml b/percent/AndroidManifest-make.xml
deleted file mode 100644
index e979013..0000000
--- a/percent/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.percent">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application />
-</manifest>
diff --git a/percent/AndroidManifest.xml b/percent/AndroidManifest.xml
index 46ccce8..58eebfe 100644
--- a/percent/AndroidManifest.xml
+++ b/percent/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.percent">
-    <uses-sdk android:minSdkVersion="9"/>
+    <uses-sdk android:minSdkVersion="14"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/percent/build.gradle b/percent/build.gradle
index b120075..bd129d7 100644
--- a/percent/build.gradle
+++ b/percent/build.gradle
@@ -1,95 +1,32 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'percent'
 
 dependencies {
-    compile project(':support-compat')
+    api project(':support-compat')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    testCompile 'junit:junit:4.12'
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['src']
         main.res.srcDir 'res'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
-
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Percent Support Library'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 7 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Percent Support Library'
+    inceptionYear '2015'
+    description 'Android Percent Support Library'
 }
diff --git a/percent/lint-baseline.xml b/percent/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/percent/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/percent/src/.readme b/percent/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/percent/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/percent/src/android/support/percent/PercentFrameLayout.java b/percent/src/android/support/percent/PercentFrameLayout.java
index 679ffbe..b9abd39 100644
--- a/percent/src/android/support/percent/PercentFrameLayout.java
+++ b/percent/src/android/support/percent/PercentFrameLayout.java
@@ -16,7 +16,6 @@
 
 package android.support.percent;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.annotation.RequiresApi;
@@ -75,7 +74,60 @@
  * </pre>
  * This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted
  * accordingly.
+ *
+ * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
+ * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
+ * are used to define each percentage break point, and then a Button view is stretched to fill
+ * the gap:
+ *
+ * <pre class="prettyprint">
+ * &lt;android.support.constraint.ConstraintLayout
+ *         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="match_parent"&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/left_guideline"
+ *         app:layout_constraintGuide_percent=".15"
+ *         android:orientation="vertical"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/right_guideline"
+ *         app:layout_constraintGuide_percent=".85"
+ *         android:orientation="vertical"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/top_guideline"
+ *         app:layout_constraintGuide_percent=".15"
+ *         android:orientation="horizontal"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/bottom_guideline"
+ *         app:layout_constraintGuide_percent=".85"
+ *         android:orientation="horizontal"/&gt
+ *
+ *     &lt;Button
+ *         android:text="Button"
+ *         android:layout_width="0dp"
+ *         android:layout_height="0dp"
+ *         android:id="@+id/button"
+ *         app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
+ *         app:layout_constraintRight_toRightOf="@+id/right_guideline"
+ *         app:layout_constraintTop_toTopOf="@+id/top_guideline"
+ *         app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /&gt
+ *
+ * &lt;/android.support.constraint.ConstraintLayout&gt
  */
+@Deprecated
 public class PercentFrameLayout extends FrameLayout {
     private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
 
@@ -116,6 +168,10 @@
         mHelper.restoreOriginalParams();
     }
 
+    /**
+     * @deprecated this class is deprecated along with its parent class.
+     */
+    @Deprecated
     public static class LayoutParams extends FrameLayout.LayoutParams
             implements PercentLayoutHelper.PercentLayoutParams {
         private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
@@ -147,7 +203,6 @@
         }
 
         @RequiresApi(19)
-        @TargetApi(19)
         public LayoutParams(LayoutParams source) {
             // The copy constructor used here is only supported on API 19+.
             this((FrameLayout.LayoutParams) source);
diff --git a/percent/src/android/support/percent/PercentLayoutHelper.java b/percent/src/android/support/percent/PercentLayoutHelper.java
index 3cb5f47..44a76c7 100644
--- a/percent/src/android/support/percent/PercentLayoutHelper.java
+++ b/percent/src/android/support/percent/PercentLayoutHelper.java
@@ -26,10 +26,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import android.support.percent.R;
-
-import java.lang.Math;
-
 /**
  * Helper for layouts that want to support percentage based dimensions.
  *
@@ -71,7 +67,59 @@
  * }
  * </pre>
  * </ol>
+ * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
+ * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
+ * are used to define each percentage break point, and then a Button view is stretched to fill
+ * the gap:
+ *
+ * <pre class="prettyprint">
+ * &lt;android.support.constraint.ConstraintLayout
+ *         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="match_parent"&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/left_guideline"
+ *         app:layout_constraintGuide_percent=".15"
+ *         android:orientation="vertical"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/right_guideline"
+ *         app:layout_constraintGuide_percent=".85"
+ *         android:orientation="vertical"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/top_guideline"
+ *         app:layout_constraintGuide_percent=".15"
+ *         android:orientation="horizontal"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/bottom_guideline"
+ *         app:layout_constraintGuide_percent=".85"
+ *         android:orientation="horizontal"/&gt
+ *
+ *     &lt;Button
+ *         android:text="Button"
+ *         android:layout_width="0dp"
+ *         android:layout_height="0dp"
+ *         android:id="@+id/button"
+ *         app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
+ *         app:layout_constraintRight_toRightOf="@+id/right_guideline"
+ *         app:layout_constraintTop_toTopOf="@+id/top_guideline"
+ *         app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /&gt
+ *
+ * &lt;/android.support.constraint.ConstraintLayout&gt
  */
+@Deprecated
 public class PercentLayoutHelper {
     private static final String TAG = "PercentLayout";
 
@@ -320,15 +368,15 @@
     }
 
     private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
-        int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
-        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&
-                info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
+        int state = view.getMeasuredWidthAndState() & View.MEASURED_STATE_MASK;
+        return state == View.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0
+                && info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
     }
 
     private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) {
-        int state = ViewCompat.getMeasuredHeightAndState(view) & ViewCompat.MEASURED_STATE_MASK;
-        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0 &&
-                info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT;
+        int state = view.getMeasuredHeightAndState() & View.MEASURED_STATE_MASK;
+        return state == View.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0
+                && info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT;
     }
 
     /* package */ static class PercentMarginLayoutParams extends ViewGroup.MarginLayoutParams {
@@ -348,7 +396,10 @@
     /**
      * Container for information about percentage dimensions and margins. It acts as an extension
      * for {@code LayoutParams}.
+     *
+     * @deprecated use ConstraintLayout and Guidelines for layout support.
      */
+    @Deprecated
     public static class PercentLayoutInfo {
         /** The decimal value of the percentage-based width. */
         public float widthPercent;
@@ -559,7 +610,10 @@
      *
      * Your {@code LayoutParams} subclass should contain an instance of {@code PercentLayoutInfo}
      * and the implementation of this interface should be a simple accessor.
+     *
+     * @deprecated this class is deprecated along with its parent class.
      */
+    @Deprecated
     public interface PercentLayoutParams {
         PercentLayoutInfo getPercentLayoutInfo();
     }
diff --git a/percent/src/android/support/percent/PercentRelativeLayout.java b/percent/src/android/support/percent/PercentRelativeLayout.java
index f068d6b..5b10349 100644
--- a/percent/src/android/support/percent/PercentRelativeLayout.java
+++ b/percent/src/android/support/percent/PercentRelativeLayout.java
@@ -73,7 +73,60 @@
  * </pre>
  * This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted
  * accordingly.
+ *
+ * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
+ * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
+ * are used to define each percentage break point, and then a Button view is stretched to fill
+ * the gap:
+ *
+ * <pre class="prettyprint">
+ * &lt;android.support.constraint.ConstraintLayout
+ *         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="match_parent"&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/left_guideline"
+ *         app:layout_constraintGuide_percent=".15"
+ *         android:orientation="vertical"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/right_guideline"
+ *         app:layout_constraintGuide_percent=".85"
+ *         android:orientation="vertical"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/top_guideline"
+ *         app:layout_constraintGuide_percent=".15"
+ *         android:orientation="horizontal"/&gt
+ *
+ *     &lt;android.support.constraint.Guideline
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:id="@+id/bottom_guideline"
+ *         app:layout_constraintGuide_percent=".85"
+ *         android:orientation="horizontal"/&gt
+ *
+ *     &lt;Button
+ *         android:text="Button"
+ *         android:layout_width="0dp"
+ *         android:layout_height="0dp"
+ *         android:id="@+id/button"
+ *         app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
+ *         app:layout_constraintRight_toRightOf="@+id/right_guideline"
+ *         app:layout_constraintTop_toTopOf="@+id/top_guideline"
+ *         app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /&gt
+ *
+ * &lt;/android.support.constraint.ConstraintLayout&gt
  */
+@Deprecated
 public class PercentRelativeLayout extends RelativeLayout {
     private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
 
@@ -114,6 +167,10 @@
         mHelper.restoreOriginalParams();
     }
 
+    /**
+     * @deprecated this class is deprecated along with its parent class.
+     */
+    @Deprecated
     public static class LayoutParams extends RelativeLayout.LayoutParams
             implements PercentLayoutHelper.PercentLayoutParams {
         private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
diff --git a/percent/tests/AndroidManifest.xml b/percent/tests/AndroidManifest.xml
index ba3aa19..969b7a8 100644
--- a/percent/tests/AndroidManifest.xml
+++ b/percent/tests/AndroidManifest.xml
@@ -15,24 +15,14 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.percent.test">
-    <uses-sdk
-        android:minSdkVersion="9"
-        android:targetSdkVersion="23"
-        tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-              android.support.test.espresso, android.support.test.espresso.idling" />
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application android:supportsRtl="true">
-        <uses-library android:name="android.test.runner" />
-
         <activity android:name="android.support.percent.TestFrameActivity"/>
         <activity android:name="android.support.percent.TestRelativeActivity"/>
         <activity android:name="android.support.percent.TestRelativeRtlActivity"/>
         <activity android:name="android.support.percent.PercentDynamicLayoutActivity"/>
     </application>
 
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.percent.test"/>
 </manifest>
diff --git a/percent/tests/java/android/support/percent/BaseInstrumentationTestCase.java b/percent/tests/java/android/support/percent/BaseInstrumentationTestCase.java
index 1fff432..5b699dc 100644
--- a/percent/tests/java/android/support/percent/BaseInstrumentationTestCase.java
+++ b/percent/tests/java/android/support/percent/BaseInstrumentationTestCase.java
@@ -16,19 +16,15 @@
 
 package android.support.percent;
 
+import static junit.framework.Assert.assertEquals;
+
 import android.app.Activity;
-import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.ActivityInstrumentationTestCase2;
-import junit.framework.Assert;
-import org.junit.Before;
+
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
-import static junit.framework.Assert.assertEquals;
-
 @RunWith(AndroidJUnit4.class)
 public abstract class BaseInstrumentationTestCase<A extends Activity> {
     @Rule
diff --git a/previewsdk/Android.mk b/previewsdk/Android.mk
deleted file mode 100644
index ccd0547..0000000
--- a/previewsdk/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Here is the final static library that apps can link against.
-include $(CLEAR_VARS)
-
-previewsdk_generate_constants_exe := $(LOCAL_PATH)/previewconstants.sh
-previewsdk_gen_java_files := $(TARGET_OUT_COMMON_GEN)/previewsdk/PreviewSdkConstants.java
-
-$(previewsdk_gen_java_files): $(previewsdk_generate_constants_exe)
-	$(hide) mkdir -p $(dir $@)
-	$(hide) PLATFORM_PREVIEW_SDK_VERSION="$(PLATFORM_PREVIEW_SDK_VERSION)" \
-		bash $< > $@
-
-LOCAL_MODULE := android-support-previewsdk
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_GENERATED_SOURCES := $(previewsdk_gen_java_files)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/previewsdk/previewconstants.sh b/previewsdk/previewconstants.sh
deleted file mode 100755
index de94f84..0000000
--- a/previewsdk/previewconstants.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-echo "/** Begin preview constants"
-echo " * autogenerated by previewconstants.sh */"
-echo "package android.support.previewsdk;"
-echo "class PreviewConstants {"
-echo "    public static final int PREVIEW_SDK_VERSION = $PLATFORM_PREVIEW_SDK_VERSION;"
-echo "}"
diff --git a/previewsdk/src/android/support/previewsdk/PreviewSdk.java b/previewsdk/src/android/support/previewsdk/PreviewSdk.java
deleted file mode 100644
index 8b494c7..0000000
--- a/previewsdk/src/android/support/previewsdk/PreviewSdk.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.previewsdk;
-
-import android.os.Build;
-
-/**
- * Utility class for performing version checks on Android platform preview SDKs.
- *
- * <p>Apps must be very careful when targeting preview builds because binary compatibility
- * is not guaranteed. APIs can be renamed or drastically changed before they are finalized
- * into a new API level. The new SDK constant <code>Build.VERSION.PREVIEW_SDK_INT</code>
- * marks a precise snapshot version of prerelease API.</p>
- *
- * <p>{@link #isKnownPreviewDevice()} will return <code>true</code> if the current device
- * is running a preview build with the same SDK snapshot this support lib was built with.
- * If it returns <code>true</code> it is safe to call prerelease APIs. If not, the app
- * should fall back to only assuming the presence of the latest public, final API level.</p>
- */
-public class PreviewSdk {
-    /**
-     * Check if the current device is running a prerelease platform preview build matching
-     * the SDK this library was built for. If it returns true, it is safe to call prerelease
-     * APIs known to this SDK.
-     *
-     * @return true if the device is running a preview build that matches the SDK.
-     */
-    public static boolean isKnownPreviewDevice() {
-        return "MNC".equals(Build.VERSION.CODENAME)
-                && Build.VERSION.PREVIEW_SDK_INT == PreviewConstants.PREVIEW_SDK_VERSION;
-    }
-}
diff --git a/recommendation/Android.mk b/recommendation/Android.mk
index 67721bb..99ba65f 100644
--- a/recommendation/Android.mk
+++ b/recommendation/Android.mk
@@ -24,48 +24,12 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-recommendation
-LOCAL_SDK_VERSION := 21
+LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v4 \
     android-support-annotations
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# ===========================================================
-# Common Droiddoc vars
-recommendation.docs.src_files := \
-    $(call all-java-files-under, src) \
-    $(call all-html-files-under, src)
-recommendation.docs.java_libraries := \
-    android-support-v4 \
-    android-support-recommendation
-
-# Documentation
-# ===========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android-support-recommendation
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(recommendation.docs.src_files)
-
-LOCAL_SDK_VERSION := 21
-LOCAL_IS_HOST_MODULE := false
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
-
-LOCAL_SHARED_ANDROID_LIBRARIES := $(recommendation.docs.java_libraries)
-
-LOCAL_DROIDDOC_OPTIONS := \
-    -offlinemode \
-    -hdf android.whichdoc offline \
-    -federate Android http://developer.android.com \
-    -federationapi Android prebuilts/sdk/api/21.txt \
-    -hide 113
-
-include $(BUILD_DROIDDOC)
-
diff --git a/recommendation/AndroidManifest-make.xml b/recommendation/AndroidManifest-make.xml
deleted file mode 100644
index ef1223e..0000000
--- a/recommendation/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.recommendation">
-    <uses-sdk android:minSdkVersion="21"/>
-    <application />
-</manifest>
diff --git a/recommendation/build.gradle b/recommendation/build.gradle
index 56633de..86743d3 100644
--- a/recommendation/build.gradle
+++ b/recommendation/build.gradle
@@ -1,83 +1,26 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'recommendation'
 
 dependencies {
-    compile project(':support-v4')
+    api project(':support-v4')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
         minSdkVersion 21
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['src']
         main.res.srcDir 'res'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
-
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android Support Recommendation'
+    inceptionYear '2015'
+    description 'Android Support Recommendation'
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Recommendation'
-                description "Android Support Recommendation"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/recommendation/lint-baseline.xml b/recommendation/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/recommendation/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/room/.gitignore b/room/.gitignore
new file mode 100644
index 0000000..be4e6f1
--- /dev/null
+++ b/room/.gitignore
@@ -0,0 +1,4 @@
+local.properties
+maven-repo/
+build/
+*.DS_Store
diff --git a/room/common/build.gradle b/room/common/build.gradle
new file mode 100644
index 0000000..5b0d53d
--- /dev/null
+++ b/room/common/build.gradle
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'java'
+apply plugin: 'maven'
+
+sourceCompatibility = 1.7
+
+dependencies {
+    compile libs.support.annotations
+    testCompile libs.junit
+    testCompile libs.mockito_core
+}
+
+archivesBaseName = "common"
+
+createAndroidCheckstyle(project)
diff --git a/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java b/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java
new file mode 100644
index 0000000..84f5844
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Allows specific customization about the column associated with this field.
+ * <p>
+ * For example, you can specify a column name for the field or change the column's type affinity.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface ColumnInfo {
+    /**
+     * Name of the column in the database. Defaults to the field name if not set.
+     * @return Name of the column in the database.
+     */
+    String name() default INHERIT_FIELD_NAME;
+
+    /**
+     * The type affinity for the column, which will be used when constructing the database.
+     * <p>
+     * If it is not specified, Room resolves it based on the field's type and available
+     * TypeConverters.
+     * <p>
+     * See <a href="https://www.sqlite.org/datatype3.html">SQLite types documentation</a> for
+     * details.
+     *
+     * @return The type affinity of the column.
+     */
+    @SuppressWarnings("unused") @SQLiteTypeAffinity int typeAffinity() default UNDEFINED;
+
+    /**
+     * Convenience method to index the field.
+     * <p>
+     * If you would like to create a composite index instead, see: {@link Index}.
+     *
+     * @return True if this field should be indexed, false otherwise. Defaults to false.
+     */
+    boolean index() default false;
+
+    /**
+     * Constant to let Room inherit the field name as the column name. If used, Room will use the
+     * field name as the column name.
+     */
+    String INHERIT_FIELD_NAME = "[field-name]";
+
+    /**
+     * Undefined type affinity. Will be resolved based on the type.
+     */
+    int UNDEFINED = 1;
+    /**
+     * Column affinity constant for strings.
+     */
+    int TEXT = 2;
+    /**
+     * Column affinity constant for integers or booleans.
+     */
+    int INTEGER = 3;
+    /**
+     * Column affinity constant for floats or doubles.
+     */
+    int REAL = 4;
+    /**
+     * Column affinity constant for binary data.
+     */
+    int BLOB = 5;
+
+    /**
+     * The SQLite column type constants that can be used in {@link #typeAffinity()}
+     */
+    @IntDef({UNDEFINED, TEXT, INTEGER, REAL, BLOB})
+    @interface SQLiteTypeAffinity {
+    }
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Dao.java b/room/common/src/main/java/android/arch/persistence/room/Dao.java
new file mode 100644
index 0000000..ea205e6
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Dao.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks the class as a Data Access Object.
+ * <p>
+ * Data Access Objects are the main classes where you define your database interactions. They can
+ * include a variety of query methods.
+ * <p>
+ * The class marked with {@code @Dao} should either be an interface or an abstract class. At compile
+ * time, Room will generate an implementation of this class when it is referenced by a
+ * {@link Database}.
+ * <p>
+ * An abstract {@code @Dao} class can optionally have a constructor that takes a {@link Database}
+ * as its only parameter.
+ * <p>
+ * It is recommended to have multiple {@code Dao} classes in your codebase depending on the tables
+ * they touch.
+ *
+ * @see Query
+ * @see Delete
+ * @see Insert
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface Dao {
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Database.java b/room/common/src/main/java/android/arch/persistence/room/Database.java
new file mode 100644
index 0000000..f12d1b9
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Database.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a class as a RoomDatabase.
+ * <p>
+ * The class should be an abstract class and extend
+ * {@link android.arch.persistence.room.RoomDatabase RoomDatabase}.
+ * <p>
+ * You can receive an implementation of the class via
+ * {@link android.arch.persistence.room.Room#databaseBuilder Room.databaseBuilder} or
+ * {@link android.arch.persistence.room.Room#inMemoryDatabaseBuilder Room.inMemoryDatabaseBuilder}.
+ * <p>
+ * <pre>
+ * // User and Book are classes annotated with {@literal @}Entity.
+ * {@literal @}Database(version = 1, entities = {User.class, Book.class})
+ * abstract class AppDatabase extends RoomDatabase() {
+ *     // BookDao is a class annotated with {@literal @}Dao.
+ *     abstract public BookDao bookDao();
+ *     // UserDao is a class annotated with {@literal @}Dao.
+ *     abstract public UserDao userDao();
+ *     // UserBookDao is a class annotated with {@literal @}Dao.
+ *     abstract public UserBookDao userBookDao();
+ * }
+ * </pre>
+ * The example above defines a class that has 2 tables and 3 DAO classes that are used to access it.
+ * There is no limit on the number of {@link Entity} or {@link Dao} classes but they must be unique
+ * within the Database.
+ * <p>
+ * Instead of running queries on the database directly, you are highly recommended to create
+ * {@link Dao} classes. Using Dao classes will allow you to abstract the database communication in
+ * a more logical layer which will be much easier to mock in tests (compared to running direct
+ * sql queries). It also automatically does the conversion from {@code Cursor} to your application
+ * classes so you don't need to deal with lower level database APIs for most of your data access.
+ * <p>
+ * Room also verifies all of your queries in {@link Dao} classes while the application is being
+ * compiled so that if there is a problem in one of the queries, you will be notified instantly.
+ * @see Dao
+ * @see Entity
+ * @see android.arch.persistence.room.RoomDatabase RoomDatabase
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface Database {
+    /**
+     * The list of entities included in the database. Each entity turns into a table in the
+     * database.
+     *
+     * @return The list of entities in the database.
+     */
+    Class[] entities();
+
+    /**
+     * The database version.
+     *
+     * @return The database version.
+     */
+    int version();
+
+    /**
+     * You can set annotation processor argument ({@code room.schemaLocation})
+     * to tell Room to export the schema into a folder. Even though it is not mandatory, it is a
+     * good practice to have version history in your codebase and you should commit that file into
+     * your version control system (but don't ship it with your app!).
+     * <p>
+     * When {@code room.schemaLocation} is set, Room will check this variable and if it is set to
+     * {@code true}, its schema will be exported into the given folder.
+     * <p>
+     * {@code exportSchema} is {@code true} by default but you can disable it for databases when
+     * you don't want to keep history of versions (like an in-memory only database).
+     *
+     * @return Whether the schema should be exported to the given folder when the
+     * {@code room.schemaLocation} argument is set. Defaults to {@code true}.
+     */
+    boolean exportSchema() default true;
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Delete.java b/room/common/src/main/java/android/arch/persistence/room/Delete.java
new file mode 100644
index 0000000..678b743
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Delete.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method in a {@link Dao} annotated class as a delete method.
+ * <p>
+ * The implementation of the method will delete its parameters from the database.
+ * <p>
+ * All of the parameters of the Delete method must either be classes annotated with {@link Entity}
+ * or collections/array of it.
+ * <p>
+ * Example:
+ * <pre>
+ * {@literal @}Dao
+ * public interface MyDao {
+ *     {@literal @}Delete
+ *     public void deleteUsers(User... users);
+ *     {@literal @}Delete
+ *     public void deleteAll(User user1, User user2);
+ *     {@literal @}Delete
+ *     public void deleteWithFriends(User user, List&lt;User&gt; friends);
+ * }
+ * </pre>
+ *
+ * @see Insert
+ * @see Query
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.CLASS)
+public @interface Delete {
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Embedded.java b/room/common/src/main/java/android/arch/persistence/room/Embedded.java
new file mode 100644
index 0000000..781f026
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Embedded.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Can be used as an annotation on a field of an {@link Entity} or {@code Pojo} to signal that
+ * nested fields (i.e. fields of the annotated field's class) can be referenced directly in the SQL
+ * queries.
+ * <p>
+ * If the container is an {@link Entity}, these sub fields will be columns in the {@link Entity}'s
+ * database table.
+ * <p>
+ * For example, if you have 2 classes:
+ * <pre>
+ *   public class Coordinates {
+ *       double latitude;
+ *       double longitude;
+ *   }
+ *   public class Address {
+ *       String street;
+ *       {@literal @}Embedded
+ *       Coordinates coordinates;
+ *   }
+ * </pre>
+ * Room will consider {@code latitude} and {@code longitude} as if they are fields of the
+ * {@code Address} class when mapping an SQLite row to {@code Address}.
+ * <p>
+ * So if you have a query that returns {@code street, latitude, longitude}, Room will properly
+ * construct an {@code Address} class.
+ * <p>
+ * If the {@code Address} class is annotated with {@link Entity}, its database table will have 3
+ * columns: {@code street, latitude, longitude}
+ * <p>
+ * If there is a name conflict with the fields of the sub object and the owner object, you can
+ * specify a {@link #prefix()} for the items of the sub object. Note that prefix is always applied
+ * to sub fields even if they have a {@link ColumnInfo} with a specific {@code name}.
+ * <p>
+ * If sub fields of an embedded field has {@link PrimaryKey} annotation, they <b>will not</b> be
+ * considered as primary keys in the owner {@link Entity}.
+ * <p>
+ * When an embedded field is read, if all fields of the embedded field (and its sub fields) are
+ * {@code null} in the {@link android.database.Cursor Cursor}, it is set to {@code null}. Otherwise,
+ * it is constructed.
+ * <p>
+ * Note that even if you have {@link TypeConverter}s that convert a {@code null} column into a
+ * {@code non-null} value, if all columns of the embedded field in the
+ * {@link android.database.Cursor Cursor} are null, the {@link TypeConverter} will never be called
+ * and the embedded field will not be constructed.
+ * <p>
+ * You can override this behavior by annotating the embedded field with
+ * {@link android.support.annotation.NonNull}.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface Embedded {
+    /**
+     * Specifies a prefix to prepend the column names of the fields in the embedded fields.
+     * <p>
+     * For the example above, if we've written:
+     * <pre>
+     *   {@literal @}Embedded(prefix = "foo_")
+     *   Coordinates coordinates;
+     * </pre>
+     * The column names for {@code latitude} and {@code longitude} will be {@code foo_latitude} and
+     * {@code foo_longitude} respectively.
+     * <p>
+     * By default, prefix is the empty string.
+     *
+     * @return The prefix to be used for the fields of the embedded item.
+     */
+    String prefix() default  "";
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Entity.java b/room/common/src/main/java/android/arch/persistence/room/Entity.java
new file mode 100644
index 0000000..f54f0f8
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Entity.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a class as an entity. This class will have a mapping SQLite table in the database.
+ * <p>
+ * Each entity must have at least 1 field annotated with {@link PrimaryKey}.
+ * You can also use {@link #primaryKeys()} attribute to define the primary key.
+ * <p>
+ * Each entity must either have a no-arg constructor or a constructor whose parameters match
+ * fields (based on type and name). Constructor does not have to receive all fields as parameters
+ * but if a field is not passed into the constructor, it should either be public or have a public
+ * setter. If a matching constructor is available, Room will always use it. If you don't want it
+ * to use a constructor, you can annotate it with {@link Ignore}.
+ * <p>
+ * When a class is marked as an Entity, all of its fields are persisted. If you would like to
+ * exclude some of its fields, you can mark them with {@link Ignore}.
+ * <p>
+ * Example:
+ * <pre>
+ * {@literal @}Entity
+ * public class User {
+ *   {@literal @}PrimaryKey
+ *   private final int uid;
+ *   private String name;
+ *   {@literal @}ColumnInfo(name = "last_name")
+ *   private String lastName;
+ *
+ *   public User(int uid) {
+ *       this.uid = uid;
+ *   }
+ *   public String getLastName() {
+ *       return lastName;
+ *   }
+ *   public void setLastName(String lastName) {
+ *       this.lastName = lastName;
+ *   }
+ * }
+ * </pre>
+ *
+ * @see Dao
+ * @see Database
+ * @see PrimaryKey
+ * @see ColumnInfo
+ * @see Index
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface Entity {
+    /**
+     * The table name in the SQLite database. If not set, defaults to the class name.
+     *
+     * @return The SQLite tableName of the Entity.
+     */
+    String tableName() default "";
+
+    /**
+     * List of indices on the table.
+     *
+     * @return The list of indices on the table.
+     */
+    Index[] indices() default {};
+
+    /**
+     * If set to {@code true}, any Index defined in parent classes of this class will be carried
+     * over to the current {@code Entity}. Note that if you set this to {@code true}, even if the
+     * {@code Entity} has a parent which sets this value to {@code false}, the {@code Entity} will
+     * still inherit indices from it and its parents.
+     * <p>
+     * When the {@code Entity} inherits an index from the parent, it is <b>always</b> renamed with
+     * the default naming schema since SQLite <b>does not</b> allow using the same index name in
+     * multiple tables. See {@link Index} for the details of the default name.
+     * <p>
+     * By default, indices defined in parent classes are dropped to avoid unexpected indices.
+     * When this happens, you will receive a {@link RoomWarnings#INDEX_FROM_PARENT_FIELD_IS_DROPPED}
+     * or {@link RoomWarnings#INDEX_FROM_PARENT_IS_DROPPED} warning during compilation.
+     *
+     * @return True if indices from parent classes should be automatically inherited by this Entity,
+     *         false otherwise. Defaults to false.
+     */
+    boolean inheritSuperIndices() default false;
+
+    /**
+     * The list of Primary Key column names.
+     * <p>
+     * If you would like to define an auto generated primary key, you can use {@link PrimaryKey}
+     * annotation on the field with {@link PrimaryKey#autoGenerate()} set to {@code true}.
+     *
+     * @return The primary key of this Entity. Can be empty if the class has a field annotated
+     * with {@link PrimaryKey}.
+     */
+    String[] primaryKeys() default {};
+
+    /**
+     * List of {@link ForeignKey} constraints on this entity.
+     *
+     * @return The list of {@link ForeignKey} constraints on this entity.
+     */
+    ForeignKey[] foreignKeys() default {};
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/ForeignKey.java b/room/common/src/main/java/android/arch/persistence/room/ForeignKey.java
new file mode 100644
index 0000000..4ba0fb3
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/ForeignKey.java
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.support.annotation.IntDef;
+
+/**
+ * Declares a foreign key on another {@link Entity}.
+ * <p>
+ * Foreign keys allows you to specify constraints across Entities such that SQLite will ensure that
+ * the relationship is valid when you modify the database.
+ * <p>
+ * When a foreign key constraint is specified, SQLite requires the referenced columns to be part of
+ * a unique index in the parent table or the primary key of that table. You must create a unique
+ * index in the parent entity that covers the referenced columns (Room will verify this at compile
+ * time and print an error if it is missing).
+ * <p>
+ * It is also recommended to create an index on the child table to avoid full table scans when the
+ * parent table is modified. If a suitable index on the child table is missing, Room will print
+ * {@link RoomWarnings#MISSING_INDEX_ON_FOREIGN_KEY_CHILD} warning.
+ * <p>
+ * A foreign key constraint can be deferred until the transaction is complete. This is useful if
+ * you are doing bulk inserts into the database in a single transaction. By default, foreign key
+ * constraints are immediate but you can change this value by setting {@link #deferred()} to
+ * {@code true}. You can also use
+ * <a href="https://sqlite.org/pragma.html#pragma_defer_foreign_keys">defer_foreign_keys</a> PRAGMA
+ * to defer them depending on your transaction.
+ * <p>
+ * Please refer to the SQLite <a href="https://sqlite.org/foreignkeys.html>foreign keys</a>
+ * documentation for details.
+ */
+public @interface ForeignKey {
+    /**
+     * The parent Entity to reference. It must be a class annotated with {@link Entity} and
+     * referenced in the same database.
+     *
+     * @return The parent Entity.
+     */
+    Class entity();
+
+    /**
+     * The list of column names in the parent {@link Entity}.
+     * <p>
+     * Number of columns must match the number of columns specified in {@link #childColumns()}.
+     *
+     * @return The list of column names in the parent Entity.
+     * @see #childColumns()
+     */
+    String[] parentColumns();
+
+    /**
+     * The list of column names in the current {@link Entity}.
+     * <p>
+     * Number of columns must match the number of columns specified in {@link #parentColumns()}.
+     *
+     * @return The list of column names in the current Entity.
+     */
+    String[] childColumns();
+
+    /**
+     * Action to take when the parent {@link Entity} is deleted from the database.
+     * <p>
+     * By default, {@link #NO_ACTION} is used.
+     *
+     * @return The action to take when the referenced entity is deleted from the database.
+     */
+    @Action int onDelete() default NO_ACTION;
+
+    /**
+     * Action to take when the parent {@link Entity} is updated in the database.
+     * <p>
+     * By default, {@link #NO_ACTION} is used.
+     *
+     * @return The action to take when the referenced entity is updated in the database.
+     */
+    @Action int onUpdate() default NO_ACTION;
+
+    /**
+     * * A foreign key constraint can be deferred until the transaction is complete. This is useful
+     * if you are doing bulk inserts into the database in a single transaction. By default, foreign
+     * key constraints are immediate but you can change it by setting this field to {@code true}.
+     * You can also use
+     * <a href="https://sqlite.org/pragma.html#pragma_defer_foreign_keys">defer_foreign_keys</a>
+     * PRAGMA to defer them depending on your transaction.
+     *
+     * @return Whether the foreign key constraint should be deferred until the transaction is
+     * complete. Defaults to {@code false}.
+     */
+    boolean deferred() default false;
+
+    /**
+     * Possible value for {@link #onDelete()} or {@link #onUpdate()}.
+     * <p>
+     * When a parent key is modified or deleted from the database, no special action is taken.
+     * This means that SQLite will not make any effort to fix the constraint failure, instead,
+     * reject the change.
+     */
+    int NO_ACTION = 1;
+
+    /**
+     * Possible value for {@link #onDelete()} or {@link #onUpdate()}.
+     * <p>
+     * The RESTRICT action means that the application is prohibited from deleting
+     * (for {@link #onDelete()}) or modifying (for {@link #onUpdate()}) a parent key when there
+     * exists one or more child keys mapped to it. The difference between the effect of a RESTRICT
+     * action and normal foreign key constraint enforcement is that the RESTRICT action processing
+     * happens as soon as the field is updated - not at the end of the current statement as it would
+     * with an immediate constraint, or at the end of the current transaction as it would with a
+     * {@link #deferred()} constraint.
+     * <p>
+     * Even if the foreign key constraint it is attached to is {@link #deferred()}, configuring a
+     * RESTRICT action causes SQLite to return an error immediately if a parent key with dependent
+     * child keys is deleted or modified.
+     */
+    int RESTRICT = 2;
+
+    /**
+     * Possible value for {@link #onDelete()} or {@link #onUpdate()}.
+     * <p>
+     * If the configured action is "SET NULL", then when a parent key is deleted
+     * (for {@link #onDelete()}) or modified (for {@link #onUpdate()}), the child key columns of all
+     * rows in the child table that mapped to the parent key are set to contain {@code NULL} values.
+     */
+    int SET_NULL = 3;
+
+    /**
+     * Possible value for {@link #onDelete()} or {@link #onUpdate()}.
+     * <p>
+     * The "SET DEFAULT" actions are similar to {@link #SET_NULL}, except that each of the child key
+     * columns is set to contain the columns default value instead of {@code NULL}.
+     */
+    int SET_DEFAULT = 4;
+
+    /**
+     * Possible value for {@link #onDelete()} or {@link #onUpdate()}.
+     * <p>
+     * A "CASCADE" action propagates the delete or update operation on the parent key to each
+     * dependent child key. For {@link #onDelete()} action, this means that each row in the child
+     * entity that was associated with the deleted parent row is also deleted. For an
+     * {@link #onUpdate()} action, it means that the values stored in each dependent child key are
+     * modified to match the new parent key values.
+     */
+    int CASCADE = 5;
+
+    /**
+     * Constants definition for values that can be used in {@link #onDelete()} and
+     * {@link #onUpdate()}.
+     */
+    @IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
+    @interface Action {
+    }
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Ignore.java b/room/common/src/main/java/android/arch/persistence/room/Ignore.java
new file mode 100644
index 0000000..6effc33
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Ignore.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Ignores the marked element from Room's processing logic.
+ * <p>
+ * This annotation can be used in multiple places where Room processor runs. For instance, you can
+ * add it to a field of an {@link Entity} and Room will not persist that field.
+ */
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface Ignore {
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Index.java b/room/common/src/main/java/android/arch/persistence/room/Index.java
new file mode 100644
index 0000000..b814efd
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Index.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares an index on an Entity.
+ * see: <a href="https://sqlite.org/lang_createindex.html">SQLite Index Documentation</a>
+ * <p>
+ * Adding an index usually speeds up your select queries but will slow down other queries like
+ * insert or update. You should be careful when adding indices to ensure that this additional cost
+ * is worth the gain.
+ * <p>
+ * There are 2 ways to define an index in an {@link Entity}. You can either set
+ * {@link ColumnInfo#index()} property to index individual fields or define composite indices via
+ * {@link Entity#indices()}.
+ * <p>
+ * If an indexed field is embedded into another Entity via {@link Embedded}, it is <b>NOT</b>
+ * added as an index to the containing {@link Entity}. If you want to keep it indexed, you must
+ * re-declare it in the containing {@link Entity}.
+ * <p>
+ * Similarly, if an {@link Entity} extends another class, indices from the super classes are
+ * <b>NOT</b> inherited. You must re-declare them in the child {@link Entity} or set
+ * {@link Entity#inheritSuperIndices()} to {@code true}.
+ * */
+@Target({})
+@Retention(RetentionPolicy.CLASS)
+public @interface Index {
+    /**
+     * List of column names in the Index.
+     * <p>
+     * The order of columns is important as it defines when SQLite can use a particular index.
+     * See <a href="https://www.sqlite.org/optoverview.html">SQLite documentation</a> for details on
+     * index usage in the query optimizer.
+     *
+     * @return The list of column names in the Index.
+     */
+    String[] value();
+
+    /**
+     * Name of the index. If not set, Room will set it to the list of columns joined by '_' and
+     * prefixed by "index_${tableName}". So if you have a table with name "Foo" and with an index
+     * of {"bar", "baz"}, generated index name will be  "index_Foo_bar_baz". If you need to specify
+     * the index in a query, you should never rely on this name, instead, specify a name for your
+     * index.
+     *
+     * @return The name of the index.
+     */
+    String name() default "";
+
+    /**
+     * If set to true, this will be a unique index and any duplicates will be rejected.
+     *
+     * @return True if index is unique. False by default.
+     */
+    boolean unique() default false;
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Insert.java b/room/common/src/main/java/android/arch/persistence/room/Insert.java
new file mode 100644
index 0000000..802dd96
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Insert.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method in a {@link Dao} annotated class as an insert method.
+ * <p>
+ * The implementation of the method will insert its parameters into the database.
+ * <p>
+ * All of the parameters of the Insert method must either be classes annotated with {@link Entity}
+ * or collections/array of it.
+ * <p>
+ * Example:
+ * <pre>
+ * {@literal @}Dao
+ * public interface MyDao {
+ *     {@literal @}Insert(onConflict = OnConflictStrategy.REPLACE)
+ *     public void insertUsers(User... users);
+ *     {@literal @}Insert
+ *     public void insertBoth(User user1, User user2);
+ *     {@literal @}Insert
+ *     public void insertWithFriends(User user, List&lt;User&gt; friends);
+ * }
+ * </pre>
+ *
+ * @see Update
+ * @see Delete
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface Insert {
+    /**
+     * What to do if a conflict happens.
+     * @see <a href="https://sqlite.org/lang_conflict.html">SQLite conflict documentation</a>
+     *
+     * @return How to handle conflicts. Defaults to {@link OnConflictStrategy#ABORT}.
+     */
+    @OnConflictStrategy
+    int onConflict() default OnConflictStrategy.ABORT;
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/OnConflictStrategy.java b/room/common/src/main/java/android/arch/persistence/room/OnConflictStrategy.java
new file mode 100644
index 0000000..5217b61
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/OnConflictStrategy.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Set of conflict handling strategies for various {@link Dao} methods.
+ * <p>
+ * Check <a href="https://sqlite.org/lang_conflict.html">SQLite conflict documentation</a> for
+ * details.
+ */
+@Retention(SOURCE)
+@IntDef({OnConflictStrategy.REPLACE, OnConflictStrategy.ROLLBACK, OnConflictStrategy.ABORT,
+        OnConflictStrategy.FAIL, OnConflictStrategy.IGNORE})
+public @interface OnConflictStrategy {
+    /**
+     * OnConflict strategy constant to replace the old data and continue the transaction.
+     */
+    int REPLACE = 1;
+    /**
+     * OnConflict strategy constant to rollback the transaction.
+     */
+    int ROLLBACK = 2;
+    /**
+     * OnConflict strategy constant to abort the transaction.
+     */
+    int ABORT = 3;
+    /**
+     * OnConflict strategy constant to fail the transaction.
+     */
+    int FAIL = 4;
+    /**
+     * OnConflict strategy constant to ignore the conflict.
+     */
+    int IGNORE = 5;
+
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/PrimaryKey.java b/room/common/src/main/java/android/arch/persistence/room/PrimaryKey.java
new file mode 100644
index 0000000..9a8062ca
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/PrimaryKey.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a field in an {@link Entity} as the primary key.
+ * <p>
+ * If you would like to define a composite primary key, you should use {@link Entity#primaryKeys()}
+ * method.
+ * <p>
+ * Each {@link Entity} must declare a primary key unless one of its super classes declares a
+ * primary key. If both an {@link Entity} and its super class defines a {@code PrimaryKey}, the
+ * child's {@code PrimaryKey} definition will override the parent's {@code PrimaryKey}.
+ * <p>
+ * If {@code PrimaryKey} annotation is used on a {@link Embedded}d field, all columns inherited
+ * from that embedded field becomes the composite primary key (including its grand children
+ * fields).
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface PrimaryKey {
+    /**
+     * Set to true to let SQLite generate the unique id.
+     * <p>
+     * When set to {@code true}, the SQLite type affinity for the field should be {@code INTEGER}.
+     * <p>
+     * If the field type is {@code long} or {@code int} (or its TypeConverter converts it to a
+     * {@code long} or {@code int}), {@link Insert} methods treat {@code 0} as not-set while
+     * inserting the item.
+     * <p>
+     * If the field's type is {@link Integer} or {@link Long} (or its TypeConverter converts it to
+     * an {@link Integer} or a {@link Long}), {@link Insert} methods treat {@code null} as
+     * not-set while inserting the item.
+     *
+     * @return Whether the primary key should be auto-generated by SQLite or not. Defaults
+     * to false.
+     */
+    boolean autoGenerate() default false;
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Query.java b/room/common/src/main/java/android/arch/persistence/room/Query.java
new file mode 100644
index 0000000..bd838e8
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Query.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method in a {@link Dao} annotated class as a query method.
+ * <p>
+ * The value of the annotation includes the query that will be run when this method is called. This
+ * query is <b>verified at compile time</b> by Room to ensure that it compiles fine against the
+ * database.
+ * <p>
+ * The arguments of the method will be bound to the bind arguments in the SQL statement. See
+ * <href="https://www.sqlite.org/c3ref/bind_blob.html">SQLite's binding documentation</> for
+ * details of bind arguments in SQLite.
+ * <p>
+ * Room only supports named bind parameter {@code :name} to avoid any confusion between the
+ * method parameters and the query bind parameters.
+ * <p>
+ * Room will automatically bind the parameters of the method into the bind arguments. This is done
+ * by matching the name of the parameters to the name of the bind arguments.
+ * <pre>
+ *     {@literal @}Query("SELECT * FROM user WHERE user_name LIKE :name AND last_name LIKE :last")
+ *     public abstract List&lt;User&gt; findUsersByNameAndLastName(String name, String last);
+ * </pre>
+ * <p>
+ * As an extension over SQLite bind arguments, Room supports binding a list of parameters to the
+ * query. At runtime, Room will build the correct query to have matching number of bind arguments
+ * depending on the number of items in the method parameter.
+ * <pre>
+ *     {@literal @}Query("SELECT * FROM user WHERE uid IN(:userIds)")
+ *     public abstract List<User> findByIds(int[] userIds);
+ * </pre>
+ * For the example above, if the {@code userIds} is an array of 3 elements, Room will run the
+ * query as: {@code SELECT * FROM user WHERE uid IN(?, ?, ?)} and bind each item in the
+ * {@code userIds} array into the statement.
+ * <p>
+ * There are 3 types of queries supported in {@code Query} methods: SELECT, UPDATE and DELETE.
+ * <p>
+ * For SELECT queries, Room will infer the result contents from the method's return type and
+ * generate the code that will automatically convert the query result into the method's return
+ * type. For single result queries, the return type can be any java object. For queries that return
+ * multiple values, you can use {@link java.util.List} or {@code Array}. In addition to these, any
+ * query may return {@link android.database.Cursor Cursor} or any query result can be wrapped in
+ * a {@link android.arch.lifecycle.LiveData LiveData}.
+ * <p>
+ * <b>RxJava2</b> If you are using RxJava2, you can also return {@code Flowable<T>} or
+ * {@code Publisher<T>} from query methods. Since Reactive Streams does not allow {@code null}, if
+ * the query returns a nullable type, it will not dispatch anything if the value is {@code null}
+ * (like fetching an {@link Entity} row that does not exist).
+ * You can return {@code Flowable<T[]>} or {@code Flowable<List<T>>} to workaround this limitation.
+ * <p>
+ * Both {@code Flowable<T>} and {@code Publisher<T>} will observe the database for changes and
+ * re-dispatch if data changes. If you want to query the database without observing changes, you can
+ * use {@code Maybe<T>} or {@code Single<T>}. If a {@code Single<T>} query returns {@code null},
+ * Room will throw
+ * {@link android.arch.persistence.room.EmptyResultSetException EmptyResultSetException}.
+ * <p>
+ * UPDATE or DELETE queries can return {@code void} or {@code int}. If it is an {@code int},
+ * the value is the number of rows affected by this query.
+ * <p>
+ * You can return arbitrary POJOs from your query methods as long as the fields of the POJO match
+ * the column names in the query result.
+ * For example, if you have class:
+ * <pre>
+ * class UserName {
+ *     public String name;
+ *     {@literal @}ColumnInfo(name = "last_name")
+ *     public String lastName;
+ * }
+ * </pre>
+ * You can write a query like this:
+ * <pre>
+ *     {@literal @}Query("SELECT last_name, name FROM user WHERE uid = :userId LIMIT 1")
+ *     public abstract UserName findOneUserName(int userId);
+ * </pre>
+ * And Room will create the correct implementation to convert the query result into a
+ * {@code UserName} object. If there is a mismatch between the query result and the fields of the
+ * POJO, as long as there is at least 1 field match, Room prints a
+ * {@link RoomWarnings#CURSOR_MISMATCH} warning and sets as many fields as it can.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.CLASS)
+public @interface Query {
+    /**
+     * The SQLite query to be run.
+     * @return The query to be run.
+     */
+    String value();
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Relation.java b/room/common/src/main/java/android/arch/persistence/room/Relation.java
new file mode 100644
index 0000000..0d2f152
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Relation.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A convenience annotation which can be used in a Pojo to automatically fetch relation entities.
+ * When the Pojo is returned from a query, all of its relations are also fetched by Room.
+ *
+ * <pre>
+ * {@literal @}Entity
+ * public class Pet {
+ *     int userId;
+ *     String name;
+ *     // other fields
+ * }
+ * public class UserNameAndAllPets {
+ *   public int id;
+ *   public String name;
+ *   {@literal @}Relation(parentColumn = "id", entityColumn = "userId")
+ *   public List&lt;Pet&gt; pets;
+ * }
+ *
+ * {@literal @}Dao
+ * public interface UserPetDao {
+ *     {@literal @}Query("SELECT id, name from User WHERE age &gte; ?")
+ *     public List&lt;UserNameAndAllPets&gt; loadUserAndPets(int minAge);
+ * }
+ * </pre>
+ * <p>
+ * The type of the field annotated with {@code Relation} must be a {@link java.util.List} or
+ * {@link java.util.Set}. By default, the {@link Entity} type is inferred from the return type.
+ * If you would like to return a different object, you can specify the {@link #entity()} property
+ * in the annotation.
+ * <pre>
+ * public class User {
+ *     int id;
+ *     // other fields
+ * }
+ * public class PetNameAndId {
+ *     int id;
+ *     String name;
+ * }
+ * public class UserAllPets {
+ *   {@literal @}Embedded
+ *   public User user;
+ *   {@literal @}Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class)
+ *   public List<PetNameAndId> pets;
+ * }
+ * {@literal @}Dao
+ * public interface UserPetDao {
+ *     {@literal @}Query("SELECT * from User WHERE age &gte; ?")
+ *     public List&lt;UserAllPets&gt; loadUserAndPets(int minAge);
+ * }
+ * </pre>
+ * <p>
+ * In the example above, {@code PetNameAndId} is a regular but all of fields are fetched
+ * from the {@code entity} defined in the {@code @Relation} annotation (<i>Pet</i>).
+ * {@code PetNameAndId} could also define its own relations all of which would also be fetched
+ * automatically.
+ * <p>
+ * If you would like to specify which columns are fetched from the child {@link Entity}, you can
+ * use {@link #projection()} property in the {@code Relation} annotation.
+ * <pre>
+ * public class UserAndAllPets {
+ *   {@literal @}Embedded
+ *   public User user;
+ *   {@literal @}Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class,
+ *           projection = {"name"})
+ *   public List<String> petNames;
+ * }
+ * </pre>
+ * <p>
+ * Note that {@code @Relation} annotation can be used only in Pojo classes, an {@link Entity} class
+ * cannot have relations. This is a design decision to avoid common pitfalls in {@link Entity}
+ * setups. You can read more about it in the main Room documentation. When loading data, you can
+ * simply work around this limitation by creating Pojo classes that extend the {@link Entity}.
+ *
+ * Note that the {@code @Relation} annotated field cannot be a constructor parameter, it must be
+ * public or have a public setter.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface Relation {
+    /**
+     * The entity to fetch the item from. You don't need to set this if the entity matches the
+     * type argument in the return type.
+     *
+     * @return The entity to fetch from. By default, inherited from the return type.
+     */
+    Class entity() default Object.class;
+
+    /**
+     * Reference field in the parent Pojo.
+     * <p>
+     * If you would like to access to a sub item of a {@link Embedded}d field, you can use
+     * the {@code .} notation.
+     * <p>
+     * For instance, if you have a {@link Embedded}d field named {@code user} with a sub field
+     * {@code id}, you can reference it via {@code user.id}.
+     * <p>
+     * This value will be matched against the value defined in {@link #entityColumn()}.
+     *
+     * @return The field reference in the parent object.
+     */
+    String parentColumn();
+
+    /**
+     * The field path to match in the {@link #entity()}. This value will be matched against the
+     * value defined in {@link #parentColumn()}.
+     */
+    String entityColumn();
+
+    /**
+     * If sub fields should be fetched from the entity, you can specify them using this field.
+     * <p>
+     * By default, inferred from the the return type.
+     *
+     * @return The list of columns to be selected from the {@link #entity()}.
+     */
+    String[] projection() default {};
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/RoomMasterTable.java b/room/common/src/main/java/android/arch/persistence/room/RoomMasterTable.java
new file mode 100644
index 0000000..621845d
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/RoomMasterTable.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.support.annotation.RestrictTo;
+
+/**
+ * Schema information about Room's master table.
+ *
+ * @hide
+ */
+@SuppressWarnings("WeakerAccess")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class RoomMasterTable {
+    /**
+     * The master table where room keeps its metadata information.
+     */
+    public static final String TABLE_NAME = "room_master_table";
+    // must match the runtime property Room#MASTER_TABLE_NAME
+    public static final String NAME = "room_master_table";
+    private static final String COLUMN_ID = "id";
+    private static final String COLUMN_IDENTITY_HASH = "identity_hash";
+    public static final String DEFAULT_ID = "42";
+
+    public static final String CREATE_QUERY = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+            + COLUMN_ID + " INTEGER PRIMARY KEY,"
+            + COLUMN_IDENTITY_HASH + " TEXT)";
+
+    public static final String READ_QUERY = "SELECT " + COLUMN_IDENTITY_HASH
+            + " FROM " + TABLE_NAME + " WHERE "
+            + COLUMN_ID + " = " + DEFAULT_ID + " LIMIT 1";
+
+    /**
+     * We don't escape here since we know what we are passing.
+     */
+    public static String createInsertQuery(String hash) {
+        return "INSERT OR REPLACE INTO " + TABLE_NAME + " ("
+                + COLUMN_ID + "," + COLUMN_IDENTITY_HASH + ")"
+                + " VALUES(" + DEFAULT_ID + ", \"" + hash + "\")";
+    }
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/RoomWarnings.java b/room/common/src/main/java/android/arch/persistence/room/RoomWarnings.java
new file mode 100644
index 0000000..91f32e4
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/RoomWarnings.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+/**
+ * The list of warnings that are produced by Room.
+ * <p>
+ * You can use these values inside a {@link SuppressWarnings} annotation to disable the warnings.
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class RoomWarnings {
+    /**
+     * The warning dispatched by Room when the return value of a {@link Query} method does not
+     * exactly match the fields in the query result.
+     */
+    // if you change this, don't forget to change android.arch.persistence.room.vo.Warning
+    public static final String CURSOR_MISMATCH = "ROOM_CURSOR_MISMATCH";
+
+    /**
+     * Reported when Room cannot verify database queries during compilation due to lack of
+     * tmp dir access in JVM.
+     */
+    public static final String MISSING_JAVA_TMP_DIR = "ROOM_MISSING_JAVA_TMP_DIR";
+
+    /**
+     * Reported when Room cannot verify database queries during compilation. This usually happens
+     * when it cannot find the SQLite JDBC driver on the host machine.
+     * <p>
+     * Room can function without query verification but its functionality will be limited.
+     */
+    public static final String CANNOT_CREATE_VERIFICATION_DATABASE =
+            "ROOM_CANNOT_CREATE_VERIFICATION_DATABASE";
+
+    /**
+     * Reported when an {@link Entity} field that is annotated with {@link Embedded} has a
+     * sub field which is annotated with {@link PrimaryKey} but the {@link PrimaryKey} is dropped
+     * while composing it into the parent object.
+     */
+    public static final String PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED =
+            "ROOM_EMBEDDED_PRIMARY_KEY_IS_DROPPED";
+
+    /**
+     * Reported when an {@link Entity} field that is annotated with {@link Embedded} has a
+     * sub field which has a {@link ColumnInfo} annotation with {@code index = true}.
+     * <p>
+     * You can re-define the index in the containing {@link Entity}.
+     */
+    public static final String INDEX_FROM_EMBEDDED_FIELD_IS_DROPPED =
+            "ROOM_EMBEDDED_INDEX_IS_DROPPED";
+
+    /**
+     * Reported when an {@link Entity} that has a {@link Embedded}d field whose type is another
+     * {@link Entity} and that {@link Entity} has some indices defined.
+     * These indices will NOT be created in the containing {@link Entity}. If you want to preserve
+     * them, you can re-define them in the containing {@link Entity}.
+     */
+    public static final String INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED =
+            "ROOM_EMBEDDED_ENTITY_INDEX_IS_DROPPED";
+
+    /**
+     * Reported when an {@link Entity}'s parent declares an {@link Index}. Room does not
+     * automatically inherit these indices to avoid hidden costs or unexpected constraints.
+     * <p>
+     * If you want your child class to have the indices of the parent, you must re-declare
+     * them in the child class. Alternatively, you can set {@link Entity#inheritSuperIndices()}
+     * to {@code true}.
+     */
+    public static final String INDEX_FROM_PARENT_IS_DROPPED =
+            "ROOM_PARENT_INDEX_IS_DROPPED";
+
+    /**
+     * Reported when an {@link Entity} inherits a field from its super class and the field has a
+     * {@link ColumnInfo} annotation with {@code index = true}.
+     * <p>
+     * These indices are dropped for the {@link Entity} and you would need to re-declare them if
+     * you want to keep them. Alternatively, you can set {@link Entity#inheritSuperIndices()}
+     * to {@code true}.
+     */
+    public static final String INDEX_FROM_PARENT_FIELD_IS_DROPPED =
+            "ROOM_PARENT_FIELD_INDEX_IS_DROPPED";
+
+    /**
+     * Reported when a {@link Relation} {@link Entity}'s SQLite column type does not match the type
+     * in the parent. Room will still do the matching using {@code String} representations.
+     */
+    public static final String RELATION_TYPE_MISMATCH = "ROOM_RELATION_TYPE_MISMATCH";
+
+    /**
+     * Reported when a `room.schemaLocation` argument is not provided into the annotation processor.
+     * You can either set {@link Database#exportSchema()} to {@code false} or provide
+     * `room.schemaLocation` to the annotation processor. You are strongly adviced to provide it
+     * and also commit them into your version control system.
+     */
+    public static final String MISSING_SCHEMA_LOCATION = "ROOM_MISSING_SCHEMA_LOCATION";
+
+    /**
+     * When there is a foreign key from Entity A to Entity B, it is a good idea to index the
+     * reference columns in B, otherwise, each modification on Entity A will trigger a full table
+     * scan on Entity B.
+     * <p>
+     * If Room cannot find a proper index in the child entity (Entity B in this case), Room will
+     * print this warning.
+     */
+    public static final String MISSING_INDEX_ON_FOREIGN_KEY_CHILD =
+            "ROOM_MISSING_FOREIGN_KEY_CHILD_INDEX";
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/SkipQueryVerification.java b/room/common/src/main/java/android/arch/persistence/room/SkipQueryVerification.java
new file mode 100644
index 0000000..dc2ded3
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/SkipQueryVerification.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Skips database verification for the annotated element.
+ * <p>
+ * If it is a class annotated with {@link Database}, none of the queries for the database will
+ * be verified at compile time.
+ * <p>
+ * If it is a class annotated with {@link Dao}, none of the queries in the Dao class will
+ * be verified at compile time.
+ * <p>
+ * If it is a method in a Dao class, just the method's sql verification will be skipped.
+ * <p>
+ * You should use this as the last resort if Room cannot properly understand your query and you are
+ * 100% sure it works. Removing validation may limit the functionality of Room since it won't be
+ * able to understand the query response.
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SkipQueryVerification {
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/TypeConverter.java b/room/common/src/main/java/android/arch/persistence/room/TypeConverter.java
new file mode 100644
index 0000000..bea68c8
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/TypeConverter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method as a type converter. A class can have as many @Converter methods as it needs.
+ * <p>
+ * Each converter method should receive 1 parameter and have non-void return type.
+ *
+ * <pre>
+ * // example converter for java.util.Date
+ * public static class Converters {
+ *    {@literal @}TypeConverter
+ *    public Date fromTimestamp(Long value) {
+ *        return value == null ? null : new Date(value);
+ *    }
+ *
+ *    {@literal @}TypeConverter
+ *    public Long dateToTimestamp(Date date) {
+ *        if (date == null) {
+ *            return null;
+ *        } else {
+ *            return date.getTime();
+ *        }
+ *    }
+ *}
+ * </pre>
+ * @see TypeConverters
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface TypeConverter {
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/TypeConverters.java b/room/common/src/main/java/android/arch/persistence/room/TypeConverters.java
new file mode 100644
index 0000000..7f7e6ab
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/TypeConverters.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies additional type converters that Room can use. The TypeConverter is added to the scope
+ * of the element so if you put it on a class / interface, all methods / fields in that class will
+ * be able to use the converters.
+ * <ul>
+ * <li>If you put it on a {@link Database}, all Daos and Entities in that database will be able to
+ * use it.
+ * <li>If you put it on a {@link Dao}, all methods in the Dao will be able to use it.
+ * <li>If you put it on an {@link Entity}, all fields of the Entity will be able to use it.
+ * <li>If you put it on a POJO, all fields of the POJO will be able to use it.
+ * <li>If you put it on an {@link Entity} field, only that field will be able to use it.
+ * <li>If you put it on a {@link Dao} method, all parameters of the method will be able to use it.
+ * <li>If you put it on a {@link Dao} method parameter, just that field will be able to use it.
+ * </ul>
+ * @see TypeConverter
+ */
+@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.CLASS)
+public @interface TypeConverters {
+    /**
+     * The list of type converter classes. If converter methods are not static, Room will create
+     * an instance of these classes.
+     *
+     * @return The list of classes that contains the converter methods.
+     */
+    Class<?>[] value();
+}
diff --git a/room/common/src/main/java/android/arch/persistence/room/Update.java b/room/common/src/main/java/android/arch/persistence/room/Update.java
new file mode 100644
index 0000000..670a085
--- /dev/null
+++ b/room/common/src/main/java/android/arch/persistence/room/Update.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room;
+
+/**
+ * Marks a method in a {@link Dao} annotated class as an update method.
+ * <p>
+ * The implementation of the method will update its parameters in the database if they already
+ * exists (checked by primary keys). If they don't already exists, this option will not change the
+ * database.
+ * <p>
+ * All of the parameters of the Update method must either be classes annotated with {@link Entity}
+ * or collections/array of it.
+ *
+ * @see Insert
+ * @see Delete
+ */
+public @interface Update {
+    /**
+     * What to do if a conflict happens.
+     * @see <a href="https://sqlite.org/lang_conflict.html">SQLite conflict documentation</a>
+     *
+     * @return How to handle conflicts. Defaults to {@link OnConflictStrategy#ABORT}.
+     */
+    @OnConflictStrategy
+    int onConflict() default OnConflictStrategy.ABORT;
+}
diff --git a/room/compiler/SQLite.g4 b/room/compiler/SQLite.g4
new file mode 100644
index 0000000..8eb895d
--- /dev/null
+++ b/room/compiler/SQLite.g4
@@ -0,0 +1,911 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 by Bart Kiers
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Project      : sqlite-parser; an ANTLR4 grammar for SQLite
+ *                https://github.com/bkiers/sqlite-parser
+ * Developed by : Bart Kiers, bart@big-o.nl
+ */
+grammar SQLite;
+
+parse
+ : ( sql_stmt_list | error )* EOF
+ ;
+
+error
+ : UNEXPECTED_CHAR 
+   { 
+     throw new RuntimeException("UNEXPECTED_CHAR=" + $UNEXPECTED_CHAR.text); 
+   }
+ ;
+
+sql_stmt_list
+ : ';'* sql_stmt ( ';'+ sql_stmt )* ';'*
+ ;
+
+sql_stmt
+ : ( K_EXPLAIN ( K_QUERY K_PLAN )? )? ( alter_table_stmt
+                                      | analyze_stmt
+                                      | attach_stmt
+                                      | begin_stmt
+                                      | commit_stmt
+                                      | compound_select_stmt
+                                      | create_index_stmt
+                                      | create_table_stmt
+                                      | create_trigger_stmt
+                                      | create_view_stmt
+                                      | create_virtual_table_stmt
+                                      | delete_stmt
+                                      | delete_stmt_limited
+                                      | detach_stmt
+                                      | drop_index_stmt
+                                      | drop_table_stmt
+                                      | drop_trigger_stmt
+                                      | drop_view_stmt
+                                      | factored_select_stmt
+                                      | insert_stmt
+                                      | pragma_stmt
+                                      | reindex_stmt
+                                      | release_stmt
+                                      | rollback_stmt
+                                      | savepoint_stmt
+                                      | simple_select_stmt
+                                      | select_stmt
+                                      | update_stmt
+                                      | update_stmt_limited
+                                      | vacuum_stmt )
+ ;
+
+alter_table_stmt
+ : K_ALTER K_TABLE ( database_name '.' )? table_name
+   ( K_RENAME K_TO new_table_name
+   | K_ADD K_COLUMN? column_def
+   )
+ ;
+
+analyze_stmt
+ : K_ANALYZE ( database_name | table_or_index_name | database_name '.' table_or_index_name )?
+ ;
+
+attach_stmt
+ : K_ATTACH K_DATABASE? expr K_AS database_name
+ ;
+
+begin_stmt
+ : K_BEGIN ( K_DEFERRED | K_IMMEDIATE | K_EXCLUSIVE )? ( K_TRANSACTION transaction_name? )?
+ ;
+
+commit_stmt
+ : ( K_COMMIT | K_END ) ( K_TRANSACTION transaction_name? )?
+ ;
+
+compound_select_stmt
+ : with_clause?
+   select_core ( ( K_UNION K_ALL? | K_INTERSECT | K_EXCEPT ) select_core )+
+   ( K_ORDER K_BY ordering_term ( ',' ordering_term )* )?
+   ( K_LIMIT expr ( ( K_OFFSET | ',' ) expr )? )?
+ ;
+
+create_index_stmt
+ : K_CREATE K_UNIQUE? K_INDEX ( K_IF K_NOT K_EXISTS )?
+   ( database_name '.' )? index_name K_ON table_name '(' indexed_column ( ',' indexed_column )* ')'
+   ( K_WHERE expr )?
+ ;
+
+create_table_stmt
+ : K_CREATE ( K_TEMP | K_TEMPORARY )? K_TABLE ( K_IF K_NOT K_EXISTS )?
+   ( database_name '.' )? table_name
+   ( '(' column_def ( ',' column_def )*? ( ',' table_constraint )* ')' ( K_WITHOUT IDENTIFIER )?
+   | K_AS select_stmt
+   )
+ ;
+
+create_trigger_stmt
+ : K_CREATE ( K_TEMP | K_TEMPORARY )? K_TRIGGER ( K_IF K_NOT K_EXISTS )?
+   ( database_name '.' )? trigger_name ( K_BEFORE  | K_AFTER | K_INSTEAD K_OF )?
+   ( K_DELETE | K_INSERT | K_UPDATE ( K_OF column_name ( ',' column_name )* )? ) K_ON ( database_name '.' )? table_name
+   ( K_FOR K_EACH K_ROW )? ( K_WHEN expr )?
+   K_BEGIN ( ( update_stmt | insert_stmt | delete_stmt | select_stmt ) ';' )+ K_END
+ ;
+
+create_view_stmt
+ : K_CREATE ( K_TEMP | K_TEMPORARY )? K_VIEW ( K_IF K_NOT K_EXISTS )?
+   ( database_name '.' )? view_name K_AS select_stmt
+ ;
+
+create_virtual_table_stmt
+ : K_CREATE K_VIRTUAL K_TABLE ( K_IF K_NOT K_EXISTS )?
+   ( database_name '.' )? table_name
+   K_USING module_name ( '(' module_argument ( ',' module_argument )* ')' )?
+ ;
+
+delete_stmt
+ : with_clause? K_DELETE K_FROM qualified_table_name
+   ( K_WHERE expr )?
+ ;
+
+delete_stmt_limited
+ : with_clause? K_DELETE K_FROM qualified_table_name
+   ( K_WHERE expr )?
+   ( ( K_ORDER K_BY ordering_term ( ',' ordering_term )* )?
+     K_LIMIT expr ( ( K_OFFSET | ',' ) expr )?
+   )?
+ ;
+
+detach_stmt
+ : K_DETACH K_DATABASE? database_name
+ ;
+
+drop_index_stmt
+ : K_DROP K_INDEX ( K_IF K_EXISTS )? ( database_name '.' )? index_name
+ ;
+
+drop_table_stmt
+ : K_DROP K_TABLE ( K_IF K_EXISTS )? ( database_name '.' )? table_name
+ ;
+
+drop_trigger_stmt
+ : K_DROP K_TRIGGER ( K_IF K_EXISTS )? ( database_name '.' )? trigger_name
+ ;
+
+drop_view_stmt
+ : K_DROP K_VIEW ( K_IF K_EXISTS )? ( database_name '.' )? view_name
+ ;
+
+factored_select_stmt
+ : with_clause?
+   select_core ( compound_operator select_core )*
+   ( K_ORDER K_BY ordering_term ( ',' ordering_term )* )?
+   ( K_LIMIT expr ( ( K_OFFSET | ',' ) expr )? )?
+ ;
+
+insert_stmt
+ : with_clause? ( K_INSERT
+                | K_REPLACE
+                | K_INSERT K_OR K_REPLACE
+                | K_INSERT K_OR K_ROLLBACK
+                | K_INSERT K_OR K_ABORT
+                | K_INSERT K_OR K_FAIL
+                | K_INSERT K_OR K_IGNORE ) K_INTO
+   ( database_name '.' )? table_name ( '(' column_name ( ',' column_name )* ')' )?
+   ( K_VALUES '(' expr ( ',' expr )* ')' ( ',' '(' expr ( ',' expr )* ')' )*
+   | select_stmt
+   | K_DEFAULT K_VALUES
+   )
+ ;
+
+pragma_stmt
+ : K_PRAGMA ( database_name '.' )? pragma_name ( '=' pragma_value
+                                               | '(' pragma_value ')' )?
+ ;
+
+reindex_stmt
+ : K_REINDEX ( collation_name
+             | ( database_name '.' )? ( table_name | index_name )
+             )?
+ ;
+
+release_stmt
+ : K_RELEASE K_SAVEPOINT? savepoint_name
+ ;
+
+rollback_stmt
+ : K_ROLLBACK ( K_TRANSACTION transaction_name? )? ( K_TO K_SAVEPOINT? savepoint_name )?
+ ;
+
+savepoint_stmt
+ : K_SAVEPOINT savepoint_name
+ ;
+
+simple_select_stmt
+ : with_clause?
+   select_core ( K_ORDER K_BY ordering_term ( ',' ordering_term )* )?
+   ( K_LIMIT expr ( ( K_OFFSET | ',' ) expr )? )?
+ ;
+
+select_stmt
+ : with_clause?
+   select_or_values ( compound_operator select_or_values )*
+   ( K_ORDER K_BY ordering_term ( ',' ordering_term )* )?
+   ( K_LIMIT expr ( ( K_OFFSET | ',' ) expr )? )?
+ ;
+
+select_or_values
+ : K_SELECT ( K_DISTINCT | K_ALL )? result_column ( ',' result_column )*
+   ( K_FROM ( table_or_subquery ( ',' table_or_subquery )* | join_clause ) )?
+   ( K_WHERE expr )?
+   ( K_GROUP K_BY expr ( ',' expr )* ( K_HAVING expr )? )?
+ | K_VALUES '(' expr ( ',' expr )* ')' ( ',' '(' expr ( ',' expr )* ')' )*
+ ;
+
+update_stmt
+ : with_clause? K_UPDATE ( K_OR K_ROLLBACK
+                         | K_OR K_ABORT
+                         | K_OR K_REPLACE
+                         | K_OR K_FAIL
+                         | K_OR K_IGNORE )? qualified_table_name
+   K_SET column_name '=' expr ( ',' column_name '=' expr )* ( K_WHERE expr )?
+ ;
+
+update_stmt_limited
+ : with_clause? K_UPDATE ( K_OR K_ROLLBACK
+                         | K_OR K_ABORT
+                         | K_OR K_REPLACE
+                         | K_OR K_FAIL
+                         | K_OR K_IGNORE )? qualified_table_name
+   K_SET column_name '=' expr ( ',' column_name '=' expr )* ( K_WHERE expr )?
+   ( ( K_ORDER K_BY ordering_term ( ',' ordering_term )* )?
+     K_LIMIT expr ( ( K_OFFSET | ',' ) expr )?
+   )?
+ ;
+
+vacuum_stmt
+ : K_VACUUM
+ ;
+
+column_def
+ : column_name type_name? column_constraint*
+ ;
+
+type_name
+ : name+? ( '(' signed_number ')'
+         | '(' signed_number ',' signed_number ')' )?
+ ;
+
+column_constraint
+ : ( K_CONSTRAINT name )?
+   ( K_PRIMARY K_KEY ( K_ASC | K_DESC )? conflict_clause K_AUTOINCREMENT?
+   | K_NOT? K_NULL conflict_clause
+   | K_UNIQUE conflict_clause
+   | K_CHECK '(' expr ')'
+   | K_DEFAULT (signed_number | literal_value | '(' expr ')')
+   | K_COLLATE collation_name
+   | foreign_key_clause
+   )
+ ;
+
+conflict_clause
+ : ( K_ON K_CONFLICT ( K_ROLLBACK
+                     | K_ABORT
+                     | K_FAIL
+                     | K_IGNORE
+                     | K_REPLACE
+                     )
+   )?
+ ;
+
+/*
+    SQLite understands the following binary operators, in order from highest to
+    lowest precedence:
+
+    ||
+    *    /    %
+    +    -
+    <<   >>   &    |
+    <    <=   >    >=
+    =    ==   !=   <>   IS   IS NOT   IN   LIKE   GLOB   MATCH   REGEXP
+    AND
+    OR
+*/
+expr
+ : literal_value
+ | BIND_PARAMETER
+ | ( ( database_name '.' )? table_name '.' )? column_name
+ | unary_operator expr
+ | expr '||' expr
+ | expr ( '*' | '/' | '%' ) expr
+ | expr ( '+' | '-' ) expr
+ | expr ( '<<' | '>>' | '&' | '|' ) expr
+ | expr ( '<' | '<=' | '>' | '>=' ) expr
+ | expr ( '=' | '==' | '!=' | '<>' ) expr
+ | expr K_AND expr
+ | expr K_OR expr
+ | function_name '(' ( K_DISTINCT? expr ( ',' expr )* | '*' )? ')'
+ | '(' expr ')'
+ | K_CAST '(' expr K_AS type_name ')'
+ | expr K_COLLATE collation_name
+ | expr K_NOT? ( K_LIKE | K_GLOB | K_REGEXP | K_MATCH ) expr ( K_ESCAPE expr )?
+ | expr ( K_ISNULL | K_NOTNULL | K_NOT K_NULL )
+ | expr K_IS K_NOT? expr
+ | expr K_NOT? K_BETWEEN expr K_AND expr
+ | expr K_NOT? K_IN ( '(' ( select_stmt
+                          | expr ( ',' expr )*
+                          )?
+                      ')'
+                    | ( database_name '.' )? table_name )
+ | ( ( K_NOT )? K_EXISTS )? '(' select_stmt ')'
+ | K_CASE expr? ( K_WHEN expr K_THEN expr )+ ( K_ELSE expr )? K_END
+ | raise_function
+ ;
+
+foreign_key_clause
+ : K_REFERENCES foreign_table ( '(' column_name ( ',' column_name )* ')' )?
+   ( ( K_ON ( K_DELETE | K_UPDATE ) ( K_SET K_NULL
+                                    | K_SET K_DEFAULT
+                                    | K_CASCADE
+                                    | K_RESTRICT
+                                    | K_NO K_ACTION )
+     | K_MATCH name
+     )
+   )*
+   ( K_NOT? K_DEFERRABLE ( K_INITIALLY K_DEFERRED | K_INITIALLY K_IMMEDIATE )? )?
+ ;
+
+raise_function
+ : K_RAISE '(' ( K_IGNORE
+               | ( K_ROLLBACK | K_ABORT | K_FAIL ) ',' error_message )
+           ')'
+ ;
+
+indexed_column
+ : column_name ( K_COLLATE collation_name )? ( K_ASC | K_DESC )?
+ ;
+
+table_constraint
+ : ( K_CONSTRAINT name )?
+   ( ( K_PRIMARY K_KEY | K_UNIQUE ) '(' indexed_column ( ',' indexed_column )* ')' conflict_clause
+   | K_CHECK '(' expr ')'
+   | K_FOREIGN K_KEY '(' column_name ( ',' column_name )* ')' foreign_key_clause
+   )
+ ;
+
+with_clause
+ : K_WITH K_RECURSIVE? common_table_expression ( ',' common_table_expression )*
+ ;
+
+qualified_table_name
+ : ( database_name '.' )? table_name ( K_INDEXED K_BY index_name
+                                     | K_NOT K_INDEXED )?
+ ;
+
+ordering_term
+ : expr ( K_COLLATE collation_name )? ( K_ASC | K_DESC )?
+ ;
+
+pragma_value
+ : signed_number
+ | name
+ | STRING_LITERAL
+ ;
+
+common_table_expression
+ : table_name ( '(' column_name ( ',' column_name )* ')' )? K_AS '(' select_stmt ')'
+ ;
+
+result_column
+ : '*'
+ | table_name '.' '*'
+ | expr ( K_AS? column_alias )?
+ ;
+
+table_or_subquery
+ : ( schema_name '.' )? table_name ( K_AS? table_alias )?
+   ( K_INDEXED K_BY index_name
+   | K_NOT K_INDEXED )?
+ | ( schema_name '.' )? table_function_name '(' ( expr ( ',' expr )* )? ')' ( K_AS? table_alias )?
+ | '(' ( table_or_subquery ( ',' table_or_subquery )*
+       | join_clause )
+   ')'
+ | '(' select_stmt ')' ( K_AS? table_alias )?
+ ;
+
+join_clause
+ : table_or_subquery ( join_operator table_or_subquery join_constraint )*
+ ;
+
+join_operator
+ : ','
+ | K_NATURAL? ( K_LEFT K_OUTER? | K_INNER | K_CROSS )? K_JOIN
+ ;
+
+join_constraint
+ : ( K_ON expr
+   | K_USING '(' column_name ( ',' column_name )* ')' )?
+ ;
+
+select_core
+ : K_SELECT ( K_DISTINCT | K_ALL )? result_column ( ',' result_column )*
+   ( K_FROM ( table_or_subquery ( ',' table_or_subquery )* | join_clause ) )?
+   ( K_WHERE expr )?
+   ( K_GROUP K_BY expr ( ',' expr )* ( K_HAVING expr )? )?
+ | K_VALUES '(' expr ( ',' expr )* ')' ( ',' '(' expr ( ',' expr )* ')' )*
+ ;
+
+compound_operator
+ : K_UNION
+ | K_UNION K_ALL
+ | K_INTERSECT
+ | K_EXCEPT
+ ;
+
+signed_number
+ : ( '+' | '-' )? NUMERIC_LITERAL
+ ;
+
+literal_value
+ : NUMERIC_LITERAL
+ | STRING_LITERAL
+ | BLOB_LITERAL
+ | K_NULL
+ | K_CURRENT_TIME
+ | K_CURRENT_DATE
+ | K_CURRENT_TIMESTAMP
+ ;
+
+unary_operator
+ : '-'
+ | '+'
+ | '~'
+ | K_NOT
+ ;
+
+error_message
+ : STRING_LITERAL
+ ;
+
+module_argument // TODO check what exactly is permitted here
+ : expr
+ | column_def
+ ;
+
+column_alias
+ : IDENTIFIER
+ | STRING_LITERAL
+ ;
+
+keyword
+ : K_ABORT
+ | K_ACTION
+ | K_ADD
+ | K_AFTER
+ | K_ALL
+ | K_ALTER
+ | K_ANALYZE
+ | K_AND
+ | K_AS
+ | K_ASC
+ | K_ATTACH
+ | K_AUTOINCREMENT
+ | K_BEFORE
+ | K_BEGIN
+ | K_BETWEEN
+ | K_BY
+ | K_CASCADE
+ | K_CASE
+ | K_CAST
+ | K_CHECK
+ | K_COLLATE
+ | K_COLUMN
+ | K_COMMIT
+ | K_CONFLICT
+ | K_CONSTRAINT
+ | K_CREATE
+ | K_CROSS
+ | K_CURRENT_DATE
+ | K_CURRENT_TIME
+ | K_CURRENT_TIMESTAMP
+ | K_DATABASE
+ | K_DEFAULT
+ | K_DEFERRABLE
+ | K_DEFERRED
+ | K_DELETE
+ | K_DESC
+ | K_DETACH
+ | K_DISTINCT
+ | K_DROP
+ | K_EACH
+ | K_ELSE
+ | K_END
+ | K_ESCAPE
+ | K_EXCEPT
+ | K_EXCLUSIVE
+ | K_EXISTS
+ | K_EXPLAIN
+ | K_FAIL
+ | K_FOR
+ | K_FOREIGN
+ | K_FROM
+ | K_FULL
+ | K_GLOB
+ | K_GROUP
+ | K_HAVING
+ | K_IF
+ | K_IGNORE
+ | K_IMMEDIATE
+ | K_IN
+ | K_INDEX
+ | K_INDEXED
+ | K_INITIALLY
+ | K_INNER
+ | K_INSERT
+ | K_INSTEAD
+ | K_INTERSECT
+ | K_INTO
+ | K_IS
+ | K_ISNULL
+ | K_JOIN
+ | K_KEY
+ | K_LEFT
+ | K_LIKE
+ | K_LIMIT
+ | K_MATCH
+ | K_NATURAL
+ | K_NO
+ | K_NOT
+ | K_NOTNULL
+ | K_NULL
+ | K_OF
+ | K_OFFSET
+ | K_ON
+ | K_OR
+ | K_ORDER
+ | K_OUTER
+ | K_PLAN
+ | K_PRAGMA
+ | K_PRIMARY
+ | K_QUERY
+ | K_RAISE
+ | K_RECURSIVE
+ | K_REFERENCES
+ | K_REGEXP
+ | K_REINDEX
+ | K_RELEASE
+ | K_RENAME
+ | K_REPLACE
+ | K_RESTRICT
+ | K_RIGHT
+ | K_ROLLBACK
+ | K_ROW
+ | K_SAVEPOINT
+ | K_SELECT
+ | K_SET
+ | K_TABLE
+ | K_TEMP
+ | K_TEMPORARY
+ | K_THEN
+ | K_TO
+ | K_TRANSACTION
+ | K_TRIGGER
+ | K_UNION
+ | K_UNIQUE
+ | K_UPDATE
+ | K_USING
+ | K_VACUUM
+ | K_VALUES
+ | K_VIEW
+ | K_VIRTUAL
+ | K_WHEN
+ | K_WHERE
+ | K_WITH
+ | K_WITHOUT
+ ;
+
+// TODO check all names below
+
+name
+ : any_name
+ ;
+
+function_name
+ : any_name
+ ;
+
+database_name
+ : any_name
+ ;
+
+schema_name
+ : any_name
+ ;
+
+table_function_name
+ : any_name
+ ;
+
+table_name
+ : any_name
+ ;
+
+table_or_index_name
+ : any_name
+ ;
+
+new_table_name
+ : any_name
+ ;
+
+column_name
+ : any_name
+ ;
+
+collation_name
+ : any_name
+ ;
+
+foreign_table
+ : any_name
+ ;
+
+index_name
+ : any_name
+ ;
+
+trigger_name
+ : any_name
+ ;
+
+view_name
+ : any_name
+ ;
+
+module_name
+ : any_name
+ ;
+
+pragma_name
+ : any_name
+ ;
+
+savepoint_name
+ : any_name
+ ;
+
+table_alias
+ : IDENTIFIER
+ | STRING_LITERAL
+ | '(' table_alias ')'
+ ;
+
+transaction_name
+ : any_name
+ ;
+
+any_name
+ : IDENTIFIER
+ | keyword
+ | STRING_LITERAL
+ | '(' any_name ')'
+ ;
+
+SCOL : ';';
+DOT : '.';
+OPEN_PAR : '(';
+CLOSE_PAR : ')';
+COMMA : ',';
+ASSIGN : '=';
+STAR : '*';
+PLUS : '+';
+MINUS : '-';
+TILDE : '~';
+PIPE2 : '||';
+DIV : '/';
+MOD : '%';
+LT2 : '<<';
+GT2 : '>>';
+AMP : '&';
+PIPE : '|';
+LT : '<';
+LT_EQ : '<=';
+GT : '>';
+GT_EQ : '>=';
+EQ : '==';
+NOT_EQ1 : '!=';
+NOT_EQ2 : '<>';
+
+// http://www.sqlite.org/lang_keywords.html
+K_ABORT : A B O R T;
+K_ACTION : A C T I O N;
+K_ADD : A D D;
+K_AFTER : A F T E R;
+K_ALL : A L L;
+K_ALTER : A L T E R;
+K_ANALYZE : A N A L Y Z E;
+K_AND : A N D;
+K_AS : A S;
+K_ASC : A S C;
+K_ATTACH : A T T A C H;
+K_AUTOINCREMENT : A U T O I N C R E M E N T;
+K_BEFORE : B E F O R E;
+K_BEGIN : B E G I N;
+K_BETWEEN : B E T W E E N;
+K_BY : B Y;
+K_CASCADE : C A S C A D E;
+K_CASE : C A S E;
+K_CAST : C A S T;
+K_CHECK : C H E C K;
+K_COLLATE : C O L L A T E;
+K_COLUMN : C O L U M N;
+K_COMMIT : C O M M I T;
+K_CONFLICT : C O N F L I C T;
+K_CONSTRAINT : C O N S T R A I N T;
+K_CREATE : C R E A T E;
+K_CROSS : C R O S S;
+K_CURRENT_DATE : C U R R E N T '_' D A T E;
+K_CURRENT_TIME : C U R R E N T '_' T I M E;
+K_CURRENT_TIMESTAMP : C U R R E N T '_' T I M E S T A M P;
+K_DATABASE : D A T A B A S E;
+K_DEFAULT : D E F A U L T;
+K_DEFERRABLE : D E F E R R A B L E;
+K_DEFERRED : D E F E R R E D;
+K_DELETE : D E L E T E;
+K_DESC : D E S C;
+K_DETACH : D E T A C H;
+K_DISTINCT : D I S T I N C T;
+K_DROP : D R O P;
+K_EACH : E A C H;
+K_ELSE : E L S E;
+K_END : E N D;
+K_ESCAPE : E S C A P E;
+K_EXCEPT : E X C E P T;
+K_EXCLUSIVE : E X C L U S I V E;
+K_EXISTS : E X I S T S;
+K_EXPLAIN : E X P L A I N;
+K_FAIL : F A I L;
+K_FOR : F O R;
+K_FOREIGN : F O R E I G N;
+K_FROM : F R O M;
+K_FULL : F U L L;
+K_GLOB : G L O B;
+K_GROUP : G R O U P;
+K_HAVING : H A V I N G;
+K_IF : I F;
+K_IGNORE : I G N O R E;
+K_IMMEDIATE : I M M E D I A T E;
+K_IN : I N;
+K_INDEX : I N D E X;
+K_INDEXED : I N D E X E D;
+K_INITIALLY : I N I T I A L L Y;
+K_INNER : I N N E R;
+K_INSERT : I N S E R T;
+K_INSTEAD : I N S T E A D;
+K_INTERSECT : I N T E R S E C T;
+K_INTO : I N T O;
+K_IS : I S;
+K_ISNULL : I S N U L L;
+K_JOIN : J O I N;
+K_KEY : K E Y;
+K_LEFT : L E F T;
+K_LIKE : L I K E;
+K_LIMIT : L I M I T;
+K_MATCH : M A T C H;
+K_NATURAL : N A T U R A L;
+K_NO : N O;
+K_NOT : N O T;
+K_NOTNULL : N O T N U L L;
+K_NULL : N U L L;
+K_OF : O F;
+K_OFFSET : O F F S E T;
+K_ON : O N;
+K_OR : O R;
+K_ORDER : O R D E R;
+K_OUTER : O U T E R;
+K_PLAN : P L A N;
+K_PRAGMA : P R A G M A;
+K_PRIMARY : P R I M A R Y;
+K_QUERY : Q U E R Y;
+K_RAISE : R A I S E;
+K_RECURSIVE : R E C U R S I V E;
+K_REFERENCES : R E F E R E N C E S;
+K_REGEXP : R E G E X P;
+K_REINDEX : R E I N D E X;
+K_RELEASE : R E L E A S E;
+K_RENAME : R E N A M E;
+K_REPLACE : R E P L A C E;
+K_RESTRICT : R E S T R I C T;
+K_RIGHT : R I G H T;
+K_ROLLBACK : R O L L B A C K;
+K_ROW : R O W;
+K_SAVEPOINT : S A V E P O I N T;
+K_SELECT : S E L E C T;
+K_SET : S E T;
+K_TABLE : T A B L E;
+K_TEMP : T E M P;
+K_TEMPORARY : T E M P O R A R Y;
+K_THEN : T H E N;
+K_TO : T O;
+K_TRANSACTION : T R A N S A C T I O N;
+K_TRIGGER : T R I G G E R;
+K_UNION : U N I O N;
+K_UNIQUE : U N I Q U E;
+K_UPDATE : U P D A T E;
+K_USING : U S I N G;
+K_VACUUM : V A C U U M;
+K_VALUES : V A L U E S;
+K_VIEW : V I E W;
+K_VIRTUAL : V I R T U A L;
+K_WHEN : W H E N;
+K_WHERE : W H E R E;
+K_WITH : W I T H;
+K_WITHOUT : W I T H O U T;
+
+IDENTIFIER
+ : '"' (~'"' | '""')* '"'
+ | '`' (~'`' | '``')* '`'
+ | '[' ~']'* ']'
+ | [a-zA-Z_] [a-zA-Z_0-9]* // TODO check: needs more chars in set
+ ;
+
+NUMERIC_LITERAL
+ : DIGIT+ ( '.' DIGIT* )? ( E [-+]? DIGIT+ )?
+ | '.' DIGIT+ ( E [-+]? DIGIT+ )?
+ ;
+
+BIND_PARAMETER
+ : '?' DIGIT*
+ | [:@$] IDENTIFIER
+ ;
+
+STRING_LITERAL
+ : '\'' ( ~'\'' | '\'\'' )* '\''
+ ;
+
+BLOB_LITERAL
+ : X STRING_LITERAL
+ ;
+
+SINGLE_LINE_COMMENT
+ : '--' ~[\r\n]* -> channel(HIDDEN)
+ ;
+
+MULTILINE_COMMENT
+ : '/*' .*? ( '*/' | EOF ) -> channel(HIDDEN)
+ ;
+
+SPACES
+ : [ \u000B\t\r\n] -> channel(HIDDEN)
+ ;
+
+UNEXPECTED_CHAR
+ : .
+ ;
+
+fragment DIGIT : [0-9];
+
+fragment A : [aA];
+fragment B : [bB];
+fragment C : [cC];
+fragment D : [dD];
+fragment E : [eE];
+fragment F : [fF];
+fragment G : [gG];
+fragment H : [hH];
+fragment I : [iI];
+fragment J : [jJ];
+fragment K : [kK];
+fragment L : [lL];
+fragment M : [mM];
+fragment N : [nN];
+fragment O : [oO];
+fragment P : [pP];
+fragment Q : [qQ];
+fragment R : [rR];
+fragment S : [sS];
+fragment T : [tT];
+fragment U : [uU];
+fragment V : [vV];
+fragment W : [wW];
+fragment X : [xX];
+fragment Y : [yY];
+fragment Z : [zZ];
\ No newline at end of file
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
new file mode 100644
index 0000000..0802a06
--- /dev/null
+++ b/room/compiler/build.gradle
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'kotlin'
+apply plugin: 'maven'
+
+def antlrOut = "$buildDir/generated/antlr/grammar-gen/"
+sourceSets {
+    main.java.srcDirs += 'src/main/grammar-gen'
+    test.java.srcDirs += 'src/tests/kotlin'
+    main.java.srcDirs += antlrOut
+}
+project.ext.noDocs = true
+
+// Temporary hack to stop AS to adding two guavas into test's classpath
+configurations.all {
+    resolutionStrategy {
+        force libs.guava
+    }
+}
+
+dependencies {
+    // taken from ButterKnife
+    def logger = new com.android.build.gradle.internal.LoggerWrapper(project.logger)
+    def sdkHandler = new com.android.build.gradle.internal.SdkHandler(project, logger)
+    compile project(":room:common")
+    compile project(":room:migration")
+    compile libs.kotlin.stdlib
+    compile libs.auto_common
+    compile libs.javapoet
+    compile libs.antlr
+    compile libs.xerial
+    compile libs.apache.commons.codec
+    testCompile libs.google_compile_testing
+    testCompile project(":paging:common")
+    testCompile libs.junit
+    testCompile libs.ij_annotations
+    testCompile libs.mockito_core
+    testCompile fileTree(dir: "${sdkHandler.sdkFolder}/platforms/android-$tools.current_sdk/",
+            include : "android.jar")
+    testCompile fileTree(dir: "${new File(project(":room:runtime").buildDir, "libJar")}",
+            include : "*.jar")
+    testCompile fileTree(dir: "${new File(project(":room:db").buildDir, "libJar")}",
+            include : "*.jar")
+    testCompile files(org.gradle.internal.jvm.Jvm.current().getToolsJar())
+}
+
+archivesBaseName = "compiler"
+
+def generateAntlrTask = task('generateAntlrGrammar', type: JavaExec) {
+    def outFolder = file(antlrOut)
+    outputs.dir(outFolder)
+    inputs.dir("$projectDir/SQLite.g4")
+    classpath configurations.runtime
+    main "org.antlr.v4.Tool"
+    args "SQLite.g4", "-visitor", "-o", new File(outFolder, "android/arch/persistence/room/parser").path,
+            "-package", "android.arch.persistence.room.parser"
+}
+
+tasks.findByName("compileKotlin").dependsOn(generateAntlrTask)
+tasks.findByName("compileKotlin").dependsOn(":room:runtime:jarDebug")
+tasks.findByName("compileKotlin").dependsOn(":room:db:jarDebug")
+
+createKotlinCheckstyle(project)
+
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/RoomProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/RoomProcessor.kt
new file mode 100644
index 0000000..78849eb
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/RoomProcessor.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room
+
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.DatabaseProcessor
+import android.arch.persistence.room.processor.ProcessorErrors
+import android.arch.persistence.room.vo.DaoMethod
+import android.arch.persistence.room.vo.Warning
+import android.arch.persistence.room.writer.DaoWriter
+import android.arch.persistence.room.writer.DatabaseWriter
+import com.google.auto.common.BasicAnnotationProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.collect.SetMultimap
+import java.io.File
+import javax.annotation.processing.SupportedSourceVersion
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Element
+
+/**
+ * The annotation processor for Room.
+ */
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+class RoomProcessor : BasicAnnotationProcessor() {
+    override fun initSteps(): MutableIterable<ProcessingStep>? {
+        val context = Context(processingEnv)
+        return arrayListOf(DatabaseProcessingStep(context))
+    }
+
+    override fun getSupportedOptions(): MutableSet<String> {
+        return Context.ARG_OPTIONS.toMutableSet()
+    }
+
+    class DatabaseProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+        override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+                : MutableSet<Element> {
+            // TODO multi step support
+            val databases = elementsByAnnotation[Database::class.java]
+                    ?.map {
+                        DatabaseProcessor(context, MoreElements.asType(it)).process()
+                    }
+            val allDaoMethods = databases?.flatMap { it.daoMethods }
+            allDaoMethods?.let {
+                prepareDaosForWriting(databases!!, it)
+                it.forEach {
+                    DaoWriter(it.dao, context.processingEnv).write(context.processingEnv)
+                }
+            }
+
+            databases?.forEach { db ->
+                DatabaseWriter(db).write(context.processingEnv)
+                if (db.exportSchema) {
+                    val schemaOutFolder = context.schemaOutFolder
+                    if (schemaOutFolder == null) {
+                        context.logger.w(Warning.MISSING_SCHEMA_LOCATION, db.element,
+                                ProcessorErrors.MISSING_SCHEMA_EXPORT_DIRECTORY)
+                    } else {
+                        if (!schemaOutFolder.exists()) {
+                            schemaOutFolder.mkdirs()
+                        }
+                        val qName = db.element.qualifiedName.toString()
+                        val dbSchemaFolder = File(schemaOutFolder, qName)
+                        if (!dbSchemaFolder.exists()) {
+                            dbSchemaFolder.mkdirs()
+                        }
+                        db.exportSchema(File(dbSchemaFolder, "${db.version}.json"))
+                    }
+                }
+            }
+            context.databaseVerifier?.let {
+                it.closeConnection()
+            }
+            return mutableSetOf()
+        }
+        override fun annotations(): MutableSet<out Class<out Annotation>> {
+            return mutableSetOf(Database::class.java, Dao::class.java, Entity::class.java)
+        }
+
+        /**
+         * Traverses all dao methods and assigns them suffix if they are used in multiple databases.
+         */
+        private fun prepareDaosForWriting(
+                databases: List<android.arch.persistence.room.vo.Database>,
+                daoMethods: List<DaoMethod>) {
+            daoMethods.groupBy { it.dao.typeName }
+                    // if used only in 1 database, nothing to do.
+                    .filter { entry -> entry.value.size > 1 }
+                    .forEach { entry ->
+                        entry.value.groupBy { daoMethod ->
+                            // first suffix guess: Database's simple name
+                            val db = databases.first { db -> db.daoMethods.contains(daoMethod) }
+                            db.typeName.simpleName()
+                        }.forEach { entry ->
+                            val dbName = entry.key
+                            val methods = entry.value
+                            if (methods.size == 1) {
+                                //good, db names do not clash, use db name as suffix
+                                methods.first().dao.setSuffix(dbName)
+                            } else {
+                                // ok looks like a dao is used in 2 different databases both of
+                                // which have the same name. enumerate.
+                                methods.forEachIndexed { index, method ->
+                                    method.dao.setSuffix("${dbName}_$index")
+                                }
+                            }
+                        }
+                    }
+        }
+    }
+
+    abstract class ContextBoundProcessingStep(val context: Context) : ProcessingStep
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/element_ext.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/element_ext.kt
new file mode 100644
index 0000000..09b6221
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/element_ext.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+
+package android.arch.persistence.room.ext
+
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.SimpleAnnotationValueVisitor6
+import kotlin.reflect.KClass
+
+fun Element.hasAnyOf(vararg modifiers: Modifier): Boolean {
+    return this.modifiers.any { modifiers.contains(it) }
+}
+
+fun Element.hasAnnotation(klass: KClass<out Annotation>): Boolean {
+    return MoreElements.isAnnotationPresent(this, klass.java)
+}
+
+fun Element.isNonNull() =
+        asType().kind.isPrimitive
+                || hasAnnotation(android.support.annotation.NonNull::class)
+                || hasAnnotation(org.jetbrains.annotations.NotNull::class)
+
+/**
+ * Checks if it has all of the annotations
+ */
+fun Element.hasAllOf(vararg klasses: KClass<out Annotation>): Boolean {
+    return !klasses.any { !hasAnnotation(it) }
+}
+
+/**
+ * gets all members including super privates. does not handle duplicate field names!!!
+ */
+// TODO handle conflicts with super: b/35568142
+fun TypeElement.getAllFieldsIncludingPrivateSupers(processingEnvironment: ProcessingEnvironment):
+        Set<VariableElement> {
+    val myMembers = processingEnvironment.elementUtils.getAllMembers(this)
+            .filter { it.kind == ElementKind.FIELD }
+            .filter { it is VariableElement }
+            .map { it as VariableElement }
+            .toSet()
+    if (superclass.kind != TypeKind.NONE) {
+        return myMembers + MoreTypes.asTypeElement(superclass)
+                .getAllFieldsIncludingPrivateSupers(processingEnvironment)
+    } else {
+        return myMembers
+    }
+}
+
+// code below taken from dagger2
+// compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java
+private val TO_LIST_OF_TYPES = object
+    : SimpleAnnotationValueVisitor6<List<TypeMirror>, Void?>() {
+    override fun visitArray(values: MutableList<out AnnotationValue>?, p: Void?)
+            : List<TypeMirror> {
+        return values?.map {
+            val tmp = TO_TYPE.visit(it)
+            tmp
+        }?.filterNotNull() ?: emptyList()
+    }
+
+
+    override fun defaultAction(o: Any?, p: Void?): List<TypeMirror>? {
+        return emptyList()
+    }
+}
+
+private val TO_TYPE = object : SimpleAnnotationValueVisitor6<TypeMirror, Void>() {
+
+    override fun visitType(t: TypeMirror, p: Void?): TypeMirror {
+        return t
+    }
+
+    override fun defaultAction(o: Any?, p: Void?): TypeMirror {
+        throw TypeNotPresentException(o!!.toString(), null)
+    }
+}
+
+fun AnnotationValue.toListOfClassTypes(): List<TypeMirror> {
+    return TO_LIST_OF_TYPES.visit(this)
+}
+
+fun AnnotationValue.toType(): TypeMirror {
+    return TO_TYPE.visit(this)
+}
+
+fun AnnotationValue.toClassType(): TypeMirror? {
+    return TO_TYPE.visit(this)
+}
+
+fun TypeMirror.isCollection(): Boolean {
+    return MoreTypes.isType(this)
+            && (MoreTypes.isTypeOf(java.util.List::class.java, this)
+            || MoreTypes.isTypeOf(java.util.Set::class.java, this))
+}
+
+fun Element.getAnnotationValue(annotation: Class<out Annotation>, fieldName: String): Any? {
+    return MoreElements.getAnnotationMirror(this, annotation)
+            .orNull()?.let {
+        AnnotationMirrors.getAnnotationValue(it, fieldName)?.value
+    }
+}
+
+private val ANNOTATION_VALUE_TO_INT_VISITOR = object : SimpleAnnotationValueVisitor6<Int?, Void>() {
+    override fun visitInt(i: Int, p: Void?): Int? {
+        return i
+    }
+}
+
+private val ANNOTATION_VALUE_TO_BOOLEAN_VISITOR = object
+    : SimpleAnnotationValueVisitor6<Boolean?, Void>() {
+    override fun visitBoolean(b: Boolean, p: Void?): Boolean? {
+        return b
+    }
+}
+
+private val ANNOTATION_VALUE_TO_STRING_VISITOR = object
+    : SimpleAnnotationValueVisitor6<String?, Void>() {
+    override fun visitString(s: String?, p: Void?): String? {
+        return s
+    }
+}
+
+private val ANNOTATION_VALUE_STRING_ARR_VISITOR = object
+    : SimpleAnnotationValueVisitor6<List<String>, Void>() {
+    override fun visitArray(vals: MutableList<out AnnotationValue>?, p: Void?): List<String> {
+        return vals?.map {
+            ANNOTATION_VALUE_TO_STRING_VISITOR.visit(it)
+        }?.filterNotNull() ?: emptyList()
+    }
+}
+
+fun AnnotationValue.getAsInt(def: Int? = null): Int? {
+    return ANNOTATION_VALUE_TO_INT_VISITOR.visit(this) ?: def
+}
+
+fun AnnotationValue.getAsString(def: String? = null): String? {
+    return ANNOTATION_VALUE_TO_STRING_VISITOR.visit(this) ?: def
+}
+
+fun AnnotationValue.getAsBoolean(def: Boolean): Boolean {
+    return ANNOTATION_VALUE_TO_BOOLEAN_VISITOR.visit(this) ?: def
+}
+
+fun AnnotationValue.getAsStringList(): List<String> {
+    return ANNOTATION_VALUE_STRING_ARR_VISITOR.visit(this)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/javapoet_ext.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/javapoet_ext.kt
new file mode 100644
index 0000000..fa2f342
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/javapoet_ext.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.ext
+
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+import kotlin.reflect.KClass
+
+val L = "\$L"
+val T = "\$T"
+val N = "\$N"
+val S = "\$S"
+
+fun KClass<*>.typeName() = ClassName.get(this.java)
+fun KClass<*>.arrayTypeName() = ArrayTypeName.of(typeName())
+fun TypeMirror.typeName() = TypeName.get(this)
+
+object SupportDbTypeNames {
+    val DB: ClassName = ClassName.get("android.arch.persistence.db", "SupportSQLiteDatabase")
+    val SQLITE_STMT : ClassName =
+            ClassName.get("android.arch.persistence.db", "SupportSQLiteStatement")
+    val SQLITE_OPEN_HELPER : ClassName =
+            ClassName.get("android.arch.persistence.db", "SupportSQLiteOpenHelper")
+    val SQLITE_OPEN_HELPER_CALLBACK : ClassName =
+            ClassName.get("android.arch.persistence.db", "SupportSQLiteOpenHelper.Callback")
+    val SQLITE_OPEN_HELPER_FACTORY : ClassName =
+            ClassName.get("android.arch.persistence.db", "SupportSQLiteOpenHelper.Factory")
+    val SQLITE_OPEN_HELPER_CONFIG : ClassName =
+            ClassName.get("android.arch.persistence.db", "SupportSQLiteOpenHelper.Configuration")
+    val SQLITE_OPEN_HELPER_CONFIG_BUILDER : ClassName =
+            ClassName.get("android.arch.persistence.db",
+                    "SupportSQLiteOpenHelper.Configuration.Builder")
+}
+
+object RoomTypeNames {
+    val STRING_UTIL: ClassName = ClassName.get("android.arch.persistence.room.util", "StringUtil")
+    val CURSOR_CONVERTER : ClassName =
+            ClassName.get("android.arch.persistence.room", "CursorConverter")
+    val ROOM : ClassName = ClassName.get("android.arch.persistence.room", "Room")
+    val ROOM_DB : ClassName = ClassName.get("android.arch.persistence.room", "RoomDatabase")
+    val ROOM_DB_CONFIG : ClassName = ClassName.get("android.arch.persistence.room",
+            "DatabaseConfiguration")
+    val INSERTION_ADAPTER : ClassName =
+            ClassName.get("android.arch.persistence.room", "EntityInsertionAdapter")
+    val DELETE_OR_UPDATE_ADAPTER : ClassName =
+            ClassName.get("android.arch.persistence.room", "EntityDeletionOrUpdateAdapter")
+    val SHARED_SQLITE_STMT : ClassName =
+            ClassName.get("android.arch.persistence.room", "SharedSQLiteStatement")
+    val INVALIDATION_TRACKER : ClassName =
+            ClassName.get("android.arch.persistence.room", "InvalidationTracker")
+    val INVALIDATION_OBSERVER : ClassName =
+            ClassName.get("android.arch.persistence.room.InvalidationTracker", "Observer")
+    val ROOM_SQL_QUERY : ClassName =
+            ClassName.get("android.arch.persistence.room", "RoomSQLiteQuery")
+    val OPEN_HELPER : ClassName =
+            ClassName.get("android.arch.persistence.room", "RoomOpenHelper")
+    val OPEN_HELPER_DELEGATE: ClassName =
+            ClassName.get("android.arch.persistence.room", "RoomOpenHelper.Delegate")
+    val TABLE_INFO : ClassName =
+            ClassName.get("android.arch.persistence.room.util", "TableInfo")
+    val TABLE_INFO_COLUMN : ClassName =
+            ClassName.get("android.arch.persistence.room.util", "TableInfo.Column")
+    val TABLE_INFO_FOREIGN_KEY : ClassName =
+            ClassName.get("android.arch.persistence.room.util", "TableInfo.ForeignKey")
+    val LIMIT_OFFSET_DATA_SOURCE : ClassName =
+            ClassName.get("android.arch.persistence.room.paging", "LimitOffsetDataSource")
+}
+
+object ArchTypeNames {
+    val APP_EXECUTOR : ClassName =
+            ClassName.get("android.arch.core.executor", "AppToolkitTaskExecutor")
+}
+
+object PagingTypeNames {
+    val COUNTED_DATA_SOURCE : ClassName =
+            ClassName.get("android.arch.util.paging", "CountedDataSource")
+    val LIVE_LAZY_LIST_PROVIDER: ClassName =
+            ClassName.get("android.arch.util.paging", "LiveLazyListProvider")
+
+}
+
+object LifecyclesTypeNames {
+    val LIVE_DATA: ClassName = ClassName.get("android.arch.lifecycle", "LiveData")
+    val COMPUTABLE_LIVE_DATA : ClassName = ClassName.get("android.arch.lifecycle",
+            "ComputableLiveData")
+}
+
+object AndroidTypeNames {
+    val CURSOR : ClassName = ClassName.get("android.database", "Cursor")
+    val ARRAY_MAP : ClassName = ClassName.get("android.support.v4.util", "ArrayMap")
+}
+
+object CommonTypeNames {
+    val LIST = ClassName.get("java.util", "List")
+    val SET = ClassName.get("java.util", "Set")
+    val STRING = ClassName.get("java.lang", "String")
+    val INTEGER = ClassName.get("java.lang", "Integer")
+}
+
+object RxJava2TypeNames {
+    val FLOWABLE = ClassName.get("io.reactivex", "Flowable")
+    val MAYBE = ClassName.get("io.reactivex", "Maybe")
+    val SINGLE = ClassName.get("io.reactivex", "Single")
+}
+
+object ReactiveStreamsTypeNames {
+    val PUBLISHER = ClassName.get("org.reactivestreams", "Publisher")
+}
+
+object RoomRxJava2TypeNames {
+    val RX_ROOM = ClassName.get("android.arch.persistence.room", "RxRoom")
+    val RX_EMPTY_RESULT_SET_EXCEPTION = ClassName.get("android.arch.persistence.room",
+            "EmptyResultSetException")
+}
+
+fun TypeName.defaultValue() : String {
+    return if (!isPrimitive) {
+        "null"
+    } else if (this == TypeName.BOOLEAN) {
+        "false"
+    } else {
+        "0"
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/string_ext.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/string_ext.kt
new file mode 100644
index 0000000..9b07e27
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/string_ext.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+private fun String.toCamelCase() : String {
+    val split = this.split("_")
+    if (split.isEmpty()) return ""
+    if (split.size == 1) return split[0].capitalize()
+    return split.joinToCamelCase()
+}
+
+private fun String.toCamelCaseAsVar() : String {
+    val split = this.split("_")
+    if (split.isEmpty()) return ""
+    if (split.size == 1) return split[0]
+    return split.joinToCamelCaseAsVar()
+}
+
+private fun List<String>.joinToCamelCase(): String = when(size) {
+    0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
+    1 -> this[0].toCamelCase()
+    else -> this.map {it.toCamelCase()}.joinToString("")
+}
+
+private fun List<String>.joinToCamelCaseAsVar(): String = when(size) {
+    0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
+    1 -> this[0].toCamelCaseAsVar()
+    else -> get(0).toCamelCaseAsVar() + drop(1).joinToCamelCase()
+}
+
+private val javaCharRegex = "[^a-zA-Z0-9]".toRegex()
+fun String.stripNonJava(): String {
+    return this.split(javaCharRegex)
+            .map(String::trim)
+            .joinToCamelCaseAsVar()
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/type_mirror_ext.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/type_mirror_ext.kt
new file mode 100644
index 0000000..97cd051
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/ext/type_mirror_ext.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+import javax.lang.model.type.TypeKind.BOOLEAN
+import javax.lang.model.type.TypeKind.BYTE
+import javax.lang.model.type.TypeKind.CHAR
+import javax.lang.model.type.TypeKind.DOUBLE
+import javax.lang.model.type.TypeKind.FLOAT
+import javax.lang.model.type.TypeKind.INT
+import javax.lang.model.type.TypeKind.LONG
+import javax.lang.model.type.TypeKind.SHORT
+import javax.lang.model.type.TypeMirror
+
+fun TypeMirror.defaultValue() : String {
+    return when(this.kind) {
+        BOOLEAN -> "false"
+        BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE -> "0"
+        else -> "null"
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/log/RLog.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/log/RLog.kt
new file mode 100644
index 0000000..10614ce
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/log/RLog.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("unused")
+
+package android.arch.persistence.room.log
+
+import android.arch.persistence.room.vo.Warning
+import java.util.UnknownFormatConversionException
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Element
+import javax.tools.Diagnostic
+import javax.tools.Diagnostic.Kind.ERROR
+import javax.tools.Diagnostic.Kind.NOTE
+import javax.tools.Diagnostic.Kind.WARNING
+
+class RLog(val messager : Messager, val suppressedWarnings : Set<Warning>,
+           val defaultElement : Element?) {
+    private fun String.safeFormat(vararg args: Any): String {
+        try {
+            return format(args)
+        } catch (ex: UnknownFormatConversionException) {
+            // the input string might be from random source in which case we rather print the
+            // msg as is instead of crashing while reporting an error.
+            return this
+        }
+    }
+
+    fun d(element: Element, msg: String, vararg args: Any) {
+        messager.printMessage(NOTE, msg.safeFormat(args), element)
+    }
+
+    fun d(msg: String, vararg args: Any) {
+        messager.printMessage(NOTE, msg.safeFormat(args))
+    }
+
+    fun e(element: Element, msg: String, vararg args: Any) {
+        messager.printMessage(ERROR, msg.safeFormat(args), element)
+    }
+
+    fun e(msg: String, vararg args: Any) {
+        messager.printMessage(ERROR, msg.safeFormat(args), defaultElement)
+    }
+
+    fun w(warning: Warning, element: Element? = null, msg: String, vararg args: Any) {
+        if (suppressedWarnings.contains(warning)) {
+            return
+        }
+        messager.printMessage(WARNING, msg.safeFormat(args),
+                element ?: defaultElement)
+    }
+
+    fun w(warning: Warning, msg: String, vararg args: Any) {
+        if (suppressedWarnings.contains(warning)) {
+            return
+        }
+        messager.printMessage(WARNING, msg.safeFormat(args), defaultElement)
+    }
+
+    interface Messager {
+        fun printMessage(kind: Diagnostic.Kind, msg: String, element: Element? = null)
+    }
+
+    class ProcessingEnvMessager(val processingEnv: ProcessingEnvironment) : Messager {
+        override fun printMessage(kind: Diagnostic.Kind, msg: String, element: Element?) {
+            processingEnv.messager.printMessage(kind, msg, element)
+        }
+    }
+
+    class CollectingMessager : Messager {
+        private val messages = mutableMapOf<Diagnostic.Kind, MutableList<Pair<String, Element?>>> ()
+        override fun printMessage(kind: Diagnostic.Kind, msg: String, element: Element?) {
+            messages.getOrPut(kind, {
+                arrayListOf<Pair<String, Element?>>()
+            }).add(Pair(msg, element))
+        }
+
+        fun hasErrors() = messages.containsKey(Diagnostic.Kind.ERROR)
+
+        fun writeTo(env : ProcessingEnvironment) {
+            messages.forEach { pair ->
+                val kind = pair.key
+                pair.value.forEach {
+                    env.messager.printMessage(kind, it.first, it.second)
+                }
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/ParsedQuery.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/ParsedQuery.kt
new file mode 100644
index 0000000..85d578d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/ParsedQuery.kt
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.parser
+
+import android.arch.persistence.room.parser.SectionType.BIND_VAR
+import android.arch.persistence.room.parser.SectionType.NEWLINE
+import android.arch.persistence.room.parser.SectionType.TEXT
+import android.arch.persistence.room.verifier.QueryResultInfo
+import org.antlr.v4.runtime.tree.TerminalNode
+
+enum class SectionType {
+    BIND_VAR,
+    TEXT,
+    NEWLINE
+}
+
+data class Section(val text: String, val type: SectionType) {
+    companion object {
+        fun text(text: String) = Section(text, SectionType.TEXT)
+        fun newline() = Section("", SectionType.NEWLINE)
+        fun bindVar(text: String) = Section(text, SectionType.BIND_VAR)
+    }
+}
+
+data class Table(val name: String, val alias: String)
+
+data class ParsedQuery(val original: String, val type: QueryType,
+                       val inputs: List<TerminalNode>,
+                       // pairs of table name and alias,
+                       val tables: Set<Table>,
+                       val syntaxErrors: List<String>) {
+    companion object {
+        val STARTS_WITH_NUMBER = "^\\?[0-9]".toRegex()
+        val MISSING = ParsedQuery("missing query", QueryType.UNKNOWN, emptyList(), emptySet(),
+                emptyList())
+    }
+
+    /**
+     * Optional data that might be assigned when the query is parsed inside an annotation processor.
+     * User may turn this off or it might be disabled for any reason so generated code should
+     * always handle not having it.
+     */
+    var resultInfo: QueryResultInfo? = null
+
+    val sections by lazy {
+        val lines = original.lines()
+        val inputsByLine = inputs.groupBy { it.symbol.line }
+        val sections = arrayListOf<Section>()
+        lines.forEachIndexed { index, line ->
+            var charInLine = 0
+            inputsByLine[index + 1]?.forEach { bindVar ->
+                if (charInLine < bindVar.symbol.charPositionInLine) {
+                    sections.add(Section.text(line.substring(charInLine,
+                            bindVar.symbol.charPositionInLine)))
+                }
+                sections.add(Section.bindVar(bindVar.text))
+                charInLine = bindVar.symbol.charPositionInLine + bindVar.symbol.text.length
+            }
+            if (charInLine < line.length) {
+                sections.add(Section.text(line.substring(charInLine)))
+            }
+            if (index + 1 < lines.size) {
+                sections.add(Section.newline())
+            }
+        }
+        sections
+    }
+
+    val bindSections by lazy { sections.filter { it.type == BIND_VAR } }
+
+    private fun unnamedVariableErrors(): List<String> {
+        val anonymousBindError = if (inputs.any { it.text == "?" }) {
+            arrayListOf(ParserErrors.ANONYMOUS_BIND_ARGUMENT)
+        } else {
+            emptyList<String>()
+        }
+        return anonymousBindError + inputs.filter {
+            it.text.matches(STARTS_WITH_NUMBER)
+        }.map {
+            ParserErrors.cannotUseVariableIndices(it.text, it.symbol.charPositionInLine)
+        }
+    }
+
+    private fun unknownQueryTypeErrors(): List<String> {
+        return if (QueryType.SUPPORTED.contains(type)) {
+            emptyList()
+        } else {
+            listOf(ParserErrors.invalidQueryType(type))
+        }
+    }
+
+    val errors by lazy {
+        if (syntaxErrors.isNotEmpty()) {
+            // if there is a syntax error, don't report others since they might be misleading.
+            syntaxErrors
+        } else {
+            unnamedVariableErrors() + unknownQueryTypeErrors()
+        }
+    }
+
+    val queryWithReplacedBindParams by lazy {
+        sections.joinToString("") {
+            when (it.type) {
+                TEXT -> it.text
+                BIND_VAR -> "?"
+                NEWLINE -> "\n"
+                else -> throw IllegalArgumentException("??")
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/ParserErrors.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/ParserErrors.kt
new file mode 100644
index 0000000..e3b4f40
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/ParserErrors.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.parser
+
+object ParserErrors {
+    val ANONYMOUS_BIND_ARGUMENT = "Room does not support ? as bind parameters. You must use" +
+            " named bind arguments (e..g :argName)"
+
+    val NOT_ONE_QUERY = "Must have exactly 1 query in @Query value"
+
+    fun invalidQueryType(type: QueryType): String {
+        return "$type query type is not supported yet. You can use:" +
+                QueryType.SUPPORTED.joinToString(", ") { it.name }
+    }
+
+    fun cannotUseVariableIndices(name: String, position: Int) = "Cannot use variable indices." +
+            " Use named parameters instead (e.g. WHERE name LIKE :nameArg and lastName LIKE " +
+            ":lastName). Problem: $name at $position"
+}
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
new file mode 100644
index 0000000..7bdd397
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/SqlParser.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.parser
+
+import android.arch.persistence.room.ColumnInfo
+import org.antlr.v4.runtime.ANTLRInputStream
+import org.antlr.v4.runtime.BaseErrorListener
+import org.antlr.v4.runtime.CommonTokenStream
+import org.antlr.v4.runtime.RecognitionException
+import org.antlr.v4.runtime.Recognizer
+import org.antlr.v4.runtime.tree.ParseTree
+import org.antlr.v4.runtime.tree.TerminalNode
+import java.util.ArrayList
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+class QueryVisitor(val original: String, val syntaxErrors: ArrayList<String>,
+                   statement: ParseTree) : SQLiteBaseVisitor<Void?>() {
+    val bindingExpressions = arrayListOf<TerminalNode>()
+    // table name alias mappings
+    val tableNames = mutableSetOf<Table>()
+    val queryType: QueryType
+
+    init {
+        queryType = (0..statement.childCount - 1).map {
+            findQueryType(statement.getChild(it))
+        }.filterNot { it == QueryType.UNKNOWN }.firstOrNull() ?: QueryType.UNKNOWN
+
+        statement.accept(this)
+    }
+
+    private fun findQueryType(statement: ParseTree): QueryType {
+        return when (statement) {
+            is SQLiteParser.Factored_select_stmtContext,
+            is SQLiteParser.Compound_select_stmtContext,
+            is SQLiteParser.Select_stmtContext,
+            is SQLiteParser.Simple_select_stmtContext ->
+                QueryType.SELECT
+
+            is SQLiteParser.Delete_stmt_limitedContext,
+            is SQLiteParser.Delete_stmtContext ->
+                QueryType.DELETE
+
+            is SQLiteParser.Insert_stmtContext ->
+                QueryType.INSERT
+            is SQLiteParser.Update_stmtContext,
+            is SQLiteParser.Update_stmt_limitedContext ->
+                QueryType.UPDATE
+            is TerminalNode -> when (statement.text) {
+                "EXPLAIN" -> QueryType.EXPLAIN
+                else -> QueryType.UNKNOWN
+            }
+            else -> QueryType.UNKNOWN
+        }
+    }
+
+    override fun visitExpr(ctx: SQLiteParser.ExprContext): Void? {
+        val bindParameter = ctx.BIND_PARAMETER()
+        if (bindParameter != null) {
+            bindingExpressions.add(bindParameter)
+        }
+        return super.visitExpr(ctx)
+    }
+
+    fun createParsedQuery(): ParsedQuery {
+        return ParsedQuery(original,
+                queryType,
+                bindingExpressions.sortedBy { it.sourceInterval.a },
+                tableNames,
+                syntaxErrors)
+    }
+
+    override fun visitTable_or_subquery(ctx: SQLiteParser.Table_or_subqueryContext): Void? {
+        val tableName = ctx.table_name()?.text
+        if (tableName != null) {
+            val tableAlias = ctx.table_alias()?.text
+            tableNames.add(Table(unescapeIdentifier(tableName),
+                    unescapeIdentifier(tableAlias ?: tableName)))
+        }
+        return super.visitTable_or_subquery(ctx)
+    }
+
+    private fun unescapeIdentifier(text : String) : String {
+        val trimmed = text.trim()
+        if (trimmed.startsWith("`") && trimmed.endsWith('`')) {
+            return unescapeIdentifier(trimmed.substring(1, trimmed.length - 1))
+        }
+        if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
+            return unescapeIdentifier(trimmed.substring(1, trimmed.length - 1))
+        }
+        return trimmed
+    }
+}
+
+class SqlParser {
+    companion object {
+        fun parse(input: String): ParsedQuery {
+            val inputStream = ANTLRInputStream(input)
+            val lexer = SQLiteLexer(inputStream)
+            val tokenStream = CommonTokenStream(lexer)
+            val parser = SQLiteParser(tokenStream)
+            val syntaxErrors = arrayListOf<String>()
+            parser.addErrorListener(object : BaseErrorListener() {
+                override fun syntaxError(recognizer: Recognizer<*, *>, offendingSymbol: Any,
+                                         line: Int, charPositionInLine: Int, msg: String,
+                                         e: RecognitionException?) {
+                    syntaxErrors.add(msg)
+                }
+            })
+            try {
+                val parsed = parser.parse()
+                val statementList = parsed.sql_stmt_list()
+                if (statementList.isEmpty()) {
+                    syntaxErrors.add(ParserErrors.NOT_ONE_QUERY)
+                    return ParsedQuery(input, QueryType.UNKNOWN, emptyList(), emptySet(),
+                            listOf(ParserErrors.NOT_ONE_QUERY))
+                }
+                val statements = statementList.first().children
+                        .filter { it is SQLiteParser.Sql_stmtContext }
+                if (statements.size != 1) {
+                    syntaxErrors.add(ParserErrors.NOT_ONE_QUERY)
+                }
+                val statement = statements.first()
+                return QueryVisitor(input, syntaxErrors, statement).createParsedQuery()
+            } catch (antlrError: RuntimeException) {
+                return ParsedQuery(input, QueryType.UNKNOWN, emptyList(), emptySet(),
+                        listOf("unknown error while parsing $input : ${antlrError.message}"))
+            }
+        }
+    }
+}
+
+enum class QueryType {
+    UNKNOWN,
+    SELECT,
+    DELETE,
+    UPDATE,
+    EXPLAIN,
+    INSERT;
+
+    companion object {
+        // IF you change this, don't forget to update @Query documentation.
+        val SUPPORTED = hashSetOf(SELECT, DELETE, UPDATE)
+    }
+}
+
+enum class SQLTypeAffinity {
+    NULL,
+    TEXT,
+    INTEGER,
+    REAL,
+    BLOB;
+    fun getTypeMirrors(env : ProcessingEnvironment) : List<TypeMirror>? {
+        val typeUtils = env.typeUtils
+        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)
+            REAL -> withBoxedTypes(env, TypeKind.DOUBLE, TypeKind.FLOAT)
+            BLOB -> listOf(typeUtils.getArrayType(
+                    typeUtils.getPrimitiveType(TypeKind.BYTE)))
+            else -> emptyList()
+        }
+    }
+
+    private fun withBoxedTypes(env : ProcessingEnvironment, vararg primitives : TypeKind) :
+            List<TypeMirror> {
+        return primitives.flatMap {
+            val primitiveType = env.typeUtils.getPrimitiveType(it)
+            listOf(primitiveType, env.typeUtils.boxedClass(primitiveType).asType())
+        }
+    }
+
+    companion object {
+        // converts from ColumnInfo#SQLiteTypeAffinity
+        fun fromAnnotationValue(value : Int) : SQLTypeAffinity? {
+            return when(value) {
+                ColumnInfo.BLOB -> BLOB
+                ColumnInfo.INTEGER -> INTEGER
+                ColumnInfo.REAL -> REAL
+                ColumnInfo.TEXT -> TEXT
+                else -> null
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/preconditions/Checks.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/preconditions/Checks.kt
new file mode 100644
index 0000000..dad5c1c
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/preconditions/Checks.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.preconditions
+
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.log.RLog
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
+import javax.lang.model.element.Element
+import kotlin.reflect.KClass
+
+/**
+ * Similar to preconditions but element bound and just logs the error instead of throwing an
+ * exception.
+ * <p>
+ * It is important for processing to continue when some errors happen so that we can generate as
+ * much code as possible, leaving only the errors in javac output.
+ */
+class Checks(private val logger: RLog) {
+
+    fun check(predicate: Boolean, element: Element, errorMsg: String, vararg args: Any): Boolean {
+        if (!predicate) {
+            logger.e(element, errorMsg, args)
+        }
+        return predicate
+    }
+
+    fun hasAnnotation(element: Element, annotation: KClass<out Annotation>, errorMsg: String,
+                      vararg args: Any): Boolean {
+        return if (!element.hasAnnotation(annotation)) {
+            logger.e(element, errorMsg, args)
+            false
+        } else {
+            true
+        }
+    }
+
+    fun notUnbound(typeName: TypeName, element: Element, errorMsg: String,
+                   vararg args: Any): Boolean {
+        // TODO support bounds cases like <T extends Foo> T bar()
+        val failed = check(typeName !is TypeVariableName, element, errorMsg, args)
+        if (typeName is ParameterizedTypeName) {
+            val nestedFailure = typeName.typeArguments
+                    .map { notUnbound(it, element, errorMsg, args) }
+                    .any { it == true }
+            return !(failed || nestedFailure)
+        }
+        return !failed
+    }
+
+    fun notBlank(value: String?, element: Element, msg: String, vararg args: Any) : Boolean {
+        return check(value != null && value.isNotBlank(), element, msg, args)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/Context.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/Context.kt
new file mode 100644
index 0000000..4e84be3
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/Context.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.log.RLog
+import android.arch.persistence.room.preconditions.Checks
+import android.arch.persistence.room.processor.cache.Cache
+import android.arch.persistence.room.solver.TypeAdapterStore
+import android.arch.persistence.room.verifier.DatabaseVerifier
+import java.io.File
+import java.util.LinkedHashSet
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Element
+import javax.lang.model.type.TypeMirror
+
+class Context private constructor(val processingEnv: ProcessingEnvironment,
+                                  val logger: RLog,
+                                  private val typeConverters
+                                        : CustomConverterProcessor.ProcessResult,
+                                  private val inheritedAdapterStore: TypeAdapterStore?,
+                                  val cache: Cache) {
+    val checker: Checks = Checks(logger)
+    val COMMON_TYPES: Context.CommonTypes = Context.CommonTypes(processingEnv)
+
+    val typeAdapterStore by lazy {
+        if (inheritedAdapterStore != null) {
+            TypeAdapterStore.copy(this, inheritedAdapterStore)
+        } else {
+            TypeAdapterStore.create(this, typeConverters.converters)
+        }
+    }
+
+    // set when database and its entities are processed.
+    var databaseVerifier: DatabaseVerifier? = null
+
+    companion object {
+        val ARG_OPTIONS by lazy {
+            ProcessorOptions.values().map { it.argName }
+        }
+    }
+
+    constructor(processingEnv: ProcessingEnvironment) : this(
+            processingEnv = processingEnv,
+            logger = RLog(RLog.ProcessingEnvMessager(processingEnv), emptySet(), null),
+            typeConverters = CustomConverterProcessor.ProcessResult.EMPTY,
+            inheritedAdapterStore = null,
+            cache = Cache(null, LinkedHashSet(), emptySet())) {
+    }
+
+    class CommonTypes(val processingEnv: ProcessingEnvironment) {
+        val STRING: TypeMirror by lazy {
+            processingEnv.elementUtils.getTypeElement("java.lang.String").asType()
+        }
+    }
+
+    val schemaOutFolder by lazy {
+        val arg = processingEnv.options[ProcessorOptions.OPTION_SCHEMA_FOLDER.argName]
+        if (arg?.isNotEmpty() ?: false) {
+            File(arg)
+        } else {
+            null
+        }
+    }
+
+    fun <T> collectLogs(handler: (Context) -> T): Pair<T, RLog.CollectingMessager> {
+        val collector = RLog.CollectingMessager()
+        val subContext = Context(processingEnv = processingEnv,
+                logger = RLog(collector, logger.suppressedWarnings, logger.defaultElement),
+                typeConverters = this.typeConverters,
+                inheritedAdapterStore = typeAdapterStore,
+                cache = cache)
+        subContext.databaseVerifier = databaseVerifier
+        val result = handler(subContext)
+        return Pair(result, collector)
+    }
+
+    fun fork(element: Element): Context {
+        val suppressedWarnings = SuppressWarningProcessor.getSuppressedWarnings(element)
+        val processConvertersResult = CustomConverterProcessor.findConverters(this, element)
+        val canReUseAdapterStore = processConvertersResult.classes.isEmpty()
+        // order here is important since the sub context should give priority to new converters.
+        val subTypeConverters = if (canReUseAdapterStore) {
+            this.typeConverters
+        } else {
+            processConvertersResult + this.typeConverters
+        }
+        val subSuppressedWarnings = suppressedWarnings + logger.suppressedWarnings
+        val subCache = Cache(cache, subTypeConverters.classes, subSuppressedWarnings)
+        val subContext = Context(
+                processingEnv = processingEnv,
+                logger = RLog(logger.messager, subSuppressedWarnings, element),
+                typeConverters = subTypeConverters,
+                inheritedAdapterStore = if (canReUseAdapterStore) typeAdapterStore else null,
+                cache = subCache)
+        subContext.databaseVerifier = databaseVerifier
+        return subContext
+    }
+
+    enum class ProcessorOptions(val argName: String) {
+        OPTION_SCHEMA_FOLDER("room.schemaLocation")
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/CustomConverterProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/CustomConverterProcessor.kt
new file mode 100644
index 0000000..1ed245d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/CustomConverterProcessor.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.TypeConverter
+import android.arch.persistence.room.TypeConverters
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.ext.hasAnyOf
+import android.arch.persistence.room.ext.toListOfClassTypes
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_BAD_RETURN_TYPE
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_EMPTY_CLASS
+import android.arch.persistence.room.processor.ProcessorErrors
+        .TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_BE_PUBLIC
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_RECEIVE_1_PARAM
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_UNBOUND_GENERIC
+import android.arch.persistence.room.solver.types.CustomTypeConverterWrapper
+import android.arch.persistence.room.vo.CustomTypeConverter
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import java.util.LinkedHashSet
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.ElementFilter
+
+/**
+ * Processes classes that are referenced in TypeConverters annotations.
+ */
+class CustomConverterProcessor(val context: Context, val element: TypeElement) {
+    companion object {
+        private val INVALID_RETURN_TYPES = setOf(TypeKind.ERROR, TypeKind.VOID, TypeKind.NONE)
+        fun findConverters(context: Context, element: Element): ProcessResult {
+            val annotation = MoreElements.getAnnotationMirror(element,
+                    TypeConverters::class.java).orNull()
+            return annotation?.let {
+                val classes = AnnotationMirrors.getAnnotationValue(annotation, "value")
+                        ?.toListOfClassTypes()
+                        ?.filter {
+                            MoreTypes.isType(it)
+                        }?.mapTo(LinkedHashSet(), {it}) ?: LinkedHashSet<TypeMirror>()
+                val converters = classes
+                        .flatMap {
+                            CustomConverterProcessor(context, MoreTypes.asTypeElement(it))
+                                    .process()
+                        }
+                converters.let {
+                    reportDuplicates(context, converters)
+                }
+                ProcessResult(classes, converters.map(::CustomTypeConverterWrapper))
+            } ?: ProcessResult.EMPTY
+        }
+
+        fun reportDuplicates(context: Context, converters : List<CustomTypeConverter>) {
+            val groupedByFrom = converters.groupBy { it.from.typeName() }
+            groupedByFrom.forEach {
+                it.value.groupBy { it.to.typeName() }.forEach {
+                    if (it.value.size > 1) {
+                        it.value.forEach { converter ->
+                            context.logger.e(converter.method, ProcessorErrors
+                                    .duplicateTypeConverters(it.value.minus(converter)))
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fun process(): List<CustomTypeConverter> {
+        // using element utils instead of MoreElements to include statics.
+        val methods = ElementFilter
+                .methodsIn(context.processingEnv.elementUtils.getAllMembers(element))
+        val declaredType = MoreTypes.asDeclared(element.asType())
+        val converterMethods = methods.filter {
+            it.hasAnnotation(TypeConverter::class)
+        }
+        context.checker.check(converterMethods.isNotEmpty(), element, TYPE_CONVERTER_EMPTY_CLASS)
+        val allStatic = converterMethods.all { it.modifiers.contains(Modifier.STATIC) }
+        val constructors = ElementFilter.constructorsIn(
+                context.processingEnv.elementUtils.getAllMembers(element))
+        context.checker.check(allStatic || constructors.isEmpty() || constructors.any {
+            it.parameters.isEmpty()
+        }, element, TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR)
+        return converterMethods.map {
+            processMethod(declaredType, it)
+        }.filterNotNull()
+    }
+
+    private fun processMethod(container: DeclaredType, methodElement: ExecutableElement)
+            : CustomTypeConverter? {
+        val asMember = context.processingEnv.typeUtils.asMemberOf(container, methodElement)
+        val executableType = MoreTypes.asExecutable(asMember)
+        val returnType = executableType.returnType
+        val invalidReturnType = INVALID_RETURN_TYPES.contains(returnType.kind)
+        context.checker.check(methodElement.hasAnyOf(Modifier.PUBLIC), methodElement,
+                TYPE_CONVERTER_MUST_BE_PUBLIC)
+        if (invalidReturnType) {
+            context.logger.e(methodElement, TYPE_CONVERTER_BAD_RETURN_TYPE)
+            return null
+        }
+        val returnTypeName = returnType.typeName()
+        context.checker.notUnbound(returnTypeName, methodElement,
+                TYPE_CONVERTER_UNBOUND_GENERIC)
+        val params = methodElement.parameters
+        if (params.size != 1) {
+            context.logger.e(methodElement, TYPE_CONVERTER_MUST_RECEIVE_1_PARAM)
+            return null
+        }
+        val param = params.map {
+            MoreTypes.asMemberOf(context.processingEnv.typeUtils, container, it)
+        }.first()
+        context.checker.notUnbound(param.typeName(), params[0], TYPE_CONVERTER_UNBOUND_GENERIC)
+        return CustomTypeConverter(container, methodElement, param, returnType)
+    }
+
+    /**
+     * Order of classes is important hence they are a LinkedHashSet not a set.
+     */
+    open class ProcessResult(val classes: LinkedHashSet<TypeMirror>,
+                             val converters: List<CustomTypeConverterWrapper>) {
+        object EMPTY : ProcessResult(LinkedHashSet(), emptyList())
+        operator fun plus(other : ProcessResult) : ProcessResult {
+            val newClasses = LinkedHashSet<TypeMirror>()
+            newClasses.addAll(classes)
+            newClasses.addAll(other.classes)
+            return ProcessResult(newClasses, converters + other.converters)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DaoProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DaoProcessor.kt
new file mode 100644
index 0000000..0c0ff49
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DaoProcessor.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Delete
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.SkipQueryVerification
+import android.arch.persistence.room.Update
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.ext.hasAnyOf
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.verifier.DatabaseVerifier
+import android.arch.persistence.room.vo.Dao
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier.ABSTRACT
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+
+class DaoProcessor(baseContext : Context, val element: TypeElement, val dbType: DeclaredType,
+                   val dbVerifier : DatabaseVerifier?) {
+    val context = baseContext.fork(element)
+
+    companion object {
+        val PROCESSED_ANNOTATIONS = listOf(Insert::class, Delete::class, Query::class,
+                Update::class)
+    }
+
+    fun process() : Dao {
+        context.checker.hasAnnotation(element, android.arch.persistence.room.Dao::class,
+                ProcessorErrors.DAO_MUST_BE_ANNOTATED_WITH_DAO)
+        context.checker.check(element.hasAnyOf(ABSTRACT) || element.kind == ElementKind.INTERFACE,
+                element, ProcessorErrors.DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE)
+
+        val declaredType = MoreTypes.asDeclared(element.asType())
+        val allMembers = context.processingEnv.elementUtils.getAllMembers(element)
+        val methods = allMembers
+            .filter {
+                it.hasAnyOf(ABSTRACT) && it.kind == ElementKind.METHOD
+            }.map {
+                MoreElements.asExecutable(it)
+            }.groupBy { method ->
+                context.checker.check(
+                        PROCESSED_ANNOTATIONS.count { method.hasAnnotation(it) } == 1, method,
+                        ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_DAO_METHOD_ANNOTATION
+                )
+                if (method.hasAnnotation(Query::class)) {
+                    Query::class
+                } else if (method.hasAnnotation(Insert::class)) {
+                    Insert::class
+                } else if (method.hasAnnotation(Delete::class)) {
+                    Delete::class
+                } else if (method.hasAnnotation(Update::class)) {
+                    Update::class
+                } else {
+                    Any::class
+                }
+            }
+        val processorVerifier = if (element.hasAnnotation(SkipQueryVerification::class)) {
+            null
+        } else {
+            dbVerifier
+        }
+
+        val queryMethods = methods[Query::class]?.map {
+            QueryMethodProcessor(
+                    baseContext = context,
+                    containing = declaredType,
+                    executableElement = it,
+                    dbVerifier = processorVerifier).process()
+        } ?: emptyList()
+
+        val insertionMethods = methods[Insert::class]?.map {
+            InsertionMethodProcessor(
+                    baseContext = context,
+                    containing = declaredType,
+                    executableElement = it).process()
+        } ?: emptyList()
+
+        val deletionMethods = methods[Delete::class]?.map {
+            DeletionMethodProcessor(
+                    baseContext = context,
+                    containing = declaredType,
+                    executableElement = it).process()
+        } ?: emptyList()
+
+        val updateMethods = methods[Update::class]?.map {
+            UpdateMethodProcessor(
+                    baseContext = context,
+                    containing = declaredType,
+                    executableElement = it).process()
+        } ?: emptyList()
+
+        val constructors = allMembers
+                .filter { it.kind == ElementKind.CONSTRUCTOR }
+                .map { MoreElements.asExecutable(it) }
+        val typeUtils = context.processingEnv.typeUtils
+        val goodConstructor = constructors
+                .filter {
+                    it.parameters.size == 1
+                            && typeUtils.isAssignable(dbType, it.parameters[0].asType())
+                }
+                .firstOrNull()
+        val constructorParamType = if (goodConstructor != null) {
+            goodConstructor.parameters[0].asType().typeName()
+        } else {
+            validateEmptyConstructor(constructors)
+            null
+        }
+
+        context.checker.check(methods[Any::class] == null, element,
+                ProcessorErrors.ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION)
+
+        val type = TypeName.get(declaredType)
+        context.checker.notUnbound(type, element,
+                ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES)
+
+        return Dao(element = element,
+                type = declaredType,
+                queryMethods = queryMethods,
+                insertionMethods = insertionMethods,
+                deletionMethods = deletionMethods,
+                updateMethods = updateMethods,
+                constructorParamType = constructorParamType)
+    }
+
+    private fun validateEmptyConstructor(constructors: List<ExecutableElement>) {
+        if (constructors.isNotEmpty() && constructors.all { it.parameters.isNotEmpty() }) {
+            context.logger.e(element, ProcessorErrors.daoMustHaveMatchingConstructor(
+                    element.toString(), dbType.toString()))
+        }
+    }
+
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DatabaseProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DatabaseProcessor.kt
new file mode 100644
index 0000000..9ebf6df
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DatabaseProcessor.kt
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.SkipQueryVerification
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.getAsBoolean
+import android.arch.persistence.room.ext.getAsInt
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.ext.hasAnyOf
+import android.arch.persistence.room.ext.toListOfClassTypes
+import android.arch.persistence.room.verifier.DatabaseVerifier
+import android.arch.persistence.room.vo.Dao
+import android.arch.persistence.room.vo.DaoMethod
+import android.arch.persistence.room.vo.Database
+import android.arch.persistence.room.vo.Entity
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeMirror
+
+
+class DatabaseProcessor(baseContext: Context, val element: TypeElement) {
+    val context = baseContext.fork(element)
+
+    val baseClassElement: TypeMirror by lazy {
+        context.processingEnv.elementUtils.getTypeElement(
+                RoomTypeNames.ROOM_DB.packageName() + "." + RoomTypeNames.ROOM_DB.simpleName())
+                .asType()
+    }
+
+    fun process(): Database {
+        val dbAnnotation = MoreElements
+                .getAnnotationMirror(element, android.arch.persistence.room.Database::class.java)
+                .orNull()
+        val entities = processEntities(dbAnnotation, element)
+        validateUniqueTableNames(element, entities)
+        validateForeignKeys(element, entities)
+
+        val extendsRoomDb = context.processingEnv.typeUtils.isAssignable(
+                MoreElements.asType(element).asType(), baseClassElement)
+        context.checker.check(extendsRoomDb, element, ProcessorErrors.DB_MUST_EXTEND_ROOM_DB)
+
+        val allMembers = context.processingEnv.elementUtils.getAllMembers(element)
+
+        val dbVerifier = if (element.hasAnnotation(SkipQueryVerification::class)) {
+            null
+        } else {
+            DatabaseVerifier.create(context, element, entities)
+        }
+        context.databaseVerifier = dbVerifier
+
+        val declaredType = MoreTypes.asDeclared(element.asType())
+        val daoMethods = allMembers.filter {
+            it.hasAnyOf(Modifier.ABSTRACT) && it.kind == ElementKind.METHOD
+        }.filterNot {
+            // remove methods that belong to room
+            val containing = it.enclosingElement
+            MoreElements.isType(containing) &&
+                    TypeName.get(containing.asType()) == RoomTypeNames.ROOM_DB
+        }.map {
+            val executable = MoreElements.asExecutable(it)
+            // TODO when we add support for non Dao return types (e.g. database), this code needs
+            // to change
+            val daoType = MoreTypes.asTypeElement(executable.returnType)
+            val dao = DaoProcessor(context, daoType, declaredType, dbVerifier).process()
+            DaoMethod(executable, executable.simpleName.toString(), dao)
+        }
+        validateUniqueDaoClasses(element, daoMethods, entities)
+        validateUniqueIndices(element, entities)
+        val version = AnnotationMirrors.getAnnotationValue(dbAnnotation, "version")
+                .getAsInt(1)!!.toInt()
+        val exportSchema = AnnotationMirrors.getAnnotationValue(dbAnnotation, "exportSchema")
+                .getAsBoolean(true)
+
+        val hasForeignKeys = entities.any { it.foreignKeys.isNotEmpty() }
+
+        val database = Database(
+                version = version,
+                element = element,
+                type = MoreElements.asType(element).asType(),
+                entities = entities,
+                daoMethods = daoMethods,
+                exportSchema = exportSchema,
+                enableForeignKeys = hasForeignKeys)
+        return database
+    }
+
+    private fun validateForeignKeys(element: TypeElement, entities: List<Entity>) {
+        val byTableName = entities.associateBy { it.tableName }
+        entities.forEach { entity ->
+            entity.foreignKeys.forEach foreignKeyLoop@ { foreignKey ->
+                val parent = byTableName[foreignKey.parentTable]
+                if (parent == null) {
+                    context.logger.e(element, ProcessorErrors
+                            .foreignKeyMissingParentEntityInDatabase(foreignKey.parentTable,
+                                    entity.element.qualifiedName.toString()))
+                    return@foreignKeyLoop
+                }
+                val parentFields = foreignKey.parentColumns.map { columnName ->
+                    val parentField = parent.fields.find {
+                        it.columnName == columnName
+                    }
+                    if (parentField == null) {
+                        context.logger.e(entity.element,
+                                ProcessorErrors.foreignKeyParentColumnDoesNotExist(
+                                        parentEntity = parent.element.qualifiedName.toString(),
+                                        missingColumn = columnName,
+                                        allColumns = parent.fields.map { it.columnName }))
+                    }
+                    parentField
+                }.filterNotNull()
+                if (parentFields.size != foreignKey.parentColumns.size) {
+                    return@foreignKeyLoop
+                }
+                // ensure that it is indexed in the parent
+                if (!parent.isUnique(foreignKey.parentColumns)) {
+                    context.logger.e(parent.element, ProcessorErrors
+                            .foreignKeyMissingIndexInParent(
+                                    parentEntity = parent.element.qualifiedName.toString(),
+                                    childEntity = entity.element.qualifiedName.toString(),
+                                    parentColumns = foreignKey.parentColumns,
+                                    childColumns = foreignKey.childFields
+                                            .map { it.columnName }))
+                    return@foreignKeyLoop
+                }
+            }
+        }
+    }
+
+    private fun validateUniqueIndices(element: TypeElement, entities: List<Entity>) {
+        entities
+                .flatMap { entity ->
+                    // associate each index with its entity
+                    entity.indices.map { Pair(it.name, entity) }
+                }
+                .groupBy { it.first } // group by index name
+                .filter { it.value.size > 1 } // get the ones with duplicate names
+                .forEach {
+                    // do not report duplicates from the same entity
+                    if (it.value.distinctBy { it.second.typeName }.size > 1) {
+                        context.logger.e(element,
+                                ProcessorErrors.duplicateIndexInDatabase(it.key,
+                                        it.value.map { "${it.second.typeName} > ${it.first}" }))
+                    }
+                }
+    }
+
+    private fun validateUniqueDaoClasses(dbElement: TypeElement, daoMethods: List<DaoMethod>,
+                                         entities : List<Entity>) {
+        val entityTypeNames = entities.map { it.typeName }.toSet()
+        daoMethods.groupBy { it.dao.typeName }
+                .forEach {
+                    if (it.value.size > 1) {
+                        val error = ProcessorErrors.duplicateDao(it.key, it.value.map { it.name })
+                        it.value.forEach { daoMethod ->
+                            context.logger.e(daoMethod.element,
+                                    ProcessorErrors.DAO_METHOD_CONFLICTS_WITH_OTHERS)
+                        }
+                        // also report the full error for the database
+                        context.logger.e(dbElement, error)
+                    }
+                }
+        val check = fun(element : Element, dao : Dao,
+                        typeName : TypeName?) {
+            typeName?.let {
+                if (!entityTypeNames.contains(typeName)) {
+                    context.logger.e(element,
+                            ProcessorErrors.shortcutEntityIsNotInDatabase(
+                                    database = dbElement.qualifiedName.toString(),
+                                    dao = dao.typeName.toString(),
+                                    entity = typeName.toString()
+                            ))
+                }
+            }
+        }
+        daoMethods.forEach { daoMethod ->
+            daoMethod.dao.shortcutMethods.forEach { method ->
+                method.entities.forEach {
+                    check(method.element, daoMethod.dao, it.value.typeName)
+                }
+            }
+            daoMethod.dao.insertionMethods.forEach { method ->
+                method.entities.forEach {
+                    check(method.element, daoMethod.dao, it.value.typeName)
+                }
+            }
+        }
+    }
+
+    private fun validateUniqueTableNames(dbElement: TypeElement, entities: List<Entity>) {
+        entities
+                .groupBy {
+                    it.tableName.toLowerCase()
+                }.filter {
+            it.value.size > 1
+        }.forEach { byTableName ->
+            val error = ProcessorErrors.duplicateTableNames(byTableName.key,
+                    byTableName.value.map { it.typeName.toString() })
+            // report it for each of them and the database to make it easier
+            // for the developer
+            byTableName.value.forEach { entity ->
+                context.logger.e(entity.element, error)
+            }
+            context.logger.e(dbElement, error)
+        }
+    }
+
+    private fun processEntities(dbAnnotation: AnnotationMirror?, element: TypeElement):
+            List<Entity> {
+        if (!context.checker.check(dbAnnotation != null, element,
+                ProcessorErrors.DATABASE_MUST_BE_ANNOTATED_WITH_DATABASE)) {
+            return listOf()
+        }
+
+        val entityList = AnnotationMirrors.getAnnotationValue(dbAnnotation, "entities")
+        val listOfTypes = entityList.toListOfClassTypes()
+        context.checker.check(listOfTypes.isNotEmpty(), element,
+                ProcessorErrors.DATABASE_ANNOTATION_MUST_HAVE_LIST_OF_ENTITIES)
+        return listOfTypes.map {
+            EntityProcessor(context, MoreTypes.asTypeElement(it)).process()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DeletionMethodProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DeletionMethodProcessor.kt
new file mode 100644
index 0000000..4812306
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/DeletionMethodProcessor.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Delete
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.vo.DeletionMethod
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+
+class DeletionMethodProcessor(baseContext: Context,
+                              val containing: DeclaredType,
+                              val executableElement: ExecutableElement) {
+    val context = baseContext.fork(executableElement)
+
+    fun process(): DeletionMethod {
+        val delegate = ShortcutMethodProcessor(context, containing, executableElement)
+        delegate.extractAnnotation(Delete::class, ProcessorErrors.MISSING_DELETE_ANNOTATION)
+
+        val returnTypeName = delegate.extractReturnType().typeName()
+        context.checker.check(
+                returnTypeName == TypeName.VOID || returnTypeName == TypeName.INT,
+                executableElement,
+                ProcessorErrors.DELETION_METHODS_MUST_RETURN_VOID_OR_INT
+        )
+
+        val (entities, params) = delegate.extractParams(
+                missingParamError = ProcessorErrors
+                        .DELETION_MISSING_PARAMS
+        )
+
+        return DeletionMethod(
+                element = delegate.executableElement,
+                name = delegate.executableElement.simpleName.toString(),
+                entities = entities,
+                returnCount = returnTypeName == TypeName.INT,
+                parameters = params
+        )
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/EntityProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/EntityProcessor.kt
new file mode 100644
index 0000000..4f4a7f4
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/EntityProcessor.kt
@@ -0,0 +1,543 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.ext.getAsBoolean
+import android.arch.persistence.room.ext.getAsInt
+import android.arch.persistence.room.ext.getAsString
+import android.arch.persistence.room.ext.getAsStringList
+import android.arch.persistence.room.ext.toType
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.processor.ProcessorErrors.INDEX_COLUMNS_CANNOT_BE_EMPTY
+import android.arch.persistence.room.processor.ProcessorErrors.RELATION_IN_ENTITY
+import android.arch.persistence.room.processor.cache.Cache
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.ForeignKey
+import android.arch.persistence.room.vo.ForeignKeyAction
+import android.arch.persistence.room.vo.Index
+import android.arch.persistence.room.vo.Pojo
+import android.arch.persistence.room.vo.PrimaryKey
+import android.arch.persistence.room.vo.Warning
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.AnnotationMirrors.getAnnotationValue
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.SimpleAnnotationValueVisitor6
+
+class EntityProcessor(baseContext: Context, val element: TypeElement) {
+    val context = baseContext.fork(element)
+
+    fun process(): Entity {
+        return context.cache.entities.get(Cache.EntityKey(element), {
+            doProcess()
+        })
+    }
+    private fun doProcess() : Entity {
+        context.checker.hasAnnotation(element, android.arch.persistence.room.Entity::class,
+                ProcessorErrors.ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY)
+        val pojo = PojoProcessor(
+                baseContext = context,
+                element = element,
+                bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+                parent = null).process()
+        context.checker.check(pojo.relations.isEmpty(), element, RELATION_IN_ENTITY)
+        val annotation = MoreElements.getAnnotationMirror(element,
+                android.arch.persistence.room.Entity::class.java).orNull()
+        val tableName: String
+        val entityIndices: List<IndexInput>
+        val foreignKeyInputs: List<ForeignKeyInput>
+        val inheritSuperIndices: Boolean
+        if (annotation != null) {
+            tableName = extractTableName(element, annotation)
+            entityIndices = extractIndices(annotation, tableName)
+            inheritSuperIndices = AnnotationMirrors
+                    .getAnnotationValue(annotation, "inheritSuperIndices").getAsBoolean(false)
+            foreignKeyInputs = extractForeignKeys(annotation)
+        } else {
+            tableName = element.simpleName.toString()
+            foreignKeyInputs = emptyList()
+            entityIndices = emptyList()
+            inheritSuperIndices = false
+        }
+        context.checker.notBlank(tableName, element,
+                ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY)
+
+        val fieldIndices = pojo.fields
+                .filter { it.indexed }
+                .map {
+                    if (it.parent != null) {
+                        it.indexed = false
+                        context.logger.w(Warning.INDEX_FROM_EMBEDDED_FIELD_IS_DROPPED, it.element,
+                                ProcessorErrors.droppedEmbeddedFieldIndex(
+                                        it.getPath(), element.qualifiedName.toString()))
+                        null
+                    } else if (it.element.enclosingElement != element && !inheritSuperIndices) {
+                        it.indexed = false
+                        context.logger.w(Warning.INDEX_FROM_PARENT_FIELD_IS_DROPPED,
+                                ProcessorErrors.droppedSuperClassFieldIndex(
+                                        it.columnName, element.toString(),
+                                        it.element.enclosingElement.toString()
+                                ))
+                        null
+                    } else {
+                        IndexInput(
+                                name = createIndexName(listOf(it.columnName), tableName),
+                                unique = false,
+                                columnNames = listOf(it.columnName)
+                        )
+                    }
+                }.filterNotNull()
+        val superIndices = loadSuperIndices(element.superclass, tableName, inheritSuperIndices)
+        val indexInputs = entityIndices + fieldIndices + superIndices
+        val indices = validateAndCreateIndices(indexInputs, pojo)
+
+        val primaryKey = findPrimaryKey(pojo.fields, pojo.embeddedFields)
+        val affinity = primaryKey.fields.firstOrNull()?.affinity ?: SQLTypeAffinity.TEXT
+        context.checker.check(
+                !primaryKey.autoGenerateId || affinity == SQLTypeAffinity.INTEGER,
+                primaryKey.fields.firstOrNull()?.element ?: element,
+                ProcessorErrors.AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT
+        )
+
+        val entityForeignKeys = validateAndCreateForeignKeyReferences(foreignKeyInputs, pojo)
+        checkIndicesForForeignKeys(entityForeignKeys, primaryKey, indices)
+
+        val entity = Entity(element = element,
+                tableName = tableName,
+                type = pojo.type,
+                fields = pojo.fields,
+                embeddedFields = pojo.embeddedFields,
+                indices = indices,
+                primaryKey = primaryKey,
+                foreignKeys = entityForeignKeys,
+                constructor = pojo.constructor)
+
+        return entity
+    }
+
+    private fun checkIndicesForForeignKeys(entityForeignKeys: List<ForeignKey>,
+                                           primaryKey: PrimaryKey,
+                                           indices: List<Index>) {
+        fun covers(columnNames: List<String>, fields : List<Field>) : Boolean =
+            fields.size >= columnNames.size && columnNames.withIndex().all {
+                fields[it.index].columnName == it.value
+            }
+
+        entityForeignKeys.forEach { fKey ->
+            val columnNames = fKey.childFields.map { it.columnName }
+            val exists = covers(columnNames, primaryKey.fields) || indices.any { index ->
+                covers(columnNames, index.fields)
+            }
+            if (!exists) {
+                if (columnNames.size == 1) {
+                    context.logger.w(Warning.MISSING_INDEX_ON_FOREIGN_KEY_CHILD, element,
+                            ProcessorErrors.foreignKeyMissingIndexInChildColumn(columnNames[0]))
+                } else {
+                    context.logger.w(Warning.MISSING_INDEX_ON_FOREIGN_KEY_CHILD, element,
+                            ProcessorErrors.foreignKeyMissingIndexInChildColumns(columnNames))
+                }
+            }
+        }
+    }
+
+    /**
+     * Does a validation on foreign keys except the parent table's columns.
+     */
+    private fun validateAndCreateForeignKeyReferences(foreignKeyInputs: List<ForeignKeyInput>,
+                                                      pojo: Pojo): List<ForeignKey> {
+        return foreignKeyInputs.map {
+            if (it.onUpdate == null) {
+                context.logger.e(element, ProcessorErrors.INVALID_FOREIGN_KEY_ACTION)
+                return@map null
+            }
+            if (it.onDelete == null) {
+                context.logger.e(element, ProcessorErrors.INVALID_FOREIGN_KEY_ACTION)
+                return@map null
+            }
+            if (it.childColumns.isEmpty()) {
+                context.logger.e(element, ProcessorErrors.FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST)
+                return@map null
+            }
+            if (it.parentColumns.isEmpty()) {
+                context.logger.e(element, ProcessorErrors.FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST)
+                return@map null
+            }
+            if (it.childColumns.size != it.parentColumns.size) {
+                context.logger.e(element, ProcessorErrors.foreignKeyColumnNumberMismatch(
+                        it.childColumns, it.parentColumns
+                ))
+                return@map null
+            }
+            val parentElement = try {
+                MoreTypes.asElement(it.parent) as TypeElement
+            } catch (noClass: IllegalArgumentException) {
+                context.logger.e(element, ProcessorErrors.FOREIGN_KEY_CANNOT_FIND_PARENT)
+                return@map null
+            }
+            val parentAnnotation = MoreElements.getAnnotationMirror(parentElement,
+                    android.arch.persistence.room.Entity::class.java).orNull()
+            if (parentAnnotation == null) {
+                context.logger.e(element,
+                        ProcessorErrors.foreignKeyNotAnEntity(parentElement.toString()))
+                return@map null
+            }
+            val tableName = extractTableName(parentElement, parentAnnotation)
+            val fields = it.childColumns.map { columnName ->
+                val field = pojo.fields.find { it.columnName == columnName }
+                if (field == null) {
+                    context.logger.e(pojo.element,
+                            ProcessorErrors.foreignKeyChildColumnDoesNotExist(columnName,
+                                    pojo.fields.map { it.columnName }))
+                }
+                field
+            }.filterNotNull()
+            if (fields.size != it.childColumns.size) {
+                return@map null
+            }
+            ForeignKey(
+                    parentTable = tableName,
+                    childFields = fields,
+                    parentColumns = it.parentColumns,
+                    onDelete = it.onDelete,
+                    onUpdate = it.onUpdate,
+                    deferred = it.deferred
+            )
+        }.filterNotNull()
+    }
+
+    private fun findPrimaryKey(fields: List<Field>, embeddedFields: List<EmbeddedField>)
+            : PrimaryKey {
+        val candidates = collectPrimaryKeysFromEntityAnnotations(element, fields) +
+                collectPrimaryKeysFromPrimaryKeyAnnotations(fields) +
+                collectPrimaryKeysFromEmbeddedFields(embeddedFields)
+
+        context.checker.check(candidates.isNotEmpty(), element, ProcessorErrors.MISSING_PRIMARY_KEY)
+        if (candidates.size == 1) {
+            // easy :)
+            return candidates.first()
+        }
+
+        return choosePrimaryKey(candidates, element)
+    }
+
+    /**
+     * Check fields for @PrimaryKey.
+     */
+    private fun collectPrimaryKeysFromPrimaryKeyAnnotations(fields: List<Field>): List<PrimaryKey> {
+        return fields.map { field ->
+            MoreElements.getAnnotationMirror(field.element,
+                    android.arch.persistence.room.PrimaryKey::class.java).orNull()?.let {
+                if (field.parent != null) {
+                    // the field in the entity that contains this error.
+                    val grandParentField = field.parent.mRootParent.field.element
+                    // bound for entity.
+                    context.fork(grandParentField).logger.w(
+                            Warning.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED,
+                            grandParentField,
+                            ProcessorErrors.embeddedPrimaryKeyIsDropped(
+                                    element.qualifiedName.toString(), field.name))
+                    null
+                } else {
+                    PrimaryKey(declaredIn = field.element.enclosingElement,
+                            fields = listOf(field),
+                            autoGenerateId = AnnotationMirrors
+                                    .getAnnotationValue(it, "autoGenerate")
+                                    .getAsBoolean(false))
+                }
+            }
+        }.filterNotNull()
+    }
+
+    /**
+     * Check classes for @Entity(primaryKeys = ?).
+     */
+    private fun collectPrimaryKeysFromEntityAnnotations(typeElement: TypeElement,
+                                                        availableFields: List<Field>)
+            : List<PrimaryKey> {
+        val myPkeys = MoreElements.getAnnotationMirror(typeElement,
+                android.arch.persistence.room.Entity::class.java).orNull()?.let {
+            val primaryKeyColumns = AnnotationMirrors.getAnnotationValue(it, "primaryKeys")
+                    .getAsStringList()
+            if (primaryKeyColumns.isEmpty()) {
+                emptyList<PrimaryKey>()
+            } else {
+                val fields = primaryKeyColumns.map { pKeyColumnName ->
+                    val field = availableFields.firstOrNull { it.columnName == pKeyColumnName }
+                    context.checker.check(field != null, typeElement,
+                            ProcessorErrors.primaryKeyColumnDoesNotExist(pKeyColumnName,
+                                    availableFields.map { it.columnName }))
+                    field
+                }.filterNotNull()
+                listOf(PrimaryKey(declaredIn = typeElement,
+                        fields = fields,
+                        autoGenerateId = false))
+            }
+        } ?: emptyList<PrimaryKey>()
+        // checks supers.
+        val mySuper = typeElement.superclass
+        val superPKeys = if (mySuper != null && mySuper.kind != TypeKind.NONE) {
+            // my super cannot see my fields so remove them.
+            val remainingFields = availableFields.filterNot {
+                it.element.enclosingElement == typeElement
+            }
+            collectPrimaryKeysFromEntityAnnotations(
+                    MoreTypes.asTypeElement(mySuper), remainingFields)
+        } else {
+            emptyList()
+        }
+        return superPKeys + myPkeys
+    }
+
+    private fun collectPrimaryKeysFromEmbeddedFields(embeddedFields: List<EmbeddedField>)
+            : List<PrimaryKey> {
+        return embeddedFields.map { embeddedField ->
+            MoreElements.getAnnotationMirror(embeddedField.field.element,
+                    android.arch.persistence.room.PrimaryKey::class.java).orNull()?.let {
+                val autoGenerate = AnnotationMirrors
+                        .getAnnotationValue(it, "autoGenerate").getAsBoolean(false)
+                context.checker.check(!autoGenerate || embeddedField.pojo.fields.size == 1,
+                        embeddedField.field.element,
+                        ProcessorErrors.AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS)
+                PrimaryKey(declaredIn = embeddedField.field.element.enclosingElement,
+                        fields = embeddedField.pojo.fields,
+                        autoGenerateId = autoGenerate)
+            }
+        }.filterNotNull()
+    }
+
+    // start from my element and check if anywhere in the list we can find the only well defined
+    // pkey, if so, use it.
+    private fun choosePrimaryKey(candidates: List<PrimaryKey>, typeElement: TypeElement)
+            : PrimaryKey {
+        // If 1 of these primary keys is declared in this class, then it is the winner. Just print
+        //    a note for the others.
+        // If 0 is declared, check the parent.
+        // If more than 1 primary key is declared in this class, it is an error.
+        val myPKeys = candidates.filter { candidate ->
+            candidate.declaredIn == typeElement
+        }
+        return if (myPKeys.size == 1) {
+            // just note, this is not worth an error or warning
+            (candidates - myPKeys).forEach {
+                context.logger.d(element,
+                        "${it.toHumanReadableString()} is" +
+                                " overridden by ${myPKeys.first().toHumanReadableString()}")
+            }
+            myPKeys.first()
+        } else if (myPKeys.isEmpty()) {
+            // i have not declared anything, delegate to super
+            val mySuper = typeElement.superclass
+            if (mySuper != null && mySuper.kind != TypeKind.NONE) {
+                return choosePrimaryKey(candidates, MoreTypes.asTypeElement(mySuper))
+            }
+            PrimaryKey.MISSING
+        } else {
+            context.logger.e(element, ProcessorErrors.multiplePrimaryKeyAnnotations(
+                    myPKeys.map(PrimaryKey::toHumanReadableString)))
+            PrimaryKey.MISSING
+        }
+    }
+
+    private fun validateAndCreateIndices(inputs: List<IndexInput>, pojo: Pojo): List<Index> {
+        // check for columns
+        val indices = inputs.map { input ->
+            context.checker.check(input.columnNames.isNotEmpty(), element,
+                    INDEX_COLUMNS_CANNOT_BE_EMPTY)
+            val fields = input.columnNames.map { columnName ->
+                val field = pojo.fields.firstOrNull {
+                    it.columnName == columnName
+                }
+                context.checker.check(field != null, element,
+                        ProcessorErrors.indexColumnDoesNotExist(
+                                columnName, pojo.fields.map { it.columnName }
+                        ))
+                field
+            }.filterNotNull()
+            if (fields.isEmpty()) {
+                null
+            } else {
+                Index(name = input.name, unique = input.unique, fields = fields)
+            }
+        }.filterNotNull()
+
+        // check for duplicate indices
+        indices
+                .groupBy { it.name }
+                .filter { it.value.size > 1 }
+                .forEach {
+                    context.logger.e(element, ProcessorErrors.duplicateIndexInEntity(it.key))
+                }
+
+        // see if any embedded field is an entity with indices, if so, report a warning
+        pojo.embeddedFields.forEach { embedded ->
+            val embeddedElement = embedded.pojo.element
+            val subEntityAnnotation = MoreElements.getAnnotationMirror(embeddedElement,
+                    android.arch.persistence.room.Entity::class.java).orNull()
+            subEntityAnnotation?.let {
+                val subIndices = extractIndices(subEntityAnnotation, "")
+                if (subIndices.isNotEmpty()) {
+                    context.logger.w(Warning.INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED,
+                            embedded.field.element, ProcessorErrors.droppedEmbeddedIndex(
+                            entityName = embedded.pojo.typeName.toString(),
+                            fieldPath = embedded.field.getPath(),
+                            grandParent = element.qualifiedName.toString()))
+                }
+            }
+        }
+        return indices
+    }
+
+    // check if parent is an Entity, if so, report its annotation indices
+    private fun loadSuperIndices(typeMirror: TypeMirror?, tableName: String, inherit: Boolean)
+            : List<IndexInput> {
+        if (typeMirror == null || typeMirror.kind == TypeKind.NONE) {
+            return emptyList()
+        }
+        val parentElement = MoreTypes.asTypeElement(typeMirror)
+        val myIndices = MoreElements.getAnnotationMirror(parentElement,
+                android.arch.persistence.room.Entity::class.java).orNull()?.let { annotation ->
+            val indices = extractIndices(annotation, tableName = "super")
+            if (indices.isEmpty()) {
+                emptyList()
+            } else if (inherit) {
+                // rename them
+                indices.map {
+                    IndexInput(
+                            name = createIndexName(it.columnNames, tableName),
+                            unique = it.unique,
+                            columnNames = it.columnNames)
+                }
+            } else {
+                context.logger.w(Warning.INDEX_FROM_PARENT_IS_DROPPED,
+                        parentElement,
+                        ProcessorErrors.droppedSuperClassIndex(
+                                childEntity = element.qualifiedName.toString(),
+                                superEntity = parentElement.qualifiedName.toString()))
+                emptyList()
+            }
+        } ?: emptyList()
+        return myIndices + loadSuperIndices(parentElement.superclass, tableName, inherit)
+    }
+
+    companion object {
+        private fun extractTableName(element: TypeElement, annotation: AnnotationMirror)
+                : String {
+            val annotationValue = AnnotationMirrors
+                    .getAnnotationValue(annotation, "tableName").value.toString()
+            return if (annotationValue == "") {
+                element.simpleName.toString()
+            } else {
+                annotationValue
+            }
+        }
+
+        private fun extractIndices(annotation: AnnotationMirror, tableName: String)
+                : List<IndexInput> {
+            val arrayOfIndexAnnotations = AnnotationMirrors.getAnnotationValue(annotation,
+                    "indices")
+            return INDEX_LIST_VISITOR.visit(arrayOfIndexAnnotations, tableName)
+        }
+
+        private val INDEX_LIST_VISITOR = object
+            : SimpleAnnotationValueVisitor6<List<IndexInput>, String>() {
+            override fun visitArray(values: MutableList<out AnnotationValue>?, tableName: String)
+                    : List<IndexInput> {
+                return values?.map {
+                    INDEX_VISITOR.visit(it, tableName)
+                }?.filterNotNull() ?: emptyList<IndexInput>()
+            }
+        }
+
+        private val INDEX_VISITOR = object : SimpleAnnotationValueVisitor6<IndexInput?, String>() {
+            override fun visitAnnotation(a: AnnotationMirror?, tableName: String): IndexInput? {
+                val fieldInput = getAnnotationValue(a, "value").getAsStringList()
+                val unique = getAnnotationValue(a, "unique").getAsBoolean(false)
+                val nameValue = getAnnotationValue(a, "name")
+                        .getAsString("")
+                val name = if (nameValue == null || nameValue == "") {
+                    createIndexName(fieldInput, tableName)
+                } else {
+                    nameValue
+                }
+                return IndexInput(name, unique, fieldInput)
+            }
+        }
+
+        private fun createIndexName(columnNames: List<String>, tableName: String): String {
+            return "index_" + tableName + "_" + columnNames.joinToString("_")
+        }
+
+        private fun extractForeignKeys(annotation: AnnotationMirror): List<ForeignKeyInput> {
+            val arrayOfForeignKeyAnnotations = getAnnotationValue(annotation, "foreignKeys")
+            return FOREIGN_KEY_LIST_VISITOR.visit(arrayOfForeignKeyAnnotations)
+        }
+
+        private val FOREIGN_KEY_LIST_VISITOR = object
+            : SimpleAnnotationValueVisitor6<List<ForeignKeyInput>, Void?>() {
+            override fun visitArray(values: MutableList<out AnnotationValue>?, void: Void?)
+                    : List<ForeignKeyInput> {
+                return values?.map {
+                    FOREIGN_KEY_VISITOR.visit(it)
+                }?.filterNotNull() ?: emptyList<ForeignKeyInput>()
+            }
+        }
+
+        private val FOREIGN_KEY_VISITOR = object : SimpleAnnotationValueVisitor6<ForeignKeyInput?,
+                Void?>() {
+            override fun visitAnnotation(a: AnnotationMirror?, void: Void?): ForeignKeyInput? {
+                val entityClass = try {
+                    getAnnotationValue(a, "entity").toType()
+                } catch (notPresent: TypeNotPresentException) {
+                    return null
+                }
+                val parentColumns = getAnnotationValue(a, "parentColumns").getAsStringList()
+                val childColumns = getAnnotationValue(a, "childColumns").getAsStringList()
+                val onDeleteInput = getAnnotationValue(a, "onDelete").getAsInt()
+                val onUpdateInput = getAnnotationValue(a, "onUpdate").getAsInt()
+                val deferred = getAnnotationValue(a, "deferred").getAsBoolean(true)
+                val onDelete = ForeignKeyAction.fromAnnotationValue(onDeleteInput)
+                val onUpdate = ForeignKeyAction.fromAnnotationValue(onUpdateInput)
+                return ForeignKeyInput(
+                        parent = entityClass,
+                        parentColumns = parentColumns,
+                        childColumns = childColumns,
+                        onDelete = onDelete,
+                        onUpdate = onUpdate,
+                        deferred = deferred)
+            }
+        }
+    }
+
+    /**
+     * processed Index annotation output
+     */
+    data class IndexInput(val name: String, val unique: Boolean, val columnNames: List<String>)
+
+    /**
+     * ForeignKey, before it is processed in the context of a database.
+     */
+    data class ForeignKeyInput(val parent : TypeMirror, val parentColumns : List<String>,
+                     val childColumns : List<String>, val onDelete : ForeignKeyAction?,
+                     val onUpdate : ForeignKeyAction?, val deferred : Boolean)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/FieldProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/FieldProcessor.kt
new file mode 100644
index 0000000..2d11588
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/FieldProcessor.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.ColumnInfo
+import android.arch.persistence.room.ext.getAsBoolean
+import android.arch.persistence.room.ext.getAsInt
+import android.arch.persistence.room.ext.getAsString
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.Field
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.Element
+import javax.lang.model.type.DeclaredType
+
+class FieldProcessor(baseContext: Context, val containing: DeclaredType, val element: Element,
+                     val bindingScope: BindingScope,
+                     // pass only if this is processed as a child of Embedded field
+                     val fieldParent: EmbeddedField?) {
+    val context = baseContext.fork(element)
+    fun process(): Field {
+        val member = context.processingEnv.typeUtils.asMemberOf(containing, element)
+        val type = TypeName.get(member)
+        val columnInfoAnnotation = MoreElements.getAnnotationMirror(element,
+                ColumnInfo::class.java)
+        val name = element.simpleName.toString()
+        val columnName: String
+        val affinity : SQLTypeAffinity?
+        val fieldPrefix = fieldParent?.prefix ?: ""
+        val indexed : Boolean
+        if (columnInfoAnnotation.isPresent) {
+            val nameInAnnotation = AnnotationMirrors
+                    .getAnnotationValue(columnInfoAnnotation.get(), "name")
+                    .getAsString(ColumnInfo.INHERIT_FIELD_NAME)
+            columnName = fieldPrefix + if (nameInAnnotation == ColumnInfo.INHERIT_FIELD_NAME) {
+                name
+            } else {
+                nameInAnnotation
+            }
+
+            affinity = try {
+                val userDefinedAffinity = AnnotationMirrors
+                        .getAnnotationValue(columnInfoAnnotation.get(), "typeAffinity")
+                        .getAsInt(ColumnInfo.UNDEFINED)!!
+                SQLTypeAffinity.fromAnnotationValue(userDefinedAffinity)
+            } catch (ex : NumberFormatException) {
+                null
+            }
+
+            indexed = AnnotationMirrors
+                    .getAnnotationValue(columnInfoAnnotation.get(), "index")
+                    .getAsBoolean(false)
+
+        } else {
+            columnName = fieldPrefix + name
+            affinity = null
+            indexed = false
+        }
+        context.checker.notBlank(columnName, element,
+                ProcessorErrors.COLUMN_NAME_CANNOT_BE_EMPTY)
+        context.checker.notUnbound(type, element,
+                ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS)
+
+        val field = Field(name = name,
+                type = member,
+                element = element,
+                columnName = columnName,
+                affinity = affinity,
+                parent = fieldParent,
+                indexed = indexed)
+
+        when (bindingScope) {
+            BindingScope.TWO_WAY -> {
+                val adapter = context.typeAdapterStore.findColumnTypeAdapter(field.type,
+                        field.affinity)
+                field.statementBinder = adapter
+                field.cursorValueReader = adapter
+                field.affinity = adapter?.typeAffinity ?: field.affinity
+                context.checker.check(adapter != null, field.element,
+                        ProcessorErrors.CANNOT_FIND_COLUMN_TYPE_ADAPTER)
+            }
+            BindingScope.BIND_TO_STMT -> {
+                field.statementBinder = context.typeAdapterStore
+                        .findStatementValueBinder(field.type, field.affinity)
+                context.checker.check(field.statementBinder != null, field.element,
+                        ProcessorErrors.CANNOT_FIND_STMT_BINDER)
+            }
+            BindingScope.READ_FROM_CURSOR -> {
+                field.cursorValueReader = context.typeAdapterStore
+                        .findCursorValueReader(field.type, field.affinity)
+                context.checker.check(field.cursorValueReader != null, field.element,
+                        ProcessorErrors.CANNOT_FIND_CURSOR_READER)
+            }
+        }
+        return field
+    }
+
+    /**
+     * Defines what we need to assign
+     */
+    enum class BindingScope {
+        TWO_WAY, // both bind and read.
+        BIND_TO_STMT, // just value to statement
+        READ_FROM_CURSOR // just cursor to value
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/InsertionMethodProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/InsertionMethodProcessor.kt
new file mode 100644
index 0000000..dba4a87
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/InsertionMethodProcessor.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+
+package android.arch.persistence.room.processor
+
+import android.support.annotation.VisibleForTesting
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.OnConflictStrategy.IGNORE
+import android.arch.persistence.room.OnConflictStrategy.REPLACE
+import android.arch.persistence.room.vo.InsertionMethod
+import android.arch.persistence.room.vo.InsertionMethod.Type
+import android.arch.persistence.room.vo.ShortcutQueryParameter
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeKind.LONG
+import javax.lang.model.type.TypeKind.VOID
+import javax.lang.model.type.TypeMirror
+
+class InsertionMethodProcessor(baseContext: Context,
+                               val containing: DeclaredType,
+                               val executableElement: ExecutableElement) {
+    val context = baseContext.fork(executableElement)
+    fun process(): InsertionMethod {
+        val delegate = ShortcutMethodProcessor(context, containing, executableElement)
+        val annotation = delegate.extractAnnotation(Insert::class,
+                ProcessorErrors.MISSING_INSERT_ANNOTATION)
+
+        val onConflict = OnConflictProcessor.extractFrom(annotation)
+        context.checker.check(onConflict <= IGNORE && onConflict >= REPLACE,
+                executableElement, ProcessorErrors.INVALID_ON_CONFLICT_VALUE)
+
+        val returnType = delegate.extractReturnType()
+        val returnTypeName = TypeName.get(returnType)
+        context.checker.notUnbound(returnTypeName, executableElement,
+                ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_INSERTION_METHODS)
+
+        val (entities, params) = delegate.extractParams(
+                missingParamError = ProcessorErrors
+                        .INSERTION_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT
+        )
+
+        // TODO we can support more types
+        var insertionType = getInsertionType(returnType)
+        context.checker.check(insertionType != null, executableElement,
+                ProcessorErrors.INVALID_INSERTION_METHOD_RETURN_TYPE)
+
+        if (insertionType != null) {
+            val acceptable = acceptableTypes(params)
+            if (insertionType !in acceptable) {
+                context.logger.e(executableElement,
+                        ProcessorErrors.insertionMethodReturnTypeMismatch(
+                                insertionType.returnTypeName,
+                                acceptable.map { it.returnTypeName }))
+                // clear it, no reason to generate code for it.
+                insertionType = null
+            }
+        }
+        return InsertionMethod(
+                element = executableElement,
+                name = executableElement.simpleName.toString(),
+                returnType = returnType,
+                entities = entities,
+                parameters = params,
+                onConflict = onConflict,
+                insertionType = insertionType
+        )
+    }
+
+    @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+    private fun getInsertionType(returnType: TypeMirror): InsertionMethod.Type? {
+        // TODO we need to support more types here.
+        fun isLongPrimitiveType(typeMirror: TypeMirror) = typeMirror.kind == LONG
+
+        fun isLongBoxType(typeMirror: TypeMirror) =
+                MoreTypes.isType(typeMirror) &&
+                        MoreTypes.isTypeOf(java.lang.Long::class.java, typeMirror)
+
+        fun isLongType(typeMirror: TypeMirror) =
+                isLongPrimitiveType(typeMirror) || isLongBoxType(typeMirror)
+
+        return if (returnType.kind == VOID) {
+            Type.INSERT_VOID
+        } else if (returnType.kind == TypeKind.ARRAY) {
+            val arrayType = MoreTypes.asArray(returnType)
+            val param = arrayType.componentType
+            if (isLongPrimitiveType(param)) {
+                Type.INSERT_ID_ARRAY
+            } else if (isLongBoxType(param)) {
+                Type.INSERT_ID_ARRAY_BOX
+            } else {
+                null
+            }
+        } else if (MoreTypes.isType(returnType)
+                && MoreTypes.isTypeOf(List::class.java, returnType)) {
+            val declared = MoreTypes.asDeclared(returnType)
+            val param = declared.typeArguments.first()
+            if (isLongBoxType(param)) {
+                Type.INSERT_ID_LIST
+            } else {
+                null
+            }
+        } else if (isLongType(returnType)) {
+            Type.INSERT_SINGLE_ID
+        } else {
+            null
+        }
+    }
+
+    companion object {
+        @VisibleForTesting
+        val VOID_SET by lazy { setOf(Type.INSERT_VOID) }
+        @VisibleForTesting
+        val SINGLE_ITEM_SET by lazy { setOf(Type.INSERT_VOID, Type.INSERT_SINGLE_ID) }
+        @VisibleForTesting
+        val MULTIPLE_ITEM_SET by lazy {
+            setOf(Type.INSERT_VOID, Type.INSERT_ID_ARRAY, Type.INSERT_ID_ARRAY_BOX,
+                    Type.INSERT_ID_LIST)
+        }
+        fun acceptableTypes(params : List<ShortcutQueryParameter>) : Set<InsertionMethod.Type> {
+            if (params.isEmpty()) {
+                return VOID_SET
+            }
+            if (params.size > 1) {
+                return VOID_SET
+            }
+            if (params.first().isMultiple) {
+                return MULTIPLE_ITEM_SET
+            } else {
+                return SINGLE_ITEM_SET
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/OnConflictProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/OnConflictProcessor.kt
new file mode 100644
index 0000000..ac5ed5b
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/OnConflictProcessor.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.OnConflictStrategy
+import com.google.auto.common.AnnotationMirrors
+import javax.lang.model.element.AnnotationMirror
+
+/**
+ * Processes on conflict fields in annotations
+ */
+object OnConflictProcessor {
+    val INVALID_ON_CONFLICT = -1
+
+    @OnConflictStrategy
+    fun extractFrom(annotation: AnnotationMirror?, fieldName: String = "onConflict"): Int {
+        return if (annotation == null) {
+            INVALID_ON_CONFLICT
+        } else {
+            try {
+                val onConflictValue = AnnotationMirrors
+                        .getAnnotationValue(annotation, fieldName)
+                        .value
+                onConflictValue.toString().toInt()
+            } catch (ex: NumberFormatException) {
+                INVALID_ON_CONFLICT
+            }
+        }
+    }
+
+    fun onConflictText(@OnConflictStrategy onConflict: Int): String {
+        return when (onConflict) {
+            OnConflictStrategy.REPLACE -> "REPLACE"
+            OnConflictStrategy.ABORT -> "ABORT"
+            OnConflictStrategy.FAIL -> "FAIL"
+            OnConflictStrategy.IGNORE -> "IGNORE"
+            OnConflictStrategy.ROLLBACK -> "ROLLBACK"
+            else -> "BAD_CONFLICT_CONSTRAINT"
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt
new file mode 100644
index 0000000..baa351d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/PojoProcessor.kt
@@ -0,0 +1,519 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Relation
+import android.arch.persistence.room.ColumnInfo
+import android.arch.persistence.room.Embedded
+import android.arch.persistence.room.Ignore
+import android.arch.persistence.room.ext.getAllFieldsIncludingPrivateSupers
+import android.arch.persistence.room.ext.getAnnotationValue
+import android.arch.persistence.room.ext.getAsString
+import android.arch.persistence.room.ext.getAsStringList
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.ext.hasAnyOf
+import android.arch.persistence.room.ext.isCollection
+import android.arch.persistence.room.ext.toClassType
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_TYPE
+import android.arch.persistence.room.processor.ProcessorErrors.POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
+import android.arch.persistence.room.processor.cache.Cache
+import android.arch.persistence.room.vo.CallType
+import android.arch.persistence.room.vo.Constructor
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.FieldGetter
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.FieldSetter
+import android.arch.persistence.room.vo.Pojo
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.Modifier.ABSTRACT
+import javax.lang.model.element.Modifier.PRIVATE
+import javax.lang.model.element.Modifier.PROTECTED
+import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.element.Modifier.STATIC
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.ElementFilter
+
+/**
+ * Processes any class as if it is a Pojo.
+ */
+class PojoProcessor(baseContext: Context, val element: TypeElement,
+                    val bindingScope: FieldProcessor.BindingScope,
+                    val parent: EmbeddedField?) {
+    val context = baseContext.fork(element)
+    companion object {
+        val PROCESSED_ANNOTATIONS = listOf(ColumnInfo::class, Embedded::class,
+                    Relation::class)
+    }
+    fun process() : Pojo {
+        return context.cache.pojos.get(Cache.PojoKey(element, bindingScope, parent), {
+            doProcess()
+        })
+    }
+
+    private fun doProcess(): Pojo {
+        // TODO handle recursion: b/35980205
+        val declaredType = MoreTypes.asDeclared(element.asType())
+        // TODO handle conflicts with super: b/35568142
+        val allFields = element.getAllFieldsIncludingPrivateSupers(context.processingEnv)
+                .filter {
+                    !it.hasAnnotation(Ignore::class) && !it.hasAnyOf(Modifier.STATIC)
+                }
+                .groupBy { field ->
+                    context.checker.check(
+                            PROCESSED_ANNOTATIONS.count { field.hasAnnotation(it) } < 2, field,
+                            ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION
+                    )
+                    if (field.hasAnnotation(Embedded::class)) {
+                        Embedded::class
+                    } else if (field.hasAnnotation(Relation::class)) {
+                        Relation::class
+                    } else {
+                        null
+                    }
+                }
+        val myFields = allFields[null]
+                ?.map {
+                    FieldProcessor(
+                            baseContext = context,
+                            containing = declaredType,
+                            element = it,
+                            bindingScope = bindingScope,
+                            fieldParent = parent).process()
+                } ?: emptyList()
+
+        val embeddedFields = allFields[Embedded::class]
+                ?.map {
+                    processEmbeddedField(declaredType, it)
+                } ?: emptyList()
+        val subFields = embeddedFields.flatMap { it.pojo.fields }
+
+        val fields = myFields + subFields
+
+        val myRelationsList = allFields[Relation::class]
+                ?.map {
+                    processRelationField(fields, declaredType, it)
+                }
+                ?.filterNotNull() ?: emptyList()
+
+        val subRelations = embeddedFields.flatMap { it.pojo.relations }
+
+        val relations = myRelationsList + subRelations
+
+        fields.groupBy { it.columnName }
+                .filter { it.value.size > 1 }
+                .forEach {
+                    context.logger.e(element, ProcessorErrors.pojoDuplicateFieldNames(
+                            it.key, it.value.map(Field::getPath)
+                    ))
+                    it.value.forEach {
+                        context.logger.e(it.element, POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME)
+                    }
+                }
+        val methods = MoreElements.getLocalAndInheritedMethods(element,
+                context.processingEnv.elementUtils)
+                .filter {
+                    !it.hasAnyOf(PRIVATE, ABSTRACT, STATIC)
+                            && !it.hasAnnotation(Ignore::class)
+                }
+                .map { MoreElements.asExecutable(it) }
+
+        val getterCandidates = methods.filter {
+            it.parameters.size == 0 && it.returnType.kind != TypeKind.VOID
+        }
+
+        val setterCandidates = methods.filter {
+            it.parameters.size == 1 && it.returnType.kind == TypeKind.VOID
+        }
+        // don't try to find a constructor for binding to statement.
+        val constructor = if (bindingScope == FieldProcessor.BindingScope.BIND_TO_STMT) {
+            // we don't need to construct this POJO.
+            null
+        } else {
+            chooseConstructor(myFields, embeddedFields)
+        }
+
+        assignGetters(myFields, getterCandidates)
+        assignSetters(myFields, setterCandidates, constructor)
+
+        embeddedFields.forEach {
+            assignGetter(it.field, getterCandidates)
+            assignSetter(it.field, setterCandidates, constructor)
+        }
+
+        myRelationsList.forEach {
+            assignGetter(it.field, getterCandidates)
+            assignSetter(it.field, setterCandidates, constructor)
+        }
+
+        val pojo = Pojo(element = element,
+                type = declaredType,
+                fields = fields,
+                embeddedFields = embeddedFields,
+                relations = relations,
+                constructor = constructor)
+        return pojo
+    }
+
+    private fun chooseConstructor(myFields: List<Field>, embedded: List<EmbeddedField>)
+            : Constructor? {
+        val constructors = ElementFilter.constructorsIn(element.enclosedElements)
+                .filterNot { it.hasAnnotation(Ignore::class) || it.hasAnyOf(PRIVATE) }
+        val fieldMap = myFields.associateBy { it.name }
+        val embeddedMap = embedded.associateBy { it.field.name }
+        val typeUtils = context.processingEnv.typeUtils
+        val failedConstructors = mutableMapOf<ExecutableElement, List<Constructor.Param?>>()
+        val goodConstructors = constructors.map { constructor ->
+            val params = constructor.parameters.map param@ { param ->
+                val paramName = param.simpleName.toString()
+                val paramType = param.asType()
+
+                val matches = fun(field: Field?): Boolean {
+                    return if (field == null) {
+                        false
+                    } else if (!field.nameWithVariations.contains(paramName)) {
+                        false
+                    } else {
+                        typeUtils.isAssignable(paramType, field.type)
+                    }
+                }
+
+                val exactFieldMatch = fieldMap[paramName]
+
+                if (matches(exactFieldMatch)) {
+                    return@param Constructor.FieldParam(exactFieldMatch!!)
+                }
+                val exactEmbeddedMatch = embeddedMap[paramName]
+                if (matches(exactEmbeddedMatch?.field)) {
+                    return@param Constructor.EmbeddedParam(exactEmbeddedMatch!!)
+                }
+
+                val matchingFields = myFields.filter {
+                    matches(it)
+                }
+                val embeddedMatches = embedded.filter {
+                    matches(it.field)
+                }
+                if (matchingFields.isEmpty() && embeddedMatches.isEmpty()) {
+                    null
+                } else if (matchingFields.size + embeddedMatches.size == 1) {
+                    if (matchingFields.isNotEmpty()) {
+                        Constructor.FieldParam(matchingFields.first())
+                    } else {
+                        Constructor.EmbeddedParam(embeddedMatches.first())
+                    }
+                } else {
+                    context.logger.e(param, ProcessorErrors.ambigiousConstructor(
+                            pojo = element.qualifiedName.toString(),
+                            paramName = param.simpleName.toString(),
+                            matchingFields = matchingFields.map { it.getPath() }
+                                    + embedded.map { it.field.getPath() }
+                    ))
+                    null
+                }
+            }
+            if (params.any { it == null }) {
+                failedConstructors.put(constructor, params)
+                null
+            } else {
+                @Suppress("UNCHECKED_CAST")
+                Constructor(constructor, params as List<Constructor.Param>)
+            }
+        }.filterNotNull()
+        if (goodConstructors.isEmpty()) {
+            if (failedConstructors.isNotEmpty()) {
+                val failureMsg = failedConstructors.entries.joinToString("\n") { entry ->
+                    val paramsMatching = entry.key.parameters.withIndex().joinToString(", ") {
+                        "${it.value.simpleName} : ${entry.value[it.index]?.log()}"
+                    }
+                    "${entry.key} : [$paramsMatching]"
+                }
+                context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR +
+                        "\nTried the following constructors but they failed to match:\n$failureMsg")
+            }
+            context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR)
+            return null
+        }
+        if (goodConstructors.size > 1) {
+            goodConstructors.forEach {
+                context.logger.e(it.element, ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS)
+            }
+            return null
+        }
+        return goodConstructors.first()
+    }
+
+    private fun processEmbeddedField(declaredType: DeclaredType?, it: Element): EmbeddedField {
+        val fieldPrefix = it.getAnnotationValue(Embedded::class.java, "prefix")
+                ?.toString() ?: ""
+        val inheritedPrefix = parent?.prefix ?: ""
+        val embeddedField = Field(
+                it,
+                it.simpleName.toString(),
+                type = context.processingEnv.typeUtils.asMemberOf(declaredType, it),
+                affinity = null,
+                parent = parent)
+        val subParent = EmbeddedField(
+                field = embeddedField,
+                prefix = inheritedPrefix + fieldPrefix,
+                parent = parent)
+        val asVariable = MoreElements.asVariable(it)
+        subParent.pojo = PojoProcessor(baseContext = context.fork(it),
+                element = MoreTypes.asTypeElement(asVariable.asType()),
+                bindingScope = bindingScope,
+                parent = subParent).process()
+        return subParent
+    }
+
+    private fun processRelationField(myFields : List<Field>, container: DeclaredType?,
+                                     relationElement: VariableElement)
+            : android.arch.persistence.room.vo.Relation? {
+        val annotation = MoreElements.getAnnotationMirror(relationElement, Relation::class.java)
+                .orNull()!!
+        val parentColumnInput = AnnotationMirrors.getAnnotationValue(annotation, "parentColumn")
+                .getAsString("") ?: ""
+
+        val parentField = myFields.firstOrNull {
+            it.columnName == parentColumnInput
+        }
+        if (parentField == null) {
+            context.logger.e(relationElement,
+                    ProcessorErrors.relationCannotFindParentEntityField(
+                            entityName = element.qualifiedName.toString(),
+                            columnName = parentColumnInput,
+                            availableColumns = myFields.map { it.columnName }))
+            return null
+        }
+        // parse it as an entity.
+        val asMember = MoreTypes
+                .asMemberOf(context.processingEnv.typeUtils, container, relationElement)
+        if (asMember.kind == TypeKind.ERROR) {
+            context.logger.e(ProcessorErrors.CANNOT_FIND_TYPE, element)
+            return null
+        }
+        val declared = MoreTypes.asDeclared(asMember)
+        if (!declared.isCollection()) {
+            context.logger.e(relationElement, ProcessorErrors.RELATION_NOT_COLLECTION)
+            return null
+        }
+        val typeArg = declared.typeArguments.first()
+        if (typeArg.kind == TypeKind.ERROR) {
+            context.logger.e(MoreTypes.asTypeElement(typeArg), CANNOT_FIND_TYPE)
+            return null
+        }
+        val typeArgElement = MoreTypes.asTypeElement(typeArg)
+        val entityClassInput = AnnotationMirrors
+                .getAnnotationValue(annotation, "entity").toClassType()
+        val pojo : Pojo
+        val entity : Entity
+        if (entityClassInput == null
+                || MoreTypes.isTypeOf(Any::class.java, entityClassInput)) {
+            entity = EntityProcessor(context, typeArgElement).process()
+            pojo = entity
+        } else {
+            entity = EntityProcessor(context, MoreTypes.asTypeElement(entityClassInput)).process()
+            pojo = PojoProcessor(baseContext = context,
+                    element = typeArgElement,
+                    bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                    parent = parent).process()
+        }
+        // now find the field in the entity.
+        val entityColumnInput = AnnotationMirrors.getAnnotationValue(annotation, "entityColumn")
+                .getAsString() ?: ""
+        val entityField = entity.fields.firstOrNull {
+            it.columnName == entityColumnInput
+        }
+
+        if (entityField == null) {
+            context.logger.e(relationElement,
+                    ProcessorErrors.relationCannotFindEntityField(
+                            entityName = entity.typeName.toString(),
+                            columnName = entityColumnInput,
+                            availableColumns = entity.fields.map { it.columnName }))
+            return null
+        }
+
+        val field = Field(
+                element = relationElement,
+                name = relationElement.simpleName.toString(),
+                type = context.processingEnv.typeUtils.asMemberOf(container, relationElement),
+                affinity = null,
+                parent = parent)
+
+        val projection = AnnotationMirrors.getAnnotationValue(annotation, "projection")
+                .getAsStringList()
+        if(projection.isNotEmpty()) {
+            val missingColumns = projection.filterNot { columnName ->
+                entity.fields.any { columnName == it.columnName }
+            }
+            if (missingColumns.isNotEmpty()) {
+                context.logger.e(relationElement,
+                        ProcessorErrors.relationBadProject(entity.typeName.toString(),
+                                missingColumns, entity.fields.map { it.columnName }))
+            }
+        }
+
+        // if types don't match, row adapter prints a warning
+        return android.arch.persistence.room.vo.Relation(
+                entity = entity,
+                pojo = pojo,
+                field = field,
+                parentField = parentField,
+                entityField = entityField,
+                projection = projection
+        )
+    }
+
+    private fun assignGetters(fields: List<Field>, getterCandidates: List<ExecutableElement>) {
+        fields.forEach { field ->
+            assignGetter(field, getterCandidates)
+        }
+    }
+
+    private fun assignGetter(field: Field, getterCandidates: List<ExecutableElement>) {
+        val success = chooseAssignment(field = field,
+                candidates = getterCandidates,
+                nameVariations = field.getterNameWithVariations,
+                getType = { method ->
+                    method.returnType
+                },
+                assignFromField = {
+                    field.getter = FieldGetter(
+                            name = field.name,
+                            type = field.type,
+                            callType = CallType.FIELD)
+                },
+                assignFromMethod = { match ->
+                    field.getter = FieldGetter(
+                            name = match.simpleName.toString(),
+                            type = match.returnType,
+                            callType = CallType.METHOD)
+                },
+                reportAmbiguity = { matching ->
+                    context.logger.e(field.element,
+                            ProcessorErrors.tooManyMatchingGetters(field, matching))
+                })
+        context.checker.check(success, field.element, CANNOT_FIND_GETTER_FOR_FIELD)
+    }
+
+    private fun assignSetters(fields: List<Field>, setterCandidates: List<ExecutableElement>,
+                              constructor : Constructor?) {
+        fields.forEach { field ->
+            assignSetter(field, setterCandidates, constructor)
+        }
+    }
+
+    private fun assignSetter(field: Field, setterCandidates: List<ExecutableElement>,
+                             constructor: Constructor?) {
+        if (constructor != null && constructor.hasField(field)) {
+            field.setter = FieldSetter(field.name, field.type, CallType.CONSTRUCTOR)
+            return
+        }
+        val success = chooseAssignment(field = field,
+                candidates = setterCandidates,
+                nameVariations = field.setterNameWithVariations,
+                getType = { method ->
+                    method.parameters.first().asType()
+                },
+                assignFromField = {
+                    field.setter = FieldSetter(
+                            name = field.name,
+                            type = field.type,
+                            callType = CallType.FIELD)
+                },
+                assignFromMethod = { match ->
+                    val paramType = match.parameters.first().asType()
+                    field.setter = FieldSetter(
+                            name = match.simpleName.toString(),
+                            type = paramType,
+                            callType = CallType.METHOD)
+                },
+                reportAmbiguity = { matching ->
+                    context.logger.e(field.element,
+                            ProcessorErrors.tooManyMatchingSetter(field, matching))
+                })
+        context.checker.check(success, field.element, CANNOT_FIND_SETTER_FOR_FIELD)
+    }
+
+    /**
+     * Finds a setter/getter from available list of methods.
+     * It returns true if assignment is successful, false otherwise.
+     * At worst case, it sets to the field as if it is accessible so that the rest of the
+     * compilation can continue.
+     */
+    private fun chooseAssignment(field: Field, candidates: List<ExecutableElement>,
+                                 nameVariations: List<String>,
+                                 getType: (ExecutableElement) -> TypeMirror,
+                                 assignFromField: () -> Unit,
+                                 assignFromMethod: (ExecutableElement) -> Unit,
+                                 reportAmbiguity: (List<String>) -> Unit): Boolean {
+        if (field.element.hasAnyOf(PUBLIC)) {
+            assignFromField()
+            return true
+        }
+        val types = context.processingEnv.typeUtils
+
+        val matching = candidates
+                .filter {
+                    types.isAssignable(getType(it), field.element.asType())
+                            && (field.nameWithVariations.contains(it.simpleName.toString())
+                            || nameVariations.contains(it.simpleName.toString()))
+                }
+                .groupBy {
+                    if (it.hasAnyOf(PUBLIC)) PUBLIC else PROTECTED
+                }
+        if (matching.isEmpty()) {
+            // we always assign to avoid NPEs in the rest of the compilation.
+            assignFromField()
+            // if field is not private, assume it works (if we are on the same package).
+            // if not, compiler will tell, we didn't have any better alternative anyways.
+            return !field.element.hasAnyOf(PRIVATE)
+        }
+        val match = verifyAndChooseOneFrom(matching[PUBLIC], reportAmbiguity) ?:
+                verifyAndChooseOneFrom(matching[PROTECTED], reportAmbiguity)
+        if (match == null) {
+            assignFromField()
+            return false
+        } else {
+            assignFromMethod(match)
+            return true
+        }
+    }
+
+    private fun verifyAndChooseOneFrom(candidates: List<ExecutableElement>?,
+                                       reportAmbiguity: (List<String>) -> Unit)
+            : ExecutableElement? {
+        if (candidates == null) {
+            return null
+        }
+        if (candidates.size > 1) {
+            reportAmbiguity(candidates.map { it.simpleName.toString() })
+        }
+        return candidates.first()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
new file mode 100644
index 0000000..fc83b30
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
@@ -0,0 +1,443 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Delete
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.Update
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.vo.CustomTypeConverter
+import android.arch.persistence.room.vo.Field
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+
+object ProcessorErrors {
+    private fun String.trim(): String {
+        return this.trimIndent().replace(System.lineSeparator(), " ")
+    }
+    val MISSING_QUERY_ANNOTATION = "Query methods must be annotated with ${Query::class.java}"
+    val MISSING_INSERT_ANNOTATION = "Insertion methods must be annotated with ${Insert::class.java}"
+    val MISSING_DELETE_ANNOTATION = "Deletion methods must be annotated with ${Delete::class.java}"
+    val MISSING_UPDATE_ANNOTATION = "Update methods must be annotated with ${Update::class.java}"
+    val INVALID_ON_CONFLICT_VALUE = "On conflict value must be one of @OnConflictStrategy values."
+    val INVALID_INSERTION_METHOD_RETURN_TYPE = "Methods annotated with @Insert can return either" +
+            " void, long, Long, long[], Long[] or List<Long>."
+
+    fun insertionMethodReturnTypeMismatch(definedReturn : TypeName,
+                                          expectedReturnTypes : List<TypeName>) : String {
+        return "Method returns $definedReturn but it should return one of the following: `" +
+                expectedReturnTypes.joinToString(", ") + "`. If you want to return the list of" +
+                " row ids from the query, your insertion method can receive only 1 parameter."
+    }
+
+    val ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION = "Abstract method in DAO must be annotated" +
+            " with ${Query::class.java} AND ${Insert::class.java}"
+    val CANNOT_USE_MORE_THAN_ONE_DAO_METHOD_ANNOTATION = "A DAO method can be annotated with only" +
+            " one of the following:" + DaoProcessor.PROCESSED_ANNOTATIONS.joinToString(",") {
+        it.java.simpleName
+    }
+    val CANNOT_RESOLVE_RETURN_TYPE = "Cannot resolve return type for %s"
+    val CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS = "Cannot use unbound generics in query" +
+            " methods. It must be bound to a type through base Dao class."
+    val CANNOT_USE_UNBOUND_GENERICS_IN_INSERTION_METHODS = "Cannot use unbound generics in" +
+            " insertion methods. It must be bound to a type through base Dao class."
+    val CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS = "Cannot use unbound fields in entities."
+    val CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES = "Cannot use unbound generics in Dao classes." +
+            " If you are trying to create a base DAO, create a normal class, extend it with type" +
+            " params then mark the subclass with @Dao."
+    val CANNOT_FIND_GETTER_FOR_FIELD = "Cannot find getter for field."
+    val CANNOT_FIND_SETTER_FOR_FIELD = "Cannot find setter for field."
+    val MISSING_PRIMARY_KEY = "An entity must have at least 1 field annotated with @PrimaryKey"
+    val AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT = "If a primary key is annotated with" +
+            " autoGenerate, its type must be int, Integer, long or Long."
+    val AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS = "When @PrimaryKey annotation is used on a" +
+            " field annotated with @Embedded, the embedded class should have only 1 field."
+
+    fun multiplePrimaryKeyAnnotations(primaryKeys: List<String>): String {
+        return """
+                You cannot have multiple primary keys defined in an Entity. If you
+                want to declare a composite primary key, you should use @Entity#primaryKeys and
+                not use @PrimaryKey. Defined Primary Keys:
+                ${primaryKeys.joinToString(", ")}""".trim()
+    }
+
+    fun primaryKeyColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
+        return "$columnName referenced in the primary key does not exists in the Entity." +
+                " Available column names:${allColumns.joinToString(", ")}"
+    }
+
+    val DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE = "Dao class must be an abstract class or" +
+            " an interface"
+    val DATABASE_MUST_BE_ANNOTATED_WITH_DATABASE = "Database must be annotated with @Database"
+    val DAO_MUST_BE_ANNOTATED_WITH_DAO = "Dao class must be annotated with @Dao"
+
+    fun daoMustHaveMatchingConstructor(daoName: String, dbName: String): String {
+        return """
+                $daoName needs to have either an empty constructor or a constructor that takes
+                $dbName as its only parameter.
+                """.trim()
+    }
+
+    val ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY = "Entity class must be annotated with @Entity"
+    val DATABASE_ANNOTATION_MUST_HAVE_LIST_OF_ENTITIES = "@Database annotation must specify list" +
+            " of entities"
+    val COLUMN_NAME_CANNOT_BE_EMPTY = "Column name cannot be blank. If you don't want to set it" +
+            ", just remove the @ColumnInfo annotation or use @ColumnInfo.INHERIT_FIELD_NAME."
+
+    val ENTITY_TABLE_NAME_CANNOT_BE_EMPTY = "Entity table name cannot be blank. If you don't want" +
+            " to set it, just remove the tableName property."
+
+    val CANNOT_BIND_QUERY_PARAMETER_INTO_STMT = "Query method parameters should either be a" +
+            " type that can be converted into a database column or a List / Array that contains" +
+            " such type. You can consider adding a Type Adapter for this."
+
+    val QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE = "Query/Insert method parameters cannot " +
+            "start with underscore (_)."
+
+    val CANNOT_FIND_QUERY_RESULT_ADAPTER = "Not sure how to convert a Cursor to this method's " +
+            "return type"
+
+    val INSERTION_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT = "Method annotated with" +
+            " @Insert but does not have any parameters to insert."
+
+    val DELETION_MISSING_PARAMS = "Method annotated with" +
+            " @Delete but does not have any parameters to delete."
+
+    val UPDATE_MISSING_PARAMS = "Method annotated with" +
+            " @Update but does not have any parameters to update."
+
+    val CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER = "Type of the parameter must be a class " +
+            "annotated with @Entity or a collection/array of it."
+
+    val DB_MUST_EXTEND_ROOM_DB = "Classes annotated with @Database should extend " +
+            RoomTypeNames.ROOM_DB
+
+    val LIVE_DATA_QUERY_WITHOUT_SELECT = "LiveData return type can only be used with SELECT" +
+            " queries."
+
+    private val TOO_MANY_MATCHING_GETTERS = "Ambiguous getter for %s. All of the following " +
+            "match: %s. You can @Ignore the ones that you don't want to match."
+
+    fun tooManyMatchingGetters(field: Field, methodNames: List<String>): String {
+        return TOO_MANY_MATCHING_GETTERS.format(field, methodNames.joinToString(", "))
+    }
+
+    private val TOO_MANY_MATCHING_SETTERS = "Ambiguous setter for %s. All of the following " +
+            "match: %s. You can @Ignore the ones that you don't want to match."
+
+    fun tooManyMatchingSetter(field: Field, methodNames: List<String>): String {
+        return TOO_MANY_MATCHING_SETTERS.format(field, methodNames.joinToString(", "))
+    }
+
+    val CANNOT_FIND_COLUMN_TYPE_ADAPTER = "Cannot figure out how to save this field into" +
+            " database. You can consider adding a type converter for it."
+
+    val CANNOT_FIND_STMT_BINDER = "Cannot figure out how to bind this field into a statement."
+
+    val CANNOT_FIND_CURSOR_READER = "Cannot figure out how to read this field from a cursor."
+
+    private val MISSING_PARAMETER_FOR_BIND = "Each bind variable in the query must have a" +
+            " matching method parameter. Cannot find method parameters for %s."
+
+    fun missingParameterForBindVariable(bindVarName: List<String>): String {
+        return MISSING_PARAMETER_FOR_BIND.format(bindVarName.joinToString(", "))
+    }
+
+    private val UNUSED_QUERY_METHOD_PARAMETER = "Unused parameter%s: %s"
+    fun unusedQueryMethodParameter(unusedParams: List<String>): String {
+        return UNUSED_QUERY_METHOD_PARAMETER.format(
+                if (unusedParams.size > 1) "s" else "",
+                unusedParams.joinToString(","))
+    }
+
+    private val DUPLICATE_TABLES = "Table name \"%s\" is used by multiple entities: %s"
+    fun duplicateTableNames(tableName: String, entityNames: List<String>): String {
+        return DUPLICATE_TABLES.format(tableName, entityNames.joinToString(", "))
+    }
+
+    val DELETION_METHODS_MUST_RETURN_VOID_OR_INT = "Deletion methods must either return void or" +
+            " return int (the number of deleted rows)."
+
+    val UPDATE_METHODS_MUST_RETURN_VOID_OR_INT = "Update methods must either return void or" +
+            " return int (the number of updated rows)."
+
+    val DAO_METHOD_CONFLICTS_WITH_OTHERS = "Dao method has conflicts."
+
+    fun duplicateDao(dao: TypeName, methodNames: List<String>): String {
+        return """
+                All of these functions [${methodNames.joinToString(", ")}] return the same DAO
+                class [$dao].
+                A database can use a DAO only once so you should remove ${methodNames.size - 1} of
+                these conflicting DAO methods. If you are implementing any of these to fulfill an
+                interface, don't make it abstract, instead, implement the code that calls the
+                other one.
+                """.trim()
+    }
+
+    fun cursorPojoMismatch(pojoTypeName: TypeName,
+                           unusedColumns: List<String>, allColumns: List<String>,
+                           unusedFields: List<Field>, allFields: List<Field>): String {
+        val unusedColumnsWarning = if (unusedColumns.isNotEmpty()) {
+            """
+                The query returns some columns [${unusedColumns.joinToString(", ")}] which are not
+                use by $pojoTypeName. You can use @ColumnInfo annotation on the fields to specify
+                the mapping.
+            """.trim()
+        } else {
+            ""
+        }
+        val unusedFieldsWarning = if (unusedFields.isNotEmpty()) {
+            """
+                $pojoTypeName has some fields
+                [${unusedFields.joinToString(", ") { it.columnName }}] which are not returned by the
+                query. If they are not supposed to be read from the result, you can mark them with
+                @Ignore annotation.
+            """.trim()
+        } else {
+            ""
+        }
+        return """
+            $unusedColumnsWarning
+            $unusedFieldsWarning
+            You can suppress this warning by annotating the method with
+            @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH).
+            Columns returned by the query: ${allColumns.joinToString(", ")}.
+            Fields in $pojoTypeName: ${allFields.joinToString(", ") { it.columnName }}.
+            """.trim()
+    }
+
+    val TYPE_CONVERTER_UNBOUND_GENERIC = "Cannot use unbound generics in Type Converters."
+    val TYPE_CONVERTER_BAD_RETURN_TYPE = "Invalid return type for a type converter."
+    val TYPE_CONVERTER_MUST_RECEIVE_1_PARAM = "Type converters must receive 1 parameter."
+    val TYPE_CONVERTER_EMPTY_CLASS = "Class is referenced as a converter but it does not have any" +
+            " converter methods."
+    val TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR = "Classes that are used as TypeConverters must" +
+            " have no-argument public constructors."
+    val TYPE_CONVERTER_MUST_BE_PUBLIC = "Type converters must be public."
+
+    fun duplicateTypeConverters(converters: List<CustomTypeConverter>): String {
+        return "Multiple methods define the same conversion. Conflicts with these:" +
+                " ${converters.joinToString(", ") { it.toString() }}"
+    }
+
+    // TODO must print field paths.
+    val POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME = "Field has non-unique column name."
+
+    fun pojoDuplicateFieldNames(columnName: String, fieldPaths: List<String>): String {
+        return "Multiple fields have the same columnName: $columnName." +
+                " Field names: ${fieldPaths.joinToString(", ")}."
+    }
+
+    fun embeddedPrimaryKeyIsDropped(entityQName: String, fieldName: String): String {
+        return "Primary key constraint on $fieldName is ignored when being merged into " +
+                entityQName
+    }
+
+    val INDEX_COLUMNS_CANNOT_BE_EMPTY = "List of columns in an index cannot be empty"
+
+    fun indexColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
+        return "$columnName referenced in the index does not exists in the Entity." +
+                " Available column names:${allColumns.joinToString(", ")}"
+    }
+
+    fun duplicateIndexInEntity(indexName: String): String {
+        return "There are multiple indices with name $indexName. This happen if you've declared" +
+                " the same index multiple times or different indices have the same name. See" +
+                " @Index documentation for details."
+    }
+
+    fun duplicateIndexInDatabase(indexName: String, indexPaths: List<String>): String {
+        return "There are multiple indices with name $indexName. You should rename " +
+                "${indexPaths.size - 1} of these to avoid the conflict:" +
+                "${indexPaths.joinToString(", ")}."
+    }
+
+    fun droppedEmbeddedFieldIndex(fieldPath: String, grandParent: String): String {
+        return "The index will be dropped when being merged into $grandParent" +
+                "($fieldPath). You must re-declare it in $grandParent if you want to index this" +
+                " field in $grandParent."
+    }
+
+    fun droppedEmbeddedIndex(entityName: String, fieldPath: String, grandParent: String)
+            : String {
+        return "Indices defined in $entityName will be dropped when it is merged into" +
+                " $grandParent ($fieldPath). You can re-declare them in $grandParent."
+    }
+
+    fun droppedSuperClassIndex(childEntity: String, superEntity: String): String {
+        return "Indices defined in $superEntity will NOT be re-used in $childEntity. If you want" +
+                " to inherit them, you must re-declare them in $childEntity." +
+                " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation."
+    }
+
+    fun droppedSuperClassFieldIndex(fieldName: String, childEntity: String,
+                                    superEntity: String): String {
+        return "Index defined on field `$fieldName` in $superEntity will NOT be re-used in" +
+                " $childEntity. " +
+                "If you want to inherit it, you must re-declare it in $childEntity." +
+                " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation."
+    }
+
+    val RELATION_NOT_COLLECTION = "Fields annotated with @Relation must be a List or Set."
+
+    fun relationCannotFindEntityField(entityName : String, columnName: String,
+                                      availableColumns: List<String>) : String {
+        return "Cannot find the child entity column `$columnName` in $entityName." +
+                " Options: ${availableColumns.joinToString(", ")}"
+    }
+
+    fun relationCannotFindParentEntityField(entityName : String, columnName: String,
+                                            availableColumns: List<String>) : String {
+        return "Cannot find the parent entity column `$columnName` in $entityName." +
+                " Options: ${availableColumns.joinToString(", ")}"
+    }
+
+    val RELATION_IN_ENTITY = "Entities cannot have relations."
+
+    val CANNOT_FIND_TYPE = "Cannot find type."
+
+    fun relationAffinityMismatch(parentColumn: String, childColumn: String,
+                                 parentAffinity : SQLTypeAffinity?,
+                                 childAffinity : SQLTypeAffinity?) : String {
+        return """
+        The affinity of parent column ($parentColumn : $parentAffinity) does not match the type
+        affinity of the child column ($childColumn : $childAffinity).
+        """.trim()
+    }
+
+    val CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION = "A field can be annotated with only" +
+            " one of the following:" + PojoProcessor.PROCESSED_ANNOTATIONS.joinToString(",") {
+        it.java.simpleName
+    }
+
+    fun relationBadProject(entityQName : String, missingColumnNames : List<String>,
+                           availableColumnNames : List<String>) : String {
+        return """
+        $entityQName does not have the following columns: ${missingColumnNames.joinToString(",")}.
+        Available columns are: ${availableColumnNames.joinToString(",")}
+        """.trim()
+    }
+
+    val MISSING_SCHEMA_EXPORT_DIRECTORY = "Schema export directory is not provided to the" +
+            " annotation processor so we cannot export the schema. You can either provide" +
+            " `room.schemaLocation` annotation processor argument OR set exportSchema to false."
+
+    val INVALID_FOREIGN_KEY_ACTION = "Invalid foreign key action. It must be one of the constants" +
+            " defined in ForeignKey.Action"
+
+    fun foreignKeyNotAnEntity(className : String) : String {
+        return """
+        Classes referenced in Foreign Key annotations must be @Entity classes. $className is not
+        an entity
+        """.trim()
+    }
+
+    val FOREIGN_KEY_CANNOT_FIND_PARENT = "Cannot find parent entity class."
+
+    fun foreignKeyChildColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
+        return "($columnName) referenced in the foreign key does not exists in the Entity." +
+                " Available column names:${allColumns.joinToString(", ")}"
+    }
+
+    fun foreignKeyParentColumnDoesNotExist(parentEntity : String,
+                                           missingColumn: String,
+                                           allColumns : List<String>): String {
+        return "($missingColumn) does not exist in $parentEntity. Available columns are" +
+                " ${allColumns.joinToString(",")}"
+    }
+
+    val FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST = "Must specify at least 1 column name for the child"
+
+    val FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST = "Must specify at least 1 column name for the parent"
+
+    fun foreignKeyColumnNumberMismatch(childColumns : List<String>, parentColumns : List<String>)
+            : String {
+        return """
+                Number of child columns in foreign key must match number of parent columns.
+                Child reference has ${childColumns.joinToString(",")} and parent reference has
+                ${parentColumns.joinToString(",")}
+               """.trim()
+    }
+
+    fun foreignKeyMissingParentEntityInDatabase(parentTable : String, childEntity : String)
+            : String {
+        return """
+                $parentTable table referenced in the foreign keys of $childEntity does not exist in
+                the database. Maybe you forgot to add the referenced entity in the entities list of
+                the @Database annotation?""".trim()
+    }
+
+    fun foreignKeyMissingIndexInParent(parentEntity : String, parentColumns: List<String>,
+                                       childEntity : String, childColumns: List<String>): String {
+        return """
+                $childEntity has a foreign key (${childColumns.joinToString(",")}) that references
+                $parentEntity (${parentColumns.joinToString(",")}) but $parentEntity does not have
+                a unique index on those columns nor the columns are its primary key.
+                SQLite requires having a unique constraint on referenced parent columns so you must
+                add a unique index to $parentEntity that has
+                (${parentColumns.joinToString(",")}) column(s).
+               """.trim()
+    }
+
+    fun foreignKeyMissingIndexInChildColumns(childColumns: List<String>) : String {
+        return """
+                (${childColumns.joinToString(",")}) column(s) reference a foreign key but
+                they are not part of an index. This may trigger full table scans whenever parent
+                table is modified so you are highly advised to create an index that covers these
+                columns.
+               """.trim()
+    }
+
+    fun foreignKeyMissingIndexInChildColumn(childColumn: String) : String {
+        return """
+                $childColumn column references a foreign key but it is not part of an index. This
+                may trigger full table scans whenever parent table is modified so you are highly
+                advised to create an index that covers this column.
+               """.trim()
+    }
+
+    fun shortcutEntityIsNotInDatabase(database : String, dao : String, entity : String) : String {
+        return """
+                $dao is part of $database but this entity is not in the database. Maybe you forgot
+                to add $entity to the entities section of the @Database?
+                """.trim()
+    }
+    val MISSING_ROOM_RXJAVA2_ARTIFACT = "To use RxJava2 features, you must add `rxjava2`" +
+            " artifact from Room as a dependency. android.arch.persistence.room:rxjava2:<version>"
+
+    fun ambigiousConstructor(pojo : String, paramName:String, matchingFields : List<String>)
+            : String {
+        return """
+            Ambiguous constructor. The parameter ($paramName) in $pojo matches multiple fields:
+            [${matchingFields.joinToString(",")}]. If you don't want to use this constructor,
+            you can annotate it with @Ignore. If you want Room to use this constructor, you can
+            rename the parameters to exactly match the field name to fix the ambiguity.
+            """.trim()
+    }
+
+    val MISSING_POJO_CONSTRUCTOR = """
+            Entities and Pojos must have a usable public constructor. You can have an empty
+            constructor or a constructor whose parameters match the fields (by name and type).
+            """.trim()
+
+    val TOO_MANY_POJO_CONSTRUCTORS = """
+            Room cannot pick a constructor since multiple constructors are suitable. Try to annotate
+            unwanted constructors with @Ignore.
+            """.trim()
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/QueryMethodProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/QueryMethodProcessor.kt
new file mode 100644
index 0000000..fc60074
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/QueryMethodProcessor.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.SkipQueryVerification
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.parser.QueryType
+import android.arch.persistence.room.parser.SqlParser
+import android.arch.persistence.room.solver.query.result.LiveDataQueryResultBinder
+import android.arch.persistence.room.verifier.DatabaseVerificaitonErrors
+import android.arch.persistence.room.verifier.DatabaseVerifier
+import android.arch.persistence.room.vo.QueryMethod
+import android.arch.persistence.room.vo.QueryParameter
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeKind
+
+class QueryMethodProcessor(baseContext: Context,
+                           val containing: DeclaredType,
+                           val executableElement: ExecutableElement,
+                           val dbVerifier: DatabaseVerifier? = null) {
+    val context = baseContext.fork(executableElement)
+
+    fun process(): QueryMethod {
+        val asMember = context.processingEnv.typeUtils.asMemberOf(containing, executableElement)
+        val executableType = MoreTypes.asExecutable(asMember)
+
+        val annotation = MoreElements.getAnnotationMirror(executableElement,
+                Query::class.java).orNull()
+        context.checker.check(annotation != null, executableElement,
+                ProcessorErrors.MISSING_QUERY_ANNOTATION)
+
+        val query = if (annotation != null) {
+            val query = SqlParser.parse(
+                    AnnotationMirrors.getAnnotationValue(annotation, "value").value.toString())
+            context.checker.check(query.errors.isEmpty(), executableElement,
+                    query.errors.joinToString("\n"))
+            if (!executableElement.hasAnnotation(SkipQueryVerification::class)) {
+                query.resultInfo = dbVerifier?.analyze(query.original)
+            }
+            if (query.resultInfo?.error != null) {
+                context.logger.e(executableElement,
+                        DatabaseVerificaitonErrors.cannotVerifyQuery(query.resultInfo!!.error!!))
+            }
+
+            context.checker.check(executableType.returnType.kind != TypeKind.ERROR,
+                    executableElement, ProcessorErrors.CANNOT_RESOLVE_RETURN_TYPE,
+                    executableElement)
+            query
+        } else {
+            ParsedQuery.MISSING
+        }
+
+        val returnTypeName = TypeName.get(executableType.returnType)
+        context.checker.notUnbound(returnTypeName, executableElement,
+                ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS)
+
+        if (query.type == QueryType.DELETE) {
+            context.checker.check(
+                    returnTypeName == TypeName.VOID || returnTypeName == TypeName.INT,
+                    executableElement,
+                    ProcessorErrors.DELETION_METHODS_MUST_RETURN_VOID_OR_INT
+            )
+        }
+        val resultBinder = context.typeAdapterStore
+                .findQueryResultBinder(executableType.returnType, query)
+        context.checker.check(resultBinder.adapter != null || query.type != QueryType.SELECT,
+                executableElement, ProcessorErrors.CANNOT_FIND_QUERY_RESULT_ADAPTER)
+        if (resultBinder is LiveDataQueryResultBinder) {
+            context.checker.check(query.type == QueryType.SELECT, executableElement,
+                    ProcessorErrors.LIVE_DATA_QUERY_WITHOUT_SELECT)
+        }
+
+        val queryMethod = QueryMethod(
+                element = executableElement,
+                query = query,
+                name = executableElement.simpleName.toString(),
+                returnType = executableType.returnType,
+                parameters = executableElement.parameters
+                        .map { QueryParameterProcessor(
+                                baseContext = context,
+                                containing = containing,
+                                element = it).process() },
+                queryResultBinder = resultBinder)
+
+        val missing = queryMethod.sectionToParamMapping
+                .filter { it.second == null }
+                .map { it.first.text }
+        if (missing.isNotEmpty()) {
+            context.logger.e(executableElement,
+                    ProcessorErrors.missingParameterForBindVariable(missing))
+        }
+
+        val unused = queryMethod.parameters.filterNot { param ->
+            queryMethod.sectionToParamMapping.any { it.second == param }
+        }.map(QueryParameter::name)
+
+        if (unused.isNotEmpty()) {
+            context.logger.e(executableElement, ProcessorErrors.unusedQueryMethodParameter(unused))
+        }
+        return queryMethod
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/QueryParameterProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/QueryParameterProcessor.kt
new file mode 100644
index 0000000..1fe3afe
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/QueryParameterProcessor.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.vo.QueryParameter
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.DeclaredType
+
+class QueryParameterProcessor(baseContext: Context,
+                              val containing: DeclaredType,
+                              val element: VariableElement) {
+    val context = baseContext.fork(element)
+    fun process(): QueryParameter {
+        val asMember = MoreTypes.asMemberOf(context.processingEnv.typeUtils, containing, element)
+        val parameterAdapter = context.typeAdapterStore.findQueryParameterAdapter(asMember)
+        context.checker.check(parameterAdapter != null, element,
+                ProcessorErrors.CANNOT_BIND_QUERY_PARAMETER_INTO_STMT)
+
+        val name = element.simpleName.toString()
+        context.checker.check(!name.startsWith("_"), element,
+                ProcessorErrors.QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE)
+        return QueryParameter(name = name,
+                type = asMember,
+                queryParamAdapter = parameterAdapter)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ShortcutMethodProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ShortcutMethodProcessor.kt
new file mode 100644
index 0000000..95d16f5
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ShortcutMethodProcessor.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.ShortcutQueryParameter
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeMirror
+import kotlin.reflect.KClass
+
+/**
+ * Common functionality for shortcut method processors
+ */
+class ShortcutMethodProcessor(baseContext : Context,
+                              val containing: DeclaredType,
+                              val executableElement: ExecutableElement) {
+    val context = baseContext.fork(executableElement)
+    private val asMember = context.processingEnv.typeUtils.asMemberOf(containing, executableElement)
+    private val executableType = MoreTypes.asExecutable(asMember)
+
+    fun extractAnnotation(klass : KClass<out Annotation>,
+                          errorMsg : String) : AnnotationMirror? {
+        val annotation = MoreElements.getAnnotationMirror(executableElement,
+                klass.java).orNull()
+        context.checker.check(annotation != null, executableElement, errorMsg)
+        return annotation
+    }
+
+    fun extractReturnType() : TypeMirror {
+        return executableType.returnType
+    }
+
+    fun extractParams(missingParamError: String)
+            : Pair<Map<String, Entity>, List<ShortcutQueryParameter>> {
+        val params = executableElement.parameters
+                .map { ShortcutParameterProcessor(
+                        baseContext = context,
+                        containing = containing,
+                        element = it).process() }
+        context.checker.check(params.isNotEmpty(), executableElement, missingParamError)
+        val entities = params
+                .filter { it.entityType != null }
+                .associateBy({ it.name }, {
+                    EntityProcessor(
+                            baseContext = context,
+                            element = MoreTypes.asTypeElement(it.entityType)).process()
+                })
+        return Pair(entities, params)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ShortcutParameterProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ShortcutParameterProcessor.kt
new file mode 100644
index 0000000..4f66920
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ShortcutParameterProcessor.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.vo.ShortcutQueryParameter
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.ArrayType
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.ElementFilter
+
+/**
+ * Processes parameters of methods that are annotated with Insert, Delete.
+ */
+class ShortcutParameterProcessor(baseContext : Context,
+                                 val containing: DeclaredType,
+                                 val element: VariableElement) {
+    val context = baseContext.fork(element)
+    fun process(): ShortcutQueryParameter {
+        val asMember = MoreTypes.asMemberOf(context.processingEnv.typeUtils, containing, element)
+        val name = element.simpleName.toString()
+        context.checker.check(!name.startsWith("_"), element,
+                ProcessorErrors.QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE)
+
+        val (entityType, isMultiple) = extractEntityType(asMember)
+        context.checker.check(entityType != null, element,
+                ProcessorErrors.CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER)
+
+        return ShortcutQueryParameter(
+                name = name,
+                type = asMember,
+                entityType = entityType,
+                isMultiple = isMultiple
+        )
+    }
+
+    @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+    fun extractEntityType(typeMirror: TypeMirror) : Pair<TypeMirror?, Boolean> {
+
+        val elementUtils = context.processingEnv.elementUtils
+        val typeUtils = context.processingEnv.typeUtils
+
+        fun verifyAndPair(entityType: TypeMirror, isMultiple : Boolean)
+                : Pair<TypeMirror?, Boolean> {
+            if (!MoreTypes.isType(entityType)) {
+                return Pair(null, isMultiple)
+            }
+            val entityElement = MoreTypes.asElement(entityType)
+            return if (entityElement.hasAnnotation(Entity::class)) {
+                Pair(entityType, isMultiple)
+            } else {
+                Pair(null, isMultiple)
+            }
+        }
+
+        fun extractEntityTypeFromIterator(iterableType: DeclaredType): TypeMirror {
+            ElementFilter.methodsIn(elementUtils
+                    .getAllMembers(typeUtils.asElement(iterableType) as TypeElement)).forEach {
+                if (it.simpleName.toString() == "iterator") {
+                    return MoreTypes.asDeclared(MoreTypes.asExecutable(
+                            typeUtils.asMemberOf(iterableType, it)).returnType)
+                            .typeArguments.first()
+                }
+            }
+            throw IllegalArgumentException("iterator() not found in Iterable $iterableType")
+        }
+
+        val iterableType = typeUtils.erasure(elementUtils
+                .getTypeElement("java.lang.Iterable").asType())
+        if (typeUtils.isAssignable(typeMirror, iterableType)) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            val entity = extractEntityTypeFromIterator(declared)
+            return verifyAndPair(entity, true)
+        }
+        if (typeMirror is ArrayType) {
+            val entity = typeMirror.componentType
+            return verifyAndPair(entity, true)
+        }
+        return verifyAndPair(typeMirror, false)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/SuppressWarningProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/SuppressWarningProcessor.kt
new file mode 100644
index 0000000..e2215d9
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/SuppressWarningProcessor.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.vo.Warning
+import com.google.auto.common.AnnotationMirrors
+import com.google.auto.common.MoreElements
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.Element
+import javax.lang.model.util.SimpleAnnotationValueVisitor6
+
+/**
+ * A visitor that reads SuppressWarnings annotations and keeps the ones we know about.
+ */
+object SuppressWarningProcessor {
+
+    fun getSuppressedWarnings(element: Element): Set<Warning> {
+        val annotation = MoreElements.getAnnotationMirror(element,
+                SuppressWarnings::class.java).orNull()
+        return if (annotation == null) {
+            emptySet<Warning>()
+        } else {
+            val value = AnnotationMirrors.getAnnotationValue(annotation, "value")
+            if (value == null) {
+                emptySet<Warning>()
+            } else {
+                VISITOR.visit(value)
+            }
+        }
+    }
+
+    private object VISITOR : SimpleAnnotationValueVisitor6<Set<Warning>, String>() {
+        override fun visitArray(values: List<AnnotationValue>?, elementName: String?)
+                : Set<Warning> {
+            return values?.map {
+                Warning.fromPublicKey(it.value.toString())
+            }?.filterNotNull()?.toSet() ?: emptySet()
+        }
+
+        override fun defaultAction(o: Any?, p: String?): Set<Warning> {
+            return emptySet()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/UpdateMethodProcessor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/UpdateMethodProcessor.kt
new file mode 100644
index 0000000..452ea4a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/UpdateMethodProcessor.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.OnConflictStrategy.IGNORE
+import android.arch.persistence.room.OnConflictStrategy.REPLACE
+import android.arch.persistence.room.Update
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.vo.UpdateMethod
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+
+class UpdateMethodProcessor(baseContext: Context,
+                              val containing: DeclaredType,
+                              val executableElement: ExecutableElement) {
+    val context = baseContext.fork(executableElement)
+
+    fun process(): UpdateMethod {
+        val delegate = ShortcutMethodProcessor(context, containing, executableElement)
+        val annotation = delegate
+                .extractAnnotation(Update::class, ProcessorErrors.MISSING_UPDATE_ANNOTATION)
+
+        val onConflict = OnConflictProcessor.extractFrom(annotation)
+        context.checker.check(onConflict <= IGNORE && onConflict >= REPLACE,
+                executableElement, ProcessorErrors.INVALID_ON_CONFLICT_VALUE)
+
+        val returnTypeName = delegate.extractReturnType().typeName()
+        context.checker.check(
+                returnTypeName == TypeName.VOID || returnTypeName == TypeName.INT,
+                executableElement,
+                ProcessorErrors.UPDATE_METHODS_MUST_RETURN_VOID_OR_INT
+        )
+
+        val (entities, params) = delegate.extractParams(
+                missingParamError = ProcessorErrors
+                        .UPDATE_MISSING_PARAMS
+        )
+
+        return UpdateMethod(
+                element = delegate.executableElement,
+                name = delegate.executableElement.simpleName.toString(),
+                entities = entities,
+                onConflictStrategy = onConflict,
+                returnCount = returnTypeName == TypeName.INT,
+                parameters = params
+        )
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/cache/Cache.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/cache/Cache.kt
new file mode 100644
index 0000000..37f6c6d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/cache/Cache.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("AddVarianceModifier")
+
+package android.arch.persistence.room.processor.cache
+
+import android.arch.persistence.room.processor.FieldProcessor
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.Pojo
+import android.arch.persistence.room.vo.Warning
+import java.util.LinkedHashSet
+import javax.lang.model.element.Element
+import javax.lang.model.type.TypeMirror
+
+/**
+ * A cache key can be used to avoid re-processing elements.
+ * <p>
+ * Each context has a cache variable that uses the same backing storage as the Root Context but
+ * adds current adapters and warning suppression list to the key.
+ */
+class Cache(val parent: Cache?, val converters: LinkedHashSet<TypeMirror>,
+            val suppressedWarnings: Set<Warning>) {
+    val entities: Bucket<EntityKey, Entity> = Bucket(parent?.entities)
+    val pojos: Bucket<PojoKey, Pojo> = Bucket(parent?.pojos)
+
+    inner class Bucket<K, T>(source: Bucket<K, T>?) {
+        private val entries: MutableMap<FullKey<K>, T> = source?.entries ?: mutableMapOf()
+        fun get(key : K, calculate: () -> T): T {
+            val fullKey = FullKey(converters, suppressedWarnings, key)
+            return entries.getOrPut(fullKey, {
+                calculate()
+            })
+        }
+    }
+
+    /**
+     * Key for Entity cache
+     */
+    data class EntityKey(val element: Element)
+
+    /**
+     * Key for Pojo cache
+     */
+    data class PojoKey(val element: Element, val scope : FieldProcessor.BindingScope,
+                       val parent : EmbeddedField?)
+
+    /**
+     * Internal key representation with adapters & warnings included.
+     * <p>
+     * Converters are kept in a linked set since the order is important for the TypeAdapterStore.
+     */
+    private data class FullKey<T>(val converters: LinkedHashSet<TypeMirror>,
+                               val suppressedWarnings: Set<Warning>,
+                               val key: T)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/CodeGenScope.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/CodeGenScope.kt
new file mode 100644
index 0000000..64e4f48
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/CodeGenScope.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver
+
+import com.google.common.annotations.VisibleForTesting
+import com.squareup.javapoet.CodeBlock
+import android.arch.persistence.room.writer.ClassWriter
+/**
+ * Defines a code generation scope where we can provide temporary variables, global variables etc
+ */
+class CodeGenScope(val writer : ClassWriter) {
+    private var tmpVarIndices = mutableMapOf<String, Int>()
+    private var builder : CodeBlock.Builder? = null
+    companion object {
+        const val TMP_VAR_DEFAULT_PREFIX = "_tmp"
+        const val CLASS_PROPERTY_PREFIX = "__"
+        @VisibleForTesting
+        fun _tmpVar(index:Int) = _tmpVar(TMP_VAR_DEFAULT_PREFIX, index)
+        fun _tmpVar(prefix : String, index:Int) = "$prefix${if(index == 0) "" else "_$index"}"
+    }
+
+    fun builder() : CodeBlock.Builder {
+        if (builder == null) {
+            builder = CodeBlock.builder()
+        }
+        return builder!!
+    }
+
+    fun getTmpVar() : String {
+        return getTmpVar(TMP_VAR_DEFAULT_PREFIX)
+    }
+
+    fun getTmpVar(prefix : String) : String {
+        if (!prefix.startsWith("_")) {
+            throw IllegalArgumentException("tmp variable prefixes should start with _")
+        }
+        if (prefix.startsWith(CLASS_PROPERTY_PREFIX)) {
+            throw IllegalArgumentException("cannot use $CLASS_PROPERTY_PREFIX for tmp variables")
+        }
+        val index = tmpVarIndices.getOrElse(prefix) { 0 }
+        val result = _tmpVar(prefix, index)
+        tmpVarIndices.put(prefix, index + 1)
+        return result
+    }
+
+    fun generate() = builder().build().toString()
+
+    /**
+     * copies all variable indices but excludes generated code.
+     */
+    fun fork() : CodeGenScope {
+        val forked = CodeGenScope(writer)
+        forked.tmpVarIndices.putAll(tmpVarIndices)
+        return forked
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/QueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/QueryResultBinderProvider.kt
new file mode 100644
index 0000000..b76be3e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/QueryResultBinderProvider.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver
+
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import javax.lang.model.type.DeclaredType
+
+interface QueryResultBinderProvider {
+    fun provide(declared : DeclaredType, query: ParsedQuery) : QueryResultBinder
+    fun matches(declared: DeclaredType) : Boolean
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..eca5986
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/TypeAdapterStore.kt
@@ -0,0 +1,448 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.EntityProcessor
+import android.arch.persistence.room.processor.FieldProcessor
+import android.arch.persistence.room.processor.PojoProcessor
+import android.arch.persistence.room.solver.binderprovider.CountedDataSourceQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.CursorQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.FlowableQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.InstantQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.LiveDataQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.LiveLazyListQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.RxMaybeQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.RxSingleQueryResultBinderProvider
+import android.arch.persistence.room.solver.query.parameter.ArrayQueryParameterAdapter
+import android.arch.persistence.room.solver.query.parameter.BasicQueryParameterAdapter
+import android.arch.persistence.room.solver.query.parameter.CollectionQueryParameterAdapter
+import android.arch.persistence.room.solver.query.parameter.QueryParameterAdapter
+import android.arch.persistence.room.solver.query.result.ArrayQueryResultAdapter
+import android.arch.persistence.room.solver.query.result.EntityRowAdapter
+import android.arch.persistence.room.solver.query.result.InstantQueryResultBinder
+import android.arch.persistence.room.solver.query.result.ListQueryResultAdapter
+import android.arch.persistence.room.solver.query.result.PojoRowAdapter
+import android.arch.persistence.room.solver.query.result.QueryResultAdapter
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import android.arch.persistence.room.solver.query.result.RowAdapter
+import android.arch.persistence.room.solver.query.result.SingleColumnRowAdapter
+import android.arch.persistence.room.solver.query.result.SingleEntityQueryResultAdapter
+import android.arch.persistence.room.solver.types.BoxedBooleanToBoxedIntConverter
+import android.arch.persistence.room.solver.types.BoxedPrimitiveColumnTypeAdapter
+import android.arch.persistence.room.solver.types.ByteArrayColumnTypeAdapter
+import android.arch.persistence.room.solver.types.ColumnTypeAdapter
+import android.arch.persistence.room.solver.types.CompositeAdapter
+import android.arch.persistence.room.solver.types.CompositeTypeConverter
+import android.arch.persistence.room.solver.types.CursorValueReader
+import android.arch.persistence.room.solver.types.NoOpConverter
+import android.arch.persistence.room.solver.types.PrimitiveBooleanToIntConverter
+import android.arch.persistence.room.solver.types.PrimitiveColumnTypeAdapter
+import android.arch.persistence.room.solver.types.StatementValueBinder
+import android.arch.persistence.room.solver.types.StringColumnTypeAdapter
+import android.arch.persistence.room.solver.types.TypeConverter
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.annotations.VisibleForTesting
+import java.util.LinkedList;
+import javax.lang.model.type.ArrayType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+/**
+ * Holds all type adapters and can create on demand composite type adapters to convert a type into a
+ * database column.
+ */
+class TypeAdapterStore private constructor(val context: Context,
+                                           /**
+                                            * first type adapter has the highest priority
+                                            */
+                                           private val columnTypeAdapters: List<ColumnTypeAdapter>,
+                                           /**
+                                            * first converter has the highest priority
+                                            */
+                                           private val typeConverters: List<TypeConverter>) {
+
+
+    companion object {
+        fun copy(context: Context, store: TypeAdapterStore): TypeAdapterStore {
+            return TypeAdapterStore(context = context,
+                    columnTypeAdapters = store.columnTypeAdapters,
+                    typeConverters = store.typeConverters)
+        }
+
+        fun create(context: Context, @VisibleForTesting vararg extras: Any): TypeAdapterStore {
+            val adapters = arrayListOf<ColumnTypeAdapter>()
+            val converters = arrayListOf<TypeConverter>()
+
+            fun addAny(extra: Any?) {
+                when (extra) {
+                    is TypeConverter -> converters.add(extra)
+                    is ColumnTypeAdapter -> adapters.add(extra)
+                    is List<*> -> extra.forEach(::addAny)
+                    else -> throw IllegalArgumentException("unknown extra $extra")
+                }
+            }
+
+            extras.forEach(::addAny)
+            fun addTypeConverter(converter: TypeConverter) {
+                converters.add(converter)
+            }
+
+            fun addColumnAdapter(adapter: ColumnTypeAdapter) {
+                adapters.add(adapter)
+            }
+
+            val primitives = PrimitiveColumnTypeAdapter
+                    .createPrimitiveAdapters(context.processingEnv)
+            primitives.forEach(::addColumnAdapter)
+            BoxedPrimitiveColumnTypeAdapter
+                    .createBoxedPrimitiveAdapters(context.processingEnv, primitives)
+                    .forEach(::addColumnAdapter)
+            addColumnAdapter(StringColumnTypeAdapter(context.processingEnv))
+            addColumnAdapter(ByteArrayColumnTypeAdapter(context.processingEnv))
+            PrimitiveBooleanToIntConverter.create(context.processingEnv).forEach(::addTypeConverter)
+            BoxedBooleanToBoxedIntConverter.create(context.processingEnv)
+                    .forEach(::addTypeConverter)
+            return TypeAdapterStore(context = context, columnTypeAdapters = adapters,
+                    typeConverters = converters)
+        }
+    }
+
+    val queryResultBinderProviders = listOf(
+            CursorQueryResultBinderProvider(context),
+            LiveDataQueryResultBinderProvider(context),
+            FlowableQueryResultBinderProvider(context),
+            RxMaybeQueryResultBinderProvider(context),
+            RxSingleQueryResultBinderProvider(context),
+            CountedDataSourceQueryResultBinderProvider(context),
+            LiveLazyListQueryResultBinderProvider(context),
+            InstantQueryResultBinderProvider(context)
+    )
+
+    // type mirrors that be converted into columns w/o an extra converter
+    private val knownColumnTypeMirrors by lazy {
+        columnTypeAdapters.map { it.out }
+    }
+
+    /**
+     * Searches 1 way to bind a value into a statement.
+     */
+    fun findStatementValueBinder(input: TypeMirror, affinity: SQLTypeAffinity?)
+            : StatementValueBinder? {
+        if (input.kind == TypeKind.ERROR) {
+            return null
+        }
+        val adapter = findDirectAdapterFor(input, affinity)
+        if (adapter != null) {
+            return adapter
+        }
+        val targetTypes = targetTypeMirrorsFor(affinity)
+        val binder = findTypeConverter(input, targetTypes) ?: return null
+        return CompositeAdapter(input, getAllColumnAdapters(binder.to).first(), binder, null)
+    }
+
+    /**
+     * Returns which entities targets the given affinity.
+     */
+    private fun targetTypeMirrorsFor(affinity: SQLTypeAffinity?): List<TypeMirror> {
+        val specifiedTargets = affinity?.getTypeMirrors(context.processingEnv)
+        return if (specifiedTargets == null || specifiedTargets.isEmpty()) {
+            knownColumnTypeMirrors
+        } else {
+            specifiedTargets
+        }
+    }
+
+    /**
+     * Searches 1 way to read it from cursor
+     */
+    fun findCursorValueReader(output: TypeMirror, affinity: SQLTypeAffinity?): CursorValueReader? {
+        if (output.kind == TypeKind.ERROR) {
+            return null
+        }
+        val adapter = findColumnTypeAdapter(output, affinity)
+        if (adapter != null) {
+            // two way is better
+            return adapter
+        }
+        // we could not find a two way version, search for anything
+        val targetTypes = targetTypeMirrorsFor(affinity)
+        val converter = findTypeConverter(targetTypes, output) ?: return null
+        return CompositeAdapter(output,
+                getAllColumnAdapters(converter.from).first(), null, converter)
+    }
+
+    /**
+     * Tries to reverse the converter going through the same nodes, if possible.
+     */
+    @VisibleForTesting
+    fun reverse(converter: TypeConverter): TypeConverter? {
+        return when (converter) {
+            is NoOpConverter -> converter
+            is CompositeTypeConverter -> {
+                val r1 = reverse(converter.conv1) ?: return null
+                val r2 = reverse(converter.conv2) ?: return null
+                CompositeTypeConverter(r2, r1)
+            }
+            else -> {
+                val types = context.processingEnv.typeUtils
+                typeConverters.firstOrNull {
+                    types.isSameType(it.from, converter.to) && types
+                            .isSameType(it.to, converter.from)
+                }
+            }
+        }
+    }
+
+    /**
+     * Finds a two way converter, if you need 1 way, use findStatementValueBinder or
+     * findCursorValueReader.
+     */
+    fun findColumnTypeAdapter(out: TypeMirror, affinity: SQLTypeAffinity?)
+            : ColumnTypeAdapter? {
+        if (out.kind == TypeKind.ERROR) {
+            return null
+        }
+        val adapter = findDirectAdapterFor(out, affinity)
+        if (adapter != null) {
+            return adapter
+        }
+        val targetTypes = targetTypeMirrorsFor(affinity)
+        val intoStatement = findTypeConverter(out, targetTypes) ?: return null
+        // ok found a converter, try the reverse now
+        val fromCursor = reverse(intoStatement) ?: findTypeConverter(intoStatement.to, out)
+                ?: return null
+        return CompositeAdapter(out, getAllColumnAdapters(intoStatement.to).first(), intoStatement,
+                fromCursor)
+    }
+
+    private fun findDirectAdapterFor(out: TypeMirror, affinity: SQLTypeAffinity?)
+            : ColumnTypeAdapter? {
+        val adapter = getAllColumnAdapters(out).firstOrNull {
+            affinity == null || it.typeAffinity == affinity
+        }
+        return adapter
+    }
+
+    fun findTypeConverter(input: TypeMirror, output: TypeMirror): TypeConverter? {
+        return findTypeConverter(listOf(input), listOf(output))
+    }
+
+    fun findQueryResultBinder(typeMirror: TypeMirror, query: ParsedQuery): QueryResultBinder {
+        return if (typeMirror.kind == TypeKind.DECLARED) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            return queryResultBinderProviders.first {
+                it.matches(declared)
+            }.provide(declared, query)
+        } else {
+            InstantQueryResultBinder(findQueryResultAdapter(typeMirror, query))
+        }
+    }
+
+    fun findQueryResultAdapter(typeMirror: TypeMirror, query: ParsedQuery)
+            : QueryResultAdapter? {
+        if (typeMirror.kind == TypeKind.ERROR) {
+            return null
+        }
+        if (typeMirror.kind == TypeKind.DECLARED) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            if (declared.typeArguments.isEmpty()) {
+                val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
+                return SingleEntityQueryResultAdapter(rowAdapter)
+            }
+            if (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)) {
+                val typeArg = declared.typeArguments.first()
+                val rowAdapter = findRowAdapter(typeArg, query) ?: return null
+                return ListQueryResultAdapter(rowAdapter)
+            }
+            return null
+        } else if (typeMirror is ArrayType && typeMirror.componentType.kind != TypeKind.BYTE) {
+            val rowAdapter =
+                    findRowAdapter(typeMirror.componentType, query) ?: return null
+            return ArrayQueryResultAdapter(rowAdapter)
+        } else {
+            val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
+            return SingleEntityQueryResultAdapter(rowAdapter)
+        }
+    }
+
+    /**
+     * Find a converter from cursor to the given type mirror.
+     * If there is information about the query result, we try to use it to accept *any* POJO.
+     */
+    @VisibleForTesting
+    fun findRowAdapter(typeMirror: TypeMirror, query: ParsedQuery): RowAdapter? {
+        if (typeMirror.kind == TypeKind.ERROR) {
+            return null
+        }
+        if (typeMirror.kind == TypeKind.DECLARED) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            if (declared.typeArguments.isNotEmpty()) {
+                // TODO one day support this
+                return null
+            }
+            val resultInfo = query.resultInfo
+
+            val (rowAdapter, rowAdapterLogs) = if (resultInfo != null && query.errors.isEmpty()
+                    && resultInfo.error == null) {
+                // if result info is not null, first try a pojo row adapter
+                context.collectLogs { subContext ->
+                    val pojo = PojoProcessor(
+                            baseContext = subContext,
+                            element = MoreTypes.asTypeElement(typeMirror),
+                            bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                            parent = null
+                    ).process()
+                    PojoRowAdapter(
+                            context = subContext,
+                            info = resultInfo,
+                            pojo = pojo,
+                            out = typeMirror)
+                }
+            } else {
+                Pair(null, null)
+            }
+
+            if (rowAdapter == null && query.resultInfo == null) {
+                // we don't know what query returns. Check for entity.
+                val asElement = MoreTypes.asElement(typeMirror)
+                if (asElement.hasAnnotation(Entity::class)) {
+                    return EntityRowAdapter(EntityProcessor(context,
+                            MoreElements.asType(asElement)).process())
+                }
+            }
+
+            if (rowAdapter != null && !(rowAdapterLogs?.hasErrors() ?: false)) {
+                rowAdapterLogs?.writeTo(context.processingEnv)
+                return rowAdapter
+            }
+
+            if ((resultInfo?.columns?.size ?: 1) == 1) {
+                val singleColumn = findCursorValueReader(typeMirror,
+                        resultInfo?.columns?.get(0)?.type)
+                if (singleColumn != null) {
+                    return SingleColumnRowAdapter(singleColumn)
+                }
+            }
+            // if we tried, return its errors
+            if (rowAdapter != null) {
+                rowAdapterLogs?.writeTo(context.processingEnv)
+                return rowAdapter
+            }
+            return null
+        } else {
+            val singleColumn = findCursorValueReader(typeMirror, null) ?: return null
+            return SingleColumnRowAdapter(singleColumn)
+        }
+    }
+
+    fun findQueryParameterAdapter(typeMirror: TypeMirror): QueryParameterAdapter? {
+        if (MoreTypes.isType(typeMirror)
+                && (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)
+                || MoreTypes.isTypeOf(java.util.Set::class.java, typeMirror))) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            val binder = findStatementValueBinder(declared.typeArguments.first(),
+                    null) ?: return null
+            return CollectionQueryParameterAdapter(binder)
+        } else if (typeMirror is ArrayType && typeMirror.componentType.kind != TypeKind.BYTE) {
+            val component = typeMirror.componentType
+            val binder = findStatementValueBinder(component, null) ?: return null
+            return ArrayQueryParameterAdapter(binder)
+        } else {
+            val binder = findStatementValueBinder(typeMirror, null) ?: return null
+            return BasicQueryParameterAdapter(binder)
+        }
+    }
+
+    private fun findTypeConverter(input: TypeMirror, outputs: List<TypeMirror>): TypeConverter? {
+        return findTypeConverter(listOf(input), outputs)
+    }
+
+    private fun findTypeConverter(input: List<TypeMirror>, output: TypeMirror): TypeConverter? {
+        return findTypeConverter(input, listOf(output))
+    }
+
+    private fun findTypeConverter(inputs: List<TypeMirror>, outputs: List<TypeMirror>)
+            : TypeConverter? {
+        if (inputs.isEmpty()) {
+            return null
+        }
+        val types = context.processingEnv.typeUtils
+        inputs.forEach { input ->
+            if (outputs.any { output -> types.isSameType(input, output) }) {
+                return NoOpConverter(input)
+            }
+        }
+
+        val excludes = arrayListOf<TypeMirror>()
+
+        val queue = LinkedList<TypeConverter>()
+        fun exactMatch(candidates: List<TypeConverter>)
+                : TypeConverter? {
+            return candidates.firstOrNull {
+                outputs.any { output -> types.isSameType(output, it.to) }
+            }
+        }
+        inputs.forEach { input ->
+            val candidates = getAllTypeConverters(input, excludes)
+            val match = exactMatch(candidates)
+            if (match != null) {
+                return match
+            }
+            candidates.forEach {
+                excludes.add(it.to)
+                queue.add(it)
+            }
+        }
+        excludes.addAll(inputs)
+        while (queue.isNotEmpty()) {
+            val prev = queue.pop()
+            val from = prev.to
+            val candidates = getAllTypeConverters(from, excludes)
+            val match = exactMatch(candidates)
+            if (match != null) {
+                return CompositeTypeConverter(prev, match)
+            }
+            candidates.forEach {
+                excludes.add(it.to)
+                queue.add(CompositeTypeConverter(prev, it))
+            }
+        }
+        return null
+    }
+
+    private fun getAllColumnAdapters(input: TypeMirror): List<ColumnTypeAdapter> {
+        return columnTypeAdapters.filter {
+            context.processingEnv.typeUtils.isSameType(input, it.out)
+        }
+    }
+
+    private fun getAllTypeConverters(input: TypeMirror, excludes: List<TypeMirror>):
+            List<TypeConverter> {
+        val types = context.processingEnv.typeUtils
+        return typeConverters.filter { converter ->
+            types.isSameType(input, converter.from) &&
+                    !excludes.any { types.isSameType(it, converter.to) }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/CountedDataSourceQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/CountedDataSourceQueryResultBinderProvider.kt
new file mode 100644
index 0000000..c23e31f
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/CountedDataSourceQueryResultBinderProvider.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.ext.PagingTypeNames
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.CountedDataSourceQueryResultBinder
+import android.arch.persistence.room.solver.query.result.ListQueryResultAdapter
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import com.google.common.annotations.VisibleForTesting
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeMirror
+
+class CountedDataSourceQueryResultBinderProvider(val context: Context) : QueryResultBinderProvider {
+    private val countedDataSourceTypeMirror: TypeMirror? by lazy {
+        context.processingEnv.elementUtils
+                .getTypeElement(PagingTypeNames.COUNTED_DATA_SOURCE.toString())?.asType()
+    }
+
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        val typeArg = declared.typeArguments.first()
+        val listAdapter = context.typeAdapterStore.findRowAdapter(typeArg, query)?.let {
+            ListQueryResultAdapter(it)
+        }
+        return CountedDataSourceQueryResultBinder(listAdapter, query.tables.map { it.name })
+    }
+
+    override fun matches(declared: DeclaredType): Boolean =
+            declared.typeArguments.size == 1 && isCountedDataSource(declared)
+
+    private fun isCountedDataSource(declared: DeclaredType): Boolean {
+        if (countedDataSourceTypeMirror == null) {
+            return false
+        }
+        val erasure = context.processingEnv.typeUtils.erasure(declared)
+        return context.processingEnv.typeUtils.isAssignable(countedDataSourceTypeMirror
+                , erasure)
+    }
+
+
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/CursorQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/CursorQueryResultBinderProvider.kt
new file mode 100644
index 0000000..e160672
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/CursorQueryResultBinderProvider.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.CursorQueryResultBinder
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.DeclaredType
+
+class CursorQueryResultBinderProvider(val context: Context) : QueryResultBinderProvider {
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        return CursorQueryResultBinder()
+    }
+
+    override fun matches(declared: DeclaredType): Boolean =
+        declared.typeArguments.size == 0 && TypeName.get(declared) == AndroidTypeNames.CURSOR
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/FlowableQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/FlowableQueryResultBinderProvider.kt
new file mode 100644
index 0000000..828f5a7
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/FlowableQueryResultBinderProvider.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.ext.RoomRxJava2TypeNames
+import android.arch.persistence.room.ext.RxJava2TypeNames
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.ProcessorErrors
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.FlowableQueryResultBinder
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import com.google.common.annotations.VisibleForTesting
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeMirror
+
+class FlowableQueryResultBinderProvider(val context : Context) : QueryResultBinderProvider {
+    private val flowableTypeMirror: TypeMirror? by lazy {
+        context.processingEnv.elementUtils
+                .getTypeElement(RxJava2TypeNames.FLOWABLE.toString())?.asType()
+    }
+
+    private val hasRxJava2Artifact by lazy {
+        context.processingEnv.elementUtils
+                .getTypeElement(RoomRxJava2TypeNames.RX_ROOM.toString()) != null
+    }
+
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        val typeArg = declared.typeArguments.first()
+        return FlowableQueryResultBinder(typeArg, query.tables.map { it.name },
+                context.typeAdapterStore.findQueryResultAdapter(typeArg, query))
+    }
+
+    override fun matches(declared: DeclaredType): Boolean =
+            declared.typeArguments.size == 1 && isRxJava2Publisher(declared)
+
+    private fun isRxJava2Publisher(declared: DeclaredType): Boolean {
+        if (flowableTypeMirror == null) {
+            return false
+        }
+        val erasure = context.processingEnv.typeUtils.erasure(declared)
+        val match = context.processingEnv.typeUtils.isAssignable(flowableTypeMirror, erasure)
+        if (match && !hasRxJava2Artifact) {
+            context.logger.e(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+        }
+        return match
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/InstantQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/InstantQueryResultBinderProvider.kt
new file mode 100644
index 0000000..af03a88
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/InstantQueryResultBinderProvider.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.InstantQueryResultBinder
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import javax.lang.model.type.DeclaredType
+
+class InstantQueryResultBinderProvider(val context : Context) : QueryResultBinderProvider {
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        return InstantQueryResultBinder(
+                context.typeAdapterStore.findQueryResultAdapter(declared, query))
+    }
+
+    override fun matches(declared: DeclaredType): Boolean = true
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/LiveDataQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/LiveDataQueryResultBinderProvider.kt
new file mode 100644
index 0000000..2aee5d2
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/LiveDataQueryResultBinderProvider.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.ext.LifecyclesTypeNames
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.LiveDataQueryResultBinder
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import com.google.common.annotations.VisibleForTesting
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeMirror
+
+class LiveDataQueryResultBinderProvider(val context : Context) : QueryResultBinderProvider {
+    private val liveDataTypeMirror: TypeMirror? by lazy {
+        context.processingEnv.elementUtils
+                .getTypeElement(LifecyclesTypeNames.LIVE_DATA.toString())?.asType()
+    }
+
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        val liveDataTypeArg = declared.typeArguments.first()
+        return LiveDataQueryResultBinder(liveDataTypeArg, query.tables.map { it.name },
+                context.typeAdapterStore.findQueryResultAdapter(liveDataTypeArg, query))
+    }
+
+    override fun matches(declared: DeclaredType): Boolean =
+            declared.typeArguments.size == 1 && isLiveData(declared)
+
+    private fun isLiveData(declared: DeclaredType): Boolean {
+        if (liveDataTypeMirror == null) {
+            return false
+        }
+        val erasure = context.processingEnv.typeUtils.erasure(declared)
+        return context.processingEnv.typeUtils.isAssignable(liveDataTypeMirror, erasure)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/LiveLazyListQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/LiveLazyListQueryResultBinderProvider.kt
new file mode 100644
index 0000000..c931d69
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/LiveLazyListQueryResultBinderProvider.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.ext.PagingTypeNames
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.CountedDataSourceQueryResultBinder
+import android.arch.persistence.room.solver.query.result.ListQueryResultAdapter
+import android.arch.persistence.room.solver.query.result.LiveLazyListQueryResultBinder
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import com.google.common.annotations.VisibleForTesting
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeMirror
+
+class LiveLazyListQueryResultBinderProvider(val context: Context) : QueryResultBinderProvider {
+    private val livePagedListTypeMirror: TypeMirror? by lazy {
+        context.processingEnv.elementUtils
+                .getTypeElement(PagingTypeNames.LIVE_LAZY_LIST_PROVIDER.toString())?.asType()
+    }
+
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        val typeArg = declared.typeArguments.first()
+        val listAdapter = context.typeAdapterStore.findRowAdapter(typeArg, query)?.let {
+            ListQueryResultAdapter(it)
+        }
+        val countedBinder = CountedDataSourceQueryResultBinder(listAdapter,
+                query.tables.map { it.name })
+        return LiveLazyListQueryResultBinder(countedBinder)
+    }
+
+    override fun matches(declared: DeclaredType): Boolean =
+            declared.typeArguments.size == 1 && isLivePagedList(declared)
+
+    private fun isLivePagedList(declared: DeclaredType): Boolean {
+        if (livePagedListTypeMirror == null) {
+            return false
+        }
+        val erasure = context.processingEnv.typeUtils.erasure(declared)
+        // we don't want to return paged list unless explicitly requested
+        return context.processingEnv.typeUtils.isAssignable(livePagedListTypeMirror, erasure)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/RxCallableQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/RxCallableQueryResultBinderProvider.kt
new file mode 100644
index 0000000..84c71eb
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/RxCallableQueryResultBinderProvider.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.binderprovider
+
+import android.arch.persistence.room.ext.RoomRxJava2TypeNames
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.ProcessorErrors
+import android.arch.persistence.room.solver.QueryResultBinderProvider
+import android.arch.persistence.room.solver.query.result.InstantQueryResultBinder
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import android.arch.persistence.room.solver.query.result.RxCallableQueryResultBinder
+import javax.lang.model.type.DeclaredType
+
+sealed class RxCallableQueryResultBinderProvider(val context: Context,
+                                                 val rxType: RxCallableQueryResultBinder.RxType)
+    : QueryResultBinderProvider {
+    private val hasRxJava2Artifact by lazy {
+        context.processingEnv.elementUtils
+                .getTypeElement(RoomRxJava2TypeNames.RX_ROOM.toString()) != null
+    }
+
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        val typeArg = declared.typeArguments.first()
+        val adapter = context.typeAdapterStore.findQueryResultAdapter(typeArg, query)
+        return RxCallableQueryResultBinder(rxType,
+                typeArg, InstantQueryResultBinder(adapter), adapter)
+    }
+
+    override fun matches(declared: DeclaredType): Boolean =
+            declared.typeArguments.size == 1 && matchesRxType(declared)
+
+    private fun matchesRxType(declared: DeclaredType): Boolean {
+        val erasure = context.processingEnv.typeUtils.erasure(declared)
+        val match = erasure.typeName() == rxType.className
+        if (match && !hasRxJava2Artifact) {
+            context.logger.e(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+        }
+        return match
+    }
+}
+
+class RxSingleQueryResultBinderProvider(context: Context)
+    : RxCallableQueryResultBinderProvider(context, RxCallableQueryResultBinder.RxType.SINGLE)
+
+class RxMaybeQueryResultBinderProvider(context: Context)
+    : RxCallableQueryResultBinderProvider(context, RxCallableQueryResultBinder.RxType.MAYBE)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/ArrayQueryParameterAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/ArrayQueryParameterAdapter.kt
new file mode 100644
index 0000000..9cafb23
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/ArrayQueryParameterAdapter.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.parameter
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.solver.types.StatementValueBinder
+import com.squareup.javapoet.TypeName
+
+/**
+ * Binds ARRAY(T) (e.g. int[]) into String[] args of a query.
+ */
+class ArrayQueryParameterAdapter(val bindAdapter : StatementValueBinder)
+            : QueryParameterAdapter(true) {
+    override fun bindToStmt(inputVarName: String, stmtVarName: String, startIndexVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            val itrVar = scope.getTmpVar("_item")
+            beginControlFlow("for ($T $L : $L)", bindAdapter.typeMirror().typeName(), itrVar,
+                    inputVarName).apply {
+                        bindAdapter.bindToStmt(stmtVarName, startIndexVarName, itrVar, scope)
+                        addStatement("$L ++", startIndexVarName)
+            }
+            endControlFlow()
+        }
+    }
+
+    override fun getArgCount(inputVarName: String, outputVarName : String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("final $T $L = $L.length", TypeName.INT, outputVarName, inputVarName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/BasicQueryParameterAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/BasicQueryParameterAdapter.kt
new file mode 100644
index 0000000..11521f2
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/BasicQueryParameterAdapter.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.parameter
+
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.solver.types.StatementValueBinder
+
+/**
+ * Knows how to convert a query parameter into arguments
+ */
+class BasicQueryParameterAdapter(val bindAdapter : StatementValueBinder)
+            : QueryParameterAdapter(false) {
+    override fun bindToStmt(inputVarName: String, stmtVarName: String, startIndexVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            bindAdapter.bindToStmt(stmtVarName, startIndexVarName, inputVarName, scope)
+        }
+    }
+
+    override fun getArgCount(inputVarName: String, outputVarName : String, scope: CodeGenScope) {
+        throw UnsupportedOperationException("should not call getArgCount on basic adapters." +
+                "It is always one.")
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/CollectionQueryParameterAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/CollectionQueryParameterAdapter.kt
new file mode 100644
index 0000000..7e0b206
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/CollectionQueryParameterAdapter.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.parameter
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.solver.types.StatementValueBinder
+import com.squareup.javapoet.TypeName
+
+/**
+ * Binds Collection<T> (e.g. List<T>) into String[] query args.
+ */
+class CollectionQueryParameterAdapter(val bindAdapter : StatementValueBinder)
+            : QueryParameterAdapter(true) {
+    override fun bindToStmt(inputVarName: String, stmtVarName: String, startIndexVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            val itrVar = scope.getTmpVar("_item")
+            beginControlFlow("for ($T $L : $L)", bindAdapter.typeMirror().typeName(), itrVar,
+                    inputVarName).apply {
+                        bindAdapter.bindToStmt(stmtVarName, startIndexVarName, itrVar, scope)
+                        addStatement("$L ++", startIndexVarName)
+                    }
+            endControlFlow()
+        }
+    }
+
+    override fun getArgCount(inputVarName: String, outputVarName : String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("final $T $L = $L.size()", TypeName.INT, outputVarName, inputVarName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/QueryParameterAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/QueryParameterAdapter.kt
new file mode 100644
index 0000000..b93659e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/parameter/QueryParameterAdapter.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.parameter
+
+import android.arch.persistence.room.solver.CodeGenScope
+
+/**
+ * Knows how to convert a query parameter into query arguments.
+ */
+abstract class QueryParameterAdapter(val isMultiple: Boolean) {
+    /**
+     * Must bind the value into the statement at the given index.
+     */
+    abstract fun bindToStmt(inputVarName: String, stmtVarName: String, startIndexVarName: String,
+                         scope: CodeGenScope)
+
+    /**
+     * Should declare and set the given value with the count
+     */
+    abstract fun getArgCount(inputVarName: String, outputVarName : String, scope : CodeGenScope)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/ArrayQueryResultAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/ArrayQueryResultAdapter.kt
new file mode 100644
index 0000000..e8330bc
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/ArrayQueryResultAdapter.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.TypeName
+
+class ArrayQueryResultAdapter(rowAdapter: RowAdapter) : QueryResultAdapter(rowAdapter) {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            rowAdapter?.onCursorReady(cursorVarName, scope)
+
+            val arrayType = ArrayTypeName.of(type.typeName())
+            addStatement("final $T $L = new $T[$L.getCount()]",
+                    arrayType, outVarName, type.typeName(), cursorVarName)
+            val tmpVarName = scope.getTmpVar("_item")
+            val indexVar = scope.getTmpVar("_index")
+            addStatement("$T $L = 0", TypeName.INT, indexVar)
+            beginControlFlow("while($L.moveToNext())", cursorVarName).apply {
+                addStatement("final $T $L", type.typeName(), tmpVarName)
+                rowAdapter?.convert(tmpVarName, cursorVarName, scope)
+                addStatement("$L[$L] = $L", outVarName, indexVar, tmpVarName)
+                addStatement("$L ++", indexVar)
+            }
+            endControlFlow()
+            rowAdapter?.onCursorFinished()?.invoke(scope)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/BaseObservableQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/BaseObservableQueryResultBinder.kt
new file mode 100644
index 0000000..7872c33
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/BaseObservableQueryResultBinder.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.writer.DaoWriter
+import com.squareup.javapoet.MethodSpec
+import javax.lang.model.element.Modifier
+
+/**
+ * Base class for query result binders that observe the database. It includes common functionality
+ * like creating a finalizer to release the query or creating the actual adapter call code.
+ */
+abstract class BaseObservableQueryResultBinder(adapter: QueryResultAdapter?)
+    : QueryResultBinder(adapter) {
+
+    protected fun createFinalizeMethod(roomSQLiteQueryVar: String): MethodSpec {
+        return MethodSpec.methodBuilder("finalize").apply {
+            addModifiers(Modifier.PROTECTED)
+            addAnnotation(Override::class.java)
+            addStatement("$L.release()", roomSQLiteQueryVar)
+        }.build()
+    }
+
+    protected fun createRunQueryAndReturnStatements(builder: MethodSpec.Builder,
+                                                    roomSQLiteQueryVar: String,
+                                                    scope: CodeGenScope) {
+        val outVar = scope.getTmpVar("_result")
+        val cursorVar = scope.getTmpVar("_cursor")
+        builder.apply {
+            addStatement("final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, cursorVar,
+                    DaoWriter.dbField, roomSQLiteQueryVar)
+            beginControlFlow("try").apply {
+                val adapterScope = scope.fork()
+                adapter?.convert(outVar, cursorVar, adapterScope)
+                addCode(adapterScope.builder().build())
+                addStatement("return $L", outVar)
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$L.close()", cursorVar)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/CountedDataSourceQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/CountedDataSourceQueryResultBinder.kt
new file mode 100644
index 0000000..f91a09a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/CountedDataSourceQueryResultBinder.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.CommonTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+class CountedDataSourceQueryResultBinder(val listAdapter : ListQueryResultAdapter?,
+                                         val tableNames : List<String>)
+            : QueryResultBinder(listAdapter) {
+    val itemTypeName : TypeName = listAdapter?.rowAdapter?.out?.typeName() ?: TypeName.OBJECT
+    val typeName : ParameterizedTypeName = ParameterizedTypeName.get(
+            RoomTypeNames.LIMIT_OFFSET_DATA_SOURCE, itemTypeName)
+    override fun convertAndReturn(roomSQLiteQueryVar: String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        val tableNamesList = tableNames.joinToString(",") { "\"$it\"" }
+        val spec = TypeSpec.anonymousClassBuilder("$N, $L, $L",
+                dbField, roomSQLiteQueryVar, tableNamesList).apply {
+            superclass(typeName)
+            addMethod(createConvertRowsMethod(scope))
+        }.build()
+        scope.builder().apply {
+            addStatement("return $L", spec)
+        }
+    }
+
+    fun createConvertRowsMethod(scope : CodeGenScope): MethodSpec =
+            MethodSpec.methodBuilder("convertRows").apply {
+                addAnnotation(Override::class.java)
+                addModifiers(Modifier.PROTECTED)
+                returns(ParameterizedTypeName.get(CommonTypeNames.LIST, itemTypeName))
+                val cursorParam = ParameterSpec.builder(AndroidTypeNames.CURSOR, "cursor")
+                        .build()
+                addParameter(cursorParam)
+                val resultVar = scope.getTmpVar("_res")
+                val rowsScope = scope.fork()
+                listAdapter?.convert(resultVar, cursorParam.name, rowsScope)
+                addCode(rowsScope.builder().build())
+                addStatement("return $L", resultVar)
+            }.build()
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/CursorQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/CursorQueryResultBinder.kt
new file mode 100644
index 0000000..cd8ea74
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/CursorQueryResultBinder.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.writer.DaoWriter
+import com.squareup.javapoet.FieldSpec
+
+class CursorQueryResultBinder : QueryResultBinder(NO_OP_RESULT_ADAPTER) {
+    override fun convertAndReturn(roomSQLiteQueryVar: String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        scope.builder().apply {
+            addStatement("return $N.query($L)", DaoWriter.dbField, roomSQLiteQueryVar)
+        }
+    }
+    companion object {
+        private val NO_OP_RESULT_ADAPTER = object : QueryResultAdapter(null) {
+            override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/EntityRowAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/EntityRowAdapter.kt
new file mode 100644
index 0000000..c97f69e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/EntityRowAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.writer.EntityCursorConverterWriter
+import com.squareup.javapoet.MethodSpec
+
+class EntityRowAdapter(val entity: Entity) : RowAdapter(entity.type) {
+    lateinit var methodSpec: MethodSpec
+    override fun onCursorReady(cursorVarName: String, scope: CodeGenScope) {
+        methodSpec = scope.writer.getOrCreateMethod(EntityCursorConverterWriter(entity))
+    }
+
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $N($L)", outVarName, methodSpec, cursorVarName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/FlowableQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/FlowableQueryResultBinder.kt
new file mode 100644
index 0000000..dbd66ce
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/FlowableQueryResultBinder.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomRxJava2TypeNames
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.arrayTypeName
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.writer.DaoWriter
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Binds the result as an RxJava2 Flowable.
+ */
+class FlowableQueryResultBinder(val typeArg: TypeMirror, val queryTableNames: List<String>,
+                                adapter: QueryResultAdapter?)
+    : BaseObservableQueryResultBinder(adapter) {
+    override fun convertAndReturn(roomSQLiteQueryVar: String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        val callableImpl = TypeSpec.anonymousClassBuilder("").apply {
+            val typeName = typeArg.typeName()
+            superclass(ParameterizedTypeName.get(java.util.concurrent.Callable::class.typeName(),
+                    typeName))
+            addMethod(MethodSpec.methodBuilder("call").apply {
+                returns(typeName)
+                addException(Exception::class.typeName())
+                addModifiers(Modifier.PUBLIC)
+                createRunQueryAndReturnStatements(this, roomSQLiteQueryVar, scope)
+            }.build())
+            addMethod(createFinalizeMethod(roomSQLiteQueryVar))
+        }.build()
+        scope.builder().apply {
+            val tableNamesList = queryTableNames.joinToString(",") { "\"$it\"" }
+            addStatement("return $T.createFlowable($N, new $T{$L}, $L)",
+                    RoomRxJava2TypeNames.RX_ROOM, DaoWriter.dbField,
+                    String::class.arrayTypeName(), tableNamesList, callableImpl)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/InstantQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/InstantQueryResultBinder.kt
new file mode 100644
index 0000000..ff15252
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/InstantQueryResultBinder.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.writer.DaoWriter
+import com.squareup.javapoet.FieldSpec
+
+/**
+ * Instantly runs and returns the query.
+ */
+class InstantQueryResultBinder(adapter: QueryResultAdapter?) : QueryResultBinder(adapter) {
+    override fun convertAndReturn(roomSQLiteQueryVar : String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        scope.builder().apply {
+            val outVar = scope.getTmpVar("_result")
+            val cursorVar = scope.getTmpVar("_cursor")
+            addStatement("final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, cursorVar,
+                    DaoWriter.dbField, roomSQLiteQueryVar)
+            beginControlFlow("try").apply {
+                adapter?.convert(outVar, cursorVar, scope)
+                addStatement("return $L", outVar)
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$L.close()", cursorVar)
+                addStatement("$L.release()", roomSQLiteQueryVar)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/ListQueryResultAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/ListQueryResultAdapter.kt
new file mode 100644
index 0000000..b70ec5a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/ListQueryResultAdapter.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import java.util.ArrayList
+
+class ListQueryResultAdapter(rowAdapter: RowAdapter) : QueryResultAdapter(rowAdapter) {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            rowAdapter?.onCursorReady(cursorVarName, scope)
+            val collectionType = ParameterizedTypeName
+                    .get(ClassName.get(List::class.java), type.typeName())
+            val arrayListType = ParameterizedTypeName
+                    .get(ClassName.get(ArrayList::class.java), type.typeName())
+            addStatement("final $T $L = new $T($L.getCount())",
+                    collectionType, outVarName, arrayListType, cursorVarName)
+            val tmpVarName = scope.getTmpVar("_item")
+            beginControlFlow("while($L.moveToNext())", cursorVarName).apply {
+                addStatement("final $T $L", type.typeName(), tmpVarName)
+                rowAdapter?.convert(tmpVarName, cursorVarName, scope)
+                addStatement("$L.add($L)", outVarName, tmpVarName)
+            }
+            endControlFlow()
+            rowAdapter?.onCursorFinished()?.invoke(scope)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/LiveDataQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/LiveDataQueryResultBinder.kt
new file mode 100644
index 0000000..4c9b0ca
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/LiveDataQueryResultBinder.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.LifecyclesTypeNames
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.RoomTypeNames.INVALIDATION_OBSERVER
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import android.support.annotation.NonNull
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Converts the query into a LiveData and returns it. No query is run until necessary.
+ */
+class LiveDataQueryResultBinder(val typeArg: TypeMirror, queryTableNames: List<String>,
+                                adapter: QueryResultAdapter?)
+    : BaseObservableQueryResultBinder(adapter) {
+    @Suppress("JoinDeclarationAndAssignment")
+    val tableNames = ((adapter?.accessedTableNames() ?: emptyList()) + queryTableNames).toSet()
+    override fun convertAndReturn(roomSQLiteQueryVar : String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        val typeName = typeArg.typeName()
+
+        val liveDataImpl = TypeSpec.anonymousClassBuilder("").apply {
+            superclass(ParameterizedTypeName.get(LifecyclesTypeNames.COMPUTABLE_LIVE_DATA,
+                    typeName))
+            val observerField = FieldSpec.builder(RoomTypeNames.INVALIDATION_OBSERVER,
+                    scope.getTmpVar("_observer"), Modifier.PRIVATE).build()
+            addField(observerField)
+            addMethod(createComputeMethod(
+                    observerField = observerField,
+                    typeName = typeName,
+                    roomSQLiteQueryVar = roomSQLiteQueryVar,
+                    dbField = dbField,
+                    scope = scope
+            ))
+            addMethod(createFinalizeMethod(roomSQLiteQueryVar))
+        }.build()
+        scope.builder().apply {
+            addStatement("return $L.getLiveData()", liveDataImpl)
+        }
+    }
+
+    private fun createComputeMethod(roomSQLiteQueryVar: String, typeName: TypeName,
+                                    observerField: FieldSpec, dbField: FieldSpec,
+                                    scope: CodeGenScope): MethodSpec {
+        return MethodSpec.methodBuilder("compute").apply {
+            addAnnotation(Override::class.java)
+            addModifiers(Modifier.PROTECTED)
+            returns(typeName)
+
+            beginControlFlow("if ($N == null)", observerField).apply {
+                addStatement("$N = $L", observerField, createAnonymousObserver())
+                addStatement("$N.getInvalidationTracker().addWeakObserver($N)",
+                        dbField, observerField)
+            }
+            endControlFlow()
+
+            createRunQueryAndReturnStatements(this, roomSQLiteQueryVar, scope)
+        }.build()
+    }
+
+    private fun createAnonymousObserver(): TypeSpec {
+        val tableNamesList = tableNames.joinToString(",") { "\"$it\"" }
+        return TypeSpec.anonymousClassBuilder(tableNamesList).apply {
+            superclass(INVALIDATION_OBSERVER)
+            addMethod(MethodSpec.methodBuilder("onInvalidated").apply {
+                returns(TypeName.VOID)
+                addAnnotation(Override::class.java)
+                addParameter(ParameterSpec.builder(
+                        ParameterizedTypeName.get(Set::class.java, String::class.java), "tables")
+                        .addAnnotation(NonNull::class.java)
+                        .build())
+                addModifiers(Modifier.PUBLIC)
+                addStatement("invalidate()")
+            }.build())
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/LiveLazyListQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/LiveLazyListQueryResultBinder.kt
new file mode 100644
index 0000000..c46698b
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/LiveLazyListQueryResultBinder.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.PagingTypeNames
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+class LiveLazyListQueryResultBinder(
+        val countedDataSourceQueryResultBinder: CountedDataSourceQueryResultBinder)
+    : QueryResultBinder(countedDataSourceQueryResultBinder.listAdapter) {
+    @Suppress("HasPlatformType")
+    val typeName = countedDataSourceQueryResultBinder.itemTypeName
+    override fun convertAndReturn(roomSQLiteQueryVar: String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        scope.builder().apply {
+            val lazyListProvider = TypeSpec
+                    .anonymousClassBuilder("").apply {
+                superclass(ParameterizedTypeName.get(PagingTypeNames.LIVE_LAZY_LIST_PROVIDER,
+                        typeName))
+                addMethod(createCreateDataSourceMethod(roomSQLiteQueryVar, dbField, scope))
+            }.build()
+            addStatement("return $L", lazyListProvider)
+        }
+    }
+
+    private fun createCreateDataSourceMethod(roomSQLiteQueryVar: String,
+                                             dbField: FieldSpec,
+                                             scope: CodeGenScope): MethodSpec
+            = MethodSpec.methodBuilder("createDataSource").apply {
+        addAnnotation(Override::class.java)
+        addModifiers(Modifier.PROTECTED)
+        returns(countedDataSourceQueryResultBinder.typeName)
+        val countedBinderScope = scope.fork()
+        countedDataSourceQueryResultBinder.convertAndReturn(roomSQLiteQueryVar, dbField,
+                countedBinderScope)
+        addCode(countedBinderScope.builder().build())
+    }.build()
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt
new file mode 100644
index 0000000..062b179
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.ProcessorErrors
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.verifier.QueryResultInfo
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.FieldWithIndex
+import android.arch.persistence.room.vo.Pojo
+import android.arch.persistence.room.vo.RelationCollector
+import android.arch.persistence.room.vo.Warning
+import android.arch.persistence.room.writer.FieldReadWriteWriter
+import com.squareup.javapoet.TypeName
+import stripNonJava
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Creates the entity from the given info.
+ * <p>
+ * The info comes from the query processor so we know about the order of columns in the result etc.
+ */
+class PojoRowAdapter(context: Context, val info: QueryResultInfo,
+                     val pojo: Pojo, out: TypeMirror) : RowAdapter(out) {
+    val mapping: Mapping
+    val relationCollectors: List<RelationCollector>
+
+    init {
+        // toMutableList documentation is not clear if it copies so lets be safe.
+        val remainingFields = pojo.fields.mapTo(mutableListOf<Field>(), { it })
+        val unusedColumns = arrayListOf<String>()
+        val matchedFields = info.columns.map { column ->
+            // first check remaining, otherwise check any. maybe developer wants to map the same
+            // column into 2 fields. (if they want to post process etc)
+            val field = remainingFields.firstOrNull { it.columnName == column.name } ?:
+                    pojo.fields.firstOrNull { it.columnName == column.name }
+            if (field == null) {
+                unusedColumns.add(column.name)
+                null
+            } else {
+                remainingFields.remove(field)
+                field
+            }
+        }.filterNotNull()
+        if (unusedColumns.isNotEmpty() || remainingFields.isNotEmpty()) {
+            val warningMsg = ProcessorErrors.cursorPojoMismatch(
+                    pojoTypeName = pojo.typeName,
+                    unusedColumns = unusedColumns,
+                    allColumns = info.columns.map { it.name },
+                    unusedFields = remainingFields,
+                    allFields = pojo.fields
+            )
+            context.logger.w(Warning.CURSOR_MISMATCH, null, warningMsg)
+        }
+        if (matchedFields.isEmpty()) {
+            context.logger.e(ProcessorErrors.CANNOT_FIND_QUERY_RESULT_ADAPTER)
+        }
+
+        relationCollectors = RelationCollector.createCollectors(context, pojo.relations)
+
+        mapping = Mapping(
+                matchedFields = matchedFields,
+                unusedColumns = unusedColumns,
+                unusedFields = remainingFields
+        )
+    }
+
+    fun relationTableNames(): List<String> {
+        return relationCollectors.flatMap {
+            val queryTableNames = it.loadAllQuery.tables.map { it.name }
+            if (it.rowAdapter is PojoRowAdapter) {
+                it.rowAdapter.relationTableNames() + queryTableNames
+            } else {
+                queryTableNames
+            }
+        }.distinct()
+    }
+
+    override fun onCursorReady(cursorVarName: String, scope: CodeGenScope) {
+        relationCollectors.forEach { it.writeInitCode(scope) }
+        mapping.fieldsWithIndices = mapping.matchedFields.map {
+            val indexVar = scope.getTmpVar("_cursorIndexOf${it.name.stripNonJava().capitalize()}")
+            scope.builder().addStatement("final $T $L = $L.getColumnIndexOrThrow($S)",
+                    TypeName.INT, indexVar, cursorVarName, it.columnName)
+            FieldWithIndex(field = it, indexVar = indexVar, alwaysExists = true)
+        }
+    }
+
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            FieldReadWriteWriter.readFromCursor(
+                    outVar = outVarName,
+                    outPojo = pojo,
+                    cursorVar = cursorVarName,
+                    fieldsWithIndices = mapping.fieldsWithIndices,
+                    relationCollectors = relationCollectors,
+                    scope = scope)
+        }
+    }
+
+    override fun onCursorFinished(): ((CodeGenScope) -> Unit)? =
+            if (relationCollectors.isEmpty()) {
+                // it is important to return empty to notify that we don't need any post process
+                // task
+                null
+            } else {
+                { scope ->
+                    relationCollectors.forEach { collector ->
+                        collector.writeCollectionCode(scope)
+                    }
+                }
+            }
+
+    data class Mapping(val matchedFields: List<Field>,
+                       val unusedColumns: List<String>,
+                       val unusedFields: List<Field>) {
+        // set when cursor is ready.
+        lateinit var fieldsWithIndices: List<FieldWithIndex>
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/QueryResultAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/QueryResultAdapter.kt
new file mode 100644
index 0000000..aaf17cd
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/QueryResultAdapter.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.solver.CodeGenScope
+
+/**
+ * Gets a Cursor and converts it into the return type of a method annotated with @Query.
+ */
+abstract class QueryResultAdapter(val rowAdapter : RowAdapter?) {
+    abstract fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope)
+    fun accessedTableNames(): List<String> {
+        return (rowAdapter as? PojoRowAdapter)?.relationTableNames() ?: emptyList<String>()
+    }
+
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/QueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/QueryResultBinder.kt
new file mode 100644
index 0000000..205bd88
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/QueryResultBinder.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.FieldSpec
+
+/**
+ * Connects the query, db and the ResultAdapter.
+ * <p>
+ * The default implementation is InstantResultBinder. If the query is deferred rather than executed
+ * directly, such alternative implementations can be implement using this interface (e.g. LiveData,
+ * Rx, caching etc)
+ */
+abstract class QueryResultBinder(val adapter: QueryResultAdapter?) {
+    /**
+     * receives the sql, bind args and adapter and generates the code that runs the query
+     * and returns the result.
+     */
+    abstract fun convertAndReturn(roomSQLiteQueryVar: String,
+                                  dbField: FieldSpec, scope: CodeGenScope)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/RowAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/RowAdapter.kt
new file mode 100644
index 0000000..581baf2
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/RowAdapter.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Converts a row of a cursor result into an Entity or a primitive.
+ * <p>
+ * An instance of this is created for each usage so that it can keep local variables.
+ */
+abstract class RowAdapter(val out : TypeMirror) {
+    /**
+     * Called when cursor variable is ready, good place to put initialization code.
+     */
+    open fun onCursorReady(cursorVarName: String, scope : CodeGenScope) {}
+
+    /**
+     * Called to convert a single row.
+     */
+    abstract fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope)
+
+    /**
+     * Called when the cursor is finished. It is important to return null if no operation is
+     * necessary so that caller can understand that we can do lazy loading.
+     */
+    open fun onCursorFinished() : ((scope : CodeGenScope) -> Unit)? = null
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/RxCallableQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/RxCallableQueryResultBinder.kt
new file mode 100644
index 0000000..da960f6
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/RxCallableQueryResultBinder.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomRxJava2TypeNames
+import android.arch.persistence.room.ext.RxJava2TypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Generic Result binder for Rx classes that accept a callable.
+ */
+class RxCallableQueryResultBinder(val rxType: RxType,
+                                  val typeArg: TypeMirror,
+                                  val instantBinder : InstantQueryResultBinder,
+                                  adapter: QueryResultAdapter?) : QueryResultBinder(adapter) {
+    override fun convertAndReturn(roomSQLiteQueryVar: String, dbField: FieldSpec,
+                                  scope: CodeGenScope) {
+        val callable = TypeSpec.anonymousClassBuilder("").apply {
+            val typeName = typeArg.typeName()
+            superclass(ParameterizedTypeName.get(java.util.concurrent.Callable::class.typeName(),
+                    typeName))
+            addMethod(createCallMethod(roomSQLiteQueryVar, dbField, scope))
+        }.build()
+        scope.builder().apply {
+            addStatement("return $T.fromCallable($L)", rxType.className, callable)
+        }
+    }
+
+    fun createCallMethod(roomSQLiteQueryVar: String, dbField: FieldSpec,
+                         scope: CodeGenScope): MethodSpec {
+        val adapterScope = scope.fork()
+        return MethodSpec.methodBuilder("call").apply {
+            returns(typeArg.typeName())
+            addException(Exception::class.typeName())
+            addModifiers(Modifier.PUBLIC)
+            val outVar = scope.getTmpVar("_result")
+            val cursorVar = scope.getTmpVar("_cursor")
+            addStatement("final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, cursorVar,
+                    dbField, roomSQLiteQueryVar)
+            beginControlFlow("try").apply {
+                adapter?.convert(outVar, cursorVar, adapterScope)
+                addCode(adapterScope.generate())
+                if (!rxType.canBeNull) {
+                    beginControlFlow("if($L == null)", outVar).apply {
+                        addStatement("throw new $T($S + $L.getSql())",
+                                RoomRxJava2TypeNames.RX_EMPTY_RESULT_SET_EXCEPTION,
+                                "Query returned empty result set: ",
+                                roomSQLiteQueryVar)
+                    }
+                    endControlFlow()
+                }
+                addStatement("return $L", outVar)
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$L.close()", cursorVar)
+                addStatement("$L.release()", roomSQLiteQueryVar)
+            }
+            endControlFlow()
+        }.build()
+    }
+
+    enum class RxType(val className : ClassName, val canBeNull : Boolean) {
+        SINGLE(RxJava2TypeNames.SINGLE, canBeNull = false),
+        MAYBE(RxJava2TypeNames.MAYBE, canBeNull = true);
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/SingleColumnRowAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/SingleColumnRowAdapter.kt
new file mode 100644
index 0000000..8237177
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/SingleColumnRowAdapter.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.solver.types.CursorValueReader
+
+/**
+ * Wraps a row adapter when there is only 1 item  with 1 column in the response.
+ */
+class SingleColumnRowAdapter(val reader: CursorValueReader) : RowAdapter(reader.typeMirror()) {
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        reader.readFromCursor(outVarName, cursorVarName, "0", scope)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/SingleEntityQueryResultAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/SingleEntityQueryResultAdapter.kt
new file mode 100644
index 0000000..3086894
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/SingleEntityQueryResultAdapter.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query.result
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import defaultValue
+
+/**
+ * Wraps a row adapter when there is only 1 item in the result
+ */
+class SingleEntityQueryResultAdapter(rowAdapter: RowAdapter) : QueryResultAdapter(rowAdapter) {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            rowAdapter?.onCursorReady(cursorVarName, scope)
+            addStatement("final $T $L", type.typeName(), outVarName)
+            beginControlFlow("if($L.moveToFirst())", cursorVarName)
+                rowAdapter?.convert(outVarName, cursorVarName, scope)
+            nextControlFlow("else").apply {
+                addStatement("$L = $L", outVarName, rowAdapter?.out?.defaultValue())
+            }
+            endControlFlow()
+            rowAdapter?.onCursorFinished()?.invoke(scope)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/BoxedBooleanToBoxedIntConverter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
new file mode 100644
index 0000000..552da9e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+
+/**
+ * int to boolean adapter.
+ */
+object BoxedBooleanToBoxedIntConverter {
+    fun create(processingEnvironment: ProcessingEnvironment): List<TypeConverter> {
+        val tBoolean = processingEnvironment.elementUtils.getTypeElement("java.lang.Boolean")
+                .asType()
+        val tInt = processingEnvironment.elementUtils.getTypeElement("java.lang.Integer")
+                .asType()
+        return listOf(
+                object : TypeConverter(tBoolean, tInt) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().addStatement("$L = $L == null ? null : ($L ? 1 : 0)",
+                                outputVarName, inputVarName, inputVarName)
+                    }
+                },
+                object : TypeConverter(tInt, tBoolean) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().addStatement("$L = $L == null ? null : $L != 0",
+                                outputVarName, inputVarName, inputVarName)
+                    }
+
+                }
+        )
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
new file mode 100644
index 0000000..1b34b46
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.solver.CodeGenScope
+import com.google.auto.common.MoreTypes
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Adapters for all boxed primitives that has direct cursor mappings.
+ */
+open class BoxedPrimitiveColumnTypeAdapter(boxed : TypeMirror,
+                                           val primitiveAdapter : PrimitiveColumnTypeAdapter)
+            : ColumnTypeAdapter(boxed, primitiveAdapter.typeAffinity) {
+    companion object {
+        fun createBoxedPrimitiveAdapters(processingEnvironment: ProcessingEnvironment,
+                                    primitiveAdapters : List<PrimitiveColumnTypeAdapter>)
+                : List<ColumnTypeAdapter> {
+
+            return primitiveAdapters.map {
+                BoxedPrimitiveColumnTypeAdapter(
+                        processingEnvironment.typeUtils
+                                .boxedClass(MoreTypes.asPrimitiveType(it.out)).asType(),
+                        it
+                )
+            }
+        }
+    }
+
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            beginControlFlow("if ($L == null)", valueVarName).apply {
+                addStatement("$L.bindNull($L)", stmtName, indexVarName)
+            }
+            nextControlFlow("else").apply {
+                primitiveAdapter.bindToStmt(stmtName, indexVarName, valueVarName, scope)
+            }
+            endControlFlow()
+        }
+    }
+
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope) {
+        scope.builder().apply {
+            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName).apply {
+                addStatement("$L = null", outVarName)
+            }
+            nextControlFlow("else").apply {
+                primitiveAdapter.readFromCursor(outVarName, cursorVarName, indexVarName, scope)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/ByteArrayColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/ByteArrayColumnTypeAdapter.kt
new file mode 100644
index 0000000..014ea44
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/ByteArrayColumnTypeAdapter.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeKind
+
+class ByteArrayColumnTypeAdapter(env : ProcessingEnvironment) : ColumnTypeAdapter(
+        out = env.typeUtils.getArrayType(env.typeUtils.getPrimitiveType(TypeKind.BYTE)),
+        typeAffinity = SQLTypeAffinity.BLOB) {
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L.getBlob($L)", outVarName, cursorVarName, indexVarName)
+    }
+
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            beginControlFlow("if ($L == null)", valueVarName)
+                    .addStatement("$L.bindNull($L)", stmtName, indexVarName)
+            nextControlFlow("else")
+                    .addStatement("$L.bindBlob($L, $L)", stmtName, indexVarName, valueVarName)
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/ColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/ColumnTypeAdapter.kt
new file mode 100644
index 0000000..d82dc79
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/ColumnTypeAdapter.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+
+/**
+ * A code generator that can read a field from Cursor and write a field to a Statement
+ */
+abstract class ColumnTypeAdapter(val out: TypeMirror, val typeAffinity: SQLTypeAffinity) :
+        StatementValueBinder, CursorValueReader {
+    val outTypeName: TypeName by lazy { TypeName.get(out) }
+    override fun typeMirror() = out
+    override fun affinity(): SQLTypeAffinity = typeAffinity
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CompositeAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CompositeAdapter.kt
new file mode 100644
index 0000000..a8678ff
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CompositeAdapter.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.lang.model.type.TypeMirror
+
+/**
+ * A column adapter that uses a type converter to do the conversion. The type converter may be
+ * a composite one.
+ */
+class CompositeAdapter(out: TypeMirror, val columnTypeAdapter: ColumnTypeAdapter,
+                       val intoStatementConverter: TypeConverter?,
+                       val fromCursorConverter: TypeConverter?)
+    : ColumnTypeAdapter(out, columnTypeAdapter.typeAffinity) {
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope) {
+        if (fromCursorConverter == null) {
+            return
+        }
+        scope.builder().apply {
+            val tmpCursorValue = scope.getTmpVar()
+            addStatement("final $T $L", columnTypeAdapter.outTypeName, tmpCursorValue)
+            columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, indexVarName, scope)
+            fromCursorConverter.convert(tmpCursorValue, outVarName, scope)
+        }
+    }
+
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope) {
+        if (intoStatementConverter == null) {
+            return
+        }
+        scope.builder().apply {
+            val tmpVar = scope.getTmpVar()
+            addStatement("final $T $L", columnTypeAdapter.out, tmpVar)
+            intoStatementConverter.convert(valueVarName, tmpVar, scope)
+            columnTypeAdapter.bindToStmt(stmtName, indexVarName, tmpVar, scope)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CompositeTypeConverter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CompositeTypeConverter.kt
new file mode 100644
index 0000000..86a5272
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CompositeTypeConverter.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+
+/**
+ * combines 2 type converters
+ */
+class CompositeTypeConverter(val conv1 : TypeConverter, val conv2 : TypeConverter) : TypeConverter(
+        conv1.from, conv2.to) {
+    override fun convert(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val tmp = scope.getTmpVar()
+            addStatement("final $T $L", conv1.to.typeName(), tmp)
+            conv1.convert(inputVarName, tmp, scope)
+            conv2.convert(tmp, outputVarName, scope)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CursorValueReader.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CursorValueReader.kt
new file mode 100644
index 0000000..e6726cd
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CursorValueReader.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Reads value from a cursor at the given index.
+ * see: StatementValueBinder
+ */
+interface CursorValueReader {
+    fun affinity() : SQLTypeAffinity
+    fun typeMirror() : TypeMirror
+    fun readFromCursor(outVarName : String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CustomTypeConverterWrapper.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CustomTypeConverterWrapper.kt
new file mode 100644
index 0000000..b4c7dc2
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/CustomTypeConverterWrapper.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.CustomTypeConverter
+import android.arch.persistence.room.writer.ClassWriter
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import javax.lang.model.element.Modifier
+
+/**
+ * Wraps a type converter specified by the developer and forwards calls to it.
+ */
+class CustomTypeConverterWrapper(val custom: CustomTypeConverter)
+    : TypeConverter(custom.from, custom.to) {
+
+    override fun convert(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            if (custom.isStatic) {
+                addStatement("$L = $T.$L($L)",
+                        outputVarName, custom.typeName,
+                        custom.methodName, inputVarName)
+            } else {
+                addStatement("$L = $N.$L($L)",
+                        outputVarName, typeConverter(scope),
+                        custom.methodName, inputVarName)
+            }
+        }
+    }
+
+    fun typeConverter(scope: CodeGenScope) : FieldSpec {
+        val baseName = (custom.typeName as ClassName).simpleName().decapitalize()
+        return scope.writer.getOrCreateField(object : ClassWriter.SharedFieldSpec(
+                baseName, custom.typeName) {
+            override fun getUniqueKey(): String {
+                return "converter_${custom.typeName}"
+            }
+
+            override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
+                builder.addModifiers(Modifier.PRIVATE)
+                builder.addModifiers(Modifier.FINAL)
+                builder.initializer("new $T()", custom.typeName)
+            }
+        })
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/NoOpConverter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/NoOpConverter.kt
new file mode 100644
index 0000000..e803d30
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/NoOpConverter.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Yes, we need this when user input is the same as the desired output.
+ * <p>
+ * Each query parameter receives an adapter that converts it into a String (or String[]). This
+ * TypeAdapter basically serves as a wrapper for converting String parameter into the String[] of
+ * the query. Not having this would require us to special case handle String, String[], List<String>
+ * etc.
+ */
+class NoOpConverter(type : TypeMirror) : TypeConverter(
+        type, type) {
+    override fun convert(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L", outputVarName, inputVarName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/PrimitiveBooleanToIntConverter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/PrimitiveBooleanToIntConverter.kt
new file mode 100644
index 0000000..c65df5c
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/PrimitiveBooleanToIntConverter.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeKind.BOOLEAN
+import javax.lang.model.type.TypeKind.INT
+
+/**
+ * int to boolean adapter.
+ */
+object PrimitiveBooleanToIntConverter {
+    fun create(processingEnvironment: ProcessingEnvironment): List<TypeConverter> {
+        val tBoolean = processingEnvironment.typeUtils.getPrimitiveType(BOOLEAN)
+        val tInt = processingEnvironment.typeUtils.getPrimitiveType(INT)
+        return listOf(
+                object : TypeConverter(tBoolean, tInt) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().addStatement("$L = $L ? 1 : 0", outputVarName, inputVarName)
+                    }
+                },
+                object : TypeConverter(tInt, tBoolean) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().addStatement("$L = $L != 0", outputVarName, inputVarName)
+                    }
+                })
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/PrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/PrimitiveColumnTypeAdapter.kt
new file mode 100644
index 0000000..c04e7bb
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/PrimitiveColumnTypeAdapter.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.parser.SQLTypeAffinity.REAL
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind.BYTE
+import javax.lang.model.type.TypeKind.CHAR
+import javax.lang.model.type.TypeKind.DOUBLE
+import javax.lang.model.type.TypeKind.FLOAT
+import javax.lang.model.type.TypeKind.INT
+import javax.lang.model.type.TypeKind.LONG
+import javax.lang.model.type.TypeKind.SHORT
+
+/**
+ * Adapters for all primitives that has direct cursor mappings.
+ */
+open class PrimitiveColumnTypeAdapter(out: PrimitiveType,
+                                      val cursorGetter: String,
+                                      val stmtSetter: String,
+                                      typeAffinity : SQLTypeAffinity)
+        : ColumnTypeAdapter(out, typeAffinity) {
+    val cast =  if (cursorGetter == "get${out.typeName().toString().capitalize()}")
+                    ""
+                else
+                    "(${out.typeName()}) "
+
+    companion object {
+        fun createPrimitiveAdapters(processingEnvironment: ProcessingEnvironment)
+                : List<PrimitiveColumnTypeAdapter> {
+            return listOf(
+                    Triple(INT, "getInt", "bindLong"),
+                    Triple(SHORT, "getShort", "bindLong"),
+                    Triple(BYTE, "getShort", "bindLong"),
+                    Triple(LONG, "getLong", "bindLong"),
+                    Triple(CHAR, "getInt", "bindLong"),
+                    Triple(FLOAT, "getFloat", "bindDouble"),
+                    Triple(DOUBLE, "getDouble", "bindDouble")
+            ).map {
+                PrimitiveColumnTypeAdapter(
+                        out = processingEnvironment.typeUtils.getPrimitiveType(it.first),
+                        cursorGetter = it.second,
+                        stmtSetter = it.third,
+                        typeAffinity = when(it.first) {
+                            INT, SHORT, BYTE, LONG, CHAR -> SQLTypeAffinity.INTEGER
+                            FLOAT, DOUBLE -> REAL
+                            else -> throw IllegalArgumentException("invalid type")
+                        }
+                )
+            }
+        }
+    }
+
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L.$L($L, $L)", stmtName, stmtSetter, indexVarName, valueVarName)
+    }
+
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L$L.$L($L)", outVarName, cast, cursorVarName,
+                        cursorGetter, indexVarName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/StatementValueBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/StatementValueBinder.kt
new file mode 100644
index 0000000..c898268
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/StatementValueBinder.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Binds a value into a statement
+ * see: CursorValueReader
+ */
+interface StatementValueBinder {
+    fun typeMirror() : TypeMirror
+    fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/StringColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/StringColumnTypeAdapter.kt
new file mode 100644
index 0000000..34ca61f
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/StringColumnTypeAdapter.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.parser.SQLTypeAffinity.TEXT
+import android.arch.persistence.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+
+class StringColumnTypeAdapter(processingEnvironment: ProcessingEnvironment)
+    : ColumnTypeAdapter((processingEnvironment.elementUtils.getTypeElement(
+        String::class.java.canonicalName)).asType(), TEXT) {
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L.getString($L)", outVarName, cursorVarName, indexVarName)
+    }
+
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            beginControlFlow("if ($L == null)", valueVarName)
+                    .addStatement("$L.bindNull($L)", stmtName, indexVarName)
+            nextControlFlow("else")
+                    .addStatement("$L.bindString($L, $L)", stmtName, indexVarName, valueVarName)
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/TypeConverter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/TypeConverter.kt
new file mode 100644
index 0000000..134daf4
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/types/TypeConverter.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.types
+
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+
+/**
+ * A code generator that can convert from 1 type to another
+ */
+abstract class TypeConverter(val from: TypeMirror, val to: TypeMirror) {
+    abstract fun convert(inputVarName: String, outputVarName: String, scope: CodeGenScope)
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/ColumnInfo.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/ColumnInfo.kt
new file mode 100644
index 0000000..e249871
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/ColumnInfo.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.verifier
+
+import android.arch.persistence.room.parser.SQLTypeAffinity
+
+/**
+ * Represents a column in a query response
+ */
+data class ColumnInfo(val name : String, val type : SQLTypeAffinity)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/DatabaseVerificaitonErrors.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/DatabaseVerificaitonErrors.kt
new file mode 100644
index 0000000..a744894
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/DatabaseVerificaitonErrors.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.verifier
+
+import java.sql.SQLException
+
+object DatabaseVerificaitonErrors {
+    private val CANNOT_CREATE_TABLE : String = "Create table statement had an error: %s"
+    fun cannotCreateTable(exception: SQLException) : String {
+        return CANNOT_CREATE_TABLE.format(exception.message)
+    }
+
+    private val CANNOT_VERIFY_QUERY : String = "There is a problem with the query: %s"
+    fun cannotVerifyQuery(exception: SQLException) : String {
+        return CANNOT_VERIFY_QUERY.format(exception.message)
+    }
+
+    private val CANNOT_CREATE_SQLITE_CONNECTION : String = "Room cannot create an SQLite" +
+            " connection to verify the queries. Query verification will be disabled. Error: %s"
+    fun cannotCreateConnection(exception: Exception) : String {
+        return CANNOT_CREATE_SQLITE_CONNECTION.format(exception.message)
+    }
+
+    val CANNOT_GET_TMP_JAVA_DIR = "Cannot read tmp java dir which is necessary to load sqlite" +
+            " lib. Database SQL verification will be disabled"
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/DatabaseVerifier.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/DatabaseVerifier.kt
new file mode 100644
index 0000000..8475f3a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/DatabaseVerifier.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.verifier
+
+import columnInfo
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.Warning
+import java.io.File
+import java.sql.Connection
+import java.sql.DriverManager
+import java.sql.SQLException
+import java.util.UUID
+import javax.lang.model.element.Element
+
+/**
+ * Builds an in-memory version of the database and verifies the queries against it.
+ * This class is also used to resolve the return types.
+ */
+class DatabaseVerifier private constructor(
+        val connection : Connection, val context : Context, val entities : List<Entity>) {
+    companion object {
+        /**
+         * Tries to create a verifier but returns null if it cannot find the driver.
+         */
+        fun create(context: Context, element: Element, entities: List<Entity>) : DatabaseVerifier? {
+            return try {
+                // see: https://github.com/xerial/sqlite-jdbc/issues/97
+                val tmpDir = System.getProperty("java.io.tmpdir")
+                if (tmpDir == null) {
+                    context.logger.w(Warning.MISSING_JAVA_TMP_DIR,
+                            element, DatabaseVerificaitonErrors.CANNOT_GET_TMP_JAVA_DIR)
+                    return null
+                }
+                val outDir = File(tmpDir, "room-${UUID.randomUUID()}")
+                outDir.mkdirs()
+                outDir.deleteOnExit()
+                System.setProperty("org.sqlite.tmpdir", outDir.absolutePath)
+                //force load:
+                Class.forName("org.sqlite.JDBC")
+                val connection = DriverManager.getConnection("jdbc:sqlite::memory:")
+                DatabaseVerifier(connection, context, entities)
+            } catch (ex : Exception) {
+                context.logger.w(Warning.CANNOT_CREATE_VERIFICATION_DATABASE, element,
+                        DatabaseVerificaitonErrors.cannotCreateConnection(ex))
+                null
+            }
+        }
+    }
+    init {
+        entities.forEach { entity ->
+            val stmt = connection.createStatement()
+            stmt.executeUpdate(entity.createTableQuery)
+        }
+    }
+
+    fun analyze(sql : String) : QueryResultInfo {
+        return try {
+            val stmt = connection.prepareStatement(sql)
+            QueryResultInfo(stmt.columnInfo())
+        } catch (ex : SQLException) {
+            QueryResultInfo(emptyList(), ex)
+        }
+    }
+
+    fun closeConnection() {
+        if (!connection.isClosed) {
+            try {
+                connection.close()
+            } catch (t : Throwable) {
+                //ignore.
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/QueryResultInfo.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/QueryResultInfo.kt
new file mode 100644
index 0000000..4a99243
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/QueryResultInfo.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.verifier
+
+import java.sql.SQLException
+
+/**
+ * Represents the result of a query.
+ * <p>
+ * This information is obtained by preparing the query against an in memory database at compile
+ * time.
+ */
+data class QueryResultInfo(val columns : List<ColumnInfo>, val error : SQLException? = null)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/jdbc_ext.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/jdbc_ext.kt
new file mode 100644
index 0000000..5ae9d6a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/verifier/jdbc_ext.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.verifier.ColumnInfo
+import java.sql.PreparedStatement
+import java.sql.ResultSet
+import java.sql.ResultSetMetaData
+import java.sql.SQLException
+
+internal fun <T> ResultSet.collect(f: (ResultSet) -> T): List<T> {
+    val result = arrayListOf<T>()
+    try {
+        while (next()) {
+            result.add(f.invoke(this))
+        }
+    } finally {
+        close()
+    }
+    return result
+}
+
+private fun <T> PreparedStatement.map(f : (Int, ResultSetMetaData) ->  T) : List<T> {
+    val columnCount = try {
+        metaData.columnCount
+    } catch (ex : SQLException) {
+        // ignore, no-result query
+        0
+    }
+    // return is separate than data creation because we want to know who throws the exception
+    return (1.rangeTo(columnCount)).map { f(it, metaData) }
+}
+
+internal fun PreparedStatement.columnNames(): List<String> {
+    return map { index, data -> data.getColumnName(index) }
+}
+
+private fun PreparedStatement.tryGetAffinity(columnIndex : Int) : SQLTypeAffinity {
+    return try {
+        SQLTypeAffinity.valueOf(metaData.getColumnTypeName(columnIndex).capitalize())
+    } catch (ex : IllegalArgumentException) {
+        SQLTypeAffinity.NULL
+    }
+}
+
+internal fun PreparedStatement.columnInfo(): List<ColumnInfo> {
+    //see: http://sqlite.1065341.n5.nabble.com/Column-order-in-resultset-td23127.html
+    return map { index, data -> ColumnInfo(data.getColumnName(index), tryGetAffinity(index)) }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/CallType.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/CallType.kt
new file mode 100644
index 0000000..40cbcc4
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/CallType.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+enum class CallType {
+    FIELD,
+    METHOD,
+    CONSTRUCTOR
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Constructor.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Constructor.kt
new file mode 100644
index 0000000..fdc2a11
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Constructor.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import javax.lang.model.element.ExecutableElement
+
+/**
+ * For each Entity / Pojo we process has a constructor. It might be the empty constructor or a
+ * constructor with fields.
+ */
+data class Constructor(val element : ExecutableElement, val params : List<Param>) {
+
+    fun hasField(field : Field) : Boolean {
+        return params.any {
+            when (it) {
+                is FieldParam -> it.field === field
+                is EmbeddedParam -> it.embedded.field === field
+                else -> false
+            }
+        }
+    }
+
+    class FieldParam(val field : Field) : Param(ParamType.FIELD) {
+        override fun log(): String = field.getPath()
+
+    }
+
+    class EmbeddedParam(val embedded: EmbeddedField) : Param(ParamType.EMBEDDED) {
+        override fun log(): String = embedded.field.getPath()
+
+    }
+
+    abstract class Param(val type : ParamType) {
+        abstract fun log() : String;
+    }
+
+    enum class ParamType {
+        FIELD,
+        EMBEDDED
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/CustomTypeConverter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/CustomTypeConverter.kt
new file mode 100644
index 0000000..5a1f0e6
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/CustomTypeConverter.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.hasAnyOf
+import android.arch.persistence.room.ext.typeName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Generated when we parse a method annotated with TypeConverter.
+ */
+data class CustomTypeConverter(val type: TypeMirror,
+                               val method : ExecutableElement,
+                               val from: TypeMirror, val to: TypeMirror) {
+    val typeName: TypeName by lazy { type.typeName() }
+    val fromTypeName: TypeName by lazy { from.typeName() }
+    val toTypeName: TypeName by lazy { to.typeName() }
+    val methodName by lazy { method.simpleName.toString() }
+    val isStatic by lazy { method.hasAnyOf(Modifier.STATIC) }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Dao.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Dao.kt
new file mode 100644
index 0000000..227fa49
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Dao.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+
+data class Dao(val element : TypeElement, val type : DeclaredType,
+               val queryMethods: List<QueryMethod>,
+               val insertionMethods : List<InsertionMethod>,
+               val deletionMethods : List<DeletionMethod>,
+               val updateMethods : List<UpdateMethod>,
+               val constructorParamType : TypeName?) {
+    // parsed dao might have a suffix if it is used in multiple databases.
+    private var suffix : String? = null
+    fun setSuffix(newSuffix : String) {
+        if (this.suffix != null) {
+            throw IllegalStateException("cannot set suffix twice")
+        }
+        this.suffix = if (newSuffix == "") "" else "_$newSuffix"
+    }
+
+    val typeName : ClassName by lazy { ClassName.get(element) }
+
+    val shortcutMethods : List<ShortcutMethod> by lazy {
+        deletionMethods + updateMethods
+    }
+
+    private val implClassName by lazy {
+        if (suffix == null) {
+            suffix = ""
+        }
+        "${typeName.simpleName()}${suffix}_Impl"
+    }
+
+    val implTypeName by lazy {
+        ClassName.get(typeName.packageName(), implClassName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/DaoMethod.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/DaoMethod.kt
new file mode 100644
index 0000000..bd5e6b8
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/DaoMethod.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import javax.lang.model.element.Element
+
+/**
+ * References a method that returns a dao in a Database
+ */
+data class DaoMethod(val element : Element, val name : String, val dao : Dao)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Database.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Database.kt
new file mode 100644
index 0000000..b118a32
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Database.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.RoomMasterTable
+import android.arch.persistence.room.migration.bundle.DatabaseBundle
+import android.arch.persistence.room.migration.bundle.SchemaBundle
+import com.squareup.javapoet.ClassName
+import org.apache.commons.codec.digest.DigestUtils
+import java.io.File
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Holds information about a class annotated with Database.
+ */
+data class Database(val element: TypeElement,
+                    val type: TypeMirror,
+                    val entities: List<Entity>,
+                    val daoMethods: List<DaoMethod>,
+                    val version: Int,
+                    val exportSchema: Boolean,
+                    val enableForeignKeys : Boolean) {
+    val typeName: ClassName by lazy { ClassName.get(element) }
+
+    private val implClassName by lazy {
+        "${typeName.simpleNames().joinToString("_")}_Impl"
+    }
+
+    val implTypeName: ClassName by lazy {
+        ClassName.get(typeName.packageName(), implClassName)
+    }
+
+    val bundle by lazy {
+        DatabaseBundle(version, identityHash, entities.map(Entity::toBundle),
+                listOf(RoomMasterTable.CREATE_QUERY,
+                        RoomMasterTable.createInsertQuery(identityHash)))
+    }
+
+    /**
+     * Create a has that identifies this database definition so that at runtime we can check to
+     * ensure developer didn't forget to update the version.
+     */
+    val identityHash: String by lazy {
+        val entityDescriptions = entities
+                .sortedBy { it.tableName }
+                .map { it.createTableQuery }
+        val indexDescriptions = entities
+                .flatMap { entity ->
+                    entity.indices.map { index ->
+                        index.createQuery(entity.tableName)
+                    }
+                }
+        val input = (entityDescriptions + indexDescriptions).joinToString("¯\\_(ツ)_/¯")
+        DigestUtils.md5Hex(input)
+    }
+
+    fun exportSchema(file: File) {
+        val schemaBundle = SchemaBundle(SchemaBundle.LATEST_FORMAT, bundle)
+        SchemaBundle.serialize(schemaBundle, file)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/DeletionMethod.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/DeletionMethod.kt
new file mode 100644
index 0000000..c4adc54
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/DeletionMethod.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.vo
+
+import javax.lang.model.element.ExecutableElement
+
+class DeletionMethod(element: ExecutableElement, name: String,
+                          entities: Map<String, Entity>, returnCount : Boolean,
+                          parameters: List<ShortcutQueryParameter>) : ShortcutMethod(
+        element, name, entities, returnCount, parameters
+)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/EmbeddedField.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/EmbeddedField.kt
new file mode 100644
index 0000000..55e9cc4
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/EmbeddedField.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.support.annotation.NonNull
+import android.arch.persistence.room.ext.hasAnnotation
+
+/**
+ * Used when a field is embedded inside an Entity or Pojo.
+ */
+// used in cache matching, must stay as a data class or implement equals
+data class EmbeddedField(val field : Field, val prefix : String = "",
+                         val parent : EmbeddedField?) {
+    val getter by lazy { field.getter }
+    val setter by lazy { field.setter }
+    val nonNull = field.element.hasAnnotation(NonNull::class)
+    lateinit var pojo: Pojo
+    val mRootParent: EmbeddedField by lazy {
+        parent?.mRootParent ?: this
+    }
+
+    fun isDescendantOf(other : EmbeddedField) : Boolean {
+        if (parent == other) {
+            return true
+        } else if (parent == null) {
+            return false
+        } else {
+            return parent.isDescendantOf(other)
+        }
+    }
+
+    fun isNonNullRecursively(): Boolean {
+        return field.nonNull && (parent == null || parent.isNonNullRecursively())
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Entity.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Entity.kt
new file mode 100644
index 0000000..06e7484
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Entity.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.migration.bundle.BundleUtil
+import android.arch.persistence.room.migration.bundle.EntityBundle
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+
+// TODO make data class when move to kotlin 1.1
+class Entity(element: TypeElement, val tableName: String, type: DeclaredType,
+             fields: List<Field>, embeddedFields: List<EmbeddedField>,
+             val primaryKey: PrimaryKey, val indices: List<Index>,
+             val foreignKeys: List<ForeignKey>,
+             constructor: Constructor?)
+    : Pojo(element, type, fields, embeddedFields, emptyList(), constructor) {
+
+    val createTableQuery by lazy {
+        createTableQuery(tableName)
+    }
+
+    fun createTableQuery(tableName : String) : String {
+        val definitions = (fields.map {
+            val autoIncrement = primaryKey.autoGenerateId && primaryKey.fields.contains(it)
+            it.databaseDefinition(autoIncrement)
+        } + createPrimaryKeyDefinition() + createForeignKeyDefinitions()).filterNotNull()
+        return "CREATE TABLE IF NOT EXISTS `$tableName` (${definitions.joinToString(", ")})"
+    }
+
+    private fun createForeignKeyDefinitions() : List<String> {
+        return foreignKeys.map { it.databaseDefinition() }
+    }
+
+    private fun createPrimaryKeyDefinition(): String? {
+        return if (primaryKey.fields.isEmpty() || primaryKey.autoGenerateId) {
+            null
+        } else {
+            val keys = primaryKey.fields
+                    .map { "`${it.columnName}`" }
+                    .joinToString(", ")
+            "PRIMARY KEY($keys)"
+        }
+    }
+
+    fun toBundle(): EntityBundle = EntityBundle(
+            tableName,
+            createTableQuery(BundleUtil.TABLE_NAME_PLACEHOLDER),
+            fields.map {it.toBundle()},
+            primaryKey.toBundle(),
+            indices.map { it.toBundle() },
+            foreignKeys.map { it.toBundle() })
+
+    fun isUnique(columns: List<String>) : Boolean {
+        return if (primaryKey.columnNames.size == columns.size
+                && primaryKey.columnNames.containsAll(columns)) {
+            true
+        } else {
+            indices.any { index ->
+                index.unique
+                        && index.fields.size == columns.size
+                        && index.columnNames.containsAll(columns)
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Field.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Field.kt
new file mode 100644
index 0000000..ee6f8a1
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Field.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.isNonNull
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.migration.bundle.FieldBundle
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.solver.types.CursorValueReader
+import android.arch.persistence.room.solver.types.StatementValueBinder
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.Element
+import javax.lang.model.type.TypeMirror
+// used in cache matching, must stay as a data class or implement equals
+data class Field(val element: Element, val name: String, val type: TypeMirror,
+                 var affinity: SQLTypeAffinity?,
+                 val columnName: String = name,
+                 /* means that this field does not belong to parent, instead, it belongs to a
+                 * embedded child of the main Pojo*/
+                 val parent: EmbeddedField? = null,
+                 // index might be removed when being merged into an Entity
+                 var indexed : Boolean = false) {
+    lateinit var getter: FieldGetter
+    lateinit var setter: FieldSetter
+    // binds the field into a statement
+    var statementBinder: StatementValueBinder? = null
+    // reads this field from a cursor column
+    var cursorValueReader: CursorValueReader? = null
+    val typeName: TypeName by lazy { type.typeName() }
+
+    /** Whether the table column for this field should be NOT NULL */
+    val nonNull = element.isNonNull() && (parent == null || parent.isNonNullRecursively())
+
+    /**
+     * Used when reporting errors on duplicate names
+     */
+    fun getPath() : String {
+        return if (parent == null) {
+            name
+        } else {
+            "${parent.field.getPath()} > $name"
+        }
+    }
+
+    private val pathWithDotNotation : String by lazy {
+        if (parent == null) {
+            name
+        } else {
+            "${parent.field.pathWithDotNotation}.$name"
+        }
+    }
+
+    /**
+     * List of names that include variations.
+     * e.g. if it is mUser, user is added to the list
+     * or if it is isAdmin, admin is added to the list
+     */
+    val nameWithVariations by lazy {
+        val result = arrayListOf(name)
+        if (name.length > 1) {
+            if (name.startsWith('_')) {
+                result.add(name.substring(1))
+            }
+            if (name.startsWith("m") && name[1].isUpperCase()) {
+                result.add(name.substring(1).decapitalize())
+            }
+
+            if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) {
+                if (name.length > 2 && name.startsWith("is") && name[2].isUpperCase()) {
+                    result.add(name.substring(2).decapitalize())
+                }
+                if (name.length > 3 && name.startsWith("has") && name[3].isUpperCase()) {
+                    result.add(name.substring(3).decapitalize())
+                }
+            }
+        }
+        result
+    }
+
+    val getterNameWithVariations by lazy {
+        nameWithVariations.map { "get${it.capitalize()}" } +
+                if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) {
+                    nameWithVariations.flatMap {
+                        listOf("is${it.capitalize()}", "has${it.capitalize()}")
+                    }
+                } else {
+                    emptyList()
+                }
+    }
+
+    val setterNameWithVariations by lazy {
+        nameWithVariations.map { "set${it.capitalize()}" }
+    }
+
+    /**
+     * definition to be used in create query
+     */
+    fun databaseDefinition(autoIncrementPKey : Boolean) : String {
+        val columnSpec = StringBuilder("")
+        if (autoIncrementPKey) {
+            columnSpec.append(" PRIMARY KEY AUTOINCREMENT")
+        }
+        if (nonNull) {
+            columnSpec.append(" NOT NULL")
+        }
+        return "`$columnName` ${(affinity ?: SQLTypeAffinity.TEXT).name}$columnSpec"
+    }
+
+    fun toBundle(): FieldBundle = FieldBundle(pathWithDotNotation, columnName,
+            affinity?.name ?: SQLTypeAffinity.TEXT.name, nonNull
+    )
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldGetter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldGetter.kt
new file mode 100644
index 0000000..6713c2c
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldGetter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+
+data class FieldGetter(val name : String, val type : TypeMirror, val callType: CallType) {
+    fun writeGet(ownerVar: String, outVar: String, builder: CodeBlock.Builder) {
+        val stmt = when (callType) {
+            CallType.FIELD -> "final $T $L = $L.$L"
+            CallType.METHOD -> "final $T $L = $L.$L()"
+            CallType.CONSTRUCTOR -> null
+        }
+        stmt?.let {
+            builder.addStatement(stmt, TypeName.get(type), outVar, ownerVar, name)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldSetter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldSetter.kt
new file mode 100644
index 0000000..5aa1806
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldSetter.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.L
+import com.squareup.javapoet.CodeBlock
+import javax.lang.model.type.TypeMirror
+
+data class FieldSetter(val name: String, val type: TypeMirror, val callType: CallType) {
+    fun writeSet(ownerVar: String, inVar: String, builder: CodeBlock.Builder) {
+        val stmt = when (callType) {
+            CallType.FIELD -> "$L.$L = $L"
+            CallType.METHOD -> "$L.$L($L)"
+            CallType.CONSTRUCTOR -> null
+        }
+        stmt?.let {
+            builder.addStatement(stmt, ownerVar, name, inVar)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldWithIndex.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldWithIndex.kt
new file mode 100644
index 0000000..6ef6979
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/FieldWithIndex.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+/**
+ * A common value object when we need to associate a Field with an Index
+ * variable.
+ * <p>
+ * If we are sure that the field will be there at compile time, we set it to always Exists so that
+ * the generated code does not check for -1 column indices.
+ */
+data class FieldWithIndex(val field : Field, val indexVar : String, val alwaysExists : Boolean) {
+    companion object {
+        fun byOrder(fields : List<Field>) : List<FieldWithIndex> {
+            return fields.mapIndexed { index, field ->
+                FieldWithIndex(field = field,
+                        indexVar = "${index + 1}",
+                        alwaysExists = true)
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ForeignKey.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ForeignKey.kt
new file mode 100644
index 0000000..66cf3a0
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ForeignKey.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.migration.bundle.ForeignKeyBundle
+
+/**
+ * Keeps information about a foreign key.
+ */
+data class ForeignKey(val parentTable: String,
+                      val parentColumns: List<String>,
+                      val childFields: List<Field>,
+                      val onDelete: ForeignKeyAction,
+                      val onUpdate: ForeignKeyAction,
+                      val deferred: Boolean) {
+    fun databaseDefinition(): String {
+        return "FOREIGN KEY(${joinEscaped(childFields.map { it.columnName })})" +
+                " REFERENCES `$parentTable`(${joinEscaped(parentColumns)})" +
+                " ON UPDATE ${onUpdate.sqlName}" +
+                " ON DELETE ${onDelete.sqlName}" +
+                " ${deferredDeclaration()}"
+    }
+
+    private fun deferredDeclaration(): String {
+        return if (deferred) {
+            "DEFERRABLE INITIALLY DEFERRED"
+        } else {
+            ""
+        }
+    }
+
+    private fun joinEscaped(values: Iterable<String>) = values.joinToString(", ") { "`$it`" }
+
+    fun toBundle(): ForeignKeyBundle = ForeignKeyBundle(
+            parentTable, onDelete.sqlName, onUpdate.sqlName,
+            childFields.map { it.columnName },
+            parentColumns
+    )
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ForeignKeyAction.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ForeignKeyAction.kt
new file mode 100644
index 0000000..1b85910
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ForeignKeyAction.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ForeignKey
+
+/**
+ * Compiler representation of ForeignKey#Action.
+ */
+enum class ForeignKeyAction(val annotationValue : Int, val sqlName : String) {
+    NO_ACTION(ForeignKey.NO_ACTION, "NO ACTION"),
+    RESTRICT(ForeignKey.RESTRICT, "RESTRICT"),
+    SET_NULL(ForeignKey.SET_NULL, "SET NULL"),
+    SET_DEFAULT(ForeignKey.SET_DEFAULT, "SET DEFAULT"),
+    CASCADE(ForeignKey.CASCADE, "CASCADE");
+    companion object {
+        private val mapping by lazy {
+            ForeignKeyAction.values().associateBy { it.annotationValue }
+        }
+        fun fromAnnotationValue(value : Int?) = mapping[value]
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Index.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Index.kt
new file mode 100644
index 0000000..60adf2b
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Index.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.migration.bundle.BundleUtil
+import android.arch.persistence.room.migration.bundle.IndexBundle
+
+/**
+ * Represents a processed index.
+ */
+data class Index(val name : String, val unique : Boolean, val fields : List<Field>) {
+
+    fun createQuery(tableName : String) : String {
+        val uniqueSQL = if (unique) {
+            "UNIQUE"
+        } else {
+            ""
+        }
+        return """
+            CREATE $uniqueSQL INDEX `$name`
+            ON `$tableName` (${fields.map { it.columnName }.joinToString(", ") { "`$it`"}})
+            """.trimIndent().replace(System.lineSeparator(), " ")
+    }
+
+    val columnNames by lazy { fields.map {it.columnName} }
+
+    fun toBundle(): IndexBundle = IndexBundle(name, unique, fields.map { it.columnName },
+            createQuery(BundleUtil.TABLE_NAME_PLACEHOLDER))
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/InsertionMethod.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/InsertionMethod.kt
new file mode 100644
index 0000000..ad3688d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/InsertionMethod.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.OnConflictStrategy
+import android.arch.persistence.room.ext.typeName
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.TypeMirror
+
+data class InsertionMethod(val element: ExecutableElement, val name: String,
+                           @OnConflictStrategy val onConflict: Int,
+                           val entities: Map<String, Entity>, val returnType: TypeMirror,
+                           val insertionType: Type?,
+                           val parameters: List<ShortcutQueryParameter>) {
+    fun insertMethodTypeFor(param : ShortcutQueryParameter) : Type {
+        return if (insertionType == Type.INSERT_VOID || insertionType == null) {
+            Type.INSERT_VOID
+        } else if (!param.isMultiple) {
+            Type.INSERT_SINGLE_ID
+        } else {
+            insertionType
+        }
+    }
+
+    enum class Type(
+            // methodName matches EntityInsertionAdapter methods
+            val methodName : String, val returnTypeName : TypeName) {
+        INSERT_VOID("insert", TypeName.VOID), // return void
+        INSERT_SINGLE_ID("insertAndReturnId", TypeName.LONG), // return long
+        INSERT_ID_ARRAY("insertAndReturnIdsArray",
+                ArrayTypeName.of(TypeName.LONG)), // return long[]
+        INSERT_ID_ARRAY_BOX("insertAndReturnIdsArrayBox",
+                ArrayTypeName.of(TypeName.LONG.box())), // return Long[]
+        INSERT_ID_LIST("insertAndReturnIdsList", // return List<Long>
+                ParameterizedTypeName.get(List::class.typeName(), TypeName.LONG.box()))
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Pojo.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Pojo.kt
new file mode 100644
index 0000000..acc63af
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Pojo.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.typeName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+
+/**
+ * A class is turned into a Pojo if it is used in a query response.
+ */
+// TODO make data class when move to kotlin 1.1
+open class Pojo(val element : TypeElement, val type: DeclaredType, val fields : List<Field>,
+                val embeddedFields: List<EmbeddedField>, val relations: List<Relation>,
+                val constructor : Constructor? = null) {
+    val typeName: TypeName by lazy { type.typeName() }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/PrimaryKey.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/PrimaryKey.kt
new file mode 100644
index 0000000..9735668
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/PrimaryKey.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.migration.bundle.PrimaryKeyBundle
+import javax.lang.model.element.Element
+
+/**
+ * Represents a PrimaryKey for an Entity.
+ */
+data class PrimaryKey(val declaredIn : Element?, val fields: List<Field>,
+                      val autoGenerateId: Boolean) {
+    companion object {
+        val MISSING = PrimaryKey(null, emptyList(), false)
+    }
+
+    val columnNames by lazy { fields.map {it.columnName} }
+
+    fun toHumanReadableString(): String {
+        return "PrimaryKey[" +
+                fields.joinToString(separator = ", ", transform = Field::getPath) + "]"
+    }
+
+    fun toBundle(): PrimaryKeyBundle = PrimaryKeyBundle(
+            autoGenerateId, fields.map { it.columnName })
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/QueryMethod.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/QueryMethod.kt
new file mode 100644
index 0000000..1aa8430
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/QueryMethod.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.solver.query.result.QueryResultBinder
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.TypeMirror
+
+/**
+ * A class that holds information about a QueryMethod.
+ * It is self sufficient and must have all generics etc resolved once created.
+ */
+data class QueryMethod(val element: ExecutableElement, val query: ParsedQuery, val name: String,
+                       val returnType: TypeMirror, val parameters: List<QueryParameter>,
+                       val queryResultBinder : QueryResultBinder) {
+    val sectionToParamMapping by lazy {
+        query.bindSections.map {
+            if (it.text.trim() == "?") {
+                Pair(it, parameters.firstOrNull())
+            } else if (it.text.startsWith(":")) {
+                val subName = it.text.substring(1)
+                Pair(it, parameters.firstOrNull {
+                    it.name == subName
+                })
+            } else {
+                Pair(it, null)
+            }
+        }
+    }
+
+    val returnsValue by lazy {
+        returnType.typeName() != TypeName.VOID
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/QueryParameter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/QueryParameter.kt
new file mode 100644
index 0000000..60ab55e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/QueryParameter.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.solver.query.parameter.QueryParameterAdapter
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Holds the parameter for a {@link QueryMethod}.
+ */
+data class QueryParameter(val name: String, val type: TypeMirror,
+                          val queryParamAdapter : QueryParameterAdapter?)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt
new file mode 100644
index 0000000..940db31
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+/**
+ * Value object created from processing a @Relation annotation.
+ */
+class Relation(
+        val entity: Entity,
+        val pojo: Pojo,
+        // field in Pojo that holds these relations (e.g. List<Pet> pets)
+        val field: Field,
+        // the parent field referenced for matching
+        val parentField: Field,
+        // the field referenced for querying. does not need to be in the response but the query
+        // we generate always has it in the response.
+        val entityField: Field,
+        // the projection for the query
+        val projection: List<String>) {
+
+    fun createLoadAllSql(): String {
+        var resultFields = if (projection.isNotEmpty()) {
+            projection
+        } else {
+            pojo.fields.map { it.columnName }
+        }
+        val entityFieldInResponse = pojo.fields.any { it.columnName == entityField.columnName }
+        if (!entityFieldInResponse) {
+            resultFields += entityField.columnName
+        }
+        return createSelect(resultFields)
+    }
+
+    private fun createSelect(resultFields: List<String>): String {
+        return "SELECT ${resultFields.joinToString(",")}" +
+                " FROM `${entity.tableName}`" +
+                " WHERE ${entityField.columnName} IN (:args)"
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/RelationCollector.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/RelationCollector.kt
new file mode 100644
index 0000000..c11091c
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/RelationCollector.kt
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.CommonTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.parser.SqlParser
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_QUERY_RESULT_ADAPTER
+import android.arch.persistence.room.processor.ProcessorErrors.relationAffinityMismatch
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.solver.query.result.RowAdapter
+import android.arch.persistence.room.solver.query.result.SingleColumnRowAdapter
+import android.arch.persistence.room.verifier.DatabaseVerificaitonErrors
+import android.arch.persistence.room.writer.QueryWriter
+import android.arch.persistence.room.writer.RelationCollectorMethodWriter
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import stripNonJava
+import java.util.ArrayList
+import java.util.HashSet
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Internal class that is used to manage fetching 1/N to N relationships.
+ */
+data class RelationCollector(val relation: Relation,
+                             val affinity : SQLTypeAffinity,
+                             val mapTypeName: ParameterizedTypeName,
+                             val keyTypeName: TypeName,
+                             val collectionTypeName: ParameterizedTypeName,
+                             val queryWriter: QueryWriter,
+                             val rowAdapter: RowAdapter,
+                             val loadAllQuery : ParsedQuery) {
+    // set when writing the code generator in writeInitCode
+    lateinit var varName: String
+
+    fun writeInitCode(scope: CodeGenScope) {
+        val tmpVar = scope.getTmpVar(
+                "_collection${relation.field.getPath().stripNonJava().capitalize()}")
+        scope.builder().addStatement("final $T $L = new $T()", mapTypeName, tmpVar, mapTypeName)
+        varName = tmpVar
+    }
+
+    // called after reading each item to extract the key if it exists
+    fun writeReadParentKeyCode(cursorVarName: String, itemVar : String,
+                               fieldsWithIndices : List<FieldWithIndex>, scope: CodeGenScope) {
+        val indexVar = fieldsWithIndices.firstOrNull {
+            it.field === relation.parentField
+        }?.indexVar
+        scope.builder().apply {
+            readKey(
+                    cursorVarName = cursorVarName,
+                    indexVar = indexVar,
+                    scope = scope
+            ) { tmpVar ->
+                val tmpCollectionVar = scope.getTmpVar("_tmpCollection")
+                addStatement("$T $L = $L.get($L)", collectionTypeName, tmpCollectionVar,
+                        varName, tmpVar)
+                beginControlFlow("if($L == null)", tmpCollectionVar).apply {
+                    addStatement("$L = new $T()", tmpCollectionVar, collectionTypeName)
+                    addStatement("$L.put($L, $L)", varName, tmpVar, tmpCollectionVar)
+                }
+                endControlFlow()
+                // set it on the item
+                relation.field.setter.writeSet(itemVar, tmpCollectionVar, this)
+            }
+        }
+    }
+
+    fun writeCollectionCode(scope: CodeGenScope) {
+        val method = scope.writer
+                .getOrCreateMethod(RelationCollectorMethodWriter(this))
+        scope.builder().apply {
+            addStatement("$N($L)", method, varName)
+        }
+    }
+
+    fun readKey(cursorVarName: String, indexVar: String?, scope: CodeGenScope,
+                postRead: CodeBlock.Builder.(String) -> Unit) {
+        val cursorGetter = when (affinity) {
+            SQLTypeAffinity.INTEGER -> "getLong"
+            SQLTypeAffinity.REAL -> "getDouble"
+            SQLTypeAffinity.TEXT -> "getString"
+            SQLTypeAffinity.BLOB -> "getBlob"
+            else -> {
+                "getString"
+            }
+        }
+        scope.builder().apply {
+            beginControlFlow("if (!$L.isNull($L))", cursorVarName, indexVar).apply {
+                val tmpVar = scope.getTmpVar("_tmpKey")
+                addStatement("final $T $L = $L.$L($L)", keyTypeName,
+                        tmpVar, cursorVarName, cursorGetter, indexVar)
+                this.postRead(tmpVar)
+            }
+            endControlFlow()
+        }
+    }
+
+    companion object {
+        fun createCollectors(baseContext : Context, relations: List<Relation>)
+                : List<RelationCollector> {
+            return relations.map { relation ->
+                // decide on the affinity
+                val context = baseContext.fork(relation.field.element)
+                val parentAffinity = relation.parentField.cursorValueReader?.affinity()
+                val childAffinity = relation.entityField.cursorValueReader?.affinity()
+                val affinity = if (parentAffinity != null && parentAffinity == childAffinity) {
+                    parentAffinity
+                } else {
+                    context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
+                            relationAffinityMismatch(
+                                    parentColumn = relation.parentField.columnName,
+                                    childColumn = relation.entityField.columnName,
+                                    parentAffinity = parentAffinity,
+                                    childAffinity = childAffinity))
+                    SQLTypeAffinity.TEXT
+                }
+                val keyType = keyTypeFor(context, affinity)
+                val collectionTypeName = if (relation.field.typeName is ParameterizedTypeName) {
+                    val paramType = relation.field.typeName as ParameterizedTypeName
+                    if (paramType.rawType == CommonTypeNames.LIST) {
+                        ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
+                                relation.pojo.typeName)
+                    } else if (paramType.rawType == CommonTypeNames.SET) {
+                        ParameterizedTypeName.get(ClassName.get(HashSet::class.java),
+                                relation.pojo.typeName)
+                    } else {
+                        ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
+                                relation.pojo.typeName)
+                    }
+                } else {
+                    ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
+                            relation.pojo.typeName)
+                }
+
+                val canUseArrayMap = context.processingEnv.elementUtils
+                        .getTypeElement(AndroidTypeNames.ARRAY_MAP.toString()) != null
+                val mapClass = if (canUseArrayMap) {
+                    AndroidTypeNames.ARRAY_MAP
+                } else {
+                    ClassName.get(java.util.HashMap::class.java)
+                }
+                val tmpMapType = ParameterizedTypeName.get(mapClass, keyType, collectionTypeName)
+                val keyTypeMirror = keyTypeMirrorFor(context, affinity)
+                val set = context.processingEnv.elementUtils.getTypeElement("java.util.Set")
+                val keySet = context.processingEnv.typeUtils.getDeclaredType(set, keyTypeMirror)
+                val loadAllQuery = relation.createLoadAllSql()
+                val parsedQuery = SqlParser.parse(loadAllQuery)
+                context.checker.check(parsedQuery.errors.isEmpty(), relation.field.element,
+                        parsedQuery.errors.joinToString("\n"))
+                if (parsedQuery.errors.isEmpty()) {
+                    val resultInfo = context.databaseVerifier?.analyze(loadAllQuery)
+                    parsedQuery.resultInfo = resultInfo
+                    if (resultInfo?.error != null) {
+                        context.logger.e(relation.field.element,
+                                DatabaseVerificaitonErrors.cannotVerifyQuery(resultInfo.error))
+                    }
+                }
+                val resultInfo = parsedQuery.resultInfo
+
+                val queryParam = QueryParameter(RelationCollectorMethodWriter.KEY_SET_VARIABLE,
+                        keySet, context.typeAdapterStore.findQueryParameterAdapter(keySet))
+                val queryWriter = QueryWriter(
+                        parameters = listOf(queryParam),
+                        sectionToParamMapping = listOf(Pair(parsedQuery.bindSections.first(),
+                                queryParam)),
+                        query = parsedQuery
+                )
+
+                // row adapter that matches full response
+                fun getDefaultRowAdapter() : RowAdapter? {
+                    return context.typeAdapterStore.findRowAdapter(relation.pojo.type, parsedQuery)
+                }
+                val rowAdapter = if (relation.projection.size == 1 && resultInfo != null &&
+                        (resultInfo.columns.size == 1 || resultInfo.columns.size == 2)) {
+                    // check for a column adapter first
+                    val cursorReader = context.typeAdapterStore.findCursorValueReader(
+                            relation.pojo.type, resultInfo.columns.first().type)
+                    if (cursorReader == null) {
+                        getDefaultRowAdapter()
+                    } else {
+                        context.logger.d("Choosing cursor adapter for the return value since" +
+                                " the query returns only 1 or 2 columns and there is a cursor" +
+                                " adapter for the return type.")
+                        SingleColumnRowAdapter(cursorReader)
+                    }
+                } else {
+                    getDefaultRowAdapter()
+                }
+
+                if (rowAdapter == null) {
+                    context.logger.e(relation.field.element, CANNOT_FIND_QUERY_RESULT_ADAPTER)
+                    null
+                } else {
+                    RelationCollector(
+                            relation = relation,
+                            affinity = affinity,
+                            mapTypeName = tmpMapType,
+                            keyTypeName = keyType,
+                            collectionTypeName = collectionTypeName,
+                            queryWriter = queryWriter,
+                            rowAdapter = rowAdapter,
+                            loadAllQuery = parsedQuery
+                    )
+                }
+            }.filterNotNull()
+        }
+
+        private fun keyTypeMirrorFor(context: Context, affinity: SQLTypeAffinity): TypeMirror {
+            val types = context.processingEnv.typeUtils
+            val elements = context.processingEnv.elementUtils
+            return when (affinity) {
+                SQLTypeAffinity.INTEGER -> elements.getTypeElement("java.lang.Long").asType()
+                SQLTypeAffinity.REAL -> elements.getTypeElement("java.lang.Double").asType()
+                SQLTypeAffinity.TEXT -> context.COMMON_TYPES.STRING
+                SQLTypeAffinity.BLOB -> types.getArrayType(types.getPrimitiveType(TypeKind.BYTE))
+                else -> {
+                    context.COMMON_TYPES.STRING
+                }
+            }
+        }
+
+        private fun keyTypeFor(context : Context, affinity: SQLTypeAffinity): TypeName {
+            return when (affinity) {
+                SQLTypeAffinity.INTEGER -> TypeName.LONG.box()
+                SQLTypeAffinity.REAL -> TypeName.DOUBLE.box()
+                SQLTypeAffinity.TEXT -> TypeName.get(String::class.java)
+                SQLTypeAffinity.BLOB -> ArrayTypeName.of(TypeName.BYTE)
+                else -> {
+                    // no affinity select from type
+                    context.COMMON_TYPES.STRING.typeName()
+                }
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ShortcutMethod.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ShortcutMethod.kt
new file mode 100644
index 0000000..121f993
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ShortcutMethod.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import javax.lang.model.element.ExecutableElement
+
+/**
+ * Base class for shortcut methods in @DAO.
+ */
+abstract class ShortcutMethod(val element: ExecutableElement, val name: String,
+                              val entities: Map<String, Entity>, val returnCount: Boolean,
+                              val parameters: List<ShortcutQueryParameter>)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ShortcutQueryParameter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ShortcutQueryParameter.kt
new file mode 100644
index 0000000..29fc759
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/ShortcutQueryParameter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Parameters used in DAO methods that are annotated with Insert, Delete, Update.
+ */
+data class ShortcutQueryParameter(val name: String, val type: TypeMirror,
+                                  val entityType: TypeMirror?, val isMultiple: Boolean) {
+    /**
+     * Method name in entity insertion or update adapter.
+     */
+    fun handleMethodName() : String {
+        return if (isMultiple) {
+            "handleMultiple"
+        } else {
+            "handle"
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/UpdateMethod.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/UpdateMethod.kt
new file mode 100644
index 0000000..ed8eb8b
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/UpdateMethod.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.OnConflictStrategy
+import javax.lang.model.element.ExecutableElement
+
+class UpdateMethod(element: ExecutableElement, name: String,
+                   entities: Map<String, Entity>, returnCount: Boolean,
+                   parameters: List<ShortcutQueryParameter>,
+                   @OnConflictStrategy val onConflictStrategy: Int) : ShortcutMethod(
+        element, name, entities, returnCount, parameters)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Warning.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Warning.kt
new file mode 100644
index 0000000..9cf137d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Warning.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+/**
+ * Internal representation of supported warnings
+ */
+enum class Warning(val publicKey: String) {
+    ALL("ALL"),
+    CURSOR_MISMATCH("ROOM_CURSOR_MISMATCH"),
+    MISSING_JAVA_TMP_DIR("ROOM_MISSING_JAVA_TMP_DIR"),
+    CANNOT_CREATE_VERIFICATION_DATABASE("ROOM_CANNOT_CREATE_VERIFICATION_DATABASE"),
+    PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED("ROOM_EMBEDDED_PRIMARY_KEY_IS_DROPPED"),
+    INDEX_FROM_EMBEDDED_FIELD_IS_DROPPED("ROOM_EMBEDDED_INDEX_IS_DROPPED"),
+    INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED("ROOM_EMBEDDED_ENTITY_INDEX_IS_DROPPED"),
+    INDEX_FROM_PARENT_IS_DROPPED("ROOM_PARENT_INDEX_IS_DROPPED"),
+    INDEX_FROM_PARENT_FIELD_IS_DROPPED("ROOM_PARENT_FIELD_INDEX_IS_DROPPED"),
+    RELATION_TYPE_MISMATCH("ROOM_RELATION_TYPE_MISMATCH"),
+    MISSING_SCHEMA_LOCATION("ROOM_MISSING_SCHEMA_LOCATION"),
+    MISSING_INDEX_ON_FOREIGN_KEY_CHILD("ROOM_MISSING_FOREIGN_KEY_CHILD_INDEX");
+
+    companion object {
+        val PUBLIC_KEY_MAP = Warning.values().associateBy { it.publicKey }
+        fun fromPublicKey(publicKey: String): Warning? {
+            return PUBLIC_KEY_MAP[publicKey.toUpperCase()]
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/ClassWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/ClassWriter.kt
new file mode 100644
index 0000000..7f386bd
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/ClassWriter.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.solver.CodeGenScope.Companion.CLASS_PROPERTY_PREFIX
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.annotation.processing.ProcessingEnvironment
+
+/**
+ * Base class for all writers that can produce a class.
+ */
+abstract class ClassWriter(val className: ClassName) {
+    private val sharedFieldSpecs = mutableMapOf<String, FieldSpec>()
+    private val sharedMethodSpecs = mutableMapOf<String, MethodSpec>()
+    private val sharedFieldNames = mutableSetOf<String>()
+    private val sharedMethodNames = mutableSetOf<String>()
+
+    abstract fun createTypeSpecBuilder(): TypeSpec.Builder
+
+    fun write(processingEnv: ProcessingEnvironment) {
+        val builder = createTypeSpecBuilder()
+        sharedFieldSpecs.values.forEach { builder.addField(it) }
+        sharedMethodSpecs.values.forEach { builder.addMethod(it) }
+        JavaFile.builder(className.packageName(), builder.build())
+                .build()
+                .writeTo(processingEnv.filer)
+    }
+
+    private fun makeUnique(set: MutableSet<String>, value: String): String {
+        if (!value.startsWith(CLASS_PROPERTY_PREFIX)) {
+            return makeUnique(set, "$CLASS_PROPERTY_PREFIX$value")
+        }
+        if (set.add(value)) {
+            return value
+        }
+        var index = 1
+        while (true) {
+            if (set.add("${value}_$index")) {
+                return "${value}_$index"
+            }
+            index++
+        }
+    }
+
+    fun getOrCreateField(sharedField: SharedFieldSpec): FieldSpec {
+        return sharedFieldSpecs.getOrPut(sharedField.getUniqueKey(), {
+            sharedField.build(this, makeUnique(sharedFieldNames, sharedField.baseName))
+        })
+    }
+
+    fun getOrCreateMethod(sharedMethod: SharedMethodSpec): MethodSpec {
+        return sharedMethodSpecs.getOrPut(sharedMethod.getUniqueKey(), {
+            sharedMethod.build(this, makeUnique(sharedMethodNames, sharedMethod.baseName))
+        })
+    }
+
+    abstract class SharedFieldSpec(val baseName: String, val type: TypeName) {
+
+        abstract fun getUniqueKey(): String
+
+        abstract fun prepare(writer: ClassWriter, builder: FieldSpec.Builder)
+
+        fun build(classWriter: ClassWriter, name: String): FieldSpec {
+            val builder = FieldSpec.builder(type, name)
+            prepare(classWriter, builder)
+            return builder.build()
+        }
+    }
+
+    abstract class SharedMethodSpec(val baseName: String) {
+
+        abstract fun getUniqueKey(): String
+        abstract fun prepare(writer: ClassWriter, builder: MethodSpec.Builder)
+
+        fun build(writer: ClassWriter, name: String): MethodSpec {
+            val builder = MethodSpec.methodBuilder(name)
+            prepare(writer, builder)
+            return builder.build()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/DaoWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/DaoWriter.kt
new file mode 100644
index 0000000..29a6ba7
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/DaoWriter.kt
@@ -0,0 +1,459 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.SupportDbTypeNames
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.QueryType
+import android.arch.persistence.room.processor.OnConflictProcessor
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Dao
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.InsertionMethod
+import android.arch.persistence.room.vo.QueryMethod
+import android.arch.persistence.room.vo.ShortcutMethod
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import stripNonJava
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier.FINAL
+import javax.lang.model.element.Modifier.PRIVATE
+import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.type.DeclaredType
+
+/**
+ * Creates the implementation for a class annotated with Dao.
+ */
+class DaoWriter(val dao: Dao, val processingEnv: ProcessingEnvironment)
+    : ClassWriter(dao.typeName) {
+    val declaredDao = MoreTypes.asDeclared(dao.element.asType())
+    companion object {
+        // TODO nothing prevents this from conflicting, we should fix.
+        val dbField: FieldSpec = FieldSpec
+                .builder(RoomTypeNames.ROOM_DB, "__db", PRIVATE, FINAL)
+                .build()
+
+        private fun typeNameToFieldName(typeName: TypeName?): String {
+            if (typeName is ClassName) {
+                return typeName.simpleName()
+            } else {
+                return typeName.toString().replace('.', '_').stripNonJava()
+            }
+        }
+    }
+
+    override fun createTypeSpecBuilder(): TypeSpec.Builder {
+        val builder = TypeSpec.classBuilder(dao.implTypeName)
+        /**
+         * if delete / update query method wants to return modified rows, we need prepared query.
+         * in that case, if args are dynamic, we cannot re-use the query, if not, we should re-use
+         * it. this requires more work but creates good performance.
+         */
+        val groupedDeleteUpdate = dao.queryMethods
+                .filter { it.query.type == QueryType.DELETE || it.query.type == QueryType.UPDATE }
+                .groupBy { it.parameters.any { it.queryParamAdapter?.isMultiple ?: true } }
+        // delete queries that can be prepared ahead of time
+        val preparedDeleteOrUpdateQueries = groupedDeleteUpdate[false] ?: emptyList()
+        // delete queries that must be rebuild every single time
+        val oneOffDeleteOrUpdateQueries = groupedDeleteUpdate[true] ?: emptyList()
+        val shortcutMethods = createInsertionMethods() +
+                createDeletionMethods() + createUpdateMethods() +
+                createPreparedDeleteOrUpdateQueries(preparedDeleteOrUpdateQueries)
+
+        builder.apply {
+            addModifiers(PUBLIC)
+            if (dao.element.kind == ElementKind.INTERFACE) {
+                addSuperinterface(dao.typeName)
+            } else {
+                superclass(dao.typeName)
+            }
+            addField(dbField)
+            val dbParam = ParameterSpec
+                    .builder(dao.constructorParamType ?: dbField.type, dbField.name).build()
+
+            addMethod(createConstructor(dbParam, shortcutMethods, dao.constructorParamType != null))
+
+            shortcutMethods.forEach {
+                addMethod(it.methodImpl)
+            }
+
+            dao.queryMethods.filter { it.query.type == QueryType.SELECT }.forEach { method ->
+                addMethod(createSelectMethod(method))
+            }
+            oneOffDeleteOrUpdateQueries.forEach {
+                addMethod(createDeleteOrUpdateQueryMethod(it))
+            }
+        }
+        return builder
+    }
+
+    private fun createPreparedDeleteOrUpdateQueries(preparedDeleteQueries: List<QueryMethod>)
+            : List<PreparedStmtQuery> {
+        return preparedDeleteQueries.map { method ->
+            val fieldSpec = getOrCreateField(PreparedStatementField(method))
+            val queryWriter = QueryWriter(method)
+            val fieldImpl = PreparedStatementWriter(queryWriter)
+                    .createAnonymous(this@DaoWriter, dbField)
+            val methodBody = createPreparedDeleteQueryMethodBody(method, fieldSpec, queryWriter)
+            PreparedStmtQuery(mapOf(PreparedStmtQuery.NO_PARAM_FIELD
+                    to (fieldSpec to fieldImpl)), methodBody)
+        }
+    }
+
+    private fun createPreparedDeleteQueryMethodBody(method: QueryMethod,
+                                                    preparedStmtField: FieldSpec,
+                                                    queryWriter: QueryWriter): MethodSpec {
+        val scope = CodeGenScope(this)
+        val methodBuilder = overrideWithoutAnnotations(method.element, declaredDao).apply {
+            val stmtName = scope.getTmpVar("_stmt")
+            addStatement("final $T $L = $N.acquire()",
+                    SupportDbTypeNames.SQLITE_STMT, stmtName, preparedStmtField)
+            addStatement("$N.beginTransaction()", dbField)
+            beginControlFlow("try").apply {
+                val bindScope = scope.fork()
+                queryWriter.bindArgs(stmtName, emptyList(), bindScope)
+                addCode(bindScope.builder().build())
+                if (method.returnsValue) {
+                    val resultVar = scope.getTmpVar("_result")
+                    addStatement("final $L $L = $L.executeUpdateDelete()",
+                            method.returnType.typeName(), resultVar, stmtName)
+                    addStatement("$N.setTransactionSuccessful()", dbField)
+                    addStatement("return $L", resultVar)
+                } else {
+                    addStatement("$L.executeUpdateDelete()", stmtName)
+                    addStatement("$N.setTransactionSuccessful()", dbField)
+                }
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$N.endTransaction()", dbField)
+                addStatement("$N.release($L)", preparedStmtField, stmtName)
+            }
+            endControlFlow()
+        }
+        return methodBuilder.build()
+    }
+
+    private fun createConstructor(dbParam: ParameterSpec,
+                                  shortcutMethods: List<PreparedStmtQuery>,
+                                  callSuper: Boolean): MethodSpec {
+        return MethodSpec.constructorBuilder().apply {
+            addParameter(dbParam)
+            addModifiers(PUBLIC)
+            if (callSuper) {
+                addStatement("super($N)", dbParam)
+            }
+            addStatement("this.$N = $N", dbField, dbParam)
+            shortcutMethods.filterNot {
+                it.fields.isEmpty()
+            }.map {
+                it.fields.values
+            }.flatten().groupBy {
+                it.first.name
+            }.map {
+                it.value.first()
+            }.forEach {
+                addStatement("this.$N = $L", it.first, it.second)
+            }
+        }.build()
+    }
+
+    private fun createSelectMethod(method: QueryMethod): MethodSpec {
+        return overrideWithoutAnnotations(method.element, declaredDao).apply {
+            addCode(createQueryMethodBody(method))
+        }.build()
+    }
+
+    private fun createDeleteOrUpdateQueryMethod(method: QueryMethod): MethodSpec {
+        return overrideWithoutAnnotations(method.element, declaredDao).apply {
+            addCode(createDeleteOrUpdateQueryMethodBody(method))
+        }.build()
+    }
+
+    /**
+     * Groups all insertion methods based on the insert statement they will use then creates all
+     * field specs, EntityInsertionAdapterWriter and actual insert methods.
+     */
+    private fun createInsertionMethods(): List<PreparedStmtQuery> {
+        return dao.insertionMethods
+                .map { insertionMethod ->
+                    val onConflict = OnConflictProcessor.onConflictText(insertionMethod.onConflict)
+                    val entities = insertionMethod.entities
+
+                    val fields = entities.mapValues {
+                        val spec = getOrCreateField(InsertionMethodField(it.value, onConflict))
+                        val impl = EntityInsertionAdapterWriter(it.value, onConflict)
+                                .createAnonymous(this@DaoWriter, dbField.name)
+                        spec to impl
+                    }
+                    val methodImpl = overrideWithoutAnnotations(insertionMethod.element,
+                            declaredDao).apply {
+                        addCode(createInsertionMethodBody(insertionMethod, fields))
+                    }.build()
+                    PreparedStmtQuery(fields, methodImpl)
+                }.filterNotNull()
+    }
+
+    private fun createInsertionMethodBody(method: InsertionMethod,
+                                          insertionAdapters: Map<String, Pair<FieldSpec, TypeSpec>>)
+            : CodeBlock {
+        val insertionType = method.insertionType
+        if (insertionAdapters.isEmpty() || insertionType == null) {
+            return CodeBlock.builder().build()
+        }
+        val scope = CodeGenScope(this)
+
+        return scope.builder().apply {
+            // TODO assert thread
+            // TODO collect results
+            addStatement("$N.beginTransaction()", dbField)
+            val needsReturnType = insertionType != InsertionMethod.Type.INSERT_VOID
+            val resultVar = if (needsReturnType) {
+                scope.getTmpVar("_result")
+            } else {
+                null
+            }
+
+            beginControlFlow("try").apply {
+                method.parameters.forEach { param ->
+                    val insertionAdapter = insertionAdapters[param.name]?.first
+                    if (needsReturnType) {
+                        // if it has more than 1 parameter, we would've already printed the error
+                        // so we don't care about re-declaring the variable here
+                        addStatement("$T $L = $N.$L($L)",
+                                insertionType.returnTypeName, resultVar,
+                                insertionAdapter, insertionType.methodName,
+                                param.name)
+                    } else {
+                        addStatement("$N.$L($L)", insertionAdapter, insertionType.methodName,
+                                param.name)
+                    }
+                }
+                addStatement("$N.setTransactionSuccessful()", dbField)
+                if (needsReturnType) {
+                    addStatement("return $L", resultVar)
+                }
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$N.endTransaction()", dbField)
+            }
+            endControlFlow()
+        }.build()
+    }
+
+    /**
+     * Creates EntityUpdateAdapter for each deletion method.
+     */
+    private fun createDeletionMethods(): List<PreparedStmtQuery> {
+        return createShortcutMethods(dao.deletionMethods, "deletion", { deletionMethod, entity ->
+            EntityDeletionAdapterWriter(entity)
+                    .createAnonymous(this@DaoWriter, dbField.name)
+        })
+    }
+
+    /**
+     * Creates EntityUpdateAdapter for each @Update method.
+     */
+    private fun createUpdateMethods(): List<PreparedStmtQuery> {
+        return createShortcutMethods(dao.updateMethods, "update", { update, entity ->
+            val onConflict = OnConflictProcessor.onConflictText(update.onConflictStrategy)
+            EntityUpdateAdapterWriter(entity, onConflict)
+                    .createAnonymous(this@DaoWriter, dbField.name)
+        })
+    }
+
+    private fun <T : ShortcutMethod> createShortcutMethods(methods: List<T>, methodPrefix: String,
+                                                           implCallback: (T, Entity) -> TypeSpec)
+            : List<PreparedStmtQuery> {
+        return methods.map { method ->
+            val entities = method.entities
+
+            if (entities.isEmpty()) {
+                null
+            } else {
+                val fields = entities.mapValues {
+                    val spec = getOrCreateField(DeleteOrUpdateAdapterField(it.value, methodPrefix))
+                    val impl = implCallback(method, it.value)
+                    spec to impl
+                }
+                val methodSpec = overrideWithoutAnnotations(method.element, declaredDao).apply {
+                    addCode(createDeleteOrUpdateMethodBody(method, fields))
+                }.build()
+                PreparedStmtQuery(fields, methodSpec)
+            }
+        }.filterNotNull()
+    }
+
+    private fun createDeleteOrUpdateMethodBody(method: ShortcutMethod,
+                                               adapters: Map<String, Pair<FieldSpec, TypeSpec>>)
+            : CodeBlock {
+        if (adapters.isEmpty()) {
+            return CodeBlock.builder().build()
+        }
+        val scope = CodeGenScope(this)
+        val resultVar = if (method.returnCount) {
+            scope.getTmpVar("_total")
+        } else {
+            null
+        }
+        return scope.builder().apply {
+            if (resultVar != null) {
+                addStatement("$T $L = 0", TypeName.INT, resultVar)
+            }
+            addStatement("$N.beginTransaction()", dbField)
+            beginControlFlow("try").apply {
+                method.parameters.forEach { param ->
+                    val adapter = adapters[param.name]?.first
+                    addStatement("$L$N.$L($L)",
+                            if (resultVar == null) "" else "$resultVar +=",
+                            adapter, param.handleMethodName(), param.name)
+                }
+                addStatement("$N.setTransactionSuccessful()", dbField)
+                if (resultVar != null) {
+                    addStatement("return $L", resultVar)
+                }
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$N.endTransaction()", dbField)
+            }
+            endControlFlow()
+        }.build()
+    }
+
+    /**
+     * @Query with delete action
+     */
+    private fun createDeleteOrUpdateQueryMethodBody(method: QueryMethod): CodeBlock {
+        val queryWriter = QueryWriter(method)
+        val scope = CodeGenScope(this)
+        val sqlVar = scope.getTmpVar("_sql")
+        val stmtVar = scope.getTmpVar("_stmt")
+        val listSizeArgs = queryWriter.prepareQuery(sqlVar, scope)
+        scope.builder().apply {
+            addStatement("$T $L = $N.compileStatement($L)",
+                    SupportDbTypeNames.SQLITE_STMT, stmtVar, dbField, sqlVar)
+            queryWriter.bindArgs(stmtVar, listSizeArgs, scope)
+            addStatement("$N.beginTransaction()", dbField)
+            beginControlFlow("try").apply {
+                if (method.returnsValue) {
+                    val resultVar = scope.getTmpVar("_result")
+                    addStatement("final $L $L = $L.executeUpdateDelete()",
+                            method.returnType.typeName(), resultVar, stmtVar)
+                    addStatement("$N.setTransactionSuccessful()", dbField)
+                    addStatement("return $L", resultVar)
+                } else {
+                    addStatement("$L.executeUpdateDelete()", stmtVar)
+                    addStatement("$N.setTransactionSuccessful()", dbField)
+                }
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$N.endTransaction()", dbField)
+            }
+            endControlFlow()
+
+        }
+        return scope.builder().build()
+    }
+
+    private fun createQueryMethodBody(method: QueryMethod): CodeBlock {
+        val queryWriter = QueryWriter(method)
+        val scope = CodeGenScope(this)
+        val sqlVar = scope.getTmpVar("_sql")
+        val roomSQLiteQueryVar = scope.getTmpVar("_statement")
+        queryWriter.prepareReadAndBind(sqlVar, roomSQLiteQueryVar, scope)
+        method.queryResultBinder.convertAndReturn(roomSQLiteQueryVar, dbField, scope)
+        return scope.builder().build()
+    }
+
+    private fun overrideWithoutAnnotations(elm: ExecutableElement,
+                                           owner : DeclaredType): MethodSpec.Builder {
+        val baseSpec = MethodSpec.overriding(elm, owner, processingEnv.typeUtils).build()
+        return MethodSpec.methodBuilder(baseSpec.name).apply {
+            addAnnotation(Override::class.java)
+            addModifiers(baseSpec.modifiers)
+            addParameters(baseSpec.parameters)
+            varargs(baseSpec.varargs)
+            returns(baseSpec.returnType)
+        }
+    }
+
+    /**
+     * Represents a query statement prepared in Dao implementation.
+     *
+     * @param fields This map holds all the member fields necessary for this query. The key is the
+     * corresponding parameter name in the defining query method. The value is a pair from the field
+     * declaration to definition.
+     * @param methodImpl The body of the query method implementation.
+     */
+    data class PreparedStmtQuery(val fields: Map<String, Pair<FieldSpec, TypeSpec>>,
+                                 val methodImpl: MethodSpec) {
+        companion object {
+            // The key to be used in `fields` where the method requires a field that is not
+            // associated with any of its parameters
+            const val NO_PARAM_FIELD = "-"
+        }
+    }
+
+    private class InsertionMethodField(val entity: Entity, val onConflictText: String)
+        : SharedFieldSpec(
+            "insertionAdapterOf${Companion.typeNameToFieldName(entity.typeName)}",
+            RoomTypeNames.INSERTION_ADAPTER) {
+
+        override fun getUniqueKey(): String {
+            return "${entity.typeName} $onConflictText"
+        }
+
+        override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
+            builder.addModifiers(FINAL, PRIVATE)
+        }
+    }
+
+    class DeleteOrUpdateAdapterField(val entity: Entity, val methodPrefix: String)
+        : SharedFieldSpec(
+            "${methodPrefix}AdapterOf${Companion.typeNameToFieldName(entity.typeName)}",
+            RoomTypeNames.DELETE_OR_UPDATE_ADAPTER) {
+        override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
+            builder.addModifiers(PRIVATE, FINAL)
+        }
+
+        override fun getUniqueKey(): String {
+            return entity.typeName.toString() + methodPrefix
+        }
+    }
+
+    class PreparedStatementField(val method: QueryMethod) : SharedFieldSpec(
+            "preparedStmtOf${method.name.capitalize()}", RoomTypeNames.SHARED_SQLITE_STMT) {
+        override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
+            builder.addModifiers(PRIVATE, FINAL)
+        }
+
+        override fun getUniqueKey(): String {
+            return method.query.original
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/DatabaseWriter.kt
new file mode 100644
index 0000000..e40bd71
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/DatabaseWriter.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.SupportDbTypeNames
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.DaoMethod
+import android.arch.persistence.room.vo.Database
+import com.google.auto.common.MoreElements
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import stripNonJava
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.Modifier.PRIVATE
+import javax.lang.model.element.Modifier.PROTECTED
+import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.element.Modifier.VOLATILE
+
+/**
+ * Writes implementation of classes that were annotated with @Database.
+ */
+class DatabaseWriter(val database : Database) : ClassWriter(database.implTypeName) {
+    override fun createTypeSpecBuilder(): TypeSpec.Builder {
+        val builder = TypeSpec.classBuilder(database.implTypeName)
+        builder.apply {
+            addModifiers(PUBLIC)
+            superclass(database.typeName)
+            addMethod(createCreateOpenHelper())
+            addMethod(createCreateInvalidationTracker())
+        }
+        addDaoImpls(builder)
+        return builder
+    }
+
+    private fun createCreateInvalidationTracker(): MethodSpec {
+        return MethodSpec.methodBuilder("createInvalidationTracker").apply {
+            addAnnotation(Override::class.java)
+            addModifiers(PROTECTED)
+            returns(RoomTypeNames.INVALIDATION_TRACKER)
+            val tableNames = database.entities.joinToString(",") {
+                "\"${it.tableName}\""
+            }
+            addStatement("return new $T(this, $L)", RoomTypeNames.INVALIDATION_TRACKER, tableNames)
+        }.build()
+    }
+
+    private fun  addDaoImpls(builder: TypeSpec.Builder) {
+        val scope = CodeGenScope(this)
+        builder.apply {
+            database.daoMethods.forEach { method ->
+                val name = method.dao.typeName.simpleName().decapitalize().stripNonJava()
+                val fieldName = scope.getTmpVar("_$name")
+                val field = FieldSpec.builder(method.dao.typeName, fieldName,
+                        PRIVATE, VOLATILE).build()
+                addField(field)
+                addMethod(createDaoGetter(field, method))
+            }
+        }
+    }
+
+    private fun createDaoGetter(field: FieldSpec, method: DaoMethod) : MethodSpec {
+        return MethodSpec.overriding(MoreElements.asExecutable(method.element)).apply {
+            beginControlFlow("if ($N != null)", field).apply {
+                addStatement("return $N", field)
+            }
+            nextControlFlow("else").apply {
+                beginControlFlow("synchronized(this)").apply {
+                    beginControlFlow("if($N == null)", field).apply {
+                        addStatement("$N = new $T(this)", field, method.dao.implTypeName)
+                    }
+                    endControlFlow()
+                    addStatement("return $N", field)
+                }
+                endControlFlow()
+            }
+            endControlFlow()
+        }.build()
+    }
+
+    private fun createCreateOpenHelper() : MethodSpec {
+        val scope = CodeGenScope(this)
+        return MethodSpec.methodBuilder("createOpenHelper").apply {
+            addModifiers(Modifier.PROTECTED)
+            returns(SupportDbTypeNames.SQLITE_OPEN_HELPER)
+
+            val configParam = ParameterSpec.builder(RoomTypeNames.ROOM_DB_CONFIG,
+                    "configuration").build()
+            addParameter(configParam)
+
+            val openHelperVar = scope.getTmpVar("_helper")
+            val openHelperCode = scope.fork()
+            SQLiteOpenHelperWriter(database)
+                    .write(openHelperVar, configParam, openHelperCode)
+            addCode(openHelperCode.builder().build())
+            addStatement("return $L", openHelperVar)
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityCursorConverterWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityCursorConverterWriter.kt
new file mode 100644
index 0000000..a83d191
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityCursorConverterWriter.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.FieldWithIndex
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import stripNonJava
+import javax.lang.model.element.Modifier.PRIVATE
+
+class EntityCursorConverterWriter(val entity: Entity) : ClassWriter.SharedMethodSpec(
+        "entityCursorConverter_${entity.typeName.toString().stripNonJava()}") {
+    override fun getUniqueKey(): String {
+        return "generic_entity_converter_of_${entity.element.qualifiedName}"
+    }
+
+    override fun prepare(writer: ClassWriter, builder: MethodSpec.Builder) {
+        builder.apply {
+            val cursorParam = ParameterSpec
+                    .builder(AndroidTypeNames.CURSOR, "cursor").build()
+            addParameter(cursorParam)
+            addModifiers(PRIVATE)
+            returns(entity.typeName)
+            addCode(buildConvertMethodBody(writer, cursorParam))
+        }
+    }
+
+    private fun depth(parent: EmbeddedField?): Int {
+        return if (parent == null) {
+            0
+        } else {
+            1 + depth(parent.parent)
+        }
+    }
+
+    private fun buildConvertMethodBody(writer: ClassWriter, cursorParam: ParameterSpec)
+            : CodeBlock {
+        val scope = CodeGenScope(writer)
+        val entityVar = scope.getTmpVar("_entity")
+        scope.builder().apply {
+            scope.builder().addStatement("final $T $L", entity.typeName, entityVar)
+            val fieldsWithIndices = entity.fields.map {
+                val indexVar = scope.getTmpVar(
+                        "_cursorIndexOf${it.name.stripNonJava().capitalize()}")
+                scope.builder().addStatement("final $T $L = $N.getColumnIndex($S)",
+                        TypeName.INT, indexVar, cursorParam, it.columnName)
+                FieldWithIndex(field = it,
+                        indexVar = indexVar,
+                        alwaysExists = false)
+            }
+            FieldReadWriteWriter.readFromCursor(
+                    outVar = entityVar,
+                    outPojo = entity,
+                    cursorVar = cursorParam.name,
+                    fieldsWithIndices = fieldsWithIndices,
+                    relationCollectors = emptyList(), // no relationship for entities
+                    scope = scope)
+            addStatement("return $L", entityVar)
+        }
+        return scope.builder().build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityDeletionAdapterWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityDeletionAdapterWriter.kt
new file mode 100644
index 0000000..29ba984
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityDeletionAdapterWriter.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.SupportDbTypeNames
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.FieldWithIndex
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier.PUBLIC
+
+class EntityDeletionAdapterWriter(val entity: Entity) {
+    fun createAnonymous(classWriter: ClassWriter, dbParam: String): TypeSpec {
+        @Suppress("RemoveSingleExpressionStringTemplate")
+        return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
+            superclass(ParameterizedTypeName.get(RoomTypeNames.DELETE_OR_UPDATE_ADAPTER,
+                    entity.typeName)
+            )
+            addMethod(MethodSpec.methodBuilder("createQuery").apply {
+                addAnnotation(Override::class.java)
+                returns(ClassName.get("java.lang", "String"))
+                addModifiers(PUBLIC)
+                val query = "DELETE FROM `${entity.tableName}` WHERE " +
+                        entity.primaryKey.fields.joinToString(" AND ") {
+                            "`${it.columnName}` = ?"
+                        }
+                addStatement("return $S", query)
+            }.build())
+            addMethod(MethodSpec.methodBuilder("bind").apply {
+                val bindScope = CodeGenScope(classWriter)
+                addAnnotation(Override::class.java)
+                val stmtParam = "stmt"
+                addParameter(ParameterSpec.builder(SupportDbTypeNames.SQLITE_STMT,
+                        stmtParam).build())
+                val valueParam = "value"
+                addParameter(ParameterSpec.builder(entity.typeName, valueParam).build())
+                returns(TypeName.VOID)
+                addModifiers(PUBLIC)
+                val mapped = FieldWithIndex.byOrder(entity.primaryKey.fields)
+                FieldReadWriteWriter.bindToStatement(ownerVar = valueParam,
+                        stmtParamVar = stmtParam,
+                        fieldsWithIndices = mapped,
+                        scope = bindScope)
+                addCode(bindScope.builder().build())
+            }.build())
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityInsertionAdapterWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityInsertionAdapterWriter.kt
new file mode 100644
index 0000000..cd3e771
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityInsertionAdapterWriter.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.SupportDbTypeNames
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.FieldWithIndex
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier.PUBLIC
+
+class EntityInsertionAdapterWriter(val entity: Entity, val onConflict: String) {
+    fun createAnonymous(classWriter: ClassWriter, dbParam : String): TypeSpec {
+        @Suppress("RemoveSingleExpressionStringTemplate")
+        return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
+            superclass(
+                    ParameterizedTypeName.get(RoomTypeNames.INSERTION_ADAPTER, entity.typeName)
+            )
+
+            // If there is an auto-increment primary key with primitive type, we consider 0 as
+            // not set. For such fields, we must generate a slightly different insertion SQL.
+            val primitiveAutoGenerateField = if (entity.primaryKey.autoGenerateId) {
+                entity.primaryKey.fields.firstOrNull()?.let { field ->
+                    field.statementBinder?.typeMirror()?.let { binderType ->
+                        if (binderType.kind.isPrimitive) {
+                            field
+                        } else {
+                            null
+                        }
+                    }
+                }
+            } else {
+                null
+            }
+            addMethod(MethodSpec.methodBuilder("createQuery").apply {
+                addAnnotation(Override::class.java)
+                returns(ClassName.get("java.lang", "String"))
+                addModifiers(PUBLIC)
+                val query =
+                        "INSERT OR $onConflict INTO `${entity.tableName}`(" +
+                                entity.fields.joinToString(",") {
+                                    "`${it.columnName}`"
+                                } + ") VALUES (" +
+                                entity.fields.joinToString(",") {
+                                    if (primitiveAutoGenerateField == it) {
+                                        "nullif(?, 0)"
+                                    } else {
+                                        "?"
+                                    }
+                                } + ")"
+                addStatement("return $S", query)
+            }.build())
+            addMethod(MethodSpec.methodBuilder("bind").apply {
+                val bindScope = CodeGenScope(classWriter)
+                addAnnotation(Override::class.java)
+                val stmtParam = "stmt"
+                addParameter(ParameterSpec.builder(SupportDbTypeNames.SQLITE_STMT,
+                        stmtParam).build())
+                val valueParam = "value"
+                addParameter(ParameterSpec.builder(entity.typeName, valueParam).build())
+                returns(TypeName.VOID)
+                addModifiers(PUBLIC)
+                val mapped = FieldWithIndex.byOrder(entity.fields)
+                FieldReadWriteWriter.bindToStatement(
+                        ownerVar = valueParam,
+                        stmtParamVar = stmtParam,
+                        fieldsWithIndices = mapped,
+                        scope = bindScope
+                )
+                addCode(bindScope.builder().build())
+            }.build())
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityUpdateAdapterWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityUpdateAdapterWriter.kt
new file mode 100644
index 0000000..b5711dc
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/EntityUpdateAdapterWriter.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.SupportDbTypeNames
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.FieldWithIndex
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier.PUBLIC
+
+class EntityUpdateAdapterWriter(val entity: Entity, val onConflict : String) {
+    fun createAnonymous(classWriter: ClassWriter, dbParam: String): TypeSpec {
+        @Suppress("RemoveSingleExpressionStringTemplate")
+        return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
+            superclass(ParameterizedTypeName.get(RoomTypeNames.DELETE_OR_UPDATE_ADAPTER,
+                    entity.typeName)
+            )
+            addMethod(MethodSpec.methodBuilder("createQuery").apply {
+                addAnnotation(Override::class.java)
+                returns(ClassName.get("java.lang", "String"))
+                addModifiers(PUBLIC)
+                val query = "UPDATE OR $onConflict `${entity.tableName}` SET " +
+                        entity.fields.joinToString(",") { field ->
+                            "`${field.columnName}` = ?"
+                        } + " WHERE " + entity.primaryKey.fields.joinToString(" AND ") {
+                            "`${it.columnName}` = ?"
+                        }
+                addStatement("return $S", query)
+            }.build())
+            addMethod(MethodSpec.methodBuilder("bind").apply {
+                val bindScope = CodeGenScope(classWriter)
+                addAnnotation(Override::class.java)
+                val stmtParam = "stmt"
+                addParameter(ParameterSpec.builder(SupportDbTypeNames.SQLITE_STMT,
+                        stmtParam).build())
+                val valueParam = "value"
+                addParameter(ParameterSpec.builder(entity.typeName, valueParam).build())
+                returns(TypeName.VOID)
+                addModifiers(PUBLIC)
+                val mappedField = FieldWithIndex.byOrder(entity.fields)
+                FieldReadWriteWriter.bindToStatement(
+                        ownerVar = valueParam,
+                        stmtParamVar = stmtParam,
+                        fieldsWithIndices = mappedField,
+                        scope = bindScope
+                )
+                val pkeyStart = entity.fields.size
+                val mappedPrimaryKeys = entity.primaryKey.fields.mapIndexed { index, field ->
+                    FieldWithIndex(field = field,
+                            indexVar = "${pkeyStart + index + 1}",
+                            alwaysExists = true)
+                }
+                FieldReadWriteWriter.bindToStatement(
+                        ownerVar = valueParam,
+                        stmtParamVar = stmtParam,
+                        fieldsWithIndices = mappedPrimaryKeys,
+                        scope = bindScope
+                )
+                addCode(bindScope.builder().build())
+            }.build())
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/FieldReadWriteWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/FieldReadWriteWriter.kt
new file mode 100644
index 0000000..8df3feb
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/FieldReadWriteWriter.kt
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.defaultValue
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.CallType
+import android.arch.persistence.room.vo.Constructor
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.FieldWithIndex
+import android.arch.persistence.room.vo.Pojo
+import android.arch.persistence.room.vo.RelationCollector
+import com.squareup.javapoet.TypeName
+
+/**
+ * Handles writing a field into statement or reading it form statement.
+ */
+class FieldReadWriteWriter(fieldWithIndex: FieldWithIndex) {
+    val field = fieldWithIndex.field
+    val indexVar = fieldWithIndex.indexVar
+    val alwaysExists = fieldWithIndex.alwaysExists
+
+    companion object {
+        /*
+         * Get all parents including the ones which have grand children in this list but does not
+         * have any direct children in the list.
+         */
+        fun getAllParents(fields: List<Field>): Set<EmbeddedField> {
+            val allParents = mutableSetOf<EmbeddedField>()
+            fun addAllParents(field: Field) {
+                var parent = field.parent
+                while (parent != null) {
+                    if (allParents.add(parent)) {
+                        parent = parent.parent
+                    } else {
+                        break
+                    }
+                }
+            }
+            fields.forEach(::addAllParents)
+            return allParents
+        }
+
+        /**
+         * Convert the fields with indices into a Node tree so that we can recursively process
+         * them. This work is done here instead of parsing because the result may include arbitrary
+         * fields.
+         */
+        private fun createNodeTree(rootVar: String,
+                           fieldsWithIndices: List<FieldWithIndex>,
+                           scope: CodeGenScope): Node {
+            val allParents = getAllParents(fieldsWithIndices.map { it.field })
+            val rootNode = Node(rootVar, null)
+            rootNode.directFields = fieldsWithIndices.filter { it.field.parent == null }
+            val parentNodes = allParents.associate {
+                Pair(it, Node(
+                        varName = scope.getTmpVar("_tmp${it.field.name.capitalize()}"),
+                        fieldParent = it))
+            }
+            parentNodes.values.forEach { node ->
+                val fieldParent = node.fieldParent!!
+                val grandParent = fieldParent.parent
+                val grandParentNode = grandParent?.let {
+                    parentNodes[it]
+                } ?: rootNode
+                node.directFields = fieldsWithIndices.filter { it.field.parent == fieldParent }
+                node.parentNode = grandParentNode
+                grandParentNode.subNodes.add(node)
+            }
+            return rootNode
+        }
+
+        fun bindToStatement(ownerVar: String, stmtParamVar: String,
+                            fieldsWithIndices: List<FieldWithIndex>,
+                            scope: CodeGenScope) {
+            fun visitNode(node: Node) {
+                fun bindWithDescendants() {
+                    node.directFields.forEach {
+                        FieldReadWriteWriter(it).bindToStatement(
+                                ownerVar = node.varName,
+                                stmtParamVar = stmtParamVar,
+                                scope = scope
+                        )
+                    }
+                    node.subNodes.forEach(::visitNode)
+                }
+
+                val fieldParent = node.fieldParent
+                if (fieldParent != null) {
+                    fieldParent.getter.writeGet(
+                            ownerVar = node.parentNode!!.varName,
+                            outVar = node.varName,
+                            builder = scope.builder()
+                    )
+                    scope.builder().apply {
+                        beginControlFlow("if($L != null)", node.varName).apply {
+                            bindWithDescendants()
+                        }
+                        nextControlFlow("else").apply {
+                            node.allFields().forEach {
+                                addStatement("$L.bindNull($L)", stmtParamVar, it.indexVar)
+                            }
+                        }
+                        endControlFlow()
+                    }
+                } else {
+                    bindWithDescendants()
+                }
+            }
+            visitNode(createNodeTree(ownerVar, fieldsWithIndices, scope))
+        }
+
+        /**
+         * Just constructs the given item, does NOT DECLARE. Declaration happens outside the
+         * reading statement since we may never read if the cursor does not have necessary
+         * columns.
+         */
+        private fun construct(outVar : String, constructor : Constructor?, typeName : TypeName,
+                              localVariableNames : Map<String, FieldWithIndex>,
+                              localEmbeddeds: List<Node>, scope: CodeGenScope) {
+            if (constructor == null) {
+                // best hope code generation
+                scope.builder().apply {
+                    addStatement("$L = new $T()", outVar, typeName)
+                }
+                return
+            }
+            val variableNames = constructor.params.map { param ->
+                when(param) {
+                    is Constructor.FieldParam -> localVariableNames.entries.firstOrNull {
+                        it.value.field === param.field
+                    }?.key
+                    is Constructor.EmbeddedParam -> localEmbeddeds.firstOrNull {
+                        it.fieldParent === param.embedded
+                    }?.varName
+                    else -> null
+                }
+            }
+            val args = variableNames.joinToString(",") { it ?: "null"}
+            scope.builder().apply {
+                addStatement("$L = new $T($L)", outVar, typeName, args)
+            }
+        }
+
+        /**
+         * Reads the row into the given variable. It does not declare it but constructs it.
+         */
+        fun readFromCursor(outVar: String,
+                           outPojo : Pojo,
+                           cursorVar: String,
+                           fieldsWithIndices: List<FieldWithIndex>,
+                           scope: CodeGenScope,
+                           relationCollectors : List<RelationCollector>) {
+            fun visitNode(node: Node) {
+                val fieldParent = node.fieldParent
+                fun readNode() {
+                    // read constructor parameters into local fields
+                    val constructorFields = node.directFields.filter {
+                        it.field.setter.callType == CallType.CONSTRUCTOR
+                    }.associateBy { fwi ->
+                        FieldReadWriteWriter(fwi).readIntoTmpVar(cursorVar, scope)
+                    }
+                    // read decomposed fields
+                    node.subNodes.forEach(::visitNode)
+                    // construct the object
+                    if (fieldParent != null) {
+                        construct(outVar = node.varName,
+                                constructor = fieldParent.pojo.constructor,
+                                typeName = fieldParent.field.typeName,
+                                localEmbeddeds = node.subNodes,
+                                localVariableNames = constructorFields,
+                                scope = scope)
+                    } else {
+                        construct(outVar = node.varName,
+                                constructor = outPojo.constructor,
+                                typeName = outPojo.typeName,
+                                localEmbeddeds = node.subNodes,
+                                localVariableNames = constructorFields,
+                                scope = scope)
+                    }
+                    // ready any field that was not part of the constructor
+                    node.directFields.filterNot {
+                        it.field.setter.callType == CallType.CONSTRUCTOR
+                    }.forEach { fwi ->
+                        FieldReadWriteWriter(fwi).readFromCursor(
+                                ownerVar = node.varName,
+                                cursorVar = cursorVar,
+                                scope = scope)
+                    }
+                    // assign relationship fields which will be read later
+                    relationCollectors.filter { (relation) ->
+                        relation.field.parent === fieldParent
+                    }.forEach {
+                        it.writeReadParentKeyCode(
+                                cursorVarName = cursorVar,
+                                itemVar = node.varName,
+                                fieldsWithIndices = fieldsWithIndices,
+                                scope = scope)
+                    }
+                    // assign sub modes to fields if they were not part of the constructor.
+                    node.subNodes.map {
+                        val setter = it.fieldParent?.setter
+                        if (setter != null && setter.callType != CallType.CONSTRUCTOR) {
+                            Pair(it.varName, setter)
+                        } else {
+                            null
+                        }
+                    }.filterNotNull().forEach { (varName, setter) ->
+                        setter.writeSet(
+                                ownerVar = node.varName,
+                                inVar = varName,
+                                builder = scope.builder())
+                    }
+                }
+                if (fieldParent == null) {
+                    // root element
+                    // always declared by the caller so we don't declare this
+                    readNode()
+                } else {
+                    // always declare, we'll set below
+                    scope.builder().addStatement("final $T $L", fieldParent.pojo.typeName,
+                                        node.varName)
+                    if (fieldParent.nonNull) {
+                        readNode()
+                    } else {
+                        val myDescendants = node.allFields()
+                        val allNullCheck = myDescendants.joinToString(" && ") {
+                            if (it.alwaysExists) {
+                                "$cursorVar.isNull(${it.indexVar})"
+                            } else {
+                                "( ${it.indexVar} == -1 || $cursorVar.isNull(${it.indexVar}))"
+                            }
+
+                        }
+                        scope.builder().apply {
+                            beginControlFlow("if (! ($L))", allNullCheck).apply {
+                                readNode()
+                            }
+                            nextControlFlow(" else ").apply {
+                                addStatement("$L = null", node.varName)
+                            }
+                            endControlFlow()
+                        }
+                    }
+                }
+            }
+            visitNode(createNodeTree(outVar, fieldsWithIndices, scope))
+        }
+    }
+
+    /**
+     * @param ownerVar The entity / pojo that owns this field. It must own this field! (not the
+     * container pojo)
+     * @param stmtParamVar The statement variable
+     * @param scope The code generation scope
+     */
+    private fun bindToStatement(ownerVar: String, stmtParamVar: String, scope: CodeGenScope) {
+        field.statementBinder?.let { binder ->
+            val varName = if (field.getter.callType == CallType.FIELD) {
+                "$ownerVar.${field.name}"
+            } else {
+                "$ownerVar.${field.getter.name}()"
+            }
+            binder.bindToStmt(stmtParamVar, indexVar, varName, scope)
+        }
+    }
+
+    /**
+     * @param ownerVar The entity / pojo that owns this field. It must own this field (not the
+     * container pojo)
+     * @param cursorVar The cursor variable
+     * @param scope The code generation scope
+     */
+    private fun readFromCursor(ownerVar: String, cursorVar: String, scope: CodeGenScope) {
+        fun toRead() {
+            field.cursorValueReader?.let { reader ->
+                scope.builder().apply {
+                    when (field.setter.callType) {
+                        CallType.FIELD -> {
+                            reader.readFromCursor("$ownerVar.${field.getter.name}", cursorVar,
+                                    indexVar, scope)
+                        }
+                        CallType.METHOD -> {
+                            val tmpField = scope.getTmpVar("_tmp${field.name.capitalize()}")
+                            addStatement("final $T $L", field.getter.type.typeName(), tmpField)
+                            reader.readFromCursor(tmpField, cursorVar, indexVar, scope)
+                            addStatement("$L.$L($L)", ownerVar, field.setter.name, tmpField)
+                        }
+                        CallType.CONSTRUCTOR -> {
+                            // no-op
+                        }
+                    }
+                }
+            }
+        }
+        if (alwaysExists) {
+            toRead()
+        } else {
+            scope.builder().apply {
+                beginControlFlow("if ($L != -1)", indexVar).apply {
+                    toRead()
+                }
+                endControlFlow()
+            }
+        }
+    }
+
+    /**
+     * Reads the value into a temporary local variable.
+     */
+    fun readIntoTmpVar(cursorVar: String, scope: CodeGenScope) : String {
+        val tmpField = scope.getTmpVar("_tmp${field.name.capitalize()}")
+        val typeName = field.getter.type.typeName()
+        scope.builder().apply {
+            addStatement("final $T $L", typeName, tmpField)
+            if (alwaysExists) {
+                field.cursorValueReader?.readFromCursor(tmpField, cursorVar, indexVar, scope)
+            } else {
+                beginControlFlow("if ($L == -1)", indexVar).apply {
+                    addStatement("$L = $L", tmpField, typeName.defaultValue())
+                }
+                nextControlFlow("else").apply {
+                    field.cursorValueReader?.readFromCursor(tmpField, cursorVar, indexVar, scope)
+                }
+                endControlFlow()
+            }
+        }
+        return tmpField
+    }
+
+    /**
+     * On demand node which is created based on the fields that were passed into this class.
+     */
+    private class Node(
+            // root for me
+            val varName: String,
+            // set if I'm a FieldParent
+            val fieldParent: EmbeddedField?) {
+        // whom do i belong
+        var parentNode: Node? = null
+        // these fields are my direct fields
+        lateinit var directFields: List<FieldWithIndex>
+        // these nodes are under me
+        val subNodes = mutableListOf<Node>()
+
+        fun allFields(): List<FieldWithIndex> {
+            return directFields + subNodes.flatMap { it.allFields() }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/PreparedStatementWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/PreparedStatementWriter.kt
new file mode 100644
index 0000000..9c3e612
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/PreparedStatementWriter.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.solver.CodeGenScope
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+/**
+ * Creates anonymous classes for RoomTypeNames#SHARED_SQLITE_STMT.
+ */
+class PreparedStatementWriter(val queryWriter: QueryWriter) {
+    fun createAnonymous(classWriter: ClassWriter, dbParam : FieldSpec): TypeSpec {
+        val scope = CodeGenScope(classWriter)
+        @Suppress("RemoveSingleExpressionStringTemplate")
+        return TypeSpec.anonymousClassBuilder("$N", dbParam).apply {
+            superclass(RoomTypeNames.SHARED_SQLITE_STMT)
+            addMethod(MethodSpec.methodBuilder("createQuery").apply {
+                addAnnotation(Override::class.java)
+                returns(ClassName.get("java.lang", "String"))
+                addModifiers(Modifier.PUBLIC)
+                val queryName = scope.getTmpVar("_query")
+                val queryGenScope = scope.fork()
+                queryWriter.prepareQuery(queryName, queryGenScope)
+                addCode(queryGenScope.builder().build())
+                addStatement("return $L", queryName)
+            }.build())
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/QueryWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/QueryWriter.kt
new file mode 100644
index 0000000..2bf471a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/QueryWriter.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.RoomTypeNames.ROOM_SQL_QUERY
+import android.arch.persistence.room.ext.RoomTypeNames.STRING_UTIL
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.ParsedQuery
+import android.arch.persistence.room.parser.Section
+import android.arch.persistence.room.parser.SectionType.BIND_VAR
+import android.arch.persistence.room.parser.SectionType.NEWLINE
+import android.arch.persistence.room.parser.SectionType.TEXT
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.QueryMethod
+import android.arch.persistence.room.vo.QueryParameter
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+
+/**
+ * Writes the SQL query and arguments for a QueryMethod.
+ */
+class QueryWriter constructor(val parameters : List<QueryParameter>,
+                              val sectionToParamMapping : List<Pair<Section, QueryParameter?>>,
+                              val query : ParsedQuery) {
+
+    constructor(queryMethod: QueryMethod) : this(queryMethod.parameters,
+            queryMethod.sectionToParamMapping, queryMethod.query) {
+    }
+
+    fun prepareReadAndBind(outSqlQueryName: String, outRoomSQLiteQueryVar: String,
+                           scope: CodeGenScope) {
+        val listSizeVars = createSqlQueryAndArgs(outSqlQueryName, outRoomSQLiteQueryVar, scope)
+        bindArgs(outRoomSQLiteQueryVar, listSizeVars, scope)
+    }
+
+    fun prepareQuery(outSqlQueryName: String, scope: CodeGenScope)
+            : List<Pair<QueryParameter, String>> {
+        return createSqlQueryAndArgs(outSqlQueryName, null, scope)
+    }
+
+    private fun createSqlQueryAndArgs(outSqlQueryName: String, outArgsName: String?,
+                                      scope: CodeGenScope): List<Pair<QueryParameter, String>> {
+        val listSizeVars = arrayListOf<Pair<QueryParameter, String>>()
+        val varargParams = parameters
+                .filter { it.queryParamAdapter?.isMultiple ?: false }
+        val sectionToParamMapping = sectionToParamMapping
+        val knownQueryArgsCount = sectionToParamMapping.filterNot {
+            it.second?.queryParamAdapter?.isMultiple ?: false
+        }.size
+        scope.builder().apply {
+            if (varargParams.isNotEmpty()) {
+                val stringBuilderVar = scope.getTmpVar("_stringBuilder")
+                addStatement("$T $L = $T.newStringBuilder()",
+                        ClassName.get(StringBuilder::class.java), stringBuilderVar, STRING_UTIL)
+                query.sections.forEach {
+                    when (it.type) {
+                        TEXT -> addStatement("$L.append($S)", stringBuilderVar, it.text)
+                        NEWLINE -> addStatement("$L.append($S)", "\n")
+                        BIND_VAR -> {
+                            // If it is null, will be reported as error before. We just try out
+                            // best to generate as much code as possible.
+                            sectionToParamMapping.firstOrNull { mapping ->
+                                mapping.first == it
+                            }?.let { pair ->
+                                if (pair.second?.queryParamAdapter?.isMultiple ?: false) {
+                                    val tmpCount = scope.getTmpVar("_inputSize")
+                                    listSizeVars.add(Pair(pair.second!!, tmpCount))
+                                    pair.second
+                                            ?.queryParamAdapter
+                                            ?.getArgCount(pair.second!!.name, tmpCount, scope)
+                                    addStatement("$T.appendPlaceholders($L, $L)",
+                                            STRING_UTIL, stringBuilderVar, tmpCount)
+                                } else {
+                                    addStatement("$L.append($S)", stringBuilderVar, "?")
+                                }
+                            }
+                        }
+                    }
+                }
+
+                addStatement("final $T $L = $L.toString()", String::class.typeName(),
+                        outSqlQueryName, stringBuilderVar)
+                if (outArgsName != null) {
+                    val argCount = scope.getTmpVar("_argCount")
+                    addStatement("final $T $L = $L$L", TypeName.INT, argCount, knownQueryArgsCount,
+                            listSizeVars.joinToString("") { " + ${it.second}" })
+                    addStatement("final $T $L = $T.acquire($L, $L)",
+                            ROOM_SQL_QUERY, outArgsName, ROOM_SQL_QUERY, outSqlQueryName,
+                            argCount)
+                }
+            } else {
+                addStatement("final $T $L = $S", String::class.typeName(),
+                        outSqlQueryName, query.queryWithReplacedBindParams)
+                if (outArgsName != null) {
+                    addStatement("final $T $L = $T.acquire($L, $L)",
+                            ROOM_SQL_QUERY, outArgsName, ROOM_SQL_QUERY, outSqlQueryName,
+                            knownQueryArgsCount)
+                }
+            }
+        }
+        return listSizeVars
+    }
+
+    fun bindArgs(outArgsName: String, listSizeVars : List<Pair<QueryParameter, String>>,
+                         scope: CodeGenScope) {
+        if (parameters.isEmpty()) {
+            return
+        }
+        scope.builder().apply {
+            val argIndex = scope.getTmpVar("_argIndex")
+            addStatement("$T $L = $L", TypeName.INT, argIndex, 1)
+            // # of bindings with 1 placeholder
+            var constInputs = 0
+            // variable names for size of the bindings that have multiple  args
+            val varInputs = arrayListOf<String>()
+            sectionToParamMapping.forEach { pair ->
+                // reset the argIndex to the correct start index
+                if (constInputs > 0 || varInputs.isNotEmpty()) {
+                    addStatement("$L = $L$L", argIndex,
+                            if (constInputs > 0) (1 + constInputs) else "1",
+                            varInputs.joinToString("") { " + $it" })
+                }
+                val param = pair.second
+                param?.let {
+                    param.queryParamAdapter?.bindToStmt(param.name, outArgsName, argIndex, scope)
+                }
+                // add these to the list so that we can use them to calculate the next count.
+                val sizeVar = listSizeVars.firstOrNull { it.first == param }
+                if (sizeVar == null) {
+                    constInputs ++
+                } else {
+                    varInputs.add(sizeVar.second)
+                }
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/RelationCollectorMethodWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/RelationCollectorMethodWriter.kt
new file mode 100644
index 0000000..092cbb0
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/RelationCollectorMethodWriter.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.AndroidTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.RelationCollector
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import stripNonJava
+import javax.lang.model.element.Modifier
+
+/**
+ * Writes the method that fetches the relations of a POJO and assigns them into the given map.
+ */
+class RelationCollectorMethodWriter(val collector: RelationCollector)
+    : ClassWriter.SharedMethodSpec(
+        "fetchRelationship${collector.relation.entity.tableName.stripNonJava()}" +
+                "As${collector.relation.pojo.typeName.toString().stripNonJava()}") {
+    companion object {
+        val KEY_SET_VARIABLE = "__mapKeySet"
+    }
+    override fun getUniqueKey(): String {
+        val relation = collector.relation
+        return "RelationCollectorMethodWriter" +
+                "-${collector.mapTypeName}" +
+                "-${relation.entity.typeName}" +
+                "-${relation.entityField.columnName}" +
+                "-${relation.pojo.typeName}" +
+                "-${relation.createLoadAllSql()}"
+    }
+
+    override fun prepare(writer: ClassWriter, builder: MethodSpec.Builder) {
+        val scope = CodeGenScope(writer)
+        val relation = collector.relation
+
+        val param = ParameterSpec.builder(collector.mapTypeName, "_map")
+                .addModifiers(Modifier.FINAL)
+                .build()
+        val sqlQueryVar = scope.getTmpVar("_sql")
+        val keySetVar = KEY_SET_VARIABLE
+
+        val cursorVar = "_cursor"
+        val itemKeyIndexVar = "_itemKeyIndex"
+        val stmtVar = scope.getTmpVar("_stmt")
+        scope.builder().apply {
+
+            val keySetType = ParameterizedTypeName.get(
+                    ClassName.get(Set::class.java), collector.keyTypeName
+            )
+            addStatement("final $T $L = $N.keySet()", keySetType, keySetVar, param)
+            beginControlFlow("if ($L.isEmpty())", keySetVar).apply {
+                addStatement("return")
+            }
+            endControlFlow()
+            collector.queryWriter.prepareReadAndBind(sqlQueryVar, stmtVar, scope)
+
+            addStatement("final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, cursorVar,
+                    DaoWriter.dbField, stmtVar)
+
+            beginControlFlow("try").apply {
+                addStatement("final $T $L = $L.getColumnIndex($S)",
+                        TypeName.INT, itemKeyIndexVar, cursorVar, relation.entityField.columnName)
+
+                beginControlFlow("if ($L == -1)", itemKeyIndexVar).apply {
+                    addStatement("return")
+                }
+                endControlFlow()
+
+                collector.rowAdapter.onCursorReady(cursorVar, scope)
+                val tmpVarName = scope.getTmpVar("_item")
+                beginControlFlow("while($L.moveToNext())", cursorVar).apply {
+                    // read key from the cursor
+                    collector.readKey(
+                            cursorVarName = cursorVar,
+                            indexVar = itemKeyIndexVar,
+                            scope = scope
+                    ) { keyVar ->
+                        val collectionVar = scope.getTmpVar("_tmpCollection")
+                        addStatement("$T $L = $N.get($L)", collector.collectionTypeName,
+                                collectionVar, param, keyVar)
+                        beginControlFlow("if ($L != null)", collectionVar).apply {
+                            addStatement("final $T $L", relation.pojo.typeName, tmpVarName)
+                            collector.rowAdapter.convert(tmpVarName, cursorVar, scope)
+                            addStatement("$L.add($L)", collectionVar, tmpVarName)
+                        }
+                        endControlFlow()
+                    }
+                }
+                endControlFlow()
+                collector.rowAdapter.onCursorFinished()?.invoke(scope)
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$L.close()", cursorVar)
+            }
+            endControlFlow()
+        }
+        builder.apply {
+            addModifiers(Modifier.PRIVATE)
+            addParameter(param)
+            returns(TypeName.VOID)
+            addCode(scope.builder().build())
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/SQLiteOpenHelperWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/SQLiteOpenHelperWriter.kt
new file mode 100644
index 0000000..afb12e9
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/SQLiteOpenHelperWriter.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.SupportDbTypeNames
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Database
+import android.arch.persistence.room.vo.Entity
+import android.support.annotation.VisibleForTesting
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier.PROTECTED
+import javax.lang.model.element.Modifier.PUBLIC
+
+/**
+ * Create an open helper using SupportSQLiteOpenHelperFactory
+ */
+class SQLiteOpenHelperWriter(val database : Database) {
+    fun write(outVar : String, configuration : ParameterSpec, scope: CodeGenScope) {
+        scope.builder().apply {
+            val sqliteConfigVar = scope.getTmpVar("_sqliteConfig")
+            val callbackVar = scope.getTmpVar("_openCallback")
+            addStatement("final $T $L = new $T($N, $L, $S)",
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK,
+                    callbackVar, RoomTypeNames.OPEN_HELPER, configuration,
+                    createOpenCallback(scope), database.identityHash)
+            // build configuration
+            addStatement(
+                    """
+                    final $T $L = $T.builder($N.context)
+                    .name($N.name)
+                    .version($L)
+                    .callback($L)
+                    .build()
+                    """.trimIndent(),
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, sqliteConfigVar,
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG,
+                    configuration, configuration, database.version, callbackVar)
+            addStatement("final $T $N = $N.sqliteOpenHelperFactory.create($L)",
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER, outVar,
+                    configuration, sqliteConfigVar)
+        }
+    }
+
+    private fun createOpenCallback(scope: CodeGenScope) : TypeSpec {
+        return TypeSpec.anonymousClassBuilder("").apply {
+            superclass(RoomTypeNames.OPEN_HELPER_DELEGATE)
+            addMethod(createCreateAllTables())
+            addMethod(createDropAllTables())
+            addMethod(createOnCreate(scope.fork()))
+            addMethod(createOnOpen(scope.fork()))
+            addMethod(createValidateMigration(scope.fork()))
+        }.build()
+    }
+
+    private fun createValidateMigration(scope: CodeGenScope): MethodSpec {
+        return MethodSpec.methodBuilder("validateMigration").apply {
+            addModifiers(PROTECTED)
+            val dbParam = ParameterSpec.builder(SupportDbTypeNames.DB, "_db").build()
+            addParameter(dbParam)
+            database.entities.forEach { entity ->
+                val methodScope = scope.fork()
+                TableInfoValidationWriter(entity).write(dbParam, methodScope)
+                addCode(methodScope.builder().build())
+            }
+        }.build()
+    }
+
+    private fun createOnCreate(scope: CodeGenScope): MethodSpec {
+        return MethodSpec.methodBuilder("onCreate").apply {
+            addModifiers(PROTECTED)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            invokeCallbacks(scope, "onCreate")
+        }.build()
+    }
+
+    private fun createOnOpen(scope: CodeGenScope): MethodSpec {
+        return MethodSpec.methodBuilder("onOpen").apply {
+            addModifiers(PUBLIC)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            addStatement("mDatabase = _db")
+            if (database.enableForeignKeys) {
+                addStatement("_db.execSQL($S)", "PRAGMA foreign_keys = ON")
+            }
+            addStatement("internalInitInvalidationTracker(_db)")
+            invokeCallbacks(scope, "onOpen")
+        }.build()
+    }
+
+    private fun createCreateAllTables() : MethodSpec {
+        return MethodSpec.methodBuilder("createAllTables").apply {
+            addModifiers(PUBLIC)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            database.bundle.buildCreateQueries().forEach {
+                addStatement("_db.execSQL($S)", it)
+            }
+        }.build()
+    }
+
+    private fun createDropAllTables() : MethodSpec {
+        return MethodSpec.methodBuilder("dropAllTables").apply {
+            addModifiers(PUBLIC)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            database.entities.forEach {
+                addStatement("_db.execSQL($S)", createDropTableQuery(it))
+            }
+        }.build()
+    }
+
+    private fun MethodSpec.Builder.invokeCallbacks(scope: CodeGenScope, methodName: String) {
+        val iVar = scope.getTmpVar("_i")
+        val sizeVar = scope.getTmpVar("_size")
+        beginControlFlow("if (mCallbacks != null)").apply {
+            beginControlFlow("for (int $N = 0, $N = mCallbacks.size(); $N < $N; $N++)",
+                    iVar, sizeVar, iVar, sizeVar, iVar).apply {
+                addStatement("mCallbacks.get($N).$N(_db)", iVar, methodName)
+            }
+            endControlFlow()
+        }
+        endControlFlow()
+    }
+
+    @VisibleForTesting
+    fun createQuery(entity : Entity) : String {
+        return entity.createTableQuery
+    }
+
+    @VisibleForTesting
+    fun createDropTableQuery(entity: Entity) : String {
+        return "DROP TABLE IF EXISTS `${entity.tableName}`"
+    }
+}
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/TableInfoValidationWriter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/TableInfoValidationWriter.kt
new file mode 100644
index 0000000..e27600a
--- /dev/null
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/writer/TableInfoValidationWriter.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.ext.CommonTypeNames
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.N
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.vo.Entity
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import stripNonJava
+import java.util.Arrays
+import java.util.HashMap
+import java.util.HashSet
+
+class TableInfoValidationWriter(val entity : Entity) {
+    fun write(dbParam : ParameterSpec, scope : CodeGenScope) {
+        val suffix = entity.tableName.stripNonJava().capitalize()
+        val expectedInfoVar = scope.getTmpVar("_info$suffix")
+        scope.builder().apply {
+            val columnListVar = scope.getTmpVar("_columns$suffix")
+            val columnListType = ParameterizedTypeName.get(HashMap::class.typeName(),
+                    CommonTypeNames.STRING, RoomTypeNames.TABLE_INFO_COLUMN)
+
+            addStatement("final $T $L = new $T($L)", columnListType, columnListVar,
+                    columnListType, entity.fields.size)
+            entity.fields.forEachIndexed { index, field ->
+                addStatement("$L.put($S, new $T($S, $S, $L, $L))",
+                        columnListVar, field.columnName, RoomTypeNames.TABLE_INFO_COLUMN,
+                        /*name*/ field.columnName,
+                        /*type*/ field.affinity?.name ?: SQLTypeAffinity.TEXT.name,
+                        /*nonNull*/ field.nonNull,
+                        /*pkeyPos*/ entity.primaryKey.fields.indexOf(field) + 1)
+            }
+
+            val foreignKeySetVar = scope.getTmpVar("_foreignKeys$suffix")
+            val foreignKeySetType = ParameterizedTypeName.get(HashSet::class.typeName(),
+                    RoomTypeNames.TABLE_INFO_FOREIGN_KEY)
+            addStatement("final $T $L = new $T($L)", foreignKeySetType, foreignKeySetVar,
+                    foreignKeySetType, entity.foreignKeys.size)
+            entity.foreignKeys.forEach {
+                val myColumnNames = it.childFields
+                        .joinToString(",") { "\"${it.columnName}\"" }
+                val refColumnNames = it.parentColumns
+                        .joinToString(",") { "\"$it\"" }
+                addStatement("$L.add(new $T($S, $S, $S," +
+                        "$T.asList($L), $T.asList($L)))", foreignKeySetVar,
+                        RoomTypeNames.TABLE_INFO_FOREIGN_KEY,
+                        /*parent table*/ it.parentTable,
+                        /*on delete*/ it.onDelete.sqlName,
+                        /*on update*/ it.onUpdate.sqlName,
+                        Arrays::class.typeName(),
+                        /*parent names*/ myColumnNames,
+                        Arrays::class.typeName(),
+                        /*parent column names*/ refColumnNames)
+            }
+
+            addStatement("final $T $L = new $T($S, $L, $L)",
+                    RoomTypeNames.TABLE_INFO, expectedInfoVar, RoomTypeNames.TABLE_INFO,
+                    entity.tableName, columnListVar, foreignKeySetVar)
+
+            val existingVar = scope.getTmpVar("_existing$suffix")
+            addStatement("final $T $L = $T.read($N, $S)",
+                    RoomTypeNames.TABLE_INFO, existingVar, RoomTypeNames.TABLE_INFO,
+                    dbParam, entity.tableName)
+
+            beginControlFlow("if (! $L.equals($L))", expectedInfoVar, existingVar).apply {
+                addStatement("throw new $T($S + $L + $S + $L)",
+                        IllegalStateException::class.typeName(),
+                        "Migration didn't properly handle ${entity.tableName}" +
+                                "(${entity.element.qualifiedName}).\n Expected:\n",
+                        expectedInfoVar, "\n Found:\n", existingVar)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..f628f64
--- /dev/null
+++ b/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.arch.persistence.room.RoomProcessor
diff --git a/room/compiler/src/main/resources/NOTICE.txt b/room/compiler/src/main/resources/NOTICE.txt
new file mode 100644
index 0000000..fd8adbc
--- /dev/null
+++ b/room/compiler/src/main/resources/NOTICE.txt
@@ -0,0 +1,1812 @@
+List of 3rd party licenses:
+-----------------------------------------------------------------------------
+* commons-codec.jar (commons-codec:commons-codec:1.10)
+
+ ****** NOTICE:
+Apache Commons Codec
+Copyright 2002-2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
+contains test data from http://aspell.net/test/orig/batch0.tab.
+Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
+
+===============================================================================
+
+The content of package org.apache.commons.codec.language.bm has been translated
+from the original php source code available at http://stevemorse.org/phoneticinfo.htm
+with permission from the original authors.
+Original source copyright:
+Copyright (c) 2008 Alexander Beider & Stephen P. Morse.
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* javapoet.jar (com.squareup:javapoet:1.8.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* antlr4.jar (org.antlr:antlr4:4.5.3)
+
+ ****** LICENSE:
+[The "BSD 3-clause license"]
+Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holder nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=====
+
+MIT License for codepointat.js from https://git.io/codepointat
+MIT License for fromcodepoint.js from https://git.io/vDW1m
+
+Copyright Mathias Bynens <https://mathiasbynens.be/>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+
+-----------------------------------------------------------------------------
+* kotlin-stdlib.jar (org.jetbrains.kotlin:kotlin-stdlib:1.1.1)
+
+ ****** NOTICE:
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Kotlin Compiler distribution.                 ==
+   =========================================================================
+
+   Kotlin Compiler
+   Copyright 2010-2015 JetBrains s.r.o and respective authors and developers
+
+ ****** LICENSE:
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * 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.
+ */
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* auto-common.jar (com.google.auto:auto-common:0.6)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* annotations.jar (org.jetbrains:annotations:13.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* gson.jar (com.google.code.gson:gson:2.8.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
+
+-----------------------------------------------------------------------------
+* sqlite-jdbc.jar (org.xerial:sqlite-jdbc:3.16.1)
+
+ ****** LICENSE:
+

+                                 Apache License

+                           Version 2.0, January 2004

+                        http://www.apache.org/licenses/

+

+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+

+   1. Definitions.

+

+      "License" shall mean the terms and conditions for use, reproduction,

+      and distribution as defined by Sections 1 through 9 of this document.

+

+      "Licensor" shall mean the copyright owner or entity authorized by

+      the copyright owner that is granting the License.

+

+      "Legal Entity" shall mean the union of the acting entity and all

+      other entities that control, are controlled by, or are under common

+      control with that entity. For the purposes of this definition,

+      "control" means (i) the power, direct or indirect, to cause the

+      direction or management of such entity, whether by contract or

+      otherwise, or (ii) ownership of fifty percent (50%) or more of the

+      outstanding shares, or (iii) beneficial ownership of such entity.

+

+      "You" (or "Your") shall mean an individual or Legal Entity

+      exercising permissions granted by this License.

+

+      "Source" form shall mean the preferred form for making modifications,

+      including but not limited to software source code, documentation

+      source, and configuration files.

+

+      "Object" form shall mean any form resulting from mechanical

+      transformation or translation of a Source form, including but

+      not limited to compiled object code, generated documentation,

+      and conversions to other media types.

+

+      "Work" shall mean the work of authorship, whether in Source or

+      Object form, made available under the License, as indicated by a

+      copyright notice that is included in or attached to the work

+      (an example is provided in the Appendix below).

+

+      "Derivative Works" shall mean any work, whether in Source or Object

+      form, that is based on (or derived from) the Work and for which the

+      editorial revisions, annotations, elaborations, or other modifications

+      represent, as a whole, an original work of authorship. For the purposes

+      of this License, Derivative Works shall not include works that remain

+      separable from, or merely link (or bind by name) to the interfaces of,

+      the Work and Derivative Works thereof.

+

+      "Contribution" shall mean any work of authorship, including

+      the original version of the Work and any modifications or additions

+      to that Work or Derivative Works thereof, that is intentionally

+      submitted to Licensor for inclusion in the Work by the copyright owner

+      or by an individual or Legal Entity authorized to submit on behalf of

+      the copyright owner. For the purposes of this definition, "submitted"

+      means any form of electronic, verbal, or written communication sent

+      to the Licensor or its representatives, including but not limited to

+      communication on electronic mailing lists, source code control systems,

+      and issue tracking systems that are managed by, or on behalf of, the

+      Licensor for the purpose of discussing and improving the Work, but

+      excluding communication that is conspicuously marked or otherwise

+      designated in writing by the copyright owner as "Not a Contribution."

+

+      "Contributor" shall mean Licensor and any individual or Legal Entity

+      on behalf of whom a Contribution has been received by Licensor and

+      subsequently incorporated within the Work.

+

+   2. Grant of Copyright License. Subject to the terms and conditions of

+      this License, each Contributor hereby grants to You a perpetual,

+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable

+      copyright license to reproduce, prepare Derivative Works of,

+      publicly display, publicly perform, sublicense, and distribute the

+      Work and such Derivative Works in Source or Object form.

+

+   3. Grant of Patent License. Subject to the terms and conditions of

+      this License, each Contributor hereby grants to You a perpetual,

+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable

+      (except as stated in this section) patent license to make, have made,

+      use, offer to sell, sell, import, and otherwise transfer the Work,

+      where such license applies only to those patent claims licensable

+      by such Contributor that are necessarily infringed by their

+      Contribution(s) alone or by combination of their Contribution(s)

+      with the Work to which such Contribution(s) was submitted. If You

+      institute patent litigation against any entity (including a

+      cross-claim or counterclaim in a lawsuit) alleging that the Work

+      or a Contribution incorporated within the Work constitutes direct

+      or contributory patent infringement, then any patent licenses

+      granted to You under this License for that Work shall terminate

+      as of the date such litigation is filed.

+

+   4. Redistribution. You may reproduce and distribute copies of the

+      Work or Derivative Works thereof in any medium, with or without

+      modifications, and in Source or Object form, provided that You

+      meet the following conditions:

+

+      (a) You must give any other recipients of the Work or

+          Derivative Works a copy of this License; and

+

+      (b) You must cause any modified files to carry prominent notices

+          stating that You changed the files; and

+

+      (c) You must retain, in the Source form of any Derivative Works

+          that You distribute, all copyright, patent, trademark, and

+          attribution notices from the Source form of the Work,

+          excluding those notices that do not pertain to any part of

+          the Derivative Works; and

+

+      (d) If the Work includes a "NOTICE" text file as part of its

+          distribution, then any Derivative Works that You distribute must

+          include a readable copy of the attribution notices contained

+          within such NOTICE file, excluding those notices that do not

+          pertain to any part of the Derivative Works, in at least one

+          of the following places: within a NOTICE text file distributed

+          as part of the Derivative Works; within the Source form or

+          documentation, if provided along with the Derivative Works; or,

+          within a display generated by the Derivative Works, if and

+          wherever such third-party notices normally appear. The contents

+          of the NOTICE file are for informational purposes only and

+          do not modify the License. You may add Your own attribution

+          notices within Derivative Works that You distribute, alongside

+          or as an addendum to the NOTICE text from the Work, provided

+          that such additional attribution notices cannot be construed

+          as modifying the License.

+

+      You may add Your own copyright statement to Your modifications and

+      may provide additional or different license terms and conditions

+      for use, reproduction, or distribution of Your modifications, or

+      for any such Derivative Works as a whole, provided Your use,

+      reproduction, and distribution of the Work otherwise complies with

+      the conditions stated in this License.

+

+   5. Submission of Contributions. Unless You explicitly state otherwise,

+      any Contribution intentionally submitted for inclusion in the Work

+      by You to the Licensor shall be under the terms and conditions of

+      this License, without any additional terms or conditions.

+      Notwithstanding the above, nothing herein shall supersede or modify

+      the terms of any separate license agreement you may have executed

+      with Licensor regarding such Contributions.

+

+   6. Trademarks. This License does not grant permission to use the trade

+      names, trademarks, service marks, or product names of the Licensor,

+      except as required for reasonable and customary use in describing the

+      origin of the Work and reproducing the content of the NOTICE file.

+

+   7. Disclaimer of Warranty. Unless required by applicable law or

+      agreed to in writing, Licensor provides the Work (and each

+      Contributor provides its Contributions) on an "AS IS" BASIS,

+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or

+      implied, including, without limitation, any warranties or conditions

+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A

+      PARTICULAR PURPOSE. You are solely responsible for determining the

+      appropriateness of using or redistributing the Work and assume any

+      risks associated with Your exercise of permissions under this License.

+

+   8. Limitation of Liability. In no event and under no legal theory,

+      whether in tort (including negligence), contract, or otherwise,

+      unless required by applicable law (such as deliberate and grossly

+      negligent acts) or agreed to in writing, shall any Contributor be

+      liable to You for damages, including any direct, indirect, special,

+      incidental, or consequential damages of any character arising as a

+      result of this License or out of the use or inability to use the

+      Work (including but not limited to damages for loss of goodwill,

+      work stoppage, computer failure or malfunction, or any and all

+      other commercial damages or losses), even if such Contributor

+      has been advised of the possibility of such damages.

+

+   9. Accepting Warranty or Additional Liability. While redistributing

+      the Work or Derivative Works thereof, You may choose to offer,

+      and charge a fee for, acceptance of support, warranty, indemnity,

+      or other liability obligations and/or rights consistent with this

+      License. However, in accepting such obligations, You may act only

+      on Your own behalf and on Your sole responsibility, not on behalf

+      of any other Contributor, and only if You agree to indemnify,

+      defend, and hold each Contributor harmless for any liability

+      incurred by, or claims asserted against, such Contributor by reason

+      of your accepting any such warranty or additional liability.

+

+   END OF TERMS AND CONDITIONS

+

+   APPENDIX: How to apply the Apache License to your work.

+

+      To apply the Apache License to your work, attach the following

+      boilerplate notice, with the fields enclosed by brackets "[]"

+      replaced with your own identifying information. (Don't include

+      the brackets!)  The text should be enclosed in the appropriate

+      comment syntax for the file format. We also recommend that a

+      file or class name and description of purpose be included on the

+      same "printed page" as the copyright notice for easier

+      identification within third-party archives.

+

+   Copyright [yyyy] [name of copyright owner]

+

+   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.

+
+ ****** LICENSE:
+Copyright (c) 2006, David Crawshaw.  All rights reserved.

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions

+are met:

+

+1. Redistributions of source code must retain the above copyright

+   notice, this list of conditions and the following disclaimer.

+2. Redistributions in binary form must reproduce the above copyright

+   notice, this list of conditions and the following disclaimer in the

+   documentation and/or other materials provided with the distribution.

+

+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND

+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE

+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL

+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS

+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY

+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

+SUCH DAMAGE.

+

+
+
+
+
+-----------------------------------------------------------------------------
+* guava.jar (com.google.guava:guava:18.0)
+
+ ****** LICENSE:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
+
+
diff --git a/documents-archive/tests/NO_DOCS b/room/compiler/src/test/data/IGNORE_CHECKSTYLE
similarity index 100%
copy from documents-archive/tests/NO_DOCS
copy to room/compiler/src/test/data/IGNORE_CHECKSTYLE
diff --git a/room/compiler/src/test/data/common/input/Book.java b/room/compiler/src/test/data/common/input/Book.java
new file mode 100644
index 0000000..00cf8b6
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/Book.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+@Entity
+public class Book {
+    @PrimaryKey
+    int bookId;
+    int uid;
+}
diff --git a/room/compiler/src/test/data/common/input/ComputableLiveData.java b/room/compiler/src/test/data/common/input/ComputableLiveData.java
new file mode 100644
index 0000000..f135244
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/ComputableLiveData.java
@@ -0,0 +1,9 @@
+//ComputableLiveData interface for tests
+package android.arch.lifecycle;
+import android.arch.lifecycle.LiveData;
+public abstract class ComputableLiveData<T> {
+    public ComputableLiveData(){}
+    abstract protected T compute();
+    public LiveData<T> getLiveData() {return null;}
+    public void invalidate() {}
+}
diff --git a/room/compiler/src/test/data/common/input/LiveData.java b/room/compiler/src/test/data/common/input/LiveData.java
new file mode 100644
index 0000000..3aea6ac
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/LiveData.java
@@ -0,0 +1,4 @@
+//LiveData interface for tests
+package android.arch.lifecycle;
+public class LiveData<T> {
+}
diff --git a/room/compiler/src/test/data/common/input/LiveLazyListProvider.java b/room/compiler/src/test/data/common/input/LiveLazyListProvider.java
new file mode 100644
index 0000000..4c12f05
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/LiveLazyListProvider.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.arch.util.paging;
+
+abstract public class LiveLazyListProvider<T> {
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/data/common/input/MultiPKeyEntity.java b/room/compiler/src/test/data/common/input/MultiPKeyEntity.java
new file mode 100644
index 0000000..dccb9dd
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/MultiPKeyEntity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+@Entity(primaryKeys = {"name", "lastName"})
+public class MultiPKeyEntity {
+    String name;
+    String lastName;
+}
diff --git a/room/compiler/src/test/data/common/input/NotAnEntity.java b/room/compiler/src/test/data/common/input/NotAnEntity.java
new file mode 100644
index 0000000..90c1dd4
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/NotAnEntity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+public class NotAnEntity {
+    int bookId;
+    int uid;
+}
diff --git a/room/compiler/src/test/data/common/input/Rx2Room.java b/room/compiler/src/test/data/common/input/Rx2Room.java
new file mode 100644
index 0000000..da474fd
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/Rx2Room.java
@@ -0,0 +1,5 @@
+// mock rx2 helper
+package android.arch.persistence.room;
+
+class RxRoom {
+}
diff --git a/room/compiler/src/test/data/common/input/User.java b/room/compiler/src/test/data/common/input/User.java
new file mode 100644
index 0000000..ce487d3
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/User.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+@Entity
+public class User {
+    @PrimaryKey
+    int uid;
+    String name;
+    private String lastName;
+    @ColumnInfo(name = "ageColumn")
+    public int age;
+
+    public String getLastName() {
+        return lastName;
+    }
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+}
diff --git a/room/compiler/src/test/data/common/input/reactivestreams/Publisher.java b/room/compiler/src/test/data/common/input/reactivestreams/Publisher.java
new file mode 100644
index 0000000..4ecc9f5
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/reactivestreams/Publisher.java
@@ -0,0 +1,4 @@
+// fake reactivestreams publisher
+package org.reactivestreams;
+public interface Publisher<T> {
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/data/common/input/rxjava2/Flowable.java b/room/compiler/src/test/data/common/input/rxjava2/Flowable.java
new file mode 100644
index 0000000..2d9d4d0
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/rxjava2/Flowable.java
@@ -0,0 +1,6 @@
+// fake rx flowable
+package io.reactivex;
+import org.reactivestreams.Publisher;
+
+public abstract class Flowable<T> implements Publisher<T> {
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/data/daoWriter/input/ComplexDao.java b/room/compiler/src/test/data/daoWriter/input/ComplexDao.java
new file mode 100644
index 0000000..ca90163
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/input/ComplexDao.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+import java.util.List;
+import android.arch.lifecycle.LiveData;
+@Dao
+abstract class ComplexDao {
+    static class FullName {
+        public int id;
+        public String fullName;
+    }
+
+    private final ComplexDatabase mDb;
+
+    public ComplexDao(ComplexDatabase db) {
+        mDb = db;
+    }
+
+    @Query("SELECT name || lastName as fullName, uid as id FROM user where uid = :id")
+    abstract public List<FullName> fullNames(int id);
+
+    @Query("SELECT * FROM user where uid = :id")
+    abstract public User getById(int id);
+
+    @Query("SELECT * FROM user where name LIKE :name AND lastName LIKE :lastName")
+    abstract public User findByName(String name, String lastName);
+
+    @Query("SELECT * FROM user where uid IN (:ids)")
+    abstract public List<User> loadAllByIds(int... ids);
+
+    @Query("SELECT ageColumn FROM user where uid = :id")
+    abstract int getAge(int id);
+
+    @Query("SELECT ageColumn FROM user where uid IN(:ids)")
+    abstract public int[] getAllAges(int... ids);
+
+    @Query("SELECT ageColumn FROM user where uid IN(:ids)")
+    abstract public List<Integer> getAllAgesAsList(List<Integer> ids);
+
+    @Query("SELECT * FROM user where uid = :id")
+    abstract public LiveData<User> getByIdLive(int id);
+
+    @Query("SELECT * FROM user where uid IN (:ids)")
+    abstract public LiveData<List<User>> loadUsersByIdsLive(int... ids);
+
+    @Query("SELECT ageColumn FROM user where uid IN(:ids1) OR uid IN (:ids2) OR uid IN (:ids3)")
+    abstract public List<Integer> getAllAgesAsList(List<Integer> ids1,
+            int[] ids2, int... ids3);
+}
diff --git a/room/compiler/src/test/data/daoWriter/input/DeletionDao.java b/room/compiler/src/test/data/daoWriter/input/DeletionDao.java
new file mode 100644
index 0000000..997f290
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/input/DeletionDao.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+import java.util.List;
+
+@Dao
+abstract interface DeletionDao {
+    @Delete
+    void deleteUser(User user);
+    @Delete
+    void deleteUsers(User user1, List<User> others);
+    @Delete
+    void deleteArrayOfUsers(User[] users);
+
+    @Delete
+    int deleteUserAndReturnCount(User user);
+    @Delete
+    int deleteUserAndReturnCount(User user1, List<User> others);
+    @Delete
+    int deleteUserAndReturnCount(User[] users);
+
+    @Delete
+    int multiPKey(MultiPKeyEntity entity);
+
+    @Query("DELETE FROM user where uid = :uid")
+    int deleteByUid(int uid);
+
+    @Query("DELETE FROM user where uid IN(:uid)")
+    int deleteByUidList(int... uid);
+
+    @Delete
+    void deleteUserAndBook(User user, Book book);
+
+    @Query("DELETE FROM user")
+    int deleteEverything();
+}
diff --git a/room/compiler/src/test/data/daoWriter/input/UpdateDao.java b/room/compiler/src/test/data/daoWriter/input/UpdateDao.java
new file mode 100644
index 0000000..040b5c7
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/input/UpdateDao.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+import java.util.List;
+
+@Dao
+abstract interface UpdateDao {
+    @Update
+    void updateUser(User user);
+    @Update
+    void updateUsers(User user1, List<User> others);
+    @Update
+    void updateArrayOfUsers(User[] users);
+
+    @Update
+    int updateUserAndReturnCount(User user);
+    @Update
+    int updateUserAndReturnCount(User user1, List<User> others);
+    @Update
+    int updateUserAndReturnCount(User[] users);
+
+    @Update
+    int multiPKey(MultiPKeyEntity entity);
+
+    @Update
+    void updateUserAndBook(User user, Book book);
+
+    @Query("UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = :uid")
+    void ageUserByUid(String uid);
+
+    @Query("UPDATE User SET ageColumn = ageColumn + 1")
+    void ageUserAll();
+}
diff --git a/room/compiler/src/test/data/daoWriter/input/WriterDao.java b/room/compiler/src/test/data/daoWriter/input/WriterDao.java
new file mode 100644
index 0000000..e122479
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/input/WriterDao.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+import java.util.List;
+
+@Dao
+abstract interface WriterDao {
+    @Insert
+    void insertUser(User user);
+    @Insert
+    void insertUsers(User user1, List<User> others);
+    @Insert(onConflict=OnConflictStrategy.REPLACE)
+    void insertUsers(User[] users);
+    @Insert
+    void insertUserAndBook(User user, Book book);
+}
diff --git a/room/compiler/src/test/data/daoWriter/output/ComplexDao.java b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
new file mode 100644
index 0000000..61594d2
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
@@ -0,0 +1,419 @@
+package foo.bar;
+
+import android.arch.lifecycle.ComputableLiveData;
+import android.arch.lifecycle.LiveData;
+import android.arch.persistence.room.InvalidationTracker.Observer;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.RoomSQLiteQuery;
+import android.arch.persistence.room.util.StringUtil;
+import android.database.Cursor;
+import android.support.annotation.NonNull;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ComplexDao_Impl extends ComplexDao {
+    private final RoomDatabase __db;
+
+    public ComplexDao_Impl(ComplexDatabase __db) {
+        super(__db);
+        this.__db = __db;
+    }
+
+    @Override
+    public List<ComplexDao.FullName> fullNames(int id) {
+        final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+        int _argIndex = 1;
+        _statement.bindLong(_argIndex, id);
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final int _cursorIndexOfFullName = _cursor.getColumnIndexOrThrow("fullName");
+            final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
+            final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>(_cursor.getCount());
+            while(_cursor.moveToNext()) {
+                final ComplexDao.FullName _item;
+                _item = new ComplexDao.FullName();
+                _item.fullName = _cursor.getString(_cursorIndexOfFullName);
+                _item.id = _cursor.getInt(_cursorIndexOfId);
+                _result.add(_item);
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    public User getById(int id) {
+        final String _sql = "SELECT * FROM user where uid = ?";
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+        int _argIndex = 1;
+        _statement.bindLong(_argIndex, id);
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
+            final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
+            final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
+            final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
+            final User _result;
+            if(_cursor.moveToFirst()) {
+                _result = new User();
+                _result.uid = _cursor.getInt(_cursorIndexOfUid);
+                _result.name = _cursor.getString(_cursorIndexOfName);
+                final String _tmpLastName;
+                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+                _result.setLastName(_tmpLastName);
+                _result.age = _cursor.getInt(_cursorIndexOfAge);
+            } else {
+                _result = null;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    public User findByName(String name, String lastName) {
+        final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 2);
+        int _argIndex = 1;
+        if (name == null) {
+            _statement.bindNull(_argIndex);
+        } else {
+            _statement.bindString(_argIndex, name);
+        }
+        _argIndex = 2;
+        if (lastName == null) {
+            _statement.bindNull(_argIndex);
+        } else {
+            _statement.bindString(_argIndex, lastName);
+        }
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
+            final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
+            final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
+            final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
+            final User _result;
+            if(_cursor.moveToFirst()) {
+                _result = new User();
+                _result.uid = _cursor.getInt(_cursorIndexOfUid);
+                _result.name = _cursor.getString(_cursorIndexOfName);
+                final String _tmpLastName;
+                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+                _result.setLastName(_tmpLastName);
+                _result.age = _cursor.getInt(_cursorIndexOfAge);
+            } else {
+                _result = null;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    public List<User> loadAllByIds(int... ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT * FROM user where uid IN (");
+        final int _inputSize = ids.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        final String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+        int _argIndex = 1;
+        for (int _item : ids) {
+            _statement.bindLong(_argIndex, _item);
+            _argIndex ++;
+        }
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
+            final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
+            final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
+            final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
+            final List<User> _result = new ArrayList<User>(_cursor.getCount());
+            while(_cursor.moveToNext()) {
+                final User _item_1;
+                _item_1 = new User();
+                _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
+                _item_1.name = _cursor.getString(_cursorIndexOfName);
+                final String _tmpLastName;
+                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+                _item_1.setLastName(_tmpLastName);
+                _item_1.age = _cursor.getInt(_cursorIndexOfAge);
+                _result.add(_item_1);
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    int getAge(int id) {
+        final String _sql = "SELECT ageColumn FROM user where uid = ?";
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+        int _argIndex = 1;
+        _statement.bindLong(_argIndex, id);
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final int _result;
+            if(_cursor.moveToFirst()) {
+                _result = _cursor.getInt(0);
+            } else {
+                _result = 0;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    public int[] getAllAges(int... ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+        final int _inputSize = ids.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        final String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+        int _argIndex = 1;
+        for (int _item : ids) {
+            _statement.bindLong(_argIndex, _item);
+            _argIndex ++;
+        }
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final int[] _result = new int[_cursor.getCount()];
+            int _index = 0;
+            while(_cursor.moveToNext()) {
+                final int _item_1;
+                _item_1 = _cursor.getInt(0);
+                _result[_index] = _item_1;
+                _index ++;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    public List<Integer> getAllAgesAsList(List<Integer> ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+        final int _inputSize = ids.size();
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        final String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+        int _argIndex = 1;
+        for (Integer _item : ids) {
+            if (_item == null) {
+                _statement.bindNull(_argIndex);
+            } else {
+                _statement.bindLong(_argIndex, _item);
+            }
+            _argIndex ++;
+        }
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
+            while(_cursor.moveToNext()) {
+                final Integer _item_1;
+                if (_cursor.isNull(0)) {
+                    _item_1 = null;
+                } else {
+                    _item_1 = _cursor.getInt(0);
+                }
+                _result.add(_item_1);
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+
+    @Override
+    public LiveData<User> getByIdLive(int id) {
+        final String _sql = "SELECT * FROM user where uid = ?";
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+        int _argIndex = 1;
+        _statement.bindLong(_argIndex, id);
+        return new ComputableLiveData<User>() {
+            private Observer _observer;
+
+            @Override
+            protected User compute() {
+                if (_observer == null) {
+                    _observer = new Observer("user") {
+                        @Override
+                        public void onInvalidated(@NonNull Set<String> tables) {
+                            invalidate();
+                        }
+                    };
+                    __db.getInvalidationTracker().addWeakObserver(_observer);
+                }
+                final Cursor _cursor = __db.query(_statement);
+                try {
+                    final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
+                    final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
+                    final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
+                    final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
+                    final User _result;
+                    if(_cursor.moveToFirst()) {
+                        _result = new User();
+                        _result.uid = _cursor.getInt(_cursorIndexOfUid);
+                        _result.name = _cursor.getString(_cursorIndexOfName);
+                        final String _tmpLastName;
+                        _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+                        _result.setLastName(_tmpLastName);
+                        _result.age = _cursor.getInt(_cursorIndexOfAge);
+                    } else {
+                        _result = null;
+                    }
+                    return _result;
+                } finally {
+                    _cursor.close();
+                }
+            }
+
+            @Override
+            protected void finalize() {
+                _statement.release();
+            }
+        }.getLiveData();
+    }
+
+    @Override
+    public LiveData<List<User>> loadUsersByIdsLive(int... ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT * FROM user where uid IN (");
+        final int _inputSize = ids.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        final String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+        int _argIndex = 1;
+        for (int _item : ids) {
+            _statement.bindLong(_argIndex, _item);
+            _argIndex ++;
+        }
+        return new ComputableLiveData<List<User>>() {
+            private Observer _observer;
+
+            @Override
+            protected List<User> compute() {
+                if (_observer == null) {
+                    _observer = new Observer("user") {
+                        @Override
+                        public void onInvalidated(@NonNull Set<String> tables) {
+                            invalidate();
+                        }
+                    };
+                    __db.getInvalidationTracker().addWeakObserver(_observer);
+                }
+                final Cursor _cursor = __db.query(_statement);
+                try {
+                    final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
+                    final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
+                    final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
+                    final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
+                    final List<User> _result = new ArrayList<User>(_cursor.getCount());
+                    while(_cursor.moveToNext()) {
+                        final User _item_1;
+                        _item_1 = new User();
+                        _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
+                        _item_1.name = _cursor.getString(_cursorIndexOfName);
+                        final String _tmpLastName;
+                        _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+                        _item_1.setLastName(_tmpLastName);
+                        _item_1.age = _cursor.getInt(_cursorIndexOfAge);
+                        _result.add(_item_1);
+                    }
+                    return _result;
+                } finally {
+                    _cursor.close();
+                }
+            }
+
+            @Override
+            protected void finalize() {
+                _statement.release();
+            }
+        }.getLiveData();
+    }
+
+    @Override
+    public List<Integer> getAllAgesAsList(List<Integer> ids1, int[] ids2, int... ids3) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+        final int _inputSize = ids1.size();
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(") OR uid IN (");
+        final int _inputSize_1 = ids2.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
+        _stringBuilder.append(") OR uid IN (");
+        final int _inputSize_2 = ids3.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
+        _stringBuilder.append(")");
+        final String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize + _inputSize_1 + _inputSize_2;
+        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+        int _argIndex = 1;
+        for (Integer _item : ids1) {
+            if (_item == null) {
+                _statement.bindNull(_argIndex);
+            } else {
+                _statement.bindLong(_argIndex, _item);
+            }
+            _argIndex ++;
+        }
+        _argIndex = 1 + _inputSize;
+        for (int _item_1 : ids2) {
+            _statement.bindLong(_argIndex, _item_1);
+            _argIndex ++;
+        }
+        _argIndex = 1 + _inputSize + _inputSize_1;
+        for (int _item_2 : ids3) {
+            _statement.bindLong(_argIndex, _item_2);
+            _argIndex ++;
+        }
+        final Cursor _cursor = __db.query(_statement);
+        try {
+            final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
+            while(_cursor.moveToNext()) {
+                final Integer _item_3;
+                if (_cursor.isNull(0)) {
+                    _item_3 = null;
+                } else {
+                    _item_3 = _cursor.getInt(0);
+                }
+                _result.add(_item_3);
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+            _statement.release();
+        }
+    }
+}
diff --git a/room/compiler/src/test/data/daoWriter/output/DeletionDao.java b/room/compiler/src/test/data/daoWriter/output/DeletionDao.java
new file mode 100644
index 0000000..d5e173f
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/output/DeletionDao.java
@@ -0,0 +1,238 @@
+package foo.bar;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.arch.persistence.room.EntityDeletionOrUpdateAdapter;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.SharedSQLiteStatement;
+import android.arch.persistence.room.util.StringUtil;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.util.List;
+
+public class DeletionDao_Impl implements DeletionDao {
+  private final RoomDatabase __db;
+
+  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfUser;
+
+  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfMultiPKeyEntity;
+
+  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfBook;
+
+  private final SharedSQLiteStatement __preparedStmtOfDeleteByUid;
+
+  private final SharedSQLiteStatement __preparedStmtOfDeleteEverything;
+
+  public DeletionDao_Impl(RoomDatabase __db) {
+    this.__db = __db;
+    this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+      @Override
+      public String createQuery() {
+        return "DELETE FROM `User` WHERE `uid` = ?";
+      }
+
+      @Override
+      public void bind(SupportSQLiteStatement stmt, User value) {
+        stmt.bindLong(1, value.uid);
+      }
+    };
+    this.__deletionAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
+      @Override
+      public String createQuery() {
+        return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
+      }
+
+      @Override
+      public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
+        if (value.name == null) {
+          stmt.bindNull(1);
+        } else {
+          stmt.bindString(1, value.name);
+        }
+        if (value.lastName == null) {
+          stmt.bindNull(2);
+        } else {
+          stmt.bindString(2, value.lastName);
+        }
+      }
+    };
+    this.__deletionAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
+      @Override
+      public String createQuery() {
+        return "DELETE FROM `Book` WHERE `bookId` = ?";
+      }
+
+      @Override
+      public void bind(SupportSQLiteStatement stmt, Book value) {
+        stmt.bindLong(1, value.bookId);
+      }
+    };
+    this.__preparedStmtOfDeleteByUid = new SharedSQLiteStatement(__db) {
+      @Override
+      public String createQuery() {
+        final String _query = "DELETE FROM user where uid = ?";
+        return _query;
+      }
+    };
+    this.__preparedStmtOfDeleteEverything = new SharedSQLiteStatement(__db) {
+      @Override
+      public String createQuery() {
+        final String _query = "DELETE FROM user";
+        return _query;
+      }
+    };
+  }
+
+  @Override
+  public void deleteUser(User user) {
+    __db.beginTransaction();
+    try {
+      __deletionAdapterOfUser.handle(user);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void deleteUsers(User user1, List<User> others) {
+    __db.beginTransaction();
+    try {
+      __deletionAdapterOfUser.handle(user1);
+      __deletionAdapterOfUser.handleMultiple(others);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void deleteArrayOfUsers(User[] users) {
+    __db.beginTransaction();
+    try {
+      __deletionAdapterOfUser.handleMultiple(users);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int deleteUserAndReturnCount(User user) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__deletionAdapterOfUser.handle(user);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int deleteUserAndReturnCount(User user1, List<User> others) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__deletionAdapterOfUser.handle(user1);
+      _total +=__deletionAdapterOfUser.handleMultiple(others);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int deleteUserAndReturnCount(User[] users) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__deletionAdapterOfUser.handleMultiple(users);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int multiPKey(MultiPKeyEntity entity) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__deletionAdapterOfMultiPKeyEntity.handle(entity);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void deleteUserAndBook(User user, Book book) {
+    __db.beginTransaction();
+    try {
+      __deletionAdapterOfUser.handle(user);
+      __deletionAdapterOfBook.handle(book);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int deleteByUid(int uid) {
+    final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
+    __db.beginTransaction();
+    try {
+      int _argIndex = 1;
+      _stmt.bindLong(_argIndex, uid);
+      final int _result = _stmt.executeUpdateDelete();
+      __db.setTransactionSuccessful();
+      return _result;
+    } finally {
+      __db.endTransaction();
+      __preparedStmtOfDeleteByUid.release(_stmt);
+    }
+  }
+
+  @Override
+  public int deleteEverything() {
+    final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteEverything.acquire();
+    __db.beginTransaction();
+    try {
+      final int _result = _stmt.executeUpdateDelete();
+      __db.setTransactionSuccessful();
+      return _result;
+    } finally {
+      __db.endTransaction();
+      __preparedStmtOfDeleteEverything.release(_stmt);
+    }
+  }
+
+  @Override
+  public int deleteByUidList(int... uid) {
+    StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+    _stringBuilder.append("DELETE FROM user where uid IN(");
+    final int _inputSize = uid.length;
+    StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+    _stringBuilder.append(")");
+    final String _sql = _stringBuilder.toString();
+    SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    int _argIndex = 1;
+    for (int _item : uid) {
+      _stmt.bindLong(_argIndex, _item);
+      _argIndex ++;
+    }
+    __db.beginTransaction();
+    try {
+      final int _result = _stmt.executeUpdateDelete();
+      __db.setTransactionSuccessful();
+      return _result;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+}
diff --git a/room/compiler/src/test/data/daoWriter/output/UpdateDao.java b/room/compiler/src/test/data/daoWriter/output/UpdateDao.java
new file mode 100644
index 0000000..1b26b6a
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/output/UpdateDao.java
@@ -0,0 +1,238 @@
+package foo.bar;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.arch.persistence.room.EntityDeletionOrUpdateAdapter;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.SharedSQLiteStatement;
+import java.lang.Override;
+import java.lang.String;
+import java.util.List;
+
+public class UpdateDao_Impl implements UpdateDao {
+  private final RoomDatabase __db;
+
+  private final EntityDeletionOrUpdateAdapter __updateAdapterOfUser;
+
+  private final EntityDeletionOrUpdateAdapter __updateAdapterOfMultiPKeyEntity;
+
+  private final EntityDeletionOrUpdateAdapter __updateAdapterOfBook;
+
+  private final SharedSQLiteStatement __preparedStmtOfAgeUserByUid;
+
+  private final SharedSQLiteStatement __preparedStmtOfAgeUserAll;
+
+  public UpdateDao_Impl(RoomDatabase __db) {
+    this.__db = __db;
+    this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+      @Override
+      public String createQuery() {
+        return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+      }
+
+      @Override
+      public void bind(SupportSQLiteStatement stmt, User value) {
+        stmt.bindLong(1, value.uid);
+        if (value.name == null) {
+          stmt.bindNull(2);
+        } else {
+          stmt.bindString(2, value.name);
+        }
+        if (value.getLastName() == null) {
+          stmt.bindNull(3);
+        } else {
+          stmt.bindString(3, value.getLastName());
+        }
+        stmt.bindLong(4, value.age);
+        stmt.bindLong(5, value.uid);
+      }
+    };
+    this.__updateAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
+      @Override
+      public String createQuery() {
+        return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
+      }
+
+      @Override
+      public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
+        if (value.name == null) {
+          stmt.bindNull(1);
+        } else {
+          stmt.bindString(1, value.name);
+        }
+        if (value.lastName == null) {
+          stmt.bindNull(2);
+        } else {
+          stmt.bindString(2, value.lastName);
+        }
+        if (value.name == null) {
+          stmt.bindNull(3);
+        } else {
+          stmt.bindString(3, value.name);
+        }
+        if (value.lastName == null) {
+          stmt.bindNull(4);
+        } else {
+          stmt.bindString(4, value.lastName);
+        }
+      }
+    };
+    this.__updateAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
+      @Override
+      public String createQuery() {
+        return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+      }
+
+      @Override
+      public void bind(SupportSQLiteStatement stmt, Book value) {
+        stmt.bindLong(1, value.bookId);
+        stmt.bindLong(2, value.uid);
+        stmt.bindLong(3, value.bookId);
+      }
+    };
+    this.__preparedStmtOfAgeUserByUid = new SharedSQLiteStatement(__db) {
+      @Override
+      public String createQuery() {
+        final String _query = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
+        return _query;
+      }
+    };
+    this.__preparedStmtOfAgeUserAll = new SharedSQLiteStatement(__db) {
+      @Override
+      public String createQuery() {
+        final String _query = "UPDATE User SET ageColumn = ageColumn + 1";
+        return _query;
+      }
+    };
+  }
+
+  @Override
+  public void updateUser(User user) {
+    __db.beginTransaction();
+    try {
+      __updateAdapterOfUser.handle(user);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void updateUsers(User user1, List<User> others) {
+    __db.beginTransaction();
+    try {
+      __updateAdapterOfUser.handle(user1);
+      __updateAdapterOfUser.handleMultiple(others);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void updateArrayOfUsers(User[] users) {
+    __db.beginTransaction();
+    try {
+      __updateAdapterOfUser.handleMultiple(users);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int updateUserAndReturnCount(User user) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__updateAdapterOfUser.handle(user);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int updateUserAndReturnCount(User user1, List<User> others) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__updateAdapterOfUser.handle(user1);
+      _total +=__updateAdapterOfUser.handleMultiple(others);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int updateUserAndReturnCount(User[] users) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__updateAdapterOfUser.handleMultiple(users);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public int multiPKey(MultiPKeyEntity entity) {
+    int _total = 0;
+    __db.beginTransaction();
+    try {
+      _total +=__updateAdapterOfMultiPKeyEntity.handle(entity);
+      __db.setTransactionSuccessful();
+      return _total;
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void updateUserAndBook(User user, Book book) {
+    __db.beginTransaction();
+    try {
+      __updateAdapterOfUser.handle(user);
+      __updateAdapterOfBook.handle(book);
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+    }
+  }
+
+  @Override
+  public void ageUserByUid(String uid) {
+    final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserByUid.acquire();
+    __db.beginTransaction();
+    try {
+      int _argIndex = 1;
+      if (uid == null) {
+        _stmt.bindNull(_argIndex);
+      } else {
+        _stmt.bindString(_argIndex, uid);
+      }
+      _stmt.executeUpdateDelete();
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+      __preparedStmtOfAgeUserByUid.release(_stmt);
+    }
+  }
+
+  @Override
+  public void ageUserAll() {
+    final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
+    __db.beginTransaction();
+    try {
+      _stmt.executeUpdateDelete();
+      __db.setTransactionSuccessful();
+    } finally {
+      __db.endTransaction();
+      __preparedStmtOfAgeUserAll.release(_stmt);
+    }
+  }
+}
diff --git a/room/compiler/src/test/data/daoWriter/output/WriterDao.java b/room/compiler/src/test/data/daoWriter/output/WriterDao.java
new file mode 100644
index 0000000..76b9975
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/output/WriterDao.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.arch.persistence.room.EntityInsertionAdapter;
+import android.arch.persistence.room.RoomDatabase;
+
+import java.lang.Override;
+import java.lang.String;
+import java.util.List;
+
+public class WriterDao_Impl implements WriterDao {
+    private final RoomDatabase __db;
+
+    private final EntityInsertionAdapter __insertionAdapterOfUser;
+
+    private final EntityInsertionAdapter __insertionAdapterOfUser_1;
+
+    private final EntityInsertionAdapter __insertionAdapterOfBook;
+
+    public WriterDao_Impl(RoomDatabase __db) {
+        this.__db = __db;
+        this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT OR ABORT INTO `User`(`uid`,`name`,`lastName`,`ageColumn`) VALUES"
+                        + " (?,?,?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, User value) {
+                stmt.bindLong(1, value.uid);
+                if (value.name == null) {
+                    stmt.bindNull(2);
+                } else {
+                    stmt.bindString(2, value.name);
+                }
+                if (value.getLastName() == null) {
+                    stmt.bindNull(3);
+                } else {
+                    stmt.bindString(3, value.getLastName());
+                }
+                stmt.bindLong(4, value.age);
+            }
+        };
+        this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT OR REPLACE INTO `User`(`uid`,`name`,`lastName`,`ageColumn`) VALUES"
+                        + " (?,?,?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, User value) {
+                stmt.bindLong(1, value.uid);
+                if (value.name == null) {
+                    stmt.bindNull(2);
+                } else {
+                    stmt.bindString(2, value.name);
+                }
+                if (value.getLastName() == null) {
+                    stmt.bindNull(3);
+                } else {
+                    stmt.bindString(3, value.getLastName());
+                }
+                stmt.bindLong(4, value.age);
+            }
+        };
+        this.__insertionAdapterOfBook = new EntityInsertionAdapter<Book>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT OR ABORT INTO `Book`(`bookId`,`uid`) VALUES (?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, Book value) {
+                stmt.bindLong(1, value.bookId);
+                stmt.bindLong(2, value.uid);
+            }
+        };
+    }
+
+    @Override
+    public void insertUser(User user) {
+        __db.beginTransaction();
+        try {
+            __insertionAdapterOfUser.insert(user);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void insertUsers(User user1, List<User> others) {
+        __db.beginTransaction();
+        try {
+            __insertionAdapterOfUser.insert(user1);
+            __insertionAdapterOfUser.insert(others);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void insertUsers(User[] users) {
+        __db.beginTransaction();
+        try {
+            __insertionAdapterOfUser_1.insert(users);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void insertUserAndBook(User user, Book book) {
+        __db.beginTransaction();
+        try {
+            __insertionAdapterOfUser.insert(user);
+            __insertionAdapterOfBook.insert(book);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+}
diff --git a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
new file mode 100644
index 0000000..f35e0b8
--- /dev/null
+++ b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package foo.bar;
+import android.arch.persistence.room.*;
+import java.util.List;
+@Database(entities = {User.class}, version = 1923)
+abstract class ComplexDatabase extends RoomDatabase {
+    abstract ComplexDao getComplexDao();
+}
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
new file mode 100644
index 0000000..0846dff
--- /dev/null
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -0,0 +1,96 @@
+package foo.bar;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.SupportSQLiteOpenHelper.Callback;
+import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
+import android.arch.persistence.room.DatabaseConfiguration;
+import android.arch.persistence.room.InvalidationTracker;
+import android.arch.persistence.room.RoomOpenHelper;
+import android.arch.persistence.room.RoomOpenHelper.Delegate;
+import android.arch.persistence.room.util.TableInfo;
+import android.arch.persistence.room.util.TableInfo.Column;
+import android.arch.persistence.room.util.TableInfo.ForeignKey;
+import java.lang.IllegalStateException;
+import java.lang.Override;
+import java.lang.String;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class ComplexDatabase_Impl extends ComplexDatabase {
+    private volatile ComplexDao _complexDao;
+
+    protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
+        final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate() {
+            public void createAllTables(SupportSQLiteDatabase _db) {
+                _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`uid` INTEGER NOT NULL, `name` TEXT, `lastName` TEXT, `ageColumn` INTEGER NOT NULL, PRIMARY KEY(`uid`))");
+                _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
+                _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6773601c5bcf94c71ee4eb0de04f21a4\")");
+            }
+
+            public void dropAllTables(SupportSQLiteDatabase _db) {
+                _db.execSQL("DROP TABLE IF EXISTS `User`");
+            }
+
+            protected void onCreate(SupportSQLiteDatabase _db) {
+                if (mCallbacks != null) {
+                    for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
+                        mCallbacks.get(_i).onCreate(_db);
+                    }
+                }
+            }
+
+            public void onOpen(SupportSQLiteDatabase _db) {
+                mDatabase = _db;
+                internalInitInvalidationTracker(_db);
+                if (mCallbacks != null) {
+                    for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
+                        mCallbacks.get(_i).onOpen(_db);
+                    }
+                }
+            }
+
+            protected void validateMigration(SupportSQLiteDatabase _db) {
+                final HashMap<String, TableInfo.Column> _columnsUser = new HashMap<String, TableInfo.Column>(4);
+                _columnsUser.put("uid", new TableInfo.Column("uid", "INTEGER", true, 1));
+                _columnsUser.put("name", new TableInfo.Column("name", "TEXT", false, 0));
+                _columnsUser.put("lastName", new TableInfo.Column("lastName", "TEXT", false, 0));
+                _columnsUser.put("ageColumn", new TableInfo.Column("ageColumn", "INTEGER", true, 0));
+                final HashSet<TableInfo.ForeignKey> _foreignKeysUser = new HashSet<TableInfo.ForeignKey>(0);
+                final TableInfo _infoUser = new TableInfo("User", _columnsUser, _foreignKeysUser);
+                final TableInfo _existingUser = TableInfo.read(_db, "User");
+                if (! _infoUser.equals(_existingUser)) {
+                    throw new IllegalStateException("Migration didn't properly handle User(foo.bar.User).\n"
+                            + " Expected:\n" + _infoUser + "\n"
+                            + " Found:\n" + _existingUser);
+                }
+            }
+        }, "6773601c5bcf94c71ee4eb0de04f21a4");
+        final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
+                .name(configuration.name)
+                .version(1923)
+                .callback(_openCallback)
+                .build();
+        final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
+        return _helper;
+    }
+
+    @Override
+    protected InvalidationTracker createInvalidationTracker() {
+        return new InvalidationTracker(this, "User");
+    }
+
+    @Override
+    ComplexDao getComplexDao() {
+        if (_complexDao != null) {
+            return _complexDao;
+        } else {
+            synchronized(this) {
+                if(_complexDao == null) {
+                    _complexDao = new ComplexDao_Impl(this);
+                }
+                return _complexDao;
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/parser/SqlParserTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/parser/SqlParserTest.kt
new file mode 100644
index 0000000..68b1868
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/parser/SqlParserTest.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.parser
+
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class SqlParserTest {
+
+    @Test
+    fun multipleQueries() {
+        assertErrors("SELECT * FROM users; SELECT * FROM books;",
+                ParserErrors.NOT_ONE_QUERY)
+    }
+
+    @Test
+    fun empty() {
+        assertErrors("", ParserErrors.NOT_ONE_QUERY)
+    }
+
+    @Test
+    fun deleteQuery() {
+        val parsed = SqlParser.parse("DELETE FROM users where id > 3")
+        assertThat(parsed.errors, `is`(emptyList()))
+        assertThat(parsed.type, `is`(QueryType.DELETE))
+    }
+
+    @Test
+    fun badDeleteQuery() {
+        assertErrors("delete from user where mAge >= :min && mAge <= :max",
+                "no viable alternative at input 'delete from user where mAge >= :min &&'")
+    }
+
+    @Test
+    fun updateQuery() {
+        val parsed = SqlParser.parse("UPDATE users set name = :name where id = :id")
+        assertThat(parsed.errors, `is`(emptyList()))
+        assertThat(parsed.type, `is`(QueryType.UPDATE))
+    }
+
+    @Test
+    fun explain() {
+        assertErrors("EXPLAIN QUERY PLAN SELECT * FROM users",
+                ParserErrors.invalidQueryType(QueryType.EXPLAIN))
+    }
+
+    @Test
+    fun extractTableNames() {
+        assertThat(SqlParser.parse("select * from users").tables,
+                `is`(setOf(Table("users", "users"))))
+        assertThat(SqlParser.parse("select * from users as ux").tables,
+                `is`(setOf(Table("users", "ux"))))
+        assertThat(SqlParser.parse("select * from (select * from books)").tables,
+                `is`(setOf(Table("books", "books"))))
+        assertThat(SqlParser.parse("select x.id from (select * from books) as x").tables,
+                `is`(setOf(Table("books", "books"))))
+    }
+
+    @Test
+    fun unescapeTableNames() {
+        assertThat(SqlParser.parse("select * from `users`").tables,
+                `is`(setOf(Table("users", "users"))))
+        assertThat(SqlParser.parse("select * from \"users\"").tables,
+                `is`(setOf(Table("users", "users"))))
+    }
+
+    @Test
+    fun findBindVariables() {
+        assertVariables("select * from users")
+        assertVariables("select * from users where name like ?", "?")
+        assertVariables("select * from users where name like :name", ":name")
+        assertVariables("select * from users where name like ?2", "?2")
+        assertVariables("select * from users where name like ?2 OR name LIKE ?1", "?2", "?1")
+        assertVariables("select * from users where name like @a", "@a")
+        assertVariables("select * from users where name like \$a", "\$a")
+    }
+
+    @Test
+    fun  indexedVariablesError() {
+        assertErrors("select * from users where name like ?",
+                ParserErrors.ANONYMOUS_BIND_ARGUMENT)
+        assertErrors("select * from users where name like ? or last_name like ?",
+                ParserErrors.ANONYMOUS_BIND_ARGUMENT)
+        assertErrors("select * from users where name like ?1",
+                ParserErrors.cannotUseVariableIndices("?1", 36))
+    }
+
+    @Test
+    fun foo() {
+        assertSections("select * from users where name like ?",
+                Section.text("select * from users where name like "),
+                Section.bindVar("?"))
+
+        assertSections("select * from users where name like :name AND last_name like :lastName",
+                Section.text("select * from users where name like "),
+                Section.bindVar(":name"),
+                Section.text(" AND last_name like "),
+                Section.bindVar(":lastName"))
+
+        assertSections("select * from users where name \nlike :name AND last_name like :lastName",
+                Section.text("select * from users where name "),
+                Section.newline(),
+                Section.text("like "),
+                Section.bindVar(":name"),
+                Section.text(" AND last_name like "),
+                Section.bindVar(":lastName"))
+
+        assertSections("select * from users where name like :name \nAND last_name like :lastName",
+                Section.text("select * from users where name like "),
+                Section.bindVar(":name"),
+                Section.text(" "),
+                Section.newline(),
+                Section.text("AND last_name like "),
+                Section.bindVar(":lastName"))
+
+        assertSections("select * from users where name like :name \nAND last_name like \n:lastName",
+                Section.text("select * from users where name like "),
+                Section.bindVar(":name"),
+                Section.text(" "),
+                Section.newline(),
+                Section.text("AND last_name like "),
+                Section.newline(),
+                Section.bindVar(":lastName"))
+    }
+
+    fun assertVariables(query: String, vararg expected: String) {
+        assertThat((SqlParser.parse(query)).inputs.map { it.text }, `is`(expected.toList()))
+    }
+
+    fun assertErrors(query: String, vararg errors: String) {
+        assertThat((SqlParser.parse(query)).errors, `is`(errors.toList()))
+    }
+
+    fun assertSections(query: String, vararg sections: Section) {
+        assertThat(SqlParser.parse(query).sections, `is`(sections.toList()))
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/BaseDaoTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/BaseDaoTest.kt
new file mode 100644
index 0000000..ae648f7
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/BaseDaoTest.kt
@@ -0,0 +1,169 @@
+package android.arch.persistence.room.processor
+
+import COMMON
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.vo.Dao
+import android.arch.persistence.room.writer.DaoWriter
+import com.google.auto.common.MoreTypes
+import com.google.testing.compile.JavaFileObjects
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import simpleRun
+
+/**
+ * we don't assert much in these tests since if type resolution fails, compilation fails.
+ */
+@RunWith(JUnit4::class)
+class BaseDaoTest {
+    private fun String.toJFO(qName: String) = JavaFileObjects.forSourceLines(qName, this)
+
+    @Test
+    fun insert() {
+        baseDao("""
+            @Insert
+            void insertMe(T t);
+        """) { dao ->
+            assertThat(dao.insertionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun insertArray() {
+        baseDao("""
+            @Insert
+            void insertMe(T[] t);
+        """) { dao ->
+            assertThat(dao.insertionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun insertVarArg() {
+        baseDao("""
+            @Insert
+            void insertMe(T... t);
+        """) { dao ->
+            assertThat(dao.insertionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun insertList() {
+        baseDao("""
+            @Insert
+            void insertMe(List<T> t);
+        """) { dao ->
+            assertThat(dao.insertionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun delete() {
+        baseDao("""
+            @Delete
+            void deleteMe(T t);
+        """) { dao ->
+            assertThat(dao.deletionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun deleteArray() {
+        baseDao("""
+            @Delete
+            void deleteMe(T[] t);
+        """) { dao ->
+            assertThat(dao.deletionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun deleteVarArg() {
+        baseDao("""
+            @Delete
+            void deleteMe(T... t);
+        """) { dao ->
+            assertThat(dao.deletionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun deleteList() {
+        baseDao("""
+            @Delete
+            void deleteMe(List<T> t);
+        """) { dao ->
+            assertThat(dao.deletionMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun update() {
+        baseDao("""
+            @Update
+            void updateMe(T t);
+        """) { dao ->
+            assertThat(dao.updateMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun updateArray() {
+        baseDao("""
+            @Update
+            void updateMe(T[] t);
+        """) { dao ->
+            assertThat(dao.updateMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun updateVarArg() {
+        baseDao("""
+            @Update
+            void updateMe(T... t);
+        """) { dao ->
+            assertThat(dao.updateMethods.size, `is`(1))
+        }
+    }
+
+    @Test
+    fun updateList() {
+        baseDao("""
+            @Update
+            void updateMe(List<T> t);
+        """) { dao ->
+            assertThat(dao.updateMethods.size, `is`(1))
+        }
+    }
+
+    fun baseDao(code : String, handler : (Dao) -> Unit) {
+        val baseClass = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            import java.util.List;
+
+            interface BaseDao<K, T> {
+                $code
+            }
+        """.toJFO("foo.bar.BaseDao")
+        val extension = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            @Dao
+            interface MyDao extends BaseDao<Integer, User> {
+            }
+        """.toJFO("foo.bar.MyDao")
+        simpleRun(baseClass, extension, COMMON.USER) { invocation ->
+            val daoElm = invocation.processingEnv.elementUtils.getTypeElement("foo.bar.MyDao")
+            val dbType = MoreTypes.asDeclared(invocation.context.processingEnv.elementUtils
+                    .getTypeElement(RoomTypeNames.ROOM_DB.toString()).asType())
+            val processedDao = DaoProcessor(invocation.context, daoElm, dbType, null).process()
+            handler(processedDao)
+            DaoWriter(processedDao, invocation.processingEnv).write(invocation.processingEnv)
+        }.compilesWithoutError()
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/BaseEntityParserTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/BaseEntityParserTest.kt
new file mode 100644
index 0000000..9bfc0e7
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/BaseEntityParserTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Embedded
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.Entity
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourceSubjectFactory
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import javax.tools.JavaFileObject
+
+abstract class BaseEntityParserTest {
+    companion object {
+        const val ENTITY_PREFIX = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            import android.support.annotation.NonNull;
+            @Entity%s
+            public class MyEntity %s {
+            """
+        const val ENTITY_SUFFIX = "}"
+    }
+
+    fun singleEntity(input: String, attributes: Map<String, String> = mapOf(),
+                     baseClass : String = "",
+                     jfos : List<JavaFileObject> = emptyList(),
+                     handler: (Entity, TestInvocation) -> Unit): CompileTester {
+        val attributesReplacement : String
+        if (attributes.isEmpty()) {
+            attributesReplacement = ""
+        } else {
+            attributesReplacement = "(" +
+                    attributes.entries.map { "${it.key} = ${it.value}" }.joinToString(",") +
+                    ")".trimIndent()
+        }
+        val baseClassReplacement : String
+        if (baseClass == "") {
+            baseClassReplacement = ""
+        } else {
+            baseClassReplacement = " extends $baseClass"
+        }
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(jfos + JavaFileObjects.forSourceString("foo.bar.MyEntity",
+                        ENTITY_PREFIX.format(attributesReplacement, baseClassReplacement)
+                                + input + ENTITY_SUFFIX
+                ))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(android.arch.persistence.room.Entity::class,
+                                android.arch.persistence.room.PrimaryKey::class,
+                                android.arch.persistence.room.Ignore::class,
+                                Embedded::class,
+                                android.arch.persistence.room.ColumnInfo::class)
+                        .nextRunHandler { invocation ->
+                            val entity = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            android.arch.persistence.room.Entity::class.java)
+                                    .first { it.toString() == "foo.bar.MyEntity" }
+                            val parser = EntityProcessor(invocation.context,
+                                    MoreElements.asType(entity))
+                            val parsedQuery = parser.process()
+                            handler(parsedQuery, invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/CustomConverterProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/CustomConverterProcessorTest.kt
new file mode 100644
index 0000000..34bbd24
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/CustomConverterProcessorTest.kt
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.TypeConverter
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_EMPTY_CLASS
+import android.arch.persistence.room.processor.ProcessorErrors
+        .TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_BE_PUBLIC
+import android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_UNBOUND_GENERIC
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.vo.CustomTypeConverter
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import com.squareup.javapoet.TypeVariableName
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import simpleRun
+import java.util.Date
+import javax.lang.model.element.Modifier
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class CustomConverterProcessorTest {
+    companion object {
+        val CONVERTER = ClassName.get("foo.bar", "MyConverter")!!
+        val CONVERTER_QNAME = CONVERTER.packageName() + "." + CONVERTER.simpleName()
+        val CONTAINER = JavaFileObjects.forSourceString("foo.bar.Container",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @TypeConverters(foo.bar.MyConverter.class)
+                public class Container {}
+                """)
+    }
+
+    @Test
+    fun validCase() {
+        singleClass(createConverter(TypeName.SHORT.box(), TypeName.CHAR.box()))
+        { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT.box()))
+            assertThat(converter?.toTypeName, `is`(TypeName.CHAR.box()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun primitiveFrom() {
+        singleClass(createConverter(TypeName.SHORT, TypeName.CHAR.box())) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT))
+            assertThat(converter?.toTypeName, `is`(TypeName.CHAR.box()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun primitiveTo() {
+        singleClass(createConverter(TypeName.INT.box(), TypeName.DOUBLE)) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.INT.box()))
+            assertThat(converter?.toTypeName, `is`(TypeName.DOUBLE))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun primitiveBoth() {
+        singleClass(createConverter(TypeName.INT, TypeName.DOUBLE)) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.INT))
+            assertThat(converter?.toTypeName, `is`(TypeName.DOUBLE))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun nonNullButNotBoxed() {
+        val string = String::class.typeName()
+        val date = Date::class.typeName()
+        singleClass(createConverter(string, date)) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(string as TypeName))
+            assertThat(converter?.toTypeName, `is`(date as TypeName))
+        }
+    }
+
+    @Test
+    fun parametrizedTypeUnbound() {
+        val typeVarT = TypeVariableName.get("T")
+        val list = ParameterizedTypeName.get(List::class.typeName(), typeVarT)
+        val typeVarK = TypeVariableName.get("K")
+        val map = ParameterizedTypeName.get(Map::class.typeName(), typeVarK, typeVarT)
+        singleClass(createConverter(list, map, listOf(typeVarK, typeVarT))) {
+            converter, invocation ->
+        }.failsToCompile().withErrorContaining(TYPE_CONVERTER_UNBOUND_GENERIC)
+    }
+
+    @Test
+    fun parametrizedTypeSpecific() {
+        val string = String::class.typeName()
+        val date = Date::class.typeName()
+        val list = ParameterizedTypeName.get(List::class.typeName(), string)
+        val map = ParameterizedTypeName.get(Map::class.typeName(), string, date)
+        singleClass(createConverter(list, map)) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(list as TypeName))
+            assertThat(converter?.toTypeName, `is`(map as TypeName))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testNoConverters() {
+        singleClass(JavaFileObjects.forSourceString(CONVERTER_QNAME,
+                """
+                package ${CONVERTER.packageName()};
+                public class ${CONVERTER.simpleName()} {
+                }
+                """)) { converter, invocation ->
+        }.failsToCompile().withErrorContaining(TYPE_CONVERTER_EMPTY_CLASS)
+    }
+
+    @Test
+    fun checkNoArgConstructor() {
+        singleClass(JavaFileObjects.forSourceString(CONVERTER_QNAME,
+                """
+                package ${CONVERTER.packageName()};
+                import android.arch.persistence.room.TypeConverter;
+
+                public class ${CONVERTER.simpleName()} {
+                    public ${CONVERTER.simpleName()}(int x) {}
+                    @TypeConverter
+                    public int x(short y) {return 0;}
+                }
+                """)) { converter, invocation ->
+        }.failsToCompile().withErrorContaining(TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR)
+    }
+
+    @Test
+    fun checkNoArgConstructor_withStatic() {
+        singleClass(JavaFileObjects.forSourceString(CONVERTER_QNAME,
+                """
+                package ${CONVERTER.packageName()};
+                import android.arch.persistence.room.TypeConverter;
+
+                public class ${CONVERTER.simpleName()} {
+                    public ${CONVERTER.simpleName()}(int x) {}
+                    @TypeConverter
+                    public static int x(short y) {return 0;}
+                }
+                """)) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT))
+            assertThat(converter?.toTypeName, `is`(TypeName.INT))
+            assertThat(converter?.isStatic, `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun checkPublic() {
+        singleClass(JavaFileObjects.forSourceString(CONVERTER_QNAME,
+                """
+                package ${CONVERTER.packageName()};
+                import android.arch.persistence.room.TypeConverter;
+
+                public class ${CONVERTER.simpleName()} {
+                    @TypeConverter static int x(short y) {return 0;}
+                    @TypeConverter private static int y(boolean y) {return 0;}
+                }
+                """)) { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT))
+            assertThat(converter?.toTypeName, `is`(TypeName.INT))
+            assertThat(converter?.isStatic, `is`(true))
+        }.failsToCompile().withErrorContaining(TYPE_CONVERTER_MUST_BE_PUBLIC).and()
+                .withErrorCount(2)
+    }
+
+    @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+    @Test
+    fun parametrizedTypeBoundViaParent() {
+        val typeVarT = TypeVariableName.get("T")
+        val list = ParameterizedTypeName.get(List::class.typeName(), typeVarT)
+        val typeVarK = TypeVariableName.get("K")
+        val map = ParameterizedTypeName.get(Map::class.typeName(), typeVarK, typeVarT)
+
+        val baseConverter = createConverter(list, map, listOf(typeVarT, typeVarK))
+        val extendingQName = "foo.bar.Extending"
+        val extendingClass = JavaFileObjects.forSourceString(extendingQName,
+                "package foo.bar;\n" +
+                        TypeSpec.classBuilder(ClassName.bestGuess(extendingQName)).apply {
+                            superclass(
+                                    ParameterizedTypeName.get(CONVERTER, String::class.typeName(),
+                                    Integer::class.typeName()))
+                        }.build().toString())
+
+        simpleRun(baseConverter, extendingClass) { invocation ->
+            val element = invocation.processingEnv.elementUtils.getTypeElement(extendingQName)
+            val converter = CustomConverterProcessor(invocation.context, element)
+                    .process().firstOrNull()
+            assertThat(converter?.fromTypeName, `is`(ParameterizedTypeName.get(
+                    List::class.typeName(), String::class.typeName()) as TypeName
+            ))
+            assertThat(converter?.toTypeName, `is`(ParameterizedTypeName.get(Map::class.typeName(),
+                    Integer::class.typeName(), String::class.typeName()) as TypeName
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun checkDuplicates() {
+        singleClass(createConverter(TypeName.SHORT.box(), TypeName.CHAR.box(), duplicate = true))
+        { converter, invocation ->
+            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT.box()))
+            assertThat(converter?.toTypeName, `is`(TypeName.CHAR.box()))
+        }.failsToCompile().withErrorContaining("Multiple methods define the same conversion")
+    }
+
+    private fun createConverter(from: TypeName, to: TypeName,
+                                typeVariables: List<TypeVariableName> = emptyList(),
+                                duplicate : Boolean = false)
+            : JavaFileObject {
+        val code = TypeSpec.classBuilder(CONVERTER).apply {
+            addTypeVariables(typeVariables)
+            addModifiers(Modifier.PUBLIC)
+            fun buildMethod(name : String) = MethodSpec.methodBuilder(name).apply {
+                addAnnotation(TypeConverter::class.java)
+                addModifiers(Modifier.PUBLIC)
+                returns(to)
+                addParameter(ParameterSpec.builder(from, "input").build())
+                if (to.isPrimitive) {
+                    addStatement("return 0")
+                } else {
+                    addStatement("return null")
+                }
+            }.build()
+            addMethod(buildMethod("convertF"))
+            if (duplicate) {
+                addMethod(buildMethod("convertF2"))
+            }
+        }.build().toString()
+        return JavaFileObjects.forSourceString(CONVERTER.toString(),
+                "package ${CONVERTER.packageName()};\n$code")
+    }
+
+    private fun singleClass(vararg jfo: JavaFileObject,
+                            handler: (CustomTypeConverter?, TestInvocation) -> Unit)
+            : CompileTester {
+        return simpleRun(*((jfo.toList() + CONTAINER).toTypedArray())) { invocation ->
+            val processed = CustomConverterProcessor.findConverters(invocation.context,
+                    invocation.processingEnv.elementUtils.getTypeElement("foo.bar.Container"))
+            handler(processed.converters.firstOrNull()?.custom, invocation)
+        }
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DaoProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DaoProcessorTest.kt
new file mode 100644
index 0000000..a53cc1e
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DaoProcessorTest.kt
@@ -0,0 +1,227 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import COMMON
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.Dao
+import android.arch.persistence.room.vo.Warning
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import createVerifierFromEntities
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class DaoProcessorTest(val enableVerification : Boolean) {
+    companion object {
+        const val DAO_PREFIX = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            """
+        @Parameterized.Parameters(name = "enableDbVerification={0}")
+        @JvmStatic
+        fun getParams() = arrayOf(true, false)
+    }
+
+    @Test
+    fun testNonAbstract() {
+        singleDao("@Dao public class MyDao {}") { dao, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE)
+    }
+
+    @Test
+    fun testAbstractMethodWithoutQuery() {
+        singleDao("""
+                @Dao public interface MyDao {
+                    int getFoo();
+                }
+        """) { dao, invocation ->
+        }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION)
+    }
+
+    @Test
+    fun testBothAnnotations() {
+        singleDao("""
+                @Dao public interface MyDao {
+                    @Query("select 1")
+                    @Insert
+                    int getFoo(int x);
+                }
+        """) { dao, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_DAO_METHOD_ANNOTATION)
+    }
+
+    @Test
+    fun testAbstractClass() {
+        singleDao("""
+                @Dao abstract class MyDao {
+                    @Query("SELECT uid FROM User")
+                    abstract int[] getIds();
+                }
+                """) { dao, invocation ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("getIds"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testInterface() {
+        singleDao("""
+                @Dao interface MyDao {
+                    @Query("SELECT uid FROM User")
+                    abstract int[] getIds();
+                }
+                """) { dao, invocation ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("getIds"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testWithInsertAndQuery() {
+        singleDao("""
+                @Dao abstract class MyDao {
+                    @Query("SELECT uid FROM User")
+                    abstract int[] getIds();
+                    @Insert
+                    abstract void insert(User user);
+                }
+                """) { dao, invocation ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("getIds"))
+            assertThat(dao.insertionMethods.size, `is`(1))
+            val insertMethod = dao.insertionMethods.first()
+            assertThat(insertMethod.name, `is`("insert"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun skipQueryVerification() {
+        singleDao("""
+                @Dao @SkipQueryVerification interface MyDao {
+                    @Query("SELECT nonExistingField FROM User")
+                    abstract int[] getIds();
+                }
+                """) { dao, invocation ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("getIds"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun suppressedWarnings() {
+        singleDao("""
+            @SuppressWarnings({"ALL", RoomWarnings.CURSOR_MISMATCH})
+            @Dao interface MyDao {
+                @Query("SELECT * from user")
+                abstract User users();
+            }
+            """) { dao, invocation ->
+            val dbType = MoreTypes.asDeclared(invocation.context.processingEnv.elementUtils
+                    .getTypeElement(RoomTypeNames.ROOM_DB.toString()).asType())
+            val daoProcessor = DaoProcessor(invocation.context, dao.element, dbType, null)
+            assertThat(daoProcessor.context.logger
+                    .suppressedWarnings, `is`(setOf(Warning.ALL, Warning.CURSOR_MISMATCH)))
+
+            dao.queryMethods.forEach {
+                assertThat(QueryMethodProcessor(
+                        baseContext = daoProcessor.context,
+                        containing = MoreTypes.asDeclared(dao.element.asType()),
+                        executableElement = it.element,
+                        dbVerifier = null).context.logger.suppressedWarnings,
+                        `is`(setOf(Warning.ALL, Warning.CURSOR_MISMATCH)))
+            }
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun suppressedWarningsInheritance() {
+        singleDao("""
+            @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
+            @Dao interface MyDao {
+                @SuppressWarnings("ALL")
+                @Query("SELECT * from user")
+                abstract User users();
+            }
+            """) { dao, invocation ->
+            val dbType = MoreTypes.asDeclared(invocation.context.processingEnv.elementUtils
+                    .getTypeElement(RoomTypeNames.ROOM_DB.toString()).asType())
+            val daoProcessor = DaoProcessor(invocation.context, dao.element, dbType, null)
+            assertThat(daoProcessor.context.logger
+                    .suppressedWarnings, `is`(setOf(Warning.CURSOR_MISMATCH)))
+
+            dao.queryMethods.forEach {
+                assertThat(QueryMethodProcessor(
+                        baseContext = daoProcessor.context,
+                        containing = MoreTypes.asDeclared(dao.element.asType()),
+                        executableElement = it.element,
+                        dbVerifier = null).context.logger.suppressedWarnings,
+                        `is`(setOf(Warning.ALL, Warning.CURSOR_MISMATCH)))
+            }
+        }.compilesWithoutError()
+    }
+
+    fun singleDao(vararg inputs: String, handler: (Dao, TestInvocation) -> Unit):
+            CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyDao",
+                        DAO_PREFIX + inputs.joinToString("\n")
+                ), COMMON.USER))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(android.arch.persistence.room.Dao::class,
+                                android.arch.persistence.room.Entity::class)
+                        .nextRunHandler { invocation ->
+                            val dao = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            android.arch.persistence.room.Dao::class.java)
+                                    .first()
+                            val dbVerifier = if (enableVerification) {
+                                createVerifierFromEntities(invocation)
+                            } else {
+                                null
+                            }
+                            val dbType = MoreTypes.asDeclared(
+                                    invocation.context.processingEnv.elementUtils
+                                            .getTypeElement(RoomTypeNames.ROOM_DB.toString())
+                                            .asType())
+                            val parser = DaoProcessor(invocation.context,
+                                    MoreElements.asType(dao), dbType, dbVerifier)
+
+                            val parsedDao = parser.process()
+                            handler(parsedDao, invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DatabaseProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DatabaseProcessorTest.kt
new file mode 100644
index 0000000..56e6641
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DatabaseProcessorTest.kt
@@ -0,0 +1,717 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.RoomProcessor
+import android.arch.persistence.room.solver.query.result.EntityRowAdapter
+import android.arch.persistence.room.solver.query.result.PojoRowAdapter
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.Database
+import android.arch.persistence.room.vo.Warning
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ClassName
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.not
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.CoreMatchers.sameInstance
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.tools.JavaFileObject
+import javax.tools.StandardLocation
+
+@RunWith(JUnit4::class)
+class DatabaseProcessorTest {
+    companion object {
+        const val DATABASE_PREFIX = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            """
+        val DB1: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Db1",
+                """
+                $DATABASE_PREFIX
+                @Database(entities = {Book.class}, version = 42)
+                public abstract class Db1 extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """)
+        val DB2: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Db2",
+                """
+                $DATABASE_PREFIX
+                @Database(entities = {Book.class}, version = 42)
+                public abstract class Db2 extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """)
+        val DB3: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Db3",
+                """
+                $DATABASE_PREFIX
+                @Database(entities = {Book.class}, version = 42)
+                public abstract class Db3 extends RoomDatabase {
+                }
+                """)
+        val USER: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.User",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity
+                public class User {
+                    @PrimaryKey
+                    int uid;
+                }
+                """)
+        val USER_DAO: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.UserDao",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Dao
+                public interface UserDao {
+                    @Query("SELECT * FROM user")
+                    public java.util.List<User> loadAll();
+
+                    @Insert
+                    public void insert(User... users);
+
+                    @Query("SELECT * FROM user where uid = :uid")
+                    public User loadOne(int uid);
+
+                    @TypeConverters(Converter.class)
+                    @Query("SELECT * FROM user where uid = :uid")
+                    public User loadWithConverter(int uid);
+
+                    @Query("SELECT * FROM user where uid = :uid")
+                    public Pojo loadOnePojo(int uid);
+
+                    @Query("SELECT * FROM user")
+                    public java.util.List<Pojo> loadAllPojos();
+
+                    @TypeConverters(Converter.class)
+                    @Query("SELECT * FROM user where uid = :uid")
+                    public Pojo loadPojoWithConverter(int uid);
+
+                    public static class Converter {
+                        @TypeConverter
+                        public static java.util.Date foo(Long input) {return null;}
+                    }
+
+                    public static class Pojo {
+                        public int uid;
+                    }
+                }
+                """)
+        val BOOK: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Book",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity
+                public class Book {
+                    @PrimaryKey
+                    int bookId;
+                }
+                """)
+        val BOOK_DAO: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.BookDao",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Dao
+                public interface BookDao {
+                    @Query("SELECT * FROM book")
+                    public java.util.List<Book> loadAllBooks();
+                    @Insert
+                    public void insert(Book book);
+                }
+                """)
+    }
+
+    @Test
+    fun simple() {
+        singleDb("""
+            @Database(entities = {User.class}, version = 42)
+            public abstract class MyDb extends RoomDatabase {
+                abstract UserDao userDao();
+            }
+            """, USER, USER_DAO) { db, invocation ->
+            assertThat(db.daoMethods.size, `is`(1))
+            assertThat(db.entities.size, `is`(1))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun multiple() {
+        singleDb("""
+            @Database(entities = {User.class, Book.class}, version = 42)
+            public abstract class MyDb extends RoomDatabase {
+                abstract UserDao userDao();
+                abstract BookDao bookDao();
+            }
+            """, USER, USER_DAO, BOOK, BOOK_DAO) { db, invocation ->
+            assertThat(db.daoMethods.size, `is`(2))
+            assertThat(db.entities.size, `is`(2))
+            assertThat(db.daoMethods.map { it.name }, `is`(listOf("userDao", "bookDao")))
+            assertThat(db.entities.map { it.type.toString() },
+                    `is`(listOf("foo.bar.User", "foo.bar.Book")))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun detectMissingBaseClass() {
+        singleDb("""
+            @Database(entities = {User.class, Book.class}, version = 42)
+            public abstract class MyDb {
+            }
+            """, USER, BOOK) { db, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.DB_MUST_EXTEND_ROOM_DB)
+    }
+
+    @Test
+    fun detectMissingTable() {
+        singleDb(
+                """
+                @Database(entities = {Book.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """, BOOK, JavaFileObjects.forSourceString("foo.bar.BookDao",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Dao
+                public interface BookDao {
+                    @Query("SELECT * FROM nonExistentTable")
+                    public java.util.List<Book> loadAllBooks();
+                }
+                """)){ db, invocation ->
+
+        }.failsToCompile().withErrorContaining("no such table: nonExistentTable")
+    }
+
+    @Test
+    fun detectDuplicateTableNames() {
+        singleDb("""
+                @Database(entities = {User.class, AnotherClass.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    abstract UserDao userDao();
+                }
+                """, USER, USER_DAO, JavaFileObjects.forSourceString("foo.bar.AnotherClass",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(tableName="user")
+                public class AnotherClass {
+                    @PrimaryKey
+                    int uid;
+                }
+                """)) { db, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.duplicateTableNames("user",
+                        listOf("foo.bar.User", "foo.bar.AnotherClass"))
+        )
+    }
+
+    @Test
+    fun skipBadQueryVerification() {
+        singleDb(
+                """
+                @SkipQueryVerification
+                @Database(entities = {Book.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """, BOOK, JavaFileObjects.forSourceString("foo.bar.BookDao",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Dao
+                public interface BookDao {
+                    @Query("SELECT nonExistingField FROM Book")
+                    public java.util.List<Book> loadAllBooks();
+                }
+                """)){ db, invocation ->
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun multipleDatabases() {
+        val db1_2 = JavaFileObjects.forSourceString("foo.barx.Db1",
+                """
+                package foo.barx;
+                import android.arch.persistence.room.*;
+                import foo.bar.*;
+                @Database(entities = {Book.class}, version = 42)
+                public abstract class Db1 extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """)
+        Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(BOOK, BOOK_DAO, DB1, DB2, db1_2))
+                .processedWith(RoomProcessor())
+                .compilesWithoutError()
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", "Db1_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", "Db2_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.barx", "Db1_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar",
+                        "BookDao_Db1_0_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar",
+                        "BookDao_Db1_1_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar",
+                        "BookDao_Db2_Impl.class")
+    }
+
+    @Test
+    fun twoDaoMethodsForTheSameDao() {
+        singleDb(
+                """
+                @Database(entities = {User.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    abstract UserDao userDao();
+                    abstract UserDao userDao2();
+                }
+                """, USER, USER_DAO){db, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.DAO_METHOD_CONFLICTS_WITH_OTHERS)
+                .and()
+                .withErrorContaining(ProcessorErrors.duplicateDao(
+                        ClassName.get("foo.bar", "UserDao"), listOf("userDao", "userDao2")
+                ))
+    }
+
+    @Test
+    fun suppressedWarnings() {
+        singleDb(
+                """
+                @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
+                @Database(entities = {User.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    abstract UserDao userDao();
+                }
+                """, USER, USER_DAO) {db, invocation ->
+            assertThat(DatabaseProcessor(invocation.context, db.element)
+                    .context.logger.suppressedWarnings, `is`(setOf(Warning.CURSOR_MISMATCH)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun duplicateIndexNames() {
+        val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = {@Index(name ="index_name", value = {"name"})})
+                public class Entity1 {
+                    @PrimaryKey
+                    int uid;
+                    String name;
+                }
+                """)
+
+        val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = {@Index(name ="index_name", value = {"anotherName"})})
+                public class Entity2 {
+                    @PrimaryKey
+                    int uid;
+                    String anotherName;
+                }
+                """)
+        singleDb("""
+                @Database(entities = {Entity1.class, Entity2.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                }
+                """, entity1, entity2){ db, invocation ->
+
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.duplicateIndexInDatabase("index_name",
+                        listOf("foo.bar.Entity1 > index_name", "foo.bar.Entity2 > index_name"))
+        )
+    }
+
+    @Test
+    fun foreignKey_missingParent() {
+        val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(foreignKeys = @ForeignKey(entity = ${COMMON.USER_TYPE_NAME}.class,
+                        parentColumns = "lastName",
+                        childColumns = "name"))
+                public class Entity1 {
+                    @PrimaryKey
+                    int uid;
+                    String name;
+                }
+                """)
+        singleDb("""
+                @Database(entities = {Entity1.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                }
+                """, entity1, COMMON.USER){ db, invocation ->
+
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.foreignKeyMissingParentEntityInDatabase("User", "foo.bar.Entity1")
+        )
+    }
+
+    @Test
+    fun foreignKey_missingParentIndex() {
+        val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(foreignKeys = @ForeignKey(entity = ${COMMON.USER_TYPE_NAME}.class,
+                        parentColumns = "lastName",
+                        childColumns = "name"))
+                public class Entity1 {
+                    @PrimaryKey
+                    int uid;
+                    String name;
+                }
+                """)
+        singleDb("""
+                @Database(entities = {Entity1.class, User.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                }
+                """, entity1, COMMON.USER){ db, invocation ->
+
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.foreignKeyMissingIndexInParent(
+                        parentEntity = COMMON.USER_TYPE_NAME.toString(),
+                        parentColumns = listOf("lastName"),
+                        childEntity = "foo.bar.Entity1",
+                        childColumns = listOf("name")
+                )
+        )
+    }
+
+    @Test
+    fun foreignKey_goodWithPrimaryKey() {
+        val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(foreignKeys = @ForeignKey(entity = Entity2.class,
+                    parentColumns = "uid",
+                    childColumns = "parentId"))
+                public class Entity1 {
+                    @PrimaryKey
+                    int uid;
+                    int parentId;
+                    String name;
+                }
+                """)
+
+        val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity
+                public class Entity2 {
+                    @PrimaryKey
+                    int uid;
+                    String anotherName;
+                }
+                """)
+        singleDb("""
+                @Database(entities = {Entity1.class, Entity2.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                }
+                """, entity1, entity2){ db, invocation ->
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun foreignKey_missingParentColumn() {
+        val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(foreignKeys = @ForeignKey(entity = Entity2.class,
+                    parentColumns = {"anotherName", "anotherName2"},
+                    childColumns = {"name", "name2"}))
+                public class Entity1 {
+                    @PrimaryKey
+                    int uid;
+                    String name;
+                    String name2;
+                }
+                """)
+
+        val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity
+                public class Entity2 {
+                    @PrimaryKey
+                    int uid;
+                    String anotherName;
+                }
+                """)
+        singleDb("""
+                @Database(entities = {Entity1.class, Entity2.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                }
+                """, entity1, entity2){ db, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.foreignKeyParentColumnDoesNotExist("foo.bar.Entity2",
+                        "anotherName2", listOf("uid", "anotherName"))
+        )
+    }
+
+    @Test
+    fun foreignKey_goodWithIndex() {
+        val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(foreignKeys = @ForeignKey(entity = Entity2.class,
+                    parentColumns = {"anotherName", "anotherName2"},
+                    childColumns = {"name", "name2"}))
+                public class Entity1 {
+                    @PrimaryKey
+                    int uid;
+                    String name;
+                    String name2;
+                }
+                """)
+
+        val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = @Index(value = {"anotherName2", "anotherName"}, unique = true))
+                public class Entity2 {
+                    @PrimaryKey
+                    int uid;
+                    String anotherName;
+                    String anotherName2;
+                }
+                """)
+        singleDb("""
+                @Database(entities = {Entity1.class, Entity2.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                }
+                """, entity1, entity2){ db, invocation ->
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertNotAReferencedEntity() {
+        singleDb("""
+                @Database(entities = {User.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """, USER, USER_DAO, BOOK, BOOK_DAO){ db, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.shortcutEntityIsNotInDatabase(
+                        database = "foo.bar.MyDb",
+                        dao = "foo.bar.BookDao",
+                        entity = "foo.bar.Book"
+                )
+        )
+    }
+
+    @Test
+    fun cache_entity() {
+        singleDb("""
+                @Database(entities = {User.class}, version = 42)
+                @SkipQueryVerification
+                public abstract class MyDb extends RoomDatabase {
+                    public abstract MyUserDao userDao();
+                    @Dao
+                    interface MyUserDao {
+                        @Insert
+                        public void insert(User... users);
+
+                        @Query("SELECT * FROM user where uid = :uid")
+                        public User loadOne(int uid);
+
+                        @TypeConverters(Converter.class)
+                        @Query("SELECT * FROM user where uid = :uid")
+                        public User loadWithConverter(int uid);
+                    }
+                    public static class Converter {
+                        @TypeConverter
+                        public static java.util.Date foo(Long input) {return null;}
+                    }
+                }
+                """, USER, USER_DAO){ db, invocation ->
+            val userDao = db.daoMethods.first().dao
+            val insertionMethod = userDao.insertionMethods.find { it.name == "insert" }
+            assertThat(insertionMethod, notNullValue())
+            val loadOne = userDao.queryMethods.find { it.name == "loadOne" }
+            assertThat(loadOne, notNullValue())
+            val adapter = loadOne?.queryResultBinder?.adapter?.rowAdapter
+            assertThat("test sanity", adapter, instanceOf(EntityRowAdapter::class.java))
+            val adapterEntity = (adapter as EntityRowAdapter).entity
+            assertThat(insertionMethod?.entities?.values?.first(), sameInstance(adapterEntity))
+
+            val withConverter = userDao.queryMethods.find { it.name == "loadWithConverter" }
+            assertThat(withConverter, notNullValue())
+            val convAdapter = withConverter?.queryResultBinder?.adapter?.rowAdapter
+            assertThat("test sanity", adapter, instanceOf(EntityRowAdapter::class.java))
+            val convAdapterEntity = (convAdapter as EntityRowAdapter).entity
+            assertThat(insertionMethod?.entities?.values?.first(),
+                    not(sameInstance(convAdapterEntity)))
+
+            assertThat(convAdapterEntity, notNullValue())
+            assertThat(adapterEntity, notNullValue())
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun cache_pojo() {
+        singleDb("""
+                @Database(entities = {User.class}, version = 42)
+                public abstract class MyDb extends RoomDatabase {
+                    public abstract UserDao userDao();
+                }
+                """, USER, USER_DAO){ db, invocation ->
+            val userDao = db.daoMethods.first().dao
+            val loadOne = userDao.queryMethods.find { it.name == "loadOnePojo" }
+            assertThat(loadOne, notNullValue())
+            val adapter = loadOne?.queryResultBinder?.adapter?.rowAdapter
+            assertThat("test sanity", adapter, instanceOf(PojoRowAdapter::class.java))
+            val adapterPojo = (adapter as PojoRowAdapter).pojo
+
+            val loadAll = userDao.queryMethods.find { it.name == "loadAllPojos" }
+            assertThat(loadAll, notNullValue())
+            val loadAllAdapter = loadAll?.queryResultBinder?.adapter?.rowAdapter
+            assertThat("test sanity", loadAllAdapter, instanceOf(PojoRowAdapter::class.java))
+            val loadAllPojo = (loadAllAdapter as PojoRowAdapter).pojo
+            assertThat(adapter, not(sameInstance(loadAllAdapter)))
+            assertThat(adapterPojo, sameInstance(loadAllPojo))
+
+            val withConverter = userDao.queryMethods.find { it.name == "loadPojoWithConverter" }
+            assertThat(withConverter, notNullValue())
+            val convAdapter = withConverter?.queryResultBinder?.adapter?.rowAdapter
+            assertThat("test sanity", adapter, instanceOf(PojoRowAdapter::class.java))
+            val convAdapterPojo = (convAdapter as PojoRowAdapter).pojo
+            assertThat(convAdapterPojo, notNullValue())
+            assertThat(convAdapterPojo, not(sameInstance(adapterPojo)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun daoConstructor_RoomDatabase() {
+        assertConstructor(listOf(DB1), "BookDao(RoomDatabase db) {}")
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun daoConstructor_specificDatabase() {
+        assertConstructor(listOf(DB1), "BookDao(Db1 db) {}")
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun daoConstructor_wrongDatabase() {
+        assertConstructor(listOf(DB1, DB3), "BookDao(Db3 db) {}")
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors
+                        .daoMustHaveMatchingConstructor("foo.bar.BookDao", "foo.bar.Db1"))
+    }
+
+    @Test
+    fun daoConstructor_multipleDatabases_RoomDatabase() {
+        assertConstructor(listOf(DB1, DB2), "BookDao(RoomDatabase db) {}")
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun daoConstructor_multipleDatabases_specificDatabases() {
+        assertConstructor(listOf(DB1, DB2), """
+                    BookDao(Db1 db) {}
+                    BookDao(Db2 db) {}
+                """)
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun daoConstructor_multipleDatabases_empty() {
+        assertConstructor(listOf(DB1, DB2), """
+                    BookDao(Db1 db) {}
+                    BookDao() {} // Db2 uses this
+                """)
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun daoConstructor_multipleDatabases_noMatch() {
+        assertConstructor(listOf(DB1, DB2), """
+                    BookDao(Db1 db) {}
+                """)
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors
+                        .daoMustHaveMatchingConstructor("foo.bar.BookDao", "foo.bar.Db2"))
+    }
+
+    fun assertConstructor(dbs: List<JavaFileObject>, constructor: String): CompileTester {
+        val bookDao = JavaFileObjects.forSourceString("foo.bar.BookDao",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Dao
+                public abstract class BookDao {
+                    $constructor
+                }
+                """)
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(BOOK, bookDao) + dbs)
+                .processedWith(RoomProcessor())
+    }
+
+    fun singleDb(input: String, vararg otherFiles: JavaFileObject,
+                 handler: (Database, TestInvocation) -> Unit): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(otherFiles.toMutableList()
+                        + JavaFileObjects.forSourceString("foo.bar.MyDb",
+                        DATABASE_PREFIX + input
+                ))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(android.arch.persistence.room.Database::class)
+                        .nextRunHandler { invocation ->
+                            val entity = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            android.arch.persistence.room.Database::class.java)
+                                    .first()
+                            val parser = DatabaseProcessor(invocation.context,
+                                    MoreElements.asType(entity))
+                            val parsedDb = parser.process()
+                            handler(parsedDb, invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DeletionMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DeletionMethodProcessorTest.kt
new file mode 100644
index 0000000..864ea9b
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/DeletionMethodProcessorTest.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Delete
+import android.arch.persistence.room.processor.ProcessorErrors.DELETION_MISSING_PARAMS
+import android.arch.persistence.room.processor.ProcessorErrors
+        .DELETION_METHODS_MUST_RETURN_VOID_OR_INT
+import android.arch.persistence.room.vo.DeletionMethod
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+@RunWith(JUnit4::class)
+class DeletionMethodProcessorTest : ShortcutMethodProcessorTest<DeletionMethod>(Delete::class) {
+    override fun invalidReturnTypeError(): String = DELETION_METHODS_MUST_RETURN_VOID_OR_INT
+
+    override fun noParamsError(): String = DELETION_MISSING_PARAMS
+
+    override fun process(baseContext: Context, containing: DeclaredType,
+                         executableElement: ExecutableElement): DeletionMethod {
+        return DeletionMethodProcessor(baseContext, containing, executableElement).process()
+    }
+
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/EntityNameMatchingVariationsTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/EntityNameMatchingVariationsTest.kt
new file mode 100644
index 0000000..e6a2ff3
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/EntityNameMatchingVariationsTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.vo.CallType
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.FieldGetter
+import android.arch.persistence.room.vo.FieldSetter
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import javax.lang.model.type.TypeKind.INT
+
+@RunWith(Parameterized::class)
+class EntityNameMatchingVariationsTest(triple: Triple<String, String, String>) :
+        BaseEntityParserTest() {
+    val fieldName = triple.first
+    val getterName = triple.second
+    val setterName = triple.third
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() : List<Triple<String, String, String>> {
+            val result = arrayListOf<Triple<String, String, String>>()
+            arrayListOf("x", "_x", "mX").forEach { field ->
+                arrayListOf("getX", "x").forEach { getter ->
+                    arrayListOf("setX", "x").forEach { setter ->
+                        result.add(Triple(field, getter, setter))
+                    }
+                }
+            }
+            return result
+        }
+    }
+
+    @Test
+    fun testSuccessfulParamToMethodMatching() {
+        singleEntity("""
+                @PrimaryKey
+                private int $fieldName;
+                public int $getterName() { return $fieldName; }
+                public void $setterName(int id) { this.$fieldName = id; }
+            """) { entity, invocation ->
+            assertThat(entity.type.toString(), `is`("foo.bar.MyEntity"))
+            assertThat(entity.fields.size, `is`(1))
+            val field = entity.fields.first()
+            val intType = invocation.processingEnv.typeUtils.getPrimitiveType(INT)
+            assertThat(field, `is`(Field(
+                    element = field.element,
+                    name = fieldName,
+                    type = intType,
+                    columnName = fieldName,
+                    affinity = SQLTypeAffinity.INTEGER)))
+            assertThat(field.setter, `is`(FieldSetter(setterName, intType, CallType.METHOD)))
+            assertThat(field.getter, `is`(FieldGetter(getterName, intType, CallType.METHOD)))
+        }.compilesWithoutError()
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/EntityProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/EntityProcessorTest.kt
new file mode 100644
index 0000000..34c522e
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/EntityProcessorTest.kt
@@ -0,0 +1,1472 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import COMMON
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.processor.ProcessorErrors.RELATION_IN_ENTITY
+import android.arch.persistence.room.vo.CallType
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.FieldGetter
+import android.arch.persistence.room.vo.FieldSetter
+import android.arch.persistence.room.vo.Index
+import android.arch.persistence.room.vo.Pojo
+import com.google.testing.compile.JavaFileObjects
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.lang.model.type.TypeKind.INT
+
+@RunWith(JUnit4::class)
+class EntityProcessorTest : BaseEntityParserTest() {
+    @Test
+    fun simple() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public int getId() { return id; }
+                public void setId(int id) { this.id = id; }
+            """) { entity, invocation ->
+            assertThat(entity.type.toString(), `is`("foo.bar.MyEntity"))
+            assertThat(entity.fields.size, `is`(1))
+            val field = entity.fields.first()
+            val intType = invocation.processingEnv.typeUtils.getPrimitiveType(INT)
+            assertThat(field, `is`(Field(
+                    element = field.element,
+                    name = "id",
+                    type = intType,
+                    columnName = "id",
+                    affinity = SQLTypeAffinity.INTEGER)))
+            assertThat(field.setter, `is`(FieldSetter("setId", intType, CallType.METHOD)))
+            assertThat(field.getter, `is`(FieldGetter("getId", intType, CallType.METHOD)))
+            assertThat(entity.primaryKey.fields, `is`(listOf(field)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun noGetter() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {this.id = id;}
+                """) { entity, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD)
+    }
+
+    @Test
+    fun getterWithBadType() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public float getId() {return 0f;}
+                public void setId(int id) {this.id = id;}
+                """) { entity, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD)
+    }
+
+    @Test
+    fun setterWithBadType() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public int getId() {return id;}
+                public void setId(float id) {}
+                """) { entity, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD)
+    }
+
+    @Test
+    fun setterWithAssignableType() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public int getId() {return id;}
+                public void setId(Integer id) {}
+                """) { entity, invocation -> }
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun getterWithAssignableType() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public Integer getId() {return id;}
+                public void setId(int id) {}
+                """) { entity, invocation -> }
+                .compilesWithoutError()
+    }
+
+    @Test
+    fun noSetter() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public int getId(){ return id; }
+                """) { entity, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD)
+    }
+
+    @Test
+    fun tooManyGetters() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {}
+                public int getId(){ return id; }
+                public int id(){ return id; }
+                """) { entity, invocation -> }
+                .failsToCompile()
+                .withErrorContaining("getId, id")
+    }
+
+    @Test
+    fun tooManyGettersWithIgnore() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {}
+                public int getId(){ return id; }
+                @Ignore public int id(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().getter.name, `is`("getId"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun tooManyGettersWithDifferentVisibility() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {}
+                public int getId(){ return id; }
+                protected int id(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().getter.name, `is`("getId"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun tooManyGettersWithDifferentTypes() {
+        singleEntity("""
+                @PrimaryKey
+                public int id;
+                public void setId(int id) {}
+                public int getId(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().getter.name, `is`("id"))
+            assertThat(entity.fields.first().getter.callType, `is`(CallType.FIELD))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun tooManySetters() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {}
+                public void id(int id) {}
+                public int getId(){ return id; }
+                """) { entity, invocation -> }
+                .failsToCompile()
+                .withErrorContaining("setId, id")
+    }
+
+    @Test
+    fun tooManySettersWithIgnore() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {}
+                @Ignore public void id(int id) {}
+                public int getId(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().setter.name, `is`("setId"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun tooManySettersWithDifferentVisibility() {
+        singleEntity("""
+                @PrimaryKey
+                private int id;
+                public void setId(int id) {}
+                protected void id(int id) {}
+                public int getId(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().setter.name, `is`("setId"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun tooManySettersWithDifferentTypes() {
+        singleEntity("""
+                @PrimaryKey
+                public int id;
+                public void setId(int id) {}
+                public int getId(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().setter.name, `is`("id"))
+            assertThat(entity.fields.first().setter.callType, `is`(CallType.FIELD))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun preferPublicOverProtected() {
+        singleEntity("""
+                @PrimaryKey
+                int id;
+                public void setId(int id) {}
+                public int getId(){ return id; }
+                """) { entity, invocation ->
+            assertThat(entity.fields.first().setter.name, `is`("setId"))
+            assertThat(entity.fields.first().getter.name, `is`("getId"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun customName() {
+        singleEntity("""
+                @PrimaryKey
+                int x;
+                """, hashMapOf(Pair("tableName", "\"foo_table\""))) { entity, invocation ->
+            assertThat(entity.tableName, `is`("foo_table"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun emptyCustomName() {
+        singleEntity("""
+                @PrimaryKey
+                int x;
+                """, hashMapOf(Pair("tableName", "\" \""))) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY)
+    }
+
+    @Test
+    fun missingPrimaryKey() {
+        singleEntity("""
+                """) { entity, invocation ->
+        }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.MISSING_PRIMARY_KEY)
+    }
+
+    @Test
+    fun missingColumnAdapter() {
+        singleEntity("""
+                @PrimaryKey
+                public java.util.Date myDate;
+                """) { entity, invocation ->
+
+        }.failsToCompile().withErrorContaining(ProcessorErrors.CANNOT_FIND_COLUMN_TYPE_ADAPTER)
+    }
+
+    @Test
+    fun dropSubPrimaryKey() {
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                @Embedded
+                Point myPoint;
+                static class Point {
+                    @PrimaryKey
+                    int x;
+                    int y;
+                }
+                """
+        ) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.map { it.name }, `is`(listOf("id")))
+        }.compilesWithoutError()
+                .withWarningCount(1)
+                .withWarningContaining(ProcessorErrors.embeddedPrimaryKeyIsDropped(
+                        "foo.bar.MyEntity", "x"))
+    }
+
+    @Test
+    fun ignoreDropSubPrimaryKey() {
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                @Embedded
+                @SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
+                Point myPoint;
+                static class Point {
+                    @PrimaryKey
+                    int x;
+                    int y;
+                }
+                """
+        ) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.map { it.name }, `is`(listOf("id")))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun notNull() {
+        singleEntity(
+                """
+                @PrimaryKey int id;
+                @NonNull public String name;
+                """
+        ) { entity, _ ->
+            val field = fieldsByName(entity, "name").first()
+            assertThat(field.name, `is`("name"))
+            assertThat(field.columnName, `is`("name"))
+            assertThat(field.nonNull, `is`(true))
+        }.compilesWithoutError()
+    }
+
+    private fun fieldsByName(entity : Pojo, vararg fieldNames : String) : List<Field> {
+        return fieldNames
+                .map { name -> entity.fields.find { it.name == name } }
+                .filterNotNull()
+    }
+
+    @Test
+    fun index_simple() {
+        val annotation = mapOf(
+                "indices" to """@Index("foo")"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "index_MyEntity_foo",
+                            unique = false,
+                            fields = fieldsByName(entity, "foo")))))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_fromField() {
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                @ColumnInfo(index = true)
+                public String foo;
+                """) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "index_MyEntity_foo",
+                            unique = false,
+                            fields = fieldsByName(entity, "foo")))
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_multiColumn() {
+        val annotation = mapOf(
+                "indices" to """@Index({"foo", "id"})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "index_MyEntity_foo_id",
+                            unique = false,
+                            fields = fieldsByName(entity, "foo", "id")))
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_multiple() {
+        val annotation = mapOf(
+                "indices" to """{@Index({"foo", "id"}), @Index({"bar_column", "foo"})}"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                @ColumnInfo(name = "bar_column")
+                public String bar;
+                """
+                , annotation) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "index_MyEntity_foo_id",
+                            unique = false,
+                            fields = fieldsByName(entity, "foo", "id")),
+                            Index(name = "index_MyEntity_bar_column_foo",
+                                    unique = false,
+                                    fields = fieldsByName(entity, "bar", "foo")))
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_unique() {
+        val annotation = mapOf(
+                "indices" to """@Index(value = {"foo", "id"}, unique = true)"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "index_MyEntity_foo_id",
+                            unique = true,
+                           fields = fieldsByName(entity, "foo", "id")))
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_customName() {
+        val annotation = mapOf(
+                "indices" to """@Index(value = {"foo"}, name = "myName")"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "myName",
+                            unique = false,
+                            fields = fieldsByName(entity, "foo")))
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_customTableName() {
+        val annotation = mapOf(
+                "tableName" to "\"MyTable\"",
+                "indices" to """@Index(value = {"foo"})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+            assertThat(entity.indices, `is`(
+                    listOf(Index(name = "index_MyTable_foo",
+                            unique = false,
+                            fields = fieldsByName(entity, "foo")))
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun index_empty() {
+        val annotation = mapOf(
+                "indices" to """@Index({})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.INDEX_COLUMNS_CANNOT_BE_EMPTY
+        )
+    }
+
+    @Test
+    fun index_missingColumn() {
+        val annotation = mapOf(
+                "indices" to """@Index({"foo", "bar"})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.indexColumnDoesNotExist("bar", listOf("id, foo"))
+        )
+    }
+
+    @Test
+    fun index_nameConflict() {
+        val annotation = mapOf(
+                "indices" to """@Index({"foo"})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                @ColumnInfo(index = true)
+                public String foo;
+                """
+                , annotation) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.duplicateIndexInEntity("index_MyEntity_foo")
+        )
+    }
+
+    @Test
+    fun index_droppedParentFieldIndex() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    @ColumnInfo(index = true)
+                    String name;
+                    String lastName;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """, baseClass = "foo.bar.Base", jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.indices.isEmpty(), `is`(true))
+        }.compilesWithoutError()
+                .withWarningContaining(
+                        ProcessorErrors.droppedSuperClassFieldIndex(
+                                fieldName = "name",
+                                childEntity = "foo.bar.MyEntity",
+                                superEntity = "foo.bar.Base")
+                )
+    }
+
+    @Test
+    fun index_keptGrandParentEntityIndex() {
+        val grandParent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = @Index({"name", "lastName"}))
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Parent",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+
+                public class Parent extends Base {
+                    String iHaveAField;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """,
+                baseClass = "foo.bar.Parent",
+                attributes = hashMapOf("inheritSuperIndices" to "true"),
+                jfos = listOf(parent, grandParent)) {
+            entity, invocation ->
+            assertThat(entity.indices.size, `is`(1))
+            assertThat(entity.indices.first(),
+                    `is`(Index(name = "index_MyEntity_name_lastName",
+                            unique = false,
+                            fields = fieldsByName(entity, "name", "lastName"))))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun index_keptParentEntityIndex() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = @Index({"name", "lastName"}))
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                attributes = hashMapOf("inheritSuperIndices" to "true"),
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.indices.size, `is`(1))
+            assertThat(entity.indices.first(),
+                    `is`(Index(name = "index_MyEntity_name_lastName",
+                            unique = false,
+                            fields = fieldsByName(entity, "name", "lastName"))))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun index_keptParentFieldIndex() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    @ColumnInfo(index = true)
+                    String name;
+                    String lastName;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                attributes = hashMapOf("inheritSuperIndices" to "true"),
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.indices.size, `is`(1))
+            assertThat(entity.indices.first(),
+                    `is`(Index(name = "index_MyEntity_name",
+                            unique = false,
+                            fields = fieldsByName(entity, "name"))))
+        }.compilesWithoutError().withWarningCount(0)
+
+    }
+
+    @Test
+    fun index_droppedGrandParentEntityIndex() {
+        val grandParent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = @Index({"name", "lastName"}))
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Parent",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+
+                public class Parent extends Base {
+                    String iHaveAField;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """, baseClass = "foo.bar.Parent", jfos = listOf(parent, grandParent)) {
+            entity, invocation ->
+            assertThat(entity.indices.isEmpty(), `is`(true))
+        }.compilesWithoutError()
+                .withWarningContaining(
+                        ProcessorErrors.droppedSuperClassIndex(
+                                childEntity = "foo.bar.MyEntity",
+                                superEntity = "foo.bar.Base")
+                )
+    }
+
+    @Test
+    fun index_droppedParentEntityIndex() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(indices = @Index({"name", "lastName"}))
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """, baseClass = "foo.bar.Base", jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.indices.isEmpty(), `is`(true))
+        }.compilesWithoutError()
+                .withWarningContaining(
+                        ProcessorErrors.droppedSuperClassIndex(
+                                childEntity = "foo.bar.MyEntity",
+                                superEntity = "foo.bar.Base")
+                )
+    }
+
+    @Test
+    fun index_droppedEmbeddedEntityIndex() {
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                @Embedded
+                public Foo foo;
+                @Entity(indices = {@Index("a")})
+                static class Foo {
+                    @PrimaryKey
+                    @ColumnInfo(name = "foo_id")
+                    int id;
+                    @ColumnInfo(index = true)
+                    public int a;
+                }
+                """) { entity, invocation ->
+            assertThat(entity.indices.isEmpty(), `is`(true))
+        }.compilesWithoutError()
+                .withWarningContaining(
+                        ProcessorErrors.droppedEmbeddedIndex(
+                                entityName = "foo.bar.MyEntity.Foo",
+                                fieldPath = "foo",
+                                grandParent = "foo.bar.MyEntity")
+                )
+    }
+
+    @Test
+    fun index_onEmbeddedField() {
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                @Embedded
+                @ColumnInfo(index = true)
+                public Foo foo;
+                static class Foo {
+                    @ColumnInfo(index = true)
+                    public int a;
+                }
+                """) { entity, invocation ->
+            assertThat(entity.indices.isEmpty(), `is`(true))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION
+        )
+    }
+
+    @Test
+    fun index_droppedEmbeddedFieldIndex() {
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                @Embedded
+                public Foo foo;
+                static class Foo {
+                    @ColumnInfo(index = true)
+                    public int a;
+                }
+                """) { entity, invocation ->
+            assertThat(entity.indices.isEmpty(), `is`(true))
+        }.compilesWithoutError()
+                .withWarningContaining(
+                        ProcessorErrors.droppedEmbeddedFieldIndex("foo > a", "foo.bar.MyEntity")
+                )
+    }
+
+    @Test
+    fun index_referenceEmbeddedField() {
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                @Embedded
+                public Foo foo;
+                static class Foo {
+                    public int a;
+                }
+                """, attributes = mapOf("indices" to "@Index(\"a\")")) { entity, invocation ->
+            assertThat(entity.indices.size, `is`(1))
+            assertThat(entity.indices.first(), `is`(
+                    Index(
+                            name = "index_MyEntity_a",
+                            unique = false,
+                            fields = fieldsByName(entity, "a")
+                    )
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun primaryKey_definedInBothWays() {
+        singleEntity(
+                """
+                public int id;
+                @PrimaryKey
+                public String foo;
+                """,
+                attributes = mapOf("primaryKeys" to "\"id\"")) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.multiplePrimaryKeyAnnotations(
+                        listOf("PrimaryKey[id]", "PrimaryKey[foo]")
+                ))
+    }
+
+    @Test
+    fun primaryKey_badColumnName() {
+        singleEntity(
+                """
+                public int id;
+                """,
+                attributes = mapOf("primaryKeys" to "\"foo\"")) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.primaryKeyColumnDoesNotExist("foo", listOf("id")))
+    }
+
+    @Test
+    fun primaryKey_multipleAnnotations() {
+        singleEntity("""
+                @PrimaryKey
+                int x;
+                @PrimaryKey
+                int y;
+                """) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.isEmpty(), `is`(true))
+        }.failsToCompile()
+                .withErrorContaining(
+                        ProcessorErrors.multiplePrimaryKeyAnnotations(
+                                listOf("PrimaryKey[x]", "PrimaryKey[y]")))
+    }
+
+    @Test
+    fun primaryKey_fromParentField() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("baseId"))
+
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun primaryKey_fromParentEntity() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(primaryKeys = "baseId")
+                public class Base {
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("baseId"))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun primaryKey_overrideFromParentField() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                public class Base {
+                    @PrimaryKey
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.size, `is`(1))
+            assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
+            assertThat(entity.primaryKey.autoGenerateId, `is`(false))
+        }.compilesWithoutError().withNoteContaining(
+                "PrimaryKey[baseId] is overridden by PrimaryKey[id]"
+        )
+    }
+
+    @Test
+    fun primaryKey_overrideFromParentEntityViaField() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(primaryKeys = "baseId")
+                public class Base {
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.size, `is`(1))
+            assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
+        }.compilesWithoutError().withNoteContaining(
+                "PrimaryKey[baseId] is overridden by PrimaryKey[id]"
+        )
+    }
+
+    @Test
+    fun primaryKey_overrideFromParentEntityViaEntity() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity(primaryKeys = "baseId")
+                public class Base {
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent),
+                attributes = mapOf("primaryKeys" to "\"id\"")) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.size, `is`(1))
+            assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
+            assertThat(entity.primaryKey.autoGenerateId, `is`(false))
+        }.compilesWithoutError().withNoteContaining(
+                "PrimaryKey[baseId] is overridden by PrimaryKey[id]"
+        )
+    }
+
+    @Test
+    fun primaryKey_autoGenerate() {
+        listOf("long", "Long", "Integer", "int").forEach { type ->
+            singleEntity(
+                    """
+                @PrimaryKey(autoGenerate = true)
+                public $type id;
+                """) { entity, invocation ->
+                assertThat(entity.primaryKey.fields.size, `is`(1))
+                assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
+                assertThat(entity.primaryKey.autoGenerateId, `is`(true))
+            }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun primaryKey_autoGenerateBadType() {
+        listOf("String", "float", "Float", "Double", "double").forEach { type ->
+            singleEntity(
+                    """
+                @PrimaryKey(autoGenerate = true)
+                public $type id;
+                """) { entity, invocation ->
+                assertThat(entity.primaryKey.fields.size, `is`(1))
+                assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
+                assertThat(entity.primaryKey.autoGenerateId, `is`(true))
+            }.failsToCompile().withErrorContaining(
+                    ProcessorErrors.AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT)
+        }
+    }
+
+    @Test
+    fun primaryKey_embedded(){
+        singleEntity(
+                """
+                public int id;
+
+                @Embedded(prefix = "bar_")
+                @PrimaryKey
+                public Foo foo;
+
+                static class Foo {
+                    public int a;
+                    public int b;
+                }
+                """) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.map { it.columnName },
+                    `is`(listOf("bar_a", "bar_b")))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun primaryKey_embeddedInherited(){
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+
+                public class Base {
+                    long baseId;
+                    String name, lastName;
+                    @Embedded(prefix = "bar_")
+                    @PrimaryKey
+                    public Foo foo;
+
+                    static class Foo {
+                        public int a;
+                        public int b;
+                    }
+                }
+                """)
+        singleEntity(
+                """
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.map { it.columnName },
+                    `is`(listOf("bar_a", "bar_b")))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun primaryKey_overrideViaEmbedded() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+
+                @Entity(primaryKeys = "baseId")
+                public class Base {
+                    long baseId;
+                    String name, lastName;
+                }
+                """)
+        singleEntity(
+                """
+                public int id;
+                @Embedded(prefix = "bar_")
+                @PrimaryKey
+                public Foo foo;
+
+                static class Foo {
+                    public int a;
+                    public int b;
+                }
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.map { it.columnName },
+                    `is`(listOf("bar_a", "bar_b")))
+        }.compilesWithoutError().withNoteContaining(
+                "PrimaryKey[baseId] is overridden by PrimaryKey[foo > a, foo > b]")
+    }
+
+    @Test
+    fun primaryKey_overrideEmbedded() {
+        val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+
+                public class Base {
+                    long baseId;
+                    String name, lastName;
+                    @Embedded(prefix = "bar_")
+                    @PrimaryKey
+                    public Foo foo;
+
+                    static class Foo {
+                        public int a;
+                        public int b;
+                    }
+                }
+                """)
+        singleEntity(
+                """
+                @PrimaryKey
+                public int id;
+                """,
+                baseClass = "foo.bar.Base",
+                jfos = listOf(parent)) { entity, invocation ->
+            assertThat(entity.primaryKey.fields.map { it.columnName },
+                    `is`(listOf("id")))
+        }.compilesWithoutError().withNoteContaining(
+                "PrimaryKey[foo > a, foo > b] is overridden by PrimaryKey[id]")
+    }
+
+    @Test
+    fun relationInEntity() {
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                java.util.List<User> users;
+                """, jfos = listOf(COMMON.USER)
+        ) { entity, invocation ->
+        }.failsToCompile().withErrorContaining(RELATION_IN_ENTITY)
+    }
+
+    @Test
+    fun foreignKey_invalidAction() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "name",
+                    onDelete = 101
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.INVALID_FOREIGN_KEY_ACTION)
+    }
+
+    @Test
+    fun foreignKey_badEntity() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = dsa.class,
+                    parentColumns = "lastName",
+                    childColumns = "name"
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining("cannot find symbol")
+    }
+
+    @Test
+    fun foreignKey_notAnEntity() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.NOT_AN_ENTITY_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "name"
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.NOT_AN_ENTITY)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.foreignKeyNotAnEntity(
+                COMMON.NOT_AN_ENTITY_TYPE_NAME.toString()))
+    }
+
+    @Test
+    fun foreignKey_invalidChildColumn() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "namex"
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.foreignKeyChildColumnDoesNotExist(
+                "namex", listOf("id", "name")))
+    }
+
+    @Test
+    fun foreignKey_columnCountMismatch() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = {"name", "id"}
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.foreignKeyColumnNumberMismatch(
+                listOf("name", "id"), listOf("lastName")))
+    }
+
+    @Test
+    fun foreignKey_emptyChildColumns() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = {}
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST)
+    }
+
+    @Test
+    fun foreignKey_emptyParentColumns() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = {},
+                    childColumns = {"name"}
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST)
+    }
+
+    @Test
+    fun foreignKey_simple() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "name",
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE,
+                    deferred = true
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+            assertThat(entity.foreignKeys.size, `is`(1))
+            val fKey = entity.foreignKeys.first()
+            assertThat(fKey.parentTable, `is`("User"))
+            assertThat(fKey.parentColumns, `is`(listOf("lastName")))
+            assertThat(fKey.deferred, `is`(true))
+            assertThat(fKey.childFields.size, `is`(1))
+            val field = fKey.childFields.first()
+            assertThat(field.name, `is`("name"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun foreignKey_dontDuplicationChildIndex_SingleColumn() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "name",
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE,
+                    deferred = true
+                )}""".trimIndent(),
+                "indices" to """@Index("name")"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ) { entity, invocation ->
+        }.compilesWithoutWarnings()
+    }
+
+    @Test
+    fun foreignKey_dontDuplicationChildIndex_MultipleColumns() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = {"lastName", "name"},
+                    childColumns = {"lName", "name"},
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE,
+                    deferred = true
+                )}""".trimIndent(),
+                "indices" to """@Index({"lName", "name"})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                String lName;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ) { entity, invocation ->
+            assertThat(entity.indices.size, `is`(1))
+        }.compilesWithoutWarnings()
+    }
+
+    @Test
+    fun foreignKey_dontDuplicationChildIndex_WhenCovered() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = {"lastName"},
+                    childColumns = {"name"},
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE,
+                    deferred = true
+                )}""".trimIndent(),
+                "indices" to """@Index({"name", "lName"})"""
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                String lName;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ) { entity, invocation ->
+            assertThat(entity.indices.size, `is`(1))
+        }.compilesWithoutWarnings()
+    }
+
+    @Test
+    fun foreignKey_warnMissingChildIndex() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "name",
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE,
+                    deferred = true
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+            assertThat(entity.indices, `is`(emptyList()))
+        }.compilesWithoutError().withWarningContaining(
+                ProcessorErrors.foreignKeyMissingIndexInChildColumn("name"))
+    }
+
+    @Test
+    fun foreignKey_warnMissingChildrenIndex() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = {"lastName", "name"},
+                    childColumns = {"lName", "name"}
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                String lName;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+            assertThat(entity.indices, `is`(emptyList()))
+        }.compilesWithoutError().withWarningContaining(
+                ProcessorErrors.foreignKeyMissingIndexInChildColumns(listOf("lName", "name")))
+    }
+
+    @Test
+    fun foreignKey_dontIndexIfAlreadyPrimaryKey() {
+        val annotation = mapOf(
+                "foreignKeys" to """{@ForeignKey(
+                    entity = ${COMMON.USER_TYPE_NAME}.class,
+                    parentColumns = "lastName",
+                    childColumns = "id",
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE,
+                    deferred = true
+                )}""".trimIndent()
+        )
+        singleEntity(
+                """
+                @PrimaryKey
+                int id;
+                String name;
+                """,
+                attributes = annotation, jfos = listOf(COMMON.USER)
+        ){ entity, invocation ->
+            assertThat(entity.indices, `is`(emptyList()))
+        }.compilesWithoutWarnings()
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/FieldProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/FieldProcessorTest.kt
new file mode 100644
index 0000000..fb64c7c
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/FieldProcessorTest.kt
@@ -0,0 +1,364 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.solver.types.ColumnTypeAdapter
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.Field
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.mock
+import simpleRun
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+@Suppress("HasPlatformType")
+@RunWith(JUnit4::class)
+class FieldProcessorTest {
+    companion object {
+        const val ENTITY_PREFIX = """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Entity
+                abstract class MyEntity {
+                """
+        const val ENTITY_SUFFIX = "}"
+        val ALL_PRIMITIVES = arrayListOf(
+                TypeKind.INT,
+                TypeKind.BYTE,
+                TypeKind.SHORT,
+                TypeKind.LONG,
+                TypeKind.CHAR,
+                TypeKind.FLOAT,
+                TypeKind.DOUBLE)
+        val ARRAY_CONVERTER = JavaFileObjects.forSourceLines("foo.bar.MyConverter",
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                public class MyConverter {
+                ${ALL_PRIMITIVES.joinToString("\n") {
+                    val arrayDef = "${it.name.toLowerCase()}[]"
+                    "@TypeConverter public static String" +
+                            " arrayIntoString($arrayDef input) { return null;}" +
+                            "@TypeConverter public static $arrayDef" +
+                            " stringIntoArray${it.name}(String input) { return null;}"
+                }}
+                ${ALL_PRIMITIVES.joinToString("\n") {
+                    val arrayDef = "${it.box()}[]"
+                    "@TypeConverter public static String" +
+                            " arrayIntoString($arrayDef input) { return null;}" +
+                            "@TypeConverter public static $arrayDef" +
+                            " stringIntoArray${it.name}Boxed(String input) { return null;}"
+                }}
+                }
+                """)
+
+        private fun TypeKind.box(): String {
+            return "java.lang." + when (this) {
+                TypeKind.INT -> "Integer"
+                TypeKind.CHAR -> "Character"
+                else -> this.name.toLowerCase().capitalize()
+            }
+        }
+
+        // these 2 box methods are ugly but makes tests nicer and they are private
+        private fun TypeKind.typeMirror(invocation: TestInvocation): TypeMirror {
+            return invocation.processingEnv.typeUtils.getPrimitiveType(this)
+        }
+
+        private fun TypeKind.affinity(): SQLTypeAffinity {
+            return when (this) {
+                TypeKind.FLOAT, TypeKind.DOUBLE -> SQLTypeAffinity.REAL
+                else -> SQLTypeAffinity.INTEGER
+            }
+        }
+
+        private fun TypeKind.box(invocation: TestInvocation): TypeMirror {
+            return invocation.processingEnv.elementUtils.getTypeElement(box()).asType()
+        }
+    }
+
+    @Test
+    fun primitives() {
+        ALL_PRIMITIVES.forEach { primitive ->
+            singleEntity("${primitive.name.toLowerCase()} x;") { field, invocation ->
+                assertThat(field, `is`(
+                        Field(name = "x",
+                                type = primitive.typeMirror(invocation),
+                                element = field.element,
+                                affinity = primitive.affinity()
+                        )))
+            }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun boxed() {
+        ALL_PRIMITIVES.forEach { primitive ->
+            singleEntity("${primitive.box()} y;") { field, invocation ->
+                assertThat(field, `is`(
+                        Field(name = "y",
+                                type = primitive.box(invocation),
+                                element = field.element,
+                                affinity = primitive.affinity())))
+            }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun columnName() {
+        singleEntity("""
+            @ColumnInfo(name = "foo")
+            @PrimaryKey
+            int x;
+            """) { field, invocation ->
+            assertThat(field, `is`(
+                    Field(name = "x",
+                            type = TypeKind.INT.typeMirror(invocation),
+                            element = field.element,
+                            columnName = "foo",
+                            affinity = SQLTypeAffinity.INTEGER)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun indexed() {
+        singleEntity("""
+            @ColumnInfo(name = "foo", index = true)
+            int x;
+            """) { field, invocation ->
+            assertThat(field, `is`(
+                    Field(name = "x",
+                            type = TypeKind.INT.typeMirror(invocation),
+                            element = field.element,
+                            columnName = "foo",
+                            affinity = SQLTypeAffinity.INTEGER,
+                            indexed = true)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun emptyColumnName() {
+        singleEntity("""
+            @ColumnInfo(name = "")
+            int x;
+            """) { field, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.COLUMN_NAME_CANNOT_BE_EMPTY)
+    }
+
+    @Test
+    fun byteArrayWithEnforcedType() {
+        singleEntity("@TypeConverters(foo.bar.MyConverter.class)" +
+                "@ColumnInfo(typeAffinity = ColumnInfo.TEXT) byte[] arr;") { field, invocation ->
+            assertThat(field, `is`(Field(name = "arr",
+                    type = invocation.processingEnv.typeUtils.getArrayType(
+                            TypeKind.BYTE.typeMirror(invocation)),
+                    element = field.element,
+                    affinity = SQLTypeAffinity.TEXT)))
+            assertThat((field.cursorValueReader as? ColumnTypeAdapter)?.typeAffinity,
+                    `is`(SQLTypeAffinity.TEXT))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun primitiveArray() {
+        ALL_PRIMITIVES.forEach { primitive ->
+            singleEntity("@TypeConverters(foo.bar.MyConverter.class) " +
+                    "${primitive.toString().toLowerCase()}[] arr;") { field, invocation ->
+                assertThat(field, `is`(
+                        Field(name = "arr",
+                                type = invocation.processingEnv.typeUtils.getArrayType(
+                                        primitive.typeMirror(invocation)),
+                                element = field.element,
+                                affinity = if (primitive == TypeKind.BYTE) {
+                                    SQLTypeAffinity.BLOB
+                                } else {
+                                    SQLTypeAffinity.TEXT
+                                })))
+            }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun boxedArray() {
+        ALL_PRIMITIVES.forEach { primitive ->
+            singleEntity("@TypeConverters(foo.bar.MyConverter.class) " +
+                    "${primitive.box()}[] arr;") { field, invocation ->
+                assertThat(field, `is`(
+                        Field(name = "arr",
+                                type = invocation.processingEnv.typeUtils.getArrayType(
+                                        primitive.box(invocation)),
+                                element = field.element,
+                                affinity = SQLTypeAffinity.TEXT)))
+            }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun generic() {
+        singleEntity("""
+                static class BaseClass<T> {
+                    T item;
+                }
+                @Entity
+                static class Extending extends BaseClass<java.lang.Integer> {
+                }
+                """) { field, invocation ->
+            assertThat(field, `is`(Field(name = "item",
+                    type = TypeKind.INT.box(invocation),
+                    element = field.element,
+                    affinity = SQLTypeAffinity.INTEGER)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun unboundGeneric() {
+        singleEntity("""
+                @Entity
+                static class BaseClass<T> {
+                    T item;
+                }
+                """) { field, invocation -> }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS)
+    }
+
+    @Test
+    fun nameVariations() {
+        simpleRun {
+            assertThat(Field(mock(Element::class.java), "x", TypeKind.INT.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("x")))
+            assertThat(Field(mock(Element::class.java), "x", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("x")))
+            assertThat(Field(mock(Element::class.java), "xAll",
+                    TypeKind.BOOLEAN.typeMirror(it), SQLTypeAffinity.INTEGER)
+                    .nameWithVariations, `is`(arrayListOf("xAll")))
+        }
+    }
+
+    @Test
+    fun nameVariations_is() {
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "isX", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("isX", "x")))
+            assertThat(Field(elm, "isX", TypeKind.INT.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("isX")))
+            assertThat(Field(elm, "is", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("is")))
+            assertThat(Field(elm, "isAllItems", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations,
+                    `is`(arrayListOf("isAllItems", "allItems")))
+        }
+    }
+
+    @Test
+    fun nameVariations_has() {
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "hasX", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("hasX", "x")))
+            assertThat(Field(elm, "hasX", TypeKind.INT.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("hasX")))
+            assertThat(Field(elm, "has", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("has")))
+            assertThat(Field(elm, "hasAllItems", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations,
+                    `is`(arrayListOf("hasAllItems", "allItems")))
+        }
+    }
+
+    @Test
+    fun nameVariations_m() {
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "mall", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("mall")))
+            assertThat(Field(elm, "mallVars", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("mallVars")))
+            assertThat(Field(elm, "mAll", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("mAll", "all")))
+            assertThat(Field(elm, "m", TypeKind.INT.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("m")))
+            assertThat(Field(elm, "mallItems", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations,
+                    `is`(arrayListOf("mallItems")))
+            assertThat(Field(elm, "mAllItems", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations,
+                    `is`(arrayListOf("mAllItems", "allItems")))
+        }
+    }
+
+    @Test
+    fun nameVariations_underscore() {
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "_all", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("_all", "all")))
+            assertThat(Field(elm, "_", TypeKind.INT.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations, `is`(arrayListOf("_")))
+            assertThat(Field(elm, "_allItems", TypeKind.BOOLEAN.typeMirror(it),
+                    SQLTypeAffinity.INTEGER).nameWithVariations,
+                    `is`(arrayListOf("_allItems", "allItems")))
+        }
+    }
+
+    fun singleEntity(vararg input: String, handler: (Field, invocation: TestInvocation) -> Unit):
+            CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyEntity",
+                        ENTITY_PREFIX + input.joinToString("\n") + ENTITY_SUFFIX
+                ), ARRAY_CONVERTER))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(android.arch.persistence.room.Entity::class)
+                        .nextRunHandler { invocation ->
+                            val (owner, field) = invocation.roundEnv
+                                    .getElementsAnnotatedWith(Entity::class.java)
+                                    .map {
+                                        Pair(it, invocation.processingEnv.elementUtils
+                                                .getAllMembers(MoreElements.asType(it))
+                                                .firstOrNull { it.kind == ElementKind.FIELD })
+                                    }
+                                    .first { it.second != null }
+                            val entityContext =
+                                    EntityProcessor(invocation.context, MoreElements.asType(owner))
+                                            .context
+                            val parser = FieldProcessor(
+                                    baseContext = entityContext,
+                                    containing = MoreTypes.asDeclared(owner.asType()),
+                                    element = field!!,
+                                    bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+                                    fieldParent = null)
+                            handler(parser.process(), invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/InsertionMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/InsertionMethodProcessorTest.kt
new file mode 100644
index 0000000..aff6346
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/InsertionMethodProcessorTest.kt
@@ -0,0 +1,422 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import COMMON
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.OnConflictStrategy
+import android.arch.persistence.room.ext.CommonTypeNames
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.InsertionMethod
+import android.arch.persistence.room.vo.InsertionMethod.Type
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertAbout
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+@RunWith(JUnit4::class)
+class InsertionMethodProcessorTest {
+    companion object {
+        const val DAO_PREFIX = """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                import java.util.*;
+                @Dao
+                abstract class MyClass {
+                """
+        const val DAO_SUFFIX = "}"
+        val USER_TYPE_NAME : TypeName = COMMON.USER_TYPE_NAME
+        val BOOK_TYPE_NAME : TypeName = ClassName.get("foo.bar", "Book")
+    }
+
+    @Test
+    fun readNoParams() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void foo();
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("foo"))
+            assertThat(insertion.parameters.size, `is`(0))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+            assertThat(insertion.entities.size, `is`(0))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.INSERTION_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT)
+    }
+
+    @Test
+    fun insertSingle() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public long foo(User user);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("foo"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(USER_TYPE_NAME))
+            assertThat(param.entityType?.typeName(), `is`(USER_TYPE_NAME))
+            assertThat(insertion.entities["user"]?.typeName,
+                    `is`(ClassName.get("foo.bar", "User") as TypeName))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.LONG))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertNotAnEntity() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void foo(NotAnEntity notValid);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("foo"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.entityType, `is`(nullValue()))
+            assertThat(insertion.entities.size, `is`(0))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER
+        )
+    }
+
+    @Test
+    fun insertTwo() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void foo(User u1, User u2);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("foo"))
+
+            assertThat(insertion.parameters.size, `is`(2))
+            insertion.parameters.forEach {
+                assertThat(it.type.typeName(), `is`(USER_TYPE_NAME))
+                assertThat(it.entityType?.typeName(), `is`(USER_TYPE_NAME))
+            }
+            assertThat(insertion.entities.size, `is`(2))
+            assertThat(insertion.entities["u1"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.entities["u2"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.parameters.map { it.name }, `is`(listOf("u1", "u2")))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertList() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public List<Long> insertUsers(List<User> users);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("insertUsers"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(
+                            ClassName.get("java.util", "List"), USER_TYPE_NAME) as TypeName))
+            assertThat(param.entityType?.typeName(), `is`(USER_TYPE_NAME))
+            assertThat(insertion.entities.size, `is`(1))
+            assertThat(insertion.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.returnType.typeName(), `is`(
+                    ParameterizedTypeName.get(ClassName.get("java.util", "List"),
+                            ClassName.get("java.lang", "Long")) as TypeName
+            ))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertArray() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void insertUsers(User[] users);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("insertUsers"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ArrayTypeName.of(COMMON.USER_TYPE_NAME) as TypeName))
+            assertThat(insertion.entities.size, `is`(1))
+            assertThat(insertion.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertSet() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void insertUsers(Set<User> users);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("insertUsers"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(ClassName.get("java.util", "Set")
+                            , COMMON.USER_TYPE_NAME) as TypeName))
+            assertThat(insertion.entities.size, `is`(1))
+            assertThat(insertion.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertQueue() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void insertUsers(Queue<User> users);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("insertUsers"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(ClassName.get("java.util", "Queue")
+                            , USER_TYPE_NAME) as TypeName))
+            assertThat(insertion.entities.size, `is`(1))
+            assertThat(insertion.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertIterable() {
+        singleInsertMethod("""
+                @Insert
+                abstract public void insert(Iterable<User> users);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("insert"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(ParameterizedTypeName.get(
+                    ClassName.get("java.lang", "Iterable"), USER_TYPE_NAME) as TypeName))
+            assertThat(insertion.entities.size, `is`(1))
+            assertThat(insertion.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertCustomCollection() {
+        singleInsertMethod("""
+                static class MyList<Irrelevant, Item> extends ArrayList<Item> {}
+                @Insert
+                abstract public void insert(MyList<String, User> users);
+                """) { insertion, invocation ->
+            assertThat(insertion.name, `is`("insert"))
+            assertThat(insertion.parameters.size, `is`(1))
+            val param = insertion.parameters.first()
+            assertThat(param.type.typeName(), `is`(ParameterizedTypeName.get(
+                    ClassName.get("foo.bar", "MyClass.MyList"),
+                    CommonTypeNames.STRING, USER_TYPE_NAME) as TypeName))
+            assertThat(insertion.entities.size, `is`(1))
+            assertThat(insertion.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun insertDifferentTypes() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void foo(User u1, Book b1);
+                """) { insertion, invocation ->
+            assertThat(insertion.parameters.size, `is`(2))
+            assertThat(insertion.parameters[0].type.typeName().toString(),
+                    `is`("foo.bar.User"))
+            assertThat(insertion.parameters[1].type.typeName().toString(),
+                    `is`("foo.bar.Book"))
+            assertThat(insertion.parameters.map { it.name }, `is`(listOf("u1", "b1")))
+            assertThat(insertion.returnType.typeName(), `is`(TypeName.VOID))
+            assertThat(insertion.entities.size, `is`(2))
+            assertThat(insertion.entities["u1"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(insertion.entities["b1"]?.typeName, `is`(BOOK_TYPE_NAME))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun onConflict_Default() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public void foo(User user);
+                """) { insertion, invocation ->
+            assertThat(insertion.onConflict, `is`(OnConflictStrategy.ABORT))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun onConflict_Invalid() {
+        singleInsertMethod(
+                """
+                @Insert(onConflict = -1)
+                abstract public void foo(User user);
+                """) { insertion, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.INVALID_ON_CONFLICT_VALUE)
+    }
+
+    @Test
+    fun onConflict_EachValue() {
+        listOf(
+                Pair("REPLACE", 1),
+                Pair("ROLLBACK", 2),
+                Pair("ABORT", 3),
+                Pair("FAIL", 4),
+                Pair("IGNORE", 5)
+        ).forEach { pair ->
+            singleInsertMethod(
+                    """
+                @Insert(onConflict=OnConflictStrategy.${pair.first})
+                abstract public void foo(User user);
+                """) { insertion, invocation ->
+                assertThat(insertion.onConflict, `is`(pair.second))
+            }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun invalidReturnType() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public int foo(User user);
+                """) { insertion, invocation ->
+            assertThat(insertion.insertionType, `is`(nullValue()))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.INVALID_INSERTION_METHOD_RETURN_TYPE)
+    }
+
+    @Test
+    fun mismatchedReturnType() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public long[] foo(User user);
+                """) { insertion, invocation ->
+            assertThat(insertion.insertionType, `is`(nullValue()))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.insertionMethodReturnTypeMismatch(
+                        ArrayTypeName.of(TypeName.LONG),
+                        InsertionMethodProcessor.SINGLE_ITEM_SET.map { it.returnTypeName }))
+    }
+
+    @Test
+    fun mismatchedReturnType2() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public long foo(User... user);
+                """) { insertion, invocation ->
+            assertThat(insertion.insertionType, `is`(nullValue()))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.insertionMethodReturnTypeMismatch(
+                        TypeName.LONG,
+                        InsertionMethodProcessor.MULTIPLE_ITEM_SET.map { it.returnTypeName }))
+    }
+
+    @Test
+    fun mismatchedReturnType3() {
+        singleInsertMethod(
+                """
+                @Insert
+                abstract public long foo(User user1, User user2);
+                """) { insertion, invocation ->
+            assertThat(insertion.insertionType, `is`(nullValue()))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.insertionMethodReturnTypeMismatch(
+                        TypeName.LONG,
+                        InsertionMethodProcessor.VOID_SET.map { it.returnTypeName }))
+    }
+
+    @Test
+    fun validReturnTypes() {
+        listOf(
+                Pair("void", Type.INSERT_VOID),
+                Pair("long", Type.INSERT_SINGLE_ID),
+                Pair("long[]", Type.INSERT_ID_ARRAY),
+                Pair("Long[]", Type.INSERT_ID_ARRAY_BOX),
+                Pair("List<Long>", Type.INSERT_ID_LIST)
+        ).forEach { pair ->
+            val dots = if (pair.second in setOf(Type.INSERT_ID_LIST, Type.INSERT_ID_ARRAY,
+                    Type.INSERT_ID_ARRAY_BOX)) {
+                "..."
+            } else {
+                ""
+            }
+            singleInsertMethod(
+                    """
+                @Insert
+                abstract public ${pair.first} foo(User$dots user);
+                """) { insertion, invocation ->
+                assertThat(insertion.insertMethodTypeFor(insertion.parameters.first()),
+                        `is`(pair.second))
+                assertThat(pair.toString(), insertion.insertionType, `is`(pair.second))
+            }.compilesWithoutError()
+        }
+    }
+
+    fun singleInsertMethod(vararg input: String,
+                          handler: (InsertionMethod, TestInvocation) -> Unit):
+            CompileTester {
+        return assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyClass",
+                        DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
+                ), COMMON.USER, COMMON.BOOK, COMMON.NOT_AN_ENTITY))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(Insert::class, Dao::class)
+                        .nextRunHandler { invocation ->
+                            val (owner, methods) = invocation.roundEnv
+                                    .getElementsAnnotatedWith(Dao::class.java)
+                                    .map {
+                                        Pair(it,
+                                                invocation.processingEnv.elementUtils
+                                                        .getAllMembers(MoreElements.asType(it))
+                                                        .filter {
+                                                            MoreElements.isAnnotationPresent(it,
+                                                                    Insert::class.java)
+                                                        }
+                                        )
+                                    }.filter { it.second.isNotEmpty() }.first()
+                            val processor = InsertionMethodProcessor(
+                                    baseContext = invocation.context,
+                                    containing = MoreTypes.asDeclared(owner.asType()),
+                                    executableElement = MoreElements.asExecutable(methods.first()))
+                            val processed = processor.process()
+                            handler(processed, invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/PojoProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/PojoProcessorTest.kt
new file mode 100644
index 0000000..0a5026a
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/PojoProcessorTest.kt
@@ -0,0 +1,640 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import COMMON
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_TYPE
+import android.arch.persistence.room.processor.ProcessorErrors.ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY
+import android.arch.persistence.room.processor.ProcessorErrors.POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
+import android.arch.persistence.room.processor.ProcessorErrors.RELATION_NOT_COLLECTION
+import android.arch.persistence.room.processor.ProcessorErrors.relationCannotFindEntityField
+import android.arch.persistence.room.processor.ProcessorErrors.relationCannotFindParentEntityField
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.vo.CallType
+import android.arch.persistence.room.vo.Constructor
+import android.arch.persistence.room.vo.EmbeddedField
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.Pojo
+import android.arch.persistence.room.vo.RelationCollector
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.not
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.CoreMatchers.sameInstance
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import simpleRun
+import javax.lang.model.element.Element
+import javax.tools.JavaFileObject
+
+/**
+ * Some of the functionality is tested via EntityProcessor.
+ */
+@RunWith(JUnit4::class)
+class PojoProcessorTest {
+
+    companion object {
+        val MY_POJO = ClassName.get("foo.bar", "MyPojo")
+        val HEADER = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            import java.util.*;
+            public class MyPojo {
+            """
+        val FOOTER = "\n}"
+    }
+
+    private fun String.toJFO(qName: String) = JavaFileObjects.forSourceLines(qName, this)
+
+    @Test
+    fun inheritedPrivate() {
+        val parent = """
+            package foo.bar.x;
+            import android.arch.persistence.room.*;
+            public class BaseClass {
+                private String baseField;
+                public String getBaseField(){ return baseField; }
+                public void setBaseField(String baseField){ }
+            }
+        """
+        simpleRun(
+                """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                public class ${MY_POJO.simpleName()} extends foo.bar.x.BaseClass {
+                    public String myField;
+                }
+                """.toJFO(MY_POJO.toString()),
+                parent.toJFO("foo.bar.x.BaseClass")) { invocation ->
+            val pojo = PojoProcessor(baseContext = invocation.context,
+                    element = invocation.typeElement(MY_POJO.toString()),
+                    bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                    parent = null).process()
+            assertThat(pojo.fields.find { it.name == "myField" }, notNullValue())
+            assertThat(pojo.fields.find { it.name == "baseField" }, notNullValue())
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun embedded() {
+        singleRun(
+                """
+                int id;
+                @Embedded
+                Point myPoint;
+                static class Point {
+                    int x;
+                    int y;
+                }
+                """
+        ) { pojo ->
+            assertThat(pojo.fields.size, `is`(3))
+            assertThat(pojo.fields[1].name, `is`("x"))
+            assertThat(pojo.fields[2].name, `is`("y"))
+            assertThat(pojo.fields[0].parent, nullValue())
+            assertThat(pojo.fields[1].parent, notNullValue())
+            assertThat(pojo.fields[2].parent, notNullValue())
+            val parent = pojo.fields[2].parent!!
+            assertThat(parent.prefix, `is`(""))
+            assertThat(parent.field.name, `is`("myPoint"))
+            assertThat(parent.pojo.typeName,
+                    `is`(ClassName.get("foo.bar.MyPojo", "Point") as TypeName))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun embeddedWithPrefix() {
+        singleRun(
+                """
+                int id;
+                @Embedded(prefix = "foo")
+                Point myPoint;
+                static class Point {
+                    int x;
+                    @ColumnInfo(name = "y2")
+                    int y;
+                }
+                """
+        ) { pojo ->
+            assertThat(pojo.fields.size, `is`(3))
+            assertThat(pojo.fields[1].name, `is`("x"))
+            assertThat(pojo.fields[2].name, `is`("y"))
+            assertThat(pojo.fields[1].columnName, `is`("foox"))
+            assertThat(pojo.fields[2].columnName, `is`("fooy2"))
+            val parent = pojo.fields[2].parent!!
+            assertThat(parent.prefix, `is`("foo"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun nestedEmbedded() {
+        singleRun(
+                """
+                int id;
+                @Embedded(prefix = "foo")
+                Point myPoint;
+                static class Point {
+                    int x;
+                    @ColumnInfo(name = "y2")
+                    int y;
+                    @Embedded(prefix = "bar")
+                    Coordinate coordinate;
+                }
+                static class Coordinate {
+                    double lat;
+                    double lng;
+                    @Ignore
+                    String ignored;
+                }
+                """
+        ) { pojo ->
+            assertThat(pojo.fields.size, `is`(5))
+            assertThat(pojo.fields.map { it.columnName }, `is`(
+                    listOf("id", "foox", "fooy2", "foobarlat", "foobarlng")))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun duplicateColumnNames() {
+        singleRun(
+                """
+                int id;
+                @ColumnInfo(name = "id")
+                int another;
+                """
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.pojoDuplicateFieldNames("id", listOf("id", "another"))
+        ).and().withErrorContaining(
+                POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
+        ).and().withErrorCount(3)
+    }
+
+    @Test
+    fun duplicateColumnNamesFromEmbedded() {
+        singleRun(
+                """
+                int id;
+                @Embedded
+                Foo foo;
+                static class Foo {
+                    @ColumnInfo(name = "id")
+                    int x;
+                }
+                """
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.pojoDuplicateFieldNames("id", listOf("id", "foo > x"))
+        ).and().withErrorContaining(
+                POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
+        ).and().withErrorCount(3)
+    }
+
+    @Test
+    fun dropSubPrimaryKeyNoWarningForPojo() {
+        singleRun(
+                """
+                @PrimaryKey
+                int id;
+                @Embedded
+                Point myPoint;
+                static class Point {
+                    @PrimaryKey
+                    int x;
+                    int y;
+                }
+                """
+        ) { pojo ->
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun relation_notCollection() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                public User user;
+                """, COMMON.USER
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(RELATION_NOT_COLLECTION)
+    }
+
+    @Test
+    fun relation_columnInfo() {
+        singleRun(
+                """
+                int id;
+                @ColumnInfo
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION)
+    }
+
+    @Test
+    fun relation_notEntity() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                public List<NotAnEntity> user;
+                """, COMMON.NOT_AN_ENTITY
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY)
+    }
+
+    @Test
+    fun relation_missingParent() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "idk", entityColumn = "uid")
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                relationCannotFindParentEntityField("foo.bar.MyPojo", "idk", listOf("id"))
+        )
+    }
+
+    @Test
+    fun relation_missingEntityField() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "idk")
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                relationCannotFindEntityField("foo.bar.User", "idk",
+                        listOf("uid", "name", "lastName", "age"))
+        )
+    }
+
+    @Test
+    fun relation_missingType() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                public List<User> user;
+                """
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(CANNOT_FIND_TYPE)
+    }
+
+    @Test
+    fun relation_nestedField() {
+        singleRun(
+                """
+                static class Nested {
+                    @ColumnInfo(name = "foo")
+                    public int id;
+                }
+                @Embedded
+                Nested nested;
+                @Relation(parentColumn = "foo", entityColumn = "uid")
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo ->
+            assertThat(pojo.relations.first().parentField.columnName, `is`("foo"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun relation_nestedRelation() {
+        singleRun(
+                """
+                static class UserWithNested {
+                    @Embedded
+                    public User user;
+                    @Relation(parentColumn = "uid", entityColumn = "uid")
+                    public List<User> selfs;
+                }
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid", entity = User.class)
+                public List<UserWithNested> user;
+                """, COMMON.USER
+        ) { pojo, invocation ->
+            assertThat(pojo.relations.first().parentField.name, `is`("id"))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun relation_affinityMismatch() {
+        singleRun(
+                """
+                String id;
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo, invocation ->
+            // trigger assignment evaluation
+            RelationCollector.createCollectors(invocation.context, pojo.relations)
+            assertThat(pojo.relations.size, `is`(1))
+            assertThat(pojo.relations.first().entityField.name, `is`("uid"))
+            assertThat(pojo.relations.first().parentField.name, `is`("id"))
+        }.compilesWithoutError().withWarningContaining(
+                ProcessorErrors.relationAffinityMismatch(
+                        parentAffinity = SQLTypeAffinity.TEXT,
+                        childAffinity = SQLTypeAffinity.INTEGER,
+                        parentColumn = "id",
+                        childColumn = "uid")
+        )
+    }
+
+    @Test
+    fun relation_simple() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid")
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo ->
+            assertThat(pojo.relations.size, `is`(1))
+            assertThat(pojo.relations.first().entityField.name, `is`("uid"))
+            assertThat(pojo.relations.first().parentField.name, `is`("id"))
+        }.compilesWithoutError().withWarningCount(0)
+    }
+
+    @Test
+    fun relation_badProjection() {
+        singleRun(
+                """
+                int id;
+                @Relation(parentColumn = "id", entityColumn = "uid", projection={"i_dont_exist"})
+                public List<User> user;
+                """, COMMON.USER
+        ) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.relationBadProject("foo.bar.User", listOf("i_dont_exist"),
+                        listOf("uid", "name", "lastName", "ageColumn"))
+        )
+    }
+
+    @Test
+    fun cache() {
+        val pojo = """
+            $HEADER
+            int id;
+            $FOOTER
+            """.toJFO(MY_POJO.toString())
+        simpleRun(pojo) { invocation ->
+            val element = invocation.typeElement(MY_POJO.toString())
+            val pojo1 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.BIND_TO_STMT, null).process()
+            assertThat(pojo1, notNullValue())
+            val pojo2 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.BIND_TO_STMT, null).process()
+            assertThat(pojo2, sameInstance(pojo1))
+
+            val pojo3 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.READ_FROM_CURSOR, null).process()
+            assertThat(pojo3, notNullValue())
+            assertThat(pojo3, not(sameInstance(pojo1)))
+
+            val pojo4 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.TWO_WAY, null).process()
+            assertThat(pojo4, notNullValue())
+            assertThat(pojo4, not(sameInstance(pojo1)))
+            assertThat(pojo4, not(sameInstance(pojo3)))
+
+            val pojo5 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.TWO_WAY, null).process()
+            assertThat(pojo5, sameInstance(pojo4))
+
+            val type = invocation.context.COMMON_TYPES.STRING
+            val mockElement = mock(Element::class.java)
+            doReturn(type).`when`(mockElement).asType()
+            val fakeField = Field(
+                    element = mockElement,
+                    name = "foo",
+                    type = type,
+                    affinity = SQLTypeAffinity.TEXT,
+                    columnName = "foo",
+                    parent = null,
+                    indexed =  false
+            )
+            val fakeEmbedded = EmbeddedField(fakeField, "", null)
+
+            val pojo6 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.TWO_WAY, fakeEmbedded).process()
+            assertThat(pojo6, notNullValue())
+            assertThat(pojo6, not(sameInstance(pojo1)))
+            assertThat(pojo6, not(sameInstance(pojo3)))
+            assertThat(pojo6, not(sameInstance(pojo4)))
+
+            val pojo7 = PojoProcessor(invocation.context, element,
+                    FieldProcessor.BindingScope.TWO_WAY, fakeEmbedded).process()
+            assertThat(pojo7, sameInstance(pojo6))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun constructor_empty() {
+        val pojo = """
+            public String mName;
+            """
+        singleRun(pojo) { pojo ->
+            assertThat(pojo.constructor, notNullValue())
+            assertThat(pojo.constructor?.params, `is`(emptyList<Constructor.Param>()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun constructor_ambiguous_twoFieldsExcatMatch() {
+        val pojo = """
+            public String mName;
+            public String _name;
+            public MyPojo(String mName) {
+            }
+            """
+        singleRun(pojo) { pojo ->
+            val param = pojo.constructor?.params?.first()
+            assertThat(param, instanceOf(Constructor.FieldParam::class.java))
+            assertThat((param as Constructor.FieldParam).field.name,  `is`("mName"))
+            assertThat(pojo.fields.find { it.name == "mName" }?.setter?.callType,
+                    `is`(CallType.CONSTRUCTOR))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun constructor_ambiguous_oneTypeMatches() {
+        val pojo = """
+            public String mName;
+            public int _name;
+            public MyPojo(String name) {
+            }
+            """
+        singleRun(pojo) { pojo ->
+            val param = pojo.constructor?.params?.first()
+            assertThat(param, instanceOf(Constructor.FieldParam::class.java))
+            assertThat((param as Constructor.FieldParam).field.name,  `is`("mName"))
+            assertThat(pojo.fields.find { it.name == "mName" }?.setter?.callType,
+                    `is`(CallType.CONSTRUCTOR))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun constructor_ambiguous_twoFields() {
+        val pojo = """
+            String mName;
+            String _name;
+            public MyPojo(String name) {
+            }
+            """
+        singleRun(pojo) { pojo ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.ambigiousConstructor(MY_POJO.toString(),
+                        "name", listOf("mName", "_name"))
+        )
+    }
+
+    @Test
+    fun constructor_noMatchBadType() {
+        singleRun("""
+            int foo;
+            public MyPojo(String foo) {
+            }
+        """) { pojo ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_POJO_CONSTRUCTOR)
+    }
+
+    @Test
+    fun constructor_noMatch() {
+        singleRun("""
+            String mName;
+            String _name;
+            public MyPojo(String foo) {
+            }
+        """) { pojo ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_POJO_CONSTRUCTOR)
+    }
+
+    @Test
+    fun constructor_noMatchMultiArg() {
+        singleRun("""
+            String mName;
+            int bar;
+            public MyPojo(String foo, String name) {
+            }
+        """) { pojo ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_POJO_CONSTRUCTOR)
+    }
+
+    @Test
+    fun constructor_multipleMatching() {
+        singleRun("""
+            String mName;
+            String mLastName;
+            public MyPojo(String name) {
+            }
+            public MyPojo(String name, String lastName) {
+            }
+        """) { pojo ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS)
+    }
+
+    @Test
+    fun constructor_multipleMatchingWithIgnored() {
+        singleRun("""
+            String mName;
+            String mLastName;
+            @Ignore
+            public MyPojo(String name) {
+            }
+            public MyPojo(String name, String lastName) {
+            }
+        """) { pojo ->
+            assertThat(pojo.constructor, notNullValue())
+            assertThat(pojo.constructor?.params?.size, `is`(2))
+            assertThat(pojo.fields.find { it.name == "mName" }?.setter?.callType,
+                    `is`(CallType.CONSTRUCTOR))
+            assertThat(pojo.fields.find { it.name == "mLastName" }?.setter?.callType,
+                    `is`(CallType.CONSTRUCTOR))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun constructor_dontTryForBindToScope() {
+        singleRun("""
+            String mName;
+            String mLastName;
+        """) { pojo, invocation ->
+            val process2 = PojoProcessor(baseContext = invocation.context,
+                    element = invocation.typeElement(MY_POJO.toString()),
+                    bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT,
+                    parent = null).process()
+            assertThat(process2.constructor, nullValue())
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun constructor_bindForTwoWay() {
+        singleRun("""
+            String mName;
+            String mLastName;
+        """) { pojo, invocation ->
+            val process2 = PojoProcessor(baseContext = invocation.context,
+                    element = invocation.typeElement(MY_POJO.toString()),
+                    bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+                    parent = null).process()
+            assertThat(process2.constructor, notNullValue())
+        }.compilesWithoutError()
+    }
+
+    fun singleRun(code: String, vararg jfos:JavaFileObject, handler: (Pojo) -> Unit)
+            : CompileTester {
+        return singleRun(code, *jfos) { pojo, invocation ->
+            handler(pojo)
+        }
+    }
+
+    fun singleRun(code: String, vararg jfos:JavaFileObject,
+                  handler: (Pojo, TestInvocation) -> Unit): CompileTester {
+        val pojoJFO = """
+                $HEADER
+                $code
+                $FOOTER
+                """.toJFO(MY_POJO.toString())
+        val all = (jfos.toList() + pojoJFO).toTypedArray()
+        return simpleRun(*all) { invocation ->
+            handler.invoke(
+                    PojoProcessor(baseContext = invocation.context,
+                            element = invocation.typeElement(MY_POJO.toString()),
+                            bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                            parent = null).process(),
+                    invocation
+            )
+        }
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt
new file mode 100644
index 0000000..f7affbc
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt
@@ -0,0 +1,647 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import COMMON
+import android.arch.persistence.room.ColumnInfo
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.PrimaryKey
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.ext.LifecyclesTypeNames
+import android.arch.persistence.room.ext.hasAnnotation
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_QUERY_RESULT_ADAPTER
+import android.arch.persistence.room.solver.query.result.LiveDataQueryResultBinder
+import android.arch.persistence.room.solver.query.result.PojoRowAdapter
+import android.arch.persistence.room.solver.query.result.SingleEntityQueryResultAdapter
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.QueryMethod
+import android.arch.persistence.room.vo.Warning
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertAbout
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
+import createVerifierFromEntities
+import mockElementAndType
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.TypeKind.INT
+import javax.lang.model.type.TypeMirror
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+@RunWith(Parameterized::class)
+class QueryMethodProcessorTest(val enableVerification: Boolean) {
+    companion object {
+        const val DAO_PREFIX = """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                @Dao
+                abstract class MyClass {
+                """
+        const val DAO_SUFFIX = "}"
+        val POJO = ClassName.get("foo.bar", "MyClass.Pojo")
+        @Parameterized.Parameters(name = "enableDbVerification={0}")
+        @JvmStatic
+        fun getParams() = arrayOf(true, false)
+
+        fun createField(name: String, columnName: String? = null): Field {
+            val (element, type) = mockElementAndType()
+            return Field(
+                    element = element,
+                    name = name,
+                    type = type,
+                    columnName = columnName ?: name,
+                    affinity = null
+            )
+        }
+    }
+
+    @Test
+    fun testReadNoParams() {
+        singleQueryMethod(
+                """
+                @Query("SELECT * from User")
+                abstract public int[] foo();
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.parameters.size, `is`(0))
+            assertThat(parsedQuery.returnType.typeName(),
+                    `is`(ArrayTypeName.of(TypeName.INT) as TypeName))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testSingleParam() {
+        singleQueryMethod(
+                """
+                @Query("SELECT * from User where uid = :x")
+                abstract public long foo(int x);
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.returnType.typeName(), `is`(TypeName.LONG))
+            assertThat(parsedQuery.parameters.size, `is`(1))
+            val param = parsedQuery.parameters.first()
+            assertThat(param.name, `is`("x"))
+            assertThat(param.type,
+                    `is`(invocation.processingEnv.typeUtils.getPrimitiveType(INT) as TypeMirror))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testVarArgs() {
+        singleQueryMethod(
+                """
+                @Query("SELECT * from User where uid in (:ids)")
+                abstract public long foo(int... ids);
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.returnType.typeName(), `is`(TypeName.LONG))
+            assertThat(parsedQuery.parameters.size, `is`(1))
+            val param = parsedQuery.parameters.first()
+            assertThat(param.name, `is`("ids"))
+            val types = invocation.processingEnv.typeUtils
+            assertThat(param.type,
+                    `is`(types.getArrayType(types.getPrimitiveType(INT)) as TypeMirror))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testParamBindingMatchingNoName() {
+        singleQueryMethod(
+                """
+                @Query("SELECT uid from User where uid = :id")
+                abstract public long getIdById(int id);
+                """) { parsedQuery, invocation ->
+            val section = parsedQuery.query.bindSections.first()
+            val param = parsedQuery.parameters.firstOrNull()
+            assertThat(section, notNullValue())
+            assertThat(param, notNullValue())
+            assertThat(parsedQuery.sectionToParamMapping, `is`(listOf(Pair(section, param))))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testParamBindingMatchingSimpleBind() {
+        singleQueryMethod(
+                """
+                @Query("SELECT uid from User where uid = :id")
+                abstract public long getIdById(int id);
+                """) { parsedQuery, invocation ->
+            val section = parsedQuery.query.bindSections.first()
+            val param = parsedQuery.parameters.firstOrNull()
+            assertThat(section, notNullValue())
+            assertThat(param, notNullValue())
+            assertThat(parsedQuery.sectionToParamMapping,
+                    `is`(listOf(Pair(section, param))))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testParamBindingTwoBindVarsIntoTheSameParameter() {
+        singleQueryMethod(
+                """
+                @Query("SELECT uid from User where uid = :id OR uid = :id")
+                abstract public long getIdById(int id);
+                """) { parsedQuery, invocation ->
+            val section = parsedQuery.query.bindSections[0]
+            val section2 = parsedQuery.query.bindSections[1]
+            val param = parsedQuery.parameters.firstOrNull()
+            assertThat(section, notNullValue())
+            assertThat(section2, notNullValue())
+            assertThat(param, notNullValue())
+            assertThat(parsedQuery.sectionToParamMapping,
+                    `is`(listOf(Pair(section, param), Pair(section2, param))))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testMissingParameterForBinding() {
+        singleQueryMethod(
+                """
+                @Query("SELECT uid from User where uid = :id OR uid = :uid")
+                abstract public long getIdById(int id);
+                """) { parsedQuery, invocation ->
+            val section = parsedQuery.query.bindSections[0]
+            val section2 = parsedQuery.query.bindSections[1]
+            val param = parsedQuery.parameters.firstOrNull()
+            assertThat(section, notNullValue())
+            assertThat(section2, notNullValue())
+            assertThat(param, notNullValue())
+            assertThat(parsedQuery.sectionToParamMapping,
+                    `is`(listOf(Pair(section, param), Pair(section2, null))))
+        }
+                .failsToCompile()
+                .withErrorContaining(
+                        ProcessorErrors.missingParameterForBindVariable(listOf(":uid")))
+    }
+
+    @Test
+    fun test2MissingParameterForBinding() {
+        singleQueryMethod(
+                """
+                @Query("SELECT uid from User where name = :bar AND uid = :id OR uid = :uid")
+                abstract public long getIdById(int id);
+                """) { parsedQuery, invocation ->
+            val bar = parsedQuery.query.bindSections[0]
+            val id = parsedQuery.query.bindSections[1]
+            val uid = parsedQuery.query.bindSections[2]
+            val param = parsedQuery.parameters.firstOrNull()
+            assertThat(bar, notNullValue())
+            assertThat(id, notNullValue())
+            assertThat(uid, notNullValue())
+            assertThat(param, notNullValue())
+            assertThat(parsedQuery.sectionToParamMapping,
+                    `is`(listOf(Pair(bar, null), Pair(id, param), Pair(uid, null))))
+        }
+                .failsToCompile()
+                .withErrorContaining(
+                        ProcessorErrors.missingParameterForBindVariable(listOf(":bar", ":uid")))
+    }
+
+    @Test
+    fun testUnusedParameters() {
+        singleQueryMethod(
+                """
+                @Query("SELECT uid from User where name = :bar")
+                abstract public long getIdById(int bar, int whyNotUseMe);
+                """) { parsedQuery, invocation ->
+            val bar = parsedQuery.query.bindSections[0]
+            val barParam = parsedQuery.parameters.firstOrNull()
+            assertThat(bar, notNullValue())
+            assertThat(barParam, notNullValue())
+            assertThat(parsedQuery.sectionToParamMapping,
+                    `is`(listOf(Pair(bar, barParam))))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.unusedQueryMethodParameter(listOf("whyNotUseMe")))
+    }
+
+    @Test
+    fun testNameWithUnderscore() {
+        singleQueryMethod(
+                """
+                @Query("select * from User where uid = :_blah")
+                abstract public long getSth(int _blah);
+                """
+        ) { parsedQuery, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE)
+    }
+
+    @Test
+    fun testGenericReturnType() {
+        singleQueryMethod(
+                """
+                @Query("select * from User")
+                abstract public <T> java.util.List<T> foo(int x);
+                """) { parsedQuery, invocation ->
+            val expected: TypeName = ParameterizedTypeName.get(ClassName.get(List::class.java),
+                    TypeVariableName.get("T"))
+            assertThat(parsedQuery.returnType.typeName(), `is`(expected))
+        }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS)
+    }
+
+    @Test
+    fun testBadQuery() {
+        singleQueryMethod(
+                """
+                @Query("select * from :1 :2")
+                abstract public long foo(int x);
+                """) { parsedQuery, invocation ->
+            // do nothing
+        }.failsToCompile()
+                .withErrorContaining("UNEXPECTED_CHAR=:")
+    }
+
+    @Test
+    fun testBoundGeneric() {
+        singleQueryMethod(
+                """
+                static abstract class BaseModel<T> {
+                    @Query("select COUNT(*) from User")
+                    abstract public T getT();
+                }
+                @Dao
+                static abstract class ExtendingModel extends BaseModel<Integer> {
+                }
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.returnType.typeName(),
+                    `is`(ClassName.get(Integer::class.java) as TypeName))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testBoundGenericParameter() {
+        singleQueryMethod(
+                """
+                static abstract class BaseModel<T> {
+                    @Query("select COUNT(*) from User where :t")
+                    abstract public int getT(T t);
+                }
+                @Dao
+                static abstract class ExtendingModel extends BaseModel<Integer> {
+                }
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.parameters.first().type,
+                    `is`(invocation.processingEnv.elementUtils
+                            .getTypeElement("java.lang.Integer").asType()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testReadDeleteWithBadReturnType() {
+        singleQueryMethod(
+                """
+                @Query("DELETE from User where uid = :id")
+                abstract public float foo(int id);
+                """) { parsedQuery, invocation ->
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.DELETION_METHODS_MUST_RETURN_VOID_OR_INT
+        )
+    }
+
+    @Test
+    fun testSimpleDelete() {
+        singleQueryMethod(
+                """
+                @Query("DELETE from User where uid = :id")
+                abstract public int foo(int id);
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.parameters.size, `is`(1))
+            assertThat(parsedQuery.returnType.typeName(), `is`(TypeName.INT))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testVoidDeleteQuery() {
+        singleQueryMethod(
+                """
+                @Query("DELETE from User where uid = :id")
+                abstract public void foo(int id);
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.parameters.size, `is`(1))
+            assertThat(parsedQuery.returnType.typeName(), `is`(TypeName.VOID))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testVoidUpdateQuery() {
+        singleQueryMethod(
+                """
+                @Query("update user set name = :name")
+                abstract public void updateAllNames(String name);
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("updateAllNames"))
+            assertThat(parsedQuery.parameters.size, `is`(1))
+            assertThat(parsedQuery.returnType.typeName(), `is`(TypeName.VOID))
+            assertThat(parsedQuery.parameters.first().type.typeName(),
+                    `is`(invocation.context.COMMON_TYPES.STRING.typeName()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testLiveDataQuery() {
+        singleQueryMethod(
+                """
+                @Query("select name from user where uid = :id")
+                abstract ${LifecyclesTypeNames.LIVE_DATA}<String> nameLiveData(String id);
+                """
+        ) { parsedQuery, invocation ->
+            assertThat(parsedQuery.returnType.typeName(),
+                    `is`(ParameterizedTypeName.get(LifecyclesTypeNames.LIVE_DATA,
+                            String::class.typeName()) as TypeName))
+            assertThat(parsedQuery.queryResultBinder,
+                    instanceOf(LiveDataQueryResultBinder::class.java))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testNonSelectLiveData() {
+        singleQueryMethod(
+                """
+                @Query("delete from user where uid = :id")
+                abstract ${LifecyclesTypeNames.LIVE_DATA}<Integer> deleteLiveData(String id);
+                """
+        ) { parsedQuery, invocation ->
+        }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.DELETION_METHODS_MUST_RETURN_VOID_OR_INT)
+    }
+
+    @Test
+    fun skipVerification() {
+        singleQueryMethod(
+                """
+                @SkipQueryVerification
+                @Query("SELECT foo from User")
+                abstract public int[] foo();
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.parameters.size, `is`(0))
+            assertThat(parsedQuery.returnType.typeName(),
+                    `is`(ArrayTypeName.of(TypeName.INT) as TypeName))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun suppressWarnings() {
+        singleQueryMethod("""
+                @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
+                @Query("SELECT uid from User")
+                abstract public int[] foo();
+                """) { method, invocation ->
+            assertThat(QueryMethodProcessor(
+                    baseContext = invocation.context,
+                    containing = Mockito.mock(DeclaredType::class.java),
+                    executableElement = method.element,
+                    dbVerifier = null).context.logger.suppressedWarnings
+                    , `is`(setOf(Warning.CURSOR_MISMATCH)))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun pojo_renamedColumn() {
+        pojoTest("""
+                String name;
+                String lName;
+                """, listOf("name", "lastName as lName")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(emptyList()))
+            assertThat(adapter?.mapping?.unusedFields, `is`(emptyList()))
+        }?.compilesWithoutError()?.withWarningCount(0)
+    }
+
+    @Test
+    fun pojo_exactMatch() {
+        pojoTest("""
+                String name;
+                String lastName;
+                """, listOf("name", "lastName")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(emptyList()))
+            assertThat(adapter?.mapping?.unusedFields, `is`(emptyList()))
+        }?.compilesWithoutError()?.withWarningCount(0)
+    }
+
+    @Test
+    fun pojo_exactMatchWithStar() {
+        pojoTest("""
+            String name;
+            String lastName;
+            int uid;
+            @ColumnInfo(name = "ageColumn")
+            int age;
+        """, listOf("*")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(emptyList()))
+            assertThat(adapter?.mapping?.unusedFields, `is`(emptyList()))
+        }?.compilesWithoutError()?.withWarningCount(0)
+    }
+
+    @Test
+    fun pojo_nonJavaName() {
+        pojoTest("""
+            @ColumnInfo(name = "MAX(ageColumn)")
+            int maxAge;
+            String name;
+            """, listOf("MAX(ageColumn)", "name")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(emptyList()))
+            assertThat(adapter?.mapping?.unusedFields, `is`(emptyList()))
+        }?.compilesWithoutError()?.withWarningCount(0)
+    }
+
+    @Test
+    fun pojo_noMatchingFields() {
+        pojoTest("""
+                String nameX;
+                String lastNameX;
+                """, listOf("name", "lastName")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(listOf("name", "lastName")))
+            assertThat(adapter?.mapping?.unusedFields, `is`(adapter?.pojo?.fields))
+        }?.failsToCompile()
+                ?.withErrorContaining(CANNOT_FIND_QUERY_RESULT_ADAPTER)
+                ?.and()
+                ?.withWarningContaining(
+                        ProcessorErrors.cursorPojoMismatch(
+                                pojoTypeName = POJO,
+                                unusedColumns = listOf("name", "lastName"),
+                                unusedFields = listOf(createField("nameX"),
+                                        createField("lastNameX")),
+                                allColumns = listOf("name", "lastName"),
+                                allFields = listOf(createField("nameX"), createField("lastNameX"))
+                        )
+                )
+    }
+
+    @Test
+    fun pojo_badQuery() {
+        // do not report mismatch if query is broken
+        pojoTest("""
+            @ColumnInfo(name = "MAX(ageColumn)")
+            int maxAge;
+            String name;
+            """, listOf("MAX(age)", "name")) { adapter, queryMethod, invocation ->
+        }?.failsToCompile()
+                ?.withErrorContaining("no such column: age")
+                ?.and()
+                ?.withErrorCount(1)
+                ?.withWarningCount(0)
+    }
+
+    @Test
+    fun pojo_tooManyColumns() {
+        pojoTest("""
+            String name;
+            String lastName;
+            """, listOf("uid", "name", "lastName")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(listOf("uid")))
+            assertThat(adapter?.mapping?.unusedFields, `is`(emptyList()))
+        }?.compilesWithoutError()?.withWarningContaining(
+                ProcessorErrors.cursorPojoMismatch(
+                        pojoTypeName = POJO,
+                        unusedColumns = listOf("uid"),
+                        unusedFields = emptyList(),
+                        allColumns = listOf("uid", "name", "lastName"),
+                        allFields = listOf(createField("name"), createField("lastName"))
+                ))
+    }
+
+    @Test
+    fun pojo_tooManyFields() {
+        pojoTest("""
+            String name;
+            String lastName;
+            """, listOf("lastName")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(emptyList()))
+            assertThat(adapter?.mapping?.unusedFields, `is`(
+                    adapter?.pojo?.fields?.filter { it.name == "name" }
+            ))
+        }?.compilesWithoutError()?.withWarningContaining(
+                ProcessorErrors.cursorPojoMismatch(
+                        pojoTypeName = POJO,
+                        unusedColumns = emptyList(),
+                        unusedFields = listOf(createField("name")),
+                        allColumns = listOf("lastName"),
+                        allFields = listOf(createField("name"), createField("lastName"))
+                ))
+    }
+
+    @Test
+    fun pojo_tooManyFieldsAndColumns() {
+        pojoTest("""
+            String name;
+            String lastName;
+            """, listOf("uid", "name")) { adapter, queryMethod, invocation ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(listOf("uid")))
+            assertThat(adapter?.mapping?.unusedFields, `is`(
+                    adapter?.pojo?.fields?.filter { it.name == "lastName" }
+            ))
+        }?.compilesWithoutError()?.withWarningContaining(
+                ProcessorErrors.cursorPojoMismatch(
+                        pojoTypeName = POJO,
+                        unusedColumns = listOf("uid"),
+                        unusedFields = listOf(createField("lastName")),
+                        allColumns = listOf("uid", "name"),
+                        allFields = listOf(createField("name"), createField("lastName"))
+                ))
+    }
+
+    fun pojoTest(pojoFields: String, queryColumns: List<String>,
+                 handler: (PojoRowAdapter?, QueryMethod, TestInvocation) -> Unit): CompileTester? {
+        val assertion = singleQueryMethod(
+                """
+                static class Pojo {
+                    $pojoFields
+                }
+                @Query("SELECT ${queryColumns.joinToString(", ")} from User LIMIT 1")
+                abstract MyClass.Pojo getNameAndLastNames();
+                """
+        ) { parsedQuery, invocation ->
+            val adapter = parsedQuery.queryResultBinder.adapter
+            if (enableVerification) {
+                if (adapter is SingleEntityQueryResultAdapter) {
+                    handler(adapter.rowAdapter as? PojoRowAdapter, parsedQuery, invocation)
+                } else {
+                    handler(null, parsedQuery, invocation)
+                }
+            } else {
+                assertThat(adapter, nullValue())
+            }
+        }
+        if (enableVerification) {
+            return assertion
+        } else {
+            assertion.failsToCompile().withErrorContaining(CANNOT_FIND_QUERY_RESULT_ADAPTER)
+            return null
+        }
+    }
+
+    fun singleQueryMethod(vararg input: String,
+                          handler: (QueryMethod, TestInvocation) -> Unit):
+            CompileTester {
+        return assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyClass",
+                        DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
+                ), COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(Query::class, Dao::class, ColumnInfo::class,
+                                Entity::class, PrimaryKey::class)
+                        .nextRunHandler { invocation ->
+                            val (owner, methods) = invocation.roundEnv
+                                    .getElementsAnnotatedWith(Dao::class.java)
+                                    .map {
+                                        Pair(it,
+                                                invocation.processingEnv.elementUtils
+                                                        .getAllMembers(MoreElements.asType(it))
+                                                        .filter {
+                                                            it.hasAnnotation(Query::class)
+                                                        }
+                                        )
+                                    }.filter { it.second.isNotEmpty() }.first()
+                            val verifier = if (enableVerification) {
+                                createVerifierFromEntities(invocation)
+                            } else {
+                                null
+                            }
+                            val parser = QueryMethodProcessor(
+                                    baseContext = invocation.context,
+                                    containing = MoreTypes.asDeclared(owner.asType()),
+                                    executableElement = MoreElements.asExecutable(methods.first()),
+                                    dbVerifier = verifier)
+                            val parsedQuery = parser.process()
+                            handler(parsedQuery, invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/ShortcutMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/ShortcutMethodProcessorTest.kt
new file mode 100644
index 0000000..a4d670c
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/ShortcutMethodProcessorTest.kt
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.ext.CommonTypeNames
+import android.arch.persistence.room.ext.typeName
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.ShortcutMethod
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import org.hamcrest.CoreMatchers
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+import kotlin.reflect.KClass
+
+/**
+ * Base test class for shortcut methods.
+ */
+abstract class ShortcutMethodProcessorTest<out T : ShortcutMethod>(
+        val annotation: KClass<out Annotation>) {
+    companion object {
+        const val DAO_PREFIX = """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                import java.util.*;
+                @Dao
+                abstract class MyClass {
+                """
+        const val DAO_SUFFIX = "}"
+        val USER_TYPE_NAME: TypeName = COMMON.USER_TYPE_NAME
+        val BOOK_TYPE_NAME : TypeName = ClassName.get("foo.bar", "Book")
+    }
+
+    @Test
+    fun noParams() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void foo();
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("foo"))
+            assertThat(shortcut.parameters.size, `is`(0))
+            assertThat(shortcut.returnCount, `is`(false))
+        }.failsToCompile().withErrorContaining(noParamsError())
+    }
+
+    abstract fun noParamsError(): String
+
+    @Test
+    fun single() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public int foo(User user);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("foo"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.type.typeName(), `is`(USER_TYPE_NAME))
+            assertThat(param.entityType?.typeName(), `is`(USER_TYPE_NAME))
+            assertThat(shortcut.entities.size, `is`(1))
+            assertThat(shortcut.entities["user"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.returnCount, `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun notAnEntity() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void foo(NotAnEntity notValid);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("foo"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.entityType, `is`(CoreMatchers.nullValue()))
+            assertThat(shortcut.entities.size, `is`(0))
+        }.failsToCompile().withErrorContaining(
+                ProcessorErrors.CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER
+        )
+    }
+
+    @Test
+    fun two() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void foo(User u1, User u2);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("foo"))
+
+            assertThat(shortcut.parameters.size, `is`(2))
+            shortcut.parameters.forEach {
+                assertThat(it.type.typeName(), `is`(USER_TYPE_NAME))
+                assertThat(it.entityType?.typeName(), `is`(USER_TYPE_NAME))
+            }
+            assertThat(shortcut.entities.size, `is`(2))
+            assertThat(shortcut.entities["u1"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.entities["u1"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.parameters.map { it.name },
+                    `is`(listOf("u1", "u2")))
+            assertThat(shortcut.returnCount, `is`(false))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun list() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public int users(List<User> users);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("users"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(
+                            ClassName.get("java.util", "List"), USER_TYPE_NAME) as TypeName))
+            assertThat(param.entityType?.typeName(), `is`(USER_TYPE_NAME))
+            assertThat(shortcut.entities.size, `is`(1))
+            assertThat(shortcut.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.returnCount, `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun array() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void users(User[] users);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("users"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ArrayTypeName.of(COMMON.USER_TYPE_NAME) as TypeName))
+            assertThat(shortcut.entities.size, `is`(1))
+            assertThat(shortcut.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.returnCount, `is`(false))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun set() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void modifyUsers(Set<User> users);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("modifyUsers"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(ClassName.get("java.util", "Set")
+                            , COMMON.USER_TYPE_NAME) as TypeName))
+            assertThat(shortcut.entities.size, `is`(1))
+            assertThat(shortcut.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.returnCount, `is`(false))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun iterable() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void modifyUsers(Iterable<User> users);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("modifyUsers"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(ClassName.get("java.lang", "Iterable")
+                            , COMMON.USER_TYPE_NAME) as TypeName))
+            assertThat(shortcut.entities.size, `is`(1))
+            assertThat(shortcut.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.returnCount, `is`(false))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun customCollection() {
+        singleShortcutMethod(
+                """
+                static class MyList<Irrelevant, Item> extends ArrayList<Item> {}
+                @${annotation.java.canonicalName}
+                abstract public void modifyUsers(MyList<String, User> users);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.name, `is`("modifyUsers"))
+            assertThat(shortcut.parameters.size, `is`(1))
+            val param = shortcut.parameters.first()
+            assertThat(param.type.typeName(), `is`(
+                    ParameterizedTypeName.get(ClassName.get("foo.bar", "MyClass.MyList")
+                            , CommonTypeNames.STRING
+                            , COMMON.USER_TYPE_NAME) as TypeName))
+            assertThat(shortcut.entities.size, `is`(1))
+            assertThat(shortcut.entities["users"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.returnCount, `is`(false))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun differentTypes() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public void foo(User u1, Book b1);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.parameters.size, `is`(2))
+            assertThat(shortcut.parameters[0].type.typeName().toString(),
+                    `is`("foo.bar.User"))
+            assertThat(shortcut.parameters[1].type.typeName().toString(),
+                    `is`("foo.bar.Book"))
+            assertThat(shortcut.parameters.map { it.name }, `is`(listOf("u1", "b1")))
+            assertThat(shortcut.returnCount, `is`(false))
+            assertThat(shortcut.entities.size, `is`(2))
+            assertThat(shortcut.entities["u1"]?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.entities["b1"]?.typeName, `is`(BOOK_TYPE_NAME))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun invalidReturnType() {
+        singleShortcutMethod(
+                """
+                @${annotation.java.canonicalName}
+                abstract public long foo(User user);
+                """) { shortcut, invocation ->
+        }.failsToCompile().withErrorContaining(invalidReturnTypeError())
+    }
+
+    abstract fun invalidReturnTypeError(): String
+
+    abstract fun process(baseContext: Context, containing: DeclaredType,
+                         executableElement: ExecutableElement): T
+
+    fun singleShortcutMethod(vararg input: String,
+                             handler: (T, TestInvocation) -> Unit):
+            CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyClass",
+                        DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
+                ), COMMON.USER, COMMON.BOOK, COMMON.NOT_AN_ENTITY))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(annotation, Dao::class)
+                        .nextRunHandler { invocation ->
+                            val (owner, methods) = invocation.roundEnv
+                                    .getElementsAnnotatedWith(Dao::class.java)
+                                    .map {
+                                        Pair(it,
+                                                invocation.processingEnv.elementUtils
+                                                        .getAllMembers(MoreElements.asType(it))
+                                                        .filter {
+                                                            MoreElements.isAnnotationPresent(it,
+                                                                    annotation.java)
+                                                        }
+                                        )
+                                    }.filter { it.second.isNotEmpty() }.first()
+                            val processed = process(
+                                    baseContext = invocation.context,
+                                    containing = MoreTypes.asDeclared(owner.asType()),
+                                    executableElement = MoreElements.asExecutable(methods.first()))
+                            handler(processed, invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/UpdateMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/UpdateMethodProcessorTest.kt
new file mode 100644
index 0000000..c8a88ae
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/UpdateMethodProcessorTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room.processor
+
+import android.arch.persistence.room.OnConflictStrategy
+import android.arch.persistence.room.Update
+import android.arch.persistence.room.processor.ProcessorErrors
+        .UPDATE_METHODS_MUST_RETURN_VOID_OR_INT
+import android.arch.persistence.room.processor.ProcessorErrors.UPDATE_MISSING_PARAMS
+import android.arch.persistence.room.vo.UpdateMethod
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.type.DeclaredType
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+@RunWith(JUnit4::class)
+class UpdateMethodProcessorTest : ShortcutMethodProcessorTest<UpdateMethod>(Update::class) {
+    override fun invalidReturnTypeError(): String = UPDATE_METHODS_MUST_RETURN_VOID_OR_INT
+
+    override fun noParamsError(): String = UPDATE_MISSING_PARAMS
+
+    override fun process(baseContext: Context, containing: DeclaredType,
+                         executableElement: ExecutableElement): UpdateMethod {
+        return UpdateMethodProcessor(baseContext, containing, executableElement).process()
+    }
+
+    @Test
+    fun goodConflict() {
+        singleShortcutMethod(
+                """
+                @Update(onConflict = OnConflictStrategy.REPLACE)
+                abstract public void foo(User user);
+                """) { shortcut, invocation ->
+            assertThat(shortcut.onConflictStrategy, `is`(OnConflictStrategy.REPLACE))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun badConflict() {
+        singleShortcutMethod(
+                """
+                @Update(onConflict = -1)
+                abstract public void foo(User user);
+                """) { shortcut, invocation ->
+        }.failsToCompile().withErrorContaining(ProcessorErrors.INVALID_ON_CONFLICT_VALUE)
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/BasicColumnTypeAdaptersTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/BasicColumnTypeAdaptersTest.kt
new file mode 100644
index 0000000..024099b
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver
+
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.testing.TestInvocation
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import simpleRun
+import testCodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+@RunWith(Parameterized::class)
+class BasicColumnTypeAdaptersTest(val input: Input, val bindCode: String,
+                                  val cursorCode: String) {
+    val scope = testCodeGenScope()
+
+    companion object {
+        val SQLITE_STMT: TypeName = ClassName.get("android.database.sqlite", "SQLiteStatement")
+        val CURSOR: TypeName = ClassName.get("android.database", "Cursor")
+
+        @Parameterized.Parameters(name = "kind:{0},bind:_{1},cursor:_{2}")
+        @JvmStatic
+        fun params(): List<Array<Any>> {
+            return listOf(
+                    arrayOf(Input(TypeKind.INT),
+                            "st.bindLong(6, inp);",
+                            "out = crs.getInt(9);"),
+                    arrayOf(Input(TypeKind.BYTE),
+                            "st.bindLong(6, inp);",
+                            "out = (byte) crs.getShort(9);"),
+                    arrayOf(Input(TypeKind.SHORT),
+                            "st.bindLong(6, inp);",
+                            "out = crs.getShort(9);"),
+                    arrayOf(Input(TypeKind.LONG),
+                            "st.bindLong(6, inp);",
+                            "out = crs.getLong(9);"),
+                    arrayOf(Input(TypeKind.CHAR),
+                            "st.bindLong(6, inp);",
+                            "out = (char) crs.getInt(9);"),
+                    arrayOf(Input(TypeKind.FLOAT),
+                            "st.bindDouble(6, inp);",
+                            "out = crs.getFloat(9);"),
+                    arrayOf(Input(TypeKind.DOUBLE),
+                            "st.bindDouble(6, inp);",
+                            "out = crs.getDouble(9);"),
+                    arrayOf(Input(TypeKind.DECLARED, "java.lang.String"),
+                            """
+                            if (inp == null) {
+                              st.bindNull(6);
+                            } else {
+                              st.bindString(6, inp);
+                            }
+                            """.trimIndent(),
+                            "out = crs.getString(9);")
+            )
+        }
+    }
+
+    @Test
+    fun bind() {
+        simpleRun { invocation ->
+            val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
+                    .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv), null)!!
+            adapter.bindToStmt("st", "6", "inp", scope)
+            assertThat(scope.generate().trim(), `is`(bindCode))
+            generateCode(invocation, false)
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun boxedBind() {
+        if (!input.typeKind.isPrimitive) {
+            return // no-op for those
+        }
+        simpleRun { invocation ->
+            val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
+                    .findColumnTypeAdapter(
+                            input.getBoxedTypeMirror(invocation.processingEnv), null)!!
+            adapter.bindToStmt("st", "6", "inp", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    if (inp == null) {
+                      st.bindNull(6);
+                    } else {
+                      $bindCode
+                    }
+                    """.trimIndent()
+            ))
+            generateCode(invocation, true)
+        }.compilesWithoutError()
+    }
+
+    private fun generateCode(invocation: TestInvocation, boxed: Boolean) {
+        val typeMirror = if (boxed) input.getBoxedTypeMirror(invocation.processingEnv)
+        else input.getTypeMirror(invocation.processingEnv)
+        val spec = TypeSpec.classBuilder("OutClass")
+                .addField(FieldSpec.builder(SQLITE_STMT, "st").build())
+                .addField(FieldSpec.builder(CURSOR, "crs").build())
+                .addField(FieldSpec.builder(TypeName.get(typeMirror), "out").build())
+                .addField(FieldSpec.builder(TypeName.get(typeMirror), "inp").build())
+                .addMethod(
+                        MethodSpec.methodBuilder("foo")
+                                .addCode(scope.builder().build())
+                                .build()
+                )
+                .build()
+        JavaFile.builder("foo.bar", spec).build().writeTo(invocation.processingEnv.filer)
+    }
+
+    @Test
+    fun read() {
+        simpleRun { invocation ->
+            val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
+                    .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv), null)!!
+            adapter.readFromCursor("out", "crs", "9", scope)
+            assertThat(scope.generate().trim(), `is`(cursorCode))
+            generateCode(invocation, false)
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun readBoxed() {
+        if (!input.typeKind.isPrimitive) {
+            return // no-op for those
+        }
+        simpleRun { invocation ->
+            val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
+                    .findColumnTypeAdapter(
+                            input.getBoxedTypeMirror(invocation.processingEnv), null)!!
+            adapter.readFromCursor("out", "crs", "9", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    if (crs.isNull(9)) {
+                      out = null;
+                    } else {
+                      $cursorCode
+                    }
+                    """.trimIndent()
+            ))
+            generateCode(invocation, true)
+        }.compilesWithoutError()
+    }
+
+    data class Input(val typeKind: TypeKind, val qName: String? = null) {
+        fun getTypeMirror(processingEnv: ProcessingEnvironment): TypeMirror {
+            return if (typeKind.isPrimitive) {
+                processingEnv.typeUtils.getPrimitiveType(typeKind)
+            } else {
+                processingEnv.elementUtils.getTypeElement(qName).asType()
+            }
+        }
+
+        fun getBoxedTypeMirror(processingEnv: ProcessingEnvironment): TypeMirror {
+            return if (typeKind.isPrimitive) {
+                processingEnv.typeUtils
+                        .boxedClass(getTypeMirror(processingEnv) as PrimitiveType)
+                        .asType()
+            } else {
+                getTypeMirror(processingEnv)
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/CustomTypeConverterResolutionTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/CustomTypeConverterResolutionTest.kt
new file mode 100644
index 0000000..1790788
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/CustomTypeConverterResolutionTest.kt
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("HasPlatformType")
+
+package android.arch.persistence.room.solver
+
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Database
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.PrimaryKey
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.RoomProcessor
+import android.arch.persistence.room.TypeConverter
+import android.arch.persistence.room.TypeConverters
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.ext.S
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_BIND_QUERY_PARAMETER_INTO_STMT
+import android.arch.persistence.room.testing.TestInvocation
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.lang.model.element.Modifier
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class CustomTypeConverterResolutionTest {
+    fun TypeSpec.toJFO() : JavaFileObject {
+        return JavaFileObjects.forSourceString("foo.bar.${this.name}",
+                "package foo.bar;\n" + toString())
+    }
+
+    companion object {
+        val ENTITY = ClassName.get("foo.bar", "MyEntity")
+        val DB = ClassName.get("foo.bar", "MyDb")
+        val DAO = ClassName.get("foo.bar", "MyDao")
+
+        val CUSTOM_TYPE = ClassName.get("foo.bar", "CustomType")
+        val CUSTOM_TYPE_JFO = JavaFileObjects.forSourceLines(CUSTOM_TYPE.toString(),
+                """
+                package ${CUSTOM_TYPE.packageName()};
+                public class ${CUSTOM_TYPE.simpleName()} {
+                    public int value;
+                }
+                """)
+        val CUSTOM_TYPE_CONVERTER = ClassName.get("foo.bar", "MyConverter")
+        val CUSTOM_TYPE_CONVERTER_JFO = JavaFileObjects.forSourceLines(
+                CUSTOM_TYPE_CONVERTER.toString(),
+                """
+                package ${CUSTOM_TYPE_CONVERTER.packageName()};
+                public class ${CUSTOM_TYPE_CONVERTER.simpleName()} {
+                    @${TypeConverter::class.java.canonicalName}
+                    public static $CUSTOM_TYPE toCustom(int value) {
+                        return null;
+                    }
+                    @${TypeConverter::class.java.canonicalName}
+                    public static int fromCustom($CUSTOM_TYPE input) {
+                        return 0;
+                    }
+                }
+                """)
+    }
+
+    @Test
+    fun useFromDatabase_forEntity() {
+        val entity = createEntity(hasCustomField = true)
+        val database = createDatabase(hasConverters = true, hasDao = true)
+        val dao = createDao(hasQueryReturningEntity = true, hasQueryWithCustomParam = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromDatabase_forQueryParameter() {
+        val entity = createEntity()
+        val database = createDatabase(hasConverters = true, hasDao = true)
+        val dao = createDao(hasQueryWithCustomParam = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromDatabase_forReturnValue() {
+        val entity = createEntity(hasCustomField = true)
+        val database = createDatabase(hasConverters = true, hasDao = true)
+        val dao = createDao(hasQueryReturningEntity = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromDao_forQueryParameter() {
+        val entity = createEntity()
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasConverters = true, hasQueryReturningEntity = true,
+                hasQueryWithCustomParam = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromEntity_forReturnValue() {
+        val entity = createEntity(hasCustomField = true, hasConverters = true)
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasQueryReturningEntity = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromEntityField_forReturnValue() {
+        val entity = createEntity(hasCustomField = true, hasConverterOnField = true)
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasQueryReturningEntity = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromEntity_forQueryParameter() {
+        val entity = createEntity(hasCustomField = true, hasConverters = true)
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasQueryWithCustomParam = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.failsToCompile().withErrorContaining(CANNOT_BIND_QUERY_PARAMETER_INTO_STMT)
+    }
+
+    @Test
+    fun useFromEntityField_forQueryParameter() {
+        val entity = createEntity(hasCustomField = true, hasConverterOnField = true)
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasQueryWithCustomParam = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.failsToCompile().withErrorContaining(CANNOT_BIND_QUERY_PARAMETER_INTO_STMT)
+    }
+
+    @Test
+    fun useFromQueryMethod_forQueryParameter() {
+        val entity = createEntity()
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasQueryWithCustomParam = true, hasMethodConverters = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun useFromQueryParameter_forQueryParameter() {
+        val entity = createEntity()
+        val database = createDatabase(hasDao = true)
+        val dao = createDao(hasQueryWithCustomParam = true, hasParameterConverters = true)
+        run(entity.toJFO(), dao.toJFO(), database.toJFO()){
+
+        }.compilesWithoutError()
+    }
+
+    fun run(vararg jfos : JavaFileObject, f: (TestInvocation) -> Unit): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(jfos.toList() + CUSTOM_TYPE_JFO + CUSTOM_TYPE_CONVERTER_JFO)
+                .processedWith(RoomProcessor())
+    }
+
+    private fun createEntity(hasCustomField : Boolean = false,
+                             hasConverters : Boolean = false,
+                             hasConverterOnField : Boolean = false) : TypeSpec {
+        if (hasConverterOnField && hasConverters) {
+            throw IllegalArgumentException("cannot have both converters")
+        }
+        return TypeSpec.classBuilder(ENTITY).apply {
+            addAnnotation(Entity::class.java)
+            addModifiers(Modifier.PUBLIC)
+            if (hasCustomField) {
+                addField(FieldSpec.builder(CUSTOM_TYPE, "myCustomField", Modifier.PUBLIC).apply {
+                    if (hasConverterOnField) {
+                        addAnnotation(createConvertersAnnotation())
+                    }
+                }.build())
+            }
+            if (hasConverters) {
+                addAnnotation(createConvertersAnnotation())
+            }
+            addField(FieldSpec.builder(TypeName.INT, "id", Modifier.PUBLIC).apply {
+                addAnnotation(PrimaryKey::class.java)
+            }.build())
+        }.build()
+    }
+
+    private fun createDatabase(hasConverters : Boolean = false,
+                               hasDao : Boolean = false) : TypeSpec {
+        return TypeSpec.classBuilder(DB).apply {
+            addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
+            superclass(RoomTypeNames.ROOM_DB)
+            if (hasConverters) {
+                addAnnotation(createConvertersAnnotation())
+            }
+            addField(FieldSpec.builder(TypeName.INT, "id", Modifier.PUBLIC).apply {
+                addAnnotation(PrimaryKey::class.java)
+            }.build())
+            if (hasDao) {
+                addMethod(MethodSpec.methodBuilder("getDao").apply {
+                    addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+                    returns(DAO)
+                }.build())
+            }
+            addAnnotation(
+                    AnnotationSpec.builder(Database::class.java).apply {
+                        addMember("entities", "{$T.class}", ENTITY)
+                        addMember("version", "42")
+                    }.build()
+            )
+        }.build()
+    }
+
+    private fun createDao(hasConverters : Boolean = false,
+                          hasQueryReturningEntity : Boolean = false,
+                          hasQueryWithCustomParam : Boolean = false,
+                          hasMethodConverters : Boolean = false,
+                          hasParameterConverters : Boolean = false) : TypeSpec {
+        val annotationCount = listOf(hasMethodConverters, hasConverters, hasParameterConverters)
+                .map { if (it) 1 else 0 }.sum()
+        if (annotationCount > 1) {
+            throw IllegalArgumentException("cannot set both of these")
+        }
+        if (hasParameterConverters && !hasQueryWithCustomParam) {
+            throw IllegalArgumentException("inconsistent")
+        }
+        return TypeSpec.classBuilder(DAO).apply {
+            addAnnotation(Dao::class.java)
+            addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
+            if (hasConverters) {
+                addAnnotation(createConvertersAnnotation())
+            }
+            if (hasQueryReturningEntity) {
+                addMethod(MethodSpec.methodBuilder("loadAll").apply {
+                    addAnnotation(AnnotationSpec.builder(Query::class.java).apply {
+                        addMember("value", S, "SELECT * FROM ${ENTITY.simpleName()} LIMIT 1")
+                    }.build())
+                    addModifiers(Modifier.ABSTRACT)
+                    returns(ENTITY)
+                }.build())
+            }
+            if (hasQueryWithCustomParam) {
+                addMethod(MethodSpec.methodBuilder("queryWithCustom").apply {
+                    addAnnotation(AnnotationSpec.builder(Query::class.java).apply {
+                        addMember("value", S, "SELECT COUNT(*) FROM ${ENTITY.simpleName()} where" +
+                                " id IN(:customs)")
+                    }.build())
+                    if (hasMethodConverters) {
+                        addAnnotation(createConvertersAnnotation())
+                    }
+                    addParameter(ParameterSpec.builder(CUSTOM_TYPE, "customs").apply {
+                        if (hasParameterConverters) {
+                            addAnnotation(createConvertersAnnotation())
+                        }
+                    }.build())
+                    addModifiers(Modifier.ABSTRACT)
+                    returns(TypeName.INT)
+                }.build())
+            }
+        }.build()
+    }
+
+    private fun createConvertersAnnotation(): AnnotationSpec {
+        return AnnotationSpec.builder(TypeConverters::class.java)
+                .addMember("value", "$T.class", CUSTOM_TYPE_CONVERTER).build()
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/TypeAdapterStoreTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/TypeAdapterStoreTest.kt
new file mode 100644
index 0000000..204adc6
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/TypeAdapterStoreTest.kt
@@ -0,0 +1,395 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver
+
+import COMMON
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.ext.L
+import android.arch.persistence.room.ext.LifecyclesTypeNames
+import android.arch.persistence.room.ext.PagingTypeNames
+import android.arch.persistence.room.ext.ReactiveStreamsTypeNames
+import android.arch.persistence.room.ext.RoomTypeNames.STRING_UTIL
+import android.arch.persistence.room.ext.RxJava2TypeNames
+import android.arch.persistence.room.ext.T
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.ProcessorErrors
+import android.arch.persistence.room.solver.binderprovider.CountedDataSourceQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.FlowableQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.LiveDataQueryResultBinderProvider
+import android.arch.persistence.room.solver.binderprovider.LiveLazyListQueryResultBinderProvider
+import android.arch.persistence.room.solver.types.CompositeAdapter
+import android.arch.persistence.room.solver.types.TypeConverter
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.util.paging.CountedDataSource
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import simpleRun
+import testCodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeKind
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+@RunWith(JUnit4::class)
+class TypeAdapterStoreTest {
+    companion object {
+        fun tmp(index: Int) = CodeGenScope._tmpVar(index)
+    }
+
+    @Test
+    fun testDirect() {
+        singleRun { invocation ->
+            val store = TypeAdapterStore.create(Context(invocation.processingEnv))
+            val primitiveType = invocation.processingEnv.typeUtils.getPrimitiveType(TypeKind.INT)
+            val adapter = store.findColumnTypeAdapter(primitiveType, null)
+            assertThat(adapter, notNullValue())
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testVia1TypeAdapter() {
+        singleRun { invocation ->
+            val store = TypeAdapterStore.create(Context(invocation.processingEnv))
+            val booleanType = invocation.processingEnv.typeUtils
+                    .getPrimitiveType(TypeKind.BOOLEAN)
+            val adapter = store.findColumnTypeAdapter(booleanType, null)
+            assertThat(adapter, notNullValue())
+            assertThat(adapter, instanceOf(CompositeAdapter::class.java))
+            val bindScope = testCodeGenScope()
+            adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
+            assertThat(bindScope.generate().trim(), `is`("""
+                    final int ${tmp(0)};
+                    ${tmp(0)} = fooVar ? 1 : 0;
+                    stmt.bindLong(41, ${tmp(0)});
+                    """.trimIndent()))
+
+            val cursorScope = testCodeGenScope()
+            adapter.readFromCursor("res", "curs", "7", cursorScope)
+            assertThat(cursorScope.generate().trim(), `is`("""
+                    final int ${tmp(0)};
+                    ${tmp(0)} = curs.getInt(7);
+                    res = ${tmp(0)} != 0;
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testVia2TypeAdapters() {
+        singleRun { invocation ->
+            val store = TypeAdapterStore.create(Context(invocation.processingEnv),
+                    pointTypeConverters(invocation.processingEnv))
+            val pointType = invocation.processingEnv.elementUtils
+                    .getTypeElement("foo.bar.Point").asType()
+            val adapter = store.findColumnTypeAdapter(pointType, null)
+            assertThat(adapter, notNullValue())
+            assertThat(adapter, instanceOf(CompositeAdapter::class.java))
+
+            val bindScope = testCodeGenScope()
+            adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
+            assertThat(bindScope.generate().trim(), `is`("""
+                    final int ${tmp(0)};
+                    final boolean ${tmp(1)};
+                    ${tmp(1)} = foo.bar.Point.toBoolean(fooVar);
+                    ${tmp(0)} = ${tmp(1)} ? 1 : 0;
+                    stmt.bindLong(41, ${tmp(0)});
+                    """.trimIndent()))
+
+            val cursorScope = testCodeGenScope()
+            adapter.readFromCursor("res", "curs", "11", cursorScope).toString()
+            assertThat(cursorScope.generate().trim(), `is`("""
+                    final int ${tmp(0)};
+                    ${tmp(0)} = curs.getInt(11);
+                    final boolean ${tmp(1)};
+                    ${tmp(1)} = ${tmp(0)} != 0;
+                    res = foo.bar.Point.fromBoolean(${tmp(1)});
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testDate() {
+        singleRun { (processingEnv) ->
+            val store = TypeAdapterStore.create(Context(processingEnv),
+                    dateTypeConverters(processingEnv))
+            val tDate = processingEnv.elementUtils.getTypeElement("java.util.Date").asType()
+            val adapter = store.findCursorValueReader(tDate, SQLTypeAffinity.INTEGER)
+            assertThat(adapter, notNullValue())
+            assertThat(adapter?.typeMirror(), `is`(tDate))
+            val bindScope = testCodeGenScope()
+            adapter!!.readFromCursor("outDate", "curs", "0", bindScope)
+            assertThat(bindScope.generate().trim(), `is`("""
+                final java.lang.Long _tmp;
+                if (curs.isNull(0)) {
+                  _tmp = null;
+                } else {
+                  _tmp = curs.getLong(0);
+                }
+                // convert Long to Date;
+            """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testIntList() {
+        singleRun { invocation ->
+            val binders = createIntListToStringBinders(invocation)
+            val store = TypeAdapterStore.create(Context(invocation.processingEnv), binders[0],
+                    binders[1])
+
+            val adapter = store.findColumnTypeAdapter(binders[0].from, null)
+            assertThat(adapter, notNullValue())
+
+            val bindScope = testCodeGenScope()
+            adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
+            assertThat(bindScope.generate().trim(), `is`("""
+                final java.lang.String ${tmp(0)};
+                ${tmp(0)} = android.arch.persistence.room.util.StringUtil.joinIntoString(fooVar);
+                if (${tmp(0)} == null) {
+                  stmt.bindNull(41);
+                } else {
+                  stmt.bindString(41, ${tmp(0)});
+                }
+                """.trimIndent()))
+
+            val converter = store.findTypeConverter(binders[0].from,
+                    invocation.context.COMMON_TYPES.STRING)
+            assertThat(converter, notNullValue())
+            assertThat(store.reverse(converter!!), `is`(binders[1]))
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testOneWayConversion() {
+        singleRun { invocation ->
+            val binders = createIntListToStringBinders(invocation)
+            val store = TypeAdapterStore.create(Context(invocation.processingEnv), binders[0])
+            val adapter = store.findColumnTypeAdapter(binders[0].from, null)
+            assertThat(adapter, nullValue())
+
+            val stmtBinder = store.findStatementValueBinder(binders[0].from, null)
+            assertThat(stmtBinder, notNullValue())
+
+            val converter = store.findTypeConverter(binders[0].from,
+                    invocation.context.COMMON_TYPES.STRING)
+            assertThat(converter, notNullValue())
+            assertThat(store.reverse(converter!!), nullValue())
+        }
+    }
+
+    @Test
+    fun testMissingRxRoom() {
+        simpleRun(jfos = *arrayOf(COMMON.PUBLISHER, COMMON.FLOWABLE)) { invocation ->
+            val publisherElement = invocation.processingEnv.elementUtils
+                    .getTypeElement(ReactiveStreamsTypeNames.PUBLISHER.toString())
+            assertThat(publisherElement, notNullValue())
+            assertThat(FlowableQueryResultBinderProvider(invocation.context).matches(
+                    MoreTypes.asDeclared(publisherElement.asType())), `is`(true))
+        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+    }
+
+    @Test
+    fun testFindPublisher() {
+        simpleRun(jfos = *arrayOf(COMMON.PUBLISHER, COMMON.FLOWABLE, COMMON.RX2_ROOM)) {
+            invocation ->
+            val publisher = invocation.processingEnv.elementUtils
+                    .getTypeElement(ReactiveStreamsTypeNames.PUBLISHER.toString())
+            assertThat(publisher, notNullValue())
+            assertThat(FlowableQueryResultBinderProvider(invocation.context).matches(
+                    MoreTypes.asDeclared(publisher.asType())), `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testFindFlowable() {
+        simpleRun(jfos = *arrayOf(COMMON.PUBLISHER, COMMON.FLOWABLE, COMMON.RX2_ROOM)) {
+            invocation ->
+            val flowable = invocation.processingEnv.elementUtils
+                    .getTypeElement(RxJava2TypeNames.FLOWABLE.toString())
+            assertThat(flowable, notNullValue())
+            assertThat(FlowableQueryResultBinderProvider(invocation.context).matches(
+                    MoreTypes.asDeclared(flowable.asType())), `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testFindLiveData() {
+        simpleRun(jfos = *arrayOf(COMMON.COMPUTABLE_LIVE_DATA, COMMON.LIVE_DATA)) {
+            invocation ->
+            val liveData = invocation.processingEnv.elementUtils
+                    .getTypeElement(LifecyclesTypeNames.LIVE_DATA.toString())
+            assertThat(liveData, notNullValue())
+            assertThat(LiveDataQueryResultBinderProvider(invocation.context).matches(
+                    MoreTypes.asDeclared(liveData.asType())), `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun findCountedDataSource() {
+        simpleRun {
+            invocation ->
+            val countedDataSource = invocation.processingEnv.elementUtils
+                    .getTypeElement(CountedDataSource::class.java.canonicalName)
+            assertThat(countedDataSource, notNullValue())
+            assertThat(CountedDataSourceQueryResultBinderProvider(invocation.context).matches(
+                    MoreTypes.asDeclared(countedDataSource.asType())), `is`(true))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun findLazyListProvider() {
+        simpleRun(jfos = COMMON.LIVE_LAZY_LIST_PROVIDER) {
+            invocation ->
+            val lazyListProvider = invocation.processingEnv.elementUtils
+                    .getTypeElement(PagingTypeNames.LIVE_LAZY_LIST_PROVIDER.toString())
+            assertThat(lazyListProvider, notNullValue())
+            assertThat(LiveLazyListQueryResultBinderProvider(invocation.context).matches(
+                    MoreTypes.asDeclared(lazyListProvider.asType())), `is`(true))
+        }.compilesWithoutError()
+    }
+
+    private fun createIntListToStringBinders(invocation: TestInvocation): List<TypeConverter> {
+        val intType = invocation.processingEnv.elementUtils
+                .getTypeElement(Integer::class.java.canonicalName)
+                .asType()
+        val listType = invocation.processingEnv.elementUtils
+                .getTypeElement(java.util.List::class.java.canonicalName)
+        val listOfInts = invocation.processingEnv.typeUtils.getDeclaredType(listType, intType)
+
+        val intListConverter = object : TypeConverter(listOfInts,
+                invocation.context.COMMON_TYPES.STRING) {
+            override fun convert(inputVarName: String, outputVarName: String,
+                                 scope: CodeGenScope) {
+                scope.builder().apply {
+                    addStatement("$L = $T.joinIntoString($L)", outputVarName, STRING_UTIL,
+                            inputVarName)
+                }
+            }
+        }
+
+        val stringToIntListConverter = object : TypeConverter(
+                invocation.context.COMMON_TYPES.STRING, listOfInts) {
+            override fun convert(inputVarName: String, outputVarName: String,
+                                 scope: CodeGenScope) {
+                scope.builder().apply {
+                    addStatement("$L = $T.splitToIntList($L)", outputVarName, STRING_UTIL,
+                            inputVarName)
+                }
+            }
+        }
+        return listOf(intListConverter, stringToIntListConverter)
+    }
+
+    fun singleRun(handler: (TestInvocation) -> Unit): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.DummyClass",
+                        """
+                        package foo.bar;
+                        import android.arch.persistence.room.*;
+                        @Entity
+                        public class DummyClass {}
+                        """
+                ), JavaFileObjects.forSourceString("foo.bar.Point",
+                        """
+                        package foo.bar;
+                        import android.arch.persistence.room.*;
+                        @Entity
+                        public class Point {
+                            public int x, y;
+                            public Point(int x, int y) {
+                                this.x = x;
+                                this.y = y;
+                            }
+                            public static Point fromBoolean(boolean val) {
+                                return val ? new Point(1, 1) : new Point(0, 0);
+                            }
+                            public static boolean toBoolean(Point point) {
+                                return point.x > 0;
+                            }
+                        }
+                        """
+                )))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(Entity::class)
+                        .nextRunHandler { invocation ->
+                            handler(invocation)
+                            true
+                        }
+                        .build())
+    }
+
+    fun pointTypeConverters(env: ProcessingEnvironment): List<TypeConverter> {
+        val tPoint = env.elementUtils.getTypeElement("foo.bar.Point").asType()
+        val tBoolean = env.typeUtils.getPrimitiveType(TypeKind.BOOLEAN)
+        return listOf(
+                object : TypeConverter(tPoint, tBoolean) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().apply {
+                            addStatement("$L = $T.toBoolean($L)", outputVarName, from, inputVarName)
+                        }
+                    }
+
+                },
+                object : TypeConverter(tBoolean, tPoint) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().apply {
+                            addStatement("$L = $T.fromBoolean($L)", outputVarName, tPoint,
+                                    inputVarName)
+                        }
+                    }
+                }
+        )
+    }
+
+    fun dateTypeConverters(env: ProcessingEnvironment): List<TypeConverter> {
+        val tDate = env.elementUtils.getTypeElement("java.util.Date").asType()
+        val tLong = env.elementUtils.getTypeElement("java.lang.Long").asType()
+        return listOf(
+                object : TypeConverter(tDate, tLong) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().apply {
+                            addStatement("// convert Date to Long")
+                        }
+                    }
+
+                },
+                object : TypeConverter(tLong, tDate) {
+                    override fun convert(inputVarName: String, outputVarName: String,
+                                         scope: CodeGenScope) {
+                        scope.builder().apply {
+                            addStatement("// convert Long to Date")
+                        }
+                    }
+                }
+        )
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/query/QueryWriterTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/query/QueryWriterTest.kt
new file mode 100644
index 0000000..6401d96
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/solver/query/QueryWriterTest.kt
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.solver.query
+
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.ext.RoomTypeNames.ROOM_SQL_QUERY
+import android.arch.persistence.room.ext.RoomTypeNames.STRING_UTIL
+import android.arch.persistence.room.processor.QueryMethodProcessor
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.writer.QueryWriter
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourceSubjectFactory
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import testCodeGenScope
+
+@RunWith(JUnit4::class)
+class QueryWriterTest {
+    companion object {
+        const val DAO_PREFIX = """
+                package foo.bar;
+                import android.arch.persistence.room.*;
+                import java.util.*;
+                @Dao
+                abstract class MyClass {
+                """
+        const val DAO_SUFFIX = "}"
+        val QUERY = ROOM_SQL_QUERY.toString()
+    }
+
+    @Test
+    fun simpleNoArgQuery() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users")
+                abstract java.util.List<Integer> selectAllIds();
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`("""
+                    final java.lang.String _sql = "SELECT id FROM users";
+                    final $QUERY _stmt = $QUERY.acquire(_sql, 0);
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun simpleStringArgs() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE name LIKE :name")
+                abstract java.util.List<Integer> selectAllIds(String name);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    final java.lang.String _sql = "SELECT id FROM users WHERE name LIKE ?";
+                    final $QUERY _stmt = $QUERY.acquire(_sql, 1);
+                    int _argIndex = 1;
+                    if (name == null) {
+                      _stmt.bindNull(_argIndex);
+                    } else {
+                      _stmt.bindString(_argIndex, name);
+                    }
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun twoIntArgs() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE id IN(:id1,:id2)")
+                abstract java.util.List<Integer> selectAllIds(int id1, int id2);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    final java.lang.String _sql = "SELECT id FROM users WHERE id IN(?,?)";
+                    final $QUERY _stmt = $QUERY.acquire(_sql, 2);
+                    int _argIndex = 1;
+                    _stmt.bindLong(_argIndex, id1);
+                    _argIndex = 2;
+                    _stmt.bindLong(_argIndex, id2);
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun aLongAndIntVarArg() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
+                abstract java.util.List<Integer> selectAllIds(long time, int... ids);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    _stringBuilder.append("SELECT id FROM users WHERE id IN(");
+                    final int _inputSize = ids.length;
+                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    _stringBuilder.append(") AND age > ");
+                    _stringBuilder.append("?");
+                    final java.lang.String _sql = _stringBuilder.toString();
+                    final int _argCount = 1 + _inputSize;
+                    final $QUERY _stmt = $QUERY.acquire(_sql, _argCount);
+                    int _argIndex = 1;
+                    for (int _item : ids) {
+                      _stmt.bindLong(_argIndex, _item);
+                      _argIndex ++;
+                    }
+                    _argIndex = 1 + _inputSize;
+                    _stmt.bindLong(_argIndex, time);
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    val collectionOut = """
+                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    _stringBuilder.append("SELECT id FROM users WHERE id IN(");
+                    final int _inputSize = ids.size();
+                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    _stringBuilder.append(") AND age > ");
+                    _stringBuilder.append("?");
+                    final java.lang.String _sql = _stringBuilder.toString();
+                    final int _argCount = 1 + _inputSize;
+                    final $QUERY _stmt = $QUERY.acquire(_sql, _argCount);
+                    int _argIndex = 1;
+                    for (java.lang.Integer _item : ids) {
+                      if (_item == null) {
+                        _stmt.bindNull(_argIndex);
+                      } else {
+                        _stmt.bindLong(_argIndex, _item);
+                      }
+                      _argIndex ++;
+                    }
+                    _argIndex = 1 + _inputSize;
+                    _stmt.bindLong(_argIndex, time);
+                    """.trimIndent()
+
+    @Test
+    fun aLongAndIntegerList() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
+                abstract List<Integer> selectAllIds(long time, List<Integer> ids);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`(collectionOut))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun aLongAndIntegerSet() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
+                abstract List<Integer> selectAllIds(long time, Set<Integer> ids);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`(collectionOut))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testMultipleBindParamsWithSameName() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE age > :age OR bage > :age")
+                abstract List<Integer> selectAllIds(int age);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`("""
+                    final java.lang.String _sql = "SELECT id FROM users WHERE age > ? OR bage > ?";
+                    final $QUERY _stmt = $QUERY.acquire(_sql, 2);
+                    int _argIndex = 1;
+                    _stmt.bindLong(_argIndex, age);
+                    _argIndex = 2;
+                    _stmt.bindLong(_argIndex, age);
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testMultipleBindParamsWithSameNameWithVarArg() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE age > :age OR bage > :age OR fage IN(:ages)")
+                abstract List<Integer> selectAllIds(int age, int... ages);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`("""
+                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    _stringBuilder.append("SELECT id FROM users WHERE age > ");
+                    _stringBuilder.append("?");
+                    _stringBuilder.append(" OR bage > ");
+                    _stringBuilder.append("?");
+                    _stringBuilder.append(" OR fage IN(");
+                    final int _inputSize = ages.length;
+                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    _stringBuilder.append(")");
+                    final java.lang.String _sql = _stringBuilder.toString();
+                    final int _argCount = 2 + _inputSize;
+                    final $QUERY _stmt = $QUERY.acquire(_sql, _argCount);
+                    int _argIndex = 1;
+                    _stmt.bindLong(_argIndex, age);
+                    _argIndex = 2;
+                    _stmt.bindLong(_argIndex, age);
+                    _argIndex = 3;
+                    for (int _item : ages) {
+                      _stmt.bindLong(_argIndex, _item);
+                      _argIndex ++;
+                    }
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testMultipleBindParamsWithSameNameWithVarArgInTwoBindings() {
+        singleQueryMethod("""
+                @Query("SELECT id FROM users WHERE age IN (:ages) OR bage > :age OR fage IN(:ages)")
+                abstract List<Integer> selectAllIds(int age, int... ages);
+                """) { writer ->
+            val scope = testCodeGenScope()
+            writer.prepareReadAndBind("_sql", "_stmt", scope)
+            assertThat(scope.generate().trim(), `is`("""
+                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    _stringBuilder.append("SELECT id FROM users WHERE age IN (");
+                    final int _inputSize = ages.length;
+                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    _stringBuilder.append(") OR bage > ");
+                    _stringBuilder.append("?");
+                    _stringBuilder.append(" OR fage IN(");
+                    final int _inputSize_1 = ages.length;
+                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize_1);
+                    _stringBuilder.append(")");
+                    final java.lang.String _sql = _stringBuilder.toString();
+                    final int _argCount = 1 + _inputSize + _inputSize_1;
+                    final $QUERY _stmt = $QUERY.acquire(_sql, _argCount);
+                    int _argIndex = 1;
+                    for (int _item : ages) {
+                      _stmt.bindLong(_argIndex, _item);
+                      _argIndex ++;
+                    }
+                    _argIndex = 1 + _inputSize;
+                    _stmt.bindLong(_argIndex, age);
+                    _argIndex = 2 + _inputSize;
+                    for (int _item_1 : ages) {
+                      _stmt.bindLong(_argIndex, _item_1);
+                      _argIndex ++;
+                    }
+                    """.trimIndent()))
+        }.compilesWithoutError()
+    }
+
+    fun singleQueryMethod(vararg input: String,
+                          handler: (QueryWriter) -> Unit):
+            CompileTester {
+        return Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
+                .that(JavaFileObjects.forSourceString("foo.bar.MyClass",
+                        DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
+                ))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(Query::class, Dao::class)
+                        .nextRunHandler { invocation ->
+                            val (owner, methods) = invocation.roundEnv
+                                    .getElementsAnnotatedWith(Dao::class.java)
+                                    .map {
+                                        Pair(it,
+                                                invocation.processingEnv.elementUtils
+                                                        .getAllMembers(MoreElements.asType(it))
+                                                        .filter {
+                                                            MoreElements.isAnnotationPresent(it,
+                                                                    Query::class.java)
+                                                        }
+                                        )
+                                    }.filter { it.second.isNotEmpty() }.first()
+                            val parser = QueryMethodProcessor(
+                                    baseContext = invocation.context,
+                                    containing = MoreTypes.asDeclared(owner.asType()),
+                                    executableElement = MoreElements.asExecutable(methods.first()))
+                            val parsedQuery = parser.process()
+                            handler(QueryWriter(parsedQuery))
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/InProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/InProcessorTest.kt
new file mode 100644
index 0000000..31e1209
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/InProcessorTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.testing
+
+import android.arch.persistence.room.Query
+import com.google.common.truth.Truth
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourceSubjectFactory
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.util.concurrent.atomic.AtomicBoolean
+
+@RunWith(JUnit4::class)
+class InProcessorTest {
+    @Test
+    fun testInProcessorTestRuns() {
+        val didRun = AtomicBoolean(false)
+        Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
+                .that(JavaFileObjects.forSourceString("foo.bar.MyClass",
+                        """
+                        package foo.bar;
+                        abstract public class MyClass {
+                        @android.arch.persistence.room.Query("foo")
+                        abstract public void setFoo(String foo);
+                        }
+                        """))
+                .processedWith(TestProcessor.builder()
+                        .nextRunHandler { invocation ->
+                            didRun.set(true)
+                            assertThat(invocation.annotations.size, `is`(1))
+                            true
+                        }
+                        .forAnnotations(Query::class)
+                        .build())
+                .compilesWithoutError()
+        assertThat(didRun.get(), `is`(true))
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/TestInvocation.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/TestInvocation.kt
new file mode 100644
index 0000000..b97628e
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/TestInvocation.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.testing
+
+import android.arch.persistence.room.processor.Context
+import javax.annotation.processing.ProcessingEnvironment
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.TypeElement
+
+data class TestInvocation(val processingEnv: ProcessingEnvironment,
+                          val annotations: MutableSet<out TypeElement>,
+                          val roundEnv: RoundEnvironment) {
+    val context = Context(processingEnv)
+
+    fun typeElement(qName: String) : TypeElement {
+        return processingEnv.elementUtils.getTypeElement(qName)
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/TestProcessor.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/TestProcessor.kt
new file mode 100644
index 0000000..16c0048
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/TestProcessor.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.testing
+
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.annotation.processing.SupportedSourceVersion
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.TypeElement
+import kotlin.reflect.KClass
+
+@SupportedSourceVersion(SourceVersion.RELEASE_8)// test are compiled w/ J_8
+class TestProcessor(val handlers: List<(TestInvocation) -> Boolean>,
+                    val annotations: MutableSet<String>) : AbstractProcessor() {
+    var count = 0
+    override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment)
+            : Boolean {
+        return handlers.getOrNull(count++)?.invoke(
+                    TestInvocation(processingEnv, annotations, roundEnv)) ?: true
+    }
+
+    override fun getSupportedAnnotationTypes(): MutableSet<String> {
+        return annotations
+    }
+
+    class Builder {
+        private var handlers = arrayListOf<(TestInvocation) -> Boolean>()
+        private var annotations = mutableSetOf<String>()
+        fun nextRunHandler(f: (TestInvocation) -> Boolean): Builder {
+            handlers.add(f)
+            return this
+        }
+
+        fun forAnnotations(vararg klasses: KClass<*>): Builder {
+            annotations.addAll(klasses.map { it.java.canonicalName })
+            return this
+        }
+
+        fun build(): TestProcessor {
+            if (annotations.isEmpty()) {
+                throw IllegalStateException("must provide at least 1 annotation")
+            }
+            if (handlers.isEmpty()) {
+                throw IllegalStateException("must provide at least 1 handler")
+            }
+            return TestProcessor(handlers, annotations)
+        }
+    }
+
+    companion object {
+        fun builder(): Builder = Builder()
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt
new file mode 100644
index 0000000..cd30c21
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+import android.arch.persistence.room.ColumnInfo
+import android.arch.persistence.room.Embedded
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.PrimaryKey
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.Relation
+import android.arch.persistence.room.ext.LifecyclesTypeNames
+import android.arch.persistence.room.ext.PagingTypeNames
+import android.arch.persistence.room.ext.ReactiveStreamsTypeNames
+import android.arch.persistence.room.ext.RoomRxJava2TypeNames
+import android.arch.persistence.room.ext.RxJava2TypeNames
+import android.arch.persistence.room.processor.EntityProcessor
+import android.arch.persistence.room.solver.CodeGenScope
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.verifier.DatabaseVerifier
+import android.arch.persistence.room.writer.ClassWriter
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ClassName
+import org.mockito.Mockito
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import java.io.File
+import javax.lang.model.element.Element
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.tools.JavaFileObject
+
+object COMMON {
+    val USER by lazy {
+        loadJavaCode("common/input/User.java", "foo.bar.User")
+    }
+    val USER_TYPE_NAME by lazy {
+        ClassName.get("foo.bar", "User")
+    }
+    val BOOK by lazy {
+        loadJavaCode("common/input/Book.java", "foo.bar.Book")
+    }
+    val NOT_AN_ENTITY by lazy {
+        loadJavaCode("common/input/NotAnEntity.java", "foo.bar.NotAnEntity")
+    }
+
+    val NOT_AN_ENTITY_TYPE_NAME by lazy {
+        ClassName.get("foo.bar", "NotAnEntity")
+    }
+
+    val MULTI_PKEY_ENTITY by lazy {
+        loadJavaCode("common/input/MultiPKeyEntity.java", "MultiPKeyEntity")
+    }
+    val LIVE_DATA by lazy {
+        loadJavaCode("common/input/LiveData.java", LifecyclesTypeNames.LIVE_DATA.toString())
+    }
+    val COMPUTABLE_LIVE_DATA by lazy {
+        loadJavaCode("common/input/ComputableLiveData.java",
+                LifecyclesTypeNames.COMPUTABLE_LIVE_DATA.toString())
+    }
+    val PUBLISHER by lazy {
+        loadJavaCode("common/input/reactivestreams/Publisher.java",
+                ReactiveStreamsTypeNames.PUBLISHER.toString())
+    }
+    val FLOWABLE by lazy {
+        loadJavaCode("common/input/rxjava2/Flowable.java", RxJava2TypeNames.FLOWABLE.toString())
+    }
+
+    val RX2_ROOM by lazy {
+        loadJavaCode("common/input/Rx2Room.java", RoomRxJava2TypeNames.RX_ROOM.toString())
+    }
+
+    val LIVE_LAZY_LIST_PROVIDER by lazy {
+        loadJavaCode("common/input/LiveLazyListProvider.java",
+                PagingTypeNames.LIVE_LAZY_LIST_PROVIDER.toString())
+    }
+}
+fun testCodeGenScope(): CodeGenScope {
+    return CodeGenScope(Mockito.mock(ClassWriter::class.java))
+}
+
+fun simpleRun(vararg jfos : JavaFileObject, f: (TestInvocation) -> Unit): CompileTester {
+    return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+            .that(jfos.toList() + JavaFileObjects.forSourceString("foo.bar.MyClass",
+                    """
+                    package foo.bar;
+                    abstract public class MyClass {
+                    @android.arch.persistence.room.Query("foo")
+                    abstract public void setFoo(String foo);
+                    }
+                    """))
+            .processedWith(TestProcessor.builder()
+                    .nextRunHandler {
+                        f(it)
+                        true
+                    }
+                    .forAnnotations(Query::class, PrimaryKey::class, Embedded::class,
+                            ColumnInfo::class, Relation::class, Entity::class)
+                    .build())
+}
+
+fun loadJavaCode(fileName : String, qName : String) : JavaFileObject {
+    val contents = File("src/test/data/$fileName").readText(Charsets.UTF_8)
+    return JavaFileObjects.forSourceString(qName, contents)
+}
+
+fun createVerifierFromEntities(invocation: TestInvocation) : DatabaseVerifier {
+    val entities = invocation.roundEnv.getElementsAnnotatedWith(Entity::class.java).map {
+        EntityProcessor(invocation.context, MoreElements.asType(it)).process()
+    }
+    return DatabaseVerifier.create(invocation.context, Mockito.mock(Element::class.java),
+            entities)!!
+}
+
+/**
+ * Create mocks of [Element] and [TypeMirror] so that they can be used for instantiating a fake
+ * [android.arch.persistence.room.vo.Field].
+ */
+fun mockElementAndType(): Pair<Element, TypeMirror> {
+    val element = mock(Element::class.java)
+    val type = mock(TypeMirror::class.java)
+    doReturn(TypeKind.DECLARED).`when`(type).kind
+    doReturn(type).`when`(element).asType()
+    return element to type
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/verifier/DatabaseVerifierTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/verifier/DatabaseVerifierTest.kt
new file mode 100644
index 0000000..604c50f
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/verifier/DatabaseVerifierTest.kt
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.verifier
+
+import collect
+import columnNames
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.vo.CallType
+import android.arch.persistence.room.vo.Constructor
+import android.arch.persistence.room.vo.Database
+import android.arch.persistence.room.vo.Entity
+import android.arch.persistence.room.vo.Field
+import android.arch.persistence.room.vo.FieldGetter
+import android.arch.persistence.room.vo.FieldSetter
+import android.arch.persistence.room.vo.PrimaryKey
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.hasItem
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import simpleRun
+import java.sql.Connection
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+@RunWith(JUnit4::class)
+class DatabaseVerifierTest {
+    @Test
+    fun testSimpleDatabase() {
+        simpleRun { invocation ->
+            val verifier = createVerifier(invocation)
+            val stmt = verifier.connection.createStatement()
+            val rs = stmt.executeQuery("select * from sqlite_master WHERE type='table'")
+            assertThat(
+                    rs.collect { set -> set.getString("name") }, hasItem(`is`("User")))
+            val table = verifier.connection.prepareStatement("select * from User")
+            assertThat(table.columnNames(), `is`(listOf("id", "name", "lastName", "ratio")))
+
+            assertThat(getPrimaryKeys(verifier.connection, "User"), `is`(listOf("id")))
+        }.compilesWithoutError()
+    }
+
+    fun createVerifier(invocation: TestInvocation): DatabaseVerifier {
+        return DatabaseVerifier.create(invocation.context, mock(Element::class.java),
+                userDb(invocation.context).entities)!!
+    }
+
+    @Test
+    fun testFullEntityQuery() {
+        validQueryTest("select * from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            ColumnInfo("id", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("name", SQLTypeAffinity.TEXT),
+                            ColumnInfo("lastName", SQLTypeAffinity.TEXT),
+                            ColumnInfo("ratio", SQLTypeAffinity.REAL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testPartialFields() {
+        validQueryTest("select id, lastName from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            ColumnInfo("id", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("lastName", SQLTypeAffinity.TEXT)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testRenamedField() {
+        validQueryTest("select id as myId, lastName from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            ColumnInfo("myId", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("lastName", SQLTypeAffinity.TEXT)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testGrouped() {
+        validQueryTest("select MAX(ratio) from User GROUP BY name") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            // unfortunately, we don't get this information
+                            ColumnInfo("MAX(ratio)", SQLTypeAffinity.NULL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testConcat() {
+        validQueryTest("select name || lastName as mergedName from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            // unfortunately, we don't get this information
+                            ColumnInfo("mergedName", SQLTypeAffinity.NULL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testResultWithArgs() {
+        validQueryTest("select id, name || lastName as mergedName from User where name LIKE ?") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            // unfortunately, we don't get this information
+                            ColumnInfo("id", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("mergedName", SQLTypeAffinity.NULL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testDeleteQuery() {
+        validQueryTest("delete from User where name LIKE ?") {
+            assertThat(it, `is`(QueryResultInfo(emptyList())))
+        }
+    }
+
+    @Test
+    fun testUpdateQuery() {
+        validQueryTest("update User set name = ? WHERE id = ?") {
+            assertThat(it, `is`(QueryResultInfo(emptyList())))
+        }
+    }
+
+    @Test
+    fun testBadQuery() {
+        simpleRun { invocation ->
+            val verifier = createVerifier(invocation)
+            val (columns, error) = verifier.analyze("select foo from User")
+            assertThat(error, notNullValue())
+        }.compilesWithoutError()
+    }
+
+    private fun validQueryTest(sql: String, cb: (QueryResultInfo) -> Unit) {
+        simpleRun { invocation ->
+            val verifier = createVerifier(invocation)
+            val info = verifier.analyze(sql)
+            cb(info)
+        }.compilesWithoutError()
+    }
+
+    private fun userDb(context: Context): Database {
+        return database(entity("User",
+                field("id", primitive(context, TypeKind.INT), SQLTypeAffinity.INTEGER),
+                field("name", context.COMMON_TYPES.STRING, SQLTypeAffinity.TEXT),
+                field("lastName", context.COMMON_TYPES.STRING, SQLTypeAffinity.TEXT),
+                field("ratio", primitive(context, TypeKind.FLOAT), SQLTypeAffinity.REAL)))
+    }
+
+    private fun database(vararg entities: Entity): Database {
+        return Database(
+                element = mock(TypeElement::class.java),
+                type = mock(TypeMirror::class.java),
+                entities = entities.toList(),
+                daoMethods = emptyList(),
+                version = -1,
+                exportSchema = false,
+                enableForeignKeys = false)
+    }
+
+    private fun entity(tableName: String, vararg fields: Field): Entity {
+        return Entity(
+                element = mock(TypeElement::class.java),
+                tableName = tableName,
+                type = mock(DeclaredType::class.java),
+                fields = fields.toList(),
+                embeddedFields = emptyList(),
+                indices = emptyList(),
+                primaryKey = PrimaryKey(null, fields.take(1), false),
+                foreignKeys = emptyList(),
+                constructor = Constructor(mock(ExecutableElement::class.java), emptyList())
+        )
+    }
+
+    private fun field(name: String, type: TypeMirror, affinity: SQLTypeAffinity): Field {
+        val element = mock(Element::class.java)
+        doReturn(type).`when`(element).asType()
+        val f = Field(
+                element = element,
+                name = name,
+                type = type,
+                columnName = name,
+                affinity = affinity
+        )
+        assignGetterSetter(f, name, type)
+        return f
+    }
+
+    private fun assignGetterSetter(f: Field, name: String, type: TypeMirror) {
+        f.getter = FieldGetter(name, type, CallType.FIELD)
+        f.setter = FieldSetter(name, type, CallType.FIELD)
+    }
+
+    private fun primitive(context: Context, kind: TypeKind): PrimitiveType {
+        return context.processingEnv.typeUtils.getPrimitiveType(kind)
+    }
+
+    private fun getPrimaryKeys(connection: Connection, tableName: String): List<String> {
+        val stmt = connection.createStatement()
+        val resultSet = stmt.executeQuery("PRAGMA table_info($tableName)")
+        return resultSet.collect {
+            Pair(it.getString("name"), it.getInt("pk"))
+        }
+                .filter { it.second > 0 }
+                .sortedBy { it.second }
+                .map { it.first }
+
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/vo/IndexTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/vo/IndexTest.kt
new file mode 100644
index 0000000..c1ac029
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/vo/IndexTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.vo
+
+import android.arch.persistence.room.parser.SQLTypeAffinity
+import mockElementAndType
+import org.hamcrest.CoreMatchers
+import org.hamcrest.MatcherAssert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class IndexTest {
+    @Test
+    fun createSimpleSQL() {
+        val index = Index("foo", false, listOf(mockField("bar"), mockField("baz")))
+        MatcherAssert.assertThat(index.createQuery("my_table"), CoreMatchers.`is`(
+                "CREATE  INDEX `foo` ON `my_table` (`bar`, `baz`)"
+        ))
+    }
+
+    @Test
+    fun createUnique() {
+        val index = Index("foo", true, listOf(mockField("bar"), mockField("baz")))
+        MatcherAssert.assertThat(index.createQuery("my_table"), CoreMatchers.`is`(
+                "CREATE UNIQUE INDEX `foo` ON `my_table` (`bar`, `baz`)"
+        ))
+    }
+
+    private fun mockField(columnName : String): Field {
+        val (element, type) = mockElementAndType()
+        return Field(
+                element = element,
+                name = columnName + "_field",
+                affinity = SQLTypeAffinity.TEXT,
+                type = type,
+                columnName = columnName
+        )
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/DaoWriterTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/DaoWriterTest.kt
new file mode 100644
index 0000000..6d2bda3
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/DaoWriterTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import COMMON
+import android.arch.persistence.room.ext.RoomTypeNames
+import android.arch.persistence.room.processor.DaoProcessor
+import android.arch.persistence.room.testing.TestProcessor
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import createVerifierFromEntities
+import loadJavaCode
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class DaoWriterTest {
+    @Test
+    fun complexDao() {
+        singleDao(
+                loadJavaCode("databasewriter/input/ComplexDatabase.java",
+                        "foo.bar.ComplexDatabase"),
+                loadJavaCode("daoWriter/input/ComplexDao.java", "foo.bar.ComplexDao")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("daoWriter/output/ComplexDao.java", "foo.bar.ComplexDao_Impl")
+        )
+    }
+
+    @Test
+    fun writerDao() {
+        singleDao(
+                loadJavaCode("daoWriter/input/WriterDao.java", "foo.bar.WriterDao")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("daoWriter/output/WriterDao.java", "foo.bar.WriterDao_Impl")
+        )
+    }
+
+    @Test
+    fun deletionDao() {
+        singleDao(
+                loadJavaCode("daoWriter/input/DeletionDao.java", "foo.bar.DeletionDao")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("daoWriter/output/DeletionDao.java", "foo.bar.DeletionDao_Impl")
+        )
+    }
+
+    @Test
+    fun updateDao() {
+        singleDao(
+                loadJavaCode("daoWriter/input/UpdateDao.java", "foo.bar.UpdateDao")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("daoWriter/output/UpdateDao.java", "foo.bar.UpdateDao_Impl")
+        )
+    }
+
+    fun singleDao(vararg jfo : JavaFileObject): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(jfo.toList() + COMMON.USER + COMMON.MULTI_PKEY_ENTITY + COMMON.BOOK +
+                        COMMON.LIVE_DATA + COMMON.COMPUTABLE_LIVE_DATA)
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(android.arch.persistence.room.Dao::class)
+                        .nextRunHandler { invocation ->
+                            val dao = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            android.arch.persistence.room.Dao::class.java)
+                                    .first()
+                            val db = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            android.arch.persistence.room.Database::class.java)
+                                    .firstOrNull()
+                            val dbType = MoreTypes.asDeclared(if (db != null) {
+                                db.asType()
+                            } else {
+                                invocation.context.processingEnv.elementUtils
+                                        .getTypeElement(RoomTypeNames.ROOM_DB.toString()).asType()
+                            })
+                            val parser = DaoProcessor(
+                                    baseContext = invocation.context,
+                                    element = MoreElements.asType(dao),
+                                    dbType = dbType,
+                                    dbVerifier = createVerifierFromEntities(invocation))
+                            val parsedDao = parser.process()
+                            DaoWriter(parsedDao, invocation.processingEnv)
+                                    .write(invocation.processingEnv)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/DatabaseWriterTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/DatabaseWriterTest.kt
new file mode 100644
index 0000000..9a3252f
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/DatabaseWriterTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import COMMON
+import android.arch.persistence.room.RoomProcessor
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import loadJavaCode
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class DatabaseWriterTest {
+    @Test
+    fun simpleDb() {
+        singleDb(
+                loadJavaCode("databasewriter/input/ComplexDatabase.java",
+                        "foo.bar.ComplexDatabase"),
+                loadJavaCode("daoWriter/input/ComplexDao.java",
+                        "foo.bar.ComplexDao")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("databasewriter/output/ComplexDatabase.java",
+                        "foo.bar.ComplexDatabase_Impl")
+        )
+    }
+
+    private fun singleDb(vararg jfo : JavaFileObject): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(jfo.toList() + COMMON.USER +  COMMON.LIVE_DATA + COMMON.COMPUTABLE_LIVE_DATA)
+                .processedWith(RoomProcessor())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/EntityCursorConverterWriterTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/EntityCursorConverterWriterTest.kt
new file mode 100644
index 0000000..aaa6b2e
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/EntityCursorConverterWriterTest.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.processor.BaseEntityParserTest
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeSpec
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.lang.model.element.Modifier
+
+@RunWith(JUnit4::class)
+class EntityCursorConverterWriterTest : BaseEntityParserTest() {
+    companion object {
+        val OUT_PREFIX = """
+            package foo.bar;
+            import android.database.Cursor;
+            public class MyContainerClass {
+            """.trimIndent()
+        const val OUT_SUFFIX = "}"
+    }
+
+    @Test
+    fun generateSimple() {
+        generateAndMatch(
+                """
+                @PrimaryKey
+                private int id;
+                String name;
+                String lastName;
+                int age;
+                public int getId() { return id; }
+                public void setId(int id) { this.id = id; }
+                """,
+                """
+                private MyEntity __entityCursorConverter_fooBarMyEntity(Cursor cursor) {
+                  final MyEntity _entity;
+                  final int _cursorIndexOfId = cursor.getColumnIndex("id");
+                  final int _cursorIndexOfName = cursor.getColumnIndex("name");
+                  final int _cursorIndexOfLastName = cursor.getColumnIndex("lastName");
+                  final int _cursorIndexOfAge = cursor.getColumnIndex("age");
+                  _entity = new MyEntity();
+                  if (_cursorIndexOfId != -1) {
+                    final int _tmpId;
+                    _tmpId = cursor.getInt(_cursorIndexOfId);
+                    _entity.setId(_tmpId);
+                  }
+                  if (_cursorIndexOfName != -1) {
+                    _entity.name = cursor.getString(_cursorIndexOfName);
+                  }
+                  if (_cursorIndexOfLastName != -1) {
+                    _entity.lastName = cursor.getString(_cursorIndexOfLastName);
+                  }
+                  if (_cursorIndexOfAge != -1) {
+                    _entity.age = cursor.getInt(_cursorIndexOfAge);
+                  }
+                  return _entity;
+                }
+                """.trimIndent())
+    }
+
+    fun generateAndMatch(input: String, output : String,
+                         attributes: Map<String, String> = mapOf()) {
+        generate(input, attributes)
+                .compilesWithoutError()
+                .and()
+                .generatesSources(JavaFileObjects.forSourceString(
+                        "foo.bar.MyEntity_CursorConverter",
+                        listOf(OUT_PREFIX,output,OUT_SUFFIX).joinToString("\n")))
+    }
+
+    fun generate(input: String, attributes: Map<String, String> = mapOf()) : CompileTester {
+        return singleEntity(input, attributes) { entity, invocation ->
+            val className = ClassName.get("foo.bar","MyContainerClass")
+            val writer = object : ClassWriter(className){
+                override fun createTypeSpecBuilder(): TypeSpec.Builder {
+                    getOrCreateMethod(EntityCursorConverterWriter(entity))
+                    return TypeSpec.classBuilder(className).apply {
+                        addModifiers(Modifier.PUBLIC)
+                    }
+                }
+            }
+            writer.write(invocation.processingEnv)
+        }
+    }
+}
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/SQLiteOpenHelperWriterTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/SQLiteOpenHelperWriterTest.kt
new file mode 100644
index 0000000..26efb4b
--- /dev/null
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/writer/SQLiteOpenHelperWriterTest.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.writer
+
+import android.arch.persistence.room.processor.DatabaseProcessor
+import android.arch.persistence.room.testing.TestInvocation
+import android.arch.persistence.room.testing.TestProcessor
+import android.arch.persistence.room.vo.Database
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class SQLiteOpenHelperWriterTest {
+    companion object {
+        const val ENTITY_PREFIX = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            @Entity%s
+            public class MyEntity {
+            """
+        const val ENTITY_SUFFIX = "}"
+        const val DATABASE_CODE = """
+            package foo.bar;
+            import android.arch.persistence.room.*;
+            @Database(entities = {MyEntity.class}, version = 3)
+            abstract public class MyDatabase extends RoomDatabase {
+            }
+            """
+    }
+
+    @Test
+    fun createSimpleEntity() {
+        singleEntity(
+                """
+                @PrimaryKey
+                String uuid;
+                String name;
+                int age;
+                """.trimIndent()
+        ) { database, invocation ->
+            val query = SQLiteOpenHelperWriter(database)
+                    .createQuery(database.entities.first())
+            assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
+                    " `MyEntity` (`uuid` TEXT, `name` TEXT, `age` INTEGER NOT NULL," +
+                    " PRIMARY KEY(`uuid`))"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun multiplePrimaryKeys() {
+        singleEntity(
+                """
+                String uuid;
+                String name;
+                int age;
+                """.trimIndent(), attributes = mapOf("primaryKeys" to "{\"uuid\", \"name\"}")
+        ) { database, invocation ->
+            val query = SQLiteOpenHelperWriter(database)
+                    .createQuery(database.entities.first())
+            assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
+                    " `MyEntity` (`uuid` TEXT, `name` TEXT, `age` INTEGER NOT NULL," +
+                    " PRIMARY KEY(`uuid`, `name`))"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun autoIncrement() {
+        singleEntity(
+                """
+                @PrimaryKey(autoGenerate = true)
+                int uuid;
+                String name;
+                int age;
+                """.trimIndent()
+        ) { database, invocation ->
+            val query = SQLiteOpenHelperWriter(database)
+                    .createQuery(database.entities.first())
+            assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
+                    " `MyEntity` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
+                    " `name` TEXT, `age` INTEGER NOT NULL)"))
+        }.compilesWithoutError()
+    }
+
+    fun singleEntity(input: String, attributes: Map<String, String> = mapOf(),
+                     handler: (Database, TestInvocation) -> Unit): CompileTester {
+        val attributesReplacement : String
+        if (attributes.isEmpty()) {
+            attributesReplacement = ""
+        } else {
+            attributesReplacement = "(" +
+                    attributes.entries.map { "${it.key} = ${it.value}" }.joinToString(",") +
+                    ")".trimIndent()
+        }
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyEntity",
+                        ENTITY_PREFIX.format(attributesReplacement) + input + ENTITY_SUFFIX
+                ), JavaFileObjects.forSourceString("foo.bar.MyDatabase",
+                        DATABASE_CODE)))
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(android.arch.persistence.room.Database::class)
+                        .nextRunHandler { invocation ->
+                            val db = MoreElements.asType(invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            android.arch.persistence.room.Database::class.java)
+                                    .first())
+                            handler(DatabaseProcessor(invocation.context, db).process(), invocation)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/db-impl/.gitignore b/room/db-impl/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/room/db-impl/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/room/db-impl/build.gradle b/room/db-impl/build.gradle
new file mode 100644
index 0000000..978681d
--- /dev/null
+++ b/room/db-impl/build.gradle
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+archivesBaseName = "support-db-impl"
+
+dependencies {
+    compile libs.support.annotations
+    compile project(":room:db")
+}
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/room/db-impl/proguard-rules.pro b/room/db-impl/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/room/db-impl/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/room/db-impl/src/main/AndroidManifest.xml b/room/db-impl/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9350b90
--- /dev/null
+++ b/room/db-impl/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.persistence.db.framework">
+</manifest>
diff --git a/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteDatabase.java b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteDatabase.java
new file mode 100644
index 0000000..92a5820
--- /dev/null
+++ b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteDatabase.java
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db.framework;
+
+import android.arch.persistence.db.SimpleSQLiteQuery;
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteQuery;
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQuery;
+import android.database.sqlite.SQLiteTransactionListener;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.support.annotation.RequiresApi;
+import android.util.Pair;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Delegates all calls to an implementation of {@link SQLiteDatabase}.
+ */
+@SuppressWarnings("unused")
+class FrameworkSQLiteDatabase implements SupportSQLiteDatabase {
+    private static final String[] CONFLICT_VALUES = new String[]
+            {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+    private final SQLiteDatabase mDelegate;
+
+    /**
+     * Creates a wrapper around {@link SQLiteDatabase}.
+     *
+     * @param delegate The delegate to receive all calls.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public FrameworkSQLiteDatabase(SQLiteDatabase delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public SupportSQLiteStatement compileStatement(String sql) {
+        return new FrameworkSQLiteStatement(mDelegate.compileStatement(sql));
+    }
+
+    @Override
+    public void beginTransaction() {
+        mDelegate.beginTransaction();
+    }
+
+    @Override
+    public void beginTransactionNonExclusive() {
+        mDelegate.beginTransactionNonExclusive();
+    }
+
+    @Override
+    public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
+        mDelegate.beginTransactionWithListener(transactionListener);
+    }
+
+    @Override
+    public void beginTransactionWithListenerNonExclusive(
+            SQLiteTransactionListener transactionListener) {
+        mDelegate.beginTransactionWithListenerNonExclusive(transactionListener);
+    }
+
+    @Override
+    public void endTransaction() {
+        mDelegate.endTransaction();
+    }
+
+    @Override
+    public void setTransactionSuccessful() {
+        mDelegate.setTransactionSuccessful();
+    }
+
+    @Override
+    public boolean inTransaction() {
+        return mDelegate.inTransaction();
+    }
+
+    @Override
+    public boolean isDbLockedByCurrentThread() {
+        return mDelegate.isDbLockedByCurrentThread();
+    }
+
+    @Override
+    public boolean yieldIfContendedSafely() {
+        return mDelegate.yieldIfContendedSafely();
+    }
+
+    @Override
+    public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
+        return mDelegate.yieldIfContendedSafely(sleepAfterYieldDelay);
+    }
+
+    @Override
+    public int getVersion() {
+        return mDelegate.getVersion();
+    }
+
+    @Override
+    public void setVersion(int version) {
+        mDelegate.setVersion(version);
+    }
+
+    @Override
+    public long getMaximumSize() {
+        return mDelegate.getMaximumSize();
+    }
+
+    @Override
+    public long setMaximumSize(long numBytes) {
+        return mDelegate.setMaximumSize(numBytes);
+    }
+
+    @Override
+    public long getPageSize() {
+        return mDelegate.getPageSize();
+    }
+
+    @Override
+    public void setPageSize(long numBytes) {
+        mDelegate.setPageSize(numBytes);
+    }
+
+    @Override
+    public Cursor query(String query) {
+        return query(new SimpleSQLiteQuery(query));
+    }
+
+    @Override
+    public Cursor query(String query, Object[] bindArgs) {
+        return query(new SimpleSQLiteQuery(query, bindArgs));
+    }
+
+
+    @Override
+    public Cursor query(final SupportSQLiteQuery supportQuery) {
+        return mDelegate.rawQueryWithFactory(new SQLiteDatabase.CursorFactory() {
+            @Override
+            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
+                    String editTable, SQLiteQuery query) {
+                supportQuery.bindTo(new FrameworkSQLiteProgram(query));
+                return new SQLiteCursor(masterQuery, editTable, query);
+            }
+        }, supportQuery.getSql(), EMPTY_STRING_ARRAY, null);
+    }
+
+    @Override
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    public Cursor query(final SupportSQLiteQuery supportQuery,
+            CancellationSignal cancellationSignal) {
+        return mDelegate.rawQueryWithFactory(new SQLiteDatabase.CursorFactory() {
+            @Override
+            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
+                    String editTable, SQLiteQuery query) {
+                supportQuery.bindTo(new FrameworkSQLiteProgram(query));
+                return new SQLiteCursor(masterQuery, editTable, query);
+            }
+        }, supportQuery.getSql(), EMPTY_STRING_ARRAY, null, cancellationSignal);
+    }
+
+    @Override
+    public long insert(String table, int conflictAlgorithm, ContentValues values)
+            throws SQLException {
+        return mDelegate.insertWithOnConflict(table, null, values,
+                conflictAlgorithm);
+    }
+
+    @Override
+    public int delete(String table, String whereClause, Object[] whereArgs) {
+        String query = "DELETE FROM " + table
+                + (isEmpty(whereClause) ? "" : " WHERE " + whereClause);
+        SupportSQLiteStatement statement = compileStatement(query);
+        SimpleSQLiteQuery.bind(statement, whereArgs);
+        return statement.executeUpdateDelete();
+    }
+
+
+    @Override
+    public int update(String table, int conflictAlgorithm, ContentValues values, String whereClause,
+            Object[] whereArgs) {
+        // taken from SQLiteDatabase class.
+        if (values == null || values.size() == 0) {
+            throw new IllegalArgumentException("Empty values");
+        }
+        StringBuilder sql = new StringBuilder(120);
+        sql.append("UPDATE ");
+        sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+        sql.append(table);
+        sql.append(" SET ");
+
+        // move all bind args to one array
+        int setValuesSize = values.size();
+        int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
+        Object[] bindArgs = new Object[bindArgsSize];
+        int i = 0;
+        for (String colName : values.keySet()) {
+            sql.append((i > 0) ? "," : "");
+            sql.append(colName);
+            bindArgs[i++] = values.get(colName);
+            sql.append("=?");
+        }
+        if (whereArgs != null) {
+            for (i = setValuesSize; i < bindArgsSize; i++) {
+                bindArgs[i] = whereArgs[i - setValuesSize];
+            }
+        }
+        if (!isEmpty(whereClause)) {
+            sql.append(" WHERE ");
+            sql.append(whereClause);
+        }
+        SupportSQLiteStatement stmt = compileStatement(sql.toString());
+        SimpleSQLiteQuery.bind(stmt, bindArgs);
+        return stmt.executeUpdateDelete();
+    }
+
+    @Override
+    public void execSQL(String sql) throws SQLException {
+        mDelegate.execSQL(sql);
+    }
+
+    @Override
+    public void execSQL(String sql, Object[] bindArgs) throws SQLException {
+        mDelegate.execSQL(sql, bindArgs);
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return mDelegate.isReadOnly();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return mDelegate.isOpen();
+    }
+
+    @Override
+    public boolean needUpgrade(int newVersion) {
+        return mDelegate.needUpgrade(newVersion);
+    }
+
+    @Override
+    public String getPath() {
+        return mDelegate.getPath();
+    }
+
+    @Override
+    public void setLocale(Locale locale) {
+        mDelegate.setLocale(locale);
+    }
+
+    @Override
+    public void setMaxSqlCacheSize(int cacheSize) {
+        mDelegate.setMaxSqlCacheSize(cacheSize);
+    }
+
+    @Override
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    public void setForeignKeyConstraintsEnabled(boolean enable) {
+        mDelegate.setForeignKeyConstraintsEnabled(enable);
+    }
+
+    @Override
+    public boolean enableWriteAheadLogging() {
+        return mDelegate.enableWriteAheadLogging();
+    }
+
+    @Override
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    public void disableWriteAheadLogging() {
+        mDelegate.disableWriteAheadLogging();
+    }
+
+    @Override
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    public boolean isWriteAheadLoggingEnabled() {
+        return mDelegate.isWriteAheadLoggingEnabled();
+    }
+
+    @Override
+    public List<Pair<String, String>> getAttachedDbs() {
+        return mDelegate.getAttachedDbs();
+    }
+
+    @Override
+    public boolean isDatabaseIntegrityOk() {
+        return mDelegate.isDatabaseIntegrityOk();
+    }
+
+    @Override
+    public void close() throws IOException {
+        mDelegate.close();
+    }
+
+    private static boolean isEmpty(String input) {
+        return input == null || input.length() == 0;
+    }
+}
diff --git a/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper.java b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper.java
new file mode 100644
index 0000000..aa08fa4
--- /dev/null
+++ b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db.framework;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
+class FrameworkSQLiteOpenHelper implements SupportSQLiteOpenHelper {
+    private final OpenHelper mDelegate;
+
+    FrameworkSQLiteOpenHelper(Context context, String name, int version,
+            DatabaseErrorHandler errorHandler,
+            SupportSQLiteOpenHelper.Callback callback) {
+        mDelegate = createDelegate(context, name, version, errorHandler, callback);
+    }
+
+    private OpenHelper createDelegate(Context context, String name,
+            int version, DatabaseErrorHandler errorHandler,
+            final Callback callback) {
+        return new OpenHelper(context, name, null, version, errorHandler) {
+            @Override
+            public void onCreate(SQLiteDatabase sqLiteDatabase) {
+                mWrappedDb = new FrameworkSQLiteDatabase(sqLiteDatabase);
+                callback.onCreate(mWrappedDb);
+            }
+
+            @Override
+            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
+                callback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion);
+            }
+
+            @Override
+            public void onConfigure(SQLiteDatabase db) {
+                callback.onConfigure(getWrappedDb(db));
+            }
+
+            @Override
+            public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+                callback.onDowngrade(getWrappedDb(db), oldVersion, newVersion);
+            }
+
+            @Override
+            public void onOpen(SQLiteDatabase db) {
+                callback.onOpen(getWrappedDb(db));
+            }
+        };
+    }
+
+    @Override
+    public String getDatabaseName() {
+        return mDelegate.getDatabaseName();
+    }
+
+    @Override
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    public void setWriteAheadLoggingEnabled(boolean enabled) {
+        mDelegate.setWriteAheadLoggingEnabled(enabled);
+    }
+
+    @Override
+    public SupportSQLiteDatabase getWritableDatabase() {
+        return mDelegate.getWritableSupportDatabase();
+    }
+
+    @Override
+    public SupportSQLiteDatabase getReadableDatabase() {
+        return mDelegate.getReadableSupportDatabase();
+    }
+
+    @Override
+    public void close() {
+        mDelegate.close();
+    }
+
+    abstract static class OpenHelper extends SQLiteOpenHelper {
+
+        FrameworkSQLiteDatabase mWrappedDb;
+
+        OpenHelper(Context context, String name,
+                SQLiteDatabase.CursorFactory factory, int version,
+                DatabaseErrorHandler errorHandler) {
+            super(context, name, factory, version, errorHandler);
+        }
+
+        SupportSQLiteDatabase getWritableSupportDatabase() {
+            SQLiteDatabase db = super.getWritableDatabase();
+            return getWrappedDb(db);
+        }
+
+        SupportSQLiteDatabase getReadableSupportDatabase() {
+            SQLiteDatabase db = super.getReadableDatabase();
+            return getWrappedDb(db);
+        }
+
+        FrameworkSQLiteDatabase getWrappedDb(SQLiteDatabase sqLiteDatabase) {
+            if (mWrappedDb == null) {
+                mWrappedDb = new FrameworkSQLiteDatabase(sqLiteDatabase);
+            }
+            return mWrappedDb;
+        }
+
+        @Override
+        public synchronized void close() {
+            super.close();
+            mWrappedDb = null;
+        }
+    }
+}
diff --git a/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java
new file mode 100644
index 0000000..7b4245b
--- /dev/null
+++ b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db.framework;
+
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+
+/**
+ * Implements {@link SupportSQLiteOpenHelper.Factory} using the SQLite implementation in the
+ * framework.
+ */
+@SuppressWarnings("unused")
+public class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
+    @Override
+    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
+        return new FrameworkSQLiteOpenHelper(
+                configuration.context, configuration.name,
+                configuration.version, configuration.errorHandler, configuration.callback
+        );
+    }
+}
diff --git a/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteProgram.java b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteProgram.java
new file mode 100644
index 0000000..6c2bb72
--- /dev/null
+++ b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteProgram.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db.framework;
+
+import android.arch.persistence.db.SupportSQLiteProgram;
+import android.database.sqlite.SQLiteProgram;
+
+/**
+ * An wrapper around {@link SQLiteProgram} to implement {@link SupportSQLiteProgram} API.
+ */
+class FrameworkSQLiteProgram implements SupportSQLiteProgram {
+    private final SQLiteProgram mDelegate;
+
+    FrameworkSQLiteProgram(SQLiteProgram delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public void bindNull(int index) {
+        mDelegate.bindNull(index);
+    }
+
+    @Override
+    public void bindLong(int index, long value) {
+        mDelegate.bindLong(index, value);
+    }
+
+    @Override
+    public void bindDouble(int index, double value) {
+        mDelegate.bindDouble(index, value);
+    }
+
+    @Override
+    public void bindString(int index, String value) {
+        mDelegate.bindString(index, value);
+    }
+
+    @Override
+    public void bindBlob(int index, byte[] value) {
+        mDelegate.bindBlob(index, value);
+    }
+
+    @Override
+    public void clearBindings() {
+        mDelegate.clearBindings();
+    }
+
+    @Override
+    public void close() throws Exception {
+        mDelegate.close();
+    }
+}
diff --git a/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteStatement.java b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteStatement.java
new file mode 100644
index 0000000..a2daf12
--- /dev/null
+++ b/room/db-impl/src/main/java/android/arch/persistence/db/framework/FrameworkSQLiteStatement.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db.framework;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.database.sqlite.SQLiteStatement;
+
+/**
+ * Delegates all calls to a {@link SQLiteStatement}.
+ */
+class FrameworkSQLiteStatement implements SupportSQLiteStatement {
+    private final SQLiteStatement mDelegate;
+
+    /**
+     * Creates a wrapper around a framework {@link SQLiteStatement}.
+     *
+     * @param delegate The SQLiteStatement to delegate calls to.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public FrameworkSQLiteStatement(SQLiteStatement delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public void bindNull(int index) {
+        mDelegate.bindNull(index);
+    }
+
+    @Override
+    public void bindLong(int index, long value) {
+        mDelegate.bindLong(index, value);
+    }
+
+    @Override
+    public void bindDouble(int index, double value) {
+        mDelegate.bindDouble(index, value);
+    }
+
+    @Override
+    public void bindString(int index, String value) {
+        mDelegate.bindString(index, value);
+    }
+
+    @Override
+    public void bindBlob(int index, byte[] value) {
+        mDelegate.bindBlob(index, value);
+    }
+
+    @Override
+    public void clearBindings() {
+        mDelegate.clearBindings();
+    }
+
+    @Override
+    public void execute() {
+        mDelegate.execute();
+    }
+
+    @Override
+    public int executeUpdateDelete() {
+        return mDelegate.executeUpdateDelete();
+    }
+
+    @Override
+    public long executeInsert() {
+        return mDelegate.executeInsert();
+    }
+
+    @Override
+    public long simpleQueryForLong() {
+        return mDelegate.simpleQueryForLong();
+    }
+
+    @Override
+    public String simpleQueryForString() {
+        return mDelegate.simpleQueryForString();
+    }
+
+    @Override
+    public void close() throws Exception {
+        mDelegate.close();
+    }
+}
diff --git a/room/db/build.gradle b/room/db/build.gradle
new file mode 100644
index 0000000..d495bb8
--- /dev/null
+++ b/room/db/build.gradle
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+archivesBaseName = "support-db"
+
+dependencies {
+    compile libs.support.annotations
+}
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/room/db/src/main/AndroidManifest.xml b/room/db/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8a27324
--- /dev/null
+++ b/room/db/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.persistence.db">
+</manifest>
diff --git a/room/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java b/room/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java
new file mode 100644
index 0000000..b821176
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+/**
+ * A basic implemtation of {@link SupportSQLiteQuery} which receives a query and its args and binds
+ * args based on the passed in Object type.
+ */
+public class SimpleSQLiteQuery implements SupportSQLiteQuery {
+    private final String mQuery;
+    private final Object[] mBindArgs;
+
+    /**
+     * Creates an SQL query with the sql string and the bind arguments.
+     *
+     * @param query    The query string, can include bind arguments (.e.g ?).
+     * @param bindArgs The bind argument value that will replace the placeholders in the query.
+     */
+    public SimpleSQLiteQuery(String query, Object[] bindArgs) {
+        mQuery = query;
+        mBindArgs = bindArgs;
+    }
+
+    /**
+     * Creates an SQL query without any bind arguments.
+     *
+     * @param query The SQL query to execute. Cannot include bind parameters.
+     */
+    public SimpleSQLiteQuery(String query) {
+        this(query, null);
+    }
+
+    @Override
+    public String getSql() {
+        return mQuery;
+    }
+
+    @Override
+    public void bindTo(SupportSQLiteProgram statement) {
+        bind(statement, mBindArgs);
+    }
+
+    /**
+     * Binds the given arguments into the given sqlite statement.
+     *
+     * @param statement The sqlite statement
+     * @param bindArgs  The list of bind arguments
+     */
+    public static void bind(SupportSQLiteProgram statement, Object[] bindArgs) {
+        if (bindArgs == null) {
+            return;
+        }
+        final int limit = bindArgs.length;
+        for (int i = 0; i < limit; i++) {
+            final Object arg = bindArgs[i];
+            bind(statement, i + 1, arg);
+        }
+    }
+
+    private static void bind(SupportSQLiteProgram statement, int index, Object arg) {
+        // extracted from android.database.sqlite.SQLiteConnection
+        if (arg == null) {
+            statement.bindNull(index);
+        } else if (arg instanceof byte[]) {
+            statement.bindBlob(index, (byte[]) arg);
+        } else if (arg instanceof Float) {
+            statement.bindDouble(index, (Float) arg);
+        } else if (arg instanceof Double) {
+            statement.bindDouble(index, (Double) arg);
+        } else if (arg instanceof Long) {
+            statement.bindLong(index, (Long) arg);
+        } else if (arg instanceof Integer) {
+            statement.bindLong(index, (Integer) arg);
+        } else if (arg instanceof Short) {
+            statement.bindLong(index, (Short) arg);
+        } else if (arg instanceof Byte) {
+            statement.bindLong(index, (Byte) arg);
+        } else if (arg instanceof String) {
+            statement.bindString(index, (String) arg);
+        } else {
+            throw new IllegalArgumentException("Cannot bind " + arg + " at index " + index
+                    + " Supported types: null, byte[], float, double, long, int, short, byte,"
+                    + " string");
+        }
+    }
+}
diff --git a/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteDatabase.java b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteDatabase.java
new file mode 100644
index 0000000..5f71baf
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteDatabase.java
@@ -0,0 +1,605 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteTransactionListener;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.support.annotation.RequiresApi;
+import android.util.Pair;
+
+import java.io.Closeable;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * A database abstraction which removes the framework dependency and allows swapping underlying
+ * sql versions. It mimics the behavior of {@link android.database.sqlite.SQLiteDatabase}
+ */
+@SuppressWarnings("unused")
+public interface SupportSQLiteDatabase extends Closeable {
+    /**
+     * Compiles the given SQL statement.
+     *
+     * @param sql The sql query.
+     * @return Compiled statement.
+     */
+    SupportSQLiteStatement compileStatement(String sql);
+
+    /**
+     * Begins a transaction in EXCLUSIVE mode.
+     * <p>
+     * Transactions can be nested.
+     * When the outer transaction is ended all of
+     * the work done in that transaction and all of the nested transactions will be committed or
+     * rolled back. The changes will be rolled back if any transaction is ended without being
+     * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
+     * </p>
+     * <p>Here is the standard idiom for transactions:
+     *
+     * <pre>
+     *   db.beginTransaction();
+     *   try {
+     *     ...
+     *     db.setTransactionSuccessful();
+     *   } finally {
+     *     db.endTransaction();
+     *   }
+     * </pre>
+     */
+    void beginTransaction();
+
+    /**
+     * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
+     * the outer transaction is ended all of the work done in that transaction
+     * and all of the nested transactions will be committed or rolled back. The
+     * changes will be rolled back if any transaction is ended without being
+     * marked as clean (by calling setTransactionSuccessful). Otherwise they
+     * will be committed.
+     * <p>
+     * Here is the standard idiom for transactions:
+     *
+     * <pre>
+     *   db.beginTransactionNonExclusive();
+     *   try {
+     *     ...
+     *     db.setTransactionSuccessful();
+     *   } finally {
+     *     db.endTransaction();
+     *   }
+     * </pre>
+     */
+    void beginTransactionNonExclusive();
+
+    /**
+     * Begins a transaction in EXCLUSIVE mode.
+     * <p>
+     * Transactions can be nested.
+     * When the outer transaction is ended all of
+     * the work done in that transaction and all of the nested transactions will be committed or
+     * rolled back. The changes will be rolled back if any transaction is ended without being
+     * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
+     * </p>
+     * <p>Here is the standard idiom for transactions:
+     *
+     * <pre>
+     *   db.beginTransactionWithListener(listener);
+     *   try {
+     *     ...
+     *     db.setTransactionSuccessful();
+     *   } finally {
+     *     db.endTransaction();
+     *   }
+     * </pre>
+     *
+     * @param transactionListener listener that should be notified when the transaction begins,
+     *                            commits, or is rolled back, either explicitly or by a call to
+     *                            {@link #yieldIfContendedSafely}.
+     */
+    void beginTransactionWithListener(SQLiteTransactionListener transactionListener);
+
+    /**
+     * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
+     * the outer transaction is ended all of the work done in that transaction
+     * and all of the nested transactions will be committed or rolled back. The
+     * changes will be rolled back if any transaction is ended without being
+     * marked as clean (by calling setTransactionSuccessful). Otherwise they
+     * will be committed.
+     * <p>
+     * Here is the standard idiom for transactions:
+     *
+     * <pre>
+     *   db.beginTransactionWithListenerNonExclusive(listener);
+     *   try {
+     *     ...
+     *     db.setTransactionSuccessful();
+     *   } finally {
+     *     db.endTransaction();
+     *   }
+     * </pre>
+     *
+     * @param transactionListener listener that should be notified when the
+     *                            transaction begins, commits, or is rolled back, either
+     *                            explicitly or by a call to {@link #yieldIfContendedSafely}.
+     */
+    void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener transactionListener);
+
+    /**
+     * End a transaction. See beginTransaction for notes about how to use this and when transactions
+     * are committed and rolled back.
+     */
+    void endTransaction();
+
+    /**
+     * Marks the current transaction as successful. Do not do any more database work between
+     * calling this and calling endTransaction. Do as little non-database work as possible in that
+     * situation too. If any errors are encountered between this and endTransaction the transaction
+     * will still be committed.
+     *
+     * @throws IllegalStateException if the current thread is not in a transaction or the
+     *                               transaction is already marked as successful.
+     */
+    void setTransactionSuccessful();
+
+    /**
+     * Returns true if the current thread has a transaction pending.
+     *
+     * @return True if the current thread is in a transaction.
+     */
+    boolean inTransaction();
+
+    /**
+     * Returns true if the current thread is holding an active connection to the database.
+     * <p>
+     * The name of this method comes from a time when having an active connection
+     * to the database meant that the thread was holding an actual lock on the
+     * database.  Nowadays, there is no longer a true "database lock" although threads
+     * may block if they cannot acquire a database connection to perform a
+     * particular operation.
+     * </p>
+     *
+     * @return True if the current thread is holding an active connection to the database.
+     */
+    boolean isDbLockedByCurrentThread();
+
+    /**
+     * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+     * successful so far. Do not call setTransactionSuccessful before calling this. When this
+     * returns a new transaction will have been created but not marked as successful. This assumes
+     * that there are no nested transactions (beginTransaction has only been called once) and will
+     * throw an exception if that is not the case.
+     *
+     * @return true if the transaction was yielded
+     */
+    boolean yieldIfContendedSafely();
+
+    /**
+     * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+     * successful so far. Do not call setTransactionSuccessful before calling this. When this
+     * returns a new transaction will have been created but not marked as successful. This assumes
+     * that there are no nested transactions (beginTransaction has only been called once) and will
+     * throw an exception if that is not the case.
+     *
+     * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
+     *                             the lock was actually yielded. This will allow other background
+     *                             threads to make some
+     *                             more progress than they would if we started the transaction
+     *                             immediately.
+     * @return true if the transaction was yielded
+     */
+    boolean yieldIfContendedSafely(long sleepAfterYieldDelay);
+
+    /**
+     * Gets the database version.
+     *
+     * @return the database version
+     */
+    int getVersion();
+
+    /**
+     * Sets the database version.
+     *
+     * @param version the new database version
+     */
+    void setVersion(int version);
+
+    /**
+     * Returns the maximum size the database may grow to.
+     *
+     * @return the new maximum database size
+     */
+    long getMaximumSize();
+
+    /**
+     * Sets the maximum size the database will grow to. The maximum size cannot
+     * be set below the current size.
+     *
+     * @param numBytes the maximum database size, in bytes
+     * @return the new maximum database size
+     */
+    long setMaximumSize(long numBytes);
+
+    /**
+     * Returns the current database page size, in bytes.
+     *
+     * @return the database page size, in bytes
+     */
+    long getPageSize();
+
+    /**
+     * Sets the database page size. The page size must be a power of two. This
+     * method does not work if any data has been written to the database file,
+     * and must be called right after the database has been created.
+     *
+     * @param numBytes the database page size, in bytes
+     */
+    void setPageSize(long numBytes);
+
+    /**
+     * Runs the given query on the database. If you would like to have typed bind arguments,
+     * use {@link #query(SupportSQLiteQuery)}.
+     *
+     * @param query The SQL query that includes the query and can bind into a given compiled
+     *              program.
+     * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+     * {@link Cursor}s are not synchronized, see the documentation for more details.
+     * @see #query(SupportSQLiteQuery)
+     */
+    Cursor query(String query);
+
+    /**
+     * Runs the given query on the database. If you would like to have bind arguments,
+     * use {@link #query(SupportSQLiteQuery)}.
+     *
+     * @param query The SQL query that includes the query and can bind into a given compiled
+     *              program.
+     * @param bindArgs The query arguments to bind.
+     * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+     * {@link Cursor}s are not synchronized, see the documentation for more details.
+     * @see #query(SupportSQLiteQuery)
+     */
+    Cursor query(String query, Object[] bindArgs);
+
+    /**
+     * Runs the given query on the database.
+     * <p>
+     * This class allows using type safe sql program bindings while running queries.
+     *
+     * @param query The SQL query that includes the query and can bind into a given compiled
+     *              program.
+     * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+     * {@link Cursor}s are not synchronized, see the documentation for more details.
+     * @see SimpleSQLiteQuery
+     */
+    Cursor query(SupportSQLiteQuery query);
+
+    /**
+     * Runs the given query on the database.
+     * <p>
+     * This class allows using type safe sql program bindings while running queries.
+     *
+     * @param query The SQL query that includes the query and can bind into a given compiled
+     *              program.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+     * when the query is executed.
+     * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+     * {@link Cursor}s are not synchronized, see the documentation for more details.
+     */
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    Cursor query(SupportSQLiteQuery query, CancellationSignal cancellationSignal);
+
+    /**
+     * Convenience method for inserting a row into the database.
+     *
+     * @param table          the table to insert the row into
+     * @param values         this map contains the initial column values for the
+     *                       row. The keys should be the column names and the values the
+     *                       column values
+     * @param conflictAlgorithm for insert conflict resolver. One of
+     * {@link SQLiteDatabase#CONFLICT_NONE}, {@link SQLiteDatabase#CONFLICT_ROLLBACK},
+     * {@link SQLiteDatabase#CONFLICT_ABORT}, {@link SQLiteDatabase#CONFLICT_FAIL},
+     * {@link SQLiteDatabase#CONFLICT_IGNORE}, {@link SQLiteDatabase#CONFLICT_REPLACE}.
+     * @return the row ID of the newly inserted row, or -1 if an error occurred
+     * @throws SQLException If the insert fails
+     */
+    long insert(String table, int conflictAlgorithm, ContentValues values) throws SQLException;
+
+    /**
+     * Convenience method for deleting rows in the database.
+     *
+     * @param table       the table to delete from
+     * @param whereClause the optional WHERE clause to apply when deleting.
+     *                    Passing null will delete all rows.
+     * @param whereArgs   You may include ?s in the where clause, which
+     *                    will be replaced by the values from whereArgs. The values
+     *                    will be bound as Strings.
+     * @return the number of rows affected if a whereClause is passed in, 0
+     * otherwise. To remove all rows and get a count pass "1" as the
+     * whereClause.
+     */
+    int delete(String table, String whereClause, Object[] whereArgs);
+
+    /**
+     * Convenience method for updating rows in the database.
+     *
+     * @param table       the table to update in
+     * @param conflictAlgorithm for update conflict resolver. One of
+     * {@link SQLiteDatabase#CONFLICT_NONE}, {@link SQLiteDatabase#CONFLICT_ROLLBACK},
+     * {@link SQLiteDatabase#CONFLICT_ABORT}, {@link SQLiteDatabase#CONFLICT_FAIL},
+     * {@link SQLiteDatabase#CONFLICT_IGNORE}, {@link SQLiteDatabase#CONFLICT_REPLACE}.
+     * @param values      a map from column names to new column values. null is a
+     *                    valid value that will be translated to NULL.
+     * @param whereClause the optional WHERE clause to apply when updating.
+     *                    Passing null will update all rows.
+     * @param whereArgs   You may include ?s in the where clause, which
+     *                    will be replaced by the values from whereArgs. The values
+     *                    will be bound as Strings.
+     * @return the number of rows affected
+     */
+    int update(String table, int conflictAlgorithm,
+            ContentValues values, String whereClause, Object[] whereArgs);
+
+    /**
+     * Execute a single SQL statement that does not return any data.
+     * <p>
+     * When using {@link #enableWriteAheadLogging()}, journal_mode is
+     * automatically managed by this class. So, do not set journal_mode
+     * using "PRAGMA journal_mode'<value>" statement if your app is using
+     * {@link #enableWriteAheadLogging()}
+     * </p>
+     *
+     * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
+     *            not supported.
+     * @throws SQLException if the SQL string is invalid
+     * @see #query(SupportSQLiteQuery)
+     */
+    void execSQL(String sql) throws SQLException;
+
+    /**
+     * Execute a single SQL statement that does not return any data.
+     * <p>
+     * When using {@link #enableWriteAheadLogging()}, journal_mode is
+     * automatically managed by this class. So, do not set journal_mode
+     * using "PRAGMA journal_mode'<value>" statement if your app is using
+     * {@link #enableWriteAheadLogging()}
+     * </p>
+     *
+     * @param sql      the SQL statement to be executed. Multiple statements separated by semicolons
+     *                 are
+     *                 not supported.
+     * @param bindArgs only byte[], String, Long and Double are supported in selectionArgs.
+     * @throws SQLException if the SQL string is invalid
+     * @see #query(SupportSQLiteQuery)
+     */
+    void execSQL(String sql, Object[] bindArgs) throws SQLException;
+
+    /**
+     * Returns true if the database is opened as read only.
+     *
+     * @return True if database is opened as read only.
+     */
+    boolean isReadOnly();
+
+    /**
+     * Returns true if the database is currently open.
+     *
+     * @return True if the database is currently open (has not been closed).
+     */
+    boolean isOpen();
+
+    /**
+     * Returns true if the new version code is greater than the current database version.
+     *
+     * @param newVersion The new version code.
+     * @return True if the new version code is greater than the current database version.
+     */
+    boolean needUpgrade(int newVersion);
+
+    /**
+     * Gets the path to the database file.
+     *
+     * @return The path to the database file.
+     */
+    String getPath();
+
+    /**
+     * Sets the locale for this database.  Does nothing if this database has
+     * the {@link SQLiteDatabase#NO_LOCALIZED_COLLATORS} flag set or was opened read only.
+     *
+     * @param locale The new locale.
+     * @throws SQLException if the locale could not be set.  The most common reason
+     *                      for this is that there is no collator available for the locale you
+     *                      requested.
+     *                      In this case the database remains unchanged.
+     */
+    void setLocale(Locale locale);
+
+    /**
+     * Sets the maximum size of the prepared-statement cache for this database.
+     * (size of the cache = number of compiled-sql-statements stored in the cache).
+     * <p>
+     * Maximum cache size can ONLY be increased from its current size (default = 10).
+     * If this method is called with smaller size than the current maximum value,
+     * then IllegalStateException is thrown.
+     * <p>
+     * This method is thread-safe.
+     *
+     * @param cacheSize the size of the cache. can be (0 to
+     *                  {@link SQLiteDatabase#MAX_SQL_CACHE_SIZE})
+     * @throws IllegalStateException if input cacheSize gt;
+     *                               {@link SQLiteDatabase#MAX_SQL_CACHE_SIZE}.
+     */
+    void setMaxSqlCacheSize(int cacheSize);
+
+    /**
+     * Sets whether foreign key constraints are enabled for the database.
+     * <p>
+     * By default, foreign key constraints are not enforced by the database.
+     * This method allows an application to enable foreign key constraints.
+     * It must be called each time the database is opened to ensure that foreign
+     * key constraints are enabled for the session.
+     * </p><p>
+     * A good time to call this method is right after calling {@code #openOrCreateDatabase}
+     * or in the {@link SupportSQLiteOpenHelper.Callback#onConfigure} callback.
+     * </p><p>
+     * When foreign key constraints are disabled, the database does not check whether
+     * changes to the database will violate foreign key constraints.  Likewise, when
+     * foreign key constraints are disabled, the database will not execute cascade
+     * delete or update triggers.  As a result, it is possible for the database
+     * state to become inconsistent.  To perform a database integrity check,
+     * call {@link #isDatabaseIntegrityOk}.
+     * </p><p>
+     * This method must not be called while a transaction is in progress.
+     * </p><p>
+     * See also <a href="http://sqlite.org/foreignkeys.html">SQLite Foreign Key Constraints</a>
+     * for more details about foreign key constraint support.
+     * </p>
+     *
+     * @param enable True to enable foreign key constraints, false to disable them.
+     * @throws IllegalStateException if the are transactions is in progress
+     *                               when this method is called.
+     */
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    void setForeignKeyConstraintsEnabled(boolean enable);
+
+    /**
+     * This method enables parallel execution of queries from multiple threads on the
+     * same database.  It does this by opening multiple connections to the database
+     * and using a different database connection for each query.  The database
+     * journal mode is also changed to enable writes to proceed concurrently with reads.
+     * <p>
+     * When write-ahead logging is not enabled (the default), it is not possible for
+     * reads and writes to occur on the database at the same time.  Before modifying the
+     * database, the writer implicitly acquires an exclusive lock on the database which
+     * prevents readers from accessing the database until the write is completed.
+     * </p><p>
+     * In contrast, when write-ahead logging is enabled (by calling this method), write
+     * operations occur in a separate log file which allows reads to proceed concurrently.
+     * While a write is in progress, readers on other threads will perceive the state
+     * of the database as it was before the write began.  When the write completes, readers
+     * on other threads will then perceive the new state of the database.
+     * </p><p>
+     * It is a good idea to enable write-ahead logging whenever a database will be
+     * concurrently accessed and modified by multiple threads at the same time.
+     * However, write-ahead logging uses significantly more memory than ordinary
+     * journaling because there are multiple connections to the same database.
+     * So if a database will only be used by a single thread, or if optimizing
+     * concurrency is not very important, then write-ahead logging should be disabled.
+     * </p><p>
+     * After calling this method, execution of queries in parallel is enabled as long as
+     * the database remains open.  To disable execution of queries in parallel, either
+     * call {@link #disableWriteAheadLogging} or close the database and reopen it.
+     * </p><p>
+     * The maximum number of connections used to execute queries in parallel is
+     * dependent upon the device memory and possibly other properties.
+     * </p><p>
+     * If a query is part of a transaction, then it is executed on the same database handle the
+     * transaction was begun.
+     * </p><p>
+     * Writers should use {@link #beginTransactionNonExclusive()} or
+     * {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
+     * to start a transaction.  Non-exclusive mode allows database file to be in readable
+     * by other threads executing queries.
+     * </p><p>
+     * If the database has any attached databases, then execution of queries in parallel is NOT
+     * possible.  Likewise, write-ahead logging is not supported for read-only databases
+     * or memory databases.  In such cases, {@code enableWriteAheadLogging} returns false.
+     * </p><p>
+     * The best way to enable write-ahead logging is to pass the
+     * {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag to
+     * {@link SQLiteDatabase#openDatabase}.  This is more efficient than calling
+     * <code><pre>
+     *     SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+     *             SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
+     *             myDatabaseErrorHandler);
+     *     db.enableWriteAheadLogging();
+     * </pre></code>
+     * </p><p>
+     * Another way to enable write-ahead logging is to call {@code enableWriteAheadLogging}
+     * after opening the database.
+     * <code><pre>
+     *     SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+     *             SQLiteDatabase.CREATE_IF_NECESSARY, myDatabaseErrorHandler);
+     *     db.enableWriteAheadLogging();
+     * </pre></code>
+     * </p><p>
+     * See also <a href="http://sqlite.org/wal.html">SQLite Write-Ahead Logging</a> for
+     * more details about how write-ahead logging works.
+     * </p>
+     *
+     * @return True if write-ahead logging is enabled.
+     * @throws IllegalStateException if there are transactions in progress at the
+     *                               time this method is called.  WAL mode can only be changed when
+     *                               there are no
+     *                               transactions in progress.
+     * @see SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING
+     * @see #disableWriteAheadLogging
+     */
+    boolean enableWriteAheadLogging();
+
+    /**
+     * This method disables the features enabled by {@link #enableWriteAheadLogging()}.
+     *
+     * @throws IllegalStateException if there are transactions in progress at the
+     *                               time this method is called.  WAL mode can only be changed when
+     *                               there are no
+     *                               transactions in progress.
+     * @see #enableWriteAheadLogging
+     */
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    void disableWriteAheadLogging();
+
+    /**
+     * Returns true if write-ahead logging has been enabled for this database.
+     *
+     * @return True if write-ahead logging has been enabled for this database.
+     * @see #enableWriteAheadLogging
+     * @see SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING
+     */
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    boolean isWriteAheadLoggingEnabled();
+
+    /**
+     * Returns list of full path names of all attached databases including the main database
+     * by executing 'pragma database_list' on the database.
+     *
+     * @return ArrayList of pairs of (database name, database file path) or null if the database
+     * is not open.
+     */
+    List<Pair<String, String>> getAttachedDbs();
+
+    /**
+     * Runs 'pragma integrity_check' on the given database (and all the attached databases)
+     * and returns true if the given database (and all its attached databases) pass integrity_check,
+     * false otherwise.
+     * <p>
+     * If the result is false, then this method logs the errors reported by the integrity_check
+     * command execution.
+     * <p>
+     * Note that 'pragma integrity_check' on a database can take a long time.
+     *
+     * @return true if the given database (and all its attached databases) pass integrity_check,
+     * false otherwise.
+     */
+    boolean isDatabaseIntegrityOk();
+}
diff --git a/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteOpenHelper.java b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteOpenHelper.java
new file mode 100644
index 0000000..5a96e5a
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteOpenHelper.java
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.DefaultDatabaseErrorHandler;
+import android.database.sqlite.SQLiteException;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+
+/**
+ * An interface to map the behavior of {@link android.database.sqlite.SQLiteOpenHelper}.
+ * Note that since that class requires overriding certain methods, support implementation
+ * uses {@link Factory#create(Configuration)} to create this and {@link Callback} to implement
+ * the methods that should be overridden.
+ */
+@SuppressWarnings("unused")
+public interface SupportSQLiteOpenHelper {
+    /**
+     * Return the name of the SQLite database being opened, as given to
+     * the constructor.
+     */
+    String getDatabaseName();
+
+    /**
+     * Enables or disables the use of write-ahead logging for the database.
+     *
+     * Write-ahead logging cannot be used with read-only databases so the value of
+     * this flag is ignored if the database is opened read-only.
+     *
+     * @param enabled True if write-ahead logging should be enabled, false if it
+     *                should be disabled.
+     * @see SupportSQLiteDatabase#enableWriteAheadLogging()
+     */
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    void setWriteAheadLoggingEnabled(boolean enabled);
+
+    /**
+     * Create and/or open a database that will be used for reading and writing.
+     * The first time this is called, the database will be opened and
+     * {@link Callback#onCreate}, {@link Callback#onUpgrade} and/or {@link Callback#onOpen} will be
+     * called.
+     *
+     * <p>Once opened successfully, the database is cached, so you can
+     * call this method every time you need to write to the database.
+     * (Make sure to call {@link #close} when you no longer need the database.)
+     * Errors such as bad permissions or a full disk may cause this method
+     * to fail, but future attempts may succeed if the problem is fixed.</p>
+     *
+     * <p class="caution">Database upgrade may take a long time, you
+     * should not call this method from the application main thread, including
+     * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
+     *
+     * @return a read/write database object valid until {@link #close} is called
+     * @throws SQLiteException if the database cannot be opened for writing
+     */
+    SupportSQLiteDatabase getWritableDatabase();
+
+    /**
+     * Create and/or open a database.  This will be the same object returned by
+     * {@link #getWritableDatabase} unless some problem, such as a full disk,
+     * requires the database to be opened read-only.  In that case, a read-only
+     * database object will be returned.  If the problem is fixed, a future call
+     * to {@link #getWritableDatabase} may succeed, in which case the read-only
+     * database object will be closed and the read/write object will be returned
+     * in the future.
+     *
+     * <p class="caution">Like {@link #getWritableDatabase}, this method may
+     * take a long time to return, so you should not call it from the
+     * application main thread, including from
+     * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
+     *
+     * @return a database object valid until {@link #getWritableDatabase}
+     * or {@link #close} is called.
+     * @throws SQLiteException if the database cannot be opened
+     */
+    SupportSQLiteDatabase getReadableDatabase();
+
+    /**
+     * Close any open database object.
+     */
+    void close();
+
+    /**
+     * Matching callback methods from {@link android.database.sqlite.SQLiteOpenHelper}.
+     */
+    @SuppressWarnings({"unused", "WeakerAccess"})
+    abstract class Callback {
+        /**
+         * Called when the database connection is being configured, to enable features such as
+         * write-ahead logging or foreign key support.
+         * <p>
+         * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade},
+         * or {@link #onOpen} are called. It should not modify the database except to configure the
+         * database connection as required.
+         * </p>
+         * <p>
+         * This method should only call methods that configure the parameters of the database
+         * connection, such as {@link SupportSQLiteDatabase#enableWriteAheadLogging}
+         * {@link SupportSQLiteDatabase#setForeignKeyConstraintsEnabled},
+         * {@link SupportSQLiteDatabase#setLocale},
+         * {@link SupportSQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
+         * </p>
+         *
+         * @param db The database.
+         */
+        public void onConfigure(SupportSQLiteDatabase db) {
+
+        }
+
+        /**
+         * Called when the database is created for the first time. This is where the
+         * creation of tables and the initial population of the tables should happen.
+         *
+         * @param db The database.
+         */
+        public abstract void onCreate(SupportSQLiteDatabase db);
+
+        /**
+         * Called when the database needs to be upgraded. The implementation
+         * should use this method to drop tables, add tables, or do anything else it
+         * needs to upgrade to the new schema version.
+         *
+         * <p>
+         * The SQLite ALTER TABLE documentation can be found
+         * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
+         * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
+         * you can use ALTER TABLE to rename the old table, then create the new table and then
+         * populate the new table with the contents of the old table.
+         * </p><p>
+         * This method executes within a transaction.  If an exception is thrown, all changes
+         * will automatically be rolled back.
+         * </p>
+         *
+         * @param db         The database.
+         * @param oldVersion The old database version.
+         * @param newVersion The new database version.
+         */
+        public abstract void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion);
+
+        /**
+         * Called when the database needs to be downgraded. This is strictly similar to
+         * {@link #onUpgrade} method, but is called whenever current version is newer than requested
+         * one.
+         * However, this method is not abstract, so it is not mandatory for a customer to
+         * implement it. If not overridden, default implementation will reject downgrade and
+         * throws SQLiteException
+         *
+         * <p>
+         * This method executes within a transaction.  If an exception is thrown, all changes
+         * will automatically be rolled back.
+         * </p>
+         *
+         * @param db         The database.
+         * @param oldVersion The old database version.
+         * @param newVersion The new database version.
+         */
+        public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
+            throw new SQLiteException("Can't downgrade database from version "
+                    + oldVersion + " to " + newVersion);
+        }
+
+        /**
+         * Called when the database has been opened.  The implementation
+         * should check {@link SupportSQLiteDatabase#isReadOnly} before updating the
+         * database.
+         * <p>
+         * This method is called after the database connection has been configured
+         * and after the database schema has been created, upgraded or downgraded as necessary.
+         * If the database connection must be configured in some way before the schema
+         * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
+         * </p>
+         *
+         * @param db The database.
+         */
+        public void onOpen(SupportSQLiteDatabase db) {
+
+        }
+    }
+
+    /**
+     * The configuration to create an SQLite open helper object using {@link Factory}.
+     */
+    @SuppressWarnings("WeakerAccess")
+    class Configuration {
+        /**
+         * Context to use to open or create the database.
+         */
+        @NonNull
+        public final Context context;
+        /**
+         * Name of the database file, or null for an in-memory database.
+         */
+        @Nullable
+        public final String name;
+        /**
+         * Version number of the database (starting at 1); if the database is older,
+         * {@link SupportSQLiteOpenHelper.Callback#onUpgrade(SupportSQLiteDatabase, int, int)}
+         * will be used to upgrade the database; if the database is newer,
+         * {@link SupportSQLiteOpenHelper.Callback#onDowngrade(SupportSQLiteDatabase, int, int)}
+         * will be used to downgrade the database.
+         */
+        public final int version;
+        /**
+         * The callback class to handle creation, upgrade and downgrade.
+         */
+        @NonNull
+        public final SupportSQLiteOpenHelper.Callback callback;
+        /**
+         * The {@link DatabaseErrorHandler} to be used when sqlite reports database
+         * corruption, or null to use the default error handler.
+         */
+        @Nullable
+        public final DatabaseErrorHandler errorHandler;
+
+        Configuration(@NonNull Context context, @Nullable String name,
+                int version, @Nullable DatabaseErrorHandler errorHandler,
+                @NonNull Callback callback) {
+            this.context = context;
+            this.name = name;
+            this.version = version;
+            this.callback = callback;
+            this.errorHandler = errorHandler;
+        }
+
+        /**
+         * Creates a new Configuration.Builder to create an instance of Configuration.
+         *
+         * @param context to use to open or create the database.
+         */
+        public static Builder builder(Context context) {
+            return new Builder(context);
+        }
+
+        /**
+         * Builder class for {@link Configuration}.
+         */
+        public static class Builder {
+            Context mContext;
+            String mName;
+            int mVersion = 1;
+            SupportSQLiteOpenHelper.Callback mCallback;
+            DatabaseErrorHandler mErrorHandler;
+
+            public Configuration build() {
+                if (mCallback == null) {
+                    throw new IllegalArgumentException("Must set a callback to create the"
+                            + " configuration.");
+                }
+                if (mContext == null) {
+                    throw new IllegalArgumentException("Must set a non-null context to create"
+                            + " the configuration.");
+                }
+                if (mErrorHandler == null) {
+                    mErrorHandler = new DefaultDatabaseErrorHandler();
+                }
+                return new Configuration(mContext, mName, mVersion, mErrorHandler,
+                        mCallback);
+            }
+
+            Builder(@NonNull Context context) {
+                mContext = context;
+            }
+
+            /**
+             * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite
+             *                     reports database corruption, or null to use the default error
+             *                     handler.
+             * @return This
+             */
+            public Builder errorHandler(@Nullable DatabaseErrorHandler errorHandler) {
+                mErrorHandler = errorHandler;
+                return this;
+            }
+
+            /**
+             * @param name Name of the database file, or null for an in-memory database.
+             * @return This
+             */
+            public Builder name(@Nullable String name) {
+                mName = name;
+                return this;
+            }
+
+            /**
+             * @param callback The callback class to handle creation, upgrade and downgrade.
+             * @return this
+             */
+            public Builder callback(@NonNull Callback callback) {
+                mCallback = callback;
+                return this;
+            }
+
+            /**
+             * @param version Version number of the database (starting at 1); if the database is
+             * older,
+             * {@link SupportSQLiteOpenHelper.Callback#onUpgrade(SupportSQLiteDatabase, int, int)}
+             * will be used to upgrade the database; if the database is newer,
+             * {@link SupportSQLiteOpenHelper.Callback#onDowngrade(SupportSQLiteDatabase, int, int)}
+             * will be used to downgrade the database.
+             * @return this
+             */
+            public Builder version(int version) {
+                mVersion = version;
+                return this;
+            }
+        }
+    }
+
+    /**
+     * Factory class to create instances of {@link SupportSQLiteOpenHelper} using
+     * {@link Configuration}.
+     */
+    interface Factory {
+        /**
+         * Creates an instance of {@link SupportSQLiteOpenHelper} using the given configuration.
+         *
+         * @param configuration The configuration to use while creating the open helper.
+         *
+         * @return A SupportSQLiteOpenHelper which can be used to open a database.
+         */
+        SupportSQLiteOpenHelper create(Configuration configuration);
+    }
+}
diff --git a/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteProgram.java b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteProgram.java
new file mode 100644
index 0000000..c6d43cc
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteProgram.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+
+/**
+ * An interface to map the behavior of {@link android.database.sqlite.SQLiteProgram}.
+ */
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+@SuppressWarnings("unused")
+public interface SupportSQLiteProgram extends AutoCloseable {
+    /**
+     * Bind a NULL value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind null to
+     */
+    void bindNull(int index);
+
+    /**
+     * Bind a long value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *addToBindArgs
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind
+     */
+    void bindLong(int index, long value);
+
+    /**
+     * Bind a double value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind
+     */
+    void bindDouble(int index, double value);
+
+    /**
+     * Bind a String value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind, must not be null
+     */
+    void bindString(int index, String value);
+
+    /**
+     * Bind a byte array value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind, must not be null
+     */
+    void bindBlob(int index, byte[] value);
+
+    /**
+     * Clears all existing bindings. Unset bindings are treated as NULL.
+     */
+    void clearBindings();
+}
diff --git a/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java
new file mode 100644
index 0000000..2007634
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+/**
+ * A query with typed bindings. It is better to use this API instead of
+ * {@link android.database.sqlite.SQLiteDatabase#rawQuery(String, String[])} because it allows
+ * binding type safe parameters.
+ */
+public interface SupportSQLiteQuery {
+    /**
+     * The SQL query. This query can have placeholders(?) for bind arguments.
+     *
+     * @return The SQL query to compile
+     */
+    String getSql();
+
+    /**
+     * Callback to bind the query parameters to the compiled statement.
+     *
+     * @param statement The compiled statement
+     */
+    void bindTo(SupportSQLiteProgram statement);
+}
diff --git a/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteQueryBuilder.java b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
new file mode 100644
index 0000000..183fb0a
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+import java.util.regex.Pattern;
+
+/**
+ * A simple query builder to create SQL SELECT queries.
+ */
+@SuppressWarnings("unused")
+public class SupportSQLiteQueryBuilder {
+    private static final Pattern sLimitPattern =
+            Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
+
+    private boolean mDistinct = false;
+    private final String mTable;
+    private String[] mColumns = null;
+    private String mSelection;
+    private Object[] mBindArgs;
+    private String mGroupBy = null;
+    private String mHaving = null;
+    private String mOrderBy = null;
+    private String mLimit = null;
+
+    /**
+     * Creates a query for the given table name.
+     *
+     * @param tableName The table name(s) to query.
+     *
+     * @return A builder to create a query.
+     */
+    public static SupportSQLiteQueryBuilder builder(String tableName) {
+        return new SupportSQLiteQueryBuilder(tableName);
+    }
+
+    private SupportSQLiteQueryBuilder(String table) {
+        mTable = table;
+    }
+
+    /**
+     * Adds DISTINCT keyword to the query.
+     *
+     * @return this
+     */
+    public SupportSQLiteQueryBuilder distinct() {
+        mDistinct = true;
+        return this;
+    }
+
+    /**
+     * Sets the given list of columns as the columns that will be returned.
+     *
+     * @param columns The list of column names that should be returned.
+     *
+     * @return this
+     */
+    public SupportSQLiteQueryBuilder columns(String[] columns) {
+        mColumns = columns;
+        return this;
+    }
+
+    /**
+     * Sets the arguments for the WHERE clause.
+     *
+     * @param selection The list of selection columns
+     * @param bindArgs The list of bind arguments to match against these columns
+     *
+     * @return this
+     */
+    public SupportSQLiteQueryBuilder selection(String selection, Object[] bindArgs) {
+        mSelection = selection;
+        mBindArgs = bindArgs;
+        return this;
+    }
+
+    /**
+     * Adds a GROUP BY statement.
+     *
+     * @param groupBy The value of the GROUP BY statement.
+     *
+     * @return this
+     */
+    @SuppressWarnings("WeakerAccess")
+    public SupportSQLiteQueryBuilder groupBy(String groupBy) {
+        mGroupBy = groupBy;
+        return this;
+    }
+
+    /**
+     * Adds a HAVING statement. You must also provide {@link #groupBy(String)} for this to work.
+     *
+     * @param having The having clause.
+     *
+     * @return this
+     */
+    public SupportSQLiteQueryBuilder having(String having) {
+        mHaving = having;
+        return this;
+    }
+
+    /**
+     * Adds an ORDER BY statement.
+     *
+     * @param orderBy The order clause.
+     *
+     * @return this
+     */
+    public SupportSQLiteQueryBuilder orderBy(String orderBy) {
+        mOrderBy = orderBy;
+        return this;
+    }
+
+    /**
+     * Adds a LIMIT statement.
+     *
+     * @param limit The limit value.
+     *
+     * @return this
+     */
+    public SupportSQLiteQueryBuilder limit(String limit) {
+        if (!isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
+            throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
+        }
+        mLimit = limit;
+        return this;
+    }
+
+    /**
+     * Creates the {@link SupportSQLiteQuery} that can be passed into
+     * {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.
+     *
+     * @return a new query
+     */
+    public SupportSQLiteQuery create() {
+        if (isEmpty(mGroupBy) && !isEmpty(mHaving)) {
+            throw new IllegalArgumentException(
+                    "HAVING clauses are only permitted when using a groupBy clause");
+        }
+        StringBuilder query = new StringBuilder(120);
+
+        query.append("SELECT ");
+        if (mDistinct) {
+            query.append("DISTINCT ");
+        }
+        if (mColumns != null && mColumns.length != 0) {
+            appendColumns(query, mColumns);
+        } else {
+            query.append(" * ");
+        }
+        query.append(" FROM ");
+        query.append(mTable);
+        appendClause(query, " WHERE ", mSelection);
+        appendClause(query, " GROUP BY ", mGroupBy);
+        appendClause(query, " HAVING ", mHaving);
+        appendClause(query, " ORDER BY ", mOrderBy);
+        appendClause(query, " LIMIT ", mLimit);
+
+        return new SimpleSQLiteQuery(query.toString(), mBindArgs);
+    }
+
+    private static void appendClause(StringBuilder s, String name, String clause) {
+        if (!isEmpty(clause)) {
+            s.append(name);
+            s.append(clause);
+        }
+    }
+
+    /**
+     * Add the names that are non-null in columns to s, separating
+     * them with commas.
+     */
+    private static void appendColumns(StringBuilder s, String[] columns) {
+        int n = columns.length;
+
+        for (int i = 0; i < n; i++) {
+            String column = columns[i];
+            if (i > 0) {
+                s.append(", ");
+            }
+            s.append(column);
+        }
+        s.append(' ');
+    }
+
+    private static boolean isEmpty(String input) {
+        return input == null || input.length() == 0;
+    }
+}
diff --git a/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteStatement.java b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteStatement.java
new file mode 100644
index 0000000..5a329d7
--- /dev/null
+++ b/room/db/src/main/java/android/arch/persistence/db/SupportSQLiteStatement.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.db;
+
+/**
+ * An interface to map the behavior of {@link android.database.sqlite.SQLiteStatement}.
+ */
+@SuppressWarnings("unused")
+public interface SupportSQLiteStatement extends SupportSQLiteProgram {
+    /**
+     * Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
+     * CREATE / DROP table, view, trigger, index etc.
+     *
+     * @throws android.database.SQLException If the SQL string is invalid for
+     *         some reason
+     */
+    void execute();
+
+    /**
+     * Execute this SQL statement, if the the number of rows affected by execution of this SQL
+     * statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements.
+     *
+     * @return the number of rows affected by this SQL statement execution.
+     * @throws android.database.SQLException If the SQL string is invalid for
+     *         some reason
+     */
+    int executeUpdateDelete();
+
+    /**
+     * Execute this SQL statement and return the ID of the row inserted due to this call.
+     * The SQL statement should be an INSERT for this to be a useful call.
+     *
+     * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
+     *
+     * @throws android.database.SQLException If the SQL string is invalid for
+     *         some reason
+     */
+    long executeInsert();
+
+    /**
+     * Execute a statement that returns a 1 by 1 table with a numeric value.
+     * For example, SELECT COUNT(*) FROM table;
+     *
+     * @return The result of the query.
+     *
+     * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
+     */
+    long simpleQueryForLong();
+    /**
+     * Execute a statement that returns a 1 by 1 table with a text value.
+     * For example, SELECT COUNT(*) FROM table;
+     *
+     * @return The result of the query.
+     *
+     * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
+     */
+    String simpleQueryForString();
+}
diff --git a/room/gradle/wrapper/gradle-wrapper.jar b/room/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..d6e2637
--- /dev/null
+++ b/room/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/room/gradle/wrapper/gradle-wrapper.properties b/room/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a5292b0
--- /dev/null
+++ b/room/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Dec 09 13:07:04 PST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-bin.zip
diff --git a/room/gradlew b/room/gradlew
new file mode 100755
index 0000000..4ef3a87
--- /dev/null
+++ b/room/gradlew
@@ -0,0 +1,171 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+for s in "${@}" ; do
+    s=\"$s\"
+    APP_ARGS=$APP_ARGS" "$s
+done
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- "$DEFAULT_JVM_OPTS" "$JAVA_OPTS" "$GRADLE_OPTS" "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/room/gradlew.bat b/room/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/room/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windows variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/room/integration-tests/kotlintestapp/.gitignore b/room/integration-tests/kotlintestapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
new file mode 100644
index 0000000..0d70666
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-android'
+
+project.ext.noDocs = true
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+            }
+        }
+    }
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+    sourceSets {
+        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
+    }
+}
+
+dependencies {
+    implementation project(":room:common")
+    implementation project(":room:db")
+    implementation project(":room:db-impl")
+    implementation project(':room:runtime')
+    implementation project(':arch:runtime')
+
+    implementation libs.support.app_compat
+    kapt project(":room:compiler")
+    kaptAndroidTest project(":room:compiler")
+
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+        exclude module: 'hamcrest-core'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+        exclude module: "hamcrest-core"
+    })
+    // IJ's gradle integration just cannot figure this out ...
+    androidTestImplementation project(':lifecycle:extensions')
+    androidTestImplementation project(':lifecycle:common')
+    androidTestImplementation project(':lifecycle:runtime')
+    androidTestImplementation project(':room:testing')
+    androidTestImplementation project(':room:rxjava2')
+    androidTestImplementation project(':arch:core-testing')
+    androidTestImplementation libs.rx_java
+    testImplementation libs.mockito_core
+}
+
+createAndroidCheckstyle(project)
+createKotlinCheckstyle(project)
+tasks['check'].dependsOn(tasks['connectedCheck'])
+
+uploadArchives.enabled = false
diff --git a/room/integration-tests/kotlintestapp/proguard-rules.pro b/room/integration-tests/kotlintestapp/proguard-rules.pro
new file mode 100644
index 0000000..d50f27e
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/florinam/Work/Coding/support/ub-supportlib-26.0/prebuilts/fullsdk-darwin/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.BooksDatabase/1.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.BooksDatabase/1.json
new file mode 100644
index 0000000..dd056e2
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.BooksDatabase/1.json
@@ -0,0 +1,159 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "64fa560604c57044726b190dadbd8258",
+    "entities": [
+      {
+        "tableName": "Book",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookId` TEXT NOT NULL, `title` TEXT NOT NULL, `bookPublisherId` TEXT NOT NULL, PRIMARY KEY(`bookId`), FOREIGN KEY(`bookPublisherId`) REFERENCES `Publisher`(`publisherId`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "bookId",
+            "columnName": "bookId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "title",
+            "columnName": "title",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "bookPublisherId",
+            "columnName": "bookPublisherId",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "bookId"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": [
+          {
+            "table": "Publisher",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "bookPublisherId"
+            ],
+            "referencedColumns": [
+              "publisherId"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Author",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`authorId` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`authorId`))",
+        "fields": [
+          {
+            "fieldPath": "authorId",
+            "columnName": "authorId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "authorId"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Publisher",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`publisherId` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`publisherId`))",
+        "fields": [
+          {
+            "fieldPath": "publisherId",
+            "columnName": "publisherId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "publisherId"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "BookAuthor",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookId` TEXT NOT NULL, `authorId` TEXT NOT NULL, PRIMARY KEY(`bookId`, `authorId`), FOREIGN KEY(`bookId`) REFERENCES `Book`(`bookId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`authorId`) REFERENCES `Author`(`authorId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "bookId",
+            "columnName": "bookId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "authorId",
+            "columnName": "authorId",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "bookId",
+            "authorId"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": [
+          {
+            "table": "Book",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "bookId"
+            ],
+            "referencedColumns": [
+              "bookId"
+            ]
+          },
+          {
+            "table": "Author",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "authorId"
+            ],
+            "referencedColumns": [
+              "authorId"
+            ]
+          }
+        ]
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"64fa560604c57044726b190dadbd8258\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/1.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/1.json
new file mode 100644
index 0000000..e05f095
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/1.json
@@ -0,0 +1,38 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "2f3557e56d7f665363f3e20d14787a59",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"2f3557e56d7f665363f3e20d14787a59\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/2.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/2.json
new file mode 100644
index 0000000..8982d4f
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/2.json
@@ -0,0 +1,63 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 2,
+    "identityHash": "aee9a6eed720c059df0f2ee0d6e96d89",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"aee9a6eed720c059df0f2ee0d6e96d89\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/3.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/3.json
new file mode 100644
index 0000000..3bf0f03
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/3.json
@@ -0,0 +1,69 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 3,
+    "identityHash": "3f2a99b6d768af0184e077808f7348fe",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `addedInV3` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3f2a99b6d768af0184e077808f7348fe\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/4.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/4.json
new file mode 100644
index 0000000..e899a02
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/4.json
@@ -0,0 +1,100 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 4,
+    "identityHash": "abbae5f17d94ff7c2c7e05ca217ccc31",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `addedInV3` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `removedInV5` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "removedInV5",
+            "columnName": "removedInV5",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"abbae5f17d94ff7c2c7e05ca217ccc31\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/5.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/5.json
new file mode 100644
index 0000000..801fdb5
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/5.json
@@ -0,0 +1,94 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 5,
+    "identityHash": "5543c44fe679f4cf8f03093d66838068",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `addedInV3` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"5543c44fe679f4cf8f03093d66838068\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/6.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/6.json
new file mode 100644
index 0000000..a6fdf5b
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/6.json
@@ -0,0 +1,69 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 6,
+    "identityHash": "3f2a99b6d768af0184e077808f7348fe",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `addedInV3` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3f2a99b6d768af0184e077808f7348fe\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/7.json b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/7.json
new file mode 100644
index 0000000..78ce189
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/android.arch.persistence.room.integration.kotlintestapp.migration.MigrationDbKotlin/7.json
@@ -0,0 +1,118 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 7,
+    "identityHash": "5653c29453937d8e34dc031af1ab4c7d",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [
+          {
+            "name": "index_Entity1_name",
+            "unique": true,
+            "columnNames": [
+              "name"
+            ],
+            "createSql": "CREATE UNIQUE INDEX `index_Entity1_name` ON `${TABLE_NAME}` (`name`)"
+          }
+        ],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `addedInV3` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Entity4",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": [
+          {
+            "table": "Entity1",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "name"
+            ],
+            "referencedColumns": [
+              "name"
+            ]
+          }
+        ]
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"5653c29453937d8e34dc031af1ab4c7d\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/BooksDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/BooksDao.kt
new file mode 100644
index 0000000..6dc0276
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/BooksDao.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp
+
+import android.arch.lifecycle.LiveData
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.integration.kotlintestapp.vo.*
+import io.reactivex.Flowable
+
+@Dao
+interface BooksDao {
+
+    @Insert
+    fun addPublishers(vararg publishers: Publisher)
+
+    @Insert
+    fun addAuthors(vararg authors: Author)
+
+    @Insert
+    fun addBooks(vararg books: Book)
+
+    @Insert
+    fun addBookAuthors(vararg bookAuthors: BookAuthor)
+
+    @Query("SELECT * FROM book WHERE bookId = :bookId")
+    fun getBook(bookId: String): Book
+
+    @Query("SELECT * FROM book WHERE bookId = :bookId")
+    fun getBookLiveData(bookId: String): LiveData<Book>
+
+    @Query("SELECT * FROM book WHERE bookId = :bookId")
+    fun getBookFlowable(bookId: String): Flowable<Book>
+
+    @Query("SELECT * FROM book INNER JOIN publisher " +
+            "ON book.bookPublisherId = publisher.publisherId ")
+    fun getBooksWithPublisher(): List<BookWithPublisher>
+
+    @Query("SELECT * FROM book INNER JOIN publisher " +
+            "ON book.bookPublisherId = publisher.publisherId ")
+    fun getBooksWithPublisherLiveData(): LiveData<List<BookWithPublisher>>
+
+    @Query("SELECT * FROM book INNER JOIN publisher " +
+            "ON book.bookPublisherId = publisher.publisherId ")
+    fun getBooksWithPublisherFlowable(): Flowable<List<BookWithPublisher>>
+
+    @Query("SELECT * FROM publisher WHERE publisherId = :publisherId")
+    fun getPublisherWithBooks(publisherId: String): PublisherWithBooks
+
+    @Query("SELECT * FROM publisher WHERE publisherId = :publisherId")
+    fun getPublisherWithBooksLiveData(publisherId: String): LiveData<PublisherWithBooks>
+
+    @Query("SELECT * FROM publisher WHERE publisherId = :publisherId")
+    fun getPublisherWithBooksFlowable(publisherId: String): Flowable<PublisherWithBooks>
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/BooksDatabase.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/BooksDatabase.kt
new file mode 100644
index 0000000..b5b6f26
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/BooksDatabase.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp
+
+import android.arch.persistence.room.Database
+import android.arch.persistence.room.RoomDatabase
+import android.arch.persistence.room.integration.kotlintestapp.vo.Author
+import android.arch.persistence.room.integration.kotlintestapp.vo.Book
+import android.arch.persistence.room.integration.kotlintestapp.vo.BookAuthor
+import android.arch.persistence.room.integration.kotlintestapp.vo.Publisher
+
+@Database(entities = arrayOf(Book::class, Author::class, Publisher::class, BookAuthor::class),
+        version = 1)
+abstract class BooksDatabase : RoomDatabase() {
+
+    abstract fun booksDao(): BooksDao
+
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/migration/MigrationDbKotlin.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/migration/MigrationDbKotlin.kt
new file mode 100644
index 0000000..67374a5
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/migration/MigrationDbKotlin.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.migration
+
+import android.arch.persistence.db.SupportSQLiteDatabase
+import android.arch.persistence.room.Dao
+import android.arch.persistence.room.Database
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.ForeignKey
+import android.arch.persistence.room.Ignore
+import android.arch.persistence.room.Index
+import android.arch.persistence.room.Insert
+import android.arch.persistence.room.PrimaryKey
+import android.arch.persistence.room.Query
+import android.arch.persistence.room.RoomDatabase
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+
+@Database(version = MigrationDbKotlin.LATEST_VERSION,
+        entities = arrayOf(MigrationDbKotlin.Entity1::class, MigrationDbKotlin.Entity2::class,
+                MigrationDbKotlin.Entity4::class))
+abstract class MigrationDbKotlin : RoomDatabase() {
+
+    internal abstract fun dao(): MigrationDao
+
+    @Entity(indices = arrayOf(Index(value = "name", unique = true)))
+    data class Entity1(@PrimaryKey var id: Int = 0, var name: String?) {
+
+        companion object {
+            val TABLE_NAME = "Entity1"
+        }
+    }
+
+    @Entity
+    open class Entity2(@PrimaryKey var id: Int = 0, var addedInV3: String?, var name: String?) {
+        companion object {
+            val TABLE_NAME = "Entity2"
+        }
+    }
+
+    @Entity
+    data class Entity3(@PrimaryKey var id: Int = 0, @Ignore var removedInV5: String?,
+                       var name: String?) { // added in version 4, removed at 6
+        companion object {
+            val TABLE_NAME = "Entity3"
+        }
+    }
+
+    @Entity(foreignKeys = arrayOf(ForeignKey(entity = Entity1::class,
+            parentColumns = arrayOf("name"),
+            childColumns = arrayOf("name"),
+            deferred = true)))
+    data class Entity4(@PrimaryKey var id: Int = 0, var name: String?) {
+        companion object {
+            val TABLE_NAME = "Entity4"
+        }
+    }
+
+    @Dao
+    internal interface MigrationDao {
+        @Query("SELECT * from Entity1 ORDER BY id ASC")
+        fun loadAllEntity1s(): List<Entity1>
+
+        @Query("SELECT * from Entity2 ORDER BY id ASC")
+        fun loadAllEntity2s(): List<Entity2>
+
+        @Query("SELECT * from Entity2 ORDER BY id ASC")
+        fun loadAllEntity2sAsPojo(): List<Entity2Pojo>
+
+        @Insert
+        fun insert(vararg entity2: Entity2)
+    }
+
+    internal class Entity2Pojo(id: Int, addedInV3: String?, name: String?)
+        : Entity2(id, addedInV3, name)
+
+    /**
+     * not a real dao because database will change.
+     */
+    internal class Dao_V1(val mDb: SupportSQLiteDatabase) {
+
+        fun insertIntoEntity1(id: Int, name: String) {
+            val values = ContentValues()
+            values.put("id", id)
+            values.put("name", name)
+            val insertionId = mDb.insert(Entity1.TABLE_NAME,
+                    SQLiteDatabase.CONFLICT_REPLACE, values)
+            if (insertionId == -1L) {
+                throw RuntimeException("test sanity failure")
+            }
+        }
+    }
+
+    /**
+     * not a real dao because database will change.
+     */
+    internal class Dao_V2(val mDb: SupportSQLiteDatabase) {
+
+        fun insertIntoEntity2(id: Int, name: String) {
+            val values = ContentValues()
+            values.put("id", id)
+            values.put("name", name)
+            val insertionId = mDb.insert(Entity2.TABLE_NAME,
+                    SQLiteDatabase.CONFLICT_REPLACE, values)
+            if (insertionId == -1L) {
+                throw RuntimeException("test sanity failure")
+            }
+        }
+    }
+
+    companion object {
+        const val LATEST_VERSION = 7
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
new file mode 100644
index 0000000..eb1a9b8
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.migration
+
+import android.arch.persistence.db.SupportSQLiteDatabase
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory
+import android.arch.persistence.room.Room
+import android.arch.persistence.room.migration.Migration
+import android.arch.persistence.room.testing.MigrationTestHelper
+import android.arch.persistence.room.util.TableInfo
+import android.support.test.InstrumentationRegistry
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.containsString
+import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Rule
+import org.junit.Test
+import java.io.FileNotFoundException
+import java.io.IOException
+
+
+class MigrationKotlinTest {
+
+    @get:Rule
+    var helper: MigrationTestHelper = MigrationTestHelper(
+            InstrumentationRegistry.getInstrumentation(),
+            MigrationDbKotlin::class.java.canonicalName,
+            FrameworkSQLiteOpenHelperFactory())
+
+    companion object {
+        val TEST_DB = "migration-test"
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun giveBadResource() {
+        val helper = MigrationTestHelper(
+                InstrumentationRegistry.getInstrumentation(),
+                "foo", FrameworkSQLiteOpenHelperFactory())
+        try {
+            helper.createDatabase(TEST_DB, 1)
+            throw AssertionError("must have failed with missing file exception")
+        } catch (exception: FileNotFoundException) {
+            assertThat<String>(exception.message, containsString("Cannot find"))
+        }
+
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun startInCurrentVersion() {
+        val db = helper.createDatabase(TEST_DB,
+                MigrationDbKotlin.LATEST_VERSION)
+        val dao = MigrationDbKotlin.Dao_V1(db)
+        dao.insertIntoEntity1(2, "x")
+        db.close()
+        val migrationDb = getLatestDb()
+        val items = migrationDb.dao().loadAllEntity1s()
+        helper.closeWhenFinished(migrationDb)
+        assertThat<Int>(items.size, `is`<Int>(1))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun addTable() {
+        var db = helper.createDatabase(TEST_DB, 1)
+        val dao = MigrationDbKotlin.Dao_V1(db)
+        dao.insertIntoEntity1(2, "foo")
+        dao.insertIntoEntity1(3, "bar")
+        db.close()
+        db = helper.runMigrationsAndValidate(TEST_DB, 2, true,
+                MIGRATION_1_2)
+        MigrationDbKotlin.Dao_V2(db).insertIntoEntity2(3, "blah")
+        db.close()
+        val migrationDb = getLatestDb()
+        val entity1s = migrationDb.dao().loadAllEntity1s()
+
+        assertThat(entity1s.size, `is`(2))
+        val entity2 = MigrationDbKotlin.Entity2(2, null, "bar")
+        // assert no error happens
+        migrationDb.dao().insert(entity2)
+        val entity2s = migrationDb.dao().loadAllEntity2s()
+        assertThat(entity2s.size, `is`(2))
+    }
+
+    private fun getLatestDb(): MigrationDbKotlin {
+        val db = Room.databaseBuilder(
+                InstrumentationRegistry.getInstrumentation().targetContext,
+                MigrationDbKotlin::class.java, TEST_DB).addMigrations(*ALL_MIGRATIONS).build()
+        // trigger open
+        db.beginTransaction()
+        db.endTransaction()
+        helper.closeWhenFinished(db)
+        return db
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun addTableFailure() {
+        testFailure(1, 2)
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun addColumnFailure() {
+        val db = helper.createDatabase(TEST_DB, 2)
+        db.close()
+        var caught: IllegalStateException? = null
+        try {
+            helper.runMigrationsAndValidate(TEST_DB, 3, true,
+                    EmptyMigration(2, 3))
+        } catch (ex: IllegalStateException) {
+            caught = ex
+        }
+
+        assertThat<IllegalStateException>(caught,
+                instanceOf<IllegalStateException>(IllegalStateException::class.java))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun addColumn() {
+        val db = helper.createDatabase(TEST_DB, 2)
+        val v2Dao = MigrationDbKotlin.Dao_V2(db)
+        v2Dao.insertIntoEntity2(7, "blah")
+        db.close()
+        helper.runMigrationsAndValidate(TEST_DB, 3, true, MIGRATION_2_3)
+        // trigger open.
+        val migrationDb = getLatestDb()
+        val entity2s = migrationDb.dao().loadAllEntity2s()
+        assertThat(entity2s.size, `is`(1))
+        assertThat<String>(entity2s[0].name, `is`("blah"))
+        assertThat<String>(entity2s[0].addedInV3, `is`<Any>(nullValue()))
+
+        val entity2Pojos = migrationDb.dao().loadAllEntity2sAsPojo()
+        assertThat(entity2Pojos.size, `is`(1))
+        assertThat<String>(entity2Pojos[0].name, `is`("blah"))
+        assertThat<String>(entity2Pojos[0].addedInV3, `is`<Any>(nullValue()))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun failedToRemoveColumn() {
+        testFailure(4, 5)
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun removeColumn() {
+        helper.createDatabase(TEST_DB, 4)
+        val db = helper.runMigrationsAndValidate(TEST_DB,
+                5, true, MIGRATION_4_5)
+        val info = TableInfo.read(db, MigrationDbKotlin.Entity3.TABLE_NAME)
+        assertThat(info.columns.size, `is`(2))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun dropTable() {
+        helper.createDatabase(TEST_DB, 5)
+        val db = helper.runMigrationsAndValidate(TEST_DB,
+                6, true, MIGRATION_5_6)
+        val info = TableInfo.read(db, MigrationDbKotlin.Entity3.TABLE_NAME)
+        assertThat(info.columns.size, `is`(0))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun failedToDropTable() {
+        testFailure(5, 6)
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun failedToDropTableDontVerify() {
+        helper.createDatabase(TEST_DB, 5)
+        val db = helper.runMigrationsAndValidate(TEST_DB,
+                6, false, EmptyMigration(5, 6))
+        val info = TableInfo.read(db, MigrationDbKotlin.Entity3.TABLE_NAME)
+        assertThat(info.columns.size, `is`(2))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun failedForeignKey() {
+        val db = helper.createDatabase(TEST_DB, 6)
+        db.close()
+        var throwable: Throwable? = null
+        try {
+            helper.runMigrationsAndValidate(TEST_DB,
+                    7, false, object : Migration(6, 7) {
+                override fun migrate(database: SupportSQLiteDatabase) {
+                    database.execSQL("CREATE TABLE Entity4 (`id` INTEGER, `name` TEXT,"
+                            + " PRIMARY KEY(`id`))")
+                }
+            })
+        } catch (t: Throwable) {
+            throwable = t
+        }
+
+        assertThat<Throwable>(throwable, instanceOf<Throwable>(IllegalStateException::class.java))
+
+        assertThat<String>(throwable!!.message, containsString("Migration failed"))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun newTableWithForeignKey() {
+        helper.createDatabase(TEST_DB, 6)
+        val db = helper.runMigrationsAndValidate(TEST_DB,
+                7, false, MIGRATION_6_7)
+        val info = TableInfo.read(db, MigrationDbKotlin.Entity4.TABLE_NAME)
+        assertThat(info.foreignKeys.size, `is`(1))
+    }
+
+    @Throws(IOException::class)
+    private fun testFailure(startVersion: Int, endVersion: Int) {
+        val db = helper.createDatabase(TEST_DB, startVersion)
+        db.close()
+        var throwable: Throwable? = null
+        try {
+            helper.runMigrationsAndValidate(TEST_DB, endVersion, true,
+                    EmptyMigration(startVersion, endVersion))
+        } catch (t: Throwable) {
+            throwable = t
+        }
+
+        assertThat<Throwable>(throwable, instanceOf<Throwable>(IllegalStateException::class.java))
+        assertThat<String>(throwable!!.message, containsString("Migration failed"))
+    }
+
+
+    internal val MIGRATION_1_2: Migration = object : Migration(1, 2) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity2` (`id` INTEGER NOT NULL,"
+                    + " `name` TEXT, PRIMARY KEY(`id`))")
+        }
+    }
+
+    internal val MIGRATION_2_3: Migration = object : Migration(2, 3) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("ALTER TABLE " + MigrationDbKotlin.Entity2.TABLE_NAME
+                    + " ADD COLUMN addedInV3 TEXT")
+        }
+    }
+
+    internal val MIGRATION_3_4: Migration = object : Migration(3, 4) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity3` (`id` INTEGER NOT NULL,"
+                    + " `removedInV5` TEXT, `name` TEXT, PRIMARY KEY(`id`))")
+        }
+    }
+
+    internal val MIGRATION_4_5: Migration = object : Migration(4, 5) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity3_New` (`id` INTEGER NOT NULL,"
+                    + " `name` TEXT, PRIMARY KEY(`id`))")
+            database.execSQL("INSERT INTO Entity3_New(`id`, `name`) "
+                    + "SELECT `id`, `name` FROM Entity3")
+            database.execSQL("DROP TABLE Entity3")
+            database.execSQL("ALTER TABLE Entity3_New RENAME TO Entity3")
+        }
+    }
+
+    internal val MIGRATION_5_6: Migration = object : Migration(5, 6) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("DROP TABLE " + MigrationDbKotlin.Entity3.TABLE_NAME)
+        }
+    }
+
+    internal val MIGRATION_6_7: Migration = object : Migration(6, 7) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS "
+                    + MigrationDbKotlin.Entity4.TABLE_NAME
+                    + " (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`),"
+                    + " FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`)"
+                    + " ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)")
+        }
+    }
+
+    private val ALL_MIGRATIONS = arrayOf(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5,
+            MIGRATION_5_6, MIGRATION_6_7)
+
+    internal class EmptyMigration(startVersion: Int, endVersion: Int)
+        : Migration(startVersion, endVersion) {
+
+        override fun migrate(database: SupportSQLiteDatabase) {
+            // do nothing
+        }
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/BooksDaoTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/BooksDaoTest.kt
new file mode 100644
index 0000000..31344d6
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/BooksDaoTest.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.persistence.room.integration.kotlintestapp.vo.Book
+import android.arch.persistence.room.integration.kotlintestapp.vo.BookWithPublisher
+import android.arch.persistence.room.integration.kotlintestapp.vo.Publisher
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+
+class BooksDaoTest : TestDatabaseTest() {
+
+    @Test
+    fun bookById() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1)
+
+        assertThat(booksDao.getBook(TestUtil.BOOK_1.bookId), `is`<Book>(TestUtil.BOOK_1))
+    }
+
+    @Test
+    fun bookWithPublisher() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1)
+
+        var expected = BookWithPublisher(TestUtil.BOOK_1.bookId, TestUtil.BOOK_1.title,
+                TestUtil.PUBLISHER)
+        var expectedList = ArrayList<BookWithPublisher>()
+        expectedList.add(expected)
+
+        assertThat(database.booksDao().getBooksWithPublisher(),
+                `is`<List<BookWithPublisher>>(expectedList))
+    }
+
+    @Test
+    fun publisherWithBooks() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        var actualPublisherWithBooks = booksDao.getPublisherWithBooks(
+                TestUtil.PUBLISHER.publisherId)
+
+        assertThat(actualPublisherWithBooks.publisher, `is`<Publisher>(TestUtil.PUBLISHER))
+        assertThat(actualPublisherWithBooks.books?.size, `is`(2))
+        assertThat(actualPublisherWithBooks.books?.get(0), `is`<Book>(TestUtil.BOOK_1))
+        assertThat(actualPublisherWithBooks.books?.get(1), `is`<Book>(TestUtil.BOOK_2))
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/LiveDataQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/LiveDataQueryTest.kt
new file mode 100644
index 0000000..41fcf82
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/LiveDataQueryTest.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.persistence.room.integration.kotlintestapp.vo.Book
+import android.arch.persistence.room.integration.kotlintestapp.vo.BookWithPublisher
+import android.arch.persistence.room.integration.kotlintestapp.vo.Publisher
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+
+
+class LiveDataQueryTest : TestDatabaseTest() {
+
+    @Test
+    fun observeBooksById() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1)
+
+        val book = LiveDataTestUtil.getValue(booksDao.getBookLiveData(TestUtil.BOOK_1.bookId))
+
+        assertThat(book, `is`<Book>(TestUtil.BOOK_1))
+    }
+
+    @Test
+    fun observeBooksWithPublisher() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1)
+
+        var expected = BookWithPublisher(TestUtil.BOOK_1.bookId, TestUtil.BOOK_1.title,
+                TestUtil.PUBLISHER)
+        var expectedList = ArrayList<BookWithPublisher>()
+        expectedList.add(expected)
+
+        val actual = LiveDataTestUtil.getValue(booksDao.getBooksWithPublisherLiveData())
+        assertThat(actual, `is`<List<BookWithPublisher>>(expectedList))
+    }
+
+    @Test
+    fun publisherWithBooks() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        var actualPublisherWithBooks = LiveDataTestUtil.getValue(
+                booksDao.getPublisherWithBooksLiveData(TestUtil.PUBLISHER.publisherId))
+
+        assertThat(actualPublisherWithBooks.publisher, `is`<Publisher>(TestUtil.PUBLISHER))
+        assertThat(actualPublisherWithBooks.books?.size, `is`(2))
+        assertThat(actualPublisherWithBooks.books?.get(0), `is`<Book>(TestUtil.BOOK_1))
+        assertThat(actualPublisherWithBooks.books?.get(1), `is`<Book>(TestUtil.BOOK_2))
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/LiveDataTestUtil.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/LiveDataTestUtil.kt
new file mode 100644
index 0000000..5533f11
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/LiveDataTestUtil.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.lifecycle.LiveData
+import android.arch.lifecycle.Observer
+
+object LiveDataTestUtil {
+
+    @Throws(InterruptedException::class)
+    fun <T> getValue(liveData: LiveData<T>): T {
+        val data = arrayOfNulls<Any>(1)
+        val observer = object : Observer<T> {
+            override fun onChanged(o: T?) {
+                data[0] = o
+                liveData.removeObserver(this)
+            }
+        }
+        liveData.observeForever(observer)
+
+        return data[0] as T
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/RxJava2QueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/RxJava2QueryTest.kt
new file mode 100644
index 0000000..a3d0a68
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/RxJava2QueryTest.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.persistence.room.integration.kotlintestapp.vo.BookWithPublisher
+import org.junit.Test
+
+class RxJava2QueryTest : TestDatabaseTest() {
+
+    @Test
+    fun observeBooksById() {
+        database.booksDao().addAuthors(TestUtil.AUTHOR_1)
+        database.booksDao().addPublishers(TestUtil.PUBLISHER)
+        database.booksDao().addBooks(TestUtil.BOOK_1)
+
+        database.booksDao().getBookFlowable(TestUtil.BOOK_1.bookId)
+                .test()
+                .assertValue { book -> book == TestUtil.BOOK_1 }
+    }
+
+    @Test
+    fun observeBooksWithPublisher() {
+        database.booksDao().addAuthors(TestUtil.AUTHOR_1)
+        database.booksDao().addPublishers(TestUtil.PUBLISHER)
+        database.booksDao().addBooks(TestUtil.BOOK_1)
+
+        var expected = BookWithPublisher(TestUtil.BOOK_1.bookId, TestUtil.BOOK_1.title,
+                TestUtil.PUBLISHER)
+        var expectedList = ArrayList<BookWithPublisher>()
+        expectedList.add(expected)
+
+        database.booksDao().getBooksWithPublisherFlowable()
+                .test()
+                .assertValue(expectedList)
+    }
+
+    @Test
+    fun publisherWithBooks() {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        booksDao.getPublisherWithBooksFlowable(TestUtil.PUBLISHER.publisherId)
+                .test()
+                .assertValue {
+                    it.publisher == TestUtil.PUBLISHER
+                            && it.books?.size == 2
+                            && it.books?.get(0) == TestUtil.BOOK_1
+                            && it.books?.get(1) == TestUtil.BOOK_2
+                }
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/TestDatabaseTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/TestDatabaseTest.kt
new file mode 100644
index 0000000..42815b2
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/TestDatabaseTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.core.executor.testing.InstantTaskExecutorRule
+import android.arch.persistence.room.Room
+import android.arch.persistence.room.integration.kotlintestapp.BooksDao
+import android.arch.persistence.room.integration.kotlintestapp.BooksDatabase
+import android.support.test.InstrumentationRegistry
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+
+abstract class TestDatabaseTest {
+
+    @get:Rule
+    var instantTaskExecutorRule = InstantTaskExecutorRule()
+
+    protected lateinit var database: BooksDatabase
+    protected lateinit var booksDao: BooksDao
+
+    @Before
+    @Throws(Exception::class)
+    fun setUp() {
+        database = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
+                BooksDatabase::class.java)
+                // allowing main thread queries, just for testing
+                .allowMainThreadQueries()
+                .build()
+
+        booksDao = database.booksDao()
+    }
+
+    @After
+    @Throws(Exception::class)
+    fun tearDown() {
+        database.close()
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/TestUtil.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/TestUtil.kt
new file mode 100644
index 0000000..c4d406c
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/test/TestUtil.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.test
+
+import android.arch.persistence.room.integration.kotlintestapp.vo.Author
+import android.arch.persistence.room.integration.kotlintestapp.vo.Book
+import android.arch.persistence.room.integration.kotlintestapp.vo.BookAuthor
+import android.arch.persistence.room.integration.kotlintestapp.vo.Publisher
+
+class TestUtil {
+
+    companion object {
+
+        val PUBLISHER = Publisher("ph1", "publisher 1")
+        val PUBLISHER2 = Publisher("ph2", "publisher 2")
+
+        val AUTHOR_1 = Author("a1", "author 1")
+        val AUTHOR_2 = Author("a2", "author 2")
+
+        val BOOK_1 = Book("b1", "book title 1", "ph1")
+        val BOOK_2 = Book("b2", "book title 2", "ph1")
+
+        val BOOK_AUTHOR_1_1 = BookAuthor(BOOK_1.bookId, AUTHOR_1.authorId)
+        val BOOK_AUTHOR_1_2 = BookAuthor(BOOK_1.bookId, AUTHOR_2.authorId)
+        val BOOK_AUTHOR_2_2 = BookAuthor(BOOK_2.bookId, AUTHOR_2.authorId)
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Author.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Author.kt
new file mode 100644
index 0000000..a51c256
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Author.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.PrimaryKey
+
+@Entity
+data class Author(@PrimaryKey val authorId: String, val name: String)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Book.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Book.kt
new file mode 100644
index 0000000..794e60b
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Book.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.ForeignKey
+import android.arch.persistence.room.PrimaryKey
+
+@Entity(foreignKeys = arrayOf(
+        ForeignKey(entity = Publisher::class,
+                parentColumns = arrayOf("publisherId"),
+                childColumns = arrayOf("bookPublisherId"),
+                deferred = true)))
+data class Book(@PrimaryKey val bookId: String, val title: String, val bookPublisherId: String)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/BookAuthor.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/BookAuthor.kt
new file mode 100644
index 0000000..4fc661c
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/BookAuthor.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.ForeignKey
+
+
+@Entity(foreignKeys = arrayOf(
+        ForeignKey(entity = Book::class,
+                parentColumns = arrayOf("bookId"),
+                childColumns = arrayOf("bookId"),
+                onUpdate = ForeignKey.CASCADE,
+                onDelete = ForeignKey.CASCADE,
+                deferred = true),
+        ForeignKey(entity = Author::class,
+                parentColumns = arrayOf("authorId"),
+                childColumns = arrayOf("authorId"),
+                onUpdate = ForeignKey.CASCADE,
+                onDelete = ForeignKey.CASCADE,
+                deferred = true)),
+        primaryKeys = arrayOf("bookId", "authorId"))
+data class BookAuthor(val bookId: String, val authorId: String)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/BookWithPublisher.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/BookWithPublisher.kt
new file mode 100644
index 0000000..6516c23
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/BookWithPublisher.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Embedded
+
+
+data class BookWithPublisher(val bookId: String, val title: String,
+                             @Embedded val publisher: Publisher)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Publisher.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Publisher.kt
new file mode 100644
index 0000000..ea1550a
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/Publisher.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Entity
+import android.arch.persistence.room.PrimaryKey
+
+@Entity
+data class Publisher(@PrimaryKey val publisherId: String, val name: String)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/PublisherWithBooks.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/PublisherWithBooks.kt
new file mode 100644
index 0000000..fe482b1
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/android/arch/persistence/room/integration/kotlintestapp/vo/PublisherWithBooks.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.kotlintestapp.vo
+
+import android.arch.persistence.room.Embedded
+import android.arch.persistence.room.Relation
+
+class PublisherWithBooks {
+    @Embedded var publisher: Publisher? = null
+    @Relation(parentColumn = "publisherId", // publisher.publisherId
+            entityColumn = "bookPublisherId", // book.bookPublisherId
+            entity = Book::class)
+    var books: List<Book>? = null
+}
diff --git a/room/integration-tests/kotlintestapp/src/main/AndroidManifest.xml b/room/integration-tests/kotlintestapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d079891
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.arch.persistence.room.integration.kotlintestapp"/>
diff --git a/room/integration-tests/testapp/.gitignore b/room/integration-tests/testapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/room/integration-tests/testapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..9873138
--- /dev/null
+++ b/room/integration-tests/testapp/build.gradle
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+apply plugin: 'com.android.application'
+
+project.ext.noDocs = true
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+            }
+        }
+    }
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+    sourceSets {
+        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
+    }
+}
+
+dependencies {
+    compile project(":room:common")
+    compile project(":room:db")
+    compile project(":room:db-impl")
+    compile project(':room:runtime')
+    compile project(':arch:runtime')
+    compile project(':arch:common')
+    compile project(':paging:common')
+    compile project(':lifecycle:extensions')
+    compile project(':lifecycle:runtime')
+    compile project(':lifecycle:common')
+    compile project(':room:rxjava2')
+    compile project(':paging:runtime')
+
+    compile libs.support.recyclerview
+    compile libs.support.app_compat
+    annotationProcessor project(":room:compiler")
+    androidTestAnnotationProcessor project(":room:compiler")
+
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+        exclude module: 'hamcrest-core'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+        exclude module: "hamcrest-core"
+    })
+    // IJ's gradle integration just cannot figure this out ...
+    androidTestCompile project(':lifecycle:extensions')
+    androidTestCompile project(':lifecycle:common')
+    androidTestCompile project(':lifecycle:runtime')
+    androidTestCompile project(':room:testing')
+    androidTestCompile project(':room:rxjava2')
+    androidTestCompile project(':arch:core-testing')
+    androidTestCompile libs.rx_java
+    androidTestCompile libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+}
+
+createAndroidCheckstyle(project)
+tasks['check'].dependsOn(tasks['connectedCheck'])
+
+uploadArchives.enabled = false
diff --git a/room/integration-tests/testapp/proguard-rules.pro b/room/integration-tests/testapp/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/room/integration-tests/testapp/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/1.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/1.json
new file mode 100644
index 0000000..e05f095
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/1.json
@@ -0,0 +1,38 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "2f3557e56d7f665363f3e20d14787a59",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"2f3557e56d7f665363f3e20d14787a59\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/2.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/2.json
new file mode 100644
index 0000000..fc5a1b6
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/2.json
@@ -0,0 +1,64 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 2,
+    "identityHash": "aee9a6eed720c059df0f2ee0d6e96d89",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"aee9a6eed720c059df0f2ee0d6e96d89\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/3.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/3.json
new file mode 100644
index 0000000..f4629da
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/3.json
@@ -0,0 +1,70 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 3,
+    "identityHash": "3f2a99b6d768af0184e077808f7348fe",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addedInV3` TEXT, `name` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3f2a99b6d768af0184e077808f7348fe\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/4.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/4.json
new file mode 100644
index 0000000..b5a0794
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/4.json
@@ -0,0 +1,101 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 4,
+    "identityHash": "abbae5f17d94ff7c2c7e05ca217ccc31",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addedInV3` TEXT, `name` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `removedInV5` TEXT, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "removedInV5",
+            "columnName": "removedInV5",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"abbae5f17d94ff7c2c7e05ca217ccc31\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/5.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/5.json
new file mode 100644
index 0000000..367b1f2
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/5.json
@@ -0,0 +1,95 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 5,
+    "identityHash": "5543c44fe679f4cf8f03093d66838068",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addedInV3` TEXT, `name` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"5543c44fe679f4cf8f03093d66838068\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/6.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/6.json
new file mode 100644
index 0000000..3468f5b
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/6.json
@@ -0,0 +1,70 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 6,
+    "identityHash": "3f2a99b6d768af0184e077808f7348fe",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addedInV3` TEXT, `name` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3f2a99b6d768af0184e077808f7348fe\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/7.json b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/7.json
new file mode 100644
index 0000000..93a9682
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/android.arch.persistence.room.integration.testapp.migration.MigrationDb/7.json
@@ -0,0 +1,118 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 7,
+    "identityHash": "03ff272b825e27b5c15545c85fe1b845",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [
+          {
+            "name": "index_Entity1_name",
+            "unique": true,
+            "columnNames": [
+              "name"
+            ],
+            "createSql": "CREATE UNIQUE INDEX `index_Entity1_name` ON `${TABLE_NAME}` (`name`)"
+          }
+        ],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addedInV3` TEXT, `name` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV3",
+            "columnName": "addedInV3",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Entity4",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": [
+          {
+            "table": "Entity1",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "name"
+            ],
+            "referencedColumns": [
+              "name"
+            ]
+          }
+        ]
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"03ff272b825e27b5c15545c85fe1b845\")"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/InvalidationTrackerTrojan.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/InvalidationTrackerTrojan.java
new file mode 100644
index 0000000..5fa15ac
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/InvalidationTrackerTrojan.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+/**
+ * Trojan class to be able to assert internal state.
+ */
+public class InvalidationTrackerTrojan {
+    public static int countObservers(InvalidationTracker tracker) {
+        return tracker.mObserverMap.size();
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/PKeyTestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/PKeyTestDatabase.java
new file mode 100644
index 0000000..e61d808
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/PKeyTestDatabase.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.integration.testapp.vo.IntAutoIncPKeyEntity;
+import android.arch.persistence.room.integration.testapp.vo.IntegerAutoIncPKeyEntity;
+
+import java.util.List;
+
+@Database(entities = {IntAutoIncPKeyEntity.class, IntegerAutoIncPKeyEntity.class}, version = 1,
+        exportSchema = false)
+public abstract class PKeyTestDatabase extends RoomDatabase {
+    public abstract IntPKeyDao intPKeyDao();
+    public abstract IntegerPKeyDao integerPKeyDao();
+
+    @Dao
+    public interface IntPKeyDao {
+        @Insert
+        void insertMe(IntAutoIncPKeyEntity... items);
+        @Insert
+        long insertAndGetId(IntAutoIncPKeyEntity item);
+
+        @Insert
+        long[] insertAndGetIds(IntAutoIncPKeyEntity... item);
+
+        @Query("select * from IntAutoIncPKeyEntity WHERE pKey = :key")
+        IntAutoIncPKeyEntity getMe(int key);
+
+        @Query("select data from IntAutoIncPKeyEntity WHERE pKey IN(:ids)")
+        List<String> loadDataById(long... ids);
+    }
+
+    @Dao
+    public interface IntegerPKeyDao {
+        @Insert
+        void insertMe(IntegerAutoIncPKeyEntity items);
+        @Query("select * from IntegerAutoIncPKeyEntity WHERE pKey = :key")
+        IntegerAutoIncPKeyEntity getMe(int key);
+
+        @Insert
+        long insertAndGetId(IntegerAutoIncPKeyEntity item);
+
+        @Insert
+        long[] insertAndGetIds(IntegerAutoIncPKeyEntity... item);
+
+        @Query("select data from IntegerAutoIncPKeyEntity WHERE pKey IN(:ids)")
+        List<String> loadDataById(long... ids);
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/TestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/TestDatabase.java
new file mode 100644
index 0000000..e573de1
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/TestDatabase.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp;
+
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.TypeConverter;
+import android.arch.persistence.room.TypeConverters;
+import android.arch.persistence.room.integration.testapp.dao.BlobEntityDao;
+import android.arch.persistence.room.integration.testapp.dao.PetCoupleDao;
+import android.arch.persistence.room.integration.testapp.dao.PetDao;
+import android.arch.persistence.room.integration.testapp.dao.ProductDao;
+import android.arch.persistence.room.integration.testapp.dao.SchoolDao;
+import android.arch.persistence.room.integration.testapp.dao.ToyDao;
+import android.arch.persistence.room.integration.testapp.dao.UserDao;
+import android.arch.persistence.room.integration.testapp.dao.UserPetDao;
+import android.arch.persistence.room.integration.testapp.vo.BlobEntity;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.PetCouple;
+import android.arch.persistence.room.integration.testapp.vo.Product;
+import android.arch.persistence.room.integration.testapp.vo.School;
+import android.arch.persistence.room.integration.testapp.vo.Toy;
+import android.arch.persistence.room.integration.testapp.vo.User;
+
+import java.util.Date;
+
+@Database(entities = {User.class, Pet.class, School.class, PetCouple.class, Toy.class,
+        BlobEntity.class, Product.class},
+        version = 1, exportSchema = false)
+@TypeConverters(TestDatabase.Converters.class)
+public abstract class TestDatabase extends RoomDatabase {
+    public abstract UserDao getUserDao();
+    public abstract PetDao getPetDao();
+    public abstract UserPetDao getUserPetDao();
+    public abstract SchoolDao getSchoolDao();
+    public abstract PetCoupleDao getPetCoupleDao();
+    public abstract ToyDao getToyDao();
+    public abstract BlobEntityDao getBlobEntityDao();
+    public abstract ProductDao getProductDao();
+
+    @SuppressWarnings("unused")
+    public static class Converters {
+        @TypeConverter
+        public Date fromTimestamp(Long value) {
+            return value == null ? null : new Date(value);
+        }
+
+        @TypeConverter
+        public Long dateToTimestamp(Date date) {
+            if (date == null) {
+                return null;
+            } else {
+                return date.getTime();
+            }
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/BlobEntityDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/BlobEntityDao.java
new file mode 100644
index 0000000..212eba7
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/BlobEntityDao.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.integration.testapp.vo.BlobEntity;
+
+import java.util.List;
+
+@Dao
+public interface BlobEntityDao {
+
+    @Insert
+    void insert(BlobEntity item);
+
+    @Query("SELECT * FROM BlobEntity")
+    List<BlobEntity> selectAll();
+
+    @Query("SELECT content FROM BlobEntity WHERE id = :id")
+    byte[] getContent(long id);
+
+    @Query("UPDATE BlobEntity SET content = :content WHERE id = :id")
+    void updateContent(long id, byte[] content);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/PetCoupleDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/PetCoupleDao.java
new file mode 100644
index 0000000..4f7c4e2
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/PetCoupleDao.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.integration.testapp.vo.PetCouple;
+
+import java.util.List;
+
+@Dao
+public interface PetCoupleDao {
+    @Insert
+    void insert(PetCouple couple);
+
+    @Query("SELECT * FROM PetCouple")
+    List<PetCouple> loadAll();
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/PetDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/PetDao.java
new file mode 100644
index 0000000..5179655
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/PetDao.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.OnConflictStrategy;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+
+@Dao
+public interface PetDao {
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    void insertOrReplace(Pet... pets);
+
+    @Insert
+    void insertAll(Pet[] pets);
+
+    @Query("SELECT COUNT(*) FROM Pet")
+    int count();
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/ProductDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/ProductDao.java
new file mode 100644
index 0000000..417fb72
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/ProductDao.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.integration.testapp.vo.Product;
+import android.support.annotation.NonNull;
+
+@Dao
+public interface ProductDao {
+
+    @Insert
+    long insert(@NonNull Product product);
+
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
new file mode 100644
index 0000000..7bb137f
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.OnConflictStrategy;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.RoomWarnings;
+import android.arch.persistence.room.integration.testapp.vo.Coordinates;
+import android.arch.persistence.room.integration.testapp.vo.School;
+import android.arch.persistence.room.integration.testapp.vo.SchoolRef;
+
+import java.util.List;
+
+@Dao
+public abstract class SchoolDao {
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    public abstract void insert(School... schools);
+
+    @Query("SELECT * from School WHERE address_street LIKE '%' || :street || '%'")
+    public abstract List<School> findByStreet(String street);
+
+    @Query("SELECT mName, manager_mName FROM School")
+    public abstract List<School> schoolAndManagerNames();
+
+    @Query("SELECT mName, manager_mName FROM School")
+    @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
+    public abstract List<SchoolRef> schoolAndManagerNamesAsPojo();
+
+    @Query("SELECT address_lat as lat, address_lng as lng FROM School WHERE mId = :schoolId")
+    public abstract Coordinates loadCoordinates(int schoolId);
+
+    @Query("SELECT address_lat, address_lng FROM School WHERE mId = :schoolId")
+    public abstract School loadCoordinatesAsSchool(int schoolId);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/ToyDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/ToyDao.java
new file mode 100644
index 0000000..5d50a62
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/ToyDao.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.integration.testapp.vo.Toy;
+
+@Dao
+public interface ToyDao {
+    @Insert
+    void insert(Toy... toys);
+}
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
new file mode 100644
index 0000000..97461c0
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.lifecycle.LiveData;
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Delete;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.OnConflictStrategy;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Update;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.vo.AvgWeightByAge;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.util.paging.CountedDataSource;
+import android.arch.util.paging.LiveLazyListProvider;
+import android.database.Cursor;
+
+import org.reactivestreams.Publisher;
+
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import io.reactivex.Flowable;
+import io.reactivex.Maybe;
+import io.reactivex.Single;
+
+@SuppressWarnings("SameParameterValue")
+@Dao
+public abstract class UserDao {
+
+    private final TestDatabase mDatabase;
+
+    public UserDao(TestDatabase database) {
+        mDatabase = database;
+    }
+
+    @Query("select * from user where mName like :name")
+    public abstract List<User> findUsersByName(String name);
+
+    @Query("select * from user where mId = :id")
+    public abstract User load(int id);
+
+    @Query("select * from user where mId IN(:ids)")
+    public abstract User[] loadByIds(int... ids);
+
+    @Insert
+    public abstract void insert(User user);
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    public abstract void insertOrReplace(User user);
+
+    @Delete
+    public abstract int delete(User user);
+
+    @Delete
+    public abstract int deleteAll(User[] users);
+
+    @Query("delete from user")
+    public abstract int deleteEverything();
+
+    @Update
+    public abstract int update(User user);
+
+    @Update
+    public abstract int updateAll(List<User> users);
+
+    @Insert
+    public abstract void insertAll(User[] users);
+
+    @Query("select * from user where mAdmin = :isAdmin")
+    public abstract List<User> findByAdmin(boolean isAdmin);
+
+    @Query("delete from user where mAge > :age")
+    public abstract int deleteAgeGreaterThan(int age);
+
+    @Query("delete from user where mId IN(:uids)")
+    public abstract int deleteByUids(int... uids);
+
+    @Query("delete from user where mAge >= :min AND mAge <= :max")
+    public abstract int deleteByAgeRange(int min, int max);
+
+    @Query("update user set mName = :name where mId = :id")
+    public abstract int updateById(int id, String name);
+
+    @Query("update user set mId = mId + :amount")
+    public abstract void incrementIds(int amount);
+
+    @Query("update user set mAge = mAge + 1")
+    public abstract void incrementAgeOfAll();
+
+    @Query("select mId from user order by mId ASC")
+    public abstract List<Integer> loadIds();
+
+    @Query("select * from user where mId = :id")
+    public abstract LiveData<User> liveUserById(int id);
+
+    @Query("select * from user where mName LIKE '%' || :name || '%' ORDER BY mId DESC")
+    public abstract LiveData<List<User>> liveUsersListByName(String name);
+
+    @Query("select * from user where length(mName) = :length")
+    public abstract List<User> findByNameLength(int length);
+
+    @Query("select * from user where mAge = :age")
+    public abstract List<User> findByAge(int age);
+
+    @Query("select mAge, AVG(mWeight) from user GROUP BY mAge ORDER BY 2 DESC")
+    public abstract List<AvgWeightByAge> weightByAge();
+
+    @Query("select mAge, AVG(mWeight) from user GROUP BY mAge ORDER BY 2 DESC LIMIT 1")
+    public abstract LiveData<AvgWeightByAge> maxWeightByAgeGroup();
+
+    @Query("select * from user where mBirthday > :from AND mBirthday < :to")
+    public abstract List<User> findByBirthdayRange(Date from, Date to);
+
+    @Query("select mId from user where mId IN (:ids)")
+    public abstract Cursor findUsersAsCursor(int... ids);
+
+    @Query("select * from user where mId = :id")
+    public abstract Flowable<User> flowableUserById(int id);
+
+    @Query("select * from user where mId = :id")
+    public abstract Maybe<User> maybeUserById(int id);
+
+    @Query("select * from user where mId IN (:ids)")
+    public abstract Maybe<List<User>> maybeUsersByIds(int... ids);
+
+    @Query("select * from user where mId = :id")
+    public abstract Single<User> singleUserById(int id);
+
+    @Query("select * from user where mId IN (:ids)")
+    public abstract Single<List<User>> singleUsersByIds(int... ids);
+
+    @Query("select COUNT(*) from user")
+    public abstract Flowable<Integer> flowableCountUsers();
+
+    @Query("select COUNT(*) from user")
+    public abstract Publisher<Integer> publisherCountUsers();
+
+    @Query("SELECT mBirthday from User where mId = :id")
+
+    public abstract Date getBirthday(int id);
+
+    @Query("SELECT COUNT(*) from user")
+    public abstract int count();
+
+    public void insertBothByRunnable(final User a, final User b) {
+        mDatabase.runInTransaction(new Runnable() {
+            @Override
+            public void run() {
+                insert(a);
+                insert(b);
+            }
+        });
+    }
+
+    public int insertBothByCallable(final User a, final User b) {
+        return mDatabase.runInTransaction(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                insert(a);
+                insert(b);
+                return 2;
+            }
+        });
+    }
+
+    @Query("SELECT * FROM user where mAge > :age")
+    public abstract LiveLazyListProvider<User> loadPagedByAge(int age);
+
+    @Query("SELECT * FROM user ORDER BY mAge DESC")
+    public abstract CountedDataSource<User> loadUsersByAgeDesc();
+
+    @Query("DELETE FROM User WHERE mId IN (:ids) AND mAge == :age")
+    public abstract int deleteByAgeAndIds(int age, List<Integer> ids);
+
+    @Query("UPDATE User set mWeight = :weight WHERE mId IN (:ids) AND mAge == :age")
+    public abstract int updateByAgeAndIds(float weight, int age, List<Integer> ids);
+
+    // QueryLoader
+
+    @Query("SELECT COUNT(*) from user")
+    public abstract Integer getUserCount();
+
+    //   name desc
+    @Query("SELECT * from user ORDER BY mName DESC LIMIT :limit OFFSET :offset")
+    public abstract List<User> userNameLimitOffset(int limit, int offset);
+
+    @Query("SELECT * from user WHERE mName < :key ORDER BY mName DESC LIMIT :limit")
+    public abstract List<User> userNameLoadAfter(String key, int limit);
+
+    @Query("SELECT * from user WHERE mName > :key ORDER BY mName ASC LIMIT :limit")
+    public abstract List<User> userNameLoadBefore(String key, int limit);
+
+    //    last asc, first desc, id asc
+    @Query("SELECT * from user"
+            + " ORDER BY mLastName DESC, mName ASC, mId DESC"
+            + " LIMIT :limit OFFSET :offset")
+    public abstract List<User> userComplexLimitOffset(int limit, int offset);
+
+    @Query("SELECT * from user"
+            + " WHERE mLastName < :lastName or (mLastName = :lastName and (mName > :name or (mName = :name and mId < :id)))"
+            + " ORDER BY mLastName DESC, mName ASC, mId DESC"
+            + " LIMIT :limit")
+    public abstract List<User> userComplexLoadAfter(String lastName, String name, int id, int limit);
+
+    @Query("SELECT * from user"
+            + " WHERE mLastName > :lastName or (mLastName = :lastName and (mName < :name or (mName = :name and mId > :id)))"
+            + " ORDER BY mLastName ASC, mName DESC, mId ASC"
+            + " LIMIT :limit")
+    public abstract List<User> userComplexLoadBefore(String lastName, String name, int id, int limit);
+
+
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserPetDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserPetDao.java
new file mode 100644
index 0000000..3507aee
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserPetDao.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.dao;
+
+import android.arch.lifecycle.LiveData;
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Delete;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Update;
+import android.arch.persistence.room.integration.testapp.vo.EmbeddedUserAndAllPets;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.persistence.room.integration.testapp.vo.UserAndAllPets;
+import android.arch.persistence.room.integration.testapp.vo.UserAndPet;
+import android.arch.persistence.room.integration.testapp.vo.UserAndPetNonNull;
+import android.arch.persistence.room.integration.testapp.vo.UserIdAndPetNames;
+import android.arch.persistence.room.integration.testapp.vo.UserWithPetsAndToys;
+
+import java.util.List;
+
+@Dao
+public interface UserPetDao {
+    @Query("SELECT * FROM User u, Pet p WHERE u.mId = p.mUserId")
+    List<UserAndPet> loadAll();
+
+    @Query("SELECT * FROM User u LEFT OUTER JOIN Pet p ON u.mId = p.mUserId")
+    List<UserAndPet> loadUsers();
+
+    @Query("SELECT * FROM User u LEFT OUTER JOIN Pet p ON u.mId = p.mUserId")
+    List<UserAndPetNonNull> loadUsersWithNonNullPet();
+
+    @Query("SELECT * FROM Pet p LEFT OUTER JOIN User u ON u.mId = p.mUserId")
+    List<UserAndPet> loadPets();
+
+    @Query("SELECT * FROM User u")
+    List<UserAndAllPets> loadAllUsersWithTheirPets();
+
+    @Query("SELECT * FROM User u")
+    List<UserIdAndPetNames> loadUserAndPetNames();
+
+    @Query("SELECT * FROM User u")
+    List<UserWithPetsAndToys> loadUserWithPetsAndToys();
+
+    @Query("SELECT * FROM User UNION ALL SELECT * FROM USER")
+    List<UserAndAllPets> unionByItself();
+
+    @Query("SELECT * FROM User u where u.mId = :userId")
+    LiveData<UserAndAllPets> liveUserWithPets(int userId);
+
+    @Query("SELECT * FROM User u where u.mId = :uid")
+    EmbeddedUserAndAllPets loadUserAndPetsAsEmbedded(int uid);
+
+    @Insert
+    void insertUserAndPet(User user, Pet pet);
+
+    @Update
+    void updateUsersAndPets(User[] users, Pet[] pets);
+
+    @Delete
+    void delete2UsersAndPets(User user1, User user2, Pet[] pets);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
new file mode 100644
index 0000000..4a95ad8
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.migration;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.arch.persistence.room.Ignore;
+import android.arch.persistence.room.Index;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.RoomDatabase;
+import android.content.ContentValues;
+import android.database.sqlite.SQLiteDatabase;
+
+import java.util.List;
+
+@SuppressWarnings("WeakerAccess")
+@Database(version = MigrationDb.LATEST_VERSION,
+        entities = {MigrationDb.Entity1.class, MigrationDb.Entity2.class,
+                MigrationDb.Entity4.class})
+public abstract class MigrationDb extends RoomDatabase {
+    static final int LATEST_VERSION = 7;
+    abstract MigrationDao dao();
+    @Entity(indices = {@Index(value = "name", unique = true)})
+    static class Entity1 {
+        public static final String TABLE_NAME = "Entity1";
+        @PrimaryKey
+        public int id;
+        public String name;
+    }
+
+    @Entity
+    static class Entity2 {
+        public static final String TABLE_NAME = "Entity2";
+        @PrimaryKey(autoGenerate = true)
+        public int id;
+        public String addedInV3;
+        public String name;
+    }
+
+    @Entity
+    static class Entity3 { // added in version 4, removed at 6
+        public static final String TABLE_NAME = "Entity3";
+        @PrimaryKey
+        public int id;
+        @Ignore //removed at 5
+        public String removedInV5;
+        public String name;
+    }
+
+    @Entity(foreignKeys = {
+            @ForeignKey(entity = Entity1.class,
+            parentColumns = "name",
+            childColumns = "name",
+            deferred = true)})
+    static class Entity4 {
+        public static final String TABLE_NAME = "Entity4";
+        @PrimaryKey
+        public int id;
+        public String name;
+    }
+
+    @Dao
+    interface MigrationDao {
+        @Query("SELECT * from Entity1 ORDER BY id ASC")
+        List<Entity1> loadAllEntity1s();
+        @Query("SELECT * from Entity2 ORDER BY id ASC")
+        List<Entity2> loadAllEntity2s();
+        @Query("SELECT * from Entity2 ORDER BY id ASC")
+        List<Entity2Pojo> loadAllEntity2sAsPojo();
+        @Insert
+        void insert(Entity2... entity2);
+    }
+
+    static class Entity2Pojo extends Entity2 {
+    }
+
+    /**
+     * not a real dao because database will change.
+     */
+    static class Dao_V1 {
+        final SupportSQLiteDatabase mDb;
+
+        Dao_V1(SupportSQLiteDatabase db) {
+            mDb = db;
+        }
+
+        public void insertIntoEntity1(int id, String name) {
+            ContentValues values = new ContentValues();
+            values.put("id", id);
+            values.put("name", name);
+            long insertionId = mDb.insert(Entity1.TABLE_NAME,
+                    SQLiteDatabase.CONFLICT_REPLACE, values);
+            if (insertionId == -1) {
+                throw new RuntimeException("test sanity failure");
+            }
+        }
+    }
+
+    /**
+     * not a real dao because database will change.
+     */
+    static class Dao_V2 {
+        final SupportSQLiteDatabase mDb;
+
+        Dao_V2(SupportSQLiteDatabase db) {
+            mDb = db;
+        }
+
+        public void insertIntoEntity2(int id, String name) {
+            ContentValues values = new ContentValues();
+            values.put("id", id);
+            values.put("name", name);
+            long insertionId = mDb.insert(Entity2.TABLE_NAME,
+                    SQLiteDatabase.CONFLICT_REPLACE, values);
+            if (insertionId == -1) {
+                throw new RuntimeException("test sanity failure");
+            }
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
new file mode 100644
index 0000000..aa297ed
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.migration;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.migration.Migration;
+import android.arch.persistence.room.testing.MigrationTestHelper;
+import android.arch.persistence.room.util.TableInfo;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Test custom database migrations.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MigrationTest {
+    private static final String TEST_DB = "migration-test";
+    @Rule
+    public MigrationTestHelper helper;
+
+    public MigrationTest() {
+        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
+                MigrationDb.class.getCanonicalName());
+    }
+
+    @Test
+    public void giveBadResource() throws IOException {
+        MigrationTestHelper helper = new MigrationTestHelper(
+                InstrumentationRegistry.getInstrumentation(),
+                "foo", new FrameworkSQLiteOpenHelperFactory());
+        try {
+            helper.createDatabase(TEST_DB, 1);
+            throw new AssertionError("must have failed with missing file exception");
+        } catch (FileNotFoundException exception) {
+            assertThat(exception.getMessage(), containsString("Cannot find"));
+        }
+    }
+
+    @Test
+    public void startInCurrentVersion() throws IOException {
+        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB,
+                MigrationDb.LATEST_VERSION);
+        final MigrationDb.Dao_V1 dao = new MigrationDb.Dao_V1(db);
+        dao.insertIntoEntity1(2, "x");
+        db.close();
+        MigrationDb migrationDb = getLatestDb();
+        List<MigrationDb.Entity1> items = migrationDb.dao().loadAllEntity1s();
+        helper.closeWhenFinished(migrationDb);
+        assertThat(items.size(), is(1));
+    }
+
+    @Test
+    public void addTable() throws IOException {
+        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
+        final MigrationDb.Dao_V1 dao = new MigrationDb.Dao_V1(db);
+        dao.insertIntoEntity1(2, "foo");
+        dao.insertIntoEntity1(3, "bar");
+        db.close();
+        db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2);
+        new MigrationDb.Dao_V2(db).insertIntoEntity2(3, "blah");
+        db.close();
+        MigrationDb migrationDb = getLatestDb();
+        List<MigrationDb.Entity1> entity1s = migrationDb.dao().loadAllEntity1s();
+
+        assertThat(entity1s.size(), is(2));
+        MigrationDb.Entity2 entity2 = new MigrationDb.Entity2();
+        entity2.id = 2;
+        entity2.name = "bar";
+        // assert no error happens
+        migrationDb.dao().insert(entity2);
+        List<MigrationDb.Entity2> entity2s = migrationDb.dao().loadAllEntity2s();
+        assertThat(entity2s.size(), is(2));
+    }
+
+    private MigrationDb getLatestDb() {
+        MigrationDb db = Room.databaseBuilder(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                MigrationDb.class, TEST_DB).addMigrations(ALL_MIGRATIONS).build();
+        // trigger open
+        db.beginTransaction();
+        db.endTransaction();
+        helper.closeWhenFinished(db);
+        return db;
+    }
+
+    @Test
+    public void addTableFailure() throws IOException {
+        testFailure(1, 2);
+    }
+
+    @Test
+    public void addColumnFailure() throws IOException {
+        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
+        db.close();
+        IllegalStateException caught = null;
+        try {
+            helper.runMigrationsAndValidate(TEST_DB, 3, true, new EmptyMigration(2, 3));
+        } catch (IllegalStateException ex) {
+            caught = ex;
+        }
+        assertThat(caught, instanceOf(IllegalStateException.class));
+    }
+
+    @Test
+    public void addColumn() throws IOException {
+        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
+        MigrationDb.Dao_V2 v2Dao = new MigrationDb.Dao_V2(db);
+        v2Dao.insertIntoEntity2(7, "blah");
+        db.close();
+        helper.runMigrationsAndValidate(TEST_DB, 3, true, MIGRATION_2_3);
+        // trigger open.
+        MigrationDb migrationDb = getLatestDb();
+        List<MigrationDb.Entity2> entity2s = migrationDb.dao().loadAllEntity2s();
+        assertThat(entity2s.size(), is(1));
+        assertThat(entity2s.get(0).name, is("blah"));
+        assertThat(entity2s.get(0).addedInV3, is(nullValue()));
+
+        List<MigrationDb.Entity2Pojo> entity2Pojos = migrationDb.dao().loadAllEntity2sAsPojo();
+        assertThat(entity2Pojos.size(), is(1));
+        assertThat(entity2Pojos.get(0).name, is("blah"));
+        assertThat(entity2Pojos.get(0).addedInV3, is(nullValue()));
+    }
+
+    @Test
+    public void failedToRemoveColumn() throws IOException {
+        testFailure(4, 5);
+    }
+
+    @Test
+    public void removeColumn() throws IOException {
+        helper.createDatabase(TEST_DB, 4);
+        final SupportSQLiteDatabase db = helper.runMigrationsAndValidate(TEST_DB,
+                5, true, MIGRATION_4_5);
+        final TableInfo info = TableInfo.read(db, MigrationDb.Entity3.TABLE_NAME);
+        assertThat(info.columns.size(), is(2));
+    }
+
+    @Test
+    public void dropTable() throws IOException {
+        helper.createDatabase(TEST_DB, 5);
+        final SupportSQLiteDatabase db = helper.runMigrationsAndValidate(TEST_DB,
+                6, true, MIGRATION_5_6);
+        final TableInfo info = TableInfo.read(db, MigrationDb.Entity3.TABLE_NAME);
+        assertThat(info.columns.size(), is(0));
+    }
+
+    @Test
+    public void failedToDropTable() throws IOException {
+        testFailure(5, 6);
+    }
+
+    @Test
+    public void failedToDropTableDontVerify() throws IOException {
+        helper.createDatabase(TEST_DB, 5);
+        final SupportSQLiteDatabase db = helper.runMigrationsAndValidate(TEST_DB,
+                6, false, new EmptyMigration(5, 6));
+        final TableInfo info = TableInfo.read(db, MigrationDb.Entity3.TABLE_NAME);
+        assertThat(info.columns.size(), is(2));
+    }
+
+    @Test
+    public void failedForeignKey() throws IOException {
+        final SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 6);
+        db.close();
+        Throwable throwable = null;
+        try {
+            helper.runMigrationsAndValidate(TEST_DB,
+                    7, false, new Migration(6, 7) {
+                        @Override
+                        public void migrate(SupportSQLiteDatabase database) {
+                            database.execSQL("CREATE TABLE Entity4 (`id` INTEGER NOT NULL,"
+                                    + " `name` TEXT, PRIMARY KEY(`id`))");
+                        }
+                    });
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertThat(throwable, instanceOf(IllegalStateException.class));
+        //noinspection ConstantConditions
+        assertThat(throwable.getMessage(), containsString("Migration failed"));
+    }
+
+    @Test
+    public void newTableWithForeignKey() throws IOException {
+        helper.createDatabase(TEST_DB, 6);
+        final SupportSQLiteDatabase db = helper.runMigrationsAndValidate(TEST_DB,
+                7, false, MIGRATION_6_7);
+        final TableInfo info = TableInfo.read(db, MigrationDb.Entity4.TABLE_NAME);
+        assertThat(info.foreignKeys.size(), is(1));
+    }
+
+    @Test
+    public void missingMigration() throws IOException {
+        SupportSQLiteDatabase database = helper.createDatabase(TEST_DB, 1);
+        database.close();
+        try {
+            Context targetContext = InstrumentationRegistry.getTargetContext();
+            MigrationDb db = Room.databaseBuilder(targetContext, MigrationDb.class, TEST_DB)
+                    .build();
+            db.dao().loadAllEntity1s();
+            throw new AssertionError("Should've failed :/");
+        } catch (IllegalStateException ignored) {
+        }
+    }
+
+    @Test
+    public void missingMigrationNuke() throws IOException {
+        SupportSQLiteDatabase database = helper.createDatabase(TEST_DB, 1);
+        final MigrationDb.Dao_V1 dao = new MigrationDb.Dao_V1(database);
+        dao.insertIntoEntity1(2, "foo");
+        dao.insertIntoEntity1(3, "bar");
+        database.close();
+
+        Context targetContext = InstrumentationRegistry.getTargetContext();
+        MigrationDb db = Room.databaseBuilder(targetContext, MigrationDb.class, TEST_DB)
+                .fallbackToDestructiveMigration()
+                .build();
+        assertThat(db.dao().loadAllEntity1s().size(), is(0));
+        db.close();
+    }
+
+    private void testFailure(int startVersion, int endVersion) throws IOException {
+        final SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, startVersion);
+        db.close();
+        Throwable throwable = null;
+        try {
+            helper.runMigrationsAndValidate(TEST_DB, endVersion, true,
+                    new EmptyMigration(startVersion, endVersion));
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertThat(throwable, instanceOf(IllegalStateException.class));
+        //noinspection ConstantConditions
+        assertThat(throwable.getMessage(), containsString("Migration failed"));
+    }
+
+    private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity2` ("
+                    + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+                    + " `name` TEXT)");
+        }
+    };
+
+    private static final Migration MIGRATION_2_3 = new Migration(2, 3) {
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            database.execSQL("ALTER TABLE " + MigrationDb.Entity2.TABLE_NAME
+                    + " ADD COLUMN addedInV3 TEXT");
+        }
+    };
+
+    private static final Migration MIGRATION_3_4 = new Migration(3, 4) {
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity3` (`id` INTEGER NOT NULL,"
+                    + " `removedInV5` TEXT, `name` TEXT, PRIMARY KEY(`id`))");
+        }
+    };
+
+    private static final Migration MIGRATION_4_5 = new Migration(4, 5) {
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity3_New` (`id` INTEGER NOT NULL,"
+                    + " `name` TEXT, PRIMARY KEY(`id`))");
+            database.execSQL("INSERT INTO Entity3_New(`id`, `name`) "
+                    + "SELECT `id`, `name` FROM Entity3");
+            database.execSQL("DROP TABLE Entity3");
+            database.execSQL("ALTER TABLE Entity3_New RENAME TO Entity3");
+        }
+    };
+
+    private static final Migration MIGRATION_5_6 = new Migration(5, 6) {
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            database.execSQL("DROP TABLE " + MigrationDb.Entity3.TABLE_NAME);
+        }
+    };
+
+    private static final Migration MIGRATION_6_7 = new Migration(6, 7) {
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS " + MigrationDb.Entity4.TABLE_NAME
+                    + " (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`),"
+                    + " FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`)"
+                    + " ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)");
+        }
+    };
+
+    private static final Migration[] ALL_MIGRATIONS = new Migration[]{MIGRATION_1_2,
+            MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7};
+
+    static final class EmptyMigration extends Migration {
+        EmptyMigration(int startVersion, int endVersion) {
+            super(startVersion, endVersion);
+        }
+
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+            // do nothing
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/LimitOffsetDataSourceTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
new file mode 100644
index 0000000..961a07e
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.paging;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.persistence.room.integration.testapp.test.TestDatabaseTest;
+import android.arch.persistence.room.integration.testapp.test.TestUtil;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.util.paging.CountedDataSource;
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LimitOffsetDataSourceTest extends TestDatabaseTest {
+    @Test
+    public void emptyPage() {
+        CountedDataSource<User> dataSource = mUserDao.loadUsersByAgeDesc();
+        assertThat(dataSource.loadCount(), is(0));
+    }
+
+    @Test
+    public void initial() {
+        List<User> users = createTestData();
+        CountedDataSource<User> dataSource = mUserDao.loadUsersByAgeDesc();
+        assertThat(dataSource.loadCount(), is(10));
+        List<User> initial = dataSource.loadAfterInitial(-1, 1);
+        assertThat(initial.get(0), is(users.get(0)));
+        List<User> second = dataSource.loadAfterInitial(0, 1);
+        assertThat(second.get(0), is(users.get(1)));
+    }
+
+    @Test
+    public void loadAll() {
+        List<User> users = createTestData();
+
+        CountedDataSource<User> dataSource = mUserDao.loadUsersByAgeDesc();
+        List<User> all = dataSource.loadAfterInitial(-1, 10);
+        assertThat(users, is(all));
+    }
+
+    @Test
+    public void loadAfter() {
+        List<User> users = createTestData();
+        CountedDataSource<User> dataSource = mUserDao.loadUsersByAgeDesc();
+        List<User> result = dataSource.loadAfter(3, users.get(3), 2);
+        assertThat(result, is(users.subList(4, 6)));
+    }
+
+    @Test
+    public void loadBefore() {
+        List<User> users = createTestData();
+        CountedDataSource<User> dataSource = mUserDao.loadUsersByAgeDesc();
+        List<User> result = dataSource.loadBefore(5, users.get(5), 3);
+        List<User> expected = new ArrayList<>(users.subList(2, 5));
+        Collections.reverse(expected);
+        assertThat(result, is(expected));
+    }
+
+    @NonNull
+    private List<User> createTestData() {
+        List<User> users = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            User user = TestUtil.createUser(i);
+            user.setAge(1);
+            mUserDao.insert(user);
+            users.add(user);
+        }
+        return users;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/LiveLazyListProviderTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/LiveLazyListProviderTest.java
new file mode 100644
index 0000000..d9472c1
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/LiveLazyListProviderTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.paging;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.testing.CountingTaskExecutorRule;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.persistence.room.integration.testapp.test.TestDatabaseTest;
+import android.arch.persistence.room.integration.testapp.test.TestUtil;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.util.paging.LazyList;
+import android.arch.util.paging.ListConfig;
+import android.support.annotation.Nullable;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class LiveLazyListProviderTest extends TestDatabaseTest {
+    @Rule
+    public CountingTaskExecutorRule mExecutorRule = new CountingTaskExecutorRule();
+    @Test
+    @LargeTest
+    public void getUsersAsLazyList()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mDatabase.beginTransaction();
+        try {
+            for (int i = 0; i < 100; i++) {
+                final User user = TestUtil.createUser(i + 1);
+                user.setAge(i);
+                mUserDao.insert(user);
+            }
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+        assertThat(mUserDao.count(), is(100));
+        final LiveData<LazyList<User>> livePagedUsers = mUserDao.loadPagedByAge(3).create(
+                ListConfig.builder().pageSize(10).prefetchDistance(1).create());
+
+        final TestLifecycleOwner testOwner = new TestLifecycleOwner();
+        testOwner.handleEvent(Lifecycle.Event.ON_CREATE);
+        drain();
+        LazyListObserver<User> observer = new LazyListObserver<>();
+
+        observe(livePagedUsers, testOwner, observer);
+        assertThat(observer.get(), nullValue());
+        observer.reset();
+
+        testOwner.handleEvent(Lifecycle.Event.ON_START);
+        drain();
+
+        LazyList<User> lazyList1 = observer.get();
+        assertThat(lazyList1, is(notNullValue()));
+
+        assertThat(lazyList1.size(), is(96));
+        assertThat(lazyList1.get(20), is(nullValue()));
+        drain();
+        assertThat(lazyList1.get(31), nullValue());
+        assertThat(lazyList1.get(20), notNullValue());
+        assertThat(lazyList1.get(16), notNullValue());
+
+        drain();
+        assertThat(lazyList1.get(31), notNullValue());
+        assertThat(lazyList1.get(50), nullValue());
+        drain();
+        assertThat(lazyList1.get(50), notNullValue());
+        observer.reset();
+        // now invalidate the database but don't get the new paged list
+        mUserDao.updateById(50, "foo");
+        assertThat(lazyList1.get(70), nullValue());
+        drain();
+        assertThat(lazyList1.get(70), nullValue());
+        LazyList<User> lazyList = observer.get();
+        assertThat(lazyList.get(70), notNullValue());
+    }
+
+    private void drain() throws InterruptedException, TimeoutException {
+        mExecutorRule.drainTasks(60, TimeUnit.SECONDS);
+    }
+
+    private void observe(final LiveData liveData, final LifecycleOwner provider,
+            final Observer observer) throws ExecutionException, InterruptedException {
+        FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                //noinspection unchecked
+                liveData.observe(provider, observer);
+                return null;
+            }
+        });
+        AppToolkitTaskExecutor.getInstance().executeOnMainThread(futureTask);
+        futureTask.get();
+    }
+
+    static class TestLifecycleOwner implements LifecycleOwner {
+
+        private LifecycleRegistry mLifecycle;
+
+        TestLifecycleOwner() {
+            mLifecycle = new LifecycleRegistry(this);
+        }
+
+        @Override
+        public Lifecycle getLifecycle() {
+            return mLifecycle;
+        }
+
+        void handleEvent(Lifecycle.Event event) {
+            mLifecycle.handleLifecycleEvent(event);
+        }
+    }
+
+    private static class LazyListObserver<T> implements Observer<LazyList<T>> {
+        private LazyList<T> mList;
+        public void reset() {
+            mList = null;
+        }
+
+        public LazyList<T> get() {
+            return mList;
+        }
+
+        @Override
+        public void onChanged(@Nullable LazyList<T> lazyList) {
+            mList = lazyList;
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ComplexQueryDataSourceTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ComplexQueryDataSourceTest.java
new file mode 100644
index 0000000..5b44c08
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ComplexQueryDataSourceTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.util.paging.CountedDataSource;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ComplexQueryDataSourceTest extends TestDatabaseTest {
+    /**
+     * Proper, keyed implementation.
+     */
+    public class KeyedUserQueryDataSource extends CountedDataSource<User> {
+        @Override
+        public int loadCount() {
+            return mUserDao.getUserCount();
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfterInitial(int position, int pageSize) {
+            return mUserDao.userComplexLimitOffset(pageSize, position + 1);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfter(int currentEndIndex, @NonNull User currentEndItem,
+                int pageSize) {
+            return mUserDao.userComplexLoadAfter(
+                    currentEndItem.getLastName(),
+                    currentEndItem.getName(),
+                    currentEndItem.getId(),
+                    pageSize);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadBefore(int currentBeginIndex, @NonNull User currentBeginItem,
+                int pageSize) {
+            return mUserDao.userComplexLoadBefore(
+                    currentBeginItem.getLastName(),
+                    currentBeginItem.getName(),
+                    currentBeginItem.getId(),
+                    pageSize);
+        }
+    }
+
+    /**
+     * Lazy, LIMIT/OFFSET implementation.
+     */
+    public class OffsetUserQueryDataSource extends CountedDataSource<User> {
+
+        @Override
+        public int loadCount() {
+            return mUserDao.getUserCount();
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfterInitial(int position, int pageSize) {
+            return mUserDao.userComplexLimitOffset(pageSize, position + 1);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfter(int currentEndIndex, @NonNull User currentEndItem,
+                int pageSize) {
+            return mUserDao.userComplexLimitOffset(pageSize, currentEndIndex + 1);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadBefore(int currentBeginIndex, @NonNull User currentBeginItem,
+                int pageSize) {
+            int targetOffset = currentBeginIndex - pageSize;
+            int offset = Math.max(0, targetOffset);
+            int limit = Math.min(pageSize, pageSize + targetOffset);
+
+            List<User> users = mUserDao.userComplexLimitOffset(limit, offset);
+            Collections.reverse(users); // :P
+            return users;
+        }
+    }
+
+    private static final User[] USERS_BY_LAST_FIRST_ID = new User[100];
+
+    @BeforeClass
+    public static void setupClass() {
+        String[] lastNames = new String[10];
+
+        String[] firstNames = new String[10];
+        for (int i = 0; i < 10; i++) {
+            lastNames[i] = "f" + (char) ('a' + i);
+            firstNames[i] = "l" + (char) ('a' + i);
+        }
+
+        for (int i = 0; i < USERS_BY_LAST_FIRST_ID.length; i++) {
+            User user = new User();
+            user.setId(i);
+            user.setName(firstNames[i % 10]);
+            user.setLastName(lastNames[(i / 10) % 10]);
+            user.setAge((int) (10 + Math.random() * 50));
+            user.setCustomField(UUID.randomUUID().toString());
+            user.setBirthday(new Date());
+            USERS_BY_LAST_FIRST_ID[i] = user;
+        }
+    }
+
+    @Before
+    public void setup() {
+        mUserDao.insertAll(USERS_BY_LAST_FIRST_ID);
+
+        Arrays.sort(USERS_BY_LAST_FIRST_ID, new Comparator<User>() {
+            @Override
+            public int compare(User o1, User o2) {
+                int diff = o2.getLastName().compareTo(o1.getLastName());
+                if (diff != 0) {
+                    return diff;
+                }
+                diff = o2.getName().compareTo(o1.getName());
+                if (diff != 0) {
+                    return -diff; // Note: 'mName' is ASC, therefore diff reversed
+                }
+
+                return o2.getId() - o1.getId();
+            }
+        });
+    }
+
+    @Test
+    public void testKeyedQueryDataSource() {
+        QueryDataSourceTest.verifyUserDataSource(USERS_BY_LAST_FIRST_ID,
+                new KeyedUserQueryDataSource());
+    }
+
+    @Test
+    public void testIndexedQueryDataSourceFull() {
+        QueryDataSourceTest.verifyUserDataSource(USERS_BY_LAST_FIRST_ID,
+                new OffsetUserQueryDataSource());
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ConstructorTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ConstructorTest.java
new file mode 100644
index 0000000..4e8fa97
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ConstructorTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SuppressWarnings("SqlNoDataSourceInspection")
+@SmallTest
+public class ConstructorTest {
+    @Database(version = 1, entities = {FullConstructor.class, PartialConstructor.class},
+            exportSchema = false)
+    abstract static class MyDb extends RoomDatabase {
+        abstract MyDao dao();
+    }
+
+    @Dao
+    interface MyDao {
+        @Insert
+        void insertFull(FullConstructor... full);
+
+        @Query("SELECT * FROM fc WHERE a = :a")
+        FullConstructor loadFull(int a);
+
+        @Insert
+        void insertPartial(PartialConstructor... partial);
+
+        @Query("SELECT * FROM pc WHERE a = :a")
+        PartialConstructor loadPartial(int a);
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @Entity(tableName = "fc")
+    static class FullConstructor {
+        @PrimaryKey
+        public final int a;
+        public final int b;
+        @Embedded
+        public final MyEmbedded embedded;
+
+        FullConstructor(int a, int b, MyEmbedded embedded) {
+            this.a = a;
+            this.b = b;
+            this.embedded = embedded;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            FullConstructor that = (FullConstructor) o;
+
+            if (a != that.a) return false;
+            //noinspection SimplifiableIfStatement
+            if (b != that.b) return false;
+            return embedded != null ? embedded.equals(that.embedded)
+                    : that.embedded == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = a;
+            result = 31 * result + b;
+            result = 31 * result + (embedded != null ? embedded.hashCode() : 0);
+            return result;
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @Entity(tableName = "pc")
+    static class PartialConstructor {
+        @PrimaryKey
+        public final int a;
+        public int b;
+        @Embedded
+        private MyEmbedded mEmbedded;
+
+        PartialConstructor(int a) {
+            this.a = a;
+        }
+
+        public MyEmbedded getEmbedded() {
+            return mEmbedded;
+        }
+
+        public void setEmbedded(MyEmbedded embedded) {
+            mEmbedded = embedded;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            PartialConstructor that = (PartialConstructor) o;
+
+            if (a != that.a) return false;
+            //noinspection SimplifiableIfStatement
+            if (b != that.b) return false;
+            return mEmbedded != null ? mEmbedded.equals(that.mEmbedded)
+                    : that.mEmbedded == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = a;
+            result = 31 * result + b;
+            result = 31 * result + (mEmbedded != null ? mEmbedded.hashCode() : 0);
+            return result;
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    static class MyEmbedded {
+        public final String text;
+
+        MyEmbedded(String text) {
+            this.text = text;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            MyEmbedded that = (MyEmbedded) o;
+
+            return text != null ? text.equals(that.text) : that.text == null;
+        }
+
+        @Override
+        public int hashCode() {
+            return text != null ? text.hashCode() : 0;
+        }
+    }
+
+    private MyDb mDb;
+    private MyDao mDao;
+
+    @Before
+    public void init() {
+        mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(), MyDb.class)
+                .build();
+        mDao = mDb.dao();
+    }
+
+    @Test
+    public void insertAndReadFullConstructor() {
+        FullConstructor inserted = new FullConstructor(1, 2, null);
+        mDao.insertFull(inserted);
+        final FullConstructor load = mDao.loadFull(1);
+        assertThat(load, is(inserted));
+    }
+
+    @Test
+    public void insertAndReadPartial() {
+        PartialConstructor item = new PartialConstructor(3);
+        item.b = 7;
+        mDao.insertPartial(item);
+        PartialConstructor load = mDao.loadPartial(3);
+        assertThat(load, is(item));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CustomDatabaseTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CustomDatabaseTest.java
new file mode 100644
index 0000000..353c2e3
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CustomDatabaseTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.mockito.AdditionalAnswers.delegatesTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.SupportSQLiteQuery;
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.integration.testapp.database.Customer;
+import android.arch.persistence.room.integration.testapp.database.SampleDatabase;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CustomDatabaseTest {
+
+    @Test
+    public void invalidationTrackerAfterClose() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        RoomDatabase.Builder<SampleDatabase> builder =
+                Room.databaseBuilder(context, SampleDatabase.class, "db")
+                        .openHelperFactory(new RethrowExceptionFactory());
+        Customer customer = new Customer();
+        for (int i = 0; i < 100; i++) {
+            SampleDatabase db = builder.build();
+            customer.setId(i);
+            db.getCustomerDao().insert(customer);
+            // Give InvalidationTracker enough time to start #mRefreshRunnable and pass the
+            // initialization check.
+            SystemClock.sleep(1);
+            // InvalidationTracker#mRefreshRunnable will cause race condition if its database query
+            // happens after close.
+            db.close();
+        }
+    }
+
+    /**
+     * This is mostly {@link FrameworkSQLiteOpenHelperFactory}, but the returned {@link
+     * SupportSQLiteDatabase} fails with {@link RuntimeException} instead of {@link
+     * IllegalStateException} or {@link SQLiteException}. This way, we can simulate custom database
+     * implementation that throws its own exception types.
+     */
+    private static class RethrowExceptionFactory implements SupportSQLiteOpenHelper.Factory {
+
+        @Override
+        public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
+            final FrameworkSQLiteOpenHelperFactory factory = new FrameworkSQLiteOpenHelperFactory();
+            final SupportSQLiteOpenHelper helper = factory.create(configuration);
+            SupportSQLiteOpenHelper helperMock = mock(SupportSQLiteOpenHelper.class,
+                    delegatesTo(helper));
+            // Inject mocks to the object hierarchy.
+            doAnswer(new Answer() {
+                @Override
+                public SupportSQLiteDatabase answer(InvocationOnMock invocation)
+                        throws Throwable {
+                    final SupportSQLiteDatabase db = helper.getWritableDatabase();
+                    SupportSQLiteDatabase dbMock = mock(SupportSQLiteDatabase.class,
+                            delegatesTo(db));
+                    doAnswer(new Answer() {
+                        @Override
+                        public Cursor answer(InvocationOnMock invocation) throws Throwable {
+                            SupportSQLiteQuery query = invocation.getArgument(0);
+                            try {
+                                return db.query(query);
+                            } catch (IllegalStateException | SQLiteException e) {
+                                // Rethrow the exception in order to simulate the way custom
+                                // database implementation throws its own exception types.
+                                throw new RuntimeException("closed", e);
+                            }
+                        }
+                    }).when(dbMock).query(any(SupportSQLiteQuery.class));
+                    return dbMock;
+                }
+            }).when(helperMock).getWritableDatabase();
+            return helperMock;
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DatabaseCallbackTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DatabaseCallbackTest.java
new file mode 100644
index 0000000..579b3e4
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DatabaseCallbackTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.core.IsCollectionContaining.hasItem;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DatabaseCallbackTest {
+
+    @Test
+    @MediumTest
+    public void createAndOpen() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        TestDatabaseCallback callback1 = new TestDatabaseCallback();
+        TestDatabase db1 = Room.databaseBuilder(context, TestDatabase.class, "test")
+                .addCallback(callback1)
+                .build();
+        assertFalse(callback1.mCreated);
+        assertFalse(callback1.mOpened);
+        User user1 = TestUtil.createUser(3);
+        user1.setName("george");
+        db1.getUserDao().insert(user1);
+        assertTrue(callback1.mCreated);
+        assertTrue(callback1.mOpened);
+        TestDatabaseCallback callback2 = new TestDatabaseCallback();
+        TestDatabase db2 = Room.databaseBuilder(context, TestDatabase.class, "test")
+                .addCallback(callback2)
+                .build();
+        assertFalse(callback2.mCreated);
+        assertFalse(callback2.mOpened);
+        User user2 = db2.getUserDao().load(3);
+        assertThat(user2.getName(), is("george"));
+        assertFalse(callback2.mCreated); // Not called; already created by db1
+        assertTrue(callback2.mOpened);
+    }
+
+    @Test
+    @SmallTest
+    public void writeOnCreate() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
+                .addCallback(new RoomDatabase.Callback() {
+                    @Override
+                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
+                        Cursor cursor = null;
+                        try {
+                            cursor = db.query(
+                                    "SELECT name FROM sqlite_master WHERE type = 'table'");
+                            ArrayList<String> names = new ArrayList<>();
+                            while (cursor.moveToNext()) {
+                                names.add(cursor.getString(0));
+                            }
+                            assertThat(names, hasItem("User"));
+                        } finally {
+                            if (cursor != null) {
+                                cursor.close();
+                            }
+                        }
+                    }
+                })
+                .build();
+        List<Integer> ids = db.getUserDao().loadIds();
+        assertThat(ids, is(empty()));
+    }
+
+    public static class TestDatabaseCallback extends RoomDatabase.Callback {
+
+        boolean mCreated;
+        boolean mOpened;
+
+        @Override
+        public void onCreate(@NonNull SupportSQLiteDatabase db) {
+            mCreated = true;
+        }
+
+        @Override
+        public void onOpen(@NonNull SupportSQLiteDatabase db) {
+            mOpened = true;
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/EmbeddedTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/EmbeddedTest.java
new file mode 100644
index 0000000..d680f3d
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/EmbeddedTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.dao.PetCoupleDao;
+import android.arch.persistence.room.integration.testapp.dao.PetDao;
+import android.arch.persistence.room.integration.testapp.dao.SchoolDao;
+import android.arch.persistence.room.integration.testapp.dao.UserDao;
+import android.arch.persistence.room.integration.testapp.dao.UserPetDao;
+import android.arch.persistence.room.integration.testapp.vo.Coordinates;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.PetCouple;
+import android.arch.persistence.room.integration.testapp.vo.School;
+import android.arch.persistence.room.integration.testapp.vo.SchoolRef;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.persistence.room.integration.testapp.vo.UserAndPet;
+import android.arch.persistence.room.integration.testapp.vo.UserAndPetNonNull;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmbeddedTest {
+    private UserDao mUserDao;
+    private PetDao mPetDao;
+    private UserPetDao mUserPetDao;
+    private SchoolDao mSchoolDao;
+    private PetCoupleDao mPetCoupleDao;
+
+    @Before
+    public void createDb() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        mUserDao = db.getUserDao();
+        mPetDao = db.getPetDao();
+        mUserPetDao = db.getUserPetDao();
+        mSchoolDao = db.getSchoolDao();
+        mPetCoupleDao = db.getPetCoupleDao();
+    }
+
+    @Test
+    public void loadAll() {
+        Pet pet = TestUtil.createPet(1);
+        User user = TestUtil.createUser(2);
+        pet.setUserId(user.getId());
+        mUserDao.insert(user);
+        mPetDao.insertOrReplace(pet);
+        List<UserAndPet> all = mUserPetDao.loadAll();
+        assertThat(all.size(), is(1));
+        assertThat(all.get(0).getUser(), is(user));
+        assertThat(all.get(0).getPet(), is(pet));
+    }
+
+    @Test
+    public void loadFromUsers() {
+        Pet pet = TestUtil.createPet(1);
+        User user = TestUtil.createUser(2);
+        pet.setUserId(user.getId());
+        mUserDao.insert(user);
+        mPetDao.insertOrReplace(pet);
+        List<UserAndPet> all = mUserPetDao.loadUsers();
+        assertThat(all.size(), is(1));
+        assertThat(all.get(0).getUser(), is(user));
+        assertThat(all.get(0).getPet(), is(pet));
+    }
+
+    @Test
+    public void loadFromUsersWithNullPet() {
+        User user = TestUtil.createUser(2);
+        mUserDao.insert(user);
+        List<UserAndPet> all = mUserPetDao.loadUsers();
+        assertThat(all.size(), is(1));
+        assertThat(all.get(0).getUser(), is(user));
+        assertThat(all.get(0).getPet(), is(nullValue()));
+    }
+
+    @Test
+    public void loadFromUsersWithNonNullPet() {
+        User user = TestUtil.createUser(2);
+        mUserDao.insert(user);
+        List<UserAndPetNonNull> all = mUserPetDao.loadUsersWithNonNullPet();
+        assertThat(all.size(), is(1));
+        assertThat(all.get(0).getUser(), is(user));
+        assertThat(all.get(0).getPet(), is(new Pet()));
+    }
+
+    @Test
+    public void loadFromPets() {
+        Pet pet = TestUtil.createPet(1);
+        User user = TestUtil.createUser(2);
+        pet.setUserId(user.getId());
+        mUserDao.insert(user);
+        mPetDao.insertOrReplace(pet);
+        List<UserAndPet> all = mUserPetDao.loadPets();
+        assertThat(all.size(), is(1));
+        assertThat(all.get(0).getUser(), is(user));
+        assertThat(all.get(0).getPet(), is(pet));
+    }
+
+    @Test
+    public void loadFromPetsWithNullUser() {
+        Pet pet = TestUtil.createPet(1);
+        mPetDao.insertOrReplace(pet);
+        List<UserAndPet> all = mUserPetDao.loadPets();
+        assertThat(all.size(), is(1));
+        assertThat(all.get(0).getUser(), is(nullValue()));
+        assertThat(all.get(0).getPet(), is(pet));
+    }
+
+    @Test
+    public void findSchoolByStreet() {
+        School school = TestUtil.createSchool(3, 5);
+        school.getAddress().setStreet("foo");
+        mSchoolDao.insert(school);
+        List<School> result = mSchoolDao.findByStreet("foo");
+        assertThat(result.size(), is(1));
+        assertThat(result.get(0), is(school));
+    }
+
+    @Test
+    public void loadSubFieldsAsPojo() throws Exception {
+        loadSubFieldsTest(new Callable<List<School>>() {
+            @Override
+            public List<School> call() throws Exception {
+                List<School> result = new ArrayList<>();
+                for (SchoolRef ref : mSchoolDao.schoolAndManagerNamesAsPojo()) {
+                    result.add(ref);
+                }
+                return result;
+            }
+        });
+    }
+
+    @Test
+    public void loadSubFieldsAsEntity() throws Exception {
+        loadSubFieldsTest(new Callable<List<School>>() {
+            @Override
+            public List<School> call() throws Exception {
+                return mSchoolDao.schoolAndManagerNames();
+            }
+        });
+    }
+
+    public void loadSubFieldsTest(Callable<List<School>> loader) throws Exception {
+        School school = TestUtil.createSchool(3, 5);
+        school.setName("MTV High");
+        school.getManager().setName("chet");
+        mSchoolDao.insert(school);
+
+        School school2 = TestUtil.createSchool(4, 6);
+        school2.setName("MTV Low");
+        school2.setManager(null);
+        mSchoolDao.insert(school2);
+
+        List<School> schools = loader.call();
+        assertThat(schools.size(), is(2));
+        assertThat(schools.get(0).getName(), is("MTV High"));
+        assertThat(schools.get(1).getName(), is("MTV Low"));
+        assertThat(schools.get(0).address, nullValue());
+        assertThat(schools.get(1).address, nullValue());
+        assertThat(schools.get(0).getManager(), notNullValue());
+        assertThat(schools.get(1).getManager(), nullValue());
+        assertThat(schools.get(0).getManager().getName(), is("chet"));
+    }
+
+    @Test
+    public void loadNestedSub() {
+        School school = TestUtil.createSchool(3, 5);
+        school.getAddress().getCoordinates().lat = 3.;
+        school.getAddress().getCoordinates().lng = 4.;
+        mSchoolDao.insert(school);
+        Coordinates coordinates = mSchoolDao.loadCoordinates(3);
+        assertThat(coordinates.lat, is(3.));
+        assertThat(coordinates.lng, is(4.));
+
+        School asSchool = mSchoolDao.loadCoordinatesAsSchool(3);
+        assertThat(asSchool.address.getCoordinates().lat, is(3.));
+        assertThat(asSchool.address.getCoordinates().lng, is(4.));
+        // didn't as for it so don't load
+        assertThat(asSchool.getManager(), nullValue());
+        assertThat(asSchool.address.getStreet(), nullValue());
+    }
+
+    @Test
+    public void sameFieldType() {
+        Pet male = TestUtil.createPet(3);
+        Pet female = TestUtil.createPet(5);
+        PetCouple petCouple = new PetCouple();
+        petCouple.id = "foo";
+        petCouple.male = male;
+        petCouple.setFemale(female);
+        mPetCoupleDao.insert(petCouple);
+        List<PetCouple> petCouples = mPetCoupleDao.loadAll();
+        assertThat(petCouples.size(), is(1));
+        PetCouple loaded = petCouples.get(0);
+        assertThat(loaded.id, is("foo"));
+        assertThat(loaded.male, is(male));
+        assertThat(loaded.getFemale(), is(female));
+    }
+
+    @Test
+    public void sameFieldOneNull() {
+        Pet loneWolf = TestUtil.createPet(3);
+        PetCouple petCouple = new PetCouple();
+        petCouple.id = "foo";
+        petCouple.male = loneWolf;
+        mPetCoupleDao.insert(petCouple);
+        List<PetCouple> petCouples = mPetCoupleDao.loadAll();
+        assertThat(petCouples.size(), is(1));
+        PetCouple loaded = petCouples.get(0);
+        assertThat(loaded.id, is("foo"));
+        assertThat(loaded.male, is(loneWolf));
+        assertThat(loaded.getFemale(), is(nullValue()));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ForeignKeyTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ForeignKeyTest.java
new file mode 100644
index 0000000..502d1f8
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/ForeignKeyTest.java
@@ -0,0 +1,425 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.either;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Delete;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.arch.persistence.room.Ignore;
+import android.arch.persistence.room.Index;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.database.sqlite.SQLiteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ForeignKeyTest {
+    @Database(version = 1, entities = {A.class, B.class, C.class, D.class, E.class},
+            exportSchema = false)
+    abstract static class ForeignKeyDb extends RoomDatabase {
+        abstract FkDao dao();
+    }
+
+    @SuppressWarnings({"SqlNoDataSourceInspection", "SameParameterValue"})
+    @Dao
+    interface FkDao {
+        @Insert
+        void insert(A... a);
+
+        @Insert
+        void insert(B... b);
+
+        @Insert
+        void insert(C... c);
+
+        @Insert
+        void insert(D... d);
+
+        @Query("SELECT * FROM A WHERE id = :id")
+        A loadA(int id);
+
+        @Query("SELECT * FROM B WHERE id = :id")
+        B loadB(int id);
+
+        @Query("SELECT * FROM C WHERE id = :id")
+        C loadC(int id);
+
+        @Query("SELECT * FROM D WHERE id = :id")
+        D loadD(int id);
+
+        @Query("SELECT * FROM E WHERE id = :id")
+        E loadE(int id);
+
+        @Delete
+        void delete(A... a);
+
+        @Delete
+        void delete(B... b);
+
+        @Delete
+        void delete(C... c);
+
+        @Query("UPDATE A SET name = :newName WHERE id = :id")
+        void changeNameA(int id, String newName);
+
+        @Insert
+        void insert(E... e);
+
+
+    }
+
+    @Entity(indices = {@Index(value = "name", unique = true),
+            @Index(value = {"name", "lastName"}, unique = true)})
+    static class A {
+        @PrimaryKey(autoGenerate = true)
+        public int id;
+        public String name;
+        public String lastName;
+
+        A(String name) {
+            this.name = name;
+        }
+
+        @Ignore
+        A(String name, String lastName) {
+            this.name = name;
+            this.lastName = lastName;
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @Entity(foreignKeys = {
+            @ForeignKey(entity = A.class,
+                    parentColumns = "name",
+                    childColumns = "aName")})
+
+    static class B {
+        @PrimaryKey(autoGenerate = true)
+        public int id;
+        public String aName;
+
+        B(String aName) {
+            this.aName = aName;
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @Entity(foreignKeys = {
+            @ForeignKey(entity = A.class,
+                    parentColumns = "name",
+                    childColumns = "aName",
+                    deferred = true)})
+    static class C {
+        @PrimaryKey(autoGenerate = true)
+        public int id;
+        public String aName;
+
+        C(String aName) {
+            this.aName = aName;
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @Entity(foreignKeys = {
+            @ForeignKey(entity = A.class,
+                    parentColumns = "name",
+                    childColumns = "aName",
+                    onDelete = ForeignKey.CASCADE,
+                    onUpdate = ForeignKey.CASCADE)})
+    static class D {
+        @PrimaryKey(autoGenerate = true)
+        public int id;
+        public String aName;
+
+        D(String aName) {
+            this.aName = aName;
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @Entity(foreignKeys = {
+            @ForeignKey(entity = A.class,
+                    parentColumns = {"name", "lastName"},
+                    childColumns = {"aName", "aLastName"},
+                    onDelete = ForeignKey.SET_NULL,
+                    onUpdate = ForeignKey.CASCADE)})
+    static class E {
+        @PrimaryKey(autoGenerate = true)
+        public int id;
+        public String aName;
+        public String aLastName;
+
+        E() {
+        }
+
+        @Ignore
+        E(String aName, String aLastName) {
+            this.aName = aName;
+            this.aLastName = aLastName;
+        }
+    }
+
+
+    private ForeignKeyDb mDb;
+    private FkDao mDao;
+
+    @Before
+    public void openDb() {
+        mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
+                ForeignKeyDb.class).build();
+        mDao = mDb.dao();
+    }
+
+    @Test
+    public void simpleForeignKeyFailure() {
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                mDao.insert(new B("foo"));
+            }
+        });
+        assertThat(t, instanceOf(SQLiteException.class));
+        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
+    }
+
+    @Test
+    public void simpleForeignKeyDeferredFailure() {
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                mDao.insert(new C("foo"));
+            }
+        });
+        assertThat(t, instanceOf(SQLiteException.class));
+        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
+    }
+
+    @Test
+    public void immediateForeignKeyFailure() {
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                try {
+                    mDb.beginTransaction();
+                    mDao.insert(new B("foo"));
+                    mDao.insert(new A("foo"));
+                    mDb.setTransactionSuccessful();
+                } finally {
+                    mDb.endTransaction();
+                }
+            }
+        });
+        assertThat(t, instanceOf(SQLiteException.class));
+    }
+
+    @Test
+    public void deferredForeignKeySuccess() {
+        try {
+            mDb.beginTransaction();
+            mDao.insert(new C("foo"));
+            mDao.insert(new A("foo"));
+            mDb.setTransactionSuccessful();
+        } finally {
+            mDb.endTransaction();
+        }
+        assertThat(mDao.loadA(1), notNullValue());
+        assertThat(mDao.loadC(1), notNullValue());
+    }
+
+    @Test
+    public void onDelete_noAction() {
+        mDao.insert(new A("a1"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new B("a1"));
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                mDao.delete(a);
+            }
+        });
+        assertThat(t, instanceOf(SQLiteException.class));
+        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
+    }
+
+    @Test
+    public void onDelete_noAction_withTransaction() {
+        mDao.insert(new A("a1"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new B("a1"));
+        final B b = mDao.loadB(1);
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                deleteInTransaction(a, b);
+            }
+        });
+        assertThat(t, instanceOf(SQLiteException.class));
+        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
+    }
+
+    @Test
+    public void onDelete_noAction_deferred() {
+        mDao.insert(new A("a1"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new C("a1"));
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                mDao.delete(a);
+            }
+        });
+        assertThat(t, instanceOf(SQLiteException.class));
+        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
+    }
+
+    @Test
+    public void onDelete_noAction__deferredWithTransaction() {
+        mDao.insert(new A("a1"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new C("a1"));
+        final C c = mDao.loadC(1);
+        deleteInTransaction(a, c);
+    }
+
+    @Test
+    public void onDelete_cascade() {
+        mDao.insert(new A("a1"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new D("a1"));
+        final D d = mDao.loadD(1);
+        assertThat("test sanity", d, notNullValue());
+        mDao.delete(a);
+        assertThat(mDao.loadD(1), nullValue());
+    }
+
+    @Test
+    public void onUpdate_cascade() {
+        mDao.insert(new A("a1"));
+        mDao.insert(new D("a1"));
+        final D d = mDao.loadD(1);
+        assertThat("test sanity", d, notNullValue());
+        mDao.changeNameA(1, "bla");
+        assertThat(mDao.loadD(1).aName, equalTo("bla"));
+        assertThat(mDao.loadA(1).name, equalTo("bla"));
+    }
+
+    @Test
+    public void multipleReferences() {
+        mDao.insert(new A("a1", "a2"));
+        final A a = mDao.loadA(1);
+        assertThat("test sanity", a, notNullValue());
+        Throwable t = catchException(new ThrowingRunnable() {
+            @Override
+            public void run() throws Exception {
+                mDao.insert(new E("a1", "dsa"));
+            }
+        });
+        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
+    }
+
+    @Test
+    public void onDelete_setNull_multipleReferences() {
+        mDao.insert(new A("a1", "a2"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new E("a1", "a2"));
+        assertThat(mDao.loadE(1), notNullValue());
+        mDao.delete(a);
+        E e = mDao.loadE(1);
+        assertThat(e, notNullValue());
+        assertThat(e.aName, nullValue());
+        assertThat(e.aLastName, nullValue());
+    }
+
+    @Test
+    public void onUpdate_cascade_multipleReferences() {
+        mDao.insert(new A("a1", "a2"));
+        final A a = mDao.loadA(1);
+        mDao.insert(new E("a1", "a2"));
+        assertThat(mDao.loadE(1), notNullValue());
+        mDao.changeNameA(1, "foo");
+        assertThat(mDao.loadE(1), notNullValue());
+        assertThat(mDao.loadE(1).aName, equalTo("foo"));
+        assertThat(mDao.loadE(1).aLastName, equalTo("a2"));
+    }
+
+    private static Matcher<String> foreignKeyErrorMessage() {
+        return either(containsString("FOREIGN KEY"))
+                .or(both(containsString("CODE 19")).and(containsString("CONSTRAINT FAILED")));
+    }
+
+    @SuppressWarnings("Duplicates")
+    private void deleteInTransaction(A a, B b) {
+        mDb.beginTransaction();
+        try {
+            mDao.delete(a);
+            mDao.delete(b);
+            mDb.setTransactionSuccessful();
+        } finally {
+            mDb.endTransaction();
+        }
+    }
+
+    @SuppressWarnings("Duplicates")
+    private void deleteInTransaction(A a, C c) {
+        mDb.beginTransaction();
+        try {
+            mDao.delete(a);
+            mDao.delete(c);
+            mDb.setTransactionSuccessful();
+        } finally {
+            mDb.endTransaction();
+        }
+    }
+
+    private static Throwable catchException(ThrowingRunnable throwingRunnable) {
+        try {
+            throwingRunnable.run();
+        } catch (Throwable t) {
+            return t;
+        }
+        throw new RuntimeException("didn't throw an exception");
+    }
+
+    private interface ThrowingRunnable {
+        void run() throws Exception;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/IdentityDetectionTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/IdentityDetectionTest.java
new file mode 100644
index 0000000..1c1b503
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/IdentityDetectionTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.vo.User;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IdentityDetectionTest {
+    static final String TAG = "IdentityDetectionTest";
+    static final String DB_FILE_NAME = "identity_test_db";
+    TestDatabase mTestDatabase;
+    @Before
+    public void createTestDatabase() {
+        deleteDbFile();
+    }
+
+    @Test
+    public void reOpenWithoutIssues() {
+        openDb();
+        mTestDatabase.getUserDao().insert(TestUtil.createUser(3));
+        closeDb();
+        openDb();
+        User[] users = mTestDatabase.getUserDao().loadByIds(3);
+        assertThat(users.length, is(1));
+    }
+
+    @Test
+    public void reOpenChangedHash() {
+        openDb();
+        mTestDatabase.getUserDao().insert(TestUtil.createUser(3));
+        // change the hash
+        SupportSQLiteDatabase db = mTestDatabase.getOpenHelper().getWritableDatabase();
+        db.execSQL("UPDATE " + Room.MASTER_TABLE_NAME + " SET `identity_hash` = ?"
+                + " WHERE id = 42", new String[]{"bad hash"});
+        closeDb();
+        Throwable[] exceptions = new Throwable[1];
+        try {
+            openDb();
+            mTestDatabase.getUserDao().loadByIds(3);
+        } catch (Throwable t) {
+            exceptions[0] = t;
+            mTestDatabase = null;
+        }
+        assertThat(exceptions[0], instanceOf(IllegalStateException.class));
+    }
+
+    private void closeDb() {
+        mTestDatabase.getOpenHelper().close();
+    }
+
+    private void openDb() {
+        mTestDatabase = Room.databaseBuilder(InstrumentationRegistry.getTargetContext(),
+                TestDatabase.class, DB_FILE_NAME).build();
+    }
+
+    @After
+    public void clear() {
+        try {
+            if (mTestDatabase != null) {
+                closeDb();
+            }
+            deleteDbFile();
+        } finally {
+            Log.e(TAG, "could not close test database");
+        }
+    }
+
+    private void deleteDbFile() {
+        File testDb = InstrumentationRegistry.getTargetContext().getDatabasePath(DB_FILE_NAME);
+        testDb.delete();
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/IndexingTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/IndexingTest.java
new file mode 100644
index 0000000..16f916b
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/IndexingTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.Index;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IndexingTest {
+    @Entity(
+            tableName = "foo_table",
+            indices = {
+                    @Index({"field1", "field2"}),
+                    @Index(value = {"field2", "mId"}, unique = true),
+                    @Index(value = {"field2"}, unique = true, name = "customIndex"),
+            })
+    static class Entity1 {
+        @PrimaryKey
+        public int mId;
+        public String field1;
+        public String field2;
+        @ColumnInfo(index = true, name = "my_field")
+        public String field3;
+    }
+
+    static class IndexInfo {
+        public String name;
+        @ColumnInfo(name = "tbl_name")
+        public String tableName;
+        public String sql;
+    }
+
+    @Dao
+    public interface SqlMasterDao {
+        @Query("SELECT * FROM sqlite_master WHERE type = 'index'")
+        List<IndexInfo> loadIndices();
+    }
+
+    @Database(entities = {Entity1.class}, version = 1, exportSchema = false)
+    abstract static class IndexingDb extends RoomDatabase {
+        abstract SqlMasterDao sqlMasterDao();
+    }
+
+    @Test
+    public void verifyIndices() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        IndexingDb db = Room.inMemoryDatabaseBuilder(context, IndexingDb.class).build();
+        List<IndexInfo> indices = db.sqlMasterDao().loadIndices();
+        assertThat(indices.size(), is(4));
+        for (IndexInfo info : indices) {
+            assertThat(info.tableName, is("foo_table"));
+        }
+        assertThat(indices.get(0).sql, is("CREATE INDEX `index_foo_table_field1_field2`"
+                + " ON `foo_table` (`field1`, `field2`)"));
+        assertThat(indices.get(1).sql, is("CREATE UNIQUE INDEX `index_foo_table_field2_mId`"
+                + " ON `foo_table` (`field2`, `mId`)"));
+        assertThat(indices.get(2).sql, is("CREATE UNIQUE INDEX `customIndex`"
+                + " ON `foo_table` (`field2`)"));
+        assertThat(indices.get(3).sql, is("CREATE INDEX `index_foo_table_my_field`"
+                + " ON `foo_table` (`my_field`)"));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
new file mode 100644
index 0000000..b6efe69
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.TaskExecutor;
+import android.arch.persistence.room.InvalidationTracker;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.dao.UserDao;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests invalidation tracking.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InvalidationTest {
+    private UserDao mUserDao;
+    private TestDatabase mDb;
+
+    @Before
+    public void createDb() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        mDb = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        mUserDao = mDb.getUserDao();
+    }
+
+    @Before
+    public void setSingleThreadedIO() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+            ExecutorService mIOExecutor = Executors.newSingleThreadExecutor();
+            Handler mHandler = new Handler(Looper.getMainLooper());
+
+            @Override
+            public void executeOnDiskIO(Runnable runnable) {
+                mIOExecutor.execute(runnable);
+            }
+
+            @Override
+            public void postToMainThread(Runnable runnable) {
+                mHandler.post(runnable);
+            }
+
+            @Override
+            public boolean isMainThread() {
+                return Thread.currentThread() == Looper.getMainLooper().getThread();
+            }
+        });
+    }
+
+    @After
+    public void clearExecutor() {
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    private void waitUntilIOThreadIsIdle() {
+        FutureTask<Void> future = new FutureTask<>(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                return null;
+            }
+        });
+        AppToolkitTaskExecutor.getInstance().executeOnDiskIO(future);
+        //noinspection TryWithIdenticalCatches
+        try {
+            future.get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testInvalidationOnUpdate() throws InterruptedException {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        LatchObserver observer = new LatchObserver(1, "User");
+        mDb.getInvalidationTracker().addObserver(observer);
+        mUserDao.updateById(3, "foo2");
+        waitUntilIOThreadIsIdle();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables(), hasSize(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("User"));
+    }
+
+    @Test
+    public void testInvalidationOnDelete() throws InterruptedException {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        LatchObserver observer = new LatchObserver(1, "User");
+        mDb.getInvalidationTracker().addObserver(observer);
+        mUserDao.delete(user);
+        waitUntilIOThreadIsIdle();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables(), hasSize(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("User"));
+    }
+
+    @Test
+    public void testInvalidationOnInsert() throws InterruptedException {
+        LatchObserver observer = new LatchObserver(1, "User");
+        mDb.getInvalidationTracker().addObserver(observer);
+        mUserDao.insert(TestUtil.createUser(3));
+        waitUntilIOThreadIsIdle();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables(), hasSize(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("User"));
+    }
+
+    @Test
+    public void testDontInvalidateOnLateInsert() throws InterruptedException {
+        LatchObserver observer = new LatchObserver(1, "User");
+        mUserDao.insert(TestUtil.createUser(3));
+        waitUntilIOThreadIsIdle();
+        mDb.getInvalidationTracker().addObserver(observer);
+        waitUntilIOThreadIsIdle();
+        assertThat(observer.await(), is(false));
+    }
+
+    private static class LatchObserver extends InvalidationTracker.Observer {
+        CountDownLatch mLatch;
+
+        private Set<String> mInvalidatedTables;
+
+        LatchObserver(int permits, String... tables) {
+            super(tables);
+            mLatch = new CountDownLatch(permits);
+        }
+
+        boolean await() throws InterruptedException {
+            return mLatch.await(5, TimeUnit.SECONDS);
+        }
+
+        @Override
+        public void onInvalidated(@NonNull Set<String> tables) {
+            mInvalidatedTables = tables;
+            mLatch.countDown();
+        }
+
+        Set<String> getInvalidatedTables() {
+            return mInvalidatedTables;
+        }
+    }
+}
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
new file mode 100644
index 0000000..927f0f4
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.testing.CountingTaskExecutorRule;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.persistence.room.InvalidationTrackerTrojan;
+import android.arch.persistence.room.integration.testapp.vo.AvgWeightByAge;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.persistence.room.integration.testapp.vo.UserAndAllPets;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests invalidation tracking.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LiveDataQueryTest extends TestDatabaseTest {
+    @Rule
+    public CountingTaskExecutorRule mExecutorRule = new CountingTaskExecutorRule();
+
+    @Test
+    public void observeById() throws InterruptedException, ExecutionException, TimeoutException {
+        final LiveData<User> userLiveData = mUserDao.liveUserById(5);
+        final TestLifecycleOwner testOwner = new TestLifecycleOwner();
+        testOwner.handleEvent(Lifecycle.Event.ON_CREATE);
+        final TestObserver<User> observer = new TestObserver<>();
+        observe(userLiveData, testOwner, observer);
+        assertThat(observer.hasValue(), is(false));
+        observer.reset();
+
+        testOwner.handleEvent(Lifecycle.Event.ON_START);
+        assertThat(observer.get(), is(nullValue()));
+
+        // another id
+        observer.reset();
+        mUserDao.insert(TestUtil.createUser(7));
+        assertThat(observer.get(), is(nullValue()));
+
+        observer.reset();
+        final User u5 = TestUtil.createUser(5);
+        mUserDao.insert(u5);
+        assertThat(observer.get(), is(notNullValue()));
+
+        u5.setName("foo-foo-foo");
+        observer.reset();
+        mUserDao.insertOrReplace(u5);
+        final User updated = observer.get();
+        assertThat(updated, is(notNullValue()));
+        assertThat(updated.getName(), is("foo-foo-foo"));
+
+        testOwner.handleEvent(Lifecycle.Event.ON_STOP);
+        observer.reset();
+        u5.setName("baba");
+        mUserDao.insertOrReplace(u5);
+        assertThat(observer.hasValue(), is(false));
+    }
+
+    @Test
+    public void observeListQuery() throws InterruptedException, ExecutionException,
+            TimeoutException {
+        final LiveData<List<User>> userLiveData = mUserDao.liveUsersListByName("frida");
+        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        final TestObserver<List<User>> observer = new TestObserver<>();
+        observe(userLiveData, lifecycleOwner, observer);
+        assertThat(observer.get(), is(Collections.<User>emptyList()));
+
+        observer.reset();
+        final User user1 = TestUtil.createUser(3);
+        user1.setName("dog frida");
+        mUserDao.insert(user1);
+        assertThat(observer.get(), is(Collections.singletonList(user1)));
+
+        observer.reset();
+        final User user2 = TestUtil.createUser(5);
+        user2.setName("does not match");
+        mUserDao.insert(user2);
+        assertThat(observer.get(), is(Collections.singletonList(user1)));
+
+        observer.reset();
+        user1.setName("i don't match either");
+        mUserDao.insertOrReplace(user1);
+        assertThat(observer.get(), is(Collections.<User>emptyList()));
+
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_STOP);
+
+        observer.reset();
+        final User user3 = TestUtil.createUser(9);
+        user3.setName("painter frida");
+        mUserDao.insertOrReplace(user3);
+        assertThat(observer.hasValue(), is(false));
+
+        observer.reset();
+        final User user4 = TestUtil.createUser(11);
+        user4.setName("friday");
+        mUserDao.insertOrReplace(user4);
+        assertThat(observer.hasValue(), is(false));
+
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        assertThat(observer.get(), is(Arrays.asList(user4, user3)));
+    }
+
+    @Test
+    public void liveDataWithPojo() throws ExecutionException, InterruptedException,
+            TimeoutException {
+        User[] users = TestUtil.createUsersArray(3, 5, 7, 9);
+        users[0].setAge(10);
+        users[0].setWeight(15);
+
+        users[1].setAge(20);
+        users[1].setWeight(25);
+
+        users[2].setAge(20);
+        users[2].setWeight(26);
+
+        users[3].setAge(10);
+        users[3].setWeight(21);
+
+        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+
+        final TestObserver<AvgWeightByAge> observer = new TestObserver<>();
+        LiveData<AvgWeightByAge> liveData = mUserDao.maxWeightByAgeGroup();
+
+        observe(liveData, lifecycleOwner, observer);
+        assertThat(observer.get(), is(nullValue()));
+
+        observer.reset();
+        mUserDao.insertAll(users);
+        assertThat(observer.get(), is(new AvgWeightByAge(20, 25.5f)));
+
+        observer.reset();
+        User user3 = mUserDao.load(3);
+        user3.setWeight(79);
+        mUserDao.insertOrReplace(user3);
+
+        assertThat(observer.get(), is(new AvgWeightByAge(10, 50)));
+    }
+
+    @Test
+    public void withRelation() throws ExecutionException, InterruptedException, TimeoutException {
+        final LiveData<UserAndAllPets> liveData = mUserPetDao.liveUserWithPets(3);
+        final TestObserver<UserAndAllPets> observer = new TestObserver<>();
+        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        observe(liveData, lifecycleOwner, observer);
+        assertThat(observer.get(), is(nullValue()));
+
+        observer.reset();
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        final UserAndAllPets noPets = observer.get();
+        assertThat(noPets.user, is(user));
+
+        observer.reset();
+        Pet[] pets = TestUtil.createPetsForUser(3, 1, 2);
+        mPetDao.insertAll(pets);
+
+        final UserAndAllPets withPets = observer.get();
+        assertThat(withPets.user, is(user));
+        assertThat(withPets.pets, is(Arrays.asList(pets)));
+    }
+
+    @MediumTest
+    @Test
+    public void handleGc() throws ExecutionException, InterruptedException, TimeoutException {
+        LiveData<User> liveData = mUserDao.liveUserById(3);
+        final TestObserver<User> observer = new TestObserver<>();
+        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        observe(liveData, lifecycleOwner, observer);
+        assertThat(observer.get(), is(nullValue()));
+        observer.reset();
+        final User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        assertThat(observer.get(), is(notNullValue()));
+        observer.reset();
+        forceGc();
+        String name = UUID.randomUUID().toString();
+        mUserDao.updateById(3, name);
+        assertThat(observer.get().getName(), is(name));
+
+        // release references
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                lifecycleOwner.handleEvent(Lifecycle.Event.ON_DESTROY);
+            }
+        });
+        WeakReference<LiveData> weakLiveData = new WeakReference<LiveData>(liveData);
+        //noinspection UnusedAssignment
+        liveData = null;
+        forceGc();
+        mUserDao.updateById(3, "Bar");
+        forceGc();
+        assertThat(InvalidationTrackerTrojan.countObservers(mDatabase.getInvalidationTracker()),
+                is(0));
+        assertThat(weakLiveData.get(), nullValue());
+    }
+
+    private void observe(final LiveData liveData, final LifecycleOwner provider,
+            final Observer observer) throws ExecutionException, InterruptedException {
+        FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                //noinspection unchecked
+                liveData.observe(provider, observer);
+                return null;
+            }
+        });
+        AppToolkitTaskExecutor.getInstance().executeOnMainThread(futureTask);
+        futureTask.get();
+    }
+
+    private void drain() throws TimeoutException, InterruptedException {
+        mExecutorRule.drainTasks(1, TimeUnit.MINUTES);
+    }
+
+    private static void forceGc() {
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+    }
+
+    static class TestLifecycleOwner implements LifecycleOwner {
+
+        private LifecycleRegistry mLifecycle;
+
+        TestLifecycleOwner() {
+            mLifecycle = new LifecycleRegistry(this);
+        }
+
+        @Override
+        public Lifecycle getLifecycle() {
+            return mLifecycle;
+        }
+
+        void handleEvent(Lifecycle.Event event) {
+            mLifecycle.handleLifecycleEvent(event);
+        }
+    }
+
+    private class TestObserver<T> implements Observer<T> {
+        private T mLastData;
+        private boolean mHasValue = false;
+
+        void reset() {
+            mHasValue = false;
+            mLastData = null;
+        }
+
+        @Override
+        public void onChanged(@Nullable T o) {
+            mLastData = o;
+            mHasValue = true;
+        }
+
+        boolean hasValue() throws TimeoutException, InterruptedException {
+            drain();
+            return mHasValue;
+        }
+
+        T get() throws InterruptedException, TimeoutException {
+            drain();
+            assertThat(hasValue(), is(true));
+            return mLastData;
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/MainThreadCheckTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/MainThreadCheckTest.java
new file mode 100644
index 0000000..f4ed5dd
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/MainThreadCheckTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.core.util.Function;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.content.Context;
+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.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MainThreadCheckTest {
+
+    @Test
+    public void testMainThread() {
+        final Throwable error = test(false, new Function<TestDatabase, Void>() {
+            @Override
+            public Void apply(TestDatabase db) {
+                db.getUserDao().load(3);
+                return null;
+            }
+        });
+        assertThat(error, notNullValue());
+        assertThat(error, instanceOf(IllegalStateException.class));
+    }
+
+    @Test
+    public void testFlowableOnMainThread() {
+        final Throwable error = test(false, new Function<TestDatabase, Void>() {
+            @Override
+            public Void apply(TestDatabase db) {
+                db.getUserDao().flowableUserById(3);
+                return null;
+            }
+        });
+        assertThat(error, nullValue());
+    }
+
+    @Test
+    public void testLiveDataOnMainThread() {
+        final Throwable error = test(false, new Function<TestDatabase, Void>() {
+            @Override
+            public Void apply(TestDatabase db) {
+                db.getUserDao().liveUserById(3);
+                return null;
+            }
+        });
+        assertThat(error, nullValue());
+    }
+
+    @Test
+    public void testAllowMainThread() {
+        final Throwable error = test(true, new Function<TestDatabase, Void>() {
+            @Override
+            public Void apply(TestDatabase db) {
+                db.getUserDao().load(3);
+                return null;
+            }
+        });
+        assertThat(error, nullValue());
+    }
+
+    private Throwable test(boolean allowMainThread, final Function<TestDatabase, Void> fun) {
+        Context context = InstrumentationRegistry.getTargetContext();
+        final RoomDatabase.Builder<TestDatabase> builder = Room.inMemoryDatabaseBuilder(
+                context, TestDatabase.class);
+        if (allowMainThread) {
+            builder.allowMainThreadQueries();
+        }
+        final TestDatabase db = builder.build();
+        final AtomicReference<Throwable> error = new AtomicReference<>();
+        try {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        fun.apply(db);
+                    } catch (Throwable t) {
+                        error.set(t);
+                    }
+                }
+            });
+        } finally {
+            db.close();
+        }
+        return error.get();
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PojoTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PojoTest.java
new file mode 100644
index 0000000..b43e274
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PojoTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.dao.UserDao;
+import android.arch.persistence.room.integration.testapp.vo.AvgWeightByAge;
+import android.arch.persistence.room.integration.testapp.vo.User;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class PojoTest {
+    private UserDao mUserDao;
+
+    @Before
+    public void createDb() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        mUserDao = db.getUserDao();
+    }
+
+    @Test
+    public void weightsByAge() {
+        User[] users = TestUtil.createUsersArray(3, 5, 7, 10);
+        users[0].setAge(10);
+        users[0].setWeight(20);
+
+        users[1].setAge(10);
+        users[1].setWeight(30);
+
+        users[2].setAge(15);
+        users[2].setWeight(12);
+
+        users[3].setAge(35);
+        users[3].setWeight(55);
+
+        mUserDao.insertAll(users);
+        assertThat(mUserDao.weightByAge(), is(
+                Arrays.asList(
+                        new AvgWeightByAge(35, 55),
+                        new AvgWeightByAge(10, 25),
+                        new AvgWeightByAge(15, 12)
+                )
+        ));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PojoWithRelationTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PojoWithRelationTest.java
new file mode 100644
index 0000000..45233f3
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PojoWithRelationTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.persistence.room.integration.testapp.vo.EmbeddedUserAndAllPets;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.Toy;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.persistence.room.integration.testapp.vo.UserAndAllPets;
+import android.arch.persistence.room.integration.testapp.vo.UserIdAndPetNames;
+import android.arch.persistence.room.integration.testapp.vo.UserWithPetsAndToys;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PojoWithRelationTest extends TestDatabaseTest {
+    @Test
+    public void fetchAll() {
+        User[] users = TestUtil.createUsersArray(1, 2, 3);
+        Pet[][] userPets = new Pet[3][];
+        mUserDao.insertAll(users);
+        for (User user : users) {
+            Pet[] pets = TestUtil.createPetsForUser(user.getId(), user.getId() * 10,
+                    user.getId() - 1);
+            mPetDao.insertAll(pets);
+            userPets[user.getId() - 1] = pets;
+        }
+        List<UserAndAllPets> usersAndPets = mUserPetDao.loadAllUsersWithTheirPets();
+        assertThat(usersAndPets.size(), is(3));
+        assertThat(usersAndPets.get(0).user, is(users[0]));
+        assertThat(usersAndPets.get(0).pets, is(Collections.<Pet>emptyList()));
+
+        assertThat(usersAndPets.get(1).user, is(users[1]));
+        assertThat(usersAndPets.get(1).pets, is(Arrays.asList(userPets[1])));
+
+        assertThat(usersAndPets.get(2).user, is(users[2]));
+        assertThat(usersAndPets.get(2).pets, is(Arrays.asList(userPets[2])));
+    }
+
+    private void createData() {
+        User[] users = TestUtil.createUsersArray(1, 2);
+        mUserDao.insertAll(users);
+        Pet user1_pet1 = TestUtil.createPet(1);
+        user1_pet1.setUserId(1);
+        user1_pet1.setName("pet1");
+        mPetDao.insertOrReplace(user1_pet1);
+
+        Pet user1_pet2 = TestUtil.createPet(2);
+        user1_pet2.setUserId(1);
+        user1_pet2.setName("pet2");
+        mPetDao.insertOrReplace(user1_pet2);
+
+        Pet user2_pet1 = TestUtil.createPet(3);
+        user2_pet1.setUserId(2);
+        user2_pet1.setName("pet3");
+        mPetDao.insertOrReplace(user2_pet1);
+    }
+
+    @Test
+    public void fetchWithNames() {
+        createData();
+
+        List<UserIdAndPetNames> usersAndPets = mUserPetDao.loadUserAndPetNames();
+        assertThat(usersAndPets.size(), is(2));
+        assertThat(usersAndPets.get(0).userId, is(1));
+        assertThat(usersAndPets.get(1).userId, is(2));
+        assertThat(usersAndPets.get(0).names, is(Arrays.asList("pet1", "pet2")));
+        assertThat(usersAndPets.get(1).names, is(Collections.singletonList("pet3")));
+    }
+
+    @Test
+    public void nested() {
+        createData();
+        Toy pet1_toy1 = new Toy();
+        pet1_toy1.setName("toy1");
+        pet1_toy1.setPetId(1);
+        Toy pet1_toy2 = new Toy();
+        pet1_toy2.setName("toy2");
+        pet1_toy2.setPetId(1);
+        mToyDao.insert(pet1_toy1, pet1_toy2);
+        List<UserWithPetsAndToys> userWithPetsAndToys = mUserPetDao.loadUserWithPetsAndToys();
+        assertThat(userWithPetsAndToys.size(), is(2));
+        UserWithPetsAndToys first = userWithPetsAndToys.get(0);
+        List<Toy> toys = first.pets.get(0).toys;
+        assertThat(toys.get(0).getName(), is("toy1"));
+        assertThat(toys.get(1).getName(), is("toy2"));
+        assertThat(userWithPetsAndToys.get(1).pets.get(0).toys, is(Collections.<Toy>emptyList()));
+    }
+
+    @Test
+    public void duplicateParentField() {
+        User[] users = TestUtil.createUsersArray(1, 2);
+        Pet[] pets_1 = TestUtil.createPetsForUser(1, 1, 2);
+        Pet[] pets_2 = TestUtil.createPetsForUser(2, 10, 1);
+        mUserDao.insertAll(users);
+        mPetDao.insertAll(pets_1);
+        mPetDao.insertAll(pets_2);
+        List<UserAndAllPets> userAndAllPets = mUserPetDao.unionByItself();
+        assertThat(userAndAllPets.size(), is(4));
+        for (int i = 0; i < 4; i++) {
+            assertThat("user at " + i, userAndAllPets.get(i).user, is(users[i % 2]));
+        }
+        assertThat(userAndAllPets.get(0).pets, is(Arrays.asList(pets_1)));
+        assertThat(userAndAllPets.get(2).pets, is(Arrays.asList(pets_1)));
+
+        assertThat(userAndAllPets.get(1).pets, is(Arrays.asList(pets_2)));
+        assertThat(userAndAllPets.get(3).pets, is(Arrays.asList(pets_2)));
+    }
+
+    @Test
+    public void embeddedRelation() {
+        createData();
+        EmbeddedUserAndAllPets relationContainer = mUserPetDao.loadUserAndPetsAsEmbedded(1);
+        assertThat(relationContainer.getUserAndAllPets(), notNullValue());
+        assertThat(relationContainer.getUserAndAllPets().user.getId(), is(1));
+        assertThat(relationContainer.getUserAndAllPets().pets.size(), is(2));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PrimaryKeyTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PrimaryKeyTest.java
new file mode 100644
index 0000000..97ce10c
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/PrimaryKeyTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.PKeyTestDatabase;
+import android.arch.persistence.room.integration.testapp.vo.IntAutoIncPKeyEntity;
+import android.arch.persistence.room.integration.testapp.vo.IntegerAutoIncPKeyEntity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PrimaryKeyTest {
+    private PKeyTestDatabase mDatabase;
+    @Before
+    public void setup() {
+        mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getTargetContext(),
+                PKeyTestDatabase.class).build();
+    }
+
+    @Test
+    public void integerTest() {
+        IntegerAutoIncPKeyEntity entity = new IntegerAutoIncPKeyEntity();
+        entity.data = "foo";
+        mDatabase.integerPKeyDao().insertMe(entity);
+        IntegerAutoIncPKeyEntity loaded = mDatabase.integerPKeyDao().getMe(1);
+        assertThat(loaded, notNullValue());
+        assertThat(loaded.data, is(entity.data));
+    }
+
+    @Test
+    public void dontOverrideNullable0() {
+        IntegerAutoIncPKeyEntity entity = new IntegerAutoIncPKeyEntity();
+        entity.pKey = 0;
+        entity.data = "foo";
+        mDatabase.integerPKeyDao().insertMe(entity);
+        IntegerAutoIncPKeyEntity loaded = mDatabase.integerPKeyDao().getMe(0);
+        assertThat(loaded, notNullValue());
+        assertThat(loaded.data, is(entity.data));
+    }
+
+    @Test
+    public void intTest() {
+        IntAutoIncPKeyEntity entity = new IntAutoIncPKeyEntity();
+        entity.data = "foo";
+        mDatabase.intPKeyDao().insertMe(entity);
+        IntAutoIncPKeyEntity loaded = mDatabase.intPKeyDao().getMe(1);
+        assertThat(loaded, notNullValue());
+        assertThat(loaded.data, is(entity.data));
+    }
+
+    @Test
+    public void getInsertedId() {
+        IntAutoIncPKeyEntity entity = new IntAutoIncPKeyEntity();
+        entity.data = "foo";
+        final long id = mDatabase.intPKeyDao().insertAndGetId(entity);
+        assertThat(mDatabase.intPKeyDao().getMe((int) id).data, is("foo"));
+    }
+
+    @Test
+    public void getInsertedIds() {
+        IntAutoIncPKeyEntity entity = new IntAutoIncPKeyEntity();
+        entity.data = "foo";
+        IntAutoIncPKeyEntity entity2 = new IntAutoIncPKeyEntity();
+        entity2.data = "foo2";
+        final long[] ids = mDatabase.intPKeyDao().insertAndGetIds(entity, entity2);
+        assertThat(mDatabase.intPKeyDao().loadDataById(ids), is(Arrays.asList("foo", "foo2")));
+    }
+
+    @Test
+    public void getInsertedIdFromInteger() {
+        IntegerAutoIncPKeyEntity entity = new IntegerAutoIncPKeyEntity();
+        entity.data = "foo";
+        final long id = mDatabase.integerPKeyDao().insertAndGetId(entity);
+        assertThat(mDatabase.integerPKeyDao().getMe((int) id).data, is("foo"));
+    }
+
+    @Test
+    public void getInsertedIdsFromInteger() {
+        IntegerAutoIncPKeyEntity entity = new IntegerAutoIncPKeyEntity();
+        entity.data = "foo";
+        IntegerAutoIncPKeyEntity entity2 = new IntegerAutoIncPKeyEntity();
+        entity2.data = "foo2";
+        final long[] ids = mDatabase.integerPKeyDao().insertAndGetIds(entity, entity2);
+        assertThat(mDatabase.integerPKeyDao().loadDataById(ids), is(Arrays.asList("foo", "foo2")));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java
new file mode 100644
index 0000000..eadb8db
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.util.paging.CountedDataSource;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class QueryDataSourceTest extends TestDatabaseTest {
+    /**
+     * Proper, keyed implementation.
+     */
+    public class KeyedUserQueryDataSource extends CountedDataSource<User> {
+        @Override
+        public int loadCount() {
+            return mUserDao.getUserCount();
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfterInitial(int position, int pageSize) {
+            return mUserDao.userNameLimitOffset(pageSize, position + 1);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfter(int currentEndIndex, @NonNull User currentEndItem,
+                int pageSize) {
+            return mUserDao.userNameLoadAfter(currentEndItem.getName(), pageSize);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadBefore(int currentBeginIndex, @NonNull User currentBeginItem,
+                int pageSize) {
+            return mUserDao.userNameLoadBefore(currentBeginItem.getName(), pageSize);
+        }
+    }
+
+    /**
+     * Lazy, LIMIT/OFFSET implementation.
+     */
+    public class OffsetUserQueryDataSource extends CountedDataSource<User> {
+
+        @Override
+        public int loadCount() {
+            return mUserDao.getUserCount();
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfterInitial(int position, int pageSize) {
+            return mUserDao.userNameLimitOffset(pageSize, position + 1);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadAfter(int currentEndIndex, @NonNull User currentEndItem,
+                int pageSize) {
+            return mUserDao.userNameLimitOffset(pageSize, currentEndIndex + 1);
+        }
+
+        @Nullable
+        @Override
+        public List<User> loadBefore(int currentBeginIndex, @NonNull User currentBeginItem,
+                int pageSize) {
+            int targetOffset = currentBeginIndex - pageSize;
+            int offset = Math.max(0, targetOffset);
+            int limit = Math.min(pageSize, pageSize + targetOffset);
+
+            List<User> users = mUserDao.userNameLimitOffset(limit, offset);
+            Collections.reverse(users); // :P
+            return users;
+        }
+    }
+
+    private static final User[] USERS_BY_NAME = new User[50];
+
+    @BeforeClass
+    public static void setupClass() {
+        for (int i = 0; i < USERS_BY_NAME.length; i++) {
+            USERS_BY_NAME[i] = TestUtil.createUser(i);
+        }
+    }
+
+    @Before
+    public void setup() {
+        mUserDao.insertAll(USERS_BY_NAME);
+
+        Arrays.sort(USERS_BY_NAME, new Comparator<User>() {
+            @Override
+            public int compare(User o1, User o2) {
+                return o2.getName().compareTo(o1.getName());
+            }
+        });
+    }
+
+    @Test
+    public void testKeyedQueryDataSource() {
+        verifyUserDataSource(USERS_BY_NAME, new KeyedUserQueryDataSource());
+    }
+
+    @Test
+    public void testIndexedQueryDataSourceFull() {
+        verifyUserDataSource(USERS_BY_NAME, new OffsetUserQueryDataSource());
+    }
+
+
+    public static void verifyUserDataSource(User[] expected, CountedDataSource<User> dataSource) {
+        List<User> list = new ArrayList<>();
+        List<User> p = dataSource.loadAfterInitial(14, 10);
+        assertNotNull(p);
+        list.addAll(p);
+
+        assertArrayEquals(Arrays.copyOfRange(expected, 15, 25), list.toArray());
+        p = dataSource.loadAfter(24, list.get(list.size() - 1), 10);
+        assertNotNull(p);
+        list.addAll(p);
+
+        assertArrayEquals(Arrays.copyOfRange(expected, 15, 35), list.toArray());
+
+        p = dataSource.loadBefore(15, list.get(0), 10);
+        assertNotNull(p);
+        for (User u : p) {
+            list.add(0, u);
+        }
+
+        assertArrayEquals(Arrays.copyOfRange(expected, 5, 35), list.toArray());
+
+        p = dataSource.loadBefore(5, list.get(0), 10);
+        assertNotNull(p);
+        for (User u : p) {
+            list.add(0, u);
+        }
+
+        assertArrayEquals(Arrays.copyOfRange(expected, 0, 35), list.toArray());
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RxJava2Test.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RxJava2Test.java
new file mode 100644
index 0000000..1bbc140
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RxJava2Test.java
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.TaskExecutor;
+import android.arch.persistence.room.EmptyResultSetException;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import io.reactivex.disposables.Disposable;
+import io.reactivex.observers.TestObserver;
+import io.reactivex.schedulers.TestScheduler;
+import io.reactivex.subscribers.TestSubscriber;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RxJava2Test extends TestDatabaseTest {
+
+    private TestScheduler mTestScheduler;
+
+    @Before
+    public void setupSchedulers() {
+        mTestScheduler = new TestScheduler();
+        mTestScheduler.start();
+        AppToolkitTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+            @Override
+            public void executeOnDiskIO(Runnable runnable) {
+                mTestScheduler.scheduleDirect(runnable);
+            }
+
+            @Override
+            public void postToMainThread(Runnable runnable) {
+                Assert.fail("no main thread in this test");
+            }
+
+            @Override
+            public boolean isMainThread() {
+                return false;
+            }
+        });
+    }
+
+    @After
+    public void clearSchedulers() {
+        mTestScheduler.shutdown();
+        AppToolkitTaskExecutor.getInstance().setDelegate(null);
+    }
+
+    private void drain() throws InterruptedException {
+        mTestScheduler.triggerActions();
+    }
+
+    @Test
+    public void maybeUser_Empty() throws InterruptedException {
+        TestObserver<User> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.maybeUserById(3).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        testObserver.assertNoValues();
+        disposable.dispose();
+    }
+
+    @Test
+    public void maybeUser_WithData() throws InterruptedException {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        TestObserver<User> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.maybeUserById(3).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        testObserver.assertValue(user);
+
+        disposable.dispose();
+    }
+
+    @Test
+    public void maybeUsers_EmptyList() throws InterruptedException {
+        TestObserver<List<User>> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.maybeUsersByIds(3, 5, 7).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        testObserver.assertValue(Collections.<User>emptyList());
+        disposable.dispose();
+    }
+
+    @Test
+    public void maybeUsers_WithValue() throws InterruptedException {
+        User[] users = TestUtil.createUsersArray(3, 5);
+        mUserDao.insertAll(users);
+        TestObserver<List<User>> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.maybeUsersByIds(3, 5, 7).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        // since this is a clean db, it is ok to rely on the order for the test.
+        testObserver.assertValue(Arrays.asList(users));
+        disposable.dispose();
+    }
+
+    @Test
+    public void singleUser_Empty() throws InterruptedException {
+        TestObserver<User> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.singleUserById(3).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        // figure out which error we should dispatch
+        testObserver.assertError(EmptyResultSetException.class);
+        testObserver.assertNoValues();
+        disposable.dispose();
+    }
+
+    @Test
+    public void singleUser_WithData() throws InterruptedException {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        TestObserver<User> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.singleUserById(3).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        testObserver.assertValue(user);
+
+        disposable.dispose();
+    }
+
+    @Test
+    public void singleUsers_EmptyList() throws InterruptedException {
+        TestObserver<List<User>> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.singleUsersByIds(3, 5, 7).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        testObserver.assertValue(Collections.<User>emptyList());
+        disposable.dispose();
+    }
+
+    @Test
+    public void singleUsers_WithValue() throws InterruptedException {
+        User[] users = TestUtil.createUsersArray(3, 5);
+        mUserDao.insertAll(users);
+        TestObserver<List<User>> testObserver = new TestObserver<>();
+        Disposable disposable = mUserDao.singleUsersByIds(3, 5, 7).observeOn(mTestScheduler)
+                .subscribeWith(testObserver);
+        drain();
+        testObserver.assertComplete();
+        // since this is a clean db, it is ok to rely on the order for the test.
+        testObserver.assertValue(Arrays.asList(users));
+        disposable.dispose();
+    }
+
+    @Test
+    public void observeOnce() throws InterruptedException {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        drain();
+        TestSubscriber<User> consumer = new TestSubscriber<>();
+        Disposable disposable = mUserDao.flowableUserById(3).subscribeWith(consumer);
+        drain();
+        consumer.assertValue(user);
+        disposable.dispose();
+    }
+
+    @Test
+    public void observeChangeAndDispose() throws InterruptedException {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        drain();
+        TestSubscriber<User> consumer = new TestSubscriber<>();
+        Disposable disposable = mUserDao.flowableUserById(3).observeOn(mTestScheduler)
+                .subscribeWith(consumer);
+        drain();
+        assertThat(consumer.values().get(0), is(user));
+        user.setName("rxy");
+        mUserDao.insertOrReplace(user);
+        drain();
+        User next = consumer.values().get(1);
+        assertThat(next, is(user));
+        disposable.dispose();
+        user.setName("foo");
+        mUserDao.insertOrReplace(user);
+        drain();
+        assertThat(consumer.valueCount(), is(2));
+    }
+
+    @Test
+    @MediumTest
+    public void observeEmpty() throws InterruptedException {
+        TestSubscriber<User> consumer = new TestSubscriber<>();
+        Disposable disposable = mUserDao.flowableUserById(3).observeOn(mTestScheduler)
+                .subscribeWith(consumer);
+        drain();
+        consumer.assertNoValues();
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        drain();
+        assertThat(consumer.values().get(0), is(user));
+        disposable.dispose();
+        user.setAge(88);
+        mUserDao.insertOrReplace(user);
+        drain();
+        assertThat(consumer.valueCount(), is(1));
+    }
+
+    @Test
+    public void flowableCountUsers() throws InterruptedException {
+        TestSubscriber<Integer> consumer = new TestSubscriber<>();
+        mUserDao.flowableCountUsers()
+                .observeOn(mTestScheduler)
+                .subscribe(consumer);
+        drain();
+        assertThat(consumer.values().get(0), is(0));
+        mUserDao.insertAll(TestUtil.createUsersArray(1, 3, 4, 6));
+        drain();
+        assertThat(consumer.values().get(1), is(4));
+        mUserDao.deleteByUids(3, 7);
+        drain();
+        assertThat(consumer.values().get(2), is(3));
+        mUserDao.deleteByUids(101);
+        drain();
+        assertThat(consumer.valueCount(), is(3));
+    }
+
+    @Test
+    @MediumTest
+    public void publisherCountUsers() throws InterruptedException {
+        TestSubscriber<Integer> subscriber = new TestSubscriber<>();
+        mUserDao.publisherCountUsers().subscribe(subscriber);
+        drain();
+        subscriber.assertSubscribed();
+        subscriber.request(2);
+        drain();
+        subscriber.assertValue(0);
+        mUserDao.insert(TestUtil.createUser(2));
+        drain();
+        subscriber.assertValues(0, 1);
+        subscriber.cancel();
+        subscriber.assertNoErrors();
+    }
+}
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
new file mode 100644
index 0000000..2b4a0e9
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -0,0 +1,495 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.dao.BlobEntityDao;
+import android.arch.persistence.room.integration.testapp.dao.PetDao;
+import android.arch.persistence.room.integration.testapp.dao.ProductDao;
+import android.arch.persistence.room.integration.testapp.dao.UserDao;
+import android.arch.persistence.room.integration.testapp.dao.UserPetDao;
+import android.arch.persistence.room.integration.testapp.vo.BlobEntity;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.Product;
+import android.arch.persistence.room.integration.testapp.vo.User;
+import android.arch.persistence.room.integration.testapp.vo.UserAndAllPets;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SimpleEntityReadWriteTest {
+    private UserDao mUserDao;
+    private BlobEntityDao mBlobEntityDao;
+    private PetDao mPetDao;
+    private UserPetDao mUserPetDao;
+    private ProductDao mProductDao;
+
+    @Before
+    public void createDb() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        mUserDao = db.getUserDao();
+        mPetDao = db.getPetDao();
+        mUserPetDao = db.getUserPetDao();
+        mBlobEntityDao = db.getBlobEntityDao();
+        mProductDao = db.getProductDao();
+    }
+
+    @Test
+    public void writeUserAndReadInList() throws Exception {
+        User user = TestUtil.createUser(3);
+        user.setName("george");
+        mUserDao.insert(user);
+        List<User> byName = mUserDao.findUsersByName("george");
+        assertThat(byName.get(0), equalTo(user));
+    }
+
+    @Test
+    public void insertNull() throws Exception {
+        @SuppressWarnings("ConstantConditions")
+        Product product = new Product(1, null);
+        Throwable throwable = null;
+        try {
+            mProductDao.insert(product);
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertNotNull("Was expecting an exception", throwable);
+        assertThat(throwable, instanceOf(SQLiteConstraintException.class));
+    }
+
+    @Test
+    public void insertDifferentEntities() throws Exception {
+        User user1 = TestUtil.createUser(3);
+        user1.setName("george");
+        Pet pet = TestUtil.createPet(1);
+        pet.setUserId(3);
+        pet.setName("a");
+        mUserPetDao.insertUserAndPet(user1, pet);
+        assertThat(mUserDao.count(), is(1));
+        List<UserAndAllPets> inserted = mUserPetDao.loadAllUsersWithTheirPets();
+        assertThat(inserted, hasSize(1));
+        assertThat(inserted.get(0).user.getId(), is(3));
+        assertThat(inserted.get(0).user.getName(), is(equalTo("george")));
+        assertThat(inserted.get(0).pets, hasSize(1));
+        assertThat(inserted.get(0).pets.get(0).getPetId(), is(1));
+        assertThat(inserted.get(0).pets.get(0).getName(), is("a"));
+        assertThat(inserted.get(0).pets.get(0).getUserId(), is(3));
+        pet.setName("b");
+        mUserPetDao.updateUsersAndPets(new User[]{user1}, new Pet[]{pet});
+        List<UserAndAllPets> updated = mUserPetDao.loadAllUsersWithTheirPets();
+        assertThat(updated, hasSize(1));
+        assertThat(updated.get(0).pets, hasSize(1));
+        assertThat(updated.get(0).pets.get(0).getName(), is("b"));
+        User user2 = TestUtil.createUser(5);
+        user2.setName("chet");
+        mUserDao.insert(user2);
+        assertThat(mUserDao.count(), is(2));
+        mUserPetDao.delete2UsersAndPets(user1, user2, new Pet[]{pet});
+        List<UserAndAllPets> deleted = mUserPetDao.loadAllUsersWithTheirPets();
+        assertThat(deleted, hasSize(0));
+    }
+
+    @Test
+    public void insertDifferentEntities_transaction() throws Exception {
+        Pet pet = TestUtil.createPet(1);
+        mPetDao.insertOrReplace(pet);
+        assertThat(mPetDao.count(), is(1));
+        User user = TestUtil.createUser(3);
+        try {
+            mUserPetDao.insertUserAndPet(user, pet);
+            fail("Exception expected");
+        } catch (SQLiteConstraintException ignored) {
+        }
+        assertThat(mUserDao.count(), is(0));
+        assertThat(mPetDao.count(), is(1));
+    }
+
+    @Test
+    public void throwExceptionOnConflict() {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+
+        User user2 = TestUtil.createUser(3);
+        try {
+            mUserDao.insert(user2);
+            throw new AssertionFailedError("didn't throw in conflicting insertion");
+        } catch (SQLiteException ignored) {
+        }
+    }
+
+    @Test
+    public void replaceOnConflict() {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+
+        User user2 = TestUtil.createUser(3);
+        mUserDao.insertOrReplace(user2);
+
+        assertThat(mUserDao.load(3), equalTo(user2));
+        assertThat(mUserDao.load(3), not(equalTo(user)));
+    }
+
+    @Test
+    public void updateSimple() {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        user.setName("i am an updated name");
+        assertThat(mUserDao.update(user), is(1));
+        assertThat(mUserDao.load(user.getId()), equalTo(user));
+    }
+
+    @Test
+    public void updateNonExisting() {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        User user2 = TestUtil.createUser(4);
+        assertThat(mUserDao.update(user2), is(0));
+    }
+
+    @Test
+    public void updateList() {
+        List<User> users = TestUtil.createUsersList(3, 4, 5);
+        mUserDao.insertAll(users.toArray(new User[3]));
+        for (User user : users) {
+            user.setName("name " + user.getId());
+        }
+        assertThat(mUserDao.updateAll(users), is(3));
+        for (User user : users) {
+            assertThat(mUserDao.load(user.getId()).getName(), is("name " + user.getId()));
+        }
+    }
+
+    @Test
+    public void updateListPartial() {
+        List<User> existingUsers = TestUtil.createUsersList(3, 4, 5);
+        mUserDao.insertAll(existingUsers.toArray(new User[3]));
+        for (User user : existingUsers) {
+            user.setName("name " + user.getId());
+        }
+        List<User> allUsers = TestUtil.createUsersList(7, 8, 9);
+        allUsers.addAll(existingUsers);
+        assertThat(mUserDao.updateAll(allUsers), is(3));
+        for (User user : existingUsers) {
+            assertThat(mUserDao.load(user.getId()).getName(), is("name " + user.getId()));
+        }
+    }
+
+    @Test
+    public void delete() {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        assertThat(mUserDao.delete(user), is(1));
+        assertThat(mUserDao.delete(user), is(0));
+        assertThat(mUserDao.load(3), is(nullValue()));
+    }
+
+    @Test
+    public void deleteAll() {
+        User[] users = TestUtil.createUsersArray(3, 5, 7, 9);
+        mUserDao.insertAll(users);
+        // there is actually no guarantee for this order by works fine since they are ordered for
+        // the test and it is a new database (no pages to recycle etc)
+        assertThat(mUserDao.loadByIds(3, 5, 7, 9), is(users));
+        int deleteCount = mUserDao.deleteAll(new User[]{users[0], users[3],
+                TestUtil.createUser(9)});
+        assertThat(deleteCount, is(2));
+        assertThat(mUserDao.loadByIds(3, 5, 7, 9), is(new User[]{users[1], users[2]}));
+    }
+
+    @Test
+    public void deleteEverything() {
+        User user = TestUtil.createUser(3);
+        mUserDao.insert(user);
+        assertThat(mUserDao.count(), is(1));
+        int count = mUserDao.deleteEverything();
+        assertThat(count, is(1));
+        assertThat(mUserDao.count(), is(0));
+    }
+
+    @Test
+    public void findByBoolean() {
+        User user1 = TestUtil.createUser(3);
+        user1.setAdmin(true);
+        User user2 = TestUtil.createUser(5);
+        user2.setAdmin(false);
+        mUserDao.insert(user1);
+        mUserDao.insert(user2);
+        assertThat(mUserDao.findByAdmin(true), is(Arrays.asList(user1)));
+        assertThat(mUserDao.findByAdmin(false), is(Arrays.asList(user2)));
+    }
+
+    @Test
+    public void deleteByAge() {
+        User user1 = TestUtil.createUser(3);
+        user1.setAge(30);
+        User user2 = TestUtil.createUser(5);
+        user2.setAge(45);
+        mUserDao.insert(user1);
+        mUserDao.insert(user2);
+        assertThat(mUserDao.deleteAgeGreaterThan(60), is(0));
+        assertThat(mUserDao.deleteAgeGreaterThan(45), is(0));
+        assertThat(mUserDao.deleteAgeGreaterThan(35), is(1));
+        assertThat(mUserDao.loadByIds(3, 5), is(new User[]{user1}));
+    }
+
+    @Test
+    public void deleteByAgeRange() {
+        User user1 = TestUtil.createUser(3);
+        user1.setAge(30);
+        User user2 = TestUtil.createUser(5);
+        user2.setAge(45);
+        mUserDao.insert(user1);
+        mUserDao.insert(user2);
+        assertThat(mUserDao.deleteByAgeRange(35, 40), is(0));
+        assertThat(mUserDao.deleteByAgeRange(25, 30), is(1));
+        assertThat(mUserDao.loadByIds(3, 5), is(new User[]{user2}));
+    }
+
+    @Test
+    public void deleteByUIds() {
+        User[] users = TestUtil.createUsersArray(3, 5, 7, 9, 11);
+        mUserDao.insertAll(users);
+        assertThat(mUserDao.deleteByUids(2, 4, 6), is(0));
+        assertThat(mUserDao.deleteByUids(3, 11), is(2));
+        assertThat(mUserDao.loadByIds(3, 5, 7, 9, 11), is(new User[]{
+                users[1], users[2], users[3]
+        }));
+    }
+
+    @Test
+    public void updateNameById() {
+        User[] usersArray = TestUtil.createUsersArray(3, 5, 7);
+        mUserDao.insertAll(usersArray);
+        assertThat("test sanity", usersArray[1].getName(), not(equalTo("updated name")));
+        int changed = mUserDao.updateById(5, "updated name");
+        assertThat(changed, is(1));
+        assertThat(mUserDao.load(5).getName(), is("updated name"));
+    }
+
+    @Test
+    public void incrementIds() {
+        User[] usersArr = TestUtil.createUsersArray(2, 4, 6);
+        mUserDao.insertAll(usersArr);
+        mUserDao.incrementIds(1);
+        assertThat(mUserDao.loadIds(), is(Arrays.asList(3, 5, 7)));
+    }
+
+    @Test
+    public void incrementAgeOfAll() {
+        User[] users = TestUtil.createUsersArray(3, 5, 7);
+        users[0].setAge(3);
+        users[1].setAge(5);
+        users[2].setAge(7);
+        mUserDao.insertAll(users);
+        assertThat(mUserDao.count(), is(3));
+        mUserDao.incrementAgeOfAll();
+        for (User user : mUserDao.loadByIds(3, 5, 7)) {
+            assertThat(user.getAge(), is(user.getId() + 1));
+        }
+    }
+
+    @Test
+    public void findByIntQueryParameter() {
+        User user = TestUtil.createUser(1);
+        final String name = "my name";
+        user.setName(name);
+        mUserDao.insert(user);
+        assertThat(mUserDao.findByNameLength(name.length()), is(Collections.singletonList(user)));
+    }
+
+    @Test
+    public void findByIntFieldMatch() {
+        User user = TestUtil.createUser(1);
+        user.setAge(19);
+        mUserDao.insert(user);
+        assertThat(mUserDao.findByAge(19), is(Collections.singletonList(user)));
+    }
+
+    @Test
+    public void customConverterField() {
+        User user = TestUtil.createUser(20);
+        Date theDate = new Date(System.currentTimeMillis() - 200);
+        user.setBirthday(theDate);
+        mUserDao.insert(user);
+        assertThat(mUserDao.findByBirthdayRange(new Date(theDate.getTime() - 100),
+                new Date(theDate.getTime() + 1)).get(0), is(user));
+        assertThat(mUserDao.findByBirthdayRange(new Date(theDate.getTime()),
+                new Date(theDate.getTime() + 1)).size(), is(0));
+    }
+
+    @Test
+    public void renamedField() {
+        User user = TestUtil.createUser(3);
+        user.setCustomField("foo laaa");
+        mUserDao.insertOrReplace(user);
+        User loaded = mUserDao.load(3);
+        assertThat(loaded.getCustomField(), is("foo laaa"));
+        assertThat(loaded, is(user));
+    }
+
+    @Test
+    public void readViaCursor() {
+        User[] users = TestUtil.createUsersArray(3, 5, 7, 9);
+        mUserDao.insertAll(users);
+        Cursor cursor = mUserDao.findUsersAsCursor(3, 5, 9);
+        try {
+            assertThat(cursor.getCount(), is(3));
+            assertThat(cursor.moveToNext(), is(true));
+            assertThat(cursor.getInt(0), is(3));
+            assertThat(cursor.moveToNext(), is(true));
+            assertThat(cursor.getInt(0), is(5));
+            assertThat(cursor.moveToNext(), is(true));
+            assertThat(cursor.getInt(0), is(9));
+            assertThat(cursor.moveToNext(), is(false));
+        } finally {
+            cursor.close();
+        }
+    }
+
+    @Test
+    public void readDirectWithTypeAdapter() {
+        User user = TestUtil.createUser(3);
+        user.setBirthday(null);
+        mUserDao.insert(user);
+        assertThat(mUserDao.getBirthday(3), is(nullValue()));
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.YEAR, 3);
+        Date birthday = calendar.getTime();
+        user.setBirthday(birthday);
+
+        mUserDao.update(user);
+        assertThat(mUserDao.getBirthday(3), is(birthday));
+    }
+
+    @Test
+    public void emptyInQuery() {
+        User[] users = mUserDao.loadByIds();
+        assertThat(users, is(new User[0]));
+    }
+
+    @Test
+    public void blob() {
+        BlobEntity a = new BlobEntity(1, "abc".getBytes());
+        BlobEntity b = new BlobEntity(2, "def".getBytes());
+        mBlobEntityDao.insert(a);
+        mBlobEntityDao.insert(b);
+        List<BlobEntity> list = mBlobEntityDao.selectAll();
+        assertThat(list, hasSize(2));
+        mBlobEntityDao.updateContent(2, "ghi".getBytes());
+        assertThat(mBlobEntityDao.getContent(2), is(equalTo("ghi".getBytes())));
+    }
+
+    @Test
+    public void transactionByRunnable() {
+        User a = TestUtil.createUser(3);
+        User b = TestUtil.createUser(5);
+        mUserDao.insertBothByRunnable(a, b);
+        assertThat(mUserDao.count(), is(2));
+    }
+
+    @Test
+    public void transactionByRunnable_failure() {
+        User a = TestUtil.createUser(3);
+        User b = TestUtil.createUser(3);
+        boolean caught = false;
+        try {
+            mUserDao.insertBothByRunnable(a, b);
+        } catch (SQLiteConstraintException e) {
+            caught = true;
+        }
+        assertTrue("SQLiteConstraintException expected", caught);
+        assertThat(mUserDao.count(), is(0));
+    }
+
+    @Test
+    public void transactionByCallable() {
+        User a = TestUtil.createUser(3);
+        User b = TestUtil.createUser(5);
+        int count = mUserDao.insertBothByCallable(a, b);
+        assertThat(mUserDao.count(), is(2));
+        assertThat(count, is(2));
+    }
+
+    @Test
+    public void transactionByCallable_failure() {
+        User a = TestUtil.createUser(3);
+        User b = TestUtil.createUser(3);
+        boolean caught = false;
+        try {
+            mUserDao.insertBothByCallable(a, b);
+        } catch (SQLiteConstraintException e) {
+            caught = true;
+        }
+        assertTrue("SQLiteConstraintException expected", caught);
+        assertThat(mUserDao.count(), is(0));
+    }
+
+    @Test
+    public void multipleInParamsFollowedByASingleParam_delete() {
+        User user = TestUtil.createUser(3);
+        user.setAge(30);
+        mUserDao.insert(user);
+        assertThat(mUserDao.deleteByAgeAndIds(20, Arrays.asList(3, 5)), is(0));
+        assertThat(mUserDao.count(), is(1));
+        assertThat(mUserDao.deleteByAgeAndIds(30, Arrays.asList(3, 5)), is(1));
+        assertThat(mUserDao.count(), is(0));
+    }
+
+    @Test
+    public void multipleInParamsFollowedByASingleParam_update() {
+        User user = TestUtil.createUser(3);
+        user.setAge(30);
+        user.setWeight(10f);
+        mUserDao.insert(user);
+        assertThat(mUserDao.updateByAgeAndIds(3f, 20, Arrays.asList(3, 5)), is(0));
+        assertThat(mUserDao.loadByIds(3)[0].getWeight(), is(10f));
+        assertThat(mUserDao.updateByAgeAndIds(3f, 30, Arrays.asList(3, 5)), is(1));
+        assertThat(mUserDao.loadByIds(3)[0].getWeight(), is(3f));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/TestDatabaseTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/TestDatabaseTest.java
new file mode 100644
index 0000000..02499a7
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/TestDatabaseTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+import android.arch.persistence.room.integration.testapp.dao.PetCoupleDao;
+import android.arch.persistence.room.integration.testapp.dao.PetDao;
+import android.arch.persistence.room.integration.testapp.dao.SchoolDao;
+import android.arch.persistence.room.integration.testapp.dao.ToyDao;
+import android.arch.persistence.room.integration.testapp.dao.UserDao;
+import android.arch.persistence.room.integration.testapp.dao.UserPetDao;
+
+import org.junit.Before;
+
+@SuppressWarnings("WeakerAccess")
+public abstract class TestDatabaseTest {
+    protected TestDatabase mDatabase;
+    protected UserDao mUserDao;
+    protected PetDao mPetDao;
+    protected UserPetDao mUserPetDao;
+    protected SchoolDao mSchoolDao;
+    protected PetCoupleDao mPetCoupleDao;
+    protected ToyDao mToyDao;
+
+    @Before
+    public void createDb() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        mDatabase = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        mUserDao = mDatabase.getUserDao();
+        mPetDao = mDatabase.getPetDao();
+        mUserPetDao = mDatabase.getUserPetDao();
+        mSchoolDao = mDatabase.getSchoolDao();
+        mPetCoupleDao = mDatabase.getPetCoupleDao();
+        mToyDao = mDatabase.getToyDao();
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/TestUtil.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/TestUtil.java
new file mode 100644
index 0000000..0a35b2f
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/TestUtil.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import android.arch.persistence.room.integration.testapp.vo.Address;
+import android.arch.persistence.room.integration.testapp.vo.Coordinates;
+import android.arch.persistence.room.integration.testapp.vo.Pet;
+import android.arch.persistence.room.integration.testapp.vo.School;
+import android.arch.persistence.room.integration.testapp.vo.User;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+public class TestUtil {
+    public static User[] createUsersArray(int... ids) {
+        User[] result = new User[ids.length];
+        for (int i = 0; i < ids.length; i++) {
+            result[i] = createUser(ids[i]);
+        }
+        return result;
+    }
+
+    public static List<User> createUsersList(int... ids) {
+        List<User> result = new ArrayList<>();
+        for (int id : ids) {
+            result.add(createUser(id));
+        }
+        return result;
+    }
+
+    public static User createUser(int id) {
+        User user = new User();
+        user.setId(id);
+        user.setName(UUID.randomUUID().toString());
+        user.setLastName(UUID.randomUUID().toString());
+        user.setAge((int) (10 + Math.random() * 50));
+        user.setCustomField(UUID.randomUUID().toString());
+        user.setBirthday(new Date());
+        return user;
+    }
+
+    public static Pet createPet(int id) {
+        Pet pet = new Pet();
+        pet.setPetId(id);
+        pet.setName(UUID.randomUUID().toString());
+        return pet;
+    }
+
+    public static Pet[] createPetsForUser(int uid, int petStartId, int count) {
+        Pet[] pets = new Pet[count];
+        for (int i = 0; i < count; i++) {
+            Pet pet = createPet(petStartId++);
+            pet.setUserId(uid);
+            pets[i] = pet;
+        }
+        return pets;
+    }
+
+    public static School createSchool(int id, int managerId) {
+        School school = new School();
+        school.setId(id);
+        school.setName(UUID.randomUUID().toString());
+        school.setManager(createUser(managerId));
+        school.setAddress(createAddress());
+        return school;
+    }
+
+    private static Address createAddress() {
+        Address address = new Address();
+        address.setCoordinates(createCoordinates());
+        address.setPostCode((int) (Math.random() * 1000 + 1000));
+        address.setState(UUID.randomUUID().toString().substring(0, 2));
+        address.setStreet(UUID.randomUUID().toString());
+        return address;
+    }
+
+    private static Coordinates createCoordinates() {
+        Coordinates coordinates = new Coordinates();
+        coordinates.lat = Math.random();
+        coordinates.lng = Math.random();
+        return coordinates;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Address.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Address.java
new file mode 100644
index 0000000..46f3bb6
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Address.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Embedded;
+
+public class Address {
+    @ColumnInfo(name = "street")
+    private String mStreet;
+    @ColumnInfo(name = "state")
+    private String mState;
+    @ColumnInfo(name = "post_code")
+    private int mPostCode;
+    @Embedded
+    private Coordinates mCoordinates;
+
+    public String getStreet() {
+        return mStreet;
+    }
+
+    public void setStreet(String street) {
+        mStreet = street;
+    }
+
+    public String getState() {
+        return mState;
+    }
+
+    public void setState(String state) {
+        mState = state;
+    }
+
+    public int getPostCode() {
+        return mPostCode;
+    }
+
+    public void setPostCode(int postCode) {
+        mPostCode = postCode;
+    }
+
+    public Coordinates getCoordinates() {
+        return mCoordinates;
+    }
+
+    public void setCoordinates(Coordinates coordinates) {
+        mCoordinates = coordinates;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Address address = (Address) o;
+
+        if (mPostCode != address.mPostCode) return false;
+        if (mStreet != null ? !mStreet.equals(address.mStreet) : address.mStreet != null) {
+            return false;
+        }
+        if (mState != null ? !mState.equals(address.mState) : address.mState != null) {
+            return false;
+        }
+        return mCoordinates != null ? mCoordinates.equals(address.mCoordinates)
+                : address.mCoordinates == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mStreet != null ? mStreet.hashCode() : 0;
+        result = 31 * result + (mState != null ? mState.hashCode() : 0);
+        result = 31 * result + mPostCode;
+        result = 31 * result + (mCoordinates != null ? mCoordinates.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/AvgWeightByAge.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/AvgWeightByAge.java
new file mode 100644
index 0000000..4d22f13
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/AvgWeightByAge.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Ignore;
+
+@SuppressWarnings("unused")
+public class AvgWeightByAge {
+
+    private int mAge;
+
+    @ColumnInfo(name = "AVG(mWeight)")
+    private float mWeight;
+
+    public AvgWeightByAge() {
+    }
+
+    @Ignore
+    public AvgWeightByAge(int age, float weight) {
+        mAge = age;
+        mWeight = weight;
+    }
+
+    public int getAge() {
+        return mAge;
+    }
+
+    public void setAge(int age) {
+        mAge = age;
+    }
+
+    public float getWeight() {
+        return mWeight;
+    }
+
+    public void setWeight(float weight) {
+        mWeight = weight;
+    }
+
+    @Override
+    public String toString() {
+        return "AvgWeightByAge{"
+                + "mAge=" + mAge
+                + ", mWeight=" + mWeight
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        AvgWeightByAge that = (AvgWeightByAge) o;
+
+        //noinspection SimplifiableIfStatement
+        if (mAge != that.mAge) {
+            return false;
+        }
+        return Float.compare(that.mWeight, mWeight) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mAge;
+        result = 31 * result + (mWeight != +0.0f ? Float.floatToIntBits(mWeight) : 0);
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/BlobEntity.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/BlobEntity.java
new file mode 100644
index 0000000..134afc7
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/BlobEntity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+
+@Entity
+public class BlobEntity {
+    @PrimaryKey
+    public long id;
+    public byte[] content;
+
+    public BlobEntity(long id, byte[] content) {
+        this.id = id;
+        this.content = content;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Coordinates.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Coordinates.java
new file mode 100644
index 0000000..e8cfd06
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Coordinates.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+public class Coordinates {
+    public double lat;
+    public double lng;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Coordinates that = (Coordinates) o;
+
+        if (Double.compare(that.lat, lat) != 0) return false;
+        return Double.compare(that.lng, lng) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        long temp;
+        temp = Double.doubleToLongBits(lat);
+        result = (int) (temp ^ (temp >>> 32));
+        temp = Double.doubleToLongBits(lng);
+        result = 31 * result + (int) (temp ^ (temp >>> 32));
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/EmbeddedUserAndAllPets.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/EmbeddedUserAndAllPets.java
new file mode 100644
index 0000000..ca82bfe
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/EmbeddedUserAndAllPets.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+
+public class EmbeddedUserAndAllPets {
+    @Embedded
+    UserAndAllPets mUserAndAllPets;
+
+    public UserAndAllPets getUserAndAllPets() {
+        return mUserAndAllPets;
+    }
+
+    public void setUserAndAllPets(UserAndAllPets userAndAllPets) {
+        this.mUserAndAllPets = userAndAllPets;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/IntAutoIncPKeyEntity.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/IntAutoIncPKeyEntity.java
new file mode 100644
index 0000000..3392649
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/IntAutoIncPKeyEntity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+
+@Entity
+public class IntAutoIncPKeyEntity {
+    @PrimaryKey(autoGenerate = true)
+    public int pKey;
+    public String data;
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/IntegerAutoIncPKeyEntity.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/IntegerAutoIncPKeyEntity.java
new file mode 100644
index 0000000..636ffd9
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/IntegerAutoIncPKeyEntity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+
+@Entity
+public class IntegerAutoIncPKeyEntity {
+    @PrimaryKey(autoGenerate = true)
+    public Integer pKey;
+    public String data;
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Pet.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Pet.java
new file mode 100644
index 0000000..8806e10
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Pet.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+
+@Entity
+public class Pet {
+    @PrimaryKey
+    private int mPetId;
+    private int mUserId;
+    @ColumnInfo(name = "mPetName")
+    private String mName;
+
+    public int getPetId() {
+        return mPetId;
+    }
+
+    public void setPetId(int petId) {
+        mPetId = petId;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        mName = name;
+    }
+
+    public int getUserId() {
+        return mUserId;
+    }
+
+    public void setUserId(int userId) {
+        mUserId = userId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Pet pet = (Pet) o;
+
+        if (mPetId != pet.mPetId) return false;
+        if (mUserId != pet.mUserId) return false;
+        return mName != null ? mName.equals(pet.mName) : pet.mName == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mPetId;
+        result = 31 * result + mUserId;
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/PetAndToys.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/PetAndToys.java
new file mode 100644
index 0000000..69fb591
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/PetAndToys.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Relation;
+
+import java.util.List;
+
+public class PetAndToys {
+    @Embedded
+    public Pet pet;
+    @Relation(parentColumn = "mPetId", entityColumn = "mPetId")
+    public List<Toy> toys;
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/PetCouple.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/PetCouple.java
new file mode 100644
index 0000000..f27b131
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/PetCouple.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.RoomWarnings;
+
+@Entity
+@SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
+public class PetCouple {
+    @PrimaryKey
+    public String id;
+    @Embedded(prefix = "male_")
+    public Pet male;
+    @Embedded(prefix = "female_")
+    private Pet mFemale;
+
+    public Pet getFemale() {
+        return mFemale;
+    }
+
+    public void setFemale(Pet female) {
+        mFemale = female;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        PetCouple petCouple = (PetCouple) o;
+
+        if (male != null ? !male.equals(petCouple.male) : petCouple.male != null) return false;
+        return mFemale != null ? mFemale.equals(petCouple.mFemale) : petCouple.mFemale == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = male != null ? male.hashCode() : 0;
+        result = 31 * result + (mFemale != null ? mFemale.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Product.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Product.java
new file mode 100644
index 0000000..a395aea
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Product.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+import android.support.annotation.NonNull;
+
+@Entity(tableName = "products")
+public class Product {
+
+    @PrimaryKey(autoGenerate = true)
+    public final int id;
+
+    @NonNull
+    public final String name;
+
+    public Product(int id, @NonNull String name) {
+        this.id = id;
+        this.name = name;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/School.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/School.java
new file mode 100644
index 0000000..f0426f1
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/School.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+
+@Entity
+public class School {
+    @PrimaryKey
+    private int mId;
+    private String mName;
+    @Embedded(prefix = "address_")
+    public Address address;
+
+    @Embedded(prefix = "manager_")
+    private User mManager;
+
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        mId = id;
+    }
+
+    public Address getAddress() {
+        return address;
+    }
+
+    public void setAddress(Address address) {
+        this.address = address;
+    }
+
+    public User getManager() {
+        return mManager;
+    }
+
+    public void setManager(User manager) {
+        mManager = manager;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        mName = name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        School school = (School) o;
+
+        if (mId != school.mId) {
+            return false;
+        }
+        if (mName != null ? !mName.equals(school.mName) : school.mName != null) {
+            return false;
+        }
+        if (address != null ? !address.equals(school.address) : school.address != null) {
+            return false;
+        }
+        return mManager != null ? mManager.equals(school.mManager) : school.mManager == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId;
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        result = 31 * result + (address != null ? address.hashCode() : 0);
+        result = 31 * result + (mManager != null ? mManager.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/SchoolRef.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/SchoolRef.java
new file mode 100644
index 0000000..bbf0cb4
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/SchoolRef.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+public class SchoolRef extends School {
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Toy.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Toy.java
new file mode 100644
index 0000000..5198c2d
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/Toy.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+
+/**
+ * The toys of a pet.
+ */
+@Entity
+public class Toy {
+    @PrimaryKey(autoGenerate = true)
+    private int mId;
+    private String mName;
+    private int mPetId;
+
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        mId = id;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        mName = name;
+    }
+
+    public int getPetId() {
+        return mPetId;
+    }
+
+    public void setPetId(int petId) {
+        mPetId = petId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Toy toy = (Toy) o;
+
+        if (mId != toy.mId) return false;
+        if (mPetId != toy.mPetId) return false;
+        return mName != null ? mName.equals(toy.mName) : toy.mName == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId;
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        result = 31 * result + mPetId;
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/User.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/User.java
new file mode 100644
index 0000000..57cf585
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/User.java
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.TypeConverters;
+import android.arch.persistence.room.integration.testapp.TestDatabase;
+
+import java.util.Date;
+
+@Entity
+@TypeConverters({TestDatabase.Converters.class})
+public class User {
+
+    @PrimaryKey
+    private int mId;
+
+    private String mName;
+
+    private String mLastName;
+
+    private int mAge;
+
+    private boolean mAdmin;
+
+    private float mWeight;
+
+    private Date mBirthday;
+
+    @ColumnInfo(name = "custommm")
+    private String mCustomField;
+
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        this.mId = id;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        this.mName = name;
+    }
+
+    public String getLastName() {
+        return mLastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.mLastName = lastName;
+    }
+
+    public int getAge() {
+        return mAge;
+    }
+
+    public void setAge(int age) {
+        this.mAge = age;
+    }
+
+    public boolean isAdmin() {
+        return mAdmin;
+    }
+
+    public void setAdmin(boolean admin) {
+        mAdmin = admin;
+    }
+
+    public float getWeight() {
+        return mWeight;
+    }
+
+    public void setWeight(float weight) {
+        mWeight = weight;
+    }
+
+    public Date getBirthday() {
+        return mBirthday;
+    }
+
+    public void setBirthday(Date birthday) {
+        mBirthday = birthday;
+    }
+
+    public String getCustomField() {
+        return mCustomField;
+    }
+
+    public void setCustomField(String customField) {
+        mCustomField = customField;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        User user = (User) o;
+
+        if (mId != user.mId) return false;
+        if (mAge != user.mAge) return false;
+        if (mAdmin != user.mAdmin) return false;
+        if (Float.compare(user.mWeight, mWeight) != 0) return false;
+        if (mName != null ? !mName.equals(user.mName) : user.mName != null) return false;
+        if (mLastName != null ? !mLastName.equals(user.mLastName) : user.mLastName != null) {
+            return false;
+        }
+        if (mBirthday != null ? !mBirthday.equals(user.mBirthday) : user.mBirthday != null) {
+            return false;
+        }
+        return mCustomField != null ? mCustomField.equals(user.mCustomField)
+                : user.mCustomField == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId;
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        result = 31 * result + (mLastName != null ? mLastName.hashCode() : 0);
+        result = 31 * result + mAge;
+        result = 31 * result + (mAdmin ? 1 : 0);
+        result = 31 * result + (mWeight != +0.0f ? Float.floatToIntBits(mWeight) : 0);
+        result = 31 * result + (mBirthday != null ? mBirthday.hashCode() : 0);
+        result = 31 * result + (mCustomField != null ? mCustomField.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "User{"
+                + "mId=" + mId
+                + ", mName='" + mName + '\''
+                + ", mLastName='" + mLastName + '\''
+                + ", mAge=" + mAge
+                + ", mAdmin=" + mAdmin
+                + ", mWeight=" + mWeight
+                + ", mBirthday=" + mBirthday
+                + ", mCustom=" + mCustomField
+                + '}';
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndAllPets.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndAllPets.java
new file mode 100644
index 0000000..24a0710
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndAllPets.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Relation;
+
+import java.util.List;
+
+public class UserAndAllPets {
+    @Embedded
+    public User user;
+    @Relation(parentColumn = "mId", entityColumn = "mUserId")
+    public List<Pet> pets;
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndPet.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndPet.java
new file mode 100644
index 0000000..628e9bf
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndPet.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+import android.arch.persistence.room.Embedded;
+
+public class UserAndPet {
+    @Embedded
+    private User mUser;
+    @Embedded
+    private Pet mPet;
+
+    public User getUser() {
+        return mUser;
+    }
+
+    public void setUser(User user) {
+        mUser = user;
+    }
+
+    public Pet getPet() {
+        return mPet;
+    }
+
+    public void setPet(Pet pet) {
+        mPet = pet;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndPetNonNull.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndPetNonNull.java
new file mode 100644
index 0000000..8739bd0
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserAndPetNonNull.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+import android.support.annotation.NonNull;
+
+public class UserAndPetNonNull {
+    @Embedded
+    private User mUser;
+    @Embedded
+    @NonNull
+    private Pet mPet;
+
+    public User getUser() {
+        return mUser;
+    }
+
+    public void setUser(User user) {
+        mUser = user;
+    }
+
+    public Pet getPet() {
+        return mPet;
+    }
+
+    public void setPet(Pet pet) {
+        mPet = pet;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserIdAndPetNames.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserIdAndPetNames.java
new file mode 100644
index 0000000..444431e
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserIdAndPetNames.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Relation;
+
+import java.util.List;
+
+/**
+ * Same as Pet class but only keeps name and user id
+ */
+public class UserIdAndPetNames {
+    @ColumnInfo(name = "mId")
+    public int userId;
+    @Relation(entity = Pet.class, parentColumn = "mId", entityColumn = "mUserId",
+            projection = "mPetName")
+    public List<String> names;
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserWithPetsAndToys.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserWithPetsAndToys.java
new file mode 100644
index 0000000..01c9bed
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/vo/UserWithPetsAndToys.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.vo;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Relation;
+
+import java.util.List;
+
+public class UserWithPetsAndToys {
+    @Embedded
+    public User user;
+    @Relation(entity = Pet.class, parentColumn = "mId", entityColumn = "mUserId")
+    public List<PetAndToys> pets;
+}
diff --git a/room/integration-tests/testapp/src/main/AndroidManifest.xml b/room/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..27fabd4
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.persistence.room.integration.testapp">
+    <application
+        android:allowBackup="true"
+        android:supportsRtl="true">
+        <activity
+            android:name=".LazyListActivity"
+            android:label="Room LazyList"
+            android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+  </manifest>
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/CustomerViewModel.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/CustomerViewModel.java
new file mode 100644
index 0000000..2d8391c
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/CustomerViewModel.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp;
+
+import android.app.Application;
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.lifecycle.AndroidViewModel;
+import android.arch.lifecycle.LiveData;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.integration.testapp.database.Customer;
+import android.arch.persistence.room.integration.testapp.database.SampleDatabase;
+import android.arch.util.paging.LazyList;
+import android.arch.util.paging.ListConfig;
+
+import java.util.UUID;
+
+/**
+ * Sample database-backed view model of Customers
+ */
+public class CustomerViewModel extends AndroidViewModel {
+    private SampleDatabase mDatabase;
+    private LiveData<LazyList<Customer>> mLiveCustomerList;
+    private static int sCustomerId = 0;
+
+    public CustomerViewModel(Application application) {
+        super(application);
+        createDb();
+        mLiveCustomerList = mDatabase.getCustomerDao().loadPagedAgeOrder().create(
+                ListConfig.builder()
+                        .pageSize(10)
+                        .prefetchDistance(10)
+                        .create());
+    }
+
+    private void createDb() {
+        mDatabase = Room.inMemoryDatabaseBuilder(this.getApplication(),
+                SampleDatabase.class).build();
+    }
+
+    public void setDatabase(SampleDatabase database) {
+        mDatabase = database;
+
+    }
+
+    void insertCustomer() {
+        AppToolkitTaskExecutor.getInstance().executeOnDiskIO(new Runnable() {
+            @Override
+            public void run() {
+                Customer customer = new Customer();
+                customer.setId(sCustomerId++);
+                customer.setName(UUID.randomUUID().toString());
+                customer.setLastName(UUID.randomUUID().toString());
+                mDatabase.getCustomerDao().insert(customer);
+            }
+        });
+    }
+
+    LiveData<LazyList<Customer>> getLazyList() {
+        return mLiveCustomerList;
+    }
+}
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/LazyListActivity.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/LazyListActivity.java
new file mode 100644
index 0000000..983a52f
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/LazyListActivity.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp;
+
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LifecycleRegistryOwner;
+import android.arch.lifecycle.ViewModelProviders;
+import android.arch.persistence.room.integration.testapp.database.Customer;
+import android.arch.util.paging.LazyListAdapterHelper;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * Sample LazyList activity which uses Room.
+ */
+public class LazyListActivity extends AppCompatActivity implements LifecycleRegistryOwner {
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final CustomerViewModel viewModel = ViewModelProviders.of(this)
+                .get(CustomerViewModel.class);
+        setContentView(R.layout.activity_recycler_view);
+        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
+        final LazyListCustomerAdapter adapter = new LazyListCustomerAdapter(
+                LazyListAdapterHelper
+                        .<Customer>builder()
+                        .lifecycle(this)
+                        .diffCallback(Customer.DIFF_CALLBACK)
+                        .source(viewModel.getLazyList()));
+        recyclerView.setAdapter(adapter);
+        final Button button = (Button) findViewById(R.id.button);
+        button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                viewModel.insertCustomer();
+            }
+        });
+    }
+
+    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+
+    @Override
+    public LifecycleRegistry getLifecycle() {
+        return mLifecycleRegistry;
+    }
+}
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/LazyListCustomerAdapter.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/LazyListCustomerAdapter.java
new file mode 100644
index 0000000..2fc7e71
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/LazyListCustomerAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp;
+
+import android.arch.persistence.room.integration.testapp.database.Customer;
+import android.arch.util.paging.LazyListAdapterHelper;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import io.reactivex.annotations.NonNull;
+
+/**
+ * Sample adapter which uses a LazyListAdapterHelper.
+ */
+public class LazyListCustomerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private final LazyListAdapterHelper<Customer> mHelper;
+
+    public LazyListCustomerAdapter(@NonNull LazyListAdapterHelper.Builder<Customer> builder) {
+        this.mHelper = builder.adapter(this).create();
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        return new RecyclerView.ViewHolder(new TextView(parent.getContext())) {
+        };
+    }
+
+    @Override
+    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        Customer customer = mHelper.get(position);
+        ((TextView) (holder.itemView)).setText(customer == null ? "loading" : customer.getLastName());
+        holder.itemView.setMinimumHeight(400);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mHelper.getItemCount();
+    }
+}
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/Customer.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/Customer.java
new file mode 100644
index 0000000..eb928da
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/Customer.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.database;
+
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.TypeConverters;
+import android.arch.util.paging.DiffCallback;
+import android.support.annotation.NonNull;
+
+/**
+ * Sample entity
+ */
+@Entity
+public class Customer {
+
+    @PrimaryKey
+    private int mId;
+
+    private String mName;
+
+    private String mLastName;
+
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        this.mId = id;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        this.mName = name;
+    }
+
+    public String getLastName() {
+        return mLastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.mLastName = lastName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Customer customer = (Customer) o;
+
+        if (mId != customer.mId) {
+            return false;
+        }
+        if (mName != null ? !mName.equals(customer.mName) : customer.mName != null) {
+            return false;
+        }
+        return mLastName != null ? mLastName.equals(customer.mLastName)
+                : customer.mLastName == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId;
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        result = 31 * result + (mLastName != null ? mLastName.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "Customer{"
+                + "mId=" + mId
+                + ", mName='" + mName + '\''
+                + ", mLastName='" + mLastName + '\''
+                + '}';
+    }
+
+    public static final DiffCallback<Customer> DIFF_CALLBACK = new DiffCallback<Customer>() {
+        @Override
+        public boolean areContentsTheSame(@NonNull Customer oldItem, @NonNull Customer newItem) {
+            return oldItem.equals(newItem);
+        }
+
+        @Override
+        public boolean areItemsTheSame(@NonNull Customer oldItem, @NonNull Customer newItem) {
+            return oldItem.getId() == newItem.getId();
+        }
+    };
+}
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/CustomerDao.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/CustomerDao.java
new file mode 100644
index 0000000..d82701b
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/CustomerDao.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.database;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+import android.arch.util.paging.ListConfig;
+import android.arch.util.paging.LiveLazyListProvider;
+
+/**
+ * Simple Customer DAO for Room Customer list sample.
+ */
+@Dao
+public interface CustomerDao {
+
+    /**
+     * Insert a customer
+     * @param customer Customer.
+     */
+    @Insert
+    void insert(Customer customer);
+
+    /**
+     * Insert multiple customers.
+     * @param customers Customers.
+     */
+    @Insert
+    void insertAll(Customer[] customers);
+
+    /**
+     * @return LiveLazyListProvider of customers, ordered by last name. Call
+     * {@link LiveLazyListProvider#create(ListConfig)} to get a LiveData of LazyLists.
+     */
+    @Query("SELECT * FROM customer ORDER BY mLastName ASC")
+    LiveLazyListProvider<Customer> loadPagedAgeOrder();
+}
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/SampleDatabase.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/SampleDatabase.java
new file mode 100644
index 0000000..eec59f6
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/SampleDatabase.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.database;
+
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.TypeConverter;
+import android.arch.persistence.room.TypeConverters;
+
+import java.util.Date;
+
+/**
+ * Sample database of customers.
+ */
+@Database(entities = {Customer.class},
+        version = 1, exportSchema = false)
+public abstract class SampleDatabase extends RoomDatabase {
+    /**
+     * @return customer dao.
+     */
+    public abstract CustomerDao getCustomerDao();
+}
diff --git a/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml b/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
new file mode 100644
index 0000000..7e2367b
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
@@ -0,0 +1,44 @@
+<?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.
+  -->
+<RelativeLayout
+    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:id="@+id/activity_recycler_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="android.arch.persistence.room.integration.testapp.LazyListActivity">
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/recyclerview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layoutManager="LinearLayoutManager"
+        android:clipToPadding="false"
+        android:paddingBottom="@dimen/activity_vertical_margin"
+        android:paddingLeft="@dimen/activity_horizontal_margin"
+        android:paddingRight="@dimen/activity_horizontal_margin"
+        android:paddingTop="@dimen/activity_vertical_margin"
+    />
+    <Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentEnd="true"
+        android:text="@string/insert"/>
+</RelativeLayout>
diff --git a/room/integration-tests/testapp/src/main/res/values-w820dp/dimens.xml b/room/integration-tests/testapp/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..edff918
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,23 @@
+
+<!--
+~ 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>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/room/integration-tests/testapp/src/main/res/values/dimens.xml b/room/integration-tests/testapp/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..3358489
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+~ 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>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/room/integration-tests/testapp/src/main/res/values/strings.xml b/room/integration-tests/testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..5f55cc3
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+~ 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>
+    <string name="insert">Insert</string>
+</resources>
diff --git a/room/integration-tests/testapp/src/test/java/android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java b/room/integration-tests/testapp/src/test/java/android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java
new file mode 100644
index 0000000..3cbffc8
--- /dev/null
+++ b/room/integration-tests/testapp/src/test/java/android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.integration.testapp.db;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+
+public class JDBCOpenHelper implements SupportSQLiteOpenHelper {
+    @Override
+    public String getDatabaseName() {
+        return null;
+    }
+
+    @Override
+    public void setWriteAheadLoggingEnabled(boolean enabled) {
+
+    }
+
+    @Override
+    public SupportSQLiteDatabase getWritableDatabase() {
+        return null;
+    }
+
+    @Override
+    public SupportSQLiteDatabase getReadableDatabase() {
+        return null;
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/room/migration/build.gradle b/room/migration/build.gradle
new file mode 100644
index 0000000..7e3794b
--- /dev/null
+++ b/room/migration/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'maven'
+apply plugin: 'java'
+
+sourceCompatibility = 1.7
+
+sourceSets {
+    test.java.srcDirs += 'src/tests/kotlin'
+}
+project.ext.noDocs = true
+dependencies {
+    compile project(":room:common")
+    compile libs.kotlin.stdlib
+    compile libs.gson
+    testCompile libs.junit
+    testCompile libs.ij_annotations
+    testCompile libs.mockito_core
+}
+
+archivesBaseName = "migration"
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/BundleUtil.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/BundleUtil.java
new file mode 100644
index 0000000..4356e69
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/BundleUtil.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+/**
+ * Utility functions for bundling.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class BundleUtil {
+    /**
+     * Placeholder for table names in queries.
+     */
+    public static final String TABLE_NAME_PLACEHOLDER = "${TABLE_NAME}";
+
+    static String replaceTableName(String contents, String tableName) {
+        return contents.replace(TABLE_NAME_PLACEHOLDER, tableName);
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/DatabaseBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/DatabaseBundle.java
new file mode 100644
index 0000000..4ac9029
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/DatabaseBundle.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Data class that holds the schema information for a
+ * {@link android.arch.persistence.room.Database Database}.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class DatabaseBundle {
+    @SerializedName("version")
+    private int mVersion;
+    @SerializedName("identityHash")
+    private String mIdentityHash;
+    @SerializedName("entities")
+    private List<EntityBundle> mEntities;
+    // then entity where we keep room information
+    @SerializedName("setupQueries")
+    private List<String> mSetupQueries;
+    private transient Map<String, EntityBundle> mEntitiesByTableName;
+
+    /**
+     * Creates a new database
+     * @param version Version
+     * @param identityHash Identity hash
+     * @param entities List of entities
+     */
+    public DatabaseBundle(int version, String identityHash, List<EntityBundle> entities,
+            List<String> setupQueries) {
+        mVersion = version;
+        mIdentityHash = identityHash;
+        mEntities = entities;
+        mSetupQueries = setupQueries;
+    }
+
+    /**
+     * @return The identity has of the Database.
+     */
+    public String getIdentityHash() {
+        return mIdentityHash;
+    }
+
+    /**
+     * @return The database version.
+     */
+    public int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * @return List of entities.
+     */
+    public List<EntityBundle> getEntities() {
+        return mEntities;
+    }
+
+    /**
+     * @return Map of entities, keyed by table name.
+     */
+    @SuppressWarnings("unused")
+    public Map<String, EntityBundle> getEntitiesByTableName() {
+        if (mEntitiesByTableName == null) {
+            mEntitiesByTableName = new HashMap<>();
+            for (EntityBundle bundle : mEntities) {
+                mEntitiesByTableName.put(bundle.getTableName(), bundle);
+            }
+        }
+        return mEntitiesByTableName;
+    }
+
+    /**
+     * @return List of SQL queries to build this database from scratch.
+     */
+    public List<String> buildCreateQueries() {
+        List<String> result = new ArrayList<>();
+        for (EntityBundle entityBundle : mEntities) {
+            result.addAll(entityBundle.buildCreateQueries());
+        }
+        result.addAll(mSetupQueries);
+        return result;
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/EntityBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/EntityBundle.java
new file mode 100644
index 0000000..8980a3b
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/EntityBundle.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Data class that holds the schema information about an
+ * {@link android.arch.persistence.room.Entity Entity}.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class EntityBundle {
+
+    static final String NEW_TABLE_PREFIX = "_new_";
+
+    @SerializedName("tableName")
+    private String mTableName;
+    @SerializedName("createSql")
+    private String mCreateSql;
+    @SerializedName("fields")
+    private List<FieldBundle> mFields;
+    @SerializedName("primaryKey")
+    private PrimaryKeyBundle mPrimaryKey;
+    @SerializedName("indices")
+    private List<IndexBundle> mIndices;
+    @SerializedName("foreignKeys")
+    private List<ForeignKeyBundle> mForeignKeys;
+
+    private transient String mNewTableName;
+    private transient Map<String, FieldBundle> mFieldsByColumnName;
+
+    /**
+     * Creates a new bundle.
+     *
+     * @param tableName The table name.
+     * @param createSql Create query with the table name placeholder.
+     * @param fields The list of fields.
+     * @param primaryKey The primary key.
+     * @param indices The list of indices
+     * @param foreignKeys The list of foreign keys
+     */
+    public EntityBundle(String tableName, String createSql,
+            List<FieldBundle> fields,
+            PrimaryKeyBundle primaryKey,
+            List<IndexBundle> indices,
+            List<ForeignKeyBundle> foreignKeys) {
+        mTableName = tableName;
+        mCreateSql = createSql;
+        mFields = fields;
+        mPrimaryKey = primaryKey;
+        mIndices = indices;
+        mForeignKeys = foreignKeys;
+    }
+
+    /**
+     * @return The table name if it is created during a table schema modification.
+     */
+    public String getNewTableName() {
+        if (mNewTableName == null) {
+            mNewTableName = NEW_TABLE_PREFIX + mTableName;
+        }
+        return mNewTableName;
+    }
+
+    /**
+     * @return Map of fields keyed by their column names.
+     */
+    public Map<String, FieldBundle> getFieldsByColumnName() {
+        if (mFieldsByColumnName == null) {
+            mFieldsByColumnName = new HashMap<>();
+            for (FieldBundle bundle : mFields) {
+                mFieldsByColumnName.put(bundle.getColumnName(), bundle);
+            }
+        }
+        return mFieldsByColumnName;
+    }
+
+    /**
+     * @return The table name.
+     */
+    public String getTableName() {
+        return mTableName;
+    }
+
+    /**
+     * @return The create query with table name placeholder.
+     */
+    public String getCreateSql() {
+        return mCreateSql;
+    }
+
+    /**
+     * @return List of fields.
+     */
+    public List<FieldBundle> getFields() {
+        return mFields;
+    }
+
+    /**
+     * @return The primary key description.
+     */
+    public PrimaryKeyBundle getPrimaryKey() {
+        return mPrimaryKey;
+    }
+
+    /**
+     * @return List of indices.
+     */
+    public List<IndexBundle> getIndices() {
+        return mIndices;
+    }
+
+    /**
+     * @return List of foreign keys.
+     */
+    public List<ForeignKeyBundle> getForeignKeys() {
+        return mForeignKeys;
+    }
+
+    /**
+     * @return Create table SQL query that uses the actual table name.
+     */
+    public String createTable() {
+        return BundleUtil.replaceTableName(mCreateSql, getTableName());
+    }
+
+    /**
+     * @return Create table SQL query that uses the table name with "new" prefix.
+     */
+    public String createNewTable() {
+        return BundleUtil.replaceTableName(mCreateSql, getNewTableName());
+    }
+
+    /**
+     * @return Renames the table with {@link #getNewTableName()} to {@link #getTableName()}.
+     */
+    @NotNull
+    public String renameToOriginal() {
+        return "ALTER TABLE " + getNewTableName() + " RENAME TO " + getTableName();
+    }
+
+    /**
+     * @return Creates the list of SQL queries that are necessary to create this entity.
+     */
+    public Collection<String> buildCreateQueries() {
+        List<String> result = new ArrayList<>();
+        result.add(createTable());
+        for (IndexBundle indexBundle : mIndices) {
+            result.add(indexBundle.create(getTableName()));
+        }
+        return result;
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/FieldBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/FieldBundle.java
new file mode 100644
index 0000000..eb73d81
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/FieldBundle.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Data class that holds the schema information for an
+ * {@link android.arch.persistence.room.Entity Entity} field.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class FieldBundle {
+    @SerializedName("fieldPath")
+    private String mFieldPath;
+    @SerializedName("columnName")
+    private String mColumnName;
+    @SerializedName("affinity")
+    private String mAffinity;
+    @SerializedName("notNull")
+    private boolean mNonNull;
+
+    public FieldBundle(String fieldPath, String columnName, String affinity, boolean nonNull) {
+        mFieldPath = fieldPath;
+        mColumnName = columnName;
+        mAffinity = affinity;
+        mNonNull = nonNull;
+    }
+
+    public String getFieldPath() {
+        return mFieldPath;
+    }
+
+    public String getColumnName() {
+        return mColumnName;
+    }
+
+    public String getAffinity() {
+        return mAffinity;
+    }
+
+    public boolean isNonNull() {
+        return mNonNull;
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
new file mode 100644
index 0000000..1467a4f
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+/**
+ * Holds the information about a foreign key reference.
+ */
+public class ForeignKeyBundle {
+    @SerializedName("table")
+    private String mTable;
+    @SerializedName("onDelete")
+    private String mOnDelete;
+    @SerializedName("onUpdate")
+    private String mOnUpdate;
+    @SerializedName("columns")
+    private List<String> mColumns;
+    @SerializedName("referencedColumns")
+    private List<String> mReferencedColumns;
+
+    /**
+     * Creates a foreign key bundle with the given parameters.
+     *
+     * @param table The target table
+     * @param onDelete OnDelete action
+     * @param onUpdate OnUpdate action
+     * @param columns The list of columns in the current table
+     * @param referencedColumns The list of columns in the referenced table
+     */
+    public ForeignKeyBundle(String table, String onDelete, String onUpdate,
+            List<String> columns, List<String> referencedColumns) {
+        mTable = table;
+        mOnDelete = onDelete;
+        mOnUpdate = onUpdate;
+        mColumns = columns;
+        mReferencedColumns = referencedColumns;
+    }
+
+    /**
+     * Returns the table name
+     *
+     * @return Returns the table name
+     */
+    public String getTable() {
+        return mTable;
+    }
+
+    /**
+     * Returns the SQLite foreign key action that will be performed when referenced row is deleted.
+     *
+     * @return The SQLite on delete action
+     */
+    public String getOnDelete() {
+        return mOnDelete;
+    }
+
+    /**
+     * Returns the SQLite foreign key action that will be performed when referenced row is updated.
+     *
+     * @return The SQLite on update action
+     */
+    public String getOnUpdate() {
+        return mOnUpdate;
+    }
+
+    /**
+     * Returns the ordered list of columns in the current table.
+     *
+     * @return The list of columns in the current entity.
+     */
+    public List<String> getColumns() {
+        return mColumns;
+    }
+
+    /**
+     * Returns the ordered list of columns in the referenced table.
+     *
+     * @return The list of columns in the referenced entity.
+     */
+    public List<String> getReferencedColumns() {
+        return mReferencedColumns;
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/IndexBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/IndexBundle.java
new file mode 100644
index 0000000..ba40618
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/IndexBundle.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+/**
+ * Data class that holds the schema information about a table Index.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class IndexBundle {
+    @SerializedName("name")
+    private String mName;
+    @SerializedName("unique")
+    private boolean mUnique;
+    @SerializedName("columnNames")
+    private List<String> mColumnNames;
+    @SerializedName("createSql")
+    private String mCreateSql;
+
+    public IndexBundle(String name, boolean unique, List<String> columnNames,
+            String createSql) {
+        mName = name;
+        mUnique = unique;
+        mColumnNames = columnNames;
+        mCreateSql = createSql;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public boolean isUnique() {
+        return mUnique;
+    }
+
+    public List<String> getColumnNames() {
+        return mColumnNames;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public String create(String tableName) {
+        return BundleUtil.replaceTableName(mCreateSql, tableName);
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/PrimaryKeyBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/PrimaryKeyBundle.java
new file mode 100644
index 0000000..c16f967
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/PrimaryKeyBundle.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+/**
+ * Data class that holds the schema information about a primary key.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class PrimaryKeyBundle {
+    @SerializedName("columnNames")
+    private List<String> mColumnNames;
+    @SerializedName("autoGenerate")
+    private boolean mAutoGenerate;
+
+    public PrimaryKeyBundle(boolean autoGenerate, List<String> columnNames) {
+        mColumnNames = columnNames;
+        mAutoGenerate = autoGenerate;
+    }
+
+    public List<String> getColumnNames() {
+        return mColumnNames;
+    }
+
+    public boolean isAutoGenerate() {
+        return mAutoGenerate;
+    }
+}
diff --git a/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/SchemaBundle.java b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/SchemaBundle.java
new file mode 100644
index 0000000..d6171aa
--- /dev/null
+++ b/room/migration/src/main/java/android/arch/persistence/room/migration/bundle/SchemaBundle.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration.bundle;
+
+import android.support.annotation.RestrictTo;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Data class that holds the information about a database schema export.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SchemaBundle {
+
+    @SerializedName("formatVersion")
+    private int mFormatVersion;
+    @SerializedName("database")
+    private DatabaseBundle mDatabase;
+
+    private static final Gson GSON;
+    private static final String CHARSET = "UTF-8";
+    public static final int LATEST_FORMAT = 1;
+    static {
+        GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
+    }
+
+    public SchemaBundle(int formatVersion, DatabaseBundle database) {
+        mFormatVersion = formatVersion;
+        mDatabase = database;
+    }
+
+    @SuppressWarnings("unused")
+    public int getFormatVersion() {
+        return mFormatVersion;
+    }
+
+    public DatabaseBundle getDatabase() {
+        return mDatabase;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static SchemaBundle deserialize(InputStream fis)
+            throws UnsupportedEncodingException {
+        InputStreamReader is = new InputStreamReader(fis, CHARSET);
+        try {
+            return GSON.fromJson(is, SchemaBundle.class);
+        } finally {
+            safeClose(is);
+            safeClose(fis);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static void serialize(SchemaBundle bundle, File file) throws IOException {
+        FileOutputStream fos = new FileOutputStream(file, false);
+        OutputStreamWriter osw = new OutputStreamWriter(fos, CHARSET);
+        try {
+            GSON.toJson(bundle, osw);
+        } finally {
+            safeClose(osw);
+            safeClose(fos);
+        }
+    }
+
+    private static void safeClose(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (Throwable ignored) {
+            }
+        }
+    }
+
+}
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
new file mode 100644
index 0000000..e44a021
--- /dev/null
+++ b/room/runtime/build.gradle
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    api project(":room:common")
+    api project(":room:db")
+    api project(":room:db-impl")
+    api project(":arch:runtime")
+    provided project(":paging:common")
+
+    provided project(":lifecycle:runtime")
+    provided project(":lifecycle:extensions")
+    compile libs.support.core_utils
+
+
+    testCompile project(":arch:core-testing")
+    testCompile libs.junit
+    testCompile libs.mockito_core
+    testCompile libs.support.annotations
+
+    androidTestCompile libs.junit
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+}
+
+archivesBaseName = "runtime"
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/room/runtime/proguard-rules.pro b/room/runtime/proguard-rules.pro
new file mode 100644
index 0000000..6cdf91c
--- /dev/null
+++ b/room/runtime/proguard-rules.pro
@@ -0,0 +1 @@
+-keep public class * extends android.arch.persistence.room.RoomDatabase
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
new file mode 100644
index 0000000..c6eade5
--- /dev/null
+++ b/room/runtime/src/androidTest/java/android/arch/persistence/room/migration/TableInfoTest.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration;
+
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
+import android.arch.persistence.room.util.TableInfo;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TableInfoTest {
+    private SupportSQLiteDatabase mDb;
+
+    @Test
+    public void readSimple() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT,"
+                        + "name TEXT)");
+        TableInfo info = TableInfo.read(mDb, "foo");
+        assertThat(info, is(new TableInfo("foo",
+                toMap(new TableInfo.Column("id", "INTEGER", false, 1),
+                        new TableInfo.Column("name", "TEXT", false, 0)),
+                Collections.<TableInfo.ForeignKey>emptySet())));
+    }
+
+    @Test
+    public void multiplePrimaryKeys() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (id INTEGER,"
+                        + "name TEXT, PRIMARY KEY(name, id))");
+        TableInfo info = TableInfo.read(mDb, "foo");
+        assertThat(info, is(new TableInfo("foo",
+                toMap(new TableInfo.Column("id", "INTEGER", false, 2),
+                        new TableInfo.Column("name", "TEXT", false, 1)),
+                Collections.<TableInfo.ForeignKey>emptySet())));
+    }
+
+    @Test
+    public void alteredTable() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (id INTEGER,"
+                        + "name TEXT, PRIMARY KEY(name))");
+        mDb.execSQL("ALTER TABLE foo ADD COLUMN added REAL;");
+        TableInfo info = TableInfo.read(mDb, "foo");
+        assertThat(info, is(new TableInfo("foo",
+                toMap(new TableInfo.Column("id", "INTEGER", false, 0),
+                        new TableInfo.Column("name", "TEXT", false, 1),
+                        new TableInfo.Column("added", "REAL", false, 0)),
+                Collections.<TableInfo.ForeignKey>emptySet())));
+    }
+
+    @Test
+    public void nonNull() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (name TEXT NOT NULL)");
+        TableInfo info = TableInfo.read(mDb, "foo");
+        assertThat(info, is(new TableInfo("foo",
+                toMap(new TableInfo.Column("name", "TEXT", true, 0)),
+                Collections.<TableInfo.ForeignKey>emptySet())));
+    }
+
+    @Test
+    public void defaultValue() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (name TEXT DEFAULT blah)");
+        TableInfo info = TableInfo.read(mDb, "foo");
+        assertThat(info, is(new TableInfo(
+                "foo",
+                toMap(new TableInfo.Column("name", "TEXT", false, 0)),
+                Collections.<TableInfo.ForeignKey>emptySet())));
+    }
+
+    @Test
+    public void foreignKey() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (name TEXT)",
+                "CREATE TABLE bar(barName TEXT, FOREIGN KEY(barName) REFERENCES foo(name))"
+        );
+        TableInfo info = TableInfo.read(mDb, "bar");
+        assertThat(info.foreignKeys.size(), is(1));
+        final TableInfo.ForeignKey foreignKey = info.foreignKeys.iterator().next();
+        assertThat(foreignKey.columnNames, is(singletonList("barName")));
+        assertThat(foreignKey.referenceColumnNames, is(singletonList("name")));
+        assertThat(foreignKey.onDelete, is("NO ACTION"));
+        assertThat(foreignKey.onUpdate, is("NO ACTION"));
+        assertThat(foreignKey.referenceTable, is("foo"));
+    }
+
+    @Test
+    public void multipleForeignKeys() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (name TEXT, lastName TEXT)",
+                "CREATE TABLE foo2 (name TEXT, lastName TEXT)",
+                "CREATE TABLE bar(barName TEXT, barLastName TEXT, "
+                        + " FOREIGN KEY(barName) REFERENCES foo(name) ON UPDATE SET NULL,"
+                        + " FOREIGN KEY(barLastName) REFERENCES foo2(lastName) ON DELETE CASCADE)");
+        TableInfo info = TableInfo.read(mDb, "bar");
+        assertThat(info.foreignKeys.size(), is(2));
+        Set<TableInfo.ForeignKey> expected = new HashSet<>();
+        expected.add(new TableInfo.ForeignKey("foo2", // table
+                "CASCADE", // on delete
+                "NO ACTION", // on update
+                singletonList("barLastName"), // my
+                singletonList("lastName")) // ref
+        );
+        expected.add(new TableInfo.ForeignKey("foo", // table
+                "NO ACTION", // on delete
+                "SET NULL", // on update
+                singletonList("barName"), // mine
+                singletonList("name")/*ref*/));
+        assertThat(info.foreignKeys, equalTo(expected));
+    }
+
+    @Test
+    public void compositeForeignKey() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (name TEXT, lastName TEXT)",
+                "CREATE TABLE bar(barName TEXT, barLastName TEXT, "
+                        + " FOREIGN KEY(barName, barLastName) REFERENCES foo(name, lastName)"
+                        + " ON UPDATE cascade ON DELETE RESTRICT)");
+        TableInfo info = TableInfo.read(mDb, "bar");
+        assertThat(info.foreignKeys.size(), is(1));
+        TableInfo.ForeignKey expected = new TableInfo.ForeignKey(
+                "foo", // table
+                "RESTRICT", // on delete
+                "CASCADE", // on update
+                asList("barName", "barLastName"), // my columns
+                asList("name", "lastName") // ref columns
+        );
+        assertThat(info.foreignKeys.iterator().next(), is(expected));
+    }
+
+    @Test
+    public void caseInsensitiveTypeName() {
+        mDb = createDatabase(
+                "CREATE TABLE foo (n integer)");
+        TableInfo info = TableInfo.read(mDb, "foo");
+        assertThat(info, is(new TableInfo(
+                "foo",
+                toMap(new TableInfo.Column("n", "INTEGER", 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) {
+            result.put(column.name, column);
+        }
+        return result;
+    }
+
+    @After
+    public void closeDb() throws IOException {
+        if (mDb != null && mDb.isOpen()) {
+            mDb.close();
+        }
+    }
+
+    private static SupportSQLiteDatabase createDatabase(final String... queries) {
+        return new FrameworkSQLiteOpenHelperFactory().create(
+                SupportSQLiteOpenHelper.Configuration
+                        .builder(InstrumentationRegistry.getTargetContext())
+                        .name(null)
+                        .version(1)
+                        .callback(new SupportSQLiteOpenHelper.Callback() {
+                            @Override
+                            public void onCreate(SupportSQLiteDatabase db) {
+                                for (String query : queries) {
+                                    db.execSQL(query);
+                                }
+                            }
+
+                            @Override
+                            public void onUpgrade(SupportSQLiteDatabase db, int oldVersion,
+                                    int newVersion) {
+                                throw new IllegalStateException("should not be upgrading");
+                            }
+                        }).build()
+        ).getWritableDatabase();
+    }
+}
diff --git a/room/runtime/src/main/AndroidManifest.xml b/room/runtime/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..54a5b36
--- /dev/null
+++ b/room/runtime/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.persistence.room">
+</manifest>
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/DatabaseConfiguration.java b/room/runtime/src/main/java/android/arch/persistence/room/DatabaseConfiguration.java
new file mode 100644
index 0000000..adf5d4d
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/DatabaseConfiguration.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.List;
+
+/**
+ * Configuration class for a {@link RoomDatabase}.
+ */
+@SuppressWarnings("WeakerAccess")
+public class DatabaseConfiguration {
+    /**
+     * The factory to use to access the database.
+     */
+    @NonNull
+    public final SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory;
+    /**
+     * The context to use while connecting to the database.
+     */
+    @NonNull
+    public final Context context;
+    /**
+     * The name of the database file or null if it is an in-memory database.
+     */
+    @Nullable
+    public final String name;
+
+    /**
+     * Collection of available migrations.
+     */
+    @NonNull
+    public final RoomDatabase.MigrationContainer migrationContainer;
+
+    @Nullable
+    public final List<RoomDatabase.Callback> callbacks;
+
+    /**
+     * Whether Room should throw an exception for queries run on the main thread.
+     */
+    public final boolean allowMainThreadQueries;
+
+    /**
+     * If true, Room should crash if a migration is missing.
+     */
+    public final boolean requireMigration;
+
+    /**
+     * Creates a database configuration with the given values.
+     *
+     * @param context The application context.
+     * @param name Name of the database, can be null if it is in memory.
+     * @param sqliteOpenHelperFactory The open helper factory to use.
+     * @param migrationContainer The migration container for migrations.
+     * @param callbacks The list of callbacks for database events.
+     * @param allowMainThreadQueries Whether to allow main thread reads/writes or not.
+     * @param requireMigration True if Room should require a valid migration if version changes,
+     *                        instead of recreating the tables.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
+            @NonNull SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory,
+            @NonNull RoomDatabase.MigrationContainer migrationContainer,
+            @Nullable List<RoomDatabase.Callback> callbacks,
+            boolean allowMainThreadQueries,
+            boolean requireMigration) {
+        this.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
+        this.context = context;
+        this.name = name;
+        this.migrationContainer = migrationContainer;
+        this.callbacks = callbacks;
+        this.allowMainThreadQueries = allowMainThreadQueries;
+        this.requireMigration = requireMigration;
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/EntityDeletionOrUpdateAdapter.java b/room/runtime/src/main/java/android/arch/persistence/room/EntityDeletionOrUpdateAdapter.java
new file mode 100644
index 0000000..6f4aa68
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/EntityDeletionOrUpdateAdapter.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Implementations of this class knows how to delete or update a particular entity.
+ * <p>
+ * This is an internal library class and all of its implementations are auto-generated.
+ *
+ * @param <T> The type parameter of the entity to be deleted
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@SuppressWarnings({"WeakerAccess", "unused"})
+public abstract class EntityDeletionOrUpdateAdapter<T> extends SharedSQLiteStatement {
+    /**
+     * Creates a DeletionOrUpdateAdapter that can delete or update the entity type T on the given
+     * database.
+     *
+     * @param database The database to delete / update the item in.
+     */
+    public EntityDeletionOrUpdateAdapter(RoomDatabase database) {
+        super(database);
+    }
+
+    /**
+     * Create the deletion or update query
+     *
+     * @return An SQL query that can delete or update instances of T.
+     */
+    protected abstract String createQuery();
+
+    /**
+     * Binds the entity into the given statement.
+     *
+     * @param statement The SQLite statement that prepared for the query returned from
+     *                  createQuery.
+     * @param entity    The entity of type T.
+     */
+    protected abstract void bind(SupportSQLiteStatement statement, T entity);
+
+    /**
+     * Deletes or updates the given entities in the database and returns the affected row count.
+     *
+     * @param entity The entity to delete or update
+     * @return The number of affected rows
+     */
+    public final int handle(T entity) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            bind(stmt, entity);
+            return stmt.executeUpdateDelete();
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Deletes or updates the given entities in the database and returns the affected row count.
+     *
+     * @param entities Entities to delete or update
+     * @return The number of affected rows
+     */
+    public final int handleMultiple(Iterable<T> entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            int total = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                total += stmt.executeUpdateDelete();
+            }
+            return total;
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Deletes or updates the given entities in the database and returns the affected row count.
+     *
+     * @param entities Entities to delete or update
+     * @return The number of affected rows
+     */
+    public final int handleMultiple(T[] entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            int total = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                total += stmt.executeUpdateDelete();
+            }
+            return total;
+        } finally {
+            release(stmt);
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/EntityInsertionAdapter.java b/room/runtime/src/main/java/android/arch/persistence/room/EntityInsertionAdapter.java
new file mode 100644
index 0000000..6cfa332
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/EntityInsertionAdapter.java
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.support.annotation.RestrictTo;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Implementations of this class knows how to insert a particular entity.
+ * <p>
+ * This is an internal library class and all of its implementations are auto-generated.
+ *
+ * @param <T> The type parameter of the entity to be inserted
+ * @hide
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class EntityInsertionAdapter<T> extends SharedSQLiteStatement {
+    /**
+     * Creates an InsertionAdapter that can insert the entity type T into the given database.
+     *
+     * @param database The database to insert into.
+     */
+    public EntityInsertionAdapter(RoomDatabase database) {
+        super(database);
+    }
+
+    /**
+     * Binds the entity into the given statement.
+     *
+     * @param statement The SQLite statement that prepared for the query returned from
+     *                  createInsertQuery.
+     * @param entity    The entity of type T.
+     */
+    protected abstract void bind(SupportSQLiteStatement statement, T entity);
+
+    /**
+     * Inserts the entity into the database.
+     *
+     * @param entity The entity to insert
+     */
+    public final void insert(T entity) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            bind(stmt, entity);
+            stmt.executeInsert();
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database.
+     *
+     * @param entities Entities to insert
+     */
+    public final void insert(T[] entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            for (T entity : entities) {
+                bind(stmt, entity);
+                stmt.executeInsert();
+            }
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database.
+     *
+     * @param entities Entities to insert
+     */
+    public final void insert(Iterable<T> entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            for (T entity : entities) {
+                bind(stmt, entity);
+                stmt.executeInsert();
+            }
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entity into the database and returns the row id.
+     *
+     * @param entity The entity to insert
+     * @return The SQLite row id
+     */
+    public final long insertAndReturnId(T entity) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            bind(stmt, entity);
+            return stmt.executeInsert();
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to insert
+     * @return The SQLite row ids
+     */
+    public final long[] insertAndReturnIdsArray(Collection<T> entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            final long[] result = new long[entities.size()];
+            int index = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                result[index] = stmt.executeInsert();
+                index++;
+            }
+            return result;
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to insert
+     * @return The SQLite row ids
+     */
+    public final long[] insertAndReturnIdsArray(T[] entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            final long[] result = new long[entities.length];
+            int index = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                result[index] = stmt.executeInsert();
+                index++;
+            }
+            return result;
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to insert
+     * @return The SQLite row ids
+     */
+    public final Long[] insertAndReturnIdsArrayBox(Collection<T> entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            final Long[] result = new Long[entities.size()];
+            int index = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                result[index] = stmt.executeInsert();
+                index++;
+            }
+            return result;
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to insert
+     * @return The SQLite row ids
+     */
+    public final Long[] insertAndReturnIdsArrayBox(T[] entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            final Long[] result = new Long[entities.length];
+            int index = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                result[index] = stmt.executeInsert();
+                index++;
+            }
+            return result;
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to insert
+     * @return The SQLite row ids
+     */
+    public final List<Long> insertAndReturnIdsList(T[] entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            final List<Long> result = new ArrayList<>(entities.length);
+            int index = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                result.add(index, stmt.executeInsert());
+                index++;
+            }
+            return result;
+        } finally {
+            release(stmt);
+        }
+    }
+
+    /**
+     * Inserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to insert
+     * @return The SQLite row ids
+     */
+    public final List<Long> insertAndReturnIdsList(Collection<T> entities) {
+        final SupportSQLiteStatement stmt = acquire();
+        try {
+            final List<Long> result = new ArrayList<>(entities.size());
+            int index = 0;
+            for (T entity : entities) {
+                bind(stmt, entity);
+                result.add(index, stmt.executeInsert());
+                index++;
+            }
+            return result;
+        } finally {
+            release(stmt);
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java b/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java
new file mode 100644
index 0000000..619c53d
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java
@@ -0,0 +1,659 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.internal.SafeIterableMap;
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.util.ArraySet;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * InvalidationTracker keeps a list of tables modified by queries and notifies its callbacks about
+ * these tables.
+ */
+// We create an in memory table with (version, table_id) where version is an auto-increment primary
+// key and a table_id (hardcoded int from initialization).
+// ObservedTableTracker tracks list of tables we should be watching (e.g. adding triggers for).
+// Before each beginTransaction, RoomDatabase invokes InvalidationTracker to sync trigger states.
+// After each endTransaction, RoomDatabase invokes InvalidationTracker to refresh invalidated
+// tables.
+// Each update on one of the observed tables triggers an insertion into this table, hence a
+// new version.
+// Unfortunately, we cannot override the previous row because sqlite uses the conflict resolution
+// of the outer query (the thing that triggered us) so we do a cleanup as we sync instead of letting
+// SQLite override the rows.
+// https://sqlite.org/lang_createtrigger.html:  An ON CONFLICT clause may be specified as part of an
+// UPDATE or INSERT action within the body of the trigger. However if an ON CONFLICT clause is
+// specified as part of the statement causing the trigger to fire, then conflict handling policy of
+// the outer statement is used instead.
+public class InvalidationTracker {
+
+    private static final String[] TRIGGERS = new String[]{"UPDATE", "DELETE", "INSERT"};
+
+    private static final String UPDATE_TABLE_NAME = "room_table_modification_log";
+
+    private static final String VERSION_COLUMN_NAME = "version";
+
+    private static final String TABLE_ID_COLUMN_NAME = "table_id";
+
+    private static final String CREATE_VERSION_TABLE_SQL = "CREATE TEMP TABLE " + UPDATE_TABLE_NAME
+            + "(" + VERSION_COLUMN_NAME
+            + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+            + TABLE_ID_COLUMN_NAME
+            + " INTEGER)";
+
+    @VisibleForTesting
+    static final String CLEANUP_SQL = "DELETE FROM " + UPDATE_TABLE_NAME
+            + " WHERE " + VERSION_COLUMN_NAME + " NOT IN( SELECT MAX("
+            + VERSION_COLUMN_NAME + ") FROM " + UPDATE_TABLE_NAME
+            + " GROUP BY " + TABLE_ID_COLUMN_NAME + ")";
+
+    @VisibleForTesting
+    // We always clean before selecting so it is unlikely to have the same row twice and if we
+    // do, it is not a big deal, just more data in the cursor.
+    static final String SELECT_UPDATED_TABLES_SQL = "SELECT * FROM " + UPDATE_TABLE_NAME
+            + " WHERE " + VERSION_COLUMN_NAME
+            + "  > ? ORDER BY " + VERSION_COLUMN_NAME + " ASC;";
+
+    @NonNull
+    @VisibleForTesting
+    ArrayMap<String, Integer> mTableIdLookup;
+    private String[] mTableNames;
+
+    @NonNull
+    @VisibleForTesting
+    long[] mTableVersions;
+
+    private Object[] mQueryArgs = new Object[1];
+
+    // max id in the last syc
+    private long mMaxVersion = -1;
+
+    private final RoomDatabase mDatabase;
+
+    AtomicBoolean mPendingRefresh = new AtomicBoolean(false);
+
+    private volatile boolean mInitialized = false;
+
+    private volatile SupportSQLiteStatement mCleanupStatement;
+
+    private ObservedTableTracker mObservedTableTracker;
+
+    // should be accessed with synchronization only.
+    @VisibleForTesting
+    final SafeIterableMap<Observer, ObserverWrapper> mObserverMap = new SafeIterableMap<>();
+
+    /**
+     * Used by the generated code.
+     *
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public InvalidationTracker(RoomDatabase database, String... tableNames) {
+        mDatabase = database;
+        mObservedTableTracker = new ObservedTableTracker(tableNames.length);
+        mTableIdLookup = new ArrayMap<>();
+        final int size = tableNames.length;
+        mTableNames = new String[size];
+        for (int id = 0; id < size; id++) {
+            final String tableName = tableNames[id].toLowerCase(Locale.US);
+            mTableIdLookup.put(tableName, id);
+            mTableNames[id] = tableName;
+        }
+        mTableVersions = new long[tableNames.length];
+        Arrays.fill(mTableVersions, 0);
+    }
+
+    /**
+     * Internal method to initialize table tracking.
+     * <p>
+     * You should never call this method, it is called by the generated code.
+     */
+    void internalInit(SupportSQLiteDatabase database) {
+        synchronized (this) {
+            if (mInitialized) {
+                Log.e(Room.LOG_TAG, "Invalidation tracker is initialized twice :/.");
+                return;
+            }
+
+            database.beginTransaction();
+            try {
+                database.execSQL("PRAGMA temp_store = MEMORY;");
+                database.execSQL("PRAGMA recursive_triggers='ON';");
+                database.execSQL(CREATE_VERSION_TABLE_SQL);
+                database.setTransactionSuccessful();
+            } finally {
+                database.endTransaction();
+            }
+            mCleanupStatement = database.compileStatement(CLEANUP_SQL);
+            mInitialized = true;
+        }
+    }
+
+    private static void appendTriggerName(StringBuilder builder, String tableName,
+            String triggerType) {
+        builder.append("room_table_modification_trigger_")
+                .append(tableName)
+                .append("_")
+                .append(triggerType);
+    }
+
+    private void stopTrackingTable(SupportSQLiteDatabase writableDb, int tableId) {
+        final String tableName = mTableNames[tableId];
+        StringBuilder stringBuilder = new StringBuilder();
+        for (String trigger : TRIGGERS) {
+            stringBuilder.setLength(0);
+            stringBuilder.append("DROP TRIGGER IF EXISTS ");
+            appendTriggerName(stringBuilder, tableName, trigger);
+            writableDb.execSQL(stringBuilder.toString());
+        }
+    }
+
+    private void startTrackingTable(SupportSQLiteDatabase writableDb, int tableId) {
+        final String tableName = mTableNames[tableId];
+        StringBuilder stringBuilder = new StringBuilder();
+        for (String trigger : TRIGGERS) {
+            stringBuilder.setLength(0);
+            stringBuilder.append("CREATE TEMP TRIGGER IF NOT EXISTS ");
+            appendTriggerName(stringBuilder, tableName, trigger);
+            stringBuilder.append(" AFTER ")
+                    .append(trigger)
+                    .append(" ON ")
+                    .append(tableName)
+                    .append(" BEGIN INSERT OR REPLACE INTO ")
+                    .append(UPDATE_TABLE_NAME)
+                    .append(" VALUES(null, ")
+                    .append(tableId)
+                    .append("); END");
+            writableDb.execSQL(stringBuilder.toString());
+        }
+    }
+
+    /**
+     * Adds the given observer to the observers list and it will be notified if any table it
+     * observes changes.
+     * <p>
+     * Database changes are pulled on another thread so in some race conditions, the observer might
+     * be invoked for changes that were done before it is added.
+     * <p>
+     * If the observer already exists, this is a no-op call.
+     * <p>
+     * If one of the tables in the Observer does not exist in the database, this method throws an
+     * {@link IllegalArgumentException}.
+     *
+     * @param observer The observer which listens the database for changes.
+     */
+    public void addObserver(Observer observer) {
+        final String[] tableNames = observer.mTables;
+        int[] tableIds = new int[tableNames.length];
+        final int size = tableNames.length;
+        long[] versions = new long[tableNames.length];
+
+        // TODO sync versions ?
+        for (int i = 0; i < size; i++) {
+            Integer tableId = mTableIdLookup.get(tableNames[i].toLowerCase(Locale.US));
+            if (tableId == null) {
+                throw new IllegalArgumentException("There is no table with name " + tableNames[i]);
+            }
+            tableIds[i] = tableId;
+            versions[i] = mMaxVersion;
+        }
+        ObserverWrapper wrapper = new ObserverWrapper(observer, tableIds, tableNames, versions);
+        ObserverWrapper currentObserver;
+        synchronized (mObserverMap) {
+            currentObserver = mObserverMap.putIfAbsent(observer, wrapper);
+        }
+        if (currentObserver == null && mObservedTableTracker.onAdded(tableIds)) {
+            AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mSyncTriggers);
+        }
+    }
+
+    /**
+     * Adds an observer but keeps a weak reference back to it.
+     * <p>
+     * Note that you cannot remove this observer once added. It will be automatically removed
+     * when the observer is GC'ed.
+     *
+     * @param observer The observer to which InvalidationTracker will keep a weak reference.
+     * @hide
+     */
+    @SuppressWarnings("unused")
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public void addWeakObserver(Observer observer) {
+        addObserver(new WeakObserver(this, observer));
+    }
+
+    /**
+     * Removes the observer from the observers list.
+     *
+     * @param observer The observer to remove.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void removeObserver(final Observer observer) {
+        ObserverWrapper wrapper;
+        synchronized (mObserverMap) {
+            wrapper = mObserverMap.remove(observer);
+        }
+        if (wrapper != null && mObservedTableTracker.onRemoved(wrapper.mTableIds)) {
+            AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mSyncTriggers);
+        }
+    }
+
+    private Runnable mSyncTriggers = new Runnable() {
+        @Override
+        public void run() {
+            if (mDatabase.inTransaction()) {
+                // we won't run this inside another transaction.
+                return;
+            }
+            if (!ensureInitialization()) {
+                return;
+            }
+            try {
+                // This method runs in a while loop because while changes are synced to db, another
+                // runnable may be skipped. If we cause it to skip, we need to do its work.
+                while (true) {
+                    // there is a potential race condition where another mSyncTriggers runnable
+                    // can start running right after we get the tables list to sync.
+                    final int[] tablesToSync = mObservedTableTracker.getTablesToSync();
+                    if (tablesToSync == null) {
+                        return;
+                    }
+                    final int limit = tablesToSync.length;
+                    final SupportSQLiteDatabase writableDatabase = mDatabase.getOpenHelper()
+                            .getWritableDatabase();
+                    try {
+                        writableDatabase.beginTransaction();
+                        for (int tableId = 0; tableId < limit; tableId++) {
+                            switch (tablesToSync[tableId]) {
+                                case ObservedTableTracker.ADD:
+                                    startTrackingTable(writableDatabase, tableId);
+                                    break;
+                                case ObservedTableTracker.REMOVE:
+                                    stopTrackingTable(writableDatabase, tableId);
+                                    break;
+                            }
+                        }
+                        writableDatabase.setTransactionSuccessful();
+                    } finally {
+                        writableDatabase.endTransaction();
+                    }
+                    mObservedTableTracker.onSyncCompleted();
+                }
+            } catch (IllegalStateException | SQLiteException exception) {
+                // may happen if db is closed. just log.
+                Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
+                        exception);
+            }
+        }
+    };
+
+    private boolean ensureInitialization() {
+        if (!mDatabase.isOpen()) {
+            return false;
+        }
+        if (!mInitialized) {
+            // trigger initialization
+            mDatabase.getOpenHelper().getWritableDatabase();
+        }
+        if (!mInitialized) {
+            Log.e(Room.LOG_TAG, "database is not initialized even though it is open");
+            return false;
+        }
+        return true;
+    }
+
+    @VisibleForTesting
+    Runnable mRefreshRunnable = new Runnable() {
+        @Override
+        public void run() {
+            final Lock closeLock = mDatabase.getCloseLock();
+            boolean hasUpdatedTable = false;
+            try {
+                closeLock.lock();
+
+                if (!ensureInitialization()) {
+                    return;
+                }
+
+                if (mDatabase.inTransaction()
+                        || !mPendingRefresh.compareAndSet(true, false)) {
+                    // no pending refresh
+                    return;
+                }
+                mCleanupStatement.executeUpdateDelete();
+                mQueryArgs[0] = mMaxVersion;
+                Cursor cursor = mDatabase.query(SELECT_UPDATED_TABLES_SQL, mQueryArgs);
+                //noinspection TryFinallyCanBeTryWithResources
+                try {
+                    while (cursor.moveToNext()) {
+                        final long version = cursor.getLong(0);
+                        final int tableId = cursor.getInt(1);
+
+                        mTableVersions[tableId] = version;
+                        hasUpdatedTable = true;
+                        // result is ordered so we can safely do this assignment
+                        mMaxVersion = version;
+                    }
+                } finally {
+                    cursor.close();
+                }
+            } catch (IllegalStateException | SQLiteException exception) {
+                // may happen if db is closed. just log.
+                Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
+                        exception);
+            } finally {
+                closeLock.unlock();
+            }
+            if (hasUpdatedTable) {
+                synchronized (mObserverMap) {
+                    for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
+                        entry.getValue().checkForInvalidation(mTableVersions);
+                    }
+                }
+            }
+        }
+    };
+
+    /**
+     * Enqueues a task to refresh the list of updated tables.
+     * <p>
+     * This method is automatically called when {@link RoomDatabase#endTransaction()} is called but
+     * if you have another connection to the database or directly use {@link
+     * SupportSQLiteDatabase}, you may need to call this manually.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void refreshVersionsAsync() {
+        // TODO we should consider doing this sync instead of async.
+        if (mPendingRefresh.compareAndSet(false, true)) {
+            AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
+        }
+    }
+
+    /**
+     * Check versions for tables, and run observers synchronously if tables have been updated.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @WorkerThread
+    public void refreshVersionsSync() {
+        syncTriggers();
+        mRefreshRunnable.run();
+    }
+
+    /**
+     * Called by RoomDatabase before each beginTransaction call.
+     * <p>
+     * It is important that pending trigger changes are applied to the database before any query
+     * runs. Otherwise, we may miss some changes.
+     * <p>
+     * This api should eventually be public.
+     */
+    void syncTriggers() {
+        mSyncTriggers.run();
+    }
+
+    /**
+     * Wraps an observer and keeps the table information.
+     * <p>
+     * Internally table ids are used which may change from database to database so the table
+     * related information is kept here rather than in the Observer.
+     */
+    @SuppressWarnings("WeakerAccess")
+    static class ObserverWrapper {
+        final int[] mTableIds;
+        private final String[] mTableNames;
+        private final long[] mVersions;
+        final Observer mObserver;
+        private final Set<String> mSingleTableSet;
+
+        ObserverWrapper(Observer observer, int[] tableIds, String[] tableNames, long[] versions) {
+            mObserver = observer;
+            mTableIds = tableIds;
+            mTableNames = tableNames;
+            mVersions = versions;
+            if (tableIds.length == 1) {
+                ArraySet<String> set = new ArraySet<>();
+                set.add(mTableNames[0]);
+                mSingleTableSet = Collections.unmodifiableSet(set);
+            } else {
+                mSingleTableSet = null;
+            }
+        }
+
+        void checkForInvalidation(long[] versions) {
+            Set<String> invalidatedTables = null;
+            final int size = mTableIds.length;
+            for (int index = 0; index < size; index++) {
+                final int tableId = mTableIds[index];
+                final long newVersion = versions[tableId];
+                final long currentVersion = mVersions[index];
+                if (currentVersion < newVersion) {
+                    mVersions[index] = newVersion;
+                    if (size == 1) {
+                        // Optimization for a single-table observer
+                        invalidatedTables = mSingleTableSet;
+                    } else {
+                        if (invalidatedTables == null) {
+                            invalidatedTables = new ArraySet<>(size);
+                        }
+                        invalidatedTables.add(mTableNames[index]);
+                    }
+                }
+            }
+            if (invalidatedTables != null) {
+                mObserver.onInvalidated(invalidatedTables);
+            }
+        }
+    }
+
+    /**
+     * An observer that can listen for changes in the database.
+     */
+    public abstract static class Observer {
+        final String[] mTables;
+
+        /**
+         * Observes the given list of tables.
+         *
+         * @param firstTable The table name
+         * @param rest       More table names
+         */
+        @SuppressWarnings("unused")
+        protected Observer(@NonNull String firstTable, String... rest) {
+            mTables = Arrays.copyOf(rest, rest.length + 1);
+            mTables[rest.length] = firstTable;
+        }
+
+        /**
+         * Observes the given list of tables.
+         *
+         * @param tables The list of tables to observe for changes.
+         */
+        public Observer(@NonNull String[] tables) {
+            // copy tables in case user modifies them afterwards
+            mTables = Arrays.copyOf(tables, tables.length);
+        }
+
+        /**
+         * Called when one of the observed tables is invalidated in the database.
+         *
+         * @param tables A set of invalidated tables. This is useful when the observer targets
+         *               multiple tables and want to know which table is invalidated.
+         */
+        public abstract void onInvalidated(@NonNull Set<String> tables);
+    }
+
+
+    /**
+     * Keeps a list of tables we should observe. Invalidation tracker lazily syncs this list w/
+     * triggers in the database.
+     * <p>
+     * This class is thread safe
+     */
+    static class ObservedTableTracker {
+        static final int NO_OP = 0; // don't change trigger state for this table
+        static final int ADD = 1; // add triggers for this table
+        static final int REMOVE = 2; // remove triggers for this table
+
+        // number of observers per table
+        final long[] mTableObservers;
+        // trigger state for each table at last sync
+        // this field is updated when syncAndGet is called.
+        final boolean[] mTriggerStates;
+        // when sync is called, this field is returned. It includes actions as ADD, REMOVE, NO_OP
+        final int[] mTriggerStateChanges;
+
+        boolean mNeedsSync;
+
+        /**
+         * After we return non-null value from getTablesToSync, we expect a onSyncCompleted before
+         * returning any non-null value from getTablesToSync.
+         * This allows us to workaround any multi-threaded state syncing issues.
+         */
+        boolean mPendingSync;
+
+        ObservedTableTracker(int tableCount) {
+            mTableObservers = new long[tableCount];
+            mTriggerStates = new boolean[tableCount];
+            mTriggerStateChanges = new int[tableCount];
+            Arrays.fill(mTableObservers, 0);
+            Arrays.fill(mTriggerStates, false);
+        }
+
+        /**
+         * @return true if # of triggers is affected.
+         */
+        boolean onAdded(int... tableIds) {
+            boolean needTriggerSync = false;
+            synchronized (this) {
+                for (int tableId : tableIds) {
+                    final long prevObserverCount = mTableObservers[tableId];
+                    mTableObservers[tableId] = prevObserverCount + 1;
+                    if (prevObserverCount == 0) {
+                        mNeedsSync = true;
+                        needTriggerSync = true;
+                    }
+                }
+            }
+            return needTriggerSync;
+        }
+
+        /**
+         * @return true if # of triggers is affected.
+         */
+        boolean onRemoved(int... tableIds) {
+            boolean needTriggerSync = false;
+            synchronized (this) {
+                for (int tableId : tableIds) {
+                    final long prevObserverCount = mTableObservers[tableId];
+                    mTableObservers[tableId] = prevObserverCount - 1;
+                    if (prevObserverCount == 1) {
+                        mNeedsSync = true;
+                        needTriggerSync = true;
+                    }
+                }
+            }
+            return needTriggerSync;
+        }
+
+        /**
+         * If this returns non-null, you must call onSyncCompleted.
+         *
+         * @return int[] An int array where the index for each tableId has the action for that
+         * table.
+         */
+        @Nullable
+        int[] getTablesToSync() {
+            synchronized (this) {
+                if (!mNeedsSync || mPendingSync) {
+                    return null;
+                }
+                final int tableCount = mTableObservers.length;
+                for (int i = 0; i < tableCount; i++) {
+                    final boolean newState = mTableObservers[i] > 0;
+                    if (newState != mTriggerStates[i]) {
+                        mTriggerStateChanges[i] = newState ? ADD : REMOVE;
+                    } else {
+                        mTriggerStateChanges[i] = NO_OP;
+                    }
+                    mTriggerStates[i] = newState;
+                }
+                mPendingSync = true;
+                mNeedsSync = false;
+                return mTriggerStateChanges;
+            }
+        }
+
+        /**
+         * if getTablesToSync returned non-null, the called should call onSyncCompleted once it
+         * is done.
+         */
+        void onSyncCompleted() {
+            synchronized (this) {
+                mPendingSync = false;
+            }
+        }
+    }
+
+    /**
+     * An Observer wrapper that keeps a weak reference to the given object.
+     * <p>
+     * This class with automatically unsubscribe when the wrapped observer goes out of memory.
+     */
+    static class WeakObserver extends Observer {
+        final InvalidationTracker mTracker;
+        final WeakReference<Observer> mDelegateRef;
+
+        WeakObserver(InvalidationTracker tracker, Observer delegate) {
+            super(delegate.mTables);
+            mTracker = tracker;
+            mDelegateRef = new WeakReference<>(delegate);
+        }
+
+        @Override
+        public void onInvalidated(@NonNull Set<String> tables) {
+            final Observer observer = mDelegateRef.get();
+            if (observer == null) {
+                mTracker.removeObserver(this);
+            } else {
+                observer.onInvalidated(tables);
+            }
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/Room.java b/room/runtime/src/main/java/android/arch/persistence/room/Room.java
new file mode 100644
index 0000000..80e95a7
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/Room.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+/**
+ * Utility class for Room.
+ */
+@SuppressWarnings("unused")
+public class Room {
+    static final String LOG_TAG = "ROOM";
+    /**
+     * The master table where room keeps its metadata information.
+     */
+    public static final String MASTER_TABLE_NAME = RoomMasterTable.TABLE_NAME;
+    private static final String CURSOR_CONV_SUFFIX = "_CursorConverter";
+
+    /**
+     * Creates a RoomDatabase.Builder for a persistent database. Once a database is built, you
+     * should keep a reference to it and re-use it.
+     *
+     * @param context The context for the database. This is usually the Application context.
+     * @param klass   The abstract class which is annotated with {@link Database} and extends
+     *                {@link RoomDatabase}.
+     * @param name    The name of the database file.
+     * @param <T>     The type of the database class.
+     * @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
+            @NonNull Context context, @NonNull Class<T> klass, @NonNull String name) {
+        //noinspection ConstantConditions
+        if (name == null || name.trim().length() == 0) {
+            throw new IllegalArgumentException("Cannot create a database with null or empty name."
+                    + " If you are trying to create an in memory database, use Room"
+                    + ".inMemoryDatabaseBuilder");
+        }
+        return new RoomDatabase.Builder<>(context, klass, name);
+    }
+
+    /**
+     * Creates a RoomDatabase.Builder for an in memory database. Information stored in an in memory
+     * database disappears when the process is killed.
+     * Once a database is built, you should keep a reference to it and re-use it.
+     *
+     * @param context The context for the database. This is usually the Application context.
+     * @param klass   The abstract class which is annotated with {@link Database} and extends
+     *                {@link RoomDatabase}.
+     * @param <T>     The type of the database class.
+     * @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
+     */
+    public static <T extends RoomDatabase> RoomDatabase.Builder<T> inMemoryDatabaseBuilder(
+            @NonNull Context context, @NonNull Class<T> klass) {
+        return new RoomDatabase.Builder<>(context, klass, null);
+    }
+
+    @NonNull
+    static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
+        final String fullPackage = klass.getPackage().getName();
+        String name = klass.getCanonicalName();
+        final String postPackageName = fullPackage.isEmpty()
+                ? name
+                : (name.substring(fullPackage.length() + 1));
+        final String implName = postPackageName.replace('.', '_') + suffix;
+        //noinspection TryWithIdenticalCatches
+        try {
+
+            @SuppressWarnings("unchecked")
+            final Class<T> aClass = (Class<T>) Class.forName(
+                    fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
+            return aClass.newInstance();
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException("cannot find implementation for "
+                    + klass.getCanonicalName() + ". " + implName + " does not exist");
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException("Cannot access the constructor"
+                    + klass.getCanonicalName());
+        } catch (InstantiationException e) {
+            throw new RuntimeException("Failed to create an instance of "
+                    + klass.getCanonicalName());
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java b/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
new file mode 100644
index 0000000..e64f2d6
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
@@ -0,0 +1,560 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.persistence.db.SimpleSQLiteQuery;
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.SupportSQLiteQuery;
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
+import android.arch.persistence.room.migration.Migration;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.util.SparseArrayCompat;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Base class for all Room databases. All classes that are annotated with {@link Database} must
+ * extend this class.
+ * <p>
+ * RoomDatabase provides direct access to the underlying database implementation but you should
+ * prefer using {@link Dao} classes.
+ *
+ * @see Database
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public abstract class RoomDatabase {
+    private static final String DB_IMPL_SUFFIX = "_Impl";
+    // set by the generated open helper.
+    protected volatile SupportSQLiteDatabase mDatabase;
+    private SupportSQLiteOpenHelper mOpenHelper;
+    private final InvalidationTracker mInvalidationTracker;
+    private boolean mAllowMainThreadQueries;
+
+    @Nullable
+    protected List<Callback> mCallbacks;
+
+    private final ReentrantLock mCloseLock = new ReentrantLock();
+
+    /**
+     * {@link InvalidationTracker} uses this lock to prevent the database from closing while it is
+     * querying database updates.
+     *
+     * @return The lock for {@link #close()}.
+     */
+    Lock getCloseLock() {
+        return mCloseLock;
+    }
+
+    /**
+     * Creates a RoomDatabase.
+     * <p>
+     * You cannot create an instance of a database, instead, you should acquire it via
+     * {@link Room#databaseBuilder(Context, Class, String)} or
+     * {@link Room#inMemoryDatabaseBuilder(Context, Class)}.
+     */
+    public RoomDatabase() {
+        mInvalidationTracker = createInvalidationTracker();
+    }
+
+    /**
+     * Called by {@link Room} when it is initialized.
+     *
+     * @param configuration The database configuration.
+     */
+    @CallSuper
+    public void init(DatabaseConfiguration configuration) {
+        mOpenHelper = createOpenHelper(configuration);
+        mCallbacks = configuration.callbacks;
+        mAllowMainThreadQueries = configuration.allowMainThreadQueries;
+    }
+
+    /**
+     * Returns the SQLite open helper used by this database.
+     *
+     * @return The SQLite open helper used by this database.
+     */
+    public SupportSQLiteOpenHelper getOpenHelper() {
+        return mOpenHelper;
+    }
+
+    /**
+     * Creates the open helper to access the database. Generated class already implements this
+     * method.
+     * Note that this method is called when the RoomDatabase is initialized.
+     *
+     * @param config The configuration of the Room database.
+     * @return A new SupportSQLiteOpenHelper to be used while connecting to the database.
+     */
+    protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
+
+    /**
+     * Called when the RoomDatabase is created.
+     * <p>
+     * This is already implemented by the generated code.
+     *
+     * @return Creates a new InvalidationTracker.
+     */
+    protected abstract InvalidationTracker createInvalidationTracker();
+
+    /**
+     * Returns true if database connection is open and initialized.
+     *
+     * @return true if the database connection is open, false otherwise.
+     */
+    public boolean isOpen() {
+        final SupportSQLiteDatabase db = mDatabase;
+        return db != null && db.isOpen();
+    }
+
+    /**
+     * Closes the database if it is already open.
+     */
+    public void close() {
+        if (isOpen()) {
+            try {
+                mCloseLock.lock();
+                mOpenHelper.close();
+            } finally {
+                mCloseLock.unlock();
+            }
+        }
+    }
+
+    /**
+     * Asserts that we are not on the main thread.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public void assertNotMainThread() {
+        if (mAllowMainThreadQueries) {
+            return;
+        }
+        if (AppToolkitTaskExecutor.getInstance().isMainThread()) {
+            throw new IllegalStateException("Cannot access database on the main thread since"
+                    + " it may potentially lock the UI for a long period of time.");
+        }
+    }
+
+    // Below, there are wrapper methods for SupportSQLiteDatabase. This helps us track which
+    // methods we are using and also helps unit tests to mock this class without mocking
+    // all SQLite database methods.
+
+    /**
+     * Convenience method to query the database with arguments.
+     *
+     * @param query The sql query
+     * @param args The bind arguments for the placeholders in the query
+     *
+     * @return A Cursor obtained by running the given query in the Room database.
+     */
+    public Cursor query(String query, @Nullable Object[] args) {
+        return mOpenHelper.getWritableDatabase().query(new SimpleSQLiteQuery(query, args));
+    }
+
+    /**
+     * Wrapper for {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.
+     *
+     * @param query The Query which includes the SQL and a bind callback for bind arguments.
+     * @return Result of the query.
+     */
+    public Cursor query(SupportSQLiteQuery query) {
+        assertNotMainThread();
+        return mOpenHelper.getWritableDatabase().query(query);
+    }
+
+    /**
+     * Wrapper for {@link SupportSQLiteDatabase#compileStatement(String)}.
+     *
+     * @param sql The query to compile.
+     * @return The compiled query.
+     */
+    public SupportSQLiteStatement compileStatement(String sql) {
+        assertNotMainThread();
+        return mOpenHelper.getWritableDatabase().compileStatement(sql);
+    }
+
+    /**
+     * Wrapper for {@link SupportSQLiteDatabase#beginTransaction()}.
+     */
+    public void beginTransaction() {
+        assertNotMainThread();
+        mInvalidationTracker.syncTriggers();
+        mOpenHelper.getWritableDatabase().beginTransaction();
+    }
+
+    /**
+     * Wrapper for {@link SupportSQLiteDatabase#endTransaction()}.
+     */
+    public void endTransaction() {
+        mOpenHelper.getWritableDatabase().endTransaction();
+        mInvalidationTracker.refreshVersionsAsync();
+    }
+
+    /**
+     * Wrapper for {@link SupportSQLiteDatabase#setTransactionSuccessful()}.
+     */
+    public void setTransactionSuccessful() {
+        mOpenHelper.getWritableDatabase().setTransactionSuccessful();
+    }
+
+    /**
+     * Executes the specified {@link Runnable} in a database transaction. The transaction will be
+     * marked as successful unless an exception is thrown in the {@link Runnable}.
+     *
+     * @param body The piece of code to execute.
+     */
+    public void runInTransaction(Runnable body) {
+        beginTransaction();
+        try {
+            body.run();
+            setTransactionSuccessful();
+        } finally {
+            endTransaction();
+        }
+    }
+
+    /**
+     * Executes the specified {@link Callable} in a database transaction. The transaction will be
+     * marked as successful unless an exception is thrown in the {@link Callable}.
+     *
+     * @param body The piece of code to execute.
+     * @param <V>  The type of the return value.
+     * @return The value returned from the {@link Callable}.
+     */
+    public <V> V runInTransaction(Callable<V> body) {
+        beginTransaction();
+        try {
+            V result = body.call();
+            setTransactionSuccessful();
+            return result;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException("Exception in transaction", e);
+        } finally {
+            endTransaction();
+        }
+    }
+
+    /**
+     * Called by the generated code when database is open.
+     * <p>
+     * You should never call this method manually.
+     *
+     * @param db The database instance.
+     */
+    protected void internalInitInvalidationTracker(SupportSQLiteDatabase db) {
+        mInvalidationTracker.internalInit(db);
+    }
+
+    /**
+     * Returns the invalidation tracker for this database.
+     * <p>
+     * You can use the invalidation tracker to get notified when certain tables in the database
+     * are modified.
+     *
+     * @return The invalidation tracker for the database.
+     */
+    public InvalidationTracker getInvalidationTracker() {
+        return mInvalidationTracker;
+    }
+
+    /**
+     * Returns true if current thread is in a transaction.
+     *
+     * @return True if there is an active transaction in current thread, false otherwise.
+     * @see SupportSQLiteDatabase#inTransaction()
+     */
+    public boolean inTransaction() {
+        return mOpenHelper.getWritableDatabase().inTransaction();
+    }
+
+    /**
+     * Builder for RoomDatabase.
+     *
+     * @param <T> The type of the abstract database class.
+     */
+    @SuppressWarnings("unused")
+    public static class Builder<T extends RoomDatabase> {
+        private final Class<T> mDatabaseClass;
+        private final String mName;
+        private final Context mContext;
+        private ArrayList<Callback> mCallbacks;
+
+        private SupportSQLiteOpenHelper.Factory mFactory;
+        private boolean mInMemory;
+        private boolean mAllowMainThreadQueries;
+        private boolean mRequireMigration;
+        /**
+         * Migrations, mapped by from-to pairs.
+         */
+        private MigrationContainer mMigrationContainer;
+
+        Builder(@NonNull Context context, @NonNull Class<T> klass, @Nullable String name) {
+            mContext = context;
+            mDatabaseClass = klass;
+            mName = name;
+            mRequireMigration = true;
+            mMigrationContainer = new MigrationContainer();
+        }
+
+        /**
+         * Sets the database factory. If not set, it defaults to
+         * {@link FrameworkSQLiteOpenHelperFactory}.
+         *
+         * @param factory The factory to use to access the database.
+         * @return this
+         */
+        public Builder<T> openHelperFactory(SupportSQLiteOpenHelper.Factory factory) {
+            mFactory = factory;
+            return this;
+        }
+
+        /**
+         * Adds a migration to the builder.
+         * <p>
+         * Each Migration has a start and end versions and Room runs these migrations to bring the
+         * database to the latest version.
+         * <p>
+         * If a migration item is missing between current version and the latest version, Room
+         * will clear the database and recreate so even if you have no changes between 2 versions,
+         * you should still provide a Migration object to the builder.
+         * <p>
+         * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
+         * going version 3 to 5 without going to version 4). If Room opens a database at version
+         * 3 and latest version is &gt;= 5, Room will use the migration object that can migrate from
+         * 3 to 5 instead of 3 to 4 and 4 to 5.
+         *
+         * @param migrations The migration object that can modify the database and to the necessary
+         *                   changes.
+         * @return this
+         */
+        public Builder<T> addMigrations(Migration... migrations) {
+            mMigrationContainer.addMigrations(migrations);
+            return this;
+        }
+
+        /**
+         * Disables the main thread query check for Room.
+         * <p>
+         * Room ensures that Database is never accessed on the main thread because it may lock the
+         * main thread and trigger an ANR. If you need to access the database from the main thread,
+         * you should always use async alternatives or manually move the call to a background
+         * thread.
+         * <p>
+         * You may want to turn this check off for testing.
+         *
+         * @return this
+         */
+        public Builder<T> allowMainThreadQueries() {
+            mAllowMainThreadQueries = true;
+            return this;
+        }
+
+        /**
+         * When the database version on the device does not match the latest schema version, Room
+         * runs necessary {@link Migration}s on the database.
+         * <p>
+         * If it cannot find the set of {@link Migration}s that will bring the database to the
+         * current version, it will throw an {@link IllegalStateException}.
+         * <p>
+         * You can call this method to change this behavior to re-create the database instead of
+         * crashing.
+         * <p>
+         * Note that this will delete all of the data in the database tables managed by Room.
+         *
+         * @return this
+         */
+        public Builder<T> fallbackToDestructiveMigration() {
+            mRequireMigration = false;
+            return this;
+        }
+
+        /**
+         * Adds a {@link Callback} to this database.
+         *
+         * @param callback The callback.
+         * @return this
+         */
+        public Builder<T> addCallback(@NonNull Callback callback) {
+            if (mCallbacks == null) {
+                mCallbacks = new ArrayList<>();
+            }
+            mCallbacks.add(callback);
+            return this;
+        }
+
+        /**
+         * Creates the databases and initializes it.
+         * <p>
+         * By default, all RoomDatabases use in memory storage for TEMP tables and enables recursive
+         * triggers.
+         *
+         * @return A new database instance.
+         */
+        public T build() {
+            //noinspection ConstantConditions
+            if (mContext == null) {
+                throw new IllegalArgumentException("Cannot provide null context for the database.");
+            }
+            //noinspection ConstantConditions
+            if (mDatabaseClass == null) {
+                throw new IllegalArgumentException("Must provide an abstract class that"
+                        + " extends RoomDatabase");
+            }
+            if (mFactory == null) {
+                mFactory = new FrameworkSQLiteOpenHelperFactory();
+            }
+            DatabaseConfiguration configuration =
+                    new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
+                            mCallbacks, mAllowMainThreadQueries, mRequireMigration);
+            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
+            db.init(configuration);
+            return db;
+        }
+    }
+
+    /**
+     * A container to hold migrations. It also allows querying its contents to find migrations
+     * between two versions.
+     */
+    public static class MigrationContainer {
+        private SparseArrayCompat<SparseArrayCompat<Migration>> mMigrations =
+                new SparseArrayCompat<>();
+
+        /**
+         * Adds the given migrations to the list of available migrations. If 2 migrations have the
+         * same start-end versions, the latter migration overrides the previous one.
+         *
+         * @param migrations List of available migrations.
+         */
+        public void addMigrations(Migration... migrations) {
+            for (Migration migration : migrations) {
+                addMigration(migration);
+            }
+        }
+
+        private void addMigration(Migration migration) {
+            final int start = migration.startVersion;
+            final int end = migration.endVersion;
+            SparseArrayCompat<Migration> targetMap = mMigrations.get(start);
+            if (targetMap == null) {
+                targetMap = new SparseArrayCompat<>();
+                mMigrations.put(start, targetMap);
+            }
+            Migration existing = targetMap.get(end);
+            if (existing != null) {
+                Log.w(Room.LOG_TAG, "Overriding migration " + existing + " with " + migration);
+            }
+            targetMap.append(end, migration);
+        }
+
+        /**
+         * Finds the list of migrations that should be run to move from {@code start} version to
+         * {@code end} version.
+         *
+         * @param start The current database version
+         * @param end   The target database version
+         * @return An ordered list of {@link Migration} objects that should be run to migrate
+         * between the given versions. If a migration path cannot be found, returns {@code null}.
+         */
+        @Nullable
+        public List<Migration> findMigrationPath(int start, int end) {
+            if (start == end) {
+                return Collections.emptyList();
+            }
+            boolean migrateUp = end > start;
+            List<Migration> result = new ArrayList<>();
+            return findUpMigrationPath(result, migrateUp, start, end);
+        }
+
+        private List<Migration> findUpMigrationPath(List<Migration> result, boolean upgrade,
+                int start, int end) {
+            final int searchDirection = upgrade ? -1 : 1;
+            while (upgrade ? start < end : start > end) {
+                SparseArrayCompat<Migration> targetNodes = mMigrations.get(start);
+                if (targetNodes == null) {
+                    return null;
+                }
+                // keys are ordered so we can start searching from one end of them.
+                final int size = targetNodes.size();
+                final int firstIndex;
+                final int lastIndex;
+
+                if (upgrade) {
+                    firstIndex = size - 1;
+                    lastIndex = -1;
+                } else {
+                    firstIndex = 0;
+                    lastIndex = size;
+                }
+                boolean found = false;
+                for (int i = firstIndex; i != lastIndex; i += searchDirection) {
+                    int targetVersion = targetNodes.keyAt(i);
+                    if (targetVersion <= end && targetVersion > start) {
+                        result.add(targetNodes.valueAt(i));
+                        start = targetVersion;
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    return null;
+                }
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Callback for {@link RoomDatabase}.
+     */
+    public abstract static class Callback {
+
+        /**
+         * Called when the database is created for the first time. This is called after all the
+         * tables are created.
+         *
+         * @param db The database.
+         */
+        public void onCreate(@NonNull SupportSQLiteDatabase db) {
+        }
+
+        /**
+         * Called when the database has been opened.
+         *
+         * @param db The database.
+         */
+        public void onOpen(@NonNull SupportSQLiteDatabase db) {
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/RoomOpenHelper.java b/room/runtime/src/main/java/android/arch/persistence/room/RoomOpenHelper.java
new file mode 100644
index 0000000..8767f06
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/RoomOpenHelper.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SimpleSQLiteQuery;
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.room.migration.Migration;
+import android.database.Cursor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.List;
+
+/**
+ * An open helper that holds a reference to the configuration until the database is opened.
+ *
+ * @hide
+ */
+@SuppressWarnings("unused")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class RoomOpenHelper extends SupportSQLiteOpenHelper.Callback {
+    @Nullable
+    private DatabaseConfiguration mConfiguration;
+    @NonNull
+    private final Delegate mDelegate;
+    @NonNull
+    private final String mIdentityHash;
+
+    public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate,
+            @NonNull String identityHash) {
+        mConfiguration = configuration;
+        mDelegate = delegate;
+        mIdentityHash = identityHash;
+    }
+
+    @Override
+    public void onConfigure(SupportSQLiteDatabase db) {
+        super.onConfigure(db);
+    }
+
+    @Override
+    public void onCreate(SupportSQLiteDatabase db) {
+        updateIdentity(db);
+        mDelegate.createAllTables(db);
+        mDelegate.onCreate(db);
+    }
+
+    @Override
+    public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
+        boolean migrated = false;
+        if (mConfiguration != null) {
+            List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath(
+                    oldVersion, newVersion);
+            if (migrations != null) {
+                for (Migration migration : migrations) {
+                    migration.migrate(db);
+                }
+                mDelegate.validateMigration(db);
+                updateIdentity(db);
+                migrated = true;
+            }
+        }
+        if (!migrated) {
+            if (mConfiguration == null || mConfiguration.requireMigration) {
+                throw new IllegalStateException("A migration from " + oldVersion + " to "
+                + newVersion + " is necessary. Please provide a Migration in the builder or call"
+                        + " fallbackToDestructiveMigration in the builder in which case Room will"
+                        + " re-create all of the tables.");
+            }
+            mDelegate.dropAllTables(db);
+            mDelegate.createAllTables(db);
+        }
+    }
+
+    @Override
+    public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
+        onUpgrade(db, oldVersion, newVersion);
+    }
+
+    @Override
+    public void onOpen(SupportSQLiteDatabase db) {
+        super.onOpen(db);
+        checkIdentity(db);
+        mDelegate.onOpen(db);
+        // there might be too many configurations etc, just clear it.
+        mConfiguration = null;
+    }
+
+    private void checkIdentity(SupportSQLiteDatabase db) {
+        createMasterTableIfNotExists(db);
+        String identityHash = "";
+        Cursor cursor = db.query(new SimpleSQLiteQuery(RoomMasterTable.READ_QUERY));
+        //noinspection TryFinallyCanBeTryWithResources
+        try {
+            if (cursor.moveToFirst()) {
+                identityHash = cursor.getString(0);
+            }
+        } finally {
+            cursor.close();
+        }
+        if (!mIdentityHash.equals(identityHash)) {
+            throw new IllegalStateException("Room cannot verify the data integrity. Looks like"
+                    + " you've changed schema but forgot to update the version number. You can"
+                    + " simply fix this by increasing the version number.");
+        }
+    }
+
+    private void updateIdentity(SupportSQLiteDatabase db) {
+        createMasterTableIfNotExists(db);
+        db.execSQL(RoomMasterTable.createInsertQuery(mIdentityHash));
+    }
+
+    private void createMasterTableIfNotExists(SupportSQLiteDatabase db) {
+        db.execSQL(RoomMasterTable.CREATE_QUERY);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public abstract static class Delegate {
+        protected abstract void dropAllTables(SupportSQLiteDatabase database);
+
+        protected abstract void createAllTables(SupportSQLiteDatabase database);
+
+        protected abstract void onOpen(SupportSQLiteDatabase database);
+
+        protected abstract void onCreate(SupportSQLiteDatabase database);
+
+        /**
+         * Called after a migration run to validate database integrity.
+         *
+         * @param db The SQLite database.
+         */
+        protected abstract void validateMigration(SupportSQLiteDatabase db);
+    }
+
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java b/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java
new file mode 100644
index 0000000..a8defd4
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SupportSQLiteProgram;
+import android.arch.persistence.db.SupportSQLiteQuery;
+import android.support.annotation.IntDef;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * This class is used as an intermediate place to keep binding arguments so that we can run
+ * Cursor queries with correct types rather than passing everything as a string.
+ * <p>
+ * Because it is relatively a big object, they are pooled and must be released after each use.
+ *
+ * @hide
+ */
+@SuppressWarnings("unused")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class RoomSQLiteQuery implements SupportSQLiteQuery, SupportSQLiteProgram {
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    // Maximum number of queries we'll keep cached.
+    static final int POOL_LIMIT = 15;
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    // Once we hit POOL_LIMIT, we'll bring the pool size back to the desired number. We always
+    // clear the bigger queries (# of arguments).
+    static final int DESIRED_POOL_SIZE = 10;
+    private volatile String mQuery;
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    final long[] mLongBindings;
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    final double[] mDoubleBindings;
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    final String[] mStringBindings;
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    final byte[][] mBlobBindings;
+
+    @Binding
+    private final int[] mBindingTypes;
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    final int mCapacity;
+    // number of arguments in the query
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    int mArgCount;
+
+
+    @SuppressWarnings("WeakerAccess")
+    @VisibleForTesting
+    static final TreeMap<Integer, RoomSQLiteQuery> sQueryPool = new TreeMap<>();
+
+    /**
+     * Returns a new RoomSQLiteQuery that can accept the given number of arguments and holds the
+     * given query.
+     *
+     * @param query         The query to prepare
+     * @param argumentCount The number of query arguments
+     * @return A RoomSQLiteQuery that holds the given query and has space for the given number of
+     * arguments.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static RoomSQLiteQuery acquire(String query, int argumentCount) {
+        synchronized (sQueryPool) {
+            final Map.Entry<Integer, RoomSQLiteQuery> entry =
+                    sQueryPool.ceilingEntry(argumentCount);
+            if (entry != null) {
+                sQueryPool.remove(entry.getKey());
+                final RoomSQLiteQuery sqliteQuery = entry.getValue();
+                sqliteQuery.init(query, argumentCount);
+                return sqliteQuery;
+            }
+        }
+        RoomSQLiteQuery sqLiteQuery = new RoomSQLiteQuery(argumentCount);
+        sqLiteQuery.init(query, argumentCount);
+        return sqLiteQuery;
+    }
+
+    private RoomSQLiteQuery(int capacity) {
+        mCapacity = capacity;
+        // because, 1 based indices... we don't want to offsets everything with 1 all the time.
+        int limit = capacity + 1;
+        //noinspection WrongConstant
+        mBindingTypes = new int[limit];
+        mLongBindings = new long[limit];
+        mDoubleBindings = new double[limit];
+        mStringBindings = new String[limit];
+        mBlobBindings = new byte[limit][];
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    void init(String query, int argCount) {
+        mQuery = query;
+        mArgCount = argCount;
+    }
+
+    /**
+     * Releases the query back to the pool.
+     * <p>
+     * After released, the statement might be returned when {@link #acquire(String, int)} is called
+     * so you should never re-use it after releasing.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void release() {
+        synchronized (sQueryPool) {
+            sQueryPool.put(mCapacity, this);
+            prunePoolLocked();
+        }
+    }
+
+    private static void prunePoolLocked() {
+        if (sQueryPool.size() > POOL_LIMIT) {
+            int toBeRemoved = sQueryPool.size() - DESIRED_POOL_SIZE;
+            final Iterator<Integer> iterator = sQueryPool.descendingKeySet().iterator();
+            while (toBeRemoved-- > 0) {
+                iterator.next();
+                iterator.remove();
+            }
+        }
+    }
+
+    @Override
+    public String getSql() {
+        return mQuery;
+    }
+
+    public int getArgCount() {
+        return mArgCount;
+    }
+
+    @Override
+    public void bindTo(SupportSQLiteProgram program) {
+        for (int index = 1; index <= mArgCount; index++) {
+            switch (mBindingTypes[index]) {
+                case NULL:
+                    program.bindNull(index);
+                    break;
+                case LONG:
+                    program.bindLong(index, mLongBindings[index]);
+                    break;
+                case DOUBLE:
+                    program.bindDouble(index, mDoubleBindings[index]);
+                    break;
+                case STRING:
+                    program.bindString(index, mStringBindings[index]);
+                    break;
+                case BLOB:
+                    program.bindBlob(index, mBlobBindings[index]);
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void bindNull(int index) {
+        mBindingTypes[index] = NULL;
+    }
+
+    @Override
+    public void bindLong(int index, long value) {
+        mBindingTypes[index] = LONG;
+        mLongBindings[index] = value;
+    }
+
+    @Override
+    public void bindDouble(int index, double value) {
+        mBindingTypes[index] = DOUBLE;
+        mDoubleBindings[index] = value;
+    }
+
+    @Override
+    public void bindString(int index, String value) {
+        mBindingTypes[index] = STRING;
+        mStringBindings[index] = value;
+    }
+
+    @Override
+    public void bindBlob(int index, byte[] value) {
+        mBindingTypes[index] = BLOB;
+        mBlobBindings[index] = value;
+    }
+
+    @Override
+    public void close() throws Exception {
+        // no-op. not calling release because it is internal API.
+    }
+
+    /**
+     * Copies arguments from another RoomSQLiteQuery into this query.
+     *
+     * @param other The other query, which holds the arguments to be copied.
+     */
+    public void copyArgumentsFrom(RoomSQLiteQuery other) {
+        int argCount = other.getArgCount() + 1; // +1 for the binding offsets
+        System.arraycopy(other.mBindingTypes, 0, mBindingTypes, 0, argCount);
+        System.arraycopy(other.mLongBindings, 0, mLongBindings, 0, argCount);
+        System.arraycopy(other.mStringBindings, 0, mStringBindings, 0, argCount);
+        System.arraycopy(other.mBlobBindings, 0, mBlobBindings, 0, argCount);
+        System.arraycopy(other.mDoubleBindings, 0, mDoubleBindings, 0, argCount);
+    }
+
+    @Override
+    public void clearBindings() {
+        Arrays.fill(mBindingTypes, NULL);
+        Arrays.fill(mStringBindings, null);
+        Arrays.fill(mBlobBindings, null);
+        mQuery = null;
+        // no need to clear others
+    }
+
+    private static final int NULL = 1;
+    private static final int LONG = 2;
+    private static final int DOUBLE = 3;
+    private static final int STRING = 4;
+    private static final int BLOB = 5;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({NULL, LONG, DOUBLE, STRING, BLOB})
+    @interface Binding {
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/SharedSQLiteStatement.java b/room/runtime/src/main/java/android/arch/persistence/room/SharedSQLiteStatement.java
new file mode 100644
index 0000000..6b1f8ea
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/SharedSQLiteStatement.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.support.annotation.RestrictTo;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Represents a prepared SQLite state that can be re-used multiple times.
+ * <p>
+ * This class is used by generated code. After it is used, {@code release} must be called so that
+ * it can be used by other threads.
+ * <p>
+ * To avoid re-entry even within the same thread, this class allows only 1 time access to the shared
+ * statement until it is released.
+ *
+ * @hide
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class SharedSQLiteStatement {
+    private final AtomicBoolean mLock = new AtomicBoolean(false);
+
+    private final RoomDatabase mDatabase;
+    private volatile SupportSQLiteStatement mStmt;
+
+    /**
+     * Creates an SQLite prepared statement that can be re-used across threads. If it is in use,
+     * it automatically creates a new one.
+     *
+     * @param database The database to create the statement in.
+     */
+    public SharedSQLiteStatement(RoomDatabase database) {
+        mDatabase = database;
+    }
+
+    /**
+     * Create the query.
+     *
+     * @return The SQL query to prepare.
+     */
+    protected abstract String createQuery();
+
+    protected void assertNotMainThread() {
+        mDatabase.assertNotMainThread();
+    }
+
+    private SupportSQLiteStatement createNewStatement() {
+        String query = createQuery();
+        return mDatabase.compileStatement(query);
+    }
+
+    private SupportSQLiteStatement getStmt(boolean canUseCached) {
+        final SupportSQLiteStatement stmt;
+        if (canUseCached) {
+            if (mStmt == null) {
+                mStmt = createNewStatement();
+            }
+            stmt = mStmt;
+        } else {
+            // it is in use, create a one off statement
+            stmt = createNewStatement();
+        }
+        return stmt;
+    }
+
+    /**
+     * Call this to get the statement. Must call {@link #release(SupportSQLiteStatement)} once done.
+     */
+    public SupportSQLiteStatement acquire() {
+        assertNotMainThread();
+        return getStmt(mLock.compareAndSet(false, true));
+    }
+
+    /**
+     * Must call this when statement will not be used anymore.
+     *
+     * @param statement The statement that was returned from acquire.
+     */
+    public void release(SupportSQLiteStatement statement) {
+        if (statement == mStmt) {
+            mLock.set(false);
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/migration/Migration.java b/room/runtime/src/main/java/android/arch/persistence/room/migration/Migration.java
new file mode 100644
index 0000000..907e624
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/migration/Migration.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.migration;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+
+/**
+ * Base class for a database migration.
+ * <p>
+ * Each migration can move between 2 versions that are defined by {@link #startVersion} and
+ * {@link #endVersion}.
+ * <p>
+ * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
+ * going version 3 to 5 without going to version 4). If Room opens a database at version
+ * 3 and latest version is &gt;= 5, Room will use the migration object that can migrate from
+ * 3 to 5 instead of 3 to 4 and 4 to 5.
+ * <p>
+ * If there are not enough migrations provided to move from the current version to the latest
+ * version, Room will clear the database and recreate so even if you have no changes between 2
+ * versions, you should still provide a Migration object to the builder.
+ */
+public abstract class Migration {
+    public final int startVersion;
+    public final int endVersion;
+
+    /**
+     * Creates a new migration between {@code startVersion} and {@code endVersion}.
+     *
+     * @param startVersion The start version of the database.
+     * @param endVersion The end version of the database after this migration is applied.
+     */
+    public Migration(int startVersion, int endVersion) {
+        this.startVersion = startVersion;
+        this.endVersion = endVersion;
+    }
+
+    /**
+     * Should run the necessary migrations.
+     * <p>
+     * This class cannot access any generated Dao in this method.
+     * <p>
+     * This method is already called inside a transaction and that transaction might actually be a
+     * composite transaction of all necessary {@code Migration}s.
+     *
+     * @param database The database instance
+     */
+    public abstract void migrate(SupportSQLiteDatabase database);
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/package-info.java b/room/runtime/src/main/java/android/arch/persistence/room/package-info.java
new file mode 100644
index 0000000..faaa952
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/package-info.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+/**
+ * Room is a Database Object Mapping library that makes it easy to access database on Android
+ * applications.
+ * <p>
+ * Rather than hiding the detail of SQLite, Room tries to embrace them by providing convenient APIs
+ * to query the database and also verify such queries at compile time. This allows you to access
+ * the full power of SQLite while having the type safety provided by Java SQL query builders.
+ * <p>
+ * There are 3 major components in Room.
+ * <ul>
+ *     <li>{@link android.arch.persistence.room.Database Database}: This annotation marks a
+ *     class as a database. It should be an abstract class that extends
+ *     {@link android.arch.persistence.room.RoomDatabase RoomDatabase}. At runtime, you can acquire
+ *     an instance of it via {@link android.arch.persistence.room.Room#databaseBuilder(
+ *     android.content.Context,java.lang.Class, java.lang.String) Room.databaseBuilder} or
+ *     {@link android.arch.persistence.room.Room#inMemoryDatabaseBuilder(android.content.Context,
+ *     java.lang.Class) Room.inMemoryDatabaseBuilder}.
+ *     <p>
+ *         This class defines the list of entities and data access objects in the database. It is
+ *         also the main access point for the underlying connection.
+ *     </li>
+ *     <li>{@link android.arch.persistence.room.Entity Entity}: This annotation marks a class as a
+ *     database row. For each {@link android.arch.persistence.room.Entity Entity}, a database table
+ *     is created to hold the items. The Entity class must be referenced in the
+ *     {@link android.arch.persistence.room.Database#entities() Database#entities} array. Each field
+ *     of the Entity is persisted in the database unless it is annotated with
+ *     {@link android.arch.persistence.room.Ignore Ignore}. Entities must have no-arg constructors.
+ *     </li>
+ *     <li>{@link android.arch.persistence.room.Dao Dao}: This annotation marks a class or interface
+ *     as a Data Access Object. Data access objects are the main component of Room that are
+ *     responsible for defining the methods that access the database. The class that is annotated
+ *     with {@link android.arch.persistence.room.Database Database} must have an abstract method
+ *     that has 0 arguments and returns the class that is annotated with Dao. While generating the
+ *     code at compile time, Room will generate an implementation of this class.
+ *     <pre>
+ *     Using Dao classes for database access rather than query builders or direct queries allows you
+ *     to keep a separation between different components and easily mock the database access while
+ *     testing your application.
+ *     </li>
+ * </ul>
+ * Below is a sample of a simple database.
+ * <pre>
+ * // File: User.java
+ * {@literal @}Entity
+ * public class User {
+ *   {@literal @}PrimaryKey
+ *   private int uid;
+ *   private String name;
+ *   {@literal @}ColumnInfo(name = "last_name")
+ *   private String lastName;
+ *   // getters and setters are ignored for brevity but they are required for Room to work.
+ * }
+ * // File: UserDao.java
+ * {@literal @}Dao
+ * public interface UserDao {
+ *   {@literal @}Query("SELECT * FROM user")
+ *   List&lt;User&gt; loadAll();
+ *   {@literal @}Query("SELECT * FROM user WHERE uid IN (:userIds)")
+ *   List&lt;User&gt; loadAllByUserId(int... userIds);
+ *   {@literal @}Query("SELECT * FROM user where name LIKE :first AND last_name LIKE :last LIMIT 1")
+ *   User loadOneByNameAndLastName(String first, String last);
+ *   {@literal @}Insert
+ *   void insertAll(User... users);
+ *   {@literal @}Delete
+ *   void delete(User user);
+ * }
+ * // File: AppDatabase.java
+ * {@literal @}Database(entities = {User.java})
+ * public abstract class AppDatabase extends RoomDatabase {
+ *   public abstract UserDao userDao();
+ * }
+ * </pre>
+ * You can create an instance of {@code AppDatabase} as follows:
+ * <pre>
+ * AppDatabase db = Room
+ *     .databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name")
+ *     .build();
+ * </pre>
+ * Since Room verifies your queries at compile time, it also detects information about which tables
+ * are accessed by the query or what columns are present in the response.
+ * <p>
+ * You can observe a particular table for changes using the
+ * {@link android.arch.persistence.room.InvalidationTracker InvalidationTracker} class which you can
+ * acquire via {@link android.arch.persistence.room.RoomDatabase#getInvalidationTracker()
+ * RoomDatabase.getInvalidationTracker}.
+ * <p>
+ * For convenience, Room allows you to return {@link android.arch.lifecycle.LiveData
+ * LiveData} from {@link android.arch.persistence.room.Query Query} methods. It will automatically
+ * observe the related tables as long as the {@code LiveData} has active observers.
+ * <pre>
+ * // This live data will automatically dispatch changes as the database changes.
+ * {@literal @}Query("SELECT * FROM user ORDER BY name LIMIT 5")
+ * LiveData&lt;User&gt; loadFirstFiveUsers();
+ * </pre>
+ * <p>
+ * You can also return arbitrary Java objects from your query results as long as the fields in the
+ * object match the list of columns in the query response. This makes it very easy to write
+ * applications that drive the UI from persistent storage.
+ * <pre>
+ * class IdAndFullName {
+ *     public int uid;
+ *     {@literal @}ColumnInfo(name = "full_name")
+ *     public String fullName;
+ * }
+ * // DAO
+ * {@literal @}Query("SELECT uid, name || lastName as full_name FROM user")
+ * public IdAndFullName[] loadFullNames();
+ * </pre>
+ * If there is a mismatch between the query result and the POJO, Room will print a warning during
+ * compilation.
+ * <p>
+ * Please see the documentation of individual classes for details.
+ */
+package android.arch.persistence.room;
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java b/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java
new file mode 100644
index 0000000..750f2b7
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.paging;
+
+import android.arch.persistence.room.InvalidationTracker;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.RoomSQLiteQuery;
+import android.arch.util.paging.CountedDataSource;
+import android.database.Cursor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A simple data source implementation that uses Limit & Offset to page the query.
+ * <p>
+ * This is NOT the most efficient way to do paging on SQLite. It is
+ * <a href="http://www.sqlite.org/cvstrac/wiki?p=ScrollingCursor">recommended</a> to use an indexed
+ * ORDER BY statement but that requires a more complex API. This solution is technically equal to
+ * receiving a {@link Cursor} from a large query but avoids the need to manually manage it, and
+ * never returns inconsistent data if it is invalidated.
+ *
+ * @param <T> Data type returned by the data source.
+ *
+ * @hide
+ */
+@SuppressWarnings("unused")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class LimitOffsetDataSource<T> extends CountedDataSource<T> {
+    private final RoomSQLiteQuery mSourceQuery;
+    private final String mCountQuery;
+    private final String mLimitOffsetQuery;
+    private final RoomDatabase mDb;
+    @SuppressWarnings("FieldCanBeLocal")
+    private final InvalidationTracker.Observer mObserver;
+
+    public Runnable invalidCallback;
+
+    protected LimitOffsetDataSource(RoomDatabase db, RoomSQLiteQuery query,
+            String... tables) {
+        mDb = db;
+        mSourceQuery = query;
+        mCountQuery = "SELECT COUNT(*) FROM ( " + mSourceQuery.getSql() + " )";
+        mLimitOffsetQuery = "SELECT * FROM ( " + mSourceQuery.getSql() + " ) LIMIT ? OFFSET ?";
+        mObserver = new InvalidationTracker.Observer(tables) {
+            @Override
+            public void onInvalidated(@NonNull Set<String> tables) {
+                invalidate();
+            }
+        };
+        db.getInvalidationTracker().addWeakObserver(mObserver);
+    }
+
+    @Override
+    public int loadCount() {
+        final RoomSQLiteQuery sqLiteQuery = RoomSQLiteQuery.acquire(mCountQuery,
+                mSourceQuery.getArgCount());
+        sqLiteQuery.copyArgumentsFrom(mSourceQuery);
+        Cursor cursor = mDb.query(sqLiteQuery);
+        try {
+            if (cursor.moveToFirst()) {
+                return cursor.getInt(0);
+            }
+            return 0;
+        } finally {
+            cursor.close();
+            sqLiteQuery.release();
+        }
+    }
+
+    @Override
+    public boolean isInvalid() {
+        mDb.getInvalidationTracker().refreshVersionsSync();
+        return super.isInvalid();
+    }
+
+    private List<T> queryRange(int offset, int limit) {
+        final RoomSQLiteQuery sqLiteQuery = RoomSQLiteQuery.acquire(mLimitOffsetQuery,
+                mSourceQuery.getArgCount() + 2);
+        sqLiteQuery.copyArgumentsFrom(mSourceQuery);
+        sqLiteQuery.bindLong(sqLiteQuery.getArgCount() - 1, limit);
+        sqLiteQuery.bindLong(sqLiteQuery.getArgCount(), offset);
+        Cursor cursor = mDb.query(sqLiteQuery);
+        try {
+            return convertRows(cursor);
+        } finally {
+            cursor.close();
+            sqLiteQuery.release();
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    protected abstract List<T> convertRows(Cursor cursor);
+
+    @Nullable
+    @Override
+    public List<T> loadAfterInitial(int position, int pageSize) {
+        if (isInvalid()) {
+            return null;
+        }
+        List<T> result = queryRange(position + 1, pageSize);
+        if (isInvalid()) {
+            return null;
+        }
+        return result;
+    }
+
+    @Nullable
+    @Override
+    public List<T> loadAfter(int currentEndIndex, @NonNull T currentEndItem, int pageSize) {
+        if (isInvalid()) {
+            return null;
+        }
+        List<T> result = queryRange(currentEndIndex + 1, pageSize);
+        if (isInvalid()) {
+            return null;
+        }
+        return result;
+    }
+
+    @Nullable
+    @Override
+    public List<T> loadBefore(int currentBeginIndex, @NonNull T currentBeginItem, int pageSize) {
+        if (isInvalid()) {
+            return null;
+        }
+        int offset = Math.max(0, currentBeginIndex - pageSize);
+        final List<T> list = queryRange(offset, pageSize);
+        // reverse it
+        Collections.reverse(list);
+
+        if (isInvalid()) {
+            return null;
+        }
+        return list;
+    }
+}
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/util/StringUtil.java b/room/runtime/src/main/java/android/arch/persistence/room/util/StringUtil.java
new file mode 100644
index 0000000..bee05dd
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/util/StringUtil.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.util;
+
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * String utilities for Room
+ */
+@SuppressWarnings("WeakerAccess")
+public class StringUtil {
+    public static final String[] EMPTY_STRING_ARRAY = new String[0];
+    /**
+     * Returns a new StringBuilder to be used while producing SQL queries.
+     *
+     * @return A new or recycled StringBuilder
+     */
+    public static StringBuilder newStringBuilder() {
+        // TODO pool:
+        return new StringBuilder();
+    }
+
+    /**
+     * Adds bind variable placeholders (?) to the given string. Each placeholder is separated
+     * by a comma.
+     *
+     * @param builder The StringBuilder for the query
+     * @param count Number of placeholders
+     */
+    public static void appendPlaceholders(StringBuilder builder, int count) {
+        for (int i = 0; i < count; i++) {
+            builder.append("?");
+            if (i < count - 1) {
+                builder.append(",");
+            }
+        }
+    }
+    /**
+     * Splits a comma separated list of integers to integer list.
+     * <p>
+     * If an input is malformed, it is omitted from the result.
+     *
+     * @param input Comma separated list of integers.
+     * @return A List containing the integers or null if the input is null.
+     */
+    @Nullable
+    public static List<Integer> splitToIntList(@Nullable String input) {
+        if (input == null) {
+            return null;
+        }
+        List<Integer> result = new ArrayList<>();
+        StringTokenizer tokenizer = new StringTokenizer(input, ",");
+        while (tokenizer.hasMoreElements()) {
+            final String item = tokenizer.nextToken();
+            try {
+                result.add(Integer.parseInt(item));
+            } catch (NumberFormatException ex) {
+                Log.e("ROOM", "Malformed integer list", ex);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Joins the given list of integers into a comma separated list.
+     *
+     * @param input The list of integers.
+     * @return Comma separated string composed of integers in the list. If the list is null, return
+     * value is null.
+     */
+    @Nullable
+    public static String joinIntoString(@Nullable List<Integer> input) {
+        if (input == null) {
+            return null;
+        }
+
+        final int size = input.size();
+        if (size == 0) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < size; i++) {
+            sb.append(Integer.toString(input.get(i)));
+            if (i < size - 1) {
+                sb.append(",");
+            }
+        }
+        return sb.toString();
+    }
+}
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
new file mode 100644
index 0000000..bcd2e9e
--- /dev/null
+++ b/room/runtime/src/main/java/android/arch/persistence/room/util/TableInfo.java
@@ -0,0 +1,382 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.util;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.database.Cursor;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A data class that holds the information about a table.
+ * <p>
+ * It directly maps to the result of {@code PRAGMA table_info(<table_name>)}. Check the
+ * <a href="http://www.sqlite.org/pragma.html#pragma_table_info">PRAGMA table_info</a>
+ * documentation for more details.
+ * <p>
+ * Even though SQLite column names are case insensitive, this class uses case sensitive matching.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@SuppressWarnings({"WeakerAccess", "unused", "TryFinallyCanBeTryWithResources"})
+// if you change this class, you must change TableInfoWriter.kt
+public class TableInfo {
+    /**
+     * The table name.
+     */
+    public final String name;
+    /**
+     * Unmodifiable map of columns keyed by column name.
+     */
+    public final Map<String, Column> columns;
+
+    public final Set<ForeignKey> foreignKeys;
+
+    @SuppressWarnings("unused")
+    public TableInfo(String name, Map<String, Column> columns, Set<ForeignKey> foreignKeys) {
+        this.name = name;
+        this.columns = Collections.unmodifiableMap(columns);
+        this.foreignKeys = Collections.unmodifiableSet(foreignKeys);
+    }
+
+    /**
+     * Reads the table information from the given database.
+     *
+     * @param database  The database to read the information from.
+     * @param tableName The table name.
+     * @return A TableInfo containing the schema information for the provided table name.
+     */
+    @SuppressWarnings("SameParameterValue")
+    public static TableInfo read(SupportSQLiteDatabase database, String tableName) {
+        Map<String, Column> columns = readColumns(database, tableName);
+        Set<ForeignKey> foreignKeys = readForeignKeys(database, tableName);
+        return new TableInfo(tableName, columns, foreignKeys);
+    }
+
+    private static Set<ForeignKey> readForeignKeys(SupportSQLiteDatabase database,
+            String tableName) {
+        Set<ForeignKey> foreignKeys = new HashSet<>();
+        // this seems to return everything in order but it is not documented so better be safe
+        Cursor cursor = database.query("PRAGMA foreign_key_list(`" + tableName + "`)");
+        try {
+            final int idColumnIndex = cursor.getColumnIndex("id");
+            final int seqColumnIndex = cursor.getColumnIndex("seq");
+            final int tableColumnIndex = cursor.getColumnIndex("table");
+            final int onDeleteColumnIndex = cursor.getColumnIndex("on_delete");
+            final int onUpdateColumnIndex = cursor.getColumnIndex("on_update");
+
+            final List<ForeignKeyWithSequence> ordered = readForeignKeyFieldMappings(cursor);
+            final int count = cursor.getCount();
+            for (int position = 0; position < count; position++) {
+                cursor.moveToPosition(position);
+                final int seq = cursor.getInt(seqColumnIndex);
+                if (seq != 0) {
+                    continue;
+                }
+                final int id = cursor.getInt(idColumnIndex);
+                List<String> myColumns = new ArrayList<>();
+                List<String> refColumns = new ArrayList<>();
+                for (ForeignKeyWithSequence key : ordered) {
+                    if (key.mId == id) {
+                        myColumns.add(key.mFrom);
+                        refColumns.add(key.mTo);
+                    }
+                }
+                foreignKeys.add(new ForeignKey(
+                        cursor.getString(tableColumnIndex),
+                        cursor.getString(onDeleteColumnIndex),
+                        cursor.getString(onUpdateColumnIndex),
+                        myColumns,
+                        refColumns
+                ));
+            }
+        } finally {
+            cursor.close();
+        }
+        return foreignKeys;
+    }
+
+    private static List<ForeignKeyWithSequence> readForeignKeyFieldMappings(Cursor cursor) {
+        final int idColumnIndex = cursor.getColumnIndex("id");
+        final int seqColumnIndex = cursor.getColumnIndex("seq");
+        final int fromColumnIndex = cursor.getColumnIndex("from");
+        final int toColumnIndex = cursor.getColumnIndex("to");
+        final int count = cursor.getCount();
+        List<ForeignKeyWithSequence> result = new ArrayList<>();
+        for (int i = 0; i < count; i++) {
+            cursor.moveToPosition(i);
+            result.add(new ForeignKeyWithSequence(
+                    cursor.getInt(idColumnIndex),
+                    cursor.getInt(seqColumnIndex),
+                    cursor.getString(fromColumnIndex),
+                    cursor.getString(toColumnIndex)
+            ));
+        }
+        Collections.sort(result);
+        return result;
+    }
+
+    private static Map<String, Column> readColumns(SupportSQLiteDatabase database,
+            String tableName) {
+        Cursor cursor = database
+                .query("PRAGMA table_info(`" + tableName + "`)");
+        //noinspection TryFinallyCanBeTryWithResources
+        Map<String, Column> columns = new HashMap<>();
+        try {
+            if (cursor.getColumnCount() > 0) {
+                int nameIndex = cursor.getColumnIndex("name");
+                int typeIndex = cursor.getColumnIndex("type");
+                int notNullIndex = cursor.getColumnIndex("notnull");
+                int pkIndex = cursor.getColumnIndex("pk");
+
+                while (cursor.moveToNext()) {
+                    final String name = cursor.getString(nameIndex);
+                    final String type = cursor.getString(typeIndex);
+                    final boolean notNull = 0 != cursor.getInt(notNullIndex);
+                    final int primaryKeyPosition = cursor.getInt(pkIndex);
+                    columns.put(name, new Column(name, type, notNull, primaryKeyPosition));
+                }
+            }
+        } finally {
+            cursor.close();
+        }
+        return columns;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TableInfo tableInfo = (TableInfo) o;
+
+        if (!name.equals(tableInfo.name)) return false;
+        //noinspection SimplifiableIfStatement
+        if (!columns.equals(tableInfo.columns)) return false;
+        return foreignKeys.equals(tableInfo.foreignKeys);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + columns.hashCode();
+        result = 31 * result + foreignKeys.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "TableInfo{"
+                + "name='" + name + '\''
+                + ", columns=" + columns
+                + ", foreignKeys=" + foreignKeys
+                + '}';
+    }
+
+    /**
+     * Holds the information about a database column.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static class Column {
+        /**
+         * The column name.
+         */
+        public final String name;
+        /**
+         * The column type affinity.
+         */
+        public final String type;
+        /**
+         * Whether or not the column can be NULL.
+         */
+        public final boolean notNull;
+        /**
+         * The position of the column in the list of primary keys, 0 if the column is not part
+         * of the primary key.
+         * <p>
+         * This information is only available in API 20+.
+         * <a href="https://www.sqlite.org/releaselog/3_7_16_2.html">(SQLite version 3.7.16.2)</a>
+         * On older platforms, it will be 1 if the column is part of the primary key and 0
+         * otherwise.
+         * <p>
+         * The {@link #equals(Object)} implementation handles this inconsistency based on
+         * API levels os if you are using a custom SQLite deployment, it may return false
+         * positives.
+         */
+        public final int primaryKeyPosition;
+
+        // if you change this constructor, you must change TableInfoWriter.kt
+        public Column(String name, String type, boolean notNull, int primaryKeyPosition) {
+            this.name = name;
+            this.type = type;
+            this.notNull = notNull;
+            this.primaryKeyPosition = primaryKeyPosition;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Column column = (Column) o;
+            if (Build.VERSION.SDK_INT >= 20) {
+                if (primaryKeyPosition != column.primaryKeyPosition) return false;
+            } else {
+                if (isPrimaryKey() != column.isPrimaryKey()) return false;
+            }
+
+            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;
+        }
+
+        /**
+         * Returns whether this column is part of the primary key or not.
+         *
+         * @return True if this column is part of the primary key, false otherwise.
+         */
+        public boolean isPrimaryKey() {
+            return primaryKeyPosition > 0;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = name.hashCode();
+            result = 31 * result + (type != null ? type.hashCode() : 0);
+            result = 31 * result + (notNull ? 1231 : 1237);
+            result = 31 * result + primaryKeyPosition;
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "Column{"
+                    + "name='" + name + '\''
+                    + ", type='" + type + '\''
+                    + ", notNull=" + notNull
+                    + ", primaryKeyPosition=" + primaryKeyPosition
+                    + '}';
+        }
+    }
+
+    /**
+     * Holds the information about an SQLite foreign key
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static class ForeignKey {
+        @NonNull
+        public final String referenceTable;
+        @NonNull
+        public final String onDelete;
+        @NonNull
+        public final String onUpdate;
+        @NonNull
+        public final List<String> columnNames;
+        @NonNull
+        public final List<String> referenceColumnNames;
+
+        public ForeignKey(@NonNull String referenceTable, @NonNull String onDelete,
+                @NonNull String onUpdate,
+                @NonNull List<String> columnNames, @NonNull List<String> referenceColumnNames) {
+            this.referenceTable = referenceTable;
+            this.onDelete = onDelete;
+            this.onUpdate = onUpdate;
+            this.columnNames = Collections.unmodifiableList(columnNames);
+            this.referenceColumnNames = Collections.unmodifiableList(referenceColumnNames);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            ForeignKey that = (ForeignKey) o;
+
+            if (!referenceTable.equals(that.referenceTable)) return false;
+            if (!onDelete.equals(that.onDelete)) return false;
+            if (!onUpdate.equals(that.onUpdate)) return false;
+            //noinspection SimplifiableIfStatement
+            if (!columnNames.equals(that.columnNames)) return false;
+            return referenceColumnNames.equals(that.referenceColumnNames);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = referenceTable.hashCode();
+            result = 31 * result + onDelete.hashCode();
+            result = 31 * result + onUpdate.hashCode();
+            result = 31 * result + columnNames.hashCode();
+            result = 31 * result + referenceColumnNames.hashCode();
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "ForeignKey{"
+                    + "referenceTable='" + referenceTable + '\''
+                    + ", onDelete='" + onDelete + '\''
+                    + ", onUpdate='" + onUpdate + '\''
+                    + ", columnNames=" + columnNames
+                    + ", referenceColumnNames=" + referenceColumnNames
+                    + '}';
+        }
+    }
+
+    /**
+     * Temporary data holder for a foreign key row in the pragma result. We need this to ensure
+     * sorting in the generated foreign key object.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    static class ForeignKeyWithSequence implements Comparable<ForeignKeyWithSequence> {
+        final int mId;
+        final int mSequence;
+        final String mFrom;
+        final String mTo;
+
+        ForeignKeyWithSequence(int id, int sequence, String from, String to) {
+            mId = id;
+            mSequence = sequence;
+            mFrom = from;
+            mTo = to;
+        }
+
+        @Override
+        public int compareTo(ForeignKeyWithSequence o) {
+            final int idCmp = mId - o.mId;
+            if (idCmp == 0) {
+                return mSequence - o.mSequence;
+            } else {
+                return idCmp;
+            }
+        }
+    }
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/BuilderTest.java b/room/runtime/src/test/java/android/arch/persistence/room/BuilderTest.java
new file mode 100644
index 0000000..0728cca
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/BuilderTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import static java.util.Arrays.asList;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
+import android.arch.persistence.room.migration.Migration;
+import android.content.Context;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+@RunWith(JUnit4.class)
+public class BuilderTest {
+    @Test(expected = IllegalArgumentException.class)
+    public void nullContext() {
+        //noinspection ConstantConditions
+        Room.databaseBuilder(null, RoomDatabase.class, "bla").build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void nullContext2() {
+        //noinspection ConstantConditions
+        Room.inMemoryDatabaseBuilder(null, RoomDatabase.class).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void nullName() {
+        //noinspection ConstantConditions
+        Room.databaseBuilder(mock(Context.class), RoomDatabase.class, null).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void emptyName() {
+        Room.databaseBuilder(mock(Context.class), RoomDatabase.class, "  ").build();
+    }
+
+    @Test
+    public void migration() {
+        Migration m1 = new EmptyMigration(0, 1);
+        Migration m2 = new EmptyMigration(1, 2);
+        TestDatabase db = Room.databaseBuilder(mock(Context.class), TestDatabase.class, "foo")
+                .addMigrations(m1, m2).build();
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        RoomDatabase.MigrationContainer migrations = config.migrationContainer;
+        assertThat(migrations.findMigrationPath(0, 1), is(asList(m1)));
+        assertThat(migrations.findMigrationPath(1, 2), is(asList(m2)));
+        assertThat(migrations.findMigrationPath(0, 2), is(asList(m1, m2)));
+        assertThat(migrations.findMigrationPath(2, 0), CoreMatchers.<List<Migration>>nullValue());
+        assertThat(migrations.findMigrationPath(0, 3), CoreMatchers.<List<Migration>>nullValue());
+    }
+
+    @Test
+    public void migrationOverride() {
+        Migration m1 = new EmptyMigration(0, 1);
+        Migration m2 = new EmptyMigration(1, 2);
+        Migration m3 = new EmptyMigration(0, 1);
+        TestDatabase db = Room.databaseBuilder(mock(Context.class), TestDatabase.class, "foo")
+                .addMigrations(m1, m2, m3).build();
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        RoomDatabase.MigrationContainer migrations = config.migrationContainer;
+        assertThat(migrations.findMigrationPath(0, 1), is(asList(m3)));
+        assertThat(migrations.findMigrationPath(1, 2), is(asList(m2)));
+        assertThat(migrations.findMigrationPath(0, 3), CoreMatchers.<List<Migration>>nullValue());
+    }
+
+    @Test
+    public void migrationJump() {
+        Migration m1 = new EmptyMigration(0, 1);
+        Migration m2 = new EmptyMigration(1, 2);
+        Migration m3 = new EmptyMigration(2, 3);
+        Migration m4 = new EmptyMigration(0, 3);
+        TestDatabase db = Room.databaseBuilder(mock(Context.class), TestDatabase.class, "foo")
+                .addMigrations(m1, m2, m3, m4).build();
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        RoomDatabase.MigrationContainer migrations = config.migrationContainer;
+        assertThat(migrations.findMigrationPath(0, 3), is(asList(m4)));
+        assertThat(migrations.findMigrationPath(1, 3), is(asList(m2, m3)));
+    }
+
+    @Test
+    public void skipMigration() {
+        Context context = mock(Context.class);
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
+                .fallbackToDestructiveMigration().build();
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        assertThat(config.requireMigration, is(false));
+    }
+
+    @Test
+    public void createBasic() {
+        Context context = mock(Context.class);
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        assertThat(db, instanceOf(BuilderTest_TestDatabase_Impl.class));
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        assertThat(config, notNullValue());
+        assertThat(config.context, is(context));
+        assertThat(config.name, is(nullValue()));
+        assertThat(config.allowMainThreadQueries, is(false));
+        assertThat(config.sqliteOpenHelperFactory,
+                instanceOf(FrameworkSQLiteOpenHelperFactory.class));
+    }
+
+    @Test
+    public void createAllowMainThread() {
+        Context context = mock(Context.class);
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
+                .allowMainThreadQueries()
+                .build();
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        assertThat(config.allowMainThreadQueries, is(true));
+    }
+
+    @Test
+    public void createWithFactoryAndVersion() {
+        Context context = mock(Context.class);
+        SupportSQLiteOpenHelper.Factory factory = mock(SupportSQLiteOpenHelper.Factory.class);
+
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
+                .openHelperFactory(factory)
+                .build();
+        assertThat(db, instanceOf(BuilderTest_TestDatabase_Impl.class));
+        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
+        assertThat(config, notNullValue());
+        assertThat(config.sqliteOpenHelperFactory, is(factory));
+    }
+
+    abstract static class TestDatabase extends RoomDatabase {
+    }
+
+    static class EmptyMigration extends Migration {
+        EmptyMigration(int start, int end) {
+            super(start, end);
+        }
+
+        @Override
+        public void migrate(SupportSQLiteDatabase database) {
+        }
+    }
+
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/BuilderTest_TestDatabase_Impl.java b/room/runtime/src/test/java/android/arch/persistence/room/BuilderTest_TestDatabase_Impl.java
new file mode 100644
index 0000000..d261454
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/BuilderTest_TestDatabase_Impl.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+
+public class BuilderTest_TestDatabase_Impl extends BuilderTest.TestDatabase {
+    DatabaseConfiguration mConfig;
+    @Override
+    public void init(DatabaseConfiguration configuration) {
+        super.init(configuration);
+        mConfig = configuration;
+    }
+
+    @Override
+    protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
+        return null;
+    }
+
+    @Override
+    protected InvalidationTracker createInvalidationTracker() {
+        return null;
+    }
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java b/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java
new file mode 100644
index 0000000..f0b730a
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java
@@ -0,0 +1,342 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsCollectionContaining.hasItem;
+import static org.hamcrest.core.IsCollectionContaining.hasItems;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.core.executor.JunitTaskExecutorRule;
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.SupportSQLiteStatement;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.support.annotation.NonNull;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+@RunWith(JUnit4.class)
+public class InvalidationTrackerTest {
+    private InvalidationTracker mTracker;
+    private RoomDatabase mRoomDatabase;
+    private SupportSQLiteOpenHelper mOpenHelper;
+    @Rule
+    public JunitTaskExecutorRule mTaskExecutorRule = new JunitTaskExecutorRule(1, true);
+
+    @Before
+    public void setup() {
+        mRoomDatabase = mock(RoomDatabase.class);
+        SupportSQLiteDatabase sqliteDb = mock(SupportSQLiteDatabase.class);
+        final SupportSQLiteStatement statement = mock(SupportSQLiteStatement.class);
+        mOpenHelper = mock(SupportSQLiteOpenHelper.class);
+
+        doReturn(statement).when(sqliteDb).compileStatement(eq(InvalidationTracker.CLEANUP_SQL));
+        doReturn(sqliteDb).when(mOpenHelper).getWritableDatabase();
+        doReturn(true).when(mRoomDatabase).isOpen();
+        ReentrantLock closeLock = new ReentrantLock();
+        doReturn(closeLock).when(mRoomDatabase).getCloseLock();
+        //noinspection ResultOfMethodCallIgnored
+        doReturn(mOpenHelper).when(mRoomDatabase).getOpenHelper();
+
+        mTracker = new InvalidationTracker(mRoomDatabase, "a", "B", "i");
+        mTracker.internalInit(sqliteDb);
+    }
+
+    @Before
+    public void setLocale() {
+        Locale.setDefault(Locale.forLanguageTag("tr-TR"));
+    }
+
+    @After
+    public void unsetLocale() {
+        Locale.setDefault(Locale.US);
+    }
+
+    @Test
+    public void tableIds() {
+        assertThat(mTracker.mTableIdLookup.get("a"), is(0));
+        assertThat(mTracker.mTableIdLookup.get("b"), is(1));
+    }
+
+    @Test
+    public void testWeak() throws InterruptedException {
+        final AtomicInteger data = new AtomicInteger(0);
+        InvalidationTracker.Observer observer = new InvalidationTracker.Observer("a") {
+            @Override
+            public void onInvalidated(@NonNull Set<String> tables) {
+                data.incrementAndGet();
+            }
+        };
+        mTracker.addWeakObserver(observer);
+        setVersions(1, 0);
+        refreshSync();
+        assertThat(data.get(), is(1));
+        observer = null;
+        forceGc();
+        setVersions(2, 0);
+        refreshSync();
+        assertThat(data.get(), is(1));
+    }
+
+    @Test
+    public void addRemoveObserver() throws Exception {
+        InvalidationTracker.Observer observer = new LatchObserver(1, "a");
+        mTracker.addObserver(observer);
+        drainTasks();
+        assertThat(mTracker.mObserverMap.size(), is(1));
+        mTracker.removeObserver(new LatchObserver(1, "a"));
+        drainTasks();
+        assertThat(mTracker.mObserverMap.size(), is(1));
+        mTracker.removeObserver(observer);
+        drainTasks();
+        assertThat(mTracker.mObserverMap.size(), is(0));
+    }
+
+    private void drainTasks() throws InterruptedException {
+        mTaskExecutorRule.drainTasks(200);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void badObserver() {
+        InvalidationTracker.Observer observer = new LatchObserver(1, "x");
+        mTracker.addObserver(observer);
+    }
+
+    @Test
+    public void refreshReadValues() throws Exception {
+        setVersions(1, 0, 2, 1);
+        refreshSync();
+        assertThat(mTracker.mTableVersions, is(new long[]{1, 2, 0}));
+
+        setVersions(3, 1);
+        refreshSync();
+        assertThat(mTracker.mTableVersions, is(new long[]{1, 3, 0}));
+
+        setVersions(7, 0);
+        refreshSync();
+        assertThat(mTracker.mTableVersions, is(new long[]{7, 3, 0}));
+
+        refreshSync();
+        assertThat(mTracker.mTableVersions, is(new long[]{7, 3, 0}));
+    }
+
+    private void refreshSync() throws InterruptedException {
+        mTracker.refreshVersionsAsync();
+        drainTasks();
+    }
+
+    @Test
+    public void refreshCheckTasks() throws Exception {
+        when(mRoomDatabase.query(anyString(), any(Object[].class)))
+                .thenReturn(mock(Cursor.class));
+        mTracker.refreshVersionsAsync();
+        mTracker.refreshVersionsAsync();
+        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
+        drainTasks();
+
+        reset(mTaskExecutorRule.getTaskExecutor());
+        mTracker.refreshVersionsAsync();
+        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
+    }
+
+    @Test
+    public void observe1Table() throws Exception {
+        LatchObserver observer = new LatchObserver(1, "a");
+        mTracker.addObserver(observer);
+        setVersions(1, 0, 2, 1);
+        refreshSync();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables().size(), is(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("a"));
+
+        setVersions(3, 1);
+        observer.reset(1);
+        refreshSync();
+        assertThat(observer.await(), is(false));
+
+        setVersions(4, 0);
+        refreshSync();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables().size(), is(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("a"));
+    }
+
+    @Test
+    public void observe2Tables() throws Exception {
+        LatchObserver observer = new LatchObserver(1, "A", "B");
+        mTracker.addObserver(observer);
+        setVersions(1, 0, 2, 1);
+        refreshSync();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables().size(), is(2));
+        assertThat(observer.getInvalidatedTables(), hasItems("A", "B"));
+
+        setVersions(3, 1);
+        observer.reset(1);
+        refreshSync();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables().size(), is(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("B"));
+
+        setVersions(4, 0);
+        observer.reset(1);
+        refreshSync();
+        assertThat(observer.await(), is(true));
+        assertThat(observer.getInvalidatedTables().size(), is(1));
+        assertThat(observer.getInvalidatedTables(), hasItem("A"));
+
+        observer.reset(1);
+        refreshSync();
+        assertThat(observer.await(), is(false));
+    }
+
+    @Test
+    public void locale() {
+        LatchObserver observer = new LatchObserver(1, "I");
+        mTracker.addObserver(observer);
+    }
+
+    @Test
+    public void closedDb() {
+        doThrow(new IllegalStateException("foo")).when(mOpenHelper).getWritableDatabase();
+        mTracker.addObserver(new LatchObserver(1, "a", "b"));
+        mTracker.syncTriggers();
+        mTracker.mRefreshRunnable.run();
+    }
+
+    @Test
+    public void closedDbAfterOpen() throws InterruptedException {
+        setVersions(3, 1);
+        mTracker.addObserver(new LatchObserver(1, "a", "b"));
+        mTracker.syncTriggers();
+        mTracker.mRefreshRunnable.run();
+        doThrow(new SQLiteException("foo")).when(mRoomDatabase).query(
+                Mockito.eq(InvalidationTracker.SELECT_UPDATED_TABLES_SQL),
+                any(Object[].class));
+        mTracker.mPendingRefresh.set(true);
+        mTracker.mRefreshRunnable.run();
+    }
+
+    /**
+     * Key value pairs of VERSION, TABLE_ID
+     */
+    private void setVersions(int... keyValuePairs) throws InterruptedException {
+        // mockito does not like multi-threaded access so before setting versions, make sure we
+        // sync background tasks.
+        drainTasks();
+        Cursor cursor = createCursorWithValues(keyValuePairs);
+        doReturn(cursor).when(mRoomDatabase).query(
+                Mockito.eq(InvalidationTracker.SELECT_UPDATED_TABLES_SQL),
+                any(Object[].class)
+        );
+    }
+
+    private Cursor createCursorWithValues(final int... keyValuePairs) {
+        Cursor cursor = mock(Cursor.class);
+        final AtomicInteger index = new AtomicInteger(-2);
+        when(cursor.moveToNext()).thenAnswer(new Answer<Boolean>() {
+            @Override
+            public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                return index.addAndGet(2) < keyValuePairs.length;
+            }
+        });
+        Answer<Integer> intAnswer = new Answer<Integer>() {
+            @Override
+            public Integer answer(InvocationOnMock invocation) throws Throwable {
+                return keyValuePairs[index.intValue() + (Integer) invocation.getArguments()[0]];
+            }
+        };
+        Answer<Long> longAnswer = new Answer<Long>() {
+            @Override
+            public Long answer(InvocationOnMock invocation) throws Throwable {
+                return (long) keyValuePairs[index.intValue()
+                        + (Integer) invocation.getArguments()[0]];
+            }
+        };
+        when(cursor.getInt(anyInt())).thenAnswer(intAnswer);
+        when(cursor.getLong(anyInt())).thenAnswer(longAnswer);
+        return cursor;
+    }
+
+    static class LatchObserver extends InvalidationTracker.Observer {
+        private CountDownLatch mLatch;
+        private Set<String> mInvalidatedTables;
+
+        LatchObserver(int count, String... tableNames) {
+            super(tableNames);
+            mLatch = new CountDownLatch(count);
+        }
+
+        boolean await() throws InterruptedException {
+            return mLatch.await(3, TimeUnit.SECONDS);
+        }
+
+        @Override
+        public void onInvalidated(@NonNull Set<String> tables) {
+            mInvalidatedTables = tables;
+            mLatch.countDown();
+        }
+
+        void reset(@SuppressWarnings("SameParameterValue") int count) {
+            mInvalidatedTables = null;
+            mLatch = new CountDownLatch(count);
+        }
+
+        Set<String> getInvalidatedTables() {
+            return mInvalidatedTables;
+        }
+    }
+
+    private static void forceGc() {
+        // Use a random index in the list to detect the garbage collection each time because
+        // .get() may accidentally trigger a strong reference during collection.
+        ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
+        do {
+            WeakReference<byte[]> arr = new WeakReference<>(new byte[100]);
+            leak.add(arr);
+        } while (leak.get((int) (Math.random() * leak.size())).get() != null);
+    }
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/ObservedTableTrackerTest.java b/room/runtime/src/test/java/android/arch/persistence/room/ObservedTableTrackerTest.java
new file mode 100644
index 0000000..ffddee9
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/ObservedTableTrackerTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+
+package android.arch.persistence.room;
+
+
+import static android.arch.persistence.room.InvalidationTracker.ObservedTableTracker.ADD;
+import static android.arch.persistence.room.InvalidationTracker.ObservedTableTracker.NO_OP;
+import static android.arch.persistence.room.InvalidationTracker.ObservedTableTracker.REMOVE;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+@RunWith(JUnit4.class)
+public class ObservedTableTrackerTest {
+    private static final int TABLE_COUNT = 5;
+    private InvalidationTracker.ObservedTableTracker mTracker;
+
+    @Before
+    public void setup() {
+        mTracker = new InvalidationTracker.ObservedTableTracker(TABLE_COUNT);
+    }
+
+    @Test
+    public void basicAdd() {
+        mTracker.onAdded(2, 3);
+        assertThat(mTracker.getTablesToSync(), is(createResponse(2, ADD, 3, ADD)));
+    }
+
+    @Test
+    public void basicRemove() {
+        initState(2, 3);
+        mTracker.onRemoved(3);
+        assertThat(mTracker.getTablesToSync(), is(createResponse(3, REMOVE)));
+    }
+
+    @Test
+    public void noChange() {
+        initState(1, 3);
+        mTracker.onAdded(3);
+        assertThat(mTracker.getTablesToSync(), is(nullValue()));
+    }
+
+    @Test
+    public void returnNullUntilSync() {
+        initState(1, 3);
+        mTracker.onAdded(4);
+        assertThat(mTracker.getTablesToSync(), is(createResponse(4, ADD)));
+        mTracker.onAdded(0);
+        assertThat(mTracker.getTablesToSync(), is(nullValue()));
+        mTracker.onSyncCompleted();
+        assertThat(mTracker.getTablesToSync(), is(createResponse(0, ADD)));
+    }
+
+    @Test
+    public void multipleAdditionsDeletions() {
+        initState(2, 4);
+        mTracker.onAdded(2);
+        assertThat(mTracker.getTablesToSync(), is(nullValue()));
+        mTracker.onAdded(2, 4);
+        assertThat(mTracker.getTablesToSync(), is(nullValue()));
+        mTracker.onRemoved(2);
+        assertThat(mTracker.getTablesToSync(), is(nullValue()));
+        mTracker.onRemoved(2, 4);
+        assertThat(mTracker.getTablesToSync(), is(nullValue()));
+        mTracker.onAdded(1, 3);
+        mTracker.onRemoved(2, 4);
+        assertThat(mTracker.getTablesToSync(), is(
+                createResponse(1, ADD, 2, REMOVE, 3, ADD, 4, REMOVE)));
+    }
+
+    private void initState(int... tableIds) {
+        mTracker.onAdded(tableIds);
+        mTracker.getTablesToSync();
+        mTracker.onSyncCompleted();
+    }
+
+    private static int[] createResponse(int... tuples) {
+        int[] result = new int[TABLE_COUNT];
+        Arrays.fill(result, NO_OP);
+        for (int i = 0; i < tuples.length; i += 2) {
+            result[tuples[i]] = tuples[i + 1];
+        }
+        return result;
+    }
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/RoomSQLiteQueryTest.java b/room/runtime/src/test/java/android/arch/persistence/room/RoomSQLiteQueryTest.java
new file mode 100644
index 0000000..e7a8644
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/RoomSQLiteQueryTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.arch.persistence.db.SupportSQLiteProgram;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RoomSQLiteQueryTest {
+    @Before
+    public void clear() {
+        RoomSQLiteQuery.sQueryPool.clear();
+    }
+
+    @Test
+    public void acquireBasic() {
+        RoomSQLiteQuery query = RoomSQLiteQuery.acquire("abc", 3);
+        assertThat(query.getSql(), is("abc"));
+        assertThat(query.mArgCount, is(3));
+        assertThat(query.mBlobBindings.length, is(4));
+        assertThat(query.mLongBindings.length, is(4));
+        assertThat(query.mStringBindings.length, is(4));
+        assertThat(query.mDoubleBindings.length, is(4));
+    }
+
+    @Test
+    public void acquireSameSizeAgain() {
+        RoomSQLiteQuery query = RoomSQLiteQuery.acquire("abc", 3);
+        query.release();
+        assertThat(RoomSQLiteQuery.acquire("blah", 3), sameInstance(query));
+    }
+
+    @Test
+    public void acquireSameSizeWithoutRelease() {
+        RoomSQLiteQuery query = RoomSQLiteQuery.acquire("abc", 3);
+        assertThat(RoomSQLiteQuery.acquire("fda", 3), not(sameInstance(query)));
+    }
+
+    @Test
+    public void bindings() {
+        RoomSQLiteQuery query = RoomSQLiteQuery.acquire("abc", 6);
+        byte[] myBlob = new byte[3];
+        long myLong = 3L;
+        double myDouble = 7.0;
+        String myString = "ss";
+        query.bindBlob(1, myBlob);
+        query.bindLong(2, myLong);
+        query.bindNull(3);
+        query.bindDouble(4, myDouble);
+        query.bindString(5, myString);
+        query.bindNull(6);
+        SupportSQLiteProgram program = mock(SupportSQLiteProgram.class);
+        query.bindTo(program);
+
+        verify(program).bindBlob(1, myBlob);
+        verify(program).bindLong(2, myLong);
+        verify(program).bindNull(3);
+        verify(program).bindDouble(4, myDouble);
+        verify(program).bindString(5, myString);
+        verify(program).bindNull(6);
+    }
+
+    @Test
+    public void dontKeepSameSizeTwice() {
+        RoomSQLiteQuery query1 = RoomSQLiteQuery.acquire("abc", 3);
+        RoomSQLiteQuery query2 = RoomSQLiteQuery.acquire("zx", 3);
+        RoomSQLiteQuery query3 = RoomSQLiteQuery.acquire("qw", 0);
+
+        query1.release();
+        query2.release();
+        assertThat(RoomSQLiteQuery.sQueryPool.size(), is(1));
+
+        query3.release();
+        assertThat(RoomSQLiteQuery.sQueryPool.size(), is(2));
+    }
+
+    @Test
+    public void returnExistingForSmallerSize() {
+        RoomSQLiteQuery query = RoomSQLiteQuery.acquire("abc", 3);
+        query.release();
+        assertThat(RoomSQLiteQuery.acquire("dsa", 2), sameInstance(query));
+    }
+
+    @Test
+    public void returnNewForBigger() {
+        RoomSQLiteQuery query = RoomSQLiteQuery.acquire("abc", 3);
+        query.release();
+        assertThat(RoomSQLiteQuery.acquire("dsa", 4), not(sameInstance(query)));
+    }
+
+    @Test
+    public void pruneCache() {
+        for (int i = 0; i < RoomSQLiteQuery.POOL_LIMIT; i++) {
+            RoomSQLiteQuery.acquire("dsdsa", i).release();
+        }
+        pruneCacheTest();
+    }
+
+    @Test
+    public void pruneCacheReverseInsertion() {
+        List<RoomSQLiteQuery> queries = new ArrayList<>();
+        for (int i = RoomSQLiteQuery.POOL_LIMIT - 1; i >= 0; i--) {
+            queries.add(RoomSQLiteQuery.acquire("dsdsa", i));
+        }
+        for (RoomSQLiteQuery query : queries) {
+            query.release();
+        }
+        pruneCacheTest();
+    }
+
+    private void pruneCacheTest() {
+        assertThat(RoomSQLiteQuery.sQueryPool.size(), is(RoomSQLiteQuery.POOL_LIMIT));
+        RoomSQLiteQuery.acquire("dsadsa", RoomSQLiteQuery.POOL_LIMIT + 1).release();
+        assertThat(RoomSQLiteQuery.sQueryPool.size(), is(RoomSQLiteQuery.DESIRED_POOL_SIZE));
+        Iterator<RoomSQLiteQuery> itr = RoomSQLiteQuery.sQueryPool.values().iterator();
+        for (int i = 0; i < RoomSQLiteQuery.DESIRED_POOL_SIZE; i++) {
+            assertThat(itr.next().mCapacity, is(i));
+        }
+    }
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/SharedSQLiteStatementTest.java b/room/runtime/src/test/java/android/arch/persistence/room/SharedSQLiteStatementTest.java
new file mode 100644
index 0000000..4e715e9
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/SharedSQLiteStatementTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+package android.arch.persistence.room;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.persistence.db.SupportSQLiteStatement;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+@RunWith(JUnit4.class)
+public class SharedSQLiteStatementTest {
+    private SharedSQLiteStatement mSharedStmt;
+    RoomDatabase mDb;
+    @Before
+    public void init() {
+        mDb = mock(RoomDatabase.class);
+        when(mDb.compileStatement(anyString())).thenAnswer(new Answer<SupportSQLiteStatement>() {
+
+            @Override
+            public SupportSQLiteStatement answer(InvocationOnMock invocation) throws Throwable {
+                return mock(SupportSQLiteStatement.class);
+            }
+        });
+        when(mDb.getInvalidationTracker()).thenReturn(mock(InvalidationTracker.class));
+        mSharedStmt = new SharedSQLiteStatement(mDb) {
+            @Override
+            protected String createQuery() {
+                return "foo";
+            }
+        };
+    }
+
+    @Test
+    public void checkMainThread() {
+        mSharedStmt.acquire();
+        verify(mDb).assertNotMainThread();
+    }
+
+    @Test
+    public void basic() {
+        assertThat(mSharedStmt.acquire(), notNullValue());
+    }
+
+    @Test
+    public void getTwiceWithoutReleasing() {
+        SupportSQLiteStatement stmt1 = mSharedStmt.acquire();
+        SupportSQLiteStatement stmt2 = mSharedStmt.acquire();
+        assertThat(stmt1, notNullValue());
+        assertThat(stmt2, notNullValue());
+        assertThat(stmt1, is(not(stmt2)));
+    }
+
+    @Test
+    public void getTwiceWithReleasing() {
+        SupportSQLiteStatement stmt1 = mSharedStmt.acquire();
+        mSharedStmt.release(stmt1);
+        SupportSQLiteStatement stmt2 = mSharedStmt.acquire();
+        assertThat(stmt1, notNullValue());
+        assertThat(stmt1, is(stmt2));
+    }
+
+    @Test
+    public void getFromAnotherThreadWhileHolding() throws ExecutionException, InterruptedException {
+        SupportSQLiteStatement stmt1 = mSharedStmt.acquire();
+        FutureTask<SupportSQLiteStatement> task = new FutureTask<>(
+                new Callable<SupportSQLiteStatement>() {
+                    @Override
+                    public SupportSQLiteStatement call() throws Exception {
+                        return mSharedStmt.acquire();
+                    }
+                });
+        new Thread(task).run();
+        SupportSQLiteStatement stmt2 = task.get();
+        assertThat(stmt1, notNullValue());
+        assertThat(stmt2, notNullValue());
+        assertThat(stmt1, is(not(stmt2)));
+    }
+
+    @Test
+    public void getFromAnotherThreadAfterReleasing() throws ExecutionException,
+            InterruptedException {
+        SupportSQLiteStatement stmt1 = mSharedStmt.acquire();
+        mSharedStmt.release(stmt1);
+        FutureTask<SupportSQLiteStatement> task = new FutureTask<>(
+                new Callable<SupportSQLiteStatement>() {
+                    @Override
+                    public SupportSQLiteStatement call() throws Exception {
+                        return mSharedStmt.acquire();
+                    }
+                });
+        new Thread(task).run();
+        SupportSQLiteStatement stmt2 = task.get();
+        assertThat(stmt1, notNullValue());
+        assertThat(stmt1, is(stmt2));
+    }
+}
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/util/StringUtilTest.java b/room/runtime/src/test/java/android/arch/persistence/room/util/StringUtilTest.java
new file mode 100644
index 0000000..c10fab6
--- /dev/null
+++ b/room/runtime/src/test/java/android/arch/persistence/room/util/StringUtilTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.util;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static java.util.Arrays.asList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collections;
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+@RunWith(JUnit4.class)
+public class StringUtilTest {
+    @Test
+    public void testEmpty() {
+        assertThat(StringUtil.splitToIntList(""), is(Collections.<Integer>emptyList()));
+        assertThat(StringUtil.joinIntoString(Collections.<Integer>emptyList()), is(""));
+    }
+
+    @Test
+    public void testNull() {
+        assertThat(StringUtil.splitToIntList(null), nullValue());
+        assertThat(StringUtil.joinIntoString(null), nullValue());
+    }
+
+    @Test
+    public void testSingle() {
+        assertThat(StringUtil.splitToIntList("4"), is(asList(4)));
+        assertThat(StringUtil.joinIntoString(asList(4)), is("4"));
+    }
+
+    @Test
+    public void testMultiple() {
+        assertThat(StringUtil.splitToIntList("4,5"), is(asList(4, 5)));
+        assertThat(StringUtil.joinIntoString(asList(4, 5)), is("4,5"));
+    }
+
+    @Test
+    public void testNegative() {
+        assertThat(StringUtil.splitToIntList("-4,-5,6,-7"), is(asList(-4, -5, 6, -7)));
+        assertThat(StringUtil.joinIntoString(asList(-4, -5, 6, -7)), is("-4,-5,6,-7"));
+    }
+
+    @Test
+    public void ignoreMalformed() {
+        assertThat(StringUtil.splitToIntList("-4,a,5,7"), is(asList(-4, 5, 7)));
+    }
+}
diff --git a/room/rxjava2/build.gradle b/room/rxjava2/build.gradle
new file mode 100644
index 0000000..d7b5ceb
--- /dev/null
+++ b/room/rxjava2/build.gradle
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile project(":room:common")
+    compile project(":room:runtime")
+    compile project(":arch:runtime")
+    compile libs.support.core_utils
+    compile libs.rx_java
+    testCompile libs.junit
+    testCompile libs.mockito_core
+    testCompile project(":arch:core-testing")
+}
+
+archivesBaseName = "rxjava2"
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/room/rxjava2/src/main/AndroidManifest.xml b/room/rxjava2/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..33279c6
--- /dev/null
+++ b/room/rxjava2/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.persistence.room.rxjava2">
+</manifest>
diff --git a/room/rxjava2/src/main/java/android/arch/persistence/room/EmptyResultSetException.java b/room/rxjava2/src/main/java/android/arch/persistence/room/EmptyResultSetException.java
new file mode 100644
index 0000000..0f2d281
--- /dev/null
+++ b/room/rxjava2/src/main/java/android/arch/persistence/room/EmptyResultSetException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+/**
+ * Thrown by Room when the query needs to return a result (e.g. in a Single&lt;T> query) but the
+ * returned result set from the database is empty.
+ */
+public class EmptyResultSetException extends RuntimeException {
+    /**
+     * Constructs a new EmptyResultSetException with the exception.
+     * @param message The SQL query which didn't return any results.
+     */
+    public EmptyResultSetException(String message) {
+        super(message);
+    }
+}
diff --git a/room/rxjava2/src/main/java/android/arch/persistence/room/RxRoom.java b/room/rxjava2/src/main/java/android/arch/persistence/room/RxRoom.java
new file mode 100644
index 0000000..adfca27
--- /dev/null
+++ b/room/rxjava2/src/main/java/android/arch/persistence/room/RxRoom.java
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import io.reactivex.BackpressureStrategy;
+import io.reactivex.Flowable;
+import io.reactivex.FlowableEmitter;
+import io.reactivex.FlowableOnSubscribe;
+import io.reactivex.Scheduler;
+import io.reactivex.annotations.NonNull;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.disposables.Disposables;
+import io.reactivex.functions.Action;
+import io.reactivex.functions.Function;
+import io.reactivex.functions.Predicate;
+
+/**
+ * Helper class to add RxJava2 support to Room.
+ */
+@SuppressWarnings("WeakerAccess")
+public class RxRoom {
+    /**
+     * Data dispatched by the publisher created by {@link #createFlowable(RoomDatabase, String...)}.
+     */
+    public static final Object NOTHING = new Object();
+
+    /**
+     * Creates a {@link Flowable} that emits at least once and also re-emits whenever one of the
+     * observed tables is updated.
+     * <p>
+     * You can easily chain a database operation to downstream of this {@link Flowable} to ensure
+     * that it re-runs when database is modified.
+     * <p>
+     * Since database invalidation is batched, multiple changes in the database may results in just
+     * 1 emission.
+     *
+     * @param database   The database instance
+     * @param tableNames The list of table names that should be observed
+     * @return A {@link Flowable} which emits {@link #NOTHING} when one of the observed tables
+     * is modified (also once when the invalidation tracker connection is established).
+     */
+    public static Flowable<Object> createFlowable(final RoomDatabase database,
+            final String... tableNames) {
+        return Flowable.create(new FlowableOnSubscribe<Object>() {
+            @Override
+            public void subscribe(final FlowableEmitter<Object> emitter) throws Exception {
+                final InvalidationTracker.Observer observer = new InvalidationTracker.Observer(
+                        tableNames) {
+                    @Override
+                    public void onInvalidated(
+                            @android.support.annotation.NonNull Set<String> tables) {
+                        if (!emitter.isCancelled()) {
+                            emitter.onNext(NOTHING);
+                        }
+                    }
+                };
+                if (!emitter.isCancelled()) {
+                    database.getInvalidationTracker().addObserver(observer);
+                    emitter.setDisposable(Disposables.fromAction(new Action() {
+                        @Override
+                        public void run() throws Exception {
+                            database.getInvalidationTracker().removeObserver(observer);
+                        }
+                    }));
+                }
+
+                // emit once to avoid missing any data and also easy chaining
+                if (!emitter.isCancelled()) {
+                    emitter.onNext(NOTHING);
+                }
+            }
+        }, BackpressureStrategy.LATEST);
+    }
+
+    /**
+     * Helper method used by generated code to bind a Callable such that it will be run in
+     * our disk io thread and will automatically block null values since RxJava2 does not like null.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static <T> Flowable<T> createFlowable(final RoomDatabase database,
+            final String[] tableNames, final Callable<T> callable) {
+        return createFlowable(database, tableNames).observeOn(sAppToolkitIOScheduler)
+                .map(new Function<Object, Optional<T>>() {
+                    @Override
+                    public Optional<T> apply(@NonNull Object o) throws Exception {
+                        T data = callable.call();
+                        return new Optional<>(data);
+                    }
+                }).filter(new Predicate<Optional<T>>() {
+                    @Override
+                    public boolean test(@NonNull Optional<T> optional) throws Exception {
+                        return optional.mValue != null;
+                    }
+                }).map(new Function<Optional<T>, T>() {
+                    @Override
+                    public T apply(@NonNull Optional<T> optional) throws Exception {
+                        return optional.mValue;
+                    }
+                });
+    }
+
+    private static Scheduler sAppToolkitIOScheduler = new Scheduler() {
+        @Override
+        public Worker createWorker() {
+            final AtomicBoolean mDisposed = new AtomicBoolean(false);
+            return new Worker() {
+                @Override
+                public Disposable schedule(@NonNull Runnable run, long delay,
+                        @NonNull TimeUnit unit) {
+                    DisposableRunnable disposable = new DisposableRunnable(run, mDisposed);
+                    AppToolkitTaskExecutor.getInstance().executeOnDiskIO(run);
+                    return disposable;
+                }
+
+                @Override
+                public void dispose() {
+                    mDisposed.set(true);
+                }
+
+                @Override
+                public boolean isDisposed() {
+                    return mDisposed.get();
+                }
+            };
+        }
+    };
+
+    private static class DisposableRunnable implements Disposable, Runnable {
+        private final Runnable mActual;
+        private volatile boolean mDisposed = false;
+        private final AtomicBoolean mGlobalDisposed;
+
+        DisposableRunnable(Runnable actual, AtomicBoolean globalDisposed) {
+            mActual = actual;
+            mGlobalDisposed = globalDisposed;
+        }
+
+        @Override
+        public void dispose() {
+            mDisposed = true;
+        }
+
+        @Override
+        public boolean isDisposed() {
+            return mDisposed || mGlobalDisposed.get();
+        }
+
+        @Override
+        public void run() {
+            if (!isDisposed()) {
+                mActual.run();
+            }
+        }
+    }
+
+    static class Optional<T> {
+        @Nullable
+        final T mValue;
+
+        Optional(@Nullable T value) {
+            this.mValue = value;
+        }
+    }
+}
diff --git a/room/rxjava2/src/test/java/android/arch/persistence/room/RxRoomTest.java b/room/rxjava2/src/test/java/android/arch/persistence/room/RxRoomTest.java
new file mode 100644
index 0000000..502eaa1
--- /dev/null
+++ b/room/rxjava2/src/test/java/android/arch/persistence/room/RxRoomTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.core.executor.JunitTaskExecutorRule;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.reactivex.Flowable;
+import io.reactivex.annotations.NonNull;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Consumer;
+import io.reactivex.subscribers.TestSubscriber;
+
+@RunWith(JUnit4.class)
+public class RxRoomTest {
+    @Rule
+    public JunitTaskExecutorRule mExecutor = new JunitTaskExecutorRule(1, false);
+    private RoomDatabase mDatabase;
+    private InvalidationTracker mInvalidationTracker;
+    private List<InvalidationTracker.Observer> mAddedObservers = new ArrayList<>();
+
+    @Before
+    public void init() {
+        mDatabase = mock(RoomDatabase.class);
+        mInvalidationTracker = mock(InvalidationTracker.class);
+        when(mDatabase.getInvalidationTracker()).thenReturn(mInvalidationTracker);
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                mAddedObservers.add((InvalidationTracker.Observer) invocation.getArguments()[0]);
+                return null;
+            }
+        }).when(mInvalidationTracker).addObserver(any(InvalidationTracker.Observer.class));
+    }
+
+    @Test
+    public void basicAddRemove() {
+        Flowable<Object> flowable = RxRoom.createFlowable(mDatabase, "a", "b");
+        verify(mInvalidationTracker, never()).addObserver(any(InvalidationTracker.Observer.class));
+        Disposable disposable = flowable.subscribe();
+        verify(mInvalidationTracker).addObserver(any(InvalidationTracker.Observer.class));
+        assertThat(mAddedObservers.size(), CoreMatchers.is(1));
+
+        InvalidationTracker.Observer observer = mAddedObservers.get(0);
+        disposable.dispose();
+
+        verify(mInvalidationTracker).removeObserver(observer);
+
+        disposable = flowable.subscribe();
+        verify(mInvalidationTracker, times(2))
+                .addObserver(any(InvalidationTracker.Observer.class));
+        assertThat(mAddedObservers.size(), CoreMatchers.is(2));
+        assertThat(mAddedObservers.get(1), CoreMatchers.not(CoreMatchers.sameInstance(observer)));
+        InvalidationTracker.Observer observer2 = mAddedObservers.get(1);
+        disposable.dispose();
+        verify(mInvalidationTracker).removeObserver(observer2);
+    }
+
+    @Test
+    public void basicNotify() throws InterruptedException {
+        String[] tables = {"a", "b"};
+        Set<String> tableSet = new HashSet<>(Arrays.asList(tables));
+        Flowable<Object> flowable = RxRoom.createFlowable(mDatabase, tables);
+        CountingConsumer consumer = new CountingConsumer();
+        Disposable disposable = flowable.subscribe(consumer);
+        assertThat(mAddedObservers.size(), CoreMatchers.is(1));
+        InvalidationTracker.Observer observer = mAddedObservers.get(0);
+        assertThat(consumer.mCount, CoreMatchers.is(1));
+        observer.onInvalidated(tableSet);
+        assertThat(consumer.mCount, CoreMatchers.is(2));
+        observer.onInvalidated(tableSet);
+        assertThat(consumer.mCount, CoreMatchers.is(3));
+        disposable.dispose();
+        observer.onInvalidated(tableSet);
+        assertThat(consumer.mCount, CoreMatchers.is(3));
+    }
+
+    @Test
+    public void internalCallable() throws InterruptedException {
+        final AtomicReference<String> value = new AtomicReference<>(null);
+        String[] tables = {"a", "b"};
+        Set<String> tableSet = new HashSet<>(Arrays.asList(tables));
+        final Flowable<String> flowable = RxRoom.createFlowable(mDatabase, tables,
+                new Callable<String>() {
+                    @Override
+                    public String call() throws Exception {
+                        return value.get();
+                    }
+                });
+        final CountingConsumer consumer = new CountingConsumer();
+        flowable.subscribe(consumer);
+        InvalidationTracker.Observer observer = mAddedObservers.get(0);
+        drain();
+        // no value because it is null
+        assertThat(consumer.mCount, CoreMatchers.is(0));
+        value.set("bla");
+        observer.onInvalidated(tableSet);
+        drain();
+        // get value
+        assertThat(consumer.mCount, CoreMatchers.is(1));
+        observer.onInvalidated(tableSet);
+        drain();
+        // get value
+        assertThat(consumer.mCount, CoreMatchers.is(2));
+        value.set(null);
+        observer.onInvalidated(tableSet);
+        drain();
+        // no value
+        assertThat(consumer.mCount, CoreMatchers.is(2));
+    }
+
+    private void drain() throws InterruptedException {
+        mExecutor.drainTasks(2);
+    }
+
+    @Test
+    public void exception() throws InterruptedException {
+        final Flowable<String> flowable = RxRoom.createFlowable(mDatabase, new String[]{"a"},
+                new Callable<String>() {
+                    @Override
+                    public String call() throws Exception {
+                        throw new Exception("i want exception");
+                    }
+                });
+        TestSubscriber<String> subscriber = new TestSubscriber<>();
+        flowable.subscribe(subscriber);
+        drain();
+        assertThat(subscriber.errorCount(), CoreMatchers.is(1));
+        assertThat(subscriber.errors().get(0).getMessage(), CoreMatchers.is("i want exception"));
+    }
+
+    private static class CountingConsumer implements Consumer<Object> {
+        int mCount = 0;
+
+        @Override
+        public void accept(@NonNull Object o) throws Exception {
+            mCount++;
+        }
+    }
+}
diff --git a/room/testing/build.gradle b/room/testing/build.gradle
new file mode 100644
index 0000000..d378d84
--- /dev/null
+++ b/room/testing/build.gradle
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile project(":room:common")
+    compile project(":room:runtime")
+    compile project(":room:db")
+    compile project(":room:db-impl")
+    compile project(":room:migration")
+    compile project(":arch:runtime")
+    compile libs.support.core_utils
+    compile libs.junit
+}
+
+archivesBaseName = "testing"
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
diff --git a/room/testing/src/main/AndroidManifest.xml b/room/testing/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..594f016
--- /dev/null
+++ b/room/testing/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.persistence.room.testing">
+</manifest>
diff --git a/room/testing/src/main/java/android/arch/persistence/room/testing/MigrationTestHelper.java b/room/testing/src/main/java/android/arch/persistence/room/testing/MigrationTestHelper.java
new file mode 100644
index 0000000..aea3e96
--- /dev/null
+++ b/room/testing/src/main/java/android/arch/persistence/room/testing/MigrationTestHelper.java
@@ -0,0 +1,420 @@
+/*
+ * 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.
+ */
+
+package android.arch.persistence.room.testing;
+
+import android.app.Instrumentation;
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
+import android.arch.persistence.room.DatabaseConfiguration;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.RoomOpenHelper;
+import android.arch.persistence.room.migration.Migration;
+import android.arch.persistence.room.migration.bundle.DatabaseBundle;
+import android.arch.persistence.room.migration.bundle.EntityBundle;
+import android.arch.persistence.room.migration.bundle.FieldBundle;
+import android.arch.persistence.room.migration.bundle.ForeignKeyBundle;
+import android.arch.persistence.room.migration.bundle.SchemaBundle;
+import android.arch.persistence.room.util.TableInfo;
+import android.content.Context;
+import android.database.Cursor;
+import android.util.Log;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class that can be used in your Instrumentation tests that can create the database in an
+ * older schema.
+ * <p>
+ * You must copy the schema json files (created by passing {@code room.schemaLocation} argument
+ * into the annotation processor) into your test assets and pass in the path for that folder into
+ * the constructor. This class will read the folder and extract the schemas from there.
+ * <pre>
+ * android {
+ *   defaultConfig {
+ *     javaCompileOptions {
+ *       annotationProcessorOptions {
+ *         arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+ *       }
+ *     }
+ *   }
+ *   sourceSets {
+ *     androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
+ *   }
+ * }
+ * </pre>
+ */
+public class MigrationTestHelper extends TestWatcher {
+    private static final String TAG = "MigrationTestHelper";
+    private final String mAssetsFolder;
+    private final SupportSQLiteOpenHelper.Factory mOpenFactory;
+    private List<WeakReference<SupportSQLiteDatabase>> mManagedDatabases = new ArrayList<>();
+    private List<WeakReference<RoomDatabase>> mManagedRoomDatabases = new ArrayList<>();
+    private boolean mTestStarted;
+    private Instrumentation mInstrumentation;
+
+    /**
+     * Creates a new migration helper. It uses the Instrumentation context to load the schema
+     * (falls back to the app resources) and the target context to create the database.
+     *
+     * @param instrumentation The instrumentation instance.
+     * @param assetsFolder    The asset folder in the assets directory.
+     */
+    public MigrationTestHelper(Instrumentation instrumentation, String assetsFolder) {
+        this(instrumentation, assetsFolder, new FrameworkSQLiteOpenHelperFactory());
+    }
+
+    /**
+     * Creates a new migration helper. It uses the Instrumentation context to load the schema
+     * (falls back to the app resources) and the target context to create the database.
+     *
+     * @param instrumentation The instrumentation instance.
+     * @param assetsFolder    The asset folder in the assets directory.
+     * @param openFactory     Factory class that allows creation of {@link SupportSQLiteOpenHelper}
+     */
+    public MigrationTestHelper(Instrumentation instrumentation, String assetsFolder,
+            SupportSQLiteOpenHelper.Factory openFactory) {
+        mInstrumentation = instrumentation;
+        if (assetsFolder.endsWith("/")) {
+            assetsFolder = assetsFolder.substring(0, assetsFolder.length() - 1);
+        }
+        mAssetsFolder = assetsFolder;
+        mOpenFactory = openFactory;
+    }
+
+    @Override
+    protected void starting(Description description) {
+        super.starting(description);
+        mTestStarted = true;
+    }
+
+    /**
+     * Creates the database in the given version.
+     * If the database file already exists, it tries to delete it first. If delete fails, throws
+     * an exception.
+     *
+     * @param name    The name of the database.
+     * @param version The version in which the database should be created.
+     * @return A database connection which has the schema in the requested version.
+     * @throws IOException If it cannot find the schema description in the assets folder.
+     */
+    @SuppressWarnings("SameParameterValue")
+    public SupportSQLiteDatabase createDatabase(String name, int version) throws IOException {
+        File dbPath = mInstrumentation.getTargetContext().getDatabasePath(name);
+        if (dbPath.exists()) {
+            Log.d(TAG, "deleting database file " + name);
+            if (!dbPath.delete()) {
+                throw new IllegalStateException("there is a database file and i could not delete"
+                        + " it. Make sure you don't have any open connections to that database"
+                        + " before calling this method.");
+            }
+        }
+        SchemaBundle schemaBundle = loadSchema(version);
+        RoomDatabase.MigrationContainer container = new RoomDatabase.MigrationContainer();
+        DatabaseConfiguration configuration = new DatabaseConfiguration(
+                mInstrumentation.getTargetContext(), name, mOpenFactory, container, null, true,
+                true);
+        RoomOpenHelper roomOpenHelper = new RoomOpenHelper(configuration,
+                new CreatingDelegate(schemaBundle.getDatabase()),
+                schemaBundle.getDatabase().getIdentityHash());
+        return openDatabase(name, version, roomOpenHelper);
+    }
+
+    /**
+     * Runs the given set of migrations on the provided database.
+     * <p>
+     * It uses the same algorithm that Room uses to choose migrations so the migrations instances
+     * that are provided to this method must be sufficient to bring the database from current
+     * version to the desired version.
+     * <p>
+     * After the migration, the method validates the database schema to ensure that migration
+     * result matches the expected schema. Handling of dropped tables depends on the
+     * {@code validateDroppedTables} argument. If set to true, the verification will fail if it
+     * finds a table that is not registered in the Database. If set to false, extra tables in the
+     * database will be ignored (this is the runtime library behavior).
+     *
+     * @param name                  The database name. You must first create this database via
+     *                              {@link #createDatabase(String, int)}.
+     * @param version               The final version after applying the migrations.
+     * @param validateDroppedTables If set to true, validation will fail if the database has
+     *                              unknown
+     *                              tables.
+     * @param migrations            The list of available migrations.
+     * @throws IOException           If it cannot find the schema for {@code toVersion}.
+     * @throws IllegalStateException If the schema validation fails.
+     */
+    public SupportSQLiteDatabase runMigrationsAndValidate(String name, int version,
+            boolean validateDroppedTables, Migration... migrations) throws IOException {
+        File dbPath = mInstrumentation.getTargetContext().getDatabasePath(name);
+        if (!dbPath.exists()) {
+            throw new IllegalStateException("Cannot find the database file for " + name + ". "
+                    + "Before calling runMigrations, you must first create the database via "
+                    + "createDatabase.");
+        }
+        SchemaBundle schemaBundle = loadSchema(version);
+        RoomDatabase.MigrationContainer container = new RoomDatabase.MigrationContainer();
+        container.addMigrations(migrations);
+        DatabaseConfiguration configuration = new DatabaseConfiguration(
+                mInstrumentation.getTargetContext(), name, mOpenFactory, container, null, true,
+                true);
+        RoomOpenHelper roomOpenHelper = new RoomOpenHelper(configuration,
+                new MigratingDelegate(schemaBundle.getDatabase(), validateDroppedTables),
+                schemaBundle.getDatabase().getIdentityHash());
+        return openDatabase(name, version, roomOpenHelper);
+    }
+
+    private SupportSQLiteDatabase openDatabase(String name, int version,
+            RoomOpenHelper roomOpenHelper) {
+        SupportSQLiteOpenHelper.Configuration config =
+                SupportSQLiteOpenHelper.Configuration
+                        .builder(mInstrumentation.getTargetContext())
+                        .callback(roomOpenHelper)
+                        .name(name)
+                        .version(version)
+                        .build();
+        SupportSQLiteDatabase db = mOpenFactory.create(config).getWritableDatabase();
+        mManagedDatabases.add(new WeakReference<>(db));
+        return db;
+    }
+
+    @Override
+    protected void finished(Description description) {
+        super.finished(description);
+        for (WeakReference<SupportSQLiteDatabase> dbRef : mManagedDatabases) {
+            SupportSQLiteDatabase db = dbRef.get();
+            if (db != null && db.isOpen()) {
+                try {
+                    db.close();
+                } catch (Throwable ignored) {
+                }
+            }
+        }
+        for (WeakReference<RoomDatabase> dbRef : mManagedRoomDatabases) {
+            final RoomDatabase roomDatabase = dbRef.get();
+            if (roomDatabase != null) {
+                roomDatabase.close();
+            }
+        }
+    }
+
+    /**
+     * Registers a database connection to be automatically closed when the test finishes.
+     * <p>
+     * This only works if {@code MigrationTestHelper} is registered as a Junit test rule via
+     * {@link org.junit.Rule Rule} annotation.
+     *
+     * @param db The database connection that should be closed after the test finishes.
+     */
+    public void closeWhenFinished(SupportSQLiteDatabase db) {
+        if (!mTestStarted) {
+            throw new IllegalStateException("You cannot register a database to be closed before"
+                    + " the test starts. Maybe you forgot to annotate MigrationTestHelper as a"
+                    + " test rule? (@Rule)");
+        }
+        mManagedDatabases.add(new WeakReference<>(db));
+    }
+
+    /**
+     * Registers a database connection to be automatically closed when the test finishes.
+     * <p>
+     * This only works if {@code MigrationTestHelper} is registered as a Junit test rule via
+     * {@link org.junit.Rule Rule} annotation.
+     *
+     * @param db The RoomDatabase instance which holds the database.
+     */
+    public void closeWhenFinished(RoomDatabase db) {
+        if (!mTestStarted) {
+            throw new IllegalStateException("You cannot register a database to be closed before"
+                    + " the test starts. Maybe you forgot to annotate MigrationTestHelper as a"
+                    + " test rule? (@Rule)");
+        }
+        mManagedRoomDatabases.add(new WeakReference<>(db));
+    }
+
+    private SchemaBundle loadSchema(int version) throws IOException {
+        try {
+            return loadSchema(mInstrumentation.getContext(), version);
+        } catch (FileNotFoundException testAssetsIOExceptions) {
+            Log.w(TAG, "Could not find the schema file in the test assets. Checking the"
+                    + " application assets");
+            try {
+                return loadSchema(mInstrumentation.getTargetContext(), version);
+            } catch (FileNotFoundException appAssetsException) {
+                // throw the test assets exception instead
+                throw new FileNotFoundException("Cannot find the schema file in the assets folder. "
+                        + "Make sure to include the exported json schemas in your test assert "
+                        + "inputs. See "
+                        + "https://developer.android.com/topic/libraries/architecture/"
+                        + "room.html#db-migration-testing for details. Missing file: "
+                        + testAssetsIOExceptions.getMessage());
+            }
+        }
+    }
+
+    private SchemaBundle loadSchema(Context context, int version) throws IOException {
+        InputStream input = context.getAssets().open(mAssetsFolder + "/" + version + ".json");
+        return SchemaBundle.deserialize(input);
+    }
+
+    private static TableInfo toTableInfo(EntityBundle entityBundle) {
+        return new TableInfo(entityBundle.getTableName(), toColumnMap(entityBundle),
+                toForeignKeys(entityBundle.getForeignKeys()));
+    }
+
+    private static Set<TableInfo.ForeignKey> toForeignKeys(
+            List<ForeignKeyBundle> bundles) {
+        if (bundles == null) {
+            return Collections.emptySet();
+        }
+        Set<TableInfo.ForeignKey> result = new HashSet<>(bundles.size());
+        for (ForeignKeyBundle bundle : bundles) {
+            result.add(new TableInfo.ForeignKey(bundle.getTable(),
+                    bundle.getOnDelete(), bundle.getOnUpdate(),
+                    bundle.getColumns(), bundle.getReferencedColumns()));
+        }
+        return result;
+    }
+
+    private static Map<String, TableInfo.Column> toColumnMap(EntityBundle entity) {
+        Map<String, TableInfo.Column> result = new HashMap<>();
+        for (FieldBundle bundle : entity.getFields()) {
+            TableInfo.Column column = toColumn(entity, bundle);
+            result.put(column.name, column);
+        }
+        return result;
+    }
+
+    private static TableInfo.Column toColumn(EntityBundle entity, FieldBundle field) {
+        return new TableInfo.Column(field.getColumnName(), field.getAffinity(),
+                field.isNonNull(), findPrimaryKeyPosition(entity, field));
+    }
+
+    private static int findPrimaryKeyPosition(EntityBundle entity, FieldBundle field) {
+        List<String> columnNames = entity.getPrimaryKey().getColumnNames();
+        int i = 0;
+        for (String columnName : columnNames) {
+            i++;
+            if (field.getColumnName().equalsIgnoreCase(columnName)) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+    class MigratingDelegate extends RoomOpenHelperDelegate {
+        private final boolean mVerifyDroppedTables;
+
+        MigratingDelegate(DatabaseBundle databaseBundle, boolean verifyDroppedTables) {
+            super(databaseBundle);
+            mVerifyDroppedTables = verifyDroppedTables;
+        }
+
+        @Override
+        protected void createAllTables(SupportSQLiteDatabase database) {
+            throw new UnsupportedOperationException("Was expecting to migrate but received create."
+                    + "Make sure you have created the database first.");
+        }
+
+        @Override
+        protected void validateMigration(SupportSQLiteDatabase db) {
+            final Map<String, EntityBundle> tables = mDatabaseBundle.getEntitiesByTableName();
+            for (EntityBundle entity : tables.values()) {
+                final TableInfo expected = toTableInfo(entity);
+                final TableInfo found = TableInfo.read(db, entity.getTableName());
+                if (!expected.equals(found)) {
+                    throw new IllegalStateException(
+                            "Migration failed. expected:" + expected + " , found:" + found);
+                }
+            }
+            if (mVerifyDroppedTables) {
+                // now ensure tables that should be removed are removed.
+                Cursor cursor = db.query("SELECT name FROM sqlite_master WHERE type='table'"
+                                + " AND name NOT IN(?, ?, ?)",
+                        new String[]{Room.MASTER_TABLE_NAME, "android_metadata",
+                                "sqlite_sequence"});
+                //noinspection TryFinallyCanBeTryWithResources
+                try {
+                    while (cursor.moveToNext()) {
+                        final String tableName = cursor.getString(0);
+                        if (!tables.containsKey(tableName)) {
+                            throw new IllegalStateException("Migration failed. Unexpected table "
+                                    + tableName);
+                        }
+                    }
+                } finally {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    static class CreatingDelegate extends RoomOpenHelperDelegate {
+
+        CreatingDelegate(DatabaseBundle databaseBundle) {
+            super(databaseBundle);
+        }
+
+        @Override
+        protected void createAllTables(SupportSQLiteDatabase database) {
+            for (String query : mDatabaseBundle.buildCreateQueries()) {
+                database.execSQL(query);
+            }
+        }
+
+        @Override
+        protected void validateMigration(SupportSQLiteDatabase db) {
+            throw new UnsupportedOperationException("This open helper just creates the database but"
+                    + " it received a migration request.");
+        }
+    }
+
+    abstract static class RoomOpenHelperDelegate extends RoomOpenHelper.Delegate {
+        final DatabaseBundle mDatabaseBundle;
+
+        RoomOpenHelperDelegate(DatabaseBundle databaseBundle) {
+            mDatabaseBundle = databaseBundle;
+        }
+
+        @Override
+        protected void dropAllTables(SupportSQLiteDatabase database) {
+            throw new UnsupportedOperationException("cannot drop all tables in the test");
+        }
+
+        @Override
+        protected void onCreate(SupportSQLiteDatabase database) {
+        }
+
+        @Override
+        protected void onOpen(SupportSQLiteDatabase database) {
+        }
+    }
+}
diff --git a/samples/Android.mk b/samples/Android.mk
deleted file mode 100644
index cc329fe..0000000
--- a/samples/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/Support13Demos/Android.mk b/samples/Support13Demos/Android.mk
deleted file mode 100644
index a227e10..0000000
--- a/samples/Support13Demos/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_MODULE_TAGS := samples tests
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v13
-
-LOCAL_PACKAGE_NAME := Support13Demos
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_MIN_SDK_VERSION := 13
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-# Use the folloing include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/Support13Demos/AndroidManifest.xml b/samples/Support13Demos/AndroidManifest.xml
index f345c63..af7fad2 100644
--- a/samples/Support13Demos/AndroidManifest.xml
+++ b/samples/Support13Demos/AndroidManifest.xml
@@ -24,8 +24,6 @@
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
 
-    <uses-sdk android:minSdkVersion="13" />
-
     <!-- The smallest screen this app works on is a phone.  The app will
          scale its UI to larger screens but doesn't make good use of them
          so allow the compatibility mode button to be shown (mostly because
diff --git a/samples/Support13Demos/build.gradle b/samples/Support13Demos/build.gradle
index 1b58043..4c015ae 100644
--- a/samples/Support13Demos/build.gradle
+++ b/samples/Support13Demos/build.gradle
@@ -1,14 +1,15 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-v13')
+    implementation project(':support-v13')
 }
 
 android {
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
-        minSdkVersion 13
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -19,7 +20,16 @@
     }
 
     lintOptions {
+        checkReleaseBuilds false
         abortOnError false
+        check 'NewApi'
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
     }
 
     compileOptions {
diff --git a/samples/Support13Demos/res/values/colors.xml b/samples/Support13Demos/res/values/colors.xml
index a1daf63..e50c7a0 100644
--- a/samples/Support13Demos/res/values/colors.xml
+++ b/samples/Support13Demos/res/values/colors.xml
@@ -18,5 +18,5 @@
     <drawable name="red">#7f00</drawable>
     <drawable name="blue">#770000ff</drawable>
     <drawable name="green">#7700ff00</drawable>
-	<drawable name="yellow">#77ffff00</drawable>
+    <drawable name="yellow">#77ffff00</drawable>
 </resources>
diff --git a/samples/Support13Demos/res/values/strings.xml b/samples/Support13Demos/res/values/strings.xml
index 30b6a18..b0950b2 100644
--- a/samples/Support13Demos/res/values/strings.xml
+++ b/samples/Support13Demos/res/values/strings.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
     <string name="activity_sample_code">Support v13 Demos</string>
 
     <string name="hello_world"><b>Hello, <i>World!</i></b></string>
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/Support13Demos.java b/samples/Support13Demos/src/com/example/android/supportv13/Support13Demos.java
index fda4b34..7db027d 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/Support13Demos.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/Support13Demos.java
@@ -113,6 +113,7 @@
         new Comparator<Map<String, Object>>() {
         private final Collator   collator = Collator.getInstance();
 
+        @Override
         public int compare(Map<String, Object> map1, Map<String, Object> map2) {
             return collator.compare(map1.get("title"), map2.get("title"));
         }
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/app/CursorFragment.java b/samples/Support13Demos/src/com/example/android/supportv13/app/CursorFragment.java
index 57f0e10..ee311fc 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/app/CursorFragment.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/CursorFragment.java
@@ -81,6 +81,7 @@
         item.setActionView(sv);
     }
 
+    @Override
     public boolean onQueryTextChange(String newText) {
         // Called when the action bar search text has changed.  Update
         // the search filter, and restart the loader to do a new query
@@ -110,6 +111,7 @@
         Contacts.LOOKUP_KEY,
     };
 
+    @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         // This is called when a new Loader needs to be created.  This
         // sample only has one Loader, so we don't care about the ID.
@@ -133,6 +135,7 @@
                 Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
     }
 
+    @Override
     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
         // Swap the new cursor in.  (The framework will take care of closing the
         // old cursor once we return.)
@@ -146,6 +149,7 @@
         }
     }
 
+    @Override
     public void onLoaderReset(Loader<Cursor> loader) {
         // This is called when the last Cursor provided to onLoadFinished()
         // above is about to be closed.  We need to make sure we are no
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentPagerSupport.java b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentPagerSupport.java
index 04532b8..3d169c8 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentPagerSupport.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentPagerSupport.java
@@ -58,12 +58,14 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.goto_first);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(0);
             }
         });
         button = (Button)findViewById(R.id.goto_last);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(NUM_ITEMS-1);
             }
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java
index e60c268..cec71dd 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java
@@ -58,12 +58,14 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.goto_first);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(0);
             }
         });
         button = (Button)findViewById(R.id.goto_last);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(NUM_ITEMS-1);
             }
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/view/CheckableFrameLayout.java b/samples/Support13Demos/src/com/example/android/supportv13/view/CheckableFrameLayout.java
index f642e8e..e2383de 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/view/CheckableFrameLayout.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/view/CheckableFrameLayout.java
@@ -33,15 +33,18 @@
         super(context, attrs);
     }
 
+    @Override
     public void setChecked(boolean checked) {
         mChecked = checked;
         setBackgroundDrawable(checked ? new ColorDrawable(0xff0000a0) : null);
     }
 
+    @Override
     public boolean isChecked() {
         return mChecked;
     }
 
+    @Override
     public void toggle() {
         setChecked(!mChecked);
     }
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/view/inputmethod/CommitContentSupport.java b/samples/Support13Demos/src/com/example/android/supportv13/view/inputmethod/CommitContentSupport.java
index 4a6181d..78d64cd 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/view/inputmethod/CommitContentSupport.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/view/inputmethod/CommitContentSupport.java
@@ -62,7 +62,7 @@
         setContentView(R.layout.commit_content);
 
         final LinearLayout layout =
-                (LinearLayout) findViewById(R.id.commit_content_sample_edit_boxes);
+                findViewById(R.id.commit_content_sample_edit_boxes);
 
         // This declares that the IME cannot commit any content with
         // InputConnectionCompat#commitContent().
@@ -90,12 +90,12 @@
         layout.addView(createEditTextWithContentMimeTypes(
                 new String[]{"image/png", "image/gif", "image/jpeg", "image/webp"}));
 
-        mWebView = (WebView) findViewById(R.id.commit_content_webview);
-        mMimeTypes = (TextView) findViewById(R.id.text_commit_content_mime_types);
-        mLabel = (TextView) findViewById(R.id.text_commit_content_label);
-        mContentUri = (TextView) findViewById(R.id.text_commit_content_content_uri);
-        mLinkUri = (TextView) findViewById(R.id.text_commit_content_link_uri);
-        mFlags = (TextView) findViewById(R.id.text_commit_content_link_flags);
+        mWebView = findViewById(R.id.commit_content_webview);
+        mMimeTypes = findViewById(R.id.text_commit_content_mime_types);
+        mLabel = findViewById(R.id.text_commit_content_label);
+        mContentUri = findViewById(R.id.text_commit_content_content_uri);
+        mLinkUri = findViewById(R.id.text_commit_content_link_uri);
+        mFlags = findViewById(R.id.text_commit_content_link_flags);
 
         if (savedInstanceState != null) {
             final InputContentInfoCompat previousInputContentInfo = InputContentInfoCompat.wrap(
diff --git a/samples/Support4Demos/Android.mk b/samples/Support4Demos/Android.mk
deleted file mode 100644
index 0ba19b0..0000000
--- a/samples/Support4Demos/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := samples tests
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v4
-
-LOCAL_PACKAGE_NAME := Support4Demos
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 4
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/Support4Demos/AndroidManifest.xml b/samples/Support4Demos/AndroidManifest.xml
index 67faa66..812d4e8 100644
--- a/samples/Support4Demos/AndroidManifest.xml
+++ b/samples/Support4Demos/AndroidManifest.xml
@@ -26,8 +26,6 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
 
-    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="22" />
-
     <!-- The smallest screen this app works on is a phone.  The app will
          scale its UI to larger screens but doesn't make good use of them
          so allow the compatibility mode button to be shown (mostly because
@@ -51,7 +49,7 @@
         <activity android:name=".app.SendResult"
                 android:theme="@style/ThemeDialogWhenLarge">
         </activity>
-        
+
         <!-- Fragment Support Samples -->
 
         <activity android:name=".app.FragmentAlertDialogSupport"
@@ -144,7 +142,7 @@
                 <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
             </intent-filter>
         </activity>
-        
+
         <activity android:name=".app.FragmentRetainInstanceSupport"
                 android:label="@string/fragment_retain_instance_support">
             <intent-filter>
@@ -216,7 +214,7 @@
                 <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
             </intent-filter>
         </activity>
-        
+
         <activity android:name=".app.LoaderCustomSupport"
                 android:label="@string/loader_custom_support">
             <intent-filter>
@@ -235,6 +233,17 @@
         <provider android:name=".app.LoaderThrottleSupport$SimpleProvider"
                   android:authorities="com.example.android.apis.supportv4.app.LoaderThrottle" />
 
+        <activity android:name=".app.SimpleJobIntentController"
+            android:label="@string/simple_job_intent_controller">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".app.SimpleJobIntentService"
+            android:permission="android.permission.BIND_JOB_SERVICE" />
+
         <activity android:name=".content.LocalServiceBroadcaster"
                 android:label="@string/local_service_broadcaster">
             <intent-filter>
@@ -415,14 +424,6 @@
         </provider>
 <!-- END_INCLUDE(file_provider_declaration) -->
 
-        <activity android:name=".media.TransportControllerActivity"
-                android:label="@string/sample_transport_controller_activity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
         <!-- MediaBrowserCompat Sample -->
         <activity android:name=".media.MediaBrowserSupport"
             android:label="@string/media_browser_support">
@@ -432,6 +433,20 @@
             </intent-filter>
         </activity>
 
+        <!-- (OPTIONAL) use this meta data to indicate which icon should be used in media
+            notifications (for example, when the music changes and the user is
+            looking at another app) -->
+        <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
+            android:resource="@drawable/ic_notification" />
+
+        <!--
+             (OPTIONAL) use this meta data to override the theme from which Android Auto will
+             look for colors. If you don't set this, Android Auto will look
+             for color attributes in your application theme.
+        -->
+        <meta-data android:name="com.google.android.gms.car.application.theme"
+            android:resource="@style/CarTheme" />
+
         <service android:name=".media.MediaBrowserServiceSupport"
             android:exported="true" android:process=":service">
             <intent-filter>
diff --git a/samples/Support4Demos/build.gradle b/samples/Support4Demos/build.gradle
index 959968a..e265698 100644
--- a/samples/Support4Demos/build.gradle
+++ b/samples/Support4Demos/build.gradle
@@ -1,14 +1,15 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-v4')
+    implementation project(':support-v4')
 }
 
 android {
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
-        minSdkVersion 9
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -18,8 +19,17 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
-        abortOnError false
+        checkReleaseBuilds false
+        abortOnError true
+        check 'NewApi'
     }
 
     compileOptions {
diff --git a/samples/Support4Demos/res/layout/accessibility_roledescription.xml b/samples/Support4Demos/res/layout/accessibility_roledescription.xml
index a617e09..842210d 100644
--- a/samples/Support4Demos/res/layout/accessibility_roledescription.xml
+++ b/samples/Support4Demos/res/layout/accessibility_roledescription.xml
@@ -35,7 +35,8 @@
                 android:padding="24dp"
                 android:background="#ffffff"
                 android:textColor="#000000"
-                android:text="@string/accessibility_roledescription_item"/>
+                android:text="@string/accessibility_roledescription_item"
+                android:layout_marginLeft="24dp"/>
 
         </LinearLayout>
 
@@ -56,7 +57,8 @@
                 android:layout_height="wrap_content"
                 android:layout_marginStart="0dp"
                 android:textSize="24sp"
-                android:text="@string/accessibility_roledescription_h1_item"/>
+                android:text="@string/accessibility_roledescription_h1_item"
+                android:layout_marginLeft="0dp"/>
 
             <TextView
                 android:id="@+id/text_heading_2"
@@ -64,7 +66,8 @@
                 android:layout_height="wrap_content"
                 android:layout_marginStart="24dp"
                 android:textSize="20sp"
-                android:text="@string/accessibility_roledescription_h2_item"/>
+                android:text="@string/accessibility_roledescription_h2_item"
+                android:layout_marginLeft="24dp"/>
 
             <TextView
                 android:id="@+id/text_heading_3"
@@ -72,7 +75,8 @@
                 android:layout_height="wrap_content"
                 android:layout_marginStart="48dp"
                 android:textSize="16sp"
-                android:text="@string/accessibility_roledescription_h3_item"/>
+                android:text="@string/accessibility_roledescription_h3_item"
+                android:layout_marginLeft="48dp"/>
 
         </LinearLayout>
 
diff --git a/samples/Support4Demos/res/layout/media_controller.xml b/samples/Support4Demos/res/layout/media_controller.xml
deleted file mode 100644
index b5e58b1..0000000
--- a/samples/Support4Demos/res/layout/media_controller.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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="match_parent"
-    android:layout_height="wrap_content"
-    android:background="#CC000000"
-    android:orientation="vertical"
-    android:layoutDirection="ltr">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:paddingTop="4dip"
-        android:orientation="horizontal">
-
-        <ImageButton android:id="@+id/prev" style="@android:style/MediaButton.Previous" />
-        <ImageButton android:id="@+id/rew" style="@android:style/MediaButton.Rew" />
-        <ImageButton android:id="@+id/pause" style="@android:style/MediaButton.Play" />
-        <ImageButton android:id="@+id/ffwd" style="@android:style/MediaButton.Ffwd" />
-        <ImageButton android:id="@+id/next" style="@android:style/MediaButton.Next" />
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-
-        <TextView android:id="@+id/time_current"
-            android:textSize="14sp"
-            android:textStyle="bold"
-            android:paddingTop="4dip"
-            android:paddingStart="4dip"
-            android:layout_gravity="center_horizontal"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingEnd="4dip"
-            android:textColor="?android:attr/textColorSecondary" />
-
-        <SeekBar
-            android:id="@+id/mediacontroller_progress"
-            style="?android:attr/progressBarStyleHorizontal"
-            android:layout_width="0dip"
-            android:layout_weight="1"
-            android:layout_height="32dip"
-            android:layout_alignParentStart="true"
-            android:layout_alignParentEnd="true" />
-
-        <TextView android:id="@+id/time"
-            android:textSize="14sp"
-            android:textStyle="bold"
-            android:paddingTop="4dip"
-            android:paddingEnd="4dip"
-            android:layout_gravity="center_horizontal"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingStart="4dip"
-            android:textColor="?android:attr/textColorSecondary" />
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/samples/Support4Demos/res/layout/media_list_item.xml b/samples/Support4Demos/res/layout/media_list_item.xml
index 72c0ccf..a1caed0 100644
--- a/samples/Support4Demos/res/layout/media_list_item.xml
+++ b/samples/Support4Demos/res/layout/media_list_item.xml
@@ -40,7 +40,8 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/margin_text_view"
             android:layout_marginTop="@dimen/margin_text_view"
-            android:textAppearance="?android:attr/textAppearanceMedium"/>
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:layout_marginLeft="@dimen/margin_text_view"/>
 
         <TextView
             android:id="@+id/description"
@@ -48,7 +49,8 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/margin_text_view"
             android:layout_marginTop="@dimen/margin_text_view"
-            android:textAppearance="?android:attr/textAppearanceSmall"/>
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:layout_marginLeft="@dimen/margin_text_view"/>
 
     </LinearLayout>
 
diff --git a/samples/Support4Demos/res/layout/simple_job_intent_controller.xml b/samples/Support4Demos/res/layout/simple_job_intent_controller.xml
new file mode 100644
index 0000000..7e06db9
--- /dev/null
+++ b/samples/Support4Demos/res/layout/simple_job_intent_controller.xml
@@ -0,0 +1,46 @@
+<?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"
+    android:orientation="vertical" android:padding="4dip"
+    android:gravity="center_horizontal"
+    android:layout_width="match_parent" android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="match_parent" android:layout_height="wrap_content"
+        android:layout_weight="0" android:paddingBottom="4dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/simple_job_intent_controller_msg"/>
+
+    <Button android:id="@+id/worka"
+        android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:text="@string/schedule_work_a">
+        <requestFocus />
+    </Button>
+
+    <Button android:id="@+id/workb"
+        android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:text="@string/schedule_work_b">
+        <requestFocus />
+    </Button>
+
+    <Button android:id="@+id/workc"
+        android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:text="@string/schedule_work_c">
+        <requestFocus />
+    </Button>
+
+</LinearLayout>
diff --git a/samples/Support4Demos/res/layout/videoview.xml b/samples/Support4Demos/res/layout/videoview.xml
deleted file mode 100644
index 6d562cb..0000000
--- a/samples/Support4Demos/res/layout/videoview.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<!-- BEGIN_INCLUDE(complete) -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent" android:layout_height="match_parent"
-    >
-    <view class="com.example.android.supportv4.media.TransportControllerActivity$Content"
-        android:id="@+id/content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        />
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:fitsSystemWindows="true"
-        android:animateLayoutChanges="true"
-        >
-        <com.example.android.supportv4.media.MediaController
-                android:id="@+id/media_controller"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="bottom">
-        </com.example.android.supportv4.media.MediaController>
-    </FrameLayout>
-</FrameLayout>
-<!-- END_INCLUDE(complete) -->
diff --git a/samples/Support4Demos/res/raw/videoviewdemo.mp4 b/samples/Support4Demos/res/raw/videoviewdemo.mp4
deleted file mode 100644
index 5772810..0000000
--- a/samples/Support4Demos/res/raw/videoviewdemo.mp4
+++ /dev/null
Binary files differ
diff --git a/samples/Support4Demos/res/values-v21/styles.xml b/samples/Support4Demos/res/values-v21/styles.xml
new file mode 100644
index 0000000..5e4f596
--- /dev/null
+++ b/samples/Support4Demos/res/values-v21/styles.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.
+-->
+
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material">
+        <item name="android:colorPrimary">#ffff5722</item>
+        <item name="android:colorPrimaryDark">#ffbf360c</item>
+        <item name="android:colorAccent">#ffff5722</item>
+    </style>
+
+    <style name="CarTheme" parent="AppTheme">
+        <!-- colorPrimaryDark is currently used in Android Auto for:
+             - App background
+             - Drawer right side ("more" custom actions) background
+             - Notification icon badge tinting
+             - Overview “now playing” icon tinting
+         -->
+        <item name="android:colorPrimaryDark">#ffbf360c</item>
+
+        <!-- colorAccent is used in Android Auto for:
+             - Spinner
+             - progress bar
+             - floating action button background (Play/Pause in media apps)
+         -->
+        <item name="android:colorAccent">#ffff5722</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/samples/Support4Demos/res/values/strings.xml b/samples/Support4Demos/res/values/strings.xml
index 24d88d6..83404e8 100644
--- a/samples/Support4Demos/res/values/strings.xml
+++ b/samples/Support4Demos/res/values/strings.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
     <string name="activity_sample_code">Support v4 Demos</string>
 
     <string name="hello_world"><b>Hello, <i>World!</i></b></string>
@@ -24,9 +24,9 @@
     </string>
     <string name="alert_dialog_ok">OK</string>
     <string name="alert_dialog_cancel">Cancel</string>
-    
+
     <string name="initial_text">Initial text.</string>
-    
+
     <string name="pick_result">Pick a result to send, or BACK to cancel.</string>
     <string name="corky">Corky</string>
     <string name="violet">Violet</string>
@@ -86,7 +86,7 @@
     <string name="home">Go home</string>
     <string name="new_fragment">Add new</string>
     <string name="delete_fragment">Pop top</string>
-    
+
     <string name="fragment_tabs">Fragment/Tabs</string>
 
     <string name="fragment_tabs_pager">Fragment/Tabs and Pager</string>
@@ -112,6 +112,13 @@
     <string name="start_service">Start Service</string>
     <string name="stop_service">Stop Service</string>
 
+    <string name="simple_job_intent_controller">App/Simple JobIntentService</string>
+    <string name="simple_job_intent_controller_msg">Demonstrates use of JobIntentService.
+        The buttons enqueue work in to the service, which it will then process.</string>
+    <string name="schedule_work_a">Work A</string>
+    <string name="schedule_work_b">Work B</string>
+    <string name="schedule_work_c">Work C</string>
+
     <string name="simple_wakeful_controller">Content/Simple WakefulReceiver</string>
 
     <string name="simple_wakeful_controller_msg">Demonstrates use of WakefulBroadcastReceiver
diff --git a/samples/Support4Demos/res/values/styles.xml b/samples/Support4Demos/res/values/styles.xml
index d94f9b0..bb090d2 100644
--- a/samples/Support4Demos/res/values/styles.xml
+++ b/samples/Support4Demos/res/values/styles.xml
@@ -4,9 +4,9 @@
      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.
@@ -36,27 +36,4 @@
          in correctly laying out an activity as a dialog. -->
     <style name="ThemeDialogWhenLarge" parent="android:style/Theme">
     </style>
-
-    <style name="AppTheme" parent="android:Theme.Material">
-        <item name="android:colorPrimary">#ffff5722</item>
-        <item name="android:colorPrimaryDark">#ffbf360c</item>
-        <item name="android:colorAccent">#ffff5722</item>
-    </style>
-
-    <style name="CarTheme" parent="AppTheme">
-        <!-- colorPrimaryDark is currently used in Android Auto for:
-             - App background
-             - Drawer right side ("more" custom actions) background
-             - Notification icon badge tinting
-             - Overview “now playing” icon tinting
-         -->
-        <item name="android:colorPrimaryDark">#ffbf360c</item>
-
-        <!-- colorAccent is used in Android Auto for:
-             - Spinner
-             - progress bar
-             - floating action button background (Play/Pause in media apps)
-         -->
-        <item name="android:colorAccent">#ffff5722</item>
-    </style>
 </resources>
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/Support4Demos.java b/samples/Support4Demos/src/com/example/android/supportv4/Support4Demos.java
index af445db..a90551f 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/Support4Demos.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/Support4Demos.java
@@ -38,10 +38,10 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        
+
         Intent intent = getIntent();
         String path = intent.getStringExtra("com.example.android.apis.Path");
-        
+
         if (path == null) {
             path = "";
         }
@@ -66,16 +66,16 @@
 
         String[] prefixPath;
         String prefixWithSlash = prefix;
-        
+
         if (prefix.equals("")) {
             prefixPath = null;
         } else {
             prefixPath = prefix.split("/");
             prefixWithSlash = prefix + "/";
         }
-        
+
         int len = list.size();
-        
+
         Map<String, Boolean> entries = new HashMap<String, Boolean>();
 
         for (int i = 0; i < len; i++) {
@@ -84,9 +84,9 @@
             String label = labelSeq != null
                     ? labelSeq.toString()
                     : info.activityInfo.name;
-            
+
             if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) {
-                
+
                 String[] labelPath = label.split("/");
 
                 String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];
@@ -105,7 +105,7 @@
         }
 
         Collections.sort(myData, sDisplayNameComparator);
-        
+
         return myData;
     }
 
@@ -113,6 +113,7 @@
         new Comparator<Map<String, Object>>() {
         private final Collator   collator = Collator.getInstance();
 
+        @Override
         public int compare(Map<String, Object> map1, Map<String, Object> map2) {
             return collator.compare(map1.get("title"), map2.get("title"));
         }
@@ -123,7 +124,7 @@
         result.setClassName(pkg, componentName);
         return result;
     }
-    
+
     protected Intent browseIntent(String path) {
         Intent result = new Intent();
         result.setClass(this, Support4Demos.class);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java
index 8c9d40c..061ed21 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java
@@ -22,7 +22,6 @@
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityManagerCompat;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -61,7 +60,7 @@
         setContentView(R.layout.accessibility_manager);
         mAccessibilityManager = (AccessibilityManager) getSystemService(
                 Service.ACCESSIBILITY_SERVICE);
-        mAccessibilityStateView = (TextView) findViewById(R.id.accessibility_state);
+        mAccessibilityStateView = findViewById(R.id.accessibility_state);
         registerAccessibilityStateChangeListener();
     }
 
@@ -79,19 +78,16 @@
      * when the global accessibility state on the device changes.
      */
     private void registerAccessibilityStateChangeListener() {
-        // The AccessibilityStateChange listener APIs were added in ICS. Therefore to be
-        // backwards compatible we use the APIs in the support library. Note that if the
-        // platform API version is lower and the called API is not available no listener
-        // is added and you will not receive a call of onAccessibilityStateChanged.
-        AccessibilityManagerCompat.addAccessibilityStateChangeListener(mAccessibilityManager,
-                new AccessibilityManagerCompat.AccessibilityStateChangeListener() {
-            @Override
-            public void onAccessibilityStateChanged(boolean enabled) {
-                Toast.makeText(AccessibilityManagerSupportActivity.this,
-                        getString(R.string.accessibility_manager_accessibility_state, enabled),
-                        Toast.LENGTH_SHORT).show();
-            }
-        });
+        mAccessibilityManager.addAccessibilityStateChangeListener(
+                new AccessibilityManager.AccessibilityStateChangeListener() {
+                    @Override
+                    public void onAccessibilityStateChanged(boolean enabled) {
+                        Toast.makeText(AccessibilityManagerSupportActivity.this,
+                                getString(R.string.accessibility_manager_accessibility_state,
+                                        Boolean.toString(enabled)),
+                                Toast.LENGTH_SHORT).show();
+                    }
+                });
     }
 
     /**
@@ -99,13 +95,9 @@
      * accessibility services.
      */
     private void updateAccessibilityStateView() {
-        // The API for getting the enabled accessibility services based on feedback
-        // type was added in ICS. Therefore to be backwards compatible we use the
-        // APIs in the support library. Note that if the platform API version is lower
-        // and the called API is not available an empty list of services is returned.
         List<AccessibilityServiceInfo> enabledServices =
-            AccessibilityManagerCompat.getEnabledAccessibilityServiceList(mAccessibilityManager,
-                    AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+                mAccessibilityManager.getEnabledAccessibilityServiceList(
+                        AccessibilityServiceInfo.FEEDBACK_SPOKEN);
         if (!enabledServices.isEmpty()) {
             StringBuilder builder = new StringBuilder();
             final int enabledServiceCount = enabledServices.size();
@@ -113,14 +105,14 @@
                 AccessibilityServiceInfo service = enabledServices.get(i);
                 // Some new APIs were added in ICS for getting more information about
                 // an accessibility service. Again accessed them via the support library.
-                ResolveInfo resolveInfo = AccessibilityServiceInfoCompat.getResolveInfo(service);
+                ResolveInfo resolveInfo = service.getResolveInfo();
                 String serviceDescription = getString(
                         R.string.accessibility_manager_enabled_service,
                         resolveInfo.loadLabel(getPackageManager()),
                         AccessibilityServiceInfoCompat.feedbackTypeToString(service.feedbackType),
                         AccessibilityServiceInfoCompat.loadDescription(
                                 service, getPackageManager()),
-                        AccessibilityServiceInfoCompat.getSettingsActivityName(service));
+                        service.getSettingsActivityName());
                 builder.append(serviceDescription);
             }
             mAccessibilityStateView.setText(builder);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java
index 5715ea4..512458b 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java
@@ -42,29 +42,29 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.accessibility_roledescription);
 
-        TextView firstTextView = (TextView) findViewById(R.id.text_view_1);
+        TextView firstTextView = findViewById(R.id.text_view_1);
         String roleDescriptionTextView = getString(R.string.accessibility_roledescription_role);
         ViewCompat.setAccessibilityDelegate(firstTextView,
             new RoleDescriptionAccessibilityDelegate(roleDescriptionTextView));
 
-        TextView heading1 = (TextView) findViewById(R.id.text_heading_1);
+        TextView heading1 = findViewById(R.id.text_heading_1);
         String roleDescriptionHeading1 = getString(R.string.accessibility_roledescription_h1_role);
         ViewCompat.setAccessibilityDelegate(heading1,
             new RoleDescriptionAccessibilityDelegate(roleDescriptionHeading1));
 
-        TextView heading2 = (TextView) findViewById(R.id.text_heading_2);
+        TextView heading2 = findViewById(R.id.text_heading_2);
         String roleDescriptionHeading2 = getString(R.string.accessibility_roledescription_h2_role);
         ViewCompat.setAccessibilityDelegate(heading2,
             new RoleDescriptionAccessibilityDelegate(roleDescriptionHeading2));
 
-        TextView heading3 = (TextView) findViewById(R.id.text_heading_3);
+        TextView heading3 = findViewById(R.id.text_heading_3);
         String roleDescriptionHeading3 = getString(R.string.accessibility_roledescription_h3_role);
         ViewCompat.setAccessibilityDelegate(heading3,
             new RoleDescriptionAccessibilityDelegate(roleDescriptionHeading3));
 
         // This is an example of an <strong>incorrect</strong> use of the role description.
         // You should not set the role description for standard widgets in your own code.
-        Button button = (Button) findViewById(R.id.button);
+        Button button = findViewById(R.id.button);
         String roleDescriptionButton =
             getString(R.string.accessibility_roledescription_button_role);
         ViewCompat.setAccessibilityDelegate(button,
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentAlertDialogSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentAlertDialogSupport.java
index f35e021..c651fdc 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentAlertDialogSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentAlertDialogSupport.java
@@ -47,6 +47,7 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.show);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 showDialog();
             }
@@ -91,6 +92,7 @@
                     .setTitle(title)
                     .setPositiveButton(R.string.alert_dialog_ok,
                         new DialogInterface.OnClickListener() {
+                            @Override
                             public void onClick(DialogInterface dialog, int whichButton) {
                                 ((FragmentAlertDialogSupport)getActivity()).doPositiveClick();
                             }
@@ -98,6 +100,7 @@
                     )
                     .setNegativeButton(R.string.alert_dialog_cancel,
                         new DialogInterface.OnClickListener() {
+                            @Override
                             public void onClick(DialogInterface dialog, int whichButton) {
                                 ((FragmentAlertDialogSupport)getActivity()).doNegativeClick();
                             }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java
index 478cb7d..871f430 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java
@@ -40,8 +40,9 @@
         setContentView(R.layout.fragment_stack);
 
         // Watch for button clicks.
-        Button button = (Button) findViewById(R.id.new_fragment);
+        Button button = findViewById(R.id.new_fragment);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 addFragmentToStack();
             }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogOrActivitySupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogOrActivitySupport.java
index 06b2730..dd5cca7 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogOrActivitySupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogOrActivitySupport.java
@@ -48,6 +48,7 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.show_dialog);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 showDialog();
             }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogSupport.java
index b387bfc..140e920 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentDialogSupport.java
@@ -48,6 +48,7 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.show);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 showDialog();
             }
@@ -155,6 +156,7 @@
             // Watch for button clicks.
             Button button = (Button)v.findViewById(R.id.show);
             button.setOnClickListener(new OnClickListener() {
+                @Override
                 public void onClick(View v) {
                     // When button is clicked, call up to owning activity.
                     ((FragmentDialogSupport)getActivity()).showDialog();
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentHideShowSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentHideShowSupport.java
index 3441506..0e8fe46 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentHideShowSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentHideShowSupport.java
@@ -57,6 +57,7 @@
     void addShowHideListener(int buttonId, final Fragment fragment) {
         final Button button = (Button)findViewById(buttonId);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                 ft.setCustomAnimations(android.R.anim.fade_in,
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java
index fb65a2b..b8d60d6 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java
@@ -40,6 +40,7 @@
 
     // Update fragment visibility when check boxes are changed.
     final OnClickListener mClickListener = new OnClickListener() {
+        @Override
         public void onClick(View v) {
             updateFragmentVisibility();
         }
@@ -64,13 +65,13 @@
             ft.add(mFragment2, "f2");
         }
         ft.commit();
-        
+
         // Watch check box clicks.
         mCheckBox1 = (CheckBox)v.findViewById(R.id.menu1);
         mCheckBox1.setOnClickListener(mClickListener);
         mCheckBox2 = (CheckBox)v.findViewById(R.id.menu2);
         mCheckBox2.setOnClickListener(mClickListener);
-        
+
         // Make sure fragments start out with correct visibility.
         updateFragmentVisibility();
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuSupport.java
index f2f5ec1..29a8e52 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuSupport.java
@@ -16,15 +16,11 @@
 
 package com.example.android.supportv4.app;
 
-import com.example.android.supportv4.R;
-
+import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
-import android.support.v4.view.MenuItemCompat;
-
-import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -32,6 +28,8 @@
 import android.view.View.OnClickListener;
 import android.widget.CheckBox;
 
+import com.example.android.supportv4.R;
+
 /**
  * Demonstrates how fragments can participate in the options menu.
  */
@@ -43,6 +41,7 @@
 
     // Update fragment visibility when check boxes are changed.
     final OnClickListener mClickListener = new OnClickListener() {
+        @Override
         public void onClick(View v) {
             updateFragmentVisibility();
         }
@@ -112,9 +111,9 @@
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
             MenuItem item;
             item = menu.add("Menu 1a");
-            MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
             item = menu.add("Menu 1b");
-            MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         }
     }
 
@@ -133,7 +132,7 @@
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
             MenuItem item;
             item = menu.add("Menu 2");
-            MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         }
     }
 }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentPagerSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentPagerSupport.java
index f413744..14688f3 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentPagerSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentPagerSupport.java
@@ -57,12 +57,14 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.goto_first);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(0);
             }
         });
         button = (Button)findViewById(R.id.goto_last);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(NUM_ITEMS-1);
             }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentReceiveResultSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentReceiveResultSupport.java
index 4a8e3a3..5f82bef 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentReceiveResultSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentReceiveResultSupport.java
@@ -62,6 +62,7 @@
         private TextView mResults;
 
         private OnClickListener mGetListener = new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 // Start the activity whose result we want to retrieve.  The
                 // result will come back with request code GET_CODE.
@@ -71,6 +72,7 @@
         };
 
         private OnClickListener mIntentSenderListener = new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 // Start the intent sender whose result we want to retrieve.  The
                 // result will come back with request code GET_INTENT_SENDER_CODE.
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentRetainInstanceSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentRetainInstanceSupport.java
index 0ff34dd..8472aa4 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentRetainInstanceSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentRetainInstanceSupport.java
@@ -63,6 +63,7 @@
             // Watch for button clicks.
             Button button = (Button)v.findViewById(R.id.restart);
             button.setOnClickListener(new OnClickListener() {
+                @Override
                 public void onClick(View v) {
                     mWorkFragment.restart();
                 }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java
index d2eb29a..2c289d7 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java
@@ -53,18 +53,21 @@
         // Watch for button clicks.
         Button button = (Button)v.findViewById(R.id.new_fragment);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 addFragmentToStack();
             }
         });
         button = (Button)v.findViewById(R.id.delete_fragment);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 getChildFragmentManager().popBackStack();
             }
         });
         button = (Button)v.findViewById(R.id.home);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 // If there is a back stack, pop it all.
                 FragmentManager fm = getChildFragmentManager();
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java
index 4115d5e..786c53d 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java
@@ -41,14 +41,16 @@
         setContentView(R.layout.fragment_stack);
 
         // Watch for button clicks.
-        Button button = (Button) findViewById(R.id.new_fragment);
+        Button button = findViewById(R.id.new_fragment);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 addFragmentToStack();
             }
         });
-        button = (Button) findViewById(R.id.home);
+        button = findViewById(R.id.home);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 // If there is a back stack, pop it all.
                 FragmentManager fm = getSupportFragmentManager();
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStatePagerSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStatePagerSupport.java
index 2939b0e..83d9db4 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStatePagerSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStatePagerSupport.java
@@ -58,12 +58,14 @@
         // Watch for button clicks.
         Button button = (Button)findViewById(R.id.goto_first);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(0);
             }
         });
         button = (Button)findViewById(R.id.goto_last);
         button.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mPager.setCurrentItem(NUM_ITEMS-1);
             }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCursorSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCursorSupport.java
index f2f9b3c..868f244 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCursorSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCursorSupport.java
@@ -16,23 +16,17 @@
 
 package com.example.android.supportv4.app;
 
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Contacts.People;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.ListFragment;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v4.widget.SearchViewCompat;
-import android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat;
-import android.support.v4.widget.SearchViewCompat.OnQueryTextListenerCompat;
 import android.support.v4.widget.SimpleCursorAdapter;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.BaseColumns;
-import android.provider.Contacts.People;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
@@ -40,6 +34,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.ListView;
+import android.widget.SearchView;
 
 /**
  * Demonstration of the use of a CursorLoader to load and display contacts
@@ -100,44 +95,44 @@
             // Place an action bar item for searching.
             MenuItem item = menu.add("Search");
             item.setIcon(android.R.drawable.ic_menu_search);
-            MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_ALWAYS
-                    | MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
-            final View searchView = SearchViewCompat.newSearchView(getActivity());
-            if (searchView != null) {
-                SearchViewCompat.setOnQueryTextListener(searchView,
-                        new OnQueryTextListenerCompat() {
-                    @Override
-                    public boolean onQueryTextChange(String newText) {
-                        // Called when the action bar search text has changed.  Update
-                        // the search filter, and restart the loader to do a new query
-                        // with this filter.
-                        String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
-                        // Don't do anything if the filter hasn't actually changed.
-                        // Prevents restarting the loader when restoring state.
-                        if (mCurFilter == null && newFilter == null) {
-                            return true;
-                        }
-                        if (mCurFilter != null && mCurFilter.equals(newFilter)) {
-                            return true;
-                        }
-                        mCurFilter = newFilter;
-                        getLoaderManager().restartLoader(0, null, CursorLoaderListFragment.this);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS
+                    | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
+            final SearchView searchView = new SearchView(getActivity());
+            searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+                @Override
+                public boolean onQueryTextChange(String newText) {
+                    // Called when the action bar search text has changed.  Update
+                    // the search filter, and restart the loader to do a new query
+                    // with this filter.
+                    String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
+                    // Don't do anything if the filter hasn't actually changed.
+                    // Prevents restarting the loader when restoring state.
+                    if (mCurFilter == null && newFilter == null) {
                         return true;
                     }
-                });
-                SearchViewCompat.setOnCloseListener(searchView,
-                        new OnCloseListenerCompat() {
-                            @Override
-                            public boolean onClose() {
-                                if (!TextUtils.isEmpty(SearchViewCompat.getQuery(searchView))) {
-                                    SearchViewCompat.setQuery(searchView, null, true);
-                                }
-                                return true;
-                            }
-                    
-                });
-                MenuItemCompat.setActionView(item, searchView);
-            }
+                    if (mCurFilter != null && mCurFilter.equals(newFilter)) {
+                        return true;
+                    }
+                    mCurFilter = newFilter;
+                    getLoaderManager().restartLoader(0, null, CursorLoaderListFragment.this);
+                    return true;
+                }
+
+                @Override
+                public boolean onQueryTextSubmit(String s) {
+                    return false;
+                }
+            });
+            searchView.setOnCloseListener(new SearchView.OnCloseListener() {
+                @Override
+                public boolean onClose() {
+                    if (!TextUtils.isEmpty(searchView.getQuery())) {
+                        searchView.setQuery(null, true);
+                    }
+                    return true;
+                }
+            });
+            item.setActionView(searchView);
         }
 
         @Override public void onListItemClick(ListView l, View v, int position, long id) {
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
index d9689d2..22267884 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
@@ -33,11 +33,8 @@
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.AsyncTaskLoader;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.content.IntentCompat;
 import android.support.v4.content.Loader;
 import android.support.v4.content.pm.ActivityInfoCompat;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v4.widget.SearchViewCompat;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -49,6 +46,7 @@
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.ListView;
+import android.widget.SearchView;
 import android.widget.TextView;
 
 import com.example.android.supportv4.R;
@@ -193,8 +191,8 @@
             mLoader.getContext().registerReceiver(this, filter);
             // Register for events related to sdcard installation.
             IntentFilter sdFilter = new IntentFilter();
-            sdFilter.addAction(IntentCompat.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-            sdFilter.addAction(IntentCompat.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             mLoader.getContext().registerReceiver(this, sdFilter);
         }
 
@@ -230,6 +228,7 @@
          */
         @Override public List<AppEntry> loadInBackground() {
             // Retrieve all known applications.
+            //noinspection WrongConstant
             List<ApplicationInfo> apps = mPm.getInstalledApplications(
                     PackageManager.MATCH_UNINSTALLED_PACKAGES
                             | PackageManager.MATCH_DISABLED_COMPONENTS);
@@ -436,38 +435,36 @@
             // Place an action bar item for searching.
             MenuItem item = menu.add("Search");
             item.setIcon(android.R.drawable.ic_menu_search);
-            MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM
-                    | MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
-            final View searchView = SearchViewCompat.newSearchView(getActivity());
-            if (searchView != null) {
-                SearchViewCompat.setOnQueryTextListener(searchView,
-                        new SearchViewCompat.OnQueryTextListener() {
-                            @Override
-                            public boolean onQueryTextChange(String newText) {
-                                // Called when the action bar search text has changed.  Since this
-                                // is a simple array adapter, we can just have it do the filtering.
-                                mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
-                                mAdapter.getFilter().filter(mCurFilter);
-                                return true;
-                            }
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
+                    | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
+            final SearchView searchView = new SearchView(getActivity());
+            searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+                @Override
+                public boolean onQueryTextChange(String newText) {
+                    // Called when the action bar search text has changed.  Since this
+                    // is a simple array adapter, we can just have it do the filtering.
+                    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+                    mAdapter.getFilter().filter(mCurFilter);
+                    return true;
+                }
 
-                            @Override
-                            public boolean onQueryTextSubmit(String query) {
-                                return false;
-                            }
-                        });
-                SearchViewCompat.setOnCloseListener(searchView,
-                        new SearchViewCompat.OnCloseListener() {
-                            @Override
-                            public boolean onClose() {
-                                if (!TextUtils.isEmpty(SearchViewCompat.getQuery(searchView))) {
-                                    SearchViewCompat.setQuery(searchView, null, true);
-                                }
-                                return true;
-                            }
-                        });
-                MenuItemCompat.setActionView(item, searchView);
-            }
+                @Override
+                public boolean onQueryTextSubmit(String query) {
+                    return false;
+                }
+            });
+
+            searchView.setOnCloseListener(new SearchView.OnCloseListener() {
+                @Override
+                public boolean onClose() {
+                    if (!TextUtils.isEmpty(searchView.getQuery())) {
+                        searchView.setQuery(null, true);
+                    }
+                    return true;
+                }
+            });
+
+            item.setActionView(searchView);
         }
 
         @Override public void onListItemClick(ListView l, View v, int position, long id) {
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderRetainedSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderRetainedSupport.java
index 8ea47e3..a6dd264 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderRetainedSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderRetainedSupport.java
@@ -16,22 +16,17 @@
 
 package com.example.android.supportv4.app;
 
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Contacts.People;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.ListFragment;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v4.widget.SearchViewCompat;
-import android.support.v4.widget.SearchViewCompat.OnQueryTextListenerCompat;
 import android.support.v4.widget.SimpleCursorAdapter;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.BaseColumns;
-import android.provider.Contacts.People;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
@@ -39,6 +34,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.ListView;
+import android.widget.SearchView;
 
 /**
  * Demonstration of the use of a CursorLoader to load and display contacts
@@ -102,33 +98,35 @@
             // Place an action bar item for searching.
             MenuItem item = menu.add("Search");
             item.setIcon(android.R.drawable.ic_menu_search);
-            MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_ALWAYS
-                    | MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
-            View searchView = SearchViewCompat.newSearchView(getActivity());
-            if (searchView != null) {
-                SearchViewCompat.setOnQueryTextListener(searchView,
-                        new OnQueryTextListenerCompat() {
-                    @Override
-                    public boolean onQueryTextChange(String newText) {
-                        // Called when the action bar search text has changed.  Update
-                        // the search filter, and restart the loader to do a new query
-                        // with this filter.
-                        String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
-                        // Don't do anything if the filter hasn't actually changed.
-                        // Prevents restarting the loader when restoring state.
-                        if (mCurFilter == null && newFilter == null) {
-                            return true;
-                        }
-                        if (mCurFilter != null && mCurFilter.equals(newFilter)) {
-                            return true;
-                        }
-                        mCurFilter = newFilter;
-                        getLoaderManager().restartLoader(0, null, CursorLoaderListFragment.this);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS
+                    | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
+            SearchView searchView = new SearchView(getActivity());
+            searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+                @Override
+                public boolean onQueryTextChange(String newText) {
+                    // Called when the action bar search text has changed.  Update
+                    // the search filter, and restart the loader to do a new query
+                    // with this filter.
+                    String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
+                    // Don't do anything if the filter hasn't actually changed.
+                    // Prevents restarting the loader when restoring state.
+                    if (mCurFilter == null && newFilter == null) {
                         return true;
                     }
-                });
-                MenuItemCompat.setActionView(item, searchView);
-            }
+                    if (mCurFilter != null && mCurFilter.equals(newFilter)) {
+                        return true;
+                    }
+                    mCurFilter = newFilter;
+                    getLoaderManager().restartLoader(0, null, CursorLoaderListFragment.this);
+                    return true;
+                }
+
+                @Override
+                public boolean onQueryTextSubmit(String s) {
+                    return false;
+                }
+            });
+            item.setActionView(searchView);
         }
 
         @Override public void onListItemClick(ListView l, View v, int position, long id) {
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderThrottleSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderThrottleSupport.java
index a1fb2c7..a03a690 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderThrottleSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderThrottleSupport.java
@@ -17,6 +17,7 @@
 package com.example.android.supportv4.app;
 
 //BEGIN_INCLUDE(complete)
+
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -39,7 +40,6 @@
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
 import android.support.v4.database.DatabaseUtilsCompat;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v4.widget.SimpleCursorAdapter;
 import android.text.TextUtils;
 import android.util.Log;
@@ -420,9 +420,9 @@
 
         @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
             MenuItem populateItem = menu.add(Menu.NONE, POPULATE_ID, 0, "Populate");
-            MenuItemCompat.setShowAsAction(populateItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+            populateItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
             MenuItem clearItem = menu.add(Menu.NONE, CLEAR_ID, 0, "Clear");
-            MenuItemCompat.setShowAsAction(clearItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+            clearItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         }
 
         @Override public boolean onOptionsItemSelected(MenuItem item) {
@@ -486,6 +486,7 @@
             MainTable.COLUMN_NAME_DATA,
         };
 
+        @Override
         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
             CursorLoader cl = new CursorLoader(getActivity(), MainTable.CONTENT_URI,
                     PROJECTION, null, null, null);
@@ -493,6 +494,7 @@
             return cl;
         }
 
+        @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
             mAdapter.swapCursor(data);
 
@@ -504,6 +506,7 @@
             }
         }
 
+        @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             mAdapter.swapCursor(null);
         }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/SendResult.java b/samples/Support4Demos/src/com/example/android/supportv4/app/SendResult.java
index 7179505..6495f1f 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/SendResult.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/SendResult.java
@@ -58,6 +58,7 @@
 
     private OnClickListener mCorkyListener = new OnClickListener()
     {
+        @Override
         public void onClick(View v)
         {
             // To send a result, simply call setResult() before your
@@ -69,6 +70,7 @@
 
     private OnClickListener mVioletListener = new OnClickListener()
     {
+        @Override
         public void onClick(View v)
         {
             // To send a result, simply call setResult() before your
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/SharingReceiverSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/SharingReceiverSupport.java
index d1efa2d..e505868 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/SharingReceiverSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/SharingReceiverSupport.java
@@ -51,13 +51,13 @@
         ShareCompat.IntentReader intentReader = ShareCompat.IntentReader.from(this);
 
         // The following provides attribution for the app that shared the data with us.
-        TextView info = (TextView) findViewById(R.id.app_info);
+        TextView info = findViewById(R.id.app_info);
         Drawable d = intentReader.getCallingActivityIcon();
         d.setBounds(0, 0, iconSize, iconSize);
         info.setCompoundDrawables(d, null, null, null);
         info.setText(intentReader.getCallingApplicationLabel());
 
-        TextView tv = (TextView) findViewById(R.id.text);
+        TextView tv = findViewById(R.id.text);
         StringBuilder txt = new StringBuilder("Received share!\nText was: ");
 
         txt.append(intentReader.getText());
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/SharingSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/SharingSupport.java
index ec099a8..4fafd2d 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/SharingSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/SharingSupport.java
@@ -16,18 +16,17 @@
 
 package com.example.android.supportv4.app;
 
-import com.example.android.supportv4.R;
-import com.example.android.supportv4.content.SharingSupportProvider;
-
 import android.app.Activity;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.ShareCompat;
-import android.support.v4.view.MenuItemCompat;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 
+import com.example.android.supportv4.R;
+import com.example.android.supportv4.content.SharingSupportProvider;
+
 import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -57,7 +56,7 @@
         b.setType("text/plain").setText("Share from menu");
         MenuItem item = menu.add("Share");
         ShareCompat.configureMenuItem(item, b);
-        MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         return true;
     }
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentController.java b/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentController.java
new file mode 100644
index 0000000..4681f7c
--- /dev/null
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentController.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.example.android.supportv4.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import com.example.android.supportv4.R;
+
+/**
+ * UI controller for SimpleJobIntentService.
+ */
+public class SimpleJobIntentController extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.simple_job_intent_controller);
+
+        // Watch for button clicks.
+        findViewById(R.id.worka).setOnClickListener(new WorkListener("ACTION_A",
+                R.string.schedule_work_a));
+        findViewById(R.id.workb).setOnClickListener(new WorkListener("ACTION_B",
+                R.string.schedule_work_b));
+        findViewById(R.id.workc).setOnClickListener(new WorkListener("ACTION_C",
+                R.string.schedule_work_c));
+    }
+
+    class WorkListener implements View.OnClickListener {
+        final String mAction;
+        final String mLabel;
+
+        WorkListener(String action, int label) {
+            mAction = action;
+            mLabel = getText(label).toString();
+        }
+
+        @Override
+        public void onClick(View view) {
+            Intent intent = new Intent(mAction);
+            intent.putExtra("label", mLabel);
+            SimpleJobIntentService.enqueueWork(SimpleJobIntentController.this, intent);
+        }
+    }
+}
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentService.java b/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentService.java
new file mode 100644
index 0000000..7e7fb41
--- /dev/null
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/SimpleJobIntentService.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.example.android.supportv4.app;
+
+//BEGIN_INCLUDE(complete)
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.support.v4.app.JobIntentService;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Example implementation of a JobIntentService.
+ */
+public class SimpleJobIntentService extends JobIntentService {
+    /**
+     * Unique job ID for this service.
+     */
+    static final int JOB_ID = 1000;
+
+    /**
+     * Convenience method for enqueuing work in to this service.
+     */
+    static void enqueueWork(Context context, Intent work) {
+        enqueueWork(context, SimpleJobIntentService.class, JOB_ID, work);
+    }
+
+    @Override
+    protected void onHandleWork(Intent intent) {
+        // We have received work to do.  The system or framework is already
+        // holding a wake lock for us at this point, so we can just go.
+        Log.i("SimpleJobIntentService", "Executing work: " + intent);
+        String label = intent.getStringExtra("label");
+        if (label == null) {
+            label = intent.toString();
+        }
+        toast("Executing: " + label);
+        for (int i = 0; i < 5; i++) {
+            Log.i("SimpleJobIntentService", "Running service " + (i + 1)
+                    + "/5 @ " + SystemClock.elapsedRealtime());
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+        }
+        Log.i("SimpleJobIntentService", "Completed service @ " + SystemClock.elapsedRealtime());
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        toast("All work complete");
+    }
+
+    final Handler mHandler = new Handler();
+
+    // Helper for showing tests
+    void toast(final CharSequence text) {
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                Toast.makeText(SimpleJobIntentService.this, text, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+}
+//END_INCLUDE(complete)
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.java b/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.java
index 62a320c..feb5734 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.java
@@ -95,12 +95,14 @@
     }
 
     private OnClickListener mStartListener = new OnClickListener() {
+        @Override
         public void onClick(View v) {
             startService(new Intent(LocalServiceBroadcaster.this, LocalService.class));
         }
     };
 
     private OnClickListener mStopListener = new OnClickListener() {
+        @Override
         public void onClick(View v) {
             stopService(new Intent(LocalServiceBroadcaster.this, LocalService.class));
         }
@@ -136,6 +138,7 @@
             mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
         }
 
+        @Override
         public int onStartCommand(Intent intent, int flags, int startId) {
             // Tell any local interested parties about the start.
             mLocalBroadcastManager.sendBroadcast(new Intent(ACTION_STARTED));
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulController.java b/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulController.java
index bf3b2c0..78f46f2 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulController.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulController.java
@@ -44,6 +44,7 @@
     }
 
     private View.OnClickListener mScheduleListener = new View.OnClickListener() {
+        @Override
         public void onClick(View v) {
             // When the alarm goes off, we want to broadcast an Intent to our
             // BroadcastReceiver.  Here we make an Intent with an explicit class
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/graphics/DrawableCompatActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/graphics/DrawableCompatActivity.java
index f679067..1a0934a 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/graphics/DrawableCompatActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/graphics/DrawableCompatActivity.java
@@ -42,14 +42,14 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.drawable_compat);
 
-        mImageView = (ImageView) findViewById(R.id.image);
+        mImageView = findViewById(R.id.image);
 
         Drawable d = ContextCompat.getDrawable(this, IMAGE_RES);
         mDrawable = DrawableCompat.wrap(d.mutate());
 
         mImageView.setImageDrawable(mDrawable);
 
-        RadioGroup rg = (RadioGroup) findViewById(R.id.drawable_compat_options);
+        RadioGroup rg = findViewById(R.id.drawable_compat_options);
         rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(RadioGroup radioGroup, int id) {
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/graphics/RoundedBitmapDrawableActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/graphics/RoundedBitmapDrawableActivity.java
index d89d462..8f54a59 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/graphics/RoundedBitmapDrawableActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/graphics/RoundedBitmapDrawableActivity.java
@@ -46,8 +46,8 @@
         mRoundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), bitmap);
 
         // Get references to the inflated views.
-        ToggleButton toggle = (ToggleButton) findViewById(R.id.toggle_round);
-        ImageView image = (ImageView) findViewById(R.id.image);
+        ToggleButton toggle = findViewById(R.id.toggle_round);
+        ImageView image = findViewById(R.id.image);
 
         // Set up initial view state and on checked change listener.
         image.setImageDrawable(mRoundedBitmapDrawable);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java b/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java
index 630b5d5..22a1e7b 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java
@@ -18,8 +18,9 @@
 
 import android.graphics.Bitmap;
 import android.os.AsyncTask;
+import android.support.v4.graphics.BitmapCompat;
+import android.support.v4.util.LruCache;
 import android.util.Log;
-import android.util.LruCache;
 
 import com.example.android.supportv4.media.utils.BitmapHelper;
 
@@ -61,8 +62,8 @@
         mCache = new LruCache<String, Bitmap[]>(maxSize) {
             @Override
             protected int sizeOf(String key, Bitmap[] value) {
-                return value[BIG_BITMAP_INDEX].getByteCount()
-                    + value[ICON_BITMAP_INDEX].getByteCount();
+                return BitmapCompat.getAllocationByteCount(value[BIG_BITMAP_INDEX])
+                        + BitmapCompat.getAllocationByteCount(value[ICON_BITMAP_INDEX]);
             }
         };
     }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java b/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
index d23ce05..6d6ab8e 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
@@ -15,11 +15,11 @@
  */
 package com.example.android.supportv4.media;
 
-import android.app.Fragment;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.support.v4.app.Fragment;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.session.MediaControllerCompat;
@@ -164,7 +164,7 @@
         @Override
         public void onConnectionSuspended() {
             Log.d(TAG, "onConnectionSuspended");
-            getActivity().setMediaController(null);
+            ((MediaBrowserSupport) getActivity()).setMediaController((MediaControllerCompat) null);
         }
     };
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserServiceSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserServiceSupport.java
index 035c62a..cb78006 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserServiceSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserServiceSupport.java
@@ -16,6 +16,10 @@
 
 package com.example.android.supportv4.media;
 
+import static com.example.android.supportv4.media.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+import static com.example.android.supportv4.media.utils.MediaIDHelper.MEDIA_ID_ROOT;
+import static com.example.android.supportv4.media.utils.MediaIDHelper.createBrowseCategoryMediaID;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -26,10 +30,10 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.support.v4.media.MediaBrowserCompat;
-import android.support.v4.media.MediaDescriptionCompat;
-import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.MediaBrowserCompat.MediaItem;
 import android.support.v4.media.MediaBrowserServiceCompat;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.session.MediaButtonReceiver;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.media.session.PlaybackStateCompat;
@@ -47,10 +51,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static com.example.android.supportv4.media.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
-import static com.example.android.supportv4.media.utils.MediaIDHelper.MEDIA_ID_ROOT;
-import static com.example.android.supportv4.media.utils.MediaIDHelper.createBrowseCategoryMediaID;
-
 /**
  * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
  * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
@@ -119,8 +119,8 @@
     // A value of a CMD_NAME key in the extras of the incoming Intent that
     // indicates that the music playback should be paused (see {@link #onStartCommand})
     public static final String CMD_PAUSE = "CMD_PAUSE";
-
-    private static final String TAG = "SampleMediaBrowserService";
+    // Log tag must be <= 23 characters, so truncate class name.
+    private static final String TAG = "MediaBrowserService";
     // Action to thumbs up a media item
     private static final String CUSTOM_ACTION_THUMBS_UP =
             "com.example.android.supportv4.media.THUMBS_UP";
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java
index 6460318..27a5e12 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java
@@ -16,24 +16,25 @@
 
 package com.example.android.supportv4.media;
 
-import com.example.android.supportv4.R;
-import android.app.Activity;
 import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
 import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.session.MediaControllerCompat;
 
+import com.example.android.supportv4.R;
+
 /**
  * Main activity for the music player.
  */
-public class MediaBrowserSupport extends Activity implements BrowseFragment.FragmentDataHelper {
-    private MediaControllerCompat mMediaController;
+public class MediaBrowserSupport extends FragmentActivity
+        implements BrowseFragment.FragmentDataHelper {
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_player);
         if (savedInstanceState == null) {
-            getFragmentManager().beginTransaction()
+            getSupportFragmentManager().beginTransaction()
                     .add(R.id.container, BrowseFragment.newInstance(null))
                     .commit();
         }
@@ -42,14 +43,17 @@
     @Override
     public void onMediaItemSelected(MediaBrowserCompat.MediaItem item) {
         if (item.isPlayable()) {
-            mMediaController.getTransportControls().playFromMediaId(item.getMediaId(), null);
-            QueueFragment queueFragment = QueueFragment.newInstance();
-            getFragmentManager().beginTransaction()
-                    .replace(R.id.container, queueFragment)
-                    .addToBackStack(null)
-                    .commit();
+            MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(this);
+            if (mediaController != null) {
+                mediaController.getTransportControls().playFromMediaId(item.getMediaId(), null);
+                QueueFragment queueFragment = QueueFragment.newInstance();
+                getSupportFragmentManager().beginTransaction()
+                        .replace(R.id.container, queueFragment)
+                        .addToBackStack(null)
+                        .commit();
+            }
         } else if (item.isBrowsable()) {
-            getFragmentManager().beginTransaction()
+            getSupportFragmentManager().beginTransaction()
                     .replace(R.id.container, BrowseFragment.newInstance(item.getMediaId()))
                     .addToBackStack(null)
                     .commit();
@@ -57,6 +61,6 @@
     }
 
     public void setMediaController(MediaControllerCompat mediaController) {
-        mMediaController = mediaController;
+        MediaControllerCompat.setMediaController(this, mediaController);
     }
 }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java
deleted file mode 100644
index b8d99d4..0000000
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2013 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 com.example.android.supportv4.media;
-
-import android.support.v4.media.TransportController;
-import android.support.v4.media.TransportMediator;
-import android.support.v4.media.TransportStateListener;
-import com.example.android.supportv4.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import java.util.Formatter;
-import java.util.Locale;
-
-/**
- * Helper for implementing media controls in an application.
- * Use instead of the very useful android.widget.MediaController.
- * This version is embedded inside of an application's layout.
- */
-public class MediaController extends FrameLayout {
-
-    private TransportController mController;
-    private Context mContext;
-    private ProgressBar mProgress;
-    private TextView mEndTime, mCurrentTime;
-    private boolean mDragging;
-    private boolean mUseFastForward;
-    private boolean mListenersSet;
-    private boolean mShowNext, mShowPrev;
-    private View.OnClickListener mNextListener, mPrevListener;
-    StringBuilder mFormatBuilder;
-    Formatter mFormatter;
-    private ImageButton mPauseButton;
-    private ImageButton mFfwdButton;
-    private ImageButton mRewButton;
-    private ImageButton mNextButton;
-    private ImageButton mPrevButton;
-
-    private TransportStateListener mStateListener = new TransportStateListener() {
-        @Override
-        public void onPlayingChanged(TransportController controller) {
-            updatePausePlay();
-        }
-        @Override
-        public void onTransportControlsChanged(TransportController controller) {
-            updateButtons();
-        }
-    };
-
-    public MediaController(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        mUseFastForward = true;
-        LayoutInflater inflate = (LayoutInflater)
-                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        inflate.inflate(R.layout.media_controller, this, true);
-        initControllerView();
-    }
-
-    public MediaController(Context context, boolean useFastForward) {
-        super(context);
-        mContext = context;
-        mUseFastForward = useFastForward;
-    }
-
-    public MediaController(Context context) {
-        this(context, true);
-    }
-
-    public void setMediaPlayer(TransportController controller) {
-        if (getWindowToken() != null) {
-            if (mController != null) {
-                mController.unregisterStateListener(mStateListener);
-            }
-            if (controller != null) {
-                controller.registerStateListener(mStateListener);
-            }
-        }
-        mController = controller;
-        updatePausePlay();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (mController != null) {
-            mController.registerStateListener(mStateListener);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mController != null) {
-            mController.unregisterStateListener(mStateListener);
-        }
-    }
-
-    private void initControllerView() {
-        mPauseButton = (ImageButton) findViewById(R.id.pause);
-        if (mPauseButton != null) {
-            mPauseButton.requestFocus();
-            mPauseButton.setOnClickListener(mPauseListener);
-        }
-
-        mFfwdButton = (ImageButton) findViewById(R.id.ffwd);
-        if (mFfwdButton != null) {
-            mFfwdButton.setOnClickListener(mFfwdListener);
-            mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
-        }
-
-        mRewButton = (ImageButton) findViewById(R.id.rew);
-        if (mRewButton != null) {
-            mRewButton.setOnClickListener(mRewListener);
-            mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
-        }
-
-        // By default these are hidden. They will be enabled when setPrevNextListeners() is called
-        mNextButton = (ImageButton) findViewById(R.id.next);
-        if (mNextButton != null && !mListenersSet) {
-            mNextButton.setVisibility(View.GONE);
-        }
-        mPrevButton = (ImageButton) findViewById(R.id.prev);
-        if (mPrevButton != null && !mListenersSet) {
-            mPrevButton.setVisibility(View.GONE);
-        }
-
-        mProgress = (ProgressBar) findViewById(R.id.mediacontroller_progress);
-        if (mProgress != null) {
-            if (mProgress instanceof SeekBar) {
-                SeekBar seeker = (SeekBar) mProgress;
-                seeker.setOnSeekBarChangeListener(mSeekListener);
-            }
-            mProgress.setMax(1000);
-        }
-
-        mEndTime = (TextView) findViewById(R.id.time);
-        mCurrentTime = (TextView) findViewById(R.id.time_current);
-        mFormatBuilder = new StringBuilder();
-        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
-
-        installPrevNextListeners();
-    }
-
-    /**
-     * Disable pause or seek buttons if the stream cannot be paused or seeked.
-     * This requires the control interface to be a MediaPlayerControlExt
-     */
-    void updateButtons() {
-        int flags = mController.getTransportControlFlags();
-        boolean enabled = isEnabled();
-        if (mPauseButton != null) {
-            mPauseButton.setEnabled(enabled && (flags&TransportMediator.FLAG_KEY_MEDIA_PAUSE) != 0);
-        }
-        if (mRewButton != null) {
-            mRewButton.setEnabled(enabled && (flags&TransportMediator.FLAG_KEY_MEDIA_REWIND) != 0);
-        }
-        if (mFfwdButton != null) {
-            mFfwdButton.setEnabled(enabled &&
-                    (flags&TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD) != 0);
-        }
-        if (mPrevButton != null) {
-            mShowPrev = (flags&TransportMediator.FLAG_KEY_MEDIA_PREVIOUS) != 0
-                    || mPrevListener != null;
-            mPrevButton.setEnabled(enabled && mShowPrev);
-        }
-        if (mNextButton != null) {
-            mShowNext = (flags&TransportMediator.FLAG_KEY_MEDIA_NEXT) != 0
-                    || mNextListener != null;
-            mNextButton.setEnabled(enabled && mShowNext);
-        }
-    }
-
-    public void refresh() {
-        updateProgress();
-        updateButtons();
-        updatePausePlay();
-    }
-
-    private String stringForTime(int timeMs) {
-        int totalSeconds = timeMs / 1000;
-
-        int seconds = totalSeconds % 60;
-        int minutes = (totalSeconds / 60) % 60;
-        int hours   = totalSeconds / 3600;
-
-        mFormatBuilder.setLength(0);
-        if (hours > 0) {
-            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
-        } else {
-            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
-        }
-    }
-
-    public long updateProgress() {
-        if (mController == null || mDragging) {
-            return 0;
-        }
-        long position = mController.getCurrentPosition();
-        long duration = mController.getDuration();
-        if (mProgress != null) {
-            if (duration > 0) {
-                // use long to avoid overflow
-                long pos = 1000L * position / duration;
-                mProgress.setProgress( (int) pos);
-            }
-            int percent = mController.getBufferPercentage();
-            mProgress.setSecondaryProgress(percent * 10);
-        }
-
-        if (mEndTime != null)
-            mEndTime.setText(stringForTime((int)duration));
-        if (mCurrentTime != null)
-            mCurrentTime.setText(stringForTime((int)position));
-
-        return position;
-    }
-
-    private View.OnClickListener mPauseListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            doPauseResume();
-        }
-    };
-
-    private void updatePausePlay() {
-        if (mPauseButton == null)
-            return;
-
-        if (mController.isPlaying()) {
-            mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
-        } else {
-            mPauseButton.setImageResource(android.R.drawable.ic_media_play);
-        }
-    }
-
-    private void doPauseResume() {
-        if (mController.isPlaying()) {
-            mController.pausePlaying();
-        } else {
-            mController.startPlaying();
-        }
-        updatePausePlay();
-    }
-
-    // There are two scenarios that can trigger the seekbar listener to trigger:
-    //
-    // The first is the user using the touchpad to adjust the posititon of the
-    // seekbar's thumb. In this case onStartTrackingTouch is called followed by
-    // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
-    // We're setting the field "mDragging" to true for the duration of the dragging
-    // session to avoid jumps in the position in case of ongoing playback.
-    //
-    // The second scenario involves the user operating the scroll ball, in this
-    // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
-    // we will simply apply the updated position without suspending regular updates.
-    private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {
-        public void onStartTrackingTouch(SeekBar bar) {
-            mDragging = true;
-        }
-
-        public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
-            if (!fromuser) {
-                // We're not interested in programmatically generated changes to
-                // the progress bar's position.
-                return;
-            }
-
-            long duration = mController.getDuration();
-            long newposition = (duration * progress) / 1000L;
-            mController.seekTo((int) newposition);
-            if (mCurrentTime != null)
-                mCurrentTime.setText(stringForTime( (int) newposition));
-        }
-
-        public void onStopTrackingTouch(SeekBar bar) {
-            mDragging = false;
-            updateProgress();
-            updatePausePlay();
-        }
-    };
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled);
-        updateButtons();
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setClassName(MediaController.class.getName());
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(MediaController.class.getName());
-    }
-
-    private View.OnClickListener mRewListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            long pos = mController.getCurrentPosition();
-            pos -= 5000; // milliseconds
-            mController.seekTo(pos);
-            updateProgress();
-        }
-    };
-
-    private View.OnClickListener mFfwdListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            long pos = mController.getCurrentPosition();
-            pos += 15000; // milliseconds
-            mController.seekTo(pos);
-            updateProgress();
-        }
-    };
-
-    private void installPrevNextListeners() {
-        if (mNextButton != null) {
-            mNextButton.setOnClickListener(mNextListener);
-            mNextButton.setEnabled(mShowNext);
-        }
-
-        if (mPrevButton != null) {
-            mPrevButton.setOnClickListener(mPrevListener);
-            mPrevButton.setEnabled(mShowPrev);
-        }
-    }
-
-    public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
-        mNextListener = next;
-        mPrevListener = prev;
-        mListenersSet = true;
-
-        installPrevNextListeners();
-
-        if (mNextButton != null) {
-            mNextButton.setVisibility(View.VISIBLE);
-            mShowNext = true;
-        }
-        if (mPrevButton != null) {
-            mPrevButton.setVisibility(View.VISIBLE);
-            mShowPrev = true;
-        }
-    }
-}
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java
index 3d327ff..80c53f6 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java
@@ -52,6 +52,7 @@
         TextView mDescriptionView;
     }
 
+    @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         ViewHolder holder;
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
index 6ec5477..e27981f 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
@@ -16,10 +16,10 @@
 
 package com.example.android.supportv4.media;
 
-import android.app.Fragment;
 import android.content.ComponentName;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.support.v4.app.Fragment;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.session.MediaControllerCompat;
@@ -98,7 +98,7 @@
             mMediaController.unregisterCallback(mSessionCallback);
             mTransportControls = null;
             mMediaController = null;
-            getActivity().setMediaController(null);
+            ((MediaBrowserSupport) getActivity()).setMediaController((MediaControllerCompat) null);
         }
     };
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/TransportControllerActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/media/TransportControllerActivity.java
deleted file mode 100644
index abb3c97..0000000
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/TransportControllerActivity.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2013 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 com.example.android.supportv4.media;
-
-//BEGIN_INCLUDE(complete)
-import android.support.v4.media.TransportMediator;
-import android.support.v4.media.TransportPerformer;
-import com.example.android.supportv4.R;
-
-import android.app.ActionBar;
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-
-import android.app.Activity;
-import android.net.Uri;
-import android.os.Bundle;
-import android.widget.VideoView;
-
-public class TransportControllerActivity extends Activity {
-
-    /**
-     * TODO: Set the path variable to a streaming video URL or a local media
-     * file path.
-     */
-    private Content mContent;
-    private TransportMediator mTransportMediator;
-    private MediaController mMediaController;
-
-    /**
-     * Handle actions from on-screen media controls.  Most of these are simple re-directs
-     * to the VideoView; some we need to capture to update our state.
-     */
-    TransportPerformer mTransportPerformer = new TransportPerformer() {
-        @Override public void onStart() {
-            mContent.start();
-        }
-
-        @Override public void onStop() {
-            mContent.pause();
-        }
-
-        @Override public void onPause() {
-            mContent.pause();
-        }
-
-        @Override public long onGetDuration() {
-            return mContent.getDuration();
-        }
-
-        @Override public long onGetCurrentPosition() {
-            return mContent.getCurrentPosition();
-        }
-
-        @Override public void onSeekTo(long pos) {
-            mContent.seekTo((int)pos);
-        }
-
-        @Override public boolean onIsPlaying() {
-            return mContent.isPlaying();
-        }
-
-        @Override public int onGetBufferPercentage() {
-            return mContent.getBufferPercentage();
-        }
-
-        @Override public int onGetTransportControlFlags() {
-            int flags = TransportMediator.FLAG_KEY_MEDIA_PLAY
-                    | TransportMediator.FLAG_KEY_MEDIA_PLAY_PAUSE
-                    | TransportMediator.FLAG_KEY_MEDIA_STOP;
-            if (mContent.canPause()) {
-                flags |= TransportMediator.FLAG_KEY_MEDIA_PAUSE;
-            }
-            if (mContent.canSeekBackward()) {
-                flags |= TransportMediator.FLAG_KEY_MEDIA_REWIND;
-            }
-            if (mContent.canSeekForward()) {
-                flags |= TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD;
-            }
-            return flags;
-        }
-    };
-
-    /**
-     * This is the actual video player.  It is the top-level content of
-     * the activity's view hierarchy, going under the status bar and nav
-     * bar areas.
-     */
-    public static class Content extends VideoView implements
-            View.OnSystemUiVisibilityChangeListener, View.OnClickListener,
-            ActionBar.OnMenuVisibilityListener, MediaPlayer.OnPreparedListener,
-            MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
-        Activity mActivity;
-        TransportMediator mTransportMediator;
-        MediaController mMediaController;
-        boolean mAddedMenuListener;
-        boolean mMenusOpen;
-        boolean mPaused;
-        boolean mNavVisible;
-        int mLastSystemUiVis;
-
-        Runnable mNavHider = new Runnable() {
-            @Override public void run() {
-                setNavVisibility(false);
-            }
-        };
-
-        Runnable mProgressUpdater = new Runnable() {
-            @Override public void run() {
-                mMediaController.updateProgress();
-                getHandler().postDelayed(this, 1000);
-            }
-        };
-
-        public Content(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            setOnSystemUiVisibilityChangeListener(this);
-            setOnClickListener(this);
-            setOnPreparedListener(this);
-            setOnCompletionListener(this);
-            setOnErrorListener(this);
-        }
-
-        public void init(Activity activity, TransportMediator transportMediator,
-                MediaController mediaController) {
-            // This called by the containing activity to supply the surrounding
-            // state of the video player that it will interact with.
-            mActivity = activity;
-            mTransportMediator = transportMediator;
-            mMediaController = mediaController;
-            pause();
-        }
-
-        @Override protected void onAttachedToWindow() {
-            super.onAttachedToWindow();
-            if (mActivity != null) {
-                mAddedMenuListener = true;
-                mActivity.getActionBar().addOnMenuVisibilityListener(this);
-            }
-        }
-
-        @Override protected void onDetachedFromWindow() {
-            super.onDetachedFromWindow();
-            if (mAddedMenuListener) {
-                mActivity.getActionBar().removeOnMenuVisibilityListener(this);
-            }
-            mNavVisible = false;
-        }
-
-        @Override public void onSystemUiVisibilityChange(int visibility) {
-            // Detect when we go out of nav-hidden mode, to clear our state
-            // back to having the full UI chrome up.  Only do this when
-            // the state is changing and nav is no longer hidden.
-            int diff = mLastSystemUiVis ^ visibility;
-            mLastSystemUiVis = visibility;
-            if ((diff&SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
-                    && (visibility&SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
-                setNavVisibility(true);
-            }
-        }
-
-        @Override protected void onWindowVisibilityChanged(int visibility) {
-            super.onWindowVisibilityChanged(visibility);
-
-            // When we become visible or invisible, play is paused.
-            pause();
-        }
-
-        @Override public void onClick(View v) {
-            // Clicking anywhere makes the navigation visible.
-            setNavVisibility(true);
-        }
-
-        @Override public void onMenuVisibilityChanged(boolean isVisible) {
-            mMenusOpen = isVisible;
-            setNavVisibility(true);
-        }
-
-        @Override
-        public void onPrepared(MediaPlayer mp) {
-            mMediaController.setEnabled(true);
-        }
-
-        @Override
-        public void onCompletion(MediaPlayer mp) {
-            mTransportMediator.pausePlaying();
-            pause();
-        }
-
-        @Override
-        public boolean onError(MediaPlayer mp, int what, int extra) {
-            mTransportMediator.pausePlaying();
-            pause();
-            return false;
-        }
-
-        @Override public void start() {
-            super.start();
-            mPaused = false;
-            setKeepScreenOn(true);
-            setNavVisibility(true);
-            mMediaController.refresh();
-            scheduleProgressUpdater();
-        }
-
-        @Override public void pause() {
-            super.pause();
-            mPaused = true;
-            setKeepScreenOn(false);
-            setNavVisibility(true);
-            mMediaController.refresh();
-            scheduleProgressUpdater();
-        }
-
-        void scheduleProgressUpdater() {
-            Handler h = getHandler();
-            if (h != null) {
-                if (mNavVisible && !mPaused) {
-                    h.removeCallbacks(mProgressUpdater);
-                    h.post(mProgressUpdater);
-                } else {
-                    h.removeCallbacks(mProgressUpdater);
-                }
-            }
-        }
-
-        void setNavVisibility(boolean visible) {
-            int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
-                    | SYSTEM_UI_FLAG_LAYOUT_STABLE;
-            if (!visible) {
-                newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
-                        | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-            }
-
-            // If we are now visible, schedule a timer for us to go invisible.
-            if (visible) {
-                Handler h = getHandler();
-                if (h != null) {
-                    h.removeCallbacks(mNavHider);
-                    if (!mMenusOpen && !mPaused) {
-                        // If the menus are open or play is paused, we will not auto-hide.
-                        h.postDelayed(mNavHider, 3000);
-                    }
-                }
-            }
-
-            // Set the new desired visibility.
-            setSystemUiVisibility(newVis);
-            mNavVisible = visible;
-            mMediaController.setVisibility(visible ? VISIBLE : INVISIBLE);
-            scheduleProgressUpdater();
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        setContentView(R.layout.videoview);
-
-        // Find the video player in our UI.
-        mContent = (Content) findViewById(R.id.content);
-
-        // Create transport controller to control video, giving the callback
-        // interface to receive actions from.
-        mTransportMediator = new TransportMediator(this, mTransportPerformer);
-
-        // Create and initialize the media control UI.
-        mMediaController = (MediaController) findViewById(R.id.media_controller);
-        mMediaController.setMediaPlayer(mTransportMediator);
-
-        // We're just playing a built-in demo video.
-        mContent.init(this, mTransportMediator, mMediaController);
-        mContent.setVideoURI(Uri.parse("android.resource://" + getPackageName() +
-                "/" + R.raw.videoviewdemo));
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        // We first dispatch keys to the transport controller -- we want it
-        // to get to consume any media keys rather than letting whoever has focus
-        // in the view hierarchy to potentially eat it.
-        if (mTransportMediator.dispatchKeyEvent(event)) {
-            return true;
-        }
-
-        return super.dispatchKeyEvent(event);
-    }
-}
-//END_INCLUDE(complete)
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/text/BidiFormatterSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/text/BidiFormatterSupport.java
index 168a8b6..308be31 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/text/BidiFormatterSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/text/BidiFormatterSupport.java
@@ -39,10 +39,10 @@
 
         String formattedText = String.format(text, phone);
 
-        TextView tv_sample = (TextView) findViewById(R.id.textview_without_bidiformatter);
+        TextView tv_sample = findViewById(R.id.textview_without_bidiformatter);
         tv_sample.setText(formattedText);
 
-        TextView tv_bidiformatter = (TextView) findViewById(R.id.textview_with_bidiformatter);
+        TextView tv_bidiformatter = findViewById(R.id.textview_with_bidiformatter);
         String wrappedPhone = BidiFormatter.getInstance(true /* rtlContext */).unicodeWrap(phone);
         formattedText = String.format(text, wrappedPhone);
         tv_bidiformatter.setText(formattedText);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java b/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java
index 1ca8840..6fc6b39 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java
@@ -34,15 +34,18 @@
         super(context, attrs);
     }
 
+    @Override
     public void setChecked(boolean checked) {
         mChecked = checked;
         ViewCompat.setBackground(this, checked ? new ColorDrawable(0xff0000a0) : null);
     }
 
+    @Override
     public boolean isChecked() {
         return mChecked;
     }
 
+    @Override
     public void toggle() {
         setChecked(!mChecked);
     }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/view/ViewPagerActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/view/ViewPagerActivity.java
index 5150363..6b18846 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/view/ViewPagerActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/view/ViewPagerActivity.java
@@ -16,8 +16,6 @@
 
 package com.example.android.supportv4.view;
 
-import com.example.android.supportv4.R;
-
 import android.app.Activity;
 import android.graphics.Color;
 import android.os.Bundle;
@@ -28,6 +26,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.example.android.supportv4.R;
+
 import java.util.ArrayList;
 
 public class ViewPagerActivity extends Activity {
@@ -46,17 +46,17 @@
         mAdapter.add("Green", Color.GREEN);
         mAdapter.add("Blue", Color.BLUE);
 
-        mPager = (ViewPager) findViewById(R.id.pager);
+        mPager = findViewById(R.id.pager);
         mPager.setAdapter(mAdapter);
 
-        mTitles = (PagerTitleStrip) findViewById(R.id.titles);
+        mTitles = findViewById(R.id.titles);
     }
 
     private static class ColorPagerAdapter extends PagerAdapter {
         private ArrayList<Pair<String, Integer>> mEntries = new ArrayList<>();
 
         public void add(String title, int color) {
-            mEntries.add(new Pair(title, color));
+            mEntries.add(new Pair<>(title, color));
         }
 
         @Override
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
index 1403a94..86c7d70 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
@@ -16,8 +16,6 @@
 
 package com.example.android.supportv4.widget;
 
-import com.example.android.supportv4.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
@@ -27,9 +25,8 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
+
+import com.example.android.supportv4.R;
 
 /**
  * Example of using the SwipeRefreshLayout.
@@ -92,7 +89,7 @@
         super.onCreate(bundle);
         setContentView(getLayoutId());
 
-        mSwipeRefreshWidget = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_widget);
+        mSwipeRefreshWidget = findViewById(R.id.swipe_refresh_widget);
         mSwipeRefreshWidget.setColorSchemeResources(R.color.color1, R.color.color2, R.color.color3,
                 R.color.color4);
         mSwipeRefreshWidget.setOnRefreshListener(this);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java
index 08c14dc..c5f86aa 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java
@@ -17,17 +17,13 @@
 package com.example.android.supportv4.widget;
 
 import android.app.Activity;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
 import android.os.Bundle;
-import android.os.Handler;
 import android.support.v4.widget.ContentLoadingProgressBar;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewTreeObserver;
-import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
 
 import com.example.android.supportv4.R;
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java
index 10db8f3..f85105f 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java
@@ -73,7 +73,7 @@
 
         setContentView(R.layout.explore_by_touch_helper);
 
-        final CustomView customView = (CustomView) findViewById(R.id.custom_view);
+        final CustomView customView = findViewById(R.id.custom_view);
 
         // Adds an item at the top-left quarter of the custom view.
         customView.addItem(getString(R.string.sample_item_a), 0, 0, 0.5f, 0.5f);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java
index b408349..73798ed 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java
@@ -75,9 +75,9 @@
 
         setContentView(R.layout.sliding_pane_layout);
 
-        mSlidingLayout = (SlidingPaneLayout) findViewById(R.id.sliding_pane_layout);
-        mList = (ListView) findViewById(R.id.left_pane);
-        mContent = (TextView) findViewById(R.id.content_text);
+        mSlidingLayout = findViewById(R.id.sliding_pane_layout);
+        mList = findViewById(R.id.left_pane);
+        mContent = findViewById(R.id.content_text);
 
         mSlidingLayout.setPanelSlideListener(new SliderListener());
         mSlidingLayout.openPane();
@@ -144,7 +144,12 @@
         @Override
         public void onGlobalLayout() {
             mActionBar.onFirstLayout();
-            mSlidingLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+                mSlidingLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+            } else {
+                //noinspection deprecation
+                mSlidingLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+            }
         }
     }
 
diff --git a/samples/Support7Demos/Android.mk b/samples/Support7Demos/Android.mk
deleted file mode 100644
index fbea278..0000000
--- a/samples/Support7Demos/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2013 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := Support7Demos
-LOCAL_MODULE_TAGS := samples tests
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 7
-LOCAL_DEX_PREOPT := false
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-v7-appcompat \
-    android-support-v7-gridlayout \
-    android-support-v7-mediarouter \
-    android-support-v7-cardview \
-    android-support-design \
-    android-support-v7-recyclerview \
-    android-support-v7-palette \
-    android-support-v4
-LOCAL_AAPT_FLAGS := --no-version-vectors
-include $(BUILD_PACKAGE)
diff --git a/samples/Support7Demos/AndroidManifest.xml b/samples/Support7Demos/AndroidManifest.xml
index 8200390..1604267 100644
--- a/samples/Support7Demos/AndroidManifest.xml
+++ b/samples/Support7Demos/AndroidManifest.xml
@@ -34,8 +34,6 @@
     <!-- Permission for ACCESS_COARSE_LOCATION is required for DayNight themes. -->
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
-    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
-
     <!-- The smallest screen this app works on is a phone.  The app will
          scale its UI to larger screens but doesn't make good use of them
          so allow the compatibility mode button to be shown (mostly because
@@ -254,6 +252,24 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".app.AppCompatWidgetsTextViews"
+                  android:label="@string/appcompat_widgets_text_views"
+                  android:theme="@style/Theme.Custom.TextLink">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".app.MenuItemIconTinting"
+                  android:label="@string/menu_item_icon_tinting"
+                  android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".app.ToolbarUsage"
                   android:label="@string/toolbar_usage"
                   android:theme="@style/Theme.Custom.NoActionBar">
diff --git a/samples/Support7Demos/build.gradle b/samples/Support7Demos/build.gradle
index d87479a..49b38ec 100644
--- a/samples/Support7Demos/build.gradle
+++ b/samples/Support7Demos/build.gradle
@@ -1,19 +1,21 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-appcompat-v7')
-    compile project(':support-cardview-v7')
-    compile project(':support-gridlayout-v7')
-    compile project(':support-mediarouter-v7')
-    compile project(':support-palette-v7')
-    compile project(':support-recyclerview-v7')
+    implementation project(':support-appcompat-v7')
+    implementation project(':support-cardview-v7')
+    implementation project(':support-gridlayout-v7')
+    implementation project(':support-mediarouter-v7')
+    implementation project(':support-palette-v7')
+    implementation project(':support-recyclerview-v7')
 }
 
 android {
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
-        minSdkVersion 9
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
+        vectorDrawables.useSupportLibrary = true
     }
 
     sourceSets {
@@ -23,8 +25,16 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
-        abortOnError false
+        abortOnError true
+        check 'NewApi'
     }
 
     compileOptions {
diff --git a/samples/Support7Demos/res/color/link_color.xml b/samples/Support7Demos/res/color/link_color.xml
new file mode 100644
index 0000000..6d71ed9
--- /dev/null
+++ b/samples/Support7Demos/res/color/link_color.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="?android:disabledAlpha"
+          android:color="?colorPrimary"
+          android:state_enabled="false"/>
+    <item android:color="?colorPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/samples/Support7Demos/res/drawable/animation_vector_drawable_grouping_1.xml b/samples/Support7Demos/res/drawable/animation_vector_drawable_grouping_1.xml
index dbdf453..5359361 100644
--- a/samples/Support7Demos/res/drawable/animation_vector_drawable_grouping_1.xml
+++ b/samples/Support7Demos/res/drawable/animation_vector_drawable_grouping_1.xml
@@ -13,8 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-                 android:drawable="@drawable/vector_drawable_grouping_1">
+                 xmlns:tools="http://schemas.android.com/tools"
+                 android:drawable="@drawable/vector_drawable_grouping_1"
+                 tools:ignore="NewApi">
 
     <target
             android:name="sun"
diff --git a/samples/Support7Demos/res/layout/appcompat_widgets_buttons.xml b/samples/Support7Demos/res/layout/appcompat_widgets_buttons.xml
index 1c60d90..517f4fa 100644
--- a/samples/Support7Demos/res/layout/appcompat_widgets_buttons.xml
+++ b/samples/Support7Demos/res/layout/appcompat_widgets_buttons.xml
@@ -29,8 +29,25 @@
         <android.support.v7.widget.SwitchCompat
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
+                android:text="SwitchCompat"/>
+
+        <android.support.v7.widget.SwitchCompat
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:enabled="false"
+                android:text="SwitchCompat disabled"/>
+
+        <Switch
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
                 android:text="Switch"/>
 
+        <Switch
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:enabled="false"
+                android:text="Switch disabled"/>
+
         <CheckBox
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/samples/Support7Demos/res/layout/appcompat_widgets_text_views.xml b/samples/Support7Demos/res/layout/appcompat_widgets_text_views.xml
new file mode 100644
index 0000000..4aafaa1
--- /dev/null
+++ b/samples/Support7Demos/res/layout/appcompat_widgets_text_views.xml
@@ -0,0 +1,52 @@
+<?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.
+  -->
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="16dp">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/text_plain_enabled"/>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/text_plain_disabled"/>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/text_link_enabled"/>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/text_link_disabled"/>
+
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/Support7Demos/res/layout/overlay_display_window.xml b/samples/Support7Demos/res/layout/overlay_display_window.xml
index 36b4a0d..1f026fe 100644
--- a/samples/Support7Demos/res/layout/overlay_display_window.xml
+++ b/samples/Support7Demos/res/layout/overlay_display_window.xml
@@ -15,10 +15,12 @@
 -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:tools="http://schemas.android.com/tools"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="#000000">
-    <TextureView android:id="@+id/overlay_display_window_texture"
+    <TextureView tools:ignore="NewApi"
+               android:id="@+id/overlay_display_window_texture"
                android:layout_width="0px"
                android:layout_height="0px" />
     <TextView android:id="@+id/overlay_display_window_title"
diff --git a/samples/Support7Demos/res/menu/actions.xml b/samples/Support7Demos/res/menu/actions.xml
index e3b576e..d558365 100644
--- a/samples/Support7Demos/res/menu/actions.xml
+++ b/samples/Support7Demos/res/menu/actions.xml
@@ -18,19 +18,26 @@
     <item android:id="@+id/action_search"
           android:title="@string/action_bar_search"
           android:icon="@drawable/ic_search"
+          android:alphabeticShortcut="s"
+          app:alphabeticModifiers="ALT"
           app:showAsAction="ifRoom|collapseActionView"
           app:actionViewClass="android.support.v7.widget.SearchView" />
     <item android:id="@+id/action_add"
           android:icon="@android:drawable/ic_menu_add"
-          android:title="@string/action_bar_add" />
+          android:title="@string/action_bar_add"
+          android:alphabeticShortcut="a"
+          app:contentDescription="@string/action_bar_add_description"
+          app:tooltipText="@string/action_bar_add_tooltip" />
     <item android:id="@+id/action_edit"
           android:icon="@android:drawable/ic_menu_edit"
           android:title="@string/action_bar_edit"
+          android:alphabeticShortcut="e"
           app:showAsAction="always" />
     <item android:id="@+id/action_share"
           android:icon="@android:drawable/ic_menu_share"
           android:title="@string/action_bar_share"
           android:enabled="false"
+          android:alphabeticShortcut="s"
           app:showAsAction="ifRoom" />
     <item android:id="@+id/action_sort"
           android:icon="@android:drawable/ic_menu_sort_by_size"
@@ -39,10 +46,14 @@
         <menu>
             <item android:id="@+id/action_sort_size"
                   android:icon="@android:drawable/ic_menu_sort_by_size"
-                  android:title="@string/action_bar_sort_size" />
+                  android:title="@string/action_bar_sort_size"
+                  android:alphabeticShortcut="s"
+                  app:alphabeticModifiers="CTRL|SHIFT" />
             <item android:id="@+id/action_sort_alpha"
                   android:icon="@android:drawable/ic_menu_sort_alphabetically"
-                  android:title="@string/action_bar_sort_alpha" />
+                  android:title="@string/action_bar_sort_alpha"
+                  android:alphabeticShortcut="a"
+                  app:alphabeticModifiers="CTRL|SHIFT" />
         </menu>
     </item>
 </menu>
diff --git a/samples/Support7Demos/res/menu/menu_with_tinted_icons.xml b/samples/Support7Demos/res/menu/menu_with_tinted_icons.xml
new file mode 100644
index 0000000..62522b8
--- /dev/null
+++ b/samples/Support7Demos/res/menu/menu_with_tinted_icons.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<menu
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_first"
+        android:icon="@drawable/ic_search"
+        android:title="@string/action_bar_search"
+        app:iconTint="@color/color_sky_night"
+        app:showAsAction="always"/>
+
+    <item
+        android:id="@+id/menu_second"
+        android:icon="@drawable/ic_media_play"
+        android:title="@string/action_bar_add"
+        app:iconTint="@color/color_sky_day"
+        app:iconTintMode="multiply"
+        app:showAsAction="always"/>
+
+    <item
+        android:id="@+id/menu_third"
+        android:icon="@drawable/ic_media_pause"
+        android:title="@string/action_bar_edit"
+        app:iconTint="@color/color_sky"
+        app:iconTintMode="screen"
+        app:showAsAction="always"/>
+
+    <item
+        android:id="@+id/menu_fourth"
+        android:icon="@android:drawable/ic_menu_share"
+        android:title="@string/action_bar_share"/>
+
+    <item
+        android:id="@+id/menu_fifth"
+        android:icon="@android:drawable/ic_menu_sort_by_size"
+        android:title="@string/action_bar_sort"/>
+
+</menu>
diff --git a/samples/Support7Demos/res/menu/popup_menu.xml b/samples/Support7Demos/res/menu/popup_menu.xml
index f50efc5..09d3bfa 100644
--- a/samples/Support7Demos/res/menu/popup_menu.xml
+++ b/samples/Support7Demos/res/menu/popup_menu.xml
@@ -13,9 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto">
     <item android:id="@+id/action_highlight"
-          android:title="@string/popup_menu_highlight" />
+          android:title="@string/popup_menu_highlight"
+          app:contentDescription="@string/popup_menu_highlight_description"
+          app:tooltipText="@string/popup_menu_highlight_tooltip" />
     <item android:id="@+id/action_edit"
           android:title="@string/popup_menu_edit" />
     <item android:id="@+id/action_delete"
diff --git a/samples/Support7Demos/res/values-v21/styles.xml b/samples/Support7Demos/res/values-v21/styles.xml
index 00d54c9..f75030d 100644
--- a/samples/Support7Demos/res/values-v21/styles.xml
+++ b/samples/Support7Demos/res/values-v21/styles.xml
@@ -1,20 +1,30 @@
 <?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.
-  -->
+     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="Theme.SampleDrawerLayout" parent="Theme.AppCompat.NoActionBar">
+        <!-- Tell SystemUI that our activity window will draw the background for the status bar. -->
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <!-- Set the status bar to be translucent black. -->
+        <item name="android:statusBarColor">#30000000</item>
+        <item name="windowActionModeOverlay">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <style name="CustomPopupNoElevation" parent="@style/Widget.AppCompat.Light.PopupMenu">
         <item name="android:popupElevation">2dp</item>
     </style>
diff --git a/samples/Support7Demos/res/values/strings.xml b/samples/Support7Demos/res/values/strings.xml
index b5b6a51..097807f 100644
--- a/samples/Support7Demos/res/values/strings.xml
+++ b/samples/Support7Demos/res/values/strings.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
     <string name="activity_sample_code">Support v7 Demos</string>
 
     <!-- MediaRouter -->
@@ -68,10 +68,15 @@
     <string name="appcompat_widgets_buttons">AppCompat/Widgets/Buttons</string>
     <string name="appcompat_widgets_spinners">AppCompat/Widgets/Spinners</string>
     <string name="appcompat_widgets_text_input">AppCompat/Widgets/Text Input</string>
+    <string name="appcompat_widgets_text_views">AppCompat/Widgets/Text Views</string>
 
     <string name="action_bar_search">Search</string>
     <string name="action_bar_add">Add</string>
+    <string name="action_bar_add_description">Add description</string>
+    <string name="action_bar_add_tooltip">Add tooltip</string>
     <string name="action_bar_edit">Edit</string>
+    <string name="action_bar_edit_description">Edit description</string>
+    <string name="action_bar_edit_tooltip">Edit tooltip</string>
     <string name="action_bar_share">Share</string>
     <string name="action_bar_sort">Sort</string>
     <string name="action_bar_sort_alpha">Alphabetically</string>
@@ -215,7 +220,11 @@
     <string name="popup_menu_button">Show popup!</string>
     <string name="popup_default_elevation">Use default elevation on popup</string>
     <string name="popup_menu_highlight">Highlight</string>
+    <string name="popup_menu_highlight_description">Highlight description</string>
+    <string name="popup_menu_highlight_tooltip">Highlight tooltip</string>
     <string name="popup_menu_edit">Edit</string>
+    <string name="popup_menu_edit_description">Edit description</string>
+    <string name="popup_menu_edit_tooltip">Edit tooltip</string>
     <string name="popup_menu_delete">Delete</string>
     <string name="popup_menu_ignore">Ignore</string>
     <string name="popup_menu_share">Share</string>
@@ -229,5 +238,12 @@
     <string name="appcompat_vector_title">AppCompat/Integrations/AnimatedVectorDrawable</string>
 
     <string name="night_mode">DAY</string>
+
+    <string name="text_plain_enabled">Plain enabled</string>
+    <string name="text_plain_disabled">Plain disabled</string>
+    <string name="text_link_enabled">With <a href="http://www.google.com">link</a> enabled</string>
+    <string name="text_link_disabled">With <a href="http://www.google.com">link</a> disabled</string>
+
+    <string name="menu_item_icon_tinting">AppCompat/Menu Item Icons</string>
 </resources>
 
diff --git a/samples/Support7Demos/res/values/styles.xml b/samples/Support7Demos/res/values/styles.xml
index ca2eb9f..9c022d3 100644
--- a/samples/Support7Demos/res/values/styles.xml
+++ b/samples/Support7Demos/res/values/styles.xml
@@ -29,6 +29,10 @@
         <item name="colorAccent">#ffff00</item>
     </style>
 
+    <style name="Theme.Custom.TextLink">
+        <item name="android:textColorLink">@color/link_color</item>
+    </style>
+
     <style name="Theme.SampleMediaRouter" parent="Theme.AppCompat">
         <item name="colorPrimary">#fff44336</item>
         <item name="colorPrimaryDark">#d32f2f</item>
@@ -49,10 +53,6 @@
     </style>
 
     <style name="Theme.SampleDrawerLayout" parent="Theme.AppCompat.NoActionBar">
-        <!-- Tell SystemUI that our activity window will draw the background for the status bar. -->
-        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
-        <!-- Set the status bar to be translucent black. -->
-        <item name="android:statusBarColor">#30000000</item>
         <item name="windowActionModeOverlay">true</item>
         <item name="android:windowContentOverlay">@null</item>
     </style>
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java
index 6edf593..e263945 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java
@@ -16,22 +16,16 @@
 
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
-import android.support.v7.widget.SearchView;
-import android.text.TextUtils;
 import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.TextView;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of an action mode.
  */
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarFragmentMenu.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarFragmentMenu.java
index e2779d0..73d63f3 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarFragmentMenu.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarFragmentMenu.java
@@ -16,13 +16,10 @@
 
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -32,6 +29,8 @@
 import android.widget.CheckBox;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * Demonstrates how fragments can participate in the options menu.
  */
@@ -136,8 +135,8 @@
 
         @Override
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-            MenuItemCompat.setShowAsAction(menu.add("Menu 1a"), MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            MenuItemCompat.setShowAsAction(menu.add("Menu 1b"), MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            menu.add("Menu 1a").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            menu.add("Menu 1b").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
             super.onCreateOptionsMenu(menu, inflater);
         }
 
@@ -178,7 +177,7 @@
 
         @Override
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-            MenuItemCompat.setShowAsAction(menu.add("Menu 2"), MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            menu.add("Menu 2").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         }
 
         @Override
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarMechanics.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarMechanics.java
index 568c287..0283ea6 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarMechanics.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarMechanics.java
@@ -16,7 +16,6 @@
 package com.example.android.supportv7.app;
 
 import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.view.Menu;
@@ -56,7 +55,7 @@
         // Items that show as actions should favor the "if room" setting, which will
         // prevent too many buttons from crowding the bar. Extra items will show in the
         // overflow area.
-        MenuItemCompat.setShowAsAction(actionItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+        actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
 
         // Items that show as actions are strongly encouraged to use an icon.
         // These icons are shown without a text description, and therefore should
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java
index 8772601..edf57b9 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java
@@ -15,8 +15,6 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v4.view.MenuItemCompat;
@@ -29,6 +27,8 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of the Action Bar. The default Honeycomb theme
  * includes the action bar by default and a menu resource is used to populate the
@@ -50,9 +50,13 @@
     public boolean onCreateOptionsMenu(Menu menu) {
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.actions, menu);
-        SearchView searchView = (SearchView) MenuItemCompat
-                .getActionView(menu.findItem(R.id.action_search));
+        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
         searchView.setOnQueryTextListener(mOnQueryTextListener);
+        final MenuItem editItem = menu.findItem(R.id.action_edit);
+        MenuItemCompat.setContentDescription(editItem,
+                getString(R.string.action_bar_edit_description));
+        MenuItemCompat.setTooltipText(editItem,
+                getString(R.string.action_bar_edit_tooltip));
         return true;
     }
 
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AlertDialogUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AlertDialogUsage.java
index 560111b..4fdcabb 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AlertDialogUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AlertDialogUsage.java
@@ -37,7 +37,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.alert_dialog_usage);
 
-        mSpinner = (Spinner) findViewById(R.id.spinner_dialogs);
+        mSpinner = findViewById(R.id.spinner_dialogs);
 
         // Add an OnClickListener to show our selected dialog
         findViewById(R.id.btn_show_dialog).setOnClickListener(new View.OnClickListener() {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatAnimatedVector.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatAnimatedVector.java
index 888bdc8..ecb6f58 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatAnimatedVector.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatAnimatedVector.java
@@ -31,7 +31,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.appcompat_animated_vector);
 
-        final ImageView imageView = (ImageView) findViewById(R.id.vector_image);
+        final ImageView imageView = findViewById(R.id.vector_image);
         if (imageView != null) {
             final Drawable src = imageView.getDrawable();
             if (src instanceof Animatable) {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java
index 276465b..91da0f4 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java
@@ -15,16 +15,15 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDelegate;
-import android.support.v7.app.AppCompatDialog;
 import android.view.View;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of AlertDialog with Theme.AppCompat.DayNight
  */
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java
index d923a92..7a0608e 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java
@@ -15,21 +15,14 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
-import android.app.Dialog;
-import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDelegate;
 import android.support.v7.app.AppCompatDialog;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
-import android.widget.Spinner;
-import android.widget.Toast;
+
+import com.example.android.supportv7.R;
 
 /**
  * This demonstrates idiomatic usage of Dialog with Theme.AppCompat.DayNight
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java
index e2ae88c..17f34243 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java
@@ -115,6 +115,7 @@
         getDelegate().onDestroy();
     }
 
+    @Override
     public void invalidateOptionsMenu() {
         getDelegate().invalidateOptionsMenu();
     }
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsSpinners.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsSpinners.java
index 2d22b99..572b373 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsSpinners.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsSpinners.java
@@ -34,11 +34,11 @@
         setContentView(R.layout.appcompat_widgets_text_spinners);
 
         // Fetch the Spinners and set an adapter
-        Spinner spinner = (Spinner) findViewById(R.id.widgets_spinner);
+        Spinner spinner = findViewById(R.id.widgets_spinner);
         spinner.setAdapter(new ArrayAdapter<>(this,
                 R.layout.support_simple_spinner_dropdown_item, Cheeses.sCheeseStrings));
 
-        spinner = (Spinner) findViewById(R.id.widgets_spinner_underlined);
+        spinner = findViewById(R.id.widgets_spinner_underlined);
         spinner.setAdapter(new ArrayAdapter<>(this,
                 R.layout.support_simple_spinner_dropdown_item, Cheeses.sCheeseStrings));
     }
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextInput.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextInput.java
index c94bd19..a8b0d6b 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextInput.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextInput.java
@@ -37,13 +37,13 @@
         setContentView(R.layout.appcompat_widgets_text_input);
 
         // Fetch the AutoCompleteTextView and set an adapter
-        AutoCompleteTextView actv = (AutoCompleteTextView) findViewById(
+        AutoCompleteTextView actv = findViewById(
                 R.id.widgets_autocompletetextview);
         actv.setAdapter(new ArrayAdapter<>(this,
                 android.R.layout.simple_dropdown_item_1line, Cheeses.sCheeseStrings));
 
         // Fetch the MultiAutoCompleteTextView and set an adapter and Tokenizer
-        MultiAutoCompleteTextView mactv = (MultiAutoCompleteTextView) findViewById(
+        MultiAutoCompleteTextView mactv = findViewById(
                 R.id.widgets_multiautocompletetextview);
         mactv.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
         mactv.setAdapter(new ArrayAdapter<>(this,
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextViews.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextViews.java
new file mode 100644
index 0000000..1480509
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextViews.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package com.example.android.supportv7.app;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+import com.example.android.supportv7.R;
+
+/**
+ * This demonstrates the styled {@link android.widget.TextView} widgets in AppCompat.
+ */
+public class AppCompatWidgetsTextViews extends AppCompatActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.appcompat_widgets_text_views);
+    }
+
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java
index f44a0df..e41ab6c 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java
@@ -15,13 +15,8 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
-import android.app.Dialog;
-import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDialog;
 import android.support.v7.app.AppCompatDialogFragment;
@@ -34,6 +29,8 @@
 import android.widget.Spinner;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of AppCompatDialogFragment.
  */
@@ -46,7 +43,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.dialog_usage);
 
-        mSpinner = (Spinner) findViewById(R.id.spinner_dialogs);
+        mSpinner = findViewById(R.id.spinner_dialogs);
 
         // Add an OnClickListener to show our selected dialog
         findViewById(R.id.btn_show_dialog).setOnClickListener(new View.OnClickListener() {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java
index ea1a07d..be5da4b 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java
@@ -15,12 +15,9 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.app.Dialog;
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDialog;
 import android.view.Menu;
@@ -29,6 +26,8 @@
 import android.widget.Spinner;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of AppCompatDialog.
  */
@@ -41,7 +40,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.dialog_usage);
 
-        mSpinner = (Spinner) findViewById(R.id.spinner_dialogs);
+        mSpinner = findViewById(R.id.spinner_dialogs);
 
         // Add an OnClickListener to show our selected dialog
         findViewById(R.id.btn_show_dialog).setOnClickListener(new View.OnClickListener() {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/MenuItemIconTinting.java b/samples/Support7Demos/src/com/example/android/supportv7/app/MenuItemIconTinting.java
new file mode 100644
index 0000000..e5f37f6
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/MenuItemIconTinting.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+package com.example.android.supportv7.app;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.example.android.supportv7.R;
+
+/**
+ * This demonstrates icon tinting on menu items.
+ */
+public class MenuItemIconTinting extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.toolbar_usage);
+
+        // Retrieve the Toolbar from our content view, and set it as the action bar
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu_with_tinted_icons, menu);
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        Toast.makeText(this, "Selected Item: " + item.getTitle(), Toast.LENGTH_SHORT).show();
+        return true;
+    }
+
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarActionMode.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarActionMode.java
index 3977048..6e11eba 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarActionMode.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarActionMode.java
@@ -37,7 +37,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.toolbar_action_mode);
 
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
 
         findViewById(R.id.btn_start_action_mode).setOnClickListener(new View.OnClickListener() {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java
index e6d179b..765ddef 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java
@@ -42,7 +42,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.toolbar_display_options);
 
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
 
         findViewById(R.id.toggle_home_as_up).setOnClickListener(this);
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java
index 575c7a1..bb6ba79 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java
@@ -16,14 +16,11 @@
 
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
@@ -37,6 +34,8 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -50,10 +49,10 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.toolbar_fragment_pager);
 
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
 
-        ViewPager vp = (ViewPager) findViewById(R.id.viewpager);
+        ViewPager vp = findViewById(R.id.viewpager);
         PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(),
                 new MenuFragment(), new Menu2Fragment());
         vp.setAdapter(adapter);
@@ -96,8 +95,8 @@
 
         @Override
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-            MenuItemCompat.setShowAsAction(menu.add("Menu 1a"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
-            MenuItemCompat.setShowAsAction(menu.add("Menu 1b"), MenuItemCompat.SHOW_AS_ACTION_NEVER);
+            menu.add("Menu 1a").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            menu.add("Menu 1b").setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
             super.onCreateOptionsMenu(menu, inflater);
         }
 
@@ -140,7 +139,7 @@
 
         @Override
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-            MenuItemCompat.setShowAsAction(menu.add("Menu 2"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+            menu.add("Menu 2").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         }
 
         @Override
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java
index 55e7f14..0710e24 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java
@@ -15,11 +15,8 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.app.SearchManager;
 import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.SearchView;
 import android.support.v7.widget.Toolbar;
@@ -28,6 +25,8 @@
 import android.view.MenuItem;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of the Toolbar as the action bar.
  */
@@ -39,7 +38,7 @@
         setContentView(R.layout.toolbar_usage);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
     }
 
@@ -49,8 +48,8 @@
         inflater.inflate(R.menu.actions, menu);
 
         // Retrieve the SearchView and plug it into SearchManager
-        final SearchView searchView = (SearchView) MenuItemCompat
-                .getActionView(menu.findItem(R.id.action_search));
+        final SearchView searchView =
+                (SearchView) menu.findItem(R.id.action_search).getActionView();
 
         SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
         searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/graphics/ImageLoader.java b/samples/Support7Demos/src/com/example/android/supportv7/graphics/ImageLoader.java
index 30a0aa2..d20103f 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/graphics/ImageLoader.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/graphics/ImageLoader.java
@@ -20,7 +20,6 @@
 import android.os.AsyncTask;
 import android.provider.MediaStore;
 import android.support.v4.graphics.BitmapCompat;
-import android.support.v4.os.AsyncTaskCompat;
 import android.support.v4.util.LruCache;
 import android.widget.ImageView;
 
@@ -57,7 +56,7 @@
             return;
         }
 
-        AsyncTaskCompat.executeParallel(new AsyncTask<Void, Void, Bitmap>() {
+        new AsyncTask<Void, Void, Bitmap>() {
             @Override
             protected Bitmap doInBackground(Void... params) {
                 return MediaStore.Images.Thumbnails.getThumbnail(
@@ -80,7 +79,7 @@
                     }
                 }
             }
-        });
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
     /**
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/graphics/PaletteDetailActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/graphics/PaletteDetailActivity.java
index 794863b..273a2ca 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/graphics/PaletteDetailActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/graphics/PaletteDetailActivity.java
@@ -62,8 +62,8 @@
 
         mImageUri = getIntent().getData();
 
-        mImageView = (ImageView) findViewById(R.id.image);
-        mGridView = (GridView) findViewById(R.id.palette);
+        mImageView = findViewById(R.id.image);
+        mGridView = findViewById(R.id.palette);
         mSwatchesPalette = new SwatchesPalette();
         mGridView.setAdapter(mSwatchesPalette);
 
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java b/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java
index b62f76e..2d0cb56 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java
@@ -16,6 +16,7 @@
 
 package com.example.android.supportv7.media;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Presentation;
 import android.content.Context;
@@ -26,8 +27,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.support.v7.media.MediaRouter.RouteInfo;
 import android.support.v7.media.MediaItemStatus;
+import android.support.v7.media.MediaRouter.RouteInfo;
 import android.util.Log;
 import android.view.Display;
 import android.view.Gravity;
@@ -373,6 +374,7 @@
         }
     }
 
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     private static final class ICSMediaPlayer {
         public static final void setSurface(MediaPlayer player, Surface surface) {
             player.setSurface(surface);
@@ -398,7 +400,6 @@
 
             // add surface holder callback
             SurfaceHolder holder = mSurfaceView.getHolder();
-            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
             holder.addCallback(this);
         }
 
@@ -412,11 +413,8 @@
         public void release() {
             super.release();
 
-            // dismiss presentation display
-            if (mPresentation != null) {
-                Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");
-                mPresentation.dismiss();
-                mPresentation = null;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                releasePresentation();
             }
 
             // remove surface holder callback
@@ -428,6 +426,7 @@
             mLayout.setVisibility(View.GONE);
         }
 
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
         @Override
         public void updatePresentation() {
             // Get the current route and its presentation display.
@@ -490,7 +489,10 @@
             int width = getVideoWidth();
             int height = getVideoHeight();
             if (width > 0 && height > 0) {
-                if (mPresentation == null) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1
+                        && mPresentation != null) {
+                    mPresentation.updateSize(width, height);
+                } else {
                     int surfaceWidth = mLayout.getWidth();
                     int surfaceHeight = mLayout.getHeight();
 
@@ -510,8 +512,6 @@
                     }
                     Log.i(TAG, "video rect is " + lp.width + "x" + lp.height);
                     mSurfaceView.setLayoutParams(lp);
-                } else {
-                    mPresentation.updateSize(width, height);
                 }
             }
         }
@@ -540,7 +540,18 @@
             }
         };
 
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+        private void releasePresentation() {
+            // dismiss presentation display
+            if (mPresentation != null) {
+                Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");
+                mPresentation.dismiss();
+                mPresentation = null;
+            }
+        }
+
         // Presentation
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
         private final class DemoPresentation extends Presentation {
             private SurfaceView mPresentationSurfaceView;
 
@@ -559,7 +570,6 @@
                 // Set up the surface view.
                 mPresentationSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
                 SurfaceHolder holder = mPresentationSurfaceView.getHolder();
-                holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
                 holder.addCallback(SurfaceViewPlayer.this);
                 Log.i(TAG, "Presentation created");
             }
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/OverlayDisplayWindow.java b/samples/Support7Demos/src/com/example/android/supportv7/media/OverlayDisplayWindow.java
index 65a7ca2..d6a82ef 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/media/OverlayDisplayWindow.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/media/OverlayDisplayWindow.java
@@ -15,30 +15,32 @@
  */
 
 package com.example.android.supportv7.media;
-import com.example.android.supportv7.R;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.SurfaceTexture;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
-import android.util.DisplayMetrics;
 import android.view.GestureDetector;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.ScaleGestureDetector;
+import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.TextureView;
-import android.view.View;
-import android.view.Surface;
-import android.view.WindowManager;
 import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.WindowManager;
 import android.widget.TextView;
 
+import com.example.android.supportv7.R;
+
 /**
  * Manages an overlay display window, used for simulating remote playback.
  */
@@ -124,8 +126,15 @@
 
                 Display display = mWindowManager.getDefaultDisplay();
 
-                WindowManager.LayoutParams params = new WindowManager.LayoutParams(
-                        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+                WindowManager.LayoutParams params;
+                if (Build.VERSION.SDK_INT >= 26) {
+                    // TYPE_SYSTEM_ALERT is deprecated in android O.
+                    params = new WindowManager.LayoutParams(
+                            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+                } else {
+                    params = new WindowManager.LayoutParams(
+                            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+                }
                 params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                         | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -177,6 +186,7 @@
     /**
      * Implementation for API version 17+.
      */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     private static final class JellybeanMr1Impl extends OverlayDisplayWindow {
         // When true, disables support for moving and resizing the overlay.
         // The window is made non-touchable, which makes it possible to
@@ -295,8 +305,14 @@
                     R.id.overlay_display_window_title);
             mNameTextView.setText(mName);
 
-            mWindowParams = new WindowManager.LayoutParams(
-                    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            if (Build.VERSION.SDK_INT >= 26) {
+                // TYPE_SYSTEM_ALERT is deprecated in android O.
+                mWindowParams = new WindowManager.LayoutParams(
+                        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+            } else {
+                mWindowParams = new WindowManager.LayoutParams(
+                        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            }
             mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java b/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java
index 5e94413..380e945 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java
@@ -16,8 +16,10 @@
 
 package com.example.android.supportv7.media;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.os.Build;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.media.session.PlaybackStateCompat;
@@ -72,7 +74,10 @@
     public void takeSnapshot() {}
     public Bitmap getSnapshot() { return null; }
 
-    // presentation display
+    /**
+     * presentation display
+     */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public void updatePresentation() {}
 
     public void setCallback(Callback callback) {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
index bae5362..c486a1a 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
@@ -24,10 +24,12 @@
 import android.media.AudioManager;
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.view.MenuItemCompat;
@@ -132,7 +134,9 @@
             Log.d(TAG, "onRouteSelected: route=" + route);
 
             mPlayer = Player.create(SampleMediaRouterActivity.this, route, mMediaSession);
-            mPlayer.updatePresentation();
+            if (isPresentationApiSupported()) {
+                mPlayer.updatePresentation();
+            }
             mSessionManager.setPlayer(mPlayer);
             mSessionManager.unsuspend();
 
@@ -150,7 +154,9 @@
                         0 : (SystemClock.elapsedRealtime() - item.getTimestamp()));
                 mSessionManager.suspend(pos);
             }
-            mPlayer.updatePresentation();
+            if (isPresentationApiSupported()) {
+                mPlayer.updatePresentation();
+            }
             mPlayer.release();
         }
 
@@ -163,7 +169,9 @@
         public void onRoutePresentationDisplayChanged(
                 MediaRouter router, RouteInfo route) {
             Log.d(TAG, "onRoutePresentationDisplayChanged: route=" + route);
-            mPlayer.updatePresentation();
+            if (isPresentationApiSupported()) {
+                mPlayer.updatePresentation();
+            }
         }
 
         @Override
@@ -180,6 +188,10 @@
         public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
             Log.d(TAG, "onRouteProviderChanged: provider=" + provider);
         }
+
+        private boolean isPresentationApiSupported() {
+            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
+        }
     };
 
     private MediaSessionCompat mMediaSession;
@@ -205,6 +217,13 @@
         // Be sure to call the super class.
         super.onCreate(savedInstanceState);
 
+        // Need overlay permission for emulating remote display.
+        if (Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(this)) {
+            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+                    Uri.parse("package:" + getPackageName()));
+            startActivityForResult(intent, 0);
+        }
+
         // Get the media router service.
         mMediaRouter = MediaRouter.getInstance(this);
 
@@ -223,15 +242,13 @@
         DiscoveryFragment fragment = (DiscoveryFragment) fm.findFragmentByTag(
                 DISCOVERY_FRAGMENT_TAG);
         if (fragment == null) {
-            fragment = new DiscoveryFragment(mMediaRouterCB);
-            fragment.setRouteSelector(mSelector);
+            fragment = new DiscoveryFragment();
             fm.beginTransaction()
                     .add(fragment, DISCOVERY_FRAGMENT_TAG)
                     .commit();
-        } else {
-            fragment.setCallback(mMediaRouterCB);
-            fragment.setRouteSelector(mSelector);
         }
+        fragment.setCallback(mMediaRouterCB);
+        fragment.setRouteSelector(mSelector);
 
         // Populate an array adapter with streaming media items.
         String[] mediaNames = getResources().getStringArray(R.array.media_names);
@@ -289,7 +306,7 @@
             }
         });
 
-        mLibraryView = (ListView) findViewById(R.id.media);
+        mLibraryView = findViewById(R.id.media);
         mLibraryView.setAdapter(mLibraryItems);
         mLibraryView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
         mLibraryView.setOnItemClickListener(new OnItemClickListener() {
@@ -299,7 +316,7 @@
             }
         });
 
-        mPlayListView = (ListView) findViewById(R.id.playlist);
+        mPlayListView = findViewById(R.id.playlist);
         mPlayListView.setAdapter(mPlayListItems);
         mPlayListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
         mPlayListView.setOnItemClickListener(new OnItemClickListener() {
@@ -309,9 +326,9 @@
             }
         });
 
-        mInfoTextView = (TextView) findViewById(R.id.info);
+        mInfoTextView = findViewById(R.id.info);
 
-        mUseDefaultControlCheckBox = (CheckBox) findViewById(R.id.custom_control_view_checkbox);
+        mUseDefaultControlCheckBox = findViewById(R.id.custom_control_view_checkbox);
         if (ENABLE_DEFAULT_CONTROL_CHECK_BOX) {
             mUseDefaultControlCheckBox.setVisibility(View.VISIBLE);
         }
@@ -335,7 +352,7 @@
             }
         });
 
-        mSeekBar = (SeekBar) findViewById(R.id.seekbar);
+        mSeekBar = findViewById(R.id.seekbar);
         mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -628,14 +645,6 @@
         private static final String TAG = "DiscoveryFragment";
         private Callback mCallback;
 
-        public DiscoveryFragment() {
-            mCallback = null;
-        }
-
-        public DiscoveryFragment(Callback cb) {
-            mCallback = cb;
-        }
-
         public void setCallback(Callback cb) {
             mCallback = cb;
         }
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
index b2e7094..87c6546 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
@@ -44,7 +44,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.sorted_list_activity);
-        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+        mRecyclerView = findViewById(R.id.recycler_view);
         mRecyclerView.setHasFixedSize(true);
         mLinearLayoutManager = new LinearLayoutManager(this);
         mRecyclerView.setLayoutManager(mLinearLayoutManager);
@@ -53,7 +53,7 @@
                 new Item("wash the dishes"));
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setHasFixedSize(true);
-        final EditText newItemTextView = (EditText) findViewById(R.id.new_item_text_view);
+        final EditText newItemTextView = findViewById(R.id.new_item_text_view);
         newItemTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
             @Override
             public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java
index 0722ebd..5649320 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java
@@ -18,7 +18,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.CardView;
 import android.view.View;
@@ -78,7 +77,7 @@
         if (mMaxElevationSeekBar.getProgress() != mCardView.getMaxCardElevation()) {
             mCardView.setMaxCardElevation(mMaxElevationSeekBar.getProgress());
         }
-        ViewCompat.setAlpha(mCardView, mAlphaSeekBar.getProgress() / 255f);
+        mCardView.setAlpha(mAlphaSeekBar.getProgress() / 255f);
         ViewGroup.LayoutParams lp;
         if (mResizeCardView) {
             lp = setViewBounds(mCardView);
@@ -109,34 +108,34 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_card_view);
-        mInfoText = (TextView) findViewById(R.id.info_text);
-        mCardView = (CardView) findViewById(R.id.card_view);
-        mCornerRadiusSeekBar = (SeekBar) findViewById(R.id.corner_radius_seek_bar);
+        mInfoText = findViewById(R.id.info_text);
+        mCardView = findViewById(R.id.card_view);
+        mCornerRadiusSeekBar = findViewById(R.id.corner_radius_seek_bar);
         mCornerRadiusSeekBar.setProgress((int) mCardView.getRadius());
         mCornerRadiusSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangedListener);
 
-        mWidthSeekBar = (SeekBar) findViewById(R.id.width_seek_bar);
+        mWidthSeekBar = findViewById(R.id.width_seek_bar);
         mWidthSeekBar.setProgress(mCardView.getLayoutParams().width);
 
         mWidthSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangedListener);
 
-        mHeightSeekBar = (SeekBar) findViewById(R.id.height_seek_bar);
+        mHeightSeekBar = findViewById(R.id.height_seek_bar);
         mHeightSeekBar.setProgress(mCardView.getLayoutParams().height);
         mHeightSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangedListener);
 
-        mElevationSeekBar = (SeekBar) findViewById(R.id.elevation_seek_bar);
+        mElevationSeekBar = findViewById(R.id.elevation_seek_bar);
         mElevationSeekBar.setProgress((int) mCardView.getCardElevation());
         mElevationSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangedListener);
 
-        mMaxElevationSeekBar = (SeekBar) findViewById(R.id.max_elevation_seek_bar);
+        mMaxElevationSeekBar = findViewById(R.id.max_elevation_seek_bar);
         mMaxElevationSeekBar.setProgress((int) mCardView.getMaxCardElevation());
         mMaxElevationSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangedListener);
 
-        mAlphaSeekBar = (SeekBar) findViewById(R.id.alpha_seek_bar);
-        mAlphaSeekBar.setProgress((int) ViewCompat.getAlpha(mCardView) * 255);
+        mAlphaSeekBar = findViewById(R.id.alpha_seek_bar);
+        mAlphaSeekBar.setProgress((int) (mCardView.getAlpha() * 255));
         mAlphaSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangedListener);
 
-        RadioGroup rb = (RadioGroup) findViewById(R.id.select_target_radio);
+        RadioGroup rb = findViewById(R.id.select_target_radio);
         rb.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(RadioGroup group, int checkedId) {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
index 6714f6d..cf2fac2 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
@@ -15,9 +15,8 @@
  */
 package com.example.android.supportv7.widget;
 
-import com.example.android.supportv7.R;
-
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.Activity;
@@ -25,9 +24,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.v4.util.ArrayMap;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.RecyclerView;
 import android.util.DisplayMetrics;
@@ -40,6 +36,8 @@
 import android.widget.CompoundButton;
 import android.widget.TextView;
 
+import com.example.android.supportv7.R;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -63,7 +61,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.animated_recycler_view);
 
-        ViewGroup container = (ViewGroup) findViewById(R.id.container);
+        ViewGroup container = findViewById(R.id.container);
         mRecyclerView = new RecyclerView(this);
         mCachedAnimator = createAnimator();
         mCachedAnimator.setChangeDuration(2000);
@@ -79,7 +77,7 @@
         mRecyclerView.setAdapter(mAdapter);
         container.addView(mRecyclerView);
 
-        CheckBox enableAnimations = (CheckBox) findViewById(R.id.enableAnimations);
+        CheckBox enableAnimations = findViewById(R.id.enableAnimations);
         enableAnimations.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@@ -93,7 +91,7 @@
         });
 
         CheckBox enablePredictiveAnimations =
-                (CheckBox) findViewById(R.id.enablePredictiveAnimations);
+                findViewById(R.id.enablePredictiveAnimations);
         enablePredictiveAnimations.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@@ -101,7 +99,7 @@
             }
         });
 
-        CheckBox enableInPlaceChange = (CheckBox) findViewById(R.id.enableInPlaceChange);
+        CheckBox enableInPlaceChange = findViewById(R.id.enableInPlaceChange);
         enableInPlaceChange.setChecked(mEnableInPlaceChange);
         enableInPlaceChange.setOnCheckedChangeListener(
                 new CompoundButton.OnCheckedChangeListener() {
@@ -130,26 +128,26 @@
                 for (int i = mPendingSettleList.size() - 1; i >=0; i--) {
                     final MyViewHolder vh = mPendingSettleList.keyAt(i);
                     final long duration = mPendingSettleList.valueAt(i);
-                    ViewCompat.animate(vh.textView).translationX(0f).alpha(1f)
+                    vh.textView.animate().translationX(0f).alpha(1f)
                             .setDuration(duration).setListener(
-                            new ViewPropertyAnimatorListener() {
-                                @Override
-                                public void onAnimationStart(View view) {
-                                    dispatchAnimationStarted(vh);
-                                }
+                                    new AnimatorListenerAdapter() {
+                                        @Override
+                                        public void onAnimationStart(Animator animator) {
+                                            dispatchAnimationStarted(vh);
+                                        }
 
-                                @Override
-                                public void onAnimationEnd(View view) {
-                                    ViewCompat.setTranslationX(vh.textView, 0f);
-                                    ViewCompat.setAlpha(vh.textView, 1f);
-                                    dispatchAnimationFinished(vh);
-                                }
+                                        @Override
+                                        public void onAnimationEnd(Animator animator) {
+                                            vh.textView.setTranslationX(0f);
+                                            vh.textView.setAlpha(1f);
+                                            dispatchAnimationFinished(vh);
+                                        }
 
-                                @Override
-                                public void onAnimationCancel(View view) {
+                                        @Override
+                                        public void onAnimationCancel(Animator animator) {
 
-                                }
-                            }).start();
+                                        }
+                                    }).start();
                 }
                 mPendingSettleList.clear();
             }
@@ -230,7 +228,7 @@
                 if (pre.text.equals(post.text)) {
                     // same content. Just translate back to 0
                     final long duration = (long) (getChangeDuration()
-                            * (ViewCompat.getTranslationX(vh.textView) / vh.textView.getWidth()));
+                            * (vh.textView.getTranslationX() / vh.textView.getWidth()));
                     mPendingSettleList.put(vh, duration);
                     // we set it here because previous endAnimation would set it to other value.
                     vh.textView.setText(finalText);
@@ -274,7 +272,7 @@
         public ItemChangeAnimator(MyViewHolder viewHolder, CharSequence finalText, long duration) {
             mViewHolder = viewHolder;
             mMaxX = mViewHolder.itemView.getWidth();
-            mStartRatio = ViewCompat.getTranslationX(mViewHolder.textView) / mMaxX;
+            mStartRatio = mViewHolder.textView.getTranslationX() / mMaxX;
             mFinalText = finalText;
             mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
             mValueAnimator.addUpdateListener(this);
@@ -286,11 +284,11 @@
         void setFraction(float fraction) {
             fraction = mStartRatio + (1f - mStartRatio) * fraction;
             if (fraction < .5f) {
-                ViewCompat.setTranslationX(mViewHolder.textView, fraction * mMaxX);
-                ViewCompat.setAlpha(mViewHolder.textView, 1f - fraction);
+                mViewHolder.textView.setTranslationX(fraction * mMaxX);
+                mViewHolder.textView.setAlpha(1f - fraction);
             } else {
-                ViewCompat.setTranslationX(mViewHolder.textView, (1f - fraction) * mMaxX);
-                ViewCompat.setAlpha(mViewHolder.textView, fraction);
+                mViewHolder.textView.setTranslationX((1f - fraction) * mMaxX);
+                mViewHolder.textView.setAlpha(fraction);
                 maybeSetFinalText();
             }
         }
@@ -307,7 +305,7 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             maybeSetFinalText();
-            ViewCompat.setAlpha(mViewHolder.textView, 1f);
+            mViewHolder.textView.setAlpha(1f);
         }
 
         public void maybeSetFinalText() {
@@ -341,7 +339,7 @@
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
-        MenuItemCompat.setShowAsAction(menu.add("Layout"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+        menu.add("Layout").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         return true;
     }
 
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/AsyncListUtilActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/AsyncListUtilActivity.java
index 8ec3a23..e713012 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/AsyncListUtilActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/AsyncListUtilActivity.java
@@ -17,20 +17,19 @@
 
 package com.example.android.supportv7.widget;
 
-import com.example.android.supportv7.Cheeses;
-
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
+import android.support.v7.util.AsyncListUtil;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
-import android.support.v7.util.AsyncListUtil;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.example.android.supportv7.Cheeses;
+
 /**
  * A sample Activity to demonstrate capabilities of {@link AsyncListUtil}.
  */
@@ -60,7 +59,7 @@
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
-        MenuItemCompat.setShowAsAction(menu.add("Layout"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+        menu.add("Layout").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         return true;
     }
 
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java
index a4c01d1..f74d91a 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java
@@ -16,14 +16,7 @@
 
 package com.example.android.supportv7.widget;
 
-import com.example.android.supportv7.Cheeses;
-import com.example.android.supportv7.R;
-import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
-import com.example.android.supportv7.widget.util.ConfigToggle;
-import com.example.android.supportv7.widget.util.ConfigViewHolder;
-
 import android.app.Activity;
-import android.content.Context;
 import android.os.Bundle;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.LinearLayoutManager;
@@ -33,11 +26,16 @@
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.CheckBox;
-import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import com.example.android.supportv7.Cheeses;
+import com.example.android.supportv7.R;
+import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
+import com.example.android.supportv7.widget.util.ConfigToggle;
+import com.example.android.supportv7.widget.util.ConfigViewHolder;
+
 /**
  * A simple activity that can be extended to demonstrate LayoutManagers.
  * <p>
@@ -65,7 +63,7 @@
     abstract protected T createLayoutManager();
 
     private void initRecyclerView() {
-        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+        mRecyclerView = findViewById(R.id.recycler_view);
         mRecyclerView.setHasFixedSize(true);
         mLayoutManager = createLayoutManager();
         mRecyclerView.setLayoutManager(mLayoutManager);
@@ -101,7 +99,7 @@
 
     private void initToggles() {
         mConfigToggles = createConfigToggles();
-        RecyclerView configView = (RecyclerView) findViewById(R.id.config_recycler_view);
+        RecyclerView configView = findViewById(R.id.config_recycler_view);
         configView.setAdapter(mConfigAdapter);
         configView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,
                 false));
@@ -109,9 +107,9 @@
     }
 
     public void onScrollClicked(View view) {
-        final EditText scrollOffset = (EditText) findViewById(R.id.scroll_offset);
-        final CheckBox checkBox = (CheckBox) findViewById(R.id.enable_smooth_scroll);
-        final Spinner spinner = (Spinner) findViewById(R.id.spinner);
+        final EditText scrollOffset = findViewById(R.id.scroll_offset);
+        final CheckBox checkBox = findViewById(R.id.enable_smooth_scroll);
+        final Spinner spinner = findViewById(R.id.spinner);
 
         Integer offset = null;
         String offsetString = scrollOffset.getText().toString();
@@ -129,7 +127,7 @@
     }
 
     private void initSpinner() {
-        final Spinner spinner = (Spinner) findViewById(R.id.spinner);
+        final Spinner spinner = findViewById(R.id.spinner);
         spinner.setAdapter(new BaseAdapter() {
             @Override
             public int getCount() {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/DrawerLayoutActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/DrawerLayoutActivity.java
index c53ec8f..3ef6293 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/DrawerLayoutActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/DrawerLayoutActivity.java
@@ -91,10 +91,10 @@
 
         setContentView(R.layout.drawer_layout);
 
-        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
-        mStartDrawer = (ListView) findViewById(R.id.start_drawer);
-        mEndDrawer = (FrameLayout) findViewById(R.id.end_drawer);
-        mContent = (TextView) findViewById(R.id.content_text);
+        mDrawerLayout = findViewById(R.id.drawer_layout);
+        mStartDrawer = findViewById(R.id.start_drawer);
+        mEndDrawer = findViewById(R.id.end_drawer);
+        mContent = findViewById(R.id.content_text);
 
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow_end, GravityCompat.END);
@@ -110,7 +110,7 @@
 
         // Find the toolbar in our layout and set it as the support action bar on the activity.
         // This is required to have the drawer slide "over" the toolbar.
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar = findViewById(R.id.toolbar);
         mToolbar.setTitle(R.string.drawer_title);
         setSupportActionBar(mToolbar);
 
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java
index 1191be0..f086bdf 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java
@@ -110,6 +110,7 @@
         }
     }
 
+    @Override
     protected RecyclerView.Adapter createAdapter() {
         mAdapter = new SimpleStringAdapter(this, Cheeses.sCheeseStrings) {
             @Override
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/ListPopupWindowActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/ListPopupWindowActivity.java
index 9e4628e..f01b825 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/ListPopupWindowActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/ListPopupWindowActivity.java
@@ -56,7 +56,7 @@
 
         mDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
 
-        mContainer = (ViewGroup) findViewById(R.id.container);
+        mContainer = findViewById(R.id.container);
         mIsModal = (CheckBox) mContainer.findViewById(R.id.is_modal);
         mLog = (TextView) mContainer.findViewById(R.id.log);
         mButton = (Button) mContainer.findViewById(R.id.test_button);
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java
index cb0c2e2..95d5c94 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java
@@ -36,7 +36,7 @@
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.list_view_activity);
-        ListView listView = (ListView) findViewById(R.id.list_view);
+        ListView listView = findViewById(R.id.list_view);
         listView.setAdapter(new BaseAdapter() {
             @Override
             public int getCount() {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
index 88b9992..dff2940 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
@@ -146,6 +146,7 @@
         }
     }
 
+    @Override
     protected RecyclerView.Adapter createAdapter() {
         return new OuterAdapter(Cheeses.sCheeseStrings);
     }
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java
index 080fb5a..fa59903 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java
@@ -17,6 +17,7 @@
 package com.example.android.supportv7.widget;
 
 import android.os.Bundle;
+import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.PopupMenu;
 import android.support.v7.widget.SwitchCompat;
@@ -46,7 +47,7 @@
 
         mDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
 
-        final ViewGroup container = (ViewGroup) findViewById(R.id.container);
+        final ViewGroup container = findViewById(R.id.container);
         mLog = (TextView) container.findViewById(R.id.log);
 
         final SwitchCompat elevationToggle = (SwitchCompat) container.findViewById(
@@ -65,8 +66,14 @@
                     popupMenu = new PopupMenu(container.getContext(), button, Gravity.NO_GRAVITY,
                             0, R.style.CustomPopupNoElevation);
                 }
+
                 final MenuInflater menuInflater = popupMenu.getMenuInflater();
                 menuInflater.inflate(R.menu.popup_menu, popupMenu.getMenu());
+                final MenuItem editItem = popupMenu.getMenu().findItem(R.id.action_edit);
+                MenuItemCompat.setContentDescription(editItem,
+                        getString(R.string.popup_menu_edit_description));
+                MenuItemCompat.setTooltipText(editItem,
+                        getString(R.string.popup_menu_edit_tooltip));
 
                 // Register a listener to be notified when a menu item in our popup menu has
                 // been clicked.
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java
index a49ddbe..7d1cd36 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java
@@ -20,7 +20,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.widget.DividerItemDecoration;
 import android.support.v7.widget.RecyclerView;
 import android.util.DisplayMetrics;
@@ -76,7 +75,7 @@
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
-        MenuItemCompat.setShowAsAction(menu.add("Layout"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+        menu.add("Layout").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         return true;
     }
 
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/StableIdActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/StableIdActivity.java
index 8932704..a538408 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/StableIdActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/StableIdActivity.java
@@ -63,6 +63,7 @@
         return new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
     }
 
+    @Override
     protected RecyclerView.Adapter createAdapter() {
         return new StableIdAdapter(Cheeses.sCheeseStrings);
     }
@@ -116,7 +117,7 @@
                     final int pos = viewHolder.getAdapterPosition();
                     if (pos != RecyclerView.NO_POSITION) {
                         // swap item to top, and notify data set changed
-                        Pair d = mData.remove(pos);
+                        Pair<Integer, String> d = mData.remove(pos);
                         mData.add(0, d);
 
                         notifyDataSetChanged();
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/DragAndDropActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/DragAndDropActivity.java
index 7daa3eb..ee092c3 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/DragAndDropActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/DragAndDropActivity.java
@@ -16,19 +16,16 @@
 
 package com.example.android.supportv7.widget.touch;
 
-import com.example.android.supportv7.R;
-import com.example.android.supportv7.widget.util.ConfigToggle;
-
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.example.android.supportv7.R;
+import com.example.android.supportv7.widget.util.ConfigToggle;
+
 public class DragAndDropActivity extends ItemTouchHelperActivity {
 
     boolean mDragUpEnabled = true;
@@ -112,7 +109,7 @@
         vh.actionButton.setOnTouchListener(new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
-                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                     mItemTouchHelper.startDrag(vh);
                 }
                 return false;
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/ItemTouchHelperActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/ItemTouchHelperActivity.java
index 54a0bb3..679af89 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/ItemTouchHelperActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/ItemTouchHelperActivity.java
@@ -57,7 +57,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_item_touch);
-        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+        mRecyclerView = findViewById(R.id.recycler_view);
         mRecyclerView.setHasFixedSize(true);
         mAdapter = createAdapter();
         mRecyclerView.setAdapter(mAdapter);
@@ -69,7 +69,7 @@
 
     private void initToggles() {
         mConfigToggles = createConfigToggles();
-        RecyclerView configView = (RecyclerView) findViewById(R.id.config_recycler_view);
+        RecyclerView configView = findViewById(R.id.config_recycler_view);
         configView.setAdapter(new RecyclerView.Adapter<ConfigViewHolder>() {
             @Override
             public ConfigViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/SwipeToDismissActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/SwipeToDismissActivity.java
index 7444c23..14a7495 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/SwipeToDismissActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/touch/SwipeToDismissActivity.java
@@ -16,20 +16,19 @@
 
 package com.example.android.supportv7.widget.touch;
 
-import com.example.android.supportv7.R;
-import com.example.android.supportv7.widget.util.ConfigToggle;
-
 import android.annotation.TargetApi;
 import android.graphics.Canvas;
 import android.os.Build;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.example.android.supportv7.R;
+import com.example.android.supportv7.widget.util.ConfigToggle;
+
 public class SwipeToDismissActivity extends ItemTouchHelperActivity {
     boolean mSwipeStartEnabled = true;
     boolean mSwipeEndEnabled = true;
@@ -153,7 +152,7 @@
         vh.actionButton.setOnTouchListener(new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
-                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                     mItemTouchHelper.startSwipe(vh);
                 }
                 return false;
diff --git a/samples/SupportAnimationDemos/Android.mk b/samples/SupportAnimationDemos/Android.mk
deleted file mode 100644
index be0240f..0000000
--- a/samples/SupportAnimationDemos/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := SupportAnimationDemos
-LOCAL_MODULE_TAGS := samples
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 16
-LOCAL_DEX_PREOPT := false
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        android-support-dynamic-animation \
-        android-support-v4
-LOCAL_AAPT_FLAGS := --no-version-vectors
-include $(BUILD_PACKAGE)
diff --git a/samples/SupportAnimationDemos/AndroidManifest.xml b/samples/SupportAnimationDemos/AndroidManifest.xml
index 25e5ec8..243e6e4 100644
--- a/samples/SupportAnimationDemos/AndroidManifest.xml
+++ b/samples/SupportAnimationDemos/AndroidManifest.xml
@@ -16,9 +16,6 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.support.animation">
-
-    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="25" />
-
     <application android:label="@string/activity_sample_code"
             android:supportsRtl="true"
             android:icon="@drawable/app_sample_code"
diff --git a/samples/SupportAnimationDemos/build.gradle b/samples/SupportAnimationDemos/build.gradle
index dd3e350..d1ea614 100644
--- a/samples/SupportAnimationDemos/build.gradle
+++ b/samples/SupportAnimationDemos/build.gradle
@@ -1,7 +1,7 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-dynamic-animation')
+    implementation project(':support-dynamic-animation')
 }
 
 android {
@@ -9,6 +9,7 @@
 
     defaultConfig {
         minSdkVersion 16
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -18,8 +19,16 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
         abortOnError true
+        check 'NewApi'
     }
 
     compileOptions {
diff --git a/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java b/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java
index 1ded677..72a7edc 100644
--- a/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java
+++ b/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java
@@ -115,9 +115,9 @@
 
     // Setup seek bars so damping ratio and stiffness for the spring can be modified through the UI.
     void setupSeekBars() {
-        SeekBar dr = (SeekBar) findViewById(R.id.damping_ratio);
+        SeekBar dr = findViewById(R.id.damping_ratio);
         dr.setMax(130);
-        final TextView drTxt = (TextView) findViewById(R.id.damping_ratio_txt);
+        final TextView drTxt = findViewById(R.id.damping_ratio_txt);
         dr.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
@@ -142,9 +142,9 @@
             }
         });
 
-        SeekBar stiff = (SeekBar) findViewById(R.id.stiffness);
+        SeekBar stiff = findViewById(R.id.stiffness);
         stiff.setMax(110);
-        final TextView nfTxt = (TextView) findViewById(R.id.stiffness_txt);
+        final TextView nfTxt = findViewById(R.id.stiffness_txt);
         stiff.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
diff --git a/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java b/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java
index e9f2d62..26d4784 100644
--- a/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java
+++ b/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java
@@ -41,7 +41,7 @@
         setContentView(R.layout.activity_main);
 
         final View v = findViewById(R.id.container);
-        mSpringView = (SpringView) findViewById(R.id.actual_spring);
+        mSpringView = findViewById(R.id.actual_spring);
 
         final View img = findViewById(R.id.imageView);
         setupSeekBars();
@@ -100,9 +100,9 @@
 
     // Setup seek bars so damping ratio and stiffness for the spring can be modified through the UI.
     void setupSeekBars() {
-        SeekBar dr = (SeekBar) findViewById(R.id.damping_ratio);
+        SeekBar dr = findViewById(R.id.damping_ratio);
         dr.setMax(130);
-        final TextView drTxt = (TextView) findViewById(R.id.damping_ratio_txt);
+        final TextView drTxt = findViewById(R.id.damping_ratio_txt);
         dr.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
@@ -127,9 +127,9 @@
             }
         });
 
-        SeekBar stiff = (SeekBar) findViewById(R.id.stiffness);
+        SeekBar stiff = findViewById(R.id.stiffness);
         stiff.setMax(110);
-        final TextView nfTxt = (TextView) findViewById(R.id.stiffness_txt);
+        final TextView nfTxt = findViewById(R.id.stiffness_txt);
         stiff.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
diff --git a/samples/SupportAppNavigation/Android.mk b/samples/SupportAppNavigation/Android.mk
deleted file mode 100644
index ea9322f..0000000
--- a/samples/SupportAppNavigation/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_MODULE_TAGS := samples tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SupportAppNavigation
-
-LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v4
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/SupportAppNavigation/AndroidManifest.xml b/samples/SupportAppNavigation/AndroidManifest.xml
index c8e4fa8..4681b33 100644
--- a/samples/SupportAppNavigation/AndroidManifest.xml
+++ b/samples/SupportAppNavigation/AndroidManifest.xml
@@ -18,9 +18,6 @@
         android:versionName="1"
         xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.android.support.appnavigation">
-
-    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />
-
     <application android:label="@string/app_name">
         <activity android:name=".app.AppNavHomeActivity"
                 android:label="@string/app_nav_home_label">
diff --git a/samples/SupportAppNavigation/build.gradle b/samples/SupportAppNavigation/build.gradle
new file mode 100644
index 0000000..c7d03b8
--- /dev/null
+++ b/samples/SupportAppNavigation/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+dependencies {
+    implementation project(':support-v4')
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
+    }
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.res.srcDirs = ['res']
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
+    lintOptions {
+        abortOnError true
+        check 'NewApi'
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
diff --git a/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/ContentViewActivity.java b/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/ContentViewActivity.java
index 42e25ea..a2b8f3c 100644
--- a/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/ContentViewActivity.java
+++ b/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/ContentViewActivity.java
@@ -40,10 +40,10 @@
 
         Intent intent = getIntent();
         if (Intent.ACTION_VIEW.equals(intent.getAction())) {
-            TextView tv = (TextView) findViewById(R.id.status_text);
+            TextView tv = findViewById(R.id.status_text);
             tv.setText("Viewing content from ACTION_VIEW");
         } else if (intent.hasExtra(EXTRA_TEXT)) {
-            TextView tv = (TextView) findViewById(R.id.status_text);
+            TextView tv = findViewById(R.id.status_text);
             tv.setText(intent.getStringExtra(EXTRA_TEXT));
         }
     }
diff --git a/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/PeerActivity.java b/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/PeerActivity.java
index a6dbca4..5304acb 100644
--- a/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/PeerActivity.java
+++ b/samples/SupportAppNavigation/src/com/example/android/support/appnavigation/app/PeerActivity.java
@@ -41,7 +41,7 @@
         ActionBarCompat.setDisplayHomeAsUpEnabled(this, true);
 
         mPeerCount = getIntent().getIntExtra(EXTRA_PEER_COUNT, 0) + 1;
-        TextView tv = (TextView) findViewById(R.id.peer_counter);
+        TextView tv = findViewById(R.id.peer_counter);
         tv.setText(getResources().getText(R.string.peer_count).toString() + mPeerCount);
     }
 
diff --git a/samples/SupportDesignDemos/Android.mk b/samples/SupportDesignDemos/Android.mk
deleted file mode 100644
index 4c74ad2..0000000
--- a/samples/SupportDesignDemos/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := SupportDesignDemos
-LOCAL_MODULE_TAGS := samples
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 7
-LOCAL_DEX_PREOPT := false
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        android-support-v4 \
-        android-support-v7-appcompat \
-        android-support-v7-recyclerview \
-        android-support-transition \
-        android-support-design
-LOCAL_AAPT_FLAGS := --no-version-vectors
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-include $(BUILD_PACKAGE)
diff --git a/samples/SupportDesignDemos/AndroidManifest.xml b/samples/SupportDesignDemos/AndroidManifest.xml
index 29b042c..bedbea9 100644
--- a/samples/SupportDesignDemos/AndroidManifest.xml
+++ b/samples/SupportDesignDemos/AndroidManifest.xml
@@ -22,8 +22,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.support.design">
 
-    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
-
     <application android:label="@string/activity_sample_code"
             android:supportsRtl="true"
             android:icon="@drawable/app_sample_code"
diff --git a/samples/SupportDesignDemos/build.gradle b/samples/SupportDesignDemos/build.gradle
index b03e63e..48dfc9c 100644
--- a/samples/SupportDesignDemos/build.gradle
+++ b/samples/SupportDesignDemos/build.gradle
@@ -1,14 +1,16 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-design')
+    implementation project(':support-design')
 }
 
 android {
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
-        minSdkVersion 9
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
+        vectorDrawables.useSupportLibrary = true
     }
 
     sourceSets {
@@ -18,8 +20,16 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
-        abortOnError false
+        abortOnError true
+        check 'NewApi'
     }
 
     compileOptions {
diff --git a/samples/SupportDesignDemos/res/menu/navigation.xml b/samples/SupportDesignDemos/res/menu/navigation.xml
index de17967..4e61bf7 100644
--- a/samples/SupportDesignDemos/res/menu/navigation.xml
+++ b/samples/SupportDesignDemos/res/menu/navigation.xml
@@ -59,6 +59,7 @@
                     android:title="@string/navigation_sub_item_2"/>
             <item
                     android:id="@+id/navigation_sub_item_3"
+                    android:title="@string/navigation_sub_item_3"
                     app:actionLayout="@layout/action_layout_custom"/>
         </menu>
     </item>
diff --git a/samples/SupportDesignDemos/res/menu/sample_actions.xml b/samples/SupportDesignDemos/res/menu/sample_actions.xml
index b869ebc..aab91ed 100644
--- a/samples/SupportDesignDemos/res/menu/sample_actions.xml
+++ b/samples/SupportDesignDemos/res/menu/sample_actions.xml
@@ -18,11 +18,11 @@
     <item android:id="@+id/action_search"
           android:title="@string/menu_search"
           android:icon="@drawable/ic_search"
-          app:showAsAction="ifRoom"/>
+          android:showAsAction="ifRoom"/>
     <item android:id="@+id/action_toggle_expand"
           android:title="@string/menu_expand"
-          app:showAsAction="never" />
+          android:showAsAction="never" />
     <item android:id="@+id/action_toggle_collapse"
           android:title="@string/menu_collapse"
-          app:showAsAction="never" />
+          android:showAsAction="never" />
 </menu>
diff --git a/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml b/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
index d6d4761..f485789 100644
--- a/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
+++ b/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
@@ -13,14 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
     <item android:id="@+id/action_search"
           android:title="@string/menu_search"
+          app:contentDescription="@string/menu_search"
+          app:tooltipText="@string/menu_search"
           android:icon="@drawable/ic_search"/>
     <item android:id="@+id/action_settings"
           android:title="@string/menu_settings"
+          app:contentDescription="@string/menu_settings"
+          app:tooltipText="@string/menu_settings"
           android:icon="@drawable/ic_add"/>
     <item android:id="@+id/action_music"
           android:title="@string/tab_text"
+          app:contentDescription="@string/tab_text"
+          app:tooltipText="@string/tab_text"
           android:icon="@drawable/ic_action_navigation_menu"/>
-</menu>
\ No newline at end of file
+</menu>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/AppBarLayoutUsageBase.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/AppBarLayoutUsageBase.java
index edb7493..6a35e30 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/AppBarLayoutUsageBase.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/AppBarLayoutUsageBase.java
@@ -45,7 +45,7 @@
         setContentView(getLayoutId());
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
 
         CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)
@@ -57,22 +57,22 @@
             collapsingToolbarLayout.setContentScrimColor(0xFFFF00FF);
         }
 
-        TextView dialog = (TextView) findViewById(R.id.textview_dialogue);
+        TextView dialog = findViewById(R.id.textview_dialogue);
         if (dialog != null) {
             dialog.setText(TextUtils.concat(Shakespeare.DIALOGUE));
         }
 
-        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.appbar_recyclerview);
+        RecyclerView recyclerView = findViewById(R.id.appbar_recyclerview);
         if (recyclerView != null) {
             setupRecyclerView(recyclerView);
         }
 
-        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
+        TabLayout tabLayout = findViewById(R.id.tabs);
         if (tabLayout != null) {
             setupTabs(tabLayout);
         }
 
-        final SwipeRefreshLayout refreshLayout = (SwipeRefreshLayout) findViewById(R.id.swiperefresh);
+        final SwipeRefreshLayout refreshLayout = findViewById(R.id.swiperefresh);
         if (refreshLayout != null) {
             refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                 private final Handler mHandler = new Handler();
@@ -101,12 +101,12 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case R.id.action_toggle_expand: {
-                AppBarLayout abl = (AppBarLayout) findViewById(R.id.app_bar);
+                AppBarLayout abl = findViewById(R.id.app_bar);
                 abl.setExpanded(true);
                 return true;
             }
             case R.id.action_toggle_collapse: {
-                AppBarLayout abl = (AppBarLayout) findViewById(R.id.app_bar);
+                AppBarLayout abl = findViewById(R.id.app_bar);
                 abl.setExpanded(false);
                 return true;
             }
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
index e19609a..0f86ba6 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
@@ -38,9 +38,9 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.design_bottom_navigation_view);
-        Button buttonDisable = (Button) findViewById(R.id.button_disable);
+        Button buttonDisable = findViewById(R.id.button_disable);
         final BottomNavigationView bottom =
-                (BottomNavigationView) findViewById(R.id.bottom_navigation);
+                findViewById(R.id.bottom_navigation);
         mOriginalTint = bottom.getItemIconTintList();
         buttonDisable.setOnClickListener(new View.OnClickListener() {
             @Override
@@ -48,7 +48,7 @@
                 bottom.getMenu().getItem(0).setEnabled(!bottom.getMenu().getItem(0).isEnabled());
             }
         });
-        Button buttonAdd = (Button) findViewById(R.id.button_add);
+        Button buttonAdd = findViewById(R.id.button_add);
         buttonAdd.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -58,7 +58,7 @@
                 }
             }
         });
-        Button buttonRemove = (Button) findViewById(R.id.button_remove);
+        Button buttonRemove = findViewById(R.id.button_remove);
         buttonRemove.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -67,7 +67,7 @@
                 }
             }
         });
-        Button buttonTint = (Button) findViewById(R.id.button_tint);
+        Button buttonTint = findViewById(R.id.button_tint);
         buttonTint.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -78,7 +78,7 @@
                 }
             }
         });
-        Button buttonNext = (Button) findViewById(R.id.button_select_next);
+        Button buttonNext = findViewById(R.id.button_select_next);
         buttonNext.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -97,7 +97,7 @@
                 bottom.setSelectedItemId(bottom.getMenu().getItem(next).getItemId());
             }
         });
-        final TextView selectedItem = (TextView) findViewById(R.id.selected_item);
+        final TextView selectedItem = findViewById(R.id.selected_item);
         bottom.setOnNavigationItemSelectedListener(
                 new BottomNavigationView.OnNavigationItemSelectedListener() {
                     @Override
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetDynamicContent.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetDynamicContent.java
index 3ee3e17..20a29e0 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetDynamicContent.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetDynamicContent.java
@@ -81,7 +81,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.design_bottom_sheet_dynamic);
 
-        RecyclerView list = (RecyclerView) findViewById(R.id.list);
+        RecyclerView list = findViewById(R.id.list);
         list.setLayoutManager(new LinearLayoutManager(this));
         mAdapter = new DynamicAdapter();
         for (int i = 0; i < 5; i++) {
@@ -90,19 +90,19 @@
         list.setAdapter(mAdapter);
         mBehavior = BottomSheetBehavior.from(list);
 
-        Button add = (Button) findViewById(R.id.add);
+        Button add = findViewById(R.id.add);
         if (add != null) {
             add.setOnClickListener(mOnClickListener);
         }
-        Button remove = (Button) findViewById(R.id.remove);
+        Button remove = findViewById(R.id.remove);
         if (remove != null) {
             remove.setOnClickListener(mOnClickListener);
         }
-        Button expand = (Button) findViewById(R.id.expand);
+        Button expand = findViewById(R.id.expand);
         if (expand != null) {
             expand.setOnClickListener(mOnClickListener);
         }
-        Button collapse = (Button) findViewById(R.id.collapse);
+        Button collapse = findViewById(R.id.collapse);
         if (collapse != null) {
             collapse.setOnClickListener(mOnClickListener);
         }
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetHideable.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetHideable.java
index b649463..f1d94ae 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetHideable.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetHideable.java
@@ -44,8 +44,8 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mTextSlideOffset = (TextView) findViewById(R.id.slide_offset);
-        mToggle = (Button) findViewById(R.id.toggle);
+        mTextSlideOffset = findViewById(R.id.slide_offset);
+        mToggle = findViewById(R.id.toggle);
         mToggle.setOnClickListener(mOnClickListener);
         mBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
             @Override
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetWithFab.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetWithFab.java
index f08cd32..af3021d 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetWithFab.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomSheetWithFab.java
@@ -56,7 +56,7 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mToggle = (Button) findViewById(R.id.toggle);
+        mToggle = findViewById(R.id.toggle);
         mToggle.setOnClickListener(mOnClickListener);
         mBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
             @Override
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CoordinatorLayoutInset.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CoordinatorLayoutInset.java
index 2d977b0..27bac85 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CoordinatorLayoutInset.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CoordinatorLayoutInset.java
@@ -30,7 +30,7 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.design_coordinatorlayout_inset);
-        TextView message = (TextView) findViewById(R.id.message);
+        TextView message = findViewById(R.id.message);
         message.setText(Shakespeare.DIALOGUE[0]);
     }
 
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java
index 163cce2..4369d94 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java
@@ -19,7 +19,6 @@
 import android.os.Bundle;
 import android.support.design.widget.BaseTransientBottomBar;
 import android.support.design.widget.CoordinatorLayout;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -38,7 +37,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.custom_snackbar_with_fab);
 
-        mContentView = (CoordinatorLayout) findViewById(R.id.content_view);
+        mContentView = findViewById(R.id.content_view);
     }
 
     /** Shows a custom snackbar with no action. */
@@ -51,15 +50,15 @@
                 new BaseTransientBottomBar.ContentViewCallback() {
                     @Override
                     public void animateContentIn(int delay, int duration) {
-                        ViewCompat.setAlpha(content, 0f);
-                        ViewCompat.animate(content).alpha(1f).setDuration(duration)
+                        content.setAlpha(0f);
+                        content.animate().alpha(1f).setDuration(duration)
                                 .setStartDelay(delay).start();
                     }
 
                     @Override
                     public void animateContentOut(int delay, int duration) {
-                        ViewCompat.setAlpha(content, 1f);
-                        ViewCompat.animate(content).alpha(0f).setDuration(duration)
+                        content.setAlpha(1f);
+                        content.animate().alpha(0f).setDuration(duration)
                                 .setStartDelay(delay).start();
                     }
                 };
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/FloatingActionButtonUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/FloatingActionButtonUsage.java
index 9193037..e49a0e6 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/FloatingActionButtonUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/FloatingActionButtonUsage.java
@@ -33,7 +33,7 @@
         setContentView(R.layout.design_fab);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
     }
 
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsage.java
index 9404cc4..809a248 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsage.java
@@ -38,7 +38,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+        mDrawerLayout = findViewById(R.id.drawer_layout);
 
         // Set the color of status bar
         TypedValue value = new TypedValue();
@@ -46,7 +46,7 @@
         mDrawerLayout.setStatusBarBackgroundColor(value.data);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
 
         // Toggle icon
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java
index fc3fefb..4dac9c1 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java
@@ -35,10 +35,10 @@
         super.onCreate(savedInstanceState);
         setContentView(getLayout());
 
-        mTextMessage = (TextView) findViewById(R.id.message);
+        mTextMessage = findViewById(R.id.message);
 
         // Menu
-        NavigationView navigation = (NavigationView) findViewById(R.id.navigation);
+        NavigationView navigation = findViewById(R.id.navigation);
         navigation.setNavigationItemSelectedListener(getNavigationItemSelectedListener());
         navigation.inflateHeaderView(R.layout.design_navigation_header);
     }
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java
index 2f9b79f..3bd06f3 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java
@@ -16,17 +16,15 @@
 
 package com.example.android.support.design.widget;
 
-import com.example.android.support.design.R;
-
 import android.content.Context;
-import android.graphics.Color;
 import android.support.v7.widget.RecyclerView;
-import android.text.Layout;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.example.android.support.design.R;
+
 import java.util.ArrayList;
 import java.util.Collections;
 
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java
index 85f2152..7c5f7be 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java
@@ -37,7 +37,7 @@
         super.onCreate(savedInstanceState);
         setContentView(getLayoutId());
 
-        mContentView = (ViewGroup) findViewById(R.id.content_view);
+        mContentView = findViewById(R.id.content_view);
     }
 
     protected int getLayoutId() {
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java
index 1b79543..14df2b1 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java
@@ -18,12 +18,6 @@
 
 import com.example.android.support.design.R;
 
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.view.ViewGroup;
-
 /**
  * This demonstrates idiomatic usage of Snackbar with a Floating Action Button present
  */
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutCustomItemsUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutCustomItemsUsage.java
index 31239be..8271f1a 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutCustomItemsUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutCustomItemsUsage.java
@@ -33,12 +33,12 @@
         setContentView(R.layout.design_tabs_custom);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
         // Create three tabs with custom views
-        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
+        TabLayout tabLayout = findViewById(R.id.tabs);
         for (int i = 0; i < 3; i++) {
             TabLayout.Tab tab = tabLayout.newTab();
 
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java
index 7e51de3..c75b44b 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java
@@ -16,24 +16,11 @@
 
 package com.example.android.support.design.widget;
 
-import com.example.android.support.design.Cheeses;
-import com.example.android.support.design.R;
-
 import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.TextView;
 
-import java.util.ArrayList;
-import java.util.Random;
+import com.example.android.support.design.R;
 
 /**
  * This demonstrates idiomatic usage of TabLayout with items inflated from the layout
@@ -46,7 +33,7 @@
         setContentView(R.layout.design_tabs_item);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
     }
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java
index 12e1842..a1e6414 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java
@@ -29,11 +29,11 @@
 import android.widget.RadioButton;
 import android.widget.RadioGroup;
 import android.widget.TextView;
+
 import com.example.android.support.design.Cheeses;
 import com.example.android.support.design.R;
 
 import java.util.ArrayList;
-import java.util.Random;
 
 /**
  * This demonstrates idiomatic usage of TabLayout with a ViewPager
@@ -50,12 +50,12 @@
         setContentView(R.layout.design_tabs_viewpager);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
-        mTabLayout = (TabLayout) findViewById(R.id.tabs);
-        mViewPager = (ViewPager) findViewById(R.id.tabs_viewpager);
+        mTabLayout = findViewById(R.id.tabs);
+        mViewPager = findViewById(R.id.tabs_viewpager);
 
         findViewById(R.id.buttons).setVisibility(View.GONE);
 
@@ -82,7 +82,7 @@
                 break;
         }
 
-        RadioGroup rg = (RadioGroup) findViewById(R.id.radiogroup_tab_mode);
+        RadioGroup rg = findViewById(R.id.radiogroup_tab_mode);
         rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(RadioGroup radioGroup, int id) {
@@ -107,7 +107,7 @@
                 break;
         }
 
-        rg = (RadioGroup) findViewById(R.id.radiogroup_tab_gravity);
+        rg = findViewById(R.id.radiogroup_tab_gravity);
         rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(RadioGroup radioGroup, int id) {
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutUsage.java
index 387e1a0..274909a 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutUsage.java
@@ -53,12 +53,12 @@
         setContentView(R.layout.design_tabs_viewpager);
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
-        mTabLayout = (TabLayout) findViewById(R.id.tabs);
-        mViewPager = (ViewPager) findViewById(R.id.tabs_viewpager);
+        mTabLayout = findViewById(R.id.tabs);
+        mViewPager = findViewById(R.id.tabs_viewpager);
 
         mPagerAdapter = new CheesePagerAdapter();
         mViewPager.setAdapter(mPagerAdapter);
@@ -94,7 +94,7 @@
                 break;
         }
 
-        RadioGroup rg = (RadioGroup) findViewById(R.id.radiogroup_tab_mode);
+        RadioGroup rg = findViewById(R.id.radiogroup_tab_mode);
         rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(RadioGroup radioGroup, int id) {
@@ -119,7 +119,7 @@
                 break;
         }
 
-        rg = (RadioGroup) findViewById(R.id.radiogroup_tab_gravity);
+        rg = findViewById(R.id.radiogroup_tab_gravity);
         rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(RadioGroup radioGroup, int id) {
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
index 5fb61f5..3dd646a 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
@@ -36,8 +36,8 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.design_text_input);
 
-        mUsernameInputLayout = (TextInputLayout) findViewById(R.id.input_username);
-        mPasswordInputLayout = (TextInputLayout) findViewById(R.id.input_password);
+        mUsernameInputLayout = findViewById(R.id.input_username);
+        mPasswordInputLayout = findViewById(R.id.input_password);
     }
 
     public void showError(View view) {
diff --git a/samples/SupportEmojiDemos/AndroidManifest.xml b/samples/SupportEmojiDemos/AndroidManifest.xml
new file mode 100755
index 0000000..5117637
--- /dev/null
+++ b/samples/SupportEmojiDemos/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?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.
+  -->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.example.android.support.text.emoji">
+
+    <application
+        android:name=".EmojiCompatApplication"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.EmojiCompat"
+        tools:ignore="GoogleAppIndexingWarning">
+
+        <activity
+            android:name=".MainActivity"
+            android:windowSoftInputMode="stateUnchanged">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
diff --git a/samples/SupportEmojiDemos/build.gradle b/samples/SupportEmojiDemos/build.gradle
new file mode 100644
index 0000000..c5be42e
--- /dev/null
+++ b/samples/SupportEmojiDemos/build.gradle
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+ext {
+    fontDir = project(':noto-emoji-compat').projectDir
+}
+
+dependencies {
+    implementation project(':support-emoji')
+    implementation project(':support-emoji-bundled')
+    implementation project(':support-emoji-appcompat')
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
+    }
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.res.srcDirs = ['res']
+        main.assets.srcDirs = [new File(fontDir, "supported-emojis").getAbsolutePath()]
+    }
+
+    lintOptions {
+        abortOnError true
+        check 'NewApi'
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
\ No newline at end of file
diff --git a/samples/SupportEmojiDemos/res/layout/activity_main.xml b/samples/SupportEmojiDemos/res/layout/activity_main.xml
new file mode 100755
index 0000000..9e44f69
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/fragment_container"
+    tools:context="com.example.android.support.text.emoji.MainActivity"/>
\ No newline at end of file
diff --git a/samples/SupportEmojiDemos/res/layout/fragment_list.xml b/samples/SupportEmojiDemos/res/layout/fragment_list.xml
new file mode 100644
index 0000000..7f563fc
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/layout/fragment_list.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:paddingLeft="@dimen/spacing_normal"
+             android:paddingRight="@dimen/spacing_normal">
+
+    <ListView
+        android:id="@+id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/SupportEmojiDemos/res/layout/fragment_main.xml b/samples/SupportEmojiDemos/res/layout/fragment_main.xml
new file mode 100644
index 0000000..f83076e
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/layout/fragment_main.xml
@@ -0,0 +1,86 @@
+<?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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:paddingLeft="@dimen/spacing_normal"
+             android:paddingRight="@dimen/spacing_normal">
+
+    <ScrollView
+        android:id="@+id/scroll"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:id="@+id/container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:padding="@dimen/spacing_normal">
+
+            <com.example.android.support.text.emoji.ConfigLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_small"/>
+
+            <android.support.text.emoji.widget.EmojiTextView
+                android:id="@+id/emoji_text_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_small"/>
+
+            <android.support.text.emoji.widget.EmojiAppCompatEditText
+                android:id="@+id/emoji_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_small"
+                android:textSize="@dimen/text_size"/>
+
+            <android.support.text.emoji.widget.EmojiAppCompatButton
+                android:id="@+id/emoji_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_small"
+                android:textSize="@dimen/text_size"/>
+
+            <TextView
+                android:id="@+id/regular_text_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_small"
+                android:textSize="@dimen/text_size"/>
+
+            <com.example.android.support.text.emoji.CustomTextView
+                android:id="@+id/emoji_custom_text_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="@dimen/text_size"/>
+
+            <android.support.text.emoji.widget.EmojiAppCompatButton
+                android:id="@+id/emoji_list_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_small"
+                android:textSize="@dimen/text_size"
+                android:text="Show all emojis"/>
+
+        </LinearLayout>
+
+    </ScrollView>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/SupportEmojiDemos/res/layout/layout_config.xml b/samples/SupportEmojiDemos/res/layout/layout_config.xml
new file mode 100644
index 0000000..14a8724
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/layout/layout_config.xml
@@ -0,0 +1,59 @@
+<?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.
+  -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="horizontal">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <Switch
+            android:id="@+id/enable"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="EmojiCompat"
+            android:layout_marginBottom="@dimen/spacing_small"/>
+
+        <Switch
+            android:id="@+id/replaceAll"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Replace All"
+            android:layout_marginBottom="@dimen/spacing_small"/>
+
+        <Switch
+            android:id="@+id/useDownloadable"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Downloadable"
+            android:layout_marginBottom="@dimen/spacing_small"/>
+
+        <Switch
+            android:id="@+id/indicator"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Indicator"
+            android:layout_marginBottom="@dimen/spacing_small"/>
+
+    </LinearLayout>
+
+</merge>
+
diff --git a/samples/SupportEmojiDemos/res/layout/list_item_emoji.xml b/samples/SupportEmojiDemos/res/layout/list_item_emoji.xml
new file mode 100644
index 0000000..61b7d0e
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/layout/list_item_emoji.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"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <android.support.text.emoji.widget.EmojiAppCompatTextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/emoji"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:lineSpacingMultiplier="1.2"
+        android:textSize="24sp"/>
+
+    <android.support.text.emoji.widget.EmojiAppCompatTextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:lineSpacingMultiplier="1.2"
+        android:textSize="14sp"
+        android:layout_marginLeft="@dimen/spacing_small"/>
+</LinearLayout>
diff --git a/samples/SupportEmojiDemos/res/mipmap-hdpi/ic_launcher.png b/samples/SupportEmojiDemos/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..9e0d97a
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportEmojiDemos/res/mipmap-mdpi/ic_launcher.png b/samples/SupportEmojiDemos/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..79d226b
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportEmojiDemos/res/mipmap-xhdpi/ic_launcher.png b/samples/SupportEmojiDemos/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..42716ab
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportEmojiDemos/res/mipmap-xxhdpi/ic_launcher.png b/samples/SupportEmojiDemos/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..4be1ff9
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportEmojiDemos/res/mipmap-xxxhdpi/ic_launcher.png b/samples/SupportEmojiDemos/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ea78de3
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportEmojiDemos/res/values/colors.xml b/samples/SupportEmojiDemos/res/values/colors.xml
new file mode 100755
index 0000000..7c4be27
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?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="primary">#3F51B5</color>
+    <color name="primary_dark">#303F9F</color>
+    <color name="accent">#009688</color>
+</resources>
diff --git a/samples/SupportEmojiDemos/res/values/dimens.xml b/samples/SupportEmojiDemos/res/values/dimens.xml
new file mode 100755
index 0000000..c944340
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?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>
+    <dimen name="spacing_normal">16dp</dimen>
+    <dimen name="spacing_small">8dp</dimen>
+    <dimen name="text_size">20sp</dimen>
+</resources>
diff --git a/samples/SupportEmojiDemos/res/values/font_certs.xml b/samples/SupportEmojiDemos/res/values/font_certs.xml
new file mode 100755
index 0000000..b77122a
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/values/font_certs.xml
@@ -0,0 +1,36 @@
+<?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>
+    <array name="com_google_android_gms_fonts_certs">
+        <item>@array/com_google_android_gms_fonts_certs_dev</item>
+        <item>@array/com_google_android_gms_fonts_certs_prod</item>
+    </array>
+    <string-array name="com_google_android_gms_fonts_certs_dev">
+        <item>
+            MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+        </item>
+    </string-array>
+    <string-array name="com_google_android_gms_fonts_certs_prod">
+        <item>
+            MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+        </item>
+    </string-array>
+
+    <string name="provider_authority">com.google.android.gms.fonts</string>
+    <string name="provider_package">com.google.android.gms</string>
+    <string name="font_query">Noto Color Emoji Compat</string>
+</resources>
diff --git a/samples/SupportEmojiDemos/res/values/strings.xml b/samples/SupportEmojiDemos/res/values/strings.xml
new file mode 100755
index 0000000..cddb5f4
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?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>
+    <string name="app_name">EmojiCompat</string>
+    <string name="emoji_text_view">Emoji TextView %s</string>
+    <string name="emoji_edit_text">Emoji EditText %s</string>
+    <string name="emoji_button">Emoji Button %s</string>
+    <string name="regular_text_view">Regular TextView %s</string>
+    <string name="custom_text_view">Custom TextView %s</string>
+</resources>
diff --git a/samples/SupportEmojiDemos/res/values/styles.xml b/samples/SupportEmojiDemos/res/values/styles.xml
new file mode 100755
index 0000000..9a8973f
--- /dev/null
+++ b/samples/SupportEmojiDemos/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?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="Theme.EmojiCompat" parent="Theme.AppCompat.Light.DarkActionBar">
+        <item name="colorPrimary">@color/primary</item>
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <item name="colorAccent">@color/accent</item>
+    </style>
+
+</resources>
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/Config.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/Config.java
new file mode 100644
index 0000000..ea58121
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/Config.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.FontRequestEmojiCompatConfig;
+import android.support.text.emoji.bundled.BundledEmojiCompatConfig;
+import android.support.v4.provider.FontRequest;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class Config {
+    private static final String TAG = "EmojiDemo";
+
+    public static final String PREF_NAME = "emojicompat";
+    public static final String KEY_ENABLED = "enabled";
+    public static final String KEY_REPLACE_ALL = "replaceAll";
+    public static final String KEY_DOWNLOADABLE = "downloadable";
+    public static final String KEY_INDICATOR = "indicator";
+    private static Config sInstance;
+
+    private SharedPreferences mSharedPref;
+    private Context mContext;
+    private boolean mCompatEnabled;
+    private boolean mReplaceAll;
+    private boolean mDownloadable;
+    private boolean mIndicator;
+
+    private Set<Listener> mListeners = new HashSet<>();
+
+    private Config() {
+    }
+
+    void init(Context context) {
+        this.mContext = context;
+        mSharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+        mCompatEnabled = mSharedPref.getBoolean(KEY_ENABLED, false);
+        mReplaceAll = mSharedPref.getBoolean(KEY_REPLACE_ALL, false);
+        mDownloadable = mSharedPref.getBoolean(KEY_DOWNLOADABLE, false);
+        mIndicator = mSharedPref.getBoolean(KEY_INDICATOR, false);
+        resetEmojiCompat();
+    }
+
+    static synchronized Config get() {
+        if (sInstance == null) {
+            sInstance = new Config();
+        }
+        return sInstance;
+    }
+
+    void registerListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    void unregisterListener(Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    void update(boolean compatEnabled, boolean replaceAll, boolean downloadable,
+            boolean indicator) {
+        mCompatEnabled = compatEnabled;
+        mReplaceAll = replaceAll;
+        mDownloadable = downloadable;
+        mIndicator = indicator;
+        mSharedPref.edit().putBoolean(KEY_ENABLED, mCompatEnabled).apply();
+        mSharedPref.edit().putBoolean(KEY_REPLACE_ALL, mReplaceAll).apply();
+        mSharedPref.edit().putBoolean(KEY_DOWNLOADABLE, mDownloadable).apply();
+        mSharedPref.edit().putBoolean(KEY_INDICATOR, mIndicator).apply();
+        resetEmojiCompat();
+        for (Listener listener : mListeners) {
+            listener.onEmojiCompatUpdated();
+        }
+    }
+
+    private void resetEmojiCompat() {
+        final EmojiCompat.Config config;
+        if (mCompatEnabled) {
+            if (mDownloadable) {
+                final FontRequest fontRequest = new FontRequest(
+                        mContext.getString(R.string.provider_authority),
+                        mContext.getString(R.string.provider_package),
+                        mContext.getString(R.string.font_query),
+                        R.array.com_google_android_gms_fonts_certs);
+
+                config = new FontRequestEmojiCompatConfig(mContext, fontRequest);
+            } else {
+                config = new BundledEmojiCompatConfig(mContext);
+            }
+        } else {
+            config = new EmojiCompat.Config(new EmojiCompat.MetadataRepoLoader() {
+                @Override
+                public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
+                    loaderCallback.onFailed(new RuntimeException("Disable"));
+                }
+            }) {
+            };
+        }
+
+        config.setReplaceAll(mReplaceAll)
+                .setEmojiSpanIndicatorEnabled(mIndicator)
+                .registerInitCallback(new EmojiCompat.InitCallback() {
+                    @Override
+                    public void onInitialized() {
+                        Log.i(TAG, "EmojiCompat initialized");
+                    }
+
+                    @Override
+                    public void onFailed(@Nullable Throwable throwable) {
+                        Log.e(TAG, "EmojiCompat initialization failed", throwable);
+                    }
+                });
+
+        EmojiCompat.reset(config);
+    }
+
+    boolean isCompatEnabled() {
+        return mCompatEnabled;
+    }
+
+    boolean isReplaceAll() {
+        return mCompatEnabled && mReplaceAll;
+    }
+
+    boolean isDownloadable() {
+        return mCompatEnabled && mDownloadable;
+    }
+
+    boolean isIndicator() {
+        return mCompatEnabled && mIndicator;
+    }
+
+    interface Listener {
+        void onEmojiCompatUpdated();
+    }
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/ConfigLayout.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/ConfigLayout.java
new file mode 100644
index 0000000..3d05c69
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/ConfigLayout.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+
+/**
+ * Layout that includes configuration parameters.
+ */
+public class ConfigLayout extends LinearLayout {
+    private Switch mEnableEmojiCompat;
+    private Switch mReplaceAll;
+    private Switch mDownloadable;
+    private Switch mIndicator;
+
+    public ConfigLayout(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public ConfigLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
+    public ConfigLayout(Context context, AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public ConfigLayout(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(context);
+    }
+
+    private void init(Context context) {
+        setOrientation(VERTICAL);
+        LayoutInflater.from(context).inflate(R.layout.layout_config, this, true);
+
+        mEnableEmojiCompat = findViewById(R.id.enable);
+        mEnableEmojiCompat.setChecked(Config.get().isCompatEnabled());
+        mEnableEmojiCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        fireListener();
+                    }
+                });
+            }
+        });
+
+        mReplaceAll = findViewById(R.id.replaceAll);
+        mReplaceAll.setChecked(Config.get().isReplaceAll());
+        mReplaceAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        fireListener();
+                    }
+                });
+            }
+        });
+
+        mDownloadable = findViewById(R.id.useDownloadable);
+        mDownloadable.setChecked(Config.get().isDownloadable());
+        mDownloadable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        fireListener();
+                    }
+                });
+            }
+        });
+
+        mIndicator = findViewById(R.id.indicator);
+        mIndicator.setChecked(Config.get().isIndicator());
+        mIndicator.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        fireListener();
+                    }
+                });
+            }
+        });
+
+    }
+
+    void fireListener() {
+        Config.get().update(mEnableEmojiCompat.isChecked(), mReplaceAll.isChecked(),
+                mDownloadable.isChecked(), mIndicator.isChecked());
+    }
+
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/CustomTextView.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/CustomTextView.java
new file mode 100755
index 0000000..e063c2a
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/CustomTextView.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.text.emoji.widget.EmojiTextViewHelper;
+import android.support.v7.widget.AppCompatTextView;
+import android.text.InputFilter;
+import android.util.AttributeSet;
+
+
+/**
+ * A sample implementation of custom TextView.
+ *
+ * <p>You can use {@link EmojiTextViewHelper} to make your custom TextView compatible with
+ * EmojiCompat.</p>
+ */
+public class CustomTextView extends AppCompatTextView {
+
+    private EmojiTextViewHelper mEmojiTextViewHelper;
+
+    public CustomTextView(Context context) {
+        this(context, null);
+    }
+
+    public CustomTextView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CustomTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        getEmojiTextViewHelper().updateTransformationMethod();
+    }
+
+    @Override
+    public void setFilters(InputFilter[] filters) {
+        super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+    }
+
+    @Override
+    public void setAllCaps(boolean allCaps) {
+        super.setAllCaps(allCaps);
+        getEmojiTextViewHelper().setAllCaps(allCaps);
+    }
+
+    /**
+     * Returns the {@link EmojiTextViewHelper} for this TextView.
+     *
+     * <p>This method can be called from super constructors through {@link
+     * #setFilters(InputFilter[])} or {@link #setAllCaps(boolean)}.</p>
+     */
+    private EmojiTextViewHelper getEmojiTextViewHelper() {
+        if (mEmojiTextViewHelper == null) {
+            mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+        }
+        return mEmojiTextViewHelper;
+    }
+
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/EmojiCompatApplication.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/EmojiCompatApplication.java
new file mode 100755
index 0000000..e1168eb
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/EmojiCompatApplication.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.app.Application;
+
+
+/**
+ * This application uses EmojiCompat.
+ */
+public class EmojiCompatApplication extends Application {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        EmojiRepo.load(this);
+        Config.get().init(this);
+    }
+
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/EmojiRepo.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/EmojiRepo.java
new file mode 100644
index 0000000..0e10289
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/EmojiRepo.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+class EmojiRepo {
+    private static final String TAG = "EmojiData";
+    private static List<EmojiData> sEmojis = new ArrayList<>();
+
+    private EmojiRepo() {
+    }
+
+    static List<EmojiData> getEmojis() {
+        return sEmojis;
+    }
+
+    static synchronized void load(final Context context) {
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    read(context);
+                } catch (Throwable t) {
+                    Log.e(TAG, "Cannot load emojis", t);
+                }
+            }
+        }).run();
+    }
+
+    private static void read(Context context) throws IOException {
+        final InputStream inputStream = context.getAssets().open("emojis.txt");
+        try {
+            final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+            final StringBuilder stringBuilder = new StringBuilder();
+            final StringBuilder codepointBuilder = new StringBuilder();
+            List<Integer> codepointsList;
+
+            String s;
+            while ((s = reader.readLine()) != null) {
+                s = s.trim();
+                // skip comments
+                if (s.isEmpty() || s.startsWith("#")) continue;
+
+                stringBuilder.setLength(0);
+                codepointBuilder.setLength(0);
+                codepointsList = new ArrayList<>();
+
+                // emoji codepoints are space separated: i.e. 0x1f1e6 0x1f1e8
+                final String[] split = s.split(" ");
+
+                for (int index = 0; index < split.length; index++) {
+                    final String part = split[index].trim();
+                    int codepoint = Integer.parseInt(part, 16);
+                    codepointsList.add(codepoint);
+                    codepointBuilder.append(String.format("u+%04x", codepoint));
+                    codepointBuilder.append(" ");
+                    stringBuilder.append(Character.toChars(codepoint));
+                }
+                final EmojiData emojiData = new EmojiData(stringBuilder.toString(), codepointsList,
+                        codepointBuilder.toString());
+                sEmojis.add(emojiData);
+            }
+        } finally {
+            inputStream.close();
+        }
+    }
+
+    static class EmojiData {
+        private String mEmoji;
+        private List<Integer> mCodepoints;
+        private String mCodepointString;
+
+        EmojiData(String emoji, List<Integer> codepoints, String codepointString) {
+            mEmoji = emoji;
+            mCodepoints = codepoints;
+            mCodepointString = codepointString;
+        }
+
+        public String getEmoji() {
+            return mEmoji;
+        }
+
+        public List<Integer> getCodepoints() {
+            return mCodepoints;
+        }
+
+        public String getCodepointString() {
+            return mCodepointString;
+        }
+    }
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/ListFragment.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/ListFragment.java
new file mode 100644
index 0000000..85a1ca9
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/ListFragment.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * UI to list all supported emojis.
+ */
+
+public class ListFragment extends Fragment {
+
+    private ListView mListView;
+
+    static ListFragment newInstance() {
+        ListFragment fragment = new ListFragment();
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.fragment_list, container, false);
+        mListView = view.findViewById(R.id.list);
+        loadList();
+        return view;
+    }
+
+    private void loadList() {
+        final ArrayAdapter<EmojiRepo.EmojiData> adapter = new MyArrayAdapter(getActivity(),
+                R.layout.list_item_emoji, R.id.text, EmojiRepo.getEmojis());
+        final int index = mListView.getFirstVisiblePosition();
+        mListView.setAdapter(adapter);
+        mListView.setSelection(index);
+    }
+
+    private static class MyArrayAdapter extends ArrayAdapter<EmojiRepo.EmojiData> {
+        MyArrayAdapter(Context context, int resource, int textViewResourceId,
+                List<EmojiRepo.EmojiData> objects) {
+            super(context, resource, textViewResourceId, objects);
+        }
+
+        @NonNull
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View view = super.getView(position, convertView, parent);
+            ViewHolder holder = (ViewHolder) view.getTag();
+            if (holder == null) {
+                holder = new ViewHolder();
+                holder.mEmojiTextView = view.findViewById(R.id.emoji);
+                holder.mTextView = view.findViewById(R.id.text);
+            }
+
+            EmojiRepo.EmojiData item = getItem(position);
+            holder.mEmojiTextView.setText(item.getEmoji());
+            holder.mTextView.setText(item.getCodepointString());
+            holder.mTextView.setContentDescription(holder.mTextView.getText());
+            return view;
+        }
+    }
+
+    private static class ViewHolder {
+        TextView mEmojiTextView;
+        TextView mTextView;
+    }
+
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/MainActivity.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/MainActivity.java
new file mode 100755
index 0000000..1358c40
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/MainActivity.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+/**
+ * Main activity.
+ */
+public class MainActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        if (savedInstanceState == null) {
+            getSupportFragmentManager()
+                    .beginTransaction()
+                    .add(R.id.fragment_container, MainFragment.newInstance())
+                    .commitNowAllowingStateLoss();
+        }
+    }
+
+    void showAllEmojis() {
+        getSupportFragmentManager()
+                .beginTransaction()
+                .addToBackStack(null)
+                .replace(R.id.fragment_container, ListFragment.newInstance())
+                .commit();
+    }
+
+}
diff --git a/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/MainFragment.java b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/MainFragment.java
new file mode 100644
index 0000000..88ca735
--- /dev/null
+++ b/samples/SupportEmojiDemos/src/com/example/android/support/text/emoji/MainFragment.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.text.emoji;
+
+import android.os.Bundle;
+import android.support.text.emoji.EmojiCompat;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Main fragment.
+ */
+public class MainFragment extends Fragment {
+
+    // [U+1F469] (WOMAN) + [U+200D] (ZERO WIDTH JOINER) + [U+1F4BB] (PERSONAL COMPUTER)
+    private static final String WOMAN_TECHNOLOGIST = "\uD83D\uDC69\u200D\uD83D\uDCBB";
+
+    // [U+1F469] (WOMAN) + [U+200D] (ZERO WIDTH JOINER) + [U+1F3A4] (MICROPHONE)
+    private static final String WOMAN_SINGER = "\uD83D\uDC69\u200D\uD83C\uDFA4";
+
+    static final String EMOJI = WOMAN_TECHNOLOGIST + " " + WOMAN_SINGER;
+
+    private TextView mEmojiTextView;
+    private TextView mEmojiEditText;
+    private TextView mEmojiButton;
+    private TextView mRegularTextView;
+    private TextView mCustomTextView;
+
+    final Config.Listener mConfigListener = new Config.Listener() {
+        @Override
+        public void onEmojiCompatUpdated() {
+            init();
+        }
+    };
+
+    static MainFragment newInstance() {
+        MainFragment fragment = new MainFragment();
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.fragment_main, container, false);
+
+        // TextView variant provided by EmojiCompat library
+        mEmojiTextView = view.findViewById(R.id.emoji_text_view);
+        // EditText variant provided by EmojiCompat library
+        mEmojiEditText = view.findViewById(R.id.emoji_edit_text);
+        // Button variant provided by EmojiCompat library
+        mEmojiButton = view.findViewById(R.id.emoji_button);
+        // Regular TextView without EmojiCompat support; you have to manually process the text
+        mRegularTextView = view.findViewById(R.id.regular_text_view);
+        // Custom TextView
+        mCustomTextView = view.findViewById(R.id.emoji_custom_text_view);
+
+        final TextView emojiListButton = view.findViewById(R.id.emoji_list_button);
+        emojiListButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                ((MainActivity) getActivity()).showAllEmojis();
+            }
+        });
+
+        init();
+        return view;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        Config.get().registerListener(mConfigListener);
+    }
+
+    @Override
+    public void onStop() {
+        Config.get().unregisterListener(mConfigListener);
+        super.onStop();
+    }
+
+    private void init() {
+        mEmojiTextView.setText(getString(R.string.emoji_text_view, EMOJI));
+        mEmojiEditText.setText(getString(R.string.emoji_edit_text, EMOJI));
+        mEmojiButton.setText(getString(R.string.emoji_button, EMOJI));
+        mRegularTextView.setText(getString(R.string.regular_text_view, EMOJI));
+        EmojiCompat.get().registerInitCallback(new EmojiCompat.InitCallback() {
+            @Override
+            public void onInitialized() {
+                final EmojiCompat compat = EmojiCompat.get();
+                mRegularTextView.setText(
+                        compat.process(getString(R.string.regular_text_view, EMOJI)));
+            }
+        });
+        mCustomTextView.setText(getString(R.string.custom_text_view, EMOJI));
+    }
+}
diff --git a/samples/SupportLeanbackDemos/Android.mk b/samples/SupportLeanbackDemos/Android.mk
deleted file mode 100644
index 5a8d110..0000000
--- a/samples/SupportLeanbackDemos/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := SupportLeanbackDemos
-LOCAL_MODULE_TAGS := samples tests
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 17
-LOCAL_DEX_PREOPT := false
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        android-support-compat \
-        android-support-core-ui \
-        android-support-media-compat \
-        android-support-fragment \
-        android-support-v7-recyclerview \
-        android-support-v17-leanback \
-        android-support-v17-preference-leanback \
-        android-support-v7-preference \
-        android-support-v14-preference
-
-include $(BUILD_PACKAGE)
diff --git a/samples/SupportLeanbackDemos/AndroidManifest.xml b/samples/SupportLeanbackDemos/AndroidManifest.xml
index ea3c1ee..010c297 100644
--- a/samples/SupportLeanbackDemos/AndroidManifest.xml
+++ b/samples/SupportLeanbackDemos/AndroidManifest.xml
@@ -4,9 +4,9 @@
     android:versionCode="1"
     android:versionName="1.0">
 
-    <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="23" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 
     <application
         android:label="@string/app_name"
@@ -75,6 +75,22 @@
             android:theme="@style/Theme.Example.Leanback.Rows"
             android:exported="true" />
 
+        <activity android:name="PlaybackTransportControlActivity"
+            android:configChanges=
+                "screenSize|smallestScreenSize|screenLayout|orientation"
+            android:resizeableActivity="true"
+            android:supportsPictureInPicture="true"
+            android:launchMode="singleTask"
+            android:exported="true" />
+
+        <activity android:name="PlaybackTransportControlSupportActivity"
+            android:configChanges=
+                "screenSize|smallestScreenSize|screenLayout|orientation"
+            android:resizeableActivity="true"
+            android:supportsPictureInPicture="true"
+            android:launchMode="singleTask"
+            android:exported="true" />
+
         <activity android:name="PlaybackActivity"
             android:configChanges=
                 "screenSize|smallestScreenSize|screenLayout|orientation"
@@ -173,5 +189,8 @@
 
         <activity android:name=".VideoSupportActivity"
             android:exported="true" />
+
+        <activity android:name=".VideoActivityWithDetailedCard"
+                  android:exported="true" />
     </application>
 </manifest>
diff --git a/samples/SupportLeanbackDemos/build.gradle b/samples/SupportLeanbackDemos/build.gradle
index e0cc44b..4ec403e 100644
--- a/samples/SupportLeanbackDemos/build.gradle
+++ b/samples/SupportLeanbackDemos/build.gradle
@@ -1,8 +1,8 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-leanback-v17')
-    compile project(':support-preference-leanback-v17')
+    implementation project(':support-leanback-v17')
+    implementation project(':support-preference-leanback-v17')
 }
 
 android {
@@ -10,6 +10,7 @@
 
     defaultConfig {
         minSdkVersion 17
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -19,8 +20,16 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
         abortOnError false
+        check 'NewApi'
     }
 
     compileOptions {
diff --git a/samples/SupportLeanbackDemos/generatev4.py b/samples/SupportLeanbackDemos/generatev4.py
index c79f1b1..6a44e17 100755
--- a/samples/SupportLeanbackDemos/generatev4.py
+++ b/samples/SupportLeanbackDemos/generatev4.py
@@ -309,6 +309,38 @@
 file.close()
 outfile.close()
 
+file = open('src/com/example/android/leanback/PlaybackTransportControlFragment.java', 'r')
+outfile = open('src/com/example/android/leanback/PlaybackTransportControlSupportFragment.java', 'w')
+write_java_head(outfile, "PlaybackTransportControlFragment")
+for line in file:
+    line = line.replace('PlaybackFragment', 'PlaybackSupportFragment')
+    line = line.replace('PlaybackTransportControlFragment', 'PlaybackTransportControlSupportFragment')
+    line = line.replace('PlaybackTransportControlActivity', 'PlaybackTransportControlSupportActivity')
+    outfile.write(line)
+file.close()
+outfile.close()
+
+file = open('src/com/example/android/leanback/PlaybackTransportControlActivity.java', 'r')
+outfile = open('src/com/example/android/leanback/PlaybackTransportControlSupportActivity.java', 'w')
+write_java_head(outfile, "PlaybackTransportControlActivity")
+for line in file:
+    line = line.replace('PlaybackTransportControlActivity', 'PlaybackTransportControlSupportActivity')
+    line = line.replace('extends Activity', 'extends FragmentActivity')
+    line = line.replace('R.layout.playback_transportcontrol_activity', 'R.layout.playback_transportcontrol_activity_support')
+    line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+    outfile.write(line)
+file.close()
+outfile.close()
+
+file = open('res/layout/playback_transportcontrol_activity.xml', 'r')
+outfile = open('res/layout/playback_transportcontrol_activity_support.xml', 'w')
+for line in file:
+    line = replace_xml_head(line, "playback_transportcontrols")
+    line = line.replace('com.example.android.leanback.PlaybackTransportControlFragment', 'com.example.android.leanback.PlaybackTransportControlSupportFragment')
+    outfile.write(line)
+file.close()
+outfile.close()
+
 
 
 file = open('src/com/example/android/leanback/PlaybackOverlayFragment.java', 'r')
diff --git a/samples/SupportLeanbackDemos/res/drawable/google_map.jpg b/samples/SupportLeanbackDemos/res/drawable/google_map.jpg
new file mode 100644
index 0000000..447de09
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable/google_map.jpg
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/layout/playback_transportcontrol_activity.xml b/samples/SupportLeanbackDemos/res/layout/playback_transportcontrol_activity.xml
new file mode 100644
index 0000000..f89f349
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/playback_transportcontrol_activity.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <fragment
+        android:id="@+id/playback_controls_fragment"
+        android:name="com.example.android.leanback.PlaybackTransportControlFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/SupportLeanbackDemos/res/layout/playback_transportcontrol_activity_support.xml b/samples/SupportLeanbackDemos/res/layout/playback_transportcontrol_activity_support.xml
new file mode 100644
index 0000000..0b74364
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/playback_transportcontrol_activity_support.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This file is auto-generated from playback_transportcontrols.xml.  DO NOT MODIFY. -->
+
+<!--
+     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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <fragment
+        android:id="@+id/playback_controls_fragment"
+        android:name="com.example.android.leanback.PlaybackTransportControlSupportFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/SupportLeanbackDemos/res/layout/video_activity_detailed_card.xml b/samples/SupportLeanbackDemos/res/layout/video_activity_detailed_card.xml
new file mode 100644
index 0000000..ddacf0f
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/video_activity_detailed_card.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:id="@+id/videoFragment">
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/SupportLeanbackDemos/res/values/strings.xml b/samples/SupportLeanbackDemos/res/values/strings.xml
index 6de7d7b..4e4a398 100644
--- a/samples/SupportLeanbackDemos/res/values/strings.xml
+++ b/samples/SupportLeanbackDemos/res/values/strings.xml
@@ -79,6 +79,8 @@
     <string name="onboarding">Onboarding</string>
     <string name="onboarding_description">Show onboarding activity.</string>
     <string name="onboarding_support">Onboarding(support version)</string>
+    <string name="video_play_with_detail_card">A video play activity with detail card</string>
+    <string name="video_play_with_detail_card_description">A video play activity with detail card</string>
 
     <!-- Strings related to guided sequence activity -->
     <string name="guidedstep_first_title">First</string>
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
index 6c1ce85..7b3f8f7 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
@@ -14,7 +14,6 @@
 package com.example.android.leanback;
 
 import android.app.Fragment;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
@@ -106,6 +105,7 @@
 
         // simulates in a real world use case  data being loaded two seconds later
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 setupRows();
                 loadData();
@@ -289,6 +289,7 @@
             setAdapter(adapter);
             // simulates late data loading:
             new Handler().postDelayed(new Runnable() {
+                @Override
                 public void run() {
                     loadFragmentData();
                 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
index fa1425b..395c498 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
@@ -17,7 +17,6 @@
 package com.example.android.leanback;
 
 import android.support.v4.app.Fragment;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
@@ -109,6 +108,7 @@
 
         // simulates in a real world use case  data being loaded two seconds later
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 setupRows();
                 loadData();
@@ -292,6 +292,7 @@
             setAdapter(adapter);
             // simulates late data loading:
             new Handler().postDelayed(new Runnable() {
+                @Override
                 public void run() {
                     loadFragmentData();
                 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
index 6d376f0..57eae06 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
@@ -14,7 +14,6 @@
 package com.example.android.leanback;
 
 import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
-import android.support.v17.leanback.widget.DetailsOverviewRow;
 
 public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter {
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
index 56acc05..bb282f4 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
@@ -183,6 +183,7 @@
         }
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 final Context context = getActivity();
                 DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
@@ -198,6 +199,7 @@
         }, TIME_TO_LOAD_OVERVIEW_ROW_MS);
 
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 for (int i = 0; i < NUM_ROWS; ++i) {
                     ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
index 4f8546e..e6cf32f 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
@@ -69,7 +69,10 @@
                 .build());
     }
 
-    private static class SetupFragment extends GuidedStepFragment {
+    /**
+     * Fragment hosted in DetailsPresenterSelectionActivity.
+     */
+    public static class SetupFragment extends GuidedStepFragment {
 
         @Override
         public Guidance onCreateGuidance(Bundle savedInstanceState) {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
index e58e2e7..8e7a127 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
@@ -186,6 +186,7 @@
         }
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 final Context context = getActivity();
                 DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
@@ -201,6 +202,7 @@
         }, TIME_TO_LOAD_OVERVIEW_ROW_MS);
 
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 for (int i = 0; i < NUM_ROWS; ++i) {
                     ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java
index a311338..e7831d6 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java
@@ -376,8 +376,10 @@
             mExpandPaymentListInOnCreateView = true;
         }
 
+        @Override
         public GuidedActionsStylist onCreateActionsStylist() {
             return new GuidedActionsStylist() {
+                @Override
                 protected void setupImeOptions(GuidedActionsStylist.ViewHolder vh,
                         GuidedAction action) {
                     if (action.getId() == PASSWORD) {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java
index 0cd6edd..717fccd 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java
@@ -379,8 +379,10 @@
             mExpandPaymentListInOnCreateView = true;
         }
 
+        @Override
         public GuidedActionsStylist onCreateActionsStylist() {
             return new GuidedActionsStylist() {
+                @Override
                 protected void setupImeOptions(GuidedActionsStylist.ViewHolder vh,
                         GuidedAction action) {
                     if (action.getId() == PASSWORD) {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
index fcd8e65..4293dbb 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
@@ -60,43 +60,45 @@
             addAction(actions, BrowseSupportActivity.class, R.string.browse_support,
                     R.string.browse_support_description);
             addAction(actions, SearchActivity.class, R.string.search, R.string.search_description);
-            addAction(actions, SearchSupportActivity.class, R.string.search_support, R.string.search_support_description);
+            addAction(actions, SearchSupportActivity.class, R.string.search_support,
+                    R.string.search_support_description);
 
             addAction(actions, DetailsActivity.class, R.string.details,
                     R.string.details_description);
-            actions.get(actions.size()-1).getIntent().putExtra(DetailsActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(DetailsActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
             addAction(actions, DetailsSupportActivity.class, R.string.details_support,
                     R.string.details_support_description);
-            actions.get(actions.size()-1).getIntent().putExtra(DetailsSupportActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(DetailsSupportActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
 
             addAction(actions, DetailsVideoActivity.class, R.string.details_video,
                     R.string.details_video_description);
-            actions.get(actions.size()-1).getIntent().putExtra(DetailsActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(DetailsActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
             addAction(actions, DetailsVideoSupportActivity.class, R.string.details_video_support,
                     R.string.details_video_support_description);
-            actions.get(actions.size()-1).getIntent().putExtra(DetailsSupportActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(DetailsSupportActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
 
             addAction(actions, DetailsCustomTitleActivity.class, R.string.details_custom_title,
                     R.string.details_custom_title_description);
-            actions.get(actions.size()-1).getIntent().putExtra(DetailsActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(DetailsActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
             addAction(actions, DetailsCustomTitleSupportActivity.class,
                     R.string.details_custom_title_support,
                     R.string.details_custom_title_support_description);
-            actions.get(actions.size()-1).getIntent().putExtra(DetailsSupportActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(DetailsSupportActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
 
             addAction(actions, SearchDetailsActivity.class, R.string.search_details,
                     R.string.search_details_description);
-            actions.get(actions.size()-1).getIntent().putExtra(SearchDetailsActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(SearchDetailsActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
             addAction(actions, SearchDetailsSupportActivity.class, R.string.search_details_support,
                     R.string.search_details_support_description);
-            actions.get(actions.size()-1).getIntent().putExtra(SearchDetailsSupportActivity.EXTRA_ITEM,
+            actions.get(actions.size() - 1).getIntent().putExtra(
+                    SearchDetailsSupportActivity.EXTRA_ITEM,
                     new PhotoItem("Hello world", R.drawable.gallery_photo_1));
             addAction(actions, VerticalGridActivity.class, R.string.vgrid,
                     R.string.vgrid_description);
@@ -109,16 +111,16 @@
             addAction(actions, GuidedStepHalfScreenActivity.class, R.string.guidedstephalfscreen,
                     R.string.guidedstep_description);
             addAction(actions, GuidedStepSupportHalfScreenActivity.class,
-                R.string.guidedstepsupporthalfscreen,
-                R.string.guidedstep_description);
+                    R.string.guidedstepsupporthalfscreen,
+                    R.string.guidedstep_description);
             addAction(actions, BrowseErrorActivity.class, R.string.browseerror,
                     R.string.browseerror_description);
             addAction(actions, BrowseErrorSupportActivity.class, R.string.browseerror_support,
                     R.string.browseerror_support_description);
-            addAction(actions, PlaybackActivity.class, R.string.playback,
+            addAction(actions, PlaybackTransportControlActivity.class, R.string.playback,
                     R.string.playback_description);
-            addAction(actions, PlaybackSupportActivity.class, R.string.playback_support,
-                    R.string.playback_support_description);
+            addAction(actions, PlaybackTransportControlSupportActivity.class,
+                    R.string.playback_support, R.string.playback_support_description);
             addAction(actions, PlaybackOverlayActivity.class, R.string.playbackoverlay,
                     R.string.playbackoverlay_description);
             addAction(actions, PlaybackOverlaySupportActivity.class,
@@ -141,6 +143,9 @@
             addAction(actions, OnboardingSupportActivity.class,
                     R.string.onboarding_support,
                     R.string.onboarding_description);
+            addAction(actions, VideoActivityWithDetailedCard.class,
+                    R.string.video_play_with_detail_card,
+                    R.string.video_play_with_detail_card_description);
         }
 
         private void addAction(List<GuidedAction> actions, Class cls, int titleRes, int descRes) {
@@ -162,4 +167,10 @@
         }
 
     }
+
+    @Override
+    protected void onDestroy() {
+        MovieData.clear();
+        super.onDestroy();
+    }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/MovieData.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MovieData.java
new file mode 100644
index 0000000..af88184
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MovieData.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+class MovieData {
+    static final int STATUS_INIT = 0;
+    static final int STATUS_OWN = 1;
+    static final int STATUS_RENTED = 2;
+
+    static int sStatus = STATUS_INIT;
+
+    static void clear() {
+        sStatus = STATUS_INIT;
+    }
+}
+
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
index 9b801dc..4d5b4cc 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
@@ -16,11 +16,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.os.Build;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.app.DetailsFragmentBackgroundController;
+import android.support.v17.leanback.media.MediaPlayerAdapter;
 import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlue;
+import android.support.v17.leanback.media.PlaybackTransportControlGlue;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -44,9 +47,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 public class NewDetailsFragment extends android.support.v17.leanback.app.DetailsFragment {
+
     private static final String TAG = "leanback.DetailsFragment";
     private static final String ITEM = "item";
 
@@ -59,9 +62,7 @@
     private static final int ACTION_RENT = 2;
     private static final int ACTION_BUY = 3;
 
-    private boolean TEST_OVERVIEW_ROW_ON_SECOND;
-    private boolean TEST_SHARED_ELEMENT_TRANSITION;
-    private boolean TEST_ENTRANCE_TRANSITION;
+    private boolean TEST_SHARED_ELEMENT_TRANSITION = true;
     private boolean TEST_BACKGROUND_PLAYER;
 
     private static final long TIME_TO_LOAD_OVERVIEW_ROW_MS = 1000;
@@ -76,29 +77,56 @@
     private final DetailsFragmentBackgroundController mDetailsBackground =
             new DetailsFragmentBackgroundController(this);
 
-    private void initializeTest() {
-        TEST_SHARED_ELEMENT_TRANSITION = Build.VERSION.SDK_INT >= 21
-                && null != getActivity().getWindow().getSharedElementEnterTransition();
-        TEST_OVERVIEW_ROW_ON_SECOND = !TEST_SHARED_ELEMENT_TRANSITION;
-        TEST_ENTRANCE_TRANSITION = false;
+    void setupTrailerVideo() {
+        MediaPlayerGlue mediaPlayerGlue = new MediaPlayerGlue(getActivity());
+        mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
+        mediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ONE);
+        mediaPlayerGlue.setArtist("A Googler");
+        mediaPlayerGlue.setTitle("Diving with Sharks Trailer");
+        mediaPlayerGlue.setMediaSource(Uri.parse("android.resource://com.example.android.leanback/"
+                + "raw/browse"));
+    }
+
+    void setupMainVideo() {
+        Context context = getActivity();
+        MediaPlayerAdapter adapter = new MediaPlayerAdapter(context);
+        PlaybackTransportControlGlue<MediaPlayerAdapter> mediaPlayerGlue =
+                new PlaybackTransportControlGlue(context, adapter);
+        mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
+        mediaPlayerGlue.setSubtitle("A Googler");
+        mediaPlayerGlue.setTitle("Diving with Sharks");
+        mediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(
+                "https://storage.googleapis.com/android-tv/Sample videos/April Fool's "
+                        + "2013/Explore Treasure Mode with Google Maps.mp4"));
+        mediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            @Override
+            public void onPreparedStateChanged(PlaybackGlue glue) {
+                super.onPreparedStateChanged(glue);
+                PlaybackTransportControlGlue controlGlue = (PlaybackTransportControlGlue) glue;
+                controlGlue.setSeekProvider(new PlaybackSeekDiskDataProvider(
+                        controlGlue.getDuration(), 1000,
+                        "/sdcard/seek/frame_%04d.jpg"));
+            }
+        });
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
-        initializeTest();
 
         mBackgroundHelper = new BackgroundHelper(getActivity());
         mDetailsBackground.enableParallax();
         if (TEST_BACKGROUND_PLAYER) {
-            MediaPlayerGlue mediaPlayerGlue = new MediaPlayerGlue(getActivity());
-            mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
-
-            mediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
-            mediaPlayerGlue.setArtist("A Googleer");
-            mediaPlayerGlue.setTitle("Diving with Sharks");
-            mediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
+            if (MovieData.sStatus == MovieData.STATUS_INIT) {
+                // not own/rented, play trailer
+                setupTrailerVideo();
+            } else {
+                // bought or rented, play the main content
+                setupMainVideo();
+                // hide details main ui
+                mDetailsBackground.switchToVideo();
+            }
         }
 
         final Context context = getActivity();
@@ -127,9 +155,7 @@
             @Override
             public void onActionClicked(Action action) {
                 final Context context = getActivity();
-                Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show();
-                int indexOfOverviewRow = TEST_OVERVIEW_ROW_ON_SECOND ? 1 : 0;
-                DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(indexOfOverviewRow);
+                DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(0);
                 if (action.getId() == ACTION_BUY) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
@@ -137,25 +163,45 @@
                     actions.set(ACTION_PLAY, mActionPlay);
                     actions.clear(ACTION_RENT);
                     actions.clear(ACTION_BUY);
-                    dor.setItem(mPhotoItem.getTitle() + "(Owned)");
+                    boolean previousRented = MovieData.sStatus == MovieData.STATUS_RENTED;
+                    MovieData.sStatus = MovieData.STATUS_OWN;
+                    dor.setItem(getDisplayTitle(mPhotoItem.getTitle()));
                     dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
                             R.drawable.details_img_16x9, context.getTheme()));
+                    if (TEST_BACKGROUND_PLAYER) {
+                        if (!previousRented) {
+                            setupMainVideo();
+                            mDetailsBackground.switchToVideo();
+                        }
+                    } else {
+                        Intent intent = new Intent(context, PlaybackOverlayActivity.class);
+                        getActivity().startActivity(intent);
+                    }
                 } else if (action.getId() == ACTION_RENT) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
                             dor.getActionsAdapter();
                     actions.set(ACTION_PLAY, mActionPlay);
                     actions.clear(ACTION_RENT);
-                    dor.setItem(mPhotoItem.getTitle() + "(Rented)");
+                    MovieData.sStatus = MovieData.STATUS_RENTED;
+                    dor.setItem(getDisplayTitle(mPhotoItem.getTitle()));
+                    if (TEST_BACKGROUND_PLAYER) {
+                        setupMainVideo();
+                        mDetailsBackground.switchToVideo();
+                    } else {
+                        Intent intent = new Intent(context, PlaybackOverlayActivity.class);
+                        getActivity().startActivity(intent);
+                    }
                 } else if (action.getId() == ACTION_PLAY) {
-                    Intent intent = new Intent(context, PlaybackOverlayActivity.class);
-                    getActivity().startActivity(intent);
+                    if (TEST_BACKGROUND_PLAYER) {
+                        mDetailsBackground.switchToVideo();
+                    } else {
+                        Intent intent = new Intent(context, PlaybackOverlayActivity.class);
+                        getActivity().startActivity(intent);
+                    }
                 }
             }
         });
-        if (TEST_OVERVIEW_ROW_ON_SECOND) {
-            dorPresenter.setInitialState(FullWidthDetailsOverviewRowPresenter.STATE_SMALL);
-        }
 
         ps.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
         ps.addClassPresenter(ListRow.class, new ListRowPresenter());
@@ -166,7 +212,7 @@
         setOnItemViewClickedListener(new OnItemViewClickedListener() {
             @Override
             public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                                      RowPresenter.ViewHolder rowViewHolder, Row row) {
+                    RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemClicked: " + item + " row " + row);
                 if (item instanceof PhotoItem) {
                     Intent intent = new Intent(getActivity(), DetailsActivity.class);
@@ -183,7 +229,7 @@
         setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                                       RowPresenter.ViewHolder rowViewHolder, Row row) {
+                    RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemSelected: " + item + " row " + row);
             }
         });
@@ -197,18 +243,11 @@
         } else {
             dorPresenter.setParticipatingEntranceTransition(true);
         }
-        if (TEST_ENTRANCE_TRANSITION) {
-            // don't run entrance transition if Activity is restored.
-            if (savedInstanceState == null) {
-                prepareEntranceTransition();
-            }
-        }
-
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
+            Bundle savedInstanceState) {
         View view = super.onCreateView(inflater, container, savedInstanceState);
         return view;
     }
@@ -222,33 +261,53 @@
         updateAdapter();
     }
 
+    static String getDisplayTitle(String title) {
+        switch (MovieData.sStatus) {
+            case MovieData.STATUS_OWN:
+                title = title + "(Owned)";
+                break;
+            case MovieData.STATUS_RENTED:
+                title = title + "(Rented)";
+                break;
+            case MovieData.STATUS_INIT:
+            default:
+        }
+        return title;
+    }
+
     void updateAdapter() {
         if (mRowsAdapter == null) {
             return;
         }
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
-                if (TEST_OVERVIEW_ROW_ON_SECOND) {
-                    ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
-                    listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1));
-                    listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2));
-                    listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3));
-                    listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4));
-                    HeaderItem header = new HeaderItem(0, "Search Result");
-                    mRowsAdapter.add(0, new ListRow(header, listRowAdapter));
+                final Context context = getActivity();
+                if (context == null) {
+                    return;
                 }
 
-                final Context context = getActivity();
-                DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
+                DetailsOverviewRow dor = new DetailsOverviewRow(
+                        getDisplayTitle(mPhotoItem.getTitle()));
                 dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
                         mPhotoItem.getImageResourceId(), context.getTheme()));
                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
-                adapter.set(ACTION_RENT, mActionRent);
-                adapter.set(ACTION_BUY, mActionBuy);
+                switch (MovieData.sStatus) {
+                    case MovieData.STATUS_INIT:
+                        adapter.set(ACTION_RENT, mActionRent);
+                        adapter.set(ACTION_BUY, mActionBuy);
+                        break;
+                    case MovieData.STATUS_OWN:
+                        adapter.set(ACTION_PLAY, mActionPlay);
+                        break;
+                    case MovieData.STATUS_RENTED:
+                        adapter.set(ACTION_PLAY, mActionPlay);
+                        adapter.set(ACTION_BUY, mActionBuy);
+                        break;
+                }
                 dor.setActionsAdapter(adapter);
-                int indexOfOverviewRow = TEST_OVERVIEW_ROW_ON_SECOND ? 1 : 0;
-                mRowsAdapter.add(indexOfOverviewRow, dor);
+                mRowsAdapter.add(0, dor);
                 setSelectedPosition(0, true);
                 if (TEST_SHARED_ELEMENT_TRANSITION) {
                     if (mHelper != null && !mHelper.getAutoStartSharedElementTransition()) {
@@ -259,7 +318,11 @@
         }, TIME_TO_LOAD_OVERVIEW_ROW_MS);
 
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
+                if (getActivity() == null) {
+                    return;
+                }
                 for (int i = 0; i < NUM_ROWS; ++i) {
                     ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
                     listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1));
@@ -269,9 +332,6 @@
                     HeaderItem header = new HeaderItem(i, "Row " + i);
                     mRowsAdapter.add(new ListRow(header, listRowAdapter));
                 }
-                if (TEST_ENTRANCE_TRANSITION) {
-                    startEntranceTransition();
-                }
             }
         }, TIME_TO_LOAD_RELATED_ROWS_MS);
         setAdapter(mRowsAdapter);
@@ -284,11 +344,11 @@
         // Restore background drawable in onStart():
         mBackgroundHelper.loadBitmap(R.drawable.spiderman,
                 new BackgroundHelper.BitmapLoadCallback() {
-                @Override
-                public void onBitmapLoaded(Bitmap bitmap) {
-                    mDetailsBackground.setCoverBitmap(bitmap);
-                }
-            });
+                    @Override
+                    public void onBitmapLoaded(Bitmap bitmap) {
+                        mDetailsBackground.setCoverBitmap(bitmap);
+                    }
+                });
     }
 
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
index 77ce368..db7d594 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
@@ -19,11 +19,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.os.Build;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.app.DetailsSupportFragmentBackgroundController;
+import android.support.v17.leanback.media.MediaPlayerAdapter;
 import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlue;
+import android.support.v17.leanback.media.PlaybackTransportControlGlue;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -47,9 +50,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 public class NewDetailsSupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+
     private static final String TAG = "leanback.DetailsSupportFragment";
     private static final String ITEM = "item";
 
@@ -62,9 +65,7 @@
     private static final int ACTION_RENT = 2;
     private static final int ACTION_BUY = 3;
 
-    private boolean TEST_OVERVIEW_ROW_ON_SECOND;
-    private boolean TEST_SHARED_ELEMENT_TRANSITION;
-    private boolean TEST_ENTRANCE_TRANSITION;
+    private boolean TEST_SHARED_ELEMENT_TRANSITION = true;
     private boolean TEST_BACKGROUND_PLAYER;
 
     private static final long TIME_TO_LOAD_OVERVIEW_ROW_MS = 1000;
@@ -79,29 +80,56 @@
     private final DetailsSupportFragmentBackgroundController mDetailsBackground =
             new DetailsSupportFragmentBackgroundController(this);
 
-    private void initializeTest() {
-        TEST_SHARED_ELEMENT_TRANSITION = Build.VERSION.SDK_INT >= 21
-                && null != getActivity().getWindow().getSharedElementEnterTransition();
-        TEST_OVERVIEW_ROW_ON_SECOND = !TEST_SHARED_ELEMENT_TRANSITION;
-        TEST_ENTRANCE_TRANSITION = false;
+    void setupTrailerVideo() {
+        MediaPlayerGlue mediaPlayerGlue = new MediaPlayerGlue(getActivity());
+        mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
+        mediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ONE);
+        mediaPlayerGlue.setArtist("A Googler");
+        mediaPlayerGlue.setTitle("Diving with Sharks Trailer");
+        mediaPlayerGlue.setMediaSource(Uri.parse("android.resource://com.example.android.leanback/"
+                + "raw/browse"));
+    }
+
+    void setupMainVideo() {
+        Context context = getActivity();
+        MediaPlayerAdapter adapter = new MediaPlayerAdapter(context);
+        PlaybackTransportControlGlue<MediaPlayerAdapter> mediaPlayerGlue =
+                new PlaybackTransportControlGlue(context, adapter);
+        mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
+        mediaPlayerGlue.setSubtitle("A Googler");
+        mediaPlayerGlue.setTitle("Diving with Sharks");
+        mediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(
+                "https://storage.googleapis.com/android-tv/Sample videos/April Fool's "
+                        + "2013/Explore Treasure Mode with Google Maps.mp4"));
+        mediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            @Override
+            public void onPreparedStateChanged(PlaybackGlue glue) {
+                super.onPreparedStateChanged(glue);
+                PlaybackTransportControlGlue controlGlue = (PlaybackTransportControlGlue) glue;
+                controlGlue.setSeekProvider(new PlaybackSeekDiskDataProvider(
+                        controlGlue.getDuration(), 1000,
+                        "/sdcard/seek/frame_%04d.jpg"));
+            }
+        });
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
-        initializeTest();
 
         mBackgroundHelper = new BackgroundHelper(getActivity());
         mDetailsBackground.enableParallax();
         if (TEST_BACKGROUND_PLAYER) {
-            MediaPlayerGlue mediaPlayerGlue = new MediaPlayerGlue(getActivity());
-            mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
-
-            mediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
-            mediaPlayerGlue.setArtist("A Googleer");
-            mediaPlayerGlue.setTitle("Diving with Sharks");
-            mediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
+            if (MovieData.sStatus == MovieData.STATUS_INIT) {
+                // not own/rented, play trailer
+                setupTrailerVideo();
+            } else {
+                // bought or rented, play the main content
+                setupMainVideo();
+                // hide details main ui
+                mDetailsBackground.switchToVideo();
+            }
         }
 
         final Context context = getActivity();
@@ -130,9 +158,7 @@
             @Override
             public void onActionClicked(Action action) {
                 final Context context = getActivity();
-                Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show();
-                int indexOfOverviewRow = TEST_OVERVIEW_ROW_ON_SECOND ? 1 : 0;
-                DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(indexOfOverviewRow);
+                DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(0);
                 if (action.getId() == ACTION_BUY) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
@@ -140,25 +166,45 @@
                     actions.set(ACTION_PLAY, mActionPlay);
                     actions.clear(ACTION_RENT);
                     actions.clear(ACTION_BUY);
-                    dor.setItem(mPhotoItem.getTitle() + "(Owned)");
+                    boolean previousRented = MovieData.sStatus == MovieData.STATUS_RENTED;
+                    MovieData.sStatus = MovieData.STATUS_OWN;
+                    dor.setItem(getDisplayTitle(mPhotoItem.getTitle()));
                     dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
                             R.drawable.details_img_16x9, context.getTheme()));
+                    if (TEST_BACKGROUND_PLAYER) {
+                        if (!previousRented) {
+                            setupMainVideo();
+                            mDetailsBackground.switchToVideo();
+                        }
+                    } else {
+                        Intent intent = new Intent(context, PlaybackOverlaySupportActivity.class);
+                        getActivity().startActivity(intent);
+                    }
                 } else if (action.getId() == ACTION_RENT) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
                             dor.getActionsAdapter();
                     actions.set(ACTION_PLAY, mActionPlay);
                     actions.clear(ACTION_RENT);
-                    dor.setItem(mPhotoItem.getTitle() + "(Rented)");
+                    MovieData.sStatus = MovieData.STATUS_RENTED;
+                    dor.setItem(getDisplayTitle(mPhotoItem.getTitle()));
+                    if (TEST_BACKGROUND_PLAYER) {
+                        setupMainVideo();
+                        mDetailsBackground.switchToVideo();
+                    } else {
+                        Intent intent = new Intent(context, PlaybackOverlaySupportActivity.class);
+                        getActivity().startActivity(intent);
+                    }
                 } else if (action.getId() == ACTION_PLAY) {
-                    Intent intent = new Intent(context, PlaybackOverlaySupportActivity.class);
-                    getActivity().startActivity(intent);
+                    if (TEST_BACKGROUND_PLAYER) {
+                        mDetailsBackground.switchToVideo();
+                    } else {
+                        Intent intent = new Intent(context, PlaybackOverlaySupportActivity.class);
+                        getActivity().startActivity(intent);
+                    }
                 }
             }
         });
-        if (TEST_OVERVIEW_ROW_ON_SECOND) {
-            dorPresenter.setInitialState(FullWidthDetailsOverviewRowPresenter.STATE_SMALL);
-        }
 
         ps.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
         ps.addClassPresenter(ListRow.class, new ListRowPresenter());
@@ -169,7 +215,7 @@
         setOnItemViewClickedListener(new OnItemViewClickedListener() {
             @Override
             public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                                      RowPresenter.ViewHolder rowViewHolder, Row row) {
+                    RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemClicked: " + item + " row " + row);
                 if (item instanceof PhotoItem) {
                     Intent intent = new Intent(getActivity(), DetailsSupportActivity.class);
@@ -186,7 +232,7 @@
         setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                                       RowPresenter.ViewHolder rowViewHolder, Row row) {
+                    RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemSelected: " + item + " row " + row);
             }
         });
@@ -200,18 +246,11 @@
         } else {
             dorPresenter.setParticipatingEntranceTransition(true);
         }
-        if (TEST_ENTRANCE_TRANSITION) {
-            // don't run entrance transition if Activity is restored.
-            if (savedInstanceState == null) {
-                prepareEntranceTransition();
-            }
-        }
-
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
+            Bundle savedInstanceState) {
         View view = super.onCreateView(inflater, container, savedInstanceState);
         return view;
     }
@@ -225,33 +264,53 @@
         updateAdapter();
     }
 
+    static String getDisplayTitle(String title) {
+        switch (MovieData.sStatus) {
+            case MovieData.STATUS_OWN:
+                title = title + "(Owned)";
+                break;
+            case MovieData.STATUS_RENTED:
+                title = title + "(Rented)";
+                break;
+            case MovieData.STATUS_INIT:
+            default:
+        }
+        return title;
+    }
+
     void updateAdapter() {
         if (mRowsAdapter == null) {
             return;
         }
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
-                if (TEST_OVERVIEW_ROW_ON_SECOND) {
-                    ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
-                    listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1));
-                    listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2));
-                    listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3));
-                    listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4));
-                    HeaderItem header = new HeaderItem(0, "Search Result");
-                    mRowsAdapter.add(0, new ListRow(header, listRowAdapter));
+                final Context context = getActivity();
+                if (context == null) {
+                    return;
                 }
 
-                final Context context = getActivity();
-                DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
+                DetailsOverviewRow dor = new DetailsOverviewRow(
+                        getDisplayTitle(mPhotoItem.getTitle()));
                 dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
                         mPhotoItem.getImageResourceId(), context.getTheme()));
                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
-                adapter.set(ACTION_RENT, mActionRent);
-                adapter.set(ACTION_BUY, mActionBuy);
+                switch (MovieData.sStatus) {
+                    case MovieData.STATUS_INIT:
+                        adapter.set(ACTION_RENT, mActionRent);
+                        adapter.set(ACTION_BUY, mActionBuy);
+                        break;
+                    case MovieData.STATUS_OWN:
+                        adapter.set(ACTION_PLAY, mActionPlay);
+                        break;
+                    case MovieData.STATUS_RENTED:
+                        adapter.set(ACTION_PLAY, mActionPlay);
+                        adapter.set(ACTION_BUY, mActionBuy);
+                        break;
+                }
                 dor.setActionsAdapter(adapter);
-                int indexOfOverviewRow = TEST_OVERVIEW_ROW_ON_SECOND ? 1 : 0;
-                mRowsAdapter.add(indexOfOverviewRow, dor);
+                mRowsAdapter.add(0, dor);
                 setSelectedPosition(0, true);
                 if (TEST_SHARED_ELEMENT_TRANSITION) {
                     if (mHelper != null && !mHelper.getAutoStartSharedElementTransition()) {
@@ -262,7 +321,11 @@
         }, TIME_TO_LOAD_OVERVIEW_ROW_MS);
 
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
+                if (getActivity() == null) {
+                    return;
+                }
                 for (int i = 0; i < NUM_ROWS; ++i) {
                     ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
                     listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1));
@@ -272,9 +335,6 @@
                     HeaderItem header = new HeaderItem(i, "Row " + i);
                     mRowsAdapter.add(new ListRow(header, listRowAdapter));
                 }
-                if (TEST_ENTRANCE_TRANSITION) {
-                    startEntranceTransition();
-                }
             }
         }, TIME_TO_LOAD_RELATED_ROWS_MS);
         setAdapter(mRowsAdapter);
@@ -287,11 +347,11 @@
         // Restore background drawable in onStart():
         mBackgroundHelper.loadBitmap(R.drawable.spiderman,
                 new BackgroundHelper.BitmapLoadCallback() {
-                @Override
-                public void onBitmapLoaded(Bitmap bitmap) {
-                    mDetailsBackground.setCoverBitmap(bitmap);
-                }
-            });
+                    @Override
+                    public void onBitmapLoaded(Bitmap bitmap) {
+                        mDetailsBackground.setCoverBitmap(bitmap);
+                    }
+                });
     }
 
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java
index 2fe9bb9..a30a3fd 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java
@@ -15,7 +15,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.ViewTreeObserver;
 
 public class OnboardingActivity extends Activity {
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
index f0a2275..177eced 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
@@ -18,7 +18,6 @@
 
 import android.support.v4.app.FragmentActivity;
 import android.os.Bundle;
-import android.view.ViewTreeObserver;
 
 public class OnboardingSupportActivity extends FragmentActivity {
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlGlue.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlGlue.java
index e04fa46..b2f89e5 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlGlue.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlGlue.java
@@ -62,9 +62,9 @@
     PlaybackControlGlue(Context context) {
         super(context, sFastForwardSpeeds);
         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
         mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
         mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
     }
@@ -217,7 +217,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) {
                     pause();
                 } else {
                     play(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
index a6752eb..e0becaa 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
@@ -69,9 +69,9 @@
     PlaybackControlHelper(Context context, PlaybackOverlayFragment fragment) {
         super(context, fragment, sFastForwardSpeeds);
         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
         mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
         mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
     }
@@ -234,7 +234,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) {
                     pausePlayback();
                 } else {
                     startPlayback(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java
index 0425e60..4ae5b37 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java
@@ -72,9 +72,9 @@
     PlaybackControlSupportHelper(Context context, PlaybackOverlaySupportFragment fragment) {
         super(context, fragment, sFastForwardSpeeds);
         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
         mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
         mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
     }
@@ -237,7 +237,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) {
                     pausePlayback();
                 } else {
                     startPlayback(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackFragment.java
index c72519d..ee6cdd9 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackFragment.java
@@ -56,6 +56,7 @@
 
     private PlaybackControlGlue mGlue;
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -74,11 +75,11 @@
         mGlue = new PlaybackControlGlue(context) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -134,7 +135,7 @@
             setFadingEnabled(true);
             fadeOut();
         } else {
-            setFadingEnabled(mGlue.isMediaPlaying());
+            setFadingEnabled(mGlue.isPlaying());
         }
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
index 248e4ab..369c464 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
@@ -82,6 +82,7 @@
                 }
     };
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -101,11 +102,11 @@
         mGlue = new PlaybackControlHelper(context, this) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -185,7 +186,7 @@
             setFadingEnabled(true);
             fadeOut();
         } else {
-            setFadingEnabled(mGlue.isMediaPlaying());
+            setFadingEnabled(mGlue.isPlaying());
         }
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
index 205037e..5642557 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
@@ -85,6 +85,7 @@
                 }
     };
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -104,11 +105,11 @@
         mGlue = new PlaybackControlSupportHelper(context, this) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -188,7 +189,7 @@
             setFadingEnabled(true);
             fadeOut();
         } else {
-            setFadingEnabled(mGlue.isMediaPlaying());
+            setFadingEnabled(mGlue.isPlaying());
         }
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekAsyncDataProvider.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekAsyncDataProvider.java
new file mode 100644
index 0000000..1a9af3c
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekAsyncDataProvider.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.support.v17.leanback.widget.PlaybackSeekDataProvider;
+import android.support.v4.util.LruCache;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ *
+ * Base class that implements PlaybackSeekDataProvider using AsyncTask.THREAD_POOL_EXECUTOR with
+ * prefetching.
+ */
+public abstract class PlaybackSeekAsyncDataProvider extends PlaybackSeekDataProvider {
+
+    static final String TAG = "SeekAsyncProvider";
+
+    long[] mSeekPositions;
+    // mCache is for the bitmap requested by user
+    final LruCache<Integer, Bitmap> mCache;
+    // mPrefetchCache is for the bitmap not requested by user but prefetched by heuristic
+    // estimation. We use a different LruCache so that items in mCache will not be evicted by
+    // prefeteched items.
+    final LruCache<Integer, Bitmap> mPrefetchCache;
+    final SparseArray<LoadBitmapTask> mRequests = new SparseArray<>();
+    int mLastRequestedIndex = -1;
+
+    protected boolean isCancelled(Object task) {
+        return ((AsyncTask) task).isCancelled();
+    }
+
+    protected abstract Bitmap doInBackground(Object task, int index, long position);
+
+    class LoadBitmapTask extends AsyncTask<Object, Object, Bitmap> {
+
+        int mIndex;
+        ResultCallback mResultCallback;
+
+        LoadBitmapTask(int index, ResultCallback callback) {
+            mIndex = index;
+            mResultCallback = callback;
+        }
+
+        @Override
+        protected Bitmap doInBackground(Object[] params) {
+            return PlaybackSeekAsyncDataProvider.this
+                    .doInBackground(this, mIndex, mSeekPositions[mIndex]);
+        }
+
+        @Override
+        protected void onPostExecute(Bitmap bitmap) {
+            mRequests.remove(mIndex);
+            Log.d(TAG, "thumb Loaded " + mIndex);
+            if (mResultCallback != null) {
+                mCache.put(mIndex, bitmap);
+                mResultCallback.onThumbnailLoaded(bitmap, mIndex);
+            } else {
+                mPrefetchCache.put(mIndex, bitmap);
+            }
+        }
+
+    }
+
+    public PlaybackSeekAsyncDataProvider() {
+        this(16, 24);
+    }
+
+    public PlaybackSeekAsyncDataProvider(int cacheSize, int prefetchCacheSize) {
+        mCache = new LruCache<Integer, Bitmap>(cacheSize);
+        mPrefetchCache = new LruCache<Integer, Bitmap>(prefetchCacheSize);
+    }
+
+    public void setSeekPositions(long[] positions) {
+        mSeekPositions = positions;
+    }
+
+    @Override
+    public long[] getSeekPositions() {
+        return mSeekPositions;
+    }
+
+    @Override
+    public void getThumbnail(int index, ResultCallback callback) {
+        Integer key = index;
+        Bitmap bitmap = mCache.get(key);
+        if (bitmap != null) {
+            callback.onThumbnailLoaded(bitmap, index);
+        } else {
+            bitmap = mPrefetchCache.get(key);
+            if (bitmap != null) {
+                mCache.put(key, bitmap);
+                mPrefetchCache.remove(key);
+                callback.onThumbnailLoaded(bitmap, index);
+            } else {
+                LoadBitmapTask task = mRequests.get(index);
+                if (task == null || task.isCancelled()) {
+                    // no normal task or prefetch for the position, create a new task
+                    task = new LoadBitmapTask(index, callback);
+                    mRequests.put(index, task);
+                    task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                } else {
+                    // update existing ResultCallback which might be normal task or prefetch
+                    task.mResultCallback = callback;
+                }
+            }
+        }
+        if (mLastRequestedIndex != index) {
+            if (mLastRequestedIndex != -1) {
+                prefetch(mLastRequestedIndex, index > mLastRequestedIndex);
+            }
+            mLastRequestedIndex = index;
+        }
+    }
+
+    protected void prefetch(int hintIndex, boolean forward) {
+        for (Iterator<Map.Entry<Integer, Bitmap>> it =
+                mPrefetchCache.snapshot().entrySet().iterator(); it.hasNext(); ) {
+            Map.Entry<Integer, Bitmap> entry = it.next();
+            if (forward ? entry.getKey() < hintIndex : entry.getKey() > hintIndex) {
+                mPrefetchCache.remove(entry.getKey());
+            }
+        }
+        int inc = forward ? 1 : -1;
+        for (int i = hintIndex; (mRequests.size() + mPrefetchCache.size()
+                < mPrefetchCache.maxSize()) && (inc > 0 ? i < mSeekPositions.length : i >= 0);
+                i += inc) {
+            Integer key = i;
+            if (mCache.get(key) == null && mPrefetchCache.get(key) == null) {
+                LoadBitmapTask task = mRequests.get(i);
+                if (task == null) {
+                    task = new LoadBitmapTask(key, null);
+                    mRequests.put(i, task);
+                    task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void reset() {
+        for (int i = 0; i < mRequests.size(); i++) {
+            LoadBitmapTask task = mRequests.valueAt(i);
+            task.cancel(true);
+        }
+        mRequests.clear();
+        mCache.evictAll();
+        mPrefetchCache.evictAll();
+        mLastRequestedIndex = -1;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("Requests<");
+        for (int i = 0; i < mRequests.size(); i++) {
+            b.append(mRequests.keyAt(i));
+            b.append(",");
+        }
+        b.append("> Cache<");
+        for (Iterator<Integer> it = mCache.snapshot().keySet().iterator(); it.hasNext();) {
+            Integer key = it.next();
+            if (mCache.get(key) != null) {
+                b.append(key);
+                b.append(",");
+            }
+        }
+        b.append(">");
+        b.append("> PrefetchCache<");
+        for (Iterator<Integer> it = mPrefetchCache.snapshot().keySet().iterator(); it.hasNext();) {
+            Integer key = it.next();
+            if (mPrefetchCache.get(key) != null) {
+                b.append(key);
+                b.append(",");
+            }
+        }
+        b.append(">");
+        return b.toString();
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekDataProviderSample.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekDataProviderSample.java
new file mode 100644
index 0000000..5243274
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekDataProviderSample.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+/**
+ * Sample PlaybackSeekDataProvider render time label as thumb.
+ */
+class PlaybackSeekDataProviderSample extends PlaybackSeekAsyncDataProvider {
+
+    Paint mPaint;
+
+    PlaybackSeekDataProviderSample(long duration, long interval) {
+        int size = (int) (duration / interval) + 1;
+        long[] pos = new long[size];
+        for (int i = 0; i < pos.length; i++) {
+            pos[i] = i * duration / pos.length;
+        }
+        setSeekPositions(pos);
+        mPaint = new Paint();
+        mPaint.setTextSize(16);
+        mPaint.setColor(Color.BLUE);
+    }
+
+    protected Bitmap doInBackground(Object task, int index, long position) {
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException ex) {
+            // Thread might be interrupted by cancel() call.
+        }
+        if (isCancelled(task)) {
+            return null;
+        }
+        Bitmap bmp = Bitmap.createBitmap(160, 160, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bmp);
+        canvas.drawColor(Color.YELLOW);
+        canvas.drawText(formatTime(position), 10, 80, mPaint);
+        canvas.drawText(Integer.toString(index), 10, 150, mPaint);
+        return bmp;
+    }
+
+    String formatTime(long ms) {
+        long seconds = ms / 1000;
+        float seconds2 = (ms - seconds * 1000) / 1000f;
+        long minutes = seconds / 60;
+        long hours = minutes / 60;
+        seconds -= minutes * 60;
+        minutes -= hours * 60;
+
+        StringBuilder b = new StringBuilder();
+        if (hours > 0) {
+            b.append(hours).append(':');
+            if (minutes < 10) {
+                b.append('0');
+            }
+        }
+        b.append(minutes).append(':');
+        if (seconds < 10) {
+            b.append('0');
+        }
+        b.append(String.format("%.2f", ((float) seconds + seconds2)));
+        return b.toString();
+    }
+
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekDiskDataProvider.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekDiskDataProvider.java
new file mode 100644
index 0000000..bdb88d3
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSeekDiskDataProvider.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import java.io.File;
+
+/**
+ * Sample PlaybackSeekDataProvider that reads bitmaps stored on disk.
+ * e.g. new PlaybackSeekDiskDataProvider(duration, 1000, "/sdcard/frame_%04d.jpg")
+ * Expects the seek positions are 1000ms interval, snapshots are stored at
+ * /sdcard/frame_0001.jpg, ...
+ */
+class PlaybackSeekDiskDataProvider extends PlaybackSeekAsyncDataProvider {
+
+    final Paint mPaint;
+    final String mPathPattern;
+    PlaybackSeekDiskDataProvider(long duration, long interval, String pathPattern) {
+        mPathPattern = pathPattern;
+        int size = (int) (duration / interval) + 1;
+        long[] pos = new long[size];
+        for (int i = 0; i < pos.length; i++) {
+            pos[i] = i * duration / pos.length;
+        }
+        setSeekPositions(pos);
+        mPaint = new Paint();
+        mPaint.setTextSize(16);
+        mPaint.setColor(Color.BLUE);
+    }
+
+    protected Bitmap doInBackground(Object task, int index, long position) {
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException ex) {
+            // Thread might be interrupted by cancel() call.
+        }
+        if (isCancelled(task)) {
+            return null;
+        }
+        String path = String.format(mPathPattern, (index + 1));
+        if (new File(path).exists()) {
+            return BitmapFactory.decodeFile(path);
+        } else {
+            Bitmap bmp = Bitmap.createBitmap(160, 160, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bmp);
+            canvas.drawColor(Color.YELLOW);
+            canvas.drawText(path, 10, 80, mPaint);
+            canvas.drawText(Integer.toString(index), 10, 150, mPaint);
+            return bmp;
+        }
+    }
+
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSupportFragment.java
index 8a5da83..696be5c 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackSupportFragment.java
@@ -59,6 +59,7 @@
 
     private PlaybackControlGlue mGlue;
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -77,11 +78,11 @@
         mGlue = new PlaybackControlGlue(context) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -137,7 +138,7 @@
             setFadingEnabled(true);
             fadeOut();
         } else {
-            setFadingEnabled(mGlue.isMediaPlaying());
+            setFadingEnabled(mGlue.isPlaying());
         }
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlActivity.java
new file mode 100644
index 0000000..878f647
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlActivity.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Host PlaybackFragment and provide PIP events.
+ */
+public class PlaybackTransportControlActivity extends Activity {
+    private List<PictureInPictureListener> mListeners = new ArrayList<>();
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.playback_transportcontrol_activity);
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        for (PictureInPictureListener listener : mListeners) {
+            listener.onPictureInPictureModeChanged(isInPictureInPictureMode);
+        }
+    }
+
+    /**
+     * Register a PIP listener.
+     */
+    public void registerPictureInPictureListener(PictureInPictureListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Unregister a PIP listener.
+     */
+    public void unregisterPictureInPictureListener(PictureInPictureListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Interface of PIP event on Activity.
+     */
+    public interface PictureInPictureListener {
+        /**
+         * Called when Activity's PIP mode is changed.
+         */
+        void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlFragment.java
new file mode 100644
index 0000000..71c85f7
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlFragment.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v17.leanback.app.PlaybackFragmentGlueHost;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.util.Log;
+
+/**
+ * Example of PlaybackFragment working with a PlaybackControlGlue.
+ */
+public class PlaybackTransportControlFragment
+        extends android.support.v17.leanback.app.PlaybackFragment
+        implements PlaybackTransportControlActivity.PictureInPictureListener {
+    private static final String TAG = "TransportFragment";
+
+    /**
+     * Change this to choose a different overlay background.
+     */
+    private static final int BACKGROUND_TYPE = BG_DARK;
+
+    /**
+     * Change the number of related content rows.
+     */
+    private static final int RELATED_CONTENT_ROWS = 3;
+
+    private static final int ROW_CONTROLS = 0;
+
+    private PlaybackTransportControlGlueSample mGlue;
+
+    public SparseArrayObjectAdapter getAdapter() {
+        return (SparseArrayObjectAdapter) super.getAdapter();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        setBackgroundType(BACKGROUND_TYPE);
+
+        createComponents(getActivity());
+    }
+
+    private void createComponents(Context context) {
+        mGlue = new PlaybackTransportControlGlueSample(context, new PlayerAdapter()) {
+            @Override
+            public void onActionClicked(Action action) {
+                if (action.getId() == R.id.lb_control_picture_in_picture) {
+                    if (Build.VERSION.SDK_INT >= 24) {
+                        getActivity().enterPictureInPictureMode();
+                    }
+                    return;
+                }
+                super.onActionClicked(action);
+            }
+
+        };
+
+        mGlue.setTitle("Title");
+        mGlue.setSubtitle("Android developer");
+        mGlue.setHost(new PlaybackFragmentGlueHost(this));
+        mGlue.setSeekProvider(new PlaybackSeekDataProviderSample(
+                PlayerAdapter.FAUX_DURATION, 100));
+
+        ClassPresenterSelector selector = new ClassPresenterSelector();
+        selector.addClassPresenter(ListRow.class, new ListRowPresenter());
+        setAdapter(new SparseArrayObjectAdapter(selector));
+
+        // Add related content rows
+        for (int i = 0; i < RELATED_CONTENT_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter());
+            listRowAdapter.add("Some related content");
+            listRowAdapter.add("Other related content");
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            getAdapter().set(ROW_CONTROLS + 1 + i, new ListRow(header, listRowAdapter));
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        ((PlaybackTransportControlActivity) getActivity()).registerPictureInPictureListener(this);
+    }
+
+    @Override
+    public void onStop() {
+        ((PlaybackTransportControlActivity) getActivity()).unregisterPictureInPictureListener(this);
+        super.onStop();
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        if (isInPictureInPictureMode) {
+            // Hide the controls in picture-in-picture mode.
+            setControlsOverlayAutoHideEnabled(true);
+            hideControlsOverlay(true);
+        } else {
+            setControlsOverlayAutoHideEnabled(mGlue.isPlaying());
+        }
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlGlueSample.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlGlueSample.java
new file mode 100644
index 0000000..91d87c1
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlGlueSample.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.content.Context;
+import android.os.Handler;
+import android.support.v17.leanback.media.PlayerAdapter;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Toast;
+
+class PlaybackTransportControlGlueSample<T extends PlayerAdapter> extends
+        android.support.v17.leanback.media.PlaybackTransportControlGlue<T> {
+
+    private PlaybackControlsRow.RepeatAction mRepeatAction;
+    private PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
+    private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
+    private PlaybackControlsRow.PictureInPictureAction mPipAction;
+    private PlaybackControlsRow.ClosedCaptioningAction mClosedCaptioningAction;
+
+    PlaybackTransportControlGlueSample(Context context, T impl) {
+        super(context, impl);
+        mClosedCaptioningAction = new PlaybackControlsRow.ClosedCaptioningAction(context);
+        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
+        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
+        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
+        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
+        mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
+        mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
+    }
+
+    @Override
+    protected void onCreateSecondaryActions(ArrayObjectAdapter adapter) {
+        adapter.add(mThumbsUpAction);
+        adapter.add(mThumbsDownAction);
+        if (android.os.Build.VERSION.SDK_INT > 23) {
+            adapter.add(mPipAction);
+        }
+    }
+
+    @Override
+    protected void onCreatePrimaryActions(ArrayObjectAdapter adapter) {
+        super.onCreatePrimaryActions(adapter);
+        adapter.add(mRepeatAction);
+        adapter.add(mClosedCaptioningAction);
+    }
+
+    @Override
+    public void onActionClicked(Action action) {
+        if (shouldDispatchAction(action)) {
+            dispatchAction(action);
+            return;
+        }
+        super.onActionClicked(action);
+    }
+
+    @Override
+    public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
+        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+            Action action = getControlsRow().getActionForKeyCode(keyEvent.getKeyCode());
+            if (shouldDispatchAction(action)) {
+                dispatchAction(action);
+                return true;
+            }
+        }
+        return super.onKey(view, keyCode, keyEvent);
+    }
+
+    private boolean shouldDispatchAction(Action action) {
+        return action == mRepeatAction || action == mThumbsUpAction || action == mThumbsDownAction;
+    }
+
+    private void dispatchAction(Action action) {
+        Toast.makeText(getContext(), action.toString(), Toast.LENGTH_SHORT).show();
+        PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action;
+        multiAction.nextIndex();
+        notifyActionChanged(multiAction);
+    }
+
+    private void notifyActionChanged(PlaybackControlsRow.MultiAction action) {
+        int index = -1;
+        if (getPrimaryActionsAdapter() != null) {
+            index = getPrimaryActionsAdapter().indexOf(action);
+        }
+        if (index >= 0) {
+            getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
+        } else {
+            if (getSecondaryActionsAdapter() != null) {
+                index = getSecondaryActionsAdapter().indexOf(action);
+                if (index >= 0) {
+                    getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
+                }
+            }
+        }
+    }
+
+    private ArrayObjectAdapter getPrimaryActionsAdapter() {
+        if (getControlsRow() == null) {
+            return null;
+        }
+        return (ArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
+    }
+
+    private ArrayObjectAdapter getSecondaryActionsAdapter() {
+        if (getControlsRow() == null) {
+            return null;
+        }
+        return (ArrayObjectAdapter) getControlsRow().getSecondaryActionsAdapter();
+    }
+
+    Handler mHandler = new Handler();
+
+    @Override
+    protected void onPlayCompleted() {
+        super.onPlayCompleted();
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mRepeatAction.getIndex() != PlaybackControlsRow.RepeatAction.INDEX_NONE) {
+                    play();
+                }
+            }
+        });
+    }
+
+    public void setMode(int mode) {
+        mRepeatAction.setIndex(mode);
+        if (getPrimaryActionsAdapter() == null) {
+            return;
+        }
+        notifyActionChanged(mRepeatAction);
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlSupportActivity.java
new file mode 100644
index 0000000..9a3f9c8
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlSupportActivity.java
@@ -0,0 +1,71 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from PlaybackTransportControlActivity.java.  DO NOT MODIFY. */
+
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.support.v4.app.FragmentActivity;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Host PlaybackFragment and provide PIP events.
+ */
+public class PlaybackTransportControlSupportActivity extends FragmentActivity {
+    private List<PictureInPictureListener> mListeners = new ArrayList<>();
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.playback_transportcontrol_activity_support);
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        for (PictureInPictureListener listener : mListeners) {
+            listener.onPictureInPictureModeChanged(isInPictureInPictureMode);
+        }
+    }
+
+    /**
+     * Register a PIP listener.
+     */
+    public void registerPictureInPictureListener(PictureInPictureListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Unregister a PIP listener.
+     */
+    public void unregisterPictureInPictureListener(PictureInPictureListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Interface of PIP event on Activity.
+     */
+    public interface PictureInPictureListener {
+        /**
+         * Called when Activity's PIP mode is changed.
+         */
+        void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlSupportFragment.java
new file mode 100644
index 0000000..0ce9a04
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackTransportControlSupportFragment.java
@@ -0,0 +1,128 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from PlaybackTransportControlFragment.java.  DO NOT MODIFY. */
+
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.util.Log;
+
+/**
+ * Example of PlaybackSupportFragment working with a PlaybackControlGlue.
+ */
+public class PlaybackTransportControlSupportFragment
+        extends android.support.v17.leanback.app.PlaybackSupportFragment
+        implements PlaybackTransportControlSupportActivity.PictureInPictureListener {
+    private static final String TAG = "TransportFragment";
+
+    /**
+     * Change this to choose a different overlay background.
+     */
+    private static final int BACKGROUND_TYPE = BG_DARK;
+
+    /**
+     * Change the number of related content rows.
+     */
+    private static final int RELATED_CONTENT_ROWS = 3;
+
+    private static final int ROW_CONTROLS = 0;
+
+    private PlaybackTransportControlGlueSample mGlue;
+
+    public SparseArrayObjectAdapter getAdapter() {
+        return (SparseArrayObjectAdapter) super.getAdapter();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        setBackgroundType(BACKGROUND_TYPE);
+
+        createComponents(getActivity());
+    }
+
+    private void createComponents(Context context) {
+        mGlue = new PlaybackTransportControlGlueSample(context, new PlayerAdapter()) {
+            @Override
+            public void onActionClicked(Action action) {
+                if (action.getId() == R.id.lb_control_picture_in_picture) {
+                    if (Build.VERSION.SDK_INT >= 24) {
+                        getActivity().enterPictureInPictureMode();
+                    }
+                    return;
+                }
+                super.onActionClicked(action);
+            }
+
+        };
+
+        mGlue.setTitle("Title");
+        mGlue.setSubtitle("Android developer");
+        mGlue.setHost(new PlaybackSupportFragmentGlueHost(this));
+        mGlue.setSeekProvider(new PlaybackSeekDataProviderSample(
+                PlayerAdapter.FAUX_DURATION, 100));
+
+        ClassPresenterSelector selector = new ClassPresenterSelector();
+        selector.addClassPresenter(ListRow.class, new ListRowPresenter());
+        setAdapter(new SparseArrayObjectAdapter(selector));
+
+        // Add related content rows
+        for (int i = 0; i < RELATED_CONTENT_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter());
+            listRowAdapter.add("Some related content");
+            listRowAdapter.add("Other related content");
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            getAdapter().set(ROW_CONTROLS + 1 + i, new ListRow(header, listRowAdapter));
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        ((PlaybackTransportControlSupportActivity) getActivity()).registerPictureInPictureListener(this);
+    }
+
+    @Override
+    public void onStop() {
+        ((PlaybackTransportControlSupportActivity) getActivity()).unregisterPictureInPictureListener(this);
+        super.onStop();
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        if (isInPictureInPictureMode) {
+            // Hide the controls in picture-in-picture mode.
+            setControlsOverlayAutoHideEnabled(true);
+            hideControlsOverlay(true);
+        } else {
+            setControlsOverlayAutoHideEnabled(mGlue.isPlaying());
+        }
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlayerAdapter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlayerAdapter.java
new file mode 100644
index 0000000..664ce7a
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlayerAdapter.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.os.Handler;
+
+class PlayerAdapter extends
+        android.support.v17.leanback.media.PlayerAdapter {
+
+    static final int FAUX_DURATION = 33 * 1000;
+
+    private boolean mIsPlaying;
+    private long mStartTime;
+    private long mStartPosition = 0;
+    private Handler mHandler = new Handler();
+    private final Runnable mUpdateProgressRunnable = new Runnable() {
+        @Override
+        public void run() {
+            getCallback().onCurrentPositionChanged(com.example.android.leanback.PlayerAdapter.this);
+            mHandler.postDelayed(this, 16);
+        }
+    };
+
+    @Override
+    public boolean isPlaying() {
+        return mIsPlaying;
+    }
+
+    @Override
+    public long getDuration() {
+        return FAUX_DURATION;
+    }
+
+    @Override
+    public void seekTo(long position) {
+        mStartPosition = position;
+        mStartTime = System.currentTimeMillis();
+        getCallback().onCurrentPositionChanged(com.example.android.leanback.PlayerAdapter.this);
+    }
+
+    @Override
+    public long getCurrentPosition() {
+        int speed;
+        if (!mIsPlaying) {
+            speed = 0;
+        } else {
+            speed = 1;
+        }
+        long position = mStartPosition + (System.currentTimeMillis() - mStartTime) * speed;
+        if (position > getDuration()) {
+            position = getDuration();
+            onPlaybackComplete(true);
+        } else if (position < 0) {
+            position = 0;
+            onPlaybackComplete(false);
+        }
+        return (int) position;
+    }
+
+    void onPlaybackComplete(final boolean ended) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mStartPosition = 0;
+                mIsPlaying = false;
+                getCallback().onPlayStateChanged(com.example.android.leanback.PlayerAdapter.this);
+                if (ended) {
+                    getCallback().onPlayCompleted(com.example.android.leanback.PlayerAdapter.this);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void play() {
+        if (mIsPlaying) {
+            return;
+        }
+        mStartPosition = getCurrentPosition();
+        mIsPlaying = true;
+        mStartTime = System.currentTimeMillis();
+        getCallback().onPlayStateChanged(com.example.android.leanback.PlayerAdapter.this);
+    }
+
+    @Override
+    public void pause() {
+        if (!mIsPlaying) {
+            return;
+        }
+        mStartPosition = getCurrentPosition();
+        mIsPlaying = false;
+        getCallback().onPlayStateChanged(com.example.android.leanback.PlayerAdapter.this);
+    }
+
+    @Override
+    public void setProgressUpdatingEnabled(boolean enable) {
+        mHandler.removeCallbacks(mUpdateProgressRunnable);
+        if (enable) {
+            mUpdateProgressRunnable.run();
+        }
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java
index 0565865..43179dd 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java
@@ -38,7 +38,7 @@
     }
 
     private void setupTitleFragment() {
-        TitleView titleView = (TitleView) findViewById(R.id.title);
+        TitleView titleView = findViewById(R.id.title);
         titleView.setTitle("RowsFragment");
         titleView.setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -48,7 +48,7 @@
             }
         });
 
-        BrowseFrameLayout frameLayout = (BrowseFrameLayout) findViewById(R.id.rows_frame);
+        BrowseFrameLayout frameLayout = findViewById(R.id.rows_frame);
         TitleHelper titleHelper = new TitleHelper(frameLayout, titleView);
         frameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener());
         mRowsFragment.setTitleHelper(titleHelper);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java
index dfdfad9..a77d2be 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java
@@ -41,7 +41,7 @@
     }
 
     private void setupTitleFragment() {
-        TitleView titleView = (TitleView) findViewById(R.id.title);
+        TitleView titleView = findViewById(R.id.title);
         titleView.setTitle("RowsSupportFragment");
         titleView.setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -51,7 +51,7 @@
             }
         });
 
-        BrowseFrameLayout frameLayout = (BrowseFrameLayout) findViewById(R.id.rows_frame);
+        BrowseFrameLayout frameLayout = findViewById(R.id.rows_frame);
         TitleHelper titleHelper = new TitleHelper(frameLayout, titleView);
         frameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener());
         mRowsSupportFragment.setTitleHelper(titleHelper);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java
index f9dd055..8824bbb 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java
@@ -41,7 +41,7 @@
         mMediaPlayerGlue.setArtist("A Googleer");
         mMediaPlayerGlue.setTitle("Diving with Sharks");
         mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
-        mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
+        mMediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
             @Override
             public void onReadyForPlayback() {
                 mMediaPlayerGlue.play();
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java
index 0d55c89..3ae377f 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java
@@ -16,68 +16,131 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v17.leanback.app.VideoFragmentGlueHost;
-import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.MediaPlayerAdapter;
 import android.support.v17.leanback.media.PlaybackGlue;
+import android.support.v17.leanback.media.PlaybackTransportControlGlue;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
 
 /**
  * Fragment demonstrating the use of {@link android.support.v17.leanback.app.VideoFragment} to
- * render video with playback controls.
+ * render video with playback controls. And demonstrates video seeking with thumbnails.
+ *
+ * Generate 1 frame per second thumbnail bitmaps and put on sdcard:
+ * <pre>
+ * sudo apt-get install libav-tools
+ * avconv -i input.mp4 -s 240x135 -vsync 1 -r 1 -an -y -qscale 8 frame_%04d.jpg
+ * adb shell mkdir /sdcard/seek
+ * adb push frame_*.jpg /sdcard/seek/
+ * </pre>
+ * Change to 1 frame per minute: use "-r 1/60".
+ * For more options, see https://wiki.libav.org/Snippets/avconv
+ *
+ * <p>
+ * Showcase:
+ * </p>
+ * <li>Auto play when ready</li>
+ * <li>Set seek provider</li>
+ * <li>switch MediaSource</li>
+ * <li>switch PlaybackGlue</li>
  */
 public class SampleVideoFragment extends android.support.v17.leanback.app.VideoFragment {
-    private MediaPlayerGlue mMediaPlayerGlue;
+    private PlaybackTransportControlGlueSample<MediaPlayerAdapter> mMediaPlayerGlue;
+
+    final VideoFragmentGlueHost mHost = new VideoFragmentGlueHost(SampleVideoFragment.this);
+
+    static void playWhenReady(PlaybackGlue glue) {
+        if (glue.isPrepared()) {
+            glue.play();
+        } else {
+            glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+                @Override
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        glue.removePlayerCallback(this);
+                        glue.play();
+                    }
+                }
+            });
+        }
+    }
+
+    static void loadSeekData(final PlaybackTransportControlGlue glue) {
+        if (glue.isPrepared()) {
+            glue.setSeekProvider(new PlaybackSeekDiskDataProvider(
+                    glue.getDuration(),
+                    1000,
+                    "/sdcard/seek/frame_%04d.jpg"));
+        } else {
+            glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+                @Override
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        glue.removePlayerCallback(this);
+                        PlaybackTransportControlGlue transportControlGlue =
+                                (PlaybackTransportControlGlue) glue;
+                        transportControlGlue.setSeekProvider(new PlaybackSeekDiskDataProvider(
+                                transportControlGlue.getDuration(),
+                                1000,
+                                "/sdcard/seek/frame_%04d.jpg"));
+                    }
+                }
+            });
+        }
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mMediaPlayerGlue = new PlaybackTransportControlGlueSample(getActivity(),
+                new MediaPlayerAdapter(getActivity()));
+        mMediaPlayerGlue.setHost(mHost);
+        mMediaPlayerGlue.setMode(PlaybackControlsRow.RepeatAction.INDEX_NONE);
+        mMediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            boolean mSecondCompleted = false;
+            @Override
+            public void onPlayCompleted(PlaybackGlue glue) {
+                if (!mSecondCompleted) {
+                    mSecondCompleted = true;
+                    mMediaPlayerGlue.setSubtitle("Leanback artist Changed!");
+                    mMediaPlayerGlue.setTitle("Leanback team at work");
+                    String uriPath = "https://storage.googleapis.com/android-tv/Sample videos/"
+                            + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4";
+                    mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
+                    loadSeekData(mMediaPlayerGlue);
+                    playWhenReady(mMediaPlayerGlue);
+                } else {
+                    mMediaPlayerGlue.removePlayerCallback(this);
+                    switchAnotherGlue();
+                }
+            }
+        });
+        mMediaPlayerGlue.setSubtitle("Leanback artist");
+        mMediaPlayerGlue.setTitle("Leanback team at work");
+        String uriPath = "https://storage.googleapis.com/android-tv/Sample videos/"
+                + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4";
+        mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
+        loadSeekData(mMediaPlayerGlue);
+        playWhenReady(mMediaPlayerGlue);
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
+    public void onPause() {
+        if (mMediaPlayerGlue != null) {
+            mMediaPlayerGlue.pause();
+        }
+        super.onPause();
     }
 
-    VideoFragmentGlueHost host = new VideoFragmentGlueHost(SampleVideoFragment.this);
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        getView().postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
-                mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
-                mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
-                    @Override
-                    public void onReadyForPlayback() {
-                        mMediaPlayerGlue.play();
-                    }
-                });
-                mMediaPlayerGlue.setArtist("Leanback");
-                mMediaPlayerGlue.setTitle("Leanback team at work");
-                String uriPath = "android.resource://com.example.android.leanback/raw/browse";
-                mMediaPlayerGlue.setMediaSource(Uri.parse(uriPath));
-                mMediaPlayerGlue.setHost(host);
-            }
-        }, 500);
-
-
-        getView().postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
-                mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
-                mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
-                    @Override
-                    public void onReadyForPlayback() {
-                        mMediaPlayerGlue.play();
-                    }
-                });
-                mMediaPlayerGlue.setArtist("A Googler");
-                mMediaPlayerGlue.setTitle("Swimming with the fishes");
-
-                mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
-                mMediaPlayerGlue.setHost(host);
-            }
-        }, 3000);
+    void switchAnotherGlue() {
+        mMediaPlayerGlue = new PlaybackTransportControlGlueSample(getActivity(),
+                new MediaPlayerAdapter(getActivity()));
+        mMediaPlayerGlue.setMode(PlaybackControlsRow.RepeatAction.INDEX_ONE);
+        mMediaPlayerGlue.setSubtitle("A Googler");
+        mMediaPlayerGlue.setTitle("Swimming with the fishes");
+        mMediaPlayerGlue.getPlayerAdapter().setDataSource(
+                Uri.parse("http://techslides.com/demos/sample-videos/small.mp4"));
+        mMediaPlayerGlue.setHost(mHost);
+        loadSeekData(mMediaPlayerGlue);
+        playWhenReady(mMediaPlayerGlue);
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
index 3c2c1ac..d7539ae 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
@@ -19,68 +19,131 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v17.leanback.app.VideoSupportFragmentGlueHost;
-import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.MediaPlayerAdapter;
 import android.support.v17.leanback.media.PlaybackGlue;
+import android.support.v17.leanback.media.PlaybackTransportControlGlue;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
 
 /**
  * Fragment demonstrating the use of {@link android.support.v17.leanback.app.VideoSupportFragment} to
- * render video with playback controls.
+ * render video with playback controls. And demonstrates video seeking with thumbnails.
+ *
+ * Generate 1 frame per second thumbnail bitmaps and put on sdcard:
+ * <pre>
+ * sudo apt-get install libav-tools
+ * avconv -i input.mp4 -s 240x135 -vsync 1 -r 1 -an -y -qscale 8 frame_%04d.jpg
+ * adb shell mkdir /sdcard/seek
+ * adb push frame_*.jpg /sdcard/seek/
+ * </pre>
+ * Change to 1 frame per minute: use "-r 1/60".
+ * For more options, see https://wiki.libav.org/Snippets/avconv
+ *
+ * <p>
+ * Showcase:
+ * </p>
+ * <li>Auto play when ready</li>
+ * <li>Set seek provider</li>
+ * <li>switch MediaSource</li>
+ * <li>switch PlaybackGlue</li>
  */
 public class SampleVideoSupportFragment extends android.support.v17.leanback.app.VideoSupportFragment {
-    private MediaPlayerGlue mMediaPlayerGlue;
+    private PlaybackTransportControlGlueSample<MediaPlayerAdapter> mMediaPlayerGlue;
+
+    final VideoSupportFragmentGlueHost mHost = new VideoSupportFragmentGlueHost(SampleVideoSupportFragment.this);
+
+    static void playWhenReady(PlaybackGlue glue) {
+        if (glue.isPrepared()) {
+            glue.play();
+        } else {
+            glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+                @Override
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        glue.removePlayerCallback(this);
+                        glue.play();
+                    }
+                }
+            });
+        }
+    }
+
+    static void loadSeekData(final PlaybackTransportControlGlue glue) {
+        if (glue.isPrepared()) {
+            glue.setSeekProvider(new PlaybackSeekDiskDataProvider(
+                    glue.getDuration(),
+                    1000,
+                    "/sdcard/seek/frame_%04d.jpg"));
+        } else {
+            glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+                @Override
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        glue.removePlayerCallback(this);
+                        PlaybackTransportControlGlue transportControlGlue =
+                                (PlaybackTransportControlGlue) glue;
+                        transportControlGlue.setSeekProvider(new PlaybackSeekDiskDataProvider(
+                                transportControlGlue.getDuration(),
+                                1000,
+                                "/sdcard/seek/frame_%04d.jpg"));
+                    }
+                }
+            });
+        }
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mMediaPlayerGlue = new PlaybackTransportControlGlueSample(getActivity(),
+                new MediaPlayerAdapter(getActivity()));
+        mMediaPlayerGlue.setHost(mHost);
+        mMediaPlayerGlue.setMode(PlaybackControlsRow.RepeatAction.INDEX_NONE);
+        mMediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            boolean mSecondCompleted = false;
+            @Override
+            public void onPlayCompleted(PlaybackGlue glue) {
+                if (!mSecondCompleted) {
+                    mSecondCompleted = true;
+                    mMediaPlayerGlue.setSubtitle("Leanback artist Changed!");
+                    mMediaPlayerGlue.setTitle("Leanback team at work");
+                    String uriPath = "https://storage.googleapis.com/android-tv/Sample videos/"
+                            + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4";
+                    mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
+                    loadSeekData(mMediaPlayerGlue);
+                    playWhenReady(mMediaPlayerGlue);
+                } else {
+                    mMediaPlayerGlue.removePlayerCallback(this);
+                    switchAnotherGlue();
+                }
+            }
+        });
+        mMediaPlayerGlue.setSubtitle("Leanback artist");
+        mMediaPlayerGlue.setTitle("Leanback team at work");
+        String uriPath = "https://storage.googleapis.com/android-tv/Sample videos/"
+                + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4";
+        mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
+        loadSeekData(mMediaPlayerGlue);
+        playWhenReady(mMediaPlayerGlue);
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
+    public void onPause() {
+        if (mMediaPlayerGlue != null) {
+            mMediaPlayerGlue.pause();
+        }
+        super.onPause();
     }
 
-    VideoSupportFragmentGlueHost host = new VideoSupportFragmentGlueHost(SampleVideoSupportFragment.this);
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        getView().postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
-                mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
-                mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
-                    @Override
-                    public void onReadyForPlayback() {
-                        mMediaPlayerGlue.play();
-                    }
-                });
-                mMediaPlayerGlue.setArtist("Leanback");
-                mMediaPlayerGlue.setTitle("Leanback team at work");
-                String uriPath = "android.resource://com.example.android.leanback/raw/browse";
-                mMediaPlayerGlue.setMediaSource(Uri.parse(uriPath));
-                mMediaPlayerGlue.setHost(host);
-            }
-        }, 500);
-
-
-        getView().postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
-                mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
-                mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
-                    @Override
-                    public void onReadyForPlayback() {
-                        mMediaPlayerGlue.play();
-                    }
-                });
-                mMediaPlayerGlue.setArtist("A Googler");
-                mMediaPlayerGlue.setTitle("Swimming with the fishes");
-
-                mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
-                mMediaPlayerGlue.setHost(host);
-            }
-        }, 3000);
+    void switchAnotherGlue() {
+        mMediaPlayerGlue = new PlaybackTransportControlGlueSample(getActivity(),
+                new MediaPlayerAdapter(getActivity()));
+        mMediaPlayerGlue.setMode(PlaybackControlsRow.RepeatAction.INDEX_ONE);
+        mMediaPlayerGlue.setSubtitle("A Googler");
+        mMediaPlayerGlue.setTitle("Swimming with the fishes");
+        mMediaPlayerGlue.getPlayerAdapter().setDataSource(
+                Uri.parse("http://techslides.com/demos/sample-videos/small.mp4"));
+        mMediaPlayerGlue.setHost(mHost);
+        loadSeekData(mMediaPlayerGlue);
+        playWhenReady(mMediaPlayerGlue);
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
index 0302f68..40883bb 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
@@ -23,6 +23,7 @@
 public class StringPresenter extends Presenter {
     private static final String TAG = "StringPresenter";
 
+    @Override
     public ViewHolder onCreateViewHolder(ViewGroup parent) {
         Log.d(TAG, "onCreateViewHolder");
         final Context context = parent.getContext();
@@ -34,12 +35,14 @@
         return new ViewHolder(tv);
     }
 
+    @Override
     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
         Log.d(TAG, "onBindViewHolder for " + item.toString());
         ((TextView) viewHolder.view).setText(item.toString());
     }
 
+    @Override
     public void onUnbindViewHolder(ViewHolder viewHolder) {
-        Log.d(TAG, "onUnbindViewHolder"); 
+        Log.d(TAG, "onUnbindViewHolder");
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
index 68de793..f7b8c8c 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
@@ -65,6 +65,7 @@
         }
         // simulates in a real world use case  data being loaded two seconds later
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 loadData();
                 startEntranceTransition();
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java
index e8f0cc8..6843e55 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java
@@ -68,6 +68,7 @@
         }
         // simulates in a real world use case  data being loaded two seconds later
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 loadData();
                 startEntranceTransition();
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoActivityWithDetailedCard.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoActivityWithDetailedCard.java
new file mode 100644
index 0000000..5acecd7
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoActivityWithDetailedCard.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.os.BuildCompat;
+
+/**
+ * Activity that hosts VideoConsumptionExampleFragment.
+ *
+ * The main purpose to add this activity is to observe the bug b/28003943
+ */
+public class VideoActivityWithDetailedCard extends Activity {
+
+    public static final String TAG = "VideoExampleActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.video_activity_detailed_card);
+
+        if (savedInstanceState == null) {
+            FragmentTransaction ft = getFragmentManager().beginTransaction();
+            ft.add(R.id.videoFragment, new VideoConsumptionWithDetailCardFragment(),
+                    VideoConsumptionWithDetailCardFragment.TAG);
+            ft.commit();
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        // This part is necessary to ensure that getIntent returns the latest intent when
+        // VideoExampleActivity is started. By default, getIntent() returns the initial intent
+        // that was set from another activity that started VideoExampleActivity. However, we need
+        // to update this intent when for example, user clicks on another video when the currently
+        // playing video is in PIP mode, and a new video needs to be started.
+        setIntent(intent);
+    }
+
+    /**
+     * Helper function to determine if picture in picture mode is supported or not
+     * @param context current context
+     * @return if Picture in Picture mode is supported or not
+     */
+    public static boolean supportsPictureInPicture(Context context) {
+        return BuildCompat.isAtLeastN()
+                && context.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_PICTURE_IN_PICTURE);
+    }
+}
+
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoConsumptionWithDetailCardFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoConsumptionWithDetailCardFragment.java
new file mode 100644
index 0000000..dd40c8f
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoConsumptionWithDetailCardFragment.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+package com.example.android.leanback;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v17.leanback.app.VideoFragment;
+import android.support.v17.leanback.app.VideoFragmentGlueHost;
+import android.support.v17.leanback.media.MediaPlayerAdapter;
+import android.support.v17.leanback.media.PlaybackBannerControlGlue;
+import android.support.v17.leanback.media.PlaybackGlue;
+
+/**
+ * Fragment used as Control Glue's host
+ */
+public class VideoConsumptionWithDetailCardFragment extends VideoFragment {
+
+    public static final String TAG = "VideoConsumptionWithDetailCardFragment";
+    // A valid video URL to play video. So the progress bar can be seen to reproduce the bug.
+    private static final String VIDEO_URL =
+            "https://storage.googleapis.com/android-tv/Sample videos/"
+                    + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4";
+    public static final String TITLE = "Diving with Sharks";
+    public static final String SUBTITLE = "A Googler";
+
+    private PlaybackBannerControlGlue<MediaPlayerAdapter> mMediaPlayerGlue;
+    final VideoFragmentGlueHost mHost = new VideoFragmentGlueHost(this);
+
+    /**
+     * helper function for playBackGlue to add/ remove callbacks
+     *
+     * @param glue The playback glue attached to this fragment
+     */
+    private static void playWhenReady(PlaybackGlue glue) {
+        if (glue.isPrepared()) {
+            glue.play();
+        } else {
+            glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+                @Override
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        glue.removePlayerCallback(this);
+                        glue.play();
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        int[] defuatSpeed = new int[]{1};
+        mMediaPlayerGlue = new PlaybackBannerControlGlue<>(getActivity(), defuatSpeed,
+                new MediaPlayerAdapter(getActivity()));
+        // attach player glue to current host
+        mMediaPlayerGlue.setHost(mHost);
+
+        // add image resource to the PlaybackControlGlue
+        mMediaPlayerGlue.setArt(getActivity().getDrawable(R.drawable.google_map));
+
+        // meta information for video player
+        mMediaPlayerGlue.setTitle(TITLE);
+        mMediaPlayerGlue.setSubtitle(SUBTITLE);
+        mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(VIDEO_URL));
+        playWhenReady(mMediaPlayerGlue);
+        setBackgroundType(BG_LIGHT);
+    }
+
+    @Override
+    public void onPause() {
+        if (mMediaPlayerGlue != null) {
+            mMediaPlayerGlue.pause();
+        }
+        super.onPause();
+    }
+}
+
diff --git a/samples/SupportLeanbackJank/AndroidManifest.xml b/samples/SupportLeanbackJank/AndroidManifest.xml
new file mode 100644
index 0000000..348f2d6
--- /dev/null
+++ b/samples/SupportLeanbackJank/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.leanbackjank">
+    <uses-feature
+        android:name="android.hardware.touchscreen"
+        android:required="false"/>
+
+    <uses-feature
+        android:name="android.software.leanback"
+        android:required="true"/>
+
+    <application
+        android:banner="@drawable/app_banner"
+        android:label="@string/app_name"
+        android:logo="@drawable/app_banner"
+        android:theme="@style/JankApp">
+        <activity
+            android:name=".ui.MainActivity"
+            android:banner="@drawable/app_banner"
+            android:icon="@drawable/app_banner"
+            android:label="@string/app_name"
+            android:logo="@drawable/app_banner"
+            android:screenOrientation="landscape"
+            android:theme="@style/Theme.Leanback.Browse">
+            <intent-filter>
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".ui.VideoActivity"
+            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+            android:launchMode="singleTask"
+            android:resizeableActivity="true"
+            android:supportsPictureInPicture="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/samples/SupportLeanbackJank/README.txt b/samples/SupportLeanbackJank/README.txt
new file mode 100644
index 0000000..09de019
--- /dev/null
+++ b/samples/SupportLeanbackJank/README.txt
@@ -0,0 +1,6 @@
+Sample leanback browser app intended to represent current best practices, while providing a set of
+features and capabilities that are relevant to performance.
+
+This app's initial purpose is to aid in the testing of hardware performance for Android TV. It is
+used in tests that are used in a test suite designed to verify hardware performance. It is placed
+here because it might also prove useful for other testing scenarios.
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/build.gradle b/samples/SupportLeanbackJank/build.gradle
new file mode 100644
index 0000000..4900a65
--- /dev/null
+++ b/samples/SupportLeanbackJank/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.application'
+
+dependencies {
+    implementation 'com.github.bumptech.glide:glide:3.6.1'
+    implementation project(':support-leanback-v17')
+    implementation project(':support-preference-leanback-v17')
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 17
+        targetSdkVersion project.ext.currentSdk
+    }
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.aidl.srcDirs = ['src']
+        main.res.srcDirs = ['res']
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
+    lintOptions {
+        abortOnError true
+        check 'NewApi'
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
diff --git a/samples/SupportLeanbackJank/res/drawable/android_header.png b/samples/SupportLeanbackJank/res/drawable/android_header.png
new file mode 100644
index 0000000..56206ec
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/drawable/android_header.png
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/drawable/app_banner.png b/samples/SupportLeanbackJank/res/drawable/app_banner.png
new file mode 100644
index 0000000..4eeaa6f
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/drawable/app_banner.png
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/drawable/default_background.xml b/samples/SupportLeanbackJank/res/drawable/default_background.xml
new file mode 100644
index 0000000..e367de8
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/drawable/default_background.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <gradient
+            android:startColor="@color/jank_yellow"
+            android:endColor="@color/jank_green"
+            android:angle="0" />
+</shape>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/res/drawable/movie.png b/samples/SupportLeanbackJank/res/drawable/movie.png
new file mode 100644
index 0000000..cb5cb6d
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/drawable/movie.png
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/layout/header_item.xml b/samples/SupportLeanbackJank/res/layout/header_item.xml
new file mode 100644
index 0000000..2e11b08
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/layout/header_item.xml
@@ -0,0 +1,34 @@
+<?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"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/header_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp" />
+    <TextView
+        android:id="@+id/header_label"
+        android:textColor="#000000"
+        android:layout_marginTop="6dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/res/layout/main.xml b/samples/SupportLeanbackJank/res/layout/main.xml
new file mode 100644
index 0000000..d1fdee2
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/layout/main.xml
@@ -0,0 +1,29 @@
+<?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
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/main_frame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <fragment
+        android:id="@+id/main_browse_fragment"
+        android:name="com.google.android.leanbackjank.ui.MainFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</FrameLayout>
diff --git a/samples/SupportLeanbackJank/res/raw/bbb_480p.mp4 b/samples/SupportLeanbackJank/res/raw/bbb_480p.mp4
new file mode 100644
index 0000000..9beaff3
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/raw/bbb_480p.mp4
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/raw/bbb_sunflower_2160p_60fps.mp4 b/samples/SupportLeanbackJank/res/raw/bbb_sunflower_2160p_60fps.mp4
new file mode 100644
index 0000000..60f0bec
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/raw/bbb_sunflower_2160p_60fps.mp4
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/raw/testvideo_1080p_60fps.mp4 b/samples/SupportLeanbackJank/res/raw/testvideo_1080p_60fps.mp4
new file mode 100644
index 0000000..9878a2a
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/raw/testvideo_1080p_60fps.mp4
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/values-v21/styles.xml b/samples/SupportLeanbackJank/res/values-v21/styles.xml
new file mode 100644
index 0000000..ef7efe9
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/values-v21/styles.xml
@@ -0,0 +1,24 @@
+<?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
+  -->
+
+<resources>
+    <style name="JankApp" parent="Theme.Leanback">
+        <item name="android:colorPrimary">@color/jank_yellow</item>
+        <item name="android:windowAllowReturnTransitionOverlap">true</item>
+        <item name="android:windowAllowEnterTransitionOverlap">true</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/res/values/colors.xml b/samples/SupportLeanbackJank/res/values/colors.xml
new file mode 100644
index 0000000..e89b729
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?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
+  -->
+
+<resources>
+    <color name="jank_yellow">#FABB05</color>
+    <color name="jank_green">#34A853</color>
+    <color name="jank_red">#EA4335</color>
+    <color name="jank_blue">#4285F4</color>
+    <color name="search_opaque">#ffaa3f</color>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/res/values/dimens.xml b/samples/SupportLeanbackJank/res/values/dimens.xml
new file mode 100644
index 0000000..e2b1f9f
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+<resources>
+    <dimen name="grid_item_width">100dp</dimen>
+    <dimen name="grid_item_height">100dp</dimen>
+    <dimen name="card_width">156dp</dimen>
+    <dimen name="card_height">88dp</dimen>
+</resources>
diff --git a/samples/SupportLeanbackJank/res/values/strings.xml b/samples/SupportLeanbackJank/res/values/strings.xml
new file mode 100644
index 0000000..37ceb80
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?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
+  -->
+
+<resources>
+    <string name="app_name"><![CDATA[Leanback library jank test application]]></string>
+    <string name="browse_title"><![CDATA[Leanback library jank test application]]></string>
+</resources>
diff --git a/samples/SupportLeanbackJank/res/values/styles.xml b/samples/SupportLeanbackJank/res/values/styles.xml
new file mode 100644
index 0000000..ba3a5de
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/values/styles.xml
@@ -0,0 +1,21 @@
+<?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
+  -->
+
+<resources>
+    <style name="JankApp" parent="Theme.Leanback">
+    </style>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java
new file mode 100644
index 0000000..722b7d6
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java
@@ -0,0 +1,32 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank;
+
+public final class IntentDefaults {
+
+    public static final int CATEGORY_COUNT = 20;
+    public static final int ENTRIES_PER_CATEGORY = 20;
+    public static final int CARD_WIDTH = 313;
+    public static final int CARD_HEIGHT = 176;
+    public static final int WHICH_VIDEO = IntentKeys.NO_VIDEO;
+    public static final boolean DISABLE_SHADOWS = false;
+    public static final boolean PLAY_VIDEO = false;
+    public static final boolean USE_SINGLE_BITMAP = false;
+
+    private IntentDefaults() {
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java
new file mode 100644
index 0000000..6d04cb0
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank;
+
+public final class IntentKeys {
+
+    public static final String CATEGORY_COUNT = "CATEGORY_COUNT";
+    public static final String ENTRIES_PER_CATEGORY = "ENTRIES_PER_CATEGORY";
+    public static final String CARD_WIDTH = "CARD_WIDTH";
+    public static final String CARD_HEIGHT = "CARD_HEIGHT";
+    public static final String DISABLE_SHADOWS = "ENABLE_SHADOWS";
+    public static final String WHICH_VIDEO = "WHICH_VIDEO";
+    public static final String USE_SINGLE_BITMAP = "USE_SINGLE_BITMAP";
+
+    // Define values for WHICH_VIDEO.
+    public static final int NO_VIDEO = 0;
+    public static final int VIDEO_480P_60FPS = 1;
+    public static final int VIDEO_1080P_60FPS = 2;
+    public static final int VIDEO_2160P_60FPS = 3;
+
+    private IntentKeys() {
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java
new file mode 100644
index 0000000..909da45
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java
@@ -0,0 +1,141 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.data;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
+import android.net.Uri;
+import android.util.Log;
+
+import com.google.android.leanbackjank.model.VideoInfo;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Generates fake data for populating the video cards.
+ */
+public final class VideoProvider {
+
+    private static final String TAG = "JankVideoProvider";
+    private static final int STUDIO_COUNT = 13;
+
+    private static final List<Integer> COLORS = Arrays.asList(
+            Color.parseColor("#4285F4"),
+            Color.parseColor("#EA4335"),
+            Color.parseColor("#FABB05"),
+            Color.parseColor("#34A853")
+    );
+
+    private VideoProvider() {
+    }
+
+    public static HashMap<String, List<VideoInfo>> buildMedia(int categoryCount, int entriesPerCat,
+            int width, int height, Context context, boolean useSingleBitmap) {
+        HashMap<String, List<VideoInfo>> ret = new HashMap<>();
+
+        int count = 0;
+        String rootPath = String.format(Locale.US, "%s/%d_%d/", context.getFilesDir(), width,
+                height);
+        File rootDirectory = new File(rootPath);
+        rootDirectory.mkdirs();
+
+        for (int i = 0; i < categoryCount; i++) {
+            List<VideoInfo> list = new ArrayList<>();
+            String category = "Category " + Integer.toString(i);
+            ret.put(category, list);
+            for (int j = 0; j < entriesPerCat; j++) {
+                String description = String.format(Locale.US,
+                        "The gripping yet whimsical description of videoInfo %d in category %d", j,
+                        i);
+                String title = String.format(Locale.US, "Video %d-%d", i, j);
+                String studio = String.format(Locale.US, "Studio %d", count % STUDIO_COUNT);
+
+                VideoInfo videoInfo = new VideoInfo();
+                videoInfo.setId(Integer.toString(count));
+                videoInfo.setTitle(title);
+                videoInfo.setDescription(description);
+                videoInfo.setStudio(studio);
+                videoInfo.setCategory(category);
+
+                int videoNumber = useSingleBitmap ? 0 : count;
+                File file = new File(rootPath + videoNumber + ".jpg");
+                if (!file.exists()) {
+                    makeIcon(width, height, "Jank", file);
+                }
+                videoInfo.setImageUri(Uri.fromFile(file));
+
+                count++;
+
+                list.add(videoInfo);
+            }
+        }
+
+        return ret;
+    }
+
+    public static void makeIcon(int width, int height, String string, File file) {
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+
+        Collections.shuffle(COLORS);
+
+        Paint paint = new Paint();
+        paint.setAntiAlias(true);
+        paint.setTextAlign(Paint.Align.CENTER);
+
+        // Draw background gradient.
+        Shader shader = new LinearGradient(0, 0, width - 1, height - 1, COLORS.get(0),
+                COLORS.get(1), TileMode.CLAMP);
+        paint.setShader(shader);
+        canvas.drawRect(0, 0, width - 1, height - 1, paint);
+
+        paint.setTextSize(height * 0.5f);
+        Rect rect = new Rect();
+        paint.getTextBounds(string, 0, string.length(), rect);
+
+        int hOffset = (height - rect.height()) / 2;
+        int wOffset = (width - rect.width()) / 2;
+        shader = new LinearGradient(wOffset, height - hOffset, width - wOffset, hOffset,
+                COLORS.get(2), COLORS.get(3), TileMode.CLAMP);
+        paint.setShader(shader);
+
+        canvas.drawText(string, width / 2, (height + rect.height()) / 2, paint);
+
+        try {
+            FileOutputStream outputStream = new FileOutputStream(file);
+            bitmap.compress(CompressFormat.JPEG, 90, outputStream);
+        } catch (IOException e) {
+            Log.e(TAG, "Cannot write image to file: " + file, e);
+        }
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/model/VideoInfo.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/model/VideoInfo.java
new file mode 100644
index 0000000..7e8cc8a
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/model/VideoInfo.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.google.android.leanbackjank.model;
+
+import android.net.Uri;
+
+/**
+ * VideoInfo class represents video entity with title, description, image thumbs and video url.
+ */
+public class VideoInfo {
+    private String mId;
+    private String mTitle;
+    private String mDescription;
+    private String mStudio;
+    private String mCategory;
+    private Uri mImageUri;
+
+    public VideoInfo() {
+    }
+
+    public String getId() {
+        return mId;
+    }
+
+    public void setId(String id) {
+        mId = id;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public void setTitle(String title) {
+        mTitle = title;
+    }
+
+    public String getDescription() {
+        return mDescription;
+    }
+
+    public void setDescription(String description) {
+        mDescription = description;
+    }
+
+    public String getStudio() {
+        return mStudio;
+    }
+
+    public void setStudio(String studio) {
+        mStudio = studio;
+    }
+
+    public String getCategory() {
+        return mCategory;
+    }
+
+    public void setCategory(String category) {
+        mCategory = category;
+    }
+
+    public Uri getImageUri() {
+        return mImageUri;
+    }
+
+    public void setImageUri(Uri imageUri) {
+        mImageUri = imageUri;
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java
new file mode 100644
index 0000000..d301c4e
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java
@@ -0,0 +1,91 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.presenter;
+
+import android.support.v17.leanback.widget.ImageCardView;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v4.content.res.ResourcesCompat;
+import android.view.ViewGroup;
+
+import com.bumptech.glide.Glide;
+import com.google.android.leanbackjank.R;
+import com.google.android.leanbackjank.model.VideoInfo;
+
+public class CardPresenter extends Presenter {
+    private int mSelectedBackgroundColor = -1;
+    private int mDefaultBackgroundColor = -1;
+    private int mCardWidth;
+    private int mCardHeight;
+
+    public CardPresenter(int width, int height) {
+        mCardWidth = width;
+        mCardHeight = height;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent) {
+        mDefaultBackgroundColor =
+                ResourcesCompat.getColor(parent.getResources(), R.color.jank_blue, null);
+        mSelectedBackgroundColor =
+                ResourcesCompat.getColor(parent.getResources(), R.color.jank_red, null);
+
+        ImageCardView cardView = new ImageCardView(parent.getContext()) {
+            @Override
+            public void setSelected(boolean selected) {
+                updateCardBackgroundColor(this, selected);
+                super.setSelected(selected);
+            }
+        };
+
+        cardView.setFocusable(true);
+        cardView.setFocusableInTouchMode(true);
+        updateCardBackgroundColor(cardView, false);
+        return new ViewHolder(cardView);
+    }
+
+    private void updateCardBackgroundColor(ImageCardView view, boolean selected) {
+        int color = selected ? mSelectedBackgroundColor : mDefaultBackgroundColor;
+
+        // Both background colors should be set because the view's
+        // background is temporarily visible during animations.
+        view.setBackgroundColor(color);
+        view.findViewById(R.id.info_field).setBackgroundColor(color);
+    }
+
+    @Override
+    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
+        VideoInfo videoInfo = (VideoInfo) item;
+
+        ImageCardView cardView = (ImageCardView) viewHolder.view;
+        cardView.setTitleText(videoInfo.getTitle());
+        cardView.setContentText(videoInfo.getStudio());
+        cardView.setMainImageDimensions(mCardWidth, mCardHeight);
+
+        Glide.with(cardView.getContext())
+                .load(videoInfo.getImageUri())
+                .into(cardView.getMainImageView());
+    }
+
+    @Override
+    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
+        ImageCardView cardView = (ImageCardView) viewHolder.view;
+
+        // Remove references to images so that the garbage collector can free up memory.
+        cardView.setBadgeImage(null);
+        cardView.setMainImage(null);
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java
new file mode 100644
index 0000000..3374f5e
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java
@@ -0,0 +1,57 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.presenter;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v4.content.res.ResourcesCompat;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.google.android.leanbackjank.R;
+
+public class GridItemPresenter extends Presenter {
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent) {
+        TextView view = new TextView(parent.getContext());
+
+        Resources res = parent.getResources();
+        int width = res.getDimensionPixelSize(R.dimen.grid_item_width);
+        int height = res.getDimensionPixelSize(R.dimen.grid_item_height);
+
+        view.setLayoutParams(new ViewGroup.LayoutParams(width, height));
+        view.setFocusable(true);
+        view.setFocusableInTouchMode(true);
+        view.setBackgroundColor(
+                ResourcesCompat.getColor(parent.getResources(), R.color.jank_yellow, null));
+        view.setTextColor(Color.WHITE);
+        view.setGravity(Gravity.CENTER);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+        ((TextView) viewHolder.view).setText((String) item);
+    }
+
+    @Override
+    public void onUnbindViewHolder(ViewHolder viewHolder) {
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java
new file mode 100644
index 0000000..6d6b36f
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.presenter;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.RowHeaderPresenter;
+import android.support.v4.content.res.ResourcesCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.google.android.leanbackjank.R;
+
+public class HeaderItemPresenter extends RowHeaderPresenter {
+    private float mUnselectedAlpha;
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
+        mUnselectedAlpha = viewGroup.getResources()
+                .getFraction(R.fraction.lb_browse_header_unselect_alpha, 1, 1);
+        LayoutInflater inflater = (LayoutInflater) viewGroup.getContext()
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        View view = inflater.inflate(R.layout.header_item, null);
+        view.setAlpha(mUnselectedAlpha); // Initialize icons to be at half-opacity.
+
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
+        HeaderItem headerItem = ((ListRow) item).getHeaderItem();
+        View rootView = viewHolder.view;
+        rootView.setFocusable(true);
+
+        ImageView iconView = (ImageView) rootView.findViewById(R.id.header_icon);
+        Drawable icon = ResourcesCompat.getDrawable(
+                rootView.getResources(), R.drawable.android_header, null);
+        iconView.setImageDrawable(icon);
+
+        TextView label = (TextView) rootView.findViewById(R.id.header_label);
+        label.setText(headerItem.getName());
+    }
+
+    @Override
+    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
+        // no op
+    }
+
+    // TODO: This is a temporary fix. Remove me when leanback onCreateViewHolder no longer sets the
+    // mUnselectAlpha, and also assumes the xml inflation will return a RowHeaderView.
+    @Override
+    protected void onSelectLevelChanged(RowHeaderPresenter.ViewHolder holder) {
+        holder.view.setAlpha(
+                mUnselectedAlpha + holder.getSelectLevel() * (1.0f - mUnselectedAlpha));
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainActivity.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainActivity.java
new file mode 100644
index 0000000..0097b31
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainActivity.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.google.android.leanbackjank.R;
+
+
+/**
+ * MainActivity class that loads MainFragment
+ */
+public class MainActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    @Override
+    protected void onStop() {
+        Intent intent = new Intent(this, VideoActivity.class);
+        startActivity(intent);
+
+        super.onStop();
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
new file mode 100644
index 0000000..42abf3e
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
@@ -0,0 +1,158 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.ui;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v17.leanback.app.BackgroundManager;
+import android.support.v17.leanback.app.BrowseFragment;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v4.content.res.ResourcesCompat;
+
+import com.google.android.leanbackjank.IntentDefaults;
+import com.google.android.leanbackjank.IntentKeys;
+import com.google.android.leanbackjank.R;
+import com.google.android.leanbackjank.data.VideoProvider;
+import com.google.android.leanbackjank.model.VideoInfo;
+import com.google.android.leanbackjank.presenter.CardPresenter;
+import com.google.android.leanbackjank.presenter.GridItemPresenter;
+import com.google.android.leanbackjank.presenter.HeaderItemPresenter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Main class to show BrowseFragment with header and rows of videos
+ */
+public class MainFragment extends BrowseFragment {
+
+    private BackgroundManager mBackgroundManager;
+    private ArrayObjectAdapter mRowsAdapter;
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        // Define defaults.
+        int categoryCount = IntentDefaults.CATEGORY_COUNT;
+        int entriesPerCat = IntentDefaults.ENTRIES_PER_CATEGORY;
+        boolean disableShadows = IntentDefaults.DISABLE_SHADOWS;
+        int cardWidth = IntentDefaults.CARD_WIDTH;
+        int cardHeight = IntentDefaults.CARD_HEIGHT;
+        int whichVideo = IntentDefaults.WHICH_VIDEO;
+        boolean useSingleBitmap = IntentDefaults.USE_SINGLE_BITMAP;
+
+        Intent intent = getActivity().getIntent();
+        if (intent.getExtras() != null) {
+            categoryCount = intent.getIntExtra(IntentKeys.CATEGORY_COUNT, categoryCount);
+            entriesPerCat = intent.getIntExtra(IntentKeys.ENTRIES_PER_CATEGORY, entriesPerCat);
+            disableShadows = intent.getBooleanExtra(IntentKeys.DISABLE_SHADOWS, disableShadows);
+            cardWidth = intent.getIntExtra(IntentKeys.CARD_WIDTH, cardWidth);
+            cardHeight = intent.getIntExtra(IntentKeys.CARD_HEIGHT, cardHeight);
+            whichVideo = intent.getIntExtra(IntentKeys.WHICH_VIDEO, whichVideo);
+            useSingleBitmap = intent.getBooleanExtra(IntentKeys.USE_SINGLE_BITMAP, useSingleBitmap);
+        }
+
+        loadVideoData(categoryCount, entriesPerCat, disableShadows, useSingleBitmap, cardWidth,
+                cardHeight);
+        setBackground();
+        setupUIElements();
+
+        if (whichVideo != IntentKeys.NO_VIDEO) {
+            int resource = 0;
+            /* For info on how to generate videos see:
+             * https://docs.google.com/document/d/1HV8O-Nm4rc2DwVwiZmT4Wa9pf8XttWndg9saGncTRGw
+             */
+            if (whichVideo == IntentKeys.VIDEO_2160P_60FPS) {
+                resource = R.raw.bbb_sunflower_2160p_60fps;
+            } else if (whichVideo == IntentKeys.VIDEO_1080P_60FPS) {
+                resource = R.raw.testvideo_1080p_60fps;
+            } else if (whichVideo == IntentKeys.VIDEO_480P_60FPS) {
+                resource = R.raw.bbb_480p;
+            }
+            Uri uri = Uri.parse("android.resource://" + getActivity().getPackageName() + "/"
+                    + resource);
+            Intent videoIntent = new Intent(Intent.ACTION_VIEW, uri, getActivity(),
+                    VideoActivity.class);
+            startActivity(videoIntent);
+        }
+    }
+
+    private void setBackground() {
+        mBackgroundManager = BackgroundManager.getInstance(getActivity());
+        mBackgroundManager.attach(getActivity().getWindow());
+        mBackgroundManager.setDrawable(
+                ResourcesCompat.getDrawable(getResources(), R.drawable.default_background, null));
+    }
+
+    private void setupUIElements() {
+        setBadgeDrawable(ResourcesCompat.getDrawable(
+                getActivity().getResources(), R.drawable.app_banner, null));
+        // Badge, when set, takes precedent over title
+        setTitle(getString(R.string.browse_title));
+        setHeadersState(HEADERS_ENABLED);
+        setHeadersTransitionOnBackEnabled(true);
+        // set headers background color
+        setBrandColor(ResourcesCompat.getColor(getResources(), R.color.jank_yellow, null));
+        // set search icon color
+        setSearchAffordanceColor(
+                ResourcesCompat.getColor(getResources(), R.color.search_opaque, null));
+
+        setHeaderPresenterSelector(new PresenterSelector() {
+            @Override
+            public Presenter getPresenter(Object o) {
+                return new HeaderItemPresenter();
+            }
+        });
+    }
+
+    private void loadVideoData(int categoryCount, int entriesPerCat, boolean disableShadows,
+            boolean useSingleBitmap, int cardWidth, int cardHeight) {
+        ListRowPresenter listRowPresenter = new ListRowPresenter();
+        listRowPresenter.setShadowEnabled(!disableShadows);
+        mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
+        HashMap<String, List<VideoInfo>> data = VideoProvider.buildMedia(categoryCount,
+                entriesPerCat, cardWidth, cardHeight, getActivity(), useSingleBitmap);
+        CardPresenter cardPresenter = new CardPresenter(cardWidth, cardHeight);
+
+        int i = 0;
+        for (Map.Entry<String, List<VideoInfo>> entry : data.entrySet()) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
+            for (VideoInfo videoInfo : entry.getValue()) {
+                listRowAdapter.add(videoInfo);
+            }
+            HeaderItem header = new HeaderItem(i++, entry.getKey());
+            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+        }
+
+        ArrayObjectAdapter settingsListAdapter = new ArrayObjectAdapter(new GridItemPresenter());
+        for (int j = 0; j < entriesPerCat; j++) {
+            settingsListAdapter.add("Settings " + j);
+        }
+        HeaderItem settingsHeader = new HeaderItem(i++, "Settings");
+        mRowsAdapter.add(new ListRow(settingsHeader, settingsListAdapter));
+
+        setAdapter(mRowsAdapter);
+    }
+}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java
new file mode 100644
index 0000000..90c8552
--- /dev/null
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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
+ */
+
+package com.google.android.leanbackjank.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.VideoView;
+
+public class VideoActivity extends Activity {
+
+    private VideoView mVideoView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+        mVideoView = new VideoView(this);
+        mVideoView.setOnPreparedListener(new OnPreparedListener() {
+            @Override
+            public void onPrepared(MediaPlayer mp) {
+                mp.setLooping(true);
+            }
+        });
+        setContentView(mVideoView);
+
+        if (checkIntent(getIntent())) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                enterPictureInPictureMode();
+            }
+        }
+    }
+
+    private void playVideo(Uri uri) {
+        mVideoView.setVideoURI(uri);
+        mVideoView.start();
+    }
+
+    private boolean checkIntent(Intent intent) {
+        if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+            Uri uri = intent.getData();
+            playVideo(uri);
+            return true;
+        } else {
+            finish();
+            return false;
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        checkIntent(intent);
+    }
+
+    @Override
+    protected void onStop() {
+        if (mVideoView != null) {
+            mVideoView.stopPlayback();
+        }
+        super.onStop();
+        finish();
+    }
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/Android.mk b/samples/SupportLeanbackShowcase/app/src/main/Android.mk
deleted file mode 100644
index 76c933e..0000000
--- a/samples/SupportLeanbackShowcase/app/src/main/Android.mk
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-#LOCAL_JACK_FLAGS := -D jack.import.jar.debug-info=false
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := SupportLeanbackShowcase
-LOCAL_MODULE_TAGS := samples tests
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-support-v4 \
-        android-support-v7-recyclerview \
-        android-support-v7-preference \
-        android-support-v7-appcompat \
-        android-support-v14-preference \
-        android-support-v17-preference-leanback \
-        android-support-v17-leanback \
-	gson-x \
-	picasso-x
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/v17/preference-leanback/res \
-        frameworks/support/v7/preference/res \
-        frameworks/support/v7/appcompat/res \
-        frameworks/support/v14/preference/res \
-        frameworks/support/v17/leanback/res \
-        frameworks/support/v7/recyclerview/res
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.v17.leanback \
-        --extra-packages android.support.v17.preference \
-        --extra-packages android.support.v7.preference \
-        --extra-packages android.support.v14.preference \
-        --extra-packages android.support.v7.appcompat \
-        --extra-packages android.support.v7.recyclerview
-include $(BUILD_PACKAGE)
-
-
-include $(CLEAR_VARS)
-
-LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
-	gson-x:../../../libs/gson-1.7.2.jar \
-	picasso-x:../../../libs/picasso-2.5.2.jar \
-
-include $(BUILD_MULTI_PREBUILT)
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/CharacterCardView.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/CharacterCardView.java
index 962adb0..89117b5 100644
--- a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/CharacterCardView.java
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/CharacterCardView.java
@@ -35,7 +35,7 @@
         setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
-                ImageView mainImage = (ImageView) findViewById(R.id.main_image);
+                ImageView mainImage = findViewById(R.id.main_image);
                 View container = findViewById(R.id.container);
                 if (hasFocus) {
                     container.setBackgroundResource(R.drawable.character_focused);
@@ -50,8 +50,8 @@
     }
 
     public void updateUi(Card card) {
-        TextView primaryText = (TextView) findViewById(R.id.primary_text);
-        final ImageView imageView = (ImageView) findViewById(R.id.main_image);
+        TextView primaryText = findViewById(R.id.primary_text);
+        final ImageView imageView = findViewById(R.id.main_image);
 
         primaryText.setText(card.getTitle());
         if (card.getLocalImageResourceName() != null) {
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/TextCardView.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/TextCardView.java
index 830b474..8f94e2a 100644
--- a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/TextCardView.java
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/cards/TextCardView.java
@@ -35,9 +35,9 @@
     }
 
     public void updateUi(Card card) {
-        TextView extraText = (TextView) findViewById(R.id.extra_text);
-        TextView primaryText = (TextView) findViewById(R.id.primary_text);
-        final ImageView imageView = (ImageView) findViewById(R.id.main_image);
+        TextView extraText = findViewById(R.id.extra_text);
+        TextView primaryText = findViewById(R.id.primary_text);
+        final ImageView imageView = findViewById(R.id.main_image);
 
         extraText.setText(card.getExtraText());
         primaryText.setText(card.getTitle());
diff --git a/samples/SupportLeanbackShowcase/app/src/main/res/drawable-xhdpi/google_map.jpg b/samples/SupportLeanbackShowcase/app/src/main/res/drawable-xhdpi/google_map.jpg
new file mode 100644
index 0000000..447de09
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/res/drawable-xhdpi/google_map.jpg
Binary files differ
diff --git a/samples/SupportPercentDemos/Android.mk b/samples/SupportPercentDemos/Android.mk
deleted file mode 100644
index 54c980f..0000000
--- a/samples/SupportPercentDemos/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := SupportPercentDemos
-LOCAL_MODULE_TAGS := samples
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 7
-LOCAL_DEX_PREOPT := false
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        android-support-percent \
-        android-support-v4 \
-        android-support-v13
-include $(BUILD_PACKAGE)
diff --git a/samples/SupportPercentDemos/AndroidManifest.xml b/samples/SupportPercentDemos/AndroidManifest.xml
index 71fea32..c720dbd 100644
--- a/samples/SupportPercentDemos/AndroidManifest.xml
+++ b/samples/SupportPercentDemos/AndroidManifest.xml
@@ -21,9 +21,6 @@
      to come from a domain that you own or have control over. -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.support.percent">
-
-    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
-
     <application android:label="@string/activity_sample_code"
             android:supportsRtl="true"
             android:icon="@drawable/app_sample_code">
diff --git a/samples/SupportPercentDemos/build.gradle b/samples/SupportPercentDemos/build.gradle
index e3caf1d..863da83 100644
--- a/samples/SupportPercentDemos/build.gradle
+++ b/samples/SupportPercentDemos/build.gradle
@@ -1,14 +1,15 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-percent')
+    implementation project(':support-percent')
 }
 
 android {
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
-        minSdkVersion 11
+        minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -19,7 +20,15 @@
     }
 
     lintOptions {
-        abortOnError false
+        abortOnError true
+        check 'NewApi'
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
     }
 
     compileOptions {
diff --git a/samples/SupportPercentDemos/res/layout/include_percent_frame_layout_content.xml b/samples/SupportPercentDemos/res/layout/include_percent_frame_layout_content.xml
index 2e5a659..091653b 100644
--- a/samples/SupportPercentDemos/res/layout/include_percent_frame_layout_content.xml
+++ b/samples/SupportPercentDemos/res/layout/include_percent_frame_layout_content.xml
@@ -18,12 +18,16 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <View
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         app:layout_widthPercent="60%"
         app:layout_heightPercent="60%"
         app:layout_marginTopPercent="20%"
         app:layout_marginLeftPercent="20%"
         android:background="#FF0000" />
     <View
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="bottom|right"
         app:layout_widthPercent="20%"
         app:layout_heightPercent="20%"
diff --git a/samples/SupportPreferenceDemos/Android.mk b/samples/SupportPreferenceDemos/Android.mk
deleted file mode 100644
index f131aa0..0000000
--- a/samples/SupportPreferenceDemos/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := SupportPreferenceDemos
-LOCAL_MODULE_TAGS := samples
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 14
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES += \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-preference \
-    android-support-v7-recyclerview \
-    android-support-v14-preference \
-    android-support-v17-leanback \
-    android-support-v17-preference-leanback \
-
-LOCAL_RESOURCE_DIR = \
-    $(LOCAL_PATH)/res \
-    frameworks/support/v7/appcompat/res \
-    frameworks/support/v7/preference/res \
-    frameworks/support/v7/recyclerview/res \
-    frameworks/support/v14/preference/res \
-    frameworks/support/v17/leanback/res \
-    frameworks/support/v17/preference-leanback/res
-
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.v7.appcompat \
-        --extra-packages android.support.v7.preference \
-        --extra-packages android.support.v7.recyclerview \
-        --extra-packages android.support.v14.preference \
-        --extra-packages android.support.v17.leanback \
-        --extra-packages android.support.v17.preference \
-        --no-version-vectors
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
diff --git a/samples/SupportPreferenceDemos/AndroidManifest.xml b/samples/SupportPreferenceDemos/AndroidManifest.xml
index e43cb44..4fb2492 100644
--- a/samples/SupportPreferenceDemos/AndroidManifest.xml
+++ b/samples/SupportPreferenceDemos/AndroidManifest.xml
@@ -19,7 +19,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.example.android.supportpreference">
 
-    <uses-sdk android:targetSdkVersion="24"
+    <uses-sdk
         tools:overrideLibrary="android.support.v17.preference, android.support.v17.leanback" />
 
     <uses-feature android:name="android.software.Leanback" android:required="false" />
@@ -27,6 +27,7 @@
     <application android:label="pref demo"
         android:icon="@drawable/app_sample_code"
         android:allowBackup="false"
+        android:supportsRtl="true"
         android:theme="@style/DemoTheme">
 
         <activity android:name=".SupportPreferenceDemos">
diff --git a/samples/SupportPreferenceDemos/build.gradle b/samples/SupportPreferenceDemos/build.gradle
index e59a348..d09e3db 100644
--- a/samples/SupportPreferenceDemos/build.gradle
+++ b/samples/SupportPreferenceDemos/build.gradle
@@ -1,12 +1,12 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-appcompat-v7')
-    compile project(':support-recyclerview-v7')
-    compile project(':support-preference-v7')
-    compile project(':support-preference-v14')
-    compile project(':support-leanback-v17')
-    compile project(':support-preference-leanback-v17')
+    implementation project(':support-appcompat-v7')
+    implementation project(':support-recyclerview-v7')
+    implementation project(':support-preference-v7')
+    implementation project(':support-preference-v14')
+    implementation project(':support-leanback-v17')
+    implementation project(':support-preference-leanback-v17')
 }
 
 android {
@@ -14,6 +14,7 @@
 
     defaultConfig {
         minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -23,8 +24,16 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
         abortOnError false
+        check 'NewApi'
     }
 
     compileOptions {
diff --git a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferences.java b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferences.java
index 298d0e5..b90f637 100644
--- a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferences.java
+++ b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferences.java
@@ -34,8 +34,10 @@
         super.onCreate(savedInstanceState);
 
         // Display the fragment as the main content.
-        getFragmentManager().beginTransaction().replace(android.R.id.content,
-                new PrefsFragment()).commit();
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction().replace(android.R.id.content,
+                    new PrefsFragment()).commit();
+        }
     }
 
     @Override
diff --git a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesCompat.java b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesCompat.java
index c716bed..bc4a943 100644
--- a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesCompat.java
+++ b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesCompat.java
@@ -34,8 +34,10 @@
         super.onCreate(savedInstanceState);
 
         // Display the fragment as the main content.
-        getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
-                new PrefsFragment()).commit();
+        if (savedInstanceState == null) {
+            getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
+                    new PrefsFragment()).commit();
+        }
     }
 
     @Override
diff --git a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
index 35591dc..eb9a4c0 100644
--- a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
+++ b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
@@ -16,6 +16,7 @@
 
 package com.example.android.supportpreference;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Fragment;
 import android.os.Bundle;
@@ -26,14 +27,17 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
+@TargetApi(17)
 public class FragmentSupportPreferencesLeanback extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         // Display the fragment as the main content.
-        getFragmentManager().beginTransaction().replace(android.R.id.content,
-                new SettingsFragment()).commit();
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction().replace(android.R.id.content,
+                    new SettingsFragment()).commit();
+        }
     }
 
 //BEGIN_INCLUDE(support_fragment_leanback)
diff --git a/samples/SupportTransitionDemos/Android.mk b/samples/SupportTransitionDemos/Android.mk
deleted file mode 100644
index 6ebc7af..0000000
--- a/samples/SupportTransitionDemos/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build the samples.
-# We need to add some special AAPT flags to generate R classes
-# for resources that are included from the libraries.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := SupportTransitionDemos
-LOCAL_MODULE_TAGS := samples
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 14
-LOCAL_DEX_PREOPT := false
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        android-support-v4 \
-        android-support-v7-appcompat \
-        android-support-transition
-LOCAL_AAPT_FLAGS := --no-version-vectors
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-include $(BUILD_PACKAGE)
diff --git a/samples/SupportTransitionDemos/AndroidManifest.xml b/samples/SupportTransitionDemos/AndroidManifest.xml
index 4028f47..a58c189 100644
--- a/samples/SupportTransitionDemos/AndroidManifest.xml
+++ b/samples/SupportTransitionDemos/AndroidManifest.xml
@@ -21,9 +21,6 @@
      to come from a domain that you own or have control over. -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.support.transition">
-
-    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />
-
     <application android:label="@string/activity_sample_code"
             android:supportsRtl="true"
             android:icon="@drawable/app_sample_code"
@@ -63,5 +60,59 @@
                 <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
             </intent-filter>
         </activity>
+
+        <activity android:name=".widget.ArcMotionUsage"
+                  android:label="@string/arcMotion"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".widget.ExplodeUsage"
+                  android:label="@string/explode"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".widget.ChangeClipBoundsUsage"
+                  android:label="@string/clipBounds"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".widget.ChangeTransformUsage"
+                  android:label="@string/changeTransform"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".widget.ChangeImageTransformUsage"
+                  android:label="@string/changeImageTransform"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".widget.ReparentImageUsage"
+                  android:label="@string/reparentImage"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/samples/SupportTransitionDemos/build.gradle b/samples/SupportTransitionDemos/build.gradle
index 5ebdc1b..2a4fffc 100644
--- a/samples/SupportTransitionDemos/build.gradle
+++ b/samples/SupportTransitionDemos/build.gradle
@@ -1,8 +1,8 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-transition')
-    compile project(':support-appcompat-v7')
+    implementation project(':support-transition')
+    implementation project(':support-appcompat-v7')
 }
 
 android {
@@ -10,6 +10,7 @@
 
     defaultConfig {
         minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
     }
 
     sourceSets {
@@ -20,12 +21,24 @@
     }
 
     lintOptions {
-        abortOnError false
+        abortOnError true
+        check 'NewApi'
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
     }
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
     }
+
+    aaptOptions {
+        additionalParameters "--no-version-transitions"
+    }
 }
 
diff --git a/samples/SupportTransitionDemos/res/layout/arc_motion.xml b/samples/SupportTransitionDemos/res/layout/arc_motion.xml
new file mode 100644
index 0000000..8dccd0a
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/arc_motion.xml
@@ -0,0 +1,45 @@
+<?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"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Button
+        android:id="@+id/move"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/move"/>
+
+    <FrameLayout
+        android:id="@+id/root"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:padding="64dp">
+
+        <View
+            android:id="@+id/target"
+            android:layout_width="64dp"
+            android:layout_height="64dp"
+            android:layout_gravity="end|bottom"
+            android:background="#00f"/>
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/begin_delayed.xml b/samples/SupportTransitionDemos/res/layout/begin_delayed.xml
index 8aa1bba..ab7de11 100644
--- a/samples/SupportTransitionDemos/res/layout/begin_delayed.xml
+++ b/samples/SupportTransitionDemos/res/layout/begin_delayed.xml
@@ -30,19 +30,28 @@
             app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
             android:elevation="4dp"/>
 
-    <FrameLayout
-            android:id="@+id/root"
+    <LinearLayout
+        android:id="@+id/root"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:padding="16dp"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/message"
             android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:padding="16dp">
+            android:layout_height="wrap_content"
+            android:text="@string/hello_world"
+            android:textSize="18sp"
+            android:padding="8dp"/>
 
         <Button
-                android:id="@+id/button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/begin"/>
+            android:id="@+id/button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/begin"/>
 
-    </FrameLayout>
+    </LinearLayout>
 
 </LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/change_transform.xml b/samples/SupportTransitionDemos/res/layout/change_transform.xml
new file mode 100644
index 0000000..c9559ab
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/change_transform.xml
@@ -0,0 +1,49 @@
+<?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"
+    android:id="@+id/root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Button
+        android:id="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:text="@string/toggle"/>
+
+    <FrameLayout
+        android:id="@+id/container_1"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginEnd="128dp"
+        android:layout_marginRight="128dp"
+        android:layout_weight="1"
+        android:background="#BBDEFB"/>
+
+    <FrameLayout
+        android:id="@+id/container_2"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginLeft="128dp"
+        android:layout_marginStart="128dp"
+        android:layout_weight="1"
+        android:background="#FFCC80"/>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/clip_bounds.xml b/samples/SupportTransitionDemos/res/layout/clip_bounds.xml
new file mode 100644
index 0000000..2e98f32
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/clip_bounds.xml
@@ -0,0 +1,37 @@
+<?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"
+    android:id="@+id/root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Button
+        android:id="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/toggle"/>
+
+    <ImageView
+        android:id="@+id/photo"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="Photo"
+        android:src="@drawable/photo"/>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/explode.xml b/samples/SupportTransitionDemos/res/layout/explode.xml
new file mode 100644
index 0000000..a612910
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/explode.xml
@@ -0,0 +1,63 @@
+<?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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/view_1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="start|top"
+        android:layout_margin="64dp"
+        android:background="#E91E63"/>
+
+    <View
+        android:id="@+id/view_2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="end|top"
+        android:layout_margin="64dp"
+        android:background="#673AB7"/>
+
+    <View
+        android:id="@+id/view_3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="start|bottom"
+        android:layout_margin="64dp"
+        android:background="#009688"/>
+
+    <View
+        android:id="@+id/view_4"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="end|bottom"
+        android:layout_margin="64dp"
+        android:background="#FF5722"/>
+
+    <Button
+        android:id="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_margin="64dp"
+        android:text="@string/toggle"/>
+
+</FrameLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/image_transform.xml b/samples/SupportTransitionDemos/res/layout/image_transform.xml
new file mode 100644
index 0000000..4dfbe24
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/image_transform.xml
@@ -0,0 +1,81 @@
+<?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"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/controls_1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/fit_xy"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/fit_xy"/>
+        <Button
+            android:id="@+id/center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/center"/>
+        <Button
+            android:id="@+id/center_crop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/center_crop"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/controls_2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/fit_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/fit_start"/>
+        <Button
+            android:id="@+id/fit_end"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/fit_end"/>
+        <Button
+            android:id="@+id/matrix"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/matrix"/>
+    </LinearLayout>
+
+    <ImageView
+        android:id="@+id/photo"
+        android:layout_width="256dp"
+        android:layout_height="256dp"
+        android:background="#eee"
+        android:contentDescription="@string/photo"
+        android:scaleType="center"
+        android:src="@drawable/photo"/>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/red_square.xml b/samples/SupportTransitionDemos/res/layout/red_square.xml
new file mode 100644
index 0000000..550b2c6
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/red_square.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/red_square"
+    android:layout_width="64dp"
+    android:layout_height="64dp"
+    android:layout_gravity="center"
+    android:background="#f00"/>
diff --git a/samples/SupportTransitionDemos/res/layout/reparent_image.xml b/samples/SupportTransitionDemos/res/layout/reparent_image.xml
new file mode 100644
index 0000000..c166429
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/reparent_image.xml
@@ -0,0 +1,53 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Button
+        android:id="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:text="@string/toggle"/>
+
+    <FrameLayout
+        android:id="@+id/outer_frame"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:background="#BBDEFB">
+
+        <FrameLayout
+            android:id="@+id/inner_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginBottom="16dp"
+            android:layout_marginEnd="16dp"
+            android:layout_marginLeft="96dp"
+            android:layout_marginRight="16dp"
+            android:layout_marginStart="200dp"
+            android:layout_marginTop="200dp"
+            android:background="#FFCC80">
+
+        </FrameLayout>
+
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/values-w540dp/dimens.xml b/samples/SupportTransitionDemos/res/values-w540dp/dimens.xml
deleted file mode 100644
index ad7744b..0000000
--- a/samples/SupportTransitionDemos/res/values-w540dp/dimens.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<resources>
-    <dimen name="bottom_sheet_horizontal_margin">64dp</dimen>
-</resources>
diff --git a/samples/SupportTransitionDemos/res/values/dimens.xml b/samples/SupportTransitionDemos/res/values/dimens.xml
index d9f7ab3..0e97329 100644
--- a/samples/SupportTransitionDemos/res/values/dimens.xml
+++ b/samples/SupportTransitionDemos/res/values/dimens.xml
@@ -15,7 +15,5 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="bottom_sheet_horizontal_margin">0dp</dimen>
-    <dimen name="bottom_sheet_elevation">16dp</dimen>
-    <dimen name="bottom_sheet_peek_height">128dp</dimen>
+    <dimen name="photo_size">96dp</dimen>
 </resources>
diff --git a/samples/SupportTransitionDemos/res/values/strings.xml b/samples/SupportTransitionDemos/res/values/strings.xml
index 9e528cd..b87b371 100644
--- a/samples/SupportTransitionDemos/res/values/strings.xml
+++ b/samples/SupportTransitionDemos/res/values/strings.xml
@@ -19,6 +19,21 @@
     <string name="scene">Scene</string>
     <string name="custom">Custom Transition</string>
     <string name="beginDelayed">Begin Delayed Transition</string>
+    <string name="arcMotion">Arc Motion</string>
+    <string name="explode">Explode</string>
+    <string name="clipBounds">Change Clip Bounds</string>
+    <string name="changeTransform">Change Transform</string>
+    <string name="changeImageTransform">Change Image Transform</string>
+    <string name="reparentImage">Reparent Image</string>
     <string name="toggle">Toggle</string>
     <string name="begin">Begin</string>
+    <string name="hello_world">Hello, world!</string>
+    <string name="move">Move</string>
+    <string name="photo">Photo</string>
+    <string name="fit_xy">FIT_XY</string>
+    <string name="center">CENTER</string>
+    <string name="center_crop">CENTER_CROP</string>
+    <string name="fit_start">FIT_START</string>
+    <string name="fit_end">FIT_END</string>
+    <string name="matrix">MATRIX</string>
 </resources>
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ArcMotionUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ArcMotionUsage.java
new file mode 100644
index 0000000..41be84a
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ArcMotionUsage.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.os.Bundle;
+import android.support.transition.ArcMotion;
+import android.support.transition.ChangeBounds;
+import android.support.transition.Transition;
+import android.support.transition.TransitionManager;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.example.android.support.transition.R;
+
+/**
+ * This demonstrates usage of {@link ArcMotion}.
+ */
+public class ArcMotionUsage extends TransitionUsageBase {
+
+    private FrameLayout mRoot;
+    private View mTarget;
+    private Transition mTransition;
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.arc_motion;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mRoot = findViewById(R.id.root);
+        mTarget = findViewById(R.id.target);
+        mTransition = new ChangeBounds();
+        mTransition.setPathMotion(new ArcMotion());
+        mTransition.setInterpolator(new FastOutSlowInInterpolator());
+        mTransition.setDuration(500);
+        findViewById(R.id.move).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTarget.getLayoutParams();
+                if ((lp.gravity & Gravity.START) == Gravity.START) {
+                    lp.gravity = Gravity.END | Gravity.BOTTOM;
+                } else {
+                    lp.gravity = Gravity.START | Gravity.TOP;
+                }
+                mTarget.setLayoutParams(lp);
+            }
+        });
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java
index 713e76d..1e86816 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java
@@ -18,16 +18,16 @@
 
 import android.os.Bundle;
 import android.support.transition.TransitionManager;
-import android.support.v4.view.GravityCompat;
 import android.view.View;
-import android.widget.Button;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
 import com.example.android.support.transition.R;
 
 public class BeginDelayedUsage extends TransitionUsageBase {
 
-    private FrameLayout mRoot;
-    private Button mButton;
+    private LinearLayout mRoot;
+    private TextView mMessage;
 
     @Override
     int getLayoutResId() {
@@ -37,9 +37,9 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mRoot = (FrameLayout) findViewById(R.id.root);
-        mButton = (Button) findViewById(R.id.button);
-        mButton.setOnClickListener(new View.OnClickListener() {
+        mRoot = findViewById(R.id.root);
+        mMessage = findViewById(R.id.message);
+        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 toggle();
@@ -49,13 +49,11 @@
 
     private void toggle() {
         TransitionManager.beginDelayedTransition(mRoot);
-        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mButton.getLayoutParams();
-        if ((params.gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) == GravityCompat.END) {
-            params.gravity = params.gravity ^ GravityCompat.END | GravityCompat.START;
+        if (mMessage.getVisibility() != View.VISIBLE) {
+            mMessage.setVisibility(View.VISIBLE);
         } else {
-            params.gravity = params.gravity ^ GravityCompat.START | GravityCompat.END;
+            mMessage.setVisibility(View.GONE);
         }
-        mButton.setLayoutParams(params);
     }
 
 }
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeClipBoundsUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeClipBoundsUsage.java
new file mode 100644
index 0000000..aa036fa
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeClipBoundsUsage.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.transition.widget;
+
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.transition.ChangeClipBounds;
+import android.support.transition.TransitionManager;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.example.android.support.transition.R;
+
+/**
+ * This demonstrates usage of {@link ChangeClipBounds}.
+ */
+public class ChangeClipBoundsUsage extends TransitionUsageBase {
+
+    private static final Rect BOUNDS = new Rect(20, 20, 100, 100);
+
+    private final ChangeClipBounds mChangeClipBounds = new ChangeClipBounds();
+    private ViewGroup mRoot;
+    private ImageView mPhoto;
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.clip_bounds;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mRoot = findViewById(R.id.root);
+        mPhoto = findViewById(R.id.photo);
+        findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                toggle();
+            }
+        });
+    }
+
+    void toggle() {
+        TransitionManager.beginDelayedTransition(mRoot, mChangeClipBounds);
+        if (BOUNDS.equals(ViewCompat.getClipBounds(mPhoto))) {
+            ViewCompat.setClipBounds(mPhoto, null);
+        } else {
+            ViewCompat.setClipBounds(mPhoto, BOUNDS);
+        }
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeImageTransformUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeImageTransformUsage.java
new file mode 100644
index 0000000..92fdf27
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeImageTransformUsage.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.graphics.Matrix;
+import android.os.Bundle;
+import android.support.transition.ChangeImageTransform;
+import android.support.transition.Transition;
+import android.support.transition.TransitionManager;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.example.android.support.transition.R;
+
+/**
+ * This demonstrates basic usage of the ChangeImageTransform Transition.
+ */
+public class ChangeImageTransformUsage extends TransitionUsageBase {
+
+    private ViewGroup mRoot;
+    private ImageView mPhoto;
+
+    private static final Transition TRANSITION = new ChangeImageTransform();
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.image_transform;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mRoot = findViewById(R.id.container);
+        mPhoto = findViewById(R.id.photo);
+        final View.OnClickListener listener = new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                TransitionManager.beginDelayedTransition(mRoot, TRANSITION);
+                switch (v.getId()) {
+                    case R.id.fit_xy:
+                        mPhoto.setScaleType(ImageView.ScaleType.FIT_XY);
+                        break;
+                    case R.id.center:
+                        mPhoto.setScaleType(ImageView.ScaleType.CENTER);
+                        break;
+                    case R.id.center_crop:
+                        mPhoto.setScaleType(ImageView.ScaleType.CENTER_CROP);
+                        break;
+                    case R.id.fit_start:
+                        mPhoto.setScaleType(ImageView.ScaleType.FIT_START);
+                        break;
+                    case R.id.fit_end:
+                        mPhoto.setScaleType(ImageView.ScaleType.FIT_END);
+                        break;
+                    case R.id.matrix:
+                        mPhoto.setScaleType(ImageView.ScaleType.MATRIX);
+                        final Matrix matrix = new Matrix();
+                        matrix.setRotate(45.f);
+                        matrix.postTranslate(200, 10);
+                        mPhoto.setImageMatrix(matrix);
+                        break;
+                }
+            }
+        };
+        findViewById(R.id.fit_xy).setOnClickListener(listener);
+        findViewById(R.id.center).setOnClickListener(listener);
+        findViewById(R.id.center_crop).setOnClickListener(listener);
+        findViewById(R.id.fit_start).setOnClickListener(listener);
+        findViewById(R.id.fit_end).setOnClickListener(listener);
+        findViewById(R.id.matrix).setOnClickListener(listener);
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java
new file mode 100644
index 0000000..af1bafd
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.os.Bundle;
+import android.support.transition.ArcMotion;
+import android.support.transition.ChangeTransform;
+import android.support.transition.TransitionManager;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import com.example.android.support.transition.R;
+
+/**
+ * This demonstrates basic usage of the ChangeTransform Transition.
+ */
+public class ChangeTransformUsage extends TransitionUsageBase {
+
+    private LinearLayout mRoot;
+    private FrameLayout mContainer1;
+    private FrameLayout mContainer2;
+    private ChangeTransform mChangeTransform;
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.change_transform;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mChangeTransform = new ChangeTransform();
+        mChangeTransform.setInterpolator(new FastOutSlowInInterpolator());
+        mChangeTransform.setPathMotion(new ArcMotion());
+        mRoot = findViewById(R.id.root);
+        mContainer1 = findViewById(R.id.container_1);
+        mContainer2 = findViewById(R.id.container_2);
+        findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                TransitionManager.beginDelayedTransition(mRoot, mChangeTransform);
+                toggle();
+            }
+        });
+        showRedSquare(mContainer1);
+    }
+
+    void toggle() {
+        if (mContainer2.getChildCount() > 0) {
+            mContainer2.removeAllViews();
+            showRedSquare(mContainer1);
+        } else {
+            mContainer1.removeAllViews();
+            showRedSquare(mContainer2);
+            mContainer2.getChildAt(0).setRotation(45);
+        }
+    }
+
+    private void showRedSquare(FrameLayout container) {
+        final View view = LayoutInflater.from(this)
+                .inflate(R.layout.red_square, container, false);
+        container.addView(view);
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ExplodeUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ExplodeUsage.java
new file mode 100644
index 0000000..a8ea703
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ExplodeUsage.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.transition.Explode;
+import android.support.transition.Transition;
+import android.support.transition.TransitionManager;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.example.android.support.transition.R;
+
+import java.util.ArrayList;
+
+/**
+ * This demonstrates usage of {@link Explode} Transition type.
+ */
+public class ExplodeUsage extends TransitionUsageBase {
+
+    private FrameLayout mRoot;
+    private final ArrayList<View> mViews = new ArrayList<>();
+    private final Explode mExplode = new Explode();
+
+    final Rect mRect = new Rect();
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.explode;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mExplode.setEpicenterCallback(new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(@NonNull Transition transition) {
+                return mRect;
+            }
+        });
+        mRoot = findViewById(R.id.root);
+        if (mViews.isEmpty()) {
+            mViews.add(findViewById(R.id.view_1));
+            mViews.add(findViewById(R.id.view_2));
+            mViews.add(findViewById(R.id.view_3));
+            mViews.add(findViewById(R.id.view_4));
+        }
+        findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                v.getGlobalVisibleRect(mRect);
+                TransitionManager.beginDelayedTransition(mRoot, mExplode);
+                int vis = mViews.get(0).getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
+                for (View view : mViews) {
+                    view.setVisibility(vis);
+                }
+            }
+        });
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ReparentImageUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ReparentImageUsage.java
new file mode 100644
index 0000000..fd37360
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ReparentImageUsage.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.os.Bundle;
+import android.support.transition.ChangeImageTransform;
+import android.support.transition.ChangeTransform;
+import android.support.transition.TransitionManager;
+import android.support.transition.TransitionSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.example.android.support.transition.R;
+
+/**
+ * Demonstrates combination usage of ChangeTransform and ChangeImageTransform.
+ */
+public class ReparentImageUsage extends TransitionUsageBase {
+
+    FrameLayout mOuterFrame;
+    FrameLayout mInnerFrame;
+    TransitionSet mTransition;
+    int mPhotoSize;
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.reparent_image;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mOuterFrame = findViewById(R.id.outer_frame);
+        mInnerFrame = findViewById(R.id.inner_frame);
+        mPhotoSize = getResources().getDimensionPixelSize(R.dimen.photo_size);
+
+        mTransition = new TransitionSet();
+        mTransition.addTransition(new ChangeImageTransform());
+        mTransition.addTransition(new ChangeTransform());
+
+        addImageView(mOuterFrame, ImageView.ScaleType.CENTER_CROP, mPhotoSize);
+        findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                TransitionManager.beginDelayedTransition(mOuterFrame, mTransition);
+                if (mInnerFrame.getChildCount() > 0) {
+                    mInnerFrame.removeAllViews();
+                    addImageView(mOuterFrame, ImageView.ScaleType.CENTER_CROP, mPhotoSize);
+                } else {
+                    mOuterFrame.removeViewAt(1);
+                    addImageView(mInnerFrame, ImageView.ScaleType.FIT_XY,
+                            FrameLayout.LayoutParams.MATCH_PARENT);
+                }
+            }
+        });
+    }
+
+    private void addImageView(FrameLayout parent, ImageView.ScaleType scaleType, int size) {
+        final ImageView photo = new ImageView(this);
+        photo.setImageResource(R.drawable.photo);
+        photo.setId(R.id.photo);
+        photo.setScaleType(scaleType);
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size, size);
+        parent.addView(photo, lp);
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java
index f2fd38a..1bc1d81 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java
@@ -39,7 +39,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        FrameLayout root = (FrameLayout) findViewById(R.id.root);
+        FrameLayout root = findViewById(R.id.root);
         mScenes = setUpScenes(root);
         TransitionManager.go(mScenes[0]);
     }
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
index 0a085f2..aad075c 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
@@ -16,18 +16,12 @@
 
 package com.example.android.support.transition.widget;
 
-import android.support.annotation.LayoutRes;
-import com.example.android.support.transition.R;
-
 import android.os.Bundle;
-import android.support.transition.Scene;
-import android.support.transition.TransitionManager;
+import android.support.annotation.LayoutRes;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
+
+import com.example.android.support.transition.R;
 
 /**
  * Base class for usages of the Transition API.
@@ -43,7 +37,7 @@
         setContentView(getLayoutResId());
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
     }
 
diff --git a/samples/SupportVectorDrawableDemos/Android.mk b/samples/SupportVectorDrawableDemos/Android.mk
deleted file mode 100644
index 376f841..0000000
--- a/samples/SupportVectorDrawableDemos/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_MODULE_TAGS := samples tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_MIN_SDK_VERSION := 14
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SupportVectorDrawableDemos
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        android-support-v7-appcompat \
-        android-support-animatedvectordrawable \
-        android-support-vectordrawable \
-        android-support-v4
-
-LOCAL_AAPT_FLAGS += --no-version-vectors
-
-include $(BUILD_PACKAGE)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/SupportVectorDrawableDemos/AndroidManifest.xml b/samples/SupportVectorDrawableDemos/AndroidManifest.xml
index 1de3a5f..b9ccf77 100644
--- a/samples/SupportVectorDrawableDemos/AndroidManifest.xml
+++ b/samples/SupportVectorDrawableDemos/AndroidManifest.xml
@@ -16,9 +16,6 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.support.vectordrawable" >
-
-    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="23"/>
-
     <application android:icon="@drawable/app_sample_code" android:label="SupportVectorDrawableDemos" >
         <activity android:name="com.example.android.support.vectordrawable.app.SupportVectorDrawableDemos">
             <intent-filter>
diff --git a/samples/SupportVectorDrawableDemos/build.gradle b/samples/SupportVectorDrawableDemos/build.gradle
index 8557c6b..79383ca 100644
--- a/samples/SupportVectorDrawableDemos/build.gradle
+++ b/samples/SupportVectorDrawableDemos/build.gradle
@@ -17,9 +17,9 @@
 apply plugin: 'com.android.application'
 
 dependencies {
-    compile project(':support-vector-drawable')
-    compile project(':support-animated-vector-drawable')
-    compile project(':support-appcompat-v7')
+    implementation project(':support-vector-drawable')
+    implementation project(':support-animated-vector-drawable')
+    implementation project(':support-appcompat-v7')
 }
 
 android {
@@ -27,6 +27,7 @@
 
     defaultConfig {
         minSdkVersion 14
+        targetSdkVersion project.ext.currentSdk
         vectorDrawables.useSupportLibrary = true
     }
 
@@ -37,7 +38,15 @@
         main.res.srcDirs = ['res']
     }
 
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
     lintOptions {
+        checkReleaseBuilds false
         abortOnError false
     }
 
diff --git a/samples/SupportVectorDrawableDemos/res/anim/path_motion.xml b/samples/SupportVectorDrawableDemos/res/anim/path_motion.xml
new file mode 100644
index 0000000..d4ae7e3
--- /dev/null
+++ b/samples/SupportVectorDrawableDemos/res/anim/path_motion.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="3000"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="m -35, 0  a 35,35 0 1,0 70,0  a 35,35 0 1,0 -70,0  m 70, 0 a 35,35 0 1,0 -70,0  a 35,35 0 1,0 70,0"/>
+</set>
\ No newline at end of file
diff --git a/samples/SupportVectorDrawableDemos/res/anim/path_motion_object.xml b/samples/SupportVectorDrawableDemos/res/anim/path_motion_object.xml
new file mode 100644
index 0000000..0906cb1
--- /dev/null
+++ b/samples/SupportVectorDrawableDemos/res/anim/path_motion_object.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="3000"
+    android:propertyXName="translateX"
+    android:propertyYName="translateY"
+    android:pathData="M0 0 M 18 18 M 0 0 m -35, 0  a 35,35 0 1,0 70,0  a 35,35 0 1,0 -70,0  m 70, 0 a 35,35 0 1,0 -70,0  a 35,35 0 1,0 70,0"/>
diff --git a/samples/SupportVectorDrawableDemos/res/drawable/animation_vector_drawable_grouping_1_path_motion.xml b/samples/SupportVectorDrawableDemos/res/drawable/animation_vector_drawable_grouping_1_path_motion.xml
new file mode 100644
index 0000000..915dfa7
--- /dev/null
+++ b/samples/SupportVectorDrawableDemos/res/drawable/animation_vector_drawable_grouping_1_path_motion.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ 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.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/vector_drawable_grouping_1" >
+
+    <target
+        android:name="sun"
+        android:animation="@anim/path_motion" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/samples/SupportVectorDrawableDemos/res/drawable/animation_vector_drawable_grouping_1_path_motion_object.xml b/samples/SupportVectorDrawableDemos/res/drawable/animation_vector_drawable_grouping_1_path_motion_object.xml
new file mode 100644
index 0000000..f251a07
--- /dev/null
+++ b/samples/SupportVectorDrawableDemos/res/drawable/animation_vector_drawable_grouping_1_path_motion_object.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/vector_drawable_grouping_1" >
+
+    <target
+        android:name="sun"
+        android:animation="@anim/path_motion_object" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java b/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java
index f228a43..e68d145 100644
--- a/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java
+++ b/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java
@@ -38,16 +38,16 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.avdc_listener);
-        final AppCompatImageView imageView1 = (AppCompatImageView) findViewById(R.id.imageView);
-        final AppCompatImageView imageView2 = (AppCompatImageView) findViewById(R.id.imageView2);
+        final AppCompatImageView imageView1 = findViewById(R.id.imageView);
+        final AppCompatImageView imageView2 = findViewById(R.id.imageView2);
 
-        final TextView textView1 = (TextView) findViewById(R.id.textView);
+        final TextView textView1 = findViewById(R.id.textView);
         textView1.setText("Should show start / end for first AVD");
-        final TextView textView2 = (TextView) findViewById(R.id.textView2);
+        final TextView textView2 = findViewById(R.id.textView2);
         textView2.setText("Not affected by AVD, b/c removed after register");
-        final TextView textView3 = (TextView) findViewById(R.id.textView3);
+        final TextView textView3 = findViewById(R.id.textView3);
         textView3.setText("Should show start / end for second AVD");
-        final TextView textView4 = (TextView) findViewById(R.id.textView4);
+        final TextView textView4 = findViewById(R.id.textView4);
         textView4.setText("Not affected by AVD, b/c unregistered after register");
 
         final Drawable drawable1 = imageView1.getDrawable();
diff --git a/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/SimpleAnimatedVectorDrawable.java b/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/SimpleAnimatedVectorDrawable.java
index feb9d3c..0cc36de 100644
--- a/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/SimpleAnimatedVectorDrawable.java
+++ b/samples/SupportVectorDrawableDemos/src/com/example/android/support/vectordrawable/app/SimpleAnimatedVectorDrawable.java
@@ -38,6 +38,8 @@
 
     private static final String LOGCAT = "VectorDrawable1";
     protected int[] mIcons = {
+            R.drawable.animation_vector_drawable_grouping_1_path_motion,
+            R.drawable.animation_vector_drawable_grouping_1_path_motion_object,
             R.drawable.animation_vector_drawable_grouping_1,
             R.drawable.animation_vector_drawable_grouping_decelerate,
             R.drawable.animation_vector_drawable_grouping_accelerate,
diff --git a/samples/SupportWearDemos/AndroidManifest.xml b/samples/SupportWearDemos/AndroidManifest.xml
new file mode 100644
index 0000000..4e4fb60
--- /dev/null
+++ b/samples/SupportWearDemos/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.support.wear" >
+    <uses-feature android:name="android.hardware.type.watch" />
+
+    <application android:icon="@drawable/app_sample_code" android:label="SupportWearDemos"
+            android:theme="@android:style/Theme.DeviceDefault">
+        <activity android:name="com.example.android.support.wear.app.SimpleWearableRecyclerViewDemo">
+        </activity>
+        <activity android:name="com.example.android.support.wear.app.WearableSwitchDemo">
+        </activity>
+        <activity android:name="com.example.android.support.wear.app.CircularProgressLayoutDemo">
+        </activity>
+        <activity android:name="com.example.android.support.wear.app.MainDemoActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/samples/SupportWearDemos/build.gradle b/samples/SupportWearDemos/build.gradle
new file mode 100644
index 0000000..4a22995
--- /dev/null
+++ b/samples/SupportWearDemos/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+dependencies {
+    implementation project(':support-wear')
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 24
+        targetSdkVersion project.ext.currentSdk
+    }
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.aidl.srcDirs = ['src']
+        main.res.srcDirs = ['res']
+    }
+
+    signingConfigs {
+        debug {
+            // Use a local debug keystore to avoid build server issues.
+            storeFile project.rootProject.init.debugKeystore
+        }
+    }
+
+    lintOptions {
+        checkReleaseBuilds false
+        abortOnError false
+        check 'NewApi'
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
diff --git a/samples/SupportWearDemos/res/drawable/app_sample_code.png b/samples/SupportWearDemos/res/drawable/app_sample_code.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/samples/SupportWearDemos/res/drawable/app_sample_code.png
Binary files differ
diff --git a/samples/SupportWearDemos/res/layout/cpl_demo.xml b/samples/SupportWearDemos/res/layout/cpl_demo.xml
new file mode 100644
index 0000000..cf29663
--- /dev/null
+++ b/samples/SupportWearDemos/res/layout/cpl_demo.xml
@@ -0,0 +1,42 @@
+<?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.
+  -->
+
+<FrameLayout 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="match_parent"
+             android:orientation="vertical">
+
+    <android.support.wear.widget.CircularProgressLayout
+        android:id="@+id/circularProgressLayout_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:padding="10dp"
+        app:backgroundColor="@color/cpl_light_yellow"
+        app:colorSchemeColors="@color/cpl_light_blue"
+        app:strokeWidth="10dp">
+        <TextView
+            android:id="@+id/circularProgressLayout_child"
+            android:layout_width="60dp"
+            android:layout_height="60dp"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:textColor="@color/cpl_black"
+            android:text="@string/cpl_click_me"/>
+    </android.support.wear.widget.CircularProgressLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/SupportWearDemos/res/layout/switch_demo.xml b/samples/SupportWearDemos/res/layout/switch_demo.xml
new file mode 100644
index 0000000..921568c
--- /dev/null
+++ b/samples/SupportWearDemos/res/layout/switch_demo.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+    <Switch
+            style="@style/Widget.Wear.RoundSwitch"
+            android:layout_gravity="center"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"/>
+</merge>
diff --git a/samples/SupportWearDemos/res/layout/wrv_demo.xml b/samples/SupportWearDemos/res/layout/wrv_demo.xml
new file mode 100644
index 0000000..89d34dd
--- /dev/null
+++ b/samples/SupportWearDemos/res/layout/wrv_demo.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.
+  -->
+
+<android.support.wear.widget.WearableRecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:id="@+id/wrv_container">
+
+</android.support.wear.widget.WearableRecyclerView>
\ No newline at end of file
diff --git a/samples/SupportWearDemos/res/values/colors.xml b/samples/SupportWearDemos/res/values/colors.xml
new file mode 100644
index 0000000..95c0119
--- /dev/null
+++ b/samples/SupportWearDemos/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?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="cpl_light_yellow">#fff176</color>
+    <color name="cpl_light_red">#ef5350</color>
+    <color name="cpl_light_green">#66bb6a</color>
+    <color name="cpl_light_blue">#4fc3f7</color>
+    <color name="cpl_black">#000000</color>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportWearDemos/res/values/strings.xml b/samples/SupportWearDemos/res/values/strings.xml
new file mode 100644
index 0000000..7f92211
--- /dev/null
+++ b/samples/SupportWearDemos/res/values/strings.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>
+    <string name="cpl_click_me">Click me!</string>
+    <string name="cpl_clicked">Clicked!</string>
+    <string name="cpl_finished">Finished!</string>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportWearDemos/src/com/example/android/support/wear/app/CircularProgressLayoutDemo.java b/samples/SupportWearDemos/src/com/example/android/support/wear/app/CircularProgressLayoutDemo.java
new file mode 100644
index 0000000..6bd4438
--- /dev/null
+++ b/samples/SupportWearDemos/src/com/example/android/support/wear/app/CircularProgressLayoutDemo.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.wear.app;
+
+import android.app.Activity;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.v4.content.ContextCompat;
+import android.support.wear.widget.CircularProgressLayout;
+import android.view.View;
+import android.widget.TextView;
+
+import com.example.android.support.wear.R;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Main activity for the CircularProgressLayout demo.
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+public class CircularProgressLayoutDemo extends Activity implements
+        CircularProgressLayout.OnTimerFinishedListener, View.OnClickListener {
+
+    private static final long TOTAL_TIME = TimeUnit.SECONDS.toMillis(10);
+
+    CircularProgressLayout mCircularProgressLayout;
+    TextView mChildView;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.cpl_demo);
+        mCircularProgressLayout = findViewById(R.id.circularProgressLayout_layout);
+        mChildView = findViewById(R.id.circularProgressLayout_child);
+
+        mCircularProgressLayout.setOnClickListener(this);
+        mCircularProgressLayout.setOnTimerFinishedListener(this);
+
+        mCircularProgressLayout.setTotalTime(TOTAL_TIME);
+        mCircularProgressLayout.startTimer();
+    }
+
+    @Override
+    public void onTimerFinished(CircularProgressLayout layout) {
+        if (layout == mCircularProgressLayout) {
+            mChildView.setText(getString(R.string.cpl_finished));
+            mCircularProgressLayout.setBackgroundColor(
+                    ContextCompat.getColor(this, R.color.cpl_light_green));
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (view == mCircularProgressLayout && mCircularProgressLayout.isTimerRunning()) {
+            mCircularProgressLayout.stopTimer();
+            mChildView.setText(getString(R.string.cpl_clicked));
+            mCircularProgressLayout.setBackgroundColor(
+                    ContextCompat.getColor(this, R.color.cpl_light_red));
+        }
+    }
+}
diff --git a/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java b/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java
new file mode 100644
index 0000000..313457b
--- /dev/null
+++ b/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.wear.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.widget.WearableRecyclerView;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Main activity for the wear demos.
+ */
+public class MainDemoActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        WearableRecyclerView demoList = new WearableRecyclerView(this);
+        demoList.setPadding(30, 0, 30, 0);
+        demoList.setLayoutManager(
+                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
+        demoList.setAdapter(new DemoAdapter(createContentMap()));
+        demoList.setEdgeItemsCenteringEnabled(true);
+        setContentView(demoList);
+    }
+
+    private Map<String, Intent> createContentMap() {
+        Map<String, Intent> contentMap = new HashMap<>();
+        contentMap.put("Wearable Recycler View", new Intent(
+                this, SimpleWearableRecyclerViewDemo.class));
+        contentMap.put("Wearable Switch", new Intent(
+                this, WearableSwitchDemo.class));
+        contentMap.put("Circular Progress Layout", new Intent(
+                this, CircularProgressLayoutDemo.class));
+
+        return contentMap;
+    }
+
+    private class ViewHolder extends RecyclerView.ViewHolder {
+        Button mView;
+
+        ViewHolder(Button itemView) {
+            super(itemView);
+            mView = itemView;
+        }
+    }
+
+    private class DemoAdapter extends WearableRecyclerView.Adapter<ViewHolder> {
+        private final Object[] mKeys;
+        private final Map<String, Intent> mData;
+
+        DemoAdapter(Map<String, Intent> dataMap) {
+            mKeys = dataMap.keySet().toArray();
+            mData = dataMap;
+        }
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            Button view = new Button(parent.getContext());
+            view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT));
+            view.setPadding(10, 10, 10, 10);
+            view.setGravity(Gravity.CENTER);
+            return new ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder holder, final int position) {
+            holder.mView.setText(mKeys[position].toString());
+            holder.mView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Intent result = new Intent();
+                    result.setClass(MainDemoActivity.this, SimpleWearableRecyclerViewDemo.class);
+                    startActivity(mData.get(mKeys[position]));
+                }
+            });
+        }
+
+
+        @Override
+        public int getItemCount() {
+            return mKeys.length;
+        }
+    }
+}
diff --git a/samples/SupportWearDemos/src/com/example/android/support/wear/app/SimpleWearableRecyclerViewDemo.java b/samples/SupportWearDemos/src/com/example/android/support/wear/app/SimpleWearableRecyclerViewDemo.java
new file mode 100644
index 0000000..d93cb11
--- /dev/null
+++ b/samples/SupportWearDemos/src/com/example/android/support/wear/app/SimpleWearableRecyclerViewDemo.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.wear.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.widget.WearableLinearLayoutManager;
+import android.support.wear.widget.WearableRecyclerView;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.support.wear.R;
+
+/**
+ * Main activity for the WearableRecyclerView demo.
+ */
+public class SimpleWearableRecyclerViewDemo extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.wrv_demo);
+
+        WearableRecyclerView wrv = findViewById(R.id.wrv_container);
+
+        wrv.setLayoutManager(new WearableLinearLayoutManager(this));
+        wrv.setAdapter(new DemoAdapter());
+        wrv.setCircularScrollingGestureEnabled(true);
+        wrv.setEdgeItemsCenteringEnabled(true);
+    }
+
+    private class ViewHolder extends RecyclerView.ViewHolder {
+        TextView mView;
+        ViewHolder(TextView itemView) {
+            super(itemView);
+            mView = itemView;
+        }
+    }
+
+    private class DemoAdapter extends WearableRecyclerView.Adapter<ViewHolder> {
+        private static final int ITEM_COUNT = 100;
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            TextView view = new TextView(parent.getContext());
+            return new ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder holder, int position) {
+            holder.mView.setText("Holder at position " + position);
+            holder.mView.setTag(position);
+        }
+
+
+        @Override
+        public int getItemCount() {
+            return ITEM_COUNT;
+        }
+    }
+}
diff --git a/samples/SupportWearDemos/src/com/example/android/support/wear/app/WearableSwitchDemo.java b/samples/SupportWearDemos/src/com/example/android/support/wear/app/WearableSwitchDemo.java
new file mode 100644
index 0000000..774021b
--- /dev/null
+++ b/samples/SupportWearDemos/src/com/example/android/support/wear/app/WearableSwitchDemo.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package com.example.android.support.wear.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.example.android.support.wear.R;
+
+/** Main activity for the Switch demo. */
+public class WearableSwitchDemo extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.switch_demo);
+    }
+}
diff --git a/settings.gradle b/settings.gradle
index af43ef2..ffc34ee 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -88,6 +88,20 @@
 include ':support-exifinterface'
 project(':support-exifinterface').projectDir = new File(rootDir, 'exifinterface')
 
+include ':support-wear'
+project(':support-wear').projectDir = new File(rootDir, 'wear')
+
+include ':support-tv-provider'
+project(':support-tv-provider').projectDir = new File(rootDir, 'tv-provider')
+
+include ':support-emoji'
+project(':support-emoji').projectDir = new File(rootDir, 'emoji/core')
+
+include ':support-emoji-bundled'
+project(':support-emoji-bundled').projectDir = new File(rootDir, 'emoji/bundled')
+
+include ':support-emoji-appcompat'
+project(':support-emoji-appcompat').projectDir = new File(rootDir, 'emoji/appcompat')
 
 /////////////////////////////
 //
@@ -103,6 +117,9 @@
 include ':support-leanback-demos'
 project(':support-leanback-demos').projectDir = new File(samplesRoot, 'SupportLeanbackDemos')
 
+include ':support-leanback-jank'
+project(':support-leanback-jank').projectDir = new File(samplesRoot, 'SupportLeanbackJank')
+
 include ':support-percent-demos'
 project(':support-percent-demos').projectDir = new File(samplesRoot, 'SupportPercentDemos')
 
@@ -127,6 +144,24 @@
 include ':support-animation-demos'
 project(':support-animation-demos').projectDir = new File(samplesRoot, 'SupportAnimationDemos')
 
+include ':support-wear-demos'
+project(':support-wear-demos').projectDir = new File(samplesRoot, 'SupportWearDemos')
+
+include ':support-app-navigation'
+project(':support-app-navigation').projectDir = new File(samplesRoot, 'SupportAppNavigation')
+
+include ':support-emoji-demos'
+project(':support-emoji-demos').projectDir = new File(samplesRoot, 'SupportEmojiDemos')
+
+/////////////////////////////
+//
+// Testing libraries
+//
+/////////////////////////////
+
+include ':support-testutils'
+project(':support-testutils').projectDir = new File(rootDir, 'testutils')
+
 /////////////////////////////
 //
 // External
@@ -137,3 +172,13 @@
 
 include ':doclava'
 project(':doclava').projectDir = new File(externalRoot, 'doclava')
+
+include ':jdiff'
+project(':jdiff').projectDir = new File(externalRoot, 'jdiff')
+
+include ':noto-emoji-compat'
+project(':noto-emoji-compat').projectDir = new File(externalRoot, 'noto-fonts/emoji-compat')
+
+///// FLATFOOT START
+
+///// FLATFOOT END
\ No newline at end of file
diff --git a/testutils/NO_DOCS b/testutils/NO_DOCS
new file mode 100644
index 0000000..4dad694
--- /dev/null
+++ b/testutils/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/testutils/build.gradle b/testutils/build.gradle
new file mode 100644
index 0000000..f155ded
--- /dev/null
+++ b/testutils/build.gradle
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'java'
+
+dependencies {
+    compile libs.junit
+}
+
+targetCompatibility = '1.7'
+sourceCompatibility = '1.7'
diff --git a/testutils/src/main/java/android/support/testutils/PollingCheck.java b/testutils/src/main/java/android/support/testutils/PollingCheck.java
new file mode 100644
index 0000000..8e85896
--- /dev/null
+++ b/testutils/src/main/java/android/support/testutils/PollingCheck.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package android.support.testutils;
+
+import org.junit.Assert;
+
+/**
+ * Utility used for testing that allows to poll for a certain condition to happen within a timeout.
+ */
+public abstract class PollingCheck {
+    private static final long DEFAULT_TIMEOUT = 3000;
+    private static final long TIME_SLICE = 50;
+    private final long mTimeout;
+
+    /**
+     * The condition that the PollingCheck should use to proceed successfully.
+     */
+    public interface PollingCheckCondition {
+        /**
+         * @return Whether the polling condition has been met.
+         */
+        boolean canProceed();
+    }
+
+    public PollingCheck(long timeout) {
+        mTimeout = timeout;
+    }
+
+    protected abstract boolean check();
+
+    /**
+     * Start running the polling check.
+     */
+    public void run() {
+        if (check()) {
+            return;
+        }
+
+        long timeout = mTimeout;
+        while (timeout > 0) {
+            try {
+                Thread.sleep(TIME_SLICE);
+            } catch (InterruptedException e) {
+                Assert.fail("unexpected InterruptedException");
+            }
+
+            if (check()) {
+                return;
+            }
+
+            timeout -= TIME_SLICE;
+        }
+
+        Assert.fail("unexpected timeout");
+    }
+
+    /**
+     * Instantiate and start polling for a given condition with a default 3000ms timeout.
+     * @param condition The condition to check for success.
+     */
+    public static void waitFor(final PollingCheckCondition condition) {
+        new PollingCheck(DEFAULT_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return condition.canProceed();
+            }
+        }.run();
+    }
+
+    /**
+     * Instantiate and start polling for a given condition.
+     * @param timeout Time out in ms
+     * @param condition The condition to check for success.
+     */
+    public static void waitFor(long timeout, final PollingCheckCondition condition) {
+        new PollingCheck(timeout) {
+            @Override
+            protected boolean check() {
+                return condition.canProceed();
+            }
+        }.run();
+    }
+}
diff --git a/transition/Android.mk b/transition/Android.mk
index aefedd7..cbff183 100644
--- a/transition/Android.mk
+++ b/transition/Android.mk
@@ -28,17 +28,17 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
     $(call all-java-files-under,base) \
-    $(call all-java-files-under,ics) \
-    $(call all-java-files-under,kitkat) \
+    $(call all-java-files-under,api14) \
+    $(call all-java-files-under,api18) \
+    $(call all-java-files-under,api19) \
     $(call all-java-files-under,api21) \
-    $(call all-java-files-under,api23) \
+    $(call all-java-files-under,api22) \
     $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-annotations \
     android-support-v4
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+LOCAL_AAPT_FLAGS := --no-version-transitions --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/transition/AndroidManifest-make.xml b/transition/AndroidManifest-make.xml
deleted file mode 100644
index 672e1b1..0000000
--- a/transition/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.transition">
-    <uses-sdk android:minSdkVersion="14"/>
-    <application />
-</manifest>
diff --git a/transition/api14/android/support/transition/AnimatorUtilsApi14.java b/transition/api14/android/support/transition/AnimatorUtilsApi14.java
new file mode 100644
index 0000000..d9f870b
--- /dev/null
+++ b/transition/api14/android/support/transition/AnimatorUtilsApi14.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+
+import java.util.ArrayList;
+
+@RequiresApi(14)
+class AnimatorUtilsApi14 implements AnimatorUtilsImpl {
+
+    @Override
+    public void addPauseListener(@NonNull Animator animator,
+            @NonNull AnimatorListenerAdapter listener) {
+        // Do nothing
+    }
+
+    @Override
+    public void pause(@NonNull Animator animator) {
+        final ArrayList<Animator.AnimatorListener> listeners = animator.getListeners();
+        if (listeners != null) {
+            for (int i = 0, size = listeners.size(); i < size; i++) {
+                final Animator.AnimatorListener listener = listeners.get(i);
+                if (listener instanceof AnimatorPauseListenerCompat) {
+                    ((AnimatorPauseListenerCompat) listener).onAnimationPause(animator);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void resume(@NonNull Animator animator) {
+        final ArrayList<Animator.AnimatorListener> listeners = animator.getListeners();
+        if (listeners != null) {
+            for (int i = 0, size = listeners.size(); i < size; i++) {
+                final Animator.AnimatorListener listener = listeners.get(i);
+                if (listener instanceof AnimatorPauseListenerCompat) {
+                    ((AnimatorPauseListenerCompat) listener).onAnimationResume(animator);
+                }
+            }
+        }
+    }
+
+    /**
+     * Listeners can implement this interface in addition to the platform AnimatorPauseListener to
+     * make them compatible with API level 18 and below. Animators will not be paused or resumed,
+     * but the callbacks here are invoked.
+     */
+    interface AnimatorPauseListenerCompat {
+
+        void onAnimationPause(Animator animation);
+
+        void onAnimationResume(Animator animation);
+
+    }
+
+}
diff --git a/transition/api14/android/support/transition/GhostViewApi14.java b/transition/api14/android/support/transition/GhostViewApi14.java
new file mode 100644
index 0000000..38acef2
--- /dev/null
+++ b/transition/api14/android/support/transition/GhostViewApi14.java
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+
+/**
+ * Backport of android.view.GhostView introduced in API level 21.
+ * <p>
+ * While the platform version uses ViewOverlay, this ghost view finds the closest FrameLayout in
+ * the hierarchy and adds itself there.
+ * <p>
+ * Since we cannot use RenderNode to delegate drawing, we instead use {@link View#draw(Canvas)} to
+ * draw the target view. We apply the same transformation matrix applied to the target view. For
+ * that, this view is sized as large as the parent FrameLayout (except padding) while the platform
+ * version becomes as large as the target view.
+ */
+@RequiresApi(14)
+@SuppressLint("ViewConstructor")
+class GhostViewApi14 extends View implements GhostViewImpl {
+
+    static class Creator implements GhostViewImpl.Creator {
+
+        @Override
+        public GhostViewImpl addGhost(View view, ViewGroup viewGroup, Matrix matrix) {
+            GhostViewApi14 ghostView = getGhostView(view);
+            if (ghostView == null) {
+                FrameLayout frameLayout = findFrameLayout(viewGroup);
+                if (frameLayout == null) {
+                    return null;
+                }
+                ghostView = new GhostViewApi14(view);
+                frameLayout.addView(ghostView);
+            }
+            ghostView.mReferences++;
+            return ghostView;
+        }
+
+        @Override
+        public void removeGhost(View view) {
+            GhostViewApi14 ghostView = getGhostView(view);
+            if (ghostView != null) {
+                ghostView.mReferences--;
+                if (ghostView.mReferences <= 0) {
+                    ViewParent parent = ghostView.getParent();
+                    if (parent instanceof ViewGroup) {
+                        ViewGroup group = (ViewGroup) parent;
+                        group.endViewTransition(ghostView);
+                        group.removeView(ghostView);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Find the closest FrameLayout in the ascendant hierarchy from the specified {@code
+         * viewGroup}.
+         */
+        private static FrameLayout findFrameLayout(ViewGroup viewGroup) {
+            while (!(viewGroup instanceof FrameLayout)) {
+                ViewParent parent = viewGroup.getParent();
+                if (!(parent instanceof ViewGroup)) {
+                    return null;
+                }
+                viewGroup = (ViewGroup) parent;
+            }
+            return (FrameLayout) viewGroup;
+        }
+
+    }
+
+    /** The target view */
+    final View mView;
+
+    /** The parent of the view that is disappearing at the beginning of the animation */
+    ViewGroup mStartParent;
+
+    /** The view that is disappearing at the beginning of the animation */
+    View mStartView;
+
+    /** The number of references to this ghost view */
+    int mReferences;
+
+    /** The horizontal distance from the ghost view to the target view */
+    private int mDeltaX;
+
+    /** The horizontal distance from the ghost view to the target view */
+    private int mDeltaY;
+
+    /** The current transformation matrix of the target view */
+    Matrix mCurrentMatrix;
+
+    /** The matrix applied to the ghost view canvas */
+    private final Matrix mMatrix = new Matrix();
+
+    private final ViewTreeObserver.OnPreDrawListener mOnPreDrawListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    // The target view was invalidated; get the transformation.
+                    mCurrentMatrix = mView.getMatrix();
+                    // We draw the view.
+                    ViewCompat.postInvalidateOnAnimation(GhostViewApi14.this);
+                    if (mStartParent != null && mStartView != null) {
+                        mStartParent.endViewTransition(mStartView);
+                        ViewCompat.postInvalidateOnAnimation(mStartParent);
+                        mStartParent = null;
+                        mStartView = null;
+                    }
+                    return true;
+                }
+            };
+
+    GhostViewApi14(View view) {
+        super(view.getContext());
+        mView = view;
+        setLayerType(LAYER_TYPE_HARDWARE, null);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        setGhostView(mView, this);
+        // Calculate the deltas
+        final int[] location = new int[2];
+        final int[] viewLocation = new int[2];
+        getLocationOnScreen(location);
+        mView.getLocationOnScreen(viewLocation);
+        viewLocation[0] = (int) (viewLocation[0] - mView.getTranslationX());
+        viewLocation[1] = (int) (viewLocation[1] - mView.getTranslationY());
+        mDeltaX = viewLocation[0] - location[0];
+        mDeltaY = viewLocation[1] - location[1];
+        // Monitor invalidation of the target view.
+        mView.getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
+        // Make the target view invisible because we draw it instead.
+        mView.setVisibility(INVISIBLE);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mView.getViewTreeObserver().removeOnPreDrawListener(mOnPreDrawListener);
+        mView.setVisibility(VISIBLE);
+        setGhostView(mView, null);
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        // Apply the matrix while adjusting the coordinates
+        mMatrix.set(mCurrentMatrix);
+        mMatrix.postTranslate(mDeltaX, mDeltaY);
+        canvas.setMatrix(mMatrix);
+        // Draw the target
+        mView.draw(canvas);
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        mView.setVisibility(visibility == VISIBLE ? INVISIBLE : VISIBLE);
+    }
+
+    @Override
+    public void reserveEndViewTransition(ViewGroup viewGroup, View view) {
+        mStartParent = viewGroup;
+        mStartView = view;
+    }
+
+    private static void setGhostView(@NonNull View view, GhostViewApi14 ghostView) {
+        view.setTag(R.id.ghost_view, ghostView);
+    }
+
+    static GhostViewApi14 getGhostView(@NonNull View view) {
+        return (GhostViewApi14) view.getTag(R.id.ghost_view);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/ImageViewUtilsApi14.java b/transition/api14/android/support/transition/ImageViewUtilsApi14.java
new file mode 100644
index 0000000..2036ac9
--- /dev/null
+++ b/transition/api14/android/support/transition/ImageViewUtilsApi14.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Matrix;
+import android.support.annotation.RequiresApi;
+import android.widget.ImageView;
+
+@RequiresApi(14)
+class ImageViewUtilsApi14 implements ImageViewUtilsImpl {
+
+    @Override
+    public void startAnimateTransform(ImageView view) {
+        final ImageView.ScaleType scaleType = view.getScaleType();
+        view.setTag(R.id.save_scale_type, scaleType);
+        if (scaleType == ImageView.ScaleType.MATRIX) {
+            view.setTag(R.id.save_image_matrix, view.getImageMatrix());
+        } else {
+            view.setScaleType(ImageView.ScaleType.MATRIX);
+        }
+        view.setImageMatrix(MatrixUtils.IDENTITY_MATRIX);
+    }
+
+    @Override
+    public void animateTransform(ImageView view, Matrix matrix) {
+        view.setImageMatrix(matrix);
+    }
+
+    @Override
+    public void reserveEndAnimateTransform(final ImageView view, Animator animator) {
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                final ImageView.ScaleType scaleType = (ImageView.ScaleType)
+                        view.getTag(R.id.save_scale_type);
+                view.setScaleType(scaleType);
+                view.setTag(R.id.save_scale_type, null);
+                if (scaleType == ImageView.ScaleType.MATRIX) {
+                    view.setImageMatrix((Matrix) view.getTag(R.id.save_image_matrix));
+                    view.setTag(R.id.save_image_matrix, null);
+                }
+                animation.removeListener(this);
+            }
+        });
+    }
+
+}
diff --git a/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java b/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java
new file mode 100644
index 0000000..a1038de
--- /dev/null
+++ b/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.ObjectAnimator;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.support.annotation.RequiresApi;
+import android.util.Property;
+
+@RequiresApi(14)
+class ObjectAnimatorUtilsApi14 implements ObjectAnimatorUtilsImpl {
+
+    @Override
+    public <T> ObjectAnimator ofPointF(T target, Property<T, PointF> property, Path path) {
+        return ObjectAnimator.ofFloat(target, new PathProperty<>(property, path), 0f, 1f);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/PathProperty.java b/transition/api14/android/support/transition/PathProperty.java
new file mode 100644
index 0000000..c5e7429
--- /dev/null
+++ b/transition/api14/android/support/transition/PathProperty.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.PointF;
+import android.util.Property;
+
+/**
+ * A special {@link Property} that can animate a pair of properties bi-dimensionally along the
+ * specified path.
+ * <p>
+ * This property should always be used with Animator that sets float fractions between
+ * {@code 0.f} and {@code 1.f}. For example, setting {@code 0.5f} to this property sets the
+ * values right in the middle of the specified path to the underlying properties.
+ * <p>
+ * Unlike many of the platform built-in properties, instances of this class cannot be reused
+ * for later animations.
+ */
+class PathProperty<T> extends Property<T, Float> {
+
+    private final Property<T, PointF> mProperty;
+    private final PathMeasure mPathMeasure;
+    private final float mPathLength;
+    private final float[] mPosition = new float[2];
+    private final PointF mPointF = new PointF();
+    private float mCurrentFraction;
+
+    PathProperty(Property<T, PointF> property, Path path) {
+        super(Float.class, property.getName());
+        mProperty = property;
+        mPathMeasure = new PathMeasure(path, false);
+        mPathLength = mPathMeasure.getLength();
+    }
+
+    @Override
+    public Float get(T object) {
+        return mCurrentFraction;
+    }
+
+    @Override
+    public void set(T target, Float fraction) {
+        mCurrentFraction = fraction;
+        mPathMeasure.getPosTan(mPathLength * fraction, mPosition, null);
+        mPointF.x = mPosition[0];
+        mPointF.y = mPosition[1];
+        mProperty.set(target, mPointF);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java b/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java
new file mode 100644
index 0000000..072178b
--- /dev/null
+++ b/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.PropertyValuesHolder;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.support.annotation.RequiresApi;
+import android.util.Property;
+
+@RequiresApi(14)
+class PropertyValuesHolderUtilsApi14 implements PropertyValuesHolderUtilsImpl {
+
+    @Override
+    public PropertyValuesHolder ofPointF(Property<?, PointF> property, Path path) {
+        return PropertyValuesHolder.ofFloat(new PathProperty<>(property, path), 0f, 1f);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/ViewGroupOverlayApi14.java b/transition/api14/android/support/transition/ViewGroupOverlayApi14.java
new file mode 100644
index 0000000..4c5579d
--- /dev/null
+++ b/transition/api14/android/support/transition/ViewGroupOverlayApi14.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+import android.view.ViewGroup;
+
+@RequiresApi(14)
+class ViewGroupOverlayApi14 extends ViewOverlayApi14 implements ViewGroupOverlayImpl {
+
+    ViewGroupOverlayApi14(Context context, ViewGroup hostView, View requestingView) {
+        super(context, hostView, requestingView);
+    }
+
+    static ViewGroupOverlayApi14 createFrom(ViewGroup viewGroup) {
+        return (ViewGroupOverlayApi14) ViewOverlayApi14.createFrom(viewGroup);
+    }
+
+    @Override
+    public void add(@NonNull View view) {
+        mOverlayViewGroup.add(view);
+    }
+
+    @Override
+    public void remove(@NonNull View view) {
+        mOverlayViewGroup.remove(view);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/ViewGroupUtilsApi14.java b/transition/api14/android/support/transition/ViewGroupUtilsApi14.java
new file mode 100644
index 0000000..e37a1cc
--- /dev/null
+++ b/transition/api14/android/support/transition/ViewGroupUtilsApi14.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.LayoutTransition;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(14)
+class ViewGroupUtilsApi14 implements ViewGroupUtilsImpl {
+
+    private static final String TAG = "ViewGroupUtilsApi14";
+
+    private static final int LAYOUT_TRANSITION_CHANGING = 4;
+
+    private static LayoutTransition sEmptyLayoutTransition;
+
+    private static Field sLayoutSuppressedField;
+    private static boolean sLayoutSuppressedFieldFetched;
+
+    private static Method sCancelMethod;
+    private static boolean sCancelMethodFetched;
+
+    @Override
+    public ViewGroupOverlayImpl getOverlay(@NonNull ViewGroup group) {
+        return ViewGroupOverlayApi14.createFrom(group);
+    }
+
+    @Override
+    public void suppressLayout(@NonNull ViewGroup group, boolean suppress) {
+        // Prepare the dummy LayoutTransition
+        if (sEmptyLayoutTransition == null) {
+            sEmptyLayoutTransition = new LayoutTransition() {
+                @Override
+                public boolean isChangingLayout() {
+                    return true;
+                }
+            };
+            sEmptyLayoutTransition.setAnimator(LayoutTransition.APPEARING, null);
+            sEmptyLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, null);
+            sEmptyLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, null);
+            sEmptyLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
+            sEmptyLayoutTransition.setAnimator(LAYOUT_TRANSITION_CHANGING, null);
+        }
+        if (suppress) {
+            // Save the current LayoutTransition
+            final LayoutTransition layoutTransition = group.getLayoutTransition();
+            if (layoutTransition != null) {
+                if (layoutTransition.isRunning()) {
+                    cancelLayoutTransition(layoutTransition);
+                }
+                if (layoutTransition != sEmptyLayoutTransition) {
+                    group.setTag(R.id.transition_layout_save, layoutTransition);
+                }
+            }
+            // Suppress the layout
+            group.setLayoutTransition(sEmptyLayoutTransition);
+        } else {
+            // Thaw the layout suppression
+            group.setLayoutTransition(null);
+            // Request layout if necessary
+            if (!sLayoutSuppressedFieldFetched) {
+                try {
+                    sLayoutSuppressedField = ViewGroup.class.getDeclaredField("mLayoutSuppressed");
+                    sLayoutSuppressedField.setAccessible(true);
+                } catch (NoSuchFieldException e) {
+                    Log.i(TAG, "Failed to access mLayoutSuppressed field by reflection");
+                }
+                sLayoutSuppressedFieldFetched = true;
+            }
+            boolean layoutSuppressed = false;
+            if (sLayoutSuppressedField != null) {
+                try {
+                    layoutSuppressed = sLayoutSuppressedField.getBoolean(group);
+                    if (layoutSuppressed) {
+                        sLayoutSuppressedField.setBoolean(group, false);
+                    }
+                } catch (IllegalAccessException e) {
+                    Log.i(TAG, "Failed to get mLayoutSuppressed field by reflection");
+                }
+            }
+            if (layoutSuppressed) {
+                group.requestLayout();
+            }
+            // Restore the saved LayoutTransition
+            final LayoutTransition layoutTransition =
+                    (LayoutTransition) group.getTag(R.id.transition_layout_save);
+            if (layoutTransition != null) {
+                group.setTag(R.id.transition_layout_save, null);
+                group.setLayoutTransition(layoutTransition);
+            }
+        }
+    }
+
+    private static void cancelLayoutTransition(LayoutTransition t) {
+        if (!sCancelMethodFetched) {
+            try {
+                sCancelMethod = LayoutTransition.class.getDeclaredMethod("cancel");
+                sCancelMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to access cancel method by reflection");
+            }
+            sCancelMethodFetched = true;
+        }
+        if (sCancelMethod != null) {
+            try {
+                sCancelMethod.invoke(t);
+            } catch (IllegalAccessException e) {
+                Log.i(TAG, "Failed to access cancel method by reflection");
+            } catch (InvocationTargetException e) {
+                Log.i(TAG, "Failed to invoke cancel method by reflection");
+            }
+        }
+    }
+
+}
diff --git a/transition/api14/android/support/transition/ViewOverlayApi14.java b/transition/api14/android/support/transition/ViewOverlayApi14.java
new file mode 100644
index 0000000..f163e45
--- /dev/null
+++ b/transition/api14/android/support/transition/ViewOverlayApi14.java
@@ -0,0 +1,356 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.view.ViewCompat;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+@RequiresApi(14)
+class ViewOverlayApi14 implements ViewOverlayImpl {
+
+    /**
+     * The actual container for the drawables (and views, if it's a ViewGroupOverlay).
+     * All of the management and rendering details for the overlay are handled in
+     * OverlayViewGroup.
+     */
+    protected OverlayViewGroup mOverlayViewGroup;
+
+    ViewOverlayApi14(Context context, ViewGroup hostView, View requestingView) {
+        mOverlayViewGroup = new OverlayViewGroup(context, hostView, requestingView, this);
+    }
+
+    static ViewGroup getContentView(View view) {
+        View parent = view;
+        while (parent != null) {
+            if (parent.getId() == android.R.id.content && parent instanceof ViewGroup) {
+                return (ViewGroup) parent;
+            }
+            if (parent.getParent() instanceof ViewGroup) {
+                parent = (ViewGroup) parent.getParent();
+            }
+        }
+        return null;
+    }
+
+    static ViewOverlayApi14 createFrom(View view) {
+        ViewGroup contentView = getContentView(view);
+        if (contentView != null) {
+            final int numChildren = contentView.getChildCount();
+            for (int i = 0; i < numChildren; ++i) {
+                View child = contentView.getChildAt(i);
+                if (child instanceof OverlayViewGroup) {
+                    return ((OverlayViewGroup) child).mViewOverlay;
+                }
+            }
+            return new ViewGroupOverlayApi14(contentView.getContext(), contentView, view);
+        }
+        return null;
+    }
+
+    /**
+     * Used internally by View and ViewGroup to handle drawing and invalidation
+     * of the overlay
+     */
+    ViewGroup getOverlayView() {
+        return mOverlayViewGroup;
+    }
+
+    @Override
+    public void add(@NonNull Drawable drawable) {
+        mOverlayViewGroup.add(drawable);
+    }
+
+    @Override
+    public void clear() {
+        mOverlayViewGroup.clear();
+    }
+
+    @Override
+    public void remove(@NonNull Drawable drawable) {
+        mOverlayViewGroup.remove(drawable);
+    }
+
+    boolean isEmpty() {
+        return mOverlayViewGroup.isEmpty();
+    }
+
+
+    /**
+     * OverlayViewGroup is a container that View and ViewGroup use to host
+     * drawables and views added to their overlays  ({@code ViewOverlay} and
+     * {@code ViewGroupOverlay}, respectively). Drawables are added to the overlay
+     * via the add/remove methods in ViewOverlay, Views are added/removed via
+     * ViewGroupOverlay. These drawable and view objects are
+     * drawn whenever the view itself is drawn; first the view draws its own
+     * content (and children, if it is a ViewGroup), then it draws its overlay
+     * (if it has one).
+     *
+     * <p>Besides managing and drawing the list of drawables, this class serves
+     * two purposes:
+     * (1) it noops layout calls because children are absolutely positioned and
+     * (2) it forwards all invalidation calls to its host view. The invalidation
+     * redirect is necessary because the overlay is not a child of the host view
+     * and invalidation cannot therefore follow the normal path up through the
+     * parent hierarchy.</p>
+     *
+     * @see View#getOverlay()
+     * @see ViewGroup#getOverlay()
+     */
+    static class OverlayViewGroup extends ViewGroup {
+
+        static Method sInvalidateChildInParentFastMethod;
+
+        static {
+            try {
+                sInvalidateChildInParentFastMethod = ViewGroup.class.getDeclaredMethod(
+                        "invalidateChildInParentFast", int.class, int.class, Rect.class);
+            } catch (NoSuchMethodException e) {
+            }
+
+        }
+
+        /**
+         * The View for which this is an overlay. Invalidations of the overlay are redirected to
+         * this host view.
+         */
+        ViewGroup mHostView;
+        View mRequestingView;
+        /**
+         * The set of drawables to draw when the overlay is rendered.
+         */
+        ArrayList<Drawable> mDrawables = null;
+        /**
+         * Reference to the hosting overlay object
+         */
+        ViewOverlayApi14 mViewOverlay;
+
+        OverlayViewGroup(Context context, ViewGroup hostView, View requestingView,
+                ViewOverlayApi14 viewOverlay) {
+            super(context);
+            mHostView = hostView;
+            mRequestingView = requestingView;
+            setRight(hostView.getWidth());
+            setBottom(hostView.getHeight());
+            hostView.addView(this);
+            mViewOverlay = viewOverlay;
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            // Intercept and noop all touch events - overlays do not allow touch events
+            return false;
+        }
+
+        public void add(Drawable drawable) {
+            if (mDrawables == null) {
+
+                mDrawables = new ArrayList<>();
+            }
+            if (!mDrawables.contains(drawable)) {
+                // Make each drawable unique in the overlay; can't add it more than once
+                mDrawables.add(drawable);
+                invalidate(drawable.getBounds());
+                drawable.setCallback(this);
+            }
+        }
+
+        public void remove(Drawable drawable) {
+            if (mDrawables != null) {
+                mDrawables.remove(drawable);
+                invalidate(drawable.getBounds());
+                drawable.setCallback(null);
+            }
+        }
+
+        @Override
+        protected boolean verifyDrawable(@NonNull Drawable who) {
+            return super.verifyDrawable(who) || (mDrawables != null && mDrawables.contains(who));
+        }
+
+        public void add(View child) {
+            if (child.getParent() instanceof ViewGroup) {
+                ViewGroup parent = (ViewGroup) child.getParent();
+                if (parent != mHostView && parent.getParent() != null
+                        && ViewCompat.isAttachedToWindow(parent)) {
+                    // Moving to different container; figure out how to position child such that
+                    // it is in the same location on the screen
+                    int[] parentLocation = new int[2];
+                    int[] hostViewLocation = new int[2];
+                    parent.getLocationOnScreen(parentLocation);
+                    mHostView.getLocationOnScreen(hostViewLocation);
+                    ViewCompat.offsetLeftAndRight(child, parentLocation[0] - hostViewLocation[0]);
+                    ViewCompat.offsetTopAndBottom(child, parentLocation[1] - hostViewLocation[1]);
+                }
+                parent.removeView(child);
+//                if (parent.getLayoutTransition() != null) {
+//                    // LayoutTransition will cause the child to delay removal - cancel it
+//                    parent.getLayoutTransition().cancel(LayoutTransition.DISAPPEARING);
+//                }
+                // fail-safe if view is still attached for any reason
+                if (child.getParent() != null) {
+                    parent.removeView(child);
+                }
+            }
+            super.addView(child, getChildCount() - 1);
+        }
+
+        public void remove(View view) {
+            super.removeView(view);
+            if (isEmpty()) {
+                mHostView.removeView(this);
+            }
+        }
+
+        public void clear() {
+            removeAllViews();
+            if (mDrawables != null) {
+                mDrawables.clear();
+            }
+        }
+
+        boolean isEmpty() {
+            return getChildCount() == 0
+                    && (mDrawables == null || mDrawables.size() == 0);
+        }
+
+        @Override
+        public void invalidateDrawable(@NonNull Drawable drawable) {
+            invalidate(drawable.getBounds());
+        }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+            int[] contentViewLocation = new int[2];
+            int[] hostViewLocation = new int[2];
+            mHostView.getLocationOnScreen(contentViewLocation);
+            mRequestingView.getLocationOnScreen(hostViewLocation);
+            canvas.translate(hostViewLocation[0] - contentViewLocation[0],
+                    hostViewLocation[1] - contentViewLocation[1]);
+            canvas.clipRect(
+                    new Rect(0, 0, mRequestingView.getWidth(), mRequestingView.getHeight()));
+            super.dispatchDraw(canvas);
+            final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
+            for (int i = 0; i < numDrawables; ++i) {
+                mDrawables.get(i).draw(canvas);
+            }
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+            // Noop: children are positioned absolutely
+        }
+
+        /*
+         The following invalidation overrides exist for the purpose of redirecting invalidation to
+         the host view. The overlay is not parented to the host view (since a View cannot be a
+         parent), so the invalidation cannot proceed through the normal parent hierarchy.
+         There is a built-in assumption that the overlay exactly covers the host view, therefore
+         the invalidation rectangles received do not need to be adjusted when forwarded to
+         the host view.
+         */
+
+        private void getOffset(int[] offset) {
+            int[] contentViewLocation = new int[2];
+            int[] hostViewLocation = new int[2];
+            mHostView.getLocationOnScreen(contentViewLocation);
+            mRequestingView.getLocationOnScreen(hostViewLocation);
+            offset[0] = hostViewLocation[0] - contentViewLocation[0];
+            offset[1] = hostViewLocation[1] - contentViewLocation[1];
+        }
+
+        public void invalidateChildFast(View child, final Rect dirty) {
+            if (mHostView != null) {
+                // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
+                // using DisplayList properties and a dirty rect instead of causing a real
+                // invalidation of the host view
+                int left = child.getLeft();
+                int top = child.getTop();
+                int[] offset = new int[2];
+                getOffset(offset);
+                // TODO: implement transforms
+//                if (!child.getMatrix().isIdentity()) {
+//                    child.transformRect(dirty);
+//                }
+                dirty.offset(left + offset[0], top + offset[1]);
+                mHostView.invalidate(dirty);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        protected ViewParent invalidateChildInParentFast(int left, int top, Rect dirty) {
+            if (mHostView instanceof ViewGroup && sInvalidateChildInParentFastMethod != null) {
+                try {
+                    int[] offset = new int[2];
+                    getOffset(offset);
+                    sInvalidateChildInParentFastMethod.invoke(mHostView, left, top, dirty);
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                } catch (InvocationTargetException e) {
+                    e.printStackTrace();
+                }
+            }
+            return null;
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+            if (mHostView != null) {
+                dirty.offset(location[0], location[1]);
+                if (mHostView instanceof ViewGroup) {
+                    location[0] = 0;
+                    location[1] = 0;
+                    int[] offset = new int[2];
+                    getOffset(offset);
+                    dirty.offset(offset[0], offset[1]);
+                    return super.invalidateChildInParent(location, dirty);
+//                    return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
+                } else {
+                    invalidate(dirty);
+                }
+            }
+            return null;
+        }
+
+        static class TouchInterceptor extends View {
+            TouchInterceptor(Context context) {
+                super(context);
+            }
+        }
+    }
+
+
+}
diff --git a/transition/api14/android/support/transition/ViewUtilsApi14.java b/transition/api14/android/support/transition/ViewUtilsApi14.java
new file mode 100644
index 0000000..f038c52
--- /dev/null
+++ b/transition/api14/android/support/transition/ViewUtilsApi14.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+import android.view.ViewParent;
+
+@RequiresApi(14)
+class ViewUtilsApi14 implements ViewUtilsImpl {
+
+    private float[] mMatrixValues;
+
+    @Override
+    public ViewOverlayImpl getOverlay(@NonNull View view) {
+        return ViewOverlayApi14.createFrom(view);
+    }
+
+    @Override
+    public WindowIdImpl getWindowId(@NonNull View view) {
+        return new WindowIdApi14(view.getWindowToken());
+    }
+
+    @Override
+    public void setTransitionAlpha(@NonNull View view, float alpha) {
+        Float savedAlpha = (Float) view.getTag(R.id.save_non_transition_alpha);
+        if (savedAlpha != null) {
+            view.setAlpha(savedAlpha * alpha);
+        } else {
+            view.setAlpha(alpha);
+        }
+    }
+
+    @Override
+    public float getTransitionAlpha(@NonNull View view) {
+        Float savedAlpha = (Float) view.getTag(R.id.save_non_transition_alpha);
+        if (savedAlpha != null) {
+            return view.getAlpha() / savedAlpha;
+        } else {
+            return view.getAlpha();
+        }
+    }
+
+    @Override
+    public void saveNonTransitionAlpha(@NonNull View view) {
+        if (view.getTag(R.id.save_non_transition_alpha) == null) {
+            view.setTag(R.id.save_non_transition_alpha, view.getAlpha());
+        }
+    }
+
+    @Override
+    public void clearNonTransitionAlpha(@NonNull View view) {
+        // We don't clear the saved value when the view is hidden; that's the situation we are
+        // saving this value for.
+        if (view.getVisibility() == View.VISIBLE) {
+            view.setTag(R.id.save_non_transition_alpha, null);
+        }
+    }
+
+    @Override
+    public void transformMatrixToGlobal(@NonNull View view, @NonNull Matrix matrix) {
+        final ViewParent parent = view.getParent();
+        if (parent instanceof View) {
+            final View vp = (View) parent;
+            transformMatrixToGlobal(vp, matrix);
+            matrix.preTranslate(-vp.getScrollX(), -vp.getScrollY());
+        }
+        matrix.preTranslate(view.getLeft(), view.getTop());
+        final Matrix vm = view.getMatrix();
+        if (!vm.isIdentity()) {
+            matrix.preConcat(vm);
+        }
+    }
+
+    @Override
+    public void transformMatrixToLocal(@NonNull View view, @NonNull Matrix matrix) {
+        final ViewParent parent = view.getParent();
+        if (parent instanceof View) {
+            final View vp = (View) parent;
+            transformMatrixToLocal(vp, matrix);
+            matrix.postTranslate(vp.getScrollX(), vp.getScrollY());
+        }
+        matrix.postTranslate(view.getLeft(), view.getTop());
+        final Matrix vm = view.getMatrix();
+        if (!vm.isIdentity()) {
+            final Matrix inverted = new Matrix();
+            if (vm.invert(inverted)) {
+                matrix.postConcat(inverted);
+            }
+        }
+    }
+
+    @Override
+    public void setAnimationMatrix(@NonNull View view, Matrix matrix) {
+        if (matrix == null || matrix.isIdentity()) {
+            view.setPivotX(view.getWidth() / 2);
+            view.setPivotY(view.getHeight() / 2);
+            view.setTranslationX(0);
+            view.setTranslationY(0);
+            view.setScaleX(1);
+            view.setScaleY(1);
+            view.setRotation(0);
+        } else {
+            float[] values = mMatrixValues;
+            if (values == null) {
+                mMatrixValues = values = new float[9];
+            }
+            matrix.getValues(values);
+            final float sin = values[Matrix.MSKEW_Y];
+            final float cos = (float) Math.sqrt(1 - sin * sin)
+                    * (values[Matrix.MSCALE_X] < 0 ? -1 : 1);
+            final float rotation = (float) Math.toDegrees(Math.atan2(sin, cos));
+            final float scaleX = values[Matrix.MSCALE_X] / cos;
+            final float scaleY = values[Matrix.MSCALE_Y] / cos;
+            final float dx = values[Matrix.MTRANS_X];
+            final float dy = values[Matrix.MTRANS_Y];
+            view.setPivotX(0);
+            view.setPivotY(0);
+            view.setTranslationX(dx);
+            view.setTranslationY(dy);
+            view.setRotation(rotation);
+            view.setScaleX(scaleX);
+            view.setScaleY(scaleY);
+        }
+    }
+
+    @Override
+    public void setLeftTopRightBottom(View v, int left, int top, int right, int bottom) {
+        v.setLeft(left);
+        v.setTop(top);
+        v.setRight(right);
+        v.setBottom(bottom);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/WindowIdApi14.java b/transition/api14/android/support/transition/WindowIdApi14.java
new file mode 100644
index 0000000..f112786
--- /dev/null
+++ b/transition/api14/android/support/transition/WindowIdApi14.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+class WindowIdApi14 implements WindowIdImpl {
+
+    private final IBinder mToken;
+
+    WindowIdApi14(IBinder token) {
+        mToken = token;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof WindowIdApi14 && ((WindowIdApi14) o).mToken.equals(this.mToken);
+    }
+
+    @Override
+    public int hashCode() {
+        return mToken.hashCode();
+    }
+}
diff --git a/transition/api18/android/support/transition/ViewGroupOverlayApi18.java b/transition/api18/android/support/transition/ViewGroupOverlayApi18.java
new file mode 100644
index 0000000..a32d8be
--- /dev/null
+++ b/transition/api18/android/support/transition/ViewGroupOverlayApi18.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroupOverlay;
+
+@RequiresApi(18)
+class ViewGroupOverlayApi18 implements ViewGroupOverlayImpl {
+
+    private final ViewGroupOverlay mViewGroupOverlay;
+
+    ViewGroupOverlayApi18(@NonNull ViewGroup group) {
+        mViewGroupOverlay = group.getOverlay();
+    }
+
+    @Override
+    public void add(@NonNull Drawable drawable) {
+        mViewGroupOverlay.add(drawable);
+    }
+
+    @Override
+    public void clear() {
+        mViewGroupOverlay.clear();
+    }
+
+    @Override
+    public void remove(@NonNull Drawable drawable) {
+        mViewGroupOverlay.remove(drawable);
+    }
+
+    @Override
+    public void add(@NonNull View view) {
+        mViewGroupOverlay.add(view);
+    }
+
+    @Override
+    public void remove(@NonNull View view) {
+        mViewGroupOverlay.remove(view);
+    }
+
+}
diff --git a/transition/api18/android/support/transition/ViewGroupUtilsApi18.java b/transition/api18/android/support/transition/ViewGroupUtilsApi18.java
new file mode 100644
index 0000000..7aad4e1
--- /dev/null
+++ b/transition/api18/android/support/transition/ViewGroupUtilsApi18.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(18)
+class ViewGroupUtilsApi18 extends ViewGroupUtilsApi14 {
+
+    private static final String TAG = "ViewUtilsApi18";
+
+    private static Method sSuppressLayoutMethod;
+    private static boolean sSuppressLayoutMethodFetched;
+
+    @Override
+    public ViewGroupOverlayImpl getOverlay(@NonNull ViewGroup group) {
+        return new ViewGroupOverlayApi18(group);
+    }
+
+    @Override
+    public void suppressLayout(@NonNull ViewGroup group, boolean suppress) {
+        fetchSuppressLayoutMethod();
+        if (sSuppressLayoutMethod != null) {
+            try {
+                sSuppressLayoutMethod.invoke(group, suppress);
+            } catch (IllegalAccessException e) {
+                Log.i(TAG, "Failed to invoke suppressLayout method", e);
+            } catch (InvocationTargetException e) {
+                Log.i(TAG, "Error invoking suppressLayout method", e);
+            }
+        }
+    }
+
+    private void fetchSuppressLayoutMethod() {
+        if (!sSuppressLayoutMethodFetched) {
+            try {
+                sSuppressLayoutMethod = ViewGroup.class.getDeclaredMethod("suppressLayout",
+                        boolean.class);
+                sSuppressLayoutMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve suppressLayout method", e);
+            }
+            sSuppressLayoutMethodFetched = true;
+        }
+    }
+
+}
diff --git a/transition/api18/android/support/transition/ViewOverlayApi18.java b/transition/api18/android/support/transition/ViewOverlayApi18.java
new file mode 100644
index 0000000..c2bc4f0
--- /dev/null
+++ b/transition/api18/android/support/transition/ViewOverlayApi18.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+import android.view.ViewOverlay;
+
+@RequiresApi(18)
+class ViewOverlayApi18 implements ViewOverlayImpl {
+
+    private final ViewOverlay mViewOverlay;
+
+    ViewOverlayApi18(@NonNull View view) {
+        mViewOverlay = view.getOverlay();
+    }
+
+    @Override
+    public void add(@NonNull Drawable drawable) {
+        mViewOverlay.add(drawable);
+    }
+
+    @Override
+    public void clear() {
+        mViewOverlay.clear();
+    }
+
+    @Override
+    public void remove(@NonNull Drawable drawable) {
+        mViewOverlay.remove(drawable);
+    }
+
+}
diff --git a/transition/api18/android/support/transition/ViewUtilsApi18.java b/transition/api18/android/support/transition/ViewUtilsApi18.java
new file mode 100644
index 0000000..9cfa668
--- /dev/null
+++ b/transition/api18/android/support/transition/ViewUtilsApi18.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+
+@RequiresApi(18)
+class ViewUtilsApi18 extends ViewUtilsApi14 {
+
+    @Override
+    public ViewOverlayImpl getOverlay(@NonNull View view) {
+        return new ViewOverlayApi18(view);
+    }
+
+    @Override
+    public WindowIdImpl getWindowId(@NonNull View view) {
+        return new WindowIdApi18(view);
+    }
+
+}
diff --git a/transition/api18/android/support/transition/WindowIdApi18.java b/transition/api18/android/support/transition/WindowIdApi18.java
new file mode 100644
index 0000000..b8808f0
--- /dev/null
+++ b/transition/api18/android/support/transition/WindowIdApi18.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+import android.view.WindowId;
+
+@RequiresApi(18)
+class WindowIdApi18 implements WindowIdImpl {
+
+    private final WindowId mWindowId;
+
+    WindowIdApi18(@NonNull View view) {
+        mWindowId = view.getWindowId();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof WindowIdApi18 && ((WindowIdApi18) o).mWindowId.equals(mWindowId);
+    }
+
+    @Override
+    public int hashCode() {
+        return mWindowId.hashCode();
+    }
+}
diff --git a/transition/api19/android/support/transition/AnimatorUtilsApi19.java b/transition/api19/android/support/transition/AnimatorUtilsApi19.java
new file mode 100644
index 0000000..0f4ae6b
--- /dev/null
+++ b/transition/api19/android/support/transition/AnimatorUtilsApi19.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+class AnimatorUtilsApi19 implements AnimatorUtilsImpl {
+
+    @Override
+    public void addPauseListener(@NonNull Animator animator,
+            @NonNull AnimatorListenerAdapter listener) {
+        animator.addPauseListener(listener);
+    }
+
+    @Override
+    public void pause(@NonNull Animator animator) {
+        animator.pause();
+    }
+
+    @Override
+    public void resume(@NonNull Animator animator) {
+        animator.resume();
+    }
+
+}
diff --git a/transition/api19/android/support/transition/ViewUtilsApi19.java b/transition/api19/android/support/transition/ViewUtilsApi19.java
new file mode 100644
index 0000000..58814b7
--- /dev/null
+++ b/transition/api19/android/support/transition/ViewUtilsApi19.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(19)
+class ViewUtilsApi19 extends ViewUtilsApi18 {
+
+    private static final String TAG = "ViewUtilsApi19";
+
+    private static Method sSetTransitionAlphaMethod;
+    private static boolean sSetTransitionAlphaMethodFetched;
+    private static Method sGetTransitionAlphaMethod;
+    private static boolean sGetTransitionAlphaMethodFetched;
+
+    @Override
+    public void setTransitionAlpha(@NonNull View view, float alpha) {
+        fetchSetTransitionAlphaMethod();
+        if (sSetTransitionAlphaMethod != null) {
+            try {
+                sSetTransitionAlphaMethod.invoke(view, alpha);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        } else {
+            view.setAlpha(alpha);
+        }
+    }
+
+    @Override
+    public float getTransitionAlpha(@NonNull View view) {
+        fetchGetTransitionAlphaMethod();
+        if (sGetTransitionAlphaMethod != null) {
+            try {
+                return (Float) sGetTransitionAlphaMethod.invoke(view);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+        return super.getTransitionAlpha(view);
+    }
+
+    @Override
+    public void saveNonTransitionAlpha(@NonNull View view) {
+        // Do nothing
+    }
+
+    @Override
+    public void clearNonTransitionAlpha(@NonNull View view) {
+        // Do nothing
+    }
+
+    private void fetchSetTransitionAlphaMethod() {
+        if (!sSetTransitionAlphaMethodFetched) {
+            try {
+                sSetTransitionAlphaMethod = View.class.getDeclaredMethod("setTransitionAlpha",
+                        float.class);
+                sSetTransitionAlphaMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve setTransitionAlpha method", e);
+            }
+            sSetTransitionAlphaMethodFetched = true;
+        }
+    }
+
+    private void fetchGetTransitionAlphaMethod() {
+        if (!sGetTransitionAlphaMethodFetched) {
+            try {
+                sGetTransitionAlphaMethod = View.class.getDeclaredMethod("getTransitionAlpha");
+                sGetTransitionAlphaMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve getTransitionAlpha method", e);
+            }
+            sGetTransitionAlphaMethodFetched = true;
+        }
+    }
+
+}
diff --git a/transition/api21/android/support/transition/GhostViewApi21.java b/transition/api21/android/support/transition/GhostViewApi21.java
new file mode 100644
index 0000000..ace0016
--- /dev/null
+++ b/transition/api21/android/support/transition/GhostViewApi21.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(21)
+class GhostViewApi21 implements GhostViewImpl {
+
+    private static final String TAG = "GhostViewApi21";
+
+    private static Class<?> sGhostViewClass;
+    private static boolean sGhostViewClassFetched;
+    private static Method sAddGhostMethod;
+    private static boolean sAddGhostMethodFetched;
+    private static Method sRemoveGhostMethod;
+    private static boolean sRemoveGhostMethodFetched;
+
+    static class Creator implements GhostViewImpl.Creator {
+
+        @Override
+        public GhostViewImpl addGhost(View view, ViewGroup viewGroup, Matrix matrix) {
+            fetchAddGhostMethod();
+            if (sAddGhostMethod != null) {
+                try {
+                    return new GhostViewApi21(
+                            (View) sAddGhostMethod.invoke(null, view, viewGroup, matrix));
+                } catch (IllegalAccessException e) {
+                    // Do nothing
+                } catch (InvocationTargetException e) {
+                    throw new RuntimeException(e.getCause());
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void removeGhost(View view) {
+            fetchRemoveGhostMethod();
+            if (sRemoveGhostMethod != null) {
+                try {
+                    sRemoveGhostMethod.invoke(null, view);
+                } catch (IllegalAccessException e) {
+                    // Do nothing
+                } catch (InvocationTargetException e) {
+                    throw new RuntimeException(e.getCause());
+                }
+            }
+        }
+
+    }
+
+    /** A handle to the platform android.view.GhostView. */
+    private final View mGhostView;
+
+    private GhostViewApi21(@NonNull View ghostView) {
+        mGhostView = ghostView;
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        mGhostView.setVisibility(visibility);
+    }
+
+    @Override
+    public void reserveEndViewTransition(ViewGroup viewGroup, View view) {
+        // No need
+    }
+
+    private static void fetchGhostViewClass() {
+        if (!sGhostViewClassFetched) {
+            try {
+                sGhostViewClass = Class.forName("android.view.GhostView");
+            } catch (ClassNotFoundException e) {
+                Log.i(TAG, "Failed to retrieve GhostView class", e);
+            }
+            sGhostViewClassFetched = true;
+        }
+    }
+
+    private static void fetchAddGhostMethod() {
+        if (!sAddGhostMethodFetched) {
+            try {
+                fetchGhostViewClass();
+                sAddGhostMethod = sGhostViewClass.getDeclaredMethod("addGhost", View.class,
+                        ViewGroup.class, Matrix.class);
+                sAddGhostMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve addGhost method", e);
+            }
+            sAddGhostMethodFetched = true;
+        }
+    }
+
+    private static void fetchRemoveGhostMethod() {
+        if (!sRemoveGhostMethodFetched) {
+            try {
+                fetchGhostViewClass();
+                sRemoveGhostMethod = sGhostViewClass.getDeclaredMethod("removeGhost", View.class);
+                sRemoveGhostMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve removeGhost method", e);
+            }
+            sRemoveGhostMethodFetched = true;
+        }
+    }
+
+}
diff --git a/transition/api21/android/support/transition/ImageViewUtilsApi21.java b/transition/api21/android/support/transition/ImageViewUtilsApi21.java
new file mode 100644
index 0000000..9c2a6c3
--- /dev/null
+++ b/transition/api21/android/support/transition/ImageViewUtilsApi21.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.graphics.Matrix;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.widget.ImageView;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(21)
+class ImageViewUtilsApi21 implements ImageViewUtilsImpl {
+
+    private static final String TAG = "ImageViewUtilsApi21";
+
+    private static Method sAnimateTransformMethod;
+    private static boolean sAnimateTransformMethodFetched;
+
+    @Override
+    public void startAnimateTransform(ImageView view) {
+        // Do nothing
+    }
+
+    @Override
+    public void animateTransform(ImageView view, Matrix matrix) {
+        fetchAnimateTransformMethod();
+        if (sAnimateTransformMethod != null) {
+            try {
+                sAnimateTransformMethod.invoke(view, matrix);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public void reserveEndAnimateTransform(ImageView view, Animator animator) {
+        // Do nothing
+    }
+
+    private void fetchAnimateTransformMethod() {
+        if (!sAnimateTransformMethodFetched) {
+            try {
+                sAnimateTransformMethod = ImageView.class.getDeclaredMethod("animateTransform",
+                        Matrix.class);
+                sAnimateTransformMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve animateTransform method", e);
+            }
+            sAnimateTransformMethodFetched = true;
+        }
+    }
+
+}
diff --git a/transition/api21/android/support/transition/ObjectAnimatorUtilsApi21.java b/transition/api21/android/support/transition/ObjectAnimatorUtilsApi21.java
new file mode 100644
index 0000000..0012500
--- /dev/null
+++ b/transition/api21/android/support/transition/ObjectAnimatorUtilsApi21.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.ObjectAnimator;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.support.annotation.RequiresApi;
+import android.util.Property;
+
+@RequiresApi(21)
+class ObjectAnimatorUtilsApi21 implements ObjectAnimatorUtilsImpl {
+
+    @Override
+    public <T> ObjectAnimator ofPointF(T target, Property<T, PointF> property, Path path) {
+        return ObjectAnimator.ofObject(target, property, null, path);
+    }
+
+}
diff --git a/transition/api21/android/support/transition/PropertyValuesHolderUtilsApi21.java b/transition/api21/android/support/transition/PropertyValuesHolderUtilsApi21.java
new file mode 100644
index 0000000..5f6d117
--- /dev/null
+++ b/transition/api21/android/support/transition/PropertyValuesHolderUtilsApi21.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.PropertyValuesHolder;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.support.annotation.RequiresApi;
+import android.util.Property;
+
+@RequiresApi(21)
+class PropertyValuesHolderUtilsApi21 implements PropertyValuesHolderUtilsImpl {
+
+    @Override
+    public PropertyValuesHolder ofPointF(Property<?, PointF> property, Path path) {
+        return PropertyValuesHolder.ofObject(property, null, path);
+    }
+
+}
diff --git a/transition/api21/android/support/transition/SceneApi21.java b/transition/api21/android/support/transition/SceneApi21.java
deleted file mode 100644
index 1e8f0ba..0000000
--- a/transition/api21/android/support/transition/SceneApi21.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-
-@RequiresApi(21)
-@TargetApi(21)
-class SceneApi21 extends SceneWrapper {
-
-    @Override
-    public void init(ViewGroup sceneRoot) {
-        mScene = new android.transition.Scene(sceneRoot);
-    }
-
-    @Override
-    public void init(ViewGroup sceneRoot, View layout) {
-        mScene = new android.transition.Scene(sceneRoot, layout);
-    }
-
-    @Override
-    public void enter() {
-        mScene.enter();
-    }
-
-}
diff --git a/transition/api21/android/support/transition/SceneStaticsApi21.java b/transition/api21/android/support/transition/SceneStaticsApi21.java
deleted file mode 100644
index 547ca70..0000000
--- a/transition/api21/android/support/transition/SceneStaticsApi21.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(21)
-@TargetApi(21)
-class SceneStaticsApi21 extends SceneStaticsImpl {
-
-    @Override
-    public SceneImpl getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
-        SceneApi21 scene = new SceneApi21();
-        scene.mScene = android.transition.Scene.getSceneForLayout(sceneRoot, layoutId, context);
-        return scene;
-    }
-
-}
diff --git a/transition/api21/android/support/transition/ViewUtilsApi21.java b/transition/api21/android/support/transition/ViewUtilsApi21.java
new file mode 100644
index 0000000..c403235
--- /dev/null
+++ b/transition/api21/android/support/transition/ViewUtilsApi21.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(21)
+class ViewUtilsApi21 extends ViewUtilsApi19 {
+
+    private static final String TAG = "ViewUtilsApi21";
+
+    private static Method sTransformMatrixToGlobalMethod;
+    private static boolean sTransformMatrixToGlobalMethodFetched;
+    private static Method sTransformMatrixToLocalMethod;
+    private static boolean sTransformMatrixToLocalMethodFetched;
+    private static Method sSetAnimationMatrixMethod;
+    private static boolean sSetAnimationMatrixMethodFetched;
+
+    @Override
+    public void transformMatrixToGlobal(@NonNull View view, @NonNull Matrix matrix) {
+        fetchTransformMatrixToGlobalMethod();
+        if (sTransformMatrixToGlobalMethod != null) {
+            try {
+                sTransformMatrixToGlobalMethod.invoke(view, matrix);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public void transformMatrixToLocal(@NonNull View view, @NonNull Matrix matrix) {
+        fetchTransformMatrixToLocalMethod();
+        if (sTransformMatrixToLocalMethod != null) {
+            try {
+                sTransformMatrixToLocalMethod.invoke(view, matrix);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public void setAnimationMatrix(@NonNull View view, Matrix matrix) {
+        fetchSetAnimationMatrix();
+        if (sSetAnimationMatrixMethod != null) {
+            try {
+                sSetAnimationMatrixMethod.invoke(view, matrix);
+            } catch (InvocationTargetException e) {
+                // Do nothing
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+    }
+
+    private void fetchTransformMatrixToGlobalMethod() {
+        if (!sTransformMatrixToGlobalMethodFetched) {
+            try {
+                sTransformMatrixToGlobalMethod = View.class.getDeclaredMethod(
+                        "transformMatrixToGlobal", Matrix.class);
+                sTransformMatrixToGlobalMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve transformMatrixToGlobal method", e);
+            }
+            sTransformMatrixToGlobalMethodFetched = true;
+        }
+    }
+
+    private void fetchTransformMatrixToLocalMethod() {
+        if (!sTransformMatrixToLocalMethodFetched) {
+            try {
+                sTransformMatrixToLocalMethod = View.class.getDeclaredMethod(
+                        "transformMatrixToLocal", Matrix.class);
+                sTransformMatrixToLocalMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve transformMatrixToLocal method", e);
+            }
+            sTransformMatrixToLocalMethodFetched = true;
+        }
+    }
+
+    private void fetchSetAnimationMatrix() {
+        if (!sSetAnimationMatrixMethodFetched) {
+            try {
+                sSetAnimationMatrixMethod = View.class.getDeclaredMethod(
+                        "setAnimationMatrix", Matrix.class);
+                sSetAnimationMatrixMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve setAnimationMatrix method", e);
+            }
+            sSetAnimationMatrixMethodFetched = true;
+        }
+    }
+
+}
diff --git a/transition/api22/android/support/transition/ViewUtilsApi22.java b/transition/api22/android/support/transition/ViewUtilsApi22.java
new file mode 100644
index 0000000..f995422
--- /dev/null
+++ b/transition/api22/android/support/transition/ViewUtilsApi22.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(22)
+class ViewUtilsApi22 extends ViewUtilsApi21 {
+
+    private static final String TAG = "ViewUtilsApi22";
+
+    private static Method sSetLeftTopRightBottomMethod;
+    private static boolean sSetLeftTopRightBottomMethodFetched;
+
+    @Override
+    public void setLeftTopRightBottom(View v, int left, int top, int right, int bottom) {
+        fetchSetLeftTopRightBottomMethod();
+        if (sSetLeftTopRightBottomMethod != null) {
+            try {
+                sSetLeftTopRightBottomMethod.invoke(v, left, top, right, bottom);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+    }
+
+    @SuppressLint("PrivateApi")
+    private void fetchSetLeftTopRightBottomMethod() {
+        if (!sSetLeftTopRightBottomMethodFetched) {
+            try {
+                sSetLeftTopRightBottomMethod = View.class.getDeclaredMethod("setLeftTopRightBottom",
+                        int.class, int.class, int.class, int.class);
+                sSetLeftTopRightBottomMethod.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                Log.i(TAG, "Failed to retrieve setLeftTopRightBottom method", e);
+            }
+            sSetLeftTopRightBottomMethodFetched = true;
+        }
+    }
+
+}
+
diff --git a/transition/api23/android/support/transition/TransitionApi23.java b/transition/api23/android/support/transition/TransitionApi23.java
deleted file mode 100644
index 0df0ec5..0000000
--- a/transition/api23/android/support/transition/TransitionApi23.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(23)
-@TargetApi(23)
-class TransitionApi23 extends TransitionKitKat {
-
-    @Override
-    public TransitionImpl removeTarget(int targetId) {
-        mTransition.removeTarget(targetId);
-        return this;
-    }
-
-}
diff --git a/transition/base/android/support/transition/AnimatorUtilsImpl.java b/transition/base/android/support/transition/AnimatorUtilsImpl.java
new file mode 100644
index 0000000..68f222d
--- /dev/null
+++ b/transition/base/android/support/transition/AnimatorUtilsImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.support.annotation.NonNull;
+
+interface AnimatorUtilsImpl {
+
+    void addPauseListener(@NonNull Animator animator, @NonNull AnimatorListenerAdapter listener);
+
+    void pause(@NonNull Animator animator);
+
+    void resume(@NonNull Animator animator);
+
+}
diff --git a/transition/base/android/support/transition/ChangeBoundsInterface.java b/transition/base/android/support/transition/ChangeBoundsInterface.java
deleted file mode 100644
index 3dc5a23..0000000
--- a/transition/base/android/support/transition/ChangeBoundsInterface.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-/**
- * Interface for platform specific ChangeBounds implementations.
- */
-interface ChangeBoundsInterface {
-
-    void setResizeClip(boolean resizeClip);
-
-}
diff --git a/transition/base/android/support/transition/GhostViewImpl.java b/transition/base/android/support/transition/GhostViewImpl.java
new file mode 100644
index 0000000..037b573
--- /dev/null
+++ b/transition/base/android/support/transition/GhostViewImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+import android.view.ViewGroup;
+
+@RequiresApi(14)
+interface GhostViewImpl {
+
+    interface Creator {
+
+        GhostViewImpl addGhost(View view, ViewGroup viewGroup, Matrix matrix);
+
+        void removeGhost(View view);
+
+    }
+
+    void setVisibility(int visibility);
+
+    /**
+     * Reserves a call to {@link ViewGroup#endViewTransition(View)} at the time when the GhostView
+     * starts drawing its real view.
+     */
+    void reserveEndViewTransition(ViewGroup viewGroup, View view);
+
+}
diff --git a/transition/base/android/support/transition/ImageViewUtilsImpl.java b/transition/base/android/support/transition/ImageViewUtilsImpl.java
new file mode 100644
index 0000000..72a71b6
--- /dev/null
+++ b/transition/base/android/support/transition/ImageViewUtilsImpl.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.graphics.Matrix;
+import android.support.annotation.RequiresApi;
+import android.widget.ImageView;
+
+@RequiresApi(14)
+interface ImageViewUtilsImpl {
+
+    void startAnimateTransform(ImageView view);
+
+    void animateTransform(ImageView view, Matrix matrix);
+
+    void reserveEndAnimateTransform(ImageView view, Animator animator);
+
+}
diff --git a/transition/base/android/support/transition/ObjectAnimatorUtilsImpl.java b/transition/base/android/support/transition/ObjectAnimatorUtilsImpl.java
new file mode 100644
index 0000000..ff66f9b
--- /dev/null
+++ b/transition/base/android/support/transition/ObjectAnimatorUtilsImpl.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.ObjectAnimator;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.util.Property;
+
+interface ObjectAnimatorUtilsImpl {
+
+    <T> ObjectAnimator ofPointF(T target, Property<T, PointF> property, Path path);
+
+}
diff --git a/transition/base/android/support/transition/PropertyValuesHolderUtilsImpl.java b/transition/base/android/support/transition/PropertyValuesHolderUtilsImpl.java
new file mode 100644
index 0000000..8ec31eb
--- /dev/null
+++ b/transition/base/android/support/transition/PropertyValuesHolderUtilsImpl.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.PropertyValuesHolder;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.util.Property;
+
+interface PropertyValuesHolderUtilsImpl {
+
+    PropertyValuesHolder ofPointF(Property<?, PointF> property, Path path);
+
+}
diff --git a/transition/base/android/support/transition/SceneImpl.java b/transition/base/android/support/transition/SceneImpl.java
deleted file mode 100644
index 0ee9461..0000000
--- a/transition/base/android/support/transition/SceneImpl.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Base class for platform specific Scene implementations.
- */
-abstract class SceneImpl {
-
-    public abstract void init(ViewGroup sceneRoot);
-
-    public abstract void init(ViewGroup sceneRoot, View layout);
-
-    public abstract ViewGroup getSceneRoot();
-
-    public abstract void exit();
-
-    public abstract void enter();
-
-    public abstract void setEnterAction(Runnable action);
-
-    public abstract void setExitAction(Runnable action);
-
-}
diff --git a/transition/base/android/support/transition/SceneStaticsImpl.java b/transition/base/android/support/transition/SceneStaticsImpl.java
deleted file mode 100644
index 2d8a138..0000000
--- a/transition/base/android/support/transition/SceneStaticsImpl.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.content.Context;
-import android.view.ViewGroup;
-
-abstract class SceneStaticsImpl {
-
-    public abstract SceneImpl getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context);
-
-}
diff --git a/transition/base/android/support/transition/TransitionImpl.java b/transition/base/android/support/transition/TransitionImpl.java
deleted file mode 100644
index ab482b8..0000000
--- a/transition/base/android/support/transition/TransitionImpl.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.List;
-
-/**
- * Base class for platform specific Transition implementations.
- */
-abstract class TransitionImpl {
-
-    public abstract void init(TransitionInterface external, Object internal);
-
-    public void init(TransitionInterface external) {
-        init(external, null);
-    }
-
-    public abstract TransitionImpl addListener(TransitionInterfaceListener listener);
-
-    public abstract TransitionImpl removeListener(TransitionInterfaceListener listener);
-
-    public abstract TransitionImpl addTarget(View target);
-
-    public abstract TransitionImpl addTarget(int targetId);
-
-    public abstract void captureEndValues(TransitionValues transitionValues);
-
-    public abstract void captureStartValues(TransitionValues transitionValues);
-
-    public abstract Animator createAnimator(ViewGroup sceneRoot,
-            TransitionValues startValues, TransitionValues endValues);
-
-    public abstract TransitionImpl excludeChildren(View target, boolean exclude);
-
-    public abstract TransitionImpl excludeChildren(int targetId, boolean exclude);
-
-    public abstract TransitionImpl excludeChildren(Class type, boolean exclude);
-
-    public abstract TransitionImpl excludeTarget(View target, boolean exclude);
-
-    public abstract TransitionImpl excludeTarget(int targetId, boolean exclude);
-
-    public abstract TransitionImpl excludeTarget(Class type, boolean exclude);
-
-    public abstract long getDuration();
-
-    public abstract TransitionImpl setDuration(long duration);
-
-    public abstract TimeInterpolator getInterpolator();
-
-    public abstract TransitionImpl setInterpolator(TimeInterpolator interpolator);
-
-    public abstract String getName();
-
-    public abstract long getStartDelay();
-
-    public abstract TransitionImpl setStartDelay(long startDelay);
-
-    public abstract List<Integer> getTargetIds();
-
-    public abstract List<View> getTargets();
-
-    public abstract String[] getTransitionProperties();
-
-    public abstract TransitionValues getTransitionValues(View view, boolean start);
-
-    public abstract TransitionImpl removeTarget(View target);
-
-    public abstract TransitionImpl removeTarget(int targetId);
-
-}
diff --git a/transition/base/android/support/transition/TransitionInterface.java b/transition/base/android/support/transition/TransitionInterface.java
deleted file mode 100644
index c498bd0..0000000
--- a/transition/base/android/support/transition/TransitionInterface.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.view.ViewGroup;
-
-/**
- * Used to reference android.support.transition.Transition in a backward compatible manner.
- */
-interface TransitionInterface {
-
-    void captureEndValues(TransitionValues transitionValues);
-
-    void captureStartValues(TransitionValues transitionValues);
-
-    Animator createAnimator(ViewGroup sceneRoot,
-            TransitionValues startValues, TransitionValues endValues);
-
-}
diff --git a/transition/base/android/support/transition/TransitionInterfaceListener.java b/transition/base/android/support/transition/TransitionInterfaceListener.java
deleted file mode 100644
index 9d27759..0000000
--- a/transition/base/android/support/transition/TransitionInterfaceListener.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-interface TransitionInterfaceListener<TransitionT extends TransitionInterface> {
-
-    void onTransitionStart(TransitionT transition);
-
-    void onTransitionEnd(TransitionT transition);
-
-    void onTransitionCancel(TransitionT transition);
-
-    void onTransitionPause(TransitionT transition);
-
-    void onTransitionResume(TransitionT transition);
-
-}
diff --git a/transition/base/android/support/transition/TransitionManagerImpl.java b/transition/base/android/support/transition/TransitionManagerImpl.java
deleted file mode 100644
index 861e875..0000000
--- a/transition/base/android/support/transition/TransitionManagerImpl.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-/**
- * Base class for platform specific TransitionManager implementations.
- */
-abstract class TransitionManagerImpl {
-
-    public abstract void setTransition(SceneImpl scene, TransitionImpl transition);
-
-    public abstract void setTransition(SceneImpl fromScene, SceneImpl toScene,
-            TransitionImpl transition);
-
-    public abstract void transitionTo(SceneImpl scene);
-
-}
diff --git a/transition/base/android/support/transition/TransitionManagerStaticsImpl.java b/transition/base/android/support/transition/TransitionManagerStaticsImpl.java
deleted file mode 100644
index 5171ba9..0000000
--- a/transition/base/android/support/transition/TransitionManagerStaticsImpl.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.view.ViewGroup;
-
-/**
- * Base class for platform specific TransitionManager implementations.
- */
-abstract class TransitionManagerStaticsImpl {
-
-    public abstract void go(SceneImpl scene);
-
-    public abstract void go(SceneImpl scene, TransitionImpl transition);
-
-    public abstract void beginDelayedTransition(final ViewGroup sceneRoot);
-
-    public abstract void beginDelayedTransition(final ViewGroup sceneRoot,
-            TransitionImpl transition);
-
-}
diff --git a/transition/base/android/support/transition/TransitionSetImpl.java b/transition/base/android/support/transition/TransitionSetImpl.java
deleted file mode 100644
index 5c5e8a6..0000000
--- a/transition/base/android/support/transition/TransitionSetImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-interface TransitionSetImpl {
-
-    int getOrdering();
-
-    TransitionSetImpl setOrdering(int ordering);
-
-    TransitionSetImpl addTransition(TransitionImpl transition);
-
-    TransitionSetImpl removeTransition(TransitionImpl transition);
-
-}
diff --git a/transition/base/android/support/transition/TransitionValues.java b/transition/base/android/support/transition/TransitionValues.java
deleted file mode 100644
index 8181ad4..0000000
--- a/transition/base/android/support/transition/TransitionValues.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.view.View;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Data structure which holds cached values for the transition.
- * The view field is the target which all of the values pertain to.
- * The values field is a map which holds information for fields
- * according to names selected by the transitions. These names should
- * be unique to avoid clobbering values stored by other transitions,
- * such as the convention project:transition_name:property_name. For
- * example, the platform might store a property "alpha" in a transition
- * "Fader" as "android:fader:alpha".
- *
- * <p>These values are cached during the
- * {@link android.support.transition.Transition#captureStartValues(TransitionValues)}
- * capture} phases of a scene change, once when the start values are captured
- * and again when the end values are captured. These start/end values are then
- * passed into the transitions via the
- * for {@link android.support.transition.Transition#createAnimator(android.view.ViewGroup,
- * TransitionValues, TransitionValues)} method.</p>
- */
-public class TransitionValues {
-
-    /**
-     * The set of values tracked by transitions for this scene
-     */
-    public final Map<String, Object> values = new HashMap<>();
-
-    /**
-     * The View with these values
-     */
-    public View view;
-
-    @Override
-    public boolean equals(Object other) {
-        if (other instanceof TransitionValues) {
-            if (view == ((TransitionValues) other).view) {
-                if (values.equals(((TransitionValues) other).values)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return 31 * view.hashCode() + values.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
-        returnValue += "    view = " + view + "\n";
-        returnValue += "    values:";
-        for (String s : values.keySet()) {
-            returnValue += "    " + s + ": " + values.get(s) + "\n";
-        }
-        return returnValue;
-    }
-
-}
diff --git a/transition/base/android/support/transition/ViewGroupOverlayImpl.java b/transition/base/android/support/transition/ViewGroupOverlayImpl.java
new file mode 100644
index 0000000..82b1f6b
--- /dev/null
+++ b/transition/base/android/support/transition/ViewGroupOverlayImpl.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+
+@RequiresApi(14)
+interface ViewGroupOverlayImpl extends ViewOverlayImpl {
+
+    /**
+     * Adds a View to the overlay. The bounds of the added view should be
+     * relative to the host view. Any view added to the overlay should be
+     * removed when it is no longer needed or no longer visible.
+     *
+     * <p>Views in the overlay are visual-only; they do not receive input
+     * events and do not participate in focus traversal. Overlay views
+     * are intended to be transient, such as might be needed by a temporary
+     * animation effect.</p>
+     *
+     * <p>If the view has a parent, the view will be removed from that parent
+     * before being added to the overlay. Also, if that parent is attached
+     * in the current view hierarchy, the view will be repositioned
+     * such that it is in the same relative location inside the activity. For
+     * example, if the view's current parent lies 100 pixels to the right
+     * and 200 pixels down from the origin of the overlay's
+     * host view, then the view will be offset by (100, 200).</p>
+     *
+     * @param view The View to be added to the overlay. The added view will be
+     *             drawn when the overlay is drawn.
+     * @see #remove(View)
+     * @see android.view.ViewOverlay#add(android.graphics.drawable.Drawable)
+     */
+    void add(@NonNull View view);
+
+    /**
+     * Removes the specified View from the overlay.
+     *
+     * @param view The View to be removed from the overlay.
+     * @see #add(View)
+     * @see android.view.ViewOverlay#remove(android.graphics.drawable.Drawable)
+     */
+    void remove(@NonNull View view);
+
+}
diff --git a/transition/base/android/support/transition/ViewGroupUtilsImpl.java b/transition/base/android/support/transition/ViewGroupUtilsImpl.java
new file mode 100644
index 0000000..8b8d8a2
--- /dev/null
+++ b/transition/base/android/support/transition/ViewGroupUtilsImpl.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.ViewGroup;
+
+@RequiresApi(14)
+interface ViewGroupUtilsImpl {
+
+    ViewGroupOverlayImpl getOverlay(@NonNull ViewGroup group);
+
+    void suppressLayout(@NonNull ViewGroup group, boolean suppress);
+
+}
diff --git a/transition/base/android/support/transition/ViewOverlayImpl.java b/transition/base/android/support/transition/ViewOverlayImpl.java
new file mode 100644
index 0000000..b699970
--- /dev/null
+++ b/transition/base/android/support/transition/ViewOverlayImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+interface ViewOverlayImpl {
+
+    /**
+     * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
+     * the host view. Any drawable added to the overlay should be removed when it is no longer
+     * needed or no longer visible.
+     *
+     * @param drawable The Drawable to be added to the overlay. This drawable will be
+     *                 drawn when the view redraws its overlay.
+     * @see #remove(Drawable)
+     */
+    void add(@NonNull Drawable drawable);
+
+    /**
+     * Removes all content from the overlay.
+     */
+    void clear();
+
+    /**
+     * Removes the specified Drawable from the overlay.
+     *
+     * @param drawable The Drawable to be removed from the overlay.
+     * @see #add(Drawable)
+     */
+    void remove(@NonNull Drawable drawable);
+
+}
diff --git a/transition/base/android/support/transition/ViewUtilsImpl.java b/transition/base/android/support/transition/ViewUtilsImpl.java
new file mode 100644
index 0000000..5fa4b75
--- /dev/null
+++ b/transition/base/android/support/transition/ViewUtilsImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+
+@RequiresApi(14)
+interface ViewUtilsImpl {
+
+    ViewOverlayImpl getOverlay(@NonNull View view);
+
+    WindowIdImpl getWindowId(@NonNull View view);
+
+    void setTransitionAlpha(@NonNull View view, float alpha);
+
+    float getTransitionAlpha(@NonNull View view);
+
+    void saveNonTransitionAlpha(@NonNull View view);
+
+    void clearNonTransitionAlpha(@NonNull View view);
+
+    void transformMatrixToGlobal(@NonNull View view, @NonNull Matrix matrix);
+
+    void transformMatrixToLocal(@NonNull View view, @NonNull Matrix matrix);
+
+    void setAnimationMatrix(@NonNull View view, Matrix matrix);
+
+    void setLeftTopRightBottom(View v, int left, int top, int right, int bottom);
+
+}
diff --git a/transition/base/android/support/transition/VisibilityImpl.java b/transition/base/android/support/transition/VisibilityImpl.java
deleted file mode 100644
index 88c8c4b..0000000
--- a/transition/base/android/support/transition/VisibilityImpl.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.view.ViewGroup;
-
-/**
- * Interface for platform specific Visibility implementations on top of {@link TransitionImpl}.
- */
-interface VisibilityImpl {
-
-    boolean isVisible(TransitionValues values);
-
-    Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility);
-
-    Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility);
-
-}
diff --git a/transition/base/android/support/transition/VisibilityInterface.java b/transition/base/android/support/transition/VisibilityInterface.java
deleted file mode 100644
index 69fdf4e..0000000
--- a/transition/base/android/support/transition/VisibilityInterface.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.view.ViewGroup;
-
-/**
- * Used to reference android.support.transition.Visibility in a backward compatible manner.
- */
-interface VisibilityInterface extends TransitionInterface {
-
-    boolean isVisible(TransitionValues values);
-
-    Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility);
-
-    Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility);
-
-}
diff --git a/transition/base/android/support/transition/WindowIdImpl.java b/transition/base/android/support/transition/WindowIdImpl.java
new file mode 100644
index 0000000..2b5aa21
--- /dev/null
+++ b/transition/base/android/support/transition/WindowIdImpl.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+interface WindowIdImpl {
+}
diff --git a/transition/build.gradle b/transition/build.gradle
index 2f47f83..ef23aef 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -1,105 +1,49 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 
 archivesBaseName = 'transition'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-v4')
+    api project(':support-annotations')
+    api project(':support-v4')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
         minSdkVersion 14
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
                 'base',
-                'ics',
-                'kitkat',
+                'api14',
+                'api18',
+                'api19',
                 'api21',
-                'api23',
+                'api22',
                 'src'
         ]
         main.res.srcDirs = [
                 'res',
                 'res-public'
         ]
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+    aaptOptions {
+        additionalParameters "--no-version-transitions"
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android Transition Support Library'
+    inceptionYear '2016'
+    description 'Android Transition Support Library'
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Transition Support Library'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
-
diff --git a/transition/ics/android/support/transition/AutoTransitionPort.java b/transition/ics/android/support/transition/AutoTransitionPort.java
deleted file mode 100644
index f3d4583..0000000
--- a/transition/ics/android/support/transition/AutoTransitionPort.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-/**
- * Utility class for creating a default transition that automatically fades,
- * moves, and resizes views during a scene change.
- *
- * <p>An AutoTransition can be described in a resource file by using the
- * tag <code>autoTransition</code>, along with the other standard
- * attributes of {@link android.R.styleable#Transition}.</p>
- */
-@RequiresApi(14)
-@TargetApi(14)
-class AutoTransitionPort extends TransitionSetPort {
-
-    /**
-     * Constructs an AutoTransition object, which is a TransitionSet which
-     * first fades out disappearing targets, then moves and resizes existing
-     * targets, and finally fades in appearing targets.
-     */
-    public AutoTransitionPort() {
-        setOrdering(ORDERING_SEQUENTIAL);
-        addTransition(new FadePort(FadePort.OUT)).
-                addTransition(new ChangeBoundsPort()).
-                addTransition(new FadePort(FadePort.IN));
-    }
-}
diff --git a/transition/ics/android/support/transition/ChangeBoundsIcs.java b/transition/ics/android/support/transition/ChangeBoundsIcs.java
deleted file mode 100644
index 61b7ac1..0000000
--- a/transition/ics/android/support/transition/ChangeBoundsIcs.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(14)
-@TargetApi(14)
-class ChangeBoundsIcs extends TransitionIcs implements ChangeBoundsInterface {
-
-    public ChangeBoundsIcs(TransitionInterface transition) {
-        init(transition, new ChangeBoundsPort());
-    }
-
-    @Override
-    public void setResizeClip(boolean resizeClip) {
-        ((ChangeBoundsPort) mTransition).setResizeClip(resizeClip);
-    }
-
-}
diff --git a/transition/ics/android/support/transition/ChangeBoundsPort.java b/transition/ics/android/support/transition/ChangeBoundsPort.java
deleted file mode 100644
index d9db2c7..0000000
--- a/transition/ics/android/support/transition/ChangeBoundsPort.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.TargetApi;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.Map;
-
-/**
- * This transition captures the layout bounds of target views before and after
- * the scene change and animates those changes during the transition.
- *
- * <p>A ChangeBounds transition can be described in a resource file by using the
- * tag <code>changeBounds</code>, along with the other standard
- * attributes of {@link android.R.styleable#Transition}.</p>
- */
-@RequiresApi(14)
-@TargetApi(14)
-class ChangeBoundsPort extends TransitionPort {
-
-    private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
-
-    private static final String PROPNAME_PARENT = "android:changeBounds:parent";
-
-    private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX";
-
-    private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY";
-
-    private static final String[] sTransitionProperties = {
-            PROPNAME_BOUNDS,
-            PROPNAME_PARENT,
-            PROPNAME_WINDOW_X,
-            PROPNAME_WINDOW_Y
-    };
-
-    private static final String LOG_TAG = "ChangeBounds";
-
-    private static RectEvaluator sRectEvaluator = new RectEvaluator();
-
-    int[] tempLocation = new int[2];
-
-    boolean mResizeClip = false;
-
-    boolean mReparent = false;
-
-    @Override
-    public String[] getTransitionProperties() {
-        return sTransitionProperties;
-    }
-
-    public void setResizeClip(boolean resizeClip) {
-        mResizeClip = resizeClip;
-    }
-
-    /**
-     * Setting this flag tells ChangeBounds to track the before/after parent
-     * of every view using this transition. The flag is not enabled by
-     * default because it requires the parent instances to be the same
-     * in the two scenes or else all parents must use ids to allow
-     * the transition to determine which parents are the same.
-     *
-     * @param reparent true if the transition should track the parent
-     *                 container of target views and animate parent changes.
-     */
-    public void setReparent(boolean reparent) {
-        mReparent = reparent;
-    }
-
-    private void captureValues(TransitionValues values) {
-        View view = values.view;
-        values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
-                view.getRight(), view.getBottom()));
-        values.values.put(PROPNAME_PARENT, values.view.getParent());
-        values.view.getLocationInWindow(tempLocation);
-        values.values.put(PROPNAME_WINDOW_X, tempLocation[0]);
-        values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]);
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        if (startValues == null || endValues == null) {
-            return null;
-        }
-        Map<String, Object> startParentVals = startValues.values;
-        Map<String, Object> endParentVals = endValues.values;
-        ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
-        ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
-        if (startParent == null || endParent == null) {
-            return null;
-        }
-        final View view = endValues.view;
-        boolean parentsEqual = (startParent == endParent) ||
-                (startParent.getId() == endParent.getId());
-        // TODO: Might want reparenting to be separate/subclass transition, or at least
-        // triggered by a property on ChangeBounds. Otherwise, we're forcing the requirement that
-        // all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
-        // of reparenting the views.
-        if (!mReparent || parentsEqual) {
-            Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
-            Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
-            int startLeft = startBounds.left;
-            int endLeft = endBounds.left;
-            int startTop = startBounds.top;
-            int endTop = endBounds.top;
-            int startRight = startBounds.right;
-            int endRight = endBounds.right;
-            int startBottom = startBounds.bottom;
-            int endBottom = endBounds.bottom;
-            int startWidth = startRight - startLeft;
-            int startHeight = startBottom - startTop;
-            int endWidth = endRight - endLeft;
-            int endHeight = endBottom - endTop;
-            int numChanges = 0;
-            if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
-                if (startLeft != endLeft) {
-                    ++numChanges;
-                }
-                if (startTop != endTop) {
-                    ++numChanges;
-                }
-                if (startRight != endRight) {
-                    ++numChanges;
-                }
-                if (startBottom != endBottom) {
-                    ++numChanges;
-                }
-            }
-            if (numChanges > 0) {
-                if (!mResizeClip) {
-                    PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
-                    int pvhIndex = 0;
-                    if (startLeft != endLeft) {
-                        view.setLeft(startLeft);
-                    }
-                    if (startTop != endTop) {
-                        view.setTop(startTop);
-                    }
-                    if (startRight != endRight) {
-                        view.setRight(startRight);
-                    }
-                    if (startBottom != endBottom) {
-                        view.setBottom(startBottom);
-                    }
-                    if (startLeft != endLeft) {
-                        pvh[pvhIndex++] = PropertyValuesHolder.ofInt("left", startLeft, endLeft);
-                    }
-                    if (startTop != endTop) {
-                        pvh[pvhIndex++] = PropertyValuesHolder.ofInt("top", startTop, endTop);
-                    }
-                    if (startRight != endRight) {
-                        pvh[pvhIndex++] = PropertyValuesHolder.ofInt("right",
-                                startRight, endRight);
-                    }
-                    if (startBottom != endBottom) {
-                        pvh[pvhIndex++] = PropertyValuesHolder.ofInt("bottom",
-                                startBottom, endBottom);
-                    }
-                    ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
-                    if (view.getParent() instanceof ViewGroup) {
-                        final ViewGroup parent = (ViewGroup) view.getParent();
-//                        parent.suppressLayout(true);
-                        TransitionListener transitionListener = new TransitionListenerAdapter() {
-                            boolean mCanceled = false;
-
-                            @Override
-                            public void onTransitionCancel(TransitionPort transition) {
-//                                parent.suppressLayout(false);
-                                mCanceled = true;
-                            }
-
-                            @Override
-                            public void onTransitionEnd(TransitionPort transition) {
-                                if (!mCanceled) {
-//                                    parent.suppressLayout(false);
-                                }
-                            }
-
-                            @Override
-                            public void onTransitionPause(TransitionPort transition) {
-//                                parent.suppressLayout(false);
-                            }
-
-                            @Override
-                            public void onTransitionResume(TransitionPort transition) {
-//                                parent.suppressLayout(true);
-                            }
-                        };
-                        addListener(transitionListener);
-                    }
-                    return anim;
-                } else {
-                    if (startWidth != endWidth) {
-                        view.setRight(endLeft +
-                                Math.max(startWidth, endWidth));
-                    }
-                    if (startHeight != endHeight) {
-                        view.setBottom(endTop +
-                                Math.max(startHeight, endHeight));
-                    }
-                    // TODO: don't clobber TX/TY
-                    if (startLeft != endLeft) {
-                        view.setTranslationX(startLeft - endLeft);
-                    }
-                    if (startTop != endTop) {
-                        view.setTranslationY(startTop - endTop);
-                    }
-                    // Animate location with translationX/Y and size with clip bounds
-                    float transXDelta = endLeft - startLeft;
-                    float transYDelta = endTop - startTop;
-                    int widthDelta = endWidth - startWidth;
-                    int heightDelta = endHeight - startHeight;
-                    numChanges = 0;
-                    if (transXDelta != 0) {
-                        numChanges++;
-                    }
-                    if (transYDelta != 0) {
-                        numChanges++;
-                    }
-                    if (widthDelta != 0 || heightDelta != 0) {
-                        numChanges++;
-                    }
-                    PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
-                    int pvhIndex = 0;
-                    if (transXDelta != 0) {
-                        pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationX",
-                                view.getTranslationX(), 0);
-                    }
-                    if (transYDelta != 0) {
-                        pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationY",
-                                view.getTranslationY(), 0);
-                    }
-                    if (widthDelta != 0 || heightDelta != 0) {
-                        Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight);
-                        Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight);
-//                        pvh[pvhIndex++] = PropertyValuesHolder.ofObject("clipBounds",
-//                                sRectEvaluator, tempStartBounds, tempEndBounds);
-                    }
-                    ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
-                    if (view.getParent() instanceof ViewGroup) {
-                        final ViewGroup parent = (ViewGroup) view.getParent();
-//                        parent.suppressLayout(true);
-                        TransitionListener transitionListener = new TransitionListenerAdapter() {
-                            boolean mCanceled = false;
-
-                            @Override
-                            public void onTransitionCancel(TransitionPort transition) {
-//                                parent.suppressLayout(false);
-                                mCanceled = true;
-                            }
-
-                            @Override
-                            public void onTransitionEnd(TransitionPort transition) {
-                                if (!mCanceled) {
-//                                    parent.suppressLayout(false);
-                                }
-                            }
-
-                            @Override
-                            public void onTransitionPause(TransitionPort transition) {
-//                                parent.suppressLayout(false);
-                            }
-
-                            @Override
-                            public void onTransitionResume(TransitionPort transition) {
-//                                parent.suppressLayout(true);
-                            }
-                        };
-                        addListener(transitionListener);
-                    }
-                    anim.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-//                            view.setClipBounds(null);
-                        }
-                    });
-                    return anim;
-                }
-            }
-        } else {
-            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
-            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
-            int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
-            int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
-            // TODO: also handle size changes: check bounds and animate size changes
-            if (startX != endX || startY != endY) {
-                sceneRoot.getLocationInWindow(tempLocation);
-                Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
-                        Bitmap.Config.ARGB_8888);
-                Canvas canvas = new Canvas(bitmap);
-                view.draw(canvas);
-                final BitmapDrawable drawable = new BitmapDrawable(bitmap);
-                view.setVisibility(View.INVISIBLE);
-                ViewOverlay.createFrom(sceneRoot).add(drawable);
-//                sceneRoot.getOverlay().add(drawable);
-                Rect startBounds1 = new Rect(startX - tempLocation[0], startY - tempLocation[1],
-                        startX - tempLocation[0] + view.getWidth(),
-                        startY - tempLocation[1] + view.getHeight());
-                Rect endBounds1 = new Rect(endX - tempLocation[0], endY - tempLocation[1],
-                        endX - tempLocation[0] + view.getWidth(),
-                        endY - tempLocation[1] + view.getHeight());
-                ObjectAnimator anim = ObjectAnimator.ofObject(drawable, "bounds",
-                        sRectEvaluator, startBounds1, endBounds1);
-                anim.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        ViewOverlay.createFrom(sceneRoot).remove(drawable);
-//                        sceneRoot.getOverlay().remove(drawable);
-                        view.setVisibility(View.VISIBLE);
-                    }
-                });
-                return anim;
-            }
-        }
-        return null;
-    }
-}
diff --git a/transition/ics/android/support/transition/FadeIcs.java b/transition/ics/android/support/transition/FadeIcs.java
deleted file mode 100644
index ead8c00..0000000
--- a/transition/ics/android/support/transition/FadeIcs.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(14)
-@TargetApi(14)
-class FadeIcs extends TransitionIcs implements VisibilityImpl {
-
-    public FadeIcs(TransitionInterface transition) {
-        init(transition, new FadePort());
-    }
-
-    public FadeIcs(TransitionInterface transition, int fadingMode) {
-        init(transition, new FadePort(fadingMode));
-    }
-
-    @Override
-    public boolean isVisible(TransitionValues values) {
-        return ((FadePort) mTransition).isVisible(values);
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        return ((FadePort) mTransition).onAppear(sceneRoot, startValues, startVisibility,
-                endValues, endVisibility);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
-            int startVisibility, TransitionValues endValues, int endVisibility) {
-        return ((FadePort) mTransition).onDisappear(sceneRoot, startValues, startVisibility,
-                startValues, startVisibility);
-    }
-
-}
diff --git a/transition/ics/android/support/transition/FadePort.java b/transition/ics/android/support/transition/FadePort.java
deleted file mode 100644
index 79673f5..0000000
--- a/transition/ics/android/support/transition/FadePort.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.support.v4.view.ViewCompat;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * This transition tracks changes to the visibility of target views in the
- * start and end scenes and fades views in or out when they become visible
- * or non-visible. Visibility is determined by both the
- * {@link View#setVisibility(int)} state of the view as well as whether it
- * is parented in the current view hierarchy.
- *
- * <p>The ability of this transition to fade out a particular view, and the
- * way that that fading operation takes place, is based on
- * the situation of the view in the view hierarchy. For example, if a view was
- * simply removed from its parent, then the view will be added into a {@link
- * android.view.ViewGroupOverlay} while fading. If a visible view is
- * changed to be {@link View#GONE} or {@link View#INVISIBLE}, then the
- * visibility will be changed to {@link View#VISIBLE} for the duration of
- * the animation. However, if a view is in a hierarchy which is also altering
- * its visibility, the situation can be more complicated. In general, if a
- * view that is no longer in the hierarchy in the end scene still has a
- * parent (so its parent hierarchy was removed, but it was not removed from
- * its parent), then it will be left alone to avoid side-effects from
- * improperly removing it from its parent. The only exception to this is if
- * the previous {@link android.transition.Scene} was
- * {@link ScenePort#getSceneForLayout(ViewGroup, int, android.content.Context)
- * created from a layout resource file}, then it is considered safe to un-parent
- * the starting scene view in order to fade it out.</p>
- *
- * <p>A Fade transition can be described in a resource file by using the
- * tag <code>fade</code>, along with the standard
- * attributes of {@link android.R.styleable#Fade} and
- * {@link android.R.styleable#Transition}.</p>
- */
-@RequiresApi(14)
-@TargetApi(14)
-class FadePort extends VisibilityPort {
-
-    /**
-     * Fading mode used in {@link #FadePort(int)} to make the transition
-     * operate on targets that are appearing. Maybe be combined with
-     * {@link #OUT} to fade both in and out.
-     */
-    public static final int IN = 0x1;
-
-    /**
-     * Fading mode used in {@link #FadePort(int)} to make the transition
-     * operate on targets that are disappearing. Maybe be combined with
-     * {@link #IN} to fade both in and out.
-     */
-    public static final int OUT = 0x2;
-
-    private static final String LOG_TAG = "Fade";
-
-    private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
-
-    private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
-
-    private static boolean DBG = TransitionPort.DBG && false;
-
-    private int mFadingMode;
-
-    /**
-     * Constructs a Fade transition that will fade targets in and out.
-     */
-    public FadePort() {
-        this(IN | OUT);
-    }
-
-    /**
-     * Constructs a Fade transition that will fade targets in
-     * and/or out, according to the value of fadingMode.
-     *
-     * @param fadingMode The behavior of this transition, a combination of
-     *                   {@link #IN} and {@link #OUT}.
-     */
-    public FadePort(int fadingMode) {
-        mFadingMode = fadingMode;
-    }
-
-    /**
-     * Utility method to handle creating and running the Animator.
-     */
-    private Animator createAnimation(View view, float startAlpha, float endAlpha,
-            AnimatorListenerAdapter listener) {
-        if (startAlpha == endAlpha) {
-            // run listener if we're noop'ing the animation, to get the end-state results now
-            if (listener != null) {
-                listener.onAnimationEnd(null);
-            }
-            return null;
-        }
-        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha,
-                endAlpha);
-        if (DBG) {
-            Log.d(LOG_TAG, "Created animator " + anim);
-        }
-        if (listener != null) {
-            anim.addListener(listener);
-        }
-        return anim;
-    }
-
-    private void captureValues(TransitionValues transitionValues) {
-        int[] loc = new int[2];
-        transitionValues.view.getLocationOnScreen(loc);
-        transitionValues.values.put(PROPNAME_SCREEN_X, loc[0]);
-        transitionValues.values.put(PROPNAME_SCREEN_Y, loc[1]);
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        super.captureStartValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot,
-            TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        if ((mFadingMode & IN) != IN || endValues == null) {
-            return null;
-        }
-        final View endView = endValues.view;
-        if (DBG) {
-            View startView = (startValues != null) ? startValues.view : null;
-            Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
-                    startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
-        }
-        endView.setAlpha(0);
-        TransitionListener transitionListener = new TransitionListenerAdapter() {
-            boolean mCanceled = false;
-
-            float mPausedAlpha;
-
-            @Override
-            public void onTransitionCancel(TransitionPort transition) {
-                endView.setAlpha(1);
-                mCanceled = true;
-            }
-
-            @Override
-            public void onTransitionEnd(TransitionPort transition) {
-                if (!mCanceled) {
-                    endView.setAlpha(1);
-                }
-            }
-
-            @Override
-            public void onTransitionPause(TransitionPort transition) {
-                mPausedAlpha = endView.getAlpha();
-                endView.setAlpha(1);
-            }
-
-            @Override
-            public void onTransitionResume(TransitionPort transition) {
-                endView.setAlpha(mPausedAlpha);
-            }
-        };
-        addListener(transitionListener);
-        return createAnimation(endView, 0, 1, null);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot,
-            TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        if ((mFadingMode & OUT) != OUT) {
-            return null;
-        }
-        View view = null;
-        View startView = (startValues != null) ? startValues.view : null;
-        View endView = (endValues != null) ? endValues.view : null;
-        if (DBG) {
-            Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " +
-                    startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
-        }
-        View overlayView = null;
-        View viewToKeep = null;
-        if (endView == null || endView.getParent() == null) {
-            if (endView != null) {
-                // endView was removed from its parent - add it to the overlay
-                view = overlayView = endView;
-            } else if (startView != null) {
-                // endView does not exist. Use startView only under certain
-                // conditions, because placing a view in an overlay necessitates
-                // it being removed from its current parent
-                if (startView.getParent() == null) {
-                    // no parent - safe to use
-                    view = overlayView = startView;
-                } else if (startView.getParent() instanceof View &&
-                        startView.getParent().getParent() == null) {
-                    View startParent = (View) startView.getParent();
-                    int id = startParent.getId();
-                    if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) {
-                        // no parent, but its parent is unparented  but the parent
-                        // hierarchy has been replaced by a new hierarchy with the same id
-                        // and it is safe to un-parent startView
-                        view = overlayView = startView;
-                    }
-                }
-            }
-        } else {
-            // visibility change
-            if (endVisibility == View.INVISIBLE) {
-                view = endView;
-                viewToKeep = view;
-            } else {
-                // Becoming GONE
-                if (startView == endView) {
-                    view = endView;
-                    viewToKeep = view;
-                } else {
-                    view = startView;
-                    overlayView = view;
-                }
-            }
-        }
-        final int finalVisibility = endVisibility;
-        // TODO: add automatic facility to Visibility superclass for keeping views around
-        if (overlayView != null) {
-            // TODO: Need to do this for general case of adding to overlay
-            int screenX = (Integer) startValues.values.get(PROPNAME_SCREEN_X);
-            int screenY = (Integer) startValues.values.get(PROPNAME_SCREEN_Y);
-            int[] loc = new int[2];
-            sceneRoot.getLocationOnScreen(loc);
-            ViewCompat.offsetLeftAndRight(overlayView, (screenX - loc[0]) - overlayView.getLeft());
-            ViewCompat.offsetTopAndBottom(overlayView, (screenY - loc[1]) - overlayView.getTop());
-            ViewGroupOverlay.createFrom(sceneRoot).add(overlayView);
-//            sceneRoot.getOverlay().add(overlayView);
-            // TODO: add automatic facility to Visibility superclass for keeping views around
-            final float startAlpha = 1;
-            float endAlpha = 0;
-            final View finalView = view;
-            final View finalOverlayView = overlayView;
-            final View finalViewToKeep = viewToKeep;
-            final ViewGroup finalSceneRoot = sceneRoot;
-            final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    finalView.setAlpha(startAlpha);
-                    // TODO: restore view offset from overlay repositioning
-                    if (finalViewToKeep != null) {
-                        finalViewToKeep.setVisibility(finalVisibility);
-                    }
-                    if (finalOverlayView != null) {
-                        ViewGroupOverlay.createFrom(finalSceneRoot).remove(finalOverlayView);
-//                        finalSceneRoot.getOverlay().remove(finalOverlayView);
-                    }
-                }
-//
-//                @Override
-//                public void onAnimationPause(Animator animation) {
-//                    if (finalOverlayView != null) {
-//                        finalSceneRoot.getOverlay().remove(finalOverlayView);
-//                    }
-//                }
-//
-//                @Override
-//                public void onAnimationResume(Animator animation) {
-//                    if (finalOverlayView != null) {
-//                        finalSceneRoot.getOverlay().add(finalOverlayView);
-//                    }
-//                }
-            };
-            return createAnimation(view, startAlpha, endAlpha, endListener);
-        }
-        if (viewToKeep != null) {
-            // TODO: find a different way to do this, like just changing the view to be
-            // VISIBLE for the duration of the transition
-            viewToKeep.setVisibility((View.VISIBLE));
-            // TODO: add automatic facility to Visibility superclass for keeping views around
-            final float startAlpha = 1;
-            float endAlpha = 0;
-            final View finalView = view;
-            final View finalOverlayView = overlayView;
-            final View finalViewToKeep = viewToKeep;
-            final ViewGroup finalSceneRoot = sceneRoot;
-            final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
-                boolean mCanceled = false;
-
-                float mPausedAlpha = -1;
-
-//                @Override
-//                public void onAnimationPause(Animator animation) {
-//                    if (finalViewToKeep != null && !mCanceled) {
-//                        finalViewToKeep.setVisibility(finalVisibility);
-//                    }
-//                    mPausedAlpha = finalView.getAlpha();
-//                    finalView.setAlpha(startAlpha);
-//                }
-//
-//                @Override
-//                public void onAnimationResume(Animator animation) {
-//                    if (finalViewToKeep != null && !mCanceled) {
-//                        finalViewToKeep.setVisibility(View.VISIBLE);
-//                    }
-//                    finalView.setAlpha(mPausedAlpha);
-//                }
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    mCanceled = true;
-                    if (mPausedAlpha >= 0) {
-                        finalView.setAlpha(mPausedAlpha);
-                    }
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (!mCanceled) {
-                        finalView.setAlpha(startAlpha);
-                    }
-                    // TODO: restore view offset from overlay repositioning
-                    if (finalViewToKeep != null && !mCanceled) {
-                        finalViewToKeep.setVisibility(finalVisibility);
-                    }
-                    if (finalOverlayView != null) {
-                        ViewGroupOverlay.createFrom(finalSceneRoot).add(finalOverlayView);
-//                        finalSceneRoot.getOverlay().remove(finalOverlayView);
-                    }
-                }
-            };
-            return createAnimation(view, startAlpha, endAlpha, endListener);
-        }
-        return null;
-    }
-
-}
\ No newline at end of file
diff --git a/transition/ics/android/support/transition/RectEvaluator.java b/transition/ics/android/support/transition/RectEvaluator.java
deleted file mode 100644
index c85ed18..0000000
--- a/transition/ics/android/support/transition/RectEvaluator.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.TypeEvaluator;
-import android.annotation.TargetApi;
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-
-/**
- * This evaluator can be used to perform type interpolation between <code>Rect</code> values.
- */
-@RequiresApi(14)
-@TargetApi(14)
-class RectEvaluator implements TypeEvaluator<Rect> {
-
-    /**
-     * When null, a new Rect is returned on every evaluate call. When non-null,
-     * mRect will be modified and returned on every evaluate.
-     */
-    private Rect mRect;
-
-    /**
-     * Construct a RectEvaluator that returns a new Rect on every evaluate call.
-     * To avoid creating an object for each evaluate call,
-     * {@link RectEvaluator#RectEvaluator(android.graphics.Rect)} should be used
-     * whenever possible.
-     */
-    public RectEvaluator() {
-    }
-
-    /**
-     * Constructs a RectEvaluator that modifies and returns <code>reuseRect</code>
-     * in {@link #evaluate(float, android.graphics.Rect, android.graphics.Rect)} calls.
-     * The value returned from
-     * {@link #evaluate(float, android.graphics.Rect, android.graphics.Rect)} should
-     * not be cached because it will change over time as the object is reused on each
-     * call.
-     *
-     * @param reuseRect A Rect to be modified and returned by evaluate.
-     */
-    public RectEvaluator(Rect reuseRect) {
-        mRect = reuseRect;
-    }
-
-    /**
-     * This function returns the result of linearly interpolating the start and
-     * end Rect values, with <code>fraction</code> representing the proportion
-     * between the start and end values. The calculation is a simple parametric
-     * calculation on each of the separate components in the Rect objects
-     * (left, top, right, and bottom).
-     *
-     * <p>If {@link #RectEvaluator(android.graphics.Rect)} was used to construct
-     * this RectEvaluator, the object returned will be the <code>reuseRect</code>
-     * passed into the constructor.</p>
-     *
-     * @param fraction   The fraction from the starting to the ending values
-     * @param startValue The start Rect
-     * @param endValue   The end Rect
-     * @return A linear interpolation between the start and end values, given the
-     * <code>fraction</code> parameter.
-     */
-    @Override
-    public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
-        int left = startValue.left + (int) ((endValue.left - startValue.left) * fraction);
-        int top = startValue.top + (int) ((endValue.top - startValue.top) * fraction);
-        int right = startValue.right + (int) ((endValue.right - startValue.right) * fraction);
-        int bottom = startValue.bottom + (int) ((endValue.bottom - startValue.bottom) * fraction);
-        if (mRect == null) {
-            return new Rect(left, top, right, bottom);
-        } else {
-            mRect.set(left, top, right, bottom);
-            return mRect;
-        }
-    }
-}
diff --git a/transition/ics/android/support/transition/SceneIcs.java b/transition/ics/android/support/transition/SceneIcs.java
deleted file mode 100644
index 01e0508..0000000
--- a/transition/ics/android/support/transition/SceneIcs.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-
-@RequiresApi(14)
-@TargetApi(14)
-class SceneIcs extends SceneImpl {
-
-    /* package */ ScenePort mScene;
-
-    @Override
-    public void init(ViewGroup sceneRoot) {
-        mScene = new ScenePort(sceneRoot);
-    }
-
-    @Override
-    public void init(ViewGroup sceneRoot, View layout) {
-        mScene = new ScenePort(sceneRoot, layout);
-    }
-
-    @Override
-    public void enter() {
-        mScene.enter();
-    }
-
-    @Override
-    public void exit() {
-        mScene.exit();
-    }
-
-
-    @Override
-    public ViewGroup getSceneRoot() {
-        return mScene.getSceneRoot();
-    }
-
-    @Override
-    public void setEnterAction(Runnable action) {
-        mScene.setEnterAction(action);
-    }
-
-    @Override
-    public void setExitAction(Runnable action) {
-        mScene.setExitAction(action);
-    }
-
-}
diff --git a/transition/ics/android/support/transition/ScenePort.java b/transition/ics/android/support/transition/ScenePort.java
deleted file mode 100644
index 34b2d7b..0000000
--- a/transition/ics/android/support/transition/ScenePort.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A scene represents the collection of values that various properties in the
- * View hierarchy will have when the scene is applied. A Scene can be
- * configured to automatically run a Transition when it is applied, which will
- * animate the various property changes that take place during the
- * scene change.
- */
-@RequiresApi(14)
-@TargetApi(14)
-final class ScenePort {
-
-    Runnable mEnterAction, mExitAction;
-
-    private Context mContext;
-
-    private int mLayoutId = -1;
-
-    private ViewGroup mSceneRoot;
-
-    private View mLayout; // alternative to layoutId
-
-    /**
-     * Constructs a Scene with no information about how values will change
-     * when this scene is applied. This constructor might be used when
-     * a Scene is created with the intention of being dynamically configured,
-     * through setting {@link #setEnterAction(Runnable)} and possibly
-     * {@link #setExitAction(Runnable)}.
-     *
-     * @param sceneRoot The root of the hierarchy in which scene changes
-     *                  and transitions will take place.
-     */
-    public ScenePort(ViewGroup sceneRoot) {
-        mSceneRoot = sceneRoot;
-    }
-
-    /**
-     * Constructs a Scene which, when entered, will remove any
-     * children from the sceneRoot container and will inflate and add
-     * the hierarchy specified by the layoutId resource file.
-     *
-     * <p>This method is hidden because layoutId-based scenes should be
-     * created by the caching factory method {@link ScenePort#getCurrentScene(View)}.</p>
-     *
-     * @param sceneRoot The root of the hierarchy in which scene changes
-     *                  and transitions will take place.
-     * @param layoutId  The id of a resource file that defines the view
-     *                  hierarchy of this scene.
-     * @param context   The context used in the process of inflating
-     *                  the layout resource.
-     */
-    private ScenePort(ViewGroup sceneRoot, int layoutId, Context context) {
-        mContext = context;
-        mSceneRoot = sceneRoot;
-        mLayoutId = layoutId;
-    }
-
-    /**
-     * Constructs a Scene which, when entered, will remove any
-     * children from the sceneRoot container and add the layout
-     * object as a new child of that container.
-     *
-     * @param sceneRoot The root of the hierarchy in which scene changes
-     *                  and transitions will take place.
-     * @param layout    The view hierarchy of this scene, added as a child
-     *                  of sceneRoot when this scene is entered.
-     */
-    public ScenePort(ViewGroup sceneRoot, View layout) {
-        mSceneRoot = sceneRoot;
-        mLayout = layout;
-    }
-
-    /**
-     * Returns a Scene described by the resource file associated with the given
-     * <code>layoutId</code> parameter.
-     *
-     * @param sceneRoot The root of the hierarchy in which scene changes
-     *                  and transitions will take place.
-     * @param layoutId  The id of a standard layout resource file.
-     * @param context   The context used in the process of inflating
-     *                  the layout resource.
-     * @return The scene for the given root and layout id
-     */
-    public static ScenePort getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
-        // We don't cache ScenePort, but android.support.transition.Scene.
-        return new ScenePort(sceneRoot, layoutId, context);
-    }
-
-    /**
-     * Set the scene that the given view is in. The current scene is set only
-     * on the root view of a scene, not for every view in that hierarchy. This
-     * information is used by Scene to determine whether there is a previous
-     * scene which should be exited before the new scene is entered.
-     *
-     * @param view The view on which the current scene is being set
-     */
-    static void setCurrentScene(View view, ScenePort scene) {
-        view.setTag(R.id.transition_current_scene, scene);
-    }
-
-    /**
-     * Gets the current {@link ScenePort} set on the given view. A scene is set on a view
-     * only if that view is the scene root.
-     *
-     * @return The current Scene set on this view. A value of null indicates that
-     * no Scene is currently set.
-     */
-    static ScenePort getCurrentScene(View view) {
-        return (ScenePort) view.getTag(R.id.transition_current_scene);
-    }
-
-    /**
-     * Gets the root of the scene, which is the root of the view hierarchy
-     * affected by changes due to this scene, and which will be animated
-     * when this scene is entered.
-     *
-     * @return The root of the view hierarchy affected by this scene.
-     */
-    public ViewGroup getSceneRoot() {
-        return mSceneRoot;
-    }
-
-    /**
-     * Exits this scene, if it is the current scene
-     * on the scene's {@link #getSceneRoot() scene root}. The current scene is
-     * set when {@link #enter() entering} a scene.
-     * Exiting a scene runs the {@link #setExitAction(Runnable) exit action}
-     * if there is one.
-     */
-    public void exit() {
-        if (getCurrentScene(mSceneRoot) == this) {
-            if (mExitAction != null) {
-                mExitAction.run();
-            }
-        }
-    }
-
-    /**
-     * Enters this scene, which entails changing all values that
-     * are specified by this scene. These may be values associated
-     * with a layout view group or layout resource file which will
-     * now be added to the scene root, or it may be values changed by
-     * an {@link #setEnterAction(Runnable)} enter action}, or a
-     * combination of the these. No transition will be run when the
-     * scene is entered. To get transition behavior in scene changes,
-     * use one of the methods in {@link TransitionManagerPort} instead.
-     */
-    public void enter() {
-
-        // Apply layout change, if any
-        if (mLayoutId > 0 || mLayout != null) {
-            // empty out parent container before adding to it
-            getSceneRoot().removeAllViews();
-
-            if (mLayoutId > 0) {
-                LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
-            } else {
-                mSceneRoot.addView(mLayout);
-            }
-        }
-
-        // Notify next scene that it is entering. Subclasses may override to configure scene.
-        if (mEnterAction != null) {
-            mEnterAction.run();
-        }
-
-        setCurrentScene(mSceneRoot, this);
-    }
-
-    /**
-     * Scenes that are not defined with layout resources or
-     * hierarchies, or which need to perform additional steps
-     * after those hierarchies are changed to, should set an enter
-     * action, and possibly an exit action as well. An enter action
-     * will cause Scene to call back into application code to do
-     * anything else the application needs after transitions have
-     * captured pre-change values and after any other scene changes
-     * have been applied, such as the layout (if any) being added to
-     * the view hierarchy. After this method is called, Transitions will
-     * be played.
-     *
-     * @param action The runnable whose {@link Runnable#run() run()} method will
-     *               be called when this scene is entered
-     * @see #setExitAction(Runnable)
-     * @see ScenePort#ScenePort(ViewGroup, int, Context)
-     * @see ScenePort#ScenePort(ViewGroup, ViewGroup)
-     */
-    public void setEnterAction(Runnable action) {
-        mEnterAction = action;
-    }
-
-    /**
-     * Scenes that are not defined with layout resources or
-     * hierarchies, or which need to perform additional steps
-     * after those hierarchies are changed to, should set an enter
-     * action, and possibly an exit action as well. An exit action
-     * will cause Scene to call back into application code to do
-     * anything the application needs to do after applicable transitions have
-     * captured pre-change values, but before any other scene changes
-     * have been applied, such as the new layout (if any) being added to
-     * the view hierarchy. After this method is called, the next scene
-     * will be entered, including a call to {@link #setEnterAction(Runnable)}
-     * if an enter action is set.
-     *
-     * @see #setEnterAction(Runnable)
-     * @see ScenePort#ScenePort(ViewGroup, int, Context)
-     * @see ScenePort#ScenePort(ViewGroup, ViewGroup)
-     */
-    public void setExitAction(Runnable action) {
-        mExitAction = action;
-    }
-
-
-    /**
-     * Returns whether this Scene was created by a layout resource file, determined
-     * by the layoutId passed into
-     * {@link #getSceneForLayout(ViewGroup, int, Context)}.
-     */
-    boolean isCreatedFromLayoutResource() {
-        return (mLayoutId > 0);
-    }
-}
\ No newline at end of file
diff --git a/transition/ics/android/support/transition/SceneStaticsIcs.java b/transition/ics/android/support/transition/SceneStaticsIcs.java
deleted file mode 100644
index bcc7451..0000000
--- a/transition/ics/android/support/transition/SceneStaticsIcs.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(14)
-@TargetApi(14)
-class SceneStaticsIcs extends SceneStaticsImpl {
-
-    @Override
-    public SceneImpl getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
-        SceneIcs scene = new SceneIcs();
-        scene.mScene = ScenePort.getSceneForLayout(sceneRoot, layoutId, context);
-        return scene;
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionIcs.java b/transition/ics/android/support/transition/TransitionIcs.java
deleted file mode 100644
index 832b59e..0000000
--- a/transition/ics/android/support/transition/TransitionIcs.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionIcs extends TransitionImpl {
-
-    /* package */ TransitionPort mTransition;
-
-    /* package */ TransitionInterface mExternalTransition;
-
-    private CompatListener mCompatListener;
-
-    @Override
-    public void init(TransitionInterface external, Object internal) {
-        mExternalTransition = external;
-        if (internal == null) {
-            mTransition = new TransitionWrapper(external);
-        } else {
-            mTransition = (TransitionPort) internal;
-        }
-    }
-
-    @Override
-    public TransitionImpl addListener(final TransitionInterfaceListener listener) {
-        if (mCompatListener == null) {
-            mCompatListener = new CompatListener();
-            mTransition.addListener(mCompatListener);
-        }
-        mCompatListener.addListener(listener);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl removeListener(TransitionInterfaceListener listener) {
-        if (mCompatListener == null) {
-            return this;
-        }
-        mCompatListener.removeListener(listener);
-        if (mCompatListener.isEmpty()) {
-            mTransition.removeListener(mCompatListener);
-            mCompatListener = null;
-        }
-        return this;
-    }
-
-    @Override
-    public TransitionImpl addTarget(View target) {
-        mTransition.addTarget(target);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl addTarget(int targetId) {
-        mTransition.addTarget(targetId);
-        return this;
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        mTransition.captureEndValues(transitionValues);
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        mTransition.captureStartValues(transitionValues);
-    }
-
-    @Override
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        return mTransition.createAnimator(sceneRoot, startValues, endValues);
-    }
-
-    @Override
-    public TransitionImpl excludeChildren(View target, boolean exclude) {
-        mTransition.excludeChildren(target, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeChildren(int targetId, boolean exclude) {
-        mTransition.excludeChildren(targetId, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeChildren(Class type, boolean exclude) {
-        mTransition.excludeChildren(type, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeTarget(View target, boolean exclude) {
-        mTransition.excludeTarget(target, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeTarget(int targetId, boolean exclude) {
-        mTransition.excludeTarget(targetId, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeTarget(Class type, boolean exclude) {
-        mTransition.excludeTarget(type, exclude);
-        return this;
-    }
-
-    @Override
-    public long getDuration() {
-        return mTransition.getDuration();
-    }
-
-    @Override
-    public TransitionImpl setDuration(long duration) {
-        mTransition.setDuration(duration);
-        return this;
-    }
-
-    @Override
-    public TimeInterpolator getInterpolator() {
-        return mTransition.getInterpolator();
-    }
-
-    @Override
-    public TransitionImpl setInterpolator(TimeInterpolator interpolator) {
-        mTransition.setInterpolator(interpolator);
-        return this;
-    }
-
-    @Override
-    public String getName() {
-        return mTransition.getName();
-    }
-
-    @Override
-    public long getStartDelay() {
-        return mTransition.getStartDelay();
-    }
-
-    @Override
-    public TransitionImpl setStartDelay(long startDelay) {
-        mTransition.setStartDelay(startDelay);
-        return this;
-    }
-
-    @Override
-    public List<Integer> getTargetIds() {
-        return mTransition.getTargetIds();
-    }
-
-    @Override
-    public List<View> getTargets() {
-        return mTransition.getTargets();
-    }
-
-    @Override
-    public String[] getTransitionProperties() {
-        return mTransition.getTransitionProperties();
-    }
-
-    @Override
-    public TransitionValues getTransitionValues(View view, boolean start) {
-        return mTransition.getTransitionValues(view, start);
-    }
-
-    @Override
-    public TransitionImpl removeTarget(View target) {
-        mTransition.removeTarget(target);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl removeTarget(int targetId) {
-        mTransition.removeTarget(targetId);
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        return mTransition.toString();
-    }
-
-    private static class TransitionWrapper extends TransitionPort {
-
-        private TransitionInterface mTransition;
-
-        public TransitionWrapper(TransitionInterface transition) {
-            mTransition = transition;
-        }
-
-        @Override
-        public void captureStartValues(TransitionValues transitionValues) {
-            mTransition.captureStartValues(transitionValues);
-        }
-
-        @Override
-        public void captureEndValues(TransitionValues transitionValues) {
-            mTransition.captureEndValues(transitionValues);
-        }
-
-        @Override
-        public Animator createAnimator(ViewGroup sceneRoot,
-                TransitionValues startValues, TransitionValues endValues) {
-            return mTransition.createAnimator(sceneRoot, startValues, endValues);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private class CompatListener implements TransitionPort.TransitionListener {
-
-        private final ArrayList<TransitionInterfaceListener> mListeners = new ArrayList<>();
-
-        CompatListener() {
-        }
-
-        public void addListener(TransitionInterfaceListener listener) {
-            mListeners.add(listener);
-        }
-
-        public void removeListener(TransitionInterfaceListener listener) {
-            mListeners.remove(listener);
-        }
-
-        public boolean isEmpty() {
-            return mListeners.isEmpty();
-        }
-
-        @Override
-        public void onTransitionStart(TransitionPort transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionStart(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionEnd(TransitionPort transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionEnd(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionCancel(TransitionPort transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionCancel(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionPause(TransitionPort transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionPause(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionResume(TransitionPort transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionResume(mExternalTransition);
-            }
-        }
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionManagerIcs.java b/transition/ics/android/support/transition/TransitionManagerIcs.java
deleted file mode 100644
index d277ae7..0000000
--- a/transition/ics/android/support/transition/TransitionManagerIcs.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionManagerIcs extends TransitionManagerImpl {
-
-    private final TransitionManagerPort mTransitionManager = new TransitionManagerPort();
-
-    @Override
-    public void setTransition(SceneImpl scene, TransitionImpl transition) {
-        mTransitionManager.setTransition(((SceneIcs) scene).mScene,
-                transition == null ? null : ((TransitionIcs) transition).mTransition);
-    }
-
-    @Override
-    public void setTransition(SceneImpl fromScene, SceneImpl toScene, TransitionImpl transition) {
-        mTransitionManager.setTransition(((SceneIcs) fromScene).mScene, ((SceneIcs) toScene).mScene,
-                transition == null ? null : ((TransitionIcs) transition).mTransition);
-    }
-
-    @Override
-    public void transitionTo(SceneImpl scene) {
-        mTransitionManager.transitionTo(((SceneIcs) scene).mScene);
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionManagerPort.java b/transition/ics/android/support/transition/TransitionManagerPort.java
deleted file mode 100644
index 2ea7656..0000000
--- a/transition/ics/android/support/transition/TransitionManagerPort.java
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.view.ViewCompat;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionManagerPort {
-    // TODO: how to handle enter/exit?
-
-    private static final String[] EMPTY_STRINGS = new String[0];
-
-    private static String LOG_TAG = "TransitionManager";
-
-    private static TransitionPort sDefaultTransition = new AutoTransitionPort();
-
-    private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<TransitionPort>>>>
-            sRunningTransitions = new ThreadLocal<>();
-
-    static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<>();
-
-    ArrayMap<ScenePort, TransitionPort> mSceneTransitions = new ArrayMap<>();
-
-    ArrayMap<ScenePort, ArrayMap<ScenePort, TransitionPort>> mScenePairTransitions =
-            new ArrayMap<>();
-
-    ArrayMap<ScenePort, ArrayMap<String, TransitionPort>> mSceneNameTransitions = new ArrayMap<>();
-
-    ArrayMap<String, ArrayMap<ScenePort, TransitionPort>> mNameSceneTransitions = new ArrayMap<>();
-
-    /**
-     * Gets the current default transition. The initial value is an {@link
-     * AutoTransition} instance.
-     *
-     * @return The current default transition.
-     * @hide pending later changes
-     * @see #setDefaultTransition(TransitionPort)
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static TransitionPort getDefaultTransition() {
-        return sDefaultTransition;
-    }
-
-    /**
-     * Sets the transition to be used for any scene change for which no
-     * other transition is explicitly set. The initial value is
-     * an {@link AutoTransition} instance.
-     *
-     * @param transition The default transition to be used for scene changes.
-     * @hide pending later changes
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setDefaultTransition(TransitionPort transition) {
-        sDefaultTransition = transition;
-    }
-
-    /**
-     * This is where all of the work of a transition/scene-change is
-     * orchestrated. This method captures the start values for the given
-     * transition, exits the current Scene, enters the new scene, captures
-     * the end values for the transition, and finally plays the
-     * resulting values-populated transition.
-     *
-     * @param scene      The scene being entered
-     * @param transition The transition to play for this scene change
-     */
-    private static void changeScene(ScenePort scene, TransitionPort transition) {
-
-        final ViewGroup sceneRoot = scene.getSceneRoot();
-
-        TransitionPort transitionClone = null;
-        if (transition != null) {
-            transitionClone = transition.clone();
-            transitionClone.setSceneRoot(sceneRoot);
-        }
-
-        ScenePort oldScene = ScenePort.getCurrentScene(sceneRoot);
-        if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
-            transitionClone.setCanRemoveViews(true);
-        }
-
-        sceneChangeSetup(sceneRoot, transitionClone);
-
-        scene.enter();
-
-        sceneChangeRunTransition(sceneRoot, transitionClone);
-    }
-
-    static ArrayMap<ViewGroup, ArrayList<TransitionPort>> getRunningTransitions() {
-        WeakReference<ArrayMap<ViewGroup, ArrayList<TransitionPort>>> runningTransitions =
-                sRunningTransitions.get();
-        if (runningTransitions == null || runningTransitions.get() == null) {
-            ArrayMap<ViewGroup, ArrayList<TransitionPort>> transitions = new ArrayMap<>();
-            runningTransitions = new WeakReference<>(transitions);
-            sRunningTransitions.set(runningTransitions);
-        }
-        return runningTransitions.get();
-    }
-
-    private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
-            final TransitionPort transition) {
-        if (transition != null && sceneRoot != null) {
-            MultiListener listener = new MultiListener(transition, sceneRoot);
-            sceneRoot.addOnAttachStateChangeListener(listener);
-            sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
-        }
-    }
-
-    private static void sceneChangeSetup(ViewGroup sceneRoot, TransitionPort transition) {
-
-        // Capture current values
-        ArrayList<TransitionPort> runningTransitions = getRunningTransitions().get(sceneRoot);
-
-        if (runningTransitions != null && runningTransitions.size() > 0) {
-            for (TransitionPort runningTransition : runningTransitions) {
-                runningTransition.pause(sceneRoot);
-            }
-        }
-
-        if (transition != null) {
-            transition.captureValues(sceneRoot, true);
-        }
-
-        // Notify previous scene that it is being exited
-        ScenePort previousScene = ScenePort.getCurrentScene(sceneRoot);
-        if (previousScene != null) {
-            previousScene.exit();
-        }
-    }
-
-    public static void go(ScenePort scene) {
-        changeScene(scene, sDefaultTransition);
-    }
-
-    public static void go(ScenePort scene, TransitionPort transition) {
-        changeScene(scene, transition);
-    }
-
-    public static void beginDelayedTransition(final ViewGroup sceneRoot) {
-        beginDelayedTransition(sceneRoot, null);
-    }
-
-    public static void beginDelayedTransition(final ViewGroup sceneRoot,
-            TransitionPort transition) {
-        if (!sPendingTransitions.contains(sceneRoot) && ViewCompat.isLaidOut(sceneRoot)) {
-            if (TransitionPort.DBG) {
-                Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
-                        sceneRoot + ", " + transition);
-            }
-            sPendingTransitions.add(sceneRoot);
-            if (transition == null) {
-                transition = sDefaultTransition;
-            }
-            final TransitionPort transitionClone = transition.clone();
-            sceneChangeSetup(sceneRoot, transitionClone);
-            ScenePort.setCurrentScene(sceneRoot, null);
-            sceneChangeRunTransition(sceneRoot, transitionClone);
-        }
-    }
-
-    public void setTransition(ScenePort scene, TransitionPort transition) {
-        mSceneTransitions.put(scene, transition);
-    }
-
-    public void setTransition(ScenePort fromScene, ScenePort toScene, TransitionPort transition) {
-        ArrayMap<ScenePort, TransitionPort> sceneTransitionMap = mScenePairTransitions.get(toScene);
-        if (sceneTransitionMap == null) {
-            sceneTransitionMap = new ArrayMap<>();
-            mScenePairTransitions.put(toScene, sceneTransitionMap);
-        }
-        sceneTransitionMap.put(fromScene, transition);
-    }
-
-    /**
-     * Returns the Transition for the given scene being entered. The result
-     * depends not only on the given scene, but also the scene which the
-     * {@link ScenePort#getSceneRoot() sceneRoot} of the Scene is currently in.
-     *
-     * @param scene The scene being entered
-     * @return The Transition to be used for the given scene change. If no
-     * Transition was specified for this scene change, the default transition
-     * will be used instead.
-     */
-    private TransitionPort getTransition(ScenePort scene) {
-        TransitionPort transition;
-        ViewGroup sceneRoot = scene.getSceneRoot();
-        if (sceneRoot != null) {
-            // TODO: cached in Scene instead? long-term, cache in View itself
-            ScenePort currScene = ScenePort.getCurrentScene(sceneRoot);
-            if (currScene != null) {
-                ArrayMap<ScenePort, TransitionPort> sceneTransitionMap = mScenePairTransitions
-                        .get(scene);
-                if (sceneTransitionMap != null) {
-                    transition = sceneTransitionMap.get(currScene);
-                    if (transition != null) {
-                        return transition;
-                    }
-                }
-            }
-        }
-        transition = mSceneTransitions.get(scene);
-        return (transition != null) ? transition : sDefaultTransition;
-    }
-
-    /**
-     * Retrieve the transition from a named scene to a target defined scene if one has been
-     * associated with this TransitionManager.
-     *
-     * <p>A named scene is an indirect link for a transition. Fundamentally a named
-     * scene represents a potentially arbitrary intersection point of two otherwise independent
-     * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO"
-     * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y.
-     * In this way applications may define an API for more sophisticated transitions between
-     * caller and called activities very similar to the way that <code>Intent</code> extras
-     * define APIs for arguments and data propagation between activities.</p>
-     *
-     * @param fromName Named scene that this transition corresponds to
-     * @param toScene  Target scene that this transition will move to
-     * @return Transition corresponding to the given fromName and toScene or null
-     * if no association exists in this TransitionManager
-     * @see #setTransition(String, ScenePort, TransitionPort)
-     */
-    public TransitionPort getNamedTransition(String fromName, ScenePort toScene) {
-        ArrayMap<ScenePort, TransitionPort> m = mNameSceneTransitions.get(fromName);
-        if (m != null) {
-            return m.get(toScene);
-        }
-        return null;
-    }
-
-    ;
-
-    /**
-     * Retrieve the transition from a defined scene to a target named scene if one has been
-     * associated with this TransitionManager.
-     *
-     * <p>A named scene is an indirect link for a transition. Fundamentally a named
-     * scene represents a potentially arbitrary intersection point of two otherwise independent
-     * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO"
-     * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y.
-     * In this way applications may define an API for more sophisticated transitions between
-     * caller and called activities very similar to the way that <code>Intent</code> extras
-     * define APIs for arguments and data propagation between activities.</p>
-     *
-     * @param fromScene Scene that this transition starts from
-     * @param toName    Name of the target scene
-     * @return Transition corresponding to the given fromScene and toName or null
-     * if no association exists in this TransitionManager
-     */
-    public TransitionPort getNamedTransition(ScenePort fromScene, String toName) {
-        ArrayMap<String, TransitionPort> m = mSceneNameTransitions.get(fromScene);
-        if (m != null) {
-            return m.get(toName);
-        }
-        return null;
-    }
-
-    /**
-     * Retrieve the supported target named scenes when transitioning away from the given scene.
-     *
-     * <p>A named scene is an indirect link for a transition. Fundamentally a named
-     * scene represents a potentially arbitrary intersection point of two otherwise independent
-     * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO"
-     * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y.
-     * In this way applications may define an API for more sophisticated transitions between
-     * caller and called activities very similar to the way that <code>Intent</code> extras
-     * define APIs for arguments and data propagation between activities.</p>
-     *
-     * @param fromScene Scene to transition from
-     * @return An array of Strings naming each supported transition starting from
-     * <code>fromScene</code>. If no transitions to a named scene from the given
-     * scene are supported this function will return a String[] of length 0.
-     * @see #setTransition(ScenePort, String, TransitionPort)
-     */
-    public String[] getTargetSceneNames(ScenePort fromScene) {
-        final ArrayMap<String, TransitionPort> m = mSceneNameTransitions.get(fromScene);
-        if (m == null) {
-            return EMPTY_STRINGS;
-        }
-        final int count = m.size();
-        final String[] result = new String[count];
-        for (int i = 0; i < count; i++) {
-            result[i] = m.keyAt(i);
-        }
-        return result;
-    }
-
-    /**
-     * Set a transition from a specific scene to a named scene.
-     *
-     * <p>A named scene is an indirect link for a transition. Fundamentally a named
-     * scene represents a potentially arbitrary intersection point of two otherwise independent
-     * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO"
-     * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y.
-     * In this way applications may define an API for more sophisticated transitions between
-     * caller and called activities very similar to the way that <code>Intent</code> extras
-     * define APIs for arguments and data propagation between activities.</p>
-     *
-     * @param fromScene  Scene to transition from
-     * @param toName     Named scene to transition to
-     * @param transition Transition to use
-     * @see #getTargetSceneNames(ScenePort)
-     */
-    public void setTransition(ScenePort fromScene, String toName, TransitionPort transition) {
-        ArrayMap<String, TransitionPort> m = mSceneNameTransitions.get(fromScene);
-        if (m == null) {
-            m = new ArrayMap<>();
-            mSceneNameTransitions.put(fromScene, m);
-        }
-        m.put(toName, transition);
-    }
-
-    /**
-     * Set a transition from a named scene to a concrete scene.
-     *
-     * <p>A named scene is an indirect link for a transition. Fundamentally a named
-     * scene represents a potentially arbitrary intersection point of two otherwise independent
-     * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO"
-     * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y.
-     * In this way applications may define an API for more sophisticated transitions between
-     * caller and called activities very similar to the way that <code>Intent</code> extras
-     * define APIs for arguments and data propagation between activities.</p>
-     *
-     * @param fromName   Named scene to transition from
-     * @param toScene    Scene to transition to
-     * @param transition Transition to use
-     * @see #getNamedTransition(String, ScenePort)
-     */
-    public void setTransition(String fromName, ScenePort toScene, TransitionPort transition) {
-        ArrayMap<ScenePort, TransitionPort> m = mNameSceneTransitions.get(fromName);
-        if (m == null) {
-            m = new ArrayMap<>();
-            mNameSceneTransitions.put(fromName, m);
-        }
-        m.put(toScene, transition);
-    }
-
-    public void transitionTo(ScenePort scene) {
-        // Auto transition if there is no transition declared for the Scene, but there is
-        // a root or parent view
-        changeScene(scene, getTransition(scene));
-    }
-
-    /**
-     * This private utility class is used to listen for both OnPreDraw and
-     * OnAttachStateChange events. OnPreDraw events are the main ones we care
-     * about since that's what triggers the transition to take place.
-     * OnAttachStateChange events are also important in case the view is removed
-     * from the hierarchy before the OnPreDraw event takes place; it's used to
-     * clean up things since the OnPreDraw listener didn't get called in time.
-     */
-    private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
-            View.OnAttachStateChangeListener {
-
-        TransitionPort mTransition;
-
-        ViewGroup mSceneRoot;
-
-        MultiListener(TransitionPort transition, ViewGroup sceneRoot) {
-            mTransition = transition;
-            mSceneRoot = sceneRoot;
-        }
-
-        private void removeListeners() {
-            mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-            mSceneRoot.removeOnAttachStateChangeListener(this);
-        }
-
-        @Override
-        public void onViewAttachedToWindow(View v) {
-        }
-
-        @Override
-        public void onViewDetachedFromWindow(View v) {
-            removeListeners();
-
-            sPendingTransitions.remove(mSceneRoot);
-            ArrayList<TransitionPort> runningTransitions = getRunningTransitions().get(mSceneRoot);
-            if (runningTransitions != null && runningTransitions.size() > 0) {
-                for (TransitionPort runningTransition : runningTransitions) {
-                    runningTransition.resume(mSceneRoot);
-                }
-            }
-            mTransition.clearValues(true);
-        }
-
-        @Override
-        public boolean onPreDraw() {
-            removeListeners();
-            sPendingTransitions.remove(mSceneRoot);
-            // Add to running list, handle end to remove it
-            final ArrayMap<ViewGroup, ArrayList<TransitionPort>> runningTransitions =
-                    getRunningTransitions();
-            ArrayList<TransitionPort> currentTransitions = runningTransitions.get(mSceneRoot);
-            ArrayList<TransitionPort> previousRunningTransitions = null;
-            if (currentTransitions == null) {
-                currentTransitions = new ArrayList<>();
-                runningTransitions.put(mSceneRoot, currentTransitions);
-            } else if (currentTransitions.size() > 0) {
-                previousRunningTransitions = new ArrayList<>(currentTransitions);
-            }
-            currentTransitions.add(mTransition);
-            mTransition.addListener(new TransitionPort.TransitionListenerAdapter() {
-                @Override
-                public void onTransitionEnd(TransitionPort transition) {
-                    ArrayList<TransitionPort> currentTransitions =
-                            runningTransitions.get(mSceneRoot);
-                    currentTransitions.remove(transition);
-                }
-            });
-            mTransition.captureValues(mSceneRoot, false);
-            if (previousRunningTransitions != null) {
-                for (TransitionPort runningTransition : previousRunningTransitions) {
-                    runningTransition.resume(mSceneRoot);
-                }
-            }
-            mTransition.playTransition(mSceneRoot);
-
-            return true;
-        }
-    }
-}
diff --git a/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java b/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
deleted file mode 100644
index aab7083..0000000
--- a/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionManagerStaticsIcs extends TransitionManagerStaticsImpl {
-
-    @Override
-    public void go(SceneImpl scene) {
-        TransitionManagerPort.go(((SceneIcs) scene).mScene);
-    }
-
-    @Override
-    public void go(SceneImpl scene, TransitionImpl transition) {
-        TransitionManagerPort.go(((SceneIcs) scene).mScene,
-                transition == null ? null : ((TransitionIcs) transition).mTransition);
-    }
-
-    @Override
-    public void beginDelayedTransition(ViewGroup sceneRoot) {
-        TransitionManagerPort.beginDelayedTransition(sceneRoot);
-    }
-
-    @Override
-    public void beginDelayedTransition(ViewGroup sceneRoot, TransitionImpl transition) {
-        TransitionManagerPort.beginDelayedTransition(sceneRoot,
-                transition == null ? null : ((TransitionIcs) transition).mTransition);
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionPort.java b/transition/ics/android/support/transition/TransitionPort.java
deleted file mode 100644
index 98b217d..0000000
--- a/transition/ics/android/support/transition/TransitionPort.java
+++ /dev/null
@@ -1,1295 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.util.LongSparseArray;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RequiresApi(14)
-@TargetApi(14)
-abstract class TransitionPort implements Cloneable {
-
-    static final boolean DBG = false;
-
-    private static final String LOG_TAG = "Transition";
-
-    // Per-animator information used for later canceling when future transitions overlap
-    private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators =
-            new ThreadLocal<>();
-
-    long mStartDelay = -1;
-
-    long mDuration = -1;
-
-    TimeInterpolator mInterpolator = null;
-
-    ArrayList<Integer> mTargetIds = new ArrayList<>();
-
-    ArrayList<View> mTargets = new ArrayList<>();
-
-    ArrayList<Integer> mTargetIdExcludes = null;
-
-    ArrayList<View> mTargetExcludes = null;
-
-    ArrayList<Class> mTargetTypeExcludes = null;
-
-    ArrayList<Integer> mTargetIdChildExcludes = null;
-
-    ArrayList<View> mTargetChildExcludes = null;
-
-    ArrayList<Class> mTargetTypeChildExcludes = null;
-
-    TransitionSetPort mParent = null;
-
-    // Scene Root is set at createAnimator() time in the cloned Transition
-    ViewGroup mSceneRoot = null;
-
-    // Whether removing views from their parent is possible. This is only for views
-    // in the start scene, which are no longer in the view hierarchy. This property
-    // is determined by whether the previous Scene was created from a layout
-    // resource, and thus the views from the exited scene are going away anyway
-    // and can be removed as necessary to achieve a particular effect, such as
-    // removing them from parents to add them to overlays.
-    boolean mCanRemoveViews = false;
-
-    // Number of per-target instances of this Transition currently running. This count is
-    // determined by calls to start() and end()
-    int mNumInstances = 0;
-
-    // Whether this transition is currently paused, due to a call to pause()
-    boolean mPaused = false;
-
-    // The set of listeners to be sent transition lifecycle events.
-    ArrayList<TransitionListener> mListeners = null;
-
-    // The set of animators collected from calls to createAnimator(),
-    // to be run in runAnimators()
-    ArrayList<Animator> mAnimators = new ArrayList<>();
-
-    private String mName = getClass().getName();
-
-    private android.support.transition.TransitionValuesMaps mStartValues
-            = new android.support.transition.TransitionValuesMaps();
-
-    private android.support.transition.TransitionValuesMaps mEndValues
-            = new android.support.transition.TransitionValuesMaps();
-
-    // Track all animators in use in case the transition gets canceled and needs to
-    // cancel running animators
-    ArrayList<Animator> mCurrentAnimators = new ArrayList<>();
-
-    // Whether this transition has ended. Used to avoid pause/resume on transitions
-    // that have completed
-    private boolean mEnded = false;
-
-    /**
-     * Constructs a Transition object with no target objects. A transition with
-     * no targets defaults to running on all target objects in the scene hierarchy
-     * (if the transition is not contained in a TransitionSet), or all target
-     * objects passed down from its parent (if it is in a TransitionSet).
-     */
-    public TransitionPort() {
-    }
-
-    private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() {
-        ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get();
-        if (runningAnimators == null) {
-            runningAnimators = new ArrayMap<>();
-            sRunningAnimators.set(runningAnimators);
-        }
-        return runningAnimators;
-    }
-
-    public long getDuration() {
-        return mDuration;
-    }
-
-    public TransitionPort setDuration(long duration) {
-        mDuration = duration;
-        return this;
-    }
-
-    public long getStartDelay() {
-        return mStartDelay;
-    }
-
-    public TransitionPort setStartDelay(long startDelay) {
-        mStartDelay = startDelay;
-        return this;
-    }
-
-    public TimeInterpolator getInterpolator() {
-        return mInterpolator;
-    }
-
-    public TransitionPort setInterpolator(TimeInterpolator interpolator) {
-        mInterpolator = interpolator;
-        return this;
-    }
-
-    public String[] getTransitionProperties() {
-        return null;
-    }
-
-
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        return null;
-    }
-
-    /**
-     * This method, essentially a wrapper around all calls to createAnimator for all
-     * possible target views, is called with the entire set of start/end
-     * values. The implementation in Transition iterates through these lists
-     * and calls {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
-     * with each set of start/end values on this transition. The
-     * TransitionSet subclass overrides this method and delegates it to
-     * each of its children in succession.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
-            TransitionValuesMaps endValues) {
-        if (DBG) {
-            Log.d(LOG_TAG, "createAnimators() for " + this);
-        }
-        ArrayMap<View, TransitionValues> endCopy =
-                new ArrayMap<>(endValues.viewValues);
-        SparseArray<TransitionValues> endIdCopy =
-                new SparseArray<>(endValues.idValues.size());
-        for (int i = 0; i < endValues.idValues.size(); ++i) {
-            int id = endValues.idValues.keyAt(i);
-            endIdCopy.put(id, endValues.idValues.valueAt(i));
-        }
-        LongSparseArray<TransitionValues> endItemIdCopy =
-                new LongSparseArray<>(endValues.itemIdValues.size());
-        for (int i = 0; i < endValues.itemIdValues.size(); ++i) {
-            long id = endValues.itemIdValues.keyAt(i);
-            endItemIdCopy.put(id, endValues.itemIdValues.valueAt(i));
-        }
-        // Walk through the start values, playing everything we find
-        // Remove from the end set as we go
-        ArrayList<TransitionValues> startValuesList = new ArrayList<>();
-        ArrayList<TransitionValues> endValuesList = new ArrayList<>();
-        for (View view : startValues.viewValues.keySet()) {
-            TransitionValues start;
-            TransitionValues end = null;
-            boolean isInListView = false;
-            if (view.getParent() instanceof ListView) {
-                isInListView = true;
-            }
-            if (!isInListView) {
-                int id = view.getId();
-                start = startValues.viewValues.get(view) != null ?
-                        startValues.viewValues.get(view) : startValues.idValues.get(id);
-                if (endValues.viewValues.get(view) != null) {
-                    end = endValues.viewValues.get(view);
-                    endCopy.remove(view);
-                } else if (id != View.NO_ID) {
-                    end = endValues.idValues.get(id);
-                    View removeView = null;
-                    for (View viewToRemove : endCopy.keySet()) {
-                        if (viewToRemove.getId() == id) {
-                            removeView = viewToRemove;
-                        }
-                    }
-                    if (removeView != null) {
-                        endCopy.remove(removeView);
-                    }
-                }
-                endIdCopy.remove(id);
-                if (isValidTarget(view, id)) {
-                    startValuesList.add(start);
-                    endValuesList.add(end);
-                }
-            } else {
-                ListView parent = (ListView) view.getParent();
-                if (parent.getAdapter().hasStableIds()) {
-                    int position = parent.getPositionForView(view);
-                    long itemId = parent.getItemIdAtPosition(position);
-                    start = startValues.itemIdValues.get(itemId);
-                    endItemIdCopy.remove(itemId);
-                    // TODO: deal with targetIDs for itemIDs for ListView items
-                    startValuesList.add(start);
-                    endValuesList.add(end);
-                }
-            }
-        }
-        int startItemIdCopySize = startValues.itemIdValues.size();
-        for (int i = 0; i < startItemIdCopySize; ++i) {
-            long id = startValues.itemIdValues.keyAt(i);
-            if (isValidTarget(null, id)) {
-                TransitionValues start = startValues.itemIdValues.get(id);
-                TransitionValues end = endValues.itemIdValues.get(id);
-                endItemIdCopy.remove(id);
-                startValuesList.add(start);
-                endValuesList.add(end);
-            }
-        }
-        // Now walk through the remains of the end set
-        for (View view : endCopy.keySet()) {
-            int id = view.getId();
-            if (isValidTarget(view, id)) {
-                TransitionValues start = startValues.viewValues.get(view) != null ?
-                        startValues.viewValues.get(view) : startValues.idValues.get(id);
-                TransitionValues end = endCopy.get(view);
-                endIdCopy.remove(id);
-                startValuesList.add(start);
-                endValuesList.add(end);
-            }
-        }
-        int endIdCopySize = endIdCopy.size();
-        for (int i = 0; i < endIdCopySize; ++i) {
-            int id = endIdCopy.keyAt(i);
-            if (isValidTarget(null, id)) {
-                TransitionValues start = startValues.idValues.get(id);
-                TransitionValues end = endIdCopy.get(id);
-                startValuesList.add(start);
-                endValuesList.add(end);
-            }
-        }
-        int endItemIdCopySize = endItemIdCopy.size();
-        for (int i = 0; i < endItemIdCopySize; ++i) {
-            long id = endItemIdCopy.keyAt(i);
-            // TODO: Deal with targetIDs and itemIDs
-            TransitionValues start = startValues.itemIdValues.get(id);
-            TransitionValues end = endItemIdCopy.get(id);
-            startValuesList.add(start);
-            endValuesList.add(end);
-        }
-        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
-        for (int i = 0; i < startValuesList.size(); ++i) {
-            TransitionValues start = startValuesList.get(i);
-            TransitionValues end = endValuesList.get(i);
-            // Only bother trying to animate with values that differ between start/end
-            if (start != null || end != null) {
-                if (start == null || !start.equals(end)) {
-                    if (DBG) {
-                        View view = (end != null) ? end.view : start.view;
-                        Log.d(LOG_TAG, "  differing start/end values for view " +
-                                view);
-                        if (start == null || end == null) {
-                            Log.d(LOG_TAG, "    " + ((start == null) ?
-                                    "start null, end non-null" : "start non-null, end null"));
-                        } else {
-                            for (String key : start.values.keySet()) {
-                                Object startValue = start.values.get(key);
-                                Object endValue = end.values.get(key);
-                                if (startValue != endValue && !startValue.equals(endValue)) {
-                                    Log.d(LOG_TAG, "    " + key + ": start(" + startValue +
-                                            "), end(" + endValue + ")");
-                                }
-                            }
-                        }
-                    }
-                    // TODO: what to do about targetIds and itemIds?
-                    Animator animator = createAnimator(sceneRoot, start, end);
-                    if (animator != null) {
-                        // Save animation info for future cancellation purposes
-                        View view;
-                        TransitionValues infoValues = null;
-                        if (end != null) {
-                            view = end.view;
-                            String[] properties = getTransitionProperties();
-                            if (view != null && properties != null && properties.length > 0) {
-                                infoValues = new TransitionValues();
-                                infoValues.view = view;
-                                TransitionValues newValues = endValues.viewValues.get(view);
-                                if (newValues != null) {
-                                    for (int j = 0; j < properties.length; ++j) {
-                                        infoValues.values.put(properties[j],
-                                                newValues.values.get(properties[j]));
-                                    }
-                                }
-                                int numExistingAnims = runningAnimators.size();
-                                for (int j = 0; j < numExistingAnims; ++j) {
-                                    Animator anim = runningAnimators.keyAt(j);
-                                    AnimationInfo info = runningAnimators.get(anim);
-                                    if (info.values != null && info.view == view &&
-                                            ((info.name == null && getName() == null) ||
-                                                    info.name.equals(getName()))) {
-                                        if (info.values.equals(infoValues)) {
-                                            // Favor the old animator
-                                            animator = null;
-                                            break;
-                                        }
-                                    }
-                                }
-                            }
-                        } else {
-                            view = start.view;
-                        }
-                        if (animator != null) {
-                            AnimationInfo info = new AnimationInfo(view, getName(),
-                                    WindowIdPort.getWindowId(sceneRoot), infoValues);
-                            runningAnimators.put(animator, info);
-                            mAnimators.add(animator);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Internal utility method for checking whether a given view/id
-     * is valid for this transition, where "valid" means that either
-     * the Transition has no target/targetId list (the default, in which
-     * cause the transition should act on all views in the hiearchy), or
-     * the given view is in the target list or the view id is in the
-     * targetId list. If the target parameter is null, then the target list
-     * is not checked (this is in the case of ListView items, where the
-     * views are ignored and only the ids are used).
-     */
-    boolean isValidTarget(View target, long targetId) {
-        if (mTargetIdExcludes != null && mTargetIdExcludes.contains((int)targetId)) {
-            return false;
-        }
-        if (mTargetExcludes != null && mTargetExcludes.contains(target)) {
-            return false;
-        }
-        if (mTargetTypeExcludes != null && target != null) {
-            int numTypes = mTargetTypeExcludes.size();
-            for (int i = 0; i < numTypes; ++i) {
-                Class type = mTargetTypeExcludes.get(i);
-                if (type.isInstance(target)) {
-                    return false;
-                }
-            }
-        }
-        if (mTargetIds.size() == 0 && mTargets.size() == 0) {
-            return true;
-        }
-        if (mTargetIds.size() > 0) {
-            for (int i = 0; i < mTargetIds.size(); ++i) {
-                if (mTargetIds.get(i) == targetId) {
-                    return true;
-                }
-            }
-        }
-        if (target != null && mTargets.size() > 0) {
-            for (int i = 0; i < mTargets.size(); ++i) {
-                if (mTargets.get(i) == target) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * This is called internally once all animations have been set up by the
-     * transition hierarchy. \
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void runAnimators() {
-        if (DBG) {
-            Log.d(LOG_TAG, "runAnimators() on " + this);
-        }
-        start();
-        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
-        // Now start every Animator that was previously created for this transition
-        for (Animator anim : mAnimators) {
-            if (DBG) {
-                Log.d(LOG_TAG, "  anim: " + anim);
-            }
-            if (runningAnimators.containsKey(anim)) {
-                start();
-                runAnimator(anim, runningAnimators);
-            }
-        }
-        mAnimators.clear();
-        end();
-    }
-
-    private void runAnimator(Animator animator,
-            final ArrayMap<Animator, AnimationInfo> runningAnimators) {
-        if (animator != null) {
-            // TODO: could be a single listener instance for all of them since it uses the param
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    mCurrentAnimators.add(animation);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    runningAnimators.remove(animation);
-                    mCurrentAnimators.remove(animation);
-                }
-            });
-            animate(animator);
-        }
-    }
-
-    public abstract void captureStartValues(TransitionValues transitionValues);
-
-    public abstract void captureEndValues(TransitionValues transitionValues);
-
-    public TransitionPort addTarget(int targetId) {
-        if (targetId > 0) {
-            mTargetIds.add(targetId);
-        }
-        return this;
-    }
-
-    public TransitionPort removeTarget(int targetId) {
-        if (targetId > 0) {
-            mTargetIds.remove((Integer) targetId);
-        }
-        return this;
-    }
-
-    public TransitionPort excludeTarget(int targetId, boolean exclude) {
-        mTargetIdExcludes = excludeId(mTargetIdExcludes, targetId, exclude);
-        return this;
-    }
-
-
-    public TransitionPort excludeChildren(int targetId, boolean exclude) {
-        mTargetIdChildExcludes = excludeId(mTargetIdChildExcludes, targetId, exclude);
-        return this;
-    }
-
-    /**
-     * Utility method to manage the boilerplate code that is the same whether we
-     * are excluding targets or their children.
-     */
-    private ArrayList<Integer> excludeId(ArrayList<Integer> list, int targetId, boolean exclude) {
-        if (targetId > 0) {
-            if (exclude) {
-                list = ArrayListManager.add(list, targetId);
-            } else {
-                list = ArrayListManager.remove(list, targetId);
-            }
-        }
-        return list;
-    }
-
-    public TransitionPort excludeTarget(View target, boolean exclude) {
-        mTargetExcludes = excludeView(mTargetExcludes, target, exclude);
-        return this;
-    }
-
-    public TransitionPort excludeChildren(View target, boolean exclude) {
-        mTargetChildExcludes = excludeView(mTargetChildExcludes, target, exclude);
-        return this;
-    }
-
-    /**
-     * Utility method to manage the boilerplate code that is the same whether we
-     * are excluding targets or their children.
-     */
-    private ArrayList<View> excludeView(ArrayList<View> list, View target, boolean exclude) {
-        if (target != null) {
-            if (exclude) {
-                list = ArrayListManager.add(list, target);
-            } else {
-                list = ArrayListManager.remove(list, target);
-            }
-        }
-        return list;
-    }
-
-    public TransitionPort excludeTarget(Class type, boolean exclude) {
-        mTargetTypeExcludes = excludeType(mTargetTypeExcludes, type, exclude);
-        return this;
-    }
-
-    public TransitionPort excludeChildren(Class type, boolean exclude) {
-        mTargetTypeChildExcludes = excludeType(mTargetTypeChildExcludes, type, exclude);
-        return this;
-    }
-
-    /**
-     * Utility method to manage the boilerplate code that is the same whether we
-     * are excluding targets or their children.
-     */
-    private ArrayList<Class> excludeType(ArrayList<Class> list, Class type, boolean exclude) {
-        if (type != null) {
-            if (exclude) {
-                list = ArrayListManager.add(list, type);
-            } else {
-                list = ArrayListManager.remove(list, type);
-            }
-        }
-        return list;
-    }
-
-    /**
-     * Sets the target view instances that this Transition is interested in
-     * animating. By default, there are no targets, and a Transition will
-     * listen for changes on every view in the hierarchy below the sceneRoot
-     * of the Scene being transitioned into. Setting targets constrains
-     * the Transition to only listen for, and act on, these views.
-     * All other views will be ignored.
-     *
-     * <p>The target list is like the {@link #addTarget(int) targetId}
-     * list except this list specifies the actual View instances, not the ids
-     * of the views. This is an important distinction when scene changes involve
-     * view hierarchies which have been inflated separately; different views may
-     * share the same id but not actually be the same instance. If the transition
-     * should treat those views as the same, then {@link #addTarget(int)} should be used
-     * instead of {@link #addTarget(View)}. If, on the other hand, scene changes involve
-     * changes all within the same view hierarchy, among views which do not
-     * necessarily have ids set on them, then the target list of views may be more
-     * convenient.</p>
-     *
-     * @param target A View on which the Transition will act, must be non-null.
-     * @return The Transition to which the target is added.
-     * Returning the same object makes it easier to chain calls during
-     * construction, such as
-     * <code>transitionSet.addTransitions(new Fade()).addTarget(someView);</code>
-     * @see #addTarget(int)
-     */
-    public TransitionPort addTarget(View target) {
-        mTargets.add(target);
-        return this;
-    }
-
-    public TransitionPort removeTarget(View target) {
-        if (target != null) {
-            mTargets.remove(target);
-        }
-        return this;
-    }
-
-    public List<Integer> getTargetIds() {
-        return mTargetIds;
-    }
-
-    public List<View> getTargets() {
-        return mTargets;
-    }
-
-    /**
-     * Recursive method that captures values for the given view and the
-     * hierarchy underneath it.
-     *
-     * @param sceneRoot The root of the view hierarchy being captured
-     * @param start     true if this capture is happening before the scene change,
-     *                  false otherwise
-     */
-    void captureValues(ViewGroup sceneRoot, boolean start) {
-        clearValues(start);
-        if (mTargetIds.size() > 0 || mTargets.size() > 0) {
-            if (mTargetIds.size() > 0) {
-                for (int i = 0; i < mTargetIds.size(); ++i) {
-                    int id = mTargetIds.get(i);
-                    View view = sceneRoot.findViewById(id);
-                    if (view != null) {
-                        TransitionValues values = new TransitionValues();
-                        values.view = view;
-                        if (start) {
-                            captureStartValues(values);
-                        } else {
-                            captureEndValues(values);
-                        }
-                        if (start) {
-                            mStartValues.viewValues.put(view, values);
-                            if (id >= 0) {
-                                mStartValues.idValues.put(id, values);
-                            }
-                        } else {
-                            mEndValues.viewValues.put(view, values);
-                            if (id >= 0) {
-                                mEndValues.idValues.put(id, values);
-                            }
-                        }
-                    }
-                }
-            }
-            if (mTargets.size() > 0) {
-                for (int i = 0; i < mTargets.size(); ++i) {
-                    View view = mTargets.get(i);
-                    if (view != null) {
-                        TransitionValues values = new TransitionValues();
-                        values.view = view;
-                        if (start) {
-                            captureStartValues(values);
-                        } else {
-                            captureEndValues(values);
-                        }
-                        if (start) {
-                            mStartValues.viewValues.put(view, values);
-                        } else {
-                            mEndValues.viewValues.put(view, values);
-                        }
-                    }
-                }
-            }
-        } else {
-            captureHierarchy(sceneRoot, start);
-        }
-    }
-
-    /**
-     * Clear valuesMaps for specified start/end state
-     *
-     * @param start true if the start values should be cleared, false otherwise
-     */
-    void clearValues(boolean start) {
-        if (start) {
-            mStartValues.viewValues.clear();
-            mStartValues.idValues.clear();
-            mStartValues.itemIdValues.clear();
-        } else {
-            mEndValues.viewValues.clear();
-            mEndValues.idValues.clear();
-            mEndValues.itemIdValues.clear();
-        }
-    }
-
-    /**
-     * Recursive method which captures values for an entire view hierarchy,
-     * starting at some root view. Transitions without targetIDs will use this
-     * method to capture values for all possible views.
-     *
-     * @param view  The view for which to capture values. Children of this View
-     *              will also be captured, recursively down to the leaf nodes.
-     * @param start true if values are being captured in the start scene, false
-     *              otherwise.
-     */
-    private void captureHierarchy(View view, boolean start) {
-        if (view == null) {
-            return;
-        }
-        boolean isListViewItem = false;
-        if (view.getParent() instanceof ListView) {
-            isListViewItem = true;
-        }
-        if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) {
-            // ignore listview children unless we can track them with stable IDs
-            return;
-        }
-        int id = View.NO_ID;
-        long itemId = View.NO_ID;
-        if (!isListViewItem) {
-            id = view.getId();
-        } else {
-            ListView listview = (ListView) view.getParent();
-            int position = listview.getPositionForView(view);
-            itemId = listview.getItemIdAtPosition(position);
-//            view.setHasTransientState(true);
-        }
-        if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) {
-            return;
-        }
-        if (mTargetExcludes != null && mTargetExcludes.contains(view)) {
-            return;
-        }
-        if (mTargetTypeExcludes != null && view != null) {
-            int numTypes = mTargetTypeExcludes.size();
-            for (int i = 0; i < numTypes; ++i) {
-                if (mTargetTypeExcludes.get(i).isInstance(view)) {
-                    return;
-                }
-            }
-        }
-        TransitionValues values = new TransitionValues();
-        values.view = view;
-        if (start) {
-            captureStartValues(values);
-        } else {
-            captureEndValues(values);
-        }
-        if (start) {
-            if (!isListViewItem) {
-                mStartValues.viewValues.put(view, values);
-                if (id >= 0) {
-                    mStartValues.idValues.put((int) id, values);
-                }
-            } else {
-                mStartValues.itemIdValues.put(itemId, values);
-            }
-        } else {
-            if (!isListViewItem) {
-                mEndValues.viewValues.put(view, values);
-                if (id >= 0) {
-                    mEndValues.idValues.put((int) id, values);
-                }
-            } else {
-                mEndValues.itemIdValues.put(itemId, values);
-            }
-        }
-        if (view instanceof ViewGroup) {
-            // Don't traverse child hierarchy if there are any child-excludes on this view
-            if (mTargetIdChildExcludes != null && mTargetIdChildExcludes.contains(id)) {
-                return;
-            }
-            if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) {
-                return;
-            }
-            if (mTargetTypeChildExcludes != null && view != null) {
-                int numTypes = mTargetTypeChildExcludes.size();
-                for (int i = 0; i < numTypes; ++i) {
-                    if (mTargetTypeChildExcludes.get(i).isInstance(view)) {
-                        return;
-                    }
-                }
-            }
-            ViewGroup parent = (ViewGroup) view;
-            for (int i = 0; i < parent.getChildCount(); ++i) {
-                captureHierarchy(parent.getChildAt(i), start);
-            }
-        }
-    }
-
-    public TransitionValues getTransitionValues(View view, boolean start) {
-        if (mParent != null) {
-            return mParent.getTransitionValues(view, start);
-        }
-        TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues;
-        TransitionValues values = valuesMaps.viewValues.get(view);
-        if (values == null) {
-            int id = view.getId();
-            if (id >= 0) {
-                values = valuesMaps.idValues.get(id);
-            }
-            if (values == null && view.getParent() instanceof ListView) {
-                ListView listview = (ListView) view.getParent();
-                int position = listview.getPositionForView(view);
-                long itemId = listview.getItemIdAtPosition(position);
-                values = valuesMaps.itemIdValues.get(itemId);
-            }
-            // TODO: Doesn't handle the case where a view was parented to a
-            // ListView (with an itemId), but no longer is
-        }
-        return values;
-    }
-
-    /**
-     * Pauses this transition, sending out calls to {@link
-     * TransitionListener#onTransitionPause(TransitionPort)} to all listeners
-     * and pausing all running animators started by this transition.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void pause(View sceneRoot) {
-        if (!mEnded) {
-            ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
-            int numOldAnims = runningAnimators.size();
-            WindowIdPort windowId = WindowIdPort.getWindowId(sceneRoot);
-            for (int i = numOldAnims - 1; i >= 0; i--) {
-                AnimationInfo info = runningAnimators.valueAt(i);
-                if (info.view != null && windowId.equals(info.windowId)) {
-                    Animator anim = runningAnimators.keyAt(i);
-                    anim.cancel(); // pause() is API Level 19
-                }
-            }
-            if (mListeners != null && mListeners.size() > 0) {
-                ArrayList<TransitionListener> tmpListeners =
-                        (ArrayList<TransitionListener>) mListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onTransitionPause(this);
-                }
-            }
-            mPaused = true;
-        }
-    }
-
-    /**
-     * Resumes this transition, sending out calls to {@link
-     * TransitionListener#onTransitionPause(TransitionPort)} to all listeners
-     * and pausing all running animators started by this transition.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void resume(View sceneRoot) {
-        if (mPaused) {
-            if (!mEnded) {
-                ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
-                int numOldAnims = runningAnimators.size();
-                WindowIdPort windowId = WindowIdPort.getWindowId(sceneRoot);
-                for (int i = numOldAnims - 1; i >= 0; i--) {
-                    AnimationInfo info = runningAnimators.valueAt(i);
-                    if (info.view != null && windowId.equals(info.windowId)) {
-                        Animator anim = runningAnimators.keyAt(i);
-                        anim.end(); // resume() is API Level 19
-                    }
-                }
-                if (mListeners != null && mListeners.size() > 0) {
-                    ArrayList<TransitionListener> tmpListeners =
-                            (ArrayList<TransitionListener>) mListeners.clone();
-                    int numListeners = tmpListeners.size();
-                    for (int i = 0; i < numListeners; ++i) {
-                        tmpListeners.get(i).onTransitionResume(this);
-                    }
-                }
-            }
-            mPaused = false;
-        }
-    }
-
-    /**
-     * Called by TransitionManager to play the transition. This calls
-     * createAnimators() to set things up and create all of the animations and then
-     * runAnimations() to actually start the animations.
-     */
-    void playTransition(ViewGroup sceneRoot) {
-        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
-        int numOldAnims = runningAnimators.size();
-        for (int i = numOldAnims - 1; i >= 0; i--) {
-            Animator anim = runningAnimators.keyAt(i);
-            if (anim != null) {
-                AnimationInfo oldInfo = runningAnimators.get(anim);
-                if (oldInfo != null && oldInfo.view != null &&
-                        oldInfo.view.getContext() == sceneRoot.getContext()) {
-                    boolean cancel = false;
-                    TransitionValues oldValues = oldInfo.values;
-                    View oldView = oldInfo.view;
-                    TransitionValues newValues = mEndValues.viewValues != null ?
-                            mEndValues.viewValues.get(oldView) : null;
-                    if (newValues == null) {
-                        newValues = mEndValues.idValues.get(oldView.getId());
-                    }
-                    if (oldValues != null) {
-                        // if oldValues null, then transition didn't care to stash values,
-                        // and won't get canceled
-                        if (newValues != null) {
-                            for (String key : oldValues.values.keySet()) {
-                                Object oldValue = oldValues.values.get(key);
-                                Object newValue = newValues.values.get(key);
-                                if (oldValue != null && newValue != null &&
-                                        !oldValue.equals(newValue)) {
-                                    cancel = true;
-                                    if (DBG) {
-                                        Log.d(LOG_TAG, "Transition.playTransition: " +
-                                                "oldValue != newValue for " + key +
-                                                ": old, new = " + oldValue + ", " + newValue);
-                                    }
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                    if (cancel) {
-                        if (anim.isRunning() || anim.isStarted()) {
-                            if (DBG) {
-                                Log.d(LOG_TAG, "Canceling anim " + anim);
-                            }
-                            anim.cancel();
-                        } else {
-                            if (DBG) {
-                                Log.d(LOG_TAG, "removing anim from info list: " + anim);
-                            }
-                            runningAnimators.remove(anim);
-                        }
-                    }
-                }
-            }
-        }
-
-        createAnimators(sceneRoot, mStartValues, mEndValues);
-        runAnimators();
-    }
-
-    /**
-     * This is a utility method used by subclasses to handle standard parts of
-     * setting up and running an Animator: it sets the {@link #getDuration()
-     * duration} and the {@link #getStartDelay() startDelay}, starts the
-     * animation, and, when the animator ends, calls {@link #end()}.
-     *
-     * @param animator The Animator to be run during this transition.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void animate(Animator animator) {
-        // TODO: maybe pass auto-end as a boolean parameter?
-        if (animator == null) {
-            end();
-        } else {
-            if (getDuration() >= 0) {
-                animator.setDuration(getDuration());
-            }
-            if (getStartDelay() >= 0) {
-                animator.setStartDelay(getStartDelay());
-            }
-            if (getInterpolator() != null) {
-                animator.setInterpolator(getInterpolator());
-            }
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    end();
-                    animation.removeListener(this);
-                }
-            });
-            animator.start();
-        }
-    }
-
-    /**
-     * This method is called automatically by the transition and
-     * TransitionSet classes prior to a Transition subclass starting;
-     * subclasses should not need to call it directly.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void start() {
-        if (mNumInstances == 0) {
-            if (mListeners != null && mListeners.size() > 0) {
-                ArrayList<TransitionListener> tmpListeners =
-                        (ArrayList<TransitionListener>) mListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onTransitionStart(this);
-                }
-            }
-            mEnded = false;
-        }
-        mNumInstances++;
-    }
-
-    /**
-     * This method is called automatically by the Transition and
-     * TransitionSet classes when a transition finishes, either because
-     * a transition did nothing (returned a null Animator from
-     * {@link TransitionPort#createAnimator(ViewGroup, TransitionValues,
-     * TransitionValues)}) or because the transition returned a valid
-     * Animator and end() was called in the onAnimationEnd()
-     * callback of the AnimatorListener.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void end() {
-        --mNumInstances;
-        if (mNumInstances == 0) {
-            if (mListeners != null && mListeners.size() > 0) {
-                ArrayList<TransitionListener> tmpListeners =
-                        (ArrayList<TransitionListener>) mListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onTransitionEnd(this);
-                }
-            }
-            for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) {
-                TransitionValues tv = mStartValues.itemIdValues.valueAt(i);
-                View v = tv.view;
-//                if (v.hasTransientState()) {
-//                    v.setHasTransientState(false);
-//                }
-            }
-            for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) {
-                TransitionValues tv = mEndValues.itemIdValues.valueAt(i);
-                View v = tv.view;
-//                if (v.hasTransientState()) {
-//                    v.setHasTransientState(false);
-//                }
-            }
-            mEnded = true;
-        }
-    }
-
-    /**
-     * This method cancels a transition that is currently running.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void cancel() {
-        int numAnimators = mCurrentAnimators.size();
-        for (int i = numAnimators - 1; i >= 0; i--) {
-            Animator animator = mCurrentAnimators.get(i);
-            animator.cancel();
-        }
-        if (mListeners != null && mListeners.size() > 0) {
-            ArrayList<TransitionListener> tmpListeners =
-                    (ArrayList<TransitionListener>) mListeners.clone();
-            int numListeners = tmpListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                tmpListeners.get(i).onTransitionCancel(this);
-            }
-        }
-    }
-
-    /**
-     * Adds a listener to the set of listeners that are sent events through the
-     * life of an animation, such as start, repeat, and end.
-     *
-     * @param listener the listener to be added to the current set of listeners
-     *                 for this animation.
-     * @return This transition object.
-     */
-    public TransitionPort addListener(TransitionListener listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<>();
-        }
-        mListeners.add(listener);
-        return this;
-    }
-
-    public TransitionPort removeListener(TransitionListener listener) {
-        if (mListeners == null) {
-            return this;
-        }
-        mListeners.remove(listener);
-        if (mListeners.size() == 0) {
-            mListeners = null;
-        }
-        return this;
-    }
-
-    TransitionPort setSceneRoot(ViewGroup sceneRoot) {
-        mSceneRoot = sceneRoot;
-        return this;
-    }
-
-    void setCanRemoveViews(boolean canRemoveViews) {
-        mCanRemoveViews = canRemoveViews;
-    }
-
-    @Override
-    public String toString() {
-        return toString("");
-    }
-
-    @Override
-    public TransitionPort clone() {
-        TransitionPort clone = null;
-        try {
-            clone = (TransitionPort) super.clone();
-            clone.mAnimators = new ArrayList<Animator>();
-            clone.mStartValues = new TransitionValuesMaps();
-            clone.mEndValues = new TransitionValuesMaps();
-        } catch (CloneNotSupportedException e) {
-        }
-
-        return clone;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    String toString(String indent) {
-        String result = indent + getClass().getSimpleName() + "@" +
-                Integer.toHexString(hashCode()) + ": ";
-        if (mDuration != -1) {
-            result += "dur(" + mDuration + ") ";
-        }
-        if (mStartDelay != -1) {
-            result += "dly(" + mStartDelay + ") ";
-        }
-        if (mInterpolator != null) {
-            result += "interp(" + mInterpolator + ") ";
-        }
-        if (mTargetIds.size() > 0 || mTargets.size() > 0) {
-            result += "tgts(";
-            if (mTargetIds.size() > 0) {
-                for (int i = 0; i < mTargetIds.size(); ++i) {
-                    if (i > 0) {
-                        result += ", ";
-                    }
-                    result += mTargetIds.get(i);
-                }
-            }
-            if (mTargets.size() > 0) {
-                for (int i = 0; i < mTargets.size(); ++i) {
-                    if (i > 0) {
-                        result += ", ";
-                    }
-                    result += mTargets.get(i);
-                }
-            }
-            result += ")";
-        }
-        return result;
-    }
-
-    public interface TransitionListener {
-
-        /**
-         * Notification about the start of the transition.
-         *
-         * @param transition The started transition.
-         */
-        void onTransitionStart(TransitionPort transition);
-
-        /**
-         * Notification about the end of the transition. Canceled transitions
-         * will always notify listeners of both the cancellation and end
-         * events. That is, {@link #onTransitionEnd(TransitionPort)} is always called,
-         * regardless of whether the transition was canceled or played
-         * through to completion.
-         *
-         * @param transition The transition which reached its end.
-         */
-        void onTransitionEnd(TransitionPort transition);
-
-        /**
-         * Notification about the cancellation of the transition.
-         * Note that cancel may be called by a parent {@link TransitionSetPort} on
-         * a child transition which has not yet started. This allows the child
-         * transition to restore state on target objects which was set at
-         * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)
-         * createAnimator()} time.
-         *
-         * @param transition The transition which was canceled.
-         */
-        void onTransitionCancel(TransitionPort transition);
-
-        /**
-         * Notification when a transition is paused.
-         * Note that createAnimator() may be called by a parent {@link TransitionSetPort} on
-         * a child transition which has not yet started. This allows the child
-         * transition to restore state on target objects which was set at
-         * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)
-         * createAnimator()} time.
-         *
-         * @param transition The transition which was paused.
-         */
-        void onTransitionPause(TransitionPort transition);
-
-        /**
-         * Notification when a transition is resumed.
-         * Note that resume() may be called by a parent {@link TransitionSetPort} on
-         * a child transition which has not yet started. This allows the child
-         * transition to restore state which may have changed in an earlier call
-         * to {@link #onTransitionPause(TransitionPort)}.
-         *
-         * @param transition The transition which was resumed.
-         */
-        void onTransitionResume(TransitionPort transition);
-    }
-
-    /**
-     * Utility adapter class to avoid having to override all three methods
-     * whenever someone just wants to listen for a single event.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class TransitionListenerAdapter implements TransitionListener {
-
-        @Override
-        public void onTransitionStart(TransitionPort transition) {
-        }
-
-        @Override
-        public void onTransitionEnd(TransitionPort transition) {
-        }
-
-        @Override
-        public void onTransitionCancel(TransitionPort transition) {
-        }
-
-        @Override
-        public void onTransitionPause(TransitionPort transition) {
-        }
-
-        @Override
-        public void onTransitionResume(TransitionPort transition) {
-        }
-    }
-
-    /**
-     * Holds information about each animator used when a new transition starts
-     * while other transitions are still running to determine whether a running
-     * animation should be canceled or a new animation noop'd. The structure holds
-     * information about the state that an animation is going to, to be compared to
-     * end state of a new animation.
-     */
-    private static class AnimationInfo {
-
-        View view;
-
-        String name;
-
-        TransitionValues values;
-
-        WindowIdPort windowId;
-
-        AnimationInfo(View view, String name, WindowIdPort windowId, TransitionValues values) {
-            this.view = view;
-            this.name = name;
-            this.values = values;
-            this.windowId = windowId;
-        }
-    }
-
-    /**
-     * Utility class for managing typed ArrayLists efficiently. In particular, this
-     * can be useful for lists that we don't expect to be used often (eg, the exclude
-     * lists), so we'd like to keep them nulled out by default. This causes the code to
-     * become tedious, with constant null checks, code to allocate when necessary,
-     * and code to null out the reference when the list is empty. This class encapsulates
-     * all of that functionality into simple add()/remove() methods which perform the
-     * necessary checks, allocation/null-out as appropriate, and return the
-     * resulting list.
-     */
-    private static class ArrayListManager {
-
-        /**
-         * Add the specified item to the list, returning the resulting list.
-         * The returned list can either the be same list passed in or, if that
-         * list was null, the new list that was created.
-         *
-         * Note that the list holds unique items; if the item already exists in the
-         * list, the list is not modified.
-         */
-        static <T> ArrayList<T> add(ArrayList<T> list, T item) {
-            if (list == null) {
-                list = new ArrayList<T>();
-            }
-            if (!list.contains(item)) {
-                list.add(item);
-            }
-            return list;
-        }
-
-        /**
-         * Remove the specified item from the list, returning the resulting list.
-         * The returned list can either the be same list passed in or, if that
-         * list becomes empty as a result of the remove(), the new list was created.
-         */
-        static <T> ArrayList<T> remove(ArrayList<T> list, T item) {
-            if (list != null) {
-                list.remove(item);
-                if (list.isEmpty()) {
-                    list = null;
-                }
-            }
-            return list;
-        }
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionSetIcs.java b/transition/ics/android/support/transition/TransitionSetIcs.java
deleted file mode 100644
index b3fcd8f..0000000
--- a/transition/ics/android/support/transition/TransitionSetIcs.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionSetIcs extends TransitionIcs implements TransitionSetImpl {
-
-    private TransitionSetPort mTransitionSet;
-
-    public TransitionSetIcs(TransitionInterface transition) {
-        mTransitionSet = new TransitionSetPort();
-        init(transition, mTransitionSet);
-    }
-
-    @Override
-    public int getOrdering() {
-        return mTransitionSet.getOrdering();
-    }
-
-    @Override
-    public TransitionSetIcs setOrdering(int ordering) {
-        mTransitionSet.setOrdering(ordering);
-        return this;
-    }
-
-    @Override
-    public TransitionSetIcs addTransition(TransitionImpl transition) {
-        mTransitionSet.addTransition(((TransitionIcs) transition).mTransition);
-        return this;
-    }
-
-    @Override
-    public TransitionSetIcs removeTransition(TransitionImpl transition) {
-        mTransitionSet.removeTransition(((TransitionIcs) transition).mTransition);
-        return this;
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionSetPort.java b/transition/ics/android/support/transition/TransitionSetPort.java
deleted file mode 100644
index d88e046..0000000
--- a/transition/ics/android/support/transition/TransitionSetPort.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.util.AndroidRuntimeException;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionSetPort extends TransitionPort {
-
-    /**
-     * A flag used to indicate that the child transitions of this set
-     * should all start at the same time.
-     */
-    public static final int ORDERING_TOGETHER = 0;
-
-    /**
-     * A flag used to indicate that the child transitions of this set should
-     * play in sequence; when one child transition ends, the next child
-     * transition begins. Note that a transition does not end until all
-     * instances of it (which are playing on all applicable targets of the
-     * transition) end.
-     */
-    public static final int ORDERING_SEQUENTIAL = 1;
-
-    ArrayList<TransitionPort> mTransitions = new ArrayList<TransitionPort>();
-
-    int mCurrentListeners;
-
-    boolean mStarted = false;
-
-    private boolean mPlayTogether = true;
-
-    public TransitionSetPort() {
-    }
-
-    public int getOrdering() {
-        return mPlayTogether ? ORDERING_TOGETHER : ORDERING_SEQUENTIAL;
-    }
-
-    public TransitionSetPort setOrdering(int ordering) {
-        switch (ordering) {
-            case ORDERING_SEQUENTIAL:
-                mPlayTogether = false;
-                break;
-            case ORDERING_TOGETHER:
-                mPlayTogether = true;
-                break;
-            default:
-                throw new AndroidRuntimeException("Invalid parameter for TransitionSet " +
-                        "ordering: " + ordering);
-        }
-        return this;
-    }
-
-    public TransitionSetPort addTransition(TransitionPort transition) {
-        if (transition != null) {
-            mTransitions.add(transition);
-            transition.mParent = this;
-            if (mDuration >= 0) {
-                transition.setDuration(mDuration);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Setting a non-negative duration on a TransitionSet causes all of the child
-     * transitions (current and future) to inherit this duration.
-     *
-     * @param duration The length of the animation, in milliseconds.
-     * @return This transitionSet object.
-     */
-    @Override
-    public TransitionSetPort setDuration(long duration) {
-        super.setDuration(duration);
-        if (mDuration >= 0) {
-            int numTransitions = mTransitions.size();
-            for (int i = 0; i < numTransitions; ++i) {
-                mTransitions.get(i).setDuration(duration);
-            }
-        }
-        return this;
-    }
-
-    @Override
-    public TransitionSetPort setStartDelay(long startDelay) {
-        return (TransitionSetPort) super.setStartDelay(startDelay);
-    }
-
-    @Override
-    public TransitionSetPort setInterpolator(TimeInterpolator interpolator) {
-        return (TransitionSetPort) super.setInterpolator(interpolator);
-    }
-
-    @Override
-    public TransitionSetPort addTarget(View target) {
-        return (TransitionSetPort) super.addTarget(target);
-    }
-
-    @Override
-    public TransitionSetPort addTarget(int targetId) {
-        return (TransitionSetPort) super.addTarget(targetId);
-    }
-
-    @Override
-    public TransitionSetPort addListener(TransitionListener listener) {
-        return (TransitionSetPort) super.addListener(listener);
-    }
-
-    @Override
-    public TransitionSetPort removeTarget(int targetId) {
-        return (TransitionSetPort) super.removeTarget(targetId);
-    }
-
-    @Override
-    public TransitionSetPort removeTarget(View target) {
-        return (TransitionSetPort) super.removeTarget(target);
-    }
-
-    @Override
-    public TransitionSetPort removeListener(TransitionListener listener) {
-        return (TransitionSetPort) super.removeListener(listener);
-    }
-
-    public TransitionSetPort removeTransition(TransitionPort transition) {
-        mTransitions.remove(transition);
-        transition.mParent = null;
-        return this;
-    }
-
-    /**
-     * Sets up listeners for each of the child transitions. This is used to
-     * determine when this transition set is finished (all child transitions
-     * must finish first).
-     */
-    private void setupStartEndListeners() {
-        TransitionSetListener listener = new TransitionSetListener(this);
-        for (TransitionPort childTransition : mTransitions) {
-            childTransition.addListener(listener);
-        }
-        mCurrentListeners = mTransitions.size();
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
-            TransitionValuesMaps endValues) {
-        for (TransitionPort childTransition : mTransitions) {
-            childTransition.createAnimators(sceneRoot, startValues, endValues);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected void runAnimators() {
-        if (mTransitions.isEmpty()) {
-            start();
-            end();
-            return;
-        }
-        setupStartEndListeners();
-        if (!mPlayTogether) {
-            // Setup sequence with listeners
-            // TODO: Need to add listeners in such a way that we can remove them later if canceled
-            for (int i = 1; i < mTransitions.size(); ++i) {
-                TransitionPort previousTransition = mTransitions.get(i - 1);
-                final TransitionPort nextTransition = mTransitions.get(i);
-                previousTransition.addListener(new TransitionListenerAdapter() {
-                    @Override
-                    public void onTransitionEnd(TransitionPort transition) {
-                        nextTransition.runAnimators();
-                        transition.removeListener(this);
-                    }
-                });
-            }
-            TransitionPort firstTransition = mTransitions.get(0);
-            if (firstTransition != null) {
-                firstTransition.runAnimators();
-            }
-        } else {
-            for (TransitionPort childTransition : mTransitions) {
-                childTransition.runAnimators();
-            }
-        }
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        int targetId = transitionValues.view.getId();
-        if (isValidTarget(transitionValues.view, targetId)) {
-            for (TransitionPort childTransition : mTransitions) {
-                if (childTransition.isValidTarget(transitionValues.view, targetId)) {
-                    childTransition.captureStartValues(transitionValues);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        int targetId = transitionValues.view.getId();
-        if (isValidTarget(transitionValues.view, targetId)) {
-            for (TransitionPort childTransition : mTransitions) {
-                if (childTransition.isValidTarget(transitionValues.view, targetId)) {
-                    childTransition.captureEndValues(transitionValues);
-                }
-            }
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void pause(View sceneRoot) {
-        super.pause(sceneRoot);
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            mTransitions.get(i).pause(sceneRoot);
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void resume(View sceneRoot) {
-        super.resume(sceneRoot);
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            mTransitions.get(i).resume(sceneRoot);
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected void cancel() {
-        super.cancel();
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            mTransitions.get(i).cancel();
-        }
-    }
-
-    @Override
-    TransitionSetPort setSceneRoot(ViewGroup sceneRoot) {
-        super.setSceneRoot(sceneRoot);
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            mTransitions.get(i).setSceneRoot(sceneRoot);
-        }
-        return (TransitionSetPort) this;
-    }
-
-    @Override
-    void setCanRemoveViews(boolean canRemoveViews) {
-        super.setCanRemoveViews(canRemoveViews);
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            mTransitions.get(i).setCanRemoveViews(canRemoveViews);
-        }
-    }
-
-    @Override
-    String toString(String indent) {
-        String result = super.toString(indent);
-        for (int i = 0; i < mTransitions.size(); ++i) {
-            result += "\n" + mTransitions.get(i).toString(indent + "  ");
-        }
-        return result;
-    }
-
-    @Override
-    public TransitionSetPort clone() {
-        TransitionSetPort clone = (TransitionSetPort) super.clone();
-        clone.mTransitions = new ArrayList<TransitionPort>();
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            clone.addTransition((TransitionPort) mTransitions.get(i).clone());
-        }
-        return clone;
-    }
-
-    /**
-     * This listener is used to detect when all child transitions are done, at
-     * which point this transition set is also done.
-     */
-    static class TransitionSetListener extends TransitionListenerAdapter {
-
-        TransitionSetPort mTransitionSet;
-
-        TransitionSetListener(TransitionSetPort transitionSet) {
-            mTransitionSet = transitionSet;
-        }
-
-        @Override
-        public void onTransitionStart(TransitionPort transition) {
-            if (!mTransitionSet.mStarted) {
-                mTransitionSet.start();
-                mTransitionSet.mStarted = true;
-            }
-        }
-
-        @Override
-        public void onTransitionEnd(TransitionPort transition) {
-            --mTransitionSet.mCurrentListeners;
-            if (mTransitionSet.mCurrentListeners == 0) {
-                // All child trans
-                mTransitionSet.mStarted = false;
-                mTransitionSet.end();
-            }
-            transition.removeListener(this);
-        }
-    }
-
-}
diff --git a/transition/ics/android/support/transition/TransitionValuesMaps.java b/transition/ics/android/support/transition/TransitionValuesMaps.java
deleted file mode 100644
index ddf05da..0000000
--- a/transition/ics/android/support/transition/TransitionValuesMaps.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.util.LongSparseArray;
-import android.util.SparseArray;
-import android.view.View;
-
-@RequiresApi(14)
-@TargetApi(14)
-class TransitionValuesMaps {
-
-    public ArrayMap<View, TransitionValues> viewValues = new ArrayMap<>();
-
-    public SparseArray<TransitionValues> idValues = new SparseArray<>();
-
-    public LongSparseArray<TransitionValues> itemIdValues = new LongSparseArray<>();
-
-}
diff --git a/transition/ics/android/support/transition/ViewGroupOverlay.java b/transition/ics/android/support/transition/ViewGroupOverlay.java
deleted file mode 100644
index da91466..0000000
--- a/transition/ics/android/support/transition/ViewGroupOverlay.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewGroupOverlay extends ViewOverlay {
-
-    ViewGroupOverlay(Context context, ViewGroup hostView, View requestingView) {
-        super(context, hostView, requestingView);
-    }
-
-    public static ViewGroupOverlay createFrom(ViewGroup viewGroup) {
-        return (ViewGroupOverlay) ViewOverlay.createFrom(viewGroup);
-    }
-
-    /**
-     * Adds a View to the overlay. The bounds of the added view should be
-     * relative to the host view. Any view added to the overlay should be
-     * removed when it is no longer needed or no longer visible.
-     *
-     * <p>Views in the overlay are visual-only; they do not receive input
-     * events and do not participate in focus traversal. Overlay views
-     * are intended to be transient, such as might be needed by a temporary
-     * animation effect.</p>
-     *
-     * <p>If the view has a parent, the view will be removed from that parent
-     * before being added to the overlay. Also, if that parent is attached
-     * in the current view hierarchy, the view will be repositioned
-     * such that it is in the same relative location inside the activity. For
-     * example, if the view's current parent lies 100 pixels to the right
-     * and 200 pixels down from the origin of the overlay's
-     * host view, then the view will be offset by (100, 200).</p>
-     *
-     * @param view The View to be added to the overlay. The added view will be
-     *             drawn when the overlay is drawn.
-     * @see #remove(View)
-     * @see android.view.ViewOverlay#add(Drawable)
-     */
-    public void add(View view) {
-        mOverlayViewGroup.add(view);
-    }
-
-    /**
-     * Removes the specified View from the overlay.
-     *
-     * @param view The View to be removed from the overlay.
-     * @see #add(View)
-     * @see android.view.ViewOverlay#remove(Drawable)
-     */
-    public void remove(View view) {
-        mOverlayViewGroup.remove(view);
-    }
-}
diff --git a/transition/ics/android/support/transition/ViewOverlay.java b/transition/ics/android/support/transition/ViewOverlay.java
deleted file mode 100644
index 8b36850..0000000
--- a/transition/ics/android/support/transition/ViewOverlay.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.R;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-
-@RequiresApi(14)
-@TargetApi(14)
-class ViewOverlay {
-
-    /**
-     * The actual container for the drawables (and views, if it's a ViewGroupOverlay).
-     * All of the management and rendering details for the overlay are handled in
-     * OverlayViewGroup.
-     */
-    protected OverlayViewGroup mOverlayViewGroup;
-
-    ViewOverlay(Context context, ViewGroup hostView, View requestingView) {
-        mOverlayViewGroup = new OverlayViewGroup(context, hostView, requestingView, this);
-    }
-
-    static ViewGroup getContentView(View view) {
-        View parent = view;
-        while (parent != null) {
-            if (parent.getId() == R.id.content && parent instanceof ViewGroup) {
-                return (ViewGroup) parent;
-            }
-            if (parent.getParent() instanceof ViewGroup) {
-                parent = (ViewGroup) parent.getParent();
-            }
-        }
-        return null;
-    }
-
-    public static ViewOverlay createFrom(View view) {
-        ViewGroup contentView = getContentView(view);
-        if (contentView != null) {
-            final int numChildren = contentView.getChildCount();
-            for (int i = 0; i < numChildren; ++i) {
-                View child = contentView.getChildAt(i);
-                if (child instanceof OverlayViewGroup) {
-                    return ((OverlayViewGroup) child).mViewOverlay;
-                }
-            }
-            return new ViewGroupOverlay(contentView.getContext(), contentView, view);
-        }
-        return null;
-    }
-
-    /**
-     * Used internally by View and ViewGroup to handle drawing and invalidation
-     * of the overlay
-     */
-    ViewGroup getOverlayView() {
-        return mOverlayViewGroup;
-    }
-
-    /**
-     * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
-     * the host view. Any drawable added to the overlay should be removed when it is no longer
-     * needed or no longer visible.
-     *
-     * @param drawable The Drawable to be added to the overlay. This drawable will be
-     *                 drawn when the view redraws its overlay.
-     * @see #remove(Drawable)
-     */
-    public void add(Drawable drawable) {
-        mOverlayViewGroup.add(drawable);
-    }
-
-    /**
-     * Removes the specified Drawable from the overlay.
-     *
-     * @param drawable The Drawable to be removed from the overlay.
-     * @see #add(Drawable)
-     */
-    public void remove(Drawable drawable) {
-        mOverlayViewGroup.remove(drawable);
-    }
-
-    /**
-     * Removes all content from the overlay.
-     */
-    public void clear() {
-        mOverlayViewGroup.clear();
-    }
-
-    boolean isEmpty() {
-        return mOverlayViewGroup.isEmpty();
-    }
-
-    /**
-     * OverlayViewGroup is a container that View and ViewGroup use to host
-     * drawables and views added to their overlays  ({@link ViewOverlay} and
-     * {@link ViewGroupOverlay}, respectively). Drawables are added to the overlay
-     * via the add/remove methods in ViewOverlay, Views are added/removed via
-     * ViewGroupOverlay. These drawable and view objects are
-     * drawn whenever the view itself is drawn; first the view draws its own
-     * content (and children, if it is a ViewGroup), then it draws its overlay
-     * (if it has one).
-     *
-     * <p>Besides managing and drawing the list of drawables, this class serves
-     * two purposes:
-     * (1) it noops layout calls because children are absolutely positioned and
-     * (2) it forwards all invalidation calls to its host view. The invalidation
-     * redirect is necessary because the overlay is not a child of the host view
-     * and invalidation cannot therefore follow the normal path up through the
-     * parent hierarchy.</p>
-     *
-     * @see View#getOverlay()
-     * @see ViewGroup#getOverlay()
-     */
-    static class OverlayViewGroup extends ViewGroup {
-
-        static Method sInvalidateChildInParentFastMethod;
-
-        static {
-            try {
-                sInvalidateChildInParentFastMethod = ViewGroup.class.getDeclaredMethod(
-                        "invalidateChildInParentFast", int.class, int.class, Rect.class);
-            } catch (NoSuchMethodException e) {
-            }
-
-        }
-
-        /**
-         * The View for which this is an overlay. Invalidations of the overlay are redirected to
-         * this host view.
-         */
-        ViewGroup mHostView;
-        View mRequestingView;
-        /**
-         * The set of drawables to draw when the overlay is rendered.
-         */
-        ArrayList<Drawable> mDrawables = null;
-        /**
-         * Reference to the hosting overlay object
-         */
-        ViewOverlay mViewOverlay;
-
-        OverlayViewGroup(Context context, ViewGroup hostView, View requestingView,
-                ViewOverlay viewOverlay) {
-            super(context);
-            mHostView = hostView;
-            mRequestingView = requestingView;
-            setRight(hostView.getWidth());
-            setBottom(hostView.getHeight());
-            ((ViewGroup) hostView).addView(this);
-            mViewOverlay = viewOverlay;
-        }
-
-        @Override
-        public boolean dispatchTouchEvent(MotionEvent ev) {
-            // Intercept and noop all touch events - overlays do not allow touch events
-            return false;
-        }
-
-        public void add(Drawable drawable) {
-            if (mDrawables == null) {
-
-                mDrawables = new ArrayList<Drawable>();
-            }
-            if (!mDrawables.contains(drawable)) {
-                // Make each drawable unique in the overlay; can't add it more than once
-                mDrawables.add(drawable);
-                invalidate(drawable.getBounds());
-                drawable.setCallback(this);
-            }
-        }
-
-        public void remove(Drawable drawable) {
-            if (mDrawables != null) {
-                mDrawables.remove(drawable);
-                invalidate(drawable.getBounds());
-                drawable.setCallback(null);
-            }
-        }
-
-        @Override
-        protected boolean verifyDrawable(Drawable who) {
-            return super.verifyDrawable(who) || (mDrawables != null && mDrawables.contains(who));
-        }
-
-        public void add(View child) {
-            if (child.getParent() instanceof ViewGroup) {
-                ViewGroup parent = (ViewGroup) child.getParent();
-                if (parent != mHostView && parent.getParent() != null) {// &&
-//                        parent.isAttachedToWindow()) {
-                    // Moving to different container; figure out how to position child such that
-                    // it is in the same location on the screen
-                    int[] parentLocation = new int[2];
-                    int[] hostViewLocation = new int[2];
-                    parent.getLocationOnScreen(parentLocation);
-                    mHostView.getLocationOnScreen(hostViewLocation);
-                    ViewCompat.offsetLeftAndRight(child, parentLocation[0] - hostViewLocation[0]);
-                    ViewCompat.offsetTopAndBottom(child, parentLocation[1] - hostViewLocation[1]);
-                }
-                parent.removeView(child);
-//                if (parent.getLayoutTransition() != null) {
-//                    // LayoutTransition will cause the child to delay removal - cancel it
-//                    parent.getLayoutTransition().cancel(LayoutTransition.DISAPPEARING);
-//                }
-                // fail-safe if view is still attached for any reason
-                if (child.getParent() != null) {
-                    parent.removeView(child);
-                }
-            }
-            super.addView(child, getChildCount() - 1);
-        }
-
-        public void remove(View view) {
-            super.removeView(view);
-            if (isEmpty()) {
-                mHostView.removeView(this);
-            }
-        }
-
-        public void clear() {
-            removeAllViews();
-            if (mDrawables != null) {
-                mDrawables.clear();
-            }
-        }
-
-        boolean isEmpty() {
-            if (getChildCount() == 0 &&
-                    (mDrawables == null || mDrawables.size() == 0)) {
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void invalidateDrawable(Drawable drawable) {
-            invalidate(drawable.getBounds());
-        }
-
-        @Override
-        protected void dispatchDraw(Canvas canvas) {
-            int[] contentViewLocation = new int[2];
-            int[] hostViewLocation = new int[2];
-            ViewGroup parent = (ViewGroup) getParent();
-            mHostView.getLocationOnScreen(contentViewLocation);
-            mRequestingView.getLocationOnScreen(hostViewLocation);
-            canvas.translate(hostViewLocation[0] - contentViewLocation[0],
-                    hostViewLocation[1] - contentViewLocation[1]);
-            canvas.clipRect(
-                    new Rect(0, 0, mRequestingView.getWidth(), mRequestingView.getHeight()));
-            super.dispatchDraw(canvas);
-            final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
-            for (int i = 0; i < numDrawables; ++i) {
-                mDrawables.get(i).draw(canvas);
-            }
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {
-            // Noop: children are positioned absolutely
-        }
-
-        /*
-         The following invalidation overrides exist for the purpose of redirecting invalidation to
-         the host view. The overlay is not parented to the host view (since a View cannot be a
-         parent), so the invalidation cannot proceed through the normal parent hierarchy.
-         There is a built-in assumption that the overlay exactly covers the host view, therefore
-         the invalidation rectangles received do not need to be adjusted when forwarded to
-         the host view.
-         */
-
-        private void getOffset(int[] offset) {
-            int[] contentViewLocation = new int[2];
-            int[] hostViewLocation = new int[2];
-            ViewGroup parent = (ViewGroup) getParent();
-            mHostView.getLocationOnScreen(contentViewLocation);
-            mRequestingView.getLocationOnScreen(hostViewLocation);
-            offset[0] = hostViewLocation[0] - contentViewLocation[0];
-            offset[1] = hostViewLocation[1] - contentViewLocation[1];
-        }
-
-        public void invalidateChildFast(View child, final Rect dirty) {
-            if (mHostView != null) {
-                // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
-                // using DisplayList properties and a dirty rect instead of causing a real
-                // invalidation of the host view
-                int left = child.getLeft();
-                int top = child.getTop();
-                int[] offset = new int[2];
-                getOffset(offset);
-                // TODO: implement transforms
-//                if (!child.getMatrix().isIdentity()) {
-//                    child.transformRect(dirty);
-//                }
-                dirty.offset(left + offset[0], top + offset[1]);
-                mHostView.invalidate(dirty);
-            }
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        protected ViewParent invalidateChildInParentFast(int left, int top, Rect dirty) {
-            if (mHostView instanceof ViewGroup && sInvalidateChildInParentFastMethod != null) {
-                try {
-                    int[] offset = new int[2];
-                    getOffset(offset);
-                    sInvalidateChildInParentFastMethod.invoke(mHostView, left, top, dirty);
-                } catch (IllegalAccessException e) {
-                    e.printStackTrace();
-                } catch (InvocationTargetException e) {
-                    e.printStackTrace();
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
-            if (mHostView != null) {
-                dirty.offset(location[0], location[1]);
-                if (mHostView instanceof ViewGroup) {
-                    location[0] = 0;
-                    location[1] = 0;
-                    int[] offset = new int[2];
-                    getOffset(offset);
-                    dirty.offset(offset[0], offset[1]);
-                    return super.invalidateChildInParent(location, dirty);
-//                    return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
-                } else {
-                    invalidate(dirty);
-                }
-            }
-            return null;
-        }
-
-        static class TouchInterceptor extends View {
-            TouchInterceptor(Context context) {
-                super(context);
-            }
-        }
-    }
-
-}
diff --git a/transition/ics/android/support/transition/VisibilityIcs.java b/transition/ics/android/support/transition/VisibilityIcs.java
deleted file mode 100644
index f2290cf..0000000
--- a/transition/ics/android/support/transition/VisibilityIcs.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(14)
-@TargetApi(14)
-class VisibilityIcs extends TransitionIcs implements VisibilityImpl {
-
-    @Override
-    public void init(TransitionInterface external, Object internal) {
-        mExternalTransition = external;
-        if (internal == null) {
-            mTransition = new VisibilityWrapper((VisibilityInterface) external);
-        } else {
-            mTransition = (VisibilityPort) internal;
-        }
-    }
-
-    @Override
-    public boolean isVisible(TransitionValues values) {
-        return ((VisibilityPort) mTransition).isVisible(values);
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        return ((VisibilityPort) mTransition).onAppear(sceneRoot, startValues, startVisibility,
-                endValues, endVisibility);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
-            int startVisibility, TransitionValues endValues, int endVisibility) {
-        return ((VisibilityPort) mTransition).onDisappear(sceneRoot, startValues, startVisibility,
-                endValues, endVisibility);
-    }
-
-    private static class VisibilityWrapper extends VisibilityPort {
-
-        private VisibilityInterface mVisibility;
-
-        VisibilityWrapper(VisibilityInterface visibility) {
-            mVisibility = visibility;
-        }
-
-        @Override
-        public void captureStartValues(TransitionValues transitionValues) {
-            mVisibility.captureStartValues(transitionValues);
-        }
-
-        @Override
-        public void captureEndValues(TransitionValues transitionValues) {
-            mVisibility.captureEndValues(transitionValues);
-        }
-
-        @Override
-        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-                TransitionValues endValues) {
-            return mVisibility.createAnimator(sceneRoot, startValues, endValues);
-        }
-
-        @Override
-        public boolean isVisible(TransitionValues values) {
-            return mVisibility.isVisible(values);
-        }
-
-        @Override
-        public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues,
-                int startVisibility,
-                TransitionValues endValues, int endVisibility) {
-            return mVisibility.onAppear(sceneRoot, startValues, startVisibility,
-                    endValues, endVisibility);
-        }
-
-        @Override
-        public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
-                int startVisibility, TransitionValues endValues, int endVisibility) {
-            return mVisibility.onDisappear(sceneRoot, startValues, startVisibility,
-                    endValues, endVisibility);
-        }
-
-    }
-
-}
diff --git a/transition/ics/android/support/transition/VisibilityPort.java b/transition/ics/android/support/transition/VisibilityPort.java
deleted file mode 100644
index e740b19..0000000
--- a/transition/ics/android/support/transition/VisibilityPort.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * This transition tracks changes to the visibility of target views in the
- * start and end scenes. Visibility is determined not just by the
- * {@link View#setVisibility(int)} state of views, but also whether
- * views exist in the current view hierarchy. The class is intended to be a
- * utility for subclasses such as {@link FadePort}, which use this visibility
- * information to determine the specific animations to run when visibility
- * changes occur. Subclasses should implement one or both of the methods
- * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
- * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
- */
-@RequiresApi(14)
-@TargetApi(14)
-abstract class VisibilityPort extends TransitionPort {
-
-    private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
-
-    private static final String PROPNAME_PARENT = "android:visibility:parent";
-
-    private static final String[] sTransitionProperties = {
-            PROPNAME_VISIBILITY,
-            PROPNAME_PARENT,
-    };
-
-    @Override
-    public String[] getTransitionProperties() {
-        return sTransitionProperties;
-    }
-
-    private void captureValues(TransitionValues transitionValues) {
-        int visibility = transitionValues.view.getVisibility();
-        transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
-        transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    /**
-     * Returns whether the view is 'visible' according to the given values
-     * object. This is determined by testing the same properties in the values
-     * object that are used to determine whether the object is appearing or
-     * disappearing in the {@link
-     * TransitionPort#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
-     * method. This method can be called by, for example, subclasses that want
-     * to know whether the object is visible in the same way that Visibility
-     * determines it for the actual animation.
-     *
-     * @param values The TransitionValues object that holds the information by
-     *               which visibility is determined.
-     * @return True if the view reference by <code>values</code> is visible,
-     * false otherwise.
-     */
-    public boolean isVisible(TransitionValues values) {
-        if (values == null) {
-            return false;
-        }
-        int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
-        View parent = (View) values.values.get(PROPNAME_PARENT);
-
-        return visibility == View.VISIBLE && parent != null;
-    }
-
-    private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
-            TransitionValues endValues) {
-        final VisibilityInfo visInfo = new VisibilityInfo();
-        visInfo.visibilityChange = false;
-        visInfo.fadeIn = false;
-        if (startValues != null) {
-            visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
-            visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
-        } else {
-            visInfo.startVisibility = -1;
-            visInfo.startParent = null;
-        }
-        if (endValues != null) {
-            visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
-            visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
-        } else {
-            visInfo.endVisibility = -1;
-            visInfo.endParent = null;
-        }
-        if (startValues != null && endValues != null) {
-            if (visInfo.startVisibility == visInfo.endVisibility &&
-                    visInfo.startParent == visInfo.endParent) {
-                return visInfo;
-            } else {
-                if (visInfo.startVisibility != visInfo.endVisibility) {
-                    if (visInfo.startVisibility == View.VISIBLE) {
-                        visInfo.fadeIn = false;
-                        visInfo.visibilityChange = true;
-                    } else if (visInfo.endVisibility == View.VISIBLE) {
-                        visInfo.fadeIn = true;
-                        visInfo.visibilityChange = true;
-                    }
-                    // no visibilityChange if going between INVISIBLE and GONE
-                } else if (visInfo.startParent != visInfo.endParent) {
-                    if (visInfo.endParent == null) {
-                        visInfo.fadeIn = false;
-                        visInfo.visibilityChange = true;
-                    } else if (visInfo.startParent == null) {
-                        visInfo.fadeIn = true;
-                        visInfo.visibilityChange = true;
-                    }
-                }
-            }
-        }
-        if (startValues == null) {
-            visInfo.fadeIn = true;
-            visInfo.visibilityChange = true;
-        } else if (endValues == null) {
-            visInfo.fadeIn = false;
-            visInfo.visibilityChange = true;
-        }
-        return visInfo;
-    }
-
-    @Override
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
-        if (visInfo.visibilityChange) {
-            // Only transition views that are either targets of this transition
-            // or whose parent hierarchies remain stable between scenes
-            boolean isTarget = false;
-            if (mTargets.size() > 0 || mTargetIds.size() > 0) {
-                View startView = startValues != null ? startValues.view : null;
-                View endView = endValues != null ? endValues.view : null;
-                int startId = startView != null ? startView.getId() : -1;
-                int endId = endView != null ? endView.getId() : -1;
-                isTarget = isValidTarget(startView, startId) || isValidTarget(endView, endId);
-            }
-            if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null))) {
-                if (visInfo.fadeIn) {
-                    return onAppear(sceneRoot, startValues, visInfo.startVisibility,
-                            endValues, visInfo.endVisibility);
-                } else {
-                    return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
-                            endValues, visInfo.endVisibility
-                    );
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * The default implementation of this method does nothing. Subclasses
-     * should override if they need to create an Animator when targets appear.
-     * The method should only be called by the Visibility class; it is
-     * not intended to be called from external classes.
-     *
-     * @param sceneRoot       The root of the transition hierarchy
-     * @param startValues     The target values in the start scene
-     * @param startVisibility The target visibility in the start scene
-     * @param endValues       The target values in the end scene
-     * @param endVisibility   The target visibility in the end scene
-     * @return An Animator to be started at the appropriate time in the
-     * overall transition for this scene change. A null value means no animation
-     * should be run.
-     */
-    public Animator onAppear(ViewGroup sceneRoot,
-            TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        return null;
-    }
-
-    /**
-     * The default implementation of this method does nothing. Subclasses
-     * should override if they need to create an Animator when targets disappear.
-     * The method should only be called by the Visibility class; it is
-     * not intended to be called from external classes.
-     *
-     * @param sceneRoot       The root of the transition hierarchy
-     * @param startValues     The target values in the start scene
-     * @param startVisibility The target visibility in the start scene
-     * @param endValues       The target values in the end scene
-     * @param endVisibility   The target visibility in the end scene
-     * @return An Animator to be started at the appropriate time in the
-     * overall transition for this scene change. A null value means no animation
-     * should be run.
-     */
-    public Animator onDisappear(ViewGroup sceneRoot,
-            TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        return null;
-    }
-
-    private static class VisibilityInfo {
-
-        boolean visibilityChange;
-
-        boolean fadeIn;
-
-        int startVisibility;
-
-        int endVisibility;
-
-        ViewGroup startParent;
-
-        ViewGroup endParent;
-
-        VisibilityInfo() {
-        }
-    }
-}
diff --git a/transition/ics/android/support/transition/WindowIdPort.java b/transition/ics/android/support/transition/WindowIdPort.java
deleted file mode 100644
index 148332e..0000000
--- a/transition/ics/android/support/transition/WindowIdPort.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.os.IBinder;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-
-/**
- * Backport of WindowId.
- *
- * <p>Since the use of WindowId in Transition API is limited to identifying windows, we can just
- * wrap a window token and use it as an identifier.</p>
- */
-@RequiresApi(14)
-@TargetApi(14)
-class WindowIdPort {
-
-    private final IBinder mToken;
-
-    private WindowIdPort(IBinder token) {
-        mToken = token;
-    }
-
-    static WindowIdPort getWindowId(@NonNull View view) {
-        return new WindowIdPort(view.getWindowToken());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return obj instanceof WindowIdPort && ((WindowIdPort) obj).mToken.equals(this.mToken);
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java b/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
deleted file mode 100644
index e8575d4..0000000
--- a/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class ChangeBoundsKitKat extends TransitionKitKat implements ChangeBoundsInterface {
-
-    public ChangeBoundsKitKat(TransitionInterface transition) {
-        init(transition, new android.transition.ChangeBounds());
-    }
-
-    @Override
-    public void setResizeClip(boolean resizeClip) {
-        ((android.transition.ChangeBounds) mTransition).setResizeClip(resizeClip);
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/FadeKitKat.java b/transition/kitkat/android/support/transition/FadeKitKat.java
deleted file mode 100644
index 26992af..0000000
--- a/transition/kitkat/android/support/transition/FadeKitKat.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.view.ViewGroup;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class FadeKitKat extends TransitionKitKat implements VisibilityImpl {
-
-    public FadeKitKat(TransitionInterface transition) {
-        init(transition, new android.transition.Fade());
-    }
-
-    public FadeKitKat(TransitionInterface transition, int fadingMode) {
-        init(transition, new android.transition.Fade(fadingMode));
-    }
-
-    @Override
-    public boolean isVisible(TransitionValues values) {
-        return ((android.transition.Fade) mTransition).isVisible(convertToPlatform(values));
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        return ((android.transition.Fade) mTransition).onAppear(sceneRoot,
-                convertToPlatform(startValues), startVisibility,
-                convertToPlatform(endValues), endVisibility);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
-            int startVisibility, TransitionValues endValues, int endVisibility) {
-        return ((android.transition.Fade) mTransition).onDisappear(sceneRoot,
-                convertToPlatform(startValues), startVisibility,
-                convertToPlatform(endValues), endVisibility);
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/SceneKitKat.java b/transition/kitkat/android/support/transition/SceneKitKat.java
deleted file mode 100644
index ae7ad10..0000000
--- a/transition/kitkat/android/support/transition/SceneKitKat.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class SceneKitKat extends SceneWrapper {
-
-    private static Field sEnterAction;
-    private static Method sSetCurrentScene;
-
-    private View mLayout; // alternative to layoutId
-
-    @Override
-    public void init(ViewGroup sceneRoot) {
-        mScene = new android.transition.Scene(sceneRoot);
-    }
-
-    @Override
-    public void init(ViewGroup sceneRoot, View layout) {
-        if (layout instanceof ViewGroup) {
-            mScene = new android.transition.Scene(sceneRoot, (ViewGroup) layout);
-        } else {
-            mScene = new android.transition.Scene(sceneRoot);
-            mLayout = layout;
-        }
-    }
-
-    @Override
-    public void enter() {
-        if (mLayout != null) {
-            // empty out parent container before adding to it
-            final ViewGroup root = getSceneRoot();
-            root.removeAllViews();
-            root.addView(mLayout);
-            invokeEnterAction();
-            updateCurrentScene(root);
-        } else {
-            mScene.enter();
-        }
-    }
-
-    private void invokeEnterAction() {
-        if (sEnterAction == null) {
-            try {
-                sEnterAction = android.transition.Scene.class.getDeclaredField("mEnterAction");
-                sEnterAction.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        try {
-            final Runnable enterAction = (Runnable) sEnterAction.get(mScene);
-            if (enterAction != null) {
-                enterAction.run();
-            }
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Sets this Scene as the current scene of the View. */
-    private void updateCurrentScene(View view) {
-        if (sSetCurrentScene == null) {
-            try {
-                sSetCurrentScene = android.transition.Scene.class.getDeclaredMethod(
-                        "setCurrentScene", View.class, android.transition.Scene.class);
-                sSetCurrentScene.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        try {
-            sSetCurrentScene.invoke(null, view, mScene);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/SceneStaticsKitKat.java b/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
deleted file mode 100644
index 94868d0..0000000
--- a/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.content.Context;
-import android.view.ViewGroup;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class SceneStaticsKitKat extends SceneStaticsImpl {
-
-    @Override
-    public SceneImpl getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
-        SceneKitKat scene = new SceneKitKat();
-        scene.mScene = android.transition.Scene.getSceneForLayout(sceneRoot, layoutId, context);
-        return scene;
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/SceneWrapper.java b/transition/kitkat/android/support/transition/SceneWrapper.java
deleted file mode 100644
index cad5db9..0000000
--- a/transition/kitkat/android/support/transition/SceneWrapper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.view.ViewGroup;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-abstract class SceneWrapper extends SceneImpl {
-
-    /* package */ android.transition.Scene mScene;
-
-    @Override
-    public ViewGroup getSceneRoot() {
-        return mScene.getSceneRoot();
-    }
-
-    @Override
-    public void exit() {
-        mScene.exit();
-    }
-
-    @Override
-    public void setEnterAction(Runnable action) {
-        mScene.setEnterAction(action);
-    }
-
-    @Override
-    public void setExitAction(Runnable action) {
-        mScene.setExitAction(action);
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/TransitionKitKat.java b/transition/kitkat/android/support/transition/TransitionKitKat.java
deleted file mode 100644
index c608f66..0000000
--- a/transition/kitkat/android/support/transition/TransitionKitKat.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.transition.Transition;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class TransitionKitKat extends TransitionImpl {
-
-    /* package */ android.transition.Transition mTransition;
-
-    /* package */ TransitionInterface mExternalTransition;
-
-    private CompatListener mCompatListener;
-
-    static void copyValues(android.transition.TransitionValues source,
-            android.support.transition.TransitionValues dest) {
-        if (source == null) {
-            return;
-        }
-        dest.view = source.view;
-        if (source.values.size() > 0) {
-            dest.values.putAll(source.values);
-        }
-    }
-
-    static void copyValues(android.support.transition.TransitionValues source,
-            android.transition.TransitionValues dest) {
-        if (source == null) {
-            return;
-        }
-        dest.view = source.view;
-        if (source.values.size() > 0) {
-            dest.values.putAll(source.values);
-        }
-    }
-
-    static void wrapCaptureStartValues(TransitionInterface transition,
-            android.transition.TransitionValues transitionValues) {
-        android.support.transition.TransitionValues externalValues =
-                new android.support.transition.TransitionValues();
-        copyValues(transitionValues, externalValues);
-        transition.captureStartValues(externalValues);
-        copyValues(externalValues, transitionValues);
-    }
-
-    static void wrapCaptureEndValues(TransitionInterface transition,
-            android.transition.TransitionValues transitionValues) {
-        android.support.transition.TransitionValues externalValues =
-                new android.support.transition.TransitionValues();
-        copyValues(transitionValues, externalValues);
-        transition.captureEndValues(externalValues);
-        copyValues(externalValues, transitionValues);
-    }
-
-    static TransitionValues convertToSupport(android.transition.TransitionValues values) {
-        if (values == null) {
-            return null;
-        }
-        TransitionValues supportValues = new TransitionValues();
-        copyValues(values, supportValues);
-        return supportValues;
-    }
-
-    static android.transition.TransitionValues convertToPlatform(TransitionValues values) {
-        if (values == null) {
-            return null;
-        }
-        android.transition.TransitionValues platformValues
-                = new android.transition.TransitionValues();
-        copyValues(values, platformValues);
-        return platformValues;
-    }
-
-    @Override
-    public void init(TransitionInterface external, Object internal) {
-        mExternalTransition = external;
-        if (internal == null) {
-            mTransition = new TransitionWrapper(external);
-        } else {
-            mTransition = (android.transition.Transition) internal;
-        }
-    }
-
-    @Override
-    public TransitionImpl addListener(TransitionInterfaceListener listener) {
-        if (mCompatListener == null) {
-            mCompatListener = new CompatListener();
-            mTransition.addListener(mCompatListener);
-        }
-        mCompatListener.addListener(listener);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl removeListener(TransitionInterfaceListener listener) {
-        if (mCompatListener == null) {
-            return this;
-        }
-        mCompatListener.removeListener(listener);
-        if (mCompatListener.isEmpty()) {
-            mTransition.removeListener(mCompatListener);
-            mCompatListener = null;
-        }
-        return this;
-    }
-
-    @Override
-    public TransitionImpl addTarget(View target) {
-        mTransition.addTarget(target);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl addTarget(int targetId) {
-        mTransition.addTarget(targetId);
-        return this;
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        android.transition.TransitionValues internalValues =
-                new android.transition.TransitionValues();
-        copyValues(transitionValues, internalValues);
-        mTransition.captureEndValues(internalValues);
-        copyValues(internalValues, transitionValues);
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        android.transition.TransitionValues internalValues =
-                new android.transition.TransitionValues();
-        copyValues(transitionValues, internalValues);
-        mTransition.captureStartValues(internalValues);
-        copyValues(internalValues, transitionValues);
-    }
-
-    @Override
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        android.transition.TransitionValues internalStartValues;
-        android.transition.TransitionValues internalEndValues;
-        if (startValues != null) {
-            internalStartValues = new android.transition.TransitionValues();
-            copyValues(startValues, internalStartValues);
-        } else {
-            internalStartValues = null;
-        }
-        if (endValues != null) {
-            internalEndValues = new android.transition.TransitionValues();
-            copyValues(endValues, internalEndValues);
-        } else {
-            internalEndValues = null;
-        }
-        return mTransition.createAnimator(sceneRoot, internalStartValues, internalEndValues);
-    }
-
-    @Override
-    public TransitionImpl excludeChildren(View target, boolean exclude) {
-        mTransition.excludeChildren(target, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeChildren(int targetId, boolean exclude) {
-        mTransition.excludeChildren(targetId, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeChildren(Class type, boolean exclude) {
-        mTransition.excludeChildren(type, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeTarget(View target, boolean exclude) {
-        mTransition.excludeTarget(target, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeTarget(int targetId, boolean exclude) {
-        mTransition.excludeTarget(targetId, exclude);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl excludeTarget(Class type, boolean exclude) {
-        mTransition.excludeTarget(type, exclude);
-        return this;
-    }
-
-    @Override
-    public long getDuration() {
-        return mTransition.getDuration();
-    }
-
-    @Override
-    public TransitionImpl setDuration(long duration) {
-        mTransition.setDuration(duration);
-        return this;
-    }
-
-    @Override
-    public TimeInterpolator getInterpolator() {
-        return mTransition.getInterpolator();
-    }
-
-    @Override
-    public TransitionImpl setInterpolator(TimeInterpolator interpolator) {
-        mTransition.setInterpolator(interpolator);
-        return this;
-    }
-
-    @Override
-    public String getName() {
-        return mTransition.getName();
-    }
-
-    @Override
-    public long getStartDelay() {
-        return mTransition.getStartDelay();
-    }
-
-    @Override
-    public TransitionImpl setStartDelay(long startDelay) {
-        mTransition.setStartDelay(startDelay);
-        return this;
-    }
-
-    @Override
-    public List<Integer> getTargetIds() {
-        return mTransition.getTargetIds();
-    }
-
-    @Override
-    public List<View> getTargets() {
-        return mTransition.getTargets();
-    }
-
-    @Override
-    public String[] getTransitionProperties() {
-        return mTransition.getTransitionProperties();
-    }
-
-    @Override
-    public TransitionValues getTransitionValues(View view, boolean start) {
-        TransitionValues values = new TransitionValues();
-        copyValues(mTransition.getTransitionValues(view, start), values);
-        return values;
-    }
-
-    @Override
-    public TransitionImpl removeTarget(View target) {
-        mTransition.removeTarget(target);
-        return this;
-    }
-
-    @Override
-    public TransitionImpl removeTarget(int targetId) {
-        if (targetId > 0) {
-            // Workaround for the issue that the platform version calls remove(int)
-            // when it should call remove(Integer)
-            getTargetIds().remove((Integer) targetId);
-        }
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        return mTransition.toString();
-    }
-
-    private static class TransitionWrapper extends android.transition.Transition {
-
-        private TransitionInterface mTransition;
-
-        public TransitionWrapper(TransitionInterface transition) {
-            mTransition = transition;
-        }
-
-        @Override
-        public void captureStartValues(android.transition.TransitionValues transitionValues) {
-            wrapCaptureStartValues(mTransition, transitionValues);
-        }
-
-        @Override
-        public void captureEndValues(android.transition.TransitionValues transitionValues) {
-            wrapCaptureEndValues(mTransition, transitionValues);
-        }
-
-        @Override
-        public Animator createAnimator(ViewGroup sceneRoot,
-                android.transition.TransitionValues startValues,
-                android.transition.TransitionValues endValues) {
-            return mTransition.createAnimator(sceneRoot, convertToSupport(startValues),
-                    convertToSupport(endValues));
-        }
-
-    }
-
-    @SuppressWarnings("unchecked")
-    private class CompatListener implements android.transition.Transition.TransitionListener {
-
-        private final ArrayList<TransitionInterfaceListener> mListeners = new ArrayList<>();
-
-        CompatListener() {
-        }
-
-        void addListener(TransitionInterfaceListener listener) {
-            mListeners.add(listener);
-        }
-
-        void removeListener(TransitionInterfaceListener listener) {
-            mListeners.remove(listener);
-        }
-
-        boolean isEmpty() {
-            return mListeners.isEmpty();
-        }
-
-        @Override
-        public void onTransitionStart(Transition transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionStart(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionEnd(Transition transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionEnd(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionCancel(Transition transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionCancel(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionPause(Transition transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionPause(mExternalTransition);
-            }
-        }
-
-        @Override
-        public void onTransitionResume(Transition transition) {
-            for (TransitionInterfaceListener listener : mListeners) {
-                listener.onTransitionResume(mExternalTransition);
-            }
-        }
-
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/TransitionManagerKitKat.java b/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
deleted file mode 100644
index 3326600..0000000
--- a/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.transition.TransitionManager;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class TransitionManagerKitKat extends TransitionManagerImpl {
-
-    private final android.transition.TransitionManager mTransitionManager = new TransitionManager();
-
-    @Override
-    public void setTransition(SceneImpl scene, TransitionImpl transition) {
-        mTransitionManager.setTransition(((SceneWrapper) scene).mScene,
-                transition == null ? null : ((TransitionKitKat) transition).mTransition);
-    }
-
-    @Override
-    public void setTransition(SceneImpl fromScene, SceneImpl toScene, TransitionImpl transition) {
-        mTransitionManager.setTransition(((SceneWrapper) fromScene).mScene,
-                ((SceneWrapper) toScene).mScene,
-                transition == null ? null : ((TransitionKitKat) transition).mTransition);
-    }
-
-    @Override
-    public void transitionTo(SceneImpl scene) {
-        mTransitionManager.transitionTo(((SceneWrapper) scene).mScene);
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java b/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
deleted file mode 100644
index e7c927b..0000000
--- a/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.view.ViewGroup;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class TransitionManagerStaticsKitKat extends TransitionManagerStaticsImpl {
-
-    @Override
-    public void go(SceneImpl scene) {
-        android.transition.TransitionManager.go(((SceneWrapper) scene).mScene);
-    }
-
-    @Override
-    public void go(SceneImpl scene, TransitionImpl transition) {
-        android.transition.TransitionManager.go(((SceneWrapper) scene).mScene,
-                transition == null ? null : ((TransitionKitKat) transition).mTransition);
-    }
-
-    @Override
-    public void beginDelayedTransition(ViewGroup sceneRoot) {
-        android.transition.TransitionManager.beginDelayedTransition(sceneRoot);
-    }
-
-    @Override
-    public void beginDelayedTransition(ViewGroup sceneRoot, TransitionImpl transition) {
-        android.transition.TransitionManager.beginDelayedTransition(sceneRoot,
-                transition == null ? null : ((TransitionKitKat) transition).mTransition);
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/TransitionSetKitKat.java b/transition/kitkat/android/support/transition/TransitionSetKitKat.java
deleted file mode 100644
index f880d71..0000000
--- a/transition/kitkat/android/support/transition/TransitionSetKitKat.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class TransitionSetKitKat extends TransitionKitKat implements TransitionSetImpl {
-
-    private android.transition.TransitionSet mTransitionSet;
-
-    public TransitionSetKitKat(TransitionInterface transition) {
-        mTransitionSet = new android.transition.TransitionSet();
-        init(transition, mTransitionSet);
-    }
-
-    @Override
-    public int getOrdering() {
-        return mTransitionSet.getOrdering();
-    }
-
-    @Override
-    public TransitionSetKitKat setOrdering(int ordering) {
-        mTransitionSet.setOrdering(ordering);
-        return this;
-    }
-
-    @Override
-    public TransitionSetKitKat addTransition(TransitionImpl transition) {
-        mTransitionSet.addTransition(((TransitionKitKat) transition).mTransition);
-        return this;
-    }
-
-    @Override
-    public TransitionSetKitKat removeTransition(TransitionImpl transition) {
-        mTransitionSet.removeTransition(((TransitionKitKat) transition).mTransition);
-        return this;
-    }
-
-}
diff --git a/transition/kitkat/android/support/transition/VisibilityKitKat.java b/transition/kitkat/android/support/transition/VisibilityKitKat.java
deleted file mode 100644
index ca603ae..0000000
--- a/transition/kitkat/android/support/transition/VisibilityKitKat.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.transition;
-
-import android.animation.Animator;
-import android.view.ViewGroup;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-@TargetApi(19)
-class VisibilityKitKat extends TransitionKitKat implements VisibilityImpl {
-
-    @Override
-    public void init(TransitionInterface external, Object internal) {
-        mExternalTransition = external;
-        if (internal == null) {
-            mTransition = new VisibilityWrapper((VisibilityInterface) external);
-        } else {
-            mTransition = (android.transition.Visibility) internal;
-        }
-    }
-
-    @Override
-    public boolean isVisible(TransitionValues values) {
-        return ((android.transition.Visibility) mTransition).isVisible(convertToPlatform(values));
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        return ((android.transition.Visibility) mTransition).onAppear(sceneRoot,
-                convertToPlatform(startValues), startVisibility,
-                convertToPlatform(endValues), endVisibility);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
-            int startVisibility, TransitionValues endValues, int endVisibility) {
-        return ((android.transition.Visibility) mTransition).onDisappear(sceneRoot,
-                convertToPlatform(startValues), startVisibility,
-                convertToPlatform(endValues), endVisibility);
-    }
-
-    private static class VisibilityWrapper extends android.transition.Visibility {
-
-        private final VisibilityInterface mVisibility;
-
-        VisibilityWrapper(VisibilityInterface visibility) {
-            mVisibility = visibility;
-        }
-
-        @Override
-        public void captureStartValues(android.transition.TransitionValues transitionValues) {
-            wrapCaptureStartValues(mVisibility, transitionValues);
-        }
-
-        @Override
-        public void captureEndValues(android.transition.TransitionValues transitionValues) {
-            wrapCaptureEndValues(mVisibility, transitionValues);
-        }
-
-        @Override
-        public Animator createAnimator(ViewGroup sceneRoot,
-                android.transition.TransitionValues startValues,
-                android.transition.TransitionValues endValues) {
-            return mVisibility.createAnimator(sceneRoot, convertToSupport(startValues),
-                    convertToSupport(endValues));
-        }
-
-        @Override
-        public boolean isVisible(android.transition.TransitionValues values) {
-            if (values == null) {
-                return false;
-            }
-            TransitionValues externalValues = new TransitionValues();
-            copyValues(values, externalValues);
-            return mVisibility.isVisible(externalValues);
-        }
-
-        @Override
-        public Animator onAppear(ViewGroup sceneRoot,
-                android.transition.TransitionValues startValues, int startVisibility,
-                android.transition.TransitionValues endValues, int endVisibility) {
-            return mVisibility.onAppear(sceneRoot, convertToSupport(startValues), startVisibility,
-                    convertToSupport(endValues), endVisibility);
-        }
-
-        @Override
-        public Animator onDisappear(ViewGroup sceneRoot,
-                android.transition.TransitionValues startValues, int startVisibility,
-                android.transition.TransitionValues endValues, int endVisibility) {
-            return mVisibility.onDisappear(sceneRoot, convertToSupport(startValues),
-                    startVisibility,
-                    convertToSupport(endValues), endVisibility);
-        }
-
-    }
-
-}
diff --git a/transition/lint-baseline.xml b/transition/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/transition/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/transition/res/values/ids.xml b/transition/res/values/ids.xml
index 9a6846b..45477b8 100644
--- a/transition/res/values/ids.xml
+++ b/transition/res/values/ids.xml
@@ -17,4 +17,12 @@
 <resources>
     <item name="transition_scene_layoutid_cache" type="id"/>
     <item name="transition_current_scene" type="id"/>
+    <item name="transition_layout_save" type="id"/>
+    <item name="transition_position" type="id"/>
+    <item name="transition_transform" type="id"/>
+    <item name="parent_matrix" type="id"/>
+    <item name="ghost_view" type="id"/>
+    <item name="save_scale_type" type="id"/>
+    <item name="save_image_matrix" type="id"/>
+    <item name="save_non_transition_alpha" type="id"/>
 </resources>
diff --git a/transition/src/android/support/transition/AnimatorUtils.java b/transition/src/android/support/transition/AnimatorUtils.java
new file mode 100644
index 0000000..215d768
--- /dev/null
+++ b/transition/src/android/support/transition/AnimatorUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.os.Build;
+import android.support.annotation.NonNull;
+
+class AnimatorUtils {
+
+    private static final AnimatorUtilsImpl IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new AnimatorUtilsApi19();
+        } else {
+            IMPL = new AnimatorUtilsApi14();
+        }
+    }
+
+    static void addPauseListener(@NonNull Animator animator,
+            @NonNull AnimatorListenerAdapter listener) {
+        IMPL.addPauseListener(animator, listener);
+    }
+
+    static void pause(@NonNull Animator animator) {
+        IMPL.pause(animator);
+    }
+
+    static void resume(@NonNull Animator animator) {
+        IMPL.resume(animator);
+    }
+
+}
diff --git a/transition/src/android/support/transition/ArcMotion.java b/transition/src/android/support/transition/ArcMotion.java
new file mode 100644
index 0000000..42f667c
--- /dev/null
+++ b/transition/src/android/support/transition/ArcMotion.java
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Path;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.util.AttributeSet;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A PathMotion that generates a curved path along an arc on an imaginary circle containing
+ * the two points. If the horizontal distance between the points is less than the vertical
+ * distance, then the circle's center point will be horizontally aligned with the end point. If the
+ * vertical distance is less than the horizontal distance then the circle's center point
+ * will be vertically aligned with the end point.
+ * <p>
+ * When the two points are near horizontal or vertical, the curve of the motion will be
+ * small as the center of the circle will be far from both points. To force curvature of
+ * the path, {@link #setMinimumHorizontalAngle(float)} and
+ * {@link #setMinimumVerticalAngle(float)} may be used to set the minimum angle of the
+ * arc between two points.
+ * </p>
+ * <p>This may be used in XML as an element inside a transition.</p>
+ * <pre>{@code
+ * <changeBounds>
+ *   <arcMotion android:minimumHorizontalAngle="15"
+ *              android:minimumVerticalAngle="0"
+ *              android:maximumAngle="90"/>
+ * </changeBounds>}
+ * </pre>
+ */
+public class ArcMotion extends PathMotion {
+
+    private static final float DEFAULT_MIN_ANGLE_DEGREES = 0;
+    private static final float DEFAULT_MAX_ANGLE_DEGREES = 70;
+    private static final float DEFAULT_MAX_TANGENT = (float)
+            Math.tan(Math.toRadians(DEFAULT_MAX_ANGLE_DEGREES / 2));
+
+    private float mMinimumHorizontalAngle = 0;
+    private float mMinimumVerticalAngle = 0;
+    private float mMaximumAngle = DEFAULT_MAX_ANGLE_DEGREES;
+    private float mMinimumHorizontalTangent = 0;
+    private float mMinimumVerticalTangent = 0;
+    private float mMaximumTangent = DEFAULT_MAX_TANGENT;
+
+    public ArcMotion() {
+    }
+
+    public ArcMotion(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.ARC_MOTION);
+        XmlPullParser parser = (XmlPullParser) attrs;
+        float minimumVerticalAngle = TypedArrayUtils.getNamedFloat(a, parser,
+                "minimumVerticalAngle", Styleable.ArcMotion.MINIMUM_VERTICAL_ANGLE,
+                DEFAULT_MIN_ANGLE_DEGREES);
+        setMinimumVerticalAngle(minimumVerticalAngle);
+        float minimumHorizontalAngle = TypedArrayUtils.getNamedFloat(a, parser,
+                "minimumHorizontalAngle", Styleable.ArcMotion.MINIMUM_HORIZONTAL_ANGLE,
+                DEFAULT_MIN_ANGLE_DEGREES);
+        setMinimumHorizontalAngle(minimumHorizontalAngle);
+        float maximumAngle = TypedArrayUtils.getNamedFloat(a, parser, "maximumAngle",
+                Styleable.ArcMotion.MAXIMUM_ANGLE, DEFAULT_MAX_ANGLE_DEGREES);
+        setMaximumAngle(maximumAngle);
+        a.recycle();
+    }
+
+    /**
+     * Sets the minimum arc along the circle between two points aligned near horizontally.
+     * When start and end points are close to horizontal, the calculated center point of the
+     * circle will be far from both points, giving a near straight path between the points.
+     * By setting a minimum angle, this forces the center point to be closer and give an
+     * exaggerated curve to the path.
+     * <p>The default value is 0.</p>
+     *
+     * @param angleInDegrees The minimum angle of the arc on a circle describing the Path
+     *                       between two nearly horizontally-separated points.
+     */
+    public void setMinimumHorizontalAngle(float angleInDegrees) {
+        mMinimumHorizontalAngle = angleInDegrees;
+        mMinimumHorizontalTangent = toTangent(angleInDegrees);
+    }
+
+    /**
+     * Returns the minimum arc along the circle between two points aligned near horizontally.
+     * When start and end points are close to horizontal, the calculated center point of the
+     * circle will be far from both points, giving a near straight path between the points.
+     * By setting a minimum angle, this forces the center point to be closer and give an
+     * exaggerated curve to the path.
+     * <p>The default value is 0.</p>
+     *
+     * @return The minimum arc along the circle between two points aligned near horizontally.
+     */
+    public float getMinimumHorizontalAngle() {
+        return mMinimumHorizontalAngle;
+    }
+
+    /**
+     * Sets the minimum arc along the circle between two points aligned near vertically.
+     * When start and end points are close to vertical, the calculated center point of the
+     * circle will be far from both points, giving a near straight path between the points.
+     * By setting a minimum angle, this forces the center point to be closer and give an
+     * exaggerated curve to the path.
+     * <p>The default value is 0.</p>
+     *
+     * @param angleInDegrees The minimum angle of the arc on a circle describing the Path
+     *                       between two nearly vertically-separated points.
+     */
+    public void setMinimumVerticalAngle(float angleInDegrees) {
+        mMinimumVerticalAngle = angleInDegrees;
+        mMinimumVerticalTangent = toTangent(angleInDegrees);
+    }
+
+    /**
+     * Returns the minimum arc along the circle between two points aligned near vertically.
+     * When start and end points are close to vertical, the calculated center point of the
+     * circle will be far from both points, giving a near straight path between the points.
+     * By setting a minimum angle, this forces the center point to be closer and give an
+     * exaggerated curve to the path.
+     * <p>The default value is 0.</p>
+     *
+     * @return The minimum angle of the arc on a circle describing the Path
+     * between two nearly vertically-separated points.
+     */
+    public float getMinimumVerticalAngle() {
+        return mMinimumVerticalAngle;
+    }
+
+    /**
+     * Sets the maximum arc along the circle between two points. When start and end points
+     * have close to equal x and y differences, the curve between them is large. This forces
+     * the curved path to have an arc of at most the given angle.
+     * <p>The default value is 70 degrees.</p>
+     *
+     * @param angleInDegrees The maximum angle of the arc on a circle describing the Path
+     *                       between the start and end points.
+     */
+    public void setMaximumAngle(float angleInDegrees) {
+        mMaximumAngle = angleInDegrees;
+        mMaximumTangent = toTangent(angleInDegrees);
+    }
+
+    /**
+     * Returns the maximum arc along the circle between two points. When start and end points
+     * have close to equal x and y differences, the curve between them is large. This forces
+     * the curved path to have an arc of at most the given angle.
+     * <p>The default value is 70 degrees.</p>
+     *
+     * @return The maximum angle of the arc on a circle describing the Path
+     * between the start and end points.
+     */
+    public float getMaximumAngle() {
+        return mMaximumAngle;
+    }
+
+    private static float toTangent(float arcInDegrees) {
+        if (arcInDegrees < 0 || arcInDegrees > 90) {
+            throw new IllegalArgumentException("Arc must be between 0 and 90 degrees");
+        }
+        return (float) Math.tan(Math.toRadians(arcInDegrees / 2));
+    }
+
+    @Override
+    public Path getPath(float startX, float startY, float endX, float endY) {
+        // Here's a little ascii art to show how this is calculated:
+        // c---------- b
+        //  \        / |
+        //    \     d  |
+        //      \  /   e
+        //        a----f
+        // This diagram assumes that the horizontal distance is less than the vertical
+        // distance between The start point (a) and end point (b).
+        // d is the midpoint between a and b. c is the center point of the circle with
+        // This path is formed by assuming that start and end points are in
+        // an arc on a circle. The end point is centered in the circle vertically
+        // and start is a point on the circle.
+
+        // Triangles bfa and bde form similar right triangles. The control points
+        // for the cubic Bezier arc path are the midpoints between a and e and e and b.
+
+        Path path = new Path();
+        path.moveTo(startX, startY);
+
+        float ex;
+        float ey;
+        float deltaX = endX - startX;
+        float deltaY = endY - startY;
+
+        // hypotenuse squared.
+        float h2 = deltaX * deltaX + deltaY * deltaY;
+
+        // Midpoint between start and end
+        float dx = (startX + endX) / 2;
+        float dy = (startY + endY) / 2;
+
+        // Distance squared between end point and mid point is (1/2 hypotenuse)^2
+        float midDist2 = h2 * 0.25f;
+
+        float minimumArcDist2;
+
+        boolean isMovingUpwards = startY > endY;
+
+        if ((Math.abs(deltaX) < Math.abs(deltaY))) {
+            // Similar triangles bfa and bde mean that (ab/fb = eb/bd)
+            // Therefore, eb = ab * bd / fb
+            // ab = hypotenuse
+            // bd = hypotenuse/2
+            // fb = deltaY
+            float eDistY = Math.abs(h2 / (2 * deltaY));
+            if (isMovingUpwards) {
+                ey = endY + eDistY;
+                ex = endX;
+            } else {
+                ey = startY + eDistY;
+                ex = startX;
+            }
+
+            minimumArcDist2 = midDist2 * mMinimumVerticalTangent
+                    * mMinimumVerticalTangent;
+        } else {
+            // Same as above, but flip X & Y and account for negative eDist
+            float eDistX = h2 / (2 * deltaX);
+            if (isMovingUpwards) {
+                ex = startX + eDistX;
+                ey = startY;
+            } else {
+                ex = endX - eDistX;
+                ey = endY;
+            }
+
+            minimumArcDist2 = midDist2 * mMinimumHorizontalTangent
+                    * mMinimumHorizontalTangent;
+        }
+        float arcDistX = dx - ex;
+        float arcDistY = dy - ey;
+        float arcDist2 = arcDistX * arcDistX + arcDistY * arcDistY;
+
+        float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
+
+        float newArcDistance2 = 0;
+        if (arcDist2 < minimumArcDist2) {
+            newArcDistance2 = minimumArcDist2;
+        } else if (arcDist2 > maximumArcDist2) {
+            newArcDistance2 = maximumArcDist2;
+        }
+        if (newArcDistance2 != 0) {
+            float ratio2 = newArcDistance2 / arcDist2;
+            float ratio = (float) Math.sqrt(ratio2);
+            ex = dx + (ratio * (ex - dx));
+            ey = dy + (ratio * (ey - dy));
+        }
+        float control1X = (startX + ex) / 2;
+        float control1Y = (startY + ey) / 2;
+        float control2X = (ex + endX) / 2;
+        float control2Y = (ey + endY) / 2;
+        path.cubicTo(control1X, control1Y, control2X, control2Y, endX, endY);
+        return path;
+    }
+
+}
diff --git a/transition/src/android/support/transition/AutoTransition.java b/transition/src/android/support/transition/AutoTransition.java
index ec0d404..02b49e2 100644
--- a/transition/src/android/support/transition/AutoTransition.java
+++ b/transition/src/android/support/transition/AutoTransition.java
@@ -16,11 +16,16 @@
 
 package android.support.transition;
 
+import android.content.Context;
+import android.util.AttributeSet;
+
 /**
  * Utility class for creating a default transition that automatically fades,
  * moves, and resizes views during a scene change.
  *
- * <p>Unlike the platform version, this does not support use in XML resources.</p>
+ * <p>An AutoTransition can be described in a resource file by using the
+ * tag <code>autoTransition</code>, along with the other standard
+ * attributes of {@link Transition}.</p>
  */
 public class AutoTransition extends TransitionSet {
 
@@ -30,6 +35,15 @@
      * targets, and finally fades in appearing targets.
      */
     public AutoTransition() {
+        init();
+    }
+
+    public AutoTransition(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    private void init() {
         setOrdering(ORDERING_SEQUENTIAL);
         addTransition(new Fade(Fade.OUT)).
                 addTransition(new ChangeBounds()).
diff --git a/transition/src/android/support/transition/ChangeBounds.java b/transition/src/android/support/transition/ChangeBounds.java
index f29f056..4108c8c 100644
--- a/transition/src/android/support/transition/ChangeBounds.java
+++ b/transition/src/android/support/transition/ChangeBounds.java
@@ -17,43 +17,171 @@
 package android.support.transition;
 
 import android.animation.Animator;
-import android.os.Build;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
 import android.view.ViewGroup;
 
+import java.util.Map;
+
 /**
  * This transition captures the layout bounds of target views before and after
  * the scene change and animates those changes during the transition.
  *
- * <p>Unlike the platform version, this does not support use in XML resources.</p>
+ * <p>A ChangeBounds transition can be described in a resource file by using the
+ * tag <code>changeBounds</code>, along with the other standard attributes of Transition.</p>
  */
 public class ChangeBounds extends Transition {
 
+    private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
+    private static final String PROPNAME_CLIP = "android:changeBounds:clip";
+    private static final String PROPNAME_PARENT = "android:changeBounds:parent";
+    private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX";
+    private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY";
+    private static final String[] sTransitionProperties = {
+            PROPNAME_BOUNDS,
+            PROPNAME_CLIP,
+            PROPNAME_PARENT,
+            PROPNAME_WINDOW_X,
+            PROPNAME_WINDOW_Y
+    };
+
+    private static final Property<Drawable, PointF> DRAWABLE_ORIGIN_PROPERTY =
+            new Property<Drawable, PointF>(PointF.class, "boundsOrigin") {
+                private Rect mBounds = new Rect();
+
+                @Override
+                public void set(Drawable object, PointF value) {
+                    object.copyBounds(mBounds);
+                    mBounds.offsetTo(Math.round(value.x), Math.round(value.y));
+                    object.setBounds(mBounds);
+                }
+
+                @Override
+                public PointF get(Drawable object) {
+                    object.copyBounds(mBounds);
+                    return new PointF(mBounds.left, mBounds.top);
+                }
+            };
+
+    private static final Property<ViewBounds, PointF> TOP_LEFT_PROPERTY =
+            new Property<ViewBounds, PointF>(PointF.class, "topLeft") {
+                @Override
+                public void set(ViewBounds viewBounds, PointF topLeft) {
+                    viewBounds.setTopLeft(topLeft);
+                }
+
+                @Override
+                public PointF get(ViewBounds viewBounds) {
+                    return null;
+                }
+            };
+
+    private static final Property<ViewBounds, PointF> BOTTOM_RIGHT_PROPERTY =
+            new Property<ViewBounds, PointF>(PointF.class, "bottomRight") {
+                @Override
+                public void set(ViewBounds viewBounds, PointF bottomRight) {
+                    viewBounds.setBottomRight(bottomRight);
+                }
+
+                @Override
+                public PointF get(ViewBounds viewBounds) {
+                    return null;
+                }
+            };
+
+    private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
+            new Property<View, PointF>(PointF.class, "bottomRight") {
+                @Override
+                public void set(View view, PointF bottomRight) {
+                    int left = view.getLeft();
+                    int top = view.getTop();
+                    int right = Math.round(bottomRight.x);
+                    int bottom = Math.round(bottomRight.y);
+                    ViewUtils.setLeftTopRightBottom(view, left, top, right, bottom);
+                }
+
+                @Override
+                public PointF get(View view) {
+                    return null;
+                }
+            };
+
+    private static final Property<View, PointF> TOP_LEFT_ONLY_PROPERTY =
+            new Property<View, PointF>(PointF.class, "topLeft") {
+                @Override
+                public void set(View view, PointF topLeft) {
+                    int left = Math.round(topLeft.x);
+                    int top = Math.round(topLeft.y);
+                    int right = view.getRight();
+                    int bottom = view.getBottom();
+                    ViewUtils.setLeftTopRightBottom(view, left, top, right, bottom);
+                }
+
+                @Override
+                public PointF get(View view) {
+                    return null;
+                }
+            };
+
+    private static final Property<View, PointF> POSITION_PROPERTY =
+            new Property<View, PointF>(PointF.class, "position") {
+                @Override
+                public void set(View view, PointF topLeft) {
+                    int left = Math.round(topLeft.x);
+                    int top = Math.round(topLeft.y);
+                    int right = left + view.getWidth();
+                    int bottom = top + view.getHeight();
+                    ViewUtils.setLeftTopRightBottom(view, left, top, right, bottom);
+                }
+
+                @Override
+                public PointF get(View view) {
+                    return null;
+                }
+            };
+
+    private int[] mTempLocation = new int[2];
+    private boolean mResizeClip = false;
+    private boolean mReparent = false;
+
+    private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
     public ChangeBounds() {
-        super(true);
-        if (Build.VERSION.SDK_INT < 19) {
-            mImpl = new ChangeBoundsIcs(this);
-        } else {
-            mImpl = new ChangeBoundsKitKat(this);
-        }
     }
 
-    @Override
-    public void captureEndValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureEndValues(transitionValues);
+    public ChangeBounds(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.CHANGE_BOUNDS);
+        boolean resizeClip = TypedArrayUtils.getNamedBoolean(a, (XmlResourceParser) attrs,
+                "resizeClip", Styleable.ChangeBounds.RESIZE_CLIP, false);
+        a.recycle();
+        setResizeClip(resizeClip);
     }
 
-    @Override
-    public void captureStartValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureStartValues(transitionValues);
-    }
-
-    @Override
     @Nullable
-    public Animator createAnimator(@NonNull ViewGroup sceneRoot,
-            @NonNull TransitionValues startValues, @NonNull TransitionValues endValues) {
-        return mImpl.createAnimator(sceneRoot, startValues, endValues);
+    @Override
+    public String[] getTransitionProperties() {
+        return sTransitionProperties;
     }
 
     /**
@@ -70,7 +198,300 @@
      * @see android.view.View#setClipBounds(android.graphics.Rect)
      */
     public void setResizeClip(boolean resizeClip) {
-        ((ChangeBoundsInterface) mImpl).setResizeClip(resizeClip);
+        mResizeClip = resizeClip;
+    }
+
+    /**
+     * Returns true when the ChangeBounds will resize by changing the clip bounds during the
+     * view animation or false when bounds are changed. The default value is false.
+     *
+     * @return true when the ChangeBounds will resize by changing the clip bounds during the
+     * view animation or false when bounds are changed. The default value is false.
+     */
+    public boolean getResizeClip() {
+        return mResizeClip;
+    }
+
+    private void captureValues(TransitionValues values) {
+        View view = values.view;
+
+        if (ViewCompat.isLaidOut(view) || view.getWidth() != 0 || view.getHeight() != 0) {
+            values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
+                    view.getRight(), view.getBottom()));
+            values.values.put(PROPNAME_PARENT, values.view.getParent());
+            if (mReparent) {
+                values.view.getLocationInWindow(mTempLocation);
+                values.values.put(PROPNAME_WINDOW_X, mTempLocation[0]);
+                values.values.put(PROPNAME_WINDOW_Y, mTempLocation[1]);
+            }
+            if (mResizeClip) {
+                values.values.put(PROPNAME_CLIP, ViewCompat.getClipBounds(view));
+            }
+        }
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    private boolean parentMatches(View startParent, View endParent) {
+        boolean parentMatches = true;
+        if (mReparent) {
+            TransitionValues endValues = getMatchedTransitionValues(startParent, true);
+            if (endValues == null) {
+                parentMatches = startParent == endParent;
+            } else {
+                parentMatches = endParent == endValues.view;
+            }
+        }
+        return parentMatches;
+    }
+
+    @Override
+    @Nullable
+    public Animator createAnimator(@NonNull final ViewGroup sceneRoot,
+            @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
+        if (startValues == null || endValues == null) {
+            return null;
+        }
+        Map<String, Object> startParentVals = startValues.values;
+        Map<String, Object> endParentVals = endValues.values;
+        ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
+        ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
+        if (startParent == null || endParent == null) {
+            return null;
+        }
+        final View view = endValues.view;
+        if (parentMatches(startParent, endParent)) {
+            Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+            Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+            final int startLeft = startBounds.left;
+            final int endLeft = endBounds.left;
+            final int startTop = startBounds.top;
+            final int endTop = endBounds.top;
+            final int startRight = startBounds.right;
+            final int endRight = endBounds.right;
+            final int startBottom = startBounds.bottom;
+            final int endBottom = endBounds.bottom;
+            final int startWidth = startRight - startLeft;
+            final int startHeight = startBottom - startTop;
+            final int endWidth = endRight - endLeft;
+            final int endHeight = endBottom - endTop;
+            Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP);
+            Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP);
+            int numChanges = 0;
+            if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) {
+                if (startLeft != endLeft || startTop != endTop) ++numChanges;
+                if (startRight != endRight || startBottom != endBottom) ++numChanges;
+            }
+            if ((startClip != null && !startClip.equals(endClip))
+                    || (startClip == null && endClip != null)) {
+                ++numChanges;
+            }
+            if (numChanges > 0) {
+                Animator anim;
+                if (!mResizeClip) {
+                    ViewUtils.setLeftTopRightBottom(view, startLeft, startTop, startRight,
+                            startBottom);
+                    if (numChanges == 2) {
+                        if (startWidth == endWidth && startHeight == endHeight) {
+                            Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
+                                    endTop);
+                            anim = ObjectAnimatorUtils.ofPointF(view, POSITION_PROPERTY,
+                                    topLeftPath);
+                        } else {
+                            final ViewBounds viewBounds = new ViewBounds(view);
+                            Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
+                                    endLeft, endTop);
+                            ObjectAnimator topLeftAnimator = ObjectAnimatorUtils
+                                    .ofPointF(viewBounds, TOP_LEFT_PROPERTY, topLeftPath);
+
+                            Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
+                                    endRight, endBottom);
+                            ObjectAnimator bottomRightAnimator = ObjectAnimatorUtils.ofPointF(
+                                    viewBounds, BOTTOM_RIGHT_PROPERTY, bottomRightPath);
+                            AnimatorSet set = new AnimatorSet();
+                            set.playTogether(topLeftAnimator, bottomRightAnimator);
+                            anim = set;
+                            set.addListener(new AnimatorListenerAdapter() {
+                                // We need a strong reference to viewBounds until the
+                                // animator ends (The ObjectAnimator holds only a weak reference).
+                                @SuppressWarnings("unused")
+                                private ViewBounds mViewBounds = viewBounds;
+                            });
+                        }
+                    } else if (startLeft != endLeft || startTop != endTop) {
+                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
+                                endLeft, endTop);
+                        anim = ObjectAnimatorUtils.ofPointF(view, TOP_LEFT_ONLY_PROPERTY,
+                                topLeftPath);
+                    } else {
+                        Path bottomRight = getPathMotion().getPath(startRight, startBottom,
+                                endRight, endBottom);
+                        anim = ObjectAnimatorUtils.ofPointF(view, BOTTOM_RIGHT_ONLY_PROPERTY,
+                                bottomRight);
+                    }
+                } else {
+                    int maxWidth = Math.max(startWidth, endWidth);
+                    int maxHeight = Math.max(startHeight, endHeight);
+
+                    ViewUtils.setLeftTopRightBottom(view, startLeft, startTop, startLeft + maxWidth,
+                            startTop + maxHeight);
+
+                    ObjectAnimator positionAnimator = null;
+                    if (startLeft != endLeft || startTop != endTop) {
+                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
+                                endTop);
+                        positionAnimator = ObjectAnimatorUtils.ofPointF(view, POSITION_PROPERTY,
+                                topLeftPath);
+                    }
+                    final Rect finalClip = endClip;
+                    if (startClip == null) {
+                        startClip = new Rect(0, 0, startWidth, startHeight);
+                    }
+                    if (endClip == null) {
+                        endClip = new Rect(0, 0, endWidth, endHeight);
+                    }
+                    ObjectAnimator clipAnimator = null;
+                    if (!startClip.equals(endClip)) {
+                        ViewCompat.setClipBounds(view, startClip);
+                        clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
+                                startClip, endClip);
+                        clipAnimator.addListener(new AnimatorListenerAdapter() {
+                            private boolean mIsCanceled;
+
+                            @Override
+                            public void onAnimationCancel(Animator animation) {
+                                mIsCanceled = true;
+                            }
+
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                if (!mIsCanceled) {
+                                    ViewCompat.setClipBounds(view, finalClip);
+                                    ViewUtils.setLeftTopRightBottom(view, endLeft, endTop, endRight,
+                                            endBottom);
+                                }
+                            }
+                        });
+                    }
+                    anim = TransitionUtils.mergeAnimators(positionAnimator,
+                            clipAnimator);
+                }
+                if (view.getParent() instanceof ViewGroup) {
+                    final ViewGroup parent = (ViewGroup) view.getParent();
+                    ViewGroupUtils.suppressLayout(parent, true);
+                    TransitionListener transitionListener = new TransitionListenerAdapter() {
+                        boolean mCanceled = false;
+
+                        @Override
+                        public void onTransitionCancel(@NonNull Transition transition) {
+                            ViewGroupUtils.suppressLayout(parent, false);
+                            mCanceled = true;
+                        }
+
+                        @Override
+                        public void onTransitionEnd(@NonNull Transition transition) {
+                            if (!mCanceled) {
+                                ViewGroupUtils.suppressLayout(parent, false);
+                            }
+                            transition.removeListener(this);
+                        }
+
+                        @Override
+                        public void onTransitionPause(@NonNull Transition transition) {
+                            ViewGroupUtils.suppressLayout(parent, false);
+                        }
+
+                        @Override
+                        public void onTransitionResume(@NonNull Transition transition) {
+                            ViewGroupUtils.suppressLayout(parent, true);
+                        }
+                    };
+                    addListener(transitionListener);
+                }
+                return anim;
+            }
+        } else {
+            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
+            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
+            int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
+            int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
+            // TODO: also handle size changes: check bounds and animate size changes
+            if (startX != endX || startY != endY) {
+                sceneRoot.getLocationInWindow(mTempLocation);
+                Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas canvas = new Canvas(bitmap);
+                view.draw(canvas);
+                @SuppressWarnings("deprecation") final BitmapDrawable drawable = new BitmapDrawable(
+                        bitmap);
+                final float transitionAlpha = ViewUtils.getTransitionAlpha(view);
+                ViewUtils.setTransitionAlpha(view, 0);
+                ViewUtils.getOverlay(sceneRoot).add(drawable);
+                Path topLeftPath = getPathMotion().getPath(startX - mTempLocation[0],
+                        startY - mTempLocation[1], endX - mTempLocation[0],
+                        endY - mTempLocation[1]);
+                PropertyValuesHolder origin = PropertyValuesHolderUtils.ofPointF(
+                        DRAWABLE_ORIGIN_PROPERTY, topLeftPath);
+                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin);
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        ViewUtils.getOverlay(sceneRoot).remove(drawable);
+                        ViewUtils.setTransitionAlpha(view, transitionAlpha);
+                    }
+                });
+                return anim;
+            }
+        }
+        return null;
+    }
+
+    private static class ViewBounds {
+
+        private int mLeft;
+        private int mTop;
+        private int mRight;
+        private int mBottom;
+        private View mView;
+        private int mTopLeftCalls;
+        private int mBottomRightCalls;
+
+        ViewBounds(View view) {
+            mView = view;
+        }
+
+        void setTopLeft(PointF topLeft) {
+            mLeft = Math.round(topLeft.x);
+            mTop = Math.round(topLeft.y);
+            mTopLeftCalls++;
+            if (mTopLeftCalls == mBottomRightCalls) {
+                setLeftTopRightBottom();
+            }
+        }
+
+        void setBottomRight(PointF bottomRight) {
+            mRight = Math.round(bottomRight.x);
+            mBottom = Math.round(bottomRight.y);
+            mBottomRightCalls++;
+            if (mTopLeftCalls == mBottomRightCalls) {
+                setLeftTopRightBottom();
+            }
+        }
+
+        private void setLeftTopRightBottom() {
+            ViewUtils.setLeftTopRightBottom(mView, mLeft, mTop, mRight, mBottom);
+            mTopLeftCalls = 0;
+            mBottomRightCalls = 0;
+        }
+
     }
 
 }
diff --git a/transition/src/android/support/transition/ChangeClipBounds.java b/transition/src/android/support/transition/ChangeClipBounds.java
new file mode 100644
index 0000000..9449a9c
--- /dev/null
+++ b/transition/src/android/support/transition/ChangeClipBounds.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * ChangeClipBounds captures the {@link android.view.View#getClipBounds()} before and after the
+ * scene change and animates those changes during the transition.
+ *
+ * <p>Prior to API 18 this does nothing.</p>
+ */
+public class ChangeClipBounds extends Transition {
+
+    private static final String PROPNAME_CLIP = "android:clipBounds:clip";
+    private static final String PROPNAME_BOUNDS = "android:clipBounds:bounds";
+
+    private static final String[] sTransitionProperties = {
+            PROPNAME_CLIP,
+    };
+
+    @Override
+    public String[] getTransitionProperties() {
+        return sTransitionProperties;
+    }
+
+    public ChangeClipBounds() {
+    }
+
+    public ChangeClipBounds(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private void captureValues(TransitionValues values) {
+        View view = values.view;
+        if (view.getVisibility() == View.GONE) {
+            return;
+        }
+
+        Rect clip = ViewCompat.getClipBounds(view);
+        values.values.put(PROPNAME_CLIP, clip);
+        if (clip == null) {
+            Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+            values.values.put(PROPNAME_BOUNDS, bounds);
+        }
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public Animator createAnimator(@NonNull final ViewGroup sceneRoot, TransitionValues startValues,
+            TransitionValues endValues) {
+        if (startValues == null || endValues == null
+                || !startValues.values.containsKey(PROPNAME_CLIP)
+                || !endValues.values.containsKey(PROPNAME_CLIP)) {
+            return null;
+        }
+        Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
+        Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
+        final boolean endIsNull = end == null;
+        if (start == null && end == null) {
+            return null; // No animation required since there is no clip.
+        }
+
+        if (start == null) {
+            start = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+        } else if (end == null) {
+            end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+        }
+        if (start.equals(end)) {
+            return null;
+        }
+
+        ViewCompat.setClipBounds(endValues.view, start);
+        RectEvaluator evaluator = new RectEvaluator(new Rect());
+        ObjectAnimator animator = ObjectAnimator.ofObject(endValues.view, ViewUtils.CLIP_BOUNDS,
+                evaluator, start, end);
+        if (endIsNull) {
+            final View endView = endValues.view;
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    ViewCompat.setClipBounds(endView, null);
+                }
+            });
+        }
+        return animator;
+    }
+}
diff --git a/transition/src/android/support/transition/ChangeImageTransform.java b/transition/src/android/support/transition/ChangeImageTransform.java
new file mode 100644
index 0000000..aa9e35c
--- /dev/null
+++ b/transition/src/android/support/transition/ChangeImageTransform.java
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.Map;
+
+/**
+ * This Transition captures an ImageView's matrix before and after the
+ * scene change and animates it during the transition.
+ *
+ * <p>In combination with ChangeBounds, ChangeImageTransform allows ImageViews
+ * that change size, shape, or {@link android.widget.ImageView.ScaleType} to animate contents
+ * smoothly.</p>
+ */
+public class ChangeImageTransform extends Transition {
+
+    private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix";
+    private static final String PROPNAME_BOUNDS = "android:changeImageTransform:bounds";
+
+    private static final String[] sTransitionProperties = {
+            PROPNAME_MATRIX,
+            PROPNAME_BOUNDS,
+    };
+
+    private static final TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() {
+        @Override
+        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
+            return null;
+        }
+    };
+
+    private static final Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY =
+            new Property<ImageView, Matrix>(Matrix.class, "animatedTransform") {
+                @Override
+                public void set(ImageView view, Matrix matrix) {
+                    ImageViewUtils.animateTransform(view, matrix);
+                }
+
+                @Override
+                public Matrix get(ImageView object) {
+                    return null;
+                }
+            };
+
+    public ChangeImageTransform() {
+    }
+
+    public ChangeImageTransform(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
+            return;
+        }
+        ImageView imageView = (ImageView) view;
+        Drawable drawable = imageView.getDrawable();
+        if (drawable == null) {
+            return;
+        }
+        Map<String, Object> values = transitionValues.values;
+
+        int left = view.getLeft();
+        int top = view.getTop();
+        int right = view.getRight();
+        int bottom = view.getBottom();
+
+        Rect bounds = new Rect(left, top, right, bottom);
+        values.put(PROPNAME_BOUNDS, bounds);
+        values.put(PROPNAME_MATRIX, copyImageMatrix(imageView));
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public String[] getTransitionProperties() {
+        return sTransitionProperties;
+    }
+
+    /**
+     * Creates an Animator for ImageViews moving, changing dimensions, and/or changing
+     * {@link android.widget.ImageView.ScaleType}.
+     *
+     * @param sceneRoot   The root of the transition hierarchy.
+     * @param startValues The values for a specific target in the start scene.
+     * @param endValues   The values for the target in the end scene.
+     * @return An Animator to move an ImageView or null if the View is not an ImageView,
+     * the Drawable changed, the View is not VISIBLE, or there was no change.
+     */
+    @Override
+    public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues,
+            final TransitionValues endValues) {
+        if (startValues == null || endValues == null) {
+            return null;
+        }
+        Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+        Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+        if (startBounds == null || endBounds == null) {
+            return null;
+        }
+
+        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+
+        boolean matricesEqual = (startMatrix == null && endMatrix == null)
+                || (startMatrix != null && startMatrix.equals(endMatrix));
+
+        if (startBounds.equals(endBounds) && matricesEqual) {
+            return null;
+        }
+
+        final ImageView imageView = (ImageView) endValues.view;
+        Drawable drawable = imageView.getDrawable();
+        int drawableWidth = drawable.getIntrinsicWidth();
+        int drawableHeight = drawable.getIntrinsicHeight();
+
+        ImageViewUtils.startAnimateTransform(imageView);
+
+        ObjectAnimator animator;
+        if (drawableWidth == 0 || drawableHeight == 0) {
+            animator = createNullAnimator(imageView);
+        } else {
+            if (startMatrix == null) {
+                startMatrix = MatrixUtils.IDENTITY_MATRIX;
+            }
+            if (endMatrix == null) {
+                endMatrix = MatrixUtils.IDENTITY_MATRIX;
+            }
+            ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
+            animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
+        }
+
+        ImageViewUtils.reserveEndAnimateTransform(imageView, animator);
+
+        return animator;
+    }
+
+    private ObjectAnimator createNullAnimator(ImageView imageView) {
+        return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
+                NULL_MATRIX_EVALUATOR, null, null);
+    }
+
+    private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
+            final Matrix endMatrix) {
+        return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
+                new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix);
+    }
+
+    private static Matrix copyImageMatrix(ImageView view) {
+        switch (view.getScaleType()) {
+            case FIT_XY:
+                return fitXYMatrix(view);
+            case CENTER_CROP:
+                return centerCropMatrix(view);
+            default:
+                return new Matrix(view.getImageMatrix());
+        }
+    }
+
+    /**
+     * Calculates the image transformation matrix for an ImageView with ScaleType FIT_XY. This
+     * needs to be manually calculated as the platform does not give us the value for this case.
+     */
+    private static Matrix fitXYMatrix(ImageView view) {
+        final Drawable image = view.getDrawable();
+        final Matrix matrix = new Matrix();
+        matrix.postScale(
+                ((float) view.getWidth()) / image.getIntrinsicWidth(),
+                ((float) view.getHeight()) / image.getIntrinsicHeight());
+        return matrix;
+    }
+
+    /**
+     * Calculates the image transformation matrix for an ImageView with ScaleType CENTER_CROP. This
+     * needs to be manually calculated for consistent behavior across all the API levels.
+     */
+    private static Matrix centerCropMatrix(ImageView view) {
+        final Drawable image = view.getDrawable();
+        final int imageWidth = image.getIntrinsicWidth();
+        final int imageViewWidth = view.getWidth();
+        final float scaleX = ((float) imageViewWidth) / imageWidth;
+
+        final int imageHeight = image.getIntrinsicHeight();
+        final int imageViewHeight = view.getHeight();
+        final float scaleY = ((float) imageViewHeight) / imageHeight;
+
+        final float maxScale = Math.max(scaleX, scaleY);
+
+        final float width = imageWidth * maxScale;
+        final float height = imageHeight * maxScale;
+        final int tx = Math.round((imageViewWidth - width) / 2f);
+        final int ty = Math.round((imageViewHeight - height) / 2f);
+
+        final Matrix matrix = new Matrix();
+        matrix.postScale(maxScale, maxScale);
+        matrix.postTranslate(tx, ty);
+        return matrix;
+    }
+
+}
diff --git a/transition/src/android/support/transition/ChangeScroll.java b/transition/src/android/support/transition/ChangeScroll.java
new file mode 100644
index 0000000..1cdd3d4
--- /dev/null
+++ b/transition/src/android/support/transition/ChangeScroll.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+/**
+ * This transition captures the scroll properties of targets before and after
+ * the scene change and animates any changes.
+ */
+public class ChangeScroll extends Transition {
+
+    private static final String PROPNAME_SCROLL_X = "android:changeScroll:x";
+    private static final String PROPNAME_SCROLL_Y = "android:changeScroll:y";
+
+    private static final String[] PROPERTIES = {
+            PROPNAME_SCROLL_X,
+            PROPNAME_SCROLL_Y,
+    };
+
+    public ChangeScroll() {}
+
+    public ChangeScroll(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Nullable
+    @Override
+    public String[] getTransitionProperties() {
+        return PROPERTIES;
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        transitionValues.values.put(PROPNAME_SCROLL_X, transitionValues.view.getScrollX());
+        transitionValues.values.put(PROPNAME_SCROLL_Y, transitionValues.view.getScrollY());
+    }
+
+    @Nullable
+    @Override
+    public Animator createAnimator(@NonNull ViewGroup sceneRoot,
+            @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
+        if (startValues == null || endValues == null) {
+            return null;
+        }
+        final View view = endValues.view;
+        int startX = (Integer) startValues.values.get(PROPNAME_SCROLL_X);
+        int endX = (Integer) endValues.values.get(PROPNAME_SCROLL_X);
+        int startY = (Integer) startValues.values.get(PROPNAME_SCROLL_Y);
+        int endY = (Integer) endValues.values.get(PROPNAME_SCROLL_Y);
+        Animator scrollXAnimator = null;
+        Animator scrollYAnimator = null;
+        if (startX != endX) {
+            view.setScrollX(startX);
+            scrollXAnimator = ObjectAnimator.ofInt(view, "scrollX", startX, endX);
+        }
+        if (startY != endY) {
+            view.setScrollY(startY);
+            scrollYAnimator = ObjectAnimator.ofInt(view, "scrollY", startY, endY);
+        }
+        return TransitionUtils.mergeAnimators(scrollXAnimator, scrollYAnimator);
+    }
+
+}
diff --git a/transition/src/android/support/transition/ChangeTransform.java b/transition/src/android/support/transition/ChangeTransform.java
new file mode 100644
index 0000000..f0adc08
--- /dev/null
+++ b/transition/src/android/support/transition/ChangeTransform.java
@@ -0,0 +1,583 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * This Transition captures scale and rotation for Views before and after the
+ * scene change and animates those changes during the transition.
+ *
+ * A change in parent is handled as well by capturing the transforms from
+ * the parent before and after the scene change and animating those during the
+ * transition.
+ */
+public class ChangeTransform extends Transition {
+
+    private static final String PROPNAME_MATRIX = "android:changeTransform:matrix";
+    private static final String PROPNAME_TRANSFORMS = "android:changeTransform:transforms";
+    private static final String PROPNAME_PARENT = "android:changeTransform:parent";
+    private static final String PROPNAME_PARENT_MATRIX = "android:changeTransform:parentMatrix";
+    private static final String PROPNAME_INTERMEDIATE_PARENT_MATRIX =
+            "android:changeTransform:intermediateParentMatrix";
+    private static final String PROPNAME_INTERMEDIATE_MATRIX =
+            "android:changeTransform:intermediateMatrix";
+
+    private static final String[] sTransitionProperties = {
+            PROPNAME_MATRIX,
+            PROPNAME_TRANSFORMS,
+            PROPNAME_PARENT_MATRIX,
+    };
+
+    /**
+     * This property sets the animation matrix properties that are not translations.
+     */
+    private static final Property<PathAnimatorMatrix, float[]> NON_TRANSLATIONS_PROPERTY =
+            new Property<PathAnimatorMatrix, float[]>(float[].class, "nonTranslations") {
+                @Override
+                public float[] get(PathAnimatorMatrix object) {
+                    return null;
+                }
+
+                @Override
+                public void set(PathAnimatorMatrix object, float[] value) {
+                    object.setValues(value);
+                }
+            };
+
+    /**
+     * This property sets the translation animation matrix properties.
+     */
+    private static final Property<PathAnimatorMatrix, PointF> TRANSLATIONS_PROPERTY =
+            new Property<PathAnimatorMatrix, PointF>(PointF.class, "translations") {
+                @Override
+                public PointF get(PathAnimatorMatrix object) {
+                    return null;
+                }
+
+                @Override
+                public void set(PathAnimatorMatrix object, PointF value) {
+                    object.setTranslation(value);
+                }
+            };
+
+    /**
+     * Newer platforms suppress view removal at the beginning of the animation.
+     */
+    private static final boolean SUPPORTS_VIEW_REMOVAL_SUPPRESSION = Build.VERSION.SDK_INT >= 21;
+
+    private boolean mUseOverlay = true;
+    private boolean mReparent = true;
+    private Matrix mTempMatrix = new Matrix();
+
+    public ChangeTransform() {
+    }
+
+    public ChangeTransform(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.CHANGE_TRANSFORM);
+        mUseOverlay = TypedArrayUtils.getNamedBoolean(a, (XmlPullParser) attrs,
+                "reparentWithOverlay", Styleable.ChangeTransform.REPARENT_WITH_OVERLAY, true);
+        mReparent = TypedArrayUtils.getNamedBoolean(a, (XmlPullParser) attrs,
+                "reparent", Styleable.ChangeTransform.REPARENT, true);
+        a.recycle();
+    }
+
+    /**
+     * Returns whether changes to parent should use an overlay or not. When the parent
+     * change doesn't use an overlay, it affects the transforms of the child. The
+     * default value is <code>true</code>.
+     *
+     * <p>Note: when Overlays are not used when a parent changes, a view can be clipped when
+     * it moves outside the bounds of its parent. Setting
+     * {@link android.view.ViewGroup#setClipChildren(boolean)} and
+     * {@link android.view.ViewGroup#setClipToPadding(boolean)} can help. Also, when
+     * Overlays are not used and the parent is animating its location, the position of the
+     * child view will be relative to its parent's final position, so it may appear to "jump"
+     * at the beginning.</p>
+     *
+     * @return <code>true</code> when a changed parent should execute the transition
+     * inside the scene root's overlay or <code>false</code> if a parent change only
+     * affects the transform of the transitioning view.
+     */
+    public boolean getReparentWithOverlay() {
+        return mUseOverlay;
+    }
+
+    /**
+     * Sets whether changes to parent should use an overlay or not. When the parent
+     * change doesn't use an overlay, it affects the transforms of the child. The
+     * default value is <code>true</code>.
+     *
+     * <p>Note: when Overlays are not used when a parent changes, a view can be clipped when
+     * it moves outside the bounds of its parent. Setting
+     * {@link android.view.ViewGroup#setClipChildren(boolean)} and
+     * {@link android.view.ViewGroup#setClipToPadding(boolean)} can help. Also, when
+     * Overlays are not used and the parent is animating its location, the position of the
+     * child view will be relative to its parent's final position, so it may appear to "jump"
+     * at the beginning.</p>
+     *
+     * @param reparentWithOverlay <code>true</code> when a changed parent should execute the
+     *                            transition inside the scene root's overlay or <code>false</code>
+     *                            if a parent change only affects the transform of the
+     *                            transitioning view.
+     */
+    public void setReparentWithOverlay(boolean reparentWithOverlay) {
+        mUseOverlay = reparentWithOverlay;
+    }
+
+    /**
+     * Returns whether parent changes will be tracked by the ChangeTransform. If parent
+     * changes are tracked, then the transform will adjust to the transforms of the
+     * different parents. If they aren't tracked, only the transforms of the transitioning
+     * view will be tracked. Default is true.
+     *
+     * @return whether parent changes will be tracked by the ChangeTransform.
+     */
+    public boolean getReparent() {
+        return mReparent;
+    }
+
+    /**
+     * Sets whether parent changes will be tracked by the ChangeTransform. If parent
+     * changes are tracked, then the transform will adjust to the transforms of the
+     * different parents. If they aren't tracked, only the transforms of the transitioning
+     * view will be tracked. Default is true.
+     *
+     * @param reparent Set to true to track parent changes or false to only track changes
+     *                 of the transitioning view without considering the parent change.
+     */
+    public void setReparent(boolean reparent) {
+        mReparent = reparent;
+    }
+
+    @Override
+    public String[] getTransitionProperties() {
+        return sTransitionProperties;
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        if (view.getVisibility() == View.GONE) {
+            return;
+        }
+        transitionValues.values.put(PROPNAME_PARENT, view.getParent());
+        Transforms transforms = new Transforms(view);
+        transitionValues.values.put(PROPNAME_TRANSFORMS, transforms);
+        Matrix matrix = view.getMatrix();
+        if (matrix == null || matrix.isIdentity()) {
+            matrix = null;
+        } else {
+            matrix = new Matrix(matrix);
+        }
+        transitionValues.values.put(PROPNAME_MATRIX, matrix);
+        if (mReparent) {
+            Matrix parentMatrix = new Matrix();
+            ViewGroup parent = (ViewGroup) view.getParent();
+            ViewUtils.transformMatrixToGlobal(parent, parentMatrix);
+            parentMatrix.preTranslate(-parent.getScrollX(), -parent.getScrollY());
+            transitionValues.values.put(PROPNAME_PARENT_MATRIX, parentMatrix);
+            transitionValues.values.put(PROPNAME_INTERMEDIATE_MATRIX,
+                    view.getTag(R.id.transition_transform));
+            transitionValues.values.put(PROPNAME_INTERMEDIATE_PARENT_MATRIX,
+                    view.getTag(R.id.parent_matrix));
+        }
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+        if (!SUPPORTS_VIEW_REMOVAL_SUPPRESSION) {
+            // We still don't know if the view is removed or not, but we need to do this here, or
+            // the view will be actually removed, resulting in flickering at the beginning of the
+            // animation. We are canceling this afterwards.
+            ((ViewGroup) transitionValues.view.getParent()).startViewTransition(
+                    transitionValues.view);
+        }
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues,
+            TransitionValues endValues) {
+        if (startValues == null || endValues == null
+                || !startValues.values.containsKey(PROPNAME_PARENT)
+                || !endValues.values.containsKey(PROPNAME_PARENT)) {
+            return null;
+        }
+
+        ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
+        ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
+        boolean handleParentChange = mReparent && !parentsMatch(startParent, endParent);
+
+        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_INTERMEDIATE_MATRIX);
+        if (startMatrix != null) {
+            startValues.values.put(PROPNAME_MATRIX, startMatrix);
+        }
+
+        Matrix startParentMatrix = (Matrix)
+                startValues.values.get(PROPNAME_INTERMEDIATE_PARENT_MATRIX);
+        if (startParentMatrix != null) {
+            startValues.values.put(PROPNAME_PARENT_MATRIX, startParentMatrix);
+        }
+
+        // First handle the parent change:
+        if (handleParentChange) {
+            setMatricesForParent(startValues, endValues);
+        }
+
+        // Next handle the normal matrix transform:
+        ObjectAnimator transformAnimator = createTransformAnimator(startValues, endValues,
+                handleParentChange);
+
+        if (handleParentChange && transformAnimator != null && mUseOverlay) {
+            createGhostView(sceneRoot, startValues, endValues);
+        } else if (!SUPPORTS_VIEW_REMOVAL_SUPPRESSION) {
+            // We didn't need to suppress the view removal in this case. Cancel the suppression.
+            startParent.endViewTransition(startValues.view);
+        }
+
+        return transformAnimator;
+    }
+
+    private ObjectAnimator createTransformAnimator(TransitionValues startValues,
+            TransitionValues endValues, final boolean handleParentChange) {
+        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+
+        if (startMatrix == null) {
+            startMatrix = MatrixUtils.IDENTITY_MATRIX;
+        }
+
+        if (endMatrix == null) {
+            endMatrix = MatrixUtils.IDENTITY_MATRIX;
+        }
+
+        if (startMatrix.equals(endMatrix)) {
+            return null;
+        }
+
+        final Transforms transforms = (Transforms) endValues.values.get(PROPNAME_TRANSFORMS);
+
+        // clear the transform properties so that we can use the animation matrix instead
+        final View view = endValues.view;
+        setIdentityTransforms(view);
+
+        final float[] startMatrixValues = new float[9];
+        startMatrix.getValues(startMatrixValues);
+        final float[] endMatrixValues = new float[9];
+        endMatrix.getValues(endMatrixValues);
+        final PathAnimatorMatrix pathAnimatorMatrix =
+                new PathAnimatorMatrix(view, startMatrixValues);
+
+        PropertyValuesHolder valuesProperty = PropertyValuesHolder.ofObject(
+                NON_TRANSLATIONS_PROPERTY, new FloatArrayEvaluator(new float[9]),
+                startMatrixValues, endMatrixValues);
+        Path path = getPathMotion().getPath(startMatrixValues[Matrix.MTRANS_X],
+                startMatrixValues[Matrix.MTRANS_Y], endMatrixValues[Matrix.MTRANS_X],
+                endMatrixValues[Matrix.MTRANS_Y]);
+        PropertyValuesHolder translationProperty = PropertyValuesHolderUtils.ofPointF(
+                TRANSLATIONS_PROPERTY, path);
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(pathAnimatorMatrix,
+                valuesProperty, translationProperty);
+
+        final Matrix finalEndMatrix = endMatrix;
+
+        AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+            private boolean mIsCanceled;
+            private Matrix mTempMatrix = new Matrix();
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mIsCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mIsCanceled) {
+                    if (handleParentChange && mUseOverlay) {
+                        setCurrentMatrix(finalEndMatrix);
+                    } else {
+                        view.setTag(R.id.transition_transform, null);
+                        view.setTag(R.id.parent_matrix, null);
+                    }
+                }
+                ViewUtils.setAnimationMatrix(view, null);
+                transforms.restore(view);
+            }
+
+            @Override
+            public void onAnimationPause(Animator animation) {
+                Matrix currentMatrix = pathAnimatorMatrix.getMatrix();
+                setCurrentMatrix(currentMatrix);
+            }
+
+            @Override
+            public void onAnimationResume(Animator animation) {
+                setIdentityTransforms(view);
+            }
+
+            private void setCurrentMatrix(Matrix currentMatrix) {
+                mTempMatrix.set(currentMatrix);
+                view.setTag(R.id.transition_transform, mTempMatrix);
+                transforms.restore(view);
+            }
+        };
+
+        animator.addListener(listener);
+        AnimatorUtils.addPauseListener(animator, listener);
+        return animator;
+    }
+
+    private boolean parentsMatch(ViewGroup startParent, ViewGroup endParent) {
+        boolean parentsMatch = false;
+        if (!isValidTarget(startParent) || !isValidTarget(endParent)) {
+            parentsMatch = startParent == endParent;
+        } else {
+            TransitionValues endValues = getMatchedTransitionValues(startParent, true);
+            if (endValues != null) {
+                parentsMatch = endParent == endValues.view;
+            }
+        }
+        return parentsMatch;
+    }
+
+    private void createGhostView(final ViewGroup sceneRoot, TransitionValues startValues,
+            TransitionValues endValues) {
+        View view = endValues.view;
+
+        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_PARENT_MATRIX);
+        Matrix localEndMatrix = new Matrix(endMatrix);
+        ViewUtils.transformMatrixToLocal(sceneRoot, localEndMatrix);
+
+        GhostViewImpl ghostView = GhostViewUtils.addGhost(view, sceneRoot, localEndMatrix);
+        if (ghostView == null) {
+            return;
+        }
+        // Ask GhostView to actually remove the start view when it starts drawing the animation.
+        ghostView.reserveEndViewTransition((ViewGroup) startValues.values.get(PROPNAME_PARENT),
+                startValues.view);
+
+        Transition outerTransition = this;
+        while (outerTransition.mParent != null) {
+            outerTransition = outerTransition.mParent;
+        }
+
+        GhostListener listener = new GhostListener(view, ghostView);
+        outerTransition.addListener(listener);
+
+        // We cannot do this for older platforms or it invalidates the view and results in
+        // flickering, but the view will still be invisible by actually removing it from the parent.
+        if (SUPPORTS_VIEW_REMOVAL_SUPPRESSION) {
+            if (startValues.view != endValues.view) {
+                ViewUtils.setTransitionAlpha(startValues.view, 0);
+            }
+            ViewUtils.setTransitionAlpha(view, 1);
+        }
+    }
+
+    private void setMatricesForParent(TransitionValues startValues, TransitionValues endValues) {
+        Matrix endParentMatrix = (Matrix) endValues.values.get(PROPNAME_PARENT_MATRIX);
+        endValues.view.setTag(R.id.parent_matrix, endParentMatrix);
+
+        Matrix toLocal = mTempMatrix;
+        toLocal.reset();
+        endParentMatrix.invert(toLocal);
+
+        Matrix startLocal = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+        if (startLocal == null) {
+            startLocal = new Matrix();
+            startValues.values.put(PROPNAME_MATRIX, startLocal);
+        }
+
+        Matrix startParentMatrix = (Matrix) startValues.values.get(PROPNAME_PARENT_MATRIX);
+        startLocal.postConcat(startParentMatrix);
+        startLocal.postConcat(toLocal);
+    }
+
+    private static void setIdentityTransforms(View view) {
+        setTransforms(view, 0, 0, 0, 1, 1, 0, 0, 0);
+    }
+
+    private static void setTransforms(View view, float translationX, float translationY,
+            float translationZ, float scaleX, float scaleY, float rotationX,
+            float rotationY, float rotationZ) {
+        view.setTranslationX(translationX);
+        view.setTranslationY(translationY);
+        ViewCompat.setTranslationZ(view, translationZ);
+        view.setScaleX(scaleX);
+        view.setScaleY(scaleY);
+        view.setRotationX(rotationX);
+        view.setRotationY(rotationY);
+        view.setRotation(rotationZ);
+    }
+
+    private static class Transforms {
+
+        final float mTranslationX;
+        final float mTranslationY;
+        final float mTranslationZ;
+        final float mScaleX;
+        final float mScaleY;
+        final float mRotationX;
+        final float mRotationY;
+        final float mRotationZ;
+
+        Transforms(View view) {
+            mTranslationX = view.getTranslationX();
+            mTranslationY = view.getTranslationY();
+            mTranslationZ = ViewCompat.getTranslationZ(view);
+            mScaleX = view.getScaleX();
+            mScaleY = view.getScaleY();
+            mRotationX = view.getRotationX();
+            mRotationY = view.getRotationY();
+            mRotationZ = view.getRotation();
+        }
+
+        public void restore(View view) {
+            setTransforms(view, mTranslationX, mTranslationY, mTranslationZ, mScaleX, mScaleY,
+                    mRotationX, mRotationY, mRotationZ);
+        }
+
+        @Override
+        public boolean equals(Object that) {
+            if (!(that instanceof Transforms)) {
+                return false;
+            }
+            Transforms thatTransform = (Transforms) that;
+            return thatTransform.mTranslationX == mTranslationX
+                    && thatTransform.mTranslationY == mTranslationY
+                    && thatTransform.mTranslationZ == mTranslationZ
+                    && thatTransform.mScaleX == mScaleX
+                    && thatTransform.mScaleY == mScaleY
+                    && thatTransform.mRotationX == mRotationX
+                    && thatTransform.mRotationY == mRotationY
+                    && thatTransform.mRotationZ == mRotationZ;
+        }
+
+        @Override
+        public int hashCode() {
+            int code = mTranslationX != +0.0f ? Float.floatToIntBits(mTranslationX) : 0;
+            code = 31 * code + (mTranslationY != +0.0f ? Float.floatToIntBits(mTranslationY) : 0);
+            code = 31 * code + (mTranslationZ != +0.0f ? Float.floatToIntBits(mTranslationZ) : 0);
+            code = 31 * code + (mScaleX != +0.0f ? Float.floatToIntBits(mScaleX) : 0);
+            code = 31 * code + (mScaleY != +0.0f ? Float.floatToIntBits(mScaleY) : 0);
+            code = 31 * code + (mRotationX != +0.0f ? Float.floatToIntBits(mRotationX) : 0);
+            code = 31 * code + (mRotationY != +0.0f ? Float.floatToIntBits(mRotationY) : 0);
+            code = 31 * code + (mRotationZ != +0.0f ? Float.floatToIntBits(mRotationZ) : 0);
+            return code;
+        }
+
+    }
+
+    private static class GhostListener extends TransitionListenerAdapter {
+
+        private View mView;
+        private GhostViewImpl mGhostView;
+
+        GhostListener(View view, GhostViewImpl ghostView) {
+            mView = view;
+            mGhostView = ghostView;
+        }
+
+        @Override
+        public void onTransitionEnd(@NonNull Transition transition) {
+            transition.removeListener(this);
+            GhostViewUtils.removeGhost(mView);
+            mView.setTag(R.id.transition_transform, null);
+            mView.setTag(R.id.parent_matrix, null);
+        }
+
+        @Override
+        public void onTransitionPause(@NonNull Transition transition) {
+            mGhostView.setVisibility(View.INVISIBLE);
+        }
+
+        @Override
+        public void onTransitionResume(@NonNull Transition transition) {
+            mGhostView.setVisibility(View.VISIBLE);
+        }
+
+    }
+
+    /**
+     * PathAnimatorMatrix allows the translations and the rest of the matrix to be set
+     * separately. This allows the PathMotion to affect the translations while scale
+     * and rotation are evaluated separately.
+     */
+    private static class PathAnimatorMatrix {
+
+        private final Matrix mMatrix = new Matrix();
+        private final View mView;
+        private final float[] mValues;
+        private float mTranslationX;
+        private float mTranslationY;
+
+        PathAnimatorMatrix(View view, float[] values) {
+            mView = view;
+            mValues = values.clone();
+            mTranslationX = mValues[Matrix.MTRANS_X];
+            mTranslationY = mValues[Matrix.MTRANS_Y];
+            setAnimationMatrix();
+        }
+
+        void setValues(float[] values) {
+            System.arraycopy(values, 0, mValues, 0, values.length);
+            setAnimationMatrix();
+        }
+
+        void setTranslation(PointF translation) {
+            mTranslationX = translation.x;
+            mTranslationY = translation.y;
+            setAnimationMatrix();
+        }
+
+        private void setAnimationMatrix() {
+            mValues[Matrix.MTRANS_X] = mTranslationX;
+            mValues[Matrix.MTRANS_Y] = mTranslationY;
+            mMatrix.setValues(mValues);
+            ViewUtils.setAnimationMatrix(mView, mMatrix);
+        }
+
+        Matrix getMatrix() {
+            return mMatrix;
+        }
+    }
+
+}
diff --git a/transition/src/android/support/transition/CircularPropagation.java b/transition/src/android/support/transition/CircularPropagation.java
new file mode 100644
index 0000000..ced4223
--- /dev/null
+++ b/transition/src/android/support/transition/CircularPropagation.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A propagation that varies with the distance to the epicenter of the Transition
+ * or center of the scene if no epicenter exists. When a View is visible in the
+ * start of the transition, Views farther from the epicenter will transition
+ * sooner than Views closer to the epicenter. When a View is not in the start
+ * of the transition or is not visible at the start of the transition, it will
+ * transition sooner when closer to the epicenter and later when farther from
+ * the epicenter. This is the default TransitionPropagation used with
+ * {@link Explode}.
+ */
+public class CircularPropagation extends VisibilityPropagation {
+
+    private float mPropagationSpeed = 3.0f;
+
+    /**
+     * Sets the speed at which transition propagation happens, relative to the duration of the
+     * Transition. A <code>propagationSpeed</code> of 1 means that a View centered farthest from
+     * the epicenter and View centered at the epicenter will have a difference
+     * in start delay of approximately the duration of the Transition. A speed of 2 means the
+     * start delay difference will be approximately half of the duration of the transition. A
+     * value of 0 is illegal, but negative values will invert the propagation.
+     *
+     * @param propagationSpeed The speed at which propagation occurs, relative to the duration
+     *                         of the transition. A speed of 4 means it works 4 times as fast
+     *                         as the duration of the transition. May not be 0.
+     */
+    public void setPropagationSpeed(float propagationSpeed) {
+        if (propagationSpeed == 0) {
+            throw new IllegalArgumentException("propagationSpeed may not be 0");
+        }
+        mPropagationSpeed = propagationSpeed;
+    }
+
+    @Override
+    public long getStartDelay(ViewGroup sceneRoot, Transition transition,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null && endValues == null) {
+            return 0;
+        }
+        int directionMultiplier = 1;
+        TransitionValues positionValues;
+        if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) {
+            positionValues = startValues;
+            directionMultiplier = -1;
+        } else {
+            positionValues = endValues;
+        }
+
+        int viewCenterX = getViewX(positionValues);
+        int viewCenterY = getViewY(positionValues);
+
+        Rect epicenter = transition.getEpicenter();
+        int epicenterX;
+        int epicenterY;
+        if (epicenter != null) {
+            epicenterX = epicenter.centerX();
+            epicenterY = epicenter.centerY();
+        } else {
+            int[] loc = new int[2];
+            sceneRoot.getLocationOnScreen(loc);
+            epicenterX = Math.round(loc[0] + (sceneRoot.getWidth() / 2)
+                    + sceneRoot.getTranslationX());
+            epicenterY = Math.round(loc[1] + (sceneRoot.getHeight() / 2)
+                    + sceneRoot.getTranslationY());
+        }
+        float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY);
+        float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight());
+        float distanceFraction = distance / maxDistance;
+
+        long duration = transition.getDuration();
+        if (duration < 0) {
+            duration = 300;
+        }
+
+        return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
+    }
+
+    private static float distance(float x1, float y1, float x2, float y2) {
+        float x = x2 - x1;
+        float y = y2 - y1;
+        return (float) Math.sqrt((x * x) + (y * y));
+    }
+
+}
diff --git a/transition/src/android/support/transition/Explode.java b/transition/src/android/support/transition/Explode.java
new file mode 100644
index 0000000..38d0fb7
--- /dev/null
+++ b/transition/src/android/support/transition/Explode.java
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes and moves views in or out from the edges of the
+ * scene. Visibility is determined by both the
+ * {@link View#setVisibility(int)} state of the view as well as whether it
+ * is parented in the current view hierarchy. Disappearing Views are
+ * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
+ * TransitionValues, int, TransitionValues, int)}.
+ * <p>Views move away from the focal View or the center of the Scene if
+ * no epicenter was provided.</p>
+ */
+public class Explode extends Visibility {
+
+    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
+    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
+    private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";
+
+    private int[] mTempLoc = new int[2];
+
+    public Explode() {
+        setPropagation(new CircularPropagation());
+    }
+
+    public Explode(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setPropagation(new CircularPropagation());
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        view.getLocationOnScreen(mTempLoc);
+        int left = mTempLoc[0];
+        int top = mTempLoc[1];
+        int right = left + view.getWidth();
+        int bottom = top + view.getHeight();
+        transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        super.captureEndValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public Animator onAppear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (endValues == null) {
+            return null;
+        }
+        Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
+        float endX = view.getTranslationX();
+        float endY = view.getTranslationY();
+        calculateOut(sceneRoot, bounds, mTempLoc);
+        float startX = endX + mTempLoc[0];
+        float startY = endY + mTempLoc[1];
+
+        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
+                startX, startY, endX, endY, sDecelerate);
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null) {
+            return null;
+        }
+        Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
+        int viewPosX = bounds.left;
+        int viewPosY = bounds.top;
+        float startX = view.getTranslationX();
+        float startY = view.getTranslationY();
+        float endX = startX;
+        float endY = startY;
+        int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transition_position);
+        if (interruptedPosition != null) {
+            // We want to have the end position relative to the interrupted position, not
+            // the position it was supposed to start at.
+            endX += interruptedPosition[0] - bounds.left;
+            endY += interruptedPosition[1] - bounds.top;
+            bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
+        }
+        calculateOut(sceneRoot, bounds, mTempLoc);
+        endX += mTempLoc[0];
+        endY += mTempLoc[1];
+
+        return TranslationAnimationCreator.createAnimation(view, startValues,
+                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate);
+    }
+
+    private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
+        sceneRoot.getLocationOnScreen(mTempLoc);
+        int sceneRootX = mTempLoc[0];
+        int sceneRootY = mTempLoc[1];
+        int focalX;
+        int focalY;
+
+        Rect epicenter = getEpicenter();
+        if (epicenter == null) {
+            focalX = sceneRootX + (sceneRoot.getWidth() / 2)
+                    + Math.round(sceneRoot.getTranslationX());
+            focalY = sceneRootY + (sceneRoot.getHeight() / 2)
+                    + Math.round(sceneRoot.getTranslationY());
+        } else {
+            focalX = epicenter.centerX();
+            focalY = epicenter.centerY();
+        }
+
+        int centerX = bounds.centerX();
+        int centerY = bounds.centerY();
+        float xVector = centerX - focalX;
+        float yVector = centerY - focalY;
+
+        if (xVector == 0 && yVector == 0) {
+            // Random direction when View is centered on focal View.
+            xVector = (float) (Math.random() * 2) - 1;
+            yVector = (float) (Math.random() * 2) - 1;
+        }
+        float vectorSize = calculateDistance(xVector, yVector);
+        xVector /= vectorSize;
+        yVector /= vectorSize;
+
+        float maxDistance =
+                calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY);
+
+        outVector[0] = Math.round(maxDistance * xVector);
+        outVector[1] = Math.round(maxDistance * yVector);
+    }
+
+    private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) {
+        int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX);
+        int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY);
+        return calculateDistance(maxX, maxY);
+    }
+
+    private static float calculateDistance(float x, float y) {
+        return (float) Math.sqrt((x * x) + (y * y));
+    }
+
+}
diff --git a/transition/src/android/support/transition/Fade.java b/transition/src/android/support/transition/Fade.java
index a9965a6..eab0f23 100644
--- a/transition/src/android/support/transition/Fade.java
+++ b/transition/src/android/support/transition/Fade.java
@@ -17,9 +17,16 @@
 package android.support.transition;
 
 import android.animation.Animator;
-import android.os.Build;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -48,23 +55,29 @@
  * created from a layout resource file}, then it is considered safe to un-parent
  * the starting scene view in order to fade it out.</p>
  *
- * <p>Unlike the platform version, this does not support use in XML resources.</p>
+ * <p>A Fade transition can be described in a resource file by using the
+ * tag <code>fade</code>, along with the standard
+ * attributes of {@code Fade} and {@link Transition}.</p>
  */
 public class Fade extends Visibility {
 
+    private static final String PROPNAME_TRANSITION_ALPHA = "android:fade:transitionAlpha";
+
+    private static final String LOG_TAG = "Fade";
+
     /**
      * Fading mode used in {@link #Fade(int)} to make the transition
      * operate on targets that are appearing. Maybe be combined with
      * {@link #OUT} to fade both in and out.
      */
-    public static final int IN = 0x1;
+    public static final int IN = Visibility.MODE_IN;
 
     /**
      * Fading mode used in {@link #Fade(int)} to make the transition
      * operate on targets that are disappearing. Maybe be combined with
      * {@link #IN} to fade both in and out.
      */
-    public static final int OUT = 0x2;
+    public static final int OUT = Visibility.MODE_OUT;
 
     /**
      * Constructs a Fade transition that will fade targets in
@@ -74,44 +87,119 @@
      *                   {@link #IN} and {@link #OUT}.
      */
     public Fade(int fadingMode) {
-        super(true);
-        if (Build.VERSION.SDK_INT >= 19) {
-            if (fadingMode > 0) {
-                mImpl = new FadeKitKat(this, fadingMode);
-            } else {
-                mImpl = new FadeKitKat(this);
-            }
-        } else {
-            if (fadingMode > 0) {
-                mImpl = new FadeIcs(this, fadingMode);
-            } else {
-                mImpl = new FadeIcs(this);
-            }
-        }
+        setMode(fadingMode);
     }
 
     /**
      * Constructs a Fade transition that will fade targets in and out.
      */
     public Fade() {
-        this(-1);
     }
 
-    @Override
-    public void captureEndValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureEndValues(transitionValues);
+    public Fade(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.FADE);
+        @Mode
+        int fadingMode = TypedArrayUtils.getNamedInt(a, (XmlResourceParser) attrs, "fadingMode",
+                Styleable.Fade.FADING_MODE, getMode());
+        setMode(fadingMode);
+        a.recycle();
     }
 
     @Override
     public void captureStartValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureStartValues(transitionValues);
+        super.captureStartValues(transitionValues);
+        transitionValues.values.put(PROPNAME_TRANSITION_ALPHA,
+                ViewUtils.getTransitionAlpha(transitionValues.view));
+    }
+
+    /**
+     * Utility method to handle creating and running the Animator.
+     */
+    private Animator createAnimation(final View view, float startAlpha, float endAlpha) {
+        if (startAlpha == endAlpha) {
+            return null;
+        }
+        ViewUtils.setTransitionAlpha(view, startAlpha);
+        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, ViewUtils.TRANSITION_ALPHA,
+                endAlpha);
+        if (DBG) {
+            Log.d(LOG_TAG, "Created animator " + anim);
+        }
+        FadeAnimatorListener listener = new FadeAnimatorListener(view);
+        anim.addListener(listener);
+        addListener(new TransitionListenerAdapter() {
+            @Override
+            public void onTransitionEnd(@NonNull Transition transition) {
+                ViewUtils.setTransitionAlpha(view, 1);
+                ViewUtils.clearNonTransitionAlpha(view);
+                transition.removeListener(this);
+            }
+        });
+        return anim;
     }
 
     @Override
-    @Nullable
-    public Animator createAnimator(@NonNull ViewGroup sceneRoot,
-            @NonNull TransitionValues startValues, @NonNull TransitionValues endValues) {
-        return mImpl.createAnimator(sceneRoot, startValues, endValues);
+    public Animator onAppear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues,
+            TransitionValues endValues) {
+        if (DBG) {
+            View startView = (startValues != null) ? startValues.view : null;
+            Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = "
+                    + startView + ", " + view);
+        }
+        float startAlpha = getStartAlpha(startValues, 0);
+        if (startAlpha == 1) {
+            startAlpha = 0;
+        }
+        return createAnimation(view, startAlpha, 1);
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
+            TransitionValues endValues) {
+        ViewUtils.saveNonTransitionAlpha(view);
+        float startAlpha = getStartAlpha(startValues, 1);
+        return createAnimation(view, startAlpha, 0);
+    }
+
+    private static float getStartAlpha(TransitionValues startValues, float fallbackValue) {
+        float startAlpha = fallbackValue;
+        if (startValues != null) {
+            Float startAlphaFloat = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+            if (startAlphaFloat != null) {
+                startAlpha = startAlphaFloat;
+            }
+        }
+        return startAlpha;
+    }
+
+    private static class FadeAnimatorListener extends AnimatorListenerAdapter {
+
+        private final View mView;
+        private boolean mLayerTypeChanged = false;
+
+        FadeAnimatorListener(View view) {
+            mView = view;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            if (ViewCompat.hasOverlappingRendering(mView)
+                    && mView.getLayerType() == View.LAYER_TYPE_NONE) {
+                mLayerTypeChanged = true;
+                mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            ViewUtils.setTransitionAlpha(mView, 1);
+            if (mLayerTypeChanged) {
+                mView.setLayerType(View.LAYER_TYPE_NONE, null);
+            }
+        }
+
     }
 
 }
diff --git a/transition/src/android/support/transition/FloatArrayEvaluator.java b/transition/src/android/support/transition/FloatArrayEvaluator.java
new file mode 100644
index 0000000..81b97b7
--- /dev/null
+++ b/transition/src/android/support/transition/FloatArrayEvaluator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.TypeEvaluator;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>float[]</code> values.
+ * Each index into the array is treated as a separate value to interpolate. For example,
+ * evaluating <code>{100, 200}</code> and <code>{300, 400}</code> will interpolate the value at
+ * the first index between 100 and 300 and the value at the second index value between 200 and 400.
+ */
+class FloatArrayEvaluator implements TypeEvaluator<float[]> {
+
+    private float[] mArray;
+
+    /**
+     * Create a FloatArrayEvaluator that reuses <code>reuseArray</code> for every evaluate() call.
+     * Caution must be taken to ensure that the value returned from
+     * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
+     * used across threads. The value will be modified on each <code>evaluate()</code> call.
+     *
+     * @param reuseArray The array to modify and return from <code>evaluate</code>.
+     */
+    FloatArrayEvaluator(float[] reuseArray) {
+        mArray = reuseArray;
+    }
+
+    /**
+     * Interpolates the value at each index by the fraction. If
+     * {@link #FloatArrayEvaluator(float[])} was used to construct this object,
+     * <code>reuseArray</code> will be returned, otherwise a new <code>float[]</code>
+     * will be returned.
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start value.
+     * @param endValue   The end value.
+     * @return A <code>float[]</code> where each element is an interpolation between
+     * the same index in startValue and endValue.
+     */
+    @Override
+    public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
+        float[] array = mArray;
+        if (array == null) {
+            array = new float[startValue.length];
+        }
+
+        for (int i = 0; i < array.length; i++) {
+            float start = startValue[i];
+            float end = endValue[i];
+            array[i] = start + (fraction * (end - start));
+        }
+        return array;
+    }
+
+}
diff --git a/transition/src/android/support/transition/GhostViewUtils.java b/transition/src/android/support/transition/GhostViewUtils.java
new file mode 100644
index 0000000..66f01c3
--- /dev/null
+++ b/transition/src/android/support/transition/GhostViewUtils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
+
+class GhostViewUtils {
+
+    private static final GhostViewImpl.Creator CREATOR;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 21) {
+            CREATOR = new GhostViewApi21.Creator();
+        } else {
+            CREATOR = new GhostViewApi14.Creator();
+        }
+    }
+
+    static GhostViewImpl addGhost(View view, ViewGroup viewGroup, Matrix matrix) {
+        return CREATOR.addGhost(view, viewGroup, matrix);
+    }
+
+    static void removeGhost(View view) {
+        CREATOR.removeGhost(view);
+    }
+
+}
diff --git a/transition/src/android/support/transition/ImageViewUtils.java b/transition/src/android/support/transition/ImageViewUtils.java
new file mode 100644
index 0000000..19f0938
--- /dev/null
+++ b/transition/src/android/support/transition/ImageViewUtils.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.graphics.Matrix;
+import android.os.Build;
+import android.widget.ImageView;
+
+class ImageViewUtils {
+
+    private static final ImageViewUtilsImpl IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ImageViewUtilsApi21();
+        } else {
+            IMPL = new ImageViewUtilsApi14();
+        }
+    }
+
+    /**
+     * Starts animating the transformation of the image view. This has to be called before calling
+     * {@link #animateTransform(ImageView, Matrix)}.
+     */
+    static void startAnimateTransform(ImageView view) {
+        IMPL.startAnimateTransform(view);
+    }
+
+    /**
+     * Sets the matrix to animate the content of the image view.
+     */
+    static void animateTransform(ImageView view, Matrix matrix) {
+        IMPL.animateTransform(view, matrix);
+    }
+
+    /**
+     * Reserves that the caller will stop calling {@link #animateTransform(ImageView, Matrix)} when
+     * the specified animator ends.
+     */
+    static void reserveEndAnimateTransform(ImageView view, Animator animator) {
+        IMPL.reserveEndAnimateTransform(view, animator);
+    }
+
+}
diff --git a/transition/src/android/support/transition/MatrixUtils.java b/transition/src/android/support/transition/MatrixUtils.java
new file mode 100644
index 0000000..1d9be29
--- /dev/null
+++ b/transition/src/android/support/transition/MatrixUtils.java
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+
+class MatrixUtils {
+
+    static final Matrix IDENTITY_MATRIX = new Matrix() {
+
+        void oops() {
+            throw new IllegalStateException("Matrix can not be modified");
+        }
+
+        @Override
+        public void set(Matrix src) {
+            oops();
+        }
+
+        @Override
+        public void reset() {
+            oops();
+        }
+
+        @Override
+        public void setTranslate(float dx, float dy) {
+            oops();
+        }
+
+        @Override
+        public void setScale(float sx, float sy, float px, float py) {
+            oops();
+        }
+
+        @Override
+        public void setScale(float sx, float sy) {
+            oops();
+        }
+
+        @Override
+        public void setRotate(float degrees, float px, float py) {
+            oops();
+        }
+
+        @Override
+        public void setRotate(float degrees) {
+            oops();
+        }
+
+        @Override
+        public void setSinCos(float sinValue, float cosValue, float px, float py) {
+            oops();
+        }
+
+        @Override
+        public void setSinCos(float sinValue, float cosValue) {
+            oops();
+        }
+
+        @Override
+        public void setSkew(float kx, float ky, float px, float py) {
+            oops();
+        }
+
+        @Override
+        public void setSkew(float kx, float ky) {
+            oops();
+        }
+
+        @Override
+        public boolean setConcat(Matrix a, Matrix b) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preTranslate(float dx, float dy) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preScale(float sx, float sy, float px, float py) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preScale(float sx, float sy) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preRotate(float degrees, float px, float py) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preRotate(float degrees) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preSkew(float kx, float ky, float px, float py) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preSkew(float kx, float ky) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean preConcat(Matrix other) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postTranslate(float dx, float dy) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postScale(float sx, float sy, float px, float py) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postScale(float sx, float sy) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postRotate(float degrees, float px, float py) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postRotate(float degrees) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postSkew(float kx, float ky, float px, float py) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postSkew(float kx, float ky) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean postConcat(Matrix other) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex,
+                int pointCount) {
+            oops();
+            return false;
+        }
+
+        @Override
+        public void setValues(float[] values) {
+            oops();
+        }
+
+    };
+
+}
diff --git a/transition/src/android/support/transition/ObjectAnimatorUtils.java b/transition/src/android/support/transition/ObjectAnimatorUtils.java
new file mode 100644
index 0000000..d8c132b
--- /dev/null
+++ b/transition/src/android/support/transition/ObjectAnimatorUtils.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.ObjectAnimator;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.os.Build;
+import android.util.Property;
+
+class ObjectAnimatorUtils {
+
+    private static final ObjectAnimatorUtilsImpl IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ObjectAnimatorUtilsApi21();
+        } else {
+            IMPL = new ObjectAnimatorUtilsApi14();
+        }
+    }
+
+    static <T> ObjectAnimator ofPointF(T target, Property<T, PointF> property, Path path) {
+        return IMPL.ofPointF(target, property, path);
+    }
+
+}
diff --git a/transition/src/android/support/transition/PathMotion.java b/transition/src/android/support/transition/PathMotion.java
new file mode 100644
index 0000000..d270a08
--- /dev/null
+++ b/transition/src/android/support/transition/PathMotion.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.content.Context;
+import android.graphics.Path;
+import android.util.AttributeSet;
+
+/**
+ * This base class can be extended to provide motion along a Path to Transitions.
+ *
+ * <p>
+ * Transitions such as {@link android.transition.ChangeBounds} move Views, typically
+ * in a straight path between the start and end positions. Applications that desire to
+ * have these motions move in a curve can change how Views interpolate in two dimensions
+ * by extending PathMotion and implementing {@link #getPath(float, float, float, float)}.
+ * </p>
+ * <p>This may be used in XML as an element inside a transition.</p>
+ * <pre>
+ * {@code
+ * <changeBounds>
+ *     <pathMotion class="my.app.transition.MyPathMotion"/>
+ * </changeBounds>
+ * }
+ * </pre>
+ */
+public abstract class PathMotion {
+
+    public PathMotion() {
+    }
+
+    public PathMotion(Context context, AttributeSet attrs) {
+    }
+
+    /**
+     * Provide a Path to interpolate between two points <code>(startX, startY)</code> and
+     * <code>(endX, endY)</code>. This allows controlled curved motion along two dimensions.
+     *
+     * @param startX The x coordinate of the starting point.
+     * @param startY The y coordinate of the starting point.
+     * @param endX   The x coordinate of the ending point.
+     * @param endY   The y coordinate of the ending point.
+     * @return A Path along which the points should be interpolated. The returned Path
+     * must start at point <code>(startX, startY)</code>, typically using
+     * {@link android.graphics.Path#moveTo(float, float)} and end at <code>(endX, endY)</code>.
+     */
+    public abstract Path getPath(float startX, float startY, float endX, float endY);
+}
diff --git a/transition/src/android/support/transition/PatternPathMotion.java b/transition/src/android/support/transition/PatternPathMotion.java
new file mode 100644
index 0000000..2ac6e72
--- /dev/null
+++ b/transition/src/android/support/transition/PatternPathMotion.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.graphics.PathParser;
+import android.util.AttributeSet;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A PathMotion that takes a Path pattern and applies it to the separation between two points.
+ * The starting point of the Path will be moved to the origin and the end point will be scaled
+ * and rotated so that it matches with the target end point.
+ * <p>This may be used in XML as an element inside a transition.</p>
+ * <pre>{@code
+ * <changeBounds>
+ *     <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
+ * </changeBounds>}
+ * </pre>
+ */
+public class PatternPathMotion extends PathMotion {
+
+    private Path mOriginalPatternPath;
+
+    private final Path mPatternPath = new Path();
+
+    private final Matrix mTempMatrix = new Matrix();
+
+    /**
+     * Constructs a PatternPathMotion with a straight-line pattern.
+     */
+    public PatternPathMotion() {
+        mPatternPath.lineTo(1, 0);
+        mOriginalPatternPath = mPatternPath;
+    }
+
+    public PatternPathMotion(Context context, AttributeSet attrs) {
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.PATTERN_PATH_MOTION);
+        try {
+            String pathData = TypedArrayUtils.getNamedString(a, (XmlPullParser) attrs,
+                    "patternPathData", Styleable.PatternPathMotion.PATTERN_PATH_DATA);
+            if (pathData == null) {
+                throw new RuntimeException("pathData must be supplied for patternPathMotion");
+            }
+            Path pattern = PathParser.createPathFromPathData(pathData);
+            setPatternPath(pattern);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    /**
+     * Creates a PatternPathMotion with the Path defining a pattern of motion between two
+     * coordinates. The pattern will be translated, rotated, and scaled to fit between the start
+     * and end points. The pattern must not be empty and must have the end point differ from the
+     * start point.
+     *
+     * @param patternPath A Path to be used as a pattern for two-dimensional motion.
+     */
+    public PatternPathMotion(Path patternPath) {
+        setPatternPath(patternPath);
+    }
+
+    /**
+     * Returns the Path defining a pattern of motion between two coordinates.
+     * The pattern will be translated, rotated, and scaled to fit between the start and end points.
+     * The pattern must not be empty and must have the end point differ from the start point.
+     *
+     * @return the Path defining a pattern of motion between two coordinates.
+     */
+    public Path getPatternPath() {
+        return mOriginalPatternPath;
+    }
+
+    /**
+     * Sets the Path defining a pattern of motion between two coordinates.
+     * The pattern will be translated, rotated, and scaled to fit between the start and end points.
+     * The pattern must not be empty and must have the end point differ from the start point.
+     *
+     * @param patternPath A Path to be used as a pattern for two-dimensional motion.
+     */
+    public void setPatternPath(Path patternPath) {
+        PathMeasure pathMeasure = new PathMeasure(patternPath, false);
+        float length = pathMeasure.getLength();
+        float[] pos = new float[2];
+        pathMeasure.getPosTan(length, pos, null);
+        float endX = pos[0];
+        float endY = pos[1];
+        pathMeasure.getPosTan(0, pos, null);
+        float startX = pos[0];
+        float startY = pos[1];
+
+        if (startX == endX && startY == endY) {
+            throw new IllegalArgumentException("pattern must not end at the starting point");
+        }
+
+        mTempMatrix.setTranslate(-startX, -startY);
+        float dx = endX - startX;
+        float dy = endY - startY;
+        float distance = distance(dx, dy);
+        float scale = 1 / distance;
+        mTempMatrix.postScale(scale, scale);
+        double angle = Math.atan2(dy, dx);
+        mTempMatrix.postRotate((float) Math.toDegrees(-angle));
+        patternPath.transform(mTempMatrix, mPatternPath);
+        mOriginalPatternPath = patternPath;
+    }
+
+    @Override
+    public Path getPath(float startX, float startY, float endX, float endY) {
+        float dx = endX - startX;
+        float dy = endY - startY;
+        float length = distance(dx, dy);
+        double angle = Math.atan2(dy, dx);
+
+        mTempMatrix.setScale(length, length);
+        mTempMatrix.postRotate((float) Math.toDegrees(angle));
+        mTempMatrix.postTranslate(startX, startY);
+        Path path = new Path();
+        mPatternPath.transform(mTempMatrix, path);
+        return path;
+    }
+
+    private static float distance(float x, float y) {
+        return (float) Math.sqrt((x * x) + (y * y));
+    }
+
+}
diff --git a/transition/src/android/support/transition/PropertyValuesHolderUtils.java b/transition/src/android/support/transition/PropertyValuesHolderUtils.java
new file mode 100644
index 0000000..7a8eeb5
--- /dev/null
+++ b/transition/src/android/support/transition/PropertyValuesHolderUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.PropertyValuesHolder;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.os.Build;
+import android.util.Property;
+
+class PropertyValuesHolderUtils {
+
+    private static final PropertyValuesHolderUtilsImpl IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new PropertyValuesHolderUtilsApi21();
+        } else {
+            IMPL = new PropertyValuesHolderUtilsApi14();
+        }
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder with a given property and
+     * a Path along which the values should be animated. This variant supports a
+     * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
+     * type.
+     *
+     * @param property The property being animated. Should not be null.
+     * @param path     The Path along which the values should be animated.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    static PropertyValuesHolder ofPointF(Property<?, PointF> property, Path path) {
+        return IMPL.ofPointF(property, path);
+    }
+
+}
diff --git a/transition/src/android/support/transition/RectEvaluator.java b/transition/src/android/support/transition/RectEvaluator.java
new file mode 100644
index 0000000..1e757a5
--- /dev/null
+++ b/transition/src/android/support/transition/RectEvaluator.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.TypeEvaluator;
+import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>Rect</code> values.
+ */
+@RequiresApi(14)
+class RectEvaluator implements TypeEvaluator<Rect> {
+
+    /**
+     * When null, a new Rect is returned on every evaluate call. When non-null,
+     * mRect will be modified and returned on every evaluate.
+     */
+    private Rect mRect;
+
+    /**
+     * Construct a RectEvaluator that returns a new Rect on every evaluate call.
+     * To avoid creating an object for each evaluate call,
+     * {@link RectEvaluator#RectEvaluator(android.graphics.Rect)} should be used
+     * whenever possible.
+     */
+    RectEvaluator() {
+    }
+
+    /**
+     * Constructs a RectEvaluator that modifies and returns <code>reuseRect</code>
+     * in {@link #evaluate(float, android.graphics.Rect, android.graphics.Rect)} calls.
+     * The value returned from
+     * {@link #evaluate(float, android.graphics.Rect, android.graphics.Rect)} should
+     * not be cached because it will change over time as the object is reused on each
+     * call.
+     *
+     * @param reuseRect A Rect to be modified and returned by evaluate.
+     */
+    RectEvaluator(Rect reuseRect) {
+        mRect = reuseRect;
+    }
+
+    /**
+     * This function returns the result of linearly interpolating the start and
+     * end Rect values, with <code>fraction</code> representing the proportion
+     * between the start and end values. The calculation is a simple parametric
+     * calculation on each of the separate components in the Rect objects
+     * (left, top, right, and bottom).
+     *
+     * <p>If {@link #RectEvaluator(android.graphics.Rect)} was used to construct
+     * this RectEvaluator, the object returned will be the <code>reuseRect</code>
+     * passed into the constructor.</p>
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start Rect
+     * @param endValue   The end Rect
+     * @return A linear interpolation between the start and end values, given the
+     * <code>fraction</code> parameter.
+     */
+    @Override
+    public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
+        int left = startValue.left + (int) ((endValue.left - startValue.left) * fraction);
+        int top = startValue.top + (int) ((endValue.top - startValue.top) * fraction);
+        int right = startValue.right + (int) ((endValue.right - startValue.right) * fraction);
+        int bottom = startValue.bottom + (int) ((endValue.bottom - startValue.bottom) * fraction);
+        if (mRect == null) {
+            return new Rect(left, top, right, bottom);
+        } else {
+            mRect.set(left, top, right, bottom);
+            return mRect;
+        }
+    }
+}
diff --git a/transition/src/android/support/transition/Scene.java b/transition/src/android/support/transition/Scene.java
index 7684351..cc40b2c 100644
--- a/transition/src/android/support/transition/Scene.java
+++ b/transition/src/android/support/transition/Scene.java
@@ -17,11 +17,11 @@
 package android.support.transition;
 
 import android.content.Context;
-import android.os.Build;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -34,53 +34,11 @@
  */
 public class Scene {
 
-    private static SceneStaticsImpl sImpl;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            sImpl = new SceneStaticsApi21();
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            sImpl = new SceneStaticsKitKat();
-        } else {
-            sImpl = new SceneStaticsIcs();
-        }
-    }
-
-    /* package */ SceneImpl mImpl;
-
-    /**
-     * Constructs a Scene with no information about how values will change
-     * when this scene is applied. This constructor might be used when
-     * a Scene is created with the intention of being dynamically configured,
-     * through setting {@link #setEnterAction(Runnable)} and possibly
-     * {@link #setExitAction(Runnable)}.
-     *
-     * @param sceneRoot The root of the hierarchy in which scene changes
-     *                  and transitions will take place.
-     */
-    public Scene(@NonNull ViewGroup sceneRoot) {
-        mImpl = createSceneImpl();
-        mImpl.init(sceneRoot);
-    }
-
-    /**
-     * Constructs a Scene which, when entered, will remove any
-     * children from the sceneRoot container and add the layout
-     * object as a new child of that container.
-     *
-     * @param sceneRoot The root of the hierarchy in which scene changes
-     *                  and transitions will take place.
-     * @param layout    The view hierarchy of this scene, added as a child
-     *                  of sceneRoot when this scene is entered.
-     */
-    public Scene(@NonNull ViewGroup sceneRoot, @NonNull View layout) {
-        mImpl = createSceneImpl();
-        mImpl.init(sceneRoot, layout);
-    }
-
-    private Scene(SceneImpl scene) {
-        mImpl = scene;
-    }
+    private Context mContext;
+    private int mLayoutId = -1;
+    private ViewGroup mSceneRoot;
+    private View mLayout; // alternative to layoutId
+    private Runnable mEnterAction, mExitAction;
 
     /**
      * Returns a Scene described by the resource file associated with the given
@@ -111,20 +69,60 @@
         if (scene != null) {
             return scene;
         } else {
-            scene = new Scene(sImpl.getSceneForLayout(sceneRoot, layoutId, context));
+            scene = new Scene(sceneRoot, layoutId, context);
             scenes.put(layoutId, scene);
             return scene;
         }
     }
 
-    private SceneImpl createSceneImpl() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return new SceneApi21();
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            return new SceneKitKat();
-        } else {
-            return new SceneIcs();
-        }
+    /**
+     * Constructs a Scene with no information about how values will change
+     * when this scene is applied. This constructor might be used when
+     * a Scene is created with the intention of being dynamically configured,
+     * through setting {@link #setEnterAction(Runnable)} and possibly
+     * {@link #setExitAction(Runnable)}.
+     *
+     * @param sceneRoot The root of the hierarchy in which scene changes
+     *                  and transitions will take place.
+     */
+    public Scene(@NonNull ViewGroup sceneRoot) {
+        mSceneRoot = sceneRoot;
+    }
+
+    /**
+     * Constructs a Scene which, when entered, will remove any
+     * children from the sceneRoot container and will inflate and add
+     * the hierarchy specified by the layoutId resource file.
+     *
+     * <p>This method is hidden because layoutId-based scenes should be
+     * created by the caching factory method {@link Scene#getCurrentScene(View)}.</p>
+     *
+     * @param sceneRoot The root of the hierarchy in which scene changes
+     *                  and transitions will take place.
+     * @param layoutId  The id of a resource file that defines the view
+     *                  hierarchy of this scene.
+     * @param context   The context used in the process of inflating
+     *                  the layout resource.
+     */
+    private Scene(ViewGroup sceneRoot, int layoutId, Context context) {
+        mContext = context;
+        mSceneRoot = sceneRoot;
+        mLayoutId = layoutId;
+    }
+
+    /**
+     * Constructs a Scene which, when entered, will remove any
+     * children from the sceneRoot container and add the layout
+     * object as a new child of that container.
+     *
+     * @param sceneRoot The root of the hierarchy in which scene changes
+     *                  and transitions will take place.
+     * @param layout    The view hierarchy of this scene, added as a child
+     *                  of sceneRoot when this scene is entered.
+     */
+    public Scene(@NonNull ViewGroup sceneRoot, @NonNull View layout) {
+        mSceneRoot = sceneRoot;
+        mLayout = layout;
     }
 
     /**
@@ -136,7 +134,7 @@
      */
     @NonNull
     public ViewGroup getSceneRoot() {
-        return mImpl.getSceneRoot();
+        return mSceneRoot;
     }
 
     /**
@@ -147,7 +145,11 @@
      * if there is one.
      */
     public void exit() {
-        mImpl.exit();
+        if (getCurrentScene(mSceneRoot) == this) {
+            if (mExitAction != null) {
+                mExitAction.run();
+            }
+        }
     }
 
     /**
@@ -161,7 +163,47 @@
      * use one of the methods in {@link android.support.transition.TransitionManager} instead.
      */
     public void enter() {
-        mImpl.enter();
+        // Apply layout change, if any
+        if (mLayoutId > 0 || mLayout != null) {
+            // empty out parent container before adding to it
+            getSceneRoot().removeAllViews();
+
+            if (mLayoutId > 0) {
+                LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
+            } else {
+                mSceneRoot.addView(mLayout);
+            }
+        }
+
+        // Notify next scene that it is entering. Subclasses may override to configure scene.
+        if (mEnterAction != null) {
+            mEnterAction.run();
+        }
+
+        setCurrentScene(mSceneRoot, this);
+    }
+
+    /**
+     * Set the scene that the given view is in. The current scene is set only
+     * on the root view of a scene, not for every view in that hierarchy. This
+     * information is used by Scene to determine whether there is a previous
+     * scene which should be exited before the new scene is entered.
+     *
+     * @param view The view on which the current scene is being set
+     */
+    static void setCurrentScene(View view, Scene scene) {
+        view.setTag(R.id.transition_current_scene, scene);
+    }
+
+    /**
+     * Gets the current {@link Scene} set on the given view. A scene is set on a view
+     * only if that view is the scene root.
+     *
+     * @return The current Scene set on this view. A value of null indicates that
+     * no Scene is currently set.
+     */
+    static Scene getCurrentScene(View view) {
+        return (Scene) view.getTag(R.id.transition_current_scene);
     }
 
     /**
@@ -182,7 +224,7 @@
      * @see android.support.transition.Scene(android.view.ViewGroup, android.view.ViewGroup)
      */
     public void setEnterAction(@Nullable Runnable action) {
-        mImpl.setEnterAction(action);
+        mEnterAction = action;
     }
 
     /**
@@ -202,7 +244,16 @@
      * @see android.support.transition.Scene(android.view.ViewGroup, android.view.ViewGroup)
      */
     public void setExitAction(@Nullable Runnable action) {
-        mImpl.setExitAction(action);
+        mExitAction = action;
+    }
+
+    /**
+     * Returns whether this Scene was created by a layout resource file, determined
+     * by the layoutId passed into
+     * {@link #getSceneForLayout(ViewGroup, int, Context)}.
+     */
+    boolean isCreatedFromLayoutResource() {
+        return (mLayoutId > 0);
     }
 
 }
diff --git a/transition/src/android/support/transition/SidePropagation.java b/transition/src/android/support/transition/SidePropagation.java
new file mode 100644
index 0000000..64b5210
--- /dev/null
+++ b/transition/src/android/support/transition/SidePropagation.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Rect;
+import android.support.v4.view.ViewCompat;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A <code>TransitionPropagation</code> that propagates based on the distance to the side
+ * and, orthogonally, the distance to epicenter. If the transitioning View is visible in
+ * the start of the transition, then it will transition sooner when closer to the side and
+ * later when farther. If the view is not visible in the start of the transition, then
+ * it will transition later when closer to the side and sooner when farther from the edge.
+ * This is the default TransitionPropagation used with {@link android.transition.Slide}.
+ */
+public class SidePropagation extends VisibilityPropagation {
+
+    private float mPropagationSpeed = 3.0f;
+    private int mSide = Gravity.BOTTOM;
+
+    /**
+     * Sets the side that is used to calculate the transition propagation. If the transitioning
+     * View is visible in the start of the transition, then it will transition sooner when
+     * closer to the side and later when farther. If the view is not visible in the start of
+     * the transition, then it will transition later when closer to the side and sooner when
+     * farther from the edge. The default is {@link Gravity#BOTTOM}.
+     *
+     * @param side The side that is used to calculate the transition propagation. Must be one of
+     *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
+     *             {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
+     */
+    public void setSide(@Slide.GravityFlag int side) {
+        mSide = side;
+    }
+
+    /**
+     * Sets the speed at which transition propagation happens, relative to the duration of the
+     * Transition. A <code>propagationSpeed</code> of 1 means that a View centered at the side
+     * set in {@link #setSide(int)} and View centered at the opposite edge will have a difference
+     * in start delay of approximately the duration of the Transition. A speed of 2 means the
+     * start delay difference will be approximately half of the duration of the transition. A
+     * value of 0 is illegal, but negative values will invert the propagation.
+     *
+     * @param propagationSpeed The speed at which propagation occurs, relative to the duration
+     *                         of the transition. A speed of 4 means it works 4 times as fast
+     *                         as the duration of the transition. May not be 0.
+     */
+    public void setPropagationSpeed(float propagationSpeed) {
+        if (propagationSpeed == 0) {
+            throw new IllegalArgumentException("propagationSpeed may not be 0");
+        }
+        mPropagationSpeed = propagationSpeed;
+    }
+
+    @Override
+    public long getStartDelay(ViewGroup sceneRoot, Transition transition,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null && endValues == null) {
+            return 0;
+        }
+        int directionMultiplier = 1;
+        Rect epicenter = transition.getEpicenter();
+        TransitionValues positionValues;
+        if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) {
+            positionValues = startValues;
+            directionMultiplier = -1;
+        } else {
+            positionValues = endValues;
+        }
+
+        int viewCenterX = getViewX(positionValues);
+        int viewCenterY = getViewY(positionValues);
+
+        int[] loc = new int[2];
+        sceneRoot.getLocationOnScreen(loc);
+        int left = loc[0] + Math.round(sceneRoot.getTranslationX());
+        int top = loc[1] + Math.round(sceneRoot.getTranslationY());
+        int right = left + sceneRoot.getWidth();
+        int bottom = top + sceneRoot.getHeight();
+
+        int epicenterX;
+        int epicenterY;
+        if (epicenter != null) {
+            epicenterX = epicenter.centerX();
+            epicenterY = epicenter.centerY();
+        } else {
+            epicenterX = (left + right) / 2;
+            epicenterY = (top + bottom) / 2;
+        }
+
+        float distance = distance(sceneRoot, viewCenterX, viewCenterY, epicenterX, epicenterY,
+                left, top, right, bottom);
+        float maxDistance = getMaxDistance(sceneRoot);
+        float distanceFraction = distance / maxDistance;
+
+        long duration = transition.getDuration();
+        if (duration < 0) {
+            duration = 300;
+        }
+
+        return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
+    }
+
+    private int distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY,
+            int left, int top, int right, int bottom) {
+        final int side;
+        if (mSide == Gravity.START) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(sceneRoot)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+            side = isRtl ? Gravity.RIGHT : Gravity.LEFT;
+        } else if (mSide == Gravity.END) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(sceneRoot)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+            side = isRtl ? Gravity.LEFT : Gravity.RIGHT;
+        } else {
+            side = mSide;
+        }
+        int distance = 0;
+        switch (side) {
+            case Gravity.LEFT:
+                distance = right - viewX + Math.abs(epicenterY - viewY);
+                break;
+            case Gravity.TOP:
+                distance = bottom - viewY + Math.abs(epicenterX - viewX);
+                break;
+            case Gravity.RIGHT:
+                distance = viewX - left + Math.abs(epicenterY - viewY);
+                break;
+            case Gravity.BOTTOM:
+                distance = viewY - top + Math.abs(epicenterX - viewX);
+                break;
+        }
+        return distance;
+    }
+
+    private int getMaxDistance(ViewGroup sceneRoot) {
+        switch (mSide) {
+            case Gravity.LEFT:
+            case Gravity.RIGHT:
+            case Gravity.START:
+            case Gravity.END:
+                return sceneRoot.getWidth();
+            default:
+                return sceneRoot.getHeight();
+        }
+    }
+
+}
diff --git a/transition/src/android/support/transition/Slide.java b/transition/src/android/support/transition/Slide.java
new file mode 100644
index 0000000..49c5e5b
--- /dev/null
+++ b/transition/src/android/support/transition/Slide.java
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes and moves views in or out from one of the edges of the
+ * scene. Visibility is determined by both the
+ * {@link View#setVisibility(int)} state of the view as well as whether it
+ * is parented in the current view hierarchy. Disappearing Views are
+ * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
+ * TransitionValues, int, TransitionValues, int)}.
+ */
+public class Slide extends Visibility {
+
+    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
+    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
+    private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
+    private CalculateSlide mSlideCalculator = sCalculateBottom;
+    private int mSlideEdge = Gravity.BOTTOM;
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Gravity.LEFT, Gravity.TOP, Gravity.RIGHT, Gravity.BOTTOM, Gravity.START, Gravity.END})
+    public @interface GravityFlag {
+    }
+
+    private interface CalculateSlide {
+
+        /** Returns the translation value for view when it goes out of the scene */
+        float getGoneX(ViewGroup sceneRoot, View view);
+
+        /** Returns the translation value for view when it goes out of the scene */
+        float getGoneY(ViewGroup sceneRoot, View view);
+    }
+
+    private abstract static class CalculateSlideHorizontal implements CalculateSlide {
+
+        @Override
+        public float getGoneY(ViewGroup sceneRoot, View view) {
+            return view.getTranslationY();
+        }
+    }
+
+    private abstract static class CalculateSlideVertical implements CalculateSlide {
+
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            return view.getTranslationX();
+        }
+    }
+
+    private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            return view.getTranslationX() - sceneRoot.getWidth();
+        }
+    };
+
+    private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(sceneRoot)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+            final float x;
+            if (isRtl) {
+                x = view.getTranslationX() + sceneRoot.getWidth();
+            } else {
+                x = view.getTranslationX() - sceneRoot.getWidth();
+            }
+            return x;
+        }
+    };
+
+    private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
+        @Override
+        public float getGoneY(ViewGroup sceneRoot, View view) {
+            return view.getTranslationY() - sceneRoot.getHeight();
+        }
+    };
+
+    private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            return view.getTranslationX() + sceneRoot.getWidth();
+        }
+    };
+
+    private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(sceneRoot)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+            final float x;
+            if (isRtl) {
+                x = view.getTranslationX() - sceneRoot.getWidth();
+            } else {
+                x = view.getTranslationX() + sceneRoot.getWidth();
+            }
+            return x;
+        }
+    };
+
+    private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
+        @Override
+        public float getGoneY(ViewGroup sceneRoot, View view) {
+            return view.getTranslationY() + sceneRoot.getHeight();
+        }
+    };
+
+    /**
+     * Constructor using the default {@link Gravity#BOTTOM}
+     * slide edge direction.
+     */
+    public Slide() {
+        setSlideEdge(Gravity.BOTTOM);
+    }
+
+    /**
+     * Constructor using the provided slide edge direction.
+     */
+    public Slide(int slideEdge) {
+        setSlideEdge(slideEdge);
+    }
+
+    public Slide(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.SLIDE);
+        int edge = TypedArrayUtils.getNamedInt(a, (XmlPullParser) attrs, "slideEdge",
+                Styleable.Slide.SLIDE_EDGE, Gravity.BOTTOM);
+        a.recycle();
+        //noinspection WrongConstant
+        setSlideEdge(edge);
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        int[] position = new int[2];
+        view.getLocationOnScreen(position);
+        transitionValues.values.put(PROPNAME_SCREEN_POSITION, position);
+    }
+
+    @Override
+    public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        super.captureEndValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    /**
+     * Change the edge that Views appear and disappear from.
+     *
+     * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
+     *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
+     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+     *                  {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
+     */
+    public void setSlideEdge(@GravityFlag int slideEdge) {
+        switch (slideEdge) {
+            case Gravity.LEFT:
+                mSlideCalculator = sCalculateLeft;
+                break;
+            case Gravity.TOP:
+                mSlideCalculator = sCalculateTop;
+                break;
+            case Gravity.RIGHT:
+                mSlideCalculator = sCalculateRight;
+                break;
+            case Gravity.BOTTOM:
+                mSlideCalculator = sCalculateBottom;
+                break;
+            case Gravity.START:
+                mSlideCalculator = sCalculateStart;
+                break;
+            case Gravity.END:
+                mSlideCalculator = sCalculateEnd;
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid slide direction");
+        }
+        mSlideEdge = slideEdge;
+        SidePropagation propagation = new SidePropagation();
+        propagation.setSide(slideEdge);
+        setPropagation(propagation);
+    }
+
+    /**
+     * Returns the edge that Views appear and disappear from.
+     *
+     * @return the edge of the scene to use for Views appearing and disappearing. One of
+     * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
+     * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+     * {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
+     */
+    @GravityFlag
+    public int getSlideEdge() {
+        return mSlideEdge;
+    }
+
+    @Override
+    public Animator onAppear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (endValues == null) {
+            return null;
+        }
+        int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
+        float endX = view.getTranslationX();
+        float endY = view.getTranslationY();
+        float startX = mSlideCalculator.getGoneX(sceneRoot, view);
+        float startY = mSlideCalculator.getGoneY(sceneRoot, view);
+        return TranslationAnimationCreator
+                .createAnimation(view, endValues, position[0], position[1],
+                        startX, startY, endX, endY, sDecelerate);
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null) {
+            return null;
+        }
+        int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
+        float startX = view.getTranslationX();
+        float startY = view.getTranslationY();
+        float endX = mSlideCalculator.getGoneX(sceneRoot, view);
+        float endY = mSlideCalculator.getGoneY(sceneRoot, view);
+        return TranslationAnimationCreator
+                .createAnimation(view, startValues, position[0], position[1],
+                        startX, startY, endX, endY, sAccelerate);
+    }
+
+}
diff --git a/transition/src/android/support/transition/Styleable.java b/transition/src/android/support/transition/Styleable.java
new file mode 100644
index 0000000..74ceb4a
--- /dev/null
+++ b/transition/src/android/support/transition/Styleable.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.StyleableRes;
+
+/**
+ * Copies of styleable ID values generated in the platform R.java.
+ */
+@SuppressLint("InlinedApi")
+class Styleable {
+
+    @StyleableRes
+    static final int[] TRANSITION_TARGET = {
+            android.R.attr.targetClass,
+            android.R.attr.targetId,
+            android.R.attr.excludeId,
+            android.R.attr.excludeClass,
+            android.R.attr.targetName,
+            android.R.attr.excludeName,
+    };
+
+    interface TransitionTarget {
+        @StyleableRes
+        int TARGET_CLASS = 0;
+        @StyleableRes
+        int TARGET_ID = 1;
+        @StyleableRes
+        int EXCLUDE_ID = 2;
+        @StyleableRes
+        int EXCLUDE_CLASS = 3;
+        @StyleableRes
+        int TARGET_NAME = 4;
+        @StyleableRes
+        int EXCLUDE_NAME = 5;
+    }
+
+    @StyleableRes
+    static final int[] TRANSITION_MANAGER = {
+            android.R.attr.fromScene,
+            android.R.attr.toScene,
+            android.R.attr.transition,
+    };
+
+    interface TransitionManager {
+        @StyleableRes
+        int FROM_SCENE = 0;
+        @StyleableRes
+        int TO_SCENE = 1;
+        @StyleableRes
+        int TRANSITION = 2;
+    }
+
+    @StyleableRes
+    static final int[] TRANSITION = {
+            android.R.attr.interpolator,
+            android.R.attr.duration,
+            android.R.attr.startDelay,
+            android.R.attr.matchOrder,
+    };
+
+    interface Transition {
+        @StyleableRes
+        int INTERPOLATOR = 0;
+        @StyleableRes
+        int DURATION = 1;
+        @StyleableRes
+        int START_DELAY = 2;
+        @StyleableRes
+        int MATCH_ORDER = 3;
+    }
+
+    @StyleableRes
+    static final int[] CHANGE_BOUNDS = {
+            android.R.attr.resizeClip,
+    };
+
+    interface ChangeBounds {
+        @StyleableRes
+        int RESIZE_CLIP = 0;
+    }
+
+    @StyleableRes
+    static final int[] VISIBILITY_TRANSITION = {
+            android.R.attr.transitionVisibilityMode,
+    };
+
+    interface VisibilityTransition {
+        @StyleableRes
+        int TRANSITION_VISIBILITY_MODE = 0;
+    }
+
+    @StyleableRes
+    static final int[] FADE = {
+            android.R.attr.fadingMode,
+    };
+
+    interface Fade {
+        @StyleableRes
+        int FADING_MODE = 0;
+    }
+
+    @StyleableRes
+    static final int[] CHANGE_TRANSFORM = {
+            android.R.attr.reparent,
+            android.R.attr.reparentWithOverlay,
+    };
+
+    interface ChangeTransform {
+        @StyleableRes
+        int REPARENT = 0;
+        @StyleableRes
+        int REPARENT_WITH_OVERLAY = 1;
+    }
+
+    @StyleableRes
+    static final int[] SLIDE = {
+            android.R.attr.slideEdge,
+    };
+
+    interface Slide {
+        @StyleableRes
+        int SLIDE_EDGE = 0;
+    }
+
+    @StyleableRes
+    static final int[] TRANSITION_SET = {
+            android.R.attr.transitionOrdering,
+    };
+
+    interface TransitionSet {
+        @StyleableRes
+        int TRANSITION_ORDERING = 0;
+    }
+
+    @StyleableRes
+    static final int[] ARC_MOTION = {
+            android.R.attr.minimumHorizontalAngle,
+            android.R.attr.minimumVerticalAngle,
+            android.R.attr.maximumAngle,
+    };
+
+    interface ArcMotion {
+        @StyleableRes
+        int MINIMUM_HORIZONTAL_ANGLE = 0;
+        @StyleableRes
+        int MINIMUM_VERTICAL_ANGLE = 1;
+        @StyleableRes
+        int MAXIMUM_ANGLE = 2;
+    }
+
+    @StyleableRes
+    static final int[] PATTERN_PATH_MOTION = {
+            android.R.attr.patternPathData,
+    };
+
+    interface PatternPathMotion {
+        @StyleableRes
+        int PATTERN_PATH_DATA = 0;
+    }
+
+}
diff --git a/transition/src/android/support/transition/Transition.java b/transition/src/android/support/transition/Transition.java
index bab890e..04cc57b 100644
--- a/transition/src/android/support/transition/Transition.java
+++ b/transition/src/android/support/transition/Transition.java
@@ -16,19 +16,43 @@
 
 package android.support.transition;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
-import android.os.Build;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Path;
+import android.graphics.Rect;
 import android.support.annotation.IdRes;
+import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.util.LongSparseArray;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.InflateException;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.widget.ListView;
 import android.widget.Spinner;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.StringTokenizer;
 
 /**
  * A Transition holds information about animations that will be run on its
@@ -52,11 +76,180 @@
  * with TextureView because they rely on {@link android.view.ViewOverlay}
  * functionality, which does not currently work with TextureView.</p>
  *
- * <p>Unlike the platform version, this does not support declaration by XML resources.</p>
+ * <p>Transitions can be declared in XML resource files inside the <code>res/transition</code>
+ * directory. Transition resources consist of a tag name for one of the Transition
+ * subclasses along with attributes to define some of the attributes of that transition.
+ * For example, here is a minimal resource file that declares a {@link ChangeBounds}
+ * transition:</p>
+ *
+ * <pre>
+ *     &lt;changeBounds/&gt;
+ * </pre>
+ *
+ * <p>Note that attributes for the transition are not required, just as they are
+ * optional when declared in code; Transitions created from XML resources will use
+ * the same defaults as their code-created equivalents. Here is a slightly more
+ * elaborate example which declares a {@link TransitionSet} transition with
+ * {@link ChangeBounds} and {@link Fade} child transitions:</p>
+ *
+ * <pre>
+ *     &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ *          android:transitionOrdering="sequential"&gt;
+ *         &lt;changeBounds/&gt;
+ *         &lt;fade android:fadingMode="fade_out"&gt;
+ *             &lt;targets&gt;
+ *                 &lt;target android:targetId="@id/grayscaleContainer"/&gt;
+ *             &lt;/targets&gt;
+ *         &lt;/fade&gt;
+ *     &lt;/transitionSet&gt;
+ * </pre>
+ *
+ * <p>In this example, the transitionOrdering attribute is used on the TransitionSet
+ * object to change from the default {@link TransitionSet#ORDERING_TOGETHER} behavior
+ * to be {@link TransitionSet#ORDERING_SEQUENTIAL} instead. Also, the {@link Fade}
+ * transition uses a fadingMode of {@link Fade#OUT} instead of the default
+ * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
+ * takes a set of {code target} tags, each of which lists a specific <code>targetId</code> which
+ * this transition acts upon. Use of targets is optional, but can be used to either limit the time
+ * spent checking attributes on unchanging views, or limiting the types of animations run on
+ * specific views. In this case, we know that only the <code>grayscaleContainer</code> will be
+ * disappearing, so we choose to limit the {@link Fade} transition to only that view.</p>
  */
-public abstract class Transition implements TransitionInterface {
+public abstract class Transition implements Cloneable {
 
-    /* package */ TransitionImpl mImpl;
+    private static final String LOG_TAG = "Transition";
+    static final boolean DBG = false;
+
+    /**
+     * With {@link #setMatchOrder(int...)}, chooses to match by View instance.
+     */
+    public static final int MATCH_INSTANCE = 0x1;
+    private static final int MATCH_FIRST = MATCH_INSTANCE;
+
+    /**
+     * With {@link #setMatchOrder(int...)}, chooses to match by
+     * {@link android.view.View#getTransitionName()}. Null names will not be matched.
+     */
+    public static final int MATCH_NAME = 0x2;
+
+    /**
+     * With {@link #setMatchOrder(int...)}, chooses to match by
+     * {@link android.view.View#getId()}. Negative IDs will not be matched.
+     */
+    public static final int MATCH_ID = 0x3;
+
+    /**
+     * With {@link #setMatchOrder(int...)}, chooses to match by the {@link android.widget.Adapter}
+     * item id. When {@link android.widget.Adapter#hasStableIds()} returns false, no match
+     * will be made for items.
+     */
+    public static final int MATCH_ITEM_ID = 0x4;
+
+    private static final int MATCH_LAST = MATCH_ITEM_ID;
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({MATCH_INSTANCE, MATCH_NAME, MATCH_ID, MATCH_ITEM_ID})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MatchOrder {
+    }
+
+    private static final String MATCH_INSTANCE_STR = "instance";
+    private static final String MATCH_NAME_STR = "name";
+    private static final String MATCH_ID_STR = "id";
+    private static final String MATCH_ITEM_ID_STR = "itemId";
+
+    private static final int[] DEFAULT_MATCH_ORDER = {
+            MATCH_NAME,
+            MATCH_INSTANCE,
+            MATCH_ID,
+            MATCH_ITEM_ID,
+    };
+
+    private static final PathMotion STRAIGHT_PATH_MOTION = new PathMotion() {
+        @Override
+        public Path getPath(float startX, float startY, float endX, float endY) {
+            Path path = new Path();
+            path.moveTo(startX, startY);
+            path.lineTo(endX, endY);
+            return path;
+        }
+    };
+
+    private String mName = getClass().getName();
+
+    private long mStartDelay = -1;
+    long mDuration = -1;
+    private TimeInterpolator mInterpolator = null;
+    ArrayList<Integer> mTargetIds = new ArrayList<>();
+    ArrayList<View> mTargets = new ArrayList<>();
+    private ArrayList<String> mTargetNames = null;
+    private ArrayList<Class> mTargetTypes = null;
+    private ArrayList<Integer> mTargetIdExcludes = null;
+    private ArrayList<View> mTargetExcludes = null;
+    private ArrayList<Class> mTargetTypeExcludes = null;
+    private ArrayList<String> mTargetNameExcludes = null;
+    private ArrayList<Integer> mTargetIdChildExcludes = null;
+    private ArrayList<View> mTargetChildExcludes = null;
+    private ArrayList<Class> mTargetTypeChildExcludes = null;
+    private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
+    private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
+    TransitionSet mParent = null;
+    private int[] mMatchOrder = DEFAULT_MATCH_ORDER;
+    private ArrayList<TransitionValues> mStartValuesList; // only valid after playTransition starts
+    private ArrayList<TransitionValues> mEndValuesList; // only valid after playTransitions starts
+
+    // Per-animator information used for later canceling when future transitions overlap
+    private static ThreadLocal<ArrayMap<Animator, Transition.AnimationInfo>> sRunningAnimators =
+            new ThreadLocal<>();
+
+    // Scene Root is set at createAnimator() time in the cloned Transition
+    private ViewGroup mSceneRoot = null;
+
+    // Whether removing views from their parent is possible. This is only for views
+    // in the start scene, which are no longer in the view hierarchy. This property
+    // is determined by whether the previous Scene was created from a layout
+    // resource, and thus the views from the exited scene are going away anyway
+    // and can be removed as necessary to achieve a particular effect, such as
+    // removing them from parents to add them to overlays.
+    boolean mCanRemoveViews = false;
+
+    // Track all animators in use in case the transition gets canceled and needs to
+    // cancel running animators
+    private ArrayList<Animator> mCurrentAnimators = new ArrayList<>();
+
+    // Number of per-target instances of this Transition currently running. This count is
+    // determined by calls to start() and end()
+    private int mNumInstances = 0;
+
+    // Whether this transition is currently paused, due to a call to pause()
+    private boolean mPaused = false;
+
+    // Whether this transition has ended. Used to avoid pause/resume on transitions
+    // that have completed
+    private boolean mEnded = false;
+
+    // The set of listeners to be sent transition lifecycle events.
+    private ArrayList<Transition.TransitionListener> mListeners = null;
+
+    // The set of animators collected from calls to createAnimator(),
+    // to be run in runAnimators()
+    private ArrayList<Animator> mAnimators = new ArrayList<>();
+
+    // The function for calculating the Animation start delay.
+    TransitionPropagation mPropagation;
+
+    // The rectangular region for Transitions like Explode and TransitionPropagations
+    // like CircularPropagation
+    private EpicenterCallback mEpicenterCallback;
+
+    // For Fragment shared element transitions, linking views explicitly by mismatching
+    // transitionNames.
+    private ArrayMap<String, String> mNameOverrides;
+
+    // The function used to interpolate along two-dimensional points. Typically used
+    // for adding curves to x/y View motion.
+    private PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
 
     /**
      * Constructs a Transition object with no target objects. A transition with
@@ -65,152 +258,180 @@
      * objects passed down from its parent (if it is in a TransitionSet).
      */
     public Transition() {
-        this(false);
-    }
-
-    // Hidden constructor for built-in transitions
-    Transition(boolean deferred) {
-        if (!deferred) {
-            if (Build.VERSION.SDK_INT >= 23) {
-                mImpl = new TransitionApi23();
-            } else if (Build.VERSION.SDK_INT >= 19) {
-                mImpl = new TransitionKitKat();
-            } else {
-                mImpl = new TransitionIcs();
-            }
-            mImpl.init(this);
-        }
     }
 
     /**
-     * Adds a listener to the set of listeners that are sent events through the
-     * life of an animation, such as start, repeat, and end.
+     * Perform inflation from XML and apply a class-specific base style from a
+     * theme attribute or style resource. This constructor of Transition allows
+     * subclasses to use their own base style when they are inflating.
      *
-     * @param listener the listener to be added to the current set of listeners
-     *                 for this animation.
+     * @param context The Context the transition is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs   The attributes of the XML tag that is inflating the transition.
+     */
+    public Transition(Context context, AttributeSet attrs) {
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.TRANSITION);
+        XmlResourceParser parser = (XmlResourceParser) attrs;
+        long duration = TypedArrayUtils.getNamedInt(a, parser, "duration",
+                Styleable.Transition.DURATION, -1);
+        if (duration >= 0) {
+            setDuration(duration);
+        }
+        long startDelay = TypedArrayUtils.getNamedInt(a, parser, "startDelay",
+                Styleable.Transition.START_DELAY, -1);
+        if (startDelay > 0) {
+            setStartDelay(startDelay);
+        }
+        final int resId = TypedArrayUtils.getNamedResourceId(a, parser, "interpolator",
+                Styleable.Transition.INTERPOLATOR, 0);
+        if (resId > 0) {
+            setInterpolator(AnimationUtils.loadInterpolator(context, resId));
+        }
+        String matchOrder = TypedArrayUtils.getNamedString(a, parser, "matchOrder",
+                Styleable.Transition.MATCH_ORDER);
+        if (matchOrder != null) {
+            setMatchOrder(parseMatchOrder(matchOrder));
+        }
+        a.recycle();
+    }
+
+    @MatchOrder
+    private static int[] parseMatchOrder(String matchOrderString) {
+        StringTokenizer st = new StringTokenizer(matchOrderString, ",");
+        @MatchOrder
+        int[] matches = new int[st.countTokens()];
+        int index = 0;
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken().trim();
+            if (MATCH_ID_STR.equalsIgnoreCase(token)) {
+                matches[index] = Transition.MATCH_ID;
+            } else if (MATCH_INSTANCE_STR.equalsIgnoreCase(token)) {
+                matches[index] = Transition.MATCH_INSTANCE;
+            } else if (MATCH_NAME_STR.equalsIgnoreCase(token)) {
+                matches[index] = Transition.MATCH_NAME;
+            } else if (MATCH_ITEM_ID_STR.equalsIgnoreCase(token)) {
+                matches[index] = Transition.MATCH_ITEM_ID;
+            } else if (token.isEmpty()) {
+                @MatchOrder
+                int[] smallerMatches = new int[matches.length - 1];
+                System.arraycopy(matches, 0, smallerMatches, 0, index);
+                matches = smallerMatches;
+                index--;
+            } else {
+                throw new InflateException("Unknown match type in matchOrder: '" + token + "'");
+            }
+            index++;
+        }
+        return matches;
+    }
+
+    /**
+     * Sets the duration of this transition. By default, there is no duration
+     * (indicated by a negative number), which means that the Animator created by
+     * the transition will have its own specified duration. If the duration of a
+     * Transition is set, that duration will override the Animator duration.
+     *
+     * @param duration The length of the animation, in milliseconds.
      * @return This transition object.
      */
     @NonNull
-    public Transition addListener(@NonNull TransitionListener listener) {
-        mImpl.addListener(listener);
+    public Transition setDuration(long duration) {
+        mDuration = duration;
         return this;
     }
 
     /**
-     * Sets the target view instances that this Transition is interested in
-     * animating. By default, there are no targets, and a Transition will
-     * listen for changes on every view in the hierarchy below the sceneRoot
-     * of the Scene being transitioned into. Setting targets constrains
-     * the Transition to only listen for, and act on, these views.
-     * All other views will be ignored.
+     * Returns the duration set on this transition. If no duration has been set,
+     * the returned value will be negative, indicating that resulting animators will
+     * retain their own durations.
      *
-     * <p>The target list is like the {@link #addTarget(int) targetId}
-     * list except this list specifies the actual View instances, not the ids
-     * of the views. This is an important distinction when scene changes involve
-     * view hierarchies which have been inflated separately; different views may
-     * share the same id but not actually be the same instance. If the transition
-     * should treat those views as the same, then {@link #addTarget(int)} should be used
-     * instead of {@link #addTarget(View)}. If, on the other hand, scene changes involve
-     * changes all within the same view hierarchy, among views which do not
-     * necessarily have ids set on them, then the target list of views may be more
-     * convenient.</p>
+     * @return The duration set on this transition, in milliseconds, if one has been
+     * set, otherwise returns a negative number.
+     */
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Sets the startDelay of this transition. By default, there is no delay
+     * (indicated by a negative number), which means that the Animator created by
+     * the transition will have its own specified startDelay. If the delay of a
+     * Transition is set, that delay will override the Animator delay.
      *
-     * @param target A View on which the Transition will act, must be non-null.
-     * @return The Transition to which the target is added.
-     * Returning the same object makes it easier to chain calls during
-     * construction, such as
-     * <code>transitionSet.addTransitions(new Fade()).addTarget(someView);</code>
-     * @see #addTarget(int)
+     * @param startDelay The length of the delay, in milliseconds.
+     * @return This transition object.
      */
     @NonNull
-    public Transition addTarget(@NonNull View target) {
-        mImpl.addTarget(target);
+    public Transition setStartDelay(long startDelay) {
+        mStartDelay = startDelay;
         return this;
     }
 
     /**
-     * Adds the id of a target view that this Transition is interested in
-     * animating. By default, there are no targetIds, and a Transition will
-     * listen for changes on every view in the hierarchy below the sceneRoot
-     * of the Scene being transitioned into. Setting targetIds constrains
-     * the Transition to only listen for, and act on, views with these IDs.
-     * Views with different IDs, or no IDs whatsoever, will be ignored.
+     * Returns the startDelay set on this transition. If no startDelay has been set,
+     * the returned value will be negative, indicating that resulting animators will
+     * retain their own startDelays.
      *
-     * <p>Note that using ids to specify targets implies that ids should be unique
-     * within the view hierarchy underneath the scene root.</p>
+     * @return The startDelay set on this transition, in milliseconds, if one has
+     * been set, otherwise returns a negative number.
+     */
+    public long getStartDelay() {
+        return mStartDelay;
+    }
+
+    /**
+     * Sets the interpolator of this transition. By default, the interpolator
+     * is null, which means that the Animator created by the transition
+     * will have its own specified interpolator. If the interpolator of a
+     * Transition is set, that interpolator will override the Animator interpolator.
      *
-     * @param targetId The id of a target view, must be a positive number.
-     * @return The Transition to which the targetId is added.
-     * Returning the same object makes it easier to chain calls during
-     * construction, such as
-     * <code>transitionSet.addTransitions(new Fade()).addTarget(someId);</code>
-     * @see View#getId()
+     * @param interpolator The time interpolator used by the transition
+     * @return This transition object.
      */
     @NonNull
-    public Transition addTarget(@IdRes int targetId) {
-        mImpl.addTarget(targetId);
+    public Transition setInterpolator(@Nullable TimeInterpolator interpolator) {
+        mInterpolator = interpolator;
         return this;
     }
 
     /**
-     * Captures the values in the end scene for the properties that this
-     * transition monitors. These values are then passed as the endValues
-     * structure in a later call to
-     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
-     * The main concern for an implementation is what the
-     * properties are that the transition cares about and what the values are
-     * for all of those properties. The start and end values will be compared
-     * later during the
-     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
-     * method to determine what, if any, animations, should be run.
+     * Returns the interpolator set on this transition. If no interpolator has been set,
+     * the returned value will be null, indicating that resulting animators will
+     * retain their own interpolators.
      *
-     * <p>Subclasses must implement this method. The method should only be called by the
-     * transition system; it is not intended to be called from external classes.</p>
-     *
-     * @param transitionValues The holder for any values that the Transition
-     *                         wishes to store. Values are stored in the <code>values</code> field
-     *                         of this TransitionValues object and are keyed from
-     *                         a String value. For example, to store a view's rotation value,
-     *                         a transition might call
-     *                         <code>transitionValues.values.put("appname:transitionname:rotation",
-     *                         view.getRotation())</code>. The target view will already be stored
-     *                         in
-     *                         the transitionValues structure when this method is called.
-     * @see #captureStartValues(TransitionValues)
-     * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
+     * @return The interpolator set on this transition, if one has been set, otherwise
+     * returns null.
      */
-    @Override
-    public abstract void captureEndValues(@NonNull TransitionValues transitionValues);
+    @Nullable
+    public TimeInterpolator getInterpolator() {
+        return mInterpolator;
+    }
 
     /**
-     * Captures the values in the start scene for the properties that this
-     * transition monitors. These values are then passed as the startValues
-     * structure in a later call to
-     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
-     * The main concern for an implementation is what the
-     * properties are that the transition cares about and what the values are
-     * for all of those properties. The start and end values will be compared
-     * later during the
-     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
-     * method to determine what, if any, animations, should be run.
+     * Returns the set of property names used stored in the {@link TransitionValues}
+     * object passed into {@link #captureStartValues(TransitionValues)} that
+     * this transition cares about for the purposes of canceling overlapping animations.
+     * When any transition is started on a given scene root, all transitions
+     * currently running on that same scene root are checked to see whether the
+     * properties on which they based their animations agree with the end values of
+     * the same properties in the new transition. If the end values are not equal,
+     * then the old animation is canceled since the new transition will start a new
+     * animation to these new values. If the values are equal, the old animation is
+     * allowed to continue and no new animation is started for that transition.
      *
-     * <p>Subclasses must implement this method. The method should only be called by the
-     * transition system; it is not intended to be called from external classes.</p>
+     * <p>A transition does not need to override this method. However, not doing so
+     * will mean that the cancellation logic outlined in the previous paragraph
+     * will be skipped for that transition, possibly leading to artifacts as
+     * old transitions and new transitions on the same targets run in parallel,
+     * animating views toward potentially different end values.</p>
      *
-     * @param transitionValues The holder for any values that the Transition
-     *                         wishes to store. Values are stored in the <code>values</code> field
-     *                         of this TransitionValues object and are keyed from
-     *                         a String value. For example, to store a view's rotation value,
-     *                         a transition might call
-     *                         <code>transitionValues.values.put("appname:transitionname:rotation",
-     *                         view.getRotation())</code>. The target view will already be stored
-     *                         in
-     *                         the transitionValues structure when this method is called.
-     * @see #captureEndValues(TransitionValues)
-     * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
+     * @return An array of property names as described in the class documentation for
+     * {@link TransitionValues}. The default implementation returns <code>null</code>.
      */
-    @Override
-    public abstract void captureStartValues(@NonNull TransitionValues transitionValues);
+    @Nullable
+    public String[] getTransitionProperties() {
+        return null;
+    }
 
     /**
      * This method creates an animation that will be run for this transition
@@ -257,7 +478,6 @@
      * overall transition for this scene change. A null value means no animation
      * should be run.
      */
-    @Override
     @Nullable
     public Animator createAnimator(@NonNull ViewGroup sceneRoot,
             @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
@@ -265,6 +485,764 @@
     }
 
     /**
+     * Sets the order in which Transition matches View start and end values.
+     * <p>
+     * The default behavior is to match first by {@link android.view.View#getTransitionName()},
+     * then by View instance, then by {@link android.view.View#getId()} and finally
+     * by its item ID if it is in a direct child of ListView. The caller can
+     * choose to have only some or all of the values of {@link #MATCH_INSTANCE},
+     * {@link #MATCH_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only
+     * the match algorithms supplied will be used to determine whether Views are the
+     * the same in both the start and end Scene. Views that do not match will be considered
+     * as entering or leaving the Scene.
+     * </p>
+     *
+     * @param matches A list of zero or more of {@link #MATCH_INSTANCE},
+     *                {@link #MATCH_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}.
+     *                If none are provided, then the default match order will be set.
+     */
+    public void setMatchOrder(@MatchOrder int... matches) {
+        if (matches == null || matches.length == 0) {
+            mMatchOrder = DEFAULT_MATCH_ORDER;
+        } else {
+            for (int i = 0; i < matches.length; i++) {
+                int match = matches[i];
+                if (!isValidMatch(match)) {
+                    throw new IllegalArgumentException("matches contains invalid value");
+                }
+                if (alreadyContains(matches, i)) {
+                    throw new IllegalArgumentException("matches contains a duplicate value");
+                }
+            }
+            mMatchOrder = matches.clone();
+        }
+    }
+
+    private static boolean isValidMatch(int match) {
+        return (match >= MATCH_FIRST && match <= MATCH_LAST);
+    }
+
+    private static boolean alreadyContains(int[] array, int searchIndex) {
+        int value = array[searchIndex];
+        for (int i = 0; i < searchIndex; i++) {
+            if (array[i] == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Match start/end values by View instance. Adds matched values to mStartValuesList
+     * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd.
+     */
+    private void matchInstances(ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd) {
+        for (int i = unmatchedStart.size() - 1; i >= 0; i--) {
+            View view = unmatchedStart.keyAt(i);
+            if (view != null && isValidTarget(view)) {
+                TransitionValues end = unmatchedEnd.remove(view);
+                if (end != null && end.view != null && isValidTarget(end.view)) {
+                    TransitionValues start = unmatchedStart.removeAt(i);
+                    mStartValuesList.add(start);
+                    mEndValuesList.add(end);
+                }
+            }
+        }
+    }
+
+    /**
+     * Match start/end values by Adapter item ID. Adds matched values to mStartValuesList
+     * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd, using
+     * startItemIds and endItemIds as a guide for which Views have unique item IDs.
+     */
+    private void matchItemIds(ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd,
+            LongSparseArray<View> startItemIds, LongSparseArray<View> endItemIds) {
+        int numStartIds = startItemIds.size();
+        for (int i = 0; i < numStartIds; i++) {
+            View startView = startItemIds.valueAt(i);
+            if (startView != null && isValidTarget(startView)) {
+                View endView = endItemIds.get(startItemIds.keyAt(i));
+                if (endView != null && isValidTarget(endView)) {
+                    TransitionValues startValues = unmatchedStart.get(startView);
+                    TransitionValues endValues = unmatchedEnd.get(endView);
+                    if (startValues != null && endValues != null) {
+                        mStartValuesList.add(startValues);
+                        mEndValuesList.add(endValues);
+                        unmatchedStart.remove(startView);
+                        unmatchedEnd.remove(endView);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Match start/end values by Adapter view ID. Adds matched values to mStartValuesList
+     * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd, using
+     * startIds and endIds as a guide for which Views have unique IDs.
+     */
+    private void matchIds(ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd,
+            SparseArray<View> startIds, SparseArray<View> endIds) {
+        int numStartIds = startIds.size();
+        for (int i = 0; i < numStartIds; i++) {
+            View startView = startIds.valueAt(i);
+            if (startView != null && isValidTarget(startView)) {
+                View endView = endIds.get(startIds.keyAt(i));
+                if (endView != null && isValidTarget(endView)) {
+                    TransitionValues startValues = unmatchedStart.get(startView);
+                    TransitionValues endValues = unmatchedEnd.get(endView);
+                    if (startValues != null && endValues != null) {
+                        mStartValuesList.add(startValues);
+                        mEndValuesList.add(endValues);
+                        unmatchedStart.remove(startView);
+                        unmatchedEnd.remove(endView);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Match start/end values by Adapter transitionName. Adds matched values to mStartValuesList
+     * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd, using
+     * startNames and endNames as a guide for which Views have unique transitionNames.
+     */
+    private void matchNames(ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd,
+            ArrayMap<String, View> startNames, ArrayMap<String, View> endNames) {
+        int numStartNames = startNames.size();
+        for (int i = 0; i < numStartNames; i++) {
+            View startView = startNames.valueAt(i);
+            if (startView != null && isValidTarget(startView)) {
+                View endView = endNames.get(startNames.keyAt(i));
+                if (endView != null && isValidTarget(endView)) {
+                    TransitionValues startValues = unmatchedStart.get(startView);
+                    TransitionValues endValues = unmatchedEnd.get(endView);
+                    if (startValues != null && endValues != null) {
+                        mStartValuesList.add(startValues);
+                        mEndValuesList.add(endValues);
+                        unmatchedStart.remove(startView);
+                        unmatchedEnd.remove(endView);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds all values from unmatchedStart and unmatchedEnd to mStartValuesList and mEndValuesList,
+     * assuming that there is no match between values in the list.
+     */
+    private void addUnmatched(ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd) {
+        // Views that only exist in the start Scene
+        for (int i = 0; i < unmatchedStart.size(); i++) {
+            final TransitionValues start = unmatchedStart.valueAt(i);
+            if (isValidTarget(start.view)) {
+                mStartValuesList.add(start);
+                mEndValuesList.add(null);
+            }
+        }
+
+        // Views that only exist in the end Scene
+        for (int i = 0; i < unmatchedEnd.size(); i++) {
+            final TransitionValues end = unmatchedEnd.valueAt(i);
+            if (isValidTarget(end.view)) {
+                mEndValuesList.add(end);
+                mStartValuesList.add(null);
+            }
+        }
+    }
+
+    private void matchStartAndEnd(TransitionValuesMaps startValues,
+            TransitionValuesMaps endValues) {
+        ArrayMap<View, TransitionValues> unmatchedStart = new ArrayMap<>(startValues.mViewValues);
+        ArrayMap<View, TransitionValues> unmatchedEnd = new ArrayMap<>(endValues.mViewValues);
+
+        for (int i = 0; i < mMatchOrder.length; i++) {
+            switch (mMatchOrder[i]) {
+                case MATCH_INSTANCE:
+                    matchInstances(unmatchedStart, unmatchedEnd);
+                    break;
+                case MATCH_NAME:
+                    matchNames(unmatchedStart, unmatchedEnd,
+                            startValues.mNameValues, endValues.mNameValues);
+                    break;
+                case MATCH_ID:
+                    matchIds(unmatchedStart, unmatchedEnd,
+                            startValues.mIdValues, endValues.mIdValues);
+                    break;
+                case MATCH_ITEM_ID:
+                    matchItemIds(unmatchedStart, unmatchedEnd,
+                            startValues.mItemIdValues, endValues.mItemIdValues);
+                    break;
+            }
+        }
+        addUnmatched(unmatchedStart, unmatchedEnd);
+    }
+
+    /**
+     * This method, essentially a wrapper around all calls to createAnimator for all
+     * possible target views, is called with the entire set of start/end
+     * values. The implementation in Transition iterates through these lists
+     * and calls {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
+     * with each set of start/end values on this transition. The
+     * TransitionSet subclass overrides this method and delegates it to
+     * each of its children in succession.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
+            TransitionValuesMaps endValues, ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList) {
+        if (DBG) {
+            Log.d(LOG_TAG, "createAnimators() for " + this);
+        }
+        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+        long minStartDelay = Long.MAX_VALUE;
+        SparseIntArray startDelays = new SparseIntArray();
+        int startValuesListCount = startValuesList.size();
+        for (int i = 0; i < startValuesListCount; ++i) {
+            TransitionValues start = startValuesList.get(i);
+            TransitionValues end = endValuesList.get(i);
+            if (start != null && !start.mTargetedTransitions.contains(this)) {
+                start = null;
+            }
+            if (end != null && !end.mTargetedTransitions.contains(this)) {
+                end = null;
+            }
+            if (start == null && end == null) {
+                continue;
+            }
+            // Only bother trying to animate with values that differ between start/end
+            boolean isChanged = start == null || end == null || isTransitionRequired(start, end);
+            if (isChanged) {
+                if (DBG) {
+                    View view = (end != null) ? end.view : start.view;
+                    Log.d(LOG_TAG, "  differing start/end values for view " + view);
+                    if (start == null || end == null) {
+                        Log.d(LOG_TAG, "    " + ((start == null)
+                                ? "start null, end non-null" : "start non-null, end null"));
+                    } else {
+                        for (String key : start.values.keySet()) {
+                            Object startValue = start.values.get(key);
+                            Object endValue = end.values.get(key);
+                            if (startValue != endValue && !startValue.equals(endValue)) {
+                                Log.d(LOG_TAG, "    " + key + ": start(" + startValue
+                                        + "), end(" + endValue + ")");
+                            }
+                        }
+                    }
+                }
+                // TODO: what to do about targetIds and itemIds?
+                Animator animator = createAnimator(sceneRoot, start, end);
+                if (animator != null) {
+                    // Save animation info for future cancellation purposes
+                    View view;
+                    TransitionValues infoValues = null;
+                    if (end != null) {
+                        view = end.view;
+                        String[] properties = getTransitionProperties();
+                        if (view != null && properties != null && properties.length > 0) {
+                            infoValues = new TransitionValues();
+                            infoValues.view = view;
+                            TransitionValues newValues = endValues.mViewValues.get(view);
+                            if (newValues != null) {
+                                for (int j = 0; j < properties.length; ++j) {
+                                    infoValues.values.put(properties[j],
+                                            newValues.values.get(properties[j]));
+                                }
+                            }
+                            int numExistingAnims = runningAnimators.size();
+                            for (int j = 0; j < numExistingAnims; ++j) {
+                                Animator anim = runningAnimators.keyAt(j);
+                                AnimationInfo info = runningAnimators.get(anim);
+                                if (info.mValues != null && info.mView == view
+                                        && info.mName.equals(getName())) {
+                                    if (info.mValues.equals(infoValues)) {
+                                        // Favor the old animator
+                                        animator = null;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    } else {
+                        view = start.view;
+                    }
+                    if (animator != null) {
+                        if (mPropagation != null) {
+                            long delay = mPropagation.getStartDelay(sceneRoot, this, start, end);
+                            startDelays.put(mAnimators.size(), (int) delay);
+                            minStartDelay = Math.min(delay, minStartDelay);
+                        }
+                        AnimationInfo info = new AnimationInfo(view, getName(), this,
+                                ViewUtils.getWindowId(sceneRoot), infoValues);
+                        runningAnimators.put(animator, info);
+                        mAnimators.add(animator);
+                    }
+                }
+            }
+        }
+        if (minStartDelay != 0) {
+            for (int i = 0; i < startDelays.size(); i++) {
+                int index = startDelays.keyAt(i);
+                Animator animator = mAnimators.get(index);
+                long delay = startDelays.valueAt(i) - minStartDelay + animator.getStartDelay();
+                animator.setStartDelay(delay);
+            }
+        }
+    }
+
+    /**
+     * Internal utility method for checking whether a given view/id
+     * is valid for this transition, where "valid" means that either
+     * the Transition has no target/targetId list (the default, in which
+     * cause the transition should act on all views in the hiearchy), or
+     * the given view is in the target list or the view id is in the
+     * targetId list. If the target parameter is null, then the target list
+     * is not checked (this is in the case of ListView items, where the
+     * views are ignored and only the ids are used).
+     */
+    boolean isValidTarget(View target) {
+        int targetId = target.getId();
+        if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) {
+            return false;
+        }
+        if (mTargetExcludes != null && mTargetExcludes.contains(target)) {
+            return false;
+        }
+        if (mTargetTypeExcludes != null) {
+            int numTypes = mTargetTypeExcludes.size();
+            for (int i = 0; i < numTypes; ++i) {
+                Class type = mTargetTypeExcludes.get(i);
+                if (type.isInstance(target)) {
+                    return false;
+                }
+            }
+        }
+        if (mTargetNameExcludes != null && ViewCompat.getTransitionName(target) != null) {
+            if (mTargetNameExcludes.contains(ViewCompat.getTransitionName(target))) {
+                return false;
+            }
+        }
+        if (mTargetIds.size() == 0 && mTargets.size() == 0
+                && (mTargetTypes == null || mTargetTypes.isEmpty())
+                && (mTargetNames == null || mTargetNames.isEmpty())) {
+            return true;
+        }
+        if (mTargetIds.contains(targetId) || mTargets.contains(target)) {
+            return true;
+        }
+        if (mTargetNames != null && mTargetNames.contains(ViewCompat.getTransitionName(target))) {
+            return true;
+        }
+        if (mTargetTypes != null) {
+            for (int i = 0; i < mTargetTypes.size(); ++i) {
+                if (mTargetTypes.get(i).isInstance(target)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() {
+        ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get();
+        if (runningAnimators == null) {
+            runningAnimators = new ArrayMap<>();
+            sRunningAnimators.set(runningAnimators);
+        }
+        return runningAnimators;
+    }
+
+    /**
+     * This is called internally once all animations have been set up by the
+     * transition hierarchy. \
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void runAnimators() {
+        if (DBG) {
+            Log.d(LOG_TAG, "runAnimators() on " + this);
+        }
+        start();
+        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+        // Now start every Animator that was previously created for this transition
+        for (Animator anim : mAnimators) {
+            if (DBG) {
+                Log.d(LOG_TAG, "  anim: " + anim);
+            }
+            if (runningAnimators.containsKey(anim)) {
+                start();
+                runAnimator(anim, runningAnimators);
+            }
+        }
+        mAnimators.clear();
+        end();
+    }
+
+    private void runAnimator(Animator animator,
+            final ArrayMap<Animator, AnimationInfo> runningAnimators) {
+        if (animator != null) {
+            // TODO: could be a single listener instance for all of them since it uses the param
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mCurrentAnimators.add(animation);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    runningAnimators.remove(animation);
+                    mCurrentAnimators.remove(animation);
+                }
+            });
+            animate(animator);
+        }
+    }
+
+    /**
+     * Captures the values in the start scene for the properties that this
+     * transition monitors. These values are then passed as the startValues
+     * structure in a later call to
+     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
+     * The main concern for an implementation is what the
+     * properties are that the transition cares about and what the values are
+     * for all of those properties. The start and end values will be compared
+     * later during the
+     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
+     * method to determine what, if any, animations, should be run.
+     *
+     * <p>Subclasses must implement this method. The method should only be called by the
+     * transition system; it is not intended to be called from external classes.</p>
+     *
+     * @param transitionValues The holder for any values that the Transition
+     *                         wishes to store. Values are stored in the <code>values</code> field
+     *                         of this TransitionValues object and are keyed from
+     *                         a String value. For example, to store a view's rotation value,
+     *                         a transition might call
+     *                         <code>transitionValues.values.put("appname:transitionname:rotation",
+     *                         view.getRotation())</code>. The target view will already be stored
+     *                         in
+     *                         the transitionValues structure when this method is called.
+     * @see #captureEndValues(TransitionValues)
+     * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
+     */
+    public abstract void captureStartValues(@NonNull TransitionValues transitionValues);
+
+    /**
+     * Captures the values in the end scene for the properties that this
+     * transition monitors. These values are then passed as the endValues
+     * structure in a later call to
+     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
+     * The main concern for an implementation is what the
+     * properties are that the transition cares about and what the values are
+     * for all of those properties. The start and end values will be compared
+     * later during the
+     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
+     * method to determine what, if any, animations, should be run.
+     *
+     * <p>Subclasses must implement this method. The method should only be called by the
+     * transition system; it is not intended to be called from external classes.</p>
+     *
+     * @param transitionValues The holder for any values that the Transition
+     *                         wishes to store. Values are stored in the <code>values</code> field
+     *                         of this TransitionValues object and are keyed from
+     *                         a String value. For example, to store a view's rotation value,
+     *                         a transition might call
+     *                         <code>transitionValues.values.put("appname:transitionname:rotation",
+     *                         view.getRotation())</code>. The target view will already be stored
+     *                         in
+     *                         the transitionValues structure when this method is called.
+     * @see #captureStartValues(TransitionValues)
+     * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
+     */
+    public abstract void captureEndValues(@NonNull TransitionValues transitionValues);
+
+    /**
+     * Sets the target view instances that this Transition is interested in
+     * animating. By default, there are no targets, and a Transition will
+     * listen for changes on every view in the hierarchy below the sceneRoot
+     * of the Scene being transitioned into. Setting targets constrains
+     * the Transition to only listen for, and act on, these views.
+     * All other views will be ignored.
+     *
+     * <p>The target list is like the {@link #addTarget(int) targetId}
+     * list except this list specifies the actual View instances, not the ids
+     * of the views. This is an important distinction when scene changes involve
+     * view hierarchies which have been inflated separately; different views may
+     * share the same id but not actually be the same instance. If the transition
+     * should treat those views as the same, then {@link #addTarget(int)} should be used
+     * instead of {@link #addTarget(View)}. If, on the other hand, scene changes involve
+     * changes all within the same view hierarchy, among views which do not
+     * necessarily have ids set on them, then the target list of views may be more
+     * convenient.</p>
+     *
+     * @param target A View on which the Transition will act, must be non-null.
+     * @return The Transition to which the target is added.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).addTarget(someView);</code>
+     * @see #addTarget(int)
+     */
+    @NonNull
+    public Transition addTarget(@NonNull View target) {
+        mTargets.add(target);
+        return this;
+    }
+
+    /**
+     * Adds the id of a target view that this Transition is interested in
+     * animating. By default, there are no targetIds, and a Transition will
+     * listen for changes on every view in the hierarchy below the sceneRoot
+     * of the Scene being transitioned into. Setting targetIds constrains
+     * the Transition to only listen for, and act on, views with these IDs.
+     * Views with different IDs, or no IDs whatsoever, will be ignored.
+     *
+     * <p>Note that using ids to specify targets implies that ids should be unique
+     * within the view hierarchy underneath the scene root.</p>
+     *
+     * @param targetId The id of a target view, must be a positive number.
+     * @return The Transition to which the targetId is added.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).addTarget(someId);</code>
+     * @see View#getId()
+     */
+    @NonNull
+    public Transition addTarget(@IdRes int targetId) {
+        if (targetId > 0) {
+            mTargetIds.add(targetId);
+        }
+        return this;
+    }
+
+    /**
+     * Adds the transitionName of a target view that this Transition is interested in
+     * animating. By default, there are no targetNames, and a Transition will
+     * listen for changes on every view in the hierarchy below the sceneRoot
+     * of the Scene being transitioned into. Setting targetNames constrains
+     * the Transition to only listen for, and act on, views with these transitionNames.
+     * Views with different transitionNames, or no transitionName whatsoever, will be ignored.
+     *
+     * <p>Note that transitionNames should be unique within the view hierarchy.</p>
+     *
+     * @param targetName The transitionName of a target view, must be non-null.
+     * @return The Transition to which the target transitionName is added.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code>
+     * @see ViewCompat#getTransitionName(View)
+     */
+    @NonNull
+    public Transition addTarget(@NonNull String targetName) {
+        if (mTargetNames == null) {
+            mTargetNames = new ArrayList<>();
+        }
+        mTargetNames.add(targetName);
+        return this;
+    }
+
+    /**
+     * Adds the Class of a target view that this Transition is interested in
+     * animating. By default, there are no targetTypes, and a Transition will
+     * listen for changes on every view in the hierarchy below the sceneRoot
+     * of the Scene being transitioned into. Setting targetTypes constrains
+     * the Transition to only listen for, and act on, views with these classes.
+     * Views with different classes will be ignored.
+     *
+     * <p>Note that any View that can be cast to targetType will be included, so
+     * if targetType is <code>View.class</code>, all Views will be included.</p>
+     *
+     * @param targetType The type to include when running this transition.
+     * @return The Transition to which the target class was added.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code>
+     * @see #addTarget(int)
+     * @see #addTarget(android.view.View)
+     * @see #excludeTarget(Class, boolean)
+     * @see #excludeChildren(Class, boolean)
+     */
+    @NonNull
+    public Transition addTarget(@NonNull Class targetType) {
+        if (mTargetTypes == null) {
+            mTargetTypes = new ArrayList<>();
+        }
+        mTargetTypes.add(targetType);
+        return this;
+    }
+
+    /**
+     * Removes the given target from the list of targets that this Transition
+     * is interested in animating.
+     *
+     * @param target The target view, must be non-null.
+     * @return Transition The Transition from which the target is removed.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).removeTarget(someView);</code>
+     */
+    @NonNull
+    public Transition removeTarget(@NonNull View target) {
+        mTargets.remove(target);
+        return this;
+    }
+
+    /**
+     * Removes the given targetId from the list of ids that this Transition
+     * is interested in animating.
+     *
+     * @param targetId The id of a target view, must be a positive number.
+     * @return The Transition from which the targetId is removed.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).removeTargetId(someId);</code>
+     */
+    @NonNull
+    public Transition removeTarget(@IdRes int targetId) {
+        if (targetId > 0) {
+            mTargetIds.remove((Integer) targetId);
+        }
+        return this;
+    }
+
+    /**
+     * Removes the given targetName from the list of transitionNames that this Transition
+     * is interested in animating.
+     *
+     * @param targetName The transitionName of a target view, must not be null.
+     * @return The Transition from which the targetName is removed.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).removeTargetName(someName);</code>
+     */
+    @NonNull
+    public Transition removeTarget(@NonNull String targetName) {
+        if (mTargetNames != null) {
+            mTargetNames.remove(targetName);
+        }
+        return this;
+    }
+
+    /**
+     * Removes the given target from the list of targets that this Transition
+     * is interested in animating.
+     *
+     * @param target The type of the target view, must be non-null.
+     * @return Transition The Transition from which the target is removed.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).removeTarget(someType);</code>
+     */
+    @NonNull
+    public Transition removeTarget(@NonNull Class target) {
+        if (mTargetTypes != null) {
+            mTargetTypes.remove(target);
+        }
+        return this;
+    }
+
+    /**
+     * Utility method to manage the boilerplate code that is the same whether we
+     * are excluding targets or their children.
+     */
+    private static <T> ArrayList<T> excludeObject(ArrayList<T> list, T target, boolean exclude) {
+        if (target != null) {
+            if (exclude) {
+                list = ArrayListManager.add(list, target);
+            } else {
+                list = ArrayListManager.remove(list, target);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Whether to add the given target to the list of targets to exclude from this
+     * transition. The <code>exclude</code> parameter specifies whether the target
+     * should be added to or removed from the excluded list.
+     *
+     * <p>Excluding targets is a general mechanism for allowing transitions to run on
+     * a view hierarchy while skipping target views that should not be part of
+     * the transition. For example, you may want to avoid animating children
+     * of a specific ListView or Spinner. Views can be excluded either by their
+     * id, or by their instance reference, or by the Class of that view
+     * (eg, {@link Spinner}).</p>
+     *
+     * @param target  The target to ignore when running this transition.
+     * @param exclude Whether to add the target to or remove the target from the
+     *                current list of excluded targets.
+     * @return This transition object.
+     * @see #excludeChildren(View, boolean)
+     * @see #excludeTarget(int, boolean)
+     * @see #excludeTarget(Class, boolean)
+     */
+    @NonNull
+    public Transition excludeTarget(@NonNull View target, boolean exclude) {
+        mTargetExcludes = excludeView(mTargetExcludes, target, exclude);
+        return this;
+    }
+
+    /**
+     * Whether to add the given id to the list of target ids to exclude from this
+     * transition. The <code>exclude</code> parameter specifies whether the target
+     * should be added to or removed from the excluded list.
+     *
+     * <p>Excluding targets is a general mechanism for allowing transitions to run on
+     * a view hierarchy while skipping target views that should not be part of
+     * the transition. For example, you may want to avoid animating children
+     * of a specific ListView or Spinner. Views can be excluded either by their
+     * id, or by their instance reference, or by the Class of that view
+     * (eg, {@link Spinner}).</p>
+     *
+     * @param targetId The id of a target to ignore when running this transition.
+     * @param exclude  Whether to add the target to or remove the target from the
+     *                 current list of excluded targets.
+     * @return This transition object.
+     * @see #excludeChildren(int, boolean)
+     * @see #excludeTarget(View, boolean)
+     * @see #excludeTarget(Class, boolean)
+     */
+    @NonNull
+    public Transition excludeTarget(@IdRes int targetId, boolean exclude) {
+        mTargetIdExcludes = excludeId(mTargetIdExcludes, targetId, exclude);
+        return this;
+    }
+
+    /**
+     * Whether to add the given transitionName to the list of target transitionNames to exclude
+     * from this transition. The <code>exclude</code> parameter specifies whether the target
+     * should be added to or removed from the excluded list.
+     *
+     * <p>Excluding targets is a general mechanism for allowing transitions to run on
+     * a view hierarchy while skipping target views that should not be part of
+     * the transition. For example, you may want to avoid animating children
+     * of a specific ListView or Spinner. Views can be excluded by their
+     * id, their instance reference, their transitionName, or by the Class of that view
+     * (eg, {@link Spinner}).</p>
+     *
+     * @param targetName The name of a target to ignore when running this transition.
+     * @param exclude    Whether to add the target to or remove the target from the
+     *                   current list of excluded targets.
+     * @return This transition object.
+     * @see #excludeTarget(View, boolean)
+     * @see #excludeTarget(int, boolean)
+     * @see #excludeTarget(Class, boolean)
+     */
+    @NonNull
+    public Transition excludeTarget(@NonNull String targetName, boolean exclude) {
+        mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetName, exclude);
+        return this;
+    }
+
+    /**
      * Whether to add the children of given target to the list of target children
      * to exclude from this transition. The <code>exclude</code> parameter specifies
      * whether the target should be added to or removed from the excluded list.
@@ -286,7 +1264,7 @@
      */
     @NonNull
     public Transition excludeChildren(@NonNull View target, boolean exclude) {
-        mImpl.excludeChildren(target, exclude);
+        mTargetChildExcludes = excludeView(mTargetChildExcludes, target, exclude);
         return this;
     }
 
@@ -316,7 +1294,63 @@
      */
     @NonNull
     public Transition excludeChildren(@IdRes int targetId, boolean exclude) {
-        mImpl.excludeChildren(targetId, exclude);
+        mTargetIdChildExcludes = excludeId(mTargetIdChildExcludes, targetId, exclude);
+        return this;
+    }
+
+    /**
+     * Utility method to manage the boilerplate code that is the same whether we
+     * are excluding targets or their children.
+     */
+    private ArrayList<Integer> excludeId(ArrayList<Integer> list, int targetId, boolean exclude) {
+        if (targetId > 0) {
+            if (exclude) {
+                list = ArrayListManager.add(list, targetId);
+            } else {
+                list = ArrayListManager.remove(list, targetId);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Utility method to manage the boilerplate code that is the same whether we
+     * are excluding targets or their children.
+     */
+    private ArrayList<View> excludeView(ArrayList<View> list, View target, boolean exclude) {
+        if (target != null) {
+            if (exclude) {
+                list = ArrayListManager.add(list, target);
+            } else {
+                list = ArrayListManager.remove(list, target);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Whether to add the given type to the list of types to exclude from this
+     * transition. The <code>exclude</code> parameter specifies whether the target
+     * type should be added to or removed from the excluded list.
+     *
+     * <p>Excluding targets is a general mechanism for allowing transitions to run on
+     * a view hierarchy while skipping target views that should not be part of
+     * the transition. For example, you may want to avoid animating children
+     * of a specific ListView or Spinner. Views can be excluded either by their
+     * id, or by their instance reference, or by the Class of that view
+     * (eg, {@link Spinner}).</p>
+     *
+     * @param type    The type to ignore when running this transition.
+     * @param exclude Whether to add the target type to or remove it from the
+     *                current list of excluded target types.
+     * @return This transition object.
+     * @see #excludeChildren(Class, boolean)
+     * @see #excludeTarget(int, boolean)
+     * @see #excludeTarget(View, boolean)
+     */
+    @NonNull
+    public Transition excludeTarget(@NonNull Class type, boolean exclude) {
+        mTargetTypeExcludes = excludeType(mTargetTypeExcludes, type, exclude);
         return this;
     }
 
@@ -343,146 +1377,852 @@
      */
     @NonNull
     public Transition excludeChildren(@NonNull Class type, boolean exclude) {
-        mImpl.excludeChildren(type, exclude);
+        mTargetTypeChildExcludes = excludeType(mTargetTypeChildExcludes, type, exclude);
         return this;
     }
 
     /**
-     * Whether to add the given target to the list of targets to exclude from this
-     * transition. The <code>exclude</code> parameter specifies whether the target
-     * should be added to or removed from the excluded list.
+     * Utility method to manage the boilerplate code that is the same whether we
+     * are excluding targets or their children.
+     */
+    private ArrayList<Class> excludeType(ArrayList<Class> list, Class type, boolean exclude) {
+        if (type != null) {
+            if (exclude) {
+                list = ArrayListManager.add(list, type);
+            } else {
+                list = ArrayListManager.remove(list, type);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Returns the array of target IDs that this transition limits itself to
+     * tracking and animating. If the array is null for both this method and
+     * {@link #getTargets()}, then this transition is
+     * not limited to specific views, and will handle changes to any views
+     * in the hierarchy of a scene change.
      *
-     * <p>Excluding targets is a general mechanism for allowing transitions to run on
-     * a view hierarchy while skipping target views that should not be part of
-     * the transition. For example, you may want to avoid animating children
-     * of a specific ListView or Spinner. Views can be excluded either by their
-     * id, or by their instance reference, or by the Class of that view
-     * (eg, {@link Spinner}).</p>
-     *
-     * @param target  The target to ignore when running this transition.
-     * @param exclude Whether to add the target to or remove the target from the
-     *                current list of excluded targets.
-     * @return This transition object.
-     * @see #excludeChildren(View, boolean)
-     * @see #excludeTarget(int, boolean)
-     * @see #excludeTarget(Class, boolean)
+     * @return the list of target IDs
      */
     @NonNull
-    public Transition excludeTarget(@NonNull View target, boolean exclude) {
-        mImpl.excludeTarget(target, exclude);
-        return this;
+    public List<Integer> getTargetIds() {
+        return mTargetIds;
     }
 
     /**
-     * Whether to add the given id to the list of target ids to exclude from this
-     * transition. The <code>exclude</code> parameter specifies whether the target
-     * should be added to or removed from the excluded list.
+     * Returns the array of target views that this transition limits itself to
+     * tracking and animating. If the array is null for both this method and
+     * {@link #getTargetIds()}, then this transition is
+     * not limited to specific views, and will handle changes to any views
+     * in the hierarchy of a scene change.
      *
-     * <p>Excluding targets is a general mechanism for allowing transitions to run on
-     * a view hierarchy while skipping target views that should not be part of
-     * the transition. For example, you may want to avoid animating children
-     * of a specific ListView or Spinner. Views can be excluded either by their
-     * id, or by their instance reference, or by the Class of that view
-     * (eg, {@link Spinner}).</p>
-     *
-     * @param targetId The id of a target to ignore when running this transition.
-     * @param exclude  Whether to add the target to or remove the target from the
-     *                 current list of excluded targets.
-     * @return This transition object.
-     * @see #excludeChildren(int, boolean)
-     * @see #excludeTarget(View, boolean)
-     * @see #excludeTarget(Class, boolean)
+     * @return the list of target views
      */
     @NonNull
-    public Transition excludeTarget(@IdRes int targetId, boolean exclude) {
-        mImpl.excludeTarget(targetId, exclude);
-        return this;
+    public List<View> getTargets() {
+        return mTargets;
     }
 
     /**
-     * Whether to add the given type to the list of types to exclude from this
-     * transition. The <code>exclude</code> parameter specifies whether the target
-     * type should be added to or removed from the excluded list.
+     * Returns the list of target transitionNames that this transition limits itself to
+     * tracking and animating. If the list is null or empty for
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
+     * {@link #getTargetTypes()} then this transition is
+     * not limited to specific views, and will handle changes to any views
+     * in the hierarchy of a scene change.
      *
-     * <p>Excluding targets is a general mechanism for allowing transitions to run on
-     * a view hierarchy while skipping target views that should not be part of
-     * the transition. For example, you may want to avoid animating children
-     * of a specific ListView or Spinner. Views can be excluded either by their
-     * id, or by their instance reference, or by the Class of that view
-     * (eg, {@link Spinner}).</p>
-     *
-     * @param type    The type to ignore when running this transition.
-     * @param exclude Whether to add the target type to or remove it from the
-     *                current list of excluded target types.
-     * @return This transition object.
-     * @see #excludeChildren(Class, boolean)
-     * @see #excludeTarget(int, boolean)
-     * @see #excludeTarget(View, boolean)
-     */
-    @NonNull
-    public Transition excludeTarget(@NonNull Class type, boolean exclude) {
-        mImpl.excludeTarget(type, exclude);
-        return this;
-    }
-
-    /**
-     * Returns the duration set on this transition. If no duration has been set,
-     * the returned value will be negative, indicating that resulting animators will
-     * retain their own durations.
-     *
-     * @return The duration set on this transition, in milliseconds, if one has been
-     * set, otherwise returns a negative number.
-     */
-    public long getDuration() {
-        return mImpl.getDuration();
-    }
-
-    /**
-     * Sets the duration of this transition. By default, there is no duration
-     * (indicated by a negative number), which means that the Animator created by
-     * the transition will have its own specified duration. If the duration of a
-     * Transition is set, that duration will override the Animator duration.
-     *
-     * @param duration The length of the animation, in milliseconds.
-     * @return This transition object.
-     * @attr name android:duration
-     */
-    @NonNull
-    public Transition setDuration(long duration) {
-        mImpl.setDuration(duration);
-        return this;
-    }
-
-    /**
-     * Returns the interpolator set on this transition. If no interpolator has been set,
-     * the returned value will be null, indicating that resulting animators will
-     * retain their own interpolators.
-     *
-     * @return The interpolator set on this transition, if one has been set, otherwise
-     * returns null.
+     * @return the list of target transitionNames
      */
     @Nullable
-    public TimeInterpolator getInterpolator() {
-        return mImpl.getInterpolator();
+    public List<String> getTargetNames() {
+        return mTargetNames;
     }
 
     /**
-     * Sets the interpolator of this transition. By default, the interpolator
-     * is null, which means that the Animator created by the transition
-     * will have its own specified interpolator. If the interpolator of a
-     * Transition is set, that interpolator will override the Animator interpolator.
+     * Returns the list of target transitionNames that this transition limits itself to
+     * tracking and animating. If the list is null or empty for
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
+     * {@link #getTargetTypes()} then this transition is
+     * not limited to specific views, and will handle changes to any views
+     * in the hierarchy of a scene change.
      *
-     * @param interpolator The time interpolator used by the transition
+     * @return the list of target Types
+     */
+    @Nullable
+    public List<Class> getTargetTypes() {
+        return mTargetTypes;
+    }
+
+    /**
+     * Recursive method that captures values for the given view and the
+     * hierarchy underneath it.
+     *
+     * @param sceneRoot The root of the view hierarchy being captured
+     * @param start     true if this capture is happening before the scene change,
+     *                  false otherwise
+     */
+    void captureValues(ViewGroup sceneRoot, boolean start) {
+        clearValues(start);
+        if ((mTargetIds.size() > 0 || mTargets.size() > 0)
+                && (mTargetNames == null || mTargetNames.isEmpty())
+                && (mTargetTypes == null || mTargetTypes.isEmpty())) {
+            for (int i = 0; i < mTargetIds.size(); ++i) {
+                int id = mTargetIds.get(i);
+                View view = sceneRoot.findViewById(id);
+                if (view != null) {
+                    TransitionValues values = new TransitionValues();
+                    values.view = view;
+                    if (start) {
+                        captureStartValues(values);
+                    } else {
+                        captureEndValues(values);
+                    }
+                    values.mTargetedTransitions.add(this);
+                    capturePropagationValues(values);
+                    if (start) {
+                        addViewValues(mStartValues, view, values);
+                    } else {
+                        addViewValues(mEndValues, view, values);
+                    }
+                }
+            }
+            for (int i = 0; i < mTargets.size(); ++i) {
+                View view = mTargets.get(i);
+                TransitionValues values = new TransitionValues();
+                values.view = view;
+                if (start) {
+                    captureStartValues(values);
+                } else {
+                    captureEndValues(values);
+                }
+                values.mTargetedTransitions.add(this);
+                capturePropagationValues(values);
+                if (start) {
+                    addViewValues(mStartValues, view, values);
+                } else {
+                    addViewValues(mEndValues, view, values);
+                }
+            }
+        } else {
+            captureHierarchy(sceneRoot, start);
+        }
+        if (!start && mNameOverrides != null) {
+            int numOverrides = mNameOverrides.size();
+            ArrayList<View> overriddenViews = new ArrayList<>(numOverrides);
+            for (int i = 0; i < numOverrides; i++) {
+                String fromName = mNameOverrides.keyAt(i);
+                overriddenViews.add(mStartValues.mNameValues.remove(fromName));
+            }
+            for (int i = 0; i < numOverrides; i++) {
+                View view = overriddenViews.get(i);
+                if (view != null) {
+                    String toName = mNameOverrides.valueAt(i);
+                    mStartValues.mNameValues.put(toName, view);
+                }
+            }
+        }
+    }
+
+    private static void addViewValues(TransitionValuesMaps transitionValuesMaps,
+            View view, TransitionValues transitionValues) {
+        transitionValuesMaps.mViewValues.put(view, transitionValues);
+        int id = view.getId();
+        if (id >= 0) {
+            if (transitionValuesMaps.mIdValues.indexOfKey(id) >= 0) {
+                // Duplicate IDs cannot match by ID.
+                transitionValuesMaps.mIdValues.put(id, null);
+            } else {
+                transitionValuesMaps.mIdValues.put(id, view);
+            }
+        }
+        String name = ViewCompat.getTransitionName(view);
+        if (name != null) {
+            if (transitionValuesMaps.mNameValues.containsKey(name)) {
+                // Duplicate transitionNames: cannot match by transitionName.
+                transitionValuesMaps.mNameValues.put(name, null);
+            } else {
+                transitionValuesMaps.mNameValues.put(name, view);
+            }
+        }
+        if (view.getParent() instanceof ListView) {
+            ListView listview = (ListView) view.getParent();
+            if (listview.getAdapter().hasStableIds()) {
+                int position = listview.getPositionForView(view);
+                long itemId = listview.getItemIdAtPosition(position);
+                if (transitionValuesMaps.mItemIdValues.indexOfKey(itemId) >= 0) {
+                    // Duplicate item IDs: cannot match by item ID.
+                    View alreadyMatched = transitionValuesMaps.mItemIdValues.get(itemId);
+                    if (alreadyMatched != null) {
+                        ViewCompat.setHasTransientState(alreadyMatched, false);
+                        transitionValuesMaps.mItemIdValues.put(itemId, null);
+                    }
+                } else {
+                    ViewCompat.setHasTransientState(view, true);
+                    transitionValuesMaps.mItemIdValues.put(itemId, view);
+                }
+            }
+        }
+    }
+
+    /**
+     * Clear valuesMaps for specified start/end state
+     *
+     * @param start true if the start values should be cleared, false otherwise
+     */
+    void clearValues(boolean start) {
+        if (start) {
+            mStartValues.mViewValues.clear();
+            mStartValues.mIdValues.clear();
+            mStartValues.mItemIdValues.clear();
+        } else {
+            mEndValues.mViewValues.clear();
+            mEndValues.mIdValues.clear();
+            mEndValues.mItemIdValues.clear();
+        }
+    }
+
+    /**
+     * Recursive method which captures values for an entire view hierarchy,
+     * starting at some root view. Transitions without targetIDs will use this
+     * method to capture values for all possible views.
+     *
+     * @param view  The view for which to capture values. Children of this View
+     *              will also be captured, recursively down to the leaf nodes.
+     * @param start true if values are being captured in the start scene, false
+     *              otherwise.
+     */
+    private void captureHierarchy(View view, boolean start) {
+        if (view == null) {
+            return;
+        }
+        int id = view.getId();
+        if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) {
+            return;
+        }
+        if (mTargetExcludes != null && mTargetExcludes.contains(view)) {
+            return;
+        }
+        if (mTargetTypeExcludes != null) {
+            int numTypes = mTargetTypeExcludes.size();
+            for (int i = 0; i < numTypes; ++i) {
+                if (mTargetTypeExcludes.get(i).isInstance(view)) {
+                    return;
+                }
+            }
+        }
+        if (view.getParent() instanceof ViewGroup) {
+            TransitionValues values = new TransitionValues();
+            values.view = view;
+            if (start) {
+                captureStartValues(values);
+            } else {
+                captureEndValues(values);
+            }
+            values.mTargetedTransitions.add(this);
+            capturePropagationValues(values);
+            if (start) {
+                addViewValues(mStartValues, view, values);
+            } else {
+                addViewValues(mEndValues, view, values);
+            }
+        }
+        if (view instanceof ViewGroup) {
+            // Don't traverse child hierarchy if there are any child-excludes on this view
+            if (mTargetIdChildExcludes != null && mTargetIdChildExcludes.contains(id)) {
+                return;
+            }
+            if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) {
+                return;
+            }
+            if (mTargetTypeChildExcludes != null) {
+                int numTypes = mTargetTypeChildExcludes.size();
+                for (int i = 0; i < numTypes; ++i) {
+                    if (mTargetTypeChildExcludes.get(i).isInstance(view)) {
+                        return;
+                    }
+                }
+            }
+            ViewGroup parent = (ViewGroup) view;
+            for (int i = 0; i < parent.getChildCount(); ++i) {
+                captureHierarchy(parent.getChildAt(i), start);
+            }
+        }
+    }
+
+    /**
+     * This method can be called by transitions to get the TransitionValues for
+     * any particular view during the transition-playing process. This might be
+     * necessary, for example, to query the before/after state of related views
+     * for a given transition.
+     */
+    @Nullable
+    public TransitionValues getTransitionValues(@NonNull View view, boolean start) {
+        if (mParent != null) {
+            return mParent.getTransitionValues(view, start);
+        }
+        TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues;
+        return valuesMaps.mViewValues.get(view);
+    }
+
+    /**
+     * Find the matched start or end value for a given View. This is only valid
+     * after playTransition starts. For example, it will be valid in
+     * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}, but not
+     * in {@link #captureStartValues(TransitionValues)}.
+     *
+     * @param view        The view to find the match for.
+     * @param viewInStart Is View from the start values or end values.
+     * @return The matching TransitionValues for view in either start or end values, depending
+     * on viewInStart or null if there is no match for the given view.
+     */
+    TransitionValues getMatchedTransitionValues(View view, boolean viewInStart) {
+        if (mParent != null) {
+            return mParent.getMatchedTransitionValues(view, viewInStart);
+        }
+        ArrayList<TransitionValues> lookIn = viewInStart ? mStartValuesList : mEndValuesList;
+        if (lookIn == null) {
+            return null;
+        }
+        int count = lookIn.size();
+        int index = -1;
+        for (int i = 0; i < count; i++) {
+            TransitionValues values = lookIn.get(i);
+            if (values == null) {
+                return null;
+            }
+            if (values.view == view) {
+                index = i;
+                break;
+            }
+        }
+        TransitionValues values = null;
+        if (index >= 0) {
+            ArrayList<TransitionValues> matchIn = viewInStart ? mEndValuesList : mStartValuesList;
+            values = matchIn.get(index);
+        }
+        return values;
+    }
+
+    /**
+     * Pauses this transition, sending out calls to {@link
+     * TransitionListener#onTransitionPause(Transition)} to all listeners
+     * and pausing all running animators started by this transition.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void pause(View sceneRoot) {
+        if (!mEnded) {
+            ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+            int numOldAnims = runningAnimators.size();
+            WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
+            for (int i = numOldAnims - 1; i >= 0; i--) {
+                AnimationInfo info = runningAnimators.valueAt(i);
+                if (info.mView != null && windowId.equals(info.mWindowId)) {
+                    Animator anim = runningAnimators.keyAt(i);
+                    AnimatorUtils.pause(anim);
+                }
+            }
+            if (mListeners != null && mListeners.size() > 0) {
+                @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
+                        (ArrayList<TransitionListener>) mListeners.clone();
+                int numListeners = tmpListeners.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    tmpListeners.get(i).onTransitionPause(this);
+                }
+            }
+            mPaused = true;
+        }
+    }
+
+    /**
+     * Resumes this transition, sending out calls to {@link
+     * TransitionListener#onTransitionPause(Transition)} to all listeners
+     * and pausing all running animators started by this transition.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void resume(View sceneRoot) {
+        if (mPaused) {
+            if (!mEnded) {
+                ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+                int numOldAnims = runningAnimators.size();
+                WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
+                for (int i = numOldAnims - 1; i >= 0; i--) {
+                    AnimationInfo info = runningAnimators.valueAt(i);
+                    if (info.mView != null && windowId.equals(info.mWindowId)) {
+                        Animator anim = runningAnimators.keyAt(i);
+                        AnimatorUtils.resume(anim);
+                    }
+                }
+                if (mListeners != null && mListeners.size() > 0) {
+                    @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
+                            (ArrayList<TransitionListener>) mListeners.clone();
+                    int numListeners = tmpListeners.size();
+                    for (int i = 0; i < numListeners; ++i) {
+                        tmpListeners.get(i).onTransitionResume(this);
+                    }
+                }
+            }
+            mPaused = false;
+        }
+    }
+
+    /**
+     * Called by TransitionManager to play the transition. This calls
+     * createAnimators() to set things up and create all of the animations and then
+     * runAnimations() to actually start the animations.
+     */
+    void playTransition(ViewGroup sceneRoot) {
+        mStartValuesList = new ArrayList<>();
+        mEndValuesList = new ArrayList<>();
+        matchStartAndEnd(mStartValues, mEndValues);
+
+        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+        int numOldAnims = runningAnimators.size();
+        WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
+        for (int i = numOldAnims - 1; i >= 0; i--) {
+            Animator anim = runningAnimators.keyAt(i);
+            if (anim != null) {
+                AnimationInfo oldInfo = runningAnimators.get(anim);
+                if (oldInfo != null && oldInfo.mView != null
+                        && windowId.equals(oldInfo.mWindowId)) {
+                    TransitionValues oldValues = oldInfo.mValues;
+                    View oldView = oldInfo.mView;
+                    TransitionValues startValues = getTransitionValues(oldView, true);
+                    TransitionValues endValues = getMatchedTransitionValues(oldView, true);
+                    boolean cancel = (startValues != null || endValues != null)
+                            && oldInfo.mTransition.isTransitionRequired(oldValues, endValues);
+                    if (cancel) {
+                        if (anim.isRunning() || anim.isStarted()) {
+                            if (DBG) {
+                                Log.d(LOG_TAG, "Canceling anim " + anim);
+                            }
+                            anim.cancel();
+                        } else {
+                            if (DBG) {
+                                Log.d(LOG_TAG, "removing anim from info list: " + anim);
+                            }
+                            runningAnimators.remove(anim);
+                        }
+                    }
+                }
+            }
+        }
+
+        createAnimators(sceneRoot, mStartValues, mEndValues, mStartValuesList, mEndValuesList);
+        runAnimators();
+    }
+
+    /**
+     * Returns whether or not the transition should create an Animator, based on the values
+     * captured during {@link #captureStartValues(TransitionValues)} and
+     * {@link #captureEndValues(TransitionValues)}. The default implementation compares the
+     * property values returned from {@link #getTransitionProperties()}, or all property values if
+     * {@code getTransitionProperties()} returns null. Subclasses may override this method to
+     * provide logic more specific to the transition implementation.
+     *
+     * @param startValues the values from captureStartValues, This may be {@code null} if the
+     *                    View did not exist in the start state.
+     * @param endValues   the values from captureEndValues. This may be {@code null} if the View
+     *                    did not exist in the end state.
+     */
+    public boolean isTransitionRequired(@Nullable TransitionValues startValues,
+            @Nullable TransitionValues endValues) {
+        boolean valuesChanged = false;
+        // if startValues null, then transition didn't care to stash values,
+        // and won't get canceled
+        if (startValues != null && endValues != null) {
+            String[] properties = getTransitionProperties();
+            if (properties != null) {
+                for (String property : properties) {
+                    if (isValueChanged(startValues, endValues, property)) {
+                        valuesChanged = true;
+                        break;
+                    }
+                }
+            } else {
+                for (String key : startValues.values.keySet()) {
+                    if (isValueChanged(startValues, endValues, key)) {
+                        valuesChanged = true;
+                        break;
+                    }
+                }
+            }
+        }
+        return valuesChanged;
+    }
+
+    private static boolean isValueChanged(TransitionValues oldValues, TransitionValues newValues,
+            String key) {
+        Object oldValue = oldValues.values.get(key);
+        Object newValue = newValues.values.get(key);
+        boolean changed;
+        if (oldValue == null && newValue == null) {
+            // both are null
+            changed = false;
+        } else if (oldValue == null || newValue == null) {
+            // one is null
+            changed = true;
+        } else {
+            // neither is null
+            changed = !oldValue.equals(newValue);
+        }
+        if (DBG && changed) {
+            Log.d(LOG_TAG, "Transition.playTransition: "
+                    + "oldValue != newValue for " + key
+                    + ": old, new = " + oldValue + ", " + newValue);
+        }
+        return changed;
+    }
+
+    /**
+     * This is a utility method used by subclasses to handle standard parts of
+     * setting up and running an Animator: it sets the {@link #getDuration()
+     * duration} and the {@link #getStartDelay() startDelay}, starts the
+     * animation, and, when the animator ends, calls {@link #end()}.
+     *
+     * @param animator The Animator to be run during this transition.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void animate(Animator animator) {
+        // TODO: maybe pass auto-end as a boolean parameter?
+        if (animator == null) {
+            end();
+        } else {
+            if (getDuration() >= 0) {
+                animator.setDuration(getDuration());
+            }
+            if (getStartDelay() >= 0) {
+                animator.setStartDelay(getStartDelay());
+            }
+            if (getInterpolator() != null) {
+                animator.setInterpolator(getInterpolator());
+            }
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    end();
+                    animation.removeListener(this);
+                }
+            });
+            animator.start();
+        }
+    }
+
+    /**
+     * This method is called automatically by the transition and
+     * TransitionSet classes prior to a Transition subclass starting;
+     * subclasses should not need to call it directly.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void start() {
+        if (mNumInstances == 0) {
+            if (mListeners != null && mListeners.size() > 0) {
+                @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
+                        (ArrayList<TransitionListener>) mListeners.clone();
+                int numListeners = tmpListeners.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    tmpListeners.get(i).onTransitionStart(this);
+                }
+            }
+            mEnded = false;
+        }
+        mNumInstances++;
+    }
+
+    /**
+     * This method is called automatically by the Transition and
+     * TransitionSet classes when a transition finishes, either because
+     * a transition did nothing (returned a null Animator from
+     * {@link Transition#createAnimator(ViewGroup, TransitionValues,
+     * TransitionValues)}) or because the transition returned a valid
+     * Animator and end() was called in the onAnimationEnd()
+     * callback of the AnimatorListener.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void end() {
+        --mNumInstances;
+        if (mNumInstances == 0) {
+            if (mListeners != null && mListeners.size() > 0) {
+                @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
+                        (ArrayList<TransitionListener>) mListeners.clone();
+                int numListeners = tmpListeners.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    tmpListeners.get(i).onTransitionEnd(this);
+                }
+            }
+            for (int i = 0; i < mStartValues.mItemIdValues.size(); ++i) {
+                View view = mStartValues.mItemIdValues.valueAt(i);
+                if (view != null) {
+                    ViewCompat.setHasTransientState(view, false);
+                }
+            }
+            for (int i = 0; i < mEndValues.mItemIdValues.size(); ++i) {
+                View view = mEndValues.mItemIdValues.valueAt(i);
+                if (view != null) {
+                    ViewCompat.setHasTransientState(view, false);
+                }
+            }
+            mEnded = true;
+        }
+    }
+
+    /**
+     * Force the transition to move to its end state, ending all the animators.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void forceToEnd(ViewGroup sceneRoot) {
+        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+        int numOldAnims = runningAnimators.size();
+        if (sceneRoot != null) {
+            WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
+            for (int i = numOldAnims - 1; i >= 0; i--) {
+                AnimationInfo info = runningAnimators.valueAt(i);
+                if (info.mView != null && windowId != null && windowId.equals(info.mWindowId)) {
+                    Animator anim = runningAnimators.keyAt(i);
+                    anim.end();
+                }
+            }
+        }
+    }
+
+    /**
+     * This method cancels a transition that is currently running.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void cancel() {
+        int numAnimators = mCurrentAnimators.size();
+        for (int i = numAnimators - 1; i >= 0; i--) {
+            Animator animator = mCurrentAnimators.get(i);
+            animator.cancel();
+        }
+        if (mListeners != null && mListeners.size() > 0) {
+            @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
+                    (ArrayList<TransitionListener>) mListeners.clone();
+            int numListeners = tmpListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                tmpListeners.get(i).onTransitionCancel(this);
+            }
+        }
+    }
+
+    /**
+     * Adds a listener to the set of listeners that are sent events through the
+     * life of an animation, such as start, repeat, and end.
+     *
+     * @param listener the listener to be added to the current set of listeners
+     *                 for this animation.
      * @return This transition object.
-     * @attr name android:interpolator
      */
     @NonNull
-    public Transition setInterpolator(@Nullable TimeInterpolator interpolator) {
-        mImpl.setInterpolator(interpolator);
+    public Transition addListener(@NonNull TransitionListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<>();
+        }
+        mListeners.add(listener);
         return this;
     }
 
     /**
+     * Removes a listener from the set listening to this animation.
+     *
+     * @param listener the listener to be removed from the current set of
+     *                 listeners for this transition.
+     * @return This transition object.
+     */
+    @NonNull
+    public Transition removeListener(@NonNull TransitionListener listener) {
+        if (mListeners == null) {
+            return this;
+        }
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            mListeners = null;
+        }
+        return this;
+    }
+
+    /**
+     * Sets the algorithm used to calculate two-dimensional interpolation.
+     * <p>
+     * Transitions such as {@link android.transition.ChangeBounds} move Views, typically
+     * in a straight path between the start and end positions. Applications that desire to
+     * have these motions move in a curve can change how Views interpolate in two dimensions
+     * by extending PathMotion and implementing
+     * {@link android.transition.PathMotion#getPath(float, float, float, float)}.
+     * </p>
+     *
+     * @param pathMotion Algorithm object to use for determining how to interpolate in two
+     *                   dimensions. If null, a straight-path algorithm will be used.
+     * @see android.transition.ArcMotion
+     * @see PatternPathMotion
+     * @see android.transition.PathMotion
+     */
+    public void setPathMotion(@Nullable PathMotion pathMotion) {
+        if (pathMotion == null) {
+            mPathMotion = STRAIGHT_PATH_MOTION;
+        } else {
+            mPathMotion = pathMotion;
+        }
+    }
+
+    /**
+     * Returns the algorithm object used to interpolate along two dimensions. This is typically
+     * used to determine the View motion between two points.
+     *
+     * @return The algorithm object used to interpolate along two dimensions.
+     * @see android.transition.ArcMotion
+     * @see PatternPathMotion
+     * @see android.transition.PathMotion
+     */
+    @NonNull
+    public PathMotion getPathMotion() {
+        return mPathMotion;
+    }
+
+    /**
+     * Sets the callback to use to find the epicenter of a Transition. A null value indicates
+     * that there is no epicenter in the Transition and onGetEpicenter() will return null.
+     * Transitions like {@link android.transition.Explode} use a point or Rect to orient
+     * the direction of travel. This is called the epicenter of the Transition and is
+     * typically centered on a touched View. The
+     * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
+     * dynamically retrieve the epicenter during a Transition.
+     *
+     * @param epicenterCallback The callback to use to find the epicenter of the Transition.
+     */
+    public void setEpicenterCallback(@Nullable EpicenterCallback epicenterCallback) {
+        mEpicenterCallback = epicenterCallback;
+    }
+
+    /**
+     * Returns the callback used to find the epicenter of the Transition.
+     * Transitions like {@link android.transition.Explode} use a point or Rect to orient
+     * the direction of travel. This is called the epicenter of the Transition and is
+     * typically centered on a touched View. The
+     * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
+     * dynamically retrieve the epicenter during a Transition.
+     *
+     * @return the callback used to find the epicenter of the Transition.
+     */
+    @Nullable
+    public EpicenterCallback getEpicenterCallback() {
+        return mEpicenterCallback;
+    }
+
+    /**
+     * Returns the epicenter as specified by the
+     * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
+     *
+     * @return the epicenter as specified by the
+     * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
+     * @see #setEpicenterCallback(EpicenterCallback)
+     */
+    @Nullable
+    public Rect getEpicenter() {
+        if (mEpicenterCallback == null) {
+            return null;
+        }
+        return mEpicenterCallback.onGetEpicenter(this);
+    }
+
+    /**
+     * Sets the method for determining Animator start delays.
+     * When a Transition affects several Views like {@link android.transition.Explode} or
+     * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
+     * such that the Animator start delay depends on position of the View. The
+     * TransitionPropagation specifies how the start delays are calculated.
+     *
+     * @param transitionPropagation The class used to determine the start delay of
+     *                              Animators created by this Transition. A null value
+     *                              indicates that no delay should be used.
+     */
+    public void setPropagation(@Nullable TransitionPropagation transitionPropagation) {
+        mPropagation = transitionPropagation;
+    }
+
+    /**
+     * Returns the {@link android.transition.TransitionPropagation} used to calculate Animator
+     * start
+     * delays.
+     * When a Transition affects several Views like {@link android.transition.Explode} or
+     * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
+     * such that the Animator start delay depends on position of the View. The
+     * TransitionPropagation specifies how the start delays are calculated.
+     *
+     * @return the {@link android.transition.TransitionPropagation} used to calculate Animator start
+     * delays. This is null by default.
+     */
+    @Nullable
+    public TransitionPropagation getPropagation() {
+        return mPropagation;
+    }
+
+    /**
+     * Captures TransitionPropagation values for the given view and the
+     * hierarchy underneath it.
+     */
+    void capturePropagationValues(TransitionValues transitionValues) {
+        if (mPropagation != null && !transitionValues.values.isEmpty()) {
+            String[] propertyNames = mPropagation.getPropagationProperties();
+            if (propertyNames == null) {
+                return;
+            }
+            boolean containsAll = true;
+            for (int i = 0; i < propertyNames.length; i++) {
+                if (!transitionValues.values.containsKey(propertyNames[i])) {
+                    containsAll = false;
+                    break;
+                }
+            }
+            if (!containsAll) {
+                mPropagation.captureValues(transitionValues);
+            }
+        }
+    }
+
+    Transition setSceneRoot(ViewGroup sceneRoot) {
+        mSceneRoot = sceneRoot;
+        return this;
+    }
+
+    void setCanRemoveViews(boolean canRemoveViews) {
+        mCanRemoveViews = canRemoveViews;
+    }
+
+    @Override
+    public String toString() {
+        return toString("");
+    }
+
+    @Override
+    public Transition clone() {
+        try {
+            Transition clone = (Transition) super.clone();
+            clone.mAnimators = new ArrayList<>();
+            clone.mStartValues = new TransitionValuesMaps();
+            clone.mEndValues = new TransitionValuesMaps();
+            clone.mStartValuesList = null;
+            clone.mEndValuesList = null;
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns the name of this Transition. This name is used internally to distinguish
      * between different transitions to determine when interrupting transitions overlap.
      * For example, a ChangeBounds running on the same target view as another ChangeBounds
@@ -496,164 +2236,55 @@
      */
     @NonNull
     public String getName() {
-        return mImpl.getName();
+        return mName;
     }
 
-    /**
-     * Returns the startDelay set on this transition. If no startDelay has been set,
-     * the returned value will be negative, indicating that resulting animators will
-     * retain their own startDelays.
-     *
-     * @return The startDelay set on this transition, in milliseconds, if one has
-     * been set, otherwise returns a negative number.
-     */
-    public long getStartDelay() {
-        return mImpl.getStartDelay();
-    }
-
-    /**
-     * Sets the startDelay of this transition. By default, there is no delay
-     * (indicated by a negative number), which means that the Animator created by
-     * the transition will have its own specified startDelay. If the delay of a
-     * Transition is set, that delay will override the Animator delay.
-     *
-     * @param startDelay The length of the delay, in milliseconds.
-     * @return This transition object.
-     * @attr name android:startDelay
-     */
-    @NonNull
-    public Transition setStartDelay(long startDelay) {
-        mImpl.setStartDelay(startDelay);
-        return this;
-    }
-
-    /**
-     * Returns the array of target IDs that this transition limits itself to
-     * tracking and animating. If the array is null for both this method and
-     * {@link #getTargets()}, then this transition is
-     * not limited to specific views, and will handle changes to any views
-     * in the hierarchy of a scene change.
-     *
-     * @return the list of target IDs
-     */
-    @NonNull
-    public List<Integer> getTargetIds() {
-        return mImpl.getTargetIds();
-    }
-
-    /**
-     * Returns the array of target views that this transition limits itself to
-     * tracking and animating. If the array is null for both this method and
-     * {@link #getTargetIds()}, then this transition is
-     * not limited to specific views, and will handle changes to any views
-     * in the hierarchy of a scene change.
-     *
-     * @return the list of target views
-     */
-    @NonNull
-    public List<View> getTargets() {
-        return mImpl.getTargets();
-    }
-
-    /**
-     * Returns the set of property names used stored in the {@link TransitionValues}
-     * object passed into {@link #captureStartValues(TransitionValues)} that
-     * this transition cares about for the purposes of canceling overlapping animations.
-     * When any transition is started on a given scene root, all transitions
-     * currently running on that same scene root are checked to see whether the
-     * properties on which they based their animations agree with the end values of
-     * the same properties in the new transition. If the end values are not equal,
-     * then the old animation is canceled since the new transition will start a new
-     * animation to these new values. If the values are equal, the old animation is
-     * allowed to continue and no new animation is started for that transition.
-     *
-     * <p>A transition does not need to override this method. However, not doing so
-     * will mean that the cancellation logic outlined in the previous paragraph
-     * will be skipped for that transition, possibly leading to artifacts as
-     * old transitions and new transitions on the same targets run in parallel,
-     * animating views toward potentially different end values.</p>
-     *
-     * @return An array of property names as described in the class documentation for
-     * {@link TransitionValues}. The default implementation returns <code>null</code>.
-     */
-    @Nullable
-    public String[] getTransitionProperties() {
-        return mImpl.getTransitionProperties();
-    }
-
-    /**
-     * This method can be called by transitions to get the TransitionValues for
-     * any particular view during the transition-playing process. This might be
-     * necessary, for example, to query the before/after state of related views
-     * for a given transition.
-     */
-    @NonNull
-    public TransitionValues getTransitionValues(@NonNull View view, boolean start) {
-        return mImpl.getTransitionValues(view, start);
-    }
-
-    /**
-     * Removes a listener from the set listening to this animation.
-     *
-     * @param listener the listener to be removed from the current set of
-     *                 listeners for this transition.
-     * @return This transition object.
-     */
-    @NonNull
-    public Transition removeListener(@NonNull TransitionListener listener) {
-        mImpl.removeListener(listener);
-        return this;
-    }
-
-    /**
-     * Removes the given target from the list of targets that this Transition
-     * is interested in animating.
-     *
-     * @param target The target view, must be non-null.
-     * @return Transition The Transition from which the target is removed.
-     * Returning the same object makes it easier to chain calls during
-     * construction, such as
-     * <code>transitionSet.addTransitions(new Fade()).removeTarget(someView);</code>
-     */
-    @NonNull
-    public Transition removeTarget(@NonNull View target) {
-        mImpl.removeTarget(target);
-        return this;
-    }
-
-    /**
-     * Removes the given targetId from the list of ids that this Transition
-     * is interested in animating.
-     *
-     * @param targetId The id of a target view, must be a positive number.
-     * @return The Transition from which the targetId is removed.
-     * Returning the same object makes it easier to chain calls during
-     * construction, such as
-     * <code>transitionSet.addTransitions(new Fade()).removeTargetId(someId);</code>
-     */
-    @NonNull
-    public Transition removeTarget(@IdRes int targetId) {
-        mImpl.removeTarget(targetId);
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        return mImpl.toString();
+    String toString(String indent) {
+        String result = indent + getClass().getSimpleName() + "@"
+                + Integer.toHexString(hashCode()) + ": ";
+        if (mDuration != -1) {
+            result += "dur(" + mDuration + ") ";
+        }
+        if (mStartDelay != -1) {
+            result += "dly(" + mStartDelay + ") ";
+        }
+        if (mInterpolator != null) {
+            result += "interp(" + mInterpolator + ") ";
+        }
+        if (mTargetIds.size() > 0 || mTargets.size() > 0) {
+            result += "tgts(";
+            if (mTargetIds.size() > 0) {
+                for (int i = 0; i < mTargetIds.size(); ++i) {
+                    if (i > 0) {
+                        result += ", ";
+                    }
+                    result += mTargetIds.get(i);
+                }
+            }
+            if (mTargets.size() > 0) {
+                for (int i = 0; i < mTargets.size(); ++i) {
+                    if (i > 0) {
+                        result += ", ";
+                    }
+                    result += mTargets.get(i);
+                }
+            }
+            result += ")";
+        }
+        return result;
     }
 
     /**
      * A transition listener receives notifications from a transition.
      * Notifications indicate transition lifecycle events.
      */
-    public interface TransitionListener extends TransitionInterfaceListener<Transition> {
+    public interface TransitionListener {
 
         /**
          * Notification about the start of the transition.
          *
          * @param transition The started transition.
          */
-        @Override
         void onTransitionStart(@NonNull Transition transition);
 
         /**
@@ -665,7 +2296,6 @@
          *
          * @param transition The transition which reached its end.
          */
-        @Override
         void onTransitionEnd(@NonNull Transition transition);
 
         /**
@@ -678,7 +2308,6 @@
          *
          * @param transition The transition which was canceled.
          */
-        @Override
         void onTransitionCancel(@NonNull Transition transition);
 
         /**
@@ -691,7 +2320,6 @@
          *
          * @param transition The transition which was paused.
          */
-        @Override
         void onTransitionPause(@NonNull Transition transition);
 
         /**
@@ -703,8 +2331,107 @@
          *
          * @param transition The transition which was resumed.
          */
-        @Override
         void onTransitionResume(@NonNull Transition transition);
     }
 
+    /**
+     * Holds information about each animator used when a new transition starts
+     * while other transitions are still running to determine whether a running
+     * animation should be canceled or a new animation noop'd. The structure holds
+     * information about the state that an animation is going to, to be compared to
+     * end state of a new animation.
+     */
+    private static class AnimationInfo {
+
+        View mView;
+
+        String mName;
+
+        TransitionValues mValues;
+
+        WindowIdImpl mWindowId;
+
+        Transition mTransition;
+
+        AnimationInfo(View view, String name, Transition transition, WindowIdImpl windowId,
+                TransitionValues values) {
+            mView = view;
+            mName = name;
+            mValues = values;
+            mWindowId = windowId;
+            mTransition = transition;
+        }
+    }
+
+    /**
+     * Utility class for managing typed ArrayLists efficiently. In particular, this
+     * can be useful for lists that we don't expect to be used often (eg, the exclude
+     * lists), so we'd like to keep them nulled out by default. This causes the code to
+     * become tedious, with constant null checks, code to allocate when necessary,
+     * and code to null out the reference when the list is empty. This class encapsulates
+     * all of that functionality into simple add()/remove() methods which perform the
+     * necessary checks, allocation/null-out as appropriate, and return the
+     * resulting list.
+     */
+    private static class ArrayListManager {
+
+        /**
+         * Add the specified item to the list, returning the resulting list.
+         * The returned list can either the be same list passed in or, if that
+         * list was null, the new list that was created.
+         *
+         * Note that the list holds unique items; if the item already exists in the
+         * list, the list is not modified.
+         */
+        static <T> ArrayList<T> add(ArrayList<T> list, T item) {
+            if (list == null) {
+                list = new ArrayList<>();
+            }
+            if (!list.contains(item)) {
+                list.add(item);
+            }
+            return list;
+        }
+
+        /**
+         * Remove the specified item from the list, returning the resulting list.
+         * The returned list can either the be same list passed in or, if that
+         * list becomes empty as a result of the remove(), the new list was created.
+         */
+        static <T> ArrayList<T> remove(ArrayList<T> list, T item) {
+            if (list != null) {
+                list.remove(item);
+                if (list.isEmpty()) {
+                    list = null;
+                }
+            }
+            return list;
+        }
+    }
+
+    /**
+     * Class to get the epicenter of Transition. Use
+     * {@link #setEpicenterCallback(EpicenterCallback)} to set the callback used to calculate the
+     * epicenter of the Transition. Override {@link #getEpicenter()} to return the rectangular
+     * region in screen coordinates of the epicenter of the transition.
+     *
+     * @see #setEpicenterCallback(EpicenterCallback)
+     */
+    public abstract static class EpicenterCallback {
+
+        /**
+         * Implementers must override to return the epicenter of the Transition in screen
+         * coordinates. Transitions like {@link android.transition.Explode} depend upon
+         * an epicenter for the Transition. In Explode, Views move toward or away from the
+         * center of the epicenter Rect along the vector between the epicenter and the center
+         * of the View appearing and disappearing. Some Transitions, such as
+         * {@link android.transition.Fade} pay no attention to the epicenter.
+         *
+         * @param transition The transition for which the epicenter applies.
+         * @return The Rect region of the epicenter of <code>transition</code> or null if
+         * there is no epicenter.
+         */
+        public abstract Rect onGetEpicenter(@NonNull Transition transition);
+    }
+
 }
diff --git a/transition/src/android/support/transition/TransitionInflater.java b/transition/src/android/support/transition/TransitionInflater.java
new file mode 100644
index 0000000..f22579a
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionInflater.java
@@ -0,0 +1,336 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.support.annotation.NonNull;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v4.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.InflateException;
+import android.view.ViewGroup;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+/**
+ * This class inflates scenes and transitions from resource files.
+ */
+public class TransitionInflater {
+
+    private static final Class<?>[] CONSTRUCTOR_SIGNATURE =
+            new Class[]{Context.class, AttributeSet.class};
+    private static final ArrayMap<String, Constructor> CONSTRUCTORS = new ArrayMap<>();
+
+    private final Context mContext;
+
+    private TransitionInflater(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Obtains the TransitionInflater from the given context.
+     */
+    public static TransitionInflater from(Context context) {
+        return new TransitionInflater(context);
+    }
+
+    /**
+     * Loads a {@link Transition} object from a resource
+     *
+     * @param resource The resource id of the transition to load
+     * @return The loaded Transition object
+     * @throws android.content.res.Resources.NotFoundException when the
+     *                                                         transition cannot be loaded
+     */
+    public Transition inflateTransition(int resource) {
+        XmlResourceParser parser = mContext.getResources().getXml(resource);
+        try {
+            return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
+        } catch (XmlPullParserException e) {
+            throw new InflateException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new InflateException(
+                    parser.getPositionDescription() + ": " + e.getMessage(), e);
+        } finally {
+            parser.close();
+        }
+    }
+
+    /**
+     * Loads a {@link TransitionManager} object from a resource
+     *
+     * @param resource The resource id of the transition manager to load
+     * @return The loaded TransitionManager object
+     * @throws android.content.res.Resources.NotFoundException when the
+     *                                                         transition manager cannot be loaded
+     */
+    public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
+        XmlResourceParser parser = mContext.getResources().getXml(resource);
+        try {
+            return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
+        } catch (XmlPullParserException e) {
+            InflateException ex = new InflateException(e.getMessage());
+            ex.initCause(e);
+            throw ex;
+        } catch (IOException e) {
+            InflateException ex = new InflateException(
+                    parser.getPositionDescription()
+                            + ": " + e.getMessage());
+            ex.initCause(e);
+            throw ex;
+        } finally {
+            parser.close();
+        }
+    }
+
+    //
+    // Transition loading
+    //
+    private Transition createTransitionFromXml(XmlPullParser parser,
+            AttributeSet attrs, Transition parent)
+            throws XmlPullParserException, IOException {
+
+        Transition transition = null;
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+
+        TransitionSet transitionSet = (parent instanceof TransitionSet)
+                ? (TransitionSet) parent : null;
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+                && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+            if ("fade".equals(name)) {
+                transition = new Fade(mContext, attrs);
+            } else if ("changeBounds".equals(name)) {
+                transition = new ChangeBounds(mContext, attrs);
+            } else if ("slide".equals(name)) {
+                transition = new Slide(mContext, attrs);
+            } else if ("explode".equals(name)) {
+                transition = new Explode(mContext, attrs);
+            } else if ("changeImageTransform".equals(name)) {
+                transition = new ChangeImageTransform(mContext, attrs);
+            } else if ("changeTransform".equals(name)) {
+                transition = new ChangeTransform(mContext, attrs);
+            } else if ("changeClipBounds".equals(name)) {
+                transition = new ChangeClipBounds(mContext, attrs);
+            } else if ("autoTransition".equals(name)) {
+                transition = new AutoTransition(mContext, attrs);
+            } else if ("changeScroll".equals(name)) {
+                transition = new ChangeScroll(mContext, attrs);
+            } else if ("transitionSet".equals(name)) {
+                transition = new TransitionSet(mContext, attrs);
+            } else if ("transition".equals(name)) {
+                transition = (Transition) createCustom(attrs, Transition.class, "transition");
+            } else if ("targets".equals(name)) {
+                getTargetIds(parser, attrs, parent);
+            } else if ("arcMotion".equals(name)) {
+                if (parent == null) {
+                    throw new RuntimeException("Invalid use of arcMotion element");
+                }
+                parent.setPathMotion(new ArcMotion(mContext, attrs));
+            } else if ("pathMotion".equals(name)) {
+                if (parent == null) {
+                    throw new RuntimeException("Invalid use of pathMotion element");
+                }
+                parent.setPathMotion((PathMotion) createCustom(attrs, PathMotion.class,
+                        "pathMotion"));
+            } else if ("patternPathMotion".equals(name)) {
+                if (parent == null) {
+                    throw new RuntimeException("Invalid use of patternPathMotion element");
+                }
+                parent.setPathMotion(new PatternPathMotion(mContext, attrs));
+            } else {
+                throw new RuntimeException("Unknown scene name: " + parser.getName());
+            }
+            if (transition != null) {
+                if (!parser.isEmptyElementTag()) {
+                    createTransitionFromXml(parser, attrs, transition);
+                }
+                if (transitionSet != null) {
+                    transitionSet.addTransition(transition);
+                    transition = null;
+                } else if (parent != null) {
+                    throw new InflateException("Could not add transition to another transition.");
+                }
+            }
+        }
+
+        return transition;
+    }
+
+    private Object createCustom(AttributeSet attrs, Class expectedType, String tag) {
+        String className = attrs.getAttributeValue(null, "class");
+
+        if (className == null) {
+            throw new InflateException(tag + " tag must have a 'class' attribute");
+        }
+
+        try {
+            synchronized (CONSTRUCTORS) {
+                Constructor constructor = CONSTRUCTORS.get(className);
+                if (constructor == null) {
+                    @SuppressWarnings("unchecked")
+                    Class<?> c = mContext.getClassLoader().loadClass(className)
+                            .asSubclass(expectedType);
+                    if (c != null) {
+                        constructor = c.getConstructor(CONSTRUCTOR_SIGNATURE);
+                        constructor.setAccessible(true);
+                        CONSTRUCTORS.put(className, constructor);
+                    }
+                }
+                //noinspection ConstantConditions
+                return constructor.newInstance(mContext, attrs);
+            }
+        } catch (Exception e) {
+            throw new InflateException("Could not instantiate " + expectedType + " class "
+                    + className, e);
+        }
+    }
+
+    private void getTargetIds(XmlPullParser parser,
+            AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+                && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+            if (name.equals("target")) {
+                TypedArray a = mContext.obtainStyledAttributes(attrs, Styleable.TRANSITION_TARGET);
+                int id = TypedArrayUtils.getNamedResourceId(a, parser, "targetId",
+                        Styleable.TransitionTarget.TARGET_ID, 0);
+                String transitionName;
+                if (id != 0) {
+                    transition.addTarget(id);
+                } else if ((id = TypedArrayUtils.getNamedResourceId(a, parser, "excludeId",
+                        Styleable.TransitionTarget.EXCLUDE_ID, 0)) != 0) {
+                    transition.excludeTarget(id, true);
+                } else if ((transitionName = TypedArrayUtils.getNamedString(a, parser, "targetName",
+                        Styleable.TransitionTarget.TARGET_NAME)) != null) {
+                    transition.addTarget(transitionName);
+                } else if ((transitionName = TypedArrayUtils.getNamedString(a, parser,
+                        "excludeName", Styleable.TransitionTarget.EXCLUDE_NAME)) != null) {
+                    transition.excludeTarget(transitionName, true);
+                } else {
+                    String className = TypedArrayUtils.getNamedString(a, parser,
+                            "excludeClass", Styleable.TransitionTarget.EXCLUDE_CLASS);
+                    try {
+                        if (className != null) {
+                            Class clazz = Class.forName(className);
+                            transition.excludeTarget(clazz, true);
+                        } else if ((className = TypedArrayUtils.getNamedString(a, parser,
+                                "targetClass", Styleable.TransitionTarget.TARGET_CLASS)) != null) {
+                            Class clazz = Class.forName(className);
+                            transition.addTarget(clazz);
+                        }
+                    } catch (ClassNotFoundException e) {
+                        a.recycle();
+                        throw new RuntimeException("Could not create " + className, e);
+                    }
+                }
+                a.recycle();
+            } else {
+                throw new RuntimeException("Unknown scene name: " + parser.getName());
+            }
+        }
+    }
+
+    //
+    // TransitionManager loading
+    //
+
+    private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
+            AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+        TransitionManager transitionManager = null;
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+                && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+            if (name.equals("transitionManager")) {
+                transitionManager = new TransitionManager();
+            } else if (name.equals("transition") && (transitionManager != null)) {
+                loadTransition(attrs, parser, sceneRoot, transitionManager);
+            } else {
+                throw new RuntimeException("Unknown scene name: " + parser.getName());
+            }
+        }
+        return transitionManager;
+    }
+
+    private void loadTransition(AttributeSet attrs, XmlPullParser parser, ViewGroup sceneRoot,
+            TransitionManager transitionManager) throws Resources.NotFoundException {
+
+        TypedArray a = mContext.obtainStyledAttributes(attrs, Styleable.TRANSITION_MANAGER);
+        int transitionId = TypedArrayUtils.getNamedResourceId(a, parser, "transition",
+                Styleable.TransitionManager.TRANSITION, -1);
+        int fromId = TypedArrayUtils.getNamedResourceId(a, parser, "fromScene",
+                Styleable.TransitionManager.FROM_SCENE, -1);
+        Scene fromScene = (fromId < 0) ? null : Scene.getSceneForLayout(sceneRoot, fromId,
+                mContext);
+        int toId = TypedArrayUtils.getNamedResourceId(a, parser, "toScene",
+                Styleable.TransitionManager.TO_SCENE, -1);
+        Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext);
+
+        if (transitionId >= 0) {
+            Transition transition = inflateTransition(transitionId);
+            if (transition != null) {
+                if (toScene == null) {
+                    throw new RuntimeException("No toScene for transition ID " + transitionId);
+                }
+                if (fromScene == null) {
+                    transitionManager.setTransition(toScene, transition);
+                } else {
+                    transitionManager.setTransition(fromScene, toScene, transition);
+                }
+            }
+        }
+        a.recycle();
+    }
+
+}
diff --git a/transition/src/android/support/transition/TransitionListenerAdapter.java b/transition/src/android/support/transition/TransitionListenerAdapter.java
new file mode 100644
index 0000000..333fbfb
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionListenerAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link
+ * Transition.TransitionListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public class TransitionListenerAdapter implements Transition.TransitionListener {
+
+    @Override
+    public void onTransitionStart(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionEnd(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionCancel(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionPause(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionResume(@NonNull Transition transition) {
+    }
+
+}
diff --git a/transition/src/android/support/transition/TransitionManager.java b/transition/src/android/support/transition/TransitionManager.java
index da9ac08..d5f46ab 100644
--- a/transition/src/android/support/transition/TransitionManager.java
+++ b/transition/src/android/support/transition/TransitionManager.java
@@ -16,10 +16,18 @@
 
 package android.support.transition;
 
-import android.os.Build;
+import android.content.Context;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.view.ViewCompat;
+import android.util.Log;
+import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 
 /**
  * This class manages the set of transitions that fire when there is a
@@ -32,28 +40,291 @@
  * only necessary if the application wants different transition behavior
  * in these situations.
  *
- * <p>Unlike the platform version, this does not support declaration by XML resources.</p>
+ * <p>TransitionManagers can be declared in XML resource files inside the
+ * <code>res/transition</code> directory. TransitionManager resources consist of
+ * the <code>transitionManager</code>tag name, containing one or more
+ * <code>transition</code> tags, each of which describe the relationship of
+ * that transition to the from/to scene information in that tag.
+ * For example, here is a resource file that declares several scene
+ * transitions:</p>
+ *
+ * <pre>
+ *     &lt;transitionManager xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *         &lt;transition android:fromScene="@layout/transition_scene1"
+ *                     android:toScene="@layout/transition_scene2"
+ *                     android:transition="@transition/changebounds"/&gt;
+ *         &lt;transition android:fromScene="@layout/transition_scene2"
+ *                     android:toScene="@layout/transition_scene1"
+ *                     android:transition="@transition/changebounds"/&gt;
+ *         &lt;transition android:toScene="@layout/transition_scene3"
+ *                     android:transition="@transition/changebounds_fadein_together"/&gt;
+ *         &lt;transition android:fromScene="@layout/transition_scene3"
+ *                     android:toScene="@layout/transition_scene1"
+ *                     android:transition="@transition/changebounds_fadeout_sequential"/&gt;
+ *         &lt;transition android:fromScene="@layout/transition_scene3"
+ *                     android:toScene="@layout/transition_scene2"
+ *                     android:transition="@transition/changebounds_fadeout_sequential"/&gt;
+ *     &lt;/transitionManager&gt;
+ * </pre>
+ *
+ * <p>For each of the <code>fromScene</code> and <code>toScene</code> attributes,
+ * there is a reference to a standard XML layout file. This is equivalent to
+ * creating a scene from a layout in code by calling
+ * {@link Scene#getSceneForLayout(ViewGroup, int, Context)}. For the
+ * <code>transition</code> attribute, there is a reference to a resource
+ * file in the <code>res/transition</code> directory which describes that
+ * transition.</p>
  */
 public class TransitionManager {
 
-    private static TransitionManagerStaticsImpl sImpl;
+    private static final String LOG_TAG = "TransitionManager";
 
-    static {
-        if (Build.VERSION.SDK_INT < 19) {
-            sImpl = new TransitionManagerStaticsIcs();
-        } else {
-            sImpl = new TransitionManagerStaticsKitKat();
+    private static Transition sDefaultTransition = new AutoTransition();
+
+    private ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<>();
+    private ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions = new ArrayMap<>();
+    private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>
+            sRunningTransitions = new ThreadLocal<>();
+    private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<>();
+
+    /**
+     * Sets a specific transition to occur when the given scene is entered.
+     *
+     * @param scene      The scene which, when applied, will cause the given
+     *                   transition to run.
+     * @param transition The transition that will play when the given scene is
+     *                   entered. A value of null will result in the default behavior of
+     *                   using the default transition instead.
+     */
+    public void setTransition(@NonNull Scene scene, @Nullable Transition transition) {
+        mSceneTransitions.put(scene, transition);
+    }
+
+    /**
+     * Sets a specific transition to occur when the given pair of scenes is
+     * exited/entered.
+     *
+     * @param fromScene  The scene being exited when the given transition will
+     *                   be run
+     * @param toScene    The scene being entered when the given transition will
+     *                   be run
+     * @param transition The transition that will play when the given scene is
+     *                   entered. A value of null will result in the default behavior of
+     *                   using the default transition instead.
+     */
+    public void setTransition(@NonNull Scene fromScene, @NonNull Scene toScene,
+            @Nullable Transition transition) {
+        ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
+        if (sceneTransitionMap == null) {
+            sceneTransitionMap = new ArrayMap<>();
+            mScenePairTransitions.put(toScene, sceneTransitionMap);
+        }
+        sceneTransitionMap.put(fromScene, transition);
+    }
+
+    /**
+     * Returns the Transition for the given scene being entered. The result
+     * depends not only on the given scene, but also the scene which the
+     * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
+     *
+     * @param scene The scene being entered
+     * @return The Transition to be used for the given scene change. If no
+     * Transition was specified for this scene change, the default transition
+     * will be used instead.
+     */
+    private Transition getTransition(Scene scene) {
+        Transition transition;
+        ViewGroup sceneRoot = scene.getSceneRoot();
+        if (sceneRoot != null) {
+            // TODO: cached in Scene instead? long-term, cache in View itself
+            Scene currScene = Scene.getCurrentScene(sceneRoot);
+            if (currScene != null) {
+                ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions
+                        .get(scene);
+                if (sceneTransitionMap != null) {
+                    transition = sceneTransitionMap.get(currScene);
+                    if (transition != null) {
+                        return transition;
+                    }
+                }
+            }
+        }
+        transition = mSceneTransitions.get(scene);
+        return (transition != null) ? transition : sDefaultTransition;
+    }
+
+    /**
+     * This is where all of the work of a transition/scene-change is
+     * orchestrated. This method captures the start values for the given
+     * transition, exits the current Scene, enters the new scene, captures
+     * the end values for the transition, and finally plays the
+     * resulting values-populated transition.
+     *
+     * @param scene      The scene being entered
+     * @param transition The transition to play for this scene change
+     */
+    private static void changeScene(Scene scene, Transition transition) {
+        final ViewGroup sceneRoot = scene.getSceneRoot();
+
+        if (!sPendingTransitions.contains(sceneRoot)) {
+            if (transition == null) {
+                scene.enter();
+            } else {
+                sPendingTransitions.add(sceneRoot);
+
+                Transition transitionClone = transition.clone();
+                transitionClone.setSceneRoot(sceneRoot);
+
+                Scene oldScene = Scene.getCurrentScene(sceneRoot);
+                if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
+                    transitionClone.setCanRemoveViews(true);
+                }
+
+                sceneChangeSetup(sceneRoot, transitionClone);
+
+                scene.enter();
+
+                sceneChangeRunTransition(sceneRoot, transitionClone);
+            }
         }
     }
 
-    private TransitionManagerImpl mImpl;
-
-    public TransitionManager() {
-        if (Build.VERSION.SDK_INT < 19) {
-            mImpl = new TransitionManagerIcs();
-        } else {
-            mImpl = new TransitionManagerKitKat();
+    static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
+        WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
+                sRunningTransitions.get();
+        if (runningTransitions == null || runningTransitions.get() == null) {
+            ArrayMap<ViewGroup, ArrayList<Transition>> transitions = new ArrayMap<>();
+            runningTransitions = new WeakReference<>(transitions);
+            sRunningTransitions.set(runningTransitions);
         }
+        return runningTransitions.get();
+    }
+
+    private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
+            final Transition transition) {
+        if (transition != null && sceneRoot != null) {
+            MultiListener listener = new MultiListener(transition, sceneRoot);
+            sceneRoot.addOnAttachStateChangeListener(listener);
+            sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
+        }
+    }
+
+    /**
+     * This private utility class is used to listen for both OnPreDraw and
+     * OnAttachStateChange events. OnPreDraw events are the main ones we care
+     * about since that's what triggers the transition to take place.
+     * OnAttachStateChange events are also important in case the view is removed
+     * from the hierarchy before the OnPreDraw event takes place; it's used to
+     * clean up things since the OnPreDraw listener didn't get called in time.
+     */
+    private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
+            View.OnAttachStateChangeListener {
+
+        Transition mTransition;
+
+        ViewGroup mSceneRoot;
+
+        MultiListener(Transition transition, ViewGroup sceneRoot) {
+            mTransition = transition;
+            mSceneRoot = sceneRoot;
+        }
+
+        private void removeListeners() {
+            mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+            mSceneRoot.removeOnAttachStateChangeListener(this);
+        }
+
+        @Override
+        public void onViewAttachedToWindow(View v) {
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            removeListeners();
+
+            sPendingTransitions.remove(mSceneRoot);
+            ArrayList<Transition> runningTransitions = getRunningTransitions().get(mSceneRoot);
+            if (runningTransitions != null && runningTransitions.size() > 0) {
+                for (Transition runningTransition : runningTransitions) {
+                    runningTransition.resume(mSceneRoot);
+                }
+            }
+            mTransition.clearValues(true);
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            removeListeners();
+
+            // Don't start the transition if it's no longer pending.
+            if (!sPendingTransitions.remove(mSceneRoot)) {
+                return true;
+            }
+
+            // Add to running list, handle end to remove it
+            final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
+                    getRunningTransitions();
+            ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
+            ArrayList<Transition> previousRunningTransitions = null;
+            if (currentTransitions == null) {
+                currentTransitions = new ArrayList<>();
+                runningTransitions.put(mSceneRoot, currentTransitions);
+            } else if (currentTransitions.size() > 0) {
+                previousRunningTransitions = new ArrayList<>(currentTransitions);
+            }
+            currentTransitions.add(mTransition);
+            mTransition.addListener(new TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(@NonNull Transition transition) {
+                    ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
+                    currentTransitions.remove(transition);
+                }
+            });
+            mTransition.captureValues(mSceneRoot, false);
+            if (previousRunningTransitions != null) {
+                for (Transition runningTransition : previousRunningTransitions) {
+                    runningTransition.resume(mSceneRoot);
+                }
+            }
+            mTransition.playTransition(mSceneRoot);
+
+            return true;
+        }
+    }
+
+    private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
+        // Capture current values
+        ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
+
+        if (runningTransitions != null && runningTransitions.size() > 0) {
+            for (Transition runningTransition : runningTransitions) {
+                runningTransition.pause(sceneRoot);
+            }
+        }
+
+        if (transition != null) {
+            transition.captureValues(sceneRoot, true);
+        }
+
+        // Notify previous scene that it is being exited
+        Scene previousScene = Scene.getCurrentScene(sceneRoot);
+        if (previousScene != null) {
+            previousScene.exit();
+        }
+    }
+
+    /**
+     * Change to the given scene, using the
+     * appropriate transition for this particular scene change
+     * (as specified to the TransitionManager, or the default
+     * if no such transition exists).
+     *
+     * @param scene The Scene to change to
+     */
+    public void transitionTo(@NonNull Scene scene) {
+        // Auto transition if there is no transition declared for the Scene, but there is
+        // a root or parent view
+        changeScene(scene, getTransition(scene));
     }
 
     /**
@@ -63,7 +334,7 @@
      * @param scene The Scene to change to
      */
     public static void go(@NonNull Scene scene) {
-        sImpl.go(scene.mImpl);
+        changeScene(scene, sDefaultTransition);
     }
 
     /**
@@ -81,7 +352,7 @@
      *                   value of null causes the scene change to happen with no transition.
      */
     public static void go(@NonNull Scene scene, @Nullable Transition transition) {
-        sImpl.go(scene.mImpl, transition == null ? null : transition.mImpl);
+        changeScene(scene, transition);
     }
 
     /**
@@ -94,7 +365,7 @@
      * @param sceneRoot The root of the View hierarchy to run the transition on.
      */
     public static void beginDelayedTransition(@NonNull final ViewGroup sceneRoot) {
-        sImpl.beginDelayedTransition(sceneRoot);
+        beginDelayedTransition(sceneRoot, null);
     }
 
     /**
@@ -122,50 +393,38 @@
      */
     public static void beginDelayedTransition(@NonNull final ViewGroup sceneRoot,
             @Nullable Transition transition) {
-        sImpl.beginDelayedTransition(sceneRoot, transition == null ? null : transition.mImpl);
+        if (!sPendingTransitions.contains(sceneRoot) && ViewCompat.isLaidOut(sceneRoot)) {
+            if (Transition.DBG) {
+                Log.d(LOG_TAG, "beginDelayedTransition: root, transition = "
+                        + sceneRoot + ", " + transition);
+            }
+            sPendingTransitions.add(sceneRoot);
+            if (transition == null) {
+                transition = sDefaultTransition;
+            }
+            final Transition transitionClone = transition.clone();
+            sceneChangeSetup(sceneRoot, transitionClone);
+            Scene.setCurrentScene(sceneRoot, null);
+            sceneChangeRunTransition(sceneRoot, transitionClone);
+        }
     }
 
     /**
-     * Sets a specific transition to occur when the given scene is entered.
+     * Ends all pending and ongoing transitions on the specified scene root.
      *
-     * @param scene      The scene which, when applied, will cause the given
-     *                   transition to run.
-     * @param transition The transition that will play when the given scene is
-     *                   entered. A value of null will result in the default behavior of
-     *                   using the default transition instead.
+     * @param sceneRoot The root of the View hierarchy to end transitions on.
      */
-    public void setTransition(@NonNull Scene scene, @Nullable Transition transition) {
-        mImpl.setTransition(scene.mImpl, transition == null ? null : transition.mImpl);
-    }
-
-    /**
-     * Sets a specific transition to occur when the given pair of scenes is
-     * exited/entered.
-     *
-     * @param fromScene  The scene being exited when the given transition will
-     *                   be run
-     * @param toScene    The scene being entered when the given transition will
-     *                   be run
-     * @param transition The transition that will play when the given scene is
-     *                   entered. A value of null will result in the default behavior of
-     *                   using the default transition instead.
-     */
-    public void setTransition(@NonNull Scene fromScene, @NonNull Scene toScene,
-            @Nullable Transition transition) {
-        mImpl.setTransition(fromScene.mImpl, toScene.mImpl,
-                transition == null ? null : transition.mImpl);
-    }
-
-    /**
-     * Change to the given scene, using the
-     * appropriate transition for this particular scene change
-     * (as specified to the TransitionManager, or the default
-     * if no such transition exists).
-     *
-     * @param scene The Scene to change to
-     */
-    public void transitionTo(@NonNull Scene scene) {
-        mImpl.transitionTo(scene.mImpl);
+    public static void endTransitions(final ViewGroup sceneRoot) {
+        sPendingTransitions.remove(sceneRoot);
+        final ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
+        if (runningTransitions != null && !runningTransitions.isEmpty()) {
+            // Make a copy in case this is called by an onTransitionEnd listener
+            ArrayList<Transition> copy = new ArrayList<>(runningTransitions);
+            for (int i = copy.size() - 1; i >= 0; i--) {
+                final Transition transition = copy.get(i);
+                transition.forceToEnd(sceneRoot);
+            }
+        }
     }
 
 }
diff --git a/transition/src/android/support/transition/TransitionPropagation.java b/transition/src/android/support/transition/TransitionPropagation.java
new file mode 100644
index 0000000..76ce9b2
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionPropagation.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.view.ViewGroup;
+
+/**
+ * Extend <code>TransitionPropagation</code> to customize start delays for Animators created
+ * in {@link Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
+ * A Transition such as {@link Explode} defaults to using {@link CircularPropagation} and Views
+ * closer to the epicenter will move out of the scene later and into the scene sooner than Views
+ * farther from the epicenter, giving the appearance of inertia. With no TransitionPropagation, all
+ * Views will react simultaneously to the start of the transition.
+ *
+ * @see Transition#setPropagation(TransitionPropagation)
+ * @see Transition#getEpicenter()
+ */
+public abstract class TransitionPropagation {
+
+    /**
+     * Called by Transition to alter the Animator start delay. All start delays will be adjusted
+     * such that the minimum becomes zero.
+     *
+     * @param sceneRoot   The root of the View hierarchy running the transition.
+     * @param transition  The transition that created the Animator
+     * @param startValues The values for a specific target in the start scene.
+     * @param endValues   The values for the target in the end scene.
+     * @return A start delay to use with the Animator created by <code>transition</code>. The
+     * delay will be offset by the minimum delay of all <code>TransitionPropagation</code>s
+     * used in the Transition so that the smallest delay will be 0. Returned values may be
+     * negative.
+     */
+    public abstract long getStartDelay(ViewGroup sceneRoot, Transition transition,
+            TransitionValues startValues, TransitionValues endValues);
+
+    /**
+     * Captures the values in the start or end scene for the properties that this
+     * transition propagation monitors. These values are then passed as the startValues
+     * or endValues structure in a later call to
+     * {@link #getStartDelay(ViewGroup, Transition, TransitionValues, TransitionValues)}.
+     * The main concern for an implementation is what the
+     * properties are that the transition cares about and what the values are
+     * for all of those properties. The start and end values will be compared
+     * later during the
+     * {@link #getStartDelay(ViewGroup, Transition, TransitionValues, TransitionValues)}.
+     * method to determine the start delay.
+     *
+     * <p>Subclasses must implement this method. The method should only be called by the
+     * transition system; it is not intended to be called from external classes.</p>
+     *
+     * @param transitionValues The holder for any values that the Transition
+     *                         wishes to store. Values are stored in the <code>values</code> field
+     *                         of this TransitionValues object and are keyed from
+     *                         a String value. For example, to store a view's rotation value,
+     *                         a transition might call
+     *                         <code>transitionValues.values.put("appname:transitionname:rotation",
+     *                         view.getRotation())</code>. The target view will already be stored
+     *                         in
+     *                         the transitionValues structure when this method is called.
+     */
+    public abstract void captureValues(TransitionValues transitionValues);
+
+    /**
+     * Returns the set of property names stored in the {@link TransitionValues}
+     * object passed into {@link #captureValues(TransitionValues)} that
+     * this transition propagation cares about for the purposes of preventing
+     * duplicate capturing of property values.
+     *
+     * <p>A <code>TransitionPropagation</code> must override this method to prevent
+     * duplicate capturing of values and must contain at least one </p>
+     *
+     * @return An array of property names as described in the class documentation for
+     * {@link TransitionValues}.
+     */
+    public abstract String[] getPropagationProperties();
+
+}
diff --git a/transition/src/android/support/transition/TransitionSet.java b/transition/src/android/support/transition/TransitionSet.java
index f43eeb1..404245a 100644
--- a/transition/src/android/support/transition/TransitionSet.java
+++ b/transition/src/android/support/transition/TransitionSet.java
@@ -16,12 +16,24 @@
 
 package android.support.transition;
 
-import android.animation.Animator;
-import android.os.Build;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.util.AndroidRuntimeException;
+import android.util.AttributeSet;
+import android.view.View;
 import android.view.ViewGroup;
 
+import java.util.ArrayList;
+
 /**
  * A TransitionSet is a parent of child transitions (including other
  * TransitionSets). Using TransitionSets enables more complex
@@ -30,10 +42,28 @@
  * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
  * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
  *
- * <p>Unlike the platform version, this does not support declaration by XML resources.</p>
+ * <p>A TransitionSet can be described in a resource file by using the
+ * tag <code>transitionSet</code>, along with the standard
+ * attributes of {@code TransitionSet} and {@link Transition}. Child transitions of the
+ * TransitionSet object can be loaded by adding those child tags inside the
+ * enclosing <code>transitionSet</code> tag. For example, the following xml
+ * describes a TransitionSet that plays a Fade and then a ChangeBounds
+ * transition on the affected view targets:</p>
+ * <pre>
+ *     &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ *             android:ordering="sequential"&gt;
+ *         &lt;fade/&gt;
+ *         &lt;changeBounds/&gt;
+ *     &lt;/transitionSet&gt;
+ * </pre>
  */
 public class TransitionSet extends Transition {
 
+    private ArrayList<Transition> mTransitions = new ArrayList<>();
+    private boolean mPlayTogether = true;
+    private int mCurrentListeners;
+    private boolean mStarted = false;
+
     /**
      * A flag used to indicate that the child transitions of this set
      * should all start at the same time.
@@ -55,24 +85,16 @@
      * child transitions will play {@link #ORDERING_TOGETHER together}.
      */
     public TransitionSet() {
-        super(true);
-        if (Build.VERSION.SDK_INT < 19) {
-            mImpl = new TransitionSetIcs(this);
-        } else {
-            mImpl = new TransitionSetKitKat(this);
-        }
     }
 
-    /**
-     * Returns the ordering of this TransitionSet. By default, the value is
-     * {@link #ORDERING_TOGETHER}.
-     *
-     * @return {@link #ORDERING_TOGETHER} if child transitions will play at the same
-     * time, {@link #ORDERING_SEQUENTIAL} if they will play in sequence.
-     * @see #setOrdering(int)
-     */
-    public int getOrdering() {
-        return ((TransitionSetImpl) mImpl).getOrdering();
+    public TransitionSet(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.TRANSITION_SET);
+        int ordering = TypedArrayUtils.getNamedInt(a, (XmlResourceParser) attrs,
+                "transitionOrdering", Styleable.TransitionSet.TRANSITION_ORDERING,
+                TransitionSet.ORDERING_TOGETHER);
+        setOrdering(ordering);
+        a.recycle();
     }
 
     /**
@@ -85,11 +107,33 @@
      */
     @NonNull
     public TransitionSet setOrdering(int ordering) {
-        ((TransitionSetImpl) mImpl).setOrdering(ordering);
+        switch (ordering) {
+            case ORDERING_SEQUENTIAL:
+                mPlayTogether = false;
+                break;
+            case ORDERING_TOGETHER:
+                mPlayTogether = true;
+                break;
+            default:
+                throw new AndroidRuntimeException("Invalid parameter for TransitionSet "
+                        + "ordering: " + ordering);
+        }
         return this;
     }
 
     /**
+     * Returns the ordering of this TransitionSet. By default, the value is
+     * {@link #ORDERING_TOGETHER}.
+     *
+     * @return {@link #ORDERING_TOGETHER} if child transitions will play at the same
+     * time, {@link #ORDERING_SEQUENTIAL} if they will play in sequence.
+     * @see #setOrdering(int)
+     */
+    public int getOrdering() {
+        return mPlayTogether ? ORDERING_TOGETHER : ORDERING_SEQUENTIAL;
+    }
+
+    /**
      * Adds child transition to this set. The order in which this child transition
      * is added relative to other child transitions that are added, in addition to
      * the {@link #getOrdering() ordering} property, determines the
@@ -104,11 +148,200 @@
      */
     @NonNull
     public TransitionSet addTransition(@NonNull Transition transition) {
-        ((TransitionSetImpl) mImpl).addTransition(transition.mImpl);
+        mTransitions.add(transition);
+        transition.mParent = this;
+        if (mDuration >= 0) {
+            transition.setDuration(mDuration);
+        }
         return this;
     }
 
     /**
+     * Returns the number of child transitions in the TransitionSet.
+     *
+     * @return The number of child transitions in the TransitionSet.
+     * @see #addTransition(Transition)
+     * @see #getTransitionAt(int)
+     */
+    public int getTransitionCount() {
+        return mTransitions.size();
+    }
+
+    /**
+     * Returns the child Transition at the specified position in the TransitionSet.
+     *
+     * @param index The position of the Transition to retrieve.
+     * @see #addTransition(Transition)
+     * @see #getTransitionCount()
+     */
+    public Transition getTransitionAt(int index) {
+        if (index < 0 || index >= mTransitions.size()) {
+            return null;
+        }
+        return mTransitions.get(index);
+    }
+
+    /**
+     * Setting a non-negative duration on a TransitionSet causes all of the child
+     * transitions (current and future) to inherit this duration.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     * @return This transitionSet object.
+     */
+    @NonNull
+    @Override
+    public TransitionSet setDuration(long duration) {
+        super.setDuration(duration);
+        if (mDuration >= 0) {
+            int numTransitions = mTransitions.size();
+            for (int i = 0; i < numTransitions; ++i) {
+                mTransitions.get(i).setDuration(duration);
+            }
+        }
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet setStartDelay(long startDelay) {
+        return (TransitionSet) super.setStartDelay(startDelay);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet setInterpolator(@Nullable TimeInterpolator interpolator) {
+        return (TransitionSet) super.setInterpolator(interpolator);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet addTarget(@NonNull View target) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).addTarget(target);
+        }
+        return (TransitionSet) super.addTarget(target);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet addTarget(@IdRes int targetId) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).addTarget(targetId);
+        }
+        return (TransitionSet) super.addTarget(targetId);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet addTarget(@NonNull String targetName) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).addTarget(targetName);
+        }
+        return (TransitionSet) super.addTarget(targetName);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet addTarget(@NonNull Class targetType) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).addTarget(targetType);
+        }
+        return (TransitionSet) super.addTarget(targetType);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet addListener(@NonNull TransitionListener listener) {
+        return (TransitionSet) super.addListener(listener);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet removeTarget(@IdRes int targetId) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).removeTarget(targetId);
+        }
+        return (TransitionSet) super.removeTarget(targetId);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet removeTarget(@NonNull View target) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).removeTarget(target);
+        }
+        return (TransitionSet) super.removeTarget(target);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet removeTarget(@NonNull Class target) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).removeTarget(target);
+        }
+        return (TransitionSet) super.removeTarget(target);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet removeTarget(@NonNull String target) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).removeTarget(target);
+        }
+        return (TransitionSet) super.removeTarget(target);
+    }
+
+    @NonNull
+    @Override
+    public Transition excludeTarget(@NonNull View target, boolean exclude) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).excludeTarget(target, exclude);
+        }
+        return super.excludeTarget(target, exclude);
+    }
+
+    @NonNull
+    @Override
+    public Transition excludeTarget(@NonNull String targetName, boolean exclude) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).excludeTarget(targetName, exclude);
+        }
+        return super.excludeTarget(targetName, exclude);
+    }
+
+    @NonNull
+    @Override
+    public Transition excludeTarget(int targetId, boolean exclude) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).excludeTarget(targetId, exclude);
+        }
+        return super.excludeTarget(targetId, exclude);
+    }
+
+    @NonNull
+    @Override
+    public Transition excludeTarget(@NonNull Class type, boolean exclude) {
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).excludeTarget(type, exclude);
+        }
+        return super.excludeTarget(type, exclude);
+    }
+
+    @NonNull
+    @Override
+    public TransitionSet removeListener(@NonNull TransitionListener listener) {
+        return (TransitionSet) super.removeListener(listener);
+    }
+
+    @Override
+    public void setPathMotion(PathMotion pathMotion) {
+        super.setPathMotion(pathMotion);
+        for (int i = 0; i < mTransitions.size(); i++) {
+            mTransitions.get(i).setPathMotion(pathMotion);
+        }
+    }
+
+    /**
      * Removes the specified child transition from this set.
      *
      * @param transition The transition to be removed.
@@ -116,25 +349,244 @@
      */
     @NonNull
     public TransitionSet removeTransition(@NonNull Transition transition) {
-        ((TransitionSetImpl) mImpl).removeTransition(transition.mImpl);
+        mTransitions.remove(transition);
+        transition.mParent = null;
         return this;
     }
 
+    /**
+     * Sets up listeners for each of the child transitions. This is used to
+     * determine when this transition set is finished (all child transitions
+     * must finish first).
+     */
+    private void setupStartEndListeners() {
+        TransitionSetListener listener = new TransitionSetListener(this);
+        for (Transition childTransition : mTransitions) {
+            childTransition.addListener(listener);
+        }
+        mCurrentListeners = mTransitions.size();
+    }
+
+    /**
+     * This listener is used to detect when all child transitions are done, at
+     * which point this transition set is also done.
+     */
+    static class TransitionSetListener extends TransitionListenerAdapter {
+
+        TransitionSet mTransitionSet;
+
+        TransitionSetListener(TransitionSet transitionSet) {
+            mTransitionSet = transitionSet;
+        }
+
+        @Override
+        public void onTransitionStart(@NonNull Transition transition) {
+            if (!mTransitionSet.mStarted) {
+                mTransitionSet.start();
+                mTransitionSet.mStarted = true;
+            }
+        }
+
+        @Override
+        public void onTransitionEnd(@NonNull Transition transition) {
+            --mTransitionSet.mCurrentListeners;
+            if (mTransitionSet.mCurrentListeners == 0) {
+                // All child trans
+                mTransitionSet.mStarted = false;
+                mTransitionSet.end();
+            }
+            transition.removeListener(this);
+        }
+
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
     @Override
-    public void captureEndValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureEndValues(transitionValues);
+    protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
+            TransitionValuesMaps endValues, ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList) {
+        long startDelay = getStartDelay();
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; i++) {
+            Transition childTransition = mTransitions.get(i);
+            // We only set the start delay on the first transition if we are playing
+            // the transitions sequentially.
+            if (startDelay > 0 && (mPlayTogether || i == 0)) {
+                long childStartDelay = childTransition.getStartDelay();
+                if (childStartDelay > 0) {
+                    childTransition.setStartDelay(startDelay + childStartDelay);
+                } else {
+                    childTransition.setStartDelay(startDelay);
+                }
+            }
+            childTransition.createAnimators(sceneRoot, startValues, endValues, startValuesList,
+                    endValuesList);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    protected void runAnimators() {
+        if (mTransitions.isEmpty()) {
+            start();
+            end();
+            return;
+        }
+        setupStartEndListeners();
+        if (!mPlayTogether) {
+            // Setup sequence with listeners
+            // TODO: Need to add listeners in such a way that we can remove them later if canceled
+            for (int i = 1; i < mTransitions.size(); ++i) {
+                Transition previousTransition = mTransitions.get(i - 1);
+                final Transition nextTransition = mTransitions.get(i);
+                previousTransition.addListener(new TransitionListenerAdapter() {
+                    @Override
+                    public void onTransitionEnd(@NonNull Transition transition) {
+                        nextTransition.runAnimators();
+                        transition.removeListener(this);
+                    }
+                });
+            }
+            Transition firstTransition = mTransitions.get(0);
+            if (firstTransition != null) {
+                firstTransition.runAnimators();
+            }
+        } else {
+            for (Transition childTransition : mTransitions) {
+                childTransition.runAnimators();
+            }
+        }
     }
 
     @Override
     public void captureStartValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureStartValues(transitionValues);
+        if (isValidTarget(transitionValues.view)) {
+            for (Transition childTransition : mTransitions) {
+                if (childTransition.isValidTarget(transitionValues.view)) {
+                    childTransition.captureStartValues(transitionValues);
+                    transitionValues.mTargetedTransitions.add(childTransition);
+                }
+            }
+        }
     }
 
     @Override
-    @Nullable
-    public Animator createAnimator(@NonNull ViewGroup sceneRoot,
-            @NonNull TransitionValues startValues, @NonNull TransitionValues endValues) {
-        return mImpl.createAnimator(sceneRoot, startValues, endValues);
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        if (isValidTarget(transitionValues.view)) {
+            for (Transition childTransition : mTransitions) {
+                if (childTransition.isValidTarget(transitionValues.view)) {
+                    childTransition.captureEndValues(transitionValues);
+                    transitionValues.mTargetedTransitions.add(childTransition);
+                }
+            }
+        }
+    }
+
+    @Override
+    void capturePropagationValues(TransitionValues transitionValues) {
+        super.capturePropagationValues(transitionValues);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).capturePropagationValues(transitionValues);
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void pause(View sceneRoot) {
+        super.pause(sceneRoot);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).pause(sceneRoot);
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void resume(View sceneRoot) {
+        super.resume(sceneRoot);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).resume(sceneRoot);
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    protected void cancel() {
+        super.cancel();
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).cancel();
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    void forceToEnd(ViewGroup sceneRoot) {
+        super.forceToEnd(sceneRoot);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).forceToEnd(sceneRoot);
+        }
+    }
+
+    @Override
+    TransitionSet setSceneRoot(ViewGroup sceneRoot) {
+        super.setSceneRoot(sceneRoot);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).setSceneRoot(sceneRoot);
+        }
+        return this;
+    }
+
+    @Override
+    void setCanRemoveViews(boolean canRemoveViews) {
+        super.setCanRemoveViews(canRemoveViews);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).setCanRemoveViews(canRemoveViews);
+        }
+    }
+
+    @Override
+    public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
+        super.setEpicenterCallback(epicenterCallback);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).setEpicenterCallback(epicenterCallback);
+        }
+    }
+
+    @Override
+    String toString(String indent) {
+        String result = super.toString(indent);
+        for (int i = 0; i < mTransitions.size(); ++i) {
+            result += "\n" + mTransitions.get(i).toString(indent + "  ");
+        }
+        return result;
+    }
+
+    @Override
+    public Transition clone() {
+        TransitionSet clone = (TransitionSet) super.clone();
+        clone.mTransitions = new ArrayList<>();
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            clone.addTransition(mTransitions.get(i).clone());
+        }
+        return clone;
     }
 
 }
diff --git a/transition/src/android/support/transition/TransitionUtils.java b/transition/src/android/support/transition/TransitionUtils.java
new file mode 100644
index 0000000..e1fe644
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionUtils.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.TypeEvaluator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+class TransitionUtils {
+
+    private static final int MAX_IMAGE_SIZE = 1024 * 1024;
+
+    /**
+     * Creates a View using the bitmap copy of <code>view</code>. If <code>view</code> is large,
+     * the copy will use a scaled bitmap of the given view.
+     *
+     * @param sceneRoot The ViewGroup in which the view copy will be displayed.
+     * @param view      The view to create a copy of.
+     * @param parent    The parent of view.
+     */
+    static View copyViewImage(ViewGroup sceneRoot, View view, View parent) {
+        Matrix matrix = new Matrix();
+        matrix.setTranslate(-parent.getScrollX(), -parent.getScrollY());
+        ViewUtils.transformMatrixToGlobal(view, matrix);
+        ViewUtils.transformMatrixToLocal(sceneRoot, matrix);
+        RectF bounds = new RectF(0, 0, view.getWidth(), view.getHeight());
+        matrix.mapRect(bounds);
+        int left = Math.round(bounds.left);
+        int top = Math.round(bounds.top);
+        int right = Math.round(bounds.right);
+        int bottom = Math.round(bounds.bottom);
+
+        ImageView copy = new ImageView(view.getContext());
+        copy.setScaleType(ImageView.ScaleType.CENTER_CROP);
+        Bitmap bitmap = createViewBitmap(view, matrix, bounds);
+        if (bitmap != null) {
+            copy.setImageBitmap(bitmap);
+        }
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(right - left, View.MeasureSpec.EXACTLY);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(bottom - top, View.MeasureSpec.EXACTLY);
+        copy.measure(widthSpec, heightSpec);
+        copy.layout(left, top, right, bottom);
+        return copy;
+    }
+
+    /**
+     * Creates a Bitmap of the given view, using the Matrix matrix to transform to the local
+     * coordinates. <code>matrix</code> will be modified during the bitmap creation.
+     *
+     * <p>If the bitmap is large, it will be scaled uniformly down to at most 1MB size.</p>
+     *
+     * @param view   The view to create a bitmap for.
+     * @param matrix The matrix converting the view local coordinates to the coordinates that
+     *               the bitmap will be displayed in. <code>matrix</code> will be modified before
+     *               returning.
+     * @param bounds The bounds of the bitmap in the destination coordinate system (where the
+     *               view should be presented. Typically, this is matrix.mapRect(viewBounds);
+     * @return A bitmap of the given view or null if bounds has no width or height.
+     */
+    private static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
+        Bitmap bitmap = null;
+        int bitmapWidth = Math.round(bounds.width());
+        int bitmapHeight = Math.round(bounds.height());
+        if (bitmapWidth > 0 && bitmapHeight > 0) {
+            float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
+            bitmapWidth = (int) (bitmapWidth * scale);
+            bitmapHeight = (int) (bitmapHeight * scale);
+            matrix.postTranslate(-bounds.left, -bounds.top);
+            matrix.postScale(scale, scale);
+            bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            canvas.concat(matrix);
+            view.draw(canvas);
+        }
+        return bitmap;
+    }
+
+    static Animator mergeAnimators(Animator animator1, Animator animator2) {
+        if (animator1 == null) {
+            return animator2;
+        } else if (animator2 == null) {
+            return animator1;
+        } else {
+            AnimatorSet animatorSet = new AnimatorSet();
+            animatorSet.playTogether(animator1, animator2);
+            return animatorSet;
+        }
+    }
+
+    static class MatrixEvaluator implements TypeEvaluator<Matrix> {
+
+        final float[] mTempStartValues = new float[9];
+
+        final float[] mTempEndValues = new float[9];
+
+        final Matrix mTempMatrix = new Matrix();
+
+        @Override
+        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
+            startValue.getValues(mTempStartValues);
+            endValue.getValues(mTempEndValues);
+            for (int i = 0; i < 9; i++) {
+                float diff = mTempEndValues[i] - mTempStartValues[i];
+                mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
+            }
+            mTempMatrix.setValues(mTempEndValues);
+            return mTempMatrix;
+        }
+
+    }
+
+}
diff --git a/transition/src/android/support/transition/TransitionValues.java b/transition/src/android/support/transition/TransitionValues.java
new file mode 100644
index 0000000..aaec8d0
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionValues.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data structure which holds cached values for the transition.
+ * The view field is the target which all of the values pertain to.
+ * The values field is a map which holds information for fields
+ * according to names selected by the transitions. These names should
+ * be unique to avoid clobbering values stored by other transitions,
+ * such as the convention project:transition_name:property_name. For
+ * example, the platform might store a property "alpha" in a transition
+ * "Fader" as "android:fader:alpha".
+ *
+ * <p>These values are cached during the
+ * {@link android.support.transition.Transition#captureStartValues(TransitionValues)}
+ * capture} phases of a scene change, once when the start values are captured
+ * and again when the end values are captured. These start/end values are then
+ * passed into the transitions via the
+ * for {@link android.support.transition.Transition#createAnimator(android.view.ViewGroup,
+ * TransitionValues, TransitionValues)} method.</p>
+ */
+public class TransitionValues {
+
+    /**
+     * The set of values tracked by transitions for this scene
+     */
+    public final Map<String, Object> values = new HashMap<>();
+
+    /**
+     * The View with these values
+     */
+    public View view;
+
+    /**
+     * The Transitions that targeted this view.
+     */
+    final ArrayList<Transition> mTargetedTransitions = new ArrayList<>();
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof TransitionValues) {
+            if (view == ((TransitionValues) other).view) {
+                if (values.equals(((TransitionValues) other).values)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * view.hashCode() + values.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
+        returnValue += "    view = " + view + "\n";
+        returnValue += "    values:";
+        for (String s : values.keySet()) {
+            returnValue += "    " + s + ": " + values.get(s) + "\n";
+        }
+        return returnValue;
+    }
+
+}
diff --git a/transition/src/android/support/transition/TransitionValuesMaps.java b/transition/src/android/support/transition/TransitionValuesMaps.java
new file mode 100644
index 0000000..98db792
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionValuesMaps.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.support.annotation.RequiresApi;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.View;
+
+@RequiresApi(14)
+class TransitionValuesMaps {
+
+    final ArrayMap<View, TransitionValues> mViewValues = new ArrayMap<>();
+
+    final SparseArray<View> mIdValues = new SparseArray<>();
+
+    final LongSparseArray<View> mItemIdValues = new LongSparseArray<>();
+
+    final ArrayMap<String, View> mNameValues = new ArrayMap<>();
+
+}
diff --git a/transition/src/android/support/transition/TranslationAnimationCreator.java b/transition/src/android/support/transition/TranslationAnimationCreator.java
new file mode 100644
index 0000000..7f2fd1d
--- /dev/null
+++ b/transition/src/android/support/transition/TranslationAnimationCreator.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.view.View;
+
+/**
+ * This class is used by Slide and Explode to create an animator that goes from the start
+ * position to the end position. It takes into account the canceled position so that it
+ * will not blink out or shift suddenly when the transition is interrupted.
+ */
+class TranslationAnimationCreator {
+
+    /**
+     * Creates an animator that can be used for x and/or y translations. When interrupted,
+     * it sets a tag to keep track of the position so that it may be continued from position.
+     *
+     * @param view         The view being moved. This may be in the overlay for onDisappear.
+     * @param values       The values containing the view in the view hierarchy.
+     * @param viewPosX     The x screen coordinate of view
+     * @param viewPosY     The y screen coordinate of view
+     * @param startX       The start translation x of view
+     * @param startY       The start translation y of view
+     * @param endX         The end translation x of view
+     * @param endY         The end translation y of view
+     * @param interpolator The interpolator to use with this animator.
+     * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
+     * a previous interruption, in which case it moves from the current position to (endX, endY).
+     */
+    static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
+            float startX, float startY, float endX, float endY, TimeInterpolator interpolator) {
+        float terminalX = view.getTranslationX();
+        float terminalY = view.getTranslationY();
+        int[] startPosition = (int[]) values.view.getTag(R.id.transition_position);
+        if (startPosition != null) {
+            startX = startPosition[0] - viewPosX + terminalX;
+            startY = startPosition[1] - viewPosY + terminalY;
+        }
+        // Initial position is at translation startX, startY, so position is offset by that amount
+        int startPosX = viewPosX + Math.round(startX - terminalX);
+        int startPosY = viewPosY + Math.round(startY - terminalY);
+
+        view.setTranslationX(startX);
+        view.setTranslationY(startY);
+        if (startX == endX && startY == endY) {
+            return null;
+        }
+        ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view,
+                PropertyValuesHolder.ofFloat(View.TRANSLATION_X, startX, endX),
+                PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, startY, endY));
+
+        TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
+                startPosX, startPosY, terminalX, terminalY);
+        anim.addListener(listener);
+        AnimatorUtils.addPauseListener(anim, listener);
+        anim.setInterpolator(interpolator);
+        return anim;
+    }
+
+    private static class TransitionPositionListener extends AnimatorListenerAdapter {
+
+        private final View mViewInHierarchy;
+        private final View mMovingView;
+        private final int mStartX;
+        private final int mStartY;
+        private int[] mTransitionPosition;
+        private float mPausedX;
+        private float mPausedY;
+        private final float mTerminalX;
+        private final float mTerminalY;
+
+        private TransitionPositionListener(View movingView, View viewInHierarchy,
+                int startX, int startY, float terminalX, float terminalY) {
+            mMovingView = movingView;
+            mViewInHierarchy = viewInHierarchy;
+            mStartX = startX - Math.round(mMovingView.getTranslationX());
+            mStartY = startY - Math.round(mMovingView.getTranslationY());
+            mTerminalX = terminalX;
+            mTerminalY = terminalY;
+            mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transition_position);
+            if (mTransitionPosition != null) {
+                mViewInHierarchy.setTag(R.id.transition_position, null);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (mTransitionPosition == null) {
+                mTransitionPosition = new int[2];
+            }
+            mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
+            mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
+            mViewInHierarchy.setTag(R.id.transition_position, mTransitionPosition);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animator) {
+            mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
+        }
+
+        @Override
+        public void onAnimationPause(Animator animator) {
+            mPausedX = mMovingView.getTranslationX();
+            mPausedY = mMovingView.getTranslationY();
+            mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
+        }
+
+        @Override
+        public void onAnimationResume(Animator animator) {
+            mMovingView.setTranslationX(mPausedX);
+            mMovingView.setTranslationY(mPausedY);
+        }
+    }
+
+}
diff --git a/transition/src/android/support/transition/ViewGroupUtils.java b/transition/src/android/support/transition/ViewGroupUtils.java
new file mode 100644
index 0000000..370e3a3
--- /dev/null
+++ b/transition/src/android/support/transition/ViewGroupUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.view.ViewGroup;
+
+/**
+ * Compatibility utilities for platform features of {@link ViewGroup}.
+ */
+class ViewGroupUtils {
+
+    private static final ViewGroupUtilsImpl IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 18) {
+            IMPL = new ViewGroupUtilsApi18();
+        } else {
+            IMPL = new ViewGroupUtilsApi14();
+        }
+    }
+
+    /**
+     * Backward-compatible {@link ViewGroup#getOverlay()}.
+     */
+    static ViewGroupOverlayImpl getOverlay(@NonNull ViewGroup group) {
+        return IMPL.getOverlay(group);
+    }
+
+    /**
+     * Provides access to the hidden ViewGroup#suppressLayout method.
+     */
+    static void suppressLayout(@NonNull ViewGroup group, boolean suppress) {
+        IMPL.suppressLayout(group, suppress);
+    }
+
+}
diff --git a/transition/src/android/support/transition/ViewUtils.java b/transition/src/android/support/transition/ViewUtils.java
new file mode 100644
index 0000000..66c3076
--- /dev/null
+++ b/transition/src/android/support/transition/ViewUtils.java
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
+import android.util.Log;
+import android.util.Property;
+import android.view.View;
+
+import java.lang.reflect.Field;
+
+/**
+ * Compatibility utilities for platform features of {@link View}.
+ */
+class ViewUtils {
+
+    private static final ViewUtilsImpl IMPL;
+    private static final String TAG = "ViewUtils";
+
+    private static Field sViewFlagsField;
+    private static boolean sViewFlagsFieldFetched;
+    private static final int VISIBILITY_MASK = 0x0000000C;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 22) {
+            IMPL = new ViewUtilsApi22();
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new ViewUtilsApi21();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new ViewUtilsApi19();
+        } else if (Build.VERSION.SDK_INT >= 18) {
+            IMPL = new ViewUtilsApi18();
+        } else {
+            IMPL = new ViewUtilsApi14();
+        }
+    }
+
+    /**
+     * A {@link Property} for animating transitionAlpha value of a View.
+     */
+    static final Property<View, Float> TRANSITION_ALPHA =
+            new Property<View, Float>(Float.class, "translationAlpha") {
+
+                @Override
+                public Float get(View view) {
+                    return getTransitionAlpha(view);
+                }
+
+                @Override
+                public void set(View view, Float alpha) {
+                    setTransitionAlpha(view, alpha);
+                }
+
+            };
+
+    static final Property<View, Rect> CLIP_BOUNDS =
+            new Property<View, Rect>(Rect.class, "clipBounds") {
+
+                @Override
+                public Rect get(View view) {
+                    return ViewCompat.getClipBounds(view);
+                }
+
+                @Override
+                public void set(View view, Rect clipBounds) {
+                    ViewCompat.setClipBounds(view, clipBounds);
+                }
+
+            };
+
+    /**
+     * Backward-compatible {@link View#getOverlay()}.
+     */
+    static ViewOverlayImpl getOverlay(@NonNull View view) {
+        return IMPL.getOverlay(view);
+    }
+
+    /**
+     * Backward-compatible {@link View#getWindowId()}.
+     */
+    static WindowIdImpl getWindowId(@NonNull View view) {
+        return IMPL.getWindowId(view);
+    }
+
+    static void setTransitionAlpha(@NonNull View view, float alpha) {
+        IMPL.setTransitionAlpha(view, alpha);
+    }
+
+    static float getTransitionAlpha(@NonNull View view) {
+        return IMPL.getTransitionAlpha(view);
+    }
+
+    /**
+     * This method needs to be called before an animation using {@link #setTransitionAlpha(View,
+     * float)} in order to make its behavior backward-compatible.
+     */
+    static void saveNonTransitionAlpha(@NonNull View view) {
+        IMPL.saveNonTransitionAlpha(view);
+    }
+
+    /**
+     * This method needs to be called after an animation using
+     * {@link #setTransitionAlpha(View, float)} if {@link #saveNonTransitionAlpha(View)} has been
+     * called.
+     */
+    static void clearNonTransitionAlpha(@NonNull View view) {
+        IMPL.clearNonTransitionAlpha(view);
+    }
+
+    /**
+     * Copy of a hidden platform method, View#setTransitionVisibility.
+     *
+     * <p>Change the visibility of the View without triggering any other changes. This is
+     * important for transitions, where visibility changes should not adjust focus or
+     * trigger a new layout. This is only used when the visibility has already been changed
+     * and we need a transient value during an animation. When the animation completes,
+     * the original visibility value is always restored.</p>
+     *
+     * @param view       The target view.
+     * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE}, or
+     *                   {@link View#GONE}.
+     */
+    static void setTransitionVisibility(@NonNull View view, int visibility) {
+        fetchViewFlagsField();
+        if (sViewFlagsField != null) {
+            try {
+                int viewFlags = sViewFlagsField.getInt(view);
+                sViewFlagsField.setInt(view, (viewFlags & ~VISIBILITY_MASK) | visibility);
+            } catch (IllegalAccessException e) {
+                // Do nothing
+            }
+        }
+    }
+
+    /**
+     * Modifies the input matrix such that it maps view-local coordinates to
+     * on-screen coordinates.
+     *
+     * <p>On API Level 21 and above, this includes transformation matrix applied to {@code
+     * ViewRootImpl}, but not on older platforms. This difference is balanced out by the
+     * implementation difference in other related platform APIs and their backport, such as
+     * GhostView.</p>
+     *
+     * @param view   target view
+     * @param matrix input matrix to modify
+     */
+    static void transformMatrixToGlobal(@NonNull View view, @NonNull Matrix matrix) {
+        IMPL.transformMatrixToGlobal(view, matrix);
+    }
+
+    /**
+     * Modifies the input matrix such that it maps on-screen coordinates to
+     * view-local coordinates.
+     *
+     * <p>On API Level 21 and above, this includes transformation matrix applied to {@code
+     * ViewRootImpl}, but not on older platforms. This difference is balanced out by the
+     * implementation difference in other related platform APIs and their backport, such as
+     * GhostView.</p>
+     *
+     * @param view   target view
+     * @param matrix input matrix to modify
+     */
+    static void transformMatrixToLocal(@NonNull View view, @NonNull Matrix matrix) {
+        IMPL.transformMatrixToLocal(view, matrix);
+    }
+
+    /**
+     * Sets the transformation matrix for animation.
+     *
+     * @param v The view
+     * @param m The matrix
+     */
+    static void setAnimationMatrix(@NonNull View v, @Nullable Matrix m) {
+        IMPL.setAnimationMatrix(v, m);
+    }
+
+    /**
+     * Assign a size and position to this view.
+     *
+     * @param left   Left position, relative to parent
+     * @param top    Top position, relative to parent
+     * @param right  Right position, relative to parent
+     * @param bottom Bottom position, relative to parent
+     */
+    static void setLeftTopRightBottom(@NonNull View v, int left, int top, int right, int bottom) {
+        IMPL.setLeftTopRightBottom(v, left, top, right, bottom);
+    }
+
+    private static void fetchViewFlagsField() {
+        if (!sViewFlagsFieldFetched) {
+            try {
+                sViewFlagsField = View.class.getDeclaredField("mViewFlags");
+                sViewFlagsField.setAccessible(true);
+            } catch (NoSuchFieldException e) {
+                Log.i(TAG, "fetchViewFlagsField: ");
+            }
+            sViewFlagsFieldFetched = true;
+        }
+    }
+
+}
diff --git a/transition/src/android/support/transition/Visibility.java b/transition/src/android/support/transition/Visibility.java
index 67b91ce..70fc0d7 100644
--- a/transition/src/android/support/transition/Visibility.java
+++ b/transition/src/android/support/transition/Visibility.java
@@ -16,12 +16,25 @@
 
 package android.support.transition;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.animation.Animator;
-import android.os.Build;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes. Visibility is determined not just by the
@@ -31,34 +44,117 @@
  * information to determine the specific animations to run when visibility
  * changes occur. Subclasses should implement one or both of the methods
  * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
- * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
+ * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or
+ * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},
+ * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
  */
-public abstract class Visibility extends Transition implements VisibilityInterface {
+public abstract class Visibility extends Transition {
 
-    public Visibility() {
-        this(false);
+    static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
+    private static final String PROPNAME_PARENT = "android:visibility:parent";
+    private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
+
+    /**
+     * Mode used in {@link #setMode(int)} to make the transition
+     * operate on targets that are appearing. Maybe be combined with
+     * {@link #MODE_OUT} to target Visibility changes both in and out.
+     */
+    public static final int MODE_IN = 0x1;
+
+    /**
+     * Mode used in {@link #setMode(int)} to make the transition
+     * operate on targets that are disappearing. Maybe be combined with
+     * {@link #MODE_IN} to target Visibility changes both in and out.
+     */
+    public static final int MODE_OUT = 0x2;
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef(flag = true, value = {MODE_IN, MODE_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Mode {
     }
 
-    Visibility(boolean deferred) {
-        super(true);
-        if (!deferred) {
-            if (Build.VERSION.SDK_INT >= 19) {
-                mImpl = new VisibilityKitKat();
-            } else {
-                mImpl = new VisibilityIcs();
-            }
-            mImpl.init(this);
+    private static final String[] sTransitionProperties = {
+            PROPNAME_VISIBILITY,
+            PROPNAME_PARENT,
+    };
+
+    private static class VisibilityInfo {
+        boolean mVisibilityChange;
+        boolean mFadeIn;
+        int mStartVisibility;
+        int mEndVisibility;
+        ViewGroup mStartParent;
+        ViewGroup mEndParent;
+    }
+
+    private int mMode = MODE_IN | MODE_OUT;
+
+    public Visibility() {
+    }
+
+    public Visibility(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.VISIBILITY_TRANSITION);
+        @Mode
+        int mode = TypedArrayUtils.getNamedInt(a, (XmlResourceParser) attrs,
+                "transitionVisibilityMode",
+                Styleable.VisibilityTransition.TRANSITION_VISIBILITY_MODE, 0);
+        a.recycle();
+        if (mode != 0) {
+            setMode(mode);
         }
     }
 
+    /**
+     * Changes the transition to support appearing and/or disappearing Views, depending
+     * on <code>mode</code>.
+     *
+     * @param mode The behavior supported by this transition, a combination of
+     *             {@link #MODE_IN} and {@link #MODE_OUT}.
+     */
+    public void setMode(@Mode int mode) {
+        if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
+            throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
+        }
+        mMode = mode;
+    }
+
+    /**
+     * Returns whether appearing and/or disappearing Views are supported.
+     *
+     * @return whether appearing and/or disappearing Views are supported. A combination of
+     * {@link #MODE_IN} and {@link #MODE_OUT}.
+     */
+    @Mode
+    public int getMode() {
+        return mMode;
+    }
+
+    @Nullable
     @Override
-    public void captureEndValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureEndValues(transitionValues);
+    public String[] getTransitionProperties() {
+        return sTransitionProperties;
+    }
+
+    private void captureValues(TransitionValues transitionValues) {
+        int visibility = transitionValues.view.getVisibility();
+        transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
+        transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
+        int[] loc = new int[2];
+        transitionValues.view.getLocationOnScreen(loc);
+        transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
     }
 
     @Override
     public void captureStartValues(@NonNull TransitionValues transitionValues) {
-        mImpl.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        captureValues(transitionValues);
     }
 
     /**
@@ -76,9 +172,86 @@
      * @return True if the view reference by <code>values</code> is visible,
      * false otherwise.
      */
-    @Override
     public boolean isVisible(TransitionValues values) {
-        return ((VisibilityImpl) mImpl).isVisible(values);
+        if (values == null) {
+            return false;
+        }
+        int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
+        View parent = (View) values.values.get(PROPNAME_PARENT);
+
+        return visibility == View.VISIBLE && parent != null;
+    }
+
+    private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
+            TransitionValues endValues) {
+        final VisibilityInfo visInfo = new VisibilityInfo();
+        visInfo.mVisibilityChange = false;
+        visInfo.mFadeIn = false;
+        if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) {
+            visInfo.mStartVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+            visInfo.mStartParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
+        } else {
+            visInfo.mStartVisibility = -1;
+            visInfo.mStartParent = null;
+        }
+        if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) {
+            visInfo.mEndVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+            visInfo.mEndParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
+        } else {
+            visInfo.mEndVisibility = -1;
+            visInfo.mEndParent = null;
+        }
+        if (startValues != null && endValues != null) {
+            if (visInfo.mStartVisibility == visInfo.mEndVisibility
+                    && visInfo.mStartParent == visInfo.mEndParent) {
+                return visInfo;
+            } else {
+                if (visInfo.mStartVisibility != visInfo.mEndVisibility) {
+                    if (visInfo.mStartVisibility == View.VISIBLE) {
+                        visInfo.mFadeIn = false;
+                        visInfo.mVisibilityChange = true;
+                    } else if (visInfo.mEndVisibility == View.VISIBLE) {
+                        visInfo.mFadeIn = true;
+                        visInfo.mVisibilityChange = true;
+                    }
+                    // no visibilityChange if going between INVISIBLE and GONE
+                } else /* if (visInfo.mStartParent != visInfo.mEndParent) */ {
+                    if (visInfo.mEndParent == null) {
+                        visInfo.mFadeIn = false;
+                        visInfo.mVisibilityChange = true;
+                    } else if (visInfo.mStartParent == null) {
+                        visInfo.mFadeIn = true;
+                        visInfo.mVisibilityChange = true;
+                    }
+                }
+            }
+        } else if (startValues == null && visInfo.mEndVisibility == View.VISIBLE) {
+            visInfo.mFadeIn = true;
+            visInfo.mVisibilityChange = true;
+        } else if (endValues == null && visInfo.mStartVisibility == View.VISIBLE) {
+            visInfo.mFadeIn = false;
+            visInfo.mVisibilityChange = true;
+        }
+        return visInfo;
+    }
+
+    @Nullable
+    @Override
+    public Animator createAnimator(@NonNull ViewGroup sceneRoot,
+            @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
+        VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
+        if (visInfo.mVisibilityChange
+                && (visInfo.mStartParent != null || visInfo.mEndParent != null)) {
+            if (visInfo.mFadeIn) {
+                return onAppear(sceneRoot, startValues, visInfo.mStartVisibility,
+                        endValues, visInfo.mEndVisibility);
+            } else {
+                return onDisappear(sceneRoot, startValues, visInfo.mStartVisibility,
+                        endValues, visInfo.mEndVisibility
+                );
+            }
+        }
+        return null;
     }
 
     /**
@@ -96,11 +269,46 @@
      * overall transition for this scene change. A null value means no animation
      * should be run.
      */
-    @Override
+    @SuppressWarnings("UnusedParameters")
     public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
             TransitionValues endValues, int endVisibility) {
-        return ((VisibilityImpl) mImpl).onAppear(sceneRoot, startValues, startVisibility,
-                endValues, endVisibility);
+        if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
+            return null;
+        }
+        if (startValues == null) {
+            View endParent = (View) endValues.view.getParent();
+            TransitionValues startParentValues = getMatchedTransitionValues(endParent,
+                    false);
+            TransitionValues endParentValues = getTransitionValues(endParent, false);
+            VisibilityInfo parentVisibilityInfo =
+                    getVisibilityChangeInfo(startParentValues, endParentValues);
+            if (parentVisibilityInfo.mVisibilityChange) {
+                return null;
+            }
+        }
+        return onAppear(sceneRoot, endValues.view, startValues, endValues);
+    }
+
+    /**
+     * The default implementation of this method returns a null Animator. Subclasses should
+     * override this method to make targets appear with the desired transition. The
+     * method should only be called from
+     * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
+     *
+     * @param sceneRoot   The root of the transition hierarchy
+     * @param view        The View to make appear. This will be in the target scene's View
+     *                    hierarchy
+     *                    and
+     *                    will be VISIBLE.
+     * @param startValues The target values in the start scene
+     * @param endValues   The target values in the end scene
+     * @return An Animator to be started at the appropriate time in the
+     * overall transition for this scene change. A null value means no animation
+     * should be run.
+     */
+    public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+            TransitionValues endValues) {
+        return null;
     }
 
     /**
@@ -118,14 +326,248 @@
      * overall transition for this scene change. A null value means no animation
      * should be run.
      */
-    @Override
+    @SuppressWarnings("UnusedParameters")
     public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
             int startVisibility, TransitionValues endValues, int endVisibility) {
-        return ((VisibilityImpl) mImpl).onDisappear(sceneRoot, startValues, startVisibility,
-                endValues, endVisibility);
+        if ((mMode & MODE_OUT) != MODE_OUT) {
+            return null;
+        }
+
+        View startView = (startValues != null) ? startValues.view : null;
+        View endView = (endValues != null) ? endValues.view : null;
+        View overlayView = null;
+        View viewToKeep = null;
+        if (endView == null || endView.getParent() == null) {
+            if (endView != null) {
+                // endView was removed from its parent - add it to the overlay
+                overlayView = endView;
+            } else if (startView != null) {
+                // endView does not exist. Use startView only under certain
+                // conditions, because placing a view in an overlay necessitates
+                // it being removed from its current parent
+                if (startView.getParent() == null) {
+                    // no parent - safe to use
+                    overlayView = startView;
+                } else if (startView.getParent() instanceof View) {
+                    View startParent = (View) startView.getParent();
+                    TransitionValues startParentValues = getTransitionValues(startParent, true);
+                    TransitionValues endParentValues = getMatchedTransitionValues(startParent,
+                            true);
+                    VisibilityInfo parentVisibilityInfo =
+                            getVisibilityChangeInfo(startParentValues, endParentValues);
+                    if (!parentVisibilityInfo.mVisibilityChange) {
+                        overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
+                                startParent);
+                    } else if (startParent.getParent() == null) {
+                        int id = startParent.getId();
+                        if (id != View.NO_ID && sceneRoot.findViewById(id) != null
+                                && mCanRemoveViews) {
+                            // no parent, but its parent is unparented  but the parent
+                            // hierarchy has been replaced by a new hierarchy with the same id
+                            // and it is safe to un-parent startView
+                            overlayView = startView;
+                        }
+                    }
+                }
+            }
+        } else {
+            // visibility change
+            if (endVisibility == View.INVISIBLE) {
+                viewToKeep = endView;
+            } else {
+                // Becoming GONE
+                if (startView == endView) {
+                    viewToKeep = endView;
+                } else {
+                    overlayView = startView;
+                }
+            }
+        }
+        final int finalVisibility = endVisibility;
+
+        if (overlayView != null && startValues != null) {
+            // TODO: Need to do this for general case of adding to overlay
+            int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
+            int screenX = screenLoc[0];
+            int screenY = screenLoc[1];
+            int[] loc = new int[2];
+            sceneRoot.getLocationOnScreen(loc);
+            overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
+            overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
+            final ViewGroupOverlayImpl overlay = ViewGroupUtils.getOverlay(sceneRoot);
+            overlay.add(overlayView);
+            Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
+            if (animator == null) {
+                overlay.remove(overlayView);
+            } else {
+                final View finalOverlayView = overlayView;
+                animator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        overlay.remove(finalOverlayView);
+                    }
+                });
+            }
+            return animator;
+        }
+
+        if (viewToKeep != null) {
+            int originalVisibility = viewToKeep.getVisibility();
+            ViewUtils.setTransitionVisibility(viewToKeep, View.VISIBLE);
+            Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
+            if (animator != null) {
+                DisappearListener disappearListener = new DisappearListener(viewToKeep,
+                        finalVisibility, true);
+                animator.addListener(disappearListener);
+                AnimatorUtils.addPauseListener(animator, disappearListener);
+                addListener(disappearListener);
+            } else {
+                ViewUtils.setTransitionVisibility(viewToKeep, originalVisibility);
+            }
+            return animator;
+        }
+        return null;
     }
 
-    // TODO: Implement API 21; onAppear (4 params), onDisappear (4 params), getMode, setMode
+    /**
+     * The default implementation of this method returns a null Animator. Subclasses should
+     * override this method to make targets disappear with the desired transition. The
+     * method should only be called from
+     * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
+     *
+     * @param sceneRoot   The root of the transition hierarchy
+     * @param view        The View to make disappear. This will be in the target scene's View
+     *                    hierarchy or in an {@link android.view.ViewGroupOverlay} and will be
+     *                    VISIBLE.
+     * @param startValues The target values in the start scene
+     * @param endValues   The target values in the end scene
+     * @return An Animator to be started at the appropriate time in the
+     * overall transition for this scene change. A null value means no animation
+     * should be run.
+     */
+    public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+            TransitionValues endValues) {
+        return null;
+    }
+
+    @Override
+    public boolean isTransitionRequired(TransitionValues startValues, TransitionValues newValues) {
+        if (startValues == null && newValues == null) {
+            return false;
+        }
+        if (startValues != null && newValues != null
+                && newValues.values.containsKey(PROPNAME_VISIBILITY)
+                != startValues.values.containsKey(PROPNAME_VISIBILITY)) {
+            // The transition wasn't targeted in either the start or end, so it couldn't
+            // have changed.
+            return false;
+        }
+        VisibilityInfo changeInfo = getVisibilityChangeInfo(startValues, newValues);
+        return changeInfo.mVisibilityChange && (changeInfo.mStartVisibility == View.VISIBLE
+                || changeInfo.mEndVisibility == View.VISIBLE);
+    }
+
+    private static class DisappearListener extends AnimatorListenerAdapter
+            implements TransitionListener, AnimatorUtilsApi14.AnimatorPauseListenerCompat {
+
+        private final View mView;
+        private final int mFinalVisibility;
+        private final ViewGroup mParent;
+        private final boolean mSuppressLayout;
+
+        private boolean mLayoutSuppressed;
+        boolean mCanceled = false;
+
+        DisappearListener(View view, int finalVisibility, boolean suppressLayout) {
+            mView = view;
+            mFinalVisibility = finalVisibility;
+            mParent = (ViewGroup) view.getParent();
+            mSuppressLayout = suppressLayout;
+            // Prevent a layout from including mView in its calculation.
+            suppressLayout(true);
+        }
+
+        // This overrides both AnimatorListenerAdapter and
+        // AnimatorUtilsApi14.AnimatorPauseListenerCompat
+        @Override
+        public void onAnimationPause(Animator animation) {
+            if (!mCanceled) {
+                ViewUtils.setTransitionVisibility(mView, mFinalVisibility);
+            }
+        }
+
+        // This overrides both AnimatorListenerAdapter and
+        // AnimatorUtilsApi14.AnimatorPauseListenerCompat
+        @Override
+        public void onAnimationResume(Animator animation) {
+            if (!mCanceled) {
+                ViewUtils.setTransitionVisibility(mView, View.VISIBLE);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mCanceled = true;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            hideViewWhenNotCanceled();
+        }
+
+        @Override
+        public void onTransitionStart(@NonNull Transition transition) {
+            // Do nothing
+        }
+
+        @Override
+        public void onTransitionEnd(@NonNull Transition transition) {
+            hideViewWhenNotCanceled();
+            transition.removeListener(this);
+        }
+
+        @Override
+        public void onTransitionCancel(@NonNull Transition transition) {
+        }
+
+        @Override
+        public void onTransitionPause(@NonNull Transition transition) {
+            suppressLayout(false);
+        }
+
+        @Override
+        public void onTransitionResume(@NonNull Transition transition) {
+            suppressLayout(true);
+        }
+
+        private void hideViewWhenNotCanceled() {
+            if (!mCanceled) {
+                // Recreate the parent's display list in case it includes mView.
+                ViewUtils.setTransitionVisibility(mView, mFinalVisibility);
+                if (mParent != null) {
+                    mParent.invalidate();
+                }
+            }
+            // Layout is allowed now that the View is in its final state
+            suppressLayout(false);
+        }
+
+        private void suppressLayout(boolean suppress) {
+            if (mSuppressLayout && mLayoutSuppressed != suppress && mParent != null) {
+                mLayoutSuppressed = suppress;
+                ViewGroupUtils.suppressLayout(mParent, suppress);
+            }
+        }
+    }
+
     // TODO: Implement API 23; isTransitionRequired
 
 }
diff --git a/transition/src/android/support/transition/VisibilityPropagation.java b/transition/src/android/support/transition/VisibilityPropagation.java
new file mode 100644
index 0000000..c9ada2e
--- /dev/null
+++ b/transition/src/android/support/transition/VisibilityPropagation.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import android.view.View;
+
+/**
+ * Base class for <code>TransitionPropagation</code>s that care about
+ * View Visibility and the center position of the View.
+ */
+public abstract class VisibilityPropagation extends TransitionPropagation {
+
+    /**
+     * The property key used for {@link android.view.View#getVisibility()}.
+     */
+    private static final String PROPNAME_VISIBILITY = "android:visibilityPropagation:visibility";
+
+    /**
+     * The property key used for the center of the View in screen coordinates. This is an
+     * int[2] with the index 0 taking the x coordinate and index 1 taking the y coordinate.
+     */
+    private static final String PROPNAME_VIEW_CENTER = "android:visibilityPropagation:center";
+
+    private static final String[] VISIBILITY_PROPAGATION_VALUES = {
+            PROPNAME_VISIBILITY,
+            PROPNAME_VIEW_CENTER,
+    };
+
+    @Override
+    public void captureValues(TransitionValues values) {
+        View view = values.view;
+        Integer visibility = (Integer) values.values.get(Visibility.PROPNAME_VISIBILITY);
+        if (visibility == null) {
+            visibility = view.getVisibility();
+        }
+        values.values.put(PROPNAME_VISIBILITY, visibility);
+        int[] loc = new int[2];
+        view.getLocationOnScreen(loc);
+        loc[0] += Math.round(view.getTranslationX());
+        loc[0] += view.getWidth() / 2;
+        loc[1] += Math.round(view.getTranslationY());
+        loc[1] += view.getHeight() / 2;
+        values.values.put(PROPNAME_VIEW_CENTER, loc);
+    }
+
+    @Override
+    public String[] getPropagationProperties() {
+        return VISIBILITY_PROPAGATION_VALUES;
+    }
+
+    /**
+     * Returns {@link android.view.View#getVisibility()} for the View at the time the values
+     * were captured.
+     * @param values The TransitionValues captured at the start or end of the Transition.
+     * @return {@link android.view.View#getVisibility()} for the View at the time the values
+     * were captured.
+     */
+    public int getViewVisibility(TransitionValues values) {
+        if (values == null) {
+            return View.GONE;
+        }
+        Integer visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
+        if (visibility == null) {
+            return View.GONE;
+        }
+        return visibility;
+    }
+
+    /**
+     * Returns the View's center x coordinate, relative to the screen, at the time the values
+     * were captured.
+     * @param values The TransitionValues captured at the start or end of the Transition.
+     * @return the View's center x coordinate, relative to the screen, at the time the values
+     * were captured.
+     */
+    public int getViewX(TransitionValues values) {
+        return getViewCoordinate(values, 0);
+    }
+
+    /**
+     * Returns the View's center y coordinate, relative to the screen, at the time the values
+     * were captured.
+     * @param values The TransitionValues captured at the start or end of the Transition.
+     * @return the View's center y coordinate, relative to the screen, at the time the values
+     * were captured.
+     */
+    public int getViewY(TransitionValues values) {
+        return getViewCoordinate(values, 1);
+    }
+
+    private static int getViewCoordinate(TransitionValues values, int coordinateIndex) {
+        if (values == null) {
+            return -1;
+        }
+
+        int[] coordinates = (int[]) values.values.get(PROPNAME_VIEW_CENTER);
+        if (coordinates == null) {
+            return -1;
+        }
+
+        return coordinates[coordinateIndex];
+    }
+
+}
diff --git a/transition/tests/AndroidManifest.xml b/transition/tests/AndroidManifest.xml
index 695d1c9..35be32b 100755
--- a/transition/tests/AndroidManifest.xml
+++ b/transition/tests/AndroidManifest.xml
@@ -15,25 +15,13 @@
   limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.transition.test">
-
-    <uses-sdk
-        android:minSdkVersion="14"
-        android:targetSdkVersion="23"
-        tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application
         android:supportsRtl="true">
-        <uses-library android:name="android.test.runner"/>
-
         <activity
             android:name="android.support.transition.TransitionActivity"/>
     </application>
 
-    <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="android.support.transition.test"/>
-
 </manifest>
diff --git a/transition/tests/res/layout/activity_transition.xml b/transition/tests/res/layout/activity_transition.xml
index cfbbe61..cf25ef9 100644
--- a/transition/tests/res/layout/activity_transition.xml
+++ b/transition/tests/res/layout/activity_transition.xml
@@ -14,7 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:id="@+id/root"
-             android:layout_width="match_parent"
-             android:layout_height="match_parent"/>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"/>
diff --git a/transition/tests/res/layout/scene1.xml b/transition/tests/res/layout/scene1.xml
index 4d83ebd..07e88b8 100644
--- a/transition/tests/res/layout/scene1.xml
+++ b/transition/tests/res/layout/scene1.xml
@@ -14,30 +14,29 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="match_parent"
-             android:layout_height="match_parent"
-             android:id="@+id/container">
-
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/holder"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:transitionName="holder">
     <View
-        android:id="@+id/view0"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_gravity="end|top"
-        android:background="#f00"/>
-
+        android:id="@+id/redSquare"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:background="#F00"
+        android:transitionName="red"/>
     <View
-        android:id="@+id/view1"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_gravity="center"
-        android:background="#0f0"/>
-
-    <View
-        android:id="@+id/view2"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_gravity="end|bottom"
-        android:background="#00f"/>
-
-</FrameLayout>
+        android:id="@+id/greenSquare"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:layout_below="@id/redSquare"
+        android:background="#0F0"
+        android:transitionName="green"/>
+    <TextView
+        android:id="@+id/hello"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/hello"
+        android:transitionName="hello"/>
+</RelativeLayout>
diff --git a/transition/tests/res/layout/scene10.xml b/transition/tests/res/layout/scene10.xml
new file mode 100644
index 0000000..7e4c804
--- /dev/null
+++ b/transition/tests/res/layout/scene10.xml
@@ -0,0 +1,52 @@
+<?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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:transitionGroup="false"
+    android:transitionName="holder"
+    android:id="@+id/holder">
+    <View
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:background="#F00"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:id="@+id/redSquare"/>
+    <View
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:background="#0F0"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:id="@+id/greenSquare"/>
+    <View
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:background="#00F"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentBottom="true"
+        android:id="@+id/blueSquare"/>
+    <View
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:background="#FF0"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentBottom="true"
+        android:id="@+id/yellowSquare"/>
+</RelativeLayout>
diff --git a/transition/tests/res/layout/scene4.xml b/transition/tests/res/layout/scene4.xml
new file mode 100644
index 0000000..12c3d62
--- /dev/null
+++ b/transition/tests/res/layout/scene4.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/holder"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:transitionName="holder"/>
diff --git a/transition/tests/res/layout/scene5.xml b/transition/tests/res/layout/scene5.xml
new file mode 100644
index 0000000..11e876d
--- /dev/null
+++ b/transition/tests/res/layout/scene5.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:transitionName="holder"
+                android:id="@+id/holder">
+    <TextView
+        android:id="@+id/text"
+        android:text="@string/longText"
+        android:layout_width="100dp"
+        android:layout_height="100dp"/>
+</RelativeLayout>
diff --git a/transition/tests/res/layout/scene6.xml b/transition/tests/res/layout/scene6.xml
new file mode 100644
index 0000000..79a36df
--- /dev/null
+++ b/transition/tests/res/layout/scene6.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/holder"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:transitionName="holder">
+
+    <View
+        android:id="@+id/greenSquare"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:background="#0F0"
+        android:transitionName="green"/>
+
+    <View
+        android:id="@+id/redSquare"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:layout_below="@id/greenSquare"
+        android:background="#F00"
+        android:transitionName="red"/>
+
+    <TextView
+        android:id="@+id/hello"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/hello"
+        android:transitionName="hello"/>
+
+</RelativeLayout>
diff --git a/transition/tests/res/layout/scene9.xml b/transition/tests/res/layout/scene9.xml
new file mode 100644
index 0000000..fd779c2
--- /dev/null
+++ b/transition/tests/res/layout/scene9.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:transitionName="holder"
+                android:id="@+id/holder">
+    <FrameLayout
+        android:layout_marginTop="50dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView
+            android:id="@+id/text"
+            android:text="@string/longText"
+            android:layout_width="100dp"
+            android:layout_height="100dp"/>
+    </FrameLayout>
+</RelativeLayout>
diff --git a/transition/tests/res/layout/scene0.xml b/transition/tests/res/layout/support_scene0.xml
similarity index 100%
rename from transition/tests/res/layout/scene0.xml
rename to transition/tests/res/layout/support_scene0.xml
diff --git a/transition/tests/res/layout/support_scene1.xml b/transition/tests/res/layout/support_scene1.xml
new file mode 100644
index 0000000..1c6ff90
--- /dev/null
+++ b/transition/tests/res/layout/support_scene1.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:id="@+id/container">
+
+    <View
+        android:id="@+id/view0"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="end|top"
+        android:background="#f00"/>
+
+    <View
+        android:id="@+id/view1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="center"
+        android:background="#0f0"/>
+
+    <View
+        android:id="@+id/view2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="end|bottom"
+        android:background="#00f"/>
+
+</FrameLayout>
diff --git a/transition/tests/res/transition/arc_motion.xml b/transition/tests/res/transition/arc_motion.xml
new file mode 100644
index 0000000..1fe9203
--- /dev/null
+++ b/transition/tests/res/transition/arc_motion.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+    <arcMotion android:minimumVerticalAngle="1"
+               android:minimumHorizontalAngle="2"
+               android:maximumAngle="53"/>
+</changeBounds>
diff --git a/transition/tests/res/transition/auto_transition.xml b/transition/tests/res/transition/auto_transition.xml
new file mode 100644
index 0000000..ae9ed46
--- /dev/null
+++ b/transition/tests/res/transition/auto_transition.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+<autoTransition/>
diff --git a/transition/tests/res/transition/change_bounds.xml b/transition/tests/res/transition/change_bounds.xml
new file mode 100644
index 0000000..1b4097e
--- /dev/null
+++ b/transition/tests/res/transition/change_bounds.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<changeBounds
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:resizeClip="true"/>
diff --git a/transition/tests/res/transition/change_clip_bounds.xml b/transition/tests/res/transition/change_clip_bounds.xml
new file mode 100644
index 0000000..e917c54
--- /dev/null
+++ b/transition/tests/res/transition/change_clip_bounds.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+<changeClipBounds/>
diff --git a/transition/tests/res/transition/change_image_transform.xml b/transition/tests/res/transition/change_image_transform.xml
new file mode 100644
index 0000000..c7dce05
--- /dev/null
+++ b/transition/tests/res/transition/change_image_transform.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+<changeImageTransform/>
diff --git a/transition/tests/res/transition/change_scroll.xml b/transition/tests/res/transition/change_scroll.xml
new file mode 100644
index 0000000..e09df96
--- /dev/null
+++ b/transition/tests/res/transition/change_scroll.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+<changeScroll/>
diff --git a/transition/tests/res/transition/change_transform.xml b/transition/tests/res/transition/change_transform.xml
new file mode 100644
index 0000000..be692fb
--- /dev/null
+++ b/transition/tests/res/transition/change_transform.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<changeTransform
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:reparent="false"
+    android:reparentWithOverlay="false"/>
diff --git a/transition/tests/res/transition/custom_path_motion.xml b/transition/tests/res/transition/custom_path_motion.xml
new file mode 100644
index 0000000..de0e51b
--- /dev/null
+++ b/transition/tests/res/transition/custom_path_motion.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<changeBounds>
+    <pathMotion class="android.support.transition.TransitionInflaterTest$CustomPathMotion"/>
+</changeBounds>
diff --git a/transition/tests/res/transition/custom_transition.xml b/transition/tests/res/transition/custom_transition.xml
new file mode 100644
index 0000000..1d17d44
--- /dev/null
+++ b/transition/tests/res/transition/custom_transition.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+<transition class="android.support.transition.TransitionInflaterTest$CustomTransition"/>
diff --git a/transition/tests/res/transition/explode.xml b/transition/tests/res/transition/explode.xml
new file mode 100644
index 0000000..71e0d26
--- /dev/null
+++ b/transition/tests/res/transition/explode.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.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"
+         android:transitionVisibilityMode="mode_in"/>
diff --git a/transition/tests/res/transition/fade.xml b/transition/tests/res/transition/fade.xml
new file mode 100644
index 0000000..b36fa2b
--- /dev/null
+++ b/transition/tests/res/transition/fade.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.
+-->
+<fade xmlns:android="http://schemas.android.com/apk/res/android"
+      android:fadingMode="fade_out"/>
diff --git a/transition/tests/res/transition/pattern_path_motion.xml b/transition/tests/res/transition/pattern_path_motion.xml
new file mode 100644
index 0000000..82e0bb2
--- /dev/null
+++ b/transition/tests/res/transition/pattern_path_motion.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+    <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
+</changeBounds>
diff --git a/transition/tests/res/transition/slide.xml b/transition/tests/res/transition/slide.xml
new file mode 100644
index 0000000..743d803
--- /dev/null
+++ b/transition/tests/res/transition/slide.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<slide
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:slideEdge="top"/>
diff --git a/transition/tests/res/transition/target_classes.xml b/transition/tests/res/transition/target_classes.xml
new file mode 100644
index 0000000..adfaee1
--- /dev/null
+++ b/transition/tests/res/transition/target_classes.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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+    <targets>
+        <target android:targetClass="android.widget.TextView"/>
+        <target android:targetClass="android.widget.ImageView"/>
+    </targets>
+</changeBounds>
diff --git a/transition/tests/res/transition/target_ids.xml b/transition/tests/res/transition/target_ids.xml
new file mode 100644
index 0000000..eb17888
--- /dev/null
+++ b/transition/tests/res/transition/target_ids.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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+    <targets>
+        <target android:targetId="@+id/hello"/>
+        <target android:targetId="@+id/world"/>
+    </targets>
+</changeBounds>
diff --git a/transition/tests/res/transition/target_names.xml b/transition/tests/res/transition/target_names.xml
new file mode 100644
index 0000000..6485198
--- /dev/null
+++ b/transition/tests/res/transition/target_names.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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+    <targets>
+        <target android:targetName="hello"/>
+        <target android:targetName="world"/>
+    </targets>
+</changeBounds>
diff --git a/transition/tests/res/transition/transition_constructors.xml b/transition/tests/res/transition/transition_constructors.xml
new file mode 100644
index 0000000..0f188fa
--- /dev/null
+++ b/transition/tests/res/transition/transition_constructors.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<transitionSet>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationFade"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationChangeBounds"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationChangeImageTransform"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationChangeTransform"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationAutoTransition"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationChangeClipBounds"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationChangeScroll"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationExplode"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationSlide"/>
+    <transition class="android.support.transition.TransitionInflaterTest$InflationTransitionSet"/>
+</transitionSet>
diff --git a/transition/tests/res/transition/transition_set.xml b/transition/tests/res/transition/transition_set.xml
new file mode 100644
index 0000000..045860a
--- /dev/null
+++ b/transition/tests/res/transition/transition_set.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+               android:transitionOrdering="sequential">
+    <changeBounds/>
+    <fade/>
+</transitionSet>
diff --git a/transition/tests/res/values/strings.xml b/transition/tests/res/values/strings.xml
new file mode 100644
index 0000000..aa87c7f
--- /dev/null
+++ b/transition/tests/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?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>
+    <string name="hello">Hello</string>
+    <string name="longText">Gummi bears sugar plum pudding. Carrot cake chupa chups lollipop brownie candy canes carrot cake. Chocolate cake dragée chocolate danish halvah brownie. Cake jelly-o danish jelly beans carrot cake toffee jelly-o. Danish sesame snaps soufflé chocolate cupcake jujubes pudding pudding candy canes. Sesame snaps sweet chupa chups marzipan tart. Dessert brownie marzipan powder. Biscuit sugar plum soufflé topping cheesecake. Jelly-o ice cream candy canes tart. Brownie ice cream cake. Chocolate bar cake tart powder. Cookie candy canes marzipan donut jelly beans cheesecake marzipan. Carrot cake dragée cupcake liquorice tiramisu chocolate cake powder macaroon. Liquorice sugar plum powder dessert jelly.</string>
+</resources>
diff --git a/transition/tests/src/android/support/transition/ArcMotionTest.java b/transition/tests/src/android/support/transition/ArcMotionTest.java
new file mode 100644
index 0000000..75d6117
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ArcMotionTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Path;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ArcMotionTest extends PathMotionTest {
+
+    @Test
+    public void test90Quadrants() {
+        ArcMotion arcMotion = new ArcMotion();
+        arcMotion.setMaximumAngle(90);
+
+        Path expected = arcWithPoint(0, 100, 100, 0, 100, 100);
+        Path path = arcMotion.getPath(0, 100, 100, 0);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(100, 0, 0, -100, 0, 0);
+        path = arcMotion.getPath(100, 0, 0, -100);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(0, -100, -100, 0, 0, 0);
+        path = arcMotion.getPath(0, -100, -100, 0);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(-100, 0, 0, 100, -100, 100);
+        path = arcMotion.getPath(-100, 0, 0, 100);
+        assertPathMatches(expected, path);
+    }
+
+    @Test
+    public void test345Triangles() {
+        // 3-4-5 triangles are easy to calculate the control points
+        ArcMotion arcMotion = new ArcMotion();
+        arcMotion.setMaximumAngle(90);
+        Path expected;
+        Path path;
+
+        expected = arcWithPoint(0, 120, 160, 0, 125, 120);
+        path = arcMotion.getPath(0, 120, 160, 0);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(0, 160, 120, 0, 120, 125);
+        path = arcMotion.getPath(0, 160, 120, 0);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(-120, 0, 0, 160, -120, 125);
+        path = arcMotion.getPath(-120, 0, 0, 160);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(-160, 0, 0, 120, -125, 120);
+        path = arcMotion.getPath(-160, 0, 0, 120);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(0, -120, -160, 0, -35, 0);
+        path = arcMotion.getPath(0, -120, -160, 0);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(0, -160, -120, 0, 0, -35);
+        path = arcMotion.getPath(0, -160, -120, 0);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(120, 0, 0, -160, 0, -35);
+        path = arcMotion.getPath(120, 0, 0, -160);
+        assertPathMatches(expected, path);
+
+        expected = arcWithPoint(160, 0, 0, -120, 35, 0);
+        path = arcMotion.getPath(160, 0, 0, -120);
+        assertPathMatches(expected, path);
+    }
+
+    private static Path arcWithPoint(float startX, float startY, float endX, float endY,
+            float eX, float eY) {
+        float c1x = (eX + startX) / 2;
+        float c1y = (eY + startY) / 2;
+        float c2x = (eX + endX) / 2;
+        float c2y = (eY + endY) / 2;
+        Path path = new Path();
+        path.moveTo(startX, startY);
+        path.cubicTo(c1x, c1y, c2x, c2y, endX, endY);
+        return path;
+    }
+
+    @Test
+    public void testMaximumAngle() {
+        ArcMotion arcMotion = new ArcMotion();
+        arcMotion.setMaximumAngle(45f);
+        assertEquals(45f, arcMotion.getMaximumAngle(), 0.0f);
+
+        float ratio = (float) Math.tan(Math.PI / 8);
+        float ex = 50 + (50 * ratio);
+        float ey = ex;
+
+        Path expected = arcWithPoint(0, 100, 100, 0, ex, ey);
+        Path path = arcMotion.getPath(0, 100, 100, 0);
+        assertPathMatches(expected, path);
+    }
+
+    @Test
+    public void testMinimumHorizontalAngle() {
+        ArcMotion arcMotion = new ArcMotion();
+        arcMotion.setMinimumHorizontalAngle(45);
+        assertEquals(45, arcMotion.getMinimumHorizontalAngle(), 0.0f);
+
+        float ex = 37.5f;
+        float ey = (float) (Math.tan(Math.PI / 4) * 50);
+        Path expected = arcWithPoint(0, 0, 100, 50, ex, ey);
+        Path path = arcMotion.getPath(0, 0, 100, 50);
+        assertPathMatches(expected, path);
+
+        // Pretty much the same, but follows a different path.
+        expected = arcWithPoint(0, 0, 100.001f, 50, ex, ey);
+        path = arcMotion.getPath(0, 0, 100.001f, 50);
+        assertPathMatches(expected, path);
+
+        // Moving in the opposite direction.
+        expected = arcWithPoint(100, 50, 0, 0, ex, ey);
+        path = arcMotion.getPath(100, 50, 0, 0);
+        assertPathMatches(expected, path);
+
+        // With x < y.
+        ex = 0;
+        ey = (float) (Math.tan(Math.PI / 4) * 62.5f);
+        expected = arcWithPoint(0, 0, 50, 100, ex, ey);
+        path = arcMotion.getPath(0, 0, 50, 100);
+        assertPathMatches(expected, path);
+
+        // Pretty much the same, but follows a different path.
+        expected = arcWithPoint(0, 0, 50, 100.001f, ex, ey);
+        path = arcMotion.getPath(0, 0, 50, 100.001f);
+        assertPathMatches(expected, path);
+
+        // Moving in the opposite direction.
+        expected = arcWithPoint(50, 100, 0, 0, ex, ey);
+        path = arcMotion.getPath(50, 100, 0, 0);
+        assertPathMatches(expected, path);
+    }
+
+    @Test
+    public void testMinimumVerticalAngle() {
+        ArcMotion arcMotion = new ArcMotion();
+        arcMotion.setMinimumVerticalAngle(45);
+        assertEquals(45, arcMotion.getMinimumVerticalAngle(), 0.0f);
+
+        float ex = 0;
+        float ey = 62.5f;
+        Path expected = arcWithPoint(0, 0, 50, 100, ex, ey);
+        Path path = arcMotion.getPath(0, 0, 50, 100);
+        assertPathMatches(expected, path);
+
+        // Pretty much the same, but follows a different path.
+        expected = arcWithPoint(0, 0, 50, 100.001f, ex, ey);
+        path = arcMotion.getPath(0, 0, 50, 100.001f);
+        assertPathMatches(expected, path);
+
+        // Moving in opposite direction.
+        expected = arcWithPoint(50, 100, 0, 0, ex, ey);
+        path = arcMotion.getPath(50, 100, 0, 0);
+        assertPathMatches(expected, path);
+
+        // With x > y.
+        ex = (float) (Math.tan(Math.PI / 4) * 37.5f);
+        ey = 50;
+        expected = arcWithPoint(0, 0, 100, 50, ex, ey);
+        path = arcMotion.getPath(0, 0, 100, 50);
+        assertPathMatches(expected, path);
+
+        // Pretty much the same, but follows a different path.
+        expected = arcWithPoint(0, 0, 100.001f, 50, ex, ey);
+        path = arcMotion.getPath(0, 0, 100.001f, 50);
+        assertPathMatches(expected, path);
+
+        // Moving in opposite direction.
+        expected = arcWithPoint(100, 50, 0, 0, ex, ey);
+        path = arcMotion.getPath(100, 50, 0, 0);
+        assertPathMatches(expected, path);
+
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/AutoTransitionTest.java b/transition/tests/src/android/support/transition/AutoTransitionTest.java
new file mode 100644
index 0000000..137f6d3
--- /dev/null
+++ b/transition/tests/src/android/support/transition/AutoTransitionTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import android.graphics.Color;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@MediumTest
+public class AutoTransitionTest extends BaseTest {
+
+    private LinearLayout mRoot;
+    private View mView0;
+    private View mView1;
+
+    @UiThreadTest
+    @Before
+    public void setUp() {
+        mRoot = (LinearLayout) rule.getActivity().getRoot();
+        mView0 = new View(rule.getActivity());
+        mView0.setBackgroundColor(Color.RED);
+        mRoot.addView(mView0, new LinearLayout.LayoutParams(100, 100));
+        mView1 = new View(rule.getActivity());
+        mView1.setBackgroundColor(Color.BLUE);
+        mRoot.addView(mView1, new LinearLayout.LayoutParams(100, 100));
+    }
+
+    @Test
+    public void testLayoutBetweenFadeAndChangeBounds() throws Throwable {
+        final LayoutCounter counter = new LayoutCounter();
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertThat(mView1.getY(), is(100.f));
+                assertThat(mView0.getVisibility(), is(View.VISIBLE));
+                mView1.addOnLayoutChangeListener(counter);
+            }
+        });
+        final SyncTransitionListener listener = new SyncTransitionListener(
+                SyncTransitionListener.EVENT_END);
+        final Transition transition = new AutoTransition();
+        transition.addListener(listener);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, transition);
+                // This makes view0 fade out and causes view1 to move upwards.
+                mView0.setVisibility(View.GONE);
+            }
+        });
+        assertThat("Timed out waiting for the TransitionListener",
+                listener.await(), is(true));
+        assertThat(mView1.getY(), is(0.f));
+        assertThat(mView0.getVisibility(), is(View.GONE));
+        counter.reset();
+        listener.reset();
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, transition);
+                // Revert
+                mView0.setVisibility(View.VISIBLE);
+            }
+        });
+        assertThat("Timed out waiting for the TransitionListener",
+                listener.await(), is(true));
+        assertThat(mView1.getY(), is(100.f));
+        assertThat(mView0.getVisibility(), is(View.VISIBLE));
+    }
+
+    private static class LayoutCounter implements View.OnLayoutChangeListener {
+
+        private int mCalledCount;
+
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                int oldLeft, int oldTop, int oldRight, int oldBottom) {
+            mCalledCount++;
+            // There should not be more than one layout request to view1.
+            if (mCalledCount > 1) {
+                fail("View layout happened too many times");
+            }
+        }
+
+        void reset() {
+            mCalledCount = 0;
+        }
+
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/BaseTest.java b/transition/tests/src/android/support/transition/BaseTest.java
index ddf43c3..4ffb2f9 100644
--- a/transition/tests/src/android/support/transition/BaseTest.java
+++ b/transition/tests/src/android/support/transition/BaseTest.java
@@ -22,7 +22,6 @@
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
-@SuppressWarnings("unchecked")
 @RunWith(AndroidJUnit4.class)
 public abstract class BaseTest {
 
@@ -30,7 +29,7 @@
     public final ActivityTestRule<TransitionActivity> rule;
 
     BaseTest() {
-        rule = new ActivityTestRule(TransitionActivity.class);
+        rule = new ActivityTestRule<>(TransitionActivity.class);
     }
 
 }
diff --git a/transition/tests/src/android/support/transition/BaseTransitionTest.java b/transition/tests/src/android/support/transition/BaseTransitionTest.java
new file mode 100644
index 0000000..5d39d94
--- /dev/null
+++ b/transition/tests/src/android/support/transition/BaseTransitionTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.support.test.InstrumentationRegistry;
+import android.support.transition.test.R;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import org.junit.Before;
+
+import java.util.ArrayList;
+
+public abstract class BaseTransitionTest extends BaseTest {
+
+    ArrayList<View> mTransitionTargets = new ArrayList<>();
+    LinearLayout mRoot;
+    Transition mTransition;
+    Transition.TransitionListener mListener;
+    float mAnimatedValue;
+
+    @Before
+    public void setUp() {
+        InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
+        mRoot = (LinearLayout) rule.getActivity().findViewById(R.id.root);
+        mTransitionTargets.clear();
+        mTransition = createTransition();
+        mListener = mock(Transition.TransitionListener.class);
+        mTransition.addListener(mListener);
+    }
+
+    Transition createTransition() {
+        return new TestTransition();
+    }
+
+    void waitForStart() {
+        verify(mListener, timeout(3000)).onTransitionStart(any(Transition.class));
+    }
+
+    void waitForEnd() {
+        verify(mListener, timeout(3000)).onTransitionEnd(any(Transition.class));
+    }
+
+    Scene loadScene(final int layoutId) throws Throwable {
+        final Scene[] scene = new Scene[1];
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                scene[0] = Scene.getSceneForLayout(mRoot, layoutId, rule.getActivity());
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        return scene[0];
+    }
+
+    void startTransition(final int layoutId) throws Throwable {
+        startTransition(loadScene(layoutId));
+    }
+
+    private void startTransition(final Scene scene) throws Throwable {
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.go(scene, mTransition);
+            }
+        });
+        waitForStart();
+    }
+
+    void enterScene(final int layoutId) throws Throwable {
+        enterScene(loadScene(layoutId));
+    }
+
+    void enterScene(final Scene scene) throws Throwable {
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                scene.enter();
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    void resetListener() {
+        mTransition.removeListener(mListener);
+        mListener = mock(Transition.TransitionListener.class);
+        mTransition.addListener(mListener);
+    }
+
+    void setAnimatedValue(float animatedValue) {
+        mAnimatedValue = animatedValue;
+    }
+
+    public class TestTransition extends Visibility {
+
+        @Override
+        public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+                TransitionValues endValues) {
+            mTransitionTargets.add(endValues.view);
+            return ObjectAnimator.ofFloat(BaseTransitionTest.this, "animatedValue", 0, 1);
+        }
+
+        @Override
+        public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+                TransitionValues endValues) {
+            mTransitionTargets.add(startValues.view);
+            return ObjectAnimator.ofFloat(BaseTransitionTest.this, "animatedValue", 1, 0);
+        }
+
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/ChangeBoundsTest.java b/transition/tests/src/android/support/transition/ChangeBoundsTest.java
new file mode 100644
index 0000000..186017c
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ChangeBoundsTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+@MediumTest
+public class ChangeBoundsTest extends BaseTransitionTest {
+
+    @Override
+    Transition createTransition() {
+        final ChangeBounds changeBounds = new ChangeBounds();
+        changeBounds.setDuration(400);
+        changeBounds.setInterpolator(new LinearInterpolator());
+        return changeBounds;
+    }
+
+    @Test
+    public void testResizeClip() {
+        ChangeBounds changeBounds = (ChangeBounds) mTransition;
+        assertThat(changeBounds.getResizeClip(), is(false));
+        changeBounds.setResizeClip(true);
+        assertThat(changeBounds.getResizeClip(), is(true));
+    }
+
+    @Test
+    public void testBasic() throws Throwable {
+        enterScene(R.layout.scene1);
+        final ViewHolder startHolder = new ViewHolder(rule.getActivity());
+        assertThat(startHolder.red, is(atTop()));
+        assertThat(startHolder.green, is(below(startHolder.red)));
+        startTransition(R.layout.scene6);
+        waitForEnd();
+        final ViewHolder endHolder = new ViewHolder(rule.getActivity());
+        assertThat(endHolder.green, is(atTop()));
+        assertThat(endHolder.red, is(below(endHolder.green)));
+    }
+
+    private static TypeSafeMatcher<View> atTop() {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            protected boolean matchesSafely(View view) {
+                return view.getTop() == 0;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is placed at the top of its parent");
+            }
+        };
+    }
+
+    private static TypeSafeMatcher<View> below(final View other) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            protected boolean matchesSafely(View item) {
+                return other.getBottom() == item.getTop();
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is placed below the specified view");
+            }
+        };
+    }
+
+    private static class ViewHolder {
+
+        public final View red;
+        public final View green;
+
+        ViewHolder(TransitionActivity activity) {
+            red = activity.findViewById(R.id.redSquare);
+            green = activity.findViewById(R.id.greenSquare);
+        }
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/ChangeClipBoundsTest.java b/transition/tests/src/android/support/transition/ChangeClipBoundsTest.java
new file mode 100644
index 0000000..c227bca
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ChangeClipBoundsTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.transition.test.R;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class ChangeClipBoundsTest extends BaseTransitionTest {
+
+    @Override
+    Transition createTransition() {
+        return new ChangeClipBounds();
+    }
+
+    @SdkSuppress(minSdkVersion = 18)
+    @Test
+    public void testChangeClipBounds() throws Throwable {
+        enterScene(R.layout.scene1);
+
+        final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+        final Rect newClip = new Rect(redSquare.getLeft() + 10, redSquare.getTop() + 10,
+                redSquare.getRight() - 10, redSquare.getBottom() - 10);
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertNull(ViewCompat.getClipBounds(redSquare));
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                ViewCompat.setClipBounds(redSquare, newClip);
+            }
+        });
+        waitForStart();
+        Thread.sleep(150);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Rect midClip = ViewCompat.getClipBounds(redSquare);
+                assertNotNull(midClip);
+                assertTrue(midClip.left > 0 && midClip.left < newClip.left);
+                assertTrue(midClip.top > 0 && midClip.top < newClip.top);
+                assertTrue(midClip.right < redSquare.getRight() && midClip.right > newClip.right);
+                assertTrue(midClip.bottom < redSquare.getBottom()
+                        && midClip.bottom > newClip.bottom);
+            }
+        });
+        waitForEnd();
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final Rect endRect = ViewCompat.getClipBounds(redSquare);
+                assertNotNull(endRect);
+                assertEquals(newClip, endRect);
+            }
+        });
+
+        resetListener();
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                ViewCompat.setClipBounds(redSquare, null);
+            }
+        });
+        waitForStart();
+        Thread.sleep(150);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Rect midClip = ViewCompat.getClipBounds(redSquare);
+                assertNotNull(midClip);
+                assertTrue(midClip.left > 0 && midClip.left < newClip.left);
+                assertTrue(midClip.top > 0 && midClip.top < newClip.top);
+                assertTrue(midClip.right < redSquare.getRight() && midClip.right > newClip.right);
+                assertTrue(midClip.bottom < redSquare.getBottom()
+                        && midClip.bottom > newClip.bottom);
+            }
+        });
+        waitForEnd();
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertNull(ViewCompat.getClipBounds(redSquare));
+            }
+        });
+
+    }
+
+    @Test
+    public void dummy() {
+        // Avoid "No tests found" on older devices
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/ChangeImageTransformTest.java b/transition/tests/src/android/support/transition/ChangeImageTransformTest.java
new file mode 100644
index 0000000..907e01e
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ChangeImageTransformTest.java
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Matrix;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.support.v4.app.ActivityCompat;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import org.junit.Test;
+
+@MediumTest
+public class ChangeImageTransformTest extends BaseTransitionTest {
+
+    private ChangeImageTransform mChangeImageTransform;
+    private Matrix mStartMatrix;
+    private Matrix mEndMatrix;
+    private Drawable mImage;
+    private ImageView mImageView;
+
+    @Override
+    Transition createTransition() {
+        mChangeImageTransform = new CaptureMatrix();
+        mChangeImageTransform.setDuration(100);
+        mTransition = mChangeImageTransform;
+        resetListener();
+        return mChangeImageTransform;
+    }
+
+    @Test
+    public void testCenterToFitXY() throws Throwable {
+        transformImage(ImageView.ScaleType.CENTER, ImageView.ScaleType.FIT_XY);
+        verifyMatrixMatches(centerMatrix(), mStartMatrix);
+        verifyMatrixMatches(fitXYMatrix(), mEndMatrix);
+    }
+
+    @Test
+    public void testCenterCropToFitCenter() throws Throwable {
+        transformImage(ImageView.ScaleType.CENTER_CROP, ImageView.ScaleType.FIT_CENTER);
+        verifyMatrixMatches(centerCropMatrix(), mStartMatrix);
+        verifyMatrixMatches(fitCenterMatrix(), mEndMatrix);
+    }
+
+    @Test
+    public void testCenterInsideToFitEnd() throws Throwable {
+        transformImage(ImageView.ScaleType.CENTER_INSIDE, ImageView.ScaleType.FIT_END);
+        // CENTER_INSIDE and CENTER are the same when the image is smaller than the View
+        verifyMatrixMatches(centerMatrix(), mStartMatrix);
+        verifyMatrixMatches(fitEndMatrix(), mEndMatrix);
+    }
+
+    @Test
+    public void testFitStartToCenter() throws Throwable {
+        transformImage(ImageView.ScaleType.FIT_START, ImageView.ScaleType.CENTER);
+        verifyMatrixMatches(fitStartMatrix(), mStartMatrix);
+        verifyMatrixMatches(centerMatrix(), mEndMatrix);
+    }
+
+    private Matrix centerMatrix() {
+        int imageWidth = mImage.getIntrinsicWidth();
+        int imageViewWidth = mImageView.getWidth();
+        float tx = Math.round((imageViewWidth - imageWidth) / 2f);
+
+        int imageHeight = mImage.getIntrinsicHeight();
+        int imageViewHeight = mImageView.getHeight();
+        float ty = Math.round((imageViewHeight - imageHeight) / 2f);
+
+        Matrix matrix = new Matrix();
+        matrix.postTranslate(tx, ty);
+        return matrix;
+    }
+
+    private Matrix fitXYMatrix() {
+        int imageWidth = mImage.getIntrinsicWidth();
+        int imageViewWidth = mImageView.getWidth();
+        float scaleX = ((float) imageViewWidth) / imageWidth;
+
+        int imageHeight = mImage.getIntrinsicHeight();
+        int imageViewHeight = mImageView.getHeight();
+        float scaleY = ((float) imageViewHeight) / imageHeight;
+
+        Matrix matrix = new Matrix();
+        matrix.postScale(scaleX, scaleY);
+        return matrix;
+    }
+
+    private Matrix centerCropMatrix() {
+        int imageWidth = mImage.getIntrinsicWidth();
+        int imageViewWidth = mImageView.getWidth();
+        float scaleX = ((float) imageViewWidth) / imageWidth;
+
+        int imageHeight = mImage.getIntrinsicHeight();
+        int imageViewHeight = mImageView.getHeight();
+        float scaleY = ((float) imageViewHeight) / imageHeight;
+
+        float maxScale = Math.max(scaleX, scaleY);
+
+        float width = imageWidth * maxScale;
+        float height = imageHeight * maxScale;
+        int tx = Math.round((imageViewWidth - width) / 2f);
+        int ty = Math.round((imageViewHeight - height) / 2f);
+
+        Matrix matrix = new Matrix();
+        matrix.postScale(maxScale, maxScale);
+        matrix.postTranslate(tx, ty);
+        return matrix;
+    }
+
+    private Matrix fitCenterMatrix() {
+        int imageWidth = mImage.getIntrinsicWidth();
+        int imageViewWidth = mImageView.getWidth();
+        float scaleX = ((float) imageViewWidth) / imageWidth;
+
+        int imageHeight = mImage.getIntrinsicHeight();
+        int imageViewHeight = mImageView.getHeight();
+        float scaleY = ((float) imageViewHeight) / imageHeight;
+
+        float minScale = Math.min(scaleX, scaleY);
+
+        float width = imageWidth * minScale;
+        float height = imageHeight * minScale;
+        float tx = (imageViewWidth - width) / 2f;
+        float ty = (imageViewHeight - height) / 2f;
+
+        Matrix matrix = new Matrix();
+        matrix.postScale(minScale, minScale);
+        matrix.postTranslate(tx, ty);
+        return matrix;
+    }
+
+    private Matrix fitStartMatrix() {
+        int imageWidth = mImage.getIntrinsicWidth();
+        int imageViewWidth = mImageView.getWidth();
+        float scaleX = ((float) imageViewWidth) / imageWidth;
+
+        int imageHeight = mImage.getIntrinsicHeight();
+        int imageViewHeight = mImageView.getHeight();
+        float scaleY = ((float) imageViewHeight) / imageHeight;
+
+        float minScale = Math.min(scaleX, scaleY);
+
+        Matrix matrix = new Matrix();
+        matrix.postScale(minScale, minScale);
+        return matrix;
+    }
+
+    private Matrix fitEndMatrix() {
+        int imageWidth = mImage.getIntrinsicWidth();
+        int imageViewWidth = mImageView.getWidth();
+        float scaleX = ((float) imageViewWidth) / imageWidth;
+
+        int imageHeight = mImage.getIntrinsicHeight();
+        int imageViewHeight = mImageView.getHeight();
+        float scaleY = ((float) imageViewHeight) / imageHeight;
+
+        float minScale = Math.min(scaleX, scaleY);
+
+        float width = imageWidth * minScale;
+        float height = imageHeight * minScale;
+        float tx = imageViewWidth - width;
+        float ty = imageViewHeight - height;
+
+        Matrix matrix = new Matrix();
+        matrix.postScale(minScale, minScale);
+        matrix.postTranslate(tx, ty);
+        return matrix;
+    }
+
+    private void verifyMatrixMatches(Matrix expected, Matrix matrix) {
+        if (expected == null) {
+            assertNull(matrix);
+            return;
+        }
+        assertNotNull(matrix);
+        float[] expectedValues = new float[9];
+        expected.getValues(expectedValues);
+
+        float[] values = new float[9];
+        matrix.getValues(values);
+
+        for (int i = 0; i < values.length; i++) {
+            final float expectedValue = expectedValues[i];
+            final float value = values[i];
+            assertEquals("Value [" + i + "]", expectedValue, value, 0.01f);
+        }
+    }
+
+    private void transformImage(ImageView.ScaleType startScale, final ImageView.ScaleType endScale)
+            throws Throwable {
+        final ImageView imageView = enterImageViewScene(startScale);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mChangeImageTransform);
+                imageView.setScaleType(endScale);
+            }
+        });
+        waitForStart();
+        verify(mListener, (startScale == endScale) ? times(1) : never())
+                .onTransitionEnd(any(Transition.class));
+        waitForEnd();
+    }
+
+    private ImageView enterImageViewScene(final ImageView.ScaleType scaleType) throws Throwable {
+        enterScene(R.layout.scene4);
+        final ViewGroup container = (ViewGroup) rule.getActivity().findViewById(R.id.holder);
+        final ImageView[] imageViews = new ImageView[1];
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mImageView = new ImageView(rule.getActivity());
+                mImage = ActivityCompat.getDrawable(rule.getActivity(),
+                        android.R.drawable.ic_media_play);
+                mImageView.setImageDrawable(mImage);
+                mImageView.setScaleType(scaleType);
+                imageViews[0] = mImageView;
+                container.addView(mImageView);
+                ViewGroup.LayoutParams layoutParams = mImageView.getLayoutParams();
+                DisplayMetrics metrics = rule.getActivity().getResources().getDisplayMetrics();
+                float size = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, metrics);
+                layoutParams.width = Math.round(size);
+                layoutParams.height = Math.round(size * 2);
+                mImageView.setLayoutParams(layoutParams);
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        return imageViews[0];
+    }
+
+    private class CaptureMatrix extends ChangeImageTransform {
+
+        @Override
+        public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues,
+                TransitionValues endValues) {
+            Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+            assertNotNull(animator);
+            animator.addListener(new CaptureMatrixListener((ImageView) endValues.view));
+            return animator;
+        }
+
+    }
+
+    private class CaptureMatrixListener extends AnimatorListenerAdapter {
+
+        private final ImageView mImageView;
+
+        CaptureMatrixListener(ImageView view) {
+            mImageView = view;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mStartMatrix = copyMatrix();
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mEndMatrix = copyMatrix();
+        }
+
+        private Matrix copyMatrix() {
+            Matrix matrix = mImageView.getImageMatrix();
+            if (matrix != null) {
+                matrix = new Matrix(matrix);
+            }
+            return matrix;
+        }
+
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/ChangeScrollTest.java b/transition/tests/src/android/support/transition/ChangeScrollTest.java
new file mode 100644
index 0000000..0f383d3
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ChangeScrollTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class ChangeScrollTest extends BaseTransitionTest {
+
+    @Override
+    Transition createTransition() {
+        return new ChangeScroll();
+    }
+
+    @Test
+    public void testChangeScroll() throws Throwable {
+        enterScene(R.layout.scene5);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final View view = rule.getActivity().findViewById(R.id.text);
+                assertEquals(0, view.getScrollX());
+                assertEquals(0, view.getScrollY());
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                view.scrollTo(150, 300);
+            }
+        });
+        waitForStart();
+        Thread.sleep(150);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final View view = rule.getActivity().findViewById(R.id.text);
+                final int scrollX = view.getScrollX();
+                final int scrollY = view.getScrollY();
+                assertThat(scrollX, is(both(greaterThan(0)).and(lessThan(150))));
+                assertThat(scrollY, is(both(greaterThan(0)).and(lessThan(300))));
+            }
+        });
+        waitForEnd();
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final View view = rule.getActivity().findViewById(R.id.text);
+                assertEquals(150, view.getScrollX());
+                assertEquals(300, view.getScrollY());
+            }
+        });
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/ChangeTransformTest.java b/transition/tests/src/android/support/transition/ChangeTransformTest.java
new file mode 100644
index 0000000..3e543aa
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ChangeTransformTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class ChangeTransformTest extends BaseTransitionTest {
+
+    @Override
+    Transition createTransition() {
+        return new ChangeTransform();
+    }
+
+    @Test
+    public void testTranslation() throws Throwable {
+        enterScene(R.layout.scene1);
+
+        final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                redSquare.setTranslationX(500);
+                redSquare.setTranslationY(600);
+            }
+        });
+        waitForStart();
+
+        verify(mListener, never()).onTransitionEnd(any(Transition.class)); // still running
+        // There is no way to validate the intermediate matrix because it uses
+        // hidden properties of the View to execute.
+        waitForEnd();
+        assertEquals(500f, redSquare.getTranslationX(), 0.0f);
+        assertEquals(600f, redSquare.getTranslationY(), 0.0f);
+    }
+
+    @Test
+    public void testRotation() throws Throwable {
+        enterScene(R.layout.scene1);
+
+        final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                redSquare.setRotation(45);
+            }
+        });
+        waitForStart();
+
+        verify(mListener, never()).onTransitionEnd(any(Transition.class)); // still running
+        // There is no way to validate the intermediate matrix because it uses
+        // hidden properties of the View to execute.
+        waitForEnd();
+        assertEquals(45f, redSquare.getRotation(), 0.0f);
+    }
+
+    @Test
+    public void testScale() throws Throwable {
+        enterScene(R.layout.scene1);
+
+        final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                redSquare.setScaleX(2f);
+                redSquare.setScaleY(3f);
+            }
+        });
+        waitForStart();
+
+        verify(mListener, never()).onTransitionEnd(any(Transition.class)); // still running
+        // There is no way to validate the intermediate matrix because it uses
+        // hidden properties of the View to execute.
+        waitForEnd();
+        assertEquals(2f, redSquare.getScaleX(), 0.0f);
+        assertEquals(3f, redSquare.getScaleY(), 0.0f);
+    }
+
+    @Test
+    public void testReparent() throws Throwable {
+        final ChangeTransform changeTransform = (ChangeTransform) mTransition;
+        assertEquals(true, changeTransform.getReparent());
+        enterScene(R.layout.scene5);
+        startTransition(R.layout.scene9);
+        verify(mListener, never()).onTransitionEnd(any(Transition.class)); // still running
+        waitForEnd();
+
+        resetListener();
+        changeTransform.setReparent(false);
+        assertEquals(false, changeTransform.getReparent());
+        startTransition(R.layout.scene5);
+        waitForEnd(); // no transition to run because reparent == false
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/ExplodeTest.java b/transition/tests/src/android/support/transition/ExplodeTest.java
new file mode 100644
index 0000000..b421537
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ExplodeTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class ExplodeTest extends BaseTransitionTest {
+
+    @Override
+    Transition createTransition() {
+        return new Explode();
+    }
+
+    @Test
+    public void testExplode() throws Throwable {
+        enterScene(R.layout.scene10);
+        final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+        final View greenSquare = rule.getActivity().findViewById(R.id.greenSquare);
+        final View blueSquare = rule.getActivity().findViewById(R.id.blueSquare);
+        final View yellowSquare = rule.getActivity().findViewById(R.id.yellowSquare);
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                redSquare.setVisibility(View.INVISIBLE);
+                greenSquare.setVisibility(View.INVISIBLE);
+                blueSquare.setVisibility(View.INVISIBLE);
+                yellowSquare.setVisibility(View.INVISIBLE);
+            }
+        });
+        waitForStart();
+        verify(mListener, never()).onTransitionEnd(any(Transition.class));
+        assertEquals(View.VISIBLE, redSquare.getVisibility());
+        assertEquals(View.VISIBLE, greenSquare.getVisibility());
+        assertEquals(View.VISIBLE, blueSquare.getVisibility());
+        assertEquals(View.VISIBLE, yellowSquare.getVisibility());
+        float redStartX = redSquare.getTranslationX();
+        float redStartY = redSquare.getTranslationY();
+
+        SystemClock.sleep(100);
+        verifyTranslation(redSquare, true, true);
+        verifyTranslation(greenSquare, false, true);
+        verifyTranslation(blueSquare, false, false);
+        verifyTranslation(yellowSquare, true, false);
+        assertTrue(redStartX > redSquare.getTranslationX()); // moving left
+        assertTrue(redStartY > redSquare.getTranslationY()); // moving up
+        waitForEnd();
+
+        verifyNoTranslation(redSquare);
+        verifyNoTranslation(greenSquare);
+        verifyNoTranslation(blueSquare);
+        verifyNoTranslation(yellowSquare);
+        assertEquals(View.INVISIBLE, redSquare.getVisibility());
+        assertEquals(View.INVISIBLE, greenSquare.getVisibility());
+        assertEquals(View.INVISIBLE, blueSquare.getVisibility());
+        assertEquals(View.INVISIBLE, yellowSquare.getVisibility());
+    }
+
+    @Test
+    public void testImplode() throws Throwable {
+        enterScene(R.layout.scene10);
+        final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+        final View greenSquare = rule.getActivity().findViewById(R.id.greenSquare);
+        final View blueSquare = rule.getActivity().findViewById(R.id.blueSquare);
+        final View yellowSquare = rule.getActivity().findViewById(R.id.yellowSquare);
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                redSquare.setVisibility(View.INVISIBLE);
+                greenSquare.setVisibility(View.INVISIBLE);
+                blueSquare.setVisibility(View.INVISIBLE);
+                yellowSquare.setVisibility(View.INVISIBLE);
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(mRoot, mTransition);
+                redSquare.setVisibility(View.VISIBLE);
+                greenSquare.setVisibility(View.VISIBLE);
+                blueSquare.setVisibility(View.VISIBLE);
+                yellowSquare.setVisibility(View.VISIBLE);
+            }
+        });
+        waitForStart();
+
+        assertEquals(View.VISIBLE, redSquare.getVisibility());
+        assertEquals(View.VISIBLE, greenSquare.getVisibility());
+        assertEquals(View.VISIBLE, blueSquare.getVisibility());
+        assertEquals(View.VISIBLE, yellowSquare.getVisibility());
+        float redStartX = redSquare.getTranslationX();
+        float redStartY = redSquare.getTranslationY();
+
+        SystemClock.sleep(100);
+        verifyTranslation(redSquare, true, true);
+        verifyTranslation(greenSquare, false, true);
+        verifyTranslation(blueSquare, false, false);
+        verifyTranslation(yellowSquare, true, false);
+        assertTrue(redStartX < redSquare.getTranslationX()); // moving right
+        assertTrue(redStartY < redSquare.getTranslationY()); // moving down
+        waitForEnd();
+
+        verifyNoTranslation(redSquare);
+        verifyNoTranslation(greenSquare);
+        verifyNoTranslation(blueSquare);
+        verifyNoTranslation(yellowSquare);
+        assertEquals(View.VISIBLE, redSquare.getVisibility());
+        assertEquals(View.VISIBLE, greenSquare.getVisibility());
+        assertEquals(View.VISIBLE, blueSquare.getVisibility());
+        assertEquals(View.VISIBLE, yellowSquare.getVisibility());
+    }
+
+    private void verifyTranslation(View view, boolean goLeft, boolean goUp) {
+        float translationX = view.getTranslationX();
+        float translationY = view.getTranslationY();
+
+        if (goLeft) {
+            assertTrue(translationX < 0);
+        } else {
+            assertTrue(translationX > 0);
+        }
+
+        if (goUp) {
+            assertTrue(translationY < 0);
+        } else {
+            assertTrue(translationY > 0);
+        }
+    }
+
+    private void verifyNoTranslation(View view) {
+        assertEquals(0f, view.getTranslationX(), 0.0f);
+        assertEquals(0f, view.getTranslationY(), 0.0f);
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/FadeTest.java b/transition/tests/src/android/support/transition/FadeTest.java
index ba6440c..3b171e2 100644
--- a/transition/tests/src/android/support/transition/FadeTest.java
+++ b/transition/tests/src/android/support/transition/FadeTest.java
@@ -16,12 +16,26 @@
 
 package android.support.transition;
 
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
 
 import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.view.View;
@@ -45,6 +59,16 @@
     }
 
     @Test
+    public void testMode() {
+        assertThat(Fade.IN, is(Visibility.MODE_IN));
+        assertThat(Fade.OUT, is(Visibility.MODE_OUT));
+        final Fade fade = new Fade();
+        assertThat(fade.getMode(), is(Visibility.MODE_IN | Visibility.MODE_OUT));
+        fade.setMode(Visibility.MODE_IN);
+        assertThat(fade.getMode(), is(Visibility.MODE_IN));
+    }
+
+    @Test
     @UiThreadTest
     public void testDisappear() {
         final Fade fade = new Fade();
@@ -90,4 +114,162 @@
         assertThat(animator, is(nullValue()));
     }
 
+    @Test
+    public void testFadeOutThenIn() throws Throwable {
+        // Fade out
+        final Runnable interrupt = mock(Runnable.class);
+        float[] valuesOut = new float[2];
+        final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, interrupt,
+                valuesOut);
+        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
+        fadeOut.addListener(listenerOut);
+        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
+        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
+
+        // The view is in the middle of fading out
+        verify(interrupt, timeout(3000)).run();
+
+        // Fade in
+        float[] valuesIn = new float[2];
+        final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, null, valuesIn);
+        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
+        fadeIn.addListener(listenerIn);
+        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
+        verify(listenerOut, timeout(3000)).onTransitionPause(any(Transition.class));
+        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
+        assertThat(valuesOut[1], allOf(greaterThan(0f), lessThan(1f)));
+        if (Build.VERSION.SDK_INT >= 19) {
+            // These won't match on API levels 18 and below due to lack of Animator pause.
+            assertEquals(valuesOut[1], valuesIn[0], 0.01f);
+        }
+
+        verify(listenerIn, timeout(3000)).onTransitionEnd(any(Transition.class));
+        assertThat(mView.getVisibility(), is(View.VISIBLE));
+        assertEquals(valuesIn[1], 1.f, 0.01f);
+    }
+
+    @Test
+    public void testFadeInThenOut() throws Throwable {
+        changeVisibility(null, mRoot, mView, View.INVISIBLE);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        // Fade in
+        final Runnable interrupt = mock(Runnable.class);
+        float[] valuesIn = new float[2];
+        final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, interrupt, valuesIn);
+        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
+        fadeIn.addListener(listenerIn);
+        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
+        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
+
+        // The view is in the middle of fading in
+        verify(interrupt, timeout(3000)).run();
+
+        // Fade out
+        float[] valuesOut = new float[2];
+        final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, null, valuesOut);
+        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
+        fadeOut.addListener(listenerOut);
+        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
+        verify(listenerIn, timeout(3000)).onTransitionPause(any(Transition.class));
+        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
+        assertThat(valuesIn[1], allOf(greaterThan(0f), lessThan(1f)));
+        if (Build.VERSION.SDK_INT >= 19) {
+            // These won't match on API levels 18 and below due to lack of Animator pause.
+            assertEquals(valuesIn[1], valuesOut[0], 0.01f);
+        }
+
+        verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
+        assertThat(mView.getVisibility(), is(View.INVISIBLE));
+    }
+
+    @Test
+    public void testFadeWithAlpha() throws Throwable {
+        // Set the view alpha to 0.5
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mView.setAlpha(0.5f);
+            }
+        });
+        // Fade out
+        final Fade fadeOut = new Fade(Fade.OUT);
+        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
+        fadeOut.addListener(listenerOut);
+        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
+        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
+        verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
+        // Fade in
+        final Fade fadeIn = new Fade(Fade.IN);
+        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
+        fadeIn.addListener(listenerIn);
+        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
+        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
+        verify(listenerIn, timeout(3000)).onTransitionEnd(any(Transition.class));
+        // Confirm that the view still has the original alpha value
+        assertThat(mView.getVisibility(), is(View.VISIBLE));
+        assertEquals(0.5f, mView.getAlpha(), 0.01f);
+    }
+
+    private void changeVisibility(final Fade fade, final ViewGroup container, final View target,
+            final int visibility) throws Throwable {
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (fade != null) {
+                    TransitionManager.beginDelayedTransition(container, fade);
+                }
+                target.setVisibility(visibility);
+            }
+        });
+    }
+
+    /**
+     * A special version of {@link Fade} that runs a specified {@link Runnable} soon after the
+     * target starts fading in or out.
+     */
+    private static class InterruptibleFade extends Fade {
+
+        static final float ALPHA_THRESHOLD = 0.2f;
+
+        float mInitialAlpha = -1;
+        Runnable mMiddle;
+        final float[] mAlphaValues;
+
+        InterruptibleFade(int mode, Runnable middle, float[] alphaValues) {
+            super(mode);
+            mMiddle = middle;
+            mAlphaValues = alphaValues;
+        }
+
+        @Nullable
+        @Override
+        public Animator createAnimator(@NonNull ViewGroup sceneRoot,
+                @Nullable final TransitionValues startValues,
+                @Nullable final TransitionValues endValues) {
+            final Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+            if (animator instanceof ObjectAnimator) {
+                ((ObjectAnimator) animator).addUpdateListener(
+                        new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                final float alpha = (float) animation.getAnimatedValue();
+                                mAlphaValues[1] = alpha;
+                                if (mInitialAlpha < 0) {
+                                    mInitialAlpha = alpha;
+                                    mAlphaValues[0] = mInitialAlpha;
+                                } else if (Math.abs(alpha - mInitialAlpha) > ALPHA_THRESHOLD) {
+                                    if (mMiddle != null) {
+                                        mMiddle.run();
+                                        mMiddle = null;
+                                    }
+                                }
+                            }
+                        });
+            }
+            return animator;
+        }
+
+    }
+
 }
diff --git a/transition/tests/src/android/support/transition/PathMotionTest.java b/transition/tests/src/android/support/transition/PathMotionTest.java
new file mode 100644
index 0000000..8bf738e
--- /dev/null
+++ b/transition/tests/src/android/support/transition/PathMotionTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+
+public abstract class PathMotionTest {
+
+    public static void assertPathMatches(Path expectedPath, Path path) {
+        PathMeasure expectedMeasure = new PathMeasure(expectedPath, false);
+        PathMeasure pathMeasure = new PathMeasure(path, false);
+
+        boolean expectedNextContour;
+        boolean pathNextContour;
+        int contourIndex = 0;
+        do {
+            float expectedLength = expectedMeasure.getLength();
+            assertEquals("Lengths differ", expectedLength, pathMeasure.getLength(), 0.01f);
+
+            float minLength = Math.min(expectedLength, pathMeasure.getLength());
+
+            float[] pos = new float[2];
+
+            float increment = minLength / 5f;
+            for (float along = 0; along <= minLength; along += increment) {
+                expectedMeasure.getPosTan(along, pos, null);
+                float expectedX = pos[0];
+                float expectedY = pos[1];
+
+                pathMeasure.getPosTan(along, pos, null);
+                assertEquals("Failed at " + increment + " in contour " + contourIndex,
+                        expectedX, pos[0], 0.01f);
+                assertEquals("Failed at " + increment + " in contour " + contourIndex,
+                        expectedY, pos[1], 0.01f);
+            }
+            expectedNextContour = expectedMeasure.nextContour();
+            pathNextContour = pathMeasure.nextContour();
+            contourIndex++;
+        } while (expectedNextContour && pathNextContour);
+        assertFalse(expectedNextContour);
+        assertFalse(pathNextContour);
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/PatternPathMotionTest.java b/transition/tests/src/android/support/transition/PatternPathMotionTest.java
new file mode 100644
index 0000000..b14ceaa
--- /dev/null
+++ b/transition/tests/src/android/support/transition/PatternPathMotionTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertSame;
+
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PatternPathMotionTest extends PathMotionTest {
+
+    @Test
+    public void testStraightPath() {
+        Path pattern = new Path();
+        pattern.moveTo(100, 500);
+        pattern.lineTo(300, 1000);
+
+        PatternPathMotion pathMotion = new PatternPathMotion(pattern);
+        assertPathMatches(pattern, pathMotion.getPatternPath());
+
+        Path expected = new Path();
+        expected.moveTo(0, 0);
+        expected.lineTo(100, 100);
+
+        assertPathMatches(expected, pathMotion.getPath(0, 0, 100, 100));
+    }
+
+    @Test
+    public void testCurve() {
+        RectF oval = new RectF();
+        Path pattern = new Path();
+        oval.set(0, 0, 100, 100);
+        pattern.addArc(oval, 0, 180);
+
+        PatternPathMotion pathMotion = new PatternPathMotion(pattern);
+        assertPathMatches(pattern, pathMotion.getPatternPath());
+
+        Path expected = new Path();
+        oval.set(-50, 0, 50, 100);
+        expected.addArc(oval, -90, 180);
+
+        assertPathMatches(expected, pathMotion.getPath(0, 0, 0, 100));
+    }
+
+    @Test
+    public void testSetPatternPath() {
+        Path pattern = new Path();
+        RectF oval = new RectF(0, 0, 100, 100);
+        pattern.addArc(oval, 0, 180);
+
+        PatternPathMotion patternPathMotion = new PatternPathMotion();
+        patternPathMotion.setPatternPath(pattern);
+        assertSame(pattern, patternPathMotion.getPatternPath());
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/PropagationTest.java b/transition/tests/src/android/support/transition/PropagationTest.java
new file mode 100644
index 0000000..932c8d3
--- /dev/null
+++ b/transition/tests/src/android/support/transition/PropagationTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class PropagationTest extends BaseTransitionTest {
+
+    @Test
+    public void testCircularPropagation() throws Throwable {
+        enterScene(R.layout.scene10);
+        CircularPropagation propagation = new CircularPropagation();
+        mTransition.setPropagation(propagation);
+        final TransitionValues redValues = new TransitionValues();
+        redValues.view = mRoot.findViewById(R.id.redSquare);
+        propagation.captureValues(redValues);
+
+        // Only the reported propagation properties are set
+        for (String prop : propagation.getPropagationProperties()) {
+            assertTrue(redValues.values.keySet().contains(prop));
+        }
+        assertEquals(propagation.getPropagationProperties().length, redValues.values.size());
+
+        // check the visibility
+        assertEquals(View.VISIBLE, propagation.getViewVisibility(redValues));
+        assertEquals(View.GONE, propagation.getViewVisibility(null));
+
+        // Check the positions
+        int[] pos = new int[2];
+        redValues.view.getLocationOnScreen(pos);
+        pos[0] += redValues.view.getWidth() / 2;
+        pos[1] += redValues.view.getHeight() / 2;
+        assertEquals(pos[0], propagation.getViewX(redValues));
+        assertEquals(pos[1], propagation.getViewY(redValues));
+
+        mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(@NonNull Transition transition) {
+                return new Rect(0, 0, redValues.view.getWidth(), redValues.view.getHeight());
+            }
+        });
+
+        long redDelay = getDelay(R.id.redSquare);
+        // red square's delay should be roughly 0 since it is at the epicenter
+        assertEquals(0f, redDelay, 30f);
+
+        // The green square is on the upper-right
+        long greenDelay = getDelay(R.id.greenSquare);
+        assertTrue(greenDelay < redDelay);
+
+        // The blue square is on the lower-right
+        long blueDelay = getDelay(R.id.blueSquare);
+        assertTrue(blueDelay < greenDelay);
+
+        // Test propagation speed
+        propagation.setPropagationSpeed(1000000000f);
+        assertEquals(0, getDelay(R.id.blueSquare));
+    }
+
+    private TransitionValues capturePropagationValues(int viewId) {
+        TransitionValues transitionValues = new TransitionValues();
+        transitionValues.view = mRoot.findViewById(viewId);
+        TransitionPropagation propagation = mTransition.getPropagation();
+        assertNotNull(propagation);
+        propagation.captureValues(transitionValues);
+        return transitionValues;
+    }
+
+    private long getDelay(int viewId) {
+        TransitionValues transitionValues = capturePropagationValues(viewId);
+        TransitionPropagation propagation = mTransition.getPropagation();
+        assertNotNull(propagation);
+        return propagation.getStartDelay(mRoot, mTransition, transitionValues, null);
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/SceneTest.java b/transition/tests/src/android/support/transition/SceneTest.java
index f192327..129a3eb 100644
--- a/transition/tests/src/android/support/transition/SceneTest.java
+++ b/transition/tests/src/android/support/transition/SceneTest.java
@@ -118,9 +118,10 @@
     public void testGetSceneForLayout_cache() {
         TransitionActivity activity = rule.getActivity();
         ViewGroup root = activity.getRoot();
-        Scene scene = Scene.getSceneForLayout(root, R.layout.scene0, activity);
+        Scene scene = Scene.getSceneForLayout(root, R.layout.support_scene0, activity);
         assertThat("getSceneForLayout should return the same instance for subsequent calls",
-                Scene.getSceneForLayout(root, R.layout.scene0, activity), is(sameInstance(scene)));
+                Scene.getSceneForLayout(root, R.layout.support_scene0, activity),
+                is(sameInstance(scene)));
     }
 
 }
diff --git a/transition/tests/src/android/support/transition/SlideBadEdgeTest.java b/transition/tests/src/android/support/transition/SlideBadEdgeTest.java
new file mode 100644
index 0000000..e43d4f3
--- /dev/null
+++ b/transition/tests/src/android/support/transition/SlideBadEdgeTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Gravity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SlideBadEdgeTest {
+
+    private static final Object[][] sBadGravity = {
+            {Gravity.AXIS_CLIP, "AXIS_CLIP"},
+            {Gravity.AXIS_PULL_AFTER, "AXIS_PULL_AFTER"},
+            {Gravity.AXIS_PULL_BEFORE, "AXIS_PULL_BEFORE"},
+            {Gravity.AXIS_SPECIFIED, "AXIS_SPECIFIED"},
+            {Gravity.AXIS_Y_SHIFT, "AXIS_Y_SHIFT"},
+            {Gravity.AXIS_X_SHIFT, "AXIS_X_SHIFT"},
+            {Gravity.CENTER, "CENTER"},
+            {Gravity.CLIP_VERTICAL, "CLIP_VERTICAL"},
+            {Gravity.CLIP_HORIZONTAL, "CLIP_HORIZONTAL"},
+            {Gravity.CENTER_VERTICAL, "CENTER_VERTICAL"},
+            {Gravity.CENTER_HORIZONTAL, "CENTER_HORIZONTAL"},
+            {Gravity.DISPLAY_CLIP_VERTICAL, "DISPLAY_CLIP_VERTICAL"},
+            {Gravity.DISPLAY_CLIP_HORIZONTAL, "DISPLAY_CLIP_HORIZONTAL"},
+            {Gravity.FILL_VERTICAL, "FILL_VERTICAL"},
+            {Gravity.FILL, "FILL"},
+            {Gravity.FILL_HORIZONTAL, "FILL_HORIZONTAL"},
+            {Gravity.HORIZONTAL_GRAVITY_MASK, "HORIZONTAL_GRAVITY_MASK"},
+            {Gravity.NO_GRAVITY, "NO_GRAVITY"},
+            {Gravity.RELATIVE_LAYOUT_DIRECTION, "RELATIVE_LAYOUT_DIRECTION"},
+            {Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK, "RELATIVE_HORIZONTAL_GRAVITY_MASK"},
+            {Gravity.VERTICAL_GRAVITY_MASK, "VERTICAL_GRAVITY_MASK"},
+    };
+
+    @Test
+    public void testBadSide() {
+        for (int i = 0; i < sBadGravity.length; i++) {
+            int badEdge = (Integer) sBadGravity[i][0];
+            String edgeName = (String) sBadGravity[i][1];
+            try {
+                new Slide(badEdge);
+                fail("Should not be able to set slide edge to " + edgeName);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+
+            try {
+                Slide slide = new Slide();
+                slide.setSlideEdge(badEdge);
+                fail("Should not be able to set slide edge to " + edgeName);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+        }
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/SlideDefaultEdgeTest.java b/transition/tests/src/android/support/transition/SlideDefaultEdgeTest.java
new file mode 100644
index 0000000..a0e7eab
--- /dev/null
+++ b/transition/tests/src/android/support/transition/SlideDefaultEdgeTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Gravity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SlideDefaultEdgeTest {
+
+    @Test
+    public void testDefaultSide() {
+        // default to bottom
+        Slide slide = new Slide();
+        assertEquals(Gravity.BOTTOM, slide.getSlideEdge());
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/SlideEdgeTest.java b/transition/tests/src/android/support/transition/SlideEdgeTest.java
new file mode 100644
index 0000000..0ddf210
--- /dev/null
+++ b/transition/tests/src/android/support/transition/SlideEdgeTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Test;
+
+@MediumTest
+public class SlideEdgeTest extends BaseTransitionTest {
+
+    private static final Object[][] sSlideEdgeArray = {
+            {Gravity.START, "START"},
+            {Gravity.END, "END"},
+            {Gravity.LEFT, "LEFT"},
+            {Gravity.TOP, "TOP"},
+            {Gravity.RIGHT, "RIGHT"},
+            {Gravity.BOTTOM, "BOTTOM"},
+    };
+
+    @Test
+    public void testSetSide() throws Throwable {
+        for (int i = 0; i < sSlideEdgeArray.length; i++) {
+            int slideEdge = (Integer) (sSlideEdgeArray[i][0]);
+            String edgeName = (String) (sSlideEdgeArray[i][1]);
+            Slide slide = new Slide(slideEdge);
+            assertEquals("Edge not set properly in constructor " + edgeName,
+                    slideEdge, slide.getSlideEdge());
+
+            slide = new Slide();
+            slide.setSlideEdge(slideEdge);
+            assertEquals("Edge not set properly with setter " + edgeName,
+                    slideEdge, slide.getSlideEdge());
+        }
+    }
+
+    @Test
+    public void testSlideOut() throws Throwable {
+        for (int i = 0; i < sSlideEdgeArray.length; i++) {
+            final int slideEdge = (Integer) (sSlideEdgeArray[i][0]);
+            final Slide slide = new Slide(slideEdge);
+            final Transition.TransitionListener listener =
+                    mock(Transition.TransitionListener.class);
+            slide.addListener(listener);
+
+            rule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    rule.getActivity().setContentView(R.layout.scene1);
+                }
+            });
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+            final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+            final View greenSquare = rule.getActivity().findViewById(R.id.greenSquare);
+            final View hello = rule.getActivity().findViewById(R.id.hello);
+            final ViewGroup sceneRoot = (ViewGroup) rule.getActivity().findViewById(R.id.holder);
+
+            rule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    TransitionManager.beginDelayedTransition(sceneRoot, slide);
+                    redSquare.setVisibility(View.INVISIBLE);
+                    greenSquare.setVisibility(View.INVISIBLE);
+                    hello.setVisibility(View.INVISIBLE);
+                }
+            });
+            verify(listener, timeout(1000)).onTransitionStart(any(Transition.class));
+            verify(listener, never()).onTransitionEnd(any(Transition.class));
+            assertEquals(View.VISIBLE, redSquare.getVisibility());
+            assertEquals(View.VISIBLE, greenSquare.getVisibility());
+            assertEquals(View.VISIBLE, hello.getVisibility());
+
+            float redStartX = redSquare.getTranslationX();
+            float redStartY = redSquare.getTranslationY();
+
+            Thread.sleep(200);
+            verifyTranslation(slideEdge, redSquare);
+            verifyTranslation(slideEdge, greenSquare);
+            verifyTranslation(slideEdge, hello);
+
+            final float redMidX = redSquare.getTranslationX();
+            final float redMidY = redSquare.getTranslationY();
+
+            switch (slideEdge) {
+                case Gravity.LEFT:
+                case Gravity.START:
+                    assertTrue(
+                            "isn't sliding out to left. Expecting " + redStartX + " > " + redMidX,
+                            redStartX > redMidX);
+                    break;
+                case Gravity.RIGHT:
+                case Gravity.END:
+                    assertTrue(
+                            "isn't sliding out to right. Expecting " + redStartX + " < " + redMidX,
+                            redStartX < redMidX);
+                    break;
+                case Gravity.TOP:
+                    assertTrue("isn't sliding out to top. Expecting " + redStartY + " > " + redMidY,
+                            redStartY > redSquare.getTranslationY());
+                    break;
+                case Gravity.BOTTOM:
+                    assertTrue(
+                            "isn't sliding out to bottom. Expecting " + redStartY + " < " + redMidY,
+                            redStartY < redSquare.getTranslationY());
+                    break;
+            }
+            verify(listener, timeout(1000)).onTransitionEnd(any(Transition.class));
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+            verifyNoTranslation(redSquare);
+            verifyNoTranslation(greenSquare);
+            verifyNoTranslation(hello);
+            assertEquals(View.INVISIBLE, redSquare.getVisibility());
+            assertEquals(View.INVISIBLE, greenSquare.getVisibility());
+            assertEquals(View.INVISIBLE, hello.getVisibility());
+        }
+    }
+
+    @Test
+    public void testSlideIn() throws Throwable {
+        for (int i = 0; i < sSlideEdgeArray.length; i++) {
+            final int slideEdge = (Integer) (sSlideEdgeArray[i][0]);
+            final Slide slide = new Slide(slideEdge);
+            final Transition.TransitionListener listener =
+                    mock(Transition.TransitionListener.class);
+            slide.addListener(listener);
+
+            rule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    rule.getActivity().setContentView(R.layout.scene1);
+                }
+            });
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+            final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+            final View greenSquare = rule.getActivity().findViewById(R.id.greenSquare);
+            final View hello = rule.getActivity().findViewById(R.id.hello);
+            final ViewGroup sceneRoot = (ViewGroup) rule.getActivity().findViewById(R.id.holder);
+
+            rule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    redSquare.setVisibility(View.INVISIBLE);
+                    greenSquare.setVisibility(View.INVISIBLE);
+                    hello.setVisibility(View.INVISIBLE);
+                }
+            });
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+            // now slide in
+            rule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    TransitionManager.beginDelayedTransition(sceneRoot, slide);
+                    redSquare.setVisibility(View.VISIBLE);
+                    greenSquare.setVisibility(View.VISIBLE);
+                    hello.setVisibility(View.VISIBLE);
+                }
+            });
+            verify(listener, timeout(1000)).onTransitionStart(any(Transition.class));
+
+            verify(listener, never()).onTransitionEnd(any(Transition.class));
+            assertEquals(View.VISIBLE, redSquare.getVisibility());
+            assertEquals(View.VISIBLE, greenSquare.getVisibility());
+            assertEquals(View.VISIBLE, hello.getVisibility());
+
+            final float redStartX = redSquare.getTranslationX();
+            final float redStartY = redSquare.getTranslationY();
+
+            Thread.sleep(200);
+            verifyTranslation(slideEdge, redSquare);
+            verifyTranslation(slideEdge, greenSquare);
+            verifyTranslation(slideEdge, hello);
+            final float redMidX = redSquare.getTranslationX();
+            final float redMidY = redSquare.getTranslationY();
+
+            switch (slideEdge) {
+                case Gravity.LEFT:
+                case Gravity.START:
+                    assertTrue(
+                            "isn't sliding in from left. Expecting " + redStartX + " < " + redMidX,
+                            redStartX < redMidX);
+                    break;
+                case Gravity.RIGHT:
+                case Gravity.END:
+                    assertTrue(
+                            "isn't sliding in from right. Expecting " + redStartX + " > " + redMidX,
+                            redStartX > redMidX);
+                    break;
+                case Gravity.TOP:
+                    assertTrue(
+                            "isn't sliding in from top. Expecting " + redStartY + " < " + redMidY,
+                            redStartY < redSquare.getTranslationY());
+                    break;
+                case Gravity.BOTTOM:
+                    assertTrue("isn't sliding in from bottom. Expecting " + redStartY + " > "
+                                    + redMidY,
+                            redStartY > redSquare.getTranslationY());
+                    break;
+            }
+            verify(listener, timeout(1000)).onTransitionEnd(any(Transition.class));
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+            verifyNoTranslation(redSquare);
+            verifyNoTranslation(greenSquare);
+            verifyNoTranslation(hello);
+            assertEquals(View.VISIBLE, redSquare.getVisibility());
+            assertEquals(View.VISIBLE, greenSquare.getVisibility());
+            assertEquals(View.VISIBLE, hello.getVisibility());
+        }
+    }
+
+    private void verifyTranslation(int slideEdge, View view) {
+        switch (slideEdge) {
+            case Gravity.LEFT:
+            case Gravity.START:
+                assertTrue(view.getTranslationX() < 0);
+                assertEquals(0f, view.getTranslationY(), 0.01f);
+                break;
+            case Gravity.RIGHT:
+            case Gravity.END:
+                assertTrue(view.getTranslationX() > 0);
+                assertEquals(0f, view.getTranslationY(), 0.01f);
+                break;
+            case Gravity.TOP:
+                assertTrue(view.getTranslationY() < 0);
+                assertEquals(0f, view.getTranslationX(), 0.01f);
+                break;
+            case Gravity.BOTTOM:
+                assertTrue(view.getTranslationY() > 0);
+                assertEquals(0f, view.getTranslationX(), 0.01f);
+                break;
+        }
+    }
+
+    private void verifyNoTranslation(View view) {
+        assertEquals(0f, view.getTranslationX(), 0.01f);
+        assertEquals(0f, view.getTranslationY(), 0.01f);
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/SyncRunnable.java b/transition/tests/src/android/support/transition/SyncRunnable.java
new file mode 100644
index 0000000..2e8a2e1
--- /dev/null
+++ b/transition/tests/src/android/support/transition/SyncRunnable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+class SyncRunnable implements Runnable {
+
+    private final CountDownLatch mLatch = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        mLatch.countDown();
+    }
+
+    boolean await() {
+        try {
+            return mLatch.await(3000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        return false;
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/SyncTransitionListener.java b/transition/tests/src/android/support/transition/SyncTransitionListener.java
new file mode 100644
index 0000000..4d7e02e
--- /dev/null
+++ b/transition/tests/src/android/support/transition/SyncTransitionListener.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+package android.support.transition;
+
+import android.support.annotation.NonNull;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This {@link Transition.TransitionListener} synchronously waits for the specified callback.
+ */
+class SyncTransitionListener implements Transition.TransitionListener {
+
+    static final int EVENT_START = 1;
+    static final int EVENT_END = 2;
+    static final int EVENT_CANCEL = 3;
+    static final int EVENT_PAUSE = 4;
+    static final int EVENT_RESUME = 5;
+
+    private final int mTargetEvent;
+    private CountDownLatch mLatch = new CountDownLatch(1);
+
+    SyncTransitionListener(int event) {
+        mTargetEvent = event;
+    }
+
+    boolean await() {
+        try {
+            return mLatch.await(3000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
+    void reset() {
+        mLatch = new CountDownLatch(1);
+    }
+
+    @Override
+    public void onTransitionStart(@NonNull Transition transition) {
+        if (mTargetEvent == EVENT_START) {
+            mLatch.countDown();
+        }
+    }
+
+    @Override
+    public void onTransitionEnd(@NonNull Transition transition) {
+        if (mTargetEvent == EVENT_END) {
+            mLatch.countDown();
+        }
+    }
+
+    @Override
+    public void onTransitionCancel(@NonNull Transition transition) {
+        if (mTargetEvent == EVENT_CANCEL) {
+            mLatch.countDown();
+        }
+    }
+
+    @Override
+    public void onTransitionPause(@NonNull Transition transition) {
+        if (mTargetEvent == EVENT_PAUSE) {
+            mLatch.countDown();
+        }
+    }
+
+    @Override
+    public void onTransitionResume(@NonNull Transition transition) {
+        if (mTargetEvent == EVENT_RESUME) {
+            mLatch.countDown();
+        }
+    }
+}
diff --git a/transition/tests/src/android/support/transition/TransitionActivity.java b/transition/tests/src/android/support/transition/TransitionActivity.java
index ff9dbcc..958435c 100644
--- a/transition/tests/src/android/support/transition/TransitionActivity.java
+++ b/transition/tests/src/android/support/transition/TransitionActivity.java
@@ -20,17 +20,17 @@
 import android.os.Bundle;
 import android.support.transition.test.R;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 public class TransitionActivity extends Activity {
 
-    private FrameLayout mRoot;
+    private LinearLayout mRoot;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_transition);
-        mRoot = (FrameLayout) findViewById(R.id.root);
+        mRoot = findViewById(R.id.root);
     }
 
     ViewGroup getRoot() {
diff --git a/transition/tests/src/android/support/transition/TransitionInflaterTest.java b/transition/tests/src/android/support/transition/TransitionInflaterTest.java
new file mode 100644
index 0000000..ec760c9
--- /dev/null
+++ b/transition/tests/src/android/support/transition/TransitionInflaterTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static junit.framework.TestCase.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.support.annotation.NonNull;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.junit.Test;
+
+import java.util.List;
+
+@MediumTest
+public class TransitionInflaterTest extends BaseTest {
+
+    @Test
+    public void testInflationConstructors() throws Throwable {
+        TransitionInflater inflater = TransitionInflater.from(rule.getActivity());
+        Transition transition = inflater.inflateTransition(R.transition.transition_constructors);
+        assertTrue(transition instanceof TransitionSet);
+        TransitionSet set = (TransitionSet) transition;
+        assertEquals(10, set.getTransitionCount());
+    }
+
+    @Test
+    public void testInflation() {
+        TransitionInflater inflater = TransitionInflater.from(rule.getActivity());
+        verifyFadeProperties(inflater.inflateTransition(R.transition.fade));
+        verifyChangeBoundsProperties(inflater.inflateTransition(R.transition.change_bounds));
+        verifySlideProperties(inflater.inflateTransition(R.transition.slide));
+        verifyExplodeProperties(inflater.inflateTransition(R.transition.explode));
+        verifyChangeImageTransformProperties(
+                inflater.inflateTransition(R.transition.change_image_transform));
+        verifyChangeTransformProperties(inflater.inflateTransition(R.transition.change_transform));
+        verifyChangeClipBoundsProperties(
+                inflater.inflateTransition(R.transition.change_clip_bounds));
+        verifyAutoTransitionProperties(inflater.inflateTransition(R.transition.auto_transition));
+        verifyChangeScrollProperties(inflater.inflateTransition(R.transition.change_scroll));
+        verifyTransitionSetProperties(inflater.inflateTransition(R.transition.transition_set));
+        verifyCustomTransitionProperties(
+                inflater.inflateTransition(R.transition.custom_transition));
+        verifyTargetIds(inflater.inflateTransition(R.transition.target_ids));
+        verifyTargetNames(inflater.inflateTransition(R.transition.target_names));
+        verifyTargetClass(inflater.inflateTransition(R.transition.target_classes));
+        verifyArcMotion(inflater.inflateTransition(R.transition.arc_motion));
+        verifyCustomPathMotion(inflater.inflateTransition(R.transition.custom_path_motion));
+        verifyPatternPathMotion(inflater.inflateTransition(R.transition.pattern_path_motion));
+    }
+
+    // TODO: Add test for TransitionManager
+
+    private void verifyFadeProperties(Transition transition) {
+        assertTrue(transition instanceof Fade);
+        Fade fade = (Fade) transition;
+        assertEquals(Fade.OUT, fade.getMode());
+    }
+
+    private void verifyChangeBoundsProperties(Transition transition) {
+        assertTrue(transition instanceof ChangeBounds);
+        ChangeBounds changeBounds = (ChangeBounds) transition;
+        assertTrue(changeBounds.getResizeClip());
+    }
+
+    private void verifySlideProperties(Transition transition) {
+        assertTrue(transition instanceof Slide);
+        Slide slide = (Slide) transition;
+        assertEquals(Gravity.TOP, slide.getSlideEdge());
+    }
+
+    private void verifyExplodeProperties(Transition transition) {
+        assertTrue(transition instanceof Explode);
+        Visibility visibility = (Visibility) transition;
+        assertEquals(Visibility.MODE_IN, visibility.getMode());
+    }
+
+    private void verifyChangeImageTransformProperties(Transition transition) {
+        assertTrue(transition instanceof ChangeImageTransform);
+    }
+
+    private void verifyChangeTransformProperties(Transition transition) {
+        assertTrue(transition instanceof ChangeTransform);
+        ChangeTransform changeTransform = (ChangeTransform) transition;
+        assertFalse(changeTransform.getReparent());
+        assertFalse(changeTransform.getReparentWithOverlay());
+    }
+
+    private void verifyChangeClipBoundsProperties(Transition transition) {
+        assertTrue(transition instanceof ChangeClipBounds);
+    }
+
+    private void verifyAutoTransitionProperties(Transition transition) {
+        assertTrue(transition instanceof AutoTransition);
+    }
+
+    private void verifyChangeScrollProperties(Transition transition) {
+        assertTrue(transition instanceof ChangeScroll);
+    }
+
+    private void verifyTransitionSetProperties(Transition transition) {
+        assertTrue(transition instanceof TransitionSet);
+        TransitionSet set = (TransitionSet) transition;
+        assertEquals(TransitionSet.ORDERING_SEQUENTIAL, set.getOrdering());
+        assertEquals(2, set.getTransitionCount());
+        assertTrue(set.getTransitionAt(0) instanceof ChangeBounds);
+        assertTrue(set.getTransitionAt(1) instanceof Fade);
+    }
+
+    private void verifyCustomTransitionProperties(Transition transition) {
+        assertTrue(transition instanceof CustomTransition);
+    }
+
+    private void verifyTargetIds(Transition transition) {
+        List<Integer> targets = transition.getTargetIds();
+        assertNotNull(targets);
+        assertEquals(2, targets.size());
+        assertEquals(R.id.hello, (int) targets.get(0));
+        assertEquals(R.id.world, (int) targets.get(1));
+    }
+
+    private void verifyTargetNames(Transition transition) {
+        List<String> targets = transition.getTargetNames();
+        assertNotNull(targets);
+        assertEquals(2, targets.size());
+        assertEquals("hello", targets.get(0));
+        assertEquals("world", targets.get(1));
+    }
+
+    private void verifyTargetClass(Transition transition) {
+        List<Class> targets = transition.getTargetTypes();
+        assertNotNull(targets);
+        assertEquals(2, targets.size());
+        assertEquals(TextView.class, targets.get(0));
+        assertEquals(ImageView.class, targets.get(1));
+    }
+
+    private void verifyArcMotion(Transition transition) {
+        assertNotNull(transition);
+        PathMotion motion = transition.getPathMotion();
+        assertNotNull(motion);
+        assertTrue(motion instanceof ArcMotion);
+        ArcMotion arcMotion = (ArcMotion) motion;
+        assertEquals(1f, arcMotion.getMinimumVerticalAngle(), 0.01f);
+        assertEquals(2f, arcMotion.getMinimumHorizontalAngle(), 0.01f);
+        assertEquals(53f, arcMotion.getMaximumAngle(), 0.01f);
+    }
+
+    private void verifyCustomPathMotion(Transition transition) {
+        assertNotNull(transition);
+        PathMotion motion = transition.getPathMotion();
+        assertNotNull(motion);
+        assertTrue(motion instanceof CustomPathMotion);
+    }
+
+    private void verifyPatternPathMotion(Transition transition) {
+        assertNotNull(transition);
+        PathMotion motion = transition.getPathMotion();
+        assertNotNull(motion);
+        assertTrue(motion instanceof PatternPathMotion);
+        PatternPathMotion pattern = (PatternPathMotion) motion;
+        Path path = pattern.getPatternPath();
+        PathMeasure measure = new PathMeasure(path, false);
+        assertEquals(200f, measure.getLength(), 0.1f);
+    }
+
+    public static class CustomTransition extends Transition {
+        public CustomTransition() {
+            fail("Default constructor was not expected");
+        }
+
+        @SuppressWarnings("unused") // This constructor is used in XML
+        public CustomTransition(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public void captureStartValues(@NonNull TransitionValues transitionValues) {
+        }
+
+        @Override
+        public void captureEndValues(@NonNull TransitionValues transitionValues) {
+        }
+    }
+
+    public static class CustomPathMotion extends PathMotion {
+        public CustomPathMotion() {
+            fail("default constructor shouldn't be called.");
+        }
+
+        public CustomPathMotion(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public Path getPath(float startX, float startY, float endX, float endY) {
+            return null;
+        }
+    }
+
+    public static class InflationFade extends Fade {
+        public InflationFade(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationChangeBounds extends ChangeBounds {
+        public InflationChangeBounds(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationSlide extends Slide {
+        public InflationSlide(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationTransitionSet extends TransitionSet {
+        public InflationTransitionSet(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationChangeImageTransform extends ChangeImageTransform {
+        public InflationChangeImageTransform(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationChangeTransform extends ChangeTransform {
+        public InflationChangeTransform(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationAutoTransition extends AutoTransition {
+        public InflationAutoTransition(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationChangeClipBounds extends ChangeClipBounds {
+        public InflationChangeClipBounds(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationChangeScroll extends ChangeScroll {
+        public InflationChangeScroll(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+    public static class InflationExplode extends Explode {
+        public InflationExplode(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/TransitionManagerTest.java b/transition/tests/src/android/support/transition/TransitionManagerTest.java
index 21bf413..dc4f983 100644
--- a/transition/tests/src/android/support/transition/TransitionManagerTest.java
+++ b/transition/tests/src/android/support/transition/TransitionManagerTest.java
@@ -20,6 +20,11 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
 
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
@@ -29,9 +34,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 @MediumTest
 public class TransitionManagerTest extends BaseTest {
 
@@ -41,8 +43,8 @@
     public void prepareScenes() {
         TransitionActivity activity = rule.getActivity();
         ViewGroup root = activity.getRoot();
-        mScenes[0] = Scene.getSceneForLayout(root, R.layout.scene0, activity);
-        mScenes[1] = Scene.getSceneForLayout(root, R.layout.scene1, activity);
+        mScenes[0] = Scene.getSceneForLayout(root, R.layout.support_scene0, activity);
+        mScenes[1] = Scene.getSceneForLayout(root, R.layout.support_scene1, activity);
     }
 
     @Test
@@ -121,66 +123,61 @@
                 listener.await(), is(true));
     }
 
-    /**
-     * This {@link Transition.TransitionListener} synchronously waits for the specified callback.
-     */
-    private static class SyncTransitionListener implements Transition.TransitionListener {
-
-        static final int EVENT_START = 1;
-        static final int EVENT_END = 2;
-        static final int EVENT_CANCEL = 3;
-        static final int EVENT_PAUSE = 4;
-        static final int EVENT_RESUME = 5;
-
-        private final int mTargetEvent;
-        private final CountDownLatch mLatch = new CountDownLatch(1);
-
-        SyncTransitionListener(int event) {
-            mTargetEvent = event;
-        }
-
-        boolean await() {
-            try {
-                return mLatch.await(3000, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                return false;
+    @Test
+    public void testGo_nullParameter() throws Throwable {
+        final ViewGroup root = rule.getActivity().getRoot();
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.go(mScenes[0], null);
+                assertThat(Scene.getCurrentScene(root), is(mScenes[0]));
+                TransitionManager.go(mScenes[1], null);
+                assertThat(Scene.getCurrentScene(root), is(mScenes[1]));
             }
-        }
+        });
+    }
 
-        @Override
-        public void onTransitionStart(Transition transition) {
-            if (mTargetEvent == EVENT_START) {
-                mLatch.countDown();
+    @Test
+    public void testEndTransitions() throws Throwable {
+        final ViewGroup root = rule.getActivity().getRoot();
+        final Transition transition = new AutoTransition();
+        // This transition is very long, but will be forced to end as soon as it starts
+        transition.setDuration(30000);
+        final Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+        transition.addListener(listener);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.go(mScenes[0], transition);
             }
-        }
+        });
+        verify(listener, timeout(3000)).onTransitionStart(any(Transition.class));
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.endTransitions(root);
+            }
+        });
+        verify(listener, timeout(3000)).onTransitionEnd(any(Transition.class));
+    }
 
-        @Override
-        public void onTransitionEnd(Transition transition) {
-            if (mTargetEvent == EVENT_END) {
-                mLatch.countDown();
+    @Test
+    public void testEndTransitionsBeforeStarted() throws Throwable {
+        final ViewGroup root = rule.getActivity().getRoot();
+        final Transition transition = new AutoTransition();
+        transition.setDuration(0);
+        final Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+        transition.addListener(listener);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.go(mScenes[0], transition);
+                // This terminates the transition before it starts
+                TransitionManager.endTransitions(root);
             }
-        }
-
-        @Override
-        public void onTransitionCancel(Transition transition) {
-            if (mTargetEvent == EVENT_CANCEL) {
-                mLatch.countDown();
-            }
-        }
-
-        @Override
-        public void onTransitionPause(Transition transition) {
-            if (mTargetEvent == EVENT_PAUSE) {
-                mLatch.countDown();
-            }
-        }
-
-        @Override
-        public void onTransitionResume(Transition transition) {
-            if (mTargetEvent == EVENT_RESUME) {
-                mLatch.countDown();
-            }
-        }
+        });
+        verify(listener, never()).onTransitionStart(any(Transition.class));
+        verify(listener, never()).onTransitionEnd(any(Transition.class));
     }
 
 }
diff --git a/transition/tests/src/android/support/transition/TransitionSetTest.java b/transition/tests/src/android/support/transition/TransitionSetTest.java
new file mode 100644
index 0000000..aec9ecb
--- /dev/null
+++ b/transition/tests/src/android/support/transition/TransitionSetTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@MediumTest
+public class TransitionSetTest extends BaseTest {
+
+    private final TransitionSet mTransitionSet = new TransitionSet();
+    private final Transition mTransition = new TransitionTest.EmptyTransition();
+
+    @Before
+    public void setUp() {
+        // mTransitionSet has 1 item from the start
+        mTransitionSet.addTransition(mTransition);
+    }
+
+    @Test
+    public void testOrdering() {
+        assertThat(mTransitionSet.getOrdering(), is(TransitionSet.ORDERING_TOGETHER));
+        assertThat(mTransitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL),
+                is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getOrdering(), is(TransitionSet.ORDERING_SEQUENTIAL));
+    }
+
+    @Test
+    public void testAddAndRemoveTransition() {
+        assertThat(mTransitionSet.getTransitionCount(), is(1));
+        assertThat(mTransitionSet.getTransitionAt(0), is(sameInstance(mTransition)));
+        Transition anotherTransition = new TransitionTest.EmptyTransition();
+        assertThat(mTransitionSet.addTransition(anotherTransition),
+                is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTransitionCount(), is(2));
+        assertThat(mTransitionSet.getTransitionAt(0), is(sameInstance(mTransition)));
+        assertThat(mTransitionSet.getTransitionAt(1), is(sameInstance(anotherTransition)));
+        assertThat(mTransitionSet.removeTransition(mTransition),
+                is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTransitionCount(), is(1));
+    }
+
+    @Test
+    public void testSetDuration() {
+        assertThat(mTransitionSet.setDuration(123), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getDuration(), is(123L));
+        assertThat(mTransition.getDuration(), is(123L));
+    }
+
+    @Test
+    public void testTargetId() {
+        assertThat(mTransitionSet.addTarget(R.id.view0), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargetIds(), hasItem(R.id.view0));
+        assertThat(mTransitionSet.getTargetIds(), hasSize(1));
+        assertThat(mTransition.getTargetIds(), hasItem(R.id.view0));
+        assertThat(mTransition.getTargetIds(), hasSize(1));
+        assertThat(mTransitionSet.removeTarget(R.id.view0), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargetIds(), hasSize(0));
+        assertThat(mTransition.getTargetIds(), hasSize(0));
+    }
+
+    @Test
+    public void testTargetView() {
+        final View view = new View(rule.getActivity());
+        assertThat(mTransitionSet.addTarget(view), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargets(), hasItem(view));
+        assertThat(mTransitionSet.getTargets(), hasSize(1));
+        assertThat(mTransition.getTargets(), hasItem(view));
+        assertThat(mTransition.getTargets(), hasSize(1));
+        assertThat(mTransitionSet.removeTarget(view), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargets(), hasSize(0));
+        assertThat(mTransition.getTargets(), hasSize(0));
+    }
+
+    @Test
+    public void testTargetName() {
+        assertThat(mTransitionSet.addTarget("abc"), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargetNames(), hasItem("abc"));
+        assertThat(mTransitionSet.getTargetNames(), hasSize(1));
+        assertThat(mTransition.getTargetNames(), hasItem("abc"));
+        assertThat(mTransition.getTargetNames(), hasSize(1));
+        assertThat(mTransitionSet.removeTarget("abc"), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargetNames(), hasSize(0));
+        assertThat(mTransition.getTargetNames(), hasSize(0));
+    }
+
+    @Test
+    public void testTargetClass() {
+        assertThat(mTransitionSet.addTarget(View.class), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargetTypes(), hasItem(View.class));
+        assertThat(mTransitionSet.getTargetTypes(), hasSize(1));
+        assertThat(mTransition.getTargetTypes(), hasItem(View.class));
+        assertThat(mTransition.getTargetTypes(), hasSize(1));
+        assertThat(mTransitionSet.removeTarget(View.class), is(sameInstance(mTransitionSet)));
+        assertThat(mTransitionSet.getTargetTypes(), hasSize(0));
+        assertThat(mTransition.getTargetTypes(), hasSize(0));
+    }
+
+}
diff --git a/transition/tests/src/android/support/transition/TransitionTest.java b/transition/tests/src/android/support/transition/TransitionTest.java
index 0e87629..72f6dae 100644
--- a/transition/tests/src/android/support/transition/TransitionTest.java
+++ b/transition/tests/src/android/support/transition/TransitionTest.java
@@ -16,22 +16,40 @@
 
 package android.support.transition;
 
+
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.animation.Animator;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.transition.test.R;
+import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.LinearInterpolator;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
 
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.List;
@@ -39,6 +57,24 @@
 @MediumTest
 public class TransitionTest extends BaseTest {
 
+    private Scene[] mScenes = new Scene[2];
+    private View[] mViews = new View[3];
+
+    @Before
+    public void prepareScenes() {
+        TransitionActivity activity = rule.getActivity();
+        ViewGroup root = activity.getRoot();
+        mScenes[0] = Scene.getSceneForLayout(root, R.layout.support_scene0, activity);
+        mScenes[1] = Scene.getSceneForLayout(root, R.layout.support_scene1, activity);
+    }
+
+    @Test
+    public void testName() {
+        Transition transition = new EmptyTransition();
+        assertThat(transition.getName(),
+                is(equalTo("android.support.transition.TransitionTest$EmptyTransition")));
+    }
+
     @Test
     public void testDuration() {
         Transition transition = new EmptyTransition();
@@ -81,11 +117,12 @@
 
     @Test
     @UiThreadTest
-    public void testTargets() {
+    public void testTargetView() {
         // Set up views
         TransitionActivity activity = rule.getActivity();
         ViewGroup root = activity.getRoot();
-        View container = LayoutInflater.from(activity).inflate(R.layout.scene0, root, false);
+        View container = LayoutInflater.from(activity)
+                .inflate(R.layout.support_scene0, root, false);
         root.addView(container);
         View view0 = container.findViewById(R.id.view0);
         View view1 = container.findViewById(R.id.view1);
@@ -105,6 +142,99 @@
     }
 
     @Test
+    public void testTargetName() {
+        Transition transition = new EmptyTransition();
+        assertThat(transition.addTarget("a"), is(sameInstance(transition)));
+        assertThat(transition.addTarget("b"), is(sameInstance(transition)));
+        List<String> targetNames = transition.getTargetNames();
+        assertNotNull(targetNames);
+        assertThat(targetNames.size(), is(2));
+        assertThat(targetNames, hasItem("a"));
+        assertThat(targetNames, hasItem("b"));
+        transition.removeTarget("a");
+        assertThat(targetNames.size(), is(1));
+        assertThat(targetNames, not(hasItem("a")));
+        assertThat(targetNames, hasItem("b"));
+    }
+
+    @Test
+    public void testTargetType() {
+        Transition transition = new EmptyTransition();
+        assertThat(transition.addTarget(Button.class), is(sameInstance(transition)));
+        assertThat(transition.addTarget(ImageView.class), is(sameInstance(transition)));
+        List<Class> targetTypes = transition.getTargetTypes();
+        assertNotNull(targetTypes);
+        assertThat(targetTypes.size(), is(2));
+        assertThat(targetTypes, hasItem(Button.class));
+        assertThat(targetTypes, hasItem(ImageView.class));
+        transition.removeTarget(Button.class);
+        assertThat(targetTypes.size(), is(1));
+        assertThat(targetTypes, not(hasItem(Button.class)));
+        assertThat(targetTypes, hasItem(ImageView.class));
+    }
+
+    @Test
+    public void testExcludeTargetId() throws Throwable {
+        showInitialScene();
+        Transition transition = new EmptyTransition();
+        transition.addTarget(R.id.view0);
+        transition.addTarget(R.id.view1);
+        View view0 = rule.getActivity().findViewById(R.id.view0);
+        View view1 = rule.getActivity().findViewById(R.id.view1);
+        assertThat(transition.isValidTarget(view0), is(true));
+        assertThat(transition.isValidTarget(view1), is(true));
+        transition.excludeTarget(R.id.view0, true);
+        assertThat(transition.isValidTarget(view0), is(false));
+        assertThat(transition.isValidTarget(view1), is(true));
+    }
+
+    @Test
+    public void testExcludeTargetView() throws Throwable {
+        showInitialScene();
+        Transition transition = new EmptyTransition();
+        View view0 = rule.getActivity().findViewById(R.id.view0);
+        View view1 = rule.getActivity().findViewById(R.id.view1);
+        transition.addTarget(view0);
+        transition.addTarget(view1);
+        assertThat(transition.isValidTarget(view0), is(true));
+        assertThat(transition.isValidTarget(view1), is(true));
+        transition.excludeTarget(view0, true);
+        assertThat(transition.isValidTarget(view0), is(false));
+        assertThat(transition.isValidTarget(view1), is(true));
+    }
+
+    @Test
+    public void testExcludeTargetName() throws Throwable {
+        showInitialScene();
+        Transition transition = new EmptyTransition();
+        View view0 = rule.getActivity().findViewById(R.id.view0);
+        View view1 = rule.getActivity().findViewById(R.id.view1);
+        ViewCompat.setTransitionName(view0, "zero");
+        ViewCompat.setTransitionName(view1, "one");
+        transition.addTarget("zero");
+        transition.addTarget("one");
+        assertThat(transition.isValidTarget(view0), is(true));
+        assertThat(transition.isValidTarget(view1), is(true));
+        transition.excludeTarget("zero", true);
+        assertThat(transition.isValidTarget(view0), is(false));
+        assertThat(transition.isValidTarget(view1), is(true));
+    }
+
+    @Test
+    public void testExcludeTargetType() throws Throwable {
+        showInitialScene();
+        Transition transition = new EmptyTransition();
+        FrameLayout container = (FrameLayout) rule.getActivity().findViewById(R.id.container);
+        View view0 = rule.getActivity().findViewById(R.id.view0);
+        transition.addTarget(View.class);
+        assertThat(transition.isValidTarget(container), is(true));
+        assertThat(transition.isValidTarget(view0), is(true));
+        transition.excludeTarget(FrameLayout.class, true);
+        assertThat(transition.isValidTarget(container), is(false));
+        assertThat(transition.isValidTarget(view0), is(true));
+    }
+
+    @Test
     public void testListener() {
         Transition transition = new EmptyTransition();
         Transition.TransitionListener listener = new EmptyTransitionListener();
@@ -112,16 +242,140 @@
         assertThat(transition.removeListener(listener), is(sameInstance(transition)));
     }
 
+    @Test
+    public void testMatchOrder() throws Throwable {
+        showInitialScene();
+        final Transition transition = new ChangeBounds() {
+            @Nullable
+            @Override
+            public Animator createAnimator(@NonNull ViewGroup sceneRoot,
+                    @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
+                if (startValues != null && endValues != null) {
+                    fail("Match by View ID should be prevented");
+                }
+                return super.createAnimator(sceneRoot, startValues, endValues);
+            }
+        };
+        transition.setDuration(0);
+        // This prevents matches between start and end scenes because they have different set of
+        // View instances. They will be regarded as independent views even though they share the
+        // same View IDs.
+        transition.setMatchOrder(Transition.MATCH_INSTANCE);
+        SyncRunnable enter1 = new SyncRunnable();
+        mScenes[1].setEnterAction(enter1);
+        goToScene(mScenes[1], transition);
+        if (!enter1.await()) {
+            fail("Timed out while waiting for scene change");
+        }
+    }
+
+    @Test
+    public void testExcludedTransitionAnimator() throws Throwable {
+        showInitialScene();
+        final Animator.AnimatorListener animatorListener = mock(Animator.AnimatorListener.class);
+        final DummyTransition transition = new DummyTransition(animatorListener);
+        final SyncTransitionListener transitionListener = new SyncTransitionListener(
+                SyncTransitionListener.EVENT_END);
+        transition.addListener(transitionListener);
+        transition.addTarget(mViews[0]);
+        transition.excludeTarget(mViews[0], true);
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(rule.getActivity().getRoot(), transition);
+                mViews[0].setTranslationX(3.f);
+            }
+        });
+        if (!transitionListener.await()) {
+            fail("Timed out waiting for the TransitionListener");
+        }
+        verify(animatorListener, never()).onAnimationStart(any(Animator.class));
+    }
+
+    @Test
+    public void testEpicenter() throws Throwable {
+        final Transition transition = new EmptyTransition();
+        final Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
+            private Rect mRect = new Rect();
+
+            @Override
+            public Rect onGetEpicenter(@NonNull Transition t) {
+                assertThat(t, is(sameInstance(transition)));
+                mRect.set(1, 2, 3, 4);
+                return mRect;
+            }
+        };
+        transition.setEpicenterCallback(epicenterCallback);
+        assertThat(transition.getEpicenterCallback(),
+                is(sameInstance(transition.getEpicenterCallback())));
+        Rect rect = transition.getEpicenter();
+        assertNotNull(rect);
+        assertThat(rect.left, is(1));
+        assertThat(rect.top, is(2));
+        assertThat(rect.right, is(3));
+        assertThat(rect.bottom, is(4));
+    }
+
+    @Test
+    public void testSetPropagation() throws Throwable {
+        final Transition transition = new EmptyTransition();
+        assertThat(transition.getPropagation(), is(nullValue()));
+        final TransitionPropagation propagation = new CircularPropagation();
+        transition.setPropagation(propagation);
+        assertThat(propagation, is(sameInstance(propagation)));
+    }
+
+    @Test
+    public void testIsTransitionRequired() throws Throwable {
+        final EmptyTransition transition = new EmptyTransition();
+        assertThat(transition.isTransitionRequired(null, null), is(false));
+        final TransitionValues start = new TransitionValues();
+        final String propname = "android:transition:dummy";
+        start.values.put(propname, 1);
+        final TransitionValues end = new TransitionValues();
+        end.values.put(propname, 1);
+        assertThat(transition.isTransitionRequired(start, end), is(false));
+        end.values.put(propname, 2);
+        assertThat(transition.isTransitionRequired(start, end), is(true));
+    }
+
+    private void showInitialScene() throws Throwable {
+        SyncRunnable enter0 = new SyncRunnable();
+        mScenes[0].setEnterAction(enter0);
+        AutoTransition transition1 = new AutoTransition();
+        transition1.setDuration(0);
+        goToScene(mScenes[0], transition1);
+        if (!enter0.await()) {
+            fail("Timed out while waiting for scene change");
+        }
+        mViews[0] = rule.getActivity().findViewById(R.id.view0);
+        mViews[1] = rule.getActivity().findViewById(R.id.view1);
+        mViews[2] = rule.getActivity().findViewById(R.id.view2);
+    }
+
+    private void goToScene(final Scene scene, final Transition transition) throws Throwable {
+        rule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.go(scene, transition);
+            }
+        });
+    }
+
     public static class EmptyTransition extends Transition {
 
-        public void captureEndValues(TransitionValues transitionValues) {
+        @Override
+        public void captureEndValues(@NonNull TransitionValues transitionValues) {
         }
 
-        public void captureStartValues(TransitionValues transitionValues) {
+        @Override
+        public void captureStartValues(@NonNull TransitionValues transitionValues) {
         }
 
-        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-                TransitionValues endValues) {
+        @Override
+        public Animator createAnimator(@NonNull ViewGroup sceneRoot,
+                @Nullable TransitionValues startValues,
+                @Nullable TransitionValues endValues) {
             return null;
         }
 
@@ -129,21 +383,60 @@
 
     public static class EmptyTransitionListener implements Transition.TransitionListener {
 
-        public void onTransitionStart(Transition transition) {
+        @Override
+        public void onTransitionStart(@NonNull Transition transition) {
         }
 
-        public void onTransitionEnd(Transition transition) {
+        @Override
+        public void onTransitionEnd(@NonNull Transition transition) {
         }
 
-        public void onTransitionCancel(Transition transition) {
+        @Override
+        public void onTransitionCancel(@NonNull Transition transition) {
         }
 
-        public void onTransitionPause(Transition transition) {
+        @Override
+        public void onTransitionPause(@NonNull Transition transition) {
         }
 
-        public void onTransitionResume(Transition transition) {
+        @Override
+        public void onTransitionResume(@NonNull Transition transition) {
         }
 
     }
 
+    /**
+     * A dummy transition for monitoring use of its animator by the Transition framework.
+     */
+    private static class DummyTransition extends Transition {
+
+        private final Animator.AnimatorListener mListener;
+
+        DummyTransition(Animator.AnimatorListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void captureStartValues(@NonNull TransitionValues transitionValues) {
+            transitionValues.values.put("state", 1);
+        }
+
+        @Override
+        public void captureEndValues(@NonNull TransitionValues transitionValues) {
+            transitionValues.values.put("state", 2);
+        }
+
+        @Override
+        public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues,
+                TransitionValues endValues) {
+            if (startValues == null || endValues == null) {
+                return null;
+            }
+            final ObjectAnimator animator = ObjectAnimator
+                    .ofFloat(startValues.view, "translationX", 1.f, 2.f);
+            animator.addListener(mListener);
+            return animator;
+        }
+
+    }
 }
diff --git a/transition/tests/src/android/support/transition/VisibilityTest.java b/transition/tests/src/android/support/transition/VisibilityTest.java
new file mode 100644
index 0000000..dcfcbdc
--- /dev/null
+++ b/transition/tests/src/android/support/transition/VisibilityTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+package android.support.transition;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+@MediumTest
+public class VisibilityTest extends BaseTest {
+
+    private View mView;
+    private ViewGroup mRoot;
+
+    @UiThreadTest
+    @Before
+    public void setUp() {
+        mRoot = rule.getActivity().getRoot();
+        mView = new View(rule.getActivity());
+        mRoot.addView(mView, new ViewGroup.LayoutParams(100, 100));
+    }
+
+    @Test
+    public void testMode() {
+        final CustomVisibility visibility = new CustomVisibility();
+        assertThat(visibility.getMode(), is(Visibility.MODE_IN | Visibility.MODE_OUT));
+        visibility.setMode(Visibility.MODE_IN);
+        assertThat(visibility.getMode(), is(Visibility.MODE_IN));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testCustomVisibility() {
+        final CustomVisibility visibility = new CustomVisibility();
+        assertThat(visibility.getName(), is(equalTo(CustomVisibility.class.getName())));
+        assertNotNull(visibility.getTransitionProperties());
+
+        // Capture start values
+        mView.setScaleX(0.5f);
+        final TransitionValues startValues = new TransitionValues();
+        startValues.view = mView;
+        visibility.captureStartValues(startValues);
+        assertThat((float) startValues.values.get(CustomVisibility.PROPNAME_SCALE_X), is(0.5f));
+
+        // Hide the view and capture end values
+        mView.setVisibility(View.GONE);
+        final TransitionValues endValues = new TransitionValues();
+        endValues.view = mView;
+        visibility.captureEndValues(endValues);
+
+        // This should invoke onDisappear, not onAppear
+        ObjectAnimator animator = (ObjectAnimator) visibility
+                .createAnimator(mRoot, startValues, endValues);
+        assertNotNull(animator);
+        assertThat(animator.getPropertyName(), is(equalTo("scaleX")));
+
+        // Jump to the end of the animation
+        animator.end();
+
+        // This value confirms that onDisappear, not onAppear, was called
+        assertThat((float) animator.getAnimatedValue(), is(0.25f));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testCustomVisibility2() {
+        final CustomVisibility2 visibility = new CustomVisibility2();
+        final TransitionValues startValues = new TransitionValues();
+        startValues.view = mView;
+        visibility.captureStartValues(startValues);
+        mView.setVisibility(View.GONE);
+        final TransitionValues endValues = new TransitionValues();
+        endValues.view = mView;
+        visibility.captureEndValues(endValues);
+        ObjectAnimator animator = (ObjectAnimator) visibility
+                .createAnimator(mRoot, startValues, endValues);
+        assertNotNull(animator);
+
+        // Jump to the end of the animation
+        animator.end();
+
+        // This value confirms that onDisappear, not onAppear, was called
+        assertThat((float) animator.getAnimatedValue(), is(0.25f));
+    }
+
+    /**
+     * A custom {@link Visibility} with 5-arg onAppear/Disappear
+     */
+    public static class CustomVisibility extends Visibility {
+
+        static final String PROPNAME_SCALE_X = "customVisibility:scaleX";
+
+        private static String[] sTransitionProperties;
+
+        @Nullable
+        @Override
+        public String[] getTransitionProperties() {
+            if (sTransitionProperties == null) {
+                String[] properties = super.getTransitionProperties();
+                if (properties != null) {
+                    sTransitionProperties = Arrays.copyOf(properties, properties.length + 1);
+                } else {
+                    sTransitionProperties = new String[1];
+                }
+                sTransitionProperties[sTransitionProperties.length - 1] = PROPNAME_SCALE_X;
+            }
+            return sTransitionProperties;
+        }
+
+        @Override
+        public void captureStartValues(@NonNull TransitionValues transitionValues) {
+            super.captureStartValues(transitionValues);
+            transitionValues.values.put(PROPNAME_SCALE_X, transitionValues.view.getScaleX());
+        }
+
+        @Override
+        public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues,
+                int startVisibility, TransitionValues endValues, int endVisibility) {
+            if (startValues == null) {
+                return null;
+            }
+            float startScaleX = (float) startValues.values.get(PROPNAME_SCALE_X);
+            return ObjectAnimator.ofFloat(startValues.view, "scaleX", startScaleX, 0.75f);
+        }
+
+        @Override
+        public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
+                int startVisibility, TransitionValues endValues, int endVisibility) {
+            if (startValues == null) {
+                return null;
+            }
+            float startScaleX = (float) startValues.values.get(PROPNAME_SCALE_X);
+            return ObjectAnimator.ofFloat(startValues.view, "scaleX", startScaleX, 0.25f);
+        }
+
+    }
+
+    /**
+     * A custom {@link Visibility} with 4-arg onAppear/Disappear
+     */
+    public static class CustomVisibility2 extends Visibility {
+
+        static final String PROPNAME_SCALE_X = "customVisibility:scaleX";
+
+        @Override
+        public void captureStartValues(@NonNull TransitionValues transitionValues) {
+            super.captureStartValues(transitionValues);
+            transitionValues.values.put(PROPNAME_SCALE_X, transitionValues.view.getScaleX());
+        }
+
+        @Override
+        public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+                TransitionValues endValues) {
+            float startScaleX = startValues == null ? 0.25f :
+                    (float) startValues.values.get(PROPNAME_SCALE_X);
+            return ObjectAnimator.ofFloat(view, "scaleX", startScaleX, 0.75f);
+        }
+
+        @Override
+        public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+                TransitionValues endValues) {
+            if (startValues == null) {
+                return null;
+            }
+            float startScaleX = (float) startValues.values.get(PROPNAME_SCALE_X);
+            return ObjectAnimator.ofFloat(view, "scaleX", startScaleX, 0.25f);
+        }
+
+    }
+
+}
diff --git a/tv-provider/Android.mk b/tv-provider/Android.mk
new file mode 100644
index 0000000..4fa8af4
--- /dev/null
+++ b/tv-provider/Android.mk
@@ -0,0 +1,34 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must include it with
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := android-support-tv-provider
+#
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-support-tv-provider
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-compat \
+    android-support-annotations
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tv-provider/AndroidManifest.xml b/tv-provider/AndroidManifest.xml
new file mode 100644
index 0000000..f07d090
--- /dev/null
+++ b/tv-provider/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.media.tv">
+    <uses-sdk android:minSdkVersion="21"/>
+    <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+    <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+    <application>
+        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+    </application>
+</manifest>
diff --git a/tv-provider/build.gradle b/tv-provider/build.gradle
new file mode 100644
index 0000000..e16394e
--- /dev/null
+++ b/tv-provider/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: android.support.SupportLibraryPlugin
+archivesBaseName = 'support-tv-provider'
+
+dependencies {
+    api project(':support-annotations')
+    api project(':support-compat')
+
+    androidTestImplementation (libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 21
+    }
+
+    sourceSets {
+        main.java.srcDirs = ['src']
+        main.res.srcDir 'res'
+    }
+}
+
+supportLibrary {
+    name 'Android Support TV Provider'
+    inceptionYear '2017'
+    description 'Android Support Library for TV Provider'
+}
\ No newline at end of file
diff --git a/tv-provider/lint-baseline.xml b/tv-provider/lint-baseline.xml
new file mode 100644
index 0000000..4eaddc8
--- /dev/null
+++ b/tv-provider/lint-baseline.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: PreviewProgramColumns.TYPE_MOVIE, PreviewProgramColumns.TYPE_TV_SERIES, PreviewProgramColumns.TYPE_TV_SEASON, PreviewProgramColumns.TYPE_TV_EPISODE, PreviewProgramColumns.TYPE_CLIP, PreviewProgramColumns.TYPE_EVENT, PreviewProgramColumns.TYPE_CHANNEL, PreviewProgramColumns.TYPE_TRACK, PreviewProgramColumns.TYPE_ALBUM, PreviewProgramColumns.TYPE_ARTIST, PreviewProgramColumns.TYPE_PLAYLIST, PreviewProgramColumns.TYPE_STATION"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/BasePreviewProgram.java"
+            line="129"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: PreviewProgramColumns.ASPECT_RATIO_16_9, PreviewProgramColumns.ASPECT_RATIO_3_2, PreviewProgramColumns.ASPECT_RATIO_4_3, PreviewProgramColumns.ASPECT_RATIO_1_1, PreviewProgramColumns.ASPECT_RATIO_2_3"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/BasePreviewProgram.java"
+            line="139"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: PreviewProgramColumns.ASPECT_RATIO_16_9, PreviewProgramColumns.ASPECT_RATIO_3_2, PreviewProgramColumns.ASPECT_RATIO_4_3, PreviewProgramColumns.ASPECT_RATIO_1_1, PreviewProgramColumns.ASPECT_RATIO_2_3"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/BasePreviewProgram.java"
+            line="149"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: PreviewProgramColumns.AVAILABILITY_AVAILABLE, PreviewProgramColumns.AVAILABILITY_FREE_WITH_SUBSCRIPTION, PreviewProgramColumns.AVAILABILITY_PAID_CONTENT"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/BasePreviewProgram.java"
+            line="167"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: PreviewProgramColumns.INTERACTION_TYPE_VIEWS, PreviewProgramColumns.INTERACTION_TYPE_LISTENS, PreviewProgramColumns.INTERACTION_TYPE_FOLLOWERS, PreviewProgramColumns.INTERACTION_TYPE_FANS, PreviewProgramColumns.INTERACTION_TYPE_LIKES, PreviewProgramColumns.INTERACTION_TYPE_THUMBS, PreviewProgramColumns.INTERACTION_TYPE_VIEWERS"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/BasePreviewProgram.java"
+            line="218"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: ProgramColumns.REVIEW_RATING_STYLE_STARS, ProgramColumns.REVIEW_RATING_STYLE_THUMBS_UP_DOWN, ProgramColumns.REVIEW_RATING_STYLE_PERCENTAGE"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/BaseProgram.java"
+            line="257"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: Genres.FAMILY_KIDS, Genres.SPORTS, Genres.SHOPPING, Genres.MOVIES, Genres.COMEDY, Genres.TRAVEL, Genres.DRAMA, Genres.EDUCATION, Genres.ANIMAL_WILDLIFE, Genres.NEWS, Genres.GAMING, Genres.ARTS, Genres.ENTERTAINMENT, Genres.LIFE_STYLE, Genres.MUSIC, Genres.PREMIER, Genres.TECH_SCIENCE"
+        errorLine1="            mValues.put(Programs.COLUMN_BROADCAST_GENRE, Programs.Genres.encode(genres));"
+        errorLine2="                                                                                ~~~~~~">
+        <location
+            file="src/android/support/media/tv/Program.java"
+            line="286"
+            column="81"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE, WatchNextPrograms.WATCH_NEXT_TYPE_NEXT, WatchNextPrograms.WATCH_NEXT_TYPE_NEW, WatchNextPrograms.WATCH_NEXT_TYPE_WATCHLIST"
+        errorLine1="        return i == null ? INVALID_INT_VALUE : i;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/media/tv/WatchNextProgram.java"
+            line="99"
+            column="28"/>
+    </issue>
+
+</issues>
diff --git a/tv-provider/src/android/support/media/tv/BasePreviewProgram.java b/tv-provider/src/android/support/media/tv/BasePreviewProgram.java
new file mode 100644
index 0000000..6f2902a
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/BasePreviewProgram.java
@@ -0,0 +1,792 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.AspectRatio;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.Availability;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.InteractionType;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.Type;
+import android.support.media.tv.TvContractCompat.PreviewPrograms;
+
+import java.net.URISyntaxException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Base class for derived classes that want to have common fields for preview programs.
+ * @hide
+ */
+@TargetApi(26)
+public abstract class BasePreviewProgram extends BaseProgram {
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String[] PROJECTION = getProjection();
+
+    private static final int INVALID_INT_VALUE = -1;
+    private static final long INVALID_LONG_VALUE = -1;
+    private static final int IS_TRANSIENT = 1;
+    private static final int IS_LIVE = 1;
+    private static final int IS_BROWSABLE = 1;
+
+    BasePreviewProgram(Builder builder) {
+        super(builder);
+    }
+
+    /**
+     * @return The internal provider ID for the program.
+     * @see PreviewPrograms#COLUMN_INTERNAL_PROVIDER_ID
+     */
+    public String getInternalProviderId() {
+        return mValues.getAsString(PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID);
+    }
+
+    /**
+     * @return The preview video URI for the program.
+     * @see PreviewPrograms#COLUMN_PREVIEW_VIDEO_URI
+     */
+    public Uri getPreviewVideoUri() {
+        String uri = mValues.getAsString(PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The last playback position of the program in millis.
+     * @see PreviewPrograms#COLUMN_LAST_PLAYBACK_POSITION_MILLIS
+     */
+    public int getLastPlaybackPositionMillis() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The duration of the program in millis.
+     * @see PreviewPrograms#COLUMN_DURATION_MILLIS
+     */
+    public int getDurationMillis() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_DURATION_MILLIS);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The intent URI which is launched when the program is selected.
+     * @see PreviewPrograms#COLUMN_INTENT_URI
+     */
+    public Uri getIntentUri() {
+        String uri = mValues.getAsString(PreviewPrograms.COLUMN_INTENT_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The intent which is launched when the program is selected.
+     * @see PreviewPrograms#COLUMN_INTENT_URI
+     */
+    public Intent getIntent() throws URISyntaxException {
+        String uri = mValues.getAsString(PreviewPrograms.COLUMN_INTENT_URI);
+        return uri == null ? null : Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
+    }
+
+    /**
+     * @return Whether the program is transient or not.
+     * @see PreviewPrograms#COLUMN_TRANSIENT
+     */
+    public boolean isTransient() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_TRANSIENT);
+        return i != null && i == IS_TRANSIENT;
+    }
+
+    /**
+     * @return The type of the program.
+     * @see PreviewPrograms#COLUMN_TYPE
+     */
+    public @Type int getType() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_TYPE);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The poster art aspect ratio for the program.
+     * @see PreviewPrograms#COLUMN_POSTER_ART_ASPECT_RATIO
+     * @see PreviewPrograms#COLUMN_POSTER_ART_URI
+     */
+    public @AspectRatio int getPosterArtAspectRatio() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The thumbnail aspect ratio for the program.
+     * @see PreviewPrograms#COLUMN_THUMBNAIL_ASPECT_RATIO
+     * @see PreviewPrograms#COLUMN_THUMBNAIL_URI
+     */
+    public @AspectRatio int getThumbnailAspectRatio() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The logo URI for the program.
+     * @see PreviewPrograms#COLUMN_LOGO_URI
+     */
+    public Uri getLogoUri() {
+        String uri = mValues.getAsString(PreviewPrograms.COLUMN_LOGO_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The availability of the program.
+     * @see PreviewPrograms#COLUMN_AVAILABILITY
+     */
+    public @Availability int getAvailability() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_AVAILABILITY);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The starting price of the program.
+     * @see PreviewPrograms#COLUMN_STARTING_PRICE
+     */
+    public String getStartingPrice() {
+        return mValues.getAsString(PreviewPrograms.COLUMN_STARTING_PRICE);
+    }
+
+    /**
+     * @return The offer price of the program.
+     * @see PreviewPrograms#COLUMN_OFFER_PRICE
+     */
+    public String getOfferPrice() {
+        return mValues.getAsString(PreviewPrograms.COLUMN_OFFER_PRICE);
+    }
+
+    /**
+     * @return The release date of the program.
+     * @see PreviewPrograms#COLUMN_RELEASE_DATE
+     */
+    public String getReleaseDate() {
+        return mValues.getAsString(PreviewPrograms.COLUMN_RELEASE_DATE);
+    }
+
+    /**
+     * @return The item count for the program.
+     * @see PreviewPrograms#COLUMN_ITEM_COUNT
+     */
+    public int getItemCount() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_ITEM_COUNT);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return Whether the program is live or not.
+     * @see PreviewPrograms#COLUMN_LIVE
+     */
+    public boolean isLive() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_LIVE);
+        return i != null && i == IS_LIVE;
+    }
+
+    /**
+     * @return The interaction type for the program.
+     * @see PreviewPrograms#COLUMN_INTERACTION_TYPE
+     */
+    public @InteractionType int getInteractionType() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_INTERACTION_TYPE);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The interaction count for the program.
+     * @see PreviewPrograms#COLUMN_INTERACTION_COUNT
+     */
+    public long getInteractionCount() {
+        Long l = mValues.getAsLong(PreviewPrograms.COLUMN_INTERACTION_COUNT);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    /**
+     * @return The author for the program.
+     * @see PreviewPrograms#COLUMN_AUTHOR
+     */
+    public String getAuthor() {
+        return mValues.getAsString(PreviewPrograms.COLUMN_AUTHOR);
+    }
+
+    /**
+     * @return Whether the program is browsable or not.
+     * @see PreviewPrograms#COLUMN_BROWSABLE;
+     */
+    public boolean isBrowsable() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_BROWSABLE);
+        return i != null && i == IS_BROWSABLE;
+    }
+
+    /**
+     * @return The content ID for the program.
+     * @see PreviewPrograms#COLUMN_CONTENT_ID
+     */
+    public String getContentId() {
+        return mValues.getAsString(PreviewPrograms.COLUMN_CONTENT_ID);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof BasePreviewProgram)) {
+            return false;
+        }
+        return mValues.equals(((BasePreviewProgram) other).mValues);
+    }
+
+    /**
+     * @return The fields of the BasePreviewProgram in {@link ContentValues} format to be easily
+     * inserted into the TV Input Framework database.
+     */
+    @Override
+    public ContentValues toContentValues() {
+        return toContentValues(false);
+    }
+
+    /**
+     * Returns fields of the BasePreviewProgram in the ContentValues format to be easily inserted
+     * into the TV Input Framework database.
+     *
+     * @param includeProtectedFields Whether the fields protected by system is included or not.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public ContentValues toContentValues(boolean includeProtectedFields) {
+        ContentValues values = super.toContentValues();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            values.remove(PreviewProgramColumns.COLUMN_INTERNAL_PROVIDER_ID);
+            values.remove(PreviewProgramColumns.COLUMN_PREVIEW_VIDEO_URI);
+            values.remove(PreviewProgramColumns.COLUMN_LAST_PLAYBACK_POSITION_MILLIS);
+            values.remove(PreviewProgramColumns.COLUMN_DURATION_MILLIS);
+            values.remove(PreviewProgramColumns.COLUMN_INTENT_URI);
+            values.remove(PreviewProgramColumns.COLUMN_TRANSIENT);
+            values.remove(PreviewProgramColumns.COLUMN_TYPE);
+            values.remove(PreviewProgramColumns.COLUMN_POSTER_ART_ASPECT_RATIO);
+            values.remove(PreviewProgramColumns.COLUMN_THUMBNAIL_ASPECT_RATIO);
+            values.remove(PreviewProgramColumns.COLUMN_LOGO_URI);
+            values.remove(PreviewProgramColumns.COLUMN_AVAILABILITY);
+            values.remove(PreviewProgramColumns.COLUMN_STARTING_PRICE);
+            values.remove(PreviewProgramColumns.COLUMN_OFFER_PRICE);
+            values.remove(PreviewProgramColumns.COLUMN_RELEASE_DATE);
+            values.remove(PreviewProgramColumns.COLUMN_ITEM_COUNT);
+            values.remove(PreviewProgramColumns.COLUMN_LIVE);
+            values.remove(PreviewProgramColumns.COLUMN_INTERACTION_COUNT);
+            values.remove(PreviewProgramColumns.COLUMN_AUTHOR);
+            values.remove(PreviewProgramColumns.COLUMN_CONTENT_ID);
+        }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !includeProtectedFields) {
+            values.remove(PreviewProgramColumns.COLUMN_BROWSABLE);
+        }
+        return values;
+    }
+
+    /**
+     * Sets the fields in the cursor to the given builder instance.
+     *
+     * @param cursor A row from the TV Input Framework database.
+     * @param builder A Builder to set the fields.
+     */
+    static void setFieldsFromCursor(Cursor cursor, Builder builder) {
+        // TODO: Add additional API which does not use costly getColumnIndex().
+        BaseProgram.setFieldsFromCursor(cursor, builder);
+        int index;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            if ((index =
+                    cursor.getColumnIndex(PreviewProgramColumns.COLUMN_INTERNAL_PROVIDER_ID)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderId(cursor.getString(index));
+            }
+            if ((index =
+                    cursor.getColumnIndex(PreviewProgramColumns.COLUMN_PREVIEW_VIDEO_URI)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setPreviewVideoUri(Uri.parse(cursor.getString(index)));
+            }
+            if ((index = cursor.getColumnIndex(
+                    PreviewProgramColumns.COLUMN_LAST_PLAYBACK_POSITION_MILLIS)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setLastPlaybackPositionMillis(cursor.getInt(index));
+            }
+            if ((index =
+                    cursor.getColumnIndex(PreviewProgramColumns.COLUMN_DURATION_MILLIS)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setDurationMillis(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_INTENT_URI)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setIntentUri(Uri.parse(cursor.getString(index)));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_TRANSIENT)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setTransient(cursor.getInt(index) == IS_TRANSIENT);
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_TYPE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setType(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(
+                    PreviewProgramColumns.COLUMN_POSTER_ART_ASPECT_RATIO)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setPosterArtAspectRatio(cursor.getInt(index));
+            }
+            if ((index =
+                    cursor.getColumnIndex(PreviewProgramColumns.COLUMN_THUMBNAIL_ASPECT_RATIO)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setThumbnailAspectRatio(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_LOGO_URI)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setLogoUri(Uri.parse(cursor.getString(index)));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_AVAILABILITY)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAvailability(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_STARTING_PRICE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setStartingPrice(cursor.getString(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_OFFER_PRICE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setOfferPrice(cursor.getString(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_RELEASE_DATE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setReleaseDate(cursor.getString(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_ITEM_COUNT)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setItemCount(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_LIVE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setLive(cursor.getInt(index) == IS_LIVE);
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_INTERACTION_TYPE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInteractionType(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_INTERACTION_COUNT)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInteractionCount(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_AUTHOR)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAuthor(cursor.getString(index));
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_BROWSABLE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setBrowsable(cursor.getInt(index) == IS_BROWSABLE);
+            }
+            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_CONTENT_ID)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setContentId(cursor.getString(index));
+            }
+        }
+    }
+
+    private static String[] getProjection() {
+        String[] oColumns = new String[] {
+                PreviewProgramColumns.COLUMN_INTERNAL_PROVIDER_ID,
+                PreviewProgramColumns.COLUMN_PREVIEW_VIDEO_URI,
+                PreviewProgramColumns.COLUMN_LAST_PLAYBACK_POSITION_MILLIS,
+                PreviewProgramColumns.COLUMN_DURATION_MILLIS,
+                PreviewProgramColumns.COLUMN_INTENT_URI,
+                PreviewProgramColumns.COLUMN_TRANSIENT,
+                PreviewProgramColumns.COLUMN_TYPE,
+                PreviewProgramColumns.COLUMN_POSTER_ART_ASPECT_RATIO,
+                PreviewProgramColumns.COLUMN_THUMBNAIL_ASPECT_RATIO,
+                PreviewProgramColumns.COLUMN_LOGO_URI,
+                PreviewProgramColumns.COLUMN_AVAILABILITY,
+                PreviewProgramColumns.COLUMN_STARTING_PRICE,
+                PreviewProgramColumns.COLUMN_OFFER_PRICE,
+                PreviewProgramColumns.COLUMN_RELEASE_DATE,
+                PreviewProgramColumns.COLUMN_ITEM_COUNT,
+                PreviewProgramColumns.COLUMN_LIVE,
+                PreviewProgramColumns.COLUMN_INTERACTION_TYPE,
+                PreviewProgramColumns.COLUMN_INTERACTION_COUNT,
+                PreviewProgramColumns.COLUMN_AUTHOR,
+                PreviewProgramColumns.COLUMN_BROWSABLE,
+                PreviewProgramColumns.COLUMN_CONTENT_ID,
+        };
+        return CollectionUtils.concatAll(BaseProgram.PROJECTION, oColumns);
+    }
+
+    /**
+     * This Builder class simplifies the creation of a {@link BasePreviewProgram} object.
+     *
+     * @param <T> The Builder of the derived classe.
+     */
+    public abstract static class Builder<T extends Builder> extends BaseProgram.Builder<T> {
+        private static final SimpleDateFormat sFormat =
+                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+        static {
+            sFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
+        }
+
+        /**
+         * Creates a new Builder object.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a new Builder object with values copied from another Program.
+         * @param other The Program you're copying from.
+         */
+        public Builder(BasePreviewProgram other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        /**
+         * Sets external ID for the program.
+         *
+         * @param externalId The internal provider ID for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_INTERNAL_PROVIDER_ID
+         */
+        public T setInternalProviderId(String externalId) {
+            mValues.put(PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID, externalId);
+            return (T) this;
+        }
+
+        /**
+         * Sets a URI for the preview video.
+         *
+         * @param previewVideoUri The preview video URI for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_PREVIEW_VIDEO_URI
+         */
+        public T setPreviewVideoUri(Uri previewVideoUri) {
+            mValues.put(PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI,
+                    previewVideoUri == null ? null : previewVideoUri.toString());
+            return (T) this;
+        }
+
+        /**
+         * Sets the last playback position (in milliseconds) of the preview video.
+         *
+         * @param position The last playback posirion for the program in millis.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_LAST_PLAYBACK_POSITION_MILLIS
+         */
+        public T setLastPlaybackPositionMillis(int position) {
+            mValues.put(PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS, position);
+            return (T) this;
+        }
+
+        /**
+         * Sets the last playback duration (in milliseconds) of the preview video.
+         *
+         * @param duration The duration the program in millis.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_DURATION_MILLIS
+         */
+        public T setDurationMillis(int duration) {
+            mValues.put(PreviewPrograms.COLUMN_DURATION_MILLIS, duration);
+            return (T) this;
+        }
+
+        /**
+         * Sets the intent URI which is launched when the program is selected.
+         *
+         * @param intentUri The intent URI for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_INTENT_URI
+         */
+        public T setIntentUri(Uri intentUri) {
+            mValues.put(PreviewPrograms.COLUMN_INTENT_URI,
+                    intentUri == null ? null : intentUri.toString());
+            return (T) this;
+        }
+
+        /**
+         * Sets the intent which is launched when the program is selected.
+         *
+         * @param intent The Intent to be executed when the preview program is selected
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public T setIntent(Intent intent) {
+            return setIntentUri(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+        }
+
+        /**
+         * Sets whether this program is transient or not.
+         *
+         * @param transientValue Whether the program is transient or not.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_TRANSIENT
+         */
+        public T setTransient(boolean transientValue) {
+            mValues.put(PreviewPrograms.COLUMN_TRANSIENT, transientValue ? IS_TRANSIENT : 0);
+            return (T) this;
+        }
+
+        /**
+         * Sets the type of this program content.
+         *
+         * <p>The value should match one of the followings:
+         * {@link PreviewPrograms#TYPE_MOVIE},
+         * {@link PreviewPrograms#TYPE_TV_SERIES},
+         * {@link PreviewPrograms#TYPE_TV_SEASON},
+         * {@link PreviewPrograms#TYPE_TV_EPISODE},
+         * {@link PreviewPrograms#TYPE_CLIP},
+         * {@link PreviewPrograms#TYPE_EVENT},
+         * {@link PreviewPrograms#TYPE_CHANNEL},
+         * {@link PreviewPrograms#TYPE_TRACK},
+         * {@link PreviewPrograms#TYPE_ALBUM},
+         * {@link PreviewPrograms#TYPE_ARTIST},
+         * {@link PreviewPrograms#TYPE_PLAYLIST}, and
+         * {@link PreviewPrograms#TYPE_STATION}.
+         *
+         * @param type The type of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_TYPE
+         */
+        public T setType(@Type int type) {
+            mValues.put(PreviewPrograms.COLUMN_TYPE, type);
+            return (T) this;
+        }
+
+        /**
+         * Sets the aspect ratio of the poster art for this TV program.
+         *
+         * <p>The value should match one of the followings:
+         * {@link PreviewPrograms#ASPECT_RATIO_16_9},
+         * {@link PreviewPrograms#ASPECT_RATIO_3_2},
+         * {@link PreviewPrograms#ASPECT_RATIO_4_3},
+         * {@link PreviewPrograms#ASPECT_RATIO_1_1}, and
+         * {@link PreviewPrograms#ASPECT_RATIO_2_3}.
+         *
+         * @param ratio The poster art aspect ratio for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_POSTER_ART_ASPECT_RATIO
+         * @see PreviewPrograms#COLUMN_POSTER_ART_URI
+         */
+        public T setPosterArtAspectRatio(@AspectRatio int ratio) {
+            mValues.put(PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO, ratio);
+            return (T) this;
+        }
+
+        /**
+         * Sets the aspect ratio of the thumbnail for this TV program.
+         *
+         * <p>The value should match one of the followings:
+         * {@link PreviewPrograms#ASPECT_RATIO_16_9},
+         * {@link PreviewPrograms#ASPECT_RATIO_3_2},
+         * {@link PreviewPrograms#ASPECT_RATIO_4_3},
+         * {@link PreviewPrograms#ASPECT_RATIO_1_1}, and
+         * {@link PreviewPrograms#ASPECT_RATIO_2_3}.
+         *
+         * @param ratio The thumbnail aspect ratio of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_THUMBNAIL_ASPECT_RATIO
+         */
+        public T setThumbnailAspectRatio(@AspectRatio int ratio) {
+            mValues.put(PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO, ratio);
+            return (T) this;
+        }
+
+        /**
+         * Sets the URI for the logo of this TV program.
+         *
+         * @param logoUri The logo URI for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_LOGO_URI
+         */
+        public T setLogoUri(Uri logoUri) {
+            mValues.put(PreviewPrograms.COLUMN_LOGO_URI,
+                    logoUri == null ? null : logoUri.toString());
+            return (T) this;
+        }
+
+        /**
+         * Sets the availability of this TV program.
+         *
+         * <p>The value should match one of the followings:
+         * {@link PreviewPrograms#AVAILABILITY_AVAILABLE},
+         * {@link PreviewPrograms#AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and
+         * {@link PreviewPrograms#AVAILABILITY_PAID_CONTENT}.
+         *
+         * @param availability The availability of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_AVAILABILITY
+         */
+        public T setAvailability(@Availability int availability) {
+            mValues.put(PreviewPrograms.COLUMN_AVAILABILITY, availability);
+            return (T) this;
+        }
+
+        /**
+         * Sets the starting price of this TV program.
+         *
+         * @param price The starting price of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_STARTING_PRICE
+         */
+        public T setStartingPrice(String price) {
+            mValues.put(PreviewPrograms.COLUMN_STARTING_PRICE, price);
+            return (T) this;
+        }
+
+        /**
+         * Sets the offer price of this TV program.
+         *
+         * @param price The offer price of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_OFFER_PRICE
+         */
+        public T setOfferPrice(String price) {
+            mValues.put(PreviewPrograms.COLUMN_OFFER_PRICE, price);
+            return (T) this;
+        }
+
+        /**
+         * Sets the release date of this TV program.
+         *
+         * <p>The value should be in one of the following formats:
+         * "yyyy", "yyyy-MM-dd", and "yyyy-MM-ddTHH:mm:ssZ" (UTC in ISO 8601).
+         *
+         * @param releaseDate The release date of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_RELEASE_DATE
+         */
+        public T setReleaseDate(String releaseDate) {
+            mValues.put(PreviewPrograms.COLUMN_RELEASE_DATE, releaseDate);
+            return (T) this;
+        }
+
+        /**
+         * Sets the release date of this TV program.
+         *
+         * @param releaseDate The release date of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_RELEASE_DATE
+         */
+        public T setReleaseDate(Date releaseDate) {
+            mValues.put(PreviewPrograms.COLUMN_RELEASE_DATE, sFormat.format(releaseDate));
+            return (T) this;
+        }
+
+        /**
+         * Sets the count of the items included in this TV program.
+         *
+         * @param itemCount The item count for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_ITEM_COUNT
+         */
+        public T setItemCount(int itemCount) {
+            mValues.put(PreviewPrograms.COLUMN_ITEM_COUNT, itemCount);
+            return (T) this;
+        }
+
+        /**
+         * Sets whether this TV program is live or not.
+         *
+         * @param live Whether the program is live or not.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_LIVE
+         */
+        public T setLive(boolean live) {
+            mValues.put(PreviewPrograms.COLUMN_LIVE, live ? IS_LIVE : 0);
+            return (T) this;
+        }
+
+        /**
+         * Sets the type of interaction for this TV program.
+         *
+         * <p> The value should match one of the followings:
+         * {@link PreviewPrograms#INTERACTION_TYPE_LISTENS},
+         * {@link PreviewPrograms#INTERACTION_TYPE_FOLLOWERS},
+         * {@link PreviewPrograms#INTERACTION_TYPE_FANS},
+         * {@link PreviewPrograms#INTERACTION_TYPE_LIKES},
+         * {@link PreviewPrograms#INTERACTION_TYPE_THUMBS},
+         * {@link PreviewPrograms#INTERACTION_TYPE_VIEWS}, and
+         * {@link PreviewPrograms#INTERACTION_TYPE_VIEWERS}.
+         *
+         * @param interactionType The interaction type of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_INTERACTION_TYPE
+         */
+        public T setInteractionType(@InteractionType int interactionType) {
+            mValues.put(PreviewPrograms.COLUMN_INTERACTION_TYPE, interactionType);
+            return (T) this;
+        }
+
+        /**
+         * Sets the interaction count for this program.
+         *
+         * @param interactionCount The interaction count for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_INTERACTION_COUNT
+         */
+        public T setInteractionCount(long interactionCount) {
+            mValues.put(PreviewPrograms.COLUMN_INTERACTION_COUNT, interactionCount);
+            return (T) this;
+        }
+
+        /**
+         * Sets the author or artist of this content.
+         *
+         * @param author The author of the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_AUTHOR
+         */
+        public T setAuthor(String author) {
+            mValues.put(PreviewPrograms.COLUMN_AUTHOR, author);
+            return (T) this;
+        }
+
+        /**
+         * Sets whether this TV program is browsable or not.
+         *
+         * @param browsable Whether the program is browsable or not.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_BROWSABLE
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public T setBrowsable(boolean browsable) {
+            mValues.put(PreviewPrograms.COLUMN_BROWSABLE, browsable ? IS_BROWSABLE : 0);
+            return (T) this;
+        }
+
+        /**
+         * Sets the content ID for this program.
+         *
+         * @param contentId The content ID for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see PreviewPrograms#COLUMN_CONTENT_ID
+         */
+        public T setContentId(String contentId) {
+            mValues.put(PreviewPrograms.COLUMN_CONTENT_ID, contentId);
+            return (T) this;
+        }
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/BaseProgram.java b/tv-provider/src/android/support/media/tv/BaseProgram.java
new file mode 100644
index 0000000..e4ce9d1
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/BaseProgram.java
@@ -0,0 +1,870 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.media.tv.TvContractCompat.BaseTvColumns;
+import android.support.media.tv.TvContractCompat.ProgramColumns;
+import android.support.media.tv.TvContractCompat.ProgramColumns.ReviewRatingStyle;
+import android.support.media.tv.TvContractCompat.Programs;
+import android.support.media.tv.TvContractCompat.Programs.Genres.Genre;
+
+/**
+ * Base class for derived classes that want to have common fields for programs defined in
+ * {@link TvContractCompat}.
+ * @hide
+ */
+public abstract class BaseProgram {
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String[] PROJECTION = getProjection();
+
+    private static final long INVALID_LONG_VALUE = -1;
+    private static final int INVALID_INT_VALUE = -1;
+    private static final int IS_SEARCHABLE = 1;
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    protected ContentValues mValues;
+
+    /* package-private */
+    BaseProgram(Builder builder) {
+        mValues = builder.mValues;
+    }
+
+    /**
+     * @return The ID for the program.
+     * @see BaseTvColumns#_ID
+     */
+    public long getId() {
+        Long l = mValues.getAsLong(BaseTvColumns._ID);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    /**
+     * @return The package name for the program.
+     * @see BaseTvColumns#COLUMN_PACKAGE_NAME
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public String getPackageName() {
+        return mValues.getAsString(BaseTvColumns.COLUMN_PACKAGE_NAME);
+    }
+
+    /**
+     * @return The title for the program.
+     * @see Programs#COLUMN_TITLE
+     */
+    public String getTitle() {
+        return mValues.getAsString(Programs.COLUMN_TITLE);
+    }
+
+    /**
+     * @return The episode title for the program.
+     * @see Programs#COLUMN_EPISODE_TITLE
+     */
+    public String getEpisodeTitle() {
+        return mValues.getAsString(Programs.COLUMN_EPISODE_TITLE);
+    }
+
+    /**
+     * @return The season display number for the program.
+     * @see Programs#COLUMN_SEASON_DISPLAY_NUMBER
+     */
+    public String getSeasonNumber() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return mValues.getAsString(Programs.COLUMN_SEASON_DISPLAY_NUMBER);
+        } else {
+            return mValues.getAsString(Programs.COLUMN_SEASON_NUMBER);
+        }
+    }
+
+    /**
+     * @return The episode display number for the program.
+     * @see Programs#COLUMN_EPISODE_DISPLAY_NUMBER
+     */
+    public String getEpisodeNumber() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return mValues.getAsString(Programs.COLUMN_EPISODE_DISPLAY_NUMBER);
+        } else {
+            return mValues.getAsString(Programs.COLUMN_EPISODE_NUMBER);
+        }
+    }
+
+    /**
+     * @return The short description for the program.
+     * @see Programs#COLUMN_SHORT_DESCRIPTION
+     */
+    public String getDescription() {
+        return mValues.getAsString(Programs.COLUMN_SHORT_DESCRIPTION);
+    }
+
+    /**
+     * @return The long description for the program.
+     * @see Programs#COLUMN_LONG_DESCRIPTION
+     */
+    public String getLongDescription() {
+        return mValues.getAsString(Programs.COLUMN_LONG_DESCRIPTION);
+    }
+
+    /**
+     * @return The video width for the program.
+     * @see Programs#COLUMN_VIDEO_WIDTH
+     */
+    public int getVideoWidth() {
+        Integer i = mValues.getAsInteger(Programs.COLUMN_VIDEO_WIDTH);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The video height for the program.
+     * @see Programs#COLUMN_VIDEO_HEIGHT
+     */
+    public int getVideoHeight() {
+        Integer i = mValues.getAsInteger(Programs.COLUMN_VIDEO_HEIGHT);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The canonical genre for the program.
+     * @see Programs#COLUMN_CANONICAL_GENRE
+     */
+    public @Genre String[] getCanonicalGenres() {
+        return Programs.Genres.decode(mValues.getAsString(Programs.COLUMN_CANONICAL_GENRE));
+    }
+
+    /**
+     * @return The content rating for the program.
+     * @see Programs#COLUMN_CONTENT_RATING
+     */
+    public TvContentRating[] getContentRatings() {
+        return TvContractUtils.stringToContentRatings(mValues.getAsString(
+                Programs.COLUMN_CONTENT_RATING));
+    }
+
+    /**
+     * @return The poster art URI for the program.
+     * @see Programs#COLUMN_POSTER_ART_URI
+     */
+    public Uri getPosterArtUri() {
+        String uri = mValues.getAsString(Programs.COLUMN_POSTER_ART_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The thumbnail URI for the program.
+     * @see Programs#COLUMN_THUMBNAIL_URI
+     */
+    public Uri getThumbnailUri() {
+        String uri = mValues.getAsString(Programs.COLUMN_POSTER_ART_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The internal provider data for the program.
+     * @see Programs#COLUMN_INTERNAL_PROVIDER_DATA
+     */
+    public byte[] getInternalProviderDataByteArray() {
+        return mValues.getAsByteArray(Programs.COLUMN_INTERNAL_PROVIDER_DATA);
+    }
+
+    /**
+     * @return The audio languages for the program.
+     * @see Programs#COLUMN_AUDIO_LANGUAGE
+     */
+    public String[] getAudioLanguages() {
+        return TvContractUtils.stringToAudioLanguages(mValues.getAsString(
+                Programs.COLUMN_AUDIO_LANGUAGE));
+    }
+
+    /**
+     * @return Whether the program is searchable or not.
+     * @see Programs#COLUMN_SEARCHABLE
+     */
+    public boolean isSearchable() {
+        Integer i = mValues.getAsInteger(Programs.COLUMN_SEARCHABLE);
+        return i == null || i == IS_SEARCHABLE;
+    }
+
+    /**
+     * @return The first internal provider flag for the program.
+     * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG1
+     */
+    public Long getInternalProviderFlag1() {
+        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG1);
+    }
+
+    /**
+     * @return The second internal provider flag for the program.
+     * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG2
+     */
+    public Long getInternalProviderFlag2() {
+        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG2);
+    }
+
+    /**
+     * @return The third internal provider flag for the program.
+     * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG3
+     */
+    public Long getInternalProviderFlag3() {
+        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG3);
+    }
+
+    /**
+     * @return The forth internal provider flag for the program.
+     * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG4
+     */
+    public Long getInternalProviderFlag4() {
+        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG4);
+    }
+
+    /**
+     * @return The season title for the program.
+     * @see Programs#COLUMN_SEASON_TITLE
+     */
+    public String getSeasonTitle() {
+        return mValues.getAsString(Programs.COLUMN_SEASON_TITLE);
+    }
+
+    /**
+     * @return The review rating style for the program.
+     * @see Programs#COLUMN_REVIEW_RATING_STYLE
+     */
+    public @ReviewRatingStyle int getReviewRatingStyle() {
+        Integer i = mValues.getAsInteger(Programs.COLUMN_REVIEW_RATING_STYLE);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The review rating for the program.
+     * @see Programs#COLUMN_REVIEW_RATING
+     */
+    public String getReviewRating() {
+        return mValues.getAsString(Programs.COLUMN_REVIEW_RATING);
+    }
+
+    @Override
+    public int hashCode() {
+        return mValues.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof BaseProgram)) {
+            return false;
+        }
+        return mValues.equals(((BaseProgram) other).mValues);
+    }
+
+    @Override
+    public String toString() {
+        return "BaseProgram{" + mValues.toString() + "}";
+    }
+
+    /**
+     * @return The fields of the BaseProgram in {@link ContentValues} format to be easily inserted
+     * into the TV Input Framework database.
+     */
+    public ContentValues toContentValues() {
+        ContentValues values = new ContentValues(mValues);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            values.remove(ProgramColumns.COLUMN_SEARCHABLE);
+            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1);
+            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2);
+            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3);
+            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4);
+        }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            values.remove(ProgramColumns.COLUMN_SEASON_TITLE);
+        }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            values.remove(ProgramColumns.COLUMN_REVIEW_RATING_STYLE);
+            values.remove(ProgramColumns.COLUMN_REVIEW_RATING);
+        }
+        return values;
+    }
+
+    /**
+     * Sets the fields in the cursor to the given builder instance.
+     *
+     * @param cursor A row from the TV Input Framework database.
+     * @param builder A Builder to set the fields.
+     */
+    static void setFieldsFromCursor(Cursor cursor, Builder builder) {
+        // TODO: Add additional API which does not use costly getColumnIndex().
+        int index;
+        if ((index = cursor.getColumnIndex(BaseTvColumns._ID)) >= 0 && !cursor.isNull(index)) {
+            builder.setId(cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(BaseTvColumns.COLUMN_PACKAGE_NAME)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setPackageName(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_TITLE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setTitle(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_EPISODE_TITLE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setEpisodeTitle(cursor.getString(index));
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if ((index =
+                    cursor.getColumnIndex(ProgramColumns.COLUMN_SEASON_DISPLAY_NUMBER)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setSeasonNumber(cursor.getString(index), INVALID_INT_VALUE);
+            }
+        } else {
+            if ((index = cursor.getColumnIndex(Programs.COLUMN_SEASON_NUMBER)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setSeasonNumber(cursor.getInt(index));
+            }
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if ((index =
+                    cursor.getColumnIndex(ProgramColumns.COLUMN_EPISODE_DISPLAY_NUMBER)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setEpisodeNumber(cursor.getString(index), INVALID_INT_VALUE);
+            }
+        } else {
+            if ((index = cursor.getColumnIndex(Programs.COLUMN_EPISODE_NUMBER)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setEpisodeNumber(cursor.getInt(index));
+            }
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_SHORT_DESCRIPTION)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setDescription(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_LONG_DESCRIPTION)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setLongDescription(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_POSTER_ART_URI)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setPosterArtUri(Uri.parse(cursor.getString(index)));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_THUMBNAIL_URI)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setThumbnailUri(Uri.parse(cursor.getString(index)));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_AUDIO_LANGUAGE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setAudioLanguages(
+                    TvContractUtils.stringToAudioLanguages(cursor.getString(index)));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_CANONICAL_GENRE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setCanonicalGenres(Programs.Genres.decode(
+                    cursor.getString(index)));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_CONTENT_RATING)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setContentRatings(
+                    TvContractUtils.stringToContentRatings(cursor.getString(index)));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_VIDEO_WIDTH)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setVideoWidth((int) cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_VIDEO_HEIGHT)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setVideoHeight((int) cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_DATA)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setInternalProviderData(cursor.getBlob(index));
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_SEARCHABLE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setSearchable(cursor.getInt(index) == IS_SEARCHABLE);
+            }
+            if ((index =
+                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag1(cursor.getLong(index));
+            }
+            if ((index =
+                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag2(cursor.getLong(index));
+            }
+            if ((index =
+                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag3(cursor.getLong(index));
+            }
+            if ((index =
+                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag4(cursor.getLong(index));
+            }
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_SEASON_TITLE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setSeasonTitle(cursor.getString(index));
+            }
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            if ((index = cursor.getColumnIndex(
+                    ProgramColumns.COLUMN_REVIEW_RATING_STYLE)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setReviewRatingStyle(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_REVIEW_RATING)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setReviewRating(cursor.getString(index));
+            }
+        }
+    }
+
+    private static String[] getProjection() {
+        String[] baseColumns = new String[] {
+                BaseTvColumns._ID,
+                BaseTvColumns.COLUMN_PACKAGE_NAME,
+                ProgramColumns.COLUMN_TITLE,
+                ProgramColumns.COLUMN_EPISODE_TITLE,
+                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+                        ? ProgramColumns.COLUMN_SEASON_DISPLAY_NUMBER
+                        : Programs.COLUMN_SEASON_NUMBER,
+                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+                        ? ProgramColumns.COLUMN_EPISODE_DISPLAY_NUMBER
+                        : Programs.COLUMN_EPISODE_NUMBER,
+                ProgramColumns.COLUMN_SHORT_DESCRIPTION,
+                ProgramColumns.COLUMN_LONG_DESCRIPTION,
+                ProgramColumns.COLUMN_POSTER_ART_URI,
+                ProgramColumns.COLUMN_THUMBNAIL_URI,
+                ProgramColumns.COLUMN_AUDIO_LANGUAGE,
+                ProgramColumns.COLUMN_CANONICAL_GENRE,
+                ProgramColumns.COLUMN_CONTENT_RATING,
+                ProgramColumns.COLUMN_VIDEO_WIDTH,
+                ProgramColumns.COLUMN_VIDEO_HEIGHT,
+                ProgramColumns.COLUMN_INTERNAL_PROVIDER_DATA
+        };
+        String[] marshmallowColumns = new String[] {
+                ProgramColumns.COLUMN_SEARCHABLE,
+                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1,
+                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2,
+                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3,
+                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4,
+        };
+        String[] nougatColumns = new String[] {
+                ProgramColumns.COLUMN_SEASON_TITLE,
+        };
+        String[] oColumns = new String[] {
+                ProgramColumns.COLUMN_REVIEW_RATING,
+                ProgramColumns.COLUMN_REVIEW_RATING_STYLE,
+        };
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            return CollectionUtils.concatAll(baseColumns, marshmallowColumns, nougatColumns,
+                oColumns);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return CollectionUtils.concatAll(baseColumns, marshmallowColumns, nougatColumns);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return CollectionUtils.concatAll(baseColumns, marshmallowColumns);
+        } else {
+            return baseColumns;
+        }
+    }
+
+    /**
+     * This Builder class simplifies the creation of a {@link BaseProgram} object.
+     *
+     * @param <T> The Builder of the derived classe.
+     */
+    public abstract static class Builder<T extends Builder> {
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        protected ContentValues mValues;
+
+        /**
+         * Creates a new Builder object.
+         */
+        public Builder() {
+            mValues = new ContentValues();
+        }
+
+        /**
+         * Creates a new Builder object with values copied from another Program.
+         * @param other The Program you're copying from.
+         */
+        public Builder(BaseProgram other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        /**
+         * Sets a unique id for this program.
+         *
+         * @param programId The ID for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see BaseTvColumns#_ID
+         */
+        public T setId(long programId) {
+            mValues.put(BaseTvColumns._ID, programId);
+            return (T) this;
+        }
+
+        /**
+         * Sets the package name for this program.
+         *
+         * @param packageName The package name for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see BaseTvColumns#COLUMN_PACKAGE_NAME
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public T setPackageName(String packageName) {
+            mValues.put(BaseTvColumns.COLUMN_PACKAGE_NAME, packageName);
+            return (T) this;
+        }
+
+        /**
+         * Sets the title of this program. For a series, this is the series title.
+         *
+         * @param title The title for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_TITLE
+         */
+        public T setTitle(String title) {
+            mValues.put(Programs.COLUMN_TITLE, title);
+            return (T) this;
+        }
+
+        /**
+         * Sets the title of this particular episode for a series.
+         *
+         * @param episodeTitle The episode title for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_EPISODE_TITLE
+         */
+        public T setEpisodeTitle(String episodeTitle) {
+            mValues.put(Programs.COLUMN_EPISODE_TITLE, episodeTitle);
+            return (T) this;
+        }
+
+        /**
+         * Sets the season number for this episode for a series.
+         *
+         * @param seasonNumber The season display number for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_SEASON_DISPLAY_NUMBER
+         */
+        public T setSeasonNumber(int seasonNumber) {
+            setSeasonNumber(String.valueOf(seasonNumber), seasonNumber);
+            return (T) this;
+        }
+
+        /**
+         * Sets the season number for this episode for a series.
+         *
+         * @param seasonNumber The season display number for the program.
+         * @param numericalSeasonNumber An integer value for {@link Programs#COLUMN_SEASON_NUMBER}
+         *                              which will be used for API Level 23 and below.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_SEASON_DISPLAY_NUMBER
+         * @see Programs#COLUMN_SEASON_NUMBER
+         */
+        public T setSeasonNumber(String seasonNumber, int numericalSeasonNumber) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                mValues.put(Programs.COLUMN_SEASON_DISPLAY_NUMBER, seasonNumber);
+            } else {
+                mValues.put(Programs.COLUMN_SEASON_NUMBER, numericalSeasonNumber);
+            }
+            return (T) this;
+        }
+
+        /**
+         * Sets the episode number in a season for this episode for a series.
+         *
+         * @param episodeNumber The value of episode display number for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_EPISODE_DISPLAY_NUMBER
+         */
+        public T setEpisodeNumber(int episodeNumber) {
+            setEpisodeNumber(String.valueOf(episodeNumber), episodeNumber);
+            return (T) this;
+        }
+
+        /**
+         * Sets the episode number in a season for this episode for a series.
+         *
+         * @param episodeNumber The value of episode display number for the program.
+         * @param numericalEpisodeNumber An integer value for {@link Programs#COLUMN_EPISODE_NUMBER}
+         *                               which will be used for API Level 23 and below.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_EPISODE_DISPLAY_NUMBER
+         * @see Programs#COLUMN_EPISODE_NUMBER
+         */
+        public T setEpisodeNumber(String episodeNumber, int numericalEpisodeNumber) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                mValues.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER, episodeNumber);
+            } else {
+                mValues.put(Programs.COLUMN_EPISODE_NUMBER, numericalEpisodeNumber);
+            }
+            return (T) this;
+        }
+
+        /**
+         * Sets a brief description of the program. For a series, this would be a brief description
+         * of the episode.
+         *
+         * @param description The short description for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_SHORT_DESCRIPTION
+         */
+        public T setDescription(String description) {
+            mValues.put(Programs.COLUMN_SHORT_DESCRIPTION, description);
+            return (T) this;
+        }
+
+        /**
+         * Sets a longer description of a program if one exists.
+         *
+         * @param longDescription The long description for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_LONG_DESCRIPTION
+         */
+        public T setLongDescription(String longDescription) {
+            mValues.put(Programs.COLUMN_LONG_DESCRIPTION, longDescription);
+            return (T) this;
+        }
+
+        /**
+         * Sets the video width of the program.
+         *
+         * @param width The video width for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_VIDEO_WIDTH
+         */
+        public T setVideoWidth(int width) {
+            mValues.put(Programs.COLUMN_VIDEO_WIDTH, width);
+            return (T) this;
+        }
+
+        /**
+         * Sets the video height of the program.
+         *
+         * @param height The video height for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_VIDEO_HEIGHT
+         */
+        public T setVideoHeight(int height) {
+            mValues.put(Programs.COLUMN_VIDEO_HEIGHT, height);
+            return (T) this;
+        }
+
+        /**
+         * Sets the content ratings for this program.
+         *
+         * @param contentRatings An array of {@link TvContentRating} that apply to this program
+         *                       which will be flattened to a String to store in a database.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_CONTENT_RATING
+         */
+        public T setContentRatings(TvContentRating[] contentRatings) {
+            mValues.put(Programs.COLUMN_CONTENT_RATING,
+                    TvContractUtils.contentRatingsToString(contentRatings));
+            return (T) this;
+        }
+
+        /**
+         * Sets the large poster art of the program.
+         *
+         * @param posterArtUri The poster art URI for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_POSTER_ART_URI
+         */
+        public T setPosterArtUri(Uri posterArtUri) {
+            mValues.put(Programs.COLUMN_POSTER_ART_URI,
+                    posterArtUri == null ? null : posterArtUri.toString());
+            return (T) this;
+        }
+
+        /**
+         * Sets a small thumbnail of the program.
+         *
+         * @param thumbnailUri The thumbnail URI for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_THUMBNAIL_URI
+         */
+        public T setThumbnailUri(Uri thumbnailUri) {
+            mValues.put(Programs.COLUMN_THUMBNAIL_URI,
+                    thumbnailUri == null ? null : thumbnailUri.toString());
+            return (T) this;
+        }
+
+        /**
+         * Sets the genres of the program.
+         *
+         * @param genres An array of {@link Programs.Genres} that apply to the program which will be
+         *               flattened to a String to store in a database.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_CANONICAL_GENRE
+         */
+        public T setCanonicalGenres(@Genre String[] genres) {
+            mValues.put(Programs.COLUMN_CANONICAL_GENRE, Programs.Genres.encode(genres));
+            return (T) this;
+        }
+
+        /**
+         * Sets the internal provider data for the program as raw bytes.
+         *
+         * @param data The internal provider data for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_DATA
+         */
+        public T setInternalProviderData(byte[] data) {
+            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_DATA, data);
+            return (T) this;
+        }
+
+        /**
+         * Sets the available audio languages for this program as an array of strings.
+         *
+         * @param audioLanguages An array of audio languages, in ISO 639-1 or 639-2/T codes, that
+         *                       apply to this program which will be stored in a database.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public T setAudioLanguages(String[] audioLanguages) {
+            mValues.put(ProgramColumns.COLUMN_AUDIO_LANGUAGE,
+                    TvContractUtils.audioLanguagesToString(audioLanguages));
+            return (T) this;
+        }
+
+        /**
+         * Sets whether this channel can be searched for in other applications.
+         *
+         * @param searchable Whether the program is searchable or not.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_SEARCHABLE
+         */
+        public T setSearchable(boolean searchable) {
+            mValues.put(Programs.COLUMN_SEARCHABLE, searchable ? IS_SEARCHABLE : 0);
+            return (T) this;
+        }
+
+        /**
+         * Sets the internal provider flag1 for the program.
+         *
+         * @param flag The first internal provider flag for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG1
+         */
+        public T setInternalProviderFlag1(long flag) {
+            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1, flag);
+            return (T) this;
+        }
+
+        /**
+         * Sets the internal provider flag2 for the program.
+         *
+         * @param flag The second internal provider flag for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG2
+         */
+        public T setInternalProviderFlag2(long flag) {
+            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2, flag);
+            return (T) this;
+        }
+
+        /**
+         * Sets the internal provider flag3 for the program.
+         *
+         * @param flag The third internal provider flag for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG3
+         */
+        public T setInternalProviderFlag3(long flag) {
+            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3, flag);
+            return (T) this;
+        }
+
+        /**
+         * Sets the internal provider flag4 for the program.
+         *
+         * @param flag The forth internal provider flag for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG4
+         */
+        public T setInternalProviderFlag4(long flag) {
+            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4, flag);
+            return (T) this;
+        }
+
+        /**
+         * Sets the review rating score style used for {@link #setReviewRating}.
+         *
+         * @param reviewRatingStyle The reviewing rating style for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         *
+         * @see Programs#COLUMN_REVIEW_RATING_STYLE
+         * @see Programs#REVIEW_RATING_STYLE_STARS
+         * @see Programs#REVIEW_RATING_STYLE_THUMBS_UP_DOWN
+         * @see Programs#REVIEW_RATING_STYLE_PERCENTAGE
+         */
+        public T setReviewRatingStyle(@ReviewRatingStyle int reviewRatingStyle) {
+            mValues.put(ProgramColumns.COLUMN_REVIEW_RATING_STYLE, reviewRatingStyle);
+            return (T) this;
+        }
+
+        /**
+         * Sets the review rating score for this program.
+         *
+         * <p>The format of the value is dependent on the review rating style. If the style is
+         * based on "stars", the value should be a real number between 0.0 and 5.0. (e.g. "4.5")
+         * If the style is based on "thumbs up/down", the value should be two integers, one for
+         * thumbs-up count and the other for thumbs-down count, with a comma between them.
+         * (e.g. "200,40") If the style is base on "percentage", the value should be a
+         * real number between 0 and 100. (e.g. "99.9")
+         *
+         * @param reviewRating The value of the review rating for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         *
+         * @see Programs#COLUMN_REVIEW_RATING
+         * @see Programs#COLUMN_REVIEW_RATING_STYLE
+         * @see Programs#REVIEW_RATING_STYLE_STARS
+         * @see Programs#REVIEW_RATING_STYLE_THUMBS_UP_DOWN
+         * @see Programs#REVIEW_RATING_STYLE_PERCENTAGE
+         */
+        public T setReviewRating(String reviewRating) {
+            mValues.put(ProgramColumns.COLUMN_REVIEW_RATING, reviewRating);
+            return (T) this;
+        }
+
+        /**
+         * Sets a custom name for the season, if applicable.
+         *
+         * @param seasonTitle The season title for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_SEASON_TITLE
+         */
+        public T setSeasonTitle(String seasonTitle) {
+            mValues.put(ProgramColumns.COLUMN_SEASON_TITLE, seasonTitle);
+            return (T) this;
+        }
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/Channel.java b/tv-provider/src/android/support/media/tv/Channel.java
new file mode 100644
index 0000000..9b13e42
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/Channel.java
@@ -0,0 +1,954 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.TvContractCompat.Channels.ServiceType;
+import android.support.media.tv.TvContractCompat.Channels.Type;
+import android.support.media.tv.TvContractCompat.Channels.VideoFormat;
+
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+
+/**
+ * A convenience class to access {@link TvContractCompat.Channels} entries in the system content
+ * provider.
+ *
+ * <p>This class makes it easy to insert or retrieve a channel from the system content provider,
+ * which is defined in {@link TvContractCompat}.
+ *
+ * <p>Usage example when inserting a channel:
+ * <pre>
+ * Channel channel = new Channel.Builder()
+ *         .setDisplayName("Channel Name")
+ *         .setDescription("Channel description")
+ *         .setType(Channels.TYPE_PREVIEW)
+ *         // Set more attributes...
+ *         .build();
+ * Uri channelUri = getContentResolver().insert(Channels.CONTENT_URI, channel.toContentValues());
+ * </pre>
+ *
+ * <p>Usage example when retrieving a channel:
+ * <pre>
+ * Channel channel;
+ * try (Cursor cursor = resolver.query(channelUri, null, null, null, null)) {
+ *     if (cursor != null && cursor.getCount() != 0) {
+ *         cursor.moveToNext();
+ *         channel = Channel.fromCursor(cursor);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Usage example when updating an existing channel:
+ * <pre>
+ * Channel updatedChannel = new Channel.Builder(channel)
+ *         .setDescription("New channel description")
+ *         .build();
+ * getContentResolver().update(TvContractCompat.buildChannelUri(updatedChannel.getId()),
+ *         updatedChannel.toContentValues(), null, null);
+ * </pre>
+ *
+ * <p>Usage example when deleting a channel:
+ * <pre>
+ * getContentResolver().delete(
+ *         TvContractCompat.buildChannelUri(existingChannel.getId()), null, null);
+ * </pre>
+ */
+@TargetApi(21)
+public final class Channel {
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String[] PROJECTION = getProjection();
+
+    private static final long INVALID_CHANNEL_ID = -1;
+    private static final int INVALID_INT_VALUE = -1;
+    private static final int IS_SEARCHABLE = 1;
+    private static final int IS_TRANSIENT = 1;
+    private static final int IS_BROWSABLE = 1;
+    private static final int IS_SYSTEM_APPROVED = 1;
+    private static final int IS_LOCKED = 1;
+
+    private ContentValues mValues;
+
+    private Channel(Builder builder) {
+        mValues = builder.mValues;
+    }
+
+    /**
+     * @return The value of {@link Channels#_ID} for the channel.
+     */
+    public long getId() {
+        Long l = mValues.getAsLong(Channels._ID);
+        return l == null ? INVALID_CHANNEL_ID : l;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_PACKAGE_NAME} for the channel.
+     */
+    public String getPackageName() {
+        return mValues.getAsString(Channels.COLUMN_PACKAGE_NAME);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INPUT_ID} for the channel.
+     */
+    public String getInputId() {
+        return mValues.getAsString(Channels.COLUMN_INPUT_ID);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_TYPE} for the channel.
+     */
+    public @Type String getType() {
+        return mValues.getAsString(Channels.COLUMN_TYPE);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_DISPLAY_NUMBER} for the channel.
+     */
+    public String getDisplayNumber() {
+        return mValues.getAsString(Channels.COLUMN_DISPLAY_NUMBER);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_DISPLAY_NAME} for the channel.
+     */
+    public String getDisplayName() {
+        return mValues.getAsString(Channels.COLUMN_DISPLAY_NAME);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_DESCRIPTION} for the channel.
+     */
+    public String getDescription() {
+        return mValues.getAsString(Channels.COLUMN_DESCRIPTION);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_VIDEO_FORMAT} for the channel.
+     */
+    public @VideoFormat String getVideoFormat() {
+        return mValues.getAsString(Channels.COLUMN_VIDEO_FORMAT);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_ORIGINAL_NETWORK_ID} for the channel.
+     */
+    public int getOriginalNetworkId() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_ORIGINAL_NETWORK_ID);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_TRANSPORT_STREAM_ID} for the channel.
+     */
+    public int getTransportStreamId() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_TRANSPORT_STREAM_ID);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_SERVICE_ID} for the channel.
+     */
+    public int getServiceId() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_SERVICE_ID);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_TEXT} for the channel.
+     */
+    public String getAppLinkText() {
+        return mValues.getAsString(Channels.COLUMN_APP_LINK_TEXT);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_COLOR} for the channel.
+     */
+    public int getAppLinkColor() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_APP_LINK_COLOR);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_ICON_URI} for the channel.
+     */
+    public Uri getAppLinkIconUri() {
+        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_ICON_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_POSTER_ART_URI} for the channel.
+     */
+    public Uri getAppLinkPosterArtUri() {
+        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_POSTER_ART_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_INTENT_URI} for the channel.
+     */
+    public Uri getAppLinkIntentUri() {
+        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_INTENT_URI} for the program.
+     */
+    public Intent getAppLinkIntent() throws URISyntaxException {
+        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI);
+        return uri == null ? null : Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_NETWORK_AFFILIATION} for the channel.
+     */
+    public String getNetworkAffiliation() {
+        return mValues.getAsString(Channels.COLUMN_NETWORK_AFFILIATION);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_SEARCHABLE} for the channel.
+     */
+    public boolean isSearchable() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_SEARCHABLE);
+        return i == null || i == IS_SEARCHABLE;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA} for the channel.
+     */
+    public byte[] getInternalProviderDataByteArray() {
+        return mValues.getAsByteArray(Channels.COLUMN_INTERNAL_PROVIDER_DATA);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_SERVICE_TYPE} for the channel.
+     *
+     * <p>Returns {@link Channels#SERVICE_TYPE_AUDIO}, {@link Channels#SERVICE_TYPE_AUDIO_VIDEO}, or
+     * {@link Channels#SERVICE_TYPE_OTHER}.
+     */
+    public @ServiceType String getServiceType() {
+        return mValues.getAsString(Channels.COLUMN_SERVICE_TYPE);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG1} for the channel.
+     */
+    public Long getInternalProviderFlag1() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG2} for the channel.
+     */
+    public Long getInternalProviderFlag2() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG3} for the channel.
+     */
+    public Long getInternalProviderFlag3() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG4} for the channel.
+     */
+    public Long getInternalProviderFlag4() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_ID} for the channel.
+     */
+    public String getInternalProviderId() {
+        return mValues.getAsString(Channels.COLUMN_INTERNAL_PROVIDER_ID);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_TRANSIENT} for the channel.
+     */
+    public boolean isTransient() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_TRANSIENT);
+        return i != null && i == IS_TRANSIENT;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_BROWSABLE} for the channel.
+     */
+    public boolean isBrowsable() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_BROWSABLE);
+        return i != null && i == IS_BROWSABLE;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_SYSTEM_APPROVED} for the channel.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public boolean isSystemApproved() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_SYSTEM_APPROVED);
+        return i != null && i == IS_SYSTEM_APPROVED;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_LOCKED} for the channel.
+     */
+    public boolean isLocked() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_LOCKED);
+        return i != null && i == IS_LOCKED;
+    }
+
+    @Override
+    public int hashCode() {
+        return mValues.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof Channel)) {
+            return false;
+        }
+        return mValues.equals(((Channel) other).mValues);
+    }
+    @Override
+    public String toString() {
+        return "Channel{" + mValues.toString() + "}";
+    }
+
+    /**
+     * @return The fields of the Channel in the ContentValues format to be easily inserted into the
+     * TV Input Framework database.
+     */
+    public ContentValues toContentValues() {
+        return toContentValues(false);
+    }
+
+    /**
+     * Returns fields of the Channel in the ContentValues format to be easily inserted into the
+     * TV Input Framework database.
+     *
+     * @param includeProtectedFields Whether the fields protected by system is included or not.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public ContentValues toContentValues(boolean includeProtectedFields) {
+        ContentValues values = new ContentValues(mValues);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            values.remove(Channels.COLUMN_APP_LINK_COLOR);
+            values.remove(Channels.COLUMN_APP_LINK_TEXT);
+            values.remove(Channels.COLUMN_APP_LINK_ICON_URI);
+            values.remove(Channels.COLUMN_APP_LINK_POSTER_ART_URI);
+            values.remove(Channels.COLUMN_APP_LINK_INTENT_URI);
+            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1);
+            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2);
+            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3);
+            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4);
+        }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_ID);
+            values.remove(Channels.COLUMN_TRANSIENT);
+        }
+
+        if (!includeProtectedFields) {
+            values.remove(Channels.COLUMN_BROWSABLE);
+            values.remove(Channels.COLUMN_LOCKED);
+        }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !includeProtectedFields) {
+            values.remove(Channels.COLUMN_SYSTEM_APPROVED);
+        }
+        return values;
+    }
+
+    /**
+     * Creates a Channel object from a cursor including the fields defined in {@link Channels}.
+     *
+     * @param cursor A row from the TV Input Framework database.
+     * @return A channel with the values taken from the cursor.
+     */
+    public static Channel fromCursor(Cursor cursor) {
+        // TODO: Add additional API which does not use costly getColumnIndex().
+        Builder builder = new Builder();
+        int index;
+        if ((index = cursor.getColumnIndex(Channels._ID)) >= 0 && !cursor.isNull(index)) {
+            builder.setId(cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_DESCRIPTION)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setDescription(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_DISPLAY_NAME)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setDisplayName(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_DISPLAY_NUMBER)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setDisplayNumber(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_INPUT_ID)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setInputId(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_DATA)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setInternalProviderData(cursor.getBlob(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_NETWORK_AFFILIATION)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setNetworkAffiliation(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_ORIGINAL_NETWORK_ID)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setOriginalNetworkId(cursor.getInt(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_PACKAGE_NAME)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setPackageName(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_SEARCHABLE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setSearchable(cursor.getInt(index) == IS_SEARCHABLE);
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_SERVICE_ID)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setServiceId(cursor.getInt(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_SERVICE_TYPE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setServiceType(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_TRANSPORT_STREAM_ID)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setTransportStreamId(cursor.getInt(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_TYPE)) >= 0 && !cursor.isNull(index)) {
+            builder.setType(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_VIDEO_FORMAT)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setVideoFormat(cursor.getString(index));
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_BROWSABLE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setBrowsable(cursor.getInt(index) == IS_BROWSABLE);
+        }
+        if ((index = cursor.getColumnIndex(Channels.COLUMN_LOCKED)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setLocked(cursor.getInt(index) == IS_LOCKED);
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_COLOR)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAppLinkColor(cursor.getInt(index));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_ICON_URI)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAppLinkIconUri(Uri.parse(cursor.getString(index)));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_INTENT_URI)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAppLinkIntentUri(Uri.parse(cursor.getString(index)));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_POSTER_ART_URI)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAppLinkPosterArtUri(Uri.parse(cursor.getString(index)));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_TEXT)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setAppLinkText(cursor.getString(index));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag1(cursor.getLong(index));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag2(cursor.getLong(index));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag3(cursor.getLong(index));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderFlag4(cursor.getLong(index));
+            }
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_ID)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setInternalProviderId(cursor.getString(index));
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_TRANSIENT)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setTransient(cursor.getInt(index) == IS_TRANSIENT);
+            }
+            if ((index = cursor.getColumnIndex(Channels.COLUMN_SYSTEM_APPROVED)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setSystemApproved(cursor.getInt(index) == IS_SYSTEM_APPROVED);
+            }
+        }
+        return builder.build();
+    }
+
+    private static String[] getProjection() {
+        String[] baseColumns = new String[] {
+                Channels._ID,
+                Channels.COLUMN_DESCRIPTION,
+                Channels.COLUMN_DISPLAY_NAME,
+                Channels.COLUMN_DISPLAY_NUMBER,
+                Channels.COLUMN_INPUT_ID,
+                Channels.COLUMN_INTERNAL_PROVIDER_DATA,
+                Channels.COLUMN_NETWORK_AFFILIATION,
+                Channels.COLUMN_ORIGINAL_NETWORK_ID,
+                Channels.COLUMN_PACKAGE_NAME,
+                Channels.COLUMN_SEARCHABLE,
+                Channels.COLUMN_SERVICE_ID,
+                Channels.COLUMN_SERVICE_TYPE,
+                Channels.COLUMN_TRANSPORT_STREAM_ID,
+                Channels.COLUMN_TYPE,
+                Channels.COLUMN_VIDEO_FORMAT,
+                Channels.COLUMN_BROWSABLE,
+                Channels.COLUMN_LOCKED,
+        };
+        String[] marshmallowColumns = new String[] {
+                Channels.COLUMN_APP_LINK_COLOR,
+                Channels.COLUMN_APP_LINK_ICON_URI,
+                Channels.COLUMN_APP_LINK_INTENT_URI,
+                Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+                Channels.COLUMN_APP_LINK_TEXT,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG1,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG3,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG4,
+        };
+        String[] oReleaseColumns = new String[] {
+                Channels.COLUMN_INTERNAL_PROVIDER_ID,
+                Channels.COLUMN_TRANSIENT,
+                Channels.COLUMN_SYSTEM_APPROVED,
+        };
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            return CollectionUtils.concatAll(baseColumns, marshmallowColumns, oReleaseColumns);
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return CollectionUtils.concatAll(baseColumns, marshmallowColumns);
+        }
+        return baseColumns;
+    }
+
+    /**
+     * The builder class that makes it easy to chain setters to create a {@link Channel} object.
+     */
+    public static final class Builder {
+        private ContentValues mValues;
+
+        public Builder() {
+            mValues = new ContentValues();
+        }
+
+        public Builder(Channel other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        /**
+         * Sets the ID of the Channel.
+         *
+         * @param id The value of {@link Channels#_ID} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        private Builder setId(long id) {
+            mValues.put(Channels._ID, id);
+            return this;
+        }
+
+        /**
+         * Sets the package name of the Channel.
+         *
+         * @param packageName The value of {@link Channels#COLUMN_PACKAGE_NAME} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        Builder setPackageName(String packageName) {
+            mValues.put(Channels.COLUMN_PACKAGE_NAME, packageName);
+            return this;
+        }
+
+        /**
+         * Sets the input id of the Channel.
+         *
+         * @param inputId The value of {@link Channels#COLUMN_INPUT_ID} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInputId(String inputId) {
+            mValues.put(Channels.COLUMN_INPUT_ID, inputId);
+            return this;
+        }
+
+        /**
+         * Sets the broadcast standard of the Channel.
+         *
+         * @param type The value of {@link Channels#COLUMN_TYPE} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setType(@Type String type) {
+            mValues.put(Channels.COLUMN_TYPE, type);
+            return this;
+        }
+
+        /**
+         * Sets the display number of the Channel.
+         *
+         * @param displayNumber The value of {@link Channels#COLUMN_DISPLAY_NUMBER} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setDisplayNumber(String displayNumber) {
+            mValues.put(Channels.COLUMN_DISPLAY_NUMBER, displayNumber);
+            return this;
+        }
+
+        /**
+         * Sets the name to be displayed for the Channel.
+         *
+         * @param displayName The value of {@link Channels#COLUMN_DISPLAY_NAME} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setDisplayName(String displayName) {
+            mValues.put(Channels.COLUMN_DISPLAY_NAME, displayName);
+            return this;
+        }
+
+        /**
+         * Sets the description of the Channel.
+         *
+         * @param description The value of {@link Channels#COLUMN_DESCRIPTION} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setDescription(String description) {
+            mValues.put(Channels.COLUMN_DESCRIPTION, description);
+            return this;
+        }
+
+        /**
+         * Sets the video format of the Channel.
+         *
+         * @param videoFormat The value of {@link Channels#COLUMN_VIDEO_FORMAT} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setVideoFormat(@VideoFormat String videoFormat) {
+            mValues.put(Channels.COLUMN_VIDEO_FORMAT, videoFormat);
+            return this;
+        }
+
+        /**
+         * Sets the original network id of the Channel.
+         *
+         * @param originalNetworkId The value of {@link Channels#COLUMN_ORIGINAL_NETWORK_ID} for the
+         *                          channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setOriginalNetworkId(int originalNetworkId) {
+            mValues.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, originalNetworkId);
+            return this;
+        }
+
+        /**
+         * Sets the transport stream id of the Channel.
+         *
+         * @param transportStreamId The value of {@link Channels#COLUMN_TRANSPORT_STREAM_ID} for the
+         *                          channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setTransportStreamId(int transportStreamId) {
+            mValues.put(Channels.COLUMN_TRANSPORT_STREAM_ID, transportStreamId);
+            return this;
+        }
+
+        /**
+         * Sets the service id of the Channel.
+         *
+         * @param serviceId The value of {@link Channels#COLUMN_SERVICE_ID} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setServiceId(int serviceId) {
+            mValues.put(Channels.COLUMN_SERVICE_ID, serviceId);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider data of the channel.
+         *
+         * @param internalProviderData The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA}
+         *                             for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderData(byte[] internalProviderData) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA, internalProviderData);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider data of the channel.
+         *
+         * @param internalProviderData The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA}
+         *                             for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderData(String internalProviderData) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA,
+                    internalProviderData.getBytes(Charset.defaultCharset()));
+            return this;
+        }
+
+        /**
+         * Sets the text to be displayed in the App Linking card.
+         *
+         * @param appLinkText The value of {@link Channels#COLUMN_APP_LINK_TEXT} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkText(String appLinkText) {
+            mValues.put(Channels.COLUMN_APP_LINK_TEXT, appLinkText);
+            return this;
+        }
+
+        /**
+         * Sets the background color of the App Linking card.
+         *
+         * @param appLinkColor The value of {@link Channels#COLUMN_APP_LINK_COLOR} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkColor(int appLinkColor) {
+            mValues.put(Channels.COLUMN_APP_LINK_COLOR, appLinkColor);
+            return this;
+        }
+
+        /**
+         * Sets the icon to be displayed next to the text of the App Linking card.
+         *
+         * @param appLinkIconUri The value of {@link Channels#COLUMN_APP_LINK_ICON_URI} for the
+         *                       channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkIconUri(Uri appLinkIconUri) {
+            mValues.put(Channels.COLUMN_APP_LINK_ICON_URI,
+                    appLinkIconUri == null ? null : appLinkIconUri.toString());
+            return this;
+        }
+
+        /**
+         * Sets the background image of the App Linking card.
+         *
+         * @param appLinkPosterArtUri The value of {@link Channels#COLUMN_APP_LINK_POSTER_ART_URI}
+         *                            for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkPosterArtUri(Uri appLinkPosterArtUri) {
+            mValues.put(Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+                    appLinkPosterArtUri == null ? null : appLinkPosterArtUri.toString());
+            return this;
+        }
+
+        /**
+         * Sets the App Linking Intent.
+         *
+         * @param appLinkIntent The Intent to be executed when the App Linking card is selected
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkIntent(Intent appLinkIntent) {
+            return setAppLinkIntentUri(Uri.parse(appLinkIntent.toUri(Intent.URI_INTENT_SCHEME)));
+        }
+
+        /**
+         * Sets the App Linking Intent.
+         *
+         * @param appLinkIntentUri The Intent that should be executed when the App Linking card is
+         *                         selected. Use the method toUri(Intent.URI_INTENT_SCHEME) on your
+         *                         Intent to turn it into a String. See
+         *                         {@link Channels#COLUMN_APP_LINK_INTENT_URI}.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkIntentUri(Uri appLinkIntentUri) {
+            mValues.put(Channels.COLUMN_APP_LINK_INTENT_URI,
+                    appLinkIntentUri == null ? null : appLinkIntentUri.toString());
+            return this;
+        }
+
+        /**
+         * Sets the network name for the channel, which may be different from its display name.
+         *
+         * @param networkAffiliation The value of
+         * {@link Channels#COLUMN_NETWORK_AFFILIATION} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setNetworkAffiliation(String networkAffiliation) {
+            mValues.put(Channels.COLUMN_NETWORK_AFFILIATION, networkAffiliation);
+            return this;
+        }
+
+        /**
+         * Sets whether this channel can be searched for in other applications.
+         *
+         * @param searchable The value of {@link Channels#COLUMN_SEARCHABLE} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setSearchable(boolean searchable) {
+            mValues.put(Channels.COLUMN_SEARCHABLE, searchable ? IS_SEARCHABLE : 0);
+            return this;
+        }
+
+        /**
+         * Sets the type of content that will appear on this channel. This could refer to the
+         * underlying broadcast standard or refer to {@link Channels#SERVICE_TYPE_AUDIO},
+         * {@link Channels#SERVICE_TYPE_AUDIO_VIDEO}, or {@link Channels#SERVICE_TYPE_OTHER}.
+         *
+         * @param serviceType The value of {@link Channels#COLUMN_SERVICE_TYPE} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setServiceType(@ServiceType String serviceType) {
+            mValues.put(Channels.COLUMN_SERVICE_TYPE, serviceType);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider flag1 for the channel.
+         *
+         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG1} for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderFlag1(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1, flag);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider flag2 for the channel.
+         *
+         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG2} for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderFlag2(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2, flag);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider flag3 for the channel.
+         *
+         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG3} for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderFlag3(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3, flag);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider flag4 for the channel.
+         *
+         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG4} for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderFlag4(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4, flag);
+            return this;
+        }
+
+        /**
+         * Sets the internal provider ID for the channel.
+         *
+         * @param internalProviderId The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_ID}
+         *                           for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setInternalProviderId(String internalProviderId) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_ID, internalProviderId);
+            return this;
+        }
+
+        /**
+         * Sets whether this channel is transient or not.
+         *
+         * @param value The value of {@link Channels#COLUMN_TRANSIENT} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setTransient(boolean value) {
+            mValues.put(Channels.COLUMN_TRANSIENT, value ? IS_TRANSIENT : 0);
+            return this;
+        }
+
+        /**
+         * Sets whether this channel is browsable or not.
+         *
+         * @param value The value of {@link Channels#COLUMN_BROWSABLE} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public Builder setBrowsable(boolean value) {
+            mValues.put(Channels.COLUMN_BROWSABLE, value ? IS_BROWSABLE : 0);
+            return this;
+        }
+
+        /**
+         * Sets whether system approved this channel or not.
+         *
+         * @param value The value of {@link Channels#COLUMN_SYSTEM_APPROVED} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public Builder setSystemApproved(boolean value) {
+            mValues.put(Channels.COLUMN_SYSTEM_APPROVED, value ? IS_SYSTEM_APPROVED : 0);
+            return this;
+        }
+
+        /**
+         * Sets whether this channel is locked or not.
+         *
+         * @param value The value of {@link Channels#COLUMN_LOCKED} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public Builder setLocked(boolean value) {
+            mValues.put(Channels.COLUMN_LOCKED, value ? IS_LOCKED : 0);
+            return this;
+        }
+
+        /**
+         * Takes the values of the Builder object and creates a Channel object.
+         * @return Channel object with values from the Builder.
+         */
+        public Channel build() {
+            return new Channel(this);
+        }
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/ChannelLogoUtils.java b/tv-provider/src/android/support/media/tv/ChannelLogoUtils.java
new file mode 100644
index 0000000..20b79f3
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/ChannelLogoUtils.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.sqlite.SQLiteException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+/** A utility class for conveniently storing and loading channel logos. */
+@WorkerThread
+public class ChannelLogoUtils {
+    private static final String TAG = "ChannelLogoUtils";
+
+    private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000;  // 3 sec
+    private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000;  // 10 sec
+
+    /**
+     * Stores channel logo in the system content provider from the given URI. The method will try
+     * to fetch the image file and decode it into {@link Bitmap}. Once the image is successfully
+     * fetched, it will be stored in the system content provider and associated with the given
+     * channel ID.
+     *
+     * <p>The URI provided to this method can be a URL referring to a image file residing in some
+     * remote site/server, or a URI in one of the following formats:
+     *
+     *    <ul>
+     *        <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+     *        <li>android.resource ({@link android.content.ContentResolver
+     *                                     #SCHEME_ANDROID_RESOURCE})</li>
+     *        <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+     *    </ul>
+     *
+     * <p>This method should be run in a worker thread since it may require network connection,
+     * which will raise an exception if it's running in the main thread.
+     *
+     * @param context the context used to access the system content provider
+     * @param channelId the ID of the target channel with which the fetched logo should be
+     *                  associated
+     * @param logoUri the {@link Uri} of the logo file to be fetched and stored in the system
+     *                provider
+     *
+     * @return {@code true} if successfully fetched the image file referred by the give logo URI
+     *         and stored it in the system content provider, or {@code false} if failed.
+     *
+     * @see #loadChannelLogo(Context, long)
+     */
+    public static boolean storeChannelLogo(@NonNull Context context, long channelId,
+            @NonNull Uri logoUri) {
+        String scheme = logoUri.normalizeScheme().getScheme();
+        URLConnection urlConnection = null;
+        InputStream inputStream = null;
+        Bitmap fetchedLogo = null;
+        try {
+            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
+                    || ContentResolver.SCHEME_FILE.equals(scheme)
+                    || ContentResolver.SCHEME_CONTENT.equals(scheme)) {
+                // A local resource
+                inputStream = context.getContentResolver().openInputStream(logoUri);
+            } else {
+                // A remote resource, should be an valid URL.
+                urlConnection = getUrlConnection(logoUri.toString());
+                inputStream = urlConnection.getInputStream();
+            }
+            fetchedLogo = BitmapFactory.decodeStream(inputStream);
+        } catch (IOException e) {
+            Log.i(TAG, "Failed to get logo from the URI: " + logoUri + "\n", e);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    // Do nothing.
+                }
+            }
+            if (urlConnection instanceof HttpURLConnection) {
+                ((HttpURLConnection) urlConnection).disconnect();
+            }
+        }
+        return fetchedLogo != null && storeChannelLogo(context, channelId, fetchedLogo);
+    }
+
+    /**
+     * Stores the given channel logo {@link Bitmap} in the system content provider and associate
+     * it with the given channel ID.
+     *
+     * @param context the context used to access the system content provider
+     * @param channelId the ID of the target channel with which the given logo should be associated
+     * @param logo the logo image to be stored
+     *
+     * @return {@code true} if successfully stored the logo in the system content provider,
+     *         otherwise {@code false}.
+     *
+     * @see #loadChannelLogo(Context, long)
+     */
+    public static boolean storeChannelLogo(@NonNull Context context, long channelId,
+            @NonNull Bitmap logo) {
+        boolean result = false;
+        Uri localUri = TvContract.buildChannelLogoUri(channelId);
+        try (OutputStream outputStream = context.getContentResolver().openOutputStream(localUri)) {
+            result = logo.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+            outputStream.flush();
+        } catch (SQLiteException | IOException e) {
+            Log.i(TAG, "Failed to store the logo to the system content provider.\n", e);
+        }
+        return result;
+    }
+
+    /**
+     * A convenient helper method to get the channel logo associated to the given channel ID from
+     * the system content provider.
+     *
+     * @param context the context used to access the system content provider
+     * @param channelId the ID of the channel whose logo is supposed to be loaded
+     *
+     * @return the requested channel logo in {@link Bitmap}, or {@code null} if not available.
+     *
+     * @see #storeChannelLogo(Context, long, Uri)
+     * @see #storeChannelLogo(Context, long, Bitmap)
+     */
+    public static Bitmap loadChannelLogo(@NonNull Context context, long channelId) {
+        Bitmap channelLogo = null;
+        try {
+            channelLogo = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(
+                    TvContract.buildChannelLogoUri(channelId)));
+        } catch (FileNotFoundException e) {
+            // Channel logo is not found in the content provider.
+            Log.i(TAG, "Channel logo for channel (ID:" + channelId + ") not found.", e);
+        }
+        return channelLogo;
+    }
+
+    private static URLConnection getUrlConnection(String uriString) throws IOException {
+        URLConnection urlConnection = new URL(uriString).openConnection();
+        urlConnection.setConnectTimeout(CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION);
+        urlConnection.setReadTimeout(READ_TIMEOUT_MS_FOR_URLCONNECTION);
+        return urlConnection;
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/CollectionUtils.java b/tv-provider/src/android/support/media/tv/CollectionUtils.java
new file mode 100644
index 0000000..7aa1074
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/CollectionUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.RestrictTo;
+
+import java.util.Arrays;
+
+/**
+ * Static utilities for collections
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class CollectionUtils {
+    /**
+     * Returns an array with the arrays concatenated together.
+     *
+     * @see <a href="http://stackoverflow.com/a/784842/1122089">Stackoverflow answer</a> by
+     *      <a href="http://stackoverflow.com/users/40342/joachim-sauer">Joachim Sauer</a>
+     */
+    public static <T> T[] concatAll(T[] first, T[]... rest) {
+        int totalLength = first.length;
+        for (T[] array : rest) {
+            totalLength += array.length;
+        }
+        T[] result = Arrays.copyOf(first, totalLength);
+        int offset = first.length;
+        for (T[] array : rest) {
+            System.arraycopy(array, 0, result, offset, array.length);
+            offset += array.length;
+        }
+        return result;
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/PreviewProgram.java b/tv-provider/src/android/support/media/tv/PreviewProgram.java
new file mode 100644
index 0000000..3df3a74
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/PreviewProgram.java
@@ -0,0 +1,227 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;  // For javadoc gen of super class
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.media.tv.TvContractCompat.PreviewPrograms;
+import android.support.media.tv.TvContractCompat.Programs;  // For javadoc gen of super class
+import android.support.media.tv.TvContractCompat.Programs.Genres;  // For javadoc gen of super class
+
+/**
+ * A convenience class to access {@link PreviewPrograms} entries in the system content
+ * provider.
+ *
+ * <p>This class makes it easy to insert or retrieve a preview program from the system content
+ * provider, which is defined in {@link TvContractCompat}.
+ *
+ * <p>Usage example when inserting a preview program:
+ * <pre>
+ * PreviewProgram previewProgram = new PreviewProgram.Builder()
+ *         .setChannelId(channel.getId())
+ *         .setType(PreviewPrograms.TYPE_MOVIE)
+ *         .setTitle("Program Title")
+ *         .setDescription("Program Description")
+ *         .setPosterArtUri(Uri.parse("http://example.com/poster_art.png"))
+ *         // Set more attributes...
+ *         .build();
+ * Uri previewProgramUri = getContentResolver().insert(PreviewPrograms.CONTENT_URI,
+ *         previewProgram.toContentValues());
+ * </pre>
+ *
+ * <p>Usage example when retrieving a preview program:
+ * <pre>
+ * PreviewProgram previewProgram;
+ * try (Cursor cursor = resolver.query(previewProgramUri, null, null, null, null)) {
+ *     if (cursor != null && cursor.getCount() != 0) {
+ *         cursor.moveToNext();
+ *         previewProgram = PreviewProgram.fromCursor(cursor);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Usage example when updating an existing preview program:
+ * <pre>
+ * PreviewProgram updatedProgram = new PreviewProgram.Builder(previewProgram)
+ *         .setWeight(20)
+ *         .build();
+ * getContentResolver().update(TvContractCompat.buildPreviewProgramUri(updatedProgram.getId()),
+ *         updatedProgram.toContentValues(), null, null);
+ * </pre>
+ *
+ * <p>Usage example when deleting a preview program:
+ * <pre>
+ * getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(existingProgram.getId()),
+ *         null, null);
+ * </pre>
+ */
+@TargetApi(26)
+public final class PreviewProgram extends BasePreviewProgram {
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String[] PROJECTION = getProjection();
+
+    private static final long INVALID_LONG_VALUE = -1;
+    private static final int INVALID_INT_VALUE = -1;
+
+    private PreviewProgram(Builder builder) {
+        super(builder);
+    }
+
+    /**
+     * @return The value of {@link PreviewPrograms#COLUMN_CHANNEL_ID} for the program.
+     */
+    public long getChannelId() {
+        Long l = mValues.getAsLong(PreviewPrograms.COLUMN_CHANNEL_ID);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    /**
+     * @return The value of {@link PreviewPrograms#COLUMN_WEIGHT} for the program.
+     */
+    public int getWeight() {
+        Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_WEIGHT);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof PreviewProgram)) {
+            return false;
+        }
+        return mValues.equals(((PreviewProgram) other).mValues);
+    }
+
+    @Override
+    public String toString() {
+        return "PreviewProgram{" + mValues.toString() + "}";
+    }
+
+    /**
+     * @return The fields of the Program in the ContentValues format to be easily inserted into the
+     * TV Input Framework database.
+     */
+    @Override
+    public ContentValues toContentValues() {
+        return toContentValues(false);
+    }
+
+    /**
+     * Returns fields of the PreviewProgram in the ContentValues format to be easily inserted
+     * into the TV Input Framework database.
+     *
+     * @param includeProtectedFields Whether the fields protected by system is included or not.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public ContentValues toContentValues(boolean includeProtectedFields) {
+        ContentValues values = super.toContentValues(includeProtectedFields);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            values.remove(PreviewPrograms.COLUMN_CHANNEL_ID);
+            values.remove(PreviewPrograms.COLUMN_WEIGHT);
+        }
+        return values;
+    }
+
+    /**
+     * Creates a Program object from a cursor including the fields defined in
+     * {@link PreviewPrograms}.
+     *
+     * @param cursor A row from the TV Input Framework database.
+     * @return A Program with the values taken from the cursor.
+     */
+    public static PreviewProgram fromCursor(Cursor cursor) {
+        // TODO: Add additional API which does not use costly getColumnIndex().
+        Builder builder = new Builder();
+        BasePreviewProgram.setFieldsFromCursor(cursor, builder);
+        int index;
+        if ((index = cursor.getColumnIndex(PreviewPrograms.COLUMN_CHANNEL_ID)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setChannelId(cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(PreviewPrograms.COLUMN_WEIGHT)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setWeight(cursor.getInt(index));
+        }
+        return builder.build();
+    }
+
+    private static String[] getProjection() {
+        String[] oColumns = new String[] {
+                PreviewPrograms.COLUMN_CHANNEL_ID,
+                PreviewPrograms.COLUMN_WEIGHT,
+        };
+        return CollectionUtils.concatAll(BasePreviewProgram.PROJECTION, oColumns);
+    }
+
+    /**
+     * This Builder class simplifies the creation of a {@link PreviewProgram} object.
+     */
+    public static final class Builder extends BasePreviewProgram.Builder<Builder> {
+
+        /**
+         * Creates a new Builder object.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a new Builder object with values copied from another Program.
+         * @param other The Program you're copying from.
+         */
+        public Builder(PreviewProgram other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        /**
+         * Sets the ID of the {@link Channel} that contains this program.
+         *
+         * @param channelId The value of {@link PreviewPrograms#COLUMN_CHANNEL_ID for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setChannelId(long channelId) {
+            mValues.put(PreviewPrograms.COLUMN_CHANNEL_ID, channelId);
+            return this;
+        }
+
+        /**
+         * Sets the weight of the preview program within the channel.
+         *
+         * @param weight The value of {@link PreviewPrograms#COLUMN_WEIGHT} for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setWeight(int weight) {
+            mValues.put(PreviewPrograms.COLUMN_WEIGHT, weight);
+            return this;
+        }
+
+        /**
+         * @return A new Program with values supplied by the Builder.
+         */
+        public PreviewProgram build() {
+            return new PreviewProgram(this);
+        }
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/Program.java b/tv-provider/src/android/support/media/tv/Program.java
new file mode 100644
index 0000000..4e3bd7a
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/Program.java
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;  // For javadoc gen of super class
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.media.tv.TvContractCompat.Programs;
+
+/**
+ * A convenience class to access {@link TvContractCompat.Programs} entries in the system content
+ * provider.
+ *
+ * <p>This class makes it easy to insert or retrieve a program from the system content provider,
+ * which is defined in {@link TvContractCompat}.
+ *
+ * <p>Usage example when inserting a program:
+ * <pre>
+ * Program program = new Program.Builder()
+ *         .setChannelId(channel.getId())
+ *         .setTitle("Program Title")
+ *         .setDescription("Program Description")
+ *         .setPosterArtUri(Uri.parse("http://example.com/poster_art.png"))
+ *         // Set more attributes...
+ *         .build();
+ * Uri programUri = getContentResolver().insert(Programs.CONTENT_URI, program.toContentValues());
+ * </pre>
+ *
+ * <p>Usage example when retrieving a program:
+ * <pre>
+ * Program program;
+ * try (Cursor cursor = resolver.query(programUri, null, null, null, null)) {
+ *     if (cursor != null && cursor.getCount() != 0) {
+ *         cursor.moveToNext();
+ *         program = Program.fromCursor(cursor);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Usage example when updating an existing program:
+ * <pre>
+ * Program updatedProgram = new Program.Builder(program)
+ *         .setEndTimeUtcMillis(newProgramEndTime)
+ *         .build();
+ * getContentResolver().update(TvContractCompat.buildProgramUri(updatedProgram.getId()),
+ *         updatedProgram.toContentValues(), null, null);
+ * </pre>
+ *
+ * <p>Usage example when deleting a program:
+ * <pre>
+ * getContentResolver().delete(TvContractCompat.buildProgramUri(existingProgram.getId()),
+ *         null, null);
+ * </pre>
+ */
+@TargetApi(21)
+public final class Program extends BaseProgram implements Comparable<Program> {
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String[] PROJECTION = getProjection();
+
+    private static final long INVALID_LONG_VALUE = -1;
+    private static final int IS_RECORDING_PROHIBITED = 1;
+
+    private Program(Builder builder) {
+        super(builder);
+    }
+
+    /**
+     * @return The value of {@link Programs#COLUMN_CHANNEL_ID} for the program.
+     */
+    public long getChannelId() {
+        Long l = mValues.getAsLong(Programs.COLUMN_CHANNEL_ID);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    /**
+     * @return The value of {@link Programs#COLUMN_START_TIME_UTC_MILLIS} for the program.
+     */
+    public long getStartTimeUtcMillis() {
+        Long l = mValues.getAsLong(Programs.COLUMN_START_TIME_UTC_MILLIS);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    /**
+     * @return The value of {@link Programs#COLUMN_END_TIME_UTC_MILLIS} for the program.
+     */
+    public long getEndTimeUtcMillis() {
+        Long l = mValues.getAsLong(Programs.COLUMN_END_TIME_UTC_MILLIS);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    /**
+     * @return The value of {@link Programs#COLUMN_BROADCAST_GENRE} for the program.
+     */
+    public String[] getBroadcastGenres() {
+        return Programs.Genres.decode(mValues.getAsString(Programs.COLUMN_BROADCAST_GENRE));
+    }
+
+    /**
+     * @return The value of {@link Programs#COLUMN_RECORDING_PROHIBITED} for the program.
+     */
+    public boolean isRecordingProhibited() {
+        Integer i = mValues.getAsInteger(Programs.COLUMN_RECORDING_PROHIBITED);
+        return i != null && i == IS_RECORDING_PROHIBITED;
+    }
+
+    @Override
+    public int hashCode() {
+        return mValues.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof Program)) {
+            return false;
+        }
+        return mValues.equals(((Program) other).mValues);
+    }
+
+    /**
+     * @param other The program you're comparing to.
+     * @return The chronological order of the programs.
+     */
+    @Override
+    public int compareTo(@NonNull Program other) {
+        return Long.compare(mValues.getAsLong(Programs.COLUMN_START_TIME_UTC_MILLIS),
+                other.mValues.getAsLong(Programs.COLUMN_START_TIME_UTC_MILLIS));
+    }
+
+    @Override
+    public String toString() {
+        return "Program{" + mValues.toString() + "}";
+    }
+
+    /**
+     * @return The fields of the Program in the ContentValues format to be easily inserted into the
+     * TV Input Framework database.
+     */
+    @Override
+    public ContentValues toContentValues() {
+        ContentValues values = super.toContentValues();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            values.remove(Programs.COLUMN_RECORDING_PROHIBITED);
+        }
+        return values;
+    }
+
+    /**
+     * Creates a Program object from a cursor including the fields defined in {@link Programs}.
+     *
+     * @param cursor A row from the TV Input Framework database.
+     * @return A Program with the values taken from the cursor.
+     */
+    public static Program fromCursor(Cursor cursor) {
+        // TODO: Add additional API which does not use costly getColumnIndex().
+        Builder builder = new Builder();
+        BaseProgram.setFieldsFromCursor(cursor, builder);
+        int index;
+        if ((index = cursor.getColumnIndex(Programs.COLUMN_CHANNEL_ID)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setChannelId(cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(Programs.COLUMN_BROADCAST_GENRE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setBroadcastGenres(Programs.Genres.decode(
+                    cursor.getString(index)));
+        }
+        if ((index = cursor.getColumnIndex(Programs.COLUMN_START_TIME_UTC_MILLIS)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setStartTimeUtcMillis(cursor.getLong(index));
+        }
+        if ((index = cursor.getColumnIndex(Programs.COLUMN_END_TIME_UTC_MILLIS)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setEndTimeUtcMillis(cursor.getLong(index));
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if ((index = cursor.getColumnIndex(Programs.COLUMN_RECORDING_PROHIBITED)) >= 0
+                    && !cursor.isNull(index)) {
+                builder.setRecordingProhibited(cursor.getInt(index) == IS_RECORDING_PROHIBITED);
+            }
+        }
+        return builder.build();
+    }
+
+    private static String[] getProjection() {
+        String[] baseColumns = new String[] {
+                Programs.COLUMN_CHANNEL_ID,
+                Programs.COLUMN_BROADCAST_GENRE,
+                Programs.COLUMN_START_TIME_UTC_MILLIS,
+                Programs.COLUMN_END_TIME_UTC_MILLIS,
+        };
+        String[] nougatColumns = new String[] {
+                Programs.COLUMN_RECORDING_PROHIBITED
+        };
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return CollectionUtils.concatAll(BaseProgram.PROJECTION, baseColumns, nougatColumns);
+        } else {
+            return CollectionUtils.concatAll(BaseProgram.PROJECTION, baseColumns);
+        }
+    }
+
+    /**
+     * This Builder class simplifies the creation of a {@link Program} object.
+     */
+    public static class Builder extends BaseProgram.Builder<Builder> {
+
+        /**
+         * Creates a new Builder object.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a new Builder object with values copied from another Program.
+         * @param other The Program you're copying from.
+         */
+        public Builder(Program other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        /**
+         * Sets the ID of the {@link Channel} that contains this program.
+         *
+         * @param channelId The value of {@link Programs#COLUMN_CHANNEL_ID for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setChannelId(long channelId) {
+            mValues.put(Programs.COLUMN_CHANNEL_ID, channelId);
+            return this;
+        }
+
+        /**
+         * Sets the time when the program is going to begin in milliseconds since the epoch.
+         *
+         * @param startTimeUtcMillis The value of {@link Programs#COLUMN_START_TIME_UTC_MILLIS} for
+         *                           the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setStartTimeUtcMillis(long startTimeUtcMillis) {
+            mValues.put(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeUtcMillis);
+            return this;
+        }
+
+        /**
+         * Sets the time when this program is going to end in milliseconds since the epoch.
+         *
+         * @param endTimeUtcMillis The value of {@link Programs#COLUMN_END_TIME_UTC_MILLIS} for the
+         *                         program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setEndTimeUtcMillis(long endTimeUtcMillis) {
+            mValues.put(Programs.COLUMN_END_TIME_UTC_MILLIS, endTimeUtcMillis);
+            return this;
+        }
+
+        /**
+         * Sets the broadcast-specified genres of the program.
+         *
+         * @param genres Array of genres that apply to the program based on the broadcast standard
+         *               which will be flattened to a String to store in a database.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see Programs#COLUMN_BROADCAST_GENRE
+         */
+        public Builder setBroadcastGenres(String[] genres) {
+            mValues.put(Programs.COLUMN_BROADCAST_GENRE, Programs.Genres.encode(genres));
+            return this;
+        }
+
+        /**
+         * Sets whether this program cannot be recorded.
+         *
+         * @param prohibited The value of {@link Programs#COLUMN_RECORDING_PROHIBITED} for the
+         *                   program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setRecordingProhibited(boolean prohibited) {
+            mValues.put(Programs.COLUMN_RECORDING_PROHIBITED,
+                    prohibited ? IS_RECORDING_PROHIBITED : 0);
+            return this;
+        }
+
+        /**
+         * @return A new Program with values supplied by the Builder.
+         */
+        public Program build() {
+            return new Program(this);
+        }
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/TvContractCompat.java b/tv-provider/src/android/support/media/tv/TvContractCompat.java
new file mode 100644
index 0000000..5773061
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/TvContractCompat.java
@@ -0,0 +1,2885 @@
+/*
+ * 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.
+ */
+
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.StringDef;
+import android.support.media.tv.TvContractCompat.Programs.Genres;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The contract between the TV provider and applications. Contains definitions for the supported
+ * URIs and columns.
+ * <h3>Overview</h3>
+ *
+ * <p>TvContract defines a basic database of TV content metadata such as channel and program
+ * information. The information is stored in {@link Channels} and {@link Programs} tables.
+ *
+ * <ul>
+ *     <li>A row in the {@link Channels} table represents information about a TV channel. The data
+ *         format can vary greatly from standard to standard or according to service provider, thus
+ *         the columns here are mostly comprised of basic entities that are usually seen to users
+ *         regardless of standard such as channel number and name.</li>
+ *     <li>A row in the {@link Programs} table represents a set of data describing a TV program such
+ *         as program title and start time.</li>
+ * </ul>
+ */
+public final class TvContractCompat {
+    /** The authority for the TV provider. */
+    public static final String AUTHORITY = "android.media.tv";
+
+    /**
+     * Permission to read TV listings. This is required to read all the TV channel and program
+     * information available on the system.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS";
+
+    private static final String PATH_CHANNEL = "channel";
+    private static final String PATH_PROGRAM = "program";
+    private static final String PATH_RECORDED_PROGRAM = "recorded_program";
+    private static final String PATH_PREVIEW_PROGRAM = "preview_program";
+    private static final String PATH_WATCH_NEXT_PROGRAM = "watch_next_program";
+    private static final String PATH_PASSTHROUGH = "passthrough";
+
+    /**
+     * Broadcast Action: sent when an application requests the system to make the given channel
+     * browsable.  The operation is performed in the background without user interaction. This
+     * is only relevant to channels with {@link Channels#TYPE_PREVIEW} type.
+     *
+     * <p>The intent must contain the following bundle parameters:
+     * <ul>
+     *     <li>{@link #EXTRA_CHANNEL_ID}: ID for the {@link Channels#TYPE_PREVIEW} channel as a long
+     *     integer.</li>
+     *     <li>{@link #EXTRA_PACKAGE_NAME}: the package name of the requesting application.</li>
+     * </ul>
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String ACTION_CHANNEL_BROWSABLE_REQUESTED =
+            "android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED";
+
+    /**
+     * Activity Action: sent by an application telling the system to make the given channel
+     * browsable with user interaction. The system may show UI to ask user to approve the channel.
+     * This is only relevant to channels with {@link Channels#TYPE_PREVIEW} type. Use
+     * {@link Activity#startActivityForResult} to get the result of the request.
+     *
+     * <p>The intent must contain the following bundle parameters:
+     * <ul>
+     *     <li>{@link #EXTRA_CHANNEL_ID}: ID for the {@link Channels#TYPE_PREVIEW} channel as a long
+     *     integer.</li>
+     * </ul>
+     */
+    public static final String ACTION_REQUEST_CHANNEL_BROWSABLE =
+            "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
+
+    /**
+     * Broadcast Action: sent by the system to tell the target TV input that one of its preview
+     * program's browsable state is disabled, i.e., it will no longer be shown to users, which, for
+     * example, might be a result of users' interaction with UI. The input is expected to delete the
+     * preview program from the content provider.
+     *
+     * <p>The intent must contain the following bundle parameter:
+     * <ul>
+     *     <li>{@link #EXTRA_PREVIEW_PROGRAM_ID}: the disabled preview program ID.</li>
+     * </ul>
+     */
+    public static final String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED =
+            "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+
+    /**
+     * Broadcast Action: sent by the system to tell the target TV input that one of its "watch next"
+     * program's browsable state is disabled, i.e., it will no longer be shown to users, which, for
+     * example, might be a result of users' interaction with UI. The input is expected to delete the
+     * "watch next" program from the content provider.
+     *
+     * <p>The intent must contain the following bundle parameter:
+     * <ul>
+     *     <li>{@link #EXTRA_WATCH_NEXT_PROGRAM_ID}: the disabled "watch next" program ID.</li>
+     * </ul>
+     */
+    public static final String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED =
+            "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
+
+    /**
+     * Broadcast Action: sent by the system to tell the target TV input that one of its existing
+     * preview programs is added to the watch next programs table by user.
+     *
+     * <p>The intent must contain the following bundle parameters:
+     * <ul>
+     *     <li>{@link #EXTRA_PREVIEW_PROGRAM_ID}: the ID of the existing preview program.</li>
+     *     <li>{@link #EXTRA_WATCH_NEXT_PROGRAM_ID}: the ID of the new watch next program.</li>
+     * </ul>
+     */
+    public static final String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT =
+            "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
+
+    /**
+     * Broadcast Action: sent to the target TV input after it is first installed to notify the input
+     * to initialize its channels and programs to the system content provider.
+     *
+     * <p>Note that this intent is sent only on devices with
+     * {@link android.content.pm.PackageManager#FEATURE_LEANBACK} enabled. Besides that, in order
+     * to receive this intent, the target TV input must:
+     * <ul>
+     *     <li>Declare a broadcast receiver for this intent in its
+     *         <code>AndroidManifest.xml</code>.</li>
+     *     <li>Declare appropriate permissions to write channel and program data in its
+     *         <code>AndroidManifest.xml</code>.</li>
+     * </ul>
+     */
+    public static final String ACTION_INITIALIZE_PROGRAMS =
+            "android.media.tv.action.INITIALIZE_PROGRAMS";
+
+    /**
+     * The key for a bundle parameter containing a channel ID as a long integer
+     */
+    public static final String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+
+    /**
+     * The key for a bundle parameter containing a package name as a string.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
+
+    /** The key for a bundle parameter containing a program ID as a long integer. */
+    public static final String EXTRA_PREVIEW_PROGRAM_ID =
+            "android.media.tv.extra.PREVIEW_PROGRAM_ID";
+
+    /** The key for a bundle parameter containing a watch next program ID as a long integer. */
+    public static final String EXTRA_WATCH_NEXT_PROGRAM_ID =
+            "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
+
+    /**
+     * The method name to get existing columns in the given table of the specified content provider.
+     *
+     * <p>The method caller must provide the following parameter:
+     * <ul>
+     *     <li>{@code arg}: The content URI of the target table as a {@link String}.</li>
+     * </ul>
+
+     * <p>On success, the returned {@link android.os.Bundle} will include existing column names
+     * with the key {@link #EXTRA_EXISTING_COLUMN_NAMES}. Otherwise, the return value will be
+     * {@code null}.
+     *
+     * @see ContentResolver#call(Uri, String, String, Bundle)
+     * @see #EXTRA_EXISTING_COLUMN_NAMES
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String METHOD_GET_COLUMNS = "get_columns";
+
+    /**
+     * The method name to add a new column in the given table of the specified content provider.
+     *
+     * <p>The method caller must provide the following parameter:
+     * <ul>
+     *     <li>{@code arg}: The content URI of the target table as a {@link String}.</li>
+     *     <li>{@code extra}: Name, data type, and default value of the new column in a Bundle:
+     *         <ul>
+     *             <li>{@link #EXTRA_COLUMN_NAME} the column name as a {@link String}.</li>
+     *             <li>{@link #EXTRA_DATA_TYPE} the data type as a {@link String}.</li>
+     *             <li>{@link #EXTRA_DEFAULT_VALUE} the default value as a {@link String}.
+     *                 (optional)</li>
+     *         </ul>
+     *     </li>
+     * </ul>
+     *
+     * <p>On success, the returned {@link android.os.Bundle} will include current colum names after
+     * the addition operation with the key {@link #EXTRA_EXISTING_COLUMN_NAMES}. Otherwise, the
+     * return value will be {@code null}.
+     *
+     * @see ContentResolver#call(Uri, String, String, Bundle)
+     * @see #EXTRA_COLUMN_NAME
+     * @see #EXTRA_DATA_TYPE
+     * @see #EXTRA_DEFAULT_VALUE
+     * @see #EXTRA_EXISTING_COLUMN_NAMES
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String METHOD_ADD_COLUMN = "add_column";
+
+    /**
+     * The key for a returned {@link Bundle} value containing existing column names in the given
+     * table as an {@link ArrayList} of {@link String}.
+     *
+     * @see #METHOD_GET_COLUMNS
+     * @see #METHOD_ADD_COLUMN
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String EXTRA_EXISTING_COLUMN_NAMES =
+            "android.media.tv.extra.EXISTING_COLUMN_NAMES";
+
+    /**
+     * The key for a {@link Bundle} parameter containing the new column name to be added in the
+     * given table as a non-empty {@link CharSequence}.
+     *
+     * @see #METHOD_ADD_COLUMN
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String EXTRA_COLUMN_NAME = "android.media.tv.extra.COLUMN_NAME";
+
+    /**
+     * The key for a {@link Bundle} parameter containing the data type of the new column to be added
+     * in the given table as a non-empty {@link CharSequence}, which should be one of the following
+     * values: {@code "TEXT"}, {@code "INTEGER"}, {@code "REAL"}, or {@code "BLOB"}.
+     *
+     * @see #METHOD_ADD_COLUMN
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String EXTRA_DATA_TYPE = "android.media.tv.extra.DATA_TYPE";
+
+    /**
+     * The key for a {@link Bundle} parameter containing the default value of the new column to be
+     * added in the given table as a {@link CharSequence}, which represents a valid default value
+     * according to the data type provided with {@link #EXTRA_DATA_TYPE}.
+     *
+     * @see #METHOD_ADD_COLUMN
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String EXTRA_DEFAULT_VALUE = "android.media.tv.extra.DEFAULT_VALUE";
+
+    /**
+     * An optional query, update or delete URI parameter that allows the caller to specify TV input
+     * ID to filter channels.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARAM_INPUT = "input";
+
+    /**
+     * An optional query, update or delete URI parameter that allows the caller to specify channel
+     * ID to filter programs.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARAM_CHANNEL = "channel";
+
+    /**
+     * An optional query, update or delete URI parameter that allows the caller to specify start
+     * time (in milliseconds since the epoch) to filter programs.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARAM_START_TIME = "start_time";
+
+    /**
+     * An optional query, update or delete URI parameter that allows the caller to specify end time
+     * (in milliseconds since the epoch) to filter programs.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARAM_END_TIME = "end_time";
+
+    /**
+     * A query, update or delete URI parameter that allows the caller to operate on all or
+     * browsable-only channels. If set to "true", the rows that contain non-browsable channels are
+     * not affected.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARAM_BROWSABLE_ONLY = "browsable_only";
+
+    /**
+     * A optional query, update or delete URI parameter that allows the caller to specify canonical
+     * genre to filter programs.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String PARAM_CANONICAL_GENRE = "canonical_genre";
+
+    /**
+     * Builds an ID that uniquely identifies a TV input service.
+     *
+     * @param name The {@link ComponentName} of the TV input service to build ID for.
+     * @return the ID for the given TV input service.
+     */
+    public static String buildInputId(ComponentName name) {
+        return TvContract.buildInputId(name);
+    }
+
+    /**
+     * Builds a URI that points to a specific channel.
+     *
+     * @param channelId The ID of the channel to point to.
+     */
+    public static Uri buildChannelUri(long channelId) {
+        return TvContract.buildChannelUri(channelId);
+    }
+
+    /**
+     * Build a special channel URI intended to be used with pass-through inputs. (e.g. HDMI)
+     *
+     * @param inputId The ID of the pass-through input to build a channels URI for.
+     */
+    public static Uri buildChannelUriForPassthroughInput(String inputId) {
+        return TvContract.buildChannelUriForPassthroughInput(inputId);
+    }
+
+    /**
+     * Builds a URI that points to a channel logo. See {@link Channels.Logo}.
+     *
+     * @param channelId The ID of the channel whose logo is pointed to.
+     */
+    public static Uri buildChannelLogoUri(long channelId) {
+        return TvContract.buildChannelLogoUri(channelId);
+    }
+
+    /**
+     * Builds a URI that points to a channel logo. See {@link Channels.Logo}.
+     *
+     * @param channelUri The URI of the channel whose logo is pointed to.
+     */
+    public static Uri buildChannelLogoUri(Uri channelUri) {
+        return TvContract.buildChannelLogoUri(channelUri);
+    }
+
+    /**
+     * Builds a URI that points to all channels from a given TV input.
+     *
+     * @param inputId The ID of the TV input to build a channels URI for. If {@code null}, builds a
+     *            URI for all the TV inputs.
+     */
+    public static Uri buildChannelsUriForInput(@Nullable String inputId) {
+        return TvContract.buildChannelsUriForInput(inputId);
+    }
+
+    /**
+     * Builds a URI that points to a specific program.
+     *
+     * @param programId The ID of the program to point to.
+     */
+    public static Uri buildProgramUri(long programId) {
+        return TvContract.buildProgramUri(programId);
+    }
+
+    /**
+     * Builds a URI that points to all programs on a given channel.
+     *
+     * @param channelId The ID of the channel to return programs for.
+     */
+    public static Uri buildProgramsUriForChannel(long channelId) {
+        return TvContract.buildProgramsUriForChannel(channelId);
+    }
+
+    /**
+     * Builds a URI that points to all programs on a given channel.
+     *
+     * @param channelUri The URI of the channel to return programs for.
+     */
+    public static Uri buildProgramsUriForChannel(Uri channelUri) {
+        return TvContract.buildProgramsUriForChannel(channelUri);
+    }
+
+    /**
+     * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+     * given time frame.
+     *
+     * @param channelId The ID of the channel to return programs for.
+     * @param startTime The start time used to filter programs. The returned programs should have
+     *            {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
+     * @param endTime The end time used to filter programs. The returned programs should have
+     *            {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
+     */
+    public static Uri buildProgramsUriForChannel(long channelId, long startTime,
+            long endTime) {
+        return TvContract.buildProgramsUriForChannel(channelId, startTime, endTime);
+    }
+
+    /**
+     * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+     * given time frame.
+     *
+     * @param channelUri The URI of the channel to return programs for.
+     * @param startTime The start time used to filter programs. The returned programs should have
+     *            {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
+     * @param endTime The end time used to filter programs. The returned programs should have
+     *            {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
+     */
+    public static Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
+            long endTime) {
+        return TvContract.buildProgramsUriForChannel(channelUri, startTime, endTime);
+    }
+
+    /**
+     * Builds a URI that points to a specific recorded program.
+     *
+     * @param recordedProgramId The ID of the recorded program to point to.
+     */
+    public static Uri buildRecordedProgramUri(long recordedProgramId) {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {
+            return TvContract.buildRecordedProgramUri(recordedProgramId);
+        } else {
+            return ContentUris.withAppendedId(RecordedPrograms.CONTENT_URI, recordedProgramId);
+        }
+    }
+
+    /**
+     * Builds a URI that points to a specific preview program.
+     *
+     * @param previewProgramId The ID of the preview program to point to.
+     */
+    public static Uri buildPreviewProgramUri(long previewProgramId) {
+        return ContentUris.withAppendedId(PreviewPrograms.CONTENT_URI, previewProgramId);
+    }
+
+    /**
+     * Builds a URI that points to all preview programs on a given channel.
+     *
+     * @param channelId The ID of the channel to return preview programs for.
+     */
+    public static Uri buildPreviewProgramsUriForChannel(long channelId) {
+        return PreviewPrograms.CONTENT_URI.buildUpon()
+                .appendQueryParameter(PARAM_CHANNEL, String.valueOf(channelId)).build();
+    }
+
+    /**
+     * Builds a URI that points to all preview programs on a given channel.
+     *
+     * @param channelUri The URI of the channel to return preview programs for.
+     */
+    public static Uri buildPreviewProgramsUriForChannel(Uri channelUri) {
+        if (!isChannelUriForTunerInput(channelUri)) {
+            throw new IllegalArgumentException("Not a channel: " + channelUri);
+        }
+        return buildPreviewProgramsUriForChannel(ContentUris.parseId(channelUri));
+    }
+
+    /**
+     * Builds a URI that points to a specific watch next program.
+     *
+     * @param watchNextProgramId The ID of the watch next program to point to.
+     */
+    public static Uri buildWatchNextProgramUri(long watchNextProgramId) {
+        return ContentUris.withAppendedId(WatchNextPrograms.CONTENT_URI, watchNextProgramId);
+    }
+
+    private static boolean isTvUri(Uri uri) {
+        return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
+                && AUTHORITY.equals(uri.getAuthority());
+    }
+
+    private static boolean isTwoSegmentUriStartingWith(Uri uri, String pathSegment) {
+        List<String> pathSegments = uri.getPathSegments();
+        return pathSegments.size() == 2 && pathSegment.equals(pathSegments.get(0));
+    }
+
+    /**
+     * Returns {@code true}, if {@code uri} is a channel URI.
+     */
+    public static boolean isChannelUri(Uri uri) {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {
+            return TvContract.isChannelUri(uri);
+        } else {
+            return isChannelUriForTunerInput(uri) || isChannelUriForPassthroughInput(uri);
+        }
+    }
+
+    /**
+     * Returns {@code true}, if {@code uri} is a channel URI for a tuner input.
+     */
+    public static boolean isChannelUriForTunerInput(Uri uri) {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {
+            return TvContract.isChannelUriForTunerInput(uri);
+        } else {
+            return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_CHANNEL);
+        }
+    }
+
+    /**
+     * Returns {@code true}, if {@code uri} is a channel URI for a pass-through input.
+     */
+    public static boolean isChannelUriForPassthroughInput(Uri uri) {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {
+            return TvContract.isChannelUriForPassthroughInput(uri);
+        } else {
+            return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PASSTHROUGH);
+        }
+    }
+
+    /**
+     * Returns {@code true}, if {@code uri} is a program URI.
+     */
+    public static boolean isProgramUri(Uri uri) {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {
+            return TvContract.isProgramUri(uri);
+        } else {
+            return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PROGRAM);
+        }
+    }
+
+    /**
+     * Returns {@code true}, if {@code uri} is a recorded program URI.
+     */
+    public static boolean isRecordedProgramUri(Uri uri) {
+        return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_RECORDED_PROGRAM);
+    }
+
+    /**
+     * Requests to make a channel browsable.
+     *
+     * <p>Once called, the system will review the request and make the channel browsable based on
+     * its policy. The first request from a package is guaranteed to be approved. This is only
+     * relevant to channels with {@link Channels#TYPE_PREVIEW} type.
+     *
+     * <p>No-op on devices prior to {@link android.os.Build.VERSION_CODES#O}.
+     *
+     * @param context The context for accessing content provider.
+     * @param channelId The channel ID to be browsable.
+     * @see Channels#COLUMN_BROWSABLE
+     */
+    @RequiresApi(api = 26)
+    public static void requestChannelBrowsable(Context context, long channelId) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            TvContract.requestChannelBrowsable(context, channelId);
+        }
+    }
+
+    private TvContractCompat() {}
+
+    /**
+     * Common base for the tables of TV channels/programs.
+     */
+    public interface BaseTvColumns extends BaseColumns {
+        /**
+         * The name of the package that owns the current row.
+         *
+         * <p>The TV provider fills in this column with the name of the package that provides the
+         * initial data of the row. If the package is later uninstalled, the rows it owns are
+         * automatically removed from the tables.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_PACKAGE_NAME = "package_name";
+    }
+
+    /**
+     * Common columns for the tables of TV programs.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    interface ProgramColumns {
+        /** @hide */
+        @IntDef({
+                REVIEW_RATING_STYLE_STARS,
+                REVIEW_RATING_STYLE_THUMBS_UP_DOWN,
+                REVIEW_RATING_STYLE_PERCENTAGE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(LIBRARY_GROUP)
+        @interface ReviewRatingStyle {}
+
+        /**
+         * The review rating style for five star rating.
+         *
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        int REVIEW_RATING_STYLE_STARS = 0;
+
+        /**
+         * The review rating style for thumbs-up and thumbs-down rating.
+         *
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1;
+
+        /**
+         * The review rating style for 0 to 100 point system.
+         *
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        int REVIEW_RATING_STYLE_PERCENTAGE = 2;
+
+        /**
+         * The title of this TV program.
+         *
+         * <p>If this program is an episodic TV show, it is recommended that the title is the series
+         * title and its related fields ({@link #COLUMN_SEASON_TITLE} and/or
+         * {@link #COLUMN_SEASON_DISPLAY_NUMBER}, {@link #COLUMN_SEASON_DISPLAY_NUMBER},
+         * {@link #COLUMN_EPISODE_DISPLAY_NUMBER}, and {@link #COLUMN_EPISODE_TITLE}) are filled in.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_TITLE = "title";
+
+        /**
+         * The season display number of this TV program for episodic TV shows.
+         *
+         * <p>This is used to indicate the season number. (e.g. 1, 2 or 3) Note that the value
+         * does not necessarily be numeric. (e.g. 12B)
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+
+        /**
+         * The title of the season for this TV program for episodic TV shows.
+         *
+         * <p>This is an optional field supplied only when the season has a special title
+         * (e.g. The Final Season). If provided, the applications should display it instead of
+         * {@link #COLUMN_SEASON_DISPLAY_NUMBER}, and should display it without alterations.
+         * (e.g. for "The Final Season", displayed string should be "The Final Season", not
+         * "Season The Final Season"). When displaying multiple programs, the order should be based
+         * on {@link #COLUMN_SEASON_DISPLAY_NUMBER}, even when {@link #COLUMN_SEASON_TITLE} exists.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_SEASON_TITLE = "season_title";
+
+        /**
+         * The episode display number of this TV program for episodic TV shows.
+         *
+         * <p>This is used to indicate the episode number. (e.g. 1, 2 or 3) Note that the value
+         * does not necessarily be numeric. (e.g. 12B)
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+
+        /**
+         * The episode title of this TV program for episodic TV shows.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_EPISODE_TITLE = "episode_title";
+
+        /**
+         * The comma-separated canonical genre string of this TV program.
+         *
+         * <p>Canonical genres are defined in {@link Genres}. Use {@link Genres#encode} to create a
+         * text that can be stored in this column. Use {@link Genres#decode} to get the canonical
+         * genre strings from the text stored in the column.
+         *
+         * <p>Type: TEXT
+         * @see Genres
+         * @see Genres#encode
+         * @see Genres#decode
+         */
+        String COLUMN_CANONICAL_GENRE = "canonical_genre";
+
+        /**
+         * The short description of this TV program that is displayed to the user by default.
+         *
+         * <p>It is recommended to limit the length of the descriptions to 256 characters.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_SHORT_DESCRIPTION = "short_description";
+
+        /**
+         * The detailed, lengthy description of this TV program that is displayed only when the user
+         * wants to see more information.
+         *
+         * <p>TV input services should leave this field empty if they have no additional details
+         * beyond {@link #COLUMN_SHORT_DESCRIPTION}.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_LONG_DESCRIPTION = "long_description";
+
+        /**
+         * The width of the video for this TV program, in the unit of pixels.
+         *
+         * <p>Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video
+         * resolution of the current TV program. Can be empty if it is not known initially or the
+         * program does not convey any video such as the programs from type
+         * {@link Channels#SERVICE_TYPE_AUDIO} channels.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_VIDEO_WIDTH = "video_width";
+
+        /**
+         * The height of the video for this TV program, in the unit of pixels.
+         *
+         * <p>Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video
+         * resolution of the current TV program. Can be empty if it is not known initially or the
+         * program does not convey any video such as the programs from type
+         * {@link Channels#SERVICE_TYPE_AUDIO} channels.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_VIDEO_HEIGHT = "video_height";
+
+        /**
+         * The comma-separated audio languages of this TV program.
+         *
+         * <p>This is used to describe available audio languages included in the program. Use either
+         * ISO 639-1 or 639-2/T codes.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_AUDIO_LANGUAGE = "audio_language";
+
+        /**
+         * The comma-separated content ratings of this TV program.
+         *
+         * <p>This is used to describe the content rating(s) of this program. Each comma-separated
+         * content rating sub-string should be generated by calling
+         * {@link TvContentRating#flattenToString}. Note that in most cases the program content is
+         * rated by a single rating system, thus resulting in a corresponding single sub-string that
+         * does not require comma separation and multiple sub-strings appear only when the program
+         * content is rated by two or more content rating systems. If any of those ratings is
+         * specified as "blocked rating" in the user's parental control settings, the TV input
+         * service should block the current content and wait for the signal that it is okay to
+         * unblock.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_CONTENT_RATING = "content_rating";
+
+        /**
+         * The URI for the poster art of this TV program.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_POSTER_ART_URI = "poster_art_uri";
+
+        /**
+         * The URI for the thumbnail of this TV program.
+         *
+         * <p>The system can generate a thumbnail from the poster art if this column is not
+         * specified. Thus it is not necessary for TV input services to include a thumbnail if it is
+         * just a scaled image of the poster art.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+
+        /**
+         * The flag indicating whether this TV program is searchable or not.
+         *
+         * <p>The columns of searchable programs can be read by other applications that have proper
+         * permission. Care must be taken not to open sensitive data.
+         *
+         * <p>A value of 1 indicates that the program is searchable and its columns can be read by
+         * other applications, a value of 0 indicates that the program is hidden and its columns can
+         * be read only by the package that owns the program and the system. If not specified, this
+         * value is set to 1 (searchable) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        String COLUMN_SEARCHABLE = "searchable";
+
+        /**
+         * Internal data used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: BLOB
+         */
+        String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+
+        /**
+         * The version number of this row entry used by TV input services.
+         *
+         * <p>This is best used by sync adapters to identify the rows to update. The number can be
+         * defined by individual TV input services. One may assign the same value as
+         * {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
+         * broadcast.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_VERSION_NUMBER = "version_number";
+
+        /**
+         * The review rating score style used for {@link #COLUMN_REVIEW_RATING}.
+         *
+         * <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS},
+         * {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}.
+         *
+         * <p>Type: INTEGER
+         * @see #COLUMN_REVIEW_RATING
+         */
+        String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+
+        /**
+         * The review rating score for this program.
+         *
+         * <p>The format of the value is dependent on {@link #COLUMN_REVIEW_RATING_STYLE}. If the
+         * style is {@link #REVIEW_RATING_STYLE_STARS}, the value should be a real number between
+         * 0.0 and 5.0. (e.g. "4.5") If the style is {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN},
+         * the value should be two integers, one for thumbs-up count and the other for thumbs-down
+         * count, with a comma between them. (e.g. "200,40") If the style is
+         * {@link #REVIEW_RATING_STYLE_PERCENTAGE}, the value shoule be a real number between 0 and
+         * 100. (e.g. "99.9")
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        String COLUMN_REVIEW_RATING = "review_rating";
+    }
+
+    /**
+     * Common columns for the tables of preview programs.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public interface PreviewProgramColumns {
+
+        /** @hide */
+        @IntDef({
+                TYPE_MOVIE,
+                TYPE_TV_SERIES,
+                TYPE_TV_SEASON,
+                TYPE_TV_EPISODE,
+                TYPE_CLIP,
+                TYPE_EVENT,
+                TYPE_CHANNEL,
+                TYPE_TRACK,
+                TYPE_ALBUM,
+                TYPE_ARTIST,
+                TYPE_PLAYLIST,
+                TYPE_STATION,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(LIBRARY_GROUP)
+        public @interface Type {}
+
+        /**
+         * The program type for movie.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_MOVIE = 0;
+
+        /**
+         * The program type for TV series.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_TV_SERIES = 1;
+
+        /**
+         * The program type for TV season.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_TV_SEASON = 2;
+
+        /**
+         * The program type for TV episode.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_TV_EPISODE = 3;
+
+        /**
+         * The program type for clip.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_CLIP = 4;
+
+        /**
+         * The program type for event.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_EVENT = 5;
+
+        /**
+         * The program type for channel.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_CHANNEL = 6;
+
+        /**
+         * The program type for track.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_TRACK = 7;
+
+        /**
+         * The program type for album.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_ALBUM = 8;
+
+        /**
+         * The program type for artist.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_ARTIST = 9;
+
+        /**
+         * The program type for playlist.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_PLAYLIST = 10;
+
+        /**
+         * The program type for station.
+         *
+         * @see #COLUMN_TYPE
+         */
+        int TYPE_STATION = 11;
+
+        /** @hide */
+        @IntDef({
+                ASPECT_RATIO_16_9,
+                ASPECT_RATIO_3_2,
+                ASPECT_RATIO_4_3,
+                ASPECT_RATIO_1_1,
+                ASPECT_RATIO_2_3,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(LIBRARY_GROUP)
+        public @interface AspectRatio {}
+
+        /**
+         * The aspect ratio for 16:9.
+         *
+         * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+         * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+         */
+        int ASPECT_RATIO_16_9 = 0;
+
+        /**
+         * The aspect ratio for 3:2.
+         *
+         * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+         * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+         */
+        int ASPECT_RATIO_3_2 = 1;
+
+        /**
+         * The aspect ratio for 4:3.
+         *
+         * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+         * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+         */
+        int ASPECT_RATIO_4_3 = 2;
+
+        /**
+         * The aspect ratio for 1:1.
+         *
+         * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+         * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+         */
+        int ASPECT_RATIO_1_1 = 3;
+
+        /**
+         * The aspect ratio for 2:3.
+         *
+         * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+         * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+         */
+        int ASPECT_RATIO_2_3 = 4;
+
+        /** @hide */
+        @IntDef({
+                AVAILABILITY_AVAILABLE,
+                AVAILABILITY_FREE_WITH_SUBSCRIPTION,
+                AVAILABILITY_PAID_CONTENT,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(LIBRARY_GROUP)
+        public @interface Availability {}
+
+        /**
+         * The availability for "available to this user".
+         *
+         * @see #COLUMN_AVAILABILITY
+         */
+        int AVAILABILITY_AVAILABLE = 0;
+
+        /**
+         * The availability for "free with subscription".
+         *
+         * @see #COLUMN_AVAILABILITY
+         */
+        int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1;
+
+        /**
+         * The availability for "paid content, either to-own or rental
+         * (user has not purchased/rented).
+         *
+         * @see #COLUMN_AVAILABILITY
+         */
+        int AVAILABILITY_PAID_CONTENT = 2;
+
+        /** @hide */
+        @IntDef({
+                INTERACTION_TYPE_VIEWS,
+                INTERACTION_TYPE_LISTENS,
+                INTERACTION_TYPE_FOLLOWERS,
+                INTERACTION_TYPE_FANS,
+                INTERACTION_TYPE_LIKES,
+                INTERACTION_TYPE_THUMBS,
+                INTERACTION_TYPE_VIEWERS,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(LIBRARY_GROUP)
+        public @interface InteractionType {}
+
+        /**
+         * The interaction type for "views".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_VIEWS = 0;
+
+        /**
+         * The interaction type for "listens".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_LISTENS = 1;
+
+        /**
+         * The interaction type for "followers".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_FOLLOWERS = 2;
+
+        /**
+         * The interaction type for "fans".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_FANS = 3;
+
+        /**
+         * The interaction type for "likes".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_LIKES = 4;
+
+        /**
+         * The interaction type for "thumbs".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_THUMBS = 5;
+
+        /**
+         * The interaction type for "viewers".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        int INTERACTION_TYPE_VIEWERS = 6;
+
+        /**
+         * The type of this program content.
+         *
+         * <p>The value should match one of the followings:
+         * {@link #TYPE_MOVIE},
+         * {@link #TYPE_TV_SERIES},
+         * {@link #TYPE_TV_SEASON},
+         * {@link #TYPE_TV_EPISODE},
+         * {@link #TYPE_CLIP},
+         * {@link #TYPE_EVENT},
+         * {@link #TYPE_CHANNEL},
+         * {@link #TYPE_TRACK},
+         * {@link #TYPE_ALBUM},
+         * {@link #TYPE_ARTIST},
+         * {@link #TYPE_PLAYLIST}, and
+         * {@link #TYPE_STATION}.
+         *
+         * <p>This is a required field if the program is from a {@link Channels#TYPE_PREVIEW}
+         * channel.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_TYPE = "type";
+
+        /**
+         * The aspect ratio of the poster art for this TV program.
+         *
+         * <p>The value should match one of the followings:
+         * {@link #ASPECT_RATIO_16_9},
+         * {@link #ASPECT_RATIO_3_2},
+         * {@link #ASPECT_RATIO_4_3},
+         * {@link #ASPECT_RATIO_1_1}, and
+         * {@link #ASPECT_RATIO_2_3}.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+
+        /**
+         * The aspect ratio of the thumbnail for this TV program.
+         *
+         * <p>The value should match one of the followings:
+         * {@link #ASPECT_RATIO_16_9},
+         * {@link #ASPECT_RATIO_3_2},
+         * {@link #ASPECT_RATIO_4_3},
+         * {@link #ASPECT_RATIO_1_1}, and
+         * {@link #ASPECT_RATIO_2_3}.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+
+        /**
+         * The URI for the logo of this TV program.
+         *
+         * <p>This is a small badge shown on top of the poster art or thumbnail representing the
+         * source of the content.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_LOGO_URI = "logo_uri";
+
+        /**
+         * The availability of this TV program.
+         *
+         * <p>The value should match one of the followings:
+         * {@link #AVAILABILITY_AVAILABLE},
+         * {@link #AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and
+         * {@link #AVAILABILITY_PAID_CONTENT}.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_AVAILABILITY = "availability";
+
+        /**
+         * The starting price of this TV program.
+         *
+         * <p>This indicates the lowest regular acquisition cost of the content. It is only used
+         * if the availability of the program is {@link #AVAILABILITY_PAID_CONTENT}.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_OFFER_PRICE
+         */
+        String COLUMN_STARTING_PRICE = "starting_price";
+
+        /**
+         * The offer price of this TV program.
+         *
+         * <p>This is the promotional cost of the content. It is only used if the availability of
+         * the program is {@link #AVAILABILITY_PAID_CONTENT}.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_STARTING_PRICE
+         */
+        String COLUMN_OFFER_PRICE = "offer_price";
+
+        /**
+         * The release date of this TV program.
+         *
+         * <p>The value should be in one of the following formats:
+         * "yyyy", "yyyy-MM-dd", and "yyyy-MM-ddTHH:mm:ssZ" (UTC in ISO 8601).
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_RELEASE_DATE = "release_date";
+
+        /**
+         * The count of the items included in this TV program.
+         *
+         * <p>This is only relevant if the program represents a collection of items such as series,
+         * episodes, or music tracks.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_ITEM_COUNT = "item_count";
+
+        /**
+         * The flag indicating whether this TV program is live or not.
+         *
+         * <p>A value of 1 indicates that the content is airing and should be consumed now, a value
+         * of 0 indicates that the content is off the air and does not need to be consumed at the
+         * present time. If not specified, the value is set to 0 (not live) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        String COLUMN_LIVE = "live";
+
+        /**
+         * The internal ID used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
+        /**
+         * The URI for the preview video.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+
+        /**
+         * The last playback position (in milliseconds) of the original content of this preview
+         * program.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_LAST_PLAYBACK_POSITION_MILLIS =
+                "last_playback_position_millis";
+
+        /**
+         * The duration (in milliseconds) of the original content of this preview program.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         */
+        String COLUMN_DURATION_MILLIS = "duration_millis";
+
+        /**
+         * The intent URI which is launched when the preview program is selected.
+         *
+         * <p>The URI is created using {@link Intent#toUri} with {@link Intent#URI_INTENT_SCHEME}
+         * and converted back to the original intent with {@link Intent#parseUri}. The intent is
+         * launched when the user selects the preview program item.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_INTENT_URI = "intent_uri";
+
+        /**
+         * The flag indicating whether this program is transient or not.
+         *
+         * <p>A value of 1 indicates that the channel will be automatically removed by the system on
+         * reboot, and a value of 0 indicates that the channel is persistent across reboot. If not
+         * specified, this value is set to 0 (not transient) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         * @see Channels#COLUMN_TRANSIENT
+         */
+        String COLUMN_TRANSIENT = "transient";
+
+        /**
+         * The type of interaction for this TV program.
+         *
+         * <p> The value should match one of the followings:
+         * {@link #INTERACTION_TYPE_LISTENS},
+         * {@link #INTERACTION_TYPE_FOLLOWERS},
+         * {@link #INTERACTION_TYPE_FANS},
+         * {@link #INTERACTION_TYPE_LIKES},
+         * {@link #INTERACTION_TYPE_THUMBS},
+         * {@link #INTERACTION_TYPE_VIEWS}, and
+         * {@link #INTERACTION_TYPE_VIEWERS}.
+         *
+         * <p>Type: INTEGER
+         * @see #COLUMN_INTERACTION_COUNT
+         */
+        String COLUMN_INTERACTION_TYPE = "interaction_type";
+
+        /**
+         * The interaction count for this program.
+         *
+         * <p>This indicates the number of times interaction has happened.
+         *
+         * <p>Type: INTEGER (long)
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        String COLUMN_INTERACTION_COUNT = "interaction_count";
+
+        /**
+         * The author or artist of this content.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_AUTHOR = "author";
+
+        /**
+         * The flag indicating whether this TV program is browsable or not.
+         *
+         * <p>This column can only be set by applications having proper system permission. For
+         * other applications, this is a read-only column.
+         *
+         * <p>A value of 1 indicates that the program is browsable and can be shown to users in
+         * the UI. A value of 0 indicates that the program should be hidden from users and the
+         * application who changes this value to 0 should send
+         * {@link #ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED} to the owner of the program
+         * to notify this change.
+         *
+         * <p>This value is set to 1 (browsable) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        String COLUMN_BROWSABLE = "browsable";
+
+        /**
+         * The content ID of this TV program.
+         *
+         * <p>A public ID of the content which allows the application to apply the same operation to
+         * all the program copies in different channels.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        String COLUMN_CONTENT_ID = "content_id";
+
+    }
+
+    /** Column definitions for the TV channels table. */
+    public static final class Channels implements BaseTvColumns {
+
+        /**
+         * The content:// style URI for this table.
+         *
+         * <p>SQL selection is not supported for {@link ContentResolver#query},
+         * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+                + PATH_CHANNEL);
+
+        /** The MIME type of a directory of TV channels. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+
+        /** The MIME type of a single TV channel. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        @StringDef({
+                TYPE_OTHER,
+                TYPE_NTSC,
+                TYPE_PAL,
+                TYPE_SECAM,
+                TYPE_DVB_T,
+                TYPE_DVB_T2,
+                TYPE_DVB_S,
+                TYPE_DVB_S2,
+                TYPE_DVB_C,
+                TYPE_DVB_C2,
+                TYPE_DVB_H,
+                TYPE_DVB_SH,
+                TYPE_ATSC_T,
+                TYPE_ATSC_C,
+                TYPE_ATSC_M_H,
+                TYPE_ISDB_T,
+                TYPE_ISDB_TB,
+                TYPE_ISDB_S,
+                TYPE_ISDB_C,
+                TYPE_1SEG,
+                TYPE_DTMB,
+                TYPE_CMMB,
+                TYPE_T_DMB,
+                TYPE_S_DMB,
+                TYPE_PREVIEW,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Type {}
+
+        /**
+         * A generic channel type.
+         *
+         * Use this if the current channel is streaming-based or its broadcast system type does not
+         * fit under any other types. This is the default channel type.
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_OTHER = "TYPE_OTHER";
+
+        /**
+         * The channel type for NTSC.
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_NTSC = "TYPE_NTSC";
+
+        /**
+         * The channel type for PAL.
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_PAL = "TYPE_PAL";
+
+        /**
+         * The channel type for SECAM.
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_SECAM = "TYPE_SECAM";
+
+        /**
+         * The channel type for DVB-T (terrestrial).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_T = "TYPE_DVB_T";
+
+        /**
+         * The channel type for DVB-T2 (terrestrial).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_T2 = "TYPE_DVB_T2";
+
+        /**
+         * The channel type for DVB-S (satellite).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_S = "TYPE_DVB_S";
+
+        /**
+         * The channel type for DVB-S2 (satellite).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_S2 = "TYPE_DVB_S2";
+
+        /**
+         * The channel type for DVB-C (cable).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_C = "TYPE_DVB_C";
+
+        /**
+         * The channel type for DVB-C2 (cable).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_C2 = "TYPE_DVB_C2";
+
+        /**
+         * The channel type for DVB-H (handheld).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_H = "TYPE_DVB_H";
+
+        /**
+         * The channel type for DVB-SH (satellite).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DVB_SH = "TYPE_DVB_SH";
+
+        /**
+         * The channel type for ATSC (terrestrial).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ATSC_T = "TYPE_ATSC_T";
+
+        /**
+         * The channel type for ATSC (cable).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ATSC_C = "TYPE_ATSC_C";
+
+        /**
+         * The channel type for ATSC-M/H (mobile/handheld).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+
+        /**
+         * The channel type for ISDB-T (terrestrial).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ISDB_T = "TYPE_ISDB_T";
+
+        /**
+         * The channel type for ISDB-Tb (Brazil).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+
+        /**
+         * The channel type for ISDB-S (satellite).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ISDB_S = "TYPE_ISDB_S";
+
+        /**
+         * The channel type for ISDB-C (cable).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_ISDB_C = "TYPE_ISDB_C";
+
+        /**
+         * The channel type for 1seg (handheld).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_1SEG = "TYPE_1SEG";
+
+        /**
+         * The channel type for DTMB (terrestrial).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_DTMB = "TYPE_DTMB";
+
+        /**
+         * The channel type for CMMB (handheld).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_CMMB = "TYPE_CMMB";
+
+        /**
+         * The channel type for T-DMB (terrestrial).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_T_DMB = "TYPE_T_DMB";
+
+        /**
+         * The channel type for S-DMB (satellite).
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_S_DMB = "TYPE_S_DMB";
+
+        /**
+         * The channel type for preview videos.
+         *
+         * <P>Unlike other broadcast TV channel types, the programs in the preview channel usually
+         * are promotional videos. The UI may treat the preview channels differently from the other
+         * broadcast channels.
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_PREVIEW = "TYPE_PREVIEW";
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        @StringDef({
+                SERVICE_TYPE_OTHER,
+                SERVICE_TYPE_AUDIO_VIDEO,
+                SERVICE_TYPE_AUDIO,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ServiceType {}
+
+        /** A generic service type. */
+        public static final String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+
+        /** The service type for regular TV channels that have both audio and video. */
+        public static final String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+
+        /** The service type for radio channels that have audio only. */
+        public static final String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        @StringDef({
+                VIDEO_FORMAT_240P,
+                VIDEO_FORMAT_360P,
+                VIDEO_FORMAT_480I,
+                VIDEO_FORMAT_576I,
+                VIDEO_FORMAT_576P,
+                VIDEO_FORMAT_720P,
+                VIDEO_FORMAT_1080I,
+                VIDEO_FORMAT_1080P,
+                VIDEO_FORMAT_2160P,
+                VIDEO_FORMAT_4320P,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface VideoFormat {}
+
+        /** The video format for 240p. */
+        public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+
+        /** The video format for 360p. */
+        public static final String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+
+        /** The video format for 480i. */
+        public static final String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+
+        /** The video format for 480p. */
+        public static final String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+
+        /** The video format for 576i. */
+        public static final String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+
+        /** The video format for 576p. */
+        public static final String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+
+        /** The video format for 720p. */
+        public static final String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+
+        /** The video format for 1080i. */
+        public static final String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+
+        /** The video format for 1080p. */
+        public static final String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+
+        /** The video format for 2160p. */
+        public static final String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+
+        /** The video format for 4320p. */
+        public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+
+        /** @hide */
+        @RestrictTo(LIBRARY_GROUP)
+        @StringDef({
+                VIDEO_RESOLUTION_SD,
+                VIDEO_RESOLUTION_ED,
+                VIDEO_RESOLUTION_HD,
+                VIDEO_RESOLUTION_FHD,
+                VIDEO_RESOLUTION_UHD,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface VideoResolution {}
+
+        /** The video resolution for standard-definition. */
+        public static final String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+
+        /** The video resolution for enhanced-definition. */
+        public static final String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+
+        /** The video resolution for high-definition. */
+        public static final String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+
+        /** The video resolution for full high-definition. */
+        public static final String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+
+        /** The video resolution for ultra high-definition. */
+        public static final String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+
+        private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP = new HashMap<>();
+
+        static {
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480I, VIDEO_RESOLUTION_SD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480P, VIDEO_RESOLUTION_ED);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576I, VIDEO_RESOLUTION_SD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576P, VIDEO_RESOLUTION_ED);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_720P, VIDEO_RESOLUTION_HD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080I, VIDEO_RESOLUTION_HD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080P, VIDEO_RESOLUTION_FHD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_2160P, VIDEO_RESOLUTION_UHD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_4320P, VIDEO_RESOLUTION_UHD);
+        }
+
+        /**
+         * Returns the video resolution (definition) for a given video format.
+         *
+         * @param videoFormat The video format defined in {@link Channels}.
+         * @return the corresponding video resolution string. {@code null} if the resolution string
+         *         is not defined for the given video format.
+         * @see #COLUMN_VIDEO_FORMAT
+         */
+        @Nullable
+        public static String getVideoResolution(@VideoFormat String videoFormat) {
+            return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat);
+        }
+
+        /**
+         * The ID of the TV input service that provides this TV channel.
+         *
+         * <p>Use {@link #buildInputId} to build the ID.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_INPUT_ID = "input_id";
+
+        /**
+         * The broadcast system type of this TV channel.
+         *
+         * <p>This is used to indicate the broadcast standard (e.g. ATSC, DVB or ISDB) the current
+         * channel conforms to. Use {@link #TYPE_OTHER} for streaming-based channels, which is the
+         * default channel type. The value should match one of the followings:
+         * {@link #TYPE_1SEG},
+         * {@link #TYPE_ATSC_C},
+         * {@link #TYPE_ATSC_M_H},
+         * {@link #TYPE_ATSC_T},
+         * {@link #TYPE_CMMB},
+         * {@link #TYPE_DTMB},
+         * {@link #TYPE_DVB_C},
+         * {@link #TYPE_DVB_C2},
+         * {@link #TYPE_DVB_H},
+         * {@link #TYPE_DVB_S},
+         * {@link #TYPE_DVB_S2},
+         * {@link #TYPE_DVB_SH},
+         * {@link #TYPE_DVB_T},
+         * {@link #TYPE_DVB_T2},
+         * {@link #TYPE_ISDB_C},
+         * {@link #TYPE_ISDB_S},
+         * {@link #TYPE_ISDB_T},
+         * {@link #TYPE_ISDB_TB},
+         * {@link #TYPE_NTSC},
+         * {@link #TYPE_OTHER},
+         * {@link #TYPE_PAL},
+         * {@link #TYPE_SECAM},
+         * {@link #TYPE_S_DMB}, and
+         * {@link #TYPE_T_DMB}.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_TYPE = "type";
+
+        /**
+         * The predefined service type of this TV channel.
+         *
+         * <p>This is primarily used to indicate whether the current channel is a regular TV channel
+         * or a radio-like channel. Use the same coding for {@code service_type} in the underlying
+         * broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB
+         * STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER},
+         * {@link #SERVICE_TYPE_AUDIO_VIDEO}, {@link #SERVICE_TYPE_AUDIO}
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_SERVICE_TYPE = "service_type";
+
+        /**
+         * The original network ID of this TV channel.
+         *
+         * <p>It is used to identify the originating delivery system, if applicable. Use the same
+         * coding for {@code original_network_id} for ETSI EN 300 468/TR 101 211 and ARIB STD-B10.
+         *
+         * <p>This is a required field only if the underlying broadcast standard defines the same
+         * name field. Otherwise, leave empty.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+
+        /**
+         * The transport stream ID of this channel.
+         *
+         * <p>It is used to identify the Transport Stream that contains the current channel from any
+         * other multiplex within a network, if applicable. Use the same coding for
+         * {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via
+         * the MPEG Transport Stream.
+         *
+         * <p>This is a required field only if the current channel is transmitted via the MPEG
+         * Transport Stream. Leave empty otherwise.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+
+        /**
+         * The service ID of this channel.
+         *
+         * <p>It is used to identify the current service, or channel from any other services within
+         * a given Transport Stream, if applicable. Use the same coding for {@code service_id} in
+         * ETSI EN 300 468 and ARIB STD-B10 or {@code program_number} in ISO/IEC 13818-1.
+         *
+         * <p>This is a required field only if the underlying broadcast standard defines the same
+         * name field, or the current channel is transmitted via the MPEG Transport Stream. Leave
+         * empty otherwise.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_SERVICE_ID = "service_id";
+
+        /**
+         * The channel number that is displayed to the user.
+         *
+         * <p>The format can vary depending on broadcast standard and product specification.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_DISPLAY_NUMBER = "display_number";
+
+        /**
+         * The channel name that is displayed to the user.
+         *
+         * <p>A call sign is a good candidate to use for this purpose but any name that helps the
+         * user recognize the current channel will be enough. Can also be empty depending on
+         * broadcast standard.
+         *
+         * <p> Type: TEXT
+         */
+        public static final String COLUMN_DISPLAY_NAME = "display_name";
+
+        /**
+         * The network affiliation for this TV channel.
+         *
+         * <p>This is used to identify a channel that is commonly called by its network affiliation
+         * instead of the display name. Examples include ABC for the channel KGO-HD, FOX for the
+         * channel KTVU-HD and NBC for the channel KNTV-HD. Can be empty if not applicable.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
+
+        /**
+         * The description of this TV channel.
+         *
+         * <p>Can be empty initially.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_DESCRIPTION = "description";
+
+        /**
+         * The typical video format for programs from this TV channel.
+         *
+         * <p>This is primarily used to filter out channels based on video format by applications.
+         * The value should match one of the followings: {@link #VIDEO_FORMAT_240P},
+         * {@link #VIDEO_FORMAT_360P}, {@link #VIDEO_FORMAT_480I}, {@link #VIDEO_FORMAT_480P},
+         * {@link #VIDEO_FORMAT_576I}, {@link #VIDEO_FORMAT_576P}, {@link #VIDEO_FORMAT_720P},
+         * {@link #VIDEO_FORMAT_1080I}, {@link #VIDEO_FORMAT_1080P}, {@link #VIDEO_FORMAT_2160P},
+         * {@link #VIDEO_FORMAT_4320P}. Note that the actual video resolution of each program from a
+         * given channel can vary thus one should use {@link Programs#COLUMN_VIDEO_WIDTH} and
+         * {@link Programs#COLUMN_VIDEO_HEIGHT} to get more accurate video resolution.
+         *
+         * <p>Type: TEXT
+         *
+         * @see #getVideoResolution
+         */
+        public static final String COLUMN_VIDEO_FORMAT = "video_format";
+
+        /**
+         * The flag indicating whether this TV channel is browsable or not.
+         *
+         * <p>This column can only be set by applications having proper system permission. For
+         * other applications, this is a read-only column.
+         *
+         * <p>A value of 1 indicates the channel is included in the channel list that applications
+         * use to browse channels, a value of 0 indicates the channel is not included in the list.
+         * If not specified, this value is set to 0 (not browsable) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        public static final String COLUMN_BROWSABLE = "browsable";
+
+        /**
+         * The flag indicating whether this TV channel is searchable or not.
+         *
+         * <p>The columns of searchable channels can be read by other applications that have proper
+         * permission. Care must be taken not to open sensitive data.
+         *
+         * <p>A value of 1 indicates that the channel is searchable and its columns can be read by
+         * other applications, a value of 0 indicates that the channel is hidden and its columns can
+         * be read only by the package that owns the channel and the system. If not specified, this
+         * value is set to 1 (searchable) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        public static final String COLUMN_SEARCHABLE = "searchable";
+
+        /**
+         * The flag indicating whether this TV channel is locked or not.
+         *
+         * <p>This is primarily used for alternative parental control to prevent unauthorized users
+         * from watching the current channel regardless of the content rating. A value of 1
+         * indicates the channel is locked and the user is required to enter passcode to unlock it
+         * in order to watch the current program from the channel, a value of 0 indicates the
+         * channel is not locked thus the user is not prompted to enter passcode If not specified,
+         * this value is set to 0 (not locked) by default.
+         *
+         * <p>This column can only be set by applications having proper system permission to
+         * modify parental control settings.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        public static final String COLUMN_LOCKED = "locked";
+
+        /**
+         * The URI for the app badge icon of the app link template for this channel.
+         *
+         * <p>This small icon is overlaid at the bottom of the poster art specified by
+         * {@link #COLUMN_APP_LINK_POSTER_ART_URI}. The data in the column must be a URI in one of
+         * the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>The app-linking allows channel input sources to provide activity links from their live
+         * channel programming to another activity. This enables content providers to increase user
+         * engagement by offering the viewer other content or actions.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_APP_LINK_COLOR
+         * @see #COLUMN_APP_LINK_INTENT_URI
+         * @see #COLUMN_APP_LINK_POSTER_ART_URI
+         * @see #COLUMN_APP_LINK_TEXT
+         */
+        public static final String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
+
+        /**
+         * The URI for the poster art used as the background of the app link template for this
+         * channel.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>The app-linking allows channel input sources to provide activity links from their live
+         * channel programming to another activity. This enables content providers to increase user
+         * engagement by offering the viewer other content or actions.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_APP_LINK_COLOR
+         * @see #COLUMN_APP_LINK_ICON_URI
+         * @see #COLUMN_APP_LINK_INTENT_URI
+         * @see #COLUMN_APP_LINK_TEXT
+         */
+        public static final String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
+
+        /**
+         * The link text of the app link template for this channel.
+         *
+         * <p>This provides a short description of the action that happens when the corresponding
+         * app link is clicked.
+         *
+         * <p>The app-linking allows channel input sources to provide activity links from their live
+         * channel programming to another activity. This enables content providers to increase user
+         * engagement by offering the viewer other content or actions.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_APP_LINK_COLOR
+         * @see #COLUMN_APP_LINK_ICON_URI
+         * @see #COLUMN_APP_LINK_INTENT_URI
+         * @see #COLUMN_APP_LINK_POSTER_ART_URI
+         */
+        public static final String COLUMN_APP_LINK_TEXT = "app_link_text";
+
+        /**
+         * The accent color of the app link template for this channel. This is primarily used for
+         * the background color of the text box in the template.
+         *
+         * <p>The app-linking allows channel input sources to provide activity links from their live
+         * channel programming to another activity. This enables content providers to increase user
+         * engagement by offering the viewer other content or actions.
+         *
+         * <p>Type: INTEGER (color value)
+         * @see #COLUMN_APP_LINK_ICON_URI
+         * @see #COLUMN_APP_LINK_INTENT_URI
+         * @see #COLUMN_APP_LINK_POSTER_ART_URI
+         * @see #COLUMN_APP_LINK_TEXT
+         */
+        public static final String COLUMN_APP_LINK_COLOR = "app_link_color";
+
+        /**
+         * The intent URI of the app link for this channel.
+         *
+         * <p>The URI is created using {@link Intent#toUri} with {@link Intent#URI_INTENT_SCHEME}
+         * and converted back to the original intent with {@link Intent#parseUri}. The intent is
+         * launched when the user clicks the corresponding app link for the current channel.
+         *
+         * <p>The app-linking allows channel input sources to provide activity links from their live
+         * channel programming to another activity. This enables content providers to increase user
+         * engagement by offering the viewer other content or actions.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_APP_LINK_COLOR
+         * @see #COLUMN_APP_LINK_ICON_URI
+         * @see #COLUMN_APP_LINK_POSTER_ART_URI
+         * @see #COLUMN_APP_LINK_TEXT
+         */
+        public static final String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
+
+        /**
+         * The internal ID used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
+        /**
+         * Internal data used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: BLOB
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+
+        /**
+         * The version number of this row entry used by TV input services.
+         *
+         * <p>This is best used by sync adapters to identify the rows to update. The number can be
+         * defined by individual TV input services. One may assign the same value as
+         * {@code version_number} that appears in ETSI EN 300 468 or ATSC A/65, if the data are
+         * coming from a TV broadcast.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_VERSION_NUMBER = "version_number";
+
+        /**
+         * The flag indicating whether this TV channel is transient or not.
+         *
+         * <p>A value of 1 indicates that the channel will be automatically removed by the system on
+         * reboot, and a value of 0 indicates that the channel is persistent across reboot. If not
+         * specified, this value is set to 0 (not transient) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         * @see PreviewPrograms#COLUMN_TRANSIENT
+         * @see WatchNextPrograms#COLUMN_TRANSIENT
+         */
+        public static final String COLUMN_TRANSIENT = "transient";
+
+        /**
+         * The flag indicating whether this TV channel is approved to be shown by the system.
+         *
+         * <p>A value of 1 indicates that the channel is approved to be shown by the system, and a
+         * value of 0 indicates that the channel is blocked by system. If not specified, this value
+         * is set to 0 (not approved) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public static final String COLUMN_SYSTEM_APPROVED = "system_approved";
+
+        private Channels() {}
+
+        /**
+         * A sub-directory of a single TV channel that represents its primary logo.
+         *
+         * <p>To access this directory, append {@link Channels.Logo#CONTENT_DIRECTORY} to the raw
+         * channel URI.  The resulting URI represents an image file, and should be interacted
+         * using ContentResolver.openAssetFileDescriptor.
+         *
+         * <p>Note that this sub-directory also supports opening the logo as an asset file in write
+         * mode.  Callers can create or replace the primary logo associated with this channel by
+         * opening the asset file and writing the full-size photo contents into it. (Make sure there
+         * is no padding around the logo image.) When the file is closed, the image will be parsed,
+         * sized down if necessary, and stored.
+         *
+         * <p>Usage example:
+         * <pre>
+         * public void writeChannelLogo(long channelId, byte[] logo) {
+         *     Uri channelLogoUri = TvContract.buildChannelLogoUri(channelId);
+         *     try {
+         *         AssetFileDescriptor fd =
+         *             getContentResolver().openAssetFileDescriptor(channelLogoUri, "rw");
+         *         OutputStream os = fd.createOutputStream();
+         *         os.write(logo);
+         *         os.close();
+         *         fd.close();
+         *     } catch (IOException e) {
+         *         // Handle error cases.
+         *     }
+         * }
+         * </pre>
+         */
+        public static final class Logo {
+
+            /**
+             * The directory twig for this sub-table.
+             */
+            public static final String CONTENT_DIRECTORY = "logo";
+
+            private Logo() {}
+        }
+    }
+
+    /**
+     * Column definitions for the TV programs table.
+     *
+     * <p>By default, the query results will be sorted by
+     * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} in ascending order.
+     */
+    public static final class Programs implements BaseTvColumns, ProgramColumns {
+
+        /**
+         * The content:// style URI for this table.
+         *
+         * <p>SQL selection is not supported for {@link ContentResolver#query},
+         * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+                + PATH_PROGRAM);
+
+        /** The MIME type of a directory of TV programs. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+
+        /** The MIME type of a single TV program. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+
+        /**
+         * The ID of the TV channel that provides this TV program.
+         *
+         * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_CHANNEL_ID = "channel_id";
+
+        /**
+         * The season number of this TV program for episodic TV shows.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         *
+         * @deprecated Use {@link #COLUMN_SEASON_DISPLAY_NUMBER} instead.
+         */
+        @Deprecated
+        public static final String COLUMN_SEASON_NUMBER = "season_number";
+
+        /**
+         * The episode number of this TV program for episodic TV shows.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         *
+         * @deprecated Use {@link #COLUMN_EPISODE_DISPLAY_NUMBER} instead.
+         */
+        @Deprecated
+        public static final String COLUMN_EPISODE_NUMBER = "episode_number";
+
+        /**
+         * The start time of this TV program, in milliseconds since the epoch.
+         *
+         * <p>The value should be equal to or larger than {@link #COLUMN_END_TIME_UTC_MILLIS} of the
+         * previous program in the same channel. In practice, start time will usually be the end
+         * time of the previous program.
+         *
+         * <p>Can be empty if this program belongs to a {@link Channels#TYPE_PREVIEW} channel.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+
+        /**
+         * The end time of this TV program, in milliseconds since the epoch.
+         *
+         * <p>The value should be equal to or less than {@link #COLUMN_START_TIME_UTC_MILLIS} of the
+         * next program in the same channel. In practice, end time will usually be the start time of
+         * the next program.
+         *
+         * <p>Can be empty if this program belongs to a {@link Channels#TYPE_PREVIEW} channel.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+
+        /**
+         * The comma-separated genre string of this TV program.
+         *
+         * <p>Use the same language appeared in the underlying broadcast standard, if applicable.
+         * (For example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty. Use
+         * {@link Genres#encode} to create a text that can be stored in this column. Use
+         * {@link Genres#decode} to get the broadcast genre strings from the text stored in the
+         * column.
+         *
+         * <p>Type: TEXT
+         * @see Genres#encode
+         * @see Genres#decode
+         */
+        public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+
+        /**
+         * The flag indicating whether recording of this program is prohibited.
+         *
+         * <p>A value of 1 indicates that recording of this program is prohibited and application
+         * will not schedule any recording for this program. A value of 0 indicates that the
+         * recording is not prohibited. If not specified, this value is set to 0 (not prohibited) by
+         * default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        public static final String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+
+        private Programs() {}
+
+        /** Canonical genres for TV programs. */
+        public static final class Genres {
+            /** @hide */
+            @StringDef({
+                    FAMILY_KIDS,
+                    SPORTS,
+                    SHOPPING,
+                    MOVIES,
+                    COMEDY,
+                    TRAVEL,
+                    DRAMA,
+                    EDUCATION,
+                    ANIMAL_WILDLIFE,
+                    NEWS,
+                    GAMING,
+                    ARTS,
+                    ENTERTAINMENT,
+                    LIFE_STYLE,
+                    MUSIC,
+                    PREMIER,
+                    TECH_SCIENCE,
+            })
+            @Retention(RetentionPolicy.SOURCE)
+            public @interface Genre {}
+
+            /** The genre for Family/Kids. */
+            public static final String FAMILY_KIDS = "FAMILY_KIDS";
+
+            /** The genre for Sports. */
+            public static final String SPORTS = "SPORTS";
+
+            /** The genre for Shopping. */
+            public static final String SHOPPING = "SHOPPING";
+
+            /** The genre for Movies. */
+            public static final String MOVIES = "MOVIES";
+
+            /** The genre for Comedy. */
+            public static final String COMEDY = "COMEDY";
+
+            /** The genre for Travel. */
+            public static final String TRAVEL = "TRAVEL";
+
+            /** The genre for Drama. */
+            public static final String DRAMA = "DRAMA";
+
+            /** The genre for Education. */
+            public static final String EDUCATION = "EDUCATION";
+
+            /** The genre for Animal/Wildlife. */
+            public static final String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+
+            /** The genre for News. */
+            public static final String NEWS = "NEWS";
+
+            /** The genre for Gaming. */
+            public static final String GAMING = "GAMING";
+
+            /** The genre for Arts. */
+            public static final String ARTS = "ARTS";
+
+            /** The genre for Entertainment. */
+            public static final String ENTERTAINMENT = "ENTERTAINMENT";
+
+            /** The genre for Life Style. */
+            public static final String LIFE_STYLE = "LIFE_STYLE";
+
+            /** The genre for Music. */
+            public static final String MUSIC = "MUSIC";
+
+            /** The genre for Premier. */
+            public static final String PREMIER = "PREMIER";
+
+            /** The genre for Tech/Science. */
+            public static final String TECH_SCIENCE = "TECH_SCIENCE";
+
+            private static final HashSet<String> CANONICAL_GENRES = new HashSet<>();
+            static {
+                CANONICAL_GENRES.add(FAMILY_KIDS);
+                CANONICAL_GENRES.add(SPORTS);
+                CANONICAL_GENRES.add(SHOPPING);
+                CANONICAL_GENRES.add(MOVIES);
+                CANONICAL_GENRES.add(COMEDY);
+                CANONICAL_GENRES.add(TRAVEL);
+                CANONICAL_GENRES.add(DRAMA);
+                CANONICAL_GENRES.add(EDUCATION);
+                CANONICAL_GENRES.add(ANIMAL_WILDLIFE);
+                CANONICAL_GENRES.add(NEWS);
+                CANONICAL_GENRES.add(GAMING);
+                CANONICAL_GENRES.add(ARTS);
+                CANONICAL_GENRES.add(ENTERTAINMENT);
+                CANONICAL_GENRES.add(LIFE_STYLE);
+                CANONICAL_GENRES.add(MUSIC);
+                CANONICAL_GENRES.add(PREMIER);
+                CANONICAL_GENRES.add(TECH_SCIENCE);
+            }
+
+            private static final char DOUBLE_QUOTE = '"';
+            private static final char COMMA = ',';
+            private static final String DELIMITER = ",";
+
+            private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+            private Genres() {}
+
+            /**
+             * Encodes genre strings to a text that can be put into the database.
+             *
+             * @param genres Genre strings.
+             * @return an encoded genre string that can be inserted into the
+             *         {@link #COLUMN_BROADCAST_GENRE} or {@link #COLUMN_CANONICAL_GENRE} column.
+             */
+            public static String encode(@NonNull @Genre String... genres) {
+                if (genres == null) {
+                    // MNC and before will throw a NPE.
+                    return null;
+                }
+                StringBuilder sb = new StringBuilder();
+                String separator = "";
+                for (String genre : genres) {
+                    sb.append(separator).append(encodeToCsv(genre));
+                    separator = DELIMITER;
+                }
+                return sb.toString();
+            }
+
+            private static String encodeToCsv(String genre) {
+                StringBuilder sb = new StringBuilder();
+                int length = genre.length();
+                for (int i = 0; i < length; ++i) {
+                    char c = genre.charAt(i);
+                    switch (c) {
+                        case DOUBLE_QUOTE:
+                            sb.append(DOUBLE_QUOTE);
+                            break;
+                        case COMMA:
+                            sb.append(DOUBLE_QUOTE);
+                            break;
+                    }
+                    sb.append(c);
+                }
+                return sb.toString();
+            }
+
+            /**
+             * Decodes the genre strings from the text stored in the database.
+             *
+             * @param genres The encoded genre string retrieved from the
+             *            {@link #COLUMN_BROADCAST_GENRE} or {@link #COLUMN_CANONICAL_GENRE} column.
+             * @return genre strings.
+             */
+            public static @Genre String[] decode(@NonNull String genres) {
+                if (TextUtils.isEmpty(genres)) {
+                    // MNC and before will throw a NPE for {@code null} genres.
+                    return EMPTY_STRING_ARRAY;
+                }
+                if (genres.indexOf(COMMA) == -1 && genres.indexOf(DOUBLE_QUOTE) == -1) {
+                    return new String[] {genres.trim()};
+                }
+                StringBuilder sb = new StringBuilder();
+                List<String> results = new ArrayList<>();
+                int length = genres.length();
+                boolean escape = false;
+                for (int i = 0; i < length; ++i) {
+                    char c = genres.charAt(i);
+                    switch (c) {
+                        case DOUBLE_QUOTE:
+                            if (!escape) {
+                                escape = true;
+                                continue;
+                            }
+                            break;
+                        case COMMA:
+                            if (!escape) {
+                                String string = sb.toString().trim();
+                                if (string.length() > 0) {
+                                    results.add(string);
+                                }
+                                sb = new StringBuilder();
+                                continue;
+                            }
+                            break;
+                    }
+                    sb.append(c);
+                    escape = false;
+                }
+                String string = sb.toString().trim();
+                if (string.length() > 0) {
+                    results.add(string);
+                }
+                return results.toArray(new String[results.size()]);
+            }
+
+            /**
+             * Returns whether a given text is a canonical genre defined in {@link Genres}.
+             *
+             * @param genre The name of genre to be checked.
+             * @return {@code true} if the genre is canonical, otherwise {@code false}.
+             */
+            public static boolean isCanonical(String genre) {
+                return CANONICAL_GENRES.contains(genre);
+            }
+        }
+    }
+
+    /**
+     * Column definitions for the recorded TV programs table.
+     *
+     * <p>By default, the query results will be sorted by {@link #COLUMN_START_TIME_UTC_MILLIS} in
+     * ascending order.
+     */
+    public static final class RecordedPrograms implements BaseTvColumns, ProgramColumns {
+
+        /**
+         * The content:// style URI for this table.
+         *
+         * <p>SQL selection is not supported for {@link ContentResolver#query},
+         * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+                + PATH_RECORDED_PROGRAM);
+
+        /** The MIME type of a directory of recorded TV programs. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+
+        /** The MIME type of a single recorded TV program. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+
+        /**
+         * The ID of the TV channel that provides this recorded program.
+         *
+         * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_CHANNEL_ID = "channel_id";
+
+        /**
+         * The ID of the TV input service that is associated with this recorded program.
+         *
+         * <p>Use {@link #buildInputId} to build the ID.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_INPUT_ID = "input_id";
+
+        /**
+         * The start time of the original TV program, in milliseconds since the epoch.
+         *
+         * <p>Type: INTEGER (long)
+         * @see Programs#COLUMN_START_TIME_UTC_MILLIS
+         */
+        public static final String COLUMN_START_TIME_UTC_MILLIS =
+                Programs.COLUMN_START_TIME_UTC_MILLIS;
+
+        /**
+         * The end time of the original TV program, in milliseconds since the epoch.
+         *
+         * <p>Type: INTEGER (long)
+         * @see Programs#COLUMN_END_TIME_UTC_MILLIS
+         */
+        public static final String COLUMN_END_TIME_UTC_MILLIS = Programs.COLUMN_END_TIME_UTC_MILLIS;
+
+        /**
+         * The comma-separated genre string of this recorded TV program.
+         *
+         * <p>Use the same language appeared in the underlying broadcast standard, if applicable.
+         * (For example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty. Use
+         * {@link Genres#encode Genres.encode()} to create a text that can be stored in this column.
+         * Use {@link Genres#decode Genres.decode()} to get the broadcast genre strings from the
+         * text stored in the column.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_BROADCAST_GENRE
+         */
+        public static final String COLUMN_BROADCAST_GENRE = Programs.COLUMN_BROADCAST_GENRE;
+
+        /**
+         * The URI of the recording data for this recorded program.
+         *
+         * <p>Together with {@link #COLUMN_RECORDING_DATA_BYTES}, applications can use this
+         * information to manage recording storage. The URI should indicate a file or directory with
+         * the scheme {@link android.content.ContentResolver#SCHEME_FILE}.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_RECORDING_DATA_BYTES
+         */
+        public static final String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+
+        /**
+         * The data size (in bytes) for this recorded program.
+         *
+         * <p>Together with {@link #COLUMN_RECORDING_DATA_URI}, applications can use this
+         * information to manage recording storage.
+         *
+         * <p>Type: INTEGER (long)
+         * @see #COLUMN_RECORDING_DATA_URI
+         */
+        public static final String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+
+        /**
+         * The duration (in milliseconds) of this recorded program.
+         *
+         * <p>The actual duration of the recorded program can differ from the one calculated by
+         * {@link #COLUMN_END_TIME_UTC_MILLIS} - {@link #COLUMN_START_TIME_UTC_MILLIS} as program
+         * recording can be interrupted in the middle for some reason, resulting in a partially
+         * recorded program, which is still playable.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+
+        /**
+         * The expiration time for this recorded program, in milliseconds since the epoch.
+         *
+         * <p>Recorded TV programs do not expire by default unless explicitly requested by the user
+         * or the user allows applications to delete them in order to free up disk space for future
+         * recording. However, some TV content can have expiration date set by the content provider
+         * when recorded. This field is used to indicate such a restriction.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS =
+                "recording_expire_time_utc_millis";
+
+        private RecordedPrograms() {}
+    }
+
+    /**
+     * Column definitions for the preview TV programs table.
+     */
+    public static final class PreviewPrograms implements BaseTvColumns, ProgramColumns,
+            PreviewProgramColumns {
+
+        /**
+         * The content:// style URI for this table.
+         *
+         * <p>SQL selection is not supported for {@link ContentResolver#query},
+         * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+                + PATH_PREVIEW_PROGRAM);
+
+        /** The MIME type of a directory of preview TV programs. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
+
+        /** The MIME type of a single preview TV program. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
+
+        /**
+         * The ID of the TV channel that provides this TV program.
+         *
+         * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_CHANNEL_ID = "channel_id";
+
+        /**
+         * The weight of the preview program within the channel.
+         *
+         * <p>The UI may choose to show this item in a different position in the channel row.
+         * A larger weight value means the program is more important than other programs having
+         * smaller weight values. The value is relevant for the preview programs in the same
+         * channel. This is only relevant to {@link Channels#TYPE_PREVIEW}.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_WEIGHT = "weight";
+
+        private PreviewPrograms() {}
+    }
+
+    /**
+     * Column definitions for the "watch next" TV programs table.
+     */
+    public static final class WatchNextPrograms implements BaseTvColumns, ProgramColumns,
+            PreviewProgramColumns {
+
+        /**
+         * The content:// style URI for this table.
+         *
+         * <p>SQL selection is not supported for {@link ContentResolver#query},
+         * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+                + PATH_WATCH_NEXT_PROGRAM);
+
+        /** The MIME type of a directory of "watch next" TV programs. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program";
+
+        /** The MIME type of a single preview TV program. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
+
+        /** @hide */
+        @IntDef({
+                WATCH_NEXT_TYPE_CONTINUE,
+                WATCH_NEXT_TYPE_NEXT,
+                WATCH_NEXT_TYPE_NEW,
+                WATCH_NEXT_TYPE_WATCHLIST,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(LIBRARY_GROUP)
+        public @interface WatchNextType {}
+
+        /**
+         * The watch next type for CONTINUE. Use this type when the user has already watched more
+         * than 1 minute of this content.
+         *
+         * @see #COLUMN_WATCH_NEXT_TYPE
+         */
+        public static final int WATCH_NEXT_TYPE_CONTINUE = 0;
+
+        /**
+         * The watch next type for NEXT. Use this type when the user has watched one or more
+         * complete episodes from some episodic content, but there remains more than one episode
+         * remaining or there is one last episode remaining, but it is not “new” in that it was
+         * released before the user started watching the show.
+         *
+         * @see #COLUMN_WATCH_NEXT_TYPE
+         */
+        public static final int WATCH_NEXT_TYPE_NEXT = 1;
+
+        /**
+         * The watch next type for NEW. Use this type when the user had watched all of the available
+         * episodes from some episodic content, but a new episode became available since the user
+         * started watching the first episode and now there is exactly one unwatched episode. This
+         * could also work for recorded events in a series e.g. soccer matches or football games.
+         *
+         * @see #COLUMN_WATCH_NEXT_TYPE
+         */
+        public static final int WATCH_NEXT_TYPE_NEW = 2;
+
+        /**
+         * The watch next type for WATCHLIST. Use this type when the user has elected to explicitly
+         * add a movie, event or series to a “watchlist” as a manual way of curating what they
+         * want to watch next.
+         *
+         * @see #COLUMN_WATCH_NEXT_TYPE
+         */
+        public static final int WATCH_NEXT_TYPE_WATCHLIST = 3;
+
+        /**
+         * The "watch next" type of this program content.
+         *
+         * <p>The value should match one of the followings:
+         * {@link #WATCH_NEXT_TYPE_CONTINUE},
+         * {@link #WATCH_NEXT_TYPE_NEXT},
+         * {@link #WATCH_NEXT_TYPE_NEW}, and
+         * {@link #WATCH_NEXT_TYPE_WATCHLIST}.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+
+        /**
+         * The last UTC time that the user engaged in this TV program, in milliseconds since the
+         * epoch. This is a hint for the application that is used for ordering of "watch next"
+         * programs.
+         *
+         * <p>The meaning of the value varies depending on the {@link #COLUMN_WATCH_NEXT_TYPE}:
+         * <ul>
+         *     <li>{@link #WATCH_NEXT_TYPE_CONTINUE}: the date that the user was last watching the
+         *     content.</li>
+         *     <li>{@link #WATCH_NEXT_TYPE_NEXT}: the date of the last episode watched.</li>
+         *     <li>{@link #WATCH_NEXT_TYPE_NEW}: the release date of the new episode.</li>
+         *     <li>{@link #WATCH_NEXT_TYPE_WATCHLIST}: the date the item was added to the Watchlist.
+         *     </li>
+         * </ul>
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS =
+                "last_engagement_time_utc_millis";
+
+        private WatchNextPrograms() {}
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/TvContractUtils.java b/tv-provider/src/android/support/media/tv/TvContractUtils.java
new file mode 100644
index 0000000..edcd917
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/TvContractUtils.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.media.tv.TvContentRating;
+import android.support.annotation.RestrictTo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Static helper methods for working with {@link android.media.tv.TvContract}.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class TvContractUtils {
+
+    static final TvContentRating[] EMPTY = new TvContentRating[0];
+
+    private static final String TAG = "TvContractUtils";
+    private static final boolean DEBUG = false;
+    private static final String DELIMITER = ",";
+
+    /**
+     * Parses a string of comma-separated ratings into an array of {@link TvContentRating}.
+     * <p>Invalid strings are droppped. Duplicates are not removed. The order is preserved.</p>
+     *
+     * @param commaSeparatedRatings String containing various ratings, separated by commas.
+     * @return An array of TvContentRatings.
+     */
+    public static TvContentRating[] stringToContentRatings(String commaSeparatedRatings) {
+        if (TextUtils.isEmpty(commaSeparatedRatings)) {
+            return EMPTY;
+        }
+        String[] ratings = commaSeparatedRatings.split("\\s*,\\s*");
+        List<TvContentRating> contentRatings = new ArrayList<>(ratings.length);
+        for (String rating : ratings) {
+            try {
+                contentRatings.add(TvContentRating.unflattenFromString(rating));
+            } catch (IllegalArgumentException e) {
+                Log.w(TAG, "Can't parse the content rating: '" + rating + "', skipping", e);
+            }
+        }
+        return contentRatings.size() == 0 ? EMPTY
+                : contentRatings.toArray(new TvContentRating[contentRatings.size()]);
+    }
+
+    /**
+     * Flattens an array of {@link TvContentRating} into a String to be inserted into a database.
+     *
+     * @param contentRatings An array of TvContentRatings.
+     * @return A comma-separated String of ratings.
+     */
+    public static String contentRatingsToString(TvContentRating[] contentRatings) {
+        if (contentRatings == null || contentRatings.length == 0) {
+            return null;
+        }
+        StringBuilder ratings = new StringBuilder(contentRatings[0].flattenToString());
+        for (int i = 1; i < contentRatings.length; ++i) {
+            ratings.append(DELIMITER);
+            ratings.append(contentRatings[i].flattenToString());
+        }
+        return ratings.toString();
+    }
+
+    /**
+     * Parses a string of comma-separated audio languages into an array of audio language strings.
+     *
+     * @param commaSeparatedString String containing audio languages, separated by commas.
+     * @return An array of audio language.
+     */
+    public static String[] stringToAudioLanguages(String commaSeparatedString) {
+        if (TextUtils.isEmpty(commaSeparatedString)) {
+            return null;
+        }
+        return commaSeparatedString.split("\\s*,\\s*");
+    }
+
+    /**
+     * Concatenate an array of audio languages into a String to be inserted into a database.
+     *
+     * @param audioLanguages An array of audio languages.
+     * @return A comma-separated String of audio languages.
+     */
+    public static String audioLanguagesToString(String[] audioLanguages) {
+        if (audioLanguages == null || audioLanguages.length == 0) {
+            return null;
+        }
+        StringBuilder ratings = new StringBuilder(audioLanguages[0]);
+        for (int i = 1; i < audioLanguages.length; ++i) {
+            ratings.append(DELIMITER);
+            ratings.append(audioLanguages[i]);
+        }
+        return ratings.toString();
+    }
+
+    private TvContractUtils() {
+    }
+}
diff --git a/tv-provider/src/android/support/media/tv/WatchNextProgram.java b/tv-provider/src/android/support/media/tv/WatchNextProgram.java
new file mode 100644
index 0000000..f466584
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/WatchNextProgram.java
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;  // For javadoc gen of super class
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.media.tv.TvContractCompat.PreviewPrograms;  // For javadoc gen of super class
+import android.support.media.tv.TvContractCompat.Programs;  // For javadoc gen of super class
+import android.support.media.tv.TvContractCompat.Programs.Genres;  // For javadoc gen of super class
+import android.support.media.tv.TvContractCompat.WatchNextPrograms;
+import android.support.media.tv.TvContractCompat.WatchNextPrograms.WatchNextType;
+
+/**
+ * A convenience class to access {@link WatchNextPrograms} entries in the system content
+ * provider.
+ *
+ * <p>This class makes it easy to insert or retrieve a program from the system content provider,
+ * which is defined in {@link TvContractCompat}.
+ *
+ * <p>Usage example when inserting a "watch next" program:
+ * <pre>
+ * WatchNextProgram watchNextProgram = new WatchNextProgram.Builder()
+ *         .setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
+ *         .setType(PreviewPrograms.TYPE_MOVIE)
+ *         .setTitle("Program Title")
+ *         .setDescription("Program Description")
+ *         .setPosterArtUri(Uri.parse("http://example.com/poster_art.png"))
+ *         // Set more attributes...
+ *         .build();
+ * Uri watchNextProgramUri = getContentResolver().insert(WatchNextPrograms.CONTENT_URI,
+ *         watchNextProgram.toContentValues());
+ * </pre>
+ *
+ * <p>Usage example when retrieving a "watch next" program:
+ * <pre>
+ * WatchNextProgram watchNextProgram;
+ * try (Cursor cursor = resolver.query(watchNextProgramUri, null, null, null, null)) {
+ *     if (cursor != null && cursor.getCount() != 0) {
+ *         cursor.moveToNext();
+ *         watchNextProgram = WatchNextProgram.fromCursor(cursor);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Usage example when updating an existing "watch next" program:
+ * <pre>
+ * WatchNextProgram updatedProgram = new WatchNextProgram.Builder(watchNextProgram)
+ *         .setLastEngagementTimeUtcMillis(System.currentTimeMillis())
+ *         .build();
+ * getContentResolver().update(TvContractCompat.buildWatchNextProgramUri(updatedProgram.getId()),
+ *         updatedProgram.toContentValues(), null, null);
+ * </pre>
+ *
+ * <p>Usage example when deleting a "watch next" program:
+ * <pre>
+ * getContentResolver().delete(TvContractCompat.buildWatchNextProgramUri(existingProgram.getId()),
+ *         null, null);
+ * </pre>
+ */
+@TargetApi(26)
+public final class WatchNextProgram extends BasePreviewProgram {
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static final String[] PROJECTION = getProjection();
+
+    private static final long INVALID_LONG_VALUE = -1;
+    private static final int INVALID_INT_VALUE = -1;
+
+    private WatchNextProgram(Builder builder) {
+        super(builder);
+    }
+
+    /**
+     * @return The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for the program.
+     */
+    public @WatchNextType int getWatchNextType() {
+        Integer i = mValues.getAsInteger(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
+        return i == null ? INVALID_INT_VALUE : i;
+    }
+
+    /**
+     * @return The value of {@link WatchNextPrograms#COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS} for the
+     * program.
+     */
+    public long getLastEngagementTimeUtcMillis() {
+        Long l = mValues.getAsLong(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS);
+        return l == null ? INVALID_LONG_VALUE : l;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof WatchNextProgram)) {
+            return false;
+        }
+        return mValues.equals(((WatchNextProgram) other).mValues);
+    }
+
+    @Override
+    public String toString() {
+        return "WatchNextProgram{" + mValues.toString() + "}";
+    }
+
+    /**
+     * @return The fields of the Program in the ContentValues format to be easily inserted into the
+     * TV Input Framework database.
+     */
+    @Override
+    public ContentValues toContentValues() {
+        return toContentValues(false);
+    }
+
+    /**
+     * Returns fields of the WatchNextProgram in the ContentValues format to be easily inserted
+     * into the TV Input Framework database.
+     *
+     * @param includeProtectedFields Whether the fields protected by system is included or not.
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public ContentValues toContentValues(boolean includeProtectedFields) {
+        ContentValues values = super.toContentValues(includeProtectedFields);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            values.remove(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
+            values.remove(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS);
+        }
+        return values;
+    }
+
+    /**
+     * Creates a WatchNextProgram object from a cursor including the fields defined in
+     * {@link WatchNextPrograms}.
+     *
+     * @param cursor A row from the TV Input Framework database.
+     * @return A Program with the values taken from the cursor.
+     */
+    public static WatchNextProgram fromCursor(Cursor cursor) {
+        // TODO: Add additional API which does not use costly getColumnIndex().
+        Builder builder = new Builder();
+        BasePreviewProgram.setFieldsFromCursor(cursor, builder);
+        int index;
+        if ((index = cursor.getColumnIndex(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setWatchNextType(cursor.getInt(index));
+        }
+        if ((index = cursor.getColumnIndex(
+                WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS)) >= 0
+                && !cursor.isNull(index)) {
+            builder.setLastEngagementTimeUtcMillis(cursor.getLong(index));
+        }
+        return builder.build();
+    }
+
+    private static String[] getProjection() {
+        String[] oColumns = new String[] {
+                WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE,
+                WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS,
+        };
+        return CollectionUtils.concatAll(BasePreviewProgram.PROJECTION, oColumns);
+    }
+
+    /**
+     * This Builder class simplifies the creation of a {@link WatchNextProgram} object.
+     */
+    public static final class Builder extends BasePreviewProgram.Builder<Builder> {
+
+        /**
+         * Creates a new Builder object.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a new Builder object with values copied from another Program.
+         * @param other The Program you're copying from.
+         */
+        public Builder(WatchNextProgram other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        /**
+         * Sets the "watch next" type of this program content.
+         *
+         * <p>The value should match one of the followings:
+         * {@link WatchNextPrograms#WATCH_NEXT_TYPE_CONTINUE},
+         * {@link WatchNextPrograms#WATCH_NEXT_TYPE_NEXT}, and
+         * {@link WatchNextPrograms#WATCH_NEXT_TYPE_NEW}.
+         *
+         * @param watchNextType The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for
+         *                      the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setWatchNextType(@WatchNextType int watchNextType) {
+            mValues.put(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE, watchNextType);
+            return this;
+        }
+
+        /**
+         * Sets the time when the program is going to begin in milliseconds since the epoch.
+         *
+         * @param lastEngagementTimeUtcMillis The value of
+         * {@link WatchNextPrograms#COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS} for the program.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setLastEngagementTimeUtcMillis(long lastEngagementTimeUtcMillis) {
+            mValues.put(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS,
+                    lastEngagementTimeUtcMillis);
+            return this;
+        }
+
+        /**
+         * @return A new Program with values supplied by the Builder.
+         */
+        public WatchNextProgram build() {
+            return new WatchNextProgram(this);
+        }
+    }
+}
diff --git a/tv-provider/tests/AndroidManifest.xml b/tv-provider/tests/AndroidManifest.xml
new file mode 100644
index 0000000..0cba0d7
--- /dev/null
+++ b/tv-provider/tests/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.media.tv.test">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
+</manifest>
diff --git a/tv-provider/tests/NO_DOCS b/tv-provider/tests/NO_DOCS
new file mode 100644
index 0000000..092a39c
--- /dev/null
+++ b/tv-provider/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/tv-provider/tests/res/drawable/test_icon.png b/tv-provider/tests/res/drawable/test_icon.png
new file mode 100644
index 0000000..d6a6ea4
--- /dev/null
+++ b/tv-provider/tests/res/drawable/test_icon.png
Binary files differ
diff --git a/tv-provider/tests/src/android/support/media/tv/ChannelLogoUtilsTest.java b/tv-provider/tests/src/android/support/media/tv/ChannelLogoUtilsTest.java
new file mode 100644
index 0000000..cd6a0e9
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/ChannelLogoUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.support.media.tv.test.R;
+import android.test.AndroidTestCase;
+
+public class ChannelLogoUtilsTest extends AndroidTestCase {
+    private static final String FAKE_INPUT_ID = "ChannelLogoUtils.test";
+
+    private ContentResolver mContentResolver;
+    private Uri mChannelUri;
+    private long mChannelId;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContentResolver = getContext().getContentResolver();
+        ContentValues contentValues = new Channel.Builder()
+                .setInputId(FAKE_INPUT_ID)
+                .setType(TvContractCompat.Channels.TYPE_OTHER).build().toContentValues();
+        mChannelUri = mContentResolver.insert(TvContract.Channels.CONTENT_URI, contentValues);
+        mChannelId = ContentUris.parseId(mChannelUri);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mContentResolver.delete(mChannelUri, null, null);
+    }
+
+    public void testStoreChannelLogo_fromBitmap() {
+        assertNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+        Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.test_icon);
+        assertNotNull(logo);
+        assertTrue(ChannelLogoUtils.storeChannelLogo(getContext(), mChannelId, logo));
+        // Workaround: the file status is not consistent between openInputStream/openOutputStream,
+        // wait 10 secs to make sure that the logo file is written into the disk.
+        SystemClock.sleep(10000);
+        assertNotNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+    }
+
+    public void testStoreChannelLogo_fromResUri() {
+        assertNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+        int resId = R.drawable.test_icon;
+        Resources res = getContext().getResources();
+        Uri logoUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                .authority(res.getResourcePackageName(resId))
+                .appendPath(res.getResourceTypeName(resId))
+                .appendPath(res.getResourceEntryName(resId))
+                .build();
+        assertTrue(ChannelLogoUtils.storeChannelLogo(getContext(), mChannelId, logoUri));
+        // Workaround: the file status is not consistent between openInputStream/openOutputStream,
+        // wait 10 secs to make sure that the logo file is written into the disk.
+        SystemClock.sleep(10000);
+        assertNotNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+    }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/ChannelTest.java b/tv-provider/tests/src/android/support/media/tv/ChannelTest.java
new file mode 100644
index 0000000..82d3a61
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/ChannelTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Build;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+/**
+ * Tests that channels can be created using the Builder pattern and correctly obtain
+ * values from them
+ */
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+public class ChannelTest extends TestCase {
+    private static final String KEY_SPLASHSCREEN = "splashscreen";
+    private static final String KEY_PREMIUM_CHANNEL = "premium";
+    private static final String SPLASHSCREEN_URL = "http://example.com/splashscreen.jpg";
+
+    @Override
+    protected void tearDown() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        resolver.delete(Channels.CONTENT_URI, null, null);
+    }
+
+    @Test
+    public void testEmptyChannel() {
+        Channel emptyChannel = new Channel.Builder()
+                .build();
+        ContentValues contentValues = emptyChannel.toContentValues(true);
+        compareChannel(emptyChannel, Channel.fromCursor(getChannelCursor(contentValues)), true);
+    }
+
+    @Test
+    public void testSampleChannel() {
+        // Tests cloning and database I/O of a channel with some defined and some undefined
+        // values.
+        Channel sampleChannel = new Channel.Builder()
+                .setDisplayName("Google")
+                .setDisplayNumber("3")
+                .setDescription("This is a sample channel")
+                .setOriginalNetworkId(1)
+                .setAppLinkIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setOriginalNetworkId(0)
+                .build();
+        ContentValues contentValues = sampleChannel.toContentValues(true);
+        compareChannel(sampleChannel, Channel.fromCursor(getChannelCursor(contentValues)), true);
+
+        Channel clonedSampleChannel = new Channel.Builder(sampleChannel).build();
+        compareChannel(sampleChannel, clonedSampleChannel, true);
+    }
+
+    @Test
+    public void testFullyPopulatedChannel() {
+        Channel fullyPopulatedChannel = createFullyPopulatedChannel();
+        ContentValues contentValues = fullyPopulatedChannel.toContentValues(true);
+        compareChannel(fullyPopulatedChannel, Channel.fromCursor(getChannelCursor(contentValues)),
+                true);
+
+        Channel clonedFullyPopulatedChannel = new Channel.Builder(fullyPopulatedChannel).build();
+        compareChannel(fullyPopulatedChannel, clonedFullyPopulatedChannel, true);
+    }
+
+    @Test
+    public void testChannelWithSystemContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        Channel fullyPopulatedChannel = createFullyPopulatedChannel();
+        ContentValues contentValues = fullyPopulatedChannel.toContentValues();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri channelUri = resolver.insert(Channels.CONTENT_URI, contentValues);
+        assertNotNull(channelUri);
+
+        Channel channelFromSystemDb = loadChannelFromContentProvider(resolver, channelUri);
+        compareChannel(fullyPopulatedChannel, channelFromSystemDb, false);
+    }
+
+    @Test
+    public void testChannelUpdateWithContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+
+        Channel fullyPopulatedChannel = createFullyPopulatedChannel();
+        ContentValues contentValues = fullyPopulatedChannel.toContentValues();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri channelUri = resolver.insert(Channels.CONTENT_URI, contentValues);
+        assertNotNull(channelUri);
+
+        Channel channelFromSystemDb = loadChannelFromContentProvider(resolver, channelUri);
+        compareChannel(fullyPopulatedChannel, channelFromSystemDb, false);
+
+        // Update a field from a fully loaded channel.
+        Channel updatedChannel = new Channel.Builder(channelFromSystemDb)
+                .setDescription("new description").build();
+        assertEquals(1, resolver.update(channelUri, updatedChannel.toContentValues(), null, null));
+        channelFromSystemDb = loadChannelFromContentProvider(resolver, channelUri);
+        compareChannel(updatedChannel, channelFromSystemDb, false);
+
+        // Update a field with null from a fully loaded channel.
+        updatedChannel = new Channel.Builder(updatedChannel)
+                .setAppLinkText(null).build();
+        assertEquals(1, resolver.update(
+                channelUri, updatedChannel.toContentValues(), null, null));
+        channelFromSystemDb = loadChannelFromContentProvider(resolver, channelUri);
+        compareChannel(updatedChannel, channelFromSystemDb, false);
+
+        // Update a field without referencing fully channel.
+        ContentValues values = new Channel.Builder().setDisplayName("abc").build()
+                .toContentValues();
+        assertEquals(1, values.size());
+        assertEquals(1, resolver.update(channelUri, values, null, null));
+        channelFromSystemDb = loadChannelFromContentProvider(resolver, channelUri);
+        Channel expectedChannel = new Channel.Builder(channelFromSystemDb)
+                .setDisplayName("abc").build();
+        compareChannel(expectedChannel, channelFromSystemDb, false);
+    }
+
+    @Test
+    public void testChannelEquals() {
+        assertEquals(createFullyPopulatedChannel(), createFullyPopulatedChannel());
+    }
+
+
+    private static Channel loadChannelFromContentProvider(
+            ContentResolver resolver, Uri channelUri) {
+        try (Cursor cursor = resolver.query(channelUri, null, null, null, null)) {
+            assertNotNull(cursor);
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            return Channel.fromCursor(cursor);
+        }
+    }
+
+    private static Channel createFullyPopulatedChannel() {
+        return new Channel.Builder()
+                .setAppLinkColor(0x00FF0000)
+                .setAppLinkIconUri(Uri.parse("http://example.com/icon.png"))
+                .setAppLinkIntent(new Intent())
+                .setAppLinkPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setAppLinkText("Open an intent")
+                .setDescription("Channel description")
+                .setDisplayName("Display Name")
+                .setDisplayNumber("100")
+                .setInputId("TestInputService")
+                .setNetworkAffiliation("Network Affiliation")
+                .setOriginalNetworkId(2)
+                .setPackageName("android.support.media.tv.test")
+                .setSearchable(false)
+                .setServiceId(3)
+                .setTransportStreamId(4)
+                .setType(TvContractCompat.Channels.TYPE_PREVIEW)
+                .setServiceType(TvContractCompat.Channels.SERVICE_TYPE_AUDIO_VIDEO)
+                .setVideoFormat(TvContractCompat.Channels.VIDEO_FORMAT_240P)
+                .setInternalProviderFlag1(0x4)
+                .setInternalProviderFlag2(0x3)
+                .setInternalProviderFlag3(0x2)
+                .setInternalProviderFlag4(0x1)
+                .setInternalProviderId("Internal Provider")
+                .setTransient(true)
+                .setBrowsable(true)
+                .setLocked(true)
+                .setSystemApproved(true)
+                .build();
+    }
+
+    private static void compareChannel(Channel channelA, Channel channelB,
+            boolean includeIdAndProtectedFields) {
+        assertEquals(channelA.isSearchable(), channelB.isSearchable());
+        assertEquals(channelA.getDescription(), channelB.getDescription());
+        assertEquals(channelA.getDisplayName(), channelB.getDisplayName());
+        assertEquals(channelA.getDisplayNumber(), channelB.getDisplayNumber());
+        assertEquals(channelA.getInputId(), channelB.getInputId());
+        assertEquals(channelA.getNetworkAffiliation(), channelB.getNetworkAffiliation());
+        assertEquals(channelA.getOriginalNetworkId(), channelB.getOriginalNetworkId());
+        assertEquals(channelA.getPackageName(), channelB.getPackageName());
+        assertEquals(channelA.getServiceId(), channelB.getServiceId());
+        assertEquals(channelA.getServiceType(), channelB.getServiceType());
+        assertEquals(channelA.getTransportStreamId(), channelB.getTransportStreamId());
+        assertEquals(channelA.getType(), channelB.getType());
+        assertEquals(channelA.getVideoFormat(), channelB.getVideoFormat());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            assertEquals(channelA.getAppLinkColor(), channelB.getAppLinkColor());
+            assertEquals(channelA.getAppLinkIconUri(), channelB.getAppLinkIconUri());
+            assertEquals(channelA.getAppLinkIntentUri(), channelB.getAppLinkIntentUri());
+            assertEquals(channelA.getAppLinkPosterArtUri(), channelB.getAppLinkPosterArtUri());
+            assertEquals(channelA.getAppLinkText(), channelB.getAppLinkText());
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            assertEquals(channelA.getInternalProviderId(), channelB.getInternalProviderId());
+            assertEquals(channelA.isTransient(), channelB.isTransient());
+        }
+        if (includeIdAndProtectedFields) {
+            // Skip row ID since the one from system DB has the valid ID while the other does not.
+            assertEquals(channelA.getId(), channelB.getId());
+            // When we insert a channel using toContentValues() to the system, we drop some
+            // protected fields since they only can be modified by system apps.
+            assertEquals(channelA.isBrowsable(), channelB.isBrowsable());
+            assertEquals(channelA.isLocked(), channelB.isLocked());
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                assertEquals(channelA.isSystemApproved(), channelB.isSystemApproved());
+            }
+            assertEquals(channelA.toContentValues(), channelB.toContentValues());
+        }
+    }
+
+    private static MatrixCursor getChannelCursor(ContentValues contentValues) {
+        String[] cols = Channel.PROJECTION;
+        MatrixCursor cursor = new MatrixCursor(cols);
+        MatrixCursor.RowBuilder builder = cursor.newRow();
+        for (String col : cols) {
+            if (col != null) {
+                builder.add(col, contentValues.get(col));
+            }
+        }
+        cursor.moveToFirst();
+        return cursor;
+    }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java b/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java
new file mode 100644
index 0000000..e222e40
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.media.tv.TvContentRating;
+import android.net.Uri;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.TvContractCompat.PreviewPrograms;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * Tests that preview programs can be created using the Builder pattern and correctly obtain
+ * values from them.
+ */
+@SmallTest
+@SdkSuppress(minSdkVersion = 26)
+@TargetApi(26)
+public class PreviewProgramTest extends TestCase {
+
+    @Override
+    protected void tearDown() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        resolver.delete(Channels.CONTENT_URI, null, null);
+    }
+
+    @Test
+    public void testEmptyPreviewProgram() {
+        PreviewProgram emptyProgram = new PreviewProgram.Builder().build();
+        ContentValues contentValues = emptyProgram.toContentValues();
+        compareProgram(emptyProgram,
+                PreviewProgram.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)),
+                true);
+    }
+
+    @Test
+    public void testSampleProgram() {
+        PreviewProgram sampleProgram = new PreviewProgram.Builder()
+                .setPackageName("My package")
+                .setTitle("Program Title")
+                .setDescription("This is a sample program")
+                .setEpisodeNumber(5)
+                .setSeasonNumber("The Final Season", 7)
+                .setThumbnailUri(Uri.parse("http://www.example.com/programs/poster.png"))
+                .setChannelId(3)
+                .setWeight(70)
+                .build();
+        ContentValues contentValues = sampleProgram.toContentValues(true);
+        compareProgram(sampleProgram,
+                PreviewProgram.fromCursor(
+                        getProgramCursor(PreviewProgram.PROJECTION, contentValues)), true);
+
+        PreviewProgram clonedSampleProgram = new PreviewProgram.Builder(sampleProgram).build();
+        compareProgram(sampleProgram, clonedSampleProgram, true);
+    }
+
+    @Test
+    public void testFullyPopulatedPreviewProgram() {
+        PreviewProgram fullyPopulatedProgram = createFullyPopulatedPreviewProgram(3);
+        ContentValues contentValues = fullyPopulatedProgram.toContentValues(true);
+        compareProgram(fullyPopulatedProgram,
+                PreviewProgram.fromCursor(
+                        getProgramCursor(PreviewProgram.PROJECTION, contentValues)), true);
+
+        PreviewProgram clonedFullyPopulatedProgram =
+                new PreviewProgram.Builder(fullyPopulatedProgram).build();
+        compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true);
+    }
+
+    @Test
+    public void testPreviewProgramWithSystemContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        Channel channel = new Channel.Builder()
+                .setInputId("TestInputService")
+                .setType(TvContractCompat.Channels.TYPE_PREVIEW)
+                .build();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues());
+        assertNotNull(channelUri);
+
+        PreviewProgram fullyPopulatedProgram = createFullyPopulatedPreviewProgram(
+                ContentUris.parseId(channelUri));
+        Uri previewProgramUri = resolver.insert(PreviewPrograms.CONTENT_URI,
+                fullyPopulatedProgram.toContentValues());
+
+        PreviewProgram programFromSystemDb =
+                loadPreviewProgramFromContentProvider(resolver, previewProgramUri);
+        compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+    }
+
+    @Test
+    public void testPreviewProgramUpdateWithContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        Channel channel = new Channel.Builder()
+                .setInputId("TestInputService")
+                .setType(TvContractCompat.Channels.TYPE_PREVIEW)
+                .build();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues());
+        assertNotNull(channelUri);
+
+        PreviewProgram fullyPopulatedProgram = createFullyPopulatedPreviewProgram(
+                ContentUris.parseId(channelUri));
+        Uri previewProgramUri = resolver.insert(PreviewPrograms.CONTENT_URI,
+                fullyPopulatedProgram.toContentValues());
+
+        PreviewProgram programFromSystemDb = loadPreviewProgramFromContentProvider(
+                resolver, previewProgramUri);
+        compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+
+        // Update a field from a fully loaded preview program.
+        PreviewProgram updatedProgram = new PreviewProgram.Builder(programFromSystemDb)
+                .setInteractionCount(programFromSystemDb.getInteractionCount() + 1).build();
+        assertEquals(1, resolver.update(
+                previewProgramUri, updatedProgram.toContentValues(), null, null));
+        programFromSystemDb = loadPreviewProgramFromContentProvider(resolver, previewProgramUri);
+        compareProgram(updatedProgram, programFromSystemDb, false);
+
+        // Update a field with null from a fully loaded preview program.
+        updatedProgram = new PreviewProgram.Builder(updatedProgram)
+                .setLongDescription(null).build();
+        assertEquals(1, resolver.update(
+                previewProgramUri, updatedProgram.toContentValues(), null, null));
+        programFromSystemDb = loadPreviewProgramFromContentProvider(resolver, previewProgramUri);
+        compareProgram(updatedProgram, programFromSystemDb, false);
+
+        // Update a field without referencing fully loaded preview program.
+        ContentValues values = new PreviewProgram.Builder().setInteractionCount(1).build()
+                .toContentValues();
+        assertEquals(1, values.size());
+        assertEquals(1, resolver.update(previewProgramUri, values, null, null));
+        programFromSystemDb = loadPreviewProgramFromContentProvider(resolver, previewProgramUri);
+        PreviewProgram expectedProgram = new PreviewProgram.Builder(programFromSystemDb)
+                .setInteractionCount(1).build();
+        compareProgram(expectedProgram, programFromSystemDb, false);
+    }
+
+    @Test
+    public void testPreviewProgramEquals() {
+        assertEquals(createFullyPopulatedPreviewProgram(1), createFullyPopulatedPreviewProgram(1));
+    }
+
+    private static PreviewProgram loadPreviewProgramFromContentProvider(
+            ContentResolver resolver, Uri previewProgramUri) {
+        try (Cursor cursor = resolver.query(previewProgramUri, null, null, null, null)) {
+            assertNotNull(cursor);
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            return PreviewProgram.fromCursor(cursor);
+        }
+    }
+
+    @Test
+    public void testPreviewProgramWithPartialData() {
+        PreviewProgram previewProgram = new PreviewProgram.Builder()
+                .setChannelId(3)
+                .setWeight(100)
+                .setInternalProviderId("ID-4321")
+                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+                .setLastPlaybackPositionMillis(0)
+                .setDurationMillis(60 * 1000)
+                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setTransient(false)
+                .setType(PreviewPrograms.TYPE_TV_EPISODE)
+                .setPosterArtAspectRatio(PreviewPrograms.ASPECT_RATIO_3_2)
+                .setThumbnailAspectRatio(PreviewPrograms.ASPECT_RATIO_16_9)
+                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+                .setAvailability(PreviewPrograms.AVAILABILITY_FREE_WITH_SUBSCRIPTION)
+                .setStartingPrice("9.99 USD")
+                .setOfferPrice("3.99 USD")
+                .setReleaseDate(new Date(Date.UTC(97, 2, 8, 9, 30, 59)))
+                .setLive(false)
+                .setInteractionType(PreviewPrograms.INTERACTION_TYPE_VIEWS)
+                .setInteractionCount(99200)
+                .setAuthor("author_name")
+                .setReviewRatingStyle(PreviewPrograms.REVIEW_RATING_STYLE_PERCENTAGE)
+                .setReviewRating("83.9")
+                .setId(10)
+                .setTitle("Recommended Video 1")
+                .setDescription("You should watch this!")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setInternalProviderFlag2(0x0010010084108410L)
+                .build();
+
+        String[] partialProjection = {
+                PreviewPrograms._ID,
+                PreviewPrograms.COLUMN_CHANNEL_ID,
+                PreviewPrograms.COLUMN_TITLE,
+                PreviewPrograms.COLUMN_SHORT_DESCRIPTION,
+                PreviewPrograms.COLUMN_POSTER_ART_URI,
+                PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+                PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID,
+                PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI,
+                PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS,
+                PreviewPrograms.COLUMN_DURATION_MILLIS,
+                PreviewPrograms.COLUMN_INTENT_URI,
+                PreviewPrograms.COLUMN_WEIGHT,
+                PreviewPrograms.COLUMN_TRANSIENT,
+                PreviewPrograms.COLUMN_TYPE,
+                PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO,
+                PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO,
+                PreviewPrograms.COLUMN_LOGO_URI,
+                PreviewPrograms.COLUMN_AVAILABILITY,
+                PreviewPrograms.COLUMN_STARTING_PRICE,
+                PreviewPrograms.COLUMN_OFFER_PRICE,
+                PreviewPrograms.COLUMN_RELEASE_DATE,
+                PreviewPrograms.COLUMN_ITEM_COUNT,
+                PreviewPrograms.COLUMN_LIVE,
+                PreviewPrograms.COLUMN_INTERACTION_TYPE,
+                PreviewPrograms.COLUMN_INTERACTION_COUNT,
+                PreviewPrograms.COLUMN_AUTHOR,
+                PreviewPrograms.COLUMN_REVIEW_RATING_STYLE,
+                PreviewPrograms.COLUMN_REVIEW_RATING,
+        };
+
+        ContentValues contentValues = previewProgram.toContentValues(true);
+        compareProgram(previewProgram,
+                PreviewProgram.fromCursor(getProgramCursor(partialProjection, contentValues)),
+                true);
+
+        PreviewProgram clonedFullyPopulatedProgram =
+                new PreviewProgram.Builder(previewProgram).build();
+        compareProgram(previewProgram, clonedFullyPopulatedProgram, true);
+    }
+
+    private static PreviewProgram createFullyPopulatedPreviewProgram(long channelId) {
+        return new PreviewProgram.Builder()
+                .setTitle("Google")
+                .setInternalProviderId("ID-4321")
+                .setChannelId(channelId)
+                .setWeight(100)
+                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+                .setLastPlaybackPositionMillis(0)
+                .setDurationMillis(60 * 1000)
+                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setTransient(false)
+                .setType(PreviewPrograms.TYPE_MOVIE)
+                .setPosterArtAspectRatio(PreviewPrograms.ASPECT_RATIO_2_3)
+                .setThumbnailAspectRatio(PreviewPrograms.ASPECT_RATIO_16_9)
+                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+                .setAvailability(PreviewPrograms.AVAILABILITY_AVAILABLE)
+                .setStartingPrice("12.99 USD")
+                .setOfferPrice("4.99 USD")
+                .setReleaseDate("1997")
+                .setItemCount(3)
+                .setLive(false)
+                .setInteractionType(PreviewPrograms.INTERACTION_TYPE_LIKES)
+                .setInteractionCount(10200)
+                .setAuthor("author_name")
+                .setReviewRatingStyle(PreviewPrograms.REVIEW_RATING_STYLE_STARS)
+                .setReviewRating("4.5")
+                .setSearchable(false)
+                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+                .setAudioLanguages(new String [] {"eng", "kor"})
+                .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
+                .setContentRatings(new TvContentRating[] {
+                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+                .setDescription("This is a sample program")
+                .setEpisodeNumber("Pilot", 0)
+                .setEpisodeTitle("Hello World")
+                .setLongDescription("This is a longer description than the previous description")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setSeasonNumber("The Final Season", 7)
+                .setSeasonTitle("The Final Season")
+                .setVideoHeight(1080)
+                .setVideoWidth(1920)
+                .setInternalProviderFlag1(0x4)
+                .setInternalProviderFlag2(0x3)
+                .setInternalProviderFlag3(0x2)
+                .setInternalProviderFlag4(0x1)
+                .setBrowsable(true)
+                .setContentId("CID-8642")
+                .build();
+    }
+
+    private static void compareProgram(PreviewProgram programA, PreviewProgram programB,
+            boolean includeIdAndProtectedFields) {
+        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
+        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
+        assertEquals(programA.getChannelId(), programB.getChannelId());
+        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
+        assertEquals(programA.getDescription(), programB.getDescription());
+        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
+        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
+        assertEquals(programA.getLongDescription(), programB.getLongDescription());
+        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
+        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
+        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
+        assertEquals(programA.getTitle(), programB.getTitle());
+        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
+        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
+        assertEquals(programA.isSearchable(), programB.isSearchable());
+        assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
+        assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
+        assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
+        assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
+        assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
+        assertEquals(programA.getInternalProviderId(), programB.getInternalProviderId());
+        assertEquals(programA.getPreviewVideoUri(), programB.getPreviewVideoUri());
+        assertEquals(programA.getLastPlaybackPositionMillis(),
+                programB.getLastPlaybackPositionMillis());
+        assertEquals(programA.getDurationMillis(), programB.getDurationMillis());
+        assertEquals(programA.getIntentUri(), programB.getIntentUri());
+        assertEquals(programA.getWeight(), programB.getWeight());
+        assertEquals(programA.isTransient(), programB.isTransient());
+        assertEquals(programA.getType(), programB.getType());
+        assertEquals(programA.getPosterArtAspectRatio(), programB.getPosterArtAspectRatio());
+        assertEquals(programA.getThumbnailAspectRatio(), programB.getThumbnailAspectRatio());
+        assertEquals(programA.getLogoUri(), programB.getLogoUri());
+        assertEquals(programA.getAvailability(), programB.getAvailability());
+        assertEquals(programA.getStartingPrice(), programB.getStartingPrice());
+        assertEquals(programA.getOfferPrice(), programB.getOfferPrice());
+        assertEquals(programA.getReleaseDate(), programB.getReleaseDate());
+        assertEquals(programA.getItemCount(), programB.getItemCount());
+        assertEquals(programA.isLive(), programB.isLive());
+        assertEquals(programA.getInteractionType(), programB.getInteractionType());
+        assertEquals(programA.getInteractionCount(), programB.getInteractionCount());
+        assertEquals(programA.getAuthor(), programB.getAuthor());
+        assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
+        assertEquals(programA.getReviewRating(), programB.getReviewRating());
+        assertEquals(programA.getContentId(), programB.getContentId());
+        if (includeIdAndProtectedFields) {
+            // Skip row ID since the one from system DB has the valid ID while the other does not.
+            assertEquals(programA.getId(), programB.getId());
+            assertEquals(programA.getPackageName(), programB.getPackageName());
+            // When we insert a channel using toContentValues() to the system, we drop some
+            // protected fields since they only can be modified by system apps.
+            assertEquals(programA.isBrowsable(), programB.isBrowsable());
+            assertEquals(programA.toContentValues(), programB.toContentValues());
+            assertEquals(programA, programB);
+        }
+    }
+
+    private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) {
+        MatrixCursor cursor = new MatrixCursor(projection);
+        MatrixCursor.RowBuilder builder = cursor.newRow();
+        for (String col : projection) {
+            if (col != null) {
+                builder.add(col, contentValues.get(col));
+            }
+        }
+        cursor.moveToFirst();
+        return cursor;
+    }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/ProgramTest.java b/tv-provider/tests/src/android/support/media/tv/ProgramTest.java
new file mode 100644
index 0000000..e809253
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/ProgramTest.java
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.media.tv.TvContentRating;
+import android.net.Uri;
+import android.os.Build;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.TvContractCompat.Programs;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Tests that programs can be created using the Builder pattern and correctly obtain
+ * values from them.
+ */
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+public class ProgramTest extends TestCase {
+    @Override
+    protected void tearDown() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        resolver.delete(Channels.CONTENT_URI, null, null);
+    }
+
+    @Test
+    public void testEmptyProgram() {
+        Program emptyProgram = new Program.Builder()
+                .build();
+        ContentValues contentValues = emptyProgram.toContentValues();
+        compareProgram(emptyProgram,
+                Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true);
+    }
+
+    @Test
+    public void testSampleProgram() {
+        Program sampleProgram = new Program.Builder()
+                .setPackageName("My package")
+                .setTitle("Program Title")
+                .setDescription("This is a sample program")
+                .setEpisodeNumber(5)
+                .setSeasonNumber("The Final Season", 7)
+                .setThumbnailUri(Uri.parse("http://www.example.com/programs/poster.png"))
+                .setChannelId(3)
+                .setStartTimeUtcMillis(0)
+                .setEndTimeUtcMillis(1000)
+                .build();
+        ContentValues contentValues = sampleProgram.toContentValues();
+        compareProgram(sampleProgram,
+                Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true);
+
+        Program clonedSampleProgram = new Program.Builder(sampleProgram).build();
+        compareProgram(sampleProgram, clonedSampleProgram, true);
+    }
+
+    @Test
+    public void testFullyPopulatedProgram() {
+        Program fullyPopulatedProgram = createFullyPopulatedProgram(3);
+
+        ContentValues contentValues = fullyPopulatedProgram.toContentValues();
+        compareProgram(fullyPopulatedProgram,
+                Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true);
+
+        Program clonedFullyPopulatedProgram = new Program.Builder(fullyPopulatedProgram).build();
+        compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true);
+    }
+
+    @Test
+    public void testChannelWithSystemContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        Channel channel = new Channel.Builder()
+                .setInputId("TestInputService")
+                .setType(TvContractCompat.Channels.TYPE_OTHER)
+                .build();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues());
+        assertNotNull(channelUri);
+
+        Program fullyPopulatedProgram =
+                createFullyPopulatedProgram(ContentUris.parseId(channelUri));
+        Uri programUri = resolver.insert(Programs.CONTENT_URI,
+                fullyPopulatedProgram.toContentValues());
+
+        Program programFromSystemDb = loadProgramFromContentProvider(resolver, programUri);
+        compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+    }
+
+    @Test
+    public void testProgramUpdateWithContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        Channel channel = new Channel.Builder()
+                .setInputId("TestInputService")
+                .setType(TvContractCompat.Channels.TYPE_OTHER)
+                .build();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues());
+        assertNotNull(channelUri);
+
+        Program fullyPopulatedProgram =
+                createFullyPopulatedProgram(ContentUris.parseId(channelUri));
+        Uri programUri = resolver.insert(Programs.CONTENT_URI,
+                fullyPopulatedProgram.toContentValues());
+
+        Program programFromSystemDb = loadProgramFromContentProvider(resolver, programUri);
+        compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+
+        // Update a field from a fully loaded program.
+        Program updatedProgram = new Program.Builder(programFromSystemDb)
+                .setDescription("description1").build();
+        assertEquals(1, resolver.update(
+                programUri, updatedProgram.toContentValues(), null, null));
+        programFromSystemDb = loadProgramFromContentProvider(resolver, programUri);
+        compareProgram(updatedProgram, programFromSystemDb, false);
+
+        // Update a field with null from a fully loaded program.
+        updatedProgram = new Program.Builder(updatedProgram)
+                .setLongDescription(null).build();
+        assertEquals(1, resolver.update(
+                programUri, updatedProgram.toContentValues(), null, null));
+        programFromSystemDb = loadProgramFromContentProvider(resolver, programUri);
+        compareProgram(updatedProgram, programFromSystemDb, false);
+
+        // Update a field without referencing fully loaded program.
+        ContentValues values = new Program.Builder().setDescription("description2").build()
+                .toContentValues();
+        assertEquals(1, values.size());
+        assertEquals(1, resolver.update(programUri, values, null, null));
+        programFromSystemDb = loadProgramFromContentProvider(resolver, programUri);
+        Program expectedProgram = new Program.Builder(programFromSystemDb)
+                .setDescription("description2").build();
+        compareProgram(expectedProgram, programFromSystemDb, false);
+    }
+
+    @Test
+    public void testProgramEquals() {
+        assertEquals(createFullyPopulatedProgram(1), createFullyPopulatedProgram(1));
+    }
+
+    private static Program loadProgramFromContentProvider(
+            ContentResolver resolver, Uri programUri) {
+        try (Cursor cursor = resolver.query(programUri, null, null, null, null)) {
+            assertNotNull(cursor);
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            return Program.fromCursor(cursor);
+        }
+    }
+
+    private static Program createFullyPopulatedProgram(long channelId) {
+        return new Program.Builder()
+                .setSearchable(false)
+                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+                .setAudioLanguages(new String [] {"eng", "kor"})
+                .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
+                .setContentRatings(new TvContentRating[] {
+                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+                .setDescription("This is a sample program")
+                .setEpisodeNumber("Pilot", 0)
+                .setEpisodeTitle("Hello World")
+                .setLongDescription("This is a longer description than the previous description")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setSeasonNumber("The Final Season", 7)
+                .setSeasonTitle("The Final Season")
+                .setTitle("Google")
+                .setVideoHeight(1080)
+                .setVideoWidth(1920)
+                .setInternalProviderFlag1(0x4)
+                .setInternalProviderFlag2(0x3)
+                .setInternalProviderFlag3(0x2)
+                .setInternalProviderFlag4(0x1)
+                .setReviewRatingStyle(Programs.REVIEW_RATING_STYLE_PERCENTAGE)
+                .setReviewRating("83.9")
+                .setChannelId(channelId)
+                .setStartTimeUtcMillis(0)
+                .setEndTimeUtcMillis(1000)
+                .setBroadcastGenres(new String[] {"Music", "Family"})
+                .setRecordingProhibited(false)
+                .build();
+    }
+
+    private static void compareProgram(Program programA, Program programB,
+            boolean includeIdAndProtectedFields) {
+        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
+        assertTrue(Arrays.deepEquals(programA.getBroadcastGenres(), programB.getBroadcastGenres()));
+        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
+        assertEquals(programA.getChannelId(), programB.getChannelId());
+        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
+        assertEquals(programA.getDescription(), programB.getDescription());
+        assertEquals(programA.getEndTimeUtcMillis(), programB.getEndTimeUtcMillis());
+        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
+        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
+        assertEquals(programA.getLongDescription(), programB.getLongDescription());
+        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
+        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
+        assertEquals(programA.getStartTimeUtcMillis(), programB.getStartTimeUtcMillis());
+        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
+        assertEquals(programA.getTitle(), programB.getTitle());
+        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
+        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            assertEquals(programA.isSearchable(), programB.isSearchable());
+            assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
+            assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
+            assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
+            assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
+            assertTrue(Objects.equals(programA.isRecordingProhibited(),
+                    programB.isRecordingProhibited()));
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
+            assertEquals(programA.getReviewRating(), programB.getReviewRating());
+        }
+        if (includeIdAndProtectedFields) {
+            // Skip row ID since the one from system DB has the valid ID while the other does not.
+            assertEquals(programA.getId(), programB.getId());
+            assertEquals(programA.getPackageName(), programB.getPackageName());
+            assertEquals(programA.toContentValues(), programB.toContentValues());
+        }
+    }
+
+    private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) {
+        MatrixCursor cursor = new MatrixCursor(projection);
+        MatrixCursor.RowBuilder builder = cursor.newRow();
+        for (String row : projection) {
+            if (row != null) {
+                builder.add(row, contentValues.get(row));
+            }
+        }
+        cursor.moveToFirst();
+        return cursor;
+    }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/TvContractUtilsTest.java b/tv-provider/tests/src/android/support/media/tv/TvContractUtilsTest.java
new file mode 100644
index 0000000..bf9ea40
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/TvContractUtilsTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+package android.support.media.tv;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.media.tv.TvContentRating;
+import android.os.Build;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+public class TvContractUtilsTest extends TestCase {
+
+    @Test
+    public void testStringToContentRatings_nullInput() {
+        assertArrayEquals(TvContractUtils.EMPTY, TvContractUtils.stringToContentRatings(null));
+    }
+
+    @Test
+    public void testStringToContentRatings_emptyInput() {
+        assertArrayEquals(TvContractUtils.EMPTY, TvContractUtils.stringToContentRatings(""));
+    }
+
+    @Test
+    public void testStringToContentRatings_singleRating() {
+        TvContentRating[] ratings = new TvContentRating[1];
+        ratings[0] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_PG",
+                "US_TV_D",
+                "US_TV_L",
+                "US_TV_S",
+                "US_TV_V");
+        assertArrayEquals(ratings, TvContractUtils.stringToContentRatings(
+                "com.android.tv/US_TV/US_TV_PG/US_TV_D/US_TV_L/US_TV_S/US_TV_V"));
+    }
+
+    @Test
+    public void testStringToContentRatings_multipleRatings() {
+        TvContentRating[] ratings = new TvContentRating[3];
+        ratings[0] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_MV",
+                "US_MV_NC17");
+        ratings[1] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_Y7");
+        ratings[2] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_PG",
+                "US_TV_D",
+                "US_TV_L",
+                "US_TV_S",
+                "US_TV_V");
+        assertArrayEquals(ratings, TvContractUtils.stringToContentRatings(
+                "com.android.tv/US_MV/US_MV_NC17,"
+                        + "com.android.tv/US_TV/US_TV_Y7,"
+                        + "com.android.tv/US_TV/US_TV_PG/US_TV_D/US_TV_L/US_TV_S/US_TV_V"));
+    }
+
+    @Test
+    public void testStringToContentRatings_allRatingsInvalid() {
+        assertArrayEquals(TvContractUtils.EMPTY, TvContractUtils.stringToContentRatings(
+                "com.android.tv/US_MV," // Invalid
+                        + "com.android.tv")); // Invalid
+    }
+
+    @Test
+    public void testStringToContentRatings_someRatingsInvalid() {
+        TvContentRating[] ratings = new TvContentRating[1];
+        ratings[0] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_PG",
+                "US_TV_D",
+                "US_TV_L",
+                "US_TV_S",
+                "US_TV_V");
+        assertArrayEquals(ratings, TvContractUtils.stringToContentRatings(
+                "com.android.tv/US_MV," // Invalid
+                        + "com.android.tv/US_TV/US_TV_PG/US_TV_D/US_TV_L/US_TV_S/US_TV_V," // Valid
+                        + "com.android.tv")); // Invalid
+    }
+
+    @Test
+    public void testContentRatingsToString_nullInput() {
+        assertEquals(null, TvContractUtils.contentRatingsToString(null));
+    }
+
+    @Test
+    public void testContentRatingsToString_emptyInput() {
+        assertEquals(null, TvContractUtils.contentRatingsToString(new TvContentRating[0]));
+    }
+
+    @Test
+    public void testContentRatingsToString_singleRating() {
+        TvContentRating[] ratings = new TvContentRating[1];
+        ratings[0] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_PG",
+                "US_TV_D",
+                "US_TV_L",
+                "US_TV_S",
+                "US_TV_V");
+        assertEquals("com.android.tv/US_TV/US_TV_PG/US_TV_D/US_TV_L/US_TV_S/US_TV_V",
+                TvContractUtils.contentRatingsToString(ratings));
+    }
+
+    @Test
+    public void testContentRatingsToString_multipleRatings() {
+        TvContentRating[] ratings = new TvContentRating[3];
+        ratings[0] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_MV",
+                "US_MV_NC17");
+        ratings[1] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_PG",
+                "US_TV_D",
+                "US_TV_L",
+                "US_TV_S",
+                "US_TV_V");
+        ratings[2] = TvContentRating.createRating(
+                "com.android.tv",
+                "US_TV",
+                "US_TV_Y7");
+        String ratingString = "com.android.tv/US_MV/US_MV_NC17,"
+                + "com.android.tv/US_TV/US_TV_PG/US_TV_D/US_TV_L/US_TV_S/US_TV_V,"
+                + "com.android.tv/US_TV/US_TV_Y7";
+        assertEquals(ratingString, TvContractUtils.contentRatingsToString(ratings));
+    }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/Utils.java b/tv-provider/tests/src/android/support/media/tv/Utils.java
new file mode 100644
index 0000000..a6ff0ad
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/Utils.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.support.media.tv;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class Utils {
+    private Utils() { }
+
+    public static boolean hasTvInputFramework(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+    }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java b/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java
new file mode 100644
index 0000000..b1a1041
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+
+package android.support.media.tv;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.media.tv.TvContentRating;
+import android.net.Uri;
+import android.support.media.tv.TvContractCompat.WatchNextPrograms;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * Tests that watch next programs can be created using the Builder pattern and correctly obtain
+ * values from them.
+ */
+@SmallTest
+@SdkSuppress(minSdkVersion = 26)
+@TargetApi(26)
+public class WatchNextProgramTest extends TestCase {
+
+    @Override
+    protected void tearDown() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        resolver.delete(WatchNextPrograms.CONTENT_URI, null, null);
+    }
+
+    @Test
+    public void testEmptyPreviewProgram() {
+        WatchNextProgram emptyProgram = new WatchNextProgram.Builder().build();
+        ContentValues contentValues = emptyProgram.toContentValues(true);
+        compareProgram(emptyProgram,
+                WatchNextProgram.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)),
+                true);
+    }
+
+    @Test
+    public void testSampleProgram() {
+        WatchNextProgram sampleProgram = new WatchNextProgram.Builder()
+                .setTitle("Program Title")
+                .setDescription("This is a sample program")
+                .setEpisodeNumber(5)
+                .setSeasonNumber("The Final Season", 7)
+                .setThumbnailUri(Uri.parse("http://www.example.com/programs/poster.png"))
+                .build();
+        ContentValues contentValues = sampleProgram.toContentValues(true);
+        compareProgram(sampleProgram,
+                WatchNextProgram.fromCursor(
+                        getProgramCursor(WatchNextProgram.PROJECTION, contentValues)), true);
+
+        WatchNextProgram clonedSampleProgram = new WatchNextProgram.Builder(sampleProgram).build();
+        compareProgram(sampleProgram, clonedSampleProgram, true);
+    }
+
+    @Test
+    public void testFullyPopulatedProgram() {
+        WatchNextProgram fullyPopulatedProgram = createFullyPopulatedWatchNextProgram();
+        ContentValues contentValues = fullyPopulatedProgram.toContentValues(true);
+        compareProgram(fullyPopulatedProgram,
+                WatchNextProgram.fromCursor(
+                        getProgramCursor(WatchNextProgram.PROJECTION, contentValues)), true);
+
+        WatchNextProgram clonedFullyPopulatedProgram =
+                new WatchNextProgram.Builder(fullyPopulatedProgram).build();
+        compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true);
+    }
+
+    @Test
+    public void testChannelWithSystemContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        WatchNextProgram fullyPopulatedProgram = createFullyPopulatedWatchNextProgram();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri watchNextProgramUri = resolver.insert(WatchNextPrograms.CONTENT_URI,
+                fullyPopulatedProgram.toContentValues());
+
+        WatchNextProgram programFromSystemDb =
+                loadWatchNextProgramFromContentProvider(resolver, watchNextProgramUri);
+        compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+    }
+
+    @Test
+    public void testWatchNextProgramUpdateWithContentProvider() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+
+        WatchNextProgram fullyPopulatedProgram = createFullyPopulatedWatchNextProgram();
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        Uri watchNextProgramUri = resolver.insert(WatchNextPrograms.CONTENT_URI,
+                fullyPopulatedProgram.toContentValues());
+
+        WatchNextProgram programFromSystemDb =
+                loadWatchNextProgramFromContentProvider(resolver, watchNextProgramUri);
+        compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+
+        // Update a field from a fully loaded watch-next program.
+        WatchNextProgram updatedProgram = new WatchNextProgram.Builder(programFromSystemDb)
+                .setInteractionCount(programFromSystemDb.getInteractionCount() + 1).build();
+        assertEquals(1, resolver.update(
+                watchNextProgramUri, updatedProgram.toContentValues(), null, null));
+        programFromSystemDb =
+                loadWatchNextProgramFromContentProvider(resolver, watchNextProgramUri);
+        compareProgram(updatedProgram, programFromSystemDb, false);
+
+        // Update a field with null from a fully loaded watch-next program.
+        updatedProgram = new WatchNextProgram.Builder(updatedProgram)
+                .setPreviewVideoUri(null).build();
+        assertEquals(1, resolver.update(
+                watchNextProgramUri, updatedProgram.toContentValues(), null, null));
+        programFromSystemDb = loadWatchNextProgramFromContentProvider(
+                resolver, watchNextProgramUri);
+        compareProgram(updatedProgram, programFromSystemDb, false);
+
+        // Update a field without referencing fully watch-next program.
+        ContentValues values = new PreviewProgram.Builder().setInteractionCount(1).build()
+                .toContentValues();
+        assertEquals(1, values.size());
+        assertEquals(1, resolver.update(watchNextProgramUri, values, null, null));
+        programFromSystemDb = loadWatchNextProgramFromContentProvider(
+                resolver, watchNextProgramUri);
+        WatchNextProgram expectedProgram = new WatchNextProgram.Builder(programFromSystemDb)
+                .setInteractionCount(1).build();
+        compareProgram(expectedProgram, programFromSystemDb, false);
+    }
+
+    @Test
+    public void testWatchNextProgramEquals() {
+        assertEquals(createFullyPopulatedWatchNextProgram(),
+                createFullyPopulatedWatchNextProgram());
+    }
+
+    private static WatchNextProgram loadWatchNextProgramFromContentProvider(
+            ContentResolver resolver, Uri watchNextProgramUri) {
+        try (Cursor cursor = resolver.query(watchNextProgramUri, null, null, null, null)) {
+            assertNotNull(cursor);
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            return WatchNextProgram.fromCursor(cursor);
+        }
+    }
+
+    @Test
+    public void testWatchNextProgramWithPartialData() {
+        WatchNextProgram previewProgram = new WatchNextProgram.Builder()
+                .setInternalProviderId("ID-4321")
+                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+                .setLastPlaybackPositionMillis(0)
+                .setDurationMillis(60 * 1000)
+                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setTransient(false)
+                .setType(WatchNextPrograms.TYPE_TV_EPISODE)
+                .setPosterArtAspectRatio(WatchNextPrograms.ASPECT_RATIO_3_2)
+                .setThumbnailAspectRatio(WatchNextPrograms.ASPECT_RATIO_16_9)
+                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+                .setAvailability(WatchNextPrograms.AVAILABILITY_FREE_WITH_SUBSCRIPTION)
+                .setStartingPrice("9.99 USD")
+                .setOfferPrice("3.99 USD")
+                .setReleaseDate(new Date(97, 2, 8))
+                .setLive(false)
+                .setInteractionType(WatchNextPrograms.INTERACTION_TYPE_VIEWS)
+                .setInteractionCount(99200)
+                .setAuthor("author_name")
+                .setReviewRatingStyle(WatchNextPrograms.REVIEW_RATING_STYLE_PERCENTAGE)
+                .setReviewRating("83.9")
+                .setId(10)
+                .setTitle("Recommended Video 1")
+                .setDescription("You should watch this!")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setInternalProviderFlag2(0x0010010084108410L)
+                .build();
+
+        String[] partialProjection = {
+                WatchNextPrograms._ID,
+                WatchNextPrograms.COLUMN_TITLE,
+                WatchNextPrograms.COLUMN_SHORT_DESCRIPTION,
+                WatchNextPrograms.COLUMN_POSTER_ART_URI,
+                WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+                WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_ID,
+                WatchNextPrograms.COLUMN_PREVIEW_VIDEO_URI,
+                WatchNextPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS,
+                WatchNextPrograms.COLUMN_DURATION_MILLIS,
+                WatchNextPrograms.COLUMN_INTENT_URI,
+                WatchNextPrograms.COLUMN_TRANSIENT,
+                WatchNextPrograms.COLUMN_TYPE,
+                WatchNextPrograms.COLUMN_POSTER_ART_ASPECT_RATIO,
+                WatchNextPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO,
+                WatchNextPrograms.COLUMN_LOGO_URI,
+                WatchNextPrograms.COLUMN_AVAILABILITY,
+                WatchNextPrograms.COLUMN_STARTING_PRICE,
+                WatchNextPrograms.COLUMN_OFFER_PRICE,
+                WatchNextPrograms.COLUMN_RELEASE_DATE,
+                WatchNextPrograms.COLUMN_ITEM_COUNT,
+                WatchNextPrograms.COLUMN_LIVE,
+                WatchNextPrograms.COLUMN_INTERACTION_TYPE,
+                WatchNextPrograms.COLUMN_INTERACTION_COUNT,
+                WatchNextPrograms.COLUMN_AUTHOR,
+                WatchNextPrograms.COLUMN_REVIEW_RATING_STYLE,
+                WatchNextPrograms.COLUMN_REVIEW_RATING,
+        };
+
+        ContentValues contentValues = previewProgram.toContentValues(true);
+        compareProgram(previewProgram,
+                WatchNextProgram.fromCursor(getProgramCursor(partialProjection, contentValues)),
+                true);
+
+        WatchNextProgram clonedFullyPopulatedProgram =
+                new WatchNextProgram.Builder(previewProgram).build();
+        compareProgram(previewProgram, clonedFullyPopulatedProgram, true);
+    }
+
+    private static WatchNextProgram createFullyPopulatedWatchNextProgram() {
+        return new WatchNextProgram.Builder()
+                .setTitle("Google")
+                .setInternalProviderId("ID-4321")
+                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+                .setLastPlaybackPositionMillis(0)
+                .setDurationMillis(60 * 1000)
+                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setTransient(false)
+                .setType(WatchNextPrograms.TYPE_MOVIE)
+                .setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
+                .setPosterArtAspectRatio(WatchNextPrograms.ASPECT_RATIO_2_3)
+                .setThumbnailAspectRatio(WatchNextPrograms.ASPECT_RATIO_16_9)
+                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+                .setAvailability(WatchNextPrograms.AVAILABILITY_AVAILABLE)
+                .setStartingPrice("12.99 USD")
+                .setOfferPrice("4.99 USD")
+                .setReleaseDate("1997")
+                .setItemCount(3)
+                .setLive(false)
+                .setInteractionType(WatchNextPrograms.INTERACTION_TYPE_LIKES)
+                .setInteractionCount(10200)
+                .setAuthor("author_name")
+                .setReviewRatingStyle(WatchNextPrograms.REVIEW_RATING_STYLE_STARS)
+                .setReviewRating("4.5")
+                .setSearchable(false)
+                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+                .setAudioLanguages(new String [] {"eng", "kor"})
+                .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
+                .setContentRatings(new TvContentRating[] {
+                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+                .setDescription("This is a sample program")
+                .setEpisodeNumber("Pilot", 0)
+                .setEpisodeTitle("Hello World")
+                .setLongDescription("This is a longer description than the previous description")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setSeasonNumber("The Final Season", 7)
+                .setSeasonTitle("The Final Season")
+                .setVideoHeight(1080)
+                .setVideoWidth(1920)
+                .setInternalProviderFlag1(0x4)
+                .setInternalProviderFlag2(0x3)
+                .setInternalProviderFlag3(0x2)
+                .setInternalProviderFlag4(0x1)
+                .setBrowsable(true)
+                .setContentId("CID-8442")
+                .build();
+    }
+
+    private static void compareProgram(WatchNextProgram programA, WatchNextProgram programB,
+            boolean includeIdAndProtectedFields) {
+        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
+        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
+        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
+        assertEquals(programA.getDescription(), programB.getDescription());
+        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
+        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
+        assertEquals(programA.getLongDescription(), programB.getLongDescription());
+        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
+        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
+        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
+        assertEquals(programA.getTitle(), programB.getTitle());
+        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
+        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
+        assertEquals(programA.isSearchable(), programB.isSearchable());
+        assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
+        assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
+        assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
+        assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
+        assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
+        assertEquals(programA.getInternalProviderId(), programB.getInternalProviderId());
+        assertEquals(programA.getPreviewVideoUri(), programB.getPreviewVideoUri());
+        assertEquals(programA.getLastPlaybackPositionMillis(),
+                programB.getLastPlaybackPositionMillis());
+        assertEquals(programA.getDurationMillis(), programB.getDurationMillis());
+        assertEquals(programA.getIntentUri(), programB.getIntentUri());
+        assertEquals(programA.isTransient(), programB.isTransient());
+        assertEquals(programA.getType(), programB.getType());
+        assertEquals(programA.getWatchNextType(), programB.getWatchNextType());
+        assertEquals(programA.getPosterArtAspectRatio(), programB.getPosterArtAspectRatio());
+        assertEquals(programA.getThumbnailAspectRatio(), programB.getThumbnailAspectRatio());
+        assertEquals(programA.getLogoUri(), programB.getLogoUri());
+        assertEquals(programA.getAvailability(), programB.getAvailability());
+        assertEquals(programA.getStartingPrice(), programB.getStartingPrice());
+        assertEquals(programA.getOfferPrice(), programB.getOfferPrice());
+        assertEquals(programA.getReleaseDate(), programB.getReleaseDate());
+        assertEquals(programA.getItemCount(), programB.getItemCount());
+        assertEquals(programA.isLive(), programB.isLive());
+        assertEquals(programA.getInteractionType(), programB.getInteractionType());
+        assertEquals(programA.getInteractionCount(), programB.getInteractionCount());
+        assertEquals(programA.getAuthor(), programB.getAuthor());
+        assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
+        assertEquals(programA.getReviewRating(), programB.getReviewRating());
+        assertEquals(programA.getContentId(), programB.getContentId());
+        if (includeIdAndProtectedFields) {
+            // Skip row ID since the one from system DB has the valid ID while the other does not.
+            assertEquals(programA.getId(), programB.getId());
+            // When we insert a channel using toContentValues() to the system, we drop some
+            // protected fields since they only can be modified by system apps.
+            assertEquals(programA.isBrowsable(), programB.isBrowsable());
+            assertEquals(programA.toContentValues(), programB.toContentValues());
+            assertEquals(programA, programB);
+        }
+    }
+
+    private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) {
+        MatrixCursor cursor = new MatrixCursor(projection);
+        MatrixCursor.RowBuilder builder = cursor.newRow();
+        for (String col : projection) {
+            if (col != null) {
+                builder.add(col, contentValues.get(col));
+            }
+        }
+        cursor.moveToFirst();
+        return cursor;
+    }
+}
diff --git a/v13/Android.mk b/v13/Android.mk
index f697512..1a95b75 100644
--- a/v13/Android.mk
+++ b/v13/Android.mk
@@ -27,14 +27,8 @@
 LOCAL_MODULE := android-support-v13
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
-        $(call all-java-files-under, ics) \
-        $(call all-java-files-under, ics-mr1) \
-        $(call all-java-files-under, api23) \
-        $(call all-java-files-under, api24) \
-        $(call all-java-files-under, api25) \
         $(call all-java-files-under, java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 # Some projects expect to inherit android-support-v4 from
 # android-support-v13, so we need to keep it static until they can be fixed.
 LOCAL_STATIC_ANDROID_LIBRARIES := \
diff --git a/v13/AndroidManifest-make.xml b/v13/AndroidManifest-make.xml
deleted file mode 100644
index ea25a74..0000000
--- a/v13/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.v13">
-    <uses-sdk android:minSdkVersion="13" tools:overrideLibrary="android.support.v13"/>
-    <application />
-</manifest>
diff --git a/v13/AndroidManifest.xml b/v13/AndroidManifest.xml
index 3520ab3..6eea520 100644
--- a/v13/AndroidManifest.xml
+++ b/v13/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v13">
-    <uses-sdk android:minSdkVersion="13" tools:overrideLibrary="android.support.v13"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.v13"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v13/api23/android/support/v13/app/FragmentCompat23.java b/v13/api23/android/support/v13/app/FragmentCompat23.java
deleted file mode 100644
index 1364e84..0000000
--- a/v13/api23/android/support/v13/app/FragmentCompat23.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v13.app;
-
-import android.annotation.TargetApi;
-import android.app.Fragment;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(23)
-@TargetApi(23)
-class FragmentCompat23 {
-    public static void requestPermissions(Fragment fragment, String[] permissions,
-            int requestCode) {
-        fragment.requestPermissions(permissions, requestCode);
-    }
-
-    public static boolean shouldShowRequestPermissionRationale(Fragment fragment,
-            String permission) {
-        return fragment.shouldShowRequestPermissionRationale(permission);
-    }
-}
diff --git a/v13/api24/android/support/v13/app/FragmentCompatApi24.java b/v13/api24/android/support/v13/app/FragmentCompatApi24.java
deleted file mode 100644
index 2b41d25..0000000
--- a/v13/api24/android/support/v13/app/FragmentCompatApi24.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v13.app;
-
-import android.annotation.TargetApi;
-import android.app.Fragment;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(24)
-@TargetApi(24)
-class FragmentCompatApi24 {
-    public static void setUserVisibleHint(Fragment f, boolean isVisible) {
-        f.setUserVisibleHint(isVisible);
-    }
-}
diff --git a/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java b/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
deleted file mode 100644
index fd38b79..0000000
--- a/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v13.view;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.support.annotation.RequiresApi;
-import android.view.DragAndDropPermissions;
-import android.view.DragEvent;
-
-@RequiresApi(24)
-@TargetApi(24)
-class DragAndDropPermissionsCompatApi24 {
-    public static Object request(Activity activity, DragEvent dragEvent) {
-        return activity.requestDragAndDropPermissions(dragEvent);
-    }
-
-    public static void release(Object dragAndDropPermissions) {
-        ((DragAndDropPermissions)dragAndDropPermissions).release();
-    }
-}
diff --git a/v13/api24/android/support/v13/view/ViewCompatApi24.java b/v13/api24/android/support/v13/view/ViewCompatApi24.java
deleted file mode 100644
index 422dbb9..0000000
--- a/v13/api24/android/support/v13/view/ViewCompatApi24.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v13.view;
-
-import android.annotation.TargetApi;
-import android.content.ClipData;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-@RequiresApi(24)
-@TargetApi(24)
-class ViewCompatApi24 {
-    public static boolean startDragAndDrop(View v, ClipData data,
-           View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
-        return v.startDragAndDrop(data, shadowBuilder, localState, flags);
-    }
-
-    public static void cancelDragAndDrop(View v) {
-        v.cancelDragAndDrop();
-    }
-
-    public static void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder) {
-        v.updateDragShadow(shadowBuilder);
-    }
-
-    private ViewCompatApi24() {}
-}
diff --git a/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
deleted file mode 100644
index 3eef2ba..0000000
--- a/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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.
- */
-
-package android.support.v13.view.inputmethod;
-
-import android.annotation.TargetApi;
-import android.support.annotation.RequiresApi;
-import android.view.inputmethod.EditorInfo;
-
-@RequiresApi(25)
-@TargetApi(25)
-final class EditorInfoCompatApi25 {
-    public static void setContentMimeTypes(EditorInfo editorInfo, String[] contentMimeTypes) {
-        editorInfo.contentMimeTypes = contentMimeTypes;
-    }
-
-    public static String[] getContentMimeTypes(EditorInfo editorInfo) {
-        return editorInfo.contentMimeTypes;
-    }
-}
diff --git a/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
deleted file mode 100644
index 41de756..0000000
--- a/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * 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.
- */
-
-package android.support.v13.view.inputmethod;
-
-import android.annotation.TargetApi;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionWrapper;
-import android.view.inputmethod.InputContentInfo;
-
-@RequiresApi(25)
-@TargetApi(25)
-final class InputConnectionCompatApi25 {
-
-    public static boolean commitContent(InputConnection ic, Object inputContentInfo, int flags,
-            Bundle opts) {
-        return ic.commitContent((InputContentInfo)inputContentInfo, flags, opts);
-    }
-
-    public interface OnCommitContentListener {
-        boolean onCommitContent(Object inputContentInfo, int flags, Bundle opts);
-    }
-
-    public static InputConnection createWrapper(InputConnection ic,
-            final OnCommitContentListener onCommitContentListener) {
-        return new InputConnectionWrapper(ic, false /* mutable */) {
-            @Override
-            public boolean commitContent(InputContentInfo inputContentInfo, int flags,
-                        Bundle opts) {
-                if (onCommitContentListener.onCommitContent(inputContentInfo, flags, opts)) {
-                    return true;
-                }
-                return super.commitContent(inputContentInfo, flags, opts);
-            }
-        };
-    }
-
-}
diff --git a/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
deleted file mode 100644
index e9b7a01..0000000
--- a/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * 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.
- */
-
-package android.support.v13.view.inputmethod;
-
-import android.annotation.TargetApi;
-import android.content.ClipDescription;
-import android.net.Uri;
-import android.support.annotation.RequiresApi;
-import android.view.inputmethod.InputContentInfo;
-
-@RequiresApi(25)
-@TargetApi(25)
-final class InputContentInfoCompatApi25 {
-
-    public static Object create(Uri contentUri, ClipDescription description, Uri linkUri) {
-        return new InputContentInfo(contentUri, description, linkUri);
-    }
-
-    public static Uri getContentUri(Object inputContentInfo) {
-        return ((InputContentInfo) inputContentInfo).getContentUri();
-    }
-
-    public static ClipDescription getDescription(Object inputContentInfo) {
-        return ((InputContentInfo) inputContentInfo).getDescription();
-    }
-
-    public static Uri getLinkUri(Object inputContentInfo) {
-        return ((InputContentInfo) inputContentInfo).getLinkUri();
-    }
-
-    public static void requestPermission(Object inputContentInfo) {
-        ((InputContentInfo) inputContentInfo).requestPermission();
-    }
-
-    public static void releasePermission(Object inputContentInfo) {
-        ((InputContentInfo) inputContentInfo).releasePermission();
-    }
-}
diff --git a/v13/build.gradle b/v13/build.gradle
index fb1c25d..477f31d 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -1,99 +1,34 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-v13'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-v4')
+    api project(':support-annotations')
+    api project(':support-v4')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile "org.mockito:mockito-core:1.9.5"
-    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
-    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 13
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
-                'ics',
-                'ics-mr1',
-                'api23',
-                'api24',
-                'api25',
                 'java'
         ]
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v13'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 13 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Library v13'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
 }
diff --git a/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java b/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
deleted file mode 100644
index 2da10ae..0000000
--- a/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2011 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.v13.app;
-
-import android.annotation.TargetApi;
-import android.app.Fragment;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(15)
-@TargetApi(15)
-class FragmentCompatICSMR1 {
-    public static void setUserVisibleHint(Fragment f, boolean isVisible) {
-        if (f.getFragmentManager() != null) {
-            f.setUserVisibleHint(isVisible);
-        }
-    }
-}
diff --git a/v13/ics/android/support/v13/app/FragmentCompatICS.java b/v13/ics/android/support/v13/app/FragmentCompatICS.java
deleted file mode 100644
index ff40337..0000000
--- a/v13/ics/android/support/v13/app/FragmentCompatICS.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2011 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.v13.app;
-
-import android.annotation.TargetApi;
-import android.app.Fragment;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(14)
-@TargetApi(14)
-class FragmentCompatICS {
-    public static void setMenuVisibility(Fragment f, boolean visible) {
-        f.setMenuVisibility(visible);
-    }
-}
diff --git a/v13/java/android/support/v13/app/ActivityCompat.java b/v13/java/android/support/v13/app/ActivityCompat.java
index 8f9a767..b0c3c30 100644
--- a/v13/java/android/support/v13/app/ActivityCompat.java
+++ b/v13/java/android/support/v13/app/ActivityCompat.java
@@ -16,18 +16,13 @@
 
 package android.support.v13.app;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
-import android.support.annotation.RequiresApi;
 import android.support.v13.view.DragAndDropPermissionsCompat;
 import android.view.DragEvent;
 
 /**
- * Helper for accessing features in {@link android.app.Activity}
- * introduced after API level 13 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.app.Activity} in a backwards compatible fashion.
  */
-@RequiresApi(13)
-@TargetApi(13)
 public class ActivityCompat extends android.support.v4.app.ActivityCompat {
 
     /**
@@ -43,6 +38,10 @@
         return DragAndDropPermissionsCompat.request(activity, dragEvent);
     }
 
+    /**
+     * This class should not be instantiated, but the constructor must be
+     * visible for the class to be extended.
+     */
     protected ActivityCompat() {
         // Not publicly instantiable, but may be extended.
     }
diff --git a/v13/java/android/support/v13/app/FragmentCompat.java b/v13/java/android/support/v13/app/FragmentCompat.java
index 99e4a80..4f21052 100644
--- a/v13/java/android/support/v13/app/FragmentCompat.java
+++ b/v13/java/android/support/v13/app/FragmentCompat.java
@@ -16,7 +16,6 @@
 
 package android.support.v13.app;
 
-import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -25,28 +24,20 @@
 import android.os.Looper;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
-import android.support.v4.os.BuildCompat;
 
 import java.util.Arrays;
 
 /**
- * Helper for accessing features in {@link Fragment} introduced after
- * API level 13 in a backwards compatible fashion.
+ * Helper for accessing features in {@link Fragment} in a backwards compatible fashion.
  */
-@RequiresApi(13)
-@TargetApi(13)
 public class FragmentCompat {
     interface FragmentCompatImpl {
-        void setMenuVisibility(Fragment f, boolean visible);
         void setUserVisibleHint(Fragment f, boolean deferStart);
         void requestPermissions(Fragment fragment, String[] permissions, int requestCode);
         boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission);
     }
 
-    static class BaseFragmentCompatImpl implements FragmentCompatImpl {
-        @Override
-        public void setMenuVisibility(Fragment f, boolean visible) {
-        }
+    static class FragmentCompatBaseImpl implements FragmentCompatImpl {
         @Override
         public void setUserVisibleHint(Fragment f, boolean deferStart) {
         }
@@ -84,51 +75,45 @@
         }
     }
 
-    static class ICSFragmentCompatImpl extends BaseFragmentCompatImpl {
-        @Override
-        public void setMenuVisibility(Fragment f, boolean visible) {
-            FragmentCompatICS.setMenuVisibility(f, visible);
-        }
-    }
-
-    static class ICSMR1FragmentCompatImpl extends ICSFragmentCompatImpl {
+    @RequiresApi(15)
+    static class FragmentCompatApi15Impl extends FragmentCompatBaseImpl {
         @Override
         public void setUserVisibleHint(Fragment f, boolean deferStart) {
-            FragmentCompatICSMR1.setUserVisibleHint(f, deferStart);
+            f.setUserVisibleHint(deferStart);
         }
     }
 
-    static class MncFragmentCompatImpl extends ICSMR1FragmentCompatImpl {
+    @RequiresApi(23)
+    static class FragmentCompatApi23Impl extends FragmentCompatApi15Impl {
         @Override
         public void requestPermissions(Fragment fragment, String[] permissions, int requestCode) {
-            FragmentCompat23.requestPermissions(fragment, permissions, requestCode);
+            fragment.requestPermissions(permissions, requestCode);
         }
 
         @Override
         public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) {
-            return FragmentCompat23.shouldShowRequestPermissionRationale(fragment, permission);
+            return fragment.shouldShowRequestPermissionRationale(permission);
         }
     }
 
-    static class NFragmentCompatImpl extends MncFragmentCompatImpl {
+    @RequiresApi(24)
+    static class FragmentCompatApi24Impl extends FragmentCompatApi23Impl {
         @Override
         public void setUserVisibleHint(Fragment f, boolean deferStart) {
-            FragmentCompatApi24.setUserVisibleHint(f, deferStart);
+            f.setUserVisibleHint(deferStart);
         }
     }
 
     static final FragmentCompatImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new NFragmentCompatImpl();
+        if (Build.VERSION.SDK_INT >= 24) {
+            IMPL = new FragmentCompatApi24Impl();
         } else if (Build.VERSION.SDK_INT >= 23) {
-            IMPL = new MncFragmentCompatImpl();
+            IMPL = new FragmentCompatApi23Impl();
         } else if (android.os.Build.VERSION.SDK_INT >= 15) {
-            IMPL = new ICSMR1FragmentCompatImpl();
-        } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-            IMPL = new ICSFragmentCompatImpl();
+            IMPL = new FragmentCompatApi15Impl();
         } else {
-            IMPL = new BaseFragmentCompatImpl();
+            IMPL = new FragmentCompatBaseImpl();
         }
     }
 
@@ -158,9 +143,12 @@
     /**
      * Call {@link Fragment#setMenuVisibility(boolean) Fragment.setMenuVisibility(boolean)}
      * if running on an appropriate version of the platform.
+     *
+     * @deprecated Use {@link Fragment#setMenuVisibility(boolean)} directly.
      */
+    @Deprecated
     public static void setMenuVisibility(Fragment f, boolean visible) {
-        IMPL.setMenuVisibility(f, visible);
+        f.setMenuVisibility(visible);
     }
 
     /**
diff --git a/v13/java/android/support/v13/app/FragmentPagerAdapter.java b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
index 78d8b89..082f883 100644
--- a/v13/java/android/support/v13/app/FragmentPagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
@@ -16,12 +16,10 @@
 
 package android.support.v13.app;
 
-import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.support.v4.view.PagerAdapter;
 import android.util.Log;
@@ -64,8 +62,6 @@
  * {@sample frameworks/support/samples/Support13Demos/res/layout/fragment_pager_list.xml
  *      complete}
  */
-@RequiresApi(13)
-@TargetApi(13)
 public abstract class FragmentPagerAdapter extends PagerAdapter {
     private static final String TAG = "FragmentPagerAdapter";
     private static final boolean DEBUG = false;
@@ -91,6 +87,7 @@
         }
     }
 
+    @SuppressWarnings("ReferenceEquality")
     @Override
     public Object instantiateItem(ViewGroup container, int position) {
         if (mCurTransaction == null) {
@@ -112,7 +109,7 @@
                     makeFragmentName(container.getId(), itemId));
         }
         if (fragment != mCurrentPrimaryItem) {
-            FragmentCompat.setMenuVisibility(fragment, false);
+            fragment.setMenuVisibility(false);
             FragmentCompat.setUserVisibleHint(fragment, false);
         }
 
@@ -129,16 +126,17 @@
         mCurTransaction.detach((Fragment)object);
     }
 
+    @SuppressWarnings("ReferenceEquality")
     @Override
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
         Fragment fragment = (Fragment)object;
         if (fragment != mCurrentPrimaryItem) {
             if (mCurrentPrimaryItem != null) {
-                FragmentCompat.setMenuVisibility(mCurrentPrimaryItem, false);
+                mCurrentPrimaryItem.setMenuVisibility(false);
                 FragmentCompat.setUserVisibleHint(mCurrentPrimaryItem, false);
             }
             if (fragment != null) {
-                FragmentCompat.setMenuVisibility(fragment, true);
+                fragment.setMenuVisibility(true);
                 FragmentCompat.setUserVisibleHint(fragment, true);
             }
             mCurrentPrimaryItem = fragment;
diff --git a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
index 2579688..8907fec 100644
--- a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
@@ -16,13 +16,11 @@
 
 package android.support.v13.app;
 
-import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.os.Bundle;
 import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
 import android.support.v4.app.FragmentPagerAdapter;
 import android.support.v4.view.PagerAdapter;
 import android.util.Log;
@@ -67,10 +65,8 @@
  * {@sample frameworks/support/samples/Support4Demos/res/layout/fragment_pager_list.xml
  *      complete}
  */
-@RequiresApi(13)
-@TargetApi(13)
 public abstract class FragmentStatePagerAdapter extends PagerAdapter {
-    private static final String TAG = "FragmentStatePagerAdapter";
+    private static final String TAG = "FragStatePagerAdapter";
     private static final boolean DEBUG = false;
 
     private final FragmentManager mFragmentManager;
@@ -125,7 +121,7 @@
         while (mFragments.size() <= position) {
             mFragments.add(null);
         }
-        FragmentCompat.setMenuVisibility(fragment, false);
+        fragment.setMenuVisibility(false);
         FragmentCompat.setUserVisibleHint(fragment, false);
         mFragments.set(position, fragment);
         mCurTransaction.add(container.getId(), fragment);
@@ -152,16 +148,17 @@
         mCurTransaction.remove(fragment);
     }
 
+    @SuppressWarnings("ReferenceEquality")
     @Override
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
         Fragment fragment = (Fragment)object;
         if (fragment != mCurrentPrimaryItem) {
             if (mCurrentPrimaryItem != null) {
-                FragmentCompat.setMenuVisibility(mCurrentPrimaryItem, false);
+                mCurrentPrimaryItem.setMenuVisibility(false);
                 FragmentCompat.setUserVisibleHint(mCurrentPrimaryItem, false);
             }
             if (fragment != null) {
-                FragmentCompat.setMenuVisibility(fragment, true);
+                fragment.setMenuVisibility(true);
                 FragmentCompat.setUserVisibleHint(fragment, true);
             }
             mCurrentPrimaryItem = fragment;
diff --git a/v13/java/android/support/v13/app/FragmentTabHost.java b/v13/java/android/support/v13/app/FragmentTabHost.java
index ba5d659..2326ccb 100644
--- a/v13/java/android/support/v13/app/FragmentTabHost.java
+++ b/v13/java/android/support/v13/app/FragmentTabHost.java
@@ -16,7 +16,6 @@
 
 package android.support.v13.app;
 
-import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -25,7 +24,6 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,10 +39,7 @@
  * used with the platform {@link android.app.Fragment} APIs.  You will not
  * normally use this, instead using action bar tabs.
  */
-@RequiresApi(13)
-@TargetApi(13)
-public class FragmentTabHost extends TabHost
-        implements TabHost.OnTabChangeListener {
+public class FragmentTabHost extends TabHost implements TabHost.OnTabChangeListener {
     private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
     private FrameLayout mRealTabContent;
     private Context mContext;
@@ -176,7 +171,8 @@
      * call {@link #setup(Context, FragmentManager)} or
      * {@link #setup(Context, FragmentManager, int)}.
      */
-    @Override @Deprecated
+    @Override
+    @Deprecated
     public void setup() {
         throw new IllegalStateException(
                 "Must call setup() that takes a Context and FragmentManager");
diff --git a/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java b/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
index 46484da..13ed203 100644
--- a/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
+++ b/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
@@ -18,19 +18,17 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
+import android.os.Build;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
+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.
  */
-@RequiresApi(13)
-@TargetApi(13)
 public final class DragAndDropPermissionsCompat {
 
     interface DragAndDropPermissionsCompatImpl {
@@ -50,22 +48,23 @@
         }
     }
 
+    @RequiresApi(24)
     static class Api24DragAndDropPermissionsCompatImpl
             extends BaseDragAndDropPermissionsCompatImpl {
         @Override
         public Object request(Activity activity, DragEvent dragEvent) {
-            return DragAndDropPermissionsCompatApi24.request(activity, dragEvent);
+            return activity.requestDragAndDropPermissions(dragEvent);
         }
 
         @Override
         public void release(Object dragAndDropPermissions) {
-            DragAndDropPermissionsCompatApi24.release(dragAndDropPermissions);
+            ((DragAndDropPermissions) dragAndDropPermissions).release();
         }
     }
 
     private static DragAndDropPermissionsCompatImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             IMPL = new Api24DragAndDropPermissionsCompatImpl();
         } else {
             IMPL = new BaseDragAndDropPermissionsCompatImpl();
diff --git a/v13/java/android/support/v13/view/DragStartHelper.java b/v13/java/android/support/v13/view/DragStartHelper.java
index 16c54b9..85bc2f3 100644
--- a/v13/java/android/support/v13/view/DragStartHelper.java
+++ b/v13/java/android/support/v13/view/DragStartHelper.java
@@ -17,9 +17,7 @@
 package android.support.v13.view;
 
 
-import android.annotation.TargetApi;
 import android.graphics.Point;
-import android.support.annotation.RequiresApi;
 import android.support.v4.view.InputDeviceCompat;
 import android.support.v4.view.MotionEventCompat;
 import android.view.MotionEvent;
@@ -70,8 +68,6 @@
  * }
  * </pre>
  */
-@RequiresApi(13)
-@TargetApi(13)
 public class DragStartHelper {
     final private View mView;
     final private OnDragStartListener mListener;
@@ -142,8 +138,8 @@
 
             case MotionEvent.ACTION_MOVE:
                 if (!MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE)
-                        || (MotionEventCompat.getButtonState(event)
-                                & MotionEventCompat.BUTTON_PRIMARY) == 0) {
+                        || (event.getButtonState()
+                                & MotionEvent.BUTTON_PRIMARY) == 0) {
                     break;
                 }
                 if (mDragging) {
diff --git a/v13/java/android/support/v13/view/ViewCompat.java b/v13/java/android/support/v13/view/ViewCompat.java
index 0fd3234..2b7906a 100644
--- a/v13/java/android/support/v13/view/ViewCompat.java
+++ b/v13/java/android/support/v13/view/ViewCompat.java
@@ -16,100 +16,16 @@
 
 package android.support.v13.view;
 
-import android.annotation.TargetApi;
-import android.content.ClipData;
-import android.support.annotation.RequiresApi;
-import android.support.v4.os.BuildCompat;
 import android.view.View;
 
 /**
  * Helper for accessing features in {@link View} introduced after API
  * level 13 in a backwards compatible fashion.
+ *
+ * @deprecated Use {@link android.support.v4.view.ViewCompat} instead.
  */
-@RequiresApi(13)
-@TargetApi(13)
+@Deprecated
 public class ViewCompat extends android.support.v4.view.ViewCompat {
-    interface ViewCompatImpl {
-        boolean startDragAndDrop(View v, ClipData data, View.DragShadowBuilder shadowBuilder,
-                                 Object localState, int flags);
-        void cancelDragAndDrop(View v);
-        void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder);
-    }
-
-    private static class BaseViewCompatImpl implements ViewCompatImpl {
-        BaseViewCompatImpl() {
-        }
-
-        @Override
-        public boolean startDragAndDrop(View v, ClipData data, View.DragShadowBuilder shadowBuilder,
-                Object localState, int flags) {
-            return v.startDrag(data, shadowBuilder, localState, flags);
-        }
-
-        @Override
-        public void cancelDragAndDrop(View v) {
-            // no-op
-        }
-
-        @Override
-        public void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder) {
-            // no-op
-        }
-    }
-
-    private static class Api24ViewCompatImpl implements ViewCompatImpl {
-        Api24ViewCompatImpl() {
-        }
-
-        @Override
-        public boolean startDragAndDrop(View v, ClipData data, View.DragShadowBuilder shadowBuilder,
-                Object localState, int flags) {
-            return ViewCompatApi24.startDragAndDrop(
-                    v, data, shadowBuilder, localState, flags);
-        }
-
-        @Override
-        public void cancelDragAndDrop(View v) {
-            ViewCompatApi24.cancelDragAndDrop(v);
-        }
-
-        @Override
-        public void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder) {
-            ViewCompatApi24.updateDragShadow(v, shadowBuilder);
-        }
-    }
-
-    static ViewCompatImpl IMPL;
-    static {
-        if (BuildCompat.isAtLeastN()) {
-            IMPL = new Api24ViewCompatImpl();
-        } else {
-            IMPL = new BaseViewCompatImpl();
-        }
-    }
-
-    /**
-     * Start the drag and drop operation.
-     */
-    public static boolean startDragAndDrop(View v, ClipData data,
-            View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
-        return IMPL.startDragAndDrop(v, data, shadowBuilder, localState, flags);
-    }
-
-    /**
-     * Cancel the drag and drop operation.
-     */
-    public static void cancelDragAndDrop(View v) {
-        IMPL.cancelDragAndDrop(v);
-    }
-
-    /**
-     * Update the drag shadow while drag and drop is in progress.
-     */
-    public static void updateDragShadow(View v, View.DragShadowBuilder shadowBuilder) {
-        IMPL.updateDragShadow(v, shadowBuilder);
-    }
-
     private ViewCompat() {
     }
 }
diff --git a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
index b1358d0..92743c2 100644
--- a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
@@ -16,23 +16,59 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v4.os.BuildCompat;
 import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
 
 /**
  * Helper for accessing features in {@link EditorInfo} introduced after API level 13 in a backwards
  * compatible fashion.
  */
-@RequiresApi(13)
-@TargetApi(13)
 public final class EditorInfoCompat {
 
+    /**
+     * Flag of {@link EditorInfo#imeOptions}: used to request that the IME does not update any
+     * personalized data such as typing history and personalized language model based on what the
+     * user typed on this text editing object.  Typical use cases are:
+     * <ul>
+     *     <li>When the application is in a special mode, where user's activities are expected to be
+     *     not recorded in the application's history.  Some web browsers and chat applications may
+     *     have this kind of modes.</li>
+     *     <li>When storing typing history does not make much sense.  Specifying this flag in typing
+     *     games may help to avoid typing history from being filled up with words that the user is
+     *     less likely to type in their daily life.  Another example is that when the application
+     *     already knows that the expected input is not a valid word (e.g. a promotion code that is
+     *     not a valid word in any natural language).</li>
+     * </ul>
+     *
+     * <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may not
+     * respect it.</p>
+     */
+    public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000;
+
+    /**
+     * Flag of {@link EditorInfo#imeOptions}: used to request an IME that is capable of inputting
+     * ASCII characters.
+     *
+     * <p>The intention of this flag is to ensure that the user can type Roman alphabet characters
+     * in a {@link android.widget.TextView}. It is typically used for an account ID or password
+     * input.</p>
+     *
+     * <p>In many cases, IMEs are already able to input ASCII even without being told so (such IMEs
+     * already respect this flag in a sense), but there are cases when this is not the default. For
+     * instance, users of languages using a different script like Arabic, Greek, Hebrew or Russian
+     * typically have a keyboard that can't input ASCII characters by default.</p>
+     *
+     * <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may not
+     * respect it. However, it is strongly recommended for IME authors to respect this flag
+     * especially when their IME could end up with a state where only languages using non-ASCII are
+     * enabled.</p>
+     */
+    public static final int IME_FLAG_FORCE_ASCII = 0x80000000;
+
     private interface EditorInfoCompatImpl {
         void setContentMimeTypes(@NonNull EditorInfo editorInfo,
                 @Nullable String[] contentMimeTypes);
@@ -40,9 +76,9 @@
         String[] getContentMimeTypes(@NonNull EditorInfo editorInfo);
     }
 
-    private final static String[] EMPTY_STRING_ARRAY = new String[0];
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
-    private final static class BaseEditorInfoCompatImpl implements EditorInfoCompatImpl {
+    private static final class EditorInfoCompatBaseImpl implements EditorInfoCompatImpl {
         private static String CONTENT_MIME_TYPES_KEY =
                 "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
 
@@ -66,27 +102,28 @@
         }
     }
 
-    private final static class Api25EditorInfoCompatImpl implements EditorInfoCompatImpl {
+    @RequiresApi(25)
+    private static final class EditorInfoCompatApi25Impl implements EditorInfoCompatImpl {
         @Override
         public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
                 @Nullable String[] contentMimeTypes) {
-            EditorInfoCompatApi25.setContentMimeTypes(editorInfo, contentMimeTypes);
+            editorInfo.contentMimeTypes = contentMimeTypes;
         }
 
         @NonNull
         @Override
         public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
-            String[] result = EditorInfoCompatApi25.getContentMimeTypes(editorInfo);
+            final String[] result = editorInfo.contentMimeTypes;
             return result != null ? result : EMPTY_STRING_ARRAY;
         }
     }
 
     private static final EditorInfoCompatImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastNMR1()) {
-            IMPL = new Api25EditorInfoCompatImpl();
+        if (Build.VERSION.SDK_INT >= 25) {
+            IMPL = new EditorInfoCompatApi25Impl();
         } else {
-            IMPL = new BaseEditorInfoCompatImpl();
+            IMPL = new EditorInfoCompatBaseImpl();
         }
     }
 
diff --git a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
index 5f7b012..5999575 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
@@ -16,26 +16,24 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.ClipDescription;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v4.os.BuildCompat;
 import android.text.TextUtils;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
 
 /**
  * Helper for accessing features in {@link InputConnection} introduced after API level 13 in a
  * backwards compatible fashion.
  */
-@RequiresApi(13)
-@TargetApi(13)
 public final class InputConnectionCompat {
 
     private interface InputConnectionCompatImpl {
@@ -47,7 +45,7 @@
                 @NonNull EditorInfo editorInfo, @NonNull OnCommitContentListener callback);
     }
 
-    static final class BaseInputContentInfoCompatImpl implements InputConnectionCompatImpl {
+    static final class InputContentInfoCompatBaseImpl implements InputConnectionCompatImpl {
 
         private static String COMMIT_CONTENT_ACTION =
                 "android.support.v13.view.inputmethod.InputConnectionCompat.COMMIT_CONTENT";
@@ -91,7 +89,7 @@
             return new InputConnectionWrapper(ic, false /* mutable */) {
                 @Override
                 public boolean performPrivateCommand(String action, Bundle data) {
-                    if (BaseInputContentInfoCompatImpl.handlePerformPrivateCommand(action, data,
+                    if (InputContentInfoCompatBaseImpl.handlePerformPrivateCommand(action, data,
                             listener)) {
                         return true;
                     }
@@ -132,14 +130,15 @@
         }
     }
 
-    private final static class Api25InputContentInfoCompatImpl
+    @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 InputConnectionCompatApi25.commitContent(inputConnection,
-                    inputContentInfo.unwrap(), flags, opts);
+            return inputConnection.commitContent((InputContentInfo) inputContentInfo.unwrap(),
+                    flags, opts);
         }
 
         @Nullable
@@ -148,26 +147,26 @@
                 @Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo,
                 @Nullable OnCommitContentListener onCommitContentListener) {
             final OnCommitContentListener listener = onCommitContentListener;
-            return InputConnectionCompatApi25.createWrapper(
-                    inputConnection,
-                    new InputConnectionCompatApi25.OnCommitContentListener() {
-                        @Override
-                        public boolean onCommitContent(Object inputContentInfo, int flags,
-                                Bundle opts) {
-                            InputContentInfoCompat inputContentInfoCompat =
-                                    InputContentInfoCompat.wrap(inputContentInfo);
-                            return listener.onCommitContent(inputContentInfoCompat, flags, opts);
-                        }
-            });
+            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 (BuildCompat.isAtLeastNMR1()) {
-            IMPL = new Api25InputContentInfoCompatImpl();
+        if (Build.VERSION.SDK_INT >= 25) {
+            IMPL = new InputContentInfoCompatApi25Impl();
         } else {
-            IMPL = new BaseInputContentInfoCompatImpl();
+            IMPL = new InputContentInfoCompatBaseImpl();
         }
     }
 
diff --git a/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
index 9379020..74b4775 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
@@ -16,20 +16,18 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.annotation.TargetApi;
 import android.content.ClipDescription;
 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.v4.os.BuildCompat;
+import android.view.inputmethod.InputContentInfo;
 
 /**
  * Helper for accessing features in InputContentInfo introduced after API level 13 in a backwards
  * compatible fashion.
  */
-@RequiresApi(13)
-@TargetApi(13)
 public final class InputContentInfoCompat {
 
     private interface InputContentInfoCompatImpl {
@@ -50,7 +48,7 @@
         void releasePermission();
     }
 
-    private final static class BaseInputContentInfoCompatImpl
+    private static final class InputContentInfoCompatBaseImpl
             implements InputContentInfoCompatImpl {
         @NonNull
         private final Uri mContentUri;
@@ -59,7 +57,7 @@
         @Nullable
         private final Uri mLinkUri;
 
-        public BaseInputContentInfoCompatImpl(@NonNull Uri contentUri,
+        InputContentInfoCompatBaseImpl(@NonNull Uri contentUri,
                 @NonNull ClipDescription description, @Nullable Uri linkUri) {
             mContentUri = contentUri;
             mDescription = description;
@@ -101,36 +99,37 @@
         }
     }
 
-    private final static class Api25InputContentInfoCompatImpl
+    @RequiresApi(25)
+    private static final class InputContentInfoCompatApi25Impl
             implements InputContentInfoCompatImpl {
         @NonNull
-        final Object mObject;
+        final InputContentInfo mObject;
 
-        public Api25InputContentInfoCompatImpl(@NonNull Object inputContentInfo) {
-            mObject = inputContentInfo;
+        InputContentInfoCompatApi25Impl(@NonNull Object inputContentInfo) {
+            mObject = (InputContentInfo) inputContentInfo;
         }
 
-        public Api25InputContentInfoCompatImpl(@NonNull Uri contentUri,
+        InputContentInfoCompatApi25Impl(@NonNull Uri contentUri,
                 @NonNull ClipDescription description, @Nullable Uri linkUri) {
-            mObject = InputContentInfoCompatApi25.create(contentUri, description, linkUri);
+            mObject = new InputContentInfo(contentUri, description, linkUri);
         }
 
         @Override
         @NonNull
         public Uri getContentUri() {
-            return InputContentInfoCompatApi25.getContentUri(mObject);
+            return mObject.getContentUri();
         }
 
         @Override
         @NonNull
         public ClipDescription getDescription() {
-            return InputContentInfoCompatApi25.getDescription(mObject);
+            return mObject.getDescription();
         }
 
         @Override
         @Nullable
         public Uri getLinkUri() {
-            return InputContentInfoCompatApi25.getLinkUri(mObject);
+            return mObject.getLinkUri();
         }
 
         @Override
@@ -141,12 +140,12 @@
 
         @Override
         public void requestPermission() {
-            InputContentInfoCompatApi25.requestPermission(mObject);
+            mObject.requestPermission();
         }
 
         @Override
         public void releasePermission() {
-            InputContentInfoCompatApi25.releasePermission(mObject);
+            mObject.releasePermission();
         }
     }
 
@@ -167,10 +166,10 @@
      */
     public InputContentInfoCompat(@NonNull Uri contentUri,
             @NonNull ClipDescription description, @Nullable Uri linkUri) {
-        if (BuildCompat.isAtLeastNMR1()) {
-            mImpl = new Api25InputContentInfoCompatImpl(contentUri, description, linkUri);
+        if (Build.VERSION.SDK_INT >= 25) {
+            mImpl = new InputContentInfoCompatApi25Impl(contentUri, description, linkUri);
         } else {
-            mImpl = new BaseInputContentInfoCompatImpl(contentUri, description, linkUri);
+            mImpl = new InputContentInfoCompatBaseImpl(contentUri, description, linkUri);
         }
     }
 
@@ -219,10 +218,10 @@
         if (inputContentInfo == null) {
             return null;
         }
-        if (!BuildCompat.isAtLeastNMR1()) {
+        if (Build.VERSION.SDK_INT < 25) {
             return null;
         }
-        return new InputContentInfoCompat(new Api25InputContentInfoCompatImpl(inputContentInfo));
+        return new InputContentInfoCompat(new InputContentInfoCompatApi25Impl(inputContentInfo));
     }
 
     /**
diff --git a/v13/lint-baseline.xml b/v13/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v13/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v13/tests/AndroidManifest.xml b/v13/tests/AndroidManifest.xml
index 4ce99ee..b2d04c9 100644
--- a/v13/tests/AndroidManifest.xml
+++ b/v13/tests/AndroidManifest.xml
@@ -15,25 +15,11 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="android.support.v13.test">
-
-    <uses-sdk
-        android:minSdkVersion="13"
-        android:targetSdkVersion="24"
-        tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+          package="android.support.v13.test">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application>
-
-        <uses-library android:name="android.test.runner"/>
-
         <activity android:name="android.support.v13.view.DragStartHelperTestActivity"/>
-
     </application>
 
-    <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="android.support.v13.test"/>
-
 </manifest>
diff --git a/v13/tests/java/android/support/v13/view/DragStartHelperTest.java b/v13/tests/java/android/support/v13/view/DragStartHelperTest.java
index ad6625f..993a4e5 100644
--- a/v13/tests/java/android/support/v13/view/DragStartHelperTest.java
+++ b/v13/tests/java/android/support/v13/view/DragStartHelperTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import android.annotation.TargetApi;
 import android.app.Instrumentation;
 import android.graphics.Point;
 import android.os.Build;
@@ -34,6 +33,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
@@ -44,7 +44,6 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 
-import org.hamcrest.Description;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -53,8 +52,6 @@
 import org.mockito.InOrder;
 
 @RequiresApi(13)
-@TargetApi(13)
-@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DragStartHelperTest {
 
@@ -137,7 +134,7 @@
                 action, buttonState, anchor, offsetX, offsetY));
     }
 
-    static class TouchPositionMatcher extends ArgumentMatcher<Point> {
+    static class TouchPositionMatcher implements ArgumentMatcher<Point> {
 
         private final Point mExpectedPosition;
 
@@ -149,12 +146,14 @@
             this(anchor.getWidth() / 2 + x, anchor.getHeight() / 2 + y);
         }
 
-        public boolean matches(Object actual) {
+        @Override
+        public boolean matches(Point actual) {
             return mExpectedPosition.equals(actual);
         }
 
-        public void describeTo(Description description) {
-            description.appendText("TouchPositionMatcher: " + mExpectedPosition);
+        @Override
+        public String toString() {
+            return "TouchPositionMatcher: " + mExpectedPosition;
         }
     }
 
@@ -168,6 +167,7 @@
         mDragSource = mActivityRule.getActivity().findViewById(R.id.drag_source);
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mouseClick() throws Throwable {
@@ -182,6 +182,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mousePressWithSecondaryButton() throws Throwable {
@@ -198,6 +199,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mouseDrag() throws Throwable {
@@ -216,6 +218,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mouseDragWithNonprimaryButton() throws Throwable {
@@ -235,6 +238,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mouseDragUsingTouchListener() throws Throwable {
@@ -260,6 +264,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mouseDragWhenListenerReturnsFalse() throws Throwable {
@@ -283,6 +288,7 @@
         inOrder.verifyNoMoreInteractions();
     }
 
+    @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     @Test
     public void mouseLongPress() throws Throwable {
@@ -299,6 +305,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @Test
     public void touchDrag() throws Throwable {
         final DragStartListener listener = createListener(false);
@@ -314,6 +321,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @SmallTest
     @Test
     public void touchTap() throws Throwable {
         final DragStartListener listener = createListener(false);
@@ -327,6 +335,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @LargeTest
     @Test
     public void touchLongPress() throws Throwable {
         final DragStartListener listener = createListener(true);
@@ -342,6 +351,7 @@
         verifyNoMoreInteractions(listener);
     }
 
+    @LargeTest
     @Test
     public void touchLongPressUsingLongClickListener() throws Throwable {
         final DragStartListener listener = createListener(true);
diff --git a/v14/preference/Android.mk b/v14/preference/Android.mk
index 195e8a3..7a0b846 100644
--- a/v14/preference/Android.mk
+++ b/v14/preference/Android.mk
@@ -31,7 +31,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-preference \
     android-support-v7-appcompat \
diff --git a/v14/preference/AndroidManifest-make.xml b/v14/preference/AndroidManifest-make.xml
deleted file mode 100644
index b917bb4..0000000
--- a/v14/preference/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.support.v14.preference">
-    <uses-sdk android:minSdkVersion="14" />
-    <application />
-</manifest>
diff --git a/v14/preference/NOTICES.md b/v14/preference/NOTICES.md
deleted file mode 100644
index a390782..0000000
--- a/v14/preference/NOTICES.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Change Log
-
-## [23.1.0](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v14/preference) (2015-09-28)
-
-**Breakage and deprecation notices:**
-
-- EditTextPreferenceDialogFragment
-  - onAddEditTextToDialogView has been removed. Any code depending on overriding this method should
-    be moved to onBindDialogView.
-  - The EditText view is now expected to be present in the dialog layout file with the id
-    @android:id/edit, and is no longer created in code.
diff --git a/v14/preference/build.gradle b/v14/preference/build.gradle
index 5583e93..7df48af 100644
--- a/v14/preference/build.gradle
+++ b/v14/preference/build.gradle
@@ -14,89 +14,31 @@
  * limitations under the License
  */
 
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'preference-v14'
 
 dependencies {
-    compile project(':support-v4')
-    compile project(':support-appcompat-v7')
-    compile project(':support-recyclerview-v7')
-    compile project(':support-preference-v7')
+    api project(':support-v4')
+    api project(':support-appcompat-v7')
+    api project(':support-recyclerview-v7')
+    api project(':support-preference-v7')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
         minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
         main.res.srcDir 'res'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
-
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Preference v14'
-                description "Android Support Preference v14"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Preference v14'
+    inceptionYear '2015'
+    description 'Android Support Preference v14'
 }
diff --git a/v14/preference/lint-baseline.xml b/v14/preference/lint-baseline.xml
new file mode 100644
index 0000000..1d140cc
--- /dev/null
+++ b/v14/preference/lint-baseline.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        android:layout_width=&quot;0dp&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout-v17/preference_dropdown_material.xml"
+            line="32"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        android:layout_width=&quot;0dp&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout-v21/preference_dropdown_material.xml"
+            line="32"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        android:layout_width=&quot;0dp&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/preference_dropdown_material.xml"
+            line="32"
+            column="9"/>
+    </issue>
+
+</issues>
diff --git a/v14/preference/res/layout-v21/preference_dropdown_material.xml b/v14/preference/res/layout-v21/preference_dropdown_material.xml
index 8e984c1..a92095e 100644
--- a/v14/preference/res/layout-v21/preference_dropdown_material.xml
+++ b/v14/preference/res/layout-v21/preference_dropdown_material.xml
@@ -70,7 +70,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
+            android:layout_alignStart="@android:id/title"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10" />
diff --git a/v14/preference/res/layout/preference_dropdown_material.xml b/v14/preference/res/layout/preference_dropdown_material.xml
index 8e984c1..a92095e 100644
--- a/v14/preference/res/layout/preference_dropdown_material.xml
+++ b/v14/preference/res/layout/preference_dropdown_material.xml
@@ -70,7 +70,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
+            android:layout_alignStart="@android:id/title"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10" />
diff --git a/v14/preference/res/layout/preference_widget_seekbar_material.xml b/v14/preference/res/layout/preference_widget_seekbar_material.xml
new file mode 100644
index 0000000..2a6fd98
--- /dev/null
+++ b/v14/preference/res/layout/preference_widget_seekbar_material.xml
@@ -0,0 +1,97 @@
+<?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.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              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:clipChildren="false"
+              android:clipToPadding="false">
+
+    <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:minWidth="@dimen/preference_icon_minWidth"/>
+
+    <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dip"
+            android:layout_marginEnd="8dip"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="6dip"
+            android:layout_weight="1"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+
+        <TextView android:id="@android:id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:singleLine="true"
+                  android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+                  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"/>
+
+        <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+        to the children of this container layout. Otherwise, the animated pressed state will also
+        play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+        The background of the SeekBar is also set to null to disable the ripple background -->
+        <android.support.v7.preference.UnPressableLinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/summary"
+                android:layout_alignStart="@android:id/title"
+                android:clipChildren="false"
+                android:clipToPadding="false">
+            <SeekBar
+                    android:id="@+id/seekbar"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:paddingStart="@dimen/preference_seekbar_padding_start"
+                    android:paddingEnd="@dimen/preference_seekbar_padding_end"
+                    android:focusable="false"
+                    android:clickable="false"
+                    android:background="@null" />
+
+            <TextView android:id="@+id/seekbar_value"
+                      android:layout_width="@dimen/preference_seekbar_value_width"
+                      android:layout_height="match_parent"
+                      android:gravity="right|center_vertical"
+                      android:fontFamily="sans-serif-condensed"
+                      android:singleLine="true"
+                      android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+                      android:ellipsize="marquee"
+                      android:fadingEdge="horizontal"/>
+        </android.support.v7.preference.UnPressableLinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/v14/preference/res/values/attrs.xml b/v14/preference/res/values/attrs.xml
index 7aa2245..7a3cd51 100644
--- a/v14/preference/res/values/attrs.xml
+++ b/v14/preference/res/values/attrs.xml
@@ -49,6 +49,8 @@
         <attr name="android:divider" />
         <!-- List separator height -->
         <attr name="android:dividerHeight" />
+        <!-- Whether a divider is allowed to draw after the last item -->
+        <attr name="allowDividerAfterLastItem" />
     </declare-styleable>
 
 </resources>
diff --git a/v14/preference/res/values/styles.xml b/v14/preference/res/values/styles.xml
index baef67f..26b1544 100644
--- a/v14/preference/res/values/styles.xml
+++ b/v14/preference/res/values/styles.xml
@@ -48,6 +48,12 @@
         <item name="android:layout">@layout/preference_material</item>
     </style>
 
+    <style name="Preference.SeekBarPreference.Material">
+        <item name="android:layout">@layout/preference_widget_seekbar_material</item>
+        <item name="adjustable">true</item>
+        <item name="showSeekBarValue">true</item>
+    </style>
+
     <style name="Preference.PreferenceScreen.Material">
         <item name="android:layout">@layout/preference_material</item>
     </style>
diff --git a/v14/preference/res/values/themes.xml b/v14/preference/res/values/themes.xml
index 026d2d8..a69126f 100644
--- a/v14/preference/res/values/themes.xml
+++ b/v14/preference/res/values/themes.xml
@@ -31,6 +31,7 @@
         <item name="checkBoxPreferenceStyle">@style/Preference.CheckBoxPreference.Material</item>
         <item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat.Material</item>
         <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.Material</item>
+        <item name="seekBarPreferenceStyle">@style/Preference.SeekBarPreference.Material</item>
         <item name="dialogPreferenceStyle">@style/Preference.DialogPreference.Material</item>
         <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference.Material</item>
         <item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
diff --git a/v14/preference/src/android/support/v14/preference/ListPreferenceDialogFragment.java b/v14/preference/src/android/support/v14/preference/ListPreferenceDialogFragment.java
index 6b385d8..6119071 100644
--- a/v14/preference/src/android/support/v14/preference/ListPreferenceDialogFragment.java
+++ b/v14/preference/src/android/support/v14/preference/ListPreferenceDialogFragment.java
@@ -80,6 +80,7 @@
 
         builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
                 new DialogInterface.OnClickListener() {
+                    @Override
                     public void onClick(DialogInterface dialog, int which) {
                         mClickedDialogEntryIndex = which;
 
diff --git a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
index cb38c44..f34b7dc 100644
--- a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
+++ b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
@@ -16,17 +16,12 @@
 
 package android.support.v14.preference;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.content.res.TypedArray;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.ArrayRes;
 import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.SharedPreferencesCompat;
 import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
 import android.util.AttributeSet;
@@ -85,64 +80,6 @@
     }
 
     /**
-     * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}.
-     * <p>
-     * This will check if this Preference is persistent, get an editor from
-     * the {@link android.preference.PreferenceManager}, put in the strings, and check if we should
-     * commit (and commit if so).
-     *
-     * @param values The values to persist.
-     * @return True if the Preference is persistent. (This is not whether the
-     *         value was persisted, since we may not necessarily commit if there
-     *         will be a batch commit later.)
-     * @see #getPersistedString
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean persistStringSet(Set<String> values) {
-        if (shouldPersist()) {
-            // Shouldn't store null
-            if (values.equals(getPersistedStringSet(null))) {
-                // It's already there, so the same as persisting
-                return true;
-            }
-
-            SharedPreferences.Editor editor = getPreferenceManager().getSharedPreferences().edit();
-            editor.putStringSet(getKey(), values);
-            SharedPreferencesCompat.EditorCompat.getInstance().apply(editor);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Attempts to get a persisted set of Strings from the
-     * {@link android.content.SharedPreferences}.
-     * <p>
-     * This will check if this Preference is persistent, get the SharedPreferences
-     * from the {@link android.preference.PreferenceManager}, and get the value.
-     *
-     * @param defaultReturnValue The default value to return if either the
-     *            Preference is not persistent or the Preference is not in the
-     *            shared preferences.
-     * @return The value from the SharedPreferences or the default return
-     *         value.
-     * @see #persistStringSet(Set)
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
-        if (!shouldPersist()) {
-            return defaultReturnValue;
-        }
-
-        return getPreferenceManager().getSharedPreferences()
-                .getStringSet(getKey(), defaultReturnValue);
-    }
-
-    /**
      * Sets the human-readable entries to be shown in the list. This will be
      * shown in subsequent dialogs.
      * <p>
@@ -169,6 +106,7 @@
      *
      * @return The list as an array.
      */
+    @Override
     public CharSequence[] getEntries() {
         return mEntries;
     }
@@ -197,6 +135,7 @@
      *
      * @return The array of values.
      */
+    @Override
     public CharSequence[] getEntryValues() {
         return mEntryValues;
     }
@@ -207,6 +146,7 @@
      *
      * @param values The values to set for the key.
      */
+    @Override
     public void setValues(Set<String> values) {
         mValues.clear();
         mValues.addAll(values);
@@ -217,6 +157,7 @@
     /**
      * Retrieves the current value of the key.
      */
+    @Override
     public Set<String> getValues() {
         return mValues;
     }
@@ -263,6 +204,7 @@
         return result;
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
         setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
@@ -320,10 +262,12 @@
 
         public static final Parcelable.Creator<SavedState> CREATOR =
                 new Parcelable.Creator<SavedState>() {
+                    @Override
                     public SavedState createFromParcel(Parcel in) {
                         return new SavedState(in);
                     }
 
+                    @Override
                     public SavedState[] newArray(int size) {
                         return new SavedState[size];
                     }
diff --git a/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java b/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
index 6f116db..8192583 100644
--- a/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
+++ b/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
@@ -102,6 +102,7 @@
         }
         builder.setMultiChoiceItems(mEntries, checkedItems,
                 new DialogInterface.OnMultiChoiceClickListener() {
+                    @Override
                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                         if (isChecked) {
                             mPreferenceChanged |= mNewValues.add(
diff --git a/v14/preference/src/android/support/v14/preference/PreferenceFragment.java b/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
index d2519d1..d1d9987 100644
--- a/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
+++ b/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
@@ -28,11 +28,11 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.XmlRes;
 import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.preference.AndroidResources;
 import android.support.v7.preference.DialogPreference;
 import android.support.v7.preference.EditTextPreference;
@@ -158,6 +158,7 @@
     };
 
     final private Runnable mRequestFocus = new Runnable() {
+        @Override
         public void run() {
             mList.focusableViewAvailable(mList);
         }
@@ -207,7 +208,7 @@
          * @param pref The preference requesting the dialog.
          * @return true if the dialog creation has been handled.
          */
-        boolean onPreferenceDisplayDialog(PreferenceFragment caller, Preference pref);
+        boolean onPreferenceDisplayDialog(@NonNull PreferenceFragment caller, Preference pref);
     }
 
     @Override
@@ -217,7 +218,7 @@
         getActivity().getTheme().resolveAttribute(
                 android.support.v7.preference.R.attr.preferenceTheme, tv, true);
         final int theme = tv.resourceId;
-        if (theme <= 0) {
+        if (theme == 0) {
             throw new IllegalStateException("Must specify preferenceTheme in theme");
         }
         mStyledContext = new ContextThemeWrapper(getActivity(), theme);
@@ -262,7 +263,8 @@
         final Drawable divider = a.getDrawable(R.styleable.PreferenceFragment_android_divider);
         final int dividerHeight = a.getDimensionPixelSize(
                 R.styleable.PreferenceFragment_android_dividerHeight, -1);
-
+        final boolean allowDividerAfterLastItem = a.getBoolean(
+                R.styleable.PreferenceFragment_allowDividerAfterLastItem, true);
         a.recycle();
 
         // Need to theme the inflater to pick up the preferenceFragmentListStyle
@@ -297,6 +299,7 @@
         if (dividerHeight != -1) {
             setDividerHeight(dividerHeight);
         }
+        mDividerDecoration.setAllowDividerAfterLastItem(allowDividerAfterLastItem);
 
         listContainer.addView(mList);
         mHandler.post(mRequestFocus);
@@ -473,6 +476,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public boolean onPreferenceTreeClick(Preference preference) {
         if (preference.getFragment() != null) {
             boolean handled = false;
@@ -519,6 +523,7 @@
      * @return The {@link Preference} with the key, or null.
      * @see android.support.v7.preference.PreferenceGroup#findPreference(CharSequence)
      */
+    @Override
     public Preference findPreference(CharSequence key) {
         if (mPreferenceManager == null) {
             return null;
@@ -779,6 +784,7 @@
 
         private Drawable mDivider;
         private int mDividerHeight;
+        private boolean mAllowDividerAfterLastItem = true;
 
         @Override
         public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
@@ -790,7 +796,7 @@
             for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
                 final View view = parent.getChildAt(childViewIndex);
                 if (shouldDrawDividerBelow(view, parent)) {
-                    int top = (int) ViewCompat.getY(view) + view.getHeight();
+                    int top = (int) view.getY() + view.getHeight();
                     mDivider.setBounds(0, top, width, top + mDividerHeight);
                     mDivider.draw(c);
                 }
@@ -812,7 +818,7 @@
             if (!dividerAllowedBelow) {
                 return false;
             }
-            boolean nextAllowed = true;
+            boolean nextAllowed = mAllowDividerAfterLastItem;
             int index = parent.indexOfChild(view);
             if (index < parent.getChildCount() - 1) {
                 final View nextView = parent.getChildAt(index + 1);
@@ -837,5 +843,9 @@
             mDividerHeight = dividerHeight;
             mList.invalidateItemDecorations();
         }
+
+        public void setAllowDividerAfterLastItem(boolean allowDividerAfterLastItem) {
+            mAllowDividerAfterLastItem = allowDividerAfterLastItem;
+        }
     }
 }
diff --git a/v17/leanback/.classpath b/v17/leanback/.classpath
deleted file mode 100644
index f568681..0000000
--- a/v17/leanback/.classpath
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="api21"/>
-	<classpathentry kind="src" path="api23"/>
-	<classpathentry kind="src" path="jbmr2"/>
-	<classpathentry kind="src" path="common"/>
-	<classpathentry kind="src" path="kitkat"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v17/leanback/.gitignore b/v17/leanback/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v17/leanback/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v17/leanback/.project b/v17/leanback/.project
deleted file mode 100644
index 9d191cd..0000000
--- a/v17/leanback/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>android-support-v17-leanback</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk
index d91436e..dbfe43a 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -35,7 +35,6 @@
     $(call all-java-files-under, api23) \
     $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-recyclerview \
     android-support-compat \
@@ -47,31 +46,3 @@
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Documentation
-# ===========================================================
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v17-leanback
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-html-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAVA_LIBRARIES := \
-    android-support-annotations \
-    android-support-v4 \
-    android-support-v7-recyclerview \
-    android-support-v17-leanback
-LOCAL_ADDITIONAL_JAVA_DIR := $(call intermediates-dir-for,JAVA_LIBRARIES,android-support-v17-leanback-res,,COMMON)/src
-LOCAL_IS_HOST_MODULE := false
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
-LOCAL_DROIDDOC_OPTIONS := \
-    -offlinemode \
-    -hdf android.whichdoc offline \
-    -federate Android http://developer.android.com \
-    -federationapi Android prebuilts/sdk/api/17.txt \
-    -hide 113
-include $(BUILD_DROIDDOC)
diff --git a/v17/leanback/AndroidManifest-make.xml b/v17/leanback/AndroidManifest-make.xml
deleted file mode 100644
index 20ef094..0000000
--- a/v17/leanback/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v17.leanback">
-    <uses-sdk android:minSdkVersion="17"/>
-    <application />
-</manifest>
diff --git a/v17/leanback/README.txt b/v17/leanback/README.txt
deleted file mode 100644
index f3dbe92..0000000
--- a/v17/leanback/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Library Project including Leanback framework support.
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
index 76e237b..0e135a9 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
@@ -20,7 +20,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -42,7 +41,6 @@
  * @hide
  */
 @RequiresApi(21)
-@TargetApi(21)
 @RestrictTo(LIBRARY_GROUP)
 public class FadeAndShortSlide extends Visibility {
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java b/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
index 5158992..46c67ac 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
@@ -15,7 +15,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
@@ -26,7 +25,6 @@
  * @hide
  */
 @RequiresApi(21)
-@TargetApi(21)
 @RestrictTo(LIBRARY_GROUP)
 public class SlideNoPropagation extends Slide {
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
index 2d0ab62..dd1253a 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
@@ -14,7 +14,6 @@
 package android.support.v17.leanback.transition;
 
 import android.R;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Rect;
 import android.support.annotation.RequiresApi;
@@ -27,7 +26,6 @@
 import android.view.animation.AnimationUtils;
 
 @RequiresApi(21)
-@TargetApi(21)
 final class TransitionHelperApi21 {
 
     TransitionHelperApi21() {
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
index 15b9081..157118e 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
@@ -6,7 +6,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
 import android.graphics.Path;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
@@ -23,7 +22,6 @@
  * @hide
  */
 @RequiresApi(21)
-@TargetApi(21)
 @RestrictTo(LIBRARY_GROUP)
 class TranslationAnimationCreator {
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
index c0ba7dd..df1f0f3 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
@@ -13,7 +13,6 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.Outline;
 import android.support.annotation.RequiresApi;
 import android.util.SparseArray;
@@ -21,7 +20,6 @@
 import android.view.ViewOutlineProvider;
 
 @RequiresApi(21)
-@TargetApi(21)
 class RoundedRectHelperApi21 {
 
     private static SparseArray<ViewOutlineProvider> sRoundedRectProvider;
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
index 35f2c51..4e03d8a 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
@@ -13,14 +13,12 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.Outline;
 import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
 @RequiresApi(21)
-@TargetApi(21)
 class ShadowHelperApi21 {
 
     static class ShadowImpl {
diff --git a/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java b/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
index 82ba369..4d5d5ad 100644
--- a/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
+++ b/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
@@ -13,11 +13,9 @@
  */
 package android.support.v17.leanback.app;
 
-import android.annotation.TargetApi;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(23)
-@TargetApi(23)
 class PermissionHelper23 {
 
     public static void requestPermissions(android.app.Fragment fragment, String[] permissions,
diff --git a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
index f00f43d..e02eea9 100644
--- a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
+++ b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
@@ -13,13 +13,11 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RequiresApi;
 import android.view.View;
 
 @RequiresApi(23)
-@TargetApi(23)
 class ForegroundHelperApi23 {
 
     public static Drawable getForeground(View view) {
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index 9ed65a8..ea11627 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -1,34 +1,25 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'leanback-v17'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-core-ui')
-    compile project(':support-media-compat')
-    compile project(':support-fragment')
-    compile project(':support-recyclerview-v7')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
-        exclude module: 'support-annotations'
-    }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
-        exclude module: 'support-annotations'
-    }
-    testCompile 'junit:junit:4.12'
-    androidTestCompile "org.mockito:mockito-core:1.9.5"
-    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
-    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
+    api project(':support-compat')
+    api project(':support-core-ui')
+    api project(':support-media-compat')
+    api project(':support-fragment')
+    api project(':support-recyclerview-v7')
+
+    androidTestImplementation libs.test_runner,      { exclude module: 'support-annotations' }
+    androidTestImplementation libs.espresso_core,    { exclude module: 'support-annotations' }
+    androidTestImplementation libs.mockito_core,     { exclude group: 'net.bytebuddy' } // DexMaker has it"s own MockMaker
+    androidTestImplementation libs.dexmaker_mockito, { exclude group: 'net.bytebuddy' } // DexMaker has it"s own MockMaker
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
         minSdkVersion 17
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
                 'common',
                 'jbmr2',
@@ -38,70 +29,11 @@
                 'src'
         ]
         main.res.srcDir 'res'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    lintOptions {
-        // Remove this once all NewApi breakages have been fixed.
-        disable "NewApi"
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Leanback v17'
-                description "Android Support Leanback v17"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Leanback v17'
+    inceptionYear '2014'
+    description 'Android Support Leanback v17'
 }
diff --git a/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java b/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
index 4ee6b29..8cd0054 100644
--- a/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
+++ b/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
@@ -13,7 +13,6 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.annotation.TargetApi;
 import android.support.annotation.RequiresApi;
 import android.support.v17.leanback.R;
 import android.view.LayoutInflater;
@@ -21,7 +20,6 @@
 import android.view.ViewGroup;
 
 @RequiresApi(18)
-@TargetApi(18)
 class ShadowHelperJbmr2 {
 
     static class ShadowImpl {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
index b6a82b7..bb30b00 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
@@ -13,7 +13,6 @@
  */
 package android.support.v17.leanback.transition;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.support.v17.leanback.R;
@@ -21,7 +20,6 @@
 import android.view.animation.AnimationUtils;
 
 @RequiresApi(19)
-@TargetApi(19)
 class LeanbackTransitionHelperKitKat {
 
     static public Object loadTitleInTransition(Context context) {
@@ -37,7 +35,7 @@
         SlideKitkat slide = new SlideKitkat();
         slide.setSlideEdge(Gravity.TOP);
         slide.setInterpolator(AnimationUtils.loadInterpolator(context,
-                R.animator.lb_decelerator_4));
+                R.anim.lb_decelerator_4));
         slide.addTarget(R.id.browse_title_group);
         return slide;
     }
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
index 5fbf414..2fe4c9a 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
@@ -17,7 +17,6 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
 import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
@@ -25,7 +24,6 @@
 import android.transition.TransitionValues;
 
 @RequiresApi(19)
-@TargetApi(19)
 class Scale extends Transition {
     private static final String PROPNAME_SCALE = "android:leanback:scale";
 
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
index e8e4c10..2d74958 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.annotation.RequiresApi;
@@ -40,7 +39,6 @@
  * This is a limited Slide implementation for KitKat without propagation support.
  */
 @RequiresApi(19)
-@TargetApi(19)
 class SlideKitkat extends Visibility {
     private static final String TAG = "SlideKitkat";
 
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
index 777b34b..211e8fc 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
@@ -15,7 +15,6 @@
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.transition.AutoTransition;
@@ -27,16 +26,13 @@
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
 import android.transition.TransitionValues;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.view.View;
 import android.view.ViewGroup;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 
 @RequiresApi(19)
-@TargetApi(19)
 final class TransitionHelperKitkat {
 
     TransitionHelperKitkat() {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
index 49cb35e..64c02a7 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
@@ -15,13 +15,11 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RequiresApi;
 import android.view.View;
 
 @RequiresApi(19)
-@TargetApi(19)
 class BackgroundHelperKitkat {
 
     public static void setBackgroundPreservingAlpha(View view, Drawable drawable) {
diff --git a/v17/leanback/lint-baseline.xml b/v17/leanback/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v17/leanback/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v17/leanback/project.properties b/v17/leanback/project.properties
deleted file mode 100644
index b2ef7dc..0000000
--- a/v17/leanback/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-23
-android.library=true
diff --git a/v17/leanback/res/animator/lb_decelerator_2.xml b/v17/leanback/res/anim/lb_decelerator_2.xml
similarity index 100%
rename from v17/leanback/res/animator/lb_decelerator_2.xml
rename to v17/leanback/res/anim/lb_decelerator_2.xml
diff --git a/v17/leanback/res/animator/lb_decelerator_4.xml b/v17/leanback/res/anim/lb_decelerator_4.xml
similarity index 100%
rename from v17/leanback/res/animator/lb_decelerator_4.xml
rename to v17/leanback/res/anim/lb_decelerator_4.xml
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_description_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_description_enter.xml
new file mode 100644
index 0000000..3cb5843
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_description_enter.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_description_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="60dp"
+        android:valueTo="0dp"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_description_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_logo_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_logo_enter.xml
new file mode 100644
index 0000000..76a4609
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_logo_enter.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="333"
+        android:interpolator="@android:interpolator/linear_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_logo_exit.xml b/v17/leanback/res/animator-v21/lb_onboarding_logo_exit.xml
new file mode 100644
index 0000000..40b618e
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_logo_exit.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:duration="666"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_page_indicator_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_page_indicator_enter.xml
new file mode 100644
index 0000000..e9fc46e
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_page_indicator_enter.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="500"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_title_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_title_enter.xml
new file mode 100644
index 0000000..9b65b48
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_title_enter.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_title_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="60dp"
+        android:valueTo="0dp"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_title_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator/lb_guidedactions_item_pressed.xml b/v17/leanback/res/animator/lb_guidedactions_item_pressed.xml
index d00e13b..28d4dbb 100644
--- a/v17/leanback/res/animator/lb_guidedactions_item_pressed.xml
+++ b/v17/leanback/res/animator/lb_guidedactions_item_pressed.xml
@@ -17,6 +17,5 @@
 <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="@integer/lb_guidedactions_item_animation_duration"
     android:propertyName="alpha"
-    android:valueFrom="1.0"
     android:valueTo="0.2"
     android:valueType="floatType" />
diff --git a/v17/leanback/res/animator/lb_guidedactions_item_unpressed.xml b/v17/leanback/res/animator/lb_guidedactions_item_unpressed.xml
index 0cf30a4..5b85509 100644
--- a/v17/leanback/res/animator/lb_guidedactions_item_unpressed.xml
+++ b/v17/leanback/res/animator/lb_guidedactions_item_unpressed.xml
@@ -17,6 +17,5 @@
 <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="@integer/lb_guidedactions_item_animation_duration"
     android:propertyName="alpha"
-    android:valueFrom="0.2"
     android:valueTo="1.0"
     android:valueType="floatType" />
diff --git a/v17/leanback/res/animator/lb_onboarding_description_enter.xml b/v17/leanback/res/animator/lb_onboarding_description_enter.xml
index 5f26cdd..d8393bd 100644
--- a/v17/leanback/res/animator/lb_onboarding_description_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_description_enter.xml
@@ -21,11 +21,11 @@
         android:valueFrom="0.0"
         android:valueTo="1.0"
         android:duration="533"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_description_delay" />
     <objectAnimator
         android:propertyName="translationY"
         android:valueFrom="60dp"
         android:valueTo="0dp"
         android:duration="533"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_description_delay" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_logo_enter.xml b/v17/leanback/res/animator/lb_onboarding_logo_enter.xml
index 76a4609..5d8d762 100644
--- a/v17/leanback/res/animator/lb_onboarding_logo_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_logo_enter.xml
@@ -20,6 +20,5 @@
         android:propertyName="alpha"
         android:valueFrom="0.0"
         android:valueTo="1.0"
-        android:duration="333"
-        android:interpolator="@android:interpolator/linear_out_slow_in" />
+        android:duration="333" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_logo_exit.xml b/v17/leanback/res/animator/lb_onboarding_logo_exit.xml
index 40b618e..820ba8e 100644
--- a/v17/leanback/res/animator/lb_onboarding_logo_exit.xml
+++ b/v17/leanback/res/animator/lb_onboarding_logo_exit.xml
@@ -20,6 +20,5 @@
         android:propertyName="alpha"
         android:valueFrom="1.0"
         android:valueTo="0.0"
-        android:duration="666"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:duration="666" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml b/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml
index e9fc46e..b8bd083 100644
--- a/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml
@@ -20,6 +20,5 @@
         android:propertyName="alpha"
         android:valueFrom="0.0"
         android:valueTo="1.0"
-        android:duration="500"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:duration="500" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_title_enter.xml b/v17/leanback/res/animator/lb_onboarding_title_enter.xml
index 5f26cdd..011f51c 100644
--- a/v17/leanback/res/animator/lb_onboarding_title_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_title_enter.xml
@@ -21,11 +21,11 @@
         android:valueFrom="0.0"
         android:valueTo="1.0"
         android:duration="533"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_title_delay" />
     <objectAnimator
         android:propertyName="translationY"
         android:valueFrom="60dp"
         android:valueTo="0dp"
         android:duration="533"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_title_delay" />
 </set>
diff --git a/v17/leanback/res/layout/lb_onboarding_fragment.xml b/v17/leanback/res/layout/lb_onboarding_fragment.xml
index 04bd0ea..823fe74 100644
--- a/v17/leanback/res/layout/lb_onboarding_fragment.xml
+++ b/v17/leanback/res/layout/lb_onboarding_fragment.xml
@@ -28,10 +28,15 @@
         android:layout_height="match_parent"
         android:visibility="gone" />
 
+    <ImageView
+        android:id="@+id/main_icon"
+        style="?attr/onboardingMainIconStyle"/>
+
     <LinearLayout
         android:id="@+id/page_container"
         style="?attr/onboardingHeaderStyle"
         android:visibility="gone">
+
         <TextView
             android:id="@+id/title"
             style="?attr/onboardingTitleStyle"/>
diff --git a/v17/leanback/res/layout/lb_playback_controls_row.xml b/v17/leanback/res/layout/lb_playback_controls_row.xml
index b449fa9..30d06bd 100644
--- a/v17/leanback/res/layout/lb_playback_controls_row.xml
+++ b/v17/leanback/res/layout/lb_playback_controls_row.xml
@@ -67,7 +67,7 @@
             <Space
                 android:id="@+id/spacer"
                 android:layout_width="match_parent"
-                android:layout_height="24dp" />
+                android:layout_height="16dp" />
 
             <FrameLayout
                 android:id="@+id/controls_dock"
diff --git a/v17/leanback/res/layout/lb_playback_fragment.xml b/v17/leanback/res/layout/lb_playback_fragment.xml
index 1b0ffa1..ab2909b 100644
--- a/v17/leanback/res/layout/lb_playback_fragment.xml
+++ b/v17/leanback/res/layout/lb_playback_fragment.xml
@@ -21,7 +21,13 @@
         android:transitionGroup="false"
         android:layout_height="match_parent">
 
-    <FrameLayout
+    <android.support.v17.leanback.widget.NonOverlappingFrameLayout
+        android:id="@+id/playback_fragment_background"
+        android:transitionGroup="false"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <android.support.v17.leanback.widget.NonOverlappingFrameLayout
             android:id="@+id/playback_controls_dock"
             android:transitionGroup="true"
             android:layout_height="match_parent"
diff --git a/v17/leanback/res/layout/lb_playback_transport_controls.xml b/v17/leanback/res/layout/lb_playback_transport_controls.xml
new file mode 100644
index 0000000..d4380f3
--- /dev/null
+++ b/v17/leanback/res/layout/lb_playback_transport_controls.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <FrameLayout
+        android:id="@+id/controls_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <android.support.v17.leanback.widget.ControlBar
+            android:id="@+id/control_bar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layoutDirection="ltr"
+            android:orientation="horizontal" />
+
+
+    </FrameLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_playback_transport_controls_row.xml b/v17/leanback/res/layout/lb_playback_transport_controls_row.xml
new file mode 100644
index 0000000..8b692f3
--- /dev/null
+++ b/v17/leanback/res/layout/lb_playback_transport_controls_row.xml
@@ -0,0 +1,139 @@
+<?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.
+  -->
+
+<!-- Note: clipChildren/clipToPadding false are needed to apply shadows to child
+     views with no padding of their own. Also to allow for negative margin on description. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:paddingBottom="@dimen/lb_playback_transport_control_row_padding_bottom" >
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:id="@+id/controls_card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:clipToPadding="false"
+            android:paddingStart="?attr/browsePaddingStart"
+            android:paddingEnd="?attr/browsePaddingEnd"
+            android:layout_marginBottom="@dimen/lb_playback_transport_control_info_margin_bottom"
+            android:orientation="horizontal" >
+
+            <ImageView
+                android:id="@+id/image"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/lb_playback_transport_image_height"
+                android:layout_gravity="top"
+                android:adjustViewBounds="true"
+                android:layout_marginEnd="@dimen/lb_playback_transport_image_margin_end"
+                android:scaleType="fitStart" />
+
+            <FrameLayout
+                android:id="@+id/description_dock"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top"
+                android:clipToPadding="false"
+                android:gravity="bottom" />
+        </LinearLayout>
+        <android.support.v17.leanback.widget.ThumbsBar
+            android:id="@+id/thumbs_row"
+            android:orientation="horizontal"
+            android:visibility="invisible"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginBottom="@dimen/lb_playback_transport_thumbs_bottom_margin" />
+    </FrameLayout>
+
+    <android.support.v17.leanback.widget.PlaybackTransportRowView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/transport_row"
+        android:orientation="vertical"
+        android:paddingStart="?attr/browsePaddingStart"
+        android:paddingEnd="?attr/browsePaddingEnd"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+        <FrameLayout
+            android:id="@+id/controls_dock"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layoutDirection="ltr"
+            android:layout_marginLeft="@dimen/lb_playback_transport_controlbar_margin_start"
+        />
+
+        <android.support.v17.leanback.widget.SeekBar
+            android:id="@+id/playback_progress"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/lb_playback_transport_progressbar_height"
+            android:focusable="true" />
+
+        <RelativeLayout android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layoutDirection="ltr"
+                        android:layout_marginLeft="@dimen/lb_playback_transport_controlbar_margin_start">
+            <FrameLayout
+                android:id="@+id/secondary_controls_dock"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentStart="true" >
+            </FrameLayout>
+
+            <TextView
+                android:id="@+id/current_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top"
+                android:layout_toStartOf="@+id/separate_time"
+                android:layout_marginStart="@dimen/lb_playback_transport_time_margin"
+                android:layout_marginTop="@dimen/lb_playback_transport_time_margin_top"
+                style="?attr/playbackControlsTimeStyle" />
+
+            <TextView
+                android:id="@+id/separate_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/lb_playback_time_separator"
+                android:layout_gravity="top"
+                android:layout_toStartOf="@+id/total_time"
+                android:layout_marginStart="@dimen/lb_playback_transport_time_margin"
+                android:layout_marginTop="@dimen/lb_playback_transport_time_margin_top"
+                style="?attr/playbackControlsTimeStyle" />
+
+            <TextView
+                android:id="@+id/total_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top"
+                android:layout_alignParentEnd="true"
+                android:layout_marginStart="@dimen/lb_playback_transport_time_margin"
+                android:layout_marginTop="@dimen/lb_playback_transport_time_margin_top"
+                style="?attr/playbackControlsTimeStyle" />
+        </RelativeLayout>
+
+
+    </android.support.v17.leanback.widget.PlaybackTransportRowView>
+</LinearLayout>
diff --git a/v17/leanback/res/layout/lb_row_media_item.xml b/v17/leanback/res/layout/lb_row_media_item.xml
index b25e922..e76e281 100644
--- a/v17/leanback/res/layout/lb_row_media_item.xml
+++ b/v17/leanback/res/layout/lb_row_media_item.xml
@@ -62,6 +62,7 @@
 
             <LinearLayout
                     android:id="@+id/mediaItemActionsContainer"
+                    android:orientation="horizontal"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:paddingStart="16dip" />
diff --git a/v17/leanback/res/layout/lb_search_bar.xml b/v17/leanback/res/layout/lb_search_bar.xml
index 37cdfb3..73a5985 100644
--- a/v17/leanback/res/layout/lb_search_bar.xml
+++ b/v17/leanback/res/layout/lb_search_bar.xml
@@ -54,7 +54,7 @@
                     android:id="@+id/lb_search_text_editor"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical|end"
+                    android:layout_gravity="center_vertical|start"
                     android:layout_marginStart="@dimen/lb_search_bar_edit_text_margin_start"
                     android:layout_centerVertical="true"
                     android:cursorVisible="true"
diff --git a/v17/leanback/res/layout/lb_video_surface.xml b/v17/leanback/res/layout/lb_video_surface.xml
index 9c6c8fd..61ac944 100644
--- a/v17/leanback/res/layout/lb_video_surface.xml
+++ b/v17/leanback/res/layout/lb_video_surface.xml
@@ -17,5 +17,6 @@
 <android.support.v17.leanback.widget.VideoSurfaceView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/video_surface"
+    android:layout_gravity="center"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />
diff --git a/v17/leanback/res/transition-v21/lb_title_out.xml b/v17/leanback/res/transition-v21/lb_title_out.xml
index 69735ab..2402857 100644
--- a/v17/leanback/res/transition-v21/lb_title_out.xml
+++ b/v17/leanback/res/transition-v21/lb_title_out.xml
@@ -18,7 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lb="http://schemas.android.com/apk/res-auto"
     class="android.support.v17.leanback.transition.SlideKitkat"
-    android:interpolator="@animator/lb_decelerator_2"
+    android:interpolator="@anim/lb_decelerator_2"
     lb:lb_slideEdge="top" >
     <targets>
         <target android:targetId="@id/browse_title_group" />
diff --git a/v17/leanback/res/values-af/strings.xml b/v17/leanback/res/values-af/strings.xml
index 2f2ca18..5e5c649 100644
--- a/v17/leanback/res/values-af/strings.xml
+++ b/v17/leanback/res/values-af/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktiveer onderskrifte"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiveer onderskrifte"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Voer prent in prentmodus in"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Mediakontroles word gewys"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Mediakontroles word versteek; druk D-paneel om te wys"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Voltooi"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Gaan voort"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-foutkode %1$d ekstra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BEGIN HIER"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Volgende"</string>
 </resources>
diff --git a/v17/leanback/res/values-am/strings.xml b/v17/leanback/res/values-am/strings.xml
index c4b461a..2427364 100644
--- a/v17/leanback/res/values-am/strings.xml
+++ b/v17/leanback/res/values-am/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ዝግ የምስል ስር ጽሑፍ አጻጻፍን አንቃ"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ዝግ የምስል ስር ጽሑፍ አጻጻፍን አሰናክል"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ስዕሉን በስዕል ሁነታ ውስጥ ያክሉ"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"የሚዲያ መቆጣጠሪያዎች እንዲታዩ ተደርገዋል"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"የሚዲያ መቆጣጠሪያዎች ተደብቀዋል። d-pad ን ለማሳየት ይጫኑ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ጨርስ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ቀጥል"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">"፦"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"የMediaPlayer ስህተት ኮድ %1$d ተጨማሪ %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ይጀምሩ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ቀጣይ"</string>
 </resources>
diff --git a/v17/leanback/res/values-ar/strings.xml b/v17/leanback/res/values-ar/strings.xml
index cda830c..90cd489 100644
--- a/v17/leanback/res/values-ar/strings.xml
+++ b/v17/leanback/res/values-ar/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"تمكين الترجمة المصاحبة"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"تعطيل الترجمة المصاحبة"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"إدخال صورة في وضع الصورة"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"تم إظهار عناصر التحكم في الوسائط"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"‏تم إخفاء عناصر التحكم في الوسائط، يمكنك الضغط على d-pad لإظهارها"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"إنهاء"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"متابعة"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"‏رمز الخطأ في MediaPlayer %1$d بالإضافة إلى %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"البدء"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"التالية"</string>
 </resources>
diff --git a/v17/leanback/res/values-az-rAZ/strings.xml b/v17/leanback/res/values-az-rAZ/strings.xml
deleted file mode 100644
index f9f0277..0000000
--- a/v17/leanback/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Naviqasiya menyusu"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Axtarış Fəaliyyəti"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Axtarış"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Axtarış üçün danışın"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Axtarış: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Axtarış üçün danışın: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Oyun"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauza"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"İrəli Ötürmə"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"İrəli sarı %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Geri ötürmə"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Geri sarı %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Növbətini atlayın"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Öncəkini atlayın"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Digər fəaliyyətlər"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Bəyənməkdən imtina edin"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Bəyənin"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Bəyənməməkdən imtina edin"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Bəyənməyin"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Təkrarlanmasın"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Hamısını təkrarlayın"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Biri təkrarlansın"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Qarışdırma aktiv edilsin"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Qarışdırma deaktiv edilsin"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Yüksək keyfiyyəti aktiv edin"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksək keyfiyyəti deaktiv edin"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Qapalı çəkilişi aktiv edin"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Qapalı çəkilişi deaktiv edin"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Şəkil içində Şəkil Rejiminə daxil olun"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Bitir"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davam edin"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BAŞLAYIN"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Növbəti"</string>
-</resources>
diff --git a/v17/leanback/res/values-az/strings.xml b/v17/leanback/res/values-az/strings.xml
new file mode 100644
index 0000000..e3fe52b
--- /dev/null
+++ b/v17/leanback/res/values-az/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Naviqasiya menyusu"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Axtarış Fəaliyyəti"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Axtarış"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Axtarış üçün danışın"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Axtarış: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Axtarış üçün danışın: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Oyun"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauza"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"İrəli Ötürmə"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"İrəli sarı %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Geri ötürmə"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Geri sarı %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Növbətini atlayın"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Öncəkini atlayın"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Digər fəaliyyətlər"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Bəyənməkdən imtina edin"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Bəyənin"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Bəyənməməkdən imtina edin"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Bəyənməyin"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Təkrarlanmasın"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Hamısını təkrarlayın"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Biri təkrarlansın"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Qarışdırma aktiv edilsin"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Qarışdırma deaktiv edilsin"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Yüksək keyfiyyəti aktiv edin"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksək keyfiyyəti deaktiv edin"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Qapalı çəkilişi aktiv edin"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Qapalı çəkilişi deaktiv edin"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Şəkil içində Şəkil Rejiminə daxil olun"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Media idarəetmələri açıqdır"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Media idarəetmələri gizlidir, göstərmək üçün d-pad\'i basın"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Bitir"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davam edin"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Media Pleyer xəta kodu %1$d əlavə %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BAŞLAYIN"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Növbəti"</string>
+</resources>
diff --git a/v17/leanback/res/values-b+sr+Latn/strings.xml b/v17/leanback/res/values-b+sr+Latn/strings.xml
index 5c466fb..4659835 100644
--- a/v17/leanback/res/values-b+sr+Latn/strings.xml
+++ b/v17/leanback/res/values-b+sr+Latn/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogući titlove"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogući titlove"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Uđi u režim Slika u slici"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Kontrole za medije su prikazane"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Kontrole za medije su skrivene, pritisnite kontrole za kretanje da biste ih prikazali"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dovrši"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastavi"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kôd greške MediaPlayer-a %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAPOČNITE"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Dalje"</string>
 </resources>
diff --git a/v17/leanback/res/values-be-rBY/strings.xml b/v17/leanback/res/values-be-rBY/strings.xml
deleted file mode 100644
index b9e72bb..0000000
--- a/v17/leanback/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Меню навігацыі"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Аперацыя пошуку"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Шукаць"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Пачніце гаварыць, каб пачаць пошук"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Шукаць у <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Пачніце гаварыць, каб пачаць пошук у <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Прайграць"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Прыпыніць"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Перамотка ўперад"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Перамотка ўперад %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Перамотка назад"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Перамотка назад %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Перайсці да наступнага элемента"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Перайсці да папярэдняга элемента"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Дадатковыя дзеянні"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Зняць адзнаку «Падабаецца»"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Паставіць адзнаку «Падабаецца»"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Зняць адзнаку «Не падабаецца»"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Паставіць адзнаку «Не падабаецца»"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Не паўтараць нічога"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Паўтарыць усё"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Паўтарыць адзін элемент"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Уключыць выпадковы парадак"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Адключыць выпадковы парадак"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Уключыць высокую якасць"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Адключыць высокую якасць"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Уключыць схаваныя цітры"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Адключыць схаваныя цітры"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Калі ласка, увядзіце выяву ў рэжыме Выяў"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Завяршыць"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Далей"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ПАЧАЦЬ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Далей"</string>
-</resources>
diff --git a/v17/leanback/res/values-be/strings.xml b/v17/leanback/res/values-be/strings.xml
new file mode 100644
index 0000000..97e05a6
--- /dev/null
+++ b/v17/leanback/res/values-be/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Меню навігацыі"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Аперацыя пошуку"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Шукаць"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Пачніце гаварыць, каб пачаць пошук"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Шукаць у <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Пачніце гаварыць, каб пачаць пошук у <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Прайграць"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Прыпыніць"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Перамотка ўперад"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Перамотка ўперад %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Перамотка назад"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Перамотка назад %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Перайсці да наступнага элемента"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Перайсці да папярэдняга элемента"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Дадатковыя дзеянні"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Зняць адзнаку «Падабаецца»"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Паставіць адзнаку «Падабаецца»"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Зняць адзнаку «Не падабаецца»"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Паставіць адзнаку «Не падабаецца»"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Не паўтараць нічога"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Паўтарыць усё"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Паўтарыць адзін элемент"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Уключыць выпадковы парадак"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Адключыць выпадковы парадак"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Уключыць высокую якасць"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Адключыць высокую якасць"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Уключыць схаваныя цітры"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Адключыць схаваныя цітры"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Калі ласка, увядзіце выяву ў рэжыме Выяў"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Элементы кіравання мультымедыя паказаны"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Элементы кіравання мультымедыя схаваны. Каб паказаць іх, націсніце пераключальнік напрамкаў"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Завяршыць"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Далей"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Код памылкі MediaPlayer %1$d дадаткова %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ПАЧАЦЬ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Далей"</string>
+</resources>
diff --git a/v17/leanback/res/values-bg/strings.xml b/v17/leanback/res/values-bg/strings.xml
index 642abc5..4099672 100644
--- a/v17/leanback/res/values-bg/strings.xml
+++ b/v17/leanback/res/values-bg/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Активиране на субтитрите"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Деактивиране на субтитрите"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Вход в режима „Картина в картина“"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Контролите за мултимедия са показани"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Контролите за мултимедия са скрити. Натиснете контролния пад, за да се покажат"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Край"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Напред"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Код на грешката на MediaPlayer %1$d (допълнително: %2$d)"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ПЪРВИ СТЪПКИ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Напред"</string>
 </resources>
diff --git a/v17/leanback/res/values-bn-rBD/strings.xml b/v17/leanback/res/values-bn-rBD/strings.xml
deleted file mode 100644
index be98f58..0000000
--- a/v17/leanback/res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"নেভিগেশান মেনু"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"অনুসন্ধান অ্যাকশন"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"অনুসন্ধান"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"অনুসন্ধান করতে বলুন"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> অনুসন্ধান করুন"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> অনুসন্ধান করতে বলুন"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"চালান"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"বিরাম দিন"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"দ্রুত ফরওয়ার্ড"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"দ্রুত ফরওয়ার্ড %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"পেছনের দিকে যান"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"পেছনের দিকে যান %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"সরাসরি পরেরটিতে চলে যান"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"সরাসরি আগেরটিতে চলে যান"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"আরো অ্যাকশন"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"উপরের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন মুক্ত করুন"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"উপরের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচিত করুন"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন মুক্ত করুন"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন করুন"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"একটিরও পুনরাবৃত্তি করবেন না"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"সবগুলির পুনরাবৃত্তি করুন"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"একটির পুনরাবৃত্তি করুন"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"শাফল করা সক্ষম করুন"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"শাফল করা অক্ষম করুন"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"উচ্চ গুণমান সক্ষম করুন"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"উচ্চ গুণমান অক্ষম করুন"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"সাবটাইটেল সক্ষম করুন"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"সাবটাইটেল অক্ষম করুন"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ছবি মোডে ছবি লগান"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"শেষ করুন"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"চালিয়ে যান"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"শুরু করা যাক"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"পরবর্তী"</string>
-</resources>
diff --git a/v17/leanback/res/values-bn/strings.xml b/v17/leanback/res/values-bn/strings.xml
new file mode 100644
index 0000000..949faac
--- /dev/null
+++ b/v17/leanback/res/values-bn/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"নেভিগেশান মেনু"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"অনুসন্ধান অ্যাকশন"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"অনুসন্ধান"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"অনুসন্ধান করতে বলুন"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> অনুসন্ধান করুন"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> অনুসন্ধান করতে বলুন"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"চালান"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"বিরাম দিন"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"দ্রুত ফরওয়ার্ড"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"দ্রুত ফরওয়ার্ড %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"পেছনের দিকে যান"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"পেছনের দিকে যান %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"সরাসরি পরেরটিতে চলে যান"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"সরাসরি আগেরটিতে চলে যান"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"আরো অ্যাকশন"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"উপরের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন মুক্ত করুন"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"উপরের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচিত করুন"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন মুক্ত করুন"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন করুন"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"একটিরও পুনরাবৃত্তি করবেন না"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"সবগুলির পুনরাবৃত্তি করুন"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"একটির পুনরাবৃত্তি করুন"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"শাফল করা সক্ষম করুন"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"শাফল করা অক্ষম করুন"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"উচ্চ গুণমান সক্ষম করুন"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"উচ্চ গুণমান অক্ষম করুন"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"সাবটাইটেল সক্ষম করুন"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"সাবটাইটেল অক্ষম করুন"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ছবি মোডে ছবি লগান"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"মিডিয়ার নিয়ন্ত্রণগুলি দেখানো হয়েছে"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"মিডিয়ার নিয়ন্ত্রণগুলি লুকানো আছে, দেখার জন্য ডি-প্যাড টিপুন"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"শেষ করুন"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"চালিয়ে যান"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer ত্রুটি কোড %1$d অতিরিক্ত %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"শুরু করা যাক"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"পরবর্তী"</string>
+</resources>
diff --git a/v17/leanback/res/values-bs-rBA/strings.xml b/v17/leanback/res/values-bs-rBA/strings.xml
deleted file mode 100644
index cdfe434..0000000
--- a/v17/leanback/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Meni za navigaciju"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Pretraživanje"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Pretraga"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Kažite nešto da pokrenete pretragu"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Pretraži <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Kažite nešto da pokrenete pretragu <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Reproduciraj"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauziraj"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Ubrzaj"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Ubrzaj %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Premotaj"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Premotaj %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Preskoči sljedeće"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Preskoči prethodno"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Više radnji"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Poništi pozitivnu ocjenu"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Odaberi pozitivnu ocjenu"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Poništi negativnu ocjenu"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Odaberi negativnu ocjenu"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ne ponavljaj"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Ponovi sve"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Ponovi jedno"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Uključi izmiješani redoslijed"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Isključi izmiješani redoslijed"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Uključi visoki kvalitet"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Isključi visoki kvalitet"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Uključi titlove"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Isključi titlove"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Uđi u način rada Slika u slici"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Završiti"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastaviti"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAPOČNITE"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Sljedeća"</string>
-</resources>
diff --git a/v17/leanback/res/values-bs/strings.xml b/v17/leanback/res/values-bs/strings.xml
new file mode 100644
index 0000000..12f404e
--- /dev/null
+++ b/v17/leanback/res/values-bs/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Meni za navigaciju"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Pretraživanje"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Pretraga"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Kažite nešto da pokrenete pretragu"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Pretraži <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Kažite nešto da pokrenete pretragu <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Reproduciraj"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauziraj"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Ubrzaj"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Ubrzaj %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Premotaj"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Premotaj %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Preskoči sljedeće"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Preskoči prethodno"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Više radnji"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Poništi pozitivnu ocjenu"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Odaberi pozitivnu ocjenu"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Poništi negativnu ocjenu"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Odaberi negativnu ocjenu"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ne ponavljaj"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Ponovi sve"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Ponovi jedno"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Uključi izmiješani redoslijed"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Isključi izmiješani redoslijed"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Uključi visoki kvalitet"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Isključi visoki kvalitet"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Uključi titlove"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Isključi titlove"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Uđi u način rada Slika u slici"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Kontrole za medije su prikazane"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Kontrole za medije su skrivene. Pritisnite d-pad da ih prikažete"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Završiti"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastaviti"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kôd greške MediaPlayera %1$d dodatno %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAPOČNITE"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Naprijed"</string>
+</resources>
diff --git a/v17/leanback/res/values-ca/strings.xml b/v17/leanback/res/values-ca/strings.xml
index 5567e23..943a6c3 100644
--- a/v17/leanback/res/values-ca/strings.xml
+++ b/v17/leanback/res/values-ca/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activa els subtítols tancats"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactiva els subtítols tancats"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entra al mode d\'imatge en imatge"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Es mostren els controls multimèdia"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"S\'han amagat els controls multimèdia; prem el teclat direccional per mostrar-los"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalitza"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continua"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Codi d\'error de MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"COMENÇA"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Següent"</string>
 </resources>
diff --git a/v17/leanback/res/values-cs/strings.xml b/v17/leanback/res/values-cs/strings.xml
index a5fbfb5..6519e9c 100644
--- a/v17/leanback/res/values-cs/strings.xml
+++ b/v17/leanback/res/values-cs/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Zapnout titulky"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Vypnout titulky"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Přejít do režimu Obraz v obraze"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Ovládací prvky médií jsou zobrazeny"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Ovládací prvky médií jsou skryty, zobrazíte je stisknutím křížového ovladače"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončit"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Pokračovat"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kód chyby přehrávače MediaPlayer %1$d, další %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAČÍNÁME"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Další"</string>
 </resources>
diff --git a/v17/leanback/res/values-da/strings.xml b/v17/leanback/res/values-da/strings.xml
index 76062f5..97c4a32 100644
--- a/v17/leanback/res/values-da/strings.xml
+++ b/v17/leanback/res/values-da/strings.xml
@@ -47,11 +47,15 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiver høj kvalitet"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivér undertekster"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver undertekster"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tilføj billedet i billedtilstand"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tilstand med integreret billede"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Knapperne til afspilning er synlige"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Knapperne til afspilning er skjult. Tryk på D-pad\'en for at se dem"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Afslut"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsæt"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-fejlkode %1$d ekstra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"KOM GODT I GANG"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Næste"</string>
 </resources>
diff --git a/v17/leanback/res/values-de/strings.xml b/v17/leanback/res/values-de/strings.xml
index 53ad13a..7cdd292 100644
--- a/v17/leanback/res/values-de/strings.xml
+++ b/v17/leanback/res/values-de/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Untertitel aktivieren"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Untertitel deaktivieren"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Bild-in-Bild-Modus aktivieren"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Mediensteuerelemente eingeblendet"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Mediensteuerelemente ausgeblendet. Drücke das Steuerkreuz, um die Steuerelemente wieder einzublenden."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fertigstellen"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Weiter"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-Fehlercode %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"JETZT STARTEN"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Weiter"</string>
 </resources>
diff --git a/v17/leanback/res/values-el/strings.xml b/v17/leanback/res/values-el/strings.xml
index 6dc20ef..c0f039f 100644
--- a/v17/leanback/res/values-el/strings.xml
+++ b/v17/leanback/res/values-el/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ενεργοποίηση υποτίτλων"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Απενεργοποίηση υποτίτλων"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Εισαγωγή εικόνας στη Λειτουργία εικόνας"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Εμφάνιση στοιχείων ελέγχου μέσων"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Απόκρυψη στοιχείων ελέγχου μέσων, πιέστε το d-pad για εμφάνιση"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Τέλος"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Συνέχεια"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Κωδικός σφάλματος MediaPlayer %1$d επιπλέον %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ΕΝΑΡΞΗ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Επόμενο"</string>
 </resources>
diff --git a/v17/leanback/res/values-en-rAU/strings.xml b/v17/leanback/res/values-en-rAU/strings.xml
index 8032f7d..c15bd3b 100644
--- a/v17/leanback/res/values-en-rAU/strings.xml
+++ b/v17/leanback/res/values-en-rAU/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Enter Picture In Picture Mode"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Media controls shown"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Media controls hidden, press d-pad to show"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer error code %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"GET STARTED"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Next"</string>
 </resources>
diff --git a/v17/leanback/res/values-en-rGB/strings.xml b/v17/leanback/res/values-en-rGB/strings.xml
index 8032f7d..c15bd3b 100644
--- a/v17/leanback/res/values-en-rGB/strings.xml
+++ b/v17/leanback/res/values-en-rGB/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Enter Picture In Picture Mode"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Media controls shown"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Media controls hidden, press d-pad to show"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer error code %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"GET STARTED"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Next"</string>
 </resources>
diff --git a/v17/leanback/res/values-en-rIN/strings.xml b/v17/leanback/res/values-en-rIN/strings.xml
index 8032f7d..c15bd3b 100644
--- a/v17/leanback/res/values-en-rIN/strings.xml
+++ b/v17/leanback/res/values-en-rIN/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Enter Picture In Picture Mode"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Media controls shown"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Media controls hidden, press d-pad to show"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer error code %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"GET STARTED"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Next"</string>
 </resources>
diff --git a/v17/leanback/res/values-es-rUS/strings.xml b/v17/leanback/res/values-es-rUS/strings.xml
index 8a81040..41fe3f4 100644
--- a/v17/leanback/res/values-es-rUS/strings.xml
+++ b/v17/leanback/res/values-es-rUS/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Habilitar subtítulos"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inhabilitar subtítulos"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar el modo Imagen en imagen"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Se muestran los controles de medios"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Los controles de medios están ocultos; presiona el control direccional para mostrarlos"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Código de error de MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"COMENZAR"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Siguiente"</string>
 </resources>
diff --git a/v17/leanback/res/values-es/strings.xml b/v17/leanback/res/values-es/strings.xml
index 59f707b..afd9195 100644
--- a/v17/leanback/res/values-es/strings.xml
+++ b/v17/leanback/res/values-es/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Habilitar subtítulos"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inhabilitar subtítulos"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar modo Imagen en imagen"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Controles multimedia mostrados"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Controles multimedia ocultos (pulsa la cruceta para mostrarlos)"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Código de error de MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"EMPEZAR"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Siguiente"</string>
 </resources>
diff --git a/v17/leanback/res/values-et-rEE/strings.xml b/v17/leanback/res/values-et-rEE/strings.xml
deleted file mode 100644
index 6d6bac8..0000000
--- a/v17/leanback/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Navigeerimismenüü"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Otsimistoiming"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Otsing"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Öelge otsimiseks"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Otsige teenusest <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Häälotsing: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Esita"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Peata"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Keri edasi"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Edasikerimine %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Keri tagasi"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Tagasikerimine %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Liigu järgmise üksuse juurde"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Liigu eelmise üksuse juurde"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Veel toiminguid"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Tühista hinnang Meeldib"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Vali hinnang Meeldib"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Tühista hinnang Ei meeldi"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Vali hinnang Ei meeldi"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ära korda midagi"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Korda kõike"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Korda ühte"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Luba juhuslikus järjekorras esitamine"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Keela juhuslikus järjekorras esitamine"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Luba kõrgkvaliteetne taasesitus"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Keela kõrgkvaliteetne taasesitus"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Luba subtiitrid"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Keela subtiitrid"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Sisene režiimi Pilt pildis"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Lõpeta"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jätka"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ALUSTAGE"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Järgmine"</string>
-</resources>
diff --git a/v17/leanback/res/values-et/strings.xml b/v17/leanback/res/values-et/strings.xml
new file mode 100644
index 0000000..d87aba3
--- /dev/null
+++ b/v17/leanback/res/values-et/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Navigeerimismenüü"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Otsimistoiming"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Otsing"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Öelge otsimiseks"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Otsige teenusest <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Häälotsing: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Esita"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Peata"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Keri edasi"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Edasikerimine %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Keri tagasi"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Tagasikerimine %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Liigu järgmise üksuse juurde"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Liigu eelmise üksuse juurde"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Veel toiminguid"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Tühista hinnang Meeldib"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Vali hinnang Meeldib"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Tühista hinnang Ei meeldi"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Vali hinnang Ei meeldi"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ära korda midagi"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Korda kõike"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Korda ühte"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Luba juhuslikus järjekorras esitamine"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Keela juhuslikus järjekorras esitamine"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Luba kõrgkvaliteetne taasesitus"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Keela kõrgkvaliteetne taasesitus"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Luba subtiitrid"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Keela subtiitrid"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Sisene režiimi Pilt pildis"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Meedia juhtnupud on kuvatud"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Meedia juhtnupud on peidetud, kuvamiseks vajutage DPAD-i"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Lõpeta"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jätka"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayeri veakood %1$d, lisa %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ALUSTAGE"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Järgmine"</string>
+</resources>
diff --git a/v17/leanback/res/values-eu-rES/strings.xml b/v17/leanback/res/values-eu-rES/strings.xml
deleted file mode 100644
index 98787d4..0000000
--- a/v17/leanback/res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Nabigazio-menua"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Bilaketa"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Bilatu"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Esan bilatu nahi duzuna"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Bilatu <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Esan bilatu nahi duzuna, bilaketa hemen egiteko: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Erreproduzitu"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pausatu"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Aurreratu"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Aurreratu %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Atzeratu"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Atzeratu %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Saltatu hurrengora"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Saltatu aurrekora"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Ekintza gehiago"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Desautatu \"erpurua gora\""</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Hautatu \"erpurua gora\""</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Desautatu \"erpurua behera\""</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Hautatu \"erpurua behera\""</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ez errepikatu"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Errepikatu guztiak"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Errepikatu bat"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Gaitu ausazko erreprodukzioa"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Desgaitu ausazko erreprodukzioa"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Gaitu kalitate handiko erreprodukzioa"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desgaitu kalitate handiko erreprodukzioa"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Gaitu azpitituluak"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desgaitu azpitituluak"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Aktibatu \"Argazkia argazkian\" modua"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Amaitu"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jarraitu"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"LEHEN URRATSAK"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Hurrengoa"</string>
-</resources>
diff --git a/v17/leanback/res/values-eu/strings.xml b/v17/leanback/res/values-eu/strings.xml
new file mode 100644
index 0000000..46140b2
--- /dev/null
+++ b/v17/leanback/res/values-eu/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Nabigazio-menua"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Bilaketa"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Bilatu"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Esan bilatu nahi duzuna"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Bilatu <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Esan bilatu nahi duzuna, bilaketa hemen egiteko: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Erreproduzitu"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pausatu"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Aurreratu"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Aurreratu %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Atzeratu"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Atzeratu %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Saltatu hurrengora"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Saltatu aurrekora"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Ekintza gehiago"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Desautatu \"erpurua gora\""</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Hautatu \"erpurua gora\""</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Desautatu \"erpurua behera\""</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Hautatu \"erpurua behera\""</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ez errepikatu"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Errepikatu guztiak"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Errepikatu bat"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Gaitu ausazko erreprodukzioa"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Desgaitu ausazko erreprodukzioa"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Gaitu kalitate handiko erreprodukzioa"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desgaitu kalitate handiko erreprodukzioa"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Gaitu azpitituluak"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desgaitu azpitituluak"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Aktibatu \"Argazkia argazkian\" modua"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Multimedia kontrolatzeko aukerak ikusgai"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Ezkutatuta daude multimedia kontrolatzeko aukerak. Erakusteko, sakatu nabigazio-gurutzea."</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Amaitu"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jarraitu"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer errore-kodea: %1$d (%2$d gehigarria)"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"LEHEN URRATSAK"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Hurrengoa"</string>
+</resources>
diff --git a/v17/leanback/res/values-fa/strings.xml b/v17/leanback/res/values-fa/strings.xml
index 3774bd2..daa6bab 100644
--- a/v17/leanback/res/values-fa/strings.xml
+++ b/v17/leanback/res/values-fa/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"فعال کردن زیرنویس"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"غیرفعال کردن زیرنویس"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"وارد حالت تصویر در تصویر شوید"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"کنترل‌های رسانه نشان داده می‌شوند"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"‏کنترل‌های رسانه پنهان هستند، برای نمایش آن‌ها d-pad (پد کنترل) را فشار دهید"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"پایان"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ادامه"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"‏کد خطای MediaPlayer‏ %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"شروع به‌ کار"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"بعدی"</string>
 </resources>
diff --git a/v17/leanback/res/values-fi/strings.xml b/v17/leanback/res/values-fi/strings.xml
index 3444642..bc83b9d 100644
--- a/v17/leanback/res/values-fi/strings.xml
+++ b/v17/leanback/res/values-fi/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ota tekstitys käyttöön"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Poista tekstitys käytöstä"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vaihda kuva kuvassa ‑tilaan"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Mediasäätimet näkyvissä"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Mediasäätimet piilotettu, näytä painamalla ohjaimen nuolia."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Valmis"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jatka"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayerin virhekoodi %1$d ylimääräinen %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ALOITA"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Seuraava"</string>
 </resources>
diff --git a/v17/leanback/res/values-fr-rCA/strings.xml b/v17/leanback/res/values-fr-rCA/strings.xml
index 4c8cc19..300c087 100644
--- a/v17/leanback/res/values-fr-rCA/strings.xml
+++ b/v17/leanback/res/values-fr-rCA/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activer le sous-titrage"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Désactiver le sous-titrage"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activer le mode Incrustation d\'image"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Les commandes multimédias sont affichées"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Les commandes multimédias sont masquées, appuyez sur le pavé directionnel pour les afficher."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Terminer"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuer"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Code d\'erreur MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"COMMENCER"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Suivant"</string>
 </resources>
diff --git a/v17/leanback/res/values-fr/strings.xml b/v17/leanback/res/values-fr/strings.xml
index 9086fa1..3c83164 100644
--- a/v17/leanback/res/values-fr/strings.xml
+++ b/v17/leanback/res/values-fr/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activer les sous-titres"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Désactiver les sous-titres"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activer le mode PIP"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Les commandes multimédias sont affichées"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Les commandes multimédias sont masquées. Appuyez sur le pavé directionnel pour les afficher"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Terminer"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuer"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Code d\'erreur MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"COMMENCER"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Suivant"</string>
 </resources>
diff --git a/v17/leanback/res/values-gl-rES/strings.xml b/v17/leanback/res/values-gl-rES/strings.xml
deleted file mode 100644
index 2d2579b..0000000
--- a/v17/leanback/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Menú de navegación"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Acción de busca"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Buscar"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Fala para efectuar a busca"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Busca <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Fala para buscar <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Reproducir"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pausar"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Avance rápido"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Avance rápido %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Rebobinar"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Rebobinado %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Saltar seguinte"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Saltar anterior"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Máis accións"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Anular \"Gústame\""</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Seleccionar polgar cara arriba"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Anular \"Non me gusta\""</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Seleccionar polgar cara abaixo"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Non repetir"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Repetir todo"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Repetir un"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Activar reprodución aleatoria"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Desactivar reprodución aleatoria"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Activar alta calidade"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desactivar alta calidade"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activar subtítulos"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactivar subtítulos"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar o modo Imaxe superposta"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"INTRODUCIÓN"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Seguinte"</string>
-</resources>
diff --git a/v17/leanback/res/values-gl/strings.xml b/v17/leanback/res/values-gl/strings.xml
new file mode 100644
index 0000000..21830de
--- /dev/null
+++ b/v17/leanback/res/values-gl/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Menú de navegación"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Acción de busca"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Buscar"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Fala para efectuar a busca"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Busca <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Fala para buscar <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Reproducir"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pausar"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Avance rápido"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Avance rápido %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Rebobinar"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Rebobinado %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Saltar seguinte"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Saltar anterior"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Máis accións"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Anular \"Gústame\""</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Seleccionar polgar cara arriba"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Anular \"Non me gusta\""</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Seleccionar polgar cara abaixo"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Non repetir"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Repetir todo"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Repetir un"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Activar reprodución aleatoria"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Desactivar reprodución aleatoria"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Activar alta calidade"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desactivar alta calidade"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activar subtítulos"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactivar subtítulos"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar o modo Imaxe superposta"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Móstranse os controis de recursos multimedia"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Os controis de recursos multimedia están ocultos. Preme d-pad para mostralos"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Código de erro de MediaPlayer %1$d %2$d de máis"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"INTRODUCIÓN"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Seguinte"</string>
+</resources>
diff --git a/v17/leanback/res/values-gu-rIN/strings.xml b/v17/leanback/res/values-gu-rIN/strings.xml
deleted file mode 100644
index 569da29..0000000
--- a/v17/leanback/res/values-gu-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"નૅવિગેશન મેનૂ"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"શોધ ક્રિયા"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"શોધો"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"શોધવા માટે બોલો"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> શોધો"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ને શોધવા માટે બોલો"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ચલાવો"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"થોભો"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ઝડપી ફોરવર્ડ"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ફાસ્ટ ફોરવર્ડ કરો %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"રીવાઇન્ડ કરો"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ને રિવાઇન્ડ કરો"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"આગલા પર જાઓ"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"પહેલાનાને છોડો"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"વધુ ક્રિયાઓ"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"એકદમ સરસ નાપસંદ કરો"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"એકદમ સરસ પસંદ કરો"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"સારું નથી નાપસંદ કરો"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"સારું નથી પસંદ કરો"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"કોઈનું પુનરાવર્તન નહીં"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"બધાનું પુનરાવર્તન કરો"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"એક પુનરાવર્તિત કરો"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"શફલ કરોને સક્ષમ કરો"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"શફલ કરોને અક્ષમ કરો"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ઉચ્ચ ગુણવત્તા સક્ષમ કરો"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ઉચ્ચ ગુણવત્તા અક્ષમ કરો"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ઉપશીર્ષક સક્ષમ કરો"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"વિગતવાર ઉપશીર્ષકોને અક્ષમ કરો"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ચિત્ર મોડમાં ચિત્ર દાખલ કરો"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"સમાપ્ત કરો"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ચાલુ રાખો"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"પ્રારંભ કરો"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"આગલું"</string>
-</resources>
diff --git a/v17/leanback/res/values-gu/strings.xml b/v17/leanback/res/values-gu/strings.xml
new file mode 100644
index 0000000..12796b3
--- /dev/null
+++ b/v17/leanback/res/values-gu/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"નૅવિગેશન મેનૂ"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"શોધ ક્રિયા"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"શોધો"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"શોધવા માટે બોલો"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> શોધો"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ને શોધવા માટે બોલો"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ચલાવો"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"થોભો"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ઝડપી ફોરવર્ડ"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ફાસ્ટ ફોરવર્ડ કરો %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"રીવાઇન્ડ કરો"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ને રિવાઇન્ડ કરો"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"આગલા પર જાઓ"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"પહેલાનાને છોડો"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"વધુ ક્રિયાઓ"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"એકદમ સરસ નાપસંદ કરો"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"એકદમ સરસ પસંદ કરો"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"સારું નથી નાપસંદ કરો"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"સારું નથી પસંદ કરો"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"કોઈનું પુનરાવર્તન નહીં"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"બધાનું પુનરાવર્તન કરો"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"એક પુનરાવર્તિત કરો"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"શફલ કરોને સક્ષમ કરો"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"શફલ કરોને અક્ષમ કરો"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ઉચ્ચ ગુણવત્તા સક્ષમ કરો"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ઉચ્ચ ગુણવત્તા અક્ષમ કરો"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ઉપશીર્ષક સક્ષમ કરો"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"વિગતવાર ઉપશીર્ષકોને અક્ષમ કરો"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ચિત્ર મોડમાં ચિત્ર દાખલ કરો"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"મીડિયા નિયંત્રણો બતાવેલા છે"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"મીડિયા નિયંત્રણો છુપાયેલા છે, તે બતાવવા માટે d-પૅડ દબાવો"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"સમાપ્ત કરો"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ચાલુ રાખો"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer ભૂલ કોડ %1$d extra %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"પ્રારંભ કરો"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"આગલું"</string>
+</resources>
diff --git a/v17/leanback/res/values-hi/strings.xml b/v17/leanback/res/values-hi/strings.xml
index 1b73165..91ff72a 100644
--- a/v17/leanback/res/values-hi/strings.xml
+++ b/v17/leanback/res/values-hi/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षक सक्षम करें"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षक अक्षम करें"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोड में चित्र डालें"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"मीडिया कंट्रोल दिखाए गए हैं"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"मीडिया नियंत्रण छिपे हुए हैं, दिखाने के लिए डी-पैड दबाएं"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त करें"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी रखें"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer गड़बड़ी कोड %1$d अतिरिक्त %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"प्रारंभ करें"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"अगला"</string>
 </resources>
diff --git a/v17/leanback/res/values-hr/strings.xml b/v17/leanback/res/values-hr/strings.xml
index 73c8dc6..6ff3ed8 100644
--- a/v17/leanback/res/values-hr/strings.xml
+++ b/v17/leanback/res/values-hr/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogući titlove"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogući titlove"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Unos slike u načinu slike"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Medijske kontrole prikazane"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Medijske kontrole skrivene su, pritisnite D-pad za prikaz"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Završi"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastavi"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kôd pogreške MediaPlayera: %1$d, dodatno %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"POČETAK"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Dalje"</string>
 </resources>
diff --git a/v17/leanback/res/values-hu/strings.xml b/v17/leanback/res/values-hu/strings.xml
index f30c4f6..c0f89c3 100644
--- a/v17/leanback/res/values-hu/strings.xml
+++ b/v17/leanback/res/values-hu/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Feliratok engedélyezése"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Feliratok letiltása"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Kép a képben mód indítása"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Médiavezérlők megjelenítve"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"A médiavezérlők el vannak rejtve. Megjelenítésükhöz nyomja le a d-padet."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Befejezés"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Folytatás"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-hibakód: %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"KEZDŐ LÉPÉSEK"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Következő"</string>
 </resources>
diff --git a/v17/leanback/res/values-hy-rAM/strings.xml b/v17/leanback/res/values-hy-rAM/strings.xml
deleted file mode 100644
index edc1f47..0000000
--- a/v17/leanback/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Նավարկման ընտրացանկ"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Որոնման հրամանը"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Որոնում"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Խոսեք՝ որոնելու համար"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Որոնեք <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Խոսեք՝ <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> որոնելու համար"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Նվագարկել"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Դադարեցնել"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Արագ առաջ անցնել"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Առագ առաջանցում %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Հետ փաթաթել"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Հետանցում %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Անցնել հաջորդին"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Անցնել նախորդին"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Այլ գործողություններ"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Ապանշել Հավանելու կոճակը"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Նշել Հավանելու կոճակը"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Ապանշել Չհավանելու կոճակը"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Նշել Չհավանելու կոճակը"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Չկրկնել"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Կրկնել բոլորը"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Կրկնել մեկը"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Միացնել խառը նվագարկումը"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Անջատել խառը նվագարկումը"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Միացնել բարձր որակը"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Անջատել բարձր որակը"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Միացնել խորագրերը"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Անջատել խորագրերը"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Մուտք «Նկար նկարի մեջ» ռեժիմ"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Վերջ"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Շարունակել"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ՍԿՍԵԼ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Հաջորդը"</string>
-</resources>
diff --git a/v17/leanback/res/values-hy/strings.xml b/v17/leanback/res/values-hy/strings.xml
new file mode 100644
index 0000000..dfaed6e
--- /dev/null
+++ b/v17/leanback/res/values-hy/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Նավարկման ընտրացանկ"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Որոնման հրամանը"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Որոնում"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Խոսեք՝ որոնելու համար"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Որոնեք <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Խոսեք՝ <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> որոնելու համար"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Նվագարկել"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Դադարեցնել"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Արագ առաջ անցնել"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Առագ առաջանցում %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Հետ փաթաթել"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Հետանցում %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Անցնել հաջորդին"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Անցնել նախորդին"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Այլ գործողություններ"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Ապանշել Հավանելու կոճակը"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Նշել Հավանելու կոճակը"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Ապանշել Չհավանելու կոճակը"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Նշել Չհավանելու կոճակը"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Չկրկնել"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Կրկնել բոլորը"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Կրկնել մեկը"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Միացնել խառը նվագարկումը"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Անջատել խառը նվագարկումը"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Միացնել բարձր որակը"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Անջատել բարձր որակը"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Միացնել խորագրերը"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Անջատել խորագրերը"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Մուտք «Նկար նկարի մեջ» ռեժիմ"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Մեդիայի կառավարման տարրերը ցուցադրվում են"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Մեդիայի կառավարման տարրերը թաքցված են։ Ցուցադրելու համար սեղմեք D-pad-ը"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Վերջ"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Շարունակել"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Մեդիա նվագարկչի սխալի կոդ %1$d լրացուցիչ %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ՍԿՍԵԼ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Հաջորդը"</string>
+</resources>
diff --git a/v17/leanback/res/values-in/strings.xml b/v17/leanback/res/values-in/strings.xml
index 05e9dc9..89ca9d0 100644
--- a/v17/leanback/res/values-in/strings.xml
+++ b/v17/leanback/res/values-in/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktifkan Pembuatan Teks"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Nonaktifkan Pembuatan Teks"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Masukkan Foto Dalam Mode Foto"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Kontrol media ditampilkan"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Kontrol media disembunyikan, tekan d-pad untuk menampilkannya"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Lanjutkan"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kode error MediaPlayer %1$d ekstra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"MULAI"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Berikutnya"</string>
 </resources>
diff --git a/v17/leanback/res/values-is-rIS/strings.xml b/v17/leanback/res/values-is-rIS/strings.xml
deleted file mode 100644
index 84b6430..0000000
--- a/v17/leanback/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Yfirlitsvalmynd"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Leitaraðgerð"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Leita"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Talaðu til að leita"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Leita í <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Talaðu til að leita í <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Spila"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Hlé"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Spóla áfram"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Spóla áfram %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Spóla til baka"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Spóla til baka %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Fara í næsta"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Fara í fyrra"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Fleiri aðgerðir"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Hætta við þumal upp"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Gefa þumal upp"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Hætta við þumal niður"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Gefa þumal niður"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Endurtaka ekkert"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Endurtaka allt"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Endurtaka eitt"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Kveikja á stokkun"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Slökkva á stokkun"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Kveikja á miklum gæðum"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Slökkva á miklum gæðum"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Kveikja á skjátextum"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Slökkva á skjátextum"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Skoða mynd í myndsniði"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Ljúka"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Halda áfram"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"HEFJAST HANDA"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Áfram"</string>
-</resources>
diff --git a/v17/leanback/res/values-is/strings.xml b/v17/leanback/res/values-is/strings.xml
new file mode 100644
index 0000000..3d49bda
--- /dev/null
+++ b/v17/leanback/res/values-is/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Yfirlitsvalmynd"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Leitaraðgerð"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Leita"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Talaðu til að leita"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Leita í <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Talaðu til að leita í <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Spila"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Hlé"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Spóla áfram"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Spóla áfram %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Spóla til baka"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Spóla til baka %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Fara í næsta"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Fara í fyrra"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Fleiri aðgerðir"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Hætta við þumal upp"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Gefa þumal upp"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Hætta við þumal niður"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Gefa þumal niður"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Endurtaka ekkert"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Endurtaka allt"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Endurtaka eitt"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Kveikja á stokkun"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Slökkva á stokkun"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Kveikja á miklum gæðum"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Slökkva á miklum gæðum"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Kveikja á skjátextum"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Slökkva á skjátextum"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Skoða mynd í myndsniði"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Spilunarstýringar sýndar"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Spilunarstýringar faldar, ýttu á stefnuhnappa til að sýna þær"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Ljúka"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Halda áfram"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Villukóði MediaPlayer %1$d aukalegt %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"HEFJAST HANDA"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Áfram"</string>
+</resources>
diff --git a/v17/leanback/res/values-it/strings.xml b/v17/leanback/res/values-it/strings.xml
index b0814d0..3eab975 100644
--- a/v17/leanback/res/values-it/strings.xml
+++ b/v17/leanback/res/values-it/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Attiva sottotitoli"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disattiva sottotitoli"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Attiva modalità Picture-in-picture"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Controlli multimediali visualizzati"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Controlli multimediali nascosti, premi il d-pad per visualizzarli"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fine"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continua"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Codice di errore MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"INIZIA"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Avanti"</string>
 </resources>
diff --git a/v17/leanback/res/values-iw/strings.xml b/v17/leanback/res/values-iw/strings.xml
index b94c431..f4761a1 100644
--- a/v17/leanback/res/values-iw/strings.xml
+++ b/v17/leanback/res/values-iw/strings.xml
@@ -31,8 +31,8 @@
     <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"‏העברה קדימה של %1$dX"</string>
     <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"הרץ אחורה"</string>
     <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"‏העברה לאחור של %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"דלג אל הפריט הבא"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"דלג אל הפריט הקודם"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ברצוני לדלג אל הפריט הבא"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ברצוני לדלג אל הפריט הקודם"</string>
     <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"עוד פעולות"</string>
     <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"בטל בחירה באגודל כלפי מעלה"</string>
     <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"בחר באגודל כלפי מעלה"</string>
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"הפעל כתוביות"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"השבת כתוביות"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"הזן את התמונה במצב תמונה"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"פקדי המדיה מוצגים"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"‏פקדי המדיה מוסתרים. הקש על ה-d-pad כדי להציג אותם"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"סיום"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"המשך"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"‏קוד שגיאה %1$d‏ של MediaPlayer ועוד %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"התחל"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"הבא"</string>
 </resources>
diff --git a/v17/leanback/res/values-ja/strings.xml b/v17/leanback/res/values-ja/strings.xml
index 650f4fd..8d5a41d 100644
--- a/v17/leanback/res/values-ja/strings.xml
+++ b/v17/leanback/res/values-ja/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"字幕を有効にする"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"字幕を無効にする"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"PIP モードに移動"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"メディア コントロールは表示されています"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"メディア コントロールは非表示になっています。表示するには D-pad を押してください"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完了"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"続行"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer エラーコード: %1$d、追加: %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"使ってみる"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"次へ"</string>
 </resources>
diff --git a/v17/leanback/res/values-ka-rGE/strings.xml b/v17/leanback/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 9962645..0000000
--- a/v17/leanback/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ნავიგაციის მენიუ"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"ძიების მოქმედება"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ძიება"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"თქვით საძიებო ფრაზა"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>-ის ძიება"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"თქვით <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>-ის საძიებლად"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"დაკვრა"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"პაუზა"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"წინ გადახვევა"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for lb_playback_controls_fast_forward_multiplier (1058753672110224526) -->
-    <skip />
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"უკან გადახვევა"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for lb_playback_controls_rewind_multiplier (1640629531440849942) -->
-    <skip />
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"შემდეგის გამოტოვება"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"წინას გამოტოვება"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"დამატებითი ქმედებები"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"მაღალი შეფასების არჩევის გაუქმება"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"მაღალი შეფასების არჩევა"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"დაბალი შეფასების არჩევის გაუქმება"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"დაბალი შეფასების არჩევა"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"არცერთის გამეორება"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ყველას გამეორება"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ერთის გამეორება"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"არეულად დაკვრის ჩართვა"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"არეულად დაკვრის გამორთვა"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"მაღალი ხარისხის ჩართვა"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"მაღალი ხარისხის გამორთვა"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"დახურული წარწერების ჩართვა"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"დახურული წარწერების გაუქმება"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"რეჟიმზე „სურათი სურათში“ გადასვლა"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"დასრულება"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"გაგრძელება"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"დაწყება"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"შემდეგი"</string>
-</resources>
diff --git a/v17/leanback/res/values-ka/strings.xml b/v17/leanback/res/values-ka/strings.xml
new file mode 100644
index 0000000..19d919c
--- /dev/null
+++ b/v17/leanback/res/values-ka/strings.xml
@@ -0,0 +1,65 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ნავიგაციის მენიუ"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"ძიების მოქმედება"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ძიება"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"თქვით საძიებო ფრაზა"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>-ის ძიება"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"თქვით <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>-ის საძიებლად"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"დაკვრა"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"პაუზა"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"წინ გადახვევა"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for lb_playback_controls_fast_forward_multiplier (1058753672110224526) -->
+    <skip />
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"უკან გადახვევა"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for lb_playback_controls_rewind_multiplier (1640629531440849942) -->
+    <skip />
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"შემდეგის გამოტოვება"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"წინას გამოტოვება"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"დამატებითი ქმედებები"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"მაღალი შეფასების არჩევის გაუქმება"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"მაღალი შეფასების არჩევა"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"დაბალი შეფასების არჩევის გაუქმება"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"დაბალი შეფასების არჩევა"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"არცერთის გამეორება"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ყველას გამეორება"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ერთის გამეორება"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"არეულად დაკვრის ჩართვა"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"არეულად დაკვრის გამორთვა"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"მაღალი ხარისხის ჩართვა"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"მაღალი ხარისხის გამორთვა"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"დახურული წარწერების ჩართვა"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"დახურული წარწერების გაუქმება"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"რეჟიმზე „სურათი სურათში“ გადასვლა"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"ნაჩვენებია მედიის მართვის საშუალებები"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"მედიის მართვის საშუალებები დამალულია, გამოსაჩენად დააჭირეთ D-pad-ს"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"დასრულება"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"გაგრძელება"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-ის შეცდომის კოდი: %1$d extra %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"დაწყება"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"შემდეგი"</string>
+</resources>
diff --git a/v17/leanback/res/values-kk-rKZ/strings.xml b/v17/leanback/res/values-kk-rKZ/strings.xml
deleted file mode 100644
index 6f7faf2..0000000
--- a/v17/leanback/res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Навигация мәзірі"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Іздеу әрекеті"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Іздеу"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Іздеу үшін сөйлеу"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> іздеу"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> іздеу үшін сөйлеңіз"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Ойнату"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Кідірту"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Алға айналдыру"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX алға айналдыру"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Кері айналдыру"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX кері айналдыру"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Келесіге өту"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Алдыңғыға өту"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Қосымша әрекеттер"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Саусақты жоғары қаратудан таңдауды алу"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Саусақты жоғары қаратуды таңдау"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Саусақты төмен қаратудан таңдауды алу"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Саусақты төмен қаратуды таңдау"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ешқайсысын қайталамау"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Барлығын қайталау"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Біреуін қайталау"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Кездейсоқ ойнатуды қосу"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Кездейсоқ ойнатуды өшіру"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Жоғары сапаны қосу"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жоғары сапаны өшіру"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жасырын титрлерді қосу"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жасырын титрлерді өшіру"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Сурет ішіндегі сурет режиміне кіру"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Аяқтау"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Жалғастыру"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ІСКЕ КІРІСУ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Келесі"</string>
-</resources>
diff --git a/v17/leanback/res/values-kk/strings.xml b/v17/leanback/res/values-kk/strings.xml
new file mode 100644
index 0000000..36902f5
--- /dev/null
+++ b/v17/leanback/res/values-kk/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Навигация мәзірі"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Іздеу әрекеті"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Іздеу"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Іздеу үшін сөйлеу"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> іздеу"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> іздеу үшін сөйлеңіз"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Ойнату"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Кідірту"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Алға айналдыру"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX алға айналдыру"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Кері айналдыру"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX кері айналдыру"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Келесіге өту"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Алдыңғыға өту"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Қосымша әрекеттер"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Саусақты жоғары қаратудан таңдауды алу"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Саусақты жоғары қаратуды таңдау"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Саусақты төмен қаратудан таңдауды алу"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Саусақты төмен қаратуды таңдау"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ешқайсысын қайталамау"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Барлығын қайталау"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Біреуін қайталау"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Кездейсоқ ойнатуды қосу"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Кездейсоқ ойнатуды өшіру"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Жоғары сапаны қосу"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жоғары сапаны өшіру"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жасырын титрлерді қосу"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жасырын титрлерді өшіру"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Сурет ішіндегі сурет режиміне кіру"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Мультимедияны басқару элементтері көрсетілген"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Мультимедияны басқару элементтері жасырын, оларды көрсету үшін d-тақтасын басыңыз"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Аяқтау"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Жалғастыру"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer қате коды %1$d, қосымша %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ІСКЕ КІРІСУ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Келесі"</string>
+</resources>
diff --git a/v17/leanback/res/values-km-rKH/strings.xml b/v17/leanback/res/values-km-rKH/strings.xml
deleted file mode 100644
index 25abb45..0000000
--- a/v17/leanback/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ម៉ឺនុយរុករក"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"​ស្វែងរក​សកម្មភាព"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ស្វែងរក"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"និយាយ​​ដើម្បី​ស្វែងរក"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"ស្វែងរក <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"និយាយ​ដើម្បី​ស្វែងរក <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ចាក់"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ផ្អាក"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"បញ្ជូន​បន្ត​រហ័ស"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ខាទៅមុខ %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ខា​ថយក្រោយ"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"ខាថយក្រោយ %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"រំលង​បន្ទាប់"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"រំលង​មុន"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"សកម្មភាព​ច្រើន​ទៀត"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"មិន​ជ្រើស​មេ​​ដៃ​ឡើង​វិញ"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ជ្រើស​មេ​ដៃ​ឡើង​លើ"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"មិន​ជ្រើស​​មេដៃ​ចុះ​ក្រោម"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ជ្រើស​​មេ​ដៃ​ចុះក្រោម"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"មិន​ធ្វើ​ឡើង​វិញ​"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ធ្វើ​ម្ដង​ទៀត​ទាំងអស់"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ធ្វើ​​ឡើងវិញ​ម្ដង"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"បើក​ការ​​ច្របល់"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"បិទ​ការ​ច្របល់"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"បើក​គុណភាព​ខ្ពស់"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"បិទ​គុណភាព​ខ្ពស់"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"បើក​ការ​ដាក់​ចំណង​ដែល​បាន​បិទ"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"បិទ​ការ​ដាក់​ចំណង​ដែល​បាន​បិទ"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"បញ្ចូលរូបភាពនៅក្នុងរបៀបរូបភាព"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"បញ្ចប់"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"បន្ត"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">"៖"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ចាប់ផ្ដើម"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"បន្ទាប់"</string>
-</resources>
diff --git a/v17/leanback/res/values-km/strings.xml b/v17/leanback/res/values-km/strings.xml
new file mode 100644
index 0000000..8439d31
--- /dev/null
+++ b/v17/leanback/res/values-km/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ម៉ឺនុយរុករក"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"​ស្វែងរក​សកម្មភាព"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ស្វែងរក"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"និយាយ​​ដើម្បី​ស្វែងរក"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"ស្វែងរក <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"និយាយ​ដើម្បី​ស្វែងរក <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ចាក់"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ផ្អាក"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"បញ្ជូន​បន្ត​រហ័ស"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ខាទៅមុខ %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ខា​ថយក្រោយ"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"ខាថយក្រោយ %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"រំលង​បន្ទាប់"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"រំលង​មុន"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"សកម្មភាព​ច្រើន​ទៀត"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"មិន​ជ្រើស​មេ​​ដៃ​ឡើង​វិញ"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ជ្រើស​មេ​ដៃ​ឡើង​លើ"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"មិន​ជ្រើស​​មេដៃ​ចុះ​ក្រោម"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ជ្រើស​​មេ​ដៃ​ចុះក្រោម"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"មិន​ធ្វើ​ឡើង​វិញ​"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ធ្វើ​ម្ដង​ទៀត​ទាំងអស់"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ធ្វើ​​ឡើងវិញ​ម្ដង"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"បើក​ការ​​ច្របល់"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"បិទ​ការ​ច្របល់"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"បើក​គុណភាព​ខ្ពស់"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"បិទ​គុណភាព​ខ្ពស់"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"បើក​ការ​ដាក់​ចំណង​ដែល​បាន​បិទ"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"បិទ​ការ​ដាក់​ចំណង​ដែល​បាន​បិទ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"បញ្ចូលរូបភាពនៅក្នុងរបៀបរូបភាព"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"ការ​គ្រប់គ្រង​មេឌៀ​ត្រូវ​បាន​បង្ហាញ"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"ការ​គ្រប់គ្រង​មេឌៀ​ត្រូវ​បាន​លាក់ សូមចុច d-pad ដើម្បី​បង្ហាញ"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"បញ្ចប់"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"បន្ត"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"៖"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"លេខកូដបញ្ហា MediaPlayer %1$d និង %2$d បន្ថែម"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ចាប់ផ្ដើម"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"បន្ទាប់"</string>
+</resources>
diff --git a/v17/leanback/res/values-kn-rIN/strings.xml b/v17/leanback/res/values-kn-rIN/strings.xml
deleted file mode 100644
index 92fdddd..0000000
--- a/v17/leanback/res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ನ್ಯಾವಿಗೇಶನ್‌ ಮೆನು"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"ಹುಡುಕಾಟ ಕ್ರಿಯೆ"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ಹುಡುಕಿ"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ಹುಡುಕಲು ಮಾತನಾಡಿ"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ಹುಡುಕಿ"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ಮಾತನಾಡಿ ಹುಡುಕಾಟ ನಡೆಸಿ"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ಪ್ಲೇ ಮಾಡು"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ವಿರಾಮಗೊಳಿಸು"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ಫಾಸ್ಟ್ ಫಾರ್ವರ್ಡ್"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ಫಾಸ್ಟ್ ಫಾರ್ವರ್ಡ್ %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ರೀವೈಂಡ್"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"ರಿವೈಂಡ್ %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ಮುಂದೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ಹಿಂದೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ಹೆಚ್ಚು ಕ್ರಿಯೆಗಳು"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"ಥಂಬ್ ಅಪ್ ಆಯ್ಕೆರದ್ದುಮಾಡಿ"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ಥಂಬ್ ಅಪ್ ಆಯ್ಕೆಮಾಡಿ"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"ಥಂಬ್ ಡೌನ್ ಆಯ್ಕೆರದ್ದುಮಾಡಿ"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ಥಂಬ್ ಡೌನ್ ಆಯ್ಕೆಮಾಡಿ"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ಯಾವುದನ್ನೂ ಪುನರಾವರ್ತಿಸಬೇಡಿ"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ಒಂದನ್ನು ಪುನರಾವರ್ತಿಸಿ"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ಜೋಡಿಸುವುದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ಜೋಡಿಸುವುದನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ಹೆಚ್ಚು ಗುಣಮಟ್ಟವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ಹೆಚ್ಚು ಗುಣಮಟ್ಟವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ಚಿತ್ರವನ್ನು ಚಿತ್ರ ಮೋಡ್‌ನಲ್ಲಿ ಪ್ರವೇಶಿಸಿ"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ಪೂರ್ಣಗೊಳಿಸು"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ಮುಂದುವರಿಸು"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ಪ್ರಾರಂಭಿಸಿ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ಮುಂದೆ"</string>
-</resources>
diff --git a/v17/leanback/res/values-kn/strings.xml b/v17/leanback/res/values-kn/strings.xml
new file mode 100644
index 0000000..dd14e38
--- /dev/null
+++ b/v17/leanback/res/values-kn/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ನ್ಯಾವಿಗೇಶನ್‌ ಮೆನು"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"ಹುಡುಕಾಟ ಕ್ರಿಯೆ"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ಹುಡುಕಿ"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ಹುಡುಕಲು ಮಾತನಾಡಿ"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ಹುಡುಕಿ"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ಮಾತನಾಡಿ ಹುಡುಕಾಟ ನಡೆಸಿ"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ಪ್ಲೇ ಮಾಡು"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ವಿರಾಮಗೊಳಿಸು"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ಫಾಸ್ಟ್ ಫಾರ್ವರ್ಡ್"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ಫಾಸ್ಟ್ ಫಾರ್ವರ್ಡ್ %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ರೀವೈಂಡ್"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"ರಿವೈಂಡ್ %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ಮುಂದೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ಹಿಂದೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ಹೆಚ್ಚು ಕ್ರಿಯೆಗಳು"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"ಥಂಬ್ ಅಪ್ ಆಯ್ಕೆರದ್ದುಮಾಡಿ"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ಥಂಬ್ ಅಪ್ ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"ಥಂಬ್ ಡೌನ್ ಆಯ್ಕೆರದ್ದುಮಾಡಿ"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ಥಂಬ್ ಡೌನ್ ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ಯಾವುದನ್ನೂ ಪುನರಾವರ್ತಿಸಬೇಡಿ"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ಒಂದನ್ನು ಪುನರಾವರ್ತಿಸಿ"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ಜೋಡಿಸುವುದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ಜೋಡಿಸುವುದನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ಹೆಚ್ಚು ಗುಣಮಟ್ಟವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ಹೆಚ್ಚು ಗುಣಮಟ್ಟವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ಚಿತ್ರವನ್ನು ಚಿತ್ರ ಮೋಡ್‌ನಲ್ಲಿ ಪ್ರವೇಶಿಸಿ"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"ಮಾಧ್ಯಮ ನಿಯಂತ್ರಣಗಳನ್ನು ತೋರಿಸಲಾಗಿದೆ"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"ಮಾಧ್ಯಮ ನಿಯಂತ್ರಣಗಳನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ, ತೋರಿಸಲು d-pad ಒತ್ತಿರಿ"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ಪೂರ್ಣಗೊಳಿಸು"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ಮುಂದುವರಿಸು"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"ಮೀಡಿಯಾ ಪ್ಲೇಯರ್ ದೋಷ ಕೋಡ್ %1$d ಹೆಚ್ಚುವರಿ %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ಮುಂದೆ"</string>
+</resources>
diff --git a/v17/leanback/res/values-ko/strings.xml b/v17/leanback/res/values-ko/strings.xml
index 1696bca..8443a25 100644
--- a/v17/leanback/res/values-ko/strings.xml
+++ b/v17/leanback/res/values-ko/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"자막 사용 설정"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"자막 사용 중지"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"사진 모드에서 사진 입력"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"미디어 컨트롤이 표시되었습니다."</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"미디어 컨트롤이 숨겨져 있습니다. 표시하려면 D-Pad를 누르세요."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"완료"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"계속"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer 오류 코드 %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"시작하기"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"다음"</string>
 </resources>
diff --git a/v17/leanback/res/values-ky-rKG/strings.xml b/v17/leanback/res/values-ky-rKG/strings.xml
deleted file mode 100644
index e2dc200..0000000
--- a/v17/leanback/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Чабыттоо менюсу"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Издөө аракети"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Издөө"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Издөө үчүн сүйлөңүз"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> издөө"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> издөө үчүн сүйлөңүз"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Ойнотуу"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Тындыруу"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Алдыга түрүү"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Алдыга түрүү %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Артка түрүү"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Артка түрүү %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Кийинкини өткөрүп жиберүү"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Мурункуну өткөрүп жиберүү"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Дагы көнүгүүлөр"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Жактырууну тандоодон чыгаруу"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Жактырууну тандоо"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Жактырбоону тандоодон чыгаруу"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Жактырбоону тандоо"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Эч бирин кайталабоо"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Баарын кайталоо"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Бирөөнү кайталоо"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Аралаштырууну иштетүү"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Аралаштырууну өчүрүү"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Жогорку сапатты иштетүү"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жогорку сапатты өчүрүү"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жабык субтитрлерди иштетүү"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жабык субтитрлерди өчүрүү"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Сүрөт режиминде сүрөт киргизүү"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Бүтүрүү"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Улантуу"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"БАШТАДЫК"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Кийинки"</string>
-</resources>
diff --git a/v17/leanback/res/values-ky/strings.xml b/v17/leanback/res/values-ky/strings.xml
new file mode 100644
index 0000000..80c7af2
--- /dev/null
+++ b/v17/leanback/res/values-ky/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Чабыттоо менюсу"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Издөө аракети"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Издөө"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Издөө үчүн сүйлөңүз"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> издөө"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> издөө үчүн сүйлөңүз"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Ойнотуу"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Тындыруу"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Алдыга түрүү"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Алдыга түрүү %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Артка түрүү"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Артка түрүү %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Кийинкини өткөрүп жиберүү"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Мурункуну өткөрүп жиберүү"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Дагы көнүгүүлөр"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Жактырууну тандоодон чыгаруу"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Жактырууну тандоо"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Жактырбоону тандоодон чыгаруу"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Жактырбоону тандоо"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Эч бирин кайталабоо"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Баарын кайталоо"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Бирөөнү кайталоо"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Аралаштырууну иштетүү"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Аралаштырууну өчүрүү"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Жогорку сапатты иштетүү"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жогорку сапатты өчүрүү"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жабык субтитрлерди иштетүү"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жабык субтитрлерди өчүрүү"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Сүрөт режиминде сүрөт киргизүү"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Медиа файлды башкаруу көрсөтүлдү"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Медиа файлды башкаруу жашырылган, көрүү үчүн d-pad көзөмөлдөө каражатын басыңыз"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Бүтүрүү"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Улантуу"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer\'деги катанын коду: 1$d, кошумча: %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"БАШТАДЫК"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Кийинки"</string>
+</resources>
diff --git a/v17/leanback/res/values-lo-rLA/strings.xml b/v17/leanback/res/values-lo-rLA/strings.xml
deleted file mode 100644
index 389b1d0..0000000
--- a/v17/leanback/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ເມນູນຳທາງ"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"ຊອກ​ຫາ​ຄຳ​ສັ່ງ"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ຊອກຫາ"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ເວົ້າ​ເພື່ອ​ຊອກ​ຫາ"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"ຊອກ​ຫາ <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"ເວົ້າ​ເພື່ອ​ຊອກ​ຫາ <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ຫຼິ້ນ"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ຢຸດຊົ່ວຄາວ"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ເລື່ອນ​ໄປ​ໜ້າ"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ໄປ​ໜ້າແບບໄວ %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"​ຣີ​ວາຍກັບ"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"​ກັບ​ຄືນ %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"​ຂ້າມ​ໄປ​ຕໍ່"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"​ຂ້າມ​ໄປ​ກ່ອນ​ໜ້າ"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ຄຳສັ່ງ​ເພີ່ມເຕີມ"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"​ຢຸດ​ເລືອກ​ຍົກ​ໂປ້​ແລ້ວ"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"​ເລືອກ​ຍົກ​ໂປ້​ແລ້ວ"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"​ຢຸດ​ຊີ້​ໂປ້​ລົງ​ແລ້ວ"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ເລືອກ​ຊີ້​ໂປ້​ລົງ​ແລ້ວ"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"​ບໍ່ຫຼິ້ນ​ຊ້ຳ"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ຫຼິ້ນ​ຊ້ຳ​ທັງ​ໝົດ"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ຫຼິ້ນ​ຊ້ຳ"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"​ເປີດ​ນຳ​ໃຊ້​ການ​ສະຫຼັບ"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"​ປິດ​ນຳ​ໃຊ້​ການ​ສະຫຼັບ"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"​ເປີດນຳ​ໃຊ້​ການຫຼິ້ນ​ດ້ວຍຄຸນ​ນະ​ພາບ​ສູງ"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"​ປິດ​ນຳ​ໃຊ້​ການຫຼິ້ນ​ດ້ວຍຄຸນ​ນະ​ພາບ​ສູງ"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"​ເປີດ​ນຳ​ໃຊ້​​ຄຳ​ບັນ​ຍາຍ​ແບບ​ປິດ"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"​ປິດ​ນຳ​ໃຊ້​ຄຳ​ບັນ​ຍາຍ​ແບບ​ປິດ"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ປ້ອນຮູບພາບໃນໂໝດຮູບພາບ"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ສໍາເລັດ"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"​ສືບ​ຕໍ່"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ເລີ່ມຕົ້ນນຳໃຊ້"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ຕໍ່ໄປ"</string>
-</resources>
diff --git a/v17/leanback/res/values-lo/strings.xml b/v17/leanback/res/values-lo/strings.xml
new file mode 100644
index 0000000..7fb28b8
--- /dev/null
+++ b/v17/leanback/res/values-lo/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ເມນູນຳທາງ"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"ຊອກ​ຫາ​ຄຳ​ສັ່ງ"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ຊອກຫາ"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ເວົ້າ​ເພື່ອ​ຊອກ​ຫາ"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"ຊອກ​ຫາ <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"ເວົ້າ​ເພື່ອ​ຊອກ​ຫາ <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ຫຼິ້ນ"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ຢຸດຊົ່ວຄາວ"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ເລື່ອນ​ໄປ​ໜ້າ"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ໄປ​ໜ້າແບບໄວ %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"​ຣີ​ວາຍກັບ"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"​ກັບ​ຄືນ %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"​ຂ້າມ​ໄປ​ຕໍ່"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"​ຂ້າມ​ໄປ​ກ່ອນ​ໜ້າ"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ຄຳສັ່ງ​ເພີ່ມເຕີມ"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"​ຢຸດ​ເລືອກ​ຍົກ​ໂປ້​ແລ້ວ"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"​ເລືອກ​ຍົກ​ໂປ້​ແລ້ວ"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"​ຢຸດ​ຊີ້​ໂປ້​ລົງ​ແລ້ວ"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ເລືອກ​ຊີ້​ໂປ້​ລົງ​ແລ້ວ"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"​ບໍ່ຫຼິ້ນ​ຊ້ຳ"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ຫຼິ້ນ​ຊ້ຳ​ທັງ​ໝົດ"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ຫຼິ້ນ​ຊ້ຳ"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"​ເປີດ​ນຳ​ໃຊ້​ການ​ສະຫຼັບ"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"​ປິດ​ນຳ​ໃຊ້​ການ​ສະຫຼັບ"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"​ເປີດນຳ​ໃຊ້​ການຫຼິ້ນ​ດ້ວຍຄຸນ​ນະ​ພາບ​ສູງ"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"​ປິດ​ນຳ​ໃຊ້​ການຫຼິ້ນ​ດ້ວຍຄຸນ​ນະ​ພາບ​ສູງ"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"​ເປີດ​ນຳ​ໃຊ້​​ຄຳ​ບັນ​ຍາຍ​ແບບ​ປິດ"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"​ປິດ​ນຳ​ໃຊ້​ຄຳ​ບັນ​ຍາຍ​ແບບ​ປິດ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ປ້ອນຮູບພາບໃນໂໝດຮູບພາບ"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"ສະແດງຕົວຄວບຄຸມມີເດຍແລ້ວ"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"ເຊື່ອງຕົວຄວບຄຸມມີເດຍແລ້ວ, ກົດປຸ່ມທິດທາງເພື່ອສະແດງ"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ສໍາເລັດ"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"​ສືບ​ຕໍ່"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"ລະຫັດ MediaPlayer ຜິດພາດ %1$d ພິເສດ %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ເລີ່ມຕົ້ນນຳໃຊ້"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ຕໍ່ໄປ"</string>
+</resources>
diff --git a/v17/leanback/res/values-lt/strings.xml b/v17/leanback/res/values-lt/strings.xml
index 43eebb8..b9d6076 100644
--- a/v17/leanback/res/values-lt/strings.xml
+++ b/v17/leanback/res/values-lt/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Įgalinti subtitrus"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Išjungti subtitrus"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Įjungti vaizdo vaizde režimą"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Medijos valdikliai rodomi"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Medijos valdikliai paslėpti. Paspauskite krypčių valdiklius, kad rodytumėte"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Baigti"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Tęsti"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"%1$d ir %2$d „MediaPlayer“ klaidos kodas"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"PRADĖTI"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Kitas"</string>
 </resources>
diff --git a/v17/leanback/res/values-lv/strings.xml b/v17/leanback/res/values-lv/strings.xml
index 644d990..8f69284 100644
--- a/v17/leanback/res/values-lv/strings.xml
+++ b/v17/leanback/res/values-lv/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Iespējot slēgtos parakstus"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Atspējot slēgtos parakstus"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Aktivizēt režīmu Attēls attēlā"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Multivides vadīklas ir redzamas."</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Multivides vadīklas ir paslēptas. Nospiediet virzienu tastatūru, lai tās tiktu parādītas."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Pabeigt"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Turpināt"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer kļūdas kods: %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"SĀKT DARBU"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Nākamā"</string>
 </resources>
diff --git a/v17/leanback/res/values-mk-rMK/strings.xml b/v17/leanback/res/values-mk-rMK/strings.xml
deleted file mode 100644
index 6e506fe..0000000
--- a/v17/leanback/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Мени за навигација"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Акција на пребарување"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Пребарување"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Зборувајте за да пребарувате"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Пребарувај <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Кажете за да се пребарува <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Пушти"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Пауза"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Брзо премотај напред"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Премотај напред %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Премотај назад"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Премотај назад %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Прескокни на следна"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Прескокни на претходна"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Повеќе дејства"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Откажи палец нагоре"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Избери палец нагоре"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Откажи палец надолу"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Избери палец надолу"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Не повторувај ниту една"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Повтори ги сите"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Повтори една"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Овозможи мешање"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Оневозможи мешање"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Овозможи висок квалитет"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Оневозможи висок квалитет"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Овозможи затворено објаснување"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Оневозможи затворено објаснување"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Влези во режимот „Слика во слика“"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Заврши"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продолжи"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ЗАПОЧНИ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Следно"</string>
-</resources>
diff --git a/v17/leanback/res/values-mk/strings.xml b/v17/leanback/res/values-mk/strings.xml
new file mode 100644
index 0000000..d694167
--- /dev/null
+++ b/v17/leanback/res/values-mk/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Мени за навигација"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Акција на пребарување"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Пребарување"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Зборувајте за да пребарувате"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Пребарувај <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Кажете за да се пребарува <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Пушти"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Пауза"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Брзо премотај напред"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Премотај напред %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Премотај назад"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Премотај назад %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Прескокни на следна"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Прескокни на претходна"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Повеќе дејства"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Откажи палец нагоре"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Избери палец нагоре"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Откажи палец надолу"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Избери палец надолу"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Не повторувај ниту една"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Повтори ги сите"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Повтори една"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Овозможи мешање"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Оневозможи мешање"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Овозможи висок квалитет"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Оневозможи висок квалитет"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Овозможи затворено објаснување"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Оневозможи затворено објаснување"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Влези во режимот „Слика во слика“"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Контролите за аудио-визуелните контроли се прикажуваат"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Контролите за аудио-визуелните медиуми се скриени, притиснете на подлогата за насока за да ги прикажете"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Заврши"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продолжи"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Код за грешка на MediaPlayer %1$d дополнително %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ЗАПОЧНИ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Следно"</string>
+</resources>
diff --git a/v17/leanback/res/values-ml-rIN/strings.xml b/v17/leanback/res/values-ml-rIN/strings.xml
deleted file mode 100644
index 0306a6a..0000000
--- a/v17/leanback/res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"നാവിഗേഷൻ മെനു"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"തിരയൽ പ്രവർത്തനം"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"തിരയുക"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ശബ്‌ദം ഉപയോഗിച്ച് തിരയുക"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> തിരയുക"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> തിരയുന്നതിന് സംസാരിക്കുക"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"പ്ലേ ചെയ്യുക"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"താൽക്കാലികമായി നിർത്തുക"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ഫാസ്റ്റ് ഫോർവേഡ് ചെയ്യുക"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX വേഗത്തിൽ ഫോർവേഡുചെയ്യുക"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"റിവൈൻഡുചെയ്യുക"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX റിവൈൻഡുചെയ്യുക"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"അടുത്തതിലേക്ക് പോകുക"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"കൂടുതൽ പ്രവർത്തനങ്ങൾ"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"തമ്പ് അപ്പ് തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"തമ്പ് അപ്പ് തിരഞ്ഞെടുക്കുക"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"തമ്പ് ഡൗൺ തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"തമ്പ് ഡൗൺ തിരഞ്ഞെടുക്കുക"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ഒന്നും ആവർത്തിക്കരുത്"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"എല്ലാം ആവർത്തിക്കുക"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ഒന്ന് ആവർത്തിക്കുക"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ഷഫിൾ ചെയ്യുന്നത് പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ഷഫിൾ ചെയ്യുന്നത് പ്രവർത്തനരഹിതമാക്കുക"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ഉയർന്ന നിലവാരം പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ഉയർന്ന നിലവാരം പ്രവർത്തനരഹിതമാക്കുക"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനരഹിതമാക്കുക"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"\'ചിത്രത്തിനുള്ളിൽ ചിത്രം\' മോഡിലേക്ക് പ്രവേശിക്കുക"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"പൂര്‍ത്തിയാക്കുക"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"തുടരുക"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ആരംഭിക്കുക"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"അടുത്തത്"</string>
-</resources>
diff --git a/v17/leanback/res/values-ml/strings.xml b/v17/leanback/res/values-ml/strings.xml
new file mode 100644
index 0000000..41ac9d5
--- /dev/null
+++ b/v17/leanback/res/values-ml/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"നാവിഗേഷൻ മെനു"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"തിരയൽ പ്രവർത്തനം"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"തിരയുക"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ശബ്‌ദം ഉപയോഗിച്ച് തിരയുക"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> തിരയുക"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> തിരയുന്നതിന് സംസാരിക്കുക"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"പ്ലേ ചെയ്യുക"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"താൽക്കാലികമായി നിർത്തുക"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ഫാസ്റ്റ് ഫോർവേഡ് ചെയ്യുക"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX വേഗത്തിൽ ഫോർവേഡുചെയ്യുക"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"റിവൈൻഡുചെയ്യുക"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX റിവൈൻഡുചെയ്യുക"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"അടുത്തതിലേക്ക് പോകുക"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"കൂടുതൽ പ്രവർത്തനങ്ങൾ"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"തമ്പ് അപ്പ് തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"തമ്പ് അപ്പ് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"തമ്പ് ഡൗൺ തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"തമ്പ് ഡൗൺ തിരഞ്ഞെടുക്കുക"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ഒന്നും ആവർത്തിക്കരുത്"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"എല്ലാം ആവർത്തിക്കുക"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ഒന്ന് ആവർത്തിക്കുക"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ഷഫിൾ ചെയ്യുന്നത് പ്രവർത്തനക്ഷമമാക്കുക"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ഷഫിൾ ചെയ്യുന്നത് പ്രവർത്തനരഹിതമാക്കുക"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ഉയർന്ന നിലവാരം പ്രവർത്തനക്ഷമമാക്കുക"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ഉയർന്ന നിലവാരം പ്രവർത്തനരഹിതമാക്കുക"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനക്ഷമമാക്കുക"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനരഹിതമാക്കുക"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"\'ചിത്രത്തിനുള്ളിൽ ചിത്രം\' മോഡിലേക്ക് പ്രവേശിക്കുക"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"മീഡിയ നിയന്ത്രണങ്ങൾ ‌കാണിച്ചിരിക്കുന്നു"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"മീഡിയ നിയന്ത്രണങ്ങൾ ‌മറച്ചിരിക്കുന്നു, കാണിക്കുന്നതിന് ഡി-‌പാഡ് അമർത്തുക"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"പൂര്‍ത്തിയാക്കുക"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"തുടരുക"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"മീഡിയ പ്ലെയർ പിശക് കോഡ്: %1$d, കൂടെ %2$d എന്നതും"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ആരംഭിക്കുക"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"അടുത്തത്"</string>
+</resources>
diff --git a/v17/leanback/res/values-mn-rMN/strings.xml b/v17/leanback/res/values-mn-rMN/strings.xml
deleted file mode 100644
index f21b649..0000000
--- a/v17/leanback/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Шилжүүлэх цэс"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Хайлтын үйлдэл"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Хайлт"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Ярьж хайх"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> Хайх"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> хайхын тулд ярина уу"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Тоглуулах"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Түр зогсоох"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Хурдан урагшлуулах"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Түргэн Урагш Гүйлгэх %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Буцааж хураах"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Хойш Гүйлгэх %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Дараагийнхийг алгасах"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Өмнөхийг алгасах"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Өөр үйлдлүүд"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Дээш  эрхий хурууг цуцлах"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Дээш эрхий хурууг сонгох"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Доош эрхий хурууг цуцлах"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Доош эрхий хурууг сонгох"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Алийг нь ч давтахгүй"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Бүгдийг давтах"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Нэгийг давтах"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Холихыг идэвхжүүлэх"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Холихыг идэвхгүйжүүлэх"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Өндөр чанарыг идэвхжүүлэх"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Өндөр чанарыг идэвхгүйжүүлэх"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Текст тайлбарыг идэвхжүүлэх"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Текст тайлбарыг идэвхгүйжүүлэх"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Зургийн горимд зураг оруулна уу"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Дуусгах"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Үргэлжлүүлэх"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ЭХЭЛЦГЭЭЕ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Дараах"</string>
-</resources>
diff --git a/v17/leanback/res/values-mn/strings.xml b/v17/leanback/res/values-mn/strings.xml
new file mode 100644
index 0000000..18e0f91
--- /dev/null
+++ b/v17/leanback/res/values-mn/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Шилжүүлэх цэс"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Хайлтын үйлдэл"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Хайлт"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Ярьж хайх"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> Хайх"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> хайхын тулд ярина уу"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Тоглуулах"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Түр зогсоох"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Хурдан урагшлуулах"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Түргэн Урагш Гүйлгэх %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Буцааж хураах"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Хойш Гүйлгэх %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Дараагийнхийг алгасах"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Өмнөхийг алгасах"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Өөр үйлдлүүд"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Дээш  эрхий хурууг цуцлах"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Дээш эрхий хурууг сонгох"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Доош эрхий хурууг цуцлах"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Доош эрхий хурууг сонгох"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Алийг нь ч давтахгүй"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Бүгдийг давтах"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Нэгийг давтах"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Холихыг идэвхжүүлэх"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Холихыг идэвхгүйжүүлэх"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Өндөр чанарыг идэвхжүүлэх"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Өндөр чанарыг идэвхгүйжүүлэх"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Текст тайлбарыг идэвхжүүлэх"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Текст тайлбарыг идэвхгүйжүүлэх"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Зургийн горимд зураг оруулна уу"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Медиа удирдлага харагдаж байна"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Медиа удирдлага нуугдсан байна, харуулахын тулд d-pad-г дарна уу"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Дуусгах"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Үргэлжлүүлэх"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-н алдааны код %1$d нэмэлт %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ЭХЭЛЦГЭЭЕ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Дараах"</string>
+</resources>
diff --git a/v17/leanback/res/values-mr-rIN/strings.xml b/v17/leanback/res/values-mr-rIN/strings.xml
deleted file mode 100644
index ef6a6a6..0000000
--- a/v17/leanback/res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"नेव्हिगेशन मेनू"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"शोध क्रिया"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"शोधा"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"शोधण्यासाठी बोला"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> शोधा"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> शोधण्यासाठी बोला"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"प्ले करा"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"विराम द्या"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"फास्ट फॉरवर्ड करा"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"फास्ट फॉरवर्ड %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"रिवाईँड करा"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"रीवाईंड %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"पुढील वगळा"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"मागील वगळा"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"अधिक क्रिया"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"वर अंगठा निवड रद्द करा"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"वर अंगठा निवडा"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"खाली अंगठा निवड रद्द करा"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"खाली अंगठा निवडा"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"काहीही पुनरावृत्ती करू नका"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"सर्व पुनरावृत्ती करा"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"एक पुनरावृत्ती करा"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"शफल करा सक्षम करा"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"शफल करा अक्षम करा"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"उच्च गुणवत्ता सक्षम करा"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करा"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षके सक्षम करा"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षके अक्षम करा"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोडमध्ये चित्र प्रविष्ट करा"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"सुरू ठेवा"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"प्रारंभ करा"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"पुढील"</string>
-</resources>
diff --git a/v17/leanback/res/values-mr/strings.xml b/v17/leanback/res/values-mr/strings.xml
new file mode 100644
index 0000000..79a7b0c
--- /dev/null
+++ b/v17/leanback/res/values-mr/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"नेव्हिगेशन मेनू"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"शोध क्रिया"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"शोधा"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"शोधण्यासाठी बोला"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> शोधा"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> शोधण्यासाठी बोला"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"प्ले करा"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"विराम द्या"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"फास्ट फॉरवर्ड करा"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"फास्ट फॉरवर्ड %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"रिवाईँड करा"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"रीवाईंड %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"पुढील वगळा"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"मागील वगळा"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"अधिक क्रिया"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"वर अंगठा निवड रद्द करा"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"वर अंगठा निवडा"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"खाली अंगठा निवड रद्द करा"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"खाली अंगठा निवडा"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"काहीही पुनरावृत्ती करू नका"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"सर्व पुनरावृत्ती करा"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"एक पुनरावृत्ती करा"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"शफल करा सक्षम करा"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"शफल करा अक्षम करा"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"उच्च गुणवत्ता सक्षम करा"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करा"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षके सक्षम करा"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षके अक्षम करा"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोडमध्ये चित्र प्रविष्ट करा"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"मीडिया नियंत्रणे दर्शवली आहेत"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"मीडिया नियंत्रणे लपलेली आहेत, दर्शवण्‍यासाठी d-pad दाबा"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"सुरू ठेवा"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"मीडियाप्लेअर एरर कोड %1$d अतिरिक्त %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"प्रारंभ करा"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"पुढील"</string>
+</resources>
diff --git a/v17/leanback/res/values-ms-rMY/strings.xml b/v17/leanback/res/values-ms-rMY/strings.xml
deleted file mode 100644
index 1764e52..0000000
--- a/v17/leanback/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Menu navigasi"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Tindakan Carian"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Carian"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Tutur untuk membuat carian"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Cari <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Sebut untuk mencari <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Main"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Jeda"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Mara Laju"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Lajukan %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Gulung semula"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Gulung semula %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Langkau Seterusnya"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Langkau Sebelumnya"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Lagi Tindakan"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Nyahpilih Bagus"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Pilih Bagus"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Nyahpilih Tidak Bagus"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Pilih Tidak Bagus"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Jangan Ulang"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Ulang Semua"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Ulang Satu"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Dayakan Rombak"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Lumpuhkan Rombak"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Dayakan Kualiti Tinggi"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Lumpuhkan Kualiti Tinggi"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Dayakan Kapsyen Tertutup"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Lumpuhkan Kapsyen Tertutup"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Masukkan Gambar Dalam Mod Gambar"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Teruskan"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"MULAKAN"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Seterusnya"</string>
-</resources>
diff --git a/v17/leanback/res/values-ms/strings.xml b/v17/leanback/res/values-ms/strings.xml
new file mode 100644
index 0000000..8cad319
--- /dev/null
+++ b/v17/leanback/res/values-ms/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Menu navigasi"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Tindakan Carian"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Carian"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Tutur untuk membuat carian"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Cari <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Sebut untuk mencari <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Main"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Jeda"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Mara Laju"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Lajukan %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Gulung semula"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Gulung semula %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Langkau Seterusnya"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Langkau Sebelumnya"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Lagi Tindakan"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Nyahpilih Bagus"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Pilih Bagus"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Nyahpilih Tidak Bagus"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Pilih Tidak Bagus"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Jangan Ulang"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Ulang Semua"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Ulang Satu"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Dayakan Rombak"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Lumpuhkan Rombak"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Dayakan Kualiti Tinggi"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Lumpuhkan Kualiti Tinggi"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Dayakan Kapsyen Tertutup"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Lumpuhkan Kapsyen Tertutup"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Masukkan Gambar Dalam Mod Gambar"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Kawalan media ditunjukkan"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Kawalan media disembunyikan, tekan d-pad untuk menunjukkan"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Teruskan"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kod ralat MediaPlayer %1$d tambahan %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"MULAKAN"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Seterusnya"</string>
+</resources>
diff --git a/v17/leanback/res/values-my-rMM/strings.xml b/v17/leanback/res/values-my-rMM/strings.xml
deleted file mode 100644
index 645f169..0000000
--- a/v17/leanback/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"လမ်းညွှန် မီနူး"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"ရှာဖွေရန် လုပ်ဆောင်ချက်"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ရှာဖွေရန်"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ရှာဖွေရန် ပြောပါ"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>ကို ရှာရန်"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ကို ရှာရန် ပြောပါ"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ခဏရပ်ရန်"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ရှေ့သို့ သွားရန်"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ရှေ့သို့ ရစ်ရန် %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ပြန်ရစ်ရန်"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"နောက်သို့ ရစ်ရန် %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"န​ောက်တစ်ပုဒ်သို့ ကျော်ရန်"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ယခင်တစ်ပုဒ်သို့ သွားရန်"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"နောက်ထပ် လုပ်ဆောင်ချက်များ"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"နှစ်ခြိုက်သော သင်္က​ေတအား မရွေးရန်"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"နှစ်ခြိုက်သော သင်္က​ေတအား ရွေးရန်"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"မနှစ်ခြိုက်သော သင်္က​ေတအား မရွေးရန်"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"မနှစ်ခြိုက်သော သင်္က​ေတအား ရွေးရန်"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ထပ်တလဲလဲမဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"အားလုံး ထပ်တလဲလဲဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"တစ်ခုအား ထပ်တလဲလဲဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ရောသမမွှေခြင်း ပြုရန်"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ရောသမမေွှခြင်း မပြုရန်"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"အရည်အသွေးကောင်းအား ဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"အရည်အသွေးကောင်းအား ပိတ်ထားရန်"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"စာတမ်းထိုး ဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"စာတမ်းထိုးအား ပိတ်ထားရန်"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ဓာတ်ပုံမုဒ်တွင် ဓာတ်ပုံထည့်ပါ"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ပြီးပြီ"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ဆက်လုပ်ရန်"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">"−"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"စတင်ပါ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ရှေ့သို့"</string>
-</resources>
diff --git a/v17/leanback/res/values-my/strings.xml b/v17/leanback/res/values-my/strings.xml
new file mode 100644
index 0000000..8a4068f
--- /dev/null
+++ b/v17/leanback/res/values-my/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"လမ်းညွှန် မီနူး"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"ရှာဖွေရန် လုပ်ဆောင်ချက်"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ရှာဖွေရန်"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ရှာဖွေရန် ပြောပါ"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>ကို ရှာရန်"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ကို ရှာရန် ပြောပါ"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ခဏရပ်ရန်"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ရှေ့သို့ သွားရန်"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"ရှေ့သို့ ရစ်ရန် %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ပြန်ရစ်ရန်"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"နောက်သို့ ရစ်ရန် %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"န​ောက်တစ်ပုဒ်သို့ ကျော်ရန်"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ယခင်တစ်ပုဒ်သို့ သွားရန်"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"နောက်ထပ် လုပ်ဆောင်ချက်များ"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"နှစ်ခြိုက်သော သင်္က​ေတအား မရွေးရန်"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"နှစ်ခြိုက်သော သင်္က​ေတအား ရွေးရန်"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"မနှစ်ခြိုက်သော သင်္က​ေတအား မရွေးရန်"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"မနှစ်ခြိုက်သော သင်္က​ေတအား ရွေးရန်"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ထပ်တလဲလဲမဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"အားလုံး ထပ်တလဲလဲဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"တစ်ခုအား ထပ်တလဲလဲဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ရောသမမွှေခြင်း ပြုရန်"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ရောသမမေွှခြင်း မပြုရန်"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"အရည်အသွေးကောင်းအား ဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"အရည်အသွေးကောင်းအား ပိတ်ထားရန်"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"စာတမ်းထိုး ဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"စာတမ်းထိုးအား ပိတ်ထားရန်"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ဓာတ်ပုံမုဒ်တွင် ဓာတ်ပုံထည့်ပါ"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"မီဒီယာ ခလုတ်များကို ပြထားပါသည်"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"မီဒီယာခလုတ်များကို ဝှက်ထားပါသည်။ ပြရန် d-pad ကို နှိပ်ပါ"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ပြီးပြီ"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ဆက်လုပ်ရန်"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"−"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer မှားယွင်းမှုကုဒ် %1$d နှင့် အပို %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"စတင်ပါ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ရှေ့သို့"</string>
+</resources>
diff --git a/v17/leanback/res/values-nb/strings.xml b/v17/leanback/res/values-nb/strings.xml
index ae3394b..1e924a8 100644
--- a/v17/leanback/res/values-nb/strings.xml
+++ b/v17/leanback/res/values-nb/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivér teksting"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver teksting"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Slå på modusen Bilde-i-bilde"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Mediekontrollene vises"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Mediekontrollene er skjult – trykk på styrepilene for å vise dem"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fullfør"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsett"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer-feilkode %1$d ekstra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"KOM I GANG"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Neste"</string>
 </resources>
diff --git a/v17/leanback/res/values-ne-rNP/strings.xml b/v17/leanback/res/values-ne-rNP/strings.xml
deleted file mode 100644
index e8baffb..0000000
--- a/v17/leanback/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"नेभिगेसन मेनु"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"कार्य खोजी गर्नुहोस्"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"खोजी गर्नुहोस्"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"खोजी गर्न बोल्नुहोस्"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> खोज्नुहोस्"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> खोजी गर्न बोल्नुहोस्"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"प्ले गर्नुहोस्"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"रोक्नुहोस्"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"फास्ट फर्वार्ड"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for lb_playback_controls_fast_forward_multiplier (1058753672110224526) -->
-    <skip />
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"दोहोर्याउनुहोस्"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"पुन: वाइन्ड गर्नुहोस् %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"अर्को छोड्नुहोस्"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"अघिल्लो छोड्नुहोस्"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"थप कार्यहरू"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"औंठा माथि चयन नगर्नुहोस्"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"औंठा माथि चयन गर्नुहोस्"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"औंठा तल चयन नगर्नुहोस्"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"औंठा तल चयन गर्नुहोस्"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"कुनै पनि नदोहोर्याउनुहोस्"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"सबै दोहोर्याउनुहोस्"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"एउटा दोहोर्याउनुहोस्"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"सफ्फल सक्षम"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"सफ्फल असक्षम"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"उच्च गुणस्तर सक्षम"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणस्तर असक्षम"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"बन्द क्याप्सनहरु सक्षम"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"बन्द क्याप्सनहरु असक्षम"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्रलाई चित्र मोडमा प्रविष्ट गर्नुहोस्"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त गर्नुहोस्"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी राख्नुहोस्"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"सुरु गरौँ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"अर्को"</string>
-</resources>
diff --git a/v17/leanback/res/values-ne/strings.xml b/v17/leanback/res/values-ne/strings.xml
new file mode 100644
index 0000000..c6579a6
--- /dev/null
+++ b/v17/leanback/res/values-ne/strings.xml
@@ -0,0 +1,63 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"नेभिगेसन मेनु"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"कार्य खोजी गर्नुहोस्"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"खोजी गर्नुहोस्"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"खोजी गर्न बोल्नुहोस्"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> खोज्नुहोस्"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> खोजी गर्न बोल्नुहोस्"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"प्ले गर्नुहोस्"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"रोक्नुहोस्"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"फास्ट फर्वार्ड"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for lb_playback_controls_fast_forward_multiplier (1058753672110224526) -->
+    <skip />
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"दोहोर्याउनुहोस्"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"पुन: वाइन्ड गर्नुहोस् %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"अर्को छोड्नुहोस्"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"अघिल्लो छोड्नुहोस्"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"थप कार्यहरू"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"औंठा माथि चयन नगर्नुहोस्"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"औंठा माथि चयन गर्नुहोस्"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"औंठा तल चयन नगर्नुहोस्"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"औंठा तल चयन गर्नुहोस्"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"कुनै पनि नदोहोर्याउनुहोस्"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"सबै दोहोर्याउनुहोस्"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"एउटा दोहोर्याउनुहोस्"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"सफ्फल सक्षम"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"सफ्फल असक्षम"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"उच्च गुणस्तर सक्षम"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणस्तर असक्षम"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"बन्द क्याप्सनहरु सक्षम"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"बन्द क्याप्सनहरु असक्षम"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्रलाई चित्र मोडमा प्रविष्ट गर्नुहोस्"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"मिडियाका नियन्त्रणहरू देखाएइका छन्"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"मिडियाका नियन्त्रणहरू लुकेका छन्, देखाउनका लागि d-pad लाई थिच्नुहोस्"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त गर्नुहोस्"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी राख्नुहोस्"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer को त्रुटि सम्बन्धी कोड %1$d अतिरिक्त %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"सुरु गरौँ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"अर्को"</string>
+</resources>
diff --git a/v17/leanback/res/values-nl/strings.xml b/v17/leanback/res/values-nl/strings.xml
index 0a5bdce..e5dc9ac 100644
--- a/v17/leanback/res/values-nl/strings.xml
+++ b/v17/leanback/res/values-nl/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ondertiteling inschakelen"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Ondertiteling uitschakelen"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Beeld-in-beeld-modus openen"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Opties voor mediabediening worden weergegeven"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Opties voor mediabediening verborgen. Druk op de D-pad om ze weer te geven."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Voltooien"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Doorgaan"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"-"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Mediaspeler: foutcode %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"AAN DE SLAG"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Volgende"</string>
 </resources>
diff --git a/v17/leanback/res/values-pa-rIN/strings.xml b/v17/leanback/res/values-pa-rIN/strings.xml
deleted file mode 100644
index b8c7591..0000000
--- a/v17/leanback/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ਆਵਾਗੌਣ ਮੀਨੂ"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"ਖੋਜ ਕਿਰਿਆ"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ਖੋਜੋ"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ਖੋਜਣ ਲਈ ਬੋਲੋ"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ਖੋਜੋ"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ਖੋਜਣ ਲਈ ਬੋਲੋ"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ਪਲੇ ਕਰੋ"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ਰੋਕੋ"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ਅੱਗੇ ਭੇਜੋ"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX ਨੂੰ ਅੱਗੇ ਭੇਜੋ"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ਰੀਵਾਈਂਡ"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ਨੂੰ ਰੀਵਾਈਂਡ ਕਰੋ"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ਅਗਲਾ ਨੂੰ ਛੱਡੋ"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ਪਿਛਲਾ ਨੂੰ ਛੱਡੋ"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ਹੋਰ ਕਿਰਿਆਵਾਂ"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"ਥੰਬ ਅਪ ਨੂੰ ਅਚੋਣਵਾਂ ਕਰੋ"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ਥੰਬ ਅਪ ਨੂੰ ਚੁਣੋ"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"ਥੰਬ ਡਾਊਨ ਨੂੰ ਅਚੋਣਵਾਂ ਕਰੋ"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ਥੰਬ ਡਾਊਨ ਨੂੰ ਚੁਣੋ"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ਕੋਈ ਵੀ ਨਾ ਦੁਹਰਾਓ"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ਸਾਰਿਆਂ ਨੂੰ ਦੁਹਰਾਓ"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ਇੱਕ ਦੁਹਰਾਓ"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ਸ਼ਫਲ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ਸ਼ਫਲ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ਉੱਚ ਗੁਣਵੱਤਾ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ਉੱਚ ਗੁਣਵੱਤਾ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਸਮਰੱਥ ਬਣਾਓ"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ਤਸਵੀਰ ਮੋਡ ਵਿੱਚ ਤਸਵੀਰ ਦਾਖਲ ਕਰੋ"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ਖ਼ਤਮ"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ਜਾਰੀ ਰੱਖੋ"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ਸ਼ੁਰੂਆਤ ਕਰੋ"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ਅੱਗੇ"</string>
-</resources>
diff --git a/v17/leanback/res/values-pa/strings.xml b/v17/leanback/res/values-pa/strings.xml
new file mode 100644
index 0000000..fe8fbd7
--- /dev/null
+++ b/v17/leanback/res/values-pa/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ਆਵਾਗੌਣ ਮੀਨੂ"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"ਖੋਜ ਕਿਰਿਆ"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"ਖੋਜੋ"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ਖੋਜਣ ਲਈ ਬੋਲੋ"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ਖੋਜੋ"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ਖੋਜਣ ਲਈ ਬੋਲੋ"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ਪਲੇ ਕਰੋ"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ਰੋਕੋ"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ਅੱਗੇ ਭੇਜੋ"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX ਨੂੰ ਅੱਗੇ ਭੇਜੋ"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ਰੀਵਾਈਂਡ"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ਨੂੰ ਰੀਵਾਈਂਡ ਕਰੋ"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ਅਗਲਾ ਨੂੰ ਛੱਡੋ"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ਪਿਛਲਾ ਨੂੰ ਛੱਡੋ"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ਹੋਰ ਕਿਰਿਆਵਾਂ"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"ਥੰਬ ਅਪ ਨੂੰ ਅਚੋਣਵਾਂ ਕਰੋ"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ਥੰਬ ਅਪ ਨੂੰ ਚੁਣੋ"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"ਥੰਬ ਡਾਊਨ ਨੂੰ ਅਚੋਣਵਾਂ ਕਰੋ"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ਥੰਬ ਡਾਊਨ ਨੂੰ ਚੁਣੋ"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ਕੋਈ ਵੀ ਨਾ ਦੁਹਰਾਓ"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ਸਾਰਿਆਂ ਨੂੰ ਦੁਹਰਾਓ"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ਇੱਕ ਦੁਹਰਾਓ"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ਸ਼ਫਲ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ਸ਼ਫਲ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"ਉੱਚ ਗੁਣਵੱਤਾ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ਉੱਚ ਗੁਣਵੱਤਾ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ਤਸਵੀਰ ਮੋਡ ਵਿੱਚ ਤਸਵੀਰ ਦਾਖਲ ਕਰੋ"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"ਮੀਡੀਆ ਕੰਟਰੋਲ ਵਿਖਾਏ ਗਏ"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"ਮੀਡੀਆ ਕੰਟਰੋਲ ਲੁਕੇ ਹੋਏ ਹਨ, ਵਿਖਾਉਣ ਲਈ ਡੀ-ਪੈਡ ਦਬਾਓ"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ਖ਼ਤਮ"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ਜਾਰੀ ਰੱਖੋ"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer ਗੜਬੜ ਕੋਡ %1$d ਵਾਧੂ %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ਸ਼ੁਰੂਆਤ ਕਰੋ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ਅੱਗੇ"</string>
+</resources>
diff --git a/v17/leanback/res/values-pl/strings.xml b/v17/leanback/res/values-pl/strings.xml
index 15de203..879c064 100644
--- a/v17/leanback/res/values-pl/strings.xml
+++ b/v17/leanback/res/values-pl/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Włącz napisy"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Wyłącz napisy"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Włącz tryb obrazu w obrazie"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Elementy sterujące multimediami są wyświetlone"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Elementy sterujące multimediami są ukryte. Naciśnij pad kierunkowy, by je wyświetlić"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Zakończ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Dalej"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer – kod błędu %1$d, dodatkowo %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ROZPOCZNIJ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Dalej"</string>
 </resources>
diff --git a/v17/leanback/res/values-pt-rBR/strings.xml b/v17/leanback/res/values-pt-rBR/strings.xml
index 4621809..1076b87 100644
--- a/v17/leanback/res/values-pt-rBR/strings.xml
+++ b/v17/leanback/res/values-pt-rBR/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar closed captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar closed captioning"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entrar no modo Picture in Picture"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Os controles de mídia estão sendo exibidos"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Os controles de mídia estão ocultos. Pressione o botão direcional para exibi-los"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Código de erro do MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"PRIMEIROS PASSOS"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Próximo"</string>
 </resources>
diff --git a/v17/leanback/res/values-pt-rPT/strings.xml b/v17/leanback/res/values-pt-rPT/strings.xml
index 0df9c00..1191a50 100644
--- a/v17/leanback/res/values-pt-rPT/strings.xml
+++ b/v17/leanback/res/values-pt-rPT/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar legendas"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar legendas"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entrar no modo Imagem na imagem"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Controlos de multimédia apresentados"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Controlos de multimédia ocultados, prima o teclado direcional para mostrar"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Código de erro do MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"INICIAR"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Seguinte"</string>
 </resources>
diff --git a/v17/leanback/res/values-pt/strings.xml b/v17/leanback/res/values-pt/strings.xml
index 4621809..1076b87 100644
--- a/v17/leanback/res/values-pt/strings.xml
+++ b/v17/leanback/res/values-pt/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar closed captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar closed captioning"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entrar no modo Picture in Picture"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Os controles de mídia estão sendo exibidos"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Os controles de mídia estão ocultos. Pressione o botão direcional para exibi-los"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Código de erro do MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"PRIMEIROS PASSOS"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Próximo"</string>
 </resources>
diff --git a/v17/leanback/res/values-ro/strings.xml b/v17/leanback/res/values-ro/strings.xml
index 5fa64ef..a362541 100644
--- a/v17/leanback/res/values-ro/strings.xml
+++ b/v17/leanback/res/values-ro/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activează subtitrările"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Dezactivează subtitrările"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activați modul Picture-in-Picture"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Comenzile media sunt afișate"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Comenzile media sunt ascunse. Apăsați pe butonul direcțional pentru a le afișa."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizați"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuați"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Cod de eroare MediaPlayer %1$d suplimentar %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ÎNCEPEȚI"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Înainte"</string>
 </resources>
diff --git a/v17/leanback/res/values-ru/strings.xml b/v17/leanback/res/values-ru/strings.xml
index c7a146f..8d045cf 100644
--- a/v17/leanback/res/values-ru/strings.xml
+++ b/v17/leanback/res/values-ru/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Включить субтитры."</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Отключить субтитры."</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Включить режим \"Картинка в картинке\"."</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Элементы управления показаны"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Элементы управления скрыты. Нажмите D-pad, чтобы показать их."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Готово"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Далее"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Код ошибки медиапроигрывателя: %1$d (дополнительный: %2$d)"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"НАЧАТЬ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Далее"</string>
 </resources>
diff --git a/v17/leanback/res/values-si-rLK/strings.xml b/v17/leanback/res/values-si-rLK/strings.xml
deleted file mode 100644
index 44e4f31..0000000
--- a/v17/leanback/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"සංචාලන මෙනුව"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"සෙවීමේ ක්‍රියාව"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"සොයන්න"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"සෙවීමට කථා කරන්න"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> සොයන්න"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> සොයන්න කථා කරන්න"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ධාවනය කරන්න"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"විරාමය"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"වේගයෙන් ඉදිරියට යන"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX වේගයෙන් ඉදිරියට යවන්න"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"නැවත ඔතන්න"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ආපස්සට යවන්න"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ඊළඟ එක මග අරින්න"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"කළින් එක මග අරින්න"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"තව ක්‍රියාකාරකම්"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"මහපටැඟිල්ල ඉහළට තිබීම තේරීම නොකරන්න"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"මහපටැඟිල්ල ඉහළට තිබීම තේරීම කරන්න"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"මහපටැඟිල්ල පහළට තිබීම තේරීම නොකරන්න"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"මහපටැඟිල්ල පහළට තිබීම තේරීම කරන්න"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"නැවත කරන්න කිසිවක් නැත"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"සියල්ල නැවත කරන්න"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"එකක් නැවත කරන්න"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ඇනීම සබල කරන්න"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ඇනීම අබල කරන්න"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"උපරිම ගුණත්වය සබල කරන ලදි"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"උපරිම ගුණත්වය අබල කරන ලදි"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"වැසුණු ශිර්ෂ කිරීම සබල කරන ලදි"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"වැසුණු ශිර්ෂ කිරීම අබල කරන ලදි"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"පින්තූරය-තුළ-පින්තූරය ප්‍රකාරයට ඇතුළු වන්න"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"අවසානය"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"දිගටම කර ගෙන යන්න"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ආරම්භ කරන්න"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ඊළඟ"</string>
-</resources>
diff --git a/v17/leanback/res/values-si/strings.xml b/v17/leanback/res/values-si/strings.xml
new file mode 100644
index 0000000..54f2f08
--- /dev/null
+++ b/v17/leanback/res/values-si/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"සංචාලන මෙනුව"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"සෙවීමේ ක්‍රියාව"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"සොයන්න"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"සෙවීමට කථා කරන්න"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> සොයන්න"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> සොයන්න කථා කරන්න"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ධාවනය කරන්න"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"විරාමය"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"වේගයෙන් ඉදිරියට යන"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX වේගයෙන් ඉදිරියට යවන්න"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"නැවත ඔතන්න"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ආපස්සට යවන්න"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ඊළඟ එක මග අරින්න"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"කළින් එක මග අරින්න"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"තව ක්‍රියාකාරකම්"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"මහපටැඟිල්ල ඉහළට තිබීම තේරීම නොකරන්න"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"මහපටැඟිල්ල ඉහළට තිබීම තේරීම කරන්න"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"මහපටැඟිල්ල පහළට තිබීම තේරීම නොකරන්න"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"මහපටැඟිල්ල පහළට තිබීම තේරීම කරන්න"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"නැවත කරන්න කිසිවක් නැත"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"සියල්ල නැවත කරන්න"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"එකක් නැවත කරන්න"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ඇනීම සබල කරන්න"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ඇනීම අබල කරන්න"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"උපරිම ගුණත්වය සබල කරන ලදි"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"උපරිම ගුණත්වය අබල කරන ලදි"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"වැසුණු ශිර්ෂ කිරීම සබල කරන ලදි"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"වැසුණු ශිර්ෂ කිරීම අබල කරන ලදි"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"පින්තූරය-තුළ-පින්තූරය ප්‍රකාරයට ඇතුළු වන්න"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"මාධ්‍ය පාලක පෙන්වා ඇත"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"මාධ්‍ය පාලක සඟවා ඇත, පෙන්වීමට d-pad ඔබන්න"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"අවසානය"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"දිගටම කර ගෙන යන්න"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer දෝෂ කේතය %1$d අමතර %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ආරම්භ කරන්න"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ඊළඟ"</string>
+</resources>
diff --git a/v17/leanback/res/values-sk/strings.xml b/v17/leanback/res/values-sk/strings.xml
index bfd972a..bd68c14 100644
--- a/v17/leanback/res/values-sk/strings.xml
+++ b/v17/leanback/res/values-sk/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Zapnúť skryté titulky"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Vypnúť skryté titulky"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vložiť obrázok v režime obrázka"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Ovládacie prvky médií sa zobrazujú"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Ovládacie prvky médií sú skryté, zobrazíte ich stlačením krížového ovládača"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončiť"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Pokračovať"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kód chyby MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAČÍNAME"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Ďalej"</string>
 </resources>
diff --git a/v17/leanback/res/values-sl/strings.xml b/v17/leanback/res/values-sl/strings.xml
index 1919b29..d47b3af 100644
--- a/v17/leanback/res/values-sl/strings.xml
+++ b/v17/leanback/res/values-sl/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogoči podnapise"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogoči podnapise"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vklop načina za sliko v sliki"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Kontrolniki predstavnosti so prikazani"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Kontrolniki predstavnosti so skriti, za prikaz pritisnite smerni gumb"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončaj"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Naprej"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Koda napake MediaPlayer %1$d dodatno %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAČNITE"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Naprej"</string>
 </resources>
diff --git a/v17/leanback/res/values-sq-rAL/strings.xml b/v17/leanback/res/values-sq-rAL/strings.xml
deleted file mode 100644
index 36cfdf3..0000000
--- a/v17/leanback/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Menyja e navigimit"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Veprim i kërkimit"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Kërko"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Fol për të kërkuar"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Kërko për <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Fol për të kërkuar <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Luaj"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauzë"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Përparo me shpejtësi"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Përparo me shpejtësi %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Kthe në fillim"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Kthe në fillim %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Kapërce për te tjetra"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Kapërce të mëparshmin"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Veprime të tjera"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Hiq nga përzgjedhja \"Gishti lart\""</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Përzgjidh \"Gishtin sipër\""</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Hiq nga përzgjedhja \"Gishti poshtë\""</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Përzgjidh \"Gishtin poshtë\""</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Mos përsërit asnjë"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Përsërit të gjitha"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Përsërit një"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Aktivizo përzierjen"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Çaktivizo përzierjen"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Aktivizo \"Cilësinë e lartë\""</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Çaktivizo \"Cilësinë e lartë\""</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivizo titrat"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Çaktivizo titrat me sekuencë kohore"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Fut një fotografi në modalitetin e fotografisë"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Përfundo"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Vazhdo"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"FILLO"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Përpara"</string>
-</resources>
diff --git a/v17/leanback/res/values-sq/strings.xml b/v17/leanback/res/values-sq/strings.xml
new file mode 100644
index 0000000..4c6aa99
--- /dev/null
+++ b/v17/leanback/res/values-sq/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Menyja e navigimit"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Veprim i kërkimit"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Kërko"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Fol për të kërkuar"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Kërko për <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Fol për të kërkuar <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Luaj"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauzë"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Përparo me shpejtësi"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Përparo me shpejtësi %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Kthe në fillim"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Kthe në fillim %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Kapërce për te tjetra"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Kapërce të mëparshmin"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Veprime të tjera"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Hiq nga përzgjedhja \"Gishti lart\""</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Përzgjidh \"Gishtin sipër\""</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Hiq nga përzgjedhja \"Gishti poshtë\""</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Përzgjidh \"Gishtin poshtë\""</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Mos përsërit asnjë"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Përsërit të gjitha"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Përsërit një"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Aktivizo përzierjen"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Çaktivizo përzierjen"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Aktivizo \"Cilësinë e lartë\""</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Çaktivizo \"Cilësinë e lartë\""</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivizo titrat"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Çaktivizo titrat me sekuencë kohore"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Fut një fotografi në modalitetin e fotografisë"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Kontrollet e medias të shfaqura"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Kontrollet e medias të fshehura, shtyp bllokun e drejtimit për t\'i shfaqur"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Përfundo"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Vazhdo"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Kodi i gabimit i MediaPlayer %1$d shtesa %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"FILLO"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Përpara"</string>
+</resources>
diff --git a/v17/leanback/res/values-sr/strings.xml b/v17/leanback/res/values-sr/strings.xml
index 1a9a0c4..e22d127 100644
--- a/v17/leanback/res/values-sr/strings.xml
+++ b/v17/leanback/res/values-sr/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Омогући титлове"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Онемогући титлове"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Уђи у режим Слика у слици"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Контроле за медије су приказане"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Контроле за медије су скривене, притисните контроле за кретање да бисте их приказали"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Доврши"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Настави"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Кôд грешке MediaPlayer-а %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ЗАПОЧНИТЕ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Даље"</string>
 </resources>
diff --git a/v17/leanback/res/values-sv/strings.xml b/v17/leanback/res/values-sv/strings.xml
index 5bdd293..80a9050 100644
--- a/v17/leanback/res/values-sv/strings.xml
+++ b/v17/leanback/res/values-sv/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivera textning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inaktivera textning"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Ange läget Bild-i-bild"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Mediakontrollerna visas"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Mediakontrollerna är dolda och visas om du trycker på styrkorset"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Slutför"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsätt"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Felkod för MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"KOM IGÅNG"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Nästa"</string>
 </resources>
diff --git a/v17/leanback/res/values-sw/strings.xml b/v17/leanback/res/values-sw/strings.xml
index 6683e46..45ab73b 100644
--- a/v17/leanback/res/values-sw/strings.xml
+++ b/v17/leanback/res/values-sw/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Washa manukuu"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Zima manukuu"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Weka Picha Katika Hali ya Picha"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Inaonyesha udhibiti wa maudhui"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Imeficha udhibiti wa maudhui, bonyeza d-pad ili uuonyeshe"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Kamilisha"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Endelea"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Msimbo wa hitilafu wa Kichezaji Maudhui %1$d %2$d zaidi"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ANZA KUTUMIA"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Inayofuata"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Endelea"</string>
 </resources>
diff --git a/v17/leanback/res/values-ta-rIN/strings.xml b/v17/leanback/res/values-ta-rIN/strings.xml
deleted file mode 100644
index 5afde83..0000000
--- a/v17/leanback/res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"வழிசெலுத்தல் மெனு"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"செயலைத் தேடுக"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"தேடு"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"தேட, பேசவும்"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> இல் தேடுக"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ஐத் தேட, பேசவும்"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"இயக்கு"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"இடைநிறுத்து"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"வேகமாக முன் நகர்த்து"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX வேகத்தில் முன்செல்"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"வேகமாக பின் நகர்த்து"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX வேகத்தில் பின்செல்"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"அடுத்ததைத் தவிர்"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"முந்தையதைத் தவிர்"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"மேலும் செயல்கள்"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"தரமேற்றத்தைத் திரும்பப் பெறு"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"தரமேற்றத்தைத் தேர்ந்தெடு"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"தரமிறக்கத்தைத் திரும்பப் பெறு"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"தரமிறக்கத்தைத் தேர்ந்தெடு"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"எதையும் மீண்டும் இயக்காதே"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"அனைத்தையும் மீண்டும் இயக்கு"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ஒன்றை மட்டும் மீண்டும் இயக்கு"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"கலைத்து இயக்கு"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"கலைக்காமல் இயக்கு"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"உயர் தரத்தை இயக்கு"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"உயர் தரத்தை முடக்கு"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"விரிவான வசனங்களை இயக்கு"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"விரிவான வசனங்களை முடக்கு"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"பிக்ச்சர் இன் பிக்ச்சர் பயன்முறைக்குச் செல்"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"முடி"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"தொடர்க"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"தொடங்குக"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"அடுத்து"</string>
-</resources>
diff --git a/v17/leanback/res/values-ta/strings.xml b/v17/leanback/res/values-ta/strings.xml
new file mode 100644
index 0000000..d7cc8ed
--- /dev/null
+++ b/v17/leanback/res/values-ta/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"வழிசெலுத்தல் மெனு"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"செயலைத் தேடுக"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"தேடு"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"தேட, பேசவும்"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> இல் தேடுக"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ஐத் தேட, பேசவும்"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"இயக்கு"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"இடைநிறுத்து"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"வேகமாக முன் நகர்த்து"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX வேகத்தில் முன்செல்"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"வேகமாக பின் நகர்த்து"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX வேகத்தில் பின்செல்"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"அடுத்ததைத் தவிர்"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"முந்தையதைத் தவிர்"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"மேலும் செயல்கள்"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"தரமேற்றத்தைத் திரும்பப் பெறு"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"தரமேற்றத்தைத் தேர்ந்தெடு"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"தரமிறக்கத்தைத் திரும்பப் பெறு"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"தரமிறக்கத்தைத் தேர்ந்தெடு"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"எதையும் மீண்டும் இயக்காதே"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"அனைத்தையும் மீண்டும் இயக்கு"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ஒன்றை மட்டும் மீண்டும் இயக்கு"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"கலைத்து இயக்கு"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"கலைக்காமல் இயக்கு"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"உயர் தரத்தை இயக்கு"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"உயர் தரத்தை முடக்கு"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"விரிவான வசனங்களை இயக்கு"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"விரிவான வசனங்களை முடக்கு"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"பிக்ச்சர் இன் பிக்ச்சர் பயன்முறைக்குச் செல்"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"மீடியா கட்டுப்பாடுகள் காட்டப்படுகின்றன"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"மீடியா கட்டுப்பாடுகள் மறைக்கப்பட்டுள்ளன. கட்டுப்பாடுகளைக் காட்ட, டிபேடை அழுத்தவும்"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"முடி"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"தொடர்க"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer பிழைக் குறியீடு: %1$d கூடுதல் %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"தொடங்குக"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"அடுத்து"</string>
+</resources>
diff --git a/v17/leanback/res/values-te-rIN/strings.xml b/v17/leanback/res/values-te-rIN/strings.xml
deleted file mode 100644
index f74c525..0000000
--- a/v17/leanback/res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"నావిగేషన్ మెను"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"శోధన చర్య"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"శోధించండి"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"శోధించడానికి చదివి వినిపించండి"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>ని శోధించండి"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>ని శోధించడానికి చదివి వినిపించండి"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"ప్లే చేయి"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"పాజ్ చేయి"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"వేగంగా ఫార్వార్డ్ చేయి"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX ఫాస్ట్ ఫార్వార్డ్ చేయి"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"రివైండ్ చేయి"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX రివైండ్ చేయి"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"తదుపరి దానికి దాటవేయి"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"మునుపటి దానికి దాటవేయి"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"మరిన్ని చర్యలు"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"విజయ సంకేతం ఎంపికను తీసివేయి"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"విజయ సంకేతాన్ని ఎంచుకోండి"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"ఓటమి సంకేతం ఎంపికను తీసివేయి"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ఓటమి సంకేతాన్ని ఎంచుకోండి"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ఏదీ పునరావృతం చేయవద్దు"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"అన్నీ పునరావృతం చేయి"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ఒకదాన్ని పునరావృతం చేయి"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"షఫుల్‌ను ప్రారంభించు"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"షఫుల్‌ను నిలిపివేయి"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"అధిక నాణ్యతను ప్రారంభించు"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"అధిక నాణ్యతను నిలిపివేయి"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"సంవృత శీర్షికలను ప్రారంభించు"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"సంవృత శీర్షికలను నిలిపివేయి"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"చిత్రంలో చిత్రం మోడ్‌లోకి ప్రవేశించండి"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ముగించు"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"కొనసాగించు"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ప్రారంభించు"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"తదుపరి"</string>
-</resources>
diff --git a/v17/leanback/res/values-te/strings.xml b/v17/leanback/res/values-te/strings.xml
new file mode 100644
index 0000000..6b8d1f4
--- /dev/null
+++ b/v17/leanback/res/values-te/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"నావిగేషన్ మెను"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"శోధన చర్య"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"శోధించండి"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"శోధించడానికి చదివి వినిపించండి"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>ని శోధించండి"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>ని శోధించడానికి చదివి వినిపించండి"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"ప్లే చేయి"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"పాజ్ చేయి"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"వేగంగా ఫార్వార్డ్ చేయి"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX ఫాస్ట్ ఫార్వార్డ్ చేయి"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"రివైండ్ చేయి"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX రివైండ్ చేయి"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"తదుపరి దానికి దాటవేయి"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"మునుపటి దానికి దాటవేయి"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"మరిన్ని చర్యలు"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"విజయ సంకేతం ఎంపికను తీసివేయి"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"విజయ సంకేతాన్ని ఎంచుకోండి"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"ఓటమి సంకేతం ఎంపికను తీసివేయి"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ఓటమి సంకేతాన్ని ఎంచుకోండి"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ఏదీ పునరావృతం చేయవద్దు"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"అన్నీ పునరావృతం చేయి"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ఒకదాన్ని పునరావృతం చేయి"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"షఫుల్‌ను ప్రారంభించు"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"షఫుల్‌ను నిలిపివేయి"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"అధిక నాణ్యతను ప్రారంభించు"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"అధిక నాణ్యతను నిలిపివేయి"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"సంవృత శీర్షికలను ప్రారంభించు"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"సంవృత శీర్షికలను నిలిపివేయి"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"చిత్రంలో చిత్రం మోడ్‌లోకి ప్రవేశించండి"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"మీడియా నియంత్రణలు చూపబడ్డాయి"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"మీడియా నియంత్రణలు దాచబడ్డాయి, చూపించడానికి d-ప్యాడ్ నొక్కండి"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ముగించు"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"కొనసాగించు"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer ఎర్రర్ కోడ్ %1$d అదనంగా %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ప్రారంభించు"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"తదుపరి"</string>
+</resources>
diff --git a/v17/leanback/res/values-th/strings.xml b/v17/leanback/res/values-th/strings.xml
index 8440dd9..0dae8eb 100644
--- a/v17/leanback/res/values-th/strings.xml
+++ b/v17/leanback/res/values-th/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"เปิดใช้คำบรรยาย"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ปิดใช้คำบรรยาย"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"เข้าสู่โหมดการแสดงผลหลายแหล่งพร้อมกัน"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"แสดงการควบคุมสื่ออยู่"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"ซ่อนการควบคุมสื่ออยู่ กด d-pad เพื่อแสดง"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"เสร็จสิ้น"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ต่อไป"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"รหัสข้อผิดพลาด MediaPlayer %1$d เพิ่มเติม %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"เริ่มต้นใช้งาน"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"ถัดไป"</string>
 </resources>
diff --git a/v17/leanback/res/values-tl/strings.xml b/v17/leanback/res/values-tl/strings.xml
index c2f52a4..0214efc 100644
--- a/v17/leanback/res/values-tl/strings.xml
+++ b/v17/leanback/res/values-tl/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"I-enable ang Paglalagay ng Subtitle"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"I-disable ang Paglalagay ng Subtitle"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Pumasok sa Picture In Picture Mode"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Ipinapakita ang mga kontrol ng media"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Nakatago ang mga kontrol ng media, pindutin ang d-pad upang ipakita"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tapusin"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Magpatuloy"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Code ng error na %1$d ng MediaPlayer na may extra na %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"MAGSIMULA"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Susunod"</string>
 </resources>
diff --git a/v17/leanback/res/values-tr/strings.xml b/v17/leanback/res/values-tr/strings.xml
index 0c6431e..fbc6a5a 100644
--- a/v17/leanback/res/values-tr/strings.xml
+++ b/v17/leanback/res/values-tr/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Altyazıları Etkinleştir"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Altyazıları Devre Dışı Bırak"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Resim İçinde Resim Moduna Geç"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"."</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Medya denetimleri gösteriliyor"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Medya denetimleri gizli durumda. Görüntülemek için d-pad\'e basın."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Son"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Devam"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer hata kodu %1$d ekstra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BAŞLA"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Sonraki"</string>
 </resources>
diff --git a/v17/leanback/res/values-uk/strings.xml b/v17/leanback/res/values-uk/strings.xml
index bb6bde7..1829560 100644
--- a/v17/leanback/res/values-uk/strings.xml
+++ b/v17/leanback/res/values-uk/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Увімкнути субтитри"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Вимкнути субтитри"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Перейти в режим \"Картинка в картинці\""</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"."</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Показано елементи керування медіа"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Елементи керування медіа сховано. Натисніть цифрову панель, щоб показати їх"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Закінчити"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продовжити"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Код помилки MediaPlayer: %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ПОЧАТИ"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Далі"</string>
 </resources>
diff --git a/v17/leanback/res/values-ur-rPK/strings.xml b/v17/leanback/res/values-ur-rPK/strings.xml
deleted file mode 100644
index f387aa3..0000000
--- a/v17/leanback/res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"نیویگیشن مینو"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"تلاش کی کارروائی"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"تلاش کریں"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"تلاش کرنے کیلئے بولیں"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> تلاش کریں"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> تلاش کرنے کیلئے بولیں"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"چلائیں"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"موقوف کریں"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"تیزی سے فارورڈ کریں"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"‏تیزی سے فارورڈ کریں ‎%1$dX‎"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ریوائینڈ کریں"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"‏ریوائنڈ کریں ‎%1$dX‎"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"اگلے پر جائیں"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"پچھلے پر جائیں"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"مزید کارروائیاں"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"اوپر کی طرف والے انگوٹھے کے نشان کو غیر منتخب کریں"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"اوپر کی طرف والے انگوٹھے کے نشان کو منتخب کریں"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"نیچے کی طرف والے انگوٹھے کے نشان کو غیر منتخب کریں"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"نیچے کی طرف والے انگوٹھے کے نشان کو منتخب کریں"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"کسی کو نہ دہرائیں"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"سبھی کو دہرائیں"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ایک کو دہرائیں"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"شفل کو فعال کریں"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"شفل کو غیر فعال کریں"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"اعلی معیار کو فعال کریں"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"اعلی معیار کو غیر فعال کریں"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"سب ٹائٹلز کو فعال کریں"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"سب ٹائٹلز کو غیر فعال کریں"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"\'تصویر میں تصویر موڈ\' میں داخل ہوں"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"مکمل کریں"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"جاری رکھیں"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"شروع کریں"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"اگلا"</string>
-</resources>
diff --git a/v17/leanback/res/values-ur/strings.xml b/v17/leanback/res/values-ur/strings.xml
new file mode 100644
index 0000000..f5b7a25
--- /dev/null
+++ b/v17/leanback/res/values-ur/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"نیویگیشن مینو"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"تلاش کی کارروائی"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"تلاش کریں"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"تلاش کرنے کیلئے بولیں"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> تلاش کریں"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> تلاش کرنے کیلئے بولیں"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"چلائیں"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"موقوف کریں"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"تیزی سے فارورڈ کریں"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"‏تیزی سے فارورڈ کریں ‎%1$dX‎"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ریوائینڈ کریں"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"‏ریوائنڈ کریں ‎%1$dX‎"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"اگلے پر جائیں"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"پچھلے پر جائیں"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"مزید کارروائیاں"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"اوپر کی طرف والے انگوٹھے کے نشان کو غیر منتخب کریں"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"اوپر کی طرف والے انگوٹھے کے نشان کو منتخب کریں"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"نیچے کی طرف والے انگوٹھے کے نشان کو غیر منتخب کریں"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"نیچے کی طرف والے انگوٹھے کے نشان کو منتخب کریں"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"کسی کو نہ دہرائیں"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"سبھی کو دہرائیں"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ایک کو دہرائیں"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"شفل کو فعال کریں"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"شفل کو غیر فعال کریں"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"اعلی معیار کو فعال کریں"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"اعلی معیار کو غیر فعال کریں"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"سب ٹائٹلز کو فعال کریں"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"سب ٹائٹلز کو غیر فعال کریں"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"\'تصویر میں تصویر موڈ\' میں داخل ہوں"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"میڈیا کنٹرولز عیاں ہیں"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"‏میڈیا کنٹرولز مخفی ہیں، شو کرنے کیلئے d-pad دبائیں"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"مکمل کریں"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"جاری رکھیں"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"‏میڈیا پلیئر کی خرابی کا کوڈ %1$d اضافی %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"شروع کریں"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"اگلا"</string>
+</resources>
diff --git a/v17/leanback/res/values-uz-rUZ/strings.xml b/v17/leanback/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 48dc067..0000000
--- a/v17/leanback/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Navigatsiya menyusi"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"Qidiruv amali"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Qidirish"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Qidirish uchun gapiring"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Qidirish: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Qidirish uchun ayting: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Ijro qilish"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"To‘xtatib turish"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Oldinga o‘tkazish"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX tezlikda oldinga o‘tkazish"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Orqaga o‘tkazish"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX tezlikda orqaga qaytarish"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Keyingisiga o‘tish"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Avvalgisiga qaytish"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Boshqa amallar"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Ijobiy baho tanlovini bekor qilish"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Ijobiy bahoni tanlash"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Salbiy baho tanlovini bekor qilish"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Salbiy bahoni tanlash"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Takrorlamaslik"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Barchasini takrorlash"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Bir marta takrorlash"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Aralashtirish funksiyasini yoqish"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Aralashtirish funksiyasini o‘chirish"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Yuqori sifatni yoqish"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yuqori sifatni o‘chirib qo‘yish"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Taglavhalarni yoqish"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Taglavhalarni o‘chirib qo‘yish"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tasvir ichida tasvir rejimiga kirish"</string>
-    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tugatish"</string>
-    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davom etish"</string>
-    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
-    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
-    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BOSHLADIK"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Keyingisi"</string>
-</resources>
diff --git a/v17/leanback/res/values-uz/strings.xml b/v17/leanback/res/values-uz/strings.xml
new file mode 100644
index 0000000..de8558f
--- /dev/null
+++ b/v17/leanback/res/values-uz/strings.xml
@@ -0,0 +1,61 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"Navigatsiya menyusi"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"Qidiruv amali"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Qidirish"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Qidirish uchun gapiring"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Qidirish: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Qidirish uchun ayting: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Ijro qilish"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"To‘xtatib turish"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Oldinga o‘tkazish"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX tezlikda oldinga o‘tkazish"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Orqaga o‘tkazish"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX tezlikda orqaga qaytarish"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Keyingisiga o‘tish"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Avvalgisiga qaytish"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Boshqa amallar"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Ijobiy baho tanlovini bekor qilish"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Ijobiy bahoni tanlash"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Salbiy baho tanlovini bekor qilish"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Salbiy bahoni tanlash"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Takrorlamaslik"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Barchasini takrorlash"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Bir marta takrorlash"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Aralashtirish funksiyasini yoqish"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Aralashtirish funksiyasini o‘chirish"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Yuqori sifatni yoqish"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yuqori sifatni o‘chirib qo‘yish"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Taglavhalarni yoqish"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Taglavhalarni o‘chirib qo‘yish"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tasvir ichida tasvir rejimiga kirish"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Boshqaruv elementlari ochiq"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Boshqaruv elementlari berkitilgan, ochish uchun D-pad tugmasini bosing"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tugatish"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davom etish"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Media pleyer xatoligi kodi: %1$d (yana: %2$d)"</string>
+    <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BOSHLADIK"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Keyingisi"</string>
+</resources>
diff --git a/v17/leanback/res/values-v18/themes.xml b/v17/leanback/res/values-v18/themes.xml
new file mode 100644
index 0000000..9fc7722
--- /dev/null
+++ b/v17/leanback/res/values-v18/themes.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources>
+    <style name="Theme.LeanbackBase" parent="android:Theme.Holo.NoActionBar">
+        <item name="playbackProgressPrimaryColor">@color/lb_playback_progress_color_no_theme</item>
+        <item name="playbackControlsIconHighlightColor">@color/lb_playback_icon_highlight_no_theme</item>
+        <item name="defaultBrandColor">@color/lb_default_brand_color</item>
+        <item name="defaultBrandColorDark">@color/lb_default_brand_color_dark</item>
+
+        <item name="android:windowOverscan">true</item>
+        <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item>
+    </style>
+</resources>
diff --git a/v17/leanback/res/values-v21/styles.xml b/v17/leanback/res/values-v21/styles.xml
index 89029c4..cd81934 100644
--- a/v17/leanback/res/values-v21/styles.xml
+++ b/v17/leanback/res/values-v21/styles.xml
@@ -25,4 +25,9 @@
         <item name="android:background">@drawable/lb_action_bg</item>
     </style>
 
+    <style name="Widget.Leanback.OnboardingStartButtonStyleBase">
+        <item name="android:elevation">1.5dp</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
 </resources>
\ No newline at end of file
diff --git a/v17/leanback/res/values-v21/themes.xml b/v17/leanback/res/values-v21/themes.xml
index 886077a..4e5bd5a 100644
--- a/v17/leanback/res/values-v21/themes.xml
+++ b/v17/leanback/res/values-v21/themes.xml
@@ -26,10 +26,49 @@
 
         <item name="android:windowOverscan">true</item>
         <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item>
+
+        <!-- android:windowSharedElementEnterTransition is kept for backward compatibility for apps still refer
+        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
+        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
+        <!-- android:windowSharedElementReturnTransition is kept for backward compatibility for apps still refer
+        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
+        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
+        <item name="android:windowEnterTransition">@transition/lb_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_return_transition</item>
+        <item name="android:windowTransitionBackgroundFadeDuration">350</item>
+
+    </style>
+
+    <style name="Theme.Leanback.Browse" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_browse_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_browse_return_transition</item>
+    </style>
+
+    <style name="Theme.Leanback.VerticalGrid" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_vertical_grid_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_vertical_grid_return_transition</item>
+    </style>
+
+    <style name="Theme.Leanback.Details" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_details_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_details_return_transition</item>
+        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
+        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
+    </style>
+
+    <style name="Theme.Leanback.Details.NoSharedElementTransition">
+        <item name="android:windowSharedElementEnterTransition">@null</item>
+        <item name="android:windowSharedElementReturnTransition">@null</item>
     </style>
 
     <style name="Theme.Leanback.GuidedStepBase" parent="Theme.LeanbackBase">
         <item name="guidedActionsSelectorDrawable">@drawable/lb_selectable_item_rounded_rect</item>
+        <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter</item>
+        <item name="android:windowTransitionBackgroundFadeDuration">@integer/lb_guidedstep_activity_background_fade_duration_ms</item>
+    </style>
+
+    <style name="Theme.Leanback.GuidedStep.HalfBase" parent="Theme.Leanback.GuidedStep">
+        <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter_bottom</item>
     </style>
 
 </resources>
diff --git a/v17/leanback/res/values-vi/strings.xml b/v17/leanback/res/values-vi/strings.xml
index 8d8c282..228bef0 100644
--- a/v17/leanback/res/values-vi/strings.xml
+++ b/v17/leanback/res/values-vi/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Bật phụ đề"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Tắt phụ đề"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vào ảnh ở chế độ ảnh"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Điều khiển phương tiện được hiển thị"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Điều khiển phương tiện bị ẩn, nhấn d-pad để hiển thị"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Hoàn tất"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Tiếp tục"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Mã lỗi MediaPlayer %1$d %2$d bổ sung"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"BẮT ĐẦU"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Tiếp theo"</string>
 </resources>
diff --git a/v17/leanback/res/values-zh-rCN/strings.xml b/v17/leanback/res/values-zh-rCN/strings.xml
index 611bff1..d635311 100644
--- a/v17/leanback/res/values-zh-rCN/strings.xml
+++ b/v17/leanback/res/values-zh-rCN/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"开启字幕"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"关闭字幕"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"进入画中画模式"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"媒体控件已显示"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"媒体控件已隐藏,按 D-pad 即可显示"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"继续"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer 错误代码:%1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"开始使用"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"下一页"</string>
 </resources>
diff --git a/v17/leanback/res/values-zh-rHK/strings.xml b/v17/leanback/res/values-zh-rHK/strings.xml
index c18032e..2da2e73 100644
--- a/v17/leanback/res/values-zh-rHK/strings.xml
+++ b/v17/leanback/res/values-zh-rHK/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"啟用字幕"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"停用字幕"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"進入「畫中畫模式」"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"畫面已顯示媒體控制項"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"畫面已隱藏媒體控制項,按十字鍵即可顯示"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"繼續"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer 錯誤代碼:%1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"開始使用"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"下一頁"</string>
 </resources>
diff --git a/v17/leanback/res/values-zh-rTW/strings.xml b/v17/leanback/res/values-zh-rTW/strings.xml
index 1e14d0a..721dc6e 100644
--- a/v17/leanback/res/values-zh-rTW/strings.xml
+++ b/v17/leanback/res/values-zh-rTW/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"啟用字幕"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"停用字幕"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"進入子母畫面模式"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"媒體控制項已顯示"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"媒體控制項已隱藏,按下 D-Pad 即可顯示"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"繼續"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"MediaPlayer 錯誤代碼:%1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"開始使用"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"繼續"</string>
 </resources>
diff --git a/v17/leanback/res/values-zu/strings.xml b/v17/leanback/res/values-zu/strings.xml
index 7fb290f..6812abd 100644
--- a/v17/leanback/res/values-zu/strings.xml
+++ b/v17/leanback/res/values-zu/strings.xml
@@ -48,10 +48,14 @@
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Nika amandla imibhalo engezansi"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Khubaza imihbalo engezansi"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Ngena isithombe kumodi yesithombe"</string>
+    <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Izilawuli zemidiya zibonisiwe"</string>
+    <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Izilawuli zemidiya zifihliwe, cindezela ku-d-pad ukuze uzibonise"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Qeda"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Qhubeka"</string>
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+    <string name="lb_media_player_error" msgid="3650250994187305396">"Ikhodi yephutha le-MediaPlayer %1$d extra %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"QALISA"</string>
     <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Okulandelayo"</string>
 </resources>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 3e2d7f2..b56011c 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -567,6 +567,10 @@
         <!-- Theme attribute for the style of the logo in onboarding screen. Default is
              {@link android.support.v17.leanback.R.style#Widget_Leanback_OnboardingLogoStyle}.-->
         <attr name="onboardingLogoStyle" format="reference" />
+
+        <!-- Theme attribute for the style of the main icon in onboarding fragment. Default is
+             {@link android.support.v17.leanback.R.style#Widget_Leanback_OnboardingMainIconStyle}.-->
+        <attr name="onboardingMainIconStyle" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="PagingIndicator">
@@ -580,6 +584,8 @@
         <attr name="dotToArrowGap" format="reference" />
         <!-- Attribute for background color of the dots in PagingIndicator. -->
         <attr name="dotBgColor" format="reference" />
+        <!-- Attribute for the arrow color in PagingIndicator. -->
+        <attr name="arrowColor" format="reference" />
         <!-- Attribute for background color of the arrow in PagingIndicator. -->
         <attr name="arrowBgColor" format="reference" />
     </declare-styleable>
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 4bf237d..7a39b78 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -160,6 +160,33 @@
     <dimen name="lb_playback_now_playing_view_size">28dp</dimen>
     <dimen name="lb_playback_play_icon_size">14dp</dimen>
 
+    <!-- margin to move controlBar button a bit left to align icon with description left -->
+    <dimen name="lb_playback_transport_controlbar_margin_start">-12dp</dimen>
+    <dimen name="lb_playback_transport_control_info_margin_bottom">20dp</dimen>
+    <dimen name="lb_playback_transport_control_row_padding_bottom">20dp</dimen>
+    <dimen name="lb_playback_transport_image_height">176dp</dimen>
+    <dimen name="lb_playback_transport_image_margin_end">24dp</dimen>
+
+    <!-- height should including enough space for thumbs when activated -->
+    <dimen name="lb_playback_transport_progressbar_height">28dp</dimen>
+    <!-- height for the bar when not focused -->
+    <dimen name="lb_playback_transport_progressbar_bar_height">4dp</dimen>
+    <!-- height for the bar when focused -->
+    <dimen name="lb_playback_transport_progressbar_active_bar_height">6dp</dimen>
+    <!-- radius of thumb when focused -->
+    <dimen name="lb_playback_transport_progressbar_active_radius">6dp</dimen>
+
+    <!-- Thumbs bar -->
+    <dimen name="lb_playback_transport_thumbs_width">154dp</dimen>
+    <dimen name="lb_playback_transport_thumbs_height">154dp</dimen>
+    <dimen name="lb_playback_transport_hero_thumbs_width">192dp</dimen>
+    <dimen name="lb_playback_transport_hero_thumbs_height">192dp</dimen>
+    <dimen name="lb_playback_transport_thumbs_margin">4dp</dimen>
+    <dimen name="lb_playback_transport_thumbs_bottom_margin">18dp</dimen>
+
+    <dimen name="lb_playback_transport_time_margin">8dp</dimen>
+    <dimen name="lb_playback_transport_time_margin_top">8dp</dimen>
+
     <dimen name="lb_control_button_diameter">90dp</dimen>
     <dimen name="lb_control_button_height">64dp</dimen>
     <dimen name="lb_control_button_secondary_diameter">48dp</dimen>
@@ -231,8 +258,8 @@
     <dimen name="lb_basic_card_info_badge_margin">4dp</dimen>
 
     <!-- z based shadow -->
-    <dimen name="lb_material_shadow_normal_z">6dp</dimen>
-    <dimen name="lb_material_shadow_focused_z">16dp</dimen>
+    <dimen name="lb_material_shadow_normal_z">0dp</dimen>
+    <dimen name="lb_material_shadow_focused_z">10dp</dimen>
     <dimen name="lb_material_shadow_details_z">8dp</dimen>
     <dimen name="lb_search_orb_unfocused_z">2dp</dimen>
     <dimen name="lb_search_orb_focused_z">8dp</dimen>
diff --git a/v17/leanback/res/values/integers.xml b/v17/leanback/res/values/integers.xml
index c9f3384..b8bdb18 100644
--- a/v17/leanback/res/values/integers.xml
+++ b/v17/leanback/res/values/integers.xml
@@ -29,6 +29,8 @@
     <integer name="lb_playback_rows_fade_out_ms">250</integer>
     <integer name="lb_playback_rows_fade_delay_ms">100</integer>
     <integer name="lb_playback_controls_show_time_ms">3000</integer>
+    <integer name="lb_onboarding_header_title_delay">33</integer>
+    <integer name="lb_onboarding_header_description_delay">33</integer>
 
     <!-- Gravity.LEFT -->
     <integer name="slideEdgeStart">3</integer>
diff --git a/v17/leanback/res/values/strings.xml b/v17/leanback/res/values/strings.xml
index cbf4904..aef086a 100644
--- a/v17/leanback/res/values/strings.xml
+++ b/v17/leanback/res/values/strings.xml
@@ -80,7 +80,8 @@
     <string name="lb_playback_controls_closed_captioning_disable">Disable Closed Captioning</string>
     <!-- Talkback label for the control button to enter picture in picture mode -->
     <string name="lb_playback_controls_picture_in_picture">Enter Picture In Picture Mode</string>
-
+    <!-- separator between current time and duration -->
+    <string name="lb_playback_time_separator">/</string>
 
     <string name="lb_playback_controls_shown">Media controls shown</string>
     <string name="lb_playback_controls_hidden">Media controls hidden, press d-pad to show</string>
@@ -95,6 +96,9 @@
     <!-- Separator for time picker [CHAR LIMIT=2] -->
     <string name="lb_time_separator">:</string>
 
+    <!-- Error string for MediaPlayer -->
+    <string name="lb_media_player_error">MediaPlayer error code %1$d extra %2$d</string>
+
     <!-- Onboarding screen -->
     <eat-comment />
     <!-- Text for "GET STARTED" button. This text should be in ALL CAPS. -->
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index dbe090a..b7e3da2 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -751,19 +751,20 @@
         <item name="arrowBgColor">@color/lb_page_indicator_arrow_background</item>
     </style>
 
+    <style name="Widget.Leanback.OnboardingStartButtonStyleBase">
+    </style>
+
     <!-- Style for the start button in OnboardingFragment. -->
-    <style name="Widget.Leanback.OnboardingStartButtonStyle">
+    <style name="Widget.Leanback.OnboardingStartButtonStyle" parent="Widget.Leanback.OnboardingStartButtonStyleBase">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">36dp</item>
         <item name="android:layout_gravity">center_horizontal</item>
         <item name="android:layout_marginBottom">4dp</item>
         <item name="android:background">@drawable/lb_onboarding_start_button_background</item>
-        <item name="android:elevation">1.5dp</item>
         <item name="android:fontFamily">sans-serif</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:paddingEnd">24dp</item>
         <item name="android:paddingStart">24dp</item>
-        <item name="android:stateListAnimator">@null</item>
         <item name="android:text">@string/lb_onboarding_get_started</item>
         <item name="android:textAllCaps">true</item>
         <item name="android:textColor">#014269</item>
@@ -778,4 +779,15 @@
         <item name="android:contentDescription">@null</item>
     </style>
 
+    <!-- Styles for the main icon in OnboardingFragment. -->
+    <style name="Widget.Leanback.OnboardingMainIconStyle">
+        <item name="android:layout_width">64dp</item>
+        <item name="android:layout_height">64dp</item>
+        <item name="android:layout_above">@id/page_container</item>
+        <item name="android:layout_centerHorizontal">true</item>
+        <item name="android:layout_marginBottom">16dp</item>
+        <item name="android:contentDescription">@null</item>
+        <item name="android:visibility">gone</item>
+    </style>
+
 </resources>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index c6d6baa..d40734e 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -24,7 +24,6 @@
         <item name="defaultBrandColor">@color/lb_default_brand_color</item>
         <item name="defaultBrandColorDark">@color/lb_default_brand_color_dark</item>
 
-        <item name="android:windowOverscan">true</item>
         <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item>
     </style>
 
@@ -103,16 +102,6 @@
 
         <item name="defaultSectionHeaderColor">?attr/defaultSearchColor</item>
 
-        <!-- android:windowSharedElementEnterTransition is kept for backward compatibility for apps still refer
-        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
-        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
-        <!-- android:windowSharedElementReturnTransition is kept for backward compatibility for apps still refer
-        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
-        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
-        <item name="android:windowEnterTransition">@transition/lb_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_return_transition</item>
-        <item name="android:windowTransitionBackgroundFadeDuration">350</item>
-
         <item name="overlayDimMaskColor">@color/lb_view_dim_mask_color</item>
         <item name="overlayDimActiveLevel">@fraction/lb_view_active_level</item>
         <item name="overlayDimDimmedLevel">@fraction/lb_view_dimmed_level</item>
@@ -120,26 +109,16 @@
     </style>
 
     <style name="Theme.Leanback.Browse" parent="Theme.Leanback">
-        <item name="android:windowEnterTransition">@transition/lb_browse_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_browse_return_transition</item>
     </style>
 
     <style name="Theme.Leanback.VerticalGrid" parent="Theme.Leanback">
-        <item name="android:windowEnterTransition">@transition/lb_vertical_grid_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_vertical_grid_return_transition</item>
     </style>
 
     <style name="Theme.Leanback.Details" parent="Theme.Leanback">
-        <item name="android:windowEnterTransition">@transition/lb_details_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_details_return_transition</item>
-        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
-        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
     </style>
 
     <!-- Theme for the details without shared element transition -->
     <style name="Theme.Leanback.Details.NoSharedElementTransition">
-        <item name="android:windowSharedElementEnterTransition">@null</item>
-        <item name="android:windowSharedElementReturnTransition">@null</item>
     </style>
 
     <style name="Theme.Leanback.GuidedStepBase" parent="Theme.LeanbackBase">
@@ -150,7 +129,6 @@
         <item name="guidedStepThemeFlag">true</item>
         <item name="guidedStepHeightWeight">@string/lb_guidedstep_height_weight</item>
 
-        <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter</item>
 
         <!-- background applied to each GuidedStepFragment by default-->
         <item name="guidedStepBackground">?android:attr/colorBackground</item>
@@ -158,7 +136,6 @@
              But We still need a dumb background to keep the temporary translucent state last
              as long as the background view fade-in transition -->
         <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowTransitionBackgroundFadeDuration">@integer/lb_guidedstep_activity_background_fade_duration_ms</item>
 
         <item name="guidedStepImeAppearingAnimation">@animator/lb_guidedstep_slide_up</item>
         <item name="guidedStepImeDisappearingAnimation">@animator/lb_guidedstep_slide_down</item>
@@ -198,8 +175,10 @@
         <item name="guidedStepKeyline">@string/lb_guidedstep_keyline</item>
     </style>
 
-    <style name="Theme.Leanback.GuidedStep.Half" parent="Theme.Leanback.GuidedStep">
-      <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter_bottom</item>
+    <style name="Theme.Leanback.GuidedStep.HalfBase" parent="Theme.Leanback.GuidedStep">
+    </style>
+
+    <style name="Theme.Leanback.GuidedStep.Half" parent="Theme.Leanback.GuidedStep.HalfBase">
       <item name="guidedStepHeightWeight">@string/lb_guidedstep_height_weight_translucent</item>
       <item name="android:windowIsTranslucent">true</item>
       <item name="android:windowBackground">@android:color/transparent</item>
@@ -213,6 +192,7 @@
         <item name="onboardingPageIndicatorStyle">@style/Widget.Leanback.OnboardingPageIndicatorStyle</item>
         <item name="onboardingStartButtonStyle">@style/Widget.Leanback.OnboardingStartButtonStyle</item>
         <item name="onboardingLogoStyle">@style/Widget.Leanback.OnboardingLogoStyle</item>
+        <item name="onboardingMainIconStyle">@style/Widget.Leanback.OnboardingMainIconStyle</item>
     </style>
 
 </resources>
diff --git a/v17/leanback/src/.readme b/v17/leanback/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v17/leanback/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 27bcd70..262a5a6 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -200,6 +200,7 @@
             invalidateSelf();
         }
 
+        @Override
         public ColorFilter getColorFilter() {
             return mState.mPaint.getColorFilter();
         }
@@ -245,9 +246,11 @@
         DrawableWrapper[] mWrapper;
         int mAlpha = FULL_ALPHA;
         boolean mSuspendInvalidation;
+        WeakReference<BackgroundManager> mManagerWeakReference;
 
-        public TranslucentLayerDrawable(Drawable[] drawables) {
+        TranslucentLayerDrawable(BackgroundManager manager, Drawable[] drawables) {
             super(drawables);
+            mManagerWeakReference = new WeakReference(manager);
             int count = drawables.length;
             mWrapper = new DrawableWrapper[count];
             for (int i = 0; i < count; i++) {
@@ -257,8 +260,14 @@
 
         @Override
         public void setAlpha(int alpha) {
-            mAlpha = alpha;
-            invalidateSelf();
+            if (mAlpha != alpha) {
+                mAlpha = alpha;
+                invalidateSelf();
+                BackgroundManager manager = mManagerWeakReference.get();
+                if (manager != null) {
+                    manager.postChangeRunnable();
+                }
+            }
         }
 
         void setWrapperAlpha(int wrapperIndex, int alpha) {
@@ -269,6 +278,7 @@
         }
 
         // Queried by system transitions
+        @Override
         public int getAlpha() {
             return mAlpha;
         }
@@ -385,7 +395,7 @@
         for (int i = 0; i < numChildren; i++) {
             drawables[i] = layerDrawable.getDrawable(i);
         }
-        TranslucentLayerDrawable result = new TranslucentLayerDrawable(drawables);
+        TranslucentLayerDrawable result = new TranslucentLayerDrawable(this, drawables);
         for (int i = 0; i < numChildren; i++) {
             result.setId(i, layerDrawable.getId(i));
         }
@@ -659,6 +669,11 @@
      */
     public void attachToView(View sceneRoot) {
         attachToViewInternal(sceneRoot);
+        // clear background to reduce overdraw since the View will act as background.
+        // Activity transition below O has ghost effect for null window background where we
+        // need set a transparent background to force redraw the whole window.
+        mContext.getWindow().getDecorView().setBackground(
+                Build.VERSION.SDK_INT >= 26 ? null : new ColorDrawable(Color.TRANSPARENT));
     }
 
     void attachToViewInternal(View sceneRoot) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
index b123b9c..7686c5c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
@@ -13,6 +13,7 @@
  */
 package android.support.v17.leanback.app;
 
+import android.annotation.SuppressLint;
 import android.os.Bundle;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.transition.TransitionListener;
@@ -24,10 +25,10 @@
 import android.view.ViewTreeObserver;
 
 /**
- * @hide
+ * Base class for leanback Fragments. This class is not intended to be subclassed by apps.
  */
 @SuppressWarnings("FragmentNotInstantiable")
-class BaseFragment extends BrandedFragment {
+public class BaseFragment extends BrandedFragment {
 
     /**
      * The start state for all
@@ -130,6 +131,7 @@
     Object mEntranceTransition;
     final ProgressBarManager mProgressBarManager = new ProgressBarManager();
 
+    @SuppressLint("ValidFragment")
     BaseFragment() {
     }
 
@@ -302,6 +304,7 @@
 
     /**
      * Returns the {@link ProgressBarManager}.
+     * @return The {@link ProgressBarManager}.
      */
     public final ProgressBarManager getProgressBarManager() {
         return mProgressBarManager;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
index d14dc74..5a83b47 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
@@ -124,7 +124,15 @@
     }
 
     void setAdapterAndSelection() {
-        mVerticalGridView.setAdapter(mBridgeAdapter);
+        if (mAdapter == null) {
+            // delay until ItemBridgeAdapter has wrappedAdapter. Once we assign ItemBridgeAdapter
+            // to RecyclerView, it will not be allowed to change "hasStableId" to true.
+            return;
+        }
+        if (mVerticalGridView.getAdapter() != mBridgeAdapter) {
+            // avoid extra layout if ItemBridgeAdapter was already set.
+            mVerticalGridView.setAdapter(mBridgeAdapter);
+        }
         // We don't set the selected position unless we've data in the adapter.
         boolean lateSelection = mBridgeAdapter.getItemCount() == 0 && mSelectedPosition >= 0;
         if (lateSelection) {
@@ -163,7 +171,8 @@
     }
 
     /**
-     * Sets the adapter for the fragment.
+     * Sets the adapter that represents a list of rows.
+     * @param rowsAdapter Adapter that represents list of rows.
      */
     public final void setAdapter(ObjectAdapter rowsAdapter) {
         mAdapter = rowsAdapter;
@@ -171,16 +180,18 @@
     }
 
     /**
-     * Returns the list of rows.
+     * Returns the Adapter that represents list of rows.
+     * @return Adapter that represents list of rows.
      */
     public final ObjectAdapter getAdapter() {
         return mAdapter;
     }
 
     /**
-     * Returns the bridge adapter.
+     * Returns the RecyclerView.Adapter that wraps {@link #getAdapter()}.
+     * @return The RecyclerView.Adapter that wraps {@link #getAdapter()}.
      */
-    final ItemBridgeAdapter getBridgeAdapter() {
+    public final ItemBridgeAdapter getBridgeAdapter() {
         return mBridgeAdapter;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java
index d36f68f..bf49295 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java
@@ -127,7 +127,15 @@
     }
 
     void setAdapterAndSelection() {
-        mVerticalGridView.setAdapter(mBridgeAdapter);
+        if (mAdapter == null) {
+            // delay until ItemBridgeAdapter has wrappedAdapter. Once we assign ItemBridgeAdapter
+            // to RecyclerView, it will not be allowed to change "hasStableId" to true.
+            return;
+        }
+        if (mVerticalGridView.getAdapter() != mBridgeAdapter) {
+            // avoid extra layout if ItemBridgeAdapter was already set.
+            mVerticalGridView.setAdapter(mBridgeAdapter);
+        }
         // We don't set the selected position unless we've data in the adapter.
         boolean lateSelection = mBridgeAdapter.getItemCount() == 0 && mSelectedPosition >= 0;
         if (lateSelection) {
@@ -166,7 +174,8 @@
     }
 
     /**
-     * Sets the adapter for the fragment.
+     * Sets the adapter that represents a list of rows.
+     * @param rowsAdapter Adapter that represents list of rows.
      */
     public final void setAdapter(ObjectAdapter rowsAdapter) {
         mAdapter = rowsAdapter;
@@ -174,16 +183,18 @@
     }
 
     /**
-     * Returns the list of rows.
+     * Returns the Adapter that represents list of rows.
+     * @return Adapter that represents list of rows.
      */
     public final ObjectAdapter getAdapter() {
         return mAdapter;
     }
 
     /**
-     * Returns the bridge adapter.
+     * Returns the RecyclerView.Adapter that wraps {@link #getAdapter()}.
+     * @return The RecyclerView.Adapter that wraps {@link #getAdapter()}.
      */
-    final ItemBridgeAdapter getBridgeAdapter() {
+    public final ItemBridgeAdapter getBridgeAdapter() {
         return mBridgeAdapter;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
index 0db81b3..213ed83 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
@@ -16,6 +16,7 @@
  */
 package android.support.v17.leanback.app;
 
+import android.annotation.SuppressLint;
 import android.os.Bundle;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.transition.TransitionListener;
@@ -27,10 +28,10 @@
 import android.view.ViewTreeObserver;
 
 /**
- * @hide
+ * Base class for leanback Fragments. This class is not intended to be subclassed by apps.
  */
 @SuppressWarnings("FragmentNotInstantiable")
-class BaseSupportFragment extends BrandedSupportFragment {
+public class BaseSupportFragment extends BrandedSupportFragment {
 
     /**
      * The start state for all
@@ -133,6 +134,7 @@
     Object mEntranceTransition;
     final ProgressBarManager mProgressBarManager = new ProgressBarManager();
 
+    @SuppressLint("ValidFragment")
     BaseSupportFragment() {
     }
 
@@ -305,6 +307,7 @@
 
     /**
      * Returns the {@link ProgressBarManager}.
+     * @return The {@link ProgressBarManager}.
      */
     public final ProgressBarManager getProgressBarManager() {
         return mProgressBarManager;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index dc40239..fd930ee 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -552,7 +552,9 @@
 
     private boolean createMainFragment(ObjectAdapter adapter, int position) {
         Object item = null;
-        if (adapter == null || adapter.size() == 0) {
+        if (!mCanShowHeaders) {
+            // when header is disabled, we can decide to use RowsFragment even no data.
+        } else if (adapter == null || adapter.size() == 0) {
             return false;
         } else {
             if (position < 0) {
@@ -565,7 +567,7 @@
         }
 
         boolean oldIsPageRow = mIsPageRow;
-        mIsPageRow = item instanceof PageRow;
+        mIsPageRow = mCanShowHeaders && item instanceof PageRow;
         boolean swap;
 
         if (mMainFragment == null) {
@@ -631,7 +633,7 @@
      * against {@link PageRow}.
      */
     public final static class MainFragmentAdapterRegistry {
-        private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap();
+        private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap<>();
         private final static FragmentFactory sDefaultFragmentFactory = new ListRowFragmentFactory();
 
         public MainFragmentAdapterRegistry() {
@@ -643,11 +645,8 @@
         }
 
         public Fragment createFragment(Object item) {
-            if (item == null) {
-                throw new IllegalArgumentException("Item can't be null");
-            }
-
-            FragmentFactory fragmentFactory = mItemToFragmentFactoryMapping.get(item.getClass());
+            FragmentFactory fragmentFactory = item == null ? sDefaultFragmentFactory :
+                    mItemToFragmentFactoryMapping.get(item.getClass());
             if (fragmentFactory == null && !(item instanceof PageRow)) {
                 fragmentFactory = sDefaultFragmentFactory;
             }
@@ -1027,7 +1026,8 @@
                         ? mHeadersFragment.getVerticalGridView() : mMainFragment.getView();
             }
 
-            boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
+            boolean isRtl = ViewCompat.getLayoutDirection(focused)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
             int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
             int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mCanShowHeaders && direction == towardStart) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index 21ce544..094fef9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -555,7 +555,9 @@
 
     private boolean createMainFragment(ObjectAdapter adapter, int position) {
         Object item = null;
-        if (adapter == null || adapter.size() == 0) {
+        if (!mCanShowHeaders) {
+            // when header is disabled, we can decide to use RowsSupportFragment even no data.
+        } else if (adapter == null || adapter.size() == 0) {
             return false;
         } else {
             if (position < 0) {
@@ -568,7 +570,7 @@
         }
 
         boolean oldIsPageRow = mIsPageRow;
-        mIsPageRow = item instanceof PageRow;
+        mIsPageRow = mCanShowHeaders && item instanceof PageRow;
         boolean swap;
 
         if (mMainFragment == null) {
@@ -634,7 +636,7 @@
      * against {@link PageRow}.
      */
     public final static class MainFragmentAdapterRegistry {
-        private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap();
+        private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap<>();
         private final static FragmentFactory sDefaultFragmentFactory = new ListRowFragmentFactory();
 
         public MainFragmentAdapterRegistry() {
@@ -646,11 +648,8 @@
         }
 
         public Fragment createFragment(Object item) {
-            if (item == null) {
-                throw new IllegalArgumentException("Item can't be null");
-            }
-
-            FragmentFactory fragmentFactory = mItemToFragmentFactoryMapping.get(item.getClass());
+            FragmentFactory fragmentFactory = item == null ? sDefaultFragmentFactory :
+                    mItemToFragmentFactoryMapping.get(item.getClass());
             if (fragmentFactory == null && !(item instanceof PageRow)) {
                 fragmentFactory = sDefaultFragmentFactory;
             }
@@ -1030,7 +1029,8 @@
                         ? mHeadersSupportFragment.getVerticalGridView() : mMainFragment.getView();
             }
 
-            boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
+            boolean isRtl = ViewCompat.getLayoutDirection(focused)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
             int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
             int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mCanShowHeaders && direction == towardStart) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
index 5bae3d0..48bf74c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
@@ -125,10 +125,10 @@
         switch (mCurrentState) {
             case PLAY_VIDEO:
                 if (mPlaybackGlue != null) {
-                    if (mPlaybackGlue.isReadyForPlayback()) {
+                    if (mPlaybackGlue.isPrepared()) {
                         internalStartPlayback();
                     } else {
-                        mPlaybackGlue.setPlayerCallback(mControlStateCallback);
+                        mPlaybackGlue.addPlayerCallback(mControlStateCallback);
                     }
                 } else {
                     crossFadeBackgroundToVideo(false);
@@ -137,7 +137,7 @@
             case NO_VIDEO:
                 crossFadeBackgroundToVideo(false);
                 if (mPlaybackGlue != null) {
-                    mPlaybackGlue.setPlayerCallback(null);
+                    mPlaybackGlue.removePlayerCallback(mControlStateCallback);
                     mPlaybackGlue.pause();
                 }
                 break;
@@ -146,7 +146,7 @@
 
     void setPlaybackGlue(PlaybackGlue playbackGlue) {
         if (mPlaybackGlue != null) {
-            mPlaybackGlue.setPlayerCallback(null);
+            mPlaybackGlue.removePlayerCallback(mControlStateCallback);
         }
         mPlaybackGlue = playbackGlue;
         applyState();
@@ -234,8 +234,10 @@
     private class PlaybackControlStateCallback extends PlaybackGlue.PlayerCallback {
 
         @Override
-        public void onReadyForPlayback() {
-            internalStartPlayback();
+        public void onPreparedStateChanged(PlaybackGlue glue) {
+            if (glue.isPrepared()) {
+                internalStartPlayback();
+            }
         }
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
index 57a85a0..2c4e24a 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -102,7 +102,7 @@
     void switchToVideoBeforeVideoFragmentCreated() {
         // if the video fragment is not ready: immediately fade out covering drawable,
         // hide title and mark mPendingFocusOnVideo and set focus on it later.
-        mDetailsBackgroundController.crossFadeBackgroundToVideo(true, true);
+        mDetailsBackgroundController.switchToVideoBeforeCreate();
         showTitle(false);
         mPendingFocusOnVideo = true;
         slideOutGridView();
@@ -168,7 +168,7 @@
         final WeakReference<DetailsFragment> mRef;
 
         WaitEnterTransitionTimeout(DetailsFragment f) {
-            mRef = new WeakReference(f);
+            mRef = new WeakReference<>(f);
             f.getView().postDelayed(this, WAIT_ENTERTRANSITION_START);
         }
 
@@ -606,6 +606,9 @@
      * @see DetailsFragmentBackgroundController#onCreateVideoFragment()
      */
     final Fragment findOrCreateVideoFragment() {
+        if (mVideoFragment != null) {
+            return mVideoFragment;
+        }
         Fragment fragment = getChildFragmentManager()
                 .findFragmentById(R.id.video_surface_container);
         if (fragment == null && mDetailsBackgroundController != null) {
@@ -617,6 +620,7 @@
                 // wait next cycle for Fragment view created so we can focus on it.
                 // This is a bit hack eventually we will do commitNow() which get view immediately.
                 getView().post(new Runnable() {
+                    @Override
                     public void run() {
                         if (getView() != null) {
                             switchToVideo();
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
index f6f389c..05dfb3a 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
@@ -115,6 +115,9 @@
     Bitmap mCoverBitmap;
     int mSolidColor;
     boolean mCanUseHost = false;
+    boolean mInitialControlVisible = false;
+
+    private Fragment mLastVideoFragmentForGlueHost;
 
     /**
      * Creates a DetailsFragmentBackgroundController for a DetailsFragment. Note that
@@ -223,17 +226,28 @@
      * @see #onCreateVideoFragment()
      * @see #onCreateGlueHost().
      */
+    @SuppressWarnings("ReferenceEquality")
     public void setupVideoPlayback(@NonNull PlaybackGlue playbackGlue) {
         if (mPlaybackGlue == playbackGlue) {
             return;
         }
+
+        PlaybackGlueHost playbackGlueHost = null;
         if (mPlaybackGlue != null) {
+            playbackGlueHost = mPlaybackGlue.getHost();
             mPlaybackGlue.setHost(null);
         }
+
         mPlaybackGlue = playbackGlue;
         mVideoHelper.setPlaybackGlue(mPlaybackGlue);
         if (mCanUseHost && mPlaybackGlue != null) {
-            mPlaybackGlue.setHost(onCreateGlueHost());
+            if (playbackGlueHost == null
+                    || mLastVideoFragmentForGlueHost != findOrCreateVideoFragment()) {
+                mPlaybackGlue.setHost(createGlueHost());
+                mLastVideoFragmentForGlueHost = findOrCreateVideoFragment();
+            } else {
+                mPlaybackGlue.setHost(playbackGlueHost);
+            }
         }
     }
 
@@ -249,7 +263,7 @@
     /**
      * Precondition allows user navigate to video fragment using DPAD. Default implementation
      * returns true if PlaybackGlue is not null. Subclass may override, e.g. only allow navigation
-     * when {@link PlaybackGlue#isReadyForPlayback()} is true. Note this method does not block
+     * when {@link PlaybackGlue#isPrepared()} is true. Note this method does not block
      * app calls {@link #switchToVideo}.
      *
      * @return True allow to navigate to video fragment.
@@ -258,8 +272,9 @@
         return mPlaybackGlue != null;
     }
 
-    void crossFadeBackgroundToVideo(boolean fadeToBackground, boolean immediate) {
-        mVideoHelper.crossFadeBackgroundToVideo(fadeToBackground, immediate);
+    void switchToVideoBeforeCreate() {
+        mVideoHelper.crossFadeBackgroundToVideo(true, true);
+        mInitialControlVisible = true;
     }
 
     /**
@@ -298,10 +313,11 @@
         if (!mCanUseHost) {
             mCanUseHost = true;
             if (mPlaybackGlue != null) {
-                mPlaybackGlue.setHost(onCreateGlueHost());
+                mPlaybackGlue.setHost(createGlueHost());
+                mLastVideoFragmentForGlueHost = findOrCreateVideoFragment();
             }
         }
-        if (mPlaybackGlue != null && mPlaybackGlue.isReadyForPlayback()) {
+        if (mPlaybackGlue != null && mPlaybackGlue.isPrepared()) {
             mPlaybackGlue.play();
         }
     }
@@ -378,6 +394,16 @@
         return new VideoFragmentGlueHost((VideoFragment) findOrCreateVideoFragment());
     }
 
+    PlaybackGlueHost createGlueHost() {
+        PlaybackGlueHost host = onCreateGlueHost();
+        if (mInitialControlVisible) {
+            host.showControlsOverlay(false);
+        } else {
+            host.hideControlsOverlay(false);
+        }
+        return host;
+    }
+
     /**
      * Adds or gets fragment for rendering video in DetailsFragment. A subclass that
      * overrides {@link #onCreateGlueHost()} should call this method to get a fragment for creating
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
index 1a74ce1..1f0c259 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
@@ -105,7 +105,7 @@
     void switchToVideoBeforeVideoSupportFragmentCreated() {
         // if the video fragment is not ready: immediately fade out covering drawable,
         // hide title and mark mPendingFocusOnVideo and set focus on it later.
-        mDetailsBackgroundController.crossFadeBackgroundToVideo(true, true);
+        mDetailsBackgroundController.switchToVideoBeforeCreate();
         showTitle(false);
         mPendingFocusOnVideo = true;
         slideOutGridView();
@@ -171,7 +171,7 @@
         final WeakReference<DetailsSupportFragment> mRef;
 
         WaitEnterTransitionTimeout(DetailsSupportFragment f) {
-            mRef = new WeakReference(f);
+            mRef = new WeakReference<>(f);
             f.getView().postDelayed(this, WAIT_ENTERTRANSITION_START);
         }
 
@@ -609,6 +609,9 @@
      * @see DetailsSupportFragmentBackgroundController#onCreateVideoSupportFragment()
      */
     final Fragment findOrCreateVideoSupportFragment() {
+        if (mVideoSupportFragment != null) {
+            return mVideoSupportFragment;
+        }
         Fragment fragment = getChildFragmentManager()
                 .findFragmentById(R.id.video_surface_container);
         if (fragment == null && mDetailsBackgroundController != null) {
@@ -620,6 +623,7 @@
                 // wait next cycle for Fragment view created so we can focus on it.
                 // This is a bit hack eventually we will do commitNow() which get view immediately.
                 getView().post(new Runnable() {
+                    @Override
                     public void run() {
                         if (getView() != null) {
                             switchToVideo();
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java
index 763b84f..4a0be6e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java
@@ -118,6 +118,9 @@
     Bitmap mCoverBitmap;
     int mSolidColor;
     boolean mCanUseHost = false;
+    boolean mInitialControlVisible = false;
+
+    private Fragment mLastVideoSupportFragmentForGlueHost;
 
     /**
      * Creates a DetailsSupportFragmentBackgroundController for a DetailsSupportFragment. Note that
@@ -226,17 +229,28 @@
      * @see #onCreateVideoSupportFragment()
      * @see #onCreateGlueHost().
      */
+    @SuppressWarnings("ReferenceEquality")
     public void setupVideoPlayback(@NonNull PlaybackGlue playbackGlue) {
         if (mPlaybackGlue == playbackGlue) {
             return;
         }
+
+        PlaybackGlueHost playbackGlueHost = null;
         if (mPlaybackGlue != null) {
+            playbackGlueHost = mPlaybackGlue.getHost();
             mPlaybackGlue.setHost(null);
         }
+
         mPlaybackGlue = playbackGlue;
         mVideoHelper.setPlaybackGlue(mPlaybackGlue);
         if (mCanUseHost && mPlaybackGlue != null) {
-            mPlaybackGlue.setHost(onCreateGlueHost());
+            if (playbackGlueHost == null
+                    || mLastVideoSupportFragmentForGlueHost != findOrCreateVideoSupportFragment()) {
+                mPlaybackGlue.setHost(createGlueHost());
+                mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment();
+            } else {
+                mPlaybackGlue.setHost(playbackGlueHost);
+            }
         }
     }
 
@@ -252,7 +266,7 @@
     /**
      * Precondition allows user navigate to video fragment using DPAD. Default implementation
      * returns true if PlaybackGlue is not null. Subclass may override, e.g. only allow navigation
-     * when {@link PlaybackGlue#isReadyForPlayback()} is true. Note this method does not block
+     * when {@link PlaybackGlue#isPrepared()} is true. Note this method does not block
      * app calls {@link #switchToVideo}.
      *
      * @return True allow to navigate to video fragment.
@@ -261,8 +275,9 @@
         return mPlaybackGlue != null;
     }
 
-    void crossFadeBackgroundToVideo(boolean fadeToBackground, boolean immediate) {
-        mVideoHelper.crossFadeBackgroundToVideo(fadeToBackground, immediate);
+    void switchToVideoBeforeCreate() {
+        mVideoHelper.crossFadeBackgroundToVideo(true, true);
+        mInitialControlVisible = true;
     }
 
     /**
@@ -301,10 +316,11 @@
         if (!mCanUseHost) {
             mCanUseHost = true;
             if (mPlaybackGlue != null) {
-                mPlaybackGlue.setHost(onCreateGlueHost());
+                mPlaybackGlue.setHost(createGlueHost());
+                mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment();
             }
         }
-        if (mPlaybackGlue != null && mPlaybackGlue.isReadyForPlayback()) {
+        if (mPlaybackGlue != null && mPlaybackGlue.isPrepared()) {
             mPlaybackGlue.play();
         }
     }
@@ -381,6 +397,16 @@
         return new VideoSupportFragmentGlueHost((VideoSupportFragment) findOrCreateVideoSupportFragment());
     }
 
+    PlaybackGlueHost createGlueHost() {
+        PlaybackGlueHost host = onCreateGlueHost();
+        if (mInitialControlVisible) {
+            host.showControlsOverlay(false);
+        } else {
+            host.hideControlsOverlay(false);
+        }
+        return host;
+    }
+
     /**
      * Adds or gets fragment for rendering video in DetailsSupportFragment. A subclass that
      * overrides {@link #onCreateGlueHost()} should call this method to get a fragment for creating
diff --git a/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java b/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java
index 23c6039..b555698 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java
@@ -15,14 +15,14 @@
  */
 package android.support.v17.leanback.app;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.app.Fragment;
 import android.content.Context;
 import android.os.Build;
 
 class FragmentUtil {
 
-    @TargetApi(23)
+    @RequiresApi(23)
     private static Context getContextNew(Fragment fragment) {
         return fragment.getContext();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index bab48bd..a01cf26 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -35,6 +35,7 @@
 import android.support.v17.leanback.widget.GuidedActionAdapter;
 import android.support.v17.leanback.widget.GuidedActionAdapterGroup;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
+import android.support.v17.leanback.widget.NonOverlappingLinearLayout;
 import android.support.v4.app.ActivityCompat;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
@@ -233,7 +234,7 @@
     @RestrictTo(LIBRARY_GROUP)
     public static final int SLIDE_FROM_BOTTOM = 1;
 
-    private static final String TAG = "GuidedStepFragment";
+    private static final String TAG = "GuidedStepF";
     private static final boolean DEBUG = false;
 
     /**
@@ -1045,6 +1046,7 @@
 
         ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment);
         ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment);
+        ((NonOverlappingLinearLayout) actionContainer).setFocusableViewAvailableFixEnabled(true);
 
         Guidance guidance = onCreateGuidance(savedInstanceState);
         View guidanceView = mGuidanceStylist.onCreateView(inflater, guidanceContainer, guidance);
@@ -1324,6 +1326,15 @@
       this.entranceTransitionType = transitionType;
     }
 
+    /**
+     * Opens the provided action in edit mode and raises ime. This can be
+     * used to programmatically skip the extra click required to go into edit mode. This method
+     * can be invoked in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+     */
+    public void openInEditMode(GuidedAction action) {
+        mActionsStylist.openInEditMode(action);
+    }
+
     private void resolveTheme() {
         // Look up the guidedStepTheme in the currently specified theme.  If it exists,
         // replace the theme with its value.
@@ -1386,5 +1397,4 @@
         set.playTogether(animators);
         set.start();
     }
-
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index f68366c..ed34548 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -38,6 +38,7 @@
 import android.support.v17.leanback.widget.GuidedActionAdapter;
 import android.support.v17.leanback.widget.GuidedActionAdapterGroup;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
+import android.support.v17.leanback.widget.NonOverlappingLinearLayout;
 import android.support.v4.app.ActivityCompat;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
@@ -236,7 +237,7 @@
     @RestrictTo(LIBRARY_GROUP)
     public static final int SLIDE_FROM_BOTTOM = 1;
 
-    private static final String TAG = "GuidedStepSupportFragment";
+    private static final String TAG = "GuidedStepF";
     private static final boolean DEBUG = false;
 
     /**
@@ -1048,6 +1049,7 @@
 
         ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment);
         ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment);
+        ((NonOverlappingLinearLayout) actionContainer).setFocusableViewAvailableFixEnabled(true);
 
         Guidance guidance = onCreateGuidance(savedInstanceState);
         View guidanceView = mGuidanceStylist.onCreateView(inflater, guidanceContainer, guidance);
@@ -1327,6 +1329,15 @@
       this.entranceTransitionType = transitionType;
     }
 
+    /**
+     * Opens the provided action in edit mode and raises ime. This can be
+     * used to programmatically skip the extra click required to go into edit mode. This method
+     * can be invoked in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+     */
+    public void openInEditMode(GuidedAction action) {
+        mActionsStylist.openInEditMode(action);
+    }
+
     private void resolveTheme() {
         // Look up the guidedStepTheme in the currently specified theme.  If it exists,
         // replace the theme with its value.
@@ -1389,5 +1400,4 @@
         set.playTogether(animators);
         set.start();
     }
-
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
index 417a2b6..724fa41 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
@@ -91,6 +91,7 @@
 
     public HeadersFragment() {
         setPresenterSelector(sHeaderPresenter);
+        FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter());
     }
 
     public void setOnHeaderClickedListener(OnHeaderClickedListener listener) {
@@ -165,7 +166,6 @@
         if (listView == null) {
             return;
         }
-        FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
         if (mBackgroundColorSet) {
             listView.setBackgroundColor(mBackgroundColor);
             updateFadingEdgeToBrandColor(mBackgroundColor);
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
index 8112a8a..be867cb 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
@@ -94,6 +94,7 @@
 
     public HeadersSupportFragment() {
         setPresenterSelector(sHeaderPresenter);
+        FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter());
     }
 
     public void setOnHeaderClickedListener(OnHeaderClickedListener listener) {
@@ -168,7 +169,6 @@
         if (listView == null) {
             return;
         }
-        FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
         if (mBackgroundColorSet) {
             listView.setBackgroundColor(mBackgroundColor);
             updateFadingEdgeToBrandColor(mBackgroundColor);
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
index df83ae9..22dd211 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
@@ -24,7 +24,9 @@
 import android.animation.TimeInterpolator;
 import android.app.Fragment;
 import android.content.Context;
+import android.graphics.Color;
 import android.os.Bundle;
+import android.support.annotation.ColorInt;
 import android.support.annotation.Nullable;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.PagingIndicator;
@@ -41,6 +43,7 @@
 import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -53,8 +56,8 @@
  * <p>
  * <h3>Building the screen</h3>
  * The view structure of onboarding screen is composed of the common parts and custom parts. The
- * common parts are composed of title, description and page navigator and the custom parts are
- * composed of background, contents and foreground.
+ * common parts are composed of icon, title, description and page navigator and the custom parts
+ * are composed of background, contents and foreground.
  * <p>
  * To build the screen views, the inherited class should override:
  * <ul>
@@ -102,8 +105,12 @@
  * If the inherited class provides neither the logo image nor the animation, the logo animation will
  * be omitted.
  * <h4>Page enter animation</h4>
- * After logo animation finishes, page enter animation starts. The application can provide the
- * animations of custom views by overriding {@link #onCreateEnterAnimation}.
+ * After logo animation finishes, page enter animation starts, which causes the header section -
+ * title and description views to fade and slide in. Users can override the default
+ * fade + slide animation by overriding {@link #onCreateTitleAnimator()} &
+ * {@link #onCreateDescriptionAnimator()}. By default we don't animate the custom views but users
+ * can provide animation by overriding {@link #onCreateEnterAnimation}.
+ *
  * <h4>Page change animation</h4>
  * When the page changes, the default animations of the title and description are played. The
  * inherited class can override {@link #onPageChanged} to start the custom animations.
@@ -145,12 +152,10 @@
  * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
  */
 abstract public class OnboardingFragment extends Fragment {
-    private static final String TAG = "OnboardingFragment";
+    private static final String TAG = "OnboardingF";
     private static final boolean DEBUG = false;
 
     private static final long LOGO_SPLASH_PAUSE_DURATION_MS = 1333;
-    private static final long START_DELAY_TITLE_MS = 33;
-    private static final long START_DELAY_DESCRIPTION_MS = 33;
 
     private static final long HEADER_ANIMATION_DURATION_MS = 417;
     private static final long DESCRIPTION_START_DELAY_MS = 33;
@@ -165,12 +170,20 @@
 
     // Keys used to save and restore the states.
     private static final String KEY_CURRENT_PAGE_INDEX = "leanback.onboarding.current_page_index";
+    private static final String KEY_LOGO_ANIMATION_FINISHED =
+            "leanback.onboarding.logo_animation_finished";
+    private static final String KEY_ENTER_ANIMATION_FINISHED =
+            "leanback.onboarding.enter_animation_finished";
 
     private ContextThemeWrapper mThemeWrapper;
 
     PagingIndicator mPageIndicator;
     View mStartButton;
     private ImageView mLogoView;
+    // Optional icon that can be displayed on top of the header section.
+    private ImageView mMainIconView;
+    private int mIconResourceId;
+
     TextView mTitleView;
     TextView mDescriptionView;
 
@@ -180,8 +193,33 @@
     // the fragment is restored.
     private int mLogoResourceId;
     boolean mLogoAnimationFinished;
+    boolean mEnterAnimationFinished;
     int mCurrentPageIndex;
 
+    @ColorInt
+    private int mTitleViewTextColor = Color.TRANSPARENT;
+    private boolean mTitleViewTextColorSet;
+
+    @ColorInt
+    private int mDescriptionViewTextColor = Color.TRANSPARENT;
+    private boolean mDescriptionViewTextColorSet;
+
+    @ColorInt
+    private int mDotBackgroundColor = Color.TRANSPARENT;
+    private boolean mDotBackgroundColorSet;
+
+    @ColorInt
+    private int mArrowColor = Color.TRANSPARENT;
+    private boolean mArrowColorSet;
+
+    @ColorInt
+    private int mArrowBackgroundColor = Color.TRANSPARENT;
+    private boolean mArrowBackgroundColorSet;
+
+    private CharSequence mStartButtonText;
+    private boolean mStartButtonTextSet;
+
+
     private AnimatorSet mAnimator;
 
     private final OnClickListener mOnClickListener = new OnClickListener() {
@@ -279,41 +317,213 @@
         mStartButton = view.findViewById(R.id.button_start);
         mStartButton.setOnClickListener(mOnClickListener);
         mStartButton.setOnKeyListener(mOnKeyListener);
+        mMainIconView = (ImageView) view.findViewById(R.id.main_icon);
         mLogoView = (ImageView) view.findViewById(R.id.logo);
         mTitleView = (TextView) view.findViewById(R.id.title);
         mDescriptionView = (TextView) view.findViewById(R.id.description);
+
+        if (mTitleViewTextColorSet) {
+            mTitleView.setTextColor(mTitleViewTextColor);
+        }
+        if (mDescriptionViewTextColorSet) {
+            mDescriptionView.setTextColor(mDescriptionViewTextColor);
+        }
+        if (mDotBackgroundColorSet) {
+            mPageIndicator.setDotBackgroundColor(mDotBackgroundColor);
+        }
+        if (mArrowColorSet) {
+            mPageIndicator.setArrowColor(mArrowColor);
+        }
+        if (mArrowBackgroundColorSet) {
+            mPageIndicator.setDotBackgroundColor(mArrowBackgroundColor);
+        }
+        if (mStartButtonTextSet) {
+            ((Button) mStartButton).setText(mStartButtonText);
+        }
         final Context context = FragmentUtil.getContext(this);
         if (sSlideDistance == 0) {
             sSlideDistance = (int) (SLIDE_DISTANCE * context.getResources()
                     .getDisplayMetrics().scaledDensity);
         }
-        if (savedInstanceState == null) {
-            mCurrentPageIndex = 0;
-            mLogoAnimationFinished = false;
-            mPageIndicator.onPageSelected(0, false);
-            view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    view.getViewTreeObserver().removeOnPreDrawListener(this);
-                    if (!startLogoAnimation()) {
-                        startEnterAnimation();
-                    }
-                    return true;
-                }
-            });
-        } else {
-            mLogoAnimationFinished = true;
-            mCurrentPageIndex = savedInstanceState.getInt(KEY_CURRENT_PAGE_INDEX);
-            initializeViews(view);
-        }
         view.requestFocus();
         return view;
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        if (savedInstanceState == null) {
+            mCurrentPageIndex = 0;
+            mLogoAnimationFinished = false;
+            mEnterAnimationFinished = false;
+            mPageIndicator.onPageSelected(0, false);
+            view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    getView().getViewTreeObserver().removeOnPreDrawListener(this);
+                    if (!startLogoAnimation()) {
+                        mLogoAnimationFinished = true;
+                        onLogoAnimationFinished();
+                    }
+                    return true;
+                }
+            });
+        } else {
+            mCurrentPageIndex = savedInstanceState.getInt(KEY_CURRENT_PAGE_INDEX);
+            mLogoAnimationFinished = savedInstanceState.getBoolean(KEY_LOGO_ANIMATION_FINISHED);
+            mEnterAnimationFinished = savedInstanceState.getBoolean(KEY_ENTER_ANIMATION_FINISHED);
+            if (!mLogoAnimationFinished) {
+                // logo animation wasn't started or was interrupted when the activity was destroyed;
+                // restart it againl
+                if (!startLogoAnimation()) {
+                    mLogoAnimationFinished = true;
+                    onLogoAnimationFinished();
+                }
+            } else {
+                onLogoAnimationFinished();
+            }
+        }
+    }
+
+    @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putInt(KEY_CURRENT_PAGE_INDEX, mCurrentPageIndex);
+        outState.putBoolean(KEY_LOGO_ANIMATION_FINISHED, mLogoAnimationFinished);
+        outState.putBoolean(KEY_ENTER_ANIMATION_FINISHED, mEnterAnimationFinished);
+    }
+
+    /**
+     * Sets the text color for TitleView. If not set, the default textColor set in style
+     * referenced by attr {@link R.attr#onboardingTitleStyle} will be used.
+     * @param color the color to use as the text color for TitleView
+     */
+    public void setTitleViewTextColor(@ColorInt int color) {
+        mTitleViewTextColor = color;
+        mTitleViewTextColorSet = true;
+        if (mTitleView != null) {
+            mTitleView.setTextColor(color);
+        }
+    }
+
+    /**
+     * Returns the text color of TitleView if it's set through
+     * {@link #setTitleViewTextColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getTitleViewTextColor() {
+        return mTitleViewTextColor;
+    }
+
+    /**
+     * Sets the text color for DescriptionView. If not set, the default textColor set in style
+     * referenced by attr {@link R.attr#onboardingDescriptionStyle} will be used.
+     * @param color the color to use as the text color for DescriptionView
+     */
+    public void setDescriptionViewTextColor(@ColorInt int color) {
+        mDescriptionViewTextColor = color;
+        mDescriptionViewTextColorSet = true;
+        if (mDescriptionView != null) {
+            mDescriptionView.setTextColor(color);
+        }
+    }
+
+    /**
+     * Returns the text color of DescriptionView if it's set through
+     * {@link #setDescriptionViewTextColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getDescriptionViewTextColor() {
+        return mDescriptionViewTextColor;
+    }
+    /**
+     * Sets the background color of the dots. If not set, the default color from attr
+     * {@link R.styleable#PagingIndicator_dotBgColor} in the theme will be used.
+     * @param color the color to use for dot backgrounds
+     */
+    public void setDotBackgroundColor(@ColorInt int color) {
+        mDotBackgroundColor = color;
+        mDotBackgroundColorSet = true;
+        if (mPageIndicator != null) {
+            mPageIndicator.setDotBackgroundColor(color);
+        }
+    }
+
+    /**
+     * Returns the background color of the dot if it's set through
+     * {@link #setDotBackgroundColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getDotBackgroundColor() {
+        return mDotBackgroundColor;
+    }
+
+    /**
+     * Sets the color of the arrow. This color will supersede the color set in the theme attribute
+     * {@link R.styleable#PagingIndicator_arrowColor} if provided. If none of these two are set, the
+     * arrow will have its original bitmap color.
+     *
+     * @param color the color to use for arrow background
+     */
+    public void setArrowColor(@ColorInt int color) {
+        mArrowColor = color;
+        mArrowColorSet = true;
+        if (mPageIndicator != null) {
+            mPageIndicator.setArrowColor(color);
+        }
+    }
+
+    /**
+     * Returns the color of the arrow if it's set through
+     * {@link #setArrowColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getArrowColor() {
+        return mArrowColor;
+    }
+
+    /**
+     * Sets the background color of the arrow. If not set, the default color from attr
+     * {@link R.styleable#PagingIndicator_arrowBgColor} in the theme will be used.
+     * @param color the color to use for arrow background
+     */
+    public void setArrowBackgroundColor(@ColorInt int color) {
+        mArrowBackgroundColor = color;
+        mArrowBackgroundColorSet = true;
+        if (mPageIndicator != null) {
+            mPageIndicator.setArrowBackgroundColor(color);
+        }
+    }
+
+    /**
+     * Returns the background color of the arrow if it's set through
+     * {@link #setArrowBackgroundColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getArrowBackgroundColor() {
+        return mArrowBackgroundColor;
+    }
+
+    /**
+     * Returns the start button text if it's set through
+     * {@link #setStartButtonText(CharSequence)}}. If no string was set, null is returned.
+     */
+    public final CharSequence getStartButtonText() {
+        return mStartButtonText;
+    }
+
+    /**
+     * Sets the text on the start button text. If not set, the default text set in
+     * {@link R.styleable#LeanbackOnboardingTheme_onboardingStartButtonStyle} will be used.
+     *
+     * @param text the start button text
+     */
+    public void setStartButtonText(CharSequence text) {
+        mStartButtonText = text;
+        mStartButtonTextSet = true;
+        if (mStartButton != null) {
+            ((Button) mStartButton).setText(mStartButtonText);
+        }
     }
 
     /**
@@ -383,6 +593,9 @@
 
     boolean startLogoAnimation() {
         final Context context = FragmentUtil.getContext(this);
+        if (context == null) {
+            return false;
+        }
         Animator animator = null;
         if (mLogoResourceId != 0) {
             mLogoView.setVisibility(View.VISIBLE);
@@ -404,7 +617,8 @@
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     if (context != null) {
-                        startEnterAnimation();
+                        mLogoAnimationFinished = true;
+                        onLogoAnimationFinished();
                     }
                 }
             });
@@ -425,8 +639,20 @@
         return null;
     }
 
-    private void initializeViews(View container) {
+
+    /**
+     * Hides the logo view and makes other fragment views visible. Also initializes the texts for
+     * Title and Description views.
+     */
+    void hideLogoView() {
         mLogoView.setVisibility(View.GONE);
+
+        if (mIconResourceId != 0) {
+            mMainIconView.setImageResource(mIconResourceId);
+            mMainIconView.setVisibility(View.VISIBLE);
+        }
+
+        View container = getView();
         // Create custom views.
         LayoutInflater inflater = getThemeInflater(LayoutInflater.from(
                 FragmentUtil.getContext(this)));
@@ -465,56 +691,102 @@
         // Header views.
         mTitleView.setText(getPageTitle(mCurrentPageIndex));
         mDescriptionView.setText(getPageDescription(mCurrentPageIndex));
-        onLogoAnimationFinished();
     }
 
     /**
-     * Called immediately after fragment views become visible. This method gives subclasses a chance
-     * to initialize themselves. If a logo animation is specified, calling this method is delayed
-     * until after the logo animation is complete.
+     * Called immediately after the logo animation is complete or no logo animation is specified.
+     * This method can also be called when the activity is recreated, i.e. when no logo animation
+     * are performed.
+     * By default, this method will hide the logo view and start the entrance animation for this
+     * fragment.
+     * Overriding subclasses can provide their own data loading logic as to when the entrance
+     * animation should be executed.
      */
     protected void onLogoAnimationFinished() {
+        startEnterAnimation(false);
     }
 
-    void startEnterAnimation() {
-        mLogoAnimationFinished = true;
-        initializeViews(getView());
-        List<Animator> animators = new ArrayList<>();
+    /**
+     * Called to start entrance transition. This can be called by subclasses when the logo animation
+     * and data loading is complete. If force flag is set to false, it will only start the animation
+     * if it's not already done yet. Otherwise, it will always start the enter animation. In both
+     * cases, the logo view will hide and the rest of fragment views become visible after this call.
+     *
+     * @param force {@code true} if enter animation has to be performed regardless of whether it's
+     *                          been done in the past, {@code false} otherwise
+     */
+    protected final void startEnterAnimation(boolean force) {
         final Context context = FragmentUtil.getContext(this);
+        if (context == null) {
+            return;
+        }
+        hideLogoView();
+        if (mEnterAnimationFinished && !force) {
+            return;
+        }
+        List<Animator> animators = new ArrayList<>();
         Animator animator = AnimatorInflater.loadAnimator(context,
                 R.animator.lb_onboarding_page_indicator_enter);
         animator.setTarget(getPageCount() <= 1 ? mStartButton : mPageIndicator);
         animators.add(animator);
-        // Header title
-        View view = getView().findViewById(R.id.title);
-        view.setAlpha(0);
-        animator = AnimatorInflater.loadAnimator(context,
-                R.animator.lb_onboarding_title_enter);
-        animator.setStartDelay(START_DELAY_TITLE_MS);
-        animator.setTarget(view);
-        animators.add(animator);
-        // Header description
-        view = getView().findViewById(R.id.description);
-        view.setAlpha(0);
-        animator = AnimatorInflater.loadAnimator(context,
-                R.animator.lb_onboarding_description_enter);
-        animator.setStartDelay(START_DELAY_DESCRIPTION_MS);
-        animator.setTarget(view);
-        animators.add(animator);
+
+        animator = onCreateTitleAnimator();
+        if (animator != null) {
+            // Header title.
+            animator.setTarget(mTitleView);
+            animators.add(animator);
+        }
+
+        animator = onCreateDescriptionAnimator();
+        if (animator != null) {
+            // Header description.
+            animator.setTarget(mDescriptionView);
+            animators.add(animator);
+        }
+
         // Customized animation by the inherited class.
         Animator customAnimator = onCreateEnterAnimation();
         if (customAnimator != null) {
             animators.add(customAnimator);
         }
+
+        // Return if we don't have any animations.
+        if (animators.isEmpty()) {
+            return;
+        }
         mAnimator = new AnimatorSet();
         mAnimator.playTogether(animators);
         mAnimator.start();
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mEnterAnimationFinished = true;
+            }
+        });
         // Search focus and give the focus to the appropriate child which has become visible.
         getView().requestFocus();
     }
 
     /**
-     * Returns whether the logo enter transition is finished.
+     * Provides the entry animation for description view. This allows users to override the
+     * default fade and slide animation. Returning null will disable the animation.
+     */
+    protected Animator onCreateDescriptionAnimator() {
+        return AnimatorInflater.loadAnimator(FragmentUtil.getContext(this),
+                R.animator.lb_onboarding_description_enter);
+    }
+
+    /**
+     * Provides the entry animation for title view. This allows users to override the
+     * default fade and slide animation. Returning null will disable the animation.
+     */
+    protected Animator onCreateTitleAnimator() {
+        return AnimatorInflater.loadAnimator(FragmentUtil.getContext(this),
+                R.animator.lb_onboarding_title_enter);
+    }
+
+    /**
+     * Returns whether the logo enter animation is finished.
      *
      * @return {@code true} if the logo enter transition is finished, {@code false} otherwise
      */
@@ -728,4 +1000,22 @@
         }
         return animator;
     }
+
+    /**
+     * Sets the resource id for the main icon.
+     */
+    public final void setIconResouceId(int resourceId) {
+        this.mIconResourceId = resourceId;
+        if (mMainIconView != null) {
+            mMainIconView.setImageResource(resourceId);
+            mMainIconView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * Returns the resource id of the main icon.
+     */
+    public final int getIconResourceId() {
+        return mIconResourceId;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
index 2c43ed8..a24ea4d 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
@@ -27,7 +27,9 @@
 import android.animation.TimeInterpolator;
 import android.support.v4.app.Fragment;
 import android.content.Context;
+import android.graphics.Color;
 import android.os.Bundle;
+import android.support.annotation.ColorInt;
 import android.support.annotation.Nullable;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.PagingIndicator;
@@ -44,6 +46,7 @@
 import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -56,8 +59,8 @@
  * <p>
  * <h3>Building the screen</h3>
  * The view structure of onboarding screen is composed of the common parts and custom parts. The
- * common parts are composed of title, description and page navigator and the custom parts are
- * composed of background, contents and foreground.
+ * common parts are composed of icon, title, description and page navigator and the custom parts
+ * are composed of background, contents and foreground.
  * <p>
  * To build the screen views, the inherited class should override:
  * <ul>
@@ -105,8 +108,12 @@
  * If the inherited class provides neither the logo image nor the animation, the logo animation will
  * be omitted.
  * <h4>Page enter animation</h4>
- * After logo animation finishes, page enter animation starts. The application can provide the
- * animations of custom views by overriding {@link #onCreateEnterAnimation}.
+ * After logo animation finishes, page enter animation starts, which causes the header section -
+ * title and description views to fade and slide in. Users can override the default
+ * fade + slide animation by overriding {@link #onCreateTitleAnimator()} &
+ * {@link #onCreateDescriptionAnimator()}. By default we don't animate the custom views but users
+ * can provide animation by overriding {@link #onCreateEnterAnimation}.
+ *
  * <h4>Page change animation</h4>
  * When the page changes, the default animations of the title and description are played. The
  * inherited class can override {@link #onPageChanged} to start the custom animations.
@@ -148,12 +155,10 @@
  * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
  */
 abstract public class OnboardingSupportFragment extends Fragment {
-    private static final String TAG = "OnboardingSupportFragment";
+    private static final String TAG = "OnboardingF";
     private static final boolean DEBUG = false;
 
     private static final long LOGO_SPLASH_PAUSE_DURATION_MS = 1333;
-    private static final long START_DELAY_TITLE_MS = 33;
-    private static final long START_DELAY_DESCRIPTION_MS = 33;
 
     private static final long HEADER_ANIMATION_DURATION_MS = 417;
     private static final long DESCRIPTION_START_DELAY_MS = 33;
@@ -168,12 +173,20 @@
 
     // Keys used to save and restore the states.
     private static final String KEY_CURRENT_PAGE_INDEX = "leanback.onboarding.current_page_index";
+    private static final String KEY_LOGO_ANIMATION_FINISHED =
+            "leanback.onboarding.logo_animation_finished";
+    private static final String KEY_ENTER_ANIMATION_FINISHED =
+            "leanback.onboarding.enter_animation_finished";
 
     private ContextThemeWrapper mThemeWrapper;
 
     PagingIndicator mPageIndicator;
     View mStartButton;
     private ImageView mLogoView;
+    // Optional icon that can be displayed on top of the header section.
+    private ImageView mMainIconView;
+    private int mIconResourceId;
+
     TextView mTitleView;
     TextView mDescriptionView;
 
@@ -183,8 +196,33 @@
     // the fragment is restored.
     private int mLogoResourceId;
     boolean mLogoAnimationFinished;
+    boolean mEnterAnimationFinished;
     int mCurrentPageIndex;
 
+    @ColorInt
+    private int mTitleViewTextColor = Color.TRANSPARENT;
+    private boolean mTitleViewTextColorSet;
+
+    @ColorInt
+    private int mDescriptionViewTextColor = Color.TRANSPARENT;
+    private boolean mDescriptionViewTextColorSet;
+
+    @ColorInt
+    private int mDotBackgroundColor = Color.TRANSPARENT;
+    private boolean mDotBackgroundColorSet;
+
+    @ColorInt
+    private int mArrowColor = Color.TRANSPARENT;
+    private boolean mArrowColorSet;
+
+    @ColorInt
+    private int mArrowBackgroundColor = Color.TRANSPARENT;
+    private boolean mArrowBackgroundColorSet;
+
+    private CharSequence mStartButtonText;
+    private boolean mStartButtonTextSet;
+
+
     private AnimatorSet mAnimator;
 
     private final OnClickListener mOnClickListener = new OnClickListener() {
@@ -282,41 +320,213 @@
         mStartButton = view.findViewById(R.id.button_start);
         mStartButton.setOnClickListener(mOnClickListener);
         mStartButton.setOnKeyListener(mOnKeyListener);
+        mMainIconView = (ImageView) view.findViewById(R.id.main_icon);
         mLogoView = (ImageView) view.findViewById(R.id.logo);
         mTitleView = (TextView) view.findViewById(R.id.title);
         mDescriptionView = (TextView) view.findViewById(R.id.description);
+
+        if (mTitleViewTextColorSet) {
+            mTitleView.setTextColor(mTitleViewTextColor);
+        }
+        if (mDescriptionViewTextColorSet) {
+            mDescriptionView.setTextColor(mDescriptionViewTextColor);
+        }
+        if (mDotBackgroundColorSet) {
+            mPageIndicator.setDotBackgroundColor(mDotBackgroundColor);
+        }
+        if (mArrowColorSet) {
+            mPageIndicator.setArrowColor(mArrowColor);
+        }
+        if (mArrowBackgroundColorSet) {
+            mPageIndicator.setDotBackgroundColor(mArrowBackgroundColor);
+        }
+        if (mStartButtonTextSet) {
+            ((Button) mStartButton).setText(mStartButtonText);
+        }
         final Context context = getContext();
         if (sSlideDistance == 0) {
             sSlideDistance = (int) (SLIDE_DISTANCE * context.getResources()
                     .getDisplayMetrics().scaledDensity);
         }
-        if (savedInstanceState == null) {
-            mCurrentPageIndex = 0;
-            mLogoAnimationFinished = false;
-            mPageIndicator.onPageSelected(0, false);
-            view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    view.getViewTreeObserver().removeOnPreDrawListener(this);
-                    if (!startLogoAnimation()) {
-                        startEnterAnimation();
-                    }
-                    return true;
-                }
-            });
-        } else {
-            mLogoAnimationFinished = true;
-            mCurrentPageIndex = savedInstanceState.getInt(KEY_CURRENT_PAGE_INDEX);
-            initializeViews(view);
-        }
         view.requestFocus();
         return view;
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        if (savedInstanceState == null) {
+            mCurrentPageIndex = 0;
+            mLogoAnimationFinished = false;
+            mEnterAnimationFinished = false;
+            mPageIndicator.onPageSelected(0, false);
+            view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    getView().getViewTreeObserver().removeOnPreDrawListener(this);
+                    if (!startLogoAnimation()) {
+                        mLogoAnimationFinished = true;
+                        onLogoAnimationFinished();
+                    }
+                    return true;
+                }
+            });
+        } else {
+            mCurrentPageIndex = savedInstanceState.getInt(KEY_CURRENT_PAGE_INDEX);
+            mLogoAnimationFinished = savedInstanceState.getBoolean(KEY_LOGO_ANIMATION_FINISHED);
+            mEnterAnimationFinished = savedInstanceState.getBoolean(KEY_ENTER_ANIMATION_FINISHED);
+            if (!mLogoAnimationFinished) {
+                // logo animation wasn't started or was interrupted when the activity was destroyed;
+                // restart it againl
+                if (!startLogoAnimation()) {
+                    mLogoAnimationFinished = true;
+                    onLogoAnimationFinished();
+                }
+            } else {
+                onLogoAnimationFinished();
+            }
+        }
+    }
+
+    @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putInt(KEY_CURRENT_PAGE_INDEX, mCurrentPageIndex);
+        outState.putBoolean(KEY_LOGO_ANIMATION_FINISHED, mLogoAnimationFinished);
+        outState.putBoolean(KEY_ENTER_ANIMATION_FINISHED, mEnterAnimationFinished);
+    }
+
+    /**
+     * Sets the text color for TitleView. If not set, the default textColor set in style
+     * referenced by attr {@link R.attr#onboardingTitleStyle} will be used.
+     * @param color the color to use as the text color for TitleView
+     */
+    public void setTitleViewTextColor(@ColorInt int color) {
+        mTitleViewTextColor = color;
+        mTitleViewTextColorSet = true;
+        if (mTitleView != null) {
+            mTitleView.setTextColor(color);
+        }
+    }
+
+    /**
+     * Returns the text color of TitleView if it's set through
+     * {@link #setTitleViewTextColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getTitleViewTextColor() {
+        return mTitleViewTextColor;
+    }
+
+    /**
+     * Sets the text color for DescriptionView. If not set, the default textColor set in style
+     * referenced by attr {@link R.attr#onboardingDescriptionStyle} will be used.
+     * @param color the color to use as the text color for DescriptionView
+     */
+    public void setDescriptionViewTextColor(@ColorInt int color) {
+        mDescriptionViewTextColor = color;
+        mDescriptionViewTextColorSet = true;
+        if (mDescriptionView != null) {
+            mDescriptionView.setTextColor(color);
+        }
+    }
+
+    /**
+     * Returns the text color of DescriptionView if it's set through
+     * {@link #setDescriptionViewTextColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getDescriptionViewTextColor() {
+        return mDescriptionViewTextColor;
+    }
+    /**
+     * Sets the background color of the dots. If not set, the default color from attr
+     * {@link R.styleable#PagingIndicator_dotBgColor} in the theme will be used.
+     * @param color the color to use for dot backgrounds
+     */
+    public void setDotBackgroundColor(@ColorInt int color) {
+        mDotBackgroundColor = color;
+        mDotBackgroundColorSet = true;
+        if (mPageIndicator != null) {
+            mPageIndicator.setDotBackgroundColor(color);
+        }
+    }
+
+    /**
+     * Returns the background color of the dot if it's set through
+     * {@link #setDotBackgroundColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getDotBackgroundColor() {
+        return mDotBackgroundColor;
+    }
+
+    /**
+     * Sets the color of the arrow. This color will supersede the color set in the theme attribute
+     * {@link R.styleable#PagingIndicator_arrowColor} if provided. If none of these two are set, the
+     * arrow will have its original bitmap color.
+     *
+     * @param color the color to use for arrow background
+     */
+    public void setArrowColor(@ColorInt int color) {
+        mArrowColor = color;
+        mArrowColorSet = true;
+        if (mPageIndicator != null) {
+            mPageIndicator.setArrowColor(color);
+        }
+    }
+
+    /**
+     * Returns the color of the arrow if it's set through
+     * {@link #setArrowColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getArrowColor() {
+        return mArrowColor;
+    }
+
+    /**
+     * Sets the background color of the arrow. If not set, the default color from attr
+     * {@link R.styleable#PagingIndicator_arrowBgColor} in the theme will be used.
+     * @param color the color to use for arrow background
+     */
+    public void setArrowBackgroundColor(@ColorInt int color) {
+        mArrowBackgroundColor = color;
+        mArrowBackgroundColorSet = true;
+        if (mPageIndicator != null) {
+            mPageIndicator.setArrowBackgroundColor(color);
+        }
+    }
+
+    /**
+     * Returns the background color of the arrow if it's set through
+     * {@link #setArrowBackgroundColor(int)}. If no color was set, transparent is returned.
+     */
+    @ColorInt
+    public final int getArrowBackgroundColor() {
+        return mArrowBackgroundColor;
+    }
+
+    /**
+     * Returns the start button text if it's set through
+     * {@link #setStartButtonText(CharSequence)}}. If no string was set, null is returned.
+     */
+    public final CharSequence getStartButtonText() {
+        return mStartButtonText;
+    }
+
+    /**
+     * Sets the text on the start button text. If not set, the default text set in
+     * {@link R.styleable#LeanbackOnboardingTheme_onboardingStartButtonStyle} will be used.
+     *
+     * @param text the start button text
+     */
+    public void setStartButtonText(CharSequence text) {
+        mStartButtonText = text;
+        mStartButtonTextSet = true;
+        if (mStartButton != null) {
+            ((Button) mStartButton).setText(mStartButtonText);
+        }
     }
 
     /**
@@ -386,6 +596,9 @@
 
     boolean startLogoAnimation() {
         final Context context = getContext();
+        if (context == null) {
+            return false;
+        }
         Animator animator = null;
         if (mLogoResourceId != 0) {
             mLogoView.setVisibility(View.VISIBLE);
@@ -407,7 +620,8 @@
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     if (context != null) {
-                        startEnterAnimation();
+                        mLogoAnimationFinished = true;
+                        onLogoAnimationFinished();
                     }
                 }
             });
@@ -428,8 +642,20 @@
         return null;
     }
 
-    private void initializeViews(View container) {
+
+    /**
+     * Hides the logo view and makes other fragment views visible. Also initializes the texts for
+     * Title and Description views.
+     */
+    void hideLogoView() {
         mLogoView.setVisibility(View.GONE);
+
+        if (mIconResourceId != 0) {
+            mMainIconView.setImageResource(mIconResourceId);
+            mMainIconView.setVisibility(View.VISIBLE);
+        }
+
+        View container = getView();
         // Create custom views.
         LayoutInflater inflater = getThemeInflater(LayoutInflater.from(
                 getContext()));
@@ -468,56 +694,102 @@
         // Header views.
         mTitleView.setText(getPageTitle(mCurrentPageIndex));
         mDescriptionView.setText(getPageDescription(mCurrentPageIndex));
-        onLogoAnimationFinished();
     }
 
     /**
-     * Called immediately after fragment views become visible. This method gives subclasses a chance
-     * to initialize themselves. If a logo animation is specified, calling this method is delayed
-     * until after the logo animation is complete.
+     * Called immediately after the logo animation is complete or no logo animation is specified.
+     * This method can also be called when the activity is recreated, i.e. when no logo animation
+     * are performed.
+     * By default, this method will hide the logo view and start the entrance animation for this
+     * fragment.
+     * Overriding subclasses can provide their own data loading logic as to when the entrance
+     * animation should be executed.
      */
     protected void onLogoAnimationFinished() {
+        startEnterAnimation(false);
     }
 
-    void startEnterAnimation() {
-        mLogoAnimationFinished = true;
-        initializeViews(getView());
-        List<Animator> animators = new ArrayList<>();
+    /**
+     * Called to start entrance transition. This can be called by subclasses when the logo animation
+     * and data loading is complete. If force flag is set to false, it will only start the animation
+     * if it's not already done yet. Otherwise, it will always start the enter animation. In both
+     * cases, the logo view will hide and the rest of fragment views become visible after this call.
+     *
+     * @param force {@code true} if enter animation has to be performed regardless of whether it's
+     *                          been done in the past, {@code false} otherwise
+     */
+    protected final void startEnterAnimation(boolean force) {
         final Context context = getContext();
+        if (context == null) {
+            return;
+        }
+        hideLogoView();
+        if (mEnterAnimationFinished && !force) {
+            return;
+        }
+        List<Animator> animators = new ArrayList<>();
         Animator animator = AnimatorInflater.loadAnimator(context,
                 R.animator.lb_onboarding_page_indicator_enter);
         animator.setTarget(getPageCount() <= 1 ? mStartButton : mPageIndicator);
         animators.add(animator);
-        // Header title
-        View view = getView().findViewById(R.id.title);
-        view.setAlpha(0);
-        animator = AnimatorInflater.loadAnimator(context,
-                R.animator.lb_onboarding_title_enter);
-        animator.setStartDelay(START_DELAY_TITLE_MS);
-        animator.setTarget(view);
-        animators.add(animator);
-        // Header description
-        view = getView().findViewById(R.id.description);
-        view.setAlpha(0);
-        animator = AnimatorInflater.loadAnimator(context,
-                R.animator.lb_onboarding_description_enter);
-        animator.setStartDelay(START_DELAY_DESCRIPTION_MS);
-        animator.setTarget(view);
-        animators.add(animator);
+
+        animator = onCreateTitleAnimator();
+        if (animator != null) {
+            // Header title.
+            animator.setTarget(mTitleView);
+            animators.add(animator);
+        }
+
+        animator = onCreateDescriptionAnimator();
+        if (animator != null) {
+            // Header description.
+            animator.setTarget(mDescriptionView);
+            animators.add(animator);
+        }
+
         // Customized animation by the inherited class.
         Animator customAnimator = onCreateEnterAnimation();
         if (customAnimator != null) {
             animators.add(customAnimator);
         }
+
+        // Return if we don't have any animations.
+        if (animators.isEmpty()) {
+            return;
+        }
         mAnimator = new AnimatorSet();
         mAnimator.playTogether(animators);
         mAnimator.start();
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mEnterAnimationFinished = true;
+            }
+        });
         // Search focus and give the focus to the appropriate child which has become visible.
         getView().requestFocus();
     }
 
     /**
-     * Returns whether the logo enter transition is finished.
+     * Provides the entry animation for description view. This allows users to override the
+     * default fade and slide animation. Returning null will disable the animation.
+     */
+    protected Animator onCreateDescriptionAnimator() {
+        return AnimatorInflater.loadAnimator(getContext(),
+                R.animator.lb_onboarding_description_enter);
+    }
+
+    /**
+     * Provides the entry animation for title view. This allows users to override the
+     * default fade and slide animation. Returning null will disable the animation.
+     */
+    protected Animator onCreateTitleAnimator() {
+        return AnimatorInflater.loadAnimator(getContext(),
+                R.animator.lb_onboarding_title_enter);
+    }
+
+    /**
+     * Returns whether the logo enter animation is finished.
      *
      * @return {@code true} if the logo enter transition is finished, {@code false} otherwise
      */
@@ -731,4 +1003,22 @@
         }
         return animator;
     }
+
+    /**
+     * Sets the resource id for the main icon.
+     */
+    public final void setIconResouceId(int resourceId) {
+        this.mIconResourceId = resourceId;
+        if (mMainIconView != null) {
+            mMainIconView.setImageResource(resourceId);
+            mMainIconView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * Returns the resource id of the main icon.
+     */
+    public final int getIconResourceId() {
+        return mIconResourceId;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
index eb2ce28..d74fd11 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
@@ -250,6 +250,7 @@
         return getControlsRowPresenter();
     }
 
+    @Override
     protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
             PresenterSelector presenterSelector) {
         return super.createPrimaryActionsAdapter(presenterSelector);
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
index 60cd06b..68a1215 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
@@ -37,6 +37,8 @@
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackSeekDataProvider;
+import android.support.v17.leanback.widget.PlaybackSeekUi;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.Row;
@@ -53,8 +55,6 @@
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 
-import java.util.ArrayList;
-
 /**
  * A fragment for displaying playback controls and related content.
  *
@@ -71,8 +71,15 @@
  * optional, app can pass playback row and PlaybackRowPresenter in the adapter using
  * {@link #setAdapter(ObjectAdapter)}.
  * </p>
+ * <p>
+ * Auto hide controls upon playing: best practice is calling
+ * {@link #setControlsOverlayAutoHideEnabled(boolean)} upon play/pause. The auto hiding timer will
+ * be cancelled upon {@link #tickle()} triggered by input event.
+ * </p>
  */
 public class PlaybackFragment extends Fragment {
+    static final String BUNDLE_CONTROL_VISIBLE_ON_CREATEVIEW = "controlvisible_oncreateview";
+
     /**
      * No background.
      */
@@ -84,6 +91,10 @@
     public static final int BG_DARK = 1;
     PlaybackGlueHost.HostCallback mHostCallback;
 
+    PlaybackSeekUi.Client mSeekUiClient;
+    boolean mInSeek;
+    ProgressBarManager mProgressBarManager = new ProgressBarManager();
+
     /**
      * Resets the focus on the button in the middle of control row.
      * @hide
@@ -182,12 +193,12 @@
 
     // Fading status
     private static final int IDLE = 0;
-    private static final int IN = 1;
-    private static final int OUT = 2;
+    private static final int ANIMATING = 1;
 
     int mPaddingBottom;
     int mOtherRowsCenterToBottom;
     View mRootView;
+    View mBackgroundView;
     int mBackgroundType = BG_DARK;
     int mBgDarkColor;
     int mBgLightColor;
@@ -197,7 +208,8 @@
     OnFadeCompleteListener mFadeCompleteListener;
     View.OnKeyListener mInputEventHandler;
     boolean mFadingEnabled = true;
-    int mFadingStatus = IDLE;
+    boolean mControlVisibleBeforeOnCreateView = true;
+    boolean mControlVisible = true;
     int mBgAlpha;
     ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator;
     ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator;
@@ -223,7 +235,6 @@
                     if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha);
                     if (mBgAlpha > 0) {
                         enableVerticalGridAnimations(true);
-                        startFadeTimer();
                         if (mFadeCompleteListener != null) {
                             mFadeCompleteListener.onFadeInComplete();
                         }
@@ -242,10 +253,13 @@
                             mFadeCompleteListener.onFadeOutComplete();
                         }
                     }
-                    mFadingStatus = IDLE;
                 }
             };
 
+    public PlaybackFragment() {
+        mProgressBarManager.setInitialDelay(500);
+    }
+
     VerticalGridView getVerticalGridView() {
         if (mRowsFragment == null) {
             return null;
@@ -257,7 +271,7 @@
         @Override
         public void handleMessage(Message message) {
             if (message.what == START_FADE_OUT && mFadingEnabled) {
-                fade(false);
+                hideControlsOverlay(true);
             }
         }
     };
@@ -280,8 +294,8 @@
 
     private void setBgAlpha(int alpha) {
         mBgAlpha = alpha;
-        if (mRootView != null) {
-            mRootView.getBackground().setAlpha(alpha);
+        if (mBackgroundView != null) {
+            mBackgroundView.getBackground().setAlpha(alpha);
         }
     }
 
@@ -292,36 +306,54 @@
     }
 
     /**
-     * Enables or disables view fading.  If enabled,
-     * the view will be faded in when the fragment starts,
-     * and will fade out after a time period.  The timeout
-     * period is reset each time {@link #tickle} is called.
+     * Enables or disables auto hiding controls overlay after a short delay fragment is resumed.
+     * If enabled and fragment is resumed, the view will fade out after a time period.
+     * {@link #tickle()} will kill the timer, next time fragment is resumed,
+     * the timer will be started again if {@link #isControlsOverlayAutoHideEnabled()} is true.
      */
-    public void setFadingEnabled(boolean enabled) {
-        if (DEBUG) Log.v(TAG, "setFadingEnabled " + enabled);
+    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
+        if (DEBUG) Log.v(TAG, "setControlsOverlayAutoHideEnabled " + enabled);
         if (enabled != mFadingEnabled) {
             mFadingEnabled = enabled;
-            if (mFadingEnabled) {
-                if (isResumed() && mFadingStatus == IDLE
-                        && !mHandler.hasMessages(START_FADE_OUT)) {
+            if (isResumed() && getView().hasFocus()) {
+                showControlsOverlay(true);
+                if (enabled) {
+                    // StateGraph 7->2 5->2
                     startFadeTimer();
+                } else {
+                    // StateGraph 4->5 2->5
+                    stopFadeTimer();
                 }
             } else {
-                // Ensure fully opaque
-                mHandler.removeMessages(START_FADE_OUT);
-                fade(true);
+                // StateGraph 6->1 1->6
             }
         }
     }
 
     /**
-     * Returns true if view fading is enabled.
+     * Returns true if controls will be auto hidden after a delay when fragment is resumed.
      */
-    public boolean isFadingEnabled() {
+    public boolean isControlsOverlayAutoHideEnabled() {
         return mFadingEnabled;
     }
 
     /**
+     * @deprecated Uses {@link #setControlsOverlayAutoHideEnabled(boolean)}
+     */
+    @Deprecated
+    public void setFadingEnabled(boolean enabled) {
+        setControlsOverlayAutoHideEnabled(enabled);
+    }
+
+    /**
+     * @deprecated Uses {@link #isControlsOverlayAutoHideEnabled()}
+     */
+    @Deprecated
+    public boolean isFadingEnabled() {
+        return isControlsOverlayAutoHideEnabled();
+    }
+
+    /**
      * Sets the listener to be called when fade in or out has completed.
      * @hide
      */
@@ -345,46 +377,29 @@
     }
 
     /**
-     * Tickles the playback controls.  Fades in the view if it was faded out,
-     * otherwise resets the fade out timer.  Tickling on input events is handled
-     * by the fragment.
+     * Tickles the playback controls. Fades in the view if it was faded out. {@link #tickle()} will
+     * also kill the timer created by {@link #setControlsOverlayAutoHideEnabled(boolean)}. When
+     * next time fragment is resumed, the timer will be started again if
+     * {@link #isControlsOverlayAutoHideEnabled()} is true. In most cases app does not need call
+     * this method, tickling on input events is handled by the fragment.
      */
     public void tickle() {
         if (DEBUG) Log.v(TAG, "tickle enabled " + mFadingEnabled + " isResumed " + isResumed());
-        if (!mFadingEnabled || !isResumed()) {
-            return;
-        }
-        if (mHandler.hasMessages(START_FADE_OUT)) {
-            // Restart the timer
-            startFadeTimer();
-        } else {
-            fade(true);
-        }
-    }
-
-    /**
-     * Fades out the playback overlay immediately.
-     */
-    public void fadeOut() {
-        mHandler.removeMessages(START_FADE_OUT);
-        fade(false);
-    }
-
-    /**
-     * Returns true/false indicating whether playback controls are visible or not.
-     */
-    private boolean areControlsHidden() {
-        return mFadingStatus == IDLE && mBgAlpha == 0;
+        //StateGraph 2->4
+        stopFadeTimer();
+        showControlsOverlay(true);
     }
 
     private boolean onInterceptInputEvent(InputEvent event) {
-        final boolean controlsHidden = areControlsHidden();
+        final boolean controlsHidden = !mControlVisible;
         if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event);
         boolean consumeEvent = false;
         int keyCode = KeyEvent.KEYCODE_UNKNOWN;
+        int keyAction = 0;
 
         if (event instanceof KeyEvent) {
             keyCode = ((KeyEvent) event).getKeyCode();
+            keyAction = ((KeyEvent) event).getAction();
             if (mInputEventHandler != null) {
                 consumeEvent = mInputEventHandler.onKey(getView(), keyCode, (KeyEvent) event);
             }
@@ -401,34 +416,60 @@
                 if (controlsHidden) {
                     consumeEvent = true;
                 }
-                tickle();
+                if (keyAction == KeyEvent.ACTION_DOWN) {
+                    tickle();
+                }
                 break;
             case KeyEvent.KEYCODE_BACK:
             case KeyEvent.KEYCODE_ESCAPE:
-                // If fading enabled and controls are not hidden, back will be consumed to fade
+                if (mInSeek) {
+                    // when in seek, the SeekUi will handle the BACK.
+                    return false;
+                }
+                // If controls are not hidden, back will be consumed to fade
                 // them out (even if the key was consumed by the handler).
-                if (mFadingEnabled && !controlsHidden) {
+                if (!controlsHidden) {
                     consumeEvent = true;
-                    mHandler.removeMessages(START_FADE_OUT);
-                    fade(false);
-                } else if (consumeEvent) {
-                    tickle();
+
+                    if (((KeyEvent) event).getAction() == KeyEvent.ACTION_UP) {
+                        hideControlsOverlay(true);
+                    }
                 }
                 break;
             default:
                 if (consumeEvent) {
-                    tickle();
+                    if (keyAction == KeyEvent.ACTION_DOWN) {
+                        tickle();
+                    }
                 }
         }
         return consumeEvent;
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        // controls view are initially visible, make it invisible
+        // if app has called hideControlsOverlay() before view created.
+        mControlVisible = true;
+        if (!mControlVisibleBeforeOnCreateView) {
+            showControlsOverlay(false, false);
+            mControlVisibleBeforeOnCreateView = true;
+        }
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
-        if (mFadingEnabled) {
-            setBgAlpha(0);
-            fade(true);
+
+        if (mControlVisible) {
+            //StateGraph: 6->5 1->2
+            if (mFadingEnabled) {
+                // StateGraph 1->2
+                startFadeTimer();
+            }
+        } else {
+            //StateGraph: 6->7 1->3
         }
         getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
         getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
@@ -437,6 +478,12 @@
         }
     }
 
+    private void stopFadeTimer() {
+        if (mHandler != null) {
+            mHandler.removeMessages(START_FADE_OUT);
+        }
+    }
+
     private void startFadeTimer() {
         if (mHandler != null) {
             mHandler.removeMessages(START_FADE_OUT);
@@ -471,31 +518,19 @@
     private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0);
     private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0);
 
-    private View getControlRowView() {
-        if (getVerticalGridView() == null) {
-            return null;
-        }
-        RecyclerView.ViewHolder vh = getVerticalGridView().findViewHolderForPosition(0);
-        if (vh == null) {
-            return null;
-        }
-        return vh.itemView;
-    }
-
     private void loadControlRowAnimator() {
-        final AnimatorListener listener = new AnimatorListener() {
-            @Override
-            void getViews(ArrayList<View> views) {
-                View view = getControlRowView();
-                if (view != null) {
-                    views.add(view);
-                }
-            }
-        };
         final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator arg0) {
-                View view = getControlRowView();
+                if (getVerticalGridView() == null) {
+                    return;
+                }
+                RecyclerView.ViewHolder vh = getVerticalGridView()
+                        .findViewHolderForAdapterPosition(0);
+                if (vh == null) {
+                    return;
+                }
+                View view = vh.itemView;
                 if (view != null) {
                     final float fraction = (Float) arg0.getAnimatedValue();
                     if (DEBUG) Log.v(TAG, "fraction " + fraction);
@@ -508,32 +543,15 @@
         Context context = FragmentUtil.getContext(this);
         mControlRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
         mControlRowFadeInAnimator.addUpdateListener(updateListener);
-        mControlRowFadeInAnimator.addListener(listener);
         mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
 
         mControlRowFadeOutAnimator = loadAnimator(context,
                 R.animator.lb_playback_controls_fade_out);
         mControlRowFadeOutAnimator.addUpdateListener(updateListener);
-        mControlRowFadeOutAnimator.addListener(listener);
         mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator);
     }
 
     private void loadOtherRowAnimator() {
-        final AnimatorListener listener = new AnimatorListener() {
-            @Override
-            void getViews(ArrayList<View> views) {
-                if (getVerticalGridView() == null) {
-                    return;
-                }
-                final int count = getVerticalGridView().getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View view = getVerticalGridView().getChildAt(i);
-                    if (view != null) {
-                        views.add(view);
-                    }
-                }
-            }
-        };
         final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator arg0) {
@@ -541,8 +559,10 @@
                     return;
                 }
                 final float fraction = (Float) arg0.getAnimatedValue();
-                for (View view : listener.mViews) {
-                    if (getVerticalGridView().getChildPosition(view) > 0) {
+                final int count = getVerticalGridView().getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View view = getVerticalGridView().getChildAt(i);
+                    if (getVerticalGridView().getChildAdapterPosition(view) > 0) {
                         view.setAlpha(fraction);
                         view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
                     }
@@ -552,67 +572,133 @@
 
         Context context = FragmentUtil.getContext(this);
         mOtherRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
-        mOtherRowFadeInAnimator.addListener(listener);
         mOtherRowFadeInAnimator.addUpdateListener(updateListener);
         mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
 
         mOtherRowFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_out);
-        mOtherRowFadeOutAnimator.addListener(listener);
         mOtherRowFadeOutAnimator.addUpdateListener(updateListener);
         mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator());
     }
 
-    private void fade(boolean fadeIn) {
-        if (DEBUG) Log.v(TAG, "fade " + fadeIn);
-        if (getView() == null) {
-            return;
-        }
-        if ((fadeIn && mFadingStatus == IN) || (!fadeIn && mFadingStatus == OUT)) {
-            if (DEBUG) Log.v(TAG, "requested fade in progress");
-            return;
-        }
-        if ((fadeIn && mBgAlpha == 255) || (!fadeIn && mBgAlpha == 0)) {
-            if (DEBUG) Log.v(TAG, "fade is no-op");
-            return;
-        }
+    /**
+     * Fades out the playback overlay immediately.
+     * @deprecated Call {@link #hideControlsOverlay(boolean)}
+     */
+    @Deprecated
+    public void fadeOut() {
+        showControlsOverlay(false, false);
+    }
 
-        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0
-                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
+    /**
+     * Show controls overlay.
+     *
+     * @param runAnimation True to run animation, false otherwise.
+     */
+    public void showControlsOverlay(boolean runAnimation) {
+        showControlsOverlay(true, runAnimation);
+    }
 
-        if (mFadingStatus == IDLE) {
-            if (fadeIn) {
-                mBgFadeInAnimator.start();
-                mControlRowFadeInAnimator.start();
-                mOtherRowFadeInAnimator.start();
-            } else {
-                mBgFadeOutAnimator.start();
-                mControlRowFadeOutAnimator.start();
-                mOtherRowFadeOutAnimator.start();
+    /**
+     * Returns true if controls overlay is visible, false otherwise.
+     *
+     * @return True if controls overlay is visible, false otherwise.
+     * @see #showControlsOverlay(boolean)
+     * @see #hideControlsOverlay(boolean)
+     */
+    public boolean isControlsOverlayVisible() {
+        return mControlVisible;
+    }
+
+    /**
+     * Hide controls overlay.
+     *
+     * @param runAnimation True to run animation, false otherwise.
+     */
+    public void hideControlsOverlay(boolean runAnimation) {
+        showControlsOverlay(false, runAnimation);
+    }
+
+    /**
+     * if first animator is still running, reverse it; otherwise start second animator.
+     */
+    static void reverseFirstOrStartSecond(ValueAnimator first, ValueAnimator second,
+            boolean runAnimation) {
+        if (first.isStarted()) {
+            first.reverse();
+            if (!runAnimation) {
+                first.end();
             }
         } else {
-            if (fadeIn) {
-                mBgFadeOutAnimator.reverse();
-                mControlRowFadeOutAnimator.reverse();
-                mOtherRowFadeOutAnimator.reverse();
-            } else {
-                mBgFadeInAnimator.reverse();
-                mControlRowFadeInAnimator.reverse();
-                mOtherRowFadeInAnimator.reverse();
+            second.start();
+            if (!runAnimation) {
+                second.end();
             }
         }
-        getView().announceForAccessibility(getString(fadeIn ? R.string.lb_playback_controls_shown
-                : R.string.lb_playback_controls_hidden));
+    }
 
-        // If fading in while control row is focused, set initial translationY so
-        // views slide in from below.
-        if (fadeIn && mFadingStatus == IDLE) {
-            final int count = getVerticalGridView().getChildCount();
-            for (int i = 0; i < count; i++) {
-                getVerticalGridView().getChildAt(i).setTranslationY(mAnimationTranslateY);
+    /**
+     * End first or second animator if they are still running.
+     */
+    static void endAll(ValueAnimator first, ValueAnimator second) {
+        if (first.isStarted()) {
+            first.end();
+        } else if (second.isStarted()) {
+            second.end();
+        }
+    }
+
+    /**
+     * Fade in or fade out rows and background.
+     *
+     * @param show True to fade in, false to fade out.
+     * @param animation True to run animation.
+     */
+    void showControlsOverlay(boolean show, boolean animation) {
+        if (DEBUG) Log.v(TAG, "showControlsOverlay " + show);
+        if (getView() == null) {
+            mControlVisibleBeforeOnCreateView = show;
+            return;
+        }
+        // force no animation when fragment is not resumed
+        if (!isResumed()) {
+            animation = false;
+        }
+        if (show == mControlVisible) {
+            if (!animation) {
+                // End animation if needed
+                endAll(mBgFadeInAnimator, mBgFadeOutAnimator);
+                endAll(mControlRowFadeInAnimator, mControlRowFadeOutAnimator);
+                endAll(mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator);
             }
+            return;
+        }
+        // StateGraph: 7<->5 4<->3 2->3
+        mControlVisible = show;
+        if (!mControlVisible) {
+            // StateGraph 2->3
+            stopFadeTimer();
         }
 
-        mFadingStatus = fadeIn ? IN : OUT;
+        mAnimationTranslateY = (getVerticalGridView() == null
+                || getVerticalGridView().getSelectedPosition() == 0)
+                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
+
+        if (show) {
+            reverseFirstOrStartSecond(mBgFadeOutAnimator, mBgFadeInAnimator, animation);
+            reverseFirstOrStartSecond(mControlRowFadeOutAnimator, mControlRowFadeInAnimator,
+                    animation);
+            reverseFirstOrStartSecond(mOtherRowFadeOutAnimator, mOtherRowFadeInAnimator, animation);
+        } else {
+            reverseFirstOrStartSecond(mBgFadeInAnimator, mBgFadeOutAnimator, animation);
+            reverseFirstOrStartSecond(mControlRowFadeInAnimator, mControlRowFadeOutAnimator,
+                    animation);
+            reverseFirstOrStartSecond(mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator, animation);
+        }
+        if (animation) {
+            getView().announceForAccessibility(getString(show
+                    ? R.string.lb_playback_controls_shown
+                    : R.string.lb_playback_controls_hidden));
+        }
     }
 
     /**
@@ -711,7 +797,7 @@
     }
 
     private void updateBackground() {
-        if (mRootView != null) {
+        if (mBackgroundView != null) {
             int color = mBgDarkColor;
             switch (mBackgroundType) {
                 case BG_DARK:
@@ -723,7 +809,8 @@
                     color = Color.TRANSPARENT;
                     break;
             }
-            mRootView.setBackground(new ColorDrawable(color));
+            mBackgroundView.setBackground(new ColorDrawable(color));
+            setBgAlpha(mBgAlpha);
         }
     }
 
@@ -732,13 +819,21 @@
                 @Override
                 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
                     if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view);
-                    if ((mFadingStatus == IDLE && mBgAlpha == 0) || mFadingStatus == OUT) {
+                    if (!mControlVisible) {
                         if (DEBUG) Log.v(TAG, "setting alpha to 0");
                         vh.getViewHolder().view.setAlpha(0);
                     }
                 }
 
                 @Override
+                public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
+                    Presenter.ViewHolder viewHolder = vh.getViewHolder();
+                    if (viewHolder instanceof PlaybackSeekUi) {
+                        ((PlaybackSeekUi) viewHolder).setPlaybackSeekUiClient(mChainedClient);
+                    }
+                }
+
+                @Override
                 public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
                     if (DEBUG) Log.v(TAG, "onDetachedFromWindow " + vh.getViewHolder().view);
                     // Reset animation state
@@ -756,6 +851,7 @@
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
         mRootView = inflater.inflate(R.layout.lb_playback_fragment, container, false);
+        mBackgroundView = mRootView.findViewById(R.id.playback_fragment_background);
         mRowsFragment = (RowsFragment) getChildFragmentManager().findFragmentById(
                 R.id.playback_controls_dock);
         if (mRowsFragment == null) {
@@ -775,6 +871,10 @@
         mBgAlpha = 255;
         updateBackground();
         mRowsFragment.setExternalAdapterListener(mAdapterListener);
+        ProgressBarManager progressBarManager = getProgressBarManager();
+        if (progressBarManager != null) {
+            progressBarManager.setRootView((ViewGroup) mRootView);
+        }
         return mRootView;
     }
 
@@ -809,6 +909,12 @@
         if (mHostCallback != null) {
             mHostCallback.onHostPause();
         }
+        if (mHandler.hasMessages(START_FADE_OUT)) {
+            // StateGraph: 2->1
+            mHandler.removeMessages(START_FADE_OUT);
+        } else {
+            // StateGraph: 5->6, 7->6, 4->1, 3->1
+        }
         super.onPause();
     }
 
@@ -839,6 +945,7 @@
     @Override
     public void onDestroyView() {
         mRootView = null;
+        mBackgroundView = null;
         super.onDestroyView();
     }
 
@@ -952,36 +1059,111 @@
         }
     }
 
-    static abstract class AnimatorListener implements Animator.AnimatorListener {
-        ArrayList<View> mViews = new ArrayList<View>();
-        ArrayList<Integer> mLayerType = new ArrayList<Integer>();
-
+    final PlaybackSeekUi.Client mChainedClient = new PlaybackSeekUi.Client() {
         @Override
-        public void onAnimationCancel(Animator animation) {
+        public boolean isSeekEnabled() {
+            return mSeekUiClient == null ? false : mSeekUiClient.isSeekEnabled();
         }
 
         @Override
-        public void onAnimationRepeat(Animator animation) {
+        public void onSeekStarted() {
+            if (mSeekUiClient != null) {
+                mSeekUiClient.onSeekStarted();
+            }
+            setSeekMode(true);
         }
 
         @Override
-        public void onAnimationStart(Animator animation) {
-            getViews(mViews);
-            for (View view : mViews) {
-                mLayerType.add(view.getLayerType());
-                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
+            return mSeekUiClient == null ? null : mSeekUiClient.getPlaybackSeekDataProvider();
+        }
+
+        @Override
+        public void onSeekPositionChanged(long pos) {
+            if (mSeekUiClient != null) {
+                mSeekUiClient.onSeekPositionChanged(pos);
             }
         }
 
         @Override
-        public void onAnimationEnd(Animator animation) {
-            for (int i = 0; i < mViews.size(); i++) {
-                mViews.get(i).setLayerType(mLayerType.get(i), null);
+        public void onSeekFinished(boolean cancelled) {
+            if (mSeekUiClient != null) {
+                mSeekUiClient.onSeekFinished(cancelled);
             }
-            mLayerType.clear();
-            mViews.clear();
+            setSeekMode(false);
         }
+    };
 
-        abstract void getViews(ArrayList<View> views);
+    /**
+     * Interface to be implemented by UI widget to support PlaybackSeekUi.
+     */
+    public void setPlaybackSeekUiClient(PlaybackSeekUi.Client client) {
+        mSeekUiClient = client;
+    }
+
+    /**
+     * Show or hide other rows other than PlaybackRow.
+     * @param inSeek True to make other rows visible, false to make other rows invisible.
+     */
+    void setSeekMode(boolean inSeek) {
+        if (mInSeek == inSeek) {
+            return;
+        }
+        mInSeek = inSeek;
+        getVerticalGridView().setSelectedPosition(0);
+        if (mInSeek) {
+            stopFadeTimer();
+        }
+        // immediately fade in control row.
+        showControlsOverlay(true);
+        final int count = getVerticalGridView().getChildCount();
+        for (int i = 0; i < count; i++) {
+            View view = getVerticalGridView().getChildAt(i);
+            if (getVerticalGridView().getChildAdapterPosition(view) > 0) {
+                view.setVisibility(mInSeek ? View.INVISIBLE : View.VISIBLE);
+            }
+        }
+    }
+
+    /**
+     * Called when size of the video changes. App may override.
+     * @param videoWidth Intrinsic width of video
+     * @param videoHeight Intrinsic height of video
+     */
+    protected void onVideoSizeChanged(int videoWidth, int videoHeight) {
+    }
+
+    /**
+     * Called when media has start or stop buffering. App may override. The default initial state
+     * is not buffering.
+     * @param start True for buffering start, false otherwise.
+     */
+    protected void onBufferingStateChanged(boolean start) {
+        ProgressBarManager progressBarManager = getProgressBarManager();
+        if (progressBarManager != null) {
+            if (start) {
+                progressBarManager.show();
+            } else {
+                progressBarManager.hide();
+            }
+        }
+    }
+
+    /**
+     * Called when media has error. App may override.
+     * @param errorCode Optional error code for specific implementation.
+     * @param errorMessage Optional error message for specific implementation.
+     */
+    protected void onError(int errorCode, CharSequence errorMessage) {
+    }
+
+    /**
+     * Returns the ProgressBarManager that will show or hide progress bar in
+     * {@link #onBufferingStateChanged(boolean)}.
+     * @return The ProgressBarManager that will show or hide progress bar in
+     * {@link #onBufferingStateChanged(boolean)}.
+     */
+    public ProgressBarManager getProgressBarManager() {
+        return mProgressBarManager;
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
index fdaa6ef..d537c3a 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
@@ -18,6 +18,7 @@
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackSeekUi;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
@@ -27,7 +28,7 @@
  * {@link PlaybackGlueHost} implementation
  * the interaction between this class and {@link PlaybackFragment}.
  */
-public class PlaybackFragmentGlueHost extends PlaybackGlueHost {
+public class PlaybackFragmentGlueHost extends PlaybackGlueHost implements PlaybackSeekUi {
     private final PlaybackFragment mFragment;
 
     public PlaybackFragmentGlueHost(PlaybackFragment fragment) {
@@ -35,8 +36,13 @@
     }
 
     @Override
-    public void setFadingEnabled(boolean enable) {
-        mFragment.setFadingEnabled(enable);
+    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
+        mFragment.setControlsOverlayAutoHideEnabled(enabled);
+    }
+
+    @Override
+    public boolean isControlsOverlayAutoHideEnabled() {
+        return mFragment.isControlsOverlayAutoHideEnabled();
     }
 
     @Override
@@ -85,4 +91,47 @@
     public void fadeOut() {
         mFragment.fadeOut();
     }
+
+    @Override
+    public boolean isControlsOverlayVisible() {
+        return mFragment.isControlsOverlayVisible();
+    }
+
+    @Override
+    public void hideControlsOverlay(boolean runAnimation) {
+        mFragment.hideControlsOverlay(runAnimation);
+    }
+
+    @Override
+    public void showControlsOverlay(boolean runAnimation) {
+        mFragment.showControlsOverlay(runAnimation);
+    }
+
+    @Override
+    public void setPlaybackSeekUiClient(Client client) {
+        mFragment.setPlaybackSeekUiClient(client);
+    }
+
+    final PlayerCallback mPlayerCallback =
+            new PlayerCallback() {
+                @Override
+                public void onBufferingStateChanged(boolean start) {
+                    mFragment.onBufferingStateChanged(start);
+                }
+
+                @Override
+                public void onError(int errorCode, CharSequence errorMessage) {
+                    mFragment.onError(errorCode, errorMessage);
+                }
+
+                @Override
+                public void onVideoSizeChanged(int videoWidth, int videoHeight) {
+                    mFragment.onVideoSizeChanged(videoWidth, videoHeight);
+                }
+            };
+
+    @Override
+    public PlayerCallback getPlayerCallback() {
+        return mPlayerCallback;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index c934f6b..d4b532b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -96,7 +96,7 @@
         }
     }
 
-    static final String TAG = "PlaybackOverlayFragment";
+    static final String TAG = "PlaybackOF";
     static final boolean DEBUG = false;
     private static final int ANIMATION_MULTIPLIER = 1;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index ecabdaa..d751320 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -99,7 +99,7 @@
         }
     }
 
-    static final String TAG = "PlaybackOverlaySupportFragment";
+    static final String TAG = "PlaybackOF";
     static final boolean DEBUG = false;
     private static final int ANIMATION_MULTIPLIER = 1;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
index 81e76a6..d63e72c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
@@ -40,6 +40,8 @@
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackSeekDataProvider;
+import android.support.v17.leanback.widget.PlaybackSeekUi;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.Row;
@@ -56,8 +58,6 @@
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 
-import java.util.ArrayList;
-
 /**
  * A fragment for displaying playback controls and related content.
  *
@@ -74,8 +74,15 @@
  * optional, app can pass playback row and PlaybackRowPresenter in the adapter using
  * {@link #setAdapter(ObjectAdapter)}.
  * </p>
+ * <p>
+ * Auto hide controls upon playing: best practice is calling
+ * {@link #setControlsOverlayAutoHideEnabled(boolean)} upon play/pause. The auto hiding timer will
+ * be cancelled upon {@link #tickle()} triggered by input event.
+ * </p>
  */
 public class PlaybackSupportFragment extends Fragment {
+    static final String BUNDLE_CONTROL_VISIBLE_ON_CREATEVIEW = "controlvisible_oncreateview";
+
     /**
      * No background.
      */
@@ -87,6 +94,10 @@
     public static final int BG_DARK = 1;
     PlaybackGlueHost.HostCallback mHostCallback;
 
+    PlaybackSeekUi.Client mSeekUiClient;
+    boolean mInSeek;
+    ProgressBarManager mProgressBarManager = new ProgressBarManager();
+
     /**
      * Resets the focus on the button in the middle of control row.
      * @hide
@@ -185,12 +196,12 @@
 
     // Fading status
     private static final int IDLE = 0;
-    private static final int IN = 1;
-    private static final int OUT = 2;
+    private static final int ANIMATING = 1;
 
     int mPaddingBottom;
     int mOtherRowsCenterToBottom;
     View mRootView;
+    View mBackgroundView;
     int mBackgroundType = BG_DARK;
     int mBgDarkColor;
     int mBgLightColor;
@@ -200,7 +211,8 @@
     OnFadeCompleteListener mFadeCompleteListener;
     View.OnKeyListener mInputEventHandler;
     boolean mFadingEnabled = true;
-    int mFadingStatus = IDLE;
+    boolean mControlVisibleBeforeOnCreateView = true;
+    boolean mControlVisible = true;
     int mBgAlpha;
     ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator;
     ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator;
@@ -226,7 +238,6 @@
                     if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha);
                     if (mBgAlpha > 0) {
                         enableVerticalGridAnimations(true);
-                        startFadeTimer();
                         if (mFadeCompleteListener != null) {
                             mFadeCompleteListener.onFadeInComplete();
                         }
@@ -245,10 +256,13 @@
                             mFadeCompleteListener.onFadeOutComplete();
                         }
                     }
-                    mFadingStatus = IDLE;
                 }
             };
 
+    public PlaybackSupportFragment() {
+        mProgressBarManager.setInitialDelay(500);
+    }
+
     VerticalGridView getVerticalGridView() {
         if (mRowsSupportFragment == null) {
             return null;
@@ -260,7 +274,7 @@
         @Override
         public void handleMessage(Message message) {
             if (message.what == START_FADE_OUT && mFadingEnabled) {
-                fade(false);
+                hideControlsOverlay(true);
             }
         }
     };
@@ -283,8 +297,8 @@
 
     private void setBgAlpha(int alpha) {
         mBgAlpha = alpha;
-        if (mRootView != null) {
-            mRootView.getBackground().setAlpha(alpha);
+        if (mBackgroundView != null) {
+            mBackgroundView.getBackground().setAlpha(alpha);
         }
     }
 
@@ -295,36 +309,54 @@
     }
 
     /**
-     * Enables or disables view fading.  If enabled,
-     * the view will be faded in when the fragment starts,
-     * and will fade out after a time period.  The timeout
-     * period is reset each time {@link #tickle} is called.
+     * Enables or disables auto hiding controls overlay after a short delay fragment is resumed.
+     * If enabled and fragment is resumed, the view will fade out after a time period.
+     * {@link #tickle()} will kill the timer, next time fragment is resumed,
+     * the timer will be started again if {@link #isControlsOverlayAutoHideEnabled()} is true.
      */
-    public void setFadingEnabled(boolean enabled) {
-        if (DEBUG) Log.v(TAG, "setFadingEnabled " + enabled);
+    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
+        if (DEBUG) Log.v(TAG, "setControlsOverlayAutoHideEnabled " + enabled);
         if (enabled != mFadingEnabled) {
             mFadingEnabled = enabled;
-            if (mFadingEnabled) {
-                if (isResumed() && mFadingStatus == IDLE
-                        && !mHandler.hasMessages(START_FADE_OUT)) {
+            if (isResumed() && getView().hasFocus()) {
+                showControlsOverlay(true);
+                if (enabled) {
+                    // StateGraph 7->2 5->2
                     startFadeTimer();
+                } else {
+                    // StateGraph 4->5 2->5
+                    stopFadeTimer();
                 }
             } else {
-                // Ensure fully opaque
-                mHandler.removeMessages(START_FADE_OUT);
-                fade(true);
+                // StateGraph 6->1 1->6
             }
         }
     }
 
     /**
-     * Returns true if view fading is enabled.
+     * Returns true if controls will be auto hidden after a delay when fragment is resumed.
      */
-    public boolean isFadingEnabled() {
+    public boolean isControlsOverlayAutoHideEnabled() {
         return mFadingEnabled;
     }
 
     /**
+     * @deprecated Uses {@link #setControlsOverlayAutoHideEnabled(boolean)}
+     */
+    @Deprecated
+    public void setFadingEnabled(boolean enabled) {
+        setControlsOverlayAutoHideEnabled(enabled);
+    }
+
+    /**
+     * @deprecated Uses {@link #isControlsOverlayAutoHideEnabled()}
+     */
+    @Deprecated
+    public boolean isFadingEnabled() {
+        return isControlsOverlayAutoHideEnabled();
+    }
+
+    /**
      * Sets the listener to be called when fade in or out has completed.
      * @hide
      */
@@ -348,46 +380,29 @@
     }
 
     /**
-     * Tickles the playback controls.  Fades in the view if it was faded out,
-     * otherwise resets the fade out timer.  Tickling on input events is handled
-     * by the fragment.
+     * Tickles the playback controls. Fades in the view if it was faded out. {@link #tickle()} will
+     * also kill the timer created by {@link #setControlsOverlayAutoHideEnabled(boolean)}. When
+     * next time fragment is resumed, the timer will be started again if
+     * {@link #isControlsOverlayAutoHideEnabled()} is true. In most cases app does not need call
+     * this method, tickling on input events is handled by the fragment.
      */
     public void tickle() {
         if (DEBUG) Log.v(TAG, "tickle enabled " + mFadingEnabled + " isResumed " + isResumed());
-        if (!mFadingEnabled || !isResumed()) {
-            return;
-        }
-        if (mHandler.hasMessages(START_FADE_OUT)) {
-            // Restart the timer
-            startFadeTimer();
-        } else {
-            fade(true);
-        }
-    }
-
-    /**
-     * Fades out the playback overlay immediately.
-     */
-    public void fadeOut() {
-        mHandler.removeMessages(START_FADE_OUT);
-        fade(false);
-    }
-
-    /**
-     * Returns true/false indicating whether playback controls are visible or not.
-     */
-    private boolean areControlsHidden() {
-        return mFadingStatus == IDLE && mBgAlpha == 0;
+        //StateGraph 2->4
+        stopFadeTimer();
+        showControlsOverlay(true);
     }
 
     private boolean onInterceptInputEvent(InputEvent event) {
-        final boolean controlsHidden = areControlsHidden();
+        final boolean controlsHidden = !mControlVisible;
         if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event);
         boolean consumeEvent = false;
         int keyCode = KeyEvent.KEYCODE_UNKNOWN;
+        int keyAction = 0;
 
         if (event instanceof KeyEvent) {
             keyCode = ((KeyEvent) event).getKeyCode();
+            keyAction = ((KeyEvent) event).getAction();
             if (mInputEventHandler != null) {
                 consumeEvent = mInputEventHandler.onKey(getView(), keyCode, (KeyEvent) event);
             }
@@ -404,34 +419,60 @@
                 if (controlsHidden) {
                     consumeEvent = true;
                 }
-                tickle();
+                if (keyAction == KeyEvent.ACTION_DOWN) {
+                    tickle();
+                }
                 break;
             case KeyEvent.KEYCODE_BACK:
             case KeyEvent.KEYCODE_ESCAPE:
-                // If fading enabled and controls are not hidden, back will be consumed to fade
+                if (mInSeek) {
+                    // when in seek, the SeekUi will handle the BACK.
+                    return false;
+                }
+                // If controls are not hidden, back will be consumed to fade
                 // them out (even if the key was consumed by the handler).
-                if (mFadingEnabled && !controlsHidden) {
+                if (!controlsHidden) {
                     consumeEvent = true;
-                    mHandler.removeMessages(START_FADE_OUT);
-                    fade(false);
-                } else if (consumeEvent) {
-                    tickle();
+
+                    if (((KeyEvent) event).getAction() == KeyEvent.ACTION_UP) {
+                        hideControlsOverlay(true);
+                    }
                 }
                 break;
             default:
                 if (consumeEvent) {
-                    tickle();
+                    if (keyAction == KeyEvent.ACTION_DOWN) {
+                        tickle();
+                    }
                 }
         }
         return consumeEvent;
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        // controls view are initially visible, make it invisible
+        // if app has called hideControlsOverlay() before view created.
+        mControlVisible = true;
+        if (!mControlVisibleBeforeOnCreateView) {
+            showControlsOverlay(false, false);
+            mControlVisibleBeforeOnCreateView = true;
+        }
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
-        if (mFadingEnabled) {
-            setBgAlpha(0);
-            fade(true);
+
+        if (mControlVisible) {
+            //StateGraph: 6->5 1->2
+            if (mFadingEnabled) {
+                // StateGraph 1->2
+                startFadeTimer();
+            }
+        } else {
+            //StateGraph: 6->7 1->3
         }
         getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
         getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
@@ -440,6 +481,12 @@
         }
     }
 
+    private void stopFadeTimer() {
+        if (mHandler != null) {
+            mHandler.removeMessages(START_FADE_OUT);
+        }
+    }
+
     private void startFadeTimer() {
         if (mHandler != null) {
             mHandler.removeMessages(START_FADE_OUT);
@@ -474,31 +521,19 @@
     private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0);
     private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0);
 
-    private View getControlRowView() {
-        if (getVerticalGridView() == null) {
-            return null;
-        }
-        RecyclerView.ViewHolder vh = getVerticalGridView().findViewHolderForPosition(0);
-        if (vh == null) {
-            return null;
-        }
-        return vh.itemView;
-    }
-
     private void loadControlRowAnimator() {
-        final AnimatorListener listener = new AnimatorListener() {
-            @Override
-            void getViews(ArrayList<View> views) {
-                View view = getControlRowView();
-                if (view != null) {
-                    views.add(view);
-                }
-            }
-        };
         final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator arg0) {
-                View view = getControlRowView();
+                if (getVerticalGridView() == null) {
+                    return;
+                }
+                RecyclerView.ViewHolder vh = getVerticalGridView()
+                        .findViewHolderForAdapterPosition(0);
+                if (vh == null) {
+                    return;
+                }
+                View view = vh.itemView;
                 if (view != null) {
                     final float fraction = (Float) arg0.getAnimatedValue();
                     if (DEBUG) Log.v(TAG, "fraction " + fraction);
@@ -511,32 +546,15 @@
         Context context = getContext();
         mControlRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
         mControlRowFadeInAnimator.addUpdateListener(updateListener);
-        mControlRowFadeInAnimator.addListener(listener);
         mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
 
         mControlRowFadeOutAnimator = loadAnimator(context,
                 R.animator.lb_playback_controls_fade_out);
         mControlRowFadeOutAnimator.addUpdateListener(updateListener);
-        mControlRowFadeOutAnimator.addListener(listener);
         mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator);
     }
 
     private void loadOtherRowAnimator() {
-        final AnimatorListener listener = new AnimatorListener() {
-            @Override
-            void getViews(ArrayList<View> views) {
-                if (getVerticalGridView() == null) {
-                    return;
-                }
-                final int count = getVerticalGridView().getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View view = getVerticalGridView().getChildAt(i);
-                    if (view != null) {
-                        views.add(view);
-                    }
-                }
-            }
-        };
         final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator arg0) {
@@ -544,8 +562,10 @@
                     return;
                 }
                 final float fraction = (Float) arg0.getAnimatedValue();
-                for (View view : listener.mViews) {
-                    if (getVerticalGridView().getChildPosition(view) > 0) {
+                final int count = getVerticalGridView().getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View view = getVerticalGridView().getChildAt(i);
+                    if (getVerticalGridView().getChildAdapterPosition(view) > 0) {
                         view.setAlpha(fraction);
                         view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
                     }
@@ -555,67 +575,133 @@
 
         Context context = getContext();
         mOtherRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
-        mOtherRowFadeInAnimator.addListener(listener);
         mOtherRowFadeInAnimator.addUpdateListener(updateListener);
         mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
 
         mOtherRowFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_out);
-        mOtherRowFadeOutAnimator.addListener(listener);
         mOtherRowFadeOutAnimator.addUpdateListener(updateListener);
         mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator());
     }
 
-    private void fade(boolean fadeIn) {
-        if (DEBUG) Log.v(TAG, "fade " + fadeIn);
-        if (getView() == null) {
-            return;
-        }
-        if ((fadeIn && mFadingStatus == IN) || (!fadeIn && mFadingStatus == OUT)) {
-            if (DEBUG) Log.v(TAG, "requested fade in progress");
-            return;
-        }
-        if ((fadeIn && mBgAlpha == 255) || (!fadeIn && mBgAlpha == 0)) {
-            if (DEBUG) Log.v(TAG, "fade is no-op");
-            return;
-        }
+    /**
+     * Fades out the playback overlay immediately.
+     * @deprecated Call {@link #hideControlsOverlay(boolean)}
+     */
+    @Deprecated
+    public void fadeOut() {
+        showControlsOverlay(false, false);
+    }
 
-        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0
-                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
+    /**
+     * Show controls overlay.
+     *
+     * @param runAnimation True to run animation, false otherwise.
+     */
+    public void showControlsOverlay(boolean runAnimation) {
+        showControlsOverlay(true, runAnimation);
+    }
 
-        if (mFadingStatus == IDLE) {
-            if (fadeIn) {
-                mBgFadeInAnimator.start();
-                mControlRowFadeInAnimator.start();
-                mOtherRowFadeInAnimator.start();
-            } else {
-                mBgFadeOutAnimator.start();
-                mControlRowFadeOutAnimator.start();
-                mOtherRowFadeOutAnimator.start();
+    /**
+     * Returns true if controls overlay is visible, false otherwise.
+     *
+     * @return True if controls overlay is visible, false otherwise.
+     * @see #showControlsOverlay(boolean)
+     * @see #hideControlsOverlay(boolean)
+     */
+    public boolean isControlsOverlayVisible() {
+        return mControlVisible;
+    }
+
+    /**
+     * Hide controls overlay.
+     *
+     * @param runAnimation True to run animation, false otherwise.
+     */
+    public void hideControlsOverlay(boolean runAnimation) {
+        showControlsOverlay(false, runAnimation);
+    }
+
+    /**
+     * if first animator is still running, reverse it; otherwise start second animator.
+     */
+    static void reverseFirstOrStartSecond(ValueAnimator first, ValueAnimator second,
+            boolean runAnimation) {
+        if (first.isStarted()) {
+            first.reverse();
+            if (!runAnimation) {
+                first.end();
             }
         } else {
-            if (fadeIn) {
-                mBgFadeOutAnimator.reverse();
-                mControlRowFadeOutAnimator.reverse();
-                mOtherRowFadeOutAnimator.reverse();
-            } else {
-                mBgFadeInAnimator.reverse();
-                mControlRowFadeInAnimator.reverse();
-                mOtherRowFadeInAnimator.reverse();
+            second.start();
+            if (!runAnimation) {
+                second.end();
             }
         }
-        getView().announceForAccessibility(getString(fadeIn ? R.string.lb_playback_controls_shown
-                : R.string.lb_playback_controls_hidden));
+    }
 
-        // If fading in while control row is focused, set initial translationY so
-        // views slide in from below.
-        if (fadeIn && mFadingStatus == IDLE) {
-            final int count = getVerticalGridView().getChildCount();
-            for (int i = 0; i < count; i++) {
-                getVerticalGridView().getChildAt(i).setTranslationY(mAnimationTranslateY);
+    /**
+     * End first or second animator if they are still running.
+     */
+    static void endAll(ValueAnimator first, ValueAnimator second) {
+        if (first.isStarted()) {
+            first.end();
+        } else if (second.isStarted()) {
+            second.end();
+        }
+    }
+
+    /**
+     * Fade in or fade out rows and background.
+     *
+     * @param show True to fade in, false to fade out.
+     * @param animation True to run animation.
+     */
+    void showControlsOverlay(boolean show, boolean animation) {
+        if (DEBUG) Log.v(TAG, "showControlsOverlay " + show);
+        if (getView() == null) {
+            mControlVisibleBeforeOnCreateView = show;
+            return;
+        }
+        // force no animation when fragment is not resumed
+        if (!isResumed()) {
+            animation = false;
+        }
+        if (show == mControlVisible) {
+            if (!animation) {
+                // End animation if needed
+                endAll(mBgFadeInAnimator, mBgFadeOutAnimator);
+                endAll(mControlRowFadeInAnimator, mControlRowFadeOutAnimator);
+                endAll(mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator);
             }
+            return;
+        }
+        // StateGraph: 7<->5 4<->3 2->3
+        mControlVisible = show;
+        if (!mControlVisible) {
+            // StateGraph 2->3
+            stopFadeTimer();
         }
 
-        mFadingStatus = fadeIn ? IN : OUT;
+        mAnimationTranslateY = (getVerticalGridView() == null
+                || getVerticalGridView().getSelectedPosition() == 0)
+                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
+
+        if (show) {
+            reverseFirstOrStartSecond(mBgFadeOutAnimator, mBgFadeInAnimator, animation);
+            reverseFirstOrStartSecond(mControlRowFadeOutAnimator, mControlRowFadeInAnimator,
+                    animation);
+            reverseFirstOrStartSecond(mOtherRowFadeOutAnimator, mOtherRowFadeInAnimator, animation);
+        } else {
+            reverseFirstOrStartSecond(mBgFadeInAnimator, mBgFadeOutAnimator, animation);
+            reverseFirstOrStartSecond(mControlRowFadeInAnimator, mControlRowFadeOutAnimator,
+                    animation);
+            reverseFirstOrStartSecond(mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator, animation);
+        }
+        if (animation) {
+            getView().announceForAccessibility(getString(show
+                    ? R.string.lb_playback_controls_shown
+                    : R.string.lb_playback_controls_hidden));
+        }
     }
 
     /**
@@ -714,7 +800,7 @@
     }
 
     private void updateBackground() {
-        if (mRootView != null) {
+        if (mBackgroundView != null) {
             int color = mBgDarkColor;
             switch (mBackgroundType) {
                 case BG_DARK:
@@ -726,7 +812,8 @@
                     color = Color.TRANSPARENT;
                     break;
             }
-            mRootView.setBackground(new ColorDrawable(color));
+            mBackgroundView.setBackground(new ColorDrawable(color));
+            setBgAlpha(mBgAlpha);
         }
     }
 
@@ -735,13 +822,21 @@
                 @Override
                 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
                     if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view);
-                    if ((mFadingStatus == IDLE && mBgAlpha == 0) || mFadingStatus == OUT) {
+                    if (!mControlVisible) {
                         if (DEBUG) Log.v(TAG, "setting alpha to 0");
                         vh.getViewHolder().view.setAlpha(0);
                     }
                 }
 
                 @Override
+                public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
+                    Presenter.ViewHolder viewHolder = vh.getViewHolder();
+                    if (viewHolder instanceof PlaybackSeekUi) {
+                        ((PlaybackSeekUi) viewHolder).setPlaybackSeekUiClient(mChainedClient);
+                    }
+                }
+
+                @Override
                 public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
                     if (DEBUG) Log.v(TAG, "onDetachedFromWindow " + vh.getViewHolder().view);
                     // Reset animation state
@@ -759,6 +854,7 @@
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
         mRootView = inflater.inflate(R.layout.lb_playback_fragment, container, false);
+        mBackgroundView = mRootView.findViewById(R.id.playback_fragment_background);
         mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
                 R.id.playback_controls_dock);
         if (mRowsSupportFragment == null) {
@@ -778,6 +874,10 @@
         mBgAlpha = 255;
         updateBackground();
         mRowsSupportFragment.setExternalAdapterListener(mAdapterListener);
+        ProgressBarManager progressBarManager = getProgressBarManager();
+        if (progressBarManager != null) {
+            progressBarManager.setRootView((ViewGroup) mRootView);
+        }
         return mRootView;
     }
 
@@ -812,6 +912,12 @@
         if (mHostCallback != null) {
             mHostCallback.onHostPause();
         }
+        if (mHandler.hasMessages(START_FADE_OUT)) {
+            // StateGraph: 2->1
+            mHandler.removeMessages(START_FADE_OUT);
+        } else {
+            // StateGraph: 5->6, 7->6, 4->1, 3->1
+        }
         super.onPause();
     }
 
@@ -842,6 +948,7 @@
     @Override
     public void onDestroyView() {
         mRootView = null;
+        mBackgroundView = null;
         super.onDestroyView();
     }
 
@@ -955,36 +1062,111 @@
         }
     }
 
-    static abstract class AnimatorListener implements Animator.AnimatorListener {
-        ArrayList<View> mViews = new ArrayList<View>();
-        ArrayList<Integer> mLayerType = new ArrayList<Integer>();
-
+    final PlaybackSeekUi.Client mChainedClient = new PlaybackSeekUi.Client() {
         @Override
-        public void onAnimationCancel(Animator animation) {
+        public boolean isSeekEnabled() {
+            return mSeekUiClient == null ? false : mSeekUiClient.isSeekEnabled();
         }
 
         @Override
-        public void onAnimationRepeat(Animator animation) {
+        public void onSeekStarted() {
+            if (mSeekUiClient != null) {
+                mSeekUiClient.onSeekStarted();
+            }
+            setSeekMode(true);
         }
 
         @Override
-        public void onAnimationStart(Animator animation) {
-            getViews(mViews);
-            for (View view : mViews) {
-                mLayerType.add(view.getLayerType());
-                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
+            return mSeekUiClient == null ? null : mSeekUiClient.getPlaybackSeekDataProvider();
+        }
+
+        @Override
+        public void onSeekPositionChanged(long pos) {
+            if (mSeekUiClient != null) {
+                mSeekUiClient.onSeekPositionChanged(pos);
             }
         }
 
         @Override
-        public void onAnimationEnd(Animator animation) {
-            for (int i = 0; i < mViews.size(); i++) {
-                mViews.get(i).setLayerType(mLayerType.get(i), null);
+        public void onSeekFinished(boolean cancelled) {
+            if (mSeekUiClient != null) {
+                mSeekUiClient.onSeekFinished(cancelled);
             }
-            mLayerType.clear();
-            mViews.clear();
+            setSeekMode(false);
         }
+    };
 
-        abstract void getViews(ArrayList<View> views);
+    /**
+     * Interface to be implemented by UI widget to support PlaybackSeekUi.
+     */
+    public void setPlaybackSeekUiClient(PlaybackSeekUi.Client client) {
+        mSeekUiClient = client;
+    }
+
+    /**
+     * Show or hide other rows other than PlaybackRow.
+     * @param inSeek True to make other rows visible, false to make other rows invisible.
+     */
+    void setSeekMode(boolean inSeek) {
+        if (mInSeek == inSeek) {
+            return;
+        }
+        mInSeek = inSeek;
+        getVerticalGridView().setSelectedPosition(0);
+        if (mInSeek) {
+            stopFadeTimer();
+        }
+        // immediately fade in control row.
+        showControlsOverlay(true);
+        final int count = getVerticalGridView().getChildCount();
+        for (int i = 0; i < count; i++) {
+            View view = getVerticalGridView().getChildAt(i);
+            if (getVerticalGridView().getChildAdapterPosition(view) > 0) {
+                view.setVisibility(mInSeek ? View.INVISIBLE : View.VISIBLE);
+            }
+        }
+    }
+
+    /**
+     * Called when size of the video changes. App may override.
+     * @param videoWidth Intrinsic width of video
+     * @param videoHeight Intrinsic height of video
+     */
+    protected void onVideoSizeChanged(int videoWidth, int videoHeight) {
+    }
+
+    /**
+     * Called when media has start or stop buffering. App may override. The default initial state
+     * is not buffering.
+     * @param start True for buffering start, false otherwise.
+     */
+    protected void onBufferingStateChanged(boolean start) {
+        ProgressBarManager progressBarManager = getProgressBarManager();
+        if (progressBarManager != null) {
+            if (start) {
+                progressBarManager.show();
+            } else {
+                progressBarManager.hide();
+            }
+        }
+    }
+
+    /**
+     * Called when media has error. App may override.
+     * @param errorCode Optional error code for specific implementation.
+     * @param errorMessage Optional error message for specific implementation.
+     */
+    protected void onError(int errorCode, CharSequence errorMessage) {
+    }
+
+    /**
+     * Returns the ProgressBarManager that will show or hide progress bar in
+     * {@link #onBufferingStateChanged(boolean)}.
+     * @return The ProgressBarManager that will show or hide progress bar in
+     * {@link #onBufferingStateChanged(boolean)}.
+     */
+    public ProgressBarManager getProgressBarManager() {
+        return mProgressBarManager;
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
index da644ae..cdf3f97 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
@@ -21,6 +21,7 @@
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackSeekUi;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
@@ -30,7 +31,7 @@
  * {@link PlaybackGlueHost} implementation
  * the interaction between this class and {@link PlaybackSupportFragment}.
  */
-public class PlaybackSupportFragmentGlueHost extends PlaybackGlueHost {
+public class PlaybackSupportFragmentGlueHost extends PlaybackGlueHost implements PlaybackSeekUi {
     private final PlaybackSupportFragment mFragment;
 
     public PlaybackSupportFragmentGlueHost(PlaybackSupportFragment fragment) {
@@ -38,8 +39,13 @@
     }
 
     @Override
-    public void setFadingEnabled(boolean enable) {
-        mFragment.setFadingEnabled(enable);
+    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
+        mFragment.setControlsOverlayAutoHideEnabled(enabled);
+    }
+
+    @Override
+    public boolean isControlsOverlayAutoHideEnabled() {
+        return mFragment.isControlsOverlayAutoHideEnabled();
     }
 
     @Override
@@ -88,4 +94,47 @@
     public void fadeOut() {
         mFragment.fadeOut();
     }
+
+    @Override
+    public boolean isControlsOverlayVisible() {
+        return mFragment.isControlsOverlayVisible();
+    }
+
+    @Override
+    public void hideControlsOverlay(boolean runAnimation) {
+        mFragment.hideControlsOverlay(runAnimation);
+    }
+
+    @Override
+    public void showControlsOverlay(boolean runAnimation) {
+        mFragment.showControlsOverlay(runAnimation);
+    }
+
+    @Override
+    public void setPlaybackSeekUiClient(Client client) {
+        mFragment.setPlaybackSeekUiClient(client);
+    }
+
+    final PlayerCallback mPlayerCallback =
+            new PlayerCallback() {
+                @Override
+                public void onBufferingStateChanged(boolean start) {
+                    mFragment.onBufferingStateChanged(start);
+                }
+
+                @Override
+                public void onError(int errorCode, CharSequence errorMessage) {
+                    mFragment.onError(errorCode, errorMessage);
+                }
+
+                @Override
+                public void onVideoSizeChanged(int videoWidth, int videoHeight) {
+                    mFragment.onVideoSizeChanged(videoWidth, videoHeight);
+                }
+            };
+
+    @Override
+    public PlayerCallback getPlayerCallback() {
+        return mPlayerCallback;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/ProgressBarManager.java b/v17/leanback/src/android/support/v17/leanback/app/ProgressBarManager.java
index c9a2b87..895ed6b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/ProgressBarManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/ProgressBarManager.java
@@ -78,6 +78,7 @@
             mProgressBarView.setVisibility(View.INVISIBLE);
         } else if (mProgressBarView != null) {
             rootView.removeView(mProgressBarView);
+            mProgressBarView = null;
         }
 
         mHandler.removeCallbacks(runnable);
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
index fe0e26f..dd0dbed 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
@@ -141,6 +141,7 @@
     boolean mViewsCreated;
     private int mAlignedTop = ALIGN_TOP_NOT_SET;
     boolean mAfterEntranceTransition = true;
+    boolean mFreezeRows;
 
     BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
     BaseOnItemViewClickedListener mOnItemViewClickedListener;
@@ -367,6 +368,10 @@
             RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
             RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
             rowPresenter.setEntranceTransitionState(rowVh, mAfterEntranceTransition);
+
+            // freeze the rows attached after RowsFragment#freezeRows() is called
+            rowPresenter.freeze(rowVh, mFreezeRows);
+
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onAttachedToWindow(vh);
             }
@@ -450,6 +455,7 @@
     }
 
     private void freezeRows(boolean freeze) {
+        mFreezeRows = freeze;
         VerticalGridView verticalView = getVerticalGridView();
         if (verticalView != null) {
             final int count = verticalView.getChildCount();
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
index 9f55aa2..c00f78b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
@@ -144,6 +144,7 @@
     boolean mViewsCreated;
     private int mAlignedTop = ALIGN_TOP_NOT_SET;
     boolean mAfterEntranceTransition = true;
+    boolean mFreezeRows;
 
     BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
     BaseOnItemViewClickedListener mOnItemViewClickedListener;
@@ -370,6 +371,10 @@
             RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
             RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
             rowPresenter.setEntranceTransitionState(rowVh, mAfterEntranceTransition);
+
+            // freeze the rows attached after RowsSupportFragment#freezeRows() is called
+            rowPresenter.freeze(rowVh, mFreezeRows);
+
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onAttachedToWindow(vh);
             }
@@ -453,6 +458,7 @@
     }
 
     private void freezeRows(boolean freeze) {
+        mFreezeRows = freeze;
         VerticalGridView verticalView = getVerticalGridView();
         if (verticalView != null) {
             final int count = verticalView.getChildCount();
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
index d65937c..acf4745 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
@@ -237,6 +237,7 @@
         }
     };
 
+    @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                            int[] grantResults) {
         if (requestCode == AUDIO_PERMISSION_REQUEST_CODE && permissions.length > 0) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
index ae4c700..36b560d 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
@@ -240,6 +240,7 @@
         }
     };
 
+    @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                            int[] grantResults) {
         if (requestCode == AUDIO_PERMISSION_REQUEST_CODE && permissions.length > 0) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
index 9e80dfc..5cf5799 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -38,7 +38,7 @@
  * an {@link ObjectAdapter}.
  */
 public class VerticalGridFragment extends BaseFragment {
-    static final String TAG = "VerticalGridFragment";
+    static final String TAG = "VerticalGF";
     static boolean DEBUG = false;
 
     private ObjectAdapter mAdapter;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
index 6327790..a38bac5 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
@@ -41,7 +41,7 @@
  * an {@link ObjectAdapter}.
  */
 public class VerticalGridSupportFragment extends BaseSupportFragment {
-    static final String TAG = "VerticalGridSupportFragment";
+    static final String TAG = "VerticalGF";
     static boolean DEBUG = false;
 
     private ObjectAdapter mAdapter;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java
index 150e461..41241d0 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java
@@ -83,6 +83,24 @@
         }
     }
 
+    @Override
+    protected void onVideoSizeChanged(int width, int height) {
+        int screenWidth = getView().getWidth();
+        int screenHeight = getView().getHeight();
+
+        ViewGroup.LayoutParams p = mVideoSurface.getLayoutParams();
+        if (screenWidth * height > width * screenHeight) {
+            // fit in screen height
+            p.height = screenHeight;
+            p.width = screenHeight * width / height;
+        } else {
+            // fit in screen width
+            p.width = screenWidth;
+            p.height = screenWidth * height / width;
+        }
+        mVideoSurface.setLayoutParams(p);
+    }
+
     /**
      * Returns the surface view.
      */
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
index ee9a536..a64b521 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
@@ -40,4 +40,5 @@
     public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
         mFragment.setSurfaceHolderCallback(callback);
     }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java
index c12b06f..321bdbe 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java
@@ -86,6 +86,24 @@
         }
     }
 
+    @Override
+    protected void onVideoSizeChanged(int width, int height) {
+        int screenWidth = getView().getWidth();
+        int screenHeight = getView().getHeight();
+
+        ViewGroup.LayoutParams p = mVideoSurface.getLayoutParams();
+        if (screenWidth * height > width * screenHeight) {
+            // fit in screen height
+            p.height = screenHeight;
+            p.width = screenHeight * width / height;
+        } else {
+            // fit in screen width
+            p.width = screenWidth;
+            p.height = screenWidth * height / width;
+        }
+        mVideoSurface.setLayoutParams(p);
+    }
+
     /**
      * Returns the surface view.
      */
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
index 67150a0..28f919b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
@@ -43,4 +43,5 @@
     public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
         mFragment.setSurfaceHolderCallback(callback);
     }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java b/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
index 5788393..4b68e69 100644
--- a/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
@@ -15,14 +15,12 @@
  */
 package android.support.v17.leanback.graphics;
 
-import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.v17.leanback.graphics.BoundsRule.ValueRule;
 import android.support.v4.graphics.drawable.DrawableCompat;
@@ -191,6 +189,7 @@
     /**
      * @return Alpha value between 0(inclusive) and 255(inclusive)
      */
+    @Override
     public int getAlpha() {
         final Drawable dr = getFirstNonNullDrawable();
         if (dr != null) {
@@ -241,7 +240,6 @@
      * Wrapper class holding a drawable object and {@link BoundsRule} to update drawable bounds
      * when parent bound changes.
      */
-    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     public static final class ChildDrawable {
         private final BoundsRule mBoundsRule;
         private final Drawable mDrawable;
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java b/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
index 910f313..6300ff2 100644
--- a/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
@@ -15,7 +15,7 @@
  */
 package android.support.v17.leanback.graphics;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
@@ -176,6 +176,7 @@
     /**
      * @return Alpha value between 0(inclusive) and 255(inclusive)
      */
+    @Override
     public int getAlpha() {
         return mBitmapState.mPaint.getAlpha();
     }
@@ -214,10 +215,12 @@
             // use Property
             PROPERTY_VERTICAL_OFFSET = new Property<FitWidthBitmapDrawable, Integer>(Integer.class,
                     "verticalOffset") {
+                @Override
                 public void set(FitWidthBitmapDrawable object, Integer value) {
                     object.setVerticalOffset(value);
                 }
 
+                @Override
                 public Integer get(FitWidthBitmapDrawable object) {
                     return object.getVerticalOffset();
                 }
@@ -225,9 +228,10 @@
         }
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     static IntProperty<FitWidthBitmapDrawable> getVerticalOffsetIntProperty() {
         return new IntProperty<FitWidthBitmapDrawable>("verticalOffset") {
+            @Override
             public void setValue(FitWidthBitmapDrawable fitWidthBitmapDrawable, int value) {
                 fitWidthBitmapDrawable.setVerticalOffset(value);
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerAdapter.java b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerAdapter.java
new file mode 100644
index 0000000..3de7aa1
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerAdapter.java
@@ -0,0 +1,395 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.v17.leanback.R;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+
+/**
+ * This implementation extends the {@link PlayerAdapter} with a {@link MediaPlayer}.
+ */
+public class MediaPlayerAdapter extends PlayerAdapter {
+
+    Context mContext;
+    final MediaPlayer mPlayer = new MediaPlayer();
+    SurfaceHolderGlueHost mSurfaceHolderGlueHost;
+    final Runnable mRunnable = new Runnable() {
+        @Override
+        public void run() {
+            getCallback().onCurrentPositionChanged(MediaPlayerAdapter.this);
+            mHandler.postDelayed(this, getUpdatePeriod());
+        }
+    };;
+    final Handler mHandler = new Handler();
+    boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
+    Uri mMediaSourceUri = null;
+    boolean mHasDisplay;
+    long mBufferedProgress;
+
+    MediaPlayer.OnPreparedListener mOnPreparedListener = new MediaPlayer.OnPreparedListener() {
+        @Override
+        public void onPrepared(MediaPlayer mp) {
+            mInitialized = true;
+            notifyBufferingStartEnd();
+            if (mSurfaceHolderGlueHost == null || mHasDisplay) {
+                getCallback().onPreparedStateChanged(MediaPlayerAdapter.this);
+            }
+        }
+    };
+
+    final MediaPlayer.OnCompletionListener mOnCompletionListener =
+            new MediaPlayer.OnCompletionListener() {
+        @Override
+        public void onCompletion(MediaPlayer mediaPlayer) {
+            getCallback().onPlayStateChanged(MediaPlayerAdapter.this);
+            getCallback().onPlayCompleted(MediaPlayerAdapter.this);
+        }
+    };
+
+    final MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener =
+            new MediaPlayer.OnBufferingUpdateListener() {
+        @Override
+        public void onBufferingUpdate(MediaPlayer mp, int percent) {
+            mBufferedProgress = getDuration() * percent / 100;
+            getCallback().onBufferedPositionChanged(MediaPlayerAdapter.this);
+        }
+    };
+
+    final MediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener =
+            new MediaPlayer.OnVideoSizeChangedListener() {
+        @Override
+        public void onVideoSizeChanged(MediaPlayer mediaPlayer, int width, int height) {
+            getCallback().onVideoSizeChanged(MediaPlayerAdapter.this, width, height);
+        }
+    };
+
+    final MediaPlayer.OnErrorListener mOnErrorListener =
+            new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer mp, int what, int extra) {
+                    getCallback().onError(MediaPlayerAdapter.this, what,
+                            mContext.getString(R.string.lb_media_player_error, what, extra));
+                    return MediaPlayerAdapter.this.onError(what, extra);
+                }
+            };
+
+    final MediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener =
+            new MediaPlayer.OnSeekCompleteListener() {
+                @Override
+                public void onSeekComplete(MediaPlayer mp) {
+                    MediaPlayerAdapter.this.onSeekComplete();
+                }
+            };
+
+    final MediaPlayer.OnInfoListener mOnInfoListener = new MediaPlayer.OnInfoListener() {
+        @Override
+        public boolean onInfo(MediaPlayer mp, int what, int extra) {
+            boolean handled = false;
+            switch (what) {
+                case MediaPlayer.MEDIA_INFO_BUFFERING_START:
+                    mBufferingStart = true;
+                    notifyBufferingStartEnd();
+                    handled = true;
+                    break;
+                case MediaPlayer.MEDIA_INFO_BUFFERING_END:
+                    mBufferingStart = false;
+                    notifyBufferingStartEnd();
+                    handled = true;
+                    break;
+            }
+            boolean thisHandled = MediaPlayerAdapter.this.onInfo(what, extra);
+            return handled || thisHandled;
+        }
+    };
+
+    boolean mBufferingStart;
+
+    void notifyBufferingStartEnd() {
+        getCallback().onBufferingStateChanged(MediaPlayerAdapter.this,
+                mBufferingStart || !mInitialized);
+    }
+
+    /**
+     * Constructor.
+     */
+    public MediaPlayerAdapter(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void onAttachedToHost(PlaybackGlueHost host) {
+        if (host instanceof SurfaceHolderGlueHost) {
+            mSurfaceHolderGlueHost = ((SurfaceHolderGlueHost) host);
+            mSurfaceHolderGlueHost.setSurfaceHolderCallback(new VideoPlayerSurfaceHolderCallback());
+        }
+    }
+
+    /**
+     * Will reset the {@link MediaPlayer} and the glue such that a new file can be played. You are
+     * not required to call this method before playing the first file. However you have to call it
+     * before playing a second one.
+     */
+    public void reset() {
+        changeToUnitialized();
+        mPlayer.reset();
+    }
+
+    void changeToUnitialized() {
+        if (mInitialized) {
+            mInitialized = false;
+            notifyBufferingStartEnd();
+            if (mHasDisplay) {
+                getCallback().onPreparedStateChanged(MediaPlayerAdapter.this);
+            }
+        }
+    }
+
+    /**
+     * Release internal MediaPlayer. Should not use the object after call release().
+     */
+    public void release() {
+        changeToUnitialized();
+        mHasDisplay = false;
+        mPlayer.release();
+    }
+
+    @Override
+    public void onDetachedFromHost() {
+        if (mSurfaceHolderGlueHost != null) {
+            mSurfaceHolderGlueHost.setSurfaceHolderCallback(null);
+            mSurfaceHolderGlueHost = null;
+        }
+        reset();
+        release();
+    }
+
+    /**
+     * Called to indicate an error.
+     *
+     * @param what    the type of error that has occurred:
+     * <ul>
+     * <li>{@link MediaPlayer#MEDIA_ERROR_UNKNOWN}
+     * <li>{@link MediaPlayer#MEDIA_ERROR_SERVER_DIED}
+     * </ul>
+     * @param extra an extra code, specific to the error. Typically
+     * implementation dependent.
+     * <ul>
+     * <li>{@link MediaPlayer#MEDIA_ERROR_IO}
+     * <li>{@link MediaPlayer#MEDIA_ERROR_MALFORMED}
+     * <li>{@link MediaPlayer#MEDIA_ERROR_UNSUPPORTED}
+     * <li>{@link MediaPlayer#MEDIA_ERROR_TIMED_OUT}
+     * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error.
+     * </ul>
+     * @return True if the method handled the error, false if it didn't.
+     * Returning false, will cause the {@link PlayerAdapter.Callback#onPlayCompleted(PlayerAdapter)}
+     * being called.
+     */
+    protected boolean onError(int what, int extra) {
+        return false;
+    }
+
+    /**
+     * Called to indicate the completion of a seek operation.
+     */
+    protected void onSeekComplete() {
+    }
+
+    /**
+     * Called to indicate an info or a warning.
+     *
+     * @param what    the type of info or warning.
+     * <ul>
+     * <li>{@link MediaPlayer#MEDIA_INFO_UNKNOWN}
+     * <li>{@link MediaPlayer#MEDIA_INFO_VIDEO_TRACK_LAGGING}
+     * <li>{@link MediaPlayer#MEDIA_INFO_VIDEO_RENDERING_START}
+     * <li>{@link MediaPlayer#MEDIA_INFO_BUFFERING_START}
+     * <li>{@link MediaPlayer#MEDIA_INFO_BUFFERING_END}
+     * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> -
+     *     bandwidth information is available (as <code>extra</code> kbps)
+     * <li>{@link MediaPlayer#MEDIA_INFO_BAD_INTERLEAVING}
+     * <li>{@link MediaPlayer#MEDIA_INFO_NOT_SEEKABLE}
+     * <li>{@link MediaPlayer#MEDIA_INFO_METADATA_UPDATE}
+     * <li>{@link MediaPlayer#MEDIA_INFO_UNSUPPORTED_SUBTITLE}
+     * <li>{@link MediaPlayer#MEDIA_INFO_SUBTITLE_TIMED_OUT}
+     * </ul>
+     * @param extra an extra code, specific to the info. Typically
+     * implementation dependent.
+     * @return True if the method handled the info, false if it didn't.
+     * Returning false, will cause the info to be discarded.
+     */
+    protected boolean onInfo(int what, int extra) {
+        return false;
+    }
+
+    /**
+     * @see MediaPlayer#setDisplay(SurfaceHolder)
+     */
+    void setDisplay(SurfaceHolder surfaceHolder) {
+        boolean hadDisplay = mHasDisplay;
+        mHasDisplay = surfaceHolder != null;
+        if (hadDisplay == mHasDisplay) {
+            return;
+        }
+        mPlayer.setDisplay(surfaceHolder);
+        if (mHasDisplay) {
+            if (mInitialized) {
+                getCallback().onPreparedStateChanged(MediaPlayerAdapter.this);
+            }
+        } else {
+            if (mInitialized) {
+                getCallback().onPreparedStateChanged(MediaPlayerAdapter.this);
+            }
+        }
+
+    }
+
+    @Override
+    public void setProgressUpdatingEnabled(final boolean enabled) {
+        mHandler.removeCallbacks(mRunnable);
+        if (!enabled) {
+            return;
+        }
+        mHandler.postDelayed(mRunnable, getUpdatePeriod());
+    }
+
+    int getUpdatePeriod() {
+        return 16;
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return mInitialized && mPlayer.isPlaying();
+    }
+
+    @Override
+    public long getDuration() {
+        return mInitialized ? mPlayer.getDuration() : -1;
+    }
+
+    @Override
+    public long getCurrentPosition() {
+        return mInitialized ? mPlayer.getCurrentPosition() : -1;
+    }
+
+    @Override
+    public void play() {
+        if (!mInitialized || mPlayer.isPlaying()) {
+            return;
+        }
+        mPlayer.start();
+        getCallback().onPlayStateChanged(MediaPlayerAdapter.this);
+        getCallback().onCurrentPositionChanged(MediaPlayerAdapter.this);
+    }
+
+    @Override
+    public void pause() {
+        if (isPlaying()) {
+            mPlayer.pause();
+            getCallback().onPlayStateChanged(MediaPlayerAdapter.this);
+        }
+    }
+
+    @Override
+    public void seekTo(long newPosition) {
+        if (!mInitialized) {
+            return;
+        }
+        mPlayer.seekTo((int) newPosition);
+    }
+
+    @Override
+    public long getBufferedPosition() {
+        return mBufferedProgress;
+    }
+
+    /**
+     * Sets the media source of the player witha given URI.
+     *
+     * @return Returns <code>true</code> if uri represents a new media; <code>false</code>
+     * otherwise.
+     * @see MediaPlayer#setDataSource(String)
+     */
+    public boolean setDataSource(Uri uri) {
+        if (mMediaSourceUri != null ? mMediaSourceUri.equals(uri) : uri == null) {
+            return false;
+        }
+        mMediaSourceUri = uri;
+        prepareMediaForPlaying();
+        return true;
+    }
+
+    private void prepareMediaForPlaying() {
+        reset();
+        try {
+            if (mMediaSourceUri != null) {
+                mPlayer.setDataSource(mContext, mMediaSourceUri);
+            } else {
+                return;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+        mPlayer.setOnPreparedListener(mOnPreparedListener);
+        mPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
+        mPlayer.setOnErrorListener(mOnErrorListener);
+        mPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
+        mPlayer.setOnCompletionListener(mOnCompletionListener);
+        mPlayer.setOnInfoListener(mOnInfoListener);
+        mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
+        notifyBufferingStartEnd();
+        mPlayer.prepareAsync();
+        getCallback().onPlayStateChanged(MediaPlayerAdapter.this);
+    }
+
+    /**
+     * @return True if MediaPlayer OnPreparedListener is invoked and got a SurfaceHolder if
+     * {@link PlaybackGlueHost} provides SurfaceHolder.
+     */
+    @Override
+    public boolean isPrepared() {
+        return mInitialized && (mSurfaceHolderGlueHost == null || mHasDisplay);
+    }
+
+    /**
+     * Implements {@link SurfaceHolder.Callback} that can then be set on the
+     * {@link PlaybackGlueHost}.
+     */
+    class VideoPlayerSurfaceHolderCallback implements SurfaceHolder.Callback {
+        @Override
+        public void surfaceCreated(SurfaceHolder surfaceHolder) {
+            setDisplay(surfaceHolder);
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
+            setDisplay(null);
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
index 38d6c92..3a274b1 100644
--- a/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
@@ -34,6 +34,7 @@
 import android.view.View;
 
 import java.io.IOException;
+import java.util.List;
 
 /**
  * This glue extends the {@link android.support.v17.leanback.media.PlaybackControlGlue} with a
@@ -71,7 +72,6 @@
     private long mLastKeyDownEvent = 0L; // timestamp when the last DPAD_CENTER KEY_DOWN occurred
     private Uri mMediaSourceUri = null;
     private String mMediaSourcePath = null;
-    private PlayerCallback mPlayerCallback;
     private MediaPlayer.OnCompletionListener mOnCompletionListener;
     private String mArtist;
     private String mTitle;
@@ -124,8 +124,8 @@
         mRepeatAction = new PlaybackControlsRow.RepeatAction(getContext());
         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(getContext());
         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(getContext());
-        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
-        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
+        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
     }
 
     @Override
@@ -138,27 +138,32 @@
     }
 
     /**
-     * Sets the callback, which would tell the listener that video is ready to be played.
-     */
-    public void setPlayerCallback(PlayerCallback callback) {
-        this.mPlayerCallback = callback;
-    }
-
-    /**
      * Will reset the {@link MediaPlayer} and the glue such that a new file can be played. You are
      * not required to call this method before playing the first file. However you have to call it
      * before playing a second one.
      */
     public void reset() {
-        mInitialized = false;
+        changeToUnitialized();
         mPlayer.reset();
     }
 
+    void changeToUnitialized() {
+        if (mInitialized) {
+            mInitialized = false;
+            List<PlayerCallback> callbacks = getPlayerCallbacks();
+            if (callbacks != null) {
+                for (PlayerCallback callback: callbacks) {
+                    callback.onPreparedStateChanged(MediaPlayerGlue.this);
+                }
+            }
+        }
+    }
+
     /**
      * Release internal MediaPlayer. Should not use the object after call release().
      */
     public void release() {
-        mInitialized = false;
+        changeToUnitialized();
         mPlayer.release();
     }
 
@@ -210,20 +215,20 @@
         // is incremented and the UI updated such that we can display the new state.
         super.onActionClicked(action);
         if (action instanceof PlaybackControlsRow.RepeatAction) {
-            mRepeatAction.nextIndex();
-        } else if (action instanceof PlaybackControlsRow.ThumbsUpAction) {
-            if (mThumbsUpAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
-                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+            ((PlaybackControlsRow.RepeatAction) action).nextIndex();
+        } else if (action == mThumbsUpAction) {
+            if (mThumbsUpAction.getIndex() == PlaybackControlsRow.ThumbsAction.INDEX_SOLID) {
+                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
             } else {
-                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
-                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_SOLID);
+                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
             }
-        } else if (action instanceof PlaybackControlsRow.ThumbsDownAction) {
-            if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
-                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+        } else if (action == mThumbsDownAction) {
+            if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.INDEX_SOLID) {
+                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
             } else {
-                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
-                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_SOLID);
+                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
             }
         }
         onMetadataChanged();
@@ -270,6 +275,11 @@
     }
 
     @Override
+    public boolean isPlaying() {
+        return isMediaPlaying();
+    }
+
+    @Override
     public CharSequence getMediaTitle() {
         return mTitle != null ? mTitle : "N/a";
     }
@@ -426,8 +436,11 @@
             @Override
             public void onPrepared(MediaPlayer mp) {
                 mInitialized = true;
-                if (mPlayerCallback != null) {
-                    mPlayerCallback.onReadyForPlayback();
+                List<PlayerCallback> callbacks = getPlayerCallbacks();
+                if (callbacks != null) {
+                    for (PlayerCallback callback: callbacks) {
+                        callback.onPreparedStateChanged(MediaPlayerGlue.this);
+                    }
                 }
             }
         });
@@ -476,6 +489,11 @@
         return mInitialized;
     }
 
+    @Override
+    public boolean isPrepared() {
+        return mInitialized;
+    }
+
     /**
      * Implements {@link SurfaceHolder.Callback} that can then be set on the
      * {@link PlaybackGlueHost}.
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackBannerControlGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackBannerControlGlue.java
new file mode 100644
index 0000000..bcf5945
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackBannerControlGlue.java
@@ -0,0 +1,635 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ObjectAdapter;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A helper class for managing a {@link PlaybackControlsRow} being displayed in
+ * {@link PlaybackGlueHost}. It supports standard playback control actions play/pause and
+ * skip next/previous. This helper class is a glue layer that manages interaction between the
+ * leanback UI components {@link PlaybackControlsRow} {@link PlaybackControlsRowPresenter}
+ * and a functional {@link PlayerAdapter} which represents the underlying
+ * media player.
+ *
+ * <p>Apps must pass a {@link PlayerAdapter} in the constructor for a specific
+ * implementation e.g. a {@link MediaPlayerAdapter}.
+ * </p>
+ *
+ * <p>The glue has two action bars: primary action bars and secondary action bars. Apps
+ * can provide additional actions by overriding {@link #onCreatePrimaryActions} and / or
+ * {@link #onCreateSecondaryActions} and respond to actions by overriding
+ * {@link #onActionClicked(Action)}.
+ * </p>
+ *
+ * <p>The subclass is responsible for implementing the "repeat mode" in
+ * {@link #onPlayCompleted()}.
+ * </p>
+ *
+ * @param <T> Type of {@link PlayerAdapter} passed in constructor.
+ */
+public class PlaybackBannerControlGlue<T extends PlayerAdapter>
+        extends PlaybackBaseControlGlue<T> {
+
+    /** @hide */
+    @IntDef(
+            flag = true,
+            value = {
+            ACTION_CUSTOM_LEFT_FIRST,
+            ACTION_SKIP_TO_PREVIOUS,
+            ACTION_REWIND,
+            ACTION_PLAY_PAUSE,
+            ACTION_FAST_FORWARD,
+            ACTION_SKIP_TO_NEXT,
+            ACTION_CUSTOM_RIGHT_FIRST
+    })
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ACTION_ {}
+
+    /**
+     * The adapter key for the first custom control on the left side
+     * of the predefined primary controls.
+     */
+    public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1;
+
+    /**
+     * The adapter key for the skip to previous control.
+     */
+    public static final int ACTION_SKIP_TO_PREVIOUS = 0x10;
+
+    /**
+     * The adapter key for the rewind control.
+     */
+    public static final int ACTION_REWIND = 0x20;
+
+    /**
+     * The adapter key for the play/pause control.
+     */
+    public static final int ACTION_PLAY_PAUSE = 0x40;
+
+    /**
+     * The adapter key for the fast forward control.
+     */
+    public static final int ACTION_FAST_FORWARD = 0x80;
+
+    /**
+     * The adapter key for the skip to next control.
+     */
+    public static final int ACTION_SKIP_TO_NEXT = 0x100;
+
+    /**
+     * The adapter key for the first custom control on the right side
+     * of the predefined primary controls.
+     */
+    public static final int ACTION_CUSTOM_RIGHT_FIRST = 0x1000;
+
+
+    /** @hide */
+    @IntDef({
+                    PLAYBACK_SPEED_INVALID,
+                    PLAYBACK_SPEED_PAUSED,
+                    PLAYBACK_SPEED_NORMAL,
+                    PLAYBACK_SPEED_FAST_L0,
+                    PLAYBACK_SPEED_FAST_L1,
+                    PLAYBACK_SPEED_FAST_L2,
+                    PLAYBACK_SPEED_FAST_L3,
+                    PLAYBACK_SPEED_FAST_L4
+    })
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface SPEED {}
+
+    /**
+     * Invalid playback speed.
+     */
+    public static final int PLAYBACK_SPEED_INVALID = -1;
+
+    /**
+     * Speed representing playback state that is paused.
+     */
+    public static final int PLAYBACK_SPEED_PAUSED = 0;
+
+    /**
+     * Speed representing playback state that is playing normally.
+     */
+    public static final int PLAYBACK_SPEED_NORMAL = 1;
+
+    /**
+     * The initial (level 0) fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L0 = 10;
+
+    /**
+     * The level 1 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L1 = 11;
+
+    /**
+     * The level 2 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L2 = 12;
+
+    /**
+     * The level 3 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L3 = 13;
+
+    /**
+     * The level 4 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L4 = 14;
+
+    private static final String TAG = PlaybackBannerControlGlue.class.getSimpleName();
+    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4
+            - PLAYBACK_SPEED_FAST_L0 + 1;
+
+    private final int[] mFastForwardSpeeds;
+    private final int[] mRewindSpeeds;
+    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
+    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
+    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
+    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
+    private PlaybackControlsRow.RewindAction mRewindAction;
+
+    @SPEED
+    private int mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
+    private long mStartTime;
+    private long mStartPosition = 0;
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param seekSpeeds The array of seek speeds for fast forward and rewind. The maximum length of
+     *                   the array is defined as NUMBER_OF_SEEK_SPEEDS.
+     * @param impl Implementation to underlying media player.
+     */
+    public PlaybackBannerControlGlue(Context context,
+            int[] seekSpeeds,
+            T impl) {
+        this(context, seekSpeeds, seekSpeeds, impl);
+    }
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param fastForwardSpeeds The array of seek speeds for fast forward. The maximum length of
+     *                   the array is defined as NUMBER_OF_SEEK_SPEEDS.
+     * @param rewindSpeeds The array of seek speeds for rewind. The maximum length of
+     *                   the array is defined as NUMBER_OF_SEEK_SPEEDS.
+     * @param impl Implementation to underlying media player.
+     */
+    public PlaybackBannerControlGlue(Context context,
+                                    int[] fastForwardSpeeds,
+                                    int[] rewindSpeeds,
+                                    T impl) {
+        super(context, impl);
+
+        if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
+            throw new IllegalArgumentException("invalid fastForwardSpeeds array size");
+        }
+        mFastForwardSpeeds = fastForwardSpeeds;
+
+        if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
+            throw new IllegalArgumentException("invalid rewindSpeeds array size");
+        }
+        mRewindSpeeds = rewindSpeeds;
+    }
+
+    @Override
+    public void setControlsRow(PlaybackControlsRow controlsRow) {
+        super.setControlsRow(controlsRow);
+        onUpdatePlaybackState();
+    }
+
+    @Override
+    protected void onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter) {
+        final long supportedActions = getSupportedActions();
+        if ((supportedActions & ACTION_SKIP_TO_PREVIOUS) != 0 && mSkipPreviousAction == null) {
+            primaryActionsAdapter.add(mSkipPreviousAction =
+                    new PlaybackControlsRow.SkipPreviousAction(getContext()));
+        } else if ((supportedActions & ACTION_SKIP_TO_PREVIOUS) == 0
+                && mSkipPreviousAction != null) {
+            primaryActionsAdapter.remove(mSkipPreviousAction);
+            mSkipPreviousAction = null;
+        }
+        if ((supportedActions & ACTION_REWIND) != 0 && mRewindAction == null) {
+            primaryActionsAdapter.add(mRewindAction =
+                    new PlaybackControlsRow.RewindAction(getContext(), mRewindSpeeds.length));
+        } else if ((supportedActions & ACTION_REWIND) == 0 && mRewindAction != null) {
+            primaryActionsAdapter.remove(mRewindAction);
+            mRewindAction = null;
+        }
+        if ((supportedActions & ACTION_PLAY_PAUSE) != 0 && mPlayPauseAction == null) {
+            mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(getContext());
+            primaryActionsAdapter.add(mPlayPauseAction =
+                    new PlaybackControlsRow.PlayPauseAction(getContext()));
+        } else if ((supportedActions & ACTION_PLAY_PAUSE) == 0 && mPlayPauseAction != null) {
+            primaryActionsAdapter.remove(mPlayPauseAction);
+            mPlayPauseAction = null;
+        }
+        if ((supportedActions & ACTION_FAST_FORWARD) != 0 && mFastForwardAction == null) {
+            mFastForwardAction = new PlaybackControlsRow.FastForwardAction(getContext(),
+                    mFastForwardSpeeds.length);
+            primaryActionsAdapter.add(mFastForwardAction =
+                    new PlaybackControlsRow.FastForwardAction(getContext(),
+                            mFastForwardSpeeds.length));
+        } else if ((supportedActions & ACTION_FAST_FORWARD) == 0 && mFastForwardAction != null) {
+            primaryActionsAdapter.remove(mFastForwardAction);
+            mFastForwardAction = null;
+        }
+        if ((supportedActions & ACTION_SKIP_TO_NEXT) != 0 && mSkipNextAction == null) {
+            primaryActionsAdapter.add(mSkipNextAction =
+                    new PlaybackControlsRow.SkipNextAction(getContext()));
+        } else if ((supportedActions & ACTION_SKIP_TO_NEXT) == 0 && mSkipNextAction != null) {
+            primaryActionsAdapter.remove(mSkipNextAction);
+            mSkipNextAction = null;
+        }
+    }
+
+    @Override
+    protected PlaybackRowPresenter onCreateRowPresenter() {
+        final AbstractDetailsDescriptionPresenter detailsPresenter =
+                new AbstractDetailsDescriptionPresenter() {
+                    @Override
+                    protected void onBindDescription(ViewHolder
+                            viewHolder, Object object) {
+                        PlaybackBannerControlGlue glue = (PlaybackBannerControlGlue) object;
+                        viewHolder.getTitle().setText(glue.getTitle());
+                        viewHolder.getSubtitle().setText(glue.getSubtitle());
+                    }
+                };
+
+        PlaybackControlsRowPresenter rowPresenter =
+                new PlaybackControlsRowPresenter(detailsPresenter) {
+            @Override
+            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
+                super.onBindRowViewHolder(vh, item);
+                vh.setOnKeyListener(PlaybackBannerControlGlue.this);
+            }
+            @Override
+            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
+                super.onUnbindRowViewHolder(vh);
+                vh.setOnKeyListener(null);
+            }
+        };
+
+        return rowPresenter;
+    }
+
+    /**
+     * Handles action clicks.  A subclass may override this add support for additional actions.
+     */
+    @Override
+    public void onActionClicked(Action action) {
+        dispatchAction(action, null);
+    }
+
+    /**
+     * Handles key events and returns true if handled.  A subclass may override this to provide
+     * additional support.
+     */
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0
+                        || mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
+                if (abortSeek) {
+                    play();
+                    onUpdatePlaybackStatusAfterUserAction();
+                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
+                }
+                return false;
+        }
+
+        final ObjectAdapter primaryActionsAdapter = mControlsRow.getPrimaryActionsAdapter();
+        Action action = mControlsRow.getActionForKeyCode(primaryActionsAdapter, keyCode);
+        if (action == null) {
+            action = mControlsRow.getActionForKeyCode(mControlsRow.getSecondaryActionsAdapter(),
+                    keyCode);
+        }
+
+        if (action != null) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                dispatchAction(action, event);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    void onUpdatePlaybackStatusAfterUserAction() {
+        updatePlaybackState(mIsPlaying);
+    }
+
+    /**
+     * Called when the given action is invoked, either by click or key event.
+     */
+    boolean dispatchAction(Action action, KeyEvent keyEvent) {
+        boolean handled = false;
+        if (action == mPlayPauseAction) {
+            boolean canPlay = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
+            boolean canPause = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
+            //            PLAY_PAUSE    PLAY      PAUSE
+            // playing    paused                  paused
+            // paused     playing       playing
+            // ff/rw      playing       playing   paused
+            if (canPause
+                    && (canPlay ? mPlaybackSpeed == PLAYBACK_SPEED_NORMAL :
+                    mPlaybackSpeed != PLAYBACK_SPEED_PAUSED)) {
+                pause();
+            } else if (canPlay && mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
+                play();
+            }
+            onUpdatePlaybackStatusAfterUserAction();
+            handled = true;
+        } else if (action == mSkipNextAction) {
+            next();
+            handled = true;
+        } else if (action == mSkipPreviousAction) {
+            previous();
+            handled = true;
+        } else if (action == mFastForwardAction) {
+            if (mPlayerAdapter.isPrepared() && mPlaybackSpeed < getMaxForwardSpeedId()) {
+                fakePause();
+
+                switch (mPlaybackSpeed) {
+                    case PLAYBACK_SPEED_FAST_L0:
+                    case PLAYBACK_SPEED_FAST_L1:
+                    case PLAYBACK_SPEED_FAST_L2:
+                    case PLAYBACK_SPEED_FAST_L3:
+                        mPlaybackSpeed++;
+                        break;
+                    default:
+                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
+                        break;
+                }
+                onUpdatePlaybackStatusAfterUserAction();
+            }
+            handled = true;
+        } else if (action == mRewindAction) {
+            if (mPlayerAdapter.isPrepared() && mPlaybackSpeed > -getMaxRewindSpeedId()) {
+                fakePause();
+
+                switch (mPlaybackSpeed) {
+                    case -PLAYBACK_SPEED_FAST_L0:
+                    case -PLAYBACK_SPEED_FAST_L1:
+                    case -PLAYBACK_SPEED_FAST_L2:
+                    case -PLAYBACK_SPEED_FAST_L3:
+                        mPlaybackSpeed--;
+                        break;
+                    default:
+                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
+                        break;
+                }
+                onUpdatePlaybackStatusAfterUserAction();
+            }
+            handled = true;
+        }
+        return handled;
+    }
+
+    @Override
+    protected void onPlayStateChanged() {
+        if (DEBUG) Log.v(TAG, "onStateChanged");
+
+        onUpdatePlaybackState();
+        super.onPlayStateChanged();
+    }
+
+    @Override
+    protected void onPlayCompleted() {
+        super.onPlayCompleted();
+        mIsPlaying = false;
+        mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
+        mStartPosition = getCurrentPosition();
+        mStartTime = System.currentTimeMillis();
+        onUpdatePlaybackState();
+    }
+
+    void onUpdatePlaybackState() {
+        updatePlaybackState(mIsPlaying);
+    }
+
+    private void updatePlaybackState(boolean isPlaying) {
+        if (mControlsRow == null) {
+            return;
+        }
+
+        if (!isPlaying) {
+            onUpdateProgress();
+            mPlayerAdapter.setProgressUpdatingEnabled(false);
+        } else {
+            mPlayerAdapter.setProgressUpdatingEnabled(true);
+        }
+
+        if (mFadeWhenPlaying && getHost() != null) {
+            getHost().setControlsOverlayAutoHideEnabled(isPlaying);
+        }
+
+
+        final ArrayObjectAdapter primaryActionsAdapter =
+                (ArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
+        if (mPlayPauseAction != null) {
+            int index = !isPlaying
+                    ? PlaybackControlsRow.PlayPauseAction.PLAY
+                    : PlaybackControlsRow.PlayPauseAction.PAUSE;
+            if (mPlayPauseAction.getIndex() != index) {
+                mPlayPauseAction.setIndex(index);
+                notifyItemChanged(primaryActionsAdapter, mPlayPauseAction);
+            }
+        }
+
+        if (mFastForwardAction != null) {
+            int index = 0;
+            if (mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0) {
+                index = mPlaybackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
+            }
+            if (mFastForwardAction.getIndex() != index) {
+                mFastForwardAction.setIndex(index);
+                notifyItemChanged(primaryActionsAdapter, mFastForwardAction);
+            }
+        }
+        if (mRewindAction != null) {
+            int index = 0;
+            if (mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
+                index = -mPlaybackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
+            }
+            if (mRewindAction.getIndex() != index) {
+                mRewindAction.setIndex(index);
+                notifyItemChanged(primaryActionsAdapter, mRewindAction);
+            }
+        }
+    }
+
+    /**
+     * Returns the fast forward speeds.
+     */
+    @NonNull
+    public int[] getFastForwardSpeeds() {
+        return mFastForwardSpeeds;
+    }
+
+    /**
+     * Returns the rewind speeds.
+     */
+    @NonNull
+    public int[] getRewindSpeeds() {
+        return mRewindSpeeds;
+    }
+
+    private int getMaxForwardSpeedId() {
+        return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
+    }
+
+    private int getMaxRewindSpeedId() {
+        return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
+    }
+
+    /**
+     * Gets current position of the player. If the player is playing/paused, this
+     * method returns current position from {@link PlayerAdapter}. Otherwise, if the player is
+     * fastforwarding/rewinding, the method fake-pauses the {@link PlayerAdapter} and returns its
+     * own calculated position.
+     * @return Current position of the player.
+     */
+    @Override
+    public long getCurrentPosition() {
+        int speed;
+        if (mPlaybackSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED
+                || mPlaybackSpeed == PlaybackControlGlue.PLAYBACK_SPEED_NORMAL) {
+            // If the adapter is playing/paused, using the position from adapter instead.
+            return mPlayerAdapter.getCurrentPosition();
+        } else if (mPlaybackSpeed >= PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
+            int index = mPlaybackSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
+            speed = getFastForwardSpeeds()[index];
+        } else if (mPlaybackSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
+            int index = -mPlaybackSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
+            speed = -getRewindSpeeds()[index];
+        } else {
+            return -1;
+        }
+
+        long position = mStartPosition + (System.currentTimeMillis() - mStartTime) * speed;
+        if (position > getDuration()) {
+            mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
+            position = getDuration();
+            mPlayerAdapter.seekTo(position);
+            mStartPosition = 0;
+            pause();
+        } else if (position < 0) {
+            mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
+            position = 0;
+            mPlayerAdapter.seekTo(position);
+            mStartPosition = 0;
+            pause();
+        }
+        return position;
+    }
+
+    /**
+     * Returns a bitmask of actions supported by the media player.
+     * @see ACTION_ for constants that may be returned by this method.
+     */
+    public long getSupportedActions() {
+        return PlaybackBannerControlGlue.ACTION_PLAY_PAUSE;
+    }
+
+    @Override
+    public void play() {
+        if (!mPlayerAdapter.isPrepared()) {
+            return;
+        }
+
+        // Solves the situation that a player pause at the end and click play button. At this case
+        // the player will restart from the beginning.
+        if (mPlaybackSpeed == PLAYBACK_SPEED_PAUSED
+                && mPlayerAdapter.getCurrentPosition() >= mPlayerAdapter.getDuration()) {
+            mStartPosition = 0;
+        } else {
+            mStartPosition = getCurrentPosition();
+        }
+
+        mStartTime = System.currentTimeMillis();
+        mIsPlaying = true;
+        mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
+        mPlayerAdapter.seekTo(mStartPosition);
+        super.play();
+
+        onUpdatePlaybackState();
+    }
+
+    @Override
+    public void pause() {
+        mIsPlaying = false;
+        mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
+        mStartPosition = getCurrentPosition();
+        mStartTime = System.currentTimeMillis();
+        super.pause();
+
+        onUpdatePlaybackState();
+    }
+
+    /**
+     * Control row shows PLAY, but the media is actually paused when the player is
+     * fastforwarding/rewinding.
+     */
+    private void fakePause() {
+        mIsPlaying = true;
+        mStartPosition = getCurrentPosition();
+        mStartTime = System.currentTimeMillis();
+        super.pause();
+
+        onUpdatePlaybackState();
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackBaseControlGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackBaseControlGlue.java
new file mode 100644
index 0000000..24b2dcb
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackBaseControlGlue.java
@@ -0,0 +1,548 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.CallSuper;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackTransportRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+import java.util.List;
+
+/**
+ * A base abstract class for managing a {@link PlaybackControlsRow} being displayed in
+ * {@link PlaybackGlueHost}. It supports standard playback control actions play/pause and
+ * skip next/previous. This helper class is a glue layer that manages interaction between the
+ * leanback UI components {@link PlaybackControlsRow} {@link PlaybackRowPresenter}
+ * and a functional {@link PlayerAdapter} which represents the underlying
+ * media player.
+ *
+ * <p>The app must pass a {@link PlayerAdapter} in constructor for a specific
+ * implementation e.g. a {@link MediaPlayerAdapter}.
+ * </p>
+ *
+ * <p>The glue has two action bars: primary action bars and secondary action bars. Apps
+ * can provide additional actions by overriding {@link #onCreatePrimaryActions} and / or
+ * {@link #onCreateSecondaryActions} and respond to actions by overriding
+ * {@link #onActionClicked(Action)}.
+ * </p>
+ *
+ * <p>The subclass is responsible for implementing the "repeat mode" in
+ * {@link #onPlayCompleted()}.
+ * </p>
+ *
+ * @param <T> Type of {@link PlayerAdapter} passed in constructor.
+ */
+public abstract class PlaybackBaseControlGlue<T extends PlayerAdapter> extends PlaybackGlue
+        implements OnActionClickedListener, View.OnKeyListener {
+
+    static final String TAG = "PlaybackTransportGlue";
+    static final boolean DEBUG = false;
+
+    final T mPlayerAdapter;
+    PlaybackControlsRow mControlsRow;
+    PlaybackRowPresenter mControlsRowPresenter;
+    PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
+    boolean mIsPlaying = false;
+    boolean mFadeWhenPlaying = true;
+
+    CharSequence mSubtitle;
+    CharSequence mTitle;
+    Drawable mCover;
+
+    PlaybackGlueHost.PlayerCallback mPlayerCallback;
+    boolean mBuffering = false;
+    int mVideoWidth = 0;
+    int mVideoHeight = 0;
+    boolean mErrorSet = false;
+    int mErrorCode;
+    String mErrorMessage;
+
+    final PlayerAdapter.Callback mAdapterCallback = new PlayerAdapter
+            .Callback() {
+
+        @Override
+        public void onPlayStateChanged(PlayerAdapter wrapper) {
+            if (DEBUG) Log.v(TAG, "onPlayStateChanged");
+            PlaybackBaseControlGlue.this.onPlayStateChanged();
+        }
+
+        @Override
+        public void onCurrentPositionChanged(PlayerAdapter wrapper) {
+            if (DEBUG) Log.v(TAG, "onCurrentPositionChanged");
+            PlaybackBaseControlGlue.this.onUpdateProgress();
+        }
+
+        @Override
+        public void onBufferedPositionChanged(PlayerAdapter wrapper) {
+            if (DEBUG) Log.v(TAG, "onBufferedPositionChanged");
+            PlaybackBaseControlGlue.this.onUpdateBufferedProgress();
+        }
+
+        @Override
+        public void onDurationChanged(PlayerAdapter wrapper) {
+            if (DEBUG) Log.v(TAG, "onDurationChanged");
+            PlaybackBaseControlGlue.this.onUpdateDuration();
+        }
+
+        @Override
+        public void onPlayCompleted(PlayerAdapter wrapper) {
+            if (DEBUG) Log.v(TAG, "onPlayCompleted");
+            PlaybackBaseControlGlue.this.onPlayCompleted();
+        }
+
+        @Override
+        public void onPreparedStateChanged(PlayerAdapter wrapper) {
+            if (DEBUG) Log.v(TAG, "onPreparedStateChanged");
+            PlaybackBaseControlGlue.this.onPreparedStateChanged();
+        }
+
+        @Override
+        public void onVideoSizeChanged(PlayerAdapter wrapper, int width, int height) {
+            mVideoWidth = width;
+            mVideoHeight = height;
+            if (mPlayerCallback != null) {
+                mPlayerCallback.onVideoSizeChanged(width, height);
+            }
+        }
+
+        @Override
+        public void onError(PlayerAdapter wrapper, int errorCode, String errorMessage) {
+            mErrorSet = true;
+            mErrorCode = errorCode;
+            mErrorMessage = errorMessage;
+            if (mPlayerCallback != null) {
+                mPlayerCallback.onError(errorCode, errorMessage);
+            }
+        }
+
+        @Override
+        public void onBufferingStateChanged(PlayerAdapter wrapper, boolean start) {
+            mBuffering = start;
+            if (mPlayerCallback != null) {
+                mPlayerCallback.onBufferingStateChanged(start);
+            }
+        }
+    };
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param impl Implementation to underlying media player.
+     */
+    public PlaybackBaseControlGlue(Context context, T impl) {
+        super(context);
+        mPlayerAdapter = impl;
+        mPlayerAdapter.setCallback(mAdapterCallback);
+    }
+
+    public final T getPlayerAdapter() {
+        return mPlayerAdapter;
+    }
+
+    @Override
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        super.onAttachedToHost(host);
+        host.setOnKeyInterceptListener(this);
+        host.setOnActionClickedListener(this);
+        onCreateDefaultControlsRow();
+        onCreateDefaultRowPresenter();
+        host.setPlaybackRowPresenter(getPlaybackRowPresenter());
+        host.setPlaybackRow(getControlsRow());
+
+        mPlayerCallback = host.getPlayerCallback();
+        onAttachHostCallback();
+        mPlayerAdapter.onAttachedToHost(host);
+    }
+
+    void onAttachHostCallback() {
+        if (mPlayerCallback != null) {
+            if (mVideoWidth != 0 && mVideoHeight != 0) {
+                mPlayerCallback.onVideoSizeChanged(mVideoWidth, mVideoHeight);
+            }
+            if (mErrorSet) {
+                mPlayerCallback.onError(mErrorCode, mErrorMessage);
+            }
+            mPlayerCallback.onBufferingStateChanged(mBuffering);
+        }
+    }
+
+    void onDetachHostCallback() {
+        mErrorSet = false;
+        mErrorCode = 0;
+        mErrorMessage = null;
+        if (mPlayerCallback != null) {
+            mPlayerCallback.onBufferingStateChanged(false);
+        }
+    }
+
+    @Override
+    protected void onHostStart() {
+        mPlayerAdapter.setProgressUpdatingEnabled(true);
+    }
+
+    @Override
+    protected void onHostStop() {
+        mPlayerAdapter.setProgressUpdatingEnabled(false);
+    }
+
+    @Override
+    protected void onDetachedFromHost() {
+        onDetachHostCallback();
+        mPlayerCallback = null;
+        mPlayerAdapter.onDetachedFromHost();
+        mPlayerAdapter.setProgressUpdatingEnabled(false);
+        super.onDetachedFromHost();
+    }
+
+    void onCreateDefaultControlsRow() {
+        if (mControlsRow == null) {
+            PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
+            setControlsRow(controlsRow);
+        }
+    }
+
+    void onCreateDefaultRowPresenter() {
+        if (mControlsRowPresenter == null) {
+            setPlaybackRowPresenter(onCreateRowPresenter());
+        }
+    }
+
+    protected abstract PlaybackRowPresenter onCreateRowPresenter();
+
+    /**
+     * Sets the controls to auto hide after a timeout when media is playing.
+     * @param enable True to enable auto hide after a timeout when media is playing.
+     * @see PlaybackGlueHost#setControlsOverlayAutoHideEnabled(boolean)
+     */
+    public void setControlsOverlayAutoHideEnabled(boolean enable) {
+        mFadeWhenPlaying = enable;
+        if (!mFadeWhenPlaying && getHost() != null) {
+            getHost().setControlsOverlayAutoHideEnabled(false);
+        }
+    }
+
+    /**
+     * Returns true if the controls auto hides after a timeout when media is playing.
+     * @see PlaybackGlueHost#isControlsOverlayAutoHideEnabled()
+     */
+    public boolean isControlsOverlayAutoHideEnabled() {
+        return mFadeWhenPlaying;
+    }
+
+    /**
+     * Sets the controls row to be managed by the glue layer. If
+     * {@link PlaybackControlsRow#getPrimaryActionsAdapter()} is not provided, a default
+     * {@link ArrayObjectAdapter} will be created and initialized in
+     * {@link #onCreatePrimaryActions(ArrayObjectAdapter)}. If
+     * {@link PlaybackControlsRow#getSecondaryActionsAdapter()} is not provided, a default
+     * {@link ArrayObjectAdapter} will be created and initialized in
+     * {@link #onCreateSecondaryActions(ArrayObjectAdapter)}.
+     * The primary actions and playback state related aspects of the row
+     * are updated by the glue.
+     */
+    public void setControlsRow(PlaybackControlsRow controlsRow) {
+        mControlsRow = controlsRow;
+        mControlsRow.setCurrentPosition(-1);
+        mControlsRow.setDuration(-1);
+        mControlsRow.setBufferedPosition(-1);
+        if (mControlsRow.getPrimaryActionsAdapter() == null) {
+            ArrayObjectAdapter adapter = new ArrayObjectAdapter(
+                    new ControlButtonPresenterSelector());
+            onCreatePrimaryActions(adapter);
+            mControlsRow.setPrimaryActionsAdapter(adapter);
+        }
+        // Add secondary actions
+        if (mControlsRow.getSecondaryActionsAdapter() == null) {
+            ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
+                    new ControlButtonPresenterSelector());
+            onCreateSecondaryActions(secondaryActions);
+            getControlsRow().setSecondaryActionsAdapter(secondaryActions);
+        }
+        updateControlsRow();
+    }
+
+    /**
+     * Sets the controls row Presenter to be managed by the glue layer.
+     */
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
+        mControlsRowPresenter = presenter;
+    }
+
+    /**
+     * Returns the playback controls row managed by the glue layer.
+     */
+    public PlaybackControlsRow getControlsRow() {
+        return mControlsRow;
+    }
+
+    /**
+     * Returns the playback controls row Presenter managed by the glue layer.
+     */
+    public PlaybackRowPresenter getPlaybackRowPresenter() {
+        return mControlsRowPresenter;
+    }
+
+    /**
+     * Handles action clicks.  A subclass may override this add support for additional actions.
+     */
+    @Override
+    public abstract void onActionClicked(Action action);
+
+    /**
+     * Handles key events and returns true if handled.  A subclass may override this to provide
+     * additional support.
+     */
+    @Override
+    public abstract boolean onKey(View v, int keyCode, KeyEvent event);
+
+    private void updateControlsRow() {
+        onMetadataChanged();
+    }
+
+    @Override
+    public final boolean isPlaying() {
+        return mPlayerAdapter.isPlaying();
+    }
+
+    @Override
+    public void play() {
+        mPlayerAdapter.play();
+    }
+
+    @Override
+    public void pause() {
+        mPlayerAdapter.pause();
+    }
+
+    protected static void notifyItemChanged(ArrayObjectAdapter adapter, Object object) {
+        int index = adapter.indexOf(object);
+        if (index >= 0) {
+            adapter.notifyArrayItemRangeChanged(index, 1);
+        }
+    }
+
+    /**
+     * May be overridden to add primary actions to the adapter. Default implementation add
+     * {@link PlaybackControlsRow.PlayPauseAction}.
+     *
+     * @param primaryActionsAdapter The adapter to add primary {@link Action}s.
+     */
+    protected void onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter) {
+    }
+
+    /**
+     * May be overridden to add secondary actions to the adapter.
+     *
+     * @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
+     */
+    protected void onCreateSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
+    }
+
+    void onUpdateProgress() {
+        if (mControlsRow != null) {
+            mControlsRow.setCurrentPosition(mPlayerAdapter.isPrepared()
+                    ? getCurrentPosition() : -1);
+        }
+    }
+
+    void onUpdateBufferedProgress() {
+        if (mControlsRow != null) {
+            mControlsRow.setBufferedPosition(mPlayerAdapter.getBufferedPosition());
+        }
+    }
+
+    void onUpdateDuration() {
+        if (mControlsRow != null) {
+            mControlsRow.setDuration(
+                    mPlayerAdapter.isPrepared() ? mPlayerAdapter.getDuration() : -1);
+        }
+    }
+
+    /**
+     * @return The duration of the media item in milliseconds.
+     */
+    public final long getDuration() {
+        return mPlayerAdapter.getDuration();
+    }
+
+    /**
+     * @return The current position of the media item in milliseconds.
+     */
+    public long getCurrentPosition() {
+        return mPlayerAdapter.getCurrentPosition();
+    }
+
+    /**
+     * @return The current buffered position of the media item in milliseconds.
+     */
+    public final long getBufferedPosition() {
+        return mPlayerAdapter.getBufferedPosition();
+    }
+
+    @Override
+    public final boolean isPrepared() {
+        return mPlayerAdapter.isPrepared();
+    }
+
+    /**
+     * Event when ready state for play changes.
+     */
+    @CallSuper
+    protected void onPreparedStateChanged() {
+        onUpdateDuration();
+        List<PlayerCallback> callbacks = getPlayerCallbacks();
+        if (callbacks != null) {
+            for (int i = 0, size = callbacks.size(); i < size; i++) {
+                callbacks.get(i).onPreparedStateChanged(this);
+            }
+        }
+    }
+
+    /**
+     * Sets the drawable representing cover image. The drawable will be rendered by default
+     * description presenter in
+     * {@link PlaybackTransportRowPresenter#setDescriptionPresenter(Presenter)}.
+     * @param cover The drawable representing cover image.
+     */
+    public void setArt(Drawable cover) {
+        if (mCover == cover) {
+            return;
+        }
+        this.mCover = cover;
+        mControlsRow.setImageDrawable(mCover);
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
+    }
+
+    /**
+     * @return The drawable representing cover image.
+     */
+    public Drawable getArt() {
+        return mCover;
+    }
+
+    /**
+     * Sets the media subtitle. The subtitle will be rendered by default description presenter
+     * {@link PlaybackTransportRowPresenter#setDescriptionPresenter(Presenter)}.
+     * @param subtitle Subtitle to set.
+     */
+    public void setSubtitle(CharSequence subtitle) {
+        if (TextUtils.equals(subtitle, mSubtitle)) {
+            return;
+        }
+        mSubtitle = subtitle;
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
+    }
+
+    /**
+     * Return The media subtitle.
+     */
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Sets the media title. The title will be rendered by default description presenter
+     * {@link PlaybackTransportRowPresenter#setDescriptionPresenter(Presenter)}.
+     */
+    public void setTitle(CharSequence title) {
+        if (TextUtils.equals(title, mTitle)) {
+            return;
+        }
+        mTitle = title;
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
+    }
+
+    /**
+     * Returns the title of the media item.
+     */
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Event when metadata changed
+     */
+    void onMetadataChanged() {
+        if (mControlsRow == null) {
+            return;
+        }
+
+        if (DEBUG) Log.v(TAG, "updateRowMetadata");
+
+        mControlsRow.setImageDrawable(getArt());
+        mControlsRow.setDuration(mPlayerAdapter.getDuration());
+        mControlsRow.setCurrentPosition(getCurrentPosition());
+
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
+    }
+
+    /**
+     * Event when play state changed.
+     */
+    @CallSuper
+    protected void onPlayStateChanged() {
+        List<PlayerCallback> callbacks = getPlayerCallbacks();
+        if (callbacks != null) {
+            for (int i = 0, size = callbacks.size(); i < size; i++) {
+                callbacks.get(i).onPlayStateChanged(this);
+            }
+        }
+    }
+
+    /**
+     * Event when play finishes, subclass may handling repeat mode here.
+     */
+    @CallSuper
+    protected void onPlayCompleted() {
+        List<PlayerCallback> callbacks = getPlayerCallbacks();
+        if (callbacks != null) {
+            for (int i = 0, size = callbacks.size(); i < size; i++) {
+                callbacks.get(i).onPlayCompleted(this);
+            }
+        }
+    }
+
+    /**
+     * Seek media to a new position.
+     * @param position New position.
+     */
+    public final void seekTo(long position) {
+        mPlayerAdapter.seekTo(position);
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java
index 945999d..5bf6cc1 100644
--- a/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java
@@ -36,6 +36,7 @@
 import android.view.View;
 
 import java.lang.ref.WeakReference;
+import java.util.List;
 
 /**
  * A helper class for managing a {@link PlaybackControlsRow}
@@ -324,7 +325,7 @@
     public void setFadingEnabled(boolean enable) {
         mFadeWhenPlaying = enable;
         if (!mFadeWhenPlaying && getHost() != null) {
-            getHost().setFadingEnabled(false);
+            getHost().setControlsOverlayAutoHideEnabled(false);
         }
     }
 
@@ -701,18 +702,24 @@
         }
 
         if (mFadeWhenPlaying && getHost() != null) {
-            getHost().setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
+            getHost().setControlsOverlayAutoHideEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
         }
 
         if (mPlayPauseAction != null) {
             int index = playbackSpeed == PLAYBACK_SPEED_PAUSED
-                    ? PlaybackControlsRow.PlayPauseAction.PLAY
-                    : PlaybackControlsRow.PlayPauseAction.PAUSE;
+                    ? PlaybackControlsRow.PlayPauseAction.INDEX_PLAY
+                    : PlaybackControlsRow.PlayPauseAction.INDEX_PAUSE;
             if (mPlayPauseAction.getIndex() != index) {
                 mPlayPauseAction.setIndex(index);
                 notifyItemChanged(primaryActionsAdapter, mPlayPauseAction);
             }
         }
+        List<PlayerCallback> callbacks = getPlayerCallbacks();
+        if (callbacks != null) {
+            for (int i = 0, size = callbacks.size(); i < size; i++) {
+                callbacks.get(i).onPlayStateChanged(this);
+            }
+        }
     }
 
     private static void notifyItemChanged(SparseArrayObjectAdapter adapter, Object object) {
@@ -764,6 +771,11 @@
      */
     public abstract boolean isMediaPlaying();
 
+    @Override
+    public boolean isPlaying() {
+        return isMediaPlaying();
+    }
+
     /**
      * Returns the title of the media item.
      */
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java
index 3f55da3..32d5545 100644
--- a/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.support.annotation.CallSuper;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Base class for abstraction of media play/pause feature. A subclass of PlaybackGlue will contain
  * implementation of Media Player or a connection to playback Service. App initializes
@@ -42,15 +45,44 @@
     private PlaybackGlueHost mPlaybackGlueHost;
 
     /**
-     * Interface to allow clients to take action once the video is ready to play.
+     * Interface to allow clients to take action once the video is ready to play and start stop.
      */
     public abstract static class PlayerCallback {
         /**
-         * This method is fired when the video is ready for playback.
+         * This method is fired when media is ready for playback {@link #isPrepared()}.
+         * @deprecated use {@link #onPreparedStateChanged(PlaybackGlue)}.
          */
-        public abstract void onReadyForPlayback();
+        @Deprecated
+        public void onReadyForPlayback() {
+        }
+
+        /**
+         * Event for {@link #isPrepared()} changed.
+         * @param glue The PlaybackGlue that has changed {@link #isPrepared()}.
+         */
+        public void onPreparedStateChanged(PlaybackGlue glue) {
+            if (glue.isPrepared()) {
+                onReadyForPlayback();
+            }
+        }
+
+        /**
+         * Event for Play/Pause state change. See {@link #isPlaying()}}.
+         * @param glue The PlaybackGlue that has changed playing or pausing state.
+         */
+        public void onPlayStateChanged(PlaybackGlue glue) {
+        }
+
+        /**
+         * Event of the current media is finished.
+         * @param glue The PlaybackGlue that has finished current media playing.
+         */
+        public void onPlayCompleted(PlaybackGlue glue) {
+        }
     }
 
+    ArrayList<PlayerCallback> mPlayerCallbacks;
+
     /**
      * Constructor.
      */
@@ -71,15 +103,74 @@
      * {@link PlayerCallback#onReadyForPlayback()} event.
      *
      * @see PlayerCallback#onReadyForPlayback()
+     * @deprecated Use isPrepared() instead.
      */
+    @Deprecated
     public boolean isReadyForPlayback() {
         return true;
     }
 
     /**
-     * Sets the {@link PlayerCallback} callback.
+     * Returns true when the media player is prepared to start media playback. When returning false,
+     * app may listen to {@link PlayerCallback#onPreparedStateChanged(PlaybackGlue)} event.
+     * @return True if prepared, false otherwise.
      */
+    public boolean isPrepared() {
+        return isReadyForPlayback();
+    }
+
+    /**
+     * Sets the {@link PlayerCallback} callback. It will reset the existing callbacks.
+     * In most cases you would call {@link #addPlayerCallback(PlayerCallback)}.
+     * @deprecated Use {@link #addPlayerCallback(PlayerCallback)}.
+     */
+    @Deprecated
     public void setPlayerCallback(PlayerCallback playerCallback) {
+        if (playerCallback == null) {
+            if (mPlayerCallbacks != null) {
+                mPlayerCallbacks.clear();
+            }
+        } else {
+            addPlayerCallback(playerCallback);
+        }
+    }
+
+    /**
+     * Add a PlayerCallback.
+     * @param playerCallback The callback to add.
+     */
+    public void addPlayerCallback(PlayerCallback playerCallback) {
+        if (mPlayerCallbacks == null) {
+            mPlayerCallbacks = new ArrayList();
+        }
+        mPlayerCallbacks.add(playerCallback);
+    }
+
+    /**
+     * Remove a PlayerCallback.
+     * @param callback The callback to remove.
+     */
+    public void removePlayerCallback(PlayerCallback callback) {
+        if (mPlayerCallbacks != null) {
+            mPlayerCallbacks.remove(callback);
+        }
+    }
+
+    /**
+     * @return A snapshot of list of PlayerCallbacks set on the Glue.
+     */
+    protected List<PlayerCallback> getPlayerCallbacks() {
+        if (mPlayerCallbacks == null) {
+            return null;
+        }
+        return new ArrayList(mPlayerCallbacks);
+    }
+
+    /**
+     * Returns true if media is currently playing.
+     */
+    public boolean isPlaying() {
+        return false;
     }
 
     /**
@@ -179,9 +270,7 @@
 
             @Override
             public void onHostDestroy() {
-                if (mPlaybackGlueHost != null) {
-                    mPlaybackGlueHost.attachToGlue(null);
-                }
+                setHost(null);
             }
         });
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java
index 799074c..8985b5d 100644
--- a/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java
@@ -18,6 +18,7 @@
 
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackSeekUi;
 import android.support.v17.leanback.widget.Row;
 import android.view.View;
 
@@ -28,7 +29,7 @@
  * <li>Render UI of PlaybackGlue: {@link #setPlaybackRow(Row)},
  * {@link #setPlaybackRowPresenter(PlaybackRowPresenter)}.
  * </li>
- * <li>Callback for fragment/activity onStart/onStop: {@link #setHostCallback(HostCallback)}.
+ * <li>Client for fragment/activity onStart/onStop: {@link #setHostCallback(HostCallback)}.
  * </li>
  * <li>Auto fade out controls after a short period: {@link #setFadingEnabled(boolean)}.
  * </li>
@@ -36,9 +37,11 @@
  * {@link #setOnActionClickedListener(OnActionClickedListener)}.
  * </li>
  *
- * Subclass of PlaybackGlueHost may implement optional interface e.g. {@link SurfaceHolderGlueHost}
- * to provide SurfaceView. These optional interface should be used during
- * {@link PlaybackGlue#setHost(PlaybackGlueHost)}.
+ * Subclass of PlaybackGlueHost may implement optional interfaces:
+ * <li>{@link SurfaceHolderGlueHost} to provide SurfaceView for video playback.</li>
+ * <li>{@link PlaybackSeekUi} to provide seek UI to glue</li>
+ * These optional interfaces should be accessed by glue in
+ * {@link PlaybackGlue#onAttachedToHost(PlaybackGlueHost)}.
  */
 public abstract class PlaybackGlueHost {
     PlaybackGlue mGlue;
@@ -50,50 +53,128 @@
      */
     public abstract static class HostCallback {
         /**
-         * Callback triggered once the host(fragment) has started.
+         * Client triggered once the host(fragment) has started.
          */
         public void onHostStart() {
         }
 
         /**
-         * Callback triggered once the host(fragment) has stopped.
+         * Client triggered once the host(fragment) has stopped.
          */
         public void onHostStop() {
         }
 
         /**
-         * Callback triggered once the host(fragment) has paused.
+         * Client triggered once the host(fragment) has paused.
          */
         public void onHostPause() {
         }
 
         /**
-         * Callback triggered once the host(fragment) has resumed.
+         * Client triggered once the host(fragment) has resumed.
          */
         public void onHostResume() {
         }
 
         /**
-         * Callback triggered once the host(fragment) has been destroyed.
+         * Client triggered once the host(fragment) has been destroyed.
          */
         public void onHostDestroy() {
         }
     }
 
     /**
+     * Optional Client that implemented by PlaybackGlueHost to respond to player event.
+     */
+    public static class PlayerCallback {
+        /**
+         * Size of the video changes, the Host should adjust SurfaceView's layout width and height.
+         * @param videoWidth
+         * @param videoHeight
+         */
+        public void onVideoSizeChanged(int videoWidth, int videoHeight) {
+        }
+
+        /**
+         * notify media starts/stops buffering/preparing. The Host could start or stop
+         * progress bar.
+         * @param start True for buffering start, false otherwise.
+         */
+        public void onBufferingStateChanged(boolean start) {
+        }
+
+        /**
+         * notify media has error. The Host could show error dialog.
+         * @param errorCode Optional error code for specific implementation.
+         * @param errorMessage Optional error message for specific implementation.
+         */
+        public void onError(int errorCode, CharSequence errorMessage) {
+        }
+    }
+
+    /**
      * Enables or disables view fading.  If enabled, the view will be faded in when the
      * fragment starts and will fade out after a time period.
+     * @deprecated Use {@link #setControlsOverlayAutoHideEnabled(boolean)}
      */
+    @Deprecated
     public void setFadingEnabled(boolean enable) {
     }
 
     /**
-     * Fade out views immediately.
+     * Enables or disables controls overlay auto hidden.  If enabled, the view will be faded out
+     * after a time period.
+     * @param enabled True to enable auto hidden of controls overlay.
+     *
      */
+    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
+        setFadingEnabled(enabled);
+    }
+
+    /**
+     * Returns true if auto hides controls overlay.
+     * @return True if auto hiding controls overlay.
+     */
+    public boolean isControlsOverlayAutoHideEnabled() {
+        return false;
+    }
+
+    /**
+     * Fades out the playback overlay immediately.
+     * @deprecated Call {@link #hideControlsOverlay(boolean)}
+     */
+    @Deprecated
     public void fadeOut() {
     }
 
     /**
+     * Returns true if controls overlay is visible, false otherwise.
+     *
+     * @return True if controls overlay is visible, false otherwise.
+     * @see #showControlsOverlay(boolean)
+     * @see #hideControlsOverlay(boolean)
+     */
+    public boolean isControlsOverlayVisible() {
+        return true;
+    }
+
+    /**
+     * Hide controls overlay.
+     *
+     * @param runAnimation True to run animation, false otherwise.
+     */
+    public void hideControlsOverlay(boolean runAnimation) {
+    }
+
+    /**
+     * Show controls overlay.
+     *
+     * @param runAnimation True to run animation, false otherwise.
+     */
+    public void showControlsOverlay(boolean runAnimation) {
+    }
+
+    /**
      * Sets the {@link android.view.View.OnKeyListener} on the host. This would trigger
      * the listener when a {@link android.view.KeyEvent} is unhandled by the host.
      */
@@ -139,4 +220,13 @@
         }
     }
 
+    /**
+     * Implemented by PlaybackGlueHost for responding to player events. Such as showing a spinning
+     * wheel progress bar when {@link PlayerCallback#onBufferingStateChanged(boolean)}.
+     * @return PlayerEventCallback that Host supports, null if not supported.
+     */
+    public PlayerCallback getPlayerCallback() {
+        return null;
+    }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackTransportControlGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
new file mode 100644
index 0000000..ee56b5c
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
@@ -0,0 +1,430 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ObjectAdapter;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackSeekDataProvider;
+import android.support.v17.leanback.widget.PlaybackSeekUi;
+import android.support.v17.leanback.widget.PlaybackTransportRowPresenter;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A helper class for managing a {@link PlaybackControlsRow} being displayed in
+ * {@link PlaybackGlueHost}, it supports standard playback control actions play/pause, and
+ * skip next/previous. This helper class is a glue layer in that manages interaction between the
+ * leanback UI components {@link PlaybackControlsRow} {@link PlaybackTransportRowPresenter}
+ * and a functional {@link PlayerAdapter} which represents the underlying
+ * media player.
+ *
+ * <p>App must pass a {@link PlayerAdapter} in constructor for a specific
+ * implementation e.g. a {@link MediaPlayerAdapter}.
+ * </p>
+ *
+ * <p>The glue has two actions bar: primary actions bar and secondary actions bar. App
+ * can provide additional actions by overriding {@link #onCreatePrimaryActions} and / or
+ * {@link #onCreateSecondaryActions} and respond to actions by override
+ * {@link #onActionClicked(Action)}.
+ * </p>
+ *
+ * <p> It's also subclass's responsibility to implement the "repeat mode" in
+ * {@link #onPlayCompleted()}.
+ * </p>
+ *
+ * <p>
+ * Apps calls {@link #setSeekProvider(PlaybackSeekDataProvider)} to provide seek data. If the
+ * {@link PlaybackGlueHost} is instance of {@link PlaybackSeekUi}, the provider will be passed to
+ * PlaybackGlueHost to render thumb bitmaps.
+ * </p>
+ * Sample Code:
+ * <pre><code>
+ * public class MyVideoFragment extends VideoFragment {
+ *     &#64;Override
+ *     public void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState);
+ *         final PlaybackTransportControlGlue<MediaPlayerAdapter> playerGlue =
+ *                 new PlaybackTransportControlGlue(getActivity(),
+ *                         new MediaPlayerAdapter(getActivity()));
+ *         playerGlue.setHost(new VideoFragmentGlueHost(this));
+ *         playerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+ *             &#64;Override
+ *             public void onPreparedStateChanged(PlaybackGlue glue) {
+ *                 if (glue.isPrepared()) {
+ *                     playerGlue.setSeekProvider(new MySeekProvider());
+ *                     playerGlue.play();
+ *                 }
+ *             }
+ *         });
+ *         playerGlue.setSubtitle("Leanback artist");
+ *         playerGlue.setTitle("Leanback team at work");
+ *         String uriPath = "android.resource://com.example.android.leanback/raw/video";
+ *         playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
+ *     }
+ * }
+ * </code></pre>
+ * @param <T> Type of {@link PlayerAdapter} passed in constructor.
+ */
+public class PlaybackTransportControlGlue<T extends PlayerAdapter>
+        extends PlaybackBaseControlGlue<T> {
+
+    static final String TAG = "PlaybackTransportGlue";
+    static final boolean DEBUG = false;
+
+    static final int MSG_UPDATE_PLAYBACK_STATE = 100;
+    static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
+
+    PlaybackSeekDataProvider mSeekProvider;
+    boolean mSeekEnabled;
+
+    static class UpdatePlaybackStateHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_UPDATE_PLAYBACK_STATE) {
+                PlaybackTransportControlGlue glue =
+                        ((WeakReference<PlaybackTransportControlGlue>) msg.obj).get();
+                if (glue != null) {
+                    glue.onUpdatePlaybackState();
+                }
+            }
+        }
+    }
+
+    static final Handler sHandler = new UpdatePlaybackStateHandler();
+
+    final WeakReference<PlaybackBaseControlGlue> mGlueWeakReference =  new WeakReference(this);
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param impl Implementation to underlying media player.
+     */
+    public PlaybackTransportControlGlue(Context context, T impl) {
+        super(context, impl);
+    }
+
+    @Override
+    public void setControlsRow(PlaybackControlsRow controlsRow) {
+        super.setControlsRow(controlsRow);
+        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
+        onUpdatePlaybackState();
+    }
+
+    @Override
+    protected void onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter) {
+        primaryActionsAdapter.add(mPlayPauseAction =
+                new PlaybackControlsRow.PlayPauseAction(getContext()));
+    }
+
+    @Override
+    protected PlaybackRowPresenter onCreateRowPresenter() {
+        final AbstractDetailsDescriptionPresenter detailsPresenter =
+                new AbstractDetailsDescriptionPresenter() {
+                    @Override
+                    protected void onBindDescription(ViewHolder
+                            viewHolder, Object obj) {
+                        PlaybackBaseControlGlue glue = (PlaybackBaseControlGlue) obj;
+                        viewHolder.getTitle().setText(glue.getTitle());
+                        viewHolder.getSubtitle().setText(glue.getSubtitle());
+                    }
+                };
+
+        PlaybackTransportRowPresenter rowPresenter = new PlaybackTransportRowPresenter() {
+            @Override
+            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
+                super.onBindRowViewHolder(vh, item);
+                vh.setOnKeyListener(PlaybackTransportControlGlue.this);
+            }
+            @Override
+            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
+                super.onUnbindRowViewHolder(vh);
+                vh.setOnKeyListener(null);
+            }
+        };
+        rowPresenter.setDescriptionPresenter(detailsPresenter);
+        return rowPresenter;
+    }
+
+    @Override
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        super.onAttachedToHost(host);
+
+        if (host instanceof PlaybackSeekUi) {
+            ((PlaybackSeekUi) host).setPlaybackSeekUiClient(mPlaybackSeekUiClient);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromHost() {
+        super.onDetachedFromHost();
+
+        if (getHost() instanceof PlaybackSeekUi) {
+            ((PlaybackSeekUi) getHost()).setPlaybackSeekUiClient(null);
+        }
+    }
+
+    @Override
+    void onUpdateProgress() {
+        if (mControlsRow != null && !mPlaybackSeekUiClient.mIsSeek) {
+            mControlsRow.setCurrentPosition(mPlayerAdapter.isPrepared()
+                    ? mPlayerAdapter.getCurrentPosition() : -1);
+        }
+    }
+
+    @Override
+    public void onActionClicked(Action action) {
+        dispatchAction(action, null);
+    }
+
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                return false;
+        }
+
+        final ObjectAdapter primaryActionsAdapter = mControlsRow.getPrimaryActionsAdapter();
+        Action action = mControlsRow.getActionForKeyCode(primaryActionsAdapter, keyCode);
+        if (action == null) {
+            action = mControlsRow.getActionForKeyCode(mControlsRow.getSecondaryActionsAdapter(),
+                    keyCode);
+        }
+
+        if (action != null) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                dispatchAction(action, event);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    void onUpdatePlaybackStatusAfterUserAction() {
+        updatePlaybackState(mIsPlaying);
+
+        // Sync playback state after a delay
+        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
+        sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_UPDATE_PLAYBACK_STATE,
+                mGlueWeakReference), UPDATE_PLAYBACK_STATE_DELAY_MS);
+    }
+
+    /**
+     * Called when the given action is invoked, either by click or keyevent.
+     */
+    boolean dispatchAction(Action action, KeyEvent keyEvent) {
+        boolean handled = false;
+        if (action instanceof PlaybackControlsRow.PlayPauseAction) {
+            boolean canPlay = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
+            boolean canPause = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
+            //            PLAY_PAUSE    PLAY      PAUSE
+            // playing    paused                  paused
+            // paused     playing       playing
+            // ff/rw      playing       playing   paused
+            if (canPause
+                    && (canPlay ? mIsPlaying :
+                    !mIsPlaying)) {
+                mIsPlaying = false;
+                pause();
+            } else if (canPlay && !mIsPlaying) {
+                mIsPlaying = true;
+                play();
+            }
+            onUpdatePlaybackStatusAfterUserAction();
+            handled = true;
+        } else if (action instanceof PlaybackControlsRow.SkipNextAction) {
+            next();
+            handled = true;
+        } else if (action instanceof PlaybackControlsRow.SkipPreviousAction) {
+            previous();
+            handled = true;
+        }
+        return handled;
+    }
+
+    @Override
+    protected void onPlayStateChanged() {
+        if (DEBUG) Log.v(TAG, "onStateChanged");
+
+        if (sHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference)) {
+            sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
+            if (mPlayerAdapter.isPlaying() != mIsPlaying) {
+                if (DEBUG) Log.v(TAG, "Status expectation mismatch, delaying update");
+                sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_UPDATE_PLAYBACK_STATE,
+                        mGlueWeakReference), UPDATE_PLAYBACK_STATE_DELAY_MS);
+            } else {
+                if (DEBUG) Log.v(TAG, "Update state matches expectation");
+                onUpdatePlaybackState();
+            }
+        } else {
+            onUpdatePlaybackState();
+        }
+
+        super.onPlayStateChanged();
+    }
+
+    void onUpdatePlaybackState() {
+        mIsPlaying = mPlayerAdapter.isPlaying();
+        updatePlaybackState(mIsPlaying);
+    }
+
+    private void updatePlaybackState(boolean isPlaying) {
+        if (mControlsRow == null) {
+            return;
+        }
+
+        if (!isPlaying) {
+            onUpdateProgress();
+            mPlayerAdapter.setProgressUpdatingEnabled(mPlaybackSeekUiClient.mIsSeek);
+        } else {
+            mPlayerAdapter.setProgressUpdatingEnabled(true);
+        }
+
+        if (mFadeWhenPlaying && getHost() != null) {
+            getHost().setControlsOverlayAutoHideEnabled(isPlaying);
+        }
+
+        if (mPlayPauseAction != null) {
+            int index = !isPlaying
+                    ? PlaybackControlsRow.PlayPauseAction.INDEX_PLAY
+                    : PlaybackControlsRow.PlayPauseAction.INDEX_PAUSE;
+            if (mPlayPauseAction.getIndex() != index) {
+                mPlayPauseAction.setIndex(index);
+                notifyItemChanged((ArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter(),
+                        mPlayPauseAction);
+            }
+        }
+    }
+
+    final SeekUiClient mPlaybackSeekUiClient = new SeekUiClient();
+
+    class SeekUiClient extends PlaybackSeekUi.Client {
+        boolean mPausedBeforeSeek;
+        long mPositionBeforeSeek;
+        long mLastUserPosition;
+        boolean mIsSeek;
+
+        @Override
+        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
+            return mSeekProvider;
+        }
+
+        @Override
+        public boolean isSeekEnabled() {
+            return mSeekProvider != null || mSeekEnabled;
+        }
+
+        @Override
+        public void onSeekStarted() {
+            mIsSeek = true;
+            mPausedBeforeSeek = !isPlaying();
+            mPlayerAdapter.setProgressUpdatingEnabled(true);
+            // if we seek thumbnails, we don't need save original position because current
+            // position is not changed during seeking.
+            // otherwise we will call seekTo() and may need to restore the original position.
+            mPositionBeforeSeek = mSeekProvider == null ? mPlayerAdapter.getCurrentPosition() : -1;
+            mLastUserPosition = -1;
+            pause();
+        }
+
+        @Override
+        public void onSeekPositionChanged(long pos) {
+            if (mSeekProvider == null) {
+                mPlayerAdapter.seekTo(pos);
+            } else {
+                mLastUserPosition = pos;
+            }
+            if (mControlsRow != null) {
+                mControlsRow.setCurrentPosition(pos);
+            }
+        }
+
+        @Override
+        public void onSeekFinished(boolean cancelled) {
+            if (!cancelled) {
+                if (mLastUserPosition > 0) {
+                    seekTo(mLastUserPosition);
+                }
+            } else {
+                if (mPositionBeforeSeek >= 0) {
+                    seekTo(mPositionBeforeSeek);
+                }
+            }
+            mIsSeek = false;
+            if (!mPausedBeforeSeek) {
+                play();
+            } else {
+                mPlayerAdapter.setProgressUpdatingEnabled(false);
+                // we neeed update UI since PlaybackControlRow still saves previous position.
+                onUpdateProgress();
+            }
+        }
+    };
+
+    /**
+     * Set seek data provider used during user seeking.
+     * @param seekProvider Seek data provider used during user seeking.
+     */
+    public final void setSeekProvider(PlaybackSeekDataProvider seekProvider) {
+        mSeekProvider = seekProvider;
+    }
+
+    /**
+     * Get seek data provider used during user seeking.
+     * @return Seek data provider used during user seeking.
+     */
+    public final PlaybackSeekDataProvider getSeekProvider() {
+        return mSeekProvider;
+    }
+
+    /**
+     * Enable or disable seek when {@link #getSeekProvider()} is null. When true,
+     * {@link PlayerAdapter#seekTo(long)} will be called during user seeking.
+     *
+     * @param seekEnabled True to enable seek, false otherwise
+     */
+    public final void setSeekEnabled(boolean seekEnabled) {
+        mSeekEnabled = seekEnabled;
+    }
+
+    /**
+     * @return True if seek is enabled without {@link PlaybackSeekDataProvider}, false otherwise.
+     */
+    public final boolean isSeekEnabled() {
+        return mSeekEnabled;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlayerAdapter.java b/v17/leanback/src/android/support/v17/leanback/media/PlayerAdapter.java
new file mode 100644
index 0000000..ea09d04
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlayerAdapter.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+/**
+ * Base class that wraps underlying media player. The class is used by PlaybackGlue, for example
+ * {@link PlaybackTransportControlGlue} is bound to a PlayerAdapter.
+ * This class is intended to be subclassed, {@link MediaPlayerAdapter} is a concrete subclass
+ * using {@link android.media.MediaPlayer}.
+ */
+public abstract class PlayerAdapter {
+
+    /**
+     * Client for client of PlayerAdapter.
+     */
+    public static class Callback {
+
+        /**
+         * Client for Play/Pause state change. See {@link #isPlaying()}.
+         */
+        public void onPlayStateChanged(PlayerAdapter adapter) {
+        }
+
+        /**
+         * Client for {@link #isPrepared()} changed.
+         * @param adapter The adapter that has changed ready state.
+         */
+        public void onPreparedStateChanged(PlayerAdapter adapter) {
+        }
+
+        /**
+         * Client when the current media is finished.
+         * @param adapter The adapter that has just finished current media.
+         */
+        public void onPlayCompleted(PlayerAdapter adapter) {
+        }
+
+        /**
+         * Event for {@link #getCurrentPosition()} changed.
+         * @param adapter The adapter whose {@link #getCurrentPosition()} changed.
+         */
+        public void onCurrentPositionChanged(PlayerAdapter adapter) {
+        }
+
+        /**
+         * Event for {@link #getBufferedPosition()} changed.
+         * @param adapter The adapter whose {@link #getBufferedPosition()} changed.
+         */
+        public void onBufferedPositionChanged(PlayerAdapter adapter) {
+        }
+
+        /**
+         * Event for {@link #getDuration()} changed. Usually the duration does not change
+         * after playing except for live stream.
+         * @param adapter The adapter whose {@link #getDuration()} changed.
+         */
+        public void onDurationChanged(PlayerAdapter adapter) {
+        }
+
+        /**
+         * Event for video size changed.
+         * @param adapter The adapter whose video size has been detected or changed.
+         * @param width Intrinsic width of the video.
+         * @param height Intrinsic height of the video.
+         */
+        public void onVideoSizeChanged(PlayerAdapter adapter, int width, int height) {
+        }
+
+        /**
+         * Event for error.
+         * @param adapter The adapter that encounters error.
+         * @param errorCode Optional error code, specific to implementation.
+         * @param errorMessage Optional error message, specific to implementation.
+         */
+        public void onError(PlayerAdapter adapter, int errorCode, String errorMessage) {
+        }
+
+        /**
+         * Event for buffering start or stop. Initial default value is false.
+         * @param adapter The adapter that begins buffering or finishes buffering.
+         * @param start True for buffering start, false otherwise.
+         */
+        public void onBufferingStateChanged(PlayerAdapter adapter, boolean start) {
+        }
+    }
+
+    Callback mCallback;
+
+    /**
+     * Sets callback for event of PlayerAdapter.
+     * @param callback Client for event of PlayerAdapter.
+     */
+    public final void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Gets callback for event of PlayerAdapter.
+     * @return Client for event of PlayerAdapter.
+     */
+    public final Callback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * @return True if media is ready for playback, false otherwise.
+     */
+    public boolean isPrepared() {
+        return true;
+    }
+
+    /**
+     * Starts the media player.
+     */
+    public abstract void play();
+
+    /**
+     * Pauses the media player.
+     */
+    public abstract void pause();
+
+    /**
+     * Seek to new position.
+     * @param positionInMs New position in milliseconds.
+     */
+    public void seekTo(long positionInMs) {
+    }
+
+    /**
+     * Implement this method to enable or disable progress updating.
+     * @param enable True to enable progress updating, false otherwise.
+     */
+    public void setProgressUpdatingEnabled(boolean enable) {
+    }
+
+    /**
+     * Returns true if media is currently playing.
+     */
+    public boolean isPlaying() {
+        return false;
+    }
+
+    /**
+     * Returns the duration of the media item in milliseconds.
+     */
+    public long getDuration() {
+        return 0;
+    }
+
+    /**
+     * Returns the current position of the media item in milliseconds.
+     */
+    public long getCurrentPosition() {
+        return 0;
+    }
+
+    /**
+     * Returns the current buffered position of the media item in milliseconds.
+     */
+    public long getBufferedPosition() {
+        return 0;
+    }
+
+    /**
+     * This method is called attached to associated {@link PlaybackGlueHost}.
+     * @param host
+     */
+    public void onAttachedToHost(PlaybackGlueHost host) {
+    }
+
+    /**
+     * This method is called when current associated {@link PlaybackGlueHost} is attached to a
+     * different {@link PlaybackGlue} or {@link PlaybackGlueHost} is destroyed. Subclass may
+     * override. A typical implementation will release resources (e.g. MediaPlayer or connection
+     * to playback service) in this method.
+     */
+    public void onDetachedFromHost() {
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java b/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java
index 119626a..679a19a 100644
--- a/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java
@@ -24,7 +24,6 @@
  * the surface holder callback during {@link PlaybackGlue#setHost(PlaybackGlueHost)}.
  *
  * @see PlaybackGlue#setHost(PlaybackGlueHost)
- * @see MediaPlayerGlue
  */
 public interface SurfaceHolderGlueHost {
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
index 2598969..f97d64e 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
 
@@ -36,6 +37,7 @@
      * Kitkat does not allow load custom transition from resource, calling
      * LeanbackTransitionHelperKitKat to build custom transition in code.
      */
+    @RequiresApi(19)
     static class LeanbackTransitionHelperKitKatImpl implements LeanbackTransitionHelperVersion {
 
         @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java b/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java
index b710694..d8ee734 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java
@@ -20,7 +20,6 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
@@ -43,7 +42,6 @@
  * @hide
  */
 @RequiresApi(21)
-@TargetApi(21)
 @RestrictTo(LIBRARY_GROUP)
 public class ParallaxTransition extends Visibility {
 
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 9f92066..de8b374 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.view.Gravity;
 import android.view.View;
@@ -427,6 +428,7 @@
     /**
      * Implementation used on KitKat (and above).
      */
+    @RequiresApi(19)
     static class TransitionHelperKitkatImpl extends TransitionHelperStubImpl {
 
         @Override
@@ -566,6 +568,7 @@
         }
     }
 
+    @RequiresApi(21)
     static final class TransitionHelperApi21Impl extends TransitionHelperKitkatImpl {
 
         @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java b/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
index 523bd4c..487188d 100644
--- a/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
+++ b/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
@@ -13,8 +13,6 @@
  */
 package android.support.v17.leanback.util;
 
-import java.lang.Exception;
-
 /**
  * Math Utilities for leanback library.
  * @hide
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
index ddcf3c6..9f2e452 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
@@ -15,11 +15,10 @@
 
 import android.content.Context;
 import android.graphics.Color;
-import android.util.TypedValue;
+import android.support.v17.leanback.R;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.support.v17.leanback.R;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Action.java b/v17/leanback/src/android/support/v17/leanback/widget/Action.java
index 5e6e313..715fdd9 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Action.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Action.java
@@ -15,7 +15,6 @@
 
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
-import android.view.KeyEvent;
 
 import java.util.ArrayList;
 
@@ -32,7 +31,7 @@
     private Drawable mIcon;
     private CharSequence mLabel1;
     private CharSequence mLabel2;
-    private ArrayList mKeyCodes = new ArrayList();
+    private ArrayList<Integer> mKeyCodes = new ArrayList<>();
 
     /**
      * Constructor for an Action.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java
index 62d94d2..6bc2462 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java
@@ -19,6 +19,7 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.view.View;
 
@@ -47,6 +48,7 @@
         }
     }
 
+    @RequiresApi(19)
     private static final class BackgroundHelperKitkatImpl implements BackgroundHelperVersionImpl {
         BackgroundHelperKitkatImpl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
index 9ca331f..735bb99 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
@@ -891,7 +891,7 @@
          * @param source The layout params to copy from.
          */
         public LayoutParams(LayoutParams source) {
-            super(source);
+            super((ViewGroup.MarginLayoutParams) source);
 
             this.viewType = source.viewType;
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
index 08c4b88..f4e01c0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
@@ -32,66 +32,79 @@
  * An abstract base class for vertically and horizontally scrolling lists. The items come
  * from the {@link RecyclerView.Adapter} associated with this view.
  * Do not directly use this class, use {@link VerticalGridView} and {@link HorizontalGridView}.
- * @hide
+ * The class is not intended to be subclassed other than {@link VerticalGridView} and
+ * {@link HorizontalGridView}.
  */
-@RestrictTo(LIBRARY_GROUP)
-abstract class BaseGridView extends RecyclerView {
+public abstract class BaseGridView extends RecyclerView {
 
     /**
      * Always keep focused item at a aligned position.  Developer can use
      * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
      * In this mode, the last focused position will be remembered and restored when focus
      * is back to the view.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public final static int FOCUS_SCROLL_ALIGNED = 0;
 
     /**
      * Scroll to make the focused item inside client area.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public final static int FOCUS_SCROLL_ITEM = 1;
 
     /**
      * Scroll a page of items when focusing to item outside the client area.
      * The page size matches the client area size of RecyclerView.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public final static int FOCUS_SCROLL_PAGE = 2;
 
     /**
      * The first item is aligned with the low edge of the viewport. When
-     * navigating away from the first item, the focus maintains a middle
-     * location.
+     * navigating away from the first item, the focus item is aligned to a key line location.
      * <p>
-     * For HorizontalGridView, low edge refers to left edge when RTL is false or
-     * right edge when RTL is true.
-     * For VerticalGridView, low edge refers to top edge.
+     * For HorizontalGridView, low edge refers to getPaddingLeft() when RTL is false or
+     * getWidth() - getPaddingRight() when RTL is true.
+     * For VerticalGridView, low edge refers to getPaddingTop().
      * <p>
-     * The middle location is calculated by "windowAlignOffset" and
+     * The key line location is calculated by "windowAlignOffset" and
      * "windowAlignOffsetPercent"; if neither of these two is defined, the
      * default value is 1/2 of the size.
+     * <p>
+     * Note if there are very few items between low edge and key line, use
+     * {@link #setWindowAlignmentPreferKeyLineOverLowEdge(boolean)} to control whether you prefer
+     * to align the items to key line or low edge. Default is preferring low edge.
      */
     public final static int WINDOW_ALIGN_LOW_EDGE = 1;
 
     /**
      * The last item is aligned with the high edge of the viewport when
      * navigating to the end of list. When navigating away from the end, the
-     * focus maintains a middle location.
+     * focus item is aligned to a key line location.
      * <p>
-     * For HorizontalGridView, high edge refers to right edge when RTL is false or
-     * left edge when RTL is true.
-     * For VerticalGridView, high edge refers to bottom edge.
+     * For HorizontalGridView, high edge refers to getWidth() - getPaddingRight() when RTL is false
+     * or getPaddingLeft() when RTL is true.
+     * For VerticalGridView, high edge refers to getHeight() - getPaddingBottom().
      * <p>
-     * The middle location is calculated by "windowAlignOffset" and
+     * The key line location is calculated by "windowAlignOffset" and
      * "windowAlignOffsetPercent"; if neither of these two is defined, the
      * default value is 1/2 of the size.
+     * <p>
+     * Note if there are very few items between high edge and key line, use
+     * {@link #setWindowAlignmentPreferKeyLineOverHighEdge(boolean)} to control whether you prefer
+     * to align the items to key line or high edge. Default is preferring key line.
      */
     public final static int WINDOW_ALIGN_HIGH_EDGE = 1 << 1;
 
     /**
      * The first item and last item are aligned with the two edges of the
      * viewport. When navigating in the middle of list, the focus maintains a
-     * middle location.
+     * key line location.
      * <p>
-     * The middle location is calculated by "windowAlignOffset" and
+     * The key line location is calculated by "windowAlignOffset" and
      * "windowAlignOffsetPercent"; if neither of these two is defined, the
      * default value is 1/2 of the size.
      */
@@ -99,9 +112,9 @@
             WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE;
 
     /**
-     * The focused item always stays in a middle location.
+     * The focused item always stays in a key line location.
      * <p>
-     * The middle location is calculated by "windowAlignOffset" and
+     * The key line location is calculated by "windowAlignOffset" and
      * "windowAlignOffsetPercent"; if neither of these two is defined, the
      * default value is 1/2 of the size.
      */
@@ -199,7 +212,7 @@
      */
     int mInitialPrefetchItemCount = 4;
 
-    public BaseGridView(Context context, AttributeSet attrs, int defStyle) {
+    BaseGridView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         mLayoutManager = new GridLayoutManager(this);
         setLayoutManager(mLayoutManager);
@@ -225,7 +238,7 @@
         });
     }
 
-    protected void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
+    void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);
         boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false);
         boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false);
@@ -252,7 +265,9 @@
      * <li>{@link #FOCUS_SCROLL_ITEM}</li>
      * <li>{@link #FOCUS_SCROLL_PAGE}</li>
      * </ul>
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public void setFocusScrollStrategy(int scrollStrategy) {
         if (scrollStrategy != FOCUS_SCROLL_ALIGNED && scrollStrategy != FOCUS_SCROLL_ITEM
             && scrollStrategy != FOCUS_SCROLL_PAGE) {
@@ -269,7 +284,9 @@
      * <li>{@link #FOCUS_SCROLL_ITEM}</li>
      * <li>{@link #FOCUS_SCROLL_PAGE}</li>
      * </ul>
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public int getFocusScrollStrategy() {
         return mLayoutManager.getFocusScrollStrategy();
     }
@@ -297,7 +314,62 @@
     }
 
     /**
-     * Sets the offset in pixels for window alignment.
+     * Sets whether prefer key line over low edge when {@link #WINDOW_ALIGN_LOW_EDGE} is used.
+     * When true, if there are very few items between low edge and key line, align items to key
+     * line instead of align items to low edge.
+     * Default value is false (aka prefer align to low edge).
+     *
+     * @param preferKeyLineOverLowEdge True to prefer key line over low edge, false otherwise.
+     */
+    public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean preferKeyLineOverLowEdge) {
+        mLayoutManager.mWindowAlignment.mainAxis()
+                .setPreferKeylineOverLowEdge(preferKeyLineOverLowEdge);
+        requestLayout();
+    }
+
+
+    /**
+     * Returns whether prefer key line over high edge when {@link #WINDOW_ALIGN_HIGH_EDGE} is used.
+     * When true, if there are very few items between high edge and key line, align items to key
+     * line instead of align items to high edge.
+     * Default value is true (aka prefer align to key line).
+     *
+     * @param preferKeyLineOverHighEdge True to prefer key line over high edge, false otherwise.
+     */
+    public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean preferKeyLineOverHighEdge) {
+        mLayoutManager.mWindowAlignment.mainAxis()
+                .setPreferKeylineOverHighEdge(preferKeyLineOverHighEdge);
+        requestLayout();
+    }
+
+    /**
+     * Returns whether prefer key line over low edge when {@link #WINDOW_ALIGN_LOW_EDGE} is used.
+     * When true, if there are very few items between low edge and key line, align items to key
+     * line instead of align items to low edge.
+     * Default value is false (aka prefer align to low edge).
+     *
+     * @return True to prefer key line over low edge, false otherwise.
+     */
+    public boolean isWindowAlignmentPreferKeyLineOverLowEdge() {
+        return mLayoutManager.mWindowAlignment.mainAxis().isPreferKeylineOverLowEdge();
+    }
+
+
+    /**
+     * Returns whether prefer key line over high edge when {@link #WINDOW_ALIGN_HIGH_EDGE} is used.
+     * When true, if there are very few items between high edge and key line, align items to key
+     * line instead of align items to high edge.
+     * Default value is true (aka prefer align to key line).
+     *
+     * @return True to prefer key line over high edge, false otherwise.
+     */
+    public boolean isWindowAlignmentPreferKeyLineOverHighEdge() {
+        return mLayoutManager.mWindowAlignment.mainAxis().isPreferKeylineOverHighEdge();
+    }
+
+
+    /**
+     * Sets the offset in pixels for window alignment key line.
      *
      * @param offset The number of pixels to offset.  If the offset is positive,
      *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
@@ -311,7 +383,7 @@
     }
 
     /**
-     * Returns the offset in pixels for window alignment.
+     * Returns the offset in pixels for window alignment key line.
      *
      * @return The number of pixels to offset.  If the offset is positive,
      *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
@@ -324,7 +396,7 @@
     }
 
     /**
-     * Sets the offset percent for window alignment in addition to {@link
+     * Sets the offset percent for window alignment key line in addition to {@link
      * #getWindowAlignmentOffset()}.
      *
      * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
@@ -338,7 +410,7 @@
     }
 
     /**
-     * Returns the offset percent for window alignment in addition to
+     * Returns the offset percent for window alignment key line in addition to
      * {@link #getWindowAlignmentOffset()}.
      *
      * @return Percentage to offset. E.g., 40 means 40% of the width from the
@@ -350,13 +422,12 @@
     }
 
     /**
-     * Sets the absolute offset in pixels for item alignment.
+     * Sets number of pixels to the end of low edge. Supports right to left layout direction.
      * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
      * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
      *
-     * @param offset The number of pixels to offset. Can be negative for
-     *        alignment from the high edge, or positive for alignment from the
-     *        low edge.
+     * @param offset In left to right or vertical case, it's the offset added to left/top edge.
+     *               In right to left case, it's the offset subtracted from right edge.
      */
     public void setItemAlignmentOffset(int offset) {
         mLayoutManager.setItemAlignmentOffset(offset);
@@ -364,23 +435,27 @@
     }
 
     /**
-     * Returns the absolute offset in pixels for item alignment.
+     * Returns number of pixels to the end of low edge. Supports right to left layout direction. In
+     * left to right or vertical case, it's the offset added to left/top edge. In right to left
+     * case, it's the offset subtracted from right edge.
+     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
+     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
      *
-     * @return The number of pixels to offset. Will be negative for alignment
-     *         from the high edge, or positive for alignment from the low edge.
-     *         Default value is 0.
+     * @return The number of pixels to the end of low edge.
      */
     public int getItemAlignmentOffset() {
         return mLayoutManager.getItemAlignmentOffset();
     }
 
     /**
-     * Set to true if include padding in calculating item align offset.
-     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
-     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
-     *
-     * @param withPadding When it is true: we include left/top padding for positive
-     *          item offset, include right/bottom padding for negative item offset.
+     * Sets whether applies padding to item alignment when {@link #getItemAlignmentOffsetPercent()}
+     * is 0 or 100.
+     * <p>When true:
+     * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0.
+     * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100.
+     * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
+     * </p>
+     * <p>When false: does not apply padding</p>
      */
     public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
         mLayoutManager.setItemAlignmentOffsetWithPadding(withPadding);
@@ -388,7 +463,14 @@
     }
 
     /**
-     * Returns true if include padding in calculating item align offset.
+     * Returns true if applies padding to item alignment when
+     * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise.
+     * <p>When true:
+     * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0.
+     * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100.
+     * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
+     * </p>
+     * <p>When false: does not apply padding</p>
      */
     public boolean isItemAlignmentOffsetWithPadding() {
         return mLayoutManager.isItemAlignmentOffsetWithPadding();
@@ -423,8 +505,8 @@
 
     /**
      * Sets the id of the view to align with. Use {@link android.view.View#NO_ID} (default)
-     * for the item view itself.
-     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
+     * for the root {@link RecyclerView.ViewHolder#itemView}.
+     * Item alignment settings on BaseGridView are if {@link ItemAlignmentFacet}
      * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
      */
     public void setItemAlignmentViewId(int viewId) {
@@ -432,7 +514,10 @@
     }
 
     /**
-     * Returns the id of the view to align with, or zero for the item view itself.
+     * Returns the id of the view to align with, or {@link android.view.View#NO_ID} for the root
+     * {@link RecyclerView.ViewHolder#itemView}.
+     * @return The id of the view to align with, or {@link android.view.View#NO_ID} for the root
+     * {@link RecyclerView.ViewHolder#itemView}.
      */
     public int getItemAlignmentViewId() {
         return mLayoutManager.getItemAlignmentViewId();
@@ -448,7 +533,8 @@
     }
 
     /**
-     * Sets the spacing in pixels between two child items.
+     * Sets the vertical and horizontal spacing in pixels between two child items.
+     * @param spacing Vertical and horizontal spacing in pixels between two child items.
      */
     public void setItemSpacing(int spacing) {
         mLayoutManager.setItemSpacing(spacing);
@@ -492,7 +578,8 @@
     }
 
     /**
-     * Sets the spacing in pixels between two child items vertically.
+     * Sets the vertical spacing in pixels between two child items.
+     * @param spacing Vertical spacing between two child items.
      */
     public void setVerticalSpacing(int spacing) {
         mLayoutManager.setVerticalSpacing(spacing);
@@ -500,14 +587,16 @@
     }
 
     /**
-     * Returns the spacing in pixels between two child items vertically.
+     * Returns the vertical spacing in pixels between two child items.
+     * @return The vertical spacing in pixels between two child items.
      */
     public int getVerticalSpacing() {
         return mLayoutManager.getVerticalSpacing();
     }
 
     /**
-     * Sets the spacing in pixels between two child items horizontally.
+     * Sets the horizontal spacing in pixels between two child items.
+     * @param spacing Horizontal spacing in pixels between two child items.
      */
     public void setHorizontalSpacing(int spacing) {
         mLayoutManager.setHorizontalSpacing(spacing);
@@ -515,7 +604,8 @@
     }
 
     /**
-     * Returns the spacing in pixels between two child items horizontally.
+     * Returns the horizontal spacing in pixels between two child items.
+     * @return The Horizontal spacing in pixels between two child items.
      */
     public int getHorizontalSpacing() {
         return mLayoutManager.getHorizontalSpacing();
@@ -588,7 +678,9 @@
 
     /**
      * Changes the selected item and/or subposition immediately without animation.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public void setSelectedPositionWithSub(int position, int subposition) {
         mLayoutManager.setSelectionWithSub(position, subposition, 0);
     }
@@ -606,7 +698,9 @@
      * Changes the selected item and/or subposition immediately without animation, scrollExtra is
      * applied in primary scroll direction.  The scrollExtra will be kept until
      * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public void setSelectedPositionWithSub(int position, int subposition, int scrollExtra) {
         mLayoutManager.setSelectionWithSub(position, subposition, scrollExtra);
     }
@@ -614,6 +708,7 @@
     /**
      * Changes the selected item and run an animation to scroll to the target
      * position.
+     * @param position Adapter position of the item to select.
      */
     public void setSelectedPositionSmooth(int position) {
         mLayoutManager.setSelectionSmooth(position);
@@ -622,7 +717,9 @@
     /**
      * Changes the selected item and/or subposition, runs an animation to scroll to the target
      * position.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public void setSelectedPositionSmoothWithSub(int position, int subposition) {
         mLayoutManager.setSelectionSmoothWithSub(position, subposition);
     }
@@ -680,7 +777,8 @@
     }
 
     /**
-     * Returns the selected item position.
+     * Returns the adapter position of selected item.
+     * @return The adapter position of selected item.
      */
     public int getSelectedPosition() {
         return mLayoutManager.getSelection();
@@ -691,15 +789,17 @@
      * multiple {@link ItemAlignmentFacet}s provided by {@link RecyclerView.ViewHolder}
      * or {@link FacetProviderAdapter}.  Zero is returned when no {@link ItemAlignmentFacet}
      * is defined.
+     * @hide
      */
+    @RestrictTo(LIBRARY_GROUP)
     public int getSelectedSubPosition() {
         return mLayoutManager.getSubSelection();
     }
 
     /**
-     * Sets whether an animation should run when a child changes size or when adding
+     * Sets whether ItemAnimator should run when a child changes size or when adding
      * or removing a child.
-     * <p><i>Unstable API, might change later.</i>
+     * @param animateChildLayout True to enable ItemAnimator, false to disable.
      */
     public void setAnimateChildLayout(boolean animateChildLayout) {
         if (mAnimateChildLayout != animateChildLayout) {
@@ -716,7 +816,7 @@
     /**
      * Returns true if an animation will run when a child changes size or when
      * adding or removing a child.
-     * <p><i>Unstable API, might change later.</i>
+     * @return True if ItemAnimator is enabled, false otherwise.
      */
     public boolean isChildLayoutAnimated() {
         return mAnimateChildLayout;
@@ -781,6 +881,7 @@
 
     /**
      * Disables or enables focus search.
+     * @param disabled True to disable focus search, false to enable.
      */
     public final void setFocusSearchDisabled(boolean disabled) {
         // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
@@ -791,6 +892,7 @@
 
     /**
      * Returns true if focus search is disabled.
+     * @return True if focus search is disabled.
      */
     public final boolean isFocusSearchDisabled() {
         return mLayoutManager.isFocusSearchDisabled();
@@ -799,6 +901,7 @@
     /**
      * Enables or disables layout.  All children will be removed when layout is
      * disabled.
+     * @param layoutEnabled True to enable layout, false otherwise.
      */
     public void setLayoutEnabled(boolean layoutEnabled) {
         mLayoutManager.setLayoutEnabled(layoutEnabled);
@@ -806,6 +909,7 @@
 
     /**
      * Changes and overrides children's visibility.
+     * @param visibility See {@link View#getVisibility()}.
      */
     public void setChildrenVisibility(int visibility) {
         mLayoutManager.setChildrenVisibility(visibility);
@@ -813,6 +917,7 @@
 
     /**
      * Enables or disables pruning of children.  Disable is useful during transition.
+     * @param pruneChild True to prune children out side visible area, false to enable.
      */
     public void setPruneChild(boolean pruneChild) {
         mLayoutManager.setPruneChild(pruneChild);
@@ -820,13 +925,15 @@
 
     /**
      * Enables or disables scrolling.  Disable is useful during transition.
+     * @param scrollEnabled True to enable scroll, false to disable.
      */
     public void setScrollEnabled(boolean scrollEnabled) {
         mLayoutManager.setScrollEnabled(scrollEnabled);
     }
 
     /**
-     * Returns true if scrolling is enabled.
+     * Returns true if scrolling is enabled, false otherwise.
+     * @return True if scrolling is enabled, false otherwise.
      */
     public boolean isScrollEnabled() {
         return mLayoutManager.isScrollEnabled();
@@ -835,24 +942,25 @@
     /**
      * Returns true if the view at the given position has a same row sibling
      * in front of it.  This will return true if first item view is not created.
-     * So application should check in both {@link OnChildSelectedListener} and {@link
-     * OnChildLaidOutListener}.
      *
      * @param position Position in adapter.
+     * @return True if the view at the given position has a same row sibling in front of it.
      */
     public boolean hasPreviousViewInSameRow(int position) {
         return mLayoutManager.hasPreviousViewInSameRow(position);
     }
 
     /**
-     * Enables or disables the default "focus draw at last" order rule.
+     * Enables or disables the default "focus draw at last" order rule. Default is enabled.
+     * @param enabled True to draw the selected child at last, false otherwise.
      */
     public void setFocusDrawingOrderEnabled(boolean enabled) {
         super.setChildrenDrawingOrderEnabled(enabled);
     }
 
     /**
-     * Returns true if default "focus draw at last" order rule is enabled.
+     * Returns true if draws selected child at last, false otherwise. Default is enabled.
+     * @return True if draws selected child at last, false otherwise.
      */
     public boolean isFocusDrawingOrderEnabled() {
         return super.isChildrenDrawingOrderEnabled();
@@ -860,6 +968,7 @@
 
     /**
      * Sets the touch intercept listener.
+     * @param listener The touch intercept listener.
      */
     public void setOnTouchInterceptListener(OnTouchInterceptListener listener) {
         mOnTouchInterceptListener = listener;
@@ -867,6 +976,7 @@
 
     /**
      * Sets the generic motion intercept listener.
+     * @param listener The motion intercept listener.
      */
     public void setOnMotionInterceptListener(OnMotionInterceptListener listener) {
         mOnMotionInterceptListener = listener;
@@ -874,6 +984,7 @@
 
     /**
      * Sets the key intercept listener.
+     * @param listener The key intercept listener.
      */
     public void setOnKeyInterceptListener(OnKeyInterceptListener listener) {
         mOnKeyInterceptListener = listener;
@@ -881,6 +992,7 @@
 
     /**
      * Sets the unhandled key listener.
+     * @param listener The unhandled key intercept listener.
      */
     public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
         mOnUnhandledKeyListener = listener;
@@ -888,6 +1000,7 @@
 
     /**
      * Returns the unhandled key listener.
+     * @return The unhandled key listener.
      */
     public OnUnhandledKeyListener getOnUnhandledKeyListener() {
         return mOnUnhandledKeyListener;
@@ -915,7 +1028,7 @@
     }
 
     @Override
-    public boolean dispatchGenericFocusedEvent(MotionEvent event) {
+    protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
         if (mOnMotionInterceptListener != null) {
             if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
                 return true;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java
index be5fd83..1942ae0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java
@@ -31,6 +31,7 @@
     private int mChildMarginFromCenter;
     private OnChildFocusedListener mOnChildFocusedListener;
     int mLastFocusIndex = -1;
+    boolean mDefaultFocusToMiddle = true;
 
     public ControlBar(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -40,11 +41,19 @@
         super(context, attrs, defStyle);
     }
 
+    void setDefaultFocusToMiddle(boolean defaultFocusToMiddle) {
+        mDefaultFocusToMiddle = defaultFocusToMiddle;
+    }
+
+    int getDefaultFocusIndex() {
+        return mDefaultFocusToMiddle ? getChildCount() / 2 : 0;
+    }
+
     @Override
     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
         if (getChildCount() > 0) {
             int index = mLastFocusIndex >= 0 && mLastFocusIndex < getChildCount()
-                    ? mLastFocusIndex : getChildCount() / 2;
+                    ? mLastFocusIndex : getDefaultFocusIndex();
             if (getChildAt(index).requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
@@ -58,7 +67,7 @@
             if (mLastFocusIndex >= 0 && mLastFocusIndex < getChildCount()) {
                 views.add(getChildAt(mLastFocusIndex));
             } else if (getChildCount() > 0) {
-                views.add(getChildAt(getChildCount() / 2));
+                views.add(getChildAt(getDefaultFocusIndex()));
             }
         } else {
             super.addFocusables(views, direction, focusableMode);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
index 4314fce..a3319ba 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
@@ -82,6 +82,7 @@
             if (mControlBar == null) {
                 throw new IllegalStateException("Couldn't find control_bar");
             }
+            mControlBar.setDefaultFocusToMiddle(mDefaultFocusToMiddle);
             mControlBar.setOnChildFocusedListener(new ControlBar.OnChildFocusedListener() {
                 @Override
                 public void onChildFocusedListener(View child, View focused) {
@@ -185,6 +186,7 @@
     private int mLayoutResourceId;
     private static int sChildMarginDefault;
     private static int sControlIconWidth;
+    boolean mDefaultFocusToMiddle = true;
 
     /**
      * Constructor for a ControlBarPresenter.
@@ -281,4 +283,12 @@
         }
         return sControlIconWidth;
     }
+
+    /**
+     * @param defaultFocusToMiddle True for middle item, false for 0.
+     */
+    void setDefaultFocusToMiddle(boolean defaultFocusToMiddle) {
+        mDefaultFocusToMiddle = defaultFocusToMiddle;
+    }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
index 22dba9c..f23acbc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
@@ -94,7 +94,7 @@
         ViewHolder vh = new ViewHolder(view);
         ViewGroup.LayoutParams lp = view.getLayoutParams();
         vh.setSizeFromDrawableIntrinsic(lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
-                && lp.width == ViewGroup.LayoutParams.WRAP_CONTENT);
+                && lp.height == ViewGroup.LayoutParams.WRAP_CONTENT);
         return vh;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
index c7aaf6c..9e8dbcd 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
@@ -17,7 +17,6 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.view.KeyEvent;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
index d484bcc..98b9f78 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
@@ -61,7 +61,7 @@
 @Deprecated
 public class DetailsOverviewRowPresenter extends RowPresenter {
 
-    static final String TAG = "DetailsOverviewRowPresenter";
+    static final String TAG = "DetailsOverviewRowP";
     static final boolean DEBUG = false;
 
     private static final int MORE_ACTIONS_FADE_MS = 100;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
index 50f0da3..0d86cfd 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
@@ -177,6 +177,9 @@
         mViewHolder.mActionsRow.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
         mViewHolder.mActionsRow.setVisibility(View.VISIBLE);
         mViewHolder.mActionsRow.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        // switch focusability to VISIBLE wont trigger focusableViewAvailable() on O because
+        // shared element details_frame is still INVISIBLE. b/63544781
+        mViewHolder.mActionsRow.requestFocus();
         mViewHolder.mDetailsDescriptionFrame.setVisibility(View.VISIBLE);
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
index 988a9fc..49565ab 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
@@ -22,8 +22,10 @@
 import android.animation.TimeAnimator;
 import android.content.res.Resources;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.app.HeadersFragment;
 import android.support.v17.leanback.graphics.ColorOverlayDimmer;
 import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -203,24 +205,26 @@
      * Sets up default focus highlight behavior of a focused item in header list. It would scale
      * the focused item and update
      * {@link RowHeaderPresenter#onSelectLevelChanged(RowHeaderPresenter.ViewHolder)}.
-     * Equivalent to call setupHeaderItemFocusHighlight(gridView, true). This method should be
-     * called after header fragment onViewCreated().
+     * Equivalent to call setupHeaderItemFocusHighlight(gridView, true).
      *
      * @param gridView  The header list.
+     * @deprecated Use {@link #setupHeaderItemFocusHighlight(ItemBridgeAdapter)}
      */
+    @Deprecated
     public static void setupHeaderItemFocusHighlight(VerticalGridView gridView) {
         setupHeaderItemFocusHighlight(gridView, true);
     }
 
     /**
-     * Sets up the focus highlight behavior of a focused item in header list. This method should be
-     * called after header fragment onViewCreated().
+     * Sets up the focus highlight behavior of a focused item in header list.
      *
      * @param gridView  The header list.
      * @param scaleEnabled True if scale the item when focused, false otherwise. Note that
      * {@link RowHeaderPresenter#onSelectLevelChanged(RowHeaderPresenter.ViewHolder)}
      * will always be called regardless value of scaleEnabled.
+     * @deprecated Use {@link #setupHeaderItemFocusHighlight(ItemBridgeAdapter, boolean)}
      */
+    @Deprecated
     public static void setupHeaderItemFocusHighlight(VerticalGridView gridView,
                                                      boolean scaleEnabled) {
         if (gridView != null && gridView.getAdapter() instanceof ItemBridgeAdapter) {
@@ -229,6 +233,33 @@
         }
     }
 
+    /**
+     * Sets up default focus highlight behavior of a focused item in header list. It would scale
+     * the focused item and update
+     * {@link RowHeaderPresenter#onSelectLevelChanged(RowHeaderPresenter.ViewHolder)}.
+     * Equivalent to call setupHeaderItemFocusHighlight(itemBridgeAdapter, true).
+     *
+     * @param adapter  The adapter of HeadersFragment.
+     * @see {@link HeadersFragment#getBridgeAdapter()}
+     */
+    public static void setupHeaderItemFocusHighlight(ItemBridgeAdapter adapter) {
+        setupHeaderItemFocusHighlight(adapter, true);
+    }
+
+    /**
+     * Sets up the focus highlight behavior of a focused item in header list.
+     *
+     * @param adapter  The adapter of HeadersFragment.
+     * @param scaleEnabled True if scale the item when focused, false otherwise. Note that
+     * {@link RowHeaderPresenter#onSelectLevelChanged(RowHeaderPresenter.ViewHolder)}
+     * will always be called regardless value of scaleEnabled.
+     * @see {@link HeadersFragment#getBridgeAdapter()}
+     */
+    public static void setupHeaderItemFocusHighlight(ItemBridgeAdapter adapter,
+            boolean scaleEnabled) {
+        adapter.setFocusHighlight(new HeaderItemFocusHighlight(scaleEnabled));
+    }
+
     static class HeaderItemFocusHighlight implements FocusHighlightHandler {
         private boolean mInitialized;
         private float mSelectScale;
@@ -242,11 +273,15 @@
         void lazyInit(View view) {
             if (!mInitialized) {
                 Resources res = view.getResources();
-                mSelectScale = mScaleEnabled
-                        ? Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale))
-                        : 1f;
-                mDuration =
-                        Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration));
+                TypedValue value = new TypedValue();
+                if (mScaleEnabled) {
+                    res.getValue(R.dimen.lb_browse_header_select_scale, value, true);
+                    mSelectScale = value.getFloat();
+                } else {
+                    mSelectScale = 1f;
+                }
+                res.getValue(R.dimen.lb_browse_header_select_duration, value, true);
+                mDuration = value.data;
                 mInitialized = true;
             }
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
index 252a317..4c2a857 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
@@ -2,8 +2,8 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.View;
-import android.view.ViewGroup;
 
 final class ForegroundHelper {
 
@@ -23,6 +23,7 @@
     /**
      * Implementation used on api 23 (and above).
      */
+    @RequiresApi(23)
     private static final class ForegroundHelperApi23Impl implements ForegroundHelperVersionImpl {
         ForegroundHelperApi23Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
index 189dfe6..dad4414 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
@@ -62,7 +62,7 @@
  */
 public class FullWidthDetailsOverviewRowPresenter extends RowPresenter {
 
-    static final String TAG = "FullWidthDetailsOverviewRowPresenter";
+    static final String TAG = "FullWidthDetailsRP";
     static final boolean DEBUG = false;
 
     private static Rect sTmpRect = new Rect();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
index eb09225..332a7bc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
@@ -17,8 +17,10 @@
 import android.support.annotation.Nullable;
 import android.support.v4.util.CircularIntArray;
 import android.support.v7.widget.RecyclerView;
+import android.util.SparseIntArray;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 
 /**
  * A grid is representation of single or multiple rows layout data structure and algorithm.
@@ -42,6 +44,8 @@
      */
     public static final int START_DEFAULT = -1;
 
+    Object[] mTmpItem = new Object[1];
+
     /**
      * When user uses Grid,  he should provide count of items and
      * the method to create item and remove item.
@@ -51,7 +55,17 @@
         /**
          * Return how many items (are in the adapter).
          */
-        public abstract int getCount();
+        int getCount();
+
+        /**
+         * @return Min index to prepend, usually it's 0; but in the preLayout case,
+         * when grid was showing 5,6,7.  Removing 3,4,5 will make the layoutPosition to
+         * be 3(deleted),4,5 in prelayout pass; Grid's index is still 5,6,7 in prelayout.
+         * When we prepend in prelayout, we can call createItem(4), createItem(3), createItem(2),
+         * the minimal index is 2, which is also the delta to mapping to layoutPosition in
+         * prelayout pass.
+         */
+        int getMinIndex();
 
         /**
          * Create visible item and where the provider should measure it.
@@ -60,9 +74,12 @@
          * @param append  True if new item is after last visible item, false if new item is
          *                before first visible item.
          * @param item    item[0] returns created item that will be passed in addItem() call.
+         * @param disappearingItem The item is a disappearing item added by
+         *                         {@link Grid#fillDisappearingItems(int[], int, SparseIntArray)}.
+         *
          * @return length of the item.
          */
-        public abstract int createItem(int index, boolean append, Object[] item);
+        int createItem(int index, boolean append, Object[] item, boolean disappearingItem);
 
         /**
          * add item to given row and given edge.  The call is always after createItem().
@@ -72,26 +89,26 @@
          * @param rowIndex  Row index to put the item
          * @param edge      min_edge if not reversed or max_edge if reversed.
          */
-        public abstract void addItem(Object item, int index, int length, int rowIndex, int edge);
+        void addItem(Object item, int index, int length, int rowIndex, int edge);
 
         /**
          * Remove visible item at index.
          * @param index     0-based index of the item in provider
          */
-        public abstract void removeItem(int index);
+        void removeItem(int index);
 
         /**
          * Get edge of an existing visible item. edge will be the min_edge
          * if not reversed or the max_edge if reversed.
          * @param index     0-based index of the item in provider
          */
-        public abstract int getEdge(int index);
+        int getEdge(int index);
 
         /**
          * Get size of an existing visible item.
          * @param index     0-based index of the item in provider
          */
-        public abstract int getSize(int index);
+        int getSize(int index);
     }
 
     /**
@@ -227,7 +244,9 @@
 
     /**
      * Invalidate items after or equal to index. This will remove visible items
-     * after that and invalidate cache of layout results after that.
+     * after that and invalidate cache of layout results after that. Note that it's client's
+     * responsibility to perform removing child action, {@link Provider#removeItem(int)} will not
+     * be called because the index might be invalidated.
      */
     public void invalidateItemsAfter(int index) {
         if (index < 0) {
@@ -236,9 +255,8 @@
         if (mLastVisibleIndex < 0) {
             return;
         }
-        while (mLastVisibleIndex >= index) {
-            mProvider.removeItem(mLastVisibleIndex);
-            mLastVisibleIndex--;
+        if (mLastVisibleIndex >= index) {
+            mLastVisibleIndex = index - 1;
         }
         resetVisibleIndexIfEmpty();
         if (getFirstVisibleIndex() < 0) {
@@ -250,7 +268,11 @@
      * Gets the row index of item at given index.
      */
     public final int getRowIndex(int index) {
-        return getLocation(index).row;
+        Location location = getLocation(index);
+        if (location == null) {
+            return -1;
+        }
+        return location.row;
     }
 
     /**
@@ -385,6 +407,8 @@
 
     /**
      * Removes invisible items from end until reaches item at aboveIndex or toLimit.
+     * @param aboveIndex Don't remove items whose index is equals or smaller than aboveIndex
+     * @param toLimit Don't remove items whose left edge is less than toLimit.
      */
     public void removeInvisibleItemsAtEnd(int aboveIndex, int toLimit) {
         while(mLastVisibleIndex >= mFirstVisibleIndex && mLastVisibleIndex > aboveIndex) {
@@ -402,13 +426,15 @@
 
     /**
      * Removes invisible items from front until reaches item at belowIndex or toLimit.
+     * @param belowIndex Don't remove items whose index is equals or larger than belowIndex
+     * @param toLimit Don't remove items whose right edge is equals or greater than toLimit.
      */
     public void removeInvisibleItemsAtFront(int belowIndex, int toLimit) {
         while(mLastVisibleIndex >= mFirstVisibleIndex && mFirstVisibleIndex < belowIndex) {
-            boolean offFront = !mReversedFlow ? mProvider.getEdge(mFirstVisibleIndex)
-                    + mProvider.getSize(mFirstVisibleIndex) <= toLimit
-                    : mProvider.getEdge(mFirstVisibleIndex)
-                            - mProvider.getSize(mFirstVisibleIndex) >= toLimit;
+            final int size = mProvider.getSize(mFirstVisibleIndex);
+            boolean offFront = !mReversedFlow
+                    ? mProvider.getEdge(mFirstVisibleIndex) + size <= toLimit
+                    : mProvider.getEdge(mFirstVisibleIndex) - size >= toLimit;
             if (offFront) {
                 mProvider.removeItem(mFirstVisibleIndex);
                 mFirstVisibleIndex++;
@@ -426,6 +452,73 @@
     }
 
     /**
+     * Fill disappearing items, i.e. the items are moved out of window, we need give them final
+     * location so recyclerview will run a slide out animation. The positions that was greater than
+     * last visible index will be appended to end, the positions that was smaller than first visible
+     * index will be prepend to beginning.
+     * @param positions Sorted list of positions of disappearing items.
+     * @param positionToRow Which row we want to put the disappearing item.
+     */
+    public void fillDisappearingItems(int[] positions, int positionsLength,
+            SparseIntArray positionToRow) {
+        final int lastPos = getLastVisibleIndex();
+        final int resultSearchLast = lastPos >= 0
+                ? Arrays.binarySearch(positions, 0, positionsLength, lastPos) : 0;
+        if (resultSearchLast < 0) {
+            // we shouldn't find lastPos in disappearing position list.
+            int firstDisappearingIndex = -resultSearchLast - 1;
+            int edge;
+            if (mReversedFlow) {
+                edge = mProvider.getEdge(lastPos) - mProvider.getSize(lastPos) - mSpacing;
+            } else {
+                edge = mProvider.getEdge(lastPos) + mProvider.getSize(lastPos) + mSpacing;
+            }
+            for (int i = firstDisappearingIndex; i < positionsLength; i++) {
+                int disappearingIndex = positions[i];
+                int disappearingRow = positionToRow.get(disappearingIndex);
+                if (disappearingRow < 0) {
+                    disappearingRow = 0; // if not found put in row 0
+                }
+                int size = mProvider.createItem(disappearingIndex, true, mTmpItem, true);
+                mProvider.addItem(mTmpItem[0], disappearingIndex, size, disappearingRow, edge);
+                if (mReversedFlow) {
+                    edge = edge - size - mSpacing;
+                } else {
+                    edge = edge + size + mSpacing;
+                }
+            }
+        }
+
+        final int firstPos = getFirstVisibleIndex();
+        final int resultSearchFirst = firstPos >= 0
+                ? Arrays.binarySearch(positions, 0, positionsLength, firstPos) : 0;
+        if (resultSearchFirst < 0) {
+            // we shouldn't find firstPos in disappearing position list.
+            int firstDisappearingIndex = -resultSearchFirst - 2;
+            int edge;
+            if (mReversedFlow) {
+                edge = mProvider.getEdge(firstPos);
+            } else {
+                edge = mProvider.getEdge(firstPos);
+            }
+            for (int i = firstDisappearingIndex; i >= 0; i--) {
+                int disappearingIndex = positions[i];
+                int disappearingRow = positionToRow.get(disappearingIndex);
+                if (disappearingRow < 0) {
+                    disappearingRow = 0; // if not found put in row 0
+                }
+                int size = mProvider.createItem(disappearingIndex, false, mTmpItem, true);
+                if (mReversedFlow) {
+                    edge = edge + mSpacing + size;
+                } else {
+                    edge = edge - mSpacing - size;
+                }
+                mProvider.addItem(mTmpItem[0], disappearingIndex, size, disappearingRow, edge);
+            }
+        }
+    }
+
+    /**
      * Queries items adjacent to the viewport (in the direction of da) into the prefetch registry.
      */
     public void collectAdjacentPrefetchPositions(int fromLimit, int da,
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 2c9f069..f2dae95 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -37,6 +37,7 @@
 import android.support.v7.widget.RecyclerView.State;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.SparseIntArray;
 import android.view.FocusFinder;
 import android.view.Gravity;
 import android.view.View;
@@ -48,6 +49,8 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 final class GridLayoutManager extends RecyclerView.LayoutManager {
 
@@ -275,13 +278,13 @@
         }
 
         void increasePendingMoves() {
-            if (mPendingMoves < MAX_PENDING_MOVES) {
+            if (mPendingMoves < mMaxPendingMoves) {
                 mPendingMoves++;
             }
         }
 
         void decreasePendingMoves() {
-            if (mPendingMoves > -MAX_PENDING_MOVES) {
+            if (mPendingMoves > -mMaxPendingMoves) {
                 mPendingMoves--;
             }
         }
@@ -377,7 +380,8 @@
     static final boolean TRACE = false;
 
     // maximum pending movement in one direction.
-    final static int MAX_PENDING_MOVES = 10;
+    static final int DEFAULT_MAX_PENDING_MOVES = 10;
+    int mMaxPendingMoves = DEFAULT_MAX_PENDING_MOVES;
     // minimal milliseconds to scroll window size in major direction,  we put a cap to prevent the
     // effect smooth scrolling too over to bind an item view then drag the item view back.
     final static int MIN_MS_SMOOTH_SCROLL_MAIN_SCREEN = 30;
@@ -423,6 +427,18 @@
     private OrientationHelper mOrientationHelper = OrientationHelper.createHorizontalHelper(this);
 
     RecyclerView.State mState;
+    // Suppose currently showing 4, 5, 6, 7; removing 2,3,4 will make the layoutPosition to be
+    // 2(deleted), 3, 4, 5 in prelayout pass. So when we add item in prelayout, we must subtract 2
+    // from index of Grid.createItem.
+    int mPositionDeltaInPreLayout;
+    // Extra layout space needs to fill in prelayout pass. Note we apply the extra space to both
+    // appends and prepends due to the fact leanback is doing mario scrolling: removing items to
+    // the left of focused item might need extra layout on the right.
+    int mExtraLayoutSpaceInPreLayout;
+    // mPositionToRowInPostLayout and mDisappearingPositions are temp variables in post layout.
+    final SparseIntArray mPositionToRowInPostLayout = new SparseIntArray();
+    int[] mDisappearingPositions;
+
     RecyclerView.Recycler mRecycler;
 
     private static final Rect sTempRect = new Rect();
@@ -488,12 +504,16 @@
     /**
      * override child visibility
      */
-    int mChildVisibility = -1;
+    @Visibility
+    int mChildVisibility;
 
     /**
-     * The scroll offsets of the viewport relative to the entire view.
+     * Pixels that scrolled in secondary forward direction. Negative value means backward.
+     * Note that we treat secondary differently than main. For the main axis, update scroll min/max
+     * based on first/last item's view location. For second axis, we don't use item's view location.
+     * We are using the {@link #getRowSizeSecondary(int)} plus mScrollOffsetSecondary. see
+     * details in {@link #updateSecondaryScrollLimits()}.
      */
-    private int mScrollOffsetPrimary;
     int mScrollOffsetSecondary;
 
     /**
@@ -649,6 +669,7 @@
 
     public GridLayoutManager(BaseGridView baseGridView) {
         mBaseGridView = baseGridView;
+        mChildVisibility = -1;
     }
 
     public void setOrientation(int orientation) {
@@ -665,13 +686,20 @@
     }
 
     public void onRtlPropertiesChanged(int layoutDirection) {
+        boolean reversePrimary, reverseSecondary;
         if (mOrientation == HORIZONTAL) {
-            mReverseFlowPrimary = layoutDirection == View.LAYOUT_DIRECTION_RTL;
-            mReverseFlowSecondary = false;
+            reversePrimary = layoutDirection == View.LAYOUT_DIRECTION_RTL;
+            reverseSecondary = false;
         } else {
-            mReverseFlowSecondary = layoutDirection == View.LAYOUT_DIRECTION_RTL;
-            mReverseFlowPrimary = false;
+            reverseSecondary = layoutDirection == View.LAYOUT_DIRECTION_RTL;
+            reversePrimary = false;
         }
+        if (mReverseFlowPrimary == reversePrimary && mReverseFlowSecondary == reverseSecondary) {
+            return;
+        }
+        mReverseFlowPrimary = reversePrimary;
+        mReverseFlowSecondary = reverseSecondary;
+        mForceFullLayout = true;
         mWindowAlignment.horizontal.setReversedFlow(layoutDirection == View.LAYOUT_DIRECTION_RTL);
     }
 
@@ -868,7 +896,7 @@
         mChildLaidOutListener = listener;
     }
 
-    private int getPositionByView(View view) {
+    private int getAdapterPositionByView(View view) {
         if (view == null) {
             return NO_POSITION;
         }
@@ -905,8 +933,8 @@
         return 0;
     }
 
-    private int getPositionByIndex(int index) {
-        return getPositionByView(getChildAt(index));
+    private int getAdapterPositionByIndex(int index) {
+        return getAdapterPositionByView(getChildAt(index));
     }
 
     void dispatchChildSelected() {
@@ -1079,6 +1107,16 @@
         return (mOrientation == HORIZONTAL) ? getViewCenterX(view) : getViewCenterY(view);
     }
 
+    private int getAdjustedViewCenter(View view) {
+        if (view.hasFocus()) {
+            View child = view.findFocus();
+            if (child != null && child != view) {
+                return getAdjustedPrimaryAlignedScrollDistance(getViewCenter(view), view, child);
+            }
+        }
+        return getViewCenter(view);
+    }
+
     private int getViewCenterSecondary(View view) {
         return (mOrientation == HORIZONTAL) ? getViewCenterY(view) : getViewCenterX(view);
     }
@@ -1102,6 +1140,8 @@
         }
         mRecycler = recycler;
         mState = state;
+        mPositionDeltaInPreLayout = 0;
+        mExtraLayoutSpaceInPreLayout = 0;
     }
 
     /**
@@ -1110,6 +1150,8 @@
     private void leaveContext() {
         mRecycler = null;
         mState = null;
+        mPositionDeltaInPreLayout = 0;
+        mExtraLayoutSpaceInPreLayout = 0;
     }
 
     /**
@@ -1119,9 +1161,6 @@
      * @return true if can fastRelayout()
      */
     private boolean layoutInit() {
-        boolean focusViewWasInTree = mGrid != null && mFocusPosition >= 0
-                && mFocusPosition >= mGrid.getFirstVisibleIndex()
-                && mFocusPosition <= mGrid.getLastVisibleIndex();
         final int newItemCount = mState.getItemCount();
         if (newItemCount == 0) {
             mFocusPosition = NO_POSITION;
@@ -1137,15 +1176,11 @@
         if (!mState.didStructureChange() && mGrid != null && mGrid.getFirstVisibleIndex() >= 0
                 && !mForceFullLayout && mGrid.getNumRows() == mNumRows) {
             updateScrollController();
-            updateScrollSecondAxis();
+            updateSecondaryScrollLimits();
             mGrid.setSpacing(mSpacingPrimary);
-            if (!focusViewWasInTree && mFocusPosition != NO_POSITION) {
-                mGrid.setStart(mFocusPosition);
-            }
             return true;
         } else {
             mForceFullLayout = false;
-            int firstVisibleIndex = focusViewWasInTree ? mGrid.getFirstVisibleIndex() : 0;
 
             if (mGrid == null || mNumRows != mGrid.getNumRows()
                     || mReverseFlowPrimary != mGrid.isReversedFlow()) {
@@ -1154,20 +1189,12 @@
                 mGrid.setReversedFlow(mReverseFlowPrimary);
             }
             initScrollController();
-            updateScrollSecondAxis();
+            updateSecondaryScrollLimits();
             mGrid.setSpacing(mSpacingPrimary);
             detachAndScrapAttachedViews(mRecycler);
             mGrid.resetVisibleIndex();
             mWindowAlignment.mainAxis().invalidateScrollMin();
             mWindowAlignment.mainAxis().invalidateScrollMax();
-            if (focusViewWasInTree && firstVisibleIndex <= mFocusPosition) {
-                // if focusView was in tree, we will add item from first visible item
-                mGrid.setStart(firstVisibleIndex);
-            } else {
-                // if focusView was not in tree, it's probably because focus position jumped
-                // far away from visible range,  so use mFocusPosition as start
-                mGrid.setStart(mFocusPosition);
-            }
             return false;
         }
     }
@@ -1242,8 +1269,7 @@
         if (TRACE) TraceCompat.beginSection("processRowSizeSecondary");
         CircularIntArray[] rows = mGrid == null ? null : mGrid.getItemPositionsInRows();
         boolean changed = false;
-        int scrapChildWidth = -1;
-        int scrapChildHeight = -1;
+        int scrapeChildSize = -1;
 
         for (int rowIndex = 0; rowIndex < mNumRows; rowIndex++) {
             CircularIntArray row = rows == null ? null : rows[rowIndex];
@@ -1254,7 +1280,7 @@
                 final int rowIndexStart = row.get(rowItemPairIndex);
                 final int rowIndexEnd = row.get(rowItemPairIndex + 1);
                 for (int i = rowIndexStart; i <= rowIndexEnd; i++) {
-                    final View view = findViewByPosition(i);
+                    final View view = findViewByPosition(i - mPositionDeltaInPreLayout);
                     if (view == null) {
                         continue;
                     }
@@ -1272,27 +1298,49 @@
 
             final int itemCount = mState.getItemCount();
             if (!mBaseGridView.hasFixedSize() && measure && rowSize < 0 && itemCount > 0) {
-                if (scrapChildWidth < 0 && scrapChildHeight < 0) {
-                    int position;
-                    if (mFocusPosition == NO_POSITION) {
+                if (scrapeChildSize < 0) {
+                    // measure a child that is close to mFocusPosition but not currently visible
+                    int position = mFocusPosition;
+                    if (position < 0) {
                         position = 0;
-                    } else if (mFocusPosition >= itemCount) {
+                    } else if (position >= itemCount) {
                         position = itemCount - 1;
-                    } else {
-                        position = mFocusPosition;
                     }
-                    measureScrapChild(position,
-                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                            mMeasuredDimension);
-                    scrapChildWidth = mMeasuredDimension[0];
-                    scrapChildHeight = mMeasuredDimension[1];
-                    if (DEBUG) {
-                        Log.v(TAG, "measured scrap child: " + scrapChildWidth + " "
-                                + scrapChildHeight);
+                    if (getChildCount() > 0) {
+                        int firstPos = mBaseGridView.getChildViewHolder(
+                                getChildAt(0)).getLayoutPosition();
+                        int lastPos = mBaseGridView.getChildViewHolder(
+                                getChildAt(getChildCount() - 1)).getLayoutPosition();
+                        // if mFocusPosition is between first and last, choose either
+                        // first - 1 or last + 1
+                        if (position >= firstPos && position <= lastPos) {
+                            position = (position - firstPos <= lastPos - position)
+                                    ? (firstPos - 1) : (lastPos + 1);
+                            // try the other value if the position is invalid. if both values are
+                            // invalid, skip measureScrapChild below.
+                            if (position < 0 && lastPos < itemCount - 1) {
+                                position = lastPos + 1;
+                            } else if (position >= itemCount && firstPos > 0) {
+                                position = firstPos - 1;
+                            }
+                        }
+                    }
+                    if (position >= 0 && position < itemCount) {
+                        measureScrapChild(position,
+                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                                mMeasuredDimension);
+                        scrapeChildSize = mOrientation == HORIZONTAL ? mMeasuredDimension[0] :
+                                mMeasuredDimension[1];
+                        if (DEBUG) {
+                            Log.v(TAG, "measured scrap child: " + mMeasuredDimension[0] + " "
+                                    + mMeasuredDimension[1]);
+                        }
                     }
                 }
-                rowSize = mOrientation == HORIZONTAL ? scrapChildHeight : scrapChildWidth;
+                if (scrapeChildSize >= 0) {
+                    rowSize = scrapeChildSize;
+                }
             }
             if (rowSize < 0) {
                 rowSize = 0;
@@ -1377,7 +1425,10 @@
                 mRowSizeSecondary = new int[mNumRows];
             }
 
-            // Measure all current children and update cached row heights
+            if (mState.isPreLayout()) {
+                updatePositionDeltaInPreLayout();
+            }
+            // Measure all current children and update cached row height or column width
             processRowSizeSecondary(true);
 
             switch (modeSecondary) {
@@ -1502,15 +1553,20 @@
     private Grid.Provider mGridProvider = new Grid.Provider() {
 
         @Override
-        public int getCount() {
-            return mState.getItemCount();
+        public int getMinIndex() {
+            return mPositionDeltaInPreLayout;
         }
 
         @Override
-        public int createItem(int index, boolean append, Object[] item) {
+        public int getCount() {
+            return mState.getItemCount() + mPositionDeltaInPreLayout;
+        }
+
+        @Override
+        public int createItem(int index, boolean append, Object[] item, boolean disappearingItem) {
             if (TRACE) TraceCompat.beginSection("createItem");
             if (TRACE) TraceCompat.beginSection("getview");
-            View v = getViewForPosition(index);
+            View v = getViewForPosition(index - mPositionDeltaInPreLayout);
             if (TRACE) TraceCompat.endSection();
             LayoutParams lp = (LayoutParams) v.getLayoutParams();
             RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(v);
@@ -1518,10 +1574,18 @@
             // See recyclerView docs:  we don't need re-add scraped view if it was removed.
             if (!lp.isItemRemoved()) {
                 if (TRACE) TraceCompat.beginSection("addView");
-                if (append) {
-                    addView(v);
+                if (disappearingItem) {
+                    if (append) {
+                        addDisappearingView(v);
+                    } else {
+                        addDisappearingView(v, 0);
+                    }
                 } else {
-                    addView(v, 0);
+                    if (append) {
+                        addView(v);
+                    } else {
+                        addView(v, 0);
+                    }
                 }
                 if (TRACE) TraceCompat.endSection();
                 if (mChildVisibility != -1) {
@@ -1572,9 +1636,9 @@
             View v = (View) item;
             int start, end;
             if (edge == Integer.MIN_VALUE || edge == Integer.MAX_VALUE) {
-                edge = !mGrid.isReversedFlow() ? mWindowAlignment.mainAxis().getPaddingLow()
+                edge = !mGrid.isReversedFlow() ? mWindowAlignment.mainAxis().getPaddingMin()
                         : mWindowAlignment.mainAxis().getSize()
-                                - mWindowAlignment.mainAxis().getPaddingHigh();
+                                - mWindowAlignment.mainAxis().getPaddingMax();
             }
             boolean edgeIsMin = !mGrid.isReversedFlow();
             if (edgeIsMin) {
@@ -1584,7 +1648,8 @@
                 start = edge - length;
                 end = edge;
             }
-            int startSecondary = getRowStartSecondary(rowIndex) - mScrollOffsetSecondary;
+            int startSecondary = getRowStartSecondary(rowIndex)
+                    + mWindowAlignment.secondAxis().getPaddingMin() - mScrollOffsetSecondary;
             mChildrenStates.loadView(v, index);
             layoutChild(rowIndex, v, start, end, startSecondary);
             if (DEBUG) {
@@ -1592,19 +1657,8 @@
             }
             if (TRACE) TraceCompat.endSection();
 
-            if (index == mGrid.getFirstVisibleIndex()) {
-                if (!mGrid.isReversedFlow()) {
-                    updateScrollMin();
-                } else {
-                    updateScrollMax();
-                }
-            }
-            if (index == mGrid.getLastVisibleIndex()) {
-                if (!mGrid.isReversedFlow()) {
-                    updateScrollMax();
-                } else {
-                    updateScrollMin();
-                }
+            if (!mState.isPreLayout()) {
+                updateScrollLimits();
             }
             if (!mInLayout && mPendingMoveSmoothScroller != null) {
                 mPendingMoveSmoothScroller.consumePendingMovesAfterLayout();
@@ -1619,7 +1673,7 @@
         @Override
         public void removeItem(int index) {
             if (TRACE) TraceCompat.beginSection("removeItem");
-            View v = findViewByPosition(index);
+            View v = findViewByPosition(index - mPositionDeltaInPreLayout);
             if (mInLayout) {
                 detachAndScrapView(v, mRecycler);
             } else {
@@ -1630,16 +1684,13 @@
 
         @Override
         public int getEdge(int index) {
-            if (mReverseFlowPrimary) {
-                return getViewMax(findViewByPosition(index));
-            } else {
-                return getViewMin(findViewByPosition(index));
-            }
+            View v = findViewByPosition(index - mPositionDeltaInPreLayout);
+            return mReverseFlowPrimary ? getViewMax(v) : getViewMin(v);
         }
 
         @Override
         public int getSize(int index) {
-            return getViewPrimarySize(findViewByPosition(index));
+            return getViewPrimarySize(findViewByPosition(index - mPositionDeltaInPreLayout));
         }
     };
 
@@ -1832,13 +1883,15 @@
     }
 
     private void appendVisibleItems() {
-        mGrid.appendVisibleItems(mReverseFlowPrimary ? -mExtraLayoutSpace
-                : mSizePrimary + mExtraLayoutSpace);
+        mGrid.appendVisibleItems(mReverseFlowPrimary
+                ? -mExtraLayoutSpace - mExtraLayoutSpaceInPreLayout
+                : mSizePrimary + mExtraLayoutSpace + mExtraLayoutSpaceInPreLayout);
     }
 
     private void prependVisibleItems() {
-        mGrid.prependVisibleItems(mReverseFlowPrimary ? mSizePrimary + mExtraLayoutSpace
-                : -mExtraLayoutSpace);
+        mGrid.prependVisibleItems(mReverseFlowPrimary
+                ? mSizePrimary + mExtraLayoutSpace + mExtraLayoutSpaceInPreLayout
+                : -mExtraLayoutSpace - mExtraLayoutSpaceInPreLayout);
     }
 
     /**
@@ -1849,28 +1902,36 @@
     private void fastRelayout() {
         boolean invalidateAfter = false;
         final int childCount = getChildCount();
-        int position = -1;
-        for (int index = 0; index < childCount; index++) {
+        int position = mGrid.getFirstVisibleIndex();
+        int index = 0;
+        for (; index < childCount; index++, position++) {
             View view = getChildAt(index);
-            position = getPositionByIndex(index);
+            // We don't hit fastRelayout() if State.didStructure() is true, but prelayout may add
+            // extra views and invalidate existing Grid position. Also the prelayout calling
+            // getViewForPosotion() may retrieve item from cache with FLAG_INVALID. The adapter
+            // postion will be -1 for this case. Either case, we should invalidate after this item
+            // and call getViewForPosition() again to rebind.
+            if (position != getAdapterPositionByView(view)) {
+                invalidateAfter = true;
+                break;
+            }
             Grid.Location location = mGrid.getLocation(position);
             if (location == null) {
-                if (DEBUG) Log.w(getTag(), "fastRelayout(): no Location at " + position);
                 invalidateAfter = true;
                 break;
             }
 
-            int startSecondary = getRowStartSecondary(location.row) - mScrollOffsetSecondary;
+            int startSecondary = getRowStartSecondary(location.row)
+                    + mWindowAlignment.secondAxis().getPaddingMin() - mScrollOffsetSecondary;
             int primarySize, end;
             int start = getViewMin(view);
             int oldPrimarySize = getViewPrimarySize(view);
 
             LayoutParams lp = (LayoutParams) view.getLayoutParams();
             if (lp.viewNeedsUpdate()) {
-                int viewIndex = mBaseGridView.indexOfChild(view);
                 detachAndScrapView(view, mRecycler);
                 view = getViewForPosition(position);
-                addView(view, viewIndex);
+                addView(view, index);
             }
 
             measureChild(view);
@@ -1891,6 +1952,10 @@
         }
         if (invalidateAfter) {
             final int savedLastPos = mGrid.getLastVisibleIndex();
+            for (int i = childCount - 1; i >= index; i--) {
+                View v = getChildAt(i);
+                detachAndScrapView(v, mRecycler);
+            }
             mGrid.invalidateItemsAfter(position);
             if (mPruneChild) {
                 // in regular prune child mode, we just append items up to edge limit
@@ -1908,9 +1973,8 @@
                         && mGrid.getLastVisibleIndex() < savedLastPos);
             }
         }
-        updateScrollMin();
-        updateScrollMax();
-        updateScrollSecondAxis();
+        updateScrollLimits();
+        updateSecondaryScrollLimits();
     }
 
     @Override
@@ -1925,7 +1989,7 @@
 
     // called by onLayoutChildren, either focus to FocusPosition or declare focusViewAvailable
     // and scroll to the view if framework focus on it.
-    private void scrollToFocusViewInLayout(boolean hadFocus, boolean alignToView) {
+    private void focusToViewInLayout(boolean hadFocus, boolean alignToView) {
         View focusView = findViewByPosition(mFocusPosition);
         if (focusView != null && alignToView) {
             scrollToView(focusView, false);
@@ -1943,10 +2007,10 @@
                         break;
                     }
                 }
-                // focusViewAvailable() might focus to the view, scroll to it if that is the case.
-                if (alignToView && focusView != null && focusView.hasFocus()) {
-                    scrollToView(focusView, false);
-                }
+            }
+            // focusViewAvailable() might focus to the view, scroll to it if that is the case.
+            if (alignToView && focusView != null && focusView.hasFocus()) {
+                scrollToView(focusView, false);
             }
         }
     }
@@ -1967,12 +2031,77 @@
         }
     }
 
+    @Override
+    public boolean supportsPredictiveItemAnimations() {
+        return true;
+    }
+
+    void updatePositionToRowMapInPostLayout() {
+        mPositionToRowInPostLayout.clear();
+        final int childCount = getChildCount();
+        for (int i = 0;  i < childCount; i++) {
+            // Grid still maps to old positions at this point, use old position to get row infor
+            int position = mBaseGridView.getChildViewHolder(getChildAt(i)).getOldPosition();
+            if (position >= 0) {
+                Grid.Location loc = mGrid.getLocation(position);
+                if (loc != null) {
+                    mPositionToRowInPostLayout.put(position, loc.row);
+                }
+            }
+        }
+    }
+
+    void fillScrapViewsInPostLayout() {
+        List<RecyclerView.ViewHolder> scrapList = mRecycler.getScrapList();
+        final int scrapSize = scrapList.size();
+        if (scrapSize == 0) {
+            return;
+        }
+        // initialize the int array or re-allocate the array.
+        if (mDisappearingPositions == null  || scrapSize > mDisappearingPositions.length) {
+            int length = mDisappearingPositions == null ? 16 : mDisappearingPositions.length;
+            while (length < scrapSize) {
+                length = length << 1;
+            }
+            mDisappearingPositions = new int[length];
+        }
+        int totalItems = 0;
+        for (int i = 0; i < scrapSize; i++) {
+            int pos = scrapList.get(i).getAdapterPosition();
+            if (pos >= 0) {
+                mDisappearingPositions[totalItems++] = pos;
+            }
+        }
+        // totalItems now has the length of disappearing items
+        if (totalItems > 0) {
+            Arrays.sort(mDisappearingPositions, 0, totalItems);
+            mGrid.fillDisappearingItems(mDisappearingPositions, totalItems,
+                    mPositionToRowInPostLayout);
+        }
+        mPositionToRowInPostLayout.clear();
+    }
+
+    // in prelayout, first child's getViewPosition can be smaller than old adapter position
+    // if there were items removed before first visible index. For example:
+    // visible items are 3, 4, 5, 6, deleting 1, 2, 3 from adapter; the view position in
+    // prelayout are not 3(deleted), 4, 5, 6. Instead it's 1(deleted), 2, 3, 4.
+    // So there is a delta (2 in this case) between last cached position and prelayout position.
+    void updatePositionDeltaInPreLayout() {
+        if (getChildCount() > 0) {
+            View view = getChildAt(0);
+            LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            mPositionDeltaInPreLayout = mGrid.getFirstVisibleIndex()
+                    - lp.getViewLayoutPosition();
+        } else {
+            mPositionDeltaInPreLayout = 0;
+        }
+    }
+
     // Lays out items based on the current scroll position
     @Override
     public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
         if (DEBUG) {
-            Log.v(getTag(), "layoutChildren start numRows " + mNumRows + " mScrollOffsetSecondary "
-                    + mScrollOffsetSecondary + " mScrollOffsetPrimary " + mScrollOffsetPrimary
+            Log.v(getTag(), "layoutChildren start numRows " + mNumRows
                     + " inPreLayout " + state.isPreLayout()
                     + " didStructureChange " + state.didStructureChange()
                     + " mForceFullLayout " + mForceFullLayout);
@@ -2003,14 +2132,53 @@
         }
         mInLayout = true;
 
-        if (state.didStructureChange()) {
-            // didStructureChange() == true means attached item has been removed/added.
-            // scroll animation: we are unable to continue a scroll animation,
-            //    kill the scroll animation,  and let ItemAnimation move the item to new position.
-            // position smooth scroller: kill the animation and stop at final position.
-            // pending smooth scroller: stop and scroll to current focus position.
-            mBaseGridView.stopScroll();
+        saveContext(recycler, state);
+        if (state.isPreLayout()) {
+            updatePositionDeltaInPreLayout();
+            int childCount = getChildCount();
+            if (mGrid != null && childCount > 0) {
+                int minChangedEdge = Integer.MAX_VALUE;
+                int maxChangeEdge = Integer.MIN_VALUE;
+                int minOldAdapterPosition = mBaseGridView.getChildViewHolder(
+                        getChildAt(0)).getOldPosition();
+                int maxOldAdapterPosition = mBaseGridView.getChildViewHolder(
+                        getChildAt(childCount - 1)).getOldPosition();
+                for (int i = 0; i < childCount; i++) {
+                    View view = getChildAt(i);
+                    LayoutParams lp = (LayoutParams) view.getLayoutParams();
+                    int newAdapterPosition = mBaseGridView.getChildAdapterPosition(view);
+                    // if either of following happening
+                    // 1. item itself has changed or layout parameter changed
+                    // 2. item is losing focus
+                    // 3. item is gaining focus
+                    // 4. item is moved out of old adapter position range.
+                    if (lp.isItemChanged() || lp.isItemRemoved() || view.isLayoutRequested()
+                            || (!view.hasFocus() && mFocusPosition == lp.getViewAdapterPosition())
+                            || (view.hasFocus() && mFocusPosition != lp.getViewAdapterPosition())
+                            || newAdapterPosition < minOldAdapterPosition
+                            || newAdapterPosition > maxOldAdapterPosition) {
+                        minChangedEdge = Math.min(minChangedEdge, getViewMin(view));
+                        maxChangeEdge = Math.max(maxChangeEdge, getViewMax(view));
+                    }
+                }
+                if (maxChangeEdge > minChangedEdge) {
+                    mExtraLayoutSpaceInPreLayout = maxChangeEdge - minChangedEdge;
+                }
+                // append items for mExtraLayoutSpaceInPreLayout
+                appendVisibleItems();
+                prependVisibleItems();
+            }
+            mInLayout = false;
+            leaveContext();
+            if (DEBUG) Log.v(getTag(), "layoutChildren end");
+            return;
         }
+
+        // save all view's row information before detach all views
+        if (state.willRunPredictiveAnimations()) {
+            updatePositionToRowMapInPostLayout();
+        }
+        // check if we need align to mFocusPosition, this is usually true unless in smoothScrolling
         final boolean scrollToFocus = !isSmoothScrolling()
                 && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED;
         if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE) {
@@ -2018,37 +2186,38 @@
             mSubFocusPosition = 0;
         }
         mFocusPositionOffset = 0;
-        saveContext(recycler, state);
 
         View savedFocusView = findViewByPosition(mFocusPosition);
         int savedFocusPos = mFocusPosition;
         int savedSubFocusPos = mSubFocusPosition;
         boolean hadFocus = mBaseGridView.hasFocus();
-
-        // Track the old focus view so we can adjust our system scroll position
-        // so that any scroll animations happening now will remain valid.
-        // We must use same delta in Pre Layout (if prelayout exists) and second layout.
-        // So we cache the deltas in PreLayout and use it in second layout.
-        int delta = 0, deltaSecondary = 0;
-        if (mFocusPosition != NO_POSITION && scrollToFocus
-                && mBaseGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            // FIXME: we should get the remaining scroll animation offset from RecyclerView
-            if (savedFocusView != null) {
-                if (getScrollPosition(savedFocusView, savedFocusView.findFocus(), sTwoInts)) {
-                    delta = sTwoInts[0];
-                    deltaSecondary = sTwoInts[1];
-                }
-            }
-        }
+        final int firstVisibleIndex = mGrid != null ? mGrid.getFirstVisibleIndex() : NO_POSITION;
+        final int lastVisibleIndex = mGrid != null ? mGrid.getLastVisibleIndex() : NO_POSITION;
+        int savedViewCenter = savedFocusView == null ? 0 : getAdjustedViewCenter(savedFocusView);
+        int savedViewCenterSecondary = savedFocusView == null ? 0 :
+                getViewCenterSecondary(savedFocusView);
 
         if (mInFastRelayout = layoutInit()) {
+            // If grid view is empty, we will start from mFocusPosition
+            mGrid.setStart(mFocusPosition);
             fastRelayout();
         } else {
+            // layoutInit() has detached all views, so start from scratch
             mInLayoutSearchFocus = hadFocus;
-            if (mFocusPosition != NO_POSITION) {
-                // appends items till focus position.
-                while (appendOneColumnVisibleItems()
-                        && findViewByPosition(mFocusPosition) == null) ;
+            int startFromPosition, endPos;
+            if (scrollToFocus && (firstVisibleIndex < 0 || mFocusPosition > lastVisibleIndex
+                    || mFocusPosition < firstVisibleIndex)) {
+                startFromPosition = endPos = mFocusPosition;
+            } else {
+                startFromPosition = firstVisibleIndex;
+                endPos = lastVisibleIndex;
+            }
+
+            mGrid.setStart(startFromPosition);
+            if (endPos != NO_POSITION) {
+                while (appendOneColumnVisibleItems() && findViewByPosition(endPos) == null) {
+                    // continuously append items until endPos
+                }
             }
         }
         // multiple rounds: scrollToView of first round may drag first/last child into
@@ -2057,11 +2226,20 @@
         int oldFirstVisible;
         int oldLastVisible;
         do {
-            updateScrollMin();
-            updateScrollMax();
+            updateScrollLimits();
             oldFirstVisible = mGrid.getFirstVisibleIndex();
             oldLastVisible = mGrid.getLastVisibleIndex();
-            scrollToFocusViewInLayout(hadFocus, scrollToFocus);
+            if (scrollToFocus && savedFocusView != null) {
+                // Make previous focused view stay at the original place:
+                focusToViewInLayout(hadFocus, false);
+                int newViewCenter = getAdjustedViewCenter(savedFocusView);
+                int newViewCenterSecondary = getViewCenterSecondary(savedFocusView);
+                scrollDirectionPrimary(newViewCenter - savedViewCenter);
+                scrollDirectionSecondary(newViewCenterSecondary - savedViewCenterSecondary);
+            } else {
+                // focus and scroll to the view
+                focusToViewInLayout(hadFocus, scrollToFocus);
+            }
             appendVisibleItems();
             prependVisibleItems();
             removeInvisibleViewsAtFront();
@@ -2070,13 +2248,62 @@
                 || mGrid.getLastVisibleIndex() != oldLastVisible);
 
         if (scrollToFocus) {
-            scrollDirectionPrimary(-delta);
-            scrollDirectionSecondary(-deltaSecondary);
+            // we need scroll to the new focus view
+            View newFocusView = findViewByPosition(mFocusPosition); // must not be null
+            View newChildFocusView = newFocusView != null && newFocusView.hasFocus()
+                    ? newFocusView.findFocus() : null;
+            if (newFocusView != null) {
+                // get scroll delta of primary / secondary to the new focus view
+                // Note that we need to multiple rounds to updateScrollLimits()
+                int newFocusViewCenter = getAdjustedViewCenter(newFocusView);
+                int newFocusViewCenterSecondary = getViewCenterSecondary(newFocusView);
+                do {
+                    updateScrollLimits();
+                    oldFirstVisible = mGrid.getFirstVisibleIndex();
+                    oldLastVisible = mGrid.getLastVisibleIndex();
+                    scrollToView(newFocusView, newChildFocusView, false);
+                    appendVisibleItems();
+                    prependVisibleItems();
+                    removeInvisibleViewsAtFront();
+                    removeInvisibleViewsAtEnd();
+                } while (mGrid.getFirstVisibleIndex() != oldFirstVisible
+                        || mGrid.getLastVisibleIndex() != oldLastVisible);
+                int primary = newFocusViewCenter - getAdjustedViewCenter(newFocusView);
+                int secondary = newFocusViewCenterSecondary - getViewCenterSecondary(newFocusView);
+                final int scrollX, scrollY;
+                if (mOrientation == HORIZONTAL) {
+                    scrollX = primary;
+                    scrollY = secondary;
+                } else {
+                    scrollY = primary;
+                    scrollX = secondary;
+                }
+                final int remainingScrollX = state.getRemainingScrollHorizontal();
+                final int remainingScrollY = state.getRemainingScrollVertical();
+                // check if the remaining scroll will stop at the new focus view
+                if (remainingScrollX != scrollX || remainingScrollY != scrollY) {
+                    if (remainingScrollX == 0 && remainingScrollY == 0) {
+                        // if there wasnt scroll animation, we dont start animation, let
+                        // ItemAnimation to do the move animation.
+                    } else {
+                        // if there was scroll animation, we will start a new scroll animation.
+                        // after move back to current position
+                        scrollAndAppendPrepend(-primary, -secondary);
+                        if (scrollX != 0 || scrollY != 0) {
+                            mBaseGridView.smoothScrollBy(scrollX, scrollY);
+                        } else {
+                            mBaseGridView.stopScroll();
+                        }
+                    }
+                } else {
+                    // move back to current position and let scroll animation continue
+                    scrollAndAppendPrepend(-primary, -secondary);
+                }
+            }
         }
-        appendVisibleItems();
-        prependVisibleItems();
-        removeInvisibleViewsAtFront();
-        removeInvisibleViewsAtEnd();
+        if (state.willRunPredictiveAnimations()) {
+            fillScrapViewsInPostLayout();
+        }
 
         if (DEBUG) {
             StringWriter sw = new StringWriter();
@@ -2101,15 +2328,24 @@
             dispatchChildSelected();
         }
         dispatchChildSelectedAndPositioned();
-
         if (mIsSlidingChildViews) {
             scrollDirectionPrimary(getSlideOutDistance());
         }
+
         mInLayout = false;
         leaveContext();
         if (DEBUG) Log.v(getTag(), "layoutChildren end");
     }
 
+    void scrollAndAppendPrepend(int primary, int secondary) {
+        scrollDirectionPrimary(primary);
+        scrollDirectionSecondary(secondary);
+        appendVisibleItems();
+        prependVisibleItems();
+        removeInvisibleViewsAtFront();
+        removeInvisibleViewsAtEnd();
+    }
+
     private void offsetChildrenSecondary(int increment) {
         final int childCount = getChildCount();
         if (mOrientation == HORIZONTAL) {
@@ -2184,16 +2420,16 @@
                 isMaxUnknown = mWindowAlignment.mainAxis().isMaxUnknown();
                 if (!isMaxUnknown) {
                     maxScroll = mWindowAlignment.mainAxis().getMaxScroll();
-                    if (mScrollOffsetPrimary + da > maxScroll) {
-                        da = maxScroll - mScrollOffsetPrimary;
+                    if (da > maxScroll) {
+                        da = maxScroll;
                     }
                 }
             } else if (da < 0) {
                 isMinUnknown = mWindowAlignment.mainAxis().isMinUnknown();
                 if (!isMinUnknown) {
                     minScroll = mWindowAlignment.mainAxis().getMinScroll();
-                    if (mScrollOffsetPrimary + da < minScroll) {
-                        da = minScroll - mScrollOffsetPrimary;
+                    if (da < minScroll) {
+                        da = minScroll;
                     }
                 }
             }
@@ -2203,8 +2439,8 @@
             return 0;
         }
         offsetChildrenPrimary(-da);
-        mScrollOffsetPrimary += da;
         if (mInLayout) {
+            updateScrollLimits();
             if (TRACE) TraceCompat.endSection();
             return da;
         }
@@ -2233,6 +2469,7 @@
         }
 
         mBaseGridView.invalidate();
+        updateScrollLimits();
         if (TRACE) TraceCompat.endSection();
         return da;
     }
@@ -2244,6 +2481,7 @@
         }
         offsetChildrenSecondary(-dy);
         mScrollOffsetSecondary += dy;
+        updateSecondaryScrollLimits();
         mBaseGridView.invalidate();
         return dy;
     }
@@ -2282,81 +2520,67 @@
         }
     }
 
-    void updateScrollMax() {
-        int highVisiblePos = (!mReverseFlowPrimary) ? mGrid.getLastVisibleIndex()
-                : mGrid.getFirstVisibleIndex();
-        int highMaxPos = (!mReverseFlowPrimary) ? mState.getItemCount() - 1 : 0;
-        if (highVisiblePos < 0) {
+    void updateScrollLimits() {
+        if (mState.getItemCount() == 0) {
+            return;
+        }
+        int highVisiblePos, lowVisiblePos;
+        int highMaxPos, lowMinPos;
+        if (!mReverseFlowPrimary) {
+            highVisiblePos = mGrid.getLastVisibleIndex();
+            highMaxPos = mState.getItemCount() - 1;
+            lowVisiblePos = mGrid.getFirstVisibleIndex();
+            lowMinPos = 0;
+        } else {
+            highVisiblePos = mGrid.getFirstVisibleIndex();
+            highMaxPos = 0;
+            lowVisiblePos = mGrid.getLastVisibleIndex();
+            lowMinPos = mState.getItemCount() - 1;
+        }
+        if (highVisiblePos < 0 || lowVisiblePos < 0) {
             return;
         }
         final boolean highAvailable = highVisiblePos == highMaxPos;
-        final boolean maxUnknown = mWindowAlignment.mainAxis().isMaxUnknown();
-        if (!highAvailable && maxUnknown) {
-            return;
-        }
-        int maxEdge = mGrid.findRowMax(true, sTwoInts) + mScrollOffsetPrimary;
-        int rowIndex = sTwoInts[0];
-        int pos = sTwoInts[1];
-        int savedMaxEdge = mWindowAlignment.mainAxis().getMaxEdge();
-        mWindowAlignment.mainAxis().setMaxEdge(maxEdge);
-        int maxScroll = getPrimarySystemScrollPositionOfChildMax(findViewByPosition(pos));
-        mWindowAlignment.mainAxis().setMaxEdge(savedMaxEdge);
-
-        if (highAvailable) {
-            mWindowAlignment.mainAxis().setMaxEdge(maxEdge);
-            mWindowAlignment.mainAxis().setMaxScroll(maxScroll);
-            if (DEBUG) {
-                Log.v(getTag(), "updating scroll maxEdge to " + maxEdge
-                        + " scrollMax to " + maxScroll);
-            }
-        } else {
-            mWindowAlignment.mainAxis().invalidateScrollMax();
-            if (DEBUG) {
-                Log.v(getTag(), "Invalidate scrollMax since it should be "
-                        + "greater than " + maxScroll);
-            }
-        }
-    }
-
-    void updateScrollMin() {
-        int lowVisiblePos = (!mReverseFlowPrimary) ? mGrid.getFirstVisibleIndex()
-                : mGrid.getLastVisibleIndex();
-        int lowMinPos = (!mReverseFlowPrimary) ? 0 : mState.getItemCount() - 1;
-        if (lowVisiblePos < 0) {
-            return;
-        }
         final boolean lowAvailable = lowVisiblePos == lowMinPos;
-        final boolean minUnknown = mWindowAlignment.mainAxis().isMinUnknown();
-        if (!lowAvailable && minUnknown) {
+        if (!highAvailable && mWindowAlignment.mainAxis().isMaxUnknown()
+                && !lowAvailable && mWindowAlignment.mainAxis().isMinUnknown()) {
             return;
         }
-        int minEdge = mGrid.findRowMin(false, sTwoInts) + mScrollOffsetPrimary;
-        int rowIndex = sTwoInts[0];
-        int pos = sTwoInts[1];
-        int savedMinEdge = mWindowAlignment.mainAxis().getMinEdge();
-        mWindowAlignment.mainAxis().setMinEdge(minEdge);
-        int minScroll = getPrimarySystemScrollPosition(findViewByPosition(pos));
-        mWindowAlignment.mainAxis().setMinEdge(savedMinEdge);
-
-        if (lowAvailable) {
-            mWindowAlignment.mainAxis().setMinEdge(minEdge);
-            mWindowAlignment.mainAxis().setMinScroll(minScroll);
-            if (DEBUG) {
-                Log.v(getTag(), "updating scroll minEdge to " + minEdge
-                        + " scrollMin to " + minScroll);
+        int maxEdge, maxViewCenter;
+        if (highAvailable) {
+            maxEdge = mGrid.findRowMax(true, sTwoInts);
+            View maxChild = findViewByPosition(sTwoInts[1]);
+            maxViewCenter = getViewCenter(maxChild);
+            final LayoutParams lp = (LayoutParams) maxChild.getLayoutParams();
+            int[] multipleAligns = lp.getAlignMultiple();
+            if (multipleAligns != null && multipleAligns.length > 0) {
+                maxViewCenter += multipleAligns[multipleAligns.length - 1] - multipleAligns[0];
             }
         } else {
-            mWindowAlignment.mainAxis().invalidateScrollMin();
-            if (DEBUG) {
-                Log.v(getTag(), "Invalidate scrollMin, since it should be "
-                        + "less than " + minScroll);
-            }
+            maxEdge = Integer.MAX_VALUE;
+            maxViewCenter = Integer.MAX_VALUE;
         }
+        int minEdge, minViewCenter;
+        if (lowAvailable) {
+            minEdge = mGrid.findRowMin(false, sTwoInts);
+            View minChild = findViewByPosition(sTwoInts[1]);
+            minViewCenter = getViewCenter(minChild);
+        } else {
+            minEdge = Integer.MIN_VALUE;
+            minViewCenter = Integer.MIN_VALUE;
+        }
+        mWindowAlignment.mainAxis().updateMinMax(minEdge, maxEdge, minViewCenter, maxViewCenter);
     }
 
-    private void updateScrollSecondAxis() {
-        mWindowAlignment.secondAxis().setMinEdge(0);
-        mWindowAlignment.secondAxis().setMaxEdge(getSizeSecondary());
+    /**
+     * Update secondary axis's scroll min/max, should be updated in
+     * {@link #scrollDirectionSecondary(int)}.
+     */
+    private void updateSecondaryScrollLimits() {
+        WindowAlignment.Axis secondAxis = mWindowAlignment.secondAxis();
+        int minEdge = secondAxis.getPaddingMin() - mScrollOffsetSecondary;
+        int maxEdge = minEdge + getSizeSecondary();
+        secondAxis.updateMinMax(minEdge, maxEdge, minEdge, maxEdge);
     }
 
     private void initScrollController() {
@@ -2366,33 +2590,15 @@
         mWindowAlignment.horizontal.setPadding(getPaddingLeft(), getPaddingRight());
         mWindowAlignment.vertical.setPadding(getPaddingTop(), getPaddingBottom());
         mSizePrimary = mWindowAlignment.mainAxis().getSize();
-        mScrollOffsetPrimary = -mWindowAlignment.mainAxis().getPaddingLow();
-        mScrollOffsetSecondary = -mWindowAlignment.secondAxis().getPaddingLow();
+        mScrollOffsetSecondary = 0;
 
         if (DEBUG) {
             Log.v(getTag(), "initScrollController mSizePrimary " + mSizePrimary
-                    + " mWindowAlignment " + mWindowAlignment
-                    + " mScrollOffsetPrimary " + mScrollOffsetPrimary);
+                    + " mWindowAlignment " + mWindowAlignment);
         }
     }
 
     private void updateScrollController() {
-        // mScrollOffsetPrimary and mScrollOffsetSecondary includes the padding.
-        // e.g. when topPadding is 16 for horizontal grid view,  the initial
-        // mScrollOffsetSecondary is -16.  fastRelayout() put views based on offsets(not padding),
-        // when padding changes to 20,  we also need update mScrollOffsetSecondary to -20 before
-        // fastRelayout() is performed
-        int paddingPrimaryDiff, paddingSecondaryDiff;
-        if (mOrientation == HORIZONTAL) {
-            paddingPrimaryDiff = getPaddingLeft() - mWindowAlignment.horizontal.getPaddingLow();
-            paddingSecondaryDiff = getPaddingTop() - mWindowAlignment.vertical.getPaddingLow();
-        } else {
-            paddingPrimaryDiff = getPaddingTop() - mWindowAlignment.vertical.getPaddingLow();
-            paddingSecondaryDiff = getPaddingLeft() - mWindowAlignment.horizontal.getPaddingLow();
-        }
-        mScrollOffsetPrimary -= paddingPrimaryDiff;
-        mScrollOffsetSecondary -= paddingSecondaryDiff;
-
         mWindowAlignment.horizontal.setSize(getWidth());
         mWindowAlignment.vertical.setSize(getHeight());
         mWindowAlignment.horizontal.setPadding(getPaddingLeft(), getPaddingRight());
@@ -2401,8 +2607,7 @@
 
         if (DEBUG) {
             Log.v(getTag(), "updateScrollController mSizePrimary " + mSizePrimary
-                    + " mWindowAlignment " + mWindowAlignment
-                    + " mScrollOffsetPrimary " + mScrollOffsetPrimary);
+                    + " mWindowAlignment " + mWindowAlignment);
         }
     }
 
@@ -2566,8 +2771,8 @@
             int pos = mFocusPosition + mFocusPositionOffset;
             if (positionStart <= pos) {
                 if (positionStart + itemCount > pos) {
-                    // stop updating offset after the focus item was removed
-                    mFocusPositionOffset = Integer.MIN_VALUE;
+                    // the focus item was removed
+                    mFocusPositionOffset += positionStart - pos;
                 } else {
                     mFocusPositionOffset -= itemCount;
                 }
@@ -2611,7 +2816,7 @@
         if (mFocusSearchDisabled) {
             return true;
         }
-        if (getPositionByView(child) == NO_POSITION) {
+        if (getAdapterPositionByView(child) == NO_POSITION) {
             // This is could be the last view in DISAPPEARING animation.
             return true;
         }
@@ -2628,69 +2833,30 @@
         return false;
     }
 
-    int getScrollOffsetX() {
-        return mOrientation == HORIZONTAL ? mScrollOffsetPrimary : mScrollOffsetSecondary;
-    }
-
-    int getScrollOffsetY() {
-        return mOrientation == HORIZONTAL ? mScrollOffsetSecondary : mScrollOffsetPrimary;
-    }
-
     public void getViewSelectedOffsets(View view, int[] offsets) {
         if (mOrientation == HORIZONTAL) {
-            offsets[0] = getPrimarySystemScrollPosition(view) - mScrollOffsetPrimary;
-            offsets[1] = getSecondarySystemScrollPosition(view) - mScrollOffsetSecondary;
+            offsets[0] = getPrimaryAlignedScrollDistance(view);
+            offsets[1] = getSecondaryScrollDistance(view);
         } else {
-            offsets[1] = getPrimarySystemScrollPosition(view) - mScrollOffsetPrimary;
-            offsets[0] = getSecondarySystemScrollPosition(view) - mScrollOffsetSecondary;
+            offsets[1] = getPrimaryAlignedScrollDistance(view);
+            offsets[0] = getSecondaryScrollDistance(view);
         }
     }
 
-    private int getPrimarySystemScrollPosition(View view) {
-        final int viewCenterPrimary = mScrollOffsetPrimary + getViewCenter(view);
-        final int viewMin = getViewMin(view);
-        final int viewMax = getViewMax(view);
-        // TODO: change to use State object in onRequestChildFocus()
-        boolean isMin, isMax;
-        if (!mReverseFlowPrimary) {
-            isMin = mGrid.getFirstVisibleIndex() == 0;
-            isMax = mGrid.getLastVisibleIndex() == (mState == null
-                    ? getItemCount() : mState.getItemCount()) - 1;
-        } else {
-            isMax = mGrid.getFirstVisibleIndex() == 0;
-            isMin = mGrid.getLastVisibleIndex() == (mState == null
-                    ? getItemCount() : mState.getItemCount()) - 1;
-        }
-        for (int i = getChildCount() - 1; (isMin || isMax) && i >= 0; i--) {
-            View v = getChildAt(i);
-            if (v == view || v == null) {
-                continue;
-            }
-            if (isMin && getViewMin(v) < viewMin) {
-                isMin = false;
-            }
-            if (isMax && getViewMax(v) > viewMax) {
-                isMax = false;
-            }
-        }
-        return mWindowAlignment.mainAxis().getSystemScrollPos(viewCenterPrimary, isMin, isMax);
-    }
-
-    private int getPrimarySystemScrollPositionOfChildMax(View view) {
-        int scrollPosition = getPrimarySystemScrollPosition(view);
-        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        int[] multipleAligns = lp.getAlignMultiple();
-        if (multipleAligns != null && multipleAligns.length > 0) {
-            scrollPosition += multipleAligns[multipleAligns.length - 1] - multipleAligns[0];
-        }
-        return scrollPosition;
-    }
-
     /**
-     * Get adjusted primary position for a given childView (if there is multiple ItemAlignment defined
-     * on the view).
+     * Return the scroll delta on primary direction to make the view selected. If the return value
+     * is 0, there is no need to scroll.
      */
-    private int getAdjustedPrimaryScrollPosition(int scrollPrimary, View view, View childView) {
+    private int getPrimaryAlignedScrollDistance(View view) {
+        return mWindowAlignment.mainAxis().getScroll(getViewCenter(view));
+    }
+
+    /**
+     * Get adjusted primary position for a given childView (if there is multiple ItemAlignment
+     * defined on the view).
+     */
+    private int getAdjustedPrimaryAlignedScrollDistance(int scrollPrimary, View view,
+            View childView) {
         int subindex = getSubPositionByView(view, childView);
         if (subindex != 0) {
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -2699,20 +2865,9 @@
         return scrollPrimary;
     }
 
-    private int getSecondarySystemScrollPosition(View view) {
-        int viewCenterSecondary = mScrollOffsetSecondary + getViewCenterSecondary(view);
-        int pos = getPositionByView(view);
-        Grid.Location location = mGrid.getLocation(pos);
-        final int row = location.row;
-        final boolean isMin, isMax;
-        if (!mReverseFlowSecondary) {
-            isMin = row == 0;
-            isMax = row == mGrid.getNumRows() - 1;
-        } else {
-            isMax = row == 0;
-            isMin = row == mGrid.getNumRows() - 1;
-        }
-        return mWindowAlignment.secondAxis().getSystemScrollPos(viewCenterSecondary, isMin, isMax);
+    private int getSecondaryScrollDistance(View view) {
+        int viewCenterSecondary = getViewCenterSecondary(view);
+        return mWindowAlignment.secondAxis().getScroll(viewCenterSecondary);
     }
 
     /**
@@ -2729,7 +2884,7 @@
         if (mIsSlidingChildViews) {
             return;
         }
-        int newFocusPosition = getPositionByView(view);
+        int newFocusPosition = getAdapterPositionByView(view);
         int newSubFocusPosition = getSubPositionByView(view, childView);
         if (newFocusPosition != mFocusPosition || newSubFocusPosition != mSubFocusPosition) {
             mFocusPosition = newFocusPosition;
@@ -2770,17 +2925,17 @@
     }
 
     private boolean getNoneAlignedPosition(View view, int[] deltas) {
-        int pos = getPositionByView(view);
+        int pos = getAdapterPositionByView(view);
         int viewMin = getViewMin(view);
         int viewMax = getViewMax(view);
         // we either align "firstView" to left/top padding edge
         // or align "lastView" to right/bottom padding edge
         View firstView = null;
         View lastView = null;
-        int paddingLow = mWindowAlignment.mainAxis().getPaddingLow();
+        int paddingMin = mWindowAlignment.mainAxis().getPaddingMin();
         int clientSize = mWindowAlignment.mainAxis().getClientSize();
         final int row = mGrid.getRowIndex(pos);
-        if (viewMin < paddingLow) {
+        if (viewMin < paddingMin) {
             // view enters low padding area:
             firstView = view;
             if (mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_PAGE) {
@@ -2798,7 +2953,7 @@
                     }
                 }
             }
-        } else if (viewMax > clientSize + paddingLow) {
+        } else if (viewMax > clientSize + paddingMin) {
             // view enters high padding area:
             if (mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_PAGE) {
                 // scroll whole one page right/bottom, align view at the low padding edge.
@@ -2823,9 +2978,9 @@
         int scrollPrimary = 0;
         int scrollSecondary = 0;
         if (firstView != null) {
-            scrollPrimary = getViewMin(firstView) - paddingLow;
+            scrollPrimary = getViewMin(firstView) - paddingMin;
         } else if (lastView != null) {
-            scrollPrimary = getViewMax(lastView) - (paddingLow + clientSize);
+            scrollPrimary = getViewMax(lastView) - (paddingMin + clientSize);
         }
         View secondaryAlignedView;
         if (firstView != null) {
@@ -2835,8 +2990,7 @@
         } else {
             secondaryAlignedView = view;
         }
-        scrollSecondary = getSecondarySystemScrollPosition(secondaryAlignedView);
-        scrollSecondary -= mScrollOffsetSecondary;
+        scrollSecondary = getSecondaryScrollDistance(secondaryAlignedView);
         if (scrollPrimary != 0 || scrollSecondary != 0) {
             deltas[0] = scrollPrimary;
             deltas[1] = scrollSecondary;
@@ -2846,23 +3000,23 @@
     }
 
     private boolean getAlignedPosition(View view, View childView, int[] deltas) {
-        int scrollPrimary = getPrimarySystemScrollPosition(view);
+        int scrollPrimary = getPrimaryAlignedScrollDistance(view);
         if (childView != null) {
-            scrollPrimary = getAdjustedPrimaryScrollPosition(scrollPrimary, view, childView);
+            scrollPrimary = getAdjustedPrimaryAlignedScrollDistance(scrollPrimary, view, childView);
         }
-        int scrollSecondary = getSecondarySystemScrollPosition(view);
+        int scrollSecondary = getSecondaryScrollDistance(view);
         if (DEBUG) {
             Log.v(getTag(), "getAlignedPosition " + scrollPrimary + " " + scrollSecondary
                     + " " + mPrimaryScrollExtra + " " + mWindowAlignment);
-            Log.v(getTag(), "getAlignedPosition " + mScrollOffsetPrimary + " " + mScrollOffsetSecondary);
         }
-        scrollPrimary -= mScrollOffsetPrimary;
-        scrollSecondary -= mScrollOffsetSecondary;
         scrollPrimary += mPrimaryScrollExtra;
         if (scrollPrimary != 0 || scrollSecondary != 0) {
             deltas[0] = scrollPrimary;
             deltas[1] = scrollSecondary;
             return true;
+        } else {
+            deltas[0] = 0;
+            deltas[1] = 0;
         }
         return false;
     }
@@ -3038,7 +3192,7 @@
         }
         final int focusedRow = mGrid.getLocation(pos).row;
         for (int i = getChildCount() - 1; i >= 0; i--) {
-            int position = getPositionByIndex(i);
+            int position = getAdapterPositionByIndex(i);
             Grid.Location loc = mGrid.getLocation(position);
             if (loc != null && loc.row == focusedRow) {
                 if (position < pos) {
@@ -3070,10 +3224,14 @@
             final int movement = getMovement(direction);
             final View focused = recyclerView.findFocus();
             final int focusedIndex = findImmediateChildIndex(focused);
-            final int focusedPos = getPositionByIndex(focusedIndex);
+            final int focusedPos = getAdapterPositionByIndex(focusedIndex);
+            // Even if focusedPos != NO_POSITION, findViewByPosition could return null if the view
+            // is ignored or getLayoutPosition does not match the adapter position of focused view.
+            final View immediateFocusedChild = (focusedPos == NO_POSITION) ? null
+                    : findViewByPosition(focusedPos);
             // Add focusables of focused item.
-            if (focusedPos != NO_POSITION) {
-                findViewByPosition(focusedPos).addFocusables(views,  direction, focusableMode);
+            if (immediateFocusedChild != null) {
+                immediateFocusedChild.addFocusables(views,  direction, focusableMode);
             }
             if (mGrid == null || getChildCount() == 0) {
                 // no grid information, or no child, bail out.
@@ -3084,7 +3242,7 @@
                 return true;
             }
             // Add focusables of neighbor depending on the focus search direction.
-            final int focusedRow = mGrid != null && focusedPos != NO_POSITION
+            final int focusedRow = mGrid != null && immediateFocusedChild != null
                     ? mGrid.getLocation(focusedPos).row : NO_POSITION;
             final int focusableCount = views.size();
             int inc = movement == NEXT_ITEM || movement == NEXT_ROW ? 1 : -1;
@@ -3100,16 +3258,16 @@
                 if (child.getVisibility() != View.VISIBLE || !child.hasFocusable()) {
                     continue;
                 }
-                // if there wasn't any focusing item,  add the very first focusable
+                // if there wasn't any focused item, add the very first focusable
                 // items and stop.
-                if (focusedPos == NO_POSITION) {
+                if (immediateFocusedChild == null) {
                     child.addFocusables(views,  direction, focusableMode);
                     if (views.size() > focusableCount) {
                         break;
                     }
                     continue;
                 }
-                int position = getPositionByIndex(i);
+                int position = getAdapterPositionByIndex(i);
                 Grid.Location loc = mGrid.getLocation(position);
                 if (loc == null) {
                     continue;
@@ -3152,7 +3310,7 @@
             int focusableCount = views.size();
             if (mFocusScrollStrategy != BaseGridView.FOCUS_SCROLL_ALIGNED) {
                 // adding views not overlapping padding area to avoid scrolling in gaining focus
-                int left = mWindowAlignment.mainAxis().getPaddingLow();
+                int left = mWindowAlignment.mainAxis().getPaddingMin();
                 int right = mWindowAlignment.mainAxis().getClientSize() + left;
                 for (int i = 0, count = getChildCount(); i < count; i++) {
                     View child = getChildAt(i);
@@ -3198,6 +3356,15 @@
         return count == 0 || mBaseGridView.findViewHolderForAdapterPosition(0) != null;
     }
 
+    boolean isItemFullyVisible(int pos) {
+        RecyclerView.ViewHolder vh = mBaseGridView.findViewHolderForAdapterPosition(pos);
+        if (vh == null) {
+            return false;
+        }
+        return vh.itemView.getLeft() >= 0 && vh.itemView.getRight() < mBaseGridView.getWidth()
+                && vh.itemView.getTop() >= 0 && vh.itemView.getBottom() < mBaseGridView.getHeight();
+    }
+
     boolean canScrollTo(View view) {
         return view.getVisibility() == View.VISIBLE && (!hasFocus() || view.hasFocusable());
     }
@@ -3245,7 +3412,7 @@
             increment = -1;
             end = -1;
         }
-        int left = mWindowAlignment.mainAxis().getPaddingLow();
+        int left = mWindowAlignment.mainAxis().getPaddingMin();
         int right = mWindowAlignment.mainAxis().getClientSize() + left;
         for (int i = index; i != end; i += increment) {
             View child = getChildAt(i);
@@ -3411,7 +3578,7 @@
         // save views currently is on screen (TODO save cached views)
         for (int i = 0, count = getChildCount(); i < count; i++) {
             View view = getChildAt(i);
-            int position = getPositionByView(view);
+            int position = getAdapterPositionByView(view);
             if (position != NO_POSITION) {
                 bundle = mChildrenStates.saveOnScreenView(bundle, view, position);
             }
@@ -3464,12 +3631,14 @@
             RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
         ViewGroup.LayoutParams lp = host.getLayoutParams();
         if (mGrid == null || !(lp instanceof LayoutParams)) {
-            super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
             return;
         }
         LayoutParams glp = (LayoutParams) lp;
-        int position = glp.getViewLayoutPosition();
-        int rowIndex = mGrid.getRowIndex(position);
+        int position = glp.getViewAdapterPosition();
+        int rowIndex = position >= 0 ? mGrid.getRowIndex(position) : -1;
+        if (rowIndex < 0) {
+            return;
+        }
         int guessSpanIndex = position / mGrid.getNumRows();
         if (mOrientation == HORIZONTAL) {
             info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
@@ -3490,11 +3659,10 @@
         saveContext(recycler, state);
         switch (action) {
             case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
-                // try to focus all the way to the last visible item on the same row.
-                processSelectionMoves(false, -mState.getItemCount());
+                processSelectionMoves(false, -1);
                 break;
             case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
-                processSelectionMoves(false, mState.getItemCount());
+                processSelectionMoves(false, 1);
                 break;
         }
         leaveContext();
@@ -3520,7 +3688,7 @@
             if (!canScrollTo(child)) {
                 continue;
             }
-            int position = getPositionByIndex(index);
+            int position = getAdapterPositionByIndex(index);
             int rowIndex = mGrid.getRowIndex(position);
             if (focusedRow == NO_POSITION) {
                 focusPosition = position;
@@ -3559,11 +3727,12 @@
     public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
             AccessibilityNodeInfoCompat info) {
         saveContext(recycler, state);
-        if (mScrollEnabled && !hasCreatedFirstItem()) {
+        int count = state.getItemCount();
+        if (mScrollEnabled && count > 1 && !isItemFullyVisible(0)) {
             info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
             info.setScrollable(true);
         }
-        if (mScrollEnabled && !hasCreatedLastItem()) {
+        if (mScrollEnabled && count > 1 && !isItemFullyVisible(count - 1)) {
             info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
             info.setScrollable(true);
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
index 2600470..e3de87c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
@@ -2,13 +2,12 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
-import android.widget.TextView;
 
 /**
  * Relative layout implementation that lays out child views based on provided keyline percent(
@@ -45,19 +44,18 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
 
-        TextView mTitleView = (TextView) getRootView().findViewById(R.id.guidance_title);
-        TextView mBreadcrumbView = (TextView) getRootView().findViewById(R.id.guidance_breadcrumb);
-        TextView mDescriptionView = (TextView) getRootView().findViewById(
+        View mTitleView = getRootView().findViewById(R.id.guidance_title);
+        View mBreadcrumbView = getRootView().findViewById(R.id.guidance_breadcrumb);
+        View mDescriptionView = getRootView().findViewById(
                 R.id.guidance_description);
-        ImageView mIconView = (ImageView) getRootView().findViewById(R.id.guidance_icon);
+        ImageView mIconView = getRootView().findViewById(R.id.guidance_icon);
         int mTitleKeylinePixels = (int) (getMeasuredHeight() * mTitleKeylinePercent / 100);
 
         if (mTitleView != null && mTitleView.getParent() == this) {
-            Paint textPaint = mTitleView.getPaint();
-            int titleViewTextHeight = -textPaint.getFontMetricsInt().top;
+            int titleViewBaseline = mTitleView.getBaseline();
             int mBreadcrumbViewHeight = mBreadcrumbView.getMeasuredHeight();
             int guidanceTextContainerTop = mTitleKeylinePixels
-                    - titleViewTextHeight - mBreadcrumbViewHeight - mTitleView.getPaddingTop();
+                    - titleViewBaseline - mBreadcrumbViewHeight - mTitleView.getPaddingTop();
             int offset = guidanceTextContainerTop - mBreadcrumbView.getTop();
 
             if (mBreadcrumbView != null && mBreadcrumbView.getParent() == this) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
index 888cc8f..edcec42 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
@@ -14,13 +14,10 @@
 package android.support.v17.leanback.widget;
 
 import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.v17.leanback.R;
 import android.text.TextUtils;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
index 632c287..8898d48 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
@@ -123,8 +123,10 @@
         private int mActionFlags;
 
         private int mEditable = EDITING_NONE;
-        private int mInputType = InputType.TYPE_CLASS_TEXT;
-        private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT;
+        private int mInputType = InputType.TYPE_CLASS_TEXT
+                | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+        private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT
+                | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
         private int mEditInputType = InputType.TYPE_CLASS_TEXT;
         private int mDescriptionEditInputType = InputType.TYPE_CLASS_TEXT;
         private int mCheckSetId = NO_CHECK_SET;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
index 7d2904a..5b755f5 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
@@ -54,7 +54,7 @@
         /**
          * Called when the user clicks on an action.
          */
-        public void onGuidedActionClicked(GuidedAction action);
+        void onGuidedActionClicked(GuidedAction action);
 
     }
 
@@ -66,7 +66,7 @@
         /**
          * Called when the user focuses on an action.
          */
-        public void onGuidedActionFocused(GuidedAction action);
+        void onGuidedActionFocused(GuidedAction action);
     }
 
     /**
@@ -77,22 +77,22 @@
         /**
          * Called when the user exits edit mode on an action.
          */
-        public void onGuidedActionEditCanceled(GuidedAction action);
+        void onGuidedActionEditCanceled(GuidedAction action);
 
         /**
          * Called when the user exits edit mode on an action and process confirm button in IME.
          */
-        public long onGuidedActionEditedAndProceed(GuidedAction action);
+        long onGuidedActionEditedAndProceed(GuidedAction action);
 
         /**
          * Called when Ime Open
          */
-        public void onImeOpen();
+        void onImeOpen();
 
         /**
          * Called when Ime Close
          */
-        public void onImeClose();
+        void onImeClose();
     }
 
     private final boolean mIsSubAdapter;
@@ -102,6 +102,8 @@
     private final List<GuidedAction> mActions;
     private ClickListener mClickListener;
     final GuidedActionsStylist mStylist;
+    GuidedActionAdapterGroup mGroup;
+
     private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -124,7 +126,6 @@
             }
         }
     };
-    GuidedActionAdapterGroup mGroup;
 
     /**
      * Constructs a GuidedActionAdapter with the given list of guided actions, the given click and
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index e5ff158..15e6109 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -21,6 +21,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -151,6 +152,7 @@
     public static final int VIEW_TYPE_DATE_PICKER = 1;
 
     final static ItemAlignmentFacet sGuidedActionItemAlignFacet;
+
     static {
         sGuidedActionItemAlignFacet = new ItemAlignmentFacet();
         ItemAlignmentFacet.ItemAlignmentDef alignedDef = new ItemAlignmentFacet.ItemAlignmentDef();
@@ -179,6 +181,7 @@
         ImageView mChevronView;
         int mEditingMode = EDITING_NONE;
         private final boolean mIsSubAction;
+        Animator mPressAnimator;
 
         final AccessibilityDelegate mDelegate = new AccessibilityDelegate() {
             @Override
@@ -364,6 +367,28 @@
             }
             return null;
         }
+
+        void press(boolean pressed) {
+            if (mPressAnimator != null) {
+                mPressAnimator.cancel();
+                mPressAnimator = null;
+            }
+            final int themeAttrId = pressed ? R.attr.guidedActionPressedAnimation :
+                    R.attr.guidedActionUnpressedAnimation;
+            Context ctx = itemView.getContext();
+            TypedValue typedValue = new TypedValue();
+            if (ctx.getTheme().resolveAttribute(themeAttrId, typedValue, true)) {
+                mPressAnimator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
+                mPressAnimator.setTarget(itemView);
+                mPressAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPressAnimator = null;
+                    }
+                });
+                mPressAnimator.start();
+            }
+        }
     }
 
     private static String TAG = "GuidedActionsStylist";
@@ -649,9 +674,9 @@
      * @return The view to be added to the caller's view hierarchy.
      */
     public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
-
         vh.mAction = action;
         if (vh.mTitleView != null) {
+            vh.mTitleView.setInputType(action.getInputType());
             vh.mTitleView.setText(action.getTitle());
             vh.mTitleView.setAlpha(action.isEnabled() ? mEnabledTextAlpha : mDisabledTextAlpha);
             vh.mTitleView.setFocusable(false);
@@ -659,6 +684,7 @@
             vh.mTitleView.setLongClickable(false);
         }
         if (vh.mDescriptionView != null) {
+            vh.mDescriptionView.setInputType(action.getDescriptionInputType());
             vh.mDescriptionView.setText(action.getDescription());
             vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription())
                     ? View.GONE : View.VISIBLE);
@@ -706,6 +732,26 @@
         updateChevronAndVisibility(vh);
     }
 
+    /**
+     * Switches action to edit mode and pops up the keyboard.
+     */
+    public void openInEditMode(GuidedAction action) {
+        final GuidedActionAdapter guidedActionAdapter =
+                (GuidedActionAdapter) getActionsGridView().getAdapter();
+        int actionIndex = guidedActionAdapter.getActions().indexOf(action);
+        if (actionIndex < 0 || !action.isEditable()) {
+            return;
+        }
+
+        getActionsGridView().setSelectedPosition(actionIndex, new ViewHolderTask() {
+            @Override
+            public void run(RecyclerView.ViewHolder viewHolder) {
+                ViewHolder vh = (ViewHolder) viewHolder;
+                guidedActionAdapter.mGroup.openIme(guidedActionAdapter, vh);
+            }
+        });
+    }
+
     private static void setMaxLines(TextView view, int maxLines) {
         // setSingleLine must be called before setMaxLines because it resets maximum to
         // Integer.MAX_VALUE.
@@ -845,9 +891,7 @@
      * @param pressed True if the action has been pressed, false if it has been unpressed.
      */
     public void onAnimateItemPressed(ViewHolder vh, boolean pressed) {
-        int attr = pressed ? R.attr.guidedActionPressedAnimation :
-                R.attr.guidedActionUnpressedAnimation;
-        createAnimator(vh.itemView, attr).start();
+        vh.press(pressed);
     }
 
     /**
@@ -855,7 +899,7 @@
      * @param vh The view holder associated with the relevant action.
      */
     public void onAnimateItemPressedCancelled(ViewHolder vh) {
-        createAnimator(vh.itemView, R.attr.guidedActionUnpressedAnimation).end();
+        vh.press(false);
     }
 
     /**
@@ -1444,19 +1488,9 @@
         return ctx.getResources().getDimensionPixelSize(typedValue.resourceId);
     }
 
-    private static Animator createAnimator(View v, int attrId) {
-        Context ctx = v.getContext();
-        TypedValue typedValue = new TypedValue();
-        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
-        Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
-        animator.setTarget(v);
-        return animator;
-    }
-
     private boolean setIcon(final ImageView iconView, GuidedAction action) {
         Drawable icon = null;
         if (iconView != null) {
-            Context context = iconView.getContext();
             icon = action.getIcon();
             if (icon != null) {
                 // setImageDrawable resets the drawable's level unless we set the view level first.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java
index d655e85..5871247 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java
@@ -15,7 +15,6 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v17.leanback.widget.picker.DatePicker;
 
 import java.util.Calendar;
 import java.util.TimeZone;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
index eff822b..8aead19 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
@@ -47,7 +47,7 @@
         // end edge with row view's end edge, otherwise align start edges.
         view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
-        boolean isRtl = ViewCompat.getLayoutDirection(view) == View.LAYOUT_DIRECTION_RTL;
+        boolean isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
         if (!isRtl && mCardLeft + view.getMeasuredWidth() > rightLimit) {
             params.leftMargin = rightLimit  - view.getMeasuredWidth();
         } else if (isRtl && mCardLeft < leftLimit) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
index a91a008..8f0c66e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
@@ -175,12 +175,12 @@
         boolean hasIconLeft =
                 !hasIconRight && (cardType & CARD_TYPE_FLAG_ICON_LEFT) == CARD_TYPE_FLAG_ICON_LEFT;
 
-        mImageView = (ImageView) findViewById(R.id.main_image);
+        mImageView = findViewById(R.id.main_image);
         if (mImageView.getDrawable() == null) {
             mImageView.setVisibility(View.INVISIBLE);
         }
 
-        mInfoArea = (ViewGroup) findViewById(R.id.info_field);
+        mInfoArea = findViewById(R.id.info_field);
         if (hasImageOnly) {
             removeView(mInfoArea);
             cardAttrs.recycle();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
index 4790a12..e1b7d13 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
@@ -16,12 +16,8 @@
 
 import static android.support.v7.widget.RecyclerView.HORIZONTAL;
 import static android.support.v7.widget.RecyclerView.VERTICAL;
-import static android.support.v17.leanback.widget.BaseGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
 
-import android.graphics.Rect;
-import android.support.v17.leanback.widget.GridLayoutManager.LayoutParams;
 import android.view.View;
-import android.view.ViewGroup;
 
 /**
  * Defines alignment position on two directions of an item view. Typically item
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java
index f1d2b95..1ca735e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java
@@ -26,17 +26,17 @@
  * ItemAlignmentFacet contains single or multiple {@link ItemAlignmentDef}s. First
  * {@link ItemAlignmentDef} describes the default alignment position for ViewHolder, it also
  * overrides the default item alignment settings on {@link VerticalGridView} and
- * {@link HorizontalGridView}. When there are multiple {@link ItemAlignmentDef}s, the extra
- * {@link ItemAlignmentDef}s are used to calculate deltas from first alignment position. When a
- * descendant view is focused within the ViewHolder, grid view will visit focused view and its
- * ancestors till the root of ViewHolder to match extra {@link ItemAlignmentDef}s'
- * {@link ItemAlignmentDef#getItemAlignmentViewId()}. Once a match found, the
- * {@link ItemAlignmentDef} is used to adjust a scroll delta from default alignment position.
+ * {@link HorizontalGridView} (see {@link BaseGridView#setItemAlignmentOffset(int)} etc). One
+ * ItemAlignmentFacet can have multiple {@link ItemAlignmentDef}s, e.g. having two aligned positions
+ * when child1 gets focus or child2 gets focus. Grid view will visit focused view and its
+ * ancestors till the root of ViewHolder to match {@link ItemAlignmentDef}s'
+ * {@link ItemAlignmentDef#getItemAlignmentFocusViewId()}. Once a match found, the
+ * {@link ItemAlignmentDef} is used to calculate alignment position.
  */
 public final class ItemAlignmentFacet {
 
     /**
-     * Value indicates that percent is not used.
+     * Value indicates that percent is not used. Equivalent to 0.
      */
     public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1;
 
@@ -52,32 +52,47 @@
         private boolean mAlignToBaseline;
 
         /**
-         * Sets number of pixels to offset. Can be negative for alignment from the high edge, or
-         * positive for alignment from the low edge.
+         * Sets number of pixels to the end of low edge. Supports right to left layout direction.
+         * @param offset In left to right or vertical case, it's the offset added to left/top edge.
+         *               In right to left case, it's the offset subtracted from right edge.
          */
         public final void setItemAlignmentOffset(int offset) {
             mOffset = offset;
         }
 
         /**
-         * Gets number of pixels to offset. Can be negative for alignment from the high edge, or
-         * positive for alignment from the low edge.
+         * Returns number of pixels to the end of low edge. Supports right to left layout direction.
+         * In left to right or vertical case, it's the offset added to left/top edge. In right to
+         * left case, it's the offset subtracted from right edge.
+         * @return Number of pixels to the end of low edge.
          */
         public final int getItemAlignmentOffset() {
             return mOffset;
         }
 
         /**
-         * Sets whether to include left/top padding for positive item offset, include
-         * right/bottom padding for negative item offset.
+         * Sets whether applies padding to item alignment when
+         * {@link #getItemAlignmentOffsetPercent()} is 0 or 100.
+         * <p>When true:
+         * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0.
+         * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100.
+         * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
+         * </p>
+         * <p>When false: does not apply padding</p>
          */
         public final void setItemAlignmentOffsetWithPadding(boolean withPadding) {
             mOffsetWithPadding = withPadding;
         }
 
         /**
-         * When it is true: we include left/top padding for positive item offset, include
-         * right/bottom padding for negative item offset.
+         * Returns true if applies padding to item alignment when
+         * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise.
+         * <p>When true:
+         * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0.
+         * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100.
+         * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
+         * </p>
+         * <p>When false: does not apply padding</p>
          */
         public final boolean isItemAlignmentOffsetWithPadding() {
             return mOffsetWithPadding;
@@ -85,8 +100,8 @@
 
         /**
          * Sets the offset percent for item alignment in addition to offset.  E.g., 40
-         * means 40% of the width from the low edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED}
-         * to disable.
+         * means 40% of width/height from the low edge. In the right to left case, it's the 40%
+         * width from right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
          */
         public final void setItemAlignmentOffsetPercent(float percent) {
             if ((percent < 0 || percent > 100)
@@ -98,8 +113,8 @@
 
         /**
          * Gets the offset percent for item alignment in addition to offset. E.g., 40
-         * means 40% of the width from the low edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED}
-         * to disable.
+         * means 40% of the width from the low edge. In the right to left case, it's the 40% from
+         * right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
          */
         public final float getItemAlignmentOffsetPercent() {
             return mOffsetPercent;
@@ -107,17 +122,28 @@
 
         /**
          * Sets Id of which child view to be aligned.  View.NO_ID refers to root view and should
-         * be only used in first one.  Extra ItemAlignmentDefs should provide view id to match
-         * currently focused view.
+         * be only used in first one.  Different view ids of {@link ItemAlignmentFacet
+         * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are
+         * two child views R.id.child1 and R.id.child2. App may allocated two
+         * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id
+         * R.id.child2. Note this id may or may not be same as the child view that takes focus.
+         *
+         * @param viewId The id of child view that will be aligned to.
+         * @see #setItemAlignmentFocusViewId(int)
          */
         public final void setItemAlignmentViewId(int viewId) {
             mViewId = viewId;
         }
 
         /**
-         * Gets Id of which child view to be aligned.  View.NO_ID refers to root view and should
-         * be only used in first one.  Extra ItemAlignmentDefs should provide view id to match
-         * currently focused view.
+         * Returns Id of which child view to be aligned.  View.NO_ID refers to root view and should
+         * be only used in first one.  Different view ids of {@link ItemAlignmentFacet
+         * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are
+         * two child views R.id.child1 and R.id.child2. App may allocated two
+         * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id
+         * R.id.child2. Note this id may or may not be same as the child view that takes focus.
+         *
+         * @see #setItemAlignmentFocusViewId(int)
          */
         public final int getItemAlignmentViewId() {
             return mViewId;
@@ -125,7 +151,8 @@
 
         /**
          * Sets Id of which child view take focus for alignment.  When not set, it will use
-         * use same id of {@link #getItemAlignmentViewId()}
+         * use same id of {@link #getItemAlignmentViewId()}.
+         * @param viewId The id of child view that will be focused to.
          */
         public final void setItemAlignmentFocusViewId(int viewId) {
             mFocusViewId = viewId;
@@ -140,16 +167,16 @@
         }
 
         /**
-         * Align to baseline if {@link #getItemAlignmentViewId()} is a TextView and
-         * alignToBaseline is true.
-         * @param alignToBaseline Boolean indicating whether to align the text to baseline.
+         * When true, align to {@link View#getBaseline()} for the view of with id equals
+         * {@link #getItemAlignmentViewId()}; false otherwise.
+         * @param alignToBaseline Boolean indicating whether to align to view baseline.
          */
         public final void setAlignedToTextViewBaseline(boolean alignToBaseline) {
             this.mAlignToBaseline = alignToBaseline;
         }
 
         /**
-         * Returns true when TextView should be aligned to the baseline.
+         * Returns true when View should be aligned to {@link View#getBaseline()}
          */
         public boolean isAlignedToTextViewBaseLine() {
             return mAlignToBaseline;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
index a51d1ca..7dc946e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
@@ -16,12 +16,10 @@
 import static android.support.v17.leanback.widget.ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
 import static android.support.v7.widget.RecyclerView.HORIZONTAL;
 
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.support.v17.leanback.widget.GridLayoutManager.LayoutParams;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 /**
  * Helper class to handle ItemAlignmentFacet in a grid view.
@@ -45,31 +43,48 @@
         }
         int alignPos = facet.mOffset;
         if (orientation == HORIZONTAL) {
-            if (facet.mOffset >= 0) {
+            if (itemView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+                alignPos = (view == itemView ? p.getOpticalWidth(view)
+                        : view.getWidth()) - alignPos;
                 if (facet.mOffsetWithPadding) {
-                    alignPos += view.getPaddingLeft();
+                    if (facet.mOffsetPercent == 0f) {
+                        alignPos -= view.getPaddingRight();
+                    } else if (facet.mOffsetPercent == 100f) {
+                        alignPos += view.getPaddingLeft();
+                    }
                 }
-            } else {
+                if (facet.mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
+                    alignPos -= (int) (((view == itemView ? p.getOpticalWidth(view)
+                            : view.getWidth()) * facet.mOffsetPercent) / 100f);
+                }
+                if (itemView != view) {
+                    sRect.right = alignPos;
+                    ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, sRect);
+                    alignPos = sRect.right + p.getOpticalRightInset();
+                }
+            } else  {
                 if (facet.mOffsetWithPadding) {
-                    alignPos -= view.getPaddingRight();
+                    if (facet.mOffsetPercent == 0f) {
+                        alignPos += view.getPaddingLeft();
+                    } else if (facet.mOffsetPercent == 100f) {
+                        alignPos -= view.getPaddingRight();
+                    }
                 }
-            }
-            if (facet.mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
-                alignPos += (int) (((view == itemView ? p.getOpticalWidth(view) : view.getWidth())
-                        * facet.mOffsetPercent) / 100f);
-            }
-            if (itemView != view) {
-                sRect.left = alignPos;
-                ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, sRect);
-                alignPos = sRect.left - p.getOpticalLeftInset();
+                if (facet.mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
+                    alignPos += (int) (((view == itemView ? p.getOpticalWidth(view)
+                            : view.getWidth()) * facet.mOffsetPercent) / 100f);
+                }
+                if (itemView != view) {
+                    sRect.left = alignPos;
+                    ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, sRect);
+                    alignPos = sRect.left - p.getOpticalLeftInset();
+                }
             }
         } else {
-            if (facet.mOffset >= 0) {
-                if (facet.mOffsetWithPadding) {
+            if (facet.mOffsetWithPadding) {
+                if (facet.mOffsetPercent == 0f) {
                     alignPos += view.getPaddingTop();
-                }
-            } else {
-                if (facet.mOffsetWithPadding) {
+                } else if (facet.mOffsetPercent == 100f) {
                     alignPos -= view.getPaddingBottom();
                 }
             }
@@ -82,10 +97,8 @@
                 ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, sRect);
                 alignPos = sRect.top - p.getOpticalTopInset();
             }
-            if (view instanceof TextView && facet.isAlignedToTextViewBaseLine()) {
-                Paint textPaint = ((TextView)view).getPaint();
-                int titleViewTextHeight = -textPaint.getFontMetricsInt().top;
-                alignPos += titleViewTextHeight;
+            if (facet.isAlignedToTextViewBaseLine()) {
+                alignPos += view.getBaseline();
             }
         }
         return alignPos;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
index 19e6e9a..ff152f7 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
@@ -15,7 +15,6 @@
 
 import android.content.Context;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
 
 /**
  * A wrapper class working with {@link ItemBridgeAdapter} to wrap item view in a
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowHoverCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowHoverCardView.java
index 3ea3b04..bc4ecc1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowHoverCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowHoverCardView.java
@@ -42,8 +42,8 @@
         super(context, attrs, defStyle);
         LayoutInflater inflater = LayoutInflater.from(context);
         inflater.inflate(R.layout.lb_list_row_hovercard, this);
-        mTitleView = (TextView) findViewById(R.id.title);
-        mDescriptionView = (TextView) findViewById(R.id.description);
+        mTitleView = findViewById(R.id.title);
+        mDescriptionView = findViewById(R.id.description);
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
index 1577082..e888f6d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -35,11 +35,11 @@
  * display a view for the currently focused list item below the rendered
  * list. This view is known as a hover card.
  *
- * <h3>Selection animation</h3>
- * ListRowPresenter disables {@link RowPresenter}'s default dimming effect and draws
- * a dim overlay on each view individually.  A subclass may override and disable
- * {@link #isUsingDefaultListSelectEffect()} and write its own dim effect in
- * {@link #onSelectLevelChanged(RowPresenter.ViewHolder)}.
+ * <h3>Row selection animation</h3>
+ * ListRowPresenter disables {@link RowPresenter}'s default full row dimming effect and draws
+ * a dim overlay on each child individually.  A subclass may disable the overlay on each child
+ * by overriding {@link #isUsingDefaultListSelectEffect()} to return false and write its own child
+ * dim effect in {@link #applySelectLevelToChild(ViewHolder, View)}.
  *
  * <h3>Shadow</h3>
  * ListRowPresenter applies a default shadow to each child view.  Call
@@ -270,10 +270,7 @@
 
         @Override
         public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) {
-                int dimmedColor = mRowViewHolder.mColorDimmer.getPaint().getColor();
-                mShadowOverlayHelper.setOverlayColor(viewHolder.itemView, dimmedColor);
-            }
+            applySelectLevelToChild(mRowViewHolder, viewHolder.itemView);
             mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
         }
 
@@ -681,7 +678,8 @@
     /**
      * Returns true so that default select effect is applied to each individual
      * child of {@link HorizontalGridView}.  Subclass may return false to disable
-     * the default implementation.
+     * the default implementation and implement {@link #applySelectLevelToChild(ViewHolder, View)}.
+     * @see #applySelectLevelToChild(ViewHolder, View)
      * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
      */
     public boolean isUsingDefaultListSelectEffect() {
@@ -781,29 +779,49 @@
     }
 
     /**
-     * Applies select level to header and draw a default color dim over each child
+     * Applies select level to header and draws a default color dim over each child
      * of {@link HorizontalGridView}.
      * <p>
-     * Subclass may override this method.  A subclass
-     * needs to call super.onSelectLevelChanged() for applying header select level
-     * and optionally applying a default select level to each child view of
-     * {@link HorizontalGridView} if {@link #isUsingDefaultListSelectEffect()}
-     * is true.  Subclass may override {@link #isUsingDefaultListSelectEffect()} to return
-     * false and deal with the individual item select level by itself.
+     * Subclass may override this method and starts with calling super if it has views to apply
+     * select effect other than header and HorizontalGridView.
+     * To override the default color dim over each child of {@link HorizontalGridView},
+     * app should override {@link #isUsingDefaultListSelectEffect()} to
+     * return false and override {@link #applySelectLevelToChild(ViewHolder, View)}.
      * </p>
+     * @see #isUsingDefaultListSelectEffect()
+     * @see RowPresenter.ViewHolder#getSelectLevel()
+     * @see #applySelectLevelToChild(ViewHolder, View)
      */
     @Override
     protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
         super.onSelectLevelChanged(holder);
+        ViewHolder vh = (ViewHolder) holder;
+        for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
+            applySelectLevelToChild(vh, vh.mGridView.getChildAt(i));
+        }
+    }
+
+    /**
+     * Applies select level to a child.  Default implementation draws a default color
+     * dim over each child of {@link HorizontalGridView}. This method is called on all children in
+     * {@link #onSelectLevelChanged(RowPresenter.ViewHolder)} and when a child is attached to
+     * {@link HorizontalGridView}.
+     * <p>
+     * Subclass may disable the default implementation by override
+     * {@link #isUsingDefaultListSelectEffect()} to return false and deal with the individual item
+     * select level by itself.
+     * </p>
+     * @param rowViewHolder The ViewHolder of the Row
+     * @param childView The child of {@link HorizontalGridView} to apply select level.
+     *
+     * @see #isUsingDefaultListSelectEffect()
+     * @see RowPresenter.ViewHolder#getSelectLevel()
+     * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
+     */
+    protected void applySelectLevelToChild(ViewHolder rowViewHolder, View childView) {
         if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) {
-            ViewHolder vh = (ViewHolder) holder;
-            int dimmedColor = vh.mColorDimmer.getPaint().getColor();
-            for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
-                mShadowOverlayHelper.setOverlayColor(vh.mGridView.getChildAt(i), dimmedColor);
-            }
-            if (vh.mGridView.getFadingLeftEdge()) {
-                vh.mGridView.invalidate();
-            }
+            int dimmedColor = rowViewHolder.mColorDimmer.getPaint().getColor();
+            mShadowOverlayHelper.setOverlayColor(childView, dimmedColor);
         }
     }
 
@@ -811,6 +829,7 @@
     public void freeze(RowPresenter.ViewHolder holder, boolean freeze) {
         ViewHolder vh = (ViewHolder) holder;
         vh.mGridView.setScrollEnabled(!freeze);
+        vh.mGridView.setAnimateChildLayout(!freeze);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java b/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java
index 88ae7d3..95f751b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java
@@ -47,9 +47,9 @@
         super(context, attrs);
 
         LayoutInflater.from(context).inflate(R.layout.lb_playback_now_playing_bars, this, true);
-        mImage1 = (ImageView) findViewById(R.id.bar1);
-        mImage2 = (ImageView) findViewById(R.id.bar2);
-        mImage3 = (ImageView) findViewById(R.id.bar3);
+        mImage1 = findViewById(R.id.bar1);
+        mImage2 = findViewById(R.id.bar2);
+        mImage3 = findViewById(R.id.bar3);
 
         mImage1.setPivotY(mImage1.getDrawable().getIntrinsicHeight());
         mImage2.setPivotY(mImage2.getDrawable().getIntrinsicHeight());
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java
index 28a5c0c..8985f82 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java
@@ -13,11 +13,26 @@
  */
 package android.support.v17.leanback.widget;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.content.Context;
+import android.support.annotation.RestrictTo;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.LinearLayout;
 
-class NonOverlappingLinearLayout extends LinearLayout {
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class NonOverlappingLinearLayout extends LinearLayout {
+
+    boolean mFocusableViewAvailableFixEnabled = false;
+    boolean mDeferFocusableViewAvailableInLayout;
+    final ArrayList<ArrayList<View>> mSortedAvailableViews = new ArrayList();
+
 
     public NonOverlappingLinearLayout(Context context) {
         this(context, null);
@@ -38,4 +53,60 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    public void setFocusableViewAvailableFixEnabled(boolean enabled) {
+        mFocusableViewAvailableFixEnabled = enabled;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        try {
+            mDeferFocusableViewAvailableInLayout = mFocusableViewAvailableFixEnabled
+                    && getOrientation() == HORIZONTAL
+                    && getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+            if (mDeferFocusableViewAvailableInLayout) {
+                while (mSortedAvailableViews.size() > getChildCount()) {
+                    mSortedAvailableViews.remove(mSortedAvailableViews.size() - 1);
+                }
+                while (mSortedAvailableViews.size() < getChildCount()) {
+                    mSortedAvailableViews.add(new ArrayList());
+                }
+            }
+            super.onLayout(changed, l, t, r, b);
+            if (mDeferFocusableViewAvailableInLayout) {
+                for (int i = 0; i < mSortedAvailableViews.size(); i++) {
+                    for (int j = 0; j < mSortedAvailableViews.get(i).size(); j++) {
+                        super.focusableViewAvailable(mSortedAvailableViews.get(i).get(j));
+                    }
+                }
+            }
+        } finally {
+            if (mDeferFocusableViewAvailableInLayout) {
+                mDeferFocusableViewAvailableInLayout = false;
+                for (int i = 0; i < mSortedAvailableViews.size(); i++) {
+                    mSortedAvailableViews.get(i).clear();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void focusableViewAvailable(View v) {
+        if (mDeferFocusableViewAvailableInLayout) {
+            View i = v;
+            int index = -1;
+            while (i != this && i != null) {
+                if (i.getParent() == this) {
+                    index = indexOfChild(i);
+                    break;
+                }
+                i = (View) i.getParent();
+            }
+            if (index != -1) {
+                mSortedAvailableViews.get(index).add(v);
+            }
+        } else {
+            super.focusableViewAvailable(v);
+        }
+    }
 }
\ No newline at end of file
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
index ae9d436..ae170c0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
@@ -15,8 +15,6 @@
 
 import android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef;
 import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewGroup;
 
 /**
  * Interface for receiving notification when a child of this ViewGroup has been selected.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java b/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java
index b0bdacd..a16afd3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java
@@ -31,6 +31,8 @@
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.support.annotation.ColorInt;
 import android.support.annotation.RestrictTo;
@@ -116,13 +118,14 @@
 
     // drawing
     @ColorInt
-    final int mDotFgSelectColor;
+    int mDotFgSelectColor;
     final Paint mBgPaint;
     final Paint mFgPaint;
     private final AnimatorSet mShowAnimator;
     private final AnimatorSet mHideAnimator;
     private final AnimatorSet mAnimator = new AnimatorSet();
     Bitmap mArrow;
+    Paint mArrowPaint;
     final Rect mArrowRect;
     final float mArrowToBgRatio;
 
@@ -149,14 +152,19 @@
                 R.dimen.lb_page_indicator_dot_gap);
         mArrowGap = getDimensionFromTypedArray(typedArray,
                 R.styleable.PagingIndicator_dotToArrowGap, R.dimen.lb_page_indicator_arrow_gap);
-        int bgColor = getColorFromTypedArray(typedArray, R.styleable.PagingIndicator_dotBgColor,
+
+        int dotBgColor = getColorFromTypedArray(typedArray, R.styleable.PagingIndicator_dotBgColor,
                 R.color.lb_page_indicator_dot);
         mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mBgPaint.setColor(bgColor);
+        mBgPaint.setColor(dotBgColor);
         mDotFgSelectColor = getColorFromTypedArray(typedArray,
                 R.styleable.PagingIndicator_arrowBgColor,
                 R.color.lb_page_indicator_arrow_background);
+        if (mArrowPaint == null && typedArray.hasValue(R.styleable.PagingIndicator_arrowColor)) {
+            setArrowColor(typedArray.getColor(R.styleable.PagingIndicator_arrowColor, 0));
+        }
         typedArray.recycle();
+
         mIsLtr = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
         int shadowColor = res.getColor(R.color.lb_page_indicator_arrow_shadow);
         mShadowRadius = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_radius);
@@ -201,6 +209,40 @@
         }
     }
 
+    /**
+     * Sets the color of the arrow. This color will take over the value set through the
+     * theme attribute {@link R.styleable#PagingIndicator_arrowColor} if provided.
+     *
+     * @param color the color of the arrow
+     */
+    public void setArrowColor(@ColorInt int color) {
+        if (mArrowPaint == null) {
+            mArrowPaint = new Paint();
+        }
+        mArrowPaint.setColorFilter(new PorterDuffColorFilter(color,
+                PorterDuff.Mode.SRC_IN));
+    }
+
+    /**
+     * Set the background color of the dot. This color will take over the value set through the
+     * theme attribute.
+     *
+     * @param color the background color of the dot
+     */
+    public void setDotBackgroundColor(@ColorInt int color) {
+        mBgPaint.setColor(color);
+    }
+
+    /**
+     * Sets the background color of the arrow. This color will take over the value set through the
+     * theme attribute.
+     *
+     * @param color the background color of the arrow
+     */
+    public void setArrowBackgroundColor(@ColorInt int color) {
+        mDotFgSelectColor = color;
+    }
+
     private Animator createDotAlphaAnimator(float from, float to) {
         ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_ALPHA, from, to);
         animator.setDuration(DURATION_ALPHA);
@@ -499,7 +541,7 @@
                 canvas.drawBitmap(mArrow, mArrowRect, new Rect((int) (centerX - mArrowImageRadius),
                         (int) (mDotCenterY - mArrowImageRadius),
                         (int) (centerX + mArrowImageRadius),
-                        (int) (mDotCenterY + mArrowImageRadius)), null);
+                        (int) (mDotCenterY + mArrowImageRadius)), mArrowPaint);
             }
         }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
index 83aff47..5e00d99 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
@@ -13,20 +13,18 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.util.MathUtil;
-import android.util.TypedValue;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.util.MathUtil;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 
 /**
@@ -47,6 +45,35 @@
 public class PlaybackControlsRow extends Row {
 
     /**
+     * Listener for progress or duration change.
+     */
+    public static class OnPlaybackProgressCallback {
+        /**
+         * Called when {@link PlaybackControlsRow#getCurrentPosition()} changed.
+         * @param row The PlaybackControlsRow that current time changed.
+         * @param currentTimeMs Current time in milliseconds.
+         */
+        public void onCurrentPositionChanged(PlaybackControlsRow row, long currentTimeMs) {
+        }
+
+        /**
+         * Called when {@link PlaybackControlsRow#getDuration()} changed.
+         * @param row The PlaybackControlsRow that total time changed.
+         * @param totalTime Total time in milliseconds.
+         */
+        public void onDurationChanged(PlaybackControlsRow row, long totalTime) {
+        }
+
+        /**
+         * Called when {@link PlaybackControlsRow#getBufferedPosition()} changed.
+         * @param row The PlaybackControlsRow that buffered progress changed.
+         * @param bufferedProgressMs Buffered time in milliseconds.
+         */
+        public void onBufferedPositionChanged(PlaybackControlsRow row, long bufferedProgressMs) {
+        }
+    }
+
+    /**
      * Base class for an action comprised of a series of icons.
      */
     public static abstract class MultiAction extends Action {
@@ -162,30 +189,44 @@
     public static class PlayPauseAction extends MultiAction {
         /**
          * Action index for the play icon.
+         * @deprecated Use {@link #INDEX_PLAY}
          */
+        @Deprecated
         public static int PLAY = 0;
 
         /**
          * Action index for the pause icon.
+         * @deprecated Use {@link #INDEX_PAUSE}
          */
+        @Deprecated
         public static int PAUSE = 1;
 
         /**
+         * Action index for the play icon.
+         */
+        public static final int INDEX_PLAY = 0;
+
+        /**
+         * Action index for the pause icon.
+         */
+        public static final int INDEX_PAUSE = 1;
+
+        /**
          * Constructor
          * @param context Context used for loading resources.
          */
         public PlayPauseAction(Context context) {
             super(R.id.lb_control_play_pause);
             Drawable[] drawables = new Drawable[2];
-            drawables[PLAY] = getStyledDrawable(context,
+            drawables[INDEX_PLAY] = getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_play);
-            drawables[PAUSE] = getStyledDrawable(context,
+            drawables[INDEX_PAUSE] = getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_pause);
             setDrawables(drawables);
 
             String[] labels = new String[drawables.length];
-            labels[PLAY] = context.getString(R.string.lb_playback_controls_play);
-            labels[PAUSE] = context.getString(R.string.lb_playback_controls_pause);
+            labels[INDEX_PLAY] = context.getString(R.string.lb_playback_controls_play);
+            labels[INDEX_PAUSE] = context.getString(R.string.lb_playback_controls_pause);
             setLabels(labels);
             addKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
             addKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY);
@@ -359,23 +400,37 @@
     public static abstract class ThumbsAction extends MultiAction {
         /**
          * Action index for the solid thumb icon.
+         * @deprecated Use {@link #INDEX_SOLID}
          */
+        @Deprecated
         public static int SOLID = 0;
 
         /**
          * Action index for the outline thumb icon.
+         * @deprecated Use {@link #INDEX_OUTLINE}
          */
+        @Deprecated
         public static int OUTLINE = 1;
 
         /**
+         * Action index for the solid thumb icon.
+         */
+        public static final int INDEX_SOLID = 0;
+
+        /**
+         * Action index for the outline thumb icon.
+         */
+        public static final int INDEX_OUTLINE = 1;
+
+        /**
          * Constructor
          * @param context Context used for loading resources.
          */
         public ThumbsAction(int id, Context context, int solidIconIndex, int outlineIconIndex) {
             super(id);
             Drawable[] drawables = new Drawable[2];
-            drawables[SOLID] = getStyledDrawable(context, solidIconIndex);
-            drawables[OUTLINE] = getStyledDrawable(context, outlineIconIndex);
+            drawables[INDEX_SOLID] = getStyledDrawable(context, solidIconIndex);
+            drawables[INDEX_OUTLINE] = getStyledDrawable(context, outlineIconIndex);
             setDrawables(drawables);
         }
     }
@@ -389,8 +444,9 @@
                     R.styleable.lbPlaybackControlsActionIcons_thumb_up,
                     R.styleable.lbPlaybackControlsActionIcons_thumb_up_outline);
             String[] labels = new String[getActionCount()];
-            labels[SOLID] = context.getString(R.string.lb_playback_controls_thumb_up);
-            labels[OUTLINE] = context.getString(R.string.lb_playback_controls_thumb_up_outline);
+            labels[INDEX_SOLID] = context.getString(R.string.lb_playback_controls_thumb_up);
+            labels[INDEX_OUTLINE] = context.getString(
+                    R.string.lb_playback_controls_thumb_up_outline);
             setLabels(labels);
         }
     }
@@ -404,8 +460,9 @@
                     R.styleable.lbPlaybackControlsActionIcons_thumb_down,
                     R.styleable.lbPlaybackControlsActionIcons_thumb_down_outline);
             String[] labels = new String[getActionCount()];
-            labels[SOLID] = context.getString(R.string.lb_playback_controls_thumb_down);
-            labels[OUTLINE] = context.getString(R.string.lb_playback_controls_thumb_down_outline);
+            labels[INDEX_SOLID] = context.getString(R.string.lb_playback_controls_thumb_down);
+            labels[INDEX_OUTLINE] = context.getString(
+                    R.string.lb_playback_controls_thumb_down_outline);
             setLabels(labels);
         }
     }
@@ -416,20 +473,41 @@
     public static class RepeatAction extends MultiAction {
         /**
          * Action index for the repeat-none icon.
+         * @deprecated Use {@link #INDEX_NONE}
          */
+        @Deprecated
         public static int NONE = 0;
 
         /**
          * Action index for the repeat-all icon.
+         * @deprecated Use {@link #INDEX_ALL}
          */
+        @Deprecated
         public static int ALL = 1;
 
         /**
          * Action index for the repeat-one icon.
+         * @deprecated Use {@link #INDEX_ONE}
          */
+        @Deprecated
         public static int ONE = 2;
 
         /**
+         * Action index for the repeat-none icon.
+         */
+        public static final int INDEX_NONE = 0;
+
+        /**
+         * Action index for the repeat-all icon.
+         */
+        public static final int INDEX_ALL = 1;
+
+        /**
+         * Action index for the repeat-one icon.
+         */
+        public static final int INDEX_ONE = 2;
+
+        /**
          * Constructor
          * @param context Context used for loading resources.
          */
@@ -459,20 +537,20 @@
                     R.styleable.lbPlaybackControlsActionIcons_repeat);
             BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_repeat_one);
-            drawables[NONE] = repeatDrawable;
-            drawables[ALL] = repeatDrawable == null ? null
+            drawables[INDEX_NONE] = repeatDrawable;
+            drawables[INDEX_ALL] = repeatDrawable == null ? null
                     : new BitmapDrawable(context.getResources(),
                             createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
-            drawables[ONE] = repeatOneDrawable == null ? null
+            drawables[INDEX_ONE] = repeatOneDrawable == null ? null
                     : new BitmapDrawable(context.getResources(),
                             createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor));
             setDrawables(drawables);
 
             String[] labels = new String[drawables.length];
             // Note, labels denote the action taken when clicked
-            labels[NONE] = context.getString(R.string.lb_playback_controls_repeat_all);
-            labels[ALL] = context.getString(R.string.lb_playback_controls_repeat_one);
-            labels[ONE] = context.getString(R.string.lb_playback_controls_repeat_none);
+            labels[INDEX_NONE] = context.getString(R.string.lb_playback_controls_repeat_all);
+            labels[INDEX_ALL] = context.getString(R.string.lb_playback_controls_repeat_one);
+            labels[INDEX_ONE] = context.getString(R.string.lb_playback_controls_repeat_none);
             setLabels(labels);
         }
     }
@@ -481,10 +559,31 @@
      * An action for displaying a shuffle icon.
      */
     public static class ShuffleAction extends MultiAction {
+        /**
+         * Action index for shuffle is off.
+         * @deprecated Use {@link #INDEX_OFF}
+         */
+        @Deprecated
         public static int OFF = 0;
+
+        /**
+         * Action index for shuffle is on.
+         * @deprecated Use {@link #INDEX_ON}
+         */
+        @Deprecated
         public static int ON = 1;
 
         /**
+         * Action index for shuffle is off
+         */
+        public static final int INDEX_OFF = 0;
+
+        /**
+         * Action index for shuffle is on.
+         */
+        public static final int INDEX_ON = 1;
+
+        /**
          * Constructor
          * @param context Context used for loading resources.
          */
@@ -502,14 +601,14 @@
             BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_shuffle);
             Drawable[] drawables = new Drawable[2];
-            drawables[OFF] = uncoloredDrawable;
-            drawables[ON] = new BitmapDrawable(context.getResources(),
+            drawables[INDEX_OFF] = uncoloredDrawable;
+            drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
                     createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
             setDrawables(drawables);
 
             String[] labels = new String[drawables.length];
-            labels[OFF] = context.getString(R.string.lb_playback_controls_shuffle_enable);
-            labels[ON] = context.getString(R.string.lb_playback_controls_shuffle_disable);
+            labels[INDEX_OFF] = context.getString(R.string.lb_playback_controls_shuffle_enable);
+            labels[INDEX_ON] = context.getString(R.string.lb_playback_controls_shuffle_disable);
             setLabels(labels);
         }
     }
@@ -518,10 +617,31 @@
      * An action for displaying a HQ (High Quality) icon.
      */
     public static class HighQualityAction extends MultiAction {
+        /**
+         * Action index for high quality is off.
+         * @deprecated Use {@link #INDEX_OFF}
+         */
+        @Deprecated
         public static int OFF = 0;
+
+        /**
+         * Action index for high quality is on.
+         * @deprecated Use {@link #INDEX_ON}
+         */
+        @Deprecated
         public static int ON = 1;
 
         /**
+         * Action index for high quality is off.
+         */
+        public static final int INDEX_OFF = 0;
+
+        /**
+         * Action index for high quality is on.
+         */
+        public static final int INDEX_ON = 1;
+
+        /**
          * Constructor
          * @param context Context used for loading resources.
          */
@@ -539,14 +659,16 @@
             BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_high_quality);
             Drawable[] drawables = new Drawable[2];
-            drawables[OFF] = uncoloredDrawable;
-            drawables[ON] = new BitmapDrawable(context.getResources(),
+            drawables[INDEX_OFF] = uncoloredDrawable;
+            drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
                     createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
             setDrawables(drawables);
 
             String[] labels = new String[drawables.length];
-            labels[OFF] = context.getString(R.string.lb_playback_controls_high_quality_enable);
-            labels[ON] = context.getString(R.string.lb_playback_controls_high_quality_disable);
+            labels[INDEX_OFF] = context.getString(
+                    R.string.lb_playback_controls_high_quality_enable);
+            labels[INDEX_ON] = context.getString(
+                    R.string.lb_playback_controls_high_quality_disable);
             setLabels(labels);
         }
     }
@@ -555,10 +677,32 @@
      * An action for displaying a CC (Closed Captioning) icon.
      */
     public static class ClosedCaptioningAction extends MultiAction {
+        /**
+         * Action index for closed caption is off.
+         * @deprecated Use {@link #INDEX_OFF}
+         */
+        @Deprecated
         public static int OFF = 0;
+
+        /**
+         * Action index for closed caption is on.
+         * @deprecated Use {@link #INDEX_ON}
+         */
+        @Deprecated
         public static int ON = 1;
 
         /**
+         * Action index for closed caption is off.
+         */
+        public static final int INDEX_OFF = 0;
+
+        /**
+         * Action index for closed caption is on.
+         */
+        public static final int INDEX_ON = 1;
+
+
+        /**
          * Constructor
          * @param context Context used for loading resources.
          */
@@ -576,14 +720,16 @@
             BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_closed_captioning);
             Drawable[] drawables = new Drawable[2];
-            drawables[OFF] = uncoloredDrawable;
-            drawables[ON] = new BitmapDrawable(context.getResources(),
+            drawables[INDEX_OFF] = uncoloredDrawable;
+            drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
                     createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
             setDrawables(drawables);
 
             String[] labels = new String[drawables.length];
-            labels[OFF] = context.getString(R.string.lb_playback_controls_closed_captioning_enable);
-            labels[ON] = context.getString(R.string.lb_playback_controls_closed_captioning_disable);
+            labels[INDEX_OFF] = context.getString(
+                    R.string.lb_playback_controls_closed_captioning_enable);
+            labels[INDEX_ON] = context.getString(
+                    R.string.lb_playback_controls_closed_captioning_disable);
             setLabels(labels);
         }
     }
@@ -626,7 +772,7 @@
     private long mTotalTimeMs;
     private long mCurrentTimeMs;
     private long mBufferedProgressMs;
-    private OnPlaybackStateChangedListener mListener;
+    private OnPlaybackProgressCallback mListener;
 
     /**
      * Constructor for a PlaybackControlsRow that displays some details from
@@ -720,39 +866,72 @@
      * Sets the total time in milliseconds for the playback controls row.
      * <p>If set after the row has been bound to a view, the adapter must be notified that
      * this row has changed.</p>
+     * @deprecated Use {@link #setDuration(long)}
      */
+    @Deprecated
     public void setTotalTime(int ms) {
-        setTotalTimeLong((long) ms);
+        setDuration((long) ms);
     }
 
     /**
      * Sets the total time in milliseconds (long type) for the playback controls row.
      * @param ms Total time in milliseconds of long type.
+     * @deprecated Use {@link #setDuration(long)}
      */
+    @Deprecated
     public void setTotalTimeLong(long ms) {
-        mTotalTimeMs = ms;
+        setDuration(ms);
+    }
+
+    /**
+     * Sets the total time in milliseconds (long type) for the playback controls row.
+     * If this row is bound to a view, the view will automatically
+     * be updated to reflect the new value.
+     * @param ms Total time in milliseconds of long type.
+     */
+    public void setDuration(long ms) {
+        if (mTotalTimeMs != ms) {
+            mTotalTimeMs = ms;
+            if (mListener != null) {
+                mListener.onDurationChanged(this, mTotalTimeMs);
+            }
+        }
     }
 
     /**
      * Returns the total time in milliseconds for the playback controls row.
      * @throws ArithmeticException If total time in milliseconds overflows int.
+     * @deprecated use {@link #getDuration()}
      */
+    @Deprecated
     public int getTotalTime() {
         return MathUtil.safeLongToInt(getTotalTimeLong());
     }
 
     /**
      * Returns the total time in milliseconds of long type for the playback controls row.
+     * @deprecated use {@link #getDuration()}
      */
+    @Deprecated
     public long getTotalTimeLong() {
         return mTotalTimeMs;
     }
 
     /**
+     * Returns duration in milliseconds.
+     * @return Duration in milliseconds.
+     */
+    public long getDuration() {
+        return mTotalTimeMs;
+    }
+
+    /**
      * Sets the current time in milliseconds for the playback controls row.
      * If this row is bound to a view, the view will automatically
      * be updated to reflect the new value.
+     * @deprecated use {@link #setCurrentPosition(long)}
      */
+    @Deprecated
     public void setCurrentTime(int ms) {
         setCurrentTimeLong((long) ms);
     }
@@ -760,61 +939,110 @@
     /**
      * Sets the current time in milliseconds for playback controls row in long type.
      * @param ms Current time in milliseconds of long type.
+     * @deprecated use {@link #setCurrentPosition(long)}
      */
+    @Deprecated
     public void setCurrentTimeLong(long ms) {
+        setCurrentPosition(ms);
+    }
+
+    /**
+     * Sets the current time in milliseconds for the playback controls row.
+     * If this row is bound to a view, the view will automatically
+     * be updated to reflect the new value.
+     * @param ms Current time in milliseconds of long type.
+     */
+    public void setCurrentPosition(long ms) {
         if (mCurrentTimeMs != ms) {
             mCurrentTimeMs = ms;
-            currentTimeChanged();
+            if (mListener != null) {
+                mListener.onCurrentPositionChanged(this, mCurrentTimeMs);
+            }
         }
     }
 
     /**
      * Returns the current time in milliseconds for the playback controls row.
      * @throws ArithmeticException If current time in milliseconds overflows int.
+     * @deprecated Use {@link #getCurrentPosition()}
      */
+    @Deprecated
     public int getCurrentTime() {
         return MathUtil.safeLongToInt(getCurrentTimeLong());
     }
 
     /**
      * Returns the current time in milliseconds of long type for playback controls row.
+     * @deprecated Use {@link #getCurrentPosition()}
      */
+    @Deprecated
     public long getCurrentTimeLong() {
         return mCurrentTimeMs;
     }
 
     /**
+     * Returns the current time in milliseconds of long type for playback controls row.
+     */
+    public long getCurrentPosition() {
+        return mCurrentTimeMs;
+    }
+
+    /**
      * Sets the buffered progress for the playback controls row.
      * If this row is bound to a view, the view will automatically
      * be updated to reflect the new value.
+     * @deprecated Use {@link #setBufferedPosition(long)}
      */
+    @Deprecated
     public void setBufferedProgress(int ms) {
-        setBufferedProgressLong((long) ms);
+        setBufferedPosition((long) ms);
+    }
+
+    /**
+     * Sets the buffered progress for the playback controls row.
+     * @param ms Buffered progress in milliseconds of long type.
+     * @deprecated Use {@link #setBufferedPosition(long)}
+     */
+    @Deprecated
+    public void setBufferedProgressLong(long ms) {
+        setBufferedPosition(ms);
     }
 
     /**
      * Sets the buffered progress for the playback controls row.
      * @param ms Buffered progress in milliseconds of long type.
      */
-    public void setBufferedProgressLong(long ms) {
+    public void setBufferedPosition(long ms) {
         if (mBufferedProgressMs != ms) {
             mBufferedProgressMs = ms;
-            bufferedProgressChanged();
+            if (mListener != null) {
+                mListener.onBufferedPositionChanged(this, mBufferedProgressMs);
+            }
         }
     }
-
     /**
      * Returns the buffered progress for the playback controls row.
      * @throws ArithmeticException If buffered progress in milliseconds overflows int.
+     * @deprecated Use {@link #getBufferedPosition()}
      */
+    @Deprecated
     public int getBufferedProgress() {
-        return MathUtil.safeLongToInt(getBufferedProgressLong());
+        return MathUtil.safeLongToInt(getBufferedPosition());
+    }
+
+    /**
+     * Returns the buffered progress of long type for the playback controls row.
+     * @deprecated Use {@link #getBufferedPosition()}
+     */
+    @Deprecated
+    public long getBufferedProgressLong() {
+        return mBufferedProgressMs;
     }
 
     /**
      * Returns the buffered progress of long type for the playback controls row.
      */
-    public long getBufferedProgressLong() {
+    public long getBufferedPosition() {
         return mBufferedProgressMs;
     }
 
@@ -846,34 +1074,10 @@
         return null;
     }
 
-    interface OnPlaybackStateChangedListener {
-        public void onCurrentTimeChanged(long currentTimeMs);
-        public void onBufferedProgressChanged(long bufferedProgressMs);
-    }
-
     /**
      * Sets a listener to be called when the playback state changes.
      */
-    void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) {
+    public void setOnPlaybackProgressChangedListener(OnPlaybackProgressCallback listener) {
         mListener = listener;
     }
-
-    /**
-     * Returns the playback state listener.
-     */
-    OnPlaybackStateChangedListener getOnPlaybackStateChangedListener() {
-        return mListener;
-    }
-
-    private void currentTimeChanged() {
-        if (mListener != null) {
-            mListener.onCurrentTimeChanged(mCurrentTimeMs);
-        }
-    }
-
-    private void bufferedProgressChanged() {
-        if (mListener != null) {
-            mListener.onBufferedProgressChanged(mBufferedProgressMs);
-        }
-    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
index ef49129..82cfa79 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
@@ -68,14 +68,20 @@
         BoundData mSecondaryBoundData = new BoundData();
         Presenter.ViewHolder mSelectedViewHolder;
         Object mSelectedItem;
-        final PlaybackControlsRow.OnPlaybackStateChangedListener mListener =
-                new PlaybackControlsRow.OnPlaybackStateChangedListener() {
+        final PlaybackControlsRow.OnPlaybackProgressCallback mListener =
+                new PlaybackControlsRow.OnPlaybackProgressCallback() {
             @Override
-            public void onCurrentTimeChanged(long ms) {
+            public void onCurrentPositionChanged(PlaybackControlsRow row, long ms) {
                 mPlaybackControlsPresenter.setCurrentTimeLong(mControlsVh, ms);
             }
+
             @Override
-            public void onBufferedProgressChanged(long ms) {
+            public void onDurationChanged(PlaybackControlsRow row, long ms) {
+                mPlaybackControlsPresenter.setTotalTimeLong(mControlsVh, ms);
+            }
+
+            @Override
+            public void onBufferedPositionChanged(PlaybackControlsRow row, long ms) {
                 mPlaybackControlsPresenter.setSecondaryProgressLong(mControlsVh, ms);
             }
         };
@@ -405,7 +411,7 @@
         mPlaybackControlsPresenter.setTotalTime(vh.mControlsVh, row.getTotalTime());
         mPlaybackControlsPresenter.setCurrentTime(vh.mControlsVh, row.getCurrentTime());
         mPlaybackControlsPresenter.setSecondaryProgress(vh.mControlsVh, row.getBufferedProgress());
-        row.setOnPlaybackStateChangedListener(vh.mListener);
+        row.setOnPlaybackProgressChangedListener(vh.mListener);
     }
 
     private void updateCardLayout(ViewHolder vh, int height) {
@@ -448,7 +454,7 @@
         }
         mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh);
         mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh);
-        row.setOnPlaybackStateChangedListener(null);
+        row.setOnPlaybackProgressChangedListener(null);
 
         super.onUnbindRowViewHolder(holder);
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java
index c10d202..1d0fce0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java
@@ -68,4 +68,9 @@
         }
         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackSeekDataProvider.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackSeekDataProvider.java
new file mode 100644
index 0000000..95493b3
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackSeekDataProvider.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import android.graphics.Bitmap;
+
+/**
+ * Class to be implemented by app to provide seeking data and thumbnails to UI.
+ */
+public class PlaybackSeekDataProvider {
+
+    /**
+     * Client to receive result for {@link PlaybackSeekDataProvider#getThumbnail(int,
+     * ResultCallback)}.
+     */
+    public static class ResultCallback {
+
+        /**
+         * Client of thumbnail bitmap being loaded. PlaybackSeekDataProvider must invoke this method
+         * in UI thread such as in {@link android.os.AsyncTask#onPostExecute(Object)}.
+         *
+         * @param bitmap Result of bitmap.
+         * @param index Index of {@link #getSeekPositions()}.
+         */
+        public void onThumbnailLoaded(Bitmap bitmap, int index) {
+        }
+    }
+
+    /**
+     * Get a list of sorted seek positions. The positions should not change after user starts
+     * seeking.
+     *
+     * @return A list of sorted seek positions.
+     */
+    public long[] getSeekPositions() {
+        return null;
+    }
+
+    /**
+     * Called to get thumbnail bitmap. This method is called on UI thread. When provider finds
+     * cache bitmap, it may invoke {@link ResultCallback#onThumbnailLoaded(Bitmap, int)}
+     * immediately. Provider may start background thread and invoke
+     * {@link ResultCallback#onThumbnailLoaded(Bitmap, int)} later in UI thread. The method might
+     * be called multiple times for the same position, PlaybackSeekDataProvider must guarantee
+     * to replace pending {@link ResultCallback} with the new one. When seeking right,
+     * getThumbnail() will be called with increasing index; when seeking left, getThumbnail() will
+     * be called with decreasing index. The increment of index can be used by subclass to determine
+     * prefetch direction.
+     *
+     * @param index Index of position in {@link #getSeekPositions()}.
+     * @param callback The callback to receive the result on UI thread. It may be called within
+     *                 getThumbnail() if hit cache directly.
+     */
+    public void getThumbnail(int index, ResultCallback callback) {
+    }
+
+    /**
+     * Called when seek stops, Provider should cancel pending requests for the thumbnails.
+     */
+    public void reset() {
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackSeekUi.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackSeekUi.java
new file mode 100644
index 0000000..3000498
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackSeekUi.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+/**
+ * Interface to be implemented by UI component to support seeking. PlaybackGlueHost may implement
+ * the interface to support seeking UI for the PlaybackGlue. There is only one single method
+ * {@link #setPlaybackSeekUiClient(Client)} in the interface. Client (PlaybackGlue) registers
+ * itself as a Client to receive events emitted by PlaybackSeekUi and provide data to the
+ * PlaybackSeekUi.
+ */
+public interface PlaybackSeekUi {
+
+    /**
+     * Client (e.g. PlaybackGlue) to register on PlaybackSeekUi so that it can interact
+     * with Seeking UI. For example client(PlaybackGlue) will pause media when PlaybackSeekUi emits
+     * {@link #onSeekStarted()} event.
+     */
+    class Client {
+
+        /**
+         * Called by PlaybackSeekUi to query client if seek is allowed.
+         * @return True if allow PlaybackSeekUi to start seek, false otherwise.
+         */
+        public boolean isSeekEnabled() {
+            return false;
+        }
+
+        /**
+         * Event for start seeking. Client will typically pause media and save the current position
+         * in the callback.
+         */
+        public void onSeekStarted() {
+        }
+
+        /**
+         * Called by PlaybackSeekUi asking for PlaybackSeekDataProvider. This method will be called
+         * after {@link #isSeekEnabled()} returns true. If client does not provide a
+         * {@link PlaybackSeekDataProvider}, client may directly seek media in
+         * {@link #onSeekPositionChanged(long)}.
+         * @return PlaybackSeekDataProvider or null if no PlaybackSeekDataProvider is available.
+         */
+        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
+            return null;
+        }
+
+        /**
+         * Called when user seeks to a different location. This callback is called multiple times
+         * between {@link #onSeekStarted()} and {@link #onSeekFinished(boolean)}.
+         * @param pos Position that user seeks to.
+         */
+        public void onSeekPositionChanged(long pos) {
+        }
+
+        /**
+         * Called when cancelled or confirmed. When cancelled, client should restore playing from
+         * the position before {@link #onSeekStarted()}. When confirmed, client should seek to
+         * last updated {@link #onSeekPositionChanged(long)}.
+         * @param cancelled True if cancelled false if confirmed.
+         */
+        public void onSeekFinished(boolean cancelled) {
+        }
+    }
+
+    /**
+     * Interface to be implemented by UI widget to support PlaybackSeekUi.
+     */
+    void setPlaybackSeekUiClient(Client client);
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
new file mode 100644
index 0000000..4505944
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
@@ -0,0 +1,801 @@
+/*
+ * 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.
+ */
+package android.support.v17.leanback.widget;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener;
+import android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.Arrays;
+
+/**
+ * A PlaybackTransportRowPresenter renders a {@link PlaybackControlsRow} to display a
+ * series of playback control buttons. Typically this row will be the first row in a fragment
+ * such as the {@link android.support.v17.leanback.app.PlaybackSupportFragment}.
+ *
+ * <p>The detailed description is rendered using a {@link Presenter} passed in
+ * {@link #setDescriptionPresenter(Presenter)}.  This can be an instance of
+ * {@link AbstractDetailsDescriptionPresenter}.  The application can access the
+ * detailed description ViewHolder from {@link ViewHolder#getDescriptionViewHolder()}.
+ * </p>
+ */
+public class PlaybackTransportRowPresenter extends PlaybackRowPresenter {
+
+    static class BoundData extends PlaybackControlsPresenter.BoundData {
+        ViewHolder mRowViewHolder;
+    }
+
+    /**
+     * A ViewHolder for the PlaybackControlsRow supporting seek UI.
+     */
+    public class ViewHolder extends PlaybackRowPresenter.ViewHolder implements PlaybackSeekUi {
+        final Presenter.ViewHolder mDescriptionViewHolder;
+        final ImageView mImageView;
+        final ViewGroup mDescriptionDock;
+        final ViewGroup mControlsDock;
+        final ViewGroup mSecondaryControlsDock;
+        final TextView mTotalTime;
+        final TextView mCurrentTime;
+        final SeekBar mProgressBar;
+        final ThumbsBar mThumbsBar;
+        long mTotalTimeInMs = Long.MIN_VALUE;
+        long mCurrentTimeInMs = Long.MIN_VALUE;
+        long mSecondaryProgressInMs;
+        final StringBuilder mTempBuilder = new StringBuilder();
+        ControlBarPresenter.ViewHolder mControlsVh;
+        ControlBarPresenter.ViewHolder mSecondaryControlsVh;
+        BoundData mControlsBoundData = new BoundData();
+        BoundData mSecondaryBoundData = new BoundData();
+        Presenter.ViewHolder mSelectedViewHolder;
+        Object mSelectedItem;
+        PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
+        int mThumbHeroIndex = -1;
+
+        Client mSeekClient;
+        boolean mInSeek;
+        PlaybackSeekDataProvider mSeekDataProvider;
+        long[] mPositions;
+        int mPositionsLength;
+
+        final PlaybackControlsRow.OnPlaybackProgressCallback mListener =
+                new PlaybackControlsRow.OnPlaybackProgressCallback() {
+            @Override
+            public void onCurrentPositionChanged(PlaybackControlsRow row, long ms) {
+                setCurrentPosition(ms);
+            }
+
+            @Override
+            public void onDurationChanged(PlaybackControlsRow row, long ms) {
+                setTotalTime(ms);
+            }
+
+            @Override
+            public void onBufferedPositionChanged(PlaybackControlsRow row, long ms) {
+                setBufferedPosition(ms);
+            }
+        };
+
+        void updateProgressInSeek(boolean forward) {
+            long newPos;
+            long pos = mCurrentTimeInMs;
+            if (mPositionsLength > 0) {
+                int index = Arrays.binarySearch(mPositions, 0, mPositionsLength, pos);
+                int thumbHeroIndex;
+                if (forward) {
+                    if (index >= 0) {
+                        // found it, seek to neighbour key position at higher side
+                        if (index < mPositionsLength - 1) {
+                            newPos = mPositions[index + 1];
+                            thumbHeroIndex = index + 1;
+                        } else {
+                            newPos = mTotalTimeInMs;
+                            thumbHeroIndex = index;
+                        }
+                    } else {
+                        // not found, seek to neighbour key position at higher side.
+                        int insertIndex = -1 - index;
+                        if (insertIndex <= mPositionsLength - 1) {
+                            newPos = mPositions[insertIndex];
+                            thumbHeroIndex = insertIndex;
+                        } else {
+                            newPos = mTotalTimeInMs;
+                            thumbHeroIndex = insertIndex > 0 ? insertIndex - 1 : 0;
+                        }
+                    }
+                } else {
+                    if (index >= 0) {
+                        // found it, seek to neighbour key position at lower side.
+                        if (index > 0) {
+                            newPos = mPositions[index - 1];
+                            thumbHeroIndex = index - 1;
+                        } else {
+                            newPos = 0;
+                            thumbHeroIndex = 0;
+                        }
+                    } else {
+                        // not found, seek to neighbour key position at lower side.
+                        int insertIndex = -1 - index;
+                        if (insertIndex > 0) {
+                            newPos = mPositions[insertIndex - 1];
+                            thumbHeroIndex = insertIndex - 1;
+                        } else {
+                            newPos = 0;
+                            thumbHeroIndex = 0;
+                        }
+                    }
+                }
+                updateThumbsInSeek(thumbHeroIndex, forward);
+            } else {
+                long interval = (long) (mTotalTimeInMs * getDefaultSeekIncrement());
+                newPos = pos + (forward ? interval : -interval);
+                if (newPos > mTotalTimeInMs) {
+                    newPos = mTotalTimeInMs;
+                } else if (newPos < 0) {
+                    newPos = 0;
+                }
+            }
+            double ratio = (double) newPos / mTotalTimeInMs;     // Range: [0, 1]
+            mProgressBar.setProgress((int) (ratio * Integer.MAX_VALUE)); // Could safely cast to int
+            mSeekClient.onSeekPositionChanged(newPos);
+        }
+
+        void updateThumbsInSeek(int thumbHeroIndex, boolean forward) {
+            if (mThumbHeroIndex == thumbHeroIndex) {
+                return;
+            }
+
+            final int totalNum = mThumbsBar.getChildCount();
+            if (totalNum < 0 || (totalNum & 1) == 0) {
+                throw new RuntimeException();
+            }
+            final int heroChildIndex = totalNum / 2;
+            final int start = Math.max(thumbHeroIndex - (totalNum / 2), 0);
+            final int end = Math.min(thumbHeroIndex + (totalNum / 2), mPositionsLength - 1);
+            final int newRequestStart;
+            final int newRequestEnd;
+
+            if (mThumbHeroIndex < 0) {
+                // first time
+                newRequestStart = start;
+                newRequestEnd = end;
+            } else {
+                forward = thumbHeroIndex > mThumbHeroIndex;
+                final int oldStart = Math.max(mThumbHeroIndex - (totalNum / 2), 0);
+                final int oldEnd = Math.min(mThumbHeroIndex + (totalNum / 2),
+                        mPositionsLength - 1);
+                if (forward) {
+                    newRequestStart = Math.max(oldEnd + 1, start);
+                    newRequestEnd = end;
+                    // overlapping area directly assign bitmap from previous result
+                    for (int i = start; i <= newRequestStart - 1; i++) {
+                        mThumbsBar.setThumbBitmap(heroChildIndex + (i - thumbHeroIndex),
+                                mThumbsBar.getThumbBitmap(heroChildIndex + (i - mThumbHeroIndex)));
+                    }
+                } else {
+                    newRequestEnd = Math.min(oldStart - 1, end);
+                    newRequestStart = start;
+                    // overlapping area directly assign bitmap from previous result in backward
+                    for (int i = end; i >= newRequestEnd + 1; i--) {
+                        mThumbsBar.setThumbBitmap(heroChildIndex + (i - thumbHeroIndex),
+                                mThumbsBar.getThumbBitmap(heroChildIndex + (i - mThumbHeroIndex)));
+                    }
+                }
+            }
+            // processing new requests with mThumbHeroIndex updated
+            mThumbHeroIndex = thumbHeroIndex;
+            if (forward) {
+                for (int i = newRequestStart; i <= newRequestEnd; i++) {
+                    mSeekDataProvider.getThumbnail(i, mThumbResult);
+                }
+            } else {
+                for (int i = newRequestEnd; i >= newRequestStart; i--) {
+                    mSeekDataProvider.getThumbnail(i, mThumbResult);
+                }
+            }
+            // set thumb bitmaps outside (start , end) to null
+            for (int childIndex = 0; childIndex < heroChildIndex - mThumbHeroIndex + start;
+                    childIndex++) {
+                mThumbsBar.setThumbBitmap(childIndex, null);
+            }
+            for (int childIndex = heroChildIndex + end - mThumbHeroIndex + 1;
+                    childIndex < totalNum; childIndex++) {
+                mThumbsBar.setThumbBitmap(childIndex, null);
+            }
+        }
+
+        PlaybackSeekDataProvider.ResultCallback mThumbResult =
+                new PlaybackSeekDataProvider.ResultCallback() {
+                    @Override
+                    public void onThumbnailLoaded(Bitmap bitmap, int index) {
+                        int childIndex = index - (mThumbHeroIndex - mThumbsBar.getChildCount() / 2);
+                        if (childIndex < 0 || childIndex >= mThumbsBar.getChildCount()) {
+                            return;
+                        }
+                        mThumbsBar.setThumbBitmap(childIndex, bitmap);
+                    }
+        };
+
+        boolean onForward() {
+            if (!startSeek()) {
+                return false;
+            }
+            updateProgressInSeek(true);
+            return true;
+        }
+
+        boolean onBackward() {
+            if (!startSeek()) {
+                return false;
+            }
+            updateProgressInSeek(false);
+            return true;
+        }
+        /**
+         * Constructor of ViewHolder of PlaybackTransportRowPresenter
+         * @param rootView Root view of the ViewHolder.
+         * @param descriptionPresenter The presenter that will be used to create description
+         *                             ViewHolder. The description view will be added into tree.
+         */
+        public ViewHolder(View rootView, Presenter descriptionPresenter) {
+            super(rootView);
+            mImageView = (ImageView) rootView.findViewById(R.id.image);
+            mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
+            mCurrentTime = (TextView) rootView.findViewById(R.id.current_time);
+            mTotalTime = (TextView) rootView.findViewById(R.id.total_time);
+            mProgressBar = (SeekBar) rootView.findViewById(R.id.playback_progress);
+            mProgressBar.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    onProgressBarClicked(ViewHolder.this);
+                }
+            });
+            mProgressBar.setOnKeyListener(new View.OnKeyListener() {
+
+                @Override
+                public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
+                    // when in seek only allow this keys
+                    switch (keyCode) {
+                        case KeyEvent.KEYCODE_DPAD_UP:
+                        case KeyEvent.KEYCODE_DPAD_DOWN:
+                            // eat DPAD UP/DOWN in seek mode
+                            return mInSeek;
+                        case KeyEvent.KEYCODE_DPAD_LEFT:
+                        case KeyEvent.KEYCODE_MINUS:
+                        case KeyEvent.KEYCODE_MEDIA_REWIND:
+                            if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                                onBackward();
+                            }
+                            return true;
+                        case KeyEvent.KEYCODE_DPAD_RIGHT:
+                        case KeyEvent.KEYCODE_PLUS:
+                        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                            if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                                onForward();
+                            }
+                            return true;
+                        case KeyEvent.KEYCODE_DPAD_CENTER:
+                        case KeyEvent.KEYCODE_ENTER:
+                            if (!mInSeek) {
+                                return false;
+                            }
+                            if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+                                stopSeek(false);
+                            }
+                            return true;
+                        case KeyEvent.KEYCODE_BACK:
+                        case KeyEvent.KEYCODE_ESCAPE:
+                            if (!mInSeek) {
+                                return false;
+                            }
+                            if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+                                // SeekBar does not support cancel in accessibility mode, so always
+                                // "confirm" if accessibility is on.
+                                stopSeek(Build.VERSION.SDK_INT >= 21
+                                        ? !mProgressBar.isAccessibilityFocused() : true);
+                            }
+                            return true;
+                    }
+                    return false;
+                }
+            });
+            mProgressBar.setAccessibilitySeekListener(new SeekBar.AccessibilitySeekListener() {
+                @Override
+                public boolean onAccessibilitySeekForward() {
+                    return onForward();
+                }
+
+                @Override
+                public boolean onAccessibilitySeekBackward() {
+                    return onBackward();
+                }
+            });
+            mProgressBar.setMax(Integer.MAX_VALUE); //current progress will be a fraction of this
+            mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
+            mSecondaryControlsDock =
+                    (ViewGroup) rootView.findViewById(R.id.secondary_controls_dock);
+            mDescriptionViewHolder = descriptionPresenter == null ? null :
+                    descriptionPresenter.onCreateViewHolder(mDescriptionDock);
+            if (mDescriptionViewHolder != null) {
+                mDescriptionDock.addView(mDescriptionViewHolder.view);
+            }
+            mThumbsBar = (ThumbsBar) rootView.findViewById(R.id.thumbs_row);
+        }
+
+        /**
+         * @return The ViewHolder for description.
+         */
+        public final Presenter.ViewHolder getDescriptionViewHolder() {
+            return mDescriptionViewHolder;
+        }
+
+        @Override
+        public void setPlaybackSeekUiClient(Client client) {
+            mSeekClient = client;
+        }
+
+        boolean startSeek() {
+            if (mInSeek) {
+                return true;
+            }
+            if (mSeekClient == null || !mSeekClient.isSeekEnabled()
+                    || mTotalTimeInMs <= 0) {
+                return false;
+            }
+            mInSeek = true;
+            mSeekClient.onSeekStarted();
+            mSeekDataProvider = mSeekClient.getPlaybackSeekDataProvider();
+            mPositions = mSeekDataProvider != null ? mSeekDataProvider.getSeekPositions() : null;
+            if (mPositions != null) {
+                int pos = Arrays.binarySearch(mPositions, mTotalTimeInMs);
+                if (pos >= 0) {
+                    mPositionsLength = pos + 1;
+                } else {
+                    mPositionsLength = -1 - pos;
+                }
+            } else {
+                mPositionsLength = 0;
+            }
+            mControlsVh.view.setVisibility(View.GONE);
+            mSecondaryControlsVh.view.setVisibility(View.INVISIBLE);
+            mDescriptionViewHolder.view.setVisibility(View.INVISIBLE);
+            mThumbsBar.setVisibility(View.VISIBLE);
+            return true;
+        }
+
+        void stopSeek(boolean cancelled) {
+            if (!mInSeek) {
+                return;
+            }
+            mInSeek = false;
+            mSeekClient.onSeekFinished(cancelled);
+            if (mSeekDataProvider != null) {
+                mSeekDataProvider.reset();
+            }
+            mThumbHeroIndex = -1;
+            mThumbsBar.clearThumbBitmaps();
+            mSeekDataProvider = null;
+            mPositions = null;
+            mPositionsLength = 0;
+            mControlsVh.view.setVisibility(View.VISIBLE);
+            mSecondaryControlsVh.view.setVisibility(View.VISIBLE);
+            mDescriptionViewHolder.view.setVisibility(View.VISIBLE);
+            mThumbsBar.setVisibility(View.INVISIBLE);
+        }
+
+        void dispatchItemSelection() {
+            if (!isSelected()) {
+                return;
+            }
+            if (mSelectedViewHolder == null) {
+                if (getOnItemViewSelectedListener() != null) {
+                    getOnItemViewSelectedListener().onItemSelected(null, null,
+                            ViewHolder.this, getRow());
+                }
+            } else {
+                if (getOnItemViewSelectedListener() != null) {
+                    getOnItemViewSelectedListener().onItemSelected(mSelectedViewHolder,
+                            mSelectedItem, ViewHolder.this, getRow());
+                }
+            }
+        };
+
+        Presenter getPresenter(boolean primary) {
+            ObjectAdapter adapter = primary
+                    ? ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter()
+                    : ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
+            if (adapter == null) {
+                return null;
+            }
+            if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) {
+                ControlButtonPresenterSelector selector =
+                        (ControlButtonPresenterSelector) adapter.getPresenterSelector();
+                return selector.getSecondaryPresenter();
+            }
+            return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null);
+        }
+
+        /**
+         * Returns the TextView that showing total time label. This method might be used in
+         * {@link #onSetDurationLabel}.
+         * @return The TextView that showing total time label.
+         */
+        public final TextView getDurationView() {
+            return mTotalTime;
+        }
+
+        /**
+         * Called to update total time label. Default implementation updates the TextView
+         * {@link #getDurationView()}. Subclass might override.
+         * @param totalTimeMs Total duration of the media in milliseconds.
+         */
+        protected void onSetDurationLabel(long totalTimeMs) {
+            if (mTotalTime != null) {
+                formatTime(totalTimeMs, mTempBuilder);
+                mTotalTime.setText(mTempBuilder.toString());
+            }
+        }
+
+        void setTotalTime(long totalTimeMs) {
+            if (mTotalTimeInMs != totalTimeMs) {
+                mTotalTimeInMs = totalTimeMs;
+                onSetDurationLabel(totalTimeMs);
+            }
+        }
+
+        /**
+         * Returns the TextView that showing current position label. This method might be used in
+         * {@link #onSetCurrentPositionLabel}.
+         * @return The TextView that showing current position label.
+         */
+        public final TextView getCurrentPositionView() {
+            return mCurrentTime;
+        }
+
+        /**
+         * Called to update current time label. Default implementation updates the TextView
+         * {@link #getCurrentPositionView}. Subclass might override.
+         * @param currentTimeMs Current playback position in milliseconds.
+         */
+        protected void onSetCurrentPositionLabel(long currentTimeMs) {
+            if (mCurrentTime != null) {
+                formatTime(currentTimeMs, mTempBuilder);
+                mCurrentTime.setText(mTempBuilder.toString());
+            }
+        }
+
+        void setCurrentPosition(long currentTimeMs) {
+            if (currentTimeMs != mCurrentTimeInMs) {
+                mCurrentTimeInMs = currentTimeMs;
+                onSetCurrentPositionLabel(currentTimeMs);
+            }
+            if (!mInSeek) {
+                int progressRatio = 0;
+                if (mTotalTimeInMs > 0) {
+                    // Use ratio to represent current progres
+                    double ratio = (double) mCurrentTimeInMs / mTotalTimeInMs;     // Range: [0, 1]
+                    progressRatio = (int) (ratio * Integer.MAX_VALUE);  // Could safely cast to int
+                }
+                mProgressBar.setProgress((int) progressRatio);
+            }
+        }
+
+        void setBufferedPosition(long progressMs) {
+            mSecondaryProgressInMs = progressMs;
+            // Solve the progress bar by using ratio
+            double ratio = (double) progressMs / mTotalTimeInMs;           // Range: [0, 1]
+            double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
+            mProgressBar.setSecondaryProgress((int) progressRatio);
+        }
+    }
+
+    static void formatTime(long ms, StringBuilder sb) {
+        sb.setLength(0);
+        if (ms < 0) {
+            sb.append("--");
+            return;
+        }
+        long seconds = ms / 1000;
+        long minutes = seconds / 60;
+        long hours = minutes / 60;
+        seconds -= minutes * 60;
+        minutes -= hours * 60;
+
+        if (hours > 0) {
+            sb.append(hours).append(':');
+            if (minutes < 10) {
+                sb.append('0');
+            }
+        }
+        sb.append(minutes).append(':');
+        if (seconds < 10) {
+            sb.append('0');
+        }
+        sb.append(seconds);
+    }
+
+    float mDefaultSeekIncrement = 0.01f;
+    int mProgressColor = Color.TRANSPARENT;
+    boolean mProgressColorSet;
+    Presenter mDescriptionPresenter;
+    ControlBarPresenter mPlaybackControlsPresenter;
+    ControlBarPresenter mSecondaryControlsPresenter;
+    OnActionClickedListener mOnActionClickedListener;
+
+    private final OnControlSelectedListener mOnControlSelectedListener =
+            new OnControlSelectedListener() {
+        @Override
+        public void onControlSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                ControlBarPresenter.BoundData data) {
+            ViewHolder vh = ((BoundData) data).mRowViewHolder;
+            if (vh.mSelectedViewHolder != itemViewHolder || vh.mSelectedItem != item) {
+                vh.mSelectedViewHolder = itemViewHolder;
+                vh.mSelectedItem = item;
+                vh.dispatchItemSelection();
+            }
+        }
+    };
+
+    private final OnControlClickedListener mOnControlClickedListener =
+            new OnControlClickedListener() {
+        @Override
+        public void onControlClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                ControlBarPresenter.BoundData data) {
+            ViewHolder vh = ((BoundData) data).mRowViewHolder;
+            if (vh.getOnItemViewClickedListener() != null) {
+                vh.getOnItemViewClickedListener().onItemClicked(itemViewHolder, item,
+                        vh, vh.getRow());
+            }
+            if (mOnActionClickedListener != null && item instanceof Action) {
+                mOnActionClickedListener.onActionClicked((Action) item);
+            }
+        }
+    };
+
+    public PlaybackTransportRowPresenter() {
+        setHeaderPresenter(null);
+        setSelectEffectEnabled(false);
+
+        mPlaybackControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
+        mPlaybackControlsPresenter.setDefaultFocusToMiddle(false);
+        mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
+        mSecondaryControlsPresenter.setDefaultFocusToMiddle(false);
+
+        mPlaybackControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
+        mSecondaryControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
+        mPlaybackControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
+        mSecondaryControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
+    }
+
+    /**
+     * @param descriptionPresenter Presenter for displaying item details.
+     */
+    public void setDescriptionPresenter(Presenter descriptionPresenter) {
+        mDescriptionPresenter = descriptionPresenter;
+    }
+
+    /**
+     * Sets the listener for {@link Action} click events.
+     */
+    public void setOnActionClickedListener(OnActionClickedListener listener) {
+        mOnActionClickedListener = listener;
+    }
+
+    /**
+     * Returns the listener for {@link Action} click events.
+     */
+    public OnActionClickedListener getOnActionClickedListener() {
+        return mOnActionClickedListener;
+    }
+
+    /**
+     * Sets the primary color for the progress bar.  If not set, a default from
+     * the theme will be used.
+     */
+    public void setProgressColor(@ColorInt int color) {
+        mProgressColor = color;
+        mProgressColorSet = true;
+    }
+
+    /**
+     * Returns the primary color for the progress bar.  If no color was set, transparent
+     * is returned.
+     */
+    @ColorInt
+    public int getProgressColor() {
+        return mProgressColor;
+    }
+
+    @Override
+    public void onReappear(RowPresenter.ViewHolder rowViewHolder) {
+        ViewHolder vh = (ViewHolder) rowViewHolder;
+        if (vh.view.hasFocus()) {
+            vh.mProgressBar.requestFocus();
+        }
+    }
+
+    private int getDefaultProgressColor(Context context) {
+        TypedValue outValue = new TypedValue();
+        if (context.getTheme()
+                .resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true)) {
+            return context.getResources().getColor(outValue.resourceId);
+        }
+        return context.getResources().getColor(R.color.lb_playback_progress_color_no_theme);
+    }
+
+    @Override
+    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
+        View v = LayoutInflater.from(parent.getContext()).inflate(
+                R.layout.lb_playback_transport_controls_row, parent, false);
+        ViewHolder vh = new ViewHolder(v, mDescriptionPresenter);
+        initRow(vh);
+        return vh;
+    }
+
+    private void initRow(final ViewHolder vh) {
+        vh.mControlsVh = (ControlBarPresenter.ViewHolder) mPlaybackControlsPresenter
+                .onCreateViewHolder(vh.mControlsDock);
+        vh.mProgressBar.setProgressColor(mProgressColorSet ? mProgressColor
+                : getDefaultProgressColor(vh.mControlsDock.getContext()));
+        vh.mControlsDock.addView(vh.mControlsVh.view);
+
+        vh.mSecondaryControlsVh = (ControlBarPresenter.ViewHolder) mSecondaryControlsPresenter
+                .onCreateViewHolder(vh.mSecondaryControlsDock);
+        vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
+        ((PlaybackTransportRowView) vh.view.findViewById(R.id.transport_row))
+                .setOnUnhandledKeyListener(new PlaybackTransportRowView.OnUnhandledKeyListener() {
+                @Override
+                public boolean onUnhandledKey(KeyEvent event) {
+                    if (vh.getOnKeyListener() != null) {
+                        if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            });
+    }
+
+    @Override
+    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
+        super.onBindRowViewHolder(holder, item);
+
+        ViewHolder vh = (ViewHolder) holder;
+        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
+
+        if (row.getItem() == null) {
+            vh.mDescriptionDock.setVisibility(View.GONE);
+        } else {
+            vh.mDescriptionDock.setVisibility(View.VISIBLE);
+            if (vh.mDescriptionViewHolder != null) {
+                mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem());
+            }
+        }
+
+        if (row.getImageDrawable() == null) {
+            vh.mImageView.setVisibility(View.GONE);
+        } else {
+            vh.mImageView.setVisibility(View.VISIBLE);
+        }
+        vh.mImageView.setImageDrawable(row.getImageDrawable());
+
+        vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
+        vh.mControlsBoundData.presenter = vh.getPresenter(true);
+        vh.mControlsBoundData.mRowViewHolder = vh;
+        mPlaybackControlsPresenter.onBindViewHolder(vh.mControlsVh, vh.mControlsBoundData);
+
+        vh.mSecondaryBoundData.adapter = row.getSecondaryActionsAdapter();
+        vh.mSecondaryBoundData.presenter = vh.getPresenter(false);
+        vh.mSecondaryBoundData.mRowViewHolder = vh;
+        mSecondaryControlsPresenter.onBindViewHolder(vh.mSecondaryControlsVh,
+                vh.mSecondaryBoundData);
+
+        vh.setTotalTime(row.getDuration());
+        vh.setCurrentPosition(row.getCurrentPosition());
+        vh.setBufferedPosition(row.getBufferedPosition());
+        row.setOnPlaybackProgressChangedListener(vh.mListener);
+    }
+
+    @Override
+    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
+        ViewHolder vh = (ViewHolder) holder;
+        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
+
+        if (vh.mDescriptionViewHolder != null) {
+            mDescriptionPresenter.onUnbindViewHolder(vh.mDescriptionViewHolder);
+        }
+        mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh);
+        mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh);
+        row.setOnPlaybackProgressChangedListener(null);
+
+        super.onUnbindRowViewHolder(holder);
+    }
+
+    /**
+     * Client of progress bar is clicked, default implementation delegate click to
+     * PlayPauseAction.
+     *
+     * @param vh ViewHolder of PlaybackTransportRowPresenter
+     */
+    protected void onProgressBarClicked(ViewHolder vh) {
+        if (vh != null) {
+            if (vh.mPlayPauseAction == null) {
+                vh.mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(vh.view.getContext());
+            }
+            if (vh.getOnItemViewClickedListener() != null) {
+                vh.getOnItemViewClickedListener().onItemClicked(vh, vh.mPlayPauseAction,
+                        vh, vh.getRow());
+            }
+            if (mOnActionClickedListener != null) {
+                mOnActionClickedListener.onActionClicked(vh.mPlayPauseAction);
+            }
+        }
+    }
+
+    /**
+     * Set default seek increment if {@link PlaybackSeekDataProvider} is null.
+     * @param ratio float value between 0(inclusive) and 1(inclusive).
+     */
+    public void setDefaultSeekIncrement(float ratio) {
+        mDefaultSeekIncrement = ratio;
+    }
+
+    /**
+     * Get default seek increment if {@link PlaybackSeekDataProvider} is null.
+     * @return float value between 0(inclusive) and 1(inclusive).
+     */
+    public float getDefaultSeekIncrement() {
+        return mDefaultSeekIncrement;
+    }
+
+    @Override
+    protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
+        super.onRowViewSelected(vh, selected);
+        if (selected) {
+            ((ViewHolder) vh).dispatchItemSelection();
+        }
+    }
+
+    @Override
+    protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
+        super.onRowViewAttachedToWindow(vh);
+        if (mDescriptionPresenter != null) {
+            mDescriptionPresenter.onViewAttachedToWindow(
+                    ((ViewHolder) vh).mDescriptionViewHolder);
+        }
+    }
+
+    @Override
+    protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
+        super.onRowViewDetachedFromWindow(vh);
+        if (mDescriptionPresenter != null) {
+            mDescriptionPresenter.onViewDetachedFromWindow(
+                    ((ViewHolder) vh).mDescriptionViewHolder);
+        }
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowView.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowView.java
new file mode 100644
index 0000000..2af7ff4
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowView.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.RestrictTo;
+import android.support.v17.leanback.R;
+import android.util.AttributeSet;
+import android.view.FocusFinder;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * View for PlaybackTransportRowPresenter that has a custom focusSearch.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class PlaybackTransportRowView extends LinearLayout {
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public interface OnUnhandledKeyListener {
+        /**
+         * Returns true if the key event should be consumed.
+         */
+        boolean onUnhandledKey(KeyEvent event);
+    }
+
+    private OnUnhandledKeyListener mOnUnhandledKeyListener;
+
+    public PlaybackTransportRowView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PlaybackTransportRowView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
+        mOnUnhandledKeyListener = listener;
+    }
+
+    OnUnhandledKeyListener getOnUnhandledKeyListener() {
+        return mOnUnhandledKeyListener;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (super.dispatchKeyEvent(event)) {
+            return true;
+        }
+        return mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event);
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        final View focused = findFocus();
+        if (focused != null && focused.requestFocus(direction, previouslyFocusedRect)) {
+            return true;
+        }
+        View progress = findViewById(R.id.playback_progress);
+        if (progress != null && progress.isFocusable()) {
+            if (progress.requestFocus(direction, previouslyFocusedRect)) {
+                return true;
+            }
+        }
+        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+    }
+
+    @Override
+    public View focusSearch(View focused, int direction) {
+        // when focusSearch vertically, return the next immediate focusable child
+        if (focused != null) {
+            if (direction == View.FOCUS_UP) {
+                int index = indexOfChild(getFocusedChild());
+                for (index = index - 1; index >= 0; index--) {
+                    View view = getChildAt(index);
+                    if (view.hasFocusable()) {
+                        return view;
+                    }
+                }
+            } else if (direction == View.FOCUS_DOWN) {
+                int index = indexOfChild(getFocusedChild());
+                for (index = index + 1; index < getChildCount(); index++) {
+                    View view = getChildAt(index);
+                    if (view.hasFocusable()) {
+                        return view;
+                    }
+                }
+            } else if (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT) {
+                if (getFocusedChild() instanceof ViewGroup) {
+                    return FocusFinder.getInstance().findNextFocus(
+                            (ViewGroup) getFocusedChild(), focused, direction);
+                }
+            }
+        }
+        return super.focusSearch(focused, direction);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
index f9382ff..60fedf2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
@@ -13,8 +13,9 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.support.v17.leanback.R;
 import android.view.View;
 
 /**
@@ -71,6 +72,7 @@
     /**
      * Implementation used on api 21 (and above).
      */
+    @RequiresApi(21)
     private static final class Api21Impl implements Impl {
         Api21Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java b/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
index a8ea24c..dffcbb5 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
@@ -48,7 +48,7 @@
         LayoutInflater inflater = LayoutInflater.from(context);
         inflater.inflate(R.layout.lb_row_container, this);
 
-        mHeaderDock = (ViewGroup) findViewById(R.id.lb_row_container_header_dock);
+        mHeaderDock = findViewById(R.id.lb_row_container_header_dock);
         setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
     }
 
@@ -72,6 +72,7 @@
         mHeaderDock.setVisibility(show ? View.VISIBLE : View.GONE);
     }
 
+    @Override
     public void setForeground(Drawable d) {
         mForeground = d;
         setWillNotDraw(mForeground == null);
@@ -87,6 +88,7 @@
         }
     }
 
+    @Override
     public Drawable getForeground() {
         return mForeground;
     }
@@ -108,4 +110,9 @@
             mForeground.draw(canvas);
         }
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
index 56c63cf..51047d3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
@@ -18,7 +18,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.widget.EditText;
 
 /**
  * EditText widget that monitors keyboard changes.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SeekBar.java b/v17/leanback/src/android/support/v17/leanback/widget/SeekBar.java
new file mode 100644
index 0000000..b86b9be
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SeekBar.java
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.support.annotation.RestrictTo;
+import android.support.v17.leanback.R;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Replacement of SeekBar, has two bar heights and two thumb size when focused/not_focused.
+ * The widget does not deal with KeyEvent, it's client's responsibility to set a key listener.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public final class SeekBar extends View {
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public abstract static class AccessibilitySeekListener {
+        /**
+         * Called to perform AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD
+         */
+        public abstract boolean onAccessibilitySeekForward();
+        /**
+         * Called to perform AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD
+         */
+        public abstract boolean onAccessibilitySeekBackward();
+    }
+
+    private final RectF mProgressRect = new RectF();
+    private final RectF mSecondProgressRect = new RectF();
+    private final RectF mBackgroundRect = new RectF();
+    private final Paint mSecondProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mKnobPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    private int mProgress;
+    private int mSecondProgress;
+    private int mMax;
+    private int mKnobx;
+
+    private int mActiveRadius;
+    private int mBarHeight;
+    private int mActiveBarHeight;
+
+    private AccessibilitySeekListener mAccessibilitySeekListener;
+
+    public SeekBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+        mBackgroundPaint.setColor(Color.GRAY);
+        mSecondProgressPaint.setColor(Color.LTGRAY);
+        mProgressPaint.setColor(Color.RED);
+        mKnobPaint.setColor(Color.WHITE);
+        mBarHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_progressbar_bar_height);
+        mActiveBarHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_progressbar_active_bar_height);
+        mActiveRadius = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_progressbar_active_radius);
+    }
+
+    /**
+     * Set radius in pixels for thumb when SeekBar is focused.
+     */
+    public void setActiveRadius(int radius) {
+        mActiveRadius = radius;
+        calculate();
+    }
+
+    /**
+     * Set horizontal bar height in pixels when SeekBar is not focused.
+     */
+    public void setBarHeight(int barHeight) {
+        mBarHeight = barHeight;
+        calculate();
+    }
+
+    /**
+     * Set horizontal bar height in pixels when SeekBar is focused.
+     */
+    public void setActiveBarHeight(int activeBarHeight) {
+        mActiveBarHeight = activeBarHeight;
+        calculate();
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus,
+            int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        calculate();
+    }
+
+    @Override
+    protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        calculate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        final int radius = isFocused() ? mActiveRadius : mBarHeight / 2;
+        canvas.drawRoundRect(mBackgroundRect, radius, radius, mBackgroundPaint);
+        canvas.drawRoundRect(mSecondProgressRect, radius, radius, mProgressPaint);
+        canvas.drawRoundRect(mProgressRect, radius, radius, mProgressPaint);
+        canvas.drawCircle(mKnobx, getHeight() / 2, radius, mKnobPaint);
+    }
+
+    /**
+     * Set progress within 0 and {@link #getMax()}
+     */
+    public void setProgress(int progress) {
+        if (progress > mMax) {
+            progress = mMax;
+        } else if (progress < 0) {
+            progress = 0;
+        }
+        mProgress = progress;
+        calculate();
+    }
+
+    /**
+     * Set secondary progress within 0 and {@link #getMax()}
+     */
+    public void setSecondaryProgress(int progress) {
+        if (progress > mMax) {
+            progress = mMax;
+        } else if (progress < 0) {
+            progress = 0;
+        }
+        mSecondProgress = progress;
+        calculate();
+    }
+
+    /**
+     * Get progress within 0 and {@link #getMax()}
+     */
+    public int getProgress() {
+        return mProgress;
+    }
+
+    /**
+     * Get secondary progress within 0 and {@link #getMax()}
+     */
+    public int getSecondProgress() {
+        return mSecondProgress;
+    }
+
+    /**
+     * Get max value.
+     */
+    public int getMax() {
+        return mMax;
+    }
+
+    /**
+     * Set max value.
+     */
+    public void setMax(int max) {
+        this.mMax = max;
+        calculate();
+    }
+
+    /**
+     * Set color for progress.
+     */
+    public void setProgressColor(int color) {
+        mProgressPaint.setColor(color);
+    }
+
+    private void calculate() {
+        final int barHeight = isFocused() ? mActiveBarHeight : mBarHeight;
+
+        final int width = getWidth();
+        final int height = getHeight();
+        final int verticalPadding = (height - barHeight) / 2;
+
+        mBackgroundRect.set(mBarHeight / 2, verticalPadding,
+                width - mBarHeight / 2, height - verticalPadding);
+
+        final int radius = isFocused() ? mActiveRadius : mBarHeight / 2;
+        final int progressWidth = width - radius * 2;
+        final float progressPixels = mProgress / (float) mMax * progressWidth;
+        mProgressRect.set(mBarHeight / 2, verticalPadding, mBarHeight / 2 + progressPixels,
+                height - verticalPadding);
+
+        final float secondProgressPixels = mSecondProgress / (float) mMax * progressWidth;
+        mSecondProgressRect.set(mBarHeight / 2, verticalPadding,
+                mBarHeight / 2 + secondProgressPixels, height - verticalPadding);
+
+        mKnobx = radius + (int) progressPixels;
+        invalidate();
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return android.widget.SeekBar.class.getName();
+    }
+
+    public void setAccessibilitySeekListener(AccessibilitySeekListener listener) {
+        mAccessibilitySeekListener = listener;
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (mAccessibilitySeekListener != null) {
+            switch (action) {
+                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
+                    return mAccessibilitySeekListener.onAccessibilitySeekForward();
+                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
+                    return mAccessibilitySeekListener.onAccessibilitySeekBackward();
+            }
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
index e5fb61d..cbdddbf 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.widget;
 
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
 
@@ -64,6 +65,7 @@
     /**
      * Implementation used on api 21 (and above).
      */
+    @RequiresApi(21)
     private static final class ShadowHelperApi21Impl implements ShadowHelperVersionImpl {
         ShadowHelperApi21Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
index 156d42c..06288dc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
@@ -77,7 +77,7 @@
     private int mRoundedCornerRadius;
     private static final Rect sTempRect = new Rect();
     private Paint mOverlayPaint;
-    private int mOverlayColor;
+    int mOverlayColor;
 
     /**
      * Create ShadowOverlayContainer and auto select shadow type.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
index 04a384a..56a3188 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
@@ -25,7 +25,6 @@
 class SingleRow extends Grid {
 
     private final Location mTmpLocation = new Location(0);
-    private Object[] mTmpItem = new Object[1];
 
     SingleRow() {
         setNumRows(1);
@@ -76,8 +75,9 @@
             return false;
         }
         boolean filledOne = false;
-        for (int index = getStartIndexForPrepend(); index >= 0; index--) {
-            int size = mProvider.createItem(index, false, mTmpItem);
+        int minIndex = mProvider.getMinIndex();
+        for (int index = getStartIndexForPrepend(); index >= minIndex; index--) {
+            int size = mProvider.createItem(index, false, mTmpItem, false);
             int edge;
             if (mFirstVisibleIndex < 0 || mLastVisibleIndex < 0) {
                 edge = mReversedFlow ? Integer.MIN_VALUE : Integer.MAX_VALUE;
@@ -110,7 +110,7 @@
         }
         boolean filledOne = false;
         for (int index = getStartIndexForAppend(); index < mProvider.getCount(); index++) {
-            int size = mProvider.createItem(index, true, mTmpItem);
+            int size = mProvider.createItem(index, true, mTmpItem, false);
             int edge;
             if (mFirstVisibleIndex < 0 || mLastVisibleIndex< 0) {
                 edge = mReversedFlow ? Integer.MAX_VALUE : Integer.MIN_VALUE;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
index 1d731db..fb47c5b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
@@ -17,8 +17,6 @@
 import android.support.v4.util.CircularIntArray;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A dynamic data structure that caches staggered grid position information
@@ -69,8 +67,6 @@
     //    <= mFirstIndex + mLocations.size() - 1
     protected int mFirstIndex = -1;
 
-    private Object[] mTmpItem = new Object[1];
-
     protected Object mPendingItem;
     protected int mPendingItemSize;
 
@@ -99,10 +95,11 @@
 
     @Override
     public final Location getLocation(int index) {
-        if (mLocations.size() == 0) {
+        final int indexInArray = index - mFirstIndex;
+        if (indexInArray < 0 || indexInArray >= mLocations.size()) {
             return null;
         }
-        return mLocations.get(index - mFirstIndex);
+        return mLocations.get(indexInArray);
     }
 
     @Override
@@ -142,8 +139,6 @@
         if (mLocations.size() == 0) {
             return false;
         }
-        final int count = mProvider.getCount();
-        final int firstIndex = getFirstIndex();
         int itemIndex;
         int edge;
         int offset;
@@ -166,10 +161,11 @@
                 return false;
             }
         }
-        for (; itemIndex >= mFirstIndex; itemIndex--) {
+        int firstIndex = Math.max(mProvider.getMinIndex(), mFirstIndex);
+        for (; itemIndex >= firstIndex; itemIndex--) {
             Location loc = getLocation(itemIndex);
             int rowIndex = loc.row;
-            int size = mProvider.createItem(itemIndex, false, mTmpItem);
+            int size = mProvider.createItem(itemIndex, false, mTmpItem, false);
             if (size != loc.size) {
                 mLocations.removeFromStart(itemIndex + 1 - mFirstIndex);
                 mFirstIndex = mFirstVisibleIndex;
@@ -256,7 +252,7 @@
             item = mPendingItem;
             mPendingItem = null;
         } else {
-            loc.size = mProvider.createItem(itemIndex, false, mTmpItem);
+            loc.size = mProvider.createItem(itemIndex, false, mTmpItem, false);
             item = mTmpItem[0];
         }
         mFirstIndex = mFirstVisibleIndex = itemIndex;
@@ -326,7 +322,7 @@
                 edge = edge + loc.offset;
             }
             int rowIndex = loc.row;
-            int size = mProvider.createItem(itemIndex, true, mTmpItem);
+            int size = mProvider.createItem(itemIndex, true, mTmpItem, false);
             if (size != loc.size) {
                 loc.size = size;
                 mLocations.removeFromEnd(lastIndex - itemIndex);
@@ -390,7 +386,7 @@
             item = mPendingItem;
             mPendingItem = null;
         } else {
-            loc.size = mProvider.createItem(itemIndex, true, mTmpItem);
+            loc.size = mProvider.createItem(itemIndex, true, mTmpItem, false);
             item = mTmpItem[0];
         }
         if (mLocations.size() == 1) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
index 6c6e664..436668f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
@@ -16,7 +16,7 @@
 package android.support.v17.leanback.widget;
 
 import android.os.Build;
-import android.view.View;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
 
@@ -65,6 +65,7 @@
     /**
      * Implementation used on JBMR2 (and above).
      */
+    @RequiresApi(19)
     private static final class ShadowHelperJbmr2Impl implements ShadowHelperVersionImpl {
         ShadowHelperJbmr2Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java b/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java
index 5bc0a8c..0b8781c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java
@@ -13,13 +13,13 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.support.v17.leanback.R;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.SpannedString;
@@ -31,7 +31,6 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.EditText;
-import android.widget.TextView;
 
 import java.util.List;
 import java.util.Random;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java b/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java
new file mode 100644
index 0000000..ca6eaca
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+package android.support.v17.leanback.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.support.annotation.RestrictTo;
+import android.support.v17.leanback.R;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class ThumbsBar extends LinearLayout {
+
+    // initial value for Thumb's number before measuring the screen size
+    int mNumOfThumbs = -1;
+    int mThumbWidthInPixel;
+    int mThumbHeightInPixel;
+    int mHeroThumbWidthInPixel;
+    int mHeroThumbHeightInPixel;
+    int mMeasuredMarginInPixel;
+    final SparseArray<Bitmap> mBitmaps = new SparseArray<>();
+
+    // flag to determine if the number of thumbs in thumbs bar is set by user through
+    // setNumberofThumbs API or auto-calculated according to android tv design spec.
+    private boolean mIsUserSets = false;
+
+    public ThumbsBar(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ThumbsBar(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        // According to the spec,
+        // the width of non-hero thumb should be 80% of HeroThumb's Width, i.e. 0.8 * 192dp = 154dp
+        mThumbWidthInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_width);
+        mThumbHeightInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_height);
+        // According to the spec, the width of HeroThumb should be 192dp
+        mHeroThumbHeightInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_hero_thumbs_width);
+        mHeroThumbWidthInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_hero_thumbs_height);
+        // According to the spec, the margin between thumbs to be 4dp
+        mMeasuredMarginInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_margin);
+    }
+
+    /**
+     * Get hero index which is the middle child.
+     */
+    public int getHeroIndex() {
+        return getChildCount() / 2;
+    }
+
+    /**
+     * Set size of thumb view in pixels
+     */
+    public void setThumbSize(int width, int height) {
+        mThumbHeightInPixel = height;
+        mThumbWidthInPixel = width;
+        int heroIndex = getHeroIndex();
+        for (int i = 0; i < getChildCount(); i++) {
+            if (heroIndex != i) {
+                View child = getChildAt(i);
+                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+                boolean changed = false;
+                if (lp.height != height) {
+                    lp.height = height;
+                    changed = true;
+                }
+                if (lp.width != width) {
+                    lp.width = width;
+                    changed = true;
+                }
+                if (changed) {
+                    child.setLayoutParams(lp);
+                }
+            }
+        }
+    }
+
+    /**
+     * Set size of hero thumb view in pixels, it is usually larger than other thumbs.
+     */
+    public void setHeroThumbSize(int width, int height) {
+        mHeroThumbHeightInPixel = height;
+        mHeroThumbWidthInPixel = width;
+        int heroIndex = getHeroIndex();
+        for (int i = 0; i < getChildCount(); i++) {
+            if (heroIndex == i) {
+                View child = getChildAt(i);
+                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+                boolean changed = false;
+                if (lp.height != height) {
+                    lp.height = height;
+                    changed = true;
+                }
+                if (lp.width != width) {
+                    lp.width = width;
+                    changed = true;
+                }
+                if (changed) {
+                    child.setLayoutParams(lp);
+                }
+            }
+        }
+    }
+
+    /**
+     * Set the space between thumbs in pixels
+     */
+    public void setThumbSpace(int spaceInPixel) {
+        mMeasuredMarginInPixel = spaceInPixel;
+        requestLayout();
+    }
+
+    /**
+     * Set number of thumb views.
+     */
+    public void setNumberOfThumbs(int numOfThumbs) {
+        mIsUserSets = true;
+        mNumOfThumbs = numOfThumbs;
+        setNumberOfThumbsInternal();
+    }
+
+    /**
+     * Helper function for setNumberOfThumbs.
+     * Will Update the layout settings in ThumbsBar based on mNumOfThumbs
+     */
+    private void setNumberOfThumbsInternal() {
+        while (getChildCount() > mNumOfThumbs) {
+            removeView(getChildAt(getChildCount() - 1));
+        }
+        while (getChildCount() < mNumOfThumbs) {
+            View view = createThumbView(this);
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(mThumbWidthInPixel,
+                    mThumbHeightInPixel);
+            addView(view, lp);
+        }
+        int heroIndex = getHeroIndex();
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+            if (heroIndex == i) {
+                lp.width = mHeroThumbWidthInPixel;
+                lp.height = mHeroThumbHeightInPixel;
+            } else {
+                lp.width = mThumbWidthInPixel;
+                lp.height = mThumbHeightInPixel;
+            }
+            child.setLayoutParams(lp);
+        }
+    }
+
+    private static int roundUp(int num, int divisor) {
+        return (num + divisor - 1) / divisor;
+    }
+
+    /**
+     * Helper function to compute how many thumbs should be put in the screen
+     * Assume we should put x's non-hero thumbs in the screen, the equation should be
+     *   192dp (width of hero thumbs) +
+     *   154dp (width of common thumbs) * x +
+     *   4dp (width of the margin between thumbs) * x
+     *     = width
+     * So the calculated number of non-hero thumbs should be (width - 192dp) / 158dp.
+     * If the calculated number of non-hero thumbs is less than 2, it will be updated to 2
+     * or if the calculated number or non-hero thumbs is not an even number, it will be
+     * decremented by one.
+     * This processing is used to make sure the arrangement of non-hero thumbs
+     * in ThumbsBar is symmetrical.
+     * Also there should be a hero thumb in the middle of the ThumbsBar,
+     * the final result should be non-hero thumbs (after processing) + 1.
+     *
+     * @param  widthInPixel measured width in pixel
+     * @return The number of thumbs
+     */
+    private int calculateNumOfThumbs(int widthInPixel) {
+        int nonHeroThumbNum = roundUp(widthInPixel - mHeroThumbWidthInPixel,
+                mThumbWidthInPixel + mMeasuredMarginInPixel);
+        if (nonHeroThumbNum < 2) {
+            // If the calculated number of non-hero thumbs is less than 2,
+            // it will be updated to 2
+            nonHeroThumbNum = 2;
+        } else if ((nonHeroThumbNum & 1) != 0) {
+            // If the calculated number or non-hero thumbs is not an even number,
+            // it will be increased by one.
+            nonHeroThumbNum++;
+        }
+        // Count Hero Thumb to the final result
+        return nonHeroThumbNum + 1;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int width = getMeasuredWidth();
+        // If the number of thumbs in ThumbsBar is not set by user explicitly, it will be
+        // recalculated based on Android TV Design Spec
+        if (!mIsUserSets) {
+            int numOfThumbs = calculateNumOfThumbs(width);
+            // Set new number of thumbs when calculation result is different with current number
+            if (mNumOfThumbs != numOfThumbs) {
+                mNumOfThumbs = numOfThumbs;
+                setNumberOfThumbsInternal();
+            }
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        int heroIndex = getHeroIndex();
+        View heroView = getChildAt(heroIndex);
+        int heroLeft = getWidth() / 2 - heroView.getMeasuredWidth() / 2;
+        int heroRight = getWidth() / 2 + heroView.getMeasuredWidth() / 2;
+        heroView.layout(heroLeft, getPaddingTop(), heroRight,
+                getPaddingTop() + heroView.getMeasuredHeight());
+        int heroCenter = getPaddingTop() + heroView.getMeasuredHeight() / 2;
+
+        for (int i = heroIndex - 1; i >= 0; i--) {
+            heroLeft -= mMeasuredMarginInPixel;
+            View child = getChildAt(i);
+            child.layout(heroLeft - child.getMeasuredWidth(),
+                    heroCenter - child.getMeasuredHeight() / 2,
+                    heroLeft,
+                    heroCenter + child.getMeasuredHeight() / 2);
+            heroLeft -= child.getMeasuredWidth();
+        }
+        for (int i = heroIndex + 1; i < mNumOfThumbs; i++) {
+            heroRight += mMeasuredMarginInPixel;
+            View child = getChildAt(i);
+            child.layout(heroRight,
+                    heroCenter - child.getMeasuredHeight() / 2,
+                    heroRight + child.getMeasuredWidth(),
+                    heroCenter + child.getMeasuredHeight() / 2);
+            heroRight += child.getMeasuredWidth();
+        }
+    }
+
+    /**
+     * Create a thumb view, it's by default a ImageView.
+     */
+    protected View createThumbView(ViewGroup parent) {
+        return new ImageView(parent.getContext());
+    }
+
+    /**
+     * Clear all thumb bitmaps set on thumb views.
+     */
+    public void clearThumbBitmaps() {
+        for (int i = 0; i < getChildCount(); i++) {
+            setThumbBitmap(i, null);
+        }
+        mBitmaps.clear();
+    }
+
+
+    /**
+     * Get bitmap of given child index.
+     */
+    public Bitmap getThumbBitmap(int index) {
+        return mBitmaps.get(index);
+    }
+
+    /**
+     * Set thumb bitmap for a given index of child.
+     */
+    public void setThumbBitmap(int index, Bitmap bitmap) {
+        mBitmaps.put(index, bitmap);
+        ((ImageView) getChildAt(index)).setImageBitmap(bitmap);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
index f12d8d0..3aba59d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
@@ -46,7 +46,7 @@
                 return mTitleView;
             }
             final boolean isRtl = ViewCompat.getLayoutDirection(focused)
-                    == View.LAYOUT_DIRECTION_RTL;
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
             final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mTitleView.hasFocus() && (direction == View.FOCUS_DOWN || direction == forward)) {
                 return mSceneRoot;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
index edb3ab2..56b6ed1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
@@ -17,11 +17,10 @@
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.system.Settings;
 import android.support.v17.leanback.transition.TransitionHelper;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.util.Log;
 
 /**
  * A presenter that renders objects in a {@link VerticalGridView}.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Visibility.java b/v17/leanback/src/android/support/v17/leanback/widget/Visibility.java
new file mode 100644
index 0000000..b16a2f9
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Visibility.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.IntDef;
+import android.support.annotation.RestrictTo;
+import android.view.View;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+@RestrictTo(LIBRARY_GROUP)
+@IntDef({View.VISIBLE, View.INVISIBLE, View.GONE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Visibility {}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java b/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
index 9a7d424..3ddb6f0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
@@ -30,28 +30,30 @@
      */
     public static class Axis {
         /**
-         * mScrollCenter is used to calculate dynamic transformation based on how far a view
-         * is from the mScrollCenter. For example, the views with center close to mScrollCenter
-         * will be scaled up.
-         */
-        private float mScrollCenter;
-        /**
          * Right or bottom edge of last child.
          */
         private int mMaxEdge;
         /**
-         * Left or top edge of first child, typically should be zero.
+         * Left or top edge of first child
          */
         private int mMinEdge;
         /**
-         * Max Scroll value
+         * Scroll distance to align last child, it defines limit of scroll.
          */
         private int mMaxScroll;
         /**
-         * Min Scroll value
+         * Scroll distance to align first child, it defines limit of scroll.
          */
         private int mMinScroll;
 
+        static final int PF_KEYLINE_OVER_LOW_EDGE = 1;
+        static final int PF_KEYLINE_OVER_HIGH_EDGE = 1 << 1;
+
+        /**
+         * By default we prefer low edge over keyline, prefer keyline over high edge.
+         */
+        private int mPreferredKeyLine = PF_KEYLINE_OVER_HIGH_EDGE;
+
         private int mWindowAlignment = WINDOW_ALIGN_BOTH_EDGE;
 
         private int mWindowAlignmentOffset = 0;
@@ -60,9 +62,15 @@
 
         private int mSize;
 
-        private int mPaddingLow;
+        /**
+         * Padding at the min edge, it is the left or top padding.
+         */
+        private int mPaddingMin;
 
-        private int mPaddingHigh;
+        /**
+         * Padding at the max edge, it is the right or bottom padding.
+         */
+        private int mPaddingMax;
 
         private boolean mReversedFlow;
 
@@ -73,23 +81,43 @@
             mName = name;
         }
 
-        final public int getWindowAlignment() {
+        public final int getWindowAlignment() {
             return mWindowAlignment;
         }
 
-        final public void setWindowAlignment(int windowAlignment) {
+        public final void setWindowAlignment(int windowAlignment) {
             mWindowAlignment = windowAlignment;
         }
 
-        final public int getWindowAlignmentOffset() {
+        final void setPreferKeylineOverLowEdge(boolean keylineOverLowEdge) {
+            mPreferredKeyLine = keylineOverLowEdge
+                    ? mPreferredKeyLine | PF_KEYLINE_OVER_LOW_EDGE
+                    : mPreferredKeyLine & ~PF_KEYLINE_OVER_LOW_EDGE;
+        }
+
+        final void setPreferKeylineOverHighEdge(boolean keylineOverHighEdge) {
+            mPreferredKeyLine = keylineOverHighEdge
+                    ? mPreferredKeyLine | PF_KEYLINE_OVER_HIGH_EDGE
+                    : mPreferredKeyLine & ~PF_KEYLINE_OVER_HIGH_EDGE;
+        }
+
+        final boolean isPreferKeylineOverHighEdge() {
+            return (mPreferredKeyLine & PF_KEYLINE_OVER_HIGH_EDGE) != 0;
+        }
+
+        final boolean isPreferKeylineOverLowEdge() {
+            return (mPreferredKeyLine & PF_KEYLINE_OVER_LOW_EDGE) != 0;
+        }
+
+        public final int getWindowAlignmentOffset() {
             return mWindowAlignmentOffset;
         }
 
-        final public void setWindowAlignmentOffset(int offset) {
+        public final void setWindowAlignmentOffset(int offset) {
             mWindowAlignmentOffset = offset;
         }
 
-        final public void setWindowAlignmentOffsetPercent(float percent) {
+        public final void setWindowAlignmentOffsetPercent(float percent) {
             if ((percent < 0 || percent > 100)
                     && percent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
                 throw new IllegalArgumentException();
@@ -97,194 +125,255 @@
             mWindowAlignmentOffsetPercent = percent;
         }
 
-        final public float getWindowAlignmentOffsetPercent() {
+        public final float getWindowAlignmentOffsetPercent() {
             return mWindowAlignmentOffsetPercent;
         }
 
-        final public int getScrollCenter() {
-            return (int) mScrollCenter;
-        }
-
-        /** set minEdge,  Integer.MIN_VALUE means unknown*/
-        final public void setMinEdge(int minEdge) {
-            mMinEdge = minEdge;
-        }
-
-        final public int getMinEdge() {
-            return mMinEdge;
-        }
-
-        /** set minScroll,  Integer.MIN_VALUE means unknown*/
-        final public void setMinScroll(int minScroll) {
-            mMinScroll = minScroll;
-        }
-
-        final public int getMinScroll() {
+        /**
+         * Returns scroll distance to align min child.
+         */
+        public final int getMinScroll() {
             return mMinScroll;
         }
 
-        final public void invalidateScrollMin() {
+        public final void invalidateScrollMin() {
             mMinEdge = Integer.MIN_VALUE;
             mMinScroll = Integer.MIN_VALUE;
         }
 
-        /** update max edge,  Integer.MAX_VALUE means unknown*/
-        final public void setMaxEdge(int maxEdge) {
-            mMaxEdge = maxEdge;
-        }
-
-        final public int getMaxEdge() {
-            return mMaxEdge;
-        }
-
-        /** update max scroll,  Integer.MAX_VALUE means unknown*/
-        final public void setMaxScroll(int maxScroll) {
-            mMaxScroll = maxScroll;
-        }
-
-        final public int getMaxScroll() {
+        /**
+         * Returns scroll distance to align max child.
+         */
+        public final int getMaxScroll() {
             return mMaxScroll;
         }
 
-        final public void invalidateScrollMax() {
+        public final void invalidateScrollMax() {
             mMaxEdge = Integer.MAX_VALUE;
             mMaxScroll = Integer.MAX_VALUE;
         }
 
-        final public float updateScrollCenter(float scrollTarget) {
-            mScrollCenter = scrollTarget;
-            return scrollTarget;
-        }
-
         void reset() {
-            mScrollCenter = Integer.MIN_VALUE;
             mMinEdge = Integer.MIN_VALUE;
             mMaxEdge = Integer.MAX_VALUE;
         }
 
-        final public boolean isMinUnknown() {
+        public final boolean isMinUnknown() {
             return mMinEdge == Integer.MIN_VALUE;
         }
 
-        final public boolean isMaxUnknown() {
+        public final boolean isMaxUnknown() {
             return mMaxEdge == Integer.MAX_VALUE;
         }
 
-        final public void setSize(int size) {
+        public final void setSize(int size) {
             mSize = size;
         }
 
-        final public int getSize() {
+        public final int getSize() {
             return mSize;
         }
 
-        final public void setPadding(int paddingLow, int paddingHigh) {
-            mPaddingLow = paddingLow;
-            mPaddingHigh = paddingHigh;
+        public final void setPadding(int paddingMin, int paddingMax) {
+            mPaddingMin = paddingMin;
+            mPaddingMax = paddingMax;
         }
 
-        final public int getPaddingLow() {
-            return mPaddingLow;
+        public final int getPaddingMin() {
+            return mPaddingMin;
         }
 
-        final public int getPaddingHigh() {
-            return mPaddingHigh;
+        public final int getPaddingMax() {
+            return mPaddingMax;
         }
 
-        final public int getClientSize() {
-            return mSize - mPaddingLow - mPaddingHigh;
+        public final int getClientSize() {
+            return mSize - mPaddingMin - mPaddingMax;
         }
 
-        final public int getSystemScrollPos(boolean isAtMin, boolean isAtMax) {
-            return getSystemScrollPos((int) mScrollCenter, isAtMin, isAtMax);
-        }
-
-        final public int getSystemScrollPos(int scrollCenter, boolean isAtMin, boolean isAtMax) {
-            int middlePosition;
+        final int calculateKeyline() {
+            int keyLine;
             if (!mReversedFlow) {
                 if (mWindowAlignmentOffset >= 0) {
-                    middlePosition = mWindowAlignmentOffset - mPaddingLow;
+                    keyLine = mWindowAlignmentOffset;
                 } else {
-                    middlePosition = mSize + mWindowAlignmentOffset - mPaddingLow;
+                    keyLine = mSize + mWindowAlignmentOffset;
                 }
                 if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
-                    middlePosition += (int) (mSize * mWindowAlignmentOffsetPercent / 100);
+                    keyLine += (int) (mSize * mWindowAlignmentOffsetPercent / 100);
                 }
             } else {
                 if (mWindowAlignmentOffset >= 0) {
-                    middlePosition = mSize - mWindowAlignmentOffset - mPaddingLow;
+                    keyLine = mSize - mWindowAlignmentOffset;
                 } else {
-                    middlePosition = - mWindowAlignmentOffset - mPaddingLow;
+                    keyLine = -mWindowAlignmentOffset;
                 }
                 if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
-                    middlePosition -= (int) (mSize * mWindowAlignmentOffsetPercent / 100);
+                    keyLine -= (int) (mSize * mWindowAlignmentOffsetPercent / 100);
                 }
             }
-            int clientSize = getClientSize();
-            int afterMiddlePosition = clientSize - middlePosition;
-            boolean isMinUnknown = isMinUnknown();
-            boolean isMaxUnknown = isMaxUnknown();
-            if (!isMinUnknown && !isMaxUnknown
-                    && (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) {
-                if (mMaxEdge - mMinEdge <= clientSize) {
-                    // total children size is less than view port and we want to align
-                    // both edge:  align first child to start edge of view port
-                    return mReversedFlow ? mMaxEdge - mPaddingLow - clientSize
-                            : mMinEdge - mPaddingLow;
-                }
-            }
+            return keyLine;
+        }
+
+        /**
+         * Returns scroll distance to move viewCenterPosition to keyLine.
+         */
+        final int calculateScrollToKeyLine(int viewCenterPosition, int keyLine) {
+            return viewCenterPosition - keyLine;
+        }
+
+        /**
+         * Update {@link #getMinScroll()} and {@link #getMaxScroll()}
+         */
+        public final void updateMinMax(int minEdge, int maxEdge,
+                int minChildViewCenter, int maxChildViewCenter) {
+            mMinEdge = minEdge;
+            mMaxEdge = maxEdge;
+            final int clientSize = getClientSize();
+            final int keyLine = calculateKeyline();
+            final boolean isMinUnknown = isMinUnknown();
+            final boolean isMaxUnknown = isMaxUnknown();
             if (!isMinUnknown) {
-                if ((!mReversedFlow ? (mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0
-                     : (mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0)
-                        && (isAtMin || scrollCenter - mMinEdge <= middlePosition)) {
-                    // scroll center is within half of view port size: align the start edge
-                    // of first child to the start edge of view port
-                    return mMinEdge - mPaddingLow;
+                if (!mReversedFlow ? (mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0
+                        : (mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0) {
+                    // calculate scroll distance to move current mMinEdge to padding at min edge
+                    mMinScroll = mMinEdge - mPaddingMin;
+                } else  {
+                    // calculate scroll distance to move min child center to key line
+                    mMinScroll = calculateScrollToKeyLine(minChildViewCenter, keyLine);
                 }
             }
             if (!isMaxUnknown) {
-                if ((!mReversedFlow ? (mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0
-                        : (mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0)
-                        && (isAtMax || mMaxEdge - scrollCenter <= afterMiddlePosition)) {
-                    // scroll center is very close to the end edge of view port : align the
-                    // end edge of last children (plus expanded size) to view port's end
-                    return mMaxEdge - mPaddingLow - clientSize;
+                if (!mReversedFlow ? (mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0
+                        : (mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0) {
+                    // calculate scroll distance to move current mMaxEdge to padding at max edge
+                    mMaxScroll = mMaxEdge - mPaddingMin - clientSize;
+                } else  {
+                    // calculate scroll distance to move max child center to key line
+                    mMaxScroll = calculateScrollToKeyLine(maxChildViewCenter, keyLine);
                 }
             }
-            // else put scroll center in middle of view port
-            return scrollCenter - middlePosition - mPaddingLow;
+            if (!isMaxUnknown && !isMinUnknown) {
+                if (!mReversedFlow) {
+                    if ((mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0) {
+                        if (isPreferKeylineOverLowEdge()) {
+                            // if we prefer key line, might align max child to key line for
+                            // minScroll
+                            mMinScroll = Math.min(mMinScroll,
+                                    calculateScrollToKeyLine(maxChildViewCenter, keyLine));
+                        } else {
+                            // don't over scroll max
+                            mMaxScroll = Math.max(mMinScroll, mMaxScroll);
+                        }
+                    } else if ((mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0) {
+                        if (isPreferKeylineOverHighEdge()) {
+                            // if we prefer key line, might align min child to key line for
+                            // maxScroll
+                            mMaxScroll = Math.max(mMaxScroll,
+                                    calculateScrollToKeyLine(minChildViewCenter, keyLine));
+                        } else {
+                            // don't over scroll min
+                            mMinScroll = Math.min(mMinScroll, mMaxScroll);
+                        }
+                    }
+                } else {
+                    if ((mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0) {
+                        if (isPreferKeylineOverLowEdge()) {
+                            // if we prefer key line, might align min child to key line for
+                            // maxScroll
+                            mMaxScroll = Math.max(mMaxScroll,
+                                    calculateScrollToKeyLine(minChildViewCenter, keyLine));
+                        } else {
+                            // don't over scroll min
+                            mMinScroll = Math.min(mMinScroll, mMaxScroll);
+                        }
+                    } else if ((mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0) {
+                        if (isPreferKeylineOverHighEdge()) {
+                            // if we prefer key line, might align max child to key line for
+                            // minScroll
+                            mMinScroll = Math.min(mMinScroll,
+                                    calculateScrollToKeyLine(maxChildViewCenter, keyLine));
+                        } else {
+                            // don't over scroll max
+                            mMaxScroll = Math.max(mMinScroll, mMaxScroll);
+                        }
+                    }
+                }
+            }
         }
 
-        final public void setReversedFlow(boolean reversedFlow) {
+        /**
+         * Get scroll distance of align an item (depends on ALIGN_LOW_EDGE, ALIGN_HIGH_EDGE or the
+         * item should be aligned to key line). The scroll distance will be capped by
+         * {@link #getMinScroll()} and {@link #getMaxScroll()}.
+         */
+        public final int getScroll(int viewCenter) {
+            final int size = getSize();
+            final int keyLine = calculateKeyline();
+            final boolean isMinUnknown = isMinUnknown();
+            final boolean isMaxUnknown = isMaxUnknown();
+            if (!isMinUnknown) {
+                final int keyLineToMinEdge = keyLine - mPaddingMin;
+                if ((!mReversedFlow ? (mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0
+                     : (mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0)
+                        && (viewCenter - mMinEdge <= keyLineToMinEdge)) {
+                    // view center is before key line: align the min edge (first child) to padding.
+                    int alignToMin = mMinEdge - mPaddingMin;
+                    // Also we need make sure don't over scroll
+                    if (!isMaxUnknown && alignToMin > mMaxScroll) {
+                        alignToMin = mMaxScroll;
+                    }
+                    return alignToMin;
+                }
+            }
+            if (!isMaxUnknown) {
+                final int keyLineToMaxEdge = size - keyLine - mPaddingMax;
+                if ((!mReversedFlow ? (mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0
+                        : (mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0)
+                        && (mMaxEdge - viewCenter <= keyLineToMaxEdge)) {
+                    // view center is after key line: align the max edge (last child) to padding.
+                    int alignToMax = mMaxEdge - (size - mPaddingMax);
+                    // Also we need make sure don't over scroll
+                    if (!isMinUnknown && alignToMax < mMinScroll) {
+                        alignToMax = mMinScroll;
+                    }
+                    return alignToMax;
+                }
+            }
+            // else put view center at key line.
+            return calculateScrollToKeyLine(viewCenter, keyLine);
+        }
+
+        public final void setReversedFlow(boolean reversedFlow) {
             mReversedFlow = reversedFlow;
         }
 
         @Override
         public String toString() {
-            return "center: " + mScrollCenter + " min:" + mMinEdge + " max:" + mMaxEdge;
+            return " min:" + mMinEdge + " " + mMinScroll + " max:" + mMaxEdge + " " + mMaxScroll;
         }
 
     }
 
     private int mOrientation = HORIZONTAL;
 
-    final public Axis vertical = new Axis("vertical");
+    public final Axis vertical = new Axis("vertical");
 
-    final public Axis horizontal = new Axis("horizontal");
+    public final Axis horizontal = new Axis("horizontal");
 
     private Axis mMainAxis = horizontal;
 
     private Axis mSecondAxis = vertical;
 
-    final public Axis mainAxis() {
+    public final Axis mainAxis() {
         return mMainAxis;
     }
 
-    final public Axis secondAxis() {
+    public final Axis secondAxis() {
         return mSecondAxis;
     }
 
-    final public void setOrientation(int orientation) {
+    public final void setOrientation(int orientation) {
         mOrientation = orientation;
         if (mOrientation == HORIZONTAL) {
             mMainAxis = horizontal;
@@ -295,11 +384,11 @@
         }
     }
 
-    final public int getOrientation() {
+    public final int getOrientation() {
         return mOrientation;
     }
 
-    final public void reset() {
+    public final void reset() {
         mainAxis().reset();
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/picker/TimePicker.java b/v17/leanback/src/android/support/v17/leanback/widget/picker/TimePicker.java
index 90acddf..29059ba 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/picker/TimePicker.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/picker/TimePicker.java
@@ -106,7 +106,7 @@
                 context.getResources());
 
         setSeparator(mConstant.timeSeparator);
-        mPickerView = (ViewGroup) findViewById(R.id.picker);
+        mPickerView = findViewById(R.id.picker);
         final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
                 R.styleable.lbTimePicker);
         mIs24hFormat = attributesArray.getBoolean(R.styleable.lbTimePicker_is24HourFormat,
diff --git a/v17/leanback/tests/AndroidManifest.xml b/v17/leanback/tests/AndroidManifest.xml
index c2c24d0..b21246e 100644
--- a/v17/leanback/tests/AndroidManifest.xml
+++ b/v17/leanback/tests/AndroidManifest.xml
@@ -15,64 +15,54 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.v17.leanback.test">
-    <uses-sdk android:minSdkVersion="17"  android:targetSdkVersion="23"/>
-
-    <!--
-        This declares that this application uses the instrumentation test runner targeting
-        the package of android.support.v17.leanback.test. To run the tests use the command:
-        "adb shell am instrument -w android.support.v17.leanback.test/android.test.InstrumentationTestRunner"
-    -->
-    <instrumentation
-            android:targetPackage="android.support.v17.leanback.test"
-            android:name="android.test.InstrumentationTestRunner" />
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application
-            android:supportsRtl="true">
-        <uses-library android:name="android.test.runner" />
-
+        android:supportsRtl="true">
         <activity android:name="android.support.v17.leanback.widget.GridActivity"
-                  android:exported="true" />
+                  android:exported="true"/>
 
         <activity android:name="android.support.v17.leanback.widget.TimePickerActivity"
-                  android:theme="@style/Theme.Leanback"
-                  android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback"/>
 
         <activity android:name="android.support.v17.leanback.widget.DatePickerActivity"
-                  android:theme="@style/Theme.Leanback"
-                  android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback"/>
 
         <activity
-                android:name="android.support.v17.leanback.app.wizard.GuidedStepAttributesTestActivity"
-                android:theme="@style/Theme.Leanback.GuidedStep"
-                android:exported="true" />
+            android:name="android.support.v17.leanback.app.wizard.GuidedStepAttributesTestActivity"
+            android:exported="true"
+            android:theme="@style/Theme.Leanback.GuidedStep"/>
 
         <activity android:name="android.support.v17.leanback.app.SingleFragmentTestActivity"
-            android:theme="@style/Theme.Leanback"
-            android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback"/>
 
         <activity android:name="android.support.v17.leanback.app.BrowseFragmentTestActivity"
-                  android:theme="@style/Theme.Leanback.Browse"
-                  android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback.Browse"/>
 
         <activity android:name="android.support.v17.leanback.app.SingleSupportFragmentTestActivity"
-            android:theme="@style/Theme.Leanback"
-            android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback"/>
 
         <activity android:name="android.support.v17.leanback.app.BrowseSupportFragmentTestActivity"
-                  android:theme="@style/Theme.Leanback.Browse"
-                  android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback.Browse"/>
 
         <activity android:name="android.support.v17.leanback.app.GuidedStepFragmentTestActivity"
-                  android:theme="@style/Theme.Leanback.GuidedStep"
-                  android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback.GuidedStep"/>
 
-        <activity android:name="android.support.v17.leanback.app.GuidedStepSupportFragmentTestActivity"
-                  android:theme="@style/Theme.Leanback.GuidedStep"
-                  android:exported="true" />
+        <activity
+            android:name="android.support.v17.leanback.app.GuidedStepSupportFragmentTestActivity"
+            android:exported="true"
+            android:theme="@style/Theme.Leanback.GuidedStep"/>
 
         <activity android:name="android.support.v17.leanback.app.TestActivity"
-            android:theme="@style/Theme.Leanback"
-            android:exported="true" />
+                  android:exported="true"
+                  android:theme="@style/Theme.Leanback"/>
     </application>
 
 </manifest>
diff --git a/v17/leanback/tests/README.txt b/v17/leanback/tests/README.txt
deleted file mode 100644
index cf64349..0000000
--- a/v17/leanback/tests/README.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Test project for support leanback.
-
-RUN TESTS
-Using gradle
-1. cd frameworks/support
-2. ./gradlew support-leanback-v17:connectedCheck --info
-
-Using adb
-adb shell am instrument -w android.support.v17.leanback.tests/android.test.InstrumentationTestRunner
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java
index 3ccfbf7..634acde 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java
@@ -29,7 +29,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.testutils.PollingCheck;
 
@@ -38,10 +38,7 @@
 import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 
-/**
- * @hide from javadoc
- */
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class BackgroundManagerTest {
 
@@ -658,4 +655,48 @@
         waitForBackgroundAnimationFinish(manager1);
         assertIsBitmapDrawable(manager1, bitmap);
     }
+
+    @Test
+    public void delayDrawableChangeUntilFullAlpha() throws Throwable {
+        final Bitmap bitmap = createBitmap(200, 100, Color.BLUE);
+        TestActivity.Provider provider1 = new TestActivity.Provider() {
+            @Override
+            public void onCreate(TestActivity activity, Bundle savedInstanceState) {
+                super.onCreate(activity, savedInstanceState);
+                BackgroundManager m = BackgroundManager.getInstance(activity);
+                m.setAutoReleaseOnStop(false);
+                m.attach(activity.getWindow());
+                m.setColor(Color.RED);
+            }
+
+        };
+        mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+        final TestActivity activity1 = mRule.launchActivity();
+        final BackgroundManager manager1 = BackgroundManager.getInstance(activity1);
+        assertIsColorDrawable(manager1, Color.RED);
+
+        // when set drawable, the change will be pending because alpha is 128
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertSame(manager1.mLayerDrawable,
+                        activity1.getWindow().getDecorView().getBackground());
+                activity1.getWindow().getDecorView().getBackground().setAlpha(128);
+                manager1.setBitmap(bitmap);
+            }
+        });
+        assertEquals(Color.RED,
+                ((ColorDrawable) manager1.mLayerDrawable.getDrawable(1)).getColor());
+
+        // Pending updates executed when set to FULL_ALPHA
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity1.getWindow().getDecorView().getBackground().setAlpha(
+                        BackgroundManager.FULL_ALPHA);
+            }
+        });
+        waitForBackgroundAnimationFinish(manager1);
+        assertIsBitmapDrawable(manager1, bitmap);
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
index 0008f93..a1c986e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
@@ -23,10 +23,12 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
@@ -45,8 +47,7 @@
 public class BrowseFragmentTest {
 
     static final String TAG = "BrowseFragmentTest";
-    static final long TRANSITION_LENGTH = 1000;
-    static final long HORIZONTAL_SCROLL_WAIT = 2000;
+    static final long WAIT_TRANSIITON_TIMEOUT = 10000;
 
     @Rule
     public ActivityTestRule<BrowseFragmentTestActivity> activityTestRule =
@@ -56,6 +57,7 @@
     @After
     public void afterTest() throws Throwable {
         activityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 if (mActivity != null) {
                     mActivity.finish();
@@ -65,6 +67,28 @@
         });
     }
 
+    void waitForEntranceTransitionFinished() {
+        PollingCheck.waitFor(WAIT_TRANSIITON_TIMEOUT, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                if (Build.VERSION.SDK_INT >= 21) {
+                    return mActivity.getBrowseTestFragment() != null
+                            && mActivity.getBrowseTestFragment().mEntranceTransitionEnded;
+                } else {
+                    // when entrance transition not supported, wait main fragment loaded.
+                    return mActivity.getBrowseTestFragment() != null
+                            && mActivity.getBrowseTestFragment().getMainFragment() != null;
+                }
+            }
+        });
+    }
+
+    void waitForHeaderTransitionFinished() {
+        View row = mActivity.getBrowseTestFragment().getRowsFragment().getRowViewHolder(
+                mActivity.getBrowseTestFragment().getSelectedPosition()).view;
+        PollingCheck.waitFor(WAIT_TRANSIITON_TIMEOUT, new PollingCheck.ViewStableOnScreen(row));
+    }
+
     @Test
     public void testTwoBackKeysWithBackStack() throws Throwable {
         final long dataLoadingDelay = 1000;
@@ -73,11 +97,11 @@
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         assertNotNull(mActivity.getBrowseTestFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
-        Thread.sleep(TRANSITION_LENGTH);
+        waitForHeaderTransitionFinished();
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
     }
 
@@ -89,11 +113,11 @@
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         assertNotNull(mActivity.getBrowseTestFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
-        Thread.sleep(TRANSITION_LENGTH);
+        waitForHeaderTransitionFinished();
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
     }
 
@@ -119,7 +143,7 @@
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         Presenter.ViewHolderTask itemTask = Mockito.spy(
                 new ItemSelectionTask(mActivity, selectRow));
@@ -158,7 +182,7 @@
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_SET_ADAPTER_AFTER_DATA_LOAD, true);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         InstrumentationRegistry.getInstrumentation().callActivityOnRestart(mActivity);
         activityTestRule.runOnUiThread(new Runnable() {
@@ -169,6 +193,25 @@
         });
     }
 
+
+    @Test
+    public void lateLoadingHeaderDisabled() throws Throwable {
+        final long dataLoadingDelay = 1000;
+        Intent intent = new Intent();
+        intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
+        intent.putExtra(BrowseFragmentTestActivity.EXTRA_HEADERS_STATE,
+                BrowseFragment.HEADERS_DISABLED);
+        mActivity = activityTestRule.launchActivity(intent);
+        waitForEntranceTransitionFinished();
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mActivity.getBrowseTestFragment().getGridView() != null
+                        && mActivity.getBrowseTestFragment().getGridView().getChildCount() > 0;
+            }
+        });
+    }
+
     private void sendKeys(int ...keys) {
         for (int i = 0; i < keys.length; i++) {
             InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]);
@@ -185,6 +228,7 @@
             this.expectedRow = expectedRow;
         }
 
+        @Override
         public void run(Presenter.ViewHolder holder) {
             android.util.Log.d(TAG, dumpRecyclerView(activity.getBrowseTestFragment()
                     .getGridView()));
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java
index e01a168..605a9ca 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java
@@ -17,7 +17,6 @@
 
 import android.app.Activity;
 import android.app.FragmentTransaction;
-
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.v17.leanback.test.R;
@@ -29,7 +28,8 @@
     public static final String EXTRA_REPEAT_PER_ROW = "repeatPerRow";
     public static final String EXTRA_LOAD_DATA_DELAY = "loadDataDelay";
     public static final String EXTRA_TEST_ENTRANCE_TRANSITION = "testEntranceTransition";
-    public final static String EXTRA_SET_ADAPTER_AFTER_DATA_LOAD = "set_adapter_after_data_load";
+    public static final String EXTRA_SET_ADAPTER_AFTER_DATA_LOAD = "set_adapter_after_data_load";
+    public static final String EXTRA_HEADERS_STATE = "headers_state";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
index c7862c9..4c86817 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
@@ -26,10 +26,12 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
@@ -48,8 +50,7 @@
 public class BrowseSupportFragmentTest {
 
     static final String TAG = "BrowseSupportFragmentTest";
-    static final long TRANSITION_LENGTH = 1000;
-    static final long HORIZONTAL_SCROLL_WAIT = 2000;
+    static final long WAIT_TRANSIITON_TIMEOUT = 10000;
 
     @Rule
     public ActivityTestRule<BrowseSupportFragmentTestActivity> activityTestRule =
@@ -59,6 +60,7 @@
     @After
     public void afterTest() throws Throwable {
         activityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 if (mActivity != null) {
                     mActivity.finish();
@@ -68,6 +70,28 @@
         });
     }
 
+    void waitForEntranceTransitionFinished() {
+        PollingCheck.waitFor(WAIT_TRANSIITON_TIMEOUT, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                if (Build.VERSION.SDK_INT >= 21) {
+                    return mActivity.getBrowseTestSupportFragment() != null
+                            && mActivity.getBrowseTestSupportFragment().mEntranceTransitionEnded;
+                } else {
+                    // when entrance transition not supported, wait main fragment loaded.
+                    return mActivity.getBrowseTestSupportFragment() != null
+                            && mActivity.getBrowseTestSupportFragment().getMainFragment() != null;
+                }
+            }
+        });
+    }
+
+    void waitForHeaderTransitionFinished() {
+        View row = mActivity.getBrowseTestSupportFragment().getRowsSupportFragment().getRowViewHolder(
+                mActivity.getBrowseTestSupportFragment().getSelectedPosition()).view;
+        PollingCheck.waitFor(WAIT_TRANSIITON_TIMEOUT, new PollingCheck.ViewStableOnScreen(row));
+    }
+
     @Test
     public void testTwoBackKeysWithBackStack() throws Throwable {
         final long dataLoadingDelay = 1000;
@@ -76,11 +100,11 @@
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         assertNotNull(mActivity.getBrowseTestSupportFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
-        Thread.sleep(TRANSITION_LENGTH);
+        waitForHeaderTransitionFinished();
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
     }
 
@@ -92,11 +116,11 @@
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         assertNotNull(mActivity.getBrowseTestSupportFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
-        Thread.sleep(TRANSITION_LENGTH);
+        waitForHeaderTransitionFinished();
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
     }
 
@@ -122,7 +146,7 @@
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         Presenter.ViewHolderTask itemTask = Mockito.spy(
                 new ItemSelectionTask(mActivity, selectRow));
@@ -161,7 +185,7 @@
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_SET_ADAPTER_AFTER_DATA_LOAD, true);
         mActivity = activityTestRule.launchActivity(intent);
 
-        Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
+        waitForEntranceTransitionFinished();
 
         InstrumentationRegistry.getInstrumentation().callActivityOnRestart(mActivity);
         activityTestRule.runOnUiThread(new Runnable() {
@@ -172,6 +196,25 @@
         });
     }
 
+
+    @Test
+    public void lateLoadingHeaderDisabled() throws Throwable {
+        final long dataLoadingDelay = 1000;
+        Intent intent = new Intent();
+        intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
+        intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_HEADERS_STATE,
+                BrowseSupportFragment.HEADERS_DISABLED);
+        mActivity = activityTestRule.launchActivity(intent);
+        waitForEntranceTransitionFinished();
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mActivity.getBrowseTestSupportFragment().getGridView() != null
+                        && mActivity.getBrowseTestSupportFragment().getGridView().getChildCount() > 0;
+            }
+        });
+    }
+
     private void sendKeys(int ...keys) {
         for (int i = 0; i < keys.length; i++) {
             InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]);
@@ -188,6 +231,7 @@
             this.expectedRow = expectedRow;
         }
 
+        @Override
         public void run(Presenter.ViewHolder holder) {
             android.util.Log.d(TAG, dumpRecyclerView(activity.getBrowseTestSupportFragment()
                     .getGridView()));
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java
index b24d466..9df846f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java
@@ -20,7 +20,6 @@
 
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentTransaction;
-
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.v17.leanback.test.R;
@@ -32,7 +31,8 @@
     public static final String EXTRA_REPEAT_PER_ROW = "repeatPerRow";
     public static final String EXTRA_LOAD_DATA_DELAY = "loadDataDelay";
     public static final String EXTRA_TEST_ENTRANCE_TRANSITION = "testEntranceTransition";
-    public final static String EXTRA_SET_ADAPTER_AFTER_DATA_LOAD = "set_adapter_after_data_load";
+    public static final String EXTRA_SET_ADAPTER_AFTER_DATA_LOAD = "set_adapter_after_data_load";
+    public static final String EXTRA_HEADERS_STATE = "headers_state";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
index 094fdc3..4fe79f0 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
@@ -13,6 +13,7 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_HEADERS_STATE;
 import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY;
 import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_NUM_ROWS;
 import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_REPEAT_PER_ROW;
@@ -51,6 +52,8 @@
 
     int NUM_ROWS;
     int REPEAT_PER_ROW;
+    boolean mEntranceTransitionStarted;
+    boolean mEntranceTransitionEnded;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -75,7 +78,7 @@
         }
 
         setTitle("BrowseTestFragment");
-        setHeadersState(HEADERS_ENABLED);
+        setHeadersState(arguments.getInt(EXTRA_HEADERS_STATE, HEADERS_ENABLED));
 
         setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -124,6 +127,18 @@
         setAdapter(mRowsAdapter);
     }
 
+    @Override
+    protected void onEntranceTransitionStart() {
+        super.onEntranceTransitionStart();
+        mEntranceTransitionStarted = true;
+    }
+
+    @Override
+    protected void onEntranceTransitionEnd() {
+        super.onEntranceTransitionEnd();
+        mEntranceTransitionEnded = true;
+    }
+
     private void loadData() {
         for (int i = 0; i < NUM_ROWS; ++i) {
             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
index 95dfc09..2acc530 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
@@ -16,6 +16,7 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_HEADERS_STATE;
 import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY;
 import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_NUM_ROWS;
 import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_REPEAT_PER_ROW;
@@ -54,6 +55,8 @@
 
     int NUM_ROWS;
     int REPEAT_PER_ROW;
+    boolean mEntranceTransitionStarted;
+    boolean mEntranceTransitionEnded;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -78,7 +81,7 @@
         }
 
         setTitle("BrowseTestSupportFragment");
-        setHeadersState(HEADERS_ENABLED);
+        setHeadersState(arguments.getInt(EXTRA_HEADERS_STATE, HEADERS_ENABLED));
 
         setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -127,6 +130,18 @@
         setAdapter(mRowsAdapter);
     }
 
+    @Override
+    protected void onEntranceTransitionStart() {
+        super.onEntranceTransitionStart();
+        mEntranceTransitionStarted = true;
+    }
+
+    @Override
+    protected void onEntranceTransitionEnd() {
+        super.onEntranceTransitionEnd();
+        mEntranceTransitionEnded = true;
+    }
+
     private void loadData() {
         for (int i = 0; i < NUM_ROWS; ++i) {
             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
index 913edac..6787491 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
@@ -37,6 +37,7 @@
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
 import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.util.StateMachine;
@@ -559,14 +560,6 @@
         final DetailsFragmentWithNoVideo detailsFragment =
                 (DetailsFragmentWithNoVideo) activity.getTestFragment();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
-                        android.support.v17.leanback.test.R.drawable.spiderman));
-            }
-        });
-
         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
             @Override
             public boolean canProceed() {
@@ -665,6 +658,79 @@
     }
 
     @Test
+    public void sharedGlueHost() {
+        final SingleFragmentTestActivity activity =
+                launchAndWaitActivity(DetailsFragmentWithNoVideo.class, new Options().uiVisibility(
+                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
+        final DetailsFragmentWithNoVideo detailsFragment =
+                (DetailsFragmentWithNoVideo) activity.getTestFragment();
+
+        SystemClock.sleep(1000);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        final MediaPlayerGlue glue1 = new MediaPlayerGlue(activity);
+                        detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue1);
+                        glue1.setArtist("A Googleer");
+                        glue1.setTitle("Diving with Sharks");
+                        glue1.setMediaSource(Uri.parse(
+                                "android.resource://android.support.v17.leanback.test/raw/video"));
+                    }
+                }
+        );
+
+        // after setup Video Playback the DPAD up will navigate to Video Fragment.
+        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return detailsFragment.mVideoFragment != null
+                        && detailsFragment.mVideoFragment.getView() != null;
+            }
+        });
+
+        final MediaPlayerGlue glue1 = (MediaPlayerGlue) detailsFragment
+                .mDetailsBackgroundController
+                .getPlaybackGlue();
+        PlaybackGlueHost playbackGlueHost = glue1.getHost();
+
+        // wait a little bit to replace with new Glue
+        SystemClock.sleep(1000);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        final MediaPlayerGlue glue2 = new MediaPlayerGlue(activity);
+                        detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue2);
+                        glue2.setArtist("A Googleer");
+                        glue2.setTitle("Diving with Sharks");
+                        glue2.setMediaSource(Uri.parse(
+                                "android.resource://android.support.v17.leanback.test/raw/video"));
+                    }
+                }
+        );
+
+        // wait for new glue to get its glue host
+        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                MediaPlayerGlue mediaPlayerGlue = (MediaPlayerGlue) detailsFragment
+                        .mDetailsBackgroundController
+                        .getPlaybackGlue();
+                return mediaPlayerGlue != null && mediaPlayerGlue != glue1
+                        && mediaPlayerGlue.getHost() != null;
+            }
+        });
+
+        final MediaPlayerGlue glue2 = (MediaPlayerGlue) detailsFragment
+                .mDetailsBackgroundController
+                .getPlaybackGlue();
+
+        assertTrue(glue1.getHost() == null);
+        assertTrue(glue2.getHost() == playbackGlueHost);
+    }
+
+    @Test
     public void clearVideo() {
         final SingleFragmentTestActivity activity =
                 launchAndWaitActivity(DetailsFragmentWithNoVideo.class, new Options().uiVisibility(
@@ -672,14 +738,6 @@
         final DetailsFragmentWithNoVideo detailsFragment =
                 (DetailsFragmentWithNoVideo) activity.getTestFragment();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
-                        android.support.v17.leanback.test.R.drawable.spiderman));
-            }
-        });
-
         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
             @Override
             public boolean canProceed() {
@@ -724,7 +782,7 @@
             }
         });
 
-        // wait a little bit then clear glue
+        // wait a little bit then reset glue
         SystemClock.sleep(1000);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 new Runnable() {
@@ -734,7 +792,7 @@
                     }
                 }
         );
-        // background should fade in upon clear playback
+        // background should fade in upon reset playback
         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
             @Override
             public boolean canProceed() {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java
index 39ab734..d4a41e0 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java
@@ -40,6 +40,7 @@
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
 import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.util.StateMachine;
@@ -562,14 +563,6 @@
         final DetailsSupportFragmentWithNoVideo detailsFragment =
                 (DetailsSupportFragmentWithNoVideo) activity.getTestFragment();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
-                        android.support.v17.leanback.test.R.drawable.spiderman));
-            }
-        });
-
         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
             @Override
             public boolean canProceed() {
@@ -668,6 +661,79 @@
     }
 
     @Test
+    public void sharedGlueHost() {
+        final SingleSupportFragmentTestActivity activity =
+                launchAndWaitActivity(DetailsSupportFragmentWithNoVideo.class, new Options().uiVisibility(
+                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
+        final DetailsSupportFragmentWithNoVideo detailsFragment =
+                (DetailsSupportFragmentWithNoVideo) activity.getTestFragment();
+
+        SystemClock.sleep(1000);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        final MediaPlayerGlue glue1 = new MediaPlayerGlue(activity);
+                        detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue1);
+                        glue1.setArtist("A Googleer");
+                        glue1.setTitle("Diving with Sharks");
+                        glue1.setMediaSource(Uri.parse(
+                                "android.resource://android.support.v17.leanback.test/raw/video"));
+                    }
+                }
+        );
+
+        // after setup Video Playback the DPAD up will navigate to Video Fragment.
+        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return detailsFragment.mVideoSupportFragment != null
+                        && detailsFragment.mVideoSupportFragment.getView() != null;
+            }
+        });
+
+        final MediaPlayerGlue glue1 = (MediaPlayerGlue) detailsFragment
+                .mDetailsBackgroundController
+                .getPlaybackGlue();
+        PlaybackGlueHost playbackGlueHost = glue1.getHost();
+
+        // wait a little bit to replace with new Glue
+        SystemClock.sleep(1000);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        final MediaPlayerGlue glue2 = new MediaPlayerGlue(activity);
+                        detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue2);
+                        glue2.setArtist("A Googleer");
+                        glue2.setTitle("Diving with Sharks");
+                        glue2.setMediaSource(Uri.parse(
+                                "android.resource://android.support.v17.leanback.test/raw/video"));
+                    }
+                }
+        );
+
+        // wait for new glue to get its glue host
+        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                MediaPlayerGlue mediaPlayerGlue = (MediaPlayerGlue) detailsFragment
+                        .mDetailsBackgroundController
+                        .getPlaybackGlue();
+                return mediaPlayerGlue != null && mediaPlayerGlue != glue1
+                        && mediaPlayerGlue.getHost() != null;
+            }
+        });
+
+        final MediaPlayerGlue glue2 = (MediaPlayerGlue) detailsFragment
+                .mDetailsBackgroundController
+                .getPlaybackGlue();
+
+        assertTrue(glue1.getHost() == null);
+        assertTrue(glue2.getHost() == playbackGlueHost);
+    }
+
+    @Test
     public void clearVideo() {
         final SingleSupportFragmentTestActivity activity =
                 launchAndWaitActivity(DetailsSupportFragmentWithNoVideo.class, new Options().uiVisibility(
@@ -675,14 +741,6 @@
         final DetailsSupportFragmentWithNoVideo detailsFragment =
                 (DetailsSupportFragmentWithNoVideo) activity.getTestFragment();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
-                        android.support.v17.leanback.test.R.drawable.spiderman));
-            }
-        });
-
         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
             @Override
             public boolean canProceed() {
@@ -727,7 +785,7 @@
             }
         });
 
-        // wait a little bit then clear glue
+        // wait a little bit then reset glue
         SystemClock.sleep(1000);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 new Runnable() {
@@ -737,7 +795,7 @@
                     }
                 }
         );
-        // background should fade in upon clear playback
+        // background should fade in upon reset playback
         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
             @Override
             public boolean canProceed() {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java
index ef6a1a3..354e574 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java
@@ -103,6 +103,7 @@
         mPhotoItem = photoItem;
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 if (getActivity() == null) {
                     return;
@@ -121,6 +122,7 @@
 
 
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 if (getActivity() == null) {
                     return;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestSupportFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestSupportFragment.java
index d963c3e..7d03a45 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestSupportFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestSupportFragment.java
@@ -106,6 +106,7 @@
         mPhotoItem = photoItem;
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 if (getActivity() == null) {
                     return;
@@ -124,6 +125,7 @@
 
 
         new Handler().postDelayed(new Runnable() {
+            @Override
             public void run() {
                 if (getActivity() == null) {
                     return;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
index 50aaa62..fa324bf 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.timeout;
@@ -25,7 +26,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.os.Bundle;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.widget.GuidedAction;
@@ -44,10 +45,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * @hide from javadoc
- */
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GuidedStepFragmentTest extends GuidedStepFragmentTestBase {
 
@@ -59,13 +57,15 @@
         final String secondFragmentName = generateMethodTestName("second");
         GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
@@ -81,13 +81,13 @@
         GuidedStepTestFragment.Provider second = mockProvider(secondFragmentName);
 
         GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName);
-        verify(first, times(1)).onCreate(any(Bundle.class));
-        verify(first, times(1)).onCreateGuidance(any(Bundle.class));
-        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
-        verify(first, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreate(nullable(Bundle.class));
+        verify(first, times(1)).onCreateGuidance(nullable(Bundle.class));
+        verify(first, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
+        verify(first, times(1)).onCreateButtonActions(any(List.class), nullable(Bundle.class));
         verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
-        verify(first, times(1)).onViewStateRestored(any(Bundle.class));
+                nullable(Bundle.class), any(View.class));
+        verify(first, times(1)).onViewStateRestored(nullable(Bundle.class));
         verify(first, times(1)).onStart();
         verify(first, times(1)).onResume();
 
@@ -98,13 +98,13 @@
         verify(first, times(1)).onPause();
         verify(first, times(1)).onStop();
         verify(first, times(1)).onDestroyView();
-        verify(second, times(1)).onCreate(any(Bundle.class));
-        verify(second, times(1)).onCreateGuidance(any(Bundle.class));
-        verify(second, times(1)).onCreateActions(any(List.class), any(Bundle.class));
-        verify(second, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
-        verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
-        verify(second, times(1)).onViewStateRestored(any(Bundle.class));
+        verify(second, times(1)).onCreate(nullable(Bundle.class));
+        verify(second, times(1)).onCreateGuidance(nullable(Bundle.class));
+        verify(second, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
+        verify(second, times(1)).onCreateButtonActions(any(List.class), nullable(Bundle.class));
+        verify(second, times(1)).onCreateView(any(LayoutInflater.class), nullable(ViewGroup.class),
+                nullable(Bundle.class), any(View.class));
+        verify(second, times(1)).onViewStateRestored(nullable(Bundle.class));
         verify(second, times(1)).onStart();
         verify(second, times(1)).onResume();
 
@@ -115,10 +115,10 @@
         verify(second, times(1)).onStop();
         verify(second, times(1)).onDestroyView();
         verify(second, times(1)).onDestroy();
-        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
-        verify(first, times(2)).onViewStateRestored(any(Bundle.class));
+                nullable(Bundle.class), any(View.class));
+        verify(first, times(2)).onViewStateRestored(nullable(Bundle.class));
         verify(first, times(2)).onStart();
         verify(first, times(2)).onResume();
 
@@ -134,6 +134,7 @@
         final String secondFragmentName = generateMethodTestName("second");
         GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
@@ -143,8 +144,9 @@
                         .autoSaveRestoreEnabled(false).build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
@@ -173,11 +175,11 @@
         });
         PollingCheck.waitFor(new EnterTransitionFinish(second));
         verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         verify(first, times(1)).onDestroy();
-        verify(second, times(2)).onCreate(any(Bundle.class));
+        verify(second, times(2)).onCreate(nullable(Bundle.class));
         verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         verify(second, times(1)).onDestroy();
 
         sendKey(KeyEvent.KEYCODE_BACK);
@@ -188,10 +190,10 @@
         verify(second, times(2)).onDestroy();
         assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
         assertEquals("text", first.getFragment().findActionById(1002).getTitle());
-        verify(first, times(2)).onCreate(any(Bundle.class));
-        verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(2)).onCreate(nullable(Bundle.class));
+        verify(first, times(2)).onCreateActions(any(List.class), nullable(Bundle.class));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
     }
 
 
@@ -200,13 +202,15 @@
         final String firstFragmentName = generateMethodTestName("first");
         GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1001).title("Finish activity").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
@@ -232,13 +236,15 @@
         final String firstFragmentName = generateMethodTestName("first");
         GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1001).title("Finish fragments").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
@@ -269,6 +275,7 @@
         final boolean[] expandSubActionInOnCreateView = new boolean[] {false};
         GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
                         invocation.getMock();
@@ -278,8 +285,9 @@
                 return null;
             }
         }).when(first).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 List<GuidedAction> subActions = new ArrayList<GuidedAction>();
@@ -289,8 +297,9 @@
                         .title("list").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Boolean>() {
+            @Override
             public Boolean answer(InvocationOnMock invocation) {
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
                         invocation.getMock();
@@ -337,14 +346,14 @@
 
         PollingCheck.waitFor(new EnterTransitionFinish(second));
         verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
 
         // test expand sub action when return to first fragment
         expandSubActionInOnCreateView[0] = true;
         sendKey(KeyEvent.KEYCODE_BACK);
         PollingCheck.waitFor(new EnterTransitionFinish(first));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         assertTrue(first.getFragment().isExpanded());
 
         sendKey(KeyEvent.KEYCODE_BACK);
@@ -361,6 +370,7 @@
         final String firstFragmentName = generateMethodTestName("first");
         GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 List<GuidedAction> subActions = new ArrayList<GuidedAction>();
@@ -369,8 +379,9 @@
                         .title("list").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Boolean>() {
+            @Override
             public Boolean answer(InvocationOnMock invocation) {
                 GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
                         invocation.getMock();
@@ -409,4 +420,32 @@
 
     }
 
+    @Test
+    public void buttonActionsRtl() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1000).title("action").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1001).title("button action").build());
+                return null;
+            }
+        }).when(first).onCreateButtonActions(any(List.class), nullable(Bundle.class));
+
+        final GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName,
+                true, View.LAYOUT_DIRECTION_RTL);
+
+        assertEquals(View.LAYOUT_DIRECTION_RTL, first.getFragment().getView().getLayoutDirection());
+        View firstView = first.getFragment().getActionItemView(0);
+        assertTrue(firstView.hasFocus());
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java
index 06beab6..4dcf188 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java
@@ -32,12 +32,21 @@
      */
     public static final String EXTRA_ADD_AS_ROOT = "addAsRoot";
 
+    /**
+     * Layout direction
+     */
+    public static final String EXTRA_LAYOUT_DIRECTION = "layoutDir";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         Intent intent = getIntent();
 
+        int layoutDirection = intent.getIntExtra(EXTRA_LAYOUT_DIRECTION, -1);
+        if (layoutDirection != -1) {
+            findViewById(android.R.id.content).setLayoutDirection(layoutDirection);
+        }
         if (savedInstanceState == null) {
             String firstTestName = intent.getStringExtra(EXTRA_TEST_NAME);
             if (firstTestName != null) {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java
index 6ed254a..7059c9a 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java
@@ -125,6 +125,15 @@
         return activityTestRule.launchActivity(intent);
     }
 
+    public GuidedStepFragmentTestActivity launchTestActivity(String firstTestName,
+            boolean addAsRoot, int layoutDirection) {
+        Intent intent = new Intent();
+        intent.putExtra(GuidedStepFragmentTestActivity.EXTRA_TEST_NAME, firstTestName);
+        intent.putExtra(GuidedStepFragmentTestActivity.EXTRA_ADD_AS_ROOT, addAsRoot);
+        intent.putExtra(GuidedStepFragmentTestActivity.EXTRA_LAYOUT_DIRECTION, layoutDirection);
+        return activityTestRule.launchActivity(intent);
+    }
+
     public GuidedStepTestFragment.Provider mockProvider(String testName) {
         GuidedStepTestFragment.Provider test = mock(GuidedStepTestFragment.Provider.class);
         when(test.getActivity()).thenCallRealMethod();
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
index 7d6b54f..b4d9b59 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.timeout;
@@ -28,7 +29,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.os.Bundle;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.widget.GuidedAction;
@@ -47,10 +48,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * @hide from javadoc
- */
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GuidedStepSupportFragmentTest extends GuidedStepSupportFragmentTestBase {
 
@@ -62,13 +60,15 @@
         final String secondFragmentName = generateMethodTestName("second");
         GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
@@ -84,13 +84,13 @@
         GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
 
         GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
-        verify(first, times(1)).onCreate(any(Bundle.class));
-        verify(first, times(1)).onCreateGuidance(any(Bundle.class));
-        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
-        verify(first, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreate(nullable(Bundle.class));
+        verify(first, times(1)).onCreateGuidance(nullable(Bundle.class));
+        verify(first, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
+        verify(first, times(1)).onCreateButtonActions(any(List.class), nullable(Bundle.class));
         verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
-        verify(first, times(1)).onViewStateRestored(any(Bundle.class));
+                nullable(Bundle.class), any(View.class));
+        verify(first, times(1)).onViewStateRestored(nullable(Bundle.class));
         verify(first, times(1)).onStart();
         verify(first, times(1)).onResume();
 
@@ -101,13 +101,13 @@
         verify(first, times(1)).onPause();
         verify(first, times(1)).onStop();
         verify(first, times(1)).onDestroyView();
-        verify(second, times(1)).onCreate(any(Bundle.class));
-        verify(second, times(1)).onCreateGuidance(any(Bundle.class));
-        verify(second, times(1)).onCreateActions(any(List.class), any(Bundle.class));
-        verify(second, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
-        verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
-        verify(second, times(1)).onViewStateRestored(any(Bundle.class));
+        verify(second, times(1)).onCreate(nullable(Bundle.class));
+        verify(second, times(1)).onCreateGuidance(nullable(Bundle.class));
+        verify(second, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
+        verify(second, times(1)).onCreateButtonActions(any(List.class), nullable(Bundle.class));
+        verify(second, times(1)).onCreateView(any(LayoutInflater.class), nullable(ViewGroup.class),
+                nullable(Bundle.class), any(View.class));
+        verify(second, times(1)).onViewStateRestored(nullable(Bundle.class));
         verify(second, times(1)).onStart();
         verify(second, times(1)).onResume();
 
@@ -118,10 +118,10 @@
         verify(second, times(1)).onStop();
         verify(second, times(1)).onDestroyView();
         verify(second, times(1)).onDestroy();
-        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
-        verify(first, times(2)).onViewStateRestored(any(Bundle.class));
+                nullable(Bundle.class), any(View.class));
+        verify(first, times(2)).onViewStateRestored(nullable(Bundle.class));
         verify(first, times(2)).onStart();
         verify(first, times(2)).onResume();
 
@@ -137,6 +137,7 @@
         final String secondFragmentName = generateMethodTestName("second");
         GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
@@ -146,8 +147,9 @@
                         .autoSaveRestoreEnabled(false).build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
@@ -176,11 +178,11 @@
         });
         PollingCheck.waitFor(new EnterTransitionFinish(second));
         verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         verify(first, times(1)).onDestroy();
-        verify(second, times(2)).onCreate(any(Bundle.class));
+        verify(second, times(2)).onCreate(nullable(Bundle.class));
         verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         verify(second, times(1)).onDestroy();
 
         sendKey(KeyEvent.KEYCODE_BACK);
@@ -191,10 +193,10 @@
         verify(second, times(2)).onDestroy();
         assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
         assertEquals("text", first.getFragment().findActionById(1002).getTitle());
-        verify(first, times(2)).onCreate(any(Bundle.class));
-        verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(2)).onCreate(nullable(Bundle.class));
+        verify(first, times(2)).onCreateActions(any(List.class), nullable(Bundle.class));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
     }
 
 
@@ -203,13 +205,15 @@
         final String firstFragmentName = generateMethodTestName("first");
         GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1001).title("Finish activity").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
@@ -235,13 +239,15 @@
         final String firstFragmentName = generateMethodTestName("first");
         GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 actions.add(new GuidedAction.Builder().id(1001).title("Finish fragments").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedAction action = (GuidedAction) invocation.getArguments()[0];
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
@@ -272,6 +278,7 @@
         final boolean[] expandSubActionInOnCreateView = new boolean[] {false};
         GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
                         invocation.getMock();
@@ -281,8 +288,9 @@
                 return null;
             }
         }).when(first).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 List<GuidedAction> subActions = new ArrayList<GuidedAction>();
@@ -292,8 +300,9 @@
                         .title("list").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Boolean>() {
+            @Override
             public Boolean answer(InvocationOnMock invocation) {
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
                         invocation.getMock();
@@ -340,14 +349,14 @@
 
         PollingCheck.waitFor(new EnterTransitionFinish(second));
         verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
 
         // test expand sub action when return to first fragment
         expandSubActionInOnCreateView[0] = true;
         sendKey(KeyEvent.KEYCODE_BACK);
         PollingCheck.waitFor(new EnterTransitionFinish(first));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
-                any(Bundle.class), any(View.class));
+                nullable(Bundle.class), any(View.class));
         assertTrue(first.getFragment().isExpanded());
 
         sendKey(KeyEvent.KEYCODE_BACK);
@@ -364,6 +373,7 @@
         final String firstFragmentName = generateMethodTestName("first");
         GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
         doAnswer(new Answer<Void>() {
+            @Override
             public Void answer(InvocationOnMock invocation) {
                 List actions = (List) invocation.getArguments()[0];
                 List<GuidedAction> subActions = new ArrayList<GuidedAction>();
@@ -372,8 +382,9 @@
                         .title("list").build());
                 return null;
             }
-        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
         doAnswer(new Answer<Boolean>() {
+            @Override
             public Boolean answer(InvocationOnMock invocation) {
                 GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
                         invocation.getMock();
@@ -412,4 +423,32 @@
 
     }
 
+    @Test
+    public void buttonActionsRtl() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1000).title("action").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1001).title("button action").build());
+                return null;
+            }
+        }).when(first).onCreateButtonActions(any(List.class), nullable(Bundle.class));
+
+        final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName,
+                true, View.LAYOUT_DIRECTION_RTL);
+
+        assertEquals(View.LAYOUT_DIRECTION_RTL, first.getFragment().getView().getLayoutDirection());
+        View firstView = first.getFragment().getActionItemView(0);
+        assertTrue(firstView.hasFocus());
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java
index 2fc8d1e..fb877ed 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java
@@ -35,12 +35,21 @@
      */
     public static final String EXTRA_ADD_AS_ROOT = "addAsRoot";
 
+    /**
+     * Layout direction
+     */
+    public static final String EXTRA_LAYOUT_DIRECTION = "layoutDir";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         Intent intent = getIntent();
 
+        int layoutDirection = intent.getIntExtra(EXTRA_LAYOUT_DIRECTION, -1);
+        if (layoutDirection != -1) {
+            findViewById(android.R.id.content).setLayoutDirection(layoutDirection);
+        }
         if (savedInstanceState == null) {
             String firstTestName = intent.getStringExtra(EXTRA_TEST_NAME);
             if (firstTestName != null) {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java
index 4fe4a24..17533fa 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java
@@ -128,6 +128,15 @@
         return activityTestRule.launchActivity(intent);
     }
 
+    public GuidedStepSupportFragmentTestActivity launchTestActivity(String firstTestName,
+            boolean addAsRoot, int layoutDirection) {
+        Intent intent = new Intent();
+        intent.putExtra(GuidedStepSupportFragmentTestActivity.EXTRA_TEST_NAME, firstTestName);
+        intent.putExtra(GuidedStepSupportFragmentTestActivity.EXTRA_ADD_AS_ROOT, addAsRoot);
+        intent.putExtra(GuidedStepSupportFragmentTestActivity.EXTRA_LAYOUT_DIRECTION, layoutDirection);
+        return activityTestRule.launchActivity(intent);
+    }
+
     public GuidedStepTestSupportFragment.Provider mockProvider(String testName) {
         GuidedStepTestSupportFragment.Provider test = mock(GuidedStepTestSupportFragment.Provider.class);
         when(test.getActivity()).thenCallRealMethod();
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersFragmentTest.java
index 9dd8a49..e05237f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersFragmentTest.java
@@ -97,4 +97,30 @@
         assertEquals(vh.itemView.getScaleY(), 1f, 0.001f);
     }
 
+    public static class F_disableScaleInConstructor extends HeadersFragment {
+        public F_disableScaleInConstructor() {
+            FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter(), false);
+        }
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            final ArrayObjectAdapter adapter = new ArrayObjectAdapter(new ListRowPresenter());
+            setAdapter(adapter);
+            loadData(adapter, 10);
+        }
+    }
+
+    @Test
+    public void disableScaleInConstructor() {
+        SingleFragmentTestActivity activity = launchAndWaitActivity(
+                F_disableScaleInConstructor.class, 1000);
+
+        final VerticalGridView gridView = ((HeadersFragment) activity.getTestFragment())
+                .getVerticalGridView();
+        ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                gridView.findViewHolderForAdapterPosition(0);
+        assertEquals(vh.itemView.getScaleX(), 1f, 0.001f);
+        assertEquals(vh.itemView.getScaleY(), 1f, 0.001f);
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersSupportFragmentTest.java
index b8f7cc8..7ec69b9 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/HeadersSupportFragmentTest.java
@@ -100,4 +100,30 @@
         assertEquals(vh.itemView.getScaleY(), 1f, 0.001f);
     }
 
+    public static class F_disableScaleInConstructor extends HeadersSupportFragment {
+        public F_disableScaleInConstructor() {
+            FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter(), false);
+        }
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            final ArrayObjectAdapter adapter = new ArrayObjectAdapter(new ListRowPresenter());
+            setAdapter(adapter);
+            loadData(adapter, 10);
+        }
+    }
+
+    @Test
+    public void disableScaleInConstructor() {
+        SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
+                F_disableScaleInConstructor.class, 1000);
+
+        final VerticalGridView gridView = ((HeadersSupportFragment) activity.getTestFragment())
+                .getVerticalGridView();
+        ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                gridView.findViewHolderForAdapterPosition(0);
+        assertEquals(vh.itemView.getScaleX(), 1f, 0.001f);
+        assertEquals(vh.itemView.getScaleY(), 1f, 0.001f);
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
index 0b40920..c118b4e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -44,7 +45,6 @@
  * Unit test for {@link ListRowDataAdapter} class.
  */
 @RunWith(AndroidJUnit4.class)
-@SmallTest
 public class ListRowDataAdapterTest {
     @Mock
     private PresenterSelector presenterSelector;
@@ -56,6 +56,7 @@
         MockitoAnnotations.initMocks(this);
     }
 
+    @SmallTest
     @Test
     public void itemRangeChangedTest() {
         int itemCount = 4;
@@ -75,6 +76,7 @@
         assertEquals(5, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void adapterSize_nonVisibleRowPresent() {
         int itemCount = 4;
@@ -96,6 +98,7 @@
         assertEquals(5, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void adapterSize_visibleRowInserted() {
         int itemCount = 4;
@@ -119,6 +122,7 @@
         assertEquals(8, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void adapterSize_nonVisibleRowInserted() {
         int itemCount = 4;
@@ -150,6 +154,7 @@
         assertEquals(9, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void adapterSize_visibleRowRemoved() {
         int itemCount = 4;
@@ -171,6 +176,7 @@
         assertEquals(3, listRowDataAdapter.size());
     }
 
+    @MediumTest
     @Test
     public void adapterSize_nonVisibleRowRemoved() {
         int itemCount = 4;
@@ -196,6 +202,7 @@
         assertEquals(4, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void adapterSize_rowsRemoveAll() {
         ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenterSelector);
@@ -216,6 +223,7 @@
         assertEquals(1, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void changeRemove_revealInvisibleItems() {
         ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenterSelector);
@@ -238,6 +246,7 @@
         assertEquals(4, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void adapterSize_rowsRemoved() {
         int itemCount = 4;
@@ -260,6 +269,7 @@
         assertEquals(3, listRowDataAdapter.size());
     }
 
+    @SmallTest
     @Test
     public void customObjectAdapterTest() {
         int itemCount = 4;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackFragmentTest.java
index 7824e63..6353ef9 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackFragmentTest.java
@@ -26,7 +26,9 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.media.PlaybackControlGlue;
 import android.support.v17.leanback.media.PlaybackGlue;
@@ -63,6 +65,7 @@
         final PlaybackTestFragment fragment = (PlaybackTestFragment) activity.getTestFragment();
         PlaybackGlue glue = fragment.getGlue();
         activityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 activity.finish();
             }
@@ -225,6 +228,8 @@
                 listRowItemPassed);
     }
 
+    @FlakyTest
+    @Suppress
     @Test
     public void alignmentRowToBottom() throws Throwable {
         SingleFragmentTestActivity activity =
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
index dca53de..f21bd04 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
@@ -20,7 +20,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.test.R;
 import android.view.View;
@@ -44,6 +46,8 @@
         assertTrue(fragment.getView().hasFocus());
     }
 
+    @FlakyTest
+    @Suppress
     @Test
     public void alignmentRowToBottom() throws Throwable {
         SingleFragmentTestActivity activity =
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java
index 4fdea79..82e37d3 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java
@@ -84,6 +84,7 @@
         }
     };
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -103,11 +104,11 @@
         mGlue = new PlaybackControlHelper(context, this) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -196,9 +197,9 @@
         PlaybackControlHelper(Context context, PlaybackOverlayFragment fragment) {
             super(context, fragment, sFastForwardSpeeds);
             mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
             mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
             mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
             mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
         }
@@ -362,7 +363,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) {
                         pausePlayback();
                     } else {
                         startPlayback(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackSupportFragmentTest.java
index 7b8fb8e..cbc8222 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackSupportFragmentTest.java
@@ -29,7 +29,9 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.media.PlaybackControlGlue;
 import android.support.v17.leanback.media.PlaybackGlue;
@@ -66,6 +68,7 @@
         final PlaybackTestSupportFragment fragment = (PlaybackTestSupportFragment) activity.getTestFragment();
         PlaybackGlue glue = fragment.getGlue();
         activityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 activity.finish();
             }
@@ -228,6 +231,8 @@
                 listRowItemPassed);
     }
 
+    @FlakyTest
+    @Suppress
     @Test
     public void alignmentRowToBottom() throws Throwable {
         SingleSupportFragmentTestActivity activity =
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestFragment.java
index 2ca92da..027ea02 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestFragment.java
@@ -60,6 +60,7 @@
     private android.support.v17.leanback.media.PlaybackControlGlue mGlue;
     boolean mDestroyCalled;
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -93,11 +94,11 @@
         mGlue = new PlaybackControlHelper(context) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -171,9 +172,9 @@
         PlaybackControlHelper(Context context) {
             super(context, sFastForwardSpeeds);
             mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
             mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
             mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
             mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
         }
@@ -314,7 +315,7 @@
             sProgressHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) {
                         pause();
                     } else {
                         play(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestSupportFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestSupportFragment.java
index 1655b76..273df26 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestSupportFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackTestSupportFragment.java
@@ -63,6 +63,7 @@
     private android.support.v17.leanback.media.PlaybackControlGlue mGlue;
     boolean mDestroyCalled;
 
+    @Override
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -96,11 +97,11 @@
         mGlue = new PlaybackControlHelper(context) {
             @Override
             public int getUpdatePeriod() {
-                int totalTime = getControlsRow().getTotalTime();
+                long totalTime = getControlsRow().getDuration();
                 if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
                     return 1000;
                 }
-                return Math.max(16, totalTime / getView().getWidth());
+                return 16;
             }
 
             @Override
@@ -174,9 +175,9 @@
         PlaybackControlHelper(Context context) {
             super(context, sFastForwardSpeeds);
             mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE);
             mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE);
             mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
             mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
         }
@@ -317,7 +318,7 @@
             sProgressHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) {
                         pause();
                     } else {
                         play(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java
new file mode 100644
index 0000000..519f1de
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+package android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertSame;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@LargeTest
+public class ProgressBarManagerTest {
+
+    Context mContext;
+    ProgressBarManager mProgressBarManager;
+    long mWaitShownTimeOutMs;
+    long mWaitHideTimeOutMs;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager = new ProgressBarManager();
+            }
+        });
+        mWaitShownTimeOutMs = Math.max(2000, mProgressBarManager.getInitialDelay() * 3);
+        mWaitHideTimeOutMs = 2000;
+    }
+
+    @Test
+    public void defaultProgressBarView() {
+        final ViewGroup rootView = new FrameLayout(mContext);
+        mProgressBarManager.setRootView(rootView);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager.show();
+            }
+        });
+        PollingCheck.waitFor(mWaitShownTimeOutMs,
+                new PollingCheck.PollingCheckCondition() {
+                    @Override
+                    public boolean canProceed() {
+                        if (rootView.getChildCount() == 0) return false;
+                        return  rootView.getChildAt(0).getVisibility() == View.VISIBLE;
+                    }
+                });
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager.hide();
+            }
+        });
+        PollingCheck.waitFor(mWaitHideTimeOutMs,
+                new PollingCheck.PollingCheckCondition() {
+                    @Override
+                    public boolean canProceed() {
+                        return rootView.getChildCount() == 0;
+                    }
+                });
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager.show();
+            }
+        });
+        PollingCheck.waitFor(mWaitShownTimeOutMs,
+                new PollingCheck.PollingCheckCondition() {
+                    @Override
+                    public boolean canProceed() {
+                        if (rootView.getChildCount() == 0) return false;
+                        return  rootView.getChildAt(0).getVisibility() == View.VISIBLE;
+                    }
+                });
+    }
+
+    @Test
+    public void customProgressBarView() {
+        final ViewGroup rootView = new FrameLayout(mContext);
+        View customProgressBar = new View(mContext);
+        rootView.addView(customProgressBar, 100, 100);
+        mProgressBarManager.setProgressBarView(customProgressBar);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager.show();
+            }
+        });
+        PollingCheck.waitFor(mWaitShownTimeOutMs,
+                new PollingCheck.PollingCheckCondition() {
+                    @Override
+                    public boolean canProceed() {
+                        if (rootView.getChildCount() == 0) return false;
+                        return  rootView.getChildAt(0).getVisibility() == View.VISIBLE;
+                    }
+                });
+        assertSame(customProgressBar, rootView.getChildAt(0));
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager.hide();
+            }
+        });
+        PollingCheck.waitFor(mWaitHideTimeOutMs,
+                new PollingCheck.PollingCheckCondition() {
+                    @Override
+                    public boolean canProceed() {
+                        return  rootView.getChildAt(0).getVisibility() != View.VISIBLE;
+                    }
+                });
+        assertSame(customProgressBar, rootView.getChildAt(0));
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mProgressBarManager.show();
+            }
+        });
+        PollingCheck.waitFor(mWaitShownTimeOutMs,
+                new PollingCheck.PollingCheckCondition() {
+                    @Override
+                    public boolean canProceed() {
+                        if (rootView.getChildCount() == 0) return false;
+                        return  rootView.getChildAt(0).getVisibility() == View.VISIBLE;
+                    }
+                });
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java
index a9bece0..b902678 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java
@@ -21,22 +21,28 @@
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.R;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.HorizontalGridView;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ListRow;
 import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.SinglePresenterSelector;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.view.KeyEvent;
 import android.view.View;
@@ -45,6 +51,7 @@
 import org.junit.runner.RunWith;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -83,7 +90,7 @@
     }
 
     @Test
-    public void defaultAlignment() throws InterruptedException {
+    public void defaultAlignment() throws Throwable {
         SingleFragmentTestActivity activity = launchAndWaitActivity(F_defaultAlignment.class, 1000);
 
         final Rect rect = new Rect();
@@ -96,6 +103,7 @@
         assertEquals("First row is initially aligned to top of screen", 0, rect.top);
 
         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        waitForScrollIdle(gridView);
         View row1 = gridView.findViewHolderForAdapterPosition(1).itemView;
         PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(row1));
 
@@ -227,6 +235,7 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 new Runnable() {
+                    @Override
                     public void run() {
                         activity.recreate();
                     }
@@ -296,4 +305,160 @@
         assertSame(prefetchedListRowVh.getItemViewHolder(0), fragment.mLastClickedItemViewHolder);
     }
 
+    @Test
+    public void changeHasStableIdToTrueAfterViewCreated() throws InterruptedException {
+        SingleFragmentTestActivity activity =
+                launchAndWaitActivity(RowsFragment.class, 2000);
+        final RowsFragment fragment = (RowsFragment) activity.getTestFragment();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                new Runnable() {
+                    public void run() {
+                        ObjectAdapter adapter = new ObjectAdapter() {
+                            @Override
+                            public int size() {
+                                return 0;
+                            }
+
+                            @Override
+                            public Object get(int position) {
+                                return null;
+                            }
+
+                            @Override
+                            public long getId(int position) {
+                                return 1;
+                            }
+                        };
+                        adapter.setHasStableIds(true);
+                        fragment.setAdapter(adapter);
+                    }
+                }
+        );
+    }
+
+    static class StableIdAdapter extends ObjectAdapter {
+        ArrayList<Integer> mList = new ArrayList();
+
+        @Override
+        public long getId(int position) {
+            return mList.get(position).longValue();
+        }
+
+        @Override
+        public Object get(int position) {
+            return mList.get(position);
+        }
+
+        @Override
+        public int size() {
+            return mList.size();
+        }
+    }
+
+    public static class F_rowNotifyItemRangeChange extends BrowseFragment {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            ListRowPresenter lrp = new ListRowPresenter();
+            final ArrayObjectAdapter adapter = new ArrayObjectAdapter(lrp);
+            for (int i = 0; i < 2; i++) {
+                StableIdAdapter listRowAdapter = new StableIdAdapter();
+                listRowAdapter.setHasStableIds(true);
+                listRowAdapter.setPresenterSelector(
+                        new SinglePresenterSelector(sCardPresenter));
+                int index = 0;
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                HeaderItem header = new HeaderItem(i, "Row " + i);
+                adapter.add(new ListRow(header, listRowAdapter));
+            }
+            setAdapter(adapter);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    StableIdAdapter rowAdapter = (StableIdAdapter)
+                            ((ListRow) adapter.get(1)).getAdapter();
+                    rowAdapter.notifyItemRangeChanged(0, 3);
+                }
+            }, 500);
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
+    public void rowNotifyItemRangeChange() throws InterruptedException {
+        SingleFragmentTestActivity activity = launchAndWaitActivity(
+                RowsFragmentTest.F_rowNotifyItemRangeChange.class, 2000);
+
+        VerticalGridView verticalGridView = ((BrowseFragment) activity.getTestFragment())
+                .getRowsFragment().getVerticalGridView();
+        for (int i = 0; i < verticalGridView.getChildCount(); i++) {
+            HorizontalGridView horizontalGridView = verticalGridView.getChildAt(i)
+                    .findViewById(R.id.row_content);
+            for (int j = 0; j < horizontalGridView.getChildCount(); j++) {
+                assertEquals(horizontalGridView.getPaddingTop(),
+                        horizontalGridView.getChildAt(j).getTop());
+            }
+        }
+    }
+
+    public static class F_rowNotifyItemRangeChangeWithTransition extends BrowseFragment {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            ListRowPresenter lrp = new ListRowPresenter();
+            prepareEntranceTransition();
+            final ArrayObjectAdapter adapter = new ArrayObjectAdapter(lrp);
+            for (int i = 0; i < 2; i++) {
+                StableIdAdapter listRowAdapter = new StableIdAdapter();
+                listRowAdapter.setHasStableIds(true);
+                listRowAdapter.setPresenterSelector(
+                        new SinglePresenterSelector(sCardPresenter));
+                int index = 0;
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                HeaderItem header = new HeaderItem(i, "Row " + i);
+                adapter.add(new ListRow(header, listRowAdapter));
+            }
+            setAdapter(adapter);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    StableIdAdapter rowAdapter = (StableIdAdapter)
+                            ((ListRow) adapter.get(1)).getAdapter();
+                    rowAdapter.notifyItemRangeChanged(0, 3);
+                }
+            }, 500);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    startEntranceTransition();
+                }
+            }, 520);
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
+    public void rowNotifyItemRangeChangeWithTransition() throws InterruptedException {
+        SingleFragmentTestActivity activity = launchAndWaitActivity(
+                        RowsFragmentTest.F_rowNotifyItemRangeChangeWithTransition.class, 3000);
+
+        VerticalGridView verticalGridView = ((BrowseFragment) activity.getTestFragment())
+                .getRowsFragment().getVerticalGridView();
+        for (int i = 0; i < verticalGridView.getChildCount(); i++) {
+            HorizontalGridView horizontalGridView = verticalGridView.getChildAt(i)
+                    .findViewById(R.id.row_content);
+            for (int j = 0; j < horizontalGridView.getChildCount(); j++) {
+                assertEquals(horizontalGridView.getPaddingTop(),
+                        horizontalGridView.getChildAt(j).getTop());
+                assertEquals(0, horizontalGridView.getChildAt(j).getTranslationY(), 0.1f);
+            }
+        }
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java
index a33c92e..a46730d 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java
@@ -24,22 +24,28 @@
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.R;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.HorizontalGridView;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ListRow;
 import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.SinglePresenterSelector;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.view.KeyEvent;
 import android.view.View;
@@ -48,6 +54,7 @@
 import org.junit.runner.RunWith;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -86,7 +93,7 @@
     }
 
     @Test
-    public void defaultAlignment() throws InterruptedException {
+    public void defaultAlignment() throws Throwable {
         SingleSupportFragmentTestActivity activity = launchAndWaitActivity(F_defaultAlignment.class, 1000);
 
         final Rect rect = new Rect();
@@ -99,6 +106,7 @@
         assertEquals("First row is initially aligned to top of screen", 0, rect.top);
 
         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        waitForScrollIdle(gridView);
         View row1 = gridView.findViewHolderForAdapterPosition(1).itemView;
         PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(row1));
 
@@ -230,6 +238,7 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 new Runnable() {
+                    @Override
                     public void run() {
                         activity.recreate();
                     }
@@ -299,4 +308,160 @@
         assertSame(prefetchedListRowVh.getItemViewHolder(0), fragment.mLastClickedItemViewHolder);
     }
 
+    @Test
+    public void changeHasStableIdToTrueAfterViewCreated() throws InterruptedException {
+        SingleSupportFragmentTestActivity activity =
+                launchAndWaitActivity(RowsSupportFragment.class, 2000);
+        final RowsSupportFragment fragment = (RowsSupportFragment) activity.getTestFragment();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                new Runnable() {
+                    public void run() {
+                        ObjectAdapter adapter = new ObjectAdapter() {
+                            @Override
+                            public int size() {
+                                return 0;
+                            }
+
+                            @Override
+                            public Object get(int position) {
+                                return null;
+                            }
+
+                            @Override
+                            public long getId(int position) {
+                                return 1;
+                            }
+                        };
+                        adapter.setHasStableIds(true);
+                        fragment.setAdapter(adapter);
+                    }
+                }
+        );
+    }
+
+    static class StableIdAdapter extends ObjectAdapter {
+        ArrayList<Integer> mList = new ArrayList();
+
+        @Override
+        public long getId(int position) {
+            return mList.get(position).longValue();
+        }
+
+        @Override
+        public Object get(int position) {
+            return mList.get(position);
+        }
+
+        @Override
+        public int size() {
+            return mList.size();
+        }
+    }
+
+    public static class F_rowNotifyItemRangeChange extends BrowseSupportFragment {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            ListRowPresenter lrp = new ListRowPresenter();
+            final ArrayObjectAdapter adapter = new ArrayObjectAdapter(lrp);
+            for (int i = 0; i < 2; i++) {
+                StableIdAdapter listRowAdapter = new StableIdAdapter();
+                listRowAdapter.setHasStableIds(true);
+                listRowAdapter.setPresenterSelector(
+                        new SinglePresenterSelector(sCardPresenter));
+                int index = 0;
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                HeaderItem header = new HeaderItem(i, "Row " + i);
+                adapter.add(new ListRow(header, listRowAdapter));
+            }
+            setAdapter(adapter);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    StableIdAdapter rowAdapter = (StableIdAdapter)
+                            ((ListRow) adapter.get(1)).getAdapter();
+                    rowAdapter.notifyItemRangeChanged(0, 3);
+                }
+            }, 500);
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
+    public void rowNotifyItemRangeChange() throws InterruptedException {
+        SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
+                RowsSupportFragmentTest.F_rowNotifyItemRangeChange.class, 2000);
+
+        VerticalGridView verticalGridView = ((BrowseSupportFragment) activity.getTestFragment())
+                .getRowsSupportFragment().getVerticalGridView();
+        for (int i = 0; i < verticalGridView.getChildCount(); i++) {
+            HorizontalGridView horizontalGridView = verticalGridView.getChildAt(i)
+                    .findViewById(R.id.row_content);
+            for (int j = 0; j < horizontalGridView.getChildCount(); j++) {
+                assertEquals(horizontalGridView.getPaddingTop(),
+                        horizontalGridView.getChildAt(j).getTop());
+            }
+        }
+    }
+
+    public static class F_rowNotifyItemRangeChangeWithTransition extends BrowseSupportFragment {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            ListRowPresenter lrp = new ListRowPresenter();
+            prepareEntranceTransition();
+            final ArrayObjectAdapter adapter = new ArrayObjectAdapter(lrp);
+            for (int i = 0; i < 2; i++) {
+                StableIdAdapter listRowAdapter = new StableIdAdapter();
+                listRowAdapter.setHasStableIds(true);
+                listRowAdapter.setPresenterSelector(
+                        new SinglePresenterSelector(sCardPresenter));
+                int index = 0;
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                listRowAdapter.mList.add(index++);
+                HeaderItem header = new HeaderItem(i, "Row " + i);
+                adapter.add(new ListRow(header, listRowAdapter));
+            }
+            setAdapter(adapter);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    StableIdAdapter rowAdapter = (StableIdAdapter)
+                            ((ListRow) adapter.get(1)).getAdapter();
+                    rowAdapter.notifyItemRangeChanged(0, 3);
+                }
+            }, 500);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    startEntranceTransition();
+                }
+            }, 520);
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
+    public void rowNotifyItemRangeChangeWithTransition() throws InterruptedException {
+        SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
+                        RowsSupportFragmentTest.F_rowNotifyItemRangeChangeWithTransition.class, 3000);
+
+        VerticalGridView verticalGridView = ((BrowseSupportFragment) activity.getTestFragment())
+                .getRowsSupportFragment().getVerticalGridView();
+        for (int i = 0; i < verticalGridView.getChildCount(); i++) {
+            HorizontalGridView horizontalGridView = verticalGridView.getChildAt(i)
+                    .findViewById(R.id.row_content);
+            for (int j = 0; j < horizontalGridView.getChildCount(); j++) {
+                assertEquals(horizontalGridView.getPaddingTop(),
+                        horizontalGridView.getChildAt(j).getTop());
+                assertEquals(0, horizontalGridView.getChildAt(j).getTranslationY(), 0.1f);
+            }
+        }
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java
index 7f9b408..9aa872c 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java
@@ -83,7 +83,7 @@
 
     public static final String EXTRA_PROVIDER = "testActivityProvider";
 
-    static HashMap<String, Provider> sProviders = new HashMap();
+    static HashMap<String, Provider> sProviders = new HashMap<>();
 
     String mProviderName;
     Provider mProvider;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java
index 6e49b65..2c36cda 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java
@@ -50,6 +50,7 @@
         final SingleFragmentTestActivity activity = launchAndWaitActivity(GridFragment.class, 500);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
             public void run() {
                 GridFragment f = new GridFragment();
                 activity.getFragmentManager().beginTransaction()
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java
index e531528..9ca930a 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java
@@ -53,6 +53,7 @@
         final SingleSupportFragmentTestActivity activity = launchAndWaitActivity(GridFragment.class, 500);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
             public void run() {
                 GridFragment f = new GridFragment();
                 activity.getSupportFragmentManager().beginTransaction()
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java
index f15521a..ba5d68e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java
@@ -163,11 +163,13 @@
             mGlue.setTitle("Leanback team at work");
             mGlue.setMediaSource(
                     Uri.parse("android.resource://android.support.v17.leanback.test/raw/video"));
-            mGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            mGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
                 @Override
-                public void onReadyForPlayback() {
-                    mGlueOnReadyForPlaybackCalled++;
-                    mGlue.play();
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        mGlueOnReadyForPlaybackCalled++;
+                        mGlue.play();
+                    }
                 }
             });
             mGlue.setHost(new VideoFragmentGlueHost(this));
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoSupportFragmentTest.java
index 8bca7fc..457268d 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoSupportFragmentTest.java
@@ -166,11 +166,13 @@
             mGlue.setTitle("Leanback team at work");
             mGlue.setMediaSource(
                     Uri.parse("android.resource://android.support.v17.leanback.test/raw/video"));
-            mGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            mGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
                 @Override
-                public void onReadyForPlayback() {
-                    mGlueOnReadyForPlaybackCalled++;
-                    mGlue.play();
+                public void onPreparedStateChanged(PlaybackGlue glue) {
+                    if (glue.isPrepared()) {
+                        mGlueOnReadyForPlaybackCalled++;
+                        mGlue.play();
+                    }
                 }
             });
             mGlue.setHost(new VideoSupportFragmentGlueHost(this));
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java
index ce11551..7d878fe 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java
@@ -20,7 +20,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.app.GuidedStepFragment;
@@ -45,7 +45,7 @@
 import java.util.Date;
 import java.util.List;
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GuidedDatePickerTest {
 
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java
index 0283687..9575d39 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java
@@ -20,7 +20,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.app.GuidedStepFragment;
@@ -39,7 +39,7 @@
 import java.util.Collections;
 import java.util.List;
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GuidedStepAttributesTest {
     static final long TRANSITION_LENGTH = 1000;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java
index 3819dac..691a9df 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java
@@ -18,7 +18,6 @@
 import android.support.v17.leanback.app.GuidedStepFragment;
 import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidedAction;
-import android.util.Log;
 
 import java.util.HashMap;
 import java.util.List;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java b/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java
index a91c221..fb68a51 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java
@@ -25,6 +25,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -37,7 +38,6 @@
  * Unit test for {@link FitWidthBitmapDrawable}
  */
 @RunWith(AndroidJUnit4.class)
-@SmallTest
 public class FitWidthBitmapDrawableTest {
     private final static int SCREEN_WIDTH = 1600;
     private final static int SCREEN_HEIGHT = 1080;
@@ -45,6 +45,7 @@
     private final static int HEIGHT = 600;
     private Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
 
+    @MediumTest
     @Test
     public void draw_withOffset() {
         int offset = 600;
@@ -66,6 +67,7 @@
         verify(canvas).drawBitmap(eq(bitmap), eq(bitmapBounds), eq(expectedDest), any(Paint.class));
     }
 
+    @SmallTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
     @Test
     public void constantState() {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/media/MediaPlayerGlueTest.java b/v17/leanback/tests/java/android/support/v17/leanback/media/MediaPlayerGlueTest.java
index a2956d6..c1f1807 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/media/MediaPlayerGlueTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/media/MediaPlayerGlueTest.java
@@ -84,7 +84,7 @@
             }
         });
 
-        // Test enableProgressUpdating(true) and enableProgressUpdating(false);
+        // Test setProgressUpdatingEnabled(true) and setProgressUpdatingEnabled(false);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackBannerControlGlueTest.java b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackBannerControlGlueTest.java
new file mode 100644
index 0000000..d020d56
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackBannerControlGlueTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.view.ContextThemeWrapper;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SmallTest
+public class PlaybackBannerControlGlueTest {
+
+    public static class PlayerAdapterSample extends PlayerAdapter {
+        @Override
+        public void play() {
+        }
+
+        @Override
+        public void pause() {
+        }
+    }
+
+    public static class PlaybackBannerControlGlueImpl
+            extends PlaybackBannerControlGlue {
+        public PlaybackBannerControlGlueImpl(Context context) {
+            super(context, new int[] {1, 2 , 3, 4, 5}, new PlayerAdapterSample());
+        }
+
+        public PlaybackBannerControlGlueImpl(Context context, PlayerAdapter impl) {
+            super(context, new int[] {1, 2 , 3, 4, 5}, impl);
+        }
+    }
+
+    Context mContext;
+    PlaybackBannerControlGlueImpl mGlue;
+    PlaybackControlsRowPresenter.ViewHolder mViewHolder;
+
+    @Test
+    public void usingDefaultRowAndPresenter() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackBannerControlGlueImpl(mContext);
+            }
+        });
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertTrue(host.mPlaybackRowPresenter instanceof PlaybackControlsRowPresenter);
+        assertTrue(host.mRow instanceof PlaybackControlsRow);
+
+    }
+    @Test
+    public void customRowPresenter() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackBannerControlGlueImpl(mContext);
+            }
+        });
+        PlaybackRowPresenter presenter = new PlaybackRowPresenter() {
+            @Override
+            protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
+                return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext()));
+            }
+        };
+        mGlue.setPlaybackRowPresenter(presenter);
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertSame(host.mPlaybackRowPresenter, presenter);
+        assertTrue(host.mRow instanceof PlaybackControlsRow);
+
+    }
+
+    @Test
+    public void customControlsRow() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackBannerControlGlueImpl(mContext);
+            }
+        });
+        PlaybackControlsRow row = new PlaybackControlsRow(mContext);
+        mGlue.setControlsRow(row);
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertTrue(host.mPlaybackRowPresenter instanceof PlaybackControlsRowPresenter);
+        assertSame(host.mRow, row);
+
+    }
+
+    @Test
+    public void customRowAndPresenter() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackBannerControlGlueImpl(mContext);
+            }
+        });
+        PlaybackControlsRow row = new PlaybackControlsRow(mContext);
+        mGlue.setControlsRow(row);
+        PlaybackRowPresenter presenter = new PlaybackRowPresenter() {
+            @Override
+            protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
+                return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext()));
+            }
+        };
+        mGlue.setPlaybackRowPresenter(presenter);
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertSame(host.mPlaybackRowPresenter, presenter);
+        assertSame(host.mRow, row);
+
+    }
+
+    @Test
+    public void playerAdapterTest() {
+        mContext = new ContextThemeWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                android.support.v17.leanback.test.R.style.Theme_Leanback);
+
+        final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class);
+        when(impl.isPrepared()).thenReturn(true);
+        when(impl.getCurrentPosition()).thenReturn(123L);
+        when(impl.getDuration()).thenReturn(20000L);
+        when(impl.getBufferedPosition()).thenReturn(321L);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackBannerControlGlueImpl(mContext, impl);
+                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+                mGlue.setHost(host);
+
+                PlaybackControlsRowPresenter presenter = (PlaybackControlsRowPresenter)
+                        mGlue.getPlaybackRowPresenter();
+                FrameLayout parent = new FrameLayout(mContext);
+                mViewHolder = (PlaybackControlsRowPresenter.ViewHolder)
+                        presenter.onCreateViewHolder(parent);
+                presenter.onBindViewHolder(mViewHolder, mGlue.getControlsRow());
+            }
+        });
+
+
+        mGlue.play();
+        Mockito.verify(impl, times(1)).play();
+        mGlue.pause();
+        Mockito.verify(impl, times(1)).pause();
+        mGlue.seekTo(123L);
+        // one call for play() and one call for seekTo()
+        Mockito.verify(impl, times(2)).seekTo(123L);
+        assertEquals(123L, mGlue.getCurrentPosition());
+        assertEquals(20000L, mGlue.getDuration());
+        assertEquals(321L, mGlue.getBufferedPosition());
+
+        assertSame(mGlue.mAdapterCallback, impl.getCallback());
+
+        when(impl.getCurrentPosition()).thenReturn(124L);
+        impl.getCallback().onCurrentPositionChanged(impl);
+        assertEquals(124L, mGlue.getControlsRow().getCurrentPosition());
+
+        when(impl.getBufferedPosition()).thenReturn(333L);
+        impl.getCallback().onBufferedPositionChanged(impl);
+        assertEquals(333L, mGlue.getControlsRow().getBufferedPosition());
+
+        when(impl.getDuration()).thenReturn((long) (Integer.MAX_VALUE) * 2);
+        impl.getCallback().onDurationChanged(impl);
+        assertEquals((long) (Integer.MAX_VALUE) * 2, mGlue.getControlsRow().getDuration());
+
+    }
+
+    @Test
+    public void savePlayerAdapterEventBeforeAttachToHost() {
+        mContext = new ContextThemeWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                android.support.v17.leanback.test.R.style.Theme_Leanback);
+
+        final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class);
+        when(impl.isPrepared()).thenReturn(true);
+        when(impl.getCurrentPosition()).thenReturn(123L);
+        when(impl.getDuration()).thenReturn(20000L);
+        when(impl.getBufferedPosition()).thenReturn(321L);
+        final PlaybackGlueHost.PlayerCallback hostCallback = Mockito.mock(
+                PlaybackGlueHost.PlayerCallback.class);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackBannerControlGlueImpl(mContext, impl);
+                // fire events before attach to host.
+                impl.getCallback().onBufferingStateChanged(impl, true);
+                impl.getCallback().onVideoSizeChanged(impl, 200, 150);
+                impl.getCallback().onError(impl, 12, "abc");
+                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+                host.setPlayerCallback(hostCallback);
+                mGlue.setHost(host);
+            }
+        });
+
+        // when attach to host, should pass the buffering state, video size and last error message
+        // to the host.
+        Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(true);
+        Mockito.verify(hostCallback, times(1)).onVideoSizeChanged(200, 150);
+        Mockito.verify(hostCallback, times(1)).onError(12, "abc");
+        Mockito.reset(hostCallback);
+
+        final PlaybackGlueHost.PlayerCallback hostCallback2 = Mockito.mock(
+                PlaybackGlueHost.PlayerCallback.class);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+                host.setPlayerCallback(hostCallback2);
+                mGlue.setHost(host);
+            }
+        });
+
+        // when detach from host, should have host stop buffering.
+        Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(false);
+        Mockito.verify(hostCallback, times(0)).onVideoSizeChanged(anyInt(), anyInt());
+        Mockito.verify(hostCallback, times(0)).onError(anyInt(), anyString());
+
+        // attach to a different host, buffering state and video size should be saved, one time
+        // error state is not saved.
+        Mockito.verify(hostCallback2, times(1)).onBufferingStateChanged(true);
+        Mockito.verify(hostCallback2, times(1)).onVideoSizeChanged(200, 150);
+        Mockito.verify(hostCallback2, times(0)).onError(anyInt(), anyString());
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueHostImpl.java b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueHostImpl.java
index 2c9aa43..0ad6ede 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueHostImpl.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueHostImpl.java
@@ -23,9 +23,10 @@
  */
 public class PlaybackGlueHostImpl extends PlaybackGlueHost {
 
-    HostCallback mHostCallback;
-    Row mRow;
-    PlaybackRowPresenter mPlaybackRowPresenter;
+    protected HostCallback mHostCallback;
+    protected Row mRow;
+    protected PlaybackRowPresenter mPlaybackRowPresenter;
+    protected PlayerCallback mPlayerCallback;
 
     @Override
     public void setHostCallback(HostCallback callback) {
@@ -71,4 +72,13 @@
     public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
         mPlaybackRowPresenter = presenter;
     }
+
+    @Override
+    public PlayerCallback getPlayerCallback() {
+        return mPlayerCallback;
+    }
+
+    public void setPlayerCallback(PlayerCallback playerCallback) {
+        mPlayerCallback = playerCallback;
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueTest.java b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueTest.java
index 3932ea6..0f96196 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackGlueTest.java
@@ -16,8 +16,10 @@
 
 package android.support.v17.leanback.media;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.times;
 
 import android.content.Context;
@@ -78,4 +80,38 @@
         Mockito.verify(glue2, times(1)).onDetachedFromHost();
     }
 
+    @Test
+    public void listenerModification() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        PlaybackGlue glue = Mockito.spy(new PlaybackGlueImpl(context));
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        glue.setHost(host);
+        final boolean[] called = new boolean[] {false, false};
+        glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            @Override
+            public void onPreparedStateChanged(PlaybackGlue glue) {
+                called[0] = true;
+            }
+        });
+        glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            @Override
+            public void onPreparedStateChanged(PlaybackGlue glue) {
+                glue.removePlayerCallback(this);
+            }
+        });
+        glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
+            @Override
+            public void onPreparedStateChanged(PlaybackGlue glue) {
+                called[1] = true;
+            }
+        });
+
+        for (PlaybackGlue.PlayerCallback callback: glue.getPlayerCallbacks()) {
+            callback.onPreparedStateChanged(glue);
+        }
+        assertTrue(called[0]);
+        assertTrue(called[1]);
+        assertEquals(2, glue.getPlayerCallbacks().size());
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackTransportControlGlueTest.java b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackTransportControlGlueTest.java
new file mode 100644
index 0000000..d6ce824
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/media/PlaybackTransportControlGlueTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.media;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.PlaybackTransportRowPresenter;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.view.ContextThemeWrapper;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SmallTest
+public class PlaybackTransportControlGlueTest {
+
+    public static class PlayerAdapterSample extends PlayerAdapter {
+        @Override
+        public void play() {
+        }
+
+        @Override
+        public void pause() {
+        }
+    }
+
+    public static class PlaybackTransportControlGlueImpl
+            extends PlaybackTransportControlGlue {
+        public PlaybackTransportControlGlueImpl(Context context) {
+            super(context, new PlayerAdapterSample());
+        }
+
+        public PlaybackTransportControlGlueImpl(Context context, PlayerAdapter impl) {
+            super(context, impl);
+        }
+    }
+
+    Context mContext;
+    PlaybackTransportControlGlueImpl mGlue;
+    PlaybackTransportRowPresenter.ViewHolder mViewHolder;
+
+    @Test
+    public void usingDefaultRowAndPresenter() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlueImpl(mContext);
+            }
+        });
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertTrue(host.mPlaybackRowPresenter instanceof PlaybackTransportRowPresenter);
+        assertTrue(host.mRow instanceof PlaybackControlsRow);
+
+    }
+    @Test
+    public void customRowPresenter() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlueImpl(mContext);
+            }
+        });
+        PlaybackRowPresenter presenter = new PlaybackRowPresenter() {
+            @Override
+            protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
+                return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext()));
+            }
+        };
+        mGlue.setPlaybackRowPresenter(presenter);
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertSame(host.mPlaybackRowPresenter, presenter);
+        assertTrue(host.mRow instanceof PlaybackControlsRow);
+
+    }
+
+    @Test
+    public void customControlsRow() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlueImpl(mContext);
+            }
+        });
+        PlaybackControlsRow row = new PlaybackControlsRow(mContext);
+        mGlue.setControlsRow(row);
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertTrue(host.mPlaybackRowPresenter instanceof PlaybackTransportRowPresenter);
+        assertSame(host.mRow, row);
+
+    }
+
+    @Test
+    public void customRowAndPresenter() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlueImpl(mContext);
+            }
+        });
+        PlaybackControlsRow row = new PlaybackControlsRow(mContext);
+        mGlue.setControlsRow(row);
+        PlaybackRowPresenter presenter = new PlaybackRowPresenter() {
+            @Override
+            protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
+                return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext()));
+            }
+        };
+        mGlue.setPlaybackRowPresenter(presenter);
+        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+
+        mGlue.setHost(host);
+        assertSame(mGlue, host.mGlue);
+        assertSame(host, mGlue.getHost());
+        assertSame(host.mPlaybackRowPresenter, presenter);
+        assertSame(host.mRow, row);
+
+    }
+
+    @Test
+    public void playerAdapterTest() {
+        mContext = new ContextThemeWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                android.support.v17.leanback.test.R.style.Theme_Leanback);
+
+        final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class);
+        when(impl.isPrepared()).thenReturn(true);
+        when(impl.getCurrentPosition()).thenReturn(123L);
+        when(impl.getDuration()).thenReturn(20000L);
+        when(impl.getBufferedPosition()).thenReturn(321L);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlueImpl(mContext, impl);
+                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+                mGlue.setHost(host);
+
+                PlaybackTransportRowPresenter presenter = (PlaybackTransportRowPresenter)
+                        mGlue.getPlaybackRowPresenter();
+                FrameLayout parent = new FrameLayout(mContext);
+                mViewHolder = (PlaybackTransportRowPresenter.ViewHolder)
+                        presenter.onCreateViewHolder(parent);
+                presenter.onBindViewHolder(mViewHolder, mGlue.getControlsRow());
+            }
+        });
+
+
+        mGlue.play();
+        Mockito.verify(impl, times(1)).play();
+        mGlue.pause();
+        Mockito.verify(impl, times(1)).pause();
+        mGlue.seekTo(123L);
+        Mockito.verify(impl, times(1)).seekTo(123L);
+        assertEquals(123L, mGlue.getCurrentPosition());
+        assertEquals(20000L, mGlue.getDuration());
+        assertEquals(321L, mGlue.getBufferedPosition());
+
+        assertSame(mGlue.mAdapterCallback, impl.getCallback());
+
+        when(impl.getCurrentPosition()).thenReturn(124L);
+        impl.getCallback().onCurrentPositionChanged(impl);
+        assertEquals(124L, mGlue.getControlsRow().getCurrentPosition());
+
+        when(impl.getBufferedPosition()).thenReturn(333L);
+        impl.getCallback().onBufferedPositionChanged(impl);
+        assertEquals(333L, mGlue.getControlsRow().getBufferedPosition());
+
+        when(impl.getDuration()).thenReturn((long) (Integer.MAX_VALUE) * 2);
+        impl.getCallback().onDurationChanged(impl);
+        assertEquals((long) (Integer.MAX_VALUE) * 2, mGlue.getControlsRow().getDuration());
+
+    }
+
+    @Test
+    public void savePlayerAdapterEventBeforeAttachToHost() {
+        mContext = new ContextThemeWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                android.support.v17.leanback.test.R.style.Theme_Leanback);
+
+        final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class);
+        when(impl.isPrepared()).thenReturn(true);
+        when(impl.getCurrentPosition()).thenReturn(123L);
+        when(impl.getDuration()).thenReturn(20000L);
+        when(impl.getBufferedPosition()).thenReturn(321L);
+        final PlaybackGlueHost.PlayerCallback hostCallback = Mockito.mock(
+                PlaybackGlueHost.PlayerCallback.class);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlueImpl(mContext, impl);
+                // fire events before attach to host.
+                impl.getCallback().onBufferingStateChanged(impl, true);
+                impl.getCallback().onVideoSizeChanged(impl, 200, 150);
+                impl.getCallback().onError(impl, 12, "abc");
+                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+                host.setPlayerCallback(hostCallback);
+                mGlue.setHost(host);
+            }
+        });
+
+        // when attach to host, should pass the buffering state, video size and last error message
+        // to the host.
+        Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(true);
+        Mockito.verify(hostCallback, times(1)).onVideoSizeChanged(200, 150);
+        Mockito.verify(hostCallback, times(1)).onError(12, "abc");
+        Mockito.reset(hostCallback);
+
+        final PlaybackGlueHost.PlayerCallback hostCallback2 = Mockito.mock(
+                PlaybackGlueHost.PlayerCallback.class);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
+                host.setPlayerCallback(hostCallback2);
+                mGlue.setHost(host);
+            }
+        });
+
+        // when detach from host, should have host stop buffering.
+        Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(false);
+        Mockito.verify(hostCallback, times(0)).onVideoSizeChanged(anyInt(), anyInt());
+        Mockito.verify(hostCallback, times(0)).onError(anyInt(), anyString());
+
+        // attach to a different host, buffering state and video size should be saved, one time
+        // error state is not saved.
+        Mockito.verify(hostCallback2, times(1)).onBufferingStateChanged(true);
+        Mockito.verify(hostCallback2, times(1)).onVideoSizeChanged(200, 150);
+        Mockito.verify(hostCallback2, times(0)).onError(anyInt(), anyString());
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java b/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
index 2f2fc5d..77a903e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
@@ -18,8 +18,6 @@
 import android.app.Activity;
 import android.view.View;
 
-import java.util.concurrent.Callable;
-
 import junit.framework.Assert;
 
 public abstract class PollingCheck {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
index f6bdf68..d9f446f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
@@ -34,6 +34,10 @@
 
     private static final String TAG = "GridActivity";
 
+    interface ImportantForAccessibilityListener {
+        void onImportantForAccessibilityChanged(View view, int newValue);
+    }
+
     interface AdapterListener {
         void onBind(RecyclerView.ViewHolder vh, int position);
     }
@@ -100,6 +104,7 @@
     int mNinePatchShadow;
 
     private int mBoundCount;
+    ImportantForAccessibilityListener mImportantForAccessibilityListener;
 
     private View createView() {
 
@@ -258,6 +263,17 @@
         mGridView.getAdapter().notifyItemMoved(index2 - 1, index1);
     }
 
+    void moveItem(int index1, int index2, boolean notify) {
+        if (index1 == index2) {
+            return;
+        }
+        int[] items = removeItems(index1, 1, false);
+        addItems(index2, items, false);
+        if (notify) {
+            mGridView.getAdapter().notifyItemMoved(index1, index2);
+        }
+    }
+
     void changeArraySize(int length) {
         mNumItems = length;
         mGridView.getAdapter().notifyDataSetChanged();
@@ -294,6 +310,10 @@
     }
 
     void addItems(int index, int[] items) {
+        addItems(index, items, true);
+    }
+
+    void addItems(int index, int[] items, boolean notify) {
         int length = items.length;
         if (mItemLengths.length < mNumItems + length) {
             int[] array = new int[mNumItems + length];
@@ -303,7 +323,7 @@
         System.arraycopy(mItemLengths, index, mItemLengths, index + length, mNumItems - index);
         System.arraycopy(items, 0, mItemLengths, index, length);
         mNumItems += length;
-        if (mGridView.getAdapter() != null) {
+        if (notify && mGridView.getAdapter() != null) {
             mGridView.getAdapter().notifyItemRangeInserted(index, length);
         }
     }
@@ -380,6 +400,15 @@
                             }
                         }
                     }
+
+                    @Override
+                    public void setImportantForAccessibility(int mode) {
+                        super.setImportantForAccessibility(mode);
+                        if (mImportantForAccessibilityListener != null) {
+                            mImportantForAccessibilityListener.onImportantForAccessibilityChanged(
+                                    this, mode);
+                        }
+                    }
                 };
                 textView.setTextColor(Color.BLACK);
                 textView.setOnFocusChangeListener(mItemFocusChangeListener);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
index 6f5529e..43793f9 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
@@ -33,12 +33,17 @@
         }
 
         @Override
+        public int getMinIndex() {
+            return 0;
+        }
+
+        @Override
         public int getCount() {
             return mCount;
         }
 
         @Override
-        public int createItem(int index, boolean append, Object[] item) {
+        public int createItem(int index, boolean append, Object[] item, boolean disappearingItem) {
             return mItems[index];
         }
 
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
index 0195ef7..61974cc 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
@@ -34,13 +35,14 @@
 import android.os.Build;
 import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.test.R;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerViewAccessibilityDelegate;
 import android.text.Selection;
@@ -62,15 +64,17 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GridWidgetTest {
 
+    private static final float DELTA = 1f;
     private static final boolean HUMAN_DELAY = false;
     private static final long WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS = 60000;
     private static final int WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS = 2000;
-    private static final int WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS = 2000;
+    private static final int WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS = 6000;
 
     protected ActivityTestRule<GridActivity> mActivityTestRule;
     protected GridActivity mActivity;
@@ -82,6 +86,7 @@
     protected int[] mRemovedItems;
 
     private final Comparator<View> mRowSortComparator = new Comparator<View>() {
+        @Override
         public int compare(View lhs, View rhs) {
             if (mOrientation == BaseGridView.HORIZONTAL) {
                 return lhs.getLeft() - rhs.getLeft();
@@ -121,6 +126,7 @@
      */
     private void changeArraySize(final int size) throws Throwable {
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mActivity.changeArraySize(size);
             }
@@ -140,14 +146,28 @@
      * Change selected position.
      */
     private void setSelectedPosition(final int position, final int scrollExtra) throws Throwable {
+        startWaitLayout();
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPosition(position, scrollExtra);
             }
         });
-        Thread.sleep(500);
+        waitForLayout(false);
     }
 
+    private void setSelectedPosition(final int position) throws Throwable {
+        setSelectedPosition(position, 0);
+    }
+
+    private void setSelectedPositionSmooth(final int position) throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(position);
+            }
+        });
+    }
     /**
      * Scrolls using given key.
      */
@@ -221,7 +241,7 @@
      */
     protected View[][] sortByRows() {
         final HashMap<Integer, ArrayList<View>> rows = new HashMap<Integer, ArrayList<View>>();
-        ArrayList<Integer> rowLocations = new ArrayList();
+        ArrayList<Integer> rowLocations = new ArrayList<>();
         for (int i = 0; i < mGridView.getChildCount(); i++) {
             View v = mGridView.getChildAt(i);
             int rowLocation;
@@ -356,6 +376,7 @@
         mActivityTestRule = new ActivityTestRule<GridActivity>(GridActivity.class, false, false);
         mActivity = mActivityTestRule.launchActivity(intent);
         mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
                 public void run() {
                     mActivity.setTitle(testName.getMethodName());
                 }
@@ -392,6 +413,14 @@
      * wait layout to be called and remove the listener.
      */
     protected void waitForLayout() {
+        waitForLayout(true);
+    }
+
+    /**
+     * wait layout to be called and remove the listener.
+     * @param force True if always wait regardless if layout requested
+     */
+    protected void waitForLayout(boolean force) {
         if (mWaitLayoutListener == null) {
             throw new IllegalStateException("startWaitLayout() not called");
         }
@@ -399,8 +428,10 @@
             throw new IllegalStateException("layout listener inconistent");
         }
         try {
-            verify(mWaitLayoutListener, timeout(WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS).atLeastOnce())
-                    .onLayoutCompleted(any(RecyclerView.State.class));
+            if (force || mGridView.isLayoutRequested()) {
+                verify(mWaitLayoutListener, timeout(WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS).atLeastOnce())
+                        .onLayoutCompleted(any(RecyclerView.State.class));
+            }
         } finally {
             mWaitLayoutListener = null;
             mLayoutManager.mLayoutCompleteListener = null;
@@ -412,12 +443,32 @@
      * To wait the ItemAnimator start, you can use waitForLayout() to make sure layout pass has
      * processed adapter change.
      */
-    protected void waitForItemAnimation() {
-        RecyclerView.ItemAnimator.ItemAnimatorFinishedListener listener = mock(
+    protected void waitForItemAnimation(int timeoutMs) throws Throwable {
+        final RecyclerView.ItemAnimator.ItemAnimatorFinishedListener listener = mock(
                 RecyclerView.ItemAnimator.ItemAnimatorFinishedListener.class);
-        if (mGridView.getItemAnimator().isRunning(listener)) {
-            verify(listener, timeout(WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS).atLeastOnce())
-                    .onAnimationsFinished();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().isRunning(listener);
+            }
+        });
+        verify(listener, timeout(timeoutMs).atLeastOnce()).onAnimationsFinished();
+    }
+
+    protected void waitForItemAnimation() throws Throwable {
+        waitForItemAnimation(WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS);
+    }
+
+    /**
+     * wait animation start
+     */
+    protected void waitForItemAnimationStart() throws Throwable {
+        long totalWait = 0;
+        while (!mGridView.getItemAnimator().isRunning()) {
+            Thread.sleep(10);
+            if ((totalWait += 10) > WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS) {
+                throw new RuntimeException("waitForItemAnimationStart Timeout");
+            }
         }
     }
 
@@ -467,7 +518,6 @@
         mNumRows = 3;
 
         scrollToEnd(mVerifyLayout);
-        verifyBoundCount(100);
 
         scrollToBegin(mVerifyLayout);
 
@@ -547,6 +597,7 @@
         final int decorationBottom = 2;
 
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop,
                         decorationRight, decorationBottom));
@@ -614,6 +665,7 @@
         assertTrue(opticalInsetsBottom > 0);
 
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop,
                         decorationRight, decorationBottom));
@@ -654,7 +706,6 @@
         mNumRows = 3;
 
         scrollToEnd(mVerifyLayout);
-        verifyBoundCount(200);
 
         scrollToBegin(mVerifyLayout);
 
@@ -683,8 +734,6 @@
 
         scrollToEnd(mVerifyLayout);
 
-        verifyBoundCount(200);
-
         scrollToBegin(mVerifyLayout);
 
         verifyBeginAligned();
@@ -709,7 +758,6 @@
         // test append without staggered result cache
         scrollToEnd(mVerifyLayout);
 
-        verifyBoundCount(100);
         int[] endEdges = getEndEdges();
 
         scrollToBegin(mVerifyLayout);
@@ -721,17 +769,668 @@
         assertEquals("Staggerd cache should be kept as is when no item size change",
                 100, ((StaggeredGrid) mLayoutManager.mGrid).mLocations.size());
 
-        mActivity.resetBoundCount();
         changeArraySize(100);
 
         scrollToEnd(mVerifyLayout);
-        verifyBoundCount(100);
 
         // we should get same aligned end edges
         int[] endEdges2 = getEndEdges();
         verifyEdgesSame(endEdges, endEdges2);
     }
 
+
+    @Test
+    public void testLayoutWhenAViewIsInvalidated() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
+        intent.putExtra(GridActivity.EXTRA_HAS_STABLE_IDS, true);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mNumRows = 1;
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        waitOneUiCycle();
+
+        // push views to cache.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.mItemLengths[0] = mActivity.mItemLengths[0] * 3;
+                mActivity.mGridView.getAdapter().notifyItemChanged(0);
+            }
+        });
+        waitForItemAnimation();
+
+        // notifyDataSetChange will mark the cached views FLAG_INVALID
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.mGridView.getAdapter().notifyDataSetChanged();
+            }
+        });
+        waitForItemAnimation();
+
+        // Cached views will be added in prelayout with FLAG_INVALID, in post layout we should
+        // handle it properly
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.mItemLengths[0] = mActivity.mItemLengths[0] / 3;
+                mActivity.mGridView.getAdapter().notifyItemChanged(0);
+            }
+        });
+
+        waitForItemAnimation();
+    }
+
+    @Test
+    public void testWrongInsertViewIndexInFastRelayout() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mNumRows = 1;
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+
+        // removing two children, they will be hidden views as first 2 children of RV.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setRemoveDuration(2000);
+                mActivity.removeItems(0, 2);
+            }
+        });
+        waitForItemAnimationStart();
+
+        // add three views and notify change of the first item.
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.addItems(0, new int[]{161, 161, 161});
+            }
+        });
+        waitForLayout();
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getAdapter().notifyItemChanged(0);
+            }
+        });
+        waitForLayout();
+        // after layout, the viewholder should still be the first child of LayoutManager.
+        assertEquals(0, mGridView.getChildAdapterPosition(
+                mGridView.getLayoutManager().getChildAt(0)));
+    }
+
+    @Test
+    public void testMoveIntoPrelayoutItems() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mNumRows = 1;
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+
+        final int lastItemPos = mGridView.getChildCount() - 1;
+        assertTrue(mGridView.getChildCount() >= 4);
+        // notify change of 3 items, so prelayout will layout extra 3 items, then move an item
+        // into the extra layout range. Post layout's fastRelayout() should handle this properly.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getAdapter().notifyItemChanged(lastItemPos - 3);
+                mGridView.getAdapter().notifyItemChanged(lastItemPos - 2);
+                mGridView.getAdapter().notifyItemChanged(lastItemPos - 1);
+                mActivity.moveItem(900, lastItemPos + 2, true);
+            }
+        });
+        waitForItemAnimation();
+    }
+
+    @Test
+    public void testMoveIntoPrelayoutItems2() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mNumRows = 1;
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+
+        setSelectedPosition(999);
+        final int firstItemPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(0));
+        assertTrue(mGridView.getChildCount() >= 4);
+        // notify change of 3 items, so prelayout will layout extra 3 items, then move an item
+        // into the extra layout range. Post layout's fastRelayout() should handle this properly.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getAdapter().notifyItemChanged(firstItemPos + 1);
+                mGridView.getAdapter().notifyItemChanged(firstItemPos + 2);
+                mGridView.getAdapter().notifyItemChanged(firstItemPos + 3);
+                mActivity.moveItem(0, firstItemPos - 2, true);
+            }
+        });
+        waitForItemAnimation();
+    }
+
+    void preparePredictiveLayout() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setAddDuration(1000);
+                mGridView.getItemAnimator().setRemoveDuration(1000);
+                mGridView.getItemAnimator().setMoveDuration(1000);
+                mGridView.getItemAnimator().setChangeDuration(1000);
+                mGridView.setSelectedPositionSmooth(50);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+    }
+
+    @Test
+    public void testPredictiveLayoutAdd1() throws Throwable {
+        preparePredictiveLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.addItems(51, new int[]{300, 300, 300, 300});
+            }
+        });
+        waitForItemAnimationStart();
+        waitForItemAnimation();
+        assertEquals(50, mGridView.getSelectedPosition());
+        assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
+    }
+
+    @Test
+    public void testPredictiveLayoutAdd2() throws Throwable {
+        preparePredictiveLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.addItems(50, new int[]{300, 300, 300, 300});
+            }
+        });
+        waitForItemAnimationStart();
+        waitForItemAnimation();
+        assertEquals(54, mGridView.getSelectedPosition());
+        assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
+    }
+
+    @Test
+    public void testPredictiveLayoutRemove1() throws Throwable {
+        preparePredictiveLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(51, 3);
+            }
+        });
+        waitForItemAnimationStart();
+        waitForItemAnimation();
+        assertEquals(50, mGridView.getSelectedPosition());
+        assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
+    }
+
+    @Test
+    public void testPredictiveLayoutRemove2() throws Throwable {
+        preparePredictiveLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(47, 3);
+            }
+        });
+        waitForItemAnimationStart();
+        waitForItemAnimation();
+        assertEquals(47, mGridView.getSelectedPosition());
+        assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
+    }
+
+    @Test
+    public void testPredictiveLayoutRemove3() throws Throwable {
+        preparePredictiveLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(0, 51);
+            }
+        });
+        waitForItemAnimationStart();
+        waitForItemAnimation();
+        assertEquals(0, mGridView.getSelectedPosition());
+        assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
+    }
+
+    @Test
+    public void testPredictiveOnMeasureWrapContent() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear_wrap_content);
+        int count = 50;
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, count);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        waitForScrollIdle(mVerifyLayout);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setHasFixedSize(false);
+            }
+        });
+
+        for (int i = 0; i < 30; i++) {
+            final int oldCount = count;
+            final int newCount = i;
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (oldCount > 0) {
+                        mActivity.removeItems(0, oldCount);
+                    }
+                    if (newCount > 0) {
+                        int[] newItems = new int[newCount];
+                        for (int i = 0; i < newCount; i++) {
+                            newItems[i] = 400;
+                        }
+                        mActivity.addItems(0, newItems);
+                    }
+                }
+            });
+            waitForItemAnimationStart();
+            waitForItemAnimation();
+            count = newCount;
+        }
+
+    }
+
+    @Test
+    public void testPredictiveLayoutRemove4() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(50);
+            }
+        });
+        waitForScrollIdle();
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(0, 49);
+            }
+        });
+        assertEquals(1, mGridView.getSelectedPosition());
+    }
+
+    @Test
+    public void testPredictiveLayoutRemove5() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(50);
+            }
+        });
+        waitForScrollIdle();
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(50, 40);
+            }
+        });
+        assertEquals(50, mGridView.getSelectedPosition());
+        scrollToBegin(mVerifyLayout);
+        verifyBeginAligned();
+    }
+
+    void waitOneUiCycle() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+            }
+        });
+    }
+
+    @Test
+    public void testDontPruneMovingItem() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setMoveDuration(2000);
+                mGridView.setSelectedPosition(50);
+            }
+        });
+        waitForScrollIdle();
+        final ArrayList<RecyclerView.ViewHolder> moveViewHolders = new ArrayList();
+        for (int i = 51;; i++) {
+            RecyclerView.ViewHolder vh = mGridView.findViewHolderForAdapterPosition(i);
+            if (vh == null) {
+                break;
+            }
+            moveViewHolders.add(vh);
+        }
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // add a lot of items, so we will push everything to right of 51 out side window
+                int[] lots_items = new int[1000];
+                for (int i = 0; i < lots_items.length; i++) {
+                    lots_items[i] = 300;
+                }
+                mActivity.addItems(51, lots_items);
+            }
+        });
+        waitOneUiCycle();
+        // run a scroll pass, the scroll pass should not remove the animating views even they are
+        // outside visible areas.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.scrollBy(-3, 0);
+            }
+        });
+        waitOneUiCycle();
+        for (int i = 0; i < moveViewHolders.size(); i++) {
+            assertSame(mGridView, moveViewHolders.get(i).itemView.getParent());
+        }
+    }
+
+    @Test
+    public void testMoveItemToTheRight() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setAddDuration(2000);
+                mGridView.getItemAnimator().setMoveDuration(2000);
+                mGridView.setSelectedPosition(50);
+            }
+        });
+        waitForScrollIdle();
+        RecyclerView.ViewHolder moveViewHolder = mGridView.findViewHolderForAdapterPosition(51);
+
+        int lastPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(
+                mGridView.getChildCount() - 1));
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.moveItem(51, 1000, true);
+            }
+        });
+        final ArrayList<View> moveInViewHolders = new ArrayList();
+        waitForItemAnimationStart();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < mGridView.getLayoutManager().getChildCount(); i++) {
+                    View v = mGridView.getLayoutManager().getChildAt(i);
+                    if (mGridView.getChildAdapterPosition(v) >= 51) {
+                        moveInViewHolders.add(v);
+                    }
+                }
+            }
+        });
+        waitOneUiCycle();
+        assertTrue("prelayout should layout extra items to slide in",
+                moveInViewHolders.size() > lastPos - 51);
+        // run a scroll pass, the scroll pass should not remove the animating views even they are
+        // outside visible areas.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.scrollBy(-3, 0);
+            }
+        });
+        waitOneUiCycle();
+        for (int i = 0; i < moveInViewHolders.size(); i++) {
+            assertSame(mGridView, moveInViewHolders.get(i).getParent());
+        }
+        assertSame(mGridView, moveViewHolder.itemView.getParent());
+        assertFalse(moveViewHolder.isRecyclable());
+        waitForItemAnimation();
+        assertNull(moveViewHolder.itemView.getParent());
+        assertTrue(moveViewHolder.isRecyclable());
+    }
+
+    @Test
+    public void testMoveItemToTheLeft() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setAddDuration(2000);
+                mGridView.getItemAnimator().setMoveDuration(2000);
+                mGridView.setSelectedPosition(1500);
+            }
+        });
+        waitForScrollIdle();
+        RecyclerView.ViewHolder moveViewHolder = mGridView.findViewHolderForAdapterPosition(1499);
+
+        int firstPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(0));
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.moveItem(1499, 1, true);
+            }
+        });
+        final ArrayList<View> moveInViewHolders = new ArrayList();
+        waitForItemAnimationStart();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < mGridView.getLayoutManager().getChildCount(); i++) {
+                    View v = mGridView.getLayoutManager().getChildAt(i);
+                    if (mGridView.getChildAdapterPosition(v) <= 1499) {
+                        moveInViewHolders.add(v);
+                    }
+                }
+            }
+        });
+        waitOneUiCycle();
+        assertTrue("prelayout should layout extra items to slide in ",
+                moveInViewHolders.size() > 1499 - firstPos);
+        // run a scroll pass, the scroll pass should not remove the animating views even they are
+        // outside visible areas.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.scrollBy(3, 0);
+            }
+        });
+        waitOneUiCycle();
+        for (int i = 0; i < moveInViewHolders.size(); i++) {
+            assertSame(mGridView, moveInViewHolders.get(i).getParent());
+        }
+        assertSame(mGridView, moveViewHolder.itemView.getParent());
+        assertFalse(moveViewHolder.isRecyclable());
+        waitForItemAnimation();
+        assertNull(moveViewHolder.itemView.getParent());
+        assertTrue(moveViewHolder.isRecyclable());
+    }
+
+    @Test
+    public void testContinuousSwapForward() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(150);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        for (int i = 150; i < 199; i++) {
+            final int swapIndex = i;
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mActivity.swap(swapIndex, swapIndex + 1);
+                }
+            });
+            Thread.sleep(10);
+        }
+        waitForItemAnimation();
+        assertEquals(199, mGridView.getSelectedPosition());
+        // check if ItemAnimation finishes at aligned positions:
+        int leftEdge = mGridView.getLayoutManager().findViewByPosition(199).getLeft();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestLayout();
+            }
+        });
+        waitForScrollIdle();
+        assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(199).getLeft());
+    }
+
+    @Test
+    public void testContinuousSwapBackward() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(50);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        for (int i = 50; i > 0; i--) {
+            final int swapIndex = i;
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mActivity.swap(swapIndex, swapIndex - 1);
+                }
+            });
+            Thread.sleep(10);
+        }
+        waitForItemAnimation();
+        assertEquals(0, mGridView.getSelectedPosition());
+        // check if ItemAnimation finishes at aligned positions:
+        int leftEdge = mGridView.getLayoutManager().findViewByPosition(0).getLeft();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestLayout();
+            }
+        });
+        waitForScrollIdle();
+        assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(0).getLeft());
+    }
+
+    @Test
+    public void testSwapAfterScroll() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setMoveDuration(1000);
+                mGridView.setSelectedPositionSmooth(150);
+            }
+        });
+        waitForScrollIdle();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(151);
+            }
+        });
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // we want to swap and select new target which is at 150 before swap
+                mGridView.setSelectedPositionSmooth(150);
+                mActivity.swap(150, 151);
+            }
+        });
+        waitForItemAnimation();
+        waitForScrollIdle();
+        assertEquals(151, mGridView.getSelectedPosition());
+        // check if ItemAnimation finishes at aligned positions:
+        int leftEdge = mGridView.getLayoutManager().findViewByPosition(151).getLeft();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestLayout();
+            }
+        });
+        waitForScrollIdle();
+        assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(151).getLeft());
+    }
+
     @Test
     public void testItemMovedHorizontal() throws Throwable {
         Intent intent = new Intent();
@@ -742,7 +1441,12 @@
         mOrientation = BaseGridView.HORIZONTAL;
         mNumRows = 3;
 
-        mGridView.setSelectedPositionSmooth(150);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(150);
+            }
+        });
         waitForScrollIdle(mVerifyLayout);
         performAndWaitForAnimation(new Runnable() {
             @Override
@@ -758,6 +1462,122 @@
     }
 
     @Test
+    public void testItemMovedHorizontalRtl() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear_rtl);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_ITEMS, new int[] {40, 40, 40});
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.moveItem(0, 1, true);
+            }
+        });
+        assertEquals(mGridView.getWidth() - mGridView.getPaddingRight(),
+                mGridView.findViewHolderForAdapterPosition(0).itemView.getRight());
+    }
+
+    @Test
+    public void testScrollSecondaryCannotScroll() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+        final int topPadding = 2;
+        final int bottomPadding = 2;
+        final int height = mGridView.getHeight();
+        final int spacing = 2;
+        final int rowHeight = (height - topPadding - bottomPadding) / 4 - spacing;
+        final HorizontalGridView horizontalGridView = (HorizontalGridView) mGridView;
+
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                horizontalGridView.setPadding(0, topPadding, 0, bottomPadding);
+                horizontalGridView.setItemSpacing(spacing);
+                horizontalGridView.setNumRows(mNumRows);
+                horizontalGridView.setRowHeight(rowHeight);
+            }
+        });
+        waitForLayout();
+        // navigate vertically in first column, first row should always be aligned to top padding
+        for (int i = 0; i < 3; i++) {
+            setSelectedPosition(i);
+            assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(0).itemView
+                    .getTop());
+        }
+        // navigate vertically in 100th column, first row should always be aligned to top padding
+        for (int i = 300; i < 301; i++) {
+            setSelectedPosition(i);
+            assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(300).itemView
+                    .getTop());
+        }
+    }
+
+    @Test
+    public void testScrollSecondaryNeedScroll() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        // test a lot of rows so we have to scroll vertically to reach
+        mNumRows = 9;
+        final int topPadding = 2;
+        final int bottomPadding = 2;
+        final int height = mGridView.getHeight();
+        final int spacing = 2;
+        final int rowHeight = (height - topPadding - bottomPadding) / 4 - spacing;
+        final HorizontalGridView horizontalGridView = (HorizontalGridView) mGridView;
+
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                horizontalGridView.setPadding(0, topPadding, 0, bottomPadding);
+                horizontalGridView.setItemSpacing(spacing);
+                horizontalGridView.setNumRows(mNumRows);
+                horizontalGridView.setRowHeight(rowHeight);
+            }
+        });
+        waitForLayout();
+        View view;
+        // first row should be aligned to top padding
+        setSelectedPosition(0);
+        assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(0).itemView.getTop());
+        // middle row should be aligned to keyline (1/2 of screen height)
+        setSelectedPosition(4);
+        view = mGridView.findViewHolderForAdapterPosition(4).itemView;
+        assertEquals(height / 2, (view.getTop() + view.getBottom()) / 2);
+        // last row should be aligned to bottom padding.
+        setSelectedPosition(8);
+        view = mGridView.findViewHolderForAdapterPosition(8).itemView;
+        assertEquals(height, view.getTop() + rowHeight + bottomPadding);
+        setSelectedPositionSmooth(4);
+        waitForScrollIdle();
+        // middle row should be aligned to keyline (1/2 of screen height)
+        setSelectedPosition(4);
+        view = mGridView.findViewHolderForAdapterPosition(4).itemView;
+        assertEquals(height / 2, (view.getTop() + view.getBottom()) / 2);
+        // first row should be aligned to top padding
+        setSelectedPositionSmooth(0);
+        waitForScrollIdle();
+        assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(0).itemView.getTop());
+    }
+
+    @Test
     public void testItemMovedVertical() throws Throwable {
 
         Intent intent = new Intent();
@@ -908,16 +1728,19 @@
         final int focusToIndex = 49;
         final ViewGroup parent = (ViewGroup) mGridView.getParent();
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 parent.removeView(mGridView);
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 parent.addView(mGridView);
                 mGridView.requestFocus();
@@ -929,16 +1752,19 @@
 
         final int focusToIndex2 = 0;
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 parent.removeView(mGridView);
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPosition(focusToIndex2);
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 parent.addView(mGridView);
                 mGridView.requestFocus();
@@ -962,6 +1788,7 @@
 
         final int focusToIndex = mGridView.getChildCount() - 1;
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
@@ -969,6 +1796,7 @@
 
         waitForScrollIdle();
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex + 1);
             }
@@ -976,6 +1804,7 @@
         // let the scroll running for a while and requestLayout during scroll
         Thread.sleep(80);
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 assertEquals(mGridView.getScrollState(), BaseGridView.SCROLL_STATE_SETTLING);
                 mGridView.requestLayout();
@@ -986,6 +1815,7 @@
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestLayout();
             }
@@ -1008,6 +1838,7 @@
 
         final int focusToIndex = mGridView.getChildCount() - 1;
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
@@ -1020,10 +1851,12 @@
             }
         });
 
+        waitOneUiCycle();
         waitForScrollIdle();
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestLayout();
             }
@@ -1051,6 +1884,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(150);
             }
@@ -1060,18 +1894,30 @@
         View view =  mGridView.getChildAt(mGridView.getChildCount() - 1);
         final int focusToIndex = mGridView.getChildAdapterPosition(view);
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        performAndWaitForAnimation(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 int[] newItems = new int[]{300, 300, 300};
                 mActivity.addItems(0, newItems);
             }
         });
+        waitForScrollIdle();
+        int topEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getTop();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestLayout();
+            }
+        });
+        waitForScrollIdle();
+        assertEquals(topEdge,
+                mGridView.getLayoutManager().findViewByPosition(focusToIndex).getTop());
     }
 
     @Test
@@ -1092,6 +1938,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(150);
             }
@@ -1101,6 +1948,7 @@
         View view =  mGridView.getChildAt(mGridView.getChildCount() - 1);
         final int focusToIndex = mGridView.getChildAdapterPosition(view);
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
@@ -1121,19 +1969,21 @@
         Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
-        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300);
         initActivity(intent);
         mOrientation = BaseGridView.HORIZONTAL;
         mNumRows = 1;
 
-        final int focusToIndex = 40;
+        final int focusToIndex = 200;
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mActivity.removeItems(focusToIndex, 1);
             }
@@ -1145,6 +1995,7 @@
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestLayout();
             }
@@ -1160,13 +2011,14 @@
         Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
-        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300);
         initActivity(intent);
         mOrientation = BaseGridView.HORIZONTAL;
         mNumRows = 1;
 
-        final int focusToIndex = 40;
+        final int focusToIndex = 200;
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
@@ -1174,6 +2026,7 @@
 
         startWaitLayout();
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 final int removeIndex = mGridView.getChildViewHolder(
                         mGridView.getChildAt(mGridView.getChildCount() - 1)).getAdapterPosition();
@@ -1182,12 +2035,14 @@
         });
         waitForLayout();
 
-        assertFalse("removing the index of attached child should kill smooth scroller",
+        assertTrue("removing the index of attached child should not kill smooth scroller",
                 mGridView.getLayoutManager().isSmoothScrolling());
         waitForItemAnimation();
+        waitForScrollIdle();
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestLayout();
             }
@@ -1219,13 +2074,15 @@
         assertTrue(mGridView.getChildAt(0).hasFocus());
 
         // Pressing lots of key to make sure smooth scroller is running
-        for (int i = 0; i < 20; i++) {
+        mGridView.mLayoutManager.mMaxPendingMoves = 100;
+        for (int i = 0; i < 100; i++) {
             sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         }
 
         assertTrue(mGridView.getLayoutManager().isSmoothScrolling());
         startWaitLayout();
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 final int removeIndex = mGridView.getChildViewHolder(
                         mGridView.getChildAt(mGridView.getChildCount() - 1)).getAdapterPosition();
@@ -1234,14 +2091,16 @@
         });
         waitForLayout();
 
-        assertFalse("removing the index of attached child should kill smooth scroller",
+        assertTrue("removing the index of attached child should not kill smooth scroller",
                 mGridView.getLayoutManager().isSmoothScrolling());
 
         waitForItemAnimation();
+        waitForScrollIdle();
         int focusIndex = mGridView.getSelectedPosition();
         int topEdge = mGridView.getLayoutManager().findViewByPosition(focusIndex).getTop();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestLayout();
             }
@@ -1263,6 +2122,7 @@
         mNumRows = 3;
 
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mRemovedItems = mActivity.removeItems(0, 200);
             }
@@ -1270,6 +2130,7 @@
 
         humanDelay(500);
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mActivity.addItems(0, mRemovedItems);
             }
@@ -1740,6 +2601,7 @@
 
         final boolean[] scrolled = new boolean[]{false};
         mGridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
             public void onScrolled(RecyclerView recyclerView, int dx, int dy){
                 if (dy > 0) {
                     scrolled[0] = true;
@@ -1772,6 +2634,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(3);
             }
@@ -1812,6 +2675,7 @@
 
         // scroll to invisible item that is far away.
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(100);
             }
@@ -1839,6 +2703,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(10);
             }
@@ -1880,6 +2745,7 @@
 
         // scroll to invisible item that is far away.
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(200);
             }
@@ -1942,6 +2808,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(10);
             }
@@ -1983,6 +2850,7 @@
 
         // scroll to invisible item that is far away.
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(200);
             }
@@ -2010,6 +2878,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(30);
             }
@@ -2049,6 +2918,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(10);
             }
@@ -2088,6 +2958,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(99);
             }
@@ -2097,12 +2968,14 @@
 
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(50);
             }
         });
         Thread.sleep(100);
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestLayout();
                 mGridView.setSelectedPositionSmooth(0);
@@ -2169,11 +3042,13 @@
         int targetPosition = items.length - 1;
         mGridView.setSelectedPositionSmooth(targetPosition);
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.stopScroll();
             }
         });
-        Thread.sleep(100);
+        waitForScrollIdle();
+        waitForItemAnimation();
         assertEquals(mGridView.getSelectedPosition(), targetPosition);
         assertSame(mGridView.getLayoutManager().findViewByPosition(targetPosition),
                 mGridView.findFocus());
@@ -2202,6 +3077,7 @@
         mActivity.addItems(items.length, new int[]{300});
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 ((VerticalGridView) mGridView).setNumColumns(2);
             }
@@ -2230,6 +3106,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(1);
             }
@@ -2261,6 +3138,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(0);
             }
@@ -2269,6 +3147,7 @@
         verifyMargin();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(1);
             }
@@ -2293,6 +3172,7 @@
         initActivity(intent);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mActivity.attachToNewAdapter(new int[0]);
             }
@@ -2300,7 +3180,6 @@
 
     }
 
-
     @Test
     public void testZeroFixedSecondarySize() throws Throwable {
         Intent intent = new Intent();
@@ -2342,6 +3221,7 @@
 
         // 1 Save view states
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0))
                         .getText()), 0, 1);
@@ -2353,6 +3233,7 @@
 
         // 2 Change view states
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0))
                         .getText()), 1, 2);
@@ -2363,6 +3244,7 @@
 
         // 3 Detached and re-attached,  should still maintain state of (2)
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(1);
             }
@@ -2375,12 +3257,14 @@
 
         // 4 Recycled and rebound, should load state from (2)
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(20);
             }
         });
         waitForScrollIdle(mVerifyLayout);
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setSelectedPositionSmooth(0);
             }
@@ -2414,6 +3298,7 @@
 
         // 1. Set text selection, save view states should do nothing on child
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 for (int i = 0; i < mGridView.getChildCount(); i++) {
                     Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(i))
@@ -2425,6 +3310,7 @@
 
         // 2. clear the text selection
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 for (int i = 0; i < mGridView.getChildCount(); i++) {
                     Selection.removeSelection((Spannable)(((TextView) mGridView.getChildAt(i))
@@ -2435,6 +3321,7 @@
 
         // 3. Restore view states should be a no-op for child
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.restoreHierarchyState(container);
                 for (int i = 0; i < mGridView.getChildCount(); i++) {
@@ -2687,6 +3574,7 @@
         mNumRows = 1;
 
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mActivity.addItems(0, new int[]{300, 300});
                 mGridView.setSelectedPosition(0);
@@ -2717,11 +3605,13 @@
         final View firstView = mGridView.getLayoutManager().findViewByPosition(0);
         final View[] selectedViewByTask = new View[1];
         final ViewHolderTask task = new ViewHolderTask() {
+            @Override
             public void run(RecyclerView.ViewHolder viewHolder) {
                 selectedViewByTask[0] = viewHolder.itemView;
             }
         };
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mActivity.removeItems(0, 1);
                 if (smooth) {
@@ -2752,12 +3642,14 @@
 
         final ArrayList<Integer> selectedLog = new ArrayList<Integer>();
         mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
+            @Override
             public void onChildSelected(ViewGroup parent, View view, int position, long id) {
                 selectedLog.add(position);
             }
         });
 
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 ChangeableViewTypesProvider.setViewType(0, 1);
                 mGridView.getAdapter().notifyItemChanged(0, 1);
@@ -2779,6 +3671,7 @@
         mNumRows = 1;
 
         performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mActivity.addItems(0, new int[]{300, 300});
                 mGridView.setSelectedPositionSmooth(0);
@@ -2804,6 +3697,7 @@
         // add extra layout space
         startWaitLayout();
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setExtraLayoutSpace(extraLayoutSize);
             }
@@ -2822,6 +3716,7 @@
 
         // clear extra layout space
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.setExtraLayoutSpace(0);
                 verifyMargin();
@@ -2891,7 +3786,7 @@
         mNumRows = 1;
 
         assertEquals(mGridView.getSelectedPosition(), 0);
-        final SparseArray states = new SparseArray();
+        final SparseArray<Parcelable> states = new SparseArray<>();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -2973,6 +3868,7 @@
         // item0 and item2 for the best match of focusSearch(FOCUS_LEFT).  The grid widget
         // must override default addFocusables(), not to add item0 or item2.
         mActivity.mAdapterListener = new GridActivity.AdapterListener() {
+            @Override
             public void onBind(RecyclerView.ViewHolder vh, int position) {
                 if (position == 1) {
                     vh.itemView.setPaddingRelative(500, 0, 0, 0);
@@ -3055,6 +3951,108 @@
         assertTrue(selectedPosition2 < selectedPosition1);
     }
 
+    @Test
+    public void testAccessibilityScrollForwardHalfVisible() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom);
+        intent.putExtra(GridActivity.EXTRA_ITEMS,  new int[]{});
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        int height = mGridView.getHeight() - mGridView.getPaddingTop()
+                - mGridView.getPaddingBottom();
+        final int childHeight = height - mGridView.getVerticalSpacing() - 100;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffset(100);
+                mGridView.setWindowAlignmentOffsetPercent(BaseGridView
+                        .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
+                mGridView.setItemAlignmentOffset(0);
+                mGridView.setItemAlignmentOffsetPercent(BaseGridView
+                        .ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
+            }
+        });
+        mActivity.addItems(0, new int[]{childHeight, childHeight});
+        waitForItemAnimation();
+        setSelectedPosition(0);
+
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        assertTrue("test sanity", info.isScrollable());
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.performAccessibilityAction(mGridView,
+                        AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(1, mGridView.getSelectedPosition());
+    }
+
+    @Test
+    public void testAccessibilityScrollBackwardHalfVisible() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_top);
+        intent.putExtra(GridActivity.EXTRA_ITEMS,  new int[]{});
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        int height = mGridView.getHeight() - mGridView.getPaddingTop()
+                - mGridView.getPaddingBottom();
+        final int childHeight = height - mGridView.getVerticalSpacing() - 100;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffset(100);
+                mGridView.setWindowAlignmentOffsetPercent(BaseGridView
+                        .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
+                mGridView.setItemAlignmentOffset(0);
+                mGridView.setItemAlignmentOffsetPercent(BaseGridView
+                        .ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
+            }
+        });
+        mActivity.addItems(0, new int[]{childHeight, childHeight});
+        waitForItemAnimation();
+        setSelectedPosition(1);
+
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        assertTrue("test sanity", info.isScrollable());
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.performAccessibilityAction(mGridView,
+                        AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(0, mGridView.getSelectedPosition());
+    }
+
     void slideInAndWaitIdle() throws Throwable {
         slideInAndWaitIdle(5000);
     }
@@ -3062,6 +4060,7 @@
     void slideInAndWaitIdle(long timeout) throws Throwable {
         // animateIn() would reset position
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateIn();
             }
@@ -3095,6 +4094,7 @@
                 mGridView.getChildAt(0).getTop());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
             }
@@ -3108,6 +4108,7 @@
         });
         // scrollToPosition() should not affect slideOut status
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.scrollToPosition(0);
             }
@@ -3146,6 +4147,7 @@
                 mGridView.getChildAt(0).getTop());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
             }
@@ -3159,6 +4161,7 @@
         });
         // smoothScrollToPosition() should not affect slideOut status
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.smoothScrollToPosition(29);
             }
@@ -3198,6 +4201,7 @@
                 mGridView.getChildAt(0).getTop());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
             }
@@ -3211,6 +4215,7 @@
         });
         // smoothScrollToPosition() should not affect slideOut status
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.scrollToPosition(29);
             }
@@ -3250,6 +4255,7 @@
                 mGridView.getChildAt(0).getTop());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
             }
@@ -3263,6 +4269,7 @@
         });
         // change adapter should not affect slideOut status
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mActivity.changeItem(0, 200);
             }
@@ -3310,6 +4317,7 @@
                 mGridView.getChildAt(0).getTop());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
                 mActivity.findViewById(R.id.button).requestFocus();
@@ -3323,6 +4331,7 @@
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.requestFocus();
             }
@@ -3361,6 +4370,7 @@
                 mGridView.getChildAt(0).getLeft());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
             }
@@ -3372,6 +4382,7 @@
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.scrollToPosition(0);
             }
@@ -3413,6 +4424,7 @@
                 mGridView.getChildAt(0).getRight());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.animateOut();
             }
@@ -3425,6 +4437,7 @@
             }
         });
         mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mGridView.smoothScrollToPosition(0);
             }
@@ -3518,5 +4531,770 @@
         Thread.sleep(500);
         assertEquals(-1, mGridView.getSelectedPosition());
     }
-}
 
+    static void assertNoCollectionItemInfo(AccessibilityNodeInfoCompat info) {
+        AccessibilityNodeInfoCompat.CollectionItemInfoCompat nodeInfoCompat =
+                info.getCollectionItemInfo();
+        if (nodeInfoCompat == null) {
+            return;
+        }
+        assertTrue(nodeInfoCompat.getRowIndex() < 0);
+        assertTrue(nodeInfoCompat.getColumnIndex() < 0);
+    }
+
+    /**
+     * This test would need talkback on.
+     */
+    @Test
+    public void testAccessibilityOfItemsBeingPushedOut() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        initActivity(intent);
+
+        final int lastPos = mGridView.getChildAdapterPosition(
+                mGridView.getChildAt(mGridView.getChildCount() - 1));
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getLayoutManager().setItemPrefetchEnabled(false);
+            }
+        });
+        final int numItemsToPushOut = mNumRows;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // set longer enough so that accessibility service will initialize node
+                // within setImportantForAccessibility().
+                mGridView.getItemAnimator().setRemoveDuration(2000);
+                mGridView.getItemAnimator().setAddDuration(2000);
+                final int[] newItems = new int[numItemsToPushOut];
+                final int newItemValue = mActivity.mItemLengths[0];
+                for (int i = 0; i < newItems.length; i++) {
+                    newItems[i] = newItemValue;
+                }
+                mActivity.addItems(lastPos - numItemsToPushOut + 1, newItems);
+            }
+        });
+        waitForItemAnimation();
+    }
+
+    /**
+     * This test simulates talkback by calling setImportanceForAccessibility at end of animation
+     */
+    @Test
+    public void simulatesAccessibilityOfItemsBeingPushedOut() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        initActivity(intent);
+
+        final HashSet<View> moveAnimationViews = new HashSet();
+        mActivity.mImportantForAccessibilityListener =
+                new GridActivity.ImportantForAccessibilityListener() {
+            RecyclerView.LayoutManager mLM = mGridView.getLayoutManager();
+            @Override
+            public void onImportantForAccessibilityChanged(View view, int newValue) {
+                // simulates talkack, having setImportantForAccessibility to call
+                // onInitializeAccessibilityNodeInfoForItem() for the DISAPPEARING items.
+                if (moveAnimationViews.contains(view)) {
+                    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+                    mLM.onInitializeAccessibilityNodeInfoForItem(
+                            null, null, view, info);
+                }
+            }
+        };
+        final int lastPos = mGridView.getChildAdapterPosition(
+                mGridView.getChildAt(mGridView.getChildCount() - 1));
+        final int numItemsToPushOut = mNumRows;
+        for (int i = 0; i < numItemsToPushOut; i++) {
+            moveAnimationViews.add(
+                    mGridView.getChildAt(mGridView.getChildCount() - 1 - i));
+        }
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setItemAnimator(new DefaultItemAnimator() {
+                    @Override
+                    public void onMoveFinished(RecyclerView.ViewHolder item) {
+                        moveAnimationViews.remove(item.itemView);
+                    }
+                });
+                mGridView.getLayoutManager().setItemPrefetchEnabled(false);
+            }
+        });
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final int[] newItems = new int[numItemsToPushOut];
+                final int newItemValue = mActivity.mItemLengths[0] + 1;
+                for (int i = 0; i < newItems.length; i++) {
+                    newItems[i] = newItemValue;
+                }
+                mActivity.addItems(lastPos - numItemsToPushOut + 1, newItems);
+            }
+        });
+        while (moveAnimationViews.size() != 0) {
+            Thread.sleep(100);
+        }
+    }
+
+    @Test
+    public void testAccessibilityNodeInfoOnRemovedFirstItem() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 6);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        initActivity(intent);
+
+        final View lastView = mGridView.findViewHolderForAdapterPosition(0).itemView;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setRemoveDuration(20000);
+                mActivity.removeItems(0, 1);
+            }
+        });
+        waitForItemAnimationStart();
+        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(lastView);
+        mGridView.getLayoutManager().onInitializeAccessibilityNodeInfoForItem(null, null,
+                lastView, info);
+        assertNoCollectionItemInfo(info);
+    }
+
+    @Test
+    public void testAccessibilityNodeInfoOnRemovedLastItem() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 6);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        initActivity(intent);
+
+        final View lastView = mGridView.findViewHolderForAdapterPosition(5).itemView;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.getItemAnimator().setRemoveDuration(20000);
+                mActivity.removeItems(5, 1);
+            }
+        });
+        waitForItemAnimationStart();
+        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(lastView);
+        mGridView.getLayoutManager().onInitializeAccessibilityNodeInfoForItem(null, null,
+                lastView, info);
+        assertNoCollectionItemInfo(info);
+    }
+
+    static class FiveViewTypesProvider implements ViewTypeProvider {
+
+        @Override
+        public int getViewType(int position) {
+            switch (position) {
+                case 0:
+                    return 0;
+                case 1:
+                    return 1;
+                case 2:
+                    return 2;
+                case 3:
+                    return 3;
+                case 4:
+                    return 4;
+            }
+            return 199;
+        }
+    }
+
+    // Used by testItemAlignmentVertical() testItemAlignmentHorizontal()
+    static class ItemAlignmentWithPaddingFacetProvider implements
+            ItemAlignmentFacetProvider {
+        final ItemAlignmentFacet mFacet0;
+        final ItemAlignmentFacet mFacet1;
+        final ItemAlignmentFacet mFacet2;
+        final ItemAlignmentFacet mFacet3;
+        final ItemAlignmentFacet mFacet4;
+
+        ItemAlignmentWithPaddingFacetProvider() {
+            ItemAlignmentFacet.ItemAlignmentDef[] defs;
+            mFacet0 = new ItemAlignmentFacet();
+            defs = new ItemAlignmentFacet.ItemAlignmentDef[1];
+            defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
+            defs[0].setItemAlignmentViewId(R.id.t1);
+            defs[0].setItemAlignmentOffsetPercent(0);
+            defs[0].setItemAlignmentOffsetWithPadding(false);
+            mFacet0.setAlignmentDefs(defs);
+            mFacet1 = new ItemAlignmentFacet();
+            defs = new ItemAlignmentFacet.ItemAlignmentDef[1];
+            defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
+            defs[0].setItemAlignmentViewId(R.id.t1);
+            defs[0].setItemAlignmentOffsetPercent(0);
+            defs[0].setItemAlignmentOffsetWithPadding(true);
+            mFacet1.setAlignmentDefs(defs);
+            mFacet2 = new ItemAlignmentFacet();
+            defs = new ItemAlignmentFacet.ItemAlignmentDef[1];
+            defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
+            defs[0].setItemAlignmentViewId(R.id.t2);
+            defs[0].setItemAlignmentOffsetPercent(100);
+            defs[0].setItemAlignmentOffsetWithPadding(true);
+            mFacet2.setAlignmentDefs(defs);
+            mFacet3 = new ItemAlignmentFacet();
+            defs = new ItemAlignmentFacet.ItemAlignmentDef[1];
+            defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
+            defs[0].setItemAlignmentViewId(R.id.t2);
+            defs[0].setItemAlignmentOffsetPercent(50);
+            defs[0].setItemAlignmentOffsetWithPadding(true);
+            mFacet3.setAlignmentDefs(defs);
+            mFacet4 = new ItemAlignmentFacet();
+            defs = new ItemAlignmentFacet.ItemAlignmentDef[1];
+            defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
+            defs[0].setItemAlignmentViewId(R.id.t2);
+            defs[0].setItemAlignmentOffsetPercent(50);
+            defs[0].setItemAlignmentOffsetWithPadding(false);
+            mFacet4.setAlignmentDefs(defs);
+        }
+
+        @Override
+        public ItemAlignmentFacet getItemAlignmentFacet(int viewType) {
+            switch (viewType) {
+                case 0:
+                    return mFacet0;
+                case 1:
+                    return mFacet1;
+                case 2:
+                    return mFacet2;
+                case 3:
+                    return mFacet3;
+                case 4:
+                    return mFacet4;
+            }
+            return null;
+        }
+    }
+
+    @Test
+    public void testItemAlignmentVertical() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout2);
+        int[] items = new int[5];
+        for (int i = 0; i < items.length; i++) {
+            items[i] = 300;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS, items);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
+                FiveViewTypesProvider.class.getName());
+        intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS,
+                ItemAlignmentWithPaddingFacetProvider.class.getName());
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        initActivity(intent);
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffsetPercent(50);
+                mGridView.setWindowAlignmentOffset(0);
+            }
+        });
+        waitForLayout();
+
+        final float windowAlignCenter = mGridView.getHeight() / 2f;
+        Rect rect = new Rect();
+        View textView;
+
+        // test 1: does not include padding
+        textView = mGridView.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.t1);
+        rect.set(0, 0, textView.getWidth(), textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.top, DELTA);
+
+        // test 2: including low padding
+        setSelectedPosition(1);
+        textView = mGridView.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.t1);
+        assertTrue(textView.getPaddingTop() > 0);
+        rect.set(0, textView.getPaddingTop(), textView.getWidth(), textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.top, DELTA);
+
+        // test 3: including high padding
+        setSelectedPosition(2);
+        textView = mGridView.findViewHolderForAdapterPosition(2).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingBottom() > 0);
+        rect.set(0, 0, textView.getWidth(),
+                textView.getHeight() - textView.getPaddingBottom());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.bottom, DELTA);
+
+        // test 4: including padding will be ignored if offsetPercent is not 0 or 100
+        setSelectedPosition(3);
+        textView = mGridView.findViewHolderForAdapterPosition(3).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingTop() != textView.getPaddingBottom());
+        rect.set(0, 0, textView.getWidth(), textView.getHeight() / 2);
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.bottom, DELTA);
+
+        // test 5: does not include padding
+        setSelectedPosition(4);
+        textView = mGridView.findViewHolderForAdapterPosition(4).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingTop() != textView.getPaddingBottom());
+        rect.set(0, 0, textView.getWidth(), textView.getHeight() / 2);
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.bottom, DELTA);
+    }
+
+    @Test
+    public void testItemAlignmentHorizontal() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout3);
+        int[] items = new int[5];
+        for (int i = 0; i < items.length; i++) {
+            items[i] = 300;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS, items);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
+                FiveViewTypesProvider.class.getName());
+        intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS,
+                ItemAlignmentWithPaddingFacetProvider.class.getName());
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        initActivity(intent);
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffsetPercent(50);
+                mGridView.setWindowAlignmentOffset(0);
+            }
+        });
+        waitForLayout();
+
+        final float windowAlignCenter = mGridView.getWidth() / 2f;
+        Rect rect = new Rect();
+        View textView;
+
+        // test 1: does not include padding
+        textView = mGridView.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.t1);
+        rect.set(0, 0, textView.getWidth(), textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.left, DELTA);
+
+        // test 2: including low padding
+        setSelectedPosition(1);
+        textView = mGridView.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.t1);
+        assertTrue(textView.getPaddingLeft() > 0);
+        rect.set(textView.getPaddingLeft(), 0, textView.getWidth(), textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.left, DELTA);
+
+        // test 3: including high padding
+        setSelectedPosition(2);
+        textView = mGridView.findViewHolderForAdapterPosition(2).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingRight() > 0);
+        rect.set(0, 0, textView.getWidth() - textView.getPaddingRight(),
+                textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+
+        // test 4: including padding will be ignored if offsetPercent is not 0 or 100
+        setSelectedPosition(3);
+        textView = mGridView.findViewHolderForAdapterPosition(3).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingLeft() != textView.getPaddingRight());
+        rect.set(0, 0, textView.getWidth() / 2, textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+
+        // test 5: does not include padding
+        setSelectedPosition(4);
+        textView = mGridView.findViewHolderForAdapterPosition(4).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingLeft() != textView.getPaddingRight());
+        rect.set(0, 0, textView.getWidth() / 2, textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+    }
+
+    @Test
+    public void testItemAlignmentHorizontalRtl() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout3);
+        int[] items = new int[5];
+        for (int i = 0; i < items.length; i++) {
+            items[i] = 300;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS, items);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
+                FiveViewTypesProvider.class.getName());
+        intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS,
+                ItemAlignmentWithPaddingFacetProvider.class.getName());
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        initActivity(intent);
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffsetPercent(50);
+                mGridView.setWindowAlignmentOffset(0);
+            }
+        });
+        waitForLayout();
+
+        final float windowAlignCenter = mGridView.getWidth() / 2f;
+        Rect rect = new Rect();
+        View textView;
+
+        // test 1: does not include padding
+        textView = mGridView.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.t1);
+        rect.set(0, 0, textView.getWidth(), textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+
+        // test 2: including low padding
+        setSelectedPosition(1);
+        textView = mGridView.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.t1);
+        assertTrue(textView.getPaddingRight() > 0);
+        rect.set(0, 0, textView.getWidth() - textView.getPaddingRight(),
+                textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+
+        // test 3: including high padding
+        setSelectedPosition(2);
+        textView = mGridView.findViewHolderForAdapterPosition(2).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingLeft() > 0);
+        rect.set(textView.getPaddingLeft(), 0, textView.getWidth(),
+                textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.left, DELTA);
+
+        // test 4: including padding will be ignored if offsetPercent is not 0 or 100
+        setSelectedPosition(3);
+        textView = mGridView.findViewHolderForAdapterPosition(3).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingLeft() != textView.getPaddingRight());
+        rect.set(0, 0, textView.getWidth() / 2, textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+
+        // test 5: does not include padding
+        setSelectedPosition(4);
+        textView = mGridView.findViewHolderForAdapterPosition(4).itemView.findViewById(R.id.t2);
+        assertTrue(textView.getPaddingLeft() != textView.getPaddingRight());
+        rect.set(0, 0, textView.getWidth() / 2, textView.getHeight());
+        mGridView.offsetDescendantRectToMyCoords(textView, rect);
+        assertEquals(windowAlignCenter, rect.right, DELTA);
+    }
+
+    enum ItemLocation {
+        ITEM_AT_LOW,
+        ITEM_AT_KEY_LINE,
+        ITEM_AT_HIGH
+    };
+
+    static class ItemAt {
+        final int mScrollPosition;
+        final int mPosition;
+        final ItemLocation mLocation;
+
+        ItemAt(int scrollPosition, int position, ItemLocation loc) {
+            mScrollPosition = scrollPosition;
+            mPosition = position;
+            mLocation = loc;
+        }
+
+        ItemAt(int position, ItemLocation loc) {
+            mScrollPosition = position;
+            mPosition = position;
+            mLocation = loc;
+        }
+    }
+
+    /**
+     * When scroll to position, item at position is expected at given location.
+     */
+    static ItemAt itemAt(int position, ItemLocation location) {
+        return new ItemAt(position, location);
+    }
+
+    /**
+     * When scroll to scrollPosition, item at position is expected at given location.
+     */
+    static ItemAt itemAt(int scrollPosition, int position, ItemLocation location) {
+        return new ItemAt(scrollPosition, position, location);
+    }
+
+    void prepareKeyLineTest(int numItems) throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        int[] items = new int[numItems];
+        for (int i = 0; i < items.length; i++) {
+            items[i] = 32;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS, items);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        initActivity(intent);
+    }
+
+    public void testPreferKeyLine(final int windowAlignment,
+            final boolean preferKeyLineOverLow,
+            final boolean preferKeyLineOverHigh,
+            ItemLocation assertFirstItemLocation,
+            ItemLocation assertLastItemLocation) throws Throwable {
+        testPreferKeyLine(windowAlignment, preferKeyLineOverLow, preferKeyLineOverHigh,
+                itemAt(0, assertFirstItemLocation),
+                itemAt(mActivity.mNumItems - 1, assertLastItemLocation));
+    }
+
+    public void testPreferKeyLine(final int windowAlignment,
+            final boolean preferKeyLineOverLow,
+            final boolean preferKeyLineOverHigh,
+            ItemLocation assertFirstItemLocation,
+            ItemAt assertLastItemLocation) throws Throwable {
+        testPreferKeyLine(windowAlignment, preferKeyLineOverLow, preferKeyLineOverHigh,
+                itemAt(0, assertFirstItemLocation),
+                assertLastItemLocation);
+    }
+
+    public void testPreferKeyLine(final int windowAlignment,
+            final boolean preferKeyLineOverLow,
+            final boolean preferKeyLineOverHigh,
+            ItemAt assertFirstItemLocation,
+            ItemLocation assertLastItemLocation) throws Throwable {
+        testPreferKeyLine(windowAlignment, preferKeyLineOverLow, preferKeyLineOverHigh,
+                assertFirstItemLocation,
+                itemAt(mActivity.mNumItems - 1, assertLastItemLocation));
+    }
+
+    public void testPreferKeyLine(final int windowAlignment,
+            final boolean preferKeyLineOverLow,
+            final boolean preferKeyLineOverHigh,
+            ItemAt assertFirstItemLocation,
+            ItemAt assertLastItemLocation) throws Throwable {
+        TestPreferKeyLineOptions options = new TestPreferKeyLineOptions();
+        options.mAssertItemLocations = new ItemAt[] {assertFirstItemLocation,
+                assertLastItemLocation};
+        options.mPreferKeyLineOverLow = preferKeyLineOverLow;
+        options.mPreferKeyLineOverHigh = preferKeyLineOverHigh;
+        options.mWindowAlignment = windowAlignment;
+
+        options.mRtl = false;
+        testPreferKeyLine(options);
+
+        options.mRtl = true;
+        testPreferKeyLine(options);
+    }
+
+    static class TestPreferKeyLineOptions {
+        int mWindowAlignment;
+        boolean mPreferKeyLineOverLow;
+        boolean mPreferKeyLineOverHigh;
+        ItemAt[] mAssertItemLocations;
+        boolean mRtl;
+    }
+
+    public void testPreferKeyLine(final TestPreferKeyLineOptions options) throws Throwable {
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (options.mRtl) {
+                    mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+                } else {
+                    mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+                }
+                mGridView.setWindowAlignment(options.mWindowAlignment);
+                mGridView.setWindowAlignmentOffsetPercent(50);
+                mGridView.setWindowAlignmentOffset(0);
+                mGridView.setWindowAlignmentPreferKeyLineOverLowEdge(options.mPreferKeyLineOverLow);
+                mGridView.setWindowAlignmentPreferKeyLineOverHighEdge(
+                        options.mPreferKeyLineOverHigh);
+            }
+        });
+        waitForLayout();
+
+        final int paddingStart = mGridView.getPaddingStart();
+        final int paddingEnd = mGridView.getPaddingEnd();
+        final int windowAlignCenter = mGridView.getWidth() / 2;
+
+        for (int i = 0; i < options.mAssertItemLocations.length; i++) {
+            ItemAt assertItemLocation = options.mAssertItemLocations[i];
+            setSelectedPosition(assertItemLocation.mScrollPosition);
+            View view = mGridView.findViewHolderForAdapterPosition(assertItemLocation.mPosition)
+                    .itemView;
+            switch (assertItemLocation.mLocation) {
+                case ITEM_AT_LOW:
+                    if (options.mRtl) {
+                        assertEquals(mGridView.getWidth() - paddingStart, view.getRight());
+                    } else {
+                        assertEquals(paddingStart, view.getLeft());
+                    }
+                    break;
+                case ITEM_AT_HIGH:
+                    if (options.mRtl) {
+                        assertEquals(paddingEnd, view.getLeft());
+                    } else {
+                        assertEquals(mGridView.getWidth() - paddingEnd, view.getRight());
+                    }
+                    break;
+                case ITEM_AT_KEY_LINE:
+                    assertEquals(windowAlignCenter, (view.getLeft() + view.getRight()) / 2, DELTA);
+                    break;
+            }
+        }
+    }
+
+    @Test
+    public void testPreferKeyLine1() throws Throwable {
+        prepareKeyLineTest(1);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, false,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, true,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, false,
+                ItemLocation.ITEM_AT_HIGH, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, false,
+                ItemLocation.ITEM_AT_HIGH, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, false,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, true,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+    }
+
+    @Test
+    public void testPreferKeyLine2() throws Throwable {
+        prepareKeyLineTest(2);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, false,
+                ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, true,
+                ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, false,
+                itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE),
+                itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, true,
+                itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE),
+                itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE));
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, false,
+                itemAt(0, 1, ItemLocation.ITEM_AT_HIGH),
+                itemAt(1, 1, ItemLocation.ITEM_AT_HIGH));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, true,
+                itemAt(0, 0, ItemLocation.ITEM_AT_KEY_LINE),
+                itemAt(1, 0, ItemLocation.ITEM_AT_KEY_LINE));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, false,
+                itemAt(0, 1, ItemLocation.ITEM_AT_HIGH),
+                itemAt(1, 1, ItemLocation.ITEM_AT_HIGH));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, true,
+                itemAt(0, 0, ItemLocation.ITEM_AT_KEY_LINE),
+                itemAt(1, 0, ItemLocation.ITEM_AT_KEY_LINE));
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, false,
+                ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, true,
+                ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, false,
+                itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE),
+                itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE));
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, true,
+                itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE),
+                itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE));
+    }
+
+    @Test
+    public void testPreferKeyLine10000() throws Throwable {
+        prepareKeyLineTest(10000);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, false,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, true,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, false,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, true,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, false,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, true,
+                ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH);
+
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, false,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, true,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, false,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH);
+        testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, true,
+                ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ListRowPresenterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ListRowPresenterTest.java
new file mode 100644
index 0000000..5e605d7
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ListRowPresenterTest.java
@@ -0,0 +1,1162 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.R;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ListRowPresenterTest {
+
+    static final float DELTA = 1f;
+    // default overlay color when setSelectLevel(0.5f)
+    static final int HALF_OVERLAY_COLOR = 0x4C000000;
+    static int sFocusedZ;
+
+    static class DummyPresenter extends Presenter {
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent) {
+            View view = new View(parent.getContext());
+            view.setFocusable(true);
+            view.setId(R.id.lb_action_button);
+            view.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
+            return new Presenter.ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+        }
+
+        @Override
+        public void onUnbindViewHolder(ViewHolder viewHolder) {
+        }
+    }
+
+    Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    ListRowPresenter mListRowPresenter;
+    ListRow mRow;
+    ListRowPresenter.ViewHolder mListVh;
+
+    void setup(ListRowPresenter listRowPresenter, ObjectAdapter adapter) {
+        sFocusedZ = mContext.getResources().getDimensionPixelSize(
+                R.dimen.lb_material_shadow_focused_z);
+        assertTrue(sFocusedZ > 0);
+        mListRowPresenter = listRowPresenter;
+        mRow = new ListRow(adapter);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                final ViewGroup parent = new FrameLayout(mContext);
+                Presenter.ViewHolder containerVh = mListRowPresenter.onCreateViewHolder(parent);
+                parent.addView(containerVh.view, 1000, 1000);
+                mListVh = (ListRowPresenter.ViewHolder) mListRowPresenter.getRowViewHolder(
+                        containerVh);
+                mListRowPresenter.onBindViewHolder(mListVh, mRow);
+                runRecyclerViewLayout();
+            }
+        });
+    }
+
+    void runRecyclerViewLayout() {
+        mListVh.view.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
+        mListVh.view.layout(0, 0, 1000, 1000);
+    }
+
+    static void assertChildrenHaveAlpha(ViewGroup group, int numChildren, float alpha) {
+        assertEquals(numChildren, group.getChildCount());
+        for (int i = 0; i < numChildren; i++) {
+            assertEquals(alpha, group.getChildAt(i).getAlpha());
+        }
+    }
+
+    static Drawable getForeground(View view) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            return view.getForeground();
+        }
+        return null;
+    }
+
+    static void assertChildrenHaveColorOverlay(ViewGroup group, int numChildren, int overlayColor,
+            boolean keepChildForeground) {
+        assertEquals(numChildren, group.getChildCount());
+        for (int i = 0; i < numChildren; i++) {
+            View view = group.getChildAt(i);
+            if (keepChildForeground) {
+                assertTrue("When keepChildForeground, always create a"
+                        + " ShadowOverlayContainer", view instanceof ShadowOverlayContainer);
+                assertEquals(overlayColor, ((ShadowOverlayContainer) view).mOverlayColor);
+            } else {
+                if (view instanceof ShadowOverlayContainer) {
+                    assertEquals(overlayColor, ((ShadowOverlayContainer) view).mOverlayColor);
+                } else {
+                    Drawable foreground = getForeground(view);
+                    assertEquals(overlayColor,
+                            foreground instanceof ColorDrawable
+                                    ? ((ColorDrawable) foreground).getColor() : Color.TRANSPARENT);
+                }
+            }
+        }
+    }
+
+    public void defaultListRowOverlayColor(ListRowPresenter listRowPresenter) {
+        final ArrayObjectAdapter arrayAdapter = new ArrayObjectAdapter(new DummyPresenter());
+        arrayAdapter.add("abc");
+        setup(listRowPresenter, arrayAdapter);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mListVh.getGridView().setItemAnimator(null);
+                mListRowPresenter.setSelectLevel(mListVh, 0.5f);
+            }
+        });
+        boolean keepChildForeground = listRowPresenter.isKeepChildForeground();
+        assertChildrenHaveColorOverlay(mListVh.getGridView(), 1, HALF_OVERLAY_COLOR,
+                keepChildForeground);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                arrayAdapter.add("def");
+                runRecyclerViewLayout();
+            }
+        });
+        assertChildrenHaveColorOverlay(mListVh.getGridView(), 2, HALF_OVERLAY_COLOR,
+                keepChildForeground);
+    }
+
+    @Test
+    public void defaultListRowOverlayColor() {
+        defaultListRowOverlayColor(new ListRowPresenter());
+    }
+
+    @Test
+    public void defaultListRowOverlayColorCombinations() {
+        for (boolean keepChildForeground: new boolean[] {false, true}) {
+            for (boolean isUzingZorder : new boolean[]{false, true}) {
+                for (boolean isUsingDefaultShadow : new boolean[]{false, true}) {
+                    for (boolean enableRoundedCorner : new boolean[]{false, true}) {
+                        for (boolean shadowEnabled : new boolean[]{false, true}) {
+                            final boolean paramIsUsingZorder = isUzingZorder;
+                            final boolean paramIsUsingDefaultShadow = isUsingDefaultShadow;
+                            ListRowPresenter presenter = new ListRowPresenter() {
+                                @Override
+                                public boolean isUsingZOrder(Context context) {
+                                    return paramIsUsingZorder;
+                                }
+
+                                @Override
+                                public boolean isUsingDefaultShadow() {
+                                    return paramIsUsingDefaultShadow;
+                                }
+                            };
+                            presenter.setKeepChildForeground(keepChildForeground);
+                            presenter.setShadowEnabled(shadowEnabled);
+                            presenter.enableChildRoundedCorners(enableRoundedCorner);
+                            defaultListRowOverlayColor(presenter);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    static class CustomSelectEffectRowPresenter extends ListRowPresenter {
+        @Override
+        public boolean isUsingDefaultListSelectEffect() {
+            return false;
+        }
+
+        @Override
+        protected void applySelectLevelToChild(ViewHolder rowViewHolder, View childView) {
+            childView.setAlpha(rowViewHolder.getSelectLevel());
+        }
+    };
+
+    public void customListRowSelectEffect(ListRowPresenter presenter) {
+        final ArrayObjectAdapter arrayAdapter = new ArrayObjectAdapter(new DummyPresenter());
+        arrayAdapter.add("abc");
+        setup(presenter, arrayAdapter);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mListVh.getGridView().setItemAnimator(null);
+                mListRowPresenter.setSelectLevel(mListVh, 0.5f);
+            }
+        });
+        assertChildrenHaveAlpha(mListVh.getGridView(), 1, 0.5f);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                arrayAdapter.add("def");
+                runRecyclerViewLayout();
+            }
+        });
+        assertChildrenHaveAlpha(mListVh.getGridView(), 2, 0.5f);
+    }
+
+    @Test
+    public void customListRowSelectEffect() {
+        customListRowSelectEffect(new CustomSelectEffectRowPresenter());
+    }
+
+    @Test
+    public void customListRowSelectEffectCombinations() {
+        for (boolean keepChildForeground: new boolean[] {false, true}) {
+            for (boolean isUzingZorder: new boolean[] {false, true}) {
+                for (boolean isUsingDefaultShadow: new boolean[] {false, true}) {
+                    for (boolean enableRoundedCorner : new boolean[]{false, true}) {
+                        for (boolean shadowEnabled : new boolean[]{false, true}) {
+                            final boolean paramIsUsingZorder = isUzingZorder;
+                            final boolean paramIsUsingDefaultShadow = isUsingDefaultShadow;
+                            ListRowPresenter presenter = new CustomSelectEffectRowPresenter() {
+                                @Override
+                                public boolean isUsingZOrder(Context context) {
+                                    return paramIsUsingZorder;
+                                }
+
+                                @Override
+                                public boolean isUsingDefaultShadow() {
+                                    return paramIsUsingDefaultShadow;
+                                }
+                            };
+                            presenter.setKeepChildForeground(keepChildForeground);
+                            presenter.setShadowEnabled(shadowEnabled);
+                            presenter.enableChildRoundedCorners(enableRoundedCorner);
+                            customListRowSelectEffect(presenter);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    static class ShadowOverlayResult {
+        boolean mShadowOverlayContainer;
+        int mShadowOverlayContainerOverlayColor;
+        float mShadowOverlayContainerOverlayZ;
+        boolean mShadowOverlayContainerOpticalBounds;
+        int mViewOverlayColor;
+        float mViewZ;
+        boolean mViewOpticalBounds;
+        void expect(boolean shadowOverlayContainer, int shadowOverlayContainerOverlayColor,
+                float shadowOverlayContainerOverlayZ, boolean shadowOverlayContainerOpticalBounds,
+                int viewOverlayColor, float viewZ, boolean viewOpticalBounds) {
+            assertEquals(this.mShadowOverlayContainer, shadowOverlayContainer);
+            assertEquals(this.mShadowOverlayContainerOverlayColor,
+                    shadowOverlayContainerOverlayColor);
+            assertEquals(this.mShadowOverlayContainerOverlayZ, shadowOverlayContainerOverlayZ,
+                    DELTA);
+            assertEquals(this.mShadowOverlayContainerOpticalBounds,
+                    shadowOverlayContainerOpticalBounds);
+            assertEquals(this.mViewOverlayColor, viewOverlayColor);
+            assertEquals(this.mViewZ, viewZ, DELTA);
+            assertEquals(this.mViewOpticalBounds, viewOpticalBounds);
+        }
+    }
+
+    ShadowOverlayResult setupDefaultPresenterWithSingleElement(final boolean isUsingZorder,
+            final boolean isUsingDefaultShadow,
+            final boolean enableRoundedCorner,
+            final boolean shadowEnabled,
+            final boolean keepChildForeground) {
+        ArrayObjectAdapter adapter = new ArrayObjectAdapter(new DummyPresenter());
+        adapter.add("abc");
+        ListRowPresenter listRowPresenter = new ListRowPresenter() {
+            @Override
+            public boolean isUsingZOrder(Context context) {
+                return isUsingZorder;
+            }
+
+            @Override
+            public boolean isUsingDefaultShadow() {
+                return isUsingDefaultShadow;
+            }
+        };
+        listRowPresenter.setShadowEnabled(shadowEnabled);
+        listRowPresenter.enableChildRoundedCorners(enableRoundedCorner);
+        listRowPresenter.setKeepChildForeground(keepChildForeground);
+        setup(listRowPresenter, adapter);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mListVh.getGridView().setItemAnimator(null);
+                mListRowPresenter.setSelectLevel(mListVh, 0.5f);
+                View child = mListVh.getGridView().getChildAt(0);
+                FocusHighlightHelper.FocusAnimator animator = (FocusHighlightHelper.FocusAnimator)
+                        child.getTag(R.id.lb_focus_animator);
+                animator.animateFocus(true, true);
+            }
+        });
+        return collectResult();
+    }
+
+    ShadowOverlayResult collectResult() {
+        ShadowOverlayResult result = new ShadowOverlayResult();
+        View view = mListVh.getGridView().getChildAt(0);
+        if (view instanceof ShadowOverlayContainer) {
+            result.mShadowOverlayContainer = true;
+            result.mShadowOverlayContainerOverlayColor = ((ShadowOverlayContainer) view)
+                    .mOverlayColor;
+            result.mShadowOverlayContainerOverlayZ = ViewCompat.getZ(view);
+            result.mShadowOverlayContainerOpticalBounds = view.getWidth() > 100;
+        } else {
+            result.mShadowOverlayContainer = false;
+        }
+        view = view.findViewById(R.id.lb_action_button);
+        Drawable d = getForeground(view);
+        result.mViewOverlayColor = d instanceof ColorDrawable ? ((ColorDrawable) d).getColor()
+                : Color.TRANSPARENT;
+        result.mViewZ = ViewCompat.getZ(view);
+        result.mViewOpticalBounds = view.getWidth() > 100;
+        return result;
+    }
+
+    @Test
+    public void shadowOverlayContainerTest01() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest02() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest03() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest04() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest05() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest06() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest07() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest08() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest09() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest10() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest11() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest12() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest13() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest14() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest15() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest16() {
+        final boolean isUsingZorder = false;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, true,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest17() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest18() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest19() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest20() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest21() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest22() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest23() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest24() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = false;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest25() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest26() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest27() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, sFocusedZ, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, sFocusedZ, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest28() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = false;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, sFocusedZ, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, sFocusedZ, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest29() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest30() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = false;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest31() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = false;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, sFocusedZ, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(false, 0, 0, false,
+                    HALF_OVERLAY_COLOR, sFocusedZ, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTest32() {
+        final boolean isUsingZorder = true;
+        final boolean isUsingDefaultShadow = true;
+        final boolean enableRoundedCorner = true;
+        final boolean shadowEnabled = true;
+        final boolean keepChildForeground = true;
+        ShadowOverlayResult result = setupDefaultPresenterWithSingleElement(isUsingZorder,
+                isUsingDefaultShadow, enableRoundedCorner, shadowEnabled, keepChildForeground);
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            // nothing supported
+            result.expect(true, HALF_OVERLAY_COLOR, 0, false,
+                    0, 0, false);
+        } else if (version < 23) {
+            // supports static/dynamic shadow, supports rounded corner
+            result.expect(true, HALF_OVERLAY_COLOR, sFocusedZ, false,
+                    0, 0, false);
+        } else {
+            // supports foreground
+            result.expect(true, HALF_OVERLAY_COLOR, sFocusedZ, false,
+                    0, 0, false);
+        }
+    }
+
+    @Test
+    public void shadowOverlayContainerTestDefaultSettings() {
+        ListRowPresenter presenter = new ListRowPresenter();
+        final int version = Build.VERSION.SDK_INT;
+        if (version < 21) {
+            assertFalse(presenter.isUsingZOrder(mContext));
+            assertFalse(presenter.isUsingDefaultShadow());
+        } else {
+            assertTrue(presenter.isUsingZOrder(mContext));
+            assertTrue(presenter.isUsingDefaultShadow());
+        }
+        assertTrue(presenter.areChildRoundedCornersEnabled());
+        assertTrue(presenter.getShadowEnabled());
+        assertTrue(presenter.isKeepChildForeground());
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
index 6d49c0e..046b4c0 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
@@ -53,6 +53,7 @@
         MockitoAnnotations.initMocks(this);
         mSource = new Parallax<Parallax.FloatProperty>() {
 
+            @Override
             public float getMaxValue() {
                 return mScreenMax;
             }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java
index a83cca2..4b45cc2 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java
@@ -43,6 +43,7 @@
     public void setUp() throws Exception {
         mSource = new Parallax<Parallax.FloatProperty>() {
 
+            @Override
             public float getMaxValue() {
                 return mScreenMax;
             }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
index cf15fa8..4311fa6 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
@@ -50,6 +50,7 @@
         MockitoAnnotations.initMocks(this);
         mSource = new Parallax<Parallax.IntProperty>() {
 
+            @Override
             public float getMaxValue() {
                 return mScreenMax;
             }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java
index 187bfb6..a49acbd 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java
@@ -40,6 +40,7 @@
     public void setUp() throws Exception {
         mSource = new Parallax<Parallax.IntProperty>() {
 
+            @Override
             public float getMaxValue() {
                 return mScreenMax;
             }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackGlueHostImplWithViewHolder.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackGlueHostImplWithViewHolder.java
new file mode 100644
index 0000000..2cee649
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackGlueHostImplWithViewHolder.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import android.content.Context;
+import android.support.v17.leanback.media.PlaybackGlueHostImpl;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+/**
+ * Example to create a ViewHolder and rebind when notifyPlaybackRowChanged.
+ */
+public class PlaybackGlueHostImplWithViewHolder extends PlaybackGlueHostImpl
+        implements PlaybackSeekUi {
+    protected Context mContext;
+    protected PlaybackRowPresenter.ViewHolder mViewHolder;
+    protected ViewGroup mRootView;
+
+    protected int mLayoutWidth = 1920;
+    protected int mLayoutHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
+    Client mSeekClient;
+
+    public PlaybackGlueHostImplWithViewHolder(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void setPlaybackRow(Row row) {
+        super.setPlaybackRow(row);
+        createViewHolderIfNeeded();
+    }
+
+    @Override
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
+        super.setPlaybackRowPresenter(presenter);
+        createViewHolderIfNeeded();
+    }
+
+    void createViewHolderIfNeeded() {
+        if (mViewHolder == null && mPlaybackRowPresenter != null && mRow != null) {
+            mViewHolder = (PlaybackRowPresenter.ViewHolder)
+                    mPlaybackRowPresenter.onCreateViewHolder(mRootView = new FrameLayout(mContext));
+            // Bind ViewHolder before measure/layout so child views will get proper size
+            mPlaybackRowPresenter.onBindViewHolder(mViewHolder, mRow);
+            mRootView.addView(mViewHolder.view, mLayoutWidth, mLayoutHeight);
+            mRootView.measure(
+                    View.MeasureSpec.makeMeasureSpec(1920, View.MeasureSpec.AT_MOST),
+                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+            mRootView.layout(0, 0, mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight());
+            if (mViewHolder instanceof PlaybackSeekUi) {
+                ((PlaybackSeekUi) mViewHolder).setPlaybackSeekUiClient(mChainedClient);
+            }
+        }
+    }
+
+    @Override
+    public void notifyPlaybackRowChanged() {
+        if (mViewHolder != null) {
+            mPlaybackRowPresenter.onUnbindRowViewHolder(mViewHolder);
+            mPlaybackRowPresenter.onBindViewHolder(mViewHolder, mRow);
+        }
+    }
+
+    public void sendKeyEvent(KeyEvent event) {
+        mRootView.dispatchKeyEvent(event);
+    }
+
+    public void sendKeyDownUp(int keyCode) {
+        sendKeyDownUp(keyCode, 1);
+    }
+
+    public void sendKeyDownUp(int keyCode, int repeat) {
+        for (int i = 0; i < repeat; i++) {
+            mRootView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+        }
+        mRootView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
+    }
+
+    @Override
+    public void setPlaybackSeekUiClient(Client client) {
+        mSeekClient = client;
+    }
+
+    protected Client mChainedClient = new Client() {
+        @Override
+        public boolean isSeekEnabled() {
+            return mSeekClient == null ? false : mSeekClient.isSeekEnabled();
+        }
+
+        @Override
+        public void onSeekStarted() {
+            mSeekClient.onSeekStarted();
+        }
+
+        @Override
+        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
+            return mSeekClient.getPlaybackSeekDataProvider();
+        }
+
+        @Override
+        public void onSeekPositionChanged(long pos) {
+            mSeekClient.onSeekPositionChanged(pos);
+        }
+
+        @Override
+        public void onSeekFinished(boolean cancelled) {
+            mSeekClient.onSeekFinished(cancelled);
+        }
+    };
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackSeekProviderSample.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackSeekProviderSample.java
new file mode 100644
index 0000000..6232e01
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackSeekProviderSample.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+public class PlaybackSeekProviderSample extends PlaybackSeekDataProvider {
+
+    protected long[] mSeekPositions;
+
+    public PlaybackSeekProviderSample(long duration, int numSeekPositions) {
+        this(0, duration, numSeekPositions);
+    }
+
+    public PlaybackSeekProviderSample(long first, long last, int numSeekPositions) {
+        mSeekPositions = new long[numSeekPositions];
+        for (int i = 0; i < mSeekPositions.length; i++) {
+            mSeekPositions[i] = first + i * (last - first) / (numSeekPositions - 1);
+        }
+    }
+
+    @Override
+    public long[] getSeekPositions() {
+        return mSeekPositions;
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackTransportRowPresenterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackTransportRowPresenterTest.java
new file mode 100644
index 0000000..db55725
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/PlaybackTransportRowPresenterTest.java
@@ -0,0 +1,647 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v17.leanback.media.PlaybackTransportControlGlue;
+import android.support.v17.leanback.media.PlayerAdapter;
+import android.support.v17.leanback.widget.PlaybackSeekDataProvider.ResultCallback;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewParent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+
+@SmallTest
+public class PlaybackTransportRowPresenterTest {
+
+    Context mContext;
+    PlaybackTransportControlGlue mGlue;
+    PlaybackGlueHostImplWithViewHolder mHost;
+    PlayerAdapter mImpl;
+    PlaybackTransportRowPresenter.ViewHolder mViewHolder;
+    AbstractDetailsDescriptionPresenter.ViewHolder mDescriptionViewHolder;
+    int mNumbThumbs;
+
+    @Before
+    public void setUp() {
+        mContext = new ContextThemeWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                android.support.v17.leanback.test.R.style.Theme_Leanback);
+        mHost = new PlaybackGlueHostImplWithViewHolder(mContext);
+        mImpl = Mockito.mock(PlayerAdapter.class);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue = new PlaybackTransportControlGlue(mContext, mImpl) {
+                    @Override
+                    protected void onCreatePrimaryActions(ArrayObjectAdapter
+                            primaryActionsAdapter) {
+                        super.onCreatePrimaryActions(primaryActionsAdapter);
+                        primaryActionsAdapter.add(
+                                new PlaybackControlsRow.ClosedCaptioningAction(mContext));
+                    }
+
+                    @Override
+                    protected void onCreateSecondaryActions(ArrayObjectAdapter
+                            secondaryActionsAdapter) {
+                        secondaryActionsAdapter.add(
+                                new PlaybackControlsRow.HighQualityAction(mContext));
+                        secondaryActionsAdapter.add(
+                                new PlaybackControlsRow.PictureInPictureAction(mContext));
+                    }
+                };
+                mGlue.setHost(mHost);
+
+            }
+        });
+        mViewHolder = (PlaybackTransportRowPresenter.ViewHolder) mHost.mViewHolder;
+        mDescriptionViewHolder = (AbstractDetailsDescriptionPresenter.ViewHolder)
+                mViewHolder.mDescriptionViewHolder;
+        mNumbThumbs = mViewHolder.mThumbsBar.getChildCount();
+        assertTrue((mNumbThumbs & 1) != 0);
+    }
+
+    void sendKeyUIThread(int keyCode) {
+        sendKeyUIThread(keyCode, 1);
+    }
+
+    void sendKeyUIThread(final int keyCode, final int repeat) {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mHost.sendKeyDownUp(keyCode, repeat);
+            }
+        });
+    }
+
+    void verifyGetThumbCalls(int firstHeroIndex, int lastHeroIndex,
+            PlaybackSeekDataProvider provider, long[] positions) {
+        int firstThumbIndex = Math.max(firstHeroIndex - (mNumbThumbs / 2), 0);
+        int lastThumbIndex = Math.min(lastHeroIndex + (mNumbThumbs / 2), positions.length - 1);
+        for (int i = firstThumbIndex; i <= lastThumbIndex; i++) {
+            Mockito.verify(provider, times(1)).getThumbnail(eq(i), any(ResultCallback.class));
+        }
+        Mockito.verify(provider, times(0)).getThumbnail(
+                eq(firstThumbIndex - 1), any(ResultCallback.class));
+        Mockito.verify(provider, times(0)).getThumbnail(
+                eq(firstThumbIndex - 2), any(ResultCallback.class));
+        Mockito.verify(provider, times(0)).getThumbnail(
+                eq(lastThumbIndex + 1), any(ResultCallback.class));
+        Mockito.verify(provider, times(0)).getThumbnail(
+                eq(lastThumbIndex + 2), any(ResultCallback.class));
+    }
+
+    void verifyAtHeroIndexWithDifferentPosition(long position, int heroIndex) {
+        assertEquals(position, mGlue.getControlsRow().getCurrentPosition());
+        assertEquals(mViewHolder.mThumbHeroIndex, heroIndex);
+    }
+
+    void verifyAtHeroIndex(long[] positions, int heroIndex) {
+        verifyAtHeroIndex(positions, heroIndex, null);
+    }
+
+    void verifyAtHeroIndex(long[] positions, int heroIndex, Bitmap[] thumbs) {
+        assertEquals(positions[heroIndex], mGlue.getControlsRow().getCurrentPosition());
+        assertEquals(mViewHolder.mThumbHeroIndex, heroIndex);
+        if (thumbs != null) {
+            int start = Math.max(0, mViewHolder.mThumbHeroIndex - mNumbThumbs / 2);
+            int end = Math.min(positions.length - 1, mViewHolder.mThumbHeroIndex + mNumbThumbs / 2);
+            verifyThumbBitmaps(thumbs, start, end,
+                    mViewHolder.mThumbsBar, start + mNumbThumbs / 2 - mViewHolder.mThumbHeroIndex,
+                    end + mNumbThumbs / 2 - mViewHolder.mThumbHeroIndex);
+        }
+    }
+
+    void verifyThumbBitmaps(Bitmap[] thumbs, int start, int end,
+            ThumbsBar thumbsBar, int childStart, int childEnd) {
+        assertEquals(end - start, childEnd - childStart);
+        for (int i = start; i <= end; i++) {
+            assertSame(thumbs[i], thumbsBar.getThumbBitmap(childStart + (i - start)));
+        }
+        for (int i = 0; i < childStart; i++) {
+            assertNull(thumbsBar.getThumbBitmap(i));
+        }
+        for (int i = childEnd + 1; i < mNumbThumbs; i++) {
+            assertNull(thumbsBar.getThumbBitmap(i));
+        }
+    }
+
+    @Test
+    public void progressUpdating() {
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(123L);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+
+        mGlue.play();
+        Mockito.verify(mImpl, times(1)).play();
+        mGlue.pause();
+        Mockito.verify(mImpl, times(1)).pause();
+        mGlue.seekTo(1231);
+        Mockito.verify(mImpl, times(1)).seekTo(1231);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+        assertEquals(123L, mGlue.getCurrentPosition());
+        assertEquals(20000L, mGlue.getDuration());
+        assertEquals(321L, mGlue.getBufferedPosition());
+        assertEquals(123L, mViewHolder.mCurrentTimeInMs);
+        assertEquals(20000L, mViewHolder.mTotalTimeInMs);
+        assertEquals(321L, mViewHolder.mSecondaryProgressInMs);
+
+        when(mImpl.getCurrentPosition()).thenReturn(124L);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        assertEquals(124L, mGlue.getControlsRow().getCurrentPosition());
+        assertEquals(124L, mViewHolder.mCurrentTimeInMs);
+        when(mImpl.getBufferedPosition()).thenReturn(333L);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+        assertEquals(333L, mGlue.getControlsRow().getBufferedPosition());
+        assertEquals(333L, mViewHolder.mSecondaryProgressInMs);
+        when(mImpl.getDuration()).thenReturn((long) (Integer.MAX_VALUE) * 2);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        assertEquals((long) (Integer.MAX_VALUE) * 2, mGlue.getControlsRow().getDuration());
+        assertEquals((long) (Integer.MAX_VALUE) * 2, mViewHolder.mTotalTimeInMs);
+    }
+
+    @Test
+    public void mediaInfo() {
+        final ColorDrawable art = new ColorDrawable();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mGlue.setTitle("xyz");
+                mGlue.setSubtitle("zyx");
+                mGlue.setArt(art);
+            }
+        });
+        assertEquals("xyz", mDescriptionViewHolder.mTitle.getText());
+        assertEquals("zyx", mDescriptionViewHolder.mSubtitle.getText());
+        assertSame(art, mViewHolder.mImageView.getDrawable());
+    }
+
+    static boolean isDescendant(View view, View descendant) {
+        while (descendant != view) {
+            ViewParent p = descendant.getParent();
+            if (!(p instanceof View)) {
+                return false;
+            }
+            descendant = (View) p;
+        }
+        return true;
+    }
+
+    @Test
+    public void navigateRightInPrimary() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mControlsVh.mControlBar.getChildAt(0).requestFocus();
+            }
+        });
+        View view = mViewHolder.view.findFocus();
+        assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(0), view));
+        assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(1),
+                view.focusSearch(View.FOCUS_RIGHT)));
+    }
+
+    @Test
+    public void navigateRightInSecondary() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0).requestFocus();
+            }
+        });
+        View view = mViewHolder.view.findFocus();
+        assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0), view));
+        assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(1),
+                view.focusSearch(View.FOCUS_RIGHT)));
+    }
+
+    @Test
+    public void navigatePrimaryDownToProgress() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mControlsVh.mControlBar.getChildAt(0).requestFocus();
+            }
+        });
+        View view = mViewHolder.view.findFocus();
+        assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(0), view));
+        assertSame(mViewHolder.mProgressBar, view.focusSearch(View.FOCUS_DOWN));
+    }
+
+    @Test
+    public void navigateProgressUpToPrimary() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mProgressBar.requestFocus();
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mProgressBar.focusSearch(View.FOCUS_UP).requestFocus();
+            }
+        });
+        View view = mViewHolder.view.findFocus();
+        assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(0), view));
+    }
+
+    @Test
+    public void navigateProgressDownToSecondary() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mProgressBar.requestFocus();
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mProgressBar.focusSearch(View.FOCUS_DOWN).requestFocus();
+            }
+        });
+        View view = mViewHolder.view.findFocus();
+        assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0), view));
+    }
+
+    @Test
+    public void navigateSecondaryUpToProgress() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0).requestFocus();
+            }
+        });
+        View view = mViewHolder.view.findFocus();
+        assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0), view));
+        assertSame(mViewHolder.mProgressBar, view.focusSearch(View.FOCUS_UP));
+    }
+
+    @Test
+    public void seekAndConfirm() {
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(0L);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 1);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 2);
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_CENTER);
+        Mockito.verify(mImpl).seekTo(positions[2]);
+
+        verifyGetThumbCalls(1, 2, provider, positions);
+    }
+
+
+    @Test
+    public void seekHoldKeyDown() {
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(4489L);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(4489L);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        int insertPosition = -1 - Arrays.binarySearch(positions, 4489L);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT, 5);
+        verifyAtHeroIndex(positions, insertPosition + 4);
+        verifyGetThumbCalls(insertPosition, insertPosition + 4, provider, positions);
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT, 5);
+        verifyAtHeroIndex(positions, insertPosition - 1);
+    }
+
+    @Test
+    public void seekAndCancel() {
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(0L);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 1);
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 2);
+
+        sendKeyUIThread(KeyEvent.KEYCODE_BACK);
+        Mockito.verify(mImpl, times(0)).seekTo(anyInt());
+        verifyGetThumbCalls(1, 2, provider, positions);
+    }
+
+    @Test
+    public void seekUpBetweenTwoKeyPosition() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+
+        // initially select between 0 and 1
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn((positions[0] + positions[1]) / 2);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 1);
+        verifyGetThumbCalls(1, 1, provider, positions);
+    }
+
+    @Test
+    public void seekDownBetweenTwoKeyPosition() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        assertTrue(positions[0] == 0);
+
+        // initially select between 0 and 1
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn((positions[0] + positions[1]) / 2);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndex(positions, 0);
+        verifyGetThumbCalls(0, 0, provider, positions);
+    }
+
+    @Test
+    public void seekDownOutOfKeyPositions() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(1000L, 10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        assertTrue(positions[0] > 0);
+
+        // initially select between 0 and 1
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn((positions[0] + positions[1]) / 2);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndex(positions, 0);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndexWithDifferentPosition(0, 0);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndexWithDifferentPosition(0, 0);
+        verifyGetThumbCalls(0, 0, provider, positions);
+    }
+
+    @Test
+    public void seekDownAheadOfKeyPositions() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(1000L, 10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        assertTrue(positions[0] > 0);
+
+        // initially select between 0 and 1
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(positions[0] / 2);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndexWithDifferentPosition(0, 0);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 0);
+        verifyGetThumbCalls(0, 0, provider, positions);
+    }
+
+    @Test
+    public void seekUpAheadOfKeyPositions() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(1000L, 10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+        assertTrue(positions[0] > 0);
+
+        // initially select between 0 and 1
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(positions[0] / 2);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 0);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndexWithDifferentPosition(0, 0);
+        verifyGetThumbCalls(0, 0, provider, positions);
+    }
+
+    @Test
+    public void seekUpOutOfKeyPositions() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+
+        // initially select between nth-1 and nth
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn((positions[positions.length - 2]
+                + positions[positions.length - 1]) / 2);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, positions.length - 1);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndex(positions, positions.length - 2);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, positions.length - 1);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
+        verifyGetThumbCalls(positions.length - 2, positions.length - 1, provider, positions);
+    }
+
+    @Test
+    public void seekUpAfterKeyPositions() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+
+        // initially select after last item
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(positions[positions.length - 1] + 100);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndex(positions, positions.length - 1);
+        verifyGetThumbCalls(positions.length - 1, positions.length - 1, provider, positions);
+    }
+
+    @Test
+    public void seekDownAfterKeyPositions() {
+        PlaybackSeekProviderSample provider = Mockito.spy(
+                new PlaybackSeekProviderSample(10000L, 101));
+        final long[] positions = provider.getSeekPositions();
+
+        // initially select after last item
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(positions[positions.length - 1] + 100);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
+        verifyAtHeroIndex(positions, positions.length - 1);
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
+        verifyGetThumbCalls(positions.length - 1, positions.length - 1, provider, positions);
+    }
+
+    @Test
+    public void thumbLoadedInCallback() {
+        when(mImpl.isPrepared()).thenReturn(true);
+        when(mImpl.getCurrentPosition()).thenReturn(0L);
+        when(mImpl.getDuration()).thenReturn(20000L);
+        when(mImpl.getBufferedPosition()).thenReturn(321L);
+        mImpl.getCallback().onCurrentPositionChanged(mImpl);
+        mImpl.getCallback().onDurationChanged(mImpl);
+        mImpl.getCallback().onBufferedPositionChanged(mImpl);
+
+        final Bitmap[] thumbs = new Bitmap[101];
+        for (int i = 0; i < 101; i++) {
+            thumbs[i] = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
+        }
+        PlaybackSeekProviderSample provider = new PlaybackSeekProviderSample(10000L, 101) {
+            @Override
+            public void getThumbnail(int index, ResultCallback callback) {
+                callback.onThumbnailLoaded(thumbs[index], index);
+            }
+        };
+        final long[] positions = provider.getSeekPositions();
+        mGlue.setSeekProvider(provider);
+        mViewHolder.mProgressBar.requestFocus();
+        assertTrue(mViewHolder.mProgressBar.hasFocus());
+
+        sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
+        verifyAtHeroIndex(positions, 1, thumbs);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ThumbsBarTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ThumbsBarTest.java
new file mode 100644
index 0000000..394d723
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ThumbsBarTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.R;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+
+public class ThumbsBarTest {
+    private Context mContext;
+    private ThumbsBar mBar;
+
+    /**
+     * Check ThumbsBar's initialization based on the constructor
+     */
+    @Test
+    public void checkThumbsBarInitialize() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        assertEquals(mBar.mThumbHeightInPixel, mContext.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_height));
+        assertEquals(mBar.mThumbWidthInPixel, mContext.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_width));
+        assertEquals(mBar.mHeroThumbHeightInPixel, mContext.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_hero_thumbs_height));
+        assertEquals(mBar.mHeroThumbWidthInPixel, mContext.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_hero_thumbs_width));
+    }
+
+    /**
+     * Check getHeroIndex method when input is an even number
+     */
+    @Test
+    public void checkGetHeroIndexOnEvenNumber() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int childCountForTest = 4;
+        // according to the algorithm, hero thumb's index should be childCounts / 2
+        int expectedHeroIndex = 2;
+        when(mBar.getChildCount()).thenReturn(childCountForTest);
+        assertEquals(mBar.getHeroIndex(), expectedHeroIndex);
+    }
+
+    /**
+     * Check getHeroIndex method when input is an odd number.
+     */
+    @Test
+    public void checkGetHeroIndexOnOddNumber() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int childCountForTest = 5;
+        // according to the algorithm, hero thumb's index should be childCounts / 2
+        int expectedHeroIndex = 2;
+        when(mBar.getChildCount()).thenReturn(childCountForTest);
+        assertEquals(mBar.getHeroIndex(), expectedHeroIndex);
+    }
+
+    /**
+     * Check setThumbSize method.
+     */
+    @Test
+    public void checkSetThumbSize() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int screenWidthInPixelForTest = 2560;
+        int screenHeightInPixelForTest = 1600;
+        int thumbsWidthInPixelForTest = 128;
+        int thumbsHeightInPixelForTest = 256;
+        // set screen size explicitly so the thumbs bar will have child view inside of it
+        mBar.measure(View.MeasureSpec.makeMeasureSpec(screenWidthInPixelForTest,
+                View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(screenHeightInPixelForTest,
+                        View.MeasureSpec.EXACTLY));
+
+        mBar.setThumbSize(thumbsWidthInPixelForTest, thumbsHeightInPixelForTest);
+        // Verify the behavior of setThumbSize method
+        assertEquals(mBar.mThumbWidthInPixel, thumbsWidthInPixelForTest);
+        assertEquals(mBar.mThumbHeightInPixel, thumbsHeightInPixelForTest);
+        // iterate through all child view to test if its width/ height has been set successfully
+        for (int i = 0; i < mBar.getChildCount(); i++) {
+            if (i != mBar.getHeroIndex()) {
+                assertEquals(mBar.getChildAt(i).getLayoutParams().width,
+                        thumbsWidthInPixelForTest);
+                assertEquals(mBar.getChildAt(i).getLayoutParams().height,
+                        thumbsHeightInPixelForTest);
+            } else {
+                assertEquals(mBar.getChildAt(i).getLayoutParams().width,
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.lb_playback_transport_hero_thumbs_width));
+                assertEquals(mBar.getChildAt(i).getLayoutParams().height,
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.lb_playback_transport_hero_thumbs_height));
+            }
+        }
+    }
+
+    /**
+     * Check setHeroThumbSize method.
+     */
+    @Test
+    public void checkSetHeroThumbSize() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int screenWidthInPixelForTest = 2560;
+        int screenHeightInPixelForTest = 1600;
+        int HeroThumbsWidthInPixelForTest = 256;
+        int HeroThumbsHeightInPixelForTest = 512;
+        // set screen size explicitly so the thumbs bar will have child view inside of it
+        mBar.measure(View.MeasureSpec.makeMeasureSpec(
+                screenWidthInPixelForTest, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(
+                        screenHeightInPixelForTest, View.MeasureSpec.EXACTLY));
+        mBar.setHeroThumbSize(HeroThumbsWidthInPixelForTest, HeroThumbsHeightInPixelForTest);
+        // Verify the behavior of setThumbSize method
+        assertEquals(mBar.mHeroThumbWidthInPixel, HeroThumbsWidthInPixelForTest);
+        assertEquals(mBar.mHeroThumbHeightInPixel, HeroThumbsHeightInPixelForTest);
+        // iterate through all child view to test if its width/ height has been set successfully
+        for (int i = 0; i < mBar.getChildCount(); i++) {
+            if (i != mBar.getHeroIndex()) {
+                assertEquals(mBar.getChildAt(i).getLayoutParams().width,
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.lb_playback_transport_thumbs_width));
+                assertEquals(mBar.getChildAt(i).getLayoutParams().height,
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.lb_playback_transport_thumbs_height));
+            } else {
+                assertEquals(mBar.getChildAt(i).getLayoutParams().width,
+                        HeroThumbsWidthInPixelForTest);
+                assertEquals(mBar.getChildAt(i).getLayoutParams().height,
+                        HeroThumbsHeightInPixelForTest);
+            }
+        }
+    }
+
+    /**
+     * Check setThumbSpace method.
+     */
+    @Test
+    public void checkSetThumbSpace() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int thumbSpaceInPixelForTest = 48;
+        mBar.setThumbSpace(thumbSpaceInPixelForTest);
+        assertEquals(mBar.mMeasuredMarginInPixel, thumbSpaceInPixelForTest);
+        verify(mBar).requestLayout();
+    }
+
+    /**
+     * check calculateNumberOfThumbs method when the result from roundUp function is less than 2
+     *
+     * Firstly, to make sure the test cases can run on different devices with different screen
+     * density (i.e. The return value from roundUp function should be the same no matter what kind
+     * of device/ emulator is connected),
+     * the screen width for test is set using dp, the pixel value will be computed by
+     * multiplying context.getResources().getDisplayMetrics().density
+     *
+     * In this test case, the screen width is set to 240 in dp, so the calculation result should
+     * be 1. According to the algorithm of calculateNumOfThumbs, it should be reassigned to 2 and
+     * the final result should be 3 after counting the hero thumb.
+     */
+    @Test
+    public void checkCalculateNumberOfThumbs1() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int screenWidthInPixelForTest =
+                (int) (240 * mContext.getResources().getDisplayMetrics().density);
+        int screenHeightInPixelForTest =
+                (int) (240 * mContext.getResources().getDisplayMetrics().density);
+        int expectedChildCounts = 3;
+        mBar.measure(View.MeasureSpec.makeMeasureSpec(
+                screenWidthInPixelForTest, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(
+                        screenHeightInPixelForTest, View.MeasureSpec.EXACTLY));
+        assertEquals(mBar.getChildCount(), expectedChildCounts);
+    }
+
+    /**
+     * check calculateNumberOfThumbs method when the result from roundUp function is an odd number
+     * and larger than 2.
+     *
+     * In this test case, the screen width is set to 680 in dp, so the calculation result should
+     * be 3. According to the algorithm of calculateNumOfThumbs, it should be incremented by 1, so
+     * the final result is 5 after counting the hero thumb.
+     */
+    @Test
+    public void checkCalculateNumberOfThumbs2() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int screenWidthInPixelForTest =
+                (int) (680 * mContext.getResources().getDisplayMetrics().density);
+        int screenHeightInPixelForTest =
+                (int) (680 * mContext.getResources().getDisplayMetrics().density);
+        int expectedChildCounts = 5;
+        mBar.measure(View.MeasureSpec.makeMeasureSpec(
+                screenWidthInPixelForTest, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(
+                        screenHeightInPixelForTest, View.MeasureSpec.EXACTLY));
+        assertEquals(mBar.getChildCount(), expectedChildCounts);
+    }
+
+    /**
+     * check calculateNumberOfThumbs method when the result from roundUp function is an even number
+     * and larger than 2
+     *
+     * In this test case, the screen width is set to 800 in dp, so the calculation result should
+     * be 4. Finally the result is expected to be 5 after counting the hero thumb.
+     */
+    @Test
+    public void checkCalculateNumberOfThumbs3() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int screenWidthInPixelForTest =
+                (int) (800 * mContext.getResources().getDisplayMetrics().density);
+        int screenHeightInPixelForTest =
+                (int) (800 * mContext.getResources().getDisplayMetrics().density);
+        int expectedChildCounts = 5;
+        mBar.measure(View.MeasureSpec.makeMeasureSpec(
+                screenWidthInPixelForTest, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(
+                        screenHeightInPixelForTest, View.MeasureSpec.EXACTLY));
+        assertEquals(mBar.getChildCount(), expectedChildCounts);
+    }
+
+    /**
+     * check setNumberOfThumbs method
+     *
+     * When user calling setNumberOfThumbs(int numOfThumbs) method. The flag mIsUserSets will be
+     * toggled to true to honor user's choice, and the result of child view's number should
+     * not be impacted by calculateNumberOfThumbs(int widthInPixel) method.
+     *
+     * In this test case, the screen width is set to 960 in dp, the calculation result from
+     * calculateNumberOfThumbs method should be 5. But after calling setNumberOfThumbs function to
+     * set thumbs' number to 3, this value should not impact the final result.
+     */
+    @Test
+    public void checkSetNumberOfThumbs() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBar = Mockito.spy(new ThumbsBar(mContext, null));
+            }
+        });
+        int screenWidthInPixelForTest =
+                (int) (960 * mContext.getResources().getDisplayMetrics().density);
+        int screenHeightInPixelForTest =
+                (int) (960 * mContext.getResources().getDisplayMetrics().density);
+        int numberOfThumbs = 3;
+        mBar.setNumberOfThumbs(numberOfThumbs);
+        mBar.measure(View.MeasureSpec.makeMeasureSpec(
+                screenWidthInPixelForTest, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(
+                        screenHeightInPixelForTest, View.MeasureSpec.EXACTLY));
+        assertEquals(mBar.getChildCount(), numberOfThumbs);
+    }
+}
diff --git a/v17/leanback/tests/res/layout/horizontal_linear_wrap_content.xml b/v17/leanback/tests/res/layout/horizontal_linear_wrap_content.xml
new file mode 100644
index 0000000..c0e2715
--- /dev/null
+++ b/v17/leanback/tests/res/layout/horizontal_linear_wrap_content.xml
@@ -0,0 +1,39 @@
+<!--
+  ~ 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:lb="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+  <android.support.v17.leanback.widget.HorizontalGridViewEx
+      android:id="@+id/gridview"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
+      lb:rowHeight="wrap_content"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</LinearLayout>
diff --git a/v17/leanback/tests/res/layout/item_button_at_bottom.xml b/v17/leanback/tests/res/layout/item_button_at_bottom.xml
new file mode 100644
index 0000000..8afc622
--- /dev/null
+++ b/v17/leanback/tests/res/layout/item_button_at_bottom.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ 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.
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="400dp"
+    android:layout_height="400dp"
+    >
+    <TextView
+        android:layout_alignParentTop="true"
+        android:text="unfocusable text"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"/>
+    <Button
+        android:layout_alignParentBottom="true"
+        android:text="button"
+        android:focusable="true"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"/>
+</RelativeLayout>
diff --git a/v17/leanback/tests/res/layout/item_button_at_top.xml b/v17/leanback/tests/res/layout/item_button_at_top.xml
new file mode 100644
index 0000000..5199193
--- /dev/null
+++ b/v17/leanback/tests/res/layout/item_button_at_top.xml
@@ -0,0 +1,33 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="400dp"
+    android:layout_height="400dp"
+    >
+    <TextView
+        android:layout_alignParentBottom="true"
+        android:text="unfocusable text"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"/>
+    <Button
+        android:layout_alignParentTop="true"
+        android:text="button"
+        android:focusable="true"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"/>
+</RelativeLayout>
diff --git a/v17/leanback/tests/res/layout/relative_layout2.xml b/v17/leanback/tests/res/layout/relative_layout2.xml
new file mode 100644
index 0000000..597db38
--- /dev/null
+++ b/v17/leanback/tests/res/layout/relative_layout2.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    >
+  <TextView
+      android:id="@+id/t1"
+      android:paddingTop="4dp"
+      android:paddingBottom="8dp"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:text="Line 1" />
+  <TextView
+      android:id="@+id/t2"
+      android:paddingTop="4dp"
+      android:paddingBottom="8dp"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:layout_below="@id/t1"
+      android:text="Line 2" />
+
+</RelativeLayout>
diff --git a/v17/leanback/tests/res/layout/relative_layout3.xml b/v17/leanback/tests/res/layout/relative_layout3.xml
new file mode 100644
index 0000000..04d6bc0
--- /dev/null
+++ b/v17/leanback/tests/res/layout/relative_layout3.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    >
+  <TextView
+      android:id="@+id/t1"
+      android:paddingStart="4dp"
+      android:paddingEnd="8dp"
+      android:layout_width="50dp"
+      android:layout_height="wrap_content"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:text="Line 1" />
+  <TextView
+      android:id="@+id/t2"
+      android:paddingStart="4dp"
+      android:paddingEnd="8dp"
+      android:layout_width="50dp"
+      android:layout_height="wrap_content"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:layout_toEndOf="@id/t1"
+      android:text="Line 2" />
+
+</RelativeLayout>
diff --git a/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml b/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml
index 02b027c..374dc47 100644
--- a/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml
+++ b/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml
@@ -20,7 +20,7 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      android_horizontalSpacing="12dip"
+      android:horizontalSpacing="12dip"
       android:verticalSpacing="4dip"
       lb:numberOfColumns="1"
       android:paddingBottom="12dip"
diff --git a/v17/preference-leanback/Android.mk b/v17/preference-leanback/Android.mk
index 8c0488f..263d334 100644
--- a/v17/preference-leanback/Android.mk
+++ b/v17/preference-leanback/Android.mk
@@ -35,7 +35,6 @@
     $(call all-java-files-under,api21) \
     $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v17-leanback \
     android-support-v14-preference \
diff --git a/v17/preference-leanback/AndroidManifest-make.xml b/v17/preference-leanback/AndroidManifest-make.xml
deleted file mode 100644
index e2cfe35..0000000
--- a/v17/preference-leanback/AndroidManifest-make.xml
+++ /dev/null
@@ -1,24 +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
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.support.v17.preference"
-    android:versionCode="1"
-    android:versionName="1.0">
-    <uses-sdk android:minSdkVersion="17" />
-    <application />
-</manifest>
diff --git a/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java b/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
index 893af77..0396469 100644
--- a/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
+++ b/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Outline;
 import android.support.annotation.RequiresApi;
@@ -35,7 +34,6 @@
  * @hide
  */
 @RequiresApi(21)
-@TargetApi(21)
 @RestrictTo(LIBRARY_GROUP)
 public class OutlineOnlyWithChildrenFrameLayout extends FrameLayout {
 
diff --git a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
index e258686..955ab9e 100644
--- a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
+++ b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
@@ -30,7 +29,6 @@
  * @hide
  */
 @RequiresApi(21)
-@TargetApi(21)
 @RestrictTo(LIBRARY_GROUP)
 public class LeanbackPreferenceFragmentTransitionHelperApi21 {
 
diff --git a/v17/preference-leanback/build.gradle b/v17/preference-leanback/build.gradle
index e58fa8b..23672e3 100644
--- a/v17/preference-leanback/build.gradle
+++ b/v17/preference-leanback/build.gradle
@@ -1,79 +1,31 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'preference-leanback-v17'
 
 dependencies {
-    compile project(':support-v4')
-    compile project(':support-appcompat-v7')
-    compile project(':support-recyclerview-v7')
-    compile project(':support-preference-v7')
-    compile project(':support-preference-v14')
-    compile project(':support-leanback-v17')
+    api project(':support-v4')
+    api project(':support-appcompat-v7')
+    api project(':support-recyclerview-v7')
+    api project(':support-preference-v7')
+    api project(':support-preference-v14')
+    api project(':support-leanback-v17')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
+    defaultConfig {
+        minSdkVersion 17
+    }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
                 'api21',
                 'src'
         ]
         main.res.srcDir 'res'
     }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Leanback Preference v17'
-                description "Android Support Leanback Preference v17"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
+supportLibrary {
+    name 'Android Support Leanback Preference v17'
+    inceptionYear '2015'
+    description 'Android Support Leanback Preference v17'
+}
\ No newline at end of file
diff --git a/v17/preference-leanback/lint-baseline.xml b/v17/preference-leanback/lint-baseline.xml
new file mode 100644
index 0000000..22aaf9d
--- /dev/null
+++ b/v17/preference-leanback/lint-baseline.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        &lt;Space android:layout_width=&quot;0dp&quot; android:layout_height=&quot;@dimen/lb_preference_item_text_space_top&quot; />"
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/leanback_list_preference_item_multi.xml"
+            line="42"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        &lt;Space android:layout_width=&quot;0dp&quot; android:layout_height=&quot;@dimen/lb_preference_item_text_space_top&quot; />"
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/leanback_list_preference_item_single.xml"
+            line="42"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        &lt;Space android:layout_width=&quot;0dp&quot; android:layout_height=&quot;@dimen/lb_preference_item_text_space_top&quot; />"
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/leanback_preference.xml"
+            line="47"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        &lt;Space android:layout_width=&quot;0dp&quot; android:layout_height=&quot;@dimen/lb_preference_item_text_space_top&quot; />"
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/leanback_preference_information.xml"
+            line="34"
+            column="16"/>
+    </issue>
+
+</issues>
diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml b/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml
index f073f3e..ab299de 100644
--- a/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml
+++ b/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml
@@ -59,8 +59,10 @@
         android:paddingEnd="56dp"
         android:visibility="gone" />
 
-    <android.support.v17.leanback.widget.VerticalGridView android:id="@android:id/list"
+    <android.support.v17.leanback.widget.VerticalGridView
+        android:id="@android:id/list"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent"
+        android:transitionGroup="true"/>
 
 </LinearLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml b/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml
index c121d77..86e2ddf 100644
--- a/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml
+++ b/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml
@@ -28,7 +28,7 @@
               android:clipToPadding="false">
 
     <ImageView
-            android:id="@+android:id/icon"
+            android:id="@android:id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
@@ -43,7 +43,7 @@
             android:clipChildren="false"
             android:clipToPadding="false">
 
-        <TextView android:id="@+android:id/title"
+        <TextView android:id="@android:id/title"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:singleLine="true"
@@ -54,7 +54,7 @@
                   android:textColor="@color/lb_preference_item_primary_text_color"
                   android:textSize="@dimen/lb_preference_item_primary_text_size"/>
 
-        <TextView android:id="@+android:id/summary"
+        <TextView android:id="@android:id/summary"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_below="@android:id/title"
@@ -97,4 +97,4 @@
 
     </RelativeLayout>
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java b/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java
index 2273fb6..c9837de 100644
--- a/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java
+++ b/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java
@@ -21,6 +21,7 @@
 import android.support.annotation.Nullable;
 import android.support.v14.preference.MultiSelectListPreference;
 import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v4.util.ArraySet;
 import android.support.v7.preference.DialogPreference;
 import android.support.v7.preference.ListPreference;
 import android.support.v7.widget.RecyclerView;
@@ -31,13 +32,34 @@
 import android.widget.Checkable;
 import android.widget.TextView;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
 public class LeanbackListPreferenceDialogFragment extends LeanbackPreferenceDialogFragment {
 
+    private static final String SAVE_STATE_IS_MULTI =
+            "LeanbackListPreferenceDialogFragment.isMulti";
+    private static final String SAVE_STATE_ENTRIES = "LeanbackListPreferenceDialogFragment.entries";
+    private static final String SAVE_STATE_ENTRY_VALUES =
+            "LeanbackListPreferenceDialogFragment.entryValues";
+    private static final String SAVE_STATE_TITLE = "LeanbackListPreferenceDialogFragment.title";
+    private static final String SAVE_STATE_MESSAGE = "LeanbackListPreferenceDialogFragment.message";
+    private static final String SAVE_STATE_INITIAL_SELECTIONS =
+            "LeanbackListPreferenceDialogFragment.initialSelections";
+    private static final String SAVE_STATE_INITIAL_SELECTION =
+            "LeanbackListPreferenceDialogFragment.initialSelection";
+
+    private boolean mMulti;
+    private CharSequence[] mEntries;
+    private CharSequence[] mEntryValues;
+    private CharSequence mDialogTitle;
+    private CharSequence mDialogMessage;
+    private Set<String> mInitialSelections;
+    private String mInitialSelection;
+
     public static LeanbackListPreferenceDialogFragment newInstanceSingle(String key) {
-        final Bundle args = new Bundle(5);
+        final Bundle args = new Bundle(1);
         args.putString(ARG_KEY, key);
 
         final LeanbackListPreferenceDialogFragment
@@ -48,7 +70,7 @@
     }
 
     public static LeanbackListPreferenceDialogFragment newInstanceMulti(String key) {
-        final Bundle args = new Bundle(5);
+        final Bundle args = new Bundle(1);
         args.putString(ARG_KEY, key);
 
         final LeanbackListPreferenceDialogFragment
@@ -62,11 +84,58 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        final DialogPreference preference = getPreference();
-        if (!(preference instanceof ListPreference) &&
-                !(preference instanceof MultiSelectListPreference)) {
-            throw new IllegalArgumentException("Preference must be a ListPreference or " +
-                    "MultiSelectListPreference");
+        if (savedInstanceState == null) {
+            final DialogPreference preference = getPreference();
+            mDialogTitle = preference.getDialogTitle();
+            mDialogMessage = preference.getDialogMessage();
+
+            if (preference instanceof ListPreference) {
+                mMulti = false;
+                mEntries = ((ListPreference) preference).getEntries();
+                mEntryValues = ((ListPreference) preference).getEntryValues();
+                mInitialSelection = ((ListPreference) preference).getValue();
+            } else if (preference instanceof MultiSelectListPreference) {
+                mMulti = true;
+                mEntries = ((MultiSelectListPreference) preference).getEntries();
+                mEntryValues = ((MultiSelectListPreference) preference).getEntryValues();
+                mInitialSelections = ((MultiSelectListPreference) preference).getValues();
+            } else {
+                throw new IllegalArgumentException("Preference must be a ListPreference or "
+                        + "MultiSelectListPreference");
+            }
+        } else {
+            mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE);
+            mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE);
+            mMulti = savedInstanceState.getBoolean(SAVE_STATE_IS_MULTI);
+            mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
+            mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
+            if (mMulti) {
+                final String[] initialSelections = savedInstanceState.getStringArray(
+                        SAVE_STATE_INITIAL_SELECTIONS);
+                mInitialSelections = new ArraySet<>(
+                        initialSelections != null ? initialSelections.length : 0);
+                if (initialSelections != null) {
+                    Collections.addAll(mInitialSelections, initialSelections);
+                }
+            } else {
+                mInitialSelection = savedInstanceState.getString(SAVE_STATE_INITIAL_SELECTION);
+            }
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle);
+        outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage);
+        outState.putBoolean(SAVE_STATE_IS_MULTI, mMulti);
+        outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
+        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
+        if (mMulti) {
+            outState.putStringArray(SAVE_STATE_INITIAL_SELECTIONS,
+                    mInitialSelections.toArray(new String[mInitialSelections.size()]));
+        } else {
+            outState.putString(SAVE_STATE_INITIAL_SELECTION, mInitialSelection);
         }
     }
 
@@ -83,14 +152,13 @@
         verticalGridView.setAdapter(onCreateAdapter());
         verticalGridView.requestFocus();
 
-        final DialogPreference preference = getPreference();
-        final CharSequence title = preference.getDialogTitle();
+        final CharSequence title = mDialogTitle;
         if (!TextUtils.isEmpty(title)) {
             final TextView titleView = (TextView) view.findViewById(R.id.decor_title);
             titleView.setText(title);
         }
 
-        final CharSequence message = preference.getDialogMessage();
+        final CharSequence message = mDialogMessage;
         if (!TextUtils.isEmpty(message)) {
             final TextView messageView = (TextView) view.findViewById(android.R.id.message);
             messageView.setVisibility(View.VISIBLE);
@@ -101,21 +169,11 @@
     }
 
     public RecyclerView.Adapter onCreateAdapter() {
-        final DialogPreference preference = getPreference();
-        if (preference instanceof MultiSelectListPreference) {
-            final MultiSelectListPreference pref = (MultiSelectListPreference) preference;
-            final CharSequence[] entries = pref.getEntries();
-            final CharSequence[] entryValues = pref.getEntryValues();
-            final Set<String> initialSelections = pref.getValues();
-            return new AdapterMulti(entries, entryValues, initialSelections);
-        } else if (preference instanceof ListPreference) {
-            final ListPreference pref = (ListPreference) preference;
-            final CharSequence[] entries = pref.getEntries();
-            final CharSequence[] entryValues = pref.getEntryValues();
-            final String initialSelection = pref.getValue();
-            return new AdapterSingle(entries, entryValues, initialSelection);
+        //final DialogPreference preference = getPreference();
+        if (mMulti) {
+            return new AdapterMulti(mEntries, mEntryValues, mInitialSelections);
         } else {
-            throw new IllegalStateException("Unknown preference type");
+            return new AdapterSingle(mEntries, mEntryValues, mInitialSelection);
         }
     }
 
@@ -224,6 +282,7 @@
             // Pass copies of the set to callChangeListener and setValues to avoid mutations
             if (multiSelectListPreference.callChangeListener(new HashSet<>(mSelections))) {
                 multiSelectListPreference.setValues(new HashSet<>(mSelections));
+                mInitialSelections = mSelections;
             } else {
                 // Change refused, back it out
                 if (mSelections.contains(entry)) {
diff --git a/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceDialogFragment.java b/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceDialogFragment.java
index 614bc32..9c1335c 100644
--- a/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceDialogFragment.java
+++ b/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceDialogFragment.java
@@ -39,18 +39,18 @@
 
         final Fragment rawFragment = getTargetFragment();
         if (!(rawFragment instanceof DialogPreference.TargetFragment)) {
-            throw new IllegalStateException("Target fragment must implement TargetFragment" +
-                    " interface");
+            throw new IllegalStateException("Target fragment " + rawFragment
+                    + " must implement TargetFragment interface");
         }
-
-        final DialogPreference.TargetFragment fragment =
-                (DialogPreference.TargetFragment) rawFragment;
-
-        final String key = getArguments().getString(LeanbackListPreferenceDialogFragment.ARG_KEY);
-        mPreference = (DialogPreference) fragment.findPreference(key);
     }
 
     public DialogPreference getPreference() {
+        if (mPreference == null) {
+            final String key = getArguments().getString(ARG_KEY);
+            final DialogPreference.TargetFragment fragment =
+                    (DialogPreference.TargetFragment) getTargetFragment();
+            mPreference = (DialogPreference) fragment.findPreference(key);
+        }
         return mPreference;
     }
 }
diff --git a/v17/preference-leanback/src/android/support/v17/preference/LeanbackSettingsFragment.java b/v17/preference-leanback/src/android/support/v17/preference/LeanbackSettingsFragment.java
index cac02b7..08f19c4 100644
--- a/v17/preference-leanback/src/android/support/v17/preference/LeanbackSettingsFragment.java
+++ b/v17/preference-leanback/src/android/support/v17/preference/LeanbackSettingsFragment.java
@@ -98,7 +98,11 @@
     }
 
     @Override
-    public boolean onPreferenceDisplayDialog(PreferenceFragment caller, Preference pref) {
+    public boolean onPreferenceDisplayDialog(@NonNull PreferenceFragment caller, Preference pref) {
+        if (caller == null) {
+            throw new IllegalArgumentException("Cannot display dialog for preference " + pref
+                    + ", Caller must not be null!");
+        }
         final Fragment f;
         if (pref instanceof ListPreference) {
             final ListPreference listPreference = (ListPreference) pref;
diff --git a/v4/Android.mk b/v4/Android.mk
index c7e35aa..a9c9145 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -35,7 +35,6 @@
     android-support-fragment \
     android-support-annotations
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v4/AndroidManifest-make.xml b/v4/AndroidManifest-make.xml
deleted file mode 100644
index d76c581..0000000
--- a/v4/AndroidManifest-make.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.v4">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.v4"/>
-    <application />
-</manifest>
diff --git a/v4/AndroidManifest.xml b/v4/AndroidManifest.xml
index 6532182..470f5c2 100644
--- a/v4/AndroidManifest.xml
+++ b/v4/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v4">
-    <uses-sdk android:minSdkVersion="9" tools:overrideLibrary="android.support.v4"/>
+    <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.v4"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v4/build.gradle b/v4/build.gradle
index f226a87..d363eba 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -1,64 +1,24 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'support-v4'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-media-compat')
-    compile project(':support-core-utils')
-    compile project(':support-core-ui')
-    compile project(':support-fragment')
+    api project(':support-compat')
+    api project(':support-media-compat')
+    api project(':support-core-utils')
+    api project(':support-core-ui')
+    api project(':support-fragment')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
     }
-
-    sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
 }
 
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v4'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Library v4'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
 }
diff --git a/v4/lint-baseline.xml b/v4/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v4/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v7/appcompat/.classpath b/v7/appcompat/.classpath
deleted file mode 100644
index a4763d1..0000000
--- a/v7/appcompat/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/appcompat/.gitignore b/v7/appcompat/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v7/appcompat/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v7/appcompat/.project b/v7/appcompat/.project
deleted file mode 100644
index 957d33d..0000000
--- a/v7/appcompat/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>android-support-v7-appcompat</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/v7/appcompat/Android.mk b/v7/appcompat/Android.mk
index 3c5b44c..93baa95 100644
--- a/v7/appcompat/Android.mk
+++ b/v7/appcompat/Android.mk
@@ -28,7 +28,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-vectordrawable \
     android-support-animatedvectordrawable
diff --git a/v7/appcompat/AndroidManifest-make.xml b/v7/appcompat/AndroidManifest-make.xml
deleted file mode 100644
index 99b77ee..0000000
--- a/v7/appcompat/AndroidManifest-make.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.v7.appcompat">
-    <uses-sdk android:minSdkVersion="9"
-              tools:overrideLibrary="android.support.graphics.drawable.animated"/>
-    <application/>
-</manifest>
diff --git a/v7/appcompat/AndroidManifest.xml b/v7/appcompat/AndroidManifest.xml
index 183d83b..7de91ff 100644
--- a/v7/appcompat/AndroidManifest.xml
+++ b/v7/appcompat/AndroidManifest.xml
@@ -16,7 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v7.appcompat">
-    <uses-sdk android:minSdkVersion="9"
+    <uses-sdk android:minSdkVersion="14"
               tools:overrideLibrary="android.support.graphics.drawable.animated"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
diff --git a/v7/appcompat/README.txt b/v7/appcompat/README.txt
deleted file mode 100644
index 8e8de05..0000000
--- a/v7/appcompat/README.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Library Project including compatibility ActionBar.
-
-This can be used by an Android project to provide
-access to ActionBar on applications running on API 7+.
-
-There is technically no source, but the src folder is necessary
-to ensure that the build system works.  The content is actually
-located in libs/android-support-v7-appcompat.jar.
-The accompanying resources must also be included in the application.
-
diff --git a/v7/appcompat/THEMES.txt b/v7/appcompat/THEMES
similarity index 100%
rename from v7/appcompat/THEMES.txt
rename to v7/appcompat/THEMES
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 4935085..20d9393 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -1,103 +1,45 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'appcompat-v7'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-v4')
-    compile project(':support-vector-drawable')
-    compile project(':support-animated-vector-drawable')
+    api project(':support-annotations')
+    api project(':support-v4')
+    api project(':support-vector-drawable')
+    api project(':support-animated-vector-drawable')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile 'org.mockito:mockito-core:1.9.5'
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+    androidTestImplementation project(':support-testutils')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
         main.res.srcDirs 'res', 'res-public'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 
     aaptOptions {
         additionalParameters "--no-version-vectors"
+        noCompress 'ttf'
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
+supportLibrary {
+    name 'Android AppCompat Library v7'
+    inceptionYear '2011'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android AppCompat Library v7'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/v7/appcompat/lint-baseline.xml b/v7/appcompat/lint-baseline.xml
new file mode 100644
index 0000000..f1b0c14
--- /dev/null
+++ b/v7/appcompat/lint-baseline.xml
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="MissingPermission"
+        message="Missing permissions required by LocationManager.getLastKnownLocation: android.permission.ACCESS_COARSE_LOCATION or android.permission.ACCESS_FINE_LOCATION"
+        errorLine1="                    return mLocationManager.getLastKnownLocation(provider);"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/app/TwilightManager.java"
+            line="128"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="MissingSuperCall"
+        message="Overriding method should call `super.draw`"
+        errorLine1="    public void draw(Canvas canvas) {"
+        errorLine2="                ~~~~">
+        <location
+            file="src/android/support/v7/widget/ViewStubCompat.java"
+            line="151"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="DuplicateIds"
+        message="Duplicate id `@+id/image`, already defined earlier in this layout"
+        errorLine1="        &lt;ImageView android:id=&quot;@+id/image&quot;"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/abc_activity_chooser_view.xml"
+            line="62"
+            column="20"/>
+        <location
+            file="res/layout/abc_activity_chooser_view.xml"
+            line="40"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Expected resource of type xml"
+        errorLine1="                    final XmlPullParser parser = res.getXml(resId);"
+        errorLine2="                                                            ~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="347"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Expected resource of type layout"
+        errorLine1="            parser = mContext.getResources().getLayout(menuRes);"
+        errorLine2="                                                       ~~~~~~~">
+        <location
+            file="src/android/support/v7/view/SupportMenuInflater.java"
+            line="123"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="Range"
+        message="Value must be ≥ 0 (was -2147483648)"
+        errorLine1="                                MeasureSpec.makeMeasureSpec(largestChildHeight,"
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/LinearLayoutCompat.java"
+            line="868"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="Range"
+        message="Value must be ≥ 0 (was -2147483648)"
+        errorLine1="                                MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),"
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/LinearLayoutCompat.java"
+            line="1286"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 30 (ActionBarDrawerToggleHoneycomb)"
+        errorLine1="                Log.w(TAG, &quot;Couldn&apos;t set home-as-up indicator via JB-MR2 API&quot;, e);"
+        errorLine2="                      ~~~">
+        <location
+            file="src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java"
+            line="62"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 30 (ActionBarDrawerToggleHoneycomb)"
+        errorLine1="            Log.w(TAG, &quot;Couldn&apos;t set home-as-up indicator&quot;);"
+        errorLine2="                  ~~~">
+        <location
+            file="src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java"
+            line="67"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 30 (ActionBarDrawerToggleHoneycomb)"
+        errorLine1="                Log.w(TAG, &quot;Couldn&apos;t set content description via JB-MR2 API&quot;, e);"
+        errorLine2="                      ~~~">
+        <location
+            file="src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java"
+            line="87"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (AppCompatDrawableManager)"
+        errorLine1="                        Log.d(TAG, &quot;[loadDrawableFromDelegates] Skipping drawable: &quot;"
+        errorLine2="                              ~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="315"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (AppCompatDrawableManager)"
+        errorLine1="                    Log.i(TAG, &quot;[loadDrawableFromDelegates] Returning cached drawable: &quot; +"
+        errorLine2="                          ~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="337"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (AppCompatDrawableManager)"
+        errorLine1="                            Log.i(TAG, &quot;[loadDrawableFromDelegates] Saved drawable to cache: &quot; +"
+        errorLine2="                                  ~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="372"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (AppCompatDrawableManager)"
+        errorLine1="                    Log.e(TAG, &quot;Exception while inflating drawable&quot;, e);"
+        errorLine2="                          ~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="377"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (AppCompatDrawableManager)"
+        errorLine1="                Log.d(TAG, &quot;[tintDrawableUsingColorFilter] Tinted &quot;"
+        errorLine2="                      ~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="482"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (AppCompatDrawableManager)"
+        errorLine1="            Log.d(TAG, &quot;Mutated drawable is not the same instance as the input.&quot;);"
+        errorLine2="                  ~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatDrawableManager.java"
+            line="698"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: PixelFormat.UNKNOWN, PixelFormat.TRANSLUCENT, PixelFormat.TRANSPARENT, PixelFormat.OPAQUE"
+        errorLine1="        return 0;"
+        errorLine2="               ~">
+        <location
+            file="src/android/support/v7/widget/ActionBarBackgroundDrawable.java"
+            line="59"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: TextView.AUTO_SIZE_TEXT_TYPE_NONE, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine1="                    ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatButton.java"
+            line="274"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: TextView.AUTO_SIZE_TEXT_TYPE_NONE, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine1="                    : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatButton.java"
+            line="275"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: TextView.AUTO_SIZE_TEXT_TYPE_NONE, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine1="        return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;"
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatButton.java"
+            line="281"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: TextView.AUTO_SIZE_TEXT_TYPE_NONE, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine1="                    ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatTextView.java"
+            line="280"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: TextView.AUTO_SIZE_TEXT_TYPE_NONE, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine1="                    : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatTextView.java"
+            line="281"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one of: TextView.AUTO_SIZE_TEXT_TYPE_NONE, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM"
+        errorLine1="        return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;"
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/widget/AppCompatTextView.java"
+            line="287"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one or more of: ActionBar.DISPLAY_USE_LOGO, ActionBar.DISPLAY_SHOW_HOME, ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_CUSTOM"
+        errorLine1="        setDisplayOptions(options, 0xffffffff);"
+        errorLine2="                                   ~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/app/ToolbarActionBar.java"
+            line="254"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
+        message="Must be one or more of: ActionBar.DISPLAY_USE_LOGO, ActionBar.DISPLAY_SHOW_HOME, ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_CUSTOM"
+        errorLine1="        setDisplayOptions(options, 0xffffffff);"
+        errorLine2="                                   ~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/app/ToolbarActionBar.java"
+            line="254"
+            column="36"/>
+    </issue>
+
+</issues>
diff --git a/v7/appcompat/project.properties b/v7/appcompat/project.properties
deleted file mode 100644
index 91d2b02..0000000
--- a/v7/appcompat/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
-android.library=true
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index a7829eb..8c9f9e9 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -54,6 +54,11 @@
      <public type="attr" name="arrowHeadLength"/>
      <public type="attr" name="arrowShaftLength"/>
      <public type="attr" name="autoCompleteTextViewStyle"/>
+     <public type="attr" name="autoSizeTextType"/>
+     <public type="attr" name="autoSizeStepGranularity"/>
+     <public type="attr" name="autoSizePresetSizes"/>
+     <public type="attr" name="autoSizeMinTextSize"/>
+     <public type="attr" name="autoSizeMaxTextSize"/>
      <public type="attr" name="background"/>
      <public type="attr" name="backgroundSplit"/>
      <public type="attr" name="backgroundStacked"/>
@@ -84,6 +89,7 @@
      <public type="attr" name="colorControlActivated"/>
      <public type="attr" name="colorControlHighlight"/>
      <public type="attr" name="colorControlNormal"/>
+     <public type="attr" name="colorError"/>
      <public type="attr" name="colorPrimary"/>
      <public type="attr" name="colorPrimaryDark"/>
      <public type="attr" name="commitIcon"/>
@@ -108,6 +114,7 @@
      <public type="attr" name="editTextColor"/>
      <public type="attr" name="editTextStyle"/>
      <public type="attr" name="elevation"/>
+     <public type="attr" name="fontFamily"/>
      <public type="attr" name="gapBetweenBars"/>
      <public type="attr" name="goIcon"/>
      <public type="attr" name="height"/>
@@ -116,6 +123,8 @@
      <public type="attr" name="homeLayout"/>
      <public type="attr" name="icon"/>
      <public type="attr" name="iconifiedByDefault"/>
+     <public type="attr" name="iconTint"/>
+     <public type="attr" name="iconTintMode"/>
      <public type="attr" name="imageButtonStyle"/>
      <public type="attr" name="indeterminateProgressStyle"/>
      <public type="attr" name="isLightTheme"/>
diff --git a/v7/appcompat/res/anim/tooltip_enter.xml b/v7/appcompat/res/anim/tooltip_enter.xml
new file mode 100644
index 0000000..134d9d7
--- /dev/null
+++ b/v7/appcompat/res/anim/tooltip_enter.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+       android:interpolator="@android:interpolator/decelerate_quad"
+       android:fromAlpha="0.0" android:toAlpha="1.0"
+       android:duration="@integer/config_tooltipAnimTime" />
diff --git a/v7/appcompat/res/anim/tooltip_exit.xml b/v7/appcompat/res/anim/tooltip_exit.xml
new file mode 100644
index 0000000..67f6af8
--- /dev/null
+++ b/v7/appcompat/res/anim/tooltip_exit.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+       android:interpolator="@android:interpolator/accelerate_quad"
+       android:fromAlpha="1.0" android:toAlpha="0.0"
+       android:duration="@integer/config_tooltipAnimTime" />
diff --git a/v7/appcompat/res/color-v23/abc_tint_switch_thumb.xml b/v7/appcompat/res/color-v23/abc_tint_switch_thumb.xml
deleted file mode 100644
index f589fdf..0000000
--- a/v7/appcompat/res/color-v23/abc_tint_switch_thumb.xml
+++ /dev/null
@@ -1,21 +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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="?attr/colorSwitchThumbNormal" android:alpha="?android:attr/disabledAlpha"/>
-    <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
-    <item android:color="?attr/colorSwitchThumbNormal"/>
-</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_switch_thumb.xml b/v7/appcompat/res/color/abc_tint_switch_thumb.xml
deleted file mode 100644
index fc8bd24..0000000
--- a/v7/appcompat/res/color/abc_tint_switch_thumb.xml
+++ /dev/null
@@ -1,22 +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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:app="http://schemas.android.com/apk/res-auto">
-    <item android:state_enabled="false" android:color="?attr/colorSwitchThumbNormal" app:alpha="?android:attr/disabledAlpha"/>
-    <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
-    <item android:color="?attr/colorSwitchThumbNormal"/>
-</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable-v21/notification_action_background.xml b/v7/appcompat/res/drawable-v21/notification_action_background.xml
deleted file mode 100644
index 852c3f0..0000000
--- a/v7/appcompat/res/drawable-v21/notification_action_background.xml
+++ /dev/null
@@ -1,22 +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
-  -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/ripple_material_light">
-    <item android:id="@android:id/mask"
-        android:drawable="@drawable/abc_btn_default_mtrl_shape" />
-</ripple>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/tooltip_frame_dark.xml b/v7/appcompat/res/drawable/tooltip_frame_dark.xml
new file mode 100644
index 0000000..43c2f99
--- /dev/null
+++ b/v7/appcompat/res/drawable/tooltip_frame_dark.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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="@color/tooltip_background_dark" />
+    <corners android:radius="@dimen/tooltip_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/tooltip_frame_light.xml b/v7/appcompat/res/drawable/tooltip_frame_light.xml
new file mode 100644
index 0000000..20966d5
--- /dev/null
+++ b/v7/appcompat/res/drawable/tooltip_frame_light.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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="@color/tooltip_background_light" />
+    <corners android:radius="@dimen/tooltip_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v11/notification_media_action.xml b/v7/appcompat/res/layout-v11/notification_media_action.xml
deleted file mode 100644
index d546792..0000000
--- a/v7/appcompat/res/layout-v11/notification_media_action.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/borderlessButtonStyle"
-    android:id="@+id/action0"
-    android:layout_width="48dp"
-    android:layout_height="match_parent"
-    android:layout_marginLeft="2dp"
-    android:layout_marginRight="2dp"
-    android:layout_weight="1"
-    android:gravity="center"/>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v11/notification_media_cancel_action.xml b/v7/appcompat/res/layout-v11/notification_media_cancel_action.xml
deleted file mode 100644
index c2bd8c2..0000000
--- a/v7/appcompat/res/layout-v11/notification_media_cancel_action.xml
+++ /dev/null
@@ -1,29 +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
-  -->
-
-<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    style="?android:attr/borderlessButtonStyle"
-    android:id="@+id/cancel_action"
-    android:layout_width="48dp"
-    android:layout_height="match_parent"
-    android:layout_marginLeft="2dp"
-    android:layout_marginRight="2dp"
-    android:layout_weight="1"
-    android:src="@android:drawable/ic_menu_close_clear_cancel"
-    android:gravity="center"
-    android:visibility="gone"/>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v11/notification_template_big_media.xml b/v7/appcompat/res/layout-v11/notification_template_big_media.xml
deleted file mode 100644
index b72fd97..0000000
--- a/v7/appcompat/res/layout-v11/notification_template_big_media.xml
+++ /dev/null
@@ -1,60 +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
-  -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="128dp"
-    >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-    />
-    <include layout="@layout/notification_media_cancel_action"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_marginLeft="2dp"
-        android:layout_marginRight="2dp"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentEnd="true" />
-    <include layout="@layout/notification_template_lines_media"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="fill_vertical"
-        android:layout_marginLeft="@dimen/notification_large_icon_width"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:layout_toLeftOf="@id/cancel_action"
-        android:layout_toStartOf="@id/cancel_action"/>
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_alignParentBottom="true"
-        android:layout_marginLeft="12dp"
-        android:layout_marginRight="12dp"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_above="@id/media_actions"
-        android:id="@+id/action_divider"
-        android:background="?android:attr/dividerHorizontal" />
-</RelativeLayout>
diff --git a/v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml
deleted file mode 100644
index c88d799..0000000
--- a/v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml
+++ /dev/null
@@ -1,107 +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
-  -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="128dp"
-    >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-    />
-    <include layout="@layout/notification_media_cancel_action"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_marginLeft="2dp"
-        android:layout_marginRight="2dp"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentEnd="true"/>
-    <LinearLayout
-        android:id="@+id/notification_main_column_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="@dimen/notification_large_icon_height"
-        android:layout_marginStart="@dimen/notification_large_icon_height"
-        android:minHeight="@dimen/notification_large_icon_height"
-        android:paddingTop="@dimen/notification_main_column_padding_top"
-        android:orientation="horizontal"
-        android:layout_toLeftOf="@id/cancel_action"
-        android:layout_toStartOf="@id/cancel_action">
-        <FrameLayout
-            android:id="@+id/notification_main_column"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_marginLeft="@dimen/notification_content_margin_start"
-            android:layout_marginStart="@dimen/notification_content_margin_start"
-            android:layout_marginRight="8dp"
-            android:layout_marginEnd="8dp"
-            android:layout_marginBottom="8dp"
-        />
-        <FrameLayout
-            android:id="@+id/right_side"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginRight="8dp"
-            android:layout_marginEnd="8dp"
-            android:paddingTop="@dimen/notification_right_side_padding_top">
-            <DateTimeView android:id="@+id/time"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:layout_gravity="end|top"
-                android:visibility="gone"
-            />
-            <Chronometer android:id="@+id/chronometer"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:layout_gravity="end|top"
-                android:visibility="gone"
-            />
-            <TextView android:id="@+id/info"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="20dp"
-                android:layout_gravity="end|bottom"
-                android:singleLine="true"
-            />
-        </FrameLayout>
-    </LinearLayout>
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_alignParentBottom="true"
-        android:layout_marginLeft="12dp"
-        android:layout_marginRight="12dp"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_above="@id/media_actions"
-        android:id="@+id/action_divider"
-        android:background="?android:attr/dividerHorizontal" />
-</RelativeLayout>
diff --git a/v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml
deleted file mode 100644
index 979c8f4..0000000
--- a/v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml
+++ /dev/null
@@ -1,68 +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
-  -->
-
-<!-- Layout to be used with only max 3 actions. It has a much larger picture at the left side-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="128dp"
-    >
-    <ImageView android:id="@+id/icon"
-        android:layout_width="128dp"
-        android:layout_height="128dp"
-        android:scaleType="centerCrop"
-        />
-
-    <include layout="@layout/notification_media_cancel_action"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_marginLeft="2dp"
-        android:layout_marginRight="2dp"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentEnd="true"/>
-
-    <include layout="@layout/notification_template_lines_media"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="128dp"
-        android:layout_marginStart="128dp"
-        android:layout_toLeftOf="@id/cancel_action"
-        android:layout_toStartOf="@id/cancel_action"/>
-
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_toRightOf="@id/icon"
-        android:layout_toEndOf="@id/icon"
-        android:layout_alignParentBottom="true"
-        android:layout_marginLeft="12dp"
-        android:layout_marginRight="12dp"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_toRightOf="@id/icon"
-        android:layout_toEndOf="@id/icon"
-        android:layout_above="@id/media_actions"
-        android:id="@+id/action_divider"
-        android:background="?android:attr/dividerHorizontal" />
-</RelativeLayout>
diff --git a/v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml
deleted file mode 100644
index b7fbff7..0000000
--- a/v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml
+++ /dev/null
@@ -1,115 +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
-  -->
-
-<!-- Layout to be used with only max 3 actions. It has a much larger picture at the left side-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="128dp"
-    >
-    <ImageView android:id="@+id/icon"
-        android:layout_width="128dp"
-        android:layout_height="128dp"
-        android:scaleType="centerCrop"
-        />
-
-    <include layout="@layout/notification_media_cancel_action"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_marginLeft="2dp"
-        android:layout_marginRight="2dp"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentEnd="true"/>
-
-    <LinearLayout
-        android:id="@+id/notification_main_column_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="128dp"
-        android:layout_marginStart="128dp"
-        android:minHeight="@dimen/notification_large_icon_height"
-        android:paddingTop="@dimen/notification_main_column_padding_top"
-        android:orientation="horizontal"
-        android:layout_toLeftOf="@id/cancel_action"
-        android:layout_toStartOf="@id/cancel_action">
-        <FrameLayout
-            android:id="@+id/notification_main_column"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_marginLeft="@dimen/notification_media_narrow_margin"
-            android:layout_marginStart="@dimen/notification_media_narrow_margin"
-            android:layout_marginRight="8dp"
-            android:layout_marginEnd="8dp"
-            android:layout_marginBottom="8dp"/>
-        <FrameLayout
-            android:id="@+id/right_side"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginRight="8dp"
-            android:layout_marginEnd="8dp"
-            android:paddingTop="@dimen/notification_right_side_padding_top">
-            <DateTimeView android:id="@+id/time"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:layout_gravity="end|top"
-                android:visibility="gone"
-            />
-            <Chronometer android:id="@+id/chronometer"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:layout_gravity="end|top"
-                android:visibility="gone"
-            />
-            <TextView android:id="@+id/info"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="20dp"
-                android:layout_gravity="end|bottom"
-                android:singleLine="true"
-            />
-        </FrameLayout>
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_toRightOf="@id/icon"
-        android:layout_toEndOf="@id/icon"
-        android:layout_alignParentBottom="true"
-        android:layout_marginLeft="12dp"
-        android:layout_marginRight="12dp"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_toRightOf="@id/icon"
-        android:layout_toEndOf="@id/icon"
-        android:layout_above="@id/media_actions"
-        android:id="@+id/action_divider"
-        android:background="?android:attr/dividerHorizontal" />
-</RelativeLayout>
diff --git a/v7/appcompat/res/layout-v16/notification_template_custom_big.xml b/v7/appcompat/res/layout-v16/notification_template_custom_big.xml
deleted file mode 100644
index 24c3323..0000000
--- a/v7/appcompat/res/layout-v16/notification_template_custom_big.xml
+++ /dev/null
@@ -1,117 +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
-  -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/notification_background"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" >
-    <ImageView android:id="@+id/icon"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        android:scaleType="center"
-    />
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:orientation="vertical" >
-        <LinearLayout
-            android:id="@+id/notification_main_column_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/notification_large_icon_width"
-            android:layout_marginStart="@dimen/notification_large_icon_width"
-            android:paddingTop="@dimen/notification_main_column_padding_top"
-            android:minHeight="@dimen/notification_large_icon_height"
-            android:orientation="horizontal">
-            <FrameLayout
-                android:id="@+id/notification_main_column"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:layout_marginLeft="@dimen/notification_content_margin_start"
-                android:layout_marginStart="@dimen/notification_content_margin_start"
-                android:layout_marginBottom="8dp"
-                android:layout_marginRight="8dp"
-                android:layout_marginEnd="8dp" />
-            <FrameLayout
-                android:id="@+id/right_side"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginRight="8dp"
-                android:layout_marginEnd="8dp"
-                android:paddingTop="@dimen/notification_right_side_padding_top">
-                <ViewStub android:id="@+id/time"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="end|top"
-                    android:visibility="gone"
-                    android:layout="@layout/notification_template_part_time" />
-                <ViewStub android:id="@+id/chronometer"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="end|top"
-                    android:visibility="gone"
-                    android:layout="@layout/notification_template_part_chronometer" />
-                <LinearLayout
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="horizontal"
-                    android:layout_gravity="end|bottom"
-                    android:layout_marginTop="20dp">
-                    <TextView android:id="@+id/info"
-                        android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:singleLine="true"
-                    />
-                    <ImageView android:id="@+id/right_icon"
-                        android:layout_width="16dp"
-                        android:layout_height="16dp"
-                        android:layout_gravity="center"
-                        android:layout_marginLeft="8dp"
-                        android:layout_marginStart="8dp"
-                        android:scaleType="centerInside"
-                        android:visibility="gone"
-                        android:alpha="0.6"
-                    />
-                </LinearLayout>
-            </FrameLayout>
-        </LinearLayout>
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1px"
-            android:id="@+id/action_divider"
-            android:visibility="gone"
-            android:layout_marginLeft="@dimen/notification_large_icon_width"
-            android:layout_marginStart="@dimen/notification_large_icon_width"
-            android:background="?android:attr/dividerHorizontal" />
-        <LinearLayout
-            android:id="@+id/actions"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:visibility="gone"
-            android:showDividers="middle"
-            android:divider="?android:attr/listDivider"
-            android:dividerPadding="12dp"
-            android:layout_marginLeft="@dimen/notification_large_icon_width"
-            android:layout_marginStart="@dimen/notification_large_icon_width" >
-            <!-- actions will be added here -->
-        </LinearLayout>
-    </LinearLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v21/notification_action.xml b/v7/appcompat/res/layout-v21/notification_action.xml
deleted file mode 100644
index c60bf7d..0000000
--- a/v7/appcompat/res/layout-v21/notification_action.xml
+++ /dev/null
@@ -1,41 +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"
-    style="@style/Widget.AppCompat.NotificationActionContainer"
-    android:id="@+id/action_container"
-    android:layout_width="0dp"
-    android:layout_weight="1"
-    android:layout_height="48dp"
-    android:paddingStart="4dp"
-    android:orientation="horizontal">
-    <ImageView
-        android:id="@+id/action_image"
-        android:layout_width="@dimen/notification_action_icon_size"
-        android:layout_height="@dimen/notification_action_icon_size"
-        android:layout_gravity="center|start"
-        android:scaleType="centerInside"/>
-    <TextView
-        style="@style/Widget.AppCompat.NotificationActionText"
-        android:id="@+id/action_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center|start"
-        android:paddingStart="4dp"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:clickable="false"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v21/notification_action_tombstone.xml b/v7/appcompat/res/layout-v21/notification_action_tombstone.xml
deleted file mode 100644
index 1637c6f..0000000
--- a/v7/appcompat/res/layout-v21/notification_action_tombstone.xml
+++ /dev/null
@@ -1,48 +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"
-    style="@style/Widget.AppCompat.NotificationActionContainer"
-    android:id="@+id/action_container"
-    android:layout_width="0dp"
-    android:layout_weight="1"
-    android:layout_height="48dp"
-    android:paddingStart="4dp"
-    android:orientation="horizontal"
-    android:enabled="false"
-    android:background="@null">
-    <ImageView
-        android:id="@+id/action_image"
-        android:layout_width="@dimen/notification_action_icon_size"
-        android:layout_height="@dimen/notification_action_icon_size"
-        android:layout_gravity="center|start"
-        android:scaleType="centerInside"
-        android:enabled="false"
-        android:alpha="0.5"/>
-    <TextView
-        style="@style/Widget.AppCompat.NotificationActionText"
-        android:id="@+id/action_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center|start"
-        android:paddingStart="4dp"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:clickable="false"
-        android:enabled="false"
-        android:alpha="0.5"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v21/notification_template_custom_big.xml b/v7/appcompat/res/layout-v21/notification_template_custom_big.xml
deleted file mode 100644
index 38332bd..0000000
--- a/v7/appcompat/res/layout-v21/notification_template_custom_big.xml
+++ /dev/null
@@ -1,90 +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
-  -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/notification_background"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-    />
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:orientation="vertical" >
-        <LinearLayout
-            android:id="@+id/notification_main_column_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_large_icon_height"
-            android:orientation="horizontal">
-            <FrameLayout
-                android:id="@+id/notification_main_column"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:layout_marginEnd="8dp"
-                android:layout_marginBottom="8dp"/>
-            <FrameLayout
-                android:id="@+id/right_side"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginEnd="8dp"
-                android:paddingTop="@dimen/notification_right_side_padding_top">
-                <ViewStub android:id="@+id/time"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="end|top"
-                    android:visibility="gone"
-                    android:layout="@layout/notification_template_part_time" />
-                <ViewStub android:id="@+id/chronometer"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="end|top"
-                    android:visibility="gone"
-                    android:layout="@layout/notification_template_part_chronometer" />
-                <TextView android:id="@+id/info"
-                    android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_marginTop="20dp"
-                    android:layout_gravity="end|bottom"
-                    android:singleLine="true"
-                />
-            </FrameLayout>
-        </LinearLayout>
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:id="@+id/action_divider"
-            android:visibility="gone"
-            android:background="#29000000" />
-        <LinearLayout
-            android:id="@+id/actions"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="-8dp"
-            android:orientation="horizontal"
-            android:visibility="gone"
-        >
-            <!-- actions will be added here -->
-        </LinearLayout>
-    </LinearLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout-v21/notification_template_icon_group.xml b/v7/appcompat/res/layout-v21/notification_template_icon_group.xml
deleted file mode 100644
index 6c19022..0000000
--- a/v7/appcompat/res/layout-v21/notification_template_icon_group.xml
+++ /dev/null
@@ -1,43 +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
-  -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/notification_large_icon_width"
-    android:layout_height="@dimen/notification_large_icon_height"
-    android:id="@+id/icon_group"
->
-    <ImageView android:id="@+id/icon"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/notification_big_circle_margin"
-        android:layout_marginBottom="@dimen/notification_big_circle_margin"
-        android:layout_marginStart="@dimen/notification_big_circle_margin"
-        android:layout_marginEnd="@dimen/notification_big_circle_margin"
-        android:scaleType="centerInside"
-    />
-    <ImageView android:id="@+id/right_icon"
-        android:layout_width="@dimen/notification_right_icon_size"
-        android:layout_height="@dimen/notification_right_icon_size"
-        android:layout_gravity="end|bottom"
-        android:scaleType="centerInside"
-        android:visibility="gone"
-        android:layout_marginEnd="8dp"
-        android:layout_marginBottom="8dp"
-    />
-</FrameLayout>
-
diff --git a/v7/appcompat/res/layout-v26/abc_screen_toolbar.xml b/v7/appcompat/res/layout-v26/abc_screen_toolbar.xml
new file mode 100644
index 0000000..26704ac
--- /dev/null
+++ b/v7/appcompat/res/layout-v26/abc_screen_toolbar.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<android.support.v7.widget.ActionBarOverlayLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/decor_content_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <include layout="@layout/abc_screen_content_include"/>
+
+    <android.support.v7.widget.ActionBarContainer
+        android:id="@+id/action_bar_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        style="?attr/actionBarStyle"
+        android:touchscreenBlocksFocus="true"
+        android:keyboardNavigationCluster="true"
+        android:gravity="top">
+
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:navigationContentDescription="@string/abc_action_bar_up_description"
+            style="?attr/toolbarStyle"/>
+
+        <android.support.v7.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            android:theme="?attr/actionBarTheme"
+            style="?attr/actionModeStyle"/>
+
+    </android.support.v7.widget.ActionBarContainer>
+
+</android.support.v7.widget.ActionBarOverlayLayout>
diff --git a/v7/appcompat/res/layout/abc_activity_chooser_view_list_item.xml b/v7/appcompat/res/layout/abc_activity_chooser_view_list_item.xml
index 887427d..cf8cb1a 100644
--- a/v7/appcompat/res/layout/abc_activity_chooser_view_list_item.xml
+++ b/v7/appcompat/res/layout/abc_activity_chooser_view_list_item.xml
@@ -21,7 +21,8 @@
               android:paddingLeft="16dip"
               android:paddingRight="16dip"
               android:minWidth="196dip"
-              android:orientation="vertical">
+              android:orientation="vertical"
+              android:background="?attr/selectableItemBackground"  >
 
     <LinearLayout
             android:layout_width="wrap_content"
diff --git a/v7/appcompat/res/layout/notification_action.xml b/v7/appcompat/res/layout/notification_action.xml
deleted file mode 100644
index 82e95a5..0000000
--- a/v7/appcompat/res/layout/notification_action.xml
+++ /dev/null
@@ -1,44 +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"
-    style="@style/Widget.AppCompat.NotificationActionContainer"
-    android:id="@+id/action_container"
-    android:layout_width="0dp"
-    android:layout_weight="1"
-    android:layout_height="48dp"
-    android:paddingLeft="4dp"
-    android:paddingStart="4dp"
-    android:orientation="horizontal">
-    <ImageView
-        android:id="@+id/action_image"
-        android:layout_width="@dimen/notification_action_icon_size"
-        android:layout_height="@dimen/notification_action_icon_size"
-        android:layout_gravity="center|start"
-        android:scaleType="centerInside"/>
-    <TextView
-        style="@style/Widget.AppCompat.NotificationActionText"
-        android:id="@+id/action_text"
-        android:textColor="#ccc"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center|start"
-        android:paddingLeft="4dp"
-        android:paddingStart="4dp"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:clickable="false"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/notification_action_tombstone.xml b/v7/appcompat/res/layout/notification_action_tombstone.xml
deleted file mode 100644
index d491c78..0000000
--- a/v7/appcompat/res/layout/notification_action_tombstone.xml
+++ /dev/null
@@ -1,51 +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"
-    style="@style/Widget.AppCompat.NotificationActionContainer"
-    android:id="@+id/action_container"
-    android:layout_width="0dp"
-    android:layout_weight="1"
-    android:layout_height="48dp"
-    android:paddingLeft="4dp"
-    android:paddingStart="4dp"
-    android:orientation="horizontal"
-    android:enabled="false"
-    android:background="@null">
-    <ImageView
-        android:id="@+id/action_image"
-        android:layout_width="@dimen/notification_action_icon_size"
-        android:layout_height="@dimen/notification_action_icon_size"
-        android:layout_gravity="center|start"
-        android:scaleType="centerInside"
-        android:enabled="false"
-        android:alpha="0.5"/>
-    <TextView
-        style="@style/Widget.AppCompat.NotificationActionText"
-        android:id="@+id/action_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center|start"
-        android:textColor="#ccc"
-        android:paddingLeft="4dp"
-        android:paddingStart="4dp"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:clickable="false"
-        android:enabled="false"
-        android:alpha="0.5"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/notification_template_custom_big.xml b/v7/appcompat/res/layout/notification_template_custom_big.xml
deleted file mode 100644
index c922629..0000000
--- a/v7/appcompat/res/layout/notification_template_custom_big.xml
+++ /dev/null
@@ -1,76 +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 android:id="@+id/notification_main_column_container"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="@dimen/notification_large_icon_height"
-    android:orientation="horizontal"
-    android:paddingRight="12dp"
-    android:paddingEnd="12dp">
-    <ImageView android:id="@+id/icon"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        android:background="@drawable/notification_tile_bg"
-        android:scaleType="center"
-    />
-    <FrameLayout
-        android:id="@+id/notification_main_column"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingLeft="12dp"
-        android:paddingStart="12dp"
-        android:paddingTop="@dimen/notification_main_column_padding_top"
-        android:layout_weight="1"/>
-    <FrameLayout
-        android:id="@+id/right_side"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingTop="12dp"
-        android:paddingLeft="12dp">
-        <include
-            layout="@layout/notification_template_part_time"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="end|top"
-            android:visibility="gone"/>
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="end|bottom"
-            android:layout_marginTop="18dp"
-            android:orientation="horizontal">
-            <TextView android:id="@+id/info"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:singleLine="true"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info"
-            />
-            <ImageView android:id="@+id/right_icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_gravity="center"
-                android:layout_marginLeft="8dp"
-                android:alpha="0.7"
-                android:scaleType="center"
-                android:visibility="gone"
-            />
-        </LinearLayout>
-    </FrameLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/notification_template_icon_group.xml b/v7/appcompat/res/layout/notification_template_icon_group.xml
deleted file mode 100644
index dd564f8..0000000
--- a/v7/appcompat/res/layout/notification_template_icon_group.xml
+++ /dev/null
@@ -1,25 +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
-  -->
-
-<ImageView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/icon"
-    android:layout_width="@dimen/notification_large_icon_width"
-    android:layout_height="@dimen/notification_large_icon_height"
-    android:scaleType="centerCrop"
-/>
-
diff --git a/v7/appcompat/res/layout/notification_template_lines_media.xml b/v7/appcompat/res/layout/notification_template_lines_media.xml
deleted file mode 100644
index 9a7b788..0000000
--- a/v7/appcompat/res/layout/notification_template_lines_media.xml
+++ /dev/null
@@ -1,112 +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"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:paddingRight="8dp"
-    android:paddingEnd="8dp"
-    android:paddingTop="2dp"
-    android:paddingBottom="2dp"
-    >
-    <LinearLayout
-        android:id="@+id/line1"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="6dp"
-        android:layout_marginLeft="@dimen/notification_content_margin_start"
-        android:layout_marginStart="@dimen/notification_content_margin_start"
-        android:orientation="horizontal"
-        >
-        <TextView android:id="@+id/title"
-            android:textAppearance="@style/TextAppearance.AppCompat.Notification.Title.Media"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            android:layout_weight="1"
-            />
-        <DateTimeView android:id="@+id/time"
-            android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:layout_gravity="center"
-            android:layout_weight="0"
-            android:visibility="gone"
-            android:paddingLeft="8dp"
-            android:paddingStart="8dp"
-        />
-        <Chronometer android:id="@+id/chronometer"
-            android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:layout_gravity="center"
-            android:layout_weight="0"
-            android:visibility="gone"
-            android:paddingLeft="8dp"
-            android:paddingStart="8dp"
-        />
-    </LinearLayout>
-    <TextView android:id="@+id/text2"
-        android:textAppearance="@style/TextAppearance.AppCompat.Notification.Line2.Media"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="-2dp"
-        android:layout_marginBottom="-2dp"
-        android:layout_marginLeft="@dimen/notification_content_margin_start"
-        android:layout_marginStart="@dimen/notification_content_margin_start"
-        android:singleLine="true"
-        android:fadingEdge="horizontal"
-        android:ellipsize="marquee"
-        android:visibility="gone"
-        />
-    <LinearLayout
-        android:id="@+id/line3"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:gravity="center_vertical"
-        android:layout_marginLeft="@dimen/notification_content_margin_start"
-        android:layout_marginStart="@dimen/notification_content_margin_start"
-        >
-        <TextView android:id="@+id/text"
-            android:textAppearance="@style/TextAppearance.AppCompat.Notification.Media"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_gravity="center"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            />
-        <TextView android:id="@+id/info"
-            android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info.Media"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:layout_weight="0"
-            android:singleLine="true"
-            android:gravity="center"
-            android:paddingLeft="8dp"
-            android:paddingStart="8dp"
-            />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/notification_template_media.xml b/v7/appcompat/res/layout/notification_template_media.xml
deleted file mode 100644
index 6eac23b7..0000000
--- a/v7/appcompat/res/layout/notification_template_media.xml
+++ /dev/null
@@ -1,51 +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"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="64dp"
-    android:orientation="horizontal"
-    >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-    />
-    <include layout="@layout/notification_template_lines_media"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"/>
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center_vertical|end"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <include layout="@layout/notification_media_cancel_action"
-        android:layout_width="48dp"
-        android:layout_height="match_parent"
-        android:layout_marginRight="6dp"
-        android:layout_marginEnd="6dp"/>
-    <ImageView android:id="@+id/end_padder"
-        android:layout_width="6dp"
-        android:layout_height="match_parent"
-        />
-</LinearLayout>
diff --git a/v7/appcompat/res/layout/notification_template_media_custom.xml b/v7/appcompat/res/layout/notification_template_media_custom.xml
deleted file mode 100644
index 62e07d4..0000000
--- a/v7/appcompat/res/layout/notification_template_media_custom.xml
+++ /dev/null
@@ -1,100 +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"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="64dp"
-    android:orientation="horizontal"
-    >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-    />
-    <LinearLayout
-        android:id="@+id/notification_main_column_container"
-        android:layout_width="0dp"
-        android:layout_weight="1"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/notification_main_column_padding_top"
-        android:minHeight="@dimen/notification_large_icon_height"
-        android:orientation="horizontal"
-        android:layout_toLeftOf="@id/cancel_action"
-        android:layout_toStartOf="@id/cancel_action">
-        <FrameLayout
-            android:id="@+id/notification_main_column"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_marginLeft="@dimen/notification_content_margin_start"
-            android:layout_marginStart="@dimen/notification_content_margin_start"
-            android:layout_marginRight="8dp"
-            android:layout_marginEnd="8dp"
-            android:layout_marginBottom="8dp"/>
-        <FrameLayout
-            android:id="@+id/right_side"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginRight="8dp"
-            android:layout_marginEnd="8dp"
-            android:paddingTop="@dimen/notification_right_side_padding_top">
-            <DateTimeView android:id="@+id/time"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:layout_gravity="end|top"
-                android:visibility="gone"
-            />
-            <Chronometer android:id="@+id/chronometer"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:layout_gravity="end|top"
-                android:visibility="gone"
-            />
-            <TextView android:id="@+id/info"
-                android:textAppearance="@style/TextAppearance.AppCompat.Notification.Info.Media"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="20dp"
-                android:layout_gravity="end|bottom"
-                android:singleLine="true"
-            />
-        </FrameLayout>
-    </LinearLayout>
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center_vertical|end"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <include layout="@layout/notification_media_cancel_action"
-        android:layout_width="48dp"
-        android:layout_height="match_parent"
-        android:layout_marginRight="6dp"
-        android:layout_marginEnd="6dp"/>
-    <ImageView android:id="@+id/end_padder"
-        android:layout_width="6dp"
-        android:layout_height="match_parent"
-        />
-</LinearLayout>
diff --git a/v7/appcompat/res/layout/notification_template_part_chronometer.xml b/v7/appcompat/res/layout/notification_template_part_chronometer.xml
deleted file mode 100644
index 9e58a38..0000000
--- a/v7/appcompat/res/layout/notification_template_part_chronometer.xml
+++ /dev/null
@@ -1,23 +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
-  -->
-
-<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:singleLine="true"
-    />
diff --git a/v7/appcompat/res/layout/notification_template_part_time.xml b/v7/appcompat/res/layout/notification_template_part_time.xml
deleted file mode 100644
index 810d1e3..0000000
--- a/v7/appcompat/res/layout/notification_template_part_time.xml
+++ /dev/null
@@ -1,23 +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
-  -->
-
-<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:textAppearance="@style/TextAppearance.AppCompat.Notification.Time"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:singleLine="true"
-    />
diff --git a/v7/appcompat/res/layout/tooltip.xml b/v7/appcompat/res/layout/tooltip.xml
new file mode 100644
index 0000000..1421cd4
--- /dev/null
+++ b/v7/appcompat/res/layout/tooltip.xml
@@ -0,0 +1,42 @@
+<?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"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/tooltip_margin"
+        android:paddingLeft="@dimen/tooltip_horizontal_padding"
+        android:paddingStart="@dimen/tooltip_horizontal_padding"
+        android:paddingRight="@dimen/tooltip_horizontal_padding"
+        android:paddingEnd="@dimen/tooltip_horizontal_padding"
+        android:paddingTop="@dimen/tooltip_vertical_padding"
+        android:paddingBottom="@dimen/tooltip_vertical_padding"
+        android:maxWidth="256dp"
+        android:background="?attr/tooltipFrameBackground"
+        android:textAppearance="@style/TextAppearance.AppCompat.Tooltip"
+        android:textColor="?attr/tooltipForegroundColor"
+        android:maxLines="3"
+        android:ellipsize="end"
+    />
+
+</LinearLayout>
diff --git a/v7/appcompat/res/values-az-rAZ/strings.xml b/v7/appcompat/res/values-az/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-az-rAZ/strings.xml
rename to v7/appcompat/res/values-az/strings.xml
diff --git a/v7/appcompat/res/values-be-rBY/strings.xml b/v7/appcompat/res/values-be/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-be-rBY/strings.xml
rename to v7/appcompat/res/values-be/strings.xml
diff --git a/v7/appcompat/res/values-bn-rBD/strings.xml b/v7/appcompat/res/values-bn/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-bn-rBD/strings.xml
rename to v7/appcompat/res/values-bn/strings.xml
diff --git a/v7/appcompat/res/values-bs-rBA/strings.xml b/v7/appcompat/res/values-bs-rBA/strings.xml
deleted file mode 100644
index 05f4a36..0000000
--- a/v7/appcompat/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2012 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_action_mode_done" msgid="4076576682505996667">"Završeno"</string>
-    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Vrati se na početnu stranicu"</string>
-    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigiraj prema gore"</string>
-    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Više opcija"</string>
-    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Skupi"</string>
-    <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
-    <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
-    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Traži"</string>
-    <string name="abc_search_hint" msgid="7723749260725869598">"Pretraži..."</string>
-    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Pretraži upit"</string>
-    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Obriši upit"</string>
-    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pošalji upit"</string>
-    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Pretraživanje glasom"</string>
-    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Odaberite aplikaciju"</string>
-    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Vidi sve"</string>
-    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Podijeli sa %s"</string>
-    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Podijeli sa"</string>
-    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
-    <string name="abc_capital_on" msgid="3405795526292276155">"UKLJUČI"</string>
-    <string name="abc_capital_off" msgid="121134116657445385">"ISKLJUČI"</string>
-    <string name="search_menu_title" msgid="146198913615257606">"Pretraži"</string>
-</resources>
diff --git a/v7/appcompat/res/values-bs/strings.xml b/v7/appcompat/res/values-bs/strings.xml
new file mode 100644
index 0000000..922e4a2
--- /dev/null
+++ b/v7/appcompat/res/values-bs/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Gotovo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Vrati se na početnu stranicu"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigiraj prema gore"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Više opcija"</string>
+    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Skupi"</string>
+    <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
+    <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Traži"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Pretraži..."</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Pretraži upit"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Obriši upit"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pošalji upit"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Pretraživanje glasom"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Odaberite aplikaciju"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Vidi sve"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Podijeli sa %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Podijeli sa"</string>
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"UKLJUČI"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"ISKLJUČI"</string>
+    <string name="search_menu_title" msgid="146198913615257606">"Pretraži"</string>
+</resources>
diff --git a/v7/appcompat/res/values-es/strings.xml b/v7/appcompat/res/values-es/strings.xml
index 5f954e1..418866d 100644
--- a/v7/appcompat/res/values-es/strings.xml
+++ b/v7/appcompat/res/values-es/strings.xml
@@ -34,7 +34,7 @@
     <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Compartir con %s"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Compartir con"</string>
     <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"+999"</string>
-    <string name="abc_capital_on" msgid="3405795526292276155">"SÍ"</string>
-    <string name="abc_capital_off" msgid="121134116657445385">"NO"</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"ACTIVADO"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"DESACTIVADO"</string>
     <string name="search_menu_title" msgid="146198913615257606">"Buscar"</string>
 </resources>
diff --git a/v7/appcompat/res/values-et-rEE/strings.xml b/v7/appcompat/res/values-et/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-et-rEE/strings.xml
rename to v7/appcompat/res/values-et/strings.xml
diff --git a/v7/appcompat/res/values-eu-rES/strings.xml b/v7/appcompat/res/values-eu/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-eu-rES/strings.xml
rename to v7/appcompat/res/values-eu/strings.xml
diff --git a/v7/appcompat/res/values-fa/strings.xml b/v7/appcompat/res/values-fa/strings.xml
index d5cac20..ffcf2cf 100644
--- a/v7/appcompat/res/values-fa/strings.xml
+++ b/v7/appcompat/res/values-fa/strings.xml
@@ -28,7 +28,7 @@
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"عبارت جستجو"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"پاک کردن عبارت جستجو"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ارسال عبارت جستجو"</string>
-    <string name="abc_searchview_description_voice" msgid="893419373245838918">"جستجوی شفاهی"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"جستجوی گفتاری"</string>
     <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"انتخاب برنامه"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"مشاهده همه"</string>
     <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"‏اشتراک‌گذاری با %s"</string>
diff --git a/v7/appcompat/res/values-gl-rES/strings.xml b/v7/appcompat/res/values-gl-rES/strings.xml
deleted file mode 100644
index 9ae2b8d..0000000
--- a/v7/appcompat/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2012 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_action_mode_done" msgid="4076576682505996667">"Feito"</string>
-    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ir á páxina de inicio"</string>
-    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Desprazarse cara arriba"</string>
-    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Máis opcións"</string>
-    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Contraer"</string>
-    <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
-    <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
-    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Buscar"</string>
-    <string name="abc_search_hint" msgid="7723749260725869598">"Buscar…"</string>
-    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de busca"</string>
-    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Borrar consulta"</string>
-    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
-    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Busca de voz"</string>
-    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Escoller unha aplicación"</string>
-    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Ver todas"</string>
-    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Compartir con %s"</string>
-    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Compartir con"</string>
-    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
-    <string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAR"</string>
-    <string name="abc_capital_off" msgid="121134116657445385">"DESACTIVAR"</string>
-    <string name="search_menu_title" msgid="146198913615257606">"Buscar"</string>
-</resources>
diff --git a/v7/appcompat/res/values-gl/strings.xml b/v7/appcompat/res/values-gl/strings.xml
new file mode 100644
index 0000000..6a86c4c
--- /dev/null
+++ b/v7/appcompat/res/values-gl/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Feito"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ir á páxina de inicio"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Desprazarse cara arriba"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Máis opcións"</string>
+    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Contraer"</string>
+    <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
+    <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Buscar"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Buscar…"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de busca"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Borrar consulta"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Busca por voz"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Escoller unha aplicación"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Ver todas"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Compartir con %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Compartir con"</string>
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAR"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"DESACTIVAR"</string>
+    <string name="search_menu_title" msgid="146198913615257606">"Buscar"</string>
+</resources>
diff --git a/v7/appcompat/res/values-gu-rIN/strings.xml b/v7/appcompat/res/values-gu/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-gu-rIN/strings.xml
rename to v7/appcompat/res/values-gu/strings.xml
diff --git a/v7/appcompat/res/values-hy-rAM/strings.xml b/v7/appcompat/res/values-hy/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-hy-rAM/strings.xml
rename to v7/appcompat/res/values-hy/strings.xml
diff --git a/v7/appcompat/res/values-is-rIS/strings.xml b/v7/appcompat/res/values-is/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-is-rIS/strings.xml
rename to v7/appcompat/res/values-is/strings.xml
diff --git a/v7/appcompat/res/values-ka-rGE/strings.xml b/v7/appcompat/res/values-ka/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ka-rGE/strings.xml
rename to v7/appcompat/res/values-ka/strings.xml
diff --git a/v7/appcompat/res/values-kk-rKZ/strings.xml b/v7/appcompat/res/values-kk/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-kk-rKZ/strings.xml
rename to v7/appcompat/res/values-kk/strings.xml
diff --git a/v7/appcompat/res/values-km-rKH/strings.xml b/v7/appcompat/res/values-km/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-km-rKH/strings.xml
rename to v7/appcompat/res/values-km/strings.xml
diff --git a/v7/appcompat/res/values-kn-rIN/strings.xml b/v7/appcompat/res/values-kn/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-kn-rIN/strings.xml
rename to v7/appcompat/res/values-kn/strings.xml
diff --git a/v7/appcompat/res/values-ky-rKG/strings.xml b/v7/appcompat/res/values-ky/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ky-rKG/strings.xml
rename to v7/appcompat/res/values-ky/strings.xml
diff --git a/v7/appcompat/res/values-lo-rLA/strings.xml b/v7/appcompat/res/values-lo/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-lo-rLA/strings.xml
rename to v7/appcompat/res/values-lo/strings.xml
diff --git a/v7/appcompat/res/values-mk-rMK/strings.xml b/v7/appcompat/res/values-mk/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-mk-rMK/strings.xml
rename to v7/appcompat/res/values-mk/strings.xml
diff --git a/v7/appcompat/res/values-ml-rIN/strings.xml b/v7/appcompat/res/values-ml/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ml-rIN/strings.xml
rename to v7/appcompat/res/values-ml/strings.xml
diff --git a/v7/appcompat/res/values-mn-rMN/strings.xml b/v7/appcompat/res/values-mn/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-mn-rMN/strings.xml
rename to v7/appcompat/res/values-mn/strings.xml
diff --git a/v7/appcompat/res/values-mr-rIN/strings.xml b/v7/appcompat/res/values-mr/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-mr-rIN/strings.xml
rename to v7/appcompat/res/values-mr/strings.xml
diff --git a/v7/appcompat/res/values-ms-rMY/strings.xml b/v7/appcompat/res/values-ms/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ms-rMY/strings.xml
rename to v7/appcompat/res/values-ms/strings.xml
diff --git a/v7/appcompat/res/values-my-rMM/strings.xml b/v7/appcompat/res/values-my/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-my-rMM/strings.xml
rename to v7/appcompat/res/values-my/strings.xml
diff --git a/v7/appcompat/res/values-ne-rNP/strings.xml b/v7/appcompat/res/values-ne/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ne-rNP/strings.xml
rename to v7/appcompat/res/values-ne/strings.xml
diff --git a/v7/appcompat/res/values-pa-rIN/strings.xml b/v7/appcompat/res/values-pa/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-pa-rIN/strings.xml
rename to v7/appcompat/res/values-pa/strings.xml
diff --git a/v7/appcompat/res/values-ro/strings.xml b/v7/appcompat/res/values-ro/strings.xml
index 46f7b33..e366d52 100644
--- a/v7/appcompat/res/values-ro/strings.xml
+++ b/v7/appcompat/res/values-ro/strings.xml
@@ -34,7 +34,7 @@
     <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Trimiteți la %s"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Trimiteți la"</string>
     <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"˃999"</string>
-    <string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAȚI"</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAT"</string>
     <string name="abc_capital_off" msgid="121134116657445385">"DEZACTIVAȚI"</string>
     <string name="search_menu_title" msgid="146198913615257606">"Căutați"</string>
 </resources>
diff --git a/v7/appcompat/res/values-si-rLK/strings.xml b/v7/appcompat/res/values-si/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-si-rLK/strings.xml
rename to v7/appcompat/res/values-si/strings.xml
diff --git a/v7/appcompat/res/values-sk/strings.xml b/v7/appcompat/res/values-sk/strings.xml
index 31a0d33..774740f 100644
--- a/v7/appcompat/res/values-sk/strings.xml
+++ b/v7/appcompat/res/values-sk/strings.xml
@@ -34,7 +34,7 @@
     <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Zdieľať pomocou %s"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Zdieľať pomocou"</string>
     <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
-    <string name="abc_capital_on" msgid="3405795526292276155">"ZAP."</string>
-    <string name="abc_capital_off" msgid="121134116657445385">"VYP."</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"ZAPNUTÉ"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"VYPNUTÉ"</string>
     <string name="search_menu_title" msgid="146198913615257606">"Vyhľadávanie"</string>
 </resources>
diff --git a/v7/appcompat/res/values-sq-rAL/strings.xml b/v7/appcompat/res/values-sq/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-sq-rAL/strings.xml
rename to v7/appcompat/res/values-sq/strings.xml
diff --git a/v7/appcompat/res/values-ta-rIN/strings.xml b/v7/appcompat/res/values-ta/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ta-rIN/strings.xml
rename to v7/appcompat/res/values-ta/strings.xml
diff --git a/v7/appcompat/res/values-te-rIN/strings.xml b/v7/appcompat/res/values-te/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-te-rIN/strings.xml
rename to v7/appcompat/res/values-te/strings.xml
diff --git a/v7/appcompat/res/values-ur-rPK/strings.xml b/v7/appcompat/res/values-ur/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ur-rPK/strings.xml
rename to v7/appcompat/res/values-ur/strings.xml
diff --git a/v7/appcompat/res/values-uz-rUZ/strings.xml b/v7/appcompat/res/values-uz/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-uz-rUZ/strings.xml
rename to v7/appcompat/res/values-uz/strings.xml
diff --git a/v7/appcompat/res/values-v14/styles.xml b/v7/appcompat/res/values-v14/styles.xml
deleted file mode 100644
index 39419d2..0000000
--- a/v7/appcompat/res/values-v14/styles.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
-  ~ 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
-  -->
-
-<resources>
-
-    <!-- Use platform styles -->
-    <style name="TextAppearance.StatusBar.EventContent"
-        parent="@android:style/TextAppearance.StatusBar.EventContent"/>
-
-    <style name="TextAppearance.StatusBar.EventContent.Title"
-        parent="@android:style/TextAppearance.StatusBar.EventContent.Title"/>
-
-    <!-- Use own styles for which platform styles are not public -->
-    <style name="TextAppearance.StatusBar.EventContent.Line2">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
-    </style>
-    <style name="TextAppearance.StatusBar.EventContent.Info"/>
-    <style name="TextAppearance.StatusBar.EventContent.Time"/>
-
-    <style name="TextAppearance.AppCompat.Notification.Title"
-        parent="@android:style/TextAppearance.StatusBar.EventContent.Title"/>
-
-    <style name="TextAppearance.AppCompat.Notification"
-        parent="@android:style/TextAppearance.StatusBar.EventContent"/>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v16/styles.xml b/v7/appcompat/res/values-v16/styles.xml
new file mode 100644
index 0000000..1b865d4
--- /dev/null
+++ b/v7/appcompat/res/values-v16/styles.xml
@@ -0,0 +1,25 @@
+<?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="TextAppearance.AppCompat.Tooltip">
+        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v21/dimens.xml b/v7/appcompat/res/values-v21/dimens.xml
deleted file mode 100644
index de665a6..0000000
--- a/v7/appcompat/res/values-v21/dimens.xml
+++ /dev/null
@@ -1,24 +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.
--->
-
-<resources>
-    <!-- the margin at the beginning of the notification content -->
-    <dimen name="notification_content_margin_start">0dp</dimen>
-    <!-- image margin on the large icon in the narrow media template -->
-    <dimen name="notification_media_narrow_margin">12dp</dimen>
-    <!-- the top padding of the notification content -->
-    <dimen name="notification_main_column_padding_top">0dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v21/styles.xml b/v7/appcompat/res/values-v21/styles.xml
index 1c435ae..48d0366 100644
--- a/v7/appcompat/res/values-v21/styles.xml
+++ b/v7/appcompat/res/values-v21/styles.xml
@@ -18,16 +18,16 @@
 
     <!-- Use platform styles -->
     <style name="TextAppearance.AppCompat.Notification.Info"
-        parent="@android:style/TextAppearance.Material.Notification.Info"/>
+           parent="@android:style/TextAppearance.Material.Notification.Info"/>
 
     <style name="TextAppearance.AppCompat.Notification.Time"
-        parent="@android:style/TextAppearance.Material.Notification.Time"/>
+           parent="@android:style/TextAppearance.Material.Notification.Time"/>
 
     <style name="TextAppearance.AppCompat.Notification.Title"
-        parent="@android:style/TextAppearance.Material.Notification.Title"/>
+           parent="@android:style/TextAppearance.Material.Notification.Title"/>
 
     <style name="TextAppearance.AppCompat.Notification"
-        parent="@android:style/TextAppearance.Material.Notification"/>
+           parent="@android:style/TextAppearance.Material.Notification"/>
 
     <style name="TextAppearance.AppCompat.Notification.Media" >
         <item name="android:textColor">@color/secondary_text_default_material_dark</item>
@@ -44,15 +44,4 @@
     <style name="TextAppearance.AppCompat.Notification.Time.Media">
         <item name="android:textColor">@color/secondary_text_default_material_dark</item>
     </style>
-
-    <style name="Widget.AppCompat.NotificationActionText" parent="">
-        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
-        <item name="android:textColor">@color/secondary_text_default_material_light</item>
-        <item name="android:textSize">@dimen/notification_action_text_size</item>
-    </style>
-
-    <style name="Widget.AppCompat.NotificationActionContainer" parent="">
-        <item name="android:background">@drawable/notification_action_background</item>
-    </style>
-
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v24/styles.xml b/v7/appcompat/res/values-v24/styles.xml
index 52bb3d4..4cd8461 100644
--- a/v7/appcompat/res/values-v24/styles.xml
+++ b/v7/appcompat/res/values-v24/styles.xml
@@ -15,7 +15,6 @@
   -->
 
 <resources>
-
     <!-- Use platform styles, Media is dark again -->
     <style name="TextAppearance.AppCompat.Notification.Media" />
 
@@ -24,5 +23,4 @@
     <style name="TextAppearance.AppCompat.Notification.Time.Media" />
 
     <style name="TextAppearance.AppCompat.Notification.Title.Media" />
-
-</resources>
\ No newline at end of file
+</resources>
diff --git a/v7/appcompat/res/values-v26/styles_base.xml b/v7/appcompat/res/values-v26/styles_base.xml
new file mode 100644
index 0000000..c56cc3e
--- /dev/null
+++ b/v7/appcompat/res/values-v26/styles_base.xml
@@ -0,0 +1,27 @@
+<?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="Base.Widget.AppCompat.Toolbar" parent="Base.V26.Widget.AppCompat.Toolbar" />
+
+    <style name="Base.V26.Widget.AppCompat.Toolbar" parent="Base.V7.Widget.AppCompat.Toolbar">
+        <item name="android:touchscreenBlocksFocus">true</item>
+        <item name="android:keyboardNavigationCluster">true</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v26/themes_base.xml b/v7/appcompat/res/values-v26/themes_base.xml
new file mode 100644
index 0000000..c4b75a8
--- /dev/null
+++ b/v7/appcompat/res/values-v26/themes_base.xml
@@ -0,0 +1,33 @@
+<?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="Base.Theme.AppCompat" parent="Base.V26.Theme.AppCompat" />
+    <style name="Base.Theme.AppCompat.Light" parent="Base.V26.Theme.AppCompat.Light" />
+
+    <style name="Base.V26.Theme.AppCompat" parent="Base.V23.Theme.AppCompat">
+        <!-- We can use the platform styles on API 26+ -->
+        <item name="colorError">?android:attr/colorError</item>
+    </style>
+
+    <style name="Base.V26.Theme.AppCompat.Light" parent="Base.V23.Theme.AppCompat.Light">
+        <!-- We can use the platform styles on API 26+ -->
+        <item name="colorError">?android:attr/colorError</item>
+    </style>
+
+</resources>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index d5ec5a1..d48d826 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -393,6 +393,20 @@
 
         <!-- Default menu-style ListView style. -->
         <attr name="listMenuViewStyle" format="reference" />
+
+        <!-- ===================== -->
+        <!-- Tooltip styles -->
+        <!-- ===================== -->
+        <eat-comment />
+
+        <!-- Background to use for tooltips -->
+        <attr name="tooltipFrameBackground" format="reference" />
+        <!-- Foreground color to use for tooltips -->
+        <attr name="tooltipForegroundColor" format="reference|color" />
+
+        <!-- Color used for error states and things that need to be drawn to
+             the user's attention. -->
+        <attr name="colorError" format="reference|color" />
     </declare-styleable>
 
 
@@ -635,10 +649,32 @@
              with alphabetic keys. -->
         <attr name="android:alphabeticShortcut" />
 
+        <!-- The alphabetic modifier key. This is the modifier when using a keyboard
+            with alphabetic keys. The values should be kept in sync with KeyEvent -->
+        <attr name="alphabeticModifiers">
+            <flag name="META" value="0x10000" />
+            <flag name="CTRL" value="0x1000" />
+            <flag name="ALT" value="0x02" />
+            <flag name="SHIFT" value="0x1" />
+            <flag name="SYM" value="0x4" />
+            <flag name="FUNCTION" value="0x8" />
+        </attr>
+
         <!-- The numeric shortcut key.  This is the shortcut when using a numeric (e.g., 12-key)
              keyboard. -->
         <attr name="android:numericShortcut" />
 
+        <!-- The numeric modifier key. This is the modifier when using a numeric (e.g., 12-key)
+            keyboard. The values should be kept in sync with KeyEvent -->
+        <attr name="numericModifiers">
+            <flag name="META" value="0x10000" />
+            <flag name="CTRL" value="0x1000" />
+            <flag name="ALT" value="0x02" />
+            <flag name="SHIFT" value="0x1" />
+            <flag name="SYM" value="0x4" />
+            <flag name="FUNCTION" value="0x8" />
+        </attr>
+
         <!-- Whether the item is capable of displaying a check mark. -->
         <attr name="android:checkable" />
 
@@ -695,6 +731,36 @@
              for more info. -->
         <attr name="actionProviderClass" format="string" />
 
+        <!-- The content description associated with the item. -->
+        <attr name="contentDescription" format="string"/>
+
+        <!-- The tooltip text associated with the item. -->
+        <attr name="tooltipText" format="string"/>
+
+        <!-- Tint to apply to the icon. -->
+        <attr name="iconTint" format="color" />
+
+        <!-- Blending mode used to apply the icon tint. -->
+        <attr name="iconTintMode">
+            <!-- The tint is drawn on top of the icon.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the icon. The icon’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the icon, but with the icon’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the icon with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and icon color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
+
     </declare-styleable>
 
     <declare-styleable name="Spinner">
@@ -765,6 +831,29 @@
         <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
         <attr name="textAllCaps" format="reference|boolean" />
         <attr name="android:textAppearance" />
+        <!-- Specify the type of auto-size. Note that this feature is not supported by EditText,
+        works only for TextView. -->
+        <attr name="autoSizeTextType" format="enum">
+            <!-- No auto-sizing (default). -->
+            <enum name="none" value="0" />
+            <!-- Uniform horizontal and vertical text size scaling to fit within the
+            container. -->
+            <enum name="uniform" value="1" />
+        </attr>
+        <!-- Specify the auto-size step size if <code>autoSizeTextType</code> is set to
+        <code>uniform</code>. The default is 1px. Overwrites
+        <code>autoSizePresetSizes</code> if set. -->
+        <attr name="autoSizeStepGranularity" format="dimension" />
+        <!-- Resource array of dimensions to be used in conjunction with
+        <code>autoSizeTextType</code> set to <code>uniform</code>. Overrides
+        <code>autoSizeStepGranularity</code> if set. -->
+        <attr name="autoSizePresetSizes" format="reference"/>
+        <!-- The minimum text size constraint to be used when auto-sizing text. -->
+        <attr name="autoSizeMinTextSize" format="dimension" />
+        <!-- The maximum text size constraint to be used when auto-sizing text. -->
+        <attr name="autoSizeMaxTextSize" format="dimension" />
+        <!-- The attribute for the font family. -->
+        <attr name="fontFamily" format="string" />
     </declare-styleable>
 
     <declare-styleable name="LinearLayoutCompat">
@@ -1022,8 +1111,11 @@
         <attr name="android:textSize" />
         <attr name="android:textColor" />
         <attr name="android:textColorHint"/>
+        <attr name="android:textColorLink"/>
         <attr name="android:textStyle" />
         <attr name="android:typeface" />
+        <attr name="android:fontFamily" />
+        <attr name="fontFamily" />
         <attr name="textAllCaps" />
         <attr name="android:shadowColor"/>
         <attr name="android:shadowDy"/>
diff --git a/v7/appcompat/res/values/colors.xml b/v7/appcompat/res/values/colors.xml
index 92c91fd..6191fea 100644
--- a/v7/appcompat/res/values/colors.xml
+++ b/v7/appcompat/res/values/colors.xml
@@ -20,12 +20,7 @@
 
     <color name="abc_input_method_navigation_guard">@android:color/black</color>
 
-    <drawable name="notification_template_icon_bg">#3333B5E5</drawable>
-    <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
-    <color name="notification_action_color_filter">#ffffffff</color>
-    <color name="notification_icon_bg_color">#ff9e9e9e</color>
-
-    <!-- The color of the material notification background for media notifications when no custom
-     color is specified -->
-    <color name="notification_material_background_media_default_color">#ff424242</color>
+    <!-- Tooltip specific colors -->
+    <color name="tooltip_background_dark">#e6616161</color>
+    <color name="tooltip_background_light">#e6FFFFFF</color>
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values/colors_material.xml b/v7/appcompat/res/values/colors_material.xml
index a90bf7a..cf21172 100644
--- a/v7/appcompat/res/values/colors_material.xml
+++ b/v7/appcompat/res/values/colors_material.xml
@@ -99,6 +99,8 @@
     <color name="primary_text_disabled_material_dark">#4Dffffff</color>
     <color name="secondary_text_disabled_material_dark">#36ffffff</color>
 
+    <color name="error_color_material">#F4511E</color>
+
     <!-- Primary & accent colors -->
     <eat-comment />
 
@@ -116,5 +118,4 @@
     <color name="material_blue_grey_800">#ff37474f</color>
     <color name="material_blue_grey_900">#ff263238</color>
     <color name="material_blue_grey_950">#ff21272b</color>
-
 </resources>
diff --git a/v7/appcompat/res/values/config.xml b/v7/appcompat/res/values/config.xml
index 6d986ea..0f99f6d 100644
--- a/v7/appcompat/res/values/config.xml
+++ b/v7/appcompat/res/values/config.xml
@@ -33,11 +33,8 @@
 
     <bool name="abc_config_closeDialogWhenTouchOutside">true</bool>
 
-    <!-- Maximum numerical value that will be shown in a status bar
-         notification icon or in the notification itself. Will be replaced
-         with @string/status_bar_notification_info_overflow when shown in the
-         UI. -->
-    <integer name="status_bar_notification_info_maxnum">999</integer>
-
     <integer name="cancel_button_image_alpha">127</integer>
+
+    <!-- The duration (in milliseconds) of the tooltip show/hide animations. -->
+    <integer name="config_tooltipAnimTime">150</integer>
 </resources>
diff --git a/v7/appcompat/res/values/dimens.xml b/v7/appcompat/res/values/dimens.xml
index e055eed..a02b7c0 100644
--- a/v7/appcompat/res/values/dimens.xml
+++ b/v7/appcompat/res/values/dimens.xml
@@ -95,49 +95,22 @@
     <!-- Minimum "smallest width" of the display for cascading menus to be enabled. -->
     <dimen name="abc_cascading_menus_min_smallest_width">720dp</dimen>
 
-    <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_width">64dp</dimen>
-
-    <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_height">64dp</dimen>
-
-    <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info,
-         Time) -->
-    <dimen name="notification_subtext_size">13sp</dimen>
-
-    <!-- Size of notification action text -->
-    <dimen name="notification_action_text_size">13sp</dimen>
-
-    <!-- Top padding for notifications in the standard layout. -->
-    <dimen name="notification_top_pad">10dp</dimen>
-
-    <!-- Top padding for notification when text is large -->
-    <dimen name="notification_top_pad_large_text">5dp</dimen>
-
-    <!-- The size of the action icons -->
-    <dimen name="notification_action_icon_size">32dp</dimen>
-
-    <!-- the size of the small icon on the right of the largeIcon -->
-    <dimen name="notification_right_icon_size">16dp</dimen>
-
-    <!-- the padding of the small icon to the circle -->
-    <dimen name="notification_small_icon_background_padding">3dp</dimen>
-
-    <!-- the side margin of the big notification circle -->
-    <dimen name="notification_big_circle_margin">12dp</dimen>
-
-    <!-- small icon size when placed as large icon -->
-    <dimen name="notification_small_icon_size_as_large">24dp</dimen>
-
-    <!-- the margin at the beginning of the notification content -->
-    <dimen name="notification_content_margin_start">8dp</dimen>
-
-    <!-- image margin on the large icon in the narrow media template -->
-    <dimen name="notification_media_narrow_margin">@dimen/notification_content_margin_start</dimen>
-
-    <!-- the top padding of the notification content -->
-    <dimen name="notification_main_column_padding_top">10dp</dimen>
-
-    <!-- the paddingtop on the right side of the notification (for time etc.) -->
-    <dimen name="notification_right_side_padding_top">2dp</dimen>
+    <!-- Tooltip dimensions. -->
+    <!-- Vertical offset from the edge of the anchor view for a touch-triggered tooltip. -->
+    <dimen name="tooltip_y_offset_touch">16dp</dimen>
+    <!-- Vertical offset from the edge of the anchor view for a non-touch-triggered tooltip. -->
+    <dimen name="tooltip_y_offset_non_touch">0dp</dimen>
+    <!-- The tooltip does not get closer than this to the window edge -->
+    <dimen name="tooltip_margin">8dp</dimen>
+    <!-- Left/right padding of the tooltip text. -->
+    <dimen name="tooltip_horizontal_padding">16dp</dimen>
+    <!-- Top/bottom padding of the tooltip text. -->
+    <dimen name="tooltip_vertical_padding">6.5dp</dimen>
+    <!-- Border corner radius of the tooltip window. -->
+    <dimen name="tooltip_corner_radius">2dp</dimen>
+    <!-- View with the height equal or above this threshold will have a tooltip anchored
+    to the mouse/touch position -->
+    <dimen name="tooltip_precise_anchor_threshold">96dp</dimen>
+    <!-- Extra tooltip offset used when anchoring to the mouse/touch position -->
+    <dimen name="tooltip_precise_anchor_extra_offset">8dp</dimen>
 </resources>
diff --git a/v7/appcompat/res/values/strings.xml b/v7/appcompat/res/values/strings.xml
index 3f1d959..55bf5ad 100644
--- a/v7/appcompat/res/values/strings.xml
+++ b/v7/appcompat/res/values/strings.xml
@@ -62,13 +62,6 @@
     <!-- Description of the choose target button in a ShareActionProvider (share UI). [CHAR LIMIT=NONE] -->
     <string name="abc_shareactionprovider_share_with">Share with</string>
 
-    <!-- Text to use when the number in a notification info is too large
-         (greater than status_bar_notification_info_maxnum, defined in
-         values/config.xml) and must be truncated. May need to be localized
-         for most appropriate textual indicator of "more than X".
-         [CHAR LIMIT=4] -->
-    <string name="status_bar_notification_info_overflow">999+</string>
-
     <!-- Default text for a button that can be toggled on and off. -->
     <string name="abc_capital_on">ON</string>
     <!-- Default text for a button that can be toggled on and off. -->
diff --git a/v7/appcompat/res/values/styles.xml b/v7/appcompat/res/values/styles.xml
index 7595d74..ce74a89 100644
--- a/v7/appcompat/res/values/styles.xml
+++ b/v7/appcompat/res/values/styles.xml
@@ -241,6 +241,7 @@
     <eat-comment />
     <style name="Animation.AppCompat.Dialog" parent="Base.Animation.AppCompat.Dialog" />
     <style name="Animation.AppCompat.DropDownUp" parent="Base.Animation.AppCompat.DropDownUp" />
+    <style name="Animation.AppCompat.Tooltip" parent="Base.Animation.AppCompat.Tooltip" />
 
 
     <!-- Text styles -->
@@ -302,6 +303,8 @@
 
     <style name="TextAppearance.AppCompat.Widget.Button.Borderless.Colored" parent="Base.TextAppearance.AppCompat.Widget.Button.Borderless.Colored" />
 
+    <style name="TextAppearance.AppCompat.Tooltip" parent="Base.TextAppearance.AppCompat.Tooltip"/>
+
     <!--
          The following themes are deprecated.
     -->
@@ -326,22 +329,10 @@
     <style name="TextAppearance.AppCompat.Light.Widget.PopupMenu.Large" parent="TextAppearance.AppCompat.Widget.PopupMenu.Large" />
     <style name="TextAppearance.AppCompat.Light.Widget.PopupMenu.Small" parent="TextAppearance.AppCompat.Widget.PopupMenu.Small" />
 
-    <!-- These styles didn't exist on v7. Since we only use the media template in later versions
-         (ICS+), just define it here and use the correct references in values/v14 -->
-    <style name="TextAppearance.StatusBar.EventContent" parent=""/>
-    <style name="TextAppearance.StatusBar.EventContent.Title" parent=""/>
-    <style name="TextAppearance.StatusBar.EventContent.Line2" parent=""/>
-    <style name="TextAppearance.StatusBar.EventContent.Info" parent=""/>
-    <style name="TextAppearance.StatusBar.EventContent.Time" parent=""/>
-
-    <style name="TextAppearance.AppCompat.Notification">
-        <item name="android:textSize">14sp</item>
-        <item name="android:textColor">?android:attr/textColorSecondary</item>
-    </style>
-    <style name="TextAppearance.AppCompat.Notification.Title">
-        <item name="android:textSize">16sp</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
-    </style>
+    <!-- These styles have moved to support-compat and should be removed when
+         android.support.v7.app.NotificationCompat is removed -->
+    <style name="TextAppearance.AppCompat.Notification" parent="@android:style/TextAppearance.StatusBar.EventContent" />
+    <style name="TextAppearance.AppCompat.Notification.Title" parent="@android:style/TextAppearance.StatusBar.EventContent.Title" />
     <style name="TextAppearance.AppCompat.Notification.Info">
         <item name="android:textSize">12sp</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
@@ -352,16 +343,11 @@
     </style>
     <style name="TextAppearance.AppCompat.Notification.Line2" parent="TextAppearance.AppCompat.Notification.Info" />
 
+    <!-- These styles have moved to media-compat and should be removed when
+         android.support.v7.app.NotificationCompat is removed -->
     <style name="TextAppearance.AppCompat.Notification.Title.Media" />
-
     <style name="TextAppearance.AppCompat.Notification.Media" />
-
-    <style name="TextAppearance.AppCompat.Notification.Info.Media"/>
-
-    <style name="TextAppearance.AppCompat.Notification.Time.Media"/>
-
-    <style name="TextAppearance.AppCompat.Notification.Line2.Media" parent="TextAppearance.AppCompat.Notification.Info.Media"/>
-
-    <style name="Widget.AppCompat.NotificationActionText" parent=""/>
-    <style name="Widget.AppCompat.NotificationActionContainer" parent=""/>
+    <style name="TextAppearance.AppCompat.Notification.Info.Media" />
+    <style name="TextAppearance.AppCompat.Notification.Time.Media" />
+    <style name="TextAppearance.AppCompat.Notification.Line2.Media" parent="TextAppearance.AppCompat.Notification.Info.Media" />
 </resources>
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index a760b78..2b2db12 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -299,7 +299,9 @@
     <style name="Base.Widget.AppCompat.PopupWindow" parent="android:Widget.PopupWindow">
     </style>
 
-    <style name="Base.Widget.AppCompat.Toolbar" parent="android:Widget">
+    <style name="Base.Widget.AppCompat.Toolbar" parent="Base.V7.Widget.AppCompat.Toolbar" />
+
+    <style name="Base.V7.Widget.AppCompat.Toolbar" parent="android:Widget">
         <item name="titleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Title</item>
         <item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
         <item name="android:minHeight">?attr/actionBarSize</item>
@@ -522,4 +524,9 @@
 
     <style name="Base.AlertDialog.AppCompat.Light" parent="Base.AlertDialog.AppCompat" />
 
+    <style name="Base.Animation.AppCompat.Tooltip" parent="android:Animation">
+        <item name="android:windowEnterAnimation">@anim/tooltip_enter</item>
+        <item name="android:windowExitAnimation">@anim/tooltip_exit</item>
+    </style>
+
 </resources>
diff --git a/v7/appcompat/res/values/styles_base_text.xml b/v7/appcompat/res/values/styles_base_text.xml
index 6a43144..11de1e6 100644
--- a/v7/appcompat/res/values/styles_base_text.xml
+++ b/v7/appcompat/res/values/styles_base_text.xml
@@ -102,6 +102,10 @@
         <item name="android:textColor">?android:textColorPrimaryInverse</item>
     </style>
 
+    <style name="Base.TextAppearance.AppCompat.Tooltip">
+        <item name="android:textSize">14sp</item>
+    </style>
+
     <!-- Deprecated text styles -->
 
     <style name="Base.TextAppearance.AppCompat.Inverse">
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 434c619..a5be8ad 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -277,6 +277,12 @@
         <item name="windowFixedWidthMinor">@null</item>
         <item name="windowFixedHeightMajor">@null</item>
         <item name="windowFixedHeightMinor">@null</item>
+
+        <!-- Tooltip attributes -->
+        <item name="tooltipFrameBackground">@drawable/tooltip_frame_light</item>
+        <item name="tooltipForegroundColor">@color/foreground_material_light</item>
+
+        <item name="colorError">@color/error_color_material</item>
     </style>
 
     <!-- Base platform-dependent theme providing an action bar in a light-themed activity. -->
@@ -439,6 +445,12 @@
         <item name="windowFixedWidthMinor">@null</item>
         <item name="windowFixedHeightMajor">@null</item>
         <item name="windowFixedHeightMinor">@null</item>
+
+        <!-- Tooltip attributes -->
+        <item name="tooltipFrameBackground">@drawable/tooltip_frame_dark</item>
+        <item name="tooltipForegroundColor">@color/foreground_material_dark</item>
+
+        <item name="colorError">@color/error_color_material</item>
     </style>
 
     <style name="Base.Theme.AppCompat" parent="Base.V7.Theme.AppCompat">
diff --git a/v7/appcompat/src/.readme b/v7/appcompat/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/appcompat/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBar.java b/v7/appcompat/src/android/support/v7/app/ActionBar.java
index 320cf7b..c619596 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBar.java
@@ -1069,6 +1069,12 @@
 
     /** @hide */
     @RestrictTo(LIBRARY_GROUP)
+    public boolean closeOptionsMenu() {
+        return false;
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
     public boolean invalidateOptionsMenu() {
         return false;
     }
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java b/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
deleted file mode 100644
index 1834681..0000000
--- a/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2012 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.v7.app;
-
-/**
- * @deprecated Use {@link android.support.v7.app.AppCompatActivity} instead.
- */
-@Deprecated
-public class ActionBarActivity extends AppCompatActivity {
-}
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
index cbfb397..509c886 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
@@ -15,7 +15,6 @@
  */
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.Context;
@@ -523,7 +522,6 @@
      * Delegate if SDK version is between Honeycomb and ICS
      */
     @RequiresApi(11)
-    @TargetApi(11)
     private static class HoneycombDelegate implements Delegate {
 
         final Activity mActivity;
@@ -572,7 +570,6 @@
      * Delegate if SDK version is between ICS and JBMR2
      */
     @RequiresApi(14)
-    @TargetApi(14)
     private static class IcsDelegate extends HoneycombDelegate {
 
         IcsDelegate(Activity activity) {
@@ -596,7 +593,6 @@
      * Delegate if SDK version is JB MR2 or newer
      */
     @RequiresApi(18)
-    @TargetApi(18)
     private static class JellybeanMr2Delegate implements Delegate {
 
         final Activity mActivity;
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
index 1463ecb..92cf866 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
@@ -18,7 +18,6 @@
 package android.support.v7.app;
 
 import android.R;
-import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.res.TypedArray;
@@ -42,7 +41,6 @@
  * Moved from Support-v4
  */
 @RequiresApi(11)
-@TargetApi(11)
 class ActionBarDrawerToggleHoneycomb {
     private static final String TAG = "ActionBarDrawerToggleHoneycomb";
 
diff --git a/v7/appcompat/src/android/support/v7/app/AlertController.java b/v7/appcompat/src/android/support/v7/app/AlertController.java
index d0f8fd6..986baf3 100644
--- a/v7/appcompat/src/android/support/v7/app/AlertController.java
+++ b/v7/appcompat/src/android/support/v7/app/AlertController.java
@@ -738,11 +738,11 @@
     static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
         if (upIndicator != null) {
             upIndicator.setVisibility(
-                    ViewCompat.canScrollVertically(v, -1) ? View.VISIBLE : View.INVISIBLE);
+                    v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE);
         }
         if (downIndicator != null) {
             downIndicator.setVisibility(
-                    ViewCompat.canScrollVertically(v, 1) ? View.VISIBLE : View.INVISIBLE);
+                    v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE);
         }
     }
 
diff --git a/v7/appcompat/src/android/support/v7/app/AlertDialog.java b/v7/appcompat/src/android/support/v7/app/AlertDialog.java
index c73e619..4b87dcc 100644
--- a/v7/appcompat/src/android/support/v7/app/AlertDialog.java
+++ b/v7/appcompat/src/android/support/v7/app/AlertDialog.java
@@ -50,7 +50,7 @@
  * and add your view to it:
  *
  * <pre>
- * FrameLayout fl = (FrameLayout) findViewById(android.R.id.custom);
+ * FrameLayout fl = findViewById(android.R.id.custom);
  * fl.addView(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
  * </pre>
  *
@@ -106,7 +106,8 @@
     }
 
     static int resolveDialogTheme(@NonNull Context context, @StyleRes int resid) {
-        if (resid >= 0x01000000) {   // start of real resource IDs.
+        // Check to see if this resourceId has a valid package ID.
+        if (((resid >>> 24) & 0x000000ff) >= 0x00000001) {   // start of real resource IDs.
             return resid;
         } else {
             TypedValue outValue = new TypedValue();
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 534cfec..c8d7b0a 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -31,7 +31,6 @@
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.NavUtils;
 import android.support.v4.app.TaskStackBuilder;
-import android.support.v4.view.KeyEventCompat;
 import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
 import android.support.v7.widget.VectorEnabledTintResources;
@@ -41,6 +40,7 @@
 import android.view.MenuInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 
 /**
  * Base class for activities that use the
@@ -63,7 +63,6 @@
 
     private AppCompatDelegate mDelegate;
     private int mThemeId = 0;
-    private boolean mEatKeyUpEvent;
     private Resources mResources;
 
     @Override
@@ -185,8 +184,9 @@
         getDelegate().onStop();
     }
 
+    @SuppressWarnings("TypeParameterUnusedInFormals")
     @Override
-    public View findViewById(@IdRes int id) {
+    public <T extends View> T findViewById(@IdRes int id) {
         return getDelegate().findViewById(id);
     }
 
@@ -523,20 +523,13 @@
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (KeyEventCompat.isCtrlPressed(event) &&
-                event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') {
-            // Capture the Control-< and send focus to the ActionBar
-            final int action = event.getAction();
-            if (action == KeyEvent.ACTION_DOWN) {
-                final ActionBar actionBar = getSupportActionBar();
-                if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
-                    mEatKeyUpEvent = true;
-                    return true;
-                }
-            } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
-                mEatKeyUpEvent = false;
-                return true;
-            }
+        // Let support action bars open menus in response to the menu key prioritized over
+        // the window handling it
+        final int keyCode = event.getKeyCode();
+        final ActionBar actionBar = getSupportActionBar();
+        if (keyCode == KeyEvent.KEYCODE_MENU
+                && actionBar != null && actionBar.onMenuKeyEvent(event)) {
+            return true;
         }
         return super.dispatchKeyEvent(event);
     }
@@ -548,4 +541,51 @@
         }
         return mResources == null ? super.getResources() : mResources;
     }
+
+    /**
+     * KeyEvents with non-default modifiers are not dispatched to menu's performShortcut in API 25
+     * or lower. Here, we check if the keypress corresponds to a menuitem's shortcut combination
+     * and perform the corresponding action.
+     */
+    private boolean performMenuItemShortcut(int keycode, KeyEvent event) {
+        if (!(Build.VERSION.SDK_INT >= 26) && !event.isCtrlPressed()
+                && !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
+                && event.getRepeatCount() == 0
+                && !KeyEvent.isModifierKey(event.getKeyCode())) {
+            final Window currentWindow = getWindow();
+            if (currentWindow != null && currentWindow.getDecorView() != null) {
+                final View decorView = currentWindow.getDecorView();
+                if (decorView.dispatchKeyShortcutEvent(event)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (performMenuItemShortcut(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public void openOptionsMenu() {
+        ActionBar actionBar = getSupportActionBar();
+        if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
+                && (actionBar == null || !actionBar.openOptionsMenu())) {
+            super.openOptionsMenu();
+        }
+    }
+
+    @Override
+    public void closeOptionsMenu() {
+        ActionBar actionBar = getSupportActionBar();
+        if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
+                && (actionBar == null || !actionBar.closeOptionsMenu())) {
+            super.closeOptionsMenu();
+        }
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
index 5a33577..5d0bf6b 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -31,9 +31,7 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.FragmentActivity;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.WindowCompat;
-import android.support.v7.appcompat.R;
 import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
 import android.util.AttributeSet;
@@ -196,14 +194,13 @@
 
     private static AppCompatDelegate create(Context context, Window window,
             AppCompatCallback callback) {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             return new AppCompatDelegateImplN(context, window, callback);
-        } else if (sdk >= 23) {
+        } else if (Build.VERSION.SDK_INT >= 23) {
             return new AppCompatDelegateImplV23(context, window, callback);
-        } else if (sdk >= 14) {
+        } else if (Build.VERSION.SDK_INT >= 14) {
             return new AppCompatDelegateImplV14(context, window, callback);
-        } else if (sdk >= 11) {
+        } else if (Build.VERSION.SDK_INT >= 11) {
             return new AppCompatDelegateImplV11(context, window, callback);
         } else {
             return new AppCompatDelegateImplV9(context, window, callback);
@@ -291,8 +288,9 @@
      *
      * @return The view if found or null otherwise.
      */
+    @SuppressWarnings("TypeParameterUnusedInFormals")
     @Nullable
-    public abstract View findViewById(@IdRes int id);
+    public abstract <T extends View> T findViewById(@IdRes int id);
 
     /**
      * Should be called instead of {@link Activity#setContentView(android.view.View)}}
@@ -391,7 +389,7 @@
 
     /**
      * This should be called from a
-     * {@link android.support.v4.view.LayoutInflaterFactory LayoutInflaterFactory} in order
+     * {@link android.view.LayoutInflater.Factory2 LayoutInflater.Factory2} in order
      * to return tint-aware widgets.
      * <p>
      * This is only needed if you are using your own
@@ -426,7 +424,8 @@
      * Allow AppCompat to apply the {@code night} and {@code notnight} resource qualifiers.
      *
      * <p>Doing this enables the
-     * {@link R.style#Theme_AppCompat_DayNight Theme.AppCompat.DayNight}
+     * {@link
+     * android.support.v7.appcompat.R.style#Theme_AppCompat_DayNight Theme.AppCompat.DayNight}
      * family of themes to work, using the computed twilight to automatically select a dark or
      * light theme.</p>
      *
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
index 37f0318..5bb5ca6 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
@@ -16,11 +16,9 @@
 
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
@@ -30,7 +28,6 @@
 import android.support.v7.view.SupportMenuInflater;
 import android.support.v7.view.WindowCallbackWrapper;
 import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.widget.AppCompatDrawableManager;
 import android.support.v7.widget.TintTypedArray;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -38,8 +35,7 @@
 import android.view.View;
 import android.view.Window;
 
-@RequiresApi(9)
-@TargetApi(9)
+@RequiresApi(14)
 abstract class AppCompatDelegateImplBase extends AppCompatDelegate {
 
     static final boolean DEBUG = false;
@@ -111,6 +107,7 @@
 
     private boolean mIsStarted;
     private boolean mIsDestroyed;
+    private boolean mEatKeyUpEvent;
 
     AppCompatDelegateImplBase(Context context, Window window, AppCompatCallback callback) {
         mContext = context;
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
index 9f162dd..e282324 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.view.KeyboardShortcutGroup;
@@ -26,7 +25,6 @@
 import java.util.List;
 
 @RequiresApi(24)
-@TargetApi(24)
 class AppCompatDelegateImplN extends AppCompatDelegateImplV23 {
 
     AppCompatDelegateImplN(Context context, Window window, AppCompatCallback callback) {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
index f3fda8e..3aa7f3f 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
@@ -16,15 +16,13 @@
 
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.Window;
 
-@RequiresApi(11)
-@TargetApi(11)
+@RequiresApi(14)
 class AppCompatDelegateImplV11 extends AppCompatDelegateImplV9 {
 
     AppCompatDelegateImplV11(Context context, Window window, AppCompatCallback callback) {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
index 152f379..2a4022c 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -27,6 +26,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
@@ -38,7 +38,6 @@
 import android.view.Window;
 
 @RequiresApi(14)
-@TargetApi(14)
 class AppCompatDelegateImplV14 extends AppCompatDelegateImplV11 {
 
     private static final String KEY_LOCAL_NIGHT_MODE = "appcompat:local_night_mode";
@@ -217,8 +216,10 @@
                 config.uiMode = newNightMode | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
                 res.updateConfiguration(config, metrics);
 
-                // We may need to flush the Resources' drawable cache due to framework bugs..
-                ResourcesFlusher.flush(res);
+                // We may need to flush the Resources' drawable cache due to framework bugs.
+                if (!(Build.VERSION.SDK_INT >= 26)) {
+                    ResourcesFlusher.flush(res);
+                }
             }
             return true;
         } else {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
index d061114..0095b55 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
 import android.app.UiModeManager;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
@@ -24,7 +23,6 @@
 import android.view.Window;
 
 @RequiresApi(23)
-@TargetApi(23)
 class AppCompatDelegateImplV23 extends AppCompatDelegateImplV14 {
 
     private final UiModeManager mUiModeManager;
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
index c5839f9..1daee3d 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
@@ -20,7 +20,6 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.Window.FEATURE_OPTIONS_PANEL;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Dialog;
 import android.content.Context;
@@ -39,13 +38,9 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.v4.app.NavUtils;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.LayoutInflaterCompat;
-import android.support.v4.view.LayoutInflaterFactory;
 import android.support.v4.view.OnApplyWindowInsetsListener;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewConfigurationCompat;
 import android.support.v4.view.ViewPropertyAnimatorCompat;
 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.WindowCompat;
@@ -94,10 +89,9 @@
 
 import org.xmlpull.v1.XmlPullParser;
 
-@RequiresApi(9)
-@TargetApi(9)
+@RequiresApi(14)
 class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
-        implements MenuBuilder.Callback, LayoutInflaterFactory {
+        implements MenuBuilder.Callback, LayoutInflater.Factory2 {
 
     private static final boolean IS_PRE_LOLLIPOP = Build.VERSION.SDK_INT < 21;
 
@@ -232,11 +226,12 @@
         invalidateOptionsMenu();
     }
 
+    @SuppressWarnings("TypeParameterUnusedInFormals")
     @Nullable
     @Override
-    public View findViewById(@IdRes int id) {
+    public <T extends View> T findViewById(@IdRes int id) {
         ensureSubDecor();
-        return mWindow.findViewById(id);
+        return (T) mWindow.findViewById(id);
     }
 
     @Override
@@ -789,7 +784,7 @@
                             endOnGoingFadeAnimation();
 
                             if (shouldAnimateActionModeView()) {
-                                ViewCompat.setAlpha(mActionModeView, 0f);
+                                mActionModeView.setAlpha(0f);
                                 mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
                                 mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
                                     @Override
@@ -799,13 +794,13 @@
 
                                     @Override
                                     public void onAnimationEnd(View view) {
-                                        ViewCompat.setAlpha(mActionModeView, 1f);
+                                        mActionModeView.setAlpha(1f);
                                         mFadeAnim.setListener(null);
                                         mFadeAnim = null;
                                     }
                                 });
                             } else {
-                                ViewCompat.setAlpha(mActionModeView, 1f);
+                                mActionModeView.setAlpha(1f);
                                 mActionModeView.setVisibility(View.VISIBLE);
                             }
                         }
@@ -832,7 +827,7 @@
                     mActionMode = mode;
 
                     if (shouldAnimateActionModeView()) {
-                        ViewCompat.setAlpha(mActionModeView, 0f);
+                        mActionModeView.setAlpha(0f);
                         mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
                         mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
                             @Override
@@ -847,13 +842,13 @@
 
                             @Override
                             public void onAnimationEnd(View view) {
-                                ViewCompat.setAlpha(mActionModeView, 1f);
+                                mActionModeView.setAlpha(1f);
                                 mFadeAnim.setListener(null);
                                 mFadeAnim = null;
                             }
                         });
                     } else {
-                        ViewCompat.setAlpha(mActionModeView, 1f);
+                        mActionModeView.setAlpha(1f);
                         mActionModeView.setVisibility(View.VISIBLE);
                         mActionModeView.sendAccessibilityEvent(
                                 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -1062,10 +1057,9 @@
     public void installViewFactory() {
         LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         if (layoutInflater.getFactory() == null) {
-            LayoutInflaterCompat.setFactory(layoutInflater, this);
+            LayoutInflaterCompat.setFactory2(layoutInflater, this);
         } else {
-            if (!(LayoutInflaterCompat.getFactory(layoutInflater)
-                    instanceof AppCompatDelegateImplV9)) {
+            if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
                 Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                         + " so we can not install AppCompat's");
             }
@@ -1073,7 +1067,7 @@
     }
 
     /**
-     * From {@link android.support.v4.view.LayoutInflaterFactory}
+     * From {@link LayoutInflater.Factory2}.
      */
     @Override
     public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
@@ -1087,6 +1081,14 @@
         return createView(parent, name, context, attrs);
     }
 
+    /**
+     * From {@link LayoutInflater.Factory2}.
+     */
+    @Override
+    public View onCreateView(String name, Context context, AttributeSet attrs) {
+        return onCreateView(null, name, context, attrs);
+    }
+
     View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
         // Let the Activity's LayoutInflater.Factory try and handle it
         if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
@@ -1207,9 +1209,9 @@
     }
 
     private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
-        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&
-                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext)) ||
-                        mDecorContentParent.isOverflowMenuShowPending())) {
+        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu()
+                && (!ViewConfiguration.get(mContext).hasPermanentMenuKey()
+                        || mDecorContentParent.isOverflowMenuShowPending())) {
 
             final Window.Callback cb = getWindowCallback();
 
@@ -1488,7 +1490,7 @@
         final PanelFeatureState st = getPanelState(featureId, true);
         if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
                 mDecorContentParent.canShowOverflowMenu() &&
-                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext))) {
+                !ViewConfiguration.get(mContext).hasPermanentMenuKey()) {
             if (!mDecorContentParent.isOverflowMenuShowing()) {
                 if (!isDestroyed() && preparePanel(st, event)) {
                     handled = mDecorContentParent.showOverflowMenu();
@@ -2097,19 +2099,22 @@
                 return savedState;
             }
 
-            public static final Parcelable.Creator<SavedState> CREATOR
-                    = ParcelableCompat.newCreator(
-                    new ParcelableCompatCreatorCallbacks<SavedState>() {
-                        @Override
-                        public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                            return readFromParcel(in, loader);
-                        }
+            public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+                @Override
+                public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                    return readFromParcel(in, loader);
+                }
 
-                        @Override
-                        public SavedState[] newArray(int size) {
-                            return new SavedState[size];
-                        }
-                    });
+                @Override
+                public SavedState createFromParcel(Parcel in) {
+                    return readFromParcel(in, null);
+                }
+
+                @Override
+                public SavedState[] newArray(int size) {
+                    return new SavedState[size];
+                }
+            };
         }
     }
 
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java b/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
index dd0a4a5..076b1ef 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
@@ -93,9 +93,10 @@
         getDelegate().setContentView(view, params);
     }
 
+    @SuppressWarnings("TypeParameterUnusedInFormals")
     @Nullable
     @Override
-    public View findViewById(@IdRes int id) {
+    public <T extends View> T findViewById(@IdRes int id) {
         return getDelegate().findViewById(id);
     }
 
@@ -143,6 +144,7 @@
     /**
      * @hide
      */
+    @Override
     @RestrictTo(LIBRARY_GROUP)
     public void invalidateOptionsMenu() {
         getDelegate().invalidateOptionsMenu();
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
index a744c5f..b1c3e95 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
@@ -16,50 +16,42 @@
 
 package android.support.v7.app;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
-import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
 import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.text.BidiFormatter;
-import android.support.v7.appcompat.R;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
 import android.widget.RemoteViews;
 
-import java.util.List;
-
 /**
- * An extension of {@link android.support.v4.app.NotificationCompat} which supports
- * {@link android.support.v7.app.NotificationCompat.MediaStyle},
- * {@link android.support.v7.app.NotificationCompat.DecoratedCustomViewStyle},
- * and {@link android.support.v7.app.NotificationCompat.DecoratedMediaCustomViewStyle}.
- * You should start using this variant if you need support any of these styles.
+ * An extension of {@link android.support.v4.app.NotificationCompat} which adds additional styles.
+ * @deprecated Use {@link android.support.v4.app.NotificationCompat}.
  */
+@Deprecated
 public class NotificationCompat extends android.support.v4.app.NotificationCompat {
 
     /**
+     * @deprecated Use the static classes in {@link android.support.v4.app.NotificationCompat}.
+     */
+    @Deprecated
+    public NotificationCompat() {
+    }
+
+    /**
      * Extracts a {@link MediaSessionCompat.Token} from the extra values
      * in the {@link MediaStyle} {@link android.app.Notification notification}.
      *
      * @param notification The notification to extract a {@link MediaSessionCompat.Token} from.
      * @return The {@link MediaSessionCompat.Token} in the {@code notification} if it contains,
      *         null otherwise.
+     * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+     * #getMediaSession(Notification)}.
      */
+    @Deprecated
     public static MediaSessionCompat.Token getMediaSession(Notification notification) {
         Bundle extras = getExtras(notification);
         if (extras != null) {
@@ -84,443 +76,22 @@
         return null;
     }
 
-    @RequiresApi(24)
-    @TargetApi(24)
-    private static void addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            NotificationCompatImpl24.addDecoratedCustomViewStyle(builder);
-        } else if (b.mStyle instanceof DecoratedMediaCustomViewStyle) {
-            NotificationCompatImpl24.addDecoratedMediaCustomViewStyle(builder);
-        } else if (!(b.mStyle instanceof MessagingStyle)) {
-            addStyleGetContentViewLollipop(builder, b);
-        }
-    }
-
-    @RequiresApi(21)
-    @TargetApi(21)
-    private static RemoteViews addStyleGetContentViewLollipop(
-            NotificationBuilderWithBuilderAccessor builder,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        if (b.mStyle instanceof MediaStyle) {
-            MediaStyle mediaStyle = (MediaStyle) b.mStyle;
-            NotificationCompatImpl21.addMediaStyle(builder,
-                    mediaStyle.mActionsToShowInCompact,
-                    mediaStyle.mToken != null ? mediaStyle.mToken.getToken() : null);
-
-            boolean hasContentView = b.getContentView() != null;
-            // If we are on L/M the media notification will only be colored if the expanded version
-            // is of media style, so we have to create a custom view for the collapsed version as
-            // well in that case.
-            boolean isMorL = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
-                    && Build.VERSION.SDK_INT <= Build.VERSION_CODES.M;
-            boolean createCustomContent = hasContentView
-                    || (isMorL && b.getBigContentView() != null);
-            if (b.mStyle instanceof DecoratedMediaCustomViewStyle && createCustomContent) {
-                RemoteViews contentViewMedia = NotificationCompatImplBase.overrideContentViewMedia(
-                        builder, b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo,
-                        b.mNumber, b.mLargeIcon, b.mSubText, b.mUseChronometer,
-                        b.getWhenIfShowing(), b.getPriority(), b.mActions,
-                        mediaStyle.mActionsToShowInCompact, false /* no cancel button on L */,
-                        null /* cancelButtonIntent */, hasContentView /* isDecoratedCustomView */);
-                if (hasContentView) {
-                    NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, contentViewMedia,
-                            b.getContentView());
-                }
-                setBackgroundColor(b.mContext, contentViewMedia, b.getColor());
-                return contentViewMedia;
-            }
-            return null;
-        } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            return getDecoratedContentView(b);
-        }
-        return addStyleGetContentViewJellybean(builder, b);
-    }
-
-    @RequiresApi(16)
-    @TargetApi(16)
-    private static RemoteViews addStyleGetContentViewJellybean(
-            NotificationBuilderWithBuilderAccessor builder,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        if (b.mStyle instanceof MessagingStyle) {
-            addMessagingFallBackStyle((MessagingStyle) b.mStyle, builder, b);
-        }
-        return addStyleGetContentViewIcs(builder, b);
-    }
-
-    private static MessagingStyle.Message findLatestIncomingMessage(MessagingStyle style) {
-        List<MessagingStyle.Message> messages = style.getMessages();
-        for (int i = messages.size() - 1; i >= 0; i--) {
-            MessagingStyle.Message m = messages.get(i);
-            // Incoming messages have a non-empty sender.
-            if (!TextUtils.isEmpty(m.getSender())) {
-                return m;
-            }
-        }
-        if (!messages.isEmpty()) {
-            // No incoming messages, fall back to outgoing message
-            return messages.get(messages.size() - 1);
-        }
-        return null;
-    }
-
-    private static CharSequence makeMessageLine(android.support.v4.app.NotificationCompat.Builder b,
-            MessagingStyle style,
-            MessagingStyle.Message m) {
-        BidiFormatter bidi = BidiFormatter.getInstance();
-        SpannableStringBuilder sb = new SpannableStringBuilder();
-        boolean afterLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
-        int color = afterLollipop || Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1
-                ? Color.BLACK : Color.WHITE;
-        CharSequence replyName = m.getSender();
-        if (TextUtils.isEmpty(m.getSender())) {
-            replyName = style.getUserDisplayName() == null
-                    ? "" : style.getUserDisplayName();
-            color = afterLollipop && b.getColor() != NotificationCompat.COLOR_DEFAULT
-                    ? b.getColor()
-                    : color;
-        }
-        CharSequence senderText = bidi.unicodeWrap(replyName);
-        sb.append(senderText);
-        sb.setSpan(makeFontColorSpan(color),
-                sb.length() - senderText.length(),
-                sb.length(),
-                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* flags */);
-        CharSequence text = m.getText() == null ? "" : m.getText();
-        sb.append("  ").append(bidi.unicodeWrap(text));
-        return sb;
-    }
-
-    private static TextAppearanceSpan makeFontColorSpan(int color) {
-        return new TextAppearanceSpan(null, 0, 0, ColorStateList.valueOf(color), null);
-    }
-
-    private static void addMessagingFallBackStyle(MessagingStyle style,
-            NotificationBuilderWithBuilderAccessor builder,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        SpannableStringBuilder completeMessage = new SpannableStringBuilder();
-        List<MessagingStyle.Message> messages = style.getMessages();
-        boolean showNames = style.getConversationTitle() != null
-                || hasMessagesWithoutSender(style.getMessages());
-        for (int i = messages.size() - 1; i >= 0; i--) {
-            MessagingStyle.Message m = messages.get(i);
-            CharSequence line;
-            line = showNames ? makeMessageLine(b, style, m) : m.getText();
-            if (i != messages.size() - 1) {
-                completeMessage.insert(0, "\n");
-            }
-            completeMessage.insert(0, line);
-        }
-        NotificationCompatImplJellybean.addBigTextStyle(builder, completeMessage);
-    }
-
-    private static boolean hasMessagesWithoutSender(
-            List<MessagingStyle.Message> messages) {
-        for (int i = messages.size() - 1; i >= 0; i--) {
-            MessagingStyle.Message m = messages.get(i);
-            if (m.getSender() == null) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @RequiresApi(14)
-    @TargetApi(14)
-    private static RemoteViews addStyleGetContentViewIcs(
-            NotificationBuilderWithBuilderAccessor builder,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        if (b.mStyle instanceof MediaStyle) {
-            MediaStyle mediaStyle = (MediaStyle) b.mStyle;
-            boolean isDecorated = b.mStyle instanceof DecoratedMediaCustomViewStyle
-                    && b.getContentView() != null;
-            RemoteViews contentViewMedia = NotificationCompatImplBase.overrideContentViewMedia(
-                    builder, b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber,
-                    b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(),
-                    b.getPriority(), b.mActions, mediaStyle.mActionsToShowInCompact,
-                    mediaStyle.mShowCancelButton, mediaStyle.mCancelButtonIntent, isDecorated);
-            if (isDecorated) {
-                NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, contentViewMedia,
-                        b.getContentView());
-                return contentViewMedia;
-            }
-        } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            return getDecoratedContentView(b);
-        }
-        return null;
-    }
-
-    @RequiresApi(16)
-    @TargetApi(16)
-    private static void addBigStyleToBuilderJellybean(Notification n,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        if (b.mStyle instanceof MediaStyle) {
-            MediaStyle mediaStyle = (MediaStyle) b.mStyle;
-            RemoteViews innerView = b.getBigContentView() != null
-                    ? b.getBigContentView()
-                    : b.getContentView();
-            boolean isDecorated = b.mStyle instanceof DecoratedMediaCustomViewStyle
-                    && innerView != null;
-            NotificationCompatImplBase.overrideMediaBigContentView(n, b.mContext,
-                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon,
-                    b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), 0,
-                    b.mActions, mediaStyle.mShowCancelButton, mediaStyle.mCancelButtonIntent,
-                    isDecorated);
-            if (isDecorated) {
-                NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, n.bigContentView,
-                        innerView);
-            }
-        } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            addDecoratedBigStyleToBuilderJellybean(n, b);
-        }
-    }
-
-    private static RemoteViews getDecoratedContentView(
-            android.support.v4.app.NotificationCompat.Builder b) {
-        if (b.getContentView() == null) {
-            // No special content view
-            return null;
-        }
-        RemoteViews remoteViews = NotificationCompatImplBase.applyStandardTemplateWithActions(
-                b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber,
-                b.mNotification.icon, b.mLargeIcon, b.mSubText, b.mUseChronometer,
-                b.getWhenIfShowing(), b.getPriority(), b.getColor(),
-                R.layout.notification_template_custom_big, false /* fitIn1U */, null /* actions */);
-        NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, remoteViews,
-                b.getContentView());
-        return remoteViews;
-    }
-
-    @RequiresApi(16)
-    @TargetApi(16)
-    private static void addDecoratedBigStyleToBuilderJellybean(Notification n,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        RemoteViews bigContentView = b.getBigContentView();
-        RemoteViews innerView = bigContentView != null ? bigContentView : b.getContentView();
-        if (innerView == null) {
-            // No expandable notification
-            return;
-        }
-        RemoteViews remoteViews = NotificationCompatImplBase.applyStandardTemplateWithActions(
-                b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber,
-                n.icon ,b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(),
-                b.getPriority(), b.getColor(), R.layout.notification_template_custom_big,
-                false /* fitIn1U */, b.mActions);
-        NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, remoteViews, innerView);
-        n.bigContentView = remoteViews;
-    }
-
-    @RequiresApi(21)
-    @TargetApi(21)
-    private static void addDecoratedHeadsUpToBuilderLollipop(Notification n,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        RemoteViews headsUp = b.getHeadsUpContentView();
-        RemoteViews innerView = headsUp != null ? headsUp : b.getContentView();
-        if (headsUp == null) {
-            // No expandable notification
-            return;
-        }
-        RemoteViews remoteViews = NotificationCompatImplBase.applyStandardTemplateWithActions(
-                b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, n.icon,
-                b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(),
-                b.getColor(), R.layout.notification_template_custom_big, false /* fitIn1U */,
-                b.mActions);
-        NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, remoteViews, innerView);
-        n.headsUpContentView = remoteViews;
-    }
-
-    @RequiresApi(21)
-    @TargetApi(21)
-    private static void addBigStyleToBuilderLollipop(Notification n,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        RemoteViews innerView = b.getBigContentView() != null
-                ? b.getBigContentView()
-                : b.getContentView();
-        if (b.mStyle instanceof DecoratedMediaCustomViewStyle && innerView != null) {
-            NotificationCompatImplBase.overrideMediaBigContentView(n, b.mContext,
-                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon,
-                    b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), 0,
-                    b.mActions, false /* showCancelButton */, null /* cancelButtonIntent */,
-                    true /* decoratedCustomView */);
-                    NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, n.bigContentView,
-                            innerView);
-            setBackgroundColor(b.mContext, n.bigContentView, b.getColor());
-        } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            addDecoratedBigStyleToBuilderJellybean(n, b);
-        }
-    }
-
-    private static void setBackgroundColor(Context context, RemoteViews views, int color) {
-        if (color == COLOR_DEFAULT) {
-            color = context.getResources().getColor(
-                    R.color.notification_material_background_media_default_color);
-        }
-        views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color);
-    }
-
-    @RequiresApi(21)
-    @TargetApi(21)
-    private static void addHeadsUpToBuilderLollipop(Notification n,
-            android.support.v4.app.NotificationCompat.Builder b) {
-        RemoteViews innerView = b.getHeadsUpContentView() != null
-                ? b.getHeadsUpContentView()
-                : b.getContentView();
-        if (b.mStyle instanceof DecoratedMediaCustomViewStyle && innerView != null) {
-            n.headsUpContentView = NotificationCompatImplBase.generateMediaBigView(b.mContext,
-                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber,
-                    b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(),
-                    b.getPriority(), 0, b.mActions, false /* showCancelButton */,
-                    null /* cancelButtonIntent */, true /* decoratedCustomView */);
-            NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, n.headsUpContentView,
-                    innerView);
-            setBackgroundColor(b.mContext, n.headsUpContentView, b.getColor());
-        } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            addDecoratedHeadsUpToBuilderLollipop(n, b);
-        }
-    }
-
     /**
-     * See {@link android.support.v4.app.NotificationCompat}. In addition to the builder in v4, this
-     * builder also supports {@link MediaStyle}.
+     * @deprecated All {@link android.support.v4.app.NotificationCompat.Style styles} can now be
+     * used with {@link android.support.v4.app.NotificationCompat.Builder}.
      */
+    @Deprecated
     public static class Builder extends android.support.v4.app.NotificationCompat.Builder {
 
         /**
          * @inheritDoc
+         * @deprecated Use {@link android.support.v4.app.NotificationCompat.Builder
+         * #NotificationCompat.Builder(Context, String)}
          */
+        @Deprecated
         public Builder(Context context) {
             super(context);
         }
-
-        /**
-         * @return the text of the notification
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        protected CharSequence resolveText() {
-            if (mStyle instanceof MessagingStyle) {
-                MessagingStyle style = (MessagingStyle) mStyle;
-                MessagingStyle.Message m = findLatestIncomingMessage(style);
-                CharSequence conversationTitle = style.getConversationTitle();
-                if (m != null) {
-                    return conversationTitle != null ? makeMessageLine(this, style, m)
-                            : m.getText();
-                }
-            }
-            return super.resolveText();
-        }
-
-        /**
-         * @return the title of the notification
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        protected CharSequence resolveTitle() {
-            if (mStyle instanceof MessagingStyle) {
-                MessagingStyle style = (MessagingStyle) mStyle;
-                MessagingStyle.Message m = findLatestIncomingMessage(style);
-                CharSequence conversationTitle = style.getConversationTitle();
-                if (conversationTitle != null || m != null) {
-                    return conversationTitle != null ? conversationTitle : m.getSender();
-                }
-            }
-            return super.resolveTitle();
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        protected BuilderExtender getExtender() {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                return new Api24Extender();
-            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-                return new LollipopExtender();
-            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-                return new JellybeanExtender();
-            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-                return new IceCreamSandwichExtender();
-            } else {
-                return super.getExtender();
-            }
-        }
-    }
-
-    private static class IceCreamSandwichExtender extends BuilderExtender {
-
-        IceCreamSandwichExtender() {
-        }
-
-        @Override
-        public Notification build(android.support.v4.app.NotificationCompat.Builder b,
-                NotificationBuilderWithBuilderAccessor builder) {
-            RemoteViews contentView = addStyleGetContentViewIcs(builder, b);
-            Notification n = builder.build();
-            // The above call might override decorated content views again, let's make sure it
-            // sticks.
-            if (contentView != null) {
-                n.contentView = contentView;
-            } else if (b.getContentView() != null) {
-                n.contentView = b.getContentView();
-            }
-            return n;
-        }
-    }
-
-    private static class JellybeanExtender extends BuilderExtender {
-
-        JellybeanExtender() {
-        }
-
-        @Override
-        public Notification build(android.support.v4.app.NotificationCompat.Builder b,
-                NotificationBuilderWithBuilderAccessor builder) {
-            RemoteViews contentView = addStyleGetContentViewJellybean(builder, b);
-            Notification n = builder.build();
-            // The above call might override decorated content views again, let's make sure it
-            // sticks.
-            if (contentView != null) {
-                n.contentView = contentView;
-            }
-            addBigStyleToBuilderJellybean(n, b);
-            return n;
-        }
-    }
-
-    private static class LollipopExtender extends BuilderExtender {
-
-        LollipopExtender() {
-        }
-
-        @Override
-        public Notification build(android.support.v4.app.NotificationCompat.Builder b,
-                NotificationBuilderWithBuilderAccessor builder) {
-            RemoteViews contentView = addStyleGetContentViewLollipop(builder, b);
-            Notification n = builder.build();
-            // The above call might override decorated content views again, let's make sure it
-            // sticks.
-            if (contentView != null) {
-                n.contentView = contentView;
-            }
-            addBigStyleToBuilderLollipop(n, b);
-            addHeadsUpToBuilderLollipop(n, b);
-            return n;
-        }
-    }
-
-    private static class Api24Extender extends BuilderExtender {
-
-        @Override
-        public Notification build(android.support.v4.app.NotificationCompat.Builder b,
-                NotificationBuilderWithBuilderAccessor builder) {
-            addStyleToBuilderApi24(builder, b);
-            return builder.build();
-        }
     }
 
     /**
@@ -528,9 +99,11 @@
      *
      * In the expanded form, {@link Notification#bigContentView}, up to 5
      * {@link android.support.v4.app.NotificationCompat.Action}s specified with
-     * {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent) addAction} will
-     * be shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
-     * {@link NotificationCompat.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will
+     * {@link android.support.v4.app.NotificationCompat.Builder#addAction(int, CharSequence,
+     * PendingIntent) addAction} will be shown as icon-only pushbuttons, suitable for transport
+     * controls. The Bitmap given to
+     * {@link android.support.v4.app.NotificationCompat.Builder#setLargeIcon(
+     * android.graphics.Bitmap) setLargeIcon()} will
      * be treated as album artwork.
      *
      * Unlike the other styles provided here, MediaStyle can also modify the standard-size
@@ -540,7 +113,8 @@
      *
      * Notifications created with MediaStyle will have their category set to
      * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
-     * category using {@link NotificationCompat.Builder#setCategory(String) setCategory()}.
+     * category using {@link android.support.v4.app.NotificationCompat.Builder#setCategory(String)
+     * setCategory()}.
      *
      * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
      * {@link android.support.v7.app.NotificationCompat.MediaStyle#setMediaSession}, the System UI
@@ -548,7 +122,7 @@
      * accordingly (by showing album artwork in the lockscreen, for example).
      *
      * To use this style with your Notification, feed it to
-     * {@link NotificationCompat.Builder#setStyle} like so:
+     * {@link android.support.v4.app.NotificationCompat.Builder#setStyle} like so:
      * <pre class="prettyprint">
      * Notification noti = new NotificationCompat.Builder()
      *     .setSmallIcon(R.drawable.ic_stat_player)
@@ -560,20 +134,28 @@
      *     .build();
      * </pre>
      *
-     * @see Notification#bigContentView
+     * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle}.
      */
-    public static class MediaStyle extends android.support.v4.app.NotificationCompat.Style {
+    @Deprecated
+    public static class MediaStyle extends
+            android.support.v4.media.app.NotificationCompat.MediaStyle {
 
-        int[] mActionsToShowInCompact = null;
-        MediaSessionCompat.Token mToken;
-        boolean mShowCancelButton;
-        PendingIntent mCancelButtonIntent;
-
+        /**
+         * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+         * #MediaStyle()}
+         */
+        @Deprecated
         public MediaStyle() {
+            super();
         }
 
+        /**
+         * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+         * #MediaStyle(android.support.v4.app.NotificationCompat.Builder)}
+         */
+        @Deprecated
         public MediaStyle(android.support.v4.app.NotificationCompat.Builder builder) {
-            setBuilder(builder);
+            super(builder);
         }
 
         /**
@@ -581,19 +163,27 @@
          * notification view.
          *
          * @param actions the indices of the actions to show in the compact notification view
+         *
+         * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+         * #setShowActionsInCompactView(int...)}
          */
+        @Deprecated
+        @Override
         public MediaStyle setShowActionsInCompactView(int...actions) {
-            mActionsToShowInCompact = actions;
-            return this;
+            return (MediaStyle) super.setShowActionsInCompactView(actions);
         }
 
         /**
          * Attaches a {@link MediaSessionCompat.Token} to this Notification
          * to provide additional playback information and control to the SystemUI.
+         *
+         * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+         * #setMediaSession(MediaSessionCompat.Token)}
          */
+        @Deprecated
+        @Override
         public MediaStyle setMediaSession(MediaSessionCompat.Token token) {
-            mToken = token;
-            return this;
+            return (MediaStyle) super.setMediaSession(token);
         }
 
         /**
@@ -619,10 +209,14 @@
          * <p>Also note that this method is a no-op when running on Lollipop and later.
          *
          * @param show whether to show a cancel button
+         *
+         * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+         * #setShowCancelButton(boolean)}
          */
+        @Deprecated
+        @Override
         public MediaStyle setShowCancelButton(boolean show) {
-            mShowCancelButton = show;
-            return this;
+            return (MediaStyle) super.setShowCancelButton(show);
         }
 
         /**
@@ -630,10 +224,14 @@
          * #setShowCancelButton}.
          *
          * @param pendingIntent the intent to be sent when the cancel button is pressed
+         *
+         * @deprecated Use {@link android.support.v4.media.app.NotificationCompat.MediaStyle
+         * #setCancelButtonIntent(PendingIntent)}
          */
+        @Deprecated
+        @Override
         public MediaStyle setCancelButtonIntent(PendingIntent pendingIntent) {
-            mCancelButtonIntent = pendingIntent;
-            return this;
+            return (MediaStyle) super.setCancelButtonIntent(pendingIntent);
         }
     }
 
@@ -651,7 +249,7 @@
      * corresponding custom views to display.
      *
      * <p>To use this style with your Notification, feed it to
-     * {@link NotificationCompat.Builder#setStyle(Style)} like so:
+     * {@link android.support.v4.app.NotificationCompat.Builder#setStyle(Style)} like so:
      * <pre class="prettyprint">
      * Notification noti = new NotificationCompat.Builder()
      *     .setSmallIcon(R.drawable.ic_stat_player)
@@ -665,10 +263,23 @@
      * {@link android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification} or
      * {@link android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification_Title} in
      * your custom views in order to get the correct styling on each platform version.
+     *
+     * @deprecated Use {@link android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle}
+     * and {@link android.support.compat.R.style#TextAppearance_Compat_Notification} or
+     * {@link android.support.compat.R.style#TextAppearance_Compat_Notification_Title}.
      */
-    public static class DecoratedCustomViewStyle extends Style {
+    @Deprecated
+    public static class DecoratedCustomViewStyle extends
+            android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle {
 
+        /**
+         * @deprecated Use
+         * {@link android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle
+         * #DecoratedCustomViewStyle()}.
+         */
+        @Deprecated
         public DecoratedCustomViewStyle() {
+            super();
         }
     }
 
@@ -685,7 +296,7 @@
      * corresponding custom views to display.
      *
      * <p>To use this style with your Notification, feed it to
-     * {@link NotificationCompat.Builder#setStyle(Style)} like so:
+     * {@link android.support.v4.app.NotificationCompat.Builder#setStyle(Style)} like so:
      * <pre class="prettyprint">
      * Notification noti = new Notification.Builder()
      *     .setSmallIcon(R.drawable.ic_stat_player)
@@ -702,12 +313,26 @@
      * android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification_Title_Media} in
      * your custom views in order to get the correct styling on each platform version.
      *
-     * @see DecoratedCustomViewStyle
-     * @see MediaStyle
+     * @see android.support.v4.app.NotificationCompat.DecoratedCustomViewStyle
+     * @see android.support.v4.media.app.NotificationCompat.MediaStyle
+     *
+     * @deprecated Use
+     * {@link android.support.v4.media.app.NotificationCompat.DecoratedMediaCustomViewStyle} and
+     * {@link android.support.mediacompat.R.style#TextAppearance_Compat_Notification_Media} and
+     * {@link android.support.mediacompat.R.style#TextAppearance_Compat_Notification_Title_Media}.
      */
-    public static class DecoratedMediaCustomViewStyle extends MediaStyle {
+    @Deprecated
+    public static class DecoratedMediaCustomViewStyle extends
+            android.support.v4.media.app.NotificationCompat.DecoratedMediaCustomViewStyle {
 
+        /**
+         * @deprecated Use
+         * {@link android.support.v4.media.app.NotificationCompat.DecoratedMediaCustomViewStyle
+         * #DecoratedMediaCustomViewStyle()}.
+         */
+        @Deprecated
         public DecoratedMediaCustomViewStyle() {
+            super();
         }
     }
 }
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
deleted file mode 100644
index 9b0f028..0000000
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v7.app;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.media.session.MediaSession;
-import android.support.annotation.RequiresApi;
-import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
-
-@RequiresApi(21)
-@TargetApi(21)
-class NotificationCompatImpl21 {
-
-    public static void addMediaStyle(NotificationBuilderWithBuilderAccessor b,
-            int[] actionsToShowInCompact,
-            Object token) {
-        Notification.MediaStyle style = new Notification.MediaStyle(b.getBuilder());
-        if (actionsToShowInCompact != null) {
-            style.setShowActionsInCompactView(actionsToShowInCompact);
-        }
-        if (token != null) {
-            style.setMediaSession((MediaSession.Token) token);
-        }
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
deleted file mode 100644
index a65751b..0000000
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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
- */
-
-package android.support.v7.app;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.support.annotation.RequiresApi;
-import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
-
-@RequiresApi(24)
-@TargetApi(24)
-class NotificationCompatImpl24 {
-
-    public static void addDecoratedCustomViewStyle(NotificationBuilderWithBuilderAccessor b) {
-        Notification.Builder builder = b.getBuilder();
-        builder.setStyle(new Notification.DecoratedCustomViewStyle());
-    }
-
-    public static void addDecoratedMediaCustomViewStyle(NotificationBuilderWithBuilderAccessor b) {
-        Notification.Builder builder = b.getBuilder();
-        builder.setStyle(new Notification.DecoratedMediaCustomViewStyle());
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
deleted file mode 100644
index a6e73ef..0000000
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * 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
- */
-
-package android.support.v7.app;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.SystemClock;
-import android.support.annotation.RequiresApi;
-import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationCompatBase;
-import android.support.v7.appcompat.R;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class to generate MediaStyle notifications for pre-Lollipop platforms. Overrides
- * contentView and bigContentView of the notification.
- */
-@RequiresApi(9)
-@TargetApi(9)
-class NotificationCompatImplBase {
-
-    static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
-    static final int MAX_MEDIA_BUTTONS = 5;
-    private static final int MAX_ACTION_BUTTONS = 3;
-
-    @RequiresApi(11)
-    @TargetApi(11)
-    public static <T extends NotificationCompatBase.Action> RemoteViews overrideContentViewMedia(
-            NotificationBuilderWithBuilderAccessor builder,
-            Context context, CharSequence contentTitle, CharSequence contentText,
-            CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
-            boolean useChronometer, long when, int priority, List<T> actions,
-            int[] actionsToShowInCompact, boolean showCancelButton,
-            PendingIntent cancelButtonIntent, boolean isDecoratedCustomView) {
-        RemoteViews views = generateContentViewMedia(context, contentTitle, contentText, contentInfo,
-                number, largeIcon, subText, useChronometer, when, priority, actions,
-                actionsToShowInCompact, showCancelButton, cancelButtonIntent,
-                isDecoratedCustomView);
-        builder.getBuilder().setContent(views);
-        if (showCancelButton) {
-            builder.getBuilder().setOngoing(true);
-        }
-        return views;
-    }
-
-    @RequiresApi(11)
-    @TargetApi(11)
-    private static <T extends NotificationCompatBase.Action> RemoteViews generateContentViewMedia(
-            Context context, CharSequence contentTitle, CharSequence contentText,
-            CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
-            boolean useChronometer, long when, int priority, List<T> actions,
-            int[] actionsToShowInCompact, boolean showCancelButton,
-            PendingIntent cancelButtonIntent, boolean isDecoratedCustomView) {
-        RemoteViews view = applyStandardTemplate(context, contentTitle, contentText, contentInfo,
-                number, 0 /* smallIcon */, largeIcon, subText, useChronometer, when, priority,
-                0 /* color is unused on media */,
-                isDecoratedCustomView ? R.layout.notification_template_media_custom
-                        : R.layout.notification_template_media,
-                true /* fitIn1U */);
-
-        final int numActions = actions.size();
-        final int N = actionsToShowInCompact == null
-                ? 0
-                : Math.min(actionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
-        view.removeAllViews(R.id.media_actions);
-        if (N > 0) {
-            for (int i = 0; i < N; i++) {
-                if (i >= numActions) {
-                    throw new IllegalArgumentException(String.format(
-                            "setShowActionsInCompactView: action %d out of bounds (max %d)",
-                            i, numActions - 1));
-                }
-
-                final NotificationCompatBase.Action action = actions.get(actionsToShowInCompact[i]);
-                final RemoteViews button = generateMediaActionButton(context, action);
-                view.addView(R.id.media_actions, button);
-            }
-        }
-        if (showCancelButton) {
-            view.setViewVisibility(R.id.end_padder, View.GONE);
-            view.setViewVisibility(R.id.cancel_action, View.VISIBLE);
-            view.setOnClickPendingIntent(R.id.cancel_action, cancelButtonIntent);
-            view.setInt(R.id.cancel_action, "setAlpha",
-                    context.getResources().getInteger(R.integer.cancel_button_image_alpha));
-        } else {
-            view.setViewVisibility(R.id.end_padder, View.VISIBLE);
-            view.setViewVisibility(R.id.cancel_action, View.GONE);
-        }
-        return view;
-    }
-
-    @RequiresApi(16)
-    @TargetApi(16)
-    public static <T extends NotificationCompatBase.Action> void overrideMediaBigContentView(
-            Notification n, Context context, CharSequence contentTitle, CharSequence contentText,
-            CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
-            boolean useChronometer, long when, int priority, int color, List<T> actions,
-            boolean showCancelButton, PendingIntent cancelButtonIntent,
-            boolean decoratedCustomView) {
-        n.bigContentView = generateMediaBigView(context, contentTitle, contentText, contentInfo,
-                number, largeIcon, subText, useChronometer, when, priority, color, actions,
-                showCancelButton, cancelButtonIntent, decoratedCustomView);
-        if (showCancelButton) {
-            n.flags |= Notification.FLAG_ONGOING_EVENT;
-        }
-    }
-
-    @RequiresApi(11)
-    @TargetApi(11)
-    public static <T extends NotificationCompatBase.Action> RemoteViews generateMediaBigView(
-            Context context, CharSequence contentTitle, CharSequence contentText,
-            CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
-            boolean useChronometer, long when, int priority, int color, List<T> actions,
-            boolean showCancelButton, PendingIntent cancelButtonIntent,
-            boolean decoratedCustomView) {
-        final int actionCount = Math.min(actions.size(), MAX_MEDIA_BUTTONS);
-        RemoteViews big = applyStandardTemplate(context, contentTitle, contentText, contentInfo,
-                number, 0 /* smallIcon */, largeIcon, subText, useChronometer, when, priority,
-                color,  /* fitIn1U */getBigMediaLayoutResource(decoratedCustomView, actionCount),
-                false);
-
-        big.removeAllViews(R.id.media_actions);
-        if (actionCount > 0) {
-            for (int i = 0; i < actionCount; i++) {
-                final RemoteViews button = generateMediaActionButton(context, actions.get(i));
-                big.addView(R.id.media_actions, button);
-            }
-        }
-        if (showCancelButton) {
-            big.setViewVisibility(R.id.cancel_action, View.VISIBLE);
-            big.setInt(R.id.cancel_action, "setAlpha",
-                    context.getResources().getInteger(R.integer.cancel_button_image_alpha));
-            big.setOnClickPendingIntent(R.id.cancel_action, cancelButtonIntent);
-        } else {
-            big.setViewVisibility(R.id.cancel_action, View.GONE);
-        }
-        return big;
-    }
-
-    @RequiresApi(11)
-    @TargetApi(11)
-    private static RemoteViews generateMediaActionButton(Context context,
-            NotificationCompatBase.Action action) {
-        final boolean tombstone = (action.getActionIntent() == null);
-        RemoteViews button = new RemoteViews(context.getPackageName(),
-                R.layout.notification_media_action);
-        button.setImageViewResource(R.id.action0, action.getIcon());
-        if (!tombstone) {
-            button.setOnClickPendingIntent(R.id.action0, action.getActionIntent());
-        }
-        if (Build.VERSION.SDK_INT >= 15) {
-            button.setContentDescription(R.id.action0, action.getTitle());
-        }
-        return button;
-    }
-
-    @RequiresApi(11)
-    @TargetApi(11)
-    private static int getBigMediaLayoutResource(boolean decoratedCustomView, int actionCount) {
-        if (actionCount <= 3) {
-            return decoratedCustomView
-                    ? R.layout.notification_template_big_media_narrow_custom
-                    : R.layout.notification_template_big_media_narrow;
-        } else {
-            return decoratedCustomView
-                    ? R.layout.notification_template_big_media_custom
-                    : R.layout.notification_template_big_media;
-        }
-    }
-
-    public static RemoteViews applyStandardTemplateWithActions(Context context,
-            CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
-            int number, int smallIcon, Bitmap largeIcon, CharSequence subText,
-            boolean useChronometer, long when, int priority, int color, int resId, boolean fitIn1U,
-            ArrayList<NotificationCompat.Action> actions) {
-        RemoteViews remoteViews = applyStandardTemplate(context, contentTitle, contentText,
-                contentInfo, number, smallIcon, largeIcon, subText, useChronometer, when, priority,
-                color, resId, fitIn1U);
-        remoteViews.removeAllViews(R.id.actions);
-        boolean actionsVisible = false;
-        if (actions != null) {
-            int N = actions.size();
-            if (N > 0) {
-                actionsVisible = true;
-                if (N > MAX_ACTION_BUTTONS) N = MAX_ACTION_BUTTONS;
-                for (int i = 0; i < N; i++) {
-                    final RemoteViews button = generateActionButton(context, actions.get(i));
-                    remoteViews.addView(R.id.actions, button);
-                }
-            }
-        }
-        int actionVisibility = actionsVisible ? View.VISIBLE : View.GONE;
-        remoteViews.setViewVisibility(R.id.actions, actionVisibility);
-        remoteViews.setViewVisibility(R.id.action_divider, actionVisibility);
-        return remoteViews;
-    }
-
-    private static RemoteViews generateActionButton(Context context,
-            NotificationCompat.Action action) {
-        final boolean tombstone = (action.actionIntent == null);
-        RemoteViews button =  new RemoteViews(context.getPackageName(),
-                tombstone ? getActionTombstoneLayoutResource()
-                        : getActionLayoutResource());
-        button.setImageViewBitmap(R.id.action_image,
-                createColoredBitmap(context, action.getIcon(),
-                        context.getResources().getColor(R.color.notification_action_color_filter)));
-        button.setTextViewText(R.id.action_text, action.title);
-        if (!tombstone) {
-            button.setOnClickPendingIntent(R.id.action_container, action.actionIntent);
-        }
-        if (Build.VERSION.SDK_INT >= 15) {
-            button.setContentDescription(R.id.action_container, action.title);
-        }
-        return button;
-    }
-
-    private static Bitmap createColoredBitmap(Context context, int iconId, int color) {
-        return createColoredBitmap(context, iconId, color, 0);
-    }
-
-    private static Bitmap createColoredBitmap(Context context, int iconId, int color, int size) {
-        Drawable drawable = context.getResources().getDrawable(iconId);
-        int width = size == 0 ? drawable.getIntrinsicWidth() : size;
-        int height = size == 0 ? drawable.getIntrinsicHeight() : size;
-        Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        drawable.setBounds(0, 0, width, height);
-        if (color != 0) {
-            drawable.mutate().setColorFilter(
-                    new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
-        }
-        Canvas canvas = new Canvas(resultBitmap);
-        drawable.draw(canvas);
-        return resultBitmap;
-    }
-
-    private static int getActionLayoutResource() {
-        return R.layout.notification_action;
-    }
-
-    private static int getActionTombstoneLayoutResource() {
-        return R.layout.notification_action_tombstone;
-    }
-
-    public static RemoteViews applyStandardTemplate(Context context,
-            CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
-            int number, int smallIcon, Bitmap largeIcon, CharSequence subText,
-            boolean useChronometer, long when, int priority, int color, int resId,
-            boolean fitIn1U) {
-        Resources res = context.getResources();
-        RemoteViews contentView = new RemoteViews(context.getPackageName(), resId);
-        boolean showLine3 = false;
-        boolean showLine2 = false;
-
-        boolean minPriority = priority < NotificationCompat.PRIORITY_LOW;
-        if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 21) {
-            // lets color the backgrounds
-            if (minPriority) {
-                contentView.setInt(R.id.notification_background,
-                        "setBackgroundResource", R.drawable.notification_bg_low);
-                contentView.setInt(R.id.icon,
-                        "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
-            } else {
-                contentView.setInt(R.id.notification_background,
-                        "setBackgroundResource", R.drawable.notification_bg);
-                contentView.setInt(R.id.icon,
-                        "setBackgroundResource", R.drawable.notification_template_icon_bg);
-            }
-        }
-
-        if (largeIcon != null) {
-            // On versions before Jellybean, the large icon was shown by SystemUI, so we need to hide
-            // it here.
-            if (Build.VERSION.SDK_INT >= 16) {
-                contentView.setViewVisibility(R.id.icon, View.VISIBLE);
-                contentView.setImageViewBitmap(R.id.icon, largeIcon);
-            } else {
-                contentView.setViewVisibility(R.id.icon, View.GONE);
-            }
-            if (smallIcon != 0) {
-                int backgroundSize = res.getDimensionPixelSize(
-                        R.dimen.notification_right_icon_size);
-                int iconSize = backgroundSize - res.getDimensionPixelSize(
-                        R.dimen.notification_small_icon_background_padding) * 2;
-                if (Build.VERSION.SDK_INT >= 21) {
-                    Bitmap smallBit = createIconWithBackground(context,
-                            smallIcon,
-                            backgroundSize,
-                            iconSize,
-                            color);
-                    contentView.setImageViewBitmap(R.id.right_icon, smallBit);
-                } else {
-                    contentView.setImageViewBitmap(R.id.right_icon,
-                            createColoredBitmap(context, smallIcon, Color.WHITE));
-                }
-                contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
-            }
-        } else if (smallIcon != 0) { // small icon at left
-            contentView.setViewVisibility(R.id.icon, View.VISIBLE);
-            if (Build.VERSION.SDK_INT >= 21) {
-                int backgroundSize = res.getDimensionPixelSize(
-                        R.dimen.notification_large_icon_width)
-                        - res.getDimensionPixelSize(R.dimen.notification_big_circle_margin);
-                int iconSize = res.getDimensionPixelSize(
-                        R.dimen.notification_small_icon_size_as_large);
-                Bitmap smallBit = createIconWithBackground(context,
-                        smallIcon,
-                        backgroundSize,
-                        iconSize,
-                        color);
-                contentView.setImageViewBitmap(R.id.icon, smallBit);
-            } else {
-                contentView.setImageViewBitmap(R.id.icon,
-                        createColoredBitmap(context, smallIcon, Color.WHITE));
-            }
-        }
-        if (contentTitle != null) {
-            contentView.setTextViewText(R.id.title, contentTitle);
-        }
-        if (contentText != null) {
-            contentView.setTextViewText(R.id.text, contentText);
-            showLine3 = true;
-        }
-        // If there is a large icon we have a right side
-        boolean hasRightSide = !(Build.VERSION.SDK_INT >= 21) && largeIcon != null;
-        if (contentInfo != null) {
-            contentView.setTextViewText(R.id.info, contentInfo);
-            contentView.setViewVisibility(R.id.info, View.VISIBLE);
-            showLine3 = true;
-            hasRightSide = true;
-        } else if (number > 0) {
-            final int tooBig = res.getInteger(
-                    R.integer.status_bar_notification_info_maxnum);
-            if (number > tooBig) {
-                contentView.setTextViewText(R.id.info, ((Resources) res).getString(
-                        R.string.status_bar_notification_info_overflow));
-            } else {
-                NumberFormat f = NumberFormat.getIntegerInstance();
-                contentView.setTextViewText(R.id.info, f.format(number));
-            }
-            contentView.setViewVisibility(R.id.info, View.VISIBLE);
-            showLine3 = true;
-            hasRightSide = true;
-        } else {
-            contentView.setViewVisibility(R.id.info, View.GONE);
-        }
-
-        // Need to show three lines? Only allow on Jellybean+
-        if (subText != null && Build.VERSION.SDK_INT >= 16) {
-            contentView.setTextViewText(R.id.text, subText);
-            if (contentText != null) {
-                contentView.setTextViewText(R.id.text2, contentText);
-                contentView.setViewVisibility(R.id.text2, View.VISIBLE);
-                showLine2 = true;
-            } else {
-                contentView.setViewVisibility(R.id.text2, View.GONE);
-            }
-        }
-
-        // RemoteViews.setViewPadding and RemoteViews.setTextViewTextSize is not available on ICS-
-        if (showLine2 && Build.VERSION.SDK_INT >= 16) {
-            if (fitIn1U) {
-                // need to shrink all the type to make sure everything fits
-                final float subTextSize = res.getDimensionPixelSize(
-                        R.dimen.notification_subtext_size);
-                contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
-            }
-            // vertical centering
-            contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
-        }
-
-        if (when != 0) {
-            if (useChronometer && Build.VERSION.SDK_INT >= 16) {
-                contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
-                contentView.setLong(R.id.chronometer, "setBase",
-                        when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
-                contentView.setBoolean(R.id.chronometer, "setStarted", true);
-            } else {
-                contentView.setViewVisibility(R.id.time, View.VISIBLE);
-                contentView.setLong(R.id.time, "setTime", when);
-            }
-            hasRightSide = true;
-        }
-        contentView.setViewVisibility(R.id.right_side, hasRightSide ? View.VISIBLE : View.GONE);
-        contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
-        return contentView;
-    }
-
-    public static Bitmap createIconWithBackground(Context ctx, int iconId, int size, int iconSize,
-            int color) {
-        Bitmap coloredBitmap = createColoredBitmap(ctx, R.drawable.notification_icon_background,
-                        color == NotificationCompat.COLOR_DEFAULT ? 0 : color, size);
-        Canvas canvas = new Canvas(coloredBitmap);
-        Drawable icon = ctx.getResources().getDrawable(iconId).mutate();
-        icon.setFilterBitmap(true);
-        int inset = (size - iconSize) / 2;
-        icon.setBounds(inset, inset, iconSize + inset, iconSize + inset);
-        icon.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP));
-        icon.draw(canvas);
-        return coloredBitmap;
-    }
-
-    public static void buildIntoRemoteViews(Context ctx, RemoteViews outerView,
-            RemoteViews innerView) {
-        // this needs to be done fore the other calls, since otherwise we might hide the wrong
-        // things if our ids collide.
-        hideNormalContent(outerView);
-        outerView.removeAllViews(R.id.notification_main_column);
-        outerView.addView(R.id.notification_main_column, innerView.clone());
-        outerView.setViewVisibility(R.id.notification_main_column, View.VISIBLE);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            // Adjust padding depending on font size.
-            outerView.setViewPadding(R.id.notification_main_column_container,
-                    0, calculateTopPadding(ctx), 0, 0);
-        }
-    }
-
-    private static void hideNormalContent(RemoteViews outerView) {
-        outerView.setViewVisibility(R.id.title, View.GONE);
-        outerView.setViewVisibility(R.id.text2, View.GONE);
-        outerView.setViewVisibility(R.id.text, View.GONE);
-    }
-
-    public static int calculateTopPadding(Context ctx) {
-        int padding = ctx.getResources().getDimensionPixelSize(R.dimen.notification_top_pad);
-        int largePadding = ctx.getResources().getDimensionPixelSize(
-                R.dimen.notification_top_pad_large_text);
-        float fontScale = ctx.getResources().getConfiguration().fontScale;
-        float largeFactor = (constrain(fontScale, 1.0f, 1.3f) - 1f) / (1.3f - 1f);
-
-        // Linearly interpolate the padding between large and normal with the font scale ranging
-        // from 1f to LARGE_TEXT_SCALE
-        return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
-    }
-
-    public static float constrain(float amount, float low, float high) {
-        return amount < low ? low : (amount > high ? high : amount);
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
deleted file mode 100644
index b600d43..0000000
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
- */
-
-package android.support.v7.app;
-
-import android.annotation.TargetApi;
-import android.app.Notification;
-import android.support.annotation.RequiresApi;
-import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
-
-@RequiresApi(16)
-@TargetApi(16)
-class NotificationCompatImplJellybean {
-
-    public static void addBigTextStyle(NotificationBuilderWithBuilderAccessor b,
-            CharSequence bigText) {
-        Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle(b.getBuilder());
-        bigTextStyle.bigText(bigText);
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java b/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java
index ad23092..8454078 100644
--- a/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java
+++ b/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java
@@ -16,6 +16,7 @@
 
 package android.support.v7.app;
 
+import android.support.annotation.RequiresApi;
 import android.content.res.Resources;
 import android.os.Build;
 import android.support.annotation.NonNull;
@@ -41,17 +42,17 @@
     private static boolean sResourcesImplFieldFetched;
 
     static boolean flush(@NonNull final Resources resources) {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk >= 24) {
+        if (Build.VERSION.SDK_INT >= 24) {
             return flushNougats(resources);
-        } else if (sdk >= 23) {
+        } else if (Build.VERSION.SDK_INT >= 23) {
             return flushMarshmallows(resources);
-        } else if (sdk >= 21) {
+        } else if (Build.VERSION.SDK_INT >= 21) {
             return flushLollipops(resources);
         }
         return false;
     }
 
+    @RequiresApi(21)
     private static boolean flushLollipops(@NonNull final Resources resources) {
         if (!sDrawableCacheFieldFetched) {
             try {
@@ -77,6 +78,7 @@
         return false;
     }
 
+    @RequiresApi(23)
     private static boolean flushMarshmallows(@NonNull final Resources resources) {
         if (!sDrawableCacheFieldFetched) {
             try {
@@ -105,6 +107,7 @@
         return drawableCache != null && flushThemedResourcesCache(drawableCache);
     }
 
+    @RequiresApi(24)
     private static boolean flushNougats(@NonNull final Resources resources) {
         if (!sResourcesImplFieldFetched) {
             try {
@@ -155,6 +158,7 @@
         return drawableCache != null && flushThemedResourcesCache(drawableCache);
     }
 
+    @RequiresApi(16)
     private static boolean flushThemedResourcesCache(@NonNull final Object cache) {
         if (!sThemedResourceCacheClazzFetched) {
             try {
diff --git a/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java b/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java
index 8828458..79059c7 100644
--- a/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java
@@ -18,11 +18,9 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
 import android.support.v7.view.WindowCallbackWrapper;
 import android.support.v7.view.menu.ListMenuPresenter;
 import android.support.v7.view.menu.MenuBuilder;
@@ -30,8 +28,6 @@
 import android.support.v7.widget.DecorToolbar;
 import android.support.v7.widget.Toolbar;
 import android.support.v7.widget.ToolbarWidgetWrapper;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -261,7 +257,7 @@
     @Override
     public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
         final int currentOptions = mDecorToolbar.getDisplayOptions();
-        mDecorToolbar.setDisplayOptions(options & mask | currentOptions & ~mask);
+        mDecorToolbar.setDisplayOptions((options & mask) | (currentOptions & ~mask));
     }
 
     @Override
@@ -428,6 +424,11 @@
     }
 
     @Override
+    public boolean closeOptionsMenu() {
+        return mDecorToolbar.hideOverflowMenu();
+    }
+
+    @Override
     public boolean invalidateOptionsMenu() {
         mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator);
         ViewCompat.postOnAnimation(mDecorToolbar.getViewGroup(), mMenuInvalidator);
@@ -477,12 +478,9 @@
             final KeyCharacterMap kmap = KeyCharacterMap.load(
                     ev != null ? ev.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
             menu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
-            menu.performShortcut(keyCode, ev, 0);
+            return menu.performShortcut(keyCode, ev, 0);
         }
-        // This action bar always returns true for handling keyboard shortcuts.
-        // This will block the window from preparing a temporary panel to handle
-        // keyboard shortcuts.
-        return true;
+        return false;
     }
 
     @Override
@@ -491,14 +489,17 @@
         mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator);
     }
 
+    @Override
     public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
         mMenuVisibilityListeners.add(listener);
     }
 
+    @Override
     public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
         mMenuVisibilityListeners.remove(listener);
     }
 
+    @Override
     public void dispatchMenuVisibilityChanged(boolean isVisible) {
         if (isVisible == mLastMenuVisibility) {
             return;
@@ -511,52 +512,6 @@
         }
     }
 
-    View getListMenuView(Menu menu) {
-        ensureListMenuPresenter(menu);
-
-        if (menu == null || mListMenuPresenter == null) {
-            return null;
-        }
-
-        if (mListMenuPresenter.getAdapter().getCount() > 0) {
-            return (View) mListMenuPresenter.getMenuView(mDecorToolbar.getViewGroup());
-        }
-        return null;
-    }
-
-    private void ensureListMenuPresenter(Menu menu) {
-        if (mListMenuPresenter == null && (menu instanceof MenuBuilder)) {
-            MenuBuilder mb = (MenuBuilder) menu;
-
-            Context context = mDecorToolbar.getContext();
-            final TypedValue outValue = new TypedValue();
-            final Resources.Theme widgetTheme = context.getResources().newTheme();
-            widgetTheme.setTo(context.getTheme());
-
-            // First apply the actionBarPopupTheme
-            widgetTheme.resolveAttribute(R.attr.actionBarPopupTheme, outValue, true);
-            if (outValue.resourceId != 0) {
-                widgetTheme.applyStyle(outValue.resourceId, true);
-            }
-
-            // Apply the panelMenuListTheme
-            widgetTheme.resolveAttribute(R.attr.panelMenuListTheme, outValue, true);
-            if (outValue.resourceId != 0) {
-                widgetTheme.applyStyle(outValue.resourceId, true);
-            } else {
-                widgetTheme.applyStyle(R.style.Theme_AppCompat_CompactMenu, true);
-            }
-
-            context = new ContextThemeWrapper(context, 0);
-            context.getTheme().setTo(widgetTheme);
-
-            // Finally create the list menu presenter
-            mListMenuPresenter = new ListMenuPresenter(context, R.layout.abc_list_menu_item_layout);
-            mListMenuPresenter.setCallback(new PanelMenuPresenterCallback());
-            mb.addMenuPresenter(mListMenuPresenter);
-        }
-    }
-
     private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
         public ToolbarCallbackWrapper(Window.Callback wrapped) {
             super(wrapped);
@@ -574,13 +529,11 @@
 
         @Override
         public View onCreatePanelView(int featureId) {
-            switch (featureId) {
-                case Window.FEATURE_OPTIONS_PANEL:
-                    final Menu menu = mDecorToolbar.getMenu();
-                    if (onPreparePanel(featureId, null, menu) && onMenuOpened(featureId, menu)) {
-                        return getListMenuView(menu);
-                    }
-                    break;
+            if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+                // This gets called by PhoneWindow.preparePanel. Since this already manages
+                // its own panel, we return a dummy view here to prevent PhoneWindow from
+                // preparing a default one.
+                return new View(mDecorToolbar.getContext());
             }
             return super.onCreatePanelView(featureId);
         }
@@ -625,26 +578,6 @@
         }
     }
 
-    private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
-        PanelMenuPresenterCallback() {
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-            if (mWindowCallback != null) {
-                mWindowCallback.onPanelClosed(Window.FEATURE_OPTIONS_PANEL, menu);
-            }
-        }
-
-        @Override
-        public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            if (subMenu == null && mWindowCallback != null) {
-                mWindowCallback.onMenuOpened(Window.FEATURE_OPTIONS_PANEL, subMenu);
-            }
-            return true;
-        }
-    }
-
     private final class MenuBuilderCallback implements MenuBuilder.Callback {
 
         MenuBuilderCallback() {
diff --git a/v7/appcompat/src/android/support/v7/app/TwilightCalculator.java b/v7/appcompat/src/android/support/v7/app/TwilightCalculator.java
index f048e55..02fe5e8 100644
--- a/v7/appcompat/src/android/support/v7/app/TwilightCalculator.java
+++ b/v7/appcompat/src/android/support/v7/app/TwilightCalculator.java
@@ -46,6 +46,7 @@
     private static final float J0 = 0.0009f;
 
     // correction for civil twilight
+    @SuppressWarnings("FloatingPointLiteralPrecision")
     private static final float ALTIDUTE_CORRECTION_CIVIL_TWILIGHT = -0.104719755f;
 
     // coefficients for calculating Equation of Center.
@@ -53,6 +54,7 @@
     private static final float C2 = 0.000349066f;
     private static final float C3 = 0.000005236f;
 
+    @SuppressWarnings("FloatingPointLiteralPrecision")
     private static final float OBLIQUITY = 0.40927971f;
 
     // Java time on Jan 1, 2000 12:00 UTC.
@@ -82,6 +84,7 @@
      * @param latitude latitude in degrees.
      * @param longitude latitude in degrees.
      */
+    @SuppressWarnings("FloatingPointLiteralPrecision")
     public void calculateTwilight(long time, double latitude, double longitude) {
         final float daysSince2000 = (float) (time - UTC_2000) / DateUtils.DAY_IN_MILLIS;
 
diff --git a/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java b/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java
index 2f7eba3..1c17922 100644
--- a/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java
@@ -25,7 +25,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentTransaction;
@@ -51,6 +50,8 @@
 import android.support.v7.widget.Toolbar;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -82,11 +83,6 @@
     private static final Interpolator sHideInterpolator = new AccelerateInterpolator();
     private static final Interpolator sShowInterpolator = new DecelerateInterpolator();
 
-    /**
-     * Only allow show/hide animations on ICS+, as that is what ViewPropertyAnimatorCompat supports
-     */
-    private static final boolean ALLOW_SHOW_HIDE_ANIMATIONS = Build.VERSION.SDK_INT >= 14;
-
     Context mContext;
     private Context mThemedContext;
     private Activity mActivity;
@@ -139,8 +135,8 @@
         @Override
         public void onAnimationEnd(View view) {
             if (mContentAnimations && mContentView != null) {
-                ViewCompat.setTranslationY(mContentView, 0f);
-                ViewCompat.setTranslationY(mContainerView, 0f);
+                mContentView.setTranslationY(0f);
+                mContainerView.setTranslationY(0f);
             }
             mContainerView.setVisibility(View.GONE);
             mContainerView.setTransitioning(false);
@@ -257,6 +253,7 @@
         return ViewCompat.getElevation(mContainerView);
     }
 
+    @Override
     public void onConfigurationChanged(Configuration newConfig) {
         setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs());
     }
@@ -318,6 +315,7 @@
         }
     }
 
+    @Override
     public void onWindowVisibilityChanged(int visibility) {
         mCurWindowVisibility = visibility;
     }
@@ -329,6 +327,7 @@
      *
      * @param enabled true to animate, false to not animate.
      */
+    @Override
     public void setShowHideAnimationEnabled(boolean enabled) {
         mShowHideAnimationEnabled = enabled;
         if (!enabled && mCurrentShowAnim != null) {
@@ -336,14 +335,17 @@
         }
     }
 
+    @Override
     public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
         mMenuVisibilityListeners.add(listener);
     }
 
+    @Override
     public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
         mMenuVisibilityListeners.remove(listener);
     }
 
+    @Override
     public void dispatchMenuVisibilityChanged(boolean isVisible) {
         if (isVisible == mLastMenuVisibility) {
             return;
@@ -402,6 +404,7 @@
         setSubtitle(mContext.getString(resId));
     }
 
+    @Override
     public void setSelectedNavigationItem(int position) {
         switch (mDecorToolbar.getNavigationMode()) {
             case NAVIGATION_MODE_TABS:
@@ -416,6 +419,7 @@
         }
     }
 
+    @Override
     public void removeAllTabs() {
         cleanupTabs();
     }
@@ -431,6 +435,7 @@
         mSavedTabPosition = INVALID_POSITION;
     }
 
+    @Override
     public void setTitle(CharSequence title) {
         mDecorToolbar.setTitle(title);
     }
@@ -450,10 +455,12 @@
         return false;
     }
 
+    @Override
     public void setSubtitle(CharSequence subtitle) {
         mDecorToolbar.setSubtitle(subtitle);
     }
 
+    @Override
     public void setDisplayOptions(int options) {
         if ((options & DISPLAY_HOME_AS_UP) != 0) {
             mDisplayHomeAsUpSet = true;
@@ -461,6 +468,7 @@
         mDecorToolbar.setDisplayOptions(options);
     }
 
+    @Override
     public void setDisplayOptions(int options, int mask) {
         final int current = mDecorToolbar.getDisplayOptions();
         if ((mask & DISPLAY_HOME_AS_UP) != 0) {
@@ -469,38 +477,47 @@
         mDecorToolbar.setDisplayOptions((options & mask) | (current & ~mask));
     }
 
+    @Override
     public void setBackgroundDrawable(Drawable d) {
         mContainerView.setPrimaryBackground(d);
     }
 
+    @Override
     public void setStackedBackgroundDrawable(Drawable d) {
         mContainerView.setStackedBackground(d);
     }
 
+    @Override
     public void setSplitBackgroundDrawable(Drawable d) {
         // no-op. We don't support split action bars
     }
 
+    @Override
     public View getCustomView() {
         return mDecorToolbar.getCustomView();
     }
 
+    @Override
     public CharSequence getTitle() {
         return mDecorToolbar.getTitle();
     }
 
+    @Override
     public CharSequence getSubtitle() {
         return mDecorToolbar.getSubtitle();
     }
 
+    @Override
     public int getNavigationMode() {
         return mDecorToolbar.getNavigationMode();
     }
 
+    @Override
     public int getDisplayOptions() {
         return mDecorToolbar.getDisplayOptions();
     }
 
+    @Override
     public ActionMode startActionMode(ActionMode.Callback callback) {
         if (mActionMode != null) {
             mActionMode.finish();
@@ -651,6 +668,7 @@
         return mContainerView.getHeight();
     }
 
+    @Override
     public void enableContentAnimations(boolean enabled) {
         mContentAnimations = enabled;
     }
@@ -673,6 +691,7 @@
         }
     }
 
+    @Override
     public void showForSystem() {
         if (mHiddenBySystem) {
             mHiddenBySystem = false;
@@ -698,6 +717,7 @@
         }
     }
 
+    @Override
     public void hideForSystem() {
         if (!mHiddenBySystem) {
             mHiddenBySystem = true;
@@ -769,23 +789,22 @@
         }
         mContainerView.setVisibility(View.VISIBLE);
 
-        if (mCurWindowVisibility == View.VISIBLE && ALLOW_SHOW_HIDE_ANIMATIONS &&
-                (mShowHideAnimationEnabled || fromSystem)) {
+        if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled || fromSystem)) {
             // because we're about to ask its window loc
-            ViewCompat.setTranslationY(mContainerView, 0f);
+            mContainerView.setTranslationY(0f);
             float startingY = -mContainerView.getHeight();
             if (fromSystem) {
                 int topLeft[] = {0, 0};
                 mContainerView.getLocationInWindow(topLeft);
                 startingY -= topLeft[1];
             }
-            ViewCompat.setTranslationY(mContainerView, startingY);
+            mContainerView.setTranslationY(startingY);
             ViewPropertyAnimatorCompatSet anim = new ViewPropertyAnimatorCompatSet();
             ViewPropertyAnimatorCompat a = ViewCompat.animate(mContainerView).translationY(0f);
             a.setUpdateListener(mUpdateListener);
             anim.play(a);
             if (mContentAnimations && mContentView != null) {
-                ViewCompat.setTranslationY(mContentView, startingY);
+                mContentView.setTranslationY(startingY);
                 anim.play(ViewCompat.animate(mContentView).translationY(0f));
             }
             anim.setInterpolator(sShowInterpolator);
@@ -801,10 +820,10 @@
             mCurrentShowAnim = anim;
             anim.start();
         } else {
-            ViewCompat.setAlpha(mContainerView, 1f);
-            ViewCompat.setTranslationY(mContainerView, 0);
+            mContainerView.setAlpha(1f);
+            mContainerView.setTranslationY(0);
             if (mContentAnimations && mContentView != null) {
-                ViewCompat.setTranslationY(mContentView, 0);
+                mContentView.setTranslationY(0);
             }
             mShowListener.onAnimationEnd(null);
         }
@@ -818,9 +837,8 @@
             mCurrentShowAnim.cancel();
         }
 
-        if (mCurWindowVisibility == View.VISIBLE && ALLOW_SHOW_HIDE_ANIMATIONS &&
-                (mShowHideAnimationEnabled || fromSystem)) {
-            ViewCompat.setAlpha(mContainerView, 1f);
+        if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled || fromSystem)) {
+            mContainerView.setAlpha(1f);
             mContainerView.setTransitioning(true);
             ViewPropertyAnimatorCompatSet anim = new ViewPropertyAnimatorCompatSet();
             float endingY = -mContainerView.getHeight();
@@ -845,6 +863,7 @@
         }
     }
 
+    @Override
     public boolean isShowing() {
         final int height = getHeight();
         // Take into account the case where the bar has a 0 height due to not being measured yet.
@@ -896,6 +915,7 @@
         return ViewCompat.isLaidOut(mContainerView);
     }
 
+    @Override
     public Context getThemedContext() {
         if (mThemedContext == null) {
             TypedValue outValue = new TypedValue();
@@ -1096,6 +1116,7 @@
             return mCustomView != null ? mCustomView.get() : null;
         }
 
+        @Override
         public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
             if (mCallback != null) {
                 return mCallback.onActionItemClicked(this, item);
@@ -1123,6 +1144,7 @@
         public void onCloseSubMenu(SubMenuBuilder menu) {
         }
 
+        @Override
         public void onMenuModeChange(MenuBuilder menu) {
             if (mCallback == null) {
                 return;
@@ -1367,9 +1389,25 @@
         return mDecorToolbar.hasLogo();
     }
 
+    @Override
     public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
         if (!mDisplayHomeAsUpSet) {
             setDisplayHomeAsUpEnabled(enable);
         }
     }
+
+    @Override
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        if (mActionMode == null) {
+            return false;
+        }
+        Menu menu = mActionMode.getMenu();
+        if (menu != null) {
+            final KeyCharacterMap kmap = KeyCharacterMap.load(
+                    event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
+            menu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
+            return menu.performShortcut(keyCode, event, 0);
+        }
+        return false;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
index 9fa45b8..b2406ac 100644
--- a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
+++ b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
@@ -101,6 +101,7 @@
         return mDrawable.getState();
     }
 
+    @Override
     public void jumpToCurrentState() {
         DrawableCompat.jumpToCurrentState(mDrawable);
     }
@@ -153,6 +154,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void invalidateDrawable(Drawable who) {
         invalidateSelf();
     }
@@ -160,6 +162,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void scheduleDrawable(Drawable who, Runnable what, long when) {
         scheduleSelf(what, when);
     }
@@ -167,6 +170,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void unscheduleDrawable(Drawable who, Runnable what) {
         unscheduleSelf(what);
     }
diff --git a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
index 5f4cf95..5142224 100644
--- a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
+++ b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
@@ -386,7 +386,7 @@
         final float barThickness = mPaint.getStrokeWidth();
         final int remainingSpace = (int) (bounds.height() - barThickness * 3 - mBarGap * 2);
         float yOffset = (remainingSpace / 4) * 2; // making sure it is a multiple of 2.
-        yOffset += barThickness * 1.5 + mBarGap;
+        yOffset += barThickness * 1.5f + mBarGap;
 
         canvas.translate(bounds.centerX(), yOffset);
         if (mSpin) {
diff --git a/v7/appcompat/src/android/support/v7/transition/ActionBarTransition.java b/v7/appcompat/src/android/support/v7/transition/ActionBarTransition.java
deleted file mode 100644
index 4deaeff..0000000
--- a/v7/appcompat/src/android/support/v7/transition/ActionBarTransition.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v7.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.ViewGroup;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionBarTransition {
-
-    private static final boolean TRANSITIONS_ENABLED = false;
-
-    private static final int TRANSITION_DURATION = 120; // ms
-
-//    private static final Transition sTransition;
-//
-//    static {
-//        if (TRANSITIONS_ENABLED) {
-////            final ChangeText tc = new ChangeText();
-////            tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN);
-//            final TransitionSet inner = new TransitionSet();
-//            inner.addTransition(new ChangeBounds());
-//            final TransitionSet tg = new TransitionSet();
-//            tg.addTransition(new Fade(Fade.OUT)).addTransition(inner).
-//                    addTransition(new Fade(Fade.IN));
-//            tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
-//            tg.setDuration(TRANSITION_DURATION);
-//            sTransition = tg;
-//        } else {
-//            sTransition = null;
-//        }
-//    }
-
-    public static void beginDelayedTransition(ViewGroup sceneRoot) {
-//        if (TRANSITIONS_ENABLED) {
-//            TransitionManager.beginDelayedTransition(sceneRoot, sTransition);
-//        }
-    }
-}
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java b/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
index 7cd7d24..d9135d2 100644
--- a/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
+++ b/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
@@ -19,12 +19,11 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
 import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.ConfigurationHelper;
-import android.support.v4.view.ViewConfigurationCompat;
 import android.support.v7.appcompat.R;
 import android.view.ViewConfiguration;
 
@@ -53,10 +52,10 @@
      * "always" items can override this.
      */
     public int getMaxActionButtons() {
-        final Resources res = mContext.getResources();
-        final int widthDp = ConfigurationHelper.getScreenWidthDp(res);
-        final int heightDp = ConfigurationHelper.getScreenHeightDp(res);
-        final int smallest = ConfigurationHelper.getSmallestScreenWidthDp(res);
+        final Configuration configuration = mContext.getResources().getConfiguration();
+        final int widthDp = configuration.screenWidthDp;
+        final int heightDp = configuration.screenHeightDp;
+        final int smallest = configuration.smallestScreenWidthDp;
 
         if (smallest > 600 || widthDp > 600 || (widthDp > 960 && heightDp > 720)
                 || (widthDp > 720 && heightDp > 960)) {
@@ -78,7 +77,7 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             return true;
         } else {
-            return !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext));
+            return !ViewConfiguration.get(mContext).hasPermanentMenuKey();
         }
     }
 
diff --git a/v7/appcompat/src/android/support/v7/view/CollapsibleActionView.java b/v7/appcompat/src/android/support/v7/view/CollapsibleActionView.java
index 3f01c5c..e84eaac 100644
--- a/v7/appcompat/src/android/support/v7/view/CollapsibleActionView.java
+++ b/v7/appcompat/src/android/support/v7/view/CollapsibleActionView.java
@@ -16,12 +16,10 @@
 
 package android.support.v7.view;
 
-import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
-
 /**
  * When a {@link android.view.View} implements this interface it will receive callbacks when expanded or
  * collapsed as an action view alongside the optional, app-specified callbacks to {@link
- * OnActionExpandListener}.
+ * android.support.v4.view.MenuItemCompat.OnActionExpandListener}.
  *
  * <p>See {@link android.support.v4.view.MenuItemCompat} for more information about action views.
  * See {@link android.app.ActionBar} for more information about the action bar.
@@ -29,14 +27,14 @@
 public interface CollapsibleActionView {
 
     /**
-     * Called when this view is expanded as an action view. See {@link
-     * android.support.v4.view.MenuItemCompat#expandActionView(android.view.MenuItem)}.
+     * Called when this view is expanded as an action view. See
+     * {@link android.view.MenuItem#expandActionView()}.
      */
-    public void onActionViewExpanded();
+    void onActionViewExpanded();
 
     /**
-     * Called when this view is collapsed as an action view. See {@link
-     * android.support.v4.view.MenuItemCompat#collapseActionView(android.view.MenuItem)}.
+     * Called when this view is collapsed as an action view. See
+     * {@link android.view.MenuItem#collapseActionView()}.
      */
-    public void onActionViewCollapsed();
+    void onActionViewCollapsed();
 }
diff --git a/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java b/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
index 5069812..aa5b36e 100644
--- a/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
@@ -45,8 +45,8 @@
 
     /**
      * Creates a new context wrapper with no theme and no base context.
-     * <p>
-     * <stong>Note:</strong> A base context <strong>must</strong> be attached
+     * <p class="note">
+     * <strong>Note:</strong> A base context <strong>must</strong> be attached
      * using {@link #attachBaseContext(Context)} before calling any other
      * method on the newly constructed context wrapper.
      */
diff --git a/v7/appcompat/src/android/support/v7/view/StandaloneActionMode.java b/v7/appcompat/src/android/support/v7/view/StandaloneActionMode.java
index d1bb05b..bfe98d0 100644
--- a/v7/appcompat/src/android/support/v7/view/StandaloneActionMode.java
+++ b/v7/appcompat/src/android/support/v7/view/StandaloneActionMode.java
@@ -19,7 +19,6 @@
 
 import android.content.Context;
 import android.support.annotation.RestrictTo;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.view.menu.MenuBuilder;
 import android.support.v7.view.menu.MenuPopupHelper;
 import android.support.v7.view.menu.SubMenuBuilder;
@@ -53,7 +52,7 @@
         mCallback = callback;
 
         mMenu = new MenuBuilder(view.getContext()).setDefaultShowAsAction(
-                MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+                MenuItem.SHOW_AS_ACTION_IF_ROOM);
         mMenu.setCallback(this);
         mFocusable = isFocusable;
     }
@@ -136,6 +135,7 @@
         return new SupportMenuInflater(mContextView.getContext());
     }
 
+    @Override
     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
         return mCallback.onActionItemClicked(this, item);
     }
@@ -155,11 +155,13 @@
     public void onCloseSubMenu(SubMenuBuilder menu) {
     }
 
+    @Override
     public void onMenuModeChange(MenuBuilder menu) {
         invalidate();
         mContextView.showOverflowMenu();
     }
 
+    @Override
     public boolean isUiFocusable() {
         return mFocusable;
     }
diff --git a/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java b/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java
index ff929ba..fa7fe1b 100644
--- a/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java
@@ -18,9 +18,7 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
-import android.os.Build;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportMenu;
 import android.support.v4.internal.view.SupportMenuItem;
@@ -40,7 +38,6 @@
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP)
-@TargetApi(Build.VERSION_CODES.HONEYCOMB)
 public class SupportActionModeWrapper extends ActionMode {
 
     final Context mContext;
diff --git a/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java b/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java
index 4f71d02..0cc5722 100644
--- a/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java
+++ b/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java
@@ -21,8 +21,10 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.graphics.PorterDuff;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportMenu;
 import android.support.v4.view.ActionProvider;
@@ -30,10 +32,12 @@
 import android.support.v7.appcompat.R;
 import android.support.v7.view.menu.MenuItemImpl;
 import android.support.v7.view.menu.MenuItemWrapperICS;
+import android.support.v7.widget.DrawableUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 import android.view.InflateException;
+import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -251,6 +255,7 @@
             }
         }
 
+        @Override
         public boolean onMenuItemClick(MenuItem item) {
             try {
                 if (mMethod.getReturnType() == Boolean.TYPE) {
@@ -292,7 +297,9 @@
         private CharSequence itemTitleCondensed;
         private int itemIconResId;
         private char itemAlphabeticShortcut;
+        private int itemAlphabeticModifiers;
         private char itemNumericShortcut;
+        private int itemNumericModifiers;
         /**
          * Sync to attrs.xml enum:
          * - 0: none
@@ -321,6 +328,12 @@
 
         ActionProvider itemActionProvider;
 
+        private CharSequence itemContentDescription;
+        private CharSequence itemTooltipText;
+
+        private ColorStateList itemIconTintList = null;
+        private PorterDuff.Mode itemIconTintMode = null;
+
         private static final int defaultGroupId = NO_ID;
         private static final int defaultItemId = NO_ID;
         private static final int defaultItemCategory = 0;
@@ -380,8 +393,12 @@
             itemIconResId = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
             itemAlphabeticShortcut =
                     getShortcut(a.getString(R.styleable.MenuItem_android_alphabeticShortcut));
+            itemAlphabeticModifiers =
+                    a.getInt(R.styleable.MenuItem_alphabeticModifiers, KeyEvent.META_CTRL_ON);
             itemNumericShortcut =
                     getShortcut(a.getString(R.styleable.MenuItem_android_numericShortcut));
+            itemNumericModifiers =
+                    a.getInt(R.styleable.MenuItem_numericModifiers, KeyEvent.META_CTRL_ON);
             if (a.hasValue(R.styleable.MenuItem_android_checkable)) {
                 // Item has attribute checkable, use it
                 itemCheckable = a.getBoolean(R.styleable.MenuItem_android_checkable, false) ? 1 : 0;
@@ -412,6 +429,23 @@
                 itemActionProvider = null;
             }
 
+            itemContentDescription = a.getText(R.styleable.MenuItem_contentDescription);
+            itemTooltipText = a.getText(R.styleable.MenuItem_tooltipText);
+            if (a.hasValue(R.styleable.MenuItem_iconTintMode)) {
+                itemIconTintMode = DrawableUtils.parseTintMode(a.getInt(
+                        R.styleable.MenuItem_iconTintMode, -1),
+                        itemIconTintMode);
+            } else {
+                // Reset to null so that it's not carried over to the next item
+                itemIconTintMode = null;
+            }
+            if (a.hasValue(R.styleable.MenuItem_iconTint)) {
+                itemIconTintList = a.getColorStateList(R.styleable.MenuItem_iconTint);
+            } else {
+                // Reset to null so that it's not carried over to the next item
+                itemIconTintList = null;
+            }
+
             a.recycle();
 
             itemAdded = false;
@@ -431,12 +465,10 @@
                     .setEnabled(itemEnabled)
                     .setCheckable(itemCheckable >= 1)
                     .setTitleCondensed(itemTitleCondensed)
-                    .setIcon(itemIconResId)
-                    .setAlphabeticShortcut(itemAlphabeticShortcut)
-                    .setNumericShortcut(itemNumericShortcut);
+                    .setIcon(itemIconResId);
 
             if (itemShowAsAction >= 0) {
-                MenuItemCompat.setShowAsAction(item, itemShowAsAction);
+                item.setShowAsAction(itemShowAsAction);
             }
 
             if (itemListenerMethodName != null) {
@@ -461,12 +493,12 @@
             if (itemActionViewClassName != null) {
                 View actionView = (View) newInstance(itemActionViewClassName,
                         ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
-                MenuItemCompat.setActionView(item, actionView);
+                item.setActionView(actionView);
                 actionViewSpecified = true;
             }
             if (itemActionViewLayout > 0) {
                 if (!actionViewSpecified) {
-                    MenuItemCompat.setActionView(item, itemActionViewLayout);
+                    item.setActionView(itemActionViewLayout);
                     actionViewSpecified = true;
                 } else {
                     Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
@@ -476,6 +508,19 @@
             if (itemActionProvider != null) {
                 MenuItemCompat.setActionProvider(item, itemActionProvider);
             }
+
+            MenuItemCompat.setContentDescription(item, itemContentDescription);
+            MenuItemCompat.setTooltipText(item, itemTooltipText);
+            MenuItemCompat.setAlphabeticShortcut(item, itemAlphabeticShortcut,
+                    itemAlphabeticModifiers);
+            MenuItemCompat.setNumericShortcut(item, itemNumericShortcut, itemNumericModifiers);
+
+            if (itemIconTintMode != null) {
+                MenuItemCompat.setIconTintMode(item, itemIconTintMode);
+            }
+            if (itemIconTintList != null) {
+                MenuItemCompat.setIconTintList(item, itemIconTintList);
+            }
         }
 
         public void addItem() {
@@ -494,7 +539,7 @@
             return itemAdded;
         }
 
-        @SuppressWarnings("unchecked")
+        @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
         private <T> T newInstance(String className, Class<?>[] constructorSignature,
                 Object[] arguments) {
             try {
diff --git a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
index 4fbdd50..e25a663 100644
--- a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.view.ActionMode;
@@ -59,8 +58,6 @@
         return mWrapped.dispatchKeyEvent(event);
     }
 
-    @RequiresApi(11)
-    @TargetApi(11)
     @Override
     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
         return mWrapped.dispatchKeyShortcutEvent(event);
@@ -76,8 +73,6 @@
         return mWrapped.dispatchTrackballEvent(event);
     }
 
-    @RequiresApi(12)
-    @TargetApi(12)
     @Override
     public boolean dispatchGenericMotionEvent(MotionEvent event) {
         return mWrapped.dispatchGenericMotionEvent(event);
@@ -144,7 +139,6 @@
     }
 
     @RequiresApi(23)
-    @TargetApi(23)
     @Override
     public boolean onSearchRequested(SearchEvent searchEvent) {
         return mWrapped.onSearchRequested(searchEvent);
@@ -155,39 +149,37 @@
         return mWrapped.onSearchRequested();
     }
 
-    @RequiresApi(11)
-    @TargetApi(11)
     @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
         return mWrapped.onWindowStartingActionMode(callback);
     }
 
     @RequiresApi(23)
-    @TargetApi(23)
     @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
         return mWrapped.onWindowStartingActionMode(callback, type);
     }
 
-    @RequiresApi(11)
-    @TargetApi(11)
     @Override
     public void onActionModeStarted(ActionMode mode) {
         mWrapped.onActionModeStarted(mode);
     }
 
-    @RequiresApi(11)
-    @TargetApi(11)
     @Override
     public void onActionModeFinished(ActionMode mode) {
         mWrapped.onActionModeFinished(mode);
     }
 
     @RequiresApi(24)
-    @TargetApi(24)
     @Override
     public void onProvideKeyboardShortcuts(
             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
         mWrapped.onProvideKeyboardShortcuts(data, menu, deviceId);
     }
+
+    @RequiresApi(26)
+    @Override
+    public void onPointerCaptureChanged(boolean hasCapture) {
+        mWrapped.onPointerCaptureChanged(hasCapture);
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java
index 2d13a56..1074202 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java
@@ -20,13 +20,17 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
-import android.support.v4.view.MenuItemCompat;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
@@ -46,7 +50,9 @@
     private CharSequence mTitleCondensed;
     private Intent mIntent;
     private char mShortcutNumericChar;
+    private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON;
     private char mShortcutAlphabeticChar;
+    private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON;
 
     private Drawable mIconDrawable;
     private int mIconResId = NO_ICON;
@@ -55,6 +61,14 @@
 
     private SupportMenuItem.OnMenuItemClickListener mClickListener;
 
+    private CharSequence mContentDescription;
+    private CharSequence mTooltipText;
+
+    private ColorStateList mIconTintList = null;
+    private PorterDuff.Mode mIconTintMode = null;
+    private boolean mHasIconTint = false;
+    private boolean mHasIconTintMode = false;
+
     private static final int NO_ICON = 0;
 
     private int mFlags = ENABLED;
@@ -74,75 +88,110 @@
         mTitle = title;
     }
 
+    @Override
     public char getAlphabeticShortcut() {
         return mShortcutAlphabeticChar;
     }
 
+    @Override
+    public int getAlphabeticModifiers() {
+        return mShortcutAlphabeticModifiers;
+    }
+
+    @Override
     public int getGroupId() {
         return mGroup;
     }
 
+    @Override
     public Drawable getIcon() {
         return mIconDrawable;
     }
 
+    @Override
     public Intent getIntent() {
         return mIntent;
     }
 
+    @Override
     public int getItemId() {
         return mId;
     }
 
+    @Override
     public ContextMenuInfo getMenuInfo() {
         return null;
     }
 
+    @Override
     public char getNumericShortcut() {
         return mShortcutNumericChar;
     }
 
+    @Override
+    public int getNumericModifiers() {
+        return mShortcutNumericModifiers;
+    }
+
+    @Override
     public int getOrder() {
         return mOrdering;
     }
 
+    @Override
     public SubMenu getSubMenu() {
         return null;
     }
 
+    @Override
     public CharSequence getTitle() {
         return mTitle;
     }
 
+    @Override
     public CharSequence getTitleCondensed() {
         return mTitleCondensed != null ? mTitleCondensed : mTitle;
     }
 
+    @Override
     public boolean hasSubMenu() {
         return false;
     }
 
+    @Override
     public boolean isCheckable() {
         return (mFlags & CHECKABLE) != 0;
     }
 
+    @Override
     public boolean isChecked() {
         return (mFlags & CHECKED) != 0;
     }
 
+    @Override
     public boolean isEnabled() {
         return (mFlags & ENABLED) != 0;
     }
 
+    @Override
     public boolean isVisible() {
         return (mFlags & HIDDEN) == 0;
     }
 
+    @Override
     public MenuItem setAlphabeticShortcut(char alphaChar) {
-        mShortcutAlphabeticChar = alphaChar;
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
         return this;
     }
 
+    @Override
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setCheckable(boolean checkable) {
         mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
         return this;
@@ -153,64 +202,97 @@
         return this;
     }
 
+    @Override
     public MenuItem setChecked(boolean checked) {
         mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
         return this;
     }
 
+    @Override
     public MenuItem setEnabled(boolean enabled) {
         mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
         return this;
     }
 
+    @Override
     public MenuItem setIcon(Drawable icon) {
         mIconDrawable = icon;
         mIconResId = NO_ICON;
+
+        applyIconTint();
         return this;
     }
 
+    @Override
     public MenuItem setIcon(int iconRes) {
         mIconResId = iconRes;
         mIconDrawable = ContextCompat.getDrawable(mContext, iconRes);
+
+        applyIconTint();
         return this;
     }
 
+    @Override
     public MenuItem setIntent(Intent intent) {
         mIntent = intent;
         return this;
     }
 
+    @Override
     public MenuItem setNumericShortcut(char numericChar) {
         mShortcutNumericChar = numericChar;
         return this;
     }
 
+    @Override
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
         mClickListener = menuItemClickListener;
         return this;
     }
 
+    @Override
     public MenuItem setShortcut(char numericChar, char alphaChar) {
         mShortcutNumericChar = numericChar;
-        mShortcutAlphabeticChar = alphaChar;
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
         return this;
     }
 
+    @Override
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers) {
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setTitle(CharSequence title) {
         mTitle = title;
         return this;
     }
 
+    @Override
     public MenuItem setTitle(int title) {
         mTitle = mContext.getResources().getString(title);
         return this;
     }
 
+    @Override
     public MenuItem setTitleCondensed(CharSequence title) {
         mTitleCondensed = title;
         return this;
     }
 
+    @Override
     public MenuItem setVisible(boolean visible) {
         mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
         return this;
@@ -229,14 +311,17 @@
         return false;
     }
 
+    @Override
     public void setShowAsAction(int show) {
         // Do nothing. ActionMenuItems always show as action buttons.
     }
 
+    @Override
     public SupportMenuItem setActionView(View actionView) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public View getActionView() {
         return null;
     }
@@ -293,8 +378,69 @@
     }
 
     @Override
-    public SupportMenuItem setSupportOnActionExpandListener(MenuItemCompat.OnActionExpandListener listener) {
-        // No need to save the listener; ActionMenuItem does not support collapsing items.
+    public SupportMenuItem setContentDescription(CharSequence contentDescription) {
+        mContentDescription = contentDescription;
         return this;
     }
+
+    @Override
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    @Override
+    public SupportMenuItem setTooltipText(CharSequence tooltipText) {
+        mTooltipText = tooltipText;
+        return this;
+    }
+
+    @Override
+    public CharSequence getTooltipText() {
+        return mTooltipText;
+    }
+
+    @Override
+    public MenuItem setIconTintList(@Nullable ColorStateList iconTintList) {
+        mIconTintList = iconTintList;
+        mHasIconTint = true;
+
+        applyIconTint();
+
+        return this;
+    }
+
+    @Override
+    public ColorStateList getIconTintList() {
+        return mIconTintList;
+    }
+
+    @Override
+    public MenuItem setIconTintMode(PorterDuff.Mode iconTintMode) {
+        mIconTintMode = iconTintMode;
+        mHasIconTintMode = true;
+
+        applyIconTint();
+
+        return this;
+    }
+
+    @Override
+    public PorterDuff.Mode getIconTintMode() {
+        return mIconTintMode;
+    }
+
+    private void applyIconTint() {
+        if (mIconDrawable != null && (mHasIconTint || mHasIconTintMode)) {
+            mIconDrawable = DrawableCompat.wrap(mIconDrawable);
+            mIconDrawable = mIconDrawable.mutate();
+
+            if (mHasIconTint) {
+                DrawableCompat.setTintList(mIconDrawable, mIconTintList);
+            }
+
+            if (mHasIconTintMode) {
+                DrawableCompat.setTintMode(mIconDrawable, mIconTintMode);
+            }
+        }
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
index 77f5f17..cc20211 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
@@ -22,31 +22,25 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
 import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.ConfigurationHelper;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.widget.ActionMenuView;
 import android.support.v7.widget.AppCompatTextView;
 import android.support.v7.widget.ForwardingListener;
+import android.support.v7.widget.TooltipCompat;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.Toast;
 
 /**
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP)
 public class ActionMenuItemView extends AppCompatTextView
-        implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
-        ActionMenuView.ActionMenuChildView {
+        implements MenuView.ItemView, View.OnClickListener, ActionMenuView.ActionMenuChildView {
 
     private static final String TAG = "ActionMenuItemView";
 
@@ -87,12 +81,12 @@
         mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f);
 
         setOnClickListener(this);
-        setOnLongClickListener(this);
 
         mSavedPaddingLeft = -1;
         setSaveEnabled(false);
     }
 
+    @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
@@ -106,8 +100,8 @@
      */
     private boolean shouldAllowTextWithIcon() {
         final Configuration config = getContext().getResources().getConfiguration();
-        final int widthDp = ConfigurationHelper.getScreenWidthDp(getResources());
-        final int heightDp = ConfigurationHelper.getScreenHeightDp(getResources());
+        final int widthDp = config.screenWidthDp;
+        final int heightDp = config.screenHeightDp;
 
         return widthDp >= 480 || (widthDp >= 640 && heightDp >= 480)
                 || config.orientation == Configuration.ORIENTATION_LANDSCAPE;
@@ -119,10 +113,12 @@
         super.setPadding(l, t, r, b);
     }
 
+    @Override
     public MenuItemImpl getItemData() {
         return mItemData;
     }
 
+    @Override
     public void initialize(MenuItemImpl itemData, int menuType) {
         mItemData = itemData;
 
@@ -163,14 +159,17 @@
         mPopupCallback = popupCallback;
     }
 
+    @Override
     public boolean prefersCondensedTitle() {
         return true;
     }
 
+    @Override
     public void setCheckable(boolean checkable) {
         // TODO Support checkable action items
     }
 
+    @Override
     public void setChecked(boolean checked) {
         // TODO Support checkable action items
     }
@@ -190,8 +189,27 @@
                 (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
 
         setText(visible ? mTitle : null);
+
+        // Show the tooltip for items that do not already show text.
+        final CharSequence contentDescription = mItemData.getContentDescription();
+        if (TextUtils.isEmpty(contentDescription)) {
+            // Use the uncondensed title for content description, but only if the title is not
+            // shown already.
+            setContentDescription(visible ? null : mItemData.getTitle());
+        } else {
+            setContentDescription(contentDescription);
+        }
+
+        final CharSequence tooltipText = mItemData.getTooltipText();
+        if (TextUtils.isEmpty(tooltipText)) {
+            // Use the uncondensed title for tooltip, but only if the title is not shown already.
+            TooltipCompat.setTooltipText(this, visible ? null : mItemData.getTitle());
+        } else {
+            TooltipCompat.setTooltipText(this, tooltipText);
+        }
     }
 
+    @Override
     public void setIcon(Drawable icon) {
         mIcon = icon;
         if (icon != null) {
@@ -200,12 +218,12 @@
             if (width > mMaxIconSize) {
                 final float scale = (float) mMaxIconSize / width;
                 width = mMaxIconSize;
-                height *= scale;
+                height = (int) (width * scale);
             }
             if (height > mMaxIconSize) {
                 final float scale = (float) mMaxIconSize / height;
                 height = mMaxIconSize;
-                width *= scale;
+                width = (int) (width * scale);
             }
             icon.setBounds(0, 0, width, height);
         }
@@ -218,64 +236,34 @@
         return !TextUtils.isEmpty(getText());
     }
 
+    @Override
     public void setShortcut(boolean showShortcut, char shortcutKey) {
         // Action buttons don't show text for shortcut keys.
     }
 
+    @Override
     public void setTitle(CharSequence title) {
         mTitle = title;
 
-        setContentDescription(mTitle);
         updateTextButtonVisibility();
     }
 
+    @Override
     public boolean showsIcon() {
         return true;
     }
 
+    @Override
     public boolean needsDividerBefore() {
         return hasText() && mItemData.getIcon() == null;
     }
 
+    @Override
     public boolean needsDividerAfter() {
         return hasText();
     }
 
     @Override
-    public boolean onLongClick(View v) {
-        if (hasText()) {
-            // Don't show the cheat sheet for items that already show text.
-            return false;
-        }
-
-        final int[] screenPos = new int[2];
-        final Rect displayFrame = new Rect();
-        getLocationOnScreen(screenPos);
-        getWindowVisibleDisplayFrame(displayFrame);
-
-        final Context context = getContext();
-        final int width = getWidth();
-        final int height = getHeight();
-        final int midy = screenPos[1] + height / 2;
-        int referenceX = screenPos[0] + width / 2;
-        if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
-            final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-            referenceX = screenWidth - referenceX; // mirror
-        }
-        Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
-        if (midy < displayFrame.height()) {
-            // Show along the top; follow action buttons
-            cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX,
-                    screenPos[1] + height - displayFrame.top);
-        } else {
-            // Show along the bottom center
-            cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
-        }
-        cheatSheet.show();
-        return true;
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final boolean textVisible = hasText();
         if (textVisible && mSavedPaddingLeft >= 0) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/BaseMenuPresenter.java b/v7/appcompat/src/android/support/v7/view/menu/BaseMenuPresenter.java
index 1cf72c3..1e89ad2 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/BaseMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/BaseMenuPresenter.java
@@ -20,7 +20,6 @@
 
 import android.content.Context;
 import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -86,6 +85,7 @@
     /**
      * Reuses item views when it can
      */
+    @Override
     public void updateMenuView(boolean cleared) {
         final ViewGroup parent = (ViewGroup) mMenuView;
         if (parent == null) return;
@@ -105,7 +105,7 @@
                     if (item != oldItem) {
                         // Don't let old states linger with new data.
                         itemView.setPressed(false);
-                        ViewCompat.jumpDrawablesToCurrentState(itemView);
+                        itemView.jumpDrawablesToCurrentState();
                     }
                     if (itemView != convertView) {
                         addItemView(itemView, childIndex);
@@ -148,6 +148,7 @@
         return true;
     }
 
+    @Override
     public void setCallback(Callback cb) {
         mCallback = cb;
     }
@@ -206,12 +207,14 @@
         return true;
     }
 
+    @Override
     public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
         if (mCallback != null) {
             mCallback.onCloseMenu(menu, allMenusAreClosing);
         }
     }
 
+    @Override
     public boolean onSubMenuSelected(SubMenuBuilder menu) {
         if (mCallback != null) {
             return mCallback.onOpenSubMenu(menu);
@@ -219,18 +222,22 @@
         return false;
     }
 
+    @Override
     public boolean flagActionItems() {
         return false;
     }
 
+    @Override
     public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
         return false;
     }
 
+    @Override
     public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
         return false;
     }
 
+    @Override
     public int getId() {
         return mId;
     }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java b/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
index dd6a5bc..73499cf 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -113,6 +114,24 @@
         }
     };
 
+    private final View.OnAttachStateChangeListener mAttachStateChangeListener =
+            new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    if (mTreeObserver != null) {
+                        if (!mTreeObserver.isAlive()) {
+                            mTreeObserver = v.getViewTreeObserver();
+                        }
+                        mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+                    }
+                    v.removeOnAttachStateChangeListener(this);
+                }
+            };
+
     private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() {
         @Override
         public void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
@@ -252,6 +271,7 @@
             if (addGlobalListener) {
                 mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
             }
+            mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener);
         }
     }
 
@@ -384,14 +404,38 @@
             final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
             mLastPosition = nextMenuPosition;
 
-            final int[] tempLocation = new int[2];
+            final int parentOffsetLeft;
+            final int parentOffsetTop;
+            if (Build.VERSION.SDK_INT >= 26) {
+                // Anchor the submenu directly to the parent menu item view. This allows for
+                // accurate submenu positioning when the parent menu is being moved.
+                popupWindow.setAnchorView(parentView);
+                parentOffsetLeft = 0;
+                parentOffsetTop = 0;
+            } else {
+                // Framework does not allow anchoring to a view in another popup window. Use the
+                // same top-level anchor as the parent menu is using, with appropriate offsets.
 
-            // This popup menu will be positioned relative to the top-left edge
-            // of the view representing its parent menu.
-            parentView.getLocationInWindow(tempLocation);
-            final int parentOffsetLeft = parentInfo.window.getHorizontalOffset() + tempLocation[0];
-            final int parentOffsetTop = parentInfo.window.getVerticalOffset() + tempLocation[1];
+                // The following computation is only accurate for the initial submenu position.
+                // Should the submenu change its below/above state due to the parent menu move,
+                // the framework will compute the new submenu position using the anchor's height,
+                // not the parent menu item height. This will work well if the two heights are
+                // close, but if they are not, the submenu will become misaligned.
 
+                final int[] anchorScreenLocation = new int[2];
+                mAnchorView.getLocationOnScreen(anchorScreenLocation);
+
+                final int[] parentViewScreenLocation = new int[2];
+                parentView.getLocationOnScreen(parentViewScreenLocation);
+
+                // If used as horizontal/vertical offsets, these values would position the submenu
+                // at the exact same position as the parent item.
+                parentOffsetLeft = parentViewScreenLocation[0] - anchorScreenLocation[0];
+                parentOffsetTop = parentViewScreenLocation[1] - anchorScreenLocation[1];
+            }
+
+            // Adjust the horizontal offset to display the submenu to the right or to the left
+            // of the parent item.
             // By now, mDropDownGravity is the resolved absolute gravity, so
             // this should work in both LTR and RTL.
             final int x;
@@ -408,11 +452,11 @@
                     x = parentOffsetLeft - menuWidth;
                 }
             }
-
             popupWindow.setHorizontalOffset(x);
 
-            final int y = parentOffsetTop;
-            popupWindow.setVerticalOffset(y);
+            // Vertically align with the parent item.
+            popupWindow.setOverlapAnchor(true);
+            popupWindow.setVerticalOffset(parentOffsetTop);
         } else {
             if (mHasXOffset) {
                 popupWindow.setHorizontalOffset(mXOffset);
@@ -429,9 +473,11 @@
 
         popupWindow.show();
 
+        final ListView listView = popupWindow.getListView();
+        listView.setOnKeyListener(this);
+
         // If this is the root menu, show the title if requested.
         if (parentInfo == null && mShowTitle && menu.getHeaderTitle() != null) {
-            final ListView listView = popupWindow.getListView();
             final FrameLayout titleItemView = (FrameLayout) inflater.inflate(
                     R.layout.abc_popup_menu_header_item_layout, listView, false);
             final TextView titleView = (TextView) titleItemView.findViewById(android.R.id.title);
@@ -649,7 +695,7 @@
                 }
                 mTreeObserver = null;
             }
-
+            mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
 
             // If every [sub]menu was dismissed, that means the whole thing was
             // dismissed, so notify the owner.
@@ -749,4 +795,4 @@
             return window.getListView();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ListMenuItemView.java b/v7/appcompat/src/android/support/v7/view/menu/ListMenuItemView.java
index 6a736aa..081f304 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ListMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ListMenuItemView.java
@@ -91,19 +91,20 @@
 
         ViewCompat.setBackground(this, mBackground);
 
-        mTitleView = (TextView) findViewById(R.id.title);
+        mTitleView = findViewById(R.id.title);
         if (mTextAppearance != -1) {
             mTitleView.setTextAppearance(mTextAppearanceContext,
                     mTextAppearance);
         }
 
-        mShortcutView = (TextView) findViewById(R.id.shortcut);
-        mSubMenuArrowView = (ImageView) findViewById(R.id.submenuarrow);
+        mShortcutView = findViewById(R.id.shortcut);
+        mSubMenuArrowView = findViewById(R.id.submenuarrow);
         if (mSubMenuArrowView != null) {
             mSubMenuArrowView.setImageDrawable(mSubMenuArrow);
         }
     }
 
+    @Override
     public void initialize(MenuItemImpl itemData, int menuType) {
         mItemData = itemData;
         mMenuType = menuType;
@@ -116,12 +117,14 @@
         setIcon(itemData.getIcon());
         setEnabled(itemData.isEnabled());
         setSubMenuArrowVisible(itemData.hasSubMenu());
+        setContentDescription(itemData.getContentDescription());
     }
 
     public void setForceShowIcon(boolean forceShow) {
         mPreserveIconSpacing = mForceShowIcon = forceShow;
     }
 
+    @Override
     public void setTitle(CharSequence title) {
         if (title != null) {
             mTitleView.setText(title);
@@ -132,10 +135,12 @@
         }
     }
 
+    @Override
     public MenuItemImpl getItemData() {
         return mItemData;
     }
 
+    @Override
     public void setCheckable(boolean checkable) {
         if (!checkable && mRadioButton == null && mCheckBox == null) {
             return;
@@ -182,6 +187,7 @@
         }
     }
 
+    @Override
     public void setChecked(boolean checked) {
         CompoundButton compoundButton;
 
@@ -206,6 +212,7 @@
         }
     }
 
+    @Override
     public void setShortcut(boolean showShortcut, char shortcutKey) {
         final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
                 ? VISIBLE : GONE;
@@ -219,6 +226,7 @@
         }
     }
 
+    @Override
     public void setIcon(Drawable icon) {
         final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
         if (!showIcon && !mPreserveIconSpacing) {
@@ -280,10 +288,12 @@
         addView(mCheckBox);
     }
 
+    @Override
     public boolean prefersCondensedTitle() {
         return false;
     }
 
+    @Override
     public boolean showsIcon() {
         return mForceShowIcon;
     }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ListMenuPresenter.java b/v7/appcompat/src/android/support/v7/view/menu/ListMenuPresenter.java
index 41d5576..e3179f4 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ListMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ListMenuPresenter.java
@@ -177,10 +177,12 @@
         return false;
     }
 
+    @Override
     public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
         return false;
     }
 
+    @Override
     public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
         return false;
     }
@@ -232,6 +234,7 @@
             findExpandedIndex();
         }
 
+        @Override
         public int getCount() {
             ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
             int count = items.size() - mItemIndexOffset;
@@ -241,6 +244,7 @@
             return count - 1;
         }
 
+        @Override
         public MenuItemImpl getItem(int position) {
             ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
             position += mItemIndexOffset;
@@ -250,12 +254,14 @@
             return items.get(position);
         }
 
+        @Override
         public long getItemId(int position) {
             // Since a menu item's ID is optional, we'll use the position as an
             // ID for the item in the AdapterView
             return position;
         }
 
+        @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             if (convertView == null) {
                 convertView = mInflater.inflate(mItemLayoutRes, parent, false);
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java b/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
index 714f801..e6dee8d 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
@@ -34,7 +34,6 @@
 import android.support.v4.internal.view.SupportMenu;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.appcompat.R;
 import android.util.SparseArray;
 import android.view.ContextMenu;
@@ -375,13 +374,13 @@
         final int itemCount = size();
         for (int i = 0; i < itemCount; i++) {
             final MenuItem item = getItem(i);
-            final View v = MenuItemCompat.getActionView(item);
+            final View v = item.getActionView();
             if (v != null && v.getId() != View.NO_ID) {
                 if (viewStates == null) {
                     viewStates = new SparseArray<Parcelable>();
                 }
                 v.saveHierarchyState(viewStates);
-                if (MenuItemCompat.isActionViewExpanded(item)) {
+                if (item.isActionViewExpanded()) {
                     outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId());
                 }
             }
@@ -407,7 +406,7 @@
         final int itemCount = size();
         for (int i = 0; i < itemCount; i++) {
             final MenuItem item = getItem(i);
-            final View v = MenuItemCompat.getActionView(item);
+            final View v = item.getActionView();
             if (v != null && v.getId() != View.NO_ID) {
                 v.restoreHierarchyState(viewStates);
             }
@@ -421,7 +420,7 @@
         if (expandedId > 0) {
             MenuItem itemToExpand = findItem(expandedId);
             if (itemToExpand != null) {
-                MenuItemCompat.expandActionView(itemToExpand);
+                itemToExpand.expandActionView();
             }
         }
     }
@@ -461,6 +460,7 @@
                 defaultShowAsAction);
     }
 
+    @Override
     public MenuItem add(CharSequence title) {
         return addInternal(0, 0, 0, title);
     }
@@ -868,7 +868,7 @@
     @SuppressWarnings("deprecation")
     void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) {
         final boolean qwerty = isQwertyMode();
-        final int metaState = event.getMetaState();
+        final int modifierState = event.getModifiers();
         final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
         // Get the chars associated with the keyCode (i.e using any chording combo)
         final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
@@ -884,14 +884,18 @@
             if (item.hasSubMenu()) {
                 ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
             }
-            final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
-            if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) &&
-                  (shortcutChar != 0) &&
-                  (shortcutChar == possibleChars.meta[0]
-                      || shortcutChar == possibleChars.meta[2]
-                      || (qwerty && shortcutChar == '\b' &&
-                          keyCode == KeyEvent.KEYCODE_DEL)) &&
-                  item.isEnabled()) {
+            final char shortcutChar =
+                    qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
+            final int shortcutModifiers =
+                    qwerty ? item.getAlphabeticModifiers() : item.getNumericModifiers();
+            final boolean isModifiersExactMatch = (modifierState & SUPPORTED_MODIFIERS_MASK)
+                    == (shortcutModifiers & SUPPORTED_MODIFIERS_MASK);
+            if (isModifiersExactMatch && (shortcutChar != 0)
+                    && (shortcutChar == possibleChars.meta[0]
+                            || shortcutChar == possibleChars.meta[2]
+                            || (qwerty && shortcutChar == '\b'
+                                && keyCode == KeyEvent.KEYCODE_DEL))
+                    && item.isEnabled()) {
                 items.add(item);
             }
         }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuDialogHelper.java b/v7/appcompat/src/android/support/v7/view/menu/MenuDialogHelper.java
index 6e4036f..eaf7c82 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuDialogHelper.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuDialogHelper.java
@@ -88,6 +88,7 @@
         mDialog.show();
     }
 
+    @Override
     public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) {
             if (event.getAction() == KeyEvent.ACTION_DOWN
@@ -162,6 +163,7 @@
         return false;
     }
 
+    @Override
     public void onClick(DialogInterface dialog, int which) {
         mMenu.performItemAction((MenuItemImpl) mPresenter.getAdapter().getItem(which), 0);
     }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java
index f1b94b0..d606aa0 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java
@@ -21,15 +21,19 @@
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.util.Log;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.SubMenu;
@@ -57,7 +61,9 @@
     private CharSequence mTitleCondensed;
     private Intent mIntent;
     private char mShortcutNumericChar;
+    private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON;
     private char mShortcutAlphabeticChar;
+    private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON;
 
     /** The icon's drawable which is only created as needed */
     private Drawable mIconDrawable;
@@ -77,6 +83,15 @@
     private Runnable mItemCallback;
     private SupportMenuItem.OnMenuItemClickListener mClickListener;
 
+    private CharSequence mContentDescription;
+    private CharSequence mTooltipText;
+
+    private ColorStateList mIconTintList = null;
+    private PorterDuff.Mode mIconTintMode = null;
+    private boolean mHasIconTint = false;
+    private boolean mHasIconTintMode = false;
+    private boolean mNeedToApplyIconTint = false;
+
     private int mFlags = ENABLED;
     private static final int CHECKABLE = 0x00000001;
     private static final int CHECKED = 0x00000002;
@@ -89,7 +104,7 @@
 
     private View mActionView;
     private ActionProvider mActionProvider;
-    private MenuItemCompat.OnActionExpandListener mOnActionExpandListener;
+    private MenuItem.OnActionExpandListener mOnActionExpandListener;
     private boolean mIsActionViewExpanded = false;
 
     /** Used for the icon resource ID if this item does not have an icon */
@@ -153,8 +168,8 @@
             return true;
         }
 
-        if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
-          return true;
+        if (mMenu.dispatchMenuItemSelected(mMenu, this)) {
+            return true;
         }
 
         if (mItemCallback != null) {
@@ -255,11 +270,35 @@
     }
 
     @Override
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+        if (mShortcutAlphabeticChar == alphaChar
+                && mShortcutAlphabeticModifiers == alphaModifiers) {
+            return this;
+        }
+
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+
+        mMenu.onItemsChanged(false);
+        return this;
+    }
+
+    @Override
+    public int getAlphabeticModifiers() {
+        return mShortcutAlphabeticModifiers;
+    }
+
+    @Override
     public char getNumericShortcut() {
         return mShortcutNumericChar;
     }
 
     @Override
+    public int getNumericModifiers() {
+        return mShortcutNumericModifiers;
+    }
+
+    @Override
     public MenuItem setNumericShortcut(char numericChar) {
         if (mShortcutNumericChar == numericChar) {
             return this;
@@ -273,6 +312,20 @@
     }
 
     @Override
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+        if (mShortcutNumericChar == numericChar && mShortcutNumericModifiers == numericModifiers) {
+            return this;
+        }
+
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
+    @Override
     public MenuItem setShortcut(char numericChar, char alphaChar) {
         mShortcutNumericChar = numericChar;
         mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
@@ -282,6 +335,19 @@
         return this;
     }
 
+    @Override
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers) {
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
     /**
      * @return The active shortcut (based on QWERTY-mode of the menu).
      */
@@ -418,14 +484,14 @@
     @Override
     public Drawable getIcon() {
         if (mIconDrawable != null) {
-            return mIconDrawable;
+            return applyIconTintIfNecessary(mIconDrawable);
         }
 
         if (mIconResId != NO_ICON) {
             Drawable icon = AppCompatResources.getDrawable(mMenu.getContext(), mIconResId);
             mIconResId = NO_ICON;
             mIconDrawable = icon;
-            return icon;
+            return applyIconTintIfNecessary(icon);
         }
 
         return null;
@@ -435,6 +501,7 @@
     public MenuItem setIcon(Drawable icon) {
         mIconResId = NO_ICON;
         mIconDrawable = icon;
+        mNeedToApplyIconTint = true;
         mMenu.onItemsChanged(false);
 
         return this;
@@ -444,6 +511,7 @@
     public MenuItem setIcon(int iconResId) {
         mIconDrawable = null;
         mIconResId = iconResId;
+        mNeedToApplyIconTint = true;
 
         // If we have a view, we need to push the Drawable to them
         mMenu.onItemsChanged(false);
@@ -451,6 +519,58 @@
         return this;
     }
 
+
+    @Override
+    public MenuItem setIconTintList(@Nullable ColorStateList iconTintList) {
+        mIconTintList = iconTintList;
+        mHasIconTint = true;
+        mNeedToApplyIconTint = true;
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
+    @Override
+    public ColorStateList getIconTintList() {
+        return mIconTintList;
+    }
+
+    @Override
+    public MenuItem setIconTintMode(PorterDuff.Mode iconTintMode) {
+        mIconTintMode = iconTintMode;
+        mHasIconTintMode = true;
+        mNeedToApplyIconTint = true;
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
+    @Override
+    public PorterDuff.Mode getIconTintMode() {
+        return mIconTintMode;
+    }
+
+    private Drawable applyIconTintIfNecessary(Drawable icon) {
+        if (icon != null && mNeedToApplyIconTint && (mHasIconTint || mHasIconTintMode)) {
+            icon = DrawableCompat.wrap(icon);
+            icon = icon.mutate();
+
+            if (mHasIconTint) {
+                DrawableCompat.setTintList(icon, mIconTintList);
+            }
+
+            if (mHasIconTintMode) {
+                DrawableCompat.setTintMode(icon, mIconTintMode);
+            }
+
+            mNeedToApplyIconTint = false;
+        }
+
+        return icon;
+    }
+
     @Override
     public boolean isCheckable() {
         return (mFlags & CHECKABLE) == CHECKABLE;
@@ -711,13 +831,6 @@
         return false;
     }
 
-    @Override
-    public SupportMenuItem setSupportOnActionExpandListener(
-            MenuItemCompat.OnActionExpandListener listener) {
-        mOnActionExpandListener = listener;
-        return this;
-    }
-
     public boolean hasCollapsibleActionView() {
         if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) {
             if (mActionView == null && mActionProvider != null) {
@@ -740,7 +853,35 @@
 
     @Override
     public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
-        throw new UnsupportedOperationException(
-                "This is not supported, use MenuItemCompat.setOnActionExpandListener()");
+        mOnActionExpandListener = listener;
+        return this;
+    }
+
+    @Override
+    public SupportMenuItem setContentDescription(CharSequence contentDescription) {
+        mContentDescription = contentDescription;
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
+    @Override
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    @Override
+    public SupportMenuItem setTooltipText(CharSequence tooltipText) {
+        mTooltipText = tooltipText;
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
+    @Override
+    public CharSequence getTooltipText() {
+        return mTooltipText;
     }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
index 4ba3c89..5a44f1a 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
@@ -18,15 +18,15 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.view.CollapsibleActionView;
 import android.util.Log;
 import android.view.ContextMenu;
@@ -42,7 +42,6 @@
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP)
-@TargetApi(14)
 @RequiresApi(14)
 public class MenuItemWrapperICS extends BaseMenuWrapper<SupportMenuItem> implements MenuItem {
     static final String LOG_TAG = "MenuItemWrapper";
@@ -132,28 +131,57 @@
     }
 
     @Override
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers) {
+        mWrappedObject.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setNumericShortcut(char numericChar) {
         mWrappedObject.setNumericShortcut(numericChar);
         return this;
     }
 
     @Override
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+        mWrappedObject.setNumericShortcut(numericChar, numericModifiers);
+        return this;
+    }
+
+    @Override
     public char getNumericShortcut() {
         return mWrappedObject.getNumericShortcut();
     }
 
     @Override
+    public int getNumericModifiers() {
+        return mWrappedObject.getNumericModifiers();
+    }
+
+    @Override
     public MenuItem setAlphabeticShortcut(char alphaChar) {
         mWrappedObject.setAlphabeticShortcut(alphaChar);
         return this;
     }
 
     @Override
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+        mWrappedObject.setAlphabeticShortcut(alphaChar, alphaModifiers);
+        return this;
+    }
+
+    @Override
     public char getAlphabeticShortcut() {
         return mWrappedObject.getAlphabeticShortcut();
     }
 
     @Override
+    public int getAlphabeticModifiers() {
+        return mWrappedObject.getAlphabeticModifiers();
+    }
+
+    @Override
     public MenuItem setCheckable(boolean checkable) {
         mWrappedObject.setCheckable(checkable);
         return this;
@@ -293,11 +321,55 @@
 
     @Override
     public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
-        mWrappedObject.setSupportOnActionExpandListener(listener != null ?
-                new OnActionExpandListenerWrapper(listener) : null);
+        mWrappedObject.setOnActionExpandListener(listener != null
+                ? new OnActionExpandListenerWrapper(listener) : null);
         return this;
     }
 
+    @Override
+    public MenuItem setContentDescription(CharSequence contentDescription) {
+        mWrappedObject.setContentDescription(contentDescription);
+        return this;
+    }
+
+    @Override
+    public CharSequence getContentDescription() {
+        return mWrappedObject.getContentDescription();
+    }
+
+    @Override
+    public MenuItem setTooltipText(CharSequence tooltipText) {
+        mWrappedObject.setTooltipText(tooltipText);
+        return this;
+    }
+
+    @Override
+    public CharSequence getTooltipText() {
+        return mWrappedObject.getTooltipText();
+    }
+
+    @Override
+    public MenuItem setIconTintList(ColorStateList tint) {
+        mWrappedObject.setIconTintList(tint);
+        return this;
+    }
+
+    @Override
+    public ColorStateList getIconTintList() {
+        return mWrappedObject.getIconTintList();
+    }
+
+    @Override
+    public MenuItem setIconTintMode(PorterDuff.Mode tintMode) {
+        mWrappedObject.setIconTintMode(tintMode);
+        return this;
+    }
+
+    @Override
+    public PorterDuff.Mode getIconTintMode() {
+        return mWrappedObject.getIconTintMode();
+    }
+
     public void setExclusiveCheckable(boolean checkable) {
         try {
             if (mSetExclusiveCheckableMethod == null) {
@@ -328,7 +400,7 @@
     }
 
     private class OnActionExpandListenerWrapper extends BaseWrapper<MenuItem.OnActionExpandListener>
-            implements MenuItemCompat.OnActionExpandListener {
+            implements MenuItem.OnActionExpandListener {
 
         OnActionExpandListenerWrapper(MenuItem.OnActionExpandListener object) {
             super(object);
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
index 07db707..267903b 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
@@ -32,7 +31,6 @@
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP)
-@TargetApi(16)
 @RequiresApi(16)
 class MenuItemWrapperJB extends MenuItemWrapperICS {
 
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuPopupHelper.java b/v7/appcompat/src/android/support/v7/view/menu/MenuPopupHelper.java
index 18642ec..654ef7d 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuPopupHelper.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuPopupHelper.java
@@ -226,10 +226,8 @@
 
         if (Build.VERSION.SDK_INT >= 17) {
             display.getRealSize(displaySize);
-        } else if (Build.VERSION.SDK_INT >= 13) {
-            display.getSize(displaySize);
         } else {
-            displaySize.set(display.getWidth(), display.getHeight());
+            display.getSize(displaySize);
         }
 
         final int smallestWidth = Math.min(displaySize.x, displaySize.y);
@@ -270,7 +268,7 @@
             final int hgrav = GravityCompat.getAbsoluteGravity(mDropDownGravity,
                     ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK;
             if (hgrav == Gravity.RIGHT) {
-                xOffset -= mAnchorView.getWidth();
+                xOffset += mAnchorView.getWidth();
             }
 
             popup.setHorizontalOffset(xOffset);
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuPresenter.java b/v7/appcompat/src/android/support/v7/view/menu/MenuPresenter.java
index 35d6860..e32e387 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuPresenter.java
@@ -56,12 +56,14 @@
     }
 
     /**
-     * Initialize this presenter for the given context and menu.
-     * This method is called by MenuBuilder when a presenter is
-     * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
+     * Initializes this presenter for the given context and menu.
+     * <p>
+     * This method is called by MenuBuilder when a presenter is added. See
+     * {@link MenuBuilder#addMenuPresenter(MenuPresenter)}.
      *
-     * @param context Context for this presenter; used for view creation and resource management
-     * @param menu Menu to host
+     * @param context the context for this presenter; used for view creation
+     *                and resource management, must be non-{@code null}
+     * @param menu the menu to host, or {@code null} to clear the hosted menu
      */
     void initForMenu(Context context, MenuBuilder menu);
 
@@ -104,8 +106,10 @@
      * closing. Presenter implementations should close the representation
      * of the menu indicated as necessary and notify a registered callback.
      *
-     * @param menu Menu or submenu that is closing.
-     * @param allMenusAreClosing True if all associated menus are closing.
+     * @param menu the menu or submenu that is closing
+     * @param allMenusAreClosing {@code true} if all displayed menus and
+     *                           submenus are closing, {@code false} if only
+     *                           the specified menu is closing
      */
     void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
 
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperFactory.java b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperFactory.java
index 4bb9bca..1839a17 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperFactory.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperFactory.java
@@ -37,25 +37,18 @@
     }
 
     public static Menu wrapSupportMenu(Context context, SupportMenu supportMenu) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            return new MenuWrapperICS(context, supportMenu);
-        }
-        throw new UnsupportedOperationException();
+        return new MenuWrapperICS(context, supportMenu);
     }
 
     public static MenuItem wrapSupportMenuItem(Context context, SupportMenuItem supportMenuItem) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
             return new MenuItemWrapperJB(context, supportMenuItem);
-        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+        } else {
             return new MenuItemWrapperICS(context, supportMenuItem);
         }
-        throw new UnsupportedOperationException();
     }
 
     public static SubMenu wrapSupportSubMenu(Context context, SupportSubMenu supportSubMenu) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            return new SubMenuWrapperICS(context, supportSubMenu);
-        }
-        throw new UnsupportedOperationException();
+        return new SubMenuWrapperICS(context, supportSubMenu);
     }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
index 25208af..9672449 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.view.menu;
 
-import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -30,7 +29,6 @@
 /**
  * Wraps a support {@link SupportMenu} as a framework {@link android.view.Menu}
  */
-@TargetApi(14)
 @RequiresApi(14)
 class MenuWrapperICS extends BaseMenuWrapper<SupportMenu> implements Menu {
 
diff --git a/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java b/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java
index 2ab1a0c..d94ff72 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java
@@ -25,7 +25,6 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnKeyListener;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -73,6 +72,22 @@
         }
     };
 
+    private final View.OnAttachStateChangeListener mAttachStateChangeListener =
+            new View.OnAttachStateChangeListener() {
+        @Override
+        public void onViewAttachedToWindow(View v) {
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            if (mTreeObserver != null) {
+                if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+                mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+            }
+            v.removeOnAttachStateChangeListener(this);
+        }
+    };
+
     private PopupWindow.OnDismissListener mOnDismissListener;
 
     private View mAnchorView;
@@ -146,6 +161,7 @@
         if (addGlobalListener) {
             mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
         }
+        anchor.addOnAttachStateChangeListener(mAttachStateChangeListener);
         mPopup.setAnchorView(anchor);
         mPopup.setDropDownGravity(mDropDownGravity);
 
@@ -216,6 +232,8 @@
             mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
             mTreeObserver = null;
         }
+        mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
+
         if (mOnDismissListener != null) {
             mOnDismissListener.onDismiss();
         }
@@ -242,6 +260,7 @@
                     mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
             subPopup.setPresenterCallback(mPresenterCallback);
             subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu));
+            subPopup.setGravity(mDropDownGravity);
 
             // Pass responsibility for handling onDismiss to the submenu.
             subPopup.setOnDismissListener(mOnDismissListener);
@@ -327,4 +346,4 @@
     public void setShowTitle(boolean showTitle) {
         mShowTitle = showTitle;
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/view/menu/SubMenuBuilder.java b/v7/appcompat/src/android/support/v7/view/menu/SubMenuBuilder.java
index fdd1b14..94300ed 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/SubMenuBuilder.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/SubMenuBuilder.java
@@ -68,6 +68,7 @@
         return mParentMenu;
     }
 
+    @Override
     public MenuItem getItem() {
         return mItem;
     }
@@ -88,32 +89,39 @@
                 mParentMenu.dispatchMenuItemSelected(menu, item);
     }
 
+    @Override
     public SubMenu setIcon(Drawable icon) {
         mItem.setIcon(icon);
         return this;
     }
 
+    @Override
     public SubMenu setIcon(int iconRes) {
         mItem.setIcon(iconRes);
         return this;
     }
 
+    @Override
     public SubMenu setHeaderIcon(Drawable icon) {
         return (SubMenu) super.setHeaderIconInt(icon);
     }
 
+    @Override
     public SubMenu setHeaderIcon(int iconRes) {
         return (SubMenu) super.setHeaderIconInt(iconRes);
     }
 
+    @Override
     public SubMenu setHeaderTitle(CharSequence title) {
         return (SubMenu) super.setHeaderTitleInt(title);
     }
 
+    @Override
     public SubMenu setHeaderTitle(int titleRes) {
         return (SubMenu) super.setHeaderTitleInt(titleRes);
     }
 
+    @Override
     public SubMenu setHeaderView(View view) {
         return (SubMenu) super.setHeaderViewInt(view);
     }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
index 55245f6..b7478a9 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RequiresApi;
@@ -34,7 +33,6 @@
  */
 @RestrictTo(LIBRARY_GROUP)
 @RequiresApi(14)
-@TargetApi(14)
 class SubMenuWrapperICS extends MenuWrapperICS implements SubMenu {
 
     SubMenuWrapperICS(Context context, SupportSubMenu subMenu) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AbsActionBarView.java b/v7/appcompat/src/android/support/v7/widget/AbsActionBarView.java
index b048ffa..58a130c 100644
--- a/v7/appcompat/src/android/support/v7/widget/AbsActionBarView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AbsActionBarView.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPropertyAnimatorCompat;
 import android.support.v4.view.ViewPropertyAnimatorListener;
@@ -91,7 +90,7 @@
         // eat the rest of the gesture without reporting the events to the default implementation
         // since that's what it expects.
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
             mEatingTouch = false;
         }
@@ -115,19 +114,19 @@
         // Same deal as onTouchEvent() above. Eat all hover events, but still
         // respect the touch event dispatch contract.
 
-        final int action = MotionEventCompat.getActionMasked(ev);
-        if (action == MotionEventCompat.ACTION_HOVER_ENTER) {
+        final int action = ev.getActionMasked();
+        if (action == MotionEvent.ACTION_HOVER_ENTER) {
             mEatingHover = false;
         }
 
         if (!mEatingHover) {
             final boolean handled = super.onHoverEvent(ev);
-            if (action == MotionEventCompat.ACTION_HOVER_ENTER && !handled) {
+            if (action == MotionEvent.ACTION_HOVER_ENTER && !handled) {
                 mEatingHover = true;
             }
         }
 
-        if (action == MotionEventCompat.ACTION_HOVER_EXIT
+        if (action == MotionEvent.ACTION_HOVER_EXIT
                 || action == MotionEvent.ACTION_CANCEL) {
             mEatingHover = false;
         }
@@ -161,7 +160,7 @@
 
         if (visibility == VISIBLE) {
             if (getVisibility() != VISIBLE) {
-                ViewCompat.setAlpha(this, 0f);
+                setAlpha(0f);
             }
             ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(1f);
             anim.setDuration(duration);
@@ -199,6 +198,7 @@
 
     public void postShowOverflowMenu() {
         post(new Runnable() {
+            @Override
             public void run() {
                 showOverflowMenu();
             }
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
index d8b0f2d..5e86497 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
@@ -16,14 +16,12 @@
 
 package android.support.v7.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(9)
-@TargetApi(9)
 class ActionBarBackgroundDrawable extends Drawable {
 
     final ActionBarContainer mContainer;
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
index 56f2a09..989fc4c 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
@@ -16,13 +16,11 @@
 
 package android.support.v7.widget;
 
-import android.annotation.TargetApi;
 import android.graphics.Outline;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(21)
-@TargetApi(21)
 class ActionBarBackgroundDrawableV21 extends ActionBarBackgroundDrawable {
 
     public ActionBarBackgroundDrawableV21(ActionBarContainer container) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java b/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java
index f34b577..afe0bb4 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java
@@ -169,18 +169,17 @@
         }
     }
 
+    @Override
     public void jumpDrawablesToCurrentState() {
-        if (Build.VERSION.SDK_INT >= 11) {
-            super.jumpDrawablesToCurrentState();
-            if (mBackground != null) {
-                mBackground.jumpToCurrentState();
-            }
-            if (mStackedBackground != null) {
-                mStackedBackground.jumpToCurrentState();
-            }
-            if (mSplitBackground != null) {
-                mSplitBackground.jumpToCurrentState();
-            }
+        super.jumpDrawablesToCurrentState();
+        if (mBackground != null) {
+            mBackground.jumpToCurrentState();
+        }
+        if (mStackedBackground != null) {
+            mStackedBackground.jumpToCurrentState();
+        }
+        if (mSplitBackground != null) {
+            mSplitBackground.jumpToCurrentState();
         }
     }
 
@@ -236,12 +235,14 @@
         return mTabContainer;
     }
 
+    @Override
     public android.view.ActionMode startActionModeForChild(View child,
             android.view.ActionMode.Callback callback) {
         // No starting an action mode for an action bar child! (Where would it go?)
         return null;
     }
 
+    @Override
     public android.view.ActionMode startActionModeForChild(View child,
             android.view.ActionMode.Callback callback, int type) {
         if (type != android.view.ActionMode.TYPE_PRIMARY) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarContextView.java b/v7/appcompat/src/android/support/v7/widget/ActionBarContextView.java
index 4f05080..35074ef 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarContextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarContextView.java
@@ -19,7 +19,6 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.content.Context;
-import android.os.Build;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
@@ -92,6 +91,7 @@
         }
     }
 
+    @Override
     public void setContentHeight(int height) {
         mContentHeight = height;
     }
@@ -167,6 +167,7 @@
 
         View closeButton = mClose.findViewById(R.id.action_mode_close_button);
         closeButton.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 mode.finish();
             }
@@ -354,16 +355,14 @@
 
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        if (Build.VERSION.SDK_INT >= 14) {
-            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-                // Action mode started
-                event.setSource(this);
-                event.setClassName(getClass().getName());
-                event.setPackageName(getContext().getPackageName());
-                event.setContentDescription(mTitle);
-            } else {
-                super.onInitializeAccessibilityEvent(event);
-            }
+        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            // Action mode started
+            event.setSource(this);
+            event.setClassName(getClass().getName());
+            event.setPackageName(getContext().getPackageName());
+            event.setContentDescription(mTitle);
+        } else {
+            super.onInitializeAccessibilityEvent(event);
         }
     }
 
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarOverlayLayout.java b/v7/appcompat/src/android/support/v7/widget/ActionBarOverlayLayout.java
index b685b4c..b5cdc7a 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarOverlayLayout.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarOverlayLayout.java
@@ -18,6 +18,8 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
@@ -30,10 +32,6 @@
 import android.support.v4.view.NestedScrollingParent;
 import android.support.v4.view.NestedScrollingParentHelper;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.support.v4.widget.ScrollerCompat;
 import android.support.v7.app.AppCompatDelegate;
 import android.support.v7.appcompat.R;
 import android.support.v7.view.menu.MenuPresenter;
@@ -42,7 +40,9 @@
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
 import android.view.Window;
+import android.widget.OverScroller;
 
 /**
  * Special layout for the containing of an overlay action bar (and its content) to correctly handle
@@ -80,6 +80,7 @@
     private final Rect mLastBaseContentInsets = new Rect();
     private final Rect mContentInsets = new Rect();
     private final Rect mBaseInnerInsets = new Rect();
+    private final Rect mLastBaseInnerInsets = new Rect();
     private final Rect mInnerInsets = new Rect();
     private final Rect mLastInnerInsets = new Rect();
 
@@ -87,37 +88,38 @@
 
     private final int ACTION_BAR_ANIMATE_DELAY = 600; // ms
 
-    private ScrollerCompat mFlingEstimator;
+    private OverScroller mFlingEstimator;
 
-    ViewPropertyAnimatorCompat mCurrentActionBarTopAnimator;
+    ViewPropertyAnimator mCurrentActionBarTopAnimator;
 
-    final ViewPropertyAnimatorListener mTopAnimatorListener
-            = new ViewPropertyAnimatorListenerAdapter() {
+    final AnimatorListenerAdapter mTopAnimatorListener = new AnimatorListenerAdapter() {
         @Override
-        public void onAnimationEnd(View view) {
+        public void onAnimationEnd(Animator animator) {
             mCurrentActionBarTopAnimator = null;
             mAnimatingForFling = false;
         }
 
         @Override
-        public void onAnimationCancel(View view) {
+        public void onAnimationCancel(Animator animator) {
             mCurrentActionBarTopAnimator = null;
             mAnimatingForFling = false;
         }
     };
 
     private final Runnable mRemoveActionBarHideOffset = new Runnable() {
+        @Override
         public void run() {
             haltActionBarHideOffsetAnimations();
-            mCurrentActionBarTopAnimator = ViewCompat.animate(mActionBarTop).translationY(0)
+            mCurrentActionBarTopAnimator = mActionBarTop.animate().translationY(0)
                     .setListener(mTopAnimatorListener);
         }
     };
 
     private final Runnable mAddActionBarHideOffset = new Runnable() {
+        @Override
         public void run() {
             haltActionBarHideOffsetAnimations();
-            mCurrentActionBarTopAnimator = ViewCompat.animate(mActionBarTop)
+            mCurrentActionBarTopAnimator = mActionBarTop.animate()
                     .translationY(-mActionBarTop.getHeight())
                     .setListener(mTopAnimatorListener);
         }
@@ -151,7 +153,7 @@
         mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
                 Build.VERSION_CODES.KITKAT;
 
-        mFlingEstimator = ScrollerCompat.create(context);
+        mFlingEstimator = new OverScroller(context);
     }
 
     @Override
@@ -215,12 +217,14 @@
 //        }
     }
 
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         init(getContext());
         ViewCompat.requestApplyInsets(this);
     }
 
+    @Override
     public void onWindowSystemUiVisibilityChanged(int visible) {
         if (Build.VERSION.SDK_INT >= 16) {
             super.onWindowSystemUiVisibilityChanged(visible);
@@ -290,6 +294,10 @@
 
         mBaseInnerInsets.set(systemInsets);
         ViewUtils.computeFitSystemWindows(this, mBaseInnerInsets, mBaseContentInsets);
+        if (!mLastBaseInnerInsets.equals(mBaseInnerInsets)) {
+            changed = true;
+            mLastBaseInnerInsets.set(mBaseInnerInsets);
+        }
         if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
             changed = true;
             mLastBaseContentInsets.set(mBaseContentInsets);
@@ -343,8 +351,7 @@
                 mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
         maxHeight = Math.max(maxHeight,
                 mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-        childState = ViewUtils.combineMeasuredStates(childState,
-                ViewCompat.getMeasuredState(mActionBarTop));
+        childState = View.combineMeasuredStates(childState, mActionBarTop.getMeasuredState());
 
         final int vis = ViewCompat.getWindowSystemUiVisibility(this);
         final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
@@ -396,8 +403,7 @@
                 mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
         maxHeight = Math.max(maxHeight,
                 mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-        childState = ViewUtils.combineMeasuredStates(childState,
-                ViewCompat.getMeasuredState(mContent));
+        childState = View.combineMeasuredStates(childState, mContent.getMeasuredState());
 
         // Account for padding too
         maxWidth += getPaddingLeft() + getPaddingRight();
@@ -408,8 +414,8 @@
         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
 
         setMeasuredDimension(
-                ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec,
+                View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+                View.resolveSizeAndState(maxHeight, heightMeasureSpec,
                         childState << MEASURED_HEIGHT_STATE_SHIFT));
     }
 
@@ -444,7 +450,7 @@
         super.draw(c);
         if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
             final int top = mActionBarTop.getVisibility() == VISIBLE ?
-                    (int) (mActionBarTop.getBottom() + ViewCompat.getTranslationY(mActionBarTop) + 0.5f)
+                    (int) (mActionBarTop.getBottom() + mActionBarTop.getTranslationY() + 0.5f)
                     : 0;
             mWindowContentOverlay.setBounds(0, top, getWidth(),
                     top + mWindowContentOverlay.getIntrinsicHeight());
@@ -527,8 +533,8 @@
 
     void pullChildren() {
         if (mContent == null) {
-            mContent = (ContentFrameLayout) findViewById(R.id.action_bar_activity_content);
-            mActionBarTop = (ActionBarContainer) findViewById(R.id.action_bar_container);
+            mContent = findViewById(R.id.action_bar_activity_content);
+            mActionBarTop = findViewById(R.id.action_bar_container);
             mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar));
         }
     }
@@ -559,14 +565,14 @@
     }
 
     public int getActionBarHideOffset() {
-        return mActionBarTop != null ? -((int) ViewCompat.getTranslationY(mActionBarTop)) : 0;
+        return mActionBarTop != null ? -((int) mActionBarTop.getTranslationY()) : 0;
     }
 
     public void setActionBarHideOffset(int offset) {
         haltActionBarHideOffsetAnimations();
         final int topHeight = mActionBarTop.getHeight();
         offset = Math.max(0, Math.min(offset, topHeight));
-        ViewCompat.setTranslationY(mActionBarTop, -offset);
+        mActionBarTop.setTranslationY(-offset);
     }
 
     void haltActionBarHideOffsetAnimations() {
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
index 3abc33b..8ed599b 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
@@ -28,7 +28,6 @@
 import android.support.v4.view.ActionProvider;
 import android.support.v4.view.GravityCompat;
 import android.support.v7.appcompat.R;
-import android.support.v7.transition.ActionBarTransition;
 import android.support.v7.view.ActionBarPolicy;
 import android.support.v7.view.menu.ActionMenuItemView;
 import android.support.v7.view.menu.BaseMenuPresenter;
@@ -227,10 +226,6 @@
 
     @Override
     public void updateMenuView(boolean cleared) {
-        final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent();
-        if (menuViewParent != null) {
-            ActionBarTransition.beginDelayedTransition(menuViewParent);
-        }
         super.updateMenuView(cleared);
 
         ((View) mMenuView).requestLayout();
@@ -284,6 +279,7 @@
         return super.filterLeftoverView(parent, childIndex);
     }
 
+    @Override
     public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
         if (!subMenu.hasVisibleItems()) return false;
 
@@ -417,6 +413,7 @@
         return mReserveOverflow;
     }
 
+    @Override
     public boolean flagActionItems() {
         final ArrayList<MenuItemImpl> visibleItems;
         final int itemsSize;
@@ -625,10 +622,12 @@
 
         public static final Parcelable.Creator<SavedState> CREATOR
                 = new Parcelable.Creator<SavedState>() {
+            @Override
             public SavedState createFromParcel(Parcel in) {
                 return new SavedState(in);
             }
 
+            @Override
             public SavedState[] newArray(int size) {
                 return new SavedState[size];
             }
@@ -647,6 +646,8 @@
             setVisibility(VISIBLE);
             setEnabled(true);
 
+            TooltipCompat.setTooltipText(this, getContentDescription());
+
             setOnTouchListener(new ForwardingListener(this) {
                 @Override
                 public ShowableListMenu getPopup() {
@@ -795,6 +796,7 @@
             mPopup = popup;
         }
 
+        @Override
         public void run() {
             if (mMenu != null) {
                 mMenu.changeMenuMode();
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java
index da56faa..76e06da 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java
@@ -618,18 +618,21 @@
     }
 
     /** @hide */
+    @Override
     @RestrictTo(LIBRARY_GROUP)
     public boolean invokeItem(MenuItemImpl item) {
         return mMenu.performItemAction(item, 0);
     }
 
     /** @hide */
+    @Override
     @RestrictTo(LIBRARY_GROUP)
     public int getWindowAnimations() {
         return 0;
     }
 
     /** @hide */
+    @Override
     @RestrictTo(LIBRARY_GROUP)
     public void initialize(MenuBuilder menu) {
         mMenu = menu;
@@ -741,6 +744,7 @@
         return result;
     }
 
+    @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         return false;
     }
@@ -784,7 +788,7 @@
         }
     }
 
-    private class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback {
+    private static class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback {
         ActionMenuPresenterCallback() {
         }
 
diff --git a/v7/appcompat/src/android/support/v7/widget/ActivityChooserModel.java b/v7/appcompat/src/android/support/v7/widget/ActivityChooserModel.java
index bdd3435..698f1a3 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActivityChooserModel.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActivityChooserModel.java
@@ -16,21 +16,20 @@
 
 package android.support.v7.widget;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.database.DataSetObservable;
 import android.os.AsyncTask;
-import android.support.v4.os.AsyncTaskCompat;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Xml;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -572,7 +571,7 @@
         }
         mHistoricalRecordsChanged = false;
         if (!TextUtils.isEmpty(mHistoryFileName)) {
-            AsyncTaskCompat.executeParallel(new PersistHistoryAsyncTask(),
+            new PersistHistoryAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
                     new ArrayList<HistoricalRecord>(mHistoricalRecords), mHistoryFileName);
         }
     }
@@ -850,7 +849,7 @@
     /**
      * Represents an activity.
      */
-    public final class ActivityResolveInfo implements Comparable<ActivityResolveInfo> {
+    public static final class ActivityResolveInfo implements Comparable<ActivityResolveInfo> {
 
         /**
          * The {@link ResolveInfo} of the activity.
@@ -894,6 +893,7 @@
             return true;
         }
 
+        @Override
         public int compareTo(ActivityResolveInfo another) {
             return  Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
         }
@@ -912,7 +912,7 @@
     /**
      * Default activity sorter implementation.
      */
-    private final class DefaultSorter implements ActivitySorter {
+    private static final class DefaultSorter implements ActivitySorter {
         private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f;
 
         private final Map<ComponentName, ActivityResolveInfo> mPackageNameToActivityMap =
@@ -921,6 +921,7 @@
         DefaultSorter() {
         }
 
+        @Override
         public void sort(Intent intent, List<ActivityResolveInfo> activities,
                 List<HistoricalRecord> historicalRecords) {
             Map<ComponentName, ActivityResolveInfo> componentNameToActivityMap =
diff --git a/v7/appcompat/src/android/support/v7/widget/ActivityChooserView.java b/v7/appcompat/src/android/support/v7/widget/ActivityChooserView.java
index a1f5018..ddcb9fc 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActivityChooserView.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActivityChooserView.java
@@ -25,10 +25,12 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ActionProvider;
-import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.view.menu.ShowableListMenu;
 import android.util.AttributeSet;
@@ -37,6 +39,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
@@ -232,16 +235,23 @@
 
         mCallbacks = new Callbacks();
 
-        mActivityChooserContent = (LinearLayoutCompat) findViewById(R.id.activity_chooser_view_content);
+        mActivityChooserContent = findViewById(R.id.activity_chooser_view_content);
         mActivityChooserContentBackground = mActivityChooserContent.getBackground();
 
-        mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button);
+        mDefaultActivityButton = findViewById(R.id.default_activity_button);
         mDefaultActivityButton.setOnClickListener(mCallbacks);
         mDefaultActivityButton.setOnLongClickListener(mCallbacks);
         mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
 
-        final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button);
+        final FrameLayout expandButton = findViewById(R.id.expand_activities_button);
         expandButton.setOnClickListener(mCallbacks);
+        expandButton.setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                AccessibilityNodeInfoCompat.wrap(info).setCanOpenPopup(true);
+            }
+        });
         expandButton.setOnTouchListener(new ForwardingListener(expandButton) {
             @Override
             public ShowableListMenu getPopup() {
@@ -385,6 +395,7 @@
             }
             popupWindow.getListView().setContentDescription(getContext().getString(
                     R.string.abc_activitychooserview_choose_application));
+            popupWindow.getListView().setSelector(new ColorDrawable(Color.TRANSPARENT));
         }
     }
 
@@ -531,7 +542,7 @@
         // Default activity button.
         final int activityCount = mAdapter.getActivityCount();
         final int historySize = mAdapter.getHistorySize();
-        if (activityCount==1 || activityCount > 1 && historySize > 0) {
+        if (activityCount == 1 || (activityCount > 1 && historySize > 0)) {
             mDefaultActivityButton.setVisibility(VISIBLE);
             ResolveInfo activity = mAdapter.getDefaultActivity();
             PackageManager packageManager = getContext().getPackageManager();
@@ -762,9 +773,9 @@
                     titleView.setText(activity.loadLabel(packageManager));
                     // Highlight the default.
                     if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
-                        ViewCompat.setActivated(convertView, true);
+                        convertView.setActivated(true);
                     } else {
-                        ViewCompat.setActivated(convertView, false);
+                        convertView.setActivated(false);
                     }
                     return convertView;
                 default:
@@ -859,4 +870,4 @@
             a.recycle();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AlertDialogLayout.java b/v7/appcompat/src/android/support/v7/widget/AlertDialogLayout.java
index 5e650b2..6739c81 100644
--- a/v7/appcompat/src/android/support/v7/widget/AlertDialogLayout.java
+++ b/v7/appcompat/src/android/support/v7/widget/AlertDialogLayout.java
@@ -106,8 +106,7 @@
             topPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
 
             usedHeight += topPanel.getMeasuredHeight();
-            childState = ViewCompat.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(topPanel));
+            childState = View.combineMeasuredStates(childState, topPanel.getMeasuredState());
         }
 
         int buttonHeight = 0;
@@ -118,8 +117,7 @@
             buttonWantsHeight = buttonPanel.getMeasuredHeight() - buttonHeight;
 
             usedHeight += buttonHeight;
-            childState = ViewCompat.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(buttonPanel));
+            childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState());
         }
 
         int middleHeight = 0;
@@ -136,8 +134,7 @@
             middleHeight = middlePanel.getMeasuredHeight();
 
             usedHeight += middleHeight;
-            childState = ViewCompat.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(middlePanel));
+            childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState());
         }
 
         int remainingHeight = heightSize - usedHeight;
@@ -159,8 +156,7 @@
             buttonPanel.measure(widthMeasureSpec, childHeightSpec);
 
             usedHeight += buttonPanel.getMeasuredHeight();
-            childState = ViewCompat.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(buttonPanel));
+            childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState());
         }
 
         // If we still have remaining space, make the middle pane bigger up
@@ -180,8 +176,7 @@
             middlePanel.measure(widthMeasureSpec, childHeightSpec);
 
             usedHeight += middlePanel.getMeasuredHeight();
-            childState = ViewCompat.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(middlePanel));
+            childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState());
         }
 
         // Compute desired width as maximum child width.
@@ -195,9 +190,9 @@
 
         maxWidth += getPaddingLeft() + getPaddingRight();
 
-        final int widthSizeAndState = ViewCompat.resolveSizeAndState(
+        final int widthSizeAndState = View.resolveSizeAndState(
                 maxWidth, widthMeasureSpec, childState);
-        final int heightSizeAndState = ViewCompat.resolveSizeAndState(
+        final int heightSizeAndState = View.resolveSizeAndState(
                 usedHeight, heightMeasureSpec, 0);
         setMeasuredDimension(widthSizeAndState, heightSizeAndState);
 
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java
index 96a481e..5b0a2f8 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java
@@ -32,18 +32,18 @@
 import android.widget.AutoCompleteTextView;
 
 /**
- * A {@link AutoCompleteTextView} which supports compatible features on older version of the
+ * A {@link AutoCompleteTextView} which supports compatible features on older versions of the
  * platform, including:
  * <ul>
- *     <li>Supports {@link R.attr#textAllCaps} style attribute which works back to
- *     {@link android.os.Build.VERSION_CODES#GINGERBREAD Gingerbread}.</li>
  *     <li>Allows dynamic tint of its background via the background tint methods in
  *     {@link android.support.v4.view.ViewCompat}.</li>
  *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
  *     {@link R.attr#backgroundTintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link AutoCompleteTextView} in your layouts.
+ * <p>This will automatically be used when you use {@link AutoCompleteTextView} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatAutoCompleteTextView extends AutoCompleteTextView implements
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
index c2075b9..2156dce 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
@@ -18,38 +18,43 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.TintableBackgroundView;
+import android.support.v4.widget.AutoSizeableTextView;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
+import android.widget.TextView;
 
 /**
- * A {@link Button} which supports compatible features on older version of the platform,
+ * A {@link Button} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
- *     <li>Supports {@link R.attr#textAllCaps} style attribute which works back to
- *     {@link android.os.Build.VERSION_CODES#GINGERBREAD Gingerbread}.</li>
  *     <li>Allows dynamic tint of its background via the background tint methods in
  *     {@link android.support.v4.view.ViewCompat}.</li>
  *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
  *     {@link R.attr#backgroundTintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link android.widget.Button} in your
- * layouts. You should only need to manually use this class when writing custom views.</p>
+ * <p>This will automatically be used when you use {@link Button} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
+ * You should only need to manually use this class when writing custom views.</p>
  */
-public class AppCompatButton extends Button implements TintableBackgroundView {
+public class AppCompatButton extends Button implements TintableBackgroundView,
+        AutoSizeableTextView {
 
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
@@ -171,13 +176,175 @@
     }
 
     @RequiresApi(14)
-    @TargetApi(14)
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(Button.class.getName());
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mTextHelper != null) {
+            mTextHelper.onLayout(changed, left, top, right, bottom);
+        }
+    }
+
+    @Override
+    public void setTextSize(int unit, float size) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setTextSize(unit, size);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setTextSize(unit, size);
+            }
+        }
+    }
+
+    @Override
+    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+        if (mTextHelper != null && Build.VERSION.SDK_INT < 26 && mTextHelper.isAutoSizeEnabled()) {
+            mTextHelper.autoSizeText();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeWithDefaults(
+            @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeUniformWithConfiguration(
+                    autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeUniformWithConfiguration(
+                        autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    @TextViewCompat.AutoSizeTextType
+    public int getAutoSizeTextType() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM
+                    ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
+                    : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeTextType();
+            }
+        }
+        return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeStepGranularity() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeStepGranularity();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeStepGranularity();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeMinTextSize() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeMinTextSize();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeMinTextSize();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeMaxTextSize() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeMaxTextSize();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeMaxTextSize();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int[] getAutoSizeTextAvailableSizes() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeTextAvailableSizes();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeTextAvailableSizes();
+            }
+        }
+        return new int[0];
+    }
+
     /**
      * Sets the properties of this field to transform input to ALL CAPS
      * display. This may use a "small caps" formatting if available.
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
index 5809d25..0d4f4cb 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
@@ -32,7 +32,7 @@
 import android.widget.CheckBox;
 
 /**
- * A {@link CheckBox} which supports compatible features on older version of the platform,
+ * A {@link CheckBox} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
  *     <li>Allows dynamic tint of its background via the background tint methods in
@@ -41,7 +41,9 @@
  *     {@link R.attr#buttonTintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link CheckBox} in your layouts.
+ * <p>This will automatically be used when you use {@link CheckBox} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatCheckBox extends CheckBox implements TintableCompoundButton {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
index 1725c24..921f0a2 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
@@ -23,9 +23,11 @@
 import android.widget.CheckedTextView;
 
 /**
- * A {@link CheckedTextView} which supports compatible features on older version of the platform.
+ * A {@link CheckedTextView} which supports compatible features on older versions of the platform.
  *
- * <p>This will automatically be used when you use {@link CheckedTextView} in your layouts.
+ * <p>This will automatically be used when you use {@link CheckedTextView} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatCheckedTextView extends CheckedTextView {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index 2437e51..5165574 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -23,8 +23,6 @@
 import static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
 import static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
 
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -533,7 +531,7 @@
             } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
                 tint = getColorStateList(context, R.color.abc_tint_switch_track);
             } else if (resId == R.drawable.abc_switch_thumb_material) {
-                tint = getColorStateList(context, R.color.abc_tint_switch_thumb);
+                tint = createSwitchThumbColorStateList(context);
             } else if (resId == R.drawable.abc_btn_default_mtrl_shape) {
                 tint = createDefaultButtonColorStateList(context);
             } else if (resId == R.drawable.abc_btn_borderless_material) {
@@ -626,6 +624,52 @@
         return new ColorStateList(states, colors);
     }
 
+    private ColorStateList createSwitchThumbColorStateList(Context context) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
+
+        final ColorStateList thumbColor = getThemeAttrColorStateList(context,
+                R.attr.colorSwitchThumbNormal);
+
+        if (thumbColor != null && thumbColor.isStateful()) {
+            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
+            // disabled colors from it
+
+            // Disabled state
+            states[i] = ThemeUtils.DISABLED_STATE_SET;
+            colors[i] = thumbColor.getColorForState(states[i], 0);
+            i++;
+
+            states[i] = ThemeUtils.CHECKED_STATE_SET;
+            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
+            i++;
+
+            // Default enabled state
+            states[i] = ThemeUtils.EMPTY_STATE_SET;
+            colors[i] = thumbColor.getDefaultColor();
+            i++;
+        } else {
+            // Else we'll use an approximation using the default disabled alpha
+
+            // Disabled state
+            states[i] = ThemeUtils.DISABLED_STATE_SET;
+            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
+            i++;
+
+            states[i] = ThemeUtils.CHECKED_STATE_SET;
+            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
+            i++;
+
+            // Default enabled state
+            states[i] = ThemeUtils.EMPTY_STATE_SET;
+            colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
+            i++;
+        }
+
+        return new ColorStateList(states, colors);
+    }
+
     private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
 
         public ColorFilterLruCache(int maxSize) {
@@ -725,7 +769,6 @@
         VdcInflateDelegate() {
         }
 
-        @SuppressLint("NewApi")
         @Override
         public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
                 @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
@@ -740,12 +783,10 @@
     }
 
     @RequiresApi(11)
-    @TargetApi(11)
     private static class AvdcInflateDelegate implements InflateDelegate {
         AvdcInflateDelegate() {
         }
 
-        @SuppressLint("NewApi")
         @Override
         public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
                 @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java b/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java
index d93f399..406e364 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java
@@ -31,19 +31,19 @@
 import android.widget.EditText;
 
 /**
- * A {@link EditText} which supports compatible features on older version of the platform,
+ * A {@link EditText} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
- *     <li>Supports {@link R.attr#textAllCaps} style attribute which works back to
- *     {@link android.os.Build.VERSION_CODES#GINGERBREAD Gingerbread}.</li>
  *     <li>Allows dynamic tint of its background via the background tint methods in
  *     {@link android.support.v4.view.ViewCompat}.</li>
  *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
  *     {@link R.attr#backgroundTintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link android.widget.EditText} in your
- * layouts. You should only need to manually use this class when writing custom views.</p>
+ * <p>This will automatically be used when you use {@link EditText} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
+ * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatEditText extends EditText implements TintableBackgroundView {
 
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java
index d0bcf00..90e6aa9 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java
@@ -37,7 +37,7 @@
 import android.widget.ImageView;
 
 /**
- * A {@link ImageButton} which supports compatible features on older version of the platform,
+ * A {@link ImageButton} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
  *     <li>Allows dynamic tint of its background via the background tint methods in
@@ -50,8 +50,10 @@
  *     {@link R.attr#tintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link android.widget.ImageButton} in your
- * layouts. You should only need to manually use this class when writing custom views.</p>
+ * <p>This will automatically be used when you use {@link ImageButton} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
+ * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatImageButton extends ImageButton implements TintableBackgroundView,
         TintableImageSourceView {
@@ -253,6 +255,7 @@
         }
     }
 
+    @Override
     public boolean hasOverlappingRendering() {
         return mImageHelper.hasOverlappingRendering() && super.hasOverlappingRendering();
     }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java
index 532a18b..0844f9a 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java
@@ -36,7 +36,7 @@
 import android.widget.ImageView;
 
 /**
- * A {@link ImageView} which supports compatible features on older version of the platform,
+ * A {@link ImageView} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
  *     <li>Allows dynamic tint of its background via the background tint methods in
@@ -49,8 +49,10 @@
  *     {@link R.attr#tintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link android.widget.ImageView} in your
- * layouts. You should only need to manually use this class when writing custom views.</p>
+ * <p>This will automatically be used when you use {@link ImageView} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
+ * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatImageView extends ImageView implements TintableBackgroundView,
         TintableImageSourceView {
@@ -264,6 +266,7 @@
         }
     }
 
+    @Override
     public boolean hasOverlappingRendering() {
         return mImageHelper.hasOverlappingRendering() && super.hasOverlappingRendering();
     }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatPopupWindow.java b/v7/appcompat/src/android/support/v7/widget/AppCompatPopupWindow.java
index c9e778d..097ce91 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatPopupWindow.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatPopupWindow.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
 import android.support.annotation.AttrRes;
@@ -50,7 +49,6 @@
         init(context, attrs, defStyleAttr, 0);
     }
 
-    @TargetApi(11)
     public AppCompatPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
             @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -95,7 +93,6 @@
         super.showAsDropDown(anchor, xoff, yoff);
     }
 
-    @TargetApi(Build.VERSION_CODES.KITKAT)
     @Override
     public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
         if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
index 8aa22f3..2a6bac0 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
@@ -32,7 +32,7 @@
 import android.widget.RadioButton;
 
 /**
- * A {@link RadioButton} which supports compatible features on older version of the platform,
+ * A {@link RadioButton} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
  *     <li>Allows dynamic tint of its background via the background tint methods in
@@ -41,7 +41,9 @@
  *     {@link R.attr#buttonTintMode}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link RadioButton} in your layouts.
+ * <p>This will automatically be used when you use {@link RadioButton} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatRadioButton extends RadioButton implements TintableCompoundButton {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
index 762e97a..b03b842 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
@@ -18,15 +18,17 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.RatingBar;
 
 /**
- * A {@link RatingBar} which supports compatible features on older version of the platform.
+ * A {@link RatingBar} which supports compatible features on older versions of the platform.
  *
- * <p>This will automatically be used when you use {@link RatingBar} in your layouts.
+ * <p>This will automatically be used when you use {@link RatingBar} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatRatingBar extends RatingBar {
@@ -55,7 +57,7 @@
         Bitmap sampleTile = mAppCompatProgressBarHelper.getSampleTime();
         if (sampleTile != null) {
             final int width = sampleTile.getWidth() * getNumStars();
-            setMeasuredDimension(ViewCompat.resolveSizeAndState(width, widthMeasureSpec, 0),
+            setMeasuredDimension(View.resolveSizeAndState(width, widthMeasureSpec, 0),
                     getMeasuredHeight());
         }
     }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
index 8b476b8..674e46b 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.widget;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.support.annotation.RequiresApi;
@@ -25,9 +24,11 @@
 import android.widget.SeekBar;
 
 /**
- * A {@link SeekBar} which supports compatible features on older version of the platform.
+ * A {@link SeekBar} which supports compatible features on older versions of the platform.
  *
- * <p>This will automatically be used when you use {@link SeekBar} in your layouts.
+ * <p>This will automatically be used when you use {@link SeekBar} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
 public class AppCompatSeekBar extends SeekBar {
@@ -50,7 +51,7 @@
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
+    protected synchronized void onDraw(Canvas canvas) {
         super.onDraw(canvas);
         mAppCompatSeekBarHelper.drawTickMarks(canvas);
     }
@@ -62,7 +63,6 @@
     }
 
     @RequiresApi(11)
-    @TargetApi(11)
     @Override
     public void jumpDrawablesToCurrentState() {
         super.jumpDrawablesToCurrentState();
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
index 93e1851..e1e62ee 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.widget;
 
-import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
@@ -143,7 +142,6 @@
     }
 
     @RequiresApi(11)
-    @TargetApi(11)
     void jumpDrawablesToCurrentState() {
         if (mTickMark != null) {
             mTickMark.jumpToCurrentState();
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
index 1be8ed8..8b6fab7 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
@@ -290,6 +290,7 @@
     /**
      * @return the context used to inflate the Spinner's popup or dialog window
      */
+    @Override
     public Context getPopupContext() {
         if (mPopup != null) {
             return mPopupContext;
@@ -299,6 +300,7 @@
         return null;
     }
 
+    @Override
     public void setPopupBackgroundDrawable(Drawable background) {
         if (mPopup != null) {
             mPopup.setBackgroundDrawable(background);
@@ -307,10 +309,12 @@
         }
     }
 
+    @Override
     public void setPopupBackgroundResource(@DrawableRes int resId) {
         setPopupBackgroundDrawable(AppCompatResources.getDrawable(getPopupContext(), resId));
     }
 
+    @Override
     public Drawable getPopupBackground() {
         if (mPopup != null) {
             return mPopup.getBackground();
@@ -320,6 +324,7 @@
         return null;
     }
 
+    @Override
     public void setDropDownVerticalOffset(int pixels) {
         if (mPopup != null) {
             mPopup.setVerticalOffset(pixels);
@@ -328,6 +333,7 @@
         }
     }
 
+    @Override
     public int getDropDownVerticalOffset() {
         if (mPopup != null) {
             return mPopup.getVerticalOffset();
@@ -337,6 +343,7 @@
         return 0;
     }
 
+    @Override
     public void setDropDownHorizontalOffset(int pixels) {
         if (mPopup != null) {
             mPopup.setHorizontalOffset(pixels);
@@ -351,6 +358,7 @@
      *
      * @return Horizontal offset in pixels
      */
+    @Override
     public int getDropDownHorizontalOffset() {
         if (mPopup != null) {
             return mPopup.getHorizontalOffset();
@@ -360,6 +368,7 @@
         return 0;
     }
 
+    @Override
     public void setDropDownWidth(int pixels) {
         if (mPopup != null) {
             mDropDownWidth = pixels;
@@ -368,6 +377,7 @@
         }
     }
 
+    @Override
     public int getDropDownWidth() {
         if (mPopup != null) {
             return mDropDownWidth;
@@ -620,37 +630,45 @@
             }
         }
 
+        @Override
         public int getCount() {
             return mAdapter == null ? 0 : mAdapter.getCount();
         }
 
+        @Override
         public Object getItem(int position) {
             return mAdapter == null ? null : mAdapter.getItem(position);
         }
 
+        @Override
         public long getItemId(int position) {
             return mAdapter == null ? -1 : mAdapter.getItemId(position);
         }
 
+        @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             return getDropDownView(position, convertView, parent);
         }
 
+        @Override
         public View getDropDownView(int position, View convertView, ViewGroup parent) {
             return (mAdapter == null) ? null
                     : mAdapter.getDropDownView(position, convertView, parent);
         }
 
+        @Override
         public boolean hasStableIds() {
             return mAdapter != null && mAdapter.hasStableIds();
         }
 
+        @Override
         public void registerDataSetObserver(DataSetObserver observer) {
             if (mAdapter != null) {
                 mAdapter.registerDataSetObserver(observer);
             }
         }
 
+        @Override
         public void unregisterDataSetObserver(DataSetObserver observer) {
             if (mAdapter != null) {
                 mAdapter.unregisterDataSetObserver(observer);
@@ -661,6 +679,7 @@
          * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
          * Otherwise, return true.
          */
+        @Override
         public boolean areAllItemsEnabled() {
             final ListAdapter adapter = mListAdapter;
             if (adapter != null) {
@@ -674,6 +693,7 @@
          * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
          * Otherwise, return true.
          */
+        @Override
         public boolean isEnabled(int position) {
             final ListAdapter adapter = mListAdapter;
             if (adapter != null) {
@@ -683,14 +703,17 @@
             }
         }
 
+        @Override
         public int getItemViewType(int position) {
             return 0;
         }
 
+        @Override
         public int getViewTypeCount() {
             return 1;
         }
 
+        @Override
         public boolean isEmpty() {
             return getCount() == 0;
         }
@@ -773,6 +796,7 @@
             setHorizontalOffset(hOffset);
         }
 
+        @Override
         public void show() {
             final boolean wasShowing = isShowing();
 
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
index c210036..afd59bd 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
@@ -16,20 +16,25 @@
 
 package android.support.v7.widget;
 
-import android.annotation.TargetApi;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
-import android.support.v7.text.AllCapsTransformationMethod;
 import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.widget.TextView;
 
 @RequiresApi(9)
-@TargetApi(9)
 class AppCompatTextHelper {
 
     static AppCompatTextHelper create(TextView textView) {
@@ -46,8 +51,14 @@
     private TintInfo mDrawableRightTint;
     private TintInfo mDrawableBottomTint;
 
+    private final @NonNull AppCompatTextViewAutoSizeHelper mAutoSizeTextHelper;
+
+    private int mStyle = Typeface.NORMAL;
+    private Typeface mFontTypeface;
+
     AppCompatTextHelper(TextView view) {
         mView = view;
+        mAutoSizeTextHelper = new AppCompatTextViewAutoSizeHelper(mView);
     }
 
     void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
@@ -86,6 +97,7 @@
         boolean allCapsSet = false;
         ColorStateList textColor = null;
         ColorStateList textColorHint = null;
+        ColorStateList textColorLink = null;
 
         // First check TextAppearance's textAllCaps value
         if (ap != -1) {
@@ -94,6 +106,8 @@
                 allCapsSet = true;
                 allCaps = a.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
             }
+
+            updateTypefaceAndStyle(context, a);
             if (Build.VERSION.SDK_INT < 23) {
                 // If we're running on < API 23, the text color may contain theme references
                 // so let's re-set using our own inflater
@@ -104,6 +118,10 @@
                     textColorHint = a.getColorStateList(
                             R.styleable.TextAppearance_android_textColorHint);
                 }
+                if (a.hasValue(R.styleable.TextAppearance_android_textColorLink)) {
+                    textColorLink = a.getColorStateList(
+                            R.styleable.TextAppearance_android_textColorLink);
+                }
             }
             a.recycle();
         }
@@ -125,7 +143,13 @@
                 textColorHint = a.getColorStateList(
                         R.styleable.TextAppearance_android_textColorHint);
             }
+            if (a.hasValue(R.styleable.TextAppearance_android_textColorLink)) {
+                textColorLink = a.getColorStateList(
+                        R.styleable.TextAppearance_android_textColorLink);
+            }
         }
+
+        updateTypefaceAndStyle(context, a);
         a.recycle();
 
         if (textColor != null) {
@@ -134,9 +158,64 @@
         if (textColorHint != null) {
             mView.setHintTextColor(textColorHint);
         }
+        if (textColorLink != null) {
+            mView.setLinkTextColor(textColorLink);
+        }
         if (!hasPwdTm && allCapsSet) {
             setAllCaps(allCaps);
         }
+        if (mFontTypeface != null) {
+            mView.setTypeface(mFontTypeface, mStyle);
+        }
+
+        mAutoSizeTextHelper.loadFromAttributes(attrs, defStyleAttr);
+
+        if (Build.VERSION.SDK_INT >= 26) {
+            // Delegate auto-size functionality to the framework implementation.
+            if (mAutoSizeTextHelper.getAutoSizeTextType()
+                    != TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) {
+                final int[] autoSizeTextSizesInPx =
+                        mAutoSizeTextHelper.getAutoSizeTextAvailableSizes();
+                if (autoSizeTextSizesInPx.length > 0) {
+                    if (mView.getAutoSizeStepGranularity() != AppCompatTextViewAutoSizeHelper
+                            .UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        // Configured with granularity, preserve details.
+                        mView.setAutoSizeTextTypeUniformWithConfiguration(
+                                mAutoSizeTextHelper.getAutoSizeMinTextSize(),
+                                mAutoSizeTextHelper.getAutoSizeMaxTextSize(),
+                                mAutoSizeTextHelper.getAutoSizeStepGranularity(),
+                                TypedValue.COMPLEX_UNIT_PX);
+                    } else {
+                        mView.setAutoSizeTextTypeUniformWithPresetSizes(
+                                autoSizeTextSizesInPx, TypedValue.COMPLEX_UNIT_PX);
+                    }
+                }
+            }
+        }
+    }
+
+    private void updateTypefaceAndStyle(Context context, TintTypedArray a) {
+        mStyle = a.getInt(R.styleable.TextAppearance_android_textStyle, mStyle);
+
+        if (a.hasValue(R.styleable.TextAppearance_android_fontFamily)
+                || a.hasValue(R.styleable.TextAppearance_fontFamily)) {
+            mFontTypeface = null;
+            int fontFamilyId = a.hasValue(R.styleable.TextAppearance_android_fontFamily)
+                    ? R.styleable.TextAppearance_android_fontFamily
+                    : R.styleable.TextAppearance_fontFamily;
+            if (!context.isRestricted()) {
+                try {
+                    mFontTypeface = a.getFont(fontFamilyId, mStyle, mView);
+                } catch (UnsupportedOperationException | Resources.NotFoundException e) {
+                    // Expected if it is not a font resource.
+                }
+            }
+            if (mFontTypeface == null) {
+                // Try with String. This is done by TextView JB+, but fails in ICS
+                String fontFamilyName = a.getString(fontFamilyId);
+                mFontTypeface = Typeface.create(fontFamilyName, mStyle);
+            }
+        }
     }
 
     void onSetTextAppearance(Context context, int resId) {
@@ -159,13 +238,16 @@
                 mView.setTextColor(textColor);
             }
         }
+
+        updateTypefaceAndStyle(context, a);
         a.recycle();
+        if (mFontTypeface != null) {
+            mView.setTypeface(mFontTypeface, mStyle);
+        }
     }
 
     void setAllCaps(boolean allCaps) {
-        mView.setTransformationMethod(allCaps
-                ? new AllCapsTransformationMethod(mView.getContext())
-                : null);
+        mView.setAllCaps(allCaps);
     }
 
     void applyCompoundDrawablesTints() {
@@ -196,4 +278,78 @@
         }
         return null;
     }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        // Auto-size is supported by the framework starting from Android O.
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            autoSizeText();
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    void setTextSize(int unit, float size) {
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            if (!isAutoSizeEnabled()) {
+                setTextSizeInternal(unit, size);
+            }
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    void autoSizeText() {
+        mAutoSizeTextHelper.autoSizeText();
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    boolean isAutoSizeEnabled() {
+        return mAutoSizeTextHelper.isAutoSizeEnabled();
+    }
+
+    private void setTextSizeInternal(int unit, float size) {
+        mAutoSizeTextHelper.setTextSizeInternal(unit, size);
+    }
+
+    void setAutoSizeTextTypeWithDefaults(@TextViewCompat.AutoSizeTextType int autoSizeTextType) {
+        mAutoSizeTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+    }
+
+    void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        mAutoSizeTextHelper.setAutoSizeTextTypeUniformWithConfiguration(
+                autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+    }
+
+    void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException {
+        mAutoSizeTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+    }
+
+    @TextViewCompat.AutoSizeTextType
+    int getAutoSizeTextType() {
+        return mAutoSizeTextHelper.getAutoSizeTextType();
+    }
+
+    int getAutoSizeStepGranularity() {
+        return mAutoSizeTextHelper.getAutoSizeStepGranularity();
+    }
+
+    int getAutoSizeMinTextSize() {
+        return mAutoSizeTextHelper.getAutoSizeMinTextSize();
+    }
+
+    int getAutoSizeMaxTextSize() {
+        return mAutoSizeTextHelper.getAutoSizeMaxTextSize();
+    }
+
+    int[] getAutoSizeTextAvailableSizes() {
+        return mAutoSizeTextHelper.getAutoSizeTextAvailableSizes();
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
index e022756..d5cc872 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.widget;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
@@ -26,7 +25,6 @@
 import android.widget.TextView;
 
 @RequiresApi(17)
-@TargetApi(17)
 class AppCompatTextHelperV17 extends AppCompatTextHelper {
     private TintInfo mDrawableStartTint;
     private TintInfo mDrawableEndTint;
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
index 96dfd5a..12d0ff9 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
@@ -22,30 +22,43 @@
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.TintableBackgroundView;
+import android.support.v4.widget.AutoSizeableTextView;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
 /**
- * A {@link TextView} which supports compatible features on older version of the platform,
+ * A {@link TextView} which supports compatible features on older versions of the platform,
  * including:
  * <ul>
- *     <li>Supports {@link R.attr#textAllCaps} style attribute which works back to
- *     {@link android.os.Build.VERSION_CODES#GINGERBREAD Gingerbread}.</li>
  *     <li>Allows dynamic tint of its background via the background tint methods in
  *     {@link android.support.v4.view.ViewCompat}.</li>
  *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
  *     {@link R.attr#backgroundTintMode}.</li>
+ *     <li>Supports auto-sizing via {@link android.support.v4.widget.TextViewCompat} by allowing
+ *     to instruct a {@link TextView} to let the size of the text expand or contract automatically
+ *     to fill its layout based on the TextView's characteristics and boundaries. The
+ *     style attributes associated with auto-sizing are {@link R.attr#autoSizeTextType},
+ *     {@link R.attr#autoSizeMinTextSize}, {@link R.attr#autoSizeMaxTextSize},
+ *     {@link R.attr#autoSizeStepGranularity} and {@link R.attr#autoSizePresetSizes}, all of
+ *     which work back to
+ *     {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH Ice Cream Sandwich}.</li>
  * </ul>
  *
- * <p>This will automatically be used when you use {@link TextView} in your layouts.
+ * <p>This will automatically be used when you use {@link TextView} in your layouts
+ * and the top-level activity / dialog is provided by
+ * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
-public class AppCompatTextView extends TextView implements TintableBackgroundView {
+public class AppCompatTextView extends TextView implements TintableBackgroundView,
+        AutoSizeableTextView {
 
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
@@ -159,4 +172,194 @@
             mTextHelper.applyCompoundDrawablesTints();
         }
     }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mTextHelper != null) {
+            mTextHelper.onLayout(changed, left, top, right, bottom);
+        }
+    }
+
+    @Override
+    public void setTextSize(int unit, float size) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setTextSize(unit, size);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setTextSize(unit, size);
+            }
+        }
+    }
+
+    @Override
+    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+        if (mTextHelper != null && Build.VERSION.SDK_INT < 26 && mTextHelper.isAutoSizeEnabled()) {
+            mTextHelper.autoSizeText();
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeWithDefaults(
+     *        TextView, int)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeWithDefaults(
+            @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+            }
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeUniformWithConfiguration(
+     *        TextView, int, int, int, int)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeUniformWithConfiguration(
+                    autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeUniformWithConfiguration(
+                        autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+            }
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeUniformWithPresetSizes(
+     *        TextView, int[], int)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+            }
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeTextType(TextView)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    @TextViewCompat.AutoSizeTextType
+    public int getAutoSizeTextType() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM
+                    ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
+                    : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeTextType();
+            }
+        }
+        return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeStepGranularity(TextView)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeStepGranularity() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeStepGranularity();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeStepGranularity();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeMinTextSize(TextView)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeMinTextSize() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeMinTextSize();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeMinTextSize();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeMaxTextSize(TextView)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeMaxTextSize() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeMaxTextSize();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeMaxTextSize();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeTextAvailableSizes(TextView)}
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int[] getAutoSizeTextAvailableSizes() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeTextAvailableSizes();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeTextAvailableSizes();
+            }
+        }
+        return new int[0];
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
new file mode 100644
index 0000000..29449f5
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
@@ -0,0 +1,812 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.RectF;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.widget.TextViewCompat;
+import android.support.v7.appcompat.R;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ * Utility class which encapsulates the logic for the TextView auto-size text feature added to
+ * the Android Framework in {@link android.os.Build.VERSION_CODES#O}.
+ *
+ * <p>A TextView can be instructed to let the size of the text expand or contract automatically to
+ * fill its layout based on the TextView's characteristics and boundaries.
+ */
+class AppCompatTextViewAutoSizeHelper {
+    private static final String TAG = "ACTVAutoSizeHelper";
+    private static final RectF TEMP_RECTF = new RectF();
+    // Default minimum size for auto-sizing text in scaled pixels.
+    private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12;
+    // Default maximum size for auto-sizing text in scaled pixels.
+    private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112;
+    // Default value for the step size in pixels.
+    private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
+    // Cache of TextView methods used via reflection; the key is the method name and the value is
+    // the method itself or null if it can not be found.
+    private static Hashtable<String, Method> sTextViewMethodByNameCache = new Hashtable<>();
+    // Use this to specify that any of the auto-size configuration int values have not been set.
+    static final float UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1f;
+    // Ported from TextView#VERY_WIDE. Represents a maximum width in pixels the TextView takes when
+    // horizontal scrolling is activated.
+    private static final int VERY_WIDE = 1024 * 1024;
+    // Auto-size text type.
+    private int mAutoSizeTextType = TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+    // Specify if auto-size text is needed.
+    private boolean mNeedsAutoSizeText = false;
+    // Step size for auto-sizing in pixels.
+    private float mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+    // Minimum text size for auto-sizing in pixels.
+    private float mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+    // Maximum text size for auto-sizing in pixels.
+    private float mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+    // Contains a (specified or computed) distinct sorted set of text sizes in pixels to pick from
+    // when auto-sizing text.
+    private int[] mAutoSizeTextSizesInPx = new int[0];
+    // Specifies whether auto-size should use the provided auto size steps set or if it should
+    // build the steps set using mAutoSizeMinTextSizeInPx, mAutoSizeMaxTextSizeInPx and
+    // mAutoSizeStepGranularityInPx.
+    private boolean mHasPresetAutoSizeValues = false;
+    private TextPaint mTempTextPaint;
+
+    private final TextView mTextView;
+    private final Context mContext;
+
+    AppCompatTextViewAutoSizeHelper(TextView textView) {
+        mTextView = textView;
+        mContext = mTextView.getContext();
+    }
+
+    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+        float autoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        float autoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        float autoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+
+        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView,
+                defStyleAttr, 0);
+        if (a.hasValue(R.styleable.AppCompatTextView_autoSizeTextType)) {
+            mAutoSizeTextType = a.getInt(R.styleable.AppCompatTextView_autoSizeTextType,
+                    TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
+        }
+        if (a.hasValue(R.styleable.AppCompatTextView_autoSizeStepGranularity)) {
+            autoSizeStepGranularityInPx = a.getDimension(
+                    R.styleable.AppCompatTextView_autoSizeStepGranularity,
+                    UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
+        }
+        if (a.hasValue(R.styleable.AppCompatTextView_autoSizeMinTextSize)) {
+            autoSizeMinTextSizeInPx = a.getDimension(
+                    R.styleable.AppCompatTextView_autoSizeMinTextSize,
+                    UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
+        }
+        if (a.hasValue(R.styleable.AppCompatTextView_autoSizeMaxTextSize)) {
+            autoSizeMaxTextSizeInPx = a.getDimension(
+                    R.styleable.AppCompatTextView_autoSizeMaxTextSize,
+                    UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
+        }
+        if (a.hasValue(R.styleable.AppCompatTextView_autoSizePresetSizes)) {
+            final int autoSizeStepSizeArrayResId = a.getResourceId(
+                    R.styleable.AppCompatTextView_autoSizePresetSizes, 0);
+            if (autoSizeStepSizeArrayResId > 0) {
+                final TypedArray autoSizePreDefTextSizes = a.getResources()
+                        .obtainTypedArray(autoSizeStepSizeArrayResId);
+                setupAutoSizeUniformPresetSizes(autoSizePreDefTextSizes);
+                autoSizePreDefTextSizes.recycle();
+            }
+        }
+        a.recycle();
+
+        if (supportsAutoSizeText()) {
+            if (mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) {
+                // If uniform auto-size has been specified but preset values have not been set then
+                // replace the auto-size configuration values that have not been specified with the
+                // defaults.
+                if (!mHasPresetAutoSizeValues) {
+                    final DisplayMetrics displayMetrics =
+                            mContext.getResources().getDisplayMetrics();
+
+                    if (autoSizeMinTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        autoSizeMinTextSizeInPx = TypedValue.applyDimension(
+                                TypedValue.COMPLEX_UNIT_SP,
+                                DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
+                                displayMetrics);
+                    }
+
+                    if (autoSizeMaxTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
+                                TypedValue.COMPLEX_UNIT_SP,
+                                DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
+                                displayMetrics);
+                    }
+
+                    if (autoSizeStepGranularityInPx
+                            == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        autoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
+                    }
+
+                    validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
+                            autoSizeMaxTextSizeInPx,
+                            autoSizeStepGranularityInPx);
+                }
+
+                setupAutoSizeText();
+            }
+        } else {
+            mAutoSizeTextType = TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+        }
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds by using the default auto-size configuration.
+     *
+     * @param autoSizeTextType the type of auto-size. Must be one of
+     *        {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *        {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}
+     *
+     * @attr ref R.styleable#AppCompatTextView_autoSizeTextType
+     *
+     * @see #getAutoSizeTextType()
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void setAutoSizeTextTypeWithDefaults(@TextViewCompat.AutoSizeTextType int autoSizeTextType) {
+        if (supportsAutoSizeText()) {
+            switch (autoSizeTextType) {
+                case TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE:
+                    clearAutoSizeConfiguration();
+                    break;
+                case TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM:
+                    final DisplayMetrics displayMetrics =
+                            mContext.getResources().getDisplayMetrics();
+                    final float autoSizeMinTextSizeInPx = TypedValue.applyDimension(
+                            TypedValue.COMPLEX_UNIT_SP,
+                            DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
+                            displayMetrics);
+                    final float autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
+                            TypedValue.COMPLEX_UNIT_SP,
+                            DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
+                            displayMetrics);
+
+                    validateAndSetAutoSizeTextTypeUniformConfiguration(
+                            autoSizeMinTextSizeInPx,
+                            autoSizeMaxTextSizeInPx,
+                            DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
+                    if (setupAutoSizeText()) {
+                        autoSizeText();
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown auto-size text type: " + autoSizeTextType);
+            }
+        }
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If all the configuration params are valid the type of auto-size is
+     * set to {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param autoSizeMinTextSize the minimum text size available for auto-size
+     * @param autoSizeMaxTextSize the maximum text size available for auto-size
+     * @param autoSizeStepGranularity the auto-size step granularity. It is used in conjunction with
+     *                                the minimum and maximum text size in order to build the set of
+     *                                text sizes the system uses to choose from when auto-sizing
+     * @param unit the desired dimension unit for all sizes above. See {@link TypedValue} for the
+     *             possible dimension units
+     *
+     * @throws IllegalArgumentException if any of the configuration params are invalid.
+     *
+     * @attr ref R.styleable#AppCompatTextView_autoSizeTextType
+     * @attr ref R.styleable#AppCompatTextView_autoSizeMinTextSize
+     * @attr ref R.styleable#AppCompatTextView_autoSizeMaxTextSize
+     * @attr ref R.styleable#AppCompatTextView_autoSizeStepGranularity
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     * @see #getAutoSizeMinTextSize()
+     * @see #getAutoSizeMaxTextSize()
+     * @see #getAutoSizeStepGranularity()
+     * @see #getAutoSizeTextAvailableSizes()
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        if (supportsAutoSizeText()) {
+            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+            final float autoSizeMinTextSizeInPx = TypedValue.applyDimension(
+                    unit, autoSizeMinTextSize, displayMetrics);
+            final float autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
+                    unit, autoSizeMaxTextSize, displayMetrics);
+            final float autoSizeStepGranularityInPx = TypedValue.applyDimension(
+                    unit, autoSizeStepGranularity, displayMetrics);
+
+            validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
+                    autoSizeMaxTextSizeInPx,
+                    autoSizeStepGranularityInPx);
+            if (setupAutoSizeText()) {
+                autoSizeText();
+            }
+        }
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If at least one value from the <code>presetSizes</code> is valid
+     * then the type of auto-size is set to {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param presetSizes an {@code int} array of sizes in pixels
+     * @param unit the desired dimension unit for the preset sizes above. See {@link TypedValue} for
+     *             the possible dimension units
+     *
+     * @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid.
+     *_
+     * @attr ref R.styleable#AppCompatTextView_autoSizeTextType
+     * @attr ref R.styleable#AppCompatTextView_autoSizePresetSizes
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #getAutoSizeMinTextSize()
+     * @see #getAutoSizeMaxTextSize()
+     * @see #getAutoSizeTextAvailableSizes()
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException {
+        if (supportsAutoSizeText()) {
+            final int presetSizesLength = presetSizes.length;
+            if (presetSizesLength > 0) {
+                int[] presetSizesInPx = new int[presetSizesLength];
+
+                if (unit == TypedValue.COMPLEX_UNIT_PX) {
+                    presetSizesInPx = Arrays.copyOf(presetSizes, presetSizesLength);
+                } else {
+                    final DisplayMetrics displayMetrics =
+                            mContext.getResources().getDisplayMetrics();
+                    // Convert all to sizes to pixels.
+                    for (int i = 0; i < presetSizesLength; i++) {
+                        presetSizesInPx[i] = Math.round(TypedValue.applyDimension(unit,
+                                presetSizes[i], displayMetrics));
+                    }
+                }
+
+                mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(presetSizesInPx);
+                if (!setupAutoSizeUniformPresetSizesConfiguration()) {
+                    throw new IllegalArgumentException("None of the preset sizes is valid: "
+                            + Arrays.toString(presetSizes));
+                }
+            } else {
+                mHasPresetAutoSizeValues = false;
+            }
+
+            if (setupAutoSizeText()) {
+                autoSizeText();
+            }
+        }
+    }
+
+    /**
+     * Returns the type of auto-size set for this widget.
+     *
+     * @return an {@code int} corresponding to one of the auto-size types:
+     *         {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *         {@link TextViewCompat#AUTO_SIZE_TEXT_TYPE_UNIFORM}
+     *
+     * @attr ref R.styleable#AppCompatTextView_autoSizeTextType
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @TextViewCompat.AutoSizeTextType
+    int getAutoSizeTextType() {
+        return mAutoSizeTextType;
+    }
+
+    /**
+     * @return the current auto-size step granularity in pixels.
+     *
+     * @attr ref R.styleable#AppCompatTextView_autoSizeStepGranularity
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getAutoSizeStepGranularity() {
+        return Math.round(mAutoSizeStepGranularityInPx);
+    }
+
+    /**
+     * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
+     *
+     * @attr ref R.styleable#AppCompatTextView_autoSizeMinTextSize
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getAutoSizeMinTextSize() {
+        return Math.round(mAutoSizeMinTextSizeInPx);
+    }
+
+    /**
+     * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
+     *
+     * @attr ref R.styleable#AppCompatTextView_autoSizeMaxTextSize
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int getAutoSizeMaxTextSize() {
+        return Math.round(mAutoSizeMaxTextSizeInPx);
+    }
+
+    /**
+     * @return the current auto-size {@code int} sizes array (in pixels).
+     *
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int[] getAutoSizeTextAvailableSizes() {
+        return mAutoSizeTextSizesInPx;
+    }
+
+    private void setupAutoSizeUniformPresetSizes(TypedArray textSizes) {
+        final int textSizesLength = textSizes.length();
+        final int[] parsedSizes = new int[textSizesLength];
+
+        if (textSizesLength > 0) {
+            for (int i = 0; i < textSizesLength; i++) {
+                parsedSizes[i] = textSizes.getDimensionPixelSize(i, -1);
+            }
+            mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(parsedSizes);
+            setupAutoSizeUniformPresetSizesConfiguration();
+        }
+    }
+
+    private boolean setupAutoSizeUniformPresetSizesConfiguration() {
+        final int sizesLength = mAutoSizeTextSizesInPx.length;
+        mHasPresetAutoSizeValues = sizesLength > 0;
+        if (mHasPresetAutoSizeValues) {
+            mAutoSizeTextType = TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM;
+            mAutoSizeMinTextSizeInPx = mAutoSizeTextSizesInPx[0];
+            mAutoSizeMaxTextSizeInPx = mAutoSizeTextSizesInPx[sizesLength - 1];
+            mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        }
+        return mHasPresetAutoSizeValues;
+    }
+
+    // Returns distinct sorted positive values.
+    private int[] cleanupAutoSizePresetSizes(int[] presetValues) {
+        final int presetValuesLength = presetValues.length;
+        if (presetValuesLength == 0) {
+            return presetValues;
+        }
+        Arrays.sort(presetValues);
+
+        final List<Integer> uniqueValidSizes = new ArrayList<>();
+        for (int i = 0; i < presetValuesLength; i++) {
+            final int currentPresetValue = presetValues[i];
+
+            if (currentPresetValue > 0
+                    && Collections.binarySearch(uniqueValidSizes, currentPresetValue) < 0) {
+                uniqueValidSizes.add(currentPresetValue);
+            }
+        }
+
+        if (presetValuesLength == uniqueValidSizes.size()) {
+            return presetValues;
+        } else {
+            final int uniqueValidSizesLength = uniqueValidSizes.size();
+            final int[] cleanedUpSizes = new int[uniqueValidSizesLength];
+            for (int i = 0; i < uniqueValidSizesLength; i++) {
+                cleanedUpSizes[i] = uniqueValidSizes.get(i);
+            }
+            return cleanedUpSizes;
+        }
+    }
+
+    /**
+     * If all params are valid then save the auto-size configuration.
+     *
+     * @throws IllegalArgumentException if any of the params are invalid
+     */
+    private void validateAndSetAutoSizeTextTypeUniformConfiguration(
+            float autoSizeMinTextSizeInPx,
+            float autoSizeMaxTextSizeInPx,
+            float autoSizeStepGranularityInPx) throws IllegalArgumentException {
+        // First validate.
+        if (autoSizeMinTextSizeInPx <= 0) {
+            throw new IllegalArgumentException("Minimum auto-size text size ("
+                    + autoSizeMinTextSizeInPx  + "px) is less or equal to (0px)");
+        }
+
+        if (autoSizeMaxTextSizeInPx <= autoSizeMinTextSizeInPx) {
+            throw new IllegalArgumentException("Maximum auto-size text size ("
+                    + autoSizeMaxTextSizeInPx + "px) is less or equal to minimum auto-size "
+                    + "text size (" + autoSizeMinTextSizeInPx + "px)");
+        }
+
+        if (autoSizeStepGranularityInPx <= 0) {
+            throw new IllegalArgumentException("The auto-size step granularity ("
+                    + autoSizeStepGranularityInPx + "px) is less or equal to (0px)");
+        }
+
+        // All good, persist the configuration.
+        mAutoSizeTextType = TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM;
+        mAutoSizeMinTextSizeInPx = autoSizeMinTextSizeInPx;
+        mAutoSizeMaxTextSizeInPx = autoSizeMaxTextSizeInPx;
+        mAutoSizeStepGranularityInPx = autoSizeStepGranularityInPx;
+        mHasPresetAutoSizeValues = false;
+    }
+
+    private boolean setupAutoSizeText() {
+        if (supportsAutoSizeText()
+                && mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) {
+            // Calculate the sizes set based on minimum size, maximum size and step size if we do
+            // not have a predefined set of sizes or if the current sizes array is empty.
+            if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
+                // Calculate sizes to choose from based on the current auto-size configuration.
+                int autoSizeValuesLength = 1;
+                float currentSize = Math.round(mAutoSizeMinTextSizeInPx);
+                while (Math.round(currentSize + mAutoSizeStepGranularityInPx)
+                        <= Math.round(mAutoSizeMaxTextSizeInPx)) {
+                    autoSizeValuesLength++;
+                    currentSize += mAutoSizeStepGranularityInPx;
+                }
+                int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
+                float sizeToAdd = mAutoSizeMinTextSizeInPx;
+                for (int i = 0; i < autoSizeValuesLength; i++) {
+                    autoSizeTextSizesInPx[i] = Math.round(sizeToAdd);
+                    sizeToAdd += mAutoSizeStepGranularityInPx;
+                }
+                mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
+            }
+
+            mNeedsAutoSizeText = true;
+        } else {
+            mNeedsAutoSizeText = false;
+        }
+
+        return mNeedsAutoSizeText;
+    }
+
+    /**
+     * Automatically computes and sets the text size.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void autoSizeText() {
+        if (!isAutoSizeEnabled()) {
+            return;
+        }
+
+        if (mNeedsAutoSizeText) {
+            if (mTextView.getMeasuredHeight() <= 0 || mTextView.getMeasuredWidth() <= 0) {
+                return;
+            }
+
+            final boolean horizontallyScrolling = invokeAndReturnWithDefault(
+                    mTextView, "getHorizontallyScrolling", false);
+            final int availableWidth = horizontallyScrolling
+                    ? VERY_WIDE
+                    : mTextView.getMeasuredWidth() - mTextView.getTotalPaddingLeft()
+                            - mTextView.getTotalPaddingRight();
+            final int availableHeight = mTextView.getHeight() - mTextView.getCompoundPaddingBottom()
+                    - mTextView.getCompoundPaddingTop();
+
+            if (availableWidth <= 0 || availableHeight <= 0) {
+                return;
+            }
+
+            synchronized (TEMP_RECTF) {
+                TEMP_RECTF.setEmpty();
+                TEMP_RECTF.right = availableWidth;
+                TEMP_RECTF.bottom = availableHeight;
+                final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
+                if (optimalTextSize != mTextView.getTextSize()) {
+                    setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize);
+                }
+            }
+        }
+        // Always try to auto-size if enabled. Functions that do not want to trigger auto-sizing
+        // after the next layout pass should set this to false.
+        mNeedsAutoSizeText = true;
+    }
+
+    private void clearAutoSizeConfiguration() {
+        mAutoSizeTextType = TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+        mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        mAutoSizeTextSizesInPx = new int[0];
+        mNeedsAutoSizeText = false;
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    void setTextSizeInternal(int unit, float size) {
+        Resources res = mContext == null
+                ? Resources.getSystem()
+                : mContext.getResources();
+
+        setRawTextSize(TypedValue.applyDimension(unit, size, res.getDisplayMetrics()));
+    }
+
+    private void setRawTextSize(float size) {
+        if (size != mTextView.getPaint().getTextSize()) {
+            mTextView.getPaint().setTextSize(size);
+
+            boolean isInLayout = false;
+            if (Build.VERSION.SDK_INT >= 18) {
+                isInLayout = mTextView.isInLayout();
+            }
+
+            if (mTextView.getLayout() != null) {
+                // Do not auto-size right after setting the text size.
+                mNeedsAutoSizeText = false;
+
+                final String methodName = "nullLayouts";
+                try {
+                    Method method = getTextViewMethod(methodName);
+                    if (method != null) {
+                        method.invoke(mTextView);
+                    }
+                } catch (Exception ex) {
+                    Log.w(TAG, "Failed to invoke TextView#" + methodName + "() method", ex);
+                }
+
+                if (!isInLayout) {
+                    mTextView.requestLayout();
+                } else {
+                    mTextView.forceLayout();
+                }
+
+                mTextView.invalidate();
+            }
+        }
+    }
+
+    /**
+     * Performs a binary search to find the largest text size that will still fit within the size
+     * available to this view.
+     */
+    private int findLargestTextSizeWhichFits(RectF availableSpace) {
+        final int sizesCount = mAutoSizeTextSizesInPx.length;
+        if (sizesCount == 0) {
+            throw new IllegalStateException("No available text sizes to choose from.");
+        }
+
+        int bestSizeIndex = 0;
+        int lowIndex = bestSizeIndex + 1;
+        int highIndex = sizesCount - 1;
+        int sizeToTryIndex;
+        while (lowIndex <= highIndex) {
+            sizeToTryIndex = (lowIndex + highIndex) / 2;
+            if (suggestedSizeFitsInSpace(mAutoSizeTextSizesInPx[sizeToTryIndex], availableSpace)) {
+                bestSizeIndex = lowIndex;
+                lowIndex = sizeToTryIndex + 1;
+            } else {
+                highIndex = sizeToTryIndex - 1;
+                bestSizeIndex = highIndex;
+            }
+        }
+
+        return mAutoSizeTextSizesInPx[bestSizeIndex];
+    }
+
+    private boolean suggestedSizeFitsInSpace(int suggestedSizeInPx, RectF availableSpace) {
+        final CharSequence text = mTextView.getText();
+        final int maxLines = Build.VERSION.SDK_INT >= 16 ? mTextView.getMaxLines() : -1;
+        if (mTempTextPaint == null) {
+            mTempTextPaint = new TextPaint();
+        } else {
+            mTempTextPaint.reset();
+        }
+        mTempTextPaint.set(mTextView.getPaint());
+        mTempTextPaint.setTextSize(suggestedSizeInPx);
+
+        // Needs reflection call due to being private.
+        Layout.Alignment alignment = invokeAndReturnWithDefault(
+                mTextView, "getLayoutAlignment", Layout.Alignment.ALIGN_NORMAL);
+        final StaticLayout layout = Build.VERSION.SDK_INT >= 23
+                ? createStaticLayoutForMeasuring(
+                        text, alignment, Math.round(availableSpace.right), maxLines)
+                : createStaticLayoutForMeasuringPre23(
+                        text, alignment, Math.round(availableSpace.right));
+        // Lines overflow.
+        if (maxLines != -1 && (layout.getLineCount() > maxLines
+                || (layout.getLineEnd(layout.getLineCount() - 1)) != text.length())) {
+            return false;
+        }
+
+        // Height overflow.
+        if (layout.getHeight() > availableSpace.bottom) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @TargetApi(23)
+    private StaticLayout createStaticLayoutForMeasuring(CharSequence text,
+            Layout.Alignment alignment, int availableWidth, int maxLines) {
+        // Can use the StaticLayout.Builder (along with TextView params added in or after
+        // API 23) to construct the layout.
+        final TextDirectionHeuristic textDirectionHeuristic = invokeAndReturnWithDefault(
+                mTextView, "getTextDirectionHeuristic",
+                TextDirectionHeuristics.FIRSTSTRONG_LTR);
+
+        final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
+                text, 0, text.length(),  mTempTextPaint, availableWidth);
+
+        return layoutBuilder.setAlignment(alignment)
+                .setLineSpacing(
+                        mTextView.getLineSpacingExtra(),
+                        mTextView.getLineSpacingMultiplier())
+                .setIncludePad(mTextView.getIncludeFontPadding())
+                .setBreakStrategy(mTextView.getBreakStrategy())
+                .setHyphenationFrequency(mTextView.getHyphenationFrequency())
+                .setMaxLines(maxLines == -1 ? Integer.MAX_VALUE : maxLines)
+                .setTextDirection(textDirectionHeuristic)
+                .build();
+    }
+
+    @TargetApi(14)
+    private StaticLayout createStaticLayoutForMeasuringPre23(CharSequence text,
+            Layout.Alignment alignment, int availableWidth) {
+        // Setup defaults.
+        float lineSpacingMultiplier = 1.0f;
+        float lineSpacingAdd = 0.0f;
+        boolean includePad = true;
+
+        if (Build.VERSION.SDK_INT >= 16) {
+            // Call public methods.
+            lineSpacingMultiplier = mTextView.getLineSpacingMultiplier();
+            lineSpacingAdd = mTextView.getLineSpacingExtra();
+            includePad = mTextView.getIncludeFontPadding();
+        } else {
+            // Call private methods and make sure to provide fallback defaults in case something
+            // goes wrong. The default values have been inlined with the StaticLayout defaults.
+            lineSpacingMultiplier = invokeAndReturnWithDefault(mTextView,
+                    "getLineSpacingMultiplier", lineSpacingMultiplier);
+            lineSpacingAdd = invokeAndReturnWithDefault(mTextView,
+                    "getLineSpacingExtra", lineSpacingAdd);
+            includePad = invokeAndReturnWithDefault(mTextView,
+                    "getIncludeFontPadding", includePad);
+        }
+
+        // The layout could not be constructed using the builder so fall back to the
+        // most broad constructor.
+        return new StaticLayout(text, mTempTextPaint, availableWidth,
+                alignment,
+                lineSpacingMultiplier,
+                lineSpacingAdd,
+                includePad);
+    }
+
+    private <T> T invokeAndReturnWithDefault(@NonNull Object object,
+            @NonNull final String methodName, @NonNull final T defaultValue) {
+        T result = null;
+        boolean exceptionThrown = false;
+
+        try {
+            // Cache lookup.
+            Method method = getTextViewMethod(methodName);
+            result = (T) method.invoke(object);
+        } catch (Exception ex) {
+            exceptionThrown = true;
+            Log.w(TAG, "Failed to invoke TextView#" + methodName + "() method", ex);
+        } finally {
+            if (result == null && exceptionThrown) {
+                result = defaultValue;
+            }
+        }
+
+        return result;
+    }
+
+    @Nullable
+    private Method getTextViewMethod(@NonNull final String methodName) {
+        try {
+            Method method = sTextViewMethodByNameCache.get(methodName);
+            if (method == null) {
+                method = TextView.class.getDeclaredMethod(methodName);
+                if (method != null) {
+                    method.setAccessible(true);
+                    // Cache update.
+                    sTextViewMethodByNameCache.put(methodName, method);
+                }
+            }
+
+            return method;
+        } catch (Exception ex) {
+            Log.w(TAG, "Failed to retrieve TextView#" + methodName + "() method", ex);
+            return null;
+        }
+    }
+
+    /**
+     * @return {@code true} if this widget supports auto-sizing text and has been configured to
+     * auto-size.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    boolean isAutoSizeEnabled() {
+        return supportsAutoSizeText()
+                && mAutoSizeTextType != TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+    }
+
+    /**
+     * @return {@code true} if this TextView supports auto-sizing text to fit within its container.
+     */
+    private boolean supportsAutoSizeText() {
+        // Auto-size only supports TextView and all siblings but EditText.
+        return !(mTextView instanceof AppCompatEditText);
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
index 732cbf5..b47a568 100644
--- a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
+++ b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
@@ -19,9 +19,7 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.os.Build;
 import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.ConfigurationHelper;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
@@ -53,8 +51,7 @@
     public ButtonBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         final boolean allowStackingDefault =
-                ConfigurationHelper.getScreenHeightDp(getResources())
-                        >= ALLOW_STACKING_MIN_HEIGHT_DP;
+                getResources().getConfiguration().screenHeightDp >= ALLOW_STACKING_MIN_HEIGHT_DP;
         final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
         mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking,
                 allowStackingDefault);
@@ -104,20 +101,9 @@
         if (mAllowStacking && !isStacked()) {
             final boolean stack;
 
-            if (Build.VERSION.SDK_INT >= 11) {
-                // On API v11+ we can use MEASURED_STATE_MASK and MEASURED_STATE_TOO_SMALL
-                final int measuredWidth = ViewCompat.getMeasuredWidthAndState(this);
-                final int measuredWidthState = measuredWidth & ViewCompat.MEASURED_STATE_MASK;
-                stack = measuredWidthState == ViewCompat.MEASURED_STATE_TOO_SMALL;
-            } else {
-                // Before that we need to manually total up the children's preferred width.
-                // This isn't perfect but works well enough for a workaround.
-                int childWidthTotal = 0;
-                for (int i = 0, count = getChildCount(); i < count; i++) {
-                    childWidthTotal += getChildAt(i).getMeasuredWidth();
-                }
-                stack = (childWidthTotal + getPaddingLeft() + getPaddingRight()) > widthSize;
-            }
+            final int measuredWidth = getMeasuredWidthAndState();
+            final int measuredWidthState = measuredWidth & View.MEASURED_STATE_MASK;
+            stack = measuredWidthState == View.MEASURED_STATE_TOO_SMALL;
 
             if (stack) {
                 setStacked(true);
@@ -143,7 +129,7 @@
                 final int secondVisible = getNextVisibleChildIndex(firstVisible + 1);
                 if (secondVisible >= 0) {
                     minHeight += getChildAt(secondVisible).getPaddingTop()
-                            + PEEK_BUTTON_DP * getResources().getDisplayMetrics().density;
+                            + (int) (PEEK_BUTTON_DP * getResources().getDisplayMetrics().density);
                 }
             } else {
                 minHeight += getPaddingBottom();
diff --git a/v7/appcompat/src/android/support/v7/widget/DrawableUtils.java b/v7/appcompat/src/android/support/v7/widget/DrawableUtils.java
index f193e65..808200f 100644
--- a/v7/appcompat/src/android/support/v7/widget/DrawableUtils.java
+++ b/v7/appcompat/src/android/support/v7/widget/DrawableUtils.java
@@ -178,7 +178,10 @@
         drawable.setState(originalState);
     }
 
-    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
+    /**
+     * Parses tint mode.
+     */
+    public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
         switch (value) {
             case 3: return PorterDuff.Mode.SRC_OVER;
             case 5: return PorterDuff.Mode.SRC_IN;
diff --git a/v7/appcompat/src/android/support/v7/widget/DropDownListView.java b/v7/appcompat/src/android/support/v7/widget/DropDownListView.java
index f6a75f7..5cad340 100644
--- a/v7/appcompat/src/android/support/v7/widget/DropDownListView.java
+++ b/v7/appcompat/src/android/support/v7/widget/DropDownListView.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.Build;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewPropertyAnimatorCompat;
 import android.support.v4.widget.ListViewAutoScrollHelper;
 import android.support.v7.appcompat.R;
@@ -98,7 +97,7 @@
         boolean handledEvent = true;
         boolean clearPressedItem = false;
 
-        final int actionMasked = MotionEventCompat.getActionMasked(event);
+        final int actionMasked = event.getActionMasked();
         switch (actionMasked) {
             case MotionEvent.ACTION_CANCEL:
                 handledEvent = false;
diff --git a/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java b/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
index 80fbbd4..284a4ea 100644
--- a/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
+++ b/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
@@ -18,20 +18,13 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.SystemClock;
-import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.view.menu.ShowableListMenu;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 
 /**
  * Abstract class that forwards touch events to a {@link ShowableListMenu}.
@@ -39,7 +32,8 @@
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP)
-public abstract class ForwardingListener implements View.OnTouchListener {
+public abstract class ForwardingListener
+        implements View.OnTouchListener, View.OnAttachStateChangeListener {
 
     /** Scaled touch slop, used for detecting movement outside bounds. */
     private final float mScaledTouchSlop;
@@ -73,12 +67,7 @@
     public ForwardingListener(View src) {
         mSrc = src;
         src.setLongClickable(true);
-
-        if (Build.VERSION.SDK_INT >= 12) {
-            addDetachListenerApi12(src);
-        } else {
-            addDetachListenerBase(src);
-        }
+        src.addOnAttachStateChangeListener(this);
 
         mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop();
         mTapTimeout = ViewConfiguration.getTapTimeout();
@@ -87,35 +76,6 @@
         mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
     }
 
-    @RequiresApi(12)
-    @TargetApi(12)
-    private void addDetachListenerApi12(View src) {
-        src.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-            @Override
-            public void onViewAttachedToWindow(View v) {}
-
-            @Override
-            public void onViewDetachedFromWindow(View v) {
-                onDetachedFromWindow();
-            }
-        });
-    }
-
-    private void addDetachListenerBase(View src) {
-        src.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-            boolean mIsAttached = ViewCompat.isAttachedToWindow(mSrc);
-
-            @Override
-            public void onGlobalLayout() {
-                final boolean wasAttached = mIsAttached;
-                mIsAttached = ViewCompat.isAttachedToWindow(mSrc);
-                if (wasAttached && !mIsAttached) {
-                    onDetachedFromWindow();
-                }
-            }
-        });
-    }
-
     /**
      * Returns the popup to which this listener is forwarding events.
      * <p>
@@ -151,7 +111,12 @@
         return forwarding || wasForwarding;
     }
 
-    private void onDetachedFromWindow() {
+    @Override
+    public void onViewAttachedToWindow(View v) {
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View v) {
         mForwarding = false;
         mActivePointerId = MotionEvent.INVALID_POINTER_ID;
 
@@ -206,7 +171,7 @@
             return false;
         }
 
-        final int actionMasked = MotionEventCompat.getActionMasked(srcEvent);
+        final int actionMasked = srcEvent.getActionMasked();
         switch (actionMasked) {
             case MotionEvent.ACTION_DOWN:
                 mActivePointerId = srcEvent.getPointerId(0);
@@ -311,7 +276,7 @@
         dstEvent.recycle();
 
         // Always cancel forwarding when the touch stream ends.
-        final int action = MotionEventCompat.getActionMasked(srcEvent);
+        final int action = srcEvent.getActionMasked();
         final boolean keepForwarding = action != MotionEvent.ACTION_UP
                 && action != MotionEvent.ACTION_CANCEL;
 
diff --git a/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java b/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java
index 10633f6..bb400f1 100644
--- a/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java
@@ -706,8 +706,8 @@
             final int margin = lp.leftMargin + lp.rightMargin;
             final int measuredWidth = child.getMeasuredWidth() + margin;
             maxWidth = Math.max(maxWidth, measuredWidth);
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(child));
+            childState = View.combineMeasuredStates(childState,
+                    child.getMeasuredState());
 
             allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
             if (lp.weight > 0) {
@@ -764,14 +764,14 @@
         heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
 
         // Reconcile our calculated size with the heightMeasureSpec
-        int heightSizeAndState = ViewCompat.resolveSizeAndState(heightSize, heightMeasureSpec, 0);
-        heightSize = heightSizeAndState & ViewCompat.MEASURED_SIZE_MASK;
+        int heightSizeAndState = View.resolveSizeAndState(heightSize, heightMeasureSpec, 0);
+        heightSize = heightSizeAndState & View.MEASURED_SIZE_MASK;
 
         // Either expand children with weight to take up available space or
         // shrink them if they extend beyond our current bounds. If we skipped
         // measurement on any children, we need to measure them now.
         int delta = heightSize - mTotalLength;
-        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
+        if (skippedMeasure || (delta != 0 && totalWeight > 0.0f)) {
             float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             mTotalLength = 0;
@@ -817,9 +817,9 @@
                     }
 
                     // Child may now not fit in vertical dimension.
-                    childState = ViewUtils.combineMeasuredStates(childState,
-                            ViewCompat.getMeasuredState(child) & (ViewCompat.MEASURED_STATE_MASK
-                                    >> ViewCompat.MEASURED_HEIGHT_STATE_SHIFT));
+                    childState = View.combineMeasuredStates(childState,
+                            child.getMeasuredState() & (View.MEASURED_STATE_MASK
+                                    >> View.MEASURED_HEIGHT_STATE_SHIFT));
                 }
 
                 final int margin =  lp.leftMargin + lp.rightMargin;
@@ -881,7 +881,7 @@
         // Check against our minimum width
         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
 
-        setMeasuredDimension(ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+        setMeasuredDimension(View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                 heightSizeAndState);
 
         if (matchWidth) {
@@ -1054,8 +1054,7 @@
 
             final int margin = lp.topMargin + lp.bottomMargin;
             final int childHeight = child.getMeasuredHeight() + margin;
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(child));
+            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
 
             if (baselineAligned) {
                 final int childBaseline = child.getBaseline();
@@ -1148,14 +1147,14 @@
         widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
 
         // Reconcile our calculated size with the widthMeasureSpec
-        int widthSizeAndState = ViewCompat.resolveSizeAndState(widthSize, widthMeasureSpec, 0);
-        widthSize = widthSizeAndState & ViewCompat.MEASURED_SIZE_MASK;
+        int widthSizeAndState = View.resolveSizeAndState(widthSize, widthMeasureSpec, 0);
+        widthSize = widthSizeAndState & View.MEASURED_SIZE_MASK;
 
         // Either expand children with weight to take up available space or
         // shrink them if they extend beyond our current bounds. If we skipped
         // measurement on any children, we need to measure them now.
         int delta = widthSize - mTotalLength;
-        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
+        if (skippedMeasure || (delta != 0 && totalWeight > 0.0f)) {
             float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
@@ -1207,8 +1206,8 @@
                     }
 
                     // Child may now not fit in horizontal dimension.
-                    childState = ViewUtils.combineMeasuredStates(childState,
-                            ViewCompat.getMeasuredState(child) & ViewCompat.MEASURED_STATE_MASK);
+                    childState = View.combineMeasuredStates(childState,
+                            child.getMeasuredState() & View.MEASURED_STATE_MASK);
                 }
 
                 if (isExactly) {
@@ -1301,9 +1300,9 @@
         // Check against our minimum height
         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
 
-        setMeasuredDimension(widthSizeAndState | (childState&ViewCompat.MEASURED_STATE_MASK),
-                ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        (childState<<ViewCompat.MEASURED_HEIGHT_STATE_SHIFT)));
+        setMeasuredDimension(widthSizeAndState | (childState & View.MEASURED_STATE_MASK),
+                View.resolveSizeAndState(maxHeight, heightMeasureSpec,
+                        (childState << View.MEASURED_HEIGHT_STATE_SHIFT)));
 
         if (matchHeight) {
             forceUniformHeight(count, widthMeasureSpec);
@@ -1751,6 +1750,7 @@
         return p instanceof LinearLayoutCompat.LayoutParams;
     }
 
+    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         if (Build.VERSION.SDK_INT >= 14) {
             super.onInitializeAccessibilityEvent(event);
@@ -1758,6 +1758,7 @@
         }
     }
 
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         if (Build.VERSION.SDK_INT >= 14) {
             super.onInitializeAccessibilityNodeInfo(info);
diff --git a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
index c363301..edc9781 100644
--- a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
+++ b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
@@ -23,7 +23,6 @@
 import android.database.DataSetObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.Handler;
 import android.support.annotation.AttrRes;
 import android.support.annotation.NonNull;
@@ -112,6 +111,8 @@
     private int mDropDownWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
     private boolean mDropDownVerticalOffsetSet;
     private boolean mIsAnimatedFromAnchor = true;
+    private boolean mOverlapAnchor;
+    private boolean mOverlapAnchorSet;
 
     private int mDropDownGravity = Gravity.NO_GRAVITY;
 
@@ -265,11 +266,7 @@
         }
         a.recycle();
 
-        if (Build.VERSION.SDK_INT >= 11) {
-            mPopup = new AppCompatPopupWindow(context, attrs, defStyleAttr, defStyleRes);
-        } else {
-            mPopup = new AppCompatPopupWindow(context, attrs, defStyleAttr);
-        }
+        mPopup = new AppCompatPopupWindow(context, attrs, defStyleAttr, defStyleRes);
         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
     }
 
@@ -567,9 +564,17 @@
     /**
      * Sets the height of the popup window in pixels. Can also be {@link #MATCH_PARENT}.
      *
-     * @param height Height of the popup window.
+     * @param height Height of the popup window must be a positive value,
+     *               {@link #MATCH_PARENT}, or {@link #WRAP_CONTENT}.
+     *
+     * @throws IllegalArgumentException if height is set to negative value
      */
     public void setHeight(int height) {
+        if (height < 0 && ViewGroup.LayoutParams.WRAP_CONTENT != height
+                && ViewGroup.LayoutParams.MATCH_PARENT != height) {
+            throw new IllegalArgumentException(
+                   "Invalid height. Must be a positive value, MATCH_PARENT, or WRAP_CONTENT.");
+        }
         mDropDownHeight = height;
     }
 
@@ -644,6 +649,10 @@
         PopupWindowCompat.setWindowLayoutType(mPopup, mDropDownWindowLayoutType);
 
         if (mPopup.isShowing()) {
+            if (!ViewCompat.isAttachedToWindow(getAnchorView())) {
+                //Don't update position if the anchor view is detached from window.
+                return;
+            }
             final int widthSpec;
             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
                 // The call to PopupWindow's update method below can accept -1 for any
@@ -711,6 +720,9 @@
             // only set this if the dropdown is not always visible
             mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
             mPopup.setTouchInterceptor(mTouchInterceptor);
+            if (mOverlapAnchorSet) {
+                PopupWindowCompat.setOverlapAnchor(mPopup, mOverlapAnchor);
+            }
             if (sSetEpicenterBoundsMethod != null) {
                 try {
                     sSetEpicenterBoundsMethod.invoke(mPopup, mEpicenterBounds);
@@ -799,10 +811,8 @@
             list.setListSelectionHidden(false);
             list.setSelection(position);
 
-            if (Build.VERSION.SDK_INT >= 11) {
-                if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
-                    list.setItemChecked(position, true);
-                }
+            if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
+                list.setItemChecked(position, true);
             }
         }
     }
@@ -1026,7 +1036,7 @@
     }
 
     /**
-     * Filter key down events. By forwarding key up events to this function,
+     * Filter key up events. By forwarding key up events to this function,
      * views using non-modal ListPopupWindow can have it handle key selection of items.
      *
      * @param keyCode keyCode param passed to the host view's onKeyUp
@@ -1298,6 +1308,16 @@
         return listContent + otherHeights;
     }
 
+    /**
+     * @hide Only used by {@link android.support.v7.view.menu.CascadingMenuPopup} to position
+     * a submenu correctly.
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public void setOverlapAnchor(boolean overlapAnchor) {
+        mOverlapAnchorSet = true;
+        mOverlapAnchor = overlapAnchor;
+    }
+
     private class PopupDataSetObserver extends DataSetObserver {
         PopupDataSetObserver() {
         }
@@ -1408,4 +1428,4 @@
         }
         return mPopup.getMaxAvailableHeight(anchor, yOffset);
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java b/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java
index 828a792..8c9c864 100644
--- a/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java
+++ b/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java
@@ -16,8 +16,6 @@
 
 package android.support.v7.widget;
 
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.content.res.AssetFileDescriptor;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -27,10 +25,13 @@
 import android.graphics.Movie;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -128,16 +129,19 @@
         return mResources.getDrawable(id);
     }
 
+    @RequiresApi(21)
     @Override
     public Drawable getDrawable(int id, Theme theme) throws NotFoundException {
         return mResources.getDrawable(id, theme);
     }
 
+    @RequiresApi(15)
     @Override
     public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
         return mResources.getDrawableForDensity(id, density);
     }
 
+    @RequiresApi(21)
     @Override
     public Drawable getDrawableForDensity(int id, int density, Theme theme) {
         return mResources.getDrawableForDensity(id, density, theme);
@@ -204,6 +208,7 @@
         mResources.getValue(id, outValue, resolveRefs);
     }
 
+    @RequiresApi(15)
     @Override
     public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
diff --git a/v7/appcompat/src/android/support/v7/widget/ScrollingTabContainerView.java b/v7/appcompat/src/android/support/v7/widget/ScrollingTabContainerView.java
index ea536d2..345e318 100644
--- a/v7/appcompat/src/android/support/v7/widget/ScrollingTabContainerView.java
+++ b/v7/appcompat/src/android/support/v7/widget/ScrollingTabContainerView.java
@@ -17,15 +17,13 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
 import android.support.v7.app.ActionBar;
 import android.support.v7.appcompat.R;
 import android.support.v7.view.ActionBarPolicy;
@@ -35,6 +33,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.ViewPropertyAnimator;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
@@ -46,7 +45,6 @@
 import android.widget.ListView;
 import android.widget.Spinner;
 import android.widget.TextView;
-import android.widget.Toast;
 
 /**
  * This widget implements the dynamic action bar tab behavior that can change across different
@@ -71,7 +69,7 @@
     private int mContentHeight;
     private int mSelectedTabIndex;
 
-    protected ViewPropertyAnimatorCompat mVisibilityAnim;
+    protected ViewPropertyAnimator mVisibilityAnim;
     protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
 
     private static final Interpolator sAlphaInterpolator = new DecelerateInterpolator();
@@ -237,17 +235,17 @@
         }
         if (visibility == VISIBLE) {
             if (getVisibility() != VISIBLE) {
-                ViewCompat.setAlpha(this, 0f);
+                setAlpha(0f);
             }
 
-            ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(1f);
+            ViewPropertyAnimator anim = animate().alpha(1f);
             anim.setDuration(FADE_DURATION);
 
             anim.setInterpolator(sAlphaInterpolator);
             anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
             anim.start();
         } else {
-            ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(0f);
+            ViewPropertyAnimator anim = animate().alpha(0f);
             anim.setDuration(FADE_DURATION);
 
             anim.setInterpolator(sAlphaInterpolator);
@@ -377,7 +375,7 @@
         // no-op
     }
 
-    private class TabView extends LinearLayoutCompat implements OnLongClickListener {
+    private class TabView extends LinearLayoutCompat {
         private final int[] BG_ATTRS = {
                 android.R.attr.background
         };
@@ -430,10 +428,8 @@
         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
             super.onInitializeAccessibilityNodeInfo(info);
 
-            if (Build.VERSION.SDK_INT >= 14) {
-                // This view masquerades as an action bar tab.
-                info.setClassName(ActionBar.Tab.class.getName());
-            }
+            // This view masquerades as an action bar tab.
+            info.setClassName(ActionBar.Tab.class.getName());
         }
 
         @Override
@@ -511,36 +507,10 @@
                 if (mIconView != null) {
                     mIconView.setContentDescription(tab.getContentDescription());
                 }
-
-                if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
-                    setOnLongClickListener(this);
-                } else {
-                    setOnLongClickListener(null);
-                    setLongClickable(false);
-                }
+                TooltipCompat.setTooltipText(this, hasText ? null : tab.getContentDescription());
             }
         }
 
-        @Override
-        public boolean onLongClick(View v) {
-            final int[] screenPos = new int[2];
-            getLocationOnScreen(screenPos);
-
-            final Context context = getContext();
-            final int width = getWidth();
-            final int height = getHeight();
-            final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-
-            Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
-                    Toast.LENGTH_SHORT);
-            // Show under the tab
-            cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
-                    (screenPos[0] + width / 2) - screenWidth / 2, height);
-
-            cheatSheet.show();
-            return true;
-        }
-
         public ActionBar.Tab getTab() {
             return mTab;
         }
@@ -592,11 +562,11 @@
         }
     }
 
-    protected class VisibilityAnimListener implements ViewPropertyAnimatorListener {
+    protected class VisibilityAnimListener extends AnimatorListenerAdapter {
         private boolean mCanceled = false;
         private int mFinalVisibility;
 
-        public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimatorCompat animation,
+        public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimator animation,
                 int visibility) {
             mFinalVisibility = visibility;
             mVisibilityAnim = animation;
@@ -604,13 +574,13 @@
         }
 
         @Override
-        public void onAnimationStart(View view) {
+        public void onAnimationStart(Animator animator) {
             setVisibility(VISIBLE);
             mCanceled = false;
         }
 
         @Override
-        public void onAnimationEnd(View view) {
+        public void onAnimationEnd(Animator animator) {
             if (mCanceled) return;
 
             mVisibilityAnim = null;
@@ -618,7 +588,7 @@
         }
 
         @Override
-        public void onAnimationCancel(View view) {
+        public void onAnimationCancel(Animator animator) {
             mCanceled = true;
         }
     }
diff --git a/v7/appcompat/src/android/support/v7/widget/SearchView.java b/v7/appcompat/src/android/support/v7/widget/SearchView.java
index 5b3b777..f017cf5 100644
--- a/v7/appcompat/src/android/support/v7/widget/SearchView.java
+++ b/v7/appcompat/src/android/support/v7/widget/SearchView.java
@@ -19,7 +19,6 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 import static android.support.v7.widget.SuggestionsAdapter.getColumnString;
 
-import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
@@ -35,19 +34,13 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.ResultReceiver;
 import android.speech.RecognizerIntent;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.ConfigurationHelper;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.KeyEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.CursorAdapter;
 import android.support.v7.appcompat.R;
@@ -69,8 +62,8 @@
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewTreeObserver;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -111,7 +104,7 @@
  * href="{@docRoot}guide/topics/ui/actionbar.html#ActionView">Action Bar</a> API guide</p>
  * </div>
  *
- * @see android.support.v4.view.MenuItemCompat#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
+ * @see android.view.MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
  */
 public class SearchView extends LinearLayoutCompat implements CollapsibleActionView {
 
@@ -180,22 +173,6 @@
 
     static final AutoCompleteTextViewReflector HIDDEN_METHOD_INVOKER = new AutoCompleteTextViewReflector();
 
-    /*
-     * SearchView can be set expanded before the IME is ready to be shown during
-     * initial UI setup. The show operation is asynchronous to account for this.
-     */
-    private Runnable mShowImeRunnable = new Runnable() {
-        @Override
-        public void run() {
-            InputMethodManager imm = (InputMethodManager)
-                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-            if (imm != null) {
-                HIDDEN_METHOD_INVOKER.showSoftInputUnchecked(imm, SearchView.this, 0);
-            }
-        }
-    };
-
     private final Runnable mUpdateDrawableStateRunnable = new Runnable() {
         @Override
         public void run() {
@@ -303,17 +280,17 @@
                 R.styleable.SearchView_layout, R.layout.abc_search_view);
         inflater.inflate(layoutResId, this, true);
 
-        mSearchSrcTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
+        mSearchSrcTextView = findViewById(R.id.search_src_text);
         mSearchSrcTextView.setSearchView(this);
 
         mSearchEditFrame = findViewById(R.id.search_edit_frame);
         mSearchPlate = findViewById(R.id.search_plate);
         mSubmitArea = findViewById(R.id.submit_area);
-        mSearchButton = (ImageView) findViewById(R.id.search_button);
-        mGoButton = (ImageView) findViewById(R.id.search_go_btn);
-        mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
-        mVoiceButton = (ImageView) findViewById(R.id.search_voice_btn);
-        mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
+        mSearchButton = findViewById(R.id.search_button);
+        mGoButton = findViewById(R.id.search_go_btn);
+        mCloseButton = findViewById(R.id.search_close_btn);
+        mVoiceButton = findViewById(R.id.search_voice_btn);
+        mCollapsedIcon = findViewById(R.id.search_mag_icon);
 
         // Set up icons and backgrounds.
         ViewCompat.setBackground(mSearchPlate,
@@ -328,6 +305,9 @@
 
         mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchHintIcon);
 
+        TooltipCompat.setTooltipText(mSearchButton,
+                getResources().getString(R.string.abc_searchview_description_search));
+
         // Extract dropdown layout resource IDs for later use.
         mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout,
                 R.layout.abc_search_dropdown_item_icons_2line);
@@ -391,38 +371,19 @@
 
         mDropDownAnchor = findViewById(mSearchSrcTextView.getDropDownAnchor());
         if (mDropDownAnchor != null) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
-                addOnLayoutChangeListenerToDropDownAnchorSDK11();
-            } else {
-                addOnLayoutChangeListenerToDropDownAnchorBase();
-            }
+            mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    adjustDropDownSizeAndPosition();
+                }
+            });
         }
 
         updateViewsVisibility(mIconifiedByDefault);
         updateQueryHint();
     }
 
-    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-    private void addOnLayoutChangeListenerToDropDownAnchorSDK11() {
-        mDropDownAnchor.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) {
-                adjustDropDownSizeAndPosition();
-            }
-        });
-    }
-
-    private void addOnLayoutChangeListenerToDropDownAnchorBase() {
-        mDropDownAnchor.getViewTreeObserver()
-                .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        adjustDropDownSizeAndPosition();
-                    }
-                });
-    }
-
     int getSuggestionRowLayout() {
         return mSuggestionRowLayout;
     }
@@ -532,9 +493,9 @@
     @Override
     public void clearFocus() {
         mClearingFocus = true;
-        setImeVisibility(false);
         super.clearFocus();
         mSearchSrcTextView.clearFocus();
+        mSearchSrcTextView.setImeVisibility(false);
         mClearingFocus = false;
     }
 
@@ -1003,20 +964,6 @@
         super.onDetachedFromWindow();
     }
 
-    void setImeVisibility(final boolean visible) {
-        if (visible) {
-            post(mShowImeRunnable);
-        } else {
-            removeCallbacks(mShowImeRunnable);
-            InputMethodManager imm = (InputMethodManager)
-                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-            if (imm != null) {
-                imm.hideSoftInputFromWindow(getWindowToken(), 0);
-            }
-        }
-    }
-
     /**
      * Called by the SuggestionsAdapter
      */
@@ -1068,7 +1015,7 @@
 
             // If there is text in the query box, handle enter, and action keys
             // The search key is handled by the dialog's onKeyDown().
-            if (!mSearchSrcTextView.isEmpty() && KeyEventCompat.hasNoModifiers(event)) {
+            if (!mSearchSrcTextView.isEmpty() && event.hasNoModifiers()) {
                 if (event.getAction() == KeyEvent.ACTION_UP) {
                     if (keyCode == KeyEvent.KEYCODE_ENTER) {
                         v.cancelLongPress();
@@ -1097,7 +1044,7 @@
         if (mSuggestionsAdapter == null) {
             return false;
         }
-        if (event.getAction() == KeyEvent.ACTION_DOWN && KeyEventCompat.hasNoModifiers(event)) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN && event.hasNoModifiers()) {
             // First, check for enter or search (both of which we'll treat as a
             // "click")
             if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
@@ -1243,7 +1190,7 @@
                 if (mSearchable != null) {
                     launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
                 }
-                setImeVisibility(false);
+                mSearchSrcTextView.setImeVisibility(false);
                 dismissSuggestions();
             }
         }
@@ -1268,7 +1215,7 @@
         } else {
             mSearchSrcTextView.setText("");
             mSearchSrcTextView.requestFocus();
-            setImeVisibility(true);
+            mSearchSrcTextView.setImeVisibility(true);
         }
 
     }
@@ -1276,7 +1223,7 @@
     void onSearchClicked() {
         updateViewsVisibility(false);
         mSearchSrcTextView.requestFocus();
-        setImeVisibility(true);
+        mSearchSrcTextView.setImeVisibility(true);
         if (mOnSearchClickListener != null) {
             mOnSearchClickListener.onClick(this);
         }
@@ -1373,18 +1320,22 @@
                     + " isIconified=" + isIconified + "}";
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 
     @Override
@@ -1435,7 +1386,7 @@
         if (mOnSuggestionListener == null
                 || !mOnSuggestionListener.onSuggestionClick(position)) {
             launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
-            setImeVisibility(false);
+            mSearchSrcTextView.setImeVisibility(false);
             dismissSuggestions();
             return true;
         }
@@ -1873,6 +1824,14 @@
         private int mThreshold;
         private SearchView mSearchView;
 
+        private boolean mHasPendingShowSoftInputRequest;
+        final Runnable mRunShowSoftInputIfNecessary = new Runnable() {
+            @Override
+            public void run() {
+                showSoftInputIfNecessary();
+            }
+        };
+
         public SearchAutoComplete(Context context) {
             this(context, null);
         }
@@ -1938,11 +1897,13 @@
             super.onWindowFocusChanged(hasWindowFocus);
 
             if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) {
-                InputMethodManager inputManager = (InputMethodManager) getContext()
-                        .getSystemService(Context.INPUT_METHOD_SERVICE);
-                inputManager.showSoftInput(this, 0);
-                // If in landscape mode, then make sure that
-                // the ime is in front of the dropdown.
+                // Since InputMethodManager#onPostWindowFocus() will be called after this callback,
+                // it is a bit too early to call InputMethodManager#showSoftInput() here. We still
+                // need to wait until the system calls back onCreateInputConnection() to call
+                // InputMethodManager#showSoftInput().
+                mHasPendingShowSoftInputRequest = true;
+
+                // If in landscape mode, then make sure that the ime is in front of the dropdown.
                 if (isLandscapeMode(getContext())) {
                     HIDDEN_METHOD_INVOKER.ensureImeVisible(this, true);
                 }
@@ -1982,7 +1943,7 @@
                     }
                     if (event.isTracking() && !event.isCanceled()) {
                         mSearchView.clearFocus();
-                        mSearchView.setImeVisibility(false);
+                        setImeVisibility(false);
                         return true;
                     }
                 }
@@ -1995,8 +1956,8 @@
          */
         private int getSearchViewTextMinWidthDp() {
             final Configuration config = getResources().getConfiguration();
-            final int widthDp = ConfigurationHelper.getScreenWidthDp(getResources());
-            final int heightDp = ConfigurationHelper.getScreenHeightDp(getResources());
+            final int widthDp = config.screenWidthDp;
+            final int heightDp = config.screenHeightDp;
 
             if (widthDp >= 960 && heightDp >= 720
                     && config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
@@ -2006,6 +1967,53 @@
             }
             return 160;
         }
+
+        /**
+         * We override {@link View#onCreateInputConnection(EditorInfo)} as a signal to schedule a
+         * pending {@link InputMethodManager#showSoftInput(View, int)} request (if any).
+         */
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+            final InputConnection ic = super.onCreateInputConnection(editorInfo);
+            if (mHasPendingShowSoftInputRequest) {
+                removeCallbacks(mRunShowSoftInputIfNecessary);
+                post(mRunShowSoftInputIfNecessary);
+            }
+            return ic;
+        }
+
+        private void showSoftInputIfNecessary() {
+            if (mHasPendingShowSoftInputRequest) {
+                final InputMethodManager imm = (InputMethodManager)
+                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+                imm.showSoftInput(this, 0);
+                mHasPendingShowSoftInputRequest = false;
+            }
+        }
+
+        private void setImeVisibility(final boolean visible) {
+            final InputMethodManager imm = (InputMethodManager)
+                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            if (!visible) {
+                mHasPendingShowSoftInputRequest = false;
+                removeCallbacks(mRunShowSoftInputIfNecessary);
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+                return;
+            }
+
+            if (imm.isActive(this)) {
+                // This means that SearchAutoComplete is already connected to the IME.
+                // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
+                mHasPendingShowSoftInputRequest = false;
+                removeCallbacks(mRunShowSoftInputIfNecessary);
+                imm.showSoftInput(this, 0);
+                return;
+            }
+
+            // Otherwise, InputMethodManager#showSoftInput() should be deferred after
+            // onCreateInputConnection().
+            mHasPendingShowSoftInputRequest = true;
+        }
     }
 
     private static class AutoCompleteTextViewReflector {
@@ -2035,13 +2043,6 @@
             } catch (NoSuchMethodException e) {
                 // Ah well.
             }
-            try {
-                showSoftInputUnchecked = InputMethodManager.class.getMethod(
-                        "showSoftInputUnchecked", int.class, ResultReceiver.class);
-                showSoftInputUnchecked.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                // Ah well.
-            }
         }
 
         void doBeforeTextChanged(AutoCompleteTextView view) {
@@ -2070,18 +2071,5 @@
                 }
             }
         }
-
-        void showSoftInputUnchecked(InputMethodManager imm, View view, int flags) {
-            if (showSoftInputUnchecked != null) {
-                try {
-                    showSoftInputUnchecked.invoke(imm, flags, null);
-                    return;
-                } catch (Exception e) {
-                }
-            }
-
-            // Hidden method failed, call public version instead
-            imm.showSoftInput(view, flags);
-        }
     }
 }
diff --git a/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java b/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
index 24dafb3..d6f0099 100644
--- a/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
+++ b/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
@@ -503,7 +503,7 @@
      * @return a Drawable, or null if none found
      */
     private Drawable getDrawableFromResourceValue(String drawableId) {
-        if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) {
+        if (drawableId == null || drawableId.isEmpty() || "0".equals(drawableId)) {
             return null;
         }
         try {
diff --git a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
index 3ccf763..44916ca 100644
--- a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
@@ -17,7 +17,6 @@
 package android.support.v7.widget;
 
 import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -32,7 +31,6 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.content.res.AppCompatResources;
@@ -82,7 +80,6 @@
  * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
  */
 @RequiresApi(14)
-@TargetApi(14)
 public class SwitchCompat extends CompoundButton {
     private static final int THUMB_ANIMATION_DURATION = 250;
 
@@ -171,7 +168,7 @@
     /** Bottom bound for drawing the switch track and thumb. */
     private int mSwitchBottom;
 
-    private TextPaint mTextPaint;
+    private final TextPaint mTextPaint;
     private ColorStateList mTextColors;
     private Layout mOnLayout;
     private Layout mOffLayout;
@@ -389,9 +386,10 @@
      * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
      * that you actually want.
      */
-    public void setSwitchTypeface(Typeface tf) {
-        if (mTextPaint.getTypeface() != tf) {
-            mTextPaint.setTypeface(tf);
+    public void setSwitchTypeface(Typeface typeface) {
+        if ((mTextPaint.getTypeface() != null && !mTextPaint.getTypeface().equals(typeface))
+                || (mTextPaint.getTypeface() == null && typeface != null)) {
+            mTextPaint.setTypeface(typeface);
 
             requestLayout();
             invalidate();
@@ -856,7 +854,7 @@
 
         final int measuredHeight = getMeasuredHeight();
         if (measuredHeight < switchHeight) {
-            setMeasuredDimension(ViewCompat.getMeasuredWidthAndState(this), switchHeight);
+            setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
         }
     }
 
@@ -904,7 +902,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         mVelocityTracker.addMovement(ev);
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
         switch (action) {
             case MotionEvent.ACTION_DOWN: {
                 final float x = ev.getX();
diff --git a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
index ca72d27..2213dd3 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
@@ -22,17 +22,23 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
+import android.support.annotation.StyleableRes;
+import android.support.v4.content.res.ResourcesCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.widget.TextView;
 
 /**
  * A class that wraps a {@link android.content.res.TypedArray} and provides the same public API
- * surface. The purpose of this class is so that we can intercept the {@link #getDrawable(int)}
- * call and tint the result.
+ * surface. The purpose of this class is so that we can intercept calls to new APIs.
  *
  * @hide
  */
@@ -84,6 +90,35 @@
         return null;
     }
 
+    /**
+     * Retrieve the Typeface for the attribute at <var>index</var>.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a font.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param style A style value used for selecting best match font from the list of family. Note
+     * that this value will be ignored if the platform supports font family(API 24 or later).
+     * @param targetView A text view to be applied this font. If async loading is specified in XML,
+     * this view will be refreshed with result typeface.
+     *
+     * @return Typeface for the attribute, or {@code null} if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not a font resource.
+     */
+    @Nullable
+    public Typeface getFont(@StyleableRes int index, int style, @NonNull TextView targetView) {
+        final int resourceId = mWrapped.getResourceId(index, 0);
+        if (resourceId == 0) {
+            return null;
+        }
+        if (mTypedValue == null) {
+            mTypedValue = new TypedValue();
+        }
+        return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style, targetView);
+    }
+
     public int length() {
         return mWrapped.length();
     }
@@ -210,6 +245,7 @@
         mWrapped.recycle();
     }
 
+    @RequiresApi(21)
     public int getChangingConfigurations() {
         return mWrapped.getChangingConfigurations();
     }
diff --git a/v7/appcompat/src/android/support/v7/widget/Toolbar.java b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
index fc5f1f1..45e2583 100644
--- a/v7/appcompat/src/android/support/v7/widget/Toolbar.java
+++ b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
@@ -31,13 +31,9 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StringRes;
 import android.support.annotation.StyleRes;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.MarginLayoutParamsCompat;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.appcompat.R;
@@ -480,6 +476,7 @@
         requestLayout();
     }
 
+    @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         if (Build.VERSION.SDK_INT >= 17) {
             super.onRtlPropertiesChanged(layoutDirection);
@@ -1433,7 +1430,7 @@
         if (ss.expandedMenuItemId != 0 && mExpandedMenuPresenter != null && menu != null) {
             final MenuItem item = menu.findItem(ss.expandedMenuItemId);
             if (item != null) {
-                MenuItemCompat.expandActionView(item);
+                item.expandActionView();
             }
         }
 
@@ -1460,7 +1457,7 @@
         // eat the rest of the gesture without reporting the events to the default implementation
         // since that's what it expects.
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
             mEatingTouch = false;
         }
@@ -1484,7 +1481,7 @@
         // Same deal as onTouchEvent() above. Eat all hover events, but still
         // respect the touch event dispatch contract.
 
-        final int action = MotionEventCompat.getActionMasked(ev);
+        final int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_HOVER_ENTER) {
             mEatingHover = false;
         }
@@ -1593,8 +1590,8 @@
             navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
             height = Math.max(height, mNavButtonView.getMeasuredHeight() +
                     getVerticalMargins(mNavButtonView));
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mNavButtonView));
+            childState = View.combineMeasuredStates(childState,
+                    mNavButtonView.getMeasuredState());
         }
 
         if (shouldLayout(mCollapseButtonView)) {
@@ -1604,8 +1601,8 @@
                     getHorizontalMargins(mCollapseButtonView);
             height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
                     getVerticalMargins(mCollapseButtonView));
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mCollapseButtonView));
+            childState = View.combineMeasuredStates(childState,
+                    mCollapseButtonView.getMeasuredState());
         }
 
         final int contentInsetStart = getCurrentContentInsetStart();
@@ -1619,8 +1616,8 @@
             menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
             height = Math.max(height, mMenuView.getMeasuredHeight() +
                     getVerticalMargins(mMenuView));
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mMenuView));
+            childState = View.combineMeasuredStates(childState,
+                    mMenuView.getMeasuredState());
         }
 
         final int contentInsetEnd = getCurrentContentInsetEnd();
@@ -1632,8 +1629,8 @@
                     heightMeasureSpec, 0, collapsingMargins);
             height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
                     getVerticalMargins(mExpandedActionView));
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mExpandedActionView));
+            childState = View.combineMeasuredStates(childState,
+                    mExpandedActionView.getMeasuredState());
         }
 
         if (shouldLayout(mLogoView)) {
@@ -1641,8 +1638,8 @@
                     heightMeasureSpec, 0, collapsingMargins);
             height = Math.max(height, mLogoView.getMeasuredHeight() +
                     getVerticalMargins(mLogoView));
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mLogoView));
+            childState = View.combineMeasuredStates(childState,
+                    mLogoView.getMeasuredState());
         }
 
         final int childCount = getChildCount();
@@ -1657,8 +1654,7 @@
             width += measureChildCollapseMargins(child, widthMeasureSpec, width,
                     heightMeasureSpec, 0, collapsingMargins);
             height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(child));
+            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
         }
 
         int titleWidth = 0;
@@ -1671,8 +1667,7 @@
                     collapsingMargins);
             titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
             titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mTitleTextView));
+            childState = View.combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
         }
         if (shouldLayout(mSubtitleTextView)) {
             titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
@@ -1681,8 +1676,8 @@
                     collapsingMargins));
             titleHeight += mSubtitleTextView.getMeasuredHeight() +
                     getVerticalMargins(mSubtitleTextView);
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    ViewCompat.getMeasuredState(mSubtitleTextView));
+            childState = View.combineMeasuredStates(childState,
+                    mSubtitleTextView.getMeasuredState());
         }
 
         width += titleWidth;
@@ -1693,12 +1688,12 @@
         width += getPaddingLeft() + getPaddingRight();
         height += getPaddingTop() + getPaddingBottom();
 
-        final int measuredWidth = ViewCompat.resolveSizeAndState(
+        final int measuredWidth = View.resolveSizeAndState(
                 Math.max(width, getSuggestedMinimumWidth()),
-                widthMeasureSpec, childState & ViewCompat.MEASURED_STATE_MASK);
-        final int measuredHeight = ViewCompat.resolveSizeAndState(
+                widthMeasureSpec, childState & View.MEASURED_STATE_MASK);
+        final int measuredHeight = View.resolveSizeAndState(
                 Math.max(height, getSuggestedMinimumHeight()),
-                heightMeasureSpec, childState << ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
+                heightMeasureSpec, childState << View.MEASURED_HEIGHT_STATE_SHIFT);
 
         setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight);
     }
@@ -1797,8 +1792,8 @@
             final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
             final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
             final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
-            final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0
-                    || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0;
+            final boolean titleHasWidth = (layoutTitle && (mTitleTextView.getMeasuredWidth() > 0))
+                    || (layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0);
 
             switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
                 case Gravity.TOP:
@@ -2287,18 +2282,22 @@
             out.writeInt(isOverflowOpen ? 1 : 0);
         }
 
-        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
 
     private class ExpandedActionViewMenuPresenter implements MenuPresenter {
diff --git a/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java b/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java
new file mode 100644
index 0000000..470c3b2
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+/**
+ * Helper class used to emulate the behavior of {@link View#setTooltipText(CharSequence)} prior
+ * to API level 26.
+ *
+ */
+public class TooltipCompat  {
+    private interface ViewCompatImpl {
+        void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText);
+    }
+
+    private static class BaseViewCompatImpl implements ViewCompatImpl {
+        @Override
+        public void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) {
+            TooltipCompatHandler.setTooltipText(view, tooltipText);
+        }
+    }
+
+    @TargetApi(26)
+    private static class Api26ViewCompatImpl implements ViewCompatImpl {
+        @Override
+        public void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) {
+            view.setTooltipText(tooltipText);
+        }
+    }
+
+    private static final ViewCompatImpl IMPL;
+    static {
+        if (Build.VERSION.SDK_INT >= 26) {
+            IMPL = new Api26ViewCompatImpl();
+        } else {
+            IMPL = new BaseViewCompatImpl();
+        }
+    }
+
+    /**
+     * Sets the tooltip text for the view.
+     * <p> Prior to API 26 this method sets or clears (when tooltip is null) the view's
+     * OnLongClickListener and OnHoverListener. A toast-like subpanel will be created on long click
+     * or mouse hover.
+     *
+     * @param view the view to set the tooltip text on
+     * @param tooltipText the tooltip text
+     */
+    public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) {
+        IMPL.setTooltipText(view, tooltipText);
+    }
+
+    private TooltipCompat() {}
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java b/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java
new file mode 100644
index 0000000..5ce1f8b
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
+
+import android.content.Context;
+import android.support.annotation.RestrictTo;
+import android.support.v4.view.ViewCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * Event handler used used to emulate the behavior of {@link View#setTooltipText(CharSequence)}
+ * prior to API level 26.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverListener,
+        View.OnAttachStateChangeListener {
+    private static final String TAG = "TooltipCompatHandler";
+
+    private static final long LONG_CLICK_HIDE_TIMEOUT_MS = 2500;
+    private static final long HOVER_HIDE_TIMEOUT_MS = 15000;
+    private static final long HOVER_HIDE_TIMEOUT_SHORT_MS = 3000;
+
+    private final View mAnchor;
+    private final CharSequence mTooltipText;
+
+    private final Runnable mShowRunnable = new Runnable() {
+        @Override
+        public void run() {
+            show(false /* not from touch*/);
+        }
+    };
+    private final Runnable mHideRunnable = new Runnable() {
+        @Override
+        public void run() {
+            hide();
+        }
+    };
+
+    private int mAnchorX;
+    private int mAnchorY;
+
+    private TooltipPopup mPopup;
+    private boolean mFromTouch;
+
+    // The handler currently showing a tooltip (there can be only one).
+    private static TooltipCompatHandler sActiveHandler;
+
+    /**
+     * Set the tooltip text for the view.
+     *
+     * @param view        view to set the tooltip on
+     * @param tooltipText the tooltip text
+     */
+    public static void setTooltipText(View view, CharSequence tooltipText) {
+        if (TextUtils.isEmpty(tooltipText)) {
+            if (sActiveHandler != null && sActiveHandler.mAnchor == view) {
+                sActiveHandler.hide();
+            }
+            view.setOnLongClickListener(null);
+            view.setLongClickable(false);
+            view.setOnHoverListener(null);
+        } else {
+            new TooltipCompatHandler(view, tooltipText);
+        }
+    }
+
+    private TooltipCompatHandler(View anchor, CharSequence tooltipText) {
+        mAnchor = anchor;
+        mTooltipText = tooltipText;
+
+        mAnchor.setOnLongClickListener(this);
+        mAnchor.setOnHoverListener(this);
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        mAnchorX = v.getWidth() / 2;
+        mAnchorY = v.getHeight() / 2;
+        show(true /* from touch */);
+        return true;
+    }
+
+    @Override
+    public boolean onHover(View v, MotionEvent event) {
+        if (mPopup != null && mFromTouch) {
+            return false;
+        }
+        AccessibilityManager manager = (AccessibilityManager)
+                mAnchor.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+        if (manager.isEnabled() && manager.isTouchExplorationEnabled()) {
+            return false;
+        }
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_MOVE:
+                if (mAnchor.isEnabled() && mPopup == null) {
+                    mAnchorX = (int) event.getX();
+                    mAnchorY = (int) event.getY();
+                    mAnchor.removeCallbacks(mShowRunnable);
+                    mAnchor.postDelayed(mShowRunnable, ViewConfiguration.getLongPressTimeout());
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                hide();
+                break;
+        }
+
+        return false;
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View v) {
+        // no-op.
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View v) {
+        hide();
+    }
+
+    private void show(boolean fromTouch) {
+        if (!ViewCompat.isAttachedToWindow(mAnchor)) {
+            return;
+        }
+        if (sActiveHandler != null) {
+            sActiveHandler.hide();
+        }
+        sActiveHandler = this;
+
+        mFromTouch = fromTouch;
+        mPopup = new TooltipPopup(mAnchor.getContext());
+        mPopup.show(mAnchor, mAnchorX, mAnchorY, mFromTouch, mTooltipText);
+        // Only listen for attach state change while the popup is being shown.
+        mAnchor.addOnAttachStateChangeListener(this);
+
+        final long timeout;
+        if (mFromTouch) {
+            timeout = LONG_CLICK_HIDE_TIMEOUT_MS;
+        } else if ((ViewCompat.getWindowSystemUiVisibility(mAnchor)
+                & SYSTEM_UI_FLAG_LOW_PROFILE) == SYSTEM_UI_FLAG_LOW_PROFILE) {
+            timeout = HOVER_HIDE_TIMEOUT_SHORT_MS - ViewConfiguration.getLongPressTimeout();
+        } else {
+            timeout = HOVER_HIDE_TIMEOUT_MS - ViewConfiguration.getLongPressTimeout();
+        }
+        mAnchor.removeCallbacks(mHideRunnable);
+        mAnchor.postDelayed(mHideRunnable, timeout);
+    }
+
+    private void hide() {
+        if (sActiveHandler == this) {
+            sActiveHandler = null;
+            if (mPopup != null) {
+                mPopup.hide();
+                mPopup = null;
+                mAnchor.removeOnAttachStateChangeListener(this);
+            } else {
+                Log.e(TAG, "sActiveHandler.mPopup == null");
+            }
+        }
+        mAnchor.removeCallbacks(mShowRunnable);
+        mAnchor.removeCallbacks(mHideRunnable);
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/TooltipPopup.java b/v7/appcompat/src/android/support/v7/widget/TooltipPopup.java
new file mode 100644
index 0000000..f707c8f
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/TooltipPopup.java
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.support.annotation.RestrictTo;
+import android.support.v7.appcompat.R;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+/**
+ * A popup window displaying a text message aligned to a specified view.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+class TooltipPopup {
+    private static final String TAG = "TooltipPopup";
+
+    private final Context mContext;
+
+    private final View mContentView;
+    private final TextView mMessageView;
+
+    private final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
+    private final Rect mTmpDisplayFrame = new Rect();
+    private final int[] mTmpAnchorPos = new int[2];
+    private final int[] mTmpAppPos = new int[2];
+
+    TooltipPopup(Context context) {
+        mContext = context;
+
+        mContentView = LayoutInflater.from(mContext).inflate(R.layout.tooltip, null);
+        mMessageView = (TextView) mContentView.findViewById(R.id.message);
+
+        mLayoutParams.setTitle(getClass().getSimpleName());
+        mLayoutParams.packageName = mContext.getPackageName();
+        mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
+        mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        mLayoutParams.format = PixelFormat.TRANSLUCENT;
+        mLayoutParams.windowAnimations = R.style.Animation_AppCompat_Tooltip;
+        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+    }
+
+    void show(View anchorView, int anchorX, int anchorY, boolean fromTouch,
+            CharSequence tooltipText) {
+        if (isShowing()) {
+            hide();
+        }
+
+        mMessageView.setText(tooltipText);
+
+        computePosition(anchorView, anchorX, anchorY, fromTouch, mLayoutParams);
+
+        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        wm.addView(mContentView, mLayoutParams);
+    }
+
+    void hide() {
+        if (!isShowing()) {
+            return;
+        }
+
+        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        wm.removeView(mContentView);
+    }
+
+    boolean isShowing() {
+        return mContentView.getParent() != null;
+    }
+
+    private void computePosition(View anchorView, int anchorX, int anchorY, boolean fromTouch,
+            WindowManager.LayoutParams outParams) {
+        final int tooltipPreciseAnchorThreshold = mContext.getResources().getDimensionPixelOffset(
+                R.dimen.tooltip_precise_anchor_threshold);
+
+        final int offsetX;
+        if (anchorView.getWidth() >= tooltipPreciseAnchorThreshold) {
+            // Wide view. Align the tooltip horizontally to the precise X position.
+            offsetX = anchorX;
+        } else {
+            // Otherwise anchor the tooltip to the view center.
+            offsetX = anchorView.getWidth() / 2;  // Center on the view horizontally.
+        }
+
+        final int offsetBelow;
+        final int offsetAbove;
+        if (anchorView.getHeight() >= tooltipPreciseAnchorThreshold) {
+            // Tall view. Align the tooltip vertically to the precise Y position.
+            final int offsetExtra = mContext.getResources().getDimensionPixelOffset(
+                    R.dimen.tooltip_precise_anchor_extra_offset);
+            offsetBelow = anchorY + offsetExtra;
+            offsetAbove = anchorY - offsetExtra;
+        } else {
+            // Otherwise anchor the tooltip to the view center.
+            offsetBelow = anchorView.getHeight();  // Place below the view in most cases.
+            offsetAbove = 0;  // Place above the view if the tooltip does not fit below.
+        }
+
+        outParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+
+        final int tooltipOffset = mContext.getResources().getDimensionPixelOffset(
+                fromTouch ? R.dimen.tooltip_y_offset_touch : R.dimen.tooltip_y_offset_non_touch);
+
+        final View appView = getAppRootView(anchorView);
+        if (appView == null) {
+            Log.e(TAG, "Cannot find app view");
+            return;
+        }
+        appView.getWindowVisibleDisplayFrame(mTmpDisplayFrame);
+        if (mTmpDisplayFrame.left < 0 && mTmpDisplayFrame.top < 0) {
+            // No meaningful display frame, the anchor view is probably in a subpanel
+            // (such as a popup window). Use the screen frame as a reasonable approximation.
+            final Resources res = mContext.getResources();
+            final int statusBarHeight;
+            int resourceId = res.getIdentifier("status_bar_height", "dimen", "android");
+            if (resourceId != 0) {
+                statusBarHeight = res.getDimensionPixelSize(resourceId);
+            } else {
+                statusBarHeight = 0;
+            }
+            final DisplayMetrics metrics = res.getDisplayMetrics();
+            mTmpDisplayFrame.set(0, statusBarHeight, metrics.widthPixels, metrics.heightPixels);
+        }
+        appView.getLocationOnScreen(mTmpAppPos);
+
+        anchorView.getLocationOnScreen(mTmpAnchorPos);
+        mTmpAnchorPos[0] -= mTmpAppPos[0];
+        mTmpAnchorPos[1] -= mTmpAppPos[1];
+        // mTmpAnchorPos is now relative to the main app window.
+
+        outParams.x = mTmpAnchorPos[0] + offsetX - mTmpDisplayFrame.width() / 2;
+
+        final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+        mContentView.measure(spec, spec);
+        final int tooltipHeight = mContentView.getMeasuredHeight();
+
+        final int yAbove = mTmpAnchorPos[1] + offsetAbove - tooltipOffset - tooltipHeight;
+        final int yBelow = mTmpAnchorPos[1] + offsetBelow + tooltipOffset;
+        if (fromTouch) {
+            if (yAbove >= 0) {
+                outParams.y = yAbove;
+            } else {
+                outParams.y = yBelow;
+            }
+        } else {
+            if (yBelow + tooltipHeight <= mTmpDisplayFrame.height()) {
+                outParams.y = yBelow;
+            } else {
+                outParams.y = yAbove;
+            }
+        }
+    }
+
+    private static View getAppRootView(View anchorView) {
+        Context context = anchorView.getContext();
+        while (context instanceof ContextWrapper) {
+            if (context instanceof Activity) {
+                return ((Activity) context).getWindow().getDecorView();
+            } else {
+                context = ((ContextWrapper) context).getBaseContext();
+            }
+        }
+        return anchorView.getRootView();
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ViewUtils.java b/v7/appcompat/src/android/support/v7/widget/ViewUtils.java
index 2a158c5..0f910c3 100644
--- a/v7/appcompat/src/android/support/v7/widget/ViewUtils.java
+++ b/v7/appcompat/src/android/support/v7/widget/ViewUtils.java
@@ -58,18 +58,6 @@
     }
 
     /**
-     * Merge two states as returned by {@link ViewCompat#getMeasuredState(android.view.View)} ()}.
-     * @param curState The current state as returned from a view or the result
-     * of combining multiple views.
-     * @param newState The new view state to combine.
-     * @return Returns a new integer reflecting the combination of the two
-     * states.
-     */
-    public static int combineMeasuredStates(int curState, int newState) {
-        return curState | newState;
-    }
-
-    /**
      * Allow calling the hidden method {@code computeFitSystemWindows(Rect, Rect)} through
      * reflection on {@code view}.
      */
diff --git a/v7/appcompat/tests/AndroidManifest.xml b/v7/appcompat/tests/AndroidManifest.xml
index 14413a2..abad7d6 100644
--- a/v7/appcompat/tests/AndroidManifest.xml
+++ b/v7/appcompat/tests/AndroidManifest.xml
@@ -15,97 +15,107 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v7.appcompat.test">
-
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application
-        android:theme="@style/Theme.AppCompat"
-        android:supportsRtl="true">
+        android:supportsRtl="true"
+        android:theme="@style/Theme.AppCompat">
+        <activity
+            android:name="android.support.v7.app.AppCompatActivity"/>
 
-        <uses-library android:name="android.test.runner"/>
+        <activity android:name="android.support.v7.app.AppCompatMenuItemShortcutsTestActivity"/>
 
         <activity
-                android:name="android.support.v7.app.AppCompatActivity"/>
+            android:name="android.support.v7.app.AppCompatMenuItemIconTintingTestActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:name="android.support.v7.app.WindowDecorAppCompatActivity"/>
+            android:name="android.support.v7.app.WindowDecorAppCompatActivity"/>
         <activity
-                android:name="android.support.v7.app.ToolbarAppCompatActivity"
-                android:theme="@style/Theme.AppCompat.NoActionBar"/>
+            android:name="android.support.v7.app.ToolbarAppCompatActivity"
+            android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
-                android:name="android.support.v7.app.DrawerLayoutActivity"
-                android:label="@string/drawer_layout_activity"
-                android:theme="@style/Theme.SampleDrawerLayout" />
+            android:name="android.support.v7.app.DrawerLayoutActivity"
+            android:label="@string/drawer_layout_activity"
+            android:theme="@style/Theme.SampleDrawerLayout"/>
 
         <activity
-                android:name="android.support.v7.app.DrawerLayoutDoubleActivity"
-                android:label="@string/drawer_layout_activity"
-                android:theme="@style/Theme.SampleDrawerLayout" />
+            android:name="android.support.v7.app.DrawerLayoutDoubleActivity"
+            android:label="@string/drawer_layout_activity"
+            android:theme="@style/Theme.SampleDrawerLayout"/>
 
         <activity
-                android:name="android.support.v7.app.DrawerDynamicLayoutActivity"
-                android:label="@string/drawer_layout_activity"
-                android:theme="@style/Theme.SampleDrawerLayout" />
+            android:name="android.support.v7.app.DrawerDynamicLayoutActivity"
+            android:label="@string/drawer_layout_activity"
+            android:theme="@style/Theme.SampleDrawerLayout"/>
 
         <activity
-                android:name="android.support.v7.app.AlertDialogTestActivity"
-                android:label="@string/alert_dialog_activity"
-                android:theme="@style/Theme.AppCompat.Light" />
+            android:name="android.support.v7.app.AlertDialogTestActivity"
+            android:label="@string/alert_dialog_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
+
+        <activity android:name="android.support.v7.view.SupportMenuInflaterTestActivity"/>
 
         <activity
-                android:name="android.support.v7.widget.PopupTestActivity"
-                android:label="@string/popup_activity"
-                android:theme="@style/Theme.AppCompat.Light" />
+            android:name="android.support.v7.widget.PopupTestActivity"
+            android:label="@string/popup_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.widget.AppCompatSpinnerActivity"
-                android:label="@string/app_compat_spinner_activity"
-                android:theme="@style/Theme.AppCompat.Light" />
+            android:name="android.support.v7.widget.AppCompatSpinnerActivity"
+            android:label="@string/app_compat_spinner_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.widget.AppCompatTextViewActivity"
-                android:label="@string/app_compat_text_view_activity"
-                android:theme="@style/Theme.TextColors" />
+            android:name="android.support.v7.widget.AppCompatTextViewActivity"
+            android:label="@string/app_compat_text_view_activity"
+            android:theme="@style/Theme.TextColors"/>
 
         <activity
-                android:name="android.support.v7.widget.AppCompatImageButtonActivity"
-                android:label="@string/app_compat_image_button_activity"
-                android:theme="@style/Theme.AppCompat.Light" />
+            android:name="android.support.v7.widget.AppCompatTextViewAutoSizeActivity"
+            android:label="@string/app_compat_text_view_auto_size_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.widget.AppCompatImageViewActivity"
-                android:label="@string/app_compat_image_view_activity"
-                android:theme="@style/Theme.AppCompat.Light" />
+            android:name="android.support.v7.widget.AppCompatButtonAutoSizeActivity"
+            android:label="@string/app_compat_button_auto_size_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.widget.AppCompatButtonActivity"
-                android:label="@string/app_compat_button_activity"
-                android:theme="@style/Theme.AppCompat.Light" />
+            android:name="android.support.v7.widget.AppCompatImageButtonActivity"
+            android:label="@string/app_compat_image_button_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.app.LayoutInflaterFactoryTestActivity"/>
+            android:name="android.support.v7.widget.AppCompatImageViewActivity"
+            android:label="@string/app_compat_image_view_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.app.FragmentContentIdActivity"/>
+            android:name="android.support.v7.widget.AppCompatButtonActivity"
+            android:label="@string/app_compat_button_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-                android:name="android.support.v7.app.NightModeActivity"
-                android:theme="@style/Theme.AppCompat.DayNight"/>
+            android:name="android.support.v7.widget.SearchViewTestActivity"
+            android:label="@string/search_view_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
+
+        <activity
+            android:name="android.support.v7.app.LayoutInflaterFactoryTestActivity"/>
+
+        <activity
+            android:name="android.support.v7.app.FragmentContentIdActivity"/>
+
+        <activity
+            android:name="android.support.v7.app.NightModeActivity"
+            android:theme="@style/Theme.AppCompat.DayNight"/>
 
         <activity
             android:name="android.support.v7.app.AppCompatVectorDrawableIntegrationActivity"/>
 
     </application>
 
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.v7.appcompat.test"/>
-
 </manifest>
diff --git a/v7/appcompat/tests/fonts_readme.txt b/v7/appcompat/tests/fonts_readme.txt
new file mode 100644
index 0000000..f0de576
--- /dev/null
+++ b/v7/appcompat/tests/fonts_readme.txt
@@ -0,0 +1,15 @@
+All fonts included in this project follow the below copyright and licensing:
+
+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.
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/color/color_state_list_link.xml b/v7/appcompat/tests/res/color/color_state_list_link.xml
new file mode 100644
index 0000000..36dd761
--- /dev/null
+++ b/v7/appcompat/tests/res/color/color_state_list_link.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/lilac_disabled"
+          android:state_enabled="false"/>
+    <item android:color="@color/lilac_default"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/drawable/icon_black.jpg b/v7/appcompat/tests/res/drawable/icon_black.jpg
new file mode 100644
index 0000000..4c9062a
--- /dev/null
+++ b/v7/appcompat/tests/res/drawable/icon_black.jpg
Binary files differ
diff --git a/v7/appcompat/tests/res/drawable/icon_blue.jpg b/v7/appcompat/tests/res/drawable/icon_blue.jpg
new file mode 100644
index 0000000..9e6c1c8
--- /dev/null
+++ b/v7/appcompat/tests/res/drawable/icon_blue.jpg
Binary files differ
diff --git a/v7/appcompat/tests/res/drawable/icon_green.jpg b/v7/appcompat/tests/res/drawable/icon_green.jpg
new file mode 100644
index 0000000..55a78f2
--- /dev/null
+++ b/v7/appcompat/tests/res/drawable/icon_green.jpg
Binary files differ
diff --git a/v7/appcompat/tests/res/drawable/icon_red.jpg b/v7/appcompat/tests/res/drawable/icon_red.jpg
new file mode 100644
index 0000000..6bc9e1f
--- /dev/null
+++ b/v7/appcompat/tests/res/drawable/icon_red.jpg
Binary files differ
diff --git a/v7/appcompat/tests/res/drawable/icon_yellow.jpg b/v7/appcompat/tests/res/drawable/icon_yellow.jpg
new file mode 100644
index 0000000..e748059
--- /dev/null
+++ b/v7/appcompat/tests/res/drawable/icon_yellow.jpg
Binary files differ
diff --git a/v7/appcompat/tests/res/font/samplefont.ttf b/v7/appcompat/tests/res/font/samplefont.ttf
new file mode 100644
index 0000000..5fccad2
--- /dev/null
+++ b/v7/appcompat/tests/res/font/samplefont.ttf
Binary files differ
diff --git a/v7/appcompat/tests/res/font/samplefont2.ttf b/v7/appcompat/tests/res/font/samplefont2.ttf
new file mode 100644
index 0000000..f31b8a9
--- /dev/null
+++ b/v7/appcompat/tests/res/font/samplefont2.ttf
Binary files differ
diff --git a/v7/appcompat/tests/res/font/samplefont3.ttf b/v7/appcompat/tests/res/font/samplefont3.ttf
new file mode 100644
index 0000000..9c850ab
--- /dev/null
+++ b/v7/appcompat/tests/res/font/samplefont3.ttf
Binary files differ
diff --git a/v7/appcompat/tests/res/font/samplefont4.ttf b/v7/appcompat/tests/res/font/samplefont4.ttf
new file mode 100644
index 0000000..9c850ab
--- /dev/null
+++ b/v7/appcompat/tests/res/font/samplefont4.ttf
Binary files differ
diff --git a/v7/appcompat/tests/res/font/samplexmldownloadedfont.xml b/v7/appcompat/tests/res/font/samplexmldownloadedfont.xml
new file mode 100644
index 0000000..659d196
--- /dev/null
+++ b/v7/appcompat/tests/res/font/samplexmldownloadedfont.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:fontProviderAuthority="com.example.test.fontprovider.authority"
+    app:fontProviderPackage="com.example.test.fontprovider.package"
+    app:fontProviderQuery="MyRequestedFont">
+</font-family>
diff --git a/v7/appcompat/tests/res/font/samplexmlfont.xml b/v7/appcompat/tests/res/font/samplexmlfont.xml
new file mode 100644
index 0000000..e225025
--- /dev/null
+++ b/v7/appcompat/tests/res/font/samplexmlfont.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
+    <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/samplefont" />
+    <font app:fontStyle="italic" app:fontWeight="400" app:font="@font/samplefont2" />
+    <font app:fontStyle="normal" app:fontWeight="800" app:font="@font/samplefont3"/>
+    <font app:fontStyle="italic" app:fontWeight="800" app:font="@font/samplefont4"/>
+</font-family>
diff --git a/v7/appcompat/tests/res/layout/appcompat_button_autosize_activity.xml b/v7/appcompat/tests/res/layout/appcompat_button_autosize_activity.xml
new file mode 100644
index 0000000..407154a
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/appcompat_button_autosize_activity.xml
@@ -0,0 +1,57 @@
+<?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"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_autosize_uniform"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizeMinTextSize="2px"
+        app:autoSizeMaxTextSize="50dp"
+        app:autoSizeStepGranularity="1px" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_autosize_uniform_predef_sizes"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizePresetSizes="@array/auto_size_predefined_sizes" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_autosize_uniform_predef_sizes_redundant_values"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizePresetSizes="@array/auto_size_predefined_sizes_redundant_values" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_text"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/layout/appcompat_searchview_activity.xml b/v7/appcompat/tests/res/layout/appcompat_searchview_activity.xml
new file mode 100644
index 0000000..69be7f5
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/appcompat_searchview_activity.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"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <android.support.v7.widget.SearchView
+        android:id="@+id/search_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <android.support.v7.widget.SearchView
+        android:id="@+id/search_view_with_defaults"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:maxWidth="@dimen/search_view_max_width"
+        app:iconifiedByDefault="false"
+        app:queryHint="@string/search_query_hint"
+        android:inputType="textCapCharacters"
+        android:imeOptions="actionDone" />
+
+</LinearLayout>
diff --git a/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml b/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml
index 9fd3f29..5ef371a 100644
--- a/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml
+++ b/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml
@@ -104,6 +104,121 @@
             android:text="@string/sample_text2"
             android:textColor="?android:attr/textColorSecondary" />
 
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/view_text_link_enabled"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/text_link_enabled"
+            android:textColor="?android:attr/textColorSecondary" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/view_text_link_disabled"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/text_link_disabled" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontresource_fontfamily_string_direct"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/sample_text1"
+            app:fontFamily="sans-serif" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontresource_fontfamily_string_resource"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/sample_text1"
+            app:fontFamily="@string/font_sans_serif" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontresource_fontfamily"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:fontFamily="@font/samplefont" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontxmlresource_fontfamily"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:fontFamily="@font/samplexmlfont" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontxmlresource_fontfamily_textstyle"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textStyle="italic"
+            app:fontFamily="@font/samplexmlfont" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontxmlresource_fontfamily_textstyle2"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textStyle="italic"
+            android:fontFamily="@font/samplexmlfont" />
+
+        <!-- This is here to test that the TextView constructor ignores references to
+         non Font resource types in the fontFamily attribute.-->
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontxmlresource_nonFontReference"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:fontFamily="@style/TextView_FontResource" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontresource_style"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="@style/TextView_FontResource" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontxmlresource_style"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="@style/TextView_FontXmlResource" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontresource_textAppearance"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextView_FontResource" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_fontxmlresource_textAppearance"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextView_FontXmlResource" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_textStyleOverride"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="@style/TextView_TextStyleOverride" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_textStyleDirect"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textStyle="italic" />
+
+        <android.support.v7.widget.AppCompatTextView
+            android:id="@+id/textview_simple"
+            android:text="@string/sample_text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
     </LinearLayout>
 
 </ScrollView>
diff --git a/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml b/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml
new file mode 100644
index 0000000..f9aafcc
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml
@@ -0,0 +1,66 @@
+<?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"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <android.support.v7.widget.AppCompatTextView
+        android:id="@+id/view_autosize_uniform"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizeMinTextSize="2px"
+        app:autoSizeMaxTextSize="50dp"
+        app:autoSizeStepGranularity="1px" />
+
+    <android.support.v7.widget.AppCompatTextView
+        android:id="@+id/view_autosize_uniform_predef_sizes"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizePresetSizes="@array/auto_size_predefined_sizes" />
+
+    <android.support.v7.widget.AppCompatTextView
+        android:id="@+id/view_autosize_uniform_predef_sizes_redundant_values"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizePresetSizes="@array/auto_size_predefined_sizes_redundant_values" />
+
+    <android.support.v7.widget.AppCompatTextView
+        android:id="@+id/view_text"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1" />
+
+    <android.support.v7.widget.AppCompatEditText
+        android:id="@+id/edittext_autosize_uniform"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizeMinTextSize="2px"
+        app:autoSizeMaxTextSize="50dp"
+        app:autoSizeStepGranularity="1px" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/layout/appcompat_toolbar_activity.xml b/v7/appcompat/tests/res/layout/appcompat_toolbar_activity.xml
new file mode 100644
index 0000000..707fd46
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/appcompat_toolbar_activity.xml
@@ -0,0 +1,33 @@
+<?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"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:minHeight="?attr/actionBarSize"
+            android:background="?attr/colorPrimaryDark"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/layout/searchview_suggestion_item.xml b/v7/appcompat/tests/res/layout/searchview_suggestion_item.xml
new file mode 100644
index 0000000..0526e0b
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/searchview_suggestion_item.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/search_view_suggestion_row_height"
+    android:textAppearance="?android:attr/textAppearanceListItemSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
diff --git a/v7/appcompat/tests/res/layout/textview_autosize_maxlines.xml b/v7/appcompat/tests/res/layout/textview_autosize_maxlines.xml
new file mode 100644
index 0000000..33c4603
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/textview_autosize_maxlines.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+<view
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    class="android.support.v7.widget.AppCompatTextViewAutoSizeTest$CustomTextViewWithTransformationMethod"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:maxLines="1"
+    app:autoSizeTextType="uniform"/>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/layout/toolbar_decor_content.xml b/v7/appcompat/tests/res/layout/toolbar_decor_content.xml
index c271f68..f5cd74c 100644
--- a/v7/appcompat/tests/res/layout/toolbar_decor_content.xml
+++ b/v7/appcompat/tests/res/layout/toolbar_decor_content.xml
@@ -21,6 +21,7 @@
 
     <android.support.v7.widget.Toolbar
             android:id="@+id/toolbar"
+            android:keyboardNavigationCluster="true"
             android:layout_width="match_parent"
             android:layout_height="?attr/actionBarSize"/>
 
diff --git a/v7/appcompat/tests/res/menu/appcompat_menu_icon_tinting.xml b/v7/appcompat/tests/res/menu/appcompat_menu_icon_tinting.xml
new file mode 100644
index 0000000..08ad3a7
--- /dev/null
+++ b/v7/appcompat/tests/res/menu/appcompat_menu_icon_tinting.xml
@@ -0,0 +1,54 @@
+<?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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_first"
+        android:icon="@drawable/icon_blue"
+        android:title="@string/menu1"
+        app:iconTint="@android:color/white"
+        app:showAsAction="always"/>
+
+    <item
+        android:id="@+id/menu_second"
+        android:icon="@drawable/icon_black"
+        android:title="@string/menu2"
+        app:iconTintMode="screen"
+        app:showAsAction="always"/>
+
+    <item
+        android:id="@+id/menu_third"
+        android:icon="@drawable/icon_green"
+        android:title="@string/menu3"/>
+
+    <item
+        android:id="@+id/menu_fourth"
+        android:icon="@drawable/icon_red"
+        android:title="@string/menu4"/>
+
+    <item
+        android:id="@+id/menu_fifth"
+        android:icon="@drawable/icon_yellow"
+        android:title="@string/menu5"/>
+
+    <item
+        android:id="@+id/menu_sixth"
+        android:title="@string/menu6"/>
+
+</menu>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/menu/appcompat_menu_shortcut.xml b/v7/appcompat/tests/res/menu/appcompat_menu_shortcut.xml
new file mode 100644
index 0000000..befebbe
--- /dev/null
+++ b/v7/appcompat/tests/res/menu/appcompat_menu_shortcut.xml
@@ -0,0 +1,39 @@
+<?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
+  -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item android:id="@+id/no_modifiers"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="a" />
+
+    <item android:id="@+id/default_modifiers"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="b"
+          app:alphabeticModifiers="CTRL" />
+
+    <item android:id="@+id/single_modifier"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="c"
+          app:alphabeticModifiers="SHIFT" />
+
+    <item android:id="@+id/multiple_modifiers"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="d"
+          app:alphabeticModifiers="ALT|SHIFT" />
+
+</menu>
diff --git a/v7/appcompat/tests/res/menu/popup_menu.xml b/v7/appcompat/tests/res/menu/popup_menu.xml
index f50efc5..09d3bfa 100644
--- a/v7/appcompat/tests/res/menu/popup_menu.xml
+++ b/v7/appcompat/tests/res/menu/popup_menu.xml
@@ -13,9 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto">
     <item android:id="@+id/action_highlight"
-          android:title="@string/popup_menu_highlight" />
+          android:title="@string/popup_menu_highlight"
+          app:contentDescription="@string/popup_menu_highlight_description"
+          app:tooltipText="@string/popup_menu_highlight_tooltip" />
     <item android:id="@+id/action_edit"
           android:title="@string/popup_menu_edit" />
     <item android:id="@+id/action_delete"
diff --git a/v7/appcompat/tests/res/menu/sample_actions.xml b/v7/appcompat/tests/res/menu/sample_actions.xml
index a854fd2..e72c4d2 100644
--- a/v7/appcompat/tests/res/menu/sample_actions.xml
+++ b/v7/appcompat/tests/res/menu/sample_actions.xml
@@ -18,12 +18,15 @@
 
     <item android:id="@+id/action_search"
           android:title="@string/search_menu_title"
+          app:contentDescription="@string/search_menu_description"
+          app:tooltipText="@string/search_menu_tooltip"
           app:showAsAction="always|collapseActionView"
           app:actionViewClass="android.support.v7.app.CustomCollapsibleView"/>
 
     <item android:id="@+id/action_alpha_shortcut"
-          android:title="Alpha"
-          android:alphabeticShortcut="A"/>
+          android:title="@string/alpha_menu_title"
+          android:alphabeticShortcut="A"
+          app:showAsAction="always" />
 
     <item android:id="@+id/action_numeric_shortcut"
           android:title="Numeric"
diff --git a/v7/appcompat/tests/res/menu/shortcut.xml b/v7/appcompat/tests/res/menu/shortcut.xml
new file mode 100644
index 0000000..b22c06c
--- /dev/null
+++ b/v7/appcompat/tests/res/menu/shortcut.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item android:id="@+id/no_modifiers"
+          android:alphabeticShortcut="a"
+          android:numericShortcut="1" />
+
+    <item android:id="@+id/default_modifiers"
+          android:alphabeticShortcut="b"
+          android:numericShortcut="2"
+          app:alphabeticModifiers="CTRL"
+          app:numericModifiers="CTRL" />
+
+    <item android:id="@+id/single_modifier"
+          android:alphabeticShortcut="c"
+          android:numericShortcut="3"
+          app:alphabeticModifiers="SHIFT"
+          app:numericModifiers="SHIFT" />
+
+    <item android:id="@+id/multiple_modifiers"
+          android:alphabeticShortcut="d"
+          android:numericShortcut="4"
+          app:alphabeticModifiers="CTRL|SHIFT"
+          app:numericModifiers="CTRL|SHIFT" />
+
+</menu>
diff --git a/v7/appcompat/tests/res/values/arrays.xml b/v7/appcompat/tests/res/values/arrays.xml
new file mode 100644
index 0000000..d695134
--- /dev/null
+++ b/v7/appcompat/tests/res/values/arrays.xml
@@ -0,0 +1,35 @@
+<?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>
+    <array name="auto_size_predefined_sizes">
+        <item>5px</item>
+        <item>11dip</item>
+        <item>19sp</item>
+        <item>29pt</item>
+        <item>43mm</item>
+        <item>53in</item>
+    </array>
+
+    <array name="auto_size_predefined_sizes_redundant_values">
+        <item>40px</item>
+        <item>10px</item>
+        <item>10px</item>
+        <item>10px</item>
+        <item>0dp</item>
+    </array>
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/values/dimens.xml b/v7/appcompat/tests/res/values/dimens.xml
index 16a8d4c..fca36c9 100644
--- a/v7/appcompat/tests/res/values/dimens.xml
+++ b/v7/appcompat/tests/res/values/dimens.xml
@@ -19,4 +19,8 @@
     <dimen name="drawable_large_size">20dip</dimen>
 
     <dimen name="popup_row_height">48dip</dimen>
+
+    <dimen name="search_view_max_width">160dp</dimen>
+    <dimen name="search_view_max_width2">200dp</dimen>
+    <dimen name="search_view_suggestion_row_height">48dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/values/strings.xml b/v7/appcompat/tests/res/values/strings.xml
index 926418a..90be8b3 100644
--- a/v7/appcompat/tests/res/values/strings.xml
+++ b/v7/appcompat/tests/res/values/strings.xml
@@ -24,7 +24,11 @@
     <string name="popup_activity">Popup activity</string>
     <string name="popup_show">Show popup</string>
     <string name="popup_menu_highlight">Highlight</string>
+    <string name="popup_menu_highlight_description">Highlight description</string>
+    <string name="popup_menu_highlight_tooltip">Highlight tooltip</string>
     <string name="popup_menu_edit">Edit</string>
+    <string name="popup_menu_edit_description">Edit description</string>
+    <string name="popup_menu_edit_tooltip">Edit tooltip</string>
     <string name="popup_menu_delete">Delete</string>
     <string name="popup_menu_ignore">Ignore</string>
     <string name="popup_menu_share">Share</string>
@@ -51,6 +55,8 @@
 
     <string name="app_compat_spinner_activity">AppCompat spinner</string>
     <string name="app_compat_text_view_activity">AppCompat text view</string>
+    <string name="app_compat_text_view_auto_size_activity">AppCompat text view auto-size</string>
+    <string name="app_compat_button_auto_size_activity">AppCompat button auto-size</string>
     <string name="sample_text1">Sample text 1</string>
     <string name="sample_text2">Sample text 2</string>
     <string name="app_compat_image_button_activity">AppCompat image button</string>
@@ -69,4 +75,26 @@
     </string-array>
 
     <string name="night_mode">DAY</string>
+
+    <string name="search_menu_description">Search description</string>
+    <string name="search_menu_tooltip">Search tooltip</string>
+    <string name="search_query_hint">query hint</string>
+    <string name="search_view_activity">SearchView activity</string>
+
+    <string name="alpha_menu_title">Alpha</string>
+    <string name="alpha_menu_description">Alpha description</string>
+    <string name="alpha_menu_tooltip">Alpha tooltip</string>
+
+    <string name="text_link_enabled">With <a href="http://www.google.com">link</a> enabled</string>
+    <string name="text_link_disabled">With <a href="http://www.google.com">link</a> disabled</string>
+
+    <string name="menu1">menu one</string>
+    <string name="menu2">menu two</string>
+    <string name="menu3">menu three</string>
+    <string name="menu4">menu four</string>
+    <string name="menu5">menu five</string>
+    <string name="menu6">menu six</string>
+
+    <string name="font_sans_serif">sans-serif</string>
+    <string name="font_serif">serif</string>
 </resources>
diff --git a/v7/appcompat/tests/res/values/styles.xml b/v7/appcompat/tests/res/values/styles.xml
index 1b416a4..bdbedb8 100644
--- a/v7/appcompat/tests/res/values/styles.xml
+++ b/v7/appcompat/tests/res/values/styles.xml
@@ -28,6 +28,7 @@
     <style name="Theme.TextColors" parent="@style/Theme.AppCompat.Light">
         <item name="android:textColorPrimary">#FF0000FF</item>
         <item name="android:textColorSecondary">@color/color_state_list_sand</item>
+        <item name="android:textColorLink">@color/color_state_list_link</item>
     </style>
 
     <style name="TextStyleAllCapsOn" parent="@android:style/TextAppearance.Medium">
@@ -51,4 +52,30 @@
     </style>
 
     <style name="PopupEmptyStyle" />
+
+    <style name="TextView_FontResource">
+        <item name="fontFamily">@font/samplefont</item>
+        <item name="android:textAppearance">@null</item>
+    </style>
+
+    <style name="TextView_FontXmlResource">
+        <item name="fontFamily">@font/samplexmlfont</item>
+    </style>
+
+    <style name="TextView_TextStyleOverride">
+        <item name="android:textStyle">italic</item>
+    </style>
+
+    <style name="TextView_FontResourceWithStyle">
+        <item name="fontFamily">@font/samplexmlfont</item>
+        <item name="android:textStyle">italic</item>
+    </style>
+
+    <style name="TextView_SansSerif">
+        <item name="fontFamily">@string/font_sans_serif</item>
+    </style>
+
+    <style name="TextView_Serif">
+        <item name="fontFamily">@string/font_serif</item>
+    </style>
 </resources>
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
index 8afd231..690cc26 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
@@ -45,6 +45,8 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.support.test.espresso.DataInteraction;
 import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.TestUtilsMatchers;
 import android.view.View;
@@ -56,13 +58,17 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.File;
 
 @MediumTest
-public class AlertDialogCursorTest
-        extends BaseInstrumentationTestCase<AlertDialogTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AlertDialogCursorTest {
+    @Rule
+    public final ActivityTestRule<AlertDialogTestActivity> mActivityTestRule;
 
     private Button mButton;
 
@@ -82,7 +88,7 @@
     private AlertDialog mAlertDialog;
 
     public AlertDialogCursorTest() {
-        super(AlertDialogTestActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AlertDialogTestActivity.class);
     }
 
     @Before
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
index 022face..a990a57 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
@@ -40,8 +40,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -60,6 +60,7 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.TestUtilsMatchers;
 import android.text.TextUtils;
@@ -76,6 +77,7 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -90,18 +92,21 @@
  *     <li>Testing <code>setIcon</code> API assumes that the icon is displayed by a separate
  *     <code>ImageView</code> which is a sibling of a title view.</li>
  *     <li>Testing <code>setMultiChoiceItems</code> API assumes that each item in the list
- *     is rendered by a single <code></code>CheckedTextView</code>.</li>
+ *     is rendered by a single <code>CheckedTextView</code>.</li>
  *     <li>Testing <code>setSingleChoiceItems</code> API assumes that each item in the list
- *     is rendered by a single <code></code>CheckedTextView</code>.</li>
+ *     is rendered by a single <code>CheckedTextView</code>.</li>
  * </ul>
  */
-public class AlertDialogTest extends BaseInstrumentationTestCase<AlertDialogTestActivity> {
+public class AlertDialogTest {
+    @Rule
+    public final ActivityTestRule<AlertDialogTestActivity> mActivityTestRule;
+
     private Button mButton;
 
     private AlertDialog mAlertDialog;
 
     public AlertDialogTest() {
-        super(AlertDialogTestActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AlertDialogTestActivity.class);
     }
 
     @Before
@@ -111,9 +116,14 @@
     }
 
     @After
-    public void tearDown() {
-        if (mAlertDialog != null) {
-            mAlertDialog.dismiss();
+    public void tearDown() throws Throwable {
+        if ((mAlertDialog != null) && mAlertDialog.isShowing()) {
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mAlertDialog.hide();
+                }
+            });
         }
     }
 
@@ -692,7 +702,7 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testSingleChoiceItemsFromRuntimeArray() {
         final String[] content = new String[] { "Alice", "Bob", "Charlie", "Delta" };
         final DialogInterface.OnClickListener mockClickListener =
@@ -706,7 +716,7 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testSingleChoiceItemsFromResourcesArray() {
         final DialogInterface.OnClickListener mockClickListener =
                 mock(DialogInterface.OnClickListener.class);
@@ -1004,7 +1014,7 @@
         // Verify that a Message with expected 'what' field has been posted on our mock handler
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(messageHandler, times(1)).sendMessageDelayed(
-                messageArgumentCaptor.capture(), anyInt());
+                messageArgumentCaptor.capture(), anyLong());
         assertEquals("Button clicked", whichButtonClicked, messageArgumentCaptor.getValue().what);
         // Verify that the dialog is no longer showing
         assertFalse("Dialog is not showing", mAlertDialog.isShowing());
@@ -1366,4 +1376,4 @@
             return mHeight;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemIconTintingTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemIconTintingTest.java
new file mode 100644
index 0000000..c842c7b
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemIconTintingTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.app;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.TestUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test icon tinting in {@link MenuItem}s
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatMenuItemIconTintingTest {
+    private AppCompatMenuItemIconTintingTestActivity mActivity;
+    private Resources mResources;
+    private Menu mMenu;
+
+    @Rule
+    public ActivityTestRule<AppCompatMenuItemIconTintingTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(AppCompatMenuItemIconTintingTestActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.getActivity();
+        mResources = mActivity.getResources();
+        mMenu = mActivity.getToolbarMenu();
+    }
+
+    @UiThreadTest
+    @Test
+    public void testIconTinting() throws Throwable {
+        final MenuItem firstItem = mMenu.getItem(0);
+        final MenuItem secondItem = mMenu.getItem(1);
+        final MenuItem thirdItem = mMenu.getItem(2);
+
+        // These are the default set in layout XML
+        assertNull(MenuItemCompat.getIconTintMode(firstItem));
+        assertEquals(Color.WHITE, MenuItemCompat.getIconTintList(firstItem).getDefaultColor());
+
+        assertEquals(PorterDuff.Mode.SCREEN, MenuItemCompat.getIconTintMode(secondItem));
+        assertNull(MenuItemCompat.getIconTintList(secondItem));
+
+        assertNull(MenuItemCompat.getIconTintMode(thirdItem));
+        assertNull(MenuItemCompat.getIconTintList(thirdItem));
+
+        // Change tint color list and mode and verify that they are returned by the getters
+        final ColorStateList colors = ColorStateList.valueOf(Color.RED);
+
+        MenuItemCompat.setIconTintList(firstItem, colors);
+        MenuItemCompat.setIconTintMode(firstItem, PorterDuff.Mode.XOR);
+        assertSame(colors, MenuItemCompat.getIconTintList(firstItem));
+        assertEquals(PorterDuff.Mode.XOR, MenuItemCompat.getIconTintMode(firstItem));
+
+        // Ensure the tint is preserved across drawable changes.
+        firstItem.setIcon(R.drawable.icon_yellow);
+        assertSame(colors, MenuItemCompat.getIconTintList(firstItem));
+        assertEquals(PorterDuff.Mode.XOR, MenuItemCompat.getIconTintMode(firstItem));
+
+        // Change tint color list and mode again and verify that they are returned by the getters
+        final ColorStateList colorsNew = ColorStateList.valueOf(Color.MAGENTA);
+        MenuItemCompat.setIconTintList(firstItem, colorsNew);
+        MenuItemCompat.setIconTintMode(firstItem, PorterDuff.Mode.SRC_IN);
+        assertSame(colorsNew, MenuItemCompat.getIconTintList(firstItem));
+        assertEquals(PorterDuff.Mode.SRC_IN, MenuItemCompat.getIconTintMode(firstItem));
+    }
+
+    private void verifyIconIsColoredAs(String description, @NonNull Drawable icon,
+            @ColorInt int color, int allowedComponentVariance) {
+        TestUtils.assertAllPixelsOfColor(description,
+                icon, icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), true,
+                color, allowedComponentVariance, false);
+    }
+
+
+    /**
+     * This method tests that icon tinting is not applied when the
+     * menu item has no icon.
+     */
+    @UiThreadTest
+    @Test
+    public void testIconTintingWithNoIcon() {
+        final MenuItem sixthItem = mMenu.getItem(5);
+
+        // Note that all the asserts in this test check that the menu item icon
+        // is null. This is because the matching entry in the XML doesn't define any
+        // icon, and there is nothing to tint.
+        assertNull("No icon after XML loading", sixthItem.getIcon());
+
+        // Load a new color state list, set it on the menu item icon and check that the icon
+        // is still null.
+        final ColorStateList sandColor = ResourcesCompat.getColorStateList(
+                mResources, R.color.color_state_list_sand, null);
+        MenuItemCompat.setIconTintList(sixthItem, sandColor);
+        assertNull("No icon after setting icon tint list", sixthItem.getIcon());
+
+        // Set tint mode on the menu item icon and check that the icon is still null.
+        MenuItemCompat.setIconTintMode(sixthItem, PorterDuff.Mode.MULTIPLY);
+        assertNull("No icon after setting icon tint mode", sixthItem.getIcon());
+    }
+
+    /**
+     * This method tests that icon tinting is applied across a number of
+     * <code>ColorStateList</code>s set as icon tint lists on the same menu item.
+     */
+    @UiThreadTest
+    @Test
+    public void testIconTintingAcrossTintListChange() {
+        final MenuItem firstItem = mMenu.getItem(0);
+
+        final @ColorInt int sandDefault = ResourcesCompat.getColor(
+                mResources, R.color.sand_default, null);
+        final @ColorInt int oceanDefault = ResourcesCompat.getColor(
+                mResources, R.color.ocean_default, null);
+
+        // Test the default state for tinting set up in the menu XML file.
+        verifyIconIsColoredAs("Default white tinting", firstItem.getIcon(), Color.WHITE, 0);
+
+        // Load a new color state list, set it on the menu item and check that the icon has
+        // switched to the matching entry in newly set color state list.
+        final ColorStateList sandColor = ResourcesCompat.getColorStateList(
+                mResources, R.color.color_state_list_sand, null);
+        MenuItemCompat.setIconTintList(firstItem, sandColor);
+        verifyIconIsColoredAs("Default white tinting", firstItem.getIcon(), sandDefault, 0);
+
+        // Load another color state list, set it on the menu item and check that the icon has
+        // switched to the matching entry in newly set color state list.
+        final ColorStateList oceanColor = ResourcesCompat.getColorStateList(
+                mResources, R.color.color_state_list_ocean, null);
+        MenuItemCompat.setIconTintList(firstItem, oceanColor);
+        verifyIconIsColoredAs("Default white tinting", firstItem.getIcon(), oceanDefault, 0);
+    }
+
+    /**
+     * This method tests that opaque icon tinting is applied correctly after changing the icon
+     * itself of the menu item.
+     */
+    @UiThreadTest
+    @Test
+    public void testIconOpaqueTintingAcrossIconChange() {
+        final MenuItem secondItem = mMenu.getItem(1);
+
+        // This is the fill color of R.drawable.icon_black set on our menu icon
+        // that we'll be testing in this method
+        final @ColorInt int iconColorBlack = 0xFF000000;
+
+        // At this point we shouldn't have any tinting since it's not defined in the menu XML
+        verifyIconIsColoredAs("Black icon before any tinting", secondItem.getIcon(),
+                iconColorBlack, 0);
+
+        // Now set up the tinting
+        final ColorStateList lilacColor = ResourcesCompat.getColorStateList(
+                mResources, R.color.color_state_list_lilac, null);
+        final @ColorInt int lilacDefault = ResourcesCompat.getColor(
+                mResources, R.color.lilac_default, null);
+        MenuItemCompat.setIconTintList(secondItem, lilacColor);
+        MenuItemCompat.setIconTintMode(secondItem, PorterDuff.Mode.SRC_OVER);
+
+        // Check that the icon is now tinted
+        verifyIconIsColoredAs("Lilac icon after tinting the black icon",
+                secondItem.getIcon(), lilacDefault, 0);
+
+        // Set a different icon on our menu item
+        secondItem.setIcon(R.drawable.test_drawable_red);
+
+        // Check that the icon is still tinted with the same color as before
+        verifyIconIsColoredAs("Lilac icon after changing icon to red",
+                secondItem.getIcon(), lilacDefault, 0);
+    }
+
+    /**
+     * This method tests that translucent icon tinting is applied correctly after changing the icon
+     * itself of the menu item.
+     */
+    @UiThreadTest
+    @Test
+    public void testIconTranslucentTintingAcrossIconChange() {
+        final MenuItem secondItem = mMenu.getItem(1);
+
+        // This is the fill color of R.drawable.icon_black set on our menu icon
+        // that we'll be testing in this method
+        final @ColorInt int iconColorBlack = 0xFF000000;
+
+        // At this point we shouldn't have any tinting since it's not defined in the menu XML
+        verifyIconIsColoredAs("Black icon before any tinting", secondItem.getIcon(),
+                iconColorBlack, 0);
+
+        final ColorStateList emeraldColor = ResourcesCompat.getColorStateList(
+                mResources, R.color.color_state_list_emerald_translucent, null);
+        final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
+                mResources, R.color.emerald_translucent_default, null);
+        // This is the fill color of R.drawable.test_background_red that will be set on our
+        // menu icon that we'll be testing in this method
+        final @ColorInt int iconColorRed = ResourcesCompat.getColor(
+                mResources, R.color.test_red, null);
+
+        // Set up the tinting of our menu item. The tint list is using translucent color, and the
+        // tint mode is going to be src_over, which will create a "mix" of the original icon with
+        // the translucent tint color.
+        MenuItemCompat.setIconTintList(secondItem, emeraldColor);
+        MenuItemCompat.setIconTintMode(secondItem, PorterDuff.Mode.SRC_OVER);
+
+        // From this point on in this method we're allowing a margin of error in checking the
+        // color of the menu icon. This is due to both translucent colors being used
+        // in the color state list and off-by-one discrepancies of SRC_OVER when it's compositing
+        // translucent color on top of solid fill color. This is where the allowed variance
+        // value of 2 comes from - one for compositing and one for color translucency.
+        final int allowedComponentVariance = 2;
+
+        // Test the tinting set up with the just loaded tint list.
+        verifyIconIsColoredAs("Emerald tinting on green icon",
+                secondItem.getIcon(), ColorUtils.compositeColors(emeraldDefault, iconColorBlack),
+                allowedComponentVariance);
+
+        // Set a different icon on our menu item
+        secondItem.setIcon(R.drawable.test_drawable_red);
+
+        // Test the tinting of the new menu icon with the same color state list
+        verifyIconIsColoredAs("Emerald tinting on red icon",
+                secondItem.getIcon(), ColorUtils.compositeColors(emeraldDefault, iconColorRed),
+                allowedComponentVariance);
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemIconTintingTestActivity.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemIconTintingTestActivity.java
new file mode 100644
index 0000000..b50f992
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemIconTintingTestActivity.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.app;
+
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+public class AppCompatMenuItemIconTintingTestActivity extends BaseTestActivity {
+    private Toolbar mToolbar;
+
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_toolbar_activity;
+    }
+
+    @Override
+    protected void onContentViewSet() {
+        mToolbar = findViewById(R.id.toolbar);
+        setSupportActionBar(mToolbar);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.appcompat_menu_icon_tinting, menu);
+
+        return true;
+    }
+
+    public Menu getToolbarMenu() {
+        return mToolbar.getMenu();
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTest.java
new file mode 100644
index 0000000..b0a7fb7
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package android.support.v7.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Build;
+import android.os.SystemClock;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.appcompat.test.R;
+import android.view.KeyEvent;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test shortcut trigger in case of MenuItems with non-default modifiers.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatMenuItemShortcutsTest {
+
+    private AppCompatMenuItemShortcutsTestActivity mActivity;
+
+    @Rule
+    public ActivityTestRule<AppCompatMenuItemShortcutsTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(AppCompatMenuItemShortcutsTestActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    @Test
+    public void testPerformShortcut() {
+        // The support library is only needed for API 25 or lower.
+        if (Build.VERSION.SDK_INT < 26) {
+            final long downTime = SystemClock.uptimeMillis();
+            int keyCodeToSend, metaState;
+            KeyEvent keyEventToSend;
+
+            // Test shortcut trigger in case of non-default single modifier
+            keyCodeToSend = KeyEvent.KEYCODE_C;
+            metaState = KeyEvent.META_SHIFT_ON;
+            keyEventToSend = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                    keyCodeToSend, 0, metaState);
+            assertTrue(mActivity.onKeyDown(keyCodeToSend, keyEventToSend));
+            assertEquals(mActivity.getMenuItemIdTracker(), R.id.single_modifier);
+
+            // Test shortcut trigger in case of multiple non-default modifiers
+            keyCodeToSend = KeyEvent.KEYCODE_D;
+            metaState = KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON;
+            keyEventToSend = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                    keyCodeToSend, 0, metaState);
+            assertTrue(mActivity.onKeyDown(keyCodeToSend, keyEventToSend));
+            assertEquals(mActivity.getMenuItemIdTracker(), R.id.multiple_modifiers);
+
+            // Test no shortcut trigger in case of incorrect modifier
+            keyCodeToSend = KeyEvent.KEYCODE_E;
+            metaState = KeyEvent.META_CTRL_ON;
+            keyEventToSend = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                    keyCodeToSend, 0, metaState);
+            assertFalse(mActivity.onKeyDown(keyCodeToSend, keyEventToSend));
+        }
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTestActivity.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTestActivity.java
new file mode 100644
index 0000000..26638f2
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package android.support.v7.app;
+
+import android.support.v7.appcompat.test.R;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+public class AppCompatMenuItemShortcutsTestActivity extends AppCompatActivity {
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.appcompat_menu_shortcut, menu);
+        return true;
+    }
+
+    private int mMenuItemIdTracker;
+
+    public int getMenuItemIdTracker() {
+        return mMenuItemIdTracker;
+    }
+
+    public boolean handleMenuItem(MenuItem item) {
+        mMenuItemIdTracker = item.getItemId();
+        return true;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
index 905521d..84816ae 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
@@ -24,17 +24,23 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.widget.ImageView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class AppCompatVectorDrawableIntegrationTest
-        extends BaseInstrumentationTestCase<AppCompatVectorDrawableIntegrationActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AppCompatVectorDrawableIntegrationTest {
+    @Rule
+    public final ActivityTestRule<AppCompatVectorDrawableIntegrationActivity> mActivityTestRule;
 
     private Bitmap mBitmap;
     private Canvas mCanvas;
@@ -46,7 +52,8 @@
     private static final int CENTER_Y = HEIGHT / 2;
 
     public AppCompatVectorDrawableIntegrationTest() {
-        super(AppCompatVectorDrawableIntegrationActivity.class);
+        mActivityTestRule =
+                new ActivityTestRule<>(AppCompatVectorDrawableIntegrationActivity.class);
     }
 
     @Before
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
index 3aa7259..c634097 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
@@ -35,9 +35,15 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.support.annotation.RequiresApi;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.FitWindowsContentLayout;
 import android.support.v7.testutils.BaseTestActivity;
@@ -46,24 +52,31 @@
 import android.view.View;
 import android.view.WindowInsets;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public abstract class BaseBasicsTestCase<A extends BaseTestActivity>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseBasicsTestCase<A extends BaseTestActivity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
 
     protected BaseBasicsTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<>(activityClass);
     }
 
     @Test
     public void testActionBarExists() {
-        assertNotNull("ActionBar is not null", getActivity().getSupportActionBar());
+        assertNotNull("ActionBar is not null",
+                mActivityTestRule.getActivity().getSupportActionBar());
     }
 
     @Test
     public void testDefaultActionBarTitle() {
-        assertEquals(getActivity().getTitle(), getActivity().getSupportActionBar().getTitle());
+        assertEquals(mActivityTestRule.getActivity().getTitle(),
+                mActivityTestRule.getActivity().getSupportActionBar().getTitle());
     }
 
     @UiThreadTest
@@ -77,11 +90,17 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 16)
+    @RequiresApi(16)
     public void testFitSystemWindowsReachesContent() {
         final FitWindowsContentLayout content =
-                (FitWindowsContentLayout) getActivity().findViewById(R.id.test_content);
+                mActivityTestRule.getActivity().findViewById(R.id.test_content);
         assertNotNull(content);
 
+        if (!canShowSystemUi(mActivityTestRule.getActivity())) {
+            // Device cannot show system UI so setSystemUiVisibility will do nothing.
+            return;
+        }
+
         // Call setSystemUiVisibility with flags which will cause window insets to be dispatched
         final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         onView(withId(R.id.test_content)).perform(setSystemUiVisibility(flags));
@@ -91,10 +110,16 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
+    @RequiresApi(21)
     public void testOnApplyWindowInsetsReachesContent() {
-        final View content = getActivity().findViewById(R.id.test_content);
+        final View content = mActivityTestRule.getActivity().findViewById(R.id.test_content);
         assertNotNull(content);
 
+        if (!canShowSystemUi(mActivityTestRule.getActivity())) {
+            // Device cannot show system UI so setSystemUiVisibility will do nothing.
+            return;
+        }
+
         // Create a spy of one of our test listener and set it on our content
         final View.OnApplyWindowInsetsListener spyListener
                 = spy(new TestOnApplyWindowInsetsListener());
@@ -112,7 +137,7 @@
     @Test
     @UiThreadTest
     public void testSupportActionModeCallbacks() {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback callback = mock(ActionMode.Callback.class);
@@ -136,7 +161,7 @@
     @Test
     @UiThreadTest
     public void testSupportActionModeCallbacksInvalidate() {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback callback = mock(ActionMode.Callback.class);
@@ -160,7 +185,7 @@
     @Test
     @UiThreadTest
     public void testSupportActionModeCallbacksWithFalseOnCreate() {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback callback = mock(ActionMode.Callback.class);
@@ -180,8 +205,17 @@
         assertNull(actionMode);
     }
 
+    @SuppressWarnings("deprecation")
+    @SuppressLint("InlinedApi")
+    private static boolean canShowSystemUi(Activity activity) {
+        PackageManager manager = activity.getPackageManager();
+        return !manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+                && !manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                && !manager.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
     protected void testSupportActionModeAppCompatCallbacks(final boolean fromWindow) {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback amCallback = mock(ActionMode.Callback.class);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
deleted file mode 100644
index c98df27..0000000
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v7.app;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public abstract class BaseInstrumentationTestCase<A extends Activity> {
-
-    @Rule
-    public final ActivityTestRule<A> mActivityTestRule;
-
-    protected BaseInstrumentationTestCase(Class<A> activityClass) {
-        mActivityTestRule = new ActivityTestRule<A>(activityClass);
-    }
-
-    @Deprecated
-    public A getActivity() {
-        return mActivityTestRule.getActivity();
-    }
-
-    @Deprecated
-    public Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
index 5afbab5..135f65d 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
@@ -23,14 +23,20 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
 import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.MenuItemCompat;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.view.ActionMode;
@@ -39,15 +45,29 @@
 import android.view.MenuItem;
 
 import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public abstract class BaseKeyEventsTestCase<A extends BaseTestActivity>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseKeyEventsTestCase<A extends BaseTestActivity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
+    private Instrumentation mInstrumentation;
+    private A mActivity;
 
     protected BaseKeyEventsTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<>(activityClass);
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityTestRule.getActivity();
     }
 
     @Test
@@ -55,10 +75,10 @@
     public void testBackDismissesActionMode() {
         final AtomicBoolean destroyed = new AtomicBoolean();
 
-        getActivity().runOnUiThread(new Runnable() {
+        mActivity.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                getActivity().startSupportActionMode(new ActionMode.Callback() {
+                mActivity.startSupportActionMode(new ActionMode.Callback() {
                     @Override
                     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                         mode.getMenuInflater().inflate(R.menu.sample_actions, menu);
@@ -83,34 +103,32 @@
             }
         });
 
-        getInstrumentation().waitForIdleSync();
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.waitForIdleSync();
 
-        assertFalse("Activity was not finished", getActivity().isFinishing());
+        assertFalse("Activity was not finished", mActivity.isFinishing());
         assertTrue("ActionMode was destroyed", destroyed.get());
     }
 
     @Test
     @LargeTest
     public void testBackCollapsesActionView() throws InterruptedException {
-        final String itemTitle = getActivity().getString(R.string.search_menu_title);
-
         // Click on the Search menu item
-        onView(withContentDescription(itemTitle)).perform(click());
+        onView(withId(R.id.action_search)).perform(click());
         // Check that the action view is displayed (expanded)
         onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName())))
                 .check(matches(isDisplayed()));
 
         // Let things settle
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         // Now send a back event to collapse the custom action view
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.waitForIdleSync();
 
         // Check that the Activity is still running
-        assertFalse(getActivity().isFinishing());
-        assertFalse(getActivity().isDestroyed());
+        assertFalse(mActivity.isFinishing());
+        assertFalse(mActivity.isDestroyed());
         // ... and that our action view is not attached
         onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName())))
                 .check(doesNotExist());
@@ -119,24 +137,24 @@
     @Test
     @SmallTest
     public void testMenuPressInvokesPanelCallbacks() throws InterruptedException {
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("onMenuOpened called", getActivity().wasOnMenuOpenedCalled());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
+        assertTrue("onMenuOpened called", mActivity.wasOnMenuOpenedCalled());
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("onPanelClosed called", getActivity().wasOnPanelClosedCalled());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
+        assertTrue("onPanelClosed called", mActivity.wasOnPanelClosedCalled());
     }
 
     @Test
     @SmallTest
     public void testBackPressWithMenuInvokesOnPanelClosed() throws InterruptedException {
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("onPanelClosed called", getActivity().wasOnPanelClosedCalled());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.waitForIdleSync();
+        assertTrue("onPanelClosed called", mActivity.wasOnPanelClosedCalled());
     }
 
     @Test
@@ -144,25 +162,25 @@
     public void testBackPressWithEmptyMenuFinishesActivity() throws InterruptedException {
         repopulateWithEmptyMenu();
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        assertTrue(getActivity().isFinishing());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        assertTrue(mActivity.isFinishing());
     }
 
     @Test
     @SmallTest
     public void testDelKeyEventReachesActivity() {
         // First send the event
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
+        mInstrumentation.waitForIdleSync();
 
-        KeyEvent downEvent = getActivity().getInvokedKeyDownEvent();
+        KeyEvent downEvent = mActivity.getInvokedKeyDownEvent();
         assertNotNull("onKeyDown called", downEvent);
         assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_DEL, downEvent.getKeyCode());
 
-        KeyEvent upEvent = getActivity().getInvokedKeyUpEvent();
+        KeyEvent upEvent = mActivity.getInvokedKeyUpEvent();
         assertNotNull("onKeyUp called", upEvent);
         assertEquals("onKeyUp event matches", KeyEvent.KEYCODE_DEL, upEvent.getKeyCode());
     }
@@ -170,23 +188,53 @@
     @Test
     @SmallTest
     public void testMenuKeyEventReachesActivity() throws InterruptedException {
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
 
-        KeyEvent downEvent = getActivity().getInvokedKeyDownEvent();
+        KeyEvent downEvent = mActivity.getInvokedKeyDownEvent();
         assertNotNull("onKeyDown called", downEvent);
         assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_MENU, downEvent.getKeyCode());
 
-        KeyEvent upEvent = getActivity().getInvokedKeyUpEvent();
+        KeyEvent upEvent = mActivity.getInvokedKeyUpEvent();
         assertNotNull("onKeyUp called", upEvent);
         assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_MENU, upEvent.getKeyCode());
     }
 
+    @Test
+    @SmallTest
+    public void testActionMenuContent() throws Throwable {
+        onView(withId(R.id.action_search))
+                .check(matches(isDisplayed()))
+                .check(matches(withContentDescription(R.string.search_menu_description)));
+
+        onView(withId(R.id.action_alpha_shortcut))
+                .check(matches(isDisplayed()))
+                .check(matches(withContentDescription((String) null)));
+
+        Menu menu = mActivity.getMenu();
+        final MenuItem alphaItem = menu.findItem(R.id.action_alpha_shortcut);
+        assertNotNull(alphaItem);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                MenuItemCompat.setContentDescription(alphaItem,
+                        mActivity.getString(R.string.alpha_menu_description));
+                MenuItemCompat.setTooltipText(alphaItem,
+                        mActivity.getString(R.string.alpha_menu_tooltip));
+            }
+        });
+
+        onView(withId(R.id.action_alpha_shortcut))
+                .check(matches(isDisplayed()))
+                .check(matches(withContentDescription(R.string.alpha_menu_description)));
+    }
+
     private void repopulateWithEmptyMenu() throws InterruptedException {
         int count = 0;
-        getActivity().setShouldPopulateOptionsMenu(false);
+        mActivity.setShouldPopulateOptionsMenu(false);
         while (count++ < 10) {
-            Menu menu = getActivity().getMenu();
+            Menu menu = mActivity.getMenu();
             if (menu == null || menu.size() != 0) {
                 Thread.sleep(100);
             } else {
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
index cb21347..88216d1 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
@@ -19,21 +19,29 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import android.app.Instrumentation;
 import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MenuItem;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public abstract class BaseKeyboardShortcutsTestCase<A extends BaseTestActivity>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseKeyboardShortcutsTestCase<A extends BaseTestActivity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
 
     protected BaseKeyboardShortcutsTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<>(activityClass);
     }
 
     @Test
@@ -45,18 +53,19 @@
     }
 
     private void testKeyboardShortcut(final int keyCode, final int meta, final int expectedId) {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final long downTime = SystemClock.uptimeMillis();
         final KeyEvent downEvent = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
                 keyCode, 0, meta, KeyCharacterMap.VIRTUAL_KEYBOARD, 0);
-        getInstrumentation().sendKeySync(downEvent);
-        getInstrumentation().waitForIdleSync();
+        instrumentation.sendKeySync(downEvent);
+        instrumentation.waitForIdleSync();
 
         final KeyEvent upEvent = new KeyEvent(downTime, downTime + 500, KeyEvent.ACTION_UP,
                 keyCode, 0, meta, KeyCharacterMap.VIRTUAL_KEYBOARD, 0);
-        getInstrumentation().sendKeySync(upEvent);
-        getInstrumentation().waitForIdleSync();
+        instrumentation.sendKeySync(upEvent);
+        instrumentation.waitForIdleSync();
 
-        MenuItem selectedItem = getActivity().getOptionsItemSelected();
+        MenuItem selectedItem = mActivityTestRule.getActivity().getOptionsItemSelected();
         assertNotNull("Options item selected", selectedItem);
         assertEquals("Correct options item selected", selectedItem.getItemId(), expectedId);
     }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
index 8230cbc..ce9c157 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
@@ -21,25 +21,30 @@
 
 import android.app.Dialog;
 import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @MediumTest
-public class DialogTestCase extends BaseInstrumentationTestCase<WindowDecorAppCompatActivity> {
-
-    public DialogTestCase() {
-        super(WindowDecorAppCompatActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class DialogTestCase {
+    @Rule
+    public final ActivityTestRule<WindowDecorAppCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(WindowDecorAppCompatActivity.class);
 
     @Test
     public void testDialogFragmentShows() {
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         TestDialogFragment fragment = new TestDialogFragment();
-        fragment.show(getActivity().getSupportFragmentManager(), null);
+        fragment.show(mActivityTestRule.getActivity().getSupportFragmentManager(), null);
 
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         assertNotNull("Dialog was null", fragment.getDialog());
         assertTrue("Dialog was not being shown", fragment.getDialog().isShowing());
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java
index 90a9b59..92d97f4 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java
@@ -18,7 +18,6 @@
 
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
-import android.util.Log;
 
 /**
  * Test activity for testing presence of single and multiple drawers in <code>DrawerLayout</code>.
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
index 198c63a..54597c0 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
@@ -28,6 +28,8 @@
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
 import android.view.View;
@@ -37,18 +39,20 @@
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test cases to verify that <code>DrawerLayout</code> only supports configurations
  * with at most one drawer child along each vertical (left / right) edge.
  */
 @SmallTest
-public class DrawerDynamicLayoutTest
-        extends BaseInstrumentationTestCase<DrawerDynamicLayoutActivity> {
-    public DrawerDynamicLayoutTest() {
-        super(DrawerDynamicLayoutActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class DrawerDynamicLayoutTest {
+    @Rule
+    public final ActivityTestRule<DrawerDynamicLayoutActivity> mActivityTestRule =
+            new ActivityTestRule<>(DrawerDynamicLayoutActivity.class);
 
     @UiThreadTest
     @After
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutActivity.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutActivity.java
index 0dba273..72fef54 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutActivity.java
@@ -58,9 +58,9 @@
     protected void onContentViewSet() {
         super.onContentViewSet();
 
-        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
-        mDrawer = (ListView) findViewById(R.id.start_drawer);
-        mContent = (TextView) findViewById(R.id.content_text);
+        mDrawerLayout = findViewById(R.id.drawer_layout);
+        mDrawer = findViewById(R.id.start_drawer);
+        mContent = findViewById(R.id.content_text);
 
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
 
@@ -75,7 +75,7 @@
 
         // Find the toolbar in our layout and set it as the support action bar on the activity.
         // This is required to have the drawer slide "over" the toolbar.
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar = findViewById(R.id.toolbar);
         mToolbar.setTitle(R.string.drawer_title);
         setSupportActionBar(mToolbar);
 
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java
index b2e0abc..c29021b 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java
@@ -16,15 +16,12 @@
 
 package android.support.v7.app;
 
-import android.content.res.Configuration;
-import android.os.Bundle;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.testutils.Shakespeare;
 import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
@@ -52,10 +49,10 @@
     protected void onContentViewSet() {
         super.onContentViewSet();
 
-        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
-        mStartDrawer = (ListView) findViewById(R.id.start_drawer);
+        mDrawerLayout = findViewById(R.id.drawer_layout);
+        mStartDrawer = findViewById(R.id.start_drawer);
         mEndDrawer = findViewById(R.id.end_drawer);
-        mContent = (TextView) findViewById(R.id.content_text);
+        mContent = findViewById(R.id.content_text);
 
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
 
@@ -70,7 +67,7 @@
 
         // Find the toolbar in our layout and set it as the support action bar on the activity.
         // This is required to have the drawer slide "over" the toolbar.
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar = findViewById(R.id.toolbar);
         mToolbar.setTitle(R.string.drawer_title);
         setSupportActionBar(mToolbar);
 
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
index 2f43430..8578798 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
@@ -28,6 +28,8 @@
 
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
@@ -35,10 +37,16 @@
 import android.view.View;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class DrawerLayoutDoubleTest
-        extends BaseInstrumentationTestCase<DrawerLayoutDoubleActivity> {
+@RunWith(AndroidJUnit4.class)
+public class DrawerLayoutDoubleTest {
+    @Rule
+    public final ActivityTestRule<DrawerLayoutDoubleActivity> mActivityTestRule =
+            new ActivityTestRule<>(DrawerLayoutDoubleActivity.class);
+
     private CustomDrawerLayout mDrawerLayout;
 
     private View mStartDrawer;
@@ -47,10 +55,6 @@
 
     private View mContentView;
 
-    public DrawerLayoutDoubleTest() {
-        super(DrawerLayoutDoubleActivity.class);
-    }
-
     @Before
     public void setUp() {
         final DrawerLayoutDoubleActivity activity = mActivityTestRule.getActivity();
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
index d81856c..f43c909 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.support.v7.app;
 
 import static android.support.test.espresso.Espresso.onView;
@@ -47,8 +48,9 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
 import android.support.test.filters.Suppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
@@ -56,21 +58,24 @@
 import android.view.View;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
-public class DrawerLayoutTest extends BaseInstrumentationTestCase<DrawerLayoutActivity> {
+@RunWith(AndroidJUnit4.class)
+public class DrawerLayoutTest {
+    @Rule
+    public final ActivityTestRule<DrawerLayoutActivity> mActivityTestRule =
+            new ActivityTestRule<DrawerLayoutActivity>(DrawerLayoutActivity.class);
+
     private CustomDrawerLayout mDrawerLayout;
 
     private View mStartDrawer;
 
     private View mContentView;
 
-    public DrawerLayoutTest() {
-        super(DrawerLayoutActivity.class);
-    }
-
     @Before
     public void setUp() {
         final DrawerLayoutActivity activity = mActivityTestRule.getActivity();
@@ -248,7 +253,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDrawerHeight() {
         // Open the drawer so it becomes visible
         onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
@@ -291,7 +296,7 @@
     // Tests for listener(s) being notified of various events
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDrawerListenerCallbacksOnOpeningViaAPI() {
         // Register a mock listener
         DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
@@ -328,7 +333,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDrawerListenerCallbacksOnOpeningNoAnimationViaAPI() {
         // Register a mock listener
         DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
@@ -357,7 +362,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testDrawerListenerCallbacksOnClosingViaAPI() {
         // Open the drawer so it becomes visible
         onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
@@ -397,7 +402,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDrawerListenerCallbacksOnClosingNoAnimationViaAPI() {
         // Open the drawer so it becomes visible
         onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START, false));
@@ -431,7 +436,7 @@
     @Suppress
     @FlakyTest(bugId = 33659300)
     @Test
-    @SmallTest
+    @MediumTest
     public void testDrawerListenerCallbacksOnOpeningViaSwipes() {
         // Register a mock listener
         DrawerLayout.DrawerListener mockedListener = mock(DrawerLayout.DrawerListener.class);
@@ -476,7 +481,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testDrawerListenerCallbacksOnClosingViaSwipes() {
         // Open the drawer so it becomes visible
         onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
@@ -524,7 +529,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testDrawerLockUnlock() {
         assertEquals("Drawer is unlocked in initial state",
                 DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerLayout.getDrawerLockMode(mStartDrawer));
diff --git a/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java b/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
index c2e2d16..c316e29 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
@@ -23,15 +23,19 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class FragmentContentIdTest extends BaseInstrumentationTestCase<FragmentContentIdActivity> {
-
-    public FragmentContentIdTest() {
-        super(FragmentContentIdActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class FragmentContentIdTest {
+    @Rule
+    public final ActivityTestRule<FragmentContentIdActivity> mActivityTestRule =
+            new ActivityTestRule<>(FragmentContentIdActivity.class);
 
     @SmallTest
     @Test
@@ -48,5 +52,4 @@
         // And that fragment_b is displayed
         onView(withId(R.id.fragment_b)).check(matches(isDisplayed()));
     }
-
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
index cdaecc0..e7d485b 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
@@ -16,8 +16,71 @@
 
 package android.support.v7.app;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v7.widget.Toolbar;
+import android.view.KeyEvent;
+import android.view.Window;
+
+import org.junit.Test;
+
 public class KeyEventsTestCaseWithToolbar extends BaseKeyEventsTestCase<ToolbarAppCompatActivity> {
     public KeyEventsTestCaseWithToolbar() {
         super(ToolbarAppCompatActivity.class);
     }
+
+    @Test
+    @SmallTest
+    @Override
+    public void testMenuKeyEventReachesActivity() throws InterruptedException {
+        // With Toolbar, MENU key gets sent-to (and consumed by) Toolbar rather than Activity
+    }
+
+    @Test
+    @SmallTest
+    public void testMenuKeyOpensToolbarMenu() {
+        // Base test only checks that *a* menu is opened, we check here that the toolbar's menu
+        // specifically is opened.
+        Toolbar toolbar = mActivityTestRule.getActivity().getToolbar();
+        assertFalse(toolbar.isOverflowMenuShowing());
+
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        assertTrue(toolbar.isOverflowMenuShowing());
+
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        assertFalse(toolbar.isOverflowMenuShowing());
+    }
+
+    @Test
+    @SmallTest
+    public void testOpenMenuOpensToolbarMenu() throws Throwable {
+        if (!mActivityTestRule.getActivity().getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            return;
+        }
+        Toolbar toolbar = mActivityTestRule.getActivity().getToolbar();
+        assertFalse(toolbar.isOverflowMenuShowing());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivityTestRule.getActivity().openOptionsMenu();
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        assertTrue(toolbar.isOverflowMenuShowing());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivityTestRule.getActivity().closeOptionsMenu();
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        assertFalse(toolbar.isOverflowMenuShowing());
+    }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
index 4f6310c..4f450ff 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
@@ -16,28 +16,41 @@
 
 package android.support.v7.app;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Build;
 import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.testutils.BaseTestActivity;
-import android.view.KeyCharacterMap;
+import android.support.v7.widget.Toolbar;
 import android.view.KeyEvent;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.Window;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class KeyboardShortcutsTestCaseWithToolbar
-        extends BaseKeyboardShortcutsTestCase<ToolbarAppCompatActivity> {
-    public KeyboardShortcutsTestCaseWithToolbar() {
-        super(ToolbarAppCompatActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class KeyboardShortcutsTestCaseWithToolbar {
+    @Rule
+    public final ActivityTestRule<ToolbarAppCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(ToolbarAppCompatActivity.class);
 
     @Test
     @SmallTest
     public void testAccessActionBar() throws Throwable {
-        final BaseTestActivity activity = getActivity();
+        // Since O, we rely on keyboard navigation clusters for jumping to actionbar
+        if (Build.VERSION.SDK_INT <= 25) {
+            return;
+        }
+        final BaseTestActivity activity = mActivityTestRule.getActivity();
 
         final View editText = activity.findViewById(android.support.v7.appcompat.test.R.id.editText);
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -47,11 +60,10 @@
             }
         });
 
-        getInstrumentation().waitForIdleSync();
-        sendControlChar('<');
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        sendMetaKey(KeyEvent.KEYCODE_TAB);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        // Should jump to the action bar after control-<
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -60,8 +72,11 @@
                 assertTrue(toolbar.hasFocus());
             }
         });
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
-        getInstrumentation().waitForIdleSync();
+        // We rely on keyboard navigation clusters for jumping out of actionbar since normal
+        // navigation won't leaves it.
+        sendMetaKey(KeyEvent.KEYCODE_TAB);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // Should jump to the first view again.
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -70,20 +85,75 @@
                 assertTrue(editText.hasFocus());
             }
         });
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_UP);
-        getInstrumentation().waitForIdleSync();
     }
 
-    private void sendControlChar(char key) throws Throwable {
-        KeyEvent tempEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
-        KeyCharacterMap map = tempEvent.getKeyCharacterMap();
-        KeyEvent[] events = map.getEvents(new char[] {key});
-        for (int i = 0; i < events.length; i++) {
-            long time = SystemClock.uptimeMillis();
-            KeyEvent event = events[i];
-            KeyEvent controlKey = new KeyEvent(time, time, event.getAction(), event.getKeyCode(),
-                    event.getRepeatCount(), event.getMetaState() | KeyEvent.META_CTRL_ON);
-            getInstrumentation().sendKeySync(controlKey);
-        }
+    @Test
+    @SmallTest
+    public void testKeyShortcuts() throws Throwable {
+        final ToolbarAppCompatActivity activity = mActivityTestRule.getActivity();
+
+        final Toolbar toolbar =
+                activity.findViewById(android.support.v7.appcompat.test.R.id.toolbar);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                toolbar.inflateMenu(android.support.v7.appcompat.test.R.menu.sample_actions);
+            }
+        });
+
+        final Boolean[] shareItemClicked = new Boolean[]{false};
+        toolbar.getMenu().findItem(android.support.v7.appcompat.test.R.id.action_alpha_shortcut)
+                .setOnMenuItemClickListener(
+                new MenuItem.OnMenuItemClickListener() {
+                        @Override
+                        public boolean onMenuItemClick(MenuItem item) {
+                            return shareItemClicked[0] = true;
+                        }
+                    });
+
+        final Window.Callback cb = activity.getWindow().getCallback();
+
+        // Make sure valid menu shortcuts get handled by toolbar menu
+        long now = SystemClock.uptimeMillis();
+        final KeyEvent handledShortcutKey = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_A, 0, KeyEvent.META_CTRL_ON);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(cb.dispatchKeyShortcutEvent(handledShortcutKey));
+            }
+        });
+        assertTrue(shareItemClicked[0]);
+
+        final KeyEvent unhandledShortcutKey = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_D, 0, KeyEvent.META_CTRL_ON);
+
+        // Make sure we aren't eating unused shortcuts.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertFalse(cb.dispatchKeyShortcutEvent(unhandledShortcutKey));
+            }
+        });
+
+        activity.resetCounters();
+
+        // Make sure that unhandled shortcuts don't prepare menus (since toolbar is handling that).
+        InstrumentationRegistry.getInstrumentation().sendKeySync(unhandledShortcutKey);
+        assertEquals(1, activity.mKeyShortcutCount);
+        assertEquals(0, activity.mPrepareMenuCount);
+        assertEquals(0, activity.mCreateMenuCount);
+    }
+
+    private void sendMetaKey(int keyCode) throws Throwable {
+        long time = SystemClock.uptimeMillis();
+        KeyEvent keyDown = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode,
+                0, KeyEvent.META_META_ON);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(keyDown);
+        time = SystemClock.uptimeMillis();
+        KeyEvent keyUp = new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
+                0, KeyEvent.META_META_ON);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(keyUp);
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
index 0502ad4..892a554 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
@@ -22,9 +22,12 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.support.annotation.RequiresApi;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.ContextWrapperFrameLayout;
 import android.support.v7.widget.AppCompatAutoCompleteTextView;
@@ -44,14 +47,15 @@
 import android.widget.LinearLayout;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class LayoutInflaterFactoryTestCase
-        extends BaseInstrumentationTestCase<LayoutInflaterFactoryTestActivity> {
-
-    public LayoutInflaterFactoryTestCase() {
-        super(LayoutInflaterFactoryTestActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class LayoutInflaterFactoryTestCase {
+    @Rule
+    public final ActivityTestRule<LayoutInflaterFactoryTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(LayoutInflaterFactoryTestActivity.class);
 
     @Before
     public void setup() {
@@ -63,7 +67,7 @@
     @Test
     @SmallTest
     public void testAndroidThemeInflation() {
-        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         assertThemedContext(inflater.inflate(R.layout.layout_android_theme, null));
     }
 
@@ -71,17 +75,18 @@
     @Test
     @SmallTest
     public void testAppThemeInflation() {
-        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         assertThemedContext(inflater.inflate(R.layout.layout_app_theme, null));
     }
 
     // Propagation of themed context to children only works on API 11+.
     @SdkSuppress(minSdkVersion = 11)
+    @RequiresApi(11)
     @UiThreadTest
     @Test
     @SmallTest
     public void testAndroidThemeWithChildrenInflation() {
-        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         final ViewGroup root = (ViewGroup) inflater.inflate(
                 R.layout.layout_android_theme_children, null);
         assertThemedContext(root);
@@ -91,7 +96,7 @@
     @Test
     @SmallTest
     public void testThemedInflationWithUnattachedParent() {
-        final Context activity = getActivity();
+        final Context activity = mActivityTestRule.getActivity();
 
         // Create a parent but not attached
         final LinearLayout parent = new LinearLayout(activity);
@@ -193,15 +198,15 @@
     @Test
     @SmallTest
     public void testDeclarativeOnClickWithContextWrapper() {
-        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         View view = inflater.inflate(R.layout.layout_button_themed_onclick, null);
 
         assertTrue(view.performClick());
-        assertTrue(getActivity().wasDeclarativeOnClickCalled());
+        assertTrue(mActivityTestRule.getActivity().wasDeclarativeOnClickCalled());
     }
 
     private void verifyAppCompatWidgetInflation(final int layout, final Class<?> expectedClass) {
-        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         View view = inflater.inflate(layout, null);
         assertSame("View is " + expectedClass.getSimpleName(), expectedClass,
                 view.getClass());
diff --git a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
index 7564e94..2981ad4 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
@@ -27,23 +27,28 @@
 
 import android.app.Instrumentation;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.content.ContextCompat;
 import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-@MediumTest
-@SdkSuppress(minSdkVersion = 14)
-public class NightModeTestCase extends BaseInstrumentationTestCase<NightModeActivity> {
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class NightModeTestCase {
+    @Rule
+    public final ActivityTestRule<NightModeActivity> mActivityTestRule;
 
     private static final String STRING_DAY = "DAY";
     private static final String STRING_NIGHT = "NIGHT";
 
     public NightModeTestCase() {
-        super(NightModeActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(NightModeActivity.class);
     }
 
     @Before
@@ -116,7 +121,7 @@
         });
 
         // Now wait for the recreate
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // Now check that the text has changed, signifying that night resources are being used
         onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
@@ -128,7 +133,7 @@
         final FakeTwilightManager twilightManager = new FakeTwilightManager();
         TwilightManager.setInstance(twilightManager);
 
-        final NightModeActivity activity = getActivity();
+        final NightModeActivity activity = mActivityTestRule.getActivity();
 
         // Set MODE_NIGHT_AUTO so that we will change to night mode automatically
         setLocalNightModeAndWaitForRecreate(activity, AppCompatDelegate.MODE_NIGHT_AUTO);
@@ -138,7 +143,8 @@
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                final Instrumentation instrumentation = getInstrumentation();
+                final Instrumentation instrumentation =
+                        InstrumentationRegistry.getInstrumentation();
                 // Now fool the Activity into thinking that it has gone into the background
                 instrumentation.callActivityOnPause(activity);
                 instrumentation.callActivityOnStop(activity);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java b/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
index 9042363..c75c3fe 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
@@ -19,11 +19,17 @@
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.widget.Toolbar;
+import android.view.KeyEvent;
+import android.view.Menu;
 
 public class ToolbarAppCompatActivity extends BaseTestActivity {
 
     private Toolbar mToolbar;
 
+    public int mCreateMenuCount;
+    public int mPrepareMenuCount;
+    public int mKeyShortcutCount;
+
     @Override
     protected int getContentViewLayoutResId() {
         return R.layout.toolbar_decor_content;
@@ -31,7 +37,33 @@
 
     @Override
     protected void onContentViewSet() {
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar = findViewById(R.id.toolbar);
         setSupportActionBar(mToolbar);
     }
+
+    public Toolbar getToolbar() {
+        return mToolbar;
+    }
+
+    @Override
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        ++mKeyShortcutCount;
+        return super.onKeyShortcut(keyCode, event);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        ++mCreateMenuCount;
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        ++mPrepareMenuCount;
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    public void resetCounters() {
+        mCreateMenuCount = mPrepareMenuCount = mKeyShortcutCount = 0;
+    }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java b/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java
index 217ccbc..6305301 100644
--- a/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java
+++ b/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java
@@ -16,12 +16,9 @@
 package android.support.v7.custom;
 
 import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowInsets;
-
 import android.support.v4.widget.DrawerLayout;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
 
 public class CustomDrawerLayout extends DrawerLayout {
     private int mSystemWindowInsetTop;
@@ -38,6 +35,7 @@
         super(context, attrs, defStyle);
     }
 
+    @Override
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
         mSystemWindowInsetTop = insets.getSystemWindowInsetTop();
         return super.dispatchApplyWindowInsets(insets);
diff --git a/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java b/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
index bfb456a..d6b9f83 100644
--- a/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
@@ -23,25 +23,31 @@
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v7.app.AppCompatActivity;
-import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.testutils.TestUtils;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class AppCompatResourcesTestCase extends BaseInstrumentationTestCase<AppCompatActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AppCompatResourcesTestCase {
+    @Rule
+    public final ActivityTestRule<AppCompatActivity> mActivityTestRule;
 
     public AppCompatResourcesTestCase() {
-        super(AppCompatActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AppCompatActivity.class);
     }
 
     @Test
     public void testGetColorStateListWithThemedAttributes() {
-        final Activity context = getActivity();
+        final Activity context = mActivityTestRule.getActivity();
 
         final int colorForegound = TestUtils.getThemeAttrColor(
                 context, android.R.attr.colorForeground);
@@ -65,7 +71,7 @@
 
     @Test
     public void testGetDrawableVectorResource() {
-        final Activity context = getActivity();
+        final Activity context = mActivityTestRule.getActivity();
         assertNotNull(AppCompatResources.getDrawable(context, R.drawable.test_vector_off));
     }
 
diff --git a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java
index 5080252..574ed6b 100644
--- a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java
+++ b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java
@@ -16,6 +16,7 @@
 
 package android.support.v7.testutils;
 
+import android.app.Instrumentation;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -26,7 +27,10 @@
 import android.support.annotation.NonNull;
 import android.support.v4.util.Pair;
 import android.support.v7.widget.TintTypedArray;
+import android.view.InputDevice;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewParent;
 
 import junit.framework.Assert;
@@ -279,4 +283,61 @@
             a.recycle();
         }
     }
+
+    /**
+     * Emulates a tap on a point relative to the top-left corner of the passed {@link View}. Offset
+     * parameters are used to compute the final screen coordinates of the tap point.
+     *
+     * @param instrumentation the instrumentation used to run the test
+     * @param anchorView the anchor view to determine the tap location on the screen
+     * @param offsetX extra X offset for the tap
+     * @param offsetY extra Y offset for the tap
+     */
+    public static void emulateTapOnView(Instrumentation instrumentation, View anchorView,
+            int offsetX, int offsetY) {
+        final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
+        // Get anchor coordinates on the screen
+        final int[] viewOnScreenXY = new int[2];
+        anchorView.getLocationOnScreen(viewOnScreenXY);
+        int xOnScreen = viewOnScreenXY[0] + offsetX;
+        int yOnScreen = viewOnScreenXY[1] + offsetY;
+        final long downTime = SystemClock.uptimeMillis();
+
+        injectDownEvent(instrumentation, downTime, xOnScreen, yOnScreen);
+        injectMoveEventForTap(instrumentation, downTime, touchSlop, xOnScreen, yOnScreen);
+        injectUpEvent(instrumentation, downTime, false, xOnScreen, yOnScreen);
+
+        // Wait for the system to process all events in the queue
+        instrumentation.waitForIdleSync();
+    }
+
+    private static long injectDownEvent(Instrumentation instrumentation, long downTime,
+            int xOnScreen, int yOnScreen) {
+        MotionEvent eventDown = MotionEvent.obtain(
+                downTime, downTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 1);
+        eventDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        instrumentation.sendPointerSync(eventDown);
+        eventDown.recycle();
+        return downTime;
+    }
+
+    private static void injectMoveEventForTap(Instrumentation instrumentation, long downTime,
+            int touchSlop, int xOnScreen, int yOnScreen) {
+        MotionEvent eventMove = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE,
+                xOnScreen + (touchSlop / 2.0f), yOnScreen + (touchSlop / 2.0f), 1);
+        eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        instrumentation.sendPointerSync(eventMove);
+        eventMove.recycle();
+    }
+
+
+    private static void injectUpEvent(Instrumentation instrumentation, long downTime,
+            boolean useCurrentEventTime, int xOnScreen, int yOnScreen) {
+        long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
+        MotionEvent eventUp = MotionEvent.obtain(
+                downTime, eventTime, MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 1);
+        eventUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        instrumentation.sendPointerSync(eventUp);
+        eventUp.recycle();
+    }
 }
\ No newline at end of file
diff --git a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
index 3e092c4..e3f25a9 100644
--- a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
+++ b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
@@ -25,10 +25,9 @@
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.widget.CheckedTextView;
 import android.widget.ImageView;
-import junit.framework.Assert;
+
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
diff --git a/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTest.java b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTest.java
new file mode 100644
index 0000000..bfcb80b
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package android.support.v7.view;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.internal.view.SupportMenuItem;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.widget.PopupMenu;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test SupportMenuInflater
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SupportMenuInflaterTest {
+
+    private SupportMenuInflaterTestActivity mActivity;
+    private MenuInflater mMenuInflater;
+    private Menu mMenu;
+
+    @Rule
+    public ActivityTestRule<SupportMenuInflaterTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(SupportMenuInflaterTestActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.getActivity();
+        mMenuInflater = mActivity.getMenuInflater();
+        mMenu = new PopupMenu(mActivity, null).getMenu();
+    }
+
+    @Test
+    public void testInflateFromXml() {
+        mMenuInflater.inflate(R.menu.shortcut, mMenu);
+        SupportMenuItem mMenuItem;
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.no_modifiers);
+        assertEquals('a', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getAlphabeticModifiers());
+        assertEquals('1', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getNumericModifiers());
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.default_modifiers);
+        assertEquals('b', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getAlphabeticModifiers());
+        assertEquals('2', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getNumericModifiers());
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.single_modifier);
+        assertEquals('c', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_SHIFT_ON, mMenuItem.getAlphabeticModifiers());
+        assertEquals('3', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_SHIFT_ON, mMenuItem.getNumericModifiers());
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.multiple_modifiers);
+        assertEquals('d', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON,
+                mMenuItem.getAlphabeticModifiers());
+        assertEquals('4', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON,
+                mMenuItem.getNumericModifiers());
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTestActivity.java b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTestActivity.java
new file mode 100644
index 0000000..45ad0d9
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTestActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package android.support.v7.view;
+
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+
+public class SupportMenuInflaterTestActivity extends BaseTestActivity {
+
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_textview_activity;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseAutoSizeTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseAutoSizeTest.java
new file mode 100644
index 0000000..cc6f91b
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseAutoSizeTest.java
@@ -0,0 +1,1210 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.IdRes;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.widget.AutoSizeableTextView;
+import android.support.v4.widget.TextViewCompat;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Base class for testing auto-size-text enabled views in appcompat-v7 that implement the
+ * <code>AutoSizeableTextView</code> interface. Extensions of this class run all tests
+ * from here and can add test cases specific to the functionality they add to the relevant
+ * base view class.
+ */
+public abstract class AppCompatBaseAutoSizeTest<A extends BaseTestActivity,
+        T extends TextView & AutoSizeableTextView> {
+
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
+    protected A mActivity;
+    protected Instrumentation mInstrumentation;
+    protected ViewGroup mContainer;
+
+    public AppCompatBaseAutoSizeTest(Class clazz) {
+        mActivityTestRule = new ActivityTestRule<A>(clazz);
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityTestRule.getActivity();
+        mContainer = mActivity.findViewById(R.id.container);
+    }
+
+    @Test
+    @MediumTest
+    @SdkSuppress(minSdkVersion = 16)
+    // public TextView#getMaxLines only introduced in API 16.
+    public void testAutoSizeCallers_setMaxLines() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Configure layout params and auto-size both in pixels to dodge flakiness on different
+        // devices.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
+        final String text = "one two three four five six seven eight nine ten";
+        mActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+                ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                        1 /* autoSizeMinTextSize */,
+                        5000 /* autoSizeMaxTextSize */,
+                        1 /* autoSizeStepGranularity */,
+                        TypedValue.COMPLEX_UNIT_PX);
+                autoSizeView.setText(text);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        float initialSize = 0;
+        for (int i = 1; i < 10; i++) {
+            final int maxLines = i;
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            float expectedSmallerSize = autoSizeView.getTextSize();
+            if (i == 1) {
+                initialSize = expectedSmallerSize;
+            }
+
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines + 1);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            assertTrue(expectedSmallerSize <= autoSizeView.getTextSize());
+        }
+        assertTrue(initialSize < autoSizeView.getTextSize());
+
+        initialSize = 999999;
+        for (int i = 10; i > 1; i--) {
+            final int maxLines = i;
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            float expectedLargerSize = autoSizeView.getTextSize();
+            if (i == 10) {
+                initialSize = expectedLargerSize;
+            }
+
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines - 1);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            assertTrue(expectedLargerSize >= autoSizeView.getTextSize());
+        }
+        assertTrue(initialSize > autoSizeView.getTextSize());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_autoSizeCalledWhenTypeChanged() throws Throwable {
+        final T view = mContainer.findViewById(R.id.view_text);
+
+        // Make sure we pick an already inflated non auto-sized text view.
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+        // Set the text size to a very low value in order to prepare for auto-size.
+        final int customTextSize = 3;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                view.setTextSize(TypedValue.COMPLEX_UNIT_PX, customTextSize);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(customTextSize, view.getTextSize(), 0f);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        // The size of the text should have changed.
+        assertNotEquals(customTextSize, view.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setText() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+
+        // Configure layout params and auto-size both in pixels to dodge flakiness on different
+        // devices.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
+        mActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+                ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                        1, 5000, 1, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final String initialText = "13characters ";
+        final StringBuilder textToSet = new StringBuilder().append(initialText);
+        float initialSize = 0;
+
+        // As we add characters the text size shrinks.
+        for (int i = 0; i < 10; i++) {
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+
+            mInstrumentation.waitForIdleSync();
+            float expectedLargerSize = autoSizeView.getTextSize();
+            if (i == 0) {
+                initialSize = expectedLargerSize;
+            }
+
+            textToSet.append(initialText);
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            assertTrue(expectedLargerSize >= autoSizeView.getTextSize());
+        }
+        assertTrue(initialSize > autoSizeView.getTextSize());
+
+        initialSize = 9999999;
+        // As we remove characters the text size expands.
+        for (int i = 9; i >= 0; i--) {
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            float expectedSmallerSize = autoSizeView.getTextSize();
+            if (i == 0) {
+                initialSize = expectedSmallerSize;
+            }
+
+            textToSet.replace((textToSet.length() - initialText.length()),
+                    textToSet.length(), "");
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertTrue(autoSizeView.getTextSize() >= expectedSmallerSize);
+        }
+        assertTrue(autoSizeView.getTextSize() > initialSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_equivalentConfigurations() throws Throwable {
+        final DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
+        final int minTextSize = 10;
+        final int maxTextSize = 20;
+        final int granularity = 2;
+        final int unit = TypedValue.COMPLEX_UNIT_SP;
+
+        final T granularityView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) granularityView).setAutoSizeTextTypeUniformWithConfiguration(
+                minTextSize, maxTextSize, granularity, unit);
+
+        final T presetView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) presetView).setAutoSizeTextTypeUniformWithPresetSizes(
+                new int[]{minTextSize, 12, 14, 16, 18, maxTextSize}, unit);
+
+        // The TextViews have been configured differently but the end result should be nearly
+        // identical.
+        final int expectedAutoSizeType = AppCompatTextView.AUTO_SIZE_TEXT_TYPE_UNIFORM;
+        assertEquals(expectedAutoSizeType,
+                ((AutoSizeableTextView) granularityView).getAutoSizeTextType());
+        assertEquals(expectedAutoSizeType,
+                ((AutoSizeableTextView) presetView).getAutoSizeTextType());
+
+        final int expectedMinTextSizeInPx = Math.round(
+                TypedValue.applyDimension(unit, minTextSize, dm));
+        assertEquals(expectedMinTextSizeInPx,
+                ((AutoSizeableTextView) granularityView).getAutoSizeMinTextSize());
+        assertEquals(expectedMinTextSizeInPx,
+                ((AutoSizeableTextView) presetView).getAutoSizeMinTextSize());
+
+        final int expectedMaxTextSizeInPx = Math.round(
+                TypedValue.applyDimension(unit, maxTextSize, dm));
+        assertEquals(expectedMaxTextSizeInPx,
+                ((AutoSizeableTextView) granularityView).getAutoSizeMaxTextSize());
+        assertEquals(expectedMaxTextSizeInPx,
+                ((AutoSizeableTextView) presetView).getAutoSizeMaxTextSize());
+
+        // Configured with granularity.
+        assertEquals(Math.round(TypedValue.applyDimension(unit, granularity, dm)),
+                ((AutoSizeableTextView) granularityView).getAutoSizeStepGranularity());
+        // Configured with preset values, there is no granularity.
+        assertEquals(-1,
+                ((AutoSizeableTextView) presetView).getAutoSizeStepGranularity());
+
+        // Both TextViews generate exactly the same sizes in pixels to choose from when auto-sizing.
+        assertArrayEquals(
+                ((AutoSizeableTextView) granularityView).getAutoSizeTextAvailableSizes(),
+                ((AutoSizeableTextView) presetView).getAutoSizeTextAvailableSizes());
+
+        final String someText = "This is a string";
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
+        // Configure identically and attach to layout.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                granularityView.setLayoutParams(layoutParams);
+                presetView.setLayoutParams(layoutParams);
+
+                LinearLayout ll = mActivity.findViewById(R.id.container);
+                ll.removeAllViews();
+                ll.addView(granularityView);
+                ll.addView(presetView);
+
+                granularityView.setText(someText);
+                presetView.setText(someText);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(granularityView.getTextSize(), presetView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setHeight() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                R.id.view_autosize_uniform, true);
+        // Do not force exact height only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                200,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setHeight(autoSizeView.getHeight() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setCompoundDrawables() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        final float initialTextSize = autoSizeView.getTextSize();
+        final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
+                R.drawable.test_drawable_red, null);
+        drawable.setBounds(0, 0, autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setCompoundDrawables(drawable, drawable, drawable, drawable);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setCompoundDrawablesRelative() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 17) {
+            final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                    R.id.view_autosize_uniform, false);
+            final float initialTextSize = autoSizeView.getTextSize();
+            final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
+                    R.drawable.test_drawable_red, null);
+            drawable.setBounds(0, 0, autoSizeView.getWidth() / 3,
+                    autoSizeView.getHeight() / 3);
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT >= 17) {
+                        autoSizeView.setCompoundDrawablesRelative(
+                                drawable, drawable, drawable, drawable);
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertTrue(autoSizeView.getTextSize() < initialTextSize);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setCompoundDrawablePadding() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Prepare a larger layout in order not to hit the min value easily.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setWidth(autoSizeView.getWidth() * 2);
+                autoSizeView.setHeight(autoSizeView.getHeight() * 2);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        // Setup the drawables before setting their padding in order to modify the available
+        // space and trigger a resize.
+        final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
+                R.drawable.test_drawable_red, null);
+        drawable.setBounds(0, 0, autoSizeView.getWidth() / 4, autoSizeView.getHeight() / 4);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setCompoundDrawables(
+                        drawable, drawable, drawable, drawable);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setCompoundDrawablePadding(
+                        autoSizeView.getCompoundDrawablePadding() + 10);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setPadding() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setPadding(
+                        autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3,
+                        autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setPaddingRelative() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 16) {
+            final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                    R.id.view_autosize_uniform, false);
+            final float initialTextSize = autoSizeView.getTextSize();
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT > 16) {
+                        autoSizeView.setPaddingRelative(
+                                autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3,
+                                autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3);
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertTrue(autoSizeView.getTextSize() < initialTextSize);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setTypeface() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setText("The typeface change needs a bit more text then "
+                        + "the default used for this batch of tests in order to get to resize text."
+                        + " The resize function is always called but even with different typefaces "
+                        + "there may not be a need to resize text because it just fits. The longer "
+                        + "the text, the higher the chance for a resize. And here is yet another "
+                        + "sentence to make sure this test is not flaky. Not flaky at all.");
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Typeface differentTypeface = Typeface.MONOSPACE;
+                if (autoSizeView.getTypeface() == Typeface.MONOSPACE) {
+                    differentTypeface = Typeface.SANS_SERIF;
+                }
+                autoSizeView.setTypeface(differentTypeface);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float changedTextSize = autoSizeView.getTextSize();
+
+        // Don't really know if it is larger or smaller (depends on the typeface chosen above),
+        // but it should definitely have changed.
+        assertNotEquals(initialTextSize, changedTextSize, 0f);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setTypeface(autoSizeView.getTypeface());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(changedTextSize, autoSizeView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setLetterSpacing() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 21) {
+            final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                    R.id.view_autosize_uniform, false);
+            final float initialTextSize = autoSizeView.getTextSize();
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        autoSizeView.setLetterSpacing(
+                                autoSizeView.getLetterSpacing() * 1.5f + 4.5f);
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            final float changedTextSize = autoSizeView.getTextSize();
+
+            assertTrue(changedTextSize < initialTextSize);
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        autoSizeView.setLetterSpacing(autoSizeView.getLetterSpacing());
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertEquals(changedTextSize, autoSizeView.getTextSize(), 0f);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setMaxHeight() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                true);
+        // Do not force exact height only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                200,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setMaxHeight(autoSizeView.getHeight() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setMaxWidth() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                true);
+        // Do not force exact width only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT,
+                200);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setMaxWidth(autoSizeView.getWidth() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() != initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setWidth() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                true);
+        // Do not force exact width only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT,
+                200);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setWidth(autoSizeView.getWidth() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() != initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setTextSizeIsNoOp() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        final float initialTextSize = autoSizeView.getTextSize();
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setTextSize(initialTextSize + 123f);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(initialTextSize, autoSizeView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setHorizontallyScrolling() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Horizontal scrolling is expected to be deactivated for this test.
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setHorizontallyScrolling(true);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertTrue(autoSizeView.getTextSize() > initialTextSize);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setHorizontallyScrolling(false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(initialTextSize, autoSizeView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSize_setEllipsize() throws Throwable {
+        final T autoSizeView = mActivity.findViewById(R.id.view_autosize_uniform_predef_sizes);
+        final int initialAutoSizeType = ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType();
+        final int initialMinTextSize =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        final int initialMaxTextSize =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        final int initialAutoSizeGranularity =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity();
+        final int initialSizes =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextAvailableSizes().length;
+
+        assertEquals(null, autoSizeView.getEllipsize());
+        // Verify styled attributes.
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM, initialAutoSizeType);
+        assertNotEquals(-1, initialMinTextSize);
+        assertNotEquals(-1, initialMaxTextSize);
+        // Because this TextView has been configured to use predefined sizes.
+        assertEquals(-1, initialAutoSizeGranularity);
+        assertNotEquals(0, initialSizes);
+
+        final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setEllipsize(newEllipsizeValue);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(newEllipsizeValue, autoSizeView.getEllipsize());
+        // Beside the ellipsis no auto-size attribute has changed.
+        assertEquals(initialAutoSizeType,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        assertEquals(initialMinTextSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        assertEquals(initialMaxTextSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        assertEquals(initialAutoSizeGranularity,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+        assertEquals(initialSizes,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextAvailableSizes().length);
+    }
+
+    @Test
+    @MediumTest
+    public void testEllipsize_setAutoSize() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                view.setEllipsize(newEllipsizeValue);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(newEllipsizeValue, view.getEllipsize());
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+        assertEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMinTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMaxTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) view).getAutoSizeStepGranularity());
+        assertEquals(0,
+                ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes().length);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(newEllipsizeValue, view.getEllipsize());
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+        // The auto-size defaults have been used.
+        assertNotEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMinTextSize());
+        assertNotEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMaxTextSize());
+        assertNotEquals(-1, ((AutoSizeableTextView) view).getAutoSizeStepGranularity());
+        assertNotEquals(0,
+                ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes().length);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_obtainStyledAttributesUsingPredefinedSizes() {
+        DisplayMetrics m = mActivity.getResources().getDisplayMetrics();
+        final T autoSizeViewUniform = mActivity.findViewById(
+                R.id.view_autosize_uniform_predef_sizes);
+
+        // In arrays.xml predefined the step sizes as: 5px, 11dip, 19sp, 29pt, 43mm and 53in.
+        // TypedValue can not use the math library and instead rounds the value by adding
+        // 0.5f when obtaining styled attributes. Check TypedValue#complexToDimensionPixelSize(...)
+        int[] expectedSizesInPx = new int[] {
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 5f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 11f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 19f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, 29f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 43f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, 53f, m))};
+
+        boolean containsValueFromExpectedSizes = false;
+        final int textSize = (int) autoSizeViewUniform.getTextSize();
+        for (int i = 0; i < expectedSizesInPx.length; i++) {
+            if (expectedSizesInPx[i] == textSize) {
+                containsValueFromExpectedSizes = true;
+                break;
+            }
+        }
+        assertTrue(containsValueFromExpectedSizes);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_obtainStyledAttributesPredefinedSizesFiltering() {
+        T autoSizeViewUniform = mActivity.findViewById(
+                R.id.view_autosize_uniform_predef_sizes_redundant_values);
+
+        // In arrays.xml predefined the step sizes as: 40px, 10px, 10px, 10px, 0dp.
+        final int[] expectedSizes = new int[] {10, 40};
+        assertArrayEquals(expectedSizes,
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_predefinedSizesFilteringAndSorting() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+
+        final int[] predefinedSizes = new int[] {400, 0, 10, 40, 10, 10, 0, 0};
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeUniformWithPresetSizes(
+                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(new int[] {10, 40, 400},
+                ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes());
+    }
+
+    @Test(expected = NullPointerException.class)
+    @SmallTest
+    public void testAutoSizeUniform_predefinedSizesNullArray() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+
+        final int[] predefinedSizes = null;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeUniformWithPresetSizes(
+                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_predefinedSizesEmptyArray() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final int[] defaultSizes = ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes();
+        assertNotNull(defaultSizes);
+        assertTrue(defaultSizes.length > 0);
+
+        final int[] predefinedSizes = new int[0];
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeUniformWithPresetSizes(
+                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final int[] newSizes = ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes();
+        assertNotNull(defaultSizes);
+        assertArrayEquals(defaultSizes, newSizes);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_buildsSizes() throws Throwable {
+        final T autoSizeViewUniform = mActivity.findViewById(R.id.view_autosize_uniform);
+
+        // Verify that the interval limits are both included.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeViewUniform)
+                        .setAutoSizeTextTypeUniformWithConfiguration(10, 20, 2,
+                                TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(
+                new int[] {10, 12, 14, 16, 18, 20},
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeViewUniform)
+                        .setAutoSizeTextTypeUniformWithConfiguration(
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeMinTextSize(),
+                                19,
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeStepGranularity(),
+                                TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(
+                new int[] {10, 12, 14, 16, 18},
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeViewUniform)
+                        .setAutoSizeTextTypeUniformWithConfiguration(
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeMinTextSize(),
+                                21,
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeStepGranularity(),
+                                TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(
+                new int[] {10, 12, 14, 16, 18, 20},
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeTextDefaults() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        // Min/Max/Granularity values for auto-sizing are 0 because they are not used.
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        assertEquals(-1,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        // Min/Max default values for auto-sizing XY have been loaded.
+        final int minSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        final int maxSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        assertTrue(0 < minSize);
+        assertTrue(minSize < maxSize);
+        assertNotEquals(0,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        ((AutoSizeableTextView) autoSizeView)
+                .setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        // Min/Max values for auto-sizing XY have been cleared.
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        assertEquals(-1,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeStepGranularity() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int initialValue = -1;
+        assertEquals(initialValue,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int defaultValue = 1; // 1px.
+        // If the auto-size type is AUTO_SIZE_TEXT_TYPE_UNIFORM then it means autoSizeView went
+        // through the auto-size setup and given that 0 is an invalid value it changed it to the
+        // default.
+        assertEquals(defaultValue,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        final int newValue = 33;
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize(),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize(),
+                newValue,
+                TypedValue.COMPLEX_UNIT_PX);
+        assertEquals(newValue, ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeMinTextSize() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int minSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+
+        // This is just a test check to verify the next assertions. If this fails it is a problem
+        // of this test setup (we need at least 2 units).
+        assertTrue((maxSize - minSize) > 1);
+        final int newMinSize = maxSize - 1;
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                newMinSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize(),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_PX);
+
+        assertEquals(newMinSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        // Max size has not changed.
+        assertEquals(maxSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                newMinSize,
+                newMinSize + 10,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_SP);
+
+        // It does not matter which unit has been used to set the min size, the getter always
+        // returns it in pixels.
+        assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                newMinSize, mActivity.getResources().getDisplayMetrics())),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_whenMaxLessThanMin() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                10, 9, 1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_minLessThanZero() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                -1, 9, 1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_maxLessThanZero() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                10, -1, 1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_granularityLessThanZero() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                10, 20, -1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeMaxTextSize() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int minSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+
+        final int newMaxSize = maxSize + 11;
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize(),
+                newMaxSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_PX);
+
+        assertEquals(newMaxSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        // Min size has not changed.
+        assertEquals(minSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize(),
+                newMaxSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_SP);
+        // It does not matter which unit has been used to set the max size, the getter always
+        // returns it in pixels.
+        assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                newMaxSize, mActivity.getResources().getDisplayMetrics())),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setTextSizeChangesSizeWhenAutoSizeDisabled() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Disable auto-size.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float newTextSizeInPx = 123f;
+        assertNotEquals(newTextSizeInPx, autoSizeView.getTextSize(), 0f);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSizeInPx);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(newTextSizeInPx, autoSizeView.getTextSize(), 0f);
+    }
+
+    /**
+     * Some View attributes require non-fixed width and/or layout height. This function removes
+     * all other existing views from the layout leaving only one auto-size TextView (for exercising
+     * the auto-size behavior) which has been set up to suit the test needs.
+     *
+     * @param viewId The id of the view to prepare.
+     * @param shouldWrapLayoutContent Specifies if the layout params should wrap content
+     *
+     * @return a View configured for auto size tests.
+     */
+    private T prepareAndRetrieveAutoSizeTestData(final @IdRes int viewId,
+            final boolean shouldWrapLayoutContent) throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                LinearLayout ll = mActivity.findViewById(R.id.container);
+                T targetedView = mActivity.findViewById(viewId);
+                ll.removeAllViews();
+                ll.addView(targetedView);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final T view = mActivity.findViewById(viewId);
+        if (shouldWrapLayoutContent) {
+            // Do not force exact width or height.
+            final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                    LinearLayout.LayoutParams.WRAP_CONTENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT);
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    view.setLayoutParams(layoutParams);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+        }
+
+        return view;
+    }
+
+    // Returns a new instance of the auto-sizable view for mActivity.
+    protected abstract T getNewAutoSizeViewInstance();
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java
index c8398ed..431dc0e 100755
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java
@@ -66,7 +66,7 @@
     @SmallTest
     public void testImageTintingAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
@@ -144,7 +144,7 @@
     @SmallTest
     public void testImageTintingAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int emeraldDefault = ResourcesCompat.getColor(
@@ -216,7 +216,7 @@
     @SmallTest
     public void testImageOpaqueTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_tinted_no_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
@@ -271,7 +271,7 @@
     @SmallTest
     public void testImageTranslucentTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_untinted_no_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int emeraldDefault = ResourcesCompat.getColor(
@@ -361,7 +361,7 @@
     @SmallTest
     public void testImageTintingAcrossBackgroundTintingChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
index 6e4e6c0..cafe2cf 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
@@ -36,9 +36,10 @@
 import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.graphics.ColorUtils;
-import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.AppCompatTintableViewActions;
 import android.support.v7.testutils.BaseTestActivity;
@@ -47,7 +48,9 @@
 import android.view.ViewGroup;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Base class for testing custom view extensions in appcompat-v7 that implement the
@@ -55,21 +58,25 @@
  * from here and add test cases specific to the functionality they add to the relevant
  * base view class (such as <code>AppCompatTextView</code>'s all-caps support).
  */
-public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
     protected ViewGroup mContainer;
 
+    protected A mActivity;
     protected Resources mResources;
 
     public AppCompatBaseViewTest(Class clazz) {
-        super(clazz);
+        mActivityTestRule = new ActivityTestRule<A>(clazz);
     }
 
     @Before
     public void setUp() {
-        final A activity = mActivityTestRule.getActivity();
-        mContainer = (ViewGroup) activity.findViewById(R.id.container);
-        mResources = activity.getResources();
+        mActivity = mActivityTestRule.getActivity();
+        mContainer = mActivity.findViewById(R.id.container);
+        mResources = mActivity.getResources();
     }
 
     /**
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeActivity.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeActivity.java
new file mode 100644
index 0000000..cd5c6a9
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+
+/**
+ * This activity is used to test the auto-size feature of the {@link AppCompatButton}
+ * class.
+ */
+public class AppCompatButtonAutoSizeActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_button_autosize_activity;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeTest.java
new file mode 100644
index 0000000..cc54463
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatButtonAutoSizeTest extends
+        AppCompatBaseAutoSizeTest<AppCompatButtonAutoSizeActivity, AppCompatButton> {
+
+    public AppCompatButtonAutoSizeTest() {
+        super(AppCompatButtonAutoSizeActivity.class);
+    }
+
+    @Override
+    protected AppCompatButton getNewAutoSizeViewInstance() {
+        return new AppCompatButton(mActivity);
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
index bfc2bc1..2144941 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
@@ -21,7 +21,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 
@@ -58,20 +57,20 @@
         // the transformed text that is set on the Layout object used to draw the final
         // (transformed) content.
         assertEquals("Button starts in all caps on", text1.toUpperCase(),
-                button1.getLayout().getText());
+                button1.getLayout().getText().toString());
         assertEquals("Button starts in all caps off", text2,
-                button2.getLayout().getText());
+                button2.getLayout().getText().toString());
 
         // Toggle all-caps mode on the two buttons
         onView(withId(R.id.button_caps1)).perform(
                 setTextAppearance(R.style.TextStyleAllCapsOff));
         assertEquals("Button is now in all caps off", text1,
-                button1.getLayout().getText());
+                button1.getLayout().getText().toString());
 
         onView(withId(R.id.button_caps2)).perform(
                 setTextAppearance(R.style.TextStyleAllCapsOn));
         assertEquals("Button is now in all caps on", text2.toUpperCase(),
-                button2.getLayout().getText());
+                button2.getLayout().getText().toString());
     }
 
     @Test
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
index b7a28af..a9da3f5 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
@@ -28,6 +28,7 @@
 import android.support.annotation.ColorInt;
 import android.support.annotation.ColorRes;
 import android.support.annotation.IdRes;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.res.ResourcesCompat;
@@ -88,11 +89,13 @@
         onView(withText(itemText)).perform(click());
     }
 
+    @LargeTest
     @Test
     public void testPopupThemingFromXmlAttribute() {
         verifySpinnerPopupTheming(R.id.view_magenta_themed_popup, R.color.test_magenta, true);
     }
 
+    @LargeTest
     @Test
     public void testUnthemedPopupRuntimeTheming() {
         final AppCompatSpinner spinner =
@@ -106,6 +109,7 @@
         verifySpinnerPopupTheming(R.id.view_unthemed_popup, R.color.test_green, false);
     }
 
+    @LargeTest
     @Test
     public void testThemedPopupRuntimeTheming() {
         final AppCompatSpinner spinner =
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java
new file mode 100644
index 0000000..5736dd9
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+
+/**
+ * This activity is used to test the auto-size feature of the {@link AppCompatTextView}
+ * class.
+ */
+public class AppCompatTextViewAutoSizeActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_textview_autosize_activity;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
new file mode 100644
index 0000000..45da1e4
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import static junit.framework.TestCase.assertEquals;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.widget.TextViewCompat;
+import android.support.v7.appcompat.test.R;
+import android.text.method.SingleLineTransformationMethod;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatTextViewAutoSizeTest extends
+        AppCompatBaseAutoSizeTest<AppCompatTextViewAutoSizeActivity, AppCompatTextView> {
+
+    public AppCompatTextViewAutoSizeTest() {
+        super(AppCompatTextViewAutoSizeActivity.class);
+    }
+
+    @Override
+    protected AppCompatTextView getNewAutoSizeViewInstance() {
+        return new AppCompatTextView(mActivity);
+    }
+
+    @Test
+    public void testAutoSize_notSupportedByEditText() throws Throwable {
+        final AppCompatEditText autoSizeEditText = mActivity.findViewById(
+                R.id.edittext_autosize_uniform);
+        // Do not force exact height only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                200,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeEditText.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeEditText.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeEditText.setHeight(autoSizeEditText.getHeight() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(initialTextSize, autoSizeEditText.getTextSize(), 0f);
+    }
+
+    @Test
+    public void testAutoSizeWithMaxLines_shouldNotThrowException() throws Throwable {
+        // the layout contains an instance of CustomTextViewWithTransformationMethod
+        final AppCompatTextView textView = (AppCompatTextView) mActivity
+                .getLayoutInflater().inflate(R.layout.textview_autosize_maxlines, null);
+        assertTrue(textView instanceof CustomTextViewWithTransformationMethod);
+        // Method added in API 16.
+        if (Build.VERSION.SDK_INT >= 16) {
+            assertEquals(1, textView.getMaxLines());
+        }
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType());
+        assertTrue(textView.getTransformationMethod() instanceof SingleLineTransformationMethod);
+    }
+
+    public static class CustomTextViewWithTransformationMethod extends AppCompatTextView {
+        public CustomTextViewWithTransformationMethod(Context context) {
+            super(context);
+            init();
+        }
+
+        public CustomTextViewWithTransformationMethod(Context context,
+                @Nullable AttributeSet attrs) {
+            super(context, attrs);
+            init();
+        }
+
+        public CustomTextViewWithTransformationMethod(Context context,
+                @Nullable AttributeSet attrs, int defStyleAttr) {
+            super(context, attrs, defStyleAttr);
+            init();
+        }
+
+        private void init() {
+            setTransformationMethod(new SingleLineTransformationMethod());
+        }
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
index d9c3c55..dd0c146 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
@@ -21,10 +21,19 @@
 import static android.support.v7.testutils.TestUtilsActions.setTextAppearance;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 
+import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.v4.content.ContextCompat;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.test.R;
 import android.widget.TextView;
 
@@ -37,6 +46,7 @@
 @SmallTest
 public class AppCompatTextViewTest
         extends AppCompatBaseViewTest<AppCompatTextViewActivity, AppCompatTextView> {
+
     public AppCompatTextViewTest() {
         super(AppCompatTextViewActivity.class);
     }
@@ -46,49 +56,47 @@
         final String text1 = mResources.getString(R.string.sample_text1);
         final String text2 = mResources.getString(R.string.sample_text2);
 
-        final AppCompatTextView textView1 =
-                (AppCompatTextView) mContainer.findViewById(R.id.text_view_caps1);
-        final AppCompatTextView textView2 =
-                (AppCompatTextView) mContainer.findViewById(R.id.text_view_caps2);
+        final AppCompatTextView textView1 = mContainer.findViewById(R.id.text_view_caps1);
+        final AppCompatTextView textView2 = mContainer.findViewById(R.id.text_view_caps2);
 
         // Note that TextView.getText() returns the original text. We are interested in
         // the transformed text that is set on the Layout object used to draw the final
         // (transformed) content.
-        assertEquals("Text view starts in all caps on", text1.toUpperCase(),
-                textView1.getLayout().getText());
-        assertEquals("Text view starts in all caps off", text2,
-                textView2.getLayout().getText());
+        assertEquals("Text view starts in all caps on",
+                text1.toUpperCase(), textView1.getLayout().getText().toString());
+        assertEquals("Text view starts in all caps off",
+                text2, textView2.getLayout().getText().toString());
 
         // Toggle all-caps mode on the two text views
         onView(withId(R.id.text_view_caps1)).perform(
                 setTextAppearance(R.style.TextStyleAllCapsOff));
-        assertEquals("Text view is now in all caps off", text1,
-                textView1.getLayout().getText());
+        assertEquals("Text view is now in all caps off",
+                text1, textView1.getLayout().getText().toString());
 
         onView(withId(R.id.text_view_caps2)).perform(
                 setTextAppearance(R.style.TextStyleAllCapsOn));
-        assertEquals("Text view is now in all caps on", text2.toUpperCase(),
-                textView2.getLayout().getText());
+        assertEquals("Text view is now in all caps on",
+                text2.toUpperCase(), textView2.getLayout().getText().toString());
     }
 
     @Test
     public void testAppCompatAllCapsFalseOnButton() {
         final String text = mResources.getString(R.string.sample_text2);
         final AppCompatTextView textView =
-                (AppCompatTextView) mContainer.findViewById(R.id.text_view_app_allcaps_false);
+                 mContainer.findViewById(R.id.text_view_app_allcaps_false);
 
         assertEquals("Text view is not in all caps", text, textView.getLayout().getText());
     }
 
     @Test
     public void testTextColorSetHex() {
-        final TextView textView = (TextView) mContainer.findViewById(R.id.view_text_color_hex);
+        final TextView textView =  mContainer.findViewById(R.id.view_text_color_hex);
         assertEquals(Color.RED, textView.getCurrentTextColor());
     }
 
     @Test
     public void testTextColorSetColorStateList() {
-        final TextView textView = (TextView) mContainer.findViewById(R.id.view_text_color_csl);
+        final TextView textView =  mContainer.findViewById(R.id.view_text_color_csl);
 
         onView(withId(R.id.view_text_color_csl)).perform(setEnabled(true));
         assertEquals(ContextCompat.getColor(textView.getContext(), R.color.ocean_default),
@@ -101,14 +109,13 @@
 
     @Test
     public void testTextColorSetThemeAttrHex() {
-        final TextView textView = (TextView) mContainer.findViewById(R.id.view_text_color_primary);
+        final TextView textView =  mContainer.findViewById(R.id.view_text_color_primary);
         assertEquals(Color.BLUE, textView.getCurrentTextColor());
     }
 
     @Test
     public void testTextColorSetThemeAttrColorStateList() {
-        final TextView textView = (TextView)
-                mContainer.findViewById(R.id.view_text_color_secondary);
+        final TextView textView =  mContainer.findViewById(R.id.view_text_color_secondary);
 
         onView(withId(R.id.view_text_color_secondary)).perform(setEnabled(true));
         assertEquals(ContextCompat.getColor(textView.getContext(), R.color.sand_default),
@@ -118,4 +125,139 @@
         assertEquals(ContextCompat.getColor(textView.getContext(), R.color.sand_disabled),
                 textView.getCurrentTextColor());
     }
+
+    private void verifyTextLinkColor(TextView textView) {
+        ColorStateList linkColorStateList = textView.getLinkTextColors();
+        assertEquals(ContextCompat.getColor(textView.getContext(), R.color.lilac_default),
+                linkColorStateList.getColorForState(new int[] { android.R.attr.state_enabled}, 0));
+        assertEquals(ContextCompat.getColor(textView.getContext(), R.color.lilac_disabled),
+                linkColorStateList.getColorForState(new int[] { -android.R.attr.state_enabled}, 0));
+    }
+
+    @Test
+    public void testTextLinkColor() {
+        verifyTextLinkColor((TextView) mContainer.findViewById(R.id.view_text_link_enabled));
+        verifyTextLinkColor((TextView) mContainer.findViewById(R.id.view_text_link_disabled));
+    }
+
+    @Test
+    public void testFontResources_setInStringFamilyName() {
+        TextView textView =
+                mContainer.findViewById(R.id.textview_fontresource_fontfamily_string_resource);
+        assertNotNull(textView.getTypeface());
+        // Pre-L, Typeface always resorts to native for a Typeface object, hence giving you a
+        // different one each call.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            assertEquals(Typeface.SANS_SERIF, textView.getTypeface());
+        }
+        textView = mContainer.findViewById(R.id.textview_fontresource_fontfamily_string_direct);
+        assertNotNull(textView.getTypeface());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            assertEquals(Typeface.SANS_SERIF, textView.getTypeface());
+        }
+    }
+
+    @Test
+    public void testFontResources_setInXmlFamilyName() {
+        TextView textView = mContainer.findViewById(R.id.textview_fontresource_fontfamily);
+        Typeface expected = ResourcesCompat.getFont(mActivity, R.font.samplefont);
+
+        assertEquals(expected, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResourcesXml_setInXmlFamilyName() {
+        TextView textView = mContainer.findViewById(R.id.textview_fontxmlresource_fontfamily);
+        Typeface expected = ResourcesCompat.getFont(mActivity, R.font.samplexmlfont);
+
+        assertEquals(expected, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResourcesXml_setInXmlFamilyNameWithTextStyle() {
+        TextView textView =
+                mContainer.findViewById(R.id.textview_fontxmlresource_fontfamily_textstyle);
+
+        assertNotEquals(Typeface.DEFAULT, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResourcesXml_setInXmlFamilyNameWithTextStyle2() {
+        TextView textView =
+                mContainer.findViewById(R.id.textview_fontxmlresource_fontfamily_textstyle2);
+
+        assertNotEquals(Typeface.DEFAULT, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResources_setInXmlStyle() {
+        TextView textView = mContainer.findViewById(R.id.textview_fontresource_style);
+        Typeface expected = ResourcesCompat.getFont(mActivity, R.font.samplefont);
+
+        assertEquals(expected, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResourcesXml_setInXmlStyle() {
+        TextView textView = mContainer.findViewById(R.id.textview_fontxmlresource_style);
+        Typeface expected = ResourcesCompat.getFont(mActivity, R.font.samplexmlfont);
+
+        assertEquals(expected, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResources_setInXmlTextAppearance() {
+        TextView textView = mContainer.findViewById(R.id.textview_fontresource_textAppearance);
+        Typeface expected = ResourcesCompat.getFont(mActivity, R.font.samplefont);
+
+        assertEquals(expected, textView.getTypeface());
+    }
+
+    @Test
+    public void testFontResourcesXml_setInXmlTextAppearance() {
+        TextView textView = mContainer.findViewById(R.id.textview_fontxmlresource_textAppearance);
+        Typeface expected = ResourcesCompat.getFont(mActivity, R.font.samplexmlfont);
+
+        assertEquals(expected, textView.getTypeface());
+    }
+
+    @Test
+    public void testTextStyle_setTextStyleInStyle() {
+        // TextView has a TextAppearance by default, but the textStyle can be overriden in style.
+        TextView textView = mContainer.findViewById(R.id.textview_textStyleOverride);
+
+        assertEquals(Typeface.ITALIC, textView.getTypeface().getStyle());
+    }
+
+    @Test
+    public void testTextStyle_setTextStyleDirectly() {
+        TextView textView = mContainer.findViewById(R.id.textview_textStyleDirect);
+
+        assertEquals(Typeface.ITALIC, textView.getTypeface().getStyle());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testFontResources_setTextAppearance() {
+        TextView textView = mContainer.findViewById(R.id.textview_simple);
+
+        TextViewCompat.setTextAppearance(textView, R.style.TextView_FontResourceWithStyle);
+
+        assertNotEquals(Typeface.DEFAULT, textView.getTypeface());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSetTextAppearance_resetTypeface() throws PackageManager.NameNotFoundException {
+        TextView textView = mContainer.findViewById(R.id.textview_simple);
+
+        TextViewCompat.setTextAppearance(textView, R.style.TextView_SansSerif);
+        Typeface firstTypeface = textView.getTypeface();
+
+        TextViewCompat.setTextAppearance(textView, R.style.TextView_Serif);
+        Typeface secondTypeface = textView.getTypeface();
+        assertNotNull(firstTypeface);
+        assertNotNull(secondTypeface);
+        assertNotEquals(firstTypeface, secondTypeface);
+    }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
index 34c07a3..2681d0f 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
@@ -29,6 +29,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.eq;
@@ -46,8 +47,8 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
-import android.support.v7.app.BaseInstrumentationTestCase;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -61,9 +62,16 @@
 import android.widget.TextView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class ListPopupWindowTest extends BaseInstrumentationTestCase<PopupTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class ListPopupWindowTest {
+    @Rule
+    public final ActivityTestRule<PopupTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(PopupTestActivity.class);
+
     private FrameLayout mContainer;
 
     private Button mButton;
@@ -87,20 +95,16 @@
         }
     }
 
-    public ListPopupWindowTest() {
-        super(PopupTestActivity.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         final PopupTestActivity activity = mActivityTestRule.getActivity();
-        mContainer = (FrameLayout) activity.findViewById(R.id.container);
-        mButton = (Button) mContainer.findViewById(R.id.test_button);
+        mContainer = activity.findViewById(R.id.container);
+        mButton = mContainer.findViewById(R.id.test_button);
         mItemClickListener = new PopupItemClickListener();
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testBasicContent() {
         Builder popupBuilder = new Builder();
         popupBuilder.wireToActionButton();
@@ -154,7 +158,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDismissalViaAPI() throws Throwable {
         Builder popupBuilder = new Builder().withDismissListener();
         popupBuilder.wireToActionButton();
@@ -178,9 +182,14 @@
         Builder popupBuilder = new Builder().setModal(setupAsModal).withDismissListener();
         popupBuilder.wireToActionButton();
 
+        final View.OnClickListener mockContainerClickListener = mock(View.OnClickListener.class);
         // Also register a click listener on the top-level container
-        View.OnClickListener mockContainerClickListener = mock(View.OnClickListener.class);
-        mContainer.setOnClickListener(mockContainerClickListener);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mContainer.setOnClickListener(mockContainerClickListener);
+            }
+        });
 
         onView(withId(R.id.test_button)).perform(click());
         assertTrue("Popup window showing", mListPopupWindow.isShowing());
@@ -241,19 +250,19 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDismissalOutsideNonModal() throws Throwable {
         testDismissalViaTouch(false);
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDismissalOutsideModal() throws Throwable {
         testDismissalViaTouch(true);
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testItemClickViaEvent() {
         Builder popupBuilder = new Builder().withItemClickListener();
         popupBuilder.wireToActionButton();
@@ -263,7 +272,7 @@
 
         // Verify that our menu item click listener hasn't been called yet
         verify(popupBuilder.mOnItemClickListener, never()).onItemClick(
-                any(AdapterView.class), any(View.class), any(int.class), any(int.class));
+                any(AdapterView.class), any(View.class), any(int.class), anyLong());
 
         final View mainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
         onView(withText("Charlie"))
@@ -273,14 +282,14 @@
         // position. Note that we use any() for other parameters, as we don't want to tie ourselves
         // to the specific implementation details of how ListPopupWindow displays its content.
         verify(popupBuilder.mOnItemClickListener, times(1)).onItemClick(
-                any(AdapterView.class), any(View.class), eq(2), any(int.class));
+                any(AdapterView.class), any(View.class), eq(2), anyLong());
 
         // Our item click listener also dismisses the popup
         assertFalse("Popup window not showing after click", mListPopupWindow.isShowing());
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testItemClickViaAPI() throws Throwable {
         Builder popupBuilder = new Builder().withItemClickListener();
         popupBuilder.wireToActionButton();
@@ -290,7 +299,7 @@
 
         // Verify that our menu item click listener hasn't been called yet
         verify(popupBuilder.mOnItemClickListener, never()).onItemClick(
-                any(AdapterView.class), any(View.class), any(int.class), any(int.class));
+                any(AdapterView.class), any(View.class), any(int.class), anyLong());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -303,7 +312,7 @@
         // position. Note that we use any() for other parameters, as we don't want to tie ourselves
         // to the specific implementation details of how ListPopupWindow displays its content.
         verify(popupBuilder.mOnItemClickListener, times(1)).onItemClick(
-                any(AdapterView.class), any(View.class), eq(1), any(int.class));
+                any(AdapterView.class), any(View.class), eq(1), anyLong());
         // Our item click listener also dismisses the popup
         assertFalse("Popup window not showing after click", mListPopupWindow.isShowing());
     }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java b/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
index 7d6e39f..7be67a6 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
@@ -51,8 +51,11 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
-import android.support.v7.app.BaseInstrumentationTestCase;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.MenuItemCompat;
 import android.support.v7.appcompat.test.R;
+import android.text.TextUtils;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -65,10 +68,18 @@
 import org.hamcrest.Matcher;
 import org.hamcrest.Matchers;
 import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class PopupMenuTest extends BaseInstrumentationTestCase<PopupTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class PopupMenuTest {
+    @Rule
+    public final ActivityTestRule<PopupTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(PopupTestActivity.class);
+
     // Since PopupMenu doesn't expose any access to the underlying
     // implementation (like ListPopupWindow.getListView), we're relying on the
     // class name of the list view from MenuPopupWindow that is being used
@@ -86,27 +97,55 @@
 
     private View mMainDecorView;
 
-    public PopupMenuTest() {
-        super(PopupTestActivity.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         final PopupTestActivity activity = mActivityTestRule.getActivity();
-        mContainer = (FrameLayout) activity.findViewById(R.id.container);
-        mButton = (Button) mContainer.findViewById(R.id.test_button);
+        mContainer = activity.findViewById(R.id.container);
+        mButton = mContainer.findViewById(R.id.test_button);
         mResources = mActivityTestRule.getActivity().getResources();
         mMainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
     }
 
+    @After
+    public void tearDown() {
+        if (mPopupMenu != null) {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+                @Override
+                public void run() {
+                    mPopupMenu.dismiss();
+                }
+            });
+        }
+    }
+
     @Test
     @MediumTest
-    public void testBasicContent() {
+    public void testBasicContent() throws Throwable {
         final Builder menuBuilder = new Builder();
         menuBuilder.wireToActionButton();
 
         onView(withId(R.id.test_button)).perform(click());
         assertNotNull("Popup menu created", mPopupMenu);
+
+        final MenuItem hightlightItem = mPopupMenu.getMenu().findItem(R.id.action_highlight);
+        assertEquals(mResources.getString(R.string.popup_menu_highlight_description),
+                MenuItemCompat.getContentDescription(hightlightItem));
+        assertEquals(mResources.getString(R.string.popup_menu_highlight_tooltip),
+                MenuItemCompat.getTooltipText(hightlightItem));
+
+        final MenuItem editItem = mPopupMenu.getMenu().findItem(R.id.action_edit);
+        assertNotNull(MenuItemCompat.getContentDescription(hightlightItem));
+        assertNotNull(MenuItemCompat.getTooltipText(hightlightItem));
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                MenuItemCompat.setContentDescription(editItem,
+                        mResources.getString(R.string.popup_menu_edit_description));
+                MenuItemCompat.setTooltipText(editItem,
+                        mResources.getString(R.string.popup_menu_edit_tooltip));
+            }
+        });
+
         // Unlike ListPopupWindow, PopupMenu doesn't have an API to check whether it is showing.
         // Use a custom matcher to check the visibility of the drop down list view instead.
         onView(withClassName(Matchers.is(DROP_DOWN_CLASS_NAME)))
@@ -120,11 +159,15 @@
         onData(anything()).atPosition(0).check(matches(isDisplayed()));
         onView(withText(mResources.getString(R.string.popup_menu_highlight)))
                 .inRoot(withDecorView(not(is(mMainDecorView))))
-                .check(matches(isDisplayed()));
+                .check(matches(isDisplayed()))
+                .check(matches(selfOrParentWithContentDescription(
+                        mResources.getString(R.string.popup_menu_highlight_description))));
         onData(anything()).atPosition(1).check(matches(isDisplayed()));
         onView(withText(mResources.getString(R.string.popup_menu_edit)))
                 .inRoot(withDecorView(not(is(mMainDecorView))))
-                .check(matches(isDisplayed()));
+                .check(matches(isDisplayed()))
+                .check(matches(selfOrParentWithContentDescription(
+                        mResources.getString(R.string.popup_menu_edit_description))));
         onData(anything()).atPosition(2).check(matches(isDisplayed()));
         onView(withText(mResources.getString(R.string.popup_menu_delete)))
                 .inRoot(withDecorView(not(is(mMainDecorView))))
@@ -601,4 +644,33 @@
             });
         }
     }
+
+    private static Matcher<View> selfOrParentWithContentDescription(final CharSequence expected) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("self of parent has content description: " + expected);
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                return TextUtils.equals(expected, getSelfOrParentContentDescription(view));
+            }
+
+            private CharSequence getSelfOrParentContentDescription(View view) {
+                while (view != null) {
+                    final CharSequence contentDescription = view.getContentDescription();
+                    if (contentDescription != null) {
+                        return contentDescription;
+                    }
+                    final ViewParent parent = view.getParent();
+                    if (!(parent instanceof View)) {
+                        break;
+                    }
+                    view = (View) parent;
+                }
+                return null;
+            }
+        };
+    }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/SearchViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/SearchViewTest.java
new file mode 100644
index 0000000..ad9f09b
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/SearchViewTest.java
@@ -0,0 +1,307 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.Resources;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.appcompat.test.R;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link SearchView}.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class SearchViewTest {
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+    private SearchView mSearchView;
+
+    @Rule
+    public ActivityTestRule<SearchViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(SearchViewTestActivity.class);
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testConstructor() {
+        new SearchView(mActivity);
+
+        new SearchView(mActivity, null);
+
+        new SearchView(mActivity, null, android.R.attr.searchViewStyle);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAttributesFromXml() {
+        SearchView searchViewWithAttributes =
+                (SearchView) mActivity.findViewById(R.id.search_view_with_defaults);
+        assertEquals(mActivity.getString(R.string.search_query_hint),
+                searchViewWithAttributes.getQueryHint());
+        assertFalse(searchViewWithAttributes.isIconfiedByDefault());
+        assertEquals(EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS | EditorInfo.TYPE_CLASS_TEXT,
+                searchViewWithAttributes.getInputType());
+        assertEquals(EditorInfo.IME_ACTION_DONE, searchViewWithAttributes.getImeOptions());
+        assertEquals(mActivity.getResources().getDimensionPixelSize(R.dimen.search_view_max_width),
+                searchViewWithAttributes.getMaxWidth());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAccessIconified() {
+        mSearchView.setIconified(true);
+        assertTrue(mSearchView.isIconified());
+
+        mSearchView.setIconified(false);
+        assertFalse(mSearchView.isIconified());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAccessIconifiedByDefault() {
+        mSearchView.setIconifiedByDefault(true);
+        assertTrue(mSearchView.isIconfiedByDefault());
+
+        mSearchView.setIconifiedByDefault(false);
+        assertFalse(mSearchView.isIconfiedByDefault());
+    }
+
+    @Test
+    public void testDenyIconifyingNonInconifiableView() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconifiedByDefault(false);
+                mSearchView.setIconified(false);
+            }
+        });
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconified(true);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Since our search view is marked with iconifiedByDefault=false, call to setIconified
+        // with true us going to be ignored, as detailed in the class-level documentation of
+        // SearchView.
+        assertFalse(mSearchView.isIconified());
+    }
+
+    @Test
+    public void testDenyIconifyingInconifiableView() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconifiedByDefault(true);
+                mSearchView.setIconified(false);
+            }
+        });
+
+        final SearchView.OnCloseListener mockDenyCloseListener =
+                mock(SearchView.OnCloseListener.class);
+        when(mockDenyCloseListener.onClose()).thenReturn(Boolean.TRUE);
+        mSearchView.setOnCloseListener(mockDenyCloseListener);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconified(true);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Our mock listener is configured to return true from its onClose, thereby preventing
+        // the iconify request to be completed. Check that the listener was called and that the
+        // search view is not iconified.
+        verify(mockDenyCloseListener, times(1)).onClose();
+        assertFalse(mSearchView.isIconified());
+    }
+
+    @Test
+    public void testAllowIconifyingInconifiableView() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconifiedByDefault(true);
+                mSearchView.setIconified(false);
+            }
+        });
+
+        final SearchView.OnCloseListener mockAllowCloseListener =
+                mock(SearchView.OnCloseListener.class);
+        when(mockAllowCloseListener.onClose()).thenReturn(Boolean.FALSE);
+        mSearchView.setOnCloseListener(mockAllowCloseListener);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconified(true);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Our mock listener is configured to return false from its onClose, thereby allowing
+        // the iconify request to be completed. Check that the listener was called and that the
+        // search view is not iconified.
+        verify(mockAllowCloseListener, times(1)).onClose();
+        assertTrue(mSearchView.isIconified());
+    }
+
+    @Test
+    public void testAccessMaxWidth() throws Throwable {
+        final Resources res = mActivity.getResources();
+        final int maxWidth1 = res.getDimensionPixelSize(R.dimen.search_view_max_width);
+        final int maxWidth2 = res.getDimensionPixelSize(R.dimen.search_view_max_width2);
+
+        // Set search view to not be iconified before running max-width tests
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconified(false);
+            }
+        });
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setMaxWidth(maxWidth1);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(maxWidth1, mSearchView.getMaxWidth());
+        assertTrue(mSearchView.getWidth() <= maxWidth1);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setMaxWidth(maxWidth2);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(maxWidth2, mSearchView.getMaxWidth());
+        assertTrue(mSearchView.getWidth() <= maxWidth2);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAccessQuery() {
+        mSearchView.setIconified(false);
+
+        final SearchView.OnQueryTextListener mockQueryTextListener =
+                mock(SearchView.OnQueryTextListener.class);
+        when(mockQueryTextListener.onQueryTextSubmit(anyString())).thenReturn(Boolean.TRUE);
+        mSearchView.setOnQueryTextListener(mockQueryTextListener);
+
+        mSearchView.setQuery("alpha", false);
+        assertTrue(TextUtils.equals("alpha", mSearchView.getQuery()));
+        // Since we passed false as the second parameter to setQuery, our query text listener
+        // should have been invoked only with text change
+        verify(mockQueryTextListener, times(1)).onQueryTextChange("alpha");
+        verify(mockQueryTextListener, never()).onQueryTextSubmit(anyString());
+
+        mSearchView.setQuery("beta", true);
+        assertTrue(TextUtils.equals("beta", mSearchView.getQuery()));
+        // Since we passed true as the second parameter to setQuery, our query text listener
+        // should have been invoked on both callbacks
+        verify(mockQueryTextListener, times(1)).onQueryTextChange("beta");
+        verify(mockQueryTextListener, times(1)).onQueryTextSubmit("beta");
+
+        mSearchView.setQuery("gamma", true);
+        assertTrue(TextUtils.equals("gamma", mSearchView.getQuery()));
+        // Since we passed true as the second parameter to setQuery, our query text listener
+        // should have been invoked on both callbacks
+        verify(mockQueryTextListener, times(1)).onQueryTextChange("gamma");
+        verify(mockQueryTextListener, times(1)).onQueryTextSubmit("gamma");
+
+        verifyNoMoreInteractions(mockQueryTextListener);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAccessQueryHint() {
+        mSearchView.setQueryHint("hint 1");
+        assertTrue(TextUtils.equals("hint 1", mSearchView.getQueryHint()));
+
+        mSearchView.setQueryHint("hint 2");
+        assertTrue(TextUtils.equals("hint 2", mSearchView.getQueryHint()));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAccessInputType() {
+        mSearchView.setInputType(InputType.TYPE_CLASS_NUMBER
+                | InputType.TYPE_NUMBER_FLAG_DECIMAL
+                | InputType.TYPE_NUMBER_FLAG_SIGNED);
+        assertEquals(InputType.TYPE_CLASS_NUMBER
+                | InputType.TYPE_NUMBER_FLAG_DECIMAL
+                | InputType.TYPE_NUMBER_FLAG_SIGNED, mSearchView.getInputType());
+
+        mSearchView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+        assertEquals(InputType.TYPE_CLASS_TEXT
+                | InputType.TYPE_TEXT_FLAG_CAP_WORDS, mSearchView.getInputType());
+
+        mSearchView.setInputType(InputType.TYPE_CLASS_PHONE);
+        assertEquals(InputType.TYPE_CLASS_PHONE, mSearchView.getInputType());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAccessImeOptions() {
+        mSearchView.setImeOptions(EditorInfo.IME_ACTION_GO);
+        assertEquals(EditorInfo.IME_ACTION_GO, mSearchView.getImeOptions());
+
+        mSearchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
+        assertEquals(EditorInfo.IME_ACTION_DONE, mSearchView.getImeOptions());
+
+        mSearchView.setImeOptions(EditorInfo.IME_NULL);
+        assertEquals(EditorInfo.IME_NULL, mSearchView.getImeOptions());
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/SearchViewTestActivity.java b/v7/appcompat/tests/src/android/support/v7/widget/SearchViewTestActivity.java
new file mode 100644
index 0000000..4ac7eae
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/SearchViewTestActivity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.appcompat.test.R;
+
+public class SearchViewTestActivity extends AppCompatActivity {
+    /**
+     * Called with the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.appcompat_searchview_activity);
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/SearchView_CursorTest.java b/v7/appcompat/tests/src/android/support/v7/widget/SearchView_CursorTest.java
new file mode 100644
index 0000000..ea919e8
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/SearchView_CursorTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.BaseColumns;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
+import android.support.v4.widget.CursorAdapter;
+import android.support.v4.widget.SimpleCursorAdapter;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.TestUtils;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.AutoCompleteTextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link SearchView} with {@link Cursor}-backed suggestions adapter.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class SearchView_CursorTest {
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+    private SearchView mSearchView;
+
+    private static final String TEXT_COLUMN_NAME = "text";
+    private String[] mTextContent;
+
+    private CursorAdapter mSuggestionsAdapter;
+
+    // This should be protected to spy an object of this class.
+    protected class MyQueryTextListener implements SearchView.OnQueryTextListener {
+        @Override
+        public boolean onQueryTextSubmit(String s) {
+            return false;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String s) {
+            if (mSuggestionsAdapter == null) {
+                return false;
+            }
+            if (!enoughToFilter()) {
+                return false;
+            }
+
+            final MatrixCursor c = new MatrixCursor(
+                    new String[] { BaseColumns._ID, TEXT_COLUMN_NAME});
+            for (int i = 0; i < mTextContent.length; i++) {
+                if (mTextContent[i].toLowerCase().startsWith(s.toLowerCase())) {
+                    c.addRow(new Object[]{i, mTextContent[i]});
+                }
+            }
+            mSuggestionsAdapter.swapCursor(c);
+            return false;
+        }
+
+        private boolean enoughToFilter() {
+            final View searchSrcText = mSearchView.findViewById(R.id.search_src_text);
+            return searchSrcText instanceof AutoCompleteTextView
+                    && ((AutoCompleteTextView) searchSrcText).enoughToFilter();
+        }
+    }
+
+    // This should be protected to spy an object of this class.
+    protected class MySuggestionListener implements SearchView.OnSuggestionListener {
+        @Override
+        public boolean onSuggestionSelect(int position) {
+            return false;
+        }
+
+        @Override
+        public boolean onSuggestionClick(int position) {
+            if (mSuggestionsAdapter != null) {
+                final Cursor cursor = mSuggestionsAdapter.getCursor();
+                if (cursor != null) {
+                    cursor.moveToPosition(position);
+                    mSearchView.setQuery(cursor.getString(1), false);
+                }
+            }
+            return true;
+        }
+    }
+
+    @Rule
+    public ActivityTestRule<SearchViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(SearchViewTestActivity.class);
+
+    @UiThreadTest
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
+
+        // Local test data for the tests
+        mTextContent = new String[] { "Akon", "Bono", "Ciara", "Dido", "Diplo" };
+
+        // Use an adapter with our custom layout for each entry. The adapter "maps"
+        // the content of the text column of our cursor to the @id/text1 view in the
+        // layout.
+        mSuggestionsAdapter = new SimpleCursorAdapter(
+                mActivity,
+                R.layout.searchview_suggestion_item,
+                null,
+                new String[] { TEXT_COLUMN_NAME },
+                new int[] { android.R.id.text1 },
+                CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
+        mSearchView.setSuggestionsAdapter(mSuggestionsAdapter);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testSuggestionFiltering() {
+        final SearchView.OnQueryTextListener mockQueryTextListener =
+                spy(new MyQueryTextListener());
+        when(mockQueryTextListener.onQueryTextChange(anyString())).thenCallRealMethod();
+
+        mSearchView.setIconifiedByDefault(false);
+        mSearchView.setOnQueryTextListener(mockQueryTextListener);
+        mSearchView.requestFocus();
+
+        assertTrue(mSearchView.hasFocus());
+        assertEquals(mSuggestionsAdapter, mSearchView.getSuggestionsAdapter());
+
+        mSearchView.setQuery("Bon", false);
+        verify(mockQueryTextListener, times(1)).onQueryTextChange("Bon");
+
+        mSearchView.setQuery("Di", false);
+        verify(mockQueryTextListener, times(1)).onQueryTextChange("Di");
+    }
+
+    @Test
+    public void testSuggestionSelection() throws Throwable {
+        final SearchView.OnSuggestionListener mockSuggestionListener =
+                spy(new MySuggestionListener());
+        when(mockSuggestionListener.onSuggestionClick(anyInt())).thenCallRealMethod();
+
+        final SearchView.OnQueryTextListener mockQueryTextListener =
+                spy(new MyQueryTextListener());
+        when(mockQueryTextListener.onQueryTextChange(anyString())).thenCallRealMethod();
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setIconifiedByDefault(false);
+                mSearchView.setOnQueryTextListener(mockQueryTextListener);
+                mSearchView.setOnSuggestionListener(mockSuggestionListener);
+                mSearchView.requestFocus();
+            }
+        });
+
+        assertTrue(mSearchView.hasFocus());
+        assertEquals(mSuggestionsAdapter, mSearchView.getSuggestionsAdapter());
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSearchView.setQuery("Di", false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        verify(mockQueryTextListener, times(1)).onQueryTextChange("Di");
+
+        // Emulate click on the first suggestion - which should be Dido
+        final int suggestionRowHeight = mActivity.getResources().getDimensionPixelSize(
+                R.dimen.search_view_suggestion_row_height);
+        TestUtils.emulateTapOnView(mInstrumentation, mSearchView,
+                mSearchView.getWidth() / 2, mSearchView.getHeight() + suggestionRowHeight / 2);
+
+        // At this point we expect the click on the first suggestion to have activated a sequence
+        // of events that ends up in our suggestion listener that sets the full suggestion text
+        // as the current query. Some parts of this sequence of events are asynchronous, and those
+        // are not "caught" by Instrumentation.waitForIdleSync - which is in general not a very
+        // reliable way to wait for everything to be completed. As such, we are using our own
+        // polling check mechanism to wait until the search view's query is the fully completed
+        // suggestion for Dido. This check will time out and fail after a few seconds if anything
+        // goes wrong during the processing of the emulated tap and the code never gets to our
+        // suggestion listener
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return TextUtils.equals("Dido", mSearchView.getQuery());
+            }
+        });
+
+        // Just to be sure, verify that our spy suggestion listener was called
+        verify(mockSuggestionListener, times(1)).onSuggestionClick(0);
+        verifyNoMoreInteractions(mockSuggestionListener);
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java b/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
index eb2d462..700fe26 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
@@ -22,26 +22,31 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.app.AppCompatActivity;
-import android.support.v7.app.BaseInstrumentationTestCase;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class TintResourcesTest extends BaseInstrumentationTestCase<AppCompatActivity> {
-
-    public TintResourcesTest() {
-        super(AppCompatActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class TintResourcesTest {
+    @Rule
+    public final ActivityTestRule<AppCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(AppCompatActivity.class);
 
     @Test
     public void testTintResourcesDelegateBackToOriginalResources() {
-        final TestResources testResources = new TestResources(getActivity().getResources());
+        final TestResources testResources =
+                new TestResources(mActivityTestRule.getActivity().getResources());
         // First make sure that the flag is false
         assertFalse(testResources.wasGetDrawableCalled());
 
         // Now wrap in a TintResources instance and get a Drawable
-        final Resources tintResources = new TintResources(getActivity(), testResources);
+        final Resources tintResources =
+                new TintResources(mActivityTestRule.getActivity(), testResources);
         tintResources.getDrawable(android.R.drawable.ic_delete);
 
         // ...and assert that the flag was flipped
diff --git a/v7/cardview/Android.mk b/v7/cardview/Android.mk
index 40f6b23..56fa996 100644
--- a/v7/cardview/Android.mk
+++ b/v7/cardview/Android.mk
@@ -25,13 +25,8 @@
 LOCAL_MODULE := android-support-v7-cardview
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
-    $(call all-java-files-under,base) \
-    $(call all-java-files-under,gingerbread) \
-    $(call all-java-files-under,jellybean-mr1) \
-    $(call all-java-files-under,api21) \
     $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/v7/cardview/AndroidManifest-make.xml b/v7/cardview/AndroidManifest-make.xml
deleted file mode 100644
index c35e369..0000000
--- a/v7/cardview/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v7.cardview">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application />
-</manifest>
diff --git a/v7/cardview/AndroidManifest.xml b/v7/cardview/AndroidManifest.xml
index 78b5250..8fcf55a 100644
--- a/v7/cardview/AndroidManifest.xml
+++ b/v7/cardview/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.v7.cardview">
-    <uses-sdk android:minSdkVersion="9"/>
+    <uses-sdk android:minSdkVersion="14"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
deleted file mode 100644
index b4f2460..0000000
--- a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-@RequiresApi(21)
-@TargetApi(21)
-class CardViewApi21 implements CardViewImpl {
-
-    @Override
-    public void initialize(CardViewDelegate cardView, Context context,
-                ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
-        final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
-        cardView.setCardBackground(background);
-
-        View view = cardView.getCardView();
-        view.setClipToOutline(true);
-        view.setElevation(elevation);
-        setMaxElevation(cardView, maxElevation);
-    }
-
-    @Override
-    public void setRadius(CardViewDelegate cardView, float radius) {
-        getCardBackground(cardView).setRadius(radius);
-    }
-
-    @Override
-    public void initStatic() {
-    }
-
-    @Override
-    public void setMaxElevation(CardViewDelegate cardView, float maxElevation) {
-        getCardBackground(cardView).setPadding(maxElevation,
-                cardView.getUseCompatPadding(), cardView.getPreventCornerOverlap());
-        updatePadding(cardView);
-    }
-
-    @Override
-    public float getMaxElevation(CardViewDelegate cardView) {
-        return getCardBackground(cardView).getPadding();
-    }
-
-    @Override
-    public float getMinWidth(CardViewDelegate cardView) {
-        return getRadius(cardView) * 2;
-    }
-
-    @Override
-    public float getMinHeight(CardViewDelegate cardView) {
-        return getRadius(cardView) * 2;
-    }
-
-    @Override
-    public float getRadius(CardViewDelegate cardView) {
-        return getCardBackground(cardView).getRadius();
-    }
-
-    @Override
-    public void setElevation(CardViewDelegate cardView, float elevation) {
-        cardView.getCardView().setElevation(elevation);
-    }
-
-    @Override
-    public float getElevation(CardViewDelegate cardView) {
-        return cardView.getCardView().getElevation();
-    }
-
-    @Override
-    public void updatePadding(CardViewDelegate cardView) {
-        if (!cardView.getUseCompatPadding()) {
-            cardView.setShadowPadding(0, 0, 0, 0);
-            return;
-        }
-        float elevation = getMaxElevation(cardView);
-        final float radius = getRadius(cardView);
-        int hPadding = (int) Math.ceil(RoundRectDrawableWithShadow
-                .calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
-        int vPadding = (int) Math.ceil(RoundRectDrawableWithShadow
-                .calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
-        cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding);
-    }
-
-    @Override
-    public void onCompatPaddingChanged(CardViewDelegate cardView) {
-        setMaxElevation(cardView, getMaxElevation(cardView));
-    }
-
-    @Override
-    public void onPreventCornerOverlapChanged(CardViewDelegate cardView) {
-        setMaxElevation(cardView, getMaxElevation(cardView));
-    }
-
-    @Override
-    public void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color) {
-        getCardBackground(cardView).setColor(color);
-    }
-
-    @Override
-    public ColorStateList getBackgroundColor(CardViewDelegate cardView) {
-        return getCardBackground(cardView).getColor();
-    }
-
-    private RoundRectDrawable getCardBackground(CardViewDelegate cardView) {
-        return ((RoundRectDrawable) cardView.getCardBackground());
-    }
-}
\ No newline at end of file
diff --git a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
deleted file mode 100644
index 7e85b7f..0000000
--- a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-
-import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateVerticalPadding;
-import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateHorizontalPadding;
-
-/**
- * Very simple drawable that draws a rounded rectangle background with arbitrary corners and also
- * reports proper outline for Lollipop.
- * <p>
- * Simpler and uses less resources compared to GradientDrawable or ShapeDrawable.
- */
-@RequiresApi(21)
-@TargetApi(21)
-class RoundRectDrawable extends Drawable {
-    private float mRadius;
-    private final Paint mPaint;
-    private final RectF mBoundsF;
-    private final Rect mBoundsI;
-    private float mPadding;
-    private boolean mInsetForPadding = false;
-    private boolean mInsetForRadius = true;
-
-    private ColorStateList mBackground;
-    private PorterDuffColorFilter mTintFilter;
-    private ColorStateList mTint;
-    private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_IN;
-
-    public RoundRectDrawable(ColorStateList backgroundColor, float radius) {
-        mRadius = radius;
-        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
-        setBackground(backgroundColor);
-
-        mBoundsF = new RectF();
-        mBoundsI = new Rect();
-    }
-
-    private void setBackground(ColorStateList color) {
-        mBackground = (color == null) ?  ColorStateList.valueOf(Color.TRANSPARENT) : color;
-        mPaint.setColor(mBackground.getColorForState(getState(), mBackground.getDefaultColor()));
-    }
-
-    void setPadding(float padding, boolean insetForPadding, boolean insetForRadius) {
-        if (padding == mPadding && mInsetForPadding == insetForPadding &&
-                mInsetForRadius == insetForRadius) {
-            return;
-        }
-        mPadding = padding;
-        mInsetForPadding = insetForPadding;
-        mInsetForRadius = insetForRadius;
-        updateBounds(null);
-        invalidateSelf();
-    }
-
-    float getPadding() {
-        return mPadding;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        final Paint paint = mPaint;
-
-        final boolean clearColorFilter;
-        if (mTintFilter != null && paint.getColorFilter() == null) {
-            paint.setColorFilter(mTintFilter);
-            clearColorFilter = true;
-        } else {
-            clearColorFilter = false;
-        }
-
-        canvas.drawRoundRect(mBoundsF, mRadius, mRadius, paint);
-
-        if (clearColorFilter) {
-            paint.setColorFilter(null);
-        }
-    }
-
-    private void updateBounds(Rect bounds) {
-        if (bounds == null) {
-            bounds = getBounds();
-        }
-        mBoundsF.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
-        mBoundsI.set(bounds);
-        if (mInsetForPadding) {
-            float vInset = calculateVerticalPadding(mPadding, mRadius, mInsetForRadius);
-            float hInset = calculateHorizontalPadding(mPadding, mRadius, mInsetForRadius);
-            mBoundsI.inset((int) Math.ceil(hInset), (int) Math.ceil(vInset));
-            // to make sure they have same bounds.
-            mBoundsF.set(mBoundsI);
-        }
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
-        updateBounds(bounds);
-    }
-
-    @Override
-    public void getOutline(Outline outline) {
-        outline.setRoundRect(mBoundsI, mRadius);
-    }
-
-    void setRadius(float radius) {
-        if (radius == mRadius) {
-            return;
-        }
-        mRadius = radius;
-        updateBounds(null);
-        invalidateSelf();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mPaint.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        mPaint.setColorFilter(cf);
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    public float getRadius() {
-        return mRadius;
-    }
-
-    public void setColor(@Nullable ColorStateList color) {
-        setBackground(color);
-        invalidateSelf();
-    }
-
-    public ColorStateList getColor() {
-        return mBackground;
-    }
-
-    @Override
-    public void setTintList(ColorStateList tint) {
-        mTint = tint;
-        mTintFilter = createTintFilter(mTint, mTintMode);
-        invalidateSelf();
-    }
-
-    @Override
-    public void setTintMode(PorterDuff.Mode tintMode) {
-        mTintMode = tintMode;
-        mTintFilter = createTintFilter(mTint, mTintMode);
-        invalidateSelf();
-    }
-
-    @Override
-    protected boolean onStateChange(int[] stateSet) {
-        final int newColor = mBackground.getColorForState(stateSet, mBackground.getDefaultColor());
-        final boolean colorChanged = newColor != mPaint.getColor();
-        if (colorChanged) {
-            mPaint.setColor(newColor);
-        }
-        if (mTint != null && mTintMode != null) {
-            mTintFilter = createTintFilter(mTint, mTintMode);
-            return true;
-        }
-        return colorChanged;
-    }
-
-    @Override
-    public boolean isStateful() {
-        return (mTint != null && mTint.isStateful())
-                || (mBackground != null && mBackground.isStateful()) || super.isStateful();
-    }
-
-    /**
-     * Ensures the tint filter is consistent with the current tint color and
-     * mode.
-     */
-    private PorterDuffColorFilter createTintFilter(ColorStateList tint, PorterDuff.Mode tintMode) {
-        if (tint == null || tintMode == null) {
-            return null;
-        }
-        final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
-        return new PorterDuffColorFilter(color, tintMode);
-    }
-}
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
deleted file mode 100644
index 2573631..0000000
--- a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-/**
- * Interface provided by CardView to implementations.
- * <p>
- * Necessary to resolve circular dependency between base CardView and platform implementations.
- */
-@RequiresApi(9)
-@TargetApi(9)
-interface CardViewDelegate {
-    void setCardBackground(Drawable drawable);
-    Drawable getCardBackground();
-    boolean getUseCompatPadding();
-    boolean getPreventCornerOverlap();
-    void setShadowPadding(int left, int top, int right, int bottom);
-    void setMinWidthHeightInternal(int width, int height);
-    View getCardView();
-}
\ No newline at end of file
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
deleted file mode 100644
index f36bd2b..0000000
--- a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-
-/**
- * Interface for platform specific CardView implementations.
- */
-@RequiresApi(9)
-@TargetApi(9)
-interface CardViewImpl {
-    void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor,
-            float radius, float elevation, float maxElevation);
-
-    void setRadius(CardViewDelegate cardView, float radius);
-
-    float getRadius(CardViewDelegate cardView);
-
-    void setElevation(CardViewDelegate cardView, float elevation);
-
-    float getElevation(CardViewDelegate cardView);
-
-    void initStatic();
-
-    void setMaxElevation(CardViewDelegate cardView, float maxElevation);
-
-    float getMaxElevation(CardViewDelegate cardView);
-
-    float getMinWidth(CardViewDelegate cardView);
-
-    float getMinHeight(CardViewDelegate cardView);
-
-    void updatePadding(CardViewDelegate cardView);
-
-    void onCompatPaddingChanged(CardViewDelegate cardView);
-
-    void onPreventCornerOverlapChanged(CardViewDelegate cardView);
-
-    void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color);
-
-    ColorStateList getBackgroundColor(CardViewDelegate cardView);
-}
diff --git a/v7/cardview/build.gradle b/v7/cardview/build.gradle
index ce3f28d..e839aee 100644
--- a/v7/cardview/build.gradle
+++ b/v7/cardview/build.gradle
@@ -1,81 +1,25 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'cardview-v7'
 
 dependencies {
-    compile project(':support-annotations')
+    api project(':support-annotations')
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
-                'base',
-                'gingerbread',
-                'jellybean-mr1',
-                'api21',
                 'src'
         ]
         main.res.srcDir 'res'
     }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support CardView v7'
-                description "Android Support CardView v7"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support CardView v7'
+    inceptionYear '2011'
+    description 'Android Support CardView v7'
 }
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
deleted file mode 100644
index f430213..0000000
--- a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.support.annotation.ColorInt;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(9)
-@TargetApi(9)
-class CardViewGingerbread implements CardViewImpl {
-
-    final RectF sCornerRect = new RectF();
-
-    @Override
-    public void initStatic() {
-        // Draws a round rect using 7 draw operations. This is faster than using
-        // canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw
-        // shapes.
-        RoundRectDrawableWithShadow.sRoundRectHelper =
-                new RoundRectDrawableWithShadow.RoundRectHelper() {
-            @Override
-            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
-                    Paint paint) {
-                final float twoRadius = cornerRadius * 2;
-                final float innerWidth = bounds.width() - twoRadius - 1;
-                final float innerHeight = bounds.height() - twoRadius - 1;
-                if (cornerRadius >= 1f) {
-                    // increment corner radius to account for half pixels.
-                    float roundedCornerRadius = cornerRadius + .5f;
-                    sCornerRect.set(-roundedCornerRadius, -roundedCornerRadius, roundedCornerRadius,
-                            roundedCornerRadius);
-                    int saved = canvas.save();
-                    canvas.translate(bounds.left + roundedCornerRadius,
-                            bounds.top + roundedCornerRadius);
-                    canvas.drawArc(sCornerRect, 180, 90, true, paint);
-                    canvas.translate(innerWidth, 0);
-                    canvas.rotate(90);
-                    canvas.drawArc(sCornerRect, 180, 90, true, paint);
-                    canvas.translate(innerHeight, 0);
-                    canvas.rotate(90);
-                    canvas.drawArc(sCornerRect, 180, 90, true, paint);
-                    canvas.translate(innerWidth, 0);
-                    canvas.rotate(90);
-                    canvas.drawArc(sCornerRect, 180, 90, true, paint);
-                    canvas.restoreToCount(saved);
-                    //draw top and bottom pieces
-                    canvas.drawRect(bounds.left + roundedCornerRadius - 1f, bounds.top,
-                            bounds.right - roundedCornerRadius + 1f,
-                            bounds.top + roundedCornerRadius, paint);
-
-                    canvas.drawRect(bounds.left + roundedCornerRadius - 1f,
-                            bounds.bottom - roundedCornerRadius,
-                            bounds.right - roundedCornerRadius + 1f, bounds.bottom, paint);
-                }
-                // center
-                canvas.drawRect(bounds.left, bounds.top + cornerRadius,
-                        bounds.right, bounds.bottom - cornerRadius , paint);
-            }
-        };
-    }
-
-    @Override
-    public void initialize(CardViewDelegate cardView, Context context,
-            ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
-        RoundRectDrawableWithShadow background = createBackground(context, backgroundColor, radius,
-                elevation, maxElevation);
-        background.setAddPaddingForCorners(cardView.getPreventCornerOverlap());
-        cardView.setCardBackground(background);
-        updatePadding(cardView);
-    }
-
-    private RoundRectDrawableWithShadow createBackground(Context context,
-                    ColorStateList backgroundColor, float radius, float elevation,
-                    float maxElevation) {
-        return new RoundRectDrawableWithShadow(context.getResources(), backgroundColor, radius,
-                elevation, maxElevation);
-    }
-
-    @Override
-    public void updatePadding(CardViewDelegate cardView) {
-        Rect shadowPadding = new Rect();
-        getShadowBackground(cardView).getMaxShadowAndCornerPadding(shadowPadding);
-        cardView.setMinWidthHeightInternal((int) Math.ceil(getMinWidth(cardView)),
-                (int) Math.ceil(getMinHeight(cardView)));
-        cardView.setShadowPadding(shadowPadding.left, shadowPadding.top,
-                shadowPadding.right, shadowPadding.bottom);
-    }
-
-    @Override
-    public void onCompatPaddingChanged(CardViewDelegate cardView) {
-        // NO OP
-    }
-
-    @Override
-    public void onPreventCornerOverlapChanged(CardViewDelegate cardView) {
-        getShadowBackground(cardView).setAddPaddingForCorners(cardView.getPreventCornerOverlap());
-        updatePadding(cardView);
-    }
-
-    @Override
-    public void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color) {
-        getShadowBackground(cardView).setColor(color);
-    }
-
-    @Override
-    public ColorStateList getBackgroundColor(CardViewDelegate cardView) {
-        return getShadowBackground(cardView).getColor();
-    }
-
-    @Override
-    public void setRadius(CardViewDelegate cardView, float radius) {
-        getShadowBackground(cardView).setCornerRadius(radius);
-        updatePadding(cardView);
-    }
-
-    @Override
-    public float getRadius(CardViewDelegate cardView) {
-        return getShadowBackground(cardView).getCornerRadius();
-    }
-
-    @Override
-    public void setElevation(CardViewDelegate cardView, float elevation) {
-        getShadowBackground(cardView).setShadowSize(elevation);
-    }
-
-    @Override
-    public float getElevation(CardViewDelegate cardView) {
-        return getShadowBackground(cardView).getShadowSize();
-    }
-
-    @Override
-    public void setMaxElevation(CardViewDelegate cardView, float maxElevation) {
-        getShadowBackground(cardView).setMaxShadowSize(maxElevation);
-        updatePadding(cardView);
-    }
-
-    @Override
-    public float getMaxElevation(CardViewDelegate cardView) {
-        return getShadowBackground(cardView).getMaxShadowSize();
-    }
-
-    @Override
-    public float getMinWidth(CardViewDelegate cardView) {
-        return getShadowBackground(cardView).getMinWidth();
-    }
-
-    @Override
-    public float getMinHeight(CardViewDelegate cardView) {
-        return getShadowBackground(cardView).getMinHeight();
-    }
-
-    private RoundRectDrawableWithShadow getShadowBackground(CardViewDelegate cardView) {
-        return ((RoundRectDrawableWithShadow) cardView.getCardBackground());
-    }
-}
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java b/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
deleted file mode 100644
index 32bf877..0000000
--- a/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v7.cardview.R;
-
-/**
- * A rounded rectangle drawable which also includes a shadow around.
- */
-@RequiresApi(9)
-@TargetApi(9)
-class RoundRectDrawableWithShadow extends Drawable {
-    // used to calculate content padding
-    final static double COS_45 = Math.cos(Math.toRadians(45));
-
-    final static float SHADOW_MULTIPLIER = 1.5f;
-
-    final int mInsetShadow; // extra shadow to avoid gaps between card and shadow
-
-    /*
-    * This helper is set by CardView implementations.
-    * <p>
-    * Prior to API 17, canvas.drawRoundRect is expensive; which is why we need this interface
-    * to draw efficient rounded rectangles before 17.
-    * */
-    static RoundRectHelper sRoundRectHelper;
-
-    Paint mPaint;
-
-    Paint mCornerShadowPaint;
-
-    Paint mEdgeShadowPaint;
-
-    final RectF mCardBounds;
-
-    float mCornerRadius;
-
-    Path mCornerShadowPath;
-
-    // updated value with inset
-    float mMaxShadowSize;
-
-    // actual value set by developer
-    float mRawMaxShadowSize;
-
-    // multiplied value to account for shadow offset
-    float mShadowSize;
-
-    // actual value set by developer
-    float mRawShadowSize;
-
-    private ColorStateList mBackground;
-
-    private boolean mDirty = true;
-
-    private final int mShadowStartColor;
-
-    private final int mShadowEndColor;
-
-    private boolean mAddPaddingForCorners = true;
-
-    /**
-     * If shadow size is set to a value above max shadow, we print a warning
-     */
-    private boolean mPrintedShadowClipWarning = false;
-
-    RoundRectDrawableWithShadow(Resources resources, ColorStateList backgroundColor, float radius,
-            float shadowSize, float maxShadowSize) {
-        mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color);
-        mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color);
-        mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow);
-        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
-        setBackground(backgroundColor);
-        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
-        mCornerShadowPaint.setStyle(Paint.Style.FILL);
-        mCornerRadius = (int) (radius + .5f);
-        mCardBounds = new RectF();
-        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
-        mEdgeShadowPaint.setAntiAlias(false);
-        setShadowSize(shadowSize, maxShadowSize);
-    }
-
-    private void setBackground(ColorStateList color) {
-        mBackground = (color == null) ?  ColorStateList.valueOf(Color.TRANSPARENT) : color;
-        mPaint.setColor(mBackground.getColorForState(getState(), mBackground.getDefaultColor()));
-    }
-
-    /**
-     * Casts the value to an even integer.
-     */
-    private int toEven(float value) {
-        int i = (int) (value + .5f);
-        if (i % 2 == 1) {
-            return i - 1;
-        }
-        return i;
-    }
-
-    public void setAddPaddingForCorners(boolean addPaddingForCorners) {
-        mAddPaddingForCorners = addPaddingForCorners;
-        invalidateSelf();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mPaint.setAlpha(alpha);
-        mCornerShadowPaint.setAlpha(alpha);
-        mEdgeShadowPaint.setAlpha(alpha);
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
-        mDirty = true;
-    }
-
-    void setShadowSize(float shadowSize, float maxShadowSize) {
-        if (shadowSize < 0f) {
-            throw new IllegalArgumentException("Invalid shadow size " + shadowSize +
-                    ". Must be >= 0");
-        }
-        if (maxShadowSize < 0f) {
-            throw new IllegalArgumentException("Invalid max shadow size " + maxShadowSize +
-                    ". Must be >= 0");
-        }
-        shadowSize = toEven(shadowSize);
-        maxShadowSize = toEven(maxShadowSize);
-        if (shadowSize > maxShadowSize) {
-            shadowSize = maxShadowSize;
-            if (!mPrintedShadowClipWarning) {
-                mPrintedShadowClipWarning = true;
-            }
-        }
-        if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
-            return;
-        }
-        mRawShadowSize = shadowSize;
-        mRawMaxShadowSize = maxShadowSize;
-        mShadowSize = (int)(shadowSize * SHADOW_MULTIPLIER + mInsetShadow + .5f);
-        mMaxShadowSize = maxShadowSize + mInsetShadow;
-        mDirty = true;
-        invalidateSelf();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
-                mAddPaddingForCorners));
-        int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
-                mAddPaddingForCorners));
-        padding.set(hOffset, vOffset, hOffset, vOffset);
-        return true;
-    }
-
-    static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
-            boolean addPaddingForCorners) {
-        if (addPaddingForCorners) {
-            return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
-        } else {
-            return maxShadowSize * SHADOW_MULTIPLIER;
-        }
-    }
-
-    static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
-            boolean addPaddingForCorners) {
-        if (addPaddingForCorners) {
-            return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
-        } else {
-            return maxShadowSize;
-        }
-    }
-
-    @Override
-    protected boolean onStateChange(int[] stateSet) {
-        final int newColor = mBackground.getColorForState(stateSet, mBackground.getDefaultColor());
-        if (mPaint.getColor() == newColor) {
-            return false;
-        }
-        mPaint.setColor(newColor);
-        mDirty = true;
-        invalidateSelf();
-        return true;
-    }
-
-    @Override
-    public boolean isStateful() {
-        return (mBackground != null && mBackground.isStateful()) || super.isStateful();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        mPaint.setColorFilter(cf);
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    void setCornerRadius(float radius) {
-        if (radius < 0f) {
-            throw new IllegalArgumentException("Invalid radius " + radius +
-                ". Must be >= 0");
-        }
-        radius = (int) (radius + .5f);
-        if (mCornerRadius == radius) {
-            return;
-        }
-        mCornerRadius = radius;
-        mDirty = true;
-        invalidateSelf();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mDirty) {
-            buildComponents(getBounds());
-            mDirty = false;
-        }
-        canvas.translate(0, mRawShadowSize / 2);
-        drawShadow(canvas);
-        canvas.translate(0, -mRawShadowSize / 2);
-        sRoundRectHelper.drawRoundRect(canvas, mCardBounds, mCornerRadius, mPaint);
-    }
-
-    private void drawShadow(Canvas canvas) {
-        final float edgeShadowTop = -mCornerRadius - mShadowSize;
-        final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2;
-        final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
-        final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
-        // LT
-        int saved = canvas.save();
-        canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawHorizontalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.width() - 2 * inset, -mCornerRadius,
-                    mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-        // RB
-        saved = canvas.save();
-        canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
-        canvas.rotate(180f);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawHorizontalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize,
-                    mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-        // LB
-        saved = canvas.save();
-        canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
-        canvas.rotate(270f);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawVerticalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-        // RT
-        saved = canvas.save();
-        canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
-        canvas.rotate(90f);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawVerticalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-    }
-
-    private void buildShadowCorners() {
-        RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
-        RectF outerBounds = new RectF(innerBounds);
-        outerBounds.inset(-mShadowSize, -mShadowSize);
-
-        if (mCornerShadowPath == null) {
-            mCornerShadowPath = new Path();
-        } else {
-            mCornerShadowPath.reset();
-        }
-        mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
-        mCornerShadowPath.moveTo(-mCornerRadius, 0);
-        mCornerShadowPath.rLineTo(-mShadowSize, 0);
-        // outer arc
-        mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
-        // inner arc
-        mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
-        mCornerShadowPath.close();
-        float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
-        mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
-                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
-                new float[]{0f, startRatio, 1f}
-                , Shader.TileMode.CLAMP));
-
-        // we offset the content shadowSize/2 pixels up to make it more realistic.
-        // this is why edge shadow shader has some extra space
-        // When drawing bottom edge shadow, we use that extra space.
-        mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
-                -mCornerRadius - mShadowSize,
-                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
-                new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
-        mEdgeShadowPaint.setAntiAlias(false);
-    }
-
-    private void buildComponents(Rect bounds) {
-        // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
-        // We could have different top-bottom offsets to avoid extra gap above but in that case
-        // center aligning Views inside the CardView would be problematic.
-        final float verticalOffset = mRawMaxShadowSize * SHADOW_MULTIPLIER;
-        mCardBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset,
-                bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset);
-        buildShadowCorners();
-    }
-
-    float getCornerRadius() {
-        return mCornerRadius;
-    }
-
-    void getMaxShadowAndCornerPadding(Rect into) {
-        getPadding(into);
-    }
-
-    void setShadowSize(float size) {
-        setShadowSize(size, mRawMaxShadowSize);
-    }
-
-    void setMaxShadowSize(float size) {
-        setShadowSize(mRawShadowSize, size);
-    }
-
-    float getShadowSize() {
-        return mRawShadowSize;
-    }
-
-    float getMaxShadowSize() {
-        return mRawMaxShadowSize;
-    }
-
-    float getMinWidth() {
-        final float content = 2 *
-                Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
-        return content + (mRawMaxShadowSize + mInsetShadow) * 2;
-    }
-
-    float getMinHeight() {
-        final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow
-                        + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
-        return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2;
-    }
-
-    void setColor(@Nullable ColorStateList color) {
-        setBackground(color);
-        invalidateSelf();
-    }
-
-    ColorStateList getColor() {
-        return mBackground;
-    }
-
-    static interface RoundRectHelper {
-        void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint);
-    }
-}
diff --git a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
deleted file mode 100644
index a9c0e0a..0000000
--- a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-package android.support.v7.widget;
-
-import android.annotation.TargetApi;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(17)
-@TargetApi(17)
-class CardViewJellybeanMr1 extends CardViewGingerbread {
-
-    @Override
-    public void initStatic() {
-        RoundRectDrawableWithShadow.sRoundRectHelper
-                = new RoundRectDrawableWithShadow.RoundRectHelper() {
-            @Override
-            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
-                    Paint paint) {
-                canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
-            }
-        };
-    }
-}
\ No newline at end of file
diff --git a/v7/cardview/lint-baseline.xml b/v7/cardview/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v7/cardview/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v7/cardview/src/.readme b/v7/cardview/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/cardview/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/cardview/src/android/support/v7/widget/CardView.java b/v7/cardview/src/android/support/v7/widget/CardView.java
index 3b47a54..3df45d9 100644
--- a/v7/cardview/src/android/support/v7/widget/CardView.java
+++ b/v7/cardview/src/android/support/v7/widget/CardView.java
@@ -80,11 +80,11 @@
 
     static {
         if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new CardViewApi21();
+            IMPL = new CardViewApi21Impl();
         } else if (Build.VERSION.SDK_INT >= 17) {
-            IMPL = new CardViewJellybeanMr1();
+            IMPL = new CardViewApi17Impl();
         } else {
-            IMPL = new CardViewGingerbread();
+            IMPL = new CardViewBaseImpl();
         }
         IMPL.initStatic();
     }
@@ -126,6 +126,7 @@
         // NO OP
     }
 
+    @Override
     public void setPaddingRelative(int start, int top, int end, int bottom) {
         // NO OP
     }
@@ -186,7 +187,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (!(IMPL instanceof CardViewApi21)) {
+        if (!(IMPL instanceof CardViewApi21Impl)) {
             final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
             switch (widthMode) {
                 case MeasureSpec.EXACTLY:
@@ -195,6 +196,9 @@
                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minWidth,
                             MeasureSpec.getSize(widthMeasureSpec)), widthMode);
                     break;
+                case MeasureSpec.UNSPECIFIED:
+                    // Do nothing
+                    break;
             }
 
             final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -205,6 +209,9 @@
                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minHeight,
                             MeasureSpec.getSize(heightMeasureSpec)), heightMode);
                     break;
+                case MeasureSpec.UNSPECIFIED:
+                    // Do nothing
+                    break;
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         } else {
diff --git a/v7/cardview/src/android/support/v7/widget/CardViewApi17Impl.java b/v7/cardview/src/android/support/v7/widget/CardViewApi17Impl.java
new file mode 100644
index 0000000..3a1a4e1
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/CardViewApi17Impl.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(17)
+class CardViewApi17Impl extends CardViewBaseImpl {
+
+    @Override
+    public void initStatic() {
+        RoundRectDrawableWithShadow.sRoundRectHelper =
+                new RoundRectDrawableWithShadow.RoundRectHelper() {
+                    @Override
+                    public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
+                            Paint paint) {
+                        canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
+                    }
+                };
+    }
+}
diff --git a/v7/cardview/src/android/support/v7/widget/CardViewApi21Impl.java b/v7/cardview/src/android/support/v7/widget/CardViewApi21Impl.java
new file mode 100644
index 0000000..7af50e5
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/CardViewApi21Impl.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.view.View;
+
+@RequiresApi(21)
+class CardViewApi21Impl implements CardViewImpl {
+
+    @Override
+    public void initialize(CardViewDelegate cardView, Context context,
+                ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
+        final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
+        cardView.setCardBackground(background);
+
+        View view = cardView.getCardView();
+        view.setClipToOutline(true);
+        view.setElevation(elevation);
+        setMaxElevation(cardView, maxElevation);
+    }
+
+    @Override
+    public void setRadius(CardViewDelegate cardView, float radius) {
+        getCardBackground(cardView).setRadius(radius);
+    }
+
+    @Override
+    public void initStatic() {
+    }
+
+    @Override
+    public void setMaxElevation(CardViewDelegate cardView, float maxElevation) {
+        getCardBackground(cardView).setPadding(maxElevation,
+                cardView.getUseCompatPadding(), cardView.getPreventCornerOverlap());
+        updatePadding(cardView);
+    }
+
+    @Override
+    public float getMaxElevation(CardViewDelegate cardView) {
+        return getCardBackground(cardView).getPadding();
+    }
+
+    @Override
+    public float getMinWidth(CardViewDelegate cardView) {
+        return getRadius(cardView) * 2;
+    }
+
+    @Override
+    public float getMinHeight(CardViewDelegate cardView) {
+        return getRadius(cardView) * 2;
+    }
+
+    @Override
+    public float getRadius(CardViewDelegate cardView) {
+        return getCardBackground(cardView).getRadius();
+    }
+
+    @Override
+    public void setElevation(CardViewDelegate cardView, float elevation) {
+        cardView.getCardView().setElevation(elevation);
+    }
+
+    @Override
+    public float getElevation(CardViewDelegate cardView) {
+        return cardView.getCardView().getElevation();
+    }
+
+    @Override
+    public void updatePadding(CardViewDelegate cardView) {
+        if (!cardView.getUseCompatPadding()) {
+            cardView.setShadowPadding(0, 0, 0, 0);
+            return;
+        }
+        float elevation = getMaxElevation(cardView);
+        final float radius = getRadius(cardView);
+        int hPadding = (int) Math.ceil(RoundRectDrawableWithShadow
+                .calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
+        int vPadding = (int) Math.ceil(RoundRectDrawableWithShadow
+                .calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
+        cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding);
+    }
+
+    @Override
+    public void onCompatPaddingChanged(CardViewDelegate cardView) {
+        setMaxElevation(cardView, getMaxElevation(cardView));
+    }
+
+    @Override
+    public void onPreventCornerOverlapChanged(CardViewDelegate cardView) {
+        setMaxElevation(cardView, getMaxElevation(cardView));
+    }
+
+    @Override
+    public void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color) {
+        getCardBackground(cardView).setColor(color);
+    }
+
+    @Override
+    public ColorStateList getBackgroundColor(CardViewDelegate cardView) {
+        return getCardBackground(cardView).getColor();
+    }
+
+    private RoundRectDrawable getCardBackground(CardViewDelegate cardView) {
+        return ((RoundRectDrawable) cardView.getCardBackground());
+    }
+}
diff --git a/v7/cardview/src/android/support/v7/widget/CardViewBaseImpl.java b/v7/cardview/src/android/support/v7/widget/CardViewBaseImpl.java
new file mode 100644
index 0000000..8cb38be
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/CardViewBaseImpl.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.support.annotation.Nullable;
+
+class CardViewBaseImpl implements CardViewImpl {
+
+    private final RectF mCornerRect = new RectF();
+
+    @Override
+    public void initStatic() {
+        // Draws a round rect using 7 draw operations. This is faster than using
+        // canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw
+        // shapes.
+        RoundRectDrawableWithShadow.sRoundRectHelper =
+                new RoundRectDrawableWithShadow.RoundRectHelper() {
+            @Override
+            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
+                    Paint paint) {
+                final float twoRadius = cornerRadius * 2;
+                final float innerWidth = bounds.width() - twoRadius - 1;
+                final float innerHeight = bounds.height() - twoRadius - 1;
+                if (cornerRadius >= 1f) {
+                    // increment corner radius to account for half pixels.
+                    float roundedCornerRadius = cornerRadius + .5f;
+                    mCornerRect.set(-roundedCornerRadius, -roundedCornerRadius, roundedCornerRadius,
+                            roundedCornerRadius);
+                    int saved = canvas.save();
+                    canvas.translate(bounds.left + roundedCornerRadius,
+                            bounds.top + roundedCornerRadius);
+                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
+                    canvas.translate(innerWidth, 0);
+                    canvas.rotate(90);
+                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
+                    canvas.translate(innerHeight, 0);
+                    canvas.rotate(90);
+                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
+                    canvas.translate(innerWidth, 0);
+                    canvas.rotate(90);
+                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
+                    canvas.restoreToCount(saved);
+                    //draw top and bottom pieces
+                    canvas.drawRect(bounds.left + roundedCornerRadius - 1f, bounds.top,
+                            bounds.right - roundedCornerRadius + 1f,
+                            bounds.top + roundedCornerRadius, paint);
+
+                    canvas.drawRect(bounds.left + roundedCornerRadius - 1f,
+                            bounds.bottom - roundedCornerRadius,
+                            bounds.right - roundedCornerRadius + 1f, bounds.bottom, paint);
+                }
+                // center
+                canvas.drawRect(bounds.left, bounds.top + cornerRadius,
+                        bounds.right, bounds.bottom - cornerRadius , paint);
+            }
+        };
+    }
+
+    @Override
+    public void initialize(CardViewDelegate cardView, Context context,
+            ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
+        RoundRectDrawableWithShadow background = createBackground(context, backgroundColor, radius,
+                elevation, maxElevation);
+        background.setAddPaddingForCorners(cardView.getPreventCornerOverlap());
+        cardView.setCardBackground(background);
+        updatePadding(cardView);
+    }
+
+    private RoundRectDrawableWithShadow createBackground(Context context,
+                    ColorStateList backgroundColor, float radius, float elevation,
+                    float maxElevation) {
+        return new RoundRectDrawableWithShadow(context.getResources(), backgroundColor, radius,
+                elevation, maxElevation);
+    }
+
+    @Override
+    public void updatePadding(CardViewDelegate cardView) {
+        Rect shadowPadding = new Rect();
+        getShadowBackground(cardView).getMaxShadowAndCornerPadding(shadowPadding);
+        cardView.setMinWidthHeightInternal((int) Math.ceil(getMinWidth(cardView)),
+                (int) Math.ceil(getMinHeight(cardView)));
+        cardView.setShadowPadding(shadowPadding.left, shadowPadding.top,
+                shadowPadding.right, shadowPadding.bottom);
+    }
+
+    @Override
+    public void onCompatPaddingChanged(CardViewDelegate cardView) {
+        // NO OP
+    }
+
+    @Override
+    public void onPreventCornerOverlapChanged(CardViewDelegate cardView) {
+        getShadowBackground(cardView).setAddPaddingForCorners(cardView.getPreventCornerOverlap());
+        updatePadding(cardView);
+    }
+
+    @Override
+    public void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color) {
+        getShadowBackground(cardView).setColor(color);
+    }
+
+    @Override
+    public ColorStateList getBackgroundColor(CardViewDelegate cardView) {
+        return getShadowBackground(cardView).getColor();
+    }
+
+    @Override
+    public void setRadius(CardViewDelegate cardView, float radius) {
+        getShadowBackground(cardView).setCornerRadius(radius);
+        updatePadding(cardView);
+    }
+
+    @Override
+    public float getRadius(CardViewDelegate cardView) {
+        return getShadowBackground(cardView).getCornerRadius();
+    }
+
+    @Override
+    public void setElevation(CardViewDelegate cardView, float elevation) {
+        getShadowBackground(cardView).setShadowSize(elevation);
+    }
+
+    @Override
+    public float getElevation(CardViewDelegate cardView) {
+        return getShadowBackground(cardView).getShadowSize();
+    }
+
+    @Override
+    public void setMaxElevation(CardViewDelegate cardView, float maxElevation) {
+        getShadowBackground(cardView).setMaxShadowSize(maxElevation);
+        updatePadding(cardView);
+    }
+
+    @Override
+    public float getMaxElevation(CardViewDelegate cardView) {
+        return getShadowBackground(cardView).getMaxShadowSize();
+    }
+
+    @Override
+    public float getMinWidth(CardViewDelegate cardView) {
+        return getShadowBackground(cardView).getMinWidth();
+    }
+
+    @Override
+    public float getMinHeight(CardViewDelegate cardView) {
+        return getShadowBackground(cardView).getMinHeight();
+    }
+
+    private RoundRectDrawableWithShadow getShadowBackground(CardViewDelegate cardView) {
+        return ((RoundRectDrawableWithShadow) cardView.getCardBackground());
+    }
+}
diff --git a/v7/cardview/src/android/support/v7/widget/CardViewDelegate.java b/v7/cardview/src/android/support/v7/widget/CardViewDelegate.java
new file mode 100644
index 0000000..7b1b83a
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/CardViewDelegate.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+/**
+ * Interface provided by CardView to implementations.
+ * <p>
+ * Necessary to resolve circular dependency between base CardView and platform implementations.
+ */
+interface CardViewDelegate {
+    void setCardBackground(Drawable drawable);
+    Drawable getCardBackground();
+    boolean getUseCompatPadding();
+    boolean getPreventCornerOverlap();
+    void setShadowPadding(int left, int top, int right, int bottom);
+    void setMinWidthHeightInternal(int width, int height);
+    View getCardView();
+}
diff --git a/v7/cardview/src/android/support/v7/widget/CardViewImpl.java b/v7/cardview/src/android/support/v7/widget/CardViewImpl.java
new file mode 100644
index 0000000..26799da
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/CardViewImpl.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.support.annotation.Nullable;
+
+/**
+ * Interface for platform specific CardView implementations.
+ */
+interface CardViewImpl {
+    void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor,
+            float radius, float elevation, float maxElevation);
+
+    void setRadius(CardViewDelegate cardView, float radius);
+
+    float getRadius(CardViewDelegate cardView);
+
+    void setElevation(CardViewDelegate cardView, float elevation);
+
+    float getElevation(CardViewDelegate cardView);
+
+    void initStatic();
+
+    void setMaxElevation(CardViewDelegate cardView, float maxElevation);
+
+    float getMaxElevation(CardViewDelegate cardView);
+
+    float getMinWidth(CardViewDelegate cardView);
+
+    float getMinHeight(CardViewDelegate cardView);
+
+    void updatePadding(CardViewDelegate cardView);
+
+    void onCompatPaddingChanged(CardViewDelegate cardView);
+
+    void onPreventCornerOverlapChanged(CardViewDelegate cardView);
+
+    void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color);
+
+    ColorStateList getBackgroundColor(CardViewDelegate cardView);
+}
diff --git a/v7/cardview/src/android/support/v7/widget/RoundRectDrawable.java b/v7/cardview/src/android/support/v7/widget/RoundRectDrawable.java
new file mode 100644
index 0000000..b8afdf7
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/RoundRectDrawable.java
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateHorizontalPadding;
+import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateVerticalPadding;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+
+/**
+ * Very simple drawable that draws a rounded rectangle background with arbitrary corners and also
+ * reports proper outline for Lollipop.
+ * <p>
+ * Simpler and uses less resources compared to GradientDrawable or ShapeDrawable.
+ */
+@RequiresApi(21)
+class RoundRectDrawable extends Drawable {
+    private float mRadius;
+    private final Paint mPaint;
+    private final RectF mBoundsF;
+    private final Rect mBoundsI;
+    private float mPadding;
+    private boolean mInsetForPadding = false;
+    private boolean mInsetForRadius = true;
+
+    private ColorStateList mBackground;
+    private PorterDuffColorFilter mTintFilter;
+    private ColorStateList mTint;
+    private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_IN;
+
+    RoundRectDrawable(ColorStateList backgroundColor, float radius) {
+        mRadius = radius;
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        setBackground(backgroundColor);
+
+        mBoundsF = new RectF();
+        mBoundsI = new Rect();
+    }
+
+    private void setBackground(ColorStateList color) {
+        mBackground = (color == null) ?  ColorStateList.valueOf(Color.TRANSPARENT) : color;
+        mPaint.setColor(mBackground.getColorForState(getState(), mBackground.getDefaultColor()));
+    }
+
+    void setPadding(float padding, boolean insetForPadding, boolean insetForRadius) {
+        if (padding == mPadding && mInsetForPadding == insetForPadding
+                && mInsetForRadius == insetForRadius) {
+            return;
+        }
+        mPadding = padding;
+        mInsetForPadding = insetForPadding;
+        mInsetForRadius = insetForRadius;
+        updateBounds(null);
+        invalidateSelf();
+    }
+
+    float getPadding() {
+        return mPadding;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Paint paint = mPaint;
+
+        final boolean clearColorFilter;
+        if (mTintFilter != null && paint.getColorFilter() == null) {
+            paint.setColorFilter(mTintFilter);
+            clearColorFilter = true;
+        } else {
+            clearColorFilter = false;
+        }
+
+        canvas.drawRoundRect(mBoundsF, mRadius, mRadius, paint);
+
+        if (clearColorFilter) {
+            paint.setColorFilter(null);
+        }
+    }
+
+    private void updateBounds(Rect bounds) {
+        if (bounds == null) {
+            bounds = getBounds();
+        }
+        mBoundsF.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        mBoundsI.set(bounds);
+        if (mInsetForPadding) {
+            float vInset = calculateVerticalPadding(mPadding, mRadius, mInsetForRadius);
+            float hInset = calculateHorizontalPadding(mPadding, mRadius, mInsetForRadius);
+            mBoundsI.inset((int) Math.ceil(hInset), (int) Math.ceil(vInset));
+            // to make sure they have same bounds.
+            mBoundsF.set(mBoundsI);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        updateBounds(bounds);
+    }
+
+    @Override
+    public void getOutline(Outline outline) {
+        outline.setRoundRect(mBoundsI, mRadius);
+    }
+
+    void setRadius(float radius) {
+        if (radius == mRadius) {
+            return;
+        }
+        mRadius = radius;
+        updateBounds(null);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mPaint.setColorFilter(cf);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    public float getRadius() {
+        return mRadius;
+    }
+
+    public void setColor(@Nullable ColorStateList color) {
+        setBackground(color);
+        invalidateSelf();
+    }
+
+    public ColorStateList getColor() {
+        return mBackground;
+    }
+
+    @Override
+    public void setTintList(ColorStateList tint) {
+        mTint = tint;
+        mTintFilter = createTintFilter(mTint, mTintMode);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setTintMode(PorterDuff.Mode tintMode) {
+        mTintMode = tintMode;
+        mTintFilter = createTintFilter(mTint, mTintMode);
+        invalidateSelf();
+    }
+
+    @Override
+    protected boolean onStateChange(int[] stateSet) {
+        final int newColor = mBackground.getColorForState(stateSet, mBackground.getDefaultColor());
+        final boolean colorChanged = newColor != mPaint.getColor();
+        if (colorChanged) {
+            mPaint.setColor(newColor);
+        }
+        if (mTint != null && mTintMode != null) {
+            mTintFilter = createTintFilter(mTint, mTintMode);
+            return true;
+        }
+        return colorChanged;
+    }
+
+    @Override
+    public boolean isStateful() {
+        return (mTint != null && mTint.isStateful())
+                || (mBackground != null && mBackground.isStateful()) || super.isStateful();
+    }
+
+    /**
+     * Ensures the tint filter is consistent with the current tint color and
+     * mode.
+     */
+    private PorterDuffColorFilter createTintFilter(ColorStateList tint, PorterDuff.Mode tintMode) {
+        if (tint == null || tintMode == null) {
+            return null;
+        }
+        final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
+        return new PorterDuffColorFilter(color, tintMode);
+    }
+}
diff --git a/v7/cardview/src/android/support/v7/widget/RoundRectDrawableWithShadow.java b/v7/cardview/src/android/support/v7/widget/RoundRectDrawableWithShadow.java
new file mode 100644
index 0000000..3250f8d
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/RoundRectDrawableWithShadow.java
@@ -0,0 +1,387 @@
+/*
+ * 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.
+ */
+package android.support.v7.widget;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.v7.cardview.R;
+
+/**
+ * A rounded rectangle drawable which also includes a shadow around.
+ */
+class RoundRectDrawableWithShadow extends Drawable {
+    // used to calculate content padding
+    private static final double COS_45 = Math.cos(Math.toRadians(45));
+
+    private static final float SHADOW_MULTIPLIER = 1.5f;
+
+    private final int mInsetShadow; // extra shadow to avoid gaps between card and shadow
+
+    /*
+    * This helper is set by CardView implementations.
+    * <p>
+    * Prior to API 17, canvas.drawRoundRect is expensive; which is why we need this interface
+    * to draw efficient rounded rectangles before 17.
+    * */
+    static RoundRectHelper sRoundRectHelper;
+
+    private Paint mPaint;
+
+    private Paint mCornerShadowPaint;
+
+    private Paint mEdgeShadowPaint;
+
+    private final RectF mCardBounds;
+
+    private float mCornerRadius;
+
+    private Path mCornerShadowPath;
+
+    // actual value set by developer
+    private float mRawMaxShadowSize;
+
+    // multiplied value to account for shadow offset
+    private float mShadowSize;
+
+    // actual value set by developer
+    private float mRawShadowSize;
+
+    private ColorStateList mBackground;
+
+    private boolean mDirty = true;
+
+    private final int mShadowStartColor;
+
+    private final int mShadowEndColor;
+
+    private boolean mAddPaddingForCorners = true;
+
+    /**
+     * If shadow size is set to a value above max shadow, we print a warning
+     */
+    private boolean mPrintedShadowClipWarning = false;
+
+    RoundRectDrawableWithShadow(Resources resources, ColorStateList backgroundColor, float radius,
+            float shadowSize, float maxShadowSize) {
+        mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color);
+        mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color);
+        mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow);
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        setBackground(backgroundColor);
+        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        mCornerShadowPaint.setStyle(Paint.Style.FILL);
+        mCornerRadius = (int) (radius + .5f);
+        mCardBounds = new RectF();
+        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
+        mEdgeShadowPaint.setAntiAlias(false);
+        setShadowSize(shadowSize, maxShadowSize);
+    }
+
+    private void setBackground(ColorStateList color) {
+        mBackground = (color == null) ?  ColorStateList.valueOf(Color.TRANSPARENT) : color;
+        mPaint.setColor(mBackground.getColorForState(getState(), mBackground.getDefaultColor()));
+    }
+
+    /**
+     * Casts the value to an even integer.
+     */
+    private int toEven(float value) {
+        int i = (int) (value + .5f);
+        if (i % 2 == 1) {
+            return i - 1;
+        }
+        return i;
+    }
+
+    void setAddPaddingForCorners(boolean addPaddingForCorners) {
+        mAddPaddingForCorners = addPaddingForCorners;
+        invalidateSelf();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        mCornerShadowPaint.setAlpha(alpha);
+        mEdgeShadowPaint.setAlpha(alpha);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        mDirty = true;
+    }
+
+    private void setShadowSize(float shadowSize, float maxShadowSize) {
+        if (shadowSize < 0f) {
+            throw new IllegalArgumentException("Invalid shadow size " + shadowSize
+                    + ". Must be >= 0");
+        }
+        if (maxShadowSize < 0f) {
+            throw new IllegalArgumentException("Invalid max shadow size " + maxShadowSize
+                    + ". Must be >= 0");
+        }
+        shadowSize = toEven(shadowSize);
+        maxShadowSize = toEven(maxShadowSize);
+        if (shadowSize > maxShadowSize) {
+            shadowSize = maxShadowSize;
+            if (!mPrintedShadowClipWarning) {
+                mPrintedShadowClipWarning = true;
+            }
+        }
+        if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
+            return;
+        }
+        mRawShadowSize = shadowSize;
+        mRawMaxShadowSize = maxShadowSize;
+        mShadowSize = (int) (shadowSize * SHADOW_MULTIPLIER + mInsetShadow + .5f);
+        mDirty = true;
+        invalidateSelf();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
+                mAddPaddingForCorners));
+        int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
+                mAddPaddingForCorners));
+        padding.set(hOffset, vOffset, hOffset, vOffset);
+        return true;
+    }
+
+    static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
+            boolean addPaddingForCorners) {
+        if (addPaddingForCorners) {
+            return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
+        } else {
+            return maxShadowSize * SHADOW_MULTIPLIER;
+        }
+    }
+
+    static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
+            boolean addPaddingForCorners) {
+        if (addPaddingForCorners) {
+            return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
+        } else {
+            return maxShadowSize;
+        }
+    }
+
+    @Override
+    protected boolean onStateChange(int[] stateSet) {
+        final int newColor = mBackground.getColorForState(stateSet, mBackground.getDefaultColor());
+        if (mPaint.getColor() == newColor) {
+            return false;
+        }
+        mPaint.setColor(newColor);
+        mDirty = true;
+        invalidateSelf();
+        return true;
+    }
+
+    @Override
+    public boolean isStateful() {
+        return (mBackground != null && mBackground.isStateful()) || super.isStateful();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mPaint.setColorFilter(cf);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    void setCornerRadius(float radius) {
+        if (radius < 0f) {
+            throw new IllegalArgumentException("Invalid radius " + radius + ". Must be >= 0");
+        }
+        radius = (int) (radius + .5f);
+        if (mCornerRadius == radius) {
+            return;
+        }
+        mCornerRadius = radius;
+        mDirty = true;
+        invalidateSelf();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mDirty) {
+            buildComponents(getBounds());
+            mDirty = false;
+        }
+        canvas.translate(0, mRawShadowSize / 2);
+        drawShadow(canvas);
+        canvas.translate(0, -mRawShadowSize / 2);
+        sRoundRectHelper.drawRoundRect(canvas, mCardBounds, mCornerRadius, mPaint);
+    }
+
+    private void drawShadow(Canvas canvas) {
+        final float edgeShadowTop = -mCornerRadius - mShadowSize;
+        final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2;
+        final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
+        final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
+        // LT
+        int saved = canvas.save();
+        canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawHorizontalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.width() - 2 * inset, -mCornerRadius,
+                    mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // RB
+        saved = canvas.save();
+        canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
+        canvas.rotate(180f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawHorizontalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize,
+                    mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // LB
+        saved = canvas.save();
+        canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
+        canvas.rotate(270f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawVerticalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // RT
+        saved = canvas.save();
+        canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
+        canvas.rotate(90f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawVerticalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+    }
+
+    private void buildShadowCorners() {
+        RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
+        RectF outerBounds = new RectF(innerBounds);
+        outerBounds.inset(-mShadowSize, -mShadowSize);
+
+        if (mCornerShadowPath == null) {
+            mCornerShadowPath = new Path();
+        } else {
+            mCornerShadowPath.reset();
+        }
+        mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
+        mCornerShadowPath.moveTo(-mCornerRadius, 0);
+        mCornerShadowPath.rLineTo(-mShadowSize, 0);
+        // outer arc
+        mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
+        // inner arc
+        mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
+        mCornerShadowPath.close();
+        float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
+        mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
+                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
+                new float[]{0f, startRatio, 1f},
+                Shader.TileMode.CLAMP));
+
+        // we offset the content shadowSize/2 pixels up to make it more realistic.
+        // this is why edge shadow shader has some extra space
+        // When drawing bottom edge shadow, we use that extra space.
+        mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
+                -mCornerRadius - mShadowSize,
+                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
+                new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
+        mEdgeShadowPaint.setAntiAlias(false);
+    }
+
+    private void buildComponents(Rect bounds) {
+        // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
+        // We could have different top-bottom offsets to avoid extra gap above but in that case
+        // center aligning Views inside the CardView would be problematic.
+        final float verticalOffset = mRawMaxShadowSize * SHADOW_MULTIPLIER;
+        mCardBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset,
+                bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset);
+        buildShadowCorners();
+    }
+
+    float getCornerRadius() {
+        return mCornerRadius;
+    }
+
+    void getMaxShadowAndCornerPadding(Rect into) {
+        getPadding(into);
+    }
+
+    void setShadowSize(float size) {
+        setShadowSize(size, mRawMaxShadowSize);
+    }
+
+    void setMaxShadowSize(float size) {
+        setShadowSize(mRawShadowSize, size);
+    }
+
+    float getShadowSize() {
+        return mRawShadowSize;
+    }
+
+    float getMaxShadowSize() {
+        return mRawMaxShadowSize;
+    }
+
+    float getMinWidth() {
+        final float content = 2
+                * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
+        return content + (mRawMaxShadowSize + mInsetShadow) * 2;
+    }
+
+    float getMinHeight() {
+        final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow
+                        + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
+        return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2;
+    }
+
+    void setColor(@Nullable ColorStateList color) {
+        setBackground(color);
+        invalidateSelf();
+    }
+
+    ColorStateList getColor() {
+        return mBackground;
+    }
+
+    interface RoundRectHelper {
+        void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint);
+    }
+}
diff --git a/v7/gridlayout/.classpath b/v7/gridlayout/.classpath
deleted file mode 100644
index a4763d1..0000000
--- a/v7/gridlayout/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/gridlayout/.gitignore b/v7/gridlayout/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v7/gridlayout/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v7/gridlayout/.project b/v7/gridlayout/.project
deleted file mode 100644
index 1e67516..0000000
--- a/v7/gridlayout/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>android-support-v7-gridlayout</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/v7/gridlayout/Android.mk b/v7/gridlayout/Android.mk
index c584dbe..6eac23b4 100644
--- a/v7/gridlayout/Android.mk
+++ b/v7/gridlayout/Android.mk
@@ -29,7 +29,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-core-ui \
diff --git a/v7/gridlayout/AndroidManifest-make.xml b/v7/gridlayout/AndroidManifest-make.xml
deleted file mode 100644
index d2cc627..0000000
--- a/v7/gridlayout/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v7.gridlayout">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application />
-</manifest>
diff --git a/v7/gridlayout/AndroidManifest.xml b/v7/gridlayout/AndroidManifest.xml
index 45a0978..b6cb783 100644
--- a/v7/gridlayout/AndroidManifest.xml
+++ b/v7/gridlayout/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.v7.gridlayout">
-    <uses-sdk android:minSdkVersion="9"/>
+    <uses-sdk android:minSdkVersion="14"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v7/gridlayout/README.txt b/v7/gridlayout/README.txt
deleted file mode 100644
index e754187..0000000
--- a/v7/gridlayout/README.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-Library Project including compatibility GridLayout.
-
-This can be used by an Android project to provide
-access to GridLayout on applications running on API 7+.
-
-There is technically no source, but the src folder is necessary
-to ensure that the build system works.  The content is actually
-located in libs/android-support-v7-gridlayout.jar.
-The accompanying resources must also be included in the application.
-
-
-USAGE:
-
-Make sure you use <android.support.v7.widget.GridLayout> in your
-layouts instead of <GridLayout>.
-Same for <android.support.v4.widget.Space> instead of <Space>.
-
-Additionally, all of GridLayout's attributes should be put in the
-namespace of the app, as those attributes have been redefined in
-the library so that it can run on older platforms that don't offer
-those attributes in their namespace.
-
-To know which attributes need the application namespace, look at
-the two declare-styleable declared in res/values/attrs.xml
-
-
-
-For instance:
-
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v7.widget.GridLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"  <==== the namespace used for the library project
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    app:columnCount="6" >                                <===== notice how we're using app:columnCount here, not android:columnCount!
-
-    <Button
-        android:id="@+id/button1"
-        app:layout_column="1"                            <=== again, note the app: namespace
-        app:layout_columnSpan="2"
-        app:layout_gravity="left"
-        app:layout_row="1"
-        android:text="Button" />
-
-    <CheckBox
-        android:id="@+id/checkBox1"
-        app:layout_column="4"
-        app:layout_gravity="left"
-        app:layout_row="2"
-        android:text="CheckBox" />
-
-    <Button
-        android:id="@+id/button2"
-        app:layout_column="5"
-        app:layout_gravity="left"
-        app:layout_row="3"
-        android:text="Button" />
-
-    <android.support.v4.widget.Space                    <=== space widgets also need the full support package path
-        android:layout_width="21dp"                     <=== use the android namespace for width, height etc -- only use app: for the grid layout library's new resources
-        android:layout_height="1dp"
-        app:layout_column="0"
-        app:layout_gravity="fill_horizontal"
-        app:layout_row="0" />
-
diff --git a/v7/gridlayout/build.gradle b/v7/gridlayout/build.gradle
index 56f320f..c66848f 100644
--- a/v7/gridlayout/build.gradle
+++ b/v7/gridlayout/build.gradle
@@ -1,95 +1,33 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'gridlayout-v7'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-core-ui')
+    api project(':support-compat')
+    api project(':support-core-ui')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    testCompile 'junit:junit:4.12'
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
         main.res.srcDir 'res'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
-
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Library v4'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Grid Layout'
+    inceptionYear '2013'
+    description 'Android Support Grid Layout'
 }
diff --git a/v7/gridlayout/lint-baseline.xml b/v7/gridlayout/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v7/gridlayout/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v7/gridlayout/project.properties b/v7/gridlayout/project.properties
deleted file mode 100644
index 1e106c3..0000000
--- a/v7/gridlayout/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-7
-android.library=true
\ No newline at end of file
diff --git a/v7/gridlayout/src/.readme b/v7/gridlayout/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/gridlayout/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/gridlayout/src/android/support/v7/widget/GridLayout.java b/v7/gridlayout/src/android/support/v7/widget/GridLayout.java
index 8dc04dc..ad9ece5 100644
--- a/v7/gridlayout/src/android/support/v7/widget/GridLayout.java
+++ b/v7/gridlayout/src/android/support/v7/widget/GridLayout.java
@@ -16,6 +16,19 @@
 
 package android.support.v7.widget;
 
+import static android.view.Gravity.AXIS_PULL_AFTER;
+import static android.view.Gravity.AXIS_PULL_BEFORE;
+import static android.view.Gravity.AXIS_SPECIFIED;
+import static android.view.Gravity.AXIS_X_SHIFT;
+import static android.view.Gravity.AXIS_Y_SHIFT;
+import static android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
+import static android.view.Gravity.VERTICAL_GRAVITY_MASK;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -23,6 +36,7 @@
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewGroupCompat;
+import android.support.v7.gridlayout.R;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.LogPrinter;
@@ -33,8 +47,6 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import android.support.v7.gridlayout.R;
-
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -42,12 +54,6 @@
 import java.util.List;
 import java.util.Map;
 
-import static android.view.Gravity.*;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-
 /**
  * A layout that places its children in a rectangular <em>grid</em>.
  * <p>
@@ -968,8 +974,8 @@
         int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
 
         setMeasuredDimension(
-                ViewCompat.resolveSizeAndState(measuredWidth, widthSpec, 0),
-                ViewCompat.resolveSizeAndState(measuredHeight, heightSpec, 0));
+                View.resolveSizeAndState(measuredWidth, widthSpec, 0),
+                View.resolveSizeAndState(measuredHeight, heightSpec, 0));
     }
 
     private int getMeasurement(View c, boolean horizontal) {
diff --git a/v7/gridlayout/tests/AndroidManifest.xml b/v7/gridlayout/tests/AndroidManifest.xml
index 4c4b0ab..e970897 100644
--- a/v7/gridlayout/tests/AndroidManifest.xml
+++ b/v7/gridlayout/tests/AndroidManifest.xml
@@ -15,21 +15,11 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v7.gridlayout.test">
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                  android.support.test.espresso, android.support.test.espresso.idling" />
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application>
-        <uses-library android:name="android.test.runner" />
-
         <activity android:name="android.support.v7.widget.GridLayoutTestActivity"/>
     </application>
 
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.v7.gridlayout.test"/>
 </manifest>
diff --git a/v7/mediarouter/.classpath b/v7/mediarouter/.classpath
deleted file mode 100644
index a4763d1..0000000
--- a/v7/mediarouter/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/mediarouter/.gitignore b/v7/mediarouter/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v7/mediarouter/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v7/mediarouter/.project b/v7/mediarouter/.project
deleted file mode 100644
index 2eca48b..0000000
--- a/v7/mediarouter/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>android-support-v7-mediarouter</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/v7/mediarouter/Android.mk b/v7/mediarouter/Android.mk
index 6162f87..21b4a62 100644
--- a/v7/mediarouter/Android.mk
+++ b/v7/mediarouter/Android.mk
@@ -35,7 +35,6 @@
     $(call all-java-files-under,api24) \
     $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-appcompat \
     android-support-v7-palette \
diff --git a/v7/mediarouter/AndroidManifest-make.xml b/v7/mediarouter/AndroidManifest-make.xml
deleted file mode 100644
index 59d9f99..0000000
--- a/v7/mediarouter/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v7.mediarouter">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application />
-</manifest>
diff --git a/v7/mediarouter/AndroidManifest.xml b/v7/mediarouter/AndroidManifest.xml
index 3f50fe6..c4577f9 100644
--- a/v7/mediarouter/AndroidManifest.xml
+++ b/v7/mediarouter/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.v7.mediarouter">
-    <uses-sdk android:minSdkVersion="9"/>
+    <uses-sdk android:minSdkVersion="14"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v7/mediarouter/README.txt b/v7/mediarouter/README.txt
deleted file mode 100644
index 5d99185..0000000
--- a/v7/mediarouter/README.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Library Project including compatibility MediaRouter.
-
-This can be used by an Android project to provide
-access to MediaRouter on applications running on API 7+.
-
-There is technically no source, but the src folder is necessary
-to ensure that the build system works.  The content is actually
-located in libs/android-support-v7-mediarouter.jar.
-The accompanying resources must also be included in the application.
-
diff --git a/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
index 48bef17..0b1eda9 100644
--- a/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
+++ b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
@@ -16,11 +16,9 @@
 
 package android.support.v7.media;
 
-import android.annotation.TargetApi;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(24)
-@TargetApi(24)
 final class MediaRouterApi24 {
     public static final class RouteInfo {
         public static int getDeviceType(Object routeObj) {
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index 7796565..eabc5e3 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -1,25 +1,25 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'mediarouter-v7'
 
 dependencies {
-    compile project(":support-appcompat-v7")
-    compile project(":support-palette-v7")
+    api project(":support-appcompat-v7")
+    api project(":support-palette-v7")
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}")
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation (libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation (libs.espresso_core) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation libs.test_rules
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = [
                 'jellybean',
                 'jellybean-mr1',
@@ -28,65 +28,11 @@
                 'src'
         ]
         main.res.srcDir 'res'
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android MediaRouter Support Library'
-                description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 4 or later."
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android MediaRouter Support Library'
+    inceptionYear '2013'
+    description 'Android MediaRouter Support Library'
 }
diff --git a/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java b/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
index 3a42a2f..6fc5ba5 100644
--- a/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
+++ b/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.media;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
@@ -30,7 +29,6 @@
 import java.lang.reflect.Method;
 
 @RequiresApi(17)
-@TargetApi(17)
 final class MediaRouterJellybeanMr1 {
     private static final String TAG = "MediaRouterJellybeanMr1";
 
diff --git a/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java b/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
index 6799faa..8abfc7f 100644
--- a/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
+++ b/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
@@ -16,11 +16,9 @@
 
 package android.support.v7.media;
 
-import android.annotation.TargetApi;
 import android.support.annotation.RequiresApi;
 
 @RequiresApi(18)
-@TargetApi(18)
 final class MediaRouterJellybeanMr2 {
     public static Object getDefaultRoute(Object routerObj) {
         return ((android.media.MediaRouter)routerObj).getDefaultRoute();
diff --git a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
index 2613f90..51bd498 100644
--- a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
+++ b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.media;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
@@ -24,14 +23,12 @@
 import android.support.annotation.RequiresApi;
 import android.util.Log;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
 @RequiresApi(16)
-@TargetApi(16)
 final class MediaRouterJellybean {
     private static final String TAG = "MediaRouterJellybean";
 
diff --git a/v7/mediarouter/lint-baseline.xml b/v7/mediarouter/lint-baseline.xml
new file mode 100644
index 0000000..9ea4e70
--- /dev/null
+++ b/v7/mediarouter/lint-baseline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="MissingSuperCall"
+        message="Overriding method should call `super.jumpDrawablesToCurrentState`"
+        errorLine1="    public void jumpDrawablesToCurrentState() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/v7/app/MediaRouteButton.java"
+            line="380"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="LongLogTag"
+        message="The logging tag can be at most 23 characters, was 24 (MediaRouteActionProvider)"
+        errorLine1="            Log.e(TAG, &quot;onCreateActionView: this ActionProvider is already associated &quot; +"
+        errorLine2="                  ~~~">
+        <location
+            file="src/android/support/v7/app/MediaRouteActionProvider.java"
+            line="248"
+            column="19"/>
+    </issue>
+
+</issues>
diff --git a/v7/mediarouter/project.properties b/v7/mediarouter/project.properties
deleted file mode 100644
index 484dab0..0000000
--- a/v7/mediarouter/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-17
-android.library=true
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png
index c04e284..f6dd214 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_light.png
index da4c296..6b7bdcd 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_00_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png
index 9fd99dc..c7fe576 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_light.png
index 6be3008..0a5d6aa 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_01_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png
index a5946ab..0aadfa3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_light.png
index 1c8a4e1..125fe0b 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_02_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png
index 1ea1f71..05c48a7 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_light.png
index 3397f14..741e911 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_03_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png
index 0c211e5..ae4218a 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_light.png
index 620ec69..8b30fab 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_04_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png
index 4ac4f94..d7aa903 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_light.png
index aee1a31..f7e2f29 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_05_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png
index 5e99a48..e7871e2 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_light.png
index d357a9f..8c57f63 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_06_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png
index 7e23799..0041b01 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_light.png
index df0247d..6dbb694 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_07_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png
index d49c09b..08e1013 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_light.png
index 924212a..5c352c3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_08_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png
index 4df5079..70532e9 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_light.png
index dfaaef4..9c6ba30 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_09_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png
index 8b2499e..9ba3b5f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_light.png
index 47482ab..bd4bb22 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_10_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png
index 373aad0..2156127 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_light.png
index b8d035e..b417a9f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_11_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png
index f813902..9bf633e 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_light.png
index 0952676..ba51811 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_12_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png
index 3825e0a..756a53c 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_light.png
index c493860..4705dca 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_13_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png
index 12da4da..50e4ea3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_light.png
index 5420e91..bc6724f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_14_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png
index 8bb448e..9e3b410 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_light.png
index 6c7fb96..2f18abd 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_15_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png
index e000188..de81133 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_light.png
index 2afcf5b..b80b191 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_16_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png
index ead72cb..48aba3d 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_light.png
index 5e91f50..ca34d5b 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_17_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png
index 1939ceb..e9957b3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_light.png
index 3d6105c..a5d384f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_18_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png
index 4be02d4..ddc6297 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_light.png
index 1b18aad..28ab684 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_19_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png
index 25afe1a..51e7f75 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_light.png
index 219ac25..4aa3ca3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_20_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png
index 1ac72a3..9caecde 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_light.png
index 9b0645d..1b8d0b6 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_21_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png
index 62308f2..400be3c 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_light.png
index 6bab08f..c14f1bf 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_22_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_23_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_23_dark.png
new file mode 100644
index 0000000..4e18b46
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_23_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_23_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_23_light.png
new file mode 100644
index 0000000..c4c2c00
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_23_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_24_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_24_dark.png
new file mode 100644
index 0000000..98fae44
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_24_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_24_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_24_light.png
new file mode 100644
index 0000000..d64c289
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_24_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_25_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_25_dark.png
new file mode 100644
index 0000000..91f9327
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_25_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_25_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_25_light.png
new file mode 100644
index 0000000..f5e1f69
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_25_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_26_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_26_dark.png
new file mode 100644
index 0000000..3e6fafd
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_26_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_26_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_26_light.png
new file mode 100644
index 0000000..ae2bd87
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_26_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_27_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_27_dark.png
new file mode 100644
index 0000000..f73a1f8
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_27_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_27_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_27_light.png
new file mode 100644
index 0000000..78c1069
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_27_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_28_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_28_dark.png
new file mode 100644
index 0000000..562b803
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_28_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_28_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_28_light.png
new file mode 100644
index 0000000..ddfba02
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_28_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_29_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_29_dark.png
new file mode 100644
index 0000000..257f2d2
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_29_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_29_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_29_light.png
new file mode 100644
index 0000000..38f5478
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_29_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_30_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_30_dark.png
new file mode 100644
index 0000000..f995af0
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_30_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_30_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_30_light.png
new file mode 100644
index 0000000..c50b7f0
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connected_30_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png
index 7c8b5bc..f6dd214 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png
index 85fe39c..6b7bdcd 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png
index b904ed1..c7fe576 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png
index 94bcad9..0a5d6aa 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png
index 9c8f73d..0aadfa3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png
index 70ecc18..125fe0b 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png
index 9cc01b3..05c48a7 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png
index 6dc67ec..741e911 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png
index 8692da8..ae4218a 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png
index 92061ac..8b30fab 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png
index 93af29a..d7aa903 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png
index 3535d6f..f7e2f29 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png
index 7a17cdb..e7871e2 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png
index 101207f..8c57f63 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png
index 186133e..0041b01 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png
index 1e3afe1..6dbb694 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png
index 34cb511..08e1013 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png
index 79f84ca..5c352c3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png
index 736bad8..70532e9 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png
index 0fe61a4..9c6ba30 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png
index 22862e3..9ba3b5f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png
index 6a310bd..bd4bb22 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png
index 3eb3fb4..f3570f4 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png
index 3bc4947..65a403e 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png
index 58a6f9e..f644bfd 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png
index f017cc6..c7d6048 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png
index ef3f046..6e0d558 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png
index cf52855..f3bc48d 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png
index f701696..14d8f8e 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png
index 26587e1..98b90e5 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png
index 7d3342c..83234a7 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png
index e6ea555..47d452f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png
index aa1f673..b81cf5a 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png
index 5ad2aa3..20d08b4 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png
index 4c412a8..6feb3f1 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png
index b919359..e6ae8b3 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png
index a27e3d7..0b0fc08 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png
index efe5264..c2a16ac 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png
index a048045..a3598cc 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png
index 8c31541..846d16d 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png
index fbb59c5..2070455 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png
index 0bf03e2..ae6db13 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png
index 6cfa4c4..7f3828a 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png
index 49f0f63..aaccc73 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png
index 6fdd92f..5c8ced9 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png
index 63fd45f..ad01b9e 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_23_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_23_dark.png
new file mode 100644
index 0000000..ce31dd3
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_23_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_23_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_23_light.png
new file mode 100644
index 0000000..9ef78e4
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_23_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_24_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_24_dark.png
new file mode 100644
index 0000000..a7c2cdb
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_24_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_24_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_24_light.png
new file mode 100644
index 0000000..e7c5bea
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_24_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_25_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_25_dark.png
new file mode 100644
index 0000000..ecad0d4
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_25_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_25_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_25_light.png
new file mode 100644
index 0000000..5fa5923
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_25_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_26_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_26_dark.png
new file mode 100644
index 0000000..f687e25
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_26_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_26_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_26_light.png
new file mode 100644
index 0000000..9c06db8
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_26_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_27_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_27_dark.png
new file mode 100644
index 0000000..90225ba
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_27_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_27_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_27_light.png
new file mode 100644
index 0000000..19697de
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_27_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_28_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_28_dark.png
new file mode 100644
index 0000000..d37ec21
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_28_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_28_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_28_light.png
new file mode 100644
index 0000000..21840bf
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_28_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_29_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_29_dark.png
new file mode 100644
index 0000000..5445e3a
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_29_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_29_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_29_light.png
new file mode 100644
index 0000000..2337c65
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_29_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_30_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_30_dark.png
new file mode 100644
index 0000000..f6dd214
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_30_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_30_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_30_light.png
new file mode 100644
index 0000000..6b7bdcd
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_mr_button_connecting_30_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png
index e3fcc5a..0db679e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png
index 8915e73..51c6051 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png
index 1f7c6a7..c083914 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png
index e2f859a..c3c3caf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png
index 5d7377d..fc444cf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png
index e87e963..abd6377 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png
index 5d98ca9..6dbd1da 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png
index 5dd1728..d2e7108 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png
index 5768bf6..d9f596b 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png
index 33dcad3..4f32e1a 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png
index 03b6d06..c568e04 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png
index 04de404..ed20dd9 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png
index b145e78..bbe39e7 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png
index 7f6b343..1edc15f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png
index f553b98..78aebaf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png
index 3c6d7ec..b5a6a4f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png
index bc96ec5..44b91ce 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png
index 78d7707..85f66f9 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png
index 1fc99d3..51ea34b 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png
index cecbb05..952de04 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png
index 85c3404..8b1aa21 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png
index 9504282..534bcc0 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png
index de5411f..f666b35 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png
index 75ac2b5..145a8fb 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png
index bc5016f..edeb132 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png
index e80a01a..9da2b60 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png
index d3bc15e..ab80aa9 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png
index 2aec231..115efe4 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png
index 0dc7afa..8c0cc31 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png
index f6b8124..e6ae6fc 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png
index 428f2a5..b8816c9 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png
index 9b07aee..bd42931 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png
index 92b87f3..10d5b7f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png
index d3aa0ec..303a0fe 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png
index c8f47f3..3c2a655 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png
index 0ffd70c..90debc2 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png
index bd6b74d..d3e78a7 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png
index 5a5644a..3a3f991 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png
index b9fcf3a..63fad9e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png
index 41904df..d6dd8d4 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png
index 7d765c3..890fd5f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png
index 1b38bd1..6b0b5c1 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png
index 1947769..9ce1ef1 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png
index 6c01a5f..81710d4 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png
index 5d84106..861c080 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png
index c03c7f6..1c4aa21 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_23_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_23_dark.png
new file mode 100644
index 0000000..59a6b30
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_23_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_23_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_23_light.png
new file mode 100644
index 0000000..c6e8fe0
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_23_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_24_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_24_dark.png
new file mode 100644
index 0000000..57b840e
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_24_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_24_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_24_light.png
new file mode 100644
index 0000000..bf24050
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_24_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_25_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_25_dark.png
new file mode 100644
index 0000000..01c18c1
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_25_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_25_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_25_light.png
new file mode 100644
index 0000000..be9753e
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_25_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_26_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_26_dark.png
new file mode 100644
index 0000000..3f291b1
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_26_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_26_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_26_light.png
new file mode 100644
index 0000000..dc1c619
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_26_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_27_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_27_dark.png
new file mode 100644
index 0000000..6504a70
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_27_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_27_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_27_light.png
new file mode 100644
index 0000000..a7e0a60
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_27_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_28_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_28_dark.png
new file mode 100644
index 0000000..57b1f3e
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_28_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_28_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_28_light.png
new file mode 100644
index 0000000..5c551ec
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_28_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_29_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_29_dark.png
new file mode 100644
index 0000000..238667e
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_29_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_29_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_29_light.png
new file mode 100644
index 0000000..ffb8183
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_29_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_30_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_30_dark.png
new file mode 100644
index 0000000..4893f18
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_30_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_30_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_30_light.png
new file mode 100644
index 0000000..ac5e156
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connected_30_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png
index 5e481fb..0db679e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png
index c1ebe58..51c6051 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png
index f3c8f71..c083914 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png
index 9a16dda..c3c3caf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png
index 17d29bb..fc444cf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png
index 8e17432..abd6377 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png
index 72aec70..6dbd1da 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png
index b41b474..d2e7108 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png
index 5a9aa0d..d9f596b 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png
index 9b7a3c7..4f32e1a 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png
index b0bd59e..c568e04 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png
index f9544ee..ed20dd9 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png
index 3543c84..bbe39e7 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png
index 9ecc2e4..1edc15f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png
index 63a5a16..78aebaf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png
index 8ae025a..b5a6a4f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png
index 2e506e8..44b91ce 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png
index 7c96cc5..85f66f9 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png
index aeb7151..51ea34b 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png
index deb6756..952de04 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png
index 4f679de..8b1aa21 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png
index 54dbd70..534bcc0 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png
index e971a46..1fffa01 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png
index 7bb99d5..0ff7e57 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png
index c7dd030..06ac4dc 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png
index 1f081a0..42a86f5 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png
index 9da3f8f..0301090 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png
index 38b45ab..4396f0e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png
index 6c05acb..e19001b 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png
index 0294d12..2271581 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png
index 043096d..5e96208 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png
index 9d9672a..0f69500 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png
index 321e868..07e1bd6 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png
index 7d8f491..cde8f19 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png
index ecd6c23..b632e95 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png
index 0eb016b..11d5d2e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png
index b6d08aa..660d527 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png
index 4df7118..2761ae1 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png
index 653754a..0aa3f84 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png
index cf62d75..27d166f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png
index 4fa3be0..ebe527e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png
index ca82881..aeb2a8e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png
index 86b20be..7337af5 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png
index 74521d0..f3f31ef 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png
index a646b60..20d9f57 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png
index be0b12a..bf8eb77 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_23_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_23_dark.png
new file mode 100644
index 0000000..56a0e14
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_23_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_23_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_23_light.png
new file mode 100644
index 0000000..67425e1
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_23_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_24_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_24_dark.png
new file mode 100644
index 0000000..7c76e19
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_24_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_24_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_24_light.png
new file mode 100644
index 0000000..e02f1ed
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_24_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_25_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_25_dark.png
new file mode 100644
index 0000000..f5fdcdd
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_25_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_25_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_25_light.png
new file mode 100644
index 0000000..8ce9b81
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_25_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_26_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_26_dark.png
new file mode 100644
index 0000000..a29e443
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_26_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_26_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_26_light.png
new file mode 100644
index 0000000..349ca89
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_26_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_27_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_27_dark.png
new file mode 100644
index 0000000..0fc75d5
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_27_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_27_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_27_light.png
new file mode 100644
index 0000000..5cbd27c
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_27_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_28_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_28_dark.png
new file mode 100644
index 0000000..0ebb0ac
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_28_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_28_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_28_light.png
new file mode 100644
index 0000000..5b514aa
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_28_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_29_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_29_dark.png
new file mode 100644
index 0000000..8e7fe5c
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_29_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_29_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_29_light.png
new file mode 100644
index 0000000..efb2c10
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_29_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_30_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_30_dark.png
new file mode 100644
index 0000000..0db679e
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_30_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_30_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_30_light.png
new file mode 100644
index 0000000..51c6051
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_mr_button_connecting_30_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable/mr_button_connected_dark.xml b/v7/mediarouter/res/drawable/mr_button_connected_dark.xml
index ddf8c91..c69ba33 100644
--- a/v7/mediarouter/res/drawable/mr_button_connected_dark.xml
+++ b/v7/mediarouter/res/drawable/mr_button_connected_dark.xml
@@ -40,4 +40,12 @@
     <item android:drawable="@drawable/ic_mr_button_connected_20_dark" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connected_21_dark" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connected_22_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_23_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_24_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_25_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_26_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_27_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_28_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_29_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_30_dark" android:duration="42" />
 </animation-list>
diff --git a/v7/mediarouter/res/drawable/mr_button_connected_light.xml b/v7/mediarouter/res/drawable/mr_button_connected_light.xml
index ba0434a..af7224b 100644
--- a/v7/mediarouter/res/drawable/mr_button_connected_light.xml
+++ b/v7/mediarouter/res/drawable/mr_button_connected_light.xml
@@ -40,4 +40,12 @@
     <item android:drawable="@drawable/ic_mr_button_connected_20_light" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connected_21_light" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connected_22_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_23_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_24_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_25_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_26_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_27_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_28_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_29_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connected_30_light" android:duration="42" />
 </animation-list>
diff --git a/v7/mediarouter/res/drawable/mr_button_connecting_dark.xml b/v7/mediarouter/res/drawable/mr_button_connecting_dark.xml
index ba3a673..697f17d 100644
--- a/v7/mediarouter/res/drawable/mr_button_connecting_dark.xml
+++ b/v7/mediarouter/res/drawable/mr_button_connecting_dark.xml
@@ -40,4 +40,12 @@
     <item android:drawable="@drawable/ic_mr_button_connecting_20_dark" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connecting_21_dark" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connecting_22_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_23_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_24_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_25_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_26_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_27_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_28_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_29_dark" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_30_dark" android:duration="42" />
 </animation-list>
diff --git a/v7/mediarouter/res/drawable/mr_button_connecting_light.xml b/v7/mediarouter/res/drawable/mr_button_connecting_light.xml
index c511782..07a135d 100644
--- a/v7/mediarouter/res/drawable/mr_button_connecting_light.xml
+++ b/v7/mediarouter/res/drawable/mr_button_connecting_light.xml
@@ -40,4 +40,12 @@
     <item android:drawable="@drawable/ic_mr_button_connecting_20_light" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connecting_21_light" android:duration="42" />
     <item android:drawable="@drawable/ic_mr_button_connecting_22_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_23_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_24_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_25_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_26_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_27_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_28_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_29_light" android:duration="42" />
+    <item android:drawable="@drawable/ic_mr_button_connecting_30_light" android:duration="42" />
 </animation-list>
diff --git a/v7/mediarouter/res/values-az-rAZ/strings.xml b/v7/mediarouter/res/values-az/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-az-rAZ/strings.xml
rename to v7/mediarouter/res/values-az/strings.xml
diff --git a/v7/mediarouter/res/values-b+sr+Latn/strings.xml b/v7/mediarouter/res/values-b+sr+Latn/strings.xml
index e64356e..c4bfa49 100644
--- a/v7/mediarouter/res/values-b+sr+Latn/strings.xml
+++ b/v7/mediarouter/res/values-b+sr+Latn/strings.xml
@@ -22,13 +22,14 @@
     <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme Prebaci. Veza je prekinuta"</string>
     <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme Prebaci. Povezuje se"</string>
     <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme Prebaci. Povezan je"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Prebacujte na"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Prebacuj na"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Pronalaženje uređaja"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
-    <string name="mr_controller_stop_casting" msgid="4570331844078181931">"Zaustavi prebacivanje"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zaustavi prebacivanje"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvori"</string>
     <string name="mr_controller_play" msgid="683634565969987458">"Pusti"</string>
     <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziraj"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"Zaustavi"</string>
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
diff --git a/v7/mediarouter/res/values-be-rBY/strings.xml b/v7/mediarouter/res/values-be-rBY/strings.xml
deleted file mode 100644
index 93ccc1a..0000000
--- a/v7/mediarouter/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Сістэма"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Прылады"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляцыі"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляцыі. Адключана"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляцыі. Ідзе падключэнне"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляцыі. Падключана"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Трансляваць на"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Пошук прылад"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Адлучыць"</string>
-    <string name="mr_controller_stop_casting" msgid="4570331844078181931">"Спыніць трансляцыю"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Закрыць"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Прайграць"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Прыпыніць"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Разгарнуць"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Згарнуць"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Вокладка альбома"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Паўзунок гучнасці"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медыяфайл не выбраны"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Інфармацыя адсутнічае"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Экран трансляцыі"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-be/strings.xml b/v7/mediarouter/res/values-be/strings.xml
new file mode 100644
index 0000000..396088f
--- /dev/null
+++ b/v7/mediarouter/res/values-be/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Сістэма"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Прылады"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляцыі"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляцыі. Адключана"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляцыі. Ідзе падключэнне"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляцыі. Падключана"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Трансліраваць на"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Пошук прылад"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Адлучыць"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Спыніць трансляцыю"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Закрыць"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Прайграць"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"Прыпыніць"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"Спыніць"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Разгарнуць"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Згарнуць"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"Вокладка альбома"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Паўзунок гучнасці"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медыяфайл не выбраны"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Інфармацыя адсутнічае"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Экран трансляцыі"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-bn-rBD/strings.xml b/v7/mediarouter/res/values-bn/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-bn-rBD/strings.xml
rename to v7/mediarouter/res/values-bn/strings.xml
diff --git a/v7/mediarouter/res/values-bs-rBA/strings.xml b/v7/mediarouter/res/values-bs-rBA/strings.xml
deleted file mode 100644
index cd8e586..0000000
--- a/v7/mediarouter/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme za prebacivanje"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme za prebacivanje. Veza je prekinuta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme za prebacivanje. Povezivanje"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme za prebacivanje. Povezan"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Prebacujte na"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Traženje uređaja"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
-    <string name="mr_controller_stop_casting" msgid="4570331844078181931">"Zaustavi prebacivanje"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvori"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduciraj"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziraj"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za jačinu zvuka"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nijedan medij nije odabran"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nema dostupnih informacija"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-bs/strings.xml b/v7/mediarouter/res/values-bs/strings.xml
new file mode 100644
index 0000000..ab9575e
--- /dev/null
+++ b/v7/mediarouter/res/values-bs/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme za emitiranje"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme za emitiranje. Veza je prekinuta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme za emitiranje. Povezivanje"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme za emitiranje. Povezano"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Emitiranje na"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Traženje uređaja"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zaustavi prebacivanje"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvori"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Reproduciraj"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziraj"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"Zaustavi"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za jačinu zvuka"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nijedan medij nije odabran"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nema dostupnih informacija"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-et-rEE/strings.xml b/v7/mediarouter/res/values-et/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-et-rEE/strings.xml
rename to v7/mediarouter/res/values-et/strings.xml
diff --git a/v7/mediarouter/res/values-eu-rES/strings.xml b/v7/mediarouter/res/values-eu/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-eu-rES/strings.xml
rename to v7/mediarouter/res/values-eu/strings.xml
diff --git a/v7/mediarouter/res/values-gl-rES/strings.xml b/v7/mediarouter/res/values-gl-rES/strings.xml
deleted file mode 100644
index 7e38bb4..0000000
--- a/v7/mediarouter/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botón de emitir"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón de emitir. Desconectado"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón de emitir. Conectando"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón de emitir. Conectado"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Emitir en"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Deter emisión"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Pechar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduce"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Deter"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ampliar"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Contraer"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Portada do álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control desprazable do volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Non se seleccionaron recursos"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Non hai información dispoñible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emisión de pantalla"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-gl/strings.xml b/v7/mediarouter/res/values-gl/strings.xml
new file mode 100644
index 0000000..e509ab7
--- /dev/null
+++ b/v7/mediarouter/res/values-gl/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Botón de emitir"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón de emitir. Desconectado"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón de emitir. Conectando"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón de emitir. Conectado"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Emitir a"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Deter emisión"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Pechar"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Reproduce"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"Deter"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ampliar"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Contraer"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"Portada do álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control desprazable do volume"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Non se seleccionaron recursos"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Non hai información dispoñible"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emisión de pantalla"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-gu-rIN/strings.xml b/v7/mediarouter/res/values-gu/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-gu-rIN/strings.xml
rename to v7/mediarouter/res/values-gu/strings.xml
diff --git a/v7/mediarouter/res/values-hy-rAM/strings.xml b/v7/mediarouter/res/values-hy/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-hy-rAM/strings.xml
rename to v7/mediarouter/res/values-hy/strings.xml
diff --git a/v7/mediarouter/res/values-is-rIS/strings.xml b/v7/mediarouter/res/values-is/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-is-rIS/strings.xml
rename to v7/mediarouter/res/values-is/strings.xml
diff --git a/v7/mediarouter/res/values-iw/strings.xml b/v7/mediarouter/res/values-iw/strings.xml
index 3e7bc50..02f50ff 100644
--- a/v7/mediarouter/res/values-iw/strings.xml
+++ b/v7/mediarouter/res/values-iw/strings.xml
@@ -22,7 +22,7 @@
     <string name="mr_cast_button_disconnected" msgid="816305490427819240">"‏לחצן הפעלת Cast. מנותק"</string>
     <string name="mr_cast_button_connecting" msgid="2187642765091873834">"‏לחצן הפעלת Cast. מתחבר"</string>
     <string name="mr_cast_button_connected" msgid="5088427771788648085">"‏לחצן הפעלת Cast. מחובר"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"העבר אל"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"העברה אל"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"מחפש מכשירים"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"נתק"</string>
     <string name="mr_controller_stop_casting" msgid="8857886794086583226">"הפסק את ההעברה"</string>
diff --git a/v7/mediarouter/res/values-ka-rGE/strings.xml b/v7/mediarouter/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 57b76e9..0000000
--- a/v7/mediarouter/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"სისტემა"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"მოწყობილობები"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ტრანსლირების ღილაკი"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ტრანსლირების ღილაკი. გათიშული"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ტრანსლირების ღილაკი. მიმდინარეობს დაკავშირება"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ტრანსლირების ღილაკი. დაკავშირებული"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ტრანსლირებული"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"მიმდინარეობს მოწყობილობების მოძიება"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"კავშირის გაწყვეტა"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ტრანსლირების შეწყვეტა"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"დახურვა"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"დაკვრა"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"პაუზა"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"შეწყვეტა"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"გაშლა"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ჩაკეცვა"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ალბომის გარეკანი"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ხმის სლაიდერი"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"მედია არჩეული არ არის"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ინფორმაცია არ არის ხელმისაწვდომი"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"მიმდინარეობს ეკრანის გადაცემა"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-ka/strings.xml b/v7/mediarouter/res/values-ka/strings.xml
new file mode 100644
index 0000000..22480ca
--- /dev/null
+++ b/v7/mediarouter/res/values-ka/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"სისტემა"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"მოწყობილობები"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"ტრანსლირების ღილაკი"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ტრანსლირების ღილაკი. გათიშული"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ტრანსლირების ღილაკი. მიმდინარეობს დაკავშირება"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ტრანსლირების ღილაკი. დაკავშირებული"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"ტრანსლირება:"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"მოწყობილობების მოძიება..."</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"კავშირის გაწყვეტა"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ტრანსლირების შეწყვეტა"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"დახურვა"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"დაკვრა"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"პაუზა"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"შეწყვეტა"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"გაშლა"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ჩაკეცვა"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"ალბომის გარეკანი"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ხმის სლაიდერი"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"მედია არჩეული არ არის"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ინფორმაცია არ არის ხელმისაწვდომი"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"მიმდინარეობს ეკრანის გადაცემა"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-kk-rKZ/strings.xml b/v7/mediarouter/res/values-kk/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-kk-rKZ/strings.xml
rename to v7/mediarouter/res/values-kk/strings.xml
diff --git a/v7/mediarouter/res/values-km-rKH/strings.xml b/v7/mediarouter/res/values-km-rKH/strings.xml
deleted file mode 100644
index e9bef13..0000000
--- a/v7/mediarouter/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ប្រព័ន្ធ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ឧបករណ៍"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ប៊ូតុងខាស"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ខាសប៊ូតុង៖ បានកាត់ផ្តាច់"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ខាសប៊ូតុង៖ កំពុងភ្ជាប់"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ខាសប៊ូតុង៖ បានភ្ជាប់ហើយ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ខាសទៅ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ស្វែងរកឧបករណ៍"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ផ្ដាច់"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ឈប់ភ្ជាប់"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"បិទ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ចាក់"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ផ្អាក"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ឈប់"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ពង្រីក"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"បង្រួម"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ស្នាដៃសិល្បៈអាល់ប៊ុម"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"របារកម្រិតសំឡេង"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"គ្មានការជ្រើសមេឌៀទេ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"មិនមានព័ត៌មានទេ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"កំពុងខាសអេក្រង់"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-km/strings.xml b/v7/mediarouter/res/values-km/strings.xml
new file mode 100644
index 0000000..f9f339d
--- /dev/null
+++ b/v7/mediarouter/res/values-km/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ប្រព័ន្ធ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ឧបករណ៍"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"ប៊ូតុងខាស"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ខាសប៊ូតុង៖ បានកាត់ផ្តាច់"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ខាសប៊ូតុង៖ កំពុងភ្ជាប់"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ខាសប៊ូតុង៖ បានភ្ជាប់ហើយ"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"បញ្ជូនទៅ"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"កំពុងស្វែងរកឧបករណ៍"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ផ្ដាច់"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ឈប់ភ្ជាប់"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"បិទ"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"ចាក់"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"ផ្អាក"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"ឈប់"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ពង្រីក"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"បង្រួម"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"ស្នាដៃសិល្បៈអាល់ប៊ុម"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"របារកម្រិតសំឡេង"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"គ្មានការជ្រើសមេឌៀទេ"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"មិនមានព័ត៌មានទេ"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"កំពុងខាសអេក្រង់"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-kn-rIN/strings.xml b/v7/mediarouter/res/values-kn/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-kn-rIN/strings.xml
rename to v7/mediarouter/res/values-kn/strings.xml
diff --git a/v7/mediarouter/res/values-ky-rKG/strings.xml b/v7/mediarouter/res/values-ky/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ky-rKG/strings.xml
rename to v7/mediarouter/res/values-ky/strings.xml
diff --git a/v7/mediarouter/res/values-lo-rLA/strings.xml b/v7/mediarouter/res/values-lo-rLA/strings.xml
deleted file mode 100644
index e0703e8..0000000
--- a/v7/mediarouter/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ລະບົບ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ອຸປະກອນ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ປຸ່ມ​ຄາ​ສ​ທ໌"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ປຸ່ມສົ່ງສັນຍານ. ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ປຸ່ມສົ່ງສັນຍານ. ກຳລັງເຊື່ອມຕໍ່"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ປຸ່ມສົ່ງສັນຍານ. ເຊື່ອມຕໍ່ແລ້ວ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ຄາ​ສ​ທ໌​ຫາ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ກຳລັງ​ຊອກ​ຫາ​ອຸ​ປະ​ກອນ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ຕັດການເຊື່ອມຕໍ່"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ຢຸດການສົ່ງສັນຍານ"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ປິດ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ຫຼິ້ນ"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ຢຸດຊົ່ວຄາວ"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ຢຸດ"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ຂະຫຍາຍ"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ຫຍໍ້ລົງ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ໜ້າປົກອະລະບໍ້າ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ຕົວປັບລະດັບສຽງ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ບໍ່​ໄດ້​ເລືອກ​ມີ​ເດຍ​ໃດ​ໄວ້"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ບໍ່​ມີ​ຂໍ້​ມູນ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ການສົ່ງພາບໜ້າຈໍ"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-lo/strings.xml b/v7/mediarouter/res/values-lo/strings.xml
new file mode 100644
index 0000000..d50ae66
--- /dev/null
+++ b/v7/mediarouter/res/values-lo/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ລະບົບ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ອຸປະກອນ"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"ປຸ່ມ​ຄາ​ສ​ທ໌"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ປຸ່ມສົ່ງສັນຍານ. ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ປຸ່ມສົ່ງສັນຍານ. ກຳລັງເຊື່ອມຕໍ່"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ປຸ່ມສົ່ງສັນຍານ. ເຊື່ອມຕໍ່ແລ້ວ"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"ສົ່ງສັນຍານຫາ"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"ກຳລັງ​ຊອກ​ຫາ​ອຸ​ປະ​ກອນ"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ຕັດການເຊື່ອມຕໍ່"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ຢຸດການສົ່ງສັນຍານ"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"ປິດ"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"ຫຼິ້ນ"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"ຢຸດຊົ່ວຄາວ"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"ຢຸດ"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ຂະຫຍາຍ"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ຫຍໍ້ລົງ"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"ໜ້າປົກອະລະບໍ້າ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ຕົວປັບລະດັບສຽງ"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ບໍ່​ໄດ້​ເລືອກ​ມີ​ເດຍ​ໃດ​ໄວ້"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ບໍ່​ມີ​ຂໍ້​ມູນ"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ການສົ່ງພາບໜ້າຈໍ"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-mk-rMK/strings.xml b/v7/mediarouter/res/values-mk-rMK/strings.xml
deleted file mode 100644
index ae1bfee..0000000
--- a/v7/mediarouter/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уреди"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Копчето за Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Копче за Cast. Исклучено"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Копче за Cast. Се поврзува"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Копче за Cast. Поврзано"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Емитувај на"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Наоѓање уреди"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Исклучи"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Сопри го емитувањето"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Затвори"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Репродуцирај"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Паузирај"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Сопри"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Прошири"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Собери"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Корица на албум"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Лизгач за јачина на звук"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Не се избрани медиуми"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Нема достапни информации"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Екранот се емитува"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-mk/strings.xml b/v7/mediarouter/res/values-mk/strings.xml
new file mode 100644
index 0000000..d5b9365
--- /dev/null
+++ b/v7/mediarouter/res/values-mk/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уреди"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Копчето за Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Копче за Cast. Исклучено"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Копче за Cast. Се поврзува"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Копче за Cast. Поврзано"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Емитувај на"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Се бараат уреди"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Исклучи"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Сопри го емитувањето"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Затвори"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Репродуцирај"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"Паузирај"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"Сопри"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Прошири"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Собери"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"Корица на албум"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Лизгач за јачина на звук"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Не се избрани медиуми"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Нема достапни информации"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Екранот се емитува"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ml-rIN/strings.xml b/v7/mediarouter/res/values-ml/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ml-rIN/strings.xml
rename to v7/mediarouter/res/values-ml/strings.xml
diff --git a/v7/mediarouter/res/values-mn-rMN/strings.xml b/v7/mediarouter/res/values-mn/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-mn-rMN/strings.xml
rename to v7/mediarouter/res/values-mn/strings.xml
diff --git a/v7/mediarouter/res/values-mr-rIN/strings.xml b/v7/mediarouter/res/values-mr/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-mr-rIN/strings.xml
rename to v7/mediarouter/res/values-mr/strings.xml
diff --git a/v7/mediarouter/res/values-ms-rMY/strings.xml b/v7/mediarouter/res/values-ms/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ms-rMY/strings.xml
rename to v7/mediarouter/res/values-ms/strings.xml
diff --git a/v7/mediarouter/res/values-my-rMM/strings.xml b/v7/mediarouter/res/values-my-rMM/strings.xml
deleted file mode 100644
index eb33498..0000000
--- a/v7/mediarouter/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"စနစ်"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"စက်ပစ္စည်းများ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ကာစ်တ်လုပ်ရန် ခလုတ်"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ကာစ်ခလုတ်။ ချိတ်ဆက်မထားပါ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ကာစ်ခလုတ်။ ချိတ်ဆက်နေသည်"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ကာစ်ခလုတ်။ ချိတ်ဆက်ထားသည်"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"သို့ ကာစ်တ်လုပ်ရန်"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"စက်ပစ္စည်းများ ရှာဖွေခြင်း"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ဆက်သွယ်မှု ဖြတ်ရန်"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ကာစ်လုပ်ခြင်း ရပ်ရန်"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ပိတ်ရန်"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ဖွင့်ရန်"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ခဏရပ်ရန်"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ရပ်ရန်"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ဖြန့်ချရန်၃"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ခေါက်သိမ်းရန်..."</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"အယ်လ်ဘမ်ပုံ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"အသံအတိုးအကျယ်ချိန်သည့် ဆလိုက်ဒါ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"မည်သည့်မီဒီမှ မရွေးချယ်ထားပါ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"အချက်အလက် မရရှိနိုင်ပါ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"တည်းဖြတ်ရေး မျက်နှာပြင်"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-my/strings.xml b/v7/mediarouter/res/values-my/strings.xml
new file mode 100644
index 0000000..fb0074d
--- /dev/null
+++ b/v7/mediarouter/res/values-my/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"စနစ်"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"စက်ပစ္စည်းများ"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"ကာစ်တ်လုပ်ရန် ခလုတ်"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ကာစ်ခလုတ်။ ချိတ်ဆက်မထားပါ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ကာစ်ခလုတ်။ ချိတ်ဆက်နေသည်"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ကာစ်ခလုတ်။ ချိတ်ဆက်ထားသည်"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"ကာစ်လုပ်ရန် စက်"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"စက်ပစ္စည်းများ ရှာဖွေခြင်း"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ဆက်သွယ်မှု ဖြတ်ရန်"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ကာစ်လုပ်ခြင်း ရပ်ရန်"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"ပိတ်ရန်"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"ဖွင့်ရန်"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"ခဏရပ်ရန်"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"ရပ်ရန်"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ဖြန့်ချရန်၃"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ခေါက်သိမ်းရန်..."</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"အယ်လ်ဘမ်ပုံ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"အသံအတိုးအကျယ်ချိန်သည့် ဆလိုက်ဒါ"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"မည်သည့်မီဒီမှ မရွေးချယ်ထားပါ"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"အချက်အလက် မရရှိနိုင်ပါ"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"တည်းဖြတ်ရေး မျက်နှာပြင်"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-nb/strings.xml b/v7/mediarouter/res/values-nb/strings.xml
index d31aa73..7c99442 100644
--- a/v7/mediarouter/res/values-nb/strings.xml
+++ b/v7/mediarouter/res/values-nb/strings.xml
@@ -23,7 +23,7 @@
     <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knappen. Kobler til"</string>
     <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knappen. Tilkoblet"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast til"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Finner enheter"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Søker etter enheter"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Koble fra"</string>
     <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stopp castingen"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"Lukk"</string>
diff --git a/v7/mediarouter/res/values-ne-rNP/strings.xml b/v7/mediarouter/res/values-ne/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ne-rNP/strings.xml
rename to v7/mediarouter/res/values-ne/strings.xml
diff --git a/v7/mediarouter/res/values-pa-rIN/strings.xml b/v7/mediarouter/res/values-pa-rIN/strings.xml
deleted file mode 100644
index e058e18..0000000
--- a/v7/mediarouter/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ਸਿਸਟਮ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ਡਿਵਾਈਸਾਂ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ਕਾਸਟ ਬਟਨ"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ਕਾਸਟ ਬਟਨ। ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ਇਸ ਨਾਲ ਕਾਸਟ ਕਰੋ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ਡਿਵਾਈਸਾਂ ਲੱਭ ਰਿਹਾ ਹੈ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ਪਲੇ ਕਰੋ"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ਰੋਕੋ"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ਵਿਸਤਾਰ ਕਰੋ"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ਐਲਬਮ ਆਰਟ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ਵੌਲਯੂਮ ਸਲਾਈਡਰ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ਕੋਈ ਵੀ ਮੀਡੀਆ ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ਕੋਈ ਜਾਣਕਾਰੀ ਉਪਲਬਧ ਨਹੀਂ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ਸਕ੍ਰੀਨ ਜੋੜ ਰਿਹਾ ਹੈ"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-pa/strings.xml b/v7/mediarouter/res/values-pa/strings.xml
new file mode 100644
index 0000000..f1a1a30
--- /dev/null
+++ b/v7/mediarouter/res/values-pa/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ਸਿਸਟਮ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ਡਿਵਾਈਸਾਂ"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"ਕਾਸਟ ਬਟਨ"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ਕਾਸਟ ਬਟਨ। ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"ਏਥੇ ਕਾਸਟ ਕਰੋ"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"ਡੀਵਾਈਸਾਂ ਨੂੰ ਲੱਭਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"ਬੰਦ ਕਰੋ"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"ਪਲੇ ਕਰੋ"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"ਰੋਕੋ"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"ਬੰਦ ਕਰੋ"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ਵਿਸਤਾਰ ਕਰੋ"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ਬੰਦ ਕਰੋ"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"ਐਲਬਮ ਆਰਟ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ਵੌਲਯੂਮ ਸਲਾਈਡਰ"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ਕੋਈ ਵੀ ਮੀਡੀਆ ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ਕੋਈ ਜਾਣਕਾਰੀ ਉਪਲਬਧ ਨਹੀਂ"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ਸਕ੍ਰੀਨ ਜੋੜ ਰਿਹਾ ਹੈ"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-si-rLK/strings.xml b/v7/mediarouter/res/values-si/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-si-rLK/strings.xml
rename to v7/mediarouter/res/values-si/strings.xml
diff --git a/v7/mediarouter/res/values-sk/strings.xml b/v7/mediarouter/res/values-sk/strings.xml
index 2df58ef..43a769a 100644
--- a/v7/mediarouter/res/values-sk/strings.xml
+++ b/v7/mediarouter/res/values-sk/strings.xml
@@ -23,7 +23,7 @@
     <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tlačidlo prenosu. Pripája sa"</string>
     <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tlačidlo prenosu. Pripojené"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Prenos do"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Vyhľadávanie zariadení"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Hľadajú sa zariadenia"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odpojiť"</string>
     <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zastaviť prenášanie"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"Zavrieť"</string>
diff --git a/v7/mediarouter/res/values-sq-rAL/strings.xml b/v7/mediarouter/res/values-sq-rAL/strings.xml
deleted file mode 100644
index 9f75316..0000000
--- a/v7/mediarouter/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistemi"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Pajisjet"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Butoni i transmetimit"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butoni i transmetimit. Je i shkëputur"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butoni i transmetimit. Po lidhet"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butoni i transmetimit. Je i lidhur"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmeto te"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Gjetja e pajisjeve"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Shkëpute"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Ndalo transmetimin"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Mbyll"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Luaj"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauzë"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Ndalo"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Zgjeroje"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Palose"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Kopertina e albumit"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Rrëshqitësi i volumit"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nuk u zgjodh asnjë media"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nuk jepet asnjë informacion"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Po transmeton ekranin"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-sq/strings.xml b/v7/mediarouter/res/values-sq/strings.xml
new file mode 100644
index 0000000..d3cae18
--- /dev/null
+++ b/v7/mediarouter/res/values-sq/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistemi"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Pajisjet"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Butoni i transmetimit"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butoni i transmetimit. Je i shkëputur"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butoni i transmetimit. Po lidhet"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butoni i transmetimit. Je i lidhur"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Transmeto te"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Po kërkon pajisje"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Shkëpute"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Ndalo transmetimin"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Mbyll"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Luaj"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauzë"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"Ndalo"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Zgjeroje"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Palose"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"Kopertina e albumit"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Rrëshqitësi i volumit"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nuk u zgjodh asnjë media"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nuk jepet asnjë informacion"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Po transmeton ekranin"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ta-rIN/strings.xml b/v7/mediarouter/res/values-ta/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ta-rIN/strings.xml
rename to v7/mediarouter/res/values-ta/strings.xml
diff --git a/v7/mediarouter/res/values-te-rIN/strings.xml b/v7/mediarouter/res/values-te/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-te-rIN/strings.xml
rename to v7/mediarouter/res/values-te/strings.xml
diff --git a/v7/mediarouter/res/values-ur-rPK/strings.xml b/v7/mediarouter/res/values-ur/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ur-rPK/strings.xml
rename to v7/mediarouter/res/values-ur/strings.xml
diff --git a/v7/mediarouter/res/values-uz-rUZ/strings.xml b/v7/mediarouter/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index fae6076..0000000
--- a/v7/mediarouter/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Tizim"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Qurilmalar"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Translatsiya tugmasi"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Translatsiya tugmasi. Uzildi"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Translatsiya tugmasi. Ulanmoqda"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Translatsiya tugmasi. Ulandi"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Quyidagiga translatsiya qilish:"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Qurilmalarni topish"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ulanishni uzish"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Translatsiyani to‘xtatish"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Yopish"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Boshlash"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"To‘xtatib turish"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"To‘xtatish"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Yoyish"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Yig‘ish"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albom muqovasi"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ovoz balandligi slayderi"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Multimedia tanlamagan"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Hech qanday ma’lumot yo‘q"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekranni translatsiya qilish"</string>
-</resources>
diff --git a/v7/mediarouter/res/values-uz/strings.xml b/v7/mediarouter/res/values-uz/strings.xml
new file mode 100644
index 0000000..2f9a8a2
--- /dev/null
+++ b/v7/mediarouter/res/values-uz/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Tizim"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Qurilmalar"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Translatsiya tugmasi"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Translatsiya tugmasi. Uzildi"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Translatsiya tugmasi. Ulanmoqda"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Translatsiya tugmasi. Ulandi"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Quyidagiga translatsiya qilish:"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Qurilmalarni topish"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ulanishni uzish"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Translatsiyani to‘xtatish"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Yopish"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Boshlash"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"To‘xtatib turish"</string>
+    <string name="mr_controller_stop" msgid="735874641921425123">"To‘xtatish"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Yoyish"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Yig‘ish"</string>
+    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albom muqovasi"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Tovush balandligi slayderi"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Multimedia tanlamagan"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Hech qanday ma’lumot yo‘q"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekranni translatsiya qilish"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-vi/strings.xml b/v7/mediarouter/res/values-vi/strings.xml
index 5801c4f..3fbc252 100644
--- a/v7/mediarouter/res/values-vi/strings.xml
+++ b/v7/mediarouter/res/values-vi/strings.xml
@@ -23,7 +23,7 @@
     <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Nút truyền. Đang kết nối"</string>
     <string name="mr_cast_button_connected" msgid="5088427771788648085">"Nút truyền. Đã kết nối"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Truyền tới"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Tìm thiết bị"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Đang tìm thiết bị"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ngắt kết nối"</string>
     <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Dừng truyền"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"Đóng"</string>
diff --git a/v7/mediarouter/res/values/attrs.xml b/v7/mediarouter/res/values/attrs.xml
index 10d1c2c..e9f20a5 100644
--- a/v7/mediarouter/res/values/attrs.xml
+++ b/v7/mediarouter/res/values/attrs.xml
@@ -22,7 +22,7 @@
              that media is playing to the local device only. -->
         <attr name="externalRouteEnabledDrawable" format="reference" />
         <!-- Tint to apply to the media route button -->
-        <attr name="buttonTint" />
+        <attr name="mediaRouteButtonTint" format="color" />
 
         <attr name="android:minWidth" />
         <attr name="android:minHeight" />
diff --git a/v7/mediarouter/src/.readme b/v7/mediarouter/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/mediarouter/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
index 2da193b..d3f7020 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
@@ -22,24 +22,22 @@
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
 import android.support.annotation.NonNull;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.GravityCompat;
 import android.support.v7.media.MediaRouteSelector;
 import android.support.v7.media.MediaRouter;
 import android.support.v7.mediarouter.R;
+import android.support.v7.widget.TooltipCompat;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
+import android.util.SparseArray;
 import android.view.SoundEffectConstants;
 import android.view.View;
-import android.widget.Toast;
 
 /**
  * The media route button allows the user to select routes and to control the
@@ -93,9 +91,11 @@
 
     private boolean mAttachedToWindow;
 
+    private static final SparseArray<Drawable.ConstantState> sRemoteIndicatorCache =
+            new SparseArray<>(2);
+    private RemoteIndicatorLoader mRemoteIndicatorLoader;
     private Drawable mRemoteIndicator;
     private boolean mRemoteActive;
-    private boolean mCheatSheetEnabled;
     private boolean mIsConnecting;
 
     private ColorStateList mButtonTint;
@@ -130,18 +130,28 @@
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.MediaRouteButton, defStyleAttr, 0);
-        mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_buttonTint);
-        setRemoteIndicatorDrawable(a.getDrawable(
-                R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
+        mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_mediaRouteButtonTint);
         mMinWidth = a.getDimensionPixelSize(
                 R.styleable.MediaRouteButton_android_minWidth, 0);
         mMinHeight = a.getDimensionPixelSize(
                 R.styleable.MediaRouteButton_android_minHeight, 0);
+        int remoteIndicatorResId = a.getResourceId(
+                R.styleable.MediaRouteButton_externalRouteEnabledDrawable, 0);
         a.recycle();
 
+        if (remoteIndicatorResId != 0) {
+            Drawable.ConstantState remoteIndicatorState =
+                    sRemoteIndicatorCache.get(remoteIndicatorResId);
+            if (remoteIndicatorState != null) {
+                setRemoteIndicatorDrawable(remoteIndicatorState.newDrawable());
+            } else {
+                mRemoteIndicatorLoader = new RemoteIndicatorLoader(remoteIndicatorResId);
+                mRemoteIndicatorLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+            }
+        }
+
         updateContentDescription();
         setClickable(true);
-        setLongClickable(true);
     }
 
     /**
@@ -280,7 +290,8 @@
      * button when the button is long pressed.
      */
     void setCheatSheetEnabled(boolean enable) {
-        mCheatSheetEnabled = enable;
+        TooltipCompat.setTooltipText(this,
+                enable ? getContext().getString(R.string.mr_button_content_description) : null);
     }
 
     @Override
@@ -294,42 +305,6 @@
     }
 
     @Override
-    public boolean performLongClick() {
-        if (super.performLongClick()) {
-            return true;
-        }
-
-        if (!mCheatSheetEnabled) {
-            return false;
-        }
-
-        final int[] screenPos = new int[2];
-        final Rect displayFrame = new Rect();
-        getLocationOnScreen(screenPos);
-        getWindowVisibleDisplayFrame(displayFrame);
-
-        final Context context = getContext();
-        final int width = getWidth();
-        final int height = getHeight();
-        final int midy = screenPos[1] + height / 2;
-        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-
-        Toast cheatSheet = Toast.makeText(context, R.string.mr_button_content_description,
-                Toast.LENGTH_SHORT);
-        if (midy < displayFrame.height()) {
-            // Show along the top; follow action buttons
-            cheatSheet.setGravity(Gravity.TOP | GravityCompat.END,
-                    screenWidth - screenPos[0] - width / 2, height);
-        } else {
-            // Show along the bottom center
-            cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
-        }
-        cheatSheet.show();
-        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        return true;
-    }
-
-    @Override
     protected int[] onCreateDrawableState(int extraSpace) {
         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
 
@@ -360,22 +335,40 @@
      * Sets a drawable to use as the remote route indicator.
      */
     public void setRemoteIndicatorDrawable(Drawable d) {
+        if (mRemoteIndicatorLoader != null) {
+            mRemoteIndicatorLoader.cancel(false);
+        }
+
         if (mRemoteIndicator != null) {
             mRemoteIndicator.setCallback(null);
             unscheduleDrawable(mRemoteIndicator);
         }
-        if (mButtonTint != null) {
-            d = DrawableCompat.wrap(d.mutate());
-            DrawableCompat.setTintList(d, mButtonTint);
-        }
-        mRemoteIndicator = d;
         if (d != null) {
+            if (mButtonTint != null) {
+                d = DrawableCompat.wrap(d.mutate());
+                DrawableCompat.setTintList(d, mButtonTint);
+            }
             d.setCallback(this);
             d.setState(getDrawableState());
             d.setVisible(getVisibility() == VISIBLE, false);
         }
+        mRemoteIndicator = d;
 
         refreshDrawableState();
+        if (mAttachedToWindow && mRemoteIndicator != null
+                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
+            AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
+            if (mIsConnecting) {
+                if (!curDrawable.isRunning()) {
+                    curDrawable.start();
+                }
+            } else if (mRemoteActive) {
+                if (curDrawable.isRunning()) {
+                    curDrawable.stop();
+                }
+                curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1);
+            }
+        }
     }
 
     @Override
@@ -383,7 +376,7 @@
         return super.verifyDrawable(who) || who == mRemoteIndicator;
     }
 
-    //@Override defined in v11
+    @Override
     public void jumpDrawablesToCurrentState() {
         // We can't call super to handle the background so we do it ourselves.
         //super.jumpDrawablesToCurrentState();
@@ -513,7 +506,8 @@
             setEnabled(mRouter.isRouteAvailable(mSelector,
                     MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
         }
-        if (mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
+        if (mRemoteIndicator != null
+                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
             AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
             if (mAttachedToWindow) {
                 if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) {
@@ -586,4 +580,35 @@
             refreshRoute();
         }
     }
+
+    private final class RemoteIndicatorLoader extends AsyncTask<Void, Void, Drawable> {
+        private final int mResId;
+
+        RemoteIndicatorLoader(int resId) {
+            mResId = resId;
+        }
+
+        @Override
+        protected Drawable doInBackground(Void... params) {
+            return getContext().getResources().getDrawable(mResId);
+        }
+
+        @Override
+        protected void onPostExecute(Drawable remoteIndicator) {
+            cacheAndReset(remoteIndicator);
+            setRemoteIndicatorDrawable(remoteIndicator);
+        }
+
+        @Override
+        protected void onCancelled(Drawable remoteIndicator) {
+            cacheAndReset(remoteIndicator);
+        }
+
+        private void cacheAndReset(Drawable remoteIndicator) {
+            if (remoteIndicator != null) {
+                sRemoteIndicatorCache.put(mResId, remoteIndicator.getConstantState());
+            }
+            mRemoteIndicatorLoader = null;
+        }
+    }
 }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 2e5b983..0ab2eb1 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -92,7 +92,10 @@
     }
 
     public MediaRouteChooserDialog(Context context, int theme) {
-        super(MediaRouterThemeHelper.createThemedContext(context, theme), theme);
+        // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context,
+        // which may override our style settings. Passes our uppermost theme ID to prevent this.
+        super(MediaRouterThemeHelper.createThemedContext(context, theme),
+                theme == 0 ? MediaRouterThemeHelper.createThemeForDialog(context, theme) : theme);
         context = getContext();
 
         mRouter = MediaRouter.getInstance(context);
@@ -187,7 +190,7 @@
         mListView.setAdapter(mAdapter);
         mListView.setOnItemClickListener(mAdapter);
         mListView.setEmptyView(findViewById(android.R.id.empty));
-        mTitleView = (TextView) findViewById(R.id.mr_chooser_title);
+        mTitleView = findViewById(R.id.mr_chooser_title);
 
         updateLayout();
     }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index 4d40610..4b9a17a 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -39,6 +39,7 @@
 import android.support.v4.media.session.MediaControllerCompat;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.media.session.PlaybackStateCompat;
+import android.support.v4.util.ObjectsCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v7.app.OverlayListView.OverlayObject;
 import android.support.v7.graphics.Palette;
@@ -72,6 +73,7 @@
 import android.widget.RelativeLayout;
 import android.widget.SeekBar;
 import android.widget.TextView;
+
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -199,8 +201,12 @@
     }
 
     public MediaRouteControllerDialog(Context context, int theme) {
+        // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context,
+        // which may override our style settings. Passes our uppermost theme ID to prevent this.
         super(MediaRouterThemeHelper.createThemedContext(context,
-                MediaRouterThemeHelper.getAlertDialogResolvedTheme(context, theme)), theme);
+                MediaRouterThemeHelper.getAlertDialogResolvedTheme(context, theme)), theme == 0
+                ? MediaRouterThemeHelper.createThemeForDialog(context, MediaRouterThemeHelper
+                        .getAlertDialogResolvedTheme(context, theme)) : theme);
         mContext = getContext();
 
         mControllerCallback = new MediaControllerCallback();
@@ -334,14 +340,14 @@
 
         ClickListener listener = new ClickListener();
 
-        mExpandableAreaLayout = (FrameLayout) findViewById(R.id.mr_expandable_area);
+        mExpandableAreaLayout = findViewById(R.id.mr_expandable_area);
         mExpandableAreaLayout.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 dismiss();
             }
         });
-        mDialogAreaLayout = (LinearLayout) findViewById(R.id.mr_dialog_area);
+        mDialogAreaLayout = findViewById(R.id.mr_dialog_area);
         mDialogAreaLayout.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -349,21 +355,21 @@
             }
         });
         int color = MediaRouterThemeHelper.getButtonTextColor(mContext);
-        mDisconnectButton = (Button) findViewById(BUTTON_DISCONNECT_RES_ID);
+        mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID);
         mDisconnectButton.setText(R.string.mr_controller_disconnect);
         mDisconnectButton.setTextColor(color);
         mDisconnectButton.setOnClickListener(listener);
 
-        mStopCastingButton = (Button) findViewById(BUTTON_STOP_RES_ID);
+        mStopCastingButton = findViewById(BUTTON_STOP_RES_ID);
         mStopCastingButton.setText(R.string.mr_controller_stop_casting);
         mStopCastingButton.setTextColor(color);
         mStopCastingButton.setOnClickListener(listener);
 
-        mRouteNameTextView = (TextView) findViewById(R.id.mr_name);
-        mCloseButton = (ImageButton) findViewById(R.id.mr_close);
+        mRouteNameTextView = findViewById(R.id.mr_name);
+        mCloseButton = findViewById(R.id.mr_close);
         mCloseButton.setOnClickListener(listener);
-        mCustomControlLayout = (FrameLayout) findViewById(R.id.mr_custom_control);
-        mDefaultControlLayout = (FrameLayout) findViewById(R.id.mr_default_control);
+        mCustomControlLayout = findViewById(R.id.mr_custom_control);
+        mDefaultControlLayout = findViewById(R.id.mr_default_control);
 
         // Start the session activity when a content item (album art, title or subtitle) is clicked.
         View.OnClickListener onClickListener = new View.OnClickListener() {
@@ -382,27 +388,27 @@
                 }
             }
         };
-        mArtView = (ImageView) findViewById(R.id.mr_art);
+        mArtView = findViewById(R.id.mr_art);
         mArtView.setOnClickListener(onClickListener);
         findViewById(R.id.mr_control_title_container).setOnClickListener(onClickListener);
 
-        mMediaMainControlLayout = (LinearLayout) findViewById(R.id.mr_media_main_control);
+        mMediaMainControlLayout = findViewById(R.id.mr_media_main_control);
         mDividerView = findViewById(R.id.mr_control_divider);
 
-        mPlaybackControlLayout = (RelativeLayout) findViewById(R.id.mr_playback_control);
-        mTitleView = (TextView) findViewById(R.id.mr_control_title);
-        mSubtitleView = (TextView) findViewById(R.id.mr_control_subtitle);
-        mPlaybackControlButton = (ImageButton) findViewById(R.id.mr_control_playback_ctrl);
+        mPlaybackControlLayout = findViewById(R.id.mr_playback_control);
+        mTitleView = findViewById(R.id.mr_control_title);
+        mSubtitleView = findViewById(R.id.mr_control_subtitle);
+        mPlaybackControlButton = findViewById(R.id.mr_control_playback_ctrl);
         mPlaybackControlButton.setOnClickListener(listener);
 
-        mVolumeControlLayout = (LinearLayout) findViewById(R.id.mr_volume_control);
+        mVolumeControlLayout = findViewById(R.id.mr_volume_control);
         mVolumeControlLayout.setVisibility(View.GONE);
-        mVolumeSlider = (SeekBar) findViewById(R.id.mr_volume_slider);
+        mVolumeSlider = findViewById(R.id.mr_volume_slider);
         mVolumeSlider.setTag(mRoute);
         mVolumeChangeListener = new VolumeChangeListener();
         mVolumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
 
-        mVolumeGroupList = (OverlayListView) findViewById(R.id.mr_volume_group_list);
+        mVolumeGroupList = findViewById(R.id.mr_volume_group_list);
         mGroupMemberRoutes = new ArrayList<MediaRouter.RouteInfo>();
         mVolumeGroupAdapter = new VolumeGroupAdapter(mVolumeGroupList.getContext(),
                 mGroupMemberRoutes);
@@ -417,7 +423,7 @@
         mVolumeSliderMap.put(mRoute, mVolumeSlider);
 
         mGroupExpandCollapseButton =
-                (MediaRouteExpandCollapseButton) findViewById(R.id.mr_group_expand_collapse);
+                findViewById(R.id.mr_group_expand_collapse);
         mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -1278,6 +1284,11 @@
         }
 
         @Override
+        public boolean isEnabled(int position) {
+            return false;
+        }
+
+        @Override
         public View getView(final int position, View convertView, ViewGroup parent) {
             View v = convertView;
             if (v == null) {
@@ -1438,7 +1449,8 @@
         @Override
         protected void onPostExecute(Bitmap art) {
             mFetchArtTask = null;
-            if (mArtIconBitmap != mIconBitmap || mArtIconUri != mIconUri) {
+            if (!ObjectsCompat.equals(mArtIconBitmap, mIconBitmap)
+                    || !ObjectsCompat.equals(mArtIconUri, mIconUri)) {
                 mArtIconBitmap = mIconBitmap;
                 mArtIconLoadedBitmap = art;
                 mArtIconUri = mIconUri;
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
index a7a0dd3..4278028 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
@@ -20,7 +20,6 @@
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.support.v7.mediarouter.R;
 import android.support.v7.widget.AppCompatSeekBar;
 import android.util.AttributeSet;
 import android.util.Log;
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index 23f3bad..9ef218e 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -54,29 +54,21 @@
      *              {@code 0} to use the parent {@code context}'s default theme.
      * @return The themed context.
      */
-    public static Context createThemedContext(Context context, int style) {
+    static Context createThemedContext(Context context, int style) {
         // First, apply dialog property overlay.
+        Context themedContext =
+                new ContextThemeWrapper(context, getStyledRouterThemeId(context, style));
+        int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme);
+        return customizedThemeId == 0 ? themedContext
+                : new ContextThemeWrapper(themedContext, customizedThemeId);
+    }
 
-        int theme;
-        if (isLightTheme(context)) {
-            if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-                theme = R.style.Theme_MediaRouter_Light;
-            } else {
-                theme = R.style.Theme_MediaRouter_Light_DarkControlPanel;
-            }
-        } else {
-            if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-                theme = R.style.Theme_MediaRouter_LightControlPanel;
-            } else {
-                theme = R.style.Theme_MediaRouter;
-            }
-        }
-        int mediaRouteThemeResId = getThemeResource(context, R.attr.mediaRouteTheme);
-        Context themedContext = new ContextThemeWrapper(context, theme);
-        if (mediaRouteThemeResId != 0) {
-            themedContext = new ContextThemeWrapper(themedContext, mediaRouteThemeResId);
-        }
-        return themedContext;
+    /**
+     * Creates the theme resource ID intended to be used by dialogs.
+     */
+    static int createThemeForDialog(Context context, int style) {
+        int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme);
+        return customizedThemeId != 0 ? customizedThemeId : getStyledRouterThemeId(context, style);
     }
 
     public static int getThemeResource(Context context, int attr) {
@@ -180,4 +172,22 @@
         }
         return value.data;
     }
+
+    private static int getStyledRouterThemeId(Context context, int style) {
+        int themeId;
+        if (isLightTheme(context)) {
+            if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
+                themeId = R.style.Theme_MediaRouter_Light;
+            } else {
+                themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel;
+            }
+        } else {
+            if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
+                themeId = R.style.Theme_MediaRouter_LightControlPanel;
+            } else {
+                themeId = R.style.Theme_MediaRouter;
+            }
+        }
+        return themeId;
+    }
 }
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/MediaRouteProvider.java
index eee05cc..99bcf21 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouteProvider.java
@@ -26,6 +26,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
+import android.support.v4.util.ObjectsCompat;
 import android.support.v7.media.MediaRouter.ControlRequestCallback;
 
 /**
@@ -150,8 +151,7 @@
     public final void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
         MediaRouter.checkCallingThread();
 
-        if (mDiscoveryRequest == request
-                || (mDiscoveryRequest != null && mDiscoveryRequest.equals(request))) {
+        if (ObjectsCompat.equals(mDiscoveryRequest, request)) {
             return;
         }
 
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java b/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java
index ec2f7ba..ac3ae5a 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java
@@ -15,7 +15,6 @@
  */
 package android.support.v7.media;
 
-import android.content.IntentFilter;
 import android.os.Bundle;
 
 import java.util.ArrayList;
@@ -31,13 +30,12 @@
  * </p>
  */
 public final class MediaRouteProviderDescriptor {
-    static final String KEY_ROUTES = "routes";
+    private static final String KEY_ROUTES = "routes";
 
-    final Bundle mBundle;
-    List<MediaRouteDescriptor> mRoutes;
+    private final Bundle mBundle;
+    private List<MediaRouteDescriptor> mRoutes;
 
-    MediaRouteProviderDescriptor(Bundle bundle,
-            List<MediaRouteDescriptor> routes) {
+    private MediaRouteProviderDescriptor(Bundle bundle, List<MediaRouteDescriptor> routes) {
         mBundle = bundle;
         mRoutes = routes;
     }
@@ -50,7 +48,7 @@
         return mRoutes;
     }
 
-    void ensureRoutes() {
+    private void ensureRoutes() {
         if (mRoutes == null) {
             ArrayList<Bundle> routeBundles = mBundle.<Bundle>getParcelableArrayList(KEY_ROUTES);
             if (routeBundles == null || routeBundles.isEmpty()) {
@@ -180,6 +178,19 @@
         }
 
         /**
+         * Sets the list of routes.
+         */
+        Builder setRoutes(Collection<MediaRouteDescriptor> routes) {
+            if (routes == null || routes.isEmpty()) {
+                mRoutes = null;
+                mBundle.remove(KEY_ROUTES);
+            } else {
+                mRoutes = new ArrayList<>(routes);
+            }
+            return this;
+        }
+
+        /**
          * Builds the {@link MediaRouteProviderDescriptor media route provider descriptor}.
          */
         public MediaRouteProviderDescriptor build() {
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderService.java b/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderService.java
index 9ddde89..faa211d 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderService.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderService.java
@@ -55,12 +55,13 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.util.ObjectsCompat;
 import android.util.Log;
 import android.util.SparseArray;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Base class for media route provider services.
@@ -191,7 +192,8 @@
                         MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor();
                         sendReply(messenger, SERVICE_MSG_REGISTERED,
                                 requestId, SERVICE_VERSION_CURRENT,
-                                createDescriptorBundleForClient(descriptor, client), null);
+                                createDescriptorBundleForClientVersion(descriptor,
+                                        client.mVersion), null);
                     }
                     return true;
                 }
@@ -412,32 +414,29 @@
         for (int i = 0; i < count; i++) {
             ClientRecord client = mClients.get(i);
             sendReply(client.mMessenger, SERVICE_MSG_DESCRIPTOR_CHANGED, 0, 0,
-                    createDescriptorBundleForClient(descriptor, client), null);
+                    createDescriptorBundleForClientVersion(descriptor, client.mVersion), null);
             if (DEBUG) {
                 Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor);
             }
         }
     }
 
-    private Bundle createDescriptorBundleForClient(MediaRouteProviderDescriptor descriptor,
-            ClientRecord client) {
+    @VisibleForTesting
+    static Bundle createDescriptorBundleForClientVersion(MediaRouteProviderDescriptor descriptor,
+            int clientVersion) {
         if (descriptor == null) {
             return null;
         }
-        List<MediaRouteDescriptor> routes = descriptor.getRoutes();
-        for (int i = routes.size() - 1; i >= 0; i--) {
-            if (client.mVersion < routes.get(i).getMinClientVersion()
-                    || client.mVersion > routes.get(i).getMaxClientVersion()) {
-                routes.remove(i);
+        MediaRouteProviderDescriptor.Builder builder =
+                new MediaRouteProviderDescriptor.Builder(descriptor);
+        builder.setRoutes(null);
+        for (MediaRouteDescriptor route : descriptor.getRoutes()) {
+            if (clientVersion >= route.getMinClientVersion()
+                    && clientVersion <= route.getMaxClientVersion()) {
+                builder.addRoute(route);
             }
         }
-
-        // Keep the values of the bundle from descriptor excepts routes values.
-        Bundle bundle = descriptor.asBundle();
-        bundle.remove(MediaRouteProviderDescriptor.KEY_ROUTES);
-        return new MediaRouteProviderDescriptor.Builder(
-                MediaRouteProviderDescriptor.fromBundle(bundle))
-                .addRoutes(routes).build().asBundle();
+        return builder.build().asBundle();
     }
 
     boolean updateCompositeDiscoveryRequest() {
@@ -463,9 +462,7 @@
         if (selectorBuilder != null) {
             composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan);
         }
-        if (mCompositeDiscoveryRequest != composite
-                && (mCompositeDiscoveryRequest == null
-                        || !mCompositeDiscoveryRequest.equals(composite))) {
+        if (!ObjectsCompat.equals(mCompositeDiscoveryRequest, composite)) {
             mCompositeDiscoveryRequest = composite;
             mProvider.setDiscoveryRequest(composite);
             return true;
@@ -615,8 +612,7 @@
         }
 
         public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-            if (mDiscoveryRequest != request
-                    && (mDiscoveryRequest == null || !mDiscoveryRequest.equals(request))) {
+            if (!ObjectsCompat.equals(mDiscoveryRequest, request)) {
                 mDiscoveryRequest = request;
                 return updateCompositeDiscoveryRequest();
             }
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index 0d85b3b..5bc0bb4 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -229,7 +229,7 @@
      */
     public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1;
 
-    MediaRouter(Context context) {
+    private MediaRouter(Context context) {
         mContext = context;
     }
 
@@ -1294,6 +1294,15 @@
                     && !supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
         }
 
+        /**
+         * Returns {@code true} if the route is selectable.
+         */
+        boolean isSelectable() {
+            // This tests whether the route is still valid and enabled.
+            // The route descriptor field is set to null when the route is removed.
+            return mDescriptor != null && mEnabled;
+        }
+
         private static boolean isSystemMediaRouteProvider(MediaRouter.RouteInfo route) {
             return TextUtils.equals(route.getProviderInstance().getMetadata().getPackageName(),
                     SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME);
@@ -1973,10 +1982,11 @@
             // the framework media router.  This one is special and receives
             // synchronization messages from the media router.
             mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
-            addProvider(mSystemProvider);
         }
 
         public void start() {
+            addProvider(mSystemProvider);
+
             // Start watching for routes published by registered media route
             // provider services.
             mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
@@ -2061,11 +2071,11 @@
             return mRoutes;
         }
 
-        public List<ProviderInfo> getProviders() {
+        List<ProviderInfo> getProviders() {
             return mProviders;
         }
 
-        public RouteInfo getDefaultRoute() {
+        @NonNull RouteInfo getDefaultRoute() {
             if (mDefaultRoute == null) {
                 // This should never happen once the media router has been fully
                 // initialized but it is good to check for the error in case there
@@ -2076,11 +2086,11 @@
             return mDefaultRoute;
         }
 
-        public RouteInfo getBluetoothRoute() {
+        RouteInfo getBluetoothRoute() {
             return mBluetoothRoute;
         }
 
-        public RouteInfo getSelectedRoute() {
+        @NonNull RouteInfo getSelectedRoute() {
             if (mSelectedRoute == null) {
                 // This should never happen once the media router has been fully
                 // initialized but it is good to check for the error in case there
@@ -2091,11 +2101,11 @@
             return mSelectedRoute;
         }
 
-        public void selectRoute(RouteInfo route) {
+        void selectRoute(@NonNull RouteInfo route) {
             selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED);
         }
 
-        public void selectRoute(RouteInfo route, int unselectReason) {
+        void selectRoute(@NonNull RouteInfo route, int unselectReason) {
             if (!mRoutes.contains(route)) {
                 Log.w(TAG, "Ignoring attempt to select removed route: " + route);
                 return;
@@ -2289,7 +2299,7 @@
                                 mRoutes.add(route);
                                 // 2. Create the route's contents.
                                 if (isGroup) {
-                                    addedGroups.add(new Pair(route, routeDescriptor));
+                                    addedGroups.add(new Pair<>(route, routeDescriptor));
                                 } else {
                                     route.maybeUpdateDescriptor(routeDescriptor);
                                     // 3. Notify clients about addition.
@@ -2309,7 +2319,7 @@
                                         sourceIndex, targetIndex++);
                                 // 2. Update the route's contents.
                                 if (route instanceof RouteGroup) {
-                                    updatedGroups.add(new Pair(route, routeDescriptor));
+                                    updatedGroups.add(new Pair<>(route, routeDescriptor));
                                 } else {
                                     // 3. Notify clients about changes.
                                     if (updateRouteDescriptorAndNotify(route, routeDescriptor)
@@ -2413,7 +2423,7 @@
             String componentName = provider.getComponentName().flattenToShortString();
             String uniqueId = componentName + ":" + routeDescriptorId;
             if (findRouteByUniqueId(uniqueId) < 0) {
-                mUniqueIdMap.put(new Pair(componentName, routeDescriptorId), uniqueId);
+                mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), uniqueId);
                 return uniqueId;
             }
             Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName
@@ -2421,7 +2431,7 @@
             for (int i = 2; ; i++) {
                 String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i);
                 if (findRouteByUniqueId(newUniqueId) < 0) {
-                    mUniqueIdMap.put(new Pair(componentName, routeDescriptorId), newUniqueId);
+                    mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), newUniqueId);
                     return newUniqueId;
                 }
             }
@@ -2439,19 +2449,19 @@
 
         private String getUniqueId(ProviderInfo provider, String routeDescriptorId) {
             String componentName = provider.getComponentName().flattenToShortString();
-            return mUniqueIdMap.get(new Pair(componentName, routeDescriptorId));
+            return mUniqueIdMap.get(new Pair<>(componentName, routeDescriptorId));
         }
 
         private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) {
             // Update default route.
-            if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) {
+            if (mDefaultRoute != null && !mDefaultRoute.isSelectable()) {
                 Log.i(TAG, "Clearing the default route because it "
                         + "is no longer selectable: " + mDefaultRoute);
                 mDefaultRoute = null;
             }
             if (mDefaultRoute == null && !mRoutes.isEmpty()) {
                 for (RouteInfo route : mRoutes) {
-                    if (isSystemDefaultRoute(route) && isRouteSelectable(route)) {
+                    if (isSystemDefaultRoute(route) && route.isSelectable()) {
                         mDefaultRoute = route;
                         Log.i(TAG, "Found default route: " + mDefaultRoute);
                         break;
@@ -2460,14 +2470,14 @@
             }
 
             // Update bluetooth route.
-            if (mBluetoothRoute != null && !isRouteSelectable(mBluetoothRoute)) {
+            if (mBluetoothRoute != null && !mBluetoothRoute.isSelectable()) {
                 Log.i(TAG, "Clearing the bluetooth route because it "
                         + "is no longer selectable: " + mBluetoothRoute);
                 mBluetoothRoute = null;
             }
             if (mBluetoothRoute == null && !mRoutes.isEmpty()) {
                 for (RouteInfo route : mRoutes) {
-                    if (isSystemLiveAudioOnlyRoute(route) && isRouteSelectable(route)) {
+                    if (isSystemLiveAudioOnlyRoute(route) && route.isSelectable()) {
                         mBluetoothRoute = route;
                         Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute);
                         break;
@@ -2476,16 +2486,9 @@
             }
 
             // Update selected route.
-            if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) {
+            if (mSelectedRoute == null || !mSelectedRoute.isSelectable()) {
                 Log.i(TAG, "Unselecting the current route because it "
                         + "is no longer selectable: " + mSelectedRoute);
-                setSelectedRouteInternal(null,
-                        MediaRouter.UNSELECT_REASON_UNKNOWN);
-            }
-            if (mSelectedRoute == null) {
-                // Choose a new route.
-                // This will have the side-effect of updating the playback info when
-                // the new route is selected.
                 setSelectedRouteInternal(chooseFallbackRoute(),
                         MediaRouter.UNSELECT_REASON_UNKNOWN);
             } else if (selectedRouteDescriptorChanged) {
@@ -2494,7 +2497,7 @@
                 if (mSelectedRoute instanceof RouteGroup) {
                     List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes();
                     // Build a set of descriptor IDs for the new route group.
-                    Set idSet = new HashSet<String>();
+                    Set<String> idSet = new HashSet<>();
                     for (RouteInfo route : routes) {
                         idSet.add(route.mDescriptorId);
                     }
@@ -2534,7 +2537,7 @@
             for (RouteInfo route : mRoutes) {
                 if (route != mDefaultRoute
                         && isSystemLiveAudioOnlyRoute(route)
-                        && isRouteSelectable(route)) {
+                        && route.isSelectable()) {
                     return route;
                 }
             }
@@ -2547,19 +2550,32 @@
                     && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
         }
 
-        private boolean isRouteSelectable(RouteInfo route) {
-            // This tests whether the route is still valid and enabled.
-            // The route descriptor field is set to null when the route is removed.
-            return route.mDescriptor != null && route.mEnabled;
-        }
-
         private boolean isSystemDefaultRoute(RouteInfo route) {
             return route.getProviderInstance() == mSystemProvider
                     && route.mDescriptorId.equals(
                             SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
         }
 
-        private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
+        private void setSelectedRouteInternal(@NonNull RouteInfo route, int unselectReason) {
+            // TODO: Remove the following logging when no longer needed.
+            if (sGlobal == null || mBluetoothRoute != null && route.isDefault()) {
+                final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+                StringBuffer sb = new StringBuffer();
+                // callStack[3] is the caller of this method.
+                for (int i = 3; i < callStack.length; i++) {
+                    StackTraceElement caller = callStack[i];
+                    sb.append(caller.getClassName() + "." + caller.getMethodName()
+                            + ":" + caller.getLineNumber()).append("  ");
+                }
+                if (sGlobal == null) {
+                    Log.w(TAG, "setSelectedRouteInternal is called while sGlobal is null: pkgName="
+                            + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
+                } else {
+                    Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
+                            + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
+                }
+            }
+
             if (mSelectedRoute != route) {
                 if (mSelectedRoute != null) {
                     if (DEBUG) {
@@ -2583,28 +2599,25 @@
                 }
 
                 mSelectedRoute = route;
+                mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
+                        route.mDescriptorId);
+                if (mSelectedRouteController != null) {
+                    mSelectedRouteController.onSelect();
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "Route selected: " + mSelectedRoute);
+                }
+                mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
 
-                if (mSelectedRoute != null) {
-                    mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
-                            route.mDescriptorId);
-                    if (mSelectedRouteController != null) {
-                        mSelectedRouteController.onSelect();
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "Route selected: " + mSelectedRoute);
-                    }
-                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
-
-                    if (mSelectedRoute instanceof RouteGroup) {
-                        List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes();
-                        mRouteControllerMap.clear();
-                        for (RouteInfo r : routes) {
-                            RouteController controller =
-                                    r.getProviderInstance().onCreateRouteController(
-                                            r.mDescriptorId, mSelectedRoute.mDescriptorId);
-                            controller.onSelect();
-                            mRouteControllerMap.put(r.mDescriptorId, controller);
-                        }
+                if (mSelectedRoute instanceof RouteGroup) {
+                    List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes();
+                    mRouteControllerMap.clear();
+                    for (RouteInfo r : routes) {
+                        RouteController controller =
+                                r.getProviderInstance().onCreateRouteController(
+                                        r.mDescriptorId, mSelectedRoute.mDescriptorId);
+                        controller.onSelect();
+                        mRouteControllerMap.put(r.mDescriptorId, controller);
                     }
                 }
 
diff --git a/v7/mediarouter/src/android/support/v7/media/RegisteredMediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/RegisteredMediaRouteProvider.java
index ca2d486..1481ce6 100644
--- a/v7/mediarouter/src/android/support/v7/media/RegisteredMediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/RegisteredMediaRouteProvider.java
@@ -641,7 +641,7 @@
         }
     }
 
-    private final class PrivateHandler extends Handler {
+    private static final class PrivateHandler extends Handler {
         PrivateHandler() {
         }
     }
diff --git a/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java b/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java
index c5dede8..085d6ff 100644
--- a/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java
+++ b/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 
 import java.lang.ref.WeakReference;
 
@@ -118,6 +119,7 @@
      * other API available to do so in this platform version.  The UserRouteInfo itself
      * is not attached to the MediaRouter so it is transparent to the user.
      */
+    @RequiresApi(16)
     static class JellybeanImpl extends RemoteControlClientCompat {
         private final Object mRouterObj;
         private final Object mUserRouteCategoryObj;
diff --git a/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java b/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java
index 1dc556b..fb05ac7 100644
--- a/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java
+++ b/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java
@@ -22,10 +22,9 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.v4.util.ObjectsCompat;
 import android.util.Log;
 
-import java.util.Iterator;
-
 /**
  * A helper class for playing media on remote routes using the remote playback protocol
  * defined by {@link MediaControlIntent}.
@@ -204,8 +203,7 @@
      * @param sessionId The new session id, or null if none.
      */
     public void setSessionId(String sessionId) {
-        if (mSessionId != sessionId
-                && (mSessionId == null || !mSessionId.equals(sessionId))) {
+        if (!ObjectsCompat.equals(mSessionId, sessionId)) {
             if (DEBUG) {
                 Log.d(TAG, "Session id is now: " + sessionId);
             }
diff --git a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
index 0833be3..d84a069 100644
--- a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v7.mediarouter.R;
 import android.view.Display;
 
@@ -96,7 +97,7 @@
      * Callbacks into the media router to synchronize state with the framework media router.
      */
     public interface SyncCallback {
-        public MediaRouter.RouteInfo getSystemRouteByDescriptorId(String id);
+        MediaRouter.RouteInfo getSystemRouteByDescriptorId(String id);
     }
 
     protected Object getDefaultRoute() {
@@ -212,6 +213,7 @@
     /**
      * Jellybean implementation.
      */
+    @RequiresApi(16)
     static class JellybeanImpl extends SystemMediaRouteProvider
             implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback {
         private static final ArrayList<IntentFilter> LIVE_AUDIO_CONTROL_FILTERS;
@@ -709,7 +711,7 @@
             }
         }
 
-        protected final class SystemRouteController extends RouteController {
+        protected static final class SystemRouteController extends RouteController {
             private final Object mRouteObj;
 
             public SystemRouteController(Object routeObj) {
@@ -731,6 +733,7 @@
     /**
      * Jellybean MR1 implementation.
      */
+    @RequiresApi(17)
     private static class JellybeanMr1Impl extends JellybeanImpl
             implements MediaRouterJellybeanMr1.Callback {
         private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround;
@@ -807,6 +810,7 @@
     /**
      * Jellybean MR2 implementation.
      */
+    @RequiresApi(18)
     private static class JellybeanMr2Impl extends JellybeanMr1Impl {
         public JellybeanMr2Impl(Context context, SyncCallback syncCallback) {
             super(context, syncCallback);
@@ -864,6 +868,7 @@
     /**
      * Api24 implementation.
      */
+    @RequiresApi(24)
     private static class Api24Impl extends JellybeanMr2Impl {
         public Api24Impl(Context context, SyncCallback syncCallback) {
             super(context, syncCallback);
diff --git a/v7/mediarouter/tests/AndroidManifest.xml b/v7/mediarouter/tests/AndroidManifest.xml
index 372a813..e1c6443 100644
--- a/v7/mediarouter/tests/AndroidManifest.xml
+++ b/v7/mediarouter/tests/AndroidManifest.xml
@@ -15,27 +15,20 @@
    limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v7.mediarouter.test">
-
-    <uses-sdk
-            android:minSdkVersion="9"
-            android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                      android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application android:supportsRtl="true">
+        <activity
+            android:name="android.support.v7.app.MediaRouteChooserDialogTestActivity"
+            android:label="MediaRouteChooserDialogTestActivity"
+            android:theme="@style/Theme.AppCompat" />
 
-        <uses-library android:name="android.test.runner"/>
         <receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
             <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
+                <action android:name="android.intent.action.MEDIA_BUTTON"/>
             </intent-filter>
         </receiver>
     </application>
 
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.v7.mediarouter.test"/>
-
 </manifest>
diff --git a/v7/mediarouter/tests/res/layout/mr_chooser_dialog_activity.xml b/v7/mediarouter/tests/res/layout/mr_chooser_dialog_activity.xml
new file mode 100644
index 0000000..484bd23
--- /dev/null
+++ b/v7/mediarouter/tests/res/layout/mr_chooser_dialog_activity.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</FrameLayout>
+
diff --git a/v7/mediarouter/tests/res/values/themes.xml b/v7/mediarouter/tests/res/values/themes.xml
new file mode 100644
index 0000000..31ae4ce
--- /dev/null
+++ b/v7/mediarouter/tests/res/values/themes.xml
@@ -0,0 +1,23 @@
+<?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="HasWindowTitle">
+        <item name="windowNoTitle">false</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java
index 7a21cdb..415ec1f 100644
--- a/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java
+++ b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java
@@ -16,28 +16,76 @@
 
 package android.support.v7.app;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.media.MediaRouter.RouteInfo;
 import android.support.v7.media.TestUtils;
+import android.support.v7.mediarouter.test.R;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-@SmallTest
 public class MediaRouteChooserDialogTest {
+
+    @Rule
+    public final ActivityTestRule<MediaRouteChooserDialogTestActivity> mActivityTestRule;
     private MediaRouteChooserDialog.RouteComparator mComparator;
 
+    public MediaRouteChooserDialogTest() {
+        mActivityTestRule = new ActivityTestRule<>(MediaRouteChooserDialogTestActivity.class);
+    }
+
     @Before
     public void setup() {
         mComparator = new MediaRouteChooserDialog.RouteComparator();
     }
 
     @Test
+    @SmallTest
+    @UiThreadTest
+    public void testWindowNoTitle() {
+        final Context context = mActivityTestRule.getActivity();
+        TypedArray typedArray;
+
+        // Without any base theme or customized theme
+        MediaRouteChooserDialog dialog = new MediaRouteChooserDialog(context);
+        typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+        assertTrue(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+        typedArray.recycle();
+
+        // No base theme, with a customized theme (has window title)
+        dialog = new MediaRouteChooserDialog(context, R.style.HasWindowTitle);
+        typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+        assertFalse(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+        typedArray.recycle();
+
+        // With base theme (has window title), no customized theme
+        context.setTheme(R.style.HasWindowTitle);
+        dialog = new MediaRouteChooserDialog(context);
+        typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+        assertTrue(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+        typedArray.recycle();
+
+        // With base theme and a customized theme (both has window title)
+        dialog = new MediaRouteChooserDialog(context, R.style.HasWindowTitle);
+        typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+        assertFalse(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+        typedArray.recycle();
+
+        context.setTheme(0);
+    }
+
+    @Test
     public void testRouteComparatorWithSameRouteName() {
         RouteInfo routeInfo1 = TestUtils.createRouteInfo("ROUTE_ID_1", "ROUTE_NAME_1");
         RouteInfo routeInfo2 = TestUtils.createRouteInfo("ROUTE_ID_2", "ROUTE_NAME_1");
diff --git a/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTestActivity.java b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTestActivity.java
new file mode 100644
index 0000000..39e4d7c
--- /dev/null
+++ b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTestActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.app;
+
+import android.os.Bundle;
+import android.support.v7.mediarouter.test.R;
+
+public class MediaRouteChooserDialogTestActivity extends AppCompatActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.mr_chooser_dialog_activity);
+    }
+}
diff --git a/v7/mediarouter/tests/src/android/support/v7/media/MediaRouteProviderTest.java b/v7/mediarouter/tests/src/android/support/v7/media/MediaRouteProviderTest.java
new file mode 100644
index 0000000..ff4409a
--- /dev/null
+++ b/v7/mediarouter/tests/src/android/support/v7/media/MediaRouteProviderTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.media;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test {@link MediaRouteProvider} and its related classes.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MediaRouteProviderTest {
+    private static final String FAKE_MEDIA_ROUTE_ID_1 = "fakeMediaRouteId1";
+    private static final String FAKE_MEDIA_ROUTE_ID_2 = "fakeMediaRouteId2";
+    private static final String FAKE_MEDIA_ROUTE_ID_3 = "fakeMediaRouteId3";
+    private static final String FAKE_MEDIA_ROUTE_ID_4 = "fakeMediaRouteId4";
+    private static final String FAKE_MEDIA_ROUTE_NAME_1 = "fakeMediaRouteName1";
+    private static final String FAKE_MEDIA_ROUTE_NAME_2 = "fakeMediaRouteName2";
+    private static final String FAKE_MEDIA_ROUTE_NAME_3 = "fakeMediaRouteName3";
+    private static final String FAKE_MEDIA_ROUTE_NAME_4 = "fakeMediaRouteName4";
+
+    @Test
+    @SmallTest
+    public void testDescriptorBuilder() {
+        // Tests for empty descriptor
+        MediaRouteProviderDescriptor.Builder builder = new MediaRouteProviderDescriptor.Builder();
+        MediaRouteProviderDescriptor descriptor = builder.build();
+        assertTrue(descriptor.getRoutes().isEmpty());
+
+        // Tests for addRoute()
+        builder.addRoute(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_1,
+                FAKE_MEDIA_ROUTE_NAME_1).build());
+        builder.addRoute(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_2,
+                FAKE_MEDIA_ROUTE_NAME_2).build());
+        descriptor = builder.build();
+        List<MediaRouteDescriptor> routes = descriptor.getRoutes();
+        assertEquals(2, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_1, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_1, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_2, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_2, routes.get(1).getName());
+
+        // Tests for addRoutes()
+        List<MediaRouteDescriptor> otherRoutes = new ArrayList<>();
+        otherRoutes.add(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_3,
+                FAKE_MEDIA_ROUTE_NAME_3).build());
+        otherRoutes.add(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_4,
+                FAKE_MEDIA_ROUTE_NAME_4).build());
+        builder.addRoutes(otherRoutes);
+        descriptor = builder.build();
+        routes = descriptor.getRoutes();
+        assertEquals(4, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_1, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_1, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_2, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_2, routes.get(1).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_3, routes.get(2).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_3, routes.get(2).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_4, routes.get(3).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_4, routes.get(3).getName());
+
+        // Tests for setRoutes()
+        builder.setRoutes(otherRoutes);
+        descriptor = builder.build();
+        routes = descriptor.getRoutes();
+        assertEquals(2, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_3, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_3, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_4, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_4, routes.get(1).getName());
+
+        // Tests setRoutes() for side effects
+        otherRoutes.add(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_1,
+                FAKE_MEDIA_ROUTE_NAME_1).build());
+        descriptor = builder.build();
+        routes = descriptor.getRoutes();
+        assertEquals(2, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_3, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_3, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_4, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_4, routes.get(1).getName());
+
+        // Tests setRoutes against null
+        builder.setRoutes(null);
+        descriptor = builder.build();
+        assertTrue(descriptor.getRoutes().isEmpty());
+    }
+
+    @Test
+    @SmallTest
+    public void testCreateDescriptorBundleForClient() {
+        MediaRouteProviderDescriptor.Builder builder = new MediaRouteProviderDescriptor.Builder();
+        builder.addRoute(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_1,
+                FAKE_MEDIA_ROUTE_NAME_1).setMaxClientVersion(15).setMinClientVersion(10).build());
+        builder.addRoute(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_2,
+                FAKE_MEDIA_ROUTE_NAME_2).setMaxClientVersion(18).setMinClientVersion(11).build());
+        builder.addRoute(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_3,
+                FAKE_MEDIA_ROUTE_NAME_3).setMaxClientVersion(25).setMinClientVersion(16).build());
+        builder.addRoute(new MediaRouteDescriptor.Builder(FAKE_MEDIA_ROUTE_ID_4,
+                FAKE_MEDIA_ROUTE_NAME_4).setMaxClientVersion(12).setMinClientVersion(4).build());
+        MediaRouteProviderDescriptor descriptor = builder.build();
+
+        Bundle bundle = MediaRouteProviderService
+                .createDescriptorBundleForClientVersion(descriptor, 3);
+        MediaRouteProviderDescriptor resultDescriptor =
+                MediaRouteProviderDescriptor.fromBundle(bundle);
+        assertTrue(resultDescriptor.getRoutes().isEmpty());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 4);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        List<MediaRouteDescriptor> routes = resultDescriptor.getRoutes();
+        assertEquals(1, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_4, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_4, routes.get(0).getName());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 10);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        routes = resultDescriptor.getRoutes();
+        assertEquals(2, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_1, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_1, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_4, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_4, routes.get(1).getName());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 12);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        routes = resultDescriptor.getRoutes();
+        assertEquals(3, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_1, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_1, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_2, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_2, routes.get(1).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_4, routes.get(2).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_4, routes.get(2).getName());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 15);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        routes = resultDescriptor.getRoutes();
+        assertEquals(2, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_1, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_1, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_2, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_2, routes.get(1).getName());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 16);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        routes = resultDescriptor.getRoutes();
+        assertEquals(2, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_2, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_2, routes.get(0).getName());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_3, routes.get(1).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_3, routes.get(1).getName());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 19);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        routes = resultDescriptor.getRoutes();
+        assertEquals(1, routes.size());
+        assertEquals(FAKE_MEDIA_ROUTE_ID_3, routes.get(0).getId());
+        assertEquals(FAKE_MEDIA_ROUTE_NAME_3, routes.get(0).getName());
+
+        bundle = MediaRouteProviderService.createDescriptorBundleForClientVersion(descriptor, 26);
+        resultDescriptor = MediaRouteProviderDescriptor.fromBundle(bundle);
+        assertTrue(resultDescriptor.getRoutes().isEmpty());
+    }
+}
diff --git a/v7/palette/.classpath b/v7/palette/.classpath
deleted file mode 100644
index 43cb38c..0000000
--- a/v7/palette/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-    <classpathentry kind="src" path="src"/>
-    <classpathentry kind="src" path="gen"/>
-    <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-    <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-    <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/palette/.project b/v7/palette/.project
deleted file mode 100644
index 76e11a6..0000000
--- a/v7/palette/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-    <name>android-support-v7-palette</name>
-    <comment></comment>
-    <projects>
-    </projects>
-    <buildSpec>
-        <buildCommand>
-            <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-            <arguments>
-            </arguments>
-        </buildCommand>
-        <buildCommand>
-            <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-            <arguments>
-            </arguments>
-        </buildCommand>
-        <buildCommand>
-            <name>org.eclipse.jdt.core.javabuilder</name>
-            <arguments>
-            </arguments>
-        </buildCommand>
-        <buildCommand>
-            <name>com.android.ide.eclipse.adt.ApkBuilder</name>
-            <arguments>
-            </arguments>
-        </buildCommand>
-    </buildSpec>
-    <natures>
-        <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-        <nature>org.eclipse.jdt.core.javanature</nature>
-    </natures>
-</projectDescription>
diff --git a/v7/palette/Android.mk b/v7/palette/Android.mk
index a9f9a75..f823d30 100644
--- a/v7/palette/Android.mk
+++ b/v7/palette/Android.mk
@@ -29,7 +29,7 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
-LOCAL_MANIFEST_FILE := src/main/AndroidManifest-make.xml
+LOCAL_MANIFEST_FILE := AndroidManifest.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-core-utils \
diff --git a/v7/palette/AndroidManifest.xml b/v7/palette/AndroidManifest.xml
new file mode 100644
index 0000000..8e5ffaa
--- /dev/null
+++ b/v7/palette/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v7.palette">
+    <uses-sdk android:minSdkVersion="14"/>
+    <application>
+        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+    </application>
+</manifest>
diff --git a/v7/palette/README.txt b/v7/palette/README.txt
deleted file mode 100644
index 4a8b5e4..0000000
--- a/v7/palette/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Library Project including Palette for color extraction from images
diff --git a/v7/palette/build.gradle b/v7/palette/build.gradle
index 686fe71..915ef13 100644
--- a/v7/palette/build.gradle
+++ b/v7/palette/build.gradle
@@ -1,76 +1,23 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'palette-v7'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-core-utils')
+    api project(':support-compat')
+    api project(':support-core-utils')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    testCompile 'junit:junit:4.12'
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+        minSdkVersion 14
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Palette v7'
-                description "Android Support for extracting color palettes from images"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Palette v7'
+    inceptionYear '2014'
+    description 'Android Support Palette v7'
 }
diff --git a/v7/palette/lint-baseline.xml b/v7/palette/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v7/palette/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v7/palette/project.properties b/v7/palette/project.properties
deleted file mode 100644
index 1e106c3..0000000
--- a/v7/palette/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-7
-android.library=true
\ No newline at end of file
diff --git a/v7/palette/src/androidTest/AndroidManifest.xml b/v7/palette/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index c4fd08f..0000000
--- a/v7/palette/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="android.support.v7.palette.test">
-
-    <uses-sdk android:minSdkVersion="9"
-              tools:overrideLibrary="android.support.test, android.app, android.support.test.rule"/>
-
-    <application>
-        <uses-library android:name="android.test.runner"/>
-    </application>
-
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="android.support.v7.palette.test"/>
-
-</manifest>
diff --git a/v7/palette/src/main/AndroidManifest-make.xml b/v7/palette/src/main/AndroidManifest-make.xml
deleted file mode 100644
index 5124bc5..0000000
--- a/v7/palette/src/main/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v7.palette">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application />
-</manifest>
diff --git a/v7/palette/src/main/AndroidManifest.xml b/v7/palette/src/main/AndroidManifest.xml
deleted file mode 100644
index 00ebcce..0000000
--- a/v7/palette/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v7.palette">
-    <uses-sdk android:minSdkVersion="9"/>
-    <application>
-        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
-    </application>
-</manifest>
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java b/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
index 268d9b4..6526715 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
@@ -356,7 +356,9 @@
             for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++)  {
                 count += hist[colors[i]];
                 if (count >= midPoint) {
-                    return i;
+                    // we never want to split on the upperIndex, as this will result in the same
+                    // box
+                    return Math.min(mUpperIndex - 1, i);
                 }
             }
 
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
index 86c2737..b7fb054 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -24,7 +24,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.graphics.ColorUtils;
-import android.support.v4.os.AsyncTaskCompat;
 import android.support.v4.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseBooleanArray;
@@ -541,7 +540,7 @@
                 final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
                         Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
 
-                if (darkBodyAlpha != -1 && darkBodyAlpha != -1) {
+                if (darkBodyAlpha != -1 && darkTitleAlpha != -1) {
                     // If we found valid dark values, use them and return
                     mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
                     mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
@@ -856,23 +855,22 @@
                 throw new IllegalArgumentException("listener can not be null");
             }
 
-            return AsyncTaskCompat.executeParallel(
-                    new AsyncTask<Bitmap, Void, Palette>() {
-                        @Override
-                        protected Palette doInBackground(Bitmap... params) {
-                            try {
-                                return generate();
-                            } catch (Exception e) {
-                                Log.e(LOG_TAG, "Exception thrown during async generate", e);
-                                return null;
-                            }
-                        }
+            return new AsyncTask<Bitmap, Void, Palette>() {
+                @Override
+                protected Palette doInBackground(Bitmap... params) {
+                    try {
+                        return generate();
+                    } catch (Exception e) {
+                        Log.e(LOG_TAG, "Exception thrown during async generate", e);
+                        return null;
+                    }
+                }
 
-                        @Override
-                        protected void onPostExecute(Palette colorExtractor) {
-                            listener.onGenerated(colorExtractor);
-                        }
-                    }, mBitmap);
+                @Override
+                protected void onPostExecute(Palette colorExtractor) {
+                    listener.onGenerated(colorExtractor);
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
         }
 
         private int[] getPixelsFromBitmap(Bitmap bitmap) {
diff --git a/v7/palette/tests/AndroidManifest.xml b/v7/palette/tests/AndroidManifest.xml
new file mode 100644
index 0000000..ca9860a
--- /dev/null
+++ b/v7/palette/tests/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v7.palette.test">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
+</manifest>
diff --git a/v7/palette/src/androidTest/NO_DOCS b/v7/palette/tests/NO_DOCS
similarity index 100%
rename from v7/palette/src/androidTest/NO_DOCS
rename to v7/palette/tests/NO_DOCS
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java b/v7/palette/tests/java/android/support/v7/graphics/BucketTests.java
similarity index 100%
rename from v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java
rename to v7/palette/tests/java/android/support/v7/graphics/BucketTests.java
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java b/v7/palette/tests/java/android/support/v7/graphics/ConsistencyTest.java
similarity index 100%
rename from v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java
rename to v7/palette/tests/java/android/support/v7/graphics/ConsistencyTest.java
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java b/v7/palette/tests/java/android/support/v7/graphics/MaxColorsTest.java
similarity index 100%
rename from v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java
rename to v7/palette/tests/java/android/support/v7/graphics/MaxColorsTest.java
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java b/v7/palette/tests/java/android/support/v7/graphics/SwatchTests.java
similarity index 100%
rename from v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java
rename to v7/palette/tests/java/android/support/v7/graphics/SwatchTests.java
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/TestUtils.java b/v7/palette/tests/java/android/support/v7/graphics/TestUtils.java
similarity index 100%
rename from v7/palette/src/androidTest/java/android/support/v7/graphics/TestUtils.java
rename to v7/palette/tests/java/android/support/v7/graphics/TestUtils.java
diff --git a/v7/palette/src/androidTest/res/drawable-nodpi/photo.jpg b/v7/palette/tests/res/drawable-nodpi/photo.jpg
similarity index 100%
rename from v7/palette/src/androidTest/res/drawable-nodpi/photo.jpg
rename to v7/palette/tests/res/drawable-nodpi/photo.jpg
Binary files differ
diff --git a/v7/preference/Android.mk b/v7/preference/Android.mk
index 110aed2..e751e1c 100644
--- a/v7/preference/Android.mk
+++ b/v7/preference/Android.mk
@@ -32,7 +32,6 @@
     $(call all-java-files-under,constants) \
     $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-appcompat \
     android-support-v7-recyclerview \
diff --git a/v7/preference/AndroidManifest-make.xml b/v7/preference/AndroidManifest-make.xml
deleted file mode 100644
index 9231775..0000000
--- a/v7/preference/AndroidManifest-make.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.support.v7.preference">
-    <uses-sdk android:minSdkVersion="9" />
-    <application />
-</manifest>
diff --git a/v7/preference/AndroidManifest.xml b/v7/preference/AndroidManifest.xml
index 0c03b91..772b410 100644
--- a/v7/preference/AndroidManifest.xml
+++ b/v7/preference/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.support.v7.preference">
-    <uses-sdk android:minSdkVersion="9" />
+    <uses-sdk android:minSdkVersion="14" />
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index b5fd656..3f81093 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -14,51 +14,38 @@
  * limitations under the License
  */
 
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'preference-v7'
 
 dependencies {
-    compile project(':support-v4')
-    compile project(':support-appcompat-v7')
-    compile project(':support-recyclerview-v7')
+    api project(':support-v4')
+    api project(':support-appcompat-v7')
+    api project(':support-recyclerview-v7')
 
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    testCompile 'junit:junit:4.12'
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-        minSdkVersion 9
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
         main.java.srcDir 'constants'
-        main.res.srcDir 'res'
+        main.res.srcDirs = [
+                'res',
+                'res-public'
+        ]
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
-
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 
     lintOptions {
@@ -71,52 +58,8 @@
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support Preference v7'
-                description "Android Support Preference v7"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2015'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support Preference v7'
+    inceptionYear '2015'
+    description 'Android Support Preference v7'
 }
diff --git a/v7/preference/lint-baseline.xml b/v7/preference/lint-baseline.xml
new file mode 100644
index 0000000..22b46d6
--- /dev/null
+++ b/v7/preference/lint-baseline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        android:layout_width=&quot;0dp&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout/preference_dropdown.xml"
+            line="29"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="Suspicious0dp"
+        message="Suspicious size: this will make the view invisible, should be used with `layout_weight`"
+        errorLine1="        android:layout_width=&quot;0dp&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/layout-v11/preference_dropdown.xml"
+            line="30"
+            column="9"/>
+    </issue>
+
+</issues>
diff --git a/v7/preference/res-public/values/public_attrs.xml b/v7/preference/res-public/values/public_attrs.xml
new file mode 100644
index 0000000..c765d7e
--- /dev/null
+++ b/v7/preference/res-public/values/public_attrs.xml
@@ -0,0 +1,82 @@
+<?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.
+-->
+
+<!-- Definitions of attributes to be exposed as public -->
+<resources>
+    <public type="attr" name="adjustable" />
+    <public type="attr" name="allowDividerAbove" />
+    <public type="attr" name="allowDividerAfterLastItem" />
+    <public type="attr" name="allowDividerBelow" />
+    <public type="attr" name="checkBoxPreferenceStyle" />
+    <public type="attr" name="defaultValue" />
+    <public type="attr" name="dependency" />
+    <public type="attr" name="dialogIcon" />
+    <public type="attr" name="dialogLayout" />
+    <public type="attr" name="dialogMessage" />
+    <public type="attr" name="dialogPreferenceStyle" />
+    <public type="attr" name="dialogTitle" />
+    <public type="attr" name="disableDependentsState" />
+    <public type="attr" name="dropdownPreferenceStyle" />
+    <public type="attr" name="editTextPreferenceStyle" />
+    <public type="attr" name="enabled" />
+    <public type="attr" name="entries" />
+    <public type="attr" name="entryValues" />
+    <public type="attr" name="fragment" />
+    <public type="attr" name="icon" />
+    <public type="attr" name="iconSpaceReserved" />
+    <public type="attr" name="key" />
+    <public type="attr" name="layout" />
+    <public type="attr" name="maxHeight" />
+    <public type="attr" name="maxWidth" />
+    <public type="attr" name="min" />
+    <public type="attr" name="negativeButtonText" />
+    <public type="attr" name="order" />
+    <public type="attr" name="orderingFromXml" />
+    <public type="attr" name="persistent" />
+    <public type="attr" name="positiveButtonText" />
+    <public type="attr" name="preferenceActivityStyle" />
+    <public type="attr" name="preferenceCategoryStyle" />
+    <public type="attr" name="preferenceFragmentCompatStyle" />
+    <public type="attr" name="preferenceFragmentListStyle" />
+    <public type="attr" name="preferenceFragmentPaddingSide" />
+    <public type="attr" name="preferenceFragmentStyle" />
+    <public type="attr" name="preferenceHeaderPanelStyle" />
+    <public type="attr" name="preferenceInformationStyle" />
+    <public type="attr" name="preferenceLayoutChild" />
+    <public type="attr" name="preferenceListStyle" />
+    <public type="attr" name="preferencePanelStyle" />
+    <public type="attr" name="preferenceScreenStyle" />
+    <public type="attr" name="preferenceStyle" />
+    <public type="attr" name="preferenceTheme" />
+    <public type="attr" name="ringtonePreferenceStyle" />
+    <public type="attr" name="seekBarIncrement" />
+    <public type="attr" name="seekBarPreferenceStyle" />
+    <public type="attr" name="selectable" />
+    <public type="attr" name="selectableItemBackground" />
+    <public type="attr" name="shouldDisableView" />
+    <public type="attr" name="showSeekBarValue" />
+    <public type="attr" name="singleLineTitle" />
+    <public type="attr" name="summary" />
+    <public type="attr" name="summaryOff" />
+    <public type="attr" name="summaryOn" />
+    <public type="attr" name="switchPreferenceCompatStyle" />
+    <public type="attr" name="switchPreferenceStyle" />
+    <public type="attr" name="switchTextOff" />
+    <public type="attr" name="switchTextOn" />
+    <public type="attr" name="title" />
+    <public type="attr" name="widgetLayout" />
+    <public type="attr" name="yesNoPreferenceStyle" />
+</resources>
diff --git a/v7/preference/res-public/values/public_styles.xml b/v7/preference/res-public/values/public_styles.xml
new file mode 100644
index 0000000..dd2c324
--- /dev/null
+++ b/v7/preference/res-public/values/public_styles.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<!-- Definitions of styles to be exposed as public -->
+<resources>
+    <public type="style" name="Preference" />
+    <public type="style" name="Preference.Category" />
+    <public type="style" name="Preference.CheckBoxPreference" />
+    <public type="style" name="Preference.DialogPreference.EditTextPreference" />
+    <public type="style" name="Preference.DialogPreference" />
+    <public type="style" name="Preference.DropDown" />
+    <public type="style" name="Preference.Information" />
+    <public type="style" name="Preference.PreferenceScreen" />
+    <public type="style" name="Preference.SeekBarPreference" />
+    <public type="style" name="Preference.SwitchPreferenceCompat" />
+    <public type="style" name="PreferenceFragment" />
+    <public type="style" name="PreferenceFragmentList" />
+</resources>
diff --git a/v7/preference/res-public/values/public_themes.xml b/v7/preference/res-public/values/public_themes.xml
new file mode 100644
index 0000000..a9241c8
--- /dev/null
+++ b/v7/preference/res-public/values/public_themes.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<!-- Definitions of themes to be exposed as public -->
+<resources>
+    <public type="style" name="PreferenceThemeOverlay" />
+</resources>
\ No newline at end of file
diff --git a/v7/preference/res/layout/preference_widget_seekbar.xml b/v7/preference/res/layout/preference_widget_seekbar.xml
index 30bc5ff..05a075f 100644
--- a/v7/preference/res/layout/preference_widget_seekbar.xml
+++ b/v7/preference/res/layout/preference_widget_seekbar.xml
@@ -25,7 +25,7 @@
               android:clipToPadding="false">
 
     <ImageView
-            android:id="@+android:id/icon"
+            android:id="@android:id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
@@ -42,7 +42,7 @@
             android:clipChildren="false"
             android:clipToPadding="false">
 
-        <TextView android:id="@+android:id/title"
+        <TextView android:id="@android:id/title"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:singleLine="true"
@@ -50,7 +50,7 @@
                   android:ellipsize="marquee"
                   android:fadingEdge="horizontal"/>
 
-        <TextView android:id="@+android:id/summary"
+        <TextView android:id="@android:id/summary"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_below="@android:id/title"
@@ -94,4 +94,4 @@
 
     </RelativeLayout>
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/v7/preference/res/values/attrs.xml b/v7/preference/res/values/attrs.xml
index 4dfcc1c..f204d45 100644
--- a/v7/preference/res/values/attrs.xml
+++ b/v7/preference/res/values/attrs.xml
@@ -80,6 +80,8 @@
         <attr name="android:divider" />
         <!-- List separator height -->
         <attr name="android:dividerHeight" />
+        <!-- Whether a divider is allowed to draw after the last item -->
+        <attr name="allowDividerAfterLastItem" format="boolean" />
     </declare-styleable>
 
     <!-- Base attributes available to PreferenceGroup. -->
@@ -145,6 +147,26 @@
              this Preference is disabled. -->
         <attr name="shouldDisableView" format="boolean" />
         <attr name="android:shouldDisableView" />
+
+        <!-- Whether the preference allows displaying divider on top -->
+        <attr name="allowDividerAbove" format="boolean" />
+
+        <!-- Whether the preference allows displaying divider below it -->
+        <attr name="allowDividerBelow" format="boolean" />
+
+        <!-- Whether to use single line for the preference title text. By default, preference title
+             will be constrained to one line, so the default value of this attribute is true. -->
+        <attr name="singleLineTitle" format="boolean" />
+        <attr name="android:singleLineTitle" />
+
+        <!-- Whether the space for the preference icon view will be reserved. If set to true, the
+             preference will be offset as if it would have the icon and thus aligned with other
+             preferences having icons. By default, preference icon view visibility will be set to
+             GONE when there is no icon provided, so the default value of this attribute is false.
+             -->
+        <attr name="iconSpaceReserved" format="boolean" />
+        <attr name="android:iconSpaceReserved" />
+
     </declare-styleable>
 
     <!-- Base attributes available to CheckBoxPreference. -->
diff --git a/v7/preference/src/android/support/v7/internal/widget/PreferenceImageView.java b/v7/preference/src/android/support/v7/internal/widget/PreferenceImageView.java
index e2a133c..e9a9dcf 100644
--- a/v7/preference/src/android/support/v7/internal/widget/PreferenceImageView.java
+++ b/v7/preference/src/android/support/v7/internal/widget/PreferenceImageView.java
@@ -69,6 +69,7 @@
         super.setMaxWidth(maxWidth);
     }
 
+    @Override
     public int getMaxWidth() {
         return mMaxWidth;
     }
@@ -79,6 +80,7 @@
         super.setMaxHeight(maxHeight);
     }
 
+    @Override
     public int getMaxHeight() {
         return mMaxHeight;
     }
diff --git a/v7/preference/src/android/support/v7/preference/DropDownPreference.java b/v7/preference/src/android/support/v7/preference/DropDownPreference.java
index 89c9d2e..3638b71 100644
--- a/v7/preference/src/android/support/v7/preference/DropDownPreference.java
+++ b/v7/preference/src/android/support/v7/preference/DropDownPreference.java
@@ -35,7 +35,7 @@
 public class DropDownPreference extends ListPreference {
 
     private final Context mContext;
-    private final ArrayAdapter<String> mAdapter;
+    private final ArrayAdapter mAdapter;
 
     private Spinner mSpinner;
 
diff --git a/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java b/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
index 370c960..791c299 100644
--- a/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
+++ b/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
@@ -102,6 +102,7 @@
         }
         builder.setMultiChoiceItems(mEntries, checkedItems,
                 new DialogInterface.OnMultiChoiceClickListener() {
+                    @Override
                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                         if (isChecked) {
                             mPreferenceChanged |= mNewValues.add(
diff --git a/v7/preference/src/android/support/v7/preference/Preference.java b/v7/preference/src/android/support/v7/preference/Preference.java
index deb3b12..cfc4311 100644
--- a/v7/preference/src/android/support/v7/preference/Preference.java
+++ b/v7/preference/src/android/support/v7/preference/Preference.java
@@ -28,6 +28,7 @@
 import android.os.Parcelable;
 import android.support.annotation.CallSuper;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.SharedPreferencesCompat;
@@ -43,6 +44,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Represents the basic Preference UI building
@@ -80,6 +82,8 @@
  * @attr name android:persistent
  * @attr name android:defaultValue
  * @attr name android:shouldDisableView
+ * @attr name android:singleLineTitle
+ * @attr name android:iconSpaceReserved
  */
 public class Preference implements Comparable<Preference> {
     /**
@@ -88,9 +92,19 @@
     public static final int DEFAULT_ORDER = Integer.MAX_VALUE;
 
     private Context mContext;
+
+    @Nullable
     private PreferenceManager mPreferenceManager;
 
     /**
+     * The data store that should be used by this Preference to store / retrieve data. If null then
+     * {@link PreferenceManager#getPreferenceDataStore()} needs to be checked. If that one is null
+     * too it means that we are using {@link android.content.SharedPreferences} to store the data.
+     */
+    @Nullable
+    private PreferenceDataStore mPreferenceDataStore;
+
+    /**
      * Set when added to hierarchy since we need a unique ID within that
      * hierarchy.
      */
@@ -128,6 +142,12 @@
     private boolean mParentDependencyMet = true;
     private boolean mVisible = true;
 
+    private boolean mAllowDividerAbove = true;
+    private boolean mAllowDividerBelow = true;
+    private boolean mHasSingleLineTitleAttr;
+    private boolean mSingleLineTitle = true;
+    private boolean mIconSpaceReserved;
+
     /**
      * @see #setShouldDisableView(boolean)
      */
@@ -139,6 +159,7 @@
     private OnPreferenceChangeInternalListener mListener;
 
     private List<Preference> mDependents;
+    private PreferenceGroup mParentGroup;
 
     private boolean mWasDetached;
     private boolean mBaseMethodCalled;
@@ -277,6 +298,12 @@
         mDependencyKey = TypedArrayUtils.getString(a, R.styleable.Preference_dependency,
                 R.styleable.Preference_android_dependency);
 
+        mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
+                R.styleable.Preference_allowDividerAbove, mSelectable);
+
+        mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
+                R.styleable.Preference_allowDividerBelow, mSelectable);
+
         if (a.hasValue(R.styleable.Preference_defaultValue)) {
             mDefaultValue = onGetDefaultValue(a, R.styleable.Preference_defaultValue);
         } else if (a.hasValue(R.styleable.Preference_android_defaultValue)) {
@@ -287,6 +314,15 @@
                 TypedArrayUtils.getBoolean(a, R.styleable.Preference_shouldDisableView,
                         R.styleable.Preference_android_shouldDisableView, true);
 
+        mHasSingleLineTitleAttr = a.hasValue(R.styleable.Preference_singleLineTitle);
+        if (mHasSingleLineTitleAttr) {
+            mSingleLineTitle = TypedArrayUtils.getBoolean(a, R.styleable.Preference_singleLineTitle,
+                R.styleable.Preference_android_singleLineTitle, true);
+        }
+
+        mIconSpaceReserved = TypedArrayUtils.getBoolean(a, R.styleable.Preference_iconSpaceReserved,
+                R.styleable.Preference_android_iconSpaceReserved, false);
+
         a.recycle();
     }
 
@@ -397,6 +433,44 @@
     }
 
     /**
+     * Sets a {@link PreferenceDataStore} to be used by this Preference instead of using
+     * {@link android.content.SharedPreferences}.
+     *
+     * <p>The data store will remain assigned even if the Preference is moved around the preference
+     * hierarchy. It will also override a data store propagated from the {@link PreferenceManager}
+     * that owns this Preference.
+     *
+     * @param dataStore the {@link PreferenceDataStore} to be used by this Preference
+     * @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
+     */
+    public void setPreferenceDataStore(PreferenceDataStore dataStore) {
+        mPreferenceDataStore = dataStore;
+    }
+
+    /**
+     * Returns {@link PreferenceDataStore} used by this Preference. Returns {@code null} if
+     * {@link android.content.SharedPreferences} is used instead.
+     *
+     * <p>By default preferences always use {@link android.content.SharedPreferences}. To make this
+     * preference to use the {@link PreferenceDataStore} you need to assign your implementation
+     * to the Preference itself via {@link #setPreferenceDataStore(PreferenceDataStore)} or to its
+     * {@link PreferenceManager} via
+     * {@link PreferenceManager#setPreferenceDataStore(PreferenceDataStore)}.
+     *
+     * @return the {@link PreferenceDataStore} used by this Preference or {@code null} if none
+     */
+    @Nullable
+    public PreferenceDataStore getPreferenceDataStore() {
+        if (mPreferenceDataStore != null) {
+            return mPreferenceDataStore;
+        } else if (mPreferenceManager != null) {
+            return mPreferenceManager.getPreferenceDataStore();
+        }
+
+        return null;
+    }
+
+    /**
      * Return the extras Bundle object associated with this preference, creating
      * a new Bundle if there currently isn't one.  You can use this to get and
      * set individual extra key/value pairs.
@@ -492,6 +566,9 @@
             if (!TextUtils.isEmpty(title)) {
                 titleView.setText(title);
                 titleView.setVisibility(View.VISIBLE);
+                if (mHasSingleLineTitleAttr) {
+                    titleView.setSingleLine(mSingleLineTitle);
+                }
             } else {
                 titleView.setVisibility(View.GONE);
             }
@@ -518,7 +595,11 @@
                     imageView.setImageDrawable(mIcon);
                 }
             }
-            imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
+            if (mIcon != null) {
+                imageView.setVisibility(View.VISIBLE);
+            } else {
+                imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
+            }
         }
 
         View imageFrame = holder.findViewById(R.id.icon_frame);
@@ -526,7 +607,11 @@
             imageFrame = holder.findViewById(AndroidResources.ANDROID_R_ICON_FRAME);
         }
         if (imageFrame != null) {
-            imageFrame.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
+            if (mIcon != null) {
+                imageFrame.setVisibility(View.VISIBLE);
+            } else {
+                imageFrame.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
+            }
         }
 
         if (mShouldDisableView) {
@@ -539,8 +624,8 @@
         holder.itemView.setFocusable(selectable);
         holder.itemView.setClickable(selectable);
 
-        holder.setDividerAllowedAbove(selectable);
-        holder.setDividerAllowedBelow(selectable);
+        holder.setDividerAllowedAbove(mAllowDividerAbove);
+        holder.setDividerAllowedBelow(mAllowDividerBelow);
     }
 
     /**
@@ -609,7 +694,7 @@
      * @param title The title for this Preference.
      */
     public void setTitle(CharSequence title) {
-        if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
+        if ((title == null && mTitle != null) || (title != null && !title.equals(mTitle))) {
             mTitle = title;
             notifyChanged();
         }
@@ -691,7 +776,8 @@
      * @param summary The summary for the preference.
      */
     public void setSummary(CharSequence summary) {
-        if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
+        if ((summary == null && mSummary != null)
+                || (summary != null && !summary.equals(mSummary))) {
             mSummary = summary;
             notifyChanged();
         }
@@ -824,8 +910,8 @@
     }
 
     /**
-     * Sets the key for this Preference, which is used as a key to the
-     * {@link android.content.SharedPreferences}. This should be unique for the package.
+     * Sets the key for this Preference, which is used as a key to the {@link SharedPreferences} or
+     * {@link PreferenceDataStore}. This should be unique for the package.
      *
      * @param key The key for the preference.
      */
@@ -838,8 +924,8 @@
     }
 
     /**
-     * Gets the key for this Preference, which is also the key used for storing
-     * values into SharedPreferences.
+     * Gets the key for this Preference, which is also the key used for storing values into
+     * {@link SharedPreferences} or {@link PreferenceDataStore}.
      *
      * @return The key.
      */
@@ -848,9 +934,8 @@
     }
 
     /**
-     * Checks whether the key is present, and if it isn't throws an
-     * exception. This should be called by subclasses that store preferences in
-     * the {@link android.content.SharedPreferences}.
+     * Checks whether the key is present, and if it isn't throws an exception. This should be called
+     * by subclasses that persist their preferences.
      *
      * @throws IllegalStateException If there is no key assigned.
      */
@@ -873,40 +958,90 @@
 
     /**
      * Checks whether this Preference is persistent. If it is, it stores its value(s) into
-     * the persistent {@link android.content.SharedPreferences} storage.
+     * the persistent {@link SharedPreferences} storage by default or into
+     * {@link PreferenceDataStore} if assigned.
      *
-     * @return True if it is persistent.
+     * @return {@code true} if persistent
      */
     public boolean isPersistent() {
         return mPersistent;
     }
 
     /**
-     * Checks whether, at the given time this method is called,
-     * this Preference should store/restore its value(s) into the
-     * {@link android.content.SharedPreferences}. This, at minimum, checks whether this
-     * Preference is persistent and it currently has a key. Before you
-     * save/restore from the {@link android.content.SharedPreferences}, check this first.
+     * Checks whether, at the given time this method is called, this Preference should store/restore
+     * its value(s) into the {@link SharedPreferences} or into {@link PreferenceDataStore} if
+     * assigned. This, at minimum, checks whether this Preference is persistent and it currently has
+     * a key. Before you save/restore from the storage, check this first.
      *
-     * @return True if it should persist the value.
+     * @return {@code true} if it should persist the value
      */
     protected boolean shouldPersist() {
         return mPreferenceManager != null && isPersistent() && hasKey();
     }
 
     /**
-     * Sets whether this Preference is persistent. When persistent,
-     * it stores its value(s) into the persistent {@link android.content.SharedPreferences}
-     * storage.
+     * Sets whether this Preference is persistent. When persistent, it stores its value(s) into
+     * the persistent {@link SharedPreferences} storage by default or into
+     * {@link PreferenceDataStore} if assigned.
      *
-     * @param persistent Set true if it should store its value(s) into the
-     *                   {@link android.content.SharedPreferences}.
+     * @param persistent set {@code true} if it should store its value(s) into the storage.
      */
     public void setPersistent(boolean persistent) {
         mPersistent = persistent;
     }
 
     /**
+     * Sets whether to constrain the title of this Preference to a single line instead of
+     * letting it wrap onto multiple lines.
+     *
+     * @param singleLineTitle set {@code true} if the title should be constrained to one line
+     *
+     * @attr ref R.styleable#Preference_android_singleLineTitle
+     */
+    public void setSingleLineTitle(boolean singleLineTitle) {
+        mHasSingleLineTitleAttr = true;
+        mSingleLineTitle = singleLineTitle;
+    }
+
+    /**
+     * Gets whether the title of this preference is constrained to a single line.
+     *
+     * @see #setSingleLineTitle(boolean)
+     * @return {@code true} if the title of this preference is constrained to a single line
+     *
+     * @attr ref R.styleable#Preference_android_singleLineTitle
+     */
+    public boolean isSingleLineTitle() {
+        return mSingleLineTitle;
+    }
+
+    /**
+     * Sets whether to reserve the space of this Preference icon view when no icon is provided. If
+     * set to true, the preference will be offset as if it would have the icon and thus aligned with
+     * other preferences having icons.
+     *
+     * @param iconSpaceReserved set {@code true} if the space for the icon view should be reserved
+     *
+     * @attr ref R.styleable#Preference_android_iconSpaceReserved
+     */
+    public void setIconSpaceReserved(boolean iconSpaceReserved) {
+        mIconSpaceReserved = iconSpaceReserved;
+        notifyChanged();
+    }
+
+    /**
+     * Returns whether the space of this preference icon view is reserved.
+     *
+     * @see #setIconSpaceReserved(boolean)
+     * @return {@code true} if the space of this preference icon view is reserved
+     *
+     * @attr ref R.styleable#Preference_android_iconSpaceReserved
+     */
+    public boolean isIconSpaceReserved() {
+        return mIconSpaceReserved;
+    }
+
+    /**
      * Call this method after the user changes the preference, but before the
      * internal state is set. This allows the client to ignore the user value.
      *
@@ -1016,11 +1151,14 @@
      * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
      * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
      * {@link #getPersistedString(String)}.
-     * @return The {@link android.content.SharedPreferences} where this Preference reads its
-     *         value(s), or null if it isn't attached to a Preference hierarchy.
+     *
+     * @return the {@link SharedPreferences} where this Preference reads its value(s). If this
+     *         preference is not attached to a Preference hierarchy or if a
+     *         {@link PreferenceDataStore} has been set, this method returns {@code null}.
+     * @see #setPreferenceDataStore(PreferenceDataStore)
      */
     public SharedPreferences getSharedPreferences() {
-        if (mPreferenceManager == null) {
+        if (mPreferenceManager == null || getPreferenceDataStore() != null) {
             return null;
         }
 
@@ -1123,6 +1261,16 @@
     }
 
     /**
+     * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set null to remove
+     * the current parent.
+     *
+     * @param parentGroup Parent preference group of this Preference or null if none.
+     */
+    void assignParent(@Nullable PreferenceGroup parentGroup) {
+        mParentGroup = parentGroup;
+    }
+
+    /**
      * Called when the Preference hierarchy has been attached to the
      * list of preferences. This can also be called when this
      * Preference has been attached to a group that was already attached
@@ -1226,8 +1374,6 @@
      *
      * @param dependent The dependent Preference that will be enabled/disabled
      *            according to the state of this Preference.
-     * @return Returns the same Preference object, for chaining multiple calls
-     *         into a single statement.
      */
     private void unregisterDependent(Preference dependent) {
         if (mDependents != null) {
@@ -1325,6 +1471,17 @@
     }
 
     /**
+     * Returns the {@link PreferenceGroup} which is this Preference assigned to or null if this
+     * preference is not assigned to any group or is a root Preference.
+     *
+     * @return The parent PreferenceGroup or null if not attached to any.
+     */
+    @Nullable
+    public PreferenceGroup getParent() {
+        return mParentGroup;
+    }
+
+    /**
      * Called when this Preference is being removed from the hierarchy. You
      * should remove any references to this Preference that you know about. Make
      * sure to call through to the superclass implementation.
@@ -1345,6 +1502,11 @@
     }
 
     private void dispatchSetInitialValue() {
+        if (getPreferenceDataStore() != null) {
+            onSetInitialValue(true, mDefaultValue);
+            return;
+        }
+
         // By now, we know if we are persistent.
         final boolean shouldPersist = shouldPersist();
         if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
@@ -1358,14 +1520,17 @@
 
     /**
      * Implement this to set the initial value of the Preference.
-     * <p>
-     * If <var>restorePersistedValue</var> is true, you should restore the
+     *
+     * <p>If <var>restorePersistedValue</var> is true, you should restore the
      * Preference value from the {@link android.content.SharedPreferences}. If
      * <var>restorePersistedValue</var> is false, you should set the Preference
      * value to defaultValue that is given (and possibly store to SharedPreferences
      * if {@link #shouldPersist()} is true).
-     * <p>
-     * This may not always be called. One example is if it should not persist
+     *
+     * <p>In case of using {@link PreferenceDataStore}, the <var>restorePersistedValue</var> is
+     * always {@code true} but the default value (if provided) is set.
+     *
+     * <p>This may not always be called. One example is if it should not persist
      * but there is no default value given.
      *
      * @param restorePersistedValue True to restore the persisted value;
@@ -1383,45 +1548,44 @@
     }
 
     /**
-     * Attempts to persist a String to the {@link android.content.SharedPreferences}.
-     * <p>
-     * This will check if this Preference is persistent, get an editor from
-     * the {@link PreferenceManager}, put in the string, and check if we should commit (and
-     * commit if so).
+     * Attempts to persist a {@link String} if this Preference is persistent.
+     *
+     * <p>The returned value doesn't reflect whether the given value was persisted, since we may not
+     * necessarily commit if there will be a batch commit later.
      *
      * @param value The value to persist.
-     * @return True if the Preference is persistent. (This is not whether the
-     *         value was persisted, since we may not necessarily commit if there
-     *         will be a batch commit later.)
+     * @return {@code true} if the Preference is persistent, {@code false} otherwise
      * @see #getPersistedString(String)
      */
     protected boolean persistString(String value) {
-        if (shouldPersist()) {
-            // Shouldn't store null
-            if (value == getPersistedString(null)) {
-                // It's already there, so the same as persisting
-                return true;
-            }
+        if (!shouldPersist()) {
+            return false;
+        }
 
+        // Shouldn't store null
+        if (TextUtils.equals(value, getPersistedString(null))) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            dataStore.putString(mKey, value);
+        } else {
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putString(mKey, value);
             tryCommit(editor);
-            return true;
         }
-        return false;
+        return true;
     }
 
     /**
-     * Attempts to get a persisted String from the {@link android.content.SharedPreferences}.
-     * <p>
-     * This will check if this Preference is persistent, get the SharedPreferences
-     * from the {@link PreferenceManager}, and get the value.
+     * Attempts to get a persisted set of Strings if this Preference is persistent.
      *
      * @param defaultReturnValue The default value to return if either the
      *            Preference is not persistent or the Preference is not in the
      *            shared preferences.
-     * @return The value from the SharedPreferences or the default return
-     *         value.
+     * @return the value from the storage or the default return value
      * @see #persistString(String)
      */
     protected String getPersistedString(String defaultReturnValue) {
@@ -1429,42 +1593,106 @@
             return defaultReturnValue;
         }
 
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            return dataStore.getString(mKey, defaultReturnValue);
+        }
+
         return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
     }
 
     /**
-     * Attempts to persist an int to the {@link android.content.SharedPreferences}.
+     * Attempts to persist a set of Strings if this Preference is persistent.
+     *
+     * <p>The returned value doesn't reflect whether the given value was persisted, since we may not
+     * necessarily commit if there will be a batch commit later.
+     *
+     * @param values the values to persist
+     * @return {@code true} if the Preference is persistent, {@code false} otherwise
+     * @see #getPersistedStringSet(Set)
+     */
+    public boolean persistStringSet(Set<String> values) {
+        if (!shouldPersist()) {
+            return false;
+        }
+
+        // Shouldn't store null
+        if (values.equals(getPersistedStringSet(null))) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            dataStore.putStringSet(mKey, values);
+        } else {
+            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
+            editor.putStringSet(mKey, values);
+            tryCommit(editor);
+        }
+        return true;
+    }
+
+    /**
+     * Attempts to get a persisted set of Strings if this Preference is persistent.
+     *
+     * @param defaultReturnValue the default value to return if either this Preference is not
+     *                           persistent or this Preference is not present
+     * @return the value from the storage or the default return value
+     * @see #persistStringSet(Set)
+     */
+    public Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            return dataStore.getStringSet(mKey, defaultReturnValue);
+        }
+
+        return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
+    }
+
+    /**
+     * Attempts to persist an {@link Integer} if this Preference is persistent.
+     *
+     * <p>The returned value doesn't reflect whether the given value was persisted, since we may not
+     * necessarily commit if there will be a batch commit later.
      *
      * @param value The value to persist.
-     * @return True if the Preference is persistent. (This is not whether the
-     *         value was persisted, since we may not necessarily commit if there
-     *         will be a batch commit later.)
+     * @return {@code true} if the Preference is persistent, {@code false} otherwise
      * @see #persistString(String)
      * @see #getPersistedInt(int)
      */
     protected boolean persistInt(int value) {
-        if (shouldPersist()) {
-            if (value == getPersistedInt(~value)) {
-                // It's already there, so the same as persisting
-                return true;
-            }
+        if (!shouldPersist()) {
+            return false;
+        }
 
+        if (value == getPersistedInt(~value)) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            dataStore.putInt(mKey, value);
+        } else {
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putInt(mKey, value);
             tryCommit(editor);
-            return true;
         }
-        return false;
+        return true;
     }
 
     /**
-     * Attempts to get a persisted int from the {@link android.content.SharedPreferences}.
+     * Attempts to get a persisted {@link Integer} if this Preference is persistent.
      *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
-     * @return The value from the SharedPreferences or the default return
-     *         value.
+     * @return the value from the storage or the default return value
      * @see #getPersistedString(String)
      * @see #persistInt(int)
      */
@@ -1473,42 +1701,53 @@
             return defaultReturnValue;
         }
 
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            return dataStore.getInt(mKey, defaultReturnValue);
+        }
+
         return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
     }
 
     /**
-     * Attempts to persist a float to the {@link android.content.SharedPreferences}.
+     * Attempts to persist a {@link Float} if this Preference is persistent.
+     *
+     * <p>The returned value doesn't reflect whether the given value was persisted, since we may not
+     * necessarily commit if there will be a batch commit later.
      *
      * @param value The value to persist.
-     * @return True if this Preference is persistent. (This is not whether the
-     *         value was persisted, since we may not necessarily commit if there
-     *         will be a batch commit later.)
+     * @return {@code true} if the Preference is persistent, {@code false} otherwise
      * @see #persistString(String)
      * @see #getPersistedFloat(float)
      */
     protected boolean persistFloat(float value) {
-        if (shouldPersist()) {
-            if (value == getPersistedFloat(Float.NaN)) {
-                // It's already there, so the same as persisting
-                return true;
-            }
+        if (!shouldPersist()) {
+            return false;
+        }
 
+        if (value == getPersistedFloat(Float.NaN)) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            dataStore.putFloat(mKey, value);
+        } else {
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putFloat(mKey, value);
             tryCommit(editor);
-            return true;
         }
-        return false;
+        return true;
     }
 
     /**
-     * Attempts to get a persisted float from the {@link android.content.SharedPreferences}.
+     * Attempts to get a persisted {@link Float} if this Preference is persistent.
      *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
-     * @return The value from the SharedPreferences or the default return
-     *         value.
+     * @return the value from the storage or the default return value
      * @see #getPersistedString(String)
      * @see #persistFloat(float)
      */
@@ -1517,42 +1756,53 @@
             return defaultReturnValue;
         }
 
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            return dataStore.getFloat(mKey, defaultReturnValue);
+        }
+
         return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
     }
 
     /**
-     * Attempts to persist a long to the {@link android.content.SharedPreferences}.
+     * Attempts to persist a {@link Long} if this Preference is persistent.
+     *
+     * <p>The returned value doesn't reflect whether the given value was persisted, since we may not
+     * necessarily commit if there will be a batch commit later.
      *
      * @param value The value to persist.
-     * @return True if this Preference is persistent. (This is not whether the
-     *         value was persisted, since we may not necessarily commit if there
-     *         will be a batch commit later.)
+     * @return {@code true} if the Preference is persistent, {@code false} otherwise
      * @see #persistString(String)
      * @see #getPersistedLong(long)
      */
     protected boolean persistLong(long value) {
-        if (shouldPersist()) {
-            if (value == getPersistedLong(~value)) {
-                // It's already there, so the same as persisting
-                return true;
-            }
+        if (!shouldPersist()) {
+            return false;
+        }
 
+        if (value == getPersistedLong(~value)) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            dataStore.putLong(mKey, value);
+        } else {
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putLong(mKey, value);
             tryCommit(editor);
-            return true;
         }
-        return false;
+        return true;
     }
 
     /**
-     * Attempts to get a persisted long from the {@link android.content.SharedPreferences}.
+     * Attempts to get a persisted {@link Long} if this Preference is persistent.
      *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
-     * @return The value from the SharedPreferences or the default return
-     *         value.
+     * @return the value from the storage or the default return value
      * @see #getPersistedString(String)
      * @see #persistLong(long)
      */
@@ -1561,42 +1811,53 @@
             return defaultReturnValue;
         }
 
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            return dataStore.getLong(mKey, defaultReturnValue);
+        }
+
         return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
     }
 
     /**
-     * Attempts to persist a boolean to the {@link android.content.SharedPreferences}.
+     * Attempts to persist a {@link Boolean} if this Preference is persistent.
+     *
+     * <p>The returned value doesn't reflect whether the given value was persisted, since we may not
+     * necessarily commit if there will be a batch commit later.
      *
      * @param value The value to persist.
-     * @return True if this Preference is persistent. (This is not whether the
-     *         value was persisted, since we may not necessarily commit if there
-     *         will be a batch commit later.)
+     * @return {@code true} if the Preference is persistent, {@code false} otherwise
      * @see #persistString(String)
      * @see #getPersistedBoolean(boolean)
      */
     protected boolean persistBoolean(boolean value) {
-        if (shouldPersist()) {
-            if (value == getPersistedBoolean(!value)) {
-                // It's already there, so the same as persisting
-                return true;
-            }
+        if (!shouldPersist()) {
+            return false;
+        }
 
+        if (value == getPersistedBoolean(!value)) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            dataStore.putBoolean(mKey, value);
+        } else {
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putBoolean(mKey, value);
             tryCommit(editor);
-            return true;
         }
-        return false;
+        return true;
     }
 
     /**
-     * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}.
+     * Attempts to get a persisted {@link Boolean} if this Preference is persistent.
      *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
-     * @return The value from the SharedPreferences or the default return
-     *         value.
+     * @return the value from the storage or the default return value
      * @see #getPersistedString(String)
      * @see #persistBoolean(boolean)
      */
@@ -1605,6 +1866,11 @@
             return defaultReturnValue;
         }
 
+        PreferenceDataStore dataStore = getPreferenceDataStore();
+        if (dataStore != null) {
+            return dataStore.getBoolean(mKey, defaultReturnValue);
+        }
+
         return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
     }
 
@@ -1617,7 +1883,7 @@
      * Returns the text that will be used to filter this Preference depending on
      * user input.
      * <p>
-     * If overridding and calling through to the superclass, make sure to prepend
+     * If overriding and calling through to the superclass, make sure to prepend
      * your additions with a space.
      *
      * @return Text as a {@link StringBuilder} that will be used to filter this
@@ -1654,8 +1920,8 @@
     }
 
     /**
-     * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children.
-     * May be overridden to modify how the save happens for children. For example, some
+     * Called by {@link #saveHierarchyState} to store the instance for this Preference and its
+     * children. May be overridden to modify how the save happens for children. For example, some
      * Preference objects may want to not store an instance for their children.
      *
      * @param container The Bundle in which to save the instance of this Preference.
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceDataStore.java b/v7/preference/src/android/support/v7/preference/PreferenceDataStore.java
new file mode 100644
index 0000000..fa5de18
--- /dev/null
+++ b/v7/preference/src/android/support/v7/preference/PreferenceDataStore.java
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference;
+
+import android.support.annotation.Nullable;
+
+import java.util.Set;
+
+/**
+ * A data store interface to be implemented and provided to the Preferences framework. This can be
+ * used to replace the default {@link android.content.SharedPreferences}, if needed.
+ *
+ * <p>In most cases you want to use {@link android.content.SharedPreferences} as it is automatically
+ * backed up and migrated to new devices. However, providing custom data store to preferences can be
+ * useful if your app stores its preferences in a local db, cloud or they are device specific like
+ * "Developer settings". It might be also useful when you want to use the preferences UI but
+ * the data are not supposed to be stored at all because they are valid per session only.
+ *
+ * <p>Once a put method is called it is full responsibility of the data store implementation to
+ * safely store the given values. Time expensive operations need to be done in the background to
+ * prevent from blocking the UI. You also need to have a plan on how to serialize the data in case
+ * the activity holding this object gets destroyed.
+ *
+ * <p>By default, all "put" methods throw {@link UnsupportedOperationException}.
+ *
+ * @see Preference#setPreferenceDataStore(PreferenceDataStore)
+ * @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
+ */
+public abstract class PreferenceDataStore {
+
+    /**
+     * Sets a {@link String} value to the data store.
+     *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
+     * @param key the name of the preference to modify
+     * @param value the new value for the preference
+     * @see #getString(String, String)
+     */
+    public void putString(String key, @Nullable String value) {
+        throw new UnsupportedOperationException("Not implemented on this data store");
+    }
+
+    /**
+     * Sets a set of Strings to the data store.
+     *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
+     * @param key the name of the preference to modify
+     * @param values the set of new values for the preference
+     * @see #getStringSet(String, Set<String>)
+     */
+    public void putStringSet(String key, @Nullable Set<String> values) {
+        throw new UnsupportedOperationException("Not implemented on this data store");
+    }
+
+    /**
+     * Sets an {@link Integer} value to the data store.
+     *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
+     * @param key the name of the preference to modify
+     * @param value the new value for the preference
+     * @see #getInt(String, int)
+     */
+    public void putInt(String key, int value) {
+        throw new UnsupportedOperationException("Not implemented on this data store");
+    }
+
+    /**
+     * Sets a {@link Long} value to the data store.
+     *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
+     * @param key the name of the preference to modify
+     * @param value the new value for the preference
+     * @see #getLong(String, long)
+     */
+    public void putLong(String key, long value) {
+        throw new UnsupportedOperationException("Not implemented on this data store");
+    }
+
+    /**
+     * Sets a {@link Float} value to the data store.
+     *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
+     * @param key the name of the preference to modify
+     * @param value the new value for the preference
+     * @see #getFloat(String, float)
+     */
+    public void putFloat(String key, float value) {
+        throw new UnsupportedOperationException("Not implemented on this data store");
+    }
+
+    /**
+     * Sets a {@link Boolean} value to the data store.
+     *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
+     * @param key the name of the preference to modify
+     * @param value the new value for the preference
+     * @see #getBoolean(String, boolean)
+     */
+    public void putBoolean(String key, boolean value) {
+        throw new UnsupportedOperationException("Not implemented on this data store");
+    }
+
+    /**
+     * Retrieves a {@link String} value from the data store.
+     *
+     * @param key the name of the preference to retrieve
+     * @param defValue value to return if this preference does not exist in the storage
+     * @return the value from the data store or the default return value
+     * @see #putString(String, String)
+     */
+    @Nullable
+    public String getString(String key, @Nullable String defValue) {
+        return defValue;
+    }
+
+    /**
+     * Retrieves a set of Strings from the data store.
+     *
+     * @param key the name of the preference to retrieve
+     * @param defValues values to return if this preference does not exist in the storage
+     * @return the values from the data store or the default return values
+     * @see #putStringSet(String, Set<String>)
+     */
+    @Nullable
+    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
+        return defValues;
+    }
+
+    /**
+     * Retrieves an {@link Integer} value from the data store.
+     *
+     * @param key the name of the preference to retrieve
+     * @param defValue value to return if this preference does not exist in the storage
+     * @return the value from the data store or the default return value
+     * @see #putInt(String, int)
+     */
+    public int getInt(String key, int defValue) {
+        return defValue;
+    }
+
+    /**
+     * Retrieves a {@link Long} value from the data store.
+     *
+     * @param key the name of the preference to retrieve
+     * @param defValue value to return if this preference does not exist in the storage
+     * @return the value from the data store or the default return value
+     * @see #putLong(String, long)
+     */
+    public long getLong(String key, long defValue) {
+        return defValue;
+    }
+
+    /**
+     * Retrieves a {@link Float} value from the data store.
+     *
+     * @param key the name of the preference to retrieve
+     * @param defValue value to return if this preference does not exist in the storage
+     * @return the value from the data store or the default return value
+     * @see #putFloat(String, float)
+     */
+    public float getFloat(String key, float defValue) {
+        return defValue;
+    }
+
+    /**
+     * Retrieves a {@link Boolean} value from the data store.
+     *
+     * @param key the name of the preference to retrieve
+     * @param defValue value to return if this preference does not exist in the storage
+     * @return the value from the data store or the default return value
+     * @see #getBoolean(String, boolean)
+     */
+    public boolean getBoolean(String key, boolean defValue) {
+        return defValue;
+    }
+}
+
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
index 7d42155..4fb9ff8 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
@@ -26,12 +26,12 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.XmlRes;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -197,7 +197,8 @@
          * @param pref The preference requesting the dialog.
          * @return true if the dialog creation has been handled.
          */
-        boolean onPreferenceDisplayDialog(PreferenceFragmentCompat caller, Preference pref);
+        boolean onPreferenceDisplayDialog(@NonNull PreferenceFragmentCompat caller,
+                Preference pref);
     }
 
     @Override
@@ -206,7 +207,7 @@
         final TypedValue tv = new TypedValue();
         getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true);
         final int theme = tv.resourceId;
-        if (theme <= 0) {
+        if (theme == 0) {
             throw new IllegalStateException("Must specify preferenceTheme in theme");
         }
         mStyledContext = new ContextThemeWrapper(getActivity(), theme);
@@ -251,6 +252,8 @@
                 R.styleable.PreferenceFragmentCompat_android_divider);
         final int dividerHeight = a.getDimensionPixelSize(
                 R.styleable.PreferenceFragmentCompat_android_dividerHeight, -1);
+        final boolean allowDividerAfterLastItem = a.getBoolean(
+                R.styleable.PreferenceFragmentCompat_allowDividerAfterLastItem, true);
 
         a.recycle();
 
@@ -285,6 +288,7 @@
         if (dividerHeight != -1) {
             setDividerHeight(dividerHeight);
         }
+        mDividerDecoration.setAllowDividerAfterLastItem(allowDividerAfterLastItem);
 
         listContainer.addView(mList);
         mHandler.post(mRequestFocus);
@@ -768,6 +772,7 @@
 
         private Drawable mDivider;
         private int mDividerHeight;
+        private boolean mAllowDividerAfterLastItem = true;
 
         @Override
         public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
@@ -779,7 +784,7 @@
             for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
                 final View view = parent.getChildAt(childViewIndex);
                 if (shouldDrawDividerBelow(view, parent)) {
-                    int top = (int) ViewCompat.getY(view) + view.getHeight();
+                    int top = (int) view.getY() + view.getHeight();
                     mDivider.setBounds(0, top, width, top + mDividerHeight);
                     mDivider.draw(c);
                 }
@@ -801,7 +806,7 @@
             if (!dividerAllowedBelow) {
                 return false;
             }
-            boolean nextAllowed = true;
+            boolean nextAllowed = mAllowDividerAfterLastItem;
             int index = parent.indexOfChild(view);
             if (index < parent.getChildCount() - 1) {
                 final View nextView = parent.getChildAt(index + 1);
@@ -826,5 +831,9 @@
             mDividerHeight = dividerHeight;
             mList.invalidateItemDecorations();
         }
+
+        public void setAllowDividerAfterLastItem(boolean allowDividerAfterLastItem) {
+            mAllowDividerAfterLastItem = allowDividerAfterLastItem;
+        }
     }
 }
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceGroup.java b/v7/preference/src/android/support/v7/preference/PreferenceGroup.java
index da7f8dc..d285ee6 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceGroup.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceGroup.java
@@ -192,6 +192,7 @@
             id = preferenceManager.getNextId();
         }
         preference.onAttachedToHierarchy(preferenceManager, id);
+        preference.assignParent(this);
 
         if (mAttachedToHierarchy) {
             preference.onAttached();
@@ -217,6 +218,9 @@
     private boolean removePreferenceInt(Preference preference) {
         synchronized(this) {
             preference.onPrepareForRemoval();
+            if (preference.getParent() == this) {
+                preference.assignParent(null);
+            }
             boolean success = mPreferenceList.remove(preference);
             if (success) {
                 // If this preference, or another preference with the same key, gets re-added
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
index feef983..d1c630f 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.annotation.RestrictTo;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.util.DiffUtil;
 import android.support.v7.widget.RecyclerView;
@@ -333,8 +334,8 @@
         Drawable background
                 = a.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground);
         if (background == null) {
-            background = parent.getContext().getResources()
-                    .getDrawable(android.R.drawable.list_selector_background);
+            background = ContextCompat.getDrawable(parent.getContext(),
+                    android.R.drawable.list_selector_background);
         }
         a.recycle();
 
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceInflater.java b/v7/preference/src/android/support/v7/preference/PreferenceInflater.java
index 33196de..453b4d4 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceInflater.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceInflater.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.XmlResourceParser;
-import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
@@ -63,12 +62,8 @@
 
     private void init(PreferenceManager preferenceManager) {
         mPreferenceManager = preferenceManager;
-        if (Build.VERSION.SDK_INT >= 14) {
-            setDefaultPackages(new String[] {"android.support.v14.preference.",
-                    "android.support.v7.preference."});
-        } else {
-            setDefaultPackages(new String[] {"android.support.v7.preference."});
-        }
+        setDefaultPackages(new String[] {"android.support.v14.preference.",
+                "android.support.v7.preference."});
     }
 
     /**
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceManager.java b/v7/preference/src/android/support/v7/preference/PreferenceManager.java
index d960e95..83af86c 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceManager.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceManager.java
@@ -21,10 +21,11 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.SharedPreferencesCompat;
-import android.support.v4.os.BuildCompat;
 import android.text.TextUtils;
 
 /**
@@ -55,12 +56,21 @@
     /**
      * Cached shared preferences.
      */
+    @Nullable
     private SharedPreferences mSharedPreferences;
 
     /**
+     * Data store to be used by the Preferences or null if {@link android.content.SharedPreferences}
+     * should be used.
+     */
+    @Nullable
+    private PreferenceDataStore mPreferenceDataStore;
+
+    /**
      * If in no-commit mode, the shared editor to give out (which will be
      * committed when exiting no-commit mode).
      */
+    @Nullable
     private SharedPreferences.Editor mEditor;
 
     /**
@@ -152,7 +162,7 @@
     }
 
     /**
-     * Returns the current name of the SharedPreferences file that preferences managed by
+     * Returns the current name of the {@link SharedPreferences} file that preferences managed by
      * this will use.
      *
      * @return The name that can be passed to {@link Context#getSharedPreferences(String, int)}.
@@ -163,11 +173,14 @@
     }
 
     /**
-     * Sets the name of the SharedPreferences file that preferences managed by this
+     * Sets the name of the {@link SharedPreferences} file that preferences managed by this
      * will use.
      *
+     * <p>If custom {@link PreferenceDataStore} is set, this won't override its usage.
+     *
      * @param sharedPreferencesName The name of the SharedPreferences file.
      * @see Context#getSharedPreferences(String, int)
+     * @see #setPreferenceDataStore(PreferenceDataStore)
      */
     public void setSharedPreferencesName(String sharedPreferencesName) {
         mSharedPreferencesName = sharedPreferencesName;
@@ -202,7 +215,7 @@
      * provided by the hosting {@link Context}.
      */
     public void setStorageDefault() {
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             mStorage = STORAGE_DEFAULT;
             mSharedPreferences = null;
         }
@@ -223,13 +236,13 @@
      * example, storing sensitive authentication tokens or passwords in the
      * device-protected area is strongly discouraged.
      * <p>
-     * Prior to {@link BuildCompat#isAtLeastN()} this method has no effect,
+     * Prior to API 24 this method has no effect,
      * since device-protected storage is not available.
      *
      * @see Context#createDeviceProtectedStorageContext()
      */
     public void setStorageDeviceProtected() {
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             mStorage = STORAGE_DEVICE_PROTECTED;
             mSharedPreferences = null;
         }
@@ -243,7 +256,7 @@
      * @see #setStorageDeviceProtected()
      */
     public boolean isStorageDefault() {
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             return mStorage == STORAGE_DEFAULT;
         } else {
             return true;
@@ -258,7 +271,7 @@
      * @see #setStorageDeviceProtected()
      */
     public boolean isStorageDeviceProtected() {
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             return mStorage == STORAGE_DEVICE_PROTECTED;
         } else {
             return false;
@@ -266,13 +279,44 @@
     }
 
     /**
-     * Gets a SharedPreferences instance that preferences managed by this will
+     * Sets a {@link PreferenceDataStore} to be used by all Preferences associated with this manager
+     * that don't have a custom {@link PreferenceDataStore} assigned via
+     * {@link Preference#setPreferenceDataStore(PreferenceDataStore)}. Also if the data store is
+     * set, the child preferences won't use {@link android.content.SharedPreferences} as long as
+     * they are assigned to this manager.
+     *
+     * @param dataStore the {@link PreferenceDataStore} to be used by this manager
+     * @see Preference#setPreferenceDataStore(PreferenceDataStore)
+     */
+    public void setPreferenceDataStore(PreferenceDataStore dataStore) {
+        mPreferenceDataStore = dataStore;
+    }
+
+    /**
+     * Returns the {@link PreferenceDataStore} associated with this manager or {@code null} if
+     * the default {@link android.content.SharedPreferences} are used instead.
+     *
+     * @return The {@link PreferenceDataStore} associated with this manager or {@code null} if none.
+     * @see #setPreferenceDataStore(PreferenceDataStore)
+     */
+    @Nullable
+    public PreferenceDataStore getPreferenceDataStore() {
+        return mPreferenceDataStore;
+    }
+
+    /**
+     * Gets a {@link SharedPreferences} instance that preferences managed by this will
      * use.
      *
-     * @return A SharedPreferences instance pointing to the file that contains
-     *         the values of preferences that are managed by this.
+     * @return a {@link SharedPreferences} instance pointing to the file that contain the values of
+     *         preferences that are managed by this PreferenceManager. If
+     *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
      */
     public SharedPreferences getSharedPreferences() {
+        if (getPreferenceDataStore() != null) {
+            return null;
+        }
+
         if (mSharedPreferences == null) {
             final Context storageContext;
             switch (mStorage) {
@@ -429,13 +473,17 @@
 
     /**
      * Returns an editor to use when modifying the shared preferences.
-     * <p>
-     * Do NOT commit unless {@link #shouldCommit()} returns true.
      *
-     * @return An editor to use to write to shared preferences.
+     * <p>Do NOT commit unless {@link #shouldCommit()} returns true.
+     *
+     * @return an editor to use to write to shared preferences. If a {@link PreferenceDataStore} has
+     *         been set, this method returns {@code null}.
      * @see #shouldCommit()
      */
     SharedPreferences.Editor getEditor() {
+        if (mPreferenceDataStore != null) {
+            return null;
+        }
 
         if (mNoCommit) {
             if (mEditor == null) {
@@ -453,6 +501,8 @@
      * {@link #getEditor()}. This will return false in cases where the writes
      * should be batched, for example when inflating preferences from XML.
      *
+     * <p>If preferences are using {@link PreferenceDataStore} this value is irrelevant.
+     *
      * @return Whether the client should commit.
      */
     boolean shouldCommit() {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
index 262fcdf..5a6426a 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
@@ -32,9 +32,7 @@
     private boolean mDividerAllowedAbove;
     private boolean mDividerAllowedBelow;
 
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.TESTS)
-    public PreferenceViewHolder(View itemView) {
+    /* package */ PreferenceViewHolder(View itemView) {
         super(itemView);
 
         // Pre-cache the views that we know in advance we'll want to find
@@ -46,6 +44,12 @@
                 itemView.findViewById(AndroidResources.ANDROID_R_ICON_FRAME));
     }
 
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    public static PreferenceViewHolder createInstanceForTests(View itemView) {
+        return new PreferenceViewHolder(itemView);
+    }
+
     /**
      * Returns a cached reference to a subview managed by this object. If the view reference is not
      * yet cached, it falls back to calling {@link View#findViewById(int)} and caches the result.
diff --git a/v7/preference/src/android/support/v7/preference/SeekBarPreference.java b/v7/preference/src/android/support/v7/preference/SeekBarPreference.java
index 7e843d8..963604c 100644
--- a/v7/preference/src/android/support/v7/preference/SeekBarPreference.java
+++ b/v7/preference/src/android/support/v7/preference/SeekBarPreference.java
@@ -362,10 +362,12 @@
         @SuppressWarnings("unused")
         public static final Parcelable.Creator<SavedState> CREATOR =
                 new Parcelable.Creator<SavedState>() {
+                    @Override
                     public SavedState createFromParcel(Parcel in) {
                         return new SavedState(in);
                     }
 
+                    @Override
                     public SavedState[] newArray(int size) {
                         return new SavedState[size];
                     }
diff --git a/v7/preference/tests/AndroidManifest.xml b/v7/preference/tests/AndroidManifest.xml
index d47fd5b..34b8a72 100644
--- a/v7/preference/tests/AndroidManifest.xml
+++ b/v7/preference/tests/AndroidManifest.xml
@@ -15,19 +15,6 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="android.support.v7.preference.tests">
-
-    <uses-sdk
-        android:minSdkVersion="9"
-        android:targetSdkVersion="24"
-        tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
-                android.support.test.espresso, android.support.test.espresso.idling" />
-
-    <application/>
-
-    <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="android.support.v7.preference.tests" />
-
+          package="android.support.v7.preference.tests">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 </manifest>
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java
new file mode 100644
index 0000000..8f9929e
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java
@@ -0,0 +1,562 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference.tests;
+
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.or;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.espresso.core.deps.guava.collect.ImmutableSet;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceDataStore;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.tests.helpers.PreferenceWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for {@link PreferenceDataStore} API.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferenceDataStoreTest {
+
+    private Context mContext;
+    private PreferenceWrapper mPreference;
+    private PreferenceDataStore mDataStore;
+    private PreferenceScreen mScreen;
+    private PreferenceManager mManager;
+    private SharedPreferences mSharedPref;
+
+    private static final String KEY = "TestPrefKey";
+    private static final String TEST_STR = "Test";
+    private static final String TEST_DEFAULT_STR = "TestDefault";
+    private static final String TEST_WRONG_STR = "TestFromSharedPref";
+
+    @Before
+    @UiThreadTest
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mDataStore = mock(PreferenceDataStore.class);
+
+        mManager = new PreferenceManager(mContext);
+        mSharedPref = mManager.getSharedPreferences();
+        mScreen = mManager.createPreferenceScreen(mContext);
+        mPreference = new PreferenceWrapper(mContext);
+        mPreference.setKey(KEY);
+
+        // Make sure that the key is not present in SharedPreferences to ensure test
+        // correctness.
+        mManager.getSharedPreferences().edit().remove(KEY).commit();
+    }
+
+    @Test
+    public void testThatDataStoreIsNullByDefault() {
+        Preference preference = new Preference(mContext);
+        mScreen.addPreference(preference);
+
+        assertNull(preference.getPreferenceDataStore());
+        assertNotNull(preference.getSharedPreferences());
+
+        assertNull(mManager.getPreferenceDataStore());
+        assertNotNull(mManager.getSharedPreferences());
+    }
+
+    @Test
+    public void testSetGetOnPreference() {
+        Preference preference = new Preference(mContext);
+
+        preference.setPreferenceDataStore(mDataStore);
+
+        assertEquals(mDataStore, preference.getPreferenceDataStore());
+    }
+
+    @Test
+    public void testSetGetOnPreferenceManager() {
+        mManager.setPreferenceDataStore(mDataStore);
+
+        assertEquals(mDataStore, mManager.getPreferenceDataStore());
+        assertNull(mManager.getSharedPreferences());
+    }
+
+    @Test
+    public void testSetOnPreferenceManagerGetOnPreference() {
+        Preference preference = new Preference(mContext);
+        mScreen.addPreference(preference);
+
+        mManager.setPreferenceDataStore(mDataStore);
+
+        assertEquals(mDataStore, preference.getPreferenceDataStore());
+        assertNull(preference.getSharedPreferences());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testDataStoresHierarchy() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        PreferenceDataStore secondaryDataStore = mock(PreferenceDataStore.class,
+                Mockito.CALLS_REAL_METHODS);
+        mManager.setPreferenceDataStore(secondaryDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.putString(TEST_STR);
+
+        // Check that the Preference returns the correct data store.
+        assertEquals(mDataStore, mPreference.getPreferenceDataStore());
+
+        // Check that the secondary data store assigned to the manager was NOT used.
+        verifyZeroInteractions(secondaryDataStore);
+
+        // Check that the primary data store assigned directly to the preference was used.
+        verify(mDataStore, atLeastOnce()).putString(eq(KEY), anyString());
+    }
+
+    /**
+     * Test that the initial value is taken from the data store (before the preference gets assigned
+     * to the preference hierarchy).
+     */
+    @Test
+    @UiThreadTest
+    public void testInitialValueIsFromDataStoreOnPreference() {
+        when(mDataStore.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+
+        CheckBoxPreference pref = new CheckBoxPreference(mContext);
+        pref.setKey("CheckboxTestPref");
+        pref.setPreferenceDataStore(mDataStore);
+
+        mScreen.addPreference(pref);
+
+        assertTrue(pref.isChecked());
+    }
+
+    /**
+     * Test that the initial value is taken from the data store (before the preference gets assigned
+     * to the preference hierarchy).
+     */
+    @Test
+    @UiThreadTest
+    public void testInitialValueIsFromDataStoreOnPreferenceManager() {
+        when(mDataStore.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+
+        mManager.setPreferenceDataStore(mDataStore);
+        CheckBoxPreference pref = new CheckBoxPreference(mContext);
+        pref.setKey("CheckboxTestPref");
+
+        mScreen.addPreference(pref);
+
+        assertTrue(pref.isChecked());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutStringWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putStringTestCommon();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutStringWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putStringTestCommon();
+    }
+
+    private void putStringTestCommon() {
+        mPreference.putString(TEST_STR);
+
+        verify(mDataStore, atLeast(0)).getString(eq(KEY), nullable(String.class));
+        verify(mDataStore, atLeastOnce()).putString(eq(KEY), anyString());
+        verifyNoMoreInteractions(mDataStore);
+
+        // Test that the value was NOT propagated to SharedPreferences.
+        assertNull(mSharedPref.getString(KEY, null));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetStringWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getString(TEST_STR);
+
+        verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetStringWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getString(TEST_STR);
+
+        verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR));
+    }
+
+    /**
+     * This test makes sure that when a default value is set to a preference that has a data store
+     * assigned that the default value is correctly propagated to
+     * {@link Preference#onSetInitialValue(boolean, Object)} instead of passing a value from
+     * {@link android.content.SharedPreferences}. We have this test only for String because the
+     * implementation is not dependent on value type so this coverage should be fine.
+     */
+    @Test
+    @UiThreadTest
+    public void testDefaultStringValue() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mPreference.setDefaultValue(TEST_DEFAULT_STR);
+        mSharedPref.edit().putString(KEY, TEST_WRONG_STR).commit();
+        mScreen.addPreference(mPreference);
+        mSharedPref.edit().remove(KEY).commit();
+        assertEquals(TEST_DEFAULT_STR, mPreference.getDefaultValue());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutStringSetWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putStringSetTestCommon();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutStringSetWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putStringSetTestCommon();
+    }
+
+    private void putStringSetTestCommon() {
+        Set<String> testSet = ImmutableSet.of(TEST_STR);
+
+        mPreference.putStringSet(testSet);
+
+        verify(mDataStore, atLeast(0)).getStringSet(eq(KEY), or(isNull(Set.class), any(Set.class)));
+        verify(mDataStore, atLeastOnce()).putStringSet(eq(KEY),
+                or(isNull(Set.class), any(Set.class)));
+        verifyNoMoreInteractions(mDataStore);
+
+        // Test that the value was NOT propagated to SharedPreferences.
+        assertNull(mSharedPref.getStringSet(KEY, null));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetStringSetWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        Set<String> testSet = new HashSet<>();
+
+        mPreference.getStringSet(testSet);
+
+        verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetStringSetWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        Set<String> testSet = new HashSet<>();
+
+        mPreference.getStringSet(testSet);
+
+        verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutIntWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putIntTestCommon();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutIntWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putIntTestCommon();
+    }
+
+    private void putIntTestCommon() {
+        mPreference.putInt(1);
+
+        verify(mDataStore, atLeast(0)).getInt(eq(KEY), anyInt());
+        verify(mDataStore, atLeastOnce()).putInt(eq(KEY), anyInt());
+        verifyNoMoreInteractions(mDataStore);
+
+        // Test that the value was NOT propagated to SharedPreferences.
+        assertEquals(-1, mSharedPref.getInt(KEY, -1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetIntWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getInt(1);
+
+        verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetIntWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getInt(1);
+
+        verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutLongWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putLongTestCommon();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutLongWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putLongTestCommon();
+    }
+
+    private void putLongTestCommon() {
+        mPreference.putLong(1L);
+
+        verify(mDataStore, atLeast(0)).getLong(eq(KEY), anyLong());
+        verify(mDataStore, atLeastOnce()).putLong(eq(KEY), anyLong());
+        verifyNoMoreInteractions(mDataStore);
+
+        // Test that the value was NOT propagated to SharedPreferences.
+        assertEquals(-1, mSharedPref.getLong(KEY, -1L));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetLongWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getLong(1L);
+
+        verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetLongWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getLong(1L);
+
+        verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutFloatWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putFloatTestCommon();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutFloatWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putFloatTestCommon();
+    }
+
+    private void putFloatTestCommon() {
+        mPreference.putFloat(1f);
+
+        verify(mDataStore, atLeast(0)).getFloat(eq(KEY), anyFloat());
+        verify(mDataStore, atLeastOnce()).putFloat(eq(KEY), anyFloat());
+        verifyNoMoreInteractions(mDataStore);
+
+        // Test that the value was NOT propagated to SharedPreferences.
+        assertEquals(-1, mSharedPref.getFloat(KEY, -1f), 0.1f /* epsilon */);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetFloatWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getFloat(1f);
+
+        verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetFloatWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getFloat(1f);
+
+        verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutBooleanWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putBooleanTestCommon();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPutBooleanWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putBooleanTestCommon();
+    }
+
+    private void putBooleanTestCommon() {
+        mPreference.putBoolean(true);
+
+        verify(mDataStore, atLeast(0)).getBoolean(eq(KEY), anyBoolean());
+        verify(mDataStore, atLeastOnce()).putBoolean(eq(KEY), anyBoolean());
+        verifyNoMoreInteractions(mDataStore);
+
+        // Test that the value was NOT propagated to SharedPreferences.
+        assertEquals(false, mSharedPref.getBoolean(KEY, false));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetBooleanWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getBoolean(true);
+
+        verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testGetBooleanWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+
+        mPreference.getBoolean(true);
+
+        verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true));
+    }
+
+    /**
+     * When {@link PreferenceDataStore} is NOT assigned, the getter for SharedPreferences must not
+     * return null for Preference.
+     */
+    @Test
+    @UiThreadTest
+    public void testSharedPrefNotNullIfNoDS() {
+        mScreen.addPreference(mPreference);
+
+        assertNotNull(mPreference.getSharedPreferences());
+    }
+
+    /**
+     * When {@link PreferenceDataStore} is NOT assigned, the getter for SharedPreferences must not
+     * return null for PreferenceManager.
+     */
+    @Test
+    @UiThreadTest
+    public void testSharedPrefNotNullIfNoDSMgr() {
+        assertNotNull(mManager.getSharedPreferences());
+    }
+
+    /**
+     * When {@link PreferenceDataStore} is assigned, the getter for SharedPreferences has to return
+     * null for Preference.
+     */
+    @Test
+    @UiThreadTest
+    public void testSharedPrefNullIfWithDS() {
+        mScreen.addPreference(mPreference);
+
+        mPreference.setPreferenceDataStore(mDataStore);
+
+        assertNull(mPreference.getSharedPreferences());
+    }
+
+    /**
+     * When {@link PreferenceDataStore} is assigned, the getter for SharedPreferences has to return
+     * null for PreferenceManager.
+     */
+    @Test
+    @UiThreadTest
+    public void testSharedPrefNullIfWithDSMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+
+        assertNull(mManager.getSharedPreferences());
+    }
+
+}
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
new file mode 100644
index 0000000..c03f411
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference.tests;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.AndroidResources;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PreferenceIconSpaceTest {
+
+    private Preference mPreference;
+
+    @Mock
+    private ViewGroup mViewGroup;
+    @Mock
+    private ImageView mIconView;
+    @Mock
+    private View mImageFrame;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mViewGroup.findViewById(AndroidResources.ANDROID_R_ICON_FRAME))
+                .thenReturn(mImageFrame);
+        when(mViewGroup.findViewById(android.R.id.icon))
+                .thenReturn(mIconView);
+
+        mPreference = new Preference(InstrumentationRegistry.getTargetContext());
+    }
+
+    @Test
+    @UiThreadTest
+    public void bindViewHolder_iconSpaceReserved_shouldReserveIconSpace() {
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(mViewGroup);
+        mPreference.setIconSpaceReserved(true);
+        mPreference.onBindViewHolder(holder);
+
+        verify(mIconView).setVisibility(View.INVISIBLE);
+        verify(mImageFrame).setVisibility(View.INVISIBLE);
+    }
+
+    @LargeTest
+    @Test
+    @UiThreadTest
+    public void bindViewHolder_iconSpaceNotReserved_shouldNotReserveIconSpace() {
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(mViewGroup);
+        mPreference.setIconSpaceReserved(false);
+        mPreference.onBindViewHolder(holder);
+
+        verify(mIconView).setVisibility(View.GONE);
+        verify(mImageFrame).setVisibility(View.GONE);
+    }
+
+    @Test
+    @UiThreadTest
+    public void bindViewHolder_hasIcon_shouldDisplayIcon() {
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(mViewGroup);
+        mPreference.setIcon(new ColorDrawable(Color.BLACK));
+        mPreference.onBindViewHolder(holder);
+
+        verify(mIconView).setVisibility(View.VISIBLE);
+        verify(mImageFrame).setVisibility(View.VISIBLE);
+    }
+}
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceParentGroupTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceParentGroupTest.java
new file mode 100644
index 0000000..c60a003
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceParentGroupTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference.tests;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PreferenceParentGroupTest {
+
+    private Context mContext;
+
+    @Before
+    public void setup() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    /**
+     * Tests that parent PreferenceGroup is correctly assigned and removed when creating preferences
+     * from code.
+     */
+    @Test
+    @UiThreadTest
+    public void parentAddRemoveTest() {
+        PreferenceManager manager = new PreferenceManager(mContext);
+
+        PreferenceScreen screen = manager.createPreferenceScreen(mContext);
+        assertNull(screen.getParent());
+
+        PreferenceCategory category = new PreferenceCategory(mContext);
+        assertNull(category.getParent());
+
+        CheckBoxPreference pref = new CheckBoxPreference(mContext);
+        assertNull(pref.getParent());
+
+        screen.addPreference(category);
+        assertEquals(screen, category.getParent());
+
+        category.addPreference(pref);
+        assertEquals(category, pref.getParent());
+
+        screen.removePreference(category);
+        assertNull(category.getParent());
+
+        category.removePreference(pref);
+        assertNull(pref.getParent());
+    }
+
+    /**
+     * Tests that parent PreferenceGroup is correctly maintained during reassignment.
+     */
+    @Test
+    @UiThreadTest
+    public void parentReassignTest() {
+        PreferenceManager manager = new PreferenceManager(mContext);
+
+        PreferenceScreen screen = manager.createPreferenceScreen(mContext);
+
+        PreferenceCategory category1 = new PreferenceCategory(mContext);
+        screen.addPreference(category1);
+        PreferenceCategory category2 = new PreferenceCategory(mContext);
+        screen.addPreference(category2);
+
+        CheckBoxPreference pref = new CheckBoxPreference(mContext);
+        assertNull(pref.getParent());
+
+        category1.addPreference(pref);
+        assertEquals(category1, pref.getParent());
+
+        category1.removePreference(pref);
+        category2.addPreference(pref);
+        assertEquals(category2, pref.getParent());
+    }
+
+    /**
+     * Adds a preference into two different groups without removing it first. This is maybe not
+     * something we want to support in the future but this makes this behavior visible.
+     */
+    @Test
+    @UiThreadTest
+    public void parentDoubleAddTest() throws InterruptedException {
+        PreferenceManager manager = new PreferenceManager(mContext);
+
+        PreferenceScreen screen = manager.createPreferenceScreen(mContext);
+
+        PreferenceCategory category1 = new PreferenceCategory(mContext);
+        screen.addPreference(category1);
+        PreferenceCategory category2 = new PreferenceCategory(mContext);
+        screen.addPreference(category2);
+
+        CheckBoxPreference pref = new CheckBoxPreference(mContext);
+        assertNull(pref.getParent());
+
+        category1.addPreference(pref);
+        category2.addPreference(pref);
+
+        assertEquals(category2, pref.getParent());
+    }
+}
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferencePersistTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferencePersistTest.java
new file mode 100644
index 0000000..536e2a7
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferencePersistTest.java
@@ -0,0 +1,405 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference.tests;
+
+import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.espresso.core.deps.guava.collect.ImmutableSet;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.tests.helpers.PreferenceWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+/**
+ * Tests for {@link android.support.v7.preference.Preference} persist / retrieve logic.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferencePersistTest {
+
+    private static final String KEY = "TestPrefKey";
+
+    private static final float FLOAT_PRECISION = 0.01f;
+
+    private static final Set<String> TEST_STR_SET = ImmutableSet.of("a", "b");
+    private static final Set<String> TEST_STR_SET2 = ImmutableSet.of("c", "d");
+    private static final Set<String> TEST_DEFAULT_STR_SET = ImmutableSet.of("e");
+
+    private PreferenceWrapper mPreference;
+    private SharedPreferences mSharedPref;
+
+    @Before
+    @UiThreadTest
+    public void setup() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        PreferenceManager manager = new PreferenceManager(context);
+        mSharedPref = manager.getSharedPreferences();
+
+        mPreference = new PreferenceWrapper(context);
+        mPreference.setKey(KEY);
+
+        PreferenceScreen screen = manager.createPreferenceScreen(context);
+        screen.addPreference(mPreference);
+
+        // Make sure that the key is not present in SharedPreferences to ensure tests
+        // correctness.
+        mSharedPref.edit().remove(KEY).apply();
+        assertNull(mSharedPref.getString(KEY, null));
+    }
+
+    @Test
+    @UiThreadTest
+    public void string_retrieveWhenEmpty_returnsDefault() {
+        final String expected = "Default";
+
+        String result = mPreference.getString(expected);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void string_persist_getsStoredToSharedPrefs() {
+        final String expected = "Test";
+
+        boolean wasPersisted = mPreference.putString(expected);
+
+        assertTrue(wasPersisted);
+        assertEquals(expected, mSharedPref.getString(KEY, null));
+    }
+
+    @Test
+    @UiThreadTest
+    public void string_persistWhileDisabled_notPersisted() {
+        mPreference.setPersistent(false);
+
+        boolean wasPersisted = mPreference.putString("Test");
+
+        assertFalse(wasPersisted);
+        assertNull(mSharedPref.getString(KEY, null));
+    }
+
+    @Test
+    @UiThreadTest
+    public void string_persistAndRetrieve_returnsPersistedValue() {
+        final String expected = "Test";
+
+        mPreference.putString(expected);
+        String result = mPreference.getString("Default");
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void string_persistTwiceAndRetrieve_returnsSecondValue() {
+        final String expected = "Second";
+
+        mPreference.putString("First");
+        mPreference.putString(expected);
+        String result = mPreference.getString("Default");
+
+        assertEquals(expected, result);
+    }
+
+
+    @Test
+    @UiThreadTest
+    public void stringSet_retrieveWhenEmpty_returnsDefault() {
+        final Set<String> expected = TEST_DEFAULT_STR_SET;
+
+        Set<String> result = mPreference.getStringSet(expected);
+
+        assertThat(result, containsInAnyOrder(expected.toArray()));
+    }
+
+    @Test
+    @UiThreadTest
+    public void stringSet_persist_getsStoredToSharedPrefs() {
+        boolean wasPersisted = mPreference.putStringSet(TEST_DEFAULT_STR_SET);
+
+        assertTrue(wasPersisted);
+        assertThat(mSharedPref.getStringSet(KEY, null),
+                containsInAnyOrder(TEST_DEFAULT_STR_SET.toArray()));
+    }
+
+    @Test
+    @UiThreadTest
+    public void stringSet_persistWhileDisabled_notPersisted() {
+        mPreference.setPersistent(false);
+
+        boolean wasPersisted = mPreference.putStringSet(TEST_STR_SET);
+
+        assertFalse(wasPersisted);
+        assertNull(mSharedPref.getString(KEY, null));
+    }
+
+    @Test
+    @UiThreadTest
+    public void stringSet_persistAndRetrieve_returnsPersistedValue() {
+        final Set<String> expected = TEST_STR_SET;
+
+        mPreference.putStringSet(expected);
+        Set<String> result = mPreference.getStringSet(TEST_DEFAULT_STR_SET);
+
+        assertThat(result, containsInAnyOrder(expected.toArray()));
+    }
+
+    @Test
+    @UiThreadTest
+    public void stringSet_persistTwiceAndRetrieve_returnsSecondValue() {
+        final Set<String> expected = TEST_STR_SET2;
+
+        mPreference.putStringSet(TEST_STR_SET);
+        mPreference.putStringSet(expected);
+        Set<String> result = mPreference.getStringSet(TEST_DEFAULT_STR_SET);
+
+        assertThat(result, containsInAnyOrder(expected.toArray()));
+    }
+
+
+    @Test
+    @UiThreadTest
+    public void int_retrieveWhenEmpty_returnsDefault() {
+        final int expected = 1;
+        int result = mPreference.getInt(expected);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void int_persist_getsStoredToSharedPrefs() {
+        final int expected = 1;
+
+        boolean wasPersisted = mPreference.putInt(expected);
+
+        assertTrue(wasPersisted);
+        assertEquals(expected, mSharedPref.getInt(KEY, -1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void int_persistWhileDisabled_notPersisted() {
+        mPreference.setPersistent(false);
+
+        boolean wasPersisted = mPreference.putInt(1);
+
+        assertFalse(wasPersisted);
+        assertEquals(-1, mSharedPref.getLong(KEY, -1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void int_persistAndRetrieve_returnsPersistedValue() {
+        final int expected = 1;
+
+        mPreference.putInt(expected);
+        int result = mPreference.getInt(-1);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void int_persistTwiceAndRetrieve_returnsSecondValue() {
+        final int expected = 2;
+
+        mPreference.putInt(1);
+        mPreference.putInt(expected);
+        int result = mPreference.getInt(-1);
+
+        assertEquals(expected, result);
+    }
+
+
+    @Test
+    @UiThreadTest
+    public void long_retrieveWhenEmpty_returnsDefault() {
+        assertEquals(1, mPreference.getLong(1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void long_persist_getsStoredToSharedPrefs() {
+        final long expected = 1;
+
+        boolean wasPersisted = mPreference.putLong(expected);
+
+        assertTrue(wasPersisted);
+        assertEquals(expected, mSharedPref.getLong(KEY, -1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void long_persistWhileDisabled_notPersisted() {
+        mPreference.setPersistent(false);
+
+        boolean wasPersisted = mPreference.putLong(1);
+
+        assertFalse(wasPersisted);
+        assertEquals(-1, mSharedPref.getLong(KEY, -1));
+    }
+
+    @Test
+    @UiThreadTest
+    public void long_persistAndRetrieve_returnsPersistedValue() {
+        final long expected = 1;
+
+        mPreference.putLong(expected);
+        long result = mPreference.getLong(-1);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void long_persistTwiceAndRetrieve_returnsSecondValue() {
+        final long expected = 2;
+
+        mPreference.putLong(1);
+        mPreference.putLong(expected);
+        long result = mPreference.getLong(-1);
+
+        assertEquals(expected, result);
+    }
+
+
+    @Test
+    @UiThreadTest
+    public void float_retrieveWhenEmpty_returnsDefault() {
+        assertEquals(1, mPreference.getFloat(1), FLOAT_PRECISION);
+    }
+
+    @Test
+    @UiThreadTest
+    public void float_persist_getsStoredToSharedPrefs() {
+        final float expected = 1;
+
+        boolean wasPersisted = mPreference.putFloat(expected);
+
+        assertTrue(wasPersisted);
+        assertEquals(expected, mSharedPref.getFloat(KEY, -1), FLOAT_PRECISION);
+    }
+
+    @Test
+    @UiThreadTest
+    public void float_persistWhileDisabled_notPersisted() {
+        mPreference.setPersistent(false);
+
+        boolean wasPersisted = mPreference.putFloat(1);
+
+        assertFalse(wasPersisted);
+        assertEquals(-1, mSharedPref.getFloat(KEY, -1), FLOAT_PRECISION);
+    }
+
+    @Test
+    @UiThreadTest
+    public void float_persistAndRetrieve_returnsPersistedValue() {
+        final float expected = 1;
+
+        mPreference.putFloat(expected);
+        float result = mPreference.getFloat(-1);
+
+        assertEquals(expected, result, FLOAT_PRECISION);
+    }
+
+    @Test
+    @UiThreadTest
+    public void float_persistTwiceAndRetrieve_returnsSecondValue() {
+        final float expected = 2;
+
+        mPreference.putFloat(1);
+        mPreference.putFloat(expected);
+        float result = mPreference.getFloat(-1);
+
+        assertEquals(expected, result, FLOAT_PRECISION);
+    }
+
+
+    @Test
+    @UiThreadTest
+    public void boolean_retrieveWhenEmpty_returnsDefault() {
+        final boolean expected = true;
+
+        boolean result = mPreference.getBoolean(expected);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void boolean_persist_getsStoredToSharedPrefs() {
+        final boolean expected = true;
+
+        boolean wasPersisted = mPreference.putBoolean(expected);
+
+        assertTrue(wasPersisted);
+        assertEquals(expected, mSharedPref.getBoolean(KEY, !expected));
+    }
+
+    @Test
+    @UiThreadTest
+    public void boolean_persistWhileDisabled_notPersisted() {
+        mPreference.setPersistent(false);
+
+        boolean wasPersisted = mPreference.putBoolean(true);
+
+        assertFalse(wasPersisted);
+        assertEquals(false, mSharedPref.getBoolean(KEY, false));
+    }
+
+    @Test
+    @UiThreadTest
+    public void boolean_persistAndRetrieve_returnsPersistedValue() {
+        final boolean expected = true;
+
+        mPreference.putBoolean(expected);
+        boolean result = mPreference.getBoolean(!expected);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    @UiThreadTest
+    public void boolean_persistTwiceAndRetrieve_returnsSecondValue() {
+        final boolean expected = false;
+
+        mPreference.putBoolean(!expected);
+        mPreference.putBoolean(expected);
+        boolean result = mPreference.getBoolean(!expected);
+
+        assertEquals(expected, result);
+    }
+
+}
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceSingleLineTitleTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceSingleLineTitleTest.java
new file mode 100644
index 0000000..6c1cf5f
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceSingleLineTitleTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference.tests;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferenceSingleLineTitleTest {
+
+    private Preference mPreference;
+
+    @Mock
+    private ViewGroup mViewGroup;
+    @Mock
+    private TextView mTitleView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mViewGroup.findViewById(android.R.id.title)).thenReturn(mTitleView);
+
+        mPreference = new Preference(InstrumentationRegistry.getTargetContext());
+        mPreference.setTitle("Test Title");
+    }
+
+    @Test
+    public void bindViewHolder_singleLineTitleNotSet_shouldNotSetSingleLine() {
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(mViewGroup);
+        mPreference.onBindViewHolder(holder);
+
+        verify(mTitleView, never()).setSingleLine(anyBoolean());
+    }
+
+    @Test
+    public void bindViewHolder_singleLineTitleSetToTrue_shouldSetSingleLineToTrue() {
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(mViewGroup);
+        mPreference.setSingleLineTitle(true);
+        mPreference.onBindViewHolder(holder);
+
+        verify(mTitleView).setSingleLine(true);
+    }
+
+    @Test
+    public void bindViewHolder_singleLineTitleSetToFalse_shouldSetSingleLineToFalse() {
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(mViewGroup);
+        mPreference.setSingleLineTitle(false);
+        mPreference.onBindViewHolder(holder);
+
+        verify(mTitleView).setSingleLine(false);
+    }
+
+}
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/helpers/PreferenceWrapper.java b/v7/preference/tests/src/android/support/v7/preference/tests/helpers/PreferenceWrapper.java
new file mode 100644
index 0000000..b2674a5
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/helpers/PreferenceWrapper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.preference.tests.helpers;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import java.util.Set;
+
+/**
+ * Wrapper to allow to easily call protected methods.
+ */
+public final class PreferenceWrapper extends Preference {
+
+    Object mDefaultValue;
+
+    public PreferenceWrapper(Context context) {
+        super(context);
+    }
+
+    public Object getDefaultValue() {
+        return mDefaultValue;
+    }
+
+    public boolean putString(String value) {
+        return persistString(value);
+    }
+
+    public String getString(String defaultValue) {
+        return getPersistedString(defaultValue);
+    }
+
+    public boolean putStringSet(Set<String> values) {
+        return persistStringSet(values);
+    }
+
+    public Set<String> getStringSet(Set<String> defaultValues) {
+        return getPersistedStringSet(defaultValues);
+    }
+
+    public boolean putInt(int value) {
+        return persistInt(value);
+    }
+
+    public int getInt(int defaultValue) {
+        return getPersistedInt(defaultValue);
+    }
+
+    public boolean putLong(long value) {
+        return persistLong(value);
+    }
+
+    public long getLong(long defaultValue) {
+        return getPersistedLong(defaultValue);
+    }
+
+    public boolean putFloat(float value) {
+        return persistFloat(value);
+    }
+
+    public float getFloat(float defaultValue) {
+        return getPersistedFloat(defaultValue);
+    }
+
+    public boolean putBoolean(boolean value) {
+        return persistBoolean(value);
+    }
+
+    public boolean getBoolean(boolean defaultValue) {
+        return getPersistedBoolean(defaultValue);
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
+        this.mDefaultValue = defaultValue;
+        super.onSetInitialValue(restorePersistedValue, defaultValue);
+    }
+}
diff --git a/v7/recyclerview/Android.mk b/v7/recyclerview/Android.mk
index 819b104..e434ab2 100644
--- a/v7/recyclerview/Android.mk
+++ b/v7/recyclerview/Android.mk
@@ -29,7 +29,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-core-ui \
diff --git a/v7/recyclerview/AndroidManifest-make.xml b/v7/recyclerview/AndroidManifest-make.xml
deleted file mode 100644
index d1c1489..0000000
--- a/v7/recyclerview/AndroidManifest-make.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v7.recyclerview">
-    <uses-sdk android:minSdkVersion="9"/>
-</manifest>
diff --git a/v7/recyclerview/AndroidManifest.xml b/v7/recyclerview/AndroidManifest.xml
index eb3d53a..5b03882 100644
--- a/v7/recyclerview/AndroidManifest.xml
+++ b/v7/recyclerview/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.v7.recyclerview">
-    <uses-sdk android:minSdkVersion="9"/>
+    <uses-sdk android:minSdkVersion="14"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index e262ec7..06cdc53 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -1,49 +1,39 @@
-apply plugin: 'com.android.library'
+apply plugin: android.support.SupportLibraryPlugin
 archivesBaseName = 'recyclerview-v7'
 
 dependencies {
-    compile project(':support-annotations')
-    compile project(':support-compat')
-    compile project(':support-core-ui')
-    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    api project(':support-annotations')
+    api project(':support-compat')
+    api project(':support-core-ui')
+
+    androidTestImplementation (libs.test_runner) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+    androidTestImplementation (libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    testCompile 'junit:junit:4.12'
-    testCompile "org.mockito:mockito-core:1.9.5"
-    testCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+    androidTestImplementation libs.junit
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+    androidTestImplementation project(':support-testutils')
+
+    testImplementation libs.junit
+    testImplementation libs.mockito_core
+    testImplementation ("$libs.test_runner") {
         exclude module: 'support-annotations'
     }
-    androidTestCompile "org.mockito:mockito-core:1.9.5"
-    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
-    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
 }
 
 android {
-    compileSdkVersion project.ext.currentSdk
-
     defaultConfig {
-        minSdkVersion 9
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 14
     }
 
     sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
         main.res.srcDirs 'res', 'res-public'
 
-        androidTest.setRoot('tests')
         test.java.srcDir 'jvm-tests/src'
-        androidTest.java.srcDir 'tests/src'
-        androidTest.res.srcDir 'tests/res'
-        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 
     testOptions {
@@ -55,52 +45,8 @@
     }
 }
 
-android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
-
-    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
-        return; // Skip debug builds.
-    }
-    def suffix = name.capitalize()
-
-    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
-        classifier = 'sources'
-        from android.sourceSets.main.java.srcDirs
-    }
-
-    artifacts.add('archives', sourcesJarTask);
-}
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri(rootProject.ext.supportRepoOut)) {
-            }
-
-            pom.project {
-                name 'Android Support RecyclerView v7'
-                description "Android Support RecyclerView v7"
-                url 'http://developer.android.com/tools/extras/support-library.html'
-                inceptionYear '2011'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "http://source.android.com"
-                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
+supportLibrary {
+    name 'Android Support RecyclerView v7'
+    inceptionYear '2014'
+    description 'Android Support RecyclerView v7'
 }
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java
index 9315a19..4ac406a 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java
@@ -22,7 +22,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.support.annotation.Nullable;
-import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
 
 import org.hamcrest.CoreMatchers;
@@ -102,8 +101,9 @@
         check();
     }
 
-    @Test
-    @LargeTest
+    //@Test
+    //@LargeTest
+    // Used for development
     public void testRandom() {
         for (int x = 0; x < 100; x++) {
             for (int i = 0; i < 100; i++) {
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
index c303c60..e0dbde5 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
@@ -1185,5 +1185,10 @@
         public MockViewHolder(View itemView) {
             super(itemView);
         }
+
+        @Override
+        public String toString() {
+            return mItem == null ? "null" : mItem.toString();
+        }
     }
 }
diff --git a/v7/recyclerview/lint-baseline.xml b/v7/recyclerview/lint-baseline.xml
new file mode 100644
index 0000000..e961253
--- /dev/null
+++ b/v7/recyclerview/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+</issues>
diff --git a/v7/recyclerview/res-public/values/public_attrs.xml b/v7/recyclerview/res-public/values/public_attrs.xml
index 3da5fd7..ca0b8e7 100644
--- a/v7/recyclerview/res-public/values/public_attrs.xml
+++ b/v7/recyclerview/res-public/values/public_attrs.xml
@@ -19,4 +19,9 @@
     <public type="attr" name="spanCount" format="integer"/>
     <public type="attr" name="reverseLayout"/>
     <public type="attr" name="stackFromEnd"/>
+    <public type="attr" name="fastScrollEnabled" format="boolean" />
+    <public type="attr" name="fastScrollVerticalThumbDrawable" format="reference" />
+    <public type="attr" name="fastScrollVerticalTrackDrawable" format="reference" />
+    <public type="attr" name="fastScrollHorizontalThumbDrawable" format="reference" />
+    <public type="attr" name="fastScrollHorizontalTrackDrawable" format="reference" />
 </resources>
diff --git a/v7/recyclerview/res/values/attrs.xml b/v7/recyclerview/res/values/attrs.xml
index a0d73c9..0a983a7 100644
--- a/v7/recyclerview/res/values/attrs.xml
+++ b/v7/recyclerview/res/values/attrs.xml
@@ -37,5 +37,10 @@
         <attr name="spanCount" format="integer"/>
         <attr name="reverseLayout" format="boolean" />
         <attr name="stackFromEnd" format="boolean" />
+        <attr name="fastScrollEnabled" format="boolean" />
+        <attr name="fastScrollVerticalThumbDrawable" format="reference" />
+        <attr name="fastScrollVerticalTrackDrawable" format="reference" />
+        <attr name="fastScrollHorizontalThumbDrawable" format="reference" />
+        <attr name="fastScrollHorizontalTrackDrawable" format="reference" />
     </declare-styleable>
 </resources>
\ No newline at end of file
diff --git a/v7/recyclerview/res/values/dimens.xml b/v7/recyclerview/res/values/dimens.xml
index 90c41b9..bd5cdb4 100644
--- a/v7/recyclerview/res/values/dimens.xml
+++ b/v7/recyclerview/res/values/dimens.xml
@@ -21,4 +21,8 @@
     <dimen name="item_touch_helper_max_drag_scroll_per_frame">20dp</dimen>
     <dimen name="item_touch_helper_swipe_escape_velocity">120dp</dimen>
     <dimen name="item_touch_helper_swipe_escape_max_velocity">800dp</dimen>
+
+    <dimen name="fastscroll_default_thickness">8dp</dimen>
+    <dimen name="fastscroll_minimum_range">50dp</dimen>
+    <dimen name="fastscroll_margin">0dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/v7/recyclerview/src/android/support/v7/util/DiffUtil.java b/v7/recyclerview/src/android/support/v7/util/DiffUtil.java
index 6f0a078..6302666 100644
--- a/v7/recyclerview/src/android/support/v7/util/DiffUtil.java
+++ b/v7/recyclerview/src/android/support/v7/util/DiffUtil.java
@@ -205,7 +205,7 @@
                 // we can reach k from k - 1 or k + 1. Check which one is further in the graph
                 int x;
                 final boolean removal;
-                if (k == -d || k != d && forward[kOffset + k - 1] < forward[kOffset + k + 1]) {
+                if (k == -d || (k != d && forward[kOffset + k - 1] < forward[kOffset + k + 1])) {
                     x = forward[kOffset + k + 1];
                     removal = false;
                 } else {
@@ -238,8 +238,8 @@
                 final int backwardK = k + delta;
                 int x;
                 final boolean removal;
-                if (backwardK == d + delta || backwardK != -d + delta
-                        && backward[kOffset + backwardK - 1] < backward[kOffset + backwardK + 1]) {
+                if (backwardK == d + delta || (backwardK != -d + delta
+                        && backward[kOffset + backwardK - 1] < backward[kOffset + backwardK + 1])) {
                     x = backward[kOffset + backwardK - 1];
                     removal = false;
                 } else {
diff --git a/v7/recyclerview/src/android/support/v7/util/MessageThreadUtil.java b/v7/recyclerview/src/android/support/v7/util/MessageThreadUtil.java
index 523801a..a932894 100644
--- a/v7/recyclerview/src/android/support/v7/util/MessageThreadUtil.java
+++ b/v7/recyclerview/src/android/support/v7/util/MessageThreadUtil.java
@@ -16,9 +16,9 @@
 
 package android.support.v7.util;
 
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.v4.content.ParallelExecutorCompat;
 import android.util.Log;
 
 import java.util.concurrent.Executor;
@@ -86,7 +86,7 @@
     public BackgroundCallback<T> getBackgroundProxy(final BackgroundCallback<T> callback) {
         return new BackgroundCallback<T>() {
             final MessageQueue mQueue = new MessageQueue();
-            final private Executor mExecutor = ParallelExecutorCompat.getParallelExecutor();
+            private final Executor mExecutor = AsyncTask.THREAD_POOL_EXECUTOR;
             AtomicBoolean mBackgroundRunning = new AtomicBoolean(false);
 
             static final int REFRESH = 1;
diff --git a/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java b/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java
index c133a98..663e484 100644
--- a/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java
@@ -43,9 +43,9 @@
  */
 class AdapterHelper implements OpReorderer.Callback {
 
-    final static int POSITION_TYPE_INVISIBLE = 0;
+    static final int POSITION_TYPE_INVISIBLE = 0;
 
-    final static int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
+    static final int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
 
     private static final boolean DEBUG = false;
 
@@ -286,7 +286,7 @@
                 if (op.cmd == UpdateOp.UPDATE) {
                     offsetPositionForPartial += tmpCnt;
                 }
-                tmpStart = updatedPos;// need to remove previously dispatched
+                tmpStart = updatedPos; // need to remove previously dispatched
                 tmpCnt = 1;
             }
         }
@@ -585,7 +585,7 @@
 
     public int applyPendingUpdatesToPosition(int position) {
         final int size = mPendingUpdates.size();
-        for (int i = 0; i < size; i ++) {
+        for (int i = 0; i < size; i++) {
             UpdateOp op = mPendingUpdates.get(i);
             switch (op.cmd) {
                 case UpdateOp.ADD:
@@ -604,7 +604,7 @@
                     break;
                 case UpdateOp.MOVE:
                     if (op.positionStart == position) {
-                        position = op.itemCount;//position end
+                        position = op.itemCount; //position end
                     } else {
                         if (op.positionStart < position) {
                             position -= 1;
@@ -672,7 +672,7 @@
         public String toString() {
             return Integer.toHexString(System.identityHashCode(this))
                     + "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount
-                    +",p:"+payload + "]";
+                    + ",p:" + payload + "]";
         }
 
         @Override
diff --git a/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java b/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
index dd75b19..f306931 100644
--- a/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
@@ -121,7 +121,7 @@
             final int diff = index - (offset - removedBefore);
             if (diff == 0) {
                 while (mBucket.get(offset)) { // ensure this offset is not hidden
-                    offset ++;
+                    offset++;
                 }
                 return offset;
             } else {
@@ -238,8 +238,8 @@
         }
         mCallback.attachViewToParent(child, offset, layoutParams);
         if (DEBUG) {
-            Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," +
-                    "h:" + hidden + ", " + this);
+            Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + ","
+                    + "h:" + hidden + ", " + this);
         }
     }
 
@@ -335,7 +335,7 @@
         mBucket.set(offset);
         hideViewInternal(view);
         if (DEBUG) {
-            Log.d(TAG, "hiding child " + view + " at offset " + offset+ ", " + this);
+            Log.d(TAG, "hiding child " + view + " at offset " + offset + ", " + this);
         }
     }
 
@@ -394,33 +394,33 @@
      */
     static class Bucket {
 
-        final static int BITS_PER_WORD = Long.SIZE;
+        static final int BITS_PER_WORD = Long.SIZE;
 
-        final static long LAST_BIT = 1L << (Long.SIZE - 1);
+        static final long LAST_BIT = 1L << (Long.SIZE - 1);
 
         long mData = 0;
 
-        Bucket next;
+        Bucket mNext;
 
         void set(int index) {
             if (index >= BITS_PER_WORD) {
                 ensureNext();
-                next.set(index - BITS_PER_WORD);
+                mNext.set(index - BITS_PER_WORD);
             } else {
                 mData |= 1L << index;
             }
         }
 
         private void ensureNext() {
-            if (next == null) {
-                next = new Bucket();
+            if (mNext == null) {
+                mNext = new Bucket();
             }
         }
 
         void clear(int index) {
             if (index >= BITS_PER_WORD) {
-                if (next != null) {
-                    next.clear(index - BITS_PER_WORD);
+                if (mNext != null) {
+                    mNext.clear(index - BITS_PER_WORD);
                 }
             } else {
                 mData &= ~(1L << index);
@@ -431,7 +431,7 @@
         boolean get(int index) {
             if (index >= BITS_PER_WORD) {
                 ensureNext();
-                return next.get(index - BITS_PER_WORD);
+                return mNext.get(index - BITS_PER_WORD);
             } else {
                 return (mData & (1L << index)) != 0;
             }
@@ -439,15 +439,15 @@
 
         void reset() {
             mData = 0;
-            if (next != null) {
-                next.reset();
+            if (mNext != null) {
+                mNext.reset();
             }
         }
 
         void insert(int index, boolean value) {
             if (index >= BITS_PER_WORD) {
                 ensureNext();
-                next.insert(index - BITS_PER_WORD, value);
+                mNext.insert(index - BITS_PER_WORD, value);
             } else {
                 final boolean lastBit = (mData & LAST_BIT) != 0;
                 long mask = (1L << index) - 1;
@@ -459,9 +459,9 @@
                 } else {
                     clear(index);
                 }
-                if (lastBit || next != null) {
+                if (lastBit || mNext != null) {
                     ensureNext();
-                    next.insert(0, lastBit);
+                    mNext.insert(0, lastBit);
                 }
             }
         }
@@ -469,7 +469,7 @@
         boolean remove(int index) {
             if (index >= BITS_PER_WORD) {
                 ensureNext();
-                return next.remove(index - BITS_PER_WORD);
+                return mNext.remove(index - BITS_PER_WORD);
             } else {
                 long mask = (1L << index);
                 final boolean value = (mData & mask) != 0;
@@ -479,18 +479,18 @@
                 // cannot use >> because it adds one.
                 final long after = Long.rotateRight(mData & ~mask, 1);
                 mData = before | after;
-                if (next != null) {
-                    if (next.get(0)) {
+                if (mNext != null) {
+                    if (mNext.get(0)) {
                         set(BITS_PER_WORD - 1);
                     }
-                    next.remove(0);
+                    mNext.remove(0);
                 }
                 return value;
             }
         }
 
         int countOnesBefore(int index) {
-            if (next == null) {
+            if (mNext == null) {
                 if (index >= BITS_PER_WORD) {
                     return Long.bitCount(mData);
                 }
@@ -499,18 +499,18 @@
             if (index < BITS_PER_WORD) {
                 return Long.bitCount(mData & ((1L << index) - 1));
             } else {
-                return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
+                return mNext.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
             }
         }
 
         @Override
         public String toString() {
-            return next == null ? Long.toBinaryString(mData)
-                    : next.toString() + "xx" + Long.toBinaryString(mData);
+            return mNext == null ? Long.toBinaryString(mData)
+                    : mNext.toString() + "xx" + Long.toBinaryString(mData);
         }
     }
 
-    static interface Callback {
+    interface Callback {
 
         int getChildCount();
 
diff --git a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
index f388c3f..11600a8 100644
--- a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
+++ b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
@@ -15,13 +15,15 @@
  */
 package android.support.v7.widget;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.support.annotation.NonNull;
-import android.support.v4.animation.AnimatorCompatHelper;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
+import android.view.ViewPropertyAnimator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,6 +38,8 @@
 public class DefaultItemAnimator extends SimpleItemAnimator {
     private static final boolean DEBUG = false;
 
+    private static TimeInterpolator sDefaultInterpolator;
+
     private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
     private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
     private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
@@ -82,14 +86,14 @@
 
         @Override
         public String toString() {
-            return "ChangeInfo{" +
-                    "oldHolder=" + oldHolder +
-                    ", newHolder=" + newHolder +
-                    ", fromX=" + fromX +
-                    ", fromY=" + fromY +
-                    ", toX=" + toX +
-                    ", toY=" + toY +
-                    '}';
+            return "ChangeInfo{"
+                    + "oldHolder=" + oldHolder
+                    + ", newHolder=" + newHolder
+                    + ", fromX=" + fromX
+                    + ", fromY=" + fromY
+                    + ", toX=" + toX
+                    + ", toY=" + toY
+                    + '}';
         }
     }
 
@@ -193,51 +197,52 @@
 
     private void animateRemoveImpl(final ViewHolder holder) {
         final View view = holder.itemView;
-        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
+        final ViewPropertyAnimator animation = view.animate();
         mRemoveAnimations.add(holder);
-        animation.setDuration(getRemoveDuration())
-                .alpha(0).setListener(new VpaListenerAdapter() {
-            @Override
-            public void onAnimationStart(View view) {
-                dispatchRemoveStarting(holder);
-            }
+        animation.setDuration(getRemoveDuration()).alpha(0).setListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animator) {
+                        dispatchRemoveStarting(holder);
+                    }
 
-            @Override
-            public void onAnimationEnd(View view) {
-                animation.setListener(null);
-                ViewCompat.setAlpha(view, 1);
-                dispatchRemoveFinished(holder);
-                mRemoveAnimations.remove(holder);
-                dispatchFinishedWhenDone();
-            }
-        }).start();
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        animation.setListener(null);
+                        view.setAlpha(1);
+                        dispatchRemoveFinished(holder);
+                        mRemoveAnimations.remove(holder);
+                        dispatchFinishedWhenDone();
+                    }
+                }).start();
     }
 
     @Override
     public boolean animateAdd(final ViewHolder holder) {
         resetAnimation(holder);
-        ViewCompat.setAlpha(holder.itemView, 0);
+        holder.itemView.setAlpha(0);
         mPendingAdditions.add(holder);
         return true;
     }
 
     void animateAddImpl(final ViewHolder holder) {
         final View view = holder.itemView;
-        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
+        final ViewPropertyAnimator animation = view.animate();
         mAddAnimations.add(holder);
-        animation.alpha(1).setDuration(getAddDuration()).
-                setListener(new VpaListenerAdapter() {
+        animation.alpha(1).setDuration(getAddDuration())
+                .setListener(new AnimatorListenerAdapter() {
                     @Override
-                    public void onAnimationStart(View view) {
+                    public void onAnimationStart(Animator animator) {
                         dispatchAddStarting(holder);
                     }
-                    @Override
-                    public void onAnimationCancel(View view) {
-                        ViewCompat.setAlpha(view, 1);
-                    }
 
                     @Override
-                    public void onAnimationEnd(View view) {
+                    public void onAnimationCancel(Animator animator) {
+                        view.setAlpha(1);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
                         animation.setListener(null);
                         dispatchAddFinished(holder);
                         mAddAnimations.remove(holder);
@@ -250,8 +255,8 @@
     public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
             int toX, int toY) {
         final View view = holder.itemView;
-        fromX += ViewCompat.getTranslationX(holder.itemView);
-        fromY += ViewCompat.getTranslationY(holder.itemView);
+        fromX += (int) holder.itemView.getTranslationX();
+        fromY += (int) holder.itemView.getTranslationY();
         resetAnimation(holder);
         int deltaX = toX - fromX;
         int deltaY = toY - fromY;
@@ -260,10 +265,10 @@
             return false;
         }
         if (deltaX != 0) {
-            ViewCompat.setTranslationX(view, -deltaX);
+            view.setTranslationX(-deltaX);
         }
         if (deltaY != 0) {
-            ViewCompat.setTranslationY(view, -deltaY);
+            view.setTranslationY(-deltaY);
         }
         mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
         return true;
@@ -274,32 +279,34 @@
         final int deltaX = toX - fromX;
         final int deltaY = toY - fromY;
         if (deltaX != 0) {
-            ViewCompat.animate(view).translationX(0);
+            view.animate().translationX(0);
         }
         if (deltaY != 0) {
-            ViewCompat.animate(view).translationY(0);
+            view.animate().translationY(0);
         }
         // TODO: make EndActions end listeners instead, since end actions aren't called when
         // vpas are canceled (and can't end them. why?)
         // need listener functionality in VPACompat for this. Ick.
-        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
+        final ViewPropertyAnimator animation = view.animate();
         mMoveAnimations.add(holder);
-        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
+        animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationStart(View view) {
+            public void onAnimationStart(Animator animator) {
                 dispatchMoveStarting(holder);
             }
+
             @Override
-            public void onAnimationCancel(View view) {
+            public void onAnimationCancel(Animator animator) {
                 if (deltaX != 0) {
-                    ViewCompat.setTranslationX(view, 0);
+                    view.setTranslationX(0);
                 }
                 if (deltaY != 0) {
-                    ViewCompat.setTranslationY(view, 0);
+                    view.setTranslationY(0);
                 }
             }
+
             @Override
-            public void onAnimationEnd(View view) {
+            public void onAnimationEnd(Animator animator) {
                 animation.setListener(null);
                 dispatchMoveFinished(holder);
                 mMoveAnimations.remove(holder);
@@ -316,22 +323,22 @@
             // run a move animation to handle position changes.
             return animateMove(oldHolder, fromX, fromY, toX, toY);
         }
-        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
-        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
-        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
+        final float prevTranslationX = oldHolder.itemView.getTranslationX();
+        final float prevTranslationY = oldHolder.itemView.getTranslationY();
+        final float prevAlpha = oldHolder.itemView.getAlpha();
         resetAnimation(oldHolder);
         int deltaX = (int) (toX - fromX - prevTranslationX);
         int deltaY = (int) (toY - fromY - prevTranslationY);
         // recover prev translation state after ending animation
-        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
-        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
-        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
+        oldHolder.itemView.setTranslationX(prevTranslationX);
+        oldHolder.itemView.setTranslationY(prevTranslationY);
+        oldHolder.itemView.setAlpha(prevAlpha);
         if (newHolder != null) {
             // carry over translation values
             resetAnimation(newHolder);
-            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
-            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
-            ViewCompat.setAlpha(newHolder.itemView, 0);
+            newHolder.itemView.setTranslationX(-deltaX);
+            newHolder.itemView.setTranslationY(-deltaY);
+            newHolder.itemView.setAlpha(0);
         }
         mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
         return true;
@@ -343,23 +350,23 @@
         final ViewHolder newHolder = changeInfo.newHolder;
         final View newView = newHolder != null ? newHolder.itemView : null;
         if (view != null) {
-            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
+            final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
                     getChangeDuration());
             mChangeAnimations.add(changeInfo.oldHolder);
             oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
             oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
-            oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
+            oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationStart(View view) {
+                public void onAnimationStart(Animator animator) {
                     dispatchChangeStarting(changeInfo.oldHolder, true);
                 }
 
                 @Override
-                public void onAnimationEnd(View view) {
+                public void onAnimationEnd(Animator animator) {
                     oldViewAnim.setListener(null);
-                    ViewCompat.setAlpha(view, 1);
-                    ViewCompat.setTranslationX(view, 0);
-                    ViewCompat.setTranslationY(view, 0);
+                    view.setAlpha(1);
+                    view.setTranslationX(0);
+                    view.setTranslationY(0);
                     dispatchChangeFinished(changeInfo.oldHolder, true);
                     mChangeAnimations.remove(changeInfo.oldHolder);
                     dispatchFinishedWhenDone();
@@ -367,25 +374,25 @@
             }).start();
         }
         if (newView != null) {
-            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
+            final ViewPropertyAnimator newViewAnimation = newView.animate();
             mChangeAnimations.add(changeInfo.newHolder);
-            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
-                    alpha(1).setListener(new VpaListenerAdapter() {
-                @Override
-                public void onAnimationStart(View view) {
-                    dispatchChangeStarting(changeInfo.newHolder, false);
-                }
-                @Override
-                public void onAnimationEnd(View view) {
-                    newViewAnimation.setListener(null);
-                    ViewCompat.setAlpha(newView, 1);
-                    ViewCompat.setTranslationX(newView, 0);
-                    ViewCompat.setTranslationY(newView, 0);
-                    dispatchChangeFinished(changeInfo.newHolder, false);
-                    mChangeAnimations.remove(changeInfo.newHolder);
-                    dispatchFinishedWhenDone();
-                }
-            }).start();
+            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
+                    .alpha(1).setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animator) {
+                            dispatchChangeStarting(changeInfo.newHolder, false);
+                        }
+                        @Override
+                        public void onAnimationEnd(Animator animator) {
+                            newViewAnimation.setListener(null);
+                            newView.setAlpha(1);
+                            newView.setTranslationX(0);
+                            newView.setTranslationY(0);
+                            dispatchChangeFinished(changeInfo.newHolder, false);
+                            mChangeAnimations.remove(changeInfo.newHolder);
+                            dispatchFinishedWhenDone();
+                        }
+                    }).start();
         }
     }
 
@@ -418,9 +425,9 @@
         } else {
             return false;
         }
-        ViewCompat.setAlpha(item.itemView, 1);
-        ViewCompat.setTranslationX(item.itemView, 0);
-        ViewCompat.setTranslationY(item.itemView, 0);
+        item.itemView.setAlpha(1);
+        item.itemView.setTranslationX(0);
+        item.itemView.setTranslationY(0);
         dispatchChangeFinished(item, oldItem);
         return true;
     }
@@ -429,24 +436,24 @@
     public void endAnimation(ViewHolder item) {
         final View view = item.itemView;
         // this will trigger end callback which should set properties to their target values.
-        ViewCompat.animate(view).cancel();
+        view.animate().cancel();
         // TODO if some other animations are chained to end, how do we cancel them as well?
         for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
             MoveInfo moveInfo = mPendingMoves.get(i);
             if (moveInfo.holder == item) {
-                ViewCompat.setTranslationY(view, 0);
-                ViewCompat.setTranslationX(view, 0);
+                view.setTranslationY(0);
+                view.setTranslationX(0);
                 dispatchMoveFinished(item);
                 mPendingMoves.remove(i);
             }
         }
         endChangeAnimation(mPendingChanges, item);
         if (mPendingRemovals.remove(item)) {
-            ViewCompat.setAlpha(view, 1);
+            view.setAlpha(1);
             dispatchRemoveFinished(item);
         }
         if (mPendingAdditions.remove(item)) {
-            ViewCompat.setAlpha(view, 1);
+            view.setAlpha(1);
             dispatchAddFinished(item);
         }
 
@@ -462,8 +469,8 @@
             for (int j = moves.size() - 1; j >= 0; j--) {
                 MoveInfo moveInfo = moves.get(j);
                 if (moveInfo.holder == item) {
-                    ViewCompat.setTranslationY(view, 0);
-                    ViewCompat.setTranslationX(view, 0);
+                    view.setTranslationY(0);
+                    view.setTranslationX(0);
                     dispatchMoveFinished(item);
                     moves.remove(j);
                     if (moves.isEmpty()) {
@@ -476,7 +483,7 @@
         for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
             ArrayList<ViewHolder> additions = mAdditionsList.get(i);
             if (additions.remove(item)) {
-                ViewCompat.setAlpha(view, 1);
+                view.setAlpha(1);
                 dispatchAddFinished(item);
                 if (additions.isEmpty()) {
                     mAdditionsList.remove(i);
@@ -512,23 +519,26 @@
     }
 
     private void resetAnimation(ViewHolder holder) {
-        AnimatorCompatHelper.clearInterpolator(holder.itemView);
+        if (sDefaultInterpolator == null) {
+            sDefaultInterpolator = new ValueAnimator().getInterpolator();
+        }
+        holder.itemView.animate().setInterpolator(sDefaultInterpolator);
         endAnimation(holder);
     }
 
     @Override
     public boolean isRunning() {
-        return (!mPendingAdditions.isEmpty() ||
-                !mPendingChanges.isEmpty() ||
-                !mPendingMoves.isEmpty() ||
-                !mPendingRemovals.isEmpty() ||
-                !mMoveAnimations.isEmpty() ||
-                !mRemoveAnimations.isEmpty() ||
-                !mAddAnimations.isEmpty() ||
-                !mChangeAnimations.isEmpty() ||
-                !mMovesList.isEmpty() ||
-                !mAdditionsList.isEmpty() ||
-                !mChangesList.isEmpty());
+        return (!mPendingAdditions.isEmpty()
+                || !mPendingChanges.isEmpty()
+                || !mPendingMoves.isEmpty()
+                || !mPendingRemovals.isEmpty()
+                || !mMoveAnimations.isEmpty()
+                || !mRemoveAnimations.isEmpty()
+                || !mAddAnimations.isEmpty()
+                || !mChangeAnimations.isEmpty()
+                || !mMovesList.isEmpty()
+                || !mAdditionsList.isEmpty()
+                || !mChangesList.isEmpty());
     }
 
     /**
@@ -548,8 +558,8 @@
         for (int i = count - 1; i >= 0; i--) {
             MoveInfo item = mPendingMoves.get(i);
             View view = item.holder.itemView;
-            ViewCompat.setTranslationY(view, 0);
-            ViewCompat.setTranslationX(view, 0);
+            view.setTranslationY(0);
+            view.setTranslationX(0);
             dispatchMoveFinished(item.holder);
             mPendingMoves.remove(i);
         }
@@ -562,8 +572,7 @@
         count = mPendingAdditions.size();
         for (int i = count - 1; i >= 0; i--) {
             ViewHolder item = mPendingAdditions.get(i);
-            View view = item.itemView;
-            ViewCompat.setAlpha(view, 1);
+            item.itemView.setAlpha(1);
             dispatchAddFinished(item);
             mPendingAdditions.remove(i);
         }
@@ -584,8 +593,8 @@
                 MoveInfo moveInfo = moves.get(j);
                 ViewHolder item = moveInfo.holder;
                 View view = item.itemView;
-                ViewCompat.setTranslationY(view, 0);
-                ViewCompat.setTranslationX(view, 0);
+                view.setTranslationY(0);
+                view.setTranslationX(0);
                 dispatchMoveFinished(moveInfo.holder);
                 moves.remove(j);
                 if (moves.isEmpty()) {
@@ -600,7 +609,7 @@
             for (int j = count - 1; j >= 0; j--) {
                 ViewHolder item = additions.get(j);
                 View view = item.itemView;
-                ViewCompat.setAlpha(view, 1);
+                view.setAlpha(1);
                 dispatchAddFinished(item);
                 additions.remove(j);
                 if (additions.isEmpty()) {
@@ -630,7 +639,7 @@
 
     void cancelAll(List<ViewHolder> viewHolders) {
         for (int i = viewHolders.size() - 1; i >= 0; i--) {
-            ViewCompat.animate(viewHolders.get(i).itemView).cancel();
+            viewHolders.get(i).itemView.animate().cancel();
         }
     }
 
@@ -655,18 +664,4 @@
             @NonNull List<Object> payloads) {
         return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
     }
-
-    private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
-        VpaListenerAdapter() {
-        }
-
-        @Override
-        public void onAnimationStart(View view) {}
-
-        @Override
-        public void onAnimationEnd(View view) {}
-
-        @Override
-        public void onAnimationCancel(View view) {}
-    }
 }
diff --git a/v7/recyclerview/src/android/support/v7/widget/DividerItemDecoration.java b/v7/recyclerview/src/android/support/v7/widget/DividerItemDecoration.java
index b3b7cd1..80524eb 100644
--- a/v7/recyclerview/src/android/support/v7/widget/DividerItemDecoration.java
+++ b/v7/recyclerview/src/android/support/v7/widget/DividerItemDecoration.java
@@ -17,14 +17,13 @@
 
 package android.support.v7.widget;
 
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
+import android.util.Log;
 import android.view.View;
 import android.widget.LinearLayout;
 
@@ -43,6 +42,7 @@
     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
     public static final int VERTICAL = LinearLayout.VERTICAL;
 
+    private static final String TAG = "DividerItem";
     private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
 
     private Drawable mDivider;
@@ -64,6 +64,10 @@
     public DividerItemDecoration(Context context, int orientation) {
         final TypedArray a = context.obtainStyledAttributes(ATTRS);
         mDivider = a.getDrawable(0);
+        if (mDivider == null) {
+            Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
+                    + "DividerItemDecoration. Please set that attribute all call setDrawable()");
+        }
         a.recycle();
         setOrientation(orientation);
     }
@@ -96,7 +100,7 @@
 
     @Override
     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        if (parent.getLayoutManager() == null) {
+        if (parent.getLayoutManager() == null || mDivider == null) {
             return;
         }
         if (mOrientation == VERTICAL) {
@@ -106,11 +110,11 @@
         }
     }
 
-    @SuppressLint("NewApi")
     private void drawVertical(Canvas canvas, RecyclerView parent) {
         canvas.save();
         final int left;
         final int right;
+        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
         if (parent.getClipToPadding()) {
             left = parent.getPaddingLeft();
             right = parent.getWidth() - parent.getPaddingRight();
@@ -125,7 +129,7 @@
         for (int i = 0; i < childCount; i++) {
             final View child = parent.getChildAt(i);
             parent.getDecoratedBoundsWithMargins(child, mBounds);
-            final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
+            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
             final int top = bottom - mDivider.getIntrinsicHeight();
             mDivider.setBounds(left, top, right, bottom);
             mDivider.draw(canvas);
@@ -133,11 +137,11 @@
         canvas.restore();
     }
 
-    @SuppressLint("NewApi")
     private void drawHorizontal(Canvas canvas, RecyclerView parent) {
         canvas.save();
         final int top;
         final int bottom;
+        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
         if (parent.getClipToPadding()) {
             top = parent.getPaddingTop();
             bottom = parent.getHeight() - parent.getPaddingBottom();
@@ -152,7 +156,7 @@
         for (int i = 0; i < childCount; i++) {
             final View child = parent.getChildAt(i);
             parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
-            final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));
+            final int right = mBounds.right + Math.round(child.getTranslationX());
             final int left = right - mDivider.getIntrinsicWidth();
             mDivider.setBounds(left, top, right, bottom);
             mDivider.draw(canvas);
@@ -163,6 +167,10 @@
     @Override
     public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
             RecyclerView.State state) {
+        if (mDivider == null) {
+            outRect.set(0, 0, 0, 0);
+            return;
+        }
         if (mOrientation == VERTICAL) {
             outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
         } else {
diff --git a/v7/recyclerview/src/android/support/v7/widget/FastScroller.java b/v7/recyclerview/src/android/support/v7/widget/FastScroller.java
new file mode 100644
index 0000000..fbe234b
--- /dev/null
+++ b/v7/recyclerview/src/android/support/v7/widget/FastScroller.java
@@ -0,0 +1,586 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.RecyclerView.ItemDecoration;
+import android.support.v7.widget.RecyclerView.OnItemTouchListener;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class responsible to animate and provide a fast scroller.
+ */
+@VisibleForTesting
+class FastScroller extends ItemDecoration implements OnItemTouchListener {
+    @IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface State { }
+    // Scroll thumb not showing
+    private static final int STATE_HIDDEN = 0;
+    // Scroll thumb visible and moving along with the scrollbar
+    private static final int STATE_VISIBLE = 1;
+    // Scroll thumb being dragged by user
+    private static final int STATE_DRAGGING = 2;
+
+    @IntDef({DRAG_X, DRAG_Y, DRAG_NONE})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface DragState{ }
+    private static final int DRAG_NONE = 0;
+    private static final int DRAG_X = 1;
+    private static final int DRAG_Y = 2;
+
+    @IntDef({ANIMATION_STATE_OUT, ANIMATION_STATE_FADING_IN, ANIMATION_STATE_IN,
+        ANIMATION_STATE_FADING_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface AnimationState { }
+    private static final int ANIMATION_STATE_OUT = 0;
+    private static final int ANIMATION_STATE_FADING_IN = 1;
+    private static final int ANIMATION_STATE_IN = 2;
+    private static final int ANIMATION_STATE_FADING_OUT = 3;
+
+    private static final int SHOW_DURATION_MS = 500;
+    private static final int HIDE_DELAY_AFTER_VISIBLE_MS = 1500;
+    private static final int HIDE_DELAY_AFTER_DRAGGING_MS = 1200;
+    private static final int HIDE_DURATION_MS = 500;
+    private static final int SCROLLBAR_FULL_OPAQUE = 255;
+
+    private static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed};
+    private static final int[] EMPTY_STATE_SET = new int[]{};
+
+    private final int mScrollbarMinimumRange;
+    private final int mMargin;
+
+    // Final values for the vertical scroll bar
+    private final StateListDrawable mVerticalThumbDrawable;
+    private final Drawable mVerticalTrackDrawable;
+    private final int mVerticalThumbWidth;
+    private final int mVerticalTrackWidth;
+
+    // Final values for the horizontal scroll bar
+    private final StateListDrawable mHorizontalThumbDrawable;
+    private final Drawable mHorizontalTrackDrawable;
+    private final int mHorizontalThumbHeight;
+    private final int mHorizontalTrackHeight;
+
+    // Dynamic values for the vertical scroll bar
+    @VisibleForTesting int mVerticalThumbHeight;
+    @VisibleForTesting int mVerticalThumbCenterY;
+    @VisibleForTesting float mVerticalDragY;
+
+    // Dynamic values for the horizontal scroll bar
+    @VisibleForTesting int mHorizontalThumbWidth;
+    @VisibleForTesting int mHorizontalThumbCenterX;
+    @VisibleForTesting float mHorizontalDragX;
+
+    private int mRecyclerViewWidth = 0;
+    private int mRecyclerViewHeight = 0;
+
+    private RecyclerView mRecyclerView;
+    /**
+     * Whether the document is long/wide enough to require scrolling. If not, we don't show the
+     * relevant scroller.
+     */
+    private boolean mNeedVerticalScrollbar = false;
+    private boolean mNeedHorizontalScrollbar = false;
+    @State private int mState = STATE_HIDDEN;
+    @DragState private int mDragState = DRAG_NONE;
+
+    private final int[] mVerticalRange = new int[2];
+    private final int[] mHorizontalRange = new int[2];
+    private final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);
+    @AnimationState private int mAnimationState = ANIMATION_STATE_OUT;
+    private final Runnable mHideRunnable = new Runnable() {
+        @Override
+        public void run() {
+            hide(HIDE_DURATION_MS);
+        }
+    };
+    private final OnScrollListener mOnScrollListener = new OnScrollListener() {
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+            updateScrollPosition(recyclerView.computeHorizontalScrollOffset(),
+                    recyclerView.computeVerticalScrollOffset());
+        }
+    };
+
+    FastScroller(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable,
+            Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
+            Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange,
+            int margin) {
+        mVerticalThumbDrawable = verticalThumbDrawable;
+        mVerticalTrackDrawable = verticalTrackDrawable;
+        mHorizontalThumbDrawable = horizontalThumbDrawable;
+        mHorizontalTrackDrawable = horizontalTrackDrawable;
+        mVerticalThumbWidth = Math.max(defaultWidth, verticalThumbDrawable.getIntrinsicWidth());
+        mVerticalTrackWidth = Math.max(defaultWidth, verticalTrackDrawable.getIntrinsicWidth());
+        mHorizontalThumbHeight = Math
+            .max(defaultWidth, horizontalThumbDrawable.getIntrinsicWidth());
+        mHorizontalTrackHeight = Math
+            .max(defaultWidth, horizontalTrackDrawable.getIntrinsicWidth());
+        mScrollbarMinimumRange = scrollbarMinimumRange;
+        mMargin = margin;
+        mVerticalThumbDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE);
+        mVerticalTrackDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE);
+
+        mShowHideAnimator.addListener(new AnimatorListener());
+        mShowHideAnimator.addUpdateListener(new AnimatorUpdater());
+
+        attachToRecyclerView(recyclerView);
+    }
+
+    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
+        if (mRecyclerView == recyclerView) {
+            return; // nothing to do
+        }
+        if (mRecyclerView != null) {
+            destroyCallbacks();
+        }
+        mRecyclerView = recyclerView;
+        if (mRecyclerView != null) {
+            setupCallbacks();
+        }
+    }
+
+    private void setupCallbacks() {
+        mRecyclerView.addItemDecoration(this);
+        mRecyclerView.addOnItemTouchListener(this);
+        mRecyclerView.addOnScrollListener(mOnScrollListener);
+    }
+
+    private void destroyCallbacks() {
+        mRecyclerView.removeItemDecoration(this);
+        mRecyclerView.removeOnItemTouchListener(this);
+        mRecyclerView.removeOnScrollListener(mOnScrollListener);
+        cancelHide();
+    }
+
+    private void requestRedraw() {
+        mRecyclerView.invalidate();
+    }
+
+    private void setState(@State int state) {
+        if (state == STATE_DRAGGING && mState != STATE_DRAGGING) {
+            mVerticalThumbDrawable.setState(PRESSED_STATE_SET);
+            cancelHide();
+        }
+
+        if (state == STATE_HIDDEN) {
+            requestRedraw();
+        } else {
+            show();
+        }
+
+        if (mState == STATE_DRAGGING && state != STATE_DRAGGING) {
+            mVerticalThumbDrawable.setState(EMPTY_STATE_SET);
+            resetHideDelay(HIDE_DELAY_AFTER_DRAGGING_MS);
+        } else if (state == STATE_VISIBLE) {
+            resetHideDelay(HIDE_DELAY_AFTER_VISIBLE_MS);
+        }
+        mState = state;
+    }
+
+    private boolean isLayoutRTL() {
+        return ViewCompat.getLayoutDirection(mRecyclerView) == ViewCompat.LAYOUT_DIRECTION_RTL;
+    }
+
+    public boolean isDragging() {
+        return mState == STATE_DRAGGING;
+    }
+
+    @VisibleForTesting boolean isVisible() {
+        return mState == STATE_VISIBLE;
+    }
+
+    @VisibleForTesting boolean isHidden() {
+        return mState == STATE_HIDDEN;
+    }
+
+
+    public void show() {
+        switch (mAnimationState) {
+            case ANIMATION_STATE_FADING_OUT:
+                mShowHideAnimator.cancel();
+                // fall through
+            case ANIMATION_STATE_OUT:
+                mAnimationState = ANIMATION_STATE_FADING_IN;
+                mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 1);
+                mShowHideAnimator.setDuration(SHOW_DURATION_MS);
+                mShowHideAnimator.setStartDelay(0);
+                mShowHideAnimator.start();
+                break;
+        }
+    }
+
+    public void hide() {
+        hide(0);
+    }
+
+    @VisibleForTesting
+    void hide(int duration) {
+        switch (mAnimationState) {
+            case ANIMATION_STATE_FADING_IN:
+                mShowHideAnimator.cancel();
+                // fall through
+            case ANIMATION_STATE_IN:
+                mAnimationState = ANIMATION_STATE_FADING_OUT;
+                mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 0);
+                mShowHideAnimator.setDuration(duration);
+                mShowHideAnimator.start();
+                break;
+        }
+    }
+
+    private void cancelHide() {
+        mRecyclerView.removeCallbacks(mHideRunnable);
+    }
+
+    private void resetHideDelay(int delay) {
+        cancelHide();
+        mRecyclerView.postDelayed(mHideRunnable, delay);
+    }
+
+    @Override
+    public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
+        if (mRecyclerViewWidth != mRecyclerView.getWidth()
+                || mRecyclerViewHeight != mRecyclerView.getHeight()) {
+            mRecyclerViewWidth = mRecyclerView.getWidth();
+            mRecyclerViewHeight = mRecyclerView.getHeight();
+            // This is due to the different events ordering when keyboard is opened or
+            // retracted vs rotate. Hence to avoid corner cases we just disable the
+            // scroller when size changed, and wait until the scroll position is recomputed
+            // before showing it back.
+            setState(STATE_HIDDEN);
+            return;
+        }
+
+        if (mAnimationState != ANIMATION_STATE_OUT) {
+            if (mNeedVerticalScrollbar) {
+                drawVerticalScrollbar(canvas);
+            }
+            if (mNeedHorizontalScrollbar) {
+                drawHorizontalScrollbar(canvas);
+            }
+        }
+    }
+
+    private void drawVerticalScrollbar(Canvas canvas) {
+        int viewWidth = mRecyclerViewWidth;
+
+        int left = viewWidth - mVerticalThumbWidth;
+        int top = mVerticalThumbCenterY - mVerticalThumbHeight / 2;
+        mVerticalThumbDrawable.setBounds(0, 0, mVerticalThumbWidth, mVerticalThumbHeight);
+        mVerticalTrackDrawable
+            .setBounds(0, 0, mVerticalTrackWidth, mRecyclerViewHeight);
+
+        if (isLayoutRTL()) {
+            mVerticalTrackDrawable.draw(canvas);
+            canvas.translate(mVerticalThumbWidth, top);
+            canvas.scale(-1, 1);
+            mVerticalThumbDrawable.draw(canvas);
+            canvas.scale(1, 1);
+            canvas.translate(-mVerticalThumbWidth, -top);
+        } else {
+            canvas.translate(left, 0);
+            mVerticalTrackDrawable.draw(canvas);
+            canvas.translate(0, top);
+            mVerticalThumbDrawable.draw(canvas);
+            canvas.translate(-left, -top);
+        }
+    }
+
+    private void drawHorizontalScrollbar(Canvas canvas) {
+        int viewHeight = mRecyclerViewHeight;
+
+        int top = viewHeight - mHorizontalThumbHeight;
+        int left = mHorizontalThumbCenterX - mHorizontalThumbWidth / 2;
+        mHorizontalThumbDrawable.setBounds(0, 0, mHorizontalThumbWidth, mHorizontalThumbHeight);
+        mHorizontalTrackDrawable
+            .setBounds(0, 0, mRecyclerViewWidth, mHorizontalTrackHeight);
+
+        canvas.translate(0, top);
+        mHorizontalTrackDrawable.draw(canvas);
+        canvas.translate(left, 0);
+        mHorizontalThumbDrawable.draw(canvas);
+        canvas.translate(-left, -top);
+    }
+
+    /**
+     * Notify the scroller of external change of the scroll, e.g. through dragging or flinging on
+     * the view itself.
+     *
+     * @param offsetX The new scroll X offset.
+     * @param offsetY The new scroll Y offset.
+     */
+    void updateScrollPosition(int offsetX, int offsetY) {
+        int verticalContentLength = mRecyclerView.computeVerticalScrollRange();
+        int verticalVisibleLength = mRecyclerViewHeight;
+        mNeedVerticalScrollbar = verticalContentLength - verticalVisibleLength > 0
+            && mRecyclerViewHeight >= mScrollbarMinimumRange;
+
+        int horizontalContentLength = mRecyclerView.computeHorizontalScrollRange();
+        int horizontalVisibleLength = mRecyclerViewWidth;
+        mNeedHorizontalScrollbar = horizontalContentLength - horizontalVisibleLength > 0
+            && mRecyclerViewWidth >= mScrollbarMinimumRange;
+
+        if (!mNeedVerticalScrollbar && !mNeedHorizontalScrollbar) {
+            if (mState != STATE_HIDDEN) {
+                setState(STATE_HIDDEN);
+            }
+            return;
+        }
+
+        if (mNeedVerticalScrollbar) {
+            float middleScreenPos = offsetY + verticalVisibleLength / 2.0f;
+            mVerticalThumbCenterY =
+                (int) ((verticalVisibleLength * middleScreenPos) / verticalContentLength);
+            mVerticalThumbHeight = Math.min(verticalVisibleLength,
+                (verticalVisibleLength * verticalVisibleLength) / verticalContentLength);
+        }
+
+        if (mNeedHorizontalScrollbar) {
+            float middleScreenPos = offsetX + horizontalVisibleLength / 2.0f;
+            mHorizontalThumbCenterX =
+                (int) ((horizontalVisibleLength * middleScreenPos) / horizontalContentLength);
+            mHorizontalThumbWidth = Math.min(horizontalVisibleLength,
+                (horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength);
+        }
+
+        if (mState == STATE_HIDDEN || mState == STATE_VISIBLE) {
+            setState(STATE_VISIBLE);
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent ev) {
+        final boolean handled;
+        if (mState == STATE_VISIBLE) {
+            boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY());
+            boolean insideHorizontalThumb = isPointInsideHorizontalThumb(ev.getX(), ev.getY());
+            if (ev.getAction() == MotionEvent.ACTION_DOWN
+                    && (insideVerticalThumb || insideHorizontalThumb)) {
+                if (insideHorizontalThumb) {
+                    mDragState = DRAG_X;
+                    mHorizontalDragX = (int) ev.getX();
+                } else if (insideVerticalThumb) {
+                    mDragState = DRAG_Y;
+                    mVerticalDragY = (int) ev.getY();
+                }
+
+                setState(STATE_DRAGGING);
+                handled = true;
+            } else {
+                handled = false;
+            }
+        } else if (mState == STATE_DRAGGING) {
+            handled = true;
+        } else {
+            handled = false;
+        }
+        return handled;
+    }
+
+    @Override
+    public void onTouchEvent(RecyclerView recyclerView, MotionEvent me) {
+        if (mState == STATE_HIDDEN) {
+            return;
+        }
+
+        if (me.getAction() == MotionEvent.ACTION_DOWN) {
+            boolean insideVerticalThumb = isPointInsideVerticalThumb(me.getX(), me.getY());
+            boolean insideHorizontalThumb = isPointInsideHorizontalThumb(me.getX(), me.getY());
+            if (insideVerticalThumb || insideHorizontalThumb) {
+                if (insideHorizontalThumb) {
+                    mDragState = DRAG_X;
+                    mHorizontalDragX = (int) me.getX();
+                } else if (insideVerticalThumb) {
+                    mDragState = DRAG_Y;
+                    mVerticalDragY = (int) me.getY();
+                }
+                setState(STATE_DRAGGING);
+            }
+        } else if (me.getAction() == MotionEvent.ACTION_UP && mState == STATE_DRAGGING) {
+            mVerticalDragY = 0;
+            mHorizontalDragX = 0;
+            setState(STATE_VISIBLE);
+            mDragState = DRAG_NONE;
+        } else if (me.getAction() == MotionEvent.ACTION_MOVE && mState == STATE_DRAGGING) {
+            show();
+            if (mDragState == DRAG_X) {
+                horizontalScrollTo(me.getX());
+            }
+            if (mDragState == DRAG_Y) {
+                verticalScrollTo(me.getY());
+            }
+        }
+    }
+
+    @Override
+    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
+
+    private void verticalScrollTo(float y) {
+        final int[] scrollbarRange = getVerticalRange();
+        y = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], y));
+        if (Math.abs(mVerticalThumbCenterY - y) < 2) {
+            return;
+        }
+        int scrollingBy = scrollTo(mVerticalDragY, y, scrollbarRange,
+                mRecyclerView.computeVerticalScrollRange(),
+                mRecyclerView.computeVerticalScrollOffset(), mRecyclerViewHeight);
+        if (scrollingBy != 0) {
+            mRecyclerView.scrollBy(0, scrollingBy);
+        }
+        mVerticalDragY = y;
+    }
+
+    private void horizontalScrollTo(float x) {
+        final int[] scrollbarRange = getHorizontalRange();
+        x = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], x));
+        if (Math.abs(mHorizontalThumbCenterX - x) < 2) {
+            return;
+        }
+
+        int scrollingBy = scrollTo(mHorizontalDragX, x, scrollbarRange,
+                mRecyclerView.computeHorizontalScrollRange(),
+                mRecyclerView.computeHorizontalScrollOffset(), mRecyclerViewWidth);
+        if (scrollingBy != 0) {
+            mRecyclerView.scrollBy(scrollingBy, 0);
+        }
+
+        mHorizontalDragX = x;
+    }
+
+    private int scrollTo(float oldDragPos, float newDragPos, int[] scrollbarRange, int scrollRange,
+            int scrollOffset, int viewLength) {
+        int scrollbarLength = scrollbarRange[1] - scrollbarRange[0];
+        if (scrollbarLength == 0) {
+            return 0;
+        }
+        float percentage = ((newDragPos - oldDragPos) / (float) scrollbarLength);
+        int totalPossibleOffset = scrollRange - viewLength;
+        int scrollingBy = (int) (percentage * totalPossibleOffset);
+        int absoluteOffset = scrollOffset + scrollingBy;
+        if (absoluteOffset < totalPossibleOffset && absoluteOffset >= 0) {
+            return scrollingBy;
+        } else {
+            return 0;
+        }
+    }
+
+    @VisibleForTesting
+    boolean isPointInsideVerticalThumb(float x, float y) {
+        return (isLayoutRTL() ? x <= mVerticalThumbWidth / 2
+            : x >= mRecyclerViewWidth - mVerticalThumbWidth)
+            && y >= mVerticalThumbCenterY - mVerticalThumbHeight / 2
+            && y <= mVerticalThumbCenterY + mVerticalThumbHeight / 2;
+    }
+
+    @VisibleForTesting
+    boolean isPointInsideHorizontalThumb(float x, float y) {
+        return (y >= mRecyclerViewHeight - mHorizontalThumbHeight)
+            && x >= mHorizontalThumbCenterX - mHorizontalThumbWidth / 2
+            && x <= mHorizontalThumbCenterX + mHorizontalThumbWidth / 2;
+    }
+
+    @VisibleForTesting
+    Drawable getHorizontalTrackDrawable() {
+        return mHorizontalTrackDrawable;
+    }
+
+    @VisibleForTesting
+    Drawable getHorizontalThumbDrawable() {
+        return mHorizontalThumbDrawable;
+    }
+
+    @VisibleForTesting
+    Drawable getVerticalTrackDrawable() {
+        return mVerticalTrackDrawable;
+    }
+
+    @VisibleForTesting
+    Drawable getVerticalThumbDrawable() {
+        return mVerticalThumbDrawable;
+    }
+
+    /**
+     * Gets the (min, max) vertical positions of the vertical scroll bar.
+     */
+    private int[] getVerticalRange() {
+        mVerticalRange[0] = mMargin;
+        mVerticalRange[1] = mRecyclerViewHeight - mMargin;
+        return mVerticalRange;
+    }
+
+    /**
+     * Gets the (min, max) horizontal positions of the horizontal scroll bar.
+     */
+    private int[] getHorizontalRange() {
+        mHorizontalRange[0] = mMargin;
+        mHorizontalRange[1] = mRecyclerViewWidth - mMargin;
+        return mHorizontalRange;
+    }
+
+    private class AnimatorListener extends AnimatorListenerAdapter {
+
+        private boolean mCanceled = false;
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            // Cancel is always followed by a new directive, so don't update state.
+            if (mCanceled) {
+                mCanceled = false;
+                return;
+            }
+            if ((float) mShowHideAnimator.getAnimatedValue() == 0) {
+                mAnimationState = ANIMATION_STATE_OUT;
+                setState(STATE_HIDDEN);
+            } else {
+                mAnimationState = ANIMATION_STATE_IN;
+                requestRedraw();
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mCanceled = true;
+        }
+    }
+
+    private class AnimatorUpdater implements AnimatorUpdateListener {
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator valueAnimator) {
+            int alpha = (int) (SCROLLBAR_FULL_OPAQUE * ((float) valueAnimator.getAnimatedValue()));
+            mVerticalThumbDrawable.setAlpha(alpha);
+            mVerticalTrackDrawable.setAlpha(alpha);
+            requestRedraw();
+        }
+    }
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index 64df96c..dfc7c12 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -432,8 +432,8 @@
                     if (invalidMatch == null) {
                         invalidMatch = view; // removed item, least preferred
                     }
-                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||
-                        mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
+                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
+                        || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
                     if (outOfBoundsMatch == null) {
                         outOfBoundsMatch = view; // item is not visible, less preferred
                     }
@@ -545,8 +545,8 @@
             int pos = layoutState.mCurrentPosition;
             final int spanSize = getSpanSize(recycler, state, pos);
             if (spanSize > mSpanCount) {
-                throw new IllegalArgumentException("Item at position " + pos + " requires " +
-                        spanSize + " spans but GridLayoutManager has only " + mSpanCount
+                throw new IllegalArgumentException("Item at position " + pos + " requires "
+                        + spanSize + " spans but GridLayoutManager has only " + mSpanCount
                         + " spans.");
             }
             remainingSpan -= spanSize;
@@ -595,8 +595,8 @@
                 maxSize = size;
             }
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view) /
-                    lp.mSpanSize;
+            final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view)
+                    / lp.mSpanSize;
             if (otherSize > maxSizeInOther) {
                 maxSizeInOther = otherSize;
             }
@@ -618,7 +618,7 @@
 
         // Views that did not measure the maxSize has to be re-measured
         // We will stop doing this once we introduce Gravity in the GLM layout params
-        for (int i = 0; i < count; i ++) {
+        for (int i = 0; i < count; i++) {
             final View view = mSet[i];
             if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
                 final LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -826,7 +826,7 @@
      *
      * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
      */
-    public static abstract class SpanSizeLookup {
+    public abstract static class SpanSizeLookup {
 
         final SparseIntArray mSpanIndexCache = new SparseIntArray();
 
@@ -838,7 +838,7 @@
          * @param position The adapter position of the item
          * @return The number of spans occupied by the item at the provided position
          */
-        abstract public int getSpanSize(int position);
+        public abstract int getSpanSize(int position);
 
         /**
          * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
diff --git a/v7/recyclerview/src/android/support/v7/widget/LayoutState.java b/v7/recyclerview/src/android/support/v7/widget/LayoutState.java
index 23d8ee8..f75da1c 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LayoutState.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LayoutState.java
@@ -24,23 +24,23 @@
  */
 class LayoutState {
 
-    final static String TAG = "LayoutState";
+    static final String TAG = "LayoutState";
 
-    final static int LAYOUT_START = -1;
+    static final int LAYOUT_START = -1;
 
-    final static int LAYOUT_END = 1;
+    static final int LAYOUT_END = 1;
 
-    final static int INVALID_LAYOUT = Integer.MIN_VALUE;
+    static final int INVALID_LAYOUT = Integer.MIN_VALUE;
 
-    final static int ITEM_DIRECTION_HEAD = -1;
+    static final int ITEM_DIRECTION_HEAD = -1;
 
-    final static int ITEM_DIRECTION_TAIL = 1;
+    static final int ITEM_DIRECTION_TAIL = 1;
 
     /**
      * We may not want to recycle children in some cases (e.g. layout)
      */
     boolean mRecycle = true;
-    
+
     /**
      * Number of pixels that we should fill, in the layout direction.
      */
@@ -104,13 +104,13 @@
 
     @Override
     public String toString() {
-        return "LayoutState{" +
-                "mAvailable=" + mAvailable +
-                ", mCurrentPosition=" + mCurrentPosition +
-                ", mItemDirection=" + mItemDirection +
-                ", mLayoutDirection=" + mLayoutDirection +
-                ", mStartLine=" + mStartLine +
-                ", mEndLine=" + mEndLine +
-                '}';
+        return "LayoutState{"
+                + "mAvailable=" + mAvailable
+                + ", mCurrentPosition=" + mCurrentPosition
+                + ", mItemDirection=" + mItemDirection
+                + ", mLayoutDirection=" + mLayoutDirection
+                + ", mStartLine=" + mStartLine
+                + ", mEndLine=" + mEndLine
+                + '}';
     }
 }
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index 0ec3b50..a12e9a8 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -27,8 +27,6 @@
 import android.support.annotation.RestrictTo;
 import android.support.v4.os.TraceCompat;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.support.v7.widget.RecyclerView.LayoutParams;
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.util.AttributeSet;
@@ -135,9 +133,9 @@
     SavedState mPendingSavedState = null;
 
     /**
-    *  Re-used variable to keep anchor information on re-layout.
-    *  Anchor position and coordinate defines the reference point for LLM while doing a layout.
-    * */
+     *  Re-used variable to keep anchor information on re-layout.
+     *  Anchor position and coordinate defines the reference point for LLM while doing a layout.
+     * */
     final AnchorInfo mAnchorInfo = new AnchorInfo();
 
     /**
@@ -180,7 +178,7 @@
      * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
      */
     public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
-                               int defStyleRes) {
+            int defStyleRes) {
         Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
         setOrientation(properties.orientation);
         setReverseLayout(properties.reverseLayout);
@@ -238,10 +236,8 @@
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         if (getChildCount() > 0) {
-            final AccessibilityRecordCompat record = AccessibilityEventCompat
-                    .asRecord(event);
-            record.setFromIndex(findFirstVisibleItemPosition());
-            record.setToIndex(findLastVisibleItemPosition());
+            event.setFromIndex(findFirstVisibleItemPosition());
+            event.setToIndex(findLastVisibleItemPosition());
         }
     }
 
@@ -257,14 +253,14 @@
             state.mAnchorLayoutFromEnd = didLayoutFromEnd;
             if (didLayoutFromEnd) {
                 final View refChild = getChildClosestToEnd();
-                state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() -
-                        mOrientationHelper.getDecoratedEnd(refChild);
+                state.mAnchorOffset = mOrientationHelper.getEndAfterPadding()
+                        - mOrientationHelper.getDecoratedEnd(refChild);
                 state.mAnchorPosition = getPosition(refChild);
             } else {
                 final View refChild = getChildClosestToStart();
                 state.mAnchorPosition = getPosition(refChild);
-                state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) -
-                        mOrientationHelper.getStartAfterPadding();
+                state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild)
+                        - mOrientationHelper.getStartAfterPadding();
             }
         } else {
             state.invalidateAnchor();
@@ -495,13 +491,30 @@
         // resolve layout direction
         resolveShouldLayoutReverse();
 
-        if (!mAnchorInfo.mValid || mPendingScrollPosition != NO_POSITION ||
-                mPendingSavedState != null) {
+        final View focused = getFocusedChild();
+        if (!mAnchorInfo.mValid || mPendingScrollPosition != NO_POSITION
+                || mPendingSavedState != null) {
             mAnchorInfo.reset();
             mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
             // calculate anchor position and coordinate
             updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
             mAnchorInfo.mValid = true;
+        } else if (focused != null && (mOrientationHelper.getDecoratedStart(focused)
+                        >= mOrientationHelper.getEndAfterPadding()
+                || mOrientationHelper.getDecoratedEnd(focused)
+                <= mOrientationHelper.getStartAfterPadding())) {
+            // This case relates to when the anchor child is the focused view and due to layout
+            // shrinking the focused view fell outside the viewport, e.g. when soft keyboard shows
+            // up after tapping an EditText which shrinks RV causing the focused view (The tapped
+            // EditText which is the anchor child) to get kicked out of the screen. Will update the
+            // anchor coordinate in order to make sure that the focused view is laid out. Otherwise,
+            // the available space in layoutState will be calculated as negative preventing the
+            // focused view from being laid out in fill.
+            // Note that we won't update the anchor position between layout passes (refer to
+            // TestResizingRelayoutWithAutoMeasure), which happens if we were to call
+            // updateAnchorInfoForLayout for an anchor that's not the focused view (e.g. a reference
+            // child which can change between layout passes).
+            mAnchorInfo.assignFromViewAndKeepVisibleRect(focused);
         }
         if (DEBUG) {
             Log.d(TAG, "Anchor info:" + mAnchorInfo);
@@ -523,8 +536,8 @@
         }
         extraForStart += mOrientationHelper.getStartAfterPadding();
         extraForEnd += mOrientationHelper.getEndPadding();
-        if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION &&
-                mPendingScrollPositionOffset != INVALID_OFFSET) {
+        if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION
+                && mPendingScrollPositionOffset != INVALID_OFFSET) {
             // if the child is visible and we are going to move it around, we should layout
             // extra items in the opposite direction to make sure new items animate nicely
             // instead of just fading in
@@ -533,8 +546,8 @@
                 final int current;
                 final int upcomingOffset;
                 if (mShouldReverseLayout) {
-                    current = mOrientationHelper.getEndAfterPadding() -
-                            mOrientationHelper.getDecoratedEnd(existing);
+                    current = mOrientationHelper.getEndAfterPadding()
+                            - mOrientationHelper.getDecoratedEnd(existing);
                     upcomingOffset = current - mPendingScrollPositionOffset;
                 } else {
                     current = mOrientationHelper.getDecoratedStart(existing)
@@ -552,11 +565,11 @@
         int endOffset;
         final int firstLayoutDirection;
         if (mAnchorInfo.mLayoutFromEnd) {
-            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :
-                    LayoutState.ITEM_DIRECTION_HEAD;
+            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
+                    : LayoutState.ITEM_DIRECTION_HEAD;
         } else {
-            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
-                    LayoutState.ITEM_DIRECTION_TAIL;
+            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
+                    : LayoutState.ITEM_DIRECTION_TAIL;
         }
 
         onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
@@ -669,7 +682,7 @@
      *                                 indices.
      */
     void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
-                       AnchorInfo anchorInfo, int firstLayoutItemDirection) {
+            AnchorInfo anchorInfo, int firstLayoutItemDirection) {
     }
 
     /**
@@ -732,7 +745,7 @@
     }
 
     private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state,
-                                           AnchorInfo anchorInfo) {
+            AnchorInfo anchorInfo) {
         if (updateAnchorFromPendingData(state, anchorInfo)) {
             if (DEBUG) {
                 Log.d(TAG, "updated anchor info from pending information");
@@ -760,7 +773,7 @@
      * If a child has focus, it is given priority.
      */
     private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler,
-                                             RecyclerView.State state, AnchorInfo anchorInfo) {
+            RecyclerView.State state, AnchorInfo anchorInfo) {
         if (getChildCount() == 0) {
             return false;
         }
@@ -823,11 +836,11 @@
             // according to our current view bounds
             anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
             if (anchorInfo.mLayoutFromEnd) {
-                anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
-                        mPendingSavedState.mAnchorOffset;
+                anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding()
+                        - mPendingSavedState.mAnchorOffset;
             } else {
-                anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +
-                        mPendingSavedState.mAnchorOffset;
+                anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding()
+                        + mPendingSavedState.mAnchorOffset;
             }
             return true;
         }
@@ -848,8 +861,8 @@
                     anchorInfo.mLayoutFromEnd = false;
                     return true;
                 }
-                final int endGap = mOrientationHelper.getEndAfterPadding() -
-                        mOrientationHelper.getDecoratedEnd(child);
+                final int endGap = mOrientationHelper.getEndAfterPadding()
+                        - mOrientationHelper.getDecoratedEnd(child);
                 if (endGap < 0) {
                     anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();
                     anchorInfo.mLayoutFromEnd = true;
@@ -874,11 +887,11 @@
         anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
         // if this changes, we should update prepareForDrop as well
         if (mShouldReverseLayout) {
-            anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
-                    mPendingScrollPositionOffset;
+            anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding()
+                    - mPendingScrollPositionOffset;
         } else {
-            anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +
-                    mPendingScrollPositionOffset;
+            anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding()
+                    + mPendingScrollPositionOffset;
         }
         return true;
     }
@@ -1288,15 +1301,6 @@
         return mInitialPrefetchItemCount;
     }
 
-
-    /**
-     * @deprecated Use {@link #getInitialPrefetchItemCount()} instead.
-     */
-    @Deprecated
-    public int getInitialItemPrefetchCount() {
-        return getInitialPrefetchItemCount();
-    }
-
     @Override
     public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
             LayoutPrefetchRegistry layoutPrefetchRegistry) {
@@ -1306,6 +1310,7 @@
             return;
         }
 
+        ensureLayoutState();
         final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
         final int absDy = Math.abs(delta);
         updateLayoutState(layoutDirection, absDy, true, state);
@@ -1743,7 +1748,7 @@
      * @return A View that can be used an an anchor View.
      */
     private View findReferenceChildClosestToEnd(RecyclerView.Recycler recycler,
-                                                RecyclerView.State state) {
+            RecyclerView.State state) {
         return mShouldReverseLayout ? findFirstReferenceChild(recycler, state) :
                 findLastReferenceChild(recycler, state);
     }
@@ -1760,7 +1765,7 @@
      * @return A View that can be used an an anchor View.
      */
     private View findReferenceChildClosestToStart(RecyclerView.Recycler recycler,
-                                                  RecyclerView.State state) {
+            RecyclerView.State state) {
         return mShouldReverseLayout ? findLastReferenceChild(recycler, state) :
                 findFirstReferenceChild(recycler, state);
     }
@@ -1775,7 +1780,7 @@
 
     // overridden by GridLayoutManager
     View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
-                            int start, int end, int itemCount) {
+            int start, int end, int itemCount) {
         ensureLayoutState();
         View invalidMatch = null;
         View outOfBoundsMatch = null;
@@ -1790,8 +1795,8 @@
                     if (invalidMatch == null) {
                         invalidMatch = view; // removed item, least preferred
                     }
-                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||
-                        mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
+                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
+                        || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
                     if (outOfBoundsMatch == null) {
                         outOfBoundsMatch = view; // item is not visible, less preferred
                     }
@@ -2048,8 +2053,8 @@
                 int screenLoc = mOrientationHelper.getDecoratedStart(child);
                 if (pos < lastPos) {
                     logChildren();
-                    throw new RuntimeException("detected invalid position. loc invalid? " +
-                            (screenLoc < lastScreenLoc));
+                    throw new RuntimeException("detected invalid position. loc invalid? "
+                            + (screenLoc < lastScreenLoc));
                 }
                 if (screenLoc > lastScreenLoc) {
                     logChildren();
@@ -2063,8 +2068,8 @@
                 int screenLoc = mOrientationHelper.getDecoratedStart(child);
                 if (pos < lastPos) {
                     logChildren();
-                    throw new RuntimeException("detected invalid position. loc invalid? " +
-                            (screenLoc < lastScreenLoc));
+                    throw new RuntimeException("detected invalid position. loc invalid? "
+                            + (screenLoc < lastScreenLoc));
                 }
                 if (screenLoc < lastScreenLoc) {
                     logChildren();
@@ -2090,8 +2095,8 @@
         resolveShouldLayoutReverse();
         final int myPos = getPosition(view);
         final int targetPos = getPosition(target);
-        final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL :
-                LayoutState.ITEM_DIRECTION_HEAD;
+        final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL
+                : LayoutState.ITEM_DIRECTION_HEAD;
         if (mShouldReverseLayout) {
             if (dropDirection == LayoutState.ITEM_DIRECTION_TAIL) {
                 scrollToPositionWithOffset(targetPos,
@@ -2100,16 +2105,16 @@
                                 + mOrientationHelper.getDecoratedMeasurement(view)));
             } else {
                 scrollToPositionWithOffset(targetPos,
-                        mOrientationHelper.getEndAfterPadding() -
-                                mOrientationHelper.getDecoratedEnd(target));
+                        mOrientationHelper.getEndAfterPadding()
+                                - mOrientationHelper.getDecoratedEnd(target));
             }
         } else {
             if (dropDirection == LayoutState.ITEM_DIRECTION_HEAD) {
                 scrollToPositionWithOffset(targetPos, mOrientationHelper.getDecoratedStart(target));
             } else {
                 scrollToPositionWithOffset(targetPos,
-                        mOrientationHelper.getDecoratedEnd(target) -
-                                mOrientationHelper.getDecoratedMeasurement(view));
+                        mOrientationHelper.getDecoratedEnd(target)
+                                - mOrientationHelper.getDecoratedMeasurement(view));
             }
         }
     }
@@ -2120,19 +2125,19 @@
      */
     static class LayoutState {
 
-        final static String TAG = "LLM#LayoutState";
+        static final String TAG = "LLM#LayoutState";
 
-        final static int LAYOUT_START = -1;
+        static final int LAYOUT_START = -1;
 
-        final static int LAYOUT_END = 1;
+        static final int LAYOUT_END = 1;
 
-        final static int INVALID_LAYOUT = Integer.MIN_VALUE;
+        static final int INVALID_LAYOUT = Integer.MIN_VALUE;
 
-        final static int ITEM_DIRECTION_HEAD = -1;
+        static final int ITEM_DIRECTION_HEAD = -1;
 
-        final static int ITEM_DIRECTION_TAIL = 1;
+        static final int ITEM_DIRECTION_TAIL = 1;
 
-        final static int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE;
+        static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE;
 
         /**
          * We may not want to recycle children in some cases (e.g. layout)
@@ -2276,8 +2281,8 @@
                 if (view == ignore || lp.isItemRemoved()) {
                     continue;
                 }
-                final int distance = (lp.getViewLayoutPosition() - mCurrentPosition) *
-                        mItemDirection;
+                final int distance = (lp.getViewLayoutPosition() - mCurrentPosition)
+                        * mItemDirection;
                 if (distance < 0) {
                     continue; // item is not in current direction
                 }
@@ -2293,8 +2298,8 @@
         }
 
         void log() {
-            Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:" +
-                    mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);
+            Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:"
+                    + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);
         }
     }
 
@@ -2346,18 +2351,18 @@
             dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0);
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
-            @Override
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
 
-            @Override
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
     }
 
     /**
@@ -2392,12 +2397,12 @@
 
         @Override
         public String toString() {
-            return "AnchorInfo{" +
-                    "mPosition=" + mPosition +
-                    ", mCoordinate=" + mCoordinate +
-                    ", mLayoutFromEnd=" + mLayoutFromEnd +
-                    ", mValid=" + mValid +
-                    '}';
+            return "AnchorInfo{"
+                    + "mPosition=" + mPosition
+                    + ", mCoordinate=" + mCoordinate
+                    + ", mLayoutFromEnd=" + mLayoutFromEnd
+                    + ", mValid=" + mValid
+                    + '}';
         }
 
         boolean isViewValidAsAnchor(View child, RecyclerView.State state) {
@@ -2419,12 +2424,12 @@
                 final int previousEndMargin = prevLayoutEnd - childEnd;
                 mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin;
                 // ensure we did not push child's top out of bounds because of this
-                if (previousEndMargin > 0) {// we have room to shift bottom if necessary
+                if (previousEndMargin > 0) { // we have room to shift bottom if necessary
                     final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
                     final int estimatedChildStart = mCoordinate - childSize;
                     final int layoutStart = mOrientationHelper.getStartAfterPadding();
-                    final int previousStartMargin = mOrientationHelper.getDecoratedStart(child) -
-                            layoutStart;
+                    final int previousStartMargin = mOrientationHelper.getDecoratedStart(child)
+                            - layoutStart;
                     final int startReference = layoutStart + Math.min(previousStartMargin, 0);
                     final int startMargin = estimatedChildStart - startReference;
                     if (startMargin < 0) {
@@ -2437,14 +2442,14 @@
                 final int startMargin = childStart - mOrientationHelper.getStartAfterPadding();
                 mCoordinate = childStart;
                 if (startMargin > 0) { // we have room to fix end as well
-                    final int estimatedEnd = childStart +
-                            mOrientationHelper.getDecoratedMeasurement(child);
-                    final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding() -
-                            spaceChange;
-                    final int previousEndMargin = previousLayoutEnd -
-                            mOrientationHelper.getDecoratedEnd(child);
-                    final int endReference = mOrientationHelper.getEndAfterPadding() -
-                            Math.min(0, previousEndMargin);
+                    final int estimatedEnd = childStart
+                            + mOrientationHelper.getDecoratedMeasurement(child);
+                    final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding()
+                            - spaceChange;
+                    final int previousEndMargin = previousLayoutEnd
+                            - mOrientationHelper.getDecoratedEnd(child);
+                    final int endReference = mOrientationHelper.getEndAfterPadding()
+                            - Math.min(0, previousEndMargin);
                     final int endMargin = endReference - estimatedEnd;
                     if (endMargin < 0) {
                         mCoordinate -= Math.min(startMargin, -endMargin);
@@ -2455,8 +2460,8 @@
 
         public void assignFromView(View child) {
             if (mLayoutFromEnd) {
-                mCoordinate = mOrientationHelper.getDecoratedEnd(child) +
-                        mOrientationHelper.getTotalSpaceChange();
+                mCoordinate = mOrientationHelper.getDecoratedEnd(child)
+                        + mOrientationHelper.getTotalSpaceChange();
             } else {
                 mCoordinate = mOrientationHelper.getDecoratedStart(child);
             }
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java b/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java
index 78250c1..74d6a3b 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java
@@ -245,9 +245,9 @@
         // To avoid UI hiccups, trigger a smooth scroll to a distance little further than the
         // interim target. Since we track the distance travelled in onSeekTargetStep callback, it
         // won't actually scroll more than what we need.
-        action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO)
-                , (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO)
-                , (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator);
+        action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO),
+                (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO),
+                (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator);
     }
 
     private int clampApplyScroll(int tmpDt, int dt) {
@@ -354,8 +354,8 @@
             return ((ScrollVectorProvider) layoutManager)
                     .computeScrollVectorForPosition(targetPosition);
         }
-        Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager" +
-                " does not implement " + ScrollVectorProvider.class.getCanonicalName());
+        Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
+                + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
         return null;
     }
 }
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearSnapHelper.java b/v7/recyclerview/src/android/support/v7/widget/LinearSnapHelper.java
index c74e20d..869bc0d 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearSnapHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearSnapHelper.java
@@ -10,7 +10,7 @@
  * 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 languag`e governing permissions and
+ * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
@@ -139,8 +139,8 @@
 
     private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
             @NonNull View targetView, OrientationHelper helper) {
-        final int childCenter = helper.getDecoratedStart(targetView) +
-                (helper.getDecoratedMeasurement(targetView) / 2);
+        final int childCenter = helper.getDecoratedStart(targetView)
+                + (helper.getDecoratedMeasurement(targetView) / 2);
         final int containerCenter;
         if (layoutManager.getClipToPadding()) {
             containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
@@ -201,8 +201,8 @@
 
         for (int i = 0; i < childCount; i++) {
             final View child = layoutManager.getChildAt(i);
-            int childCenter = helper.getDecoratedStart(child) +
-                    (helper.getDecoratedMeasurement(child) / 2);
+            int childCenter = helper.getDecoratedStart(child)
+                    + (helper.getDecoratedMeasurement(child) / 2);
             int absDistance = Math.abs(childCenter - center);
 
             /** if child center is closer than previous closest, set it as closest  **/
diff --git a/v7/recyclerview/src/android/support/v7/widget/OpReorderer.java b/v7/recyclerview/src/android/support/v7/widget/OpReorderer.java
index db01a0c..b288061 100644
--- a/v7/recyclerview/src/android/support/v7/widget/OpReorderer.java
+++ b/v7/recyclerview/src/android/support/v7/widget/OpReorderer.java
@@ -16,19 +16,20 @@
 
 package android.support.v7.widget;
 
-import java.util.List;
-
-import android.support.v7.widget.AdapterHelper.UpdateOp;
 import static android.support.v7.widget.AdapterHelper.UpdateOp.ADD;
 import static android.support.v7.widget.AdapterHelper.UpdateOp.MOVE;
 import static android.support.v7.widget.AdapterHelper.UpdateOp.REMOVE;
 import static android.support.v7.widget.AdapterHelper.UpdateOp.UPDATE;
 
+import android.support.v7.widget.AdapterHelper.UpdateOp;
+
+import java.util.List;
+
 class OpReorderer {
 
     final Callback mCallback;
 
-    public OpReorderer(Callback callback) {
+    OpReorderer(Callback callback) {
         mCallback = callback;
     }
 
@@ -72,8 +73,8 @@
             }
         } else {
             moveIsBackwards = true;
-            if (removeOp.positionStart == moveOp.itemCount + 1 &&
-                    removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) {
+            if (removeOp.positionStart == moveOp.itemCount + 1
+                    && removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) {
                 revertedMove = true;
             }
         }
@@ -83,7 +84,7 @@
             removeOp.positionStart--;
         } else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
             // move is removed.
-            removeOp.itemCount --;
+            removeOp.itemCount--;
             moveOp.cmd = REMOVE;
             moveOp.itemCount = 1;
             if (removeOp.itemCount == 0) {
@@ -229,7 +230,7 @@
         return -1;
     }
 
-    static interface Callback {
+    interface Callback {
 
         UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount, Object payload);
 
diff --git a/v7/recyclerview/src/android/support/v7/widget/PositionMap.java b/v7/recyclerview/src/android/support/v7/widget/PositionMap.java
index 3777937..8225da4 100644
--- a/v7/recyclerview/src/android/support/v7/widget/PositionMap.java
+++ b/v7/recyclerview/src/android/support/v7/widget/PositionMap.java
@@ -33,7 +33,7 @@
     /**
      * Creates a new SparseArray containing no mappings.
      */
-    public PositionMap() {
+    PositionMap() {
         this(10);
     }
 
@@ -44,7 +44,7 @@
      * sparse array will be initialized with a light-weight representation
      * not requiring any additional array allocations.
      */
-    public PositionMap(int initialCapacity) {
+    PositionMap(int initialCapacity) {
         if (initialCapacity == 0) {
             mKeys = ContainerHelpers.EMPTY_INTS;
             mValues = ContainerHelpers.EMPTY_OBJECTS;
@@ -305,9 +305,11 @@
             gc();
         }
 
-        for (int i = 0; i < mSize; i++)
-            if (mValues[i] == value)
+        for (int i = 0; i < mSize; i++) {
+            if (mValues[i] == value) {
                 return i;
+            }
+        }
 
         return -1;
     }
@@ -376,7 +378,7 @@
 
         StringBuilder buffer = new StringBuilder(mSize * 28);
         buffer.append('{');
-        for (int i=0; i<mSize; i++) {
+        for (int i = 0; i < mSize; i++) {
             if (i > 0) {
                 buffer.append(", ");
             }
@@ -395,9 +397,11 @@
     }
 
     static int idealByteArraySize(int need) {
-        for (int i = 4; i < 32; i++)
-            if (need <= (1 << i) - 12)
+        for (int i = 4; i < 32; i++) {
+            if (need <= (1 << i) - 12) {
                 return (1 << i) - 12;
+            }
+        }
 
         return need;
     }
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index a88213b..ff8e0e6 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -18,8 +18,11 @@
 package android.support.v7.widget;
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static android.support.v4.view.ViewCompat.TYPE_NON_TOUCH;
+import static android.support.v4.view.ViewCompat.TYPE_TOUCH;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.Observable;
 import android.graphics.Canvas;
@@ -27,6 +30,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -38,30 +43,26 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
 import android.support.v4.os.TraceCompat;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.InputDeviceCompat;
 import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.NestedScrollingChild;
+import android.support.v4.view.NestedScrollingChild2;
 import android.support.v4.view.NestedScrollingChildHelper;
 import android.support.v4.view.ScrollingView;
-import android.support.v4.view.VelocityTrackerCompat;
 import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewConfigurationCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.support.v4.widget.EdgeEffectCompat;
-import android.support.v4.widget.ScrollerCompat;
 import android.support.v7.recyclerview.R;
 import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.view.Display;
 import android.view.FocusFinder;
+import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -71,6 +72,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Interpolator;
+import android.widget.EdgeEffect;
+import android.widget.OverScroller;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -148,7 +151,7 @@
  *
  * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
  */
-public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
+public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
 
     static final String TAG = "RecyclerView";
 
@@ -156,8 +159,8 @@
 
     static final boolean VERBOSE_TRACING = false;
 
-    private static final int[]  NESTED_SCROLLING_ATTRS
-            = {16843830 /* android.R.attr.nestedScrollingEnabled */};
+    private static final int[]  NESTED_SCROLLING_ATTRS =
+            {16843830 /* android.R.attr.nestedScrollingEnabled */};
 
     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
 
@@ -356,6 +359,7 @@
     private OnItemTouchListener mActiveOnItemTouchListener;
     boolean mIsAttached;
     boolean mHasFixedSize;
+    boolean mEnableFastScroller;
     @VisibleForTesting boolean mFirstLayoutComplete;
 
     // Counting lock to control whether we should ignore requestLayout calls from children or not.
@@ -405,7 +409,7 @@
      */
     private int mDispatchScrollCounter = 0;
 
-    private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
+    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
 
     ItemAnimator mItemAnimator = new DefaultItemAnimator();
 
@@ -445,8 +449,11 @@
     private OnFlingListener mOnFlingListener;
     private final int mMinFlingVelocity;
     private final int mMaxFlingVelocity;
-    // This value is used when handling generic motion events.
-    private float mScrollFactor = Float.MIN_VALUE;
+
+    // This value is used when handling rotary encoder generic motion events.
+    private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
+    private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
+
     private boolean mPreserveFocusAfterLayout = true;
 
     final ViewFlinger mViewFlinger = new ViewFlinger();
@@ -484,7 +491,7 @@
      * the View's state is undefined.
      */
     @VisibleForTesting
-    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
+    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
 
     private Runnable mItemAnimatorRunner = new Runnable() {
         @Override
@@ -565,6 +572,10 @@
 
         final ViewConfiguration vc = ViewConfiguration.get(context);
         mTouchSlop = vc.getScaledTouchSlop();
+        mScaledHorizontalScrollFactor =
+                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
+        mScaledVerticalScrollFactor =
+                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
@@ -595,6 +606,19 @@
             if (descendantFocusability == -1) {
                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
             }
+            mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
+            if (mEnableFastScroller) {
+                StateListDrawable verticalThumbDrawable = (StateListDrawable) a
+                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
+                Drawable verticalTrackDrawable = a
+                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
+                StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
+                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
+                Drawable horizontalTrackDrawable = a
+                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
+                initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
+                        horizontalThumbDrawable, horizontalTrackDrawable);
+            }
             a.recycle();
             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
 
@@ -613,6 +637,17 @@
     }
 
     /**
+     * Label appended to all public exception strings, used to help find which RV in an app is
+     * hitting an exception.
+     */
+    String exceptionLabel() {
+        return " " + super.toString()
+                + ", adapter:" + mAdapter
+                + ", layout:" + mLayout
+                + ", context:" + getContext();
+    }
+
+    /**
      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
      */
@@ -637,7 +672,7 @@
             int defStyleAttr, int defStyleRes) {
         if (className != null) {
             className = className.trim();
-            if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
+            if (!className.isEmpty()) {
                 className = getFullClassName(context, className);
                 try {
                     ClassLoader classLoader;
@@ -660,8 +695,8 @@
                             constructor = layoutManagerClass.getConstructor();
                         } catch (NoSuchMethodException e1) {
                             e1.initCause(e);
-                            throw new IllegalStateException(attrs.getPositionDescription() +
-                                    ": Error creating LayoutManager " + className, e1);
+                            throw new IllegalStateException(attrs.getPositionDescription()
+                                    + ": Error creating LayoutManager " + className, e1);
                         }
                     }
                     constructor.setAccessible(true);
@@ -748,7 +783,7 @@
             @Override
             public void removeAllViews() {
                 final int count = getChildCount();
-                for (int i = 0; i < count; i ++) {
+                for (int i = 0; i < count; i++) {
                     View child = getChildAt(i);
                     dispatchChildDetached(child);
 
@@ -772,7 +807,7 @@
                 if (vh != null) {
                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
                         throw new IllegalArgumentException("Called attach on a child which is not"
-                                + " detached: " + vh);
+                                + " detached: " + vh + exceptionLabel());
                     }
                     if (DEBUG) {
                         Log.d(TAG, "reAttach " + vh);
@@ -790,7 +825,7 @@
                     if (vh != null) {
                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
                             throw new IllegalArgumentException("called detach on an already"
-                                    + " detached child " + vh);
+                                    + " detached child " + vh + exceptionLabel());
                         }
                         if (DEBUG) {
                             Log.d(TAG, "tmpDetach " + vh);
@@ -846,11 +881,13 @@
             }
 
             @Override
-            public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
+            public void offsetPositionsForRemovingLaidOutOrNewView(
+                    int positionStart, int itemCount) {
                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
                 mItemsAddedOrRemoved = true;
             }
 
+
             @Override
             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
                 viewRangeUpdate(positionStart, itemCount, payload);
@@ -966,7 +1003,7 @@
         switch (slopConstant) {
             default:
                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
-                      + slopConstant + "; using default value");
+                        + slopConstant + "; using default value");
                 // fall-through
             case TOUCH_SLOP_DEFAULT:
                 mTouchSlop = vc.getScaledTouchSlop();
@@ -996,7 +1033,6 @@
         // bail out if layout is frozen
         setLayoutFrozen(false);
         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
-        setDataSetChangedAfterLayout();
         requestLayout();
     }
     /**
@@ -1065,7 +1101,7 @@
         }
         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
         mState.mStructureChanged = true;
-        markKnownViewsInvalid();
+        setDataSetChangedAfterLayout();
     }
 
     /**
@@ -1190,8 +1226,9 @@
         mLayout = layout;
         if (layout != null) {
             if (layout.mRecyclerView != null) {
-                throw new IllegalArgumentException("LayoutManager " + layout +
-                        " is already attached to a RecyclerView: " + layout.mRecyclerView);
+                throw new IllegalArgumentException("LayoutManager " + layout
+                        + " is already attached to a RecyclerView:"
+                        + layout.mRecyclerView.exceptionLabel());
             }
             mLayout.setRecyclerView(this);
             if (mIsAttached) {
@@ -1283,7 +1320,7 @@
         if (viewHolder.isTmpDetached()) {
             // re-attach
             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
-        } else if(!alreadyParented) {
+        } else if (!alreadyParented) {
             mChildHelper.addView(view, true);
         } else {
             mChildHelper.hide(view);
@@ -1445,6 +1482,20 @@
     }
 
     /**
+     * Returns an {@link ItemDecoration} previously added to this RecyclerView.
+     *
+     * @param index The index position of the desired ItemDecoration.
+     * @return the ItemDecoration at index position, or null if invalid index.
+     */
+    public ItemDecoration getItemDecorationAt(int index) {
+        if (index < 0 || index >= mItemDecorations.size()) {
+            return null;
+        }
+
+        return mItemDecorations.get(index);
+    }
+
+    /**
      * Remove an {@link ItemDecoration} from this RecyclerView.
      *
      * <p>The given decoration will no longer impact the measurement and drawing of
@@ -1549,8 +1600,8 @@
         }
         stopScroll();
         if (mLayout == null) {
-            Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
-                    "Call setLayoutManager with a non-null argument.");
+            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
             return;
         }
         mLayout.scrollToPosition(position);
@@ -1585,8 +1636,8 @@
             return;
         }
         if (mLayout == null) {
-            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
-                    "Call setLayoutManager with a non-null argument.");
+            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
             return;
         }
         mLayout.smoothScrollToPosition(this, mState, position);
@@ -1601,8 +1652,8 @@
     @Override
     public void scrollBy(int x, int y) {
         if (mLayout == null) {
-            Log.e(TAG, "Cannot scroll without a LayoutManager set. " +
-                    "Call setLayoutManager with a non-null argument.");
+            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
             return;
         }
         if (mLayoutFrozen) {
@@ -1698,6 +1749,7 @@
             eatRequestLayout();
             onEnterLayoutOrScroll();
             TraceCompat.beginSection(TRACE_SCROLL_TAG);
+            fillRemainingScrollValues(mState);
             if (x != 0) {
                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
                 unconsumedX = x - consumedX;
@@ -1715,7 +1767,8 @@
             invalidate();
         }
 
-        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
+        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
+                TYPE_TOUCH)) {
             // Update the last touch co-ords, taking any scroll offset into account
             mLastTouchX -= mScrollOffset[0];
             mLastTouchY -= mScrollOffset[1];
@@ -1725,7 +1778,7 @@
             mNestedOffsets[0] += mScrollOffset[0];
             mNestedOffsets[1] += mScrollOffset[1];
         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
-            if (ev != null) {
+            if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
             }
             considerReleasingGlowsOnScroll(x, y);
@@ -1897,7 +1950,8 @@
         if (mEatRequestLayout < 1) {
             //noinspection PointlessBooleanExpression
             if (DEBUG) {
-                throw new IllegalStateException("invalid eat request layout count");
+                throw new IllegalStateException("invalid eat request layout count"
+                        + exceptionLabel());
             }
             mEatRequestLayout = 1;
         }
@@ -1914,8 +1968,8 @@
         }
         if (mEatRequestLayout == 1) {
             // when layout is frozen we should delay dispatchLayout()
-            if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen &&
-                    mLayout != null && mAdapter != null) {
+            if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
+                    && mLayout != null && mAdapter != null) {
                 dispatchLayout();
             }
             if (!mLayoutFrozen) {
@@ -1998,8 +2052,8 @@
      */
     public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
         if (mLayout == null) {
-            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
-                    "Call setLayoutManager with a non-null argument.");
+            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
             return;
         }
         if (mLayoutFrozen) {
@@ -2031,8 +2085,8 @@
      */
     public boolean fling(int velocityX, int velocityY) {
         if (mLayout == null) {
-            Log.e(TAG, "Cannot fling without a LayoutManager set. " +
-                    "Call setLayoutManager with a non-null argument.");
+            Log.e(TAG, "Cannot fling without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
             return false;
         }
         if (mLayoutFrozen) {
@@ -2062,6 +2116,15 @@
             }
 
             if (canScroll) {
+                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
+                if (canScrollHorizontal) {
+                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
+                }
+                if (canScrollVertical) {
+                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
+                }
+                startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
+
                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
                 mViewFlinger.fling(velocityX, velocityY);
@@ -2116,26 +2179,22 @@
         boolean invalidate = false;
         if (overscrollX < 0) {
             ensureLeftGlow();
-            if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
+            invalidate = true;
         } else if (overscrollX > 0) {
             ensureRightGlow();
-            if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
+            invalidate = true;
         }
 
         if (overscrollY < 0) {
             ensureTopGlow();
-            if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
+            invalidate = true;
         } else if (overscrollY > 0) {
             ensureBottomGlow();
-            if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
+            invalidate = true;
         }
 
         if (invalidate || overscrollX != 0 || overscrollY != 0) {
@@ -2145,10 +2204,22 @@
 
     private void releaseGlows() {
         boolean needsInvalidate = false;
-        if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
-        if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
-        if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
-        if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
+        if (mLeftGlow != null) {
+            mLeftGlow.onRelease();
+            needsInvalidate = mLeftGlow.isFinished();
+        }
+        if (mTopGlow != null) {
+            mTopGlow.onRelease();
+            needsInvalidate |= mTopGlow.isFinished();
+        }
+        if (mRightGlow != null) {
+            mRightGlow.onRelease();
+            needsInvalidate |= mRightGlow.isFinished();
+        }
+        if (mBottomGlow != null) {
+            mBottomGlow.onRelease();
+            needsInvalidate |= mBottomGlow.isFinished();
+        }
         if (needsInvalidate) {
             ViewCompat.postInvalidateOnAnimation(this);
         }
@@ -2157,16 +2228,20 @@
     void considerReleasingGlowsOnScroll(int dx, int dy) {
         boolean needsInvalidate = false;
         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
-            needsInvalidate = mLeftGlow.onRelease();
+            mLeftGlow.onRelease();
+            needsInvalidate = mLeftGlow.isFinished();
         }
         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
-            needsInvalidate |= mRightGlow.onRelease();
+            mRightGlow.onRelease();
+            needsInvalidate |= mRightGlow.isFinished();
         }
         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
-            needsInvalidate |= mTopGlow.onRelease();
+            mTopGlow.onRelease();
+            needsInvalidate |= mTopGlow.isFinished();
         }
         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
-            needsInvalidate |= mBottomGlow.onRelease();
+            mBottomGlow.onRelease();
+            needsInvalidate |= mBottomGlow.isFinished();
         }
         if (needsInvalidate) {
             ViewCompat.postInvalidateOnAnimation(this);
@@ -2199,7 +2274,7 @@
         if (mLeftGlow != null) {
             return;
         }
-        mLeftGlow = new EdgeEffectCompat(getContext());
+        mLeftGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2212,7 +2287,7 @@
         if (mRightGlow != null) {
             return;
         }
-        mRightGlow = new EdgeEffectCompat(getContext());
+        mRightGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2225,7 +2300,7 @@
         if (mTopGlow != null) {
             return;
         }
-        mTopGlow = new EdgeEffectCompat(getContext());
+        mTopGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2239,7 +2314,7 @@
         if (mBottomGlow != null) {
             return;
         }
-        mBottomGlow = new EdgeEffectCompat(getContext());
+        mBottomGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2377,7 +2452,7 @@
             return true;
         }
 
-        if(direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
+        if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
             final boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
@@ -2421,7 +2496,8 @@
                         || mTempRect.bottom <= mTempRect2.top)
                         && mTempRect.bottom < mTempRect2.bottom;
         }
-        throw new IllegalArgumentException("direction must be absolute. received:" + direction);
+        throw new IllegalArgumentException("direction must be absolute. received:"
+                + direction + exceptionLabel());
     }
 
     @Override
@@ -2550,7 +2626,7 @@
     /**
      * Returns true if RecyclerView is attached to window.
      */
-    // @override
+    @Override
     public boolean isAttachedToWindow() {
         return mIsAttached;
     }
@@ -2566,9 +2642,9 @@
         if (!isComputingLayout()) {
             if (message == null) {
                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
-                        + "computing a layout or scrolling");
+                        + "computing a layout or scrolling" + exceptionLabel());
             }
-            throw new IllegalStateException(message);
+            throw new IllegalStateException(message + exceptionLabel());
 
         }
     }
@@ -2584,7 +2660,7 @@
         if (isComputingLayout()) {
             if (message == null) {
                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
-                        + "computing a layout or scrolling");
+                        + "computing a layout or scrolling" + exceptionLabel());
             }
             throw new IllegalStateException(message);
         }
@@ -2594,7 +2670,7 @@
                             + "RecyclerView data. Any method call that might change the structure"
                             + "of the RecyclerView or the adapter contents should be postponed to"
                             + "the next frame.",
-                    new IllegalStateException(""));
+                    new IllegalStateException("" + exceptionLabel()));
         }
     }
 
@@ -2699,8 +2775,8 @@
         }
         mVelocityTracker.addMovement(e);
 
-        final int action = MotionEventCompat.getActionMasked(e);
-        final int actionIndex = MotionEventCompat.getActionIndex(e);
+        final int action = e.getActionMasked();
+        final int actionIndex = e.getActionIndex();
 
         switch (action) {
             case MotionEvent.ACTION_DOWN:
@@ -2726,10 +2802,10 @@
                 if (canScrollVertically) {
                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
                 }
-                startNestedScroll(nestedScrollAxis);
+                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
                 break;
 
-            case MotionEventCompat.ACTION_POINTER_DOWN:
+            case MotionEvent.ACTION_POINTER_DOWN:
                 mScrollPointerId = e.getPointerId(actionIndex);
                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
@@ -2738,8 +2814,8 @@
             case MotionEvent.ACTION_MOVE: {
                 final int index = e.findPointerIndex(mScrollPointerId);
                 if (index < 0) {
-                    Log.e(TAG, "Error processing scroll; pointer index for id " +
-                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
+                    Log.e(TAG, "Error processing scroll; pointer index for id "
+                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
                     return false;
                 }
 
@@ -2750,11 +2826,11 @@
                     final int dy = y - mInitialTouchY;
                     boolean startScroll = false;
                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
-                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
+                        mLastTouchX = x;
                         startScroll = true;
                     }
                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
-                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
+                        mLastTouchY = y;
                         startScroll = true;
                     }
                     if (startScroll) {
@@ -2763,13 +2839,13 @@
                 }
             } break;
 
-            case MotionEventCompat.ACTION_POINTER_UP: {
+            case MotionEvent.ACTION_POINTER_UP: {
                 onPointerUp(e);
             } break;
 
             case MotionEvent.ACTION_UP: {
                 mVelocityTracker.clear();
-                stopNestedScroll();
+                stopNestedScroll(TYPE_TOUCH);
             } break;
 
             case MotionEvent.ACTION_CANCEL: {
@@ -2812,8 +2888,8 @@
         boolean eventAddedToVelocityTracker = false;
 
         final MotionEvent vtev = MotionEvent.obtain(e);
-        final int action = MotionEventCompat.getActionMasked(e);
-        final int actionIndex = MotionEventCompat.getActionIndex(e);
+        final int action = e.getActionMasked();
+        final int actionIndex = e.getActionIndex();
 
         if (action == MotionEvent.ACTION_DOWN) {
             mNestedOffsets[0] = mNestedOffsets[1] = 0;
@@ -2833,10 +2909,10 @@
                 if (canScrollVertically) {
                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
                 }
-                startNestedScroll(nestedScrollAxis);
+                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
             } break;
 
-            case MotionEventCompat.ACTION_POINTER_DOWN: {
+            case MotionEvent.ACTION_POINTER_DOWN: {
                 mScrollPointerId = e.getPointerId(actionIndex);
                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
@@ -2845,8 +2921,8 @@
             case MotionEvent.ACTION_MOVE: {
                 final int index = e.findPointerIndex(mScrollPointerId);
                 if (index < 0) {
-                    Log.e(TAG, "Error processing scroll; pointer index for id " +
-                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");
+                    Log.e(TAG, "Error processing scroll; pointer index for id "
+                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
                     return false;
                 }
 
@@ -2855,7 +2931,7 @@
                 int dx = mLastTouchX - x;
                 int dy = mLastTouchY - y;
 
-                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
+                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
                     dx -= mScrollConsumed[0];
                     dy -= mScrollConsumed[1];
                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
@@ -2903,7 +2979,7 @@
                 }
             } break;
 
-            case MotionEventCompat.ACTION_POINTER_UP: {
+            case MotionEvent.ACTION_POINTER_UP: {
                 onPointerUp(e);
             } break;
 
@@ -2911,10 +2987,10 @@
                 mVelocityTracker.addMovement(vtev);
                 eventAddedToVelocityTracker = true;
                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
-                final float xvel = canScrollHorizontally ?
-                        -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
-                final float yvel = canScrollVertically ?
-                        -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
+                final float xvel = canScrollHorizontally
+                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
+                final float yvel = canScrollVertically
+                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                     setScrollState(SCROLL_STATE_IDLE);
                 }
@@ -2938,7 +3014,7 @@
         if (mVelocityTracker != null) {
             mVelocityTracker.clear();
         }
-        stopNestedScroll();
+        stopNestedScroll(TYPE_TOUCH);
         releaseGlows();
     }
 
@@ -2948,7 +3024,7 @@
     }
 
     private void onPointerUp(MotionEvent e) {
-        final int actionIndex = MotionEventCompat.getActionIndex(e);
+        final int actionIndex = e.getActionIndex();
         if (e.getPointerId(actionIndex) == mScrollPointerId) {
             // Pick a new pointer to pick up the slack.
             final int newIndex = actionIndex == 0 ? 1 : 0;
@@ -2958,7 +3034,7 @@
         }
     }
 
-    // @Override
+    @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         if (mLayout == null) {
             return false;
@@ -2966,51 +3042,48 @@
         if (mLayoutFrozen) {
             return false;
         }
-        if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
-            if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
-                final float vScroll, hScroll;
+        if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
+            final float vScroll, hScroll;
+            if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
                 if (mLayout.canScrollVertically()) {
                     // Inverse the sign of the vertical scroll to align the scroll orientation
                     // with AbsListView.
-                    vScroll = -MotionEventCompat
-                            .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL);
+                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
                 } else {
                     vScroll = 0f;
                 }
                 if (mLayout.canScrollHorizontally()) {
-                    hScroll = MotionEventCompat
-                            .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL);
+                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
                 } else {
                     hScroll = 0f;
                 }
-
-                if (vScroll != 0 || hScroll != 0) {
-                    final float scrollFactor = getScrollFactor();
-                    scrollByInternal((int) (hScroll * scrollFactor),
-                            (int) (vScroll * scrollFactor), event);
+            } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
+                final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
+                if (mLayout.canScrollVertically()) {
+                    // Invert the sign of the vertical scroll to align the scroll orientation
+                    // with AbsListView.
+                    vScroll = -axisScroll;
+                    hScroll = 0f;
+                } else if (mLayout.canScrollHorizontally()) {
+                    vScroll = 0f;
+                    hScroll = axisScroll;
+                } else {
+                    vScroll = 0f;
+                    hScroll = 0f;
                 }
+            } else {
+                vScroll = 0f;
+                hScroll = 0f;
+            }
+
+            if (vScroll != 0 || hScroll != 0) {
+                scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
+                        (int) (vScroll * mScaledVerticalScrollFactor), event);
             }
         }
         return false;
     }
 
-    /**
-     * Ported from View.getVerticalScrollFactor.
-     */
-    private float getScrollFactor() {
-        if (mScrollFactor == Float.MIN_VALUE) {
-            TypedValue outValue = new TypedValue();
-            if (getContext().getTheme().resolveAttribute(
-                    android.R.attr.listPreferredItemHeight, outValue, true)) {
-                mScrollFactor = outValue.getDimension(
-                        getContext().getResources().getDisplayMetrics());
-            } else {
-                return 0; //listPreferredItemHeight is not defined, no generic scrolling
-            }
-        }
-        return mScrollFactor;
-    }
-
     @Override
     protected void onMeasure(int widthSpec, int heightSpec) {
         if (mLayout == null) {
@@ -3070,6 +3143,14 @@
                 }
                 mAdapterUpdateDuringMeasure = false;
                 resumeRequestLayout(false);
+            } else if (mState.mRunPredictiveAnimations) {
+                // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
+                // this means there is already an onMeasure() call performed to handle the pending
+                // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
+                // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
+                // because getViewForPosition() will crash when LM uses a child to measure.
+                setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
+                return;
             }
 
             if (mAdapter != null) {
@@ -3132,7 +3213,7 @@
     }
 
     void onEnterLayoutOrScroll() {
-        mLayoutOrScrollCounter ++;
+        mLayoutOrScrollCounter++;
     }
 
     void onExitLayoutOrScroll() {
@@ -3140,11 +3221,11 @@
     }
 
     void onExitLayoutOrScroll(boolean enableChangeEvents) {
-        mLayoutOrScrollCounter --;
+        mLayoutOrScrollCounter--;
         if (mLayoutOrScrollCounter < 1) {
             if (DEBUG && mLayoutOrScrollCounter < 0) {
                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
-                        + "Some calls are not matching");
+                        + "Some calls are not matching" + exceptionLabel());
             }
             mLayoutOrScrollCounter = 0;
             if (enableChangeEvents) {
@@ -3327,8 +3408,8 @@
             dispatchLayoutStep1();
             mLayout.setExactMeasureSpecsFrom(this);
             dispatchLayoutStep2();
-        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
-                mLayout.getHeight() != getHeight()) {
+        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
+                || mLayout.getHeight() != getHeight()) {
             // First 2 steps are done in onMeasure but looks like we have to run again due to
             // changed size.
             mLayout.setExactMeasureSpecsFrom(this);
@@ -3354,8 +3435,8 @@
             // mFocusedItemPosition should hold the current adapter position of the previously
             // focused item. If the item is removed, we store the previous adapter position of the
             // removed item.
-            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION :
-                    (focusedVh.isRemoved() ? focusedVh.mOldPosition
+            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
+                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
                             : focusedVh.getAdapterPosition());
             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
         }
@@ -3489,6 +3570,17 @@
         return lastKnownId;
     }
 
+    final void fillRemainingScrollValues(State state) {
+        if (getScrollState() == SCROLL_STATE_SETTLING) {
+            final OverScroller scroller = mViewFlinger.mScroller;
+            state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
+            state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
+        } else {
+            state.mRemainingScrollHorizontal = 0;
+            state.mRemainingScrollVertical = 0;
+        }
+    }
+
     /**
      * The first step of a layout where we;
      * - process adapter updates
@@ -3498,6 +3590,7 @@
      */
     private void dispatchLayoutStep1() {
         mState.assertLayoutStep(State.STEP_START);
+        fillRemainingScrollValues(mState);
         mState.mIsMeasuring = false;
         eatRequestLayout();
         mViewInfoStore.clear();
@@ -3729,19 +3822,21 @@
                 if (mAdapter != null && mAdapter.hasStableIds()) {
                     throw new IllegalStateException("Two different ViewHolders have the same stable"
                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
-                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
+                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
+                            + exceptionLabel());
                 } else {
                     throw new IllegalStateException("Two different ViewHolders have the same change"
                             + " ID. This might happen due to inconsistent Adapter update events or"
                             + " if the LayoutManager lays out the same View multiple times."
-                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
+                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
+                            + exceptionLabel());
                 }
             }
         }
         // Very unlikely to happen but if it does, notify the developer.
         Log.e(TAG, "Problem while matching changed view holders with the new"
                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
-                + " cannot be found but it is necessary for " + holder);
+                + " cannot be found but it is necessary for " + holder + exceptionLabel());
     }
 
     /**
@@ -3788,8 +3883,8 @@
 
     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
-        return mMinMaxLayoutPositions[0] != minPositionPreLayout ||
-                mMinMaxLayoutPositions[1] != maxPositionPreLayout;
+        return mMinMaxLayoutPositions[0] != minPositionPreLayout
+                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
     }
 
     @Override
@@ -3800,7 +3895,7 @@
                 vh.clearTmpDetachFlag();
             } else if (!vh.shouldIgnore()) {
                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
-                        + " is not flagged as tmp detached." + vh);
+                        + " is not flagged as tmp detached." + vh + exceptionLabel());
             }
         }
 
@@ -3938,8 +4033,8 @@
         // If some views are animating, ItemDecorators are likely to move/change with them.
         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
         // display lists are not invalidated.
-        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
-                mItemAnimator.isRunning()) {
+        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
+                && mItemAnimator.isRunning()) {
             needsInvalidate = true;
         }
 
@@ -3966,7 +4061,7 @@
     @Override
     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
         if (mLayout == null) {
-            throw new IllegalStateException("RecyclerView has no LayoutManager");
+            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
         }
         return mLayout.generateDefaultLayoutParams();
     }
@@ -3974,7 +4069,7 @@
     @Override
     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
         if (mLayout == null) {
-            throw new IllegalStateException("RecyclerView has no LayoutManager");
+            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
         }
         return mLayout.generateLayoutParams(getContext(), attrs);
     }
@@ -3982,7 +4077,7 @@
     @Override
     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
         if (mLayout == null) {
-            throw new IllegalStateException("RecyclerView has no LayoutManager");
+            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
         }
         return mLayout.generateLayoutParams(p);
     }
@@ -4005,7 +4100,7 @@
             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
                 throw new IllegalStateException("view holder cannot have position -1 unless it"
-                        + " is removed");
+                        + " is removed" + exceptionLabel());
             }
             if (!holder.shouldIgnore()) {
                 holder.saveOldPosition();
@@ -4043,8 +4138,8 @@
                 continue;
             }
             if (DEBUG) {
-                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
-                        holder);
+                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
+                        + holder);
             }
             if (holder.mPosition == from) {
                 holder.offsetPosition(to - from, false);
@@ -4064,8 +4159,8 @@
             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
                 if (DEBUG) {
-                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
-                            holder + " now at position " + (holder.mPosition + itemCount));
+                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
+                            + holder + " now at position " + (holder.mPosition + itemCount));
                 }
                 holder.offsetPosition(itemCount, false);
                 mState.mStructureChanged = true;
@@ -4084,16 +4179,16 @@
             if (holder != null && !holder.shouldIgnore()) {
                 if (holder.mPosition >= positionEnd) {
                     if (DEBUG) {
-                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
-                                " holder " + holder + " now at position " +
-                                (holder.mPosition - itemCount));
+                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
+                                + " holder " + holder + " now at position "
+                                + (holder.mPosition - itemCount));
                     }
                     holder.offsetPosition(-itemCount, applyToPreLayout);
                     mState.mStructureChanged = true;
                 } else if (holder.mPosition >= positionStart) {
                     if (DEBUG) {
-                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
-                                " holder " + holder + " now REMOVED");
+                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
+                                + " holder " + holder + " now REMOVED");
                     }
                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
                             applyToPreLayout);
@@ -4141,8 +4236,8 @@
 
     /**
      * Call this method to signal that *all* adapter content has changed (generally, because of
-     * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
-     * be discarded or animated.
+     * setAdapter, swapAdapter, or notifyDataSetChanged), and that once layout occurs, all
+     * attached items should be discarded or animated.
      *
      * Attached items are labeled as invalid, and all cached items are discarded.
      *
@@ -4229,8 +4324,8 @@
     public ViewHolder getChildViewHolder(View child) {
         final ViewParent parent = child.getParent();
         if (parent != null && parent != this) {
-            throw new IllegalArgumentException("View " + child + " is not a direct child of " +
-                    this);
+            throw new IllegalArgumentException("View " + child + " is not a direct child of "
+                    + this);
         }
         return getChildViewHolderInt(child);
     }
@@ -4387,7 +4482,8 @@
         ViewHolder hidden = null;
         for (int i = 0; i < childCount; i++) {
             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
+            if (holder != null && !holder.isRemoved()
+                    && getAdapterPositionFor(holder) == position) {
                 if (mChildHelper.isHidden(holder.itemView)) {
                     hidden = holder;
                 } else {
@@ -4468,12 +4564,12 @@
         final int count = mChildHelper.getChildCount();
         for (int i = count - 1; i >= 0; i--) {
             final View child = mChildHelper.getChildAt(i);
-            final float translationX = ViewCompat.getTranslationX(child);
-            final float translationY = ViewCompat.getTranslationY(child);
-            if (x >= child.getLeft() + translationX &&
-                    x <= child.getRight() + translationX &&
-                    y >= child.getTop() + translationY &&
-                    y <= child.getBottom() + translationY) {
+            final float translationX = child.getTranslationX();
+            final float translationY = child.getTranslationY();
+            if (x >= child.getLeft() + translationX
+                    && x <= child.getRight() + translationX
+                    && y >= child.getTop() + translationY
+                    && y <= child.getBottom() + translationY) {
                 return child;
             }
         }
@@ -4606,7 +4702,7 @@
     }
 
     void dispatchOnScrolled(int hresult, int vresult) {
-        mDispatchScrollCounter ++;
+        mDispatchScrollCounter++;
         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
         // but some general-purpose code may choose to respond to changes this way.
         final int scrollX = getScrollX();
@@ -4626,7 +4722,7 @@
                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
             }
         }
-        mDispatchScrollCounter --;
+        mDispatchScrollCounter--;
     }
 
     /**
@@ -4686,7 +4782,7 @@
     class ViewFlinger implements Runnable {
         private int mLastFlingX;
         private int mLastFlingY;
-        private ScrollerCompat mScroller;
+        private OverScroller mScroller;
         Interpolator mInterpolator = sQuinticInterpolator;
 
 
@@ -4696,8 +4792,8 @@
         // Tracks if postAnimationCallback should be re-attached when it is done
         private boolean mReSchedulePostAnimationCallback = false;
 
-        public ViewFlinger() {
-            mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
+        ViewFlinger() {
+            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
         }
 
         @Override
@@ -4710,22 +4806,30 @@
             consumePendingUpdateOperations();
             // keep a local reference so that if it is changed during onAnimation method, it won't
             // cause unexpected behaviors
-            final ScrollerCompat scroller = mScroller;
+            final OverScroller scroller = mScroller;
             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
             if (scroller.computeScrollOffset()) {
+                final int[] scrollConsumed = mScrollConsumed;
                 final int x = scroller.getCurrX();
                 final int y = scroller.getCurrY();
-                final int dx = x - mLastFlingX;
-                final int dy = y - mLastFlingY;
+                int dx = x - mLastFlingX;
+                int dy = y - mLastFlingY;
                 int hresult = 0;
                 int vresult = 0;
                 mLastFlingX = x;
                 mLastFlingY = y;
                 int overscrollX = 0, overscrollY = 0;
+
+                if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
+                    dx -= scrollConsumed[0];
+                    dy -= scrollConsumed[1];
+                }
+
                 if (mAdapter != null) {
                     eatRequestLayout();
                     onEnterLayoutOrScroll();
                     TraceCompat.beginSection(TRACE_SCROLL_TAG);
+                    fillRemainingScrollValues(mState);
                     if (dx != 0) {
                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
                         overscrollX = dx - hresult;
@@ -4740,8 +4844,8 @@
                     onExitLayoutOrScroll();
                     resumeRequestLayout(false);
 
-                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
-                            smoothScroller.isRunning()) {
+                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
+                            && smoothScroller.isRunning()) {
                         final int adapterSize = mState.getItemCount();
                         if (adapterSize == 0) {
                             smoothScroller.stop();
@@ -4759,7 +4863,10 @@
                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
                     considerReleasingGlowsOnScroll(dx, dy);
                 }
-                if (overscrollX != 0 || overscrollY != 0) {
+
+                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
+                        TYPE_NON_TOUCH)
+                        && (overscrollX != 0 || overscrollY != 0)) {
                     final int vel = (int) scroller.getCurrVelocity();
 
                     int velX = 0;
@@ -4775,8 +4882,8 @@
                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
                         absorbGlows(velX, velY);
                     }
-                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
-                            (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
+                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
+                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
                         scroller.abortAnimation();
                     }
                 }
@@ -4795,11 +4902,14 @@
                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
                         || fullyConsumedVertical;
 
-                if (scroller.isFinished() || !fullyConsumedAny) {
-                    setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
+                if (scroller.isFinished() || (!fullyConsumedAny
+                        && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
+                    // setting state to idle will stop this.
+                    setScrollState(SCROLL_STATE_IDLE);
                     if (ALLOW_THREAD_GAP_WORK) {
                         mPrefetchRegistry.clearPrefetchPositions();
                     }
+                    stopNestedScroll(TYPE_NON_TOUCH);
                 } else {
                     postOnAnimation();
                     if (mGapWorker != null) {
@@ -4858,7 +4968,7 @@
 
         private float distanceInfluenceForSnapDuration(float f) {
             f -= 0.5f; // center the values about 0.
-            f *= 0.3f * Math.PI / 2.0f;
+            f *= 0.3f * (float) Math.PI / 2.0f;
             return (float) Math.sin(f);
         }
 
@@ -4871,8 +4981,8 @@
             final int containerSize = horizontal ? getWidth() : getHeight();
             final int halfContainerSize = containerSize / 2;
             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
-            final float distance = halfContainerSize + halfContainerSize *
-                    distanceInfluenceForSnapDuration(distanceRatio);
+            final float distance = halfContainerSize + halfContainerSize
+                    * distanceInfluenceForSnapDuration(distanceRatio);
 
             final int duration;
             if (velocity > 0) {
@@ -4896,7 +5006,7 @@
         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
             if (mInterpolator != interpolator) {
                 mInterpolator = interpolator;
-                mScroller = ScrollerCompat.create(getContext(), interpolator);
+                mScroller = new OverScroller(getContext(), interpolator);
             }
             setScrollState(SCROLL_STATE_SETTLING);
             mLastFlingX = mLastFlingY = 0;
@@ -4921,8 +5031,7 @@
                 View shadowingView = holder.mShadowingHolder.itemView;
                 int left = view.getLeft();
                 int top = view.getTop();
-                if (left != shadowingView.getLeft() ||
-                        top != shadowingView.getTop()) {
+                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
                     shadowingView.layout(left, top,
                             left + shadowingView.getWidth(),
                             top + shadowingView.getHeight());
@@ -5060,7 +5169,7 @@
 
         int size() {
             int count = 0;
-            for (int i = 0; i < mScrap.size(); i ++) {
+            for (int i = 0; i < mScrap.size(); i++) {
                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
                 if (viewHolders != null) {
                     count += viewHolders.size();
@@ -5071,7 +5180,7 @@
 
         public void putRecycledView(ViewHolder scrap) {
             final int viewType = scrap.getItemViewType();
-            final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
+            final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
                 return;
             }
@@ -5297,13 +5406,13 @@
             if (holder.isRemoved()) {
                 if (DEBUG && !mState.isPreLayout()) {
                     throw new IllegalStateException("should not receive a removed view unless it"
-                            + " is pre layout");
+                            + " is pre layout" + exceptionLabel());
                 }
                 return mState.isPreLayout();
             }
             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
-                        + "adapter position" + holder);
+                        + "adapter position" + holder + exceptionLabel());
             }
             if (!mState.isPreLayout()) {
                 // don't check type if it is pre-layout.
@@ -5343,7 +5452,7 @@
             mAdapter.bindViewHolder(holder, offsetPosition);
             long endBindNs = getNanoTime();
             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
-            attachAccessibilityDelegate(holder.itemView);
+            attachAccessibilityDelegateOnBind(holder);
             if (mState.isPreLayout()) {
                 holder.mPreLayoutPosition = position;
             }
@@ -5370,13 +5479,13 @@
             if (holder == null) {
                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
                         + " pass arbitrary views to this method, they should be created by the "
-                        + "Adapter");
+                        + "Adapter" + exceptionLabel());
             }
             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                         + "position " + position + "(offset:" + offsetPosition + ")."
-                        + "state:" + mState.getItemCount());
+                        + "state:" + mState.getItemCount() + exceptionLabel());
             }
             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
 
@@ -5418,7 +5527,7 @@
         public int convertPreLayoutPositionToPostLayout(int position) {
             if (position < 0 || position >= mState.getItemCount()) {
                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
-                        + "item count is " + mState.getItemCount());
+                        + "item count is " + mState.getItemCount() + exceptionLabel());
             }
             if (!mState.isPreLayout()) {
                 return position;
@@ -5471,7 +5580,8 @@
                 boolean dryRun, long deadlineNs) {
             if (position < 0 || position >= mState.getItemCount()) {
                 throw new IndexOutOfBoundsException("Invalid item position " + position
-                        + "(" + position + "). Item count:" + mState.getItemCount());
+                        + "(" + position + "). Item count:" + mState.getItemCount()
+                        + exceptionLabel());
             }
             boolean fromScrapOrHiddenOrCache = false;
             ViewHolder holder = null;
@@ -5509,7 +5619,7 @@
                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                             + "position " + position + "(offset:" + offsetPosition + ")."
-                            + "state:" + mState.getItemCount());
+                            + "state:" + mState.getItemCount() + exceptionLabel());
                 }
 
                 final int type = mAdapter.getItemViewType(offsetPosition);
@@ -5532,11 +5642,12 @@
                         holder = getChildViewHolder(view);
                         if (holder == null) {
                             throw new IllegalArgumentException("getViewForPositionAndType returned"
-                                    + " a view which does not have a ViewHolder");
+                                    + " a view which does not have a ViewHolder"
+                                    + exceptionLabel());
                         } else if (holder.shouldIgnore()) {
                             throw new IllegalArgumentException("getViewForPositionAndType returned"
                                     + " a view that is ignored. You must call stopIgnoring before"
-                                    + " returning this view.");
+                                    + " returning this view." + exceptionLabel());
                         }
                     }
                 }
@@ -5600,7 +5711,8 @@
             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                 if (DEBUG && holder.isRemoved()) {
                     throw new IllegalStateException("Removed holder should be bound and it should"
-                            + " come here only in pre-layout. Holder: " + holder);
+                            + " come here only in pre-layout. Holder: " + holder
+                            + exceptionLabel());
                 }
                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
@@ -5622,14 +5734,16 @@
             return holder;
         }
 
-        private void attachAccessibilityDelegate(View itemView) {
+        private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
             if (isAccessibilityEnabled()) {
-                if (ViewCompat.getImportantForAccessibility(itemView) ==
-                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                final View itemView = holder.itemView;
+                if (ViewCompat.getImportantForAccessibility(itemView)
+                        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                     ViewCompat.setImportantForAccessibility(itemView,
                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
                 }
                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
+                    holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
                     ViewCompat.setAccessibilityDelegate(itemView,
                             mAccessibilityDelegate.getItemDelegate());
                 }
@@ -5682,7 +5796,7 @@
             }
             if (holder.isScrap()) {
                 holder.unScrap();
-            } else if (holder.wasReturnedFromScrap()){
+            } else if (holder.wasReturnedFromScrap()) {
                 holder.clearReturnedFromScrapFlag();
             }
             recycleViewHolderInternal(holder);
@@ -5741,17 +5855,19 @@
                 throw new IllegalArgumentException(
                         "Scrapped or attached views may not be recycled. isScrap:"
                                 + holder.isScrap() + " isAttached:"
-                                + (holder.itemView.getParent() != null));
+                                + (holder.itemView.getParent() != null) + exceptionLabel());
             }
 
             if (holder.isTmpDetached()) {
                 throw new IllegalArgumentException("Tmp detached view should be removed "
-                        + "from RecyclerView before it can be recycled: " + holder);
+                        + "from RecyclerView before it can be recycled: " + holder
+                        + exceptionLabel());
             }
 
             if (holder.shouldIgnore()) {
                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
-                        + " should first call stopIgnoringView(view) before calling recycle.");
+                        + " should first call stopIgnoringView(view) before calling recycle."
+                        + exceptionLabel());
             }
             //noinspection unchecked
             final boolean transientStatePreventsRecycling = holder
@@ -5762,8 +5878,8 @@
             boolean cached = false;
             boolean recycled = false;
             if (DEBUG && mCachedViews.contains(holder)) {
-                throw new IllegalArgumentException("cached view received recycle internal? " +
-                        holder);
+                throw new IllegalArgumentException("cached view received recycle internal? "
+                        + holder + exceptionLabel());
             }
             if (forceRecycle || holder.isRecyclable()) {
                 if (mViewCacheMax > 0
@@ -5809,7 +5925,8 @@
                 // to return it to the pool faster
                 if (DEBUG) {
                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
-                            + "re-visit here. We are still removing it from animation lists");
+                            + "re-visit here. We are still removing it from animation lists"
+                            + exceptionLabel());
                 }
             }
             // even if the holder is not removed, we still call this method so that it is removed
@@ -5830,7 +5947,10 @@
          */
         void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
             clearNestedRecyclerViewIfNotNested(holder);
-            ViewCompat.setAccessibilityDelegate(holder.itemView, null);
+            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
+                holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
+                ViewCompat.setAccessibilityDelegate(holder.itemView, null);
+            }
             if (dispatchRecycled) {
                 dispatchViewRecycled(holder);
             }
@@ -5867,7 +5987,7 @@
                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
                     throw new IllegalArgumentException("Called scrap view with an invalid view."
                             + " Invalid views cannot be reused from scrap, they should rebound from"
-                            + " recycler pool.");
+                            + " recycler pool." + exceptionLabel());
                 }
                 holder.setScrapContainer(this, false);
                 mAttachedScrap.add(holder);
@@ -5973,7 +6093,7 @@
                     int layoutIndex = mChildHelper.indexOfChild(view);
                     if (layoutIndex == RecyclerView.NO_POSITION) {
                         throw new IllegalStateException("layout index should not be -1 after "
-                                + "unhiding a view:" + vh);
+                                + "unhiding a view:" + vh + exceptionLabel());
                     }
                     mChildHelper.detachViewFromParent(layoutIndex);
                     scrapView(view);
@@ -6021,8 +6141,8 @@
                             // because item was invisible to us and we don't know what happened in
                             // between.
                             if (!mState.isPreLayout()) {
-                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
-                                        ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
+                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
+                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
                             }
                         }
                         return holder;
@@ -6098,8 +6218,8 @@
                     holder.offsetPosition(inBetweenOffset, false);
                 }
                 if (DEBUG) {
-                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
-                            holder);
+                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
+                            + holder);
                 }
             }
         }
@@ -6110,8 +6230,8 @@
                 final ViewHolder holder = mCachedViews.get(i);
                 if (holder != null && holder.mPosition >= insertedAt) {
                     if (DEBUG) {
-                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
-                                holder + " now at position " + (holder.mPosition + count));
+                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
+                                + holder + " now at position " + (holder.mPosition + count));
                     }
                     holder.offsetPosition(count, true);
                 }
@@ -6132,9 +6252,9 @@
                 if (holder != null) {
                     if (holder.mPosition >= removedEnd) {
                         if (DEBUG) {
-                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
-                                    " holder " + holder + " now at position " +
-                                    (holder.mPosition - count));
+                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
+                                    + " holder " + holder + " now at position "
+                                    + (holder.mPosition - count));
                         }
                         holder.offsetPosition(-count, applyToPreLayout);
                     } else if (holder.mPosition >= removedFrom) {
@@ -6263,7 +6383,7 @@
          * @return A View that is bound to the given position or NULL if there is no View to re-use
          * @see LayoutManager#ignoreView(View)
          */
-        abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
+        public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
     }
 
     /**
@@ -6271,8 +6391,10 @@
      *
      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
      * within a {@link RecyclerView}.</p>
+     *
+     * @param <VH> A class that extends ViewHolder that will be used by the adapter.
      */
-    public static abstract class Adapter<VH extends ViewHolder> {
+    public abstract static class Adapter<VH extends ViewHolder> {
         private final AdapterDataObservable mObservable = new AdapterDataObservable();
         private boolean mHasStableIds = false;
 
@@ -6419,8 +6541,8 @@
          */
         public void setHasStableIds(boolean hasStableIds) {
             if (hasObservers()) {
-                throw new IllegalStateException("Cannot change whether this adapter has " +
-                        "stable IDs while the adapter has registered observers.");
+                throw new IllegalStateException("Cannot change whether this adapter has "
+                        + "stable IDs while the adapter has registered observers.");
             }
             mHasStableIds = hasStableIds;
         }
@@ -6651,8 +6773,8 @@
         }
 
         /**
-         * Notify any registered observers that the item at <code>position</code> has changed with an
-         * optional payload object.
+         * Notify any registered observers that the item at <code>position</code> has changed with
+         * an optional payload object.
          *
          * <p>This is an item change event, not a structural change event. It indicates that any
          * reflection of the data at <code>position</code> is out of date and should be updated.
@@ -6856,7 +6978,7 @@
      * precedence.
      *
      */
-    public static abstract class LayoutManager {
+    public abstract static class LayoutManager {
         ChildHelper mChildHelper;
         RecyclerView mRecyclerView;
 
@@ -7136,7 +7258,7 @@
          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
          */
         public void requestLayout() {
-            if(mRecyclerView != null) {
+            if (mRecyclerView != null) {
                 mRecyclerView.requestLayout();
             }
         }
@@ -7343,9 +7465,9 @@
          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
          * traversals.</p>
          *
-         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for each
-         * item to be prepared, and these positions will have their ViewHolders created and bound,
-         * if there is sufficient time available, in advance of being needed by a
+         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
+         * each item to be prepared, and these positions will have their ViewHolders created and
+         * bound, if there is sufficient time available, in advance of being needed by a
          * scroll or layout.</p>
          *
          * @param dx X movement component.
@@ -7373,9 +7495,9 @@
          * vertically scrolling LayoutManager, this method would be called when the horizontal list
          * is about to come onscreen.</p>
          *
-         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for each
-         * item to be prepared, and these positions will have their ViewHolders created and bound,
-         * if there is sufficient time available, in advance of being needed by a
+         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
+         * each item to be prepared, and these positions will have their ViewHolders created and
+         * bound, if there is sufficient time available, in advance of being needed by a
          * scroll or layout.</p>
          *
          * @param adapterItemCount number of items in the associated adapter.
@@ -7866,7 +7988,7 @@
                 if (currentIndex == -1) {
                     throw new IllegalStateException("Added View has RecyclerView as parent but"
                             + " view is not a real child. Unfiltered index:"
-                            + mRecyclerView.indexOfChild(child));
+                            + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
                 }
                 if (currentIndex != index) {
                     mRecyclerView.mLayout.moveView(currentIndex, index);
@@ -8008,8 +8130,8 @@
                 if (vh == null) {
                     continue;
                 }
-                if (vh.getLayoutPosition() == position && !vh.shouldIgnore() &&
-                        (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
+                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
+                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
                     return child;
                 }
             }
@@ -8129,7 +8251,7 @@
             View view = getChildAt(fromIndex);
             if (view == null) {
                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
-                        + fromIndex);
+                        + fromIndex + mRecyclerView.toString());
             }
             detachViewAt(fromIndex);
             attachView(view, toIndex);
@@ -8352,9 +8474,11 @@
         /**
          * Returns the number of items in the adapter bound to the parent RecyclerView.
          * <p>
-         * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
-         * methods where State is available, you should use {@link State#getItemCount()} instead.
-         * For more details, check the documentation for {@link State#getItemCount()}.
+         * Note that this number is not necessarily equal to
+         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
+         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
+         * For more details, check the documentation for
+         * {@link State#getItemCount() State#getItemCount()}.
          *
          * @return The number of items in the bound adapter
          * @see State#getItemCount()
@@ -8408,7 +8532,8 @@
             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
                 // checking this because calling this method on a recycled or detached view may
                 // cause loss of state.
-                throw new IllegalArgumentException("View should be fully attached to be ignored");
+                throw new IllegalArgumentException("View should be fully attached to be ignored"
+                        + mRecyclerView.exceptionLabel());
             }
             final ViewHolder vh = getChildViewHolderInt(view);
             vh.addFlags(ViewHolder.FLAG_IGNORE);
@@ -8454,8 +8579,8 @@
                 }
                 return;
             }
-            if (viewHolder.isInvalid() && !viewHolder.isRemoved() &&
-                    !mRecyclerView.mAdapter.hasStableIds()) {
+            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
+                    && !mRecyclerView.mAdapter.hasStableIds()) {
                 removeViewAt(index);
                 recycler.recycleViewHolderInternal(viewHolder);
             } else {
@@ -8623,12 +8748,12 @@
             heightUsed += insets.top + insets.bottom;
 
             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
-                    getPaddingLeft() + getPaddingRight() +
-                            lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
+                    getPaddingLeft() + getPaddingRight()
+                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
                     canScrollHorizontally());
             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
-                    getPaddingTop() + getPaddingBottom() +
-                            lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
+                    getPaddingTop() + getPaddingBottom()
+                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
                     canScrollVertically());
             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
                 child.measure(widthSpec, heightSpec);
@@ -8860,7 +8985,7 @@
             }
 
             if (mRecyclerView != null) {
-                final Matrix childMatrix = ViewCompat.getMatrix(child);
+                final Matrix childMatrix = child.getMatrix();
                 if (childMatrix != null && !childMatrix.isIdentity()) {
                     final RectF tempRectF = mRecyclerView.mTempRectF;
                     tempRectF.set(out);
@@ -9181,10 +9306,11 @@
          * bounds.
          *
          * @param child The child view to be examined.
-         * @param completelyVisible If true, the method returns true iff the child is completely
-         *                          visible. If false, the method returns true iff the child is only
-         *                          partially visible (that is it will return false if the child is
-         *                          either completely visible or out of RV's bounds).
+         * @param completelyVisible If true, the method returns true if and only if the child is
+         *                          completely visible. If false, the method returns true if and
+         *                          only if the child is only partially visible (that is it will
+         *                          return false if the child is either completely visible or out
+         *                          of RV's bounds).
          * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
          *                                bounds is enough to consider it partially visible,
          *                                false otherwise.
@@ -9605,22 +9731,20 @@
          */
         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
                 AccessibilityNodeInfoCompat info) {
-            if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
-                    ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
+            if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
                 info.setScrollable(true);
             }
-            if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
-                    ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
+            if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
                 info.setScrollable(true);
             }
-            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
-                    = AccessibilityNodeInfoCompat.CollectionInfoCompat
-                    .obtain(getRowCountForAccessibility(recycler, state),
-                            getColumnCountForAccessibility(recycler, state),
-                            isLayoutHierarchical(recycler, state),
-                            getSelectionModeForAccessibility(recycler, state));
+            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
+                    AccessibilityNodeInfoCompat.CollectionInfoCompat
+                            .obtain(getRowCountForAccessibility(recycler, state),
+                                    getColumnCountForAccessibility(recycler, state),
+                                    isLayoutHierarchical(recycler, state),
+                                    getSelectionModeForAccessibility(recycler, state));
             info.setCollectionInfo(collectionInfo);
         }
 
@@ -9642,18 +9766,16 @@
          */
         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
                 AccessibilityEvent event) {
-            final AccessibilityRecordCompat record = AccessibilityEventCompat
-                    .asRecord(event);
-            if (mRecyclerView == null || record == null) {
+            if (mRecyclerView == null || event == null) {
                 return;
             }
-            record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
-                    || ViewCompat.canScrollVertically(mRecyclerView, -1)
-                    || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
-                    || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
+            event.setScrollable(mRecyclerView.canScrollVertically(1)
+                    || mRecyclerView.canScrollVertically(-1)
+                    || mRecyclerView.canScrollHorizontally(-1)
+                    || mRecyclerView.canScrollHorizontally(1));
 
             if (mRecyclerView.mAdapter != null) {
-                record.setItemCount(mRecyclerView.mAdapter.getItemCount());
+                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
             }
         }
 
@@ -9685,9 +9807,9 @@
                 View host, AccessibilityNodeInfoCompat info) {
             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
-            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
-                    = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
-                    columnIndexGuess, 1, false, false);
+            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
+                    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
+                            columnIndexGuess, 1, false, false);
             info.setCollectionItemInfo(itemInfo);
         }
 
@@ -9800,18 +9922,18 @@
             int vScroll = 0, hScroll = 0;
             switch (action) {
                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
-                    if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
+                    if (mRecyclerView.canScrollVertically(-1)) {
                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
                     }
-                    if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
+                    if (mRecyclerView.canScrollHorizontally(-1)) {
                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
                     }
                     break;
                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
-                    if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
+                    if (mRecyclerView.canScrollVertically(1)) {
                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
                     }
-                    if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
+                    if (mRecyclerView.canScrollHorizontally(1)) {
                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
                     }
                     break;
@@ -9864,7 +9986,8 @@
             Properties properties = new Properties();
             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
                     defStyleAttr, defStyleRes);
-            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation, VERTICAL);
+            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
+                    VERTICAL);
             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
@@ -9930,7 +10053,7 @@
      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
      * RecyclerView.State)}.</p>
      */
-    public static abstract class ItemDecoration {
+    public abstract static class ItemDecoration {
         /**
          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
          * Any content drawn by this method will be drawn before the item views are drawn,
@@ -10021,7 +10144,7 @@
      *
      * @see SimpleOnItemTouchListener
      */
-    public static interface OnItemTouchListener {
+    public interface OnItemTouchListener {
         /**
          * Silently observe and/or take over touch events sent to the RecyclerView
          * before they are handled by either the RecyclerView itself or its child views.
@@ -10036,7 +10159,7 @@
          *         to continue with the current behavior and continue observing future events in
          *         the gesture.
          */
-        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
+        boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
 
         /**
          * Process a touch event as part of a gesture that was claimed by returning true from
@@ -10045,7 +10168,7 @@
          * @param e MotionEvent describing the touch event. All coordinates are in
          *          the RecyclerView's coordinate system.
          */
-        public void onTouchEvent(RecyclerView rv, MotionEvent e);
+        void onTouchEvent(RecyclerView rv, MotionEvent e);
 
         /**
          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
@@ -10056,12 +10179,12 @@
          *            intercept touch events.
          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
          */
-        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
+        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
     }
 
     /**
-     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
-     * default return values.
+     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
+     * and default return values.
      * <p>
      * You may prefer to extend this class if you don't need to override all methods. Another
      * benefit of using this class is future compatibility. As the interface may change, we'll
@@ -10134,7 +10257,7 @@
          *
          * @param holder The ViewHolder containing the view that was recycled
          */
-        public void onViewRecycled(ViewHolder holder);
+        void onViewRecycled(ViewHolder holder);
     }
 
     /**
@@ -10148,14 +10271,14 @@
          *
          * @param view The View which is attached to the RecyclerView
          */
-        public void onChildViewAttachedToWindow(View view);
+        void onChildViewAttachedToWindow(View view);
 
         /**
          * Called when a view is detached from RecyclerView.
          *
          * @param view The View which is being detached from the RecyclerView
          */
-        public void onChildViewDetachedFromWindow(View view);
+        void onChildViewDetachedFromWindow(View view);
     }
 
     /**
@@ -10171,7 +10294,7 @@
      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
      * strong references to extra off-screen item views for caching purposes</p>
      */
-    public static abstract class ViewHolder {
+    public abstract static class ViewHolder {
         public final View itemView;
         WeakReference<RecyclerView> mNestedRecyclerView;
         int mPosition = NO_POSITION;
@@ -10278,6 +10401,12 @@
          */
         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
 
+        /**
+         * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
+         * #getItemDelegate()} in onBindView when app does not provide a delegate.
+         */
+        static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
+
         private int mFlags;
 
         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
@@ -10590,9 +10719,9 @@
 
         @Override
         public String toString() {
-            final StringBuilder sb = new StringBuilder("ViewHolder{" +
-                    Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
-                    ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
+            final StringBuilder sb = new StringBuilder("ViewHolder{"
+                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
+                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
             if (isScrap()) {
                 sb.append(" scrap ")
                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
@@ -10629,11 +10758,11 @@
             if (mIsRecyclableCount < 0) {
                 mIsRecyclableCount = 0;
                 if (DEBUG) {
-                    throw new RuntimeException("isRecyclable decremented below 0: " +
-                            "unmatched pair of setIsRecyable() calls for " + this);
+                    throw new RuntimeException("isRecyclable decremented below 0: "
+                            + "unmatched pair of setIsRecyable() calls for " + this);
                 }
-                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
-                        "unmatched pair of setIsRecyable() calls for " + this);
+                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
+                        + "unmatched pair of setIsRecyable() calls for " + this);
             } else if (!recyclable && mIsRecyclableCount == 1) {
                 mFlags |= FLAG_NOT_RECYCLABLE;
             } else if (recyclable && mIsRecyclableCount == 0) {
@@ -10650,8 +10779,8 @@
          * @see #setIsRecyclable(boolean)
          */
         public final boolean isRecyclable() {
-            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
-                    !ViewCompat.hasTransientState(itemView);
+            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
+                    && !ViewCompat.hasTransientState(itemView);
         }
 
         /**
@@ -10691,7 +10820,7 @@
     }
 
     void dispatchPendingImportantForAccessibilityChanges() {
-        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i --) {
+        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
                 continue;
@@ -10708,14 +10837,32 @@
     }
 
     int getAdapterPositionFor(ViewHolder viewHolder) {
-        if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
-                ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
+        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
+                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
                 || !viewHolder.isBound()) {
             return RecyclerView.NO_POSITION;
         }
         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
     }
 
+    @VisibleForTesting
+    void initFastScroller(StateListDrawable verticalThumbDrawable,
+            Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
+            Drawable horizontalTrackDrawable) {
+        if (verticalThumbDrawable == null || verticalTrackDrawable == null
+                || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
+            throw new IllegalArgumentException(
+                "Trying to set fast scroller without both required drawables." + exceptionLabel());
+        }
+
+        Resources resources = getContext().getResources();
+        new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
+                horizontalThumbDrawable, horizontalTrackDrawable,
+                resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
+                resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
+                resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
+    }
+
     // NestedScrollingChild
 
     @Override
@@ -10734,16 +10881,31 @@
     }
 
     @Override
+    public boolean startNestedScroll(int axes, int type) {
+        return getScrollingChildHelper().startNestedScroll(axes, type);
+    }
+
+    @Override
     public void stopNestedScroll() {
         getScrollingChildHelper().stopNestedScroll();
     }
 
     @Override
+    public void stopNestedScroll(int type) {
+        getScrollingChildHelper().stopNestedScroll(type);
+    }
+
+    @Override
     public boolean hasNestedScrollingParent() {
         return getScrollingChildHelper().hasNestedScrollingParent();
     }
 
     @Override
+    public boolean hasNestedScrollingParent(int type) {
+        return getScrollingChildHelper().hasNestedScrollingParent(type);
+    }
+
+    @Override
     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
             int dyUnconsumed, int[] offsetInWindow) {
         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
@@ -10751,11 +10913,25 @@
     }
 
     @Override
+    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
+            int dyUnconsumed, int[] offsetInWindow, int type) {
+        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
+                dxUnconsumed, dyUnconsumed, offsetInWindow, type);
+    }
+
+    @Override
     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
     }
 
     @Override
+    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
+            int type) {
+        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
+                type);
+    }
+
+    @Override
     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
     }
@@ -10877,7 +11053,7 @@
      * Observer base class for watching changes to an {@link Adapter}.
      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
      */
-    public static abstract class AdapterDataObserver {
+    public abstract static class AdapterDataObserver {
         public void onChanged() {
             // Do nothing
         }
@@ -10911,7 +11087,7 @@
      *
      * @see LinearSmoothScroller
      */
-    public static abstract class SmoothScroller {
+    public abstract static class SmoothScroller {
 
         private int mTargetPosition = RecyclerView.NO_POSITION;
 
@@ -10976,7 +11152,7 @@
          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
          */
-        final protected void stop() {
+        protected final void stop() {
             if (!mRunning) {
                 return;
             }
@@ -11099,8 +11275,8 @@
          * @param scrollVector The vector that points to the target scroll position
          */
         protected void normalize(PointF scrollVector) {
-            final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
-                    * scrollVector.y);
+            final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
+                    + scrollVector.y * scrollVector.y);
             scrollVector.x /= magnitude;
             scrollVector.y /= magnitude;
         }
@@ -11108,13 +11284,13 @@
         /**
          * Called when smooth scroll is started. This might be a good time to do setup.
          */
-        abstract protected void onStart();
+        protected abstract void onStart();
 
         /**
          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
          * @see #stop()
          */
-        abstract protected void onStop();
+        protected abstract void onStop();
 
         /**
          * <p>RecyclerView will call this method each time it scrolls until it can find the target
@@ -11128,7 +11304,7 @@
          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
          *                  update this object.
          */
-        abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
+        protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
 
         /**
          * Called when the target position is laid out. This is the last callback SmoothScroller
@@ -11139,7 +11315,7 @@
          * @param action        Action instance that you should update to define final scroll action
          *                      towards the targetView
          */
-        abstract protected void onTargetFound(View targetView, State state, Action action);
+        protected abstract void onTargetFound(View targetView, State state, Action action);
 
         /**
          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
@@ -11158,11 +11334,11 @@
 
             private Interpolator mInterpolator;
 
-            private boolean changed = false;
+            private boolean mChanged = false;
 
             // we track this variable to inform custom implementer if they are updating the action
             // in every animation callback
-            private int consecutiveUpdates = 0;
+            private int mConsecutiveUpdates = 0;
 
             /**
              * @param dx Pixels to scroll horizontally
@@ -11223,10 +11399,10 @@
                     final int position = mJumpToPosition;
                     mJumpToPosition = NO_POSITION;
                     recyclerView.jumpToPositionForSmoothScroller(position);
-                    changed = false;
+                    mChanged = false;
                     return;
                 }
-                if (changed) {
+                if (mChanged) {
                     validate();
                     if (mInterpolator == null) {
                         if (mDuration == UNDEFINED_DURATION) {
@@ -11235,18 +11411,19 @@
                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
                         }
                     } else {
-                        recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
+                        recyclerView.mViewFlinger.smoothScrollBy(
+                                mDx, mDy, mDuration, mInterpolator);
                     }
-                    consecutiveUpdates ++;
-                    if (consecutiveUpdates > 10) {
+                    mConsecutiveUpdates++;
+                    if (mConsecutiveUpdates > 10) {
                         // A new action is being set in every animation step. This looks like a bad
                         // implementation. Inform developer.
                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
                                 + " you are not changing it unless necessary");
                     }
-                    changed = false;
+                    mChanged = false;
                 } else {
-                    consecutiveUpdates = 0;
+                    mConsecutiveUpdates = 0;
                 }
             }
 
@@ -11264,7 +11441,7 @@
             }
 
             public void setDx(int dx) {
-                changed = true;
+                mChanged = true;
                 mDx = dx;
             }
 
@@ -11273,7 +11450,7 @@
             }
 
             public void setDy(int dy) {
-                changed = true;
+                mChanged = true;
                 mDy = dy;
             }
 
@@ -11282,7 +11459,7 @@
             }
 
             public void setDuration(int duration) {
-                changed = true;
+                mChanged = true;
                 mDuration = duration;
             }
 
@@ -11297,7 +11474,7 @@
              * @see #setDuration(int)
              */
             public void setInterpolator(Interpolator interpolator) {
-                changed = true;
+                mChanged = true;
                 mInterpolator = interpolator;
             }
 
@@ -11314,7 +11491,7 @@
                 mDy = dy;
                 mDuration = duration;
                 mInterpolator = interpolator;
-                changed = true;
+                mChanged = true;
             }
         }
 
@@ -11400,7 +11577,7 @@
     }
 
     /**
-     * This is public so that the CREATOR can be access on cold launch.
+     * This is public so that the CREATOR can be accessed on cold launch.
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
@@ -11434,19 +11611,24 @@
             mLayoutState = other.mLayoutState;
         }
 
-        public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
-                new ParcelableCompatCreatorCallbacks<SavedState>() {
-                    @Override
-                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                        return new SavedState(in, loader);
-                    }
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
 
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                });
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
     }
+
     /**
      * <p>Contains useful information about the current RecyclerView state like target scroll
      * position or view focus. State object can also keep arbitrary data, identified by resource
@@ -11536,6 +11718,9 @@
         // that one instead
         int mFocusedSubChildId;
 
+        int mRemainingScrollHorizontal;
+        int mRemainingScrollVertical;
+
         ////////////////////////////////////////////////////////////////////////////////////////////
 
         State reset() {
@@ -11632,6 +11817,7 @@
          *                   to
          *                   preserve cross functionality and avoid conflicts.
          */
+        @SuppressWarnings("TypeParameterUnusedInFormals")
         public <T> T get(int resourceId) {
             if (mData == null) {
                 return null;
@@ -11707,25 +11893,47 @@
          * @see LayoutManager#getItemCount()
          */
         public int getItemCount() {
-            return mInPreLayout ?
-                    (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :
-                    mItemCount;
+            return mInPreLayout
+                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
+                    : mItemCount;
+        }
+
+        /**
+         * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
+         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
+         * other than {@link #SCROLL_STATE_SETTLING}.
+         *
+         * @return Remaining horizontal scroll distance
+         */
+        public int getRemainingScrollHorizontal() {
+            return mRemainingScrollHorizontal;
+        }
+
+        /**
+         * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
+         * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
+         * other than {@link #SCROLL_STATE_SETTLING}.
+         *
+         * @return Remaining vertical scroll distance
+         */
+        public int getRemainingScrollVertical() {
+            return mRemainingScrollVertical;
         }
 
         @Override
         public String toString() {
-            return "State{" +
-                    "mTargetPosition=" + mTargetPosition +
-                    ", mData=" + mData +
-                    ", mItemCount=" + mItemCount +
-                    ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount +
-                    ", mDeletedInvisibleItemCountSincePreviousLayout="
-                    + mDeletedInvisibleItemCountSincePreviousLayout +
-                    ", mStructureChanged=" + mStructureChanged +
-                    ", mInPreLayout=" + mInPreLayout +
-                    ", mRunSimpleAnimations=" + mRunSimpleAnimations +
-                    ", mRunPredictiveAnimations=" + mRunPredictiveAnimations +
-                    '}';
+            return "State{"
+                    + "mTargetPosition=" + mTargetPosition
+                    + ", mData=" + mData
+                    + ", mItemCount=" + mItemCount
+                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
+                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
+                    + mDeletedInvisibleItemCountSincePreviousLayout
+                    + ", mStructureChanged=" + mStructureChanged
+                    + ", mInPreLayout=" + mInPreLayout
+                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
+                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
+                    + '}';
         }
     }
 
@@ -11736,7 +11944,7 @@
      *
      * @see #setOnFlingListener(OnFlingListener)
      */
-    public static abstract class OnFlingListener {
+    public abstract static class OnFlingListener {
 
         /**
          * Override this to handle a fling given the velocities in both x and y directions.
@@ -11801,7 +12009,7 @@
      * @see #setItemAnimator(ItemAnimator)
      */
     @SuppressWarnings("UnusedParameters")
-    public static abstract class ItemAnimator {
+    public abstract static class ItemAnimator {
 
         /**
          * The Item represented by this ViewHolder is updated.
@@ -11844,14 +12052,14 @@
          * <p>
          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
          */
-        public static final int FLAG_APPEARED_IN_PRE_LAYOUT
-                = ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
+        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
+                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
 
         /**
          * The set of flags that might be passed to
          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
          */
-        @IntDef(flag=true, value={
+        @IntDef(flag = true, value = {
                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
                 FLAG_APPEARED_IN_PRE_LAYOUT
         })
@@ -12193,7 +12401,7 @@
             if ((flags & FLAG_INVALIDATED) == 0) {
                 final int oldPos = viewHolder.getOldPosition();
                 final int pos = viewHolder.getAdapterPosition();
-                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos){
+                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
                     flags |= FLAG_MOVED;
                 }
             }
@@ -12214,7 +12422,7 @@
          * called later to start the associated animations. runPendingAnimations() will be scheduled
          * to be run on the next frame.
          */
-        abstract public void runPendingAnimations();
+        public abstract void runPendingAnimations();
 
         /**
          * Method called when an animation on a view should be ended immediately.
@@ -12227,7 +12435,7 @@
          *
          * @param item The item for which an animation should be stopped.
          */
-        abstract public void endAnimation(ViewHolder item);
+        public abstract void endAnimation(ViewHolder item);
 
         /**
          * Method called when all item animations should be ended immediately.
@@ -12238,7 +12446,7 @@
          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
          * animation since the animations are effectively done when this method is called.
          */
-        abstract public void endAnimations();
+        public abstract void endAnimations();
 
         /**
          * Method which returns whether there are any item animations currently running.
@@ -12247,7 +12455,7 @@
          *
          * @return true if there are any item animations currently running, false otherwise.
          */
-        abstract public boolean isRunning();
+        public abstract boolean isRunning();
 
         /**
          * Method to be called by subclasses when an animation is finished.
@@ -12450,6 +12658,9 @@
          * @see #isRunning(ItemAnimatorFinishedListener)
          */
         public interface ItemAnimatorFinishedListener {
+            /**
+             * Notifies when all pending or running animations in an ItemAnimator are finished.
+             */
             void onAnimationsFinished();
         }
 
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
index c44c802..ba7260d 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
@@ -29,10 +29,12 @@
  */
 public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegateCompat {
     final RecyclerView mRecyclerView;
+    final AccessibilityDelegateCompat mItemDelegate;
 
 
     public RecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
         mRecyclerView = recyclerView;
+        mItemDelegate = new ItemDelegate(this);
     }
 
     boolean shouldIgnore() {
@@ -81,13 +83,33 @@
         return mItemDelegate;
     }
 
-    final AccessibilityDelegateCompat mItemDelegate = new AccessibilityDelegateCompat() {
+    /**
+     * The default implementation of accessibility delegate for the individual items of the
+     * RecyclerView.
+     * <p>
+     * If you are overriding {@code RecyclerViewAccessibilityDelegate#getItemDelegate()} but still
+     * want to keep some default behavior, you can create an instance of this class and delegate to
+     * the parent as necessary.
+     */
+    public static class ItemDelegate extends AccessibilityDelegateCompat {
+        final RecyclerViewAccessibilityDelegate mRecyclerViewDelegate;
+
+        /**
+         * Creates an item delegate for the given {@code RecyclerViewAccessibilityDelegate}.
+         *
+         * @param recyclerViewDelegate The parent RecyclerView's accessibility delegate.
+         */
+        public ItemDelegate(RecyclerViewAccessibilityDelegate recyclerViewDelegate) {
+            mRecyclerViewDelegate = recyclerViewDelegate;
+        }
+
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
-            if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
-                mRecyclerView.getLayoutManager().
-                        onInitializeAccessibilityNodeInfoForItem(host, info);
+            if (!mRecyclerViewDelegate.shouldIgnore()
+                    && mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
+                mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
+                        .onInitializeAccessibilityNodeInfoForItem(host, info);
             }
         }
 
@@ -96,11 +118,13 @@
             if (super.performAccessibilityAction(host, action, args)) {
                 return true;
             }
-            if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
-                return mRecyclerView.getLayoutManager().
-                        performAccessibilityActionForItem(host, action, args);
+            if (!mRecyclerViewDelegate.shouldIgnore()
+                    && mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
+                return mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
+                        .performAccessibilityActionForItem(host, action, args);
             }
             return false;
         }
-    };
+    }
 }
+
diff --git a/v7/recyclerview/src/android/support/v7/widget/ScrollbarHelper.java b/v7/recyclerview/src/android/support/v7/widget/ScrollbarHelper.java
index 724fac8..ae42baa 100644
--- a/v7/recyclerview/src/android/support/v7/widget/ScrollbarHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/ScrollbarHelper.java
@@ -29,8 +29,8 @@
     static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
             View startChild, View endChild, RecyclerView.LayoutManager lm,
             boolean smoothScrollbarEnabled, boolean reverseLayout) {
-        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
-                endChild == null) {
+        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
+                || endChild == null) {
             return 0;
         }
         final int minPosition = Math.min(lm.getPosition(startChild),
@@ -43,10 +43,10 @@
         if (!smoothScrollbarEnabled) {
             return itemsBefore;
         }
-        final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
-                orientation.getDecoratedStart(startChild));
-        final int itemRange = Math.abs(lm.getPosition(startChild) -
-                lm.getPosition(endChild)) + 1;
+        final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild)
+                - orientation.getDecoratedStart(startChild));
+        final int itemRange = Math.abs(lm.getPosition(startChild)
+                - lm.getPosition(endChild)) + 1;
         final float avgSizePerRow = (float) laidOutArea / itemRange;
 
         return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
@@ -60,8 +60,8 @@
     static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation,
             View startChild, View endChild, RecyclerView.LayoutManager lm,
             boolean smoothScrollbarEnabled) {
-        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
-                endChild == null) {
+        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
+                || endChild == null) {
             return 0;
         }
         if (!smoothScrollbarEnabled) {
@@ -79,18 +79,18 @@
     static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation,
             View startChild, View endChild, RecyclerView.LayoutManager lm,
             boolean smoothScrollbarEnabled) {
-        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
-                endChild == null) {
+        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
+                || endChild == null) {
             return 0;
         }
         if (!smoothScrollbarEnabled) {
             return state.getItemCount();
         }
         // smooth scrollbar enabled. try to estimate better.
-        final int laidOutArea = orientation.getDecoratedEnd(endChild) -
-                orientation.getDecoratedStart(startChild);
-        final int laidOutRange = Math.abs(lm.getPosition(startChild) -
-                lm.getPosition(endChild))
+        final int laidOutArea = orientation.getDecoratedEnd(endChild)
+                - orientation.getDecoratedStart(startChild);
+        final int laidOutRange = Math.abs(lm.getPosition(startChild)
+                - lm.getPosition(endChild))
                 + 1;
         // estimate a size for full list.
         return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
diff --git a/v7/recyclerview/src/android/support/v7/widget/SimpleItemAnimator.java b/v7/recyclerview/src/android/support/v7/widget/SimpleItemAnimator.java
index 2db7541..fe704dc 100644
--- a/v7/recyclerview/src/android/support/v7/widget/SimpleItemAnimator.java
+++ b/v7/recyclerview/src/android/support/v7/widget/SimpleItemAnimator.java
@@ -1,15 +1,29 @@
+/*
+ * 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.
+ */
+
 package android.support.v7.widget;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView.Adapter;
-import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
+import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.Log;
 import android.view.View;
 
-import java.util.List;
-
 /**
  * A wrapper class for ItemAnimator that records View bounds and decides whether it should run
  * move, change, add or remove animations. This class also replicates the original ItemAnimator
@@ -20,7 +34,7 @@
  * extend this class, you can override {@link #obtainHolderInfo()} method to provide your own info
  * class that extends {@link ItemHolderInfo}.
  */
-abstract public class SimpleItemAnimator extends RecyclerView.ItemAnimator {
+public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
 
     private static final boolean DEBUG = false;
 
@@ -120,8 +134,8 @@
             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
         if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
             if (DEBUG) {
-                Log.d(TAG, "PERSISTENT: " + viewHolder +
-                        " with view " + viewHolder.itemView);
+                Log.d(TAG, "PERSISTENT: " + viewHolder
+                        + " with view " + viewHolder.itemView);
             }
             return animateMove(viewHolder,
                     preInfo.left, preInfo.top, postInfo.left, postInfo.top);
@@ -172,7 +186,7 @@
      * @return true if a later call to {@link #runPendingAnimations()} is requested,
      * false otherwise.
      */
-    abstract public boolean animateRemove(ViewHolder holder);
+    public abstract boolean animateRemove(ViewHolder holder);
 
     /**
      * Called when an item is added to the RecyclerView. Implementors can choose
@@ -197,7 +211,7 @@
      * @return true if a later call to {@link #runPendingAnimations()} is requested,
      * false otherwise.
      */
-    abstract public boolean animateAdd(ViewHolder holder);
+    public abstract boolean animateAdd(ViewHolder holder);
 
     /**
      * Called when an item is moved in the RecyclerView. Implementors can choose
@@ -217,7 +231,7 @@
      * @return true if a later call to {@link #runPendingAnimations()} is requested,
      * false otherwise.
      */
-    abstract public boolean animateMove(ViewHolder holder, int fromX, int fromY,
+    public abstract boolean animateMove(ViewHolder holder, int fromX, int fromY,
             int toX, int toY);
 
     /**
@@ -250,7 +264,7 @@
      * @return true if a later call to {@link #runPendingAnimations()} is requested,
      * false otherwise.
      */
-    abstract public boolean animateChange(ViewHolder oldHolder,
+    public abstract boolean animateChange(ViewHolder oldHolder,
             ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
 
     /**
@@ -440,3 +454,4 @@
     public void onChangeFinished(ViewHolder item, boolean oldItem) {
     }
 }
+
diff --git a/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java b/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java
index d63045b..d85a27a 100644
--- a/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java
@@ -19,6 +19,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.SmoothScroller;
 import android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider;
 import android.util.DisplayMetrics;
 import android.view.View;
@@ -159,7 +160,7 @@
             return false;
         }
 
-        RecyclerView.SmoothScroller smoothScroller = createSnapScroller(layoutManager);
+        SmoothScroller smoothScroller = createScroller(layoutManager);
         if (smoothScroller == null) {
             return false;
         }
@@ -203,9 +204,24 @@
      * @param layoutManager     The {@link RecyclerView.LayoutManager} associated with the attached
      *                          {@link RecyclerView}.
      *
-     * @return a {@link LinearSmoothScroller} which will handle the scrolling.
+     * @return a {@link SmoothScroller} which will handle the scrolling.
      */
     @Nullable
+    protected SmoothScroller createScroller(LayoutManager layoutManager) {
+        return createSnapScroller(layoutManager);
+    }
+
+    /**
+     * Creates a scroller to be used in the snapping implementation.
+     *
+     * @param layoutManager     The {@link RecyclerView.LayoutManager} associated with the attached
+     *                          {@link RecyclerView}.
+     *
+     * @return a {@link LinearSmoothScroller} which will handle the scrolling.
+     * @deprecated use {@link #createScroller(LayoutManager)} instead.
+     */
+    @Nullable
+    @Deprecated
     protected LinearSmoothScroller createSnapScroller(LayoutManager layoutManager) {
         if (!(layoutManager instanceof ScrollVectorProvider)) {
             return null;
@@ -281,4 +297,4 @@
      */
     public abstract int findTargetSnapPosition(LayoutManager layoutManager, int velocityX,
             int velocityY);
-}
\ No newline at end of file
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index bbf4acd..f3ea045 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -32,9 +32,7 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -57,7 +55,7 @@
 public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager implements
         RecyclerView.SmoothScroller.ScrollVectorProvider {
 
-    private static final String TAG = "StaggeredGridLayoutManager";
+    private static final String TAG = "StaggeredGridLManager";
 
     static final boolean DEBUG = false;
 
@@ -373,7 +371,7 @@
                     int myEnd = mPrimaryOrientation.getDecoratedEnd(child);
                     int nextEnd = mPrimaryOrientation.getDecoratedEnd(nextChild);
                     if (myEnd < nextEnd) {
-                        return child;//i should have a better position
+                        return child; //i should have a better position
                     } else if (myEnd == nextEnd) {
                         compareSpans = true;
                     }
@@ -381,7 +379,7 @@
                     int myStart = mPrimaryOrientation.getDecoratedStart(child);
                     int nextStart = mPrimaryOrientation.getDecoratedStart(nextChild);
                     if (myStart > nextStart) {
-                        return child;//i should have a better position
+                        return child; //i should have a better position
                     } else if (myStart == nextStart) {
                         compareSpans = true;
                     }
@@ -514,8 +512,8 @@
         if (gapStrategy == mGapStrategy) {
             return;
         }
-        if (gapStrategy != GAP_HANDLING_NONE &&
-                gapStrategy != GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS) {
+        if (gapStrategy != GAP_HANDLING_NONE
+                && gapStrategy != GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS) {
             throw new IllegalArgumentException("invalid gap strategy. Must be GAP_HANDLING_NONE "
                     + "or GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS");
         }
@@ -618,8 +616,8 @@
             }
         }
 
-        boolean recalculateAnchor = !anchorInfo.mValid || mPendingScrollPosition != NO_POSITION ||
-                mPendingSavedState != null;
+        boolean recalculateAnchor = !anchorInfo.mValid || mPendingScrollPosition != NO_POSITION
+                || mPendingSavedState != null;
         if (recalculateAnchor) {
             anchorInfo.reset();
             if (mPendingSavedState != null) {
@@ -632,15 +630,15 @@
             anchorInfo.mValid = true;
         }
         if (mPendingSavedState == null && mPendingScrollPosition == NO_POSITION) {
-            if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd ||
-                    isLayoutRTL() != mLastLayoutRTL) {
+            if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd
+                    || isLayoutRTL() != mLastLayoutRTL) {
                 mLazySpanLookup.clear();
                 anchorInfo.mInvalidateOffsets = true;
             }
         }
 
-        if (getChildCount() > 0 && (mPendingSavedState == null ||
-                mPendingSavedState.mSpanOffsetsSize < 1)) {
+        if (getChildCount() > 0 && (mPendingSavedState == null
+                || mPendingSavedState.mSpanOffsetsSize < 1)) {
             if (anchorInfo.mInvalidateOffsets) {
                 for (int i = 0; i < mSpanCount; i++) {
                     // Scroll to position is set, clear.
@@ -737,7 +735,7 @@
         }
         float maxSize = 0;
         final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i ++) {
+        for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
             float size = mSecondaryOrientation.getDecoratedMeasurement(child);
             if (size < maxSize) {
@@ -758,7 +756,7 @@
         if (mSizePerSpan == before) {
             return; // nothing has changed
         }
-        for (int i = 0; i < childCount; i ++) {
+        for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.mFullSpan) {
@@ -868,12 +866,12 @@
                         : getFirstChildPosition();
                 if (mPendingScrollPositionOffset != INVALID_OFFSET) {
                     if (anchorInfo.mLayoutFromEnd) {
-                        final int target = mPrimaryOrientation.getEndAfterPadding() -
-                                mPendingScrollPositionOffset;
+                        final int target = mPrimaryOrientation.getEndAfterPadding()
+                                - mPendingScrollPositionOffset;
                         anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedEnd(child);
                     } else {
-                        final int target = mPrimaryOrientation.getStartAfterPadding() +
-                                mPendingScrollPositionOffset;
+                        final int target = mPrimaryOrientation.getStartAfterPadding()
+                                + mPendingScrollPositionOffset;
                         anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedStart(child);
                     }
                     return true;
@@ -895,8 +893,8 @@
                     anchorInfo.mOffset = -startGap;
                     return true;
                 }
-                final int endGap = mPrimaryOrientation.getEndAfterPadding() -
-                        mPrimaryOrientation.getDecoratedEnd(child);
+                final int endGap = mPrimaryOrientation.getEndAfterPadding()
+                        - mPrimaryOrientation.getDecoratedEnd(child);
                 if (endGap < 0) {
                     anchorInfo.mOffset = endGap;
                     return true;
@@ -1266,8 +1264,6 @@
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         if (getChildCount() > 0) {
-            final AccessibilityRecordCompat record = AccessibilityEventCompat
-                    .asRecord(event);
             final View start = findFirstVisibleItemClosestToStart(false);
             final View end = findFirstVisibleItemClosestToEnd(false);
             if (start == null || end == null) {
@@ -1276,11 +1272,11 @@
             final int startPos = getPosition(start);
             final int endPos = getPosition(end);
             if (startPos < endPos) {
-                record.setFromIndex(startPos);
-                record.setToIndex(endPos);
+                event.setFromIndex(startPos);
+                event.setToIndex(endPos);
             } else {
-                record.setFromIndex(endPos);
-                record.setToIndex(startPos);
+                event.setFromIndex(endPos);
+                event.setToIndex(startPos);
             }
         }
     }
@@ -1329,7 +1325,7 @@
             final View child = getChildAt(i);
             final int childStart = mPrimaryOrientation.getDecoratedStart(child);
             final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
-            if(childEnd <= boundsStart || childStart >= boundsEnd) {
+            if (childEnd <= boundsStart || childStart >= boundsEnd) {
                 continue; // not visible at all
             }
             if (childStart >= boundsStart || !fullyVisible) {
@@ -1358,7 +1354,7 @@
             final View child = getChildAt(i);
             final int childStart = mPrimaryOrientation.getDecoratedStart(child);
             final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
-            if(childEnd <= boundsStart || childStart >= boundsEnd) {
+            if (childEnd <= boundsStart || childStart >= boundsEnd) {
                 continue; // not visible at all
             }
             if (childEnd <= boundsEnd || !fullyVisible) {
@@ -1438,14 +1434,14 @@
         }
         mLayoutState.mStopInFocusable = false;
         mLayoutState.mRecycle = true;
-        mLayoutState.mInfinite = mPrimaryOrientation.getMode() == View.MeasureSpec.UNSPECIFIED &&
-                mPrimaryOrientation.getEnd() == 0;
+        mLayoutState.mInfinite = mPrimaryOrientation.getMode() == View.MeasureSpec.UNSPECIFIED
+                && mPrimaryOrientation.getEnd() == 0;
     }
 
     private void setLayoutStateDirection(int direction) {
         mLayoutState.mLayoutDirection = direction;
-        mLayoutState.mItemDirection = (mShouldReverseLayout == (direction == LAYOUT_START)) ?
-                ITEM_DIRECTION_TAIL : ITEM_DIRECTION_HEAD;
+        mLayoutState.mItemDirection = (mShouldReverseLayout == (direction == LAYOUT_START))
+                ? ITEM_DIRECTION_TAIL : ITEM_DIRECTION_HEAD;
     }
 
     @Override
@@ -1496,8 +1492,8 @@
      */
     private void handleUpdate(int positionStart, int itemCountOrToPosition, int cmd) {
         int minPosition = mShouldReverseLayout ? getLastChildPosition() : getFirstChildPosition();
-        final int affectedRangeEnd;// exclusive
-        final int affectedRangeStart;// inclusive
+        final int affectedRangeEnd; // exclusive
+        final int affectedRangeStart; // inclusive
 
         if (cmd == AdapterHelper.UpdateOp.MOVE) {
             if (positionStart < itemCountOrToPosition) {
@@ -1560,8 +1556,8 @@
 
         updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
         if (DEBUG) {
-            Log.d(TAG, "FILLING targetLine: " + targetLine + "," +
-                    "remaining spans:" + mRemainingSpans + ", state: " + layoutState);
+            Log.d(TAG, "FILLING targetLine: " + targetLine + ","
+                    + "remaining spans:" + mRemainingSpans + ", state: " + layoutState);
         }
 
         // the default coordinate to add new view.
@@ -1655,8 +1651,8 @@
                 otherStart = otherEnd - mSecondaryOrientation.getDecoratedMeasurement(view);
             } else {
                 otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding()
-                        : currentSpan.mIndex * mSizePerSpan +
-                                mSecondaryOrientation.getStartAfterPadding();
+                        : currentSpan.mIndex * mSizePerSpan
+                                + mSecondaryOrientation.getStartAfterPadding();
                 otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view);
             }
 
@@ -1873,8 +1869,8 @@
     private void recycleFromStart(RecyclerView.Recycler recycler, int line) {
         while (getChildCount() > 0) {
             View child = getChildAt(0);
-            if (mPrimaryOrientation.getDecoratedEnd(child) <= line &&
-                    mPrimaryOrientation.getTransformedEndWithDecoration(child) <= line) {
+            if (mPrimaryOrientation.getDecoratedEnd(child) <= line
+                    && mPrimaryOrientation.getTransformedEndWithDecoration(child) <= line) {
                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
                 // Don't recycle the last View in a span not to lose span's start/end lines
                 if (lp.mFullSpan) {
@@ -1894,7 +1890,7 @@
                 }
                 removeAndRecycleView(child, recycler);
             } else {
-                return;// done
+                return; // done
             }
         }
     }
@@ -1904,8 +1900,8 @@
         int i;
         for (i = childCount - 1; i >= 0; i--) {
             View child = getChildAt(i);
-            if (mPrimaryOrientation.getDecoratedStart(child) >= line &&
-                    mPrimaryOrientation.getTransformedStartWithDecoration(child) >= line) {
+            if (mPrimaryOrientation.getDecoratedStart(child) >= line
+                    && mPrimaryOrientation.getTransformedStartWithDecoration(child) >= line) {
                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
                 // Don't recycle the last View in a span not to lose span's start/end lines
                 if (lp.mFullSpan) {
@@ -1925,7 +1921,7 @@
                 }
                 removeAndRecycleView(child, recycler);
             } else {
-                return;// done
+                return; // done
             }
         }
     }
@@ -2114,7 +2110,8 @@
 
         // then assign them in order to the next N views (where N = span count)
         for (int i = 0; i < itemPrefetchCount && mLayoutState.hasMore(state); i++) {
-            layoutPrefetchRegistry.addPosition(mLayoutState.mCurrentPosition, mPrefetchDistances[i]);
+            layoutPrefetchRegistry.addPosition(mLayoutState.mCurrentPosition,
+                    mPrefetchDistances[i]);
             mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
         }
     }
@@ -2288,14 +2285,14 @@
         // either could not find from the desired span or prev view is full span.
         // traverse all spans
         if (preferLastSpan(layoutDir)) {
-            for (int i = mSpanCount - 1; i >= 0; i --) {
+            for (int i = mSpanCount - 1; i >= 0; i--) {
                 View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
                 if (view != null && view != directChild) {
                     return view;
                 }
             }
         } else {
-            for (int i = 0; i < mSpanCount; i ++) {
+            for (int i = 0; i < mSpanCount; i++) {
                 View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
                 if (view != null && view != directChild) {
                     return view;
@@ -2586,8 +2583,8 @@
             if (reference == INVALID_LINE) {
                 return;
             }
-            if ((reverseLayout && reference < mPrimaryOrientation.getEndAfterPadding()) ||
-                    (!reverseLayout && reference > mPrimaryOrientation.getStartAfterPadding())) {
+            if ((reverseLayout && reference < mPrimaryOrientation.getEndAfterPadding())
+                    || (!reverseLayout && reference > mPrimaryOrientation.getStartAfterPadding())) {
                 return;
             }
             if (offset != INVALID_OFFSET) {
@@ -3023,8 +3020,8 @@
                     return null;
                 }
                 if (fsi.mPosition >= minPos
-                        && (gapDir == 0 || fsi.mGapDir == gapDir ||
-                        (hasUnwantedGapAfter && fsi.mHasUnwantedGapAfter))) {
+                        && (gapDir == 0 || fsi.mGapDir == gapDir
+                        || (hasUnwantedGapAfter && fsi.mHasUnwantedGapAfter))) {
                     return fsi;
                 }
             }
@@ -3044,7 +3041,7 @@
             // view is still on the screen after scroll stops, we have to recalculate layout
             boolean mHasUnwantedGapAfter;
 
-            public FullSpanItem(Parcel in) {
+            FullSpanItem(Parcel in) {
                 mPosition = in.readInt();
                 mGapDir = in.readInt();
                 mHasUnwantedGapAfter = in.readInt() == 1;
@@ -3055,7 +3052,7 @@
                 }
             }
 
-            public FullSpanItem() {
+            FullSpanItem() {
             }
 
             int getGapForSpan(int spanIndex) {
@@ -3082,26 +3079,26 @@
 
             @Override
             public String toString() {
-                return "FullSpanItem{" +
-                        "mPosition=" + mPosition +
-                        ", mGapDir=" + mGapDir +
-                        ", mHasUnwantedGapAfter=" + mHasUnwantedGapAfter +
-                        ", mGapPerSpan=" + Arrays.toString(mGapPerSpan) +
-                        '}';
+                return "FullSpanItem{"
+                        + "mPosition=" + mPosition
+                        + ", mGapDir=" + mGapDir
+                        + ", mHasUnwantedGapAfter=" + mHasUnwantedGapAfter
+                        + ", mGapPerSpan=" + Arrays.toString(mGapPerSpan)
+                        + '}';
             }
 
-            public static final Parcelable.Creator<FullSpanItem> CREATOR
-                    = new Parcelable.Creator<FullSpanItem>() {
-                @Override
-                public FullSpanItem createFromParcel(Parcel in) {
-                    return new FullSpanItem(in);
-                }
+            public static final Parcelable.Creator<FullSpanItem> CREATOR =
+                    new Parcelable.Creator<FullSpanItem>() {
+                        @Override
+                        public FullSpanItem createFromParcel(Parcel in) {
+                            return new FullSpanItem(in);
+                        }
 
-                @Override
-                public FullSpanItem[] newArray(int size) {
-                    return new FullSpanItem[size];
-                }
-            };
+                        @Override
+                        public FullSpanItem[] newArray(int size) {
+                            return new FullSpanItem[size];
+                        }
+                    };
         }
     }
 
@@ -3198,18 +3195,18 @@
             dest.writeList(mFullSpanItems);
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
-            @Override
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
 
-            @Override
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
     }
 
     /**
@@ -3226,7 +3223,7 @@
         // measure steps
         int[] mSpanReferenceLines;
 
-        public AnchorInfo() {
+        AnchorInfo() {
             reset();
         }
 
diff --git a/v7/recyclerview/src/android/support/v7/widget/ViewBoundsCheck.java b/v7/recyclerview/src/android/support/v7/widget/ViewBoundsCheck.java
index 8a4f89c..191a069 100644
--- a/v7/recyclerview/src/android/support/v7/widget/ViewBoundsCheck.java
+++ b/v7/recyclerview/src/android/support/v7/widget/ViewBoundsCheck.java
@@ -122,7 +122,7 @@
             FLAG_CVS_GT_PVS, FLAG_CVS_EQ_PVS, FLAG_CVS_LT_PVS,
             FLAG_CVS_GT_PVE, FLAG_CVS_EQ_PVE, FLAG_CVS_LT_PVE,
             FLAG_CVE_GT_PVS, FLAG_CVE_EQ_PVS, FLAG_CVE_LT_PVS,
-            FLAG_CVE_EQ_PVE, FLAG_CVE_EQ_PVE, FLAG_CVE_LT_PVE
+            FLAG_CVE_GT_PVE, FLAG_CVE_EQ_PVE, FLAG_CVE_LT_PVE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ViewBounds {}
diff --git a/v7/recyclerview/src/android/support/v7/widget/ViewInfoStore.java b/v7/recyclerview/src/android/support/v7/widget/ViewInfoStore.java
index 90f3a6f..68bbde4 100644
--- a/v7/recyclerview/src/android/support/v7/widget/ViewInfoStore.java
+++ b/v7/recyclerview/src/android/support/v7/widget/ViewInfoStore.java
@@ -15,24 +15,22 @@
  */
 package android.support.v7.widget;
 
+import static android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
+import static android.support.v7.widget.RecyclerView.ViewHolder;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_AND_DISAPPEAR;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_PRE_AND_POST;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_POST;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE_AND_POST;
+
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.LongSparseArray;
 import android.support.v4.util.Pools;
-import android.view.View;
-
-import static android.support.v7.widget.RecyclerView.ViewHolder;
-import static android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
-
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_PRE_AND_POST;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_AND_DISAPPEAR;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE_AND_POST;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_POST;
 /**
  * This class abstracts all tracking for Views to run animations.
  */
@@ -218,7 +216,7 @@
     }
 
     void process(ProcessCallback callback) {
-        for (int index = mLayoutHolderMap.size() - 1; index >= 0; index --) {
+        for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
             final ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
             final InfoRecord record = mLayoutHolderMap.removeAt(index);
             if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
index c3f3a53..b0a2cb3 100644
--- a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
@@ -16,18 +16,14 @@
 
 package android.support.v7.widget.helper;
 
+import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Build;
 import android.support.annotation.Nullable;
-import android.support.v4.animation.AnimatorCompatHelper;
-import android.support.v4.animation.AnimatorListenerCompat;
-import android.support.v4.animation.AnimatorUpdateListenerCompat;
-import android.support.v4.animation.ValueAnimatorCompat;
 import android.support.v4.view.GestureDetectorCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.recyclerview.R;
 import android.support.v7.widget.LinearLayoutManager;
@@ -62,16 +58,13 @@
  * {@link ItemTouchHelper.Callback} class or implementing {@link ItemTouchHelper.ViewDropHandler}
  * interface in your LayoutManager.
  * <p>
- * By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. On
- * platforms older than Honeycomb, ItemTouchHelper uses canvas translations and View's visibility
- * property to move items in response to touch events. You can customize these behaviors by
- * overriding {@link Callback#onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,
- * boolean)}
+ * By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. You can
+ * customize these behaviors by overriding {@link Callback#onChildDraw(Canvas, RecyclerView,
+ * ViewHolder, float, float, int, boolean)}
  * or {@link Callback#onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
  * boolean)}.
  * <p/>
- * Most of the time, you only need to override <code>onChildDraw</code> but due to limitations of
- * platform prior to Honeycomb, you may need to implement <code>onChildDrawOver</code> as well.
+ * Most of the time you only need to override <code>onChildDraw</code>.
  */
 public class ItemTouchHelper extends RecyclerView.ItemDecoration
         implements RecyclerView.OnChildAttachStateChangeListener {
@@ -299,15 +292,14 @@
      */
     GestureDetectorCompat mGestureDetector;
 
-    private final OnItemTouchListener mOnItemTouchListener
-            = new OnItemTouchListener() {
+    private final OnItemTouchListener mOnItemTouchListener = new OnItemTouchListener() {
         @Override
         public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
             mGestureDetector.onTouchEvent(event);
             if (DEBUG) {
                 Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);
             }
-            final int action = MotionEventCompat.getActionMasked(event);
+            final int action = event.getActionMasked();
             if (action == MotionEvent.ACTION_DOWN) {
                 mActivePointerId = event.getPointerId(0);
                 mInitialTouchX = event.getX();
@@ -359,7 +351,7 @@
             if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
                 return;
             }
-            final int action = MotionEventCompat.getActionMasked(event);
+            final int action = event.getActionMasked();
             final int activePointerIndex = event.findPointerIndex(mActivePointerId);
             if (activePointerIndex >= 0) {
                 checkSelectForSwipe(action, event, activePointerIndex);
@@ -390,7 +382,7 @@
                     mActivePointerId = ACTIVE_POINTER_ID_NONE;
                     break;
                 case MotionEvent.ACTION_POINTER_UP: {
-                    final int pointerIndex = MotionEventCompat.getActionIndex(event);
+                    final int pointerIndex = event.getActionIndex();
                     final int pointerId = event.getPointerId(pointerIndex);
                     if (pointerId == mActivePointerId) {
                         // This was our active pointer going up. Choose a new
@@ -437,10 +429,10 @@
     }
 
     private static boolean hitTest(View child, float x, float y, float left, float top) {
-        return x >= left &&
-                x <= left + child.getWidth() &&
-                y >= top &&
-                y <= top + child.getHeight();
+        return x >= left
+                && x <= left + child.getWidth()
+                && y >= top
+                && y <= top + child.getHeight();
     }
 
     /**
@@ -507,12 +499,12 @@
         if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {
             outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();
         } else {
-            outPosition[0] = ViewCompat.getTranslationX(mSelected.itemView);
+            outPosition[0] = mSelected.itemView.getTranslationX();
         }
         if ((mSelectedFlags & (UP | DOWN)) != 0) {
             outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();
         } else {
-            outPosition[1] = ViewCompat.getTranslationY(mSelected.itemView);
+            outPosition[1] = mSelected.itemView.getTranslationY();
         }
     }
 
@@ -609,7 +601,7 @@
                         prevActionState, currentTranslateX, currentTranslateY,
                         targetTranslateX, targetTranslateY) {
                     @Override
-                    public void onAnimationEnd(ValueAnimatorCompat animation) {
+                    public void onAnimationEnd(Animator animation) {
                         super.onAnimationEnd(animation);
                         if (this.mOverridden) {
                             return;
@@ -674,9 +666,9 @@
         mRecyclerView.post(new Runnable() {
             @Override
             public void run() {
-                if (mRecyclerView != null && mRecyclerView.isAttachedToWindow() &&
-                        !anim.mOverridden &&
-                        anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {
+                if (mRecyclerView != null && mRecyclerView.isAttachedToWindow()
+                        && !anim.mOverridden
+                        && anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {
                     final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();
                     // if animator is running or we have other active recover animations, we try
                     // not to call onSwiped because DefaultItemAnimator is not good at merging
@@ -740,8 +732,8 @@
             if (mDy < 0 && topDiff < 0) {
                 scrollY = topDiff;
             } else if (mDy > 0) {
-                final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom -
-                        (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
+                final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom
+                        - (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
                 if (bottomDiff > 0) {
                     scrollY = bottomDiff;
                 }
@@ -788,7 +780,7 @@
         for (int i = 0; i < childCount; i++) {
             View other = lm.getChildAt(i);
             if (other == viewHolder.itemView) {
-                continue;//myself!
+                continue; //myself!
             }
             if (other.getBottom() < top || other.getTop() > bottom
                     || other.getRight() < left || other.getLeft() > right) {
@@ -1044,7 +1036,7 @@
      * <pre>
      *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
      *         public boolean onTouch(View v, MotionEvent event) {
-     *             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+     *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
      *                 mItemTouchHelper.startDrag(viewHolder);
      *             }
      *             return false;
@@ -1093,7 +1085,7 @@
      * <pre>
      *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
      *         public boolean onTouch(View v, MotionEvent event) {
-     *             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+     *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
      *                 mItemTouchHelper.startSwipe(viewHolder);
      *             }
      *             return false;
@@ -1206,15 +1198,13 @@
             if (mVelocityTracker != null && mActivePointerId > -1) {
                 mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
                         mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
-                final float xVelocity = VelocityTrackerCompat
-                        .getXVelocity(mVelocityTracker, mActivePointerId);
-                final float yVelocity = VelocityTrackerCompat
-                        .getYVelocity(mVelocityTracker, mActivePointerId);
+                final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);
+                final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);
                 final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT;
                 final float absXVelocity = Math.abs(xVelocity);
-                if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag &&
-                        absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&
-                        absXVelocity > Math.abs(yVelocity)) {
+                if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag
+                        && absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)
+                        && absXVelocity > Math.abs(yVelocity)) {
                     return velDirFlag;
                 }
             }
@@ -1235,15 +1225,13 @@
             if (mVelocityTracker != null && mActivePointerId > -1) {
                 mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
                         mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
-                final float xVelocity = VelocityTrackerCompat
-                        .getXVelocity(mVelocityTracker, mActivePointerId);
-                final float yVelocity = VelocityTrackerCompat
-                        .getYVelocity(mVelocityTracker, mActivePointerId);
+                final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);
+                final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);
                 final int velDirFlag = yVelocity > 0f ? DOWN : UP;
                 final float absYVelocity = Math.abs(yVelocity);
-                if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag &&
-                        absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&
-                        absYVelocity > Math.abs(xVelocity)) {
+                if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag
+                        && absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)
+                        && absYVelocity > Math.abs(xVelocity)) {
                     return velDirFlag;
                 }
             }
@@ -1259,7 +1247,7 @@
 
     private void addChildDrawingOrderCallback() {
         if (Build.VERSION.SDK_INT >= 21) {
-            return;// we use elevation on Lollipop
+            return; // we use elevation on Lollipop
         }
         if (mChildDrawingOrderCallback == null) {
             mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
@@ -1297,7 +1285,7 @@
      * An interface which can be implemented by LayoutManager for better integration with
      * {@link ItemTouchHelper}.
      */
-    public static interface ViewDropHandler {
+    public interface ViewDropHandler {
 
         /**
          * Called by the {@link ItemTouchHelper} after a View is dropped over another View.
@@ -1318,7 +1306,7 @@
          * @param y      The <code>top</code> offset of the View that is being dragged. This value
          *               includes the movement caused by the user.
          */
-        public void prepareForDrop(View view, View target, int x, int y);
+        void prepareForDrop(View view, View target, int x, int y);
     }
 
     /**
@@ -1358,15 +1346,15 @@
 
         public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250;
 
-        static final int RELATIVE_DIR_FLAGS = START | END |
-                ((START | END) << DIRECTION_FLAG_COUNT) |
-                ((START | END) << (2 * DIRECTION_FLAG_COUNT));
+        static final int RELATIVE_DIR_FLAGS = START | END
+                | ((START | END) << DIRECTION_FLAG_COUNT)
+                | ((START | END) << (2 * DIRECTION_FLAG_COUNT));
 
         private static final ItemTouchUIUtil sUICallback;
 
-        private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT |
-                ((LEFT | RIGHT) << DIRECTION_FLAG_COUNT) |
-                ((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT));
+        private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT
+                | ((LEFT | RIGHT) << DIRECTION_FLAG_COUNT)
+                | ((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT));
 
         private static final Interpolator sDragScrollInterpolator = new Interpolator() {
             @Override
@@ -1392,11 +1380,9 @@
 
         static {
             if (Build.VERSION.SDK_INT >= 21) {
-                sUICallback = new ItemTouchUIUtilImpl.Lollipop();
-            } else if (Build.VERSION.SDK_INT >= 11) {
-                sUICallback = new ItemTouchUIUtilImpl.Honeycomb();
+                sUICallback = new ItemTouchUIUtilImpl.Api21Impl();
             } else {
-                sUICallback = new ItemTouchUIUtilImpl.Gingerbread();
+                sUICallback = new ItemTouchUIUtilImpl.BaseImpl();
             }
         }
 
@@ -1458,7 +1444,7 @@
         public static int convertToRelativeDirection(int flags, int layoutDirection) {
             int masked = flags & ABS_HORIZONTAL_DIR_FLAGS;
             if (masked == 0) {
-                return flags;// does not have any abs flags, good.
+                return flags; // does not have any abs flags, good.
             }
             flags &= ~masked; //remove left / right.
             if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
@@ -1486,9 +1472,9 @@
          * @return Returns an integer composed of the given drag and swipe flags.
          */
         public static int makeMovementFlags(int dragFlags, int swipeFlags) {
-            return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags) |
-                    makeFlag(ACTION_STATE_SWIPE, swipeFlags) | makeFlag(ACTION_STATE_DRAG,
-                    dragFlags);
+            return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
+                    | makeFlag(ACTION_STATE_SWIPE, swipeFlags)
+                    | makeFlag(ACTION_STATE_DRAG, dragFlags);
         }
 
         /**
@@ -1547,7 +1533,7 @@
         public int convertToAbsoluteDirection(int flags, int layoutDirection) {
             int masked = flags & RELATIVE_DIR_FLAGS;
             if (masked == 0) {
-                return flags;// does not have any relative flags, good.
+                return flags; // does not have any relative flags, good.
             }
             flags &= ~masked; //remove start / end
             if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
@@ -2134,8 +2120,8 @@
             final int direction = (int) Math.signum(viewSizeOutOfBounds);
             // might be negative if other direction
             float outOfBoundsRatio = Math.min(1f, 1f * absOutOfBounds / viewSize);
-            final int cappedScroll = (int) (direction * maxScroll *
-                    sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio));
+            final int cappedScroll = (int) (direction * maxScroll
+                    * sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio));
             final float timeRatio;
             if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) {
                 timeRatio = 1f;
@@ -2297,7 +2283,7 @@
         }
     }
 
-    private class RecoverAnimation implements AnimatorListenerCompat {
+    private static class RecoverAnimation implements Animator.AnimatorListener {
 
         final float mStartDx;
 
@@ -2311,7 +2297,7 @@
 
         final int mActionState;
 
-        private final ValueAnimatorCompat mValueAnimator;
+        private final ValueAnimator mValueAnimator;
 
         final int mAnimationType;
 
@@ -2329,7 +2315,7 @@
 
         private float mFraction;
 
-        public RecoverAnimation(ViewHolder viewHolder, int animationType,
+        RecoverAnimation(ViewHolder viewHolder, int animationType,
                 int actionState, float startDx, float startDy, float targetX, float targetY) {
             mActionState = actionState;
             mAnimationType = animationType;
@@ -2338,11 +2324,11 @@
             mStartDy = startDy;
             mTargetX = targetX;
             mTargetY = targetY;
-            mValueAnimator = AnimatorCompatHelper.emptyValueAnimator();
+            mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
             mValueAnimator.addUpdateListener(
-                    new AnimatorUpdateListenerCompat() {
+                    new ValueAnimator.AnimatorUpdateListener() {
                         @Override
-                        public void onAnimationUpdate(ValueAnimatorCompat animation) {
+                        public void onAnimationUpdate(ValueAnimator animation) {
                             setFraction(animation.getAnimatedFraction());
                         }
                     });
@@ -2374,24 +2360,24 @@
          */
         public void update() {
             if (mStartDx == mTargetX) {
-                mX = ViewCompat.getTranslationX(mViewHolder.itemView);
+                mX = mViewHolder.itemView.getTranslationX();
             } else {
                 mX = mStartDx + mFraction * (mTargetX - mStartDx);
             }
             if (mStartDy == mTargetY) {
-                mY = ViewCompat.getTranslationY(mViewHolder.itemView);
+                mY = mViewHolder.itemView.getTranslationY();
             } else {
                 mY = mStartDy + mFraction * (mTargetY - mStartDy);
             }
         }
 
         @Override
-        public void onAnimationStart(ValueAnimatorCompat animation) {
+        public void onAnimationStart(Animator animation) {
 
         }
 
         @Override
-        public void onAnimationEnd(ValueAnimatorCompat animation) {
+        public void onAnimationEnd(Animator animation) {
             if (!mEnded) {
                 mViewHolder.setIsRecyclable(true);
             }
@@ -2399,13 +2385,13 @@
         }
 
         @Override
-        public void onAnimationCancel(ValueAnimatorCompat animation) {
+        public void onAnimationCancel(Animator animation) {
             setFraction(1f); //make sure we recover the view's state.
         }
 
         @Override
-        public void onAnimationRepeat(ValueAnimatorCompat animation) {
+        public void onAnimationRepeat(Animator animation) {
 
         }
     }
-}
\ No newline at end of file
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java
index ea25477..559b37e 100644
--- a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java
+++ b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java
@@ -18,17 +18,16 @@
 
 import android.graphics.Canvas;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.recyclerview.R;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
-import android.support.v7.recyclerview.R;
-
 
 /**
  * Package private class to keep implementations. Putting them inside ItemTouchUIUtil makes them
  * public API, which is not desired in this case.
  */
 class ItemTouchUIUtilImpl {
-    static class Lollipop extends Honeycomb {
+    static class Api21Impl extends BaseImpl {
         @Override
         public void onDraw(Canvas c, RecyclerView recyclerView, View view,
                 float dX, float dY, int actionState, boolean isCurrentlyActive) {
@@ -71,12 +70,12 @@
         }
     }
 
-    static class Honeycomb implements ItemTouchUIUtil {
+    static class BaseImpl implements ItemTouchUIUtil {
 
         @Override
         public void clearView(View view) {
-            ViewCompat.setTranslationX(view, 0f);
-            ViewCompat.setTranslationY(view, 0f);
+            view.setTranslationX(0f);
+            view.setTranslationY(0f);
         }
 
         @Override
@@ -87,8 +86,8 @@
         @Override
         public void onDraw(Canvas c, RecyclerView recyclerView, View view,
                 float dX, float dY, int actionState, boolean isCurrentlyActive) {
-            ViewCompat.setTranslationX(view, dX);
-            ViewCompat.setTranslationY(view, dY);
+            view.setTranslationX(dX);
+            view.setTranslationY(dY);
         }
 
         @Override
@@ -97,42 +96,4 @@
 
         }
     }
-
-    static class Gingerbread implements ItemTouchUIUtil {
-
-        private void draw(Canvas c, RecyclerView parent, View view,
-                float dX, float dY) {
-            c.save();
-            c.translate(dX, dY);
-            parent.drawChild(c, view, 0);
-            c.restore();
-        }
-
-        @Override
-        public void clearView(View view) {
-            view.setVisibility(View.VISIBLE);
-        }
-
-        @Override
-        public void onSelected(View view) {
-            view.setVisibility(View.INVISIBLE);
-        }
-
-        @Override
-        public void onDraw(Canvas c, RecyclerView recyclerView, View view,
-                float dX, float dY, int actionState, boolean isCurrentlyActive) {
-            if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) {
-                draw(c, recyclerView, view, dX, dY);
-            }
-        }
-
-        @Override
-        public void onDrawOver(Canvas c, RecyclerView recyclerView,
-                View view, float dX, float dY,
-                int actionState, boolean isCurrentlyActive) {
-            if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
-                draw(c, recyclerView, view, dX, dY);
-            }
-        }
-    }
 }
diff --git a/v7/recyclerview/tests/Android.mk b/v7/recyclerview/tests/Android.mk
index 9c523b7..c6299d7 100644
--- a/v7/recyclerview/tests/Android.mk
+++ b/v7/recyclerview/tests/Android.mk
@@ -32,6 +32,7 @@
         android-support-annotations
 
 LOCAL_PACKAGE_NAME := RecyclerViewTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_AAPT_FLAGS := \
         --auto-add-overlay \
         --extra-packages android.support.v7.recyclerview
diff --git a/v7/recyclerview/tests/AndroidManifest.xml b/v7/recyclerview/tests/AndroidManifest.xml
index 1c8684d..3d10333 100644
--- a/v7/recyclerview/tests/AndroidManifest.xml
+++ b/v7/recyclerview/tests/AndroidManifest.xml
@@ -15,22 +15,13 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v7.recyclerview.test">
-    <uses-sdk android:minSdkVersion="9"
-              android:targetSdkVersion="23"
-              tools:overrideLibrary="android.support.test,
-                      android.app, android.support.test.rule, android.support.test.espresso,
-                      android.support.test.espresso.idling"/>
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application android:supportsRtl="true">
-        <uses-library android:name="android.test.runner"/>
         <activity android:name="android.support.v7.widget.test.RecyclerViewTestActivity"
                   android:theme="@style/noAnimTheme"/>
-        <activity android:name="android.support.v7.widget.TestActivity" android:theme="@style/noAnimTheme"/>
+        <activity android:name="android.support.v7.widget.TestActivity"
+                  android:theme="@style/noAnimTheme"/>
     </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.support.v7.recyclerview.test"
-    />
 </manifest>
diff --git a/v7/recyclerview/tests/res/drawable/fast_scroll_thumb_drawable.xml b/v7/recyclerview/tests/res/drawable/fast_scroll_thumb_drawable.xml
new file mode 100644
index 0000000..47af3de
--- /dev/null
+++ b/v7/recyclerview/tests/res/drawable/fast_scroll_thumb_drawable.xml
@@ -0,0 +1,30 @@
+<?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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape  android:shape="rectangle">
+            <solid android:color="#FF0000" />
+            <size android:width="8dp" android:height="48dp" />
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="#0000FF" />
+            <size android:width="8dp" android:height="48dp" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/v7/recyclerview/tests/res/drawable/fast_scroll_track_drawable.xml b/v7/recyclerview/tests/res/drawable/fast_scroll_track_drawable.xml
new file mode 100644
index 0000000..04a4ac5
--- /dev/null
+++ b/v7/recyclerview/tests/res/drawable/fast_scroll_track_drawable.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+  android:shape="rectangle">
+    <solid android:color="#00FF00" />
+    <size android:width="8dp" />
+</shape>
\ No newline at end of file
diff --git a/v7/recyclerview/tests/res/layout/fast_scrollbar_test_rv.xml b/v7/recyclerview/tests/res/layout/fast_scrollbar_test_rv.xml
new file mode 100644
index 0000000..80c021e
--- /dev/null
+++ b/v7/recyclerview/tests/res/layout/fast_scrollbar_test_rv.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<android.support.v7.widget.RecyclerView
+        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="match_parent"
+        android:scrollbars="none"
+        android:id="@+id/recycler_view"
+        app:fastScrollEnabled="true"
+        app:fastScrollVerticalThumbDrawable="@drawable/fast_scroll_thumb_drawable"
+        app:fastScrollVerticalTrackDrawable="@drawable/fast_scroll_track_drawable"
+        app:fastScrollHorizontalThumbDrawable="@drawable/fast_scroll_thumb_drawable"
+        app:fastScrollHorizontalTrackDrawable="@drawable/fast_scroll_track_drawable"/>
\ No newline at end of file
diff --git a/v7/recyclerview/tests/res/values/styles.xml b/v7/recyclerview/tests/res/values/styles.xml
index f7aad81..d2b5a82 100644
--- a/v7/recyclerview/tests/res/values/styles.xml
+++ b/v7/recyclerview/tests/res/values/styles.xml
@@ -1,6 +1,25 @@
 <?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="noAnimTheme" parent="android:Theme">
         <item name="android:windowAnimationStyle">@null</item>
     </style>
+    <style name="nullListDivider" parent="android:Theme">
+        <item name="android:listDivider">@null</item>
+    </style>
 </resources>
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
index 8766f8c..f8d27ed 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
@@ -210,6 +210,7 @@
             return mDataItemCount;
         }
 
+        @Override
         public int getMaxCachedTiles() {
             return mCacheSize;
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/ImeCleanUpTestRule.java b/v7/recyclerview/tests/src/android/support/v7/util/ImeCleanUpTestRule.java
new file mode 100644
index 0000000..f4caad3
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/util/ImeCleanUpTestRule.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.util;
+
+import android.graphics.Rect;
+import android.support.testutils.PollingCheck;
+import android.view.View;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit rule that ensures that IME is closed after a test is finished (or exception thrown).
+ * A test that triggers IME open/close should call setContainerView with the activity's container
+ * view in order to trigger this cleanup at the end of the test. Otherwise, no cleanup happens.
+ */
+public class ImeCleanUpTestRule implements TestRule {
+
+    private View mContainerView;
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    base.evaluate();
+                } finally {
+                    closeImeIfOpen();
+                }
+            }
+        };
+    }
+
+    /**
+     * Sets the container view used to calculate the total screen height and the height available
+     * to the test activity.
+     */
+    public void setContainerView(View containerView) {
+        mContainerView = containerView;
+    }
+
+    private void closeImeIfOpen() {
+        if (mContainerView == null) {
+            return;
+        }
+        // Ensuring that IME is closed after starting each test.
+        final Rect r = new Rect();
+        mContainerView.getWindowVisibleDisplayFrame(r);
+        // This is the entire height of the screen available to both the view and IME
+        final int screenHeight = mContainerView.getRootView().getHeight();
+
+        // r.bottom is the position above IME if it's open or device button.
+        // if IME is shown, r.bottom is smaller than screenHeight.
+        int imeHeight = screenHeight - r.bottom;
+
+        // Picking a threshold to detect when IME is open
+        if (imeHeight > screenHeight * 0.15) {
+            // Soft keyboard is shown, will wait for it to close after running the test. Note that
+            // we don't press back button here as the IME should close by itself when a test
+            // finishes. If the wait isn't done here, the IME can mess up with the layout of the
+            // next test.
+            PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+                @Override
+                public boolean canProceed() {
+                    mContainerView.getWindowVisibleDisplayFrame(r);
+                    int imeHeight = screenHeight - r.bottom;
+                    return imeHeight < screenHeight * 0.15;
+                }
+            });
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/PollingCheck.java b/v7/recyclerview/tests/src/android/support/v7/util/PollingCheck.java
deleted file mode 100644
index 796a4ae..0000000
--- a/v7/recyclerview/tests/src/android/support/v7/util/PollingCheck.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v7.util;
-
-import junit.framework.Assert;
-
-import java.util.concurrent.Callable;
-
-public abstract class PollingCheck {
-    private static final long TIME_SLICE = 50;
-    private long mTimeout = 3000;
-
-    public interface PollingCheckCondition {
-        boolean canProceed();
-    }
-
-    public PollingCheck() {
-    }
-
-    public PollingCheck(long timeout) {
-        mTimeout = timeout;
-    }
-
-    protected abstract boolean check();
-
-    public void run() {
-        if (check()) {
-            return;
-        }
-
-        long timeout = mTimeout;
-        while (timeout > 0) {
-            try {
-                Thread.sleep(TIME_SLICE);
-            } catch (InterruptedException e) {
-                Assert.fail("unexpected InterruptedException");
-            }
-
-            if (check()) {
-                return;
-            }
-
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail("unexpected timeout");
-    }
-
-    public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
-            throws Exception {
-        while (timeout > 0) {
-            if (condition.call()) {
-                return;
-            }
-
-            Thread.sleep(TIME_SLICE);
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail(message.toString());
-    }
-
-    public static void waitFor(final PollingCheckCondition condition) {
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-
-    public static void waitFor(long timeout, final PollingCheckCondition condition) {
-        new PollingCheck(timeout) {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-}
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/TouchUtils.java b/v7/recyclerview/tests/src/android/support/v7/util/TouchUtils.java
index d033004..02099ba 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/TouchUtils.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/TouchUtils.java
@@ -19,7 +19,6 @@
 import android.app.Instrumentation;
 import android.os.SystemClock;
 import android.support.v7.widget.RecyclerView;
-import android.test.InstrumentationTestCase;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
index cc6bbe8..13dd1e4 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
@@ -325,6 +325,36 @@
         }
     }
 
+    class GridEditTextAdapter extends EditTextAdapter {
+
+        Set<Integer> mFullSpanItems = new HashSet<Integer>();
+        int mSpanPerItem = 1;
+
+        GridEditTextAdapter(int count) {
+            this(count, 1);
+        }
+
+        GridEditTextAdapter(int count, int spanPerItem) {
+            super(count);
+            mSpanPerItem = spanPerItem;
+        }
+
+        void setFullSpan(int... items) {
+            for (int i : items) {
+                mFullSpanItems.add(i);
+            }
+        }
+
+        void assignSpanSizeLookup(final GridLayoutManager glm) {
+            glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+                @Override
+                public int getSpanSize(int position) {
+                    return mFullSpanItems.contains(position) ? glm.getSpanCount() : mSpanPerItem;
+                }
+            });
+        }
+    }
+
     class GridTestAdapter extends TestAdapter {
 
         Set<Integer> mFullSpanItems = new HashSet<Integer>();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index 8ca5f4e..2139497 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -38,10 +38,12 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v7.recyclerview.test.R;
 import android.support.v7.recyclerview.test.SameActivityTestRule;
+import android.text.Editable;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -793,6 +795,37 @@
         }
     }
 
+    public class EditTextAdapter extends RecyclerView.Adapter<TestViewHolder> {
+
+        final ArrayList<Editable> mEditables;
+        public EditTextAdapter(int count) {
+            mEditables = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                mEditables.add(Editable.Factory.getInstance().newEditable("Sample Text " + i));
+            }
+        }
+
+        @Override
+        public TestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            final EditText editText = new EditText(parent.getContext());
+            editText.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+            final TestViewHolder viewHolder = new TestViewHolder(editText);
+            return viewHolder;
+        }
+
+        @Override
+        public void onBindViewHolder(TestViewHolder holder, int position) {
+            ((EditText) holder.itemView).setText(Editable.Factory.getInstance().newEditable(
+                    mEditables.get(position)));
+        }
+
+        @Override
+        public int getItemCount() {
+            return mEditables.size();
+        }
+    }
+
     public class TestAdapter extends RecyclerView.Adapter<TestViewHolder>
             implements AttachDetachCountingAdapter {
 
@@ -812,7 +845,7 @@
             mLayoutParams = layoutParams;
         }
 
-        private void addItems(int pos, int count, String prefix) {
+        void addItems(int pos, int count, String prefix) {
             for (int i = 0; i < count; i++, pos++) {
                 mItems.add(pos, new Item(pos, prefix));
             }
@@ -1197,6 +1230,27 @@
         }
     }
 
+
+    public static View findFirstFullyVisibleChild(RecyclerView parent) {
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            View child = parent.getChildAt(i);
+            if (isViewFullyInBound(parent, child)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    public static View findLastFullyVisibleChild(RecyclerView parent) {
+        for (int i = parent.getChildCount() - 1; i >= 0; i--) {
+            View child = parent.getChildAt(i);
+            if (isViewFullyInBound(parent, child)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
     /**
      * Returns whether a child of RecyclerView is partially in bound. A child is
      * partially in-bounds if it's either fully or partially visible on the screen.
@@ -1232,7 +1286,7 @@
      * @param child The child view to be checked whether is fully within RV's bounds.
      * @return True if the child view is fully visible; false otherwise.
      */
-    public boolean isViewFullyInBound(RecyclerView parent, View child) {
+    public static boolean isViewFullyInBound(RecyclerView parent, View child) {
         if (child == null) {
             return false;
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
index 52777d3..c28623f 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 import android.view.View;
@@ -40,7 +40,7 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class DefaultItemAnimatorTest extends BaseRecyclerViewInstrumentationTest {
 
@@ -111,6 +111,7 @@
         mDummyParent = getActivity().getContainer();
     }
 
+    @Override
     void checkForMainThreadException() throws Throwable {
         if (mainThreadException != null) {
             throw mainThreadException;
@@ -385,6 +386,7 @@
         return vh;
     }
 
+    @Override
     void postExceptionToInstrumentation(Throwable t) {
         if (mainThreadException == null) {
             mainThreadException = t;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java
index 08da3fb..c756ca0 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java
@@ -152,6 +152,7 @@
         runTest();
     }
 
+    @Override
     @Test
     public void runTest() {
         mRecyclerView.defaultOnMeasure(mWSpec, mHSpec);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DividerItemDecorationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/DividerItemDecorationTest.java
new file mode 100644
index 0000000..6459522
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DividerItemDecorationTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.recyclerview.test.R;
+import android.view.ContextThemeWrapper;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link DividerItemDecoration}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DividerItemDecorationTest {
+    private static final String[] STRINGS = {"Foo", "Bar", "Great"};
+
+    @Test
+    public void testNullListDivider() {
+        final Context context = InstrumentationRegistry.getContext();
+        RecyclerView rv = new RecyclerView(context);
+        rv.setLayoutManager(new LinearLayoutManager(context));
+        rv.setAdapter(new MyAdapter(STRINGS));
+        DividerItemDecoration decoration = new DividerItemDecoration(
+                new ContextThemeWrapper(context, R.style.nullListDivider),
+                DividerItemDecoration.HORIZONTAL);
+        rv.addItemDecoration(decoration);
+        rv.layout(0, 0, 1000, 1000);
+        decoration.onDraw(new Canvas(), rv, null);
+    }
+
+    private static class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
+        private String[] mDataset;
+
+        public static class ViewHolder extends RecyclerView.ViewHolder {
+            TextView mTextView;
+            ViewHolder(TextView v) {
+                super(v);
+                mTextView = v;
+            }
+        }
+
+        MyAdapter(String[] myDataset) {
+            mDataset = myDataset;
+        }
+
+        @Override
+        public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new ViewHolder(new TextView(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder holder, int position) {
+            holder.mTextView.setText(mDataset[position]);
+        }
+
+        @Override
+        public int getItemCount() {
+            return mDataset.length;
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
index 55bed7e..3755018 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
@@ -30,7 +30,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.recyclerview.test.R;
@@ -57,7 +57,7 @@
 /**
  * This class tests RecyclerView focus search failure handling by using a real LayoutManager.
  */
-@MediumTest
+@LargeTest
 @RunWith(Parameterized.class)
 public class FocusSearchNavigationTest {
     @Rule
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java
index bc107b2..b4e0ae3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java
@@ -22,8 +22,9 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.support.annotation.Nullable;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.view.View;
+import android.widget.TextView;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,6 +34,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+@LargeTest
 @RunWith(Parameterized.class)
 public class GridLayoutManagerSnappingTest extends BaseGridLayoutManagerTest {
 
@@ -56,7 +58,6 @@
         return result;
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -79,7 +80,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollNextItem() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -103,7 +103,6 @@
         assertCenterAligned(viewAfterScroll);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -130,8 +129,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-
-    @MediumTest
     @Test
     public void snapOnFlingNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -144,7 +141,7 @@
         assertCenterAligned(view);
 
         // Velocity high enough to scroll beyond the current view.
-        int velocity = (int) (0.2 * mRecyclerView.getMaxFlingVelocity());
+        int velocity = (int) (0.25 * mRecyclerView.getMaxFlingVelocity());
         int velocityDir = mReverseScroll ? -velocity : velocity;
 
         mGlm.expectIdleState(1);
@@ -154,7 +151,8 @@
 
         View viewAfterFling = findCenterView(mGlm);
 
-        assertNotSame("The view should have scrolled", view, viewAfterFling);
+        assertNotSame("The view should have scrolled!",
+                ((TextView) view).getText(),((TextView) viewAfterFling).getText());
         assertCenterAligned(viewAfterFling);
     }
 
@@ -204,12 +202,12 @@
             assertEquals("The child should align with the center of the parent",
                     mRecyclerView.getWidth() / 2,
                     mGlm.getDecoratedLeft(view) +
-                            mGlm.getDecoratedMeasuredWidth(view) / 2);
+                            mGlm.getDecoratedMeasuredWidth(view) / 2, 1);
         } else {
             assertEquals("The child should align with the center of the parent",
                     mRecyclerView.getHeight() / 2,
                     mGlm.getDecoratedTop(view) +
-                            mGlm.getDecoratedMeasuredHeight(view) / 2);
+                            mGlm.getDecoratedMeasuredHeight(view) / 2, 1);
         }
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index 4800344..23eaf52 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -18,6 +18,9 @@
 
 import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
@@ -31,18 +34,23 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.StateListDrawable;
 import android.os.Build;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v7.util.ImeCleanUpTestRule;
+import android.support.v7.util.TouchUtils;
 import android.test.UiThreadTest;
 import android.util.SparseIntArray;
 import android.util.StateSet;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
 import org.hamcrest.CoreMatchers;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -52,10 +60,13 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GridLayoutManagerTest extends BaseGridLayoutManagerTest {
 
+    @Rule
+    public final ImeCleanUpTestRule imeCleanUp = new ImeCleanUpTestRule();
+
     @Test
     public void focusSearchFailureUp() throws Throwable {
         focusSearchFailure(false);
@@ -168,6 +179,101 @@
         }
     }
 
+    @Test
+    public void editTextVisibility() throws Throwable {
+        final int spanCount = 3;
+        final int itemCount = 100;
+
+        imeCleanUp.setContainerView(getActivity().getContainer());
+        RecyclerView recyclerView = new WrappedRecyclerView(getActivity());
+        GridEditTextAdapter editTextAdapter = new GridEditTextAdapter(itemCount) {
+            @Override
+            public TestViewHolder onCreateViewHolder(ViewGroup parent,
+                    int viewType) {
+                TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
+                // Good to have colors for debugging
+                StateListDrawable stl = new StateListDrawable();
+                stl.addState(new int[]{android.R.attr.state_focused},
+                        new ColorDrawable(Color.RED));
+                stl.addState(StateSet.WILD_CARD, new ColorDrawable(Color.BLUE));
+                //noinspection deprecation using this for kitkat tests
+                testViewHolder.itemView.setBackgroundDrawable(stl);
+                return testViewHolder;
+            }
+        };
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivityRule.getActivity().getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_RESIZE);
+            }
+        });
+
+        recyclerView.setLayoutParams(
+                new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+
+        Config config = new Config(spanCount, itemCount);
+        mGlm = new WrappedGridLayoutManager(getActivity(), config.mSpanCount, config.mOrientation,
+                config.mReverseLayout);
+        editTextAdapter.assignSpanSizeLookup(mGlm);
+        recyclerView.setAdapter(editTextAdapter);
+        recyclerView.setLayoutManager(mGlm);
+        waitForFirstLayout(recyclerView);
+
+        // First focus on the last fully visible EditText located at span index #1.
+        View toFocus = findLastFullyVisibleChild(mRecyclerView);
+        int focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
+        focusIndex = (focusIndex / spanCount) * spanCount + 1;
+        toFocus = mRecyclerView.findViewHolderForAdapterPosition(focusIndex).itemView;
+        assertTrue(focusIndex >= 1 && focusIndex < itemCount);
+
+        final int heightBeforeImeOpen = mRecyclerView.getHeight();
+        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
+        getInstrumentation().waitForIdleSync();
+        // Wait for IME to pop up.
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mRecyclerView.getHeight() < heightBeforeImeOpen;
+            }
+        });
+
+        assertThat("Child at position " + focusIndex + " should be focused",
+                toFocus.hasFocus(), is(true));
+        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(mRecyclerView, toFocus));
+
+        // Close IME
+        final int heightBeforeImeClose = mRecyclerView.getHeight();
+        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        getInstrumentation().waitForIdleSync();
+        // Wait for IME to close
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mRecyclerView.getHeight() > heightBeforeImeClose;
+            }
+        });
+        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(mRecyclerView, toFocus));
+
+        // Now focus on the first fully visible EditText located at the last span index.
+        toFocus = findFirstFullyVisibleChild(mRecyclerView);
+        focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
+        focusIndex = (focusIndex / spanCount) * spanCount + (spanCount - 1);
+        toFocus = mRecyclerView.findViewHolderForAdapterPosition(focusIndex).itemView;
+        final int heightBeforeImeOpen2 = mRecyclerView.getHeight();
+        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
+        getInstrumentation().waitForIdleSync();
+        // Wait for IME to pop up
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mRecyclerView.getHeight() < heightBeforeImeOpen2;
+            }
+        });
+        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(mRecyclerView, toFocus));
+    }
 
     @Test
     public void topUnfocusableViewsVisibility() throws Throwable {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
index 0a9095c..731cc67 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
@@ -29,8 +29,7 @@
 import static org.junit.Assert.assertThat;
 
 import android.graphics.Rect;
-import android.support.test.filters.MediumTest;
-import android.support.v4.view.ViewCompat;
+import android.support.test.filters.LargeTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
@@ -47,7 +46,7 @@
  * Tests that rely on the basic configuration and does not do any additions / removals
  */
 @RunWith(Parameterized.class)
-@MediumTest
+@LargeTest
 public class LinearLayoutManagerBaseConfigSetTest extends BaseLinearLayoutManagerTest {
 
     private final Config mConfig;
@@ -246,9 +245,9 @@
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
-                    ViewCompat.setTranslationX(vh.itemView, size * 2);
+                    vh.itemView.setTranslationX(size * 2);
                 } else {
-                    ViewCompat.setTranslationY(vh.itemView, size * 2);
+                    vh.itemView.setTranslationY(size * 2);
                 }
             }
         });
@@ -280,9 +279,9 @@
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
-                    ViewCompat.setTranslationX(vh.itemView, -size * 2);
+                    vh.itemView.setTranslationX(-size * 2);
                 } else {
-                    ViewCompat.setTranslationY(vh.itemView, -size * 2);
+                    vh.itemView.setTranslationY(-size * 2);
                 }
             }
         });
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java
index f211920..ae09600 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java
@@ -22,7 +22,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.support.annotation.Nullable;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -33,6 +33,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+@LargeTest
 @RunWith(Parameterized.class)
 public class LinearLayoutManagerSnappingTest extends BaseLinearLayoutManagerTest {
 
@@ -56,7 +57,6 @@
         return result;
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameViewEdge() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -82,7 +82,6 @@
         mLayoutManager.assertNoCallbacks("There should be no callbacks after some time", 3);
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -105,7 +104,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -128,7 +126,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -154,7 +151,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
index ae70900..91d0dbf 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -18,6 +18,9 @@
 
 import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
@@ -34,15 +37,19 @@
 import android.os.Build;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.testutils.PollingCheck;
 import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.support.v7.util.ImeCleanUpTestRule;
+import android.support.v7.util.TouchUtils;
 import android.util.Log;
 import android.util.StateSet;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.LinearLayout;
 
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -62,6 +69,111 @@
 @LargeTest
 public class LinearLayoutManagerTest extends BaseLinearLayoutManagerTest {
 
+    @Rule
+    public final ImeCleanUpTestRule imeCleanUp = new ImeCleanUpTestRule();
+
+    @Test
+    public void editTextVisibility() throws Throwable {
+
+        // Simulating a scenario where an EditText is tapped (which will receive focus).
+        // The soft keyboard that's opened overlaps the focused EditText which will shrink RV's
+        // padded bounded area. LLM should still lay out the focused EditText so that it becomes
+        // visible above the soft keyboard.
+        // The condition for this test is setting RV's height to a non-exact height, so that measure
+        // is called twice (once with the larger height and another time with smaller height when
+        // the keyboard shows up). To ensure this resizing of RV, SOFT_INPUT_ADJUST_RESIZE is set.
+        imeCleanUp.setContainerView(getActivity().getContainer());
+        final LinearLayout container = new LinearLayout(getActivity());
+        container.setOrientation(LinearLayout.VERTICAL);
+        container.setLayoutParams(
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup
+                        .LayoutParams.MATCH_PARENT));
+
+        final EditTextAdapter editTextAdapter = new EditTextAdapter(50);
+
+        mRecyclerView = inflateWrappedRV();
+        ViewGroup.LayoutParams lp = mRecyclerView.getLayoutParams();
+        lp.height = WRAP_CONTENT;
+        lp.width = MATCH_PARENT;
+
+        mRecyclerView.setHasFixedSize(true);
+        mRecyclerView.setAdapter(editTextAdapter);
+        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), VERTICAL, false);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+
+        container.addView(mRecyclerView);
+
+        mLayoutManager.expectLayouts(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                getActivity().getContainer().addView(container);
+            }
+        });
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivityRule.getActivity().getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_RESIZE);
+            }
+        });
+
+        // First focus on the last fully visible EditText.
+        View toFocus = findLastFullyVisibleChild(mRecyclerView);
+        int focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
+
+        final int heightBeforeImeOpen = mRecyclerView.getHeight();
+        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
+        getInstrumentation().waitForIdleSync();
+       // Wait for IME to pop up.
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mRecyclerView.getHeight() < heightBeforeImeOpen;
+            }
+        });
+
+        assertThat("Child at position " + focusIndex + " should be focused",
+                toFocus.hasFocus(), is(true));
+        // Testing for partial visibility instead of full visibility since TextView calls
+        // requestRectangleOnScreen (inside bringPointIntoView) for the focused view with a rect
+        // containing the content area. This rect is guaranteed to be fully visible whereas a
+        // portion of TextView could be out of bounds.
+        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(mRecyclerView, toFocus));
+
+        // Close IME
+        final int heightBeforeImeClose = mRecyclerView.getHeight();
+        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        getInstrumentation().waitForIdleSync();
+        // Wait for IME to close
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mRecyclerView.getHeight() > heightBeforeImeClose;
+            }
+        });
+        assertThat("Child at position " + focusIndex + " should be focused",
+                toFocus.hasFocus(), is(true));
+        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(mRecyclerView, toFocus));
+
+        // Now focus on the first fully visible EditText.
+        toFocus = findFirstFullyVisibleChild(mRecyclerView);
+        focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
+        final int heightBeforeImeOpen2 = mRecyclerView.getHeight();
+        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
+        getInstrumentation().waitForIdleSync();
+        // Wait for IME to pop up
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mRecyclerView.getHeight() < heightBeforeImeOpen2;
+            }
+        });
+        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(mRecyclerView, toFocus));
+    }
+
     @Test
     public void topUnfocusableViewsVisibility() throws Throwable {
         // The maximum number of child views that can be visible at any time.
@@ -951,13 +1063,11 @@
                 delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
             }
         });
-        final AccessibilityRecordCompat record = AccessibilityEventCompat
-                .asRecord(event);
         assertEquals("result should have first position",
-                record.getFromIndex(),
+                event.getFromIndex(),
                 mLayoutManager.findFirstVisibleItemPosition());
         assertEquals("result should have last position",
-                record.getToIndex(),
+                event.getToIndex(),
                 mLayoutManager.findLastVisibleItemPosition());
     }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
index bda7297..7cc1a65 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
@@ -22,7 +22,7 @@
 
 import android.graphics.Rect;
 import android.os.Build;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.v4.view.ViewCompat;
 import android.view.Gravity;
@@ -36,7 +36,7 @@
 import java.util.List;
 
 @RunWith(Parameterized.class)
-@MediumTest
+@LargeTest
 public class LinearLayoutManagerWrapContentTest extends BaseWrapContentTest {
 
     Config mConfig;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java
index aaaa43f..3eba432 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java
@@ -25,7 +25,6 @@
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.view.ViewCompat;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -144,7 +143,7 @@
             rv.layout(0, 0, 200, 200);
             rv.scrollBy(0, 100);
 
-            ViewCompat.setTranslationX(rv, 100 * i);
+            rv.setTranslationX(100 * i);
         }
 
         GapWorker worker = GapWorker.sGapWorker.get();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java
index 0799d3d..abdcbe8 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java
@@ -22,7 +22,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.support.annotation.Nullable;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -35,6 +35,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+@LargeTest
 @RunWith(Parameterized.class)
 public class PagerSnapHelperTest extends BaseLinearLayoutManagerTest {
 
@@ -58,7 +59,6 @@
         return result;
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -85,7 +85,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -116,7 +115,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -154,7 +152,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -167,7 +164,6 @@
         runSnapOnMaxFlingNextView((int) (0.2 * mRecyclerView.getMaxFlingVelocity()));
     }
 
-    @MediumTest
     @Test
     public void snapOnMaxFlingNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
index bd14d2c..5529ead 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -18,6 +18,10 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -28,9 +32,12 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -150,4 +157,179 @@
             }
         }
     }
+
+    @Test
+    public void notClearCustomViewDelegate() throws Throwable {
+        final RecyclerView recyclerView = new RecyclerView(getActivity()) {
+            @Override
+            boolean isAccessibilityEnabled() {
+                return true;
+            }
+        };
+        final int[] layoutStart = new int[] {0};
+        final int layoutCount = 5;
+        final TestLayoutManager layoutManager = new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                detachAndScrapAttachedViews(recycler);
+                removeAndRecycleScrapInt(recycler);
+                layoutRange(recycler, layoutStart[0], layoutStart[0] + layoutCount);
+                if (layoutLatch != null) {
+                    layoutLatch.countDown();
+                }
+            }
+        };
+        final AccessibilityDelegateCompat delegateCompat = new AccessibilityDelegateCompat() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host,
+                    AccessibilityNodeInfoCompat info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setChecked(true);
+            }
+        };
+        final TestAdapter adapter = new TestAdapter(100) {
+            @Override
+            public TestViewHolder onCreateViewHolder(ViewGroup parent,
+                    int viewType) {
+                TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
+                ViewCompat.setAccessibilityDelegate(vh.itemView, delegateCompat);
+                return vh;
+            }
+        };
+        layoutManager.expectLayouts(1);
+        recyclerView.getRecycledViewPool().setMaxRecycledViews(0, 100);
+        recyclerView.setItemViewCacheSize(0); // no cache, directly goes to pool
+        recyclerView.setLayoutManager(layoutManager);
+        setRecyclerView(recyclerView);
+         mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                recyclerView.setAdapter(adapter);
+            }
+        });
+        layoutManager.waitForLayout(1);
+
+        assertEquals(layoutCount, recyclerView.getChildCount());
+        final ArrayList<View> children = new ArrayList();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < recyclerView.getChildCount(); i++) {
+                    View view = recyclerView.getChildAt(i);
+                    assertEquals(layoutStart[0] + i,
+                            recyclerView.getChildAdapterPosition(view));
+                    AccessibilityNodeInfo info = recyclerView.getChildAt(i)
+                            .createAccessibilityNodeInfo();
+                    assertTrue("custom delegate sets isChecked", info.isChecked());
+                    assertFalse(recyclerView.findContainingViewHolder(view).hasAnyOfTheFlags(
+                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
+                    assertTrue(ViewCompat.hasAccessibilityDelegate(view));
+                    children.add(view);
+                }
+            }
+        });
+
+        // invalidate and start layout at 50, all existing views will goes to recycler and
+        // being reused.
+        layoutStart[0] = 50;
+        layoutManager.expectLayouts(1);
+        adapter.dispatchDataSetChanged();
+        layoutManager.waitForLayout(1);
+        assertEquals(layoutCount, recyclerView.getChildCount());
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < recyclerView.getChildCount(); i++) {
+                    View view = recyclerView.getChildAt(i);
+                    assertEquals(layoutStart[0] + i,
+                            recyclerView.getChildAdapterPosition(view));
+                    assertTrue(children.contains(view));
+                    AccessibilityNodeInfo info = view.createAccessibilityNodeInfo();
+                    assertTrue("custom delegate sets isChecked", info.isChecked());
+                    assertFalse(recyclerView.findContainingViewHolder(view).hasAnyOfTheFlags(
+                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
+                    assertTrue(ViewCompat.hasAccessibilityDelegate(view));
+                }
+            }
+        });
+    }
+
+    @Test
+    public void clearItemDelegateWhenGoesToPool() throws Throwable {
+        final RecyclerView recyclerView = new RecyclerView(getActivity()) {
+            @Override
+            boolean isAccessibilityEnabled() {
+                return true;
+            }
+        };
+        final int firstPassLayoutCount = 5;
+        final int[] layoutCount = new int[] {firstPassLayoutCount};
+        final TestLayoutManager layoutManager = new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                detachAndScrapAttachedViews(recycler);
+                removeAndRecycleScrapInt(recycler);
+                layoutRange(recycler, 0, layoutCount[0]);
+                if (layoutLatch != null) {
+                    layoutLatch.countDown();
+                }
+            }
+        };
+        final TestAdapter adapter = new TestAdapter(100);
+        layoutManager.expectLayouts(1);
+        recyclerView.getRecycledViewPool().setMaxRecycledViews(0, 100);
+        recyclerView.setItemViewCacheSize(0); // no cache, directly goes to pool
+        recyclerView.setLayoutManager(layoutManager);
+        setRecyclerView(recyclerView);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                recyclerView.setAdapter(adapter);
+            }
+        });
+        layoutManager.waitForLayout(1);
+
+        assertEquals(firstPassLayoutCount, recyclerView.getChildCount());
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < recyclerView.getChildCount(); i++) {
+                    View view = recyclerView.getChildAt(i);
+                    assertEquals(i, recyclerView.getChildAdapterPosition(view));
+                    assertTrue(recyclerView.findContainingViewHolder(view).hasAnyOfTheFlags(
+                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
+                    assertTrue(ViewCompat.hasAccessibilityDelegate(view));
+                    AccessibilityNodeInfo info = view.createAccessibilityNodeInfo();
+                    if (Build.VERSION.SDK_INT >= 19) {
+                        assertNotNull(info.getCollectionItemInfo());
+                    }
+                }
+            }
+        });
+
+        // let all items go to recycler pool
+        layoutManager.expectLayouts(1);
+        layoutCount[0] = 0;
+        adapter.resetItemsTo(new ArrayList());
+        layoutManager.waitForLayout(1);
+        assertEquals(0, recyclerView.getChildCount());
+        assertEquals(firstPassLayoutCount, recyclerView.getRecycledViewPool()
+                .getRecycledViewCount(0));
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < firstPassLayoutCount; i++) {
+                    RecyclerView.ViewHolder vh = recyclerView.getRecycledViewPool()
+                            .getRecycledView(0);
+                    View view = vh.itemView;
+                    assertEquals(RecyclerView.NO_POSITION,
+                            recyclerView.getChildAdapterPosition(view));
+                    assertFalse(vh.hasAnyOfTheFlags(
+                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
+                    assertFalse(ViewCompat.hasAccessibilityDelegate(view));
+                }
+            }
+        });
+
+    }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
index e952370..4e9656a 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
@@ -24,9 +24,7 @@
 import android.os.Build;
 import android.support.test.filters.MediumTest;
 import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
@@ -75,13 +73,13 @@
     @Test
     public void onInitializeAccessibilityNodeInfoTest() throws Throwable {
         final RecyclerView recyclerView = new RecyclerView(getActivity()) {
-            //@Override
+            @Override
             public boolean canScrollHorizontally(int direction) {
                 return direction < 0 && mHorizontalScrollBefore ||
                         direction > 0 && mHorizontalScrollAfter;
             }
 
-            //@Override
+            @Override
             public boolean canScrollVertically(int direction) {
                 return direction < 0 && mVerticalScrollBefore ||
                         direction > 0 && mVerticalScrollAfter;
@@ -172,11 +170,9 @@
                 delegateCompat.onInitializeAccessibilityEvent(recyclerView, event);
             }
         });
-        final AccessibilityRecordCompat record = AccessibilityEventCompat
-                .asRecord(event);
-        assertEquals(record.isScrollable(), mVerticalScrollAfter || mHorizontalScrollAfter ||
-                mVerticalScrollBefore || mHorizontalScrollBefore);
-        assertEquals(record.getItemCount(), adapter.getItemCount());
+        assertEquals(event.isScrollable(), mVerticalScrollAfter || mHorizontalScrollAfter
+                || mVerticalScrollBefore || mHorizontalScrollBefore);
+        assertEquals(event.getItemCount(), adapter.getItemCount());
 
         getInstrumentation().waitForIdleSync();
         if (SUPPORTS_COLLECTION_INFO) {
@@ -240,11 +236,13 @@
     public void ignoreAccessibilityIfAdapterHasChanged() throws Throwable {
         final RecyclerView recyclerView = new RecyclerView(getActivity()) {
             //@Override
+            @Override
             public boolean canScrollHorizontally(int direction) {
                 return true;
             }
 
             //@Override
+            @Override
             public boolean canScrollVertically(int direction) {
                 return true;
             }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index 5379354..da5be39 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -28,7 +28,7 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.support.annotation.NonNull;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.ViewCompat;
@@ -53,7 +53,7 @@
 /**
  * Tests for {@link SimpleItemAnimator} API.
  */
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class RecyclerViewAnimationsTest extends BaseRecyclerViewAnimationsTest {
 
@@ -1404,7 +1404,7 @@
             public void onViewDetachedFromWindow(TestViewHolder holder) {
                 if ((addedView[0] == holder.itemView || addedView[1] == holder.itemView)
                         && ViewCompat.hasTransientState(holder.itemView)) {
-                    ViewCompat.animate(holder.itemView).cancel();
+                    holder.itemView.animate().cancel();
                 }
                 super.onViewDetachedFromWindow(holder);
             }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 9fbc967..5a13f52 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.os.Build;
@@ -58,7 +59,6 @@
 
     RecyclerView mRecyclerView;
 
-
     @Before
     public void setUp() throws Exception {
         mRecyclerView = new RecyclerView(getContext());
@@ -459,6 +459,27 @@
         assertEquals(recyclerView, holder.mNestedRecyclerView.get());
     }
 
+    @Test
+    public void exceptionContainsClasses() {
+        RecyclerView first = new RecyclerView(getContext());
+        first.setLayoutManager(new LinearLayoutManager(getContext()));
+        first.setAdapter(new MockAdapter(10));
+
+        RecyclerView second = new RecyclerView(getContext());
+        try {
+            second.setLayoutManager(first.getLayoutManager());
+            fail("exception expected");
+        } catch (IllegalArgumentException e) {
+            // Note: exception contains first RV
+            String m = e.getMessage();
+            assertTrue("must contain RV class", m.contains(RecyclerView.class.getName()));
+            assertTrue("must contain Adapter class", m.contains(MockAdapter.class.getName()));
+            assertTrue("must contain LM class", m.contains(LinearLayoutManager.class.getName()));
+            assertTrue("must contain ctx class", m.contains(getContext().getClass().getName()));
+
+        }
+    }
+
     static class MockLayoutManager extends RecyclerView.LayoutManager {
 
         int mLayoutCount = 0;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
index 1006e1b..1267fdb 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
@@ -44,12 +44,11 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -299,6 +298,41 @@
     }
 
     @Test
+    public void prefetchAfterOrientationChange() {
+        LinearLayoutManager layout = new LinearLayoutManager(getContext(),
+                LinearLayoutManager.VERTICAL, false);
+        mRecyclerView.setLayoutManager(layout);
+
+        // 100x100 pixel views
+        mRecyclerView.setAdapter(new RecyclerView.Adapter() {
+            @Override
+            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+                View view = new View(getContext());
+                view.setMinimumWidth(100);
+                view.setMinimumHeight(100);
+                assertTrue(mRecyclerView.isComputingLayout());
+                return new RecyclerView.ViewHolder(view) {};
+            }
+
+            @Override
+            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
+
+            @Override
+            public int getItemCount() {
+                return 100;
+            }
+        });
+
+        layout(100, 100);
+
+        layout.setOrientation(LinearLayoutManager.HORIZONTAL);
+
+        // Prefetch an item after changing orientation, before layout - shouldn't crash
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(1, 1);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+    }
+
+    @Test
     public void prefetchDrag() {
         // event dispatch requires a parent
         ViewGroup parent = new FrameLayout(getContext());
@@ -687,15 +721,11 @@
 
         verify(mockAdapter, times(2)).onCreateViewHolder(any(ViewGroup.class), anyInt());
         verify(mockAdapter, times(2)).onBindViewHolder(
-                argThat(new BaseMatcher<RecyclerView.ViewHolder>() {
+                argThat(new ArgumentMatcher<RecyclerView.ViewHolder>() {
                     @Override
-                    public boolean matches(Object item) {
-                        RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) item;
+                    public boolean matches(RecyclerView.ViewHolder holder) {
                         return holder.itemView == holder.mNestedRecyclerView.get();
                     }
-
-                    @Override
-                    public void describeTo(Description description) { }
                 }),
                 anyInt(),
                 any(List.class));
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFastScrollerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFastScrollerTest.java
new file mode 100644
index 0000000..dba3770
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFastScrollerTest.java
@@ -0,0 +1,413 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static android.support.v7.widget.RecyclerView.VERTICAL;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.StateListDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.recyclerview.R;
+import android.support.v7.util.TouchUtils;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewFastScrollerTest extends BaseRecyclerViewInstrumentationTest {
+    private static final int FLAG_HORIZONTAL = 1;
+    private static final int FLAG_VERTICAL = 1 << 1;
+    private int mScrolledByY = -1000;
+    private int mScrolledByX = -1000;
+    private int mVerticalScrollRange;
+    private int mVerticalScrollExtent;
+    private int mVerticalScrollOffset;
+    private int mHorizontalScrollRange;
+    private int mHorizontalScrollExtent;
+    private int mHorizontalScrollOffset;
+    private int mWidth;
+    private int mHeight;
+    private FastScroller mScroller;
+    private boolean mHide;
+
+    private void setContentView(final int layoutId) throws Throwable {
+        final Activity activity = mActivityRule.getActivity();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.setContentView(layoutId);
+            }
+        });
+    }
+
+    @Before
+    public void setup() throws Exception {
+        mWidth = 500;
+        mHeight = 500;
+        mVerticalScrollRange = 1000;
+        mVerticalScrollOffset = 250;
+        mVerticalScrollExtent = 500;
+        mHorizontalScrollRange = 1000;
+        mHorizontalScrollExtent = 500;
+        mHorizontalScrollOffset = 250;
+        mRecyclerView = new RecyclerView(getActivity()) {
+            @Override
+            public int computeVerticalScrollRange() {
+                return mVerticalScrollRange;
+            }
+
+            @Override
+            public int computeVerticalScrollExtent() {
+                return mVerticalScrollExtent;
+            }
+
+            @Override
+            public int computeVerticalScrollOffset() {
+                return mVerticalScrollOffset;
+            }
+
+            @Override
+            public int computeHorizontalScrollRange() {
+                return mHorizontalScrollRange;
+            }
+
+            @Override
+            public int computeHorizontalScrollExtent() {
+                return mHorizontalScrollExtent;
+            }
+
+            @Override
+            public int computeHorizontalScrollOffset() {
+                return mHorizontalScrollOffset;
+            }
+
+            @Override
+            public void scrollBy(int x, int y) {
+                mScrolledByY = y;
+                mScrolledByX = x;
+            }
+        };
+        mRecyclerView.setAdapter(new TestAdapter(50));
+        mRecyclerView.measure(
+                View.MeasureSpec.makeMeasureSpec(mWidth, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(mHeight, View.MeasureSpec.EXACTLY));
+        mRecyclerView.layout(0, 0, mWidth, mHeight);
+
+        Resources res = getActivity().getResources();
+        mScroller = new FastScroller(mRecyclerView, (StateListDrawable) res.getDrawable(
+                android.support.v7.recyclerview.test.R.drawable.fast_scroll_thumb_drawable),
+                res.getDrawable(
+                        android.support.v7.recyclerview.test.R.drawable.fast_scroll_track_drawable),
+                (StateListDrawable) res.getDrawable(
+                        android.support.v7.recyclerview.test.R.drawable.fast_scroll_thumb_drawable),
+                res.getDrawable(
+                        android.support.v7.recyclerview.test.R.drawable.fast_scroll_track_drawable),
+                res.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
+                res.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
+                res.getDimensionPixelOffset(R.dimen.fastscroll_margin)) {
+            @Override
+            public void show() {
+                // Overriden to avoid animation calls in instrumentation thread
+            }
+
+            @Override
+            public void hide(int duration) {
+                mHide = true;
+            }
+        };
+        mRecyclerView.mEnableFastScroller = true;
+
+        // Draw it once so height/width gets updated
+        mScroller.onDrawOver(null, mRecyclerView, null);
+    }
+
+    @Test
+    public void sanityScrollingInstrumentation() throws Throwable {
+        mRecyclerView = new RecyclerView(getActivity());
+        final Activity activity = mActivityRule.getActivity();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.setContentView(
+                        android.support.v7.recyclerview.test.R.layout.fast_scrollbar_test_rv);
+                mRecyclerView = (RecyclerView) activity.findViewById(
+                        android.support.v7.recyclerview.test.R.id.recycler_view);
+                LinearLayoutManager layout = new LinearLayoutManager(activity.getBaseContext());
+                layout.setOrientation(VERTICAL);
+                mRecyclerView.setLayoutManager(layout);
+                mRecyclerView.setAdapter(new TestAdapter(50));
+                mScroller = (FastScroller) mRecyclerView.getItemDecorationAt(0);
+                assertTrue("Expected centerY to start == 0", mScroller.mVerticalThumbCenterY == 0);
+                assertFalse("Expected thumb to start invisible", mScroller.isVisible());
+            }
+        });
+        waitForIdleScroll(mRecyclerView);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.scrollBy(0, 400);
+                assertTrue("Expected centerY to be > 0" + mScroller.mVerticalThumbCenterY,
+                        mScroller.mVerticalThumbCenterY > 0);
+                assertTrue("Expected thumb to be visible", mScroller.isVisible());
+
+            }
+        });
+        int oldOffset = mRecyclerView.computeVerticalScrollOffset();
+        int[] absoluteCoords = new int[2];
+        mRecyclerView.getLocationOnScreen(absoluteCoords);
+        TouchUtils.drag(InstrumentationRegistry.getInstrumentation(), mRecyclerView.getWidth() - 10,
+                mRecyclerView.getWidth() - 10, mScroller.mVerticalThumbCenterY + absoluteCoords[1],
+                mRecyclerView.getHeight() + absoluteCoords[1], 100);
+        assertTrue("Expected dragging thumb to move recyclerView",
+                mRecyclerView.computeVerticalScrollOffset() > oldOffset);
+    }
+
+    @Test
+    public void properCleanUp() throws Throwable {
+        mRecyclerView = new RecyclerView(getActivity());
+        final Activity activity = mActivityRule.getActivity();
+        final CountDownLatch latch = new CountDownLatch(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.setContentView(
+                        android.support.v7.recyclerview.test.R.layout.fast_scrollbar_test_rv);
+                mRecyclerView = (RecyclerView) activity.findViewById(
+                        android.support.v7.recyclerview.test.R.id.recycler_view);
+                LinearLayoutManager layout = new LinearLayoutManager(activity.getBaseContext());
+                layout.setOrientation(VERTICAL);
+                mRecyclerView.setLayoutManager(layout);
+                mRecyclerView.setAdapter(new TestAdapter(50));
+                Resources res = getActivity().getResources();
+                mScroller = new FastScroller(mRecyclerView, (StateListDrawable) res.getDrawable(
+                        android.support.v7.recyclerview.test.R.drawable.fast_scroll_thumb_drawable),
+                        res.getDrawable(
+                                android.support.v7.recyclerview.test.R.drawable
+                                        .fast_scroll_track_drawable),
+                        (StateListDrawable) res.getDrawable(
+                                android.support.v7.recyclerview.test.R.drawable
+                                        .fast_scroll_thumb_drawable),
+                        res.getDrawable(
+                                android.support.v7.recyclerview.test.R.drawable
+                                        .fast_scroll_track_drawable),
+                        res.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
+                        res.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
+                        res.getDimensionPixelOffset(R.dimen.fastscroll_margin)) {
+                    @Override
+                    public void show() {
+                        // Overriden to avoid animation calls in instrumentation thread
+                    }
+
+                    @Override
+                    public void hide(int duration) {
+                        latch.countDown();
+                        mHide = true;
+                    }
+                };
+
+            }
+        });
+        waitForIdleScroll(mRecyclerView);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.scrollBy(0, 400);
+                mScroller.attachToRecyclerView(new RecyclerView(getActivity()));
+            }
+        });
+        assertFalse(latch.await(2, TimeUnit.SECONDS));
+        assertFalse(mHide);
+    }
+
+    @Test
+    public void inflationTest() throws Throwable {
+        setContentView(android.support.v7.recyclerview.test.R.layout.fast_scrollbar_test_rv);
+        getInstrumentation().waitForIdleSync();
+        RecyclerView view = (RecyclerView) getActivity().findViewById(
+                android.support.v7.recyclerview.test.R.id.recycler_view);
+        assertTrue(view.getItemDecorationAt(0) instanceof FastScroller);
+        FastScroller scroller = (FastScroller) view.getItemDecorationAt(0);
+        assertNotNull(scroller.getHorizontalThumbDrawable());
+        assertNotNull(scroller.getHorizontalTrackDrawable());
+        assertNotNull(scroller.getVerticalThumbDrawable());
+        assertNotNull(scroller.getVerticalTrackDrawable());
+    }
+
+    @UiThreadTest
+    @Test
+    public void initWithBadDrawables() throws Throwable {
+        Throwable exception = null;
+        try {
+            mRecyclerView.initFastScroller(null, null, null, null);
+        } catch (Throwable t) {
+            exception = t;
+        }
+        assertTrue(exception instanceof IllegalArgumentException);
+    }
+
+    @Test
+    public void verticalScrollUpdatesFastScrollThumb() throws Throwable {
+        scrollUpdatesFastScrollThumb(FLAG_VERTICAL);
+    }
+
+    @Test
+    public void horizontalScrollUpdatesFastScrollThumb() throws Throwable {
+        scrollUpdatesFastScrollThumb(FLAG_HORIZONTAL);
+    }
+
+    private void scrollUpdatesFastScrollThumb(int direction) throws Throwable {
+        mScroller.updateScrollPosition(direction == FLAG_VERTICAL ? 0 : 250,
+                direction == FLAG_VERTICAL ? 250 : 0);
+        if (direction == FLAG_VERTICAL) {
+            assertTrue("Expected 250 for centerY, got " + mScroller.mVerticalThumbCenterY,
+                    mScroller.mVerticalThumbCenterY == 250);
+            assertTrue("Expected 250 for thumb height, got " + mScroller.mVerticalThumbHeight,
+                    mScroller.mVerticalThumbHeight == 250);
+        } else if (direction == FLAG_HORIZONTAL) {
+            assertTrue("Expected 250 for centerX, got " + mScroller.mHorizontalThumbCenterX,
+                    mScroller.mHorizontalThumbCenterX == 250);
+            assertTrue("Expected 250 for thumb width, got " + mScroller.mHorizontalThumbWidth,
+                    mScroller.mHorizontalThumbWidth == 250);
+        }
+        assertTrue(mScroller.isVisible());
+
+        mScroller.updateScrollPosition(direction == FLAG_VERTICAL ? 0 : 42,
+                direction == FLAG_VERTICAL ? 42 : 0);
+        if (direction == FLAG_VERTICAL) {
+            assertTrue("Expected 146 for centerY, got " + mScroller.mVerticalThumbCenterY,
+                    mScroller.mVerticalThumbCenterY == 146);
+            assertTrue("Expected 250 for thumb height, got " + mScroller.mVerticalThumbHeight,
+                    mScroller.mVerticalThumbHeight == 250);
+        } else if (direction == FLAG_HORIZONTAL) {
+            assertTrue("Expected 146 for centerX, got " + mScroller.mHorizontalThumbCenterX,
+                    mScroller.mHorizontalThumbCenterX == 146);
+            assertTrue("Expected 250 for thumb width, got " + mScroller.mHorizontalThumbWidth,
+                    mScroller.mHorizontalThumbWidth == 250);
+        }
+        assertTrue(mScroller.isVisible());
+    }
+
+    @Test
+    public void draggingDoesNotTriggerFastScrollIfNotInThumb() throws Throwable {
+        mScroller.updateScrollPosition(0, 250);
+        final MotionEvent downEvent = MotionEvent.obtain(10, 10, MotionEvent.ACTION_DOWN, 250, 250,
+                0);
+        assertFalse(mScroller.onInterceptTouchEvent(mRecyclerView, downEvent));
+        final MotionEvent moveEvent = MotionEvent.obtain(10, 10, MotionEvent.ACTION_MOVE, 250, 275,
+                0);
+        assertFalse(mScroller.onInterceptTouchEvent(mRecyclerView, moveEvent));
+    }
+
+    @Test
+    public void verticalDraggingFastScrollThumbDoesActualScrolling() throws Throwable {
+        draggingFastScrollThumbDoesActualScrolling(FLAG_VERTICAL);
+    }
+
+    @Test
+    public void horizontalDraggingFastScrollThumbDoesActualScrolling() throws Throwable {
+        draggingFastScrollThumbDoesActualScrolling(FLAG_HORIZONTAL);
+    }
+
+    private void draggingFastScrollThumbDoesActualScrolling(int direction) throws Throwable {
+        mScroller.updateScrollPosition(direction == FLAG_VERTICAL ? 0 : 250,
+                direction == FLAG_VERTICAL ? 250 : 0);
+        final MotionEvent downEvent = MotionEvent.obtain(10, 10, MotionEvent.ACTION_DOWN,
+                direction == FLAG_VERTICAL ? 500 : 250, direction == FLAG_VERTICAL ? 250 : 500, 0);
+        assertTrue(mScroller.onInterceptTouchEvent(mRecyclerView, downEvent));
+        assertTrue(mScroller.isDragging());
+        final MotionEvent moveEvent = MotionEvent.obtain(10, 10, MotionEvent.ACTION_MOVE,
+                direction == FLAG_VERTICAL ? 500 : 221, direction == FLAG_VERTICAL ? 221 : 500, 0);
+        mScroller.onTouchEvent(mRecyclerView, moveEvent);
+        if (direction == FLAG_VERTICAL) {
+            assertTrue("Expected to get -29, but got " + mScrolledByY, mScrolledByY == -29);
+        } else {
+            assertTrue("Expected to get -29, but got " + mScrolledByX, mScrolledByX == -29);
+        }
+    }
+
+    static class TestAdapter extends RecyclerView.Adapter {
+        private int mItemCount;
+
+        public static class ViewHolder extends RecyclerView.ViewHolder {
+            public TextView mTextView;
+
+            ViewHolder(TextView v) {
+                super(v);
+                mTextView = v;
+            }
+
+            @Override
+            public String toString() {
+                return super.toString() + " '" + mTextView.getText();
+            }
+        }
+
+        TestAdapter(int itemCount) {
+            mItemCount = itemCount;
+        }
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
+                int viewType) {
+            final ViewHolder h = new ViewHolder(new TextView(parent.getContext()));
+            h.mTextView.setMinimumHeight(128);
+            h.mTextView.setPadding(20, 0, 20, 0);
+            h.mTextView.setFocusable(true);
+            h.mTextView.setBackgroundColor(Color.BLUE);
+            RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(
+                    LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT);
+            lp.leftMargin = 10;
+            lp.rightMargin = 5;
+            lp.topMargin = 20;
+            lp.bottomMargin = 15;
+            h.mTextView.setLayoutParams(lp);
+            return h;
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            holder.itemView.setTag("pos " + position);
+        }
+
+        @Override
+        public int getItemCount() {
+            return mItemCount;
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index 6fd9a86..42fde85 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.support.v7.widget;
 
 import static android.support.v7.widget.RecyclerView.NO_POSITION;
@@ -37,6 +36,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -50,15 +50,17 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.SystemClock;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.NestedScrollingParent2;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.util.TouchUtils;
+import android.support.v7.widget.test.NestedScrollingParent2Adapter;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -85,7 +87,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 @RunWith(AndroidJUnit4.class)
-@MediumTest
+@LargeTest
 public class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest {
     private static final int FLAG_HORIZONTAL = 1;
     private static final int FLAG_VERTICAL = 1 << 1;
@@ -250,6 +252,112 @@
     }
 
     @Test
+    public void setAdapterNotifyItemRangeInsertedCrashTest() throws Throwable {
+        final RecyclerView rv = new RecyclerView(getActivity());
+        final TestLayoutManager lm = new LayoutAllLayoutManager(true);
+        lm.setSupportsPredictive(true);
+        rv.setLayoutManager(lm);
+        setRecyclerView(rv);
+        lm.expectLayouts(1);
+        setAdapter(new TestAdapter(1));
+        lm.waitForLayout(2);
+        lm.expectLayouts(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final TestAdapter adapter2 = new TestAdapter(0);
+                rv.setAdapter(adapter2);
+                adapter2.addItems(0, 1, "1");
+                adapter2.notifyItemRangeInserted(0, 1);
+            }
+        });
+        lm.waitForLayout(2);
+    }
+
+    @Test
+    public void swapAdapterNotifyItemRangeInsertedCrashTest() throws Throwable {
+        final RecyclerView rv = new RecyclerView(getActivity());
+        final TestLayoutManager lm = new LayoutAllLayoutManager(true);
+        lm.setSupportsPredictive(true);
+        rv.setLayoutManager(lm);
+        setRecyclerView(rv);
+        lm.expectLayouts(1);
+        setAdapter(new TestAdapter(1));
+        lm.waitForLayout(2);
+        lm.expectLayouts(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final TestAdapter adapter2 = new TestAdapter(0);
+                rv.swapAdapter(adapter2, true);
+                adapter2.addItems(0, 1, "1");
+                adapter2.notifyItemRangeInserted(0, 1);
+            }
+        });
+        lm.waitForLayout(2);
+    }
+
+    @Test
+    public void predictiveMeasuredCrashTest() throws Throwable {
+        final RecyclerView rv = new RecyclerView(getActivity());
+        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true) {
+            @Override
+            public void onAttachedToWindow(RecyclerView view) {
+                super.onAttachedToWindow(view);
+                assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
+            }
+
+            @Override
+            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
+                super.onDetachedFromWindow(view, recycler);
+                assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
+            }
+
+            @Override
+            public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
+                    int widthSpec,
+                    int heightSpec) {
+                if (state.getItemCount() > 0) {
+                    // A typical LayoutManager will use a child view to measure the size.
+                    View v = recycler.getViewForPosition(0);
+                }
+                super.onMeasure(recycler, state, widthSpec, heightSpec);
+            }
+        };
+        lm.setSupportsPredictive(true);
+        lm.setAutoMeasureEnabled(false);
+        rv.setHasFixedSize(false);
+        final TestAdapter adapter = new TestAdapter(0);
+        rv.setAdapter(adapter);
+        rv.setLayoutManager(lm);
+        lm.expectLayouts(1);
+        setRecyclerView(rv);
+        lm.waitForLayout(2);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ViewGroup parent = (ViewGroup) rv.getParent();
+                parent.removeView(rv);
+                // setting RV as child of LinearLayout using MATCH_PARENT will cause
+                // RV.onMeasure() being called twice before layout(). This may cause crash.
+                LinearLayout linearLayout = new LinearLayout(parent.getContext());
+                linearLayout.setOrientation(LinearLayout.VERTICAL);
+                parent.addView(linearLayout,
+                        ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT);
+                linearLayout.addView(rv, ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT);
+
+            }
+        });
+
+        lm.expectLayouts(1);
+        adapter.addAndNotify(1);
+        lm.waitForLayout(2);
+        checkForMainThreadException();
+    }
+
+    @Test
     public void detachRvAndLayoutManagerProperly() throws Throwable {
         final RecyclerView rv = new RecyclerView(getActivity());
         final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true) {
@@ -421,7 +529,7 @@
             @Override
             public void run(View view) throws RuntimeException {
                 view.layout(10, 10, 30, 50);
-                ViewCompat.setTranslationX(view, 10);
+                view.setTranslationX(10);
                 assertThat(getTransformedBoundingBox(view), is(new Rect(20, 10, 40, 50)));
             }
         });
@@ -433,7 +541,7 @@
             @Override
             public void run(View view) throws RuntimeException {
                 view.layout(10, 10, 30, 50);
-                ViewCompat.setTranslationY(view, 10);
+                view.setTranslationY(10);
                 assertThat(getTransformedBoundingBox(view), is(new Rect(10, 20, 30, 60)));
             }
         });
@@ -445,7 +553,7 @@
             @Override
             public void run(View view) throws RuntimeException {
                 view.layout(10, 10, 30, 50);
-                ViewCompat.setScaleX(view, 2);
+                view.setScaleX(2);
                 assertThat(getTransformedBoundingBox(view), is(new Rect(0, 10, 40, 50)));
             }
         });
@@ -457,7 +565,7 @@
             @Override
             public void run(View view) throws RuntimeException {
                 view.layout(10, 10, 30, 50);
-                ViewCompat.setScaleY(view, 2);
+                view.setScaleY(2);
                 assertThat(getTransformedBoundingBox(view), is(new Rect(10, -10, 30, 70)));
             }
         });
@@ -469,7 +577,7 @@
             @Override
             public void run(View view) throws RuntimeException {
                 view.layout(10, 10, 30, 50);
-                ViewCompat.setRotation(view, 90);
+                view.setRotation(90);
                 assertThat(getTransformedBoundingBox(view), is(new Rect(0, 20, 40, 40)));
             }
         });
@@ -500,7 +608,7 @@
                 // trigger decor offsets calculation
                 calculateItemDecorationsForChild(view, new Rect());
                 view.layout(10, 10, 30, 50);
-                ViewCompat.setRotation(view, 90);
+                view.setRotation(90);
                 assertThat(RecyclerViewLayoutTest.this.getTransformedBoundingBox(view),
                         is(new Rect(-4, 19, 42, 43)));
 
@@ -1344,38 +1452,110 @@
         scrollInOtherOrientationTest(FLAG_VERTICAL | FLAG_FLING);
     }
 
+    @SuppressWarnings("WrongConstant")
     @Test
     public void nestedDragVertical() throws Throwable {
-        TestedFrameLayout tfl = getActivity().getContainer();
-        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
-        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
+        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
+        getActivity().getContainer().setNestedScrollingDelegate(nsp);
+        // Scroll and expect the RV to not scroll
         scrollInOtherOrientationTest(FLAG_VERTICAL, 0);
+
+        // Verify that the touch nested scroll was started and finished
+        verify(nsp, atLeastOnce()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, atLeastOnce()).onStopNestedScroll(eq(mRecyclerView), eq(ViewCompat.TYPE_TOUCH));
+
+        // Verify that the non-touch events were dispatched by the fling settle
+        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
     }
 
+    @SuppressWarnings("WrongConstant")
     @Test
     public void nestedDragHorizontal() throws Throwable {
-        TestedFrameLayout tfl = getActivity().getContainer();
-        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
-        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
+        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
+        getActivity().getContainer().setNestedScrollingDelegate(nsp);
+        // Scroll and expect the RV to not scroll
         scrollInOtherOrientationTest(FLAG_HORIZONTAL, 0);
+
+        // Verify that the touch nested scroll was started and finished
+        verify(nsp, atLeastOnce()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, atLeastOnce()).onStopNestedScroll(eq(mRecyclerView), eq(ViewCompat.TYPE_TOUCH));
+
+        // Verify that the non-touch events were dispatched by the fling settle
+        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
     }
 
+    @SuppressWarnings("WrongConstant")
     @Test
-    public void nestedDragHorizontalCallsStopNestedScroll() throws Throwable {
-        TestedFrameLayout tfl = getActivity().getContainer();
-        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
-        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
-        scrollInOtherOrientationTest(FLAG_HORIZONTAL, 0);
-        assertTrue("onStopNestedScroll called", tfl.stopNestedScrollCalled());
+    public void nestedFlingVertical() throws Throwable {
+        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
+        getActivity().getContainer().setNestedScrollingDelegate(nsp);
+        // Fling and expect the RV to not scroll
+        scrollInOtherOrientationTest(FLAG_VERTICAL | FLAG_FLING, FLAG_FLING);
+
+        // Verify that the touch nested scroll was not started
+        verify(nsp, never()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, never()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, never()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, never()).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_TOUCH);
+
+        // Verify that the non-touch nested scroll was started and finished
+        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, times(1)).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_NON_TOUCH);
     }
 
+    @SuppressWarnings("WrongConstant")
     @Test
-    public void nestedDragVerticalCallsStopNestedScroll() throws Throwable {
-        TestedFrameLayout tfl = getActivity().getContainer();
-        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
-        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
-        scrollInOtherOrientationTest(FLAG_VERTICAL, 0);
-        assertTrue("onStopNestedScroll called", tfl.stopNestedScrollCalled());
+    public void nestedFlingHorizontal() throws Throwable {
+        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
+        getActivity().getContainer().setNestedScrollingDelegate(nsp);
+        // Fling and expect the RV to not scroll
+        scrollInOtherOrientationTest(FLAG_HORIZONTAL | FLAG_FLING, FLAG_FLING);
+
+        // Verify that the touch nested scroll was not started
+        verify(nsp, never()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, never()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, never()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
+        verify(nsp, never()).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_TOUCH);
+
+        // Verify that the non-touch nested scroll was started and finished
+        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(nsp, times(1)).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_NON_TOUCH);
     }
 
     private void scrollInOtherOrientationTest(int flags)
@@ -4463,4 +4643,108 @@
     private interface ViewRunnable {
         void run(View view) throws RuntimeException;
     }
+
+    public static class FullyConsumingNestedScroller extends NestedScrollingParent2Adapter {
+        @Override
+        public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
+                @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) {
+            // Always start regardless of type
+            return true;
+        }
+
+        @Override
+        public void onNestedPreScroll(@NonNull View target, int dx, int dy,
+                @Nullable int[] consumed, @ViewCompat.NestedScrollType int type) {
+            // Consume everything!
+            consumed[0] = dx;
+            consumed[1] = dy;
+        }
+
+        @Override
+        public int getNestedScrollAxes() {
+            return ViewCompat.SCROLL_AXIS_VERTICAL | ViewCompat.SCROLL_AXIS_HORIZONTAL;
+        }
+
+        @Override
+        public void onStopNestedScroll(View target) {
+            super.onStopNestedScroll(target);
+        }
+
+        @Override
+        public void onStopNestedScroll(@NonNull View target,
+                @ViewCompat.NestedScrollType int type) {
+            super.onStopNestedScroll(target, type);
+        }
+    }
+
+    @Test
+    public void testRemainingScrollInLayout() throws Throwable {
+        final RecyclerView recyclerView = new RecyclerView(getActivity());
+        final TestAdapter adapter = new TestAdapter(100);
+
+        final CountDownLatch firstScrollDone = new CountDownLatch(1);
+        final CountDownLatch scrollFinished = new CountDownLatch(1);
+        final int[] totalScrollDistance = new int[] {0};
+        recyclerView.setLayoutManager(new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                if (firstScrollDone.getCount() < 1 && scrollFinished.getCount() == 1) {
+                    try {
+                        assertTrue("layout pass has remaining scroll",
+                                state.getRemainingScrollVertical() != 0);
+                        assertEquals("layout pass has remaining scroll",
+                                1000 - totalScrollDistance[0], state.getRemainingScrollVertical());
+                    } catch (Throwable throwable) {
+                        postExceptionToInstrumentation(throwable);
+                    }
+                }
+                super.onLayoutChildren(recycler, state);
+            }
+
+            @Override
+            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+                    RecyclerView.State state) {
+                firstScrollDone.countDown();
+                totalScrollDistance[0] += dy;
+                if (state.getRemainingScrollVertical() == 0) {
+                    // the last scroll pass will have remaining 0
+                    scrollFinished.countDown();
+                }
+                return super.scrollVerticallyBy(dy, recycler, state);
+            }
+
+            @Override
+            public boolean canScrollVertically() {
+                return true;
+            }
+        });
+        recyclerView.setAdapter(adapter);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    setRecyclerView(recyclerView);
+                    recyclerView.smoothScrollBy(0, 1000);
+                } catch (Throwable throwable) {
+                    postExceptionToInstrumentation(throwable);
+                }
+            }
+        });
+
+        firstScrollDone.await(1, TimeUnit.SECONDS);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    recyclerView.requestLayout();
+                } catch (Throwable throwable) {
+                    postExceptionToInstrumentation(throwable);
+                }
+            }
+        });
+        waitForIdleScroll(recyclerView);
+        assertTrue(scrollFinished.getCount() < 1);
+        assertEquals(totalScrollDistance[0], 1000);
+    }
+
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewOnGenericMotionEventTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewOnGenericMotionEventTest.java
new file mode 100644
index 0000000..aee15dd
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewOnGenericMotionEventTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.InputDeviceCompat;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewConfigurationCompat;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewOnGenericMotionEventTest {
+
+    TestRecyclerView mRecyclerView;
+
+    @Before
+    public void setUp() throws Exception {
+        mRecyclerView = new TestRecyclerView(getContext());
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    private void layout() {
+        mRecyclerView.layout(0, 0, 320, 320);
+    }
+
+    @Test
+    public void rotaryEncoderVerticalScroll() {
+        MockLayoutManager layoutManager = new MockLayoutManager(true, true);
+        mRecyclerView.setLayoutManager(layoutManager);
+        layout();
+        MotionEvent e = obtainScrollMotionEvent(
+                MotionEventCompat.AXIS_SCROLL, 2, InputDeviceCompat.SOURCE_ROTARY_ENCODER);
+        mRecyclerView.onGenericMotionEvent(e);
+        assertTotalScroll(0, (int) (-2f * getScaledVerticalScrollFactor()));
+    }
+
+    @Test
+    public void rotaryEncoderHorizontalScroll() {
+        // The encoder is one-dimensional, and can only scroll horizontally if vertical scrolling
+        // is not enabled.
+        MockLayoutManager layoutManager = new MockLayoutManager(true, false);
+        mRecyclerView.setLayoutManager(layoutManager);
+        layout();
+        MotionEvent e = obtainScrollMotionEvent(
+                MotionEventCompat.AXIS_SCROLL, 2, InputDeviceCompat.SOURCE_ROTARY_ENCODER);
+        mRecyclerView.onGenericMotionEvent(e);
+        assertTotalScroll((int) (2f * getScaledHorizontalScrollFactor()), 0);
+    }
+
+    @Test
+    public void pointerVerticalScroll() {
+        MockLayoutManager layoutManager = new MockLayoutManager(true, true);
+        mRecyclerView.setLayoutManager(layoutManager);
+        layout();
+        MotionEvent e = obtainScrollMotionEvent(
+                MotionEventCompat.AXIS_VSCROLL, 2, InputDeviceCompat.SOURCE_CLASS_POINTER);
+        mRecyclerView.onGenericMotionEvent(e);
+        assertTotalScroll(0, (int) (-2f * getScaledVerticalScrollFactor()));
+    }
+
+    @Test
+    public void pointerHorizontalScroll() {
+        MockLayoutManager layoutManager = new MockLayoutManager(true, true);
+        mRecyclerView.setLayoutManager(layoutManager);
+        layout();
+        MotionEvent e = obtainScrollMotionEvent(
+                MotionEventCompat.AXIS_HSCROLL, 2, InputDeviceCompat.SOURCE_CLASS_POINTER);
+        mRecyclerView.onGenericMotionEvent(e);
+        assertTotalScroll((int) (2f * getScaledHorizontalScrollFactor()), 0);
+    }
+
+    @Test
+    public void nonZeroScaledVerticalScrollFactor() {
+        assertNotEquals(0, getScaledVerticalScrollFactor());
+    }
+
+    @Test
+    public void nonZeroScaledHorizontalScrollFactor() {
+        assertNotEquals(0, getScaledHorizontalScrollFactor());
+    }
+
+    private void assertTotalScroll(int x, int y) {
+        assertEquals("x total scroll", x, mRecyclerView.mTotalX);
+        assertEquals("y total scroll", y, mRecyclerView.mTotalY);
+    }
+
+    private static MotionEvent obtainScrollMotionEvent(int axis, int axisValue, int inputDevice) {
+        MotionEvent.PointerProperties[] pointerProperties = { new MotionEvent.PointerProperties() };
+        MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+        coords.setAxisValue(axis, axisValue);
+        MotionEvent.PointerCoords[] pointerCoords = { coords };
+        float xPrecision = 1;
+        float yPrecision = 1;
+        int deviceId = 0;
+        int edgeFlags = 0;
+        int flags = 0;
+        return MotionEvent.obtain(0, System.currentTimeMillis(), MotionEvent.ACTION_SCROLL,
+                1, pointerProperties, pointerCoords, 0, 0, xPrecision, yPrecision, deviceId,
+                edgeFlags, inputDevice, flags);
+    }
+
+    private float getScaledVerticalScrollFactor() {
+        return ViewConfigurationCompat.getScaledVerticalScrollFactor(
+                ViewConfiguration.get(getContext()), getContext());
+    }
+
+    private float getScaledHorizontalScrollFactor() {
+        return ViewConfigurationCompat.getScaledHorizontalScrollFactor(
+                ViewConfiguration.get(getContext()), getContext());
+    }
+
+    static class MockLayoutManager extends RecyclerView.LayoutManager {
+
+        private final boolean mCanScrollHorizontally;
+
+        private final boolean mCanScrollVertically;
+
+        MockLayoutManager(boolean canScrollHorizontally, boolean canScrollVertically) {
+            mCanScrollHorizontally = canScrollHorizontally;
+            mCanScrollVertically = canScrollVertically;
+        }
+
+        @Override
+        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
+            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        @Override
+        public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
+                RecyclerView.State state) {
+            return dx;
+        }
+
+        @Override
+        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+                RecyclerView.State state) {
+            return dy;
+        }
+
+        @Override
+        public boolean canScrollHorizontally() {
+            return mCanScrollHorizontally;
+        }
+
+        @Override
+        public boolean canScrollVertically() {
+            return mCanScrollVertically;
+        }
+    }
+
+    static class MockAdapter extends RecyclerView.Adapter {
+
+        private int mCount = 0;
+
+        MockAdapter(int count) {
+            this.mCount = count;
+        }
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new MockViewHolder(new TextView(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+
+        }
+
+        @Override
+        public int getItemCount() {
+            return mCount;
+        }
+    }
+
+    static class MockViewHolder extends RecyclerView.ViewHolder {
+        MockViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
+    private static class TestRecyclerView extends RecyclerView {
+        int mTotalX = 0;
+        int mTotalY = 0;
+
+        TestRecyclerView(Context context) {
+            super(context);
+        }
+
+        boolean scrollByInternal(int x, int y, MotionEvent ev) {
+            mTotalX += x;
+            mTotalY += y;
+            return super.scrollByInternal(x, y, ev);
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
index f231c44..6a32572 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.widget;
 
-
 import static android.support.v7.widget.LayoutState.LAYOUT_START;
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
@@ -36,9 +35,7 @@
 import android.os.Parcelable;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
 import android.support.test.filters.Suppress;
-import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
@@ -54,7 +51,7 @@
 import java.util.UUID;
 
 @RunWith(Parameterized.class)
-@MediumTest
+@LargeTest
 public class StaggeredGridLayoutManagerBaseConfigSetTest
         extends BaseStaggeredGridLayoutManagerTest {
 
@@ -766,9 +763,9 @@
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
-                    ViewCompat.setTranslationX(vh.itemView, size * 2);
+                    vh.itemView.setTranslationX(size * 2);
                 } else {
-                    ViewCompat.setTranslationY(vh.itemView, size * 2);
+                    vh.itemView.setTranslationY(size * 2);
                 }
             }
         });
@@ -803,9 +800,9 @@
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
-                    ViewCompat.setTranslationX(vh.itemView, -size * 2);
+                    vh.itemView.setTranslationX(-size * 2);
                 } else {
-                    ViewCompat.setTranslationY(vh.itemView, -size * 2);
+                    vh.itemView.setTranslationY(-size * 2);
                 }
             }
         });
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java
index 17299a0..08c64f0 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertNull;
 
 import android.graphics.Rect;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +32,7 @@
 import java.util.Map;
 
 @RunWith(Parameterized.class)
-@MediumTest
+@LargeTest
 public class StaggeredGridLayoutManagerGapTest extends BaseStaggeredGridLayoutManagerTest {
     private final Config mConfig;
     private final int mDeletePosition;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java
index 4757a54..50bdf1b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java
@@ -153,6 +153,7 @@
                 void onBoundItem(TestViewHolder vh, int position) {
                 }
 
+                @Override
                 boolean assignRandomSize() {
                     return false;
                 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
index 828ffab..fd67e03 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
@@ -22,7 +22,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.support.annotation.Nullable;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -33,7 +33,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-@MediumTest
+@LargeTest
 @RunWith(Parameterized.class)
 public class StaggeredGridLayoutManagerSnappingTest extends BaseStaggeredGridLayoutManagerTest {
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
index c85b711..13e781e 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
@@ -37,10 +37,8 @@
 import android.graphics.drawable.StateListDrawable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StateSet;
@@ -58,7 +56,7 @@
 import java.util.Map;
 import java.util.UUID;
 
-@MediumTest
+@LargeTest
 public class StaggeredGridLayoutManagerTest extends BaseStaggeredGridLayoutManagerTest {
     @Test
     public void forceLayoutOnDetach() throws Throwable {
@@ -1256,8 +1254,6 @@
                 delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
             }
         });
-        final AccessibilityRecordCompat record = AccessibilityEventCompat
-                .asRecord(event);
         final int start = mRecyclerView
                 .getChildLayoutPosition(
                         mLayoutManager.findFirstVisibleItemClosestToStart(false));
@@ -1265,9 +1261,9 @@
                 .getChildLayoutPosition(
                         mLayoutManager.findFirstVisibleItemClosestToEnd(false));
         assertEquals("first item position should match",
-                Math.min(start, end), record.getFromIndex());
+                Math.min(start, end), event.getFromIndex());
         assertEquals("last item position should match",
-                Math.max(start, end), record.getToIndex());
+                Math.max(start, end), event.getToIndex());
 
     }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/TestedFrameLayout.java b/v7/recyclerview/tests/src/android/support/v7/widget/TestedFrameLayout.java
index 040d001..cd5fa22 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/TestedFrameLayout.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/TestedFrameLayout.java
@@ -17,22 +17,18 @@
 package android.support.v7.widget;
 
 import android.content.Context;
-import android.support.v4.view.NestedScrollingParent;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.view.NestedScrollingParent2;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-public class TestedFrameLayout extends FrameLayout implements NestedScrollingParent {
+public class TestedFrameLayout extends FrameLayout implements NestedScrollingParent2 {
 
-    static final int TEST_NESTED_SCROLL_MODE_IGNORE = 0;
-    static final int TEST_NESTED_SCROLL_MODE_CONSUME = 1;
-
-    private int mNestedScrollMode;
-    private int mNestedFlingMode;
-    private boolean mNestedStopNestedScrollCalled;
+    private NestedScrollingParent2 mNestedScrollingDelegate;
 
     public TestedFrameLayout(Context context) {
         super(context);
@@ -105,7 +101,6 @@
         }
     }
 
-
     private RecyclerView getRvChild() {
         for (int i = 0; i < getChildCount(); i++) {
             if (getChildAt(i) instanceof RecyclerView) {
@@ -137,65 +132,91 @@
 
     @Override
     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
-        // Always start nested scroll
-        return mNestedFlingMode == TEST_NESTED_SCROLL_MODE_CONSUME
-                || mNestedScrollMode == TEST_NESTED_SCROLL_MODE_CONSUME;
+        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
     }
 
     @Override
-    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
-        Log.d("TestedFrameLayout", "onNestedPreFling: " + mNestedFlingMode);
-
-        return mNestedFlingMode == TEST_NESTED_SCROLL_MODE_CONSUME;
+    public void onNestedScrollAccepted(View child, View target, int axes) {
+        onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH);
     }
 
     @Override
     public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
-        if (mNestedScrollMode == TEST_NESTED_SCROLL_MODE_CONSUME) {
-            // We consume all scroll deltas
-            consumed[0] = dx;
-            consumed[1] = dy;
-        }
+        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
     }
 
     @Override
     public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
             int dyUnconsumed) {
-        // ignore
-    }
-
-    @Override
-    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
-        // ignore
-        return false;
-    }
-
-    @Override
-    public void onNestedScrollAccepted(View child, View target, int axes) {
-        // ignore
-    }
-
-    @Override
-    public int getNestedScrollAxes() {
-        // We can scroll in both direction
-        return ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL;
+        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                ViewCompat.TYPE_TOUCH);
     }
 
     @Override
     public void onStopNestedScroll(View target) {
-        mNestedStopNestedScrollCalled = true;
+        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
     }
 
-    public boolean stopNestedScrollCalled() {
-        return mNestedStopNestedScrollCalled;
+    @Override
+    public int getNestedScrollAxes() {
+        return mNestedScrollingDelegate != null
+                ? mNestedScrollingDelegate.getNestedScrollAxes()
+                : 0;
     }
 
-    public void setNestedScrollMode(int mode) {
-        mNestedScrollMode = mode;
+    @Override
+    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
+            @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) {
+        return mNestedScrollingDelegate != null
+                && mNestedScrollingDelegate.onStartNestedScroll(child, target, axes, type);
     }
 
-    public void setNestedFlingMode(int mode) {
-        mNestedFlingMode = mode;
+    @Override
+    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
+            @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) {
+        if (mNestedScrollingDelegate != null) {
+            mNestedScrollingDelegate.onNestedScrollAccepted(child, target, axes, type);
+        }
+    }
+
+    @Override
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        return mNestedScrollingDelegate != null
+                && mNestedScrollingDelegate.onNestedPreFling(target, velocityX, velocityY);
+    }
+
+    @Override
+    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+        return mNestedScrollingDelegate != null
+                && mNestedScrollingDelegate.onNestedFling(target, velocityX, velocityY, consumed);
+    }
+
+    @Override
+    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @ViewCompat.NestedScrollType int type) {
+        if (mNestedScrollingDelegate != null) {
+            mNestedScrollingDelegate.onNestedScroll(target, dxConsumed, dyConsumed,
+                    dxUnconsumed, dyUnconsumed, type);
+        }
+    }
+
+    @Override
+    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @Nullable int[] consumed,
+            @ViewCompat.NestedScrollType int type) {
+        if (mNestedScrollingDelegate != null) {
+            mNestedScrollingDelegate.onNestedPreScroll(target, dx, dy, consumed, type);
+        }
+    }
+
+    @Override
+    public void onStopNestedScroll(@NonNull View target, @ViewCompat.NestedScrollType int type) {
+        if (mNestedScrollingDelegate != null) {
+            mNestedScrollingDelegate.onStopNestedScroll(target, type);
+        }
+    }
+
+    public void setNestedScrollingDelegate(NestedScrollingParent2 delegate) {
+        mNestedScrollingDelegate = delegate;
     }
 
     public static class FullControlLayoutParams extends FrameLayout.LayoutParams {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java b/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java
index c95f9ab..aa66b8f 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java
@@ -16,12 +16,10 @@
 
 package android.support.v7.widget;
 
-import android.app.Instrumentation;
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
-import android.view.View;
 
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.MatcherAssert;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
index c251c5a..18af47b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
@@ -27,11 +27,11 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Build;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.util.PollingCheck;
+import android.support.testutils.PollingCheck;
 import android.support.v7.util.TouchUtils;
 import android.support.v7.widget.BaseRecyclerViewInstrumentationTest;
 import android.support.v7.widget.RecyclerView;
@@ -44,7 +44,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class ItemTouchHelperTest extends BaseRecyclerViewInstrumentationTest {
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/test/NestedScrollingParent2Adapter.java b/v7/recyclerview/tests/src/android/support/v7/widget/test/NestedScrollingParent2Adapter.java
new file mode 100644
index 0000000..da0d86f
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/test/NestedScrollingParent2Adapter.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package android.support.v7.widget.test;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.view.NestedScrollingParent2;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+public class NestedScrollingParent2Adapter implements NestedScrollingParent2 {
+
+    @Override
+    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
+            @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) {
+        return false;
+    }
+
+    @Override
+    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
+            @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) {
+    }
+
+    @Override
+    public void onStopNestedScroll(@NonNull View target, @ViewCompat.NestedScrollType int type) {
+    }
+
+    @Override
+    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, @ViewCompat.NestedScrollType int type) {
+    }
+
+    @Override
+    public void onNestedPreScroll(@NonNull View target, int dx, int dy,
+            @Nullable int[] consumed, @ViewCompat.NestedScrollType int type) {
+    }
+
+    @Override
+    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onNestedScrollAccepted(View child, View target, int axes) {
+        onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
+            int dyUnconsumed) {
+        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public void onStopNestedScroll(View target) {
+        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
+    }
+
+    @Override
+    public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY,
+            boolean consumed) {
+        return false;
+    }
+
+    @Override
+    public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
+        return false;
+    }
+
+    @Override
+    public int getNestedScrollAxes() {
+        return 0;
+    }
+}
diff --git a/v8/Android.mk b/v8/Android.mk
deleted file mode 100644
index 14ff0aa..0000000
--- a/v8/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/wear/Android.mk b/wear/Android.mk
new file mode 100644
index 0000000..eec890b
--- /dev/null
+++ b/wear/Android.mk
@@ -0,0 +1,41 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-wear \
+#       android-support-core-ui \
+#       android-support-v7-recyclerview
+#
+# in their makefiles to include the resources and their dependencies in their package.
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-support-wear
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+        android-support-core-ui \
+        android-support-annotations \
+        android-support-percent \
+        android-support-v7-recyclerview \
+        android-support-v4
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/wear/AndroidManifest.xml b/wear/AndroidManifest.xml
new file mode 100644
index 0000000..5fa5a83
--- /dev/null
+++ b/wear/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.wear">
+    <application>
+        <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+    </application>
+</manifest>
diff --git a/wear/README.txt b/wear/README.txt
new file mode 100644
index 0000000..58f2168
--- /dev/null
+++ b/wear/README.txt
@@ -0,0 +1 @@
+Library Project including Wearable Support UI Components and associated utilities.
diff --git a/wear/build.gradle b/wear/build.gradle
new file mode 100644
index 0000000..1e39dfc
--- /dev/null
+++ b/wear/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: android.support.SupportLibraryPlugin
+archivesBaseName = 'wear'
+
+dependencies {
+    api project(':support-annotations')
+    api project(':support-core-ui')
+    api project(':support-percent')
+    api project(':support-recyclerview-v7')
+
+    androidTestImplementation (libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation (libs.espresso_core) {
+        exclude module: 'support-annotations'
+    }
+    androidTestImplementation libs.mockito_core
+    androidTestImplementation libs.dexmaker_mockito
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 23
+    }
+
+    sourceSets {
+        main.java.srcDir 'src'
+        main.res.srcDirs 'res', 'res-public'
+    }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
+}
+
+supportLibrary {
+    name 'Android Wear Support UI'
+    inceptionYear '2016'
+    description 'Android Wear Support UI'
+}
diff --git a/wear/lint-baseline.xml b/wear/lint-baseline.xml
new file mode 100644
index 0000000..81531cf
--- /dev/null
+++ b/wear/lint-baseline.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0-alpha7">
+
+    <issue
+        id="DuplicateIds"
+        message="Duplicate id `@+id/off`, already defined earlier in this layout"
+        errorLine1="        android:id=&quot;@+id/off&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/drawable-v21/ws_switch_thumb_material_anim.xml"
+            line="27"
+            column="9"/>
+        <location
+            file="res/drawable-v21/ws_switch_thumb_material_anim.xml"
+            line="19"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="ResourceAsColor"
+        message="Should pass resolved color instead of resource id here: `getResources().getColor(android.R.color.darker_gray)`"
+        errorLine1="            mCircleColor = ColorStateList.valueOf(android.R.color.darker_gray);"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/android/support/wear/widget/CircledImageView.java"
+            line="148"
+            column="51"/>
+    </issue>
+
+</issues>
diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro
new file mode 100644
index 0000000..c6cd374
--- /dev/null
+++ b/wear/proguard-rules.pro
@@ -0,0 +1,19 @@
+# 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.
+
+# When layoutManager xml attribute is used, RecyclerView inflates
+# LayoutManagers' constructors using reflection.
+-keep public class * extends android.support.v7.widget.RecyclerView$LayoutManager {
+    public <init>(...);
+}
diff --git a/wear/res-public/values-v24/public_styles.xml b/wear/res-public/values-v24/public_styles.xml
new file mode 100644
index 0000000..d4575e4
--- /dev/null
+++ b/wear/res-public/values-v24/public_styles.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<!-- Definitions of styles to be exposed as public -->
+<resources>
+    <public type="style" name="Widget.Wear.RoundSwitch"/>
+</resources>
diff --git a/wear/res-public/values/public_attrs.xml b/wear/res-public/values/public_attrs.xml
new file mode 100644
index 0000000..b4b35db
--- /dev/null
+++ b/wear/res-public/values/public_attrs.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+
+<!-- Definitions of attributes to be exposed as public -->
+<resources>
+    <!-- BoxInsetLayout -->
+    <public type="attr" name="boxedEdges" />
+
+    <!-- WearableRecyclerView -->
+    <public type="attr" name="bezelWidth" />
+    <public type="attr" name="circularScrollingGestureEnabled" />
+    <public type="attr" name="scrollDegreesPerScreen" />
+
+    <!-- WearableDrawerView -->
+    <public type="attr" name="peekView" />
+    <public type="attr" name="drawerContent" />
+    <public type="attr" name="enableAutoPeek" />
+
+    <!-- WearableActionDrawerView -->
+    <public type="attr" name="actionMenu" />
+    <public type="attr" name="showOverflowInPeek" />
+    <public type="attr" name="drawerTitle" />
+
+    <!-- WearableNavigationDrawerView -->
+    <public type="attr" name="navigationStyle" />
+
+    <!-- CircularProgressLayout -->
+    <public type="attr" name="backgroundColor" />
+    <public type="attr" name="colorSchemeColors" />
+    <public type="attr" name="strokeWidth" />
+    <public type="attr" name="indeterminate" />
+</resources>
diff --git a/wear/res/color/ws_switch_thumb_color_material.xml b/wear/res/color/ws_switch_thumb_color_material.xml
new file mode 100644
index 0000000..0707d69
--- /dev/null
+++ b/wear/res/color/ws_switch_thumb_color_material.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorButtonNormal"
+            android:alpha="?android:attr/disabledAlpha" android:state_enabled="false" />
+    <item android:color="?android:attr/colorControlActivated" android:state_checked="true" />
+    <item android:color="?android:attr/colorButtonNormal" />
+</selector>
diff --git a/wear/res/color/ws_switch_track_color_material.xml b/wear/res/color/ws_switch_track_color_material.xml
new file mode 100644
index 0000000..214bf4a
--- /dev/null
+++ b/wear/res/color/ws_switch_track_color_material.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/colorPrimary" />
+    <item android:color="?android:attr/colorPrimary" />
+</selector>
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_14w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_14w.png
new file mode 100644
index 0000000..035e901
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_14w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_15w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_15w.png
new file mode 100644
index 0000000..6688631
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_15w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_16w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_16w.png
new file mode 100644
index 0000000..0dd9fce
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_16w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_17w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_17w.png
new file mode 100644
index 0000000..a15b133
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_17w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_18w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_18w.png
new file mode 100644
index 0000000..1cd259e
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_18w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_track_mtrl.png b/wear/res/drawable-hdpi/ws_switch_track_mtrl.png
new file mode 100644
index 0000000..8ed1c97
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_track_mtrl.png
Binary files differ
diff --git a/wear/res/drawable-v21/ws_ic_expand_more_white_22.xml b/wear/res/drawable-v21/ws_ic_expand_more_white_22.xml
new file mode 100644
index 0000000..4b8d7d5
--- /dev/null
+++ b/wear/res/drawable-v21/ws_ic_expand_more_white_22.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="22dp"
+        android:height="22dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+</vector>
diff --git a/wear/res/drawable-v21/ws_switch_thumb_material_anim.xml b/wear/res/drawable-v21/ws_switch_thumb_material_anim.xml
new file mode 100644
index 0000000..15423e5
--- /dev/null
+++ b/wear/res/drawable-v21/ws_switch_thumb_material_anim.xml
@@ -0,0 +1,95 @@
+<?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.
+-->
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:constantSize="true">
+    <item
+        android:id="@+id/off"
+        android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+        android:state_enabled="false" />
+    <item
+        android:id="@+id/on"
+        android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+        android:state_checked="true" />
+    <item
+        android:id="@+id/off"
+        android:drawable="@drawable/ws_switch_thumb_mtrl_14w" />
+    <transition
+        android:fromId="@id/off"
+        android:toId="@id/on">
+        <animation-list>
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_18w"
+                android:duration="75" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+        </animation-list>
+    </transition>
+    <transition
+        android:fromId="@id/on"
+        android:toId="@id/off">
+        <animation-list>
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_18w"
+                android:duration="75" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+        </animation-list>
+    </transition>
+</animated-selector>
diff --git a/wear/res/drawable-v23/ws_action_item_background.xml b/wear/res/drawable-v23/ws_action_item_background.xml
new file mode 100644
index 0000000..640a071
--- /dev/null
+++ b/wear/res/drawable-v23/ws_action_item_background.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.
+-->
+<ripple
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <color android:color="?android:attr/colorControlHighlight" />
+    </item>
+</ripple>
diff --git a/wear/res/drawable-v23/ws_action_item_icon_background.xml b/wear/res/drawable-v23/ws_action_item_icon_background.xml
new file mode 100644
index 0000000..42f4b34
--- /dev/null
+++ b/wear/res/drawable-v23/ws_action_item_icon_background.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+  <solid android:color="?android:attr/colorForeground"/>
+</shape>
diff --git a/wear/res/drawable-v23/ws_ic_expand_less_white_22.xml b/wear/res/drawable-v23/ws_ic_expand_less_white_22.xml
new file mode 100644
index 0000000..879e168
--- /dev/null
+++ b/wear/res/drawable-v23/ws_ic_expand_less_white_22.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="22dp"
+        android:height="22dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"/>
+</vector>
diff --git a/wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml b/wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml
new file mode 100644
index 0000000..f9c5fab
--- /dev/null
+++ b/wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<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="#ffffff"
+        android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml b/wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml
new file mode 100644
index 0000000..d98ec34
--- /dev/null
+++ b/wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<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="#ffffffff"
+        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_14w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_14w.png
new file mode 100644
index 0000000..119207b
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_14w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_15w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_15w.png
new file mode 100644
index 0000000..b89c86a
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_15w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_16w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_16w.png
new file mode 100644
index 0000000..7528731
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_16w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_17w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_17w.png
new file mode 100644
index 0000000..dba351f
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_17w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_18w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_18w.png
new file mode 100644
index 0000000..ab7b1df
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_18w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_track_mtrl.png b/wear/res/drawable-xhdpi/ws_switch_track_mtrl.png
new file mode 100644
index 0000000..1769795
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_track_mtrl.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_14w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_14w.png
new file mode 100644
index 0000000..8a00760
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_14w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_15w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_15w.png
new file mode 100644
index 0000000..64a0cab
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_15w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_16w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_16w.png
new file mode 100644
index 0000000..ce7369e
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_16w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_17w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_17w.png
new file mode 100644
index 0000000..398e3f2
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_17w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_18w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_18w.png
new file mode 100644
index 0000000..1cf40e2
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_18w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_track_mtrl.png b/wear/res/drawable-xxhdpi/ws_switch_track_mtrl.png
new file mode 100644
index 0000000..a329817
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_track_mtrl.png
Binary files differ
diff --git a/wear/res/layout-v23/ws_action_drawer_item_view.xml b/wear/res/layout-v23/ws_action_drawer_item_view.xml
new file mode 100644
index 0000000..fc84862
--- /dev/null
+++ b/wear/res/layout-v23/ws_action_drawer_item_view.xml
@@ -0,0 +1,41 @@
+<?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:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/ws_action_item_background"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/ws_action_drawer_item_icon"
+        android:layout_width="@dimen/ws_action_drawer_item_icon_size"
+        android:layout_height="@dimen/ws_action_drawer_item_icon_size"
+        android:layout_gravity="center_vertical"
+        android:background="@drawable/ws_action_item_icon_background"
+        android:tint="?android:attr/colorBackground"
+        android:padding="@dimen/ws_action_drawer_item_icon_padding"
+        android:scaleType="fitCenter"
+        tools:ignore="ContentDescription" />
+
+    <TextView
+        android:id="@+id/ws_action_drawer_item_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/WsWearableActionDrawerItemText"/>
+
+</LinearLayout>
diff --git a/wear/res/layout-v23/ws_action_drawer_peek_view.xml b/wear/res/layout-v23/ws_action_drawer_peek_view.xml
new file mode 100644
index 0000000..ce66d73
--- /dev/null
+++ b/wear/res/layout-v23/ws_action_drawer_peek_view.xml
@@ -0,0 +1,43 @@
+<?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:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/ws_action_drawer_peek_view_height"
+    android:layout_gravity="center"
+    android:gravity="center_vertical"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/ws_action_drawer_peek_top_padding">
+
+    <ImageView
+        android:id="@+id/ws_action_drawer_peek_action_icon"
+        android:layout_width="@dimen/ws_peek_view_icon_size"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:tint="?android:attr/colorForeground"
+        tools:ignore="ContentDescription" />
+
+    <ImageView
+        android:id="@+id/ws_action_drawer_expand_icon"
+        android:layout_width="@dimen/ws_peek_view_icon_size"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:layout_marginTop="@dimen/ws_action_drawer_expand_icon_top_margin"
+        android:src="@drawable/ws_ic_expand_less_white_22"
+        android:alpha="0.5"
+        android:tint="?android:attr/colorForeground"
+        tools:ignore="NegativeMargin,ContentDescription" />
+
+</LinearLayout>
diff --git a/wear/res/layout-v23/ws_action_drawer_title_view.xml b/wear/res/layout-v23/ws_action_drawer_title_view.xml
new file mode 100644
index 0000000..e5535c4
--- /dev/null
+++ b/wear/res/layout-v23/ws_action_drawer_title_view.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ws_action_drawer_title"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/ws_action_drawer_item_icon_size"
+    android:layout_gravity="center"
+    android:gravity="center"
+    style="@style/WsWearableActionDrawerTitleText" />
diff --git a/wear/res/layout-v23/ws_wearable_drawer_view.xml b/wear/res/layout-v23/ws_wearable_drawer_view.xml
new file mode 100644
index 0000000..c7c5907
--- /dev/null
+++ b/wear/res/layout-v23/ws_wearable_drawer_view.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <!-- Layout gravity for peek is set in code by WearableDrawerView. -->
+    <FrameLayout
+        android:id="@+id/ws_drawer_view_peek_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:id="@+id/ws_drawer_view_peek_icon"
+            android:layout_width="@dimen/ws_peek_view_icon_size"
+            android:layout_height="@dimen/ws_peek_view_icon_size"
+            android:layout_marginTop="@dimen/ws_peek_view_top_padding"
+            android:layout_marginBottom="@dimen/ws_peek_view_bottom_padding"
+            android:layout_gravity="center"
+            android:tint="?android:attr/colorForeground"
+            tools:ignore="ContentDescription" />
+
+    </FrameLayout>
+</merge>
diff --git a/wear/res/layout/ws_navigation_drawer_item_view.xml b/wear/res/layout/ws_navigation_drawer_item_view.xml
new file mode 100644
index 0000000..dcd5e41
--- /dev/null
+++ b/wear/res/layout/ws_navigation_drawer_item_view.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ImageView
+        android:id="@+id/ws_navigation_drawer_item_icon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_centerInParent="true"
+        android:scaleType="fitCenter"
+        tools:ignore="ContentDescription" />
+    <TextView
+        android:id="@+id/ws_navigation_drawer_item_text"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_below="@id/ws_navigation_drawer_item_icon"
+        android:layout_marginTop="6dp"
+        android:gravity="center_horizontal"
+        android:textColor="?android:attr/textColorPrimary"/>
+</RelativeLayout>
diff --git a/wear/res/layout/ws_navigation_drawer_view.xml b/wear/res/layout/ws_navigation_drawer_view.xml
new file mode 100644
index 0000000..ea4eced
--- /dev/null
+++ b/wear/res/layout/ws_navigation_drawer_view.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.
+-->
+<FrameLayout
+    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="match_parent" >
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/ws_navigation_drawer_view_pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <android.support.wear.widget.drawer.PageIndicatorView
+        android:id="@+id/ws_navigation_drawer_page_indicator"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:layout_marginBottom="@dimen/ws_peek_view_bottom_padding"
+        android:layout_gravity="bottom|center_horizontal"
+        app:wsPageIndicatorDotFadeWhenIdle="false" />
+
+</FrameLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_1_item.xml b/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
new file mode 100644
index 0000000..550d737
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <TextView
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_0"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_2_item.xml b/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
new file mode 100644
index 0000000..63dbbe6
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_2_items"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_2_items"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <TextView
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_1"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_3_item.xml b/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
new file mode 100644
index 0000000..209ec16
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_3_items"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_3_items"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <TextView
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_2"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
new file mode 100644
index 0000000..1b2c163
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
@@ -0,0 +1,58 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_4_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_4_items_horizontal"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_3"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_4_items_horizontal"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <TextView
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_3"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_5_item.xml b/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
new file mode 100644
index 0000000..699533d
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
@@ -0,0 +1,68 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_5_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle"/>
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_5_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_3"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_4"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <TextView
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_4"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_6_item.xml b/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
new file mode 100644
index 0000000..00625a1
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
@@ -0,0 +1,76 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_3"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_4"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_5"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_7_item.xml b/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
new file mode 100644
index 0000000..5daef22
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_3"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_4"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_5"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_6"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_peek_view.xml b/wear/res/layout/ws_single_page_nav_drawer_peek_view.xml
new file mode 100644
index 0000000..8b11554
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_peek_view.xml
@@ -0,0 +1,33 @@
+<?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:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/ws_peek_view_top_padding"
+    android:paddingBottom="@dimen/ws_peek_view_bottom_padding">
+
+    <ImageView
+        android:layout_width="@dimen/ws_peek_view_icon_size"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:src="@drawable/ws_ic_expand_more_white_22"
+        android:tint="?android:attr/colorForeground"
+        tools:ignore="ContentDescription"/>
+
+</LinearLayout>
diff --git a/wear/res/values-sw180dp-notround/dimens.xml b/wear/res/values-sw180dp-notround/dimens.xml
new file mode 100644
index 0000000..f6395d3
--- /dev/null
+++ b/wear/res/values-sw180dp-notround/dimens.xml
@@ -0,0 +1,25 @@
+<?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>
+
+    <dimen name="ws_action_drawer_item_top_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_bottom_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_right_margin">12dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_size">40dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_text_size">16sp</dimen>
+
+</resources>
diff --git a/wear/res/values-sw210dp-round/dimens.xml b/wear/res/values-sw210dp-round/dimens.xml
new file mode 100644
index 0000000..1a2581e
--- /dev/null
+++ b/wear/res/values-sw210dp-round/dimens.xml
@@ -0,0 +1,23 @@
+<?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>
+    <dimen name="ws_action_drawer_item_top_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_bottom_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_right_margin">12dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_size">40dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_text_size">16sp</dimen>
+</resources>
diff --git a/wear/res/values-sw210dp/dimens.xml b/wear/res/values-sw210dp/dimens.xml
new file mode 100644
index 0000000..8f69e0b
--- /dev/null
+++ b/wear/res/values-sw210dp/dimens.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>
+
+    <dimen name="ws_nav_drawer_text_size">14sp</dimen>
+    <dimen name="ws_nav_drawer_single_page_icon_size">32dp</dimen>
+    <dimen name="ws_nav_drawer_single_page_circle_radius">27dp</dimen>
+
+</resources>
diff --git a/wear/res/values-v20/styles.xml b/wear/res/values-v20/styles.xml
new file mode 100644
index 0000000..92613f2
--- /dev/null
+++ b/wear/res/values-v20/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>
+
+    <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>
+
+</resources>
diff --git a/wear/res/values-v23/styles.xml b/wear/res/values-v23/styles.xml
new file mode 100644
index 0000000..6bb1a51
--- /dev/null
+++ b/wear/res/values-v23/styles.xml
@@ -0,0 +1,28 @@
+<?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="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-v24/styles.xml b/wear/res/values-v24/styles.xml
new file mode 100644
index 0000000..a7f50fa
--- /dev/null
+++ b/wear/res/values-v24/styles.xml
@@ -0,0 +1,31 @@
+<?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="Widget.Wear.RoundSwitch"
+            parent="@android:style/Widget.Material.CompoundButton.Switch">
+        <item name="android:layout_width">@dimen/ws_switch_size</item>
+        <item name="android:layout_height">@dimen/ws_switch_size</item>
+        <item name="android:switchMinWidth">@dimen/ws_switch_size</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:thumb">@drawable/ws_switch_thumb_material_anim</item>
+        <item name="android:thumbTint">@color/ws_switch_thumb_color_material</item>
+        <item name="android:thumbTintMode">multiply</item>
+        <item name="android:track">@drawable/ws_switch_track_mtrl</item>
+        <item name="android:trackTint">@color/ws_switch_track_color_material</item>
+        <item name="android:background">@empty</item>
+        <item name="android:showText">false</item>
+    </style>
+</resources>
diff --git a/wear/res/values/arrays.xml b/wear/res/values/arrays.xml
new file mode 100644
index 0000000..3d7740d
--- /dev/null
+++ b/wear/res/values/arrays.xml
@@ -0,0 +1,26 @@
+<?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>
+    <!-- Default color scheme colors for CircularProgressLayout -->
+    <array name="circular_progress_layout_color_scheme_colors">
+        <item>@color/circular_progress_layout_red</item>
+        <item>@color/circular_progress_layout_yellow</item>
+        <item>@color/circular_progress_layout_green</item>
+        <item>@color/circular_progress_layout_blue</item>
+    </array>
+</resources>
\ No newline at end of file
diff --git a/wear/res/values/attrs.xml b/wear/res/values/attrs.xml
new file mode 100644
index 0000000..c8b47c7
--- /dev/null
+++ b/wear/res/values/attrs.xml
@@ -0,0 +1,186 @@
+<?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.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+     the documentation output. To suppress comment lines from the documentation
+     output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+    <!-- Attributes that can be used with any member view of
+        {@link android.support.wear.widget.BoxInsetLayout}.
+        These attributes are specified with the rest of a view's normal attributes
+        (such as {@link android.R.attr#background}, but will be parsed by the view's parent and
+        ignored by the child.
+        <p>The values defined here correspond to the base layout attribute
+        class {@link android.support.wear.widget.BoxInsetLayout.LayoutParams}. -->
+    <declare-styleable name="BoxInsetLayout_Layout">
+        <!-- The types of insets this view can force on its children. The view will respect the
+             defined values of other child attributes such as ones provided by
+             {@link android.view.ViewGroup.MarginLayoutParams}, but it will add an additional inset
+              as requested -->
+        <attr name="boxedEdges">
+            <!-- Default boxing setting. There are no insets forced on the child views. -->
+            <flag name="none" value="0x00" />
+            <!-- The view will force an inset on the left edge of the children. -->
+            <flag name="left" value="0x01" />
+            <!-- The view will force an inset on the top edge of the children. -->
+            <flag name="top" value="0x02" />
+            <!-- The view will force an inset on the right edge of the children. -->
+            <flag name="right" value="0x04" />
+            <!-- The view will force an inset on the bottom edge of the children. -->
+            <flag name="bottom" value="0x08" />
+            <!-- The view will force an inset on all of the edges of the children. -->
+            <flag name="all" value="0x0F" />
+        </attr>
+    </declare-styleable>
+
+    <!-- Attributes that can be used with any
+        {@link android.support.wear.widget.WearableRecyclerView}.
+        These attributes relate to the circular scrolling gesture of the view. -->
+    <declare-styleable name="WearableRecyclerView">
+        <!-- Taps within this radius and the radius of the screen are considered close enough to the
+            bezel to be candidates for circular scrolling. Expressed as a fraction of the screen's
+            radius. The default is the whole screen i.e 1.0f -->
+        <attr name="bezelWidth" format="fraction" />
+        <!-- Enables/disables circular touch scrolling for this view. When enabled, circular touch
+            gestures around the edge of the screen will cause the view to scroll up or down. -->
+        <attr name="circularScrollingGestureEnabled" format="boolean" />
+        <!-- Sets how many degrees the user has to rotate by to scroll through one screen height
+            when they are using the circular scrolling gesture. The default value equates 180
+            degrees scroll to one screen.-->
+        <attr name="scrollDegreesPerScreen" format="float" />
+    </declare-styleable>
+
+    <declare-styleable name="WearableDrawerView">
+        <!-- Sets which view should be shown in the drawer's peek state. -->
+        <attr name="peekView" format="reference" />
+        <!-- Sets which view should be used as the drawer's content. -->
+        <attr name="drawerContent" format="reference" />
+        <!-- Sets whether or not the drawer will automatically peek and un-peek.  -->
+        <attr name="enableAutoPeek" format="boolean" />
+        <attr name="android:background" />
+        <attr name="android:elevation" />
+    </declare-styleable>
+
+    <declare-styleable name="WearableActionDrawerView">
+        <!-- Adds a title to the drawer content. -->
+        <attr name="drawerTitle" format="string" />
+        <!-- Provides a Menu to the action drawer. -->
+        <attr name="actionMenu" format="reference" />
+        <!-- Shows the overflow icon in the peek view rather than the first action's icon. Also,
+             taps on the peek view will open the drawer rather than executing the first action. -->
+        <attr name="showOverflowInPeek" format="boolean" />
+    </declare-styleable>
+
+    <declare-styleable name="WearableNavigationDrawerView">
+        <!-- Sets the navigation style. Defaults to singlePage. -->
+        <attr name="navigationStyle" format="enum">
+            <!-- Single page navigation drawer style. This is the default drawer style. It is ideal
+                 for 1-5 items, but works with up to 7 items. If more than 7 items exist, then the
+                 drawer will be displayed as empty. -->
+            <enum name="singlePage" value="0" />
+            <!-- Multi-page navigation drawer style. Each item is on its own page. Useful when more
+                 than 7 items exist. -->
+            <enum name="multiPage" value="1" />
+        </attr>
+    </declare-styleable>
+
+    <declare-styleable name="PageIndicatorView">
+        <!-- Sets the distance between dots. -->
+        <attr name="wsPageIndicatorDotSpacing" format="dimension" />
+        <!-- Sets the radius of a dot when it is not selected. -->
+        <attr name="wsPageIndicatorDotRadius" format="dimension" />
+        <!-- Sets the radius of a dot when it is selected. -->
+        <attr name="wsPageIndicatorDotRadiusSelected" format="dimension" />
+        <!-- Sets the color of a dot when it is not selected. -->
+        <attr name="wsPageIndicatorDotColor" format="color" />
+        <!-- Sets the color of a dot when it is selected. -->
+        <attr name="wsPageIndicatorDotColorSelected" format="color" />
+        <!-- Sets whether the dots should fade out after inactivity. -->
+        <attr name="wsPageIndicatorDotFadeWhenIdle" format="boolean" />
+        <!-- Sets the delay between the pager arriving at an idle state, and the fade out animation
+             beginning, in milliseconds. -->
+        <attr name="wsPageIndicatorDotFadeOutDelay" format="integer" />
+        <!-- Sets the duration of the fade out animation. -->
+        <attr name="wsPageIndicatorDotFadeOutDuration" format="integer" />
+        <!-- Sets the duration of the fade in animation. -->
+        <attr name="wsPageIndicatorDotFadeInDuration" format="integer" />
+        <!-- Sets the shadow color. -->
+        <attr name="wsPageIndicatorDotShadowColor" format="color" />
+        <!-- Sets the shadow radius. -->
+        <attr name="wsPageIndicatorDotShadowRadius" format="dimension" />
+        <!-- Sets the horizontal shadow offset. -->
+        <attr name="wsPageIndicatorDotShadowDx" format="dimension" />
+        <!-- Sets the vertical shadow offset. -->
+        <attr name="wsPageIndicatorDotShadowDy" format="dimension" />
+    </declare-styleable>
+
+    <declare-styleable name="CircledImageView">
+        <attr name="android:src" />
+        <!-- Sets the color of the circle. -->
+        <attr name="background_color" format="color" />
+        <!-- Sets the radius of the circle. -->
+        <attr name="background_radius" format="dimension" />
+        <!-- Sets the radius of the circle while the circle is being pressed. -->
+        <attr name="background_radius_pressed" format="dimension" />
+        <!-- Sets the width of the border. -->
+        <attr name="background_border_width" format="dimension" />
+        <!-- Sets the color of the border. -->
+        <attr name="background_border_color" format="color" />
+        <!-- Sets the stroke cap for the border around the circle. -->
+        <attr name="background_border_cap" format="enum">
+            <enum name="butt" value="0" />
+            <enum name="round" value="1" />
+            <enum name="square" value="2" />
+        </attr>
+        <!-- Sets the padding between the edge of the circle and the start of the image. -->
+        <attr name="img_padding" format="dimension" />
+        <!-- Sets the width of the shadow. -->
+        <attr name="background_shadow_width" format="dimension" />
+        <!-- Sets the percentage of the circle which the image should occupy. -->
+        <attr name="img_circle_percentage" format="dimension" />
+        <!-- Sets the percentage of the circle which the image should should be offset
+             horizontally. -->
+        <attr name="img_horizontal_offset_percentage" format="dimension" />
+        <!-- Sets the tint color of the image. -->
+        <attr name="img_tint" format="color" />
+        <!-- Sets the radius of the circle to be a percentage of the largest dimension of the
+             view. -->
+        <attr name="background_radius_percent" format="fraction" />
+        <!-- Sets the circle radius when pressed. -->
+        <attr name="background_radius_pressed_percent" format="fraction" />
+        <!-- Sets which dimension to use if the image isn't square. -->
+        <attr name="clip_dimen" format="enum">
+            <enum name="none" value="0" />
+            <enum name="height" value="1" />
+            <enum name="width" value="2" />
+        </attr>
+    </declare-styleable>
+
+    <declare-styleable name="CircularProgressLayout">
+        <!-- Sets the color of the background circle. -->
+        <attr name="backgroundColor" format="color" />
+        <!-- Sets the stroke width of the progress indicator. -->
+        <attr name="strokeWidth" format="dimension" />
+        <!-- Sets the color scheme used by the progress indicator. This may be an array of colors or
+        a single color. If an array of colors is used, first color will be used for determinate
+        progress indicator, while the rest will be shown in order during indeterminate spinner. -->
+        <attr name="colorSchemeColors" format="reference|color" />
+        <!-- Sets if the progress should be shown as an indeterminate spinner. -->
+        <attr name="indeterminate" format="boolean" />
+    </declare-styleable>
+</resources>
diff --git a/wear/res/values/colors.xml b/wear/res/values/colors.xml
new file mode 100644
index 0000000..a44c7a0
--- /dev/null
+++ b/wear/res/values/colors.xml
@@ -0,0 +1,27 @@
+<?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>
+    <!-- From material color palette -->
+    <color name="circular_progress_layout_red">#FFF44336</color>
+    <color name="circular_progress_layout_yellow">#FFFFEB3B</color>
+    <color name="circular_progress_layout_green">#FF4CAF50</color>
+    <color name="circular_progress_layout_blue">#FF2196F3</color>
+
+    <!-- Default background color for CircularProgressLayout -->
+    <color name="circular_progress_layout_background_color">#00000000</color>
+</resources>
\ No newline at end of file
diff --git a/wear/res/values/dimens.xml b/wear/res/values/dimens.xml
new file mode 100644
index 0000000..26d6c0a
--- /dev/null
+++ b/wear/res/values/dimens.xml
@@ -0,0 +1,69 @@
+<?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>
+    <!-- Values for the WearableRecyclerView. -->
+    <dimen name="ws_wrv_curve_default_x_offset">24dp</dimen>
+
+    <!-- Values for WearableDrawerView. -->
+    <dimen name="ws_wearable_drawer_view_elevation">12dp</dimen>
+
+    <!-- Values for WearableActionDrawerView. -->
+    <item name="ws_action_drawer_item_first_item_top_padding" type="fraction">15%</item>
+    <item name="ws_action_drawer_item_last_item_bottom_padding" type="fraction">15%</item>
+    <item name="ws_action_drawer_item_left_padding" type="fraction">15%</item>
+    <item name="ws_action_drawer_item_right_padding" type="fraction">10%</item>
+    <dimen name="ws_action_drawer_item_top_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_bottom_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_right_margin">8dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_size">36dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_padding">7dp</dimen>
+    <dimen name="ws_action_drawer_item_text_size">14sp</dimen>
+    <dimen name="ws_action_drawer_peek_view_height">43dp</dimen>
+    <dimen name="ws_action_drawer_peek_top_padding">2dp</dimen>
+    <dimen name="ws_action_drawer_expand_icon_top_margin">-3dp</dimen>
+
+    <!-- Dimensions for the single page WearableNavigationDrawerView. -->
+    <dimen name="ws_nav_drawer_single_page_icon_size">28dp</dimen>
+    <dimen name="ws_nav_drawer_single_page_icon_padding">6dp</dimen>
+    <dimen name="ws_nav_drawer_single_page_circle_radius">24dp</dimen>
+    <dimen name="ws_nav_drawer_text_size">12sp</dimen>
+    <fraction name="ws_nav_drawer_text_margin">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_2_items">20%</fraction>
+    <fraction name="ws_nav_drawer_margin_3_items">16%</fraction>
+    <fraction name="ws_nav_drawer_margin_4_items_vertical">16.9%</fraction>
+    <fraction name="ws_nav_drawer_margin_4_items_horizontal">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_5_items_horizontal_outer_rows">27.4%</fraction>
+    <fraction name="ws_nav_drawer_margin_5_items_horizontal_middle_row">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_5_items_vertical">16.9%</fraction>
+    <fraction name="ws_nav_drawer_margin_6_items_horizontal">16%</fraction>
+    <fraction name="ws_nav_drawer_margin_6_items_vertical">26.8%</fraction>
+    <fraction name="ws_nav_drawer_margin_7_items_horizontal_outer_rows">27.4%</fraction>
+    <fraction name="ws_nav_drawer_margin_7_items_horizontal_middle_row">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_7_items_vertical">16.9%</fraction>
+
+    <dimen name="ws_peek_view_top_padding">8dp</dimen>
+    <dimen name="ws_peek_view_bottom_padding">8dp</dimen>
+    <dimen name="ws_peek_view_icon_size">22dp</dimen>
+
+    <!-- Maximum distance from edge to consider an edge drag. This should be the sum of the peek
+         view's top padding, bottom padding, and icon size. -->
+    <dimen name="ws_drawer_view_edge_size">38dp</dimen>
+    <!-- Dimensions for the wearable switch. -->
+    <dimen name="ws_switch_size">40dp</dimen>
+
+    <!-- Default stroke width for CircularProgressLayout -->
+    <dimen name="circular_progress_layout_stroke_width">4dp</dimen>
+</resources>
diff --git a/wear/res/values/ids.xml b/wear/res/values/ids.xml
new file mode 100644
index 0000000..8a7324e
--- /dev/null
+++ b/wear/res/values/ids.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>
+    <item name="ws_navigation_drawer_view_pager" type="id" />
+</resources>
diff --git a/wear/res/values/strings.xml b/wear/res/values/strings.xml
new file mode 100644
index 0000000..3bbb78c
--- /dev/null
+++ b/wear/res/values/strings.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>
+    <!-- String describing the navigation drawer which is read aloud when focused using accessibility gestures [CHAR LIMIT=35] -->
+    <string name="ws_navigation_drawer_content_description">Navigation drawer</string>
+
+    <!-- String describing the action drawer which is read aloud when focused using accessibility gestures [CHAR LIMIT=35] -->
+    <string name="ws_action_drawer_content_description">Action drawer</string>
+</resources>
diff --git a/wear/res/values/styles.xml b/wear/res/values/styles.xml
new file mode 100644
index 0000000..44ab0b0
--- /dev/null
+++ b/wear/res/values/styles.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.
+-->
+<resources>
+    <style name="WsSinglePageNavDrawerIconStyle">
+        <item name="android:layout_height">@dimen/ws_nav_drawer_single_page_icon_size</item>
+        <item name="android:layout_width">@dimen/ws_nav_drawer_single_page_icon_size</item>
+        <item name="android:padding">@dimen/ws_nav_drawer_single_page_icon_padding</item>
+        <item name="android:clipToPadding">false</item>
+        <item name="background_radius">@dimen/ws_nav_drawer_single_page_circle_radius</item>
+        <item name="background_color">#33ffffff</item>
+    </style>
+    <style name="WsSinglePageNavDrawerTextStyle">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
+        <item name="android:textSize">@dimen/ws_nav_drawer_text_size</item>
+        <item name="android:gravity">center</item>
+        <item name="android:maxLines">2</item>
+        <item name="layout_marginTopPercent">@fraction/ws_nav_drawer_text_margin</item>
+        <item name="layout_marginStartPercent">@fraction/ws_nav_drawer_text_margin</item>
+        <item name="layout_marginEndPercent">@fraction/ws_nav_drawer_text_margin</item>
+    </style>
+    <style name="Widget.Wear.WearableDrawerView" parent="">
+        <item name="android:elevation">@dimen/ws_wearable_drawer_view_elevation</item>
+        <item name="android:background">?android:attr/colorBackgroundFloating</item>
+    </style>
+</resources>
diff --git a/wear/src/android/support/wear/internal/widget/ResourcesUtil.java b/wear/src/android/support/wear/internal/widget/ResourcesUtil.java
new file mode 100644
index 0000000..f23a688
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/ResourcesUtil.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.internal.widget;
+
+import android.content.Context;
+import android.support.annotation.FractionRes;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+
+/**
+ * Utility methods to help with resource calculations.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public final class ResourcesUtil {
+
+    /**
+     * Returns the screen width in pixels.
+     */
+    public static int getScreenWidthPx(Context context) {
+        return context.getResources().getDisplayMetrics().widthPixels;
+    }
+
+    /**
+     * Returns the screen height in pixels.
+     */
+    public static int getScreenHeightPx(Context context) {
+        return context.getResources().getDisplayMetrics().heightPixels;
+    }
+
+    /**
+     * Returns the number of pixels equivalent to the percentage of {@code resId} to the current
+     * screen.
+     */
+    public static int getFractionOfScreenPx(Context context, int screenPx, @FractionRes int resId) {
+        float marginPercent = context.getResources().getFraction(resId, 1, 1);
+        return (int) (marginPercent * screenPx);
+    }
+
+    private ResourcesUtil() {}
+}
diff --git a/wear/src/android/support/wear/internal/widget/drawer/MultiPagePresenter.java b/wear/src/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
new file mode 100644
index 0000000..ad56048
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.internal.widget.drawer;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter;
+
+/**
+ * Provides a {@link WearableNavigationDrawerPresenter} implementation that is designed for the
+ * multi-page navigation drawer.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class MultiPagePresenter extends WearableNavigationDrawerPresenter {
+
+    private final Ui mUi;
+    private final WearableNavigationDrawerView mDrawer;
+    private final boolean mIsAccessibilityEnabled;
+    @Nullable private WearableNavigationDrawerAdapter mAdapter;
+
+    /**
+     * Controls the user interface of a multi-page {@link WearableNavigationDrawerView}.
+     */
+    public interface Ui {
+
+        /**
+         * Initializes the {@code Ui}.
+         */
+        void initialize(WearableNavigationDrawerView drawer,
+                WearableNavigationDrawerPresenter presenter);
+
+        /**
+         * Should notify the {@code NavigationPagerAdapter} that the underlying data has changed.
+         */
+        void notifyNavigationPagerAdapterDataChanged();
+
+        /**
+         * Should notify the Page Indicator that the underlying data has changed.
+         */
+        void notifyPageIndicatorDataChanged();
+
+        /**
+         * Associates the given {@code adapter} with this {@link Ui}.
+         */
+        void setNavigationPagerAdapter(WearableNavigationDrawerAdapter adapter);
+
+        /**
+         * Sets which item is selected and optionally smooth scrolls to it.
+         */
+        void setNavigationPagerSelectedItem(int index, boolean smoothScrollTo);
+    }
+
+    public MultiPagePresenter(WearableNavigationDrawerView drawer, Ui ui,
+            boolean isAccessibilityEnabled) {
+        if (drawer == null) {
+            throw new IllegalArgumentException("Received null drawer.");
+        }
+        if (ui == null) {
+            throw new IllegalArgumentException("Received null ui.");
+        }
+        mDrawer = drawer;
+        mUi = ui;
+        mUi.initialize(drawer, this);
+        mIsAccessibilityEnabled = isAccessibilityEnabled;
+    }
+
+    @Override
+    public void onDataSetChanged() {
+        mUi.notifyNavigationPagerAdapterDataChanged();
+        mUi.notifyPageIndicatorDataChanged();
+    }
+
+    @Override
+    public void onNewAdapter(WearableNavigationDrawerAdapter adapter) {
+        if (adapter == null) {
+            throw new IllegalArgumentException("Received null adapter.");
+        }
+        mAdapter = adapter;
+        mAdapter.setPresenter(this);
+        mUi.setNavigationPagerAdapter(adapter);
+    }
+
+    @Override
+    public void onSelected(int index) {
+        notifyItemSelectedListeners(index);
+    }
+
+    @Override
+    public void onSetCurrentItemRequested(int index, boolean smoothScrollTo) {
+        mUi.setNavigationPagerSelectedItem(index, smoothScrollTo);
+    }
+
+    @Override
+    public boolean onDrawerTapped() {
+        if (mDrawer.isOpened()) {
+            if (mIsAccessibilityEnabled) {
+                // When accessibility gestures are enabled, the user can't access a closed nav
+                // drawer, so peek it instead.
+                mDrawer.getController().peekDrawer();
+            } else {
+                mDrawer.getController().closeDrawer();
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java b/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java
new file mode 100644
index 0000000..9056845
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.internal.widget.drawer;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.wear.R;
+import android.support.wear.widget.drawer.PageIndicatorView;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Handles view logic for the multi page style {@link WearableNavigationDrawerView}.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class MultiPageUi implements MultiPagePresenter.Ui {
+
+    private static final String TAG = "MultiPageUi";
+
+    private WearableNavigationDrawerPresenter mPresenter;
+
+    @Nullable private ViewPager mNavigationPager;
+    @Nullable private PageIndicatorView mPageIndicatorView;
+
+    @Override
+    public void initialize(
+            WearableNavigationDrawerView drawer, WearableNavigationDrawerPresenter presenter) {
+        if (drawer == null) {
+            throw new IllegalArgumentException("Received null drawer.");
+        }
+        if (presenter == null) {
+            throw new IllegalArgumentException("Received null presenter.");
+        }
+        mPresenter = presenter;
+
+        LayoutInflater inflater = LayoutInflater.from(drawer.getContext());
+        final View content = inflater.inflate(R.layout.ws_navigation_drawer_view, drawer,
+                false /* attachToRoot */);
+
+        mNavigationPager =
+                (ViewPager) content
+                        .findViewById(R.id.ws_navigation_drawer_view_pager);
+        mPageIndicatorView =
+                (PageIndicatorView)
+                        content.findViewById(
+                                R.id.ws_navigation_drawer_page_indicator);
+
+        drawer.setDrawerContent(content);
+    }
+
+    @Override
+    public void setNavigationPagerAdapter(final WearableNavigationDrawerAdapter adapter) {
+        if (mNavigationPager == null || mPageIndicatorView == null) {
+            Log.w(TAG, "setNavigationPagerAdapter was called before initialize.");
+            return;
+        }
+
+        NavigationPagerAdapter navigationPagerAdapter = new NavigationPagerAdapter(adapter);
+        mNavigationPager.setAdapter(navigationPagerAdapter);
+
+        // Clear out the old page listeners and add a new one for this adapter.
+        mNavigationPager.clearOnPageChangeListeners();
+        mNavigationPager.addOnPageChangeListener(
+                new ViewPager.SimpleOnPageChangeListener() {
+                    @Override
+                    public void onPageSelected(int position) {
+                        mPresenter.onSelected(position);
+                    }
+                });
+        // PageIndicatorView adds itself as a page change listener here, so this must come after
+        // they are cleared.
+        mPageIndicatorView.setPager(mNavigationPager);
+    }
+
+    @Override
+    public void notifyPageIndicatorDataChanged() {
+        if (mPageIndicatorView != null) {
+            mPageIndicatorView.notifyDataSetChanged();
+        }
+    }
+
+    @Override
+    public void notifyNavigationPagerAdapterDataChanged() {
+        if (mNavigationPager != null) {
+            PagerAdapter adapter = mNavigationPager.getAdapter();
+            if (adapter != null) {
+                adapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    public void setNavigationPagerSelectedItem(int index, boolean smoothScrollTo) {
+        if (mNavigationPager != null) {
+            mNavigationPager.setCurrentItem(index, smoothScrollTo);
+        }
+    }
+
+    /**
+     * Adapter for {@link ViewPager} used in the multi-page UI.
+     */
+    private static final class NavigationPagerAdapter extends PagerAdapter {
+
+        private final WearableNavigationDrawerAdapter mAdapter;
+
+        NavigationPagerAdapter(WearableNavigationDrawerAdapter adapter) {
+            mAdapter = adapter;
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            // Do not attach to root in the inflate method. The view needs to returned at the end
+            // of this method. Attaching to root will cause view to point to container instead.
+            final View view =
+                    LayoutInflater.from(container.getContext())
+                            .inflate(R.layout.ws_navigation_drawer_item_view, container, false);
+            container.addView(view);
+            final ImageView iconView =
+                    (ImageView) view
+                            .findViewById(R.id.ws_navigation_drawer_item_icon);
+            final TextView textView =
+                    (TextView) view.findViewById(R.id.ws_navigation_drawer_item_text);
+            iconView.setImageDrawable(mAdapter.getItemDrawable(position));
+            textView.setText(mAdapter.getItemText(position));
+            return view;
+        }
+
+        @Override
+        public void destroyItem(ViewGroup container, int position, Object object) {
+            container.removeView((View) object);
+        }
+
+        @Override
+        public int getCount() {
+            return mAdapter.getCount();
+        }
+
+        @Override
+        public int getItemPosition(Object object) {
+            return POSITION_NONE;
+        }
+
+        @Override
+        public boolean isViewFromObject(View view, Object object) {
+            return view == object;
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/internal/widget/drawer/SinglePagePresenter.java b/wear/src/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
new file mode 100644
index 0000000..d90b589
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.internal.widget.drawer;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter;
+
+/**
+ * Provides a {@link WearableNavigationDrawerPresenter} implementation that is designed for the
+ * single page navigation drawer.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class SinglePagePresenter extends WearableNavigationDrawerPresenter {
+
+    private static final long DRAWER_CLOSE_DELAY_MS = 500;
+
+    private final Ui mUi;
+    private final boolean mIsAccessibilityEnabled;
+    @Nullable
+    private WearableNavigationDrawerAdapter mAdapter;
+    private int mCount = 0;
+    private int mSelected = 0;
+
+    /**
+     * Controls the user interface of a single-page {@link WearableNavigationDrawerView}.
+     */
+    public interface Ui {
+
+        /**
+         * Associates a {@link WearableNavigationDrawerPresenter} with this {@link Ui}.
+         */
+        void setPresenter(WearableNavigationDrawerPresenter presenter);
+
+        /**
+         * Initializes the {@link Ui} with {@code count} items.
+         */
+        void initialize(int count);
+
+        /**
+         * Sets the item's {@link Drawable} icon and its {@code contentDescription}.
+         */
+        void setIcon(int index, Drawable drawable, CharSequence contentDescription);
+
+        /**
+         * Displays {@code itemText} in a {@link android.widget.TextView} used to indicate which
+         * item is selected. When the {@link Ui} doesn't have space, it should show a {@link
+         * android.widget.Toast} if {@code showToastIfNoTextView} is {@code true}.
+         */
+        void setText(CharSequence itemText, boolean showToastIfNoTextView);
+
+        /**
+         * Indicates that the item at {@code index} has been selected.
+         */
+        void selectItem(int index);
+
+        /**
+         * Removes the indication that the item at {@code index} has been selected.
+         */
+        void deselectItem(int index);
+
+        /**
+         * Closes the drawer after the given delay.
+         */
+        void closeDrawerDelayed(long delayMs);
+
+        /**
+         * Peeks the {@link WearableNavigationDrawerView}.
+         */
+        void peekDrawer();
+    }
+
+    public SinglePagePresenter(Ui ui, boolean isAccessibilityEnabled) {
+        if (ui == null) {
+            throw new IllegalArgumentException("Received null ui.");
+        }
+
+        mIsAccessibilityEnabled = isAccessibilityEnabled;
+        mUi = ui;
+        mUi.setPresenter(this);
+        onDataSetChanged();
+    }
+
+    @Override
+    public void onDataSetChanged() {
+        if (mAdapter == null) {
+            return;
+        }
+        int count = mAdapter.getCount();
+        if (mCount != count) {
+            mCount = count;
+            mSelected = Math.min(mSelected, count - 1);
+            mUi.initialize(count);
+        }
+        for (int i = 0; i < count; i++) {
+            mUi.setIcon(i, mAdapter.getItemDrawable(i), mAdapter.getItemText(i));
+        }
+
+        mUi.setText(mAdapter.getItemText(mSelected), false /* showToastIfNoTextView */);
+        mUi.selectItem(mSelected);
+    }
+
+    @Override
+    public void onNewAdapter(WearableNavigationDrawerAdapter adapter) {
+        if (adapter == null) {
+            throw new IllegalArgumentException("Received null adapter.");
+        }
+        mAdapter = adapter;
+        mAdapter.setPresenter(this);
+        onDataSetChanged();
+    }
+
+    @Override
+    public void onSelected(int index) {
+        mUi.deselectItem(mSelected);
+        mUi.selectItem(index);
+        mSelected = index;
+        if (mIsAccessibilityEnabled) {
+            // When accessibility gestures are enabled, the user can't access a closed nav drawer,
+            // so peek it instead.
+            mUi.peekDrawer();
+        } else {
+            mUi.closeDrawerDelayed(DRAWER_CLOSE_DELAY_MS);
+        }
+
+        if (mAdapter != null) {
+            mUi.setText(mAdapter.getItemText(index), true /* showToastIfNoTextView */);
+        }
+        notifyItemSelectedListeners(index);
+    }
+
+    @Override
+    public void onSetCurrentItemRequested(int index, boolean smoothScrollTo) {
+        mUi.deselectItem(mSelected);
+        mUi.selectItem(index);
+        mSelected = index;
+        if (mAdapter != null) {
+            mUi.setText(mAdapter.getItemText(index), false /* showToastIfNoTextView */);
+        }
+        notifyItemSelectedListeners(index);
+    }
+
+    @Override
+    public boolean onDrawerTapped() {
+        // Do nothing. Use onSelected as our tap trigger so that we get which index was tapped on.
+        return false;
+    }
+}
diff --git a/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java b/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java
new file mode 100644
index 0000000..f3a4290
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.internal.widget.drawer;
+
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.IdRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.R;
+import android.support.wear.widget.CircledImageView;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Handles view logic for the single page style {@link WearableNavigationDrawerView}.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class SinglePageUi implements SinglePagePresenter.Ui {
+
+    @IdRes
+    private static final int[] SINGLE_PAGE_BUTTON_IDS =
+            new int[]{
+                    R.id.ws_nav_drawer_icon_0,
+                    R.id.ws_nav_drawer_icon_1,
+                    R.id.ws_nav_drawer_icon_2,
+                    R.id.ws_nav_drawer_icon_3,
+                    R.id.ws_nav_drawer_icon_4,
+                    R.id.ws_nav_drawer_icon_5,
+                    R.id.ws_nav_drawer_icon_6,
+            };
+
+    @LayoutRes
+    private static final int[] SINGLE_PAGE_LAYOUT_RES =
+            new int[]{
+                    0,
+                    R.layout.ws_single_page_nav_drawer_1_item,
+                    R.layout.ws_single_page_nav_drawer_2_item,
+                    R.layout.ws_single_page_nav_drawer_3_item,
+                    R.layout.ws_single_page_nav_drawer_4_item,
+                    R.layout.ws_single_page_nav_drawer_5_item,
+                    R.layout.ws_single_page_nav_drawer_6_item,
+                    R.layout.ws_single_page_nav_drawer_7_item,
+            };
+
+    private final WearableNavigationDrawerView mDrawer;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final Runnable mCloseDrawerRunnable =
+            new Runnable() {
+                @Override
+                public void run() {
+                    mDrawer.getController().closeDrawer();
+                }
+            };
+    private WearableNavigationDrawerPresenter mPresenter;
+    private CircledImageView[] mSinglePageImageViews;
+    /**
+     * Indicates currently selected item. {@code null} when the layout lacks space to display it.
+     */
+    @Nullable
+    private TextView mTextView;
+
+    public SinglePageUi(WearableNavigationDrawerView navigationDrawer) {
+        if (navigationDrawer == null) {
+            throw new IllegalArgumentException("Received null navigationDrawer.");
+        }
+        mDrawer = navigationDrawer;
+    }
+
+    @Override
+    public void setPresenter(WearableNavigationDrawerPresenter presenter) {
+        mPresenter = presenter;
+    }
+
+    @Override
+    public void initialize(int count) {
+        if (count < 0 || count >= SINGLE_PAGE_LAYOUT_RES.length
+                || SINGLE_PAGE_LAYOUT_RES[count] == 0) {
+            mDrawer.setDrawerContent(null);
+            return;
+        }
+
+        @LayoutRes int layoutRes = SINGLE_PAGE_LAYOUT_RES[count];
+        LayoutInflater inflater = LayoutInflater.from(mDrawer.getContext());
+        View content = inflater.inflate(layoutRes, mDrawer, false /* attachToRoot */);
+        final View peek =
+                inflater.inflate(
+                        R.layout.ws_single_page_nav_drawer_peek_view, mDrawer,
+                        false /* attachToRoot */);
+
+        mTextView = (TextView) content.findViewById(R.id.ws_nav_drawer_text);
+        mSinglePageImageViews = new CircledImageView[count];
+        for (int i = 0; i < count; i++) {
+            mSinglePageImageViews[i] = (CircledImageView) content
+                    .findViewById(SINGLE_PAGE_BUTTON_IDS[i]);
+            mSinglePageImageViews[i].setOnClickListener(new OnSelectedClickHandler(i, mPresenter));
+            mSinglePageImageViews[i].setCircleHidden(true);
+        }
+
+        mDrawer.setDrawerContent(content);
+        mDrawer.setPeekContent(peek);
+    }
+
+    @Override
+    public void setIcon(int index, Drawable drawable, CharSequence contentDescription) {
+        mSinglePageImageViews[index].setImageDrawable(drawable);
+        mSinglePageImageViews[index].setContentDescription(contentDescription);
+    }
+
+    @Override
+    public void setText(CharSequence itemText, boolean showToastIfNoTextView) {
+        if (mTextView != null) {
+            mTextView.setText(itemText);
+        } else if (showToastIfNoTextView) {
+            Toast toast = Toast.makeText(mDrawer.getContext(), itemText, Toast.LENGTH_SHORT);
+            toast.setGravity(Gravity.CENTER, 0 /* xOffset */, 0 /* yOffset */);
+            toast.show();
+        }
+    }
+
+    @Override
+    public void selectItem(int index) {
+        mSinglePageImageViews[index].setCircleHidden(false);
+    }
+
+    @Override
+    public void deselectItem(int index) {
+        mSinglePageImageViews[index].setCircleHidden(true);
+    }
+
+    @Override
+    public void closeDrawerDelayed(long delayMs) {
+        mMainThreadHandler.removeCallbacks(mCloseDrawerRunnable);
+        mMainThreadHandler.postDelayed(mCloseDrawerRunnable, delayMs);
+    }
+
+    @Override
+    public void peekDrawer() {
+        mDrawer.getController().peekDrawer();
+    }
+
+    /**
+     * Notifies the {@code presenter} that the item at the given {@code index} has been selected.
+     */
+    private static class OnSelectedClickHandler implements View.OnClickListener {
+
+        private final int mIndex;
+        private final WearableNavigationDrawerPresenter mPresenter;
+
+        private OnSelectedClickHandler(int index, WearableNavigationDrawerPresenter presenter) {
+            mIndex = index;
+            mPresenter = presenter;
+        }
+
+        @Override
+        public void onClick(View v) {
+            mPresenter.onSelected(mIndex);
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java b/wear/src/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
new file mode 100644
index 0000000..1c8c4fb
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.internal.widget.drawer;
+
+import android.support.annotation.MainThread;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView.OnItemSelectedListener;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Controls the behavior of this view where the behavior may differ between single and multi-page.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public abstract class WearableNavigationDrawerPresenter {
+
+    private final Set<OnItemSelectedListener> mOnItemSelectedListeners = new HashSet<>();
+
+    /**
+     * Indicates to the presenter that the underlying data has changed.
+     */
+    @MainThread
+    public abstract void onDataSetChanged();
+
+    /**
+     * Indicates to the presenter that the drawer has a new adapter.
+     */
+    @MainThread
+    public abstract void onNewAdapter(WearableNavigationDrawerAdapter adapter);
+
+    /**
+     * Indicates to the presenter that the user has selected an item.
+     */
+    @MainThread
+    public abstract void onSelected(int index);
+
+    /**
+     * Indicates to the presenter that the developer wishes to change which item is selected.
+     */
+    @MainThread
+    public abstract void onSetCurrentItemRequested(int index, boolean smoothScrollTo);
+
+    /**
+     * Indicates to the presenter that the user has tapped on the drawer.
+     *
+     * @return {@code true} if the touch event has been handled and should not propagate further.
+     */
+    @MainThread
+    public abstract boolean onDrawerTapped();
+
+    /**
+     * Indicates to the presenter that a new {@link OnItemSelectedListener} has been added.
+     */
+    @MainThread
+    public void onItemSelectedListenerAdded(OnItemSelectedListener listener) {
+        mOnItemSelectedListeners.add(listener);
+    }
+
+    /**
+     * Indicates to the presenter that an {@link OnItemSelectedListener} has been removed.
+     */
+    @MainThread
+    public void onItemSelectedListenerRemoved(OnItemSelectedListener listener) {
+        mOnItemSelectedListeners.remove(listener);
+    }
+
+    /**
+     * Notifies all listeners that the item at {@code selectedPos} has been selected.
+     */
+    @MainThread
+    void notifyItemSelectedListeners(int selectedPos) {
+        for (OnItemSelectedListener listener : mOnItemSelectedListeners) {
+            listener.onItemSelected(selectedPos);
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/internal/widget/drawer/package-info.java b/wear/src/android/support/wear/internal/widget/drawer/package-info.java
new file mode 100644
index 0000000..519428a
--- /dev/null
+++ b/wear/src/android/support/wear/internal/widget/drawer/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * @hide
+ */
+package android.support.wear.internal.widget.drawer;
diff --git a/wear/src/android/support/wear/widget/BezierSCurveInterpolator.java b/wear/src/android/support/wear/widget/BezierSCurveInterpolator.java
new file mode 100644
index 0000000..131bae8
--- /dev/null
+++ b/wear/src/android/support/wear/widget/BezierSCurveInterpolator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+
+/**
+ * Interpolator that uses a Bezier derived S shaped curve.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+class BezierSCurveInterpolator implements TimeInterpolator {
+
+    /**
+     * An instance of {@link BezierSCurveInterpolator}.
+     */
+    public static final BezierSCurveInterpolator INSTANCE = new BezierSCurveInterpolator();
+    /**
+     * Lookup table values. Generated using a Bezier curve from (0,0) to (1,1) with control points:
+     * P0 (0,0) P1 (0.4, 0) P2 (0.2, 1.0) P3 (1.0, 1.0)
+     *
+     * <p>Values sampled with x at regular intervals between 0 and 1.
+     */
+    private static final float[] VALUES = new float[]{
+            0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
+            0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f,
+            0.1186f, 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f,
+            0.3386f, 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f,
+            0.5933f, 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f,
+            0.7502f, 0.763f, 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f,
+            0.8588f, 0.8672f, 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f,
+            0.9232f, 0.9281f, 0.9328f, 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f,
+            0.9632f, 0.9662f, 0.9695f, 0.9722f, 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f,
+            0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f, 0.9944f, 0.9955f, 0.9964f, 0.9973f,
+            0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
+    };
+    private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
+
+    /**
+     * To avoid users of this class creating multiple copies needlessly, the constructor is
+     * private.
+     */
+    private BezierSCurveInterpolator() {
+    }
+
+    @Override
+    public float getInterpolation(float input) {
+        if (input >= 1.0f) {
+            return 1.0f;
+        }
+
+        if (input <= 0f) {
+            return 0f;
+        }
+
+        int position = Math.min((int) (input * (VALUES.length - 1)), VALUES.length - 2);
+
+        float quantized = position * STEP_SIZE;
+        float difference = input - quantized;
+        float weight = difference / STEP_SIZE;
+
+        return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
+    }
+}
diff --git a/wear/src/android/support/wear/widget/BoxInsetLayout.java b/wear/src/android/support/wear/widget/BoxInsetLayout.java
new file mode 100644
index 0000000..ba35f2c
--- /dev/null
+++ b/wear/src/android/support/wear/widget/BoxInsetLayout.java
@@ -0,0 +1,528 @@
+/*
+ * 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.
+ */
+package android.support.wear.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.StyleRes;
+import android.support.annotation.UiThread;
+import android.support.wear.R;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * BoxInsetLayout is a screen shape-aware ViewGroup that can box its children in the center
+ * square of a round screen by using the {@code boxedEdges} attribute. The values for this attribute
+ * specify the child's edges to be boxed in: {@code left|top|right|bottom} or {@code all}. The
+ * {@code boxedEdges} attribute is ignored on a device with a rectangular screen.
+ */
+@UiThread
+public class BoxInsetLayout extends ViewGroup {
+
+    private static final float FACTOR = 0.146467f; //(1 - sqrt(2)/2)/2
+    private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
+
+    private final int mScreenHeight;
+    private final int mScreenWidth;
+
+    private boolean mIsRound;
+    private Rect mForegroundPadding;
+    private Rect mInsets;
+    private Drawable mForegroundDrawable;
+
+    /**
+     * Simple constructor to use when creating a view from code.
+     *
+     * @param context The {@link Context} the view is running in, through which it can access
+     *                the current theme, resources, etc.
+     */
+    public BoxInsetLayout(@NonNull Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Constructor that is called when inflating a view from XML. This is called when a view is
+     * being constructed from an XML file, supplying attributes that were specified in the XML
+     * file. This version uses a default style of 0, so the only attribute values applied are those
+     * in the Context's Theme and the given AttributeSet.
+     * <p>
+     * <p>
+     * The method onFinishInflate() will be called after all children have been added.
+     *
+     * @param context The {@link Context} the view is running in, through which it can access
+     *                the current theme, resources, etc.
+     * @param attrs   The attributes of the XML tag that is inflating the view.
+     */
+    public BoxInsetLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * Perform inflation from XML and apply a class-specific base style from a theme attribute.
+     * This constructor allows subclasses to use their own base style when they are inflating.
+     *
+     * @param context  The {@link Context} the view is running in, through which it can
+     *                 access the current theme, resources, etc.
+     * @param attrs    The attributes of the XML tag that is inflating the view.
+     * @param defStyle An attribute in the current theme that contains a reference to a style
+     *                 resource that supplies default values for the view. Can be 0 to not look for
+     *                 defaults.
+     */
+    public BoxInsetLayout(@NonNull Context context, @Nullable AttributeSet attrs, @StyleRes int
+            defStyle) {
+        super(context, attrs, defStyle);
+        // make sure we have a foreground padding object
+        if (mForegroundPadding == null) {
+            mForegroundPadding = new Rect();
+        }
+        if (mInsets == null) {
+            mInsets = new Rect();
+        }
+        mScreenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
+        mScreenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        insets = super.onApplyWindowInsets(insets);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            final boolean round = insets.isRound();
+            if (round != mIsRound) {
+                mIsRound = round;
+                requestLayout();
+            }
+            mInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                    insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+        }
+        return insets;
+    }
+
+    @Override
+    public void setForeground(Drawable drawable) {
+        super.setForeground(drawable);
+        mForegroundDrawable = drawable;
+        if (mForegroundPadding == null) {
+            mForegroundPadding = new Rect();
+        }
+        if (mForegroundDrawable != null) {
+            drawable.getPadding(mForegroundPadding);
+        }
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new BoxInsetLayout.LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            requestApplyInsets();
+        } else {
+            mIsRound = getResources().getConfiguration().isScreenRound();
+            WindowInsets insets = getRootWindowInsets();
+            mInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                    insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int count = getChildCount();
+        // find max size
+        int maxWidth = 0;
+        int maxHeight = 0;
+        int childState = 0;
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                LayoutParams lp = (BoxInsetLayout.LayoutParams) child.getLayoutParams();
+                int marginLeft = 0;
+                int marginRight = 0;
+                int marginTop = 0;
+                int marginBottom = 0;
+                if (mIsRound) {
+                    // round screen, check boxed, don't use margins on boxed
+                    if ((lp.boxedEdges & LayoutParams.BOX_LEFT) == 0) {
+                        marginLeft = lp.leftMargin;
+                    }
+                    if ((lp.boxedEdges & LayoutParams.BOX_RIGHT) == 0) {
+                        marginRight = lp.rightMargin;
+                    }
+                    if ((lp.boxedEdges & LayoutParams.BOX_TOP) == 0) {
+                        marginTop = lp.topMargin;
+                    }
+                    if ((lp.boxedEdges & LayoutParams.BOX_BOTTOM) == 0) {
+                        marginBottom = lp.bottomMargin;
+                    }
+                } else {
+                    // rectangular, ignore boxed, use margins
+                    marginLeft = lp.leftMargin;
+                    marginTop = lp.topMargin;
+                    marginRight = lp.rightMargin;
+                    marginBottom = lp.bottomMargin;
+                }
+                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+                maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + marginLeft + marginRight);
+                maxHeight = Math.max(maxHeight,
+                        child.getMeasuredHeight() + marginTop + marginBottom);
+                childState = combineMeasuredStates(childState, child.getMeasuredState());
+            }
+        }
+        // Account for padding too
+        maxWidth += getPaddingLeft() + mForegroundPadding.left + getPaddingRight()
+                + mForegroundPadding.right;
+        maxHeight += getPaddingTop() + mForegroundPadding.top + getPaddingBottom()
+                + mForegroundPadding.bottom;
+
+        // Check against our minimum height and width
+        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+        // Check against our foreground's minimum height and width
+        if (mForegroundDrawable != null) {
+            maxHeight = Math.max(maxHeight, mForegroundDrawable.getMinimumHeight());
+            maxWidth = Math.max(maxWidth, mForegroundDrawable.getMinimumWidth());
+        }
+
+        int measuredWidth = resolveSizeAndState(maxWidth, widthMeasureSpec, childState);
+        int measuredHeight = resolveSizeAndState(maxHeight, heightMeasureSpec,
+                childState << MEASURED_HEIGHT_STATE_SHIFT);
+        setMeasuredDimension(measuredWidth, measuredHeight);
+
+        // determine boxed inset
+        int boxInset = calculateInset(measuredWidth, measuredHeight);
+        // adjust the the children measures, if necessary
+        for (int i = 0; i < count; i++) {
+            measureChild(widthMeasureSpec, heightMeasureSpec, boxInset, i);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        final int count = getChildCount();
+
+        final int parentLeft = getPaddingLeft() + mForegroundPadding.left;
+        final int parentRight = right - left - getPaddingRight() - mForegroundPadding.right;
+
+        final int parentTop = getPaddingTop() + mForegroundPadding.top;
+        final int parentBottom = bottom - top - getPaddingBottom() - mForegroundPadding.bottom;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+                final int width = child.getMeasuredWidth();
+                final int height = child.getMeasuredHeight();
+
+                int childLeft;
+                int childTop;
+
+                int gravity = lp.gravity;
+                if (gravity == -1) {
+                    gravity = DEFAULT_CHILD_GRAVITY;
+                }
+
+                final int layoutDirection = getLayoutDirection();
+                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+                final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+                int desiredInset = calculateInset(getMeasuredWidth(), getMeasuredHeight());
+
+                // If the child's width is match_parent then we can ignore gravity.
+                int leftChildMargin = calculateChildLeftMargin(lp, horizontalGravity, desiredInset);
+                int rightChildMargin = calculateChildRightMargin(lp, horizontalGravity,
+                        desiredInset);
+                if (lp.width == LayoutParams.MATCH_PARENT) {
+                    childLeft = parentLeft + leftChildMargin;
+                } else {
+                    switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                        case Gravity.CENTER_HORIZONTAL:
+                            childLeft = parentLeft + (parentRight - parentLeft - width) / 2
+                                    + leftChildMargin - rightChildMargin;
+                            break;
+                        case Gravity.RIGHT:
+                            childLeft = parentRight - width - rightChildMargin;
+                            break;
+                        case Gravity.LEFT:
+                        default:
+                            childLeft = parentLeft + leftChildMargin;
+                    }
+                }
+
+                // If the child's height is match_parent then we can ignore gravity.
+                int topChildMargin = calculateChildTopMargin(lp, verticalGravity, desiredInset);
+                int bottomChildMargin = calculateChildBottomMargin(lp, verticalGravity,
+                        desiredInset);
+                if (lp.height == LayoutParams.MATCH_PARENT) {
+                    childTop = parentTop + topChildMargin;
+                } else {
+                    switch (verticalGravity) {
+                        case Gravity.CENTER_VERTICAL:
+                            childTop = parentTop + (parentBottom - parentTop - height) / 2
+                                    + topChildMargin - bottomChildMargin;
+                            break;
+                        case Gravity.BOTTOM:
+                            childTop = parentBottom - height - bottomChildMargin;
+                            break;
+                        case Gravity.TOP:
+                        default:
+                            childTop = parentTop + topChildMargin;
+                    }
+                }
+                child.layout(childLeft, childTop, childLeft + width, childTop + height);
+            }
+        }
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams;
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return new LayoutParams(p);
+    }
+
+    private void measureChild(int widthMeasureSpec, int heightMeasureSpec, int desiredMinInset,
+            int i) {
+        final View child = getChildAt(i);
+        final LayoutParams childLayoutParams = (LayoutParams) child.getLayoutParams();
+
+        int gravity = childLayoutParams.gravity;
+        if (gravity == -1) {
+            gravity = DEFAULT_CHILD_GRAVITY;
+        }
+        final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+        final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+
+        int childWidthMeasureSpec;
+        int childHeightMeasureSpec;
+
+        int leftParentPadding = getPaddingLeft() + mForegroundPadding.left;
+        int rightParentPadding = getPaddingRight() + mForegroundPadding.right;
+        int topParentPadding = getPaddingTop() + mForegroundPadding.top;
+        int bottomParentPadding = getPaddingBottom() + mForegroundPadding.bottom;
+
+        // adjust width
+        int totalWidthMargin = leftParentPadding + rightParentPadding + calculateChildLeftMargin(
+                childLayoutParams, horizontalGravity, desiredMinInset) + calculateChildRightMargin(
+                childLayoutParams, horizontalGravity, desiredMinInset);
+
+        // adjust height
+        int totalHeightMargin = topParentPadding + bottomParentPadding + calculateChildTopMargin(
+                childLayoutParams, verticalGravity, desiredMinInset) + calculateChildBottomMargin(
+                childLayoutParams, verticalGravity, desiredMinInset);
+
+        childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, totalWidthMargin,
+                childLayoutParams.width);
+        childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, totalHeightMargin,
+                childLayoutParams.height);
+
+        int maxAllowedWidth = getMeasuredWidth() - totalWidthMargin;
+        int maxAllowedHeight = getMeasuredHeight() - totalHeightMargin;
+        if (child.getMeasuredWidth() > maxAllowedWidth
+                || child.getMeasuredHeight() > maxAllowedHeight) {
+            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+        }
+    }
+
+    private int calculateChildLeftMargin(LayoutParams lp, int horizontalGravity, int
+            desiredMinInset) {
+        if (mIsRound && ((lp.boxedEdges & LayoutParams.BOX_LEFT) != 0)) {
+            if (lp.width == LayoutParams.MATCH_PARENT || horizontalGravity == Gravity.LEFT) {
+                return lp.leftMargin + desiredMinInset;
+            }
+        }
+        return lp.leftMargin;
+    }
+
+    private int calculateChildRightMargin(LayoutParams lp, int horizontalGravity, int
+            desiredMinInset) {
+        if (mIsRound && ((lp.boxedEdges & LayoutParams.BOX_RIGHT) != 0)) {
+            if (lp.width == LayoutParams.MATCH_PARENT || horizontalGravity == Gravity.RIGHT) {
+                return lp.rightMargin + desiredMinInset;
+            }
+        }
+        return lp.rightMargin;
+    }
+
+    private int calculateChildTopMargin(LayoutParams lp, int verticalGravity, int desiredMinInset) {
+        if (mIsRound && ((lp.boxedEdges & LayoutParams.BOX_TOP) != 0)) {
+            if (lp.height == LayoutParams.MATCH_PARENT || verticalGravity == Gravity.TOP) {
+                return lp.topMargin + desiredMinInset;
+            }
+        }
+        return lp.topMargin;
+    }
+
+    private int calculateChildBottomMargin(LayoutParams lp, int verticalGravity, int
+            desiredMinInset) {
+        if (mIsRound && ((lp.boxedEdges & LayoutParams.BOX_BOTTOM) != 0)) {
+            if (lp.height == LayoutParams.MATCH_PARENT || verticalGravity == Gravity.BOTTOM) {
+                return lp.bottomMargin + desiredMinInset;
+            }
+        }
+        return lp.bottomMargin;
+    }
+
+    private int calculateInset(int measuredWidth, int measuredHeight) {
+        int rightEdge = Math.min(measuredWidth, mScreenWidth);
+        int bottomEdge = Math.min(measuredHeight, mScreenHeight);
+        return (int) (FACTOR * Math.max(rightEdge, bottomEdge));
+    }
+
+    /**
+     * Per-child layout information for layouts that support margins, gravity and boxedEdges.
+     * See {@link R.styleable#BoxInsetLayout_Layout BoxInsetLayout Layout Attributes} for a list
+     * of all child view attributes that this class supports.
+     *
+     * @attr ref R.styleable#BoxInsetLayout_Layout_boxedEdges
+     */
+    public static class LayoutParams extends FrameLayout.LayoutParams {
+
+        /** @hide */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @IntDef({BOX_NONE, BOX_LEFT, BOX_TOP, BOX_RIGHT, BOX_BOTTOM, BOX_ALL})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface BoxedEdges {}
+
+        /** Default boxing setting. There are no insets forced on the child views. */
+        public static final int BOX_NONE = 0x0;
+        /** The view will force an inset on the left edge of the children. */
+        public static final int BOX_LEFT = 0x01;
+        /** The view will force an inset on the top edge of the children. */
+        public static final int BOX_TOP = 0x02;
+        /** The view will force an inset on the right edge of the children. */
+        public static final int BOX_RIGHT = 0x04;
+        /** The view will force an inset on the bottom edge of the children. */
+        public static final int BOX_BOTTOM = 0x08;
+        /** The view will force an inset on all of the edges of the children. */
+        public static final int BOX_ALL = 0x0F;
+
+        /** Specifies the screen-specific insets for each of the child edges. */
+        @BoxedEdges
+        public int boxedEdges = BOX_NONE;
+
+        /**
+         * Creates a new set of layout parameters. The values are extracted from the supplied
+         * attributes set and context.
+         *
+         * @param context the application environment
+         * @param attrs the set of attributes from which to extract the layout parameters' values
+         */
+        @SuppressWarnings("ResourceType")
+        public LayoutParams(@NonNull Context context, @Nullable AttributeSet attrs) {
+            super(context, attrs);
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BoxInsetLayout_Layout,
+                    0, 0);
+            boxedEdges = a.getInt(R.styleable.BoxInsetLayout_Layout_boxedEdges, BOX_NONE);
+            a.recycle();
+        }
+
+        /**
+         * Creates a new set of layout parameters with the specified width and height.
+         *
+         * @param width the width, either {@link #MATCH_PARENT},
+         *              {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param height the height, either {@link #MATCH_PARENT},
+         *               {@link #WRAP_CONTENT} or a fixed size in pixelsy
+         */
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        /**
+         * Creates a new set of layout parameters with the specified width, height
+         * and gravity.
+         *
+         * @param width the width, either {@link #MATCH_PARENT},
+         *              {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param height the height, either {@link #MATCH_PARENT},
+         *               {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param gravity the gravity
+         *
+         * @see android.view.Gravity
+         */
+        public LayoutParams(int width, int height, int gravity) {
+            super(width, height, gravity);
+        }
+
+
+        public LayoutParams(int width, int height, int gravity, @BoxedEdges int boxed) {
+            super(width, height, gravity);
+            boxedEdges = boxed;
+        }
+
+        /**
+         * Copy constructor. Clones the width and height of the source.
+         *
+         * @param source The layout params to copy from.
+         */
+        public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        /**
+         * Copy constructor. Clones the width, height and margin values.
+         *
+         * @param source The layout params to copy from.
+         */
+        public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
+            super(source);
+        }
+
+        /**
+         * Copy constructor. Clones the width, height, margin values, and
+         * gravity of the source.
+         *
+         * @param source The layout params to copy from.
+         */
+        public LayoutParams(@NonNull FrameLayout.LayoutParams source) {
+            super(source);
+        }
+
+        /**
+         * Copy constructor. Clones the width, height, margin values, boxedEdges and
+         * gravity of the source.
+         *
+         * @param source The layout params to copy from.
+         */
+        public LayoutParams(@NonNull LayoutParams source) {
+            super(source);
+            this.boxedEdges = source.boxedEdges;
+            this.gravity = source.gravity;
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/CircledImageView.java b/wear/src/android/support/wear/widget/CircledImageView.java
new file mode 100644
index 0000000..e21a1ab
--- /dev/null
+++ b/wear/src/android/support/wear/widget/CircledImageView.java
@@ -0,0 +1,802 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.Px;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.R;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.Objects;
+
+/**
+ * An image view surrounded by a circle.
+ *
+ * @hide
+ */
+@TargetApi(Build.VERSION_CODES.M)
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class CircledImageView extends View {
+
+    private static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
+
+    private static final int SQUARE_DIMEN_NONE = 0;
+    private static final int SQUARE_DIMEN_HEIGHT = 1;
+    private static final int SQUARE_DIMEN_WIDTH = 2;
+
+    private final RectF mOval;
+    private final Paint mPaint;
+    private final OvalShadowPainter mShadowPainter;
+    private final float mInitialCircleRadius;
+    private final ProgressDrawable mIndeterminateDrawable;
+    private final Rect mIndeterminateBounds = new Rect();
+    private final Drawable.Callback mDrawableCallback =
+            new Drawable.Callback() {
+                @Override
+                public void invalidateDrawable(Drawable drawable) {
+                    invalidate();
+                }
+
+                @Override
+                public void scheduleDrawable(Drawable drawable, Runnable runnable, long l) {
+                    // Not needed.
+                }
+
+                @Override
+                public void unscheduleDrawable(Drawable drawable, Runnable runnable) {
+                    // Not needed.
+                }
+            };
+    private ColorStateList mCircleColor;
+    private Drawable mDrawable;
+    private float mCircleRadius;
+    private float mCircleRadiusPercent;
+    private float mCircleRadiusPressed;
+    private float mCircleRadiusPressedPercent;
+    private float mRadiusInset;
+    private int mCircleBorderColor;
+    private Paint.Cap mCircleBorderCap;
+    private float mCircleBorderWidth;
+    private boolean mCircleHidden = false;
+    private float mProgress = 1f;
+    private boolean mPressed = false;
+    private boolean mProgressIndeterminate;
+    private boolean mVisible;
+    private boolean mWindowVisible;
+    private long mColorChangeAnimationDurationMs = 0;
+    private float mImageCirclePercentage = 1f;
+    private float mImageHorizontalOffcenterPercentage = 0f;
+    private Integer mImageTint;
+    private Integer mSquareDimen;
+    private int mCurrentColor;
+
+    private final AnimatorUpdateListener mAnimationListener =
+            new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    int color = (int) animation.getAnimatedValue();
+                    if (color != CircledImageView.this.mCurrentColor) {
+                        CircledImageView.this.mCurrentColor = color;
+                        CircledImageView.this.invalidate();
+                    }
+                }
+            };
+
+    private ValueAnimator mColorAnimator;
+
+    public CircledImageView(Context context) {
+        this(context, null);
+    }
+
+    public CircledImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CircledImageView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CircledImageView);
+        mDrawable = a.getDrawable(R.styleable.CircledImageView_android_src);
+        if (mDrawable != null && mDrawable.getConstantState() != null) {
+            // The provided Drawable may be used elsewhere, so make a mutable clone before setTint()
+            // or setAlpha() is called on it.
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                mDrawable =
+                        mDrawable.getConstantState()
+                                .newDrawable(context.getResources(), context.getTheme());
+            } else {
+                mDrawable = mDrawable.getConstantState().newDrawable(context.getResources());
+            }
+            mDrawable = mDrawable.mutate();
+        }
+
+        mCircleColor = a.getColorStateList(R.styleable.CircledImageView_background_color);
+        if (mCircleColor == null) {
+            mCircleColor = ColorStateList.valueOf(android.R.color.darker_gray);
+        }
+
+        mCircleRadius = a.getDimension(R.styleable.CircledImageView_background_radius, 0);
+        mInitialCircleRadius = mCircleRadius;
+        mCircleRadiusPressed = a.getDimension(
+                R.styleable.CircledImageView_background_radius_pressed, mCircleRadius);
+        mCircleBorderColor = a
+                .getColor(R.styleable.CircledImageView_background_border_color, Color.BLACK);
+        mCircleBorderCap =
+                Paint.Cap.values()[a.getInt(R.styleable.CircledImageView_background_border_cap, 0)];
+        mCircleBorderWidth = a.getDimension(
+                R.styleable.CircledImageView_background_border_width, 0);
+
+        if (mCircleBorderWidth > 0) {
+            // The border arc is drawn from the middle of the arc - take that into account.
+            mRadiusInset += mCircleBorderWidth / 2;
+        }
+
+        float circlePadding = a.getDimension(R.styleable.CircledImageView_img_padding, 0);
+        if (circlePadding > 0) {
+            mRadiusInset += circlePadding;
+        }
+
+        mImageCirclePercentage = a
+                .getFloat(R.styleable.CircledImageView_img_circle_percentage, 0f);
+
+        mImageHorizontalOffcenterPercentage =
+                a.getFloat(R.styleable.CircledImageView_img_horizontal_offset_percentage, 0f);
+
+        if (a.hasValue(R.styleable.CircledImageView_img_tint)) {
+            mImageTint = a.getColor(R.styleable.CircledImageView_img_tint, 0);
+        }
+
+        if (a.hasValue(R.styleable.CircledImageView_clip_dimen)) {
+            mSquareDimen = a.getInt(R.styleable.CircledImageView_clip_dimen, SQUARE_DIMEN_NONE);
+        }
+
+        mCircleRadiusPercent =
+                a.getFraction(R.styleable.CircledImageView_background_radius_percent, 1, 1, 0f);
+
+        mCircleRadiusPressedPercent =
+                a.getFraction(
+                        R.styleable.CircledImageView_background_radius_pressed_percent, 1, 1,
+                        mCircleRadiusPercent);
+
+        float shadowWidth = a.getDimension(R.styleable.CircledImageView_background_shadow_width, 0);
+
+        a.recycle();
+
+        mOval = new RectF();
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mShadowPainter = new OvalShadowPainter(shadowWidth, 0, getCircleRadius(),
+                mCircleBorderWidth);
+
+        mIndeterminateDrawable = new ProgressDrawable();
+        // {@link #mDrawableCallback} must be retained as a member, as Drawable callback
+        // is held by weak reference, we must retain it for it to continue to be called.
+        mIndeterminateDrawable.setCallback(mDrawableCallback);
+
+        setWillNotDraw(false);
+
+        setColorForCurrentState();
+    }
+
+    /** Sets the circle to be hidden. */
+    public void setCircleHidden(boolean circleHidden) {
+        if (circleHidden != mCircleHidden) {
+            mCircleHidden = circleHidden;
+            invalidate();
+        }
+    }
+
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        return true;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int paddingLeft = getPaddingLeft();
+        int paddingTop = getPaddingTop();
+
+        float circleRadius = mPressed ? getCircleRadiusPressed() : getCircleRadius();
+
+        // Maybe draw the shadow
+        mShadowPainter.draw(canvas, getAlpha());
+        if (mCircleBorderWidth > 0) {
+            // First let's find the center of the view.
+            mOval.set(
+                    paddingLeft,
+                    paddingTop,
+                    getWidth() - getPaddingRight(),
+                    getHeight() - getPaddingBottom());
+            // Having the center, lets make the border meet the circle.
+            mOval.set(
+                    mOval.centerX() - circleRadius,
+                    mOval.centerY() - circleRadius,
+                    mOval.centerX() + circleRadius,
+                    mOval.centerY() + circleRadius);
+            mPaint.setColor(mCircleBorderColor);
+            // {@link #Paint.setAlpha} is a helper method that just sets the alpha portion of the
+            // color. {@link #Paint.setPaint} will clear any previously set alpha value.
+            mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha()));
+            mPaint.setStyle(Style.STROKE);
+            mPaint.setStrokeWidth(mCircleBorderWidth);
+            mPaint.setStrokeCap(mCircleBorderCap);
+
+            if (mProgressIndeterminate) {
+                mOval.roundOut(mIndeterminateBounds);
+                mIndeterminateDrawable.setBounds(mIndeterminateBounds);
+                mIndeterminateDrawable.setRingColor(mCircleBorderColor);
+                mIndeterminateDrawable.setRingWidth(mCircleBorderWidth);
+                mIndeterminateDrawable.draw(canvas);
+            } else {
+                canvas.drawArc(mOval, -90, 360 * mProgress, false, mPaint);
+            }
+        }
+        if (!mCircleHidden) {
+            mOval.set(
+                    paddingLeft,
+                    paddingTop,
+                    getWidth() - getPaddingRight(),
+                    getHeight() - getPaddingBottom());
+            // {@link #Paint.setAlpha} is a helper method that just sets the alpha portion of the
+            // color. {@link #Paint.setPaint} will clear any previously set alpha value.
+            mPaint.setColor(mCurrentColor);
+            mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha()));
+
+            mPaint.setStyle(Style.FILL);
+            float centerX = mOval.centerX();
+            float centerY = mOval.centerY();
+
+            canvas.drawCircle(centerX, centerY, circleRadius, mPaint);
+        }
+
+        if (mDrawable != null) {
+            mDrawable.setAlpha(Math.round(getAlpha() * 255));
+
+            if (mImageTint != null) {
+                mDrawable.setTint(mImageTint);
+            }
+            mDrawable.draw(canvas);
+        }
+
+        super.onDraw(canvas);
+    }
+
+    private void setColorForCurrentState() {
+        int newColor =
+                mCircleColor.getColorForState(getDrawableState(), mCircleColor.getDefaultColor());
+        if (mColorChangeAnimationDurationMs > 0) {
+            if (mColorAnimator != null) {
+                mColorAnimator.cancel();
+            } else {
+                mColorAnimator = new ValueAnimator();
+            }
+            mColorAnimator.setIntValues(new int[]{mCurrentColor, newColor});
+            mColorAnimator.setEvaluator(ARGB_EVALUATOR);
+            mColorAnimator.setDuration(mColorChangeAnimationDurationMs);
+            mColorAnimator.addUpdateListener(this.mAnimationListener);
+            mColorAnimator.start();
+        } else {
+            if (newColor != mCurrentColor) {
+                mCurrentColor = newColor;
+                invalidate();
+            }
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+        final float radius =
+                getCircleRadius()
+                        + mCircleBorderWidth
+                        + mShadowPainter.mShadowWidth * mShadowPainter.mShadowVisibility;
+        float desiredWidth = radius * 2;
+        float desiredHeight = radius * 2;
+
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+        int width;
+        int height;
+
+        if (widthMode == MeasureSpec.EXACTLY) {
+            width = widthSize;
+        } else if (widthMode == MeasureSpec.AT_MOST) {
+            width = (int) Math.min(desiredWidth, widthSize);
+        } else {
+            width = (int) desiredWidth;
+        }
+
+        if (heightMode == MeasureSpec.EXACTLY) {
+            height = heightSize;
+        } else if (heightMode == MeasureSpec.AT_MOST) {
+            height = (int) Math.min(desiredHeight, heightSize);
+        } else {
+            height = (int) desiredHeight;
+        }
+
+        if (mSquareDimen != null) {
+            switch (mSquareDimen) {
+                case SQUARE_DIMEN_HEIGHT:
+                    width = height;
+                    break;
+                case SQUARE_DIMEN_WIDTH:
+                    height = width;
+                    break;
+            }
+        }
+
+        super.onMeasure(
+                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mDrawable != null) {
+            // Retrieve the sizes of the drawable and the view.
+            final int nativeDrawableWidth = mDrawable.getIntrinsicWidth();
+            final int nativeDrawableHeight = mDrawable.getIntrinsicHeight();
+            final int viewWidth = getMeasuredWidth();
+            final int viewHeight = getMeasuredHeight();
+            final float imageCirclePercentage =
+                    mImageCirclePercentage > 0 ? mImageCirclePercentage : 1;
+
+            final float scaleFactor =
+                    Math.min(
+                            1f,
+                            Math.min(
+                                    (float) nativeDrawableWidth != 0
+                                            ? imageCirclePercentage * viewWidth
+                                            / nativeDrawableWidth
+                                            : 1,
+                                    (float) nativeDrawableHeight != 0
+                                            ? imageCirclePercentage * viewHeight
+                                            / nativeDrawableHeight
+                                            : 1));
+
+            // Scale the drawable down to fit the view, if needed.
+            final int drawableWidth = Math.round(scaleFactor * nativeDrawableWidth);
+            final int drawableHeight = Math.round(scaleFactor * nativeDrawableHeight);
+
+            // Center the drawable within the view.
+            final int drawableLeft =
+                    (viewWidth - drawableWidth) / 2
+                            + Math.round(mImageHorizontalOffcenterPercentage * drawableWidth);
+            final int drawableTop = (viewHeight - drawableHeight) / 2;
+
+            mDrawable.setBounds(
+                    drawableLeft, drawableTop, drawableLeft + drawableWidth,
+                    drawableTop + drawableHeight);
+        }
+
+        super.onLayout(changed, left, top, right, bottom);
+    }
+
+    /** Sets the image given a resource. */
+    public void setImageResource(int resId) {
+        setImageDrawable(resId == 0 ? null : getContext().getDrawable(resId));
+    }
+
+    /** Sets the size of the image based on a percentage in [0, 1]. */
+    public void setImageCirclePercentage(float percentage) {
+        float clamped = Math.max(0, Math.min(1, percentage));
+        if (clamped != mImageCirclePercentage) {
+            mImageCirclePercentage = clamped;
+            invalidate();
+        }
+    }
+
+    /** Sets the horizontal offset given a percentage in [0, 1]. */
+    public void setImageHorizontalOffcenterPercentage(float percentage) {
+        if (percentage != mImageHorizontalOffcenterPercentage) {
+            mImageHorizontalOffcenterPercentage = percentage;
+            invalidate();
+        }
+    }
+
+    /** Sets the tint. */
+    public void setImageTint(int tint) {
+        if (mImageTint == null || tint != mImageTint) {
+            mImageTint = tint;
+            invalidate();
+        }
+    }
+
+    /** Returns the circle radius. */
+    public float getCircleRadius() {
+        float radius = mCircleRadius;
+        if (mCircleRadius <= 0 && mCircleRadiusPercent > 0) {
+            radius = Math.max(getMeasuredHeight(), getMeasuredWidth()) * mCircleRadiusPercent;
+        }
+
+        return radius - mRadiusInset;
+    }
+
+    /** Sets the circle radius. */
+    public void setCircleRadius(float circleRadius) {
+        if (circleRadius != mCircleRadius) {
+            mCircleRadius = circleRadius;
+            mShadowPainter
+                    .setInnerCircleRadius(mPressed ? getCircleRadiusPressed() : getCircleRadius());
+            invalidate();
+        }
+    }
+
+    /** Gets the circle radius percent. */
+    public float getCircleRadiusPercent() {
+        return mCircleRadiusPercent;
+    }
+
+    /**
+     * Sets the radius of the circle to be a percentage of the largest dimension of the view.
+     *
+     * @param circleRadiusPercent A {@code float} from 0 to 1 representing the radius percentage.
+     */
+    public void setCircleRadiusPercent(float circleRadiusPercent) {
+        if (circleRadiusPercent != mCircleRadiusPercent) {
+            mCircleRadiusPercent = circleRadiusPercent;
+            mShadowPainter
+                    .setInnerCircleRadius(mPressed ? getCircleRadiusPressed() : getCircleRadius());
+            invalidate();
+        }
+    }
+
+    /** Gets the circle radius when pressed. */
+    public float getCircleRadiusPressed() {
+        float radius = mCircleRadiusPressed;
+
+        if (mCircleRadiusPressed <= 0 && mCircleRadiusPressedPercent > 0) {
+            radius =
+                    Math.max(getMeasuredHeight(), getMeasuredWidth()) * mCircleRadiusPressedPercent;
+        }
+
+        return radius - mRadiusInset;
+    }
+
+    /** Sets the circle radius when pressed. */
+    public void setCircleRadiusPressed(float circleRadiusPressed) {
+        if (circleRadiusPressed != mCircleRadiusPressed) {
+            mCircleRadiusPressed = circleRadiusPressed;
+            invalidate();
+        }
+    }
+
+    /** Gets the circle radius when pressed as a percent. */
+    public float getCircleRadiusPressedPercent() {
+        return mCircleRadiusPressedPercent;
+    }
+
+    /**
+     * Sets the radius of the circle to be a percentage of the largest dimension of the view when
+     * pressed.
+     *
+     * @param circleRadiusPressedPercent A {@code float} from 0 to 1 representing the radius
+     * percentage.
+     */
+    public void setCircleRadiusPressedPercent(float circleRadiusPressedPercent) {
+        if (circleRadiusPressedPercent != mCircleRadiusPressedPercent) {
+            mCircleRadiusPressedPercent = circleRadiusPressedPercent;
+            mShadowPainter
+                    .setInnerCircleRadius(mPressed ? getCircleRadiusPressed() : getCircleRadius());
+            invalidate();
+        }
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        setColorForCurrentState();
+    }
+
+    /** Sets the circle color. */
+    public void setCircleColor(int circleColor) {
+        setCircleColorStateList(ColorStateList.valueOf(circleColor));
+    }
+
+    /** Gets the circle color. */
+    public ColorStateList getCircleColorStateList() {
+        return mCircleColor;
+    }
+
+    /** Sets the circle color. */
+    public void setCircleColorStateList(ColorStateList circleColor) {
+        if (!Objects.equals(circleColor, mCircleColor)) {
+            mCircleColor = circleColor;
+            setColorForCurrentState();
+            invalidate();
+        }
+    }
+
+    /** Gets the default circle color. */
+    public int getDefaultCircleColor() {
+        return mCircleColor.getDefaultColor();
+    }
+
+    /**
+     * Show the circle border as an indeterminate progress spinner. The views circle border width
+     * and color must be set for this to have an effect.
+     *
+     * @param show true if the progress spinner is shown, false to hide it.
+     */
+    public void showIndeterminateProgress(boolean show) {
+        mProgressIndeterminate = show;
+        if (mIndeterminateDrawable != null) {
+            if (show && mVisible && mWindowVisible) {
+                mIndeterminateDrawable.startAnimation();
+            } else {
+                mIndeterminateDrawable.stopAnimation();
+            }
+        }
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mVisible = (visibility == View.VISIBLE);
+        showIndeterminateProgress(mProgressIndeterminate);
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mWindowVisible = (visibility == View.VISIBLE);
+        showIndeterminateProgress(mProgressIndeterminate);
+    }
+
+    /** Sets the progress. */
+    public void setProgress(float progress) {
+        if (progress != mProgress) {
+            mProgress = progress;
+            invalidate();
+        }
+    }
+
+    /**
+     * Set how much of the shadow should be shown.
+     *
+     * @param shadowVisibility Value between 0 and 1.
+     */
+    public void setShadowVisibility(float shadowVisibility) {
+        if (shadowVisibility != mShadowPainter.mShadowVisibility) {
+            mShadowPainter.setShadowVisibility(shadowVisibility);
+            invalidate();
+        }
+    }
+
+    public float getInitialCircleRadius() {
+        return mInitialCircleRadius;
+    }
+
+    public void setCircleBorderColor(int circleBorderColor) {
+        mCircleBorderColor = circleBorderColor;
+    }
+
+    /**
+     * Set the border around the circle.
+     *
+     * @param circleBorderWidth Width of the border around the circle.
+     */
+    public void setCircleBorderWidth(float circleBorderWidth) {
+        if (circleBorderWidth != mCircleBorderWidth) {
+            mCircleBorderWidth = circleBorderWidth;
+            mShadowPainter.setInnerCircleBorderWidth(circleBorderWidth);
+            invalidate();
+        }
+    }
+
+    /**
+     * Set the stroke cap for the border around the circle.
+     *
+     * @param circleBorderCap Stroke cap for the border around the circle.
+     */
+    public void setCircleBorderCap(Paint.Cap circleBorderCap) {
+        if (circleBorderCap != mCircleBorderCap) {
+            mCircleBorderCap = circleBorderCap;
+            invalidate();
+        }
+    }
+
+    @Override
+    public void setPressed(boolean pressed) {
+        super.setPressed(pressed);
+        if (pressed != mPressed) {
+            mPressed = pressed;
+            mShadowPainter
+                    .setInnerCircleRadius(mPressed ? getCircleRadiusPressed() : getCircleRadius());
+            invalidate();
+        }
+    }
+
+    @Override
+    public void setPadding(@Px int left, @Px int top, @Px int right, @Px int bottom) {
+        if (left != getPaddingLeft()
+                || top != getPaddingTop()
+                || right != getPaddingRight()
+                || bottom != getPaddingBottom()) {
+            mShadowPainter.setBounds(left, top, getWidth() - right, getHeight() - bottom);
+        }
+        super.setPadding(left, top, right, bottom);
+    }
+
+    @Override
+    public void onSizeChanged(int newWidth, int newHeight, int oldWidth, int oldHeight) {
+        if (newWidth != oldWidth || newHeight != oldHeight) {
+            mShadowPainter.setBounds(
+                    getPaddingLeft(),
+                    getPaddingTop(),
+                    newWidth - getPaddingRight(),
+                    newHeight - getPaddingBottom());
+        }
+    }
+
+    public Drawable getImageDrawable() {
+        return mDrawable;
+    }
+
+    /** Sets the image drawable. */
+    public void setImageDrawable(Drawable drawable) {
+        if (drawable != mDrawable) {
+            final Drawable existingDrawable = mDrawable;
+            mDrawable = drawable;
+            if (mDrawable != null && mDrawable.getConstantState() != null) {
+                // The provided Drawable may be used elsewhere, so make a mutable clone before
+                // setTint() or setAlpha() is called on it.
+                mDrawable =
+                        mDrawable
+                                .getConstantState()
+                                .newDrawable(getResources(), getContext().getTheme())
+                                .mutate();
+            }
+
+            final boolean skipLayout =
+                    drawable != null
+                            && existingDrawable != null
+                            && existingDrawable.getIntrinsicHeight() == drawable
+                            .getIntrinsicHeight()
+                            && existingDrawable.getIntrinsicWidth() == drawable.getIntrinsicWidth();
+
+            if (skipLayout) {
+                mDrawable.setBounds(existingDrawable.getBounds());
+            } else {
+                requestLayout();
+            }
+
+            invalidate();
+        }
+    }
+
+    /**
+     * @return the milliseconds duration of the transition animation when the color changes.
+     */
+    public long getColorChangeAnimationDuration() {
+        return mColorChangeAnimationDurationMs;
+    }
+
+    /**
+     * @param mColorChangeAnimationDurationMs the milliseconds duration of the color change
+     * animation. The color change animation will run if the color changes with {@link
+     * #setCircleColor} or as a result of the active state changing.
+     */
+    public void setColorChangeAnimationDuration(long mColorChangeAnimationDurationMs) {
+        this.mColorChangeAnimationDurationMs = mColorChangeAnimationDurationMs;
+    }
+
+    /**
+     * Helper class taking care of painting a shadow behind the displayed image. TODO(amad): Replace
+     * this with elevation, when moving to support/wearable?
+     */
+    private static class OvalShadowPainter {
+
+        private final int[] mShaderColors = new int[]{Color.BLACK, Color.TRANSPARENT};
+        private final float[] mShaderStops = new float[]{0.6f, 1f};
+        private final RectF mBounds = new RectF();
+        private final float mShadowWidth;
+        private final Paint mShadowPaint = new Paint();
+
+        private float mShadowRadius;
+        private float mShadowVisibility;
+        private float mInnerCircleRadius;
+        private float mInnerCircleBorderWidth;
+
+        OvalShadowPainter(
+                float shadowWidth,
+                float shadowVisibility,
+                float innerCircleRadius,
+                float innerCircleBorderWidth) {
+            mShadowWidth = shadowWidth;
+            mShadowVisibility = shadowVisibility;
+            mInnerCircleRadius = innerCircleRadius;
+            mInnerCircleBorderWidth = innerCircleBorderWidth;
+            mShadowRadius =
+                    mInnerCircleRadius + mInnerCircleBorderWidth + mShadowWidth * mShadowVisibility;
+            mShadowPaint.setColor(Color.BLACK);
+            mShadowPaint.setStyle(Style.FILL);
+            mShadowPaint.setAntiAlias(true);
+            updateRadialGradient();
+        }
+
+        void draw(Canvas canvas, float alpha) {
+            if (mShadowWidth > 0 && mShadowVisibility > 0) {
+                mShadowPaint.setAlpha(Math.round(mShadowPaint.getAlpha() * alpha));
+                canvas.drawCircle(mBounds.centerX(), mBounds.centerY(), mShadowRadius,
+                        mShadowPaint);
+            }
+        }
+
+        void setBounds(@Px int left, @Px int top, @Px int right, @Px int bottom) {
+            mBounds.set(left, top, right, bottom);
+            updateRadialGradient();
+        }
+
+        void setInnerCircleRadius(float newInnerCircleRadius) {
+            mInnerCircleRadius = newInnerCircleRadius;
+            updateRadialGradient();
+        }
+
+        void setInnerCircleBorderWidth(float newInnerCircleBorderWidth) {
+            mInnerCircleBorderWidth = newInnerCircleBorderWidth;
+            updateRadialGradient();
+        }
+
+        void setShadowVisibility(float newShadowVisibility) {
+            mShadowVisibility = newShadowVisibility;
+            updateRadialGradient();
+        }
+
+        private void updateRadialGradient() {
+            // Make the shadow start beyond the circled and possibly the border.
+            mShadowRadius =
+                    mInnerCircleRadius + mInnerCircleBorderWidth + mShadowWidth * mShadowVisibility;
+            // This may happen if the innerCircleRadius has not been correctly computed yet while
+            // the view has already been inflated, but not yet measured. In this case, if the view
+            // specifies the radius as a percentage of the screen width, then that evaluates to 0
+            // and will be corrected after measuring, through onSizeChanged().
+            if (mShadowRadius > 0) {
+                mShadowPaint.setShader(
+                        new RadialGradient(
+                                mBounds.centerX(),
+                                mBounds.centerY(),
+                                mShadowRadius,
+                                mShaderColors,
+                                mShaderStops,
+                                Shader.TileMode.MIRROR));
+            }
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/CircularProgressLayout.java b/wear/src/android/support/wear/widget/CircularProgressLayout.java
new file mode 100644
index 0000000..c317f28
--- /dev/null
+++ b/wear/src/android/support/wear/widget/CircularProgressLayout.java
@@ -0,0 +1,372 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.widget.CircularProgressDrawable;
+import android.support.wear.R;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * {@link CircularProgressLayout} adds a circular countdown timer behind the view it contains,
+ * typically used to automatically confirm an operation after a short delay has elapsed.
+ *
+ * <p>The developer can specify a countdown interval via {@link #setTotalTime(long)} and a listener
+ * via {@link #setOnTimerFinishedListener(OnTimerFinishedListener)} to be called when the time has
+ * elapsed after {@link #startTimer()} has been called. Tap action can be received via {@link
+ * #setOnClickListener(OnClickListener)} and can be used to cancel the timer via {@link
+ * #stopTimer()} method.
+ *
+ * <p>Alternatively, this layout can be used to show indeterminate progress by calling {@link
+ * #setIndeterminate(boolean)} method.
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+public class CircularProgressLayout extends FrameLayout {
+
+    /**
+     * Update interval for 60 fps.
+     */
+    private static final long DEFAULT_UPDATE_INTERVAL = 1000 / 60;
+
+    /**
+     * Starting rotation for the progress indicator. Geometric clockwise [0..360] degree range
+     * correspond to [0..1] range. 0.75 corresponds to 12 o'clock direction on a watch.
+     */
+    private static final float DEFAULT_ROTATION = 0.75f;
+
+    /**
+     * Used as background of this layout.
+     */
+    private CircularProgressDrawable mProgressDrawable;
+
+    /**
+     * Used to control this layout.
+     */
+    private CircularProgressLayoutController mController;
+
+    /**
+     * Angle for the progress to start from.
+     */
+    private float mStartingRotation = DEFAULT_ROTATION;
+
+    /**
+     * Duration of the timer in milliseconds.
+     */
+    private long mTotalTime;
+
+
+    /**
+     * Interface to implement for listening to {@link
+     * OnTimerFinishedListener#onTimerFinished(CircularProgressLayout)} event.
+     */
+    public interface OnTimerFinishedListener {
+
+        /**
+         * Called when the timer started by {@link #startTimer()} method finishes.
+         *
+         * @param layout {@link CircularProgressLayout} that calls this method.
+         */
+        void onTimerFinished(CircularProgressLayout layout);
+    }
+
+    public CircularProgressLayout(Context context) {
+        this(context, null);
+    }
+
+    public CircularProgressLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public CircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mProgressDrawable = new CircularProgressDrawable(context);
+        mProgressDrawable.setProgressRotation(DEFAULT_ROTATION);
+        mProgressDrawable.setStrokeCap(Paint.Cap.BUTT);
+        setBackground(mProgressDrawable);
+
+        // If a child view is added, make it center aligned so it fits in the progress drawable.
+        setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
+            @Override
+            public void onChildViewAdded(View parent, View child) {
+                // Ensure that child view is aligned in center
+                LayoutParams params = (LayoutParams) child.getLayoutParams();
+                params.gravity = Gravity.CENTER;
+                child.setLayoutParams(params);
+            }
+
+            @Override
+            public void onChildViewRemoved(View parent, View child) {
+
+            }
+        });
+
+        mController = new CircularProgressLayoutController(this);
+
+        Resources r = context.getResources();
+        TypedArray a = r.obtainAttributes(attrs, R.styleable.CircularProgressLayout);
+
+        if (a.getType(R.styleable.CircularProgressLayout_colorSchemeColors) == TypedValue
+                .TYPE_REFERENCE || !a.hasValue(
+                R.styleable.CircularProgressLayout_colorSchemeColors)) {
+            int arrayResId = a.getResourceId(R.styleable.CircularProgressLayout_colorSchemeColors,
+                    R.array.circular_progress_layout_color_scheme_colors);
+            setColorSchemeColors(getColorListFromResources(r, arrayResId));
+        } else {
+            setColorSchemeColors(a.getColor(R.styleable.CircularProgressLayout_colorSchemeColors,
+                    Color.BLACK));
+        }
+
+        setStrokeWidth(a.getDimensionPixelSize(R.styleable.CircularProgressLayout_strokeWidth,
+                r.getDimensionPixelSize(
+                        R.dimen.circular_progress_layout_stroke_width)));
+
+        setBackgroundColor(a.getColor(R.styleable.CircularProgressLayout_backgroundColor,
+                ContextCompat.getColor(context,
+                        R.color.circular_progress_layout_background_color)));
+
+        setIndeterminate(a.getBoolean(R.styleable.CircularProgressLayout_indeterminate, false));
+
+        a.recycle();
+    }
+
+    private int[] getColorListFromResources(Resources resources, int arrayResId) {
+        TypedArray colorArray = resources.obtainTypedArray(arrayResId);
+        int[] colors = new int[colorArray.length()];
+        for (int i = 0; i < colorArray.length(); i++) {
+            colors[i] = colorArray.getColor(i, 0);
+        }
+        colorArray.recycle();
+        return colors;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (getChildCount() != 0) {
+            View childView = getChildAt(0);
+            // Wrap the drawable around the child view
+            mProgressDrawable.setCenterRadius(
+                    Math.min(childView.getWidth(), childView.getHeight()) / 2f);
+        } else {
+            // Fill the bounds if no child view is present
+            mProgressDrawable.setCenterRadius(0f);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mController.reset();
+    }
+
+    /**
+     * Sets the background color of the {@link CircularProgressDrawable}, which is drawn as a circle
+     * inside the progress drawable. Colors are in ARGB format defined in {@link Color}.
+     *
+     * @param color an ARGB color
+     */
+    @Override
+    public void setBackgroundColor(@ColorInt int color) {
+        mProgressDrawable.setBackgroundColor(color);
+    }
+
+    /**
+     * Returns the background color of the {@link CircularProgressDrawable}.
+     *
+     * @return an ARGB color
+     */
+    @ColorInt
+    public int getBackgroundColor() {
+        return mProgressDrawable.getBackgroundColor();
+    }
+
+    /**
+     * Returns the {@link CircularProgressDrawable} used as background of this layout.
+     *
+     * @return {@link CircularProgressDrawable}
+     */
+    @NonNull
+    public CircularProgressDrawable getProgressDrawable() {
+        return mProgressDrawable;
+    }
+
+    /**
+     * Sets if progress should be shown as an indeterminate spinner.
+     *
+     * @param indeterminate {@code true} if indeterminate spinner should be shown, {@code false}
+     *                      otherwise.
+     */
+    public void setIndeterminate(boolean indeterminate) {
+        mController.setIndeterminate(indeterminate);
+    }
+
+    /**
+     * Returns if progress is showing as an indeterminate spinner.
+     *
+     * @return {@code true} if indeterminate spinner is shown, {@code false} otherwise.
+     */
+    public boolean isIndeterminate() {
+        return mController.isIndeterminate();
+    }
+
+    /**
+     * Sets the total time in milliseconds for the timer to countdown to. Calling this method while
+     * the timer is already running will not change the duration of the current timer.
+     *
+     * @param totalTime total time in milliseconds
+     */
+    public void setTotalTime(long totalTime) {
+        if (totalTime <= 0) {
+            throw new IllegalArgumentException("Total time should be greater than zero.");
+        }
+        mTotalTime = totalTime;
+    }
+
+    /**
+     * Returns the total time in milliseconds for the timer to countdown to.
+     *
+     * @return total time in milliseconds
+     */
+    public long getTotalTime() {
+        return mTotalTime;
+    }
+
+    /**
+     * Starts the timer countdown. Once the countdown is finished, if there is an {@link
+     * OnTimerFinishedListener} registered by {@link
+     * #setOnTimerFinishedListener(OnTimerFinishedListener)} method, its
+     * {@link OnTimerFinishedListener#onTimerFinished(CircularProgressLayout)} method is called. If
+     * this method is called while there is already a running timer, it will restart the timer.
+     */
+    public void startTimer() {
+        mController.startTimer(mTotalTime, DEFAULT_UPDATE_INTERVAL);
+        mProgressDrawable.setProgressRotation(mStartingRotation);
+    }
+
+    /**
+     * Stops the timer countdown. If there is no timer running, calling this method will not do
+     * anything.
+     */
+    public void stopTimer() {
+        mController.stopTimer();
+    }
+
+    /**
+     * Returns if the timer is running.
+     *
+     * @return {@code true} if the timer is running, {@code false} otherwise
+     */
+    public boolean isTimerRunning() {
+        return mController.isTimerRunning();
+    }
+
+    /**
+     * Sets the starting rotation for the progress drawable to start from. Default starting rotation
+     * is {@code 0.75} and it corresponds clockwise geometric 270 degrees (12 o'clock on a watch)
+     *
+     * @param rotation starting rotation from [0..1]
+     */
+    public void setStartingRotation(float rotation) {
+        mStartingRotation = rotation;
+    }
+
+    /**
+     * Returns the starting rotation of the progress drawable.
+     *
+     * @return starting rotation from [0..1]
+     */
+    public float getStartingRotation() {
+        return mStartingRotation;
+    }
+
+    /**
+     * Sets the stroke width of the progress drawable in pixels.
+     *
+     * @param strokeWidth stroke width in pixels
+     */
+    public void setStrokeWidth(float strokeWidth) {
+        mProgressDrawable.setStrokeWidth(strokeWidth);
+    }
+
+    /**
+     * Returns the stroke width of the progress drawable in pixels.
+     *
+     * @return stroke width in pixels
+     */
+    public float getStrokeWidth() {
+        return mProgressDrawable.getStrokeWidth();
+    }
+
+    /**
+     * Sets the color scheme colors of the progress drawable, which is equivalent to calling {@link
+     * CircularProgressDrawable#setColorSchemeColors(int...)} method on background drawable of this
+     * layout.
+     *
+     * @param colors list of ARGB colors
+     */
+    public void setColorSchemeColors(int... colors) {
+        mProgressDrawable.setColorSchemeColors(colors);
+    }
+
+    /**
+     * Returns the color scheme colors of the progress drawable
+     *
+     * @return list of ARGB colors
+     */
+    public int[] getColorSchemeColors() {
+        return mProgressDrawable.getColorSchemeColors();
+    }
+
+    /**
+     * Returns the {@link OnTimerFinishedListener} that is registered to this layout.
+     *
+     * @return registered {@link OnTimerFinishedListener}
+     */
+    @Nullable
+    public OnTimerFinishedListener getOnTimerFinishedListener() {
+        return mController.getOnTimerFinishedListener();
+    }
+
+    /**
+     * Sets the {@link OnTimerFinishedListener} to be notified when timer countdown is finished.
+     *
+     * @param listener {@link OnTimerFinishedListener} to be notified, or {@code null} to clear
+     */
+    public void setOnTimerFinishedListener(@Nullable OnTimerFinishedListener listener) {
+        mController.setOnTimerFinishedListener(listener);
+    }
+}
diff --git a/wear/src/android/support/wear/widget/CircularProgressLayoutController.java b/wear/src/android/support/wear/widget/CircularProgressLayoutController.java
new file mode 100644
index 0000000..4fd7156
--- /dev/null
+++ b/wear/src/android/support/wear/widget/CircularProgressLayoutController.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.os.CountDownTimer;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+
+/**
+ * Controller for {@link CircularProgressLayout}.
+ */
+class CircularProgressLayoutController {
+
+    private final CircularProgressLayout mLayout;
+    @VisibleForTesting CountDownTimer mTimer;
+    private boolean mIsIndeterminate;
+    private boolean mIsTimerRunning;
+
+    /**
+     * Called when the timer is finished.
+     */
+    @Nullable
+    private CircularProgressLayout.OnTimerFinishedListener mOnTimerFinishedListener;
+
+    CircularProgressLayoutController(CircularProgressLayout layout) {
+        mLayout = layout;
+    }
+
+    /**
+     * Returns the registered {@link CircularProgressLayout.OnTimerFinishedListener}.
+     */
+    @Nullable
+    public CircularProgressLayout.OnTimerFinishedListener getOnTimerFinishedListener() {
+        return mOnTimerFinishedListener;
+    }
+
+    /**
+     * Sets the {@link CircularProgressLayout.OnTimerFinishedListener} to be notified when timer
+     * countdown is finished.
+     */
+    public void setOnTimerFinishedListener(
+            @Nullable CircularProgressLayout.OnTimerFinishedListener listener) {
+        mOnTimerFinishedListener = listener;
+    }
+
+    /** Returns true if the progress is shown as an indeterminate spinner. */
+    boolean isIndeterminate() {
+        return mIsIndeterminate;
+    }
+
+    /** Returns true if timer is running. */
+    boolean isTimerRunning() {
+        return mIsTimerRunning;
+    }
+
+    /** Sets if the progress should be shown as an indeterminate spinner. */
+    void setIndeterminate(boolean indeterminate) {
+        if (mIsIndeterminate == indeterminate) {
+            return;
+        }
+        mIsIndeterminate = indeterminate;
+        if (mIsIndeterminate) {
+            if (mIsTimerRunning) {
+                stopTimer();
+            }
+            mLayout.getProgressDrawable().start();
+        } else {
+            mLayout.getProgressDrawable().stop();
+        }
+    }
+
+    void startTimer(long totalTime, long updateInterval) {
+        reset();
+        mIsTimerRunning = true;
+        mTimer = new CircularProgressTimer(totalTime, updateInterval);
+        mTimer.start();
+    }
+
+    void stopTimer() {
+        if (mIsTimerRunning) {
+            mTimer.cancel();
+            mIsTimerRunning = false;
+            mLayout.getProgressDrawable().setStartEndTrim(0f, 0f); // Reset the progress
+        }
+    }
+
+    /**
+     * Resets everything.
+     */
+    void reset() {
+        setIndeterminate(false); // If showing indeterminate progress, stop it
+        stopTimer(); // Stop the previous timer if there is one
+        mLayout.getProgressDrawable().setStartEndTrim(0f, 0f); // Reset the progress
+    }
+
+    /**
+     * Class to handle timing for {@link CircularProgressLayout}.
+     */
+    private class CircularProgressTimer extends CountDownTimer {
+
+        private final long mTotalTime;
+
+        CircularProgressTimer(long totalTime, long updateInterval) {
+            super(totalTime, updateInterval);
+            mTotalTime = totalTime;
+        }
+
+        @Override
+        public void onTick(long millisUntilFinished) {
+            mLayout.getProgressDrawable()
+                    .setStartEndTrim(0f, 1f - (float) millisUntilFinished / (float) mTotalTime);
+            mLayout.invalidate();
+        }
+
+        @Override
+        public void onFinish() {
+            mLayout.getProgressDrawable().setStartEndTrim(0f, 1f);
+            if (mOnTimerFinishedListener != null) {
+                mOnTimerFinishedListener.onTimerFinished(mLayout);
+            }
+            mIsTimerRunning = false;
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/CurvingLayoutCallback.java b/wear/src/android/support/wear/widget/CurvingLayoutCallback.java
new file mode 100644
index 0000000..275f1f8
--- /dev/null
+++ b/wear/src/android/support/wear/widget/CurvingLayoutCallback.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.content.Context;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.R;
+import android.view.View;
+
+/**
+ * An implementation of the {@link WearableLinearLayoutManager.LayoutCallback} aligning the children
+ * of the associated {@link WearableRecyclerView} along a pre-defined vertical curve.
+ */
+public class CurvingLayoutCallback extends WearableLinearLayoutManager.LayoutCallback {
+    private static final float EPSILON = 0.001f;
+
+    private final Path mCurvePath;
+    private final PathMeasure mPathMeasure;
+    private int mCurvePathHeight;
+    private int mXCurveOffset;
+    private float mPathLength;
+    private float mCurveBottom;
+    private float mCurveTop;
+    private float mLineGradient;
+    private final float[] mPathPoints = new float[2];
+    private final float[] mPathTangent = new float[2];
+    private final float[] mAnchorOffsetXY = new float[2];
+
+    private RecyclerView mParentView;
+    private boolean mIsScreenRound;
+    private int mLayoutWidth;
+    private int mLayoutHeight;
+
+    public CurvingLayoutCallback(Context context) {
+        mCurvePath = new Path();
+        mPathMeasure = new PathMeasure();
+        mIsScreenRound = context.getResources().getConfiguration().isScreenRound();
+        mXCurveOffset = context.getResources().getDimensionPixelSize(
+                R.dimen.ws_wrv_curve_default_x_offset);
+    }
+
+    @Override
+    public void onLayoutFinished(View child, RecyclerView parent) {
+        if (mParentView != parent || (mParentView != null && (
+                mParentView.getWidth() != parent.getWidth()
+                        || mParentView.getHeight() != parent.getHeight()))) {
+            mParentView = parent;
+            mLayoutWidth = mParentView.getWidth();
+            mLayoutHeight = mParentView.getHeight();
+        }
+        if (mIsScreenRound) {
+            maybeSetUpCircularInitialLayout(mLayoutWidth, mLayoutHeight);
+            mAnchorOffsetXY[0] = mXCurveOffset;
+            mAnchorOffsetXY[1] = child.getHeight() / 2.0f;
+            adjustAnchorOffsetXY(child, mAnchorOffsetXY);
+            float minCenter = -(float) child.getHeight() / 2;
+            float maxCenter = mLayoutHeight + (float) child.getHeight() / 2;
+            float range = maxCenter - minCenter;
+            float verticalAnchor = (float) child.getTop() + mAnchorOffsetXY[1];
+            float mYScrollProgress = (verticalAnchor + Math.abs(minCenter)) / range;
+
+            mPathMeasure.getPosTan(mYScrollProgress * mPathLength, mPathPoints, mPathTangent);
+
+            boolean topClusterRisk =
+                    Math.abs(mPathPoints[1] - mCurveBottom) < EPSILON
+                            && minCenter < mPathPoints[1];
+            boolean bottomClusterRisk =
+                    Math.abs(mPathPoints[1] - mCurveTop) < EPSILON
+                            && maxCenter > mPathPoints[1];
+            // Continue offsetting the child along the straight-line part of the curve, if it
+            // has not gone off the screen when it reached the end of the original curve.
+            if (topClusterRisk || bottomClusterRisk) {
+                mPathPoints[1] = verticalAnchor;
+                mPathPoints[0] = (Math.abs(verticalAnchor) * mLineGradient);
+            }
+
+            // Offset the View to match the provided anchor point.
+            int newLeft = (int) (mPathPoints[0] - mAnchorOffsetXY[0]);
+            child.offsetLeftAndRight(newLeft - child.getLeft());
+            float verticalTranslation = mPathPoints[1] - verticalAnchor;
+            child.setTranslationY(verticalTranslation);
+        } else {
+            child.setTranslationY(0);
+        }
+    }
+
+    /**
+     * Override this method if you wish to adjust the anchor coordinates for each child view
+     * during a layout pass. In the override set the new desired anchor coordinates in
+     * the provided array. The coordinates should be provided in relation to the child view.
+     *
+     * @param child          The child view to which the anchor coordinates will apply.
+     * @param anchorOffsetXY The anchor coordinates for the provided child view, by default set
+     *                       to a pre-defined constant on the horizontal axis and half of the
+     *                       child height on the vertical axis (vertical center).
+     */
+    public void adjustAnchorOffsetXY(View child, float[] anchorOffsetXY) {
+        return;
+    };
+
+    @VisibleForTesting
+    void setRound(boolean isScreenRound) {
+        mIsScreenRound = isScreenRound;
+    }
+
+    @VisibleForTesting
+    void setOffset(int offset) {
+        mXCurveOffset = offset;
+    }
+
+    /** Set up the initial layout for round screens. */
+    private void maybeSetUpCircularInitialLayout(int width, int height) {
+        // The values in this function are custom to the curve we use.
+        if (mCurvePathHeight != height) {
+            mCurvePathHeight = height;
+            mCurveBottom = -0.048f * height;
+            mCurveTop = 1.048f * height;
+            mLineGradient = 0.5f / 0.048f;
+            mCurvePath.reset();
+            mCurvePath.moveTo(0.5f * width, mCurveBottom);
+            mCurvePath.lineTo(0.34f * width, 0.075f * height);
+            mCurvePath.cubicTo(
+                    0.22f * width, 0.17f * height, 0.13f * width, 0.32f * height, 0.13f * width,
+                    height / 2);
+            mCurvePath.cubicTo(
+                    0.13f * width,
+                    0.68f * height,
+                    0.22f * width,
+                    0.83f * height,
+                    0.34f * width,
+                    0.925f * height);
+            mCurvePath.lineTo(width / 2, mCurveTop);
+            mPathMeasure.setPath(mCurvePath, false);
+            mPathLength = mPathMeasure.getLength();
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/ProgressDrawable.java b/wear/src/android/support/wear/widget/ProgressDrawable.java
new file mode 100644
index 0000000..08e8ec2
--- /dev/null
+++ b/wear/src/android/support/wear/widget/ProgressDrawable.java
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.util.Property;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * Drawable for showing an indeterminate progress indicator.
+ *
+ * @hide
+ */
+@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+@RestrictTo(Scope.LIBRARY_GROUP)
+class ProgressDrawable extends Drawable {
+
+    private static final Property<ProgressDrawable, Integer> LEVEL =
+            new Property<ProgressDrawable, Integer>(Integer.class, "level") {
+                @Override
+                public Integer get(ProgressDrawable drawable) {
+                    return drawable.getLevel();
+                }
+
+                @Override
+                public void set(ProgressDrawable drawable, Integer value) {
+                    drawable.setLevel(value);
+                    drawable.invalidateSelf();
+                }
+            };
+    /**
+     * Max level for a level drawable, as specified in developer docs for {@link Drawable}.
+     */
+    private static final int MAX_LEVEL = 10000;
+
+    /**
+     * How many different sections are there, five gives us the material style star. *
+     */
+    private static final int NUMBER_OF_SEGMENTS = 5;
+
+    private static final int LEVELS_PER_SEGMENT = MAX_LEVEL / NUMBER_OF_SEGMENTS;
+    private static final float STARTING_ANGLE = -90f;
+    private static final long ANIMATION_DURATION = 6000;
+    private static final int FULL_CIRCLE = 360;
+    private static final int MAX_SWEEP = 306;
+    private static final int CORRECTION_ANGLE = FULL_CIRCLE - MAX_SWEEP;
+    /**
+     * How far through each cycle does the bar stop growing and start shrinking, half way. *
+     */
+    private static final float GROW_SHRINK_RATIO = 0.5f;
+    // TODO: replace this with BakedBezierInterpolator when its available in support library.
+    private static final TimeInterpolator sInterpolator = BezierSCurveInterpolator.INSTANCE;
+
+    private final RectF mInnerCircleBounds = new RectF();
+    private final Paint mPaint = new Paint();
+    private final ObjectAnimator mAnimator;
+    private float mCircleBorderWidth;
+    private int mCircleBorderColor;
+
+    ProgressDrawable() {
+        mPaint.setAntiAlias(true);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mAnimator = ObjectAnimator.ofInt(this, LEVEL, 0, MAX_LEVEL);
+        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mAnimator.setRepeatMode(ValueAnimator.RESTART);
+        mAnimator.setDuration(ANIMATION_DURATION);
+        mAnimator.setInterpolator(new LinearInterpolator());
+    }
+
+    /**
+     * Returns the interpolation scalar (s) that satisfies the equation:
+     * {@code value = }lerp(a, b, s)
+     *
+     * <p>If {@code a == b}, then this function will return 0.
+     */
+    private static float lerpInv(float a, float b, float value) {
+        return a != b ? ((value - a) / (b - a)) : 0.0f;
+    }
+
+    public void setRingColor(int color) {
+        mCircleBorderColor = color;
+    }
+
+    public void setRingWidth(float width) {
+        mCircleBorderWidth = width;
+    }
+
+    public void startAnimation() {
+        if (!mAnimator.isStarted()) {
+            mAnimator.start();
+        }
+    }
+
+    public void stopAnimation() {
+        mAnimator.cancel();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.save();
+        mInnerCircleBounds.set(getBounds());
+        mInnerCircleBounds.inset(mCircleBorderWidth / 2.0f, mCircleBorderWidth / 2.0f);
+        mPaint.setStrokeWidth(mCircleBorderWidth);
+        mPaint.setColor(mCircleBorderColor);
+
+        int level = getLevel();
+        int currentSegment = level / LEVELS_PER_SEGMENT;
+        int offset = currentSegment * LEVELS_PER_SEGMENT;
+        float progress = (level - offset) / (float) LEVELS_PER_SEGMENT;
+
+        boolean growing = progress < GROW_SHRINK_RATIO;
+        float correctionAngle = CORRECTION_ANGLE * progress;
+
+        float sweepAngle;
+
+        if (growing) {
+            sweepAngle = MAX_SWEEP
+                    * sInterpolator.getInterpolation(lerpInv(0f, GROW_SHRINK_RATIO, progress));
+        } else {
+            sweepAngle =
+                    MAX_SWEEP
+                            * (1.0f - sInterpolator.getInterpolation(
+                            lerpInv(GROW_SHRINK_RATIO, 1.0f, progress)));
+        }
+
+        sweepAngle = Math.max(1, sweepAngle);
+
+        canvas.rotate(
+                level * (1.0f / MAX_LEVEL) * 2 * FULL_CIRCLE + STARTING_ANGLE + correctionAngle,
+                mInnerCircleBounds.centerX(),
+                mInnerCircleBounds.centerY());
+        canvas.drawArc(
+                mInnerCircleBounds, growing ? 0 : MAX_SWEEP - sweepAngle, sweepAngle, false,
+                mPaint);
+        canvas.restore();
+    }
+
+    @Override
+    public void setAlpha(int i) {
+        // Not supported.
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        // Not supported.
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        return true; // Changing the level of this drawable does change its appearance.
+    }
+}
diff --git a/wear/src/android/support/wear/widget/RoundedDrawable.java b/wear/src/android/support/wear/widget/RoundedDrawable.java
new file mode 100644
index 0000000..e2d7611
--- /dev/null
+++ b/wear/src/android/support/wear/widget/RoundedDrawable.java
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+package android.support.wear.widget;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Maintains and draws a drawable inside rounded rectangular bounds.
+ *
+ * <p>The drawable set by the {@link #setDrawable(Drawable)} method will be drawn within the rounded
+ * bounds specified by {@link #setBounds(Rect)} and {@link #setRadius(int)} when the
+ * {@link #draw(Canvas)} method is called.
+ *
+ * <p>By default, RoundedDrawable will apply padding to the drawable inside to fit the drawable into
+ * the rounded rectangle. If clipping is enabled by the {@link #setClipEnabled(boolean)} method, it
+ * will clip the drawable to a rounded rectangle instead of resizing it.
+ *
+ * <p>The {@link #setRadius(int)} method is used to specify the amount of border radius applied to
+ * the corners of inner drawable, regardless of whether or not the clipping is enabled, border
+ * radius will be applied to prevent overflowing of the drawable from specified rounded rectangular
+ * area.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class RoundedDrawable extends Drawable {
+
+    @VisibleForTesting
+    final Paint mPaint;
+    final Paint mBackgroundPaint;
+
+    @Nullable
+    private Drawable mDrawable;
+    private int mRadius; // Radius applied to corners in pixels
+    private boolean mIsClipEnabled;
+
+    // Used to avoid creating new Rect objects every time draw() is called
+    private final Rect mTmpBounds = new Rect();
+    private final RectF mTmpBoundsF = new RectF();
+
+    public RoundedDrawable() {
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mBackgroundPaint = new Paint();
+        mBackgroundPaint.setAntiAlias(true);
+        mBackgroundPaint.setColor(Color.TRANSPARENT);
+    }
+
+    /**
+     * Sets the drawable to be rendered.
+     *
+     * @param drawable {@link Drawable} to be rendered
+     */
+    public void setDrawable(@Nullable Drawable drawable) {
+        if (Objects.equals(mDrawable, drawable)) {
+            return;
+        }
+        mDrawable = drawable;
+        mPaint.setShader(null); // Clear the shader so it can be reinitialized
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the drawable that will be rendered.
+     *
+     * @return {@link Drawable} that will be rendered.
+     */
+    @Nullable
+    public Drawable getDrawable() {
+        return mDrawable;
+    }
+
+    /**
+     * Sets the background color of the rounded drawable.
+     *
+     * @param color an ARGB color
+     */
+    public void setBackgroundColor(@ColorInt int color) {
+        mBackgroundPaint.setColor(color);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the background color.
+     *
+     * @return an ARGB color
+     */
+    @ColorInt
+    public int getBackgroundColor() {
+        return mBackgroundPaint.getColor();
+    }
+
+    /**
+     * Sets whether the drawable inside should be clipped or resized to fit the rounded bounds. If
+     * the drawable is animated, don't set clipping to {@code true} as clipping on animated
+     * drawables is not supported.
+     *
+     * @param clipEnabled {@code true} if the drawable should be clipped, {@code false} if it
+     *                    should be resized.
+     */
+    public void setClipEnabled(boolean clipEnabled) {
+        mIsClipEnabled = clipEnabled;
+        if (!clipEnabled) {
+            mPaint.setShader(null); // Clear the shader so it's garbage collected
+        }
+        invalidateSelf();
+    }
+
+    /**
+     * Returns whether the drawable inside is clipped or resized to fit the rounded bounds.
+     *
+     * @return {@code true} if the drawable is clipped, {@code false} if it's resized.
+     */
+    public boolean isClipEnabled() {
+        return mIsClipEnabled;
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mTmpBounds.right = bounds.width();
+        mTmpBounds.bottom = bounds.height();
+        mTmpBoundsF.right = bounds.width();
+        mTmpBoundsF.bottom = bounds.height();
+        mPaint.setShader(null); // Clear the shader so it can be reinitialized
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        Rect bounds = getBounds();
+        if (mDrawable == null || bounds.isEmpty()) {
+            return;
+        }
+        canvas.save();
+        canvas.translate(bounds.left, bounds.top);
+        // mTmpBoundsF is bounds translated to (0,0) and converted to RectF as drawRoundRect
+        // requires.
+        canvas.drawRoundRect(mTmpBoundsF, (float) mRadius, (float) mRadius,
+                mBackgroundPaint);
+        if (mIsClipEnabled) {
+            // Update the shader if it's not present.
+            if (mPaint.getShader() == null) {
+                updateBitmapShader();
+            }
+            // Clip to a rounded rectangle
+            canvas.drawRoundRect(mTmpBoundsF, (float) mRadius, (float) mRadius, mPaint);
+        } else {
+            // Scale to fit the rounded rectangle
+            int minEdge = Math.min(bounds.width(), bounds.height());
+            int padding = (int) Math.ceil(
+                    Math.min(mRadius, minEdge / 2) * (1 - 1 / (float) Math.sqrt(2.0)));
+            mTmpBounds.inset(padding, padding);
+            mDrawable.setBounds(mTmpBounds);
+            mDrawable.draw(canvas);
+            mTmpBounds.inset(-padding, -padding);
+        }
+        canvas.restore();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        mBackgroundPaint.setAlpha(alpha);
+    }
+
+    @Override
+    public int getAlpha() {
+        return mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mPaint.setColorFilter(cf);
+    }
+
+    /**
+     * Sets the border radius to be applied when rendering the drawable in pixels.
+     *
+     * @param radius radius in pixels
+     */
+    public void setRadius(int radius) {
+        mRadius = radius;
+    }
+
+    /**
+     * Returns the border radius applied when rendering the drawable in pixels.
+     *
+     * @return radius in pixels
+     */
+    public int getRadius() {
+        return mRadius;
+    }
+
+    /**
+     * Updates the shader of the paint. To avoid scaling and creation of a BitmapShader every time,
+     * this method should be called only if the drawable or the bounds has changed.
+     */
+    private void updateBitmapShader() {
+        if (mDrawable == null) {
+            return;
+        }
+        Rect bounds = getBounds();
+        if (!bounds.isEmpty()) {
+            Bitmap bitmap = drawableToBitmap(mDrawable, bounds.width(), bounds.height());
+
+            Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mPaint.setShader(shader);
+        }
+    }
+
+    /** Converts a drawable to a bitmap of specified width and height. */
+    private Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, width, height);
+        drawable.draw(canvas);
+        return bitmap;
+    }
+}
diff --git a/wear/src/android/support/wear/widget/ScrollManager.java b/wear/src/android/support/wear/widget/ScrollManager.java
new file mode 100644
index 0000000..8155f62
--- /dev/null
+++ b/wear/src/android/support/wear/widget/ScrollManager.java
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.annotation.RestrictTo;
+import android.support.v7.widget.RecyclerView;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+/**
+ * Class adding circular scrolling support to {@link WearableRecyclerView}.
+ *
+ * @hide
+ */
+@TargetApi(Build.VERSION_CODES.M)
+@RestrictTo(LIBRARY_GROUP)
+class ScrollManager {
+    // One second in milliseconds.
+    private static final int ONE_SEC_IN_MS = 1000;
+    private static final float VELOCITY_MULTIPLIER = 1.5f;
+    private static final float FLING_EDGE_RATIO = 1.5f;
+
+    /**
+     * Taps beyond this radius fraction are considered close enough to the bezel to be candidates
+     * for circular scrolling.
+     */
+    private float mMinRadiusFraction = 0.0f;
+
+    private float mMinRadiusFractionSquared = mMinRadiusFraction * mMinRadiusFraction;
+
+    /** How many degrees you have to drag along the bezel to scroll one screen height. */
+    private float mScrollDegreesPerScreen = 180;
+
+    private float mScrollRadiansPerScreen = (float) Math.toRadians(mScrollDegreesPerScreen);
+
+    /** Radius of screen in pixels, ignoring insets, if any. */
+    private float mScreenRadiusPx;
+
+    private float mScreenRadiusPxSquared;
+
+    /** How many pixels to scroll for each radian of bezel scrolling. */
+    private float mScrollPixelsPerRadian;
+
+    /** Whether an {@link MotionEvent#ACTION_DOWN} was received near the bezel. */
+    private boolean mDown;
+
+    /**
+     * Whether the user tapped near the bezel and dragged approximately tangentially to initiate
+     * bezel scrolling.
+     */
+    private boolean mScrolling;
+    /**
+     * The angle of the user's finger relative to the center of the screen for the last {@link
+     * MotionEvent} during bezel scrolling.
+     */
+    private float mLastAngleRadians;
+
+    private RecyclerView mRecyclerView;
+    VelocityTracker mVelocityTracker;
+
+    /** Should be called after the window is attached to the view. */
+    void setRecyclerView(RecyclerView recyclerView, int width, int height) {
+        mRecyclerView = recyclerView;
+        mScreenRadiusPx = Math.max(width, height) / 2f;
+        mScreenRadiusPxSquared = mScreenRadiusPx * mScreenRadiusPx;
+        mScrollPixelsPerRadian = height / mScrollRadiansPerScreen;
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    /** Remove the binding with a {@link RecyclerView} */
+    void clearRecyclerView() {
+        mRecyclerView = null;
+    }
+
+    /**
+     * Method dealing with touch events intercepted from the attached {@link RecyclerView}.
+     *
+     * @param event the intercepted touch event.
+     * @return true if the even was handled, false otherwise.
+     */
+    boolean onTouchEvent(MotionEvent event) {
+        float deltaX = event.getRawX() - mScreenRadiusPx;
+        float deltaY = event.getRawY() - mScreenRadiusPx;
+        float radiusSquared = deltaX * deltaX + deltaY * deltaY;
+        final MotionEvent vtev = MotionEvent.obtain(event);
+        mVelocityTracker.addMovement(vtev);
+        vtev.recycle();
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                if (radiusSquared / mScreenRadiusPxSquared > mMinRadiusFractionSquared) {
+                    mDown = true;
+                    return true; // Consume the event.
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                if (mScrolling) {
+                    float angleRadians = (float) Math.atan2(deltaY, deltaX);
+                    float deltaRadians = angleRadians - mLastAngleRadians;
+                    deltaRadians = normalizeAngleRadians(deltaRadians);
+                    int scrollPixels = Math.round(deltaRadians * mScrollPixelsPerRadian);
+                    if (scrollPixels != 0) {
+                        mRecyclerView.scrollBy(0 /* x */, scrollPixels /* y */);
+                        // Recompute deltaRadians in terms of rounded scrollPixels.
+                        deltaRadians = scrollPixels / mScrollPixelsPerRadian;
+                        mLastAngleRadians += deltaRadians;
+                        mLastAngleRadians = normalizeAngleRadians(mLastAngleRadians);
+                    }
+                    // Always consume the event so that we never break the circular scrolling
+                    // gesture.
+                    return true;
+                }
+
+                if (mDown) {
+                    float deltaXFromCenter = event.getRawX() - mScreenRadiusPx;
+                    float deltaYFromCenter = event.getRawY() - mScreenRadiusPx;
+                    float distFromCenter = (float) Math.hypot(deltaXFromCenter, deltaYFromCenter);
+                    if (distFromCenter != 0) {
+                        deltaXFromCenter /= distFromCenter;
+                        deltaYFromCenter /= distFromCenter;
+
+                        mScrolling = true;
+                        mRecyclerView.invalidate();
+                        mLastAngleRadians = (float) Math.atan2(deltaYFromCenter, deltaXFromCenter);
+                        return true; // Consume the event.
+                    }
+                } else {
+                    // Double check we're not missing an event we should really be handling.
+                    if (radiusSquared / mScreenRadiusPxSquared > mMinRadiusFractionSquared) {
+                        mDown = true;
+                        return true; // Consume the event.
+                    }
+                }
+                break;
+
+            case MotionEvent.ACTION_UP:
+                mDown = false;
+                mScrolling = false;
+                mVelocityTracker.computeCurrentVelocity(ONE_SEC_IN_MS,
+                        mRecyclerView.getMaxFlingVelocity());
+                int velocityY = (int) mVelocityTracker.getYVelocity();
+                if (event.getX() < FLING_EDGE_RATIO * mScreenRadiusPx) {
+                    velocityY = -velocityY;
+                }
+                mVelocityTracker.clear();
+                if (Math.abs(velocityY) > mRecyclerView.getMinFlingVelocity()) {
+                    return mRecyclerView.fling(0, (int) (VELOCITY_MULTIPLIER * velocityY));
+                }
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                if (mDown) {
+                    mDown = false;
+                    mScrolling = false;
+                    mRecyclerView.invalidate();
+                    return true; // Consume the event.
+                }
+                break;
+        }
+
+        return false;
+    }
+
+    /**
+     * Normalizes an angle to be in the range [-pi, pi] by adding or subtracting 2*pi if necessary.
+     *
+     * @param angleRadians an angle in radians. Must be no more than 2*pi out of normal range.
+     * @return an angle in radians in the range [-pi, pi]
+     */
+    private static float normalizeAngleRadians(float angleRadians) {
+        if (angleRadians < -Math.PI) {
+            angleRadians = (float) (angleRadians + Math.PI * 2);
+        }
+        if (angleRadians > Math.PI) {
+            angleRadians = (float) (angleRadians - Math.PI * 2);
+        }
+        return angleRadians;
+    }
+
+    /**
+     * Set how many degrees you have to drag along the bezel to scroll one screen height.
+     *
+     * @param degreesPerScreen desired degrees per screen scroll.
+     */
+    public void setScrollDegreesPerScreen(float degreesPerScreen) {
+        mScrollDegreesPerScreen = degreesPerScreen;
+        mScrollRadiansPerScreen = (float) Math.toRadians(mScrollDegreesPerScreen);
+    }
+
+    /**
+     * Sets the width of a virtual 'bezel' close to the edge of the screen within which taps can be
+     * recognized as belonging to a rotary scrolling gesture.
+     *
+     * @param fraction desired fraction of the width of the screen to be treated as a valid rotary
+     *                 scrolling target.
+     */
+    public void setBezelWidth(float fraction) {
+        mMinRadiusFraction = 1 - fraction;
+        mMinRadiusFractionSquared = mMinRadiusFraction * mMinRadiusFraction;
+    }
+
+    /**
+     * Returns how many degrees you have to drag along the bezel to scroll one screen height. See
+     * {@link #setScrollDegreesPerScreen(float)} for details.
+     */
+    public float getScrollDegreesPerScreen() {
+        return mScrollDegreesPerScreen;
+    }
+
+    /**
+     * Returns the current bezel width for circular scrolling. See {@link #setBezelWidth(float)}
+     * for details.
+     */
+    public float getBezelWidth() {
+        return 1 - mMinRadiusFraction;
+    }
+}
diff --git a/wear/src/android/support/wear/widget/SimpleAnimatorListener.java b/wear/src/android/support/wear/widget/SimpleAnimatorListener.java
new file mode 100644
index 0000000..a60b0bd
--- /dev/null
+++ b/wear/src/android/support/wear/widget/SimpleAnimatorListener.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.animation.Animator;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+
+/**
+ * Convenience class for listening for Animator events that implements the AnimatorListener
+ * interface and allows extending only methods that are necessary.
+ *
+ * @hide Hidden until this goes through review
+ */
+@RequiresApi(Build.VERSION_CODES.KITKAT_WATCH)
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class SimpleAnimatorListener implements Animator.AnimatorListener {
+
+    private boolean mWasCanceled;
+
+    @Override
+    public void onAnimationCancel(Animator animator) {
+        mWasCanceled = true;
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animator) {
+        if (!mWasCanceled) {
+            onAnimationComplete(animator);
+        }
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animator) {}
+
+    @Override
+    public void onAnimationStart(Animator animator) {
+        mWasCanceled = false;
+    }
+
+    /**
+     * Called when the animation finishes. Not called if the animation was canceled.
+     */
+    public void onAnimationComplete(Animator animator) {}
+
+    /**
+     * Provides information if the animation was cancelled.
+     *
+     * @return True if animation was cancelled.
+     */
+    public boolean wasCanceled() {
+        return mWasCanceled;
+    }
+}
diff --git a/wear/src/android/support/wear/widget/SwipeDismissFrameLayout.java b/wear/src/android/support/wear/widget/SwipeDismissFrameLayout.java
new file mode 100644
index 0000000..c2b595d
--- /dev/null
+++ b/wear/src/android/support/wear/widget/SwipeDismissFrameLayout.java
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.content.Context;
+import android.support.annotation.UiThread;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * A layout enabling left-to-right swipe-to-dismiss, intended for use within an activity.
+ *
+ * <p>At least one listener must be {@link #addCallback(Callback) added} to act on a dismissal
+ * action. A listener will typically remove a containing view or fragment from the current
+ * activity.
+ *
+ * <p>To suppress a swipe-dismiss gesture, at least one contained view must be scrollable,
+ * indicating that it would like to consume any horizontal touch gestures in that direction. In
+ * this  case this view will only allow swipe-to-dismiss on the very edge of the left-hand-side of
+ * the screen. If you wish to entirely disable the swipe-to-dismiss gesture,
+ * {@link #setSwipeable(boolean)} can be used for more direct control over the feature.
+ */
+@UiThread
+public class SwipeDismissFrameLayout extends SwipeDismissLayout {
+
+    private static final String TAG = "SwipeDismissFrameLayout";
+
+    private static final float TRANSLATION_MIN_ALPHA = 0.5f;
+    private static final float DEFAULT_INTERPOLATION_FACTOR = 1.5f;
+
+    /** Implement this callback to act on particular stages of the dismissal. */
+    @UiThread
+    public abstract static class Callback {
+        /**
+         * Notifies listeners that the view is now considering to start a dismiss gesture from a
+         * particular point on the screen. The default implementation returns true for all
+         * coordinates so that is is possible to start a swipe-to-dismiss gesture from any location.
+         * If any one instance of this Callback returns false for a given set of coordinates,
+         * swipe-to-dismiss will not be allowed to start in that point.
+         *
+         * @param layout The layout associated with this callback.
+         * @param xDown The x coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN}
+         *              event for this motion.
+         * @param yDown The y coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN}
+         *              event for this motion.
+         * @return true if this gesture should be recognized as a swipe to dismiss gesture, false
+         * otherwise.
+         */
+        boolean onPreSwipeStart(SwipeDismissFrameLayout layout, float xDown, float yDown) {
+            return true;
+        }
+
+        /**
+         * Notifies listeners that the view is now being dragged as part of a dismiss gesture.
+         *
+         * @param layout The layout associated with this callback.
+        */
+        public void onSwipeStarted(SwipeDismissFrameLayout layout) {
+        }
+
+        /**
+         * Notifies listeners that the swipe gesture has ended without a dismissal.
+         *
+         * @param layout The layout associated with this callback.
+         */
+        public void onSwipeCanceled(SwipeDismissFrameLayout layout) {
+        }
+
+        /**
+         * Notifies listeners the dismissal is complete and the view now off screen.
+         *
+         * @param layout The layout associated with this callback.
+         */
+        public void onDismissed(SwipeDismissFrameLayout layout) {
+        }
+    }
+
+    private final OnPreSwipeListener mOnPreSwipeListener = new MyOnPreSwipeListener();
+    private final OnDismissedListener mOnDismissedListener = new MyOnDismissedListener();
+
+    private final OnSwipeProgressChangedListener mOnSwipeProgressListener =
+            new MyOnSwipeProgressChangedListener();
+
+    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final int mAnimationTime;
+    private final DecelerateInterpolator mCancelInterpolator;
+    private final AccelerateInterpolator mDismissInterpolator;
+    private final DecelerateInterpolator mCompleteDismissGestureInterpolator;
+
+    private boolean mStarted;
+
+    /**
+     * Simple constructor to use when creating a view from code.
+     *
+     * @param context The {@link Context} the view is running in, through which it can access the
+     *                current theme, resources, etc.
+     */
+    public SwipeDismissFrameLayout(Context context) {
+        this(context, null, 0);
+    }
+
+    /**
+     * Constructor that is called when inflating a view from XML. This is called when a view is
+     * being constructed from an XML file, supplying attributes that were specified in the XML file.
+     * This version uses a default style of 0, so the only attribute values applied are those in the
+     * Context's Theme and the given AttributeSet.
+     *
+     * <p>
+     *
+     * <p>The method onFinishInflate() will be called after all children have been added.
+     *
+     * @param context The {@link Context} the view is running in, through which it can access the
+     *                current theme, resources, etc.
+     * @param attrs   The attributes of the XML tag that is inflating the view.
+     */
+    public SwipeDismissFrameLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * Perform inflation from XML and apply a class-specific base style from a theme attribute.
+     * This constructor allows subclasses to use their own base style when they are inflating.
+     *
+     * @param context  The {@link Context} the view is running in, through which it can access the
+     *                 current theme, resources, etc.
+     * @param attrs    The attributes of the XML tag that is inflating the view.
+     * @param defStyle An attribute in the current theme that contains a reference to a style
+     *                 resource that supplies default values for the view. Can be 0 to not look for
+     *                 defaults.
+     */
+    public SwipeDismissFrameLayout(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, 0);
+    }
+
+    /**
+     * Perform inflation from XML and apply a class-specific base style from a theme attribute.
+     * This constructor allows subclasses to use their own base style when they are inflating.
+     *
+     * @param context  The {@link Context} the view is running in, through which it can access the
+     *                 current theme, resources, etc.
+     * @param attrs    The attributes of the XML tag that is inflating the view.
+     * @param defStyle An attribute in the current theme that contains a reference to a style
+     *                 resource that supplies default values for the view. Can be 0 to not look for
+     *                 defaults.
+     * @param defStyleRes This corresponds to the fourth argument
+     *                    of {@link View#View(Context, AttributeSet, int, int)}. It allows a style
+     *                    resource to be specified when creating the view.
+     */
+    public SwipeDismissFrameLayout(Context context, AttributeSet attrs, int defStyle,
+            int defStyleRes) {
+        super(context, attrs, defStyle, defStyleRes);
+        setOnPreSwipeListener(mOnPreSwipeListener);
+        setOnDismissedListener(mOnDismissedListener);
+        setOnSwipeProgressChangedListener(mOnSwipeProgressListener);
+        mAnimationTime = getContext().getResources().getInteger(
+                android.R.integer.config_shortAnimTime);
+        mCancelInterpolator = new DecelerateInterpolator(DEFAULT_INTERPOLATION_FACTOR);
+        mDismissInterpolator = new AccelerateInterpolator(DEFAULT_INTERPOLATION_FACTOR);
+        mCompleteDismissGestureInterpolator = new DecelerateInterpolator(
+                DEFAULT_INTERPOLATION_FACTOR);
+    }
+
+    /** Adds a callback for dismissal. */
+    public void addCallback(Callback callback) {
+        if (callback == null) {
+            throw new NullPointerException("addCallback called with null callback");
+        }
+        mCallbacks.add(callback);
+    }
+
+    /** Removes a callback that was added with {@link #addCallback(Callback)}. */
+    public void removeCallback(Callback callback) {
+        if (callback == null) {
+            throw new NullPointerException("removeCallback called with null callback");
+        }
+        if (!mCallbacks.remove(callback)) {
+            throw new IllegalStateException("removeCallback called with nonexistent callback");
+        }
+    }
+
+    /**
+     * Resets this view to the original state. This method cancels any pending animations on this
+     * view and resets the alpha as well as x translation values.
+     */
+    private void resetTranslationAndAlpha() {
+        animate().cancel();
+        setTranslationX(0);
+        setAlpha(1);
+        mStarted = false;
+    }
+
+    private final class MyOnPreSwipeListener implements OnPreSwipeListener {
+
+        @Override
+        public boolean onPreSwipe(SwipeDismissLayout layout, float xDown, float yDown) {
+            for (Callback callback : mCallbacks) {
+                if (!callback.onPreSwipeStart(SwipeDismissFrameLayout.this, xDown, yDown)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private final class MyOnDismissedListener implements OnDismissedListener {
+
+        @Override
+        public void onDismissed(SwipeDismissLayout layout) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onDismissed()");
+            }
+            animate()
+                    .translationX(getWidth())
+                    .alpha(0)
+                    .setDuration(mAnimationTime)
+                    .setInterpolator(
+                            mStarted ? mCompleteDismissGestureInterpolator : mDismissInterpolator)
+                    .withEndAction(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                                        Callback callbacks = mCallbacks.get(i);
+                                        callbacks.onDismissed(SwipeDismissFrameLayout.this);
+                                    }
+                                    resetTranslationAndAlpha();
+                                }
+                            });
+        }
+    }
+
+    private final class MyOnSwipeProgressChangedListener implements OnSwipeProgressChangedListener {
+
+        @Override
+        public void onSwipeProgressChanged(SwipeDismissLayout layout, float progress,
+                float translate) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onSwipeProgressChanged() - " + translate);
+            }
+            setTranslationX(translate);
+            setAlpha(1 - (progress * TRANSLATION_MIN_ALPHA));
+            if (!mStarted) {
+                for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                    Callback callbacks = mCallbacks.get(i);
+                    callbacks.onSwipeStarted(SwipeDismissFrameLayout.this);
+                }
+                mStarted = true;
+            }
+        }
+
+        @Override
+        public void onSwipeCanceled(SwipeDismissLayout layout) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onSwipeCanceled() run swipe cancel animation");
+            }
+            mStarted = false;
+            animate()
+                    .translationX(0)
+                    .alpha(1)
+                    .setDuration(mAnimationTime)
+                    .setInterpolator(mCancelInterpolator)
+                    .withEndAction(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                                        Callback callbacks = mCallbacks.get(i);
+                                        callbacks.onSwipeCanceled(SwipeDismissFrameLayout.this);
+                                    }
+                                    resetTranslationAndAlpha();
+                                }
+                            });
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/SwipeDismissLayout.java b/wear/src/android/support/wear/widget/SwipeDismissLayout.java
new file mode 100644
index 0000000..6e7a6f3
--- /dev/null
+++ b/wear/src/android/support/wear/widget/SwipeDismissLayout.java
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.UiThread;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+/**
+ * Special layout that finishes its activity when swiped away.
+ *
+ * <p>This is a modified copy of the internal framework class
+ * com.android.internal.widget.SwipeDismissLayout.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@UiThread
+class SwipeDismissLayout extends FrameLayout {
+    private static final String TAG = "SwipeDismissLayout";
+
+    public static final float DEFAULT_DISMISS_DRAG_WIDTH_RATIO = .33f;
+    // A value between 0.0 and 1.0 determining the percentage of the screen on the left-hand-side
+    // where edge swipe gestures are permitted to begin.
+    private static final float EDGE_SWIPE_THRESHOLD = 0.1f;
+
+    /** Called when the layout is about to consider a swipe. */
+    @UiThread
+    interface OnPreSwipeListener {
+        /**
+         * Notifies listeners that the view is now considering to start a dismiss gesture from a
+         * particular point on the screen. The default implementation returns true for all
+         * coordinates so that is is possible to start a swipe-to-dismiss gesture from any location.
+         * If any one instance of this Callback returns false for a given set of coordinates,
+         * swipe-to-dismiss will not be allowed to start in that point.
+         *
+         * @param xDown the x coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN}
+         *              event for this motion
+         * @param yDown the y coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN}
+         *              event for this motion
+         * @return {@code true} if these coordinates should be considered as a start of a swipe
+         * gesture, {@code false} otherwise
+         */
+        boolean onPreSwipe(SwipeDismissLayout swipeDismissLayout, float xDown, float yDown);
+    }
+
+    /**
+     * Interface enabling listeners to react to when the swipe gesture is done and the view should
+     * probably be dismissed from the UI.
+     */
+    @UiThread
+    interface OnDismissedListener {
+        void onDismissed(SwipeDismissLayout layout);
+    }
+
+    /**
+     * Interface enabling listeners to react to changes in the progress of the swipe-to-dismiss
+     * gesture.
+     */
+    @UiThread
+    interface OnSwipeProgressChangedListener {
+        /**
+         * Called when the layout has been swiped and the position of the window should change.
+         *
+         * @param layout    the layout associated with this listener.
+         * @param progress  a number in [0, 1] representing how far to the right the window has
+         *                  been swiped
+         * @param translate a number in [0, w], where w is the width of the layout. This is
+         *                  equivalent to progress * layout.getWidth()
+         */
+        void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
+
+        /**
+         * Called when the layout started to be swiped away but then the gesture was canceled.
+         *
+         * @param layout    the layout associated with this listener
+         */
+        void onSwipeCanceled(SwipeDismissLayout layout);
+    }
+
+    // Cached ViewConfiguration and system-wide constant values
+    private int mSlop;
+    private int mMinFlingVelocity;
+    private float mGestureThresholdPx;
+
+    // Transient properties
+    private int mActiveTouchId;
+    private float mDownX;
+    private float mDownY;
+    private boolean mSwipeable;
+    private boolean mSwiping;
+    // This variable holds information about whether the initial move of a longer swipe
+    // (consisting of multiple move events) has conformed to the definition of a horizontal
+    // swipe-to-dismiss. A swipe gesture is only ever allowed to be recognized if this variable is
+    // set to true. Otherwise, the motion events will be allowed to propagate to the children.
+    private boolean mCanStartSwipe = true;
+    private boolean mDismissed;
+    private boolean mDiscardIntercept;
+    private VelocityTracker mVelocityTracker;
+    private float mTranslationX;
+    private boolean mDisallowIntercept;
+
+    @Nullable
+    private OnPreSwipeListener mOnPreSwipeListener;
+    private OnDismissedListener mDismissedListener;
+    private OnSwipeProgressChangedListener mProgressListener;
+
+    private float mLastX;
+    private float mDismissMinDragWidthRatio = DEFAULT_DISMISS_DRAG_WIDTH_RATIO;
+
+    SwipeDismissLayout(Context context) {
+        this(context, null);
+    }
+
+    SwipeDismissLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, 0);
+    }
+
+    SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
+        super(context, attrs, defStyle, defStyleRes);
+        ViewConfiguration vc = ViewConfiguration.get(context);
+        mSlop = vc.getScaledTouchSlop();
+        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
+        mGestureThresholdPx =
+                Resources.getSystem().getDisplayMetrics().widthPixels * EDGE_SWIPE_THRESHOLD;
+
+        // By default, the view is swipeable.
+        setSwipeable(true);
+    }
+
+    /**
+     * Sets the minimum ratio of the screen after which the swipe gesture is treated as swipe-to-
+     * dismiss.
+     *
+     * @param ratio  the ratio of the screen at which the swipe gesture is treated as
+     *               swipe-to-dismiss. should be provided as a fraction of the screen
+     */
+    public void setDismissMinDragWidthRatio(float ratio) {
+        mDismissMinDragWidthRatio = ratio;
+    }
+
+    /**
+     * Returns the current ratio of te screen at which the swipe gesture is treated as
+     * swipe-to-dismiss.
+     *
+     * @return the current ratio of te screen at which the swipe gesture is treated as
+     * swipe-to-dismiss
+     */
+    public float getDismissMinDragWidthRatio() {
+        return mDismissMinDragWidthRatio;
+    }
+
+    /**
+     * Sets the layout to swipeable or not. This effectively turns the functionality of this layout
+     * on or off.
+     *
+     * @param swipeable whether the layout should react to the swipe gesture
+     */
+    public void setSwipeable(boolean swipeable) {
+        mSwipeable = swipeable;
+    }
+
+    /** Returns true if the layout reacts to swipe gestures. */
+    public boolean isSwipeable() {
+        return mSwipeable;
+    }
+
+    void setOnPreSwipeListener(@Nullable OnPreSwipeListener listener) {
+        mOnPreSwipeListener = listener;
+    }
+
+    void setOnDismissedListener(@Nullable OnDismissedListener listener) {
+        mDismissedListener = listener;
+    }
+
+    void setOnSwipeProgressChangedListener(@Nullable OnSwipeProgressChangedListener listener) {
+        mProgressListener = listener;
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        mDisallowIntercept = disallowIntercept;
+        if (getParent() != null) {
+            getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (!mSwipeable) {
+            return super.onInterceptTouchEvent(ev);
+        }
+
+        // offset because the view is translated during swipe
+        ev.offsetLocation(mTranslationX, 0);
+
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                resetMembers();
+                mDownX = ev.getRawX();
+                mDownY = ev.getRawY();
+                mActiveTouchId = ev.getPointerId(0);
+                mVelocityTracker = VelocityTracker.obtain();
+                mVelocityTracker.addMovement(ev);
+                break;
+
+            case MotionEvent.ACTION_POINTER_DOWN:
+                int actionIndex = ev.getActionIndex();
+                mActiveTouchId = ev.getPointerId(actionIndex);
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                actionIndex = ev.getActionIndex();
+                int pointerId = ev.getPointerId(actionIndex);
+                if (pointerId == mActiveTouchId) {
+                    // This was our active pointer going up. Choose a new active pointer.
+                    int newActionIndex = actionIndex == 0 ? 1 : 0;
+                    mActiveTouchId = ev.getPointerId(newActionIndex);
+                }
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                resetMembers();
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                if (mVelocityTracker == null || mDiscardIntercept) {
+                    break;
+                }
+
+                int pointerIndex = ev.findPointerIndex(mActiveTouchId);
+                if (pointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointer index: ignoring.");
+                    mDiscardIntercept = true;
+                    break;
+                }
+                float dx = ev.getRawX() - mDownX;
+                float x = ev.getX(pointerIndex);
+                float y = ev.getY(pointerIndex);
+
+                if (dx != 0 && mDownX >= mGestureThresholdPx && canScroll(this, false, dx, x, y)) {
+                    mDiscardIntercept = true;
+                    break;
+                }
+                updateSwiping(ev);
+                break;
+        }
+
+        if ((mOnPreSwipeListener == null && !mDisallowIntercept)
+                || mOnPreSwipeListener.onPreSwipe(this, mDownX, mDownY)) {
+            return (!mDiscardIntercept && mSwiping);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean canScrollHorizontally(int direction) {
+        // This view can only be swiped horizontally from left to right - this means a negative
+        // SCROLLING direction. We return false if the view is not visible to avoid capturing swipe
+        // gestures when the view is hidden.
+        return direction < 0 && isSwipeable() && getVisibility() == View.VISIBLE;
+    }
+
+    /**
+     * Helper function determining if a particular move gesture was verbose enough to qualify as a
+     * beginning of a swipe.
+     *
+     * @param dx distance traveled in the x direction, from the initial touch down
+     * @param dy distance traveled in the y direction, from the initial touch down
+     * @return {@code true} if the gesture was long enough to be considered a potential swipe
+     */
+    private boolean isPotentialSwipe(float dx, float dy) {
+        return (dx * dx) + (dy * dy) > mSlop * mSlop;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (!mSwipeable) {
+            return super.onTouchEvent(ev);
+        }
+
+        if (mVelocityTracker == null) {
+            return super.onTouchEvent(ev);
+        }
+
+        if (mOnPreSwipeListener != null && !mOnPreSwipeListener.onPreSwipe(this, mDownX, mDownY)) {
+            return super.onTouchEvent(ev);
+        }
+
+        // offset because the view is translated during swipe
+        ev.offsetLocation(mTranslationX, 0);
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_UP:
+                updateDismiss(ev);
+                if (mDismissed) {
+                    dismiss();
+                } else if (mSwiping) {
+                    cancel();
+                }
+                resetMembers();
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                cancel();
+                resetMembers();
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                mVelocityTracker.addMovement(ev);
+                mLastX = ev.getRawX();
+                updateSwiping(ev);
+                if (mSwiping) {
+                    setProgress(ev.getRawX() - mDownX);
+                    break;
+                }
+        }
+        return true;
+    }
+
+    private void setProgress(float deltaX) {
+        mTranslationX = deltaX;
+        if (mProgressListener != null && deltaX >= 0) {
+            mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
+        }
+    }
+
+    private void dismiss() {
+        if (mDismissedListener != null) {
+            mDismissedListener.onDismissed(this);
+        }
+    }
+
+    private void cancel() {
+        if (mProgressListener != null) {
+            mProgressListener.onSwipeCanceled(this);
+        }
+    }
+
+    /** Resets internal members when canceling or finishing a given gesture. */
+    private void resetMembers() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = null;
+        mTranslationX = 0;
+        mDownX = 0;
+        mDownY = 0;
+        mSwiping = false;
+        mDismissed = false;
+        mDiscardIntercept = false;
+        mCanStartSwipe = true;
+        mDisallowIntercept = false;
+    }
+
+    private void updateSwiping(MotionEvent ev) {
+        if (!mSwiping) {
+            float deltaX = ev.getRawX() - mDownX;
+            float deltaY = ev.getRawY() - mDownY;
+            if (isPotentialSwipe(deltaX, deltaY)) {
+                // There are three conditions on which we want want to start swiping:
+                // 1. The swipe is from left to right AND
+                // 2. It is horizontal AND
+                // 3. We actually can start swiping
+                mSwiping = mCanStartSwipe && Math.abs(deltaY) < Math.abs(deltaX) && deltaX > 0;
+                mCanStartSwipe = mSwiping;
+            }
+        }
+    }
+
+    private void updateDismiss(MotionEvent ev) {
+        float deltaX = ev.getRawX() - mDownX;
+        mVelocityTracker.addMovement(ev);
+        mVelocityTracker.computeCurrentVelocity(1000);
+        if (!mDismissed) {
+            if ((deltaX > (getWidth() * mDismissMinDragWidthRatio) && ev.getRawX() >= mLastX)
+                    || mVelocityTracker.getXVelocity() >= mMinFlingVelocity) {
+                mDismissed = true;
+            }
+        }
+        // Check if the user tried to undo this.
+        if (mDismissed && mSwiping) {
+            // Check if the user's finger is actually flinging back to left
+            if (mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
+                mDismissed = false;
+            }
+        }
+    }
+
+    /**
+     * Tests scrollability within child views of v in the direction of dx.
+     *
+     * @param v      view to test for horizontal scrollability
+     * @param checkV whether the view v passed should itself be checked for scrollability
+     *               ({@code true}), or just its children ({@code false})
+     * @param dx     delta scrolled in pixels. Only the sign of this is used
+     * @param x      x coordinate of the active touch point
+     * @param y      y coordinate of the active touch point
+     * @return {@code true} if child views of v can be scrolled by delta of dx
+     */
+    protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {
+        if (v instanceof ViewGroup) {
+            final ViewGroup group = (ViewGroup) v;
+            final int scrollX = v.getScrollX();
+            final int scrollY = v.getScrollY();
+            final int count = group.getChildCount();
+            for (int i = count - 1; i >= 0; i--) {
+                final View child = group.getChildAt(i);
+                if (x + scrollX >= child.getLeft()
+                        && x + scrollX < child.getRight()
+                        && y + scrollY >= child.getTop()
+                        && y + scrollY < child.getBottom()
+                        && canScroll(
+                        child, true, dx, x + scrollX - child.getLeft(),
+                        y + scrollY - child.getTop())) {
+                    return true;
+                }
+            }
+        }
+
+        return checkV && v.canScrollHorizontally((int) -dx);
+    }
+}
diff --git a/wear/src/android/support/wear/widget/WearableLinearLayoutManager.java b/wear/src/android/support/wear/widget/WearableLinearLayoutManager.java
new file mode 100644
index 0000000..9c882ab
--- /dev/null
+++ b/wear/src/android/support/wear/widget/WearableLinearLayoutManager.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+package android.support.wear.widget;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * This wear-specific implementation of {@link LinearLayoutManager} provides basic
+ * offsetting logic for updating child layout. For round devices it offsets the children
+ * horizontally to make them appear to travel around a circle. For square devices it aligns them in
+ * a straight list. This functionality is provided by the {@link CurvingLayoutCallback} which is
+ * set when constructing the this class with its default constructor
+ * {@link #WearableLinearLayoutManager(Context)}.
+ */
+public class WearableLinearLayoutManager extends LinearLayoutManager {
+
+    @Nullable
+    private LayoutCallback mLayoutCallback;
+
+    /**
+     * Callback for interacting with layout passes.
+     */
+    public abstract static class LayoutCallback {
+        /**
+         * Override this method to implement custom child layout behavior on scroll. It is called
+         * at the end of each layout pass of the view (including scrolling) and enables you to
+         * modify any property of the child view. Examples include scaling the children based on
+         * their distance from the center of the parent, or changing the translation of the children
+         * to create an illusion of the path they are moving along.
+         *
+         * @param child  the current child to be affected.
+         * @param parent the {@link RecyclerView} parent that this class is attached to.
+         */
+        public abstract void onLayoutFinished(View child, RecyclerView parent);
+    }
+
+    /**
+     * Creates a {@link WearableLinearLayoutManager} for a vertical list.
+     *
+     * @param context Current context, will be used to access resources.
+     * @param layoutCallback Callback to be associated with this {@link WearableLinearLayoutManager}
+     */
+    public WearableLinearLayoutManager(Context context, LayoutCallback layoutCallback) {
+        super(context, VERTICAL, false);
+        mLayoutCallback = layoutCallback;
+    }
+
+    /**
+     * Creates a {@link WearableLinearLayoutManager} for a vertical list.
+     *
+     * @param context Current context, will be used to access resources.
+     */
+    public WearableLinearLayoutManager(Context context) {
+        this(context, new CurvingLayoutCallback(context));
+    }
+
+    /**
+     * Set a particular instance of the layout callback for this
+     * {@link WearableLinearLayoutManager}. The callback will be called on the Ui thread.
+     *
+     * @param layoutCallback
+     */
+    public void setLayoutCallback(@Nullable LayoutCallback layoutCallback) {
+        mLayoutCallback = layoutCallback;
+    }
+
+    /**
+     * @return the current {@link LayoutCallback} associated with this
+     * {@link WearableLinearLayoutManager}.
+     */
+    @Nullable
+    public LayoutCallback getLayoutCallback() {
+        return mLayoutCallback;
+    }
+
+    @Override
+    public int scrollVerticallyBy(
+            int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
+        int scrolled = super.scrollVerticallyBy(dy, recycler, state);
+
+        updateLayout();
+        return scrolled;
+    }
+
+    @Override
+    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        super.onLayoutChildren(recycler, state);
+        if (getChildCount() == 0) {
+            return;
+        }
+
+        updateLayout();
+    }
+
+    private void updateLayout() {
+        if (mLayoutCallback == null) {
+            return;
+        }
+        final int childCount = getChildCount();
+        for (int count = 0; count < childCount; count++) {
+            View child = getChildAt(count);
+            mLayoutCallback.onLayoutFinished(child, (WearableRecyclerView) child.getParent());
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/WearableRecyclerView.java b/wear/src/android/support/wear/widget/WearableRecyclerView.java
new file mode 100644
index 0000000..5cacdfc
--- /dev/null
+++ b/wear/src/android/support/wear/widget/WearableRecyclerView.java
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Point;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.R;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+/**
+ * Wearable specific implementation of the {@link RecyclerView} enabling {@link
+ * #setCircularScrollingGestureEnabled(boolean)} circular scrolling} and semi-circular layouts.
+ *
+ * @see #setCircularScrollingGestureEnabled(boolean)
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class WearableRecyclerView extends RecyclerView {
+    private static final String TAG = "WearableRecyclerView";
+
+    private static final int NO_VALUE = Integer.MIN_VALUE;
+
+    private final ScrollManager mScrollManager = new ScrollManager();
+    private boolean mCircularScrollingEnabled;
+    private boolean mEdgeItemsCenteringEnabled;
+    private boolean mCenterEdgeItemsWhenThereAreChildren;
+
+    private int mOriginalPaddingTop = NO_VALUE;
+    private int mOriginalPaddingBottom = NO_VALUE;
+
+    /** Pre-draw listener which is used to adjust the padding on this view before its first draw. */
+    private final ViewTreeObserver.OnPreDrawListener mPaddingPreDrawListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    if (mCenterEdgeItemsWhenThereAreChildren && getChildCount() > 0) {
+                        setupCenteredPadding();
+                        mCenterEdgeItemsWhenThereAreChildren = false;
+                    }
+                    return true;
+                }
+            };
+
+    public WearableRecyclerView(Context context) {
+        this(context, null);
+    }
+
+    public WearableRecyclerView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, 0);
+    }
+
+    public WearableRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle,
+            int defStyleRes) {
+        super(context, attrs, defStyle);
+
+        setHasFixedSize(true);
+        // Padding is used to center the top and bottom items in the list, don't clip to padding to
+        // allows the items to draw in that space.
+        setClipToPadding(false);
+
+        if (attrs != null) {
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WearableRecyclerView,
+                    defStyle, defStyleRes);
+
+            setCircularScrollingGestureEnabled(
+                    a.getBoolean(
+                            R.styleable.WearableRecyclerView_circularScrollingGestureEnabled,
+                            mCircularScrollingEnabled));
+            setBezelFraction(
+                    a.getFloat(R.styleable.WearableRecyclerView_bezelWidth,
+                            mScrollManager.getBezelWidth()));
+            setScrollDegreesPerScreen(
+                    a.getFloat(
+                            R.styleable.WearableRecyclerView_scrollDegreesPerScreen,
+                            mScrollManager.getScrollDegreesPerScreen()));
+            a.recycle();
+        }
+    }
+
+    private void setupCenteredPadding() {
+        if (getChildCount() < 1 || !mEdgeItemsCenteringEnabled) {
+            return;
+        }
+        // All the children in the view are the same size, as we set setHasFixedSize
+        // to true, so the height of the first child is the same as all of them.
+        View child = getChildAt(0);
+        int height = child.getHeight();
+        // This is enough padding to center the child view in the parent.
+        int desiredPadding = (int) ((getHeight() * 0.5f) - (height * 0.5f));
+
+        if (getPaddingTop() != desiredPadding) {
+            mOriginalPaddingTop = getPaddingTop();
+            mOriginalPaddingBottom = getPaddingBottom();
+            // The view is symmetric along the vertical axis, so the top and bottom
+            // can be the same.
+            setPadding(getPaddingLeft(), desiredPadding, getPaddingRight(), desiredPadding);
+
+            // The focused child should be in the center, so force a scroll to it.
+            View focusedChild = getFocusedChild();
+            int focusedPosition =
+                    (focusedChild != null) ? getLayoutManager().getPosition(
+                            focusedChild) : 0;
+            getLayoutManager().scrollToPosition(focusedPosition);
+        }
+    }
+
+    private void setupOriginalPadding() {
+        if (mOriginalPaddingTop == NO_VALUE) {
+            return;
+        } else {
+            setPadding(getPaddingLeft(), mOriginalPaddingTop, getPaddingRight(),
+                    mOriginalPaddingBottom);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (mCircularScrollingEnabled && mScrollManager.onTouchEvent(event)) {
+            return true;
+        }
+        return super.onTouchEvent(event);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        Point screenSize = new Point();
+        getDisplay().getSize(screenSize);
+        mScrollManager.setRecyclerView(this, screenSize.x, screenSize.y);
+        getViewTreeObserver().addOnPreDrawListener(mPaddingPreDrawListener);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mScrollManager.clearRecyclerView();
+        getViewTreeObserver().removeOnPreDrawListener(mPaddingPreDrawListener);
+    }
+
+    /**
+     * Enables/disables circular touch scrolling for this view. When enabled, circular touch
+     * gestures around the edge of the screen will cause the view to scroll up or down. Related
+     * methods let you specify the characteristics of the scrolling, like the speed of the scroll
+     * or the are considered for the start of this scrolling gesture.
+     *
+     * @see #setScrollDegreesPerScreen(float)
+     * @see #setBezelFraction(float)
+     */
+    public void setCircularScrollingGestureEnabled(boolean circularScrollingGestureEnabled) {
+        mCircularScrollingEnabled = circularScrollingGestureEnabled;
+    }
+
+    /**
+     * Returns whether circular scrolling is enabled for this view.
+     *
+     * @see #setCircularScrollingGestureEnabled(boolean)
+     */
+    public boolean isCircularScrollingGestureEnabled() {
+        return mCircularScrollingEnabled;
+    }
+
+    /**
+     * Sets how many degrees the user has to rotate by to scroll through one screen height when they
+     * are using the circular scrolling gesture.The default value equates 180 degrees scroll to one
+     * screen.
+     *
+     * @see #setCircularScrollingGestureEnabled(boolean)
+     *
+     * @param degreesPerScreen the number of degrees to rotate by to scroll through one whole
+     *                         height of the screen,
+     */
+    public void setScrollDegreesPerScreen(float degreesPerScreen) {
+        mScrollManager.setScrollDegreesPerScreen(degreesPerScreen);
+    }
+
+    /**
+     * Returns how many degrees does the user have to rotate for to scroll through one screen
+     * height.
+     *
+     * @see #setCircularScrollingGestureEnabled(boolean)
+     * @see #setScrollDegreesPerScreen(float).
+     */
+    public float getScrollDegreesPerScreen() {
+        return mScrollManager.getScrollDegreesPerScreen();
+    }
+
+    /**
+     * Taps within this radius and the radius of the screen are considered close enough to the
+     * bezel to be candidates for circular scrolling. Expressed as a fraction of the screen's
+     * radius. The default is the whole screen i.e 1.0f.
+     */
+    public void setBezelFraction(float fraction) {
+        mScrollManager.setBezelWidth(fraction);
+    }
+
+    /**
+     * Returns the current bezel width for circular scrolling as a fraction of the screen's
+     * radius.
+     *
+     * @see #setBezelFraction(float)
+     */
+    public float getBezelFraction() {
+        return mScrollManager.getBezelWidth();
+    }
+
+    /**
+     * Use this method to configure the {@link WearableRecyclerView} to always align the first and
+     * last items with the vertical center of the screen. This effectively moves the start and end
+     * of the list to the middle of the screen if the user has scrolled so far. It takes the height
+     * of the children into account so that they are correctly centered.
+     *
+     * @param isEnabled set to true if you wish to align the edge children (first and last)
+     *                        with the center of the screen.
+     */
+    public void setEdgeItemsCenteringEnabled(boolean isEnabled) {
+        mEdgeItemsCenteringEnabled = isEnabled;
+        if (mEdgeItemsCenteringEnabled) {
+            if (getChildCount() > 0) {
+                setupCenteredPadding();
+            } else {
+                mCenterEdgeItemsWhenThereAreChildren = true;
+            }
+        } else {
+            setupOriginalPadding();
+            mCenterEdgeItemsWhenThereAreChildren = false;
+        }
+    }
+
+    /**
+     * Returns whether the view is currently configured to center the edge children. See {@link
+     * #setEdgeItemsCenteringEnabled} for details.
+     */
+    public boolean isEdgeItemsCenteringEnabled() {
+        return mEdgeItemsCenteringEnabled;
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java b/wear/src/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
new file mode 100644
index 0000000..f1cb640
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingWatcher;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * {@link FlingWatcher} implementation for {@link AbsListView AbsListViews}. Detects the end of
+ * a Fling by waiting until the scroll state is no longer {@link
+ * OnScrollListener#SCROLL_STATE_FLING}.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+class AbsListViewFlingWatcher implements FlingWatcher, OnScrollListener {
+
+    private final FlingListener mListener;
+    private final WeakReference<AbsListView> mListView;
+
+    AbsListViewFlingWatcher(FlingListener listener, AbsListView listView) {
+        mListener = listener;
+        mListView = new WeakReference<>(listView);
+    }
+
+    @Override
+    public void watch() {
+        AbsListView absListView = mListView.get();
+        if (absListView != null) {
+            absListView.setOnScrollListener(this);
+        }
+    }
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        if (scrollState != OnScrollListener.SCROLL_STATE_FLING) {
+            view.setOnScrollChangeListener(null);
+            mListener.onFlingComplete(view);
+        }
+    }
+
+    @Override
+    public void onScroll(
+            AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
+}
diff --git a/wear/src/android/support/wear/widget/drawer/FlingWatcherFactory.java b/wear/src/android/support/wear/widget/drawer/FlingWatcherFactory.java
new file mode 100644
index 0000000..3fe84c6
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/FlingWatcherFactory.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.v4.widget.NestedScrollView;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ScrollView;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Creates a {@link FlingWatcher} based on the type of {@link View}.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+class FlingWatcherFactory {
+
+    /**
+     * Listener that is notified when a fling completes and the view has settled. Polling may be
+     * used to determine when the fling has completed, so there may be up to a 100ms delay.
+     */
+    interface FlingListener {
+        void onFlingComplete(View view);
+    }
+
+    /**
+     * Watches a given {@code view} to detect the end of a fling. Will notify a {@link
+     * FlingListener} when the end is found.
+     */
+    interface FlingWatcher {
+        void watch();
+    }
+
+    private final FlingListener mListener;
+    private final Map<View, FlingWatcher> mWatchers = new WeakHashMap<>();
+
+    FlingWatcherFactory(FlingListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("FlingListener was null");
+        }
+
+        mListener = listener;
+    }
+
+    /**
+     * Returns a {@link FlingWatcher} for the particular type of {@link View}.
+     */
+    @Nullable
+    FlingWatcher getFor(View view) {
+        FlingWatcher watcher = mWatchers.get(view);
+        if (watcher == null) {
+            watcher = createFor(view);
+            if (watcher != null) {
+                mWatchers.put(view, watcher);
+            }
+        }
+
+        return watcher;
+    }
+
+    /**
+     * Creates a {@link FlingWatcher} for the particular type of {@link View}.
+     */
+    @Nullable
+    private FlingWatcher createFor(View view) {
+        if (view == null) {
+            throw new IllegalArgumentException("View was null");
+        }
+
+        if (view instanceof RecyclerView) {
+            return new RecyclerViewFlingWatcher(mListener, (RecyclerView) view);
+        } else if (view instanceof AbsListView) {
+            return new AbsListViewFlingWatcher(mListener, (AbsListView) view);
+        } else if (view instanceof ScrollView) {
+            return new ScrollViewFlingWatcher(mListener, (ScrollView) view);
+        } else if (view instanceof NestedScrollView) {
+            return new NestedScrollViewFlingWatcher(mListener, (NestedScrollView) view);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java b/wear/src/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
new file mode 100644
index 0000000..ca95ab2
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.v4.widget.NestedScrollView;
+import android.support.v4.widget.NestedScrollView.OnScrollChangeListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingWatcher;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * {@link FlingWatcher} implementation for {@link NestedScrollView NestedScrollViews}.
+ * <p>
+ * Because {@link NestedScrollView} does not provide a way to listen to the scroll state, there's no
+ * callback which definitely indicates the fling has finished. So, we instead listen for scroll
+ * events. If we reach the top or bottom of the view or if there are no events within {@link
+ * #MAX_WAIT_TIME_MS}, we assume the fling has finished.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+class NestedScrollViewFlingWatcher implements FlingWatcher, OnScrollChangeListener {
+
+    static final int MAX_WAIT_TIME_MS = 100;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final FlingListener mListener;
+    private final WeakReference<NestedScrollView> mNestedScrollView;
+    private final Runnable mNotifyListenerRunnable = new Runnable() {
+        @Override
+        public void run() {
+            onEndOfFlingFound();
+        }
+    };
+
+    NestedScrollViewFlingWatcher(FlingListener listener, NestedScrollView nestedScrollView) {
+        mListener = listener;
+        mNestedScrollView = new WeakReference<>(nestedScrollView);
+    }
+
+    private static boolean isViewAtTopOrBottom(View view) {
+        return !view.canScrollVertically(-1 /* up */) || !view.canScrollVertically(1 /* down */);
+    }
+
+    @Override
+    public void watch() {
+        NestedScrollView nestedScrollView = mNestedScrollView.get();
+        if (nestedScrollView != null) {
+            nestedScrollView.setOnScrollChangeListener(this);
+            scheduleNext();
+        }
+    }
+
+    @Override
+    public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX,
+                               int oldScrollY) {
+        if (isViewAtTopOrBottom(v)) {
+            onEndOfFlingFound();
+        } else {
+            scheduleNext();
+        }
+    }
+
+    private void onEndOfFlingFound() {
+        mMainThreadHandler.removeCallbacks(mNotifyListenerRunnable);
+        NestedScrollView nestedScrollView = mNestedScrollView.get();
+        if (nestedScrollView != null) {
+            nestedScrollView.setOnScrollChangeListener((OnScrollChangeListener) null);
+            mListener.onFlingComplete(nestedScrollView);
+        }
+    }
+
+    private void scheduleNext() {
+        mMainThreadHandler.removeCallbacks(mNotifyListenerRunnable);
+        mMainThreadHandler.postDelayed(mNotifyListenerRunnable, MAX_WAIT_TIME_MS);
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java b/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java
new file mode 100644
index 0000000..99c7c09
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java
@@ -0,0 +1,627 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.animation.Animator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.support.wear.R;
+import android.support.wear.widget.SimpleAnimatorListener;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A page indicator for {@link ViewPager} based on {@link
+ * android.support.wear.view.DotsPageIndicator} which identifies the current page in relation to
+ * all available pages. Pages are represented as dots. The current page can be highlighted with a
+ * different color or size dot.
+ *
+ * <p>The default behavior is to fade out the dots when the pager is idle (not settling or being
+ * dragged). This can be changed with {@link #setDotFadeWhenIdle(boolean)}.
+ *
+ * <p>Use {@link #setPager(ViewPager)} to connect this view to a pager instance.
+ *
+ * @hide
+ */
+@RequiresApi(Build.VERSION_CODES.M)
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class PageIndicatorView extends View implements OnPageChangeListener {
+
+    private static final String TAG = "Dots";
+    private final Paint mDotPaint;
+    private final Paint mDotPaintShadow;
+    private final Paint mDotPaintSelected;
+    private final Paint mDotPaintShadowSelected;
+    private int mDotSpacing;
+    private float mDotRadius;
+    private float mDotRadiusSelected;
+    private int mDotColor;
+    private int mDotColorSelected;
+    private boolean mDotFadeWhenIdle;
+    private int mDotFadeOutDelay;
+    private int mDotFadeOutDuration;
+    private int mDotFadeInDuration;
+    private float mDotShadowDx;
+    private float mDotShadowDy;
+    private float mDotShadowRadius;
+    private int mDotShadowColor;
+    private PagerAdapter mAdapter;
+    private int mNumberOfPositions;
+    private int mSelectedPosition;
+    private int mCurrentViewPagerState;
+    private boolean mVisible;
+
+    public PageIndicatorView(Context context) {
+        this(context, null);
+    }
+
+    public PageIndicatorView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PageIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        final TypedArray a =
+                getContext()
+                        .obtainStyledAttributes(
+                                attrs, R.styleable.PageIndicatorView, defStyleAttr,
+                                R.style.WsPageIndicatorViewStyle);
+
+        mDotSpacing = a.getDimensionPixelOffset(
+                R.styleable.PageIndicatorView_wsPageIndicatorDotSpacing, 0);
+        mDotRadius = a.getDimension(R.styleable.PageIndicatorView_wsPageIndicatorDotRadius, 0);
+        mDotRadiusSelected =
+                a.getDimension(R.styleable.PageIndicatorView_wsPageIndicatorDotRadiusSelected, 0);
+        mDotColor = a.getColor(R.styleable.PageIndicatorView_wsPageIndicatorDotColor, 0);
+        mDotColorSelected = a
+                .getColor(R.styleable.PageIndicatorView_wsPageIndicatorDotColorSelected, 0);
+        mDotFadeOutDelay =
+                a.getInt(R.styleable.PageIndicatorView_wsPageIndicatorDotFadeOutDelay, 0);
+        mDotFadeOutDuration =
+                a.getInt(R.styleable.PageIndicatorView_wsPageIndicatorDotFadeOutDuration, 0);
+        mDotFadeInDuration =
+                a.getInt(R.styleable.PageIndicatorView_wsPageIndicatorDotFadeInDuration, 0);
+        mDotFadeWhenIdle =
+                a.getBoolean(R.styleable.PageIndicatorView_wsPageIndicatorDotFadeWhenIdle, false);
+        mDotShadowDx = a.getDimension(R.styleable.PageIndicatorView_wsPageIndicatorDotShadowDx, 0);
+        mDotShadowDy = a.getDimension(R.styleable.PageIndicatorView_wsPageIndicatorDotShadowDy, 0);
+        mDotShadowRadius =
+                a.getDimension(R.styleable.PageIndicatorView_wsPageIndicatorDotShadowRadius, 0);
+        mDotShadowColor =
+                a.getColor(R.styleable.PageIndicatorView_wsPageIndicatorDotShadowColor, 0);
+        a.recycle();
+
+        mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mDotPaint.setColor(mDotColor);
+        mDotPaint.setStyle(Style.FILL);
+
+        mDotPaintSelected = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mDotPaintSelected.setColor(mDotColorSelected);
+        mDotPaintSelected.setStyle(Style.FILL);
+        mDotPaintShadow = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mDotPaintShadowSelected = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        mCurrentViewPagerState = ViewPager.SCROLL_STATE_IDLE;
+        if (isInEditMode()) {
+            // When displayed in layout preview:
+            // Simulate 5 positions, currently on the 3rd position.
+            mNumberOfPositions = 5;
+            mSelectedPosition = 2;
+            mDotFadeWhenIdle = false;
+        }
+
+        if (mDotFadeWhenIdle) {
+            mVisible = false;
+            animate().alpha(0f).setStartDelay(2000).setDuration(mDotFadeOutDuration).start();
+        } else {
+            animate().cancel();
+            setAlpha(1.0f);
+        }
+        updateShadows();
+    }
+
+    private void updateShadows() {
+        updateDotPaint(
+                mDotPaint, mDotPaintShadow, mDotRadius, mDotShadowRadius, mDotColor,
+                mDotShadowColor);
+        updateDotPaint(
+                mDotPaintSelected,
+                mDotPaintShadowSelected,
+                mDotRadiusSelected,
+                mDotShadowRadius,
+                mDotColorSelected,
+                mDotShadowColor);
+    }
+
+    private void updateDotPaint(
+            Paint dotPaint,
+            Paint shadowPaint,
+            float baseRadius,
+            float shadowRadius,
+            int color,
+            int shadowColor) {
+        float radius = baseRadius + shadowRadius;
+        float shadowStart = baseRadius / radius;
+        Shader gradient =
+                new RadialGradient(
+                        0,
+                        0,
+                        radius,
+                        new int[]{shadowColor, shadowColor, Color.TRANSPARENT},
+                        new float[]{0f, shadowStart, 1f},
+                        TileMode.CLAMP);
+
+        shadowPaint.setShader(gradient);
+        dotPaint.setColor(color);
+        dotPaint.setStyle(Style.FILL);
+    }
+
+    /**
+     * Supplies the ViewPager instance, and attaches this views {@link OnPageChangeListener} to the
+     * pager.
+     *
+     * @param pager the pager for the page indicator
+     */
+    public void setPager(ViewPager pager) {
+        pager.addOnPageChangeListener(this);
+        setPagerAdapter(pager.getAdapter());
+        mAdapter = pager.getAdapter();
+        if (mAdapter != null && mAdapter.getCount() > 0) {
+            positionChanged(0);
+        }
+    }
+
+    /**
+     * Gets the center-to-center distance between page dots.
+     *
+     * @return the distance between page dots
+     */
+    public float getDotSpacing() {
+        return mDotSpacing;
+    }
+
+    /**
+     * Sets the center-to-center distance between page dots.
+     *
+     * @param spacing the distance between page dots
+     */
+    public void setDotSpacing(int spacing) {
+        if (mDotSpacing != spacing) {
+            mDotSpacing = spacing;
+            requestLayout();
+        }
+    }
+
+    /**
+     * Gets the radius of the page dots.
+     *
+     * @return the radius of the page dots
+     */
+    public float getDotRadius() {
+        return mDotRadius;
+    }
+
+    /**
+     * Sets the radius of the page dots.
+     *
+     * @param radius the radius of the page dots
+     */
+    public void setDotRadius(int radius) {
+        if (mDotRadius != radius) {
+            mDotRadius = radius;
+            updateShadows();
+            invalidate();
+        }
+    }
+
+    /**
+     * Gets the radius of the page dot for the selected page.
+     *
+     * @return the radius of the selected page dot
+     */
+    public float getDotRadiusSelected() {
+        return mDotRadiusSelected;
+    }
+
+    /**
+     * Sets the radius of the page dot for the selected page.
+     *
+     * @param radius the radius of the selected page dot
+     */
+    public void setDotRadiusSelected(int radius) {
+        if (mDotRadiusSelected != radius) {
+            mDotRadiusSelected = radius;
+            updateShadows();
+            invalidate();
+        }
+    }
+
+    /**
+     * Returns the color used for dots other than the selected page.
+     *
+     * @return color the color used for dots other than the selected page
+     */
+    public int getDotColor() {
+        return mDotColor;
+    }
+
+    /**
+     * Sets the color used for dots other than the selected page.
+     *
+     * @param color the color used for dots other than the selected page
+     */
+    public void setDotColor(int color) {
+        if (mDotColor != color) {
+            mDotColor = color;
+            invalidate();
+        }
+    }
+
+    /**
+     * Returns the color of the dot for the selected page.
+     *
+     * @return the color used for the selected page dot
+     */
+    public int getDotColorSelected() {
+        return mDotColorSelected;
+    }
+
+    /**
+     * Sets the color of the dot for the selected page.
+     *
+     * @param color the color of the dot for the selected page
+     */
+    public void setDotColorSelected(int color) {
+        if (mDotColorSelected != color) {
+            mDotColorSelected = color;
+            invalidate();
+        }
+    }
+
+    /**
+     * Indicates if the dots fade out when the pager is idle.
+     *
+     * @return whether the dots fade out when idle
+     */
+    public boolean getDotFadeWhenIdle() {
+        return mDotFadeWhenIdle;
+    }
+
+    /**
+     * Sets whether the dots fade out when the pager is idle.
+     *
+     * @param fade whether the dots fade out when idle
+     */
+    public void setDotFadeWhenIdle(boolean fade) {
+        mDotFadeWhenIdle = fade;
+        if (!fade) {
+            fadeIn();
+        }
+    }
+
+    /**
+     * Returns the duration of fade out animation, in milliseconds.
+     *
+     * @return the duration of the fade out animation, in milliseconds
+     */
+    public int getDotFadeOutDuration() {
+        return mDotFadeOutDuration;
+    }
+
+    /**
+     * Sets the duration of the fade out animation.
+     *
+     * @param duration the duration of the fade out animation
+     */
+    public void setDotFadeOutDuration(int duration, TimeUnit unit) {
+        mDotFadeOutDuration = (int) TimeUnit.MILLISECONDS.convert(duration, unit);
+    }
+
+    /**
+     * Returns the duration of the fade in duration, in milliseconds.
+     *
+     * @return the duration of the fade in duration, in milliseconds
+     */
+    public int getDotFadeInDuration() {
+        return mDotFadeInDuration;
+    }
+
+    /**
+     * Sets the duration of the fade in animation.
+     *
+     * @param duration the duration of the fade in animation
+     */
+    public void setDotFadeInDuration(int duration, TimeUnit unit) {
+        mDotFadeInDuration = (int) TimeUnit.MILLISECONDS.convert(duration, unit);
+    }
+
+    /**
+     * Sets the delay between the pager arriving at an idle state, and the fade out animation
+     * beginning, in milliseconds.
+     *
+     * @return the delay before the fade out animation begins, in milliseconds
+     */
+    public int getDotFadeOutDelay() {
+        return mDotFadeOutDelay;
+    }
+
+    /**
+     * Sets the delay between the pager arriving at an idle state, and the fade out animation
+     * beginning, in milliseconds.
+     *
+     * @param delay the delay before the fade out animation begins, in milliseconds
+     */
+    public void setDotFadeOutDelay(int delay) {
+        mDotFadeOutDelay = delay;
+    }
+
+    /**
+     * Sets the pixel radius of shadows drawn beneath the dots.
+     *
+     * @return the pixel radius of shadows rendered beneath the dots
+     */
+    public float getDotShadowRadius() {
+        return mDotShadowRadius;
+    }
+
+    /**
+     * Sets the pixel radius of shadows drawn beneath the dots.
+     *
+     * @param radius the pixel radius of shadows rendered beneath the dots
+     */
+    public void setDotShadowRadius(float radius) {
+        if (mDotShadowRadius != radius) {
+            mDotShadowRadius = radius;
+            updateShadows();
+            invalidate();
+        }
+    }
+
+    /**
+     * Returns the horizontal offset of shadows drawn beneath the dots.
+     *
+     * @return the horizontal offset of shadows drawn beneath the dots
+     */
+    public float getDotShadowDx() {
+        return mDotShadowDx;
+    }
+
+    /**
+     * Sets the horizontal offset of shadows drawn beneath the dots.
+     *
+     * @param dx the horizontal offset of shadows drawn beneath the dots
+     */
+    public void setDotShadowDx(float dx) {
+        mDotShadowDx = dx;
+        invalidate();
+    }
+
+    /**
+     * Returns the vertical offset of shadows drawn beneath the dots.
+     *
+     * @return the vertical offset of shadows drawn beneath the dots
+     */
+    public float getDotShadowDy() {
+        return mDotShadowDy;
+    }
+
+    /**
+     * Sets the vertical offset of shadows drawn beneath the dots.
+     *
+     * @param dy the vertical offset of shadows drawn beneath the dots
+     */
+    public void setDotShadowDy(float dy) {
+        mDotShadowDy = dy;
+        invalidate();
+    }
+
+    /**
+     * Returns the color of the shadows drawn beneath the dots.
+     *
+     * @return the color of the shadows drawn beneath the dots
+     */
+    public int getDotShadowColor() {
+        return mDotShadowColor;
+    }
+
+    /**
+     * Sets the color of the shadows drawn beneath the dots.
+     *
+     * @param color the color of the shadows drawn beneath the dots
+     */
+    public void setDotShadowColor(int color) {
+        mDotShadowColor = color;
+        updateShadows();
+        invalidate();
+    }
+
+    private void positionChanged(int position) {
+        mSelectedPosition = position;
+        invalidate();
+    }
+
+    private void updateNumberOfPositions() {
+        int count = mAdapter.getCount();
+        if (count != mNumberOfPositions) {
+            mNumberOfPositions = count;
+            requestLayout();
+        }
+    }
+
+    private void fadeIn() {
+        mVisible = true;
+        animate().cancel();
+        animate().alpha(1f).setStartDelay(0).setDuration(mDotFadeInDuration).start();
+    }
+
+    private void fadeOut(long delayMillis) {
+        mVisible = false;
+        animate().cancel();
+        animate().alpha(0f).setStartDelay(delayMillis).setDuration(mDotFadeOutDuration).start();
+    }
+
+    private void fadeInOut() {
+        mVisible = true;
+        animate().cancel();
+        animate()
+                .alpha(1f)
+                .setStartDelay(0)
+                .setDuration(mDotFadeInDuration)
+                .setListener(
+                        new SimpleAnimatorListener() {
+                            @Override
+                            public void onAnimationComplete(Animator animator) {
+                                mVisible = false;
+                                animate()
+                                        .alpha(0f)
+                                        .setListener(null)
+                                        .setStartDelay(mDotFadeOutDelay)
+                                        .setDuration(mDotFadeOutDuration)
+                                        .start();
+                            }
+                        })
+                .start();
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        if (mDotFadeWhenIdle) {
+            if (mCurrentViewPagerState == ViewPager.SCROLL_STATE_DRAGGING) {
+                if (positionOffset != 0) {
+                    if (!mVisible) {
+                        fadeIn();
+                    }
+                } else {
+                    if (mVisible) {
+                        fadeOut(0);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        if (position != mSelectedPosition) {
+            positionChanged(position);
+        }
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+        if (mCurrentViewPagerState != state) {
+            mCurrentViewPagerState = state;
+            if (mDotFadeWhenIdle) {
+                if (state == ViewPager.SCROLL_STATE_IDLE) {
+                    if (mVisible) {
+                        fadeOut(mDotFadeOutDelay);
+                    } else {
+                        fadeInOut();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the {@link PagerAdapter}.
+     */
+    public void setPagerAdapter(PagerAdapter adapter) {
+        mAdapter = adapter;
+        if (mAdapter != null) {
+            updateNumberOfPositions();
+            if (mDotFadeWhenIdle) {
+                fadeInOut();
+            }
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int totalWidth;
+        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
+            totalWidth = MeasureSpec.getSize(widthMeasureSpec);
+        } else {
+            int contentWidth = mNumberOfPositions * mDotSpacing;
+            totalWidth = contentWidth + getPaddingLeft() + getPaddingRight();
+        }
+        int totalHeight;
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
+            totalHeight = MeasureSpec.getSize(heightMeasureSpec);
+        } else {
+            float maxRadius =
+                    Math.max(mDotRadius + mDotShadowRadius, mDotRadiusSelected + mDotShadowRadius);
+            int contentHeight = (int) Math.ceil(maxRadius * 2);
+            contentHeight = (int) (contentHeight + mDotShadowDy);
+            totalHeight = contentHeight + getPaddingTop() + getPaddingBottom();
+        }
+        setMeasuredDimension(
+                resolveSizeAndState(totalWidth, widthMeasureSpec, 0),
+                resolveSizeAndState(totalHeight, heightMeasureSpec, 0));
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mNumberOfPositions > 1) {
+            float dotCenterLeft = getPaddingLeft() + (mDotSpacing / 2f);
+            float dotCenterTop = getHeight() / 2f;
+            canvas.save();
+            canvas.translate(dotCenterLeft, dotCenterTop);
+            for (int i = 0; i < mNumberOfPositions; i++) {
+                if (i == mSelectedPosition) {
+                    float radius = mDotRadiusSelected + mDotShadowRadius;
+                    canvas.drawCircle(mDotShadowDx, mDotShadowDy, radius, mDotPaintShadowSelected);
+                    canvas.drawCircle(0, 0, mDotRadiusSelected, mDotPaintSelected);
+                } else {
+                    float radius = mDotRadius + mDotShadowRadius;
+                    canvas.drawCircle(mDotShadowDx, mDotShadowDy, radius, mDotPaintShadow);
+                    canvas.drawCircle(0, 0, mDotRadius, mDotPaint);
+                }
+                canvas.translate(mDotSpacing, 0);
+            }
+            canvas.restore();
+        }
+    }
+
+    /**
+     * Notifies the view that the data set has changed.
+     */
+    public void notifyDataSetChanged() {
+        if (mAdapter != null && mAdapter.getCount() > 0) {
+            updateNumberOfPositions();
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java b/wear/src/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
new file mode 100644
index 0000000..7570fae
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingWatcher;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * {@link FlingWatcher} implementation for {@link RecyclerView RecyclerViews}. Detects the end of
+ * a Fling by waiting until the scroll state becomes idle.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+class RecyclerViewFlingWatcher extends OnScrollListener implements FlingWatcher {
+
+    private final FlingListener mListener;
+    private final WeakReference<RecyclerView> mRecyclerView;
+
+    RecyclerViewFlingWatcher(FlingListener listener, RecyclerView view) {
+        mListener = listener;
+        mRecyclerView = new WeakReference<>(view);
+    }
+
+    @Override
+    public void watch() {
+        RecyclerView recyclerView = mRecyclerView.get();
+        if (recyclerView != null) {
+            recyclerView.addOnScrollListener(this);
+        }
+    }
+
+    @Override
+    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+            mListener.onFlingComplete(recyclerView);
+            recyclerView.removeOnScrollListener(this);
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java b/wear/src/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
new file mode 100644
index 0000000..f0b973b
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingWatcher;
+import android.view.View;
+import android.view.View.OnScrollChangeListener;
+import android.widget.ScrollView;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * {@link FlingWatcher} implementation for {@link ScrollView ScrollViews}.
+ * <p>
+ * Because {@link ScrollView} does not provide a way to listen to the scroll state, there's no
+ * callback which definitely indicates the fling has finished. So, we instead listen for scroll
+ * events. If we reach the top or bottom of the view or if there are no events within {@link
+ * #MAX_WAIT_TIME_MS}, we assume the fling has finished.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+class ScrollViewFlingWatcher implements FlingWatcher, OnScrollChangeListener {
+
+    static final int MAX_WAIT_TIME_MS = 100;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final FlingListener mListener;
+    private final WeakReference<ScrollView> mScrollView;
+    private final Runnable mNotifyListenerRunnable = new Runnable() {
+        @Override
+        public void run() {
+            onEndOfFlingFound();
+        }
+    };
+
+    ScrollViewFlingWatcher(FlingListener listener, ScrollView scrollView) {
+        mListener = listener;
+        mScrollView = new WeakReference<>(scrollView);
+    }
+
+    private static boolean isViewAtTopOrBottom(View view) {
+        return !view.canScrollVertically(-1 /* up */) || !view.canScrollVertically(1 /* down */);
+    }
+
+    @Override
+    public void watch() {
+        ScrollView scrollView = mScrollView.get();
+        if (scrollView != null) {
+            scrollView.setOnScrollChangeListener(this);
+            scheduleNext();
+        }
+    }
+
+    @Override
+    public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX,
+            int oldScrollY) {
+        if (isViewAtTopOrBottom(v)) {
+            onEndOfFlingFound();
+        } else {
+            scheduleNext();
+        }
+    }
+
+    private void onEndOfFlingFound() {
+        mMainThreadHandler.removeCallbacks(mNotifyListenerRunnable);
+
+        ScrollView scrollView = mScrollView.get();
+        if (scrollView != null) {
+            scrollView.setOnScrollChangeListener(null);
+            mListener.onFlingComplete(scrollView);
+        }
+    }
+
+    private void scheduleNext() {
+        mMainThreadHandler.removeCallbacks(mNotifyListenerRunnable);
+        mMainThreadHandler.postDelayed(mNotifyListenerRunnable, MAX_WAIT_TIME_MS);
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/WearableActionDrawerMenu.java b/wear/src/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
new file mode 100644
index 0000000..158467d
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.view.ActionProvider;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.M)
+/* package */ class WearableActionDrawerMenu implements Menu {
+
+    private final Context mContext;
+    private final List<WearableActionDrawerMenuItem> mItems = new ArrayList<>();
+    private final WearableActionDrawerMenuListener mListener;
+    private final WearableActionDrawerMenuItem.MenuItemChangedListener mItemChangedListener =
+            new WearableActionDrawerMenuItem.MenuItemChangedListener() {
+                @Override
+                public void itemChanged(WearableActionDrawerMenuItem item) {
+                    for (int i = 0; i < mItems.size(); i++) {
+                        if (mItems.get(i) == item) {
+                            mListener.menuItemChanged(i);
+                        }
+                    }
+                }
+            };
+
+    WearableActionDrawerMenu(Context context, WearableActionDrawerMenuListener listener) {
+        mContext = context;
+        mListener = listener;
+    }
+
+    @Override
+    public MenuItem add(CharSequence title) {
+        return add(0, 0, 0, title);
+    }
+
+    @Override
+    public MenuItem add(int titleRes) {
+        return add(0, 0, 0, titleRes);
+    }
+
+    @Override
+    public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+        return add(groupId, itemId, order, mContext.getResources().getString(titleRes));
+    }
+
+    @Override
+    public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+        WearableActionDrawerMenuItem item =
+                new WearableActionDrawerMenuItem(mContext, itemId, title, mItemChangedListener);
+        mItems.add(item);
+        mListener.menuItemAdded(mItems.size() - 1);
+        return item;
+    }
+
+    @Override
+    public void clear() {
+        mItems.clear();
+        mListener.menuChanged();
+    }
+
+    @Override
+    public void removeItem(int id) {
+        int index = findItemIndex(id);
+        if ((index < 0) || (index >= mItems.size())) {
+            return;
+        }
+        mItems.remove(index);
+        mListener.menuItemRemoved(index);
+    }
+
+    @Override
+    public MenuItem findItem(int id) {
+        int index = findItemIndex(id);
+        if ((index < 0) || (index >= mItems.size())) {
+            return null;
+        }
+        return mItems.get(index);
+    }
+
+    @Override
+    public int size() {
+        return mItems.size();
+    }
+
+    @Override
+    @Nullable
+    public MenuItem getItem(int index) {
+        if ((index < 0) || (index >= mItems.size())) {
+            return null;
+        }
+        return mItems.get(index);
+    }
+
+    private int findItemIndex(int id) {
+        final List<WearableActionDrawerMenuItem> items = mItems;
+        final int itemCount = items.size();
+        for (int i = 0; i < itemCount; i++) {
+            if (items.get(i).getItemId() == id) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    @Override
+    public void close() {
+        throw new UnsupportedOperationException("close is not implemented");
+    }
+
+    @Override
+    public SubMenu addSubMenu(CharSequence title) {
+        throw new UnsupportedOperationException("addSubMenu is not implemented");
+    }
+
+    @Override
+    public SubMenu addSubMenu(int titleRes) {
+        throw new UnsupportedOperationException("addSubMenu is not implemented");
+    }
+
+    @Override
+    public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
+        throw new UnsupportedOperationException("addSubMenu is not implemented");
+    }
+
+    @Override
+    public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+        throw new UnsupportedOperationException("addSubMenu is not implemented");
+    }
+
+    @Override
+    public int addIntentOptions(
+            int groupId,
+            int itemId,
+            int order,
+            ComponentName caller,
+            Intent[] specifics,
+            Intent intent,
+            int flags,
+            MenuItem[] outSpecificItems) {
+        throw new UnsupportedOperationException("addIntentOptions is not implemented");
+    }
+
+    @Override
+    public void removeGroup(int groupId) {
+    }
+
+    @Override
+    public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
+        throw new UnsupportedOperationException("setGroupCheckable is not implemented");
+    }
+
+    @Override
+    public void setGroupVisible(int group, boolean visible) {
+        throw new UnsupportedOperationException("setGroupVisible is not implemented");
+    }
+
+    @Override
+    public void setGroupEnabled(int group, boolean enabled) {
+        throw new UnsupportedOperationException("setGroupEnabled is not implemented");
+    }
+
+    @Override
+    public boolean hasVisibleItems() {
+        return false;
+    }
+
+    @Override
+    public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+        throw new UnsupportedOperationException("performShortcut is not implemented");
+    }
+
+    @Override
+    public boolean isShortcutKey(int keyCode, KeyEvent event) {
+        return false;
+    }
+
+    @Override
+    public boolean performIdentifierAction(int id, int flags) {
+        throw new UnsupportedOperationException("performIdentifierAction is not implemented");
+    }
+
+    @Override
+    public void setQwertyMode(boolean isQwerty) {
+    }
+
+    /* package */ interface WearableActionDrawerMenuListener {
+
+        void menuItemChanged(int position);
+
+        void menuItemAdded(int position);
+
+        void menuItemRemoved(int position);
+
+        void menuChanged();
+    }
+
+    public static final class WearableActionDrawerMenuItem implements MenuItem {
+
+        private final int mId;
+
+        private final Context mContext;
+        private final MenuItemChangedListener mItemChangedListener;
+        private CharSequence mTitle;
+        private Drawable mIconDrawable;
+        private MenuItem.OnMenuItemClickListener mClickListener;
+
+        WearableActionDrawerMenuItem(
+                Context context, int id, CharSequence title, MenuItemChangedListener listener) {
+            mContext = context;
+            mId = id;
+            mTitle = title;
+            mItemChangedListener = listener;
+        }
+
+        @Override
+        public int getItemId() {
+            return mId;
+        }
+
+        @Override
+        public MenuItem setTitle(CharSequence title) {
+            mTitle = title;
+            if (mItemChangedListener != null) {
+                mItemChangedListener.itemChanged(this);
+            }
+            return this;
+        }
+
+        @Override
+        public MenuItem setTitle(int title) {
+            return setTitle(mContext.getResources().getString(title));
+        }
+
+        @Override
+        public CharSequence getTitle() {
+            return mTitle;
+        }
+
+        @Override
+        public MenuItem setIcon(Drawable icon) {
+            mIconDrawable = icon;
+            if (mItemChangedListener != null) {
+                mItemChangedListener.itemChanged(this);
+            }
+            return this;
+        }
+
+        @Override
+        public MenuItem setIcon(int iconRes) {
+            return setIcon(mContext.getResources().getDrawable(iconRes));
+        }
+
+        @Override
+        public Drawable getIcon() {
+            return mIconDrawable;
+        }
+
+        @Override
+        public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+            mClickListener = menuItemClickListener;
+            return this;
+        }
+
+        @Override
+        public int getGroupId() {
+            return 0;
+        }
+
+        @Override
+        public int getOrder() {
+            return 0;
+        }
+
+        @Override
+        public MenuItem setTitleCondensed(CharSequence title) {
+            return this;
+        }
+
+        @Override
+        public CharSequence getTitleCondensed() {
+            return null;
+        }
+
+        @Override
+        public MenuItem setIntent(Intent intent) {
+            throw new UnsupportedOperationException("setIntent is not implemented");
+        }
+
+        @Override
+        public Intent getIntent() {
+            return null;
+        }
+
+        @Override
+        public MenuItem setShortcut(char numericChar, char alphaChar) {
+            throw new UnsupportedOperationException("setShortcut is not implemented");
+        }
+
+        @Override
+        public MenuItem setNumericShortcut(char numericChar) {
+            return this;
+        }
+
+        @Override
+        public char getNumericShortcut() {
+            return 0;
+        }
+
+        @Override
+        public MenuItem setAlphabeticShortcut(char alphaChar) {
+            return this;
+        }
+
+        @Override
+        public char getAlphabeticShortcut() {
+            return 0;
+        }
+
+        @Override
+        public MenuItem setCheckable(boolean checkable) {
+            return this;
+        }
+
+        @Override
+        public boolean isCheckable() {
+            return false;
+        }
+
+        @Override
+        public MenuItem setChecked(boolean checked) {
+            return this;
+        }
+
+        @Override
+        public boolean isChecked() {
+            return false;
+        }
+
+        @Override
+        public MenuItem setVisible(boolean visible) {
+            return this;
+        }
+
+        @Override
+        public boolean isVisible() {
+            return false;
+        }
+
+        @Override
+        public MenuItem setEnabled(boolean enabled) {
+            return this;
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean hasSubMenu() {
+            return false;
+        }
+
+        @Override
+        public SubMenu getSubMenu() {
+            return null;
+        }
+
+        @Override
+        public ContextMenu.ContextMenuInfo getMenuInfo() {
+            return null;
+        }
+
+        @Override
+        public void setShowAsAction(int actionEnum) {
+            throw new UnsupportedOperationException("setShowAsAction is not implemented");
+        }
+
+        @Override
+        public MenuItem setShowAsActionFlags(int actionEnum) {
+            throw new UnsupportedOperationException("setShowAsActionFlags is not implemented");
+        }
+
+        @Override
+        public MenuItem setActionView(View view) {
+            throw new UnsupportedOperationException("setActionView is not implemented");
+        }
+
+        @Override
+        public MenuItem setActionView(int resId) {
+            throw new UnsupportedOperationException("setActionView is not implemented");
+        }
+
+        @Override
+        public View getActionView() {
+            return null;
+        }
+
+        @Override
+        public MenuItem setActionProvider(ActionProvider actionProvider) {
+            throw new UnsupportedOperationException("setActionProvider is not implemented");
+        }
+
+        @Override
+        public ActionProvider getActionProvider() {
+            return null;
+        }
+
+        @Override
+        public boolean expandActionView() {
+            throw new UnsupportedOperationException("expandActionView is not implemented");
+        }
+
+        @Override
+        public boolean collapseActionView() {
+            throw new UnsupportedOperationException("collapseActionView is not implemented");
+        }
+
+        @Override
+        public boolean isActionViewExpanded() {
+            throw new UnsupportedOperationException("isActionViewExpanded is not implemented");
+        }
+
+        @Override
+        public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+            throw new UnsupportedOperationException("setOnActionExpandListener is not implemented");
+        }
+
+        /**
+         * Invokes the item by calling the listener if set.
+         *
+         * @return true if the invocation was handled, false otherwise
+         */
+    /* package */ boolean invoke() {
+            return mClickListener != null && mClickListener.onMenuItemClick(this);
+
+        }
+
+        private interface MenuItemChangedListener {
+
+            void itemChanged(WearableActionDrawerMenuItem item);
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java b/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java
new file mode 100644
index 0000000..994b804
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java
@@ -0,0 +1,461 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.R;
+import android.support.wear.internal.widget.ResourcesUtil;
+import android.support.wear.widget.drawer.WearableActionDrawerMenu.WearableActionDrawerMenuItem;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * Ease of use class for creating a Wearable action drawer. This can be used with {@link
+ * WearableDrawerLayout} to create a drawer for users to easily pull up contextual actions. These
+ * contextual actions may be specified by using a {@link Menu}, which may be populated by either:
+ *
+ * <ul> <li>Specifying the {@code app:actionMenu} attribute in the XML layout file. Example:
+ * <pre>
+ * &lt;android.support.wear.widget.drawer.WearableActionDrawerView
+ *     xmlns:app="http://schemas.android.com/apk/res-auto"
+ *     android:layout_width=”match_parent”
+ *     android:layout_height=”match_parent”
+ *     app:actionMenu="@menu/action_drawer" /&gt;</pre>
+ *
+ * <li>Getting the menu with {@link #getMenu}, and then inflating it with {@link
+ * MenuInflater#inflate}. Example:
+ * <pre>
+ * Menu menu = actionDrawer.getMenu();
+ * getMenuInflater().inflate(R.menu.action_drawer, menu);</pre>
+ *
+ * </ul>
+ *
+ * <p><b>The full {@link Menu} and {@link MenuItem} APIs are not implemented.</b> The following
+ * methods are guaranteed to work:
+ *
+ * <p>For {@link Menu}, the add methods, {@link Menu#clear}, {@link Menu#removeItem}, {@link
+ * Menu#findItem}, {@link Menu#size}, and {@link Menu#getItem} are implemented.
+ *
+ * <p>For {@link MenuItem}, setting and getting the title and icon, {@link MenuItem#getItemId}, and
+ * {@link MenuItem#setOnMenuItemClickListener} are implemented.
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class WearableActionDrawerView extends WearableDrawerView {
+
+    private static final String TAG = "WearableActionDrawer";
+
+    private final RecyclerView mActionList;
+    private final int mTopPadding;
+    private final int mBottomPadding;
+    private final int mLeftPadding;
+    private final int mRightPadding;
+    private final int mFirstItemTopPadding;
+    private final int mLastItemBottomPadding;
+    private final int mIconRightMargin;
+    private final boolean mShowOverflowInPeek;
+    @Nullable private final ImageView mPeekActionIcon;
+    @Nullable private final ImageView mPeekExpandIcon;
+    private final RecyclerView.Adapter<RecyclerView.ViewHolder> mActionListAdapter;
+    private OnMenuItemClickListener mOnMenuItemClickListener;
+    private Menu mMenu;
+    @Nullable private CharSequence mTitle;
+
+    public WearableActionDrawerView(Context context) {
+        this(context, null);
+    }
+
+    public WearableActionDrawerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableActionDrawerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WearableActionDrawerView(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        setLockedWhenClosed(true);
+
+        boolean showOverflowInPeek = false;
+        int menuRes = 0;
+        if (attrs != null) {
+            TypedArray typedArray = context.obtainStyledAttributes(
+                    attrs, R.styleable.WearableActionDrawerView, defStyleAttr, 0 /* defStyleRes */);
+
+            try {
+                mTitle = typedArray.getString(R.styleable.WearableActionDrawerView_drawerTitle);
+                showOverflowInPeek = typedArray.getBoolean(
+                        R.styleable.WearableActionDrawerView_showOverflowInPeek, false);
+                menuRes = typedArray
+                        .getResourceId(R.styleable.WearableActionDrawerView_actionMenu, 0);
+            } finally {
+                typedArray.recycle();
+            }
+        }
+
+        AccessibilityManager accessibilityManager =
+                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mShowOverflowInPeek = showOverflowInPeek || accessibilityManager.isEnabled();
+
+        if (!mShowOverflowInPeek) {
+            LayoutInflater layoutInflater = LayoutInflater.from(context);
+            View peekView = layoutInflater.inflate(R.layout.ws_action_drawer_peek_view,
+                    getPeekContainer(), false /* attachToRoot */);
+            setPeekContent(peekView);
+            mPeekActionIcon =
+                    (ImageView) peekView
+                            .findViewById(R.id.ws_action_drawer_peek_action_icon);
+            mPeekExpandIcon =
+                    (ImageView) peekView
+                            .findViewById(R.id.ws_action_drawer_expand_icon);
+        } else {
+            mPeekActionIcon = null;
+            mPeekExpandIcon = null;
+            getPeekContainer().setContentDescription(
+                    context.getString(R.string.ws_action_drawer_content_description));
+        }
+
+        if (menuRes != 0) {
+            // This must occur after initializing mPeekActionIcon, otherwise updatePeekIcons will
+            // exit early.
+            MenuInflater inflater = new MenuInflater(context);
+            inflater.inflate(menuRes, getMenu());
+        }
+
+        int screenWidthPx = ResourcesUtil.getScreenWidthPx(context);
+        int screenHeightPx = ResourcesUtil.getScreenHeightPx(context);
+
+        Resources res = getResources();
+        mTopPadding = res.getDimensionPixelOffset(R.dimen.ws_action_drawer_item_top_padding);
+        mBottomPadding = res.getDimensionPixelOffset(R.dimen.ws_action_drawer_item_bottom_padding);
+        mLeftPadding =
+                ResourcesUtil.getFractionOfScreenPx(
+                        context, screenWidthPx, R.fraction.ws_action_drawer_item_left_padding);
+        mRightPadding =
+                ResourcesUtil.getFractionOfScreenPx(
+                        context, screenWidthPx, R.fraction.ws_action_drawer_item_right_padding);
+
+        mFirstItemTopPadding =
+                ResourcesUtil.getFractionOfScreenPx(
+                        context, screenHeightPx,
+                        R.fraction.ws_action_drawer_item_first_item_top_padding);
+        mLastItemBottomPadding =
+                ResourcesUtil.getFractionOfScreenPx(
+                        context, screenHeightPx,
+                        R.fraction.ws_action_drawer_item_last_item_bottom_padding);
+
+        mIconRightMargin = res
+                .getDimensionPixelOffset(R.dimen.ws_action_drawer_item_icon_right_margin);
+
+        mActionList = new RecyclerView(context);
+        mActionList.setLayoutManager(new LinearLayoutManager(context));
+        mActionListAdapter = new ActionListAdapter(getMenu());
+        mActionList.setAdapter(mActionListAdapter);
+        setDrawerContent(mActionList);
+    }
+
+    @Override
+    public boolean canScrollHorizontally(int direction) {
+        // Prevent the window from being swiped closed while it is open by saying that it can scroll
+        // horizontally.
+        return isOpened();
+    }
+
+    @Override
+    public void onPeekContainerClicked(View v) {
+        if (mShowOverflowInPeek) {
+            super.onPeekContainerClicked(v);
+        } else {
+            onMenuItemClicked(0);
+        }
+    }
+
+    @Override
+  /* package */ int preferGravity() {
+        return Gravity.BOTTOM;
+    }
+
+    /**
+     * Set a {@link OnMenuItemClickListener} for this action drawer.
+     */
+    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
+        mOnMenuItemClickListener = listener;
+    }
+
+    /**
+     * Sets the title for this action drawer. If {@code title} is {@code null}, then the title will
+     * be removed.
+     */
+    public void setTitle(@Nullable CharSequence title) {
+        if (Objects.equals(title, mTitle)) {
+            return;
+        }
+
+        CharSequence oldTitle = mTitle;
+        mTitle = title;
+        if (oldTitle == null) {
+            mActionListAdapter.notifyItemInserted(0);
+        } else if (title == null) {
+            mActionListAdapter.notifyItemRemoved(0);
+        } else {
+            mActionListAdapter.notifyItemChanged(0);
+        }
+    }
+
+    private boolean hasTitle() {
+        return mTitle != null;
+    }
+
+    private void onMenuItemClicked(int position) {
+        if (position >= 0 && position < getMenu().size()) { // Sanity check.
+            WearableActionDrawerMenuItem menuItem =
+                    (WearableActionDrawerMenuItem) getMenu().getItem(position);
+            if (menuItem.invoke()) {
+                return;
+            }
+
+            if (mOnMenuItemClickListener != null) {
+                mOnMenuItemClickListener.onMenuItemClick(menuItem);
+            }
+        }
+    }
+
+    private void updatePeekIcons() {
+        if (mPeekActionIcon == null || mPeekExpandIcon == null) {
+            return;
+        }
+
+        Menu menu = getMenu();
+        int numberOfActions = menu.size();
+
+        // Only show drawer content (and allow it to be opened) when there's more than one action.
+        if (numberOfActions > 1) {
+            setDrawerContent(mActionList);
+            mPeekExpandIcon.setVisibility(VISIBLE);
+        } else {
+            setDrawerContent(null);
+            mPeekExpandIcon.setVisibility(GONE);
+        }
+
+        if (numberOfActions >= 1) {
+            Drawable firstActionDrawable = menu.getItem(0).getIcon();
+            // Because the ImageView will tint the Drawable white, attempt to get a mutable copy of
+            // it. If a copy isn't made, the icon will be white in the expanded state, rendering it
+            // invisible.
+            if (firstActionDrawable != null) {
+                firstActionDrawable = firstActionDrawable.getConstantState().newDrawable().mutate();
+                firstActionDrawable.clearColorFilter();
+            }
+
+            mPeekActionIcon.setImageDrawable(firstActionDrawable);
+            mPeekActionIcon.setContentDescription(menu.getItem(0).getTitle());
+        }
+    }
+
+    /**
+     * Returns the Menu object that this WearableActionDrawer represents.
+     *
+     * <p>Applications should use this method to obtain the WearableActionDrawers's Menu object and
+     * inflate or add content to it as necessary.
+     *
+     * @return the Menu presented by this view
+     */
+    public Menu getMenu() {
+        if (mMenu == null) {
+            mMenu = new WearableActionDrawerMenu(
+                    getContext(),
+                    new WearableActionDrawerMenu.WearableActionDrawerMenuListener() {
+                        @Override
+                        public void menuItemChanged(int position) {
+                            if (mActionListAdapter != null) {
+                                mActionListAdapter.notifyItemChanged(position);
+                            }
+                            if (position == 0) {
+                                updatePeekIcons();
+                            }
+                        }
+
+                        @Override
+                        public void menuItemAdded(int position) {
+                            if (mActionListAdapter != null) {
+                                mActionListAdapter.notifyItemChanged(position);
+                            }
+                            // Handle transitioning from 0->1 items (set peek icon) and
+                            // 1->2 (switch to ellipsis.)
+                            if (position <= 1) {
+                                updatePeekIcons();
+                            }
+                        }
+
+                        @Override
+                        public void menuItemRemoved(int position) {
+                            if (mActionListAdapter != null) {
+                                mActionListAdapter.notifyItemChanged(position);
+                            }
+                            // Handle transitioning from 2->1 items (remove ellipsis), and
+                            // also the removal of item 1, which could cause the peek icon
+                            // to change.
+                            if (position <= 1) {
+                                updatePeekIcons();
+                            }
+                        }
+
+                        @Override
+                        public void menuChanged() {
+                            if (mActionListAdapter != null) {
+                                mActionListAdapter.notifyDataSetChanged();
+                            }
+                            updatePeekIcons();
+                        }
+                    });
+        }
+
+        return mMenu;
+    }
+
+    private static final class TitleViewHolder extends RecyclerView.ViewHolder {
+
+        public final View view;
+        public final TextView textView;
+
+        TitleViewHolder(View view) {
+            super(view);
+            this.view = view;
+            textView = (TextView) view.findViewById(R.id.ws_action_drawer_title);
+        }
+    }
+
+    private final class ActionListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+        public static final int TYPE_ACTION = 0;
+        public static final int TYPE_TITLE = 1;
+        private final Menu mActionMenu;
+        private final View.OnClickListener mItemClickListener =
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        int childPos =
+                                mActionList.getChildAdapterPosition(v) - (hasTitle() ? 1 : 0);
+                        if (childPos == RecyclerView.NO_POSITION) {
+                            Log.w(TAG, "invalid child position");
+                            return;
+                        }
+                        onMenuItemClicked(childPos);
+                    }
+                };
+
+        ActionListAdapter(Menu menu) {
+            mActionMenu = getMenu();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mActionMenu.size() + (hasTitle() ? 1 : 0);
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
+            int titleAwarePosition = hasTitle() ? position - 1 : position;
+            if (viewHolder instanceof ActionItemViewHolder) {
+                ActionItemViewHolder holder = (ActionItemViewHolder) viewHolder;
+                holder.view.setPadding(
+                        mLeftPadding,
+                        position == 0 ? mFirstItemTopPadding : mTopPadding,
+                        mRightPadding,
+                        position == getItemCount() - 1 ? mLastItemBottomPadding : mBottomPadding);
+
+                Drawable icon = mActionMenu.getItem(titleAwarePosition).getIcon();
+                if (icon != null) {
+                    icon = icon.getConstantState().newDrawable().mutate();
+                }
+                CharSequence title = mActionMenu.getItem(titleAwarePosition).getTitle();
+                holder.textView.setText(title);
+                holder.textView.setContentDescription(title);
+                holder.iconView.setContentDescription(title);
+                holder.iconView.setImageDrawable(icon);
+            } else if (viewHolder instanceof TitleViewHolder) {
+                TitleViewHolder holder = (TitleViewHolder) viewHolder;
+                holder.view.setPadding(mLeftPadding, mFirstItemTopPadding, mRightPadding,
+                        mBottomPadding);
+                holder.textView.setText(mTitle);
+            }
+        }
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            switch (viewType) {
+                case TYPE_TITLE:
+                    View titleView =
+                            LayoutInflater.from(parent.getContext())
+                                    .inflate(R.layout.ws_action_drawer_title_view, parent, false);
+                    return new TitleViewHolder(titleView);
+
+                case TYPE_ACTION:
+                default:
+                    View actionView =
+                            LayoutInflater.from(parent.getContext())
+                                    .inflate(R.layout.ws_action_drawer_item_view, parent, false);
+                    actionView.setOnClickListener(mItemClickListener);
+                    return new ActionItemViewHolder(actionView);
+            }
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return hasTitle() && position == 0 ? TYPE_TITLE : TYPE_ACTION;
+        }
+    }
+
+    private final class ActionItemViewHolder extends RecyclerView.ViewHolder {
+
+        public final View view;
+        public final ImageView iconView;
+        public final TextView textView;
+
+        ActionItemViewHolder(View view) {
+            super(view);
+            this.view = view;
+            iconView = (ImageView) view.findViewById(R.id.ws_action_drawer_item_icon);
+            ((LinearLayout.LayoutParams) iconView.getLayoutParams()).setMarginEnd(mIconRightMargin);
+            textView = (TextView) view.findViewById(R.id.ws_action_drawer_item_text);
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/WearableDrawerController.java b/wear/src/android/support/wear/widget/drawer/WearableDrawerController.java
new file mode 100644
index 0000000..f5d69ec
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/WearableDrawerController.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+/**
+ * Provides the ability to manipulate a {@link WearableDrawerView WearableDrawerView's} position
+ * within a {@link WearableDrawerLayout}.
+ */
+public class WearableDrawerController {
+
+    private final WearableDrawerLayout mDrawerLayout;
+    private final WearableDrawerView mDrawerView;
+
+    WearableDrawerController(WearableDrawerLayout drawerLayout, WearableDrawerView drawerView) {
+        mDrawerLayout = drawerLayout;
+        mDrawerView = drawerView;
+    }
+
+    /**
+     * Requests that the {@link WearableDrawerView} be opened.
+     */
+    public void openDrawer() {
+        mDrawerLayout.openDrawer(mDrawerView);
+    }
+
+    /**
+     * Requests that the {@link WearableDrawerView} be closed.
+     */
+    public void closeDrawer() {
+        mDrawerLayout.closeDrawer(mDrawerView);
+    }
+
+    /**
+     * Requests that the {@link WearableDrawerView} be peeked.
+     */
+    public void peekDrawer() {
+        mDrawerLayout.peekDrawer(mDrawerView);
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/WearableDrawerLayout.java b/wear/src/android/support/wear/widget/drawer/WearableDrawerLayout.java
new file mode 100644
index 0000000..6d27064
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/WearableDrawerLayout.java
@@ -0,0 +1,1192 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import static android.support.wear.widget.drawer.WearableDrawerView.STATE_IDLE;
+import static android.support.wear.widget.drawer.WearableDrawerView.STATE_SETTLING;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.view.NestedScrollingParent;
+import android.support.v4.view.NestedScrollingParentHelper;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingListener;
+import android.support.wear.widget.drawer.FlingWatcherFactory.FlingWatcher;
+import android.support.wear.widget.drawer.WearableDrawerView.DrawerState;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.FrameLayout;
+
+/**
+ * Top-level container that allows interactive drawers to be pulled from the top and bottom edge of
+ * the window. For WearableDrawerLayout to work properly, scrolling children must send nested
+ * scrolling events. Views that implement {@link android.support.v4.view.NestedScrollingChild} do
+ * this by default. To enable nested scrolling on frameworks views like {@link
+ * android.widget.ListView}, set <code>android:nestedScrollingEnabled="true"</code> on the view in
+ * the layout file, or call {@link View#setNestedScrollingEnabled} in code. This includes the main
+ * content in a WearableDrawerLayout, as well as the content inside of the drawers.
+ *
+ * <p>To use WearableDrawerLayout with {@link WearableActionDrawerView} or {@link
+ * WearableNavigationDrawerView}, place either drawer in a WearableDrawerLayout.
+ *
+ * <pre>
+ * &lt;android.support.wear.widget.drawer.WearableDrawerLayout [...]&gt;
+ *     &lt;FrameLayout android:id=”@+id/content” /&gt;
+ *
+ *     &lt;android.support.wear.widget.drawer.WearableNavigationDrawerView
+ *         android:layout_width=”match_parent”
+ *         android:layout_height=”match_parent” /&gt;
+ *
+ *     &lt;android.support.wear.widget.drawer.WearableActionDrawerView
+ *         android:layout_width=”match_parent”
+ *         android:layout_height=”match_parent” /&gt;
+ *
+ * &lt;/android.support.wear.widget.drawer.WearableDrawerLayout&gt;</pre>
+ *
+ * <p>To use custom content in a drawer, place {@link WearableDrawerView} in a WearableDrawerLayout
+ * and specify the layout_gravity to pick the drawer location (the following example is for a top
+ * drawer). <b>Note:</b> You must either call {@link WearableDrawerView#setDrawerContent} and pass
+ * in your drawer content view, or specify it in the {@code app:drawerContent} XML attribute.
+ *
+ * <pre>
+ * &lt;android.support.wear.widget.drawer.WearableDrawerLayout [...]&gt;
+ *     &lt;FrameLayout
+ *         android:id=”@+id/content”
+ *         android:layout_width=”match_parent”
+ *         android:layout_height=”match_parent” /&gt;
+ *
+ *     &lt;android.support.wear.widget.drawer.WearableDrawerView
+ *         android:layout_width=”match_parent”
+ *         android:layout_height=”match_parent”
+ *         android:layout_gravity=”top”
+ *         app:drawerContent="@+id/top_drawer_content" &gt;
+ *
+ *         &lt;FrameLayout
+ *             android:id=”@id/top_drawer_content”
+ *             android:layout_width=”match_parent”
+ *             android:layout_height=”match_parent” /&gt;
+ *
+ *     &lt;/android.support.wear.widget.drawer.WearableDrawerView&gt;
+ * &lt;/android.support.wear.widget.drawer.WearableDrawerLayout&gt;</pre>
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class WearableDrawerLayout extends FrameLayout
+        implements View.OnLayoutChangeListener, NestedScrollingParent, FlingListener {
+
+    private static final String TAG = "WearableDrawerLayout";
+
+    /**
+     * Undefined layout_gravity. This is different from {@link Gravity#NO_GRAVITY}. Follow up with
+     * frameworks to find out why (b/27576632).
+     */
+    private static final int GRAVITY_UNDEFINED = -1;
+
+    private static final int PEEK_FADE_DURATION_MS = 150;
+
+    private static final int PEEK_AUTO_CLOSE_DELAY_MS = 1000;
+
+    /**
+     * The downward scroll direction for use as a parameter to canScrollVertically.
+     */
+    private static final int DOWN = 1;
+
+    /**
+     * The upward scroll direction for use as a parameter to canScrollVertically.
+     */
+    private static final int UP = -1;
+
+    /**
+     * The percent at which the drawer will be opened when the drawer is released mid-drag.
+     */
+    private static final float OPENED_PERCENT_THRESHOLD = 0.5f;
+
+    /**
+     * When a user lifts their finger off the screen, this may trigger a couple of small scroll
+     * events. If the user is scrolling down and the final events from the user lifting their finger
+     * are up, this will cause the bottom drawer to peek. To prevent this from happening, we prevent
+     * the bottom drawer from peeking until this amount of scroll is exceeded. Note, scroll up
+     * events are considered negative.
+     */
+    private static final int NESTED_SCROLL_SLOP_DP = 5;
+    @VisibleForTesting final ViewDragHelper.Callback mTopDrawerDraggerCallback;
+    @VisibleForTesting final ViewDragHelper.Callback mBottomDrawerDraggerCallback;
+    private final int mNestedScrollSlopPx;
+    private final NestedScrollingParentHelper mNestedScrollingParentHelper =
+            new NestedScrollingParentHelper(this);
+    /**
+     * Helper for dragging the top drawer.
+     */
+    private final ViewDragHelper mTopDrawerDragger;
+    /**
+     * Helper for dragging the bottom drawer.
+     */
+    private final ViewDragHelper mBottomDrawerDragger;
+    private final boolean mIsAccessibilityEnabled;
+    private final FlingWatcherFactory mFlingWatcher;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final ClosePeekRunnable mCloseTopPeekRunnable = new ClosePeekRunnable(Gravity.TOP);
+    private final ClosePeekRunnable mCloseBottomPeekRunnable = new ClosePeekRunnable(
+            Gravity.BOTTOM);
+    /**
+     * Top drawer view.
+     */
+    @Nullable private WearableDrawerView mTopDrawerView;
+    /**
+     * Bottom drawer view.
+     */
+    @Nullable private WearableDrawerView mBottomDrawerView;
+    /**
+     * What we have inferred the scrolling content view to be, should one exist.
+     */
+    @Nullable private View mScrollingContentView;
+    /**
+     * Listens to drawer events.
+     */
+    private DrawerStateCallback mDrawerStateCallback;
+    private int mSystemWindowInsetBottom;
+    /**
+     * Tracks the amount of nested scroll in the up direction. This is used with {@link
+     * #NESTED_SCROLL_SLOP_DP} to prevent false drawer peeks.
+     */
+    private int mCurrentNestedScrollSlopTracker;
+    /**
+     * Tracks whether the top drawer should be opened after layout.
+     */
+    private boolean mShouldOpenTopDrawerAfterLayout;
+    /**
+     * Tracks whether the bottom drawer should be opened after layout.
+     */
+    private boolean mShouldOpenBottomDrawerAfterLayout;
+    /**
+     * Tracks whether the top drawer should be peeked after layout.
+     */
+    private boolean mShouldPeekTopDrawerAfterLayout;
+    /**
+     * Tracks whether the bottom drawer should be peeked after layout.
+     */
+    private boolean mShouldPeekBottomDrawerAfterLayout;
+    /**
+     * Tracks whether the top drawer is in a state where it can be closed. The content in the drawer
+     * can scroll, and {@link #mTopDrawerDragger} should not intercept events unless the top drawer
+     * is scrolled to the bottom of its content.
+     */
+    private boolean mCanTopDrawerBeClosed;
+    /**
+     * Tracks whether the bottom drawer is in a state where it can be closed. The content in the
+     * drawer can scroll, and {@link #mBottomDrawerDragger} should not intercept events unless the
+     * bottom drawer is scrolled to the top of its content.
+     */
+    private boolean mCanBottomDrawerBeClosed;
+    /**
+     * Tracks whether the last scroll resulted in a fling. Fling events do not contain the amount
+     * scrolled, which makes it difficult to determine when to unlock an open drawer. To work around
+     * this, if the last scroll was a fling and the next scroll unlocks the drawer, pass {@link
+     * #mDrawerOpenLastInterceptedTouchEvent} to {@link #onTouchEvent} to start the drawer.
+     */
+    private boolean mLastScrollWasFling;
+    /**
+     * The last intercepted touch event. See {@link #mLastScrollWasFling} for more information.
+     */
+    private MotionEvent mDrawerOpenLastInterceptedTouchEvent;
+
+    public WearableDrawerLayout(Context context) {
+        this(context, null);
+    }
+
+    public WearableDrawerLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WearableDrawerLayout(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mFlingWatcher = new FlingWatcherFactory(this);
+        mTopDrawerDraggerCallback = new TopDrawerDraggerCallback();
+        mTopDrawerDragger =
+                ViewDragHelper.create(this, 1f /* sensitivity */, mTopDrawerDraggerCallback);
+        mTopDrawerDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_TOP);
+
+        mBottomDrawerDraggerCallback = new BottomDrawerDraggerCallback();
+        mBottomDrawerDragger =
+                ViewDragHelper.create(this, 1f /* sensitivity */, mBottomDrawerDraggerCallback);
+        mBottomDrawerDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_BOTTOM);
+
+        WindowManager windowManager = (WindowManager) context
+                .getSystemService(Context.WINDOW_SERVICE);
+        DisplayMetrics metrics = new DisplayMetrics();
+        windowManager.getDefaultDisplay().getMetrics(metrics);
+        mNestedScrollSlopPx = Math.round(metrics.density * NESTED_SCROLL_SLOP_DP);
+
+        AccessibilityManager accessibilityManager =
+                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mIsAccessibilityEnabled = accessibilityManager.isEnabled();
+    }
+
+    private static void animatePeekVisibleAfterBeingClosed(WearableDrawerView drawer) {
+        final View content = drawer.getDrawerContent();
+        if (content != null) {
+            content.animate()
+                    .setDuration(PEEK_FADE_DURATION_MS)
+                    .alpha(0)
+                    .withEndAction(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    content.setVisibility(GONE);
+                                }
+                            })
+                    .start();
+        }
+
+        ViewGroup peek = drawer.getPeekContainer();
+        peek.setVisibility(VISIBLE);
+        peek.animate()
+                .setStartDelay(PEEK_FADE_DURATION_MS)
+                .setDuration(PEEK_FADE_DURATION_MS)
+                .alpha(1)
+                .scaleX(1)
+                .scaleY(1)
+                .start();
+
+        drawer.setIsPeeking(true);
+    }
+
+    /**
+     * Shows the drawer's contents. If the drawer is peeking, an animation is used to fade out the
+     * peek view and fade in the drawer content.
+     */
+    private static void showDrawerContentMaybeAnimate(WearableDrawerView drawerView) {
+        drawerView.bringToFront();
+        final View contentView = drawerView.getDrawerContent();
+        if (contentView != null) {
+            contentView.setVisibility(VISIBLE);
+        }
+
+        if (drawerView.isPeeking()) {
+            final View peekView = drawerView.getPeekContainer();
+            peekView.animate().alpha(0).scaleX(0).scaleY(0).setDuration(PEEK_FADE_DURATION_MS)
+                    .start();
+
+            if (contentView != null) {
+                contentView.setAlpha(0);
+                contentView
+                        .animate()
+                        .setStartDelay(PEEK_FADE_DURATION_MS)
+                        .alpha(1)
+                        .setDuration(PEEK_FADE_DURATION_MS)
+                        .start();
+            }
+        } else {
+            drawerView.getPeekContainer().setAlpha(0);
+            if (contentView != null) {
+                contentView.setAlpha(1);
+            }
+        }
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mSystemWindowInsetBottom = insets.getSystemWindowInsetBottom();
+
+        if (mSystemWindowInsetBottom != 0) {
+            MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
+            layoutParams.bottomMargin = mSystemWindowInsetBottom;
+            setLayoutParams(layoutParams);
+        }
+
+        return super.onApplyWindowInsets(insets);
+    }
+
+    /**
+     * Closes drawer after {@code delayMs} milliseconds.
+     */
+    private void closeDrawerDelayed(final int gravity, long delayMs) {
+        switch (gravity) {
+            case Gravity.TOP:
+                mMainThreadHandler.removeCallbacks(mCloseTopPeekRunnable);
+                mMainThreadHandler.postDelayed(mCloseTopPeekRunnable, delayMs);
+                break;
+            case Gravity.BOTTOM:
+                mMainThreadHandler.removeCallbacks(mCloseBottomPeekRunnable);
+                mMainThreadHandler.postDelayed(mCloseBottomPeekRunnable, delayMs);
+                break;
+            default:
+                Log.w(TAG, "Invoked a delayed drawer close with an invalid gravity: " + gravity);
+        }
+    }
+
+    /**
+     * Close the specified drawer by animating it out of view.
+     *
+     * @param gravity Gravity.TOP to move the top drawer or Gravity.BOTTOM for the bottom.
+     */
+    void closeDrawer(int gravity) {
+        closeDrawer(findDrawerWithGravity(gravity));
+    }
+
+    /**
+     * Close the specified drawer by animating it out of view.
+     *
+     * @param drawer The drawer view to close.
+     */
+    void closeDrawer(WearableDrawerView drawer) {
+        if (drawer == null) {
+            return;
+        }
+        if (drawer == mTopDrawerView) {
+            mTopDrawerDragger.smoothSlideViewTo(
+                    mTopDrawerView, 0 /* finalLeft */, -mTopDrawerView.getHeight());
+            invalidate();
+        } else if (drawer == mBottomDrawerView) {
+            mBottomDrawerDragger
+                    .smoothSlideViewTo(mBottomDrawerView, 0 /* finalLeft */, getHeight());
+            invalidate();
+        } else {
+            Log.w(TAG, "closeDrawer(View) should be passed in the top or bottom drawer");
+        }
+    }
+
+    /**
+     * Open the specified drawer by animating it into view.
+     *
+     * @param gravity Gravity.TOP to move the top drawer or Gravity.BOTTOM for the bottom.
+     */
+    void openDrawer(int gravity) {
+        if (!isLaidOut()) {
+            switch (gravity) {
+                case Gravity.TOP:
+                    mShouldOpenTopDrawerAfterLayout = true;
+                    break;
+                case Gravity.BOTTOM:
+                    mShouldOpenBottomDrawerAfterLayout = true;
+                    break;
+                default: // fall out
+            }
+            return;
+        }
+        openDrawer(findDrawerWithGravity(gravity));
+    }
+
+    /**
+     * Open the specified drawer by animating it into view.
+     *
+     * @param drawer The drawer view to open.
+     */
+    void openDrawer(WearableDrawerView drawer) {
+        if (drawer == null) {
+            return;
+        }
+        if (!isLaidOut()) {
+            if (drawer == mTopDrawerView) {
+                mShouldOpenTopDrawerAfterLayout = true;
+            } else if (drawer == mBottomDrawerView) {
+                mShouldOpenBottomDrawerAfterLayout = true;
+            }
+            return;
+        }
+
+        if (drawer == mTopDrawerView) {
+            mTopDrawerDragger
+                    .smoothSlideViewTo(mTopDrawerView, 0 /* finalLeft */, 0 /* finalTop */);
+            showDrawerContentMaybeAnimate(mTopDrawerView);
+            invalidate();
+        } else if (drawer == mBottomDrawerView) {
+            mBottomDrawerDragger.smoothSlideViewTo(
+                    mBottomDrawerView, 0 /* finalLeft */,
+                    getHeight() - mBottomDrawerView.getHeight());
+            showDrawerContentMaybeAnimate(mBottomDrawerView);
+            invalidate();
+        } else {
+            Log.w(TAG, "openDrawer(View) should be passed in the top or bottom drawer");
+        }
+    }
+
+    /**
+     * Peek the drawer.
+     *
+     * @param gravity {@link Gravity#TOP} to peek the top drawer or {@link Gravity#BOTTOM} to peek
+     * the bottom drawer.
+     */
+    void peekDrawer(final int gravity) {
+        if (!isLaidOut()) {
+            // If this view is not laid out yet, postpone the peek until onLayout is called.
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "WearableDrawerLayout not laid out yet. Postponing peek.");
+            }
+            switch (gravity) {
+                case Gravity.TOP:
+                    mShouldPeekTopDrawerAfterLayout = true;
+                    break;
+                case Gravity.BOTTOM:
+                    mShouldPeekBottomDrawerAfterLayout = true;
+                    break;
+                default: // fall out
+            }
+            return;
+        }
+        final WearableDrawerView drawerView = findDrawerWithGravity(gravity);
+        maybePeekDrawer(drawerView);
+    }
+
+    /**
+     * Peek the given {@link WearableDrawerView}, which may either be the top drawer or bottom
+     * drawer. This should only be used after the drawer has been added as a child of the {@link
+     * WearableDrawerLayout}.
+     */
+    void peekDrawer(WearableDrawerView drawer) {
+        if (drawer == null) {
+            throw new IllegalArgumentException(
+                    "peekDrawer(WearableDrawerView) received a null drawer.");
+        } else if (drawer != mTopDrawerView && drawer != mBottomDrawerView) {
+            throw new IllegalArgumentException(
+                    "peekDrawer(WearableDrawerView) received a drawer that isn't a child.");
+        }
+
+        if (!isLaidOut()) {
+            // If this view is not laid out yet, postpone the peek until onLayout is called.
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "WearableDrawerLayout not laid out yet. Postponing peek.");
+            }
+            if (drawer == mTopDrawerView) {
+                mShouldPeekTopDrawerAfterLayout = true;
+            } else if (drawer == mBottomDrawerView) {
+                mShouldPeekBottomDrawerAfterLayout = true;
+            }
+            return;
+        }
+
+        maybePeekDrawer(drawer);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Do not intercept touch events if a drawer is open. If the content in a drawer scrolls,
+        // then the touch event can be intercepted if the content in the drawer is scrolled to
+        // the maximum opposite of the drawer's gravity (ex: the touch event can be intercepted
+        // if the top drawer is open and scrolling content is at the bottom.
+        if ((mBottomDrawerView != null && mBottomDrawerView.isOpened() && !mCanBottomDrawerBeClosed)
+                || (mTopDrawerView != null && mTopDrawerView.isOpened()
+                && !mCanTopDrawerBeClosed)) {
+            mDrawerOpenLastInterceptedTouchEvent = ev;
+            return false;
+        }
+
+        // Delegate event to drawer draggers.
+        final boolean shouldInterceptTop = mTopDrawerDragger.shouldInterceptTouchEvent(ev);
+        final boolean shouldInterceptBottom = mBottomDrawerDragger.shouldInterceptTouchEvent(ev);
+        return shouldInterceptTop || shouldInterceptBottom;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (ev == null) {
+            Log.w(TAG, "null MotionEvent passed to onTouchEvent");
+            return false;
+        }
+        // Delegate event to drawer draggers.
+        mTopDrawerDragger.processTouchEvent(ev);
+        mBottomDrawerDragger.processTouchEvent(ev);
+        return true;
+    }
+
+    @Override
+    public void computeScroll() {
+        // For scrolling the drawers.
+        final boolean topSettling = mTopDrawerDragger.continueSettling(true /* deferCallbacks */);
+        final boolean bottomSettling = mBottomDrawerDragger.continueSettling(true /*
+        deferCallbacks */);
+        if (topSettling || bottomSettling) {
+            ViewCompat.postInvalidateOnAnimation(this);
+        }
+    }
+
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        super.addView(child, index, params);
+
+        if (!(child instanceof WearableDrawerView)) {
+            return;
+        }
+
+        WearableDrawerView drawerChild = (WearableDrawerView) child;
+        drawerChild.setDrawerController(new WearableDrawerController(this, drawerChild));
+        int childGravity = ((FrameLayout.LayoutParams) params).gravity;
+        // Check for preferential gravity if no gravity is set in the layout.
+        if (childGravity == Gravity.NO_GRAVITY || childGravity == GRAVITY_UNDEFINED) {
+            ((FrameLayout.LayoutParams) params).gravity = drawerChild.preferGravity();
+            childGravity = drawerChild.preferGravity();
+            drawerChild.setLayoutParams(params);
+        }
+        WearableDrawerView drawerView;
+        if (childGravity == Gravity.TOP) {
+            mTopDrawerView = drawerChild;
+            drawerView = mTopDrawerView;
+        } else if (childGravity == Gravity.BOTTOM) {
+            mBottomDrawerView = drawerChild;
+            drawerView = mBottomDrawerView;
+        } else {
+            drawerView = null;
+        }
+
+        if (drawerView != null) {
+            drawerView.addOnLayoutChangeListener(this);
+        }
+    }
+
+    @Override
+    public void onLayoutChange(
+            View v,
+            int left,
+            int top,
+            int right,
+            int bottom,
+            int oldLeft,
+            int oldTop,
+            int oldRight,
+            int oldBottom) {
+        if (v == mTopDrawerView) {
+            // Layout the top drawer base on the openedPercent. It is initially hidden.
+            final float openedPercent = mTopDrawerView.getOpenedPercent();
+            final int height = v.getHeight();
+            final int childTop = -height + (int) (height * openedPercent);
+            v.layout(v.getLeft(), childTop, v.getRight(), childTop + height);
+        } else if (v == mBottomDrawerView) {
+            // Layout the bottom drawer base on the openedPercent. It is initially hidden.
+            final float openedPercent = mBottomDrawerView.getOpenedPercent();
+            final int height = v.getHeight();
+            final int childTop = (int) (getHeight() - height * openedPercent);
+            v.layout(v.getLeft(), childTop, v.getRight(), childTop + height);
+        }
+    }
+
+    /**
+     * Sets a listener to be notified of drawer events.
+     */
+    public void setDrawerStateCallback(DrawerStateCallback callback) {
+        mDrawerStateCallback = callback;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mShouldPeekBottomDrawerAfterLayout
+                || mShouldPeekTopDrawerAfterLayout
+                || mShouldOpenTopDrawerAfterLayout
+                || mShouldOpenBottomDrawerAfterLayout) {
+            getViewTreeObserver()
+                    .addOnGlobalLayoutListener(
+                            new OnGlobalLayoutListener() {
+                                @Override
+                                public void onGlobalLayout() {
+                                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                                    if (mShouldOpenBottomDrawerAfterLayout) {
+                                        openDrawerWithoutAnimation(mBottomDrawerView);
+                                        mShouldOpenBottomDrawerAfterLayout = false;
+                                    } else if (mShouldPeekBottomDrawerAfterLayout) {
+                                        peekDrawer(Gravity.BOTTOM);
+                                        mShouldPeekBottomDrawerAfterLayout = false;
+                                    }
+
+                                    if (mShouldOpenTopDrawerAfterLayout) {
+                                        openDrawerWithoutAnimation(mTopDrawerView);
+                                        mShouldOpenTopDrawerAfterLayout = false;
+                                    } else if (mShouldPeekTopDrawerAfterLayout) {
+                                        peekDrawer(Gravity.TOP);
+                                        mShouldPeekTopDrawerAfterLayout = false;
+                                    }
+                                }
+                            });
+        }
+    }
+
+    @Override
+    public void onFlingComplete(View view) {
+        boolean canTopPeek = mTopDrawerView != null && mTopDrawerView.isAutoPeekEnabled();
+        boolean canBottomPeek = mBottomDrawerView != null && mBottomDrawerView.isAutoPeekEnabled();
+        boolean canScrollUp = view.canScrollVertically(UP);
+        boolean canScrollDown = view.canScrollVertically(DOWN);
+
+        if (canTopPeek && !canScrollUp && !mTopDrawerView.isPeeking()) {
+            peekDrawer(Gravity.TOP);
+        }
+        if (canBottomPeek && (!canScrollUp || !canScrollDown) && !mBottomDrawerView.isPeeking()) {
+            peekDrawer(Gravity.BOTTOM);
+        }
+    }
+
+    @Override // NestedScrollingParent
+    public int getNestedScrollAxes() {
+        return mNestedScrollingParentHelper.getNestedScrollAxes();
+    }
+
+    @Override // NestedScrollingParent
+    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+        return false;
+    }
+
+    @Override // NestedScrollingParent
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        maybeUpdateScrollingContentView(target);
+        mLastScrollWasFling = true;
+
+        if (target == mScrollingContentView) {
+            FlingWatcher flingWatcher = mFlingWatcher.getFor(mScrollingContentView);
+            if (flingWatcher != null) {
+                flingWatcher.watch();
+            }
+        }
+        // We do not want to intercept the child from receiving the fling, so return false.
+        return false;
+    }
+
+    @Override // NestedScrollingParent
+    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        maybeUpdateScrollingContentView(target);
+    }
+
+    @Override // NestedScrollingParent
+    public void onNestedScroll(
+            View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+
+        boolean scrolledUp = dyConsumed < 0;
+        boolean scrolledDown = dyConsumed > 0;
+        boolean overScrolledUp = dyUnconsumed < 0;
+        boolean overScrolledDown = dyUnconsumed > 0;
+
+        // When the top drawer is open, we need to track whether it can be closed.
+        if (mTopDrawerView != null && mTopDrawerView.isOpened()) {
+            // When the top drawer is overscrolled down or cannot scroll down, we consider it to be
+            // at the bottom of its content, so it can be closed.
+            mCanTopDrawerBeClosed =
+                    overScrolledDown || !mTopDrawerView.getDrawerContent()
+                            .canScrollVertically(DOWN);
+            // If the last scroll was a fling and the drawer can be closed, pass along the last
+            // touch event to start closing the drawer. See the javadocs on mLastScrollWasFling
+            // for more information.
+            if (mCanTopDrawerBeClosed && mLastScrollWasFling) {
+                onTouchEvent(mDrawerOpenLastInterceptedTouchEvent);
+            }
+            mLastScrollWasFling = false;
+            return;
+        }
+
+        // When the bottom drawer is open, we need to track whether it can be closed.
+        if (mBottomDrawerView != null && mBottomDrawerView.isOpened()) {
+            // When the bottom drawer is scrolled to the top of its content, it can be closed.
+            mCanBottomDrawerBeClosed = overScrolledUp;
+            // If the last scroll was a fling and the drawer can be closed, pass along the last
+            // touch event to start closing the drawer. See the javadocs on mLastScrollWasFling
+            // for more information.
+            if (mCanBottomDrawerBeClosed && mLastScrollWasFling) {
+                onTouchEvent(mDrawerOpenLastInterceptedTouchEvent);
+            }
+            mLastScrollWasFling = false;
+            return;
+        }
+
+        mLastScrollWasFling = false;
+
+        // The following code assumes that neither drawer is open.
+
+        // The bottom and top drawer are not open. Look at the scroll events to figure out whether
+        // a drawer should peek, close it's peek, or do nothing.
+        boolean canTopAutoPeek = mTopDrawerView != null && mTopDrawerView.isAutoPeekEnabled();
+        boolean canBottomAutoPeek =
+                mBottomDrawerView != null && mBottomDrawerView.isAutoPeekEnabled();
+        boolean isTopDrawerPeeking = mTopDrawerView != null && mTopDrawerView.isPeeking();
+        boolean isBottomDrawerPeeking = mBottomDrawerView != null && mBottomDrawerView.isPeeking();
+        boolean scrolledDownPastSlop = false;
+        boolean shouldPeekOnScrollDown =
+                mBottomDrawerView != null && mBottomDrawerView.isPeekOnScrollDownEnabled();
+        if (scrolledDown) {
+            mCurrentNestedScrollSlopTracker += dyConsumed;
+            scrolledDownPastSlop = mCurrentNestedScrollSlopTracker > mNestedScrollSlopPx;
+        }
+
+        if (canTopAutoPeek) {
+            if (overScrolledUp && !isTopDrawerPeeking) {
+                peekDrawer(Gravity.TOP);
+            } else if (scrolledDown && isTopDrawerPeeking && !isClosingPeek(mTopDrawerView)) {
+                closeDrawer(Gravity.TOP);
+            }
+        }
+
+        if (canBottomAutoPeek) {
+            if ((overScrolledDown || overScrolledUp) && !isBottomDrawerPeeking) {
+                peekDrawer(Gravity.BOTTOM);
+            } else if (shouldPeekOnScrollDown && scrolledDownPastSlop && !isBottomDrawerPeeking) {
+                peekDrawer(Gravity.BOTTOM);
+            } else if ((scrolledUp || (!shouldPeekOnScrollDown && scrolledDown))
+                    && isBottomDrawerPeeking
+                    && !isClosingPeek(mBottomDrawerView)) {
+                closeDrawer(mBottomDrawerView);
+            }
+        }
+    }
+
+    /**
+     * Peeks the given drawer if it is not {@code null} and has a peek view.
+     */
+    private void maybePeekDrawer(WearableDrawerView drawerView) {
+        if (drawerView == null) {
+            return;
+        }
+        View peekView = drawerView.getPeekContainer();
+        if (peekView == null) {
+            return;
+        }
+
+        View drawerContent = drawerView.getDrawerContent();
+        int layoutGravity = ((FrameLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
+        int gravity =
+                layoutGravity == Gravity.NO_GRAVITY ? drawerView.preferGravity() : layoutGravity;
+
+        drawerView.setIsPeeking(true);
+        peekView.setAlpha(1);
+        peekView.setScaleX(1);
+        peekView.setScaleY(1);
+        peekView.setVisibility(VISIBLE);
+        if (drawerContent != null) {
+            drawerContent.setAlpha(0);
+            drawerContent.setVisibility(GONE);
+        }
+
+        if (gravity == Gravity.BOTTOM) {
+            mBottomDrawerDragger.smoothSlideViewTo(
+                    drawerView, 0 /* finalLeft */, getHeight() - peekView.getHeight());
+        } else if (gravity == Gravity.TOP) {
+            mTopDrawerDragger.smoothSlideViewTo(
+                    drawerView, 0 /* finalLeft */,
+                    -(drawerView.getHeight() - peekView.getHeight()));
+            if (!mIsAccessibilityEnabled) {
+                // Don't automatically close the top drawer when in accessibility mode.
+                closeDrawerDelayed(gravity, PEEK_AUTO_CLOSE_DELAY_MS);
+            }
+        }
+
+        invalidate();
+    }
+
+    private void openDrawerWithoutAnimation(WearableDrawerView drawer) {
+        if (drawer == null) {
+            return;
+        }
+
+        int offset;
+        if (drawer == mTopDrawerView) {
+            offset = mTopDrawerView.getHeight();
+        } else if (drawer == mBottomDrawerView) {
+            offset = -mBottomDrawerView.getHeight();
+        } else {
+            Log.w(TAG, "openDrawer(View) should be passed in the top or bottom drawer");
+            return;
+        }
+
+        drawer.offsetTopAndBottom(offset);
+        drawer.setOpenedPercent(1f);
+        drawer.onDrawerOpened();
+        if (mDrawerStateCallback != null) {
+            mDrawerStateCallback.onDrawerOpened(this, drawer);
+        }
+        showDrawerContentMaybeAnimate(drawer);
+        invalidate();
+    }
+
+    /**
+     * @param gravity the gravity of the child to return.
+     * @return the drawer with the specified gravity
+     */
+    @Nullable
+    private WearableDrawerView findDrawerWithGravity(int gravity) {
+        switch (gravity) {
+            case Gravity.TOP:
+                return mTopDrawerView;
+            case Gravity.BOTTOM:
+                return mBottomDrawerView;
+            default:
+                Log.w(TAG, "Invalid drawer gravity: " + gravity);
+                return null;
+        }
+    }
+
+    /**
+     * Updates {@link #mScrollingContentView} if {@code view} is not a descendant of a {@link
+     * WearableDrawerView}.
+     */
+    private void maybeUpdateScrollingContentView(View view) {
+        if (view != mScrollingContentView && !isDrawerOrChildOfDrawer(view)) {
+            mScrollingContentView = view;
+        }
+    }
+
+    /**
+     * Returns {@code true} if {@code view} is a descendant of a {@link WearableDrawerView}.
+     */
+    private boolean isDrawerOrChildOfDrawer(View view) {
+        while (view != null && view != this) {
+            if (view instanceof WearableDrawerView) {
+                return true;
+            }
+
+            view = (View) view.getParent();
+        }
+
+        return false;
+    }
+
+    private boolean isClosingPeek(WearableDrawerView drawerView) {
+        return drawerView != null && drawerView.getDrawerState() == STATE_SETTLING;
+    }
+
+    @Override // NestedScrollingParent
+    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
+    }
+
+    @Override // NestedScrollingParent
+    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+        mCurrentNestedScrollSlopTracker = 0;
+        return true;
+    }
+
+    @Override // NestedScrollingParent
+    public void onStopNestedScroll(View target) {
+        mNestedScrollingParentHelper.onStopNestedScroll(target);
+    }
+
+    private boolean canDrawerContentScrollVertically(
+            @Nullable WearableDrawerView drawerView, int direction) {
+        if (drawerView == null) {
+            return false;
+        }
+
+        View drawerContent = drawerView.getDrawerContent();
+        if (drawerContent == null) {
+            return false;
+        }
+
+        return drawerContent.canScrollVertically(direction);
+    }
+
+    /**
+     * Listener for monitoring events about drawers.
+     */
+    public static class DrawerStateCallback {
+
+        /**
+         * Called when a drawer has settled in a completely open state. The drawer is interactive at
+         * this point.
+         */
+        public void onDrawerOpened(WearableDrawerLayout layout, WearableDrawerView drawerView) {
+        }
+
+        /**
+         * Called when a drawer has settled in a completely closed state.
+         */
+        public void onDrawerClosed(WearableDrawerLayout layout, WearableDrawerView drawerView) {
+        }
+
+        /**
+         * Called when the drawer motion state changes. The new state will be one of {@link
+         * WearableDrawerView#STATE_IDLE}, {@link WearableDrawerView#STATE_DRAGGING} or {@link
+         * WearableDrawerView#STATE_SETTLING}.
+         */
+        public void onDrawerStateChanged(WearableDrawerLayout layout, @DrawerState int newState) {
+        }
+    }
+
+    private void allowAccessibilityFocusOnAllChildren() {
+        if (!mIsAccessibilityEnabled) {
+            return;
+        }
+
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+    }
+
+    private void allowAccessibilityFocusOnOnly(WearableDrawerView drawer) {
+        if (!mIsAccessibilityEnabled) {
+            return;
+        }
+
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (child != drawer) {
+                child.setImportantForAccessibility(
+                        View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            }
+        }
+    }
+
+    /**
+     * Base class for top and bottom drawer dragger callbacks.
+     */
+    private abstract class DrawerDraggerCallback extends ViewDragHelper.Callback {
+
+        public abstract WearableDrawerView getDrawerView();
+
+        @Override
+        public boolean tryCaptureView(View child, int pointerId) {
+            WearableDrawerView drawerView = getDrawerView();
+            // Returns true if the dragger is dragging the drawer.
+            return child == drawerView && !drawerView.isLocked()
+                    && drawerView.getDrawerContent() != null;
+        }
+
+        @Override
+        public int getViewVerticalDragRange(View child) {
+            // Defines the vertical drag range of the drawer.
+            return child == getDrawerView() ? child.getHeight() : 0;
+        }
+
+        @Override
+        public void onViewCaptured(View capturedChild, int activePointerId) {
+            showDrawerContentMaybeAnimate((WearableDrawerView) capturedChild);
+        }
+
+        @Override
+        public void onViewDragStateChanged(int state) {
+            final WearableDrawerView drawerView = getDrawerView();
+            switch (state) {
+                case ViewDragHelper.STATE_IDLE:
+                    boolean openedOrClosed = false;
+                    if (drawerView.isOpened()) {
+                        openedOrClosed = true;
+                        drawerView.onDrawerOpened();
+                        allowAccessibilityFocusOnOnly(drawerView);
+                        if (mDrawerStateCallback != null) {
+                            mDrawerStateCallback
+                                    .onDrawerOpened(WearableDrawerLayout.this, drawerView);
+                        }
+
+                        // Drawers can be closed if a drag to close them will not cause a scroll.
+                        mCanTopDrawerBeClosed = !canDrawerContentScrollVertically(mTopDrawerView,
+                                DOWN);
+                        mCanBottomDrawerBeClosed = !canDrawerContentScrollVertically(
+                                mBottomDrawerView, UP);
+                    } else if (drawerView.isClosed()) {
+                        openedOrClosed = true;
+                        drawerView.onDrawerClosed();
+                        allowAccessibilityFocusOnAllChildren();
+                        if (mDrawerStateCallback != null) {
+                            mDrawerStateCallback
+                                    .onDrawerClosed(WearableDrawerLayout.this, drawerView);
+                        }
+                    } else { // drawerView is peeking
+                        allowAccessibilityFocusOnAllChildren();
+                    }
+
+                    // If the drawer is fully opened or closed, change it to non-peeking mode.
+                    if (openedOrClosed && drawerView.isPeeking()) {
+                        drawerView.setIsPeeking(false);
+                        drawerView.getPeekContainer().setVisibility(INVISIBLE);
+                    }
+                    break;
+                default: // fall out
+            }
+
+            if (drawerView.getDrawerState() != state) {
+                drawerView.setDrawerState(state);
+                drawerView.onDrawerStateChanged(state);
+                if (mDrawerStateCallback != null) {
+                    mDrawerStateCallback.onDrawerStateChanged(WearableDrawerLayout.this, state);
+                }
+            }
+        }
+    }
+
+    /**
+     * For communicating with top drawer view dragger.
+     */
+    private class TopDrawerDraggerCallback extends DrawerDraggerCallback {
+
+        @Override
+        public int clampViewPositionVertical(View child, int top, int dy) {
+            if (mTopDrawerView == child) {
+                int peekHeight = mTopDrawerView.getPeekContainer().getHeight();
+                // The top drawer can be dragged vertically from peekHeight - height to 0.
+                return Math.max(peekHeight - child.getHeight(), Math.min(top, 0));
+            }
+            return 0;
+        }
+
+        @Override
+        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
+            if (mTopDrawerView != null
+                    && edgeFlags == ViewDragHelper.EDGE_TOP
+                    && !mTopDrawerView.isLocked()
+                    && (mBottomDrawerView == null || !mBottomDrawerView.isOpened())
+                    && mTopDrawerView.getDrawerContent() != null) {
+
+                boolean atTop =
+                        mScrollingContentView == null || !mScrollingContentView
+                                .canScrollVertically(UP);
+                if (!mTopDrawerView.isOpenOnlyAtTopEnabled() || atTop) {
+                    mTopDrawerDragger.captureChildView(mTopDrawerView, pointerId);
+                }
+            }
+        }
+
+        @Override
+        public void onViewReleased(View releasedChild, float xvel, float yvel) {
+            if (releasedChild == mTopDrawerView) {
+                // Settle to final position. Either swipe open or close.
+                final float openedPercent = mTopDrawerView.getOpenedPercent();
+
+                final int finalTop;
+                if (yvel > 0 || (yvel == 0 && openedPercent > OPENED_PERCENT_THRESHOLD)) {
+                    // Drawer was being flung open or drawer is mostly open, so finish opening.
+                    finalTop = 0;
+                } else {
+                    // Drawer should be closed to its peek state.
+                    animatePeekVisibleAfterBeingClosed(mTopDrawerView);
+                    finalTop = mTopDrawerView.getPeekContainer().getHeight() - releasedChild
+                            .getHeight();
+                }
+
+                mTopDrawerDragger.settleCapturedViewAt(0 /* finalLeft */, finalTop);
+                invalidate();
+            }
+        }
+
+        @Override
+        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+            if (changedView == mTopDrawerView) {
+                // Compute the offset and invalidate will move the drawer during layout.
+                final int height = changedView.getHeight();
+                mTopDrawerView.setOpenedPercent((float) (top + height) / height);
+                invalidate();
+            }
+        }
+
+        @Override
+        public WearableDrawerView getDrawerView() {
+            return mTopDrawerView;
+        }
+    }
+
+    /**
+     * For communicating with bottom drawer view dragger.
+     */
+    private class BottomDrawerDraggerCallback extends DrawerDraggerCallback {
+
+        @Override
+        public int clampViewPositionVertical(View child, int top, int dy) {
+            if (mBottomDrawerView == child) {
+                // The bottom drawer can be dragged vertically from (parentHeight - height) to
+                // (parentHeight - peekHeight).
+                int parentHeight = getHeight();
+                int peekHeight = mBottomDrawerView.getPeekContainer().getHeight();
+                return Math.max(parentHeight - child.getHeight(),
+                        Math.min(top, parentHeight - peekHeight));
+            }
+            return 0;
+        }
+
+        @Override
+        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
+            if (mBottomDrawerView != null
+                    && edgeFlags == ViewDragHelper.EDGE_BOTTOM
+                    && !mBottomDrawerView.isLocked()
+                    && (mTopDrawerView == null || !mTopDrawerView.isOpened())
+                    && mBottomDrawerView.getDrawerContent() != null) {
+                // Tells the dragger which view to start dragging.
+                mBottomDrawerDragger.captureChildView(mBottomDrawerView, pointerId);
+            }
+        }
+
+        @Override
+        public void onViewReleased(View releasedChild, float xvel, float yvel) {
+            if (releasedChild == mBottomDrawerView) {
+                // Settle to final position. Either swipe open or close.
+                final int parentHeight = getHeight();
+                final float openedPercent = mBottomDrawerView.getOpenedPercent();
+                final int finalTop;
+                if (yvel < 0 || (yvel == 0 && openedPercent > OPENED_PERCENT_THRESHOLD)) {
+                    // Drawer was being flung open or drawer is mostly open, so finish opening it.
+                    finalTop = parentHeight - releasedChild.getHeight();
+                } else {
+                    // Drawer should be closed to its peek state.
+                    animatePeekVisibleAfterBeingClosed(mBottomDrawerView);
+                    finalTop = getHeight() - mBottomDrawerView.getPeekContainer().getHeight();
+                }
+                mBottomDrawerDragger.settleCapturedViewAt(0 /* finalLeft */, finalTop);
+                invalidate();
+            }
+        }
+
+        @Override
+        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+            if (changedView == mBottomDrawerView) {
+                // Compute the offset and invalidate will move the drawer during layout.
+                final int height = changedView.getHeight();
+                final int parentHeight = getHeight();
+
+                mBottomDrawerView.setOpenedPercent((float) (parentHeight - top) / height);
+                invalidate();
+            }
+        }
+
+        @Override
+        public WearableDrawerView getDrawerView() {
+            return mBottomDrawerView;
+        }
+    }
+
+    /**
+     * Runnable that closes the given drawer if it is just peeking.
+     */
+    private class ClosePeekRunnable implements Runnable {
+
+        private final int mGravity;
+
+        private ClosePeekRunnable(int gravity) {
+            mGravity = gravity;
+        }
+
+        @Override
+        public void run() {
+            WearableDrawerView drawer = findDrawerWithGravity(mGravity);
+            if (drawer != null
+                    && !drawer.isOpened()
+                    && drawer.getDrawerState() == STATE_IDLE) {
+                closeDrawer(mGravity);
+            }
+        }
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java b/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java
new file mode 100644
index 0000000..dafac39
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java
@@ -0,0 +1,510 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.IdRes;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.annotation.StyleableRes;
+import android.support.v4.widget.ViewDragHelper;
+import android.support.wear.R;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * View that contains drawer content and a peeking view for use with {@link WearableDrawerLayout}.
+ *
+ * <p>This view provides the ability to set its main content as well as a view shown while peeking.
+ * Specifying the peek view is entirely optional; a default is used if none are set. However, the
+ * content must be provided.
+ *
+ * <p>There are two ways to specify the content and peek views: by invoking {@code setter} methods
+ * on the {@code WearableDrawerView}, or by specifying the {@code app:drawerContent} and {@code
+ * app:peekView} attributes. Examples:
+ *
+ * <pre>
+ * // From Java:
+ * drawerView.setDrawerContent(drawerContentView);
+ * drawerView.setPeekContent(peekContentView);
+ *
+ * &lt;!-- From XML: --&gt;
+ * &lt;android.support.wear.widget.drawer.WearableDrawerView
+ *     android:layout_width="match_parent"
+ *     android:layout_height="match_parent"
+ *     android:layout_gravity="bottom"
+ *     android:background="@color/red"
+ *     app:drawerContent="@+id/drawer_content"
+ *     app:peekView="@+id/peek_view"&gt;
+ *
+ *     &lt;FrameLayout
+ *         android:id="@id/drawer_content"
+ *         android:layout_width="match_parent"
+ *         android:layout_height="match_parent" /&gt;
+ *
+ *     &lt;LinearLayout
+ *         android:id="@id/peek_view"
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:layout_gravity="center_horizontal"
+ *         android:orientation="horizontal"&gt;
+ *         &lt;ImageView
+ *             android:layout_width="wrap_content"
+ *             android:layout_height="wrap_content"
+ *             android:src="@android:drawable/ic_media_play" /&gt;
+ *         &lt;ImageView
+ *             android:layout_width="wrap_content"
+ *             android:layout_height="wrap_content"
+ *             android:src="@android:drawable/ic_media_pause" /&gt;
+ *     &lt;/LinearLayout&gt;
+ * &lt;/android.support.wear.widget.drawer.WearableDrawerView&gt;</pre>
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class WearableDrawerView extends FrameLayout {
+    /**
+     * Indicates that the drawer is in an idle, settled state. No animation is in progress.
+     */
+    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
+
+    /**
+     * Indicates that the drawer is currently being dragged by the user.
+     */
+    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
+
+    /**
+     * Indicates that the drawer is in the process of settling to a final position.
+     */
+    public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
+
+    /**
+     * Enumeration of possible drawer states.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @IntDef({STATE_IDLE, STATE_DRAGGING, STATE_SETTLING})
+    public @interface DrawerState {}
+
+    private final ViewGroup mPeekContainer;
+    private final ImageView mPeekIcon;
+    private View mContent;
+    private WearableDrawerController mController;
+    /**
+     * Vertical offset of the drawer. Ranges from 0 (closed) to 1 (opened)
+     */
+    private float mOpenedPercent;
+    /**
+     * True if the drawer's position cannot be modified by the user. This includes edge dragging,
+     * view dragging, and scroll based auto-peeking.
+     */
+    private boolean mIsLocked = false;
+    private boolean mCanAutoPeek = true;
+    private boolean mLockWhenClosed = false;
+    private boolean mOpenOnlyAtTop = false;
+    private boolean mPeekOnScrollDown = false;
+    private boolean mIsPeeking;
+    @DrawerState private int mDrawerState;
+    @IdRes private int mPeekResId = 0;
+    @IdRes private int mContentResId = 0;
+    public WearableDrawerView(Context context) {
+        this(context, null);
+    }
+
+    public WearableDrawerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableDrawerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WearableDrawerView(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        LayoutInflater.from(context).inflate(R.layout.ws_wearable_drawer_view, this, true);
+
+        setClickable(true);
+        setElevation(context.getResources()
+                .getDimension(R.dimen.ws_wearable_drawer_view_elevation));
+
+        mPeekContainer = (ViewGroup) findViewById(R.id.ws_drawer_view_peek_container);
+        mPeekIcon = (ImageView) findViewById(R.id.ws_drawer_view_peek_icon);
+
+        mPeekContainer.setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        onPeekContainerClicked(v);
+                    }
+                });
+
+        parseAttributes(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private static Drawable getDrawable(
+            Context context, TypedArray typedArray, @StyleableRes int index) {
+        Drawable background;
+        int backgroundResId =
+                typedArray.getResourceId(index, 0);
+        if (backgroundResId == 0) {
+            background = typedArray.getDrawable(index);
+        } else {
+            background = context.getDrawable(backgroundResId);
+        }
+        return background;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        // Drawer content is added after the peek view, so we need to bring the peek view
+        // to the front so it shows on top of the content.
+        mPeekContainer.bringToFront();
+    }
+
+    /**
+     * Called when anything within the peek container is clicked. However, if a custom peek view is
+     * supplied and it handles the click, then this may not be called. The default behavior is to
+     * open the drawer.
+     */
+    public void onPeekContainerClicked(View v) {
+        mController.openDrawer();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        // The peek view has a layout gravity of bottom for the top drawer, and a layout gravity
+        // of top for the bottom drawer. This is required so that the peek view shows. On the top
+        // drawer, the bottom peeks from the top, and on the bottom drawer, the top peeks.
+        // LayoutParams are not guaranteed to return a non-null value until a child is attached to
+        // the window.
+        LayoutParams peekParams = (LayoutParams) mPeekContainer.getLayoutParams();
+        if (!Gravity.isVertical(peekParams.gravity)) {
+            final boolean isTopDrawer =
+                    (((LayoutParams) getLayoutParams()).gravity & Gravity.VERTICAL_GRAVITY_MASK)
+                            == Gravity.TOP;
+            if (isTopDrawer) {
+                peekParams.gravity = Gravity.BOTTOM;
+                mPeekIcon.setImageResource(R.drawable.ws_ic_more_horiz_24dp_wht);
+            } else {
+                peekParams.gravity = Gravity.TOP;
+                mPeekIcon.setImageResource(R.drawable.ws_ic_more_vert_24dp_wht);
+            }
+            mPeekContainer.setLayoutParams(peekParams);
+        }
+    }
+
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        @IdRes int childId = child.getId();
+        if (childId != 0) {
+            if (childId == mPeekResId) {
+                setPeekContent(child, index, params);
+                return;
+            }
+            if (childId == mContentResId && !setDrawerContentWithoutAdding(child)) {
+                return;
+            }
+        }
+
+        super.addView(child, index, params);
+    }
+
+    int preferGravity() {
+        return Gravity.NO_GRAVITY;
+    }
+
+    ViewGroup getPeekContainer() {
+        return mPeekContainer;
+    }
+
+    void setDrawerController(WearableDrawerController controller) {
+        mController = controller;
+    }
+
+    /**
+     * Returns the drawer content view.
+     */
+    @Nullable
+    public View getDrawerContent() {
+        return mContent;
+    }
+
+    /**
+     * Set the drawer content view.
+     *
+     * @param content The view to show when the drawer is open, or {@code null} if it should not
+     * open.
+     */
+    public void setDrawerContent(@Nullable View content) {
+        if (setDrawerContentWithoutAdding(content)) {
+            addView(content);
+        }
+    }
+
+    /**
+     * Set the peek content view.
+     *
+     * @param content The view to show when the drawer peeks.
+     */
+    public void setPeekContent(View content) {
+        ViewGroup.LayoutParams layoutParams = content.getLayoutParams();
+        setPeekContent(
+                content,
+                -1 /* index */,
+                layoutParams != null ? layoutParams : generateDefaultLayoutParams());
+    }
+
+    /**
+     * Called when the drawer has settled in a completely open state. The drawer is interactive at
+     * this point. This is analogous to {@link
+     * WearableDrawerLayout.DrawerStateCallback#onDrawerOpened}.
+     */
+    public void onDrawerOpened() {}
+
+    /**
+     * Called when the drawer has settled in a completely closed state. This is analogous to {@link
+     * WearableDrawerLayout.DrawerStateCallback#onDrawerClosed}.
+     */
+    public void onDrawerClosed() {}
+
+    /**
+     * Called when the drawer state changes. This is analogous to {@link
+     * WearableDrawerLayout.DrawerStateCallback#onDrawerStateChanged}.
+     *
+     * @param state one of {@link #STATE_DRAGGING}, {@link #STATE_SETTLING}, or {@link #STATE_IDLE}
+     */
+    public void onDrawerStateChanged(@DrawerState int state) {}
+
+    /**
+     * Only allow the user to open this drawer when at the top of the scrolling content. If there is
+     * no scrolling content, then this has no effect. Defaults to {@code false}.
+     */
+    public void setOpenOnlyAtTopEnabled(boolean openOnlyAtTop) {
+        mOpenOnlyAtTop = openOnlyAtTop;
+    }
+
+    /**
+     * Returns whether this drawer may only be opened by the user when at the top of the scrolling
+     * content. If there is no scrolling content, then this has no effect. Defaults to {@code
+     * false}.
+     */
+    public boolean isOpenOnlyAtTopEnabled() {
+        return mOpenOnlyAtTop;
+    }
+
+    /**
+     * Sets whether or not this drawer should peek while scrolling down. This is currently only
+     * supported for bottom drawers. Defaults to {@code false}.
+     */
+    public void setPeekOnScrollDownEnabled(boolean peekOnScrollDown) {
+        mPeekOnScrollDown = peekOnScrollDown;
+    }
+
+    /**
+     * Gets whether or not this drawer should peek while scrolling down. This is currently only
+     * supported for bottom drawers. Defaults to {@code false}.
+     */
+    public boolean isPeekOnScrollDownEnabled() {
+        return mPeekOnScrollDown;
+    }
+
+    /**
+     * Sets whether this drawer should be locked when the user cannot see it.
+     * @see #isLocked
+     */
+    public void setLockedWhenClosed(boolean locked) {
+        mLockWhenClosed = locked;
+    }
+
+    /**
+     * Returns true if this drawer should be locked when the user cannot see it.
+     * @see #isLocked
+     */
+    public boolean isLockedWhenClosed() {
+        return mLockWhenClosed;
+    }
+
+    /**
+     * Returns the current drawer state, which will be one of {@link #STATE_DRAGGING}, {@link
+     * #STATE_SETTLING}, or {@link #STATE_IDLE}
+     */
+    @DrawerState
+    public int getDrawerState() {
+        return mDrawerState;
+    }
+
+    /**
+     * Sets the {@link DrawerState}.
+     */
+    void setDrawerState(@DrawerState int drawerState) {
+        mDrawerState = drawerState;
+    }
+
+    /**
+     * Returns whether the drawer is either peeking or the peek view is animating open.
+     */
+    public boolean isPeeking() {
+        return mIsPeeking;
+    }
+
+    /**
+     * Returns true if this drawer has auto-peeking enabled. This will always return {@code false}
+     * for a locked drawer.
+     */
+    public boolean isAutoPeekEnabled() {
+        return mCanAutoPeek && !mIsLocked;
+    }
+
+    /**
+     * Sets whether or not the drawer can automatically adjust its peek state. Note that locked
+     * drawers will never auto-peek, but their {@code isAutoPeekEnabled} state will be maintained
+     * through a lock/unlock cycle.
+     */
+    public void setIsAutoPeekEnabled(boolean canAutoPeek) {
+        mCanAutoPeek = canAutoPeek;
+    }
+
+    /**
+     * Returns true if the position of the drawer cannot be modified by user interaction.
+     * Specifically, a drawer cannot be opened, closed, or automatically peeked by {@link
+     * WearableDrawerLayout}. However, it can be explicitly opened, closed, and peeked by the
+     * developer. A drawer may be considered locked if the drawer is locked open, locked closed, or
+     * is closed and {@link #isLockedWhenClosed} returns true.
+     */
+    public boolean isLocked() {
+        return mIsLocked || (isLockedWhenClosed() && mOpenedPercent <= 0);
+    }
+
+    /**
+     * Sets whether or not the position of the drawer can be modified by user interaction.
+     * @see #isLocked
+     */
+    public void setIsLocked(boolean locked) {
+        mIsLocked = locked;
+    }
+
+    /**
+     * Returns true if the drawer is fully open.
+     */
+    public boolean isOpened() {
+        return mOpenedPercent == 1;
+    }
+
+    /**
+     * Returns true if the drawer is fully closed.
+     */
+    public boolean isClosed() {
+        return mOpenedPercent == 0;
+    }
+
+    /**
+     * Returns the {@link WearableDrawerController} associated with this {@link WearableDrawerView}.
+     * This will only be valid after this {@code View} has been added to its parent.
+     */
+    public WearableDrawerController getController() {
+        return mController;
+    }
+
+    /**
+     * Sets whether the drawer is either peeking or the peek view is animating open.
+     */
+    void setIsPeeking(boolean isPeeking) {
+        mIsPeeking = isPeeking;
+    }
+
+    /**
+     * Returns the percent the drawer is open, from 0 (fully closed) to 1 (fully open).
+     */
+    float getOpenedPercent() {
+        return mOpenedPercent;
+    }
+
+    /**
+     * Sets the percent the drawer is open, from 0 (fully closed) to 1 (fully open).
+     */
+    void setOpenedPercent(float openedPercent) {
+        mOpenedPercent = openedPercent;
+    }
+
+    private void parseAttributes(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        if (attrs == null) {
+            return;
+        }
+
+        TypedArray typedArray =
+                context.obtainStyledAttributes(
+                        attrs, R.styleable.WearableDrawerView, defStyleAttr,
+                        R.style.Widget_Wear_WearableDrawerView);
+
+        Drawable background =
+                getDrawable(context, typedArray, R.styleable.WearableDrawerView_android_background);
+        int elevation = typedArray
+                .getDimensionPixelSize(R.styleable.WearableDrawerView_android_elevation, 0);
+        setBackground(background);
+        setElevation(elevation);
+
+        mContentResId = typedArray.getResourceId(R.styleable.WearableDrawerView_drawerContent, 0);
+        mPeekResId = typedArray.getResourceId(R.styleable.WearableDrawerView_peekView, 0);
+        mCanAutoPeek =
+                typedArray.getBoolean(R.styleable.WearableDrawerView_enableAutoPeek, mCanAutoPeek);
+        typedArray.recycle();
+    }
+
+    private void setPeekContent(View content, int index, ViewGroup.LayoutParams params) {
+        if (content == null) {
+            return;
+        }
+        if (mPeekContainer.getChildCount() > 0) {
+            mPeekContainer.removeAllViews();
+        }
+        mPeekContainer.addView(content, index, params);
+    }
+
+    /**
+     * @return {@code true} if this is a new and valid {@code content}.
+     */
+    private boolean setDrawerContentWithoutAdding(View content) {
+        if (content == mContent) {
+            return false;
+        }
+        if (mContent != null) {
+            removeView(mContent);
+        }
+
+        mContent = content;
+        return mContent != null;
+    }
+}
diff --git a/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java b/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
new file mode 100644
index 0000000..480812b
--- /dev/null
+++ b/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.wear.R;
+import android.support.wear.internal.widget.drawer.MultiPagePresenter;
+import android.support.wear.internal.widget.drawer.MultiPageUi;
+import android.support.wear.internal.widget.drawer.SinglePagePresenter;
+import android.support.wear.internal.widget.drawer.SinglePageUi;
+import android.support.wear.internal.widget.drawer.WearableNavigationDrawerPresenter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Ease of use class for creating a Wearable navigation drawer. This can be used with {@link
+ * WearableDrawerLayout} to create a drawer for users to easily navigate a wearable app.
+ *
+ * <p>There are two ways this information may be presented: as a single page and as multiple pages.
+ * The single page navigation drawer will display 1-7 items to the user representing different
+ * navigation verticals. If more than 7 items are provided to a single page navigation drawer, the
+ * navigation drawer will be displayed as empty. The multiple page navigation drawer will display 1
+ * or more pages to the user, each representing different navigation verticals.
+ *
+ * <p>The developer may specify which style to use with the {@code app:navigationStyle} custom
+ * attribute. If not specified, {@link #SINGLE_PAGE singlePage} will be used as the default.
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class WearableNavigationDrawerView extends WearableDrawerView {
+
+    private static final String TAG = "WearableNavDrawer";
+
+    /**
+     * Listener which is notified when the user selects an item.
+     */
+    public interface OnItemSelectedListener {
+
+        /**
+         * Notified when the user has selected an item at position {@code pos}.
+         */
+        void onItemSelected(int pos);
+    }
+
+    /**
+     * Enumeration of possible drawer styles.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @IntDef({SINGLE_PAGE, MULTI_PAGE})
+    public @interface NavigationStyle {}
+
+    /**
+     * Single page navigation drawer style. This is the default drawer style. It is ideal for 1-5
+     * items, but works with up to 7 items. If more than 7 items exist, then the drawer will be
+     * displayed as empty.
+     */
+    public static final int SINGLE_PAGE = 0;
+
+    /**
+     * Multi-page navigation drawer style. Each item is on its own page. Useful when more than 7
+     * items exist.
+     */
+    public static final int MULTI_PAGE = 1;
+
+    @NavigationStyle private static final int DEFAULT_STYLE = SINGLE_PAGE;
+    private static final long AUTO_CLOSE_DRAWER_DELAY_MS = TimeUnit.SECONDS.toMillis(5);
+    private final boolean mIsAccessibilityEnabled;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final Runnable mCloseDrawerRunnable =
+            new Runnable() {
+                @Override
+                public void run() {
+                    getController().closeDrawer();
+                }
+            };
+    /**
+     * Listens for single taps on the drawer.
+     */
+    @Nullable private final GestureDetector mGestureDetector;
+    @NavigationStyle private final int mNavigationStyle;
+    private final WearableNavigationDrawerPresenter mPresenter;
+    private final SimpleOnGestureListener mOnGestureListener =
+            new SimpleOnGestureListener() {
+                @Override
+                public boolean onSingleTapUp(MotionEvent e) {
+                    return mPresenter.onDrawerTapped();
+                }
+            };
+    public WearableNavigationDrawerView(Context context) {
+        this(context, (AttributeSet) null);
+    }
+    public WearableNavigationDrawerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableNavigationDrawerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WearableNavigationDrawerView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mGestureDetector = new GestureDetector(getContext(), mOnGestureListener);
+
+        @NavigationStyle int navStyle = DEFAULT_STYLE;
+        if (attrs != null) {
+            TypedArray typedArray = context.obtainStyledAttributes(
+                    attrs,
+                    R.styleable.WearableNavigationDrawerView,
+                    defStyleAttr,
+                    0 /* defStyleRes */);
+
+            //noinspection WrongConstant
+            navStyle = typedArray.getInt(
+                    R.styleable.WearableNavigationDrawerView_navigationStyle, DEFAULT_STYLE);
+            typedArray.recycle();
+        }
+
+        mNavigationStyle = navStyle;
+        AccessibilityManager accessibilityManager =
+                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mIsAccessibilityEnabled = accessibilityManager.isEnabled();
+
+        mPresenter =
+                mNavigationStyle == SINGLE_PAGE
+                        ? new SinglePagePresenter(new SinglePageUi(this), mIsAccessibilityEnabled)
+                        : new MultiPagePresenter(this, new MultiPageUi(), mIsAccessibilityEnabled);
+
+        getPeekContainer()
+                .setContentDescription(
+                        context.getString(R.string.ws_navigation_drawer_content_description));
+
+        setOpenOnlyAtTopEnabled(true);
+    }
+
+    /**
+     * Set a {@link WearableNavigationDrawerAdapter} that will supply data for this drawer.
+     */
+    public void setAdapter(final WearableNavigationDrawerAdapter adapter) {
+        mPresenter.onNewAdapter(adapter);
+    }
+
+    /**
+     * Add an {@link OnItemSelectedListener} that will be notified when the user selects an item.
+     */
+    public void addOnItemSelectedListener(OnItemSelectedListener listener) {
+        mPresenter.onItemSelectedListenerAdded(listener);
+    }
+
+    /**
+     * Remove an {@link OnItemSelectedListener}.
+     */
+    public void removeOnItemSelectedListener(OnItemSelectedListener listener) {
+        mPresenter.onItemSelectedListenerRemoved(listener);
+    }
+
+    /**
+     * Changes which index is selected. {@link OnItemSelectedListener#onItemSelected} will
+     * be called when the specified {@code index} is reached, but it won't be called for items
+     * between the current index and the destination index.
+     */
+    public void setCurrentItem(int index, boolean smoothScrollTo) {
+        mPresenter.onSetCurrentItemRequested(index, smoothScrollTo);
+    }
+
+    /**
+     * Returns the style this drawer is using, either {@link #SINGLE_PAGE} or {@link #MULTI_PAGE}.
+     */
+    @NavigationStyle
+    public int getNavigationStyle() {
+        return mNavigationStyle;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        autoCloseDrawerAfterDelay();
+        return mGestureDetector != null && mGestureDetector.onTouchEvent(ev);
+    }
+
+    @Override
+    public boolean canScrollHorizontally(int direction) {
+        // Prevent the window from being swiped closed while it is open by saying that it can scroll
+        // horizontally.
+        return isOpened();
+    }
+
+    @Override
+    public void onDrawerOpened() {
+        autoCloseDrawerAfterDelay();
+    }
+
+    @Override
+    public void onDrawerClosed() {
+        mMainThreadHandler.removeCallbacks(mCloseDrawerRunnable);
+    }
+
+    private void autoCloseDrawerAfterDelay() {
+        if (!mIsAccessibilityEnabled) {
+            mMainThreadHandler.removeCallbacks(mCloseDrawerRunnable);
+            mMainThreadHandler.postDelayed(mCloseDrawerRunnable, AUTO_CLOSE_DRAWER_DELAY_MS);
+        }
+    }
+
+    @Override
+  /* package */ int preferGravity() {
+        return Gravity.TOP;
+    }
+
+    /**
+     * Adapter for specifying the contents of WearableNavigationDrawer.
+     */
+    public abstract static class WearableNavigationDrawerAdapter {
+
+        @Nullable
+        private WearableNavigationDrawerPresenter mPresenter;
+
+        /**
+         * Get the text associated with the item at {@code pos}.
+         */
+        public abstract CharSequence getItemText(int pos);
+
+        /**
+         * Get the drawable associated with the item at {@code pos}.
+         */
+        public abstract Drawable getItemDrawable(int pos);
+
+        /**
+         * Returns the number of items in this adapter.
+         */
+        public abstract int getCount();
+
+        /**
+         * This method should be called by the application if the data backing this adapter has
+         * changed and associated views should update.
+         */
+        public void notifyDataSetChanged() {
+            // If this method is called before drawer.setAdapter, then we will not yet have a
+            // presenter.
+            if (mPresenter != null) {
+                mPresenter.onDataSetChanged();
+            } else {
+                Log.w(TAG,
+                        "adapter.notifyDataSetChanged called before drawer.setAdapter; ignoring.");
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        public void setPresenter(WearableNavigationDrawerPresenter presenter) {
+            mPresenter = presenter;
+        }
+    }
+
+}
diff --git a/wear/tests/AndroidManifest.xml b/wear/tests/AndroidManifest.xml
new file mode 100644
index 0000000..23d66f4
--- /dev/null
+++ b/wear/tests/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.wear.test">
+    <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+    <application android:supportsRtl="true">
+        <activity android:name="android.support.wear.widget.LayoutTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name="android.support.wear.widget.SwipeDismissFrameLayoutTestActivity"
+                  android:theme="@style/AppThemeNoSwipe">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.support.wear.widget.WearableRecyclerViewTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.support.wear.widget.drawer.DrawerTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/wear/tests/NO_DOCS b/wear/tests/NO_DOCS
new file mode 100644
index 0000000..092a39c
--- /dev/null
+++ b/wear/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/wear/tests/res/layout/box_inset_layout_testcase_1.xml b/wear/tests/res/layout/box_inset_layout_testcase_1.xml
new file mode 100644
index 0000000..7da6191
--- /dev/null
+++ b/wear/tests/res/layout/box_inset_layout_testcase_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.wear.widget.BoxInsetLayout 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="match_parent"
+    android:id="@+id/box">
+
+    <FrameLayout
+        android:id="@+id/child1"
+        app:boxedEdges="bottom|right"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <TextView
+            android:id="@+id/content1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="bottom|right"
+            android:text="Test Case 1"/>
+    </FrameLayout>
+
+</android.support.wear.widget.BoxInsetLayout>
diff --git a/wear/tests/res/layout/box_inset_layout_testcase_2.xml b/wear/tests/res/layout/box_inset_layout_testcase_2.xml
new file mode 100644
index 0000000..a20d71d
--- /dev/null
+++ b/wear/tests/res/layout/box_inset_layout_testcase_2.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.wear.widget.BoxInsetLayout
+    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="match_parent"
+    android:id="@+id/box">
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child1"
+        android:layout_width="match_parent"
+        android:layout_height="60dp"
+        android:layout_gravity="top">
+        <TextView
+            android:id="@+id/content1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="top|right"
+            android:text="Top Right"/>
+    </FrameLayout>
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child2"
+        android:layout_width="match_parent"
+        android:layout_height="60dp"
+        android:layout_gravity="bottom">
+        <TextView
+            android:id="@+id/content2"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="bottom|left"
+            android:text="Bottom Left"/>
+    </FrameLayout>
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child3"
+        android:layout_width="wrap_content"
+        android:layout_height="20dp"
+        android:layout_gravity="left|center_vertical">
+        <TextView
+            android:id="@+id/content3"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical|left"
+            android:text="Left"/>
+    </FrameLayout>
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child4"
+        android:layout_width="wrap_content"
+        android:layout_height="20dp"
+        android:layout_gravity="right|center_vertical">
+        <TextView
+            android:id="@+id/content4"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical|right"
+            android:text="Right"/>
+    </FrameLayout>
+
+
+</android.support.wear.widget.BoxInsetLayout>
diff --git a/wear/tests/res/layout/box_inset_layout_testcase_3.xml b/wear/tests/res/layout/box_inset_layout_testcase_3.xml
new file mode 100644
index 0000000..47fe749
--- /dev/null
+++ b/wear/tests/res/layout/box_inset_layout_testcase_3.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.wear.widget.BoxInsetLayout
+    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="match_parent"
+    android:id="@+id/box">
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child1"
+        android:layout_gravity="top|left"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView
+            android:id="@+id/content1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:text="Top Left"/>
+    </FrameLayout>
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child2"
+        android:layout_gravity="top|right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView
+            android:id="@+id/content2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:text="Top Right"/>
+    </FrameLayout>
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child3"
+        android:layout_gravity="bottom|right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView
+            android:id="@+id/content3"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:text="Bottom Right"/>
+    </FrameLayout>
+
+    <FrameLayout
+        app:boxedEdges="all"
+        android:id="@+id/child4"
+        android:layout_gravity="bottom|left"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView
+            android:id="@+id/content4"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:text="Bottom Left"/>
+    </FrameLayout>
+
+</android.support.wear.widget.BoxInsetLayout>
diff --git a/wear/tests/res/layout/box_inset_layout_testcase_4.xml b/wear/tests/res/layout/box_inset_layout_testcase_4.xml
new file mode 100644
index 0000000..1f32b10
--- /dev/null
+++ b/wear/tests/res/layout/box_inset_layout_testcase_4.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.wear.widget.BoxInsetLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:id="@+id/child1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text Super long text Super long text Super long text
+            Super long text Super long text "
+            app:boxedEdges="left|right" />
+
+
+    </android.support.wear.widget.BoxInsetLayout>
+
+</ScrollView>
diff --git a/wear/tests/res/layout/circular_progress_layout.xml b/wear/tests/res/layout/circular_progress_layout.xml
new file mode 100644
index 0000000..cce69e6
--- /dev/null
+++ b/wear/tests/res/layout/circular_progress_layout.xml
@@ -0,0 +1,35 @@
+<?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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             xmlns:app="http://schemas.android.com/apk/res-auto"
+             android:orientation="vertical">
+    <android.support.wear.widget.CircularProgressLayout
+        android:id="@+id/circular_progress_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:padding="10dp"
+        app:strokeWidth="10dp">
+        <View
+            android:id="@+id/child_view"
+            android:layout_width="40dp"
+            android:layout_height="40dp"/>
+    </android.support.wear.widget.CircularProgressLayout>
+</FrameLayout >
\ No newline at end of file
diff --git a/wear/tests/res/layout/curved_offsetting_helper_child.xml b/wear/tests/res/layout/curved_offsetting_helper_child.xml
new file mode 100644
index 0000000..781d6b3
--- /dev/null
+++ b/wear/tests/res/layout/curved_offsetting_helper_child.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content1"
+    android:layout_width="100dp"
+    android:layout_height="50dp"
+    android:text="Test Case 1"/>
\ No newline at end of file
diff --git a/wear/tests/res/layout/rounded_drawable_layout.xml b/wear/tests/res/layout/rounded_drawable_layout.xml
new file mode 100644
index 0000000..6c83b58
--- /dev/null
+++ b/wear/tests/res/layout/rounded_drawable_layout.xml
@@ -0,0 +1,23 @@
+<?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"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/wear/tests/res/layout/swipe_dismiss_layout_testcase_1.xml b/wear/tests/res/layout/swipe_dismiss_layout_testcase_1.xml
new file mode 100644
index 0000000..a3d6e67
--- /dev/null
+++ b/wear/tests/res/layout/swipe_dismiss_layout_testcase_1.xml
@@ -0,0 +1,40 @@
+<!--
+  ~ 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.
+  -->
+
+<android.support.wear.widget.SwipeDismissFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#333333"
+    android:id="@+id/swipe_dismiss_root" >
+
+    <TextView
+        android:id="@+id/test_content"
+        android:layout_width="150dp"
+        android:layout_height="100dp"
+        android:layout_gravity="center"
+        android:background="@android:color/holo_orange_dark"
+        android:drawablePadding="10dp"
+        android:gravity="center"
+        android:paddingLeft="20dp"
+        android:paddingRight="20dp"
+        android:text="Test content"
+        android:textColor="@android:color/primary_text_dark"
+        android:textStyle="bold"
+        tools:ignore="HardcodedText,RtlHardcoded" />
+
+</android.support.wear.widget.SwipeDismissFrameLayout>
diff --git a/wear/tests/res/layout/swipe_dismiss_layout_testcase_2.xml b/wear/tests/res/layout/swipe_dismiss_layout_testcase_2.xml
new file mode 100644
index 0000000..93420a1
--- /dev/null
+++ b/wear/tests/res/layout/swipe_dismiss_layout_testcase_2.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ 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.
+  -->
+
+<android.support.wear.widget.SwipeDismissFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#333333"
+    android:id="@+id/swipe_dismiss_root" >
+
+    <android.support.v7.widget.RecyclerView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_gravity="center"
+        android:paddingTop="80dp"
+        android:id="@+id/recycler_container"/>
+
+</android.support.wear.widget.SwipeDismissFrameLayout>
diff --git a/wear/tests/res/layout/swipe_dismiss_layout_testcase_3.xml b/wear/tests/res/layout/swipe_dismiss_layout_testcase_3.xml
new file mode 100644
index 0000000..3cd220e
--- /dev/null
+++ b/wear/tests/res/layout/swipe_dismiss_layout_testcase_3.xml
@@ -0,0 +1,46 @@
+<?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.
+  -->
+<android.support.v7.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
+<!--
+<android.support.wear.view.drawer.WearableDrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center">
+
+    <fragment android:name="android.support.wear.view.SwipeDismissPreferenceFragment"
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+-->
+    <!--
+        Top drawer has a 16dp transparent padding at the bottom to show the underlying view.
+        Set clickable=true to avoid sending click events to the underlying view.
+      -->
+<!--
+    <android.support.wear.view.drawer.WearableNavigationDrawer
+        android:id="@+id/top_drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:navigation_style="single_page" />
+</android.support.wear.view.drawer.WearableDrawerLayout>
+-->
\ No newline at end of file
diff --git a/wear/tests/res/layout/test_multi_page_nav_drawer_layout.xml b/wear/tests/res/layout/test_multi_page_nav_drawer_layout.xml
new file mode 100644
index 0000000..ed18863
--- /dev/null
+++ b/wear/tests/res/layout/test_multi_page_nav_drawer_layout.xml
@@ -0,0 +1,39 @@
+<?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.
+  -->
+
+<android.support.wear.widget.drawer.WearableDrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.wear.widget.drawer.WearableNavigationDrawerView
+        android:id="@+id/navigation_drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/holo_red_dark"
+        app:navigationStyle="multiPage" />
+
+    <android.support.wear.widget.drawer.WearableActionDrawerView
+        android:id="@+id/action_drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/holo_blue_dark"
+        app:actionMenu="@menu/action_drawer" />
+
+</android.support.wear.widget.drawer.WearableDrawerLayout>
diff --git a/wear/tests/res/layout/test_only_action_drawer_with_title_layout.xml b/wear/tests/res/layout/test_only_action_drawer_with_title_layout.xml
new file mode 100644
index 0000000..f3859a9
--- /dev/null
+++ b/wear/tests/res/layout/test_only_action_drawer_with_title_layout.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<android.support.wear.widget.drawer.WearableDrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.wear.widget.drawer.WearableActionDrawerView
+        android:id="@+id/action_drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/holo_blue_dark"
+        app:drawerTitle="Action Drawer Title"
+        app:actionMenu="@menu/action_drawer" />
+
+</android.support.wear.widget.drawer.WearableDrawerLayout>
diff --git a/wear/tests/res/layout/test_single_page_nav_drawer_layout.xml b/wear/tests/res/layout/test_single_page_nav_drawer_layout.xml
new file mode 100644
index 0000000..f739472
--- /dev/null
+++ b/wear/tests/res/layout/test_single_page_nav_drawer_layout.xml
@@ -0,0 +1,38 @@
+<?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.
+  -->
+
+<android.support.wear.widget.drawer.WearableDrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer_layout"
+    style="@style/DrawerLayoutStyle">
+
+    <android.support.wear.widget.drawer.WearableNavigationDrawerView
+        android:id="@+id/navigation_drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/holo_red_dark"
+        app:navigationStyle="singlePage" />
+
+    <android.support.wear.widget.drawer.WearableActionDrawerView
+        android:id="@+id/action_drawer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/holo_blue_dark"
+        app:actionMenu="@menu/action_drawer" />
+
+</android.support.wear.widget.drawer.WearableDrawerLayout>
diff --git a/wear/tests/res/layout/wearable_recycler_view_basic.xml b/wear/tests/res/layout/wearable_recycler_view_basic.xml
new file mode 100644
index 0000000..69952c7
--- /dev/null
+++ b/wear/tests/res/layout/wearable_recycler_view_basic.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<android.support.wear.widget.WearableRecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/wrv">
+
+</android.support.wear.widget.WearableRecyclerView>
diff --git a/wear/tests/res/menu/action_drawer.xml b/wear/tests/res/menu/action_drawer.xml
new file mode 100644
index 0000000..2c24fb7
--- /dev/null
+++ b/wear/tests/res/menu/action_drawer.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:title="Item 1" android:icon="@android:drawable/star_on"/>
+  <item android:title="Item 2" android:icon="@android:drawable/star_on"/>
+  <item android:title="Item 3" android:icon="@android:drawable/star_on"/>
+  <item android:title="Item 4" android:icon="@android:drawable/star_on"/>
+  <item android:title="Item 5" android:icon="@android:drawable/star_on"/>
+  <item android:title="Item 6" android:icon="@android:drawable/star_on"/>
+</menu>
diff --git a/wear/tests/res/values-sw213dp/styles.xml b/wear/tests/res/values-sw213dp/styles.xml
new file mode 100644
index 0000000..be1d648
--- /dev/null
+++ b/wear/tests/res/values-sw213dp/styles.xml
@@ -0,0 +1,23 @@
+<?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="DrawerLayoutStyle" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">213dp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/wear/tests/res/values/styles.xml b/wear/tests/res/values/styles.xml
new file mode 100644
index 0000000..302db80
--- /dev/null
+++ b/wear/tests/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?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="AppThemeNoSwipe" parent="@android:style/Theme.DeviceDefault.Light">
+        <item name="android:windowSwipeToDismiss">false</item>
+    </style>
+    <style name="DrawerLayoutStyle" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/wear/tests/src/android/support/wear/widget/BoxInsetLayoutTest.java b/wear/tests/src/android/support/wear/widget/BoxInsetLayoutTest.java
new file mode 100644
index 0000000..731f57a
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/BoxInsetLayoutTest.java
@@ -0,0 +1,364 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.wear.widget.util.MoreViewAssertions.approximateBottom;
+import static android.support.wear.widget.util.MoreViewAssertions.approximateTop;
+import static android.support.wear.widget.util.MoreViewAssertions.bottom;
+import static android.support.wear.widget.util.MoreViewAssertions.left;
+import static android.support.wear.widget.util.MoreViewAssertions.right;
+import static android.support.wear.widget.util.MoreViewAssertions.screenBottom;
+import static android.support.wear.widget.util.MoreViewAssertions.screenLeft;
+import static android.support.wear.widget.util.MoreViewAssertions.screenRight;
+import static android.support.wear.widget.util.MoreViewAssertions.screenTop;
+import static android.support.wear.widget.util.MoreViewAssertions.top;
+
+import static org.hamcrest.Matchers.closeTo;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.wear.test.R;
+import android.support.wear.widget.util.WakeLockRule;
+import android.util.DisplayMetrics;
+import android.view.View;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BoxInsetLayoutTest {
+    private static final float FACTOR = 0.146467f; //(1 - sqrt(2)/2)/2
+
+    @Rule
+    public final WakeLockRule mWakeLock = new WakeLockRule();
+
+    @Rule
+    public final ActivityTestRule<LayoutTestActivity> mActivityRule = new ActivityTestRule<>(
+            LayoutTestActivity.class, true, false);
+
+    @Test
+    public void testCase1() throws Throwable {
+        mActivityRule.launchActivity(new Intent().putExtra(LayoutTestActivity
+                .EXTRA_LAYOUT_RESOURCE_ID, R.layout.box_inset_layout_testcase_1));
+        DisplayMetrics dm = InstrumentationRegistry.getTargetContext().getResources()
+                .getDisplayMetrics();
+        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
+
+        int desiredPadding = 0;
+        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
+            desiredPadding = boxInset;
+        }
+
+        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
+            @Override
+            public void run() {
+                View box = mActivityRule.getActivity().findViewById(R.id.box);
+                mIdViewMap.put(R.id.box, box);
+            }
+        };
+        mActivityRule.runOnUiThread(customRunnable);
+
+        View box = customRunnable.mIdViewMap.get(R.id.box);
+        // proxy for window location
+        View boxParent = (View) box.getParent();
+        int parentLeft = boxParent.getLeft();
+        int parentTop = boxParent.getTop();
+        int parentRight = boxParent.getLeft() + boxParent.getWidth();
+        int parentBottom = boxParent.getTop() + boxParent.getHeight();
+
+        // Child 1 is match_parent width and height
+        // layout_box=right|bottom
+        // Padding of boxInset should be added to the right and bottom sides only
+        onView(withId(R.id.child1))
+                .check(screenLeft(equalTo(parentLeft)))
+                .check(screenTop(equalTo(parentTop)))
+                .check(screenRight(equalTo(parentRight - desiredPadding)))
+                .check(screenBottom(equalTo(parentBottom - desiredPadding)));
+
+        // Content 1 is is width and height match_parent
+        // The bottom and right sides should be inset by boxInset pixels due to padding
+        // on the parent view
+        onView(withId(R.id.content1))
+                .check(screenLeft(equalTo(parentLeft)))
+                .check(screenTop(equalTo(parentTop)))
+                .check(screenRight(equalTo(parentRight - desiredPadding)))
+                .check(screenBottom(equalTo(parentBottom - desiredPadding)));
+    }
+
+    @Test
+    public void testCase2() throws Throwable {
+        mActivityRule.launchActivity(
+                new Intent().putExtra(LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                        R.layout.box_inset_layout_testcase_2));
+        DisplayMetrics dm =
+                InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
+        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
+
+        int desiredPadding = 0;
+        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
+            desiredPadding = boxInset;
+        }
+
+        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
+            @Override
+            public void run() {
+                View box = mActivityRule.getActivity().findViewById(R.id.box);
+                View child1 = mActivityRule.getActivity().findViewById(R.id.child1);
+                View child2 = mActivityRule.getActivity().findViewById(R.id.child2);
+                View child3 = mActivityRule.getActivity().findViewById(R.id.child3);
+                View child4 = mActivityRule.getActivity().findViewById(R.id.child4);
+                mIdViewMap.put(R.id.box, box);
+                mIdViewMap.put(R.id.child1, child1);
+                mIdViewMap.put(R.id.child2, child2);
+                mIdViewMap.put(R.id.child3, child3);
+                mIdViewMap.put(R.id.child4, child4);
+
+            }
+        };
+        mActivityRule.runOnUiThread(customRunnable);
+
+        View box = customRunnable.mIdViewMap.get(R.id.box);
+        View child1 = customRunnable.mIdViewMap.get(R.id.child1);
+        View child2 = customRunnable.mIdViewMap.get(R.id.child2);
+        View child3 = customRunnable.mIdViewMap.get(R.id.child3);
+        View child4 = customRunnable.mIdViewMap.get(R.id.child4);
+
+        // proxy for window location
+        View boxParent = (View) box.getParent();
+        int parentLeft = boxParent.getLeft();
+        int parentTop = boxParent.getTop();
+        int parentRight = boxParent.getLeft() + boxParent.getWidth();
+        int parentBottom = boxParent.getTop() + boxParent.getHeight();
+        int parentWidth = boxParent.getWidth();
+        int parentHeight = boxParent.getHeight();
+
+        // Child 1 is width match_parent, height=60dp, gravity top
+        // layout_box=all means it should have padding added to left, top and right
+        onView(withId(R.id.child1))
+                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
+                .check(screenTop(is(equalTo(parentTop + desiredPadding))))
+                .check(screenRight(is(equalTo(parentRight - desiredPadding))))
+                .check(screenBottom(is(equalTo(parentTop + desiredPadding + child1.getHeight()))));
+
+        // Content 1 is width and height match_parent
+        // the left top and right edges should be inset by boxInset pixels, due to
+        // padding in the parent
+        onView(withId(R.id.content1))
+                .check(screenLeft(equalTo(parentLeft + desiredPadding)))
+                .check(screenTop(equalTo(parentTop + desiredPadding)))
+                .check(screenRight(equalTo(parentRight - desiredPadding)));
+
+        // Child 2 is width match_parent, height=60dp, gravity bottom
+        // layout_box=all means it should have padding added to left, bottom and right
+        onView(withId(R.id.child2))
+                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
+                .check(screenTop(is(equalTo(parentBottom - desiredPadding - child2.getHeight()))))
+                .check(screenRight(is(equalTo(parentRight - desiredPadding))))
+                .check(screenBottom(is(equalTo(parentBottom - desiredPadding))));
+
+        // Content 2 is width and height match_parent
+        // the left bottom and right edges should be inset by boxInset pixels, due to
+        // padding in the parent
+        onView(withId(R.id.content2))
+                .check(screenLeft(equalTo(parentLeft + desiredPadding)))
+                .check(screenRight(equalTo(parentRight - desiredPadding)))
+                .check(screenBottom(equalTo(parentBottom - desiredPadding)));
+
+        // Child 3 is width wrap_content, height=20dp, gravity left|center_vertical.
+        // layout_box=all means it should have padding added to left
+        // marginLeft be ignored due to gravity and layout_box=all (screenLeft=0)
+        onView(withId(R.id.child3))
+                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
+                .check(approximateTop(is(closeTo((parentHeight / 2 - child3.getHeight() / 2), 1))))
+                .check(screenRight(is(equalTo(parentLeft + desiredPadding + child3.getWidth()))))
+                .check(approximateBottom(is(
+                        closeTo((parentHeight / 2 + child3.getHeight() / 2), 1))));
+
+        // Content 3 width and height match_parent
+        // the left edge should be offset from the screen edge by boxInset pixels, due to left on
+        // the parent
+        onView(withId(R.id.content3)).check(screenLeft(equalTo(desiredPadding)));
+
+        // Child 4 is width wrap_content, height=20dp, gravity right|center_vertical.
+        // layout_box=all means it should have padding added to right
+        // it should have marginRight ignored due to gravity and layout_box=all (screenRight=max)
+        onView(withId(R.id.child4))
+                .check(screenLeft(is(parentWidth - desiredPadding - child4.getWidth())))
+                .check(approximateTop(is(closeTo((parentHeight / 2 - child3.getHeight() / 2), 1))))
+                .check(screenRight(is(equalTo(parentWidth - desiredPadding))))
+                .check(approximateBottom(is(
+                        closeTo((parentHeight / 2 + child4.getHeight() / 2), 1))));
+
+        // Content 4 width and height wrap_content
+        // the right edge should be offset from the screen edge by boxInset pixels, due to
+        // right on the parent
+        onView(withId(R.id.content4)).check(screenRight(equalTo(parentWidth - desiredPadding)));
+    }
+
+    @Test
+    public void testCase3() throws Throwable {
+        mActivityRule.launchActivity(
+                new Intent().putExtra(LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                        R.layout.box_inset_layout_testcase_3));
+        DisplayMetrics dm =
+                InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
+        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
+
+        int desiredPadding = 0;
+        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
+            desiredPadding = boxInset;
+        }
+
+        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
+            @Override
+            public void run() {
+                View box = mActivityRule.getActivity().findViewById(R.id.box);
+                View child1 = mActivityRule.getActivity().findViewById(R.id.child1);
+                View child2 = mActivityRule.getActivity().findViewById(R.id.child2);
+                View child3 = mActivityRule.getActivity().findViewById(R.id.child3);
+                View child4 = mActivityRule.getActivity().findViewById(R.id.child4);
+                mIdViewMap.put(R.id.box, box);
+                mIdViewMap.put(R.id.child1, child1);
+                mIdViewMap.put(R.id.child2, child2);
+                mIdViewMap.put(R.id.child3, child3);
+                mIdViewMap.put(R.id.child4, child4);
+            }
+        };
+        mActivityRule.runOnUiThread(customRunnable);
+
+        View box = customRunnable.mIdViewMap.get(R.id.box);
+        View child1 = customRunnable.mIdViewMap.get(R.id.child1);
+        View child2 = customRunnable.mIdViewMap.get(R.id.child2);
+        View child3 = customRunnable.mIdViewMap.get(R.id.child3);
+        View child4 = customRunnable.mIdViewMap.get(R.id.child4);
+        // proxy for window location
+        View boxParent = (View) box.getParent();
+        int parentLeft = boxParent.getLeft();
+        int parentTop = boxParent.getTop();
+        int parentBottom = boxParent.getTop() + boxParent.getHeight();
+        int parentWidth = boxParent.getWidth();
+
+        // Child 1 is width and height wrap_content
+        // gravity is top|left, position should be 0,0 on screen
+        onView(withId(R.id.child1))
+                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
+                .check(screenTop(is(equalTo(parentTop + desiredPadding))))
+                .check(screenRight(is(equalTo(parentLeft + desiredPadding + child1.getWidth()))))
+                .check(screenBottom(is(equalTo(parentTop + desiredPadding + child1.getHeight()))));
+
+        // Content 1 is width and height wrap_content
+        // the left and top edges should be offset from the screen edges by boxInset pixels
+        onView(withId(R.id.content1))
+                .check(screenLeft(equalTo(parentLeft + desiredPadding)))
+                .check(screenTop(equalTo(parentTop + desiredPadding)));
+
+        // Child 2 is width and height wrap_content
+        // gravity is top|right, position should be 0,max on screen
+        onView(withId(R.id.child2))
+                .check(screenLeft(is(equalTo(parentWidth - desiredPadding - child2.getWidth()))))
+                .check(screenTop(is(equalTo(parentTop + desiredPadding))))
+                .check(screenRight(is(equalTo(parentWidth - desiredPadding))))
+                .check(screenBottom(is(equalTo(parentTop + desiredPadding + child2.getHeight()))));
+
+        // Content 2 is width and height wrap_content
+        // the top and right edges should be offset from the screen edges by boxInset pixels
+        onView(withId(R.id.content2))
+                .check(screenTop(equalTo(parentTop + desiredPadding)))
+                .check(screenRight(equalTo(parentWidth - desiredPadding)));
+
+        // Child 3 is width and height wrap_content
+        // gravity is bottom|right, position should be max,max on screen
+        onView(withId(R.id.child3))
+                .check(screenLeft(is(equalTo(parentWidth - desiredPadding - child3.getWidth()))))
+                .check(screenTop(is(
+                        equalTo(parentBottom - desiredPadding - child3.getHeight()))))
+                .check(screenRight(is(equalTo(parentWidth - desiredPadding))))
+                .check(screenBottom(is(equalTo(parentBottom - desiredPadding))));
+
+        // Content 3 is width and height wrap_content
+        // the right and bottom edges should be offset from the screen edges by boxInset pixels
+        onView(withId(R.id.content3))
+                .check(screenBottom(equalTo(parentBottom - desiredPadding)))
+                .check(screenRight(equalTo(parentWidth - desiredPadding)));
+
+        // Child 4 is width and height wrap_content
+        // gravity is bottom|left, position should be max,0 on screen
+        onView(withId(R.id.child4))
+                .check(screenLeft(is(equalTo(parentLeft + desiredPadding))))
+                .check(screenTop(is(equalTo(parentBottom - desiredPadding - child4.getHeight()))))
+                .check(screenRight(is(equalTo(parentLeft + desiredPadding + child4.getWidth()))))
+                .check(screenBottom(is(equalTo(parentBottom - desiredPadding))));
+
+        // Content 3 is width and height wrap_content
+        // the bottom and left edges should be offset from the screen edges by boxInset pixels
+        onView(withId(R.id.content4)).check(
+                screenBottom(equalTo(parentBottom - desiredPadding)))
+                .check(screenLeft(equalTo(parentLeft + desiredPadding)));
+    }
+
+    @Test
+    public void testCase4() throws Throwable {
+        mActivityRule.launchActivity(new Intent().putExtra(LayoutTestActivity
+                .EXTRA_LAYOUT_RESOURCE_ID, R.layout.box_inset_layout_testcase_4));
+        DisplayMetrics dm = InstrumentationRegistry.getTargetContext().getResources()
+                .getDisplayMetrics();
+        int boxInset = (int) (FACTOR * Math.min(dm.widthPixels, dm.heightPixels));
+
+        int desiredPadding = 0;
+        if (mActivityRule.getActivity().getResources().getConfiguration().isScreenRound()) {
+            desiredPadding = boxInset;
+        }
+
+        ViewFetchingRunnable customRunnable = new ViewFetchingRunnable(){
+            @Override
+            public void run() {
+                View container = mActivityRule.getActivity().findViewById(R.id.container);
+                View child1 = mActivityRule.getActivity().findViewById(R.id.child1);
+                mIdViewMap.put(R.id.container, container);
+                mIdViewMap.put(R.id.child1, child1);
+
+            }
+        };
+        mActivityRule.runOnUiThread(customRunnable);
+
+        View container = customRunnable.mIdViewMap.get(R.id.container);
+        View child1 = customRunnable.mIdViewMap.get(R.id.child1);
+        // Child 1 is match_parent width and wrap_content height
+        // layout_box=right|left
+        // Padding of boxInset should be added to the right and bottom sides only
+        onView(withId(R.id.child1)).check(left(equalTo(desiredPadding))).check(
+                top(equalTo(container.getTop()))).check(
+                right(equalTo(dm.widthPixels - desiredPadding))).check(
+                bottom(equalTo(container.getTop() + child1.getHeight())));
+    }
+
+    private abstract class ViewFetchingRunnable implements Runnable {
+        Map<Integer, View> mIdViewMap = new HashMap<>();
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/CircularProgressLayoutControllerTest.java b/wear/tests/src/android/support/wear/widget/CircularProgressLayoutControllerTest.java
new file mode 100644
index 0000000..8d5406d
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/CircularProgressLayoutControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.widget.CircularProgressDrawable;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class CircularProgressLayoutControllerTest {
+
+    private static final long TOTAL_TIME = TimeUnit.SECONDS.toMillis(1);
+    private static final long UPDATE_INTERVAL = TimeUnit.MILLISECONDS.toMillis(30);
+
+    private CircularProgressLayoutController mControllerUnderTest;
+
+    @Mock
+    CircularProgressDrawable mMockDrawable;
+    @Mock
+    CircularProgressLayout mMockLayout;
+    @Mock
+    CircularProgressLayout.OnTimerFinishedListener mMockListener;
+
+    @Before
+    public void setUp() {
+        mMockDrawable = mock(CircularProgressDrawable.class);
+        mMockLayout = mock(CircularProgressLayout.class);
+        mMockListener = mock(CircularProgressLayout.OnTimerFinishedListener.class);
+        when(mMockLayout.getProgressDrawable()).thenReturn(mMockDrawable);
+        when(mMockLayout.getOnTimerFinishedListener()).thenReturn(mMockListener);
+        mControllerUnderTest = new CircularProgressLayoutController(mMockLayout);
+    }
+
+    @Test
+    public void testSetIndeterminate() {
+        mControllerUnderTest.setIndeterminate(true);
+
+        assertEquals(true, mControllerUnderTest.isIndeterminate());
+        verify(mMockDrawable).start();
+    }
+
+    @Test
+    public void testIsIndeterminateAfterSetToFalse() {
+        mControllerUnderTest.setIndeterminate(true);
+        mControllerUnderTest.setIndeterminate(false);
+
+        assertEquals(false, mControllerUnderTest.isIndeterminate());
+        verify(mMockDrawable).stop();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testIsTimerRunningAfterStart() {
+        mControllerUnderTest.startTimer(TOTAL_TIME, UPDATE_INTERVAL);
+
+        assertEquals(true, mControllerUnderTest.isTimerRunning());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testIsTimerRunningAfterStop() {
+        mControllerUnderTest.startTimer(TOTAL_TIME, UPDATE_INTERVAL);
+        mControllerUnderTest.stopTimer();
+
+        assertEquals(false, mControllerUnderTest.isTimerRunning());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSwitchFromIndeterminateToDeterminate() {
+        mControllerUnderTest.setIndeterminate(true);
+        mControllerUnderTest.startTimer(TOTAL_TIME, UPDATE_INTERVAL);
+
+        assertEquals(false, mControllerUnderTest.isIndeterminate());
+        assertEquals(true, mControllerUnderTest.isTimerRunning());
+        verify(mMockDrawable).stop();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSwitchFromDeterminateToIndeterminate() {
+        mControllerUnderTest.startTimer(TOTAL_TIME, UPDATE_INTERVAL);
+        mControllerUnderTest.setIndeterminate(true);
+
+        assertEquals(true, mControllerUnderTest.isIndeterminate());
+        assertEquals(false, mControllerUnderTest.isTimerRunning());
+        verify(mMockDrawable).start();
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/CircularProgressLayoutTest.java b/wear/tests/src/android/support/wear/widget/CircularProgressLayoutTest.java
new file mode 100644
index 0000000..ff98c30
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/CircularProgressLayoutTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.wear.test.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class CircularProgressLayoutTest {
+
+    private static final long TOTAL_TIME = TimeUnit.SECONDS.toMillis(1);
+
+    @Rule
+    public final ActivityTestRule<LayoutTestActivity> mActivityRule = new ActivityTestRule<>(
+            LayoutTestActivity.class, true, false);
+    private CircularProgressLayout mLayoutUnderTest;
+
+    @Before
+    public void setUp() {
+        mActivityRule.launchActivity(new Intent().putExtra(LayoutTestActivity
+                .EXTRA_LAYOUT_RESOURCE_ID, R.layout.circular_progress_layout));
+        mLayoutUnderTest = mActivityRule.getActivity().findViewById(R.id.circular_progress_layout);
+        mLayoutUnderTest.setOnTimerFinishedListener(new FakeListener());
+    }
+
+    @Test
+    public void testListenerIsNotified() {
+        mLayoutUnderTest.setTotalTime(TOTAL_TIME);
+        startTimerOnUiThread();
+        waitForTimer(TOTAL_TIME + 100);
+        assertNotNull(mLayoutUnderTest.getOnTimerFinishedListener());
+        assertTrue(((FakeListener) mLayoutUnderTest.getOnTimerFinishedListener()).mFinished);
+    }
+
+    @Test
+    public void testListenerIsNotNotifiedWhenStopped() {
+        mLayoutUnderTest.setTotalTime(TOTAL_TIME);
+        startTimerOnUiThread();
+        stopTimerOnUiThread();
+        waitForTimer(TOTAL_TIME + 100);
+        assertNotNull(mLayoutUnderTest.getOnTimerFinishedListener());
+        assertFalse(((FakeListener) mLayoutUnderTest.getOnTimerFinishedListener()).mFinished);
+    }
+
+    private class FakeListener implements CircularProgressLayout.OnTimerFinishedListener {
+
+        boolean mFinished;
+
+        @Override
+        public void onTimerFinished(CircularProgressLayout layout) {
+            mFinished = true;
+        }
+    }
+
+    private void startTimerOnUiThread() {
+        mActivityRule.getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mLayoutUnderTest.startTimer();
+            }
+        });
+    }
+
+    private void stopTimerOnUiThread() {
+        mActivityRule.getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mLayoutUnderTest.stopTimer();
+            }
+        });
+    }
+
+    private void waitForTimer(long time) {
+        try {
+            Thread.sleep(time);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/LayoutTestActivity.java b/wear/tests/src/android/support/wear/widget/LayoutTestActivity.java
new file mode 100644
index 0000000..ec909db
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/LayoutTestActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LayoutTestActivity extends Activity {
+    public static final String EXTRA_LAYOUT_RESOURCE_ID = "layout_resource_id";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+        if (!intent.hasExtra(EXTRA_LAYOUT_RESOURCE_ID)) {
+            throw new IllegalArgumentException(
+                    "Intent extras must contain EXTRA_LAYOUT_RESOURCE_ID");
+        }
+        int layoutId = intent.getIntExtra(EXTRA_LAYOUT_RESOURCE_ID, -1);
+        setContentView(layoutId);
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/RoundedDrawableTest.java b/wear/tests/src/android/support/wear/widget/RoundedDrawableTest.java
new file mode 100644
index 0000000..dbe575f
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/RoundedDrawableTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+package android.support.wear.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/** Tests for {@link RoundedDrawable} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RoundedDrawableTest {
+
+    @Rule
+    public final ActivityTestRule<LayoutTestActivity> mActivityRule = new ActivityTestRule<>(
+            LayoutTestActivity.class, true, false);
+    private static final int BITMAP_WIDTH = 64;
+    private static final int BITMAP_HEIGHT = 32;
+
+    private RoundedDrawable mRoundedDrawable;
+    private BitmapDrawable mBitmapDrawable;
+
+    @Mock
+    Canvas mMockCanvas;
+
+    @Before
+    public void setUp() {
+        mMockCanvas = mock(Canvas.class);
+        mActivityRule.launchActivity(new Intent().putExtra(LayoutTestActivity
+                        .EXTRA_LAYOUT_RESOURCE_ID,
+                android.support.wear.test.R.layout.rounded_drawable_layout));
+        mRoundedDrawable = new RoundedDrawable();
+        mBitmapDrawable =
+                new BitmapDrawable(
+                        mActivityRule.getActivity().getResources(),
+                        Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888));
+    }
+
+    @Test
+    public void colorFilterIsAppliedCorrectly() {
+        ColorFilter cf = new ColorFilter();
+        mRoundedDrawable.setColorFilter(cf);
+        assertEquals(cf, mRoundedDrawable.mPaint.getColorFilter());
+    }
+
+    @Test
+    public void alphaIsAppliedCorrectly() {
+        int alpha = 128;
+        mRoundedDrawable.setAlpha(alpha);
+        assertEquals(alpha, mRoundedDrawable.mPaint.getAlpha());
+    }
+
+    @Test
+    public void radiusIsAppliedCorrectly() {
+        int radius = 10;
+        Rect bounds = new Rect(0, 0, BITMAP_WIDTH, BITMAP_HEIGHT);
+        mRoundedDrawable.setDrawable(mBitmapDrawable);
+        mRoundedDrawable.setClipEnabled(true);
+        mRoundedDrawable.setRadius(radius);
+        mRoundedDrawable.setBounds(bounds);
+        mRoundedDrawable.draw(mMockCanvas);
+        // One for background and one for the actual drawable, this should be called two times.
+        verify(mMockCanvas, times(2))
+                .drawRoundRect(
+                        eq(new RectF(0, 0, bounds.width(), bounds.height())),
+                        eq((float) radius),
+                        eq((float) radius),
+                        any(Paint.class));
+    }
+
+    @Test
+    public void scalingIsAppliedCorrectly() {
+        int radius = 14;
+        // 14 px radius should apply 5 px padding due to formula ceil(radius * 1 - 1 / sqrt(2))
+        Rect bounds = new Rect(0, 0, BITMAP_WIDTH, BITMAP_HEIGHT);
+        mRoundedDrawable.setDrawable(mBitmapDrawable);
+        mRoundedDrawable.setClipEnabled(false);
+        mRoundedDrawable.setRadius(radius);
+        mRoundedDrawable.setBounds(bounds);
+        mRoundedDrawable.draw(mMockCanvas);
+        assertEquals(BITMAP_WIDTH - 10, mBitmapDrawable.getBounds().width());
+        assertEquals(BITMAP_HEIGHT - 10, mBitmapDrawable.getBounds().height());
+        assertEquals(bounds.centerX(), mBitmapDrawable.getBounds().centerX());
+        assertEquals(bounds.centerY(), mBitmapDrawable.getBounds().centerY());
+        // Background should also be drawn
+        verify(mMockCanvas)
+                .drawRoundRect(
+                        eq(new RectF(0, 0, bounds.width(), bounds.height())),
+                        eq((float) radius),
+                        eq((float) radius),
+                        any(Paint.class));
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/ScrollManagerTest.java b/wear/tests/src/android/support/wear/widget/ScrollManagerTest.java
new file mode 100644
index 0000000..34faea3
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/ScrollManagerTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.wear.widget.util.WakeLockRule;
+import android.view.MotionEvent;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ScrollManagerTest {
+    private static final int TEST_WIDTH = 400;
+    private static final int TEST_HEIGHT = 400;
+    private static final int STEP_COUNT = 300;
+
+    private static final int EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE = 36;
+    private static final int EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE = 199;
+
+    @Rule
+    public final WakeLockRule wakeLock = new WakeLockRule();
+
+    @Rule
+    public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true);
+
+    @Mock
+    WearableRecyclerView mMockWearableRecyclerView;
+
+    ScrollManager mScrollManagerUnderTest;
+
+    @Before
+    public void setUp() throws Throwable {
+        MockitoAnnotations.initMocks(this);
+        mScrollManagerUnderTest = new ScrollManager();
+        mScrollManagerUnderTest.setRecyclerView(mMockWearableRecyclerView, TEST_WIDTH, TEST_HEIGHT);
+    }
+
+    @Test
+    public void testStraightUpScrollingGestureLeft() throws Throwable {
+        // Pretend to scroll in a straight line from center left to upper left
+        scroll(mScrollManagerUnderTest, 30, 30, 200, 150);
+        // The scroll manager should require the recycler view to scroll up and only up
+        verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
+                .scrollBy(0, 1);
+    }
+
+    @Test
+    public void testStraightDownScrollingGestureLeft() throws Throwable {
+        // Pretend to scroll in a straight line upper left to center left
+        scroll(mScrollManagerUnderTest, 30, 30, 150, 200);
+        // The scroll manager should require the recycler view to scroll down and only down
+        verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
+                .scrollBy(0, -1);
+    }
+
+    @Test
+    public void testStraightUpScrollingGestureRight() throws Throwable {
+        // Pretend to scroll in a straight line from center right to upper right
+        scroll(mScrollManagerUnderTest, 370, 370, 200, 150);
+        // The scroll manager should require the recycler view to scroll down and only down
+        verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
+                .scrollBy(0, -1);
+    }
+
+    @Test
+    public void testStraightDownScrollingGestureRight() throws Throwable {
+        // Pretend to scroll in a straight line upper right to center right
+        scroll(mScrollManagerUnderTest, 370, 370, 150, 200);
+        // The scroll manager should require the recycler view to scroll up and only up
+        verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_STRAIGHT_GESTURE))
+                .scrollBy(0, 1);
+    }
+
+    @Test
+    public void testCircularScrollingGestureLeft() throws Throwable {
+        // Pretend to scroll in an arch from center left to center right
+        scrollOnArch(mScrollManagerUnderTest, 30, 200, 180.0f);
+        // The scroll manager should never reverse the scroll direction and scroll up
+        verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE))
+                .scrollBy(0, 1);
+    }
+
+    @Test
+    public void testCircularScrollingGestureRight() throws Throwable {
+        // Pretend to scroll in an arch from center left to center right
+        scrollOnArch(mScrollManagerUnderTest, 370, 200, -180.0f);
+        // The scroll manager should never reverse the scroll direction and scroll down.
+        verify(mMockWearableRecyclerView, times(EXPECTED_SCROLLS_FOR_CIRCULAR_GESTURE))
+                .scrollBy(0, -1);
+    }
+
+    private static void scroll(ScrollManager scrollManager, float fromX, float toX, float fromY,
+            float toY) {
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+
+        float y = fromY;
+        float x = fromX;
+
+        float yStep = (toY - fromY) / STEP_COUNT;
+        float xStep = (toX - fromX) / STEP_COUNT;
+
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
+        scrollManager.onTouchEvent(event);
+        for (int i = 0; i < STEP_COUNT; ++i) {
+            y += yStep;
+            x += xStep;
+            eventTime = SystemClock.uptimeMillis();
+            event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
+            scrollManager.onTouchEvent(event);
+        }
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        scrollManager.onTouchEvent(event);
+    }
+
+    private static void scrollOnArch(ScrollManager scrollManager, float fromX, float fromY,
+            float deltaAngle) {
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+
+        float stepAngle = deltaAngle / STEP_COUNT;
+        double relativeX = fromX - (TEST_WIDTH / 2);
+        double relativeY = fromY - (TEST_HEIGHT / 2);
+        float radius = (float) Math.sqrt(relativeX * relativeX + relativeY * relativeY);
+        float angle = getAngle(fromX, fromY, TEST_WIDTH, TEST_HEIGHT);
+
+        float y = fromY;
+        float x = fromX;
+
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
+        scrollManager.onTouchEvent(event);
+        for (int i = 0; i < STEP_COUNT; ++i) {
+            angle += stepAngle;
+            x = getX(angle, radius, TEST_WIDTH);
+            y = getY(angle, radius, TEST_HEIGHT);
+            eventTime = SystemClock.uptimeMillis();
+            event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
+            scrollManager.onTouchEvent(event);
+        }
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        scrollManager.onTouchEvent(event);
+    }
+
+    private static float getX(double angle, double radius, double viewWidth) {
+        double radianAngle = Math.toRadians(angle - 90);
+        double relativeX = cos(radianAngle) * radius;
+        return (float) (relativeX + (viewWidth / 2));
+    }
+
+    private static float getY(double angle, double radius, double viewHeight) {
+        double radianAngle = Math.toRadians(angle - 90);
+        double relativeY = sin(radianAngle) * radius;
+        return (float) (relativeY + (viewHeight / 2));
+    }
+
+    private static float getAngle(double x, double y, double viewWidth, double viewHeight) {
+        double relativeX = x - (viewWidth / 2);
+        double relativeY = y - (viewHeight / 2);
+        double rowAngle = Math.atan2(relativeX, relativeY);
+        double angle = -Math.toDegrees(rowAngle) - 180;
+        if (angle < 0) {
+            angle += 360;
+        }
+        return (float) angle;
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/SwipeDismissFrameLayoutTest.java b/wear/tests/src/android/support/wear/widget/SwipeDismissFrameLayoutTest.java
new file mode 100644
index 0000000..e4e47aa
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/SwipeDismissFrameLayoutTest.java
@@ -0,0 +1,460 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.wear.widget.util.AsyncViewActions.waitForMatchingView;
+import static android.support.wear.widget.util.MoreViewAssertions.withPositiveVerticalScrollOffset;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.RectF;
+import android.support.annotation.IdRes;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.GeneralLocation;
+import android.support.test.espresso.action.GeneralSwipeAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Swipe;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.test.R;
+import android.support.wear.widget.util.ArcSwipe;
+import android.support.wear.widget.util.WakeLockRule;
+import android.view.View;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SwipeDismissFrameLayoutTest {
+
+    private static final long MAX_WAIT_TIME = 4000; //ms
+    private final SwipeDismissFrameLayout.Callback mDismissCallback = new DismissCallback();
+
+    @Rule
+    public final WakeLockRule wakeLock = new WakeLockRule();
+
+    @Rule
+    public final ActivityTestRule<SwipeDismissFrameLayoutTestActivity> activityRule =
+            new ActivityTestRule<>(
+                    SwipeDismissFrameLayoutTestActivity.class,
+                    true, /** initial touch mode */
+                    false /** launchActivity */
+            );
+
+    private int mLayoutWidth;
+    private int mLayoutHeight;
+
+    @Test
+    @SmallTest
+    public void testCanScrollHorizontally() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout
+        setUpSimpleLayout();
+        Activity activity = activityRule.getActivity();
+        SwipeDismissFrameLayout testLayout =
+                (SwipeDismissFrameLayout) activity.findViewById(R.id.swipe_dismiss_root);
+        // WHEN we check that the layout is horizontally scrollable from left to right.
+        // THEN the layout is found to be horizontally swipeable from left to right.
+        assertTrue(testLayout.canScrollHorizontally(-20));
+        // AND the layout is found to NOT be horizontally swipeable from right to left.
+        assertFalse(testLayout.canScrollHorizontally(20));
+
+        // WHEN we switch off the swipe-to-dismiss functionality for the layout
+        testLayout.setSwipeable(false);
+        // THEN the layout is found NOT to be horizontally swipeable from left to right.
+        assertFalse(testLayout.canScrollHorizontally(-20));
+        // AND the layout is found to NOT be horizontally swipeable from right to left.
+        assertFalse(testLayout.canScrollHorizontally(20));
+    }
+
+    @Test
+    @SmallTest
+    public void canScrollHorizontallyShouldBeFalseWhenInvisible() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout
+        setUpSimpleLayout();
+        Activity activity = activityRule.getActivity();
+        final SwipeDismissFrameLayout testLayout = activity.findViewById(R.id.swipe_dismiss_root);
+        // GIVEN the layout is invisible
+        // Note: We have to run this on the main thread, because of thread checks in View.java.
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                testLayout.setVisibility(View.INVISIBLE);
+            }
+        });
+        // WHEN we check that the layout is horizontally scrollable
+        // THEN the layout is found to be NOT horizontally swipeable from left to right.
+        assertFalse(testLayout.canScrollHorizontally(-20));
+        // AND the layout is found to NOT be horizontally swipeable from right to left.
+        assertFalse(testLayout.canScrollHorizontally(20));
+    }
+
+    @Test
+    @SmallTest
+    public void canScrollHorizontallyShouldBeFalseWhenGone() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout
+        setUpSimpleLayout();
+        Activity activity = activityRule.getActivity();
+        final SwipeDismissFrameLayout testLayout =
+                (SwipeDismissFrameLayout) activity.findViewById(R.id.swipe_dismiss_root);
+        // GIVEN the layout is gone
+        // Note: We have to run this on the main thread, because of thread checks in View.java.
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                testLayout.setVisibility(View.GONE);
+            }
+        });
+        // WHEN we check that the layout is horizontally scrollable
+        // THEN the layout is found to be NOT horizontally swipeable from left to right.
+        assertFalse(testLayout.canScrollHorizontally(-20));
+        // AND the layout is found to NOT be horizontally swipeable from right to left.
+        assertFalse(testLayout.canScrollHorizontally(20));
+    }
+
+    @Test
+    @SmallTest
+    public void testSwipeDismissEnabledByDefault() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout
+        setUpSimpleLayout();
+        Activity activity = activityRule.getActivity();
+        SwipeDismissFrameLayout testLayout =
+                (SwipeDismissFrameLayout) activity.findViewById(R.id.swipe_dismiss_root);
+        // WHEN we check that the layout is dismissible
+        // THEN the layout is find to be dismissible
+        assertTrue(testLayout.isSwipeable());
+    }
+
+    @Test
+    @SmallTest
+    public void testSwipeDismissesViewIfEnabled() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout
+        setUpSimpleLayout();
+        // WHEN we perform a swipe to dismiss
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRight());
+        // AND hidden
+        assertHidden(R.id.swipe_dismiss_root);
+    }
+
+    @Test
+    @SmallTest
+    public void testSwipeDoesNotDismissViewIfDisabled() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned off.
+        setUpSimpleLayout();
+        Activity activity = activityRule.getActivity();
+        SwipeDismissFrameLayout testLayout =
+                (SwipeDismissFrameLayout) activity.findViewById(R.id.swipe_dismiss_root);
+        testLayout.setSwipeable(false);
+        // WHEN we perform a swipe to dismiss
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRight());
+        // THEN the layout is not hidden
+        assertNotHidden(R.id.swipe_dismiss_root);
+    }
+
+    @Test
+    @SmallTest
+    public void testAddRemoveCallback() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout
+        setUpSimpleLayout();
+        Activity activity = activityRule.getActivity();
+        SwipeDismissFrameLayout testLayout = activity.findViewById(R.id.swipe_dismiss_root);
+        // WHEN we remove the swipe callback
+        testLayout.removeCallback(mDismissCallback);
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRight());
+        // THEN the layout is not hidden
+        assertNotHidden(R.id.swipe_dismiss_root);
+    }
+
+    @Test
+    @SmallTest
+    public void testSwipeDoesNotDismissViewIfScrollable() throws Throwable {
+        // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned off.
+        setUpSwipeDismissWithHorizontalRecyclerView();
+        activityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Activity activity = activityRule.getActivity();
+                RecyclerView testLayout = activity.findViewById(R.id.recycler_container);
+                // Scroll to a position from which the child is scrollable.
+                testLayout.scrollToPosition(50);
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        // WHEN we perform a swipe to dismiss from the center of the screen.
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRightFromCenter());
+        // THEN the layout is not hidden
+        assertNotHidden(R.id.swipe_dismiss_root);
+    }
+
+
+    @Test
+    @SmallTest
+    public void testEdgeSwipeDoesDismissViewIfScrollable() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned off.
+        setUpSwipeDismissWithHorizontalRecyclerView();
+        // WHEN we perform a swipe to dismiss from the left edge of the screen.
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRightFromLeftEdge());
+        // THEN the layout is hidden
+        assertHidden(R.id.swipe_dismiss_root);
+    }
+
+    @Test
+    @SmallTest
+    public void testSwipeDoesNotDismissViewIfStartsInWrongPosition() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned on, but only for an
+        // inner circle.
+        setUpSwipeableRegion();
+        // WHEN we perform a swipe to dismiss from the left edge of the screen.
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRightFromLeftEdge());
+        // THEN the layout is not not hidden
+        assertNotHidden(R.id.swipe_dismiss_root);
+    }
+
+    @Test
+    @SmallTest
+    public void testSwipeDoesDismissViewIfStartsInRightPosition() {
+        // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned on, but only for an
+        // inner circle.
+        setUpSwipeableRegion();
+        // WHEN we perform a swipe to dismiss from the center of the screen.
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeRightFromCenter());
+        // THEN the layout is hidden
+        assertHidden(R.id.swipe_dismiss_root);
+    }
+
+    /**
+     @Test public void testSwipeInPreferenceFragmentAndNavDrawer() {
+     // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned on, but only for an inner
+     // circle.
+     setUpPreferenceFragmentAndNavDrawer();
+     // WHEN we perform a swipe to dismiss from the center of the screen to the bottom.
+     onView(withId(R.id.drawer_layout)).perform(swipeBottomFromCenter());
+     // THEN the navigation drawer is shown.
+     assertPeeking(R.id.top_drawer);
+     }*/
+
+    @Test
+    @SmallTest
+    public void testArcSwipeDoesNotTriggerDismiss() throws Throwable {
+        // GIVEN a freshly setup SwipeDismissFrameLayout with vertically scrollable content
+        setUpSwipeDismissWithVerticalRecyclerView();
+        int center = mLayoutHeight / 2;
+        int halfBound = mLayoutWidth / 2;
+        RectF bounds = new RectF(0, center - halfBound, mLayoutWidth, center + halfBound);
+        // WHEN the view is scrolled on an arc from top to bottom.
+        onView(withId(R.id.swipe_dismiss_root)).perform(swipeTopFromBottomOnArc(bounds));
+        // THEN the layout is not dismissed and not hidden.
+        assertNotHidden(R.id.swipe_dismiss_root);
+        // AND the content view is scrolled.
+        assertScrolledY(R.id.recycler_container);
+    }
+
+    /**
+     * Set ups the simplest possible layout for test cases - a {@link SwipeDismissFrameLayout} with
+     * a single static child.
+     */
+    private void setUpSimpleLayout() {
+        activityRule.launchActivity(
+                new Intent()
+                        .putExtra(
+                                LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                                R.layout.swipe_dismiss_layout_testcase_1));
+        setDismissCallback();
+    }
+
+
+    /**
+     * Sets up a slightly more involved layout for testing swipe-to-dismiss with scrollable
+     * containers. This layout contains a {@link SwipeDismissFrameLayout} with a horizontal {@link
+     * android.support.v7.widget.RecyclerView} as a child, ready to accept an adapter.
+     */
+    private void setUpSwipeDismissWithHorizontalRecyclerView() {
+        Intent launchIntent = new Intent();
+        launchIntent.putExtra(LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.swipe_dismiss_layout_testcase_2);
+        launchIntent.putExtra(SwipeDismissFrameLayoutTestActivity.EXTRA_LAYOUT_HORIZONTAL, true);
+        activityRule.launchActivity(launchIntent);
+        setDismissCallback();
+    }
+
+    /**
+     * Sets up a slightly more involved layout for testing swipe-to-dismiss with scrollable
+     * containers. This layout contains a {@link SwipeDismissFrameLayout} with a vertical {@link
+     * WearableRecyclerView} as a child, ready to accept an adapter.
+     */
+    private void setUpSwipeDismissWithVerticalRecyclerView() {
+        Intent launchIntent = new Intent();
+        launchIntent.putExtra(LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.swipe_dismiss_layout_testcase_2);
+        launchIntent.putExtra(SwipeDismissFrameLayoutTestActivity.EXTRA_LAYOUT_HORIZONTAL, false);
+        activityRule.launchActivity(launchIntent);
+        setDismissCallback();
+    }
+
+    /**
+     * Sets up a {@link SwipeDismissFrameLayout} in which only a certain region is allowed to react
+     * to swipe-dismiss gestures.
+     */
+    private void setUpSwipeableRegion() {
+        activityRule.launchActivity(
+                new Intent()
+                        .putExtra(
+                                LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                                R.layout.swipe_dismiss_layout_testcase_1));
+        setCallback(
+                new DismissCallback() {
+                    @Override
+                    public boolean onPreSwipeStart(SwipeDismissFrameLayout layout, float x,
+                            float y) {
+                        float normalizedX = x - mLayoutWidth / 2;
+                        float normalizedY = y - mLayoutWidth / 2;
+                        float squareX = normalizedX * normalizedX;
+                        float squareY = normalizedY * normalizedY;
+                        // 30 is an arbitrary number limiting the circle.
+                        return Math.sqrt(squareX + squareY) < (mLayoutWidth / 2 - 30);
+                    }
+                });
+    }
+
+    /**
+     * Sets up a more involved test case where the layout consists of a
+     * {@code WearableNavigationDrawer} and a
+     * {@code android.support.wear.internal.view.SwipeDismissPreferenceFragment}
+     */
+  /*
+  private void setUpPreferenceFragmentAndNavDrawer() {
+    activityRule.launchActivity(
+      new Intent()
+          .putExtra(
+              LayoutTestActivity.EXTRA_LAYOUT_RESOURCE_ID,
+              R.layout.swipe_dismiss_layout_testcase_3));
+    Activity activity = activityRule.getActivity();
+    InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+      WearableNavigationDrawer wearableNavigationDrawer =
+              (WearableNavigationDrawer) activity.findViewById(R.id.top_drawer);
+      wearableNavigationDrawer.setAdapter(
+              new WearableNavigationDrawer.WearableNavigationDrawerAdapter() {
+                @Override
+                public String getItemText(int pos) {
+                  return "test";
+                }
+
+                @Override
+                public Drawable getItemDrawable(int pos) {
+                  return null;
+                }
+
+                @Override
+                public void onItemSelected(int pos) {
+                  return;
+                }
+
+                @Override
+                public int getCount() {
+                  return 3;
+                }
+              });
+    });
+  }*/
+    private void setDismissCallback() {
+        setCallback(mDismissCallback);
+    }
+
+    private void setCallback(SwipeDismissFrameLayout.Callback callback) {
+        Activity activity = activityRule.getActivity();
+        SwipeDismissFrameLayout testLayout = activity.findViewById(R.id.swipe_dismiss_root);
+        mLayoutWidth = testLayout.getWidth();
+        mLayoutHeight = testLayout.getHeight();
+        testLayout.addCallback(callback);
+    }
+
+    /**
+     * private static void assertPeeking(@IdRes int layoutId) {
+     * onView(withId(layoutId))
+     * .perform(
+     * waitForMatchingView(
+     * allOf(withId(layoutId), isOpened(true)), MAX_WAIT_TIME));
+     * }
+     */
+
+    private static void assertHidden(@IdRes int layoutId) {
+        onView(withId(layoutId))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(layoutId),
+                                        withEffectiveVisibility(ViewMatchers.Visibility.GONE)),
+                                MAX_WAIT_TIME));
+    }
+
+    private static void assertNotHidden(@IdRes int layoutId) {
+        onView(withId(layoutId))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(layoutId),
+                                        withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)),
+                                MAX_WAIT_TIME));
+    }
+
+    private static void assertScrolledY(@IdRes int layoutId) {
+        onView(withId(layoutId))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(layoutId), withPositiveVerticalScrollOffset()),
+                                MAX_WAIT_TIME));
+    }
+
+    private static ViewAction swipeRightFromCenter() {
+        return new GeneralSwipeAction(
+                Swipe.SLOW, GeneralLocation.CENTER, GeneralLocation.CENTER_RIGHT, Press.FINGER);
+    }
+
+    private static ViewAction swipeRightFromLeftEdge() {
+        return new GeneralSwipeAction(
+                Swipe.SLOW, GeneralLocation.CENTER_LEFT, GeneralLocation.CENTER_RIGHT,
+                Press.FINGER);
+    }
+
+    private static ViewAction swipeTopFromBottomOnArc(RectF bounds) {
+        return new GeneralSwipeAction(
+                new ArcSwipe(ArcSwipe.Gesture.SLOW_ANTICLOCKWISE, bounds),
+                GeneralLocation.BOTTOM_CENTER,
+                GeneralLocation.TOP_CENTER,
+                Press.FINGER);
+    }
+
+    /** Helper class hiding the view after a successful swipe-to-dismiss. */
+    private static class DismissCallback extends SwipeDismissFrameLayout.Callback {
+
+        @Override
+        public void onDismissed(SwipeDismissFrameLayout layout) {
+            layout.setVisibility(View.GONE);
+        }
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/SwipeDismissFrameLayoutTestActivity.java b/wear/tests/src/android/support/wear/widget/SwipeDismissFrameLayoutTestActivity.java
new file mode 100644
index 0000000..5d86832
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/SwipeDismissFrameLayoutTestActivity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.test.R;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class SwipeDismissFrameLayoutTestActivity extends LayoutTestActivity {
+
+    public static final String EXTRA_LAYOUT_HORIZONTAL = "layout_horizontal";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        int layoutId = getIntent().getIntExtra(EXTRA_LAYOUT_RESOURCE_ID, -1);
+        boolean horizontal = getIntent().getBooleanExtra(EXTRA_LAYOUT_HORIZONTAL, false);
+
+        if (layoutId == R.layout.swipe_dismiss_layout_testcase_2) {
+            createScrollableContent(horizontal);
+        }
+    }
+
+    private void createScrollableContent(boolean horizontal) {
+        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_container);
+        if (recyclerView == null) {
+            throw new NullPointerException("There has to be a relevant container defined");
+        }
+        recyclerView.setLayoutManager(
+                new LinearLayoutManager(
+                        this,
+                        horizontal ? LinearLayoutManager.HORIZONTAL : LinearLayoutManager.VERTICAL,
+                        false));
+        recyclerView.setAdapter(new MyRecyclerViewAdapter());
+    }
+
+    private static class MyRecyclerViewAdapter
+            extends RecyclerView.Adapter<MyRecyclerViewAdapter.CustomViewHolder> {
+        @Override
+        public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            TextView textView = new TextView(parent.getContext());
+            textView.setText("A LOT OF TEXT VIEW");
+            textView.setGravity(Gravity.CENTER_VERTICAL);
+            return new CustomViewHolder(textView);
+        }
+
+        @Override
+        public void onBindViewHolder(CustomViewHolder holder, int position) {
+        }
+
+        @Override
+        public int getItemCount() {
+            return 100;
+        }
+
+        static class CustomViewHolder extends RecyclerView.ViewHolder {
+
+            CustomViewHolder(View view) {
+                super(view);
+            }
+        }
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/SwipeDismissPreferenceFragment.java b/wear/tests/src/android/support/wear/widget/SwipeDismissPreferenceFragment.java
new file mode 100644
index 0000000..a892cb6
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/SwipeDismissPreferenceFragment.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+import android.support.wear.widget.SwipeDismissFrameLayout.Callback;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * {@link PreferenceFragment} that supports swipe-to-dismiss.
+ *
+ * <p>Unlike a regular PreferenceFragment, this Fragment has a solid color background using the
+ * background color from the theme. This allows the fragment to be layered on top of other
+ * fragments so that the previous layer is seen when this fragment is swiped away.
+ */
+public class SwipeDismissPreferenceFragment extends PreferenceFragment {
+
+    private SwipeDismissFrameLayout mSwipeLayout;
+
+    private final Callback mCallback =
+            new Callback() {
+                @Override
+                public void onSwipeStarted(SwipeDismissFrameLayout layout) {
+                    SwipeDismissPreferenceFragment.this.onSwipeStart();
+                }
+
+                @Override
+                public void onSwipeCanceled(SwipeDismissFrameLayout layout) {
+                    SwipeDismissPreferenceFragment.this.onSwipeCancelled();
+                }
+
+                @Override
+                public void onDismissed(SwipeDismissFrameLayout layout) {
+                    SwipeDismissPreferenceFragment.this.onDismiss();
+                }
+            };
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+        mSwipeLayout = new SwipeDismissFrameLayout(getActivity());
+        mSwipeLayout.addCallback(mCallback);
+
+        View contents = super.onCreateView(inflater, mSwipeLayout, savedInstanceState);
+
+        mSwipeLayout.setBackgroundColor(getBackgroundColor());
+        mSwipeLayout.addView(contents);
+
+        return mSwipeLayout;
+    }
+
+    /** Called when the fragment is dismissed with a swipe. */
+    public void onDismiss() {
+    }
+
+    /** Called when a swipe-to-dismiss gesture is started. */
+    public void onSwipeStart() {
+    }
+
+    /** Called when a swipe-to-dismiss gesture is cancelled. */
+    public void onSwipeCancelled() {
+    }
+
+    /**
+     * Sets whether or not the preferences list can be focused. If {@code focusable} is false, any
+     * existing focus will be cleared.
+     */
+    public void setFocusable(boolean focusable) {
+        if (focusable) {
+            mSwipeLayout.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+            mSwipeLayout.setFocusable(true);
+        } else {
+            // Prevent any child views from receiving focus.
+            mSwipeLayout.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+
+            mSwipeLayout.setFocusable(false);
+            mSwipeLayout.clearFocus();
+        }
+    }
+
+    private int getBackgroundColor() {
+        TypedValue value = new TypedValue();
+        getActivity().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
+        return value.data;
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/WearableLinearLayoutManagerTest.java b/wear/tests/src/android/support/wear/widget/WearableLinearLayoutManagerTest.java
new file mode 100644
index 0000000..74b0f3a
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/WearableLinearLayoutManagerTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.app.Activity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.wear.test.R;
+import android.support.wear.widget.util.WakeLockRule;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class WearableLinearLayoutManagerTest {
+
+    @Rule
+    public final WakeLockRule wakeLock = new WakeLockRule();
+
+    @Rule
+    public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true);
+
+    WearableLinearLayoutManager mWearableLinearLayoutManagerUnderTest;
+
+    @Before
+    public void setUp() throws Throwable {
+        Activity activity = mActivityRule.getActivity();
+        CurvingLayoutCallback mCurvingCallback = new CurvingLayoutCallback(activity);
+        mCurvingCallback.setOffset(10);
+        mWearableLinearLayoutManagerUnderTest =
+                new WearableLinearLayoutManager(mActivityRule.getActivity(), mCurvingCallback);
+    }
+
+    @Test
+    public void testRoundOffsetting() throws Throwable {
+        ((CurvingLayoutCallback) mWearableLinearLayoutManagerUnderTest.getLayoutCallback())
+                .setRound(true);
+        final AtomicReference<WearableRecyclerView> wrvReference = new AtomicReference<>();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                // Set a fixed layout so that the test adapts to different device screens.
+                wrv.setLayoutParams(new FrameLayout.LayoutParams(390, 390));
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                wrv.setLayoutManager(mWearableLinearLayoutManagerUnderTest);
+                wrvReference.set(wrv);
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        WearableRecyclerView wrv = wrvReference.get();
+
+        View child1 = wrv.getChildAt(0);
+        View child2 = wrv.getChildAt(1);
+        View child3 = wrv.getChildAt(2);
+        View child4 = wrv.getChildAt(3);
+        View child5 = wrv.getChildAt(4);
+
+        // The left position and the translation of the child is modified if the screen is round.
+        // Check if the 5th child is not null as some devices will not be able to display 5 views.
+        assertEquals(136, child1.getLeft());
+        assertEquals(-6.3, child1.getTranslationY(), 0.1);
+
+        assertEquals(91, child2.getLeft(), 1);
+        assertEquals(-15.21, child2.getTranslationY(), 0.1);
+
+        assertEquals(58, child3.getLeft(), 1);
+        assertEquals(-13.5, child3.getTranslationY(), 0.1);
+
+        assertEquals(42, child4.getLeft(), 1);
+        assertEquals(-4.5, child4.getTranslationY(), 0.1);
+
+        if (child5 != null) {
+            assertEquals(43, child5.getLeft(), 1);
+            assertEquals(6.7, child5.getTranslationY(), 0.1);
+        }
+    }
+
+    @Test
+    public void testStraightOffsetting() throws Throwable {
+        ((CurvingLayoutCallback) mWearableLinearLayoutManagerUnderTest.getLayoutCallback())
+                .setRound(
+                false);
+        final AtomicReference<WearableRecyclerView> wrvReference = new AtomicReference<>();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                wrv.setLayoutManager(mWearableLinearLayoutManagerUnderTest);
+                wrvReference.set(wrv);
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        WearableRecyclerView wrv = wrvReference.get();
+
+        View child1 = wrv.getChildAt(0);
+        View child2 = wrv.getChildAt(1);
+        View child3 = wrv.getChildAt(2);
+        View child4 = wrv.getChildAt(3);
+        View child5 = wrv.getChildAt(4);
+
+        // The left position and the translation of the child is not modified if the screen is
+        // straight. Check if the 5th child is not null as some devices will not be able to display
+        // 5 views.
+        assertEquals(0, child1.getLeft());
+        assertEquals(0.0f, child1.getTranslationY(), 0);
+
+        assertEquals(0, child2.getLeft());
+        assertEquals(0.0f, child2.getTranslationY(), 0);
+
+        assertEquals(0, child3.getLeft());
+        assertEquals(0.0f, child3.getTranslationY(), 0);
+
+        assertEquals(0, child4.getLeft());
+        assertEquals(0.0f, child4.getTranslationY(), 0);
+
+        if (child5 != null) {
+            assertEquals(0, child5.getLeft());
+            assertEquals(0.0f, child5.getTranslationY(), 0);
+        }
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/WearableRecyclerViewTest.java b/wear/tests/src/android/support/wear/widget/WearableRecyclerViewTest.java
new file mode 100644
index 0000000..ba6fbe7
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/WearableRecyclerViewTest.java
@@ -0,0 +1,227 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.wear.widget.util.AsyncViewActions.waitForMatchingView;
+import static android.support.wear.widget.util.MoreViewAssertions.withNoVerticalScrollOffset;
+import static android.support.wear.widget.util.MoreViewAssertions.withPositiveVerticalScrollOffset;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.hamcrest.Matchers.allOf;
+
+import android.app.Activity;
+import android.support.annotation.IdRes;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.GeneralLocation;
+import android.support.test.espresso.action.GeneralSwipeAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Swipe;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.test.R;
+import android.support.wear.widget.util.WakeLockRule;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class WearableRecyclerViewTest {
+
+    private static final long MAX_WAIT_TIME = 10000;
+    @Mock
+    WearableRecyclerView.LayoutManager mMockChildLayoutManager;
+
+    @Rule
+    public final WakeLockRule wakeLock = new WakeLockRule();
+
+    @Rule
+    public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testCaseInitState() {
+        WearableRecyclerView wrv = new WearableRecyclerView(mActivityRule.getActivity());
+        wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext()));
+
+        assertFalse(wrv.isEdgeItemsCenteringEnabled());
+        assertFalse(wrv.isCircularScrollingGestureEnabled());
+        assertEquals(1.0f, wrv.getBezelFraction());
+        assertEquals(180.0f, wrv.getScrollDegreesPerScreen());
+    }
+
+    @Test
+    public void testEdgeItemsCenteringOnAndOff() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                wrv.setEdgeItemsCenteringEnabled(true);
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                View child = wrv.getChildAt(0);
+                assertNotNull("child", child);
+                assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop());
+            }
+        });
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                wrv.setEdgeItemsCenteringEnabled(false);
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                View child = wrv.getChildAt(0);
+                assertNotNull("child", child);
+                assertEquals(0, child.getTop());
+
+            }
+        });
+    }
+
+    @Test
+    public void testEdgeItemsCenteringBeforeChildrenDrawn() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Activity activity = mActivityRule.getActivity();
+                WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(R.id.wrv);
+                RecyclerView.Adapter<WearableRecyclerView.ViewHolder> adapter = wrv.getAdapter();
+                wrv.setAdapter(null);
+                wrv.setEdgeItemsCenteringEnabled(true);
+                wrv.setAdapter(adapter);
+            }
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                // Verify the first child
+                View child = wrv.getChildAt(0);
+                assertNotNull("child", child);
+                assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop());
+            }
+        });
+    }
+
+    @Test
+    public void testCircularScrollingGesture() throws Throwable {
+        onView(withId(R.id.wrv)).perform(swipeDownFromTopRight());
+        assertNotScrolledY(R.id.wrv);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                wrv.setCircularScrollingGestureEnabled(true);
+            }
+        });
+
+        onView(withId(R.id.wrv)).perform(swipeDownFromTopRight());
+        assertScrolledY(R.id.wrv);
+    }
+
+    @Test
+    public void testCurvedOffsettingHelper() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WearableRecyclerView wrv =
+                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
+                wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext()));
+            }
+        });
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        onView(withId(R.id.wrv)).perform(swipeDownFromTopRight());
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Activity activity = mActivityRule.getActivity();
+                WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(R.id.wrv);
+                if (activity.getResources().getConfiguration().isScreenRound()) {
+                    View child = wrv.getChildAt(0);
+                    assertTrue(child.getLeft() > 0);
+                } else {
+                    for (int i = 0; i < wrv.getChildCount(); i++) {
+                        assertEquals(0, wrv.getChildAt(i).getLeft());
+                    }
+                }
+            }
+        });
+    }
+
+    private static ViewAction swipeDownFromTopRight() {
+        return new GeneralSwipeAction(
+                Swipe.FAST, GeneralLocation.TOP_RIGHT, GeneralLocation.BOTTOM_RIGHT,
+                Press.FINGER);
+    }
+
+    private void assertScrolledY(@IdRes int layoutId) {
+        onView(withId(layoutId)).perform(waitForMatchingView(
+                allOf(withId(layoutId), withPositiveVerticalScrollOffset()), MAX_WAIT_TIME));
+    }
+
+    private void assertNotScrolledY(@IdRes int layoutId) {
+        onView(withId(layoutId)).perform(waitForMatchingView(
+                allOf(withId(layoutId), withNoVerticalScrollOffset()), MAX_WAIT_TIME));
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/WearableRecyclerViewTestActivity.java b/wear/tests/src/android/support/wear/widget/WearableRecyclerViewTestActivity.java
new file mode 100644
index 0000000..2329fc5
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/WearableRecyclerViewTestActivity.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.test.R;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class WearableRecyclerViewTestActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.wearable_recycler_view_basic);
+        WearableRecyclerView wrv = findViewById(R.id.wrv);
+        wrv.setLayoutManager(new WearableLinearLayoutManager(this));
+        wrv.setAdapter(new TestAdapter());
+    }
+
+    private class ViewHolder extends RecyclerView.ViewHolder {
+        TextView mView;
+        ViewHolder(TextView itemView) {
+            super(itemView);
+            mView = itemView;
+        }
+    }
+
+    private class TestAdapter extends WearableRecyclerView.Adapter<ViewHolder> {
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            TextView view = new TextView(parent.getContext());
+            view.setLayoutParams(new RecyclerView.LayoutParams(200, 50));
+            return new ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder holder, int position) {
+            holder.mView.setText("holder at position " + position);
+            holder.mView.setTag(position);
+        }
+
+        @Override
+        public int getItemCount() {
+            return 100;
+        }
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/drawer/DrawerTestActivity.java b/wear/tests/src/android/support/wear/widget/drawer/DrawerTestActivity.java
new file mode 100644
index 0000000..14af0e1
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/drawer/DrawerTestActivity.java
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.IntDef;
+import android.support.wear.test.R;
+import android.support.wear.widget.drawer.WearableDrawerLayout.DrawerStateCallback;
+import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter;
+import android.util.ArrayMap;
+import android.view.Gravity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+
+/**
+ * Test {@link Activity} for {@link WearableDrawerLayout} and implementations of {@link
+ * android.support.wear.widget.drawer.WearableDrawerView}.
+ */
+public class DrawerTestActivity extends Activity {
+
+    private static final int DRAWER_SIZE = 5;
+    private static final String STYLE_EXTRA = "style";
+    private static final String OPEN_TOP_IN_ONCREATE_EXTRA = "openTopInOnCreate";
+    private static final String OPEN_BOTTOM_IN_ONCREATE_EXTRA = "openBottomInOnCreate";
+    private static final String CLOSE_FIRST_DRAWER_OPENED = "closeFirstDrawerOpened";
+    private static final Map<Integer, Integer> STYLE_TO_RES_ID = new ArrayMap<>();
+
+    static {
+        STYLE_TO_RES_ID.put(
+                DrawerStyle.BOTH_DRAWER_NAV_MULTI_PAGE,
+                R.layout.test_multi_page_nav_drawer_layout);
+        STYLE_TO_RES_ID.put(
+                DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE,
+                R.layout.test_single_page_nav_drawer_layout);
+        STYLE_TO_RES_ID.put(
+                DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE,
+                R.layout.test_only_action_drawer_with_title_layout);
+
+    }
+
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private final WearableNavigationDrawerAdapter mDrawerAdapter =
+            new WearableNavigationDrawerAdapter() {
+                @Override
+                public String getItemText(int pos) {
+                    return Integer.toString(pos);
+                }
+
+                @Override
+                public Drawable getItemDrawable(int pos) {
+                    return getDrawable(android.R.drawable.star_on);
+                }
+
+                @Override
+                public int getCount() {
+                    return DRAWER_SIZE;
+                }
+            };
+    private WearableActionDrawerView mActionDrawer;
+    private WearableDrawerLayout mDrawerLayout;
+    private WearableNavigationDrawerView mNavigationDrawer;
+    private final Runnable mCloseTopDrawerRunnable =
+            new Runnable() {
+                @Override
+                public void run() {
+                    mNavigationDrawer.getController().closeDrawer();
+                }
+            };
+    private final DrawerStateCallback mCloseFirstDrawerOpenedCallback =
+            new DrawerStateCallback() {
+                @Override
+                public void onDrawerOpened(WearableDrawerLayout layout,
+                        WearableDrawerView drawerView) {
+                    mMainThreadHandler.postDelayed(mCloseTopDrawerRunnable, 1000);
+                }
+            };
+    @DrawerStyle private int mNavigationStyle;
+    private boolean mOpenTopDrawerInOnCreate;
+    private boolean mOpenBottomDrawerInOnCreate;
+    private boolean mCloseFirstDrawerOpened;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        parseIntent(getIntent());
+
+        setContentView(STYLE_TO_RES_ID.get(mNavigationStyle));
+
+        mDrawerLayout = (WearableDrawerLayout) findViewById(R.id.drawer_layout);
+        mNavigationDrawer = (WearableNavigationDrawerView) findViewById(R.id.navigation_drawer);
+        mActionDrawer = (WearableActionDrawerView) findViewById(R.id.action_drawer);
+
+        if (mCloseFirstDrawerOpened) {
+            mDrawerLayout.setDrawerStateCallback(mCloseFirstDrawerOpenedCallback);
+        }
+
+        if (mNavigationDrawer != null) {
+            mNavigationDrawer.setAdapter(mDrawerAdapter);
+            if (mOpenTopDrawerInOnCreate) {
+                mDrawerLayout.openDrawer(Gravity.TOP);
+            } else {
+                mDrawerLayout.peekDrawer(Gravity.TOP);
+            }
+        }
+
+        if (mActionDrawer != null) {
+            if (mOpenBottomDrawerInOnCreate) {
+                mDrawerLayout.openDrawer(Gravity.BOTTOM);
+            } else {
+                mDrawerLayout.peekDrawer(Gravity.BOTTOM);
+            }
+        }
+    }
+
+    private void parseIntent(Intent intent) {
+        //noinspection WrongConstant - Linter doesn't know intent contains a NavigationStyle
+        mNavigationStyle = intent.getIntExtra(STYLE_EXTRA, DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE);
+        mOpenTopDrawerInOnCreate = intent.getBooleanExtra(OPEN_TOP_IN_ONCREATE_EXTRA, false);
+        mOpenBottomDrawerInOnCreate = intent.getBooleanExtra(OPEN_BOTTOM_IN_ONCREATE_EXTRA, false);
+        mCloseFirstDrawerOpened = intent.getBooleanExtra(CLOSE_FIRST_DRAWER_OPENED, false);
+    }
+
+    /**
+     * Which configuration of drawers should be used.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE,
+            DrawerStyle.BOTH_DRAWER_NAV_MULTI_PAGE,
+            DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE
+    })
+    public @interface DrawerStyle {
+    int BOTH_DRAWER_NAV_SINGLE_PAGE = 0;
+    int BOTH_DRAWER_NAV_MULTI_PAGE = 1;
+    int ONLY_ACTION_DRAWER_WITH_TITLE = 2;
+    }
+
+    /**
+     * Builds an {@link Intent} to start this {@link Activity} with the appropriate extras.
+     */
+    public static class Builder {
+
+        @DrawerStyle private int mStyle = DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE;
+        private boolean mOpenTopDrawerInOnCreate = false;
+        private boolean mOpenBottomDrawerInOnCreate = false;
+        private boolean mCloseFirstDrawerOpened = false;
+
+        public Builder setStyle(@DrawerStyle int style) {
+            mStyle = style;
+            return this;
+        }
+
+        public Builder openTopDrawerInOnCreate() {
+            mOpenTopDrawerInOnCreate = true;
+            return this;
+        }
+
+        public Builder openBottomDrawerInOnCreate() {
+            mOpenBottomDrawerInOnCreate = true;
+            return this;
+        }
+
+        public Builder closeFirstDrawerOpened() {
+            mCloseFirstDrawerOpened = true;
+            return this;
+        }
+
+        public Intent build() {
+            return new Intent()
+                    .putExtra(STYLE_EXTRA, mStyle)
+                    .putExtra(OPEN_TOP_IN_ONCREATE_EXTRA, mOpenTopDrawerInOnCreate)
+                    .putExtra(OPEN_BOTTOM_IN_ONCREATE_EXTRA, mOpenBottomDrawerInOnCreate)
+                    .putExtra(CLOSE_FIRST_DRAWER_OPENED, mCloseFirstDrawerOpened);
+        }
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java b/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
new file mode 100644
index 0000000..07eaa87
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
@@ -0,0 +1,467 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.drawer;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.swipeDown;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withParent;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static android.support.wear.widget.util.AsyncViewActions.waitForMatchingView;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.widget.RecyclerView;
+import android.support.wear.test.R;
+import android.support.wear.widget.drawer.DrawerTestActivity.DrawerStyle;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import android.widget.ImageView;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Espresso tests for {@link WearableDrawerLayout}.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class WearableDrawerLayoutEspressoTest {
+
+    private static final long MAX_WAIT_MS = 4000;
+
+    @Rule public final ActivityTestRule<DrawerTestActivity> activityRule =
+            new ActivityTestRule<>(
+                    DrawerTestActivity.class, true /* touchMode */, false /* initialLaunch*/);
+
+    private final Intent mSinglePageIntent =
+            new DrawerTestActivity.Builder().setStyle(DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE)
+                    .build();
+    @Mock WearableNavigationDrawerView.OnItemSelectedListener mNavDrawerItemSelectedListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void openingNavigationDrawerDoesNotCloseActionDrawer() {
+        // GIVEN a drawer layout with a peeking action and navigation drawer
+        activityRule.launchActivity(mSinglePageIntent);
+        DrawerTestActivity activity = activityRule.getActivity();
+        WearableDrawerView actionDrawer =
+                (WearableDrawerView) activity.findViewById(R.id.action_drawer);
+        WearableDrawerView navigationDrawer =
+                (WearableDrawerView) activity.findViewById(R.id.navigation_drawer);
+        assertTrue(actionDrawer.isPeeking());
+        assertTrue(navigationDrawer.isPeeking());
+
+        // WHEN the top drawer is opened
+        openDrawer(navigationDrawer);
+        onView(withId(R.id.navigation_drawer))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.navigation_drawer), isOpened(true)),
+                                MAX_WAIT_MS));
+
+        // THEN the action drawer should still be peeking
+        assertTrue(actionDrawer.isPeeking());
+    }
+
+    @Test
+    public void swipingDownNavigationDrawerDoesNotCloseActionDrawer() {
+        // GIVEN a drawer layout with a peeking action and navigation drawer
+        activityRule.launchActivity(mSinglePageIntent);
+        onView(withId(R.id.action_drawer)).check(matches(isPeeking()));
+        onView(withId(R.id.navigation_drawer)).check(matches(isPeeking()));
+
+        // WHEN the top drawer is opened by swiping down
+        onView(withId(R.id.drawer_layout)).perform(swipeDown());
+        onView(withId(R.id.navigation_drawer))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.navigation_drawer), isOpened(true)),
+                                MAX_WAIT_MS));
+
+        // THEN the action drawer should still be peeking
+        onView(withId(R.id.action_drawer)).check(matches(isPeeking()));
+    }
+
+
+    @Test
+    public void firstNavDrawerItemShouldBeSelectedInitially() {
+        // GIVEN a top drawer
+        // WHEN it is first opened
+        activityRule.launchActivity(mSinglePageIntent);
+        onView(withId(R.id.drawer_layout)).perform(swipeDown());
+        onView(withId(R.id.navigation_drawer))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.navigation_drawer), isOpened(true)),
+                                MAX_WAIT_MS));
+
+        // THEN the text should display "0".
+        onView(withId(R.id.ws_nav_drawer_text)).check(matches(withText("0")));
+    }
+
+    @Test
+    public void selectingNavItemChangesTextAndClosedDrawer() {
+        // GIVEN an open top drawer
+        activityRule.launchActivity(mSinglePageIntent);
+        onView(withId(R.id.drawer_layout)).perform(swipeDown());
+        onView(withId(R.id.navigation_drawer))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.navigation_drawer), isOpened(true)),
+                                MAX_WAIT_MS));
+
+        // WHEN the second item is selected
+        onView(withId(R.id.ws_nav_drawer_icon_1)).perform(click());
+
+        // THEN the text should display "1" and it should close.
+        onView(withId(R.id.ws_nav_drawer_text))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.ws_nav_drawer_text), withText("1")),
+                                MAX_WAIT_MS));
+        onView(withId(R.id.navigation_drawer))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.navigation_drawer), isClosed(true)),
+                                MAX_WAIT_MS));
+    }
+
+    @Test
+    public void programmaticallySelectingNavItemChangesTextInSinglePage() {
+        // GIVEN an open top drawer
+        activityRule.launchActivity(new DrawerTestActivity.Builder()
+                .setStyle(DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE)
+                .openTopDrawerInOnCreate()
+                .build());
+        final WearableNavigationDrawerView navDrawer =
+                activityRule.getActivity().findViewById(R.id.navigation_drawer);
+        navDrawer.addOnItemSelectedListener(mNavDrawerItemSelectedListener);
+
+        // WHEN the second item is selected programmatically
+        selectNavItem(navDrawer, 1);
+
+        // THEN the text should display "1" and the listener should be notified.
+        onView(withId(R.id.ws_nav_drawer_text))
+                .check(matches(withText("1")));
+        verify(mNavDrawerItemSelectedListener).onItemSelected(1);
+    }
+
+    @Test
+    public void programmaticallySelectingNavItemChangesTextInMultiPage() {
+        // GIVEN an open top drawer
+        activityRule.launchActivity(new DrawerTestActivity.Builder()
+                .setStyle(DrawerStyle.BOTH_DRAWER_NAV_MULTI_PAGE)
+                .openTopDrawerInOnCreate()
+                .build());
+        final WearableNavigationDrawerView navDrawer =
+                activityRule.getActivity().findViewById(R.id.navigation_drawer);
+        navDrawer.addOnItemSelectedListener(mNavDrawerItemSelectedListener);
+
+        // WHEN the second item is selected programmatically
+        selectNavItem(navDrawer, 1);
+
+        // THEN the text should display "1" and the listener should be notified.
+        onView(allOf(withId(R.id.ws_navigation_drawer_item_text), isDisplayed()))
+                .check(matches(withText("1")));
+        verify(mNavDrawerItemSelectedListener).onItemSelected(1);
+    }
+
+    @Test
+    public void navDrawerShouldOpenWhenCalledInOnCreate() {
+        // GIVEN an activity which calls openDrawer(Gravity.TOP) in onCreate
+        // WHEN it is launched
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE)
+                        .openTopDrawerInOnCreate()
+                        .build());
+
+        // THEN the nav drawer should be open
+        onView(withId(R.id.navigation_drawer)).check(matches(isOpened(true)));
+    }
+
+    @Test
+    public void actionDrawerShouldOpenWhenCalledInOnCreate() {
+        // GIVEN an activity with only an action drawer which is opened in onCreate
+        // WHEN it is launched
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
+                        .openBottomDrawerInOnCreate()
+                        .build());
+
+        // THEN the action drawer should be open
+        onView(withId(R.id.action_drawer)).check(matches(isOpened(true)));
+    }
+
+    @Test
+    public void navDrawerShouldOpenWhenCalledInOnCreateAndThenCloseWhenRequested() {
+        // GIVEN an activity which calls openDrawer(Gravity.TOP) in onCreate, then closes it
+        // WHEN it is launched
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE)
+                        .openTopDrawerInOnCreate()
+                        .closeFirstDrawerOpened()
+                        .build());
+
+        // THEN the nav drawer should be open and then close
+        onView(withId(R.id.navigation_drawer))
+                .check(matches(isOpened(true)))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.navigation_drawer), isClosed(true)),
+                                MAX_WAIT_MS));
+    }
+
+    @Test
+    public void openedNavDrawerShouldPreventSwipeToClose() {
+        // GIVEN an activity which calls openDrawer(Gravity.TOP) in onCreate
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE)
+                        .openTopDrawerInOnCreate()
+                        .build());
+
+        // THEN the view should prevent swipe to close
+        onView(withId(R.id.navigation_drawer)).check(matches(not(allowsSwipeToClose())));
+    }
+
+    @Test
+    public void closedNavDrawerShouldNotPreventSwipeToClose() {
+        // GIVEN an activity which doesn't start with the nav drawer open
+        activityRule.launchActivity(mSinglePageIntent);
+
+        // THEN the view should allow swipe to close
+        onView(withId(R.id.navigation_drawer)).check(matches(allowsSwipeToClose()));
+    }
+
+    @Test
+    public void scrolledDownActionDrawerCanScrollUpWhenReOpened() {
+        // GIVEN a freshly launched activity
+        activityRule.launchActivity(mSinglePageIntent);
+        WearableActionDrawerView actionDrawer =
+                (WearableActionDrawerView) activityRule.getActivity()
+                        .findViewById(R.id.action_drawer);
+        RecyclerView recyclerView = (RecyclerView) actionDrawer.getDrawerContent();
+
+        // WHEN the action drawer is opened and scrolled to the last item (Item 6)
+        openDrawer(actionDrawer);
+        scrollToPosition(recyclerView, 5);
+        onView(withId(R.id.action_drawer))
+                .perform(
+                        waitForMatchingView(allOf(withId(R.id.action_drawer), isOpened(true)),
+                                MAX_WAIT_MS))
+                .perform(
+                        waitForMatchingView(allOf(withText("Item 6"), isCompletelyDisplayed()),
+                                MAX_WAIT_MS));
+        // and then it is peeked
+        peekDrawer(actionDrawer);
+        onView(withId(R.id.action_drawer))
+                .perform(waitForMatchingView(allOf(withId(R.id.action_drawer), isPeeking()),
+                        MAX_WAIT_MS));
+        // and re-opened
+        openDrawer(actionDrawer);
+        onView(withId(R.id.action_drawer))
+                .perform(
+                        waitForMatchingView(allOf(withId(R.id.action_drawer), isOpened(true)),
+                                MAX_WAIT_MS));
+
+        // THEN item 6 should be visible, but swiping down should scroll up, not close the drawer.
+        onView(withText("Item 6")).check(matches(isDisplayed()));
+        onView(withId(R.id.action_drawer)).perform(swipeDown()).check(matches(isOpened(true)));
+    }
+
+    @Test
+    public void actionDrawerPeekIconShouldNotBeNull() {
+        // GIVEN a drawer layout with a peeking action drawer whose menu is initialized in XML
+        activityRule.launchActivity(mSinglePageIntent);
+        DrawerTestActivity activity = activityRule.getActivity();
+        ImageView peekIconView =
+                (ImageView) activity
+                        .findViewById(R.id.ws_action_drawer_peek_action_icon);
+        // THEN its peek icon should not be null
+        assertNotNull(peekIconView.getDrawable());
+    }
+
+    @Test
+    public void tappingActionDrawerPeekIconShouldTriggerFirstAction() {
+        // GIVEN a drawer layout with a peeking action drawer, title, and mock click listener
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
+                        .build());
+        WearableActionDrawerView actionDrawer =
+                (WearableActionDrawerView) activityRule.getActivity()
+                        .findViewById(R.id.action_drawer);
+        OnMenuItemClickListener mockClickListener = mock(OnMenuItemClickListener.class);
+        actionDrawer.setOnMenuItemClickListener(mockClickListener);
+        // WHEN the action drawer peek view is tapped
+        onView(
+                allOf(
+                        withParent(withId(R.id.action_drawer)),
+                        withId(R.id.ws_drawer_view_peek_container)))
+                .perform(click());
+        // THEN its click listener should be notified
+        verify(mockClickListener).onMenuItemClick(any(MenuItem.class));
+    }
+
+    @Test
+    public void tappingActionDrawerPeekIconShouldTriggerFirstActionAfterItWasOpened() {
+        // GIVEN a drawer layout with an open action drawer with a title, and mock click listener
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
+                        .openBottomDrawerInOnCreate()
+                        .build());
+        WearableActionDrawerView actionDrawer =
+                (WearableActionDrawerView) activityRule.getActivity()
+                        .findViewById(R.id.action_drawer);
+        OnMenuItemClickListener mockClickListener = mock(OnMenuItemClickListener.class);
+        actionDrawer.setOnMenuItemClickListener(mockClickListener);
+
+        // WHEN the action drawer is closed to its peek state and then tapped
+        peekDrawer(actionDrawer);
+        onView(withId(R.id.action_drawer))
+                .perform(waitForMatchingView(allOf(withId(R.id.action_drawer), isPeeking()),
+                        MAX_WAIT_MS));
+        actionDrawer.getPeekContainer().callOnClick();
+
+        // THEN its click listener should be notified
+        verify(mockClickListener).onMenuItemClick(any(MenuItem.class));
+    }
+
+    private void scrollToPosition(final RecyclerView recyclerView, final int position) {
+        recyclerView.post(new Runnable() {
+            @Override
+            public void run() {
+                recyclerView.scrollToPosition(position);
+            }
+        });
+    }
+
+    private void selectNavItem(final WearableNavigationDrawerView navDrawer, final int index) {
+        navDrawer.post(new Runnable() {
+            @Override
+            public void run() {
+                navDrawer.setCurrentItem(index, false);
+            }
+        });
+    }
+
+    private void peekDrawer(final WearableDrawerView drawer) {
+        drawer.post(new Runnable() {
+            @Override
+            public void run() {
+                drawer.getController().peekDrawer();
+            }
+        });
+    }
+
+    private void openDrawer(final WearableDrawerView drawer) {
+        drawer.post(new Runnable() {
+            @Override
+            public void run() {
+                drawer.getController().openDrawer();
+            }
+        });
+    }
+
+    private static TypeSafeMatcher<View> isOpened(final boolean isOpened) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is opened == " + isOpened);
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                return ((WearableDrawerView) view).isOpened() == isOpened;
+            }
+        };
+    }
+
+    private static TypeSafeMatcher<View> isClosed(final boolean isClosed) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            protected boolean matchesSafely(View view) {
+                WearableDrawerView drawer = (WearableDrawerView) view;
+                return drawer.isClosed() == isClosed;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is closed");
+            }
+        };
+    }
+
+    private TypeSafeMatcher<View> isPeeking() {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            protected boolean matchesSafely(View view) {
+                WearableDrawerView drawer = (WearableDrawerView) view;
+                return drawer.isPeeking();
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is peeking");
+            }
+        };
+    }
+
+    private TypeSafeMatcher<View> allowsSwipeToClose() {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            protected boolean matchesSafely(View view) {
+                return !view.canScrollHorizontally(-2) && !view.canScrollHorizontally(2);
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("can be swiped closed");
+            }
+        };
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/util/ArcSwipe.java b/wear/tests/src/android/support/wear/widget/util/ArcSwipe.java
new file mode 100644
index 0000000..2630d19
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/util/ArcSwipe.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.util;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.RectF;
+import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.action.MotionEvents;
+import android.support.test.espresso.action.Swiper;
+import android.support.v4.util.Preconditions;
+import android.util.Log;
+import android.view.MotionEvent;
+
+/**
+ * Swiper for gestures meant to be performed on an arc - part of a circle - not a straight line.
+ * This class assumes a square bounding box with the radius of the circle being half the height of
+ * the box.
+ */
+public class ArcSwipe implements Swiper {
+
+    /** Enum describing the exact gesture which will perform the curved swipe. */
+    public enum Gesture {
+        /** Swipes quickly between the co-ordinates, clockwise. */
+        FAST_CLOCKWISE(SWIPE_FAST_DURATION_MS, true),
+        /** Swipes deliberately slowly between the co-ordinates, clockwise. */
+        SLOW_CLOCKWISE(SWIPE_SLOW_DURATION_MS, true),
+        /** Swipes quickly between the co-ordinates, anticlockwise. */
+        FAST_ANTICLOCKWISE(SWIPE_FAST_DURATION_MS, false),
+        /** Swipes deliberately slowly between the co-ordinates, anticlockwise. */
+        SLOW_ANTICLOCKWISE(SWIPE_SLOW_DURATION_MS, false);
+
+        private final int mDuration;
+        private final boolean mClockwise;
+
+        Gesture(int duration, boolean clockwise) {
+            mDuration = duration;
+            mClockwise = clockwise;
+        }
+    }
+
+    /** The number of motion events to send for each swipe. */
+    private static final int SWIPE_EVENT_COUNT = 10;
+
+    /** Length of time a "fast" swipe should last for, in milliseconds. */
+    private static final int SWIPE_FAST_DURATION_MS = 100;
+
+    /** Length of time a "slow" swipe should last for, in milliseconds. */
+    private static final int SWIPE_SLOW_DURATION_MS = 1500;
+
+    private static final String TAG = ArcSwipe.class.getSimpleName();
+    private final RectF mBounds;
+    private final Gesture mGesture;
+
+    public ArcSwipe(Gesture gesture, RectF bounds) {
+        Preconditions.checkArgument(bounds.height() == bounds.width());
+        mGesture = gesture;
+        mBounds = bounds;
+    }
+
+    @Override
+    public Swiper.Status sendSwipe(
+            UiController uiController,
+            float[] startCoordinates,
+            float[] endCoordinates,
+            float[] precision) {
+        return sendArcSwipe(
+                uiController,
+                startCoordinates,
+                endCoordinates,
+                precision,
+                mGesture.mDuration,
+                mGesture.mClockwise);
+    }
+
+    private float[][] interpolate(float[] start, float[] end, int steps, boolean isClockwise) {
+        float startAngle = getAngle(start[0], start[1]);
+        float endAngle = getAngle(end[0], end[1]);
+
+        Path path = new Path();
+        PathMeasure pathMeasure = new PathMeasure();
+        path.moveTo(start[0], start[1]);
+        path.arcTo(mBounds, startAngle, getSweepAngle(startAngle, endAngle, isClockwise));
+        pathMeasure.setPath(path, false);
+        float pathLength = pathMeasure.getLength();
+
+        float[][] res = new float[steps][2];
+        float[] mPathTangent = new float[2];
+
+        for (int i = 1; i < steps + 1; i++) {
+            pathMeasure.getPosTan((pathLength * i) / (steps + 2f), res[i - 1], mPathTangent);
+        }
+
+        return res;
+    }
+
+    private Swiper.Status sendArcSwipe(
+            UiController uiController,
+            float[] startCoordinates,
+            float[] endCoordinates,
+            float[] precision,
+            int duration,
+            boolean isClockwise) {
+
+        float[][] steps = interpolate(startCoordinates, endCoordinates, SWIPE_EVENT_COUNT,
+                isClockwise);
+        final int delayBetweenMovements = duration / steps.length;
+
+        MotionEvent downEvent = MotionEvents.sendDown(uiController, startCoordinates,
+                precision).down;
+        try {
+            for (int i = 0; i < steps.length; i++) {
+                if (!MotionEvents.sendMovement(uiController, downEvent, steps[i])) {
+                    Log.e(TAG,
+                            "Injection of move event as part of the swipe failed. Sending cancel "
+                                    + "event.");
+                    MotionEvents.sendCancel(uiController, downEvent);
+                    return Swiper.Status.FAILURE;
+                }
+
+                long desiredTime = downEvent.getDownTime() + delayBetweenMovements * i;
+                long timeUntilDesired = desiredTime - SystemClock.uptimeMillis();
+                if (timeUntilDesired > 10) {
+                    uiController.loopMainThreadForAtLeast(timeUntilDesired);
+                }
+            }
+
+            if (!MotionEvents.sendUp(uiController, downEvent, endCoordinates)) {
+                Log.e(TAG,
+                        "Injection of up event as part of the swipe failed. Sending cancel event.");
+                MotionEvents.sendCancel(uiController, downEvent);
+                return Swiper.Status.FAILURE;
+            }
+        } finally {
+            downEvent.recycle();
+        }
+        return Swiper.Status.SUCCESS;
+    }
+
+    @VisibleForTesting
+    float getAngle(double x, double y) {
+        double relativeX = x - (mBounds.width() / 2);
+        double relativeY = y - (mBounds.height() / 2);
+        double rowAngle = Math.atan2(relativeX, relativeY);
+        double angle = -Math.toDegrees(rowAngle) - 180;
+        if (angle < 0) {
+            angle += 360;
+        }
+        return (float) angle;
+    }
+
+    @VisibleForTesting
+    float getSweepAngle(float startAngle, float endAngle, boolean isClockwise) {
+        float sweepAngle = endAngle - startAngle;
+        if (sweepAngle < 0) {
+            sweepAngle += 360;
+        }
+        return isClockwise ? sweepAngle : (360 - sweepAngle);
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/util/ArcSwipeTest.java b/wear/tests/src/android/support/wear/widget/util/ArcSwipeTest.java
new file mode 100644
index 0000000..e443ca0
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/util/ArcSwipeTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.util;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.RectF;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link ArcSwipe}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ArcSwipeTest {
+    private ArcSwipe mArcSwipeUnderTest;
+    private final RectF mFakeBounds = new RectF(0, 0, 400, 400);
+
+    @Before
+    public void setup() {
+        mArcSwipeUnderTest = new ArcSwipe(ArcSwipe.Gesture.FAST_CLOCKWISE, mFakeBounds);
+    }
+
+    @Test
+    public void testSweepAngleClockwise() {
+        assertEquals(0, mArcSwipeUnderTest.getSweepAngle(0, 0, true), 0.0f);
+        assertEquals(360, mArcSwipeUnderTest.getSweepAngle(0, 360, true), 0.0f);
+        assertEquals(90, mArcSwipeUnderTest.getSweepAngle(0, 90, true), 0.0f);
+        assertEquals(90, mArcSwipeUnderTest.getSweepAngle(90, 180, true), 0.0f);
+        assertEquals(225, mArcSwipeUnderTest.getSweepAngle(45, 270, true), 0.0f);
+        assertEquals(270, mArcSwipeUnderTest.getSweepAngle(90, 0, true), 0.0f);
+        assertEquals(170, mArcSwipeUnderTest.getSweepAngle(280, 90, true), 0.0f);
+    }
+
+    @Test
+    public void testSweepAngleAntiClockwise() {
+        assertEquals(360, mArcSwipeUnderTest.getSweepAngle(0, 0, false), 0.0f);
+        assertEquals(0, mArcSwipeUnderTest.getSweepAngle(0, 360, false), 0.0f);
+        assertEquals(270, mArcSwipeUnderTest.getSweepAngle(0, 90, false), 0.0f);
+        assertEquals(270, mArcSwipeUnderTest.getSweepAngle(90, 180, false), 0.0f);
+        assertEquals(135, mArcSwipeUnderTest.getSweepAngle(45, 270, false), 0.0f);
+        assertEquals(90, mArcSwipeUnderTest.getSweepAngle(90, 0, false), 0.0f);
+        assertEquals(190, mArcSwipeUnderTest.getSweepAngle(280, 90, false), 0.0f);
+    }
+
+    @Test
+    public void testGetAngle() {
+        assertEquals(0, mArcSwipeUnderTest.getAngle(200, 0), 0.0f);
+        assertEquals(90, mArcSwipeUnderTest.getAngle(400, 200), 0.0f);
+        assertEquals(180, mArcSwipeUnderTest.getAngle(200, 400), 0.0f);
+        assertEquals(270, mArcSwipeUnderTest.getAngle(0, 200), 0.0f);
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/util/AsyncViewActions.java b/wear/tests/src/android/support/wear/widget/util/AsyncViewActions.java
new file mode 100644
index 0000000..3db4619
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/util/AsyncViewActions.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.util;
+
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.util.HumanReadables;
+import android.support.test.espresso.util.TreeIterables;
+import android.view.View;
+
+import org.hamcrest.Matchers;
+import org.hamcrest.StringDescription;
+
+import java.util.concurrent.TimeoutException;
+
+public class AsyncViewActions {
+
+    /** Perform action of waiting for a specific view id. */
+    public static ViewAction waitForMatchingView(
+            final org.hamcrest.Matcher<? extends View> viewMatcher, final long millis) {
+        return new ViewAction() {
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+                final long startTime = System.currentTimeMillis();
+                final long endTime = startTime + millis;
+                do {
+                    for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
+                        // found matching view
+                        if (viewMatcher.matches(child)) {
+                            return;
+                        }
+                    }
+                    uiController.loopMainThreadForAtLeast(100); // at least 3 frames
+                } while (System.currentTimeMillis() < endTime);
+
+                // timeout happens
+                throw new PerformException.Builder()
+                        .withActionDescription(this.getDescription())
+                        .withViewDescription(HumanReadables.describe(view))
+                        .withCause(new TimeoutException())
+                        .build();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Wait for view which matches " + StringDescription.asString(viewMatcher);
+            }
+
+            @Override
+            public org.hamcrest.Matcher<View> getConstraints() {
+                return Matchers.any(View.class);
+            }
+        };
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/util/MoreViewAssertions.java b/wear/tests/src/android/support/wear/widget/util/MoreViewAssertions.java
new file mode 100644
index 0000000..503336b
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/util/MoreViewAssertions.java
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.util;
+
+import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
+import android.support.test.espresso.util.HumanReadables;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class MoreViewAssertions {
+
+    public static ViewAssertion left(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                assertThat("View left: " + HumanReadables.describe(view), view.getLeft(), matcher);
+            }
+        };
+    }
+
+    public static ViewAssertion approximateTop(final Matcher<Double> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                assertThat("View top: " + HumanReadables.describe(view), ((double) view.getTop()),
+                        matcher);
+            }
+        };
+    }
+
+    public static ViewAssertion top(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                assertThat("View top: " + HumanReadables.describe(view), view.getTop(), matcher);
+            }
+        };
+    }
+
+    public static ViewAssertion right(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                assertThat("View right: " + HumanReadables.describe(view), view.getRight(),
+                        matcher);
+            }
+        };
+    }
+
+    public static ViewAssertion bottom(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                assertThat("View bottom: " + HumanReadables.describe(view), view.getBottom(),
+                        matcher);
+            }
+        };
+    }
+
+    public static ViewAssertion approximateBottom(final Matcher<Double> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                assertThat("View bottom: " + HumanReadables.describe(view), ((double) view
+                        .getBottom()), matcher);
+            }
+        };
+    }
+
+    /**
+     * Returns a new ViewAssertion against a match of the view's left position, relative to the
+     * left
+     * edge of the containing window.
+     *
+     * @param matcher matcher for the left position
+     */
+    public static ViewAssertion screenLeft(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                int[] screenXy = {0, 0};
+                view.getLocationInWindow(screenXy);
+                assertThat("View screenLeft: " + HumanReadables.describe(view), screenXy[0],
+                        matcher);
+            }
+        };
+    }
+
+    /**
+     * Returns a new ViewAssertion against a match of the view's top position, relative to the top
+     * edge of the containing window.
+     *
+     * @param matcher matcher for the top position
+     */
+    public static ViewAssertion screenTop(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                int[] screenXy = {0, 0};
+                view.getLocationInWindow(screenXy);
+                assertThat("View screenTop: " + HumanReadables.describe(view), screenXy[1],
+                        matcher);
+            }
+        };
+    }
+
+    /**
+     * Returns a new ViewAssertion against a match of the view's right position, relative to the
+     * left
+     * edge of the containing window.
+     *
+     * @param matcher matcher for the right position
+     */
+    public static ViewAssertion screenRight(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                int[] screenXy = {0, 0};
+                view.getLocationInWindow(screenXy);
+                assertThat("View screenRight: " + HumanReadables.describe(view),
+                        screenXy[0] + view.getWidth(), matcher);
+            }
+        };
+    }
+
+    /**
+     * Returns a new ViewAssertion against a match of the view's bottom position, relative to the
+     * top
+     * edge of the containing window.
+     *
+     * @param matcher matcher for the bottom position
+     */
+    public static ViewAssertion screenBottom(final Matcher<Integer> matcher) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException noViewException) {
+                int[] screenXy = {0, 0};
+                view.getLocationInWindow(screenXy);
+                assertThat("View screenBottom: " + HumanReadables.describe(view),
+                        screenXy[1] + view.getHeight(), matcher);
+            }
+        };
+    }
+
+    public static Matcher<View> withTranslationX(final int xTranslation) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("with x translation == " + xTranslation);
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                return view.getTranslationX() == xTranslation;
+            }
+        };
+    }
+
+    public static Matcher<RecyclerView> withPositiveVerticalScrollOffset() {
+        return new TypeSafeMatcher<RecyclerView>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("with positive y scroll offset");
+            }
+
+            @Override
+            public boolean matchesSafely(RecyclerView view) {
+                return view.computeVerticalScrollOffset() > 0;
+            }
+        };
+    }
+
+    public static Matcher<RecyclerView> withNoVerticalScrollOffset() {
+        return new TypeSafeMatcher<RecyclerView>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("with no y scroll offset");
+            }
+
+            @Override
+            public boolean matchesSafely(RecyclerView view) {
+                return view.computeVerticalScrollOffset() == 0;
+            }
+        };
+    }
+}
diff --git a/wear/tests/src/android/support/wear/widget/util/WakeLockRule.java b/wear/tests/src/android/support/wear/widget/util/WakeLockRule.java
new file mode 100644
index 0000000..13b627e
--- /dev/null
+++ b/wear/tests/src/android/support/wear/widget/util/WakeLockRule.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package android.support.wear.widget.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule which holds a wake lock for the duration of the test.
+ */
+public class WakeLockRule implements TestRule {
+    @SuppressWarnings("deprecation")
+    private static final int WAKELOCK_FLAGS =
+            PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
+
+    @Override
+    public Statement apply(final Statement statement, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                WakeLock wakeLock = createWakeLock();
+                wakeLock.acquire();
+                try {
+                    statement.evaluate();
+                } finally {
+                    wakeLock.release();
+                }
+            }
+        };
+    }
+
+    private WakeLock createWakeLock() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        PowerManager power = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        return power.newWakeLock(WAKELOCK_FLAGS, context.getPackageName());
+    }
+}